javax @PostConstruct注解的使用

本文详细介绍了JavaEE5中@PostConstruct注解的使用方法及其在依赖注入后的初始化作用。通过示例展示了如何在Spring环境中使用该注解进行类的初始化,并对比了其与构造函数初始化的区别。

简介

Java EE5 引入了@PostConstruct和@PreDestroy这两个作用于类的生命周期的注解,实现类执行构造函数之后和销毁之前的自定义操作。此文主要说明@PostConstruct。

API使用说明

以下为@PostConstruct的API使用说明:

PostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。支持依赖关系注入的所有类都必须支持此注释。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注释进行注释。应用 PostConstruct 注释的方法必须遵守以下所有标准:该方法不得有任何参数,除非是在 EJB 拦截器 (interceptor) 的情况下,根据 EJB 规范的定义,在这种情况下它将带有一个 InvocationContext 对象 ;该方法的返回类型必须为 void;该方法不得抛出已检查异常;应用 PostConstruct 的方法可以是 public、protected、package private 或 private;除了应用程序客户端之外,该方法不能是 static;该方法可以是 final;如果该方法抛出未检查异常,那么不得将类放入服务中,除非是能够处理异常并可从中恢复的 EJB。

总结为一下几点:

  • 只有一个方法可以使用此注释进行注解;
  • 被注解方法不得有任何参数;
  • 被注解方法返回值为void;
  • 被注解方法不得抛出已检查异常;
  • 被注解方法需是非静态方法;
  • 此方法只会被执行一次;

实例

基于Spring boot编写的可执行方法见github:https://github.com/HappySecondBrother/example UserService方法(提供缓存数据):

package com.secbro.service;

import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 二师兄
 * @date 2016/8/10
 */
@Service
public class UserService {

    public List<String> getUser(){

        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        return list;
    }
}

BusinessService方法,通过@PostConstruct调用UserService:

package com.secbro.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * @author 二师兄
 * @date 2016/8/10
 */
@Service
public class BusinessService {

    @Autowired
    private UserService userService;

    private List<String> list = null;

    /**
     * 构造方法执行之后,调用此方法
     */
    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct方法被调用");
        // 实例化类之前缓存获得用户信息
        List<String> list = userService.getUser();
        this.list = list;
        if(list != null && !list.isEmpty()){
            for(String user : list){
                System.out.println("用户:" + user);
            }
        }
    }

    public BusinessService(){
        System.out.println("构造方法被调用");
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

执行结果:

构造方法被调用
@PostConstruct方法被调用
用户:张三
用户:李四

项目应用

在项目中@PostConstruct的应用场景之一是在初始化Servlet时加载一些缓存数据等。

Servlet执行流程图
这2个注解加入Servlet实现类中,则Servlet实现类的执行流程图为:

  这里写图片描述

在Servlet实现类的实例化过程中,@PostConstruct注释的方法,会在构造方法之后,init方法之前进行调用。

程序实践

web.xml

复制代码
1 <!-- @PostConstruct和@PreDestroy注解 -->
2   <servlet>
3     <servlet-name>AnnotationServlet</servlet-name>
4     <servlet-class>com.servlet.AnnotationServlet</servlet-class>
5   </servlet>
6 <servlet-mapping>
7     <servlet-name>AnnotationServlet</servlet-name>
8     <url-pattern>/servlet/AnnotationServlet</url-pattern>
9   </servlet-mapping>
复制代码
AnnotationServlet
复制代码
 1 package com.servlet;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 import java.sql.Time;
 6 import java.text.SimpleDateFormat;
 7 import java.util.Date;
 8 
 9 import javax.annotation.PostConstruct;
10 import javax.annotation.PreDestroy;
11 import javax.servlet.ServletException;
12 import javax.servlet.http.HttpServlet;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 
16 public class AnnotationServlet extends HttpServlet {
17     SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");//设置日期格式,精确到毫秒
18     
19     public AnnotationServlet(){
20         System.out.println("时间:"+df.format(new Date())+"执行构造函数...");
21     }
22     
23     public void destroy() {
24         this.log("时间:"+df.format(new Date())+"执行destroy()方法...");
25         //super.destroy(); // Just puts "destroy" string in log
26         // Put your code here
27     }
28 
29     @PostConstruct
30     public void someMethod(){
31         //this.log("执行@PostConstruct修饰的someMethod()方法...");//注意:这样会出错
32         System.out.println("时间:"+df.format(new Date())+"执行@PostConstruct修饰的someMethod()方法...");
33     }
34     
35     @PreDestroy
36     public void otherMethod(){
37         System.out.println("时间:"+df.format(new Date())+"执行@PreDestroy修饰的otherMethod()方法...");
38     }
39     
40     public void doGet(HttpServletRequest request, HttpServletResponse response)
41             throws ServletException, IOException {
42         this.log("时间:"+df.format(new Date())+"执行doGet()方法...");
43     }
44 
45     public void init() throws ServletException {
46         // Put your code here
47         this.log("时间:"+df.format(new Date())+"执行init()方法...");
48     }
49     
50     protected void service(HttpServletRequest request, HttpServletResponse response)
51                throws ServletException, IOException{
52         this.log("时间:"+df.format(new Date())+"执行service()方法...");
53         super.service(request, response);
54     }
55 
56 }
复制代码

运行结果:


应用场景之二是与spring结合使用,通过@PostConstruct 和 @PreDestroy 方法 实现bean初始化之后和bean销毁之前进行的操作。

下面演示通过  @PostConstruct 和 @PreDestory

1:定义相关的实现类:

[java]  view plain  copy
  1. package com.myapp.core.annotation.init;  
  2.   
  3. import javax.annotation.PostConstruct;  
  4. import javax.annotation.PreDestroy;  
  5.   
  6. public class PersonService {  
  7.     
  8.     private String  message;  
  9.   
  10.     public String getMessage() {  
  11.         return message;  
  12.     }  
  13.   
  14.     public void setMessage(String message) {  
  15.         this.message = message;  
  16.     }  
  17.       
  18.     @PostConstruct  
  19.     public void  init(){  
  20.         System.out.println("I'm  init  method  using  @PostConstrut...."+message);  
  21.     }  
  22.       
  23.     @PreDestroy  
  24.     public void  dostory(){  
  25.         System.out.println("I'm  destory method  using  @PreDestroy....."+message);  
  26.     }  
  27.       
  28. }  
2:定义相关的配置文件:
[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. xmlns:context="http://www.springframework.org/schema/context"  
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6. http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
  7. http://www.springframework.org/schema/context  
  8. http://www.springframework.org/schema/context/spring-context-3.1.xsd">  
  9.   
  10. <!-- <context:component-scan  base-package="com.myapp.core.jsr330"/> -->  
  11.   
  12. <context:annotation-config />  
  13.   
  14. <bean id="personService" class="com.myapp.core.annotation.init.PersonService">  
  15.   <property name="message" value="123"></property>  
  16. </bean>  
  17.   
  18. </beans>  
其中<context:annotation-config />告诉spring 容器采用注解配置:扫描注解配置;

测试类:

[java]  view plain  copy
  1. package com.myapp.core.annotation.init;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class MainTest {  
  7.       
  8.     public static void main(String[] args) {  
  9.           
  10.         ApplicationContext  context = new ClassPathXmlApplicationContext("resource/annotation.xml");  
  11.           
  12.         PersonService   personService  =  (PersonService)context.getBean("personService");  
  13.           
  14.         personService.dostory();  
  15.     }  
  16.   
  17. }  
测试结果:

I'm  init  method  using  @PostConstrut....123
I'm  destory method  using  @PreDestroy.....123

spring中Constructor、@Autowired、@PostConstruct的顺序

其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象p与对象a,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowired注解,那么@Autowired注入是发生在A的构造方法执行完之后的。

如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

Constructor >> @Autowired >> @PostConstruct

举个例子:

[java]  view plain  copy
  1. public Class AAA {  
  2.     @Autowired  
  3.     private BBB b;  
  4.       
  5.     public AAA() {  
  6.         System.out.println("此时b还未被注入: b = " + b);  
  7.     }  
  8.    
  9.     @PostConstruct  
  10.     private void init() {  
  11.         System.out.println("@PostConstruct将在依赖注入完成后被自动调用: b = " + b);  
  12.     }  
  13. }  


为什么我应该使用@PostConstruct来代替常规的构造函数执行初始化操作?

In a managed bean, @PostConstruct is called after the regular Java object constructor.
Why would I use @PostConstruct to initialize by bean, instead of the regular constructor itself?

  • because when the constructor is called, the bean is not yet initialized - i.e. no dependencies are injected. In the @PostConstruct method the bean is fully initialized and you can use the dependencies.
  • because this is the contract that guarantees that this method will be invoked only once in the bean lifecycle. It may happen (though unlikely) that a bean is instantiated multiple times by the container in its internal working, but it guarantees that @PostConstruct will be invoked only once.

in a constructor, the injection of the dependencies has not yet occurred.

In case someone still have doubts about what this means, this is a real world example just happened to me:

public class Foo {

    @Inject
    Logger LOG;

    @PostConstruct
    public void fooInit(){
        LOG.info("This will be printed; LOG has already been injected");
    }

    public Foo() {
        LOG.info("This will NOT be printed, LOG is still null");
        // NullPointerException will be thrown here
    }
}

Hope that helps.

换言之,还是那句话,在执行构造函数的时候,依赖还没有注入。如果你执行的初始化操作需要使用依赖,你就应该使用@PostConstruct。

参考:注解@PostConstruct与@PreDestroy详解及实例

Java开发之@PostConstruct和@PreConstruct注解

通过javax @PostConstruct 和 @PreDestroy 方法 实现bean初始化之后和bean销毁之前进行的操作

spring中Constructor、@Autowired、@PostConstruct的顺序

Why would I use @PostConstruct to initialize by bean, instead of the regular constructor itself?


The other answers, especially @Bozho's one, already explained the main problem (among the others):

in a constructor, the injection of the dependencies has not yet occurred.

In case someone still have doubts about what this means, this is a real world example just happened to me:

public class Foo {

    @Inject
    Logger LOG;

    @PostConstruct
    public void fooInit(){
        LOG.info("This will be printed; LOG has already been injected");
    }

    public Foo() {
        LOG.info("This will NOT be printed, LOG is still null");
        // NullPointerException will be thrown here
    }
}

Hope that helps.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值