《Spring In Action(第4版)》阅读总结(一)Spring上下文、Bean的生命周期、装配Bean

本文基于Spring5.05
官网地址:https://projects.spring.io/sp...java

spring 加载应用上下文

AnnotationConfigApplicationContext 一个或多个Java配置类加载Spring应用上下文
AnnotationConfigWebApplicationContext 一个或多个Java配置类加载Spring Web应用上下文
ClassPathXmlApplicationContext 从类路径下的一个或多个XML配置文件加载上下文,把应用上下文的定义文件做为类资源
FileSystemXmlApplicationContext 从文件系统下的一个或多个XML配置文件加载上下文
XmlWebApplicationContext 从Web应用下的一个或多个XML配置文件加载上下文
GenericApplicationContext 以可刷新的方式读取不一样bean定义格式加载上下文

Bean的生命周期

实例化 Spfing对Bean进行实例化
填充属性 Spring将值和bean的引用注入到bean对应的属性中
BeanNameAware 的 SetBeanName() 若是Bean实现了BeanNameAware接口,Spring将bean的id传递给SetBeanNamer()方法
BeanFactoryAware 的 setBeanFactory() 若是Bean实现了BeanFactoryAware,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
ApplicationContextAware 的 setApplicationContext() 若是Bean实现了ApplicationContextAware,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
BeanPostProcess 的 postProcessBeforeInitialization() -
InitializingBean 的 afterPropertiesSet() 和自定义初始方法 @PostConstruct -> InitializingBean -> init-method
BeanPostProcess 的 postProcessAfterInitialization() -
已完成Bean的初始化,使用Bean直至容器关闭 -
DisposableBean 的 destory()方法 和自定义销毁方法 @PreDestory -> DisposableBean -> destory-method

装配Bean

  1. 声明bean
  2. 构造器注入和Setter方法注入
  3. 装配bean
  4. 控制bean的建立和销毁

Spring配置的可选方案

  1. 在XML中进行显式配置
  2. 在Java中进行显式配置(推荐,一般用于第三方Bean定义)
  3. 隐式的bean发现机制和自动装配(推荐)

实现自动化配置

1)组件扫描(compoent sacnning):Spring会自动发现应用上下文所建立的bean
2)自动装配(autowiring):Spring自动知足bean之间的依赖
实现自动化配置一般是在Java类中声明注解,并经过组件扫描使指定范围内的注解生效的一种方式正则表达式

I 在XML中进行显式配置
元素 父元素 属性 说明
<bean>
声明Bean定义
<beans> id 惟一标示
name 名称,多个用','隔开,不能与已有id重复
class 指定实例类型
scope 单例/多例
lazy-init 懒加载
autowire 自动装配策略
autowire-candidate 是否参与自动注入
primary 优先注入
depends-on 依赖指定bean
init-method 自定义初始化方法
destroy-method 自定义销毁方法
abstract 抽象类,是则不建立对象
parent 指定父类Bean,继承父类属性值
factory-bean 指定工厂Bean
factory-method 指定工厂方法
<alias>
声明Bean的别名
<beans> name 指定Bean
alias 别名
<property>
经过Setter方法初始化Bean
(可以使用P-命名空间代替)
<bean> name 指定属性名,以setName()形式
ref 给引用类型指定bean
value 给基本类型赋值
<constructor-arg>
经过构造器方法初始化Bean
(spring3.0后可以使用c-命名空间代替)
<bean> index 构造方法参数索引,从0开始
type 构造方法参数类型,会有歧义
name 构造方法参数名称
ref 引用类型指定bean
value 基本类型赋值
<set>/<List>
声明集合
<constructor-arg>/<property> 注入参数为集合时使用(c/p-命名空间没法装配集合)
<ref>
声明集合元素
<set>/<List> bean 指定Bean定义
<value>
声明集合元素
<set>/<List> <value>val</value> 指定集合字面量元素值
util-命名空间 <beans> <util:constant> 应用某个类型的public static域,并将其暴露为bean
<util:list> 建立一个java.util.List类型的bean,其中包含值与引用
<util:set> 建立一个java.util.Set类型的bean,其中包含值与引用
<util:map> 建立一个java.util.Map类型的bean,其中包含值与引用
<util:properties> 建立一个java.util.properties类型的bean
<util:property-path> 应用某个类型的public static域,并将其暴露为bean
<import> 引入新的xml配置

Ps:关于p/c-命名空间,添加xml配置后,在bean中做为属性使用
语法:p:属性名[-ref]="字面量或BeanId"
属性名可设置为 name(参数名)、_0(参数下标,从0开始)、_(只有一个参数时可以使用)spring

II 在Java中进行显式配置
注解 说明 属性 属性说明
@Bean 声明Bean定义,定义在方法上,Bean的Id默认为方法名 value/name 指定Bean的Id
autowire 指定自动注入策略,默认为NO
initMethod 指定自定义初始化方法
destroyMethod 指定自定义销毁方法
@Scope 指定做用域 value/scopeName 设置单例、多例等做用域
proxyMode 设置动态代理模式,JDK、cglib等
@Import 导入新的Java配置类 value 指定要导入的Java配置类,可设置单个(a.class)或数组({a.class,b.class})
@ImportResource 导入新的XML配置 value 指定导入XML配置路径,可为单个("a.xml")或数组({"a.xml","b.xml"})
@PropertySource 导入新的properties文件 value 指定导入properties文件路径,可为单个("a.properties")或数组({"a.properties","b.properties"})
III 实现自动化装配
注解 说明 属性 属性说明
@Configuration 声明配置类,Spring会从配置类中加载上下文 value
@Component 声明组件,同@Controller,@Service... value 指定Bean的Id,也可经过@Named声明Id(jsr330)
@Autowired 可修饰类变量、set方法,以ByType方式自动注入组件,同@Resource(jsr250),@Inject(jsr330) required 默认true,未找到注入的Bean会报错,false关闭,关闭时注意NullPointerException
@Qualifier 与Autowired配置使用,指定Bean的Id注入 value 指定Bean的Id
@ComponentScan 启用组件扫描,也可在XML中配置<context:component-scan> basePackages 指定扫描包,可接受单个包名("com.*")和数组{"service","controller"},不安全,重构代码修改包结构会出现问题
basePackageClasses 指定类所在的包做为组件扫描的基础包。可经过在须要导入的包中建立并指定Marker interface空接口

高级装配

  1. Spring profile
  2. 条件化的bean声明
  3. 自动装配与歧义性
  4. bean的做用域
  5. Spring表达式语言

环境与profile

在Spring3.1中,Spring引入了bean profile的功能,要使用profile,将全部不一样的bean定义整理到一个或多个profile之中,在将应用部署到每一个环境时,要确保对应的profile处于激活(active)的状态。没有指定profile的bean始终会被建立数组

在Java中声明profile

@Profile()
在Spring3.1中,只能在类级别上使用@Profile注释,与@Configuration配合使用
在Spring3.2开始,能够再方法级别上使用,与@Bean注解一同使用安全

在XML中声明profile

在<beans>中声明profile属性
可在<beans>中嵌套声明<beans> 实现多个profile共存一个xml文档session

激活profile,依赖于两个独立的属性

spring.profiles.active 设置激活的profile
spring.profiles.default active未设置则默认为default值dom

设置激活的方式

  1. 做为DispatcherServlet的初始化参数 <init-param>
  2. 做为Web应用的上下文参数 <context-param>
  3. 做为JNDI条目
  4. 做为环境变量 ctx.getEnvironment().setActiveProfiles("dev");ctx.refresh();
  5. 做为JVM的系统属性 -Dspring.profiles.active="dev"
  6. 在集成测试类上,使用@ActiveProfiles注解设置

条件化的bean

Spring4引入@Conditional注解
可使用到带有@Bean注解的方法上。若是给定的条件为true,就会建立这个Bean,不然忽略。post

@Conditional能够指定任意实现Condition接口的类型,
并实现boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata)返回true则建立Bean测试

ConditionContext的做用:

  1. getRegistry() 返回的BeanDefinitionRegistry检查bean定义
  2. getBeanFactory() 返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性
  3. getEnvironment() 返回的Environment检查环境变量是否存在以及它的值是什么
  4. getResourceLoader() 返回的ResourceLoader 检查加载的资源
  5. getClassLoader() 返回的ClassLoader 加载并检查类是否存在

AnnotatedTypeMetadata的做用:

检查带有@Bean注解的方法上还有什么其余注解
isAnnotatrd() 指定注解是否存在
getAnnotationAttributes 等获取注解集合ui

spring4开始,Profile注解进行重构,使其基于@conditional和Condition实现。

处理自动装配的歧义性

仅有一个Bean匹配所需的结果时,自动装配才是有效的。
若是不只有一个bean可以匹配结果的话,这种歧义性会阻碍Spring自动装配属性、构造器参数或方法参数。

消除歧义性

  1. 选Bean中的某一个设为首选primary,@Primary(Java配置)、<bean primary="true">(XML配置)
  2. 使用限定符(qualifier)帮助Spring将可选Bean的范围缩小到一个bean。@Qualifier

Bean的做用域

单例(singleton): 在整个应用中,只会建立bean的一个实例(默认)
原型(prototype):每次注入或经过Spring应用上下文获取的时候,都会建立一个新的bean实例。
会话(session) :在Web应用中,为每一个会话建立一个bean实例
请求(request) :在Web应用中,为每一个请求建立一个bean实例
Java配置:使用 @Scope 注解声明做用域,能够与@Component 或 @Bean一块儿使用
XML配置:<bean scope="singleton">
可经过ConfigurableBeanFactory.SCOPE_PROTOTYPE 或 SCOPE_SINGLETON 指定
也可用WebApplicationContext.SCOPE_SESSION等(须要在Web应用中使用)
@Scope 还有一个proxyMode 能够设置动态代理模式(经过ScopedProxyMode 枚举类来设置JDK、cglib等)
在XML中声明做用域代理:<bean><aop:scoped-proxy ></bean>默认是cglib
可经过设置proxy-target-class=false 更改成JDK代理

Spring表达式语言(Spring Expression Language)

运行时值注入

1)属性占位符(Property placeholder)
2) Spring表达式语言(SpEL)

注入外部的值

使用@PropertySource和Environment

最简单方式:声明属性源(@PropertySource导入资源)并经过Spring的Environment来检索属性
@Autowired
Environment env;

Environment 经常使用API

String getProperty(String key)
String getProperty(String key, String defaultValue)
T getProperty(String key, Class<T> type)
T getProperty(String key, Class<T> type, String defaultValue)

String getRequiredProperty(String key)
T getRequiredProperty(String key, Class<T> type)

boolean containsProperty(String var1)

Class<T> getPropertyAsClass(String key, Class<T> type)

String[] getActiveProfiles(); 获取激活的profiles
String[] getDefaultProfiles(); 获取默认的profiles
boolean acceptsProfiles(String... var1); 若是environment支持给定profile的话,就返回true

解析属性占位符

在Spring装配中,占位符的形式为使用"${...}"包装的属性名称

在XML中使用属性占位符(前提是经过<context:property-placeholder location=" "/>引入资源)

<context:property-placeholder location="classpath:demo.properties"/>
<bean id="demo" class="" c:_name="${demo.name}">

在Java中使用属性占位符(需配置一个PropertyPlaceholderConfigurer 或 PropertySourcesPlaceholderConfigurer Bean,Spring 3.1 之后推荐后者,由于其可以基于Spring Environment及其属性源来解析占位符)

public class Demo {
    @Value("${demo.name}")
    private String name;
}

使用Spring表达式语言进行装配

Spring3引入Spring表达式语言(Spring Expression Language,SpEL)。经过表达式,在运行时计算获得值,实现装配。
SpEL的形式为"#{...}"

SpEL的特性包括:

1) 使用Bean的Id来引用Bean
2)调用方法和访问对象的属性
3)对值进行算术、关系和逻辑运算
4)正则表达式匹配
5)集合操做
除依赖注入,Spring Security支持SpEL定义安全限制规则;Thymeleaf模板支持SpEL引用模型数据

列子:

//获取当前时间的毫秒值,T()表达式会将java.lang.System视为Java中对应的类,并调用其静态方法currentTimeMills()
#{T(System).currentTimeMills()} 

//获取Id为demo的Bean,并引用其name属性(应该是经过get方法获取的吧!未验证)
#{demo.name}

//经过systemProperties对象引用系统属性
#{systemProperties['disc.title']}

SpEL基础表达式

//1)表示字面值
#{3.14159}
#{9.87E4}
#{'demo'}
#{false}    //true和false的计算结果就是它们对应的Boolean类型的值

//2)引用bean、属性和方法
#{demo}    //获取Id为demo的Bean的引用
#{demo.name}    //获取Id为demo的Bean的name属性值
#{demo.getName()}
#{demo.getName().toUpperCase()}
#{demo.getName()?.toUpperCase()}    //避免getName()返回null,出现NullPointException,使用'?.'类型安全的运算符,若是getName()返回null,则不会调用toUpperCase(),表达式返回null

//3)在表达式中使用类型(依赖T()这个关键的运算符,其真正价值在于访问目标类型的静态方法和常量)
#{T(java.lang.Math)}    //表示Math的class对象引用
#{T(java.lang.Math).PI}
#{T(java.lang.Math).random()}

//4)SpEL运算符
//算数运算:+ - * / % ^
//比较运算:< > == <= >= lt gt eq le ge
//逻辑运算:and or not
//条件运算:?:(ternary) ?:(Elvis)
//正则匹配:matches
#{2 * T(java.lang.Math).PI * cricle.radius}
#{T(java.lang.Math).PI * cricle.radius ^ 2}    //^ 是用于乘方计算的运算符
#{demo.name + 'and' + demo.realname}    //使用String类型的值,+ 为链接符
#{demo.age == 20}
#{demo.age eq 20}    //比较运算符有两种形式:符号形式和文本形式,二者等同,计算结果为Boolean值
#{demo.name != null ? demo.name : "roylion"}    //三元运算符的一个常见场景:检查null值,并用一个默认值替代null
#{demo.name ?: "roylion"}    //此三元运算符一般称为Elvis运算符,用来简化上述场景。
#{demo.emial matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}    //匹配正则表达式

//5)计算集合
#{demo.books[4].title}
#{demo.books[T(java.lang.Math).random() * demo.books.size()].title}
//[] 运算符能够从集合或数组中按照索引获取元素,甚至String(基于0开始)
#{'Im a big handsome '[3]}
//.?[...] 对集合进行过滤,获得集合的一个子集,
//[]中接受另外一个表达式,当SpEL迭代书本列表时,会对每一本书计算这个表达式,若是为true,则会存放到新的集合中
#{demo.books.?[title eq 'springInAction']}
#{demo.books.^[title eq 'springInAction']}    //.^[] 查询第一个匹配项
#{demo.books.$[title eq 'springInAction']}    //.$[] 查询最后一个匹配项

#{demo.books.![title]}    //.![] 从集合每一个成员中选择特定的属性放到另一个集合中

保持SpEL表达式的简洁,尽可能不要写复杂的SpEL表达式。由于SpEl表达式是String类型,测试困难

相关文章
相关标签/搜索