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),如:TomcatServletWebServerFactory
JettyServletWebServerFactory
UndertowServletWebServerFactory
- 底层直接会有一个自动配置类
ServletWebServerFactoryAutoConfiguration
。 ServletWebServerFactoryAutoConfiguration
导入了ServletWebServerFactoryConfiguration
(配置类)。ServletWebServerFactoryConfiguration
根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有TomcatServletWebServerFactory
TomcatServletWebServerFactory
创建出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的默认规则
@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
单元测试数据源
@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);
}
}
61、数据访问-自定义方式整合druid数据源
Druid是什么?
它是数据库连接池,它能够提供强大的监控和扩展功能。
Spring Boot整合第三方技术的两种方式:
自定义
找starter场景
自定义方式
添加依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
配置Druid数据源:
@Configuration
public class MyConfig {
@Bean
@ConfigurationProperties("spring.datasource")//复用配置文件的数据源配置
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
// druidDataSource.setUrl();
// druidDataSource.setUsername();
// druidDataSource.setPassword();
return druidDataSource;
}
}
配置Druid的监控页功能:
Druid内置提供了一个
StatViewServlet
用于展示Druid的统计信息。官方文档 - 配置_StatViewServlet配置。这个StatViewServlet
的用途包括:- 提供监控信息展示的html页面
- 提供监控信息的JSON API
Druid内置提供一个
StatFilter
,用于统计监控信息。官方文档 - 配置_StatFilterWebStatFilter
用于采集web-jdbc关联监控的数据,如SQL监控、URI监控。官方文档 - 配置_配置WebStatFilter- Druid提供了
WallFilter
,它是基于SQL语义分析来实现防御SQL注入攻击的。官方文档 - 配置 wallfilter
@Configuration
public class MyConfig {
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
//加入监控和防火墙功能功能
druidDataSource.setFilters("stat,wall");
return druidDataSource;
}
/**
* 配置 druid的监控页功能
* @return
*/
@Bean
public ServletRegistrationBean statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean =
new ServletRegistrationBean<>(statViewServlet, "/druid/*");
//监控页账号密码:
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123456");
return registrationBean;
}
/**
* WebStatFilter 用于采集web-jdbc关联监控的数据。
*/
@Bean
public FilterRegistrationBean webStatFilter(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}