如何注册
1.注解+启动类扫描
@ServletComponentScan@SpringBootApplicationpublic class SpringbootUploadApplication {public static void main(String[] args) {SpringApplication.run(SpringbootUploadApplication.class, args);}}
@WebServlet(urlPatterns = "/my")public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.err.println("MyServlet doGet");resp.getWriter().write("my");}}@WebFilter(urlPatterns = "/my")public class MyFilter extends HttpFilter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;System.err.println("MyFilter doFilter");chain.doFilter(request,response);}}@WebListenerpublic class MyListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.err.println("MyListener contextInitialized"+sce.getServletContext().getServerInfo());}@Overridepublic void contextDestroyed(ServletContextEvent sce) {}}
2.注册ServletRegistrationBean
还是这三个组件,不过把注解@WebServlet/@WebFilter/@WebListener去掉,自动扫描就失效了
取而代之配置类去注册
@Configurationpublic class ServletConfig {@Beanpublic ServletRegistrationBean myServlet(){MyServlet myServlet = new MyServlet();return new ServletRegistrationBean(myServlet,"/my","/my02");}@Beanpublic FilterRegistrationBean myFilter(){MyFilter myFilter = new MyFilter();FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));return filterRegistrationBean;}@Beanpublic ServletListenerRegistrationBean myListener(){MyListener mySwervletContextListener = new MyListener();return new ServletListenerRegistrationBean(mySwervletContextListener);}}
源码
先看看注册Bean的类图
感觉用不上,不想看源码 蛋疼,硬着头皮早上写!干饭
- SpringApplication#refreshContext
- AnnotationConfigServletWebServerApplicationContext#refresh #onRefresh
- TomcatServletWebServerFactory 创建tomcat服务器并start
下面就是tomcat流程
接下来就是异步任务
子类调用父类public class StandardEngine extends ContainerBase implements Engine {@Overrideprotected synchronized void startInternal() throws LifecycleException {// Log our server identification informationif (log.isInfoEnabled()) {log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));}// Standard container startupsuper.startInternal();}}父类方法逻辑public abstract class ContainerBase extends LifecycleMBeanBase implements Container {@Overrideprotected synchronized void startInternal() throws LifecycleException {// Start our subordinate components, if anylogger = null;getLogger();Cluster cluster = getClusterInternal();if (cluster instanceof Lifecycle) {((Lifecycle) cluster).start();}Realm realm = getRealmInternal();if (realm instanceof Lifecycle) {((Lifecycle) realm).start();}// Start our child containers, if anyContainer children[] = findChildren();List<Future<Void>> results = new ArrayList<>();for (Container child : children) {results.add(startStopExecutor.submit(new StartChild(child)));}MultiThrowable multiThrowable = null;for (Future<Void> result : results) {try {result.get();} catch (Throwable e) {log.error(sm.getString("containerBase.threadedStartFailed"), e);if (multiThrowable == null) {multiThrowable = new MultiThrowable();}multiThrowable.add(e);}}if (multiThrowable != null) {throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),multiThrowable.getThrowable());}// Start the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}setState(LifecycleState.STARTING);// Start our threadif (backgroundProcessorDelay > 0) {monitorFuture = Container.getService(ContainerBase.this).getServer().getUtilityExecutor().scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);}}}
然后其中一个异步任务就会执行以下逻辑,具体跟不到是哪里
StandardHost->异步->TomcatEmbeddedContext#start->#startInternal->
部分逻辑public class StandardContext extends ContainerBase implements Context, NotificationEmitter {@Overrideprotected synchronized void startInternal() throws LifecycleException {// Call ServletContainerInitializersfor (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :initializers.entrySet()) {try {entry.getKey().onStartup(entry.getValue(),getServletContext());} catch (ServletException e) {log.error(sm.getString("standardContext.sciFail"), e);ok = false;break;}}}}
class TomcatStarter implements ServletContainerInitializer {//有三个,见下图private final ServletContextInitializer[] initializers;@Overridepublic void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {try {for (ServletContextInitializer initializer : this.initializers) {initializer.onStartup(servletContext);}}catch (Exception ex) {this.startUpException = ex;// Prevent Tomcat from logging and re-throwing when we know we can// deal with it in the main thread, but log for information here.if (logger.isErrorEnabled()) {logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "+ ex.getMessage());}}}}
好像起作用的是第一个噢!只不过是一个接口函数声明,看不到是啥样的
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {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());}}
调用关系图
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {@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);//1添加初始化BeanaddServletContextInitializerBeans(beanFactory);//2添加适配BeanaddAdaptableBeans(beanFactory);//3排序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);}//1添加初始化Beanprivate 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 <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type) {return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());}//这里的type就是上级接口ServletContextInitializer,返回的就是我们自定的三个ServletRegistrationBean/FilterRegistrationBean/ServletListenerRegistrationBean,详见ServletContextInitializer继承图//至于这几个是什么时候注册好的,请看下回分解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;}//2添加适配Beanprotected 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()) {//6种监听器,见下图addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,new ServletListenerRegistrationBeanAdapter());}}}

大致就在这里了,没有细看,缺的没有看这几个Bean是怎么注册到Spring中
接下来走到
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);//这一步走完了,得到了啥 见下图for (ServletContextInitializer beans : getServletContextInitializerBeans()) {//现在到这里了beans.onStartup(servletContext);}}}
都是熟悉的面孔
上面这些组件startUp顺序执行,逻辑都差不多
其实是ServletRegistrationBeanpublic abstract class RegistrationBean implements ServletContextInitializer, Ordered {@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);}@Overrideprotected final void register(String description, ServletContext servletContext) {D registration = addRegistration(description, servletContext);if (registration == null) {logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");return;}configure(registration);}@Overrideprotected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {String name = getServletName();return servletContext.addServlet(name, this.servlet);}@Overrideprotected void configure(ServletRegistration.Dynamic registration) {super.configure(registration);String[] urlMapping = StringUtils.toStringArray(this.urlMappings);if (urlMapping.length == 0 && this.alwaysMapUrl) {urlMapping = DEFAULT_MAPPINGS;}if (!ObjectUtils.isEmpty(urlMapping)) {registration.addMapping(urlMapping);}registration.setLoadOnStartup(this.loadOnStartup);if (this.multipartConfig != null) {registration.setMultipartConfig(this.multipartConfig);}}}
然后就完毕了
