编辑
2020-07-03
编程
00

目录

1.我们印象中的 SpringBoot 做了啥?
2.配置文件都干了什么?
2.1 spring 相关的 xml
2.1.1 applicationContext.xml
2.1.2 springMVC.xml
2.2 web.xml
2.2.1 ContextLoaderListener
2.2.2 DispatcherServlet
2.3 基本流程
3 实现零配置
3.1 spring mvc官方文档 example
3.2 如何注册 DispatcherServlet
3.2.1 怎么注册一个 servlet
3.2 零配置代码说明
3.3 为什么会调到 onStartup
3.3.1 servlet 3.0 - javax.servlet.ServletContainerInitializer
3.3.2 org.springframework.web.SpringServletContainerInitializer
3.3.3 onStartup 的入参 - webAppInitializerClasses
3.4.1 依赖
3.4.2 main

1.我们印象中的 SpringBoot 做了啥?

  • mvc 功能
  • 零配置
    • 没有 spring 相关的 xml
    • 没有 web.xml
  • 内嵌容器(tomcat jetty)
  • 自动配置

2.配置文件都干了什么?

2.1 spring 相关的 xml

2.1.1 applicationContext.xml

xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <span style="color:#ff0000;"><!-- 自动扫描web包 ,将带有注解的类 纳入spring容器管理 --></span> <context:component-scan base-package="com.eduoinfo.finances.bank.web"></context:component-scan> <!-- 引入jdbc配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath*:jdbc.properties</value> </list> </property> </bean> <!-- dataSource 配置 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="20" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="SELECT 'x'" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="false" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 配置监控统计拦截的filters --> <property name="filters" value="stat" /> </bean> <!-- mybatis文件配置,扫描所有mapper文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource" p:configLocation="classpath:mybatis-config.xml" p:mapperLocations="classpath:com/eduoinfo/finances/bank/web/dao/*.xml" /> <span style="color:#ff0000;"><!-- spring与mybatis整合配置,扫描所有dao --></span> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.eduoinfo.finances.bank.web.dao" p:sqlSessionFactoryBeanName="sqlSessionFactory" /> <!-- 对dataSource 数据源进行事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> <span style="color:#ff0000;"><!-- 配置使Spring采用CGLIB代理 --></span> <aop:aspectj-autoproxy proxy-target-class="true" /> <span style="color:#ff0000;"> <!-- 启用对事务注解的支持 --></span> <tx:annotation-driven transaction-manager="transactionManager" /> <span style="color:#ff0000;"> <!-- Cache配置 --></span> <cache:annotation-driven cache-manager="cacheManager" /> <bean id="ehCacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml" /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehCacheManagerFactory" /> </beans>
  • 扫描业务 bean
  • 配置 bean

2.1.2 springMVC.xml

xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd"> <!-- 使用注解开发,不用配置controller,需要配置一个组件扫描器 --> <context:component-scan base-package="com.edu.test.controller"/> <!--避免IE执行AJAX时,返回JSON出现下载文件 --> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置从项目根目录到指定目录一端路径 ,建议指定浅一点的目录--> <property name="prefix" value="/WEB-INF/jsp/"></property> <!-- 文件的后缀名 --> <property name="suffix" value=".jsp"></property> </bean> </beans>
  • 扫描 controller
  • Jackson2HttpMessageConverter(json 解析器,非必须的)
  • 2.1.2.3 InternalResourceViewResolver(视图解析器)

2.2 web.xml

xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " version="2.5"> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>webAppRootKey</param-name> <param-value>spring4.root</param-value> </context-param> <!-- ================Spring配置开始================ --> <!-- 设置Spring容器加载所有的配置文件的路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:Spring/applicationContext.xml,classpath:Spring/spring-mybatis.xml</param-value> </context-param> <!-- ================Spring配置结束================ --> <!-- =========================================== filter =========================================== --> <!-- Spring 字符编码配置 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!--<description>字符集过滤器</description>--> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <!--<description>字符集编码</description>--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <!-- =========================================== listener =========================================== --> <!-- 配置Spring监听器,可以在容器启动时,加载contextConfigLocation的context-param节点的配置文件 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 监听HTTP请求事件,为Bean的request,session,globalsession等作用域提供支持 --> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> <!-- =========================================== servlet =========================================== --> <!-- ================配置SpringMVC核心调度器================ --> <!-- 不指定具体文件的话,默认为"/WEB-INF/<servlet name>-servlet.xml" --> <!-- load-on-startup代表启动顺序,设置为大于等于0表示容器在应用启动时就加载并初始化这个servlet --> <!-- 推荐拦截/,风格优雅 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:Spring/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- =========================================== servlet-mapping =========================================== --> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- 设置session过期时间为60分钟 --> <session-config> <session-timeout>60</session-timeout> </session-config> <!-- 欢迎页面 --> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> </web-app>

2.2.1 ContextLoaderListener

对于一个 spring 项目,在项目启动的时候需要初始化 spring 的环境,说白了就是执行下面的代码

java
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);

如果是在非 web 项目中,我们可以在 main 方法中,执行上述的代码; 但是在 web 项目中,由于项目的入口在容器( tomcat ) 中,而 tomcat 启动回去解析 web.xml ,于是 web.xml 中配置一个 ContextLoaderListener, 让容器在启动的时候去初始化 spring 环境

2.2.2 DispatcherServlet

- 拦截请求 - 负责解析 spring-mvc.xml - <load-on-startup>1</load-on-startup> 让容器启动的时候调用该 servlet 的 init 方法

2.3 基本流程

容器( tomcat )启动 -> 加载 web.xml -> 调用 ContextLoaderListener 去 init spring (spring.xml) -> DispatcherServlet 加载 springMVC.xml

3 实现零配置

在 springBoot 之前,其实就已经可以实现无 xml 了 基于 xml 配置的是原 spring 2 和之前版本的写法。在 spring4 之后,已经提供了无 xml 写法。

3.1 spring mvc官方文档 example

SpringWebMVC 官方文档链接 The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config):

java
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); ac.refresh(); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
java
@Configuration @ComponentScan(basePackages = "com.yui.study") public class AppConfig { /* @Bean public MyService myService() { return new MyServiceImpl(); }*/ }

3.2 如何注册 DispatcherServlet

3.2.1 怎么注册一个 servlet

  • web.xml 中配置
  • @WebServlet
  • servletCxt.addServlet

3.2 零配置代码说明

  • ContextLoaderListener: 初始化 spring 环境
java
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
  • spring.xml 和 springMVC.xml :配置和扫描 bean
java
ac.register(AppConfig.class);
java
@Configuration @ComponentScan(basePackages = "com.yui.study") // 扫描注解 public class AppConfig { // 配置 bean @Bean public MyService myService() { return new MyServiceImpl(); } }
  • DispatcherServlet 实现拦截
java
// 声明 DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); // 向容器注册 DispatcherServlet ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); // 设置容器启动时加载该 servlet registration.setLoadOnStartup(1); // 拦截请求设置 registration.addMapping("/app/*");

3.3 为什么会调到 onStartup

3.3.1 servlet 3.0 - javax.servlet.ServletContainerInitializer

在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。

每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作

3.3.2 org.springframework.web.SpringServletContainerInitializer

java
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { /** 1. webAppInitializerClasses 所有实现 WebApplicationInitializer 的接口, {@link javax.servlet.annotation.HandlesTypes} 注解表示要拦截个规则 2. servletContext servlet初始化上下文 */ // WebApplicationInitializer 实例化 list, 由 Class -> Object List<WebApplicationInitializer> initializers = new LinkedList<>(); if (webAppInitializerClasses != null) { // 从 webAppInitializerClasses 中获取 WebApplicationInitializer 类型的类 for (Class<?> waiClass : webAppInitializerClasses) { /* 如果 {waiClass 不是一个接口} 且 {waiClass 不是抽象的} 且 {waiClass 必须是实现 WebApplicationInitializer.class} WebApplicationInitializer.class.isAssignableFrom(waiClass) 这句是为了防止容器( tomcat 等)实现有问题,传递了非指定类型过来 */ if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { // 实例化 waiClass 并添加到 initializers list 中 initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } // 如果 initializers 为空 输出日志,并结束初始化 if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); /** 对 initializers 进行排序,从小到大排 @see org.springframework.core.OrderComparator#doCompare 继承 Ordered 或者 PriorityOrdered 重写 getOrder 进行排序 排序规则: PriorityOrdered 在 Ordered 前面,同个接口实现,getOrder() 的值越小越排在前面 */ AnnotationAwareOrderComparator.sort(initializers); // 调用所有 initializer 的 onStartUp() 方法, 并将 servlet 上下文传递过去 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }

3.3.3 onStartup 的入参 - webAppInitializerClasses

  • Set<Class<?>> webAppInitializerClasses
java
所有当前 class 的类注解 @HandlesTypes 的 value 的实现类

3.4 内嵌容器

3.4.1 依赖

gradle
compile("org.apache.tomcat.embed:tomcat-embed-core:8.5.33")

3.4.2 main

public class MySpringBootApplication { public static void main(String[] args) throws Exception{ Tomcat myTomcat = new Tomcat(); myTomcat.setPort(8090); myTomcat.addWebapp("/","D:\\data\\web"); myTomcat.start(); myTomcat.getServer().await(); /*// Load Spring web application configuration myTomcat.addContext("/","D:\\data\\web"); AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); // ac.refresh(); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); Wrapper mvc = myTomcat.addServlet("/","app", servlet); mvc.setLoadOnStartup(1); mvc.addMapping("/*"); myTomcat.start(); myTomcat.getServer().await();*/ } }

本文作者:Yui_HTT

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!