Spring Boot @Autowired 注入失效问题

96
双流小二
2018.09.27 11:31 字数 575

1. 问题

最近在项目中添加了会话验证过滤器,该Filter中使用@Autowired自动装载了一些从数据库中获取的系统配置,调试的时候发现注入失败,返回为null

// RequestAuthFilter只是一个普通类
// 这段代码处于Application.java启动类中
@Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new RequestAuthFilter());
        registrationBean.addUrlPatterns("/web/*", "/wechat/*", "/core/*");
        registrationBean.setName(GlobalConstants.AUTH_FILTER_NAME);
        registrationBean.setOrder(1);

        return registrationBean;
    }

2. 处理方案

尝试以下两种方案均成功,本质上都是将Filter变成让Spring容器中的bean。

2.1 修改filter为Spring中bean

  • 修改RequestAuthFilter为Component
@Component
public class RequestAuthFilter implements Filter {
  • 自动装载该类
@Autowired
    RequestAuthFilter filter;

@Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        ...
        registrationBean.setFilter(filter);
        ...
        return registrationBean;
    }

2.2 保留Filter为普通类,在Application.java中重新定义一个Bean

@Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        ...
        registrationBean.setFilter(getFilterRegistrationBean());
        ...
    }

    @Bean
    public Filter getFilterRegistrationBean() {
        return new RequestAuthFilter();
    }

3. 分析原因

后期通过new产生的实例中无法自动注入Spring容器中装载的实例。
原理需要了解Spring依赖注入,或者说控制反转,查阅Introduction to the Spring IoC container and beans

It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean.

这些Bean被Spring IoC 容器(org.springframework.context.ApplicationContext)管理,Spring容器也就是一个Bean工厂。
所以自动注入失效,一般有以下两种可能。

3.1 包没有被扫描到

引用自spring-boot-and-component-scan

  • If your other packages hierarchies are below your main app with the @SpringBootApplication annotation, you’re covered by implicit components scan.
  • If there are beans/components in other packages which are not sub packages of the main package, you should manually add them as @ComponentScan

Spring Boot项目的Bean装配默认规则是根据Application类(指项目入口类)所在的包位置从上往下扫描。
假设Application类在包com.comp.appname下,则只会扫描com.comp.appname包及其所有子包,如果需要自动装载的类所在包不在com.comp.appname及其子包下,则不会被扫描,自然就没法被注入!

3.2 调用者是使用new创建的

如果类A中存在成员属性B, B是通过@Autowired自动注入,而类A的实例是通过new的方式产生的,那么自动注入会失效的,此时通过Spring的上下文获取所有的Bean的方法来获取B。

/**
 * Spring上下文工具类,用以让普通类获取Spring容器中的Bean
 */
@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    /**
     * 获取applicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

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

    /**
     * 通过name获取 Bean
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
}

然后在A中就可以这样来获取Spring容器中的B实例

BclassInterface b = (BclassInterfaceImpl) SpringUtil.getBean("bclassInterfaceImpl");
Java