解决@Autowired注入空指针问题(利用Bean的生命周期)

目录
  • 我就写出了下面这样的代码进行抽取
  • 问题轻松解决
  • 下面介绍其中两种办法
    • 第一种JSR250的@PostConstruct
    • 第二种是Spring的InitializingBean(定义初始化逻辑)

今天做项目的时候遇到一个问题,需要将线程池的参数抽取到yml文件里进行设置。这不是so easy吗?

我就写出了下面这样的代码进行抽取

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * @author BestQiang
 */
@Component
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPool {
    private int corePoolSize;
    private int maximumPoolSize;
    private long keepAliveTime;
    private int capacity;
    public int getCorePoolSize() {
        return corePoolSize;
    }
    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }
    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }
    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }
    public long getKeepAliveTime() {
        return keepAliveTime;
    }
    public void setKeepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
    }
    public int getCapacity() {
        return capacity;
    }
    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }
}
package cn.bestqiang.util;
import cn.bestqiang.pojo.ThreadPool;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.*;
/**
 * @author Yaqiang Chen
 */
@Component
public class MyThreadUtils {
    @Autowired
    ThreadPool threadPool1;
    private ExecutorService threadPool = new ThreadPoolExecutor(
                threadPool1.getCorePoolSize(),
                threadPool1.getMaximumPoolSize(),
                threadPool1.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(threadPool1.getCapacity()),
                namedThreadFactory,
                new ThreadPoolExecutor.DiscardPolicy()
        );;
    private ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("pool-%d").build();
    public void execute(Runnable runnable){
        threadPool.submit(runnable);
    }
}

在yml文件的配置如下:

thread-pool:
  core-pool-size: 5
  maximum-pool-size: 20
  keep-alive-time: 1
  capacity: 1024

本想应该毫无问题,但是,报错了:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myThreadUtils' defined in fileXXXXXXXXXX(省略)Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.itcast.util.MyThreadUtils]: Constructor threw exception; nested exception is java.lang.NullPointerExceptionCaused by: java.lang.NullPointerException: null

空指针异常?检查好几遍配置没错。因为公司开发环境没法上网,只好拖到下班google了一下,结合我比较深厚的基础(自恋一下),

问题轻松解决

这就是答案。上面说所有的Spring的@Autowired注解都在构造函数之后,而如果一个对象像下面代码一样声明(private XXX = new XXX() 直接在类中声明)的话,成员变量是在构造函数之前进行初始化的,甚至可以作为构造函数的参数。

即 成员变量初始化 -> Constructor -> @Autowired

所以,在这个时候如果成员变量初始化时调用了利用@Autowired注解初始化的对象时,必然会报空指针异常的啊。

真相大白了。如果解决呢?那就让上面我写的代码的成员变量threadPool在@Autowired之后执行就好了。

要想解决这个问题,首先要知道@Autowired的原理:

AutowiredAnnotationBeanPostProcessor 这个类

其实看到这个继承结构,我心中已经有解决办法了。具体详细为什么,等997的工作结束(无奈)我会在后续博客里将Spring的注解配置详细的捋一遍,到时候会讲到Bean的生命周期的。

继承的BeanFactoryAware是在属性赋值完成,执行构造方法后,postProcessBeforeInitialization才执行,而且,是在其他生命周期之前,而@Autowired注解就是依靠这个原理进行的自动注入。想要解决这个问题很简单,就是把要赋值的成员变量放到其他生命周期中就可以。

下面介绍其中两种办法

第一种JSR250的@PostConstruct

@PostConstruct
public void init() {
	// 这里放要执行的赋值
}

第二种是Spring的InitializingBean(定义初始化逻辑)

继承接口实现方法即可,这种直接放上完整用法

/**
 * @author Yaqiang Chen
 */
@Component
public class MyThreadUtils implements InitializingBean {
    @Autowired
    ThreadPool threadPool1;
    private ExecutorService threadPool;
    private ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("pool-%d").build();
    public void execute(Runnable runnable){
        threadPool.submit(runnable);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        threadPool = new ThreadPoolExecutor(
                threadPool1.getCorePoolSize(),
                threadPool1.getMaximumPoolSize(),
                threadPool1.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(threadPool1.getCapacity()),
                namedThreadFactory,
                new ThreadPoolExecutor.DiscardPolicy()
        );
    }
}

设置完成后,问题解决!

(0)

相关推荐

  • 详解SpringBoot 多线程处理任务 无法@Autowired注入bean问题解决

    在多线程处理问题时,无法通过@Autowired注入bean,报空指针异常, 在线程中为了线程安全,是防注入的,如果要用到这个类,只能从bean工厂里拿个实例. 解决方法如下: 1.创建一个工具类代码: package com.hqgd.pms.common; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.spri

  • @Autowired注入为null问题原因分析

    问题说明 最近看到Spring事务,在学习过程中遇到一个很苦恼问题 搭建好Spring的启动环境后出现了一点小问题 在启动时候却出现[java.lang.NullPointerException] 不过因为当时一个小小的疏忽很low的问题 请往下看... 工程结构 代码片段 spring.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springfr

  • 解决Test类中不能使用Autowired注入bean的问题

    目录 Test类中不能使用Autowired注入bean 在测试类中我自己使用的测试单元是 正确的应该是使用Spring-test里面的测试单元 Test包中使用autowired注入提示Could not autowire. No beans of 'xxx' type found. 将autowired注解换成Resource注解完美解决 Test类中不能使用Autowired注入bean 今天下午好好看了下关于Spring的注解问题. 在测试类中使用AutoWired注解一直不能获取到Be

  • 解决@Autowired注入空指针问题(利用Bean的生命周期)

    目录 我就写出了下面这样的代码进行抽取 问题轻松解决 下面介绍其中两种办法 第一种JSR250的@PostConstruct 第二种是Spring的InitializingBean(定义初始化逻辑) 今天做项目的时候遇到一个问题,需要将线程池的参数抽取到yml文件里进行设置.这不是so easy吗? 我就写出了下面这样的代码进行抽取 import org.springframework.boot.context.properties.ConfigurationProperties; import

  • Spring创建Bean的生命周期详析

    目录 1.Bean 的创建生命周期 2.Spring AOP 大致流程 3.Spring 事务 4.Spring 源码阅读前戏 BeanDefinition BeanDefinitionReader AnnotatedBeanDefinitionReader XmlBeanDefinitionReader ClassPathBeanDefinitionScanner BeanFactory ApplicationContext AnnotationConfigApplicationContext

  • Spring中Bean的生命周期使用解析

    Bean的生命周期 解释 (1)BeanFactoryPostProcessor的postProcessorBeanFactory()方法:若某个IoC容器内添加了实现了BeanFactoryPostProcessor接口的实现类Bean,那么在该容器中实例化任何其他Bean之前可以回调该Bean中的postPrcessorBeanFactory()方法来对Bean的配置元数据进行更改,比如从XML配置文件中获取到的配置信息. (2)Bean的实例化:Bean的实例化是使用反射实现的. (3)B

  • 详解Spring 中 Bean 的生命周期

    前言 这其实是一道面试题,是我在面试百度的时候被问到的,当时没有答出来(因为自己真的很菜),后来在网上寻找答案,看到也是一头雾水,直到看到了<Spring in action>这本书,书上有对Bean声明周期的大致解释,但是没有代码分析,所以就自己上网寻找资料,一定要把这个Bean生命周期弄明白! ​ 网上大部分都是验证的Bean 在面试问的生命周期,其实查阅JDK还有一个完整的Bean生命周期,这同时也验证了书是具有片面性的,最fresh 的资料还是查阅原始JDK!!! 一.Bean 的完整

  • Spring源码解析之Bean的生命周期

    一.Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDefinitionMap容器中,这一篇重点分析如何利用bean的定义信息BeanDefinition实例化bean. 二.流程概览 其实bean的实例化过程比较复杂,中间细节很多,为了抓住重点,先将核心流程梳理出来,主要包含以下几个流程: step1: 通过反射创建实例: step2:给实例属性赋初始值: step3:如果Bean类实现B

  • Java开发学习之Bean的生命周期详解

    目录 一.什么是生命周期 二.环境准备 三.生命周期设置 步骤1:添加初始化和销毁方法 步骤2:配置生命周期 步骤3:运行程序 四.close关闭容器 五.注册钩子关闭容器 六.bean生命周期总结 一.什么是生命周期 首先理解下什么是生命周期? 从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期. bean生命周期是什么? bean对象从创建到销毁的整体过程. bean生命周期控制是什么? 在bean创建后到销毁前做一些事情. 二.环境准备 环境搭建: 创建一个Maven项目

  • 一文搞懂Spring中Bean的生命周期

    目录 一.使用配置生命周期的方法 二.生命周期控制——接口控制(了解) 小结 生命周期:从创建到消亡的完整过程 bean声明周期:bean从创建到销毁的整体过程 bean声明周期控制:在bean创建后到销毁前做一些事情 一.使用配置生命周期的方法 在BookDaoImpl中实现类中创建相应的方法: //表示bean初始化对应的操作 public void init(){ System.out.println("init..."); } //表示bean销毁前对应的操作 public v

  • 一文读懂Spring Bean的生命周期

    目录 一.前言 1.1 什么是 Bean 1.2 什么是 Spring Bean 的生命周期 二.Spring Bean 的生命周期 三.Spring Bean 的生命周期的扩展点 3.1 Bean 自身的方法 3.2 容器级的方法(BeanPostProcessor 一系列接口) 3.2.1 InstantiationAwareBeanPostProcessor 源码分析 3.2.2 BeanPostProcessor 源码分析 3.3 工厂后处理器方法(BeanFactoryProcesso

  • SpringBoot源码之Bean的生命周期

    入口方法为SpringApplication#run() 1.SpringApplication#run() /** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running

  • spring之Bean的生命周期详解

    Bean的生命周期: Bean的定义--Bean的初始化--Bean的使用--Bean的销毁 Bean的定义 Bean 是 spring 装配的组件模型,一切实体类都可以配置成一个 Bean ,进而就可以在任何其他的 Bean 中使用,一个 Bean 也可以不是指定的实体类,这就是抽象 Bean . Bean的初始化 Spring中bean的初始化回调有两种方法 一种是在配置文件中声明init-method="init",然后在一个实体类中用init()方法来初始化 另一种是实现Ini

随机推荐