Spring In Action
第一部分
核心概念:依赖注入(dependency injection),面向切面编程(aspect-oriented programming)
第一章
介绍DI和AOP以及怎样利用它们来开发松耦合的Java程序
Spring简化Java开发的关键策略
- 基于POJO的轻量级和最小侵入性编程
- 通过依赖性注入和面向接口实现松耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模板减少样板式代码
简化java开发:
- 构造器注入,依赖注入的一种,在构造时把必须实现的接口从构造方法传入,
如public BraveKnight(Quest quest){this.quest=quest;//Quest被注入,Quest不是具体的某个方式,这是松耦合的 }
- 横切关注点,系统服务被融入了自身逻辑以外的功能。AOP使这些功能变成声明的方式,确保了POJO的简单
- 使用模板消除样板式代码,JdbcTemplate等
- 构造器注入,依赖注入的一种,在构造时把必须实现的接口从构造方法传入,
容纳你的Bean
容器是Spring框架的核心 ,Spring容器通过使用依赖注入管理构成应用的组件。
Spring自带几种容器的实现,可以分为Bean Factories,和ApplicationContext。前者比较低级。
ClassPathXmlApplicationContext
从类文件下的XML配置文件中加载上下文定义,把应用上下文定义文件当作类资源
FileSystemXmlApplicationContext
XmlWebApplicationContext
Bean的生命周期:- Spring对Bean进行实例化
- Spring将值和Bean的引用注入进Bean对应的属性中
- 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()接口方法
- 如果Bean实现BeanFactoryAware接口,Spring将调用setBeanFactory()接口方法,将BeanFactory容器实例传入
- 如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()接口方法,将上下文引用传入
- 如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()方法
- 如果Bean实现了InitializingBean接口,将调用afterPropertiesSet()方法,若init-method声明了初始化方法,该方法也会被调用
- 如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法
- Bean准备就绪,将一直驻留在上下文中,直到该应用上下文被销毁
- 如果Bean实现DisposableBean接口,Spring将会调用destroy()方法,若Bean使用destroy-method声明了销毁方法,该方法也会被调用
Spring的模块组成
- 核心Spring容器
- AOP模块
- 数据访问与集成
- Web和远程调用
- 测试
第二章
介绍怎样使用依赖注入来配置和关联应用程序对象。如何编写松耦合的组建,
以及如何在Spring容器利用XML来织入它们的依赖和属性
创建Spring配置
传统方式
在XML文件中声明Bean时,Spring配置文件的根元素是来源于Spring 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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- <context:annotation-config /> -->
</beans>
命名空间
- aop
- beans
- context
- jee
- jms
- lang
- mvc
- oxm
- tx
- util
<bean id="" class=""/>
<bean>
元素是Spring中最基本的配置单元,通过该元素Spring将创建一个对象。
构造器注入对象引用
<bean id="" class="">
<constructor-arg value="" />
</bean>
通过工厂方式创造bean
当bean没有公开的构造方法时,使用静态工厂实例化对象,通过<bean>
的factory-method来装配bean。
public class Stage(){
private Stage(){}
//延迟加载
private static class StageSingletonHolder{
static Stage instance=new Stage();
}
//返回实例
public static Stage getInstance(){
return StageSingletonHolder.instance;
}
}
<bean id="theStage" class="" factory-method="" />
Bean的作用域
所有的Spring Bean默认都是单例的,如果要获得多例,要使用 <bean id="" class=" " scope="prototype" >
。
- singleton
- prototype
- request
- session
- globel-session
Spring的单例概念仅限于Spring的上下文 ,并不是真正的单例,即在每个类加载器中只有一个实例,Spring的单例只能保证在同一个Spring上下文中只有一个bean的实例。
初始化和销毁Bean
Bean生命周期的钩子方法:
- 通过
<bean>
元素中的 init-method和destroy-method指定初始化和移除时所执行的方法。 - 另一种实现方式,实现InitializingBean和DisposableBean接口,缺点:与SpringAPI产生耦合。
- 如果在上下文中定义的很多Bean都有相同名字的初始化方法和销毁方法,可以使用
<bean>
元素的default-init-method和default-destroy 为应用上下文中所有Bean设置共同的初始化和销毁方法。
注入Bean属性
setter注入
- 注入简单值:
<bean><property name="" value=""/></bean>
依赖注入的真正价值在于把相互协作的对象装配在一起,而不需要对象自己负责装配 - 引入其他Bean:
<property name="" ref="" />
- 注入内部Bean:类似内部类,并不限于setter注入,还可以把内部Bean装配到构造方法的入参,缺点:仅适用于一次注入,不能被复用,而且也不能被其他Bean所引用,同时影响了SpringXML配置的可读性
使用Spring的命名空间p装配属性
命名空间p的schema URI为http://www.springframework.org/schema/p。如果想使用命名空间p,需要在Spring的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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
然后就可以使用p:作为前缀装配属性<bean id="" class="" p:xxx="" p:xxxx-ref="" />
命名空间和property是等价的。
装配集合
<list><ref bean=""></list>
<set>
<map><entry value="" value-ref=""></map>
<props><prop key="">VALUE<prop></props>
property,props,prop区别:
<property>
元素用于把值或Bean引用注入到Bean的属性中<props>
用于定义一个java.util.Properties类型的集合值<prop>
用于定义<props>
集合的一个成员
装配空值
<property name=""><null/></property>
用于给一些默认属性赋空值,或者覆盖自动装配的值。
使用表达式装配
通过使用#{}调用Bean的属性或者方法。
使用null-safe存取器”?.”代替”.”防止抛出NullPointerException
使用T()运算符调用类作用域的方法和常量,如:T(java.lang.Math)
操纵集合<util:list>
,访问集合成员#{VALUE[]},查询集合成员”.?[]”
第三章 最小化Spring XML配置
让你可以用最少的XML装配应用对象,甚至不需要配置XML
自动装配Bean属性
4种类型的自动装配策略
- byName 把与Bean的属性具有相同名字或ID的其他bean自动装配到Bean的对应属性中,通过autowire=”byName”进行装配
- byType 只存在一个匹配时才使用,不然会报错
- constructor 把与Bean的构造器入参具有相同类型的其他bean自动装配到bean的对应入参中,首先移除
<constructor-args>
设置autowire=”constructor”,当应用上下文的Bean和入参匹配时,会自动装入 - autodetect 首先尝试constructor,如果失败,再尝试byType
默认自动装配
在根元素<beans>
上添加default-autowire属性,可以使指定Spring配置文件中的所有Bean采取同样的自动装配策略
混合使用自动装配和显式装配
注意不能混合使用constructor自动装配策略和<construcrtor-args>
使用注解装配
首先在配置文件中启用注解<context:annotation-config />
使用@Autowired
在setter上使用@Autowired注解后就可以删除对应的<property>
元素了,在构造方法上使用时,相当于<constructor-args>
。
还可以直接在属性上标注@Autowired,这样连setter都可以省略。
- 可选的自动装配
使用@Autowired时,如果没有Bean可以装配,就会抛出异常,当null可以接受时,可以使用@Autowired(required=false),如果没有找到对应类型的bean,就会设置为null。 - 限定歧义性的依赖
当多个Bean满足条件时,也会抛出异常(NoSuchBeanDefinitionException),这时需要使用@Qualifier()帮助Spring识别哪一个才是需要的Bean,这样的效果相当于byName。
自动检测Bean
用<context:component-scan>
代替<context:annotation-config>
,可以扫描指定的包和子包,查找出能够自动注册为Spring Bean的类,使用base-package标识了扫描的包。
为自动检测标注Bean
- @Component:通用的构造型注解,标识该类为Spring组件
- @Controller:标识将该类定义为Spring MVC controller
- @Repository:标识将该类定义为数据仓库
- @Service:标识将该类定义为服务
- 使用@Component标注的任意自定义注解
过滤组件扫描
<context:include-filter>
的type属性和expression属性一起协作来定义组件扫描策略。
- annotation
- assignable
- aspectj
- custom
- regex
但是默认的过滤策略是最经常使用的。
使用Spring基于Java的配置
创建基于Java的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="" />
</beans>
<context:component-scan base-package="" />
也会自动加载@Configuration注解所标注的类。
定义一个配置类
package test;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfTest {
//Bean配置
}
使用@Configuration注解的java类相当于XML中<beans>
元素,这个注解会告知Spring,这个类将包含一个或多个Spring Bean的定义。
声明一个简单的Bean
@Bean
public Test testBean(){
return new Test();
}
这段代码等价于<bean>
元素,该方法返回的对象会被注册为Spring应用上下文中的一个Bean,方法名作为Bean的ID。
对比XML配置Bean,如果XML中的标识符在重命名Bean时忘记修改,在编译期难以发现问题。
使用Spring的基于Java的配置进行注入
在上面的Bean类中直接构造器注入或者setter注入。
在Bean中引用Bean时也不会创建多个被引用的实例,Spring会拦截该调用。
小结
- 通过自动装配代替
<property>
和<constructor-args>
- 通过组件检测自动处理配置整个
<bean>
元素 - 使用java代替XML定义Spring配置