好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

使用spring容器在初始化Bean时前和后的操作

spring容器初始化Bean操作

在某些情况下,Spring容器在初始化Bean的时候,希望在初始化bean前和销毁bean前进行一些资源的加载和释放的操作。可以通过一下三种方式完成。

Bean的方法加上@PostConstruct和@PreDestroy注解 在xml中定义init-method和destory-method方法 Bean实现InitializingBean和DisposableBean接口

@PostConstruct和@PreDestroy注解

JavaBean代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Component

public class PersonService {

     private String message;

     public String getMessage() {

         return message;

     }

     public void setMessage(String message) {

         this .message = message;

     }

     @PostConstruct

     public void init() {

         System.out.println( "PersonService.class init method ..." );

     }

     @PreDestroy

     public void cleanUp() {

         System.out.println( "PersonService.class cleanUp method ..." );

     }

}

spring配置文件

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<? xml version = "1.0" encoding = "UTF-8" ?>

< beans xmlns = "http://HdhCmsTestspringframework.org/schema/beans"

     xmlns:xsi = "http://HdhCmsTestw3.org/2001/XMLSchema-instance" xmlns:context = "http://HdhCmsTestspringframework.org/schema/context"

     xmlns:aop = "http://HdhCmsTestspringframework.org/schema/aop" xmlns:mvc = "http://HdhCmsTestspringframework.org/schema/mvc"

     xsi:schemaLocation="

         http://HdhCmsTestspringframework.org/schema/beans

         http://HdhCmsTestspringframework.org/schema/beans/spring-beans-4.2.xsd       

         http://HdhCmsTestspringframework.org/schema/context

         http://HdhCmsTestspringframework.org/schema/context/spring-context-4.2.xsd

         http://HdhCmsTestspringframework.org/schema/aop

         http://HdhCmsTestspringframework.org/schema/aop/spring-aop-4.2.xsd

         http://HdhCmsTestspringframework.org/schema/mvc

         http://HdhCmsTestspringframework.org/schema/mvc/spring-mvc-4.2.xsd">

     <!-- spring扫描的路径 -->

     < context:component-scan base-package = "spring.zhujie" />

</ beans >

测试代码和结果

测试代码

?

1

2

3

4

public static void main(String[] args) {

      AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-zhujie.xml" );

      context.registerShutdownHook();

}

运行结果

PersonService.class init method ...

PersonService.class cleanUp method ...

在XML中定义init-method和destory-method方法

JavaBean代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class PersonService {

     private String message;

     public String getMessage() {

         return message;

     }

     public void setMessage(String message) {

         this .message = message;

     }

     public void init() {

         System.out.println( "PersonService.class init method ..." );

     }

     public void cleanUp() {

         System.out.println( "PersonService.class cleanUp method ..." );

     }

}

spring配置文件

?

1

< bean class = "spring.zhujie.PersonService" init-method = "init" destroy-method = "cleanUp" />

测试代码和结果

测试代码

?

1

2

3

4

public static void main(String[] args) {

         AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-xml.xml" );

         context.registerShutdownHook();

}

运行结果

PersonService.class init method ...

六月 23, 2017 9:42:06 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose

信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7a94c5e7: startup date [Fri Jun 23 21:42:06 CST 2017]; root of context hierarchy

PersonService.class cleanUp method ...

Bean实现InitializingBean和DisposableBean接口

JavaBean代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class PersonService implements InitializingBean, DisposableBean {

     private String message;

     public String getMessage() {

         return message;

     }

     public void setMessage(String message) {

         this .message = message;

     }

     @Override

     public void afterPropertiesSet() throws Exception {

         System.out.println( "PersonService.class init method ..." );

     }

     @Override

     public void destroy() throws Exception {

         System.out.println( "PersonService.class cleanUp method ..." );

     }

}

spring配置文件

?

1

< bean id = "personService" class = "spring.zhujie.PersonService" />

测试代码和结果

测试代码

?

1

2

3

4

public static void main(String[] args) {

         AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-interface.xml" );

         context.registerShutdownHook();

     }

运行结果

PersonService.class init method ...

PersonService.class cleanUp method ...

Spring bean 初始化顺序

InitializingBean, init-method 和 PostConstruct

1、概述

从接口的名字上不难发现,InitializingBean 的作用就是在 bean 初始化后执行定制化的操作。

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:

通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;

通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 /销毁之前调用的操作方法;

在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

2、InitializingBean vs init-method

接口定义如下:

?

1

2

3

public interface InitializingBean {

     void afterPropertiesSet() throws Exception;

}

接口只有一个方法afterPropertiesSet,

此方法的调用入口是负责加载 spring bean 的AbstractAutowireCapableBeanFactory,源码如下:

?

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

40

41

protected void invokeInitMethods(String beanName, Object bean,

    RootBeanDefinition mbd) throws Throwable {

   boolean isInitializingBean = bean instanceof InitializingBean;

   if ((isInitializingBean)

     && (((mbd == null ) || (!(mbd

       .isExternallyManagedInitMethod( "afterPropertiesSet" )))))) {

    if ( this .logger.isDebugEnabled()) {

     this .logger

       .debug( "Invoking afterPropertiesSet() on bean with name '"

         + beanName + "'" );

    }

    //先调用afterPropertiesSet()进行初始化

    if (System.getSecurityManager() != null ) {

     try {

      AccessController.doPrivileged(

        new PrivilegedExceptionAction(bean) {

         public Object run() throws Exception {

          ((InitializingBean) this .val$bean)

            .afterPropertiesSet();

          return null ;

         }

        }, getAccessControlContext());

     } catch (PrivilegedActionException pae) {

      throw pae.getException();

     }

    } else {

     ((InitializingBean) bean).afterPropertiesSet();

    }

   }

  

   //然后调用InitMethod()进行初始化

   if (mbd != null ) {

    String initMethodName = mbd.getInitMethodName();

    if ((initMethodName == null )

      || ((isInitializingBean) && ( "afterPropertiesSet"

        .equals(initMethodName)))

      || (mbd.isExternallyManagedInitMethod(initMethodName)))

     return ;

    invokeCustomInitMethod(beanName, bean, mbd);

   }

  }

从这段源码可以得出以下结论:

spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用

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

先调用afterPropertiesSet,再执行 init-method 方法,如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法

3、@PostConstruct

通过 debug 和调用栈找到类InitDestroyAnnotationBeanPostProcessor, 其中的核心方法,即 @PostConstruct 方法调用的入口:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

     LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());

     try {

         metadata.invokeInitMethods(bean, beanName);

     }

     catch (InvocationTargetException ex) {

         throw new BeanCreationException(beanName, "Invocation of init method failed" , ex.getTargetException());

     }

     catch (Throwable ex) {

         throw new BeanCreationException(beanName, "Failed to invoke init method" , ex);

     }

     return bean;

}

从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被调用的。另外通过跟踪,@PostConstruct方法的调用方式也是通过反射机制。

4、小结一下吧

spring bean的初始化执行顺序:构造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具体可以参考例子

afterPropertiesSet通过接口实现方式调用(效率上高一点),@PostConstruct和init-method都是通过反射机制调用

同理,bean销毁过程的顺序为:@PreDestroy > DisposableBean > destroy-method

不再展开,看源码就好

测试代码如下:

?

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

@Slf4j

public class InitSequenceBean implements InitializingBean {

     public InitSequenceBean() {

         log.info( "InitSequenceBean: construct" );

     }

     @Override

     public void afterPropertiesSet() throws Exception {

         log.info( "InitSequenceBean: afterPropertiesSet" );

     }

     @PostConstruct

     public void postConstruct() {

         log.info( "InitSequenceBean: postConstruct" );

     }

     public void initMethod() {

         log.info( "InitSequenceBean: initMethod" );

     }

}

@Configuration

public class SystemConfig {

     @Bean (initMethod = "initMethod" , name = "initSequenceBean" )

     public InitSequenceBean initSequenceBean() {

         return new InitSequenceBean();

     }

}

@Slf4j

public class InitSequenceBeanTest extends ApplicationTests {

     @Autowired

     private InitSequenceBean initSequenceBean;

     @Test

     public void initSequenceBeanTest() {

         log.info( "Finish: {}" , initSequenceBean.toString());

     }

}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/zbw18297786698/article/details/7365646

查看更多关于使用spring容器在初始化Bean时前和后的操作的详细内容...

  阅读:15次