对于springboot来说,还有两块是比较有意思的,第一就是发现他内置了tomcat,接下来一快就是他对springmvc进行了无缝整合
1. 内嵌tomcat首先来看下最简单的tomcat集成。
pom新建立一个servlet4.0.0 hgy embed-tomcat1.0-SNAPSHOT org.apache.tomcat.embed tomcat-embed-core8.5.11 org.apache.tomcat tomcat-jasper8.5.11
package len.hgy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("this is index... tomcat"); } }tomcat 整合
package len.hgy; import len.hgy.servlet.IndexServlet; import org.apache.catalina.LifecycleException; import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.Tomcat; public class MyTomcat { private static final int PORT = 8080; private static final String CONTEX_PATH = "/hgy"; private static final String SERVLET_NAME = "indexServlet"; public static void main(String[] args) throws LifecycleException, InterruptedException { // 创建tomcat服务器 Tomcat tomcatServer = new Tomcat(); // 指定端口号 tomcatServer.setPort(PORT); // 是否设置自动部署 tomcatServer.getHost().setAutoDeploy(false); // 创建上下文 StandardContext standardContext = new StandardContext(); standardContext.setPath(CONTEX_PATH); // 监听上下文 standardContext.addLifecycleListener(new Tomcat.FixContextListener()); // tomcat容器添加standardContext tomcatServer.getHost().addChild(standardContext); // 创建Servlet tomcatServer.addServlet(CONTEX_PATH, SERVLET_NAME, new IndexServlet()); // servleturl映射 standardContext.addServletMappingDecoded("/index", SERVLET_NAME); tomcatServer.start(); System.out.println("tomcat服务器启动成功.."); // 异步进行接收请求 tomcatServer.getServer().await(); } }
运行main方法,在浏览器输入:
http://localhost:8080/hgy/index
注意: 9.xx版本的并不能直接访问
2. SpringMVC 整合 新增spring依赖UserServiceorg.springframework spring-web5.0.4.RELEASE compile org.springframework spring-webmvc5.0.4.RELEASE compile
package len.hgy.service; import org.springframework.stereotype.Service; @Service public class UserService { public String index() { return "springboot 2.0 我正在加载UserService"; } }RestController
package len.hgy.controller; import len.hgy.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexController { @Autowired private UserService userService; @RequestMapping(value = "/index", produces = "text/html;charset=UTF-8") public String index() { return userService.index(); } }DispatcherServlet配置类
虽然前面有了service,有了controller,但依然没有把这些组建交给spring,对于springmvc来说,有个DispatcherServlet,这是springmvc的前端控制器,以前是配置在web.xml中。只是现在用的是注解。
package len.hgy.conf; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("len.hgy") public class RootConfig { }
package len.hgy.conf; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc @ComponentScan(basePackages = {"len.hgy.controller"}) //public class WebConfig extends WebMvcConfigurerAdapter { public class WebConfig implements WebMvcConfigurer { }
package len.hgy.conf; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; @Configuration public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { // 加载根配置信息 spring核心 protected Class>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } // springmvc 加载 配置信息 protected Class>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } // springmvc 拦截url映射 拦截所有请求 protected String[] getServletMappings() { return new String[]{"/"}; } }Tomcat集成
TomcatApp
package len.hgy; import java.io.File; import javax.servlet.ServletException; import org.apache.catalina.LifecycleException; import org.apache.catalina.WebResourceRoot; import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.webresources.DirResourceSet; import org.apache.catalina.webresources.StandardRoot; public class TomcatApp { public static void main(String[] args) throws ServletException, LifecycleException { start(); } public static void start() throws ServletException, LifecycleException { // 创建Tomcat容器 Tomcat tomcatServer = new Tomcat(); // 端口号设置 tomcatServer.setPort(9090); // 读取项目路径 加载静态资源 StandardContext ctx = (StandardContext) tomcatServer.addWebapp("/", new File("src/main").getAbsolutePath()); // 禁止重新载入 ctx.setReloadable(false); // class文件读取地址 File additionWebInfClasses = new File("target/classes"); // 创建WebRoot WebResourceRoot resources = new StandardRoot(ctx); // tomcat内部读取Class执行 resources.addPreResources( new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), "/")); tomcatServer.start(); // 异步等待请求执行 tomcatServer.getServer().await(); } }
启动,在地址栏输入:
http://localhost:9090/index
JSP支持要支持JSP,回忆学习springmvc中的内容,需要用到一个试图解析器
修改WebConfig
@Configuration @EnableWebMvc @ComponentScan(basePackages = {"len.hgy.controller"}) //public class WebConfig extends WebMvcConfigurerAdapter { public class WebConfig implements WebMvcConfigurer { // 创建SpringMVC视图解析器 @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); // 可以在JSP页面中通过${}访问beans viewResolver.setExposeContextBeansAsAttributes(true); registry.viewResolver(viewResolver); } }
新增controller
@Controller public class UserController { @RequestMapping("/pageIndex") public String pageIndex() { return "pageIndex"; } }
增加JSP
在resources里面新增WEB-INFviewspageIndex.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>jsp page 这是个jsp页面
重启tomcat ,访问 http://localhost:9090/pageIndex
3. SpringBootTomcat 加载流程 tomcat如何启动的?org.springframework.boot.autoconfigure.EnableAutoConfiguration= ... org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration, ...
ServletWebServerFactoryAutoConfiguration 这类里面有个TomcatServletWebServerFactoryCustomizer这个类实现了WebServerFactoryCustomizer
@Configuration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration {
EmbeddedTomcat->TomcatServletWebServerFactory->TomcatServletWebServerFactory.getWebServer()->getTomcatWebServer->TomcatWebServer->启动tomcat
// Start the server to trigger initialization listeners
this.tomcat.start();
在注入bean时候已经在构造器里面启动了tomcat
getWebServer谁调用的?SpringApplication.run(App.class, args);
org.springframework.boot.SpringApplication#refreshContext
org.springframework.context.support.AbstractApplicationContext#refresh (Spring启动的核心方法)
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
大致分析:
更具classpath中的class分析出当前的项目类型是REACTIVE, SERVELET, NONE, 如果是SERVLET类型创建ServletWebServerApplicationContext的Context, 在AbstractApplicationContext的onRefresh()的钩子方法调用ServletWebServerApplicationContext的onRefresh()方法调用createWebServer(),最终调用如下代码和Tomcat挂钩
if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); }
以上是SpringBoot在onRefresh()中启动Tomcat的逻辑, 其实还是在Spring的refresh()流程中
Tomcat如何初始化Spring呢?只需要在refresh()方法打一个断点就知道了
其中有好几个异步追踪, 说明Tomcat初始化Spring使用的是异步线程方式
main线程
tomcatServer.start();
server.start();
startInternal(); 提交异步任务
异步任务线程1
异步任务线程2
listener.contextInitialized(tldEvent);
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
configureAndRefreshWebApplicationContext的实例是AnnotationConfigWebApplicationContext
而AnnotationConfigWebApplicationContext的继承结构是
所以在调用refresh()方法就会触发spring初始化的核心逻辑
而此时的onRefresh()钩子方法不会触发再次Tomcat启动, 所以不会出现Tomcat重复启动
@Override protected void onRefresh() { this.themeSource = UiApplicationContextUtils.initThemeSource(this); }4. SpringBoot 启动流程分析 准备工作
ApplicationContextInitializer Context初始化后调用的类
SpringApplicationRunListener SpringBoot运行监听的类
ApplicationRunner/CommandLineRunner 两个几乎可以等价,用于启动后做客户自定义的 *** 作
MyApplicationRunner
@Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("MyApplicationRunner.run()执行了"); } }
MyCommandLineRunner
@Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("MyCommandLineRunner.run()执行了" + Arrays.asList(args)); } }
MyApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer{ @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("MyApplicationContextInitializer.initialize()执行了" + applicationContext); } }
MySpringApplicationRunListener
public class MySpringApplicationRunListener implements SpringApplicationRunListener { //必须有的构造器 public MySpringApplicationRunListener(SpringApplication application, String[] args) { } @Override public void starting() { System.out.println("MySpringApplicationRunListener.starting()执行了"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { System.out.println("MySpringApplicationRunListener.environmentPrepared()执行了"); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener.contextPrepared()执行了"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener.contextLoaded()执行了"); } @Override public void started(ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener.started()执行了"); } @Override public void running(ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener.running()执行了"); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("MySpringApplicationRunListener.failed()执行了"); } }
在resources/meta-INF/spring.factories增加
org.springframework.context.ApplicationContextInitializer=len.hgy.listener.MyApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener=len.hgy.listener.MySpringApplicationRunListener创建SpringApplication
SpringApplication源码
public static ConfigurableApplicationContext run(Class> primarySource, String... args) { return run(new Class>[] { primarySource }, args); }
其实SpringBoot启动就着两个步骤,先创建ConfigurableApplicationContext ,然后再调用Run方法。
ConfigurableApplicationContext 是其他方式接入Spring的入口上下文
SpringApplication构造方法源码
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //保存主类 this.primarySources = new linkedHashSet<>(Arrays.asList(primarySources)); //判断当前是什么类型项目, Tomcat启动Spring不需要判断,因为使用Tomcat就说明是SERVLET类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //从类路径下找到meta-INF/spring.factories配置的所有 ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //从类路径下找到meta-INF/spring.factories配置的所有ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }Run方法
核心方法
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; CollectionexceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //从类路径下meta‐INF/spring.factories,取得 SpringApplicationRunListeners; SpringApplicationRunListeners listeners = getRunListeners(args); //回调所有的获取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //创回调SpringApplicationRunListener.environmentPrepared(); //表示环境准备完成 //打印 Banner Banner printedBanner = printBanner(environment); //根据环境创建context context = createApplicationContext(); //错误的异常报表 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备上下文环境; //将environment保存到ioc中; //applyInitializers()调用所有的ApplicationContextInitializer的initialize方法 //调用所有的SpringApplicationRunListener的contextPrepared(); prepareContext(context, environment, listeners, applicationArguments, printedBanner); //SpringApplicationRunListener的contextLoaded //刷新容器 //扫描,创建,加载所有组件; refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //所有的SpringApplicationRunListener回调started方法 listeners.started(context); //获取所有的ApplicationRunner和CommandLineRunner进行调用 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //所有的SpringApplicationRunListener的running(); listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
注意
ApplicationContextInitializer是在构造方法中从spring.factories中获取初始化的
SpringApplicationRunListener是在run中获取的
callRunners(context, applicationArguments); // 会调用所有的ApplicationRunner和CommandLineRunner
private void callRunners(ApplicationContext context, ApplicationArguments args) { List
spi的bean不需要加注解就可以导入到Spring中, Runner需要加注解才能导入
总结:springboot的启动核心步骤就是调用refresh()方法,启动spring核心流程, 调用onRefresh()方法, new Tomcat(),并设置tomcat项目路径,端口等(就是创建一个类似web.xml的对象)
另外springboot还有一个核心功能是自动配置, 接下来看自动配置
1、SPI
SPI 在 springboot 中是去读取 meta-INF/spring.factories 目录的配置文件内 容,把配置文件中的类加载到 spring 容器中。这样如果你想把一个类加载到 spring 容器中,也可以采用这种方式来做。把类配置到 spring.factories 配置 文件中即可。
如果你想把一个类加载到 spring 容器中管理有几种方式:
1、通过 xml 的 bean 标签
2、通过加@Component 注解被@ComponentScan 扫描
3、通过在 spring.factories 配置该类
前两者是加载本工程的 bean,扫描本工程的 bean,第三点可以加载第三方定义 的 jar 包中的 bean,毕竟第三方 jar 包的包名跟本工程包名可能不一样,所以 前两个方式扫描不到。
2、创建 springboot 的上下文对象
在这个上下文对象构造函数中把 ConfigurationClassPostProcessor 变成 beanDefinition 对象(核心的ConfigurationClassPostProcessor 是在这个地方编程了BeanDefinition的)
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { ... public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } ... } this.reader = new AnnotatedBeanDefinitionReader(this); // 注册 ConfigurationClassPostProcessor的BeanDefinition if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); }
3、容器的启动
其实很简单就是调用了上下文对象的 refresh 核心方法:
// 核心方法, 启动Spring容器 refreshContext(context); // 进入AbstractApplicationContext public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // 创建BeanFactory和xml的标签解析哦 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); // 设置一些属性, 不重要 try { // 允许在上下文子类中对bean工厂进行后处理, 主要是注册WebApplication的Scopes和Environment的Beans到BeanFactory中, 所以是到beanFactory的后处理 //WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); //WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); postProcessBeanFactory(beanFactory); * private final ListbeanFactoryPostProcessors = new ArrayList<>(); * SpringBoot中在applyInitializers(context);里面添加这个容器 * 这些bean不在核心容器里面, 是在一个单独的beanFactoryPostProcessors中 * 通过getBean实例化,让后调用后置处理器 * currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); * */ invokeBeanFactoryPostProcessors(beanFactory); // 是BeanDefinitionRegistry和BeanFactory的后置处理器调用 registerBeanPostProcessors(beanFactory); // 是Bean的后置处理器调用 // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // new Tomcat(), 并启动 // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
4、内置 tomcat 的启动和部署
Tomcat 的启动在 onRefresh()中:
// org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh @Override public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); // host File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setbaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); // connector tomcat.getService().addConnector(connector); // set service customizeConnector(connector); tomcat.setConnector(connector); // set connector tomcat.getHost().setAutoDeploy(false); // 设置非自动部署 configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }SpringBoot启动核心就做了两件是
启动Spring的refresh()方法(refresh())
AbstractApplicationContext启动核心流程中回调ServletWebServerApplicationContext.onRefresh()的钩子方法(onRefresh())
5. SpringBoot的自动配置 为什么要有 springboot 自动配置功能?在 springboot 项目中,我们可以在业务代码里面用事务注解,用缓存注解,用 mvn 相关的功能等等,但是我们并没有在 springboot 项目把这些功能开启添加 进来,那么为什么我们可以在业务代码中使用这些功能呢?也就是说这些功能如 何跑到 springboot 项目中来的呢?这就是 springboot 的自动配置功能
自动配置功能开启
@SpringBootConfiguration @EnableAutoConfiguration // SpringBoot自动配置注解 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
@AutoConfigurationPackage @import(AutoConfigurationimportSelector.class) // 使用import导入 public @interface EnableAutoConfiguration {那这个AutoConfigurationimportSelector如何被调用的?
在refresh()的核心流程中有两个非常重要的后置处理方法
invokeBeanFactoryPostProcessors(beanFactory); // BeanDefinitionRegistry和BeanFactory的后置处理器
// 这两个是对beanFactory的修改(当然, 主要是修改BeanDefinition)
// 继承关系 public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // BeaDefinitionRegistry // BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(registry); invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); // BeanFactory // BeanFactoryPostProcessor.postProcessBeanFactory(beanFactory); // BeanDefinitionRegistryPostProcessor调用后将对象添加到BeanFactoryPostProcessor容器中 // 所以BeanDefinitionRegistryPostProcessor容器是BeanFactoryPostProcessor容器的一个自己 // 所有的BeanDefinitionRegistryPostProcessor对象会调用postProcessBeanDefinitionRegistry和postProcessBeanFactory两个方法
registerBeanPostProcessors(beanFactory); // BeanPostProcessor接口的调用, 即bean的后置处理器调用
public interface BeanPostProcessor { // 接口可以看出, bean的后置处理器是对Bean的修改 @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; // 默认不修改返回原始bean } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; // 默认不修改返回原始bean } }
@Configuration,@import,@Bean,@importResource,@ComponentScan等注解是在ConfigurationClassPostProcessor中完成的
ConfigurationClassPostProcessor的类结构
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
理所当然Registry的postProcessBeanDefinitionRegistry后置处理器就是处理这些注解的入口
@Override // 不用忘记了, 这个是refresh核心流程调用过来的invokeBeanFactoryPostProcessor() public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); // 处理配置beanDefinition, 处理注解的核心逻辑 }
this.deferredimportSelectorHandler.process(); // deferredimportSelector的处理器 // ConfigurationClassParser.DeferredimportSelectorHandler#process public void process() { Listdeferredimports = this.deferredimportSelectors; this.deferredimportSelectors = null; try { if (deferredimports != null) { DeferredimportSelectorGroupingHandler handler = new DeferredimportSelectorGroupingHandler(); deferredimports.sort(DEFERRED_import_COMPARATOR); deferredimports.forEach(handler::register); handler.processGroupimports(); // 处理 group imports逻辑 } } finally { this.deferredimportSelectors = new ArrayList<>(); } } // ConfigurationClassParser.DeferredimportSelectorGrouping#getimports public Iterable getimports() { for (DeferredimportSelectorHolder deferredimport : this.deferredimports) { this.group.process(deferredimport.getConfigurationClass().getmetadata(), deferredimport.getimportSelector()); // process调用importSelector的接口方法 } return this.group.selectimports(); }
AutoConfigurationimportSelector的类继承结构如下
public class AutoConfigurationimportSelector // 是DeferredimportSelector的类 implements DeferredimportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
getimports()->this.group.process()->AutoConfigurationimportSelector.AutoConfigurationGroup#process()
->AutoConfigurationimportSelector#getAutoConfigurationEntry()
->AutoConfigurationimportSelector#getCandidateConfigurations()
-> SpringFactoriesLoader.loadFactoryNames() // 此处就是SpringBoot通过SPI获取spring.Factories中bean实现,然后导入里面的类的
public static ListloadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
然后就是Spring的import注解的处理逻辑
AutoConfigurationimportSelector实现了 DeferredimportSelector 接口。 这个类的核心功能是通过 SPI 机制收集 EnableAutoConfiguration 为 key 的所有 类,然后通过 ConfigurationClassPostProcessor 这个类调用到该类中的方法, 把收集到的类变成 beanDefinition 对象最终实例化加入到 spring 容器。
# Auto Configure, 一个实现代表导入一个功能 org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration, org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, ...
AutoConfigurationimportSelector 类中两个方法会被 ConfigurationClassPostProcessor 调到:
org.springframework.boot.autoconfigure.AutoConfigurationimportSelector.AutoConfigurationGroup#process
org.springframework.boot.autoconfigure.AutoConfigurationimportSelector#selectimports
在这两个方法中完成了 SPI 类的收集。
ConfigurationClassPostProcessor 类只是把收集到的类变成 beanDefinition 并加入到 spring 容器。
ConfigurationClassPostProcessor 类调用的地方。
private void processimports(ConfigurationClass configClass, SourceClass currentSourceClass, CollectionimportCandidates, boolean checkForCircularimports) { ... if (candidate.isAssignable(importSelector.class)) { // Candidate class is an importSelector -> delegate to it to determine imports Class> candidateClass = candidate.loadClass(); // 需要实例化这个候选的bean importSelector selector = BeanUtils.instantiateClass(candidateClass, importSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (selector instanceof DeferredimportSelector) { // 看看是不是一个Defer selector(延迟被导入的) this.deferredimportSelectorHandler.handle( // 处理加入 configClass, (DeferredimportSelector) selector); } else { String[] importClassNames = selector.selectimports(currentSourceClass.getmetadata()); Collection importSourceClasses = asSourceClasses(importClassNames); processimports(configClass, currentSourceClass, importSourceClasses, false); } } ... } public void handle(ConfigurationClass configClass, DeferredimportSelector importSelector) { DeferredimportSelectorHolder holder = new DeferredimportSelectorHolder( configClass, importSelector); if (this.deferredimportSelectors == null) { DeferredimportSelectorGroupingHandler handler = new DeferredimportSelectorGroupingHandler(); handler.register(holder); handler.processGroupimports(); // 调用处理 } else { this.deferredimportSelectors.add(holder); } } public void processGroupimports() { for (DeferredimportSelectorGrouping grouping : this.groupings.values()) { grouping.getimports().forEach(entry -> { // here ConfigurationClass configurationClass = this.configurationClasses.get( entry.getmetadata()); try { processimports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getimportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getmetadata().getClassName() + "]", ex); } }); } } public Iterable getimports() { for (DeferredimportSelectorHolder deferredimport : this.deferredimports) { // 调用 process this.group.process(deferredimport.getConfigurationClass().getmetadata(), deferredimport.getimportSelector()); } return this.group.selectimports(); // selectimports }
这就是这两个方法的调用地方。
上述就是 EnableAutoConfiguration 为 key 自动配置类的收集过程。有自动配置类的收集并加入到 spring 容器,前面提到的 aop,事务,缓存,mvc 功能就已经导入到 springboot 工程了。
6. AOP 功能的自动配置类# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
AopAutoConfiguration
这个就是 AOP 功能的自动配置类,如果我们要开启 aop 功能,必须要在 xml 里面加配置或者用注解的方式手动开启 aop 功能、注解方式开启 aop
功能如图:
手动开启 aop 功能的这个注解其实就是往 spring 容器里面加入了 一个支持 aop 功能的入口类。
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }
默认是开启 aop 自动配置功能的,默认是开启了 cglib 的动态代理功能的,并且也加上了EnableAspectJAutoProxy(proxyTargetClass = false) 注解了,加上这个注解就会开启 aop 功能。
EnableAspectJAutoProxy这个注解可以作用在内部类, 并且在SpringBoot的自动配置类中经常出现内部类, 并且这两个方法都是标记作用,没有任何实现逻辑
EnableAspectJAutoProxy说明
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @documented @import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
Configuration说明
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @documented @Component public @interface Configuration { @AliasFor(annotation = Component.class) String value() default ""; boolean proxyBeanMethods() default true; // 代理@Bean的方法, 默认代理且代理都是通过CGLIB代理实现, 这里没有使用JDK的选项, 这里和EnableAspectJAutoProxy#proxyTargetClass的相同,true表示使用cglib, 只不过这里的false不使用代理, 默认比是true不是false }7. Condition 功能的实现
Condition 功能的使用很简单 使用的需求是:有的时候我们需要当某一个条件满足的时候才把一些类实例化并 加入到 spring 容器中。
@ConditionalOnBean: 当 spring 容器中存在 jack 这个 bean 时,才调用该方法
@Bean @ConditionalOnBean(name = "order") public User CglibAutoProxyConfiguration { return new User(); }
其他注解类似
@ConditionalOnClass
@ConditionalOnMissingBean
@ConditionalOnMissingClass
@ConditionalOnexpression( " ${spring.datasource.max-idle} == 10")
@ConditionalOnProperty(perfix = “spring.redis”, name = “host”, havingValue = “192.168.10.10”)
自定义 conditionpublic class CustomCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypemetadata metadata) { System.out.println("=====CustomCondition.matches======"); String property = context.getEnvironment().getProperty("spring.redis.jedis.pool.max-active"); return !"8".equals(property); } }
当自定义 CustomCondition 中的 matches 方法返回 true 时才会调用到该方法。 @Conditional 注解作用在类上面也是一样的使用方式
@Component @Conditional(value = CustomCondition.class) public class CustomConditionBean { }
Condition 的原理和源码
从 condition 的使用需求我们知道,这个是单条件满足的时候才实例化 bean 和 加入到 spring 容器,而在 spring 中一个类的实例化必须要变成 beanDefinition 对象。而 ConfigurationClassPostProcessor 是所有 beanDefinition 对象的集散地,所有的 beanDefinition 都会在这个类里面处理。那么我们要完成 Condition 功能也必定在这个类里面。
ConfigurationClassParser类中的 shouldSkip 方法就是做 bean 过滤 的。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionevaluator.shouldSkip(configClass.getmetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ... }
for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // matches方法,是核心方法 if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } }
SpringBootCondition#matches(ConditionContext, AnnotatedTypemetadata)
public final boolean matches(ConditionContext context, AnnotatedTypemetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { // 模板方法,不同注解调用实现不一样 ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordevaluation(context, classOrMethodName, outcome); return outcome.isMatch(); } catch (NoClassDefFoundError ex) { throw new IllegalStateException("...", ex); } catch (RuntimeException ex) { throw new IllegalStateException("Error processing condition on " + getName(metadata), ex); } }
这里看两个注解的实现
org.springframework.boot.autoconfigure.condition.ConditionalOnBean对应的注解实现是: org.springframework.boot.autoconfigure.condition.OnBeanCondition
Bean 存在时才掉用方法,这个其实很好理解,判断 bean 是否存在其实就只要从 BeanFactory 中找就行了,源码里面就是从 BeanFactory 中找。
@Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypemetadata metadata) { ConditionMessage matchMessage = ConditionMessage.empty(); MergedAnnotations annotations = metadata.getAnnotations(); if (annotations.isPresent(ConditionalOnBean.class)) { Specspec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class); MatchResult matchResult = getMatchingBeans(context, spec); // 核心逻辑 if (!matchResult.isAllMatched()) { String reason = createOnBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(spec.message().because(reason)); } matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches()); } ... return ConditionOutcome.match(matchMessage); }
protected final MatchResult getMatchingBeans(ConditionContext context, Spec> spec) { ... MatchResult result = new MatchResult(); SetbeansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,spec.getIgnoredTypes(), parameterizedContainers); for (String type : spec.getTypes()) { // 更具注解类型从容器中获取bean Collection typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers); typeMatches.removeAll(beansIgnoredByType); if (typeMatches.isEmpty()) { result.recordUnmatchedType(type); } else { result.recordMatchedType(type, typeMatches); } } ... return result; }
从 BeanFactory 中获取对应的实例,如果有则匹配。
org.springframework.boot.autoconfigure.condition.ConditionalOnClass对应的注解实现是: org.springframework.boot.autoconfigure.condition.OnClassCondition
当工程上下文中存在该类时才调用方法 实现原理,就是通过反射的方式,如果反射有异常则返回 false,如果反射没异 常返回 true
@Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypemetadata metadata) { ClassLoader classLoader = context.getClassLoader(); ConditionMessage matchMessage = ConditionMessage.empty(); ListonClasses = getCandidates(metadata, ConditionalOnClass.class); if (onClasses != null) { // 核心逻辑 List missing = filter(onClasses, ClassNameFilter.MISSING, classLoader); if (!missing.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) .didNotFind("required class", "required classes").items(Style.QUOTE, missing)); } matchMessage = matchMessage.andCondition(ConditionalOnClass.class) .found("required class", "required classes") .items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader)); } List onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class); if (onMissingClasses != null) { List present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader); if (!present.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class) .found("unwanted class", "unwanted classes").items(Style.QUOTE, present)); } matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class) .didNotFind("unwanted class", "unwanted classes") .items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader)); } return ConditionOutcome.match(matchMessage); }
protected final Listfilter(Collection classNames, ClassNameFilter classNameFilter, ClassLoader classLoader) { if (CollectionUtils.isEmpty(classNames)) { return Collections.emptyList(); } List matches = new ArrayList<>(classNames.size()); for (String candidate : classNames) { if (classNameFilter.matches(candidate, classLoader)) { // 一个枚举方法,看入参的实现 matches.add(candidate); } } return matches; }
MISSING { @Override public boolean matches(String className, ClassLoader classLoader) { return !isPresent(className, classLoader); } };
static boolean isPresent(String className, ClassLoader classLoader) { if (classLoader == null) { classLoader = ClassUtils.getDefaultClassLoader(); } try { resolve(className, classLoader); // 看是否有异常 return true; } catch (Throwable ex) { return false; } }8. Redis 自动配置
自动配置类
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
为什么可以在程序里面直接依赖注入 RedisTemplate,在自动配置类创建了该实
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) // 更具class决定是否引入该功能 @EnableConfigurationProperties(RedisProperties.class) // 开启配置属性的java bean @import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) // 导入功能相关类 public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") // 没有bean时候才注入 public RedisTemplate9. 数据源自动配置
自动配置类
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) // 导入公共类 @import({ DataSourcePoolmetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @import(EmbeddedDataSourceConfiguration.class) // 导入需要的类 protected static class EmbeddedDatabaseConfiguration { } @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { } static class PooledDataSourceCondition extends AnyNestedCondition { PooledDataSourceCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @ConditionalOnProperty(prefix = "spring.datasource", name = "type") static class ExplicitType { } @Conditional(PooledDataSourceAvailableCondition.class) static class PooledDataSourceAvailable { } } static class PooledDataSourceAvailableCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypemetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource"); if (DataSourceBuilder.findType(context.getClassLoader()) != null) { return ConditionOutcome.match(message.foundExactly("supported DataSource")); } return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll()); } } static class EmbeddedDatabaseCondition extends SpringBootCondition { private final SpringBootCondition pooledCondition = new PooledDataSourceCondition(); @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypemetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource"); if (anyMatches(context, metadata, this.pooledCondition)) { return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source")); } EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType(); if (type == null) { return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll()); } return ConditionOutcome.match(message.found("embedded database").items(type)); } } }
默认是有 Hikari 数据源对象的。
// org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari @Configuration(proxyBeanMethods = false) @ConditionalOnClass(HikariDataSource.class) @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) static class Hikari { @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") HikariDataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } }10. JdbcTemplate 自动配置
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, JdbcTemplate.class }) @ConditionalOnSingleCandidate(DataSource.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) @EnableConfigurationProperties(JdbcProperties.class) @import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class }) public class JdbcTemplateAutoConfiguration { }
JdbcTemplateConfiguration配置JdbcTemplate的bean
@Configuration(proxyBeanMethods = false) // false, 说明,每次调用都会原始方法,即不代理原始方法 @ConditionalOnMissingBean(JdbcOperations.class) class JdbcTemplateConfiguration { @Bean @Primary JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); JdbcProperties.Template template = properties.getTemplate(); jdbcTemplate.setFetchSize(template.getFetchSize()); jdbcTemplate.setMaxRows(template.getMaxRows()); if (template.getQueryTimeout() != null) { jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds()); } return jdbcTemplate; } }11. 事务管理器自动配置
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) @EnableConfigurationProperties(DataSourceProperties.class) public class DataSourceTransactionManagerAutoConfiguration { @Configuration(proxyBeanMethods = false) // 不使用代理(CGLIB代理:true), 即每次都调用原始的@Bean方法 @ConditionalOnSingleCandidate(DataSource.class) static class DataSourceTransactionManagerConfiguration { @Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) DataSourceTransactionManager transactionManager(DataSource dataSource, ObjectProvider12. 事务自动配置transactionManagerCustomizers) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager)); return transactionManager; } } }
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
编程式事务
@Configuration(proxyBeanMethods = false) @ConditionalOnSingleCandidate(PlatformTransactionManager.class) public static class TransactionTemplateConfiguration { @Bean @ConditionalOnMissingBean(TransactionOperations.class) public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { return new TransactionTemplate(transactionManager); } }
事务功能导入注解
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(TransactionManager.class) @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) public static class EnableTransactionManagementConfiguration { @Configuration(proxyBeanMethods = false) @EnableTransactionManagement(proxyTargetClass = false) // 开启事务管理器代理 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration(proxyBeanMethods = false) @EnableTransactionManagement(proxyTargetClass = true) // 开启事务管理器代理 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }13. DispatcherServlet 自动配置
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,
DispatcherServlet
@Configuration(proxyBeanMethods = false) @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class }) protected static class DispatcherServletConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.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; } }
DispatcherServletRegistrationBean
@Configuration(proxyBeanMethods = false) @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { @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, ObjectProvider14. MVC 自动配置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; } }
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,
其实 mvc 的自动配置就是把一些支持 mvc 功能的类创建出来 比如:handlerMapping,HandlerAdapter,ViewResolver 等等实例
@Bean @Override public RequestMappingHandlerAdapter requestMappingHandlerAdapter( // 映射处理适配器 @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) { RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator); adapter.setIgnoreDefaultModelOnRedirect( this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect()); return adapter; } @Override protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { // 被调用的方法 if (this.mvcRegistrations != null && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) { return this.mvcRegistrations.getRequestMappingHandlerAdapter(); } return super.createRequestMappingHandlerAdapter(); } @Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping( // 映射处理器 @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { // Must be @Primary for MvcUriComponentsBuilder to work return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider); } @Bean // 欢迎页面映射 public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping( new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); return welcomePageHandlerMapping; } private OptionalgetWelcomePage() { // 获取欢迎页面 String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations()); return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst(); } private Resource getIndexHtml(String location) { // 获取默认的索引页面, index.html return this.resourceLoader.getResource(location + "index.html"); } @Bean @Override public FormattingConversionService mvcConversionService() { WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat()); addFormatters(conversionService); return conversionService; } @Bean @ConditionalOnMissingBean public InternalResourceViewResolver defaultViewResolver() { // 内部视图解析器 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix(this.mvcProperties.getView().getPrefix()); resolver.setSuffix(this.mvcProperties.getView().getSuffix()); return resolver; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)