Spring学习
三层架构
- A 表现层 web层 MVC是表现层的一个设计模式
- B 业务层 service层
- C 持久层 dao层
Spring的优良特性
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
- 控制反转:IOC-Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建,而使用Spring之后,对象的创建就交给了Spring框架。
- 依赖注入:DI-Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
- 面向切面编程:Aspect Oriented Programming-Aop
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring自身也提供了表现层的SpringMVC和持久层的SpringJDBC)
核心容器
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression等模块组成,细节如下:
- spring-core:提供了框架的基本组成部分,包括IOC和依赖注入功能。
- spring-beans:提供了BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且把配置和依赖从实际编码逻辑中解耦。
- context:建立在由core和beans模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化、事件传播、资源加载和透明地创建上下文(比如:通过Servlet容器)等功能。Context模块也支持java ee的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。Spring-context-support提供了对第三方集成到Spring上下文的支持,比如缓存(EhCache、Guava、JCache)、邮件(JavaMail)、调度(CommonJ,Quartz)、模板引擎(FreeMarker,JasperReports,Velocity)等。
- spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从SpingnIOC容器检索对象,还支持列表的投影、选择以及聚合等。
数据访问/集成
数据访问/集成层包括JDBC,ORM,JMS和事务处理模块,它们的细节如下:
- JDBC模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。
- ORM模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和Spring的其他功能整合,比如前面提及的事务管理。
- OXM模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
- JMS模块包含生产和消费消息的功能。从Spring4.1开始,集成了spring-messaging模块。
- 事务模块为实现特殊接口类及所有的POJO支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细。
Web
Web层由Web、Web-MVC、Web-Portlet组成,它们的细节如下:
- Web模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化Ioc容器。它还包括HTTP客户端以及spring远程调用中与web相关的部分。
- Web-MVC模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型和web表单完全地分离,且可以与spring框架的其他所有功能进行集成
- Web-Socket模块为WebSocket-based提供了支持,而且在web应用程序中提供了客户端和服务端之间通信的两种方式。
- Web-Portlet模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。
Test模块
Test模块:spring支持Junit和TestNG测试框架,而且还额外提供了一些基于Spring的测试功能,比如在测试Web框架时,模拟Http请求的功能。
Spring IoC容器
Ioc容器
Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象为SpringBeans。
Spring提供了以下两种类型的容器:
-
Spring BeanFactory
它是最简单的容器,给DI提供了基本的支持,它用org.springframework.beans.factory.BeanFactory接口来定义。BeanFactory或者相关的接口,如BeanFactoryAware,InitializingBean,DisposableBean,在Spring中仍然存在具有大量的与spring整合的第三方框架的反向兼容性的目的。 -
Spring ApplicationContext容器
该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由org.springframework.context.ApplicationContext接口定义。
ApplicationContext容器包括BeanFactory容器的所有功能,所以不建议使用BeanFactory。BeanFactory仍然可以用于轻量级的应用程序,如移动设备或基于applet的应用程序,其中它的数据量和速度是显著。
Spring的BeanFactory容器
这是一个最简单的容器,它主要的功能是为依赖注入(DI)提供支持。
在Spring中,有大量对BeanFactory接口的实现。其中,最常被使用的是xmlBeanFactory类。这个容器从一个XML文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
步骤 | 描述 |
---|---|
1 | 使用idea新建一个spring的项目 |
2 | 在src下新建2个包beanfactory和xml |
3 | 在beanfactory里面写Student类和test类 |
4 | 在xml里面写Beans.xml |
public class Student {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
<?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.xsd">
<bean id="1" class="beanfactory.Student">
<property name="code" value="20181672"/>
<property name="name" value="rtt"/>
</bean>
</beans>
public class test {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("xml/Beans.xml"));
Student student = (Student) factory.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
}
}
SpringApplicationContext容器
Application Context是BeanFactory的子接口,也被称为Spring上下文。
最常被使用的ApplicationContext接口实现:
- FileSystemXmlApplicationContext接口实现:该容器从XML文件中加载已被定义的bean,在这里,你需要提供给构造器XML文件的完整路径。
- ClassPathXmlApplicationContext:该容器从XML文件中加载已被定义的bean。在这里,你不需要提供XML文件的完整路径,只需正确配置CLASSPATH环境变量即可,因为,容器会从CLASSPATH中搜索bean配置文件。
- WebXmlApplicationContext:该容器会在一个web应用程序的范围内加载在XML文件中已被定义的bean。
public class testApplicationContext {
public static void main(String[] args) {
ApplicationContext context=new FileSystemXmlApplicationContext("D:/soft_File/IdeaFile/hand_rtt/springLearning/src/xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
}
}
Spring Bean定义
Bean定义
被称作bean的对象是构成应用程序的支柱也是由SpringIoC容器管理的。bean是一个被实例化,组装,并通过SpringIoC容器所管理的对象。这些bean是由容器提供的配置元数据创建的,例如,在XML的表单中的定义。
bean定义包含称为配置元数据的信息
- 如何创建一个bean
- bean 的生命周期的详细信息
- bean的依赖关系
上述所有的配置元数据转换成一组构成每个bean定义的下列属性。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建bean的bean类 |
name | 这个属性指定唯一的bean标识符。在基于XML的配置元数据中,你可以使用ID和name属性来指定bean标识符 |
scope | 这个属性由特定的bean定义创建的对象的作用域。 |
constructor-arg | 它是用来注入依赖关系的 |
properties | 它是用来注入依赖关系的 |
autowiring mode | 它是用来注入依赖关系的 |
lazy-initialization mode | 延迟初始化的bean告诉IoC容器在它第一次请求时,而不是在启动时去创建一个bean实例 |
initialization | 在bean的所有必需的属性被容器设置之后,调用回调方法。 |
destruction | 当包含该bean的容器被销毁时,使用回调方法。 |
Bean与Spring容器的关系:
Spring配置元数据
Spring IoC容器完全由实际编写的配置元数据的格式解耦。有下面三个重要的方法把配置元数据提供给Spring容器:
- 基于XML的配置文件
- 基于注解的配置
- 基于java的配置
提示:对于基于XML的配置,Spring2.0以后使用Schema的格式,使得不同类型的配置拥有了自己的命名空间,使配置文件更具有扩展性。
<?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">
<!-- A simple bean definition -->
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- A bean definition with lazy init set on -->
<bean id="..." class="..." lazy-init="true">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- A bean definition with initialization method -->
<bean id="..." class="..." init-method="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- A bean definition with destruction method -->
<bean id="..." class="..." destroy-method="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
在上述示例中:
- xmlns=“http://www.springframework.org/schema/beans”.默认命名空间:它没有空间名,用于Spring Bean的定义;
- xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”,xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间。
Spring Bean 作用域
当在Spring中定义一个bean时,你必须声明该bean的作用域的选项。例如,为了强制Spring在每次需要时都产生一个新的bean实例,你应该声明bean的作用域的属性为prototype。同理,如果你想让Spring在每次在每次需要时都返回同一个bean实例,你应该声明bean的作用域为singleton
Spring框架支持五个作用域:singleton,prototype,request,session和global-session
注意:如果你使用web-aware ApplicationContext时,其中三个是可用的。
作用域 | 描述 |
---|---|
singleton | 在spring Ioc容器仅存在一个bean实例,Bean以单例方式存在 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean时,都相当于new XXBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
Spring生命周期
为了定义安装和拆卸一个bean,我们只要声明带有init-method和destroy-method参数的。init-method属性指定一个方法,在实例化bean时,立即调用该方法。同样,destory-method指定一个方法,只有从容器中移除bean之后,才能调用该方法。
Bean的声明周期可以表达为:Bean的定义->Bean的初始化->Bean的使用->Bean的销毁
//添加init,destroy方法
public class Student {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public void init(){
System.out.println("bean被初始化了");
}
public void destroy(){
System.out.println("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.xsd">
<bean id="1" class="beanfactory.Student" init-method="init" destroy-method="destroy">
<property name="code" value="20181672"/>
<property name="name" value="rtt"/>
</bean>
</beans>
public class testApplicationContext {
public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
//确保正常关闭
context.registerShutdownHook();
}
}
默认的初始化和销毁方法
如果你有太多具有相同名称的初始化或者销毁方法的 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="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
Spring Bean后置处理器
Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。
BeanPostProcessor接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。你也可以在Spring容器通过插入一个或多个BeanPostProcessor的实现来完成实例化,配置和初始化一个bean之后实现一些自定义逻辑回调方法。
你可以配置多个BeanPostProcessor接口,通过设置BeanPostProcessor实现的Ordered接口提供的order属性来控制这些BeanPostProcessor接口的执行顺序。
注意:ApplicationContext会自动检测由BeanPostProcessor接口的实现定义的bean,注册这些bean为后置处理器,然后通过容器中创建bean,在适当的时候调用它。
在你自己自定义的BeanPostProcessor接口实现类中,要实现以下两个抽象方法BeanPostProcessor.postProcessBeforeInitialization(Object,String)和BeanPostProcessor.postProcessAfterInitialization(Object,String)
public class StudentOne {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void init(){
System.out.println("studentOne初始化了");
}
public void destroy(){
System.out.println("studentOne销毁了");
}
}
public class InitStudent implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"初始化之前");
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"初始化之后");
return null;
}
}
<?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.xsd">
<bean id="1" class="beanfactory.Student" init-method="init" destroy-method="destroy">
<property name="code" value="20181672"/>
<property name="name" value="rtt"/>
</bean>
<bean id="2" class="beanfactory.StudentOne" init-method="init" destroy-method="destroy">
<property name="name" value="Wxx"/>
</bean>
<bean class="beanfactory.InitStudent"/>
</beans>
public class testApplicationContext {
public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println("----------------");
System.out.println(student.getName()+"+"+student.getCode());
StudentOne studentOne=(StudentOne)context.getBean("2");
System.out.println(studentOne.getName()+"测试看看");
//确保正常关闭
context.registerShutdownHook();
}
}
Spring Bean 定义继承
bean定义可以包含很多的配置信息,包括构造函数的参数、属性值、容器的具体信息例如初始化方法,静态工厂方法名,等等。
子bean的定义继承父定义的配置数据。自定义可以根据需要重写一些值,或者添加其他值。
Spring Bean定义的继承与Java类的继承无关,但是继承的概念是一样的。你可以一定一个父bean的定义作为模板和其他子bean就可以从父bean中继承所需要的配置。
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定义来使用的,充当自定义的父定义来使用。
Spring依赖注入
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
依赖注入有助于把一些类粘合在一起,同时保持他们独立。
//假设学生依赖于书本
public class Student{
private Book book;
public Student(){book=new Book();}
}
在控制反转IoC的场景中,我们会这样做:
public class Student{
private Book book;
public Student(Book book){book=new Book();}
}
在这里Student不用担心Book的实现,Book将会独立实现,并且在Student实例化的时候提供给Student,整个过程由Spring框架控制。
在这里,我们已经从Student删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系(即 Book类)通过类构造函数被注入到 Student 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。
依赖注入的第二种方法是通过Student类的Setter方法,我们将创建Book实例,该实例将被用于调用setter方法来初始化Student属性。
DI主要有两种变体:
序号 | 依赖注入类型&描述 |
---|---|
1 | Constructor-based dependency injection 当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。 |
2 | Setter-based dependency injection 基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后容器调用 beans 的 setter 方法来实现的。 |
Spring基于构造函数的依赖注入
如果存在不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,那么构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序就可以了。
package x.y;
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>
package x.y;
public class Foo {
public Foo(int year, String name) {
// ...
}
}
<beans>
//使用type属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="2001"/>
<constructor-arg type="java.lang.String" value="Zara"/>
</bean>
</beans>
<beans>
//使用index属性显示的指定构造函数参数的索引
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="2001"/>
<constructor-arg index="1" value="Zara"/>
</bean>
</beans>
最后,如果你想要向一个对象传递一个引用,你需要使用标签的ref属性,如果你想要直接传递值,那么你应该使用如上所示的value属性。
Spring基于设值函数的依赖注入
//getter和setter函数
class student{
private BigString name;
public BigString getName(){
return name;
}
public void setName(BigString name){
this.name=name;
}
}
<bean id="student" class="com.XX.student">
<property name="name" ref="1"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="1" class="com.XX.BigString">
</bean>
基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。
第二个你需要注意的点是,如果你要把一个引用传递给一个对象,那么你需要使用 标签的 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 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的引用。
Spring 注入内部Beans
正如你所知道的Java内部类是在其他类的范围内被定义的,同理,inner beans是在其他bean的范围内定义的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>
Spring注入集合
现在如何你想传递多个值,如Java Collection类型LIst、Set、Map、Properties。为了处理这种情况,Spring提供了四种类型的集合的配置元素:
元素 | 描述 |
---|---|
有助于连线,如注入一列值,允许重复 | |
有助于连线,如注入一列值,不能重复 | |
可以用来注入键值对的集合,其中键和值可以是任何类型 | |
可以用来注入键值对的集合,其中键和值可以都是字符串类型 |
public class User {
private List<String> list;
private Set<String> set;
private Map<String,Integer> map;
private Properties pros;
public List<String> getList() {
System.out.println("User.list="+list);
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
System.out.println("User.set="+set);
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, Integer> getMap() {
System.out.println("User.map="+map);
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public Properties getPros() {
System.out.println("User.pros="+pros);
return pros;
}
public void setPros(Properties pros) {
this.pros = pros;
}
}
<?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.xsd">
<bean id="user" class="bean.User">
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<property name="map">
<map>
<entry key="map1" value="1"/>
<entry key="map2" value="2"/>
<entry key="map3" value="3"/>
</map>
</property>
<property name="pros">
<props>
<prop key="map1">RTT1</prop>
<prop key="map2">RTT2</prop>
<prop key="map3">RTT3</prop>
</props>
</property>
</bean>
</beans>
public class test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
User user=(User)context.getBean("user");
user.getList();
user.getMap();
user.getPros();
user.getSet();
}
}
注入null值
<bean id="..." class="exampleBean">
<property name="email"><null/></property>
</bean>
注入空字符串
<bean id="..." class="exampleBean">
<property name="email" value=""/>
</bean>
Spring Beans自动装配
Spring容器可以在不使用和元素的情况下自动装配相互协作的bean之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。
自动装配模式
下列自动装配模式,它们可用于指示Spring容器为来使用自动装配依赖注入。你可以使用元素的autowire属性为一个bean定义指定自动装配模式。
模式 | 描述 |
---|---|
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect(3.0版本不支持) | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
自动装配的局限性
限制 | 描述 |
---|---|
重写的可能性 | 你可以使用总是重写自动装配的 和 设置来指定依赖关系。 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
Spring自动装配byName
public class Student {
private String name;
private Book book;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
}
public class Book {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<bean id="student" class="bean.Student" autowire="byName">
<property name="name" value="RTT"/>
</bean>
<bean id="book" class="bean.Book">
<property name="name" value="java"/>
</bean>
public class test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("student");
System.out.println(student.getName()+"看了"+student.getBook().getName()+"这本书");
}
}
如果将 改成 将会报null pointer的错误
Spring自动装配byType
将之前的配置文件改为如下,就是按照类型自动装配
<bean id="student" class="bean.Student" autowire="byType">
<property name="name" value="RTT"/>
</bean>
<bean id="book1" class="bean.Book">
<property name="name" value="java"/>
</bean>
Spring由构造函数自动装配
public class Student {
private String name;
private Book book;
public Student(String name, Book book) {
this.name = name;
this.book = book;
}
public String getName() {
return name;
}
public Book getBook() {
return book;
}
}
<bean id="student" class="bean.Student" autowire="constructor">
<constructor-arg name="name" value="RTT"/>
</bean>
<bean id="book1" class="bean.Book">
<property name="name" value="java"/>
</bean>
Spring基于注解的配置
从Spring2.5开始就可以使用注解来配置依赖注入,而不是采用XML,你可以使用相关类,方法或字段声明的注解,将bean配置移动到组件类本身
注解连线在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 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/>
<!-- bean definitions go here -->
</beans>
序号 | 注解&描述 |
---|---|
1 | @Required 注解应用于 bean 属性的 setter 方法。 |
2 | @Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。 |
3 | @Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。 |
4 | JSR-250 Annotations:Spring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。 |
Spring@Required注解
@Required注解应用于bean属性的setter方法,它表明受影响的bean属性在配置时必须放在XML配置文件中,idea会出现爆红
public class Book {
private String name;
public String getName() {
return name;
}
@Required
public void setName(String name) {
this.name = name;
}
}
<?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="book" class="bean.Book">
<!-- <property name="name" value="Zara" />-->
</bean>
</beans>
但是运行不会出错
Spring@Autowired注解
@Autowired注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
那么使用@Autowire的原理是什么?
其实在启动spring IoC时,容器自动装载了一个AutowiredAnnotationPostProcessor后置处理器,当容器扫描到@Autowired、@Resource、@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性
注意事项:
在使用@Autowired时,首先在容器中查询对应类型的bean,如果查询结果刚好就一个,将该bean装配给@Autowired指定的数据,如果查询的结果不止一个(例如这个类被继承或是实现),那么@Autowired会根据名称来查找,可以使用@Qualifier指定需要装配bean的名称,如果查询的结果为空,那么会抛出异常,解决方法:使用required=false
Spring@Qualifier注解
当你创建多个具有相同类型的bean时,并且想要用一个属性只为它们其中一个进行装配,在这种情况下,你可以使用@Qualifier注解和@Autowired注解通过指定哪一个真正的bean将会被装配来消除误会。
Spring JSR-250注释
public class Book {
private String name;
public String getName() {
return name;
}
@Required
public void setName(String name) {
this.name = name;
}
@PostConstruct
public void init(){
System.out.println("实例化前");
}
@PreDestroy
public void destroy(){
System.out.println("销毁前");
}
}
<context:annotation-config/>
<!-- Definition for student bean -->
<bean id="book" class="bean.Book">
<property name="name" value="Zara" />
</bean>
public class test {
public static void main(String[] args) {
AbstractApplicationContext context1=new ClassPathXmlApplicationContext("xml/BeanAs.xml");
Book book=(Book)context1.getBean("book");
System.out.println(book.getName());
context1.registerShutdownHook();
}
}
singleton作用域的bean通常会随着容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则需要在JVM里面注册一个关闭钩子(shutdown hook)也就是registerShutdownHook(),这样就可以保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。
使用@PostConstruct和@PreDestroy可以作为init-method="init"和destroy-method=“destroy”的替代
@Resource
该属性以一个bean名称的形式被注入,遵循byName自动连接语义。
Spring基于Java的配置
基于Java的配置选项,可以使你在不用配置XML的情况下编写大多数的Spring.
@Configuration和@Bean注解
带有@Configuration的注解类表示这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
上面的代码等同于下面的XML配置:
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>
@Configuration
public class HelloConfig {
@Bean
public Book book(){
return new Book();
}
//注入bean的依赖性
@Bean
public Student student(){
return new Student("RTT",new Book());
}
}
public class testAnnotation {
public static void main(String[] args) {
AbstractApplicationContext ctx=
new AnnotationConfigApplicationContext(HelloConfig.class);
Book book=ctx.getBean(Book.class);
Student student=ctx.getBean(Student.class);
book.setName("455");
System.out.println(book.getName());
System.out.println(student.getBook().getName());
System.out.println( student.getName());
ctx.registerShutdownHook();
}
}
你可以加载各种配置类
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Import注解
@import注解允许从另一个配置类中加载@Bean定义。
@Configuration
@Import(HelloConfig.class)
public class HelloBConfig {
@Bean
public User user(){
return new User();
}
}
public class testAnnotation {
public static void main(String[] args) {
//将HelloConfig改成HelloBConfig
AbstractApplicationContext ctx=
new AnnotationConfigApplicationContext(HelloBConfig.class);
Book book=ctx.getBean(Book.class);
Student student=ctx.getBean(Student.class);
book.setName("455");
System.out.println(book.getName());
System.out.println(student.getBook().getName());
System.out.println( student.getName());
ctx.registerShutdownHook();
}
}
结果没有变化
生命周期回调
@Bean注解支持指定任意的初始化和销毁的回调方法:
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();
}
}
Spring中的事件处理
你已经看到了在所有章节中 Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。
通过 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 请求已经被服务。 |
public class ContextStartHandler implements ApplicationListener<ContextStartedEvent> {
@Override
public void onApplicationEvent(ContextStartedEvent contextStartedEvent) {
System.out.println("上下文开启被监听到了");
}
}
public class ContextStopHandler implements ApplicationListener<ContextStoppedEvent> {
@Override
public void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {
System.out.println("上下文停止被监听到了");
}
}
public class testEvent {
public static void main(String[] args) {
ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("xml/BeanAs.xml");
context.start();
Book book=(Book)context.getBean("book");
System.out.println("book="+book.getName());
context.stop();
}
}
Spring中的自定义事件
public class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
public void happen(){
System.out.println("自定义事件发生");
}
}
public class CustomPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher=applicationEventPublisher;
}
public void publish(){
CustomEvent customEvent=new CustomEvent(this);
applicationEventPublisher.publishEvent(customEvent);
}
}
public class CustomListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent customEvent) {
customEvent.happen();
}
}
<?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.xsd">
<bean id="customPublisher" class="bean.CustomPublisher"/>
<bean id="customListener" class="bean.CustomListener"/>
</beans>
public class testCustomEvent {
public static void main(String[] args) {
ConfigurableApplicationContext context=
new ClassPathXmlApplicationContext("xml/BeanBs.xml");
CustomPublisher customPublisher=(CustomPublisher)context.getBean("customPublisher");
customPublisher.publish();
customPublisher.publish();
}
}
Spring框架中的AOP
Spring框架的关键组件是面向切面编程框架。面向切面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。在软件开发过程中有各种各样的很好的切面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
AOP术语
顶 | 描述 |
---|---|
Aspect | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point | 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。 |
Introduction | 引用允许你添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
通知的类型:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知 |
后置通知 | 在一个方法之后,不考虑其结果,执行通知 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知 |
环绕通知 | 在建议方法调用之前和之后,执行通知 |
实现自定义方面
Spring支持@AspectJ annotation tyle的方法和基于模式来实现自定义方面。
方法 | 描述 |
---|---|
XML Schama based | 方面是使用常规类以及基于配置的XML来实现的 |
@AspectJ based | @AspectJ引用一种声明方面的风格作为带有Java5注释的常规Java类注释 |
Spring中基于AOP的XML架构
Spring JDBC框架
在使用普通的JDBC框架时,会需要写一些代码来打开连接和关闭连接等。但Spring JDBC框架负责所有的底层细节,从开始打开连接,准备和执行SQL语句,处理异常,处理事务,到最后关闭连接。
JdbcTemplate类
JdbcTemplate类执行SQL查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获JDBC异常并转换它们到org.springframework.dao包中定义的通用类、更多的信息、异常层次结构。
JdbcTemplate类的实例是线程安全配置的。所以你可以配置JdbcTemplate的单个实例,然后将这个共享的引用安全地注入到多个DAOs中。
使用JdbcTemplate类时常见的做法是在你的Spring配置文件中配置数据源,然后共享数据源bean依赖注入到DAO类中,并在数据源的设值函数中创建了JdbcTemplate。
Spring事务管理
ACID:
- 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么成功,要么失败。
- 一致性:这表示数据库的应用完整性的一致性,表中唯一的主键等。
- 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据破坏。
- 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。
Spring支持两种类型的事务管理:
- 编程式事务管理:这意味着你在编程的帮助下有管理事务,这给了你极大的灵活性。
- 声明式事务管理:这意味着你从业务代码中分离事务管理。你仅仅使用注释或XML配置来管理事务。
Spring 事务抽象
Spring事务管理的五大属性:隔离级别、传播行为、是否只读、事务超时、回滚规则
MVC框架
MVC框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的web应用程序的组件。
- 模型:封装了应用程序数据,并且通常他们由POJO组成。(POJO:简单java对象,POJO的内在含义是指那些没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的java对象)
- 视图:主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的HTML输出。
- 控制器:主要用于处理用户需求,并且构建合适的模型并将其传递到视图呈现。
DispatcherServlet
MVC框架是围绕DispatcherServlet设计的,DispatcherServlet用来处理所有的HTTP请求和响应,工作流程如下图:
- 收到一个HTTP请求后,DispatcherServlet根据HandlerMapping来选择并且调用适当的控制器。
- 控制器接受请求,并基于使用的GET或POST方法来调用适当的service方法。Service方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到DispatcherServlet中
- DispatcherServlet会从ViewResolver获取帮助,为请求检取定义视图。
- 一旦确定视图,DispatcherServlet将把模型数据传递给视图,最后呈现在浏览器中。
上面所提到的所有组件,即HandlerMapping、Controller和ViewResolver是WebApplicationContext的一部分,而WebApplicationContext是带有一些对web应用程序必要的额外特性的ApplicationContext的扩展。