小工具      在线工具  汉语词典  css  js  c++  java

Spring基础

# Spring,java,java-ee,spring 额外说明

收录于:152天前

Spring简介

Spring 是一个轻量级框架,其基础版本大小仅约 2 MB。

Spring框架的核心功能可用于开发任何Java应用程序,但在Java EE平台上构建Web应用程序需要扩展。 Spring 框架的目标是通过启用基于 POJO 的编程模型,使 J2EE 开发更易于使用并促进良好的编程实践。

Spring框架特点:

  • 代码解耦简化开发
    Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
  • 横向扩展,方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
  • 使 Java EE API 更易于使用
    Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
  • 方便程序测试
    Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
  • AOP编程支持
    Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
  • 声明式事务支持
    只需要通过配置就可以完成对事务的管理,而无须手动编程。

Spring优势

  1. Spring 使开发人员能够使用 POJO 开发企业级应用程序。仅使用 POJO 的优点是您不需要 EJB 容器产品,例如应用程序服务器,但可以选择使用健壮的 servlet 容器,例如 Tomcat 或一些商业产品。
  2. Spring提供了对一些在Java EE开发中非常难使用的API(JDBC、JavaMail、远程调用等)的封装,大大降低了这些API的应用难度。
  3. Spring的Web框架是一个设计良好的Web MVC框架。 MVC 模式导致应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)分离,同时在这些元素之间提供松散耦合。模型封装应用程序数据,通常由 POJO 类组成。视图负责渲染模型数据,通常它生成可由客户端浏览器解释的 HTML 输出。控制器负责处理用户请求并构建适当的模型并将其传递给视图进行渲染。
  4. 测试用 Spring 编写的应用程序很容易,因为与环境相关的代码已移至框架中。此外,通过使用 JavaBean 风格的 POJO,使用依赖注入来注入测试数据变得更加容易。
  5. 轻量级 IOC 容器往往是轻量级的,尤其是与 EJB 容器相比。这有助于在内存和 CPU 资源有限的计算机上开发和部署应用程序。
  6. Spring 提供了一致的事务管理接口,可以缩小(例如使用单个数据库)到本地事务,扩大到全局事务(例如使用 JTA)。

Spring特性

  • 非侵入性:基于Spring开发的应用程序中的对象不依赖于Spring的API
  • Inversion of Control:IOC——控制反转,是指将对象的创建权交给Spring来创建。在使用Spring之前,对象都是我们自己在代码中使用new来创建的。使用Spring之后。对象的创建交给了Spring框架。
  • 依赖注入:DI——依赖注入是指依赖对象不需要通过调用setXXX方法手动设置,而是通过配置来赋值。
  • 面向切面编程:AOP,OOP(面向对象编程)中模块化的关键单元是类,AOP中模块化的关键单元是切面。 AOP 帮助您将横切关注点与其影响的对象分开,而依赖注入则帮助您将应用程序对象彼此分开。
  • 容器:Spring是一个容器,因为它包含并管理应用程序对象的生命周期
  • 组件化:Spring 允许使用简单的组件配置来组合成复杂的应用程序。这些对象可以在 Spring 中使用 XML 和 Java 注释进行组合。
  • 一站式:基于IOC和AOP,可以集成企业应用的各种开源框架和优秀的第三方类库(其实Spring本身也提供了表示层的Spring MVC和持久层的Spring JDBC)

Spring体系结构

在这里插入图片描述
Core Continer(核心容器)
核心容器由 spring-core,spring-beans,spring-context(spring-context-support)和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:

  • spring-core 模块提供了框架的基本组件,包括 IoC 和依赖注入功能。

  • spring-beans 模块提供了 BeanFactory,这是工厂模式的一种微妙实现,它消除了对编码单例的需要,并将配置和依赖项与实际编码逻辑解耦。

  • context 模块构建在 core 和 beans 模块之上,并以类似于 JNDI 注册的方式访问对象。 Context 模块继承自 Bean 模块,并添加了国际化(例如,使用资源包)、事件传播、资源加载和上下文的透明创建(例如,通过 Servelet 容器)等功能。 Context 模块还支持 Java EE 功能,例如 EJB、JMX 和远程调用。 ApplicationContext接口是Context模块的重点。 spring-context-support 提供对第三方集成到 Spring 上下文中的支持,例如缓存(EhCache、Guava、JCache)、电子邮件(JavaMail)、调度(CommonJ、Quartz)、模板引擎(FreeMarker、JasperReports、Velocity)、 ETC。

  • spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达语言的扩展。它支持设置和获取属性值、属性赋值、方法调用、访问数组集合和索引的内容、逻辑算术运算、命名变量以及按名称从 Spring IoC 容器进行访问。检索对象,还支持列表投影、选择、聚合等。

它们的完整依赖关系如下所示:
在这里插入图片描述
所有其他部分都取决于核心容器
Spring Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC(Java Data Base Connectivity),ORM(Object Relational Mapping),OXM(Object XML Mapping),JMS(Java Message Service) 和事务处理模块,它们的细节如下:

  • JDBC 模块提供了一个 JDBC 抽象层,消除了繁琐的 JDBC 编码和数据库供应商特定错误代码的解析。

  • ORM 模块提供与流行的对象关系映射 API 的集成,包括 JPA、JDO、Hibernate 等。通过这个模块,这些ORM框架可以和Spring的其他功能集成,比如前面提到的事务管理。

  • OXM 模块提供对 OXM 实现的支持,例如 JAXB、Castor、XML Beans、JiBX、XStream 等。

  • JMS 模块包含用于生成和使用消息的函数。从 Spring 4.1 开始,集成了 spring-messaging 模块。

  • 事务模块支持编程式和声明式事务管理,用于实现特殊接口类和所有 POJO。 (注:编程式事务需要你编写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务由spring通过注解或者配置自动处理,编程式事务更细化)

网络
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:

  • Web模块提供基本的面向Web的功能和面向Web的应用上下文,例如分段文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端和Spring Remoting的Web相关部分。

  • WebMVC 模块为 Web 应用程序提供模型视图控制 (MVC) 和 REST Web 服务的实现。 Spring的MVC框架可以将领域模型代码和Web表单完全分离,并且可以与Spring框架的所有其他功能集成。

  • WebSocket模块提供了对基于WebSocket的支持,并提供了Web应用程序中客户端和服务器之间通信的两种方法。

  • WebFlux 是 Spring Framework 5.0 中引入的新的反应式 Web 框架。通过Reactor项目实现Reactive Streams规范,这是一个完全异步和非阻塞的框架。它本身不会加快程序执行速度,但在高并发情况下,异步IO可以用少量稳定的线程处理更高的吞吐量,避免文件IO/网络IO阻塞造成的线程积累。

奥普
AOP 模块提供了面向切面的编程实现,允许你定义方法拦截器和切入点对代码进行解耦,从而使实现功能的代码彻底的解耦出来。

方面
Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

仪器仪表
Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

消息传递
Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

测试
测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。

Spring部署

Spring依赖IoC特性,若将其看作一个大工厂(容器)Bean管理就交由它完成,那么就需要有相应规则(配置文件)来指导容器进行Bean生产,依赖注入,事务管理,AOP编程等等。

IoC的初步应用

  1. 创建java项目
  2. 官网下载
  3. 创建配置文件Beans.xml
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
     <bean id="stu2" class="model.Student">
   		<property name="name"><value>Spring FrameWork Runingtime</value></property>
   		<property name="age"><value>22</value></property>
   		<property name="address"><value>北京</value></property>
   </bean>

</beans>

这个配置文件是spring配置文件最基本的结构。通过命名空间进行属性注入和其他配置。

  1. 创建IoC容器用于Bean管理
import org.springframework.context.support.ClassPathXmlApplicationContext;

import model.HelloWorld;
import model.Student;

public class Test{
    
	 public static void main(String[] args) {
    
		 ClassPathXmlApplicationContext context = 
	             new ClassPathXmlApplicationContext("Beans.xml");

		 Student student= (Student) context.getBean("stu2");
		 System.out.print(student);
	   }
}

在这里插入图片描述
代码中中并没有通过new关键字实例化对象并赋值,而是通过Spring容器getBean方法来实现bean的创建,这样就没有注入到代码中即使编译为class文件只需要修改配置文件就可以,降低了代码的耦合性。

DI(依赖注入)
上面代码中通过bean标签创建了一个IoC容器,其中property就是在进行依赖注入,即在Spring完成对象创建的同时,依赖Spring容器完成对属性的赋值。如果没有DI的话都是值都是null

IoC 容器

Spring容器是Spring框架的核心。容器将创建对象,将它们连接在一起,配置它们,并管理它们从创建到销毁的整个生命周期。 Spring 容器使用依赖注入 (DI) 来管理组成应用程序的组件。这些对象称为 Spring Bean。

通过读取 IoC 配置元数据提供的指令,容器知道要实例化、配置和组装哪些对象。配置元数据可以通过 XML、Java 注释或 Java 代码表示。下图是 Spring 工作原理的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置的可执行系统或应用程序。

IOC容器是具有依赖注入功能的容器,可以创建对象。 IOC容器负责实例化、定位、配置应用程序中的对象并建立这些对象之间的依赖关系。通常一个新实例是由程序员控制的,“控制反转”是指新实例的工作不是由程序员完成而是交给Spring容器。在Spring中,BeanFactory是IOC容器的实际代表。

Spring提供了以下两种不同类型的容器:

容器 描述
BeanFactory容器 org.springframework.beans.factory.BeanFactory 接口来定义。 BeanFactory或相关接口,如BeanFactoryAware、InitializingBean、DisposableBean等,主要是为了向后兼容现有的与Spring集成的第三方框架。
应用程序上下文容器 Application Context是BeanFactory的子接口,也称为Spring上下文。 ApplicationContext是Spring中的一个更高级别的容器。与BeanFactory类似,它可以加载配置文件中定义的bean,将所有bean聚集在一起,并在有请求时分配bean。

BeanFactory 容器

最常用的 BeanFactory 容器是 XmlBeanFactory 类。该容器从 XML 文件中读取配置元数据,并使用该元数据生成已配置的系统或应用程序。

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MainApp {
    
   public static void main(String[] args) {
    
      XmlBeanFactory factory = new XmlBeanFactory
                             (new ClassPathResource("Beans.xml"));
      HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
      obj.getMessage();
   }
}
/*利用框架提供的 XmlBeanFactory() API 去生成工厂 bean 以及利 用 ClassPathResource() API 去加载在路径 CLASSPATH 下可用的 bean 配置文件。XmlBeanFactory() API 负责创建并初始化所有的 对象,即在配置文件中提到的 bean。*/
/* 利用第一步生成的 bean 工厂对象的 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象,*/

ApplicationContext 容器

Application Context 是 BeanFactory 的子接口,也被称为 Spring 上下文。
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。

该容器常用的ApplicationContext接口:

  • 文件系统Xml应用程序上下文:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MainApp {
    
   public static void main(String[] args) {
    
      ApplicationContext context = new FileSystemXmlApplicationContext
            ("C:/HelloSpring/src/Beans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
   }
}
  • 类路径XmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
  • WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。

DI依赖注入

依赖注入是控制反转的具体示例。编写Java应用程序时,应用程序类(数据操作)应尽可能独立于其他Java类,以增加这些类的可重用性;单元测试时,它们可以独立于其他类进行测试。依赖注入可以通过向构造函数传递参数来实现,依赖:A类依赖于B类。注入:当需要时,B类将通过IoC注入到A类中。

Spring容器加载配置文件后,通过反射创建类的对象,并给属性赋值。通过反射实现属性赋值由三种方式:

  • setter方法注入bean对象中使用property标签调用set方法
//属性是基本数据类型,直接用value属性或value标签赋值即可
<property name="name" value="Spring FrameWork Runingtime"/>

<property name="list"><value>Spring,FrameWork,Runingtime</value></property>

//name属性对应成员变量,value对应赋值。spring容器自动根据bean的类型定义自动转化
//属性是特殊数据类型用ref属性引用<bean>标签生产的对象或<ref>标签bean属性引用或直接属性内部<bean>生产,对象可以是内置或自定义对象
 <bean id="date" class="java.util.Date">
   </bean>
   
   <bean id="date1" class="model.DateTest">
   		<property name="dateFromat" ref="date"></property>   //ref=id
   </bean>
	||
	<bean id="date2" class="model.DateTest">
		<property name="dateFromat">
			<ref bean="date"></ref>
		</property>
   </bean>

//也可以属性内进行bean生产
<bean id="date2" class="model.DateTest">
   		<property name="dateFromat">
   			<bean class="java.util.Date"></bean>
   		</property>
   </bean>
//演示的是Date类型,可以是其他自定义类型

<bean id="date3" class="model.DateTest">
   		<property name="student">
   			<bean class="model.Student">
   				<property name="name" value="张三"></property>
   				<property name="age" value="22"></property>
   				<property name="address" value="wuhan"></property>
   			</bean>
   		</property>
   </bean>

//注入 null 和空字符串的值
<bean id="..." class="...">
   <property name="email" value=""/>
</bean>

<bean id="..." class="...">
   <property name="email"><null/></property>
</bean>
//如果是基本数据结构List,Set,Map就需要借助对应<list>,<set>,<map>标签实现,
list,set值少也可以借助value属性或<value>标签实现
<bean id="list" class="model.ListTest">
   		<!-- <property name="list"><value>Java,Python,Php,Javacript</value></property> -->
   		<!-- <property name="list" value="Java,Python,Php,Javacript"></property> -->
   		<property name="list">
   			<list>
   				<value>Java</value>
   				<value>Python</value>
   				<value>JavaScript</value>
   			</list>
   			
   		</property>	
  </bean>


 <bean id="set" class="model.SetTest">
 		<!-- <property name="set"><value>Java,Python,Php,Javacript</value></property> -->
   		<!-- <property name="set" value="Java,Python,Php,Javacript"></property> -->
   		<property name="set">
   			<set>
   				<value>Java</value>
   				<value>Python</value>
   				<value>JavaScript</value>
   			</set>
   			
   		</property>
   </bean>

<bean id="map" class="model.MapTest">
   		<property name="map">
   			<map>
   				<entry>     //map标签的套用标签
   					<key>      
   						<value>public</value>
   					</key>
   					<value>语文,英语,数学</value>
   				</entry>
   				<entry>
   					<key>
   						<value>professonal</value>
   					</key>
   					<value>Java,Python,JavaScript</value>
   				</entry>
   				
   			</map>
   		</property>
   		
   </bean>
   

  • 构造函数注入使用constructor-arg标签传递构造参数
 type 属性显式的指定了构造函数参数的类型
<bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg type="int" value="2001"/>
      <constructor-arg type="java.lang.String" value="Zara"/>
   </bean>


 index 属性来显式的指定构造函数参数的索引
<bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg index="0" value="2001"/>
      <constructor-arg index="1" value="Zara"/>
   </bean>

向一个对象传递一个引用,需要使用 标签的 ref 属性
//在编写java bean是一般包含无参构造和有参构造,构造方法注入局势利用了有参构造
public class Book {
    
	private String name;
	private Double price;
	private List chapter;
	private Map<String,String> chapterMap;
	
	public Book() {
    
		
	}
	public Book(String arg1,Double arg2,List arg3,Map arg4) {
    
		this.name=arg1;
		this.price=arg2;
		this.chapter=arg3;
		this.chapterMap=arg4;
		
	}
	public String toString() {
    
		return this.name+
				","+this.price+
				","+this.chapter+
				","+this.chapterMap;
	}

}
//利用构造方法为属性赋值,也分为基本数据类型和复杂数据类型,用法同上
 <bean id="book" class="model.Book">
   		<constructor-arg index="0" value="完美世界"></constructor-arg>
   		<constructor-arg index="1"><value>48.5</value></constructor-arg>
   		<constructor-arg index="2">
   			<list>
   				<value>石村篇</value>
   				<value>大战荒域</value>
   			</list>
   		</constructor-arg>
   		
   		<constructor-arg index="3">
   			<map>
   				<entry>
   					<key>
   					<value>第一章</value>
   					</key>
   					<value>石村篇</value>
   				</entry>
   				
   				<entry>
   					<key>
   						<value>第二章</value>
   					</key>
   					<value>大战荒域</value>
   				</entry>
   			</map>
   		</constructor-arg>
   		
   
   </bean>

/*index属性是构造参数的位置,如果位置和对象的一致可以不写。基本数据类型用value标签或value属性,特殊用
对应的标签即可,对象用<ref bean=""><bean class="">
*/


// type 属性显式的指定了构造函数参数的类型

在这里插入图片描述

  • 接口注入

Spring配置文件介绍

①xmlns="http://www.springframework.org/schema/beans",默认命名空间:没有空间名,用于Spring Bean的定义;

②xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",xsi命名空间:该命名空间用于指定每个文档中的命名空间对应的Schema样式文件,由标准组织定义标准命名空间。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Spring Bean

Spring IoC 容器使用依赖注入 (DI) 来管理组成应用程序的组件。这些对象称为 Spring Bean。每个对象称为一个 bean。 XML 中的定义。

bean的属性及子元素

bean定义包含容器所需的元数据信息:

属性 描述
姓名 该属性指定一个唯一的 bean 标识符。配置基于 XML 的元数据时,可以使用 id 和/或 name 属性指定 bean 标识符
班级 该属性是强制性的,指定用于创建 bean 的 bean 类
范围 用于配置spring bean的范围。取值为singleton(单实例模式)和prototype(多实例模式)
惰性初始化 设置为true,延迟加载,在ApplicationContext启动时,bean不会提前实例化,而是在第一次通过getBean向容器请求bean时实例化,这只适用于单例bean。
自动装配 用于配置 spring 对象属性的默认装配方法。 no:默认值不启用自动组装; byType:根据类型自动组装; byName:根据名称自动组装。一般情况下,如果没有声明bean的名称,则默认值为id;构造函数实现
依赖性检查 依赖性检查
依赖于取决于 指示一个 Bean 的实例化取决于首先实例化的另一个 Bean。
自动装配候选者 设置为 false,容器在寻找自动装配对象时不会考虑该 bean,即不会将其视为其他 bean 自动装配的候选者,但该 bean 本身可以使用自动装配来注入其他 bean
基本的 首先注入bean
初始化方法 初始化bean时调用的方法
销毁方法 容器被销毁前调用的方法
工厂方法 仅当调用factory-method指向的方法时,bean才会被实例化。
工厂bean 通过调用静态工厂方法创建bean

bean 的子元素

子元素 描述
元数据,当需要使用里面的信息时,可以通过key获取
查找方法 Getter注入是声明一个方法返回某种类型的bean,但实际返回的bean是在配置文件中配置的。
替换方法 你可以在运行时调用新的方法来替换现有的方法,也可以动态更新原有方法的逻辑。
构造函数参数 初始化时自动为bean找到对应的构造函数并传入设定的参数。
财产 通过setter方法给bean管理的对象赋值
预选赛 通过Qualifier指定注入的bean的名称

bean的作用域

在 Spring 中定义 bean 时,必须声明 bean 范围的选项。例如,要强制 Spring 在每次需要时生成一个新的 bean 实例,您应该将 bean 的范围属性声明为原型。类似地,如果您希望 Spring 每次需要时都返回相同的 bean 实例,则应该将 bean 的范围属性声明为单例。

<bean id="book" class="model.Book"></bean>
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");		 
Book book =(Book) context.getBean("book");
System.out.print(book);
		 

通过getBean方法获取IoC容器的bean对象,默认是同一个引用,每个Book对象的地址是相同的,因为bean标签的默认作用域是单例,如果有多处使用会产生线程问题,通过scope属性改变作用域。
scope=singleton (单例模式)|| prototype(多例模式)
单例模式在初始化容器时就创建,可以通过lazy-init=true实现懒加载。一般工厂类使用单例模式,其他使用多例模式,在调用bean时创建。

Spring 框架支持以下五个作用域,分别为 singleton、prototype、request、session 和 global session,5种作用域说明如下:
在这里插入图片描述

bean的生命周期

当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。豆的生命周期

为了定义安装和拆卸一个 bean,我们只要声明带有 初始化方法销毁方法 参数的 。init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。

Bean的生命周期可以表示为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- 创建spring bean -->
   <bean id="..." class="...">
      
   </bean>

   <!-- 懒加载 -->
   <bean id="..." class="..." lazy-init="true">
      
   </bean>

   <!-- 初始化方法,在构造方法之后-->
   <bean id="..." class="..." init-method="...">
     
   </bean>

   <!-- 销毁方法 -->
   <bean id="..." class="..." destroy-method="...">
     
   </bean>

</beans>

init-method 和 destroy-method 的方法体位于 IoC 容器管理的 Java Bean 中。

Spring——Bean后处理器
Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。

Bean后处理器 ​接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。你也可以在 ​Spring​ 容器通过插入一个或多个 ​BeanPostProcessor​ 的实现来完成实例化,配置和初始化一个​bean​之后实现一些自定义逻辑回调方法。

你可以配置多个 ​BeanPostProcessor ​接口,通过设置 ​BeanPostProcessor ​实现的​ 已订购 ​接口提供的​ 命令​ 属性来控制这些​ BeanPostProcessor​ 接口的执行顺序。

ApplicationContext 自动检测由 BeanPostProcessor 接口的实现定义的 bean,将这些 bean 注册为后处理器,然后通过在容器中创建 bean 来在适当的时间调用它。

在您的自定义 BeanPostProcessor 接口实现类中,实现以下两个抽象方法: BeanPostProcessor.postProcessBeforeInitialization(Object, String) 和 BeanPostProcessor.postProcessAfterInitialization(Object, String)

案件

bean继承

在定义一个 Bean 时,指定类抽象属性抽象的,被明确地标记为抽象的,自身不能被实例化,子类家长指定父类实现继承。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="father" class="model.Father" abstract="true">
      <property name="message1" value="Hello World!"/>
     
   </bean>

   <bean id="son" class="model.Son" parent="father">
      <property name="message1" value="Hello!"/>
      <property name="message2" value="Spring !"/>
   </bean>

</beans>

自动装配

自动装配:是指IoC容器在实例化bean时,从容器中查找匹配的实例赋值给当前的bean属性。
在这里插入图片描述

//byName表示,初始化bean时根据bean的属性名查找IoC容器中匹配的标识符自动注入
<bean id="dat3" class="model.DateTest" autowire="byName">
   </bean>
/*
java bean(DateTest.java):
private Data date;

spring bean:
<bean id="date" class="java.util.Date">
   </bean>
<bean id="date1" class="model.DateTest" autowire="byName">
   </bean>
 在初始化第二个bean时会将spring bean的date注入到 date1的date变量,但是name对应的类型不匹配时报错
*/



//byType表示,初始化bean时根据bean的类型查找IoC容器中匹配的标识符自动注入
java bean(DateTest.java):
private Data date;

<bean id="date" class="java.util.Date">
   </bean>

<bean id="date2" class="model.DateTest" autowire="byType">
   </bean>
/*
在初始化第二个bean时在IoC容器中查找Date类型的对象,将其注入到date变量。但是有多个类型时会报错
*/

//当然还有构造方法和默认值no不启动自动装配

事件处理

Spring的核心是ApplicationContext,它管理bean的整个生命周期。当 bean 加载到 ApplicationContext 中时,会发布某些类型的事件。例如,当上下文启动时,会发布 ContextStartedEvent;当上下文停止时,会发布 ContextStoppedEvent。

在事件处理中,ApplicationContext是通过ApplicationEvent类和ApplicationListener接口提供的。因此,如果 bean 实现了 ApplicationListener,则每次将 ApplicationEvent 发布到 ApplicationContext 时都会通知该 bean。

在这里插入图片描述
Spring事件处理是单线程的,所以如果一个事件被发布,直至及除非所有的接收器得到的消息,该进程被阻塞并且流程将不会继续。因此,应注意在设计应用程序时,如果事件处理被使用。

要监听上下文事件,bean 应该实现 ApplicationListener 接口,该接口只有一个方法 onApplicationEvent()。用于实现spring事件监听

//开始事件
/* StartEvent.java */
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;

public class StartEvent
   implements ApplicationListener<ContextStartedEvent>{
    

   public void onApplicationEvent(ContextStartedEvent event) {
    
      System.out.println("ContextStartedEvent Received");
   }
}

//结束事件
/* StopEvent.java */
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStoppedEvent;

public class StopEvent
   implements ApplicationListener<ContextStoppedEvent>{
    

   public void onApplicationEvent(ContextStoppedEvent event) {
    
      System.out.println("ContextStoppedEvent Received");
   }
}

//监听
ApplicationContext context = 
      new ClassPathXmlApplicationContext("Beans.xml");

      // start event.
      context.start();
	  
      /* 被监听事件 */

      // stop event.
      context.stop();
//在配置文件中声明一下就可以了
 <bean id="StartEvent" class="com.manongjc.StartEvent"/>

   <bean id="StopEvent" class="com.manongjc.StopEvent"/>

Spring基于注解开发

前面的步骤都是基于配置文件applicationContext.xml进行bean的生产和管理。 XML 还可以用于描述 bean 连接,实现与注释相关的类、方法或字段声明,以将 bean 配置到组件类本身。注解注入发生在XML注入之前,因此后者的配置将覆盖前者以两种方式连接的属性。

Spring 容器中默认情况下不启用注释连接。因此,我们可以使用基于注释的接线。要使用注解,您需要在 Spring 配置文件中启用它。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:annotation-config/>
   <!-- 注解扫描 -->

</beans>

配置 context:annotation-config 后,您就可以开始注释声明,Spring 会自动将值连接到属性、方法和构造函数。

简单的例子:

//配置文件继上
//bean对象
public class Book {
    
	private String name;
	private double price;
	
	public void setName(String name) {
    
		this.name=name;
	}
	
	public String getName() {
    
		return this.name;
	}
	
	public void setPrice(Double price) {
    
		this.price=price;
	}
	
	public String toString() {
    
		return this.name+","+this.price;
	}

}
//声明IoC容器类管理bean对象
@Configuration
public class BookConfig {
    
	@Bean
	public Book1 book() {
    
		return new Book();
		
	}
}
//bean生产
import model.Book;
import model.BookConfig;

public class AppOne {
    
	public static void main(String[] args) {
    
		ApplicationContext context= new AnnotationConfigApplicationContext(Book1Config.class);
		
		Book1 book1=context.getBean(Book1.class);
		book1.setName("完美世界");
		System.out.print(book1.getName());
		
	}

}

在这里插入图片描述
上面的简单实例就是实现注解开发。其中用到了 @配置作用是声明一个Spring IoC容器进行bean的定义Bean 的生产是在这个类中完成的@豆作用是bean生产,交由IoC容器管理。

<beans ...>
	...
   <bean id="book" class="model.Book" />
</beans>
@Configuration
public class BookConfig {
    
	@Bean
	public Book1 book() {
    
		return new Book();
		
	}
}

这两段代码的效果是一样的。

基于Java注解

基于java的注解开发配置文件除开启注解外不做任何声明和定义,初始化及配置都基于java注解实现。导入时beans工厂的配置类:ApplicationContext context= new AnnotationConfigApplicationContext(BeansConfig.class)在该工厂中通过@Bean等注解进行bean生产和装配。
核心:容器配置类BeansConfig

@Configuration   //声明一个beans工厂
public class BeansConfig {
    
	@Bean    //bean生产
	public Book1 book() {
    
		return new Book();
	}
}

案件:

Book.java:
public class Book {
    ...}

BeansConfig.java:
@Configuration
public class BookConfig {
    
	@Bean
	public Book book() {
    
		return new Book();
		
	}
}

App.java (主函数):
public class App {
    
	public static void main(String[] args) {
    
		ApplicationContext context= new AnnotationConfigApplicationContext(BeansConfig.class);
		
		Book book1=context.getBean(Book.class);  // byType
		//Book book1=(Book) context.getBean("book") //byName
		book1.setName("完美世界");
		System.out.print(book1.getName());
		
	}

}

getBean()方法有两种方法式,通过@bean注解的方法名(相当于id)和导入的类获取。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
	
	<!-- 开启注解扫描 -->
   <context:annotation-config/>
   
    <!-- 定义扫描范围 -->
   <context:component-scan base-package="model"></context:component-scan>
   

</beans>

<!--配置文件中没有任何声明和定义处理开启注解扫描-->

国际奥委会注释:

注解 描述
@配置 声明一个 IoC 容器,用于 Bean 生产、依赖注入、事务管理和 AOP 编程
@成分 进行bean生产并将bean交给IoC容器管理
@豆 Bean生产,将Bean对象交给IoC管理
@价值 财产注入
@进口 允许加载从另一个配置类定义的@Bean
@Configuration

该注解基于Java,声明了一个IoC容器,用于Beans生产、依赖注入、事务管理和AOP编程。

@Configuration              //申明IoC容器
public class AppConfig {
    
   @Bean(initMethod = "init", destroyMethod = "destory" )    //生产bean方法明为id
   public Book book() {
    
      return new Book();
   }
}

该注解可以用来替换xml配置文件。由于默认加载配置文件,因此需要在配置类中开启注解扫描,才能实现完整的注解开发。
@ComponentScan(basePackages={“com.model”}) 代替xml中开启注解扫描,测试类中加载配置类而不是xml了,实现完全注解开发。

@Bean注解

bean注解就相当于<bean>标签,位于@Configuration声明的配置类容器的bean上方。其内部定义了bean属性、子元素、生命周期函数和作用域等。

声明生命周期函数

public class Book {
    
   public void init() {
    
      // initialization
   }
   public void destory() {
    
      // destruction
   }
}


@Configuration
public class AppConfig {
    
   @Bean(initMethod = "init", destroyMethod = "destory" )
   public Book book() {
    
      return new Book();
   }
}
//和下面的配置一致
  <!-- 初始化方法,在构造方法之后-->
   <bean id="..." class="..." init-method="...">
     
   </bean>

   <!-- 销毁方法 -->
   <bean id="..." class="..." destroy-method="...">
     
   </bean>

指定bean的范围

@Configuration
public class BookConfig {
    
   @Bean
   @Scope("prototype")
   public Book book() {
    
      return new Book();
   }
}
@Value注解
@Component
public class Book1 {
    
    @Value(value = "zhansan")
    private String name;
    @Value(value = "48.45")
    private Double price;

    public void setName(String name) {
    
        this.name = name;
    }

    public void setPrice(Double price) {
    
        this.price = price;
    }

    @Override
    public String toString() {
    
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
@Import注解
@Configuration
public class ConfigA {
    
   @Bean
   public A a() {
    
      return new A(); 
   }
}

//在ConfigB配置类中引入ConfigA的bean
@Configuration
@Import(ConfigA.class)
public class ConfigB {
    
   @Bean
   public B a() {
    
      return new A(); 
   }
}
//只需要实例化ConfigB即可
public static void main(String[] args) {
    
   ApplicationContext context = 
   new AnnotationConfigApplicationContext(ConfigB.class);

   A a = context.getBean(A.class);
   B b = context.getBean(B.class);
}

基于配置文件注解

以配置文件主、Java注解为辅分工合作完成注解开发,仍然导入配置文件ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml")
@成分
该注解基于配置文件,生产bean并交由IoC容器管理。@Component(value="id")相当于bean标签,value为id属性。如果没有配置,则通过小写类名获取beanBook book =(Book) context.getBean("book")

@Component("id")   //生产bean交由IoC管理,不用导入注解配置类(beansConfig)中
public class Book {
    
	private String name;
	private double price;
	
	public void setName(String name) {
    
		this.name=name;
	}
	
	public String getName() {
    
		return this.name;
	}
	
	public void setPrice(Double price) {
    
		this.price=price;
	}
	
	public String toString() {
    
		return this.name+","+this.price;
	}

}


ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
Book book =(Book) context.getBean("id");

@Service、@Controller和@Repository具有相同的功能,他们主要时语义上的区别:

  • @Controller声明将控制器类交给IoC管理,例如Servlet。
  • @Service声明将业务管理类转交给IoC管理,是Servlet接口的实现类。
  • @Repository声明持久化类交给IoC管理和DAO接口。
  • @Component 声明常规类。

bean对象使用这些注解直接在成员变量定义处赋值:

@Component("book")
public class Book {
    
	private String name = "完美世界";   //直接在定义处赋值(静态赋值)
	private double price = "48";
	
	public void setName(String name) {
    
		this.name=name;
	}
	
	public String getName() {
    
		return this.name;
	}
	
	public void setPrice(Double price) {
    
		this.price=price;
	}
	
	public String toString() {
    
		return this.name+","+this.price;
	}

}

@范围注解
用于改变bean的作用域,两种[singleton,prototype]。

@Component("book")
@Scope(value="singleton")
public class Book {
    
	private String name = "完美世界";   //直接在定义处赋值
	private double price = "48";
	
	public void setName(String name) {
    
		this.name=name;
	}
	
	public String getName() {
    
		return this.name;
	}
	
	public void setPrice(Double price) {
    
		this.price=price;
	}
	
	public String toString() {
    
		return this.name+","+this.price;
	}

}

@Lazy 注释
懒加载,singleton作用域下,使其在bean生产时加载bean而不是初始化配置文件时。

@Component("book")
@Scope(value="singleton")
@Lazy(true)   //默认为false
public class Book {
    ...}

@PostConstruct 和 @PreDestroy 注释
相当于 init-method 和destroy-method 管理bean的生命周期。

@Component("book")
...
public class Book {
    
	...
	
	@PostConstruct
   	public void init(){
    
      	System.out.println("Bean is init.");
   	}
   	@PreDestroy
   	public void destroy(){
    
      System.out.println("Bean is destroy");
}

//这样注解就相当于在<bean>标签中的 init-method="init" 和destroy-method="destory"
//初始化方法子构造方法之后,销毁方法子容器销毁之前

@Autowired注解
自动装配,从IoC容器中寻找匹配的对象自动注入到@Autowired 注解的对象。
该注解默认byType如果没有找到对应的类型就会报错,若想没有找到就返回null设置required=false属性可以。

//用在成员变量之前
@Autowired(required=false)
private Book book;

//用在setter方法前
@Autowired(required=false)
public void setBook(Book book){
    
	this.book=book;
}
//用在方法前
@Autowired(required=false)

@Qualifier注解
当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注解和 @Autowired 注解通过指定哪一个真正的 bean 将会被装配来消除混乱。

...
@Component("book")
public class Book {
    ...}

...
@Component("book1")
public class Book {
    ...}

@Autowired
@Qualifier("book1")
private Book book;


@Autowired
public setBook(@Qualifier("book1") Book book){
    
	this.book=book;
}

//@Qualifier注解引用了名称(byName)也可以看作为对ref的引用。

@Resource注解
在字段中或者 setter 方法中使用 @Resource 注释。@Resource 注释使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入,即它遵循 byName 自动连接语义。

如果未显式指定“名称”,则默认名称源自字段名称或 setter 方法。

如果byName没有找到,则再byType搜索,否则会报错。

@Resource(name= "book")
public void setBook( Book book ){
    
   this.book = book;
}

@价值注入普通数据类型即属性。

@Component
@Scope("singleton")

public class Book2 {
    
    @Value(value = "zhansang")
    private String name;

    @Value(value = "45.23")
    public Double price;

    public void setName(String name) {
    
        this.name = name;
    }

    public void setPrice(Double price) {
    
        this.price = price;
    }

    @Override
    public String toString() {
    
        return "Book2{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
    @PostConstruct
    public void before(){
    
        System.out.println("init ....");
    }
    @PreDestroy
    public void after(){
    
        System.out.println("destroy ...");
    }

}

@必填注解
@Required 注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。

Spring AOP

Spring 框架的一个关键组件是面向切面的编程(AOP)框架。面向切面的编程需要把程序逻辑分解成不同的部分称为部分。跨一个应用程序的多个点的功能被称为入口点,这些部分在概念上独立于应用程序的业务逻辑。在软件开发过程中有各种各样的很好的切面的例子,如日志记录、审计、声明式事务、安全性和缓存等。

在OOP中,模块化的关键单元是类,而在AOP中,模块化的单元是方面。依赖注入可以帮助您将应用程序对象彼此解耦,AOP 可以帮助您将横切关注点与其影响的对象解耦。

Spring AOP 模块提供拦截器来拦截一个应用程序,或者说表演例如,当执行一个方法时,将该方法由Spring代理执行,那么通过框架就可以在方法执行之前或之后添加额外的功能。
AOP底层就是JDK动态代理(JDK动态代理,CGlib动态代理)代理可以看千锋教育的SSM框架。

面向切面编程简单来说就是再不改变原来代码的基础上添加新的功能。
在这里插入图片描述

基本概念:
在这里插入图片描述
在这里插入图片描述

Spring AOP部署

下载额外工具包并引入:
在这里插入图片描述

xml配置文件使用aop命名空间,并引入aop标签:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">


</beans>

声明代理并增强功能:

<aop:config>
		<!--声明一个切入点-->
		<aop:pointcut expression="execution(* aop_test.AopPointCut.insert())" id="myPoint"/>

		<!--声明一个切面-->
		<aop:aspect id="myAspect" ref="aspect1">
		
			<!--通知-->
			<aop:before method="before" pointcut-ref="myPoint"/>
		</aop:aspect>
	</aop:config>

基于配置文件的通知

通知是强化的方法,通过代理再代理对象的前后添加强化的功能。
通过<aop:aspect>中使用<aop:{通知类型名}>元素声明任意五种类型的通知:

<aop:config>
   <aop:aspect id="myAspect" ref="aBean">
      <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/>
      <!-- a before advice definition -->
      <aop:before pointcut-ref="businessService" method="doRequiredTask"/>
      <!-- an after advice definition -->
      <aop:after pointcut-ref="businessService" method="doRequiredTask"/>
      <!-- an after-returning advice definition -->
      <!--The doRequiredTask method must have parameter named retVal -->
      <aop:after-returning pointcut-ref="businessService" returning="retVal" method="doRequiredTask"/>
      <!-- an after-throwing advice definition -->
      <!--The doRequiredTask method must have parameter named ex -->
      <aop:after-throwing pointcut-ref="businessService" throwing="ex" method="doRequiredTask"/>
      <!-- an around advice definition -->
      <aop:around pointcut-ref="businessService" method="doRequiredTask"/>
   ...
   </aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

切入点表达式
对类的哪个方法加强,进行声明和配置:
execution([权限修饰符] [返回类型] [类的全路径] [方法名称] ([参数列表]))

<aop:pointcut expression="execution(* aop_test.AopPointCut.insert())" id="myPoint"/>中的execution括号中的第一个*表示返回类型(所有);第二个参数是方法的相对路径的相对路径,如果方法括号中(..)表示参数对所有方法都有效。
声明代理

<aop:config>
   
</aop:config>

在代理内部声明切入点

<aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/>

声明方面

<aop:aspect id="myAspect" ref="aBean">
   ...
   </aop:aspect>

通知定义

<!--前置通知-->
<aop:before pointcut-ref="businessService" method="doRequiredTask"/>
<!--后置通知-->
<aop:after pointcut-ref="businessService" method="doRequiredTask"/>

<!--返回后置通知-->
<aop:after-returning pointcut-ref="businessService" returning="retVal" method="doRequiredTask"/>

<!--抛出异常后通知-->
<aop:after-throwing pointcut-ref="businessService" throwing="ex" method="doRequiredTask"/>

<!--环绕通知-->
<aop:around pointcut-ref="businessService" method="doRequiredTask"/>

简单的aop例子
切入点和切面容易混淆,简单来说面向对象的基本单位是对象,面向切面的基本单位是方法,切入点就是被加强的方法,切面就是加强方法的集合。

//创建切入点(要加强的方法)
public class PointCutTest {
    
    public void aspectApp(){
    
        System.out.println("主程序运行...");
    }
}

//创建切面(加强方法集合)
public class AspectTest {
    

    public void before(){
    
        System.out.println("aop starting ...");
    }
    public void after(){
    
        System.out.println("aop ending ....");
    }
}

在 xml 中配置方面和入口点

<--将切面和切入点交由IoC容器管理-->
<bean id="aspectTest" class="aspect.AspectTest"></bean>
<bean id="pointCutTest" class="aspect.PointCutTest"></bean>

<!--配置切面和切入点-->
 <aop:config>
 	<!--切入点(要加强的方法)-->
     <aop:pointcut id="cupTest" expression="execution(public void aspect.PointCutTest.*(..))"/>
     <!--切面(加强方法合集)-->
     <aop:aspect ref="aspectTest">  <!--ref引入IoC管理的切面-->
         <aop:before method="before" pointcut-ref="cupTest"></aop:before>
         <aop:after method="after" pointcut-ref="cupTest"></aop:after>
         <!--method是切面中的加强方法,poincut-ref引入切入点-->
     </aop:aspect>
 </aop:config>

<!--整个反射过程IoC容器管理-->

//测试主程序
public class AopMainApp {
    
    public static void main(String[] args) {
    
        ApplicationContext context= new ClassPathXmlApplicationContext("classpath:aop.xml");
        PointCutTest pointCutTest =(PointCutTest) context.getBean("pointCutTest");  //DL依赖查询创建切入点(加强方法)
        pointCutTest.aspectApp();  
    }
}

结果:
在这里插入图片描述

基于注解的通知

基于注解的aop配置会更加方便。在配置文件中启用注解aop注解扫描:

开启组件扫描和组件扫描路径
...
//基于注解配置的aop代理
 <aop:aspectj-autoproxy/>

注解配置一定要导入aspectjweaver.jar工具包,它是aop基于java扩展的@AspectJ风格的注解,用于注解实现面向切面编程。

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.8</version>
            <scope>runtime</scope>
        </dependency>

开发时一定到删除scope,不然会报错:
在这里插入图片描述

根据注释,必须导入以下包:

spring-aop:AOP核心功能,例如代理工厂等。
aspectjweaver:支持aop相关注解等。
aspectjrt:支持切入点表达式等。
aopalliance:是AOP联盟的API包,里面包含了针对面向切面的接口。通常Spring等其它具备动态织入功能的框架依赖此包。

如果实现完全注解开发需要用注解开启aop代理扫描: @EnableAspectJAutoProxy(proxyTargetClass = true) (和@ComponentScan同级)这样就不需要配置文件实现完全注解开发。

@切入点

import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression 
private void businessService() {
    }  // signature

@方面

@Component
@Aspect
public class AspectTest {
    
    @Before("execution(public void PointTest.strengthMethod(..))")
    public void beforeMetod(){
    

        System.out.println("one ...");
    }
    @After("execution(public void PointTest.strengthMethod(..))")
    public void afterMethod(){
    
        System.out.println("two ...");
    }
}

@{交易名称}

@Before("businessService()")  //businessService()相当于execution(* com.xyz.myapp.service.*.*(..))归功于@Pointcut
public void doBeforeTask(){
    
 ...
}
@After("businessService()")
public void doAfterTask(){
    
 ...
}
@AfterReturning(pointcut = "businessService()", returning="retVal")
public void doAfterReturnningTask(Object retVal){
    
  // you can intercept retVal here.
  ...
}
@AfterThrowing(pointcut = "businessService()", throwing="ex")
public void doAfterThrowingTask(Exception ex){
    
  // you can intercept thrown exception here.
  ...
}
@Around("businessService()")
public Object arround(ProceedingJoinPoint point){
    
	...
	Object obj=point.proceed();
	...
	return obj;
}

环绕通知的方法格式是确定的:

@Around("businessService()")
public Object arround(ProceedingJoinPoint point){
    
	...
	Object obj=point.proceed();
	...
	return obj;
}
@Component("beans")
@Aspect    //声明切面 和<aop:aspect>功能一样
public calss Test{
    
	@Before("execution(* controller.*.*(..))") 
}

@订单号)
确定多个增强方法的优先级。

@Aspect
@Order(1)
public class Proxy(){
    ...}

案例:
Beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    
    
   <bean id="stu2" class="model.Student">
   		<property name="name"><value>Spring FrameWork Runingtime</value></property>
   		<property name="age"><value>22</value></property>
   </bean>
   
   <bean id="m1" class="aoptest.PutApp"></bean>
   <bean id="m2" class="aoptest.AddOne"></bean>
   
   <!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
   <aop:config>
   
   	<aop:pointcut expression="execution(* aoptest.PutApp.put())" id="pointOne"/>
   	
   	<aop:aspect id="aspectOne" ref="m2">
   		<aop:before method="before" pointcut-ref="pointOne"/>
   		<aop:after method="after" pointcut-ref="pointOne"/>
   	</aop:aspect>
   </aop:config>

</beans>

项目结构:
在这里插入图片描述
额外工具包:
在这里插入图片描述

添加一个:

public class AddOne {
    
	public void before() {
    
		System.out.println("before...");
	}
	public void after() {
    
		System.out.println("after...");
	}
}

放置应用程序:

public class PutApp {
    
	
	public void put() {
    
		System.out.println("主程序!");
	}
	

}

测试程序应用程序:

import aoptest.PutApp;

public class AppOne {
    
	public static void main(String[] args) {
    
		ApplicationContext context=new ClassPathXmlApplicationContext("Beans.xml");
		PutApp put=(PutApp) context.getBean("m1");
		
		put.put();
	}

}

结果:
在这里插入图片描述
注解案例:

<!--开启注解驱动-->
<context:annotation-config></context:annotation-config>
   <context:component-scan base-package="aspect"></context:component-scan>
   <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
//切入点,被加强方法
@Component("pointTest")
public class Point2 {
    
	//该注解声明再方法上就是一个切入点
    @Pointcut
    public void strengthMethod(){
    
        System.out.println("加强方法...");
    }

}

//切面,加强方法
@Component
@Aspect
public class AspectTest {
    
	//通知的方法引入切入点
    @Before("execution(public void Point2.strengthMethod(..))")
    public void one(){
    

        System.out.println("one ...");
    }
    @After("execution(public void Point2.strengthMethod(..))")
    public void two(){
    
        System.out.println("two ...");
    }
}

//测试主类
public class AopMain {
    
    public static void main(String[] args) {
    
        ApplicationContext context= new ClassPathXmlApplicationContext("classpath:aop.xml");
        PointTest point=(PointTest) context.getBean("pointTest");
        point.strengthMethod();
    }
    
}

结果:

在这里插入图片描述

需要注意的是:@PointCut声明的就是切入点,@Aspect声明的是切面,通知如@Before引入切入点配置的切入点要正确。如果在@PointCut声明了类,则在@Before只需要声明方法。

Spring JDBC (Spring Data Access模块)

使用普通的JDBC数据库时,需要编写大量代码来处理异常,包括打开和关闭数据库连接。 Spring JDBC 框架负责所有底层细节,从打开连接、准备和执​​行 SQL 语句、处理异常、处理事务,最后关闭连接。

Spring JDBC在数据库中提供了多种方法以及对应的不同类和接口。 JdbcTemplate 类是管理所有数据库通信和异常处理的中心框架类。

JdbcTemplate 类执行 SQL 查询、更新语句和存储过程调用,执行结果集的迭代,并提取返回参数值。它还捕获 JDBC 异常并将其转换为 org.springframework.dao 包中定义的通用类,并提供更多信息和异常层次结构。

JdbcTemplate 类的实例配置为线程安全。因此,您可以配置 JdbcTemplate 的单个实例,然后安全地将这个共享引用注入到多个 DAO 中。

配置数据源和JdbcTemplate

工具包:
在这里插入图片描述

如果在数据库操作中有事务管理,还需要spring的 spring-tx-5.2.5.RELEASE.jar事务管理的包。如果整合的其他框架如mybatis则要使用spring的orm工具包spring-orm-5.2.5.RELEASE.jar。需要连接池的还要德鲁伊连接池。

jdbc操作数据库对象:Driver、DiverManager、Connecttion、Statement、PreparedStatement、ResultSet。前三部分是配置数据源,Statement是进行数据库会话,最后一部分是接收数据处理的结果。

Spring jdbc在此基础上做了封装,Spring jdbc的核心是jdbcTemplate,DDL操作都通过该对象实现,在IoC容器配置数据源DriverManagerDataSource,通过jdbcTemplate数据库会话和返回结果。

配置数据源和工厂模板

 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
      <property name="username" value="root"/>
      <property name="password" value="root"/>
   </bean>
   
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

除了在xml配置直接写入配置信息外也可从外部properties文件导入:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
jdbc.username=root
jdbc.password=root

在xml配置文件中导入外部配置文件并注入:

<!--导入外部properies文件-->
   <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

   <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
   <!--SEpl表达式语言-->
      <property name="driverClassName" value="${jdbc.driver}"></property>
      <property name="url" value="${jdbc.url}"></property>
      <property name="username" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
   </bean>

   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
      <property name="dataSource" ref="dataSource"></property>
   </bean>
</beans>

通过<context:property-placeholder>导入外部配置,${}表达式语言注入。

JdbcTemplate接口操作数据库

jdbcTemplate工厂提供了如下接口:
在这里插入图片描述
execute:可以执行所有SQL语句,一般用于执行DDL语句。
update:用于执行INSERT、UPDATE、DELETE等DML语句。
queryXxx,query:用于DQL数据查询语句。
batchUpdate:批量执行DML语句。

jdbc对Resulset的解析过程非常麻烦,但是spring jdbc提供了如下对象来判断返回集合(List)的类型,而不需要循环遍历:

//执行查询语句,返回一个指定类型的数据(返回一个对象)
public <T> T queryForObject(String sql, Class<T> requiredType)
/* int count= jdbcTemplate.queryForObject(sql, Integer.class); */


//执行查询语句,将一条记录放到一个Map中
public Map<String, Object> queryForMap(String sql)
/* Map<String, Object> map = jdbcTemplate.queryForMap(sql, 6); */


//执行查询语句,返回一个List集合,List中存放的是Map类型的数据
public List<Map<String, Object>> queryForList(String sql)
/* List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, 8); for (Map<String, Object> map : list) { System.out.println(map); } */

//执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据
public <T> List<T> query(String sql, RowMapper<T> rowMapper)


public <T> List<T> query(sql, new BeanPropertyRowMapper<>() )
/* public class BeanPropertyRowMapper<T> implements RowMapper<T> BeanPropertyRowMapper类实现了RowMapper接口 */

RowMapperBeanPropertyRowMapper

RowMapperBeanPropertyRowMapper类都是用于解析自定义的java bean对象,该对象是spring jdbc提供用来映射java bean对象和数据库表的数据项(一行数据),并返回包含所有数据项的list集合。

但是java bean对象的成员变量和数据项之间的对应关系需要声明,方法是实现RowMapperBeanPropertyRowMapper对象。(和mybatis有区别,其声明更简单只需要resultType=bean即可,字段与成员变量的关系不需要声明)具体如下:

public static class StudentMapper implements RowMapper<User>{
    

     @Override
      public User mapRow(ResultSet resultSet, int i) throws SQLException {
    
          User user=new User();
          user.setUsername(resultSet.getString("username"));
          user.setPassword(resultSet.getString("password"));
          return user;
      }
}

RowMapper的实现类重写mapRow方法,将ResultSet的结果,与User的成员变量关联,最终返回一个User对象,再将实现类作为传递给query方法,最终返回元素是User的List集合List<User>

JdbcTemplate jdbcTemplate=(JdbcTemplate) context.getBean("jdbcTemplate");
String sql="select * from user";
//实例化实现类作为参数
List<User> userList=jdbcTemplate.query(sql,new StudentMapper());
System.out.println(userList);

完整的实现案例:
xml配置文件参考前面的教程。

//User.java
package data;

public class User {
    
    private String username;
    private String password;

    public String getUsername() {
    
        return username;
    }

    public void setUsername(String username) {
    
        this.username = username;
    }

    public String getPassword() {
    
        return password;
    }

    public void setPassword(String password) {
    
        this.password = password;
    }

    @Override
    public String toString() {
    
        return "user{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}


//测试类
public class JdbcMain {
    
    public static void main(String[] args) {
    
        ApplicationContext context= new ClassPathXmlApplicationContext("jdbc.xml");
        JdbcTemplate jdbcTemplate=(JdbcTemplate) context.getBean("jdbcTemplate");
        String sql="select * from user";
        List<User> userList=jdbcTemplate.query(sql,new StudentMapper());
        System.out.println(userList);
    }

    public static class StudentMapper implements RowMapper<User>{
    

        @Override
        public User mapRow(ResultSet resultSet, int i) throws SQLException {
    
            User user=new User();
            user.setUsername(resultSet.getString("username"));
            user.setPassword(resultSet.getString("password"));
            return user;
        }
    }
}

RowMapper的作用是将对象的成员变量与数据库表的数据项关联起来。查询方法的返回结果是一个List集合。

传参依然通过?占位:

@Repository
public class UserDao {
    
 
     @Autowired
     private JdbcTemplate jdbcTemplate;
     
     public User get(int id){
    
         String sql="select id,name,deptid from user where id=?";
         RowMapper<User> rowMapper=new BeanPropertyRowMapper<User>(User.class);
         return jdbcTemplate.queryForObject(sql, rowMapper,id);
     }
 }

在这里插入图片描述

JdbcTemplate的基本使用

自动装配注入属性

JdbcTemplate有IoC容器管理通过自动装配获取对象:

@Autowired
    public JdbcTemplate jdbcTemplate;

通过自动组装将查询到的数据映射到ORM模型。

//dao层
public class UserMapper implements RowMapper<User> {
    
    @Override
    public User mapRow(ResultSet resultSet, int i) throws SQLException {
    
        User user=new User();
        user.setPassword(resultSet.getString("username"));
        user.setPassword(resultSet.getString("password"));
        return user;
    }
}

//service层
public class UserService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

   
    public List<User> selectUsers(){
    
        String sql="select * from user";
        List<User> userList=jdbcTemplate.query(sql,new UserMapper());
        return userList;
    }
}

Spring Transactions

交易理念
事务是数据库操作的基本单元,逻辑上的操作要么都成功,要么都失败回溯。
交易管理
一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:

  • 原子性:事务应被视为单个操作单元,这意味着整个操作序列要么成功,要么失败。

  • 一致性:指数据库的引用完整性、表中唯一主键等的一致性。

  • 隔离性:具有相同数据集的多个事务可能会同时处理,每个事务应与其他事务隔离,以防止数据损坏。

  • 持久性:一旦事务完成所有操作,事务的结果必须是永久的,不能因系统故障而从数据库中删除。

真正的 RDBMS 数据库系统将保证每个事务的所有四个属性。使用 SQL 发布到数据库的事务的简单视图如下:

  1. 使用开始事务命令来启动事务。

  2. 使用 SQL 查询语句执行各种删除、更新或插入操作。

  3. 如果所有操作都成功,则执行提交操作,否则回滚所有操作。

程序化事务管理
编程式事务管理方法是在源代码下管理事务。有极大地灵活性,但是它很难维护。成功逻辑和失败逻辑由程序决定。
声明式事务管理
声明式事务管理方法是通过配置,而不是源代码硬编程来管理事务。可以将事务管理从事务代码中分离出来(使用AOP原理)。只使用注释或基于配置的 XML 来管理事务。逻辑由spring框架管理。

工具包:
mysql-connector-java.jar和org.springframework.jdbc.jar 和 org.springframework.transaction.jar

xml配置


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<!--tx是事务的缩写-->

<!--创建数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
      <property name="username" value="root"/>
      <property name="password" value="baby5429"/>
   </bean>

<!--创建交互模板-->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>


<!--创建事务管理-->
<bean id="transaction" class="org.springframework.jdbc.DataSourceTransactionManager">
	<!--注入要管理的数据源-->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置事务-->
<tx:advice id="...">
	<tx:attributes>
		<!--为方法添加事务-->
		<tx:method name="" .../> //name对应方法名,其他参数就是上面表格中的
	</tx:attributes>
	
</tx:advice>

<!--通过aop将事务添加到方法上-->
<aop:config>
	<aop:pointcut ...>
	<aop:advisor advice-ref="" poincut-ref="">  //这里不再是普通的切面了是事务切面。 
</aop:config>

</beans>

以上是XML配置方法。当然,也可以注释掉:

在xml中启用事务注释功能:

<!--创建数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
      <property name="username" value="root"/>
      <property name="password" value="baby5429"/>
   </bean>

<!--创建交互模板-->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>


<!--创建事务管理-->
<bean id="transaction" class="org.springframework.jdbc.DataSourceTransactionManager">
	<!--注入要管理的数据源-->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!--上面配置数据库连接和对哪个数据库开启事务管理不可以省-->
<tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>
<!--transaction-manager 对应创建的事务-->

@交易
该注解在类和方法上都可以,只是包裹的范围不同。声明该类或方法为对数据库的操作以事务为基本单位,全程由spring管理。

@Service
@Transaction
public class Dao(){
    ...}

@Transaction的参数配置

  • propagation:事务传播行为,调用多个事务方法,以及如何管理这个过程。
范围 描述
必需的 如果被调用的方法没有事务,则创建一个新事务,否则使用共享事务。
REQUIRED_NEW 无论被调用的方法是否有事务,都会创建一个新的事务。
支持 如果当前有一个事务正在运行,则被调用的方法将在事务中运行,否则无法在事务中运行。
不支持 当前方法无法在事务中运行。如果有正在运行的事务,它将被暂停。
强制的 当前方法必须在事务中运行,否则会抛出异常
绝不 当前方法不能在事务中运行,否则会抛出异常
嵌套 如果有事务正在运行,则调用的方法将被嵌套,否则将开始一个新的事物。
  • ioslation:事务隔离级别
    多事务之间的操作是否产生影响。主要解决脏读(事务未提交完毕被读取了),不可重复读(已读取的事务被修改了),虚读等问题。
    在这里插入图片描述

  • timeout:超时时间
    事务在一定时间内提交否者回滚。

  • readOnly:是否只读
    默认值为false,管控是是否只读。

  • noRollbackFor:
    设置出现那些异常不进行回滚。

  • rollbackFor
    设置出现那些异常进行回滚

通过配置类实现完全注解开发:
@启用事务管理开启事务,注意其他注解也相应开启。

@豆创建连接池和数据源以及JdbcTemplate对象和事务管理器。

@交易声明事务。

Spring MVC

Spring Web MVC 框架提供了模型-视图-控制架构和组件,可用于开发灵活、松散耦合的 Web 应用程序。 MVC 模式导致应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)分离,同时在这些元素之间提供松散耦合。

  • 模型封装应用程序数据,通常由 POJO 组成。

  • 视图主要用于呈现模型数据,通常它生成客户端浏览器可以解释的 HTML 输出。

  • 控制器主要用于处理用户请求并构建适当的模型并将其传递给视图进行渲染。

Spring 的 Web 模型-视图-控制器 (MVC) 框架是围绕 DispatcherServlet 类设计的,该类将请求分发到不同的处理程序,并辅以可配置的处理程序映射、视图解析、本地化、时区、主题解析)以及对文件上传的支持。

Spring MVC框架部署

不同于非框架开发基于Servlet,每个Servlet对应一个数据处理。Spring MVC框架是围绕 DispatcherServlet 设计的,所有http请求发送到DispatcherServlet再有映射信息传递到对应的处理逻辑,避免了编写众多的Servlet。
在这里插入图片描述
SpringMVC 是构建在 Servlet 基础之上的,它对外提供了一个名为 DispatchServlet 的类,这个类相当于是 SpringMVC 和 Servlet API 的一个交界点。它也是 SpringMVC 当中对于请求处理的一个分发器。(它将 Servlet 传递过来的请求根据 URL 分发给对应的 Controller)

在 web.xml 文件里配置 url-pattern ,也可以起到分发请求的作用(或者@WebServlet)。
但是,如果有大量的 URL 都需要在 web.xml 进行配置时,整个 web.xml 就变成了一个灾难。
因此 Web 框架都选择避免 web.xml,在此之上构建自己的请求分发机制(通过一个sertvlet判断分发请求)。

所以部署时只需要在web.xml中部署DispatcherServlet即可,如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>FastServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup> <!--该servlet必须最先加载-->
    </servlet>

    <servlet-mapping>
        <servlet-name>FastServlet</servlet-name>
        <url-pattern>/*</url-pattern> <!--对所有路径作用-->
    </servlet-mapping>
</web-app>

除了修改Servlet以提供Spring提供的DispatcherServlet之外,还有两点需要注意:

  1. 添加的load-on-startup参数用于告诉Container在启动时加载这个Servlet(而不是在收到请求时加载)
  2. 使用 /* URL 表示所有请求都将由 DispatcherServlet 处理。这也会导致静态页面无法访问(因为静态页面不是通过DispatcherServlet管理的),需要释放静态资源(释放是由spring管理的,写在spring配置文件中)。另外,对于jsp页面,将/*改为/进行jsp Release(jsp也是servlet,不需要DispatcherServlet管理)。
  3. 当Spring MVC初始化时,它会在应用程序的WEB-INF目录中搜索Spring MVC配置文件。您还可以将 Spring MVC 配置文件存储在应用程序目录中的任何位置,但需要使用 servlet 的 init-param 元素来加载配置。 file,通过contextConfigLocation参数指定Spring MVC配置文件的位置。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://JAVA.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>


        <load-on-startup>1</load-on-startup>


    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

添加工具包(mvc框架的核心是web和webmvc的jar包):
在这里插入图片描述
使用spring MVC的配置文件(spring-mvc.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

  
   <!--开启组件扫描-->
    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="springdemo"/>

	<!--开启mvc注解扫描-->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

Spring MVC的第一个程序

spring配置文件中静态资源的释放:

<mvc:resources mapping="/html/** " location="/html/" />
css/js/图片等资源同理

spring MVC框架默认使用注解,需要先开启mvc注解扫描,相对的@WebServlet比web.xml更方便,该框架也是如此。主要的注解有:@控制器 将该类注册为DispatcherServlet 的实现类,并在该类中进行逻辑处理。@RequestMapping(value = “/hello”, method = RequestMethod.GET)配置url路径和方法。@ResponseBody响应数据。

//对象的配置后写一个controller
package springdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/book")
public class BookCtrl {
    

    @RequestMapping("/add")
    @ResponseBody
    public String add(){
    
        return "add";
    }
    @RequestMapping("/list")
    public void list(){
    
        System.out.println("list");
    }

}

除了注解之外,还可以在Spring mvc配置文件中进行配置:

 <!-- LoginController控制器类,映射到"/login" -->
    <bean name="/login" class="springmvcdemo.controller.LoginController"/>
    <!-- LoginController控制器类,映射到"/register" -->
    <bean name="/register" class="springmvcdemo.controller.RegisterController"/>

通过上面的配置后访问/book/add会在页面打印add,访问/book/list会在控制台打印list:
在这里插入图片描述
在这里插入图片描述
@Controller注解
@Controller 注解用于声明某类的实例是一个控制器。

import org.springframework.stereotype.Controller;
@Controller
public class IndexController {
    
    // 处理请求的方法
}

@RequestMapping注解
一个控制器内有多个处理请求的方法,每个方法负责不同的请求操作,而 @RequestMapping 就负责将请求映射到对应的控制器方法上(相当于一个个的HtttpServlet)。

在基于注解的控制器类中可以为每个请求编写相应的处理方法。使用@RequestMapping注解将请求和处理方法一对一映射。

@RequestMapping 注解可以用在类或方法上。用在类上,表示类中所有响应请求的方法都使用这个地址作为父路径。

  • value 属性
    value 属性是地址映射,是 @RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名。

  • path属性
    path 属性和 value 属性都用来作为映射使用。即 @RequestMapping(value=“toUser”) 和 @RequestMapping(path=“toUser”) 都能访问 toUser() 方法。path 属性支持通配符匹配。

  • name属性
    name属性相当于方法的注释,使方法更易理解。

  • method属性
    method 属性用于表示该方法支持哪些 HTTP 请求。如果省略 method 属性,则说明该方法支持全部的 HTTP 请求。

  • params属性
    params 属性用于指定请求中规定的参数,表示请求中必须包含 type 参数时才能执行该请求。即 http://localhost:8080/hello?type=xxx 能够正常访问 toUser() 方法。

  • header属性
    header 属性表示请求中必须包含某些指定的 header 值。@RequestMapping(value = “toUser”,headers = “Referer=http://www.xxx.com”) 表示请求的 header 中必须包含了指定的“Referer”请求头,以及值为“http://www.xxx.com”时,才能执行该请求。

  • consumers属性
    consumers 属性用于指定处理请求的提交内容类型(Content-Type),例如:application/json、text/html。如 @RequestMapping(value = “toUser”,consumes = “application/json”)。

  • produces属性
    produces 属性用于指定返回的内容类型,返回的内容类型必须是 request 请求头(Accept)中所包含的类型。如 @RequestMapping(value = “toUser”,produces = “application/json”)。
    除此之外,produces 属性还可以指定返回值的编码。如 @RequestMapping(value = “toUser”,produces = “application/json,charset=utf-8”),表示返回 utf-8 编码。

使用@RequestMapping完成映射,包括四个方面的信息项:请求URL、请求参数、请求方法和请求头。

Spring MVC与servlet的对比

Servlet处理逻辑,HtppServlet继承了父类的方法判断逻辑,一条路径对应一个servlet。通过判断分发到相应的doGet或者其他方法。写处理逻辑就可以了。

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends HttpServlet {
    
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        //使用PrintWriter.write()方法向前台页面输出内容
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("Hello World");
        writer.close();
    }
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        this.doGet(req, resp);
    }
}

DispatcherServlet 用于Spring 的Servlet。所有路径均发送至此处。该类解析路径和方法以确定并将它们分发给其子类。

@Controller    //注册为DispatcherServlet的子类
@RequestMapping("/book")   //配置路径和方法默认get,value = "/book", method=“get”
public class BookCtrl {
    

    @RequestMapping("/add")
    @ResponseBody
    public String add(){
    
        return "add";
    }
    @RequestMapping("/list")
    public void list(){
    
        System.out.println("list");
    }

}

Spring处理请求流程

在这里插入图片描述
SpringMVC 的执行流程如下。

  1. 用户点击某个请求路径发起HTTP请求,该请求会提交给DispatcherServlet(前端控制器);
  2. DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一条执行链(HandlerExecutionChain)。
  3. DispatcherServlet将执行链返回的Handler信息发送给HandlerAdapter(处理器适配器);
  4. HandlerAdapter根据Handler信息找到并执行对应的Handler(通常称为Controller);
  5. Handler执行后,会向HandlerAdapter返回一个ModelAndView对象(Spring MVC的底层对象,包括Model数据模型和View视图信息);
  6. HandlerAdapter接收到ModelAndView对象后,返回给DispatcherServlet;
  7. DispatcherServlet接收到ModelAndView对象后,会请求ViewResolver(视图解析器)解析视图;
  8. ViewResolver根据View信息匹配对应的视图结果并返回给DispatcherServlet;
  9. DispatcherServlet接收到具体的View后,渲染视图,将Model中的模型数据填充到View中的请求字段中,生成最终的View;
  10. 视图负责将结果显示给浏览器(客户端)。

SpringMVC核心组件

Spring MVC涉及的组件有DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)和View(视图)。

  1. DispatcherServlet
    DispatcherServlet 是前端控制器,从图 1 可以看出,Spring MVC 的所有请求都要经过 DispatcherServlet 来统一分发。DispatcherServlet 相当于一个转发器或中央处理器,控制整个流程的执行,对各个组件进行统一调度,以降低组件之间的耦合性,有利于组件之间的拓展。
  2. HandlerMapping
    HandlerMapping 是处理器映射器,其作用是根据请求的 URL 路径,通过注解或者 XML 配置,寻找匹配的处理器(Handler)信息。
  3. HandlerAdapter
    HandlerAdapter 是处理器适配器,其作用是根据映射器找到的处理器(Handler)信息,按照特定规则执行相关的处理器(Handler)。
  4. Handler
    Handler 是处理器,和 Java Servlet 扮演的角色一致。其作用是执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至 ModelAndView 对象中。
  5. View Resolver
    View Resolver 是视图解析器,其作用是进行解析操作,通过 ModelAndView 对象中的 View 信息将逻辑视图名解析成真正的视图 View(如通过一个 JSP 路径返回一个真正的 JSP 页面)。
  6. View
    View 是视图,其本身是一个接口,实现类支持不同的 View 类型(JSP、FreeMarker、Excel 等)。

Spring MVC传递参数

Spring MVC Controller接收请求参数的方式有很多种:

  • 通过实体bean接收请求参数

实体 bean 接收 get 和 post 提交请求方法的请求参数。请注意,bean 的属性名称必须与请求参数名称相同。

@RequestMapping("/login")
public String login(User user, Model model) {
    
    if ("bianchengbang".equals(user.getName())
            && "123456".equals(user.getPwd())) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
  • 通过处理方法的形参接收请求参数

通过处理方法的形参接收请求参数,是指直接将形参写入控制器类相应方法的形参中,即形参名称与请求参数名称完全一致。

@RequestMapping("/login")
public String login(String name, String pwd, Model model) {
    
    if ("bianchengbang".equals(user.getName())
            && "123456".equals(user.getPwd())) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
  • ==通过HttpServletRequest接收请求参数==
@RequestMapping("/login")
public String login(HttpServletRequest request, Model model) {
    
    String name = request.getParameter("name");
    String pwd = request.getParameter("pwd");
   
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";   //return相当于resopnse.getWriter.write();
    }
}
  • 通过@PathVariable接收URL中的请求参数
@RequestMapping("/login/{name}/{pwd}")
public String login(@PathVariable String name, @PathVariable String pwd, Model model) {
    
   
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
  • 通过@RequestParam接收请求参数

在方法入参处使用 @RequestParam 注解指定其对应的请求参数。@RequestParam 有以下三个参数:
value:参数名;required:是否必须,默认为 true,表示请求中必须包含对应的参数名,若不存在将抛出异常;defaultValue:参数默认值。

@RequestMapping("/login")
public String login(@RequestParam String name, @RequestParam String pwd, Model model) {
    
   
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
  • 通过@ModelAttribute接收请求参数
    @ModelAttribute 注解用于将多个请求参数封装到一个实体对象中,从而简化数据绑定流程,而且自动暴露为模型数据,在视图页面展示时使用。
@RequestMapping("/login")
public String login(@ModelAttribute("user") User user, Model model) {
    
   
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
    
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
    
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}

Spring MVC文件上传

多部分解析器接口

在Spring MVC中实现文件上传非常容易,它提供了对文件上传的直接支持,即MultpartiResolver接口。 MultipartResolver用于处理上传请求,将上传请求打包成可以直接获取文件的数据,从而方便操作。

MultpartiResolver接口有以下两个实现类:

  • StandardServletMultipartResolver:使用Servlet 3.0标准上传方法。

  • CommonsMultipartResolver:使用了 Apache 的 commons-fileupload 来完成具体的上传操作。
    在这里插入图片描述
    上传文件部署

  • 导入工具包
    文件上传使用 Apache Commons FileUpload 组件,需要导入 commons-io-2.4.jar 和 commons-fileupload-1.2.2.jar 两个 jar 文件。

  • 配置MultipartResolver

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="5000000" />
    <property name="defaultEncoding" value="UTF-8" />
</bean>

defaultEncoding:请求的编码格式,默认为 ISO-8859-1,此处设置为 UTF-8(注:defaultEncoding 必须和 JSP 中的 pageEncoding 一致,以便正确读取表单的内容)。
maxUploadSize:上传文件大小上限,单位为字节。

  • 编写一个文件上传表单页面

负责文件上传表单的编码类型必须是“multipart/form-data”类型。基于表单的文件上传需要使用enctype属性,将其值设置为multipart/form-data,并将表单提交方式设置为post。

表单的enctype属性指定表单数据的编码方法。该属性有以下三个值。

  • application/x-www-form-urlencoded:这是默认的编码方式,只处理表单字段中的value属性值。
  • multipart/form-data:这种编码方式以二进制流的形式处理表单数据,并将file字段指定的文件内容封装到请求参数中。
  • text/plain:只有当表单的action属性是“mailto:”URL形式时才使用这种编码方法。主要适合直接通过表单发送邮件。
<form action="${pageContext.request.contextPath }/fileupload" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="myfile"><br> 
        文件描述:<input type="text" name="description"><br> 
        <input type="submit" value="提交">
    </form>
  • 创建 POJO 类

在POJO类中声明一个MultipartFile类型属性来封装上传的文件信息

public class FileDomain {
    
    private String description;
    private MultipartFile myfile;
    /** 省略setter和getter参数*/
}
  • 编写控制器
    控制器接收fileUpload文件
@Controller
public class FileUploadController {
    
    // 得到一个用来记录日志的对象,这样在打印信息时能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(FileUploadController.class);
    @RequestMapping("getFileUpload")
    public String getFileUpload() {
    
        return "fileUpload";
    }
    /** * 单文件上传 */
    @RequestMapping("/fileupload")
    public String oneFileUpload(@ModelAttribute FileDomain fileDomain, HttpServletRequest request) {
    
        /* * 文件上传到服务器的位置“/uploadfiles”,该位置是指 workspace\.metadata\.plugins\org.eclipse * .wst.server.core\tmp0\wtpwebapps, 发布后使用 */
        String realpath = request.getServletContext().getRealPath("uploadfiles");
        String fileName = fileDomain.getMyfile().getOriginalFilename();
        File targetFile = new File(realpath, fileName);
        if (!targetFile.exists()) {
    
            targetFile.mkdirs();
        }
        // 上传
        try {
    
            fileDomain.getMyfile().transferTo(targetFile);
            logger.info("成功");
        } catch (Exception e) {
    
            e.printStackTrace();
        }
        return "showFile";
    }
}

Spring Test单元测试

新单元测试中提供@RunWith注解,该注解是一个运行器,用来指定运行的环境。@RunWith注解在一个类上表示是一个测试类,@Test注释在一个方法发上表示为测试方法。

使用配置文件进行单元测试

配置文件的单元测试

有配置文件,在测试时必须要先加载配置文件,但每次用ClassPathXmlApplicationContext加载配置文件很繁琐,org.springframework.test.context提供了@ContextConfiguration注解用来快速导入配置文件。

//导入单个文件
@ContextConfiguration("classpath:bean1.xml")

//导入多个文件
@ContextConfiguration(locations = {
     "classpath*:/spring1.xml", "classpath*:/spring2.xml" })

要使用该注解,需要导入 spring-test 依赖,并且还需要在 runner 中指定 spring 环境:

@RunWith(SpringRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)

SpringRunner继承SpringJUnit4ClassRunner,不扩展任何功能;这只是一个简称。

测试步骤:声明运行器并配置运行环境——@ContextConfiguration导入配置文件初始化IoC容器——@Test声明测试方法

@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:bean1.xml")
public class AnoationApp {
    

    @Autowired
    private Book book;

    @Autowired
    private Book1 book1;




    @Test
    public void method1(){
    
        System.out.println(book);
    }

    @Test
    public void method2(){
    
        System.out.println(book1);
    }

}

配置类的单元测试

配置同样通过@ContextConfiguration注解导入,该注解默认时xml导入需要声明属性。如上面所示locations时声明xml,只是xml的位置;classes声明配置类,值是配置类的位置。

//配置类定义
@Configuration
@ComponentScan(basePackages = "pojo")
public class ApplicationConfig {
    
    @Bean
    public Cup method1(){
    
        return new Cup();
    }

}

//注解注册bean
@Component
public class Cup1 {
    
    @Value("blue cup")
    private String name;
    @Value("15")
    private int width;

    public void setName(String name) {
    
        this.name = name;
    }

    public void setHeight(int height) {
    
        this.width = height;
    }

    @Override
    public String toString() {
    
        return "Cup{" +
                "name='" + name + '\'' +
                ", height=" + width +
                '}';
    }
}

//单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class App2 {
    

    @Autowired
    private Cup cup;

    @Autowired
    private Cup1 cup1;

    @Test
    public void method(){
    
        System.out.println(cup);
        System.out.println("-----------");
        System.out.println(cup1);
    }
}
. . .

相关推荐

额外说明

探索 box-shadow 的属性

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta

额外说明

Linux搭建我的世界服务器 + 公网远程联机教程「不需要公网IP」

文章目录 前言 1. 安装JAVA 2. MCSManager安装 3.局域网访问MCSM 4.创建我的世界服务器 5.局域网联机测试 6.安装cpolar内网穿透 7. 配置公网访问地址 8.远程联机测试 9. 配置固定远程联机端口地址 9.1 保留一

额外说明

企业级实战——品优购电商系统开发-012345介绍、目标、电商模式及行业技术特点

QQ 1274510382 Wechat JNZ_aming 商业联盟 QQ群538250800 技术搞事 QQ群599020441 解决方案 QQ群152889761 加入我们 QQ群649347320 共享学习 QQ群674240731 纪年科技am

额外说明

Unity当中的灯光类型

文章目录 前言 一、Directional平行光 二、Point点灯 三、Spot 聚光灯 四、Area面光灯,只用于烘培 前言 Unity当中的灯光类型 一、Directional平行光 Unity当中最重要的灯管类型,类似现实中的太阳光 二、Poin

额外说明

Oracle统计大小语句(用户、表、分区表、索引等)

查询整个数据库剩余和使用的表空间大小使用情况 SELECT df.tablespace_name "表空间名", totalspace "总空间M", freespace "剩余空间M", round((1 -

额外说明

VUE中solt内容插槽与ng4中投影的使用

一、在VUE中slot的认识 表示父组件分发内容的插槽,父组件中定义这个后,可以直接在子组件中往里面填充内容 1、一个父组件就定义一个slot <div class="page"> <!-- 定义一个分发内容的插槽 --> <slot><

额外说明

Git分支的合并和删除

在Git中,分支的合并和删除是常见的操作。本文将介绍如何将一个分支合并到另一个分支,并删除已经完成的分支。 合并分支: git checkout [target_branch]:切换到目标分支。 git merge [source_branch]:将源分

额外说明

《天天数学》连载14:一月十四日

1月14日 数学格言 想象比知识更重要。——阿尔伯特·爱因斯坦(Albert Einstein) 数学习题 求半径 r = 3 16 π 2 3 4 π

额外说明

Windows系统丢失StructuredQuery.dll文件出现系统错误问题

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个StructuredQuer

ads via 小工具