❶ Spring源码解析(一)- 容器的基本实现
Spring使用 基本的javaBean 来完成以前只可能由EJB完成的事情,是个分层架构。Spring创建bean都需要通过 读取 、 解析 、 校验配置文件, 然后注册创建成Bean。 Spring是一个Bean容器 , 主要作用是替我们管理bean对象 (简单的Java类对象的生命周期)。不管框架如何强大,还是需要我们程序员来告诉其一些必要信息的(比如要 管理的bean对象的类相关信息、是否开启组件扫档纳描 等),这些我们称之为对 Spring框架的配置 ,目前主流的配置方式是 通过使用配置文件或注解。
Spring中最核心的两个源雀类: DefaultListableBeanFactory、XmlBeanDifinitionReader。DefaultListableBeanFactory 是整个bean加载的核心部分,是Spring注册及加载bean的默认实现 。XmlBeanDefinitionReader 主要使用reader属性对资源文件进行读取和注册。
XML配置文件读取是Spring中重要的功能,大部分Spring大部分功能都是 以配置作为切入点 。 XmlBeanFactory 继承自 DefaultListableBeanFactory ,而对于 DefaultListableBeanFactory 不同的地方其实是在 XmlBeanFactory 中使用了自定义的XML读取器 XmlBeanDefinitionReader ,主要用于从XML文档中读取 BeanDefinition, 实现了个性化的 BeanDefinitionReader 读取, DefaultListableBeanFactory 继承了 并实现了 以及 BeanDefinitionRegistry 接口。
Spring的配置文件读取是通过ClasaPathResource进行封装的 ,如:new ClassPathResource("bean.xml")。在java中, 将不同来源的资源的读取逻辑抽象成URL ,通过注册不同的 handler来处理。 一般handler的类型使用不同的前缀,URL没有默认定义相对的path路径,也 没有提供相关方法对资源进行检查 ,顾Spring对其内部需要使用到的资源做了属于自己的抽象结构, 用Resource接口来封装底层资源。
Resource 接口继承 InputStreamSource(封装了任何能返回InputStream的类)。
Resource接口抽象了所有Spring内部使用到的底层资源 ,首先它定义了3个能判断当前资源状态的方法: 存在性(exists)、可读性(isReadable)、是否处于打开状态(isOpen) 。有了Resource接口便可以对所有资源进行统一处理。 ClassPathResource 中的实现是通过class或 classLoader 提供的底层方法进行调用。以此完成对配置文件资源的封装。
当通过Resource相关类完成了对配置文件进行封装,接下来由 XmlBeanDefinitionReader 完成对配置文件的读取工作。雹蠢早
XML文件的验证模式有两种:DTD、XSD(XML Schema).
DTD即文档类型定义, 是一种XML约束模式语言,是XML文件的验证机制 。是一种保证XML文档格式正确的有效方法, 可以通过比较XML文档和DTD文件来查看文档是否符合规范,元素和标签的使用是否正确 。一个DTD文档包含:元素的定义规则、元素间关系的定义规则、元素可使用的属性、可使用的实体或符号规则。 要使用DTD验证模式需要在XML文件的头部声明。
XML Schema语言就是XSD。 XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。也可以 通过XML Schema指定一个XML文档所允许的结构和内容 。XML Schema本身也是一个XML文档,符合XML语法结构,可以用通用的XML解析器解析它。
使用XML Schema文档对XML实例进行校验,要声明名称空间和指定该名称空间所对应的XML Schema文档存储位置 。通过schemaLocation属性来指定名称空间所对应的XML Schema文档的存储地址(1、名称空间URL;2、该名称空间所标识的XML Schema文件地址或URL地址)。
另外验证模式通过 XmlBeanDefinitionReader 中的setValidationMode方法进行设定。而 Spring 用来检测验证模式的方法实际上就是判断是否包含 DOCTYPE ,如果包含就是 DTD ,否则就是 XSD 。
XML文件经过验证模式,交由DocumentLoader进行解析成对应的 Document。 而解析的过程中存在这么一环节:(EntityResolver) 根据声明去寻找对应的DTD定义,以便对文档进行验证认证 。也可以通过setEntityResolver设置DTD定义。EntityResolver它用来接收两个参数publicId和systemId,xsd格式文件通常publicId为null。而对于不同的验证模式采用不同的解析器进行解析,并把文件转换成Document文件,用于提取及注册bean。
Document 文件通过 BeanDefinitionDocumentReader 进行内部逻辑处理,并提取root用于作为参数继续完成BeanDefinition的注册。
❷ Spring Tx源码解析(二)
上一篇 我们介绍了 spring-tx 中的底层抽象,本篇我们一起来看看围绕这些抽象概念 spring-tx 是如何打造出声明式事务的吧。笼统的说, spring-tx-5.2.6.RELEASE 的实现主要分为两个部分:
这两部分彼此独立又相互成就,并且每个部分都有着大量的源码支撑,本篇我们先来分析 spring-tx 中的AOP部分吧。
EnableTransactionManagement 注解想必大家都很熟悉了,它是启用 Spring 中注释驱动的事务管理功能的关键。
EnableTransactionManagement 注解的主要作用是向容器中导入 ,至于注解中定义的几个属性在 Spring AOP源码解析 中有过详细分析,这里就不再赘述了。
由于我们并没有使用 AspectJ ,因此导入容器的自然是 这个配置类。
这个配置类的核心是向容器中导入一个类型为 的Bean。这是一个 PointcutAdvisor ,它的 Pointcut 是 , Advice 是 TransactionInterceptor 。
利用 TransactionAttributeSource 解析 @Transactional 注解的能力来选取标注了 @Transactional 注解的方法,而 TransactionInterceptor 则根据应用提出的需求(来自对 @Transactional 注解的解析)将方法增强为事务方法,因此 可以识别出那些标注了 @Transactional 注解的方法,为它们应用上事务相关功能。
TransactionInterceptor 能对方法进行增强,但是它却不知道该如何增强,比如是为方法新开一个独立事务还是沿用已有的事务?什么情况下需要回滚,什么情况下不需要?必须有一个‘人’告诉它该如何增强,这个‘人’便是 TransactionAttributeSource 。
@Transactional 注解定义了事务的基础信息,它表达了应用程序期望的事务形态。 TransactionAttributeSource 的主要作用就是解析 @Transactional 注解,提取其属性,包装成 TransactionAttribute ,这样 TransactionInterceptor 的增强便有了依据。
前面我们已经见过, spring-tx 使用 来做具体的解析工作,其父类 定义了解析 TransactionAttribute 的优先级,核心方法是 computeTransactionAttribute(...) 。
默认只解析 public 修饰的方法,这也是导致 @Transactional 注解失效的一个原因,除此之外它还实现了父类中定义的两个模板方法:
同时为了支持 EJB 中定义的 javax.ejb.TransactionAttribute 和 JTA 中定义的 javax.transaction.Transactional 注解, 选择将实际的提取工作代理给 TransactionAnnotationParser 。Spring 提供的 @Transactional 注解由 进行解析。
的源码还是很简单的,它使用 AnnotatedElementUtils 工具类定义的 find 语义来获取 @Transactional 注解信息。 RuleBasedTransactionAttribute 中 rollbackOn(...) 的实现还是挺有意思的,其它的都平平无奇。
RollbackRuleAttribute 是用来确定在发生特定类型的异常(或其子类)时是否应该回滚,而 NoRollbackRuleAttribute 继承自 RollbackRuleAttribute ,但表达的是相反的含义。 RollbackRuleAttribute 持有某个异常的名称,通过 getDepth(Throwable ex) 算法来计算指定的 Throwable 和持有的异常在继承链上的距离。
程序猿只有在拿到需求以后才能开工, TransactionInterceptor 也一样,有了 TransactionAttributeSource 之后就可以有依据的增强了。观察类图, TransactionInterceptor 实现了 MethodInterceptor 接口,那么自然要实现接口中的方法:
可以看到, TransactionInterceptor 本身是没有实现任何逻辑的,它更像一个适配器。这样分层以后, TransactionAspectSupport 理论上就可以支持任意类型的 Advice 而不只是 MethodInterceptor 。实现上 TransactionAspectSupport 确实也考虑了这一点,我们马上就会看到。
invokeWithinTransaction(...) 的流程还是非常清晰的:
第一步前文已经分析过了,我们来看第二步。
TransactionInfo 是一个非常简单的类,我们就不费什么笔墨去分析它了。接着看第三步,这一步涉及到两个不同的操作——提交或回滚。
至此, TransactionInterceptor 于我们而言已经没有任何秘密了。
本篇我们一起分析了 spring-tx 是如何通过 spring-aop 的拦截器将普通方法增强为事务方法的,下篇就该说道说道 PlatformTransactionManager 抽象下的事务管理细节啦,我们下篇再见~~
❸ [Spring boot源码解析] 2 启动流程分析
在了解 Spring Boot 的启动流程的时候,我们先看一下一个Spring Boot 应用是如何启动的,如下是一个简单的 SpringBoot 程序,非常的简洁,他是如何做到的呢,我们接下来就将一步步分解。
我们追踪 SpringApplication.run() 方法,其实最终它主要的逻辑是新建一个 SpringApplication ,然后调用他的 run 方法,如下:
我们先来看一下创建 SpringApplication 的方法:
在将Main class 设置 primarySources 后,调用了 WebApplicationType.deceFromClasspath() 方法,该方法是为了检查当前的应用类型,并设置给 webApplicationType 。 我们进入 deceFromClasspath 方法 :
这里主要是通过类加载器判断是否存在 REACTIVE 相关的类信息,假如有就代表是一个 REACTIVE 的应用,假如不是就检查是否存在 Servelt 和 ,假如都没有,就代表应用为非 WEB 类应用,返回 NONE ,默认返回 SERVLET 类型,我们这期以我们目前最常使用的 SERVLET 类型进行讲解,所以我们在应用中引入了 spring-boot-starter-web 作为依赖:
他会包含 Spring-mvc 的依赖,所以就包含了内嵌 tomcat 中的 Servlet 和 Spring-web 中的 ,因此返回了 SERVLET 类型。
回到刚才创建 SpringApplication 的构建方法中,我们设置完成应用类型后,就寻找所有的 Initializer 实现类,并设置到 SpringApplication 的 Initializers 中,这里先说一下 getSpringFactoriesInstances 方法,我们知道在我们使用 SpringBoot 程序中,会经常在 META-INF/spring.factories 目录下看到一些 EnableAutoConfiguration ,来出发 config 类注入到容器中,我们知道一般一个 config 类要想被 SpringBoot 扫描到需要使用 @CompnentScan 来扫描具体的路径,对于 jar 包来说这无疑是非常不方便的,所以 SpringBoot 提供了另外一种方式来实现,就是使用 spring.factories ,比如下面这个,我们从 Springboot-test 中找到的例子,这里先定义了一个ExampleAutoConfiguration,并加上了 Configuration 注解:
然后在 spring.factories 中定义如下:
那这种方式是怎么实现的你,这就要回到我们刚才的方法 getSpringFactoriesInstances :
我们先来看一下传入参数,这里需要注意的是 args,这个是初始化对应 type 的时候传入的构造参数,我们先看一下 SpringFactoriesLoader#loadFactoryNames 方法:
首先是会先检查缓存,假如缓存中存在就直接返回,假如没有就调用 classLoader#getResources 方法,传入 META-INF/spring.factories ,即获取所有 jar 包下的对应文件,并封装成 UrlResource ,然后使用 PropertiesLoaderUtils 将这些信息读取成一个对一对的 properties,我们观察一下 spring.factories 都是按 properties 格式排版的,假如有多个就用逗号隔开,所以这里还需要将逗号的多个类分隔开来,并加到 result 中,由于 result 是一个 LinkedMultiValueMap 类型,支持多个值插入,最后放回缓存中。最终完成加载 META-INF/spring.factories 中的配置,如下:
我们可以看一下我们找到的 initializer 有多少个:
在获取到所有的 Initializer 后接下来是调用 方法进行初始化。
这里的 names 就是我们上面通过类加载器加载到的类名,到这里会先通过反射生成 class 对象,然后判断该类是否继承与 ApplicationContextInitializer ,最后通过发射的方式获取这个类的构造方法,并调用该构造方法,传入已经定义好的构造参数,对于 ApplicationContextInitializer 是无参的构造方法,然后初始化实例并返回,回到原来的方法,这里会先对所有的 ApplicationContextInitializer 进行排序,调用 #sort(instances) 方法,这里就是根据 @Order 中的顺序进行排序。
接下来是设置 ApplicationListener ,我们跟进去就会发现这里和上面获取 ApplicationContextInitializer 的方法如出一辙,最终会加载到如图的 15 个 listener (这里除了 外,其他都是 SpringBoot 内部的 Listener):
在完成 SpringApplication 对象的初始化后,我们进入了他的 run 方法,这个方法几乎涵盖了 SpringBoot 生命周期的所有内容,主要分为九个步骤,每一个步骤这里都使用注解进行标识:
主要步骤如下:
第一步:获取 SpringApplicationRunListener, 然后调用他的 staring 方法启动监听器。
第二步:根据 SpringApplicationRunListeners以及参数来准备环境。
第三步:创建 Spring 容器。
第四步:Spring 容器的前置处理。
第五步:刷新 Spring 容器。
第六步: Spring 容器的后置处理器。
第七步:通知所有 listener 结束启动。
第八步:调用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件。
我们接下来一一讲解这些内容。
我们首先看一下第一步,获取 SpringApplicationRunListener :
这里和上面获取 initializer 和 listener 的方式基本一致,都是通过 getSpringFactoriesInstances , 最终只找到一个类就是: org.springframework.boot.context.event.EventPublishingRunListener ,然后调用其构造方法并传入产生 args , 和 SpringApplication 本身:
我们先看一下构造函数,首先将我们获取到的 ApplicationListener 集合添加到initialMulticaster 中, 最后都是通过操作 来进行广播,我,他继承于 ,我们先看一下他的 addApplicationListener 方法:
我们可以看出,最后是放到了 applicationListenters 这个容器中。他是 defaultRetriever 的成员属性, defaultRetriever 则是 的私有类,我们简单看一下这个类:
我们只需要看一下这里的 getApplicationListeners 方法,它主要是到 beanFactory 中检查是否存在多的 ApplicationListener 和旧的 applicationListeners 组合并返回,接着执行 listener 的 start 方法,最后也是调用了 的 multicastEvent 查找支持对应的 ApplicationEvent 类型的通知的 ApplicationListener 的 onApplicationEvent 方法 ,这里除了会:
筛选的方法如下,都是调用了对应类型的 supportsEventType 方法 :
如图,我们可以看到对 org.springframework.boot.context.event.ApplicationStartingEvent 感兴趣的有5个 Listener
环境准备的具体方法如下:
首先是调用 getOrCreateEnvironment 方法来创建 environment ,我们跟进去可以发现这里是根据我们上面设置的环境的类型来进行选择的,当前环境会创建 StandardServletEnvironment
我们先来看一下 StandardServletEnvironment 的类继承关系图,我们可以看出他是继承了 AbstractEnvironment :
他会调用子类的 customizePropertySources 方法实现,首先是 StandardServletEnvironment 的实现如下,他会添加 servletConfigInitParams , servletContextInitParams , jndiProperties 三种 properties,当前调试环境没有配置 jndi properties,所以这里不会添加。接着调用父类的 customizePropertySources 方法,即调用到了 StandardEnvironment 。
我们看一下 StandardEnvironment#customizePropertySources 方法,与上面的三个 properties 创建不同,这两个是会进行赋值的,包括系统环境变量放入 systemEnvironment 中,jvm 先关参数放到 systemProperties 中:
这里会添加 systemEnvironment 和 systemProperties 这两个 properties,最终拿到的 properties 数量如下 4个:
在创建完成 Environment 后,接下来就到了调用 configureEnvironment 方法:
我们先看一下 configurePropertySources 方法,这里主要分两部分,首先是查询当前是否存在 defaultProperties ,假如不为空就会添加到 environment 的 propertySources 中,接着是处理命令行参数,将命令行参数作为一个 CompositePropertySource 或则 添加到 environment 的 propertySources 里面,
接着调用 ConfigurationPropertySources#attach 方法,他会先去 environment 中查找 configurationProperties , 假如寻找到了,先检查 configurationProperties 和当前 environment 是否匹配,假如不相等,就先去除,最后添加 configurationProperties 并将其 sources 属性设置进去。
回到我们的 prepareEnvironment 逻辑,下一步是通知观察者,发送 事件,调用的是 SpringApplicationRunListeners#environmentPrepared 方法,最终回到了 #multicastEvent 方法,我们通过 debug 找到最后对这个时间感兴趣的 Listener 如下:
其主要逻辑如下:
这个方法最后加载了 PropertySourceLoader , 这里主要是两种,一个是用于 Properties 的,一个是用于 YAML 的如下:
其中 apply 方法主要是加载 defaultProperties ,假如已经存在,就进行替换,而替换的目标 PropertySource 就是 load 这里最后的一个 consumer 函数加载出来的,这里列一下主要做的事情:
1、加载系统中设置的所有的 Profile 。
2、遍历所有的 Profile ,假如是默认的 Profile , 就将这个 Profile 加到 environment 中。
3、调用load 方法,加载配置,我们深入看一下这个方法:
他会先调用 getSearchLocations 方法,加载所有的需要加载的路径,最终有如下路径:
其核心方法是遍历所有的 propertySourceLoader ,也就是上面加载到两种 propertySourceLoader ,最红 loadForFileExtension 方法,加载配置文件,这里就不展开分析了,说一下主要的作用,因为每个 propertySourceLoader 都有自己可以加载的扩展名,默认扩展名有如下四个 properties, xml, yml, yaml,所以最终拿到文件名字,然后通过 - 拼接所有的真实的名字,然后加上路径一起加载。
接下来,我们分析 BackgroundPreinitializer ,这个方法在接收 ApplicationPrepareEnvironment 事件的时候真正调用了这份方法:
1、 ConversionServiceInitializer 主要负责将包括 日期,货币等一些默认的转换器注册到 formatterRegistry 中。
2、 ValidationInitializer 创建 validation 的匹配器。
3、 MessageConverterInitializer 主要是添加了一些 http 的 Message Converter。
4、 JacksonInitializer 主要用于生成 xml 转换器的。
接着回到我们将的主体方法, prepareEnvironment 在调用完成 listeners.environmentPrepared(environment) 方法后,调用 bindToSpringApplication(environment) 方法,将 environment 绑定到 SpirngApplication 中。
接着将 enviroment 转化为 StandardEnvironment 对象。
最后将 configurationProperties 加入到 enviroment 中, configurationProperties 其实是将 environment 中其他的 PropertySource 重新包装了一遍,并放到 environment 中,这里主要的作用是方便 进行解析。
它主要是检查是否存在 spring.beaninfo.ignore 配置,这个配置的主要作用是设置 javaBean 的内省模式,所谓内省就是应用程序在 Runtime 的时候能检查对象类型的能力,通常也可以称作运行时类型检查,区别于反射主要用于修改类属性,内省主要用户获取类属性。那么我们什么时候会使用到内省呢,java主要是通过内省工具 Introspector 来完成内省的工作,内省的结果通过一个 Beaninfo 对象返回,主要包括类的一些相关信息,而在 Spring中,主要是 BeanUtils#Properties 会使用到,Spring 对内省机制还进行了改进,有三种内省模式,如下图中红色框框的内容,默认情况下是使用 USE_ALL_BEANINFO。假如设置为true,就是改成第三中 IGNORE_ALL_BEANINFO
首先是检查 Application的类型,然后获取对应的 ApplicationContext 类,我们这里是获取到了 org.springframework.boot.web.servlet.context. 接着调用 BeanUtils.instantiateClass(contextClass); 方法进行对象的初始化。
最终其实是调用了 的默认构造方法。我们看一下这个方法做了什么事情。这里只是简单的设置了一个 reader 和一个 scanner,作用于 bean 的扫描工作。
我们再来看一下这个类的继承关系
这里获取 ExceptionReporter 的方式主要还是和之前 Listener 的方式一致,通过 getSpringFactoriesInstances 来获取所有的 SpringBootExceptionReporter 。
其主要方法执行如下:
❹ Spring IOC源码解析(02)PropertySource和PropertySources
PropertySource 主要是对属性源的抽象,抽象除了熟悉源名称和属性源内容对象。其主要搭老者方法仍然是对这两个字段进行操作。
属性源抽象类有很多子类的知薯实现,我们仅仅分析其中最常用含睁的一个,即: PropertiesPropertySource 。
其继承关系如下:
逐层分析如下:
PropertySource 主要是对属性源的抽象,抽象除了熟悉源名称和属性源内容对象。其主要方法仍然是对这两个字段进行操作。
属性源抽象类有很多子类的实现,我们仅仅分析其中最常用的一个,即: PropertiesPropertySource 。
其继承关系如下:
逐层分析如下:
PropertySources 是对属性源列表操作的封装。主要加入了 迭代器 、 可变性 和 集合操作 。这个类有一个唯一的子类,即: MutablePropertySources 。
MutablePropertySources 在 PropertySources 的基础上,增加了 可变性 和 集合操作 。
❺ Spring源码解析之BeanPostProcessor
源码版本4.3.10-RELEASE
我们都知道,实缺咐现BeanPostProcessor接口,在bean初始化前后,spring会分别回调和。目的是保留扩展接口修改bean的属性,甚至可以替换bean :
那spring究竟是如何做到的呢?我们一起来看看吧。
从spring容器启动开始看起:new ("classpath:application.xml");
看AbstractApplicationContext.refresh();方法。
1、在 obtainFreshBeanFactory 里会解析xml配置,把所有bean注册到beanDefinitionNames,包含我们自定义的BeanPostProcessor。
2、先看 prepareBeanFactory:
这里添加了三个spring默认的BeanPostProcessor。
3、再看 postProcessBeanFactory:
这个是(AbstractApplicationContext的子类)的方法咐猜,它同样也添加了一个默认的BeanPostProcessor。
4、接着看 :
这里设置一下TempClassLoader,并重新添加LoadTimeWeaverAwareProcessor,后面会单独开个文章分析内建的这衡扮型些类的作用。
5、继续看 registerBeanPostProcessors:
registerBeanPostProcessors 会继续添加一个内建Processor:
然后从容器取出所有Processor(包括我们自定义的)进行排序,再添加回beanFactory(先移除后添加),最后重新把ApplicationListenerDetector添加到末尾:
6、最后看一下调用过程吧, :
在这里beanFactory.preInstantiateSingletons();(在子类DefaultListableBeanFactory里实现)
再到其父类的AbstractBeanFactory.doGetBean->. createBean ->resolveBeforeInstantiation
就是在这里回调接口的2个方法,其实这里允许直接返回bean了。继续看resolveBeforeInstantiation后面的 doCreateBean ,同样会在里回调,不过只是针对某一类型的Processor:
doCreateBean 的另一个方法initializeBean里也会做回调:
执行完 createBean 后,再回到其父类的AbstractBeanFactory. getObjectForBeanInstance (该方法就是处理FactoryBean类型的bean)
调到其父类FactoryBeanRegistrySupport.getObjectFromFactoryBean
到.(在doGetObjectFromFactoryBean后执行,该方法正是初始化bean的过程)->
至此,完.
❻ Spring事件监听机制源码解析
1.Spring事件监听体系包括三个组件:事件、事件监听器搜哪,事件广播器。
事件:定义事件类型和事件源,需要继承ApplicationEvent。
事件监听器:用来监听某一类的事件,并且执行具体业务逻辑,需要实现ApplicationListener 接口或者需要用@ListenerEvent(T)注解。好比观察者模式中的观察者。
事件多播器:负责广播通知所有监听器,所有的事件监听判猛器都注册在了事件多播器中。好比观察者模式中的被观察者。Spring容器默认生成的是同步事件多播器。可以自定义事件多播器,定义为异步方式。
创建 的过程中,会执行refresh()中的()方法。该方法先获取bean工厂,然后判断工厂是否包含了beanName 为 applicationEventMulticaster的bean。如果包含了,则获取该bean,赋值给applicationEventMulticaster 属性。如果没有,则创建一个 对象,并且赋值给 applicationEventMulticaster 。实现了源码如下:
监听器的注册有两种,通过实现 ApplicationListener接口或者添加@EventListener注解。
注册的逻辑实现在refresh()中的registerListeners()方法里面。第一步,先获取当前ApplicationContext中已经添加的 applicationListeners(SpringMVC源码中有用到),遍历添加到多播器中。第二步,获取实现了ApplicationListener接口的listenerBeanNames集合,添加至多掘漏桥播器中。第三步,判断是否有早期事件,如果有则发起广播。
思考一下,上面的代码中第二步为啥添加的是listenerBeanName?
如果监听器是懒加载的话(即有@Lazy 注解)。那么在这个时候创建监听器显然是不对的,这个时候不能创建监听器。所以添加监听器到多播器的具体逻辑放在初始化具体的监听器之后。通过 BeanPostProcessor 的接口实现。具体的实现类是 ApplicationListenerDetector 。这个类是在 refreah()中prepareBeanFactory()方法中添加的。代码如下:
在创建 的构造方法中,会执行org.springframework.context.annotation.AnnotationConfigUtils#(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) 方法。这个方法中会添加两个 beanDefs, 代码如下:
EventListenerMethodProcessor:事件监听器的BeanFactory后置处理器,在前期会创建 DefaultEventListenerFactory ,后期在创建好Bean之后,根据 EventListener 属性,调用DefaultEventListenerFactory创建具体的 。
DefaultEventListenerFactory:监听器的创建工厂,用来创建 。
EventListenerMethodProcessor 的类继承图如下:
在refreash的()中会调用 org.springframework.context.event.EventListenerMethodProcessor#postProcessBeanFactory方法,获取EventListenerFactory 类型的 Bean。代码如下:
在 org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons 方法中,创建完所有的单例Bean 之后,会遍历所有Bean是否实现了 SmartInitializingSingleton 接口。如果实现接口会执行该 Bean 的 afterSingletonsInstantiated() 方法。代码如下:
org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated 中会调用私有方法 processBean()进行 ApplicationEventAdatper 的创建。代码如下:
可以通过调用 org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) 方法进行事件的调用。代码如下:
中的 multicasEvent,invokeListener,doInvokeListener 三个方法代码如下:
SpringMVC中就是通过Spring的事件机制进行九大组件的初始化。
监听器定义在FrameworkServlet类中,作为内部类。代码如下:
监听器的添加在org.springframework.web.servlet.FrameworkServlet# 中进行。通过SourceFilteringListener进行包装。添加代码如下:
在refresh中的registerListeners方法进行添加,代码如下:
在refresh中的finishRefresh()方法中,会调用publishEvnet(new ContextRefreshedEvent(this))发布事件。进行多播器广播,代码如下
最终会调到FrameworkServlet.this.onApplicationEvent(event)。
❼ Spring GateWay 路由源码分析
Spring Cloud提供了两套方便我们编写网关的中间件,分别是zuul和Spring GateWay,在zuul1的IO模型是使用BIO(图1-1)。而zuul2对IO模型使用NIO进行了重构(图1-2)。而Spring GateWay的IO模型是使用NIO。而在Netflix发布zuul2的时候Spring Cloud已经开始不集成到Spring Cloud中,因为Spring Cloud 等着zuul2集成太久,才有了Spring Gateway。Spring GateWay的架构是基于Spring webflux的基础上开发的。而对webflux的RP中涉及的Back Pressure、Stream、asynchronous好处不多说哈哈。
在Spring mvc是通过HandlerMapping解析请求链接,然后根据请求链接找到执行这个请求Controller类 。而在Spring GateWay中也是使用HandlerMapping对请求的链接进行解析匹配对应的Route进行代理转发到对应的服务。图2-1为整个请求的流程,用户请求先通过DispatcherHandler找到对应GateWwayHandlerMapping,再通过GateWwayHandlerMapping解析匹配到对应的Handler。Handler处理完后,再经过Filter,最终到Proxied Service.
1.请求先由DispatcherHanlder进行处理,DispatcherHanlder初始化的时候会从IOC中查找实现HandlerMapping接口的实现类。然后保存到内部变量handlerMappings中,旦戚饥DispatcerHandler调用Handler方法迭代handler
Mappings中的HandlerMapping,
2.这里只讲解RoutePredicateHandlerMapping,因此然后调用RoutePredicateHandlerMapping中的获取路由的方法,当RoutePredicateHandlerMapping获取到对应的路由的时候会将Route存储到ServerWebExchanges的属性中,然后返回实现了WebHandler接口的FilteringWebHandler。FilteringWebHandler是一个存放过滤器的Handler。模返
3.最后DispatcherHanlder通过SimpleHandlerAdapter适配器的方式调用FilteringWebHandler的handler方法,FilteringWebHandler调用所有的过滤器,包括proxy filter。通过proxyFilter请求被代理的服务。处理完毕后,并仔桐将Response响应回去。
图3-1为handler类关系图。这里主要涉及到Spring GateWay相关类的探讨。如:Spring Webflux使用到的RouteFuntionMapping和SimpleUrlHandlerMapping等不做探讨。
HandlerMapping和Ordered接口主要定义了获取getHandler和当前hanler加载顺序。AbstractHandlerMapping在getHanlder封装了CORS处理。因为所有Handler都可能会涉及到CORS的处理,抽象到AbstractHandlerMapping处理,再提供了getHandlerInternal让子类实现具体的查找Handler的方法。
RoutePredicateHandlerMapping是处理获取路由的hanlder。Route
PredicateHandlerMapping中的RouteLocator是存储了我们启动的时候加载的路由对象信息。获取路由的时候,调用RoutePredicateHanlderMapping的getHandlerInternal方法从RouteLocator获取路由存放在ServerWebExchange中,返回webFilter。
RouteLocator主要作用是提供获取路由的类型。我们在分析Route
PredicateHandlerMapping的时候,知道RoutePredicateHandlerMapping获取路由是通过RouteLocator进行获取的。那么我们这里分析下RouteLocator加载路由。
Route主要为三部分:
最总的 RouteLocator是CachingRoutelocator。加载过程是自上而下进行创建。
第二种方式是通过Properties文件进行创建路由。Properties路由的创建包括:和.
第三种方式是通过MYSQL或者Reids、内存()方式创建路由。实现RouteDefinitionRepository接口实现接口中的方式。为默认方式。
Filter我们区分为全局Filter和RouteFilter
在转发过程分析中我们知道最终的代理请求是通过一个Proxy Filter进行请求Proxy Service,那么这个Proxy Filter就是NettyRoutingFilter。通过下面的源码我们可以看到在 proxyRequest.sendHeaders() .send(request.getBody().map(dataBuffer -> ((NettyDataBuffer) dataBuffer).getNativeBuffer())); 中请求Proxy Service.
❽ 详解Spring mvc工作原理及源码分析
Model 模型层 (javaBean组件 = 领域模型(javaBean) + 业务层 + 持久层)
View 视图层( html、jsp…)
Controller 控制层(委托模型层进行数据处理)
springmvc是一个web层mvc框架,类似struts2。
springmvc是spring的部分,其实就是spring在原有基础上,又提供了web应用的mvc模块。
实现机制:
struts2是基于过滤器实现的。
springmvc是基于servlet实现的。
运行速度:
因为过滤器底层是servlet,所以springmvc的运行速度会稍微比structs2快。
struts2是多例的
springmvc单例的
参数封装:
struts2参数封装是基于属性进行封装。
springmvc是基于方法封装。颗粒度更细。
⑴ 用户发送请求至DispatcherServlet。
⑵ DispatcherServlet收到请求调用HandlerMapping查询具体的Handler。
⑶ HandlerMapping找到具体的处理器(具体配置的是哪个处理器的实现类),生成处理器对象及处理器拦截器(HandlerExcutorChain包含了Handler以及拦截器集合)返回给DispatcherServlet。
⑷ DispatcherServlet接收到HandlerMapping返回的HandlerExcutorChain后,调用HandlerAdapter请求执行具体的Handler(Controller)。
⑸ HandlerAdapter经过适配调用具体的Handler(Controller即后端控制器)。
⑹ Controller执行完成返回ModelAndView(其中包含逻辑视图和数据)给HandlerAdaptor。
⑺ HandlerAdaptor再将ModelAndView返回给DispatcherServlet。
⑻ DispatcherServlet请求视图解析器ViewReslover解析ModelAndView。
⑼ ViewReslover解析后返回具体View(物理视图)到DispatcherServlet。
⑽ DispatcherServlet请求渲染视图(即将模型数据填充至视图中) 根据View进行渲染视图。
⑾ 将渲染后的视图返回给DispatcherServlet。
⑿ DispatcherServlet将响应结果返回给用户。
(1)前端控制器DispatcherServlet(配置即可)
功能:中央处理器,接收请求,自己不做任何处理,而是将请求发送给其他组件进行处理。DispatcherServlet 是整个流程的控制中心。
(2)处理器映射器HandlerMapping(配置即可)
功能:根据DispatcherServlet发送的url请求路径查找Handler
常见的处理器映射器:BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,
,(不建议使用)
(3)处理器适配器HandlerAdapter(配置即可)
功能:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展多个适配器对更多类型的处理器进行执行。
常见的处理器适配器:HttpRequestHandlerAdapter,,
(4)处理器Handler即Controller(程序猿编写)
功能:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
(5)视图解析器ViewReslover(配置即可)
功能:进行视图解析,根据逻辑视图名解析成真正的视图。
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
springmvc框架提供了多种View视图类型,如:jstlView、freemarkerView、pdfView...
(6)视图View(程序猿编写)
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)
引入相关依赖:spring的基本包、springmvc需要的spring-webmvc,日志相关的slf4j-log4j12,jsp相关的jstl、servlet-api、jsp-api。
因为DispatcherServlet本身就是一个Servlet,所以需要在web.xml配置。
一、使用默认加载springmvc配置文件的方式,必须按照以下规范:
①命名规则:-servlet.xml ====> springmvc-servlet.xml
②路径规则:-servlet.xml必须放在WEB-INF下边
二、如果要不按照默认加载位置,则需要在web.xml中通过标签来指定springmvc配置文件的加载路径,如上图所示。
将自定义的 Controller 处理器配置到 spring 容器中交由 spring 容器来管理,因为这里的 springmvc.xml 配置文件中处理器映射器配置的是 BeanNameUrlHandlerMapping ,根据名字可知这个处理器映射器是根据 bean (自定义Controller) 的 name 属性值url去寻找执行类 Handler(Controller) , 所以bean的name属性值即是要和用户发送的请求路径匹配的 url 。
根据视图解析路径:WEB-INF/jsps/index.jsp
功能:根据bean(自定义Controller)的name属性的url去寻找执行类Controller。
功能:自定义的处理器(Controller)实现了Controller接口时,适配器就会执行Controller的具体方法。
会自动判断自定义的处理器(Controller)是否实现了Controller接口,如果是,它将会自动调用处理器的handleRequest方法。
Controller接口中有一个方法叫handleRequest,也就是处理器方法。
因此,自定义的Controller要想被调用就必须实现Controller接口,重写Controller接口中的处理器方法。
❾ 【Spring源码配置文件解析】2. xml注入配置信息 & @Value
这样xml中配置的bean的属性就会被注入配置文件里面对应的值
首先xml中的bean会在扫描的过程中封装成BeanDefinition对象,property标签会被弄成一个ProprotyValue的集合放在BeanDefinition的ProprotyValues变量中,所以在xml解析完成之后的BeanDefinition的ProprotyValues变量是这样的
上节这个类收集了environment配置信息和本地配置信息,并把它放在了的propertySources属性中
最后创建了一个StringValueResolver对象会调用来处理配置信息的替换
接下来就是取出所有的BeanDefinition,看看beanDefinition中的属性中是否有${}表达式,有的话就替换
很多的属性都可以用${}来引用配置信息局橡物
重点看看属性是如何被替换的
在BeanDefinitionVisitor.resolveValue方法中,String类型的走这
最终会调到创建的StringValueResolver匿名对象的实现方法中
这个匿名对象实现的方法如游又会调用来替换值,前面有提到所有的配置信息都在.propertySources中,那么接下来的工作就是从这个容器中找到对应的配置信息的key所对应的value
这里在入参时会创建一个PlaceholderResolver的匿名桐液对象,实现的resolvePlaceholder方法将会调用.getPropertyAsRawString()
最后返回了被替换成对应配置信息的值
这里就会调用前面创建的匿名对象的实现方法,方法体重会调用调用.getPropertyAsRawString(),去用key获取对应的配置信息
.getPropertyAsRawString()
PropertiesPropertySource对象内部有name,和source,source是一个泛型,当前类型为Properties,PropertiesPropertySource需要实现getProperty方法,其实就是从source中获取属性值
最后调到了Properties类的get方法,返回value
对每个beanDefination都这样操作过一遍
注意这个StringValueResolver的resolveStringValue会调用的方法来处理配置信息的替换,持有了所有的配置信息。 那么后面@Value的解析也将StringValueResolver来完成
@Value的解析工作是在Bean实例化后,属性注入的时候从配置文件找出并设置进去的
populateBean方法
只有string类型的才能@Value注解,才需要处理
又是这个容器,之前的doProcessProperties放进去的StringValueResolver
最后又会回到这个地方解析并注入值,和xml方式获取配置信息是一样的
找到对应的配置信息之后,反射设置这个属性的值
❿ SpringSecurity源码整体解析
spring对请求的处理过程如下:
而security所有认证逻辑作为特殊的一个Filter加入到spring处理servelet的过滤链中,即FilterChainProxy;
而这个FilterChainProxy内部又有多个过滤器链FilterChain,每个链有matcher,用于匹配请求,请求来时选择最先匹配的一条过滤器链做权限认证,每条过滤器链又由多个多滤器Filter依序连接而成。如下图:
configuator配置类装配到builder类中,builder类借助confuguator类构造filter或者filterChain
SpringSecurity有两个重要的builder:
首先生成FilterChainProxy实例,将FilterChainProxy实例再封装到DelegatingFilterProxy(java web的标准过滤器),作为一个web的Filter再注册到spring上下文。
至于this.webSecurity.build()内部怎么实现的,后面再讲。
将FilterChainProxy再封装到DelegatingFilterProxy,在注入到spring上下文。有两种方式,如下:
上面讲到了通过调用this.webSecurity.build()方法产生FilterChainProxy实例,现在仔细分析具体怎么实现的。
由上可见,主要是init、configure、performBuild三个方法
2.1. init(WebSecurity)
会遍历所有之前加载好的配置类configuator(adaptor),调用其init。
其中配置类的init方法,主要是构造了HttpSecurity,放入到securityFilterChainBuilders;并在postBuild之后设置inteceptor到websecurity。可见WebSecurityConfigurerAdapter类的init方法实现:
2.2. configure(WebSecurity)
会遍历所有之前加载好的配置类configuator(adaptor),调用其configure
配置类的configure方法,主要是配置上一步构造好的HttpSecurity实例,将其相关的configuator配置类装配到HttpSecurity实例。可见WebSecurityConfigurerAdapter类的configure方法实现:
我们在定义自己的过滤器链的时候,可以继承WebSecurityConfigurerAdapter重写configure方法,比如:
2.3. performBuild(WebSecurity)
遍历securityFilterChainBuilders(其实就是HttpSecurity)列表调用其build方法,生成SecurityFilterChain实例,最后利用多个SecurityFilterChain实例组成List,再封装到FilterChainProxy。
securityFilterChainBuilders(其实就是HttpSecurity)的build方法,内部最后也是调用自己的init、configure、performBuild。
2.3.1. init(HttpSecurity)
会遍历所前含有之前加载好的配置类configuator,调用其init
配置类的init一般是配置HttpSecurity。以HttpBasicConfigurer配置类为例:
2.3.2. configure(HttpSecurity)
会遍历所有之前加载好的配置贺袜类configuator,调用其configure
配置类的configure一般构造Filter,添加到HttpSecurity的禅悔激Filter列表中,作为过滤器链的其中一个。以HttpBasicConfigurer配置类为例:
2.3.3. performBuild(HttpSecurity)
将List<Filter>以及matcher封装成SecurityFilterChain
请求到达的时候,FilterChainProxy的dofilter()方法内部,会遍历所有的SecurityFilterChain,匹配url,第一个匹配到之后则调用该SecurityFilterChain中的List<filter>依次做认证或鉴权,执行最后调用外层传入的filterchain,将控制权交给上层过滤链,即spring 的过滤链。
核心逻辑如下: