SpringBootWeb开发-7
上一级页面:ssm-spring-boot速成学习索引
前言
56、原生组件注入-原生注解与Spring方式注入
官方文档 - Servlets, Filters, and listeners
使用原生的注解
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("66666");
    }
}@Slf4j
@WebFilter(urlPatterns={"/css/*","/images/*"}) //my
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter初始化完成");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("MyFilter工作");
        chain.doFilter(request,response);
    }
    @Override
    public void destroy() {
        log.info("MyFilter销毁");
    }
}@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MySwervletContextListener监听到项目初始化完成");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MySwervletContextListener监听到项目销毁");
    }
}最后还要在主启动类添加注解@ServletComponentScan
@ServletComponentScan(basePackages = "com.lun")//
@SpringBootApplication(exclude = RedisAutoConfiguration.class)
public class Boot05WebAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(Boot05WebAdminApplication.class, args);
    }
}Spring方式注入
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
@Configuration(proxyBeanMethods = true)
public class MyRegistConfig {
    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();
        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }
    @Bean
    public FilterRegistrationBean myFilter(){
        MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }
    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}57、原生组件注入-【源码分析】DispatcherServlet注入原理
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration配置类
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
	/*
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
	/*
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {
        //创建DispatcherServlet类的Bean
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		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;
		}
		@Bean
		@ConditionalOnBean(MultipartResolver.class)
		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}
	}
    
    @Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {
        //注册DispatcherServlet类
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}
	}
    
    ...
    
}DispatcherServlet默认映射的是 / 路径,可以通过在配置文件修改spring.mvc.servlet.path=/mvc。
58、嵌入式Servlet容器-【源码分析】切换web服务器与定制化
默认支持的WebServer
Tomcat,Jetty, orUndertow。ServletWebServerApplicationContext容器启动寻找ServletWebServerFactory并引导创建服务器。
原理
- SpringBoot应用启动发现当前是Web应用,web场景包-导入tomcat。
 - web应用会创建一个web版的IOC容器 
ServletWebServerApplicationContext。 ServletWebServerApplicationContext启动的时候寻找ServletWebServerFactory(Servlet 的web服务器工厂——>Servlet 的web服务器)。- SpringBoot底层默认有很多的WebServer工厂(
ServletWebServerFactoryConfiguration内创建Bean),如:TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory
 - 底层直接会有一个自动配置类
ServletWebServerFactoryAutoConfiguration。 ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)。ServletWebServerFactoryConfiguration根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有TomcatServletWebServerFactoryTomcatServletWebServerFactory创建出Tomcat服务器并启动;TomcatWebServer的构造器拥有初始化方法initialize——this.tomcat.start();- 内嵌服务器,与以前手动把启动服务器相比,改成现在使用代码启动(tomcat核心jar包存在)。
 
Spring Boot默认使用Tomcat服务器,若需更改其他服务器,则修改工程pom.xml:
<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-jetty</artifactId>
</dependency>定制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> {
    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }
}59、定制化原理-SpringBoot定制化组件的几种方式(小结)
定制化的常见方式
修改配置文件
xxxxxCustomizer编写自定义的配置类
xxxConfiguration+@Bean替换、增加容器中默认组件,视图解析器Web应用 编写一个配置类实现
WebMvcConfigurer即可定制化web功能 +@Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{
}@EnableWebMvc+WebMvcConfigurer—@Bean可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能(高级功能,初学者退避三舍)。- 原理: 
WebMvcAutoConfiguration默认的SpringMVC的自动配置功能类,如静态资源、欢迎页等。- 一旦使用 
@EnableWebMvc,会@Import(DelegatingWebMvcConfiguration.class)。 DelegatingWebMvcConfiguration的作用,只保证SpringMVC最基本的使用- 把所有系统中的
WebMvcConfigurer拿过来,所有功能的定制都是这些WebMvcConfigurer合起来一起生效。 - 自动配置了一些非常底层的组件,如
RequestMappingHandlerMapping,这些组件依赖的组件都是从容器中获取如。 public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport。
- 把所有系统中的
 WebMvcAutoConfiguration里面的配置要能生效必须@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)。- @EnableWebMvc 导致了WebMvcAutoConfiguration 没有生效。
 
 
- 原理: 
 
原理分析套路
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties - 绑定配置文件项。
60、数据访问-数据库场景的自动配置分析与整合测试
导入JDBC场景
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>接着导入数据库驱动包(MySQL为例)。
<!--默认版本:-->
<mysql.version>8.0.22</mysql.version>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--<version>5.1.49</version>-->
</dependency>
<!--
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
-->
<properties>
    <java.version>1.8</java.version>
    <mysql.version>5.1.49</mysql.version>
</properties>相关数据源配置类
DataSourceAutoConfiguration: 数据源的自动配置。- 修改数据源相关的配置:
spring.datasource。 - 数据库连接池的配置,是自己容器中没有DataSource才自动配置的。
 - 底层配置好的连接池是:
HikariDataSource。 
- 修改数据源相关的配置:
 DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置。JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,可以来对数据库进行CRUD。- 可以修改前缀为
spring.jdbc的配置项来修改JdbcTemplate。 @Bean @Primary JdbcTemplate:Spring容器中有这个JdbcTemplate组件,使用@Autowired。
- 可以修改前缀为
 JndiDataSourceAutoConfiguration: JNDI的自动配置。XADataSourceAutoConfiguration: 分布式事务相关的。
修改配置项
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver单元测试数据源
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
@SpringBootTest
class Boot05WebAdminApplicationTests {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Test//用@org.junit.Test会报空指针异常,可能跟JUnit新版本有关
    void contextLoads() {
//        jdbcTemplate.queryForObject("select * from account_tbl")
//        jdbcTemplate.queryForList("select * from account_tbl",)
        Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);
        log.info("记录总数:{}",aLong);
    }
}