跳到主要内容

@Postconstruct注解的用法

·1759 字·4 分钟

@PostConstruct注解的用法总结

1.定义 #

从JavaEE5规范开始,Servlet增加了两个影响Servlet生命周期的注解(Annotation):@PostConstruct和@PreConstruct。这两个注解被用来修饰一个非静态的void()方法.而且这个方法不能有抛出异常声明。

@PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。

@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

2.用法 #

@PostConstruct
 
public void someMethod(){}

或者

public @PostConstruct void someMethod(){}

3.执行顺序 #

被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法知性之后执行。

另外,spring中Constructor、@Autowired、@PostConstruct的顺序

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

Constructor » @Autowired » @PostConstruct

例如:

public Class AAA {

    @Autowired
    private BBB b;

    public AAA() {
        System.out.println("此时b还未被注入: b = " + b);
    }

    @PostConstruct
    private void init() {
        System.out.println("@PostConstruct将在依赖注入完成后被自动调用: b = " + b);
    }
}

4.应用场景及作用 #

作用:

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

应用场景:

  • 1.spring项目加载数据字典: @PostConstruct注解的方法在项目启动的时候执行这个方法,也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的。

    @Service
    public class SelectCodeServiceImpl implements SelectCodeService {
    
        /** 日志 */
        private static Logger LOGGER = LoggerFactory.getLogger(SelectCodeServiceImpl.class);
    
        // 缓存中相对应的key
        private static final String SELECT_CODE_VERSION_KEY = "select_code_key";
    
        // 系统启动默认版本号
        private static final int INIT_DEFAULT_VERSION = 0;
    
        // 缓存默认版本号
        private static int version = -100;
    
        // 二级代码本地缓存
        private static Map<String, Map<String, SelectCode>> selectCodeMap;
    
        @Resource
        private SelectCodeRepository selectCodeRepository;
    
        @Autowired
        private CacheManager cacheManager;
    
        /**
         * 获取代码表
         */
        @Override
        public Map<String, Map<String, SelectCode>> getSelectCodeMap() {
            // 比对版本号
            if (isVersionUpdate() {
                // 版本号更新,同步当前全局版本号
                LOGGER.debug("发现代码版本更新..");
                refreshLocalSelectCodeMap();
            }
            return selectCodeMap;
        }
    
        /**
         * 启动时检查并设置版本号
         */
        @PostConstruct
        public void init() {
            boolean hasCodeVersion = getCachedVersion() == null ? false : true;
            if (!hasCodeVersion) {
                // 设置全局版本号为INIT_DEFAULT_VERSION
                LOGGER.debug("系统初始化设置全局二级代码版本号");
                setCachedVersion(INIT_DEFAULT_VERSION);
            }
            // 启动时加载一遍二级代码,同步当前全局版本号
            refreshLocalSelectCodeMap();
        }
    
        /**
         * 刷新缓存
         */
        @Override
    //    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
        public synchronized void notifyCodeRefresh() {
            Integer remoteVersion = getCachedVersion();
            // 增加代码版本号
            setCachedVersion(++remoteVersion);
            // 更新本地代码缓存
            refreshLocalSelectCodeMap();
        }
    
        /**
         * 判断全局版本是否更新
         * 
         * @return
         */
        private boolean isVersionUpdate() {
            return getCachedVersion() > SelectCodeServiceImpl.version;
        }
    
        /**
         * 加载数据
         */
        private synchronized void refreshLocalSelectCodeMap() {
            // 更新本地版本号
            Integer oldVersion = version;
            version = getCachedVersion();
            LOGGER.debug("刷新当前二级代码数据从版本{}到版本{}", oldVersion, version);
            // 从数据库加载数据
    //        List<SelectCode> list = selectCodeRepository.findAll();
    
            List<SelectCode> list = selectCodeRepository.findAll(new Sort("id");
            // 包装成前台所需结构
            selectCodeMap = SelectCodeMapFactory.crtSelectCodeMap(list);
        }
    
        /**
         * 获取缓存版本号
         * 
         * @return
         */
        private Integer getCachedVersion() {
            ValueWrapper value = getSelectCodeCache().get(SELECT_CODE_VERSION_KEY);
            if (null == value) {
                return null;
            } else {
                return (Integer) value.get();
            }
        }
    
        /**
         * 设置缓存版本号
         * 
         * @return
         */
        private void setCachedVersion(Integer version) {
            LOGGER.debug("设置二级代码版本号version={}", version);
            getSelectCodeCache().put(SELECT_CODE_VERSION_KEY, version);
        }
    
        /**
         * 获取二级代码缓存
         * 
         * @return
         */
        private Cache getSelectCodeCache() {
            return cacheManager.getCache(ApplyCoreConstant.CACHE_SELECT_CODE);
        }
    
        /**
         * 获得二级代码名称
         */
        @Override
        public String getCodeName(String codeType, String value) {
            // 获取代码类
            Map<String, SelectCode> codeMap = selectCodeMap.get(codeType);
            if (null == codeMap) {
                return null;
            }
            // 获取代码对象
            SelectCode code = codeMap.get(value);
            if (null == code) {
                return null;
            }
            return code.getName();
        }
    
    }
    
  • 2.spring项目的定时任务: spring自带的@schedule,没有开关,项目启动总会启动一个线程; 做项目的时候就使用Java的timer,这个设置开关即可自由的控制,关闭的时候,不会启动线程; Java的timer也需要找到一个启动类,可以放到main函数里面启动,这样的话,代码的耦合性太高了,而使用PostConstruct是很干净的。