
环境:springboot2.3.9.RELEASE 1 Servlet注册方式1: 在配置类(启动类)上添加@ServletComponentScan注解 @SpringBootApplication @ServletComponentScan public class SpringBootComprehensiveApplication } Servlet类上添加@WebServlet注解接口 @WebServlet("/hello") public class MyServlet extends HttpServlet { } 对应的注册t种Filter, Linstener有:@WebFilter,都知道内 @WebListener 方式2: 通过向IOC容器添加ServletRegistrationBean方式;该种方式可以在Servlet中注入其它Bean或者读取application.properties配置信息等。对应的部实filter, Listener有对应的bean;FilterRegistrationBean,ServletListenerRegistrationBean @Bean public ServletRegistrationBean<MyServlet> servlet() { ServletRegistrationBean<MyServlet> servlet = new ServletRegistrationBean<>(new MyServlet()) ; servlet.addUrlMappings("/hello") ; return servlet ; } 方式3: 动态注册Servlet @Component public class DynamicRegServlet implements ServletContextInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { ServletRegistration initServlet = servletContext.addServlet("myServlet", MyServlet.class) ; initServlet.addMapping("/hello") ; } } 该种方式是利用的Servlet3.0开始才有的功能,通过SPI技术在容器启动的现原析时候做一些初始化工作,比如注册Servlet等。理解在Servle规范中通过 ServletContainerInitializer实现该功能。注册t种 该Servlet规范的都知道内开发流程如下; 2、配置ServletContainerInitializer 在src/META-INF/services下新建 javax.servlet.ServletContainerInitializer文件,部实文件内容为ServletContainerInitializer接口的现原析实现类(完整的包名+类名)如下: com.pack.container.config.CustomServletInitializer CustomServletInitializer类 @HandlesTypes({ScoreWebInit.class}) public class CustomServletInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException { c.forEach(web -> { try { ((ScoreWebInit)web.newInstance()).pay() ; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); ServletRegistration.Dynamic reg = ctx.addServlet("MyServlet", com.pack.servlet.MyServlet.class) ; reg.setLoadOnStartup(1) ; reg.addMapping("/hello") ; } } 注意:@HandlesTypes该注解会把属性value配置的值(ScoreWebInt.class)对应的所有类都收集上然后在onStartup方法中的Set 集合中应用。 在spring-web-xxxx.jar下就有通过该技术实现的理解相应功能。 3 扫描Servlet实现原理在方式1中的注册t种实现原理就是扫描类路径下所有@WebServlet,@WebFilter,都知道内@WebListener。部实找到所有的现原析类后再通过方式2的方式进行注册。下面将核心源码贴出 3.1 导入核心类// 该注解如果没有配置basePackages或者basePackageClasses属性,理解那么会读取当前添加@ServletComponentScan注解的b2b信息网类的包路径进行扫描。 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServletComponentScanRegistrar.class) public @interface ServletComponentScan { } // 注册BeanDefinition class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); if (registry.containsBeanDefinition(BEAN_NAME)) { updatePostProcessor(registry, packagesToScan); } else { // 当前容器中没有对应的Bean时执行该方法 addPostProcessor(registry, packagesToScan); } } } 3.2 注册BeanFactory 处理器ServletComponentScanRegistrar最核心的功能就是注册BeanFactoryPostProcessor class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{ private void addPostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN_NAME, beanDefinition); } } 3.3 实例化扫描组件进入 ServletComponentRegisteringPostProcessor类中首先这个类有个static代码块 class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private static final List<ServletComponentHandler> HANDLERS; static { List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>(); servletComponentHandlers.add(new WebServletHandler()); servletComponentHandlers.add(new WebFilterHandler()); servletComponentHandlers.add(new WebListenerHandler()); HANDLERS = Collections.unmodifiableList(servletComponentHandlers); } } 这段代码分别是添加相应Servlet, Filter, Listener的处理句柄,分别处理@WebServlet,@WebFilter,@WebListener 注解。 查看WebServletHandler class WebServletHandler extends ServletComponentHandler { WebServletHandler() { super(WebServlet.class); } // 看到该方法也就十分清楚了最终找到所有的Class以后,通过ServletRegistrationBean进行注册为Bean @Override public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class); builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported")); builder.addPropertyValue("initParameters", extractInitParameters(attributes)); builder.addPropertyValue("loadOnStartup", attributes.get("loadOnStartup")); String name = determineName(attributes, beanDefinition); builder.addPropertyValue("name", name); builder.addPropertyValue("servlet", beanDefinition); builder.addPropertyValue("urlMappings", extractUrlPatterns(attributes)); builder.addPropertyValue("multipartConfig", determineMultipartConfig(beanDefinition)); registry.registerBeanDefinition(name, builder.getBeanDefinition()); } // other code } // 父类ServletComponentHandler;在父类总添加相应的过滤器(分别查找相应注解的类,@WebServlet等。) abstract class ServletComponentHandler { private final Class<? extends Annotation> annotationType; private final TypeFilter typeFilter; protected ServletComponentHandler(Class<? extends Annotation> annotationType) { this.typeFilter = new AnnotationTypeFilter(annotationType); this.annotationType = annotationType; } } 接下来执行BeanFactoryPostProcessor对应的回调方法了 class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (isRunningInEmbeddedWebServer()) { ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider(); for (String packageToScan : this.packagesToScan) { scanPackage(componentProvider, packageToScan); } } } } createComponentProvider方法进行创建扫描相应符合条件的Bean扫描类 private ClassPathScanningCandidateComponentProvider createComponentProvider() { ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); componentProvider.setEnvironment(this.applicationContext.getEnvironment()); componentProvider.setResourceLoader(this.applicationContext); for (ServletComponentHandler handler : HANDLERS) { componentProvider.addIncludeFilter(handler.getTypeFilter()); } return componentProvider; } 在该方法中为当前的 ClassPathScanningCandidateComponentProvider类扫描设置过滤器;过滤器在上面的static静态代码块中已经设置了WebServletHandler,WebFilterHandler,WebListenerHandler在父类中分别创建不同注解的new AnnotationTypeFilter(annotationType)过滤类。 创建完类扫描类以后开始扫描通过该类扫描相应包(路径)下的所有类文件 检查是否有对应的注解。 3.4 查找及注册Beanclass ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) { for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) { if (candidate instanceof AnnotatedBeanDefinition) { for (ServletComponentHandler handler : HANDLERS) { handler.handle(((AnnotatedBeanDefinition) candidate), (BeanDefinitionRegistry) this.applicationContext); } } } } } findCandidateComponents方法查找候选的(符合条件)的类并实例化为BeanDefinition对象。 方法执行链findCandidateComponents ---》scanCandidateComponents 在scanCandidateComponents方法中查找符合条件的类,服务器托管然后实例化为BeanDefinition public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware { private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } return candidates; } protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; } } 在第二个for开始匹配所有的类是否有相关的注解。如果匹配上就相应的创建BeanDefinition对象放入集合Set中。 查找到所有的类以后分别调用相应的Web*Handler(ServletComponentHandler)进行注册Bean。 返回到上面的scanPackage方法中执行handler.handle方法。 abstract class ServletComponentHandler { void handle(AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) { Map<String, Object> attributes = beanDefinition.getMetadata().getAnnotationAttributes(this.annotationType.getName()); if (attributes != null) { doHandle(attributes, beanDefinition, registry); } } } doHandle方法分别在子类(WebServletHandler,WebFilterHandler,WebListenerHandler)中实现,如上面已经提到的WebServletHandler类的doHandler方法。 到这里我们知道了注解的方式最终也是被注册为ServletRegistrationBean 实例。那这个ServletRegistrationBean又是如何被容器(Tomcat)所感知的呢? 3.5 Tomcat注册Servlet在Web容器下 BeanFactory使用的是 AnnotationConfigServletWebServerApplicationContext Spring IOC容器核心方法是refresh方法 public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { try { // Initialize other special beans in specific context subclasses. onRefresh(); } catch (BeansException ex) { throw ex; } finally { resetCommonCaches(); } } } } 这里只留了onRefresh方法,进入该方法: onRefresh会进入到子类的方法 public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } } 进入createWebServer方法 public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); } } 这里会进入factory.getWebServer(getSelfInitializer())方法执行 进入getSelfInitializer()方法 public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } } 这里的for循环是遍历当前容器中所有ServletContextInitializer类型的Bean。ServletRegistrationBean就是继承ServletContextInitializer 到这里分别调用ServletContextInitializer的亿华云onStartup方法,进入onStartup方法: public abstract class RegistrationBean implements ServletContextInitializer, Ordered { @Override 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); } // 在子类中实现 protected abstract void register(String description, ServletContext servletContext); } 进入子类DynamicRegistrationBean public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean { @Override protected 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; } // 配置Servlet Mapping相关的信息(在子类ServletRegistrationBean中实现的) configure(registration); } // 子类中实现 protected abstract D addRegistration(String description, ServletContext servletContext); } 进入子类ServletRegistrationBean public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> { @Override protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) { String name = getServletName(); return servletContext.addServlet(name, this.servlet); } } 到这里就是通过ServletContext来动态注册Servlet(Servilet3.0)。 这里返回了 ServletRegistration.Dynamic对象会继续执行configure方法配置urlMapping等信息。 public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> { @Override protected 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); } } } 到此在Springboot环境下Servlet如何被注册到Servlet容器中就已经清晰了。这动态注册Servlet的相关API都是在Servlet3.0规范中才有的。 |