@PostConstruct的应用场景

2 篇文章 0 订阅

前言

最近在代码中两次看到同事使用@PostConstruct的场景, 有些别扭, 但是也不能说不对, 毕竟是在项目初始化的时候, 代码只会执行一次, 下面我挨个举例说明并附上正统用法.

参考代码1

@Configuration
@EnableApolloConfig
public class MqConfig {
	@Value("${rabbitMq.returnVisit.result.exchange}")
	public String RABBITMQ_RETURNVISIT_RESULT_EXCHANGE;
	@Value("${rabbitMq.returnVisit.result.routintKey}")
	public String RABBITMQ_RETURNVISIT_RESULT_ROUTING_KEY;
	public static final String RETURN_VISIT_RESULT_QUEUE="return.visit.result.queue";
	public static String RETURN_VISIT_RESULT_EXCHANGE;
	public static String RETURN_VISIT_RESULT_ROUTING_KEY;

	@PostConstruct
	public void init() {
		CONT_RECEIVE_EXCHANGE = this.RABBITMQ_CONT_RECEIVE_EXCHANGE;
		WECHAT_CONT_RECEIVE_ROUTING_KEY = this.RABBITMQ_WECHAT_CONT_RECEIVE_ROUTING_KEY;
		SF_CONT_RECEIVE_ROUTING_KEY = this.RABBITMQ_SF_CONT_RECEIVE_ROUTING_KEY;
		IUDP_CONT_RECEIVE_ROUTING_KEY = this.RABBITMQ_IUDP_CONT_RECEIVE_ROUTING_KEY;
		RETURN_VISIT_RESULT_EXCHANGE = this.RABBITMQ_RETURNVISIT_RESULT_EXCHANGE;
		RETURN_VISIT_RESULT_ROUTING_KEY = this.RABBITMQ_RETURNVISIT_RESULT_ROUTING_KEY;

	}
	
	
	@Bean
	public Queue dsfDataSendQueue() {
		return new Queue(DSF_DATA_SEND_QUEUE, true);
	}

	@Bean
	public TopicExchange dsfDataSendTopicExchange() {
		return new TopicExchange(RABBITMQ_DSF_SEND_EXCHANGE);
	}

	@Bean
	public Binding dsfDataSendBinding() {
		Binding a = BindingBuilder.bind(dsfDataSendQueue()).to(dsfDataSendTopicExchange())
				.with(RABBITMQ_DSF_SEND_ROUTING_KEY);
		return a;

	}

经过合理的推演, 同事1应该是由于spring boot里@Value和@bean执行顺序问题采取了这种写法, apollo获取到的value会慢于rabbitmq的初始化@bean, 通过加@PostConstruct注解提前获取了@Value再执行@Bean, 挺精明的还.

参考代码2

        if(inputJson.contains("hsrCIRCByMonth")){
            CallToAllMediaServiceImpl callToAllMediaService = new CallToAllMediaServiceImpl();
            response = callToAllMediaService.dealHesitateSuccessRateCIRC(inputJson);
        }
@Component
public class CallToAllMediaServiceImpl {

    @Autowired
    protected IDsCallToAllMediaDAO dsCallToAllMediaDAO;
    @Autowired
    protected ICallToAllMediaDAO callToAllMediaDAO;
    @Autowired
    protected IRvReportFormDAO reportFormDAO;
    @Autowired
    protected CallToAllMediaManager callToAllMediaManager;

    public static CallToAllMediaServiceImpl callToAllMediaService;

    // 初始化返回参数map
    private static HashMap<String, Boolean> fieldMap = new HashMap<String, Boolean>() {
        {
            put("FIELD1", false);
            put("FIELD2", false);
            put("FIELD3", false);
            put("FIELD4", false);
            put("FIELD5", false);
            put("FIELD6", false);
            put("FIELD7", false);
            put("FIELD8", false);
            put("FIELD9", false);
        }
    };

    // 实际返回参数map
    private static HashMap<String, Boolean> fieldsMap = new HashMap<String, Boolean>();

    @PostConstruct
    public void init(){
        callToAllMediaService = this;
        callToAllMediaService.callToAllMediaDAO = this.callToAllMediaDAO;
        callToAllMediaService.dsCallToAllMediaDAO = this.dsCallToAllMediaDAO;
        callToAllMediaService.reportFormDAO = this.reportFormDAO;
        callToAllMediaService.callToAllMediaManager = this.callToAllMediaManager;
    }


    public String hesitateSuccessRateCIRC(String inputJson) {
    ................

经过推演, 同事2年限较短在写代码时, 习惯于new 对象(), 这是个springboot的项目, 在使用类的时候new了对象, 当然这里不推荐, 有一个问题就是new出来的对象对于@Autowired注入的成员变量并没有再一次的注入, 同事2先是在类首次初始化的时候加了一个static本身, 这个本身是完整的, 之后每次new的对象用@PostConstruct把首次@Autowired注入的成员变量挨个设置给新new的对象, 当然特定的场景是可以的, 并不涉及多线程的并发问题. 话说也算是挺精明的.

正统用法

查百度资料可以看到很多内容, 构造方法 > @Autowired > @PostConstruct
@PostConstruct和@PreDestroy 这两个注解是Java 5引入, 已经完全在Java 11中删除. 使用需要引入jar包

<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version></dependency>
    PostConstruct注释用于在执行任何初始化时执行依赖注入后需要执行的方法。必须在类投入服务之前调用此方法。所有支持依赖注入的类都必须支持此注释。

    JSR-250章。2.5 javax.notation.PostConstruct
PostConstruct注释用于在完成依赖项注入以执行任何初始化之后需要执行的方法。必须在类投入使用之前调用此方法。
所有支持依赖注入的类都必须支持此注释。即使类没有请求注入任何资源,也必须调用使用PostConstruct注释的方法。
只有一个方法可以使用此批注进行批注。
应用PostConstruct注释的方法必须满足以下所有条件:除了拦截器之外,方法绝不能有任何参数,在这种情况下它采用Interceptor规范定义的InvocationContext对象。
在拦截器类上定义的方法必须具有以下签名之一:
void <METHOD>(InvocationContext)Object <METHOD>(InvocationContext)抛出异常注意:
PostConstruct拦截器方法不能抛出应用程序异常,但可以声明它抛出检查异常,包括java.lang.Exception,
如果相同的拦截器方法除了生命周期事件之外插入业务或超时方法。
如果PostConstruct拦截器方法返回一个值,容器将忽略它。
在非拦截器类上定义的方法必须具有以下签名:void <METHOD>()应用PostConstruct的方法可以是publicprotectedpackage privateprivate。
除应用程序客户端外,该方法绝不能是静态的。
该方法可能是最终的。如果该方法抛出一个未经检查的异常,那么该类绝不能投入使用,除非EJB可以处理异常甚至从它们恢复的EJB
特点:

1、只有一个非静态方法能使用此注解

2、被注解的方法不得有任何参数

3、被注解的方法返回值必须为void

4、被注解方法不得抛出已检查异常

5、此方法只会被执行一次

servlet执行流程

引入别人的一个图:
在这里插入图片描述

总结

@PostConstruct注释允许在实例化并执行所有注入之后执行方法的定义。
相当于后构造方法, 需要使用构造方法之后的成员变量来初始化另外一些变量.

@Service
public class BeanA {

    @Autowired
    private BeanB beanB;

    public BeanA() {
        System.out.println("这是Bean A 的构造方法");
    }

    @PostConstruct
    private void init() {
        System.out.println("需要调用BeanB的方法初始化");
        beanB.testB();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值