记录遇到监听器无法使用@Autowired和@Resource注入对象的问题

432 阅读3分钟

记录遇到监听器无法使用@Autowired和@Resource注入对象的问题

  1. 遇到的问题

    今天尝试做一个导入和导出表格的功能,使用的工具是阿里提供的easyexcel,导出的功能按照实例很快就做出来了,但是做导入的功能时一直都显示注入的Mapper为空

  2. 寻找问题的思路

    • 按照往常的经验我首先去检查了启动类上是否加了@MapperScan注解,发现加了并且路径也正确,为了保险起见我在为空的Mapper类上分别尝试了@Mapper与@Repository注解进行尝试,发现无效
    • 我想知道是否是这个Mapper类的问题,因此在一个测试类中使用了@Autowired注入了这个Mapper类并测试了一些方法,发现可以正常使用,因此排除Mapper注入失败这个问题
    • 既然排除了Mapper注入失败的问题那就应该观察Mapper类被注入的类中是否有什么问题,然后我搜索了com.alibaba.excel.event.AnalysisEventListener这个类中为什么注入不了Bean对象,大佬们还是给力的,然后就发现了是因为监听器内中无法直接使用@Autowired和@Resource注入
  3. 解决问题的方法

    网上有很多遇到这个问题的人,因此也有对应的解决方案,通用的解决方案是创建一个单例的IOC容器工具类,让其他类可以方便地通过类名或者类的class对象去获取对应的bean,代码如下:

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    ​
    import java.util.Map;
    ​
    /**
     * SpringApplicationUtils
     * 使用IOC容器获取bean对象
     * @author wcl76
     * @date 2022/11/10
     */
    @Component
    //通过上下文获取到实例bean
    public class SpringApplicationUtils  implements ApplicationContextAware {
        private static ApplicationContext applicationContext = null;
    ​
        /**
         * 获取applicationContext
         *
         * @return
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    ​
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (SpringApplicationUtils.applicationContext == null) {
                SpringApplicationUtils.applicationContext = applicationContext;
            }
        }
    ​
        /**
         * 通过name获取 Bean.
         */
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }
    ​
        /**
         * 通过class获取Bean.
         *
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }
    ​
        /**
         * 通过name,以及Clazz返回指定的Bean
         *
         * @param name
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
    ​
        /**
         * 获取指定类型的所有bean实例
         *
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
            return getApplicationContext().getBeansOfType(clazz);
        }
    }
    
  4. 最后的总结

    在Listener监听器中无法使用Spring容器的@Resource或者@Autowired 注解的方法注入bean,因为,在web Server容器中,无论是Servlet,Filter,还是Listener都不是Spring容器管理的,因此我们都无法在这些类中直接使用Spring注解的方式来注入我们需要的对象。在这里,Servlet的整个生命周期都是由Servlet容器来处理的。如果把它硬放到Spring容器中去创建,Servlet对象是可被Spring容器建出来,但Servlet容器可能跟本就不知道这个Servlet是否存在,因为不在它自己的容器中。所以,servlet交给web server来管理,不要交给spring管理。

    总结部分内容来源