如何注册

1.注解+启动类扫描

  1. @ServletComponentScan
  2. @SpringBootApplication
  3. public class SpringbootUploadApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringbootUploadApplication.class, args);
  6. }
  7. }
  1. @WebServlet(urlPatterns = "/my")
  2. public class MyServlet extends HttpServlet {
  3. @Override
  4. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  5. System.err.println("MyServlet doGet");
  6. resp.getWriter().write("my");
  7. }
  8. }
  9. @WebFilter(urlPatterns = "/my")
  10. public class MyFilter extends HttpFilter {
  11. @Override
  12. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  13. HttpServletRequest req = (HttpServletRequest) request;
  14. System.err.println("MyFilter doFilter");
  15. chain.doFilter(request,response);
  16. }
  17. }
  18. @WebListener
  19. public class MyListener implements ServletContextListener {
  20. @Override
  21. public void contextInitialized(ServletContextEvent sce) {
  22. System.err.println("MyListener contextInitialized"+sce.getServletContext().getServerInfo());
  23. }
  24. @Override
  25. public void contextDestroyed(ServletContextEvent sce) {
  26. }
  27. }

2.注册ServletRegistrationBean

还是这三个组件,不过把注解@WebServlet/@WebFilter/@WebListener去掉,自动扫描就失效了
取而代之配置类去注册

  1. @Configuration
  2. public class ServletConfig {
  3. @Bean
  4. public ServletRegistrationBean myServlet(){
  5. MyServlet myServlet = new MyServlet();
  6. return new ServletRegistrationBean(myServlet,"/my","/my02");
  7. }
  8. @Bean
  9. public FilterRegistrationBean myFilter(){
  10. MyFilter myFilter = new MyFilter();
  11. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
  12. filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
  13. return filterRegistrationBean;
  14. }
  15. @Bean
  16. public ServletListenerRegistrationBean myListener(){
  17. MyListener mySwervletContextListener = new MyListener();
  18. return new ServletListenerRegistrationBean(mySwervletContextListener);
  19. }
  20. }

源码

先看看注册Bean的类图
image.png
感觉用不上,不想看源码 蛋疼,硬着头皮早上写!干饭
image.png

  1. SpringApplication#refreshContext
  2. AnnotationConfigServletWebServerApplicationContext#refresh #onRefresh
  3. TomcatServletWebServerFactory 创建tomcat服务器并start

下面就是tomcat流程
image.png
接下来就是异步任务

  1. 子类调用父类
  2. public class StandardEngine extends ContainerBase implements Engine {
  3. @Override
  4. protected synchronized void startInternal() throws LifecycleException {
  5. // Log our server identification information
  6. if (log.isInfoEnabled()) {
  7. log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
  8. }
  9. // Standard container startup
  10. super.startInternal();
  11. }
  12. }
  13. 父类方法逻辑
  14. public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
  15. @Override
  16. protected synchronized void startInternal() throws LifecycleException {
  17. // Start our subordinate components, if any
  18. logger = null;
  19. getLogger();
  20. Cluster cluster = getClusterInternal();
  21. if (cluster instanceof Lifecycle) {
  22. ((Lifecycle) cluster).start();
  23. }
  24. Realm realm = getRealmInternal();
  25. if (realm instanceof Lifecycle) {
  26. ((Lifecycle) realm).start();
  27. }
  28. // Start our child containers, if any
  29. Container children[] = findChildren();
  30. List<Future<Void>> results = new ArrayList<>();
  31. for (Container child : children) {
  32. results.add(startStopExecutor.submit(new StartChild(child)));
  33. }
  34. MultiThrowable multiThrowable = null;
  35. for (Future<Void> result : results) {
  36. try {
  37. result.get();
  38. } catch (Throwable e) {
  39. log.error(sm.getString("containerBase.threadedStartFailed"), e);
  40. if (multiThrowable == null) {
  41. multiThrowable = new MultiThrowable();
  42. }
  43. multiThrowable.add(e);
  44. }
  45. }
  46. if (multiThrowable != null) {
  47. throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
  48. multiThrowable.getThrowable());
  49. }
  50. // Start the Valves in our pipeline (including the basic), if any
  51. if (pipeline instanceof Lifecycle) {
  52. ((Lifecycle) pipeline).start();
  53. }
  54. setState(LifecycleState.STARTING);
  55. // Start our thread
  56. if (backgroundProcessorDelay > 0) {
  57. monitorFuture = Container.getService(ContainerBase.this).getServer()
  58. .getUtilityExecutor().scheduleWithFixedDelay(
  59. new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
  60. }
  61. }
  62. }

然后其中一个异步任务就会执行以下逻辑,具体跟不到是哪里
image.png
StandardHost->异步->TomcatEmbeddedContext#start->#startInternal->

  1. 部分逻辑
  2. public class StandardContext extends ContainerBase implements Context, NotificationEmitter {
  3. @Override
  4. protected synchronized void startInternal() throws LifecycleException {
  5. // Call ServletContainerInitializers
  6. for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
  7. initializers.entrySet()) {
  8. try {
  9. entry.getKey().onStartup(entry.getValue(),
  10. getServletContext());
  11. } catch (ServletException e) {
  12. log.error(sm.getString("standardContext.sciFail"), e);
  13. ok = false;
  14. break;
  15. }
  16. }
  17. }
  18. }
  1. class TomcatStarter implements ServletContainerInitializer {
  2. //有三个,见下图
  3. private final ServletContextInitializer[] initializers;
  4. @Override
  5. public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
  6. try {
  7. for (ServletContextInitializer initializer : this.initializers) {
  8. initializer.onStartup(servletContext);
  9. }
  10. }
  11. catch (Exception ex) {
  12. this.startUpException = ex;
  13. // Prevent Tomcat from logging and re-throwing when we know we can
  14. // deal with it in the main thread, but log for information here.
  15. if (logger.isErrorEnabled()) {
  16. logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
  17. + ex.getMessage());
  18. }
  19. }
  20. }
  21. }

image.png好像起作用的是第一个噢!只不过是一个接口函数声明,看不到是啥样的

  1. public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
  2. private void selfInitialize(ServletContext servletContext) throws ServletException {
  3. prepareWebApplicationContext(servletContext);
  4. registerApplicationScope(servletContext);
  5. WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
  6. for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
  7. beans.onStartup(servletContext);
  8. }
  9. }
  10. protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
  11. return new ServletContextInitializerBeans(getBeanFactory());
  12. }
  13. }

image.png调用关系图

  1. public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
  2. @SafeVarargs
  3. @SuppressWarnings("varargs")
  4. public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
  5. Class<? extends ServletContextInitializer>... initializerTypes) {
  6. this.initializers = new LinkedMultiValueMap<>();
  7. this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
  8. : Collections.singletonList(ServletContextInitializer.class);
  9. //1添加初始化Bean
  10. addServletContextInitializerBeans(beanFactory);
  11. //2添加适配Bean
  12. addAdaptableBeans(beanFactory);
  13. //3排序
  14. List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
  15. .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
  16. .collect(Collectors.toList());
  17. this.sortedList = Collections.unmodifiableList(sortedInitializers);
  18. logMappings(this.initializers);
  19. }
  20. //1添加初始化Bean
  21. private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
  22. for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
  23. for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,initializerType)) {
  24. addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
  25. }
  26. }
  27. }
  28. //通用获取接口
  29. private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type) {
  30. return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());
  31. }
  32. //这里的type就是上级接口ServletContextInitializer,返回的就是我们自定的三个ServletRegistrationBean/FilterRegistrationBean/ServletListenerRegistrationBean,详见ServletContextInitializer继承图
  33. //至于这几个是什么时候注册好的,请看下回分解
  34. private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
  35. Set<?> excludes) {
  36. String[] names = beanFactory.getBeanNamesForType(type, true, false);
  37. Map<String, T> map = new LinkedHashMap<>();
  38. for (String name : names) {
  39. if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
  40. T bean = beanFactory.getBean(name, type);
  41. if (!excludes.contains(bean)) {
  42. map.put(name, bean);
  43. }
  44. }
  45. }
  46. List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
  47. beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
  48. return beans;
  49. }
  50. //2添加适配Bean
  51. protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
  52. MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
  53. addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
  54. addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
  55. for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {//6种监听器,见下图
  56. addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
  57. new ServletListenerRegistrationBeanAdapter());
  58. }
  59. }
  60. }

image.png
大致就在这里了,没有细看,缺的没有看这几个Bean是怎么注册到Spring中
接下来走到

  1. public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
  2. private void selfInitialize(ServletContext servletContext) throws ServletException {
  3. prepareWebApplicationContext(servletContext);
  4. registerApplicationScope(servletContext);
  5. WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
  6. //这一步走完了,得到了啥 见下图
  7. for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
  8. //现在到这里了
  9. beans.onStartup(servletContext);
  10. }
  11. }
  12. }

image.png都是熟悉的面孔
上面这些组件startUp顺序执行,逻辑都差不多

  1. 其实是ServletRegistrationBean
  2. public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
  3. @Override
  4. public final void onStartup(ServletContext servletContext) throws ServletException {
  5. String description = getDescription();
  6. if (!isEnabled()) {
  7. logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
  8. return;
  9. }
  10. register(description, servletContext);
  11. }
  12. @Override
  13. protected final void register(String description, ServletContext servletContext) {
  14. D registration = addRegistration(description, servletContext);
  15. if (registration == null) {
  16. logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
  17. return;
  18. }
  19. configure(registration);
  20. }
  21. @Override
  22. protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
  23. String name = getServletName();
  24. return servletContext.addServlet(name, this.servlet);
  25. }
  26. @Override
  27. protected void configure(ServletRegistration.Dynamic registration) {
  28. super.configure(registration);
  29. String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
  30. if (urlMapping.length == 0 && this.alwaysMapUrl) {
  31. urlMapping = DEFAULT_MAPPINGS;
  32. }
  33. if (!ObjectUtils.isEmpty(urlMapping)) {
  34. registration.addMapping(urlMapping);
  35. }
  36. registration.setLoadOnStartup(this.loadOnStartup);
  37. if (this.multipartConfig != null) {
  38. registration.setMultipartConfig(this.multipartConfig);
  39. }
  40. }
  41. }

然后就完毕了