springboot源码(web篇)

一.原生web注解和用spring方式注入的区别

1.使用原生注解servlet API

  • 在主类上添加servlet组件扫描注解@ServletComponentScan,在其中指定组件的位置,示例如下:
@ServletComponentScan(basePackages = "com.example.admin")
@SpringBootApplication
public class webAdmin {
    public static void main(String[] args) {
        SpringApplication.run(webAdmin.class,args);
    }
}

1.1 servlet用法

  • 创建自定义servlet类并继承HttpServlet,然后添加@WebServlet注解,其中指定访问地址,可调用HttpServlet中的方法进行操作,示例如下:
@WebServlet(urlPatterns = "/myServlet")
public class myServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("5533");
    }
}

1.2 listener用法

  • 创建自定义listener类作为ServletContextListener的实现类,然后添加@WebListener注解,可调用ServletContextListener中的方法进行实现,示例如下:
@WebListener
public class myListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("监听初始化");
    }
}

1.3 filter用法

  • 创建自定义filter类作为Filter的实现类,然后添加@WebFilter注解,指定过滤地址,然后可调用Filter中的方法进行实现,示例如下:
@WebFilter(urlPatterns = "/image/*")
public class myFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化完成");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("工作");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("销毁");
    }
}

2.使用spring方式注入Servlet组件

  • 去掉三个组件类的web原生注解,然后创建配置类,以xxRegistrationBean类型的形式将这三个组件注入spring容器中
@Configuration
public class ServletConfig {
    @Bean
    public ServletRegistrationBean myServlet(){
         myServlet servlet = new myServlet();
         return new ServletRegistrationBean(servlet,"/my");
     }

     @Bean
    public FilterRegistrationBean myFilter(){
         myFilter filter = new myFilter();
         //filterRegistrationBean = new FilterRegistrationBean(filter,myServlet());针对myServlet地址
         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter, myServlet());
         filterRegistrationBean.setUrlPatterns(Arrays.asList("/my"));//针对自定义地址
         return filterRegistrationBean;
     }
    
     @Bean
    public ServletListenerRegistrationBean myListener(){
         myListener listener = new myListener();
         return new ServletListenerRegistrationBean(listener);
     }
}

3.dispatcherservlet如何注入进来(分析其自动配置类)

@Bean(
            name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

通过以上源码可知,这个自动配置类把dispatcherservlet属性与WebMvcProperties进行绑定,对应配置文件是spring.mvc,如下所示:

@ConfigurationProperties(
    prefix = "spring.mvc"
)
public class WebMvcProperties 

又通过ServletRegistrationBean将dispatcherservlet注入进来,如下:

public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet> implements DispatcherServletPath {

public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

该注册dispatcherservlet的方法的第一个参数取出之前注入容器的dispatcherservlet,由于之前将这个dispatcherservlet与webmvcproperties进行绑定,然后就可以通过第一行代码,将获取webmvcproperties的path值与dispatcherservlet进行绑定。这个path默认为“/”,可以通过绑定的spring.mvc.servlet.path进行修改。

  • 为什么以上代码示例中存在tomcat-servlet和dispatcherservlet两个servlet时,执行的tomcat-servlet?
    原因:由于示例中的myServlet针对的路径是/my,而dispatcherservlet默认针对/,由于多个servlet存在时遵循精确优先原则,而myServlet比dispatcherservlet针对的路径更精确,所以执行了它。

二.嵌入式servlet容器

1. 切换嵌入式servlet容器

1.1 原理

  • web应用创建一个web的ioc容器ServletWebServerApplicationContext
  • 启动时从上到下依次执行以下三个方法,寻找ServletWebServerFactory(web服务器工厂–生产web服务器)
  • springboot底层默认有三种web服务器工厂:
    JettyServletWebServerFactory
    TomcatServletWebServerFactory
    UndertowServletWebServerFactory
protected void onRefresh() {
        super.onRefresh();

        try {
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }
    
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }

protected ServletWebServerFactory getWebServerFactory() {
        String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
        } else if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        } else {
            return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
        }
    }

  • 由上图第三个方法可知,目的是获取ServletWebServerFactory,即用来生产webserver。根据代码逻辑,只有当ioc容器存在一个ServletWebServerFactory类型的工厂才行。而由以下自动配置类的注解可知,Embeddedxx类型的三种webserver已经注入了容器中,深入Embeddedxx类型看到第二张图的@Bean就是证据,所以返回了容器中的TomcatServletWebServerFactory。
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {

在这里插入图片描述

  • 继续看以上代码createWebServer方法中调用的getWebServer方法,而这个方法属于TomcatServletWebServerFactory,以下代码具体实现可以看出,它创建tomcat服务的能力。
public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }

        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatWebServer(tomcat);
    }

  • 以上代码最终返回的是webServer(接口)的实现类tomcatwebserver,而tomcatwebserver的构造器如下代码,调用了初始化方法,进而通过初始化方法中的this.tomcat.start方法启动。以下第一张图说明了可切换web服务器。
    在这里插入图片描述
 public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        this.monitor = new Object();
        this.serviceConnectors = new HashMap();
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
        this.initialize();
    }

    private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
        synchronized(this.monitor) {
            try {
                this.addInstanceIdToEngineName();
                Context context = this.findContext();
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                        this.removeServiceConnectors();
                    }

                });
                this.tomcat.start();
                this.rethrowDeferredStartupExceptions();

                try {
                    ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
                } catch (NamingException var5) {
                }

                this.startDaemonAwaitThread();
            } catch (Exception var6) {
                this.stopSilently();
                this.destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", var6);
            }

        }
    }

1.2 切换嵌入式web服务器实操(tomcat切换为undertow)

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                 <exclusion>
                     <groupId>org.springframework.boot</groupId>
                     <artifactId>spring-boot-starter-tomcat</artifactId>
                 <exclusion>
             <exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
    </dependencies>

2. 定制servlet容器

2.1 修改配置文件

  • 由下图代码ServletWebServerFactoryAutoConfiguration的注解可以看到绑定了ServerProperties,而由ServerProperties的注解可知配置前缀是server,则可以通过修改配置server.xxx来进行定制。
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {....
}

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {...}

2.2 直接自定义ConfigurableServletWebServerFactory

  • 由于web服务器的生成主要用到了相应的工厂ServletWebServerFactory,而ConfigurableServletWebServerFactory是它的子接口,可以通过实现这个类型的方法,进行自定义参数设置。示例如下
@Bean
public ConfigurableServletWebServerFactory webServerFactory(){
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(xx);
factory.setSession(xx,TimeUnit.MINUTES);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,"/notfound.html"));
return factory;
}

2.3 实现WebServerFactoryCustomizer

  • 分析:由以下第二个方法可知,此接口是ServletWebServerFactoryCustomizer类型的要实现的,而通过ServletWebServerFactoryCustomizer的customize方法(自行源码查阅)和以下第一个方法可知,其作用是将配置文件的值与工厂ServletWebServerFactory进行绑定
    ServerProperties就是配置文件的类
@Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }
    
public class ServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {...}
  • 实现代码示例如下:
@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

三.定制化原理

1.常见方式

1.1 修改配置文件

  • 原理分析套路:
    1.场景starter
    2.xxxxAutoConfiguration
    3.导入xxx组件
    4.绑定xxxProperties
    5.绑定配置文件项

1.2 xxxxCustomizer(示例见上面2.3 实现WebServerFactoryCustomizer )

1.3 web应用实现webmvcconfigurer来定制web功能

  • 官方文档
  • 示例:先通过实现HandlerInterceptor接口来定制一个loginInterceptors,然后通过以下代码进行注册。
@Configuration
public class adminWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(new loginInterceptors())
                 .addPathPatterns("/**")
                 .excludePathPatterns("/","/login");
    }
}

1.4 @EnableWebMvc配合实现WebMvcConfigurer的配置类

  • 此组合会实现全面接管springmvc,则所有规则全部需要自己重新配置,原来的功能失效。
  • 原理
    1.原本依赖于WebMvcAutoConfiguration,使得默认的springmvc能自动配置许多web相关功能类,包括静态资源,欢迎页等
    2.一旦使用@EnableWebMvc,该注解添加了@Import({DelegatingWebMvcConfiguration.class})
    3.DelegatingWebMvcConfiguration添加了全部的WebMvcConfigurer配置,如下所示,说明凡是向容器中添加了实现WebMvcConfigurer接口的配置类都能生效。
public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

4.对于DelegatingWebMvcConfiguration的父类WebMvcConfigurationSupport,则是配置更底层的组件。包括RequestMappingHandlerMapping,及其依赖的组件(方法参数)都可以从容器中取。如下所示

 @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, 
    @Qualifier("mvcConversionService") FormattingConversionService conversionService, 
    @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {...}
   

5.由于WebMvcAutoConfiguration标注了如下注解,从而导致如果使用@EnableWebMvc会使这个自动配置类失效。

@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值