2020-11-27springboot中Servlet容器运用

一、定制和修改Servlet容器的相关配置

1、修改和server有关的配置
ServerProperties;

server.port=8081
server.context-path=/crud

server.tomcat.uri-encoding=UTF-8

//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx

2、编写一个EmbeddedServletContainerCustomizer的Servlet容器的定制器,并将这个定制器加入到容器中,来修改Servlet容器的配置

@Configuration
public class MyServletContainerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        factory.setPort(8082);
    }
    
  @Bean
    public WebServerFactoryCustomizer webServerFactoryCustomizer(){
        return new MyServletContainerFactoryCustomizer();
    }

注意:当两者同时存在时,自定义定制器的优先级高于配置文件,而且会与配置文件形成互补配置。例如:1+2两个步骤最后的记过就是

localhost:8082/crud

二、Servlet三大组件

注册三大组件:ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBean

1、Servlet
(1)自定义servlet

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.getWriter().write("hello");
    }
}

(2)通过ServletRegistrationBean注入容器

@Bean
    public ServletRegistrationBean servletRegistrationBean() {
        return new ServletRegistrationBean(new MyServlet(), "/520");
    }

2、Filter
(1)自定义filter

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截了.....");
        filterChain.doFilter(servletRequest,servletResponse);
    }

(2)通过FilterRegistrationBean注入容器

 @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean<MyFilter> myFilterFilterRegistrationBean = new FilterRegistrationBean<>();
        myFilterFilterRegistrationBean.setFilter(new MyFilter());
        myFilterFilterRegistrationBean.addUrlPatterns("/666");
        return myFilterFilterRegistrationBean;
    }

3、listener
(1)自定义listener

public class MyListenner implements ServletContextListener {
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("毁灭了");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("产生了");
    }
}

(2)通过ServletListenerRegistrationBean注入容器

  @Bean
    public ServletListenerRegistrationBean registrationBean() {
        ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
        servletListenerRegistrationBean.setListener(new MyListenner());
        return servletListenerRegistrationBean;
    }

三、使用外置的Servlet容器

1、嵌入式和外置Servlet比较
(1)、嵌入式Servlet容器:应用打成可执行的jar。简单,不用配置就可以直接使用。
缺点:
         默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂【EmbeddedServletContainerFactory】);
(2、)外置的Servlet容器:外面安装Tomcat—应用war包的方式打包,可以使用jsp
步骤:
(a)、必须创建一个war项目
在这里插入图片描述
(b)、将嵌入式的Tomcat指定为provided
即只在编译期使用嵌入式tomcat

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>

(c)、编写一个SpringBootServletInitializer的子类,并调用configure方法(必须)

public class ServletInitializer extends SpringBootServletInitializer {

   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       //传入SpringBoot应用的主程序
      return application.sources(SpringBoot04WebJspApplication.class);
   }

}

在这里插入图片描述
2、原理

jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器;

war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器;

(1)根据servlet3.0文档:

(a)、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:
(b)、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
(c)、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

(2)启动流程
(a)启动Tomcat
(b)寻找META-INF\services中的ServletContainerInitializer,创建当前web应用里面每一个jar包里面ServletContainerInitializer实例
在这里插入图片描述
在这里插入图片描述
(c)由于使用了@HandlesTypes({WebApplicationInitializer.class})注解,所以将会同时将WebApplicationInitializer.class传入onstartup方法来创建WebApplicationInitializer的实例。在这个方法的最后,会循环遍历WebApplicationInitializer 实例,并调用其initializer.onStartup(servletContext);方法。

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }
	传入WebApplicationInitializer
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = Collections.emptyList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            initializers = new ArrayList(webAppInitializerClasses.size());
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        ((List)initializers).add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (((List)initializers).isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort((List)initializers);
            var4 = ((List)initializers).iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

        }
    }

(d)WebApplicationInitializer是一个接口,调用其方法其实就是调用实现类的方法。

在这里插入图片描述
(e)即SpringBootServletInitializer的类会被创建对象,并执行重写后的onStartup方法。而WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);就创建了Servlet容器。

  public void onStartup(ServletContext servletContext) throws ServletException {
        this.logger = LogFactory.getLog(this.getClass());
        WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);
        if (rootApplicationContext != null) {
            servletContext.addListener(new SpringBootServletInitializer.SpringBootContextLoaderListener(rootApplicationContext, servletContext));
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }

    }

(f)createRootApplicationContext中首先创建了SpringApplicationBuilder构建器,一直执行到builder = configure(builder);时,由于SpringBootServletInitializer子类重写了configure方法,此处会调用重写后的方法, 并返回application.sources(SpringBoot04WebJspApplication.class);将SpringBoot的主程序类传入了进来,并通过SpringApplication application = builder.build();创建了SpringApplication 主程序。最后进入run方法创建IOC容器。【ioc创建流程详见:https://blog.csdn.net/TMHJHZTMGB/article/details/110262738

protected WebApplicationContext createRootApplicationContext(
      ServletContext servletContext) {
    //1、创建SpringApplicationBuilder
   SpringApplicationBuilder builder = createSpringApplicationBuilder();
   StandardServletEnvironment environment = new StandardServletEnvironment();
   environment.initPropertySources(servletContext, null);
   builder.environment(environment);
   builder.main(getClass());
   ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
   if (parent != null) {
      this.logger.info("Root context already created (using as parent).");
      servletContext.setAttribute(
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
      builder.initializers(new ParentContextApplicationContextInitializer(parent));
   }
   builder.initializers(
         new ServletContextApplicationContextInitializer(servletContext));
   builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
    
    //调用configure方法,子类重写了这个方法,
   builder = configure(builder);
    
    //使用builder创建一个Spring应用
   SpringApplication application = builder.build();
   if (application.getSources().isEmpty() && AnnotationUtils
         .findAnnotation(getClass(), Configuration.class) != null) {
      application.getSources().add(getClass());
   }
   Assert.state(!application.getSources().isEmpty(),
         "No SpringApplication sources have been defined. Either override the "
               + "configure method or add an @Configuration annotation");
   // Ensure error pages are registered
   if (this.registerErrorPageFilter) {
      application.getSources().add(ErrorPageFilterConfiguration.class);
   }
    //启动Spring应用
   return run(application);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值