参考文章:https://blog.csdn.net/weixin_48033662/article/details/120000961

核心注解

注解 功能
@Bean 容器中注册组件
@Primary 同类组件如果有多个,标注主组件
@DependsOn 组件之间声明依赖关系
@Lazy 组件懒加载(最后使用的时候才创建)
@Scope 声明组件的作用范围(SCOPE_PROTOTYPE,SCOPE_SINGLETON)
@Configuration 声明这是一个配置类,替换以前配置文件
@Component @Controller、@Service、@Repository
@Indexed 加速注解,所有标注了 @Indexed 的组件,直接会启动快速加载
@Order 数字越小优先级越高,越先工作
@ComponentScan 包扫描
@Conditional 条件注入
@Import 导入第三方jar包中的组件,或定制批量导入组件逻辑
@ImportResource 导入以前的xml配置文件,让其生效
@Profile 基于多环境激活
@PropertySource 外部properties配置文件和JavaBean进行绑定.结合ConfigurationProperties
@PropertySources @PropertySource组合注解
@Autowired 自动装配
@Qualifier 精确指定
@Value 取值、计算机环境变量、JVM系统。xxxx。@Value(“${xx}”)
@Lookup 单例组件依赖非单例组件,非单例组件获取需要使用方法

XML和注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
// 通过xml获取配置信息
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// Person person = (Person) applicationContext.getBean("person");
// System.out.println(person);

// 通过@Configuration获取配置信息
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person);

// Person这个类型的组件在IOC容器中的名字是什么呢?
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);
}
}

@Bean

注册bean

想ioc中主动注入bean

原始xml配置:

1
2
3
4
<bean id="person" class="top.lvxiaoyi.bean.Person">
<property name="age" value="18"></property>
<property name="name" value="lvxiaoyi"></property>
</bean>

使用注解方式向Spring的IOC容器中注入JavaBean时

  • 如果没有在@Bean注解中明确指定bean的名称,那么就会使用当前方法的名称来作为bean的名称;

    1
    2
    3
    4
    5
    6
    @Bean
    public Person person01() {
    return new Person("lvxiaoyi", 20);
    }
    // 获取benaName
    // applicationContext.getBeanNamesForType(Person.class);

    这个时候的beanName为person01

  • 如果在@Bean注解中明确指定了bean的名称,那么就会使用@Bean注解中指定的名称来作为bean的名称。

    1
    2
    3
    4
    @Bean("person")
    public Person person01() {
    return new Person("lvxiaoyi", 20);
    }

    这个时候的beanName为person

自定义初始化和销毁方法

使用

在Spring中,我们可以自己来指定bean的初始化和销毁的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。

xml:

1
2
3
4
<bean id="person" class="top.lvxiaoyi.bean.Person" init-method="init" destroy-method="destroy">
<property name="age" value="18"></property>
<property name="name" value="lvxiaoyi"></property>
</bean>

==注意:在我们自己写的Person类中,需要存在init()方法和destroy()方法。而且Spring中还规定,这里的init()方法和destroy()方法必须是无参方法,但可以抛出异常。==

1
2
3
4
5
6
7
public void init() {
System.out.println("Person ... init...");
}

public void destroy() {
System.out.println("Person ... destroy...");
}

bean注解:

1
2
3
4
@Bean(initMethod="init", destroyMethod="destroy")
public Person person01() {
return new Person("lvxiaoyi", 18);
}

执行过程

bean的销毁方法是在容器关闭的时候被调用的。

1
2
3
4
5
6
7
8
9
10
11
@Test
public void test01() {
// 1. 创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");

// 关闭容器
// 这个时候才执行销毁方法
applicationContext.close();

}
  • bean对象的初始化方法调用的时机:对象创建完成,如果对象中存在一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化方法。对于单实例bean来说,在Spring容器创建完成后,Spring容器会自动调用bean的初始化方法;对于多实例bean来说,在每次获取bean对象的时候,调用bean的初始化方法。
  • bean对象的销毁方法调用的时机:对于单实例bean来说,在容器关闭的时候,会调用bean的销毁方法;对于多实例bean来说,Spring容器不会管理这个bean,也就不会自动调用这个bean的销毁方法了。可以(必须)手动调用多实例bean的销毁方法。

使用场景

一个典型的使用场景就是对于数据源的管理。例如,在配置数据源时,在初始化的时候,会对很多的数据源的属性进行赋值操作;在销毁的时候,我们需要对数据源的连接等信息进行关闭和清理。这个时候,我们就可以在自定义的初始化和销毁方法中来做这些事情了!

@ComponentScan

基本使用

指定包扫描路径,指定路径后该路径下的包及其子包都会被扫描到,且标注了@Repository、@Service、@Controller、@Component注解的类会自动注入到Spring容器中。

原始xml配置:

1
<context:component-scan base-package="top.lvxiaoyi"></context:component-scan>

@ComponentScan配置:

在配置上或启动类上添加

1
@ComponentScan(value="top.lvxiaoyi") 

指定包

1
Filter[] includeFilters() default {};

这个一个数组,而且参数名为includeFilters

使用:

==注意:当使用includeFilters()方法指定只包含哪些组件时,需要禁用掉默认的过滤规则。==

1
2
3
4
5
6
7
8
9
10
11
12
@ComponentScan(
value="top.lvxiaoyi", includeFilters={
/*
* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
* classes:我们需要Spring在扫描时,只包含@Controller注解标注的类
*/
// @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
// @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
@Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class})
},
// 禁用掉默认的过滤规则
useDefaultFilters=false)

xml:

1
<context:component-scan base-package="com.meimeixia" use-default-filters="false"></context:component-scan>

自定义过滤规则:MyTypeFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyTypeFilter implements TypeFilter {

/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();

String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}

}

具体Filter的使用,可以查看api文档,或者看源码

1
2
3
4
5
6
7
8
9
10
11
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;

@AliasFor("classes")
Class<?>[] value() default {};

@AliasFor("value")
Class<?>[] classes() default {};

String[] pattern() default {};
}

小知识

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
// 作用目标是:接口、类、枚举、注解
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan

@Repeatable注解是java8提供的注解,标志着这个注解可以在重复使用在你指定的类型上

排除包

1
2
3
4
5
6
7
@ComponentScan(value="top.lvxiaoyi", excludeFilters={
/*
* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
* classes:除了@Controller和@Service标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。
*/
@Filter(type=FilterType.ANNOTATION, classes={Controller.class, Service.class})
}) // value指定要扫描的包

@Component

凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。

@Scope

@Scope注解能够设置组件的作用域

在这里插入图片描述

1
2
3
4
ConfigurableBeanFactory#SCOPE_PROTOTYPE
ConfigurableBeanFactory#SCOPE_SINGLETON
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION

xml:

1
2
3
4
<bean id="person" class="top.lvxiaoyi.bean.Person" scope="prototype" >
<property name="age" value="${}"></property>
<property name="name" value="lvxiaoyi"></property>
</bean>
1
2
3
4
5
6
7
8
@Configuration
public class MainConfig2 {
@Scope("prototype") // 通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
@Bean("person")
public Person person() {
return new Person("lvxiaoyi", 25);
}
}

@Lazy

组件懒加载(最后使用的时候才创建)

1
2
3
4
5
6
@Lazy
@Bean("person")
public Person person() {
System.out.println("给容器中添加Person对象...");
return new Person("lvxiaoyi", 25);
}

==懒加载,也称延时加载,仅针对单实例bean生效==。 单实例bean是在Spring容器启动的时候加载的,添加@Lazy注解后就会延迟加载,在Spring容器启动的时候并不会加载,而是在第一次使用此bean的时候才会加载,但当你多次获取bean的时候并不会重复加载,只是在第一次获取的时候才会加载,这不是延迟加载的特性,而是单实例bean的特性。

若bean是多实例,则Spring容器启动时,不会实例化bean,也不会将bean注册到IOC容器中,只是在以后每次从IOC容器中获取bean的时候,都会创建一个新的bean返回。

@Conditional

@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。需要自定义匹配方法,这个方法需要实现Condition 接口

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class LinuxCondition implements Condition {

/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断操作系统是否是Linux系统

// 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2. 获取到类加载器
ClassLoader classLoader = context.getClassLoader();
// 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
Environment environment = context.getEnvironment();
// 4. 获取到bean定义的注册类
// Spring容器中所有的bean都可以通过BeanDefinitionRegistry对象来进行注册,因此我们可以通过它来查看Spring容器中到底注册了哪些bean,并且可以添加、移除、查看bean信息、查看是否包含bean
BeanDefinitionRegistry registry = context.getRegistry();

String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}

return false;
}

}

使用:

1
2
3
4
5
@Conditional({LinuxCondition.class})
@Bean("myLinux")
public Person person02() {
return new Person("myLinux", 18);
}

在这里插入图片描述

@Conditional与@Profile这俩注解的对比

Spring 3.0也有一些和@Conditional相似的注解,它们是Spring SPEL表达式和Spring Profiles注解,但是Spring 4.0之后的@Conditional注解要比@Profile注解更加高级。@Profile注解用来加载应用程序的环境,该注解仅限于根据预定义属性编写条件检查,而@Conditional注解则没有此限制。

Spring中的@Profile和@Conditional这俩注解都是用来检查If…then…else的语义。然而,Spring 4.0之后的@Conditional注解是@Profile注解的更新用法。

Spring 3.0中的@Profile仅用于编写基于Environment变量的条件检查。配置文件可用于基于环境加载应用程序配置。
Spring 4.0之后的@Conditional注解允许开发人员为条件检查定义用户定义的策略。此外,@Conditional注解还可以用于条件bean注册。

@Import

我们自己写的类,自然是可以通过包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component)的形式将其注册到IOC容器中,但这种方式比较有局限性,局限于我们自己写的类,比方说我们自己写的类,我们当然能把以上这些注解标注上去了。

我们在项目中会经常引入一些第三方的类库,我们需要将这些第三方类库中的类注册到Spring容器中,此时,我们就可以使用@Bean和@Import注解将这些类快速的导入Spring容器中。

@Import注解的使用方式

@Import注解的三种用法主要包括:

  1. 直接填写class数组的方式

    1
    2
    3
    4
    @Configuration
    // @Import({Color.class, Red.class}) 可以多个
    @Import(Color.class) // @Import快速地导入组件,id默认是组件的全类名
    public class MainConfig2
  2. ImportSelector接口的方式,即批量导入,这是重点

    1
    2
    3
    @Configuration
    @Import(MyImportSelector.class)
    public class MainConfig2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class MyImportSelector implements ImportSelector {
    // 返回值:就是要导入到容器中的组件的全类名
    // AnnotationMetadata:当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[]{"com.lvxiaoyi.bean.Bule", "com.lvxiaoyi.bean.Yellow"};
    }

    }

  3. ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

    Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。

    所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。

    ImportBeanDefinitionRegistrar需要配合@Configuration和@Import这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。

    1
    2
    3
    @Configuration
    @Import(MyImportBeanDefinitionRegistrar.class)
    public class MainConfig2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
    * AnnotationMetadata:当前类的注解信息
    * BeanDefinitionRegistry:BeanDefinition注册类
    *
    * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
    */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 查看是否包含这个bean,上面@Conditional的时候提到过
    boolean definition = registry.containsBeanDefinition("com.lvxiaoyi.bean.Red");
    boolean definition2 = registry.containsBeanDefinition("com.lvxiaoyi.bean.Bule");
    if (definition && definition2) {
    // 指定bean的定义信息,包括bean的类型、作用域等等
    // RootBeanDefinition是BeanDefinition接口的一个实现类
    RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); // bean的定义信息
    // 注册一个bean,并且指定bean的名称
    registry.registerBeanDefinition("rainBow", beanDefinition);
    }
    }

    }

非注解

FactoryBean注册bean

一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。

FactoryBean接口对于Spring框架来说占有非常重要的地位,Spring自身就提供了70多个FactoryBean接口的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式。

1
2
3
4
5
6
7
8
9
10
package org.springframework.beans.factory;

public interface FactoryBean<T> {
// 返回由FactoryBean创建的bean实例,如果isSingleton()返回true,那么该实例会放到Spring容器中单实例缓存池中
T getObject() throws Exception;
// 返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
Class<?> getObjectType();
// 返回FactoryBean创建的bean实例的类型
boolean isSingleton();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}

@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Color.class; // 返回这个对象的类型
}

// 是单例吗?
// 如果返回true,那么代表这个bean是单实例,在容器中只会保存一份;
// 如果返回false,那么代表这个bean是多实例,每次获取都会创建一个新的bean
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}

}

使用FactoryBean注解的时候,获取的name为类名(ColorFactoryBean),但是类型为你实际的注册bean的类型(top.lvxiaoyi.bean.Color)

因为注册bean的时候,是注册的ColorFactoryBean,而不是Color,那么我们如果想要拿到ColorFactoryBean类型的bean怎么获取?

只需要在获取工厂Bean本身时,在id前面加上&符号即可,例如&colorFactoryBean。

1
2
3
Object bean4 = applicationContext.getBean("&colorFactoryBean");
//class top.lvxiaoyi.bean.colorFactoryBean
System.out.println(bean4.getClass());

为什么?

因为我们所有的bean都是通过BeanFactory生产的,我们去查看BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface BeanFactory {

/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/

/**
*用于解除对FactoryBean实例的引用,并将其与由FactoryBean创建的bean区分开来。
*例如,如果名为myJndiObject的bean是一个FactoryBean,获取&myJndiObject将返回工厂,而不是由工厂返回的实例。
*/
String FACTORY_BEAN_PREFIX = "&";

我们看到BeanFactory的第一个Filed就是这个”&”。

InitializingBean和DisposableBean初始化和销毁

InitializingBean接口

概述

为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface InitializingBean {

/**
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;

}

何时调用

我们定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory这个类里面的invokeInitMethods()方法中,来查看Spring加载bean的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
// 判断bean是否实现了initializingBean接口,如果实现了InitializingBean接口,那么调用bean的afterPropertiesSet
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
// 如果配置了系统安全管理器
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}else {
((InitializingBean) bean).afterPropertiesSet();
}
}

if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);//通过反射的方式调用init-method
}
}
}

分析上述代码后,我们可以初步得出如下信息:

Spring为bean提供了两种初始化的方式,实现InitializingBean接口(也就是要实现该接口中的afterPropertiesSet方法),或者在配置文件或@Bean注解中通过init-method来指定,==两种方式可以同时使用==,==同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。==

实现InitializingBean接口是直接调用afterPropertiesSet()方法,与通过反射调用init-method指定的方法相比,效率相对来说要高点。但是init-method方式消除了对Spring的依赖。

如果调用afterPropertiesSet方法时出错,那么就不会调用init-method指定的方法了。

DisposableBean接口

概述

实现org.springframework.beans.factory.DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。也就是说我们可以实现DisposableBean这个接口来定义咱们这个销毁的逻辑。

1
2
3
4
5
6
7
8
9
10
11
public interface DisposableBean {

/**
* Invoked by a BeanFactory on destruction of a singleton.
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
void destroy() throws Exception;

}

注意

多实例bean的生命周期不归Spring容器来管理,这里的DisposableBean接口中的方法是由Spring容器来调用的,所以如果一个多实例bean实现了DisposableBean接口是没有啥意义的,因为相应的方法根本不会被调用,当然了,在XML配置文件中指定了destroy方法,也是没有任何意义的。所以,==在多实例bean情况下,Spring是不会自动调用bean的销毁方法==。

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
public class Cat implements InitializingBean,DisposableBean {

public Cat(){
System.out.println("cat constructor...");
}
/**
* 会在容器关闭的时候进行调用
*/
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat...destroy...");
}
/**
* 会在bean创建完成,并且属性都赋好值以后进行调用
*/
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat...afterPropertiesSet...");
}

}
1
2
3
4
5
6
7
8
@Configuration
public class MainConfigOfLifeCycle {

@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}

BeanPostProcessor

Spring容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接口的实现类中的这两个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public interface BeanPostProcessor {

/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

postProcessBeforeInitialization

也就是,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。

当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。

postProcessAfterInitialization

后置处理器可用于bean对象初始化前后进行逻辑增强。Spring提供了BeanPostProcessor接口的很多实现类,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的实现,AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的动态代理等等。

以AnnotationAwareAspectJAutoProxyCreato我们都知道spring AOP的实现原理是动态代理,最终放入容器的是代理类的对象,而不是bean本身的对象,那么Spring是什么时候做到这一步的呢?就是在AnnotationAwareAspectJAutoProxyCreator后置处理器的postProcessAfterInitialization方法中,即bean对象初始化完成之后,后置处理器会判断该bean是否注册了切面,若是,则生成代理对象注入到容器中。

定位到AbstractAutoProxyCreator抽象类中的postProcessAfterInitialization方法处

1
2
3
4
5
6
7
8
9
10
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}

return bean;
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}

@Override
public int getOrder() {
// TODO Auto-generated method stub
return 3;
}

}
1
Constructor(构造方法)→postProcessBeforeInitialization→bean的afterPropertiesSet

注解

@PostConstruct注解和@PreDestroy注解

@PostConstruct注解

是Java自己的注解,是JSR-250规范里面定义的一个注解。

  • @PostConstruct注解被用来修饰一个非静态的void()方法。

  • 被@PostConstruct注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。

  • 被@PostConstruct注解修饰的方法通常在构造函数之后,init()方法之前执行。

通常我们是会在Spring框架中使用到@PostConstruct注解的,该注解的方法在整个bean初始化中的执行顺序如下:

1
Constructor(构造方法)→@Autowired(依赖注入)→@PostConstruct(注释的方法)

@PreDestroy

@PreDestroy注解同样是Java提供的,它也是JSR-250规范里面定义的一个注解。

被@PreDestroy注解修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy注解修饰的方法会在destroy()方法之后,Servlet被彻底卸载之前执行。执行顺序如下所示:

1
调用destroy()方法→@PreDestroy→destroy()方法→bean销毁

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class Dog {

public Dog() {
System.out.println("dog constructor...");
}

// 在对象创建完成并且属性赋值完成之后调用
@PostConstruct
public void init() {
System.out.println("dog...@PostConstruct...");
}

// 在容器销毁(移除)对象之前调用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}

}