在非SpringBean类里获取SpringBean,会是什么情况?

case1

下面这段代码中,PlainClass 表示一个普通Java类:


public class PlainClass {

    public void foo1() {
        TheOtherBean bean = SpringContextUtils.getBean(TheOtherBean.class);
        System.out.println(bean);
    }
}


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtils implements ApplicationContextAware {
    /**
     * Spring容器上下文对象实例
     */
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

我们在SpringBean里执行 PlainClass#foo1 是没问题的。

而在非SpringBean中执行 PlainClass#foo1,会抛空指针异常,这是因为在这种情况下,SpringContextUtils未被Spring容器管理,无法获取到applicationContext对象。


java.lang.NullPointerException
    at jstudy.redislimit.SpringContextUtils.getBean(SpringContextUtils.java:30)
    at jstudy.beantest.PlainClass.foo1(PlainClass.java:12)
    at jstudy.beantest.BeanTest.test(BeanTest.java:18)
  • 1.
  • 2.
  • 3.
  • 4.

 

 

case2

接着看下面代码中的 PlainClass :


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

public class PlainClass {
    @Autowired
    private TheOtherBean theOtherBean;

    public void foo2() {
        System.out.println(theOtherBean);
    }
}


import org.springframework.stereotype.Component;

@Component
public class TheOtherBean {
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

此时,在SpringBean 里执行 PlainClass#foo2 , 输出结果是什么?

 

 

是null。也就是说 theOtherBean 对象是null。为什么? 因为Spring启动时,程序先加载的是 PlainClass 类(它不受Spring管理),而此时`TheOtherBean`尚未被注入容器里,自动装配失败,注入的`TheOtherBean`为null。

当然, 我们通常在项目中不会写这种烂代码来“博人眼球”。

 

细心的同学可以发现,IDE会在 PlainClass 这个非SpringBean里使用的@Autowired注解上给出警告,提示"Autowired members must be defined in valid Spring bean (@Component|@Service|...)",提醒开发者合适地定义Bean以避免这类问题。

 

另外,在非SpringBean 里执行 PlainClass#foo2呢?依然是null。

 

 

附上我所使用的测试类代码


package jstudy.beantest;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanTest {
    @Test
    public void test() {
        new PlainClass().foo2();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

该测试类`BeanTest`在SpringBoot环境下运行。注释掉 @SpringBootTest 和 @RunWith(SpringRunner.class)后,`BeanTest`就是一个非SpringBean(普通Java类)了。

 

 

总结

在非SpringBean类中直接获取SpringBean可能会引发问题,例如上面案例里提到的空指针和自动装配失败。为避免这些问题,建议将需要访问Spring Bean的类也注册为Spring Bean,以确保依赖关系得到正确管理。