1. 体系结构
下载地址:http://repo.spring.io/release/org/springframework/spring/
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。
1.1. 核心容器
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
l spring-core 框架的基本组成部分,包括 IoC 和依赖注入功能。
l spring-beans 提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
l 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)等。
l spring-expression 提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。。
1.2. 数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
l JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。
l ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。(其他orm框架整合点)
l OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。(json)
l JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。
l Transactions 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)(一般用在applicationContext.xml中使用声明式配置)
1.3. Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
l Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。。
l Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。
l Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
l Web-Portlet 模块提供了用于Portlet(一种门户导入的组件)环境的MVC实现,并反映了spring-webmvc模块的功能。
1.4. 其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
l AOP 模块提供了面向方面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
l Aspects 模块提供了与 AspectJ(面向切面编程) 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
l Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
l Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
l Test 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
2. IOC容器
Ioc容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置(通过 XML,Java 注释或 Java 代码来配置)它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。
Spring工作视图:
视图中可以看出,spring容器起承前启后作用,使程序耦合度大大降低。
Spring提供了2种不同类型的容器:
l Spring BeanFactory容器--基本弃用
最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的
l Spring ApplicationContext容器
该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义
注:
ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常建议超过 BeanFactory。BeanFactory 仍然可以用于轻量级的应用程序,如移动设备或基于 applet 的应用程序,其中它的数据量和速度是显著。
2.1. Spring BeanFactory容器
使用XmlBeanFactory加载xml文件:
XmlBeanFactory xbf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
HelloWorld hw1 = (HelloWorld)xbf.getBean("helloworld");
此容器在spring4之后基本弃用
2.2. Spring ApplicationContext容器
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface 接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
l FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
ApplicationContext context = new
FileSystemXmlApplicationContext("C:/Users/ZARA/workspace/HelloSpring/src/Beans.xml");
l ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
l WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean,相对程序的位置。
2.3. Bean定义
bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。
bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:
l 如何创建一个 bean
l bean 的生命周期的详细信息
l bean 的依赖关系
Bean配置元数据属性:
属性 | 描述 |
class | 必选属性,用于指定用来创建bean的pojo类 |
id/name | bean的唯一标识符,操作bean是利于快速定位 |
scope | 定义bean作用域 |
constructor-arg | 注入依赖关系,构造函数注入 |
properties | 注入依赖关系 |
autowiring mode | 注入依赖关系 |
lazy-initialization mode | 延迟初始化时机,使用后,IoC容器在它第一次调用时创建bean而不是容器启东时 |
initialization方法 | bean的其余属性设置完成后代用的回调方法,用于初始化bean |
destruction方法 | 包含该bean的容器被销毁时调用的回调方法,用于销毁bean |
Spring配置元数据方式:
l 基于xml配置文件
l 基于注解
l 基于java配置
包含初始化、延迟初始化、销毁方法的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="..." class="..."> <!-- bean相关配置 --> </bean> <!-- 延迟初始化 --> <bean id="..." class="..." lazy-init="true"> <!-- bean相关配置 --> </bean> <!-- 定义初始化方法 --> <bean id="..." class="..." init-method="..."> <!-- bean相关配置 --> </bean> <!-- 定义销毁方法 --> <bean id="..." class="..." destroy-method="..."> <!-- bean相关配置 --> </bean> </beans>
|
2.4. Spring bean 作用域
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。
Spring 框架支持以下五个作用域:
作用域 | 描述 |
singleton | 定义bean时的默认值,在springIOC中仅存在一个bean实例,bean以单例方式存在 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,改作用于仅适用于WebApplicationContext环境 |
Singleton:
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:
<!-- 定义单例模式 --> <bean id="..." class="..." scope="singleton"> <!-- bean相关配置 --> </bean> |
Prototype:
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
为了定义 prototype 作用域,你可以在 bean 的配置文件中设置作用域的属性为 prototype,如下所示:
<!-- 定义非单例模式 --> <bean id="..." class="..." scope="prototype"> <!-- bean相关配置 --> </bean> |
2.5. Spring bean 声明周期
当一个 bean 被实例化时,它可能需要执行一些初始化操作使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。即:一个bean从实例化开始到从容器中移除为一个生命周期。
初始化和销毁方式:
l 实现接口
//初始化回调方法 public class ExampleBean implements org.springframework.beans.factory.InitializingBean{ public void afterPropertiesSet() throws Exception{ // 初始化定义 } } //销毁回调方法 public class ExampleBean implements org.springframework.beans.factory.DisposableBean{ public void destroy() throws Exception{ // 销毁处理 } } |
l Xml配置元数据
<bean id="exampleBean" class="examples.ExampleBean" init-method="init" destroy-method="destroy"/> 其中init-method和destory-method属性指定的方法为带有void修饰的无参方法。一般直接定义在pojo类中。 public void init(){} public void destroy(){} |
如果你在非 web 应用程序环境中使用 Spring 的 IoC 容器;例如在丰富的客户端桌面环境中;那么在 JVM 中你要注册关闭 hook。这样做可以确保正常关闭,为了让所有的资源都被释放,可以在单个 beans 上调用 destroy 方法。
建议你不要使用 InitializingBean 或者 DisposableBean 的回调方法,因为 XML 配置在命名方法上提供了极大的灵活性。
默认初始化和销毁方法:
如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。框架使用元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示
<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" default-init-method="init" default-destroy-method="destroy">
<bean id="..." class="..."> <!-- bean相关配置 --> </bean>
</beans> |
2.6. Spring bean 后置处理器
BeanPostProcessor 接口定义回调方法,可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。也可以在 Spring 容器通过插入一个或多个 BeanPostProcessor 的实现来完成实例化,配置和初始化一个bean之后实现一些自定义逻辑回调方法。
可以同时配置多个 BeanPostProcesso r接口,通过设置 BeanPostProcessor 实现的 Ordered 接口提供的 order 属性来控制这些 BeanPostProcessor 接口的执行顺序。
BeanPostProcessor 可以对 bean(或对象)实例进行操作,这意味着 Spring IoC 容器实例化一个 bean 实例,然后 BeanPostProcessor 接口进行它们的工作。
ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器,然后通过在容器中创建 bean,在适当的时候调用它。
实现实例化前和实例化后的逻辑添加:
HelloWorld.java
public class HelloWorld { private String message; public void init(){ System.out.println("message初始化!"); } public void destroy(){ System.out.println("message销毁!"); } public void getMessage() { System.out.println("get方法:"+message); } public void setMessage(String message) { this.message = message; } }
InitHelloWorld.java
public class InitHelloWorld implements BeanPostProcessor { //初始化之前执行 public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException { System.out.println("调用postProcessBeforeInitialization"); return bean; } //初始化之后执行 public Object postProcessAfterInitialization(Object bean,String beanName) throws BeansException { System.out.println("调用postProcessAfterInitialization"); return bean; } }
mainApp.java
public static void main(String[] args) { //利用ApplicationContext容器 AbstractApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); HelloWorld hw = (HelloWorld)ac.getBean("helloworld"); //使用BeanBactory容器 hw.getMessage(); ac.registerShutdownHook(); }
Bean.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id = "helloworld" class = "com.fukai.HelloWorld" init-method="init" destroy-method="destroy"> <property name = "message" value = "你好世界!"></property> </bean> <bean id = "initHelloWorld" class = "com.fukai.InitHelloWorld"></bean> </beans>
输出结果: 调用postProcessBeforeInitialization message初始化! 调用postProcessAfterInitialization get方法:你好世界! message销毁! |
2.7. Spring bean 定义继承
bean 定义可以包含很多的配置信息,包括构造函数的参数,属性值,容器的具体信息例如初始化方法,静态工厂方法名,等等。
子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值。
Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置。
当你使用基于 XML 的配置元数据时,通过使用父属性,指定父 bean 作为该属性的值来表明子 bean 的定义。
指定父类后,子类若没有重写父类包含的属性方法,则默认继承父类方法逻辑和属性值。
Xml指定方式:
<bean id = "initHelloWorld" class = "com.fukai.InitHelloWorld" parent = "父类注册的bean id"></bean> |
Bean定义模板
定义模板后,可以更加快捷的被其他子类bean定义使用。在定义一个 Bean 定义模板时,不应该指定类的属性,而应该指定带 true 值的抽象属性,如下所示:
<?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="beanTeamplate" abstract="true"> <property name="message1" value="Hello World!"/> <property name="message2" value="Hello Second World!"/> <property name="message3" value="Namaste India!"/> </bean> <bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="beanTeamplate"> <property name="message1" value="Hello India!"/> <property name="message3" value="Namaste India!"/> </bean> </beans> |
父 bean 自身不能被实例化,因为它是不完整的,而且它也被明确地标记为抽象的。当一个定义是抽象的,它仅仅作为一个纯粹的模板 bean 定义来使用的,充当子定义的父定义使用。
3. Spring依赖注入
3.1. Spring 基于构造函数的依赖注入
基于构造函数注入时,构造函数必须含有参数
如果存在不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。解决方式:
l 具体指定
l 使用参数类型type区分
l 使用构造函数参数下标 --- 推荐
// 具体指定 public class Foo { public Foo(Bar bar, Baz baz) { // ... } } <beans> <bean id="foo" class="x.y.Foo"> <!-- 必须按照构造函数参数顺序依次写入 --> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
// type区分 public class Foo { public Foo(Bar bar, Baz baz , String name, int age) { // ... } } <beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> <constructor-arg type="int" value = "28"/> <constructor-arg type="java.lang.String" value = "hello"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
// index下标 public class Foo { public Foo(Bar bar, String name, int age) { // ... } } <beans> <bean id="foo" class="x.y.Foo"> <constructor-arg index="0" ref = "bar"/> <constructor-arg index="1" value = "hello"/> <constructor-arg index="2" value = "28"/> </bean> <bean id="bar" class="x.y.Bar"/> </beans> |
3.2. Spring 基于设值函数的依赖注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在 bean 上调用设值函数,基于设值函数的 DI 就完成了。
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">
<!-- Definition for textEditor bean --> <bean id="textEditor" class="com.tutorialspoint.TextEditor"> <property name="spellChecker" ref="spellChecker"/> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean> </beans> |
如果你要把一个引用传递给一个对象,那么你需要使用 标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性。
使用 p-namespace 实现 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="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean>
<bean name="jane" class="com.example.Person"> <property name="name" value="John Doe"/> </bean> </beans> |
上述 XML 配置文件可以使用 p-namespace 以一种更简洁的方式重写,如下所示:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="john-classic" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> </bean> <bean name="jane" class="com.example.Person" p:name="John Doe"/> </bean> </beans> |
-ref 部分表明这不是一个直接的值,而是对另一个 bean 的引用。
3.3. Spring 注入内部 Beans
inner beans 是在其他 bean 的范围内定义的 bean。因此在 或 元素内 元素被称为内部bean。类似于java中的内部类,当当前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="outerBean" class="..."> <property name="target"> <bean id="innerBean" class="..."/> </property> </bean> </beans> |
3.4. Spring 注入集合
针对java Collection类型的List、set、map、properties,spring提供了四种针对类型:
元素 | 描述 |
<list> | 它有助于连线,如注入一列值,允许重复。 |
<set> | 它有助于连线一组值,但不能重复。 |
<map> | 它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。 |
<props> | 它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。 |
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">
<!-- collection对象中包含基本属性时的注入 --> <!-- Definition for javaCollection --> <bean id="javaCollection" class="com.tutorialspoint.JavaCollection">
<!-- results in a setAddressList(java.util.List) call --> <property name="addressList"> <list> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </list> </property>
<!-- results in a setAddressSet(java.util.Set) call --> <property name="addressSet"> <set> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </set> </property>
<!-- results in a setAddressMap(java.util.Map) call --> <property name="addressMap"> <map> <entry key="1" value="INDIA"/> <entry key="2" value="Pakistan"/> <entry key="3" value="USA"/> <entry key="4" value="USA"/> </map> </property>
<!-- results in a setAddressProp(java.util.Properties) call --> <property name="addressProp"> <props> <prop key="one">INDIA</prop> <prop key="two">Pakistan</prop> <prop key="three">USA</prop> <prop key="four">USA</prop> </props> </property> </bean>
<!-- collection对象中包含其它bean对象的注入 --> <!-- 需要引用的bean的定义 --> <bean id="..." class="..."> <!-- Passing bean reference for java.util.List --> <property name="addressList"> <list> <ref bean="address1"/> <ref bean="address2"/> <value>Pakistan</value> </list> </property>
<!-- Passing bean reference for java.util.Set --> <property name="addressSet"> <set> <ref bean="address1"/> <ref bean="address2"/> <value>Pakistan</value> </set> </property>
<!-- Passing bean reference for java.util.Map --> <property name="addressMap"> <map> <entry key="one" value="INDIA"/> <entry key ="two" value-ref="address1"/> <entry key ="three" value-ref="address2"/> </map> </property> </bean> <!-- 传递空字符串 --> <bean id="..." class="exampleBean"> <property name="email" value=""/> </bean> <!-- 传递NULL值 --> <bean id="..." class="exampleBean"> <property name="email"><null/></property> </bean> </beans> |
4. Spring Beans 自动装配
自动装配可以理解为Spring容器根据bean之间的相互关系自动连线完成注入的过程。自动装配不使用<constructor-arg>和<property>元素,从而简化了注入的编程量。使用<bean>元素的 autowire 属性为一个 bean 定义指定自动装配模式。
Spring提供的自动装配模式:
模式 | 描述 |
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。 |
由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 | |
由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 | |
类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 | |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
自动装配的局限性
当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不使用自动装配,它可能会使开发人员混淆的使用它来连接只有一个或两个 bean 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应该在使用它们之前考虑到自动装配的局限性和缺点。
限制 | 描述 |
重写的可能性 | 你可以使用总是重写自动装配的 <constructor-arg>和 <property> 设置来指定依赖关系。 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
4.1. Spring 自动装配 ‘byName’
这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans(必须包含需要引用的bean的set方法) 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
自动装配和显式装配可以共存
示例:
<?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">
<!-- Definition for textEditor bean --> <bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byName"> <property name="name" value="Generic Text Editor" /> </bean>
<!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
</beans> |
4.2. Spring 自动装配 ‘byType’
这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
自动装配和显式装配可以共存
示例:
<?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">
<!-- Definition for textEditor bean --> <bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byType"> <property name="name" value="Generic Text Editor" /> </bean>
<!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
</beans> |
4.3. Spring 由构造函数自动装配
这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 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">
<!-- Definition for textEditor bean --> <bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="constructor"> <property name="name" value="Generic Text Editor" /> </bean>
<!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
</beans> |
5. Spring 基于注解的配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。
注解连线在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。
启用方法:
在applicationContext.xml中添加<context:annotation-config/>,启用注解。
5.1. Spring @Required 注释
@Required 注释应用于 bean 属性的 set方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。
示例:
实体类: import org.springframework.beans.factory.annotation.Required; public class Student { private Integer age; private String name; @Required public void setAge(Integer age) { this.age = age; } public Integer getAge() { return age; } @Required public void setName(String name) { this.name = name; } public String getName() { return name; } } Bean.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" 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/>
<!-- Definition for student bean --> <bean id="student" class="com.tutorialspoint.Student"> <property name="name" value="Zara" /> <property name="age" value="11"/> </bean>
</beans> |
5.2. Spring @Autowired 注释
@Autowired 注释对在哪里和如何完成自动连接提供了更多的细微的控制。
@Autowired 注释可以在 setter 方法中被用于自动连接 bean,也可以用于注释容器、属性或者任意命名的可能带有多个参数的方法。
Setter 方法中的 @Autowired
import org.springframework.beans.factory.annotation.Autowired; public class TextEditor { private SpellChecker spellChecker; @Autowired public void setSpellChecker( SpellChecker spellChecker ){ this.spellChecker = spellChecker; } public SpellChecker getSpellChecker( ) { return spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } }
<?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/>
<!-- Definition for textEditor bean without constructor-arg --> <bean id="textEditor" class="com.tutorialspoint.TextEditor"></bean>
<!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"></bean>
</beans> |
属性中的 @Autowired
属性中使用 @Autowired 注释后,可以省略属性的 set 方法。当时使用 为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。
import org.springframework.beans.factory.annotation.Autowired; public class TextEditor { @Autowired private SpellChecker spellChecker; public TextEditor() { System.out.println("Inside TextEditor constructor." ); } public SpellChecker getSpellChecker( ){ return spellChecker; } public void spellCheck(){ spellChecker.checkSpelling(); } }
<context:annotation-config/> <!-- Definition for textEditor bean --> <bean id="textEditor" class="com.tutorialspoint.TextEditor"> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean> |
构造函数中的 @Autowired
一个构造函数使用 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用元素配置 bean ,构造函数也会被自动连接。
@Autowired public TextEditor(SpellChecker spellChecker){ System.out.println("Inside TextEditor constructor." ); this.spellChecker = spellChecker; }
<context:annotation-config/> <!-- Definition for textEditor bean without constructor-arg --> <bean id="textEditor" class="com.tutorialspoint.TextEditor"> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean> |
@Autowired 的(required=false)选项
默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。
private Integer age; private String name; @Autowired(required=false) public void setAge(Integer age) { this.age = age; } public Integer getAge() { return age; } @Autowired public void setName(String name) { this.name = name; } public String getName() { return name; } <context:annotation-config/>
<!-- Definition for student bean --> <bean id="student" class="com.tutorialspoint.Student"> <property name="name" value="Zara" /> <!-- age为非必配项 --> <!-- <property name="age" value="11"/> --> </bean> |
5.3. Spring @Qualifier 注释
注释类属性,当同时存在多个相同类型的bean时,将其注入时无法确定具体注入的是哪一个bean,容易出现混乱,在这种情况下,需使用@Qualifier 注释和 @Autowired 注释来指定哪一个真正的 bean 将会被装配来消除混乱。
import org.springframework.beans.factory.annotation.Qualifier; @Autowired @Qualifier("student1") private Student student;
<?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/> <!-- Definition for profile bean --> <bean id="profile" class="com.tutorialspoint.Profile"></bean>
<!-- Definition for student1 bean --> <bean id="student1" class="com.tutorialspoint.Student"> <property name="name" value="Zara" /> <property name="age" value="11"/> </bean>
<!-- Definition for student2 bean --> <bean id="student2" class="com.tutorialspoint.Student"> <property name="name" value="Nuha" /> <property name="age" value="2"/> </bean> |
5.4. Spring JSR-250 注释
基于 JSR-250 注释,它包括 @PostConstruct, @PreDestroy 和 @Resource 注释。
@PostConstruct 和 @PreDestroy 注释:
用于替换xml文件中init-method和destroy-method属性,结果相同。
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy;
public class HelloWorld { private String message; @PostConstruct public void init(){ System.out.println("message初始化!"); } @PreDestroy public void destroy(){ System.out.println("message销毁!"); } public void getMessage() { System.out.println("get方法:"+message); } public void setMessage(String message) { this.message = message; } }
<?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/>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld"> <property name="message" value="Hello World!"/> </bean> </beans> |
@Resource 注释
修饰变量或者setter方法,用于注入变量或引用,包含name属性,name属性为需要注入的bean的名称,它遵循byName自动连接语义。
import javax.annotation.Resource; public class TextEditor { private SpellChecker spellChecker; @Resource(name= "spellChecker") public void setSpellChecker( SpellChecker spellChecker ){ this.spellChecker = spellChecker; } public SpellChecker getSpellChecker(){ return spellChecker; } public void spellCheck(){ spellChecker.checkSpelling(); } } |
5.5. Spring 基于 Java 的配置
基于java的注解,可以直接用java代码实现xml文件内容,即:可以将基于java注解的代码看成是另一种xml文件。
l @Configuration
修饰的类可以作为sping IOC容器的bean定义来源(即告诉spring当前类类似于bean的xml定义文件),
容器启动时直接执行加载
l @Bean
修饰方法,修饰的方法必须能返回对象,次注释告诉spring其修饰的方法返回的对象为一个bean实例
l @Import
用于使用其它基于java注解的java文件中@Bean定义,类似于java中的import
l @Scope
说明bean的作用域
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Scope;
//告诉spring当前类为可执行文件 @Configuration //引入其他基于java注释修饰的java文件,引入后,可以直接使用其中定义的bean @Import(SpellChecker.class) public class HelloWorldConfig { //说明当前方法返回的bean需要注册定义,可以同时制定初始化和销毁方法 @Bean(initMethod="init",destroyMethod="destroy") //定义bean作用域 @Scope("prototype") public HelloWorld helloworld(){ //构造函数依赖注入(直接调用@bean定义的方法即可),需要注入的bean必须有相应的构造函数 HelloWorld h = new HelloWorld(spellChecker()); //直接注入属性值 h.setMessage("asdfasdf"); return h; } @Bean public SpellChecker spellChecker(){ return new SpellChecker(); } }
public static void main(String[] args) { ApplicationContext ac = new AnnotationConfigApplicationContext(HelloWorldConfig.class); HelloWorld hw = (HelloWorld)ac.getBean(HelloWorld.class); hw.getMessage(); }
|
5.6. Spring 中的事件处理
用于监控spring在执行过程中发生的变化。
Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。
Spring通过ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
Spring提供的标准事件:
序号 | Spring 内置事件 & 描述 |
1 | ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。 |
2 | ContextStartedEvent 当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
3 | ContextStoppedEvent 当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。 |
4 | ContextClosedEvent 当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
5 | RequestHandledEvent 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。 |
由于 Spring 的事件处理是单线程的,所以如果一个事件被发布,直至并且除非所有的接收者得到的该消息,该进程被阻塞并且流程将不会继续。因此,如果事件处理被使用,在设计应用程序时应注意。
监听上下文事件:
为了监听上下文事件,一个 bean 应该实现只有一个方法 onApplicationEvent() 的 ApplicationListener 接口。因此,我们写一个例子来看看事件是如何传播的,以及如何可以用代码来执行基于某些事件所需的任务。
HelloWorld.java
public class HelloWorld { private String message; public void setMessage(String message){ this.message = message; } public void getMessage(){ System.out.println("Your Message : " + message); } } |
CStartEventHandler.java
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextStartedEvent;
public class CStartEventHandler implements ApplicationListener<ContextStartedEvent> { public void onApplicationEvent(ContextStartedEvent event) { System.out.println("ContextStartedEvent Received"); } } |
CStopEventHandler.java
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextStoppedEvent;
public class CStopEventHandler implements ApplicationListener<ContextStoppedEvent> { public void onApplicationEvent(ContextStoppedEvent event) { System.out.println("ContextStoppedEvent Received"); } } |
MainApp.java
public class MainApp { public static void main(String[] args) { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); // ApplicationContext启动时触发 context.start(); HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); obj.getMessage(); // ApplicationContext停止时触发 context.stop(); } } |
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="helloWorld" class="com.tutorialspoint.HelloWorld"> <property name="message" value="Hello World!"/> </bean>
<bean id="cStartEventHandler" class="com.tutorialspoint.CStartEventHandler"/>
<bean id="cStopEventHandler" class="com.tutorialspoint.CStopEventHandler"/>
</beans> |
5.7. Spring 中的自定义事件
自定义事件一般用于处理程序逻辑监听等。
实现方式:
/** * 定义事件及事件逻辑 * @author fukai */ public class CustomEvent extends ApplicationEvent { //必须有构造函数 public CustomEvent(Object source) { super(source); } //事件逻辑方法 public String toString(){ //编写事件逻辑 return "hello"; } } /** * 发布事件 * @author fukai */ public class CustomEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
/* * 接口自带方法 */ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } //发布 public void publish() { CustomEvent ce = new CustomEvent(this); publisher.publishEvent(ce); } } /** * 事件响应 * @author fukai */ public class CustomEventHandler implements ApplicationListener<CustomEvent> {
@Override //响应事件 public void onApplicationEvent(CustomEvent event) { // TODO Auto-generated method stub System.out.println(event.toString()); }
} /** * 注册发布和响应bean * @author fukai */ <?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="customEventHandler" class="com.fukai.example2.CustomEventHandler"/> <bean id="customEventPublisher" class="com.fukai.example2.CustomEventPublisher"/>
</beans> /** * 测试 * @author fukai * */ public class MainApp { public static void main(String[] args) { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml"); CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher"); cvp.publish(); cvp.publish(); } } |
6. Spring 框架的 AOP
Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的很好的例子,如日志记录、审计、声明式事务、安全性和缓存等。
在 OOP 中,关键单元模块量度是类,而在 AOP 中单元模块量度是方面。依赖注入可以使应用程序对象相互解耦, AOP 可以帮助你从它们所影响的对象中对横切关注点解耦。AOP 是像编程语言的触发物,如 Perl,.NET,Java 或者其他。
相关jar包:
l aspectjrt.jar
l aspectjweaver.jar
l aspectj.jar
l aopalliance.jar
Spring AOP 模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。
使用aop需要导入
AOP术语
项 | 描述 |
Aspect | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point | 在应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。 |
Introduction | 引用允许你添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
Spring方面使用的通知的类型
通知 | 描述 |
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
实现自定义方面方式
l 基于注解模式
l 基于xml模式
6.1. Spring 中基于 AOP 的 XML架构
//实体类 public class Student {
private Integer age; private String name;
public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age); return age; } public void setName(String name) { this.name = name; } //切入点 public String getName() { System.out.println("Name : " + name); return name; } public void printThrowException() { System.out.println("Exception raised"); throw new IllegalArgumentException(); }
} //切面 public class Logging { /** * 选定方法执行前执行 */ public void beforeAdvice() { System.out.println("初始化bean"); } /** * 选定方法执行后执行 */ public void afterAdvice() { System.out.println("初始化完成"); } /** * 选定方法return后执行,该方法参数未固定参数 */ public void afterReturningAdvice(Object retVal) { System.out.println("Returning:" + retVal.toString()); } /** * 选定方法有异常抛出后执行,该方法参数未固定参数 */ public void AfterThrowingAdvice(IllegalArgumentException ex) { System.out.println("There has been an exception: " + ex.toString()); } } //配置文件 <?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 "> <!-- aop配置 --> <aop:config> <!-- 声明方面 --> <aop:aspect id="log" ref="logging"> <!-- 声明切入点,可指定具体方法或者指定部分方法 <aop:pointcut id="selectAll" expression="execution(* com.fukai.aop.Student.getName(..))" /> --> <aop:pointcut id="selectAll" expression="execution(* com.tutorialspoint.*.*(..))"/> <!-- 切点前执行 --> <aop:before pointcut-ref="selectAll" method="beforeAdvice" /> <!-- 切点后执行 --> <aop:after pointcut-ref="selectAll" method="afterAdvice" /> <!-- return后执行, 当执行请求任务时,该方法必须包含Object retVal参数 --> <aop:after-returning pointcut-ref="selectAll" returning="retVal" method="afterReturningAdvice" /> <!-- 抛出异常后执行,当执行请求任务时,该方法必须包含IllegalArgumentException ex参数,用于全局异常处理等 --> <aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="AfterThrowingAdvice" /> </aop:aspect> </aop:config> <!-- 配置依赖注入的bean --> <!-- 定义 student bean --> <bean id="student" class="com.fukai.aop.Student"> <property name="name" value="Zara" /> <property name="age" value="11" /> </bean>
<!-- 定义 logging 方面 --> <bean id="logging" class="com.fukai.aop.Logging" />
</beans> //测试 public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml"); Student student = (Student) context.getBean("student"); student.getName(); student.getAge(); student.printThrowException(); } } |
6.2. Spring 中基于 AOP 的 @AspectJ
//实体类 public class Student {
private Integer age; private String name;
public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age); return age; } public void setName(String name) { this.name = name; } //切入点 public String getName() { System.out.println("Name : " + name); return name; } public void printThrowException() { System.out.println("Exception raised"); throw new IllegalArgumentException(); }
} //切面 @Aspect public class Logging { /** * 指定切入点,可以是单个方法,也可以是多个 * @Pointcut("execution(* com.fukai.aopAspectJ.Student.getName(..))") */ @Pointcut("execution(* com.fukai.aopAspectJ.*.*(..))") private void selectAll(){} /** * 选定方法执行前执行 */ @Before("selectAll()") public void beforeAdvice() { System.out.println("Going to setup student profile."); } /** * 选定方法执行后执行 */ @After("selectAll()") public void afterAdvice() { System.out.println("Student profile has been setup."); } /** * 选定方法return后执行 */ @AfterReturning(pointcut = "selectAll()", returning="retVal") public void afterReturningAdvice(Object retVal) { System.out.println("Returning:" + retVal.toString()); } /** * 选定方法有一场抛出后执行 */ @AfterThrowing(pointcut = "selectAll()", throwing = "ex") public void AfterThrowingAdvice(IllegalArgumentException ex) { System.out.println("There has been an exception: " + ex.toString()); } } //配置文件 <?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 "> <!-- 启用AspectJ注释 --> <aop:aspectj-autoproxy/> <!-- 定义 student bean --> <bean id="student" class="com.fukai.aopAspectJ.Student"> <property name="name" value="Zara" /> <property name="age" value="11" /> </bean>
<!-- 定义 logging 方面 --> <bean id="logging" class="com.fukai.aopAspectJ.Logging" />
</beans> //测试 public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("aopAspectJ.xml"); Student student = (Student) context.getBean("student"); student.getName(); student.getAge(); student.printThrowException(); } } |
7. Spring JDBC
Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。
JdbcTemplate 类
JdbcTemplate 类执行 SQL 查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获 JDBC 异常并转换它们到 org.springframework.dao 包中定义的通用类、更多的信息、异常层次结构。
JdbcTemplate 类的实例是线程安全配置的。所以可以配置 JdbcTemplate 的单个实例,然后将这个共享的引用安全地注入到多个 DAOs 中。
使用 JdbcTemplate 类时常见的做法是在 Spring 配置文件中配置数据源,然后共享数据源 bean 依赖注入到 DAO 类中,并在数据源的设值函数中创建了 JdbcTemplate。
配置数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/TEST" /> <property name="username" value="root" /> <property name="password" value="password" /> |
数据访问对象(DAO)
DAO 代表常用的数据库交互的数据访问对象。DAOs 提供一种方法来读取数据并将数据写入到数据库中,它们应该通过一个接口显示此功能,应用程序的其余部分将访问它们。
在 Spring 中,数据访问对象(DAO)支持很容易用统一的方法使用数据访问技术,如 JDBC、Hibernate、JPA 或者 JDO。
Spring JDBC 示例
//实体类 public class Student { private int id; private Integer age; private String name;
public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age); return age; } public void setName(String name) { this.name = name; } //切入点 public String getName() { System.out.println("Name : " + name); return name; } public void printThrowException() { System.out.println("Exception raised"); throw new IllegalArgumentException(); } public int getId() { return id; } public void setId(int id) { this.id = id; } } //dao接口 public interface StudentDAO { /** * 创建数据源连接 */ public void setDataSource(DataSource ds);
/** * 插入 */ public void create(String name, Integer age);
/** * 查询 */ public Student getStudent(Integer id);
/** * 查询集合 */ public List<Student> listStudents();
/** * 删除 */ public void delete(Integer id);
/** * 更新 */ public void update(Integer id, Integer age); } //dao实现 public class StudentDAOImp implements StudentDAO { private DataSource dataSource; private JdbcTemplate jdbcTemplateObject; @Override public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplateObject = new JdbcTemplate(dataSource); } @Override public void create(String name, Integer age) { String SQL = "insert into Student (name, age) values (?, ?)"; jdbcTemplateObject.update(SQL, name, age); System.out.println("Created Record Name = " + name + " Age = " + age); return; } @Override public Student getStudent(Integer id) { String SQL = "select * from Student where id = ?"; Student student = jdbcTemplateObject.queryForObject(SQL, new Object[] { id }, new StudentMapper()); return student; } @Override public List<Student> listStudents() { String SQL = "select * from Student"; List<Student> students = jdbcTemplateObject.query(SQL, new StudentMapper()); return students; } @Override public void delete(Integer id) { String SQL = "delete from Student where id = ?"; jdbcTemplateObject.update(SQL, id); System.out.println("Deleted Record with ID = " + id); return; } @Override public void update(Integer id, Integer age) { String SQL = "update Student set age = ? where id = ?"; jdbcTemplateObject.update(SQL, age, id); System.out.println("Updated Record with ID = " + id); return; } } //RowMapper<T>查询结果转换为实体接口,T代表需要转换为的实体名称 public class StudentMapper implements RowMapper<Student> { /* * 接口包含方法,用于将结果集中数据转换为实体 */ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student student = new Student(); student.setId(rs.getInt("id")); student.setName(rs.getString("name")); student.setAge(rs.getInt("age")); return student; } } //配置文件 <?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 ">
<!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean>
<!-- Definition for studentJDBCTemplate bean --> <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean>
</beans>
//测试 public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); StudentDAOImp sdi = (StudentDAOImp) context.getBean("studentJDBCTemplate"); System.out.println("------Records Creation--------"); sdi.create("Zara", 11); sdi.create("Nuha", 2); sdi.create("Ayan", 15); System.out.println("------Listing Multiple Records--------"); List<Student> students = sdi.listStudents(); for (Student record : students) { System.out.print("ID : " + record.getId()); System.out.print(", Name : " + record.getName()); System.out.println(", Age : " + record.getAge()); } System.out.println("----Updating Record with ID = 2 -----"); sdi.update(2, 20); System.out.println("----Listing Record with ID = 2 -----"); Student student = sdi.getStudent(2); System.out.print("ID : " + student.getId()); System.out.print(", Name : " + student.getName()); System.out.println(", Age : " + student.getAge()); } } |
8. Spring 事务管理
事务管理
一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:
原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中 删除。
一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:
使用 begin transaction 命令开始事务。
使用 SQL 查询语句执行各种删除、更新或插入操作。
如果所有的操作都成功,则执行提交操作,否则回滚所有操作。
Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现。
局部事物 vs. 全局事务
局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。
局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。
全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。
编程式 vs. 声明式
Spring 支持两种类型的事务管理:
编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。
8.1. Spring 编程式事务管理
使用 PlatformTransactionManager 来实现编程式方法从而实现事务。要开始一个新事务,需要有一个带有适当的 transaction 属性的 TransactionDefinition 的实例。下面例子中,我们使用默认的 transaction 属性简单的创建了 DefaultTransactionDefinition 的一个实例。
当 TransactionDefinition 创建后,你可以通过调用 getTransaction() 方法来开始你的事务,该方法会返回 TransactionStatus 的一个实例。 TransactionStatus 对象帮助追踪当前的事务状态,并且最终,如果一切运行顺利,你可以使用 PlatformTransactionManager 的 commit() 方法来提交这个事务,否则的话,你可以使用 rollback() 方法来回滚整个操作。
//dao实现 public class StudentDAOImp implements StudentDAO {
private DataSource dataSource; private JdbcTemplate jdbcTemplateObject; private PlatformTransactionManager transactionManager;
public PlatformTransactionManager getTransactionManager() { return transactionManager; }
public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; }
@Override public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplateObject = new JdbcTemplate(dataSource); }
@Override public void create(String name, Integer age) { //开始事务 TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { String SQL = "insert into Student(id, name, age) " + "values (?, ?, ?)"; jdbcTemplateObject.update(SQL); System.out.println("Created Name = " + name + ", Age = " + age); //提交 transactionManager.commit(status); } catch (DataAccessException e) { System.out.println("Error in creating record, rolling back"); transactionManager.rollback(status); throw e; } return; } }
//配置文件添加 <!-- 定义事务实例 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> |
8.2. Spring 声明式事务管理
声明式事务管理方法允许你在配置的帮助下而不是源代码硬编程来管理事务。这意味着你可以将事务管理从事务代码中隔离出来。你可以只使用注释或基于配置的 XML 来管理事务。 bean 配置会指定事务型方法。下面是与声明式事务相关的步骤:
我们使用标签,它创建一个事务处理的建议,同时,我们定义一个匹配所有方法的切入点,我们希望这些方法是事务型的并且会引用事务型的建议。
如果在事务型配置中包含了一个方法的名称,那么创建的建议在调用方法之前就会在事务中开始进行。
目标方法会在 try / catch 块中执行。
如果方法正常结束,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: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">
<!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean>
<!-- Definition for studentJDBCTemplate bean --> <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean>
<!-- 定义事务实例 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="create"/> </tx:attributes> </tx:advice>
<aop:config> <aop:pointcut id="createOperation" expression="execution(* com.tutorialspoint.StudentJDBCTemplate.create(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/> </aop:config>
</beans> |
9. Spring Web MVC 框架
框架布局
Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
l 模型封装了应用程序数据,并且通常它们由 POJO 组成。
l 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
l 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
DispatcherServlet
Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应。Spring Web MVC DispatcherServlet 的请求处理的工作流程如下图所示:
下面是对应于 DispatcherServlet 传入 HTTP 请求的事件序列:
l 收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器。
l 控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。
l DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。
l 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。
上面所提到的所有组件,即 HandlerMapping、Controller 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是带有一些对 web 应用程序必要的额外特性的 ApplicationContext 的扩展。
注释
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /* * 使用springMVC注释时,需要在配置文件中开启注解扫描 * <context:component-scan base-package="com.fukai" /> */ @Controller//声明当前类为控制器 @RequestMapping("/hello")//定义当前控制器请求根路径 public class HelloController { //定义访问当前方法的请求url和方式 @RequestMapping(value = "/hello",method = RequestMethod.GET) public void getStudent(){ } } |