1.Web 原生组件注入(Servlet、Filter、Listener)
1.1 使用 Servlet API
- 在启动类加上【@ServletComponentScan】注解,指定原生 Servlet 扫描包的路径。
- 在原生 Servlet 类加上 【@WebServlet(urlPatterns = "/my")】注解
- 在原生 Filter 类加上注解 【@WebFilter(urlPatterns = {"/form/*","/images/*"})】
- 在原生 Listener 类加上注解 【@WebListener】
启动类源码:
@ServletComponentScan("com.cj.web")
@SpringBootApplication
public class Boot05Web01Application {
public static void main(String[] args) {
SpringApplication.run(Boot05Web01Application.class, args);
}
}
其他源码
@WebServlet(urlPatterns = "/my") //urlPatterns 定义映射路径
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("666");
}
}
@Slf4j
@WebFilter(urlPatterns = {"/form/*","/images/*"}) //注意元素使用 * ,SpringMVc使用 **
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("MyFilter doFilter");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyFilter 初始化");
}
@Override
public void destroy() {
log.info("MyFilter 销毁");
}
}
@Slf4j
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("MyListener 启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("MyListener 销毁");
}
}
1.2.使用 RegistrationBean
沿用 1.1节中的类,并去掉 注解
@Slf4j
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("MyListener 启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("MyListener 销毁");
}
}
@Slf4j
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("MyFilter doFilter");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyFilter 初始化");
}
@Override
public void destroy() {
log.info("MyFilter 销毁");
}
}
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("666");
}
}
然后使用配置类配置 相关 Bean
@Configuration(proxyBeanMethods = true)
public class RegistrationBeanConfig {
@Bean
ServletRegistrationBean myServlet() {
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet, "/my");
}
@Bean
FilterRegistrationBean myFilter() {
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter, myServlet()); //第一种方式
FilterRegistrationBean registrationBean = new FilterRegistrationBean(myFilter);
registrationBean.setUrlPatterns(Arrays.asList("/form/*","/images/*"));
return registrationBean;
}
@Bean
ServletListenerRegistrationBean myListener() {
return new ServletListenerRegistrationBean(new MyListener());
}
}
1.3 DispatcherServlet 如何注入
- SpringBoot 通过 DispatcherServletAutoConfiguration 自动配置了 DispatcherServlet 组件,DispatcherServlet 相关属性绑定到了 WebMvcProperties。对应的配置文件项以【spring.mvc】开头。
- 通过 ServletRegistrationBean<DispatcherServlet> 把 DispatcherServlet 配置进来。
- 默认的映射路径是【/】
Tomcat-Servlet:多个 Servlet 都能处理到同一请求时,遵循精确匹配原则。
由此原则可知,上面章节例子运行时会在Tomcat 容器中有 DispatcherServlet 和 MyServlet ,当发出 /my 请求时,Tomcat 会直接调用 MyServlet 中的方法,所以我们定义的 SpringMVC 拦截器没有执行。
2.嵌入式 Web 容器
SpringBoot 默认支持的 WebServer 有:Tomcat、Jetty、Undertow。
官方文档说明
- SpringBoot 应用启动发现当前是 Web 应用。Web 场景包导入 Tomcat。
- web 应用会创建一个 Web 版的 IOC 容器 ServletWebServerApplicationContext。
- ServletWebServerApplicationContext 会在启动的时候寻找 ServletWebServerFactory(Servlet 的 Web 服务器工厂)。
- SpringBoot 底层有很多的 WebServlet工厂:TomcatServletWebServerFactory、 JettyServletWebServerFactory、 UndertowServletWebServerFactory。
通过 SpringBoot 源码分析
- SpringBoot 底层会有一个自动配置类 ServletWebServerFactoryAutoConfiguration。配置类导入了 ServletWebServerFactoryConfiguration 。
- ServletWebServerFactoryConfiguration 配置类根据动态判断系统中到底导入了哪个 Web 服务器的包,而创建对于的 web 服务器工厂。
默认是导入 tomcat 包,所以会创建 TomcatServletWebServerFactory 。
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
...
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
@Bean
JettyServletWebServerFactory JettyServletWebServerFactory(
ObjectProvider<JettyServerCustomizer> serverCustomizers) {
...
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
...
}
...
}
}
- TomcatServletWebServerFactory 会创建 Tomcat 服务器(TomcatWebServer)。TomcatWebServer 的构造器函数会调用初始化方法 initialize() 该方法会启动 Tomcat,如下图。
内嵌服务器,就是手动调用启动服务器的代码(Tomcat 核心 jar 包存在),启动 Tomcat。
下图是系统中各个服务对应的类型:
切换服务器
<properties>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
3.定制化Servlet
定制方法:
1、 修改配置文件 server.xxx 【对应类型为 ServerProperties】
2、直接自定义 ConfigurableServletWebServerFactory (实现它)
3、实现 WebServerFactoryCustomizer<T extends WebServerFactory>。把配置文件的值和 ServletWebServerFactory 进行绑定。官方说明链接。
在 SpringBoot 中 xxxxCustomizer 就是是定制化器,可以改变 xxxx 默认规则
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000); //修改 tomcat 服务端口
}
}
4.Web开发总结
4.1 常见的定制化方式
- @Bean 替换、增加容器中默认组件、视图解析器。
- 修改配置文件
- xxxxCustomizer
- 编写自定义的配置类 xxxConfiguration
- web 应用编写一个配置类实现 WebMvcConfigurer 即可定制化 web 功能
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
}
- @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管 SpringMVC,所有规则全部自己重新设置;实现定制和扩展功能。
@EnableWebMvc
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
}
@EnableWebMvc 全面接管后
- 静态资源、视图解析器、欢迎页、......,将会全部失效
4.2 使用 @EnableWebMvc 所有规则全部自己重新设置的原因
- WebMvcAutoConfiguration 是默认的 SpringMVC 的自动配置功能类。
- WebMvcAutoConfiguration 里面的配置要能生效,则必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
- 我们一旦使用了 @EnableWebMvc 注解后会自动导入 DelegatingWebMvcConfiguration 类,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
- 而 DelegatingWebMvcConfiguration 是 WebMvcConfigurationSupport 的子类,所以使用 @EnableWebMvc 注解后,WebMvcAutoConfiguration 就不会生效,很多的默认的 SpringMVC 组件都不能生效,需要自行定制。
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}
- 而且 DelegatingWebMvcConfiguration 的作用是指保证 SpringMVC 的最基本使用
DelegatingWebMvcConfiguration 的功能如下:
- 把系统中所有定制的 WebMvcConfigurer 合并起来一起生效。
- 自动配置了一些非常底层的组件,如 RequestMappingHandlerMapping ,这些组件依赖的组件都是从容器中获取的。
4.3 原理分析套路
场景starter - xxxxAutoConfiguration - 导入 xxx 组件 - 绑定 xxxProperties - 绑定配置文件项