本文基于Spring5.05
官网地址:https://projects.spring.io/sp...
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
- 声明bean
- 构造器注入和Setter方法注入
- 装配bean
- 控制bean的创建和销毁
Spring配置的可选方案
- 在XML中进行显式配置
- 在Java中进行显式配置(推荐,通常用于第三方Bean定义)
- 隐式的bean发现机制和自动装配(推荐)
实现自动化配置
1)组件扫描(compoent sacnning):Spring会自动发现应用上下文所创建的bean
2)自动装配(autowiring):Spring自动满足bean之间的依赖
实现自动化配置通常是在Java类中声明注解,并通过组件扫描使指定范围内的注解生效的一种方式
元素 | 父元素 | 属性 | 说明 |
<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开始)、_(只有一个参数时可使用)
注解 | 说明 | 属性 | 属性说明 |
@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"}) |
注解 | 说明 | 属性 | 属性说明 |
@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空接口 |
高级装配
- Spring profile
- 条件化的bean声明
- 自动装配与歧义性
- bean的作用域
- 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文档
激活profile,依赖于两个独立的属性
spring.profiles.active 设置激活的profile
spring.profiles.default active未设置则默认为default值
设置激活的方式
- 作为DispatcherServlet的初始化参数 <init-param>
- 作为Web应用的上下文参数 <context-param>
- 作为JNDI条目
- 作为环境变量 ctx.getEnvironment().setActiveProfiles("dev");ctx.refresh();
- 作为JVM的系统属性 -Dspring.profiles.active="dev"
- 在集成测试类上,使用@ActiveProfiles注解设置
条件化的bean
Spring4引入@Conditional注解
可以使用到带有@Bean注解的方法上。如果给定的条件为true,就会创建这个Bean,否则忽略。
@Conditional可以指定任意实现Condition接口的类型,
并实现boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata)返回true则创建Bean
ConditionContext的作用:
- getRegistry() 返回的BeanDefinitionRegistry检查bean定义
- getBeanFactory() 返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性
- getEnvironment() 返回的Environment检查环境变量是否存在以及它的值是什么
- getResourceLoader() 返回的ResourceLoader 检查加载的资源
- getClassLoader() 返回的ClassLoader 加载并检查类是否存在
AnnotatedTypeMetadata的作用:
检查带有@Bean注解的方法上还有什么其他注解
isAnnotatrd() 指定注解是否存在
getAnnotationAttributes 等获取注解集合
spring4开始,Profile注解进行重构,使其基于@conditional和Condition实现。
处理自动装配的歧义性
仅有一个Bean匹配所需的结果时,自动装配才是有效的。
如果不仅有一个bean能够匹配结果的话,这种歧义性会阻碍Spring自动装配属性、构造器参数或方法参数。
消除歧义性
- 选Bean中的某一个设为首选primary,@Primary(Java配置)、<bean primary="true">(XML配置)
- 使用限定符(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类型,测试困难
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。