相关文章:
SpringBoot 之AOP切面的使用
SpringBoot之Interceptor拦截器注入使用
SpringBoot之Filter过滤器的实现及排序问题
SpringBoot 之多个过滤器(Filter) ,监听器(Listener),切面(AOP),拦截器(Interceptor)的指定排序问题总结篇
使用Filter,Listener 时无法注入Bean的解决方法

前言

同事项目中需要开一个Listener,正好我做的项目中正好有,所以给他铐了一个过去,结果一会告诉我Listener类没进Listener。
开始的时候使用了最常见的@WebListener注解(我项目就是使用的这个)。然后开始排查原因,起初发现同事项目Application启动器上没有加@ServletComponentScan注解。
加上后发现还是不行。继续找,然后发现可能是因为Listener类不在Application模块中。无法自动注入(可以在@ServletComponentScan加入需要扫描的模块包)
后面百度发现注入Listener还有其他几种方法,我们通过其中一种方法解决了其问题,故在此记录一下。

1、@WebListener注解

@WebListener注解为Servlet3+提供的注解,可以标识一个类为Listener,跟Servlet、Filter差不多

@WebListener
public class AnoContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("@WebListener context 初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("@WebListener context 销毁");}
}

因为@WebListener注解不是spring的规范,所以为了识别它,需要在启动类上添加注解@ServletComponentScan

@ServletComponentScan
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class);}
}

@ServletComponentScan
在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。
注意:如果Application类和Listerner,Servlet,Filter类不在同一包下,则@ServletComponentScan需要添加相应的路径,如Application类在包com.hui.xiao下,则写为@ServletComponentScan(“com.demo.jarvis”)或@ServletComponentScan(“com.demo”)
附上@ServletComponentScan源码:

/**
* Enables scanning for Servlet components ({@link WebFilter filters}, {@link WebServlet
* servlets}, and {@link WebListener listeners}). Scanning is only performed when using an
* embedded web server.
* <p>
* Typically, one of {@code value}, {@code basePackages}, or {@code basePackageClasses}
* should be specified to control the packages to be scanned for components. In their
* absence, scanning will be performed from the package of the class with the annotation.
*
* @author Andy Wilkinson
* @since 1.3.0
* @see WebServlet
* @see WebFilter
* @see WebListener
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {/*** Alias for the {@link #basePackages()} attribute. Allows for more concise annotation* declarations e.g.: {@code @ServletComponentScan("org.my.pkg")} instead of* {@code @ServletComponentScan(basePackages="org.my.pkg")}.* @return the base packages to scan*/@AliasFor("basePackages")String[] value() default {};/*** Base packages to scan for annotated servlet components. {@link #value()} is an* alias for (and mutually exclusive with) this attribute.* <p>* Use {@link #basePackageClasses()} for a type-safe alternative to String-based* package names.* @return the base packages to scan*/@AliasFor("value")String[] basePackages() default {};/*** Type-safe alternative to {@link #basePackages()} for specifying the packages to* scan for annotated servlet components. The package of each class specified will be* scanned.* @return classes from the base packages to scan*/Class<?>[] basePackageClasses() default {};}

2、普通Bean

第二种使用方式是将Listener当成一个普通的spring bean,spring boot会自动将其包装为ServletListenerRegistrationBean对象

@Component
public class BeanContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("bean context 初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("bean context 销毁");}
}

3、ServletListenerRegistrationBean

通过java config来主动将一个普通的Listener对象,塞入ServletListenerRegistrationBean对象,创建为spring的bean对象

public class ConfigContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("config context 初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("java context 销毁");}
}

上面只是一个普通的类定义,下面的bean创建才是关键点:

@Bean
public ServletListenerRegistrationBean configContextListener() {ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();bean.setListener(new ConfigContextListener());return bean;
}

ServletListenerRegistrationBean:
RegistrationBean的子类 基于Servlet 3.0+的注册bean的基类。

/**
* Base class for Servlet 3.0+ based registration beans.
*
* @author Phillip Webb
* @since 1.4.0
* @see ServletRegistrationBean
* @see FilterRegistrationBean
* @see DelegatingFilterProxyRegistrationBean
* @see ServletListenerRegistrationBean
*/
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {private static final Log logger = LogFactory.getLog(RegistrationBean.class);private int order = Ordered.LOWEST_PRECEDENCE;private boolean enabled = true;@Overridepublic final void onStartup(ServletContext servletContext) throws ServletException {String description = getDescription();if (!isEnabled()) {logger.info(StringUtils.capitalize(description)+ " was not registered (disabled)");return;}register(description, servletContext);}/*** Return a description of the registration. For example "Servlet resourceServlet"* @return a description of the registration*/protected abstract String getDescription();/*** Register this bean with the servlet context.* @param description a description of the item being registered* @param servletContext the servlet context*/protected abstract void register(String description, ServletContext servletContext);/*** Flag to indicate that the registration is enabled.* @param enabled the enabled to set*/public void setEnabled(boolean enabled) {this.enabled = enabled;}/*** Return if the registration is enabled.* @return if enabled (default {@code true})*/public boolean isEnabled() {return this.enabled;}/*** Set the order of the registration bean.* @param order the order*/public void setOrder(int order) {this.order = order;}/*** Get the order of the registration bean.* @return the order*/@Overridepublic int getOrder() {return this.order;}}

RegistrationBean类方法说明:

修饰符和类型 方法和说明 描述
void addInitParameter(String name, String value) 添加一个init参数,将所有现有参数替换为相同的名称.
protected void configure(Registration.Dynamic registration) 配置注册基础设置
Map<String,String> getInitParameters() 返回注册初始化参数的可变映射.
protected String getOrDeduceName(Object value) 推导此注册的名称.
int getOrder()
boolean isAsyncSupported() 返回异步操作是否支持此注册.
boolean isEnabled() 启用的标志(默认为true)
void setAsyncSupported(boolean asyncSupported) 设置异步操作是否支持此注册.
void setEnabled(boolean enabled) 指示已启用注册的标志.
void setInitParameters(Map<String,String> initParameters) 设置此注册的初始化参数.
void setName(String name) 设置此注册的名称.
void setOrder(int order) 设置执行顺序,值越小,越先执行

SpringBoot 提供的RegistrationBean常用的子类:

  • FilterRegistrationBean 用于注册Filter
  • ServletRegistrationBean 用于注册Servlet
  • ServletListenerRegistrationBean 用于注册Listener

4. ServletContextInitializer

这里主要是借助在ServletContext上下文创建的实际,主动的向其中添加Filter,Servlet, Listener,从而实现一种主动注册的效果

public class SelfContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("ServletContextInitializer context 初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("ServletContextInitializer context 销毁");}
}
@Component
public class ExtendServletConfigInitializer implements ServletContextInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {servletContext.addListener(SelfContextListener.class);}
}

注意ExtendServletConfigInitializer的主动注册时机,在启动时添加了这个Listenrer,所以它的优先级会是最高

5、多个Listener 指定顺序。

同Filter类似
第一种:通过类名实现排序方式
比如:Listener1MyListener,Listener2MyListener,Listener3MyListener
第二种:ServletListenerRegistrationBean通过设置order进行排序,数值越小越先执行。
比如:


@Bean
public ServletListenerRegistrationBean configContextListener() {ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();bean.setListener(new ConfigContextListener());bean.setOrder(1);return bean;
}

其他比如@Order注解,Ordered都无法实现排序

6、Listener无法注入Bean解决方法

使用FIlter 或者Listener 无法使用@Autowired 或者@Resource注解。要想使用spring容器中的bean,则需要实现ApplicationContextAware接口的context注入函数, 将其存入静态变量. 通过ApplicationContext进行获取。或者通过WebApplicationContextUtils.getWebApplicationContext 方式获取。

5、总结

项目一般采用前两种即可解决问题,前两种采用注解方式,后面两种相对麻烦。我这里采用了第二种解决方案,第一种是因为项目本身@ServletComponentScan是已经注释掉了,故在这不再去改变它。
参考博文:https://www.cnblogs.com/yihuihui/p/12034522.html

SpringBoot之Listener注册到Spring容器中的多种方法相关推荐

  1. SpringBoot 之 普通类获取Spring容器中的bean

    SpringBoot 之 普通类获取Spring容器中的bean 转载于:https://www.cnblogs.com/lwmp/p/8892927.html

  2. Spring注解驱动开发第7讲——如何按照条件向Spring容器中注册bean?这次我懂了!!

    写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,而 ...

  3. 将 Bean 放入 Spring 容器中的方式

    文章目录 将 Bean 放入 Spring 容器中的方式 1.@Configuration + @Bean 2.@Componet + @ComponentScan 3.@Import注解导入 3.1 ...

  4. IoC基础篇(一)--- Spring容器中Bean的生命周期

    IoC基础篇(一)--- Spring容器中Bean的生命周期 日出日落,春去秋来,花随流水,北雁南飞,世间万物皆有生死轮回.从调用XML中的Bean配置信息,到应用到具体实例中,再到销毁,Bean也 ...

  5. Q1 SpringBoot启动类如何作为配置类注册进Spring容器的?(ok)

    @SpringBootApplication public class HelloSpringBoot{public static void main(String[] args) {SpringAp ...

  6. SpringBoot教程(十一)——将Bean放入Spring容器中的五种方式

    将bean放入Spring容器中有哪些方式? 我们知道平时在开发中使用Spring的时候,都是将对象交由Spring去管理,那么将一个对象加入到Spring容器中,有哪些方式呢,下面我就来总结一下 1 ...

  7. SpringBoot中在普通类里面加载Spring容器中的类

    前言 在我们的工作中,经常会遇到需要在普通类中使用放在Spring容器中的类的情况.最常见的情况大概就是有一个类他的属性的是通过spring的配置文件读取的.这样这个类必然要交给Spring容器进行管 ...

  8. 【SpringBoot】在普通类中获取spring容器中的bean

    这段时间公司搞封闭开发,做一个联通总部的客服系统项目,是基于springboot的.在开发工程中遇到一个页面datagrid数据排序的功能,因为有多个表的数据都要用到排序功能,于是我就写了一个排序功能 ...

  9. Spring中将BeanDefinition注册到IOC容器中

    Spring中将BeanDefinition注册到IOC容器中 XML配置元信息 <bean name="-" - /> 注解: @Bean,@Component,@I ...

最新文章

  1. CodeForces - 1215C Swap Letters(暴力+思维+模拟)
  2. 魔法值是什么?(为什么在阿里巴巴开发手册中提到不允许任何魔法值直接出现在代码中)
  3. 微软编程题:寻找最小的k个值
  4. python初始化函数_当你学会了Python爬虫,网上的图片素材就免费了
  5. 【Python】Python列表基本操作
  6. kubernetes_Kubernetes领域的标准安全性。
  7. 查看eclipse使用的jre版本
  8. 西贝凭什么一年做到50多亿?| 独创的合伙人制:创业分部+赛场制
  9. SPSS异常值处理(图文+数据集)【SPSS 010期】
  10. jeecg框架表格合计解决方案
  11. MDK(Keil) 自动生成bin文件、汇编文件或者HEX文件、ASM文件
  12. 接线端子冲击和振动测试要求
  13. c语言生成正弦波,方波等mif
  14. 个税计算python版(2021最新版)
  15. python听歌识曲爬虫_【python爬虫】 爬云音乐我和xxx共同听过的歌曲
  16. Keil(MDK-ARM)的调试步骤
  17. 船桨数学模型matlab,船用螺旋桨负载特性数字仿真
  18. java合并txt文件_Java程序将两个txt文件合并为第三个txt文件 - Break易站
  19. win+r的快捷操作
  20. Java中获取整点时间戳

热门文章

  1. Cocos2d-x-3.0rc0创建新项目
  2. android中json解析及使用(上)
  3. read -p 的使用
  4. C#学习基础---BrowsableAttribute.Browsable属性
  5. CodeForces 501B - Misha and Changing Handles
  6. Mac 识别NTFS移动硬盘
  7. 速修复!这个严重的 Apache Struts RCE 漏洞补丁不完整
  8. 英国政府寻求在2022年成为“全球性网络力量”
  9. 又一款主流勒索软件 Paradise 的源代码遭泄露
  10. 谷歌全球服务均宕机,系因内部存储配额问题引发