场景:
- 封装工具类,方便业务处理
- 工具类中需要调用 spring管理的Bean
- 测试调用报空指针异常问题
初步工具类代码:
@Component public class ScriptExecuteContent { @Autowired private static SignRepository signRepository; public static String checkSign(String certNo, String acctNo, String instCode) { Sign sign = signRepository.findByCertNoAndAcctNoAndInstCode(certNo, acctNo, instCode); if (null != sign && StringUtils.equals(sign.getStatus(), StatusEnum.SUCCESS.code()) && DateUtil.getCurrentDate().before(sign.getExpireTime())) { return "1"; } else { return "0"; } } }
该段代码晃眼一看没啥问题,但是运行就会null异常,因为此处注入的signRepository为null,这是因为静态方法是属于类的,普通方法才属于对象,spring注入是在容器中实例化变量的,并且静态是优先于对象存在的,所以直接在静态方法中调用注入的静态变量其实是为null的。
思考:
- 还是Spring Bean的生命周期问题
- 以及加载顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)问题
Spring Bean的生命周期:
实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
初始化阶段Initialization ==> Bean 对象改造的4中方法:
销毁 Destruction阶段调用时机:
- 单实例:容器关闭的时候
- 多实例:容器不会管理这个Bean,不会调用它的销毁方法
解决方案一:
@Autowired 用在构造函数上
我们知道@Autowired 注释,可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,此种方式就是在构造函数上使用@Autowired。
@Component public class ScriptExecuteContent { private static SignRepository signRepository; @Autowired public ScriptExecuteContent(SignRepository signRepository) { ScriptExecuteContent.signRepository = signRepository; } public static String checkSign(String certNo, String acctNo, String instCode) { Sign sign = signRepository.findByCertNoAndAcctNoAndInstCode(certNo, acctNo, instCode); if (null != sign && StringUtils.equals(sign.getStatus(), StatusEnum.SUCCESS.code()) && DateUtil.getCurrentDate().before(sign.getExpireTime())) { return "1"; } else { return "0"; } } }
解决方案二:
使用 @PostConstruct 注解
- 从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy
- Java中该注解的说明:@PostConstruct该注解是javax.annotation包下的,被用来修饰一个非静态的void()方法
- 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次
- 被@PostConstruct修饰的方法在构造函数之后执行,init()方法之前执行
- 被@PreDestroy()方法在destroy()方法执行之后执行
- @PostConstruct注释规则:除了拦截器这个特殊情况以外,其他情况都不允许有参数,否则spring框架会报IllegalStateException;而且返回值要是void,但实际也可以有返回值,至少不会报错,只会忽略
- 通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
@Component public class ScriptExecuteContent { @Autowired private SignRepository signRepository; private static ScriptExecuteContent scriptExecuteContent; @PostConstruct public void update() { scriptExecuteContent = this; scriptExecuteContent.signRepository = this.signRepository; } public static String checkSign(String certNo, String acctNo, String instCode) { Sign sign = scriptExecuteContent.signRepository.findByCertNoAndAcctNoAndInstCode(certNo, acctNo, instCode); if (null != sign && StringUtils.equals(sign.getStatus(), StatusEnum.SUCCESS.code()) && DateUtil.getCurrentDate().before(sign.getExpireTime())) { return "1"; } else { return "0"; } } }
范例2:
@Component public class StaticBook { private static String url; @Value("${test.str}") private String str; @PostConstruct public void update() { this.url = this.str; } public static BookTestTemp getValue(){ return new BookTestTemp(1, "bookName", "123", url); } }