问题
@SpringBootApplication
public class SampleTomcatApplication {
private static Log logger = LogFactory.getLog(SampleTomcatApplication.class);
@Bean
protected ServletContextListener listener() {
return new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("ServletContext initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("ServletContext destroyed");
}
};
}
public static void main(String[] args) {
SpringApplication.run(SampleTomcatApplication.class, args);
}
}
如上是 Springboot 源码中的示例代码,现在的问题是为什么提供了一个 ServletContextListener
的 Bean
后,其中的回调就一定能得到调用呢?
调用流程
- 通过 https://www.jianshu.com/p/c54882984197 这篇文章我们知道了 Tomcat 会回调 Spring 的
ServletWebServerApplicationContext#selfInitialize()
方法:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// getServletContextInitializerBeans() 方法返回的是 ServletContextInitializerBeans 对象
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
- 注意看
getServletContextInitializerBeans()
方法:
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
该方法返回了一个 ServletContextInitializerBeans
对象,并把 beanFactory
传了进去。
- 再看
ServletContextInitializerBeans
类:
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
private static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
private static final Log logger = LogFactory.getLog(ServletContextInitializerBeans.class);
/**
* Seen bean instances or bean names.
*/
private final Set<Object> seen = new HashSet<>();
private final MultiValueMap<Class<?>, ServletContextInitializer> initializers;
private final List<Class<? extends ServletContextInitializer>> initializerTypes;
private List<ServletContextInitializer> sortedList;
@SafeVarargs
@SuppressWarnings("varargs")
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);
}
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);
}
}
}
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
if (source != null) {
// Mark the underlying source as seen in case it wraps an existing bean
this.seen.add(source);
}
if (logger.isTraceEnabled()) {
String resourceDescription = getResourceDescription(beanName, beanFactory);
int order = getOrder(initializer);
logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
+ order + ", resource=" + resourceDescription);
}
}
private String getResourceDescription(String beanName, ListableBeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
return registry.getBeanDefinition(beanName).getResourceDescription();
}
return "unknown";
}
@SuppressWarnings("unchecked")
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 MultipartConfigElement getMultipartConfig(ListableBeanFactory beanFactory) {
List<Entry<String, MultipartConfigElement>> beans = getOrderedBeansOfType(beanFactory,
MultipartConfigElement.class);
return beans.isEmpty() ? null : beans.get(0).getValue();
}
protected <T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
RegistrationBeanAdapter<T> adapter) {
addAsRegistrationBean(beanFactory, type, type, adapter);
}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
if (this.seen.add(bean)) {
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
}
}
}
}
private int getOrder(Object value) {
return new AnnotationAwareOrderComparator() {
@Override
public int getOrder(Object obj) {
return super.getOrder(obj);
}
}.getOrder(value);
}
private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type) {
return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());
}
private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
Set<?> excludes) {
String[] names = beanFactory.getBeanNamesForType(type, true, false);
Map<String, T> map = new LinkedHashMap<>();
for (String name : names) {
if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
T bean = beanFactory.getBean(name, type);
if (!excludes.contains(bean)) {
map.put(name, bean);
}
}
}
List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
return beans;
}
private void logMappings(MultiValueMap<Class<?>, ServletContextInitializer> initializers) {
if (logger.isDebugEnabled()) {
logMappings("filters", initializers, Filter.class, FilterRegistrationBean.class);
logMappings("servlets", initializers, Servlet.class, ServletRegistrationBean.class);
}
}
private void logMappings(String name, MultiValueMap<Class<?>, ServletContextInitializer> initializers,
Class<?> type, Class<? extends RegistrationBean> registrationType) {
List<ServletContextInitializer> registrations = new ArrayList<>();
registrations.addAll(initializers.getOrDefault(registrationType, Collections.emptyList()));
registrations.addAll(initializers.getOrDefault(type, Collections.emptyList()));
String info = registrations.stream().map(Object::toString).collect(Collectors.joining(", "));
logger.debug("Mapping " + name + ": " + info);
}
@Override
public Iterator<ServletContextInitializer> iterator() {
return this.sortedList.iterator();
}
@Override
public int size() {
return this.sortedList.size();
}
/**
* Adapter to convert a given Bean type into a {@link RegistrationBean} (and hence a
* {@link ServletContextInitializer}).
*
* @param <T> the type of the Bean to adapt
*/
@FunctionalInterface
protected interface RegistrationBeanAdapter<T> {
RegistrationBean createRegistrationBean(String name, T source, int totalNumberOfSourceBeans);
}
/**
* {@link RegistrationBeanAdapter} for {@link Servlet} beans.
*/
private static class ServletRegistrationBeanAdapter implements RegistrationBeanAdapter<Servlet> {
private final MultipartConfigElement multipartConfig;
ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig) {
this.multipartConfig = multipartConfig;
}
@Override
public RegistrationBean createRegistrationBean(String name, Servlet source, int totalNumberOfSourceBeans) {
String url = (totalNumberOfSourceBeans != 1) ? "/" + name + "/" : "/";
if (name.equals(DISPATCHER_SERVLET_NAME)) {
url = "/"; // always map the main dispatcherServlet to "/"
}
ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(source, url);
bean.setName(name);
bean.setMultipartConfig(this.multipartConfig);
return bean;
}
}
/**
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
*/
private static class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {
@Override
public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
bean.setName(name);
return bean;
}
}
/**
* {@link RegistrationBeanAdapter} for certain {@link EventListener} beans.
*/
private static class ServletListenerRegistrationBeanAdapter implements RegistrationBeanAdapter<EventListener> {
@Override
public RegistrationBean createRegistrationBean(String name, EventListener source,
int totalNumberOfSourceBeans) {
return new ServletListenerRegistrationBean<>(source);
}
}
}
该类在构造方法里通过调用 addAdaptableBeans(ListableBeanFactory)
方法,将相关的 bean 添加到了 initializers
成员里,然后又复制了一份到 sortedList
成员中。
注意到该类是继承了 AbstractCollection<ServletContextInitializer>
类,而在 iterator()
方法中返回的 this.sortedList.iterator()
,也也就是为什么在 ServletWebServerApplicationContext#selfInitialize()
方法中可以对该类的对象进行迭代的原因。
- 重点看
addAdaptableBeans
方法:
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());
}
}
该方法创建了几个 Adapter
对象并调用了 addAsRegistrationBean
方法,其中一个就是 ServletListenerRegistrationBeanAdapter
对象,而添加该类型对象的时候是一个 for
循环添加的,循环的对象是 ServletListenerRegistrationBean.getSupportedTypes()
方法的返回:
public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean {
private static final Set<Class<?>> SUPPORTED_TYPES;
static {
Set<Class<?>> types = new HashSet<>();
types.add(ServletContextAttributeListener.class);
types.add(ServletRequestListener.class);
types.add(ServletRequestAttributeListener.class);
types.add(HttpSessionAttributeListener.class);
types.add(HttpSessionIdListener.class);
types.add(HttpSessionListener.class);
types.add(ServletContextListener.class);
SUPPORTED_TYPES = Collections.unmodifiableSet(types);
}
public static Set<Class<?>> getSupportedTypes() {
return SUPPORTED_TYPES;
}
...
}
可以看到是包含了 7 个对应的类型,也就是说会添加 7 个 ServletListenerRegistrationBeanAdapter
对象,创建了该类型的对象后,会调用 addAsRegistrationBean()
方法并把该类型的对象传递进去:
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
if (this.seen.add(bean)) {
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
}
}
}
}
而在该方法中,会调用对应 Adapter 的 createRegistrationBean()
方法,我们这里是 ServletListenerRegistrationBeanAdapter
类型的 Adapter,来看 ServletListenerRegistrationBeanAdapter#createRegistrationBean()
方法:
private static class ServletListenerRegistrationBeanAdapter implements RegistrationBeanAdapter<EventListener> {
@Override
public RegistrationBean createRegistrationBean(String name, EventListener source,
int totalNumberOfSourceBeans) {
return new ServletListenerRegistrationBean<>(source);
}
}
可以看到实际是返回了一个 ServletListenerRegistrationBean
对象并添加到了 ServletContextInitializerBeans
的 initializers
成员中,而在 selfInitialize
方法中迭代的对象就是 initializers
中的内容(其复制到了 sortedList
成员中)。
另外,构造 ServletListenerRegistrationBean
对象时,把真正的 EventListener
对象传递了进去,这也就是我们在 SampleTomcatApplication
中用 @Bean
声明的 ServletContextListener
对象。
- 现在回到
selfInitialize
方法中,for
循环迭代时,也就是迭代ServletContextInitializerBeans
对象的sortedList
成员时,调用了每个sortedList
成员中每个对象的onStarup()
方法,上面提到其中一个是ServletListenerRegistrationBean
对象,来看ServletListenerRegistrationBean#onStarup()
方法:
public 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);
}
再看 register
方法:
protected void register(String description, ServletContext servletContext) {
try {
servletContext.addListener(this.listener);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Failed to add listener '" + this.listener + "' to servlet context", ex);
}
}
可以看到,核心的就是通过 ServletContext
对象,调用了其 addListener()
方法把真正的 ServletContextListener
对象给传递了过去。
- 注意到在
addAdaptableBeans()
方法中调用addAsRegistrationBean
方法时,是把具体的listenerType
也就是支持的类型传递进去了的:
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
// 注意 addAsRegistrationBean() 方法的第三个参数,是具体支持的类型
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}
这样在 addAsRegistrationBean
方法中根据 beanFactory
寻找对应类型的对象时就把 SampleTomcatApplication
中我们声明的 ServletContextListener
对象找出来了:
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
// 寻找支持类型的对象,这里是 ServletContextListener 对象
List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
if (this.seen.add(bean)) {
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
}
}
}
}
- 我们成功把
ServletContextListener
对象通过ServletContext
对象传递给了Tomcat
,那也就可以坐等回调被调用就可以了。
总结
- Tomcat 会回调 Spring 的 selfInitialize 方法
- 在 selfInitialize 方法中会创建一个可迭代的 ServletContextInitializerBeans 对象
- 迭代 ServletContextInitializerBeans 对像时其中一个对象是 ServletListenerRegistrationBean 对象
- 所以会调用 ServletListenerRegistrationBean#onStartup() 方法
- 在 ServletListenerRegistrationBean#onStartup() 方法中会调用 register 方法
- 在 register 方法中会调用 ServletContext#addListener() 方法将真正的 ServletContextListener 传递给 Tomcat,以坐等回调
- 为什么可以得到 ServletContextListener 对象?那是因为使用了 beanFactory 来寻找了 ServletListenerRegistrationBean 类中明确支持的类型,这里是 ServletContextListener 类型
网友评论