一、Web原生组件注入
如何向SpringBoot中注入Web的原生组件?
1、使用Servlet API
(1)Servlet原生组件
- 创建一个Servlet类,让它继承原生的Servlet的实现类 HttpServlet ,使用WebServlet注解指定我们的请求,效果直接响应没有经过Spring的拦截器【@WebServlet 是servlet3.0 提供的注解】
- 我们需要在主程序类中,使用@ServletComponentScan 指明我们原生的Servlet组件都放在哪里
package com.atguigu.admin.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author Bonbons* @version 1.0*/
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("666");}
}
(2)Filter 拦截器
- 与使用原生Servlet方式相同,包扫描不用重新设置,只需要配置我们的Filter类
- 让其实现Filter接口,进而实现它的三个方法
- 注意:单星是Servlet的写法,双星是Spring的写法
package com.atguigu.admin.servlet;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;/*** @author Bonbons* @version 1.0*/
@Slf4j
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MyFilter初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("MyFilter工作");//myFilter要想放行,就要拿到Filter的链filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {log.info("MyFilter销毁");}
}
(3)Listener监听器
- 创建MyServletContextListener类实现ServletContextListener接口
- 实现它初始化和销毁方法
package com.atguigu.admin.servlet;import lombok.extern.slf4j.Slf4j;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;/*** @author Bonbons* @version 1.0*/
@WebListener
@Slf4j
public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {log.info("MyServletContextListener 监听到项目初始化完成");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {log.info("MyServletContextListener 监听到项目销毁");}
}
2、使用RegistrationBean
-
第二种方式,使用Spring底层给我们提供的三种RegistrationBean (Servlet、Filter、Listener)
-
我们需要知道,这个是基于上面我们创建的三个文件 MyServlet、MyFilter、MyServletContextListener 【去掉其中的@WebServlet、@WebFilter、@WebServletContextListener】
-
定义一个配置类,声明我们对应的三种Registration的Bean
package com.atguigu.admin.servlet;import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Arrays;/*** @author Bonbons* @version 1.0*/
@Configuration
public class MyRegistConfig {@Beanpublic ServletRegistrationBean myServlet (){//创建Servlet对象MyServlet myServlet = new MyServlet();return new ServletRegistrationBean(myServlet, "/my", "/my02");}@Beanpublic FilterRegistrationBean myFilter(){MyFilter myFilter = new MyFilter();//我们myServlet作为urlMappings的参数,就是将其路径作为我们拦截器的路径//return new FilterRegistrationBean(myFilter, myServlet());//第二种方式,我们自己重新写请求路径FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*", "/my"));return filterRegistrationBean;}@Beanpublic ServletListenerRegistrationBean myListener(){MyServletContextListener myServletContextListener = new MyServletContextListener();return new ServletListenerRegistrationBean(myServletContextListener);}
}
- 两个小细节:
- 单实例,没有将 @Configuration的 proxyBeanMethods 设置为 false 【调用一次创建一个Bean对象】
- 为什么我们的Servlet没有被拦截器拦截?
- 原生Servlet和springMvC的dispatcherServlet派发器的处理方式不一样 (url=“/”)
- 原生Servlet没有走Spring的流程,不能被拦截器拦截
- DispatcherServlet 如何注册进来的呢?
- 容器中自动配置了 DispatcherServlet 属性绑定到WebMvcProperties,对应的配置文件配置项是 spring.mvc
- 通过
ServletRegistrationBean<DispatcherServlet>
把DispatcherServlet配置进来 - 默认映射路径是 / 路径
- 当然我们可以在SpringBoot的配置文件中进行修改
spring.mvc.servlet.path=/mvc/
- 当然我们可以在SpringBoot的配置文件中进行修改
- 当多个Servlet都能处理到同一层路径,精确优选原则
二、嵌入式Web容器
1、切换嵌入式 Servlet 容器
- 默认支持的 webServlet
- Tomcat、Jetty、Undertow
- ServletWebServerApplicationContext 容器启动寻找 ServletWebServerFactory 并引导创建服务器
- 如何切换服务器呢?
<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>
- 整个流程的原理:
- SpringBoot应用启动发现当前是Web服务器,就会加载Web场景包,附带导入tomcat
- web 应用会创建一个web版本的 ioc 容器(ServletWebServerApplicationContext)
- ServletWebServerApplicationContext 启动的时候会寻找 web的服务器工厂(ServletWebServerFactory)【用于生产servlet的web服务器】
- SpringBoot 底层默认有很多的WebServer工厂
- TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory
- 底层直接会由一个自动配置类——ServletWebServerFactoryAutoConfiguration
- ServletWebServerFactoryAutoConfiguration 导入了 ServletWebServerFactoryConfiguration(配置类)
- ServletWebServerFactoryConfiguration 配置类,根据动态判断系统重到底导入了哪个web服务器的包【默认web-start导入tomcat包】,容器中就有了 TomcatServletWebServerFactory
- TomcatServletWebServerFactory 创建出Tomcat服务器并启动,TomcatWebServer 的构造器初始化方法
initialize --- this.tomcat.start()
- 内嵌服务器,就是手动把启动服务器的代码调用 【tomcat核心jar包存在】
2、定制Servlet容器
- 实现
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
- 把配置文件的值和 ServletWebServerFactory 进行绑定
- 修改配置文件 server.xxx
- 直接自定义 ConfigurableServletWebServerFactory
- xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {@Overridepublic void customize(ConfigurableServletWebServerFactory server) {server.setPort(9000);}}
三、定制化原理
1、定制化的常见方式
- 修改配置文件;
- xxxxxCustomizer;
- 编写自定义的配置类 xxxConfiguration;+ @Bean替换、增加容器中默认组件;视图解析器
- Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer - @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
- 原理
- 1、WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…
- 2、一旦使用 @EnableWebMvc 、。会 @Import(DelegatingWebMvcConfiguration.class)
- 3、DelegatingWebMvcConfiguration 的 作用,只保证SpringMVC最基本的使用
- 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
- 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
- public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
- 4、WebMvcAutoConfiguration 里面的配置要能生效 必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
- 5、@EnableWebMvc 导致了 WebMvcAutoCo
2、原理分析套路
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项