从spring2.5之后,spring注解驱动开发慢慢取代了Spring的xml配置文件的作用,而且目前流行的SpringBoot开发也是基于spring注解驱动做扩展的,所以想要理解好SpringBoot,就必须掌握一些spring的注解驱动。
@Configuration+@Bean
用于创建bean并注册到spring的IOC容器中
如果我们想将Person初始化并加载到IOC容器中
package com.example.demo;
public class Person {
private String name;
private int age;
}
以前xml的做法是
<bean id="person" class="com.example.demo.Person">
<property name="name" value="小小">
<property name="age" value="23">
<bean>
现在使用spring注解的做法是
// 等同于xml配置文件
@Configuration //定义一个配置类
public class MyConfig{
// 给容器注册一个Bean,如果不指定bean的id,默认为方法名
@Bean
//@Bean("persionX") 给bean指定ID名称
public Person person(){
new Person("小小",23)
}
}
@Componentscan
指定包的扫描范围,在指定包及其子包下的类只要标注了@Controller、@Service、@Repository、@Component等注解的类都会被注册到IOC容器中,@Componentscan(value="com.example.demo")注解加到在配置类(加了@Configuration的类或springboot的配置类(启动类))上,等同于xml配置的<context:component-scan base-package="com.example.demo"></context:component-scan>
@Componentscan的一些参数用法
excludeFilters过滤掉一些不需要被注册的类,FilterType.ANNOTATION以注解的类型过滤,
过滤掉标注了@Controller的类,标注了@Controller注解的类不会被注册到IOC容器中
//
@Componentscan(value="com.example.demo",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
// ----------------------------------------------------------------------
includeFilters 只包含,只注册满足扫描规则的类,只注册标注了@Service的类,
注意:使用includeFilters还需要加上useDefaultFilters=false禁用掉默认的过滤规则,includeFilters才会生效,因为默认是扫描所有的注解驱动
//
@Componentscan(value="com.example.demo",includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Service.class})
},useDefaultFilters=false)
// ---------------------------
FilterType的过滤规则类型
FilterType.ANNOTATION : 按照注解过滤
FilterType.ASSIGNABLE_TYPE:按照指定类型过滤
//
下面定义的FilterType.ASSIGNABLE_TYPE,classes={Hello.class}表示,只要是Hello类或者其子类都会被加载到IOC容器中
@Componentscan(value="com.example.demo",includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Service.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={Hello.class})
},useDefaultFilters=false))
//
FilterType.ASPECTJ:使用ASPECTJ表达式过滤
FilterType.REGEX:使用正则表达式过滤
//-----------------------------------------------------------------------
FilterType.CUSTOM:使用自定义规则
/**
*使用FilterType.CUSTOM自定义扫描规则,编写自定义类MyTypeFilter,
*需要实现org.springframework.core.type.filter.TypeFilter接口,重写match方法
*如果match方法返回ture,则满足条件,当前扫描的类会被加载到IOC容器中,false则反之
*/
@Componentscan(value="com.example.demo",includeFilters={
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters=false))
// --------------------------------------
// 自定义的TypeFilter类,如果match方法返回ture,则满足条件,当前扫描的类会被加载到IOC容器中,false则反之
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader:读取到的当前正在扫描的类的信息
* @param metadataReaderFactory:可以获取到其他任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的全类名(包+类名)
String className = classMetadata.getClassName();
System.out.println("当前扫描的类是:"+className);
// 只有全类名包含Controller字符串的类才满足条件
if (className.contains("Controller")){
return true;
}
return false;
}
}
java8之后ComponentScan注册加了@Repeatable(ComponentScans.class),可以直接在配置类上标注多个@Componentscan,在java8之前想配置多个@Componentscan扫描,需要用@ComponentScans
@Repeatable(ComponentScans.class)
public @interface ComponentScan {}
@Scope
指定bean的实例是单例还是多例,或者作用域,有4个属性值。默认为@Scope("singleton") 单例
1.prototype:指定当前bean为多实例:spring容器启动时不会创建对象放到容器中,而是每次需要调用该bean时才会创建
2.singleton:指定当前bean为单例(默认),spring容器启动时就会创建对象加载到容器中,每次调用该bean时直接从容器(map)中拿
3.request:同一个request请求使用同一个bean实例,只在web应用下生效
4.session:同一个session请求使用同一个bean实例,只在web应用下生效
@Lazy
该注解的作用是将bean创建设置为懒加载,针对于单例,spring容器启动时就会创建对象加载到容器中,如果在类上加上@Lazy注解,第一次调用该bean时才会去创建,然后放到spring容器中。之后的调用都从容器中获取
@Conditional
按照一定的条件进行判断,满足条件就给容器注册bean
标注在类或方法(一般是用于注册bean的方法(加了@Bean注册))上,只有满足条件才会给容器注册这个bean
自定义一个类(HelloConditional)通过实现Condition重写matches方法来编写判断条件的逻辑,matches方法返回true表示满足条件,就会给容器注册bean(就会执行hello方法,给容器注册Hello的bean)
@Configuration
public class MyConfig {
@Bean
@Conditional({HelloConditional.class})
public Hello hello(){
return new Hello();
}
}
// --------------------------------------------
//自定义条件判断的类,实现Condition接口的matches方法,该方法返回true表示满足条件
public class HelloConditional implements Condition {
/**
*
* @param context :判断条件使用的上下文
* @param metadata :注解信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
@Import
spring注册中给容器导入组件有几种方式,其中一种@Import,可以给容器快速导入组件 bean的id为全类名
@Import的使用方式
第一种使用方式:在配置类上加入@Import({Hello.class}),可以快速的把Hello的bean加入到容器中,bean的id为全类名。
第二种使用方式:@Import中传入实现了ImportSelector接口的类,并重写selectImports方法,这个方法的返回值就是需要导入到IOC容器的类的全类名
第三种使用方式:@Import中传入实现了ImportBeanDefinitionRegistrar接口的类,并重写registerBeanDefinitions方式来手工注册bean(看下面代码例子)
方式一,将Hello类注册到IOC容器
@Configuration
// 将hello加入到IOC容器中
@Import({Hello.class})
public class MyConfig {
}
方式二,将Hello类注册到IOC容器(不变),MyImportSelector实现了ImportSelector 接口,会把返回值所有类注册到IOC容器中
@Configuration
// 将hello和MyImportSelector返回的组数中的所有类加入到IOC容器中
@Import({Hello.class,MyImportSelector.class})
public class MyConfig {
}
实现了ImportSelector的类
public class MyImportSelector implements ImportSelector {
/**
*
* @param importingClassMetadata :当前标注了@Import注解的类(当前正在扫描的类)的所有注解
* @return 返回值就是需要导入到容器中的组件数组(全类名)
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.myspringbootstartautoconfigurer.HelloImport"};
}
}
// ------------------------------
方式三,通过实现ImportBeanDefinitionRegistrar接口手工将MyBean类注册到容器中,bean id为myBean666
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
}
// --
ImportBeanDefinitionRegistrar的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata : 当前加了@Import类的注解信息
* @param registry :BeanDefinition注册类 可以把需要注册到容器的bean通过registry.registerBeanDefinition方法手工注册到容器中
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// bean的定义信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(MyBean.class);
// 手工注册bean 第一个参数为bean的id,第二个为beanDefinition bean的定义信息
registry.registerBeanDefinition("myBean666",rootBeanDefinition);
}
}
给容器注册组件(bean)的几种方式
1.包扫描+组件标注注解(@Service、@Controller、@Repository、@Componet),这种方式用于导入自己写的类
2.@Bean方式导入,常用入导入第三方包里面的类 默认bean id为导入执行的方法名
3.@Import方式导入(有三种用法),默认的bean id为全类名
4.通过FactoryBean(工厂bean)
通过FactoryBean(工厂bean)给容器注册bean
通过实现FactoryBean<T>接口来创建bean
1.如果通过applicationContext.getBean("helloFactoryBean ")来获取,获取到的对象是工厂bean调用getObject创建的对象,即Hello对象
2.要获取工厂bean本身(HelloFactoryBean对象),需要在id前面加一个&,即
applicationContext.getBean("&helloFactoryBean ")。因为spring的BeanFactory定义了来获取工厂bean本身
3.如果通过@Autowired来获取的是工厂bean本身(HelloFactoryBean对象)
@Autowired
private HelloFactoryBean helloFactoryBean;
@Component
public class HelloFactoryBean implements FactoryBean<Hello> {
// 返回一个对象,这个对象会被注册到容器中
@Override
public Hello getObject() throws Exception {
return new Hello();
}
// 返回对应的类型
@Override
public Class<?> getObjectType() {
return null;
}
// 是否单例 true为单例
// false是多例,每次获取都会创建一个实例
@Override
public boolean isSingleton() {
return false;
}
}
@PropertySource
读取外部配置文件中的k/v保存到运行环境变量(environment)中
@Value()给属性赋值,可以读取到环境变量(environment)中的值
// 读取hello.properties配置文件的信息并保存到运行环境变量(environment)中
@PropertySource({"classpath:/hello.properties"})
@Configuration
public class MyConfig {
}
// @Value()读取配置文件中的值,下面代码表示如果配置文件中有hello.name的key,就读取它的value并赋值给name,没有就默认将666赋值给name
@Value("${hello.name:666}")
public String name;
自动装配注解
@Autowired
1.使用@Autowired自动注入(默认required=ture,表示必须注入,找不到对应的组件就会报错),默认优先按照类型去容器中找对应的组件进行注入
2.如果容器中有多个相同类型的组件(bean),会通过属性的名称作为组件的id去容器中查找
3.也可以通过@Qualifier指定组件的id进行装配
public class HelloService {
默认会先找HelloProperties这个类型进行注入
如果容器中有多个HelloProperties类型的bean,会通过属性的名称作为组件的id去容器中查找
也可以通过@Qualifier指定组件的id进行装配 (默认为@Qualifier("属性名"))
@Qualifier("helloProperties2") // 默认为@Qualifier("属性名")
@Autowired
HelloProperties helloProperties;
}
@Autowired使用在map<String,T>上
会自动将所有T类型的bean保存到map中,key为bean的id,value为bean对象
自动将所有Hello类型(包括子类)的bean保存到map中,key为bean的id,value为bean对象
public class HelloService {
@Autowired
private Map<String,HelloProperties> map;
}
@Autowired在构造方法上的使用,可以将构造方法上的参数自动注入
这种方式是在执行构造方法的时候就已经把参数注入了,而@Autowired写在属性上(上面的例子),是执行完构造方法后才注入的
public class HelloService {
默认不写@Autowired也是能注入的,
也可以写在参数前面 @Autowired HelloProperties helloProperties
@Autowired
public HelloService(HelloProperties helloProperties ){
System.out.println(helloProperties );
}
}
@Autowired + @Bean,注册组件时传入的参数自动注入
@Configuration
public class MyConfig {
@Autowired //自动注入hello参数 默认不加@Autowired也会自动注入
@Bean()
public HelloService helloService(Hello hello){
HelloService helloService = new HelloService();
helloService.setHello(hello);
return helloService;
}
}
管理Bean的生命周期
-bean创建->初始化->销毁
自定义bean的初始化和销毁方法
1.通过@Bean(initMethod="",destroyMethod="")指定初始化和销毁时的方法
构造函数:创建实例时调用,单实例对象在容器启动时就会创建bean(调用构造函数),多实例对象在每次获取时才调用
初始化方法:对象创建(调用构造函数)完成并赋值后调用会调用初始化方法
销毁方法:对于单例对象容器正常关闭时调用,但多实例对象是不会调用销毁方法的
@Configuration
public class MyConfig {
// 创建Hello的bean实例,通过initMethod指定初始化方法,destroyMethod指定销毁方法
@Bean(initMethod = "init",destroyMethod = "destroy")
public Hello hello(){
return new Hello();
}
}
// Hello类
public class Hello {
public String name;
public Hello(){
// 无参构造方法,创建bean时就调用
System.out.println("hello构造方法执行......");
}
// 自定义Hello的的初始化执行方法 在@Bean中指定这个方法为初始化方法即可
public void init(){
System.out.println("init......");
}
// 自定义Hello的的销毁执行方法 在@Bean中指定这个方法为销毁方法即可
public void destroy(){
System.out.println("destroy.......");
}
}
2.通过实现InitializingBean接口的afterPropertiesSet方法自定义初始化和DisposableBean的destroy方法销毁
@Component
public class HelloL implements InitializingBean,DisposableBean{
public HelloL(){
System.out.println("HelloL......构造方法");
}
// 销毁时调用
@Override
public void destroy() throws Exception {
System.out.println("HelloL....destroy");
}
// 对象创建并且属性赋值完之后,调用的初始化方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("HelloL....afterPropertiesSet");
}
}
3.使用JSR520的注解自定义初始化和销毁方法
@PostConstruct:在bean创建并完成赋值后执行的初始化方法
@PreDestroy:在容器销毁bean之前执行的方法
@Component
public class HelloPost {
public HelloPost(){
System.out.println("HelloPost......构造方法");
}
//在bean创建并完成赋值后执行的初始化方法
@PostConstruct
public void init(){
System.out.println("HelloPost.......@PostConstruct");
}
//在容器销毁bean之前执行的方法
@PreDestroy
public void destroy(){
System.out.println("HelloPost.......@PreDestroy");
}
}
后置处理器
场景:比如在bean创建时将spring的一些底屋组件注入到bean中,或者初始化前后进行拦截处理
BeanPostProcessor接口(Bean的后置处理器) ,bean创建对象初始化前后进行拦截处理
postProcessBeforeInitialization方法:在每个bean的初始化方法调用之前会调用这个方法
postProcessAfterInitialization方法:在每个bean的初始化方法调用之后会调用这个方法自定义后置处理器,实现BeanPostProcessor接口,会处理所有的bean
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
// 在每个bean的初始化方法调用之前都会调用执行方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..."+beanName);
return bean;
}
//在每个bean的初始化方法调用之前都会调用执行方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..."+beanName);
return bean;
}
}
Aware注入spring底层组件(其原理是通过后置处理器来实现的)
applicationContextAeare和EmbeddedValueResolverAeare都是通过ApplicationContextAwareProcessor后置处理器来实现的
如果需要使用spring底层组件如applicationContext、BeanFactroy、EmbeddedValueResolver等,可以通过实现对应的xxxAeare接口,在创建对象时会调用接口规定的方法注入相关组件(比BeanPostProcessor的方法先执行)。
@Component
public class HelloPost implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware{
private ApplicationContext applicationContext;
public HelloPost(){
System.out.println("HelloPost......构造方法");
}
@PostConstruct
public void init(){
System.out.println("HelloPost.......@PostConstruct");
}
@PreDestroy
public void destroy(){
System.out.println("HelloPost.......@PreDestroy");
}
// 注入ApplicationContext对象
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("appilcation...................");
this.applicationContext=applicationContext;
}
// 注入bean的名称
@Override
public void setBeanName(String name) {
System.out.println(name);
}
// 注入StringValueResolver,这个类的作用是用来解析字符串的占位符
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
System.out.println(resolver.resolveStringValue("当前操作系统:${os.name}"));
}
}
Bean工厂后置处理器
BeanFactoryPostProcessor接口
在Bean标准初始化之后调用(所有的baen定义信息已经保存加载到beanFactory,但是bean的实例还未创建),可以定制和修改BeanFactory的内容
/**
* 在BeanFactory标准初始化之后(所有的baen定义信息已经保存加载到beanFactory,但是bean的实例还未创建)调用
* 意思就是在baen定义信息(类名,socpe作用域,类属性等信息)加载完成之后调用(此时bean的实例还未创建)
*/
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取所有bean定义的id
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
}
}
BeanDefinitionRegistryPostProcessor接口
bean定义信息(BeanDefinitionRegistry)的后置处理器
这个接口extends BeanFactoryPostProcessor,在bean定义信息将要被加载时调用(bean实例还未创建),可手动把某个bean的定义信息加载进BeanDefinitionRegistry
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
// BeanDefinitionRegistry是 Bean定义信息的存储中心
// 以后BeanFactory就是按BeanDefinitionRegistry里面保存的每一个bean定义信息来创建bean实例的
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("postProcessBeanDefinitionRegistry..........bean的数量:"+registry.getBeanDefinitionCount());
// 手动将某个bean的定义信息加载到BeanDefinitionRegistry中
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Hello.class);
registry.registerBeanDefinition("helloBeanDefinition",rootBeanDefinition);
}
// 这是父类BeanFactoryPostProcessor的方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("子类postProcessBeanFactory..........bean的数量:"+beanFactory.getBeanDefinitionCount());
}
}
BeanFactoryPostProcessor是在bean定义信息加载完成后调用
BeanDefinitionRegistryPostProcessor是在bean定义信息将要加载时调用
先执行BeanDefinitionRegistryPostProcessor的实现类再执行BeanFactoryPostProcessor的实现类
Bean的生命周期流程图
创建bean的源码位置
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
->org.springframework.beans.factory.BeanFactory#getBean(java.lang.String, java.lang.Class<T>)
-->org.springframework.beans.factory.support.AbstractBeanFactory#createBean
--->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
执行bean对象的实例化
->org.springframework.beans.factory.support.AbstractBeanFactory#createBean
给bean的属性赋值
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
initializeBean方法主要做了三个操作1.循环执行后置处理器的的前置方法(applyBeanPostProcessorsBeforeInitialization)、2.再执行初始化方法(invokeInitMethods),3.再执行后置处理器的的后置方法(applyBeanPostProcessorsAfterInitialization)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
网友评论