新聞中心
環(huán)境:springboot2.3.9.RELEASE

1 Servlet注冊(cè)
方式1:
在配置類(啟動(dòng)類)上添加@ServletComponentScan注解
- @SpringBootApplication
- @ServletComponentScan
- public class SpringBootComprehensiveApplication
- }
Servlet類上添加@WebServlet注解接口
- @WebServlet("/hello")
- public class MyServlet extends HttpServlet {
- }
對(duì)應(yīng)的Filter, Linstener有:@WebFilter, @WebListener
方式2:
通過(guò)向IOC容器添加ServletRegistrationBean方式;該種方式可以在Servlet中注入其它Bean或者讀取application.properties配置信息等。對(duì)應(yīng)的filter, Listener有對(duì)應(yīng)的bean;FilterRegistrationBean,ServletListenerRegistrationBean
- @Bean
- public ServletRegistrationBean
servlet() { - ServletRegistrationBean
servlet = new ServletRegistrationBean<>(new MyServlet()) ; - servlet.addUrlMappings("/hello") ;
- return servlet ;
- }
方式3:
動(dòng)態(tài)注冊(cè)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開(kāi)始才有的功能,通過(guò)SPI技術(shù)在容器啟動(dòng)的時(shí)候做一些初始化工作,比如注冊(cè)Servlet等。在Servle規(guī)范中通過(guò)
ServletContainerInitializer實(shí)現(xiàn)該功能。
該Servlet規(guī)范的開(kāi)發(fā)流程如下;
2、配置
ServletContainerInitializer
在src/META-INF/services下新建
javax.servlet.ServletContainerInitializer文件,文件內(nèi)容為ServletContainerInitializer接口的實(shí)現(xiàn)類(完整的包名+類名)如下:
- com.pack.container.config.CustomServletInitializer
CustomServletInitializer類
- @HandlesTypes({ScoreWebInit.class})
- public class CustomServletInitializer implements ServletContainerInitializer {
- @Override
- public void onStartup(Set
> 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該注解會(huì)把屬性value配置的值(ScoreWebInt.class)對(duì)應(yīng)的所有類都收集上然后在onStartup方法中的Set 集合中應(yīng)用。
在spring-web-xxxx.jar下就有通過(guò)該技術(shù)實(shí)現(xiàn)的相應(yīng)功能。
3 掃描Servlet實(shí)現(xiàn)原理
在方式1中的實(shí)現(xiàn)原理就是掃描類路徑下所有@WebServlet,@WebFilter,@WebListener。找到所有的類后再通過(guò)方式2的方式進(jìn)行注冊(cè)。下面將核心源碼貼出
3.1 導(dǎo)入核心類
- // 該注解如果沒(méi)有配置basePackages或者basePackageClasses屬性,那么會(huì)讀取當(dāng)前添加@ServletComponentScan注解的類的包路徑進(jìn)行掃描。
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Import(ServletComponentScanRegistrar.class)
- public @interface ServletComponentScan {
- }
- // 注冊(cè)BeanDefinition
- class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- Set
packagesToScan = getPackagesToScan(importingClassMetadata); - if (registry.containsBeanDefinition(BEAN_NAME)) {
- updatePostProcessor(registry, packagesToScan);
- } else {
- // 當(dāng)前容器中沒(méi)有對(duì)應(yīng)的Bean時(shí)執(zhí)行該方法
- addPostProcessor(registry, packagesToScan);
- }
- }
- }
3.2 注冊(cè)BeanFactory 處理器
ServletComponentScanRegistrar最核心的功能就是注冊(cè)BeanFactoryPostProcessor
- class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
- private void addPostProcessor(BeanDefinitionRegistry registry, Set
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 實(shí)例化掃描組件
進(jìn)入
ServletComponentRegisteringPostProcessor類中首先這個(gè)類有個(gè)static代碼塊
- class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
- private static final List
HANDLERS; - static {
- List
servletComponentHandlers = new ArrayList<>(); - servletComponentHandlers.add(new WebServletHandler());
- servletComponentHandlers.add(new WebFilterHandler());
- servletComponentHandlers.add(new WebListenerHandler());
- HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
- }
- }
這段代碼分別是添加相應(yīng)Servlet, Filter, Listener的處理句柄,分別處理@WebServlet,@WebFilter,@WebListener 注解。
查看WebServletHandler
- class WebServletHandler extends ServletComponentHandler {
- WebServletHandler() {
- super(WebServlet.class);
- }
- // 看到該方法也就十分清楚了最終找到所有的Class以后,通過(guò)ServletRegistrationBean進(jìn)行注冊(cè)為Bean
- @Override
- public void doHandle(Map
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;在父類總添加相應(yīng)的過(guò)濾器(分別查找相應(yīng)注解的類,@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;
- }
- }
接下來(lái)執(zhí)行BeanFactoryPostProcessor對(duì)應(yīng)的回調(diào)方法了
- 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方法進(jìn)行創(chuàng)建掃描相應(yīng)符合條件的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;
- }
在該方法中為當(dāng)前的
ClassPathScanningCandidateComponentProvider類掃描設(shè)置過(guò)濾器;過(guò)濾器在上面的static靜態(tài)代碼塊中已經(jīng)設(shè)置了WebServletHandler,WebFilterHandler,WebListenerHandler在父類中分別創(chuàng)建不同注解的new AnnotationTypeFilter(annotationType)過(guò)濾類。
創(chuàng)建完類掃描類以后開(kāi)始掃描通過(guò)該類掃描相應(yīng)包(路徑)下的所有類文件 檢查是否有對(duì)應(yīng)的注解。
3.4 查找及注冊(cè)Bean
- class 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方法查找候選的(符合條件)的類并實(shí)例化為BeanDefinition對(duì)象。
方法執(zhí)行鏈findCandidateComponents ---》scanCandidateComponents
在scanCandidateComponents方法中查找符合條件的類,然后實(shí)例化為BeanDefinition
- public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
- private Set
scanCandidateComponents(String basePackage) { - Set
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;
- }
- }
在第二個(gè)for開(kāi)始匹配所有的類是否有相關(guān)的注解。如果匹配上就相應(yīng)的創(chuàng)建BeanDefinition對(duì)象放入集合Set中。
查找到所有的類以后分別調(diào)用相應(yīng)的Web*Handler(ServletComponentHandler)進(jìn)行注冊(cè)Bean。
返回到上面的scanPackage方法中執(zhí)行handler.handle方法。
- abstract class ServletComponentHandler {
- void handle(AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
- Map
attributes = beanDefinition.getMetadata().getAnnotationAttributes(this.annotationType.getName()); - if (attributes != null) {
- doHandle(attributes, beanDefinition, registry);
- }
- }
- }
doHandle方法分別在子類(WebServletHandler,WebFilterHandler,WebListenerHandler)中實(shí)現(xiàn),如上面已經(jīng)提到的WebServletHandler類的doHandler方法。
到這里我們知道了注解的方式最終也是被注冊(cè)為ServletRegistrationBean 實(shí)例。那這個(gè)ServletRegistrationBean又是如何被容器(Tomcat)所感知的呢?
3.5 Tomcat注冊(cè)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方法,進(jìn)入該方法:
onRefresh會(huì)進(jìn)入到子類的方法
- 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);
- }
- }
- }
進(jìn)入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();
- }
- }
這里會(huì)進(jìn)入factory.getWebServer(getSelfInitializer())方法執(zhí)行
進(jìn)入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循環(huán)是遍歷當(dāng)前容器中所有ServletContextInitializer類型的Bean。ServletRegistrationBean就是繼承ServletContextInitializer
到這里分別調(diào)用ServletContextInitializer的onStartup方法,進(jìn)入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);
- }
- // 在子類中實(shí)現(xiàn)
- protected abstract void register(String description, ServletContext servletContext);
- }
進(jìn)入子類DynamicRegistrationBean
- public abstract class DynamicRegistrationBean
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相關(guān)的信息(在子類ServletRegistrationBean中實(shí)現(xiàn)的)
- configure(registration);
- }
- // 子類中實(shí)現(xiàn)
- protected abstract D addRegistration(String description, ServletContext servletContext);
- }
進(jìn)入子類ServletRegistrationBean
- public class ServletRegistrationBean
extends DynamicRegistrationBean { - @Override
- protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
- String name = getServletName();
- return servletContext.addServlet(name, this.servlet);
- }
- }
到這里就是通過(guò)ServletContext來(lái)動(dòng)態(tài)注冊(cè)Servlet(Servilet3.0)。
這里返回了
ServletRegistration.Dynamic對(duì)象會(huì)繼續(xù)執(zhí)行configure方法配置urlMapping等信息。
- public class ServletRegistrationBean
extends DynamicRegistrationBean { - @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環(huán)境下Servlet如何被注冊(cè)到Servlet容器中就已經(jīng)清晰了。這動(dòng)態(tài)注冊(cè)Servlet的相關(guān)API都是在Servlet3.0規(guī)范中才有的。
當(dāng)前題目:Springboot注冊(cè)Servlet幾種方式你都知道??jī)?nèi)部實(shí)現(xiàn)原理解析
本文地址:http://www.dlmjj.cn/article/cceesis.html


咨詢
建站咨詢
