目录
2.Spring的ApplicationContext 容器
Spring简介
1.概述
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码
Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
2.依赖注入(DI)
Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。
例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。
依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分。
3.面向方面的程序设计(AOP)
Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等
在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。
Spring 框架的 AOP 模块提供了面向方面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。
4.组成
核心容器:spring-core,spring-beans,spring-context,spring-context-support和spring-expression
数据访问/集成:JDBC,ORM,OXM,JMS 和事务处理模块
注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
web:web,web-mvc.web-socket和web-portlet
其他:AOP,Aspects,Instrumentation,Web 和测试模块
Spring IOC 容器
基于maven的开发,引入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
其中包含了spring几个核心包:spring-core,spring-beans,spring-context,spring-context,spring-expression,spring-aop和commons-logging
1.Spring的BeanFactory容器
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据
XmlBeanFactory factory = new XmlBeanFactory (new ClassPathResource("Beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
配置文件 Beans.xml 中的内容:
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
2.Spring的ApplicationContext 容器
Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文。
Application Context 是 spring 中较高级的容器。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。
最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext:需要提供给构造器 XML 文件的完整路径。
ClassPathXmlApplicationContext:不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
WebXmlApplicationContext:会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
3.Spring的Bean 定义
如下表所示:
属性 | 例如 | 描述 |
---|---|---|
class | <bean id="..." class="..."> | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | <bean name="..." class="..."> | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | <bean name="..." class="..." scop="singleton"> | 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。 |
constructor-arg | 无 | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
properties | 无 | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
autowiring mode | 无 | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
lazy-initialization mode | <bean id="..." class="..." lazy-init="true"> | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
initialization 方法 | <bean id="..." class="..." init-method="..."> | 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。 |
destruction 方法 | <bean id="..." class="..." destroy-method="..."> | 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。 |
bean与spring容器之间的关系
1.bean配置信息,一般有三种:基于 XML 的配置文件;基于注解的配置;基于java的配置
2.XML配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
##默认命名空间:它没有空间名,用于Spring Bean的定义
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
##xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间。
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
4.Bean 的作用域
bean定义中的scope属性
支持以下五个作用域,分别为singleton、prototype、request、session和global session
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境 |
默认是singleton 单例模式 <bean name="..." class="..." scop="singleton">
singleton 模式就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。
Prototype,表示一个bean定义对应多个对象实例 <bean id="..." class="..." scope="prototype">
prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。总之,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
有状态的bean和无状态的bean的区别在于bean的生命周期,例如频繁的销毁创建,适用于有状态的bean,像登录用户;初始化创建后,一直保持不变的bean,适用于无状态的bean
5.Bean 的生命周期
Bean的生命周期可以表达为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁
在bean的定义属性中提到了initialization 和destruction方法,
两种方式的写法:
1.接口方式,InitializingBean和DisposableBean,实现init和destroy
public class TestMessage implements InitializingBean,DisposableBean{
private String message;@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init=============================");
}@Override
public void destroy() throws Exception {
System.out.println("destroy=============================");
}
}
2.XML方式,配置init-method和destroy-method属性,实现init和destroy
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</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">
6.bean的后置处理器
Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。
public class InitMessage implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("AfterInitialization : " + beanName);
return bean;
}@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("BeforeInitialization : " + beanName);
return bean;
}
}
同时需要注入该bean到上下文中
<bean id="initMessage" class="com.emap.init.InitMessage"></bean>
对bean的init方法前后增加逻辑操作
执行结果
BeforeInitialization : tMessage
init=============================
AfterInitialization : tMessage
7.Bean 定义继承
Spring Bean 定义的继承与 Java 类的继承无关
可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置
子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
</bean>
<bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="helloWorld">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>
执行结果
World Message1 : Hello World!
World Message2 : Hello Second World!
India Message1 : Hello India! India
Message2 : Hello Second World!
India Message3 : Namaste India!
另外,Bean 定义模板
可以创建一个 Bean 定义模板,不需要花太多功夫它就可以被其他子 bean 定义使用。在定义一个 Bean 定义模板时,你不应该指定类的属性,而应该指定带true 值的抽象属性
<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>
父 bean 自身不能被实例化,因为它是不完整的,而且它也被明确地标记为抽象的。当一个定义是抽象的,它仅仅作为一个纯粹的模板 bean 定义来使用的,充当子定义的父定义使用。
依赖注入
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
两种方式:
基于构造函数的依赖注入
基于设值函数的依赖注入
代码比较:
标准代码看起来是这样的:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SpellChecker();
}
}
控制反转的场景中,我们反而会做这样的事情:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
}
在这里,TextEditor 不应该担心 SpellChecker 的实现。SpellChecker 将会独立实现,并且在 TextEditor 实例化的时候将提供给 TextEditor,整个过程是由 Spring 框架的控制。
通过类构造函数被注入到 TextEditor 类中。控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。
依赖注入的第二种方法是通过 TextEditor 类的 Setter 方法,我们将创建 SpellChecker 实例,该实例将被用于调用 setter 方法来初始化 TextEditor 的属性。
1.Spring 基于构造函数的依赖注入
当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖
两个类SpellChecker和TextEditor
public class SpellChecker {
public SpellChecker() {
System.out.println("spellchecker constructor=========");
}
public void checkSpelling() {
System.out.println("SpellChecker checkSpelling=========" );
}
}public class TextEditor {
private SpellChecker spellChecker;public TextEditor(SpellChecker spellChecker) {
System.out.println("TextEditor constructor=========");
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
xml配置
<bean id="textEditor" class="com.emap.entity.TextEditor">
<constructor-arg ref="spellChecker"></constructor-arg>
</bean>
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
执行结果
spellchecker constructor=========
TextEditor constructor=========
SpellChecker checkSpelling=========
另外,构造函数参数解析,下面介绍constructor-arg
1.构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序
<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>
2.使用 type 属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型。
<beans>
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="2001"/>
<constructor-arg type="java.lang.String" value="Zara"/>
</bean>
</beans>
3.最好的传递构造函数参数的方式,使用 index 属性来显式的指定构造函数参数的索引。下面是基于索引为 0 的例子
<beans>
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="2001"/>
<constructor-arg index="1" value="Zara"/>
</bean>
</beans>
4.想要向一个对象传递一个引用,你需要使用 标签的 ref 属性,如果你想要直接传递值,那么你应该使用如上所示的 value 属性。
2.Spring 基于设值函数的依赖注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了
两个类SpellChecker和TextEditor
public class SpellChecker {
public SpellChecker() {
System.out.println("spellchecker constructor=========");
}
public void checkSpelling() {
System.out.println("SpellChecker checkSpelling=========" );
}
}public class TextEditor {
private SpellChecker spellChecker;public void setSpellChecker(SpellChecker spellChecker) {
System.out.println("TextEditor constructor=========");
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
xml配置
<bean id="textEditor" class="com.emap.entity.TextEditor">
<property name="spellChecker" ref="spellChecker"></property>
</bean>
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
执行结果
spellchecker constructor=========
TextEditor constructor=========
SpellChecker checkSpelling=========
综上述,区别在哪?
在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素
使用 p-namespace 实现 XML 配置:
标准写法
<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>
可以使用 p-namespace 以一种更简洁的方式重写
<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>
3.Spring 注入内部 Beans
一句话表达就是bean的嵌套写法
在基于设值函数的依赖注入中可以修改xml的写法,如下所示:
<bean id="textEditor" class="com.emap.entity.TextEditor">
<property name="spellChecker">
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
</property>
</bean>
4.Spring 注入集合
例如:list,set,map,properties
基于set方式的依赖注入,在xml的配置上
<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>
另外,注入 null 和空字符串的值
<bean id="..." class="exampleBean">
<property name="email" value=""/>
</bean>
相当于Java 代码:exampleBean.setEmail("")。
<bean id="..." class="exampleBean">
<property name="email"><null/></property>
</bean>
相当于 Java 代码:exampleBean.setEmail(null)。
Beans 自动装配
基于set方式的依赖注入:autowire="byName"或者autowire="byType"
基于构造函数方式的依赖注入: autowire="constructor"
你已经学会如何使用<bean>
元素来声明 bean 和通过使用 XML 配置文件中的<constructor-arg>
和<property>
元素来注入 。
Spring 容器可以在不使用<constructor-arg>
和<property>
元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。
可以使用<bean>
元素的 autowire 属性为一个 bean 定义指定自动装配模式。
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
基于set方式的依赖注入,xml文件可以修改成
<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byName">
<property name="name" value="Generic Text Editor" />
</bean>
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
或者
<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byType">
<property name="name" value="Generic Text Editor" />
</bean>
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
基于构造函数方式的依赖注入 ,xml文件可以修改成
<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="constructor">
<constructor-arg value="Generic Text Editor"/>
</bean>
<bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
基于注解的配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
注解连线在默认情况下在 Spring 容器中不打开,需要在我们的 Spring 配置文件中启用它
<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>
介绍几个常用注解
序号 | 注解 & 描述 |
---|---|
1 | @Required @Required 注解应用于 bean 属性的 setter 方法。 |
2 | @Autowired @Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。 |
3 | @Qualifier 通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。 |
4 | JSR-250 Annotations Spring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。 |
1.Spring @Required 注释
@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常
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;
}
}
xml配置
<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>
2.Spring @Autowired 注释
1.Setter 方法中的 @Autowired:你可以在 XML 文件中的 setter 方法中使用 @Autowired 注释来除去 元素。当 Spring遇到一个在 setter 方法中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。
@Autowired
public void setSpellChecker(SpellChecker spellChecker) {
System.out.println("TextEditor constructor=========");
this.spellChecker = spellChecker;
}
xml配置:
<context:annotation-config/>
<bean id="textEditor" class="com.emap.entity.TextEditor"></bean>
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
2.属性中的 @Autowired:
可以在属性中使用 @Autowired 注释来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。所以利用在属性中 @Autowired 的用法。
public class TextEditor {
@Autowired
private SpellChecker spellChecker;
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
xml配置:
<context:annotation-config/>
<bean id="textEditor" class="com.emap.entity.TextEditor"></bean>
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
3.构造函数中的 @Autowired:
可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接
public class TextEditor {
private SpellChecker spellChecker;
public SpellChecker getSpellChecker() {
return spellChecker;
}@Autowired
public TextEditor(SpellChecker spellChecker) {
System.out.println("TextEditor constructor=========");
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
xml配置
<context:annotation-config/>
<bean id="textEditor" class="com.emap.entity.TextEditor"></bean>
<bean id="spellChecker" class="com.emap.entity.SpellChecker"></bean>
默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。
3.Spring @Qualifier 注释
可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱
public class Profile {
@Autowired
@Qualifier("student1")
private Student student;
public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}
xml的配置
<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>
4.Spring JSR-250 注释
包括三种注解:@PostConstruct, @PreDestroy 和 @Resource 注释
其中@PostConstruct, @PreDestroy分别替代XML中init-method和destroy-method,这是第三种写法了
前两种分别是XML中init-method和destroy-method写法
和实现接口InitializingBean,DisposableBean中的afterPropertiesSet()和destroy()
public class TestMessage2{
private String message;public String getMessage() {
return message;
}public void setMessage(String message) {
this.message = message;
}@PostConstruct
public void init() throws Exception {
System.out.println("init=============================");
}@PreDestroy
public void destroy() throws Exception {
System.out.println("destroy=============================");
}
}
xml配置
<context:annotation-config/>
<bean id="tMessage2" class="com.emap.entity.TestMessage2" scope="singleton">
<property name="message" value="Hello World!"/>
</bean>
另外,@Resource 注释:
你可以在字段中或者 setter 方法中使用 @Resource 注释,它和在 Java EE 5 中的运作是一样的。@Resource 注释使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入。你可以说,它遵循 by-name 自动连接语义
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.Spring 基于 Java 的配置
到目前为止,你已经看到如何使用 XML 配置文件来配置 Spring bean。如果你熟悉使用 XML 配置,那么我会说,不需要再学习如何进行基于 Java 的配置是,因为你要达到相同的结果,可以使用其他可用的配置。
基于 Java 的配置选项,可以使你在不用配置 XML 的情况下编写大多数的 Spring,但是一些有帮助的基于 Java 的注解
@Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。
@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
等价于
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>
@import:注解允许从另一个配置类中加载 @Bean 定义
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B a() {
return new A();
}
}
生命周期回调
@Bean 注解支持指定任意的初始化和销毁的回调方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和销毁方法的属性
public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public Foo foo() {
return new Foo();
}
}
指定 Bean 的范围:
默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法,如下所示:
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public Foo foo() {
return new Foo();
}
}
6.Spring 中的事件处理
Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
序号 | 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 请求已经被服务。 |
监听上下文事件
public class StartEventHandler implements ApplicationListener<ContextStartedEvent> {
@Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("StartEventHandler=============");
}
}
public class StopEventHandler implements ApplicationListener<ContextStoppedEvent> {
@Override
public void onApplicationEvent(ContextStoppedEvent event) {
System.out.println("StopEventHandler=============");
}
}
xml配置
<bean id="startEventHandler" class="com.emap.entity.StartEventHandler"></bean>
<bean id="stopEventHandler" class="com.emap.entity.StopEventHandler"></bean>
7.Spring 中的自定义事件
source:
public class CustomEvent extends ApplicationEvent{
public CustomEvent(Object source) {
super(source);
}
public String toString(){
return "My Custom Event";
}
}
给source绑上监听器listener:
public class CustomEventHandler
implements ApplicationListener<CustomEvent>{
public void onApplicationEvent(CustomEvent event) {
System.out.println(event.toString());
}
}
发布类,用来发布source:
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);
}
}
xml配置
<bean id="customEventHandler" class="com.tutorialspoint.CustomEventHandler"/>
<bean id="customEventPublisher" class="com.tutorialspoint.CustomEventPublisher"/>
执行:
CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher");
cvp.publish();
Spring 框架的 AOP
1.Spring 框架的 AOP
Spring 框架的一个关键组件是面向方面的编程(AOP)框架。
面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
在 OOP 中,关键单元模块度是类,而在 AOP 中单元模块度是方面。依赖注入帮助你对应用程序对象相互解耦。
AOP 可以帮助你从它们所影响的对象中对横切关注点解耦。
Spring AOP 模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。
通知的类型
Spring 方面可以使用下面提到的五种通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
需要额外加入:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
AOP分两种:
2.Spring 中基于 AOP 的 XML架构:
使用 aop 命名空间标签,你需要导入 spring-aop架构
声明一个 aspect:一个 aspect 是使用 元素声明的,支持的 bean 是使用 ref 属性引用的
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
然后声明一个切点:一个切入点有助于确定使用不同建议执行的感兴趣的连接点(即方法)
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<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:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
3.Spring 中基于 AOP 的 @AspectJ
@AspectJ 作为通过 Java 5 注释的普通的 Java 类,它指的是声明 aspects 的一种风格。通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的
<aop:aspectj-autoproxy/>
@Aspect
public class UserListener{@Pointcut("execution(* com.emap.aop.*.*(..))")
private void loginPoint(){}
@Before(value = "loginPoint()")
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
@After(value = "loginPoint()")
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
@AfterReturning(pointcut = "loginPoint()", returning="retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
@AfterThrowing(pointcut = "loginPoint()", throwing="ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
用来替代xml中的aop配置
xml配置
<bean id="user" class="com.emap.aop.User">
<property name="userName" value="ssj"></property>
<property name="passWord" value="ssj2"></property>
</bean>
<bean id="userListener" class="com.emap.aop.UserListener"></bean><aop:aspectj-autoproxy/>
Spring 事务管理
1.事务管理
一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。
事务的概念可以描述为具有以下四个关键属性说成是 ACID:
-
原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
-
一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
-
隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
- 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。
Spring 支持两种类型的事务管理:
声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
Public interface PlatformTransactionManager()...{
// 由TransactionDefinition得到TransactionStatus对象
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交
Void commit(TransactionStatus status) throws TransactionException;
// 回滚
Void rollback(TransactionStatus status) throws TransactionException;
}
1> JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。
2> Hibernate事务
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Session Factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingLocations">
<list>
<value>classpath:/*.hbm.xml</value>
</list>
</property><property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。
3> Java持久化API事务(JPA)
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。
4> Java原生API事务
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。
2.编程式事务管理:
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
//数据库操作
transactionManager.commit(status);
出现异常
transactionManager.rollback(status);
xml配置
<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><!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean><!-- Definition for studentJDBCTemplate bean -->
<bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
</bean>
3.声明式事务管理
声明式事务管理方法允许你在配置的帮助下而不是源代码硬编程来管理事务。这意味着你可以将事务管理从事务代码中隔离出来。你可以只使用注释或基于配置的 XML 来管理事务。
xml配置
<!-- 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="cohondob"/>
</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><!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean><!-- Definition for studentJDBCTemplate bean -->
<bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Spring Web MVC 框架
Spring Web MVC 框架是围绕DispatcherServlet设计的。所谓DispatcherServlet就是将请求分发到handler,默认的handler是基于@Controller和@RequestMapping注解。
像很多其他web MVC框架一样,Spring MVC框架也是请求驱动的,围绕一个中心Servlet来设计,该中心Servlet可以分发请求到Controllers,并提供其他功能。然而Spring的DispatcherServlet做的更多。它被彻底地与Spring IoC容器集成到了一起。
DispatcherServlet是一个具体的Servlet(继承自HttpServlet基类),所以你需要使用一个URL mapping来映射请求 -- 就是你想让DispatcherServlet处理的请求。这是一个标准Java EE Servlet配置
-
收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器。
-
控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到DispatcherServlet 中。
-
DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。
- 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。
上面所提到的所有组件,即 HandlerMapping、Controller 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是带有一些对 web 应用程序必要的额外特性的 ApplicationContext 的扩展。
在Spring MVC框架中,每个DispatcherServlet都有其自有的WebApplicationContext,它会继承在root WebApplicationContext中定义的beans。
bean type | 解释 |
HandlerMapping | 基于criteria(不同的HandlerMapping实现有不同的criteria)将incoming requests映射到handlers以及一个pre和post-processors(handler interceptors)。最流行的实现支持注解controllers。 |
HandlerAdapter | 帮助DispatcherServlet调用映射到一个request的handler,无论handler是否实际被调用了。例如,调用一个注解Controller需要处理不同的注解。所以,HandlerAdapter的主要目的是让DispatcherServlet远离这些细节。 |
HandlerExceptionResolver | 将exceptions映射到views,也允许更复杂的exception处理代码。 |
ViewResolver | 处理基于字符串的逻辑视图的名字,将其转成实际的View 类型。 |
LocaleResolver & LocaleContextResolver | Resolves the locale a client is using and possibly their time zone, in order to be able to offer internationalized views |
ThemeResolver | Resolves themes your web application can use, for example, to offer personalized layouts |
MultipartResolver | 解析multi-part请求,例如,支持处理来自HTML forms的文件上传。 |
FlashMapManager | 存储和获取input 和 output的FlashMap -- 可用于从一个request将attributes传递至另一个,通常跨域一个redirect。 |
在web.xml
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/config/springmvc.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
@Controller 注释定义该类作为一个 Spring MVC 控制器
@RequestMapping 表明在该控制器中处理的所有方法都是相对于 ****路径的。
@GetMapping
@PostMapping
@PutMapping
@PatchMapping
@DeleteMapping
@RestController代替@ResponseBody与@Controller
@RequestParam将请求参数绑定到方法参数
@RequestBody该注解放在方法上指示返回类型会被写入HTTP response body
@ModelAttribute当同一个controller中有任意一个方法被@ModelAttribute注解标记,页面请求只要进入这个控制器,不管请求那个方法,均会先执行被@ModelAttribute标记的方法,所以我们可以用@ModelAttribute注解的方法做一些初始化操作。当同一个controller中有多个方法被@ModelAttribute注解标记,所有被@ModelAttribute标记的方法均会被执行,按先后顺序执行,然后再进入请求的方法。
请求流程
第一步:用户发送请求到前端控制器(DispatcherServlet)。
第二步:前端控制器请求 HandlerMapping 查找 Handler,可以根据 xml 配置、注解进行查找。
第三步: 处理器映射器 HandlerMapping 向前端控制器返回 Handler
第四步:前端控制器调用处理器适配器去执行 Handler
第五步:处理器适配器执行 Handler
第六步:Handler 执行完成后给适配器返回 ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView
ModelAndView 是SpringMVC 框架的一个底层对象,包括 Model 和 View
第八步:前端控制器请求试图解析器去进行视图解析
根据逻辑视图名来解析真正的视图。
第九步:试图解析器向前端控制器返回 view
第十步:前端控制器进行视图渲染
就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第十一步:前端控制器向用户响应结果
下面我们对上面出现的一些组件进行解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
可以总结出:需要我们开发的工作只有处理器 Handler 的编写以及视图比如JSP页面的编写。