由于springboot基于servlet3.0+,内嵌tomcat容器 因此无法像之前一样通过web.xml中配置Listener,本文基于springboot2.1.1
第一种
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyFirstListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MyFirstFilter:Servlet容器初始化...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("MyFirstFilter:Servlet容器被销毁了");
}
}
@SpringBootApplication
@ServletComponentScan
public class ListenerMain {
public static void main(String[] args) {
SpringApplication.run(ListenerMain.class, args);
}
}
启动打印:
org.springframework.web.context.ContextLoader:284 Root WebApplicationContext: initialization completed in 3597 ms
MyFirstFilter:Servlet容器初始化...
@ServletComponentScan
扫描包所在路径下所有包含@WebListener类进行初始化
第二种
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MySecondListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MySecondListener:Servlet容器初始化...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("MySecondListener:Servlet容器被销毁了");
}
}
import com.ly.mp.project.listener.MySecondListener;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySecondConfiguration {
@Bean
public ServletListenerRegistrationBean getServletListenerRegistrationBean() {
return new ServletListenerRegistrationBean(new MySecondListener());
}
}
@SpringBootApplication
public class ListenerMain {
public static void main(String[] args) {
SpringApplication.run(ListenerMain.class, args);
}
}
纳入到SpringBoot容器方式:
1)将FilterAutoConfiguration纳入扫描路径:
@Import({MySecondConfiguration .class})或@ComponentScan(basePackages = {"com.shu.**.configuration"})
2)META-INF/spring.factories中配置(针对提供依赖包方式):org.springframework.boot.autoconfigure.EnableAutoConfiguration=***.MySecondConfiguration
说明:
- 优先级:第二种>第一种
EmbeddedWebApplicationContext 容器启动后执行 ,springboot 2.0+变更为ServletWebServerApplicationContext
The ServletWebServerApplicationContext
is a special type of WebApplicationContext
that bootstraps itself by searching for a single ServletWebServerFactory
bean.
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
getServletContextInitializerBeans() 会返回实现了ServletContextInitializer
接口的集合,同时初始化一些数据,如下:
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0)
? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values()
.stream()
.flatMap((value) -> value.stream()
.sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
ServletContextInitializerBeans对象的addServletContextInitializerBeans/addAdaptableBeans
在调用onStartup()方法之前会添加Filter/Servlet/Listener,注意Filter/Servlet/Listener必须已经声明为Bean
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
addAsRegistrationBean(beanFactory, Servlet.class,
new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class,
new FilterRegistrationBeanAdapter());
for (Class<?> listenerType : ServletListenerRegistrationBean
.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class,
(Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(
beanFactory, initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(),
initializerBean.getValue(), beanFactory);
}
}
}
- 第一种则最终也是采用第二种方式,区别就是在Springboot启动时将扫描到的包路径作为入参传递给
ServletComponentRegisteringPostProcessor
而该类实现了BeanFactoryPostProcessor
接口,因此在BeanFactory初始化后,会调用postProcessBeanFactory()
方法,在该方法中通过之前传入的包来扫描相应的类,并将@WebListener
的类由相应的处理器转换为FilterRegistrationBean
ServletComponentRegisteringPostProcessor
ServletComponentRegisteringPostProcessor(Set<String> packagesToScan) {
this.packagesToScan = packagesToScan;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (isRunningInEmbeddedWebServer()) {
ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
for (String packageToScan : this.packagesToScan) {
scanPackage(componentProvider, packageToScan);
}
}
}
private void scanPackage(
ClassPathScanningCandidateComponentProvider componentProvider,
String packageToScan) {
for (BeanDefinition candidate : componentProvider
.findCandidateComponents(packageToScan)) {
if (candidate instanceof ScannedGenericBeanDefinition) {
for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((ScannedGenericBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext);
}
}
}
}
WebFilterHandler
/**
* Handler for {@link WebListener @WebListener}-annotated classes.
*
* @author Andy Wilkinson
*/
class WebListenerHandler extends ServletComponentHandler {
WebListenerHandler() {
super(WebListener.class);
}
@Override
protected void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,
BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletListenerRegistrationBean.class);
builder.addPropertyValue("listener", beanDefinition);
registry.registerBeanDefinition(beanDefinition.getBeanClassName(), builder.getBeanDefinition());
}
}
题外话
添加自定义Servlet 也可采用方法一
@WebServlet
或者ServletRegistrationBean
添加自定义Filter也可以采用方法一@WebFilter
或者FilterRegistrationBean