SpringFramework源码阅读
[TOC]
Spring源码
Spring IOC源码
- 抓住主干:
- 解析配置
- 定位与注册对象
- 容器初始化主要做的事情:配置文件读取,Resource解析,BeanDefinition注册到容器
- 注入对象
- xml(注解)配置的资源定位、加载、解析、注册全链路分析
BeanDefinition
- org.springframework.beans.factory.config.BeanDefinition的定义
- 在JVM中使用java.lang.Class对象来描述对象。
- 在spring中使用
org.springframework.beans.factory.config.BeanDefinition
来描述Bean的配置信息 - 常用属性
@Scope
作用范围@Lzy
决定Bean实例是否延迟加载Primary
:设置为true的bean会是优先的实现类- 工厂方法提现:factory-bean(工厂bean名称)和factory-method(工厂方法名称)(@Configuration和@Bean)
- spring中有大量的接口,如果某个类实现了接口,就说明这个接口有某种能力。
- 源码架构图
org.springframework.beans.factory.support.RootBeanDefinition
:用来合并具有继承的BeanDefinationorg.springframework.core.AttributeAccessor
:提供访问和修改元数据信息的能力org.springframework.beans.factory.support.GenericBeanDefinition
:在spring 2.5之后,是RootBeanDefinition和ChildBeanDefinotion的替换,用于bean的配置文件类
BeanFactory
org.springframework.beans.factory.BeanFactory
的定义- 要求所有的IOC容器都要实现该接口
org.springframework.beans.factory.BeanFactory
和org.springframework.beans.factory.FactoryBean
的区别- BeanFactory是顶级的容器接口,定义了IOC容器最基本的功能,是管理Bean的容器,等价工厂方法的抽象工厂方法
- FactoryBean基于getObject()可以使用一套更加复杂的逻辑生成bean,用来生成bean,等价于在工厂方法的具体工厂方法。
- 架构图
- 那么多接口是因为架构的设计遵循设计模式单一职责原则
AutowireCapableBeanFactory
:组件扫描和组件装配
- ?
ApplicationContext(上下文)
- spring的IOC容器分为2类:
org.springframework.beans.factory.BeanFactory
为主的简单IOC容器,已org.springframework.context.ApplicationContext
为高级的IOC容器- BeanFactory是spring IOC容器的基本设施(面向spring自身),与用户打交道的ApplicationContext的高级IOC容器,除了创建bean之外很多额外的功能
- ApplicationContext常用容器
- 传统的基于XML配置的经典容器
- org.springframework.context.support.FileSystemXmlApplicationContext:从文件系统加载配置
- org.springframework.context.support.ClassPathXmlApplicationContext:从classpath加载配置
- org.springframework.web.context.support.XmlWebApplicationContext:用于web应用程序的容器
- 目前比较流行的容器
- AnnotationConfigServletWebServerApplicationContext(springboot)
- AnnotationConfigReactiveWebServerApplicationContext(springboot响应式编程)
- AnnotationConfigApplicationContext(spring,基于注解的容器)
- 传统的基于XML配置的经典容器
DefaultListableBeanFactory
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
Resource
- org.springframework.core.io.Resource
- ResourceLoader:根据资源自动地址自动选择正确的Resource
- 自动识别”classpath:”、’file:” 等资源地址前缀
- 支持自动解析Ant风格带通配符的资源地址(路径匹配表达式,用来对URI进行匹配)
ResourceLoad
- org.springframework.core.io.DefaultResourceLoader#getResource
- 大部分Resource的实现逻辑:根据不同的路径返回不同的resouce实例
- 非简单工厂模式(黑盒,用户完全不了解内部),而是策略模式
BeanDefinitionReader
- org.springframework.beans.factory.support.BeanDefinitionReader:使用ResourceLoad读取配置来转换为BeanDefinition。
- org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>):核心逻辑
- 问:BeanDefinitionDocumentReader documentReader替换为(DefaultBeanDefinitionDocumentReader):错误!!!!!DefaultListableBeanFactory通过实现了BeanDefinitionRegistry,实现
Map<String, BeanDefinition> beanDefinitionMap
作为BeanDefinition存储的载体。在分析XmlBeanDefinitionReader的时候,DefaultListableBeanFactory通过献祭自己转换为BeanDefinitionRegistry给XmlBeanDefinitionReader使用
DefaultBeanDefinitionDocumentReader
- org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
- 事情监听:xml中的import标签和alias标签完成后都发送事件
- org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition,把创建好的BeanDefinition注册到容器BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
BeanDefinitionRegistry
AbstractBeanDefinitionReader
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { |
- 细节:也就是说可以把IOC容器(DefaultListableBeanFactory和ApplicationContext)当成ResourceLoad来使用
PostProcessor
- postProcessor作为容器或者bean的后置处理器
- 本身也是一种需要注册到容器的bean
- 其里面的方法会在特定的时机被容器调用
- 实现不改变容器或者Bean核心逻辑的情况下对Bean进行扩展.
- 对bean进行包装,影响其行为、修改bean的内容等
- 种类:大类分为容器级别的后置处理器以及Bean级别的后置处理器
- BeanDefinitionRegistryPostProcessor(容器级别)
- BeanFactoryPostProcessor(容器级别)
- BeanPostProcessor(bean级别)
- org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor:
- 允许在正常的BeanFactoryPostProcessor检测开始之前注册更多的自定义beandefinition
- org.springframework.beans.factory.config.BeanFactoryPostProcessor:
- org.springframework.beans.factory.config.BeanPostProcessor:
- 责任链模式:为什么能能够在容器内调用postprocessor方法?
- 排序好的postprocessor按照责任链模式来执行
Aware(可感知)
- 从Bean里面获取到的容器实例并对其进行操作
- 需要获取容器能力的实现,实现特定的Aware接口,就能得到该能力。在创建该bean的时候,将能力赋值给bean。
- 本身也必须是bean
AbstractApplicationContext#refresh(模板方法)
prepareRefresh();刷新前的工作准备
obtainFreshBeanFactory();获取子类刷新后的内部beanFactory实例,对于xml这里对beanDefinition进行解释了注册,注解比较简单
prepareBeanFactory(beanFactory);为容器注册必要的系统级别的Bean
postProcessBeanFactory(beanFactory);允许容器的子类去注册postProcessor
invokeBeanFactoryPostProcessors(beanFactory);调用容器注册的容器级别的后置处理器(而注解的方式,是在容器后置处理器警醒解析和注册的)
registerBeanPostProcessors(beanFactory);向容器注册bean级别的后置处理器
initMessageSource();初始化国际化配置
initApplicationEventMulticaster();初始化事件发布者组件
onRefresh();在单例bean初始化之前预留给子类初始化其他特殊bean的口子
registerListeners();向前面的事件发布者组件注册事件监听器
finishBeanFactoryInitialization(beanFactory);设置系统级别的服务,实例化所有非懒加载的单例(依赖注入)
finishRefresh();触发初始化完成的回调方法,并发布容器刷新完成的事件给监听者
resetCommonCaches();重置spring内核中的共用缓存
依赖注入
- AbstractBeanFactory
- doGetBean
- 尝试从缓存获取bean(bean实例或者factoryBean.getObject()方法)
- 循环依赖的判断
- 递归去父容器获取bean实例
- 从当前容器获取beanDefinition实例
- 递归实例化显示依赖的bean(depengs-on,后者会先实例化)
- 依据不同的scope采用不同的策略创建bean实例
- 对bean进行类型检查
- doGetBean
- DefaultSingletonBeanRegistry
- AbstractAutowireCapableBeanFactory
- createBean
- bean类型解析
- 处理方法覆盖(lookup-method和replace-method等替换bean属性)
- bean实例化前的后置处理
- doCreateBean
- doCreateBean
- 创建bean实例(工厂方法,含参构造器注入、无参数构造器注入)
- 记录下被@Autowired或者@value标记上的方法和成员变量(通过MergedBeanDefinitionPostProcessor的后置处理器,将其放到InjectionMetadata中,为后面依赖注入做准备,还是责任链模式)
- 是否允许提前暴露(bean是单例&&允许循环依赖&&正在创建的)
- 填充bean属性
- initializabean(beanpostProcessor)
- 如果实现了aware接口,则设置值
- Aop:初始化前操作
- Aop:初始化操作
- Aop:初始化后操作
- 注册相关销毁逻辑
- 返回创建好的实例
- AbstractAutowireCapableBeanFactory#populateBean(属性填充)
- postProcessAfterInitialization:在设置属性前去修改bean状态,也可以控制是否继续给bean设置属性值
- 注入属性到propertyValue中(按名字装配 or 按类型装配)
- postProcessPropertyValue:对解析完但是未设置的属性进行再处理
- 是否进行依赖检查
- 将propertyValue中的属性值设置到beanwrapper中
- createBean
- AutowiredAnnotationBeanPostProcessor
- DefaultListableBeanFactory
- DependencyDescriptor
spring循环依赖
- 针对prototype的循环依赖,spring无解,直接抛出异常
- spring不支持显示循环依赖,(depends-on)
- 静态的file不支持依赖注入,代码报错
- 要想框架设计的好(强大和灵活),就要把功能拆拆的够细,每个过程都留有可操作的空间,使用责任链模式完美
- 一图胜前文
- 循环依赖的情况
- 构造器循环依赖(Singleton、prototype)
- Setter注入循环依赖(Singleton、prototype)
- 总结:构造器或者prototype都不支持循环依赖
- 三级缓存解决的问题:
- 解决了单例循环依赖问题
- 单例的唯一性问题
- prototype无三级缓存(故不支持)
- AbstractAutowireCapableBeanFactory#createBeanInstance(构造器依赖注入,不支持循环依赖的问题,因为这里还没有使用到缓存)
- 总结:只支持setter注入的单例循环依赖
- singletonObjects:一级缓存
- singletonFactories:三级缓存
- earlySingletonObjects:二级缓存
BeanWrapperImpl
- 功能:修改bean里面的属性
Spring AOP源码
AOP源码流程
- 注册解析AOP的服务
- 解析和加载横切逻辑
- 将横切逻辑织入目标Bean中
- 具体实现细节
- Bean级别的后置处理器的执行时机分析
- SmartInstantiationAwareBeanPostProcessor及其子类
- abstractAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator的注册
- @import:BeanDefinitionRegistry.postProcessBeanDefinitionRegistr
- 将切面逻辑解析成一个个advisor实例
- InstantiationAwareBeanPostProcessor . postProcessBeforeInstantiation
- @aspect的解析和加载
- 针对每个bean筛选出匹配的advisor并创建动态代理
- cglib or jdk动态大理
- BeanPostProcessor . postProces sAfterInitialization
- 通过调用链按顺序递归执行被代理的方法
- Bean级别的后置处理器的执行时机分析
BeanPostProcessor
- InstantiationAwareBeanPostProcessor:
- 在bean的实例化过程中(在实例化(依赖注入附上属性)之前的),给bean加上额外的逻辑。它区别于BeanPostProcessor,里面的方法先执行。
- InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation:用来在对象实例化前直接返回一个对象 (如代理对象)来代替通过内置的实例化流程创建对象
- InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation:在对象实例化完毕执行populateBean之前(在bean实例化之后),如果返回false则spring不再对对应的bean实例进行自动依赖注入
- SmartInstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- DestructionAwareBeanPostProcessor
- BeanPostProcessor的执行顺序
- 1——->InstantiationAwareBeanPostProcessor . postProcessBeforeInstantiation
- 2——– >SmartInstantiat ionAwareBeanPostProcessor.determineCandidateConstructors
- 3——– >MergedBeanDefinitionPostProces sor . postProces sMergedBeanDefinition
- 4——–>Instantiat ionAwareBeanPostProcessor . postProcessAfterInstantiation
- 5——–>InstantiationAwareBeanPostProcessor . postProces sProperties
- 6——— >BeanPostProcessor . postProcessBeforeInitialization
- 7——– >BeanPostProcessor . postProces sAfterInitialization
- 8——– >DestructionAwareBeanPostProces sor . postProcessBeforeDestruction
EnableAspectJAutoProxy
- boolean proxyTargetClass() default false;
- 表明该类采用CGLIB(true)代理还是使用JDK(false)的动态代理
- boolean exposeProxy() default false;
- Spring框架上下文中拿到当前代理对象
- AspectJAutoProxyRegistrar
通过注解将类当做Bean管理起来的方式
- @Controller @Service @Repository @Component标记的类
- @Bean标记的方法
- @Import标签
- 获取bean需要加上包名
AnnotationAwareAspectJAutoProxyCreator
- 基于注解的动态代理创建器
AdvisedSupport
- getInterceptorsAndDynamicInterceptionAdvice:递归方式,获取被代理方法的拦截器链(多个切面命中同一个类)
ProxyCreatorSupport
TargetSource
- SingletonTargetSource
- PrototypeTargetSource
- CommonsPool2TargetSource
- ThreadLocalTargetSource
- HotSwappableTargetSource
解析和加载横切逻辑
- AbstractAutowireCapableBeanFactory#createBean()
- AbstractAutoProxyCreator#postProcessBeforeInstantiation
- AbstractAutoProxyCreator#shouldSkip
- AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
- 合并注解和非注解的advisors
- BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
- 从容器获取所有beanName
- 遍历beanName,解析出被@Aspect标记的类
- 提出aspect类哩的advisors
- 将提取的结果加入缓存
将横切逻辑织入目标Bean中
- 在bean初始化完之后才织入横切逻辑。入口:AbstractAutowireCapableBeanFactory#initializeBean()
- postProcessAfterInitialization :正常流程的织入入口
- getEarlyBeanReference :循环依赖的织入入口
- 一图胜千文系列
泛型Generics
让数据类型变得参数化
- 定义泛型时,对应的数据类型是不确定的
- 泛型方法被调用时,会指定具体类型
- 核心目标:解决容器类型在编译时安全检查的问题
泛型类
泛型的参数不支持基本类型
泛型相关的信息不会进入到运行时阶段
能否在泛型里面使用具备继承关系的类?
使用通配符
?
,但是会使得泛型的类型检查失去意义给泛型加入上边界
? Extends E
给泛型加入下边界
? super E
泛型接口
- 与泛型类的用法基本相同,常用于数据类型的生产工厂接口中
泛型方法
- 既能用在泛型类、泛型接口里,也能用在普通类或者接口里
- 泛型方法不被泛型类约束
泛型字母的含义
- E- Element :在集合中使用,因为集合中存放的是元素
- T- Type : Java类
- K-Key :键
- V-Value:值
- N-Number:数值类型
工厂模式
简单工厂模式
- 定义:一个工厂类,根据传入的参数的值不同返回不同的实例
- 特点:被创建的实例具有共同的父类或接口
- 适用场景:
- 需要创建的对象较少
- 客户端不关心对象的创建过程
- 优缺点:
- 优点:可以对创建的对象进行”加工”, 对客户端隐藏相关细节
- 缺点:因创建逻辑复杂或创建对象过多而造成代码臃肿
- 缺点:新增、删除子类均会违反开闭原则
- 开闭原则:一个软件实体(一个函数、类、模块),应该对扩展开放,对修改关闭
工厂方法模式
- 定义:一个用于创建对象的接口,让子类决定实例化哪一个类。(对类的实例化延迟到其子类)
- 优缺点:
- 优点:符合开闭原则、符合单一职责原则
- 优点: 对客户端隐藏相关细节(是简单工厂模式的进一步抽象和扩展)
- 缺点:添加子类的时候”拖家带口”(类的个数指数增加,系统复杂和臃肿)
- 缺点:只支持同一类产品的创建
抽象工厂模式
- 定义:提供一个创建一系列相关或者相互依赖对象的接口。(对工厂模式进行了抽象)
- 抽象工厂模式侧重的是同一产品族
- 工厂方法模式更加侧重于同一产品等级
- 优缺点:
- 优点:解决了工厂模式只支持生成一种产品的弊端
- 优点:新增一个产品族,只需要增加一个新的具体工厂,不需要修改代码
- 缺点:添加新产品时依旧违反开闭原则
反射
- 定义:允许程序在运行时来进行自我检查并且对内部的成员进行操作
- 作用:
- 在运行时判断任意一个对象所属的类
- 在运行时获取类的对象
- 在运行时访问java对象的属性,方法,构造方法等
java.lang.reflect
类库中主要的类- Field : 表示类中的成员变量
- Method : 表示类中的方法
- Constructor :表示类的构造方法
- Array :该类提供了动态创建数组和访问数组元素的静态方法
- 反射依赖的
Class
- 用来表示运行时类型信息的对应类
- 每个类都有唯一一个 与之相对应的Class对象
- Class类为类类型,而Class对象为类类型对象
- 特点
- Class类也是类的一种, class则是关键字
- Class类只有一个私有的构造函数,只有JVM能够创建Class类的实例
- JVM中只有唯一一个和类相对应的Class对象来描述其类型信息
- 获取Class对象的三种方式
- Object 一> getClass()
- 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
- 通过Class类的静态方法: forName(String className)(常用)
- 在运行期间,一个类,只有一个与之相对应的Class对象产生(单例,构造方法私有)
- Class对象就像一面镜 子,透过这面镜子可以看到类的结构
- 反射的主要用法
- 如何获取类的构造方法并使用
- 如何获取类的成员变量并使用
- 如何获取类的成员方法并使用
- 用来表示运行时类型信息的对应类
- 反射的获取源
- 用XML来保存类相关的信息以供反射调用
- 以注解来保存类相关的信息以供反射调用
注解
- 定义:提供一种为程序元素设置元数据的方法
- 元数据:元数据是添加到程序元素如方法、字段、类和包上的额外信息
- 注解不能直接干扰程序代码的运行
- 注解是一分钟分散式的元数据设置方式,XML是集中式的设置方式
java.lang.annotation.Annotation
- 所有的注解都继承自该接口
public interface demo.annotation.TestAnnotation extends java.lang.annotation.Annotation
- 编译(.java->.class)和反编译(
javap -verbose
)- javac将.java编译为.class信息
javap -verbose
反编译为可读的信息
- 注解的功能
- 作为特定的标记,用于告诉编译器一些信息
- 编译时动态处理,如动态生成代码
- 运行时动态处理,作为额外信息的载体,如获取注解信息
- 注解的分类
- 标准注解 : @Override. @Deprecated. @SuppressWarnings
- 元注解 : @Retention. @Target. @Inherited. @Documented
- 自定义注解
- 元注解:用于修饰注解的注解,通常用在注解的定义上
@Target
:注解的作用目标@Retention
:注解的生命周期@Documented
:注解是否应当被包含在JavaDoc文档中@Inherited
:是否允许子类继承该注解
- 注解的底层实现(JDK动态代理)
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
,展示JDK动态代理中间类细节。-XX:+TraceClassLoading
,追踪类加载详情
- 注解的工作原理
- 通过键值对的形式为注解属性赋值
- 编译器检查注解的使用范围,将注解信息写入元素属性表
- 运行时JVM将RUNTIME的所有注解属性取出并最终存入Map里
- 创建AnnotationInvocationHandler实例并传入前面的map
- JVM使用JDK动态代理为注解生成代理类,并初始化处理器
- 调用invoke方法,通过传入方法名返回注解对应的属性值
- 意义
- 使用注解标记需要工厂管理的实例,并依据注解属性做精细控制
控制反转
- 控制反转IOC:Inversion of Control
- 依托一个类似工厂的IoC容器
- 将对象的创建、依赖关系的管理以及生命周期交由IoC容器管理
- 降低系统在实现上的复杂性和耦合度,易于扩展,满足开闭原则
- 依赖注入(Dependency Inversion)
- 含义:把底层类作为参数传递给上层类,实现上层对下层的控制,而不是上层类直接new对象
- 注入方式:Set注入、接口注入、注解注入、构造器注入
- 梳理依赖倒置原则、IOC、DI/IOC容器的关系
- 关系图
- IOC容器的优势
- 避免在各处使用new来创建类,并且可以做到统一维护
- 创建实例的时候不需要了解其中的细节
- 反射+工厂方法模式的合体,满足开闭原则
自研框架IOC
- 自研IOC架构最基本功能:
- 解析配置
- 定位(注解)和注册对象
- 注入对象
- 提供通用的工具类
- 实现的点:创建注解,提取标记对象,实现容器,依赖注入
- 创建注解
- 提取标记对象
- org.simpleframework.util.ClassUtil#extractPackageClass,需要实现的功能:
- 获取到类的加载器(获取项目发布的实际路径)
- 通过类加载器获取到加载的资源信息
- 依据不同的资源类型,采用不同的方式获取资源的集合
- 为什么不让用户传入绝对路径?
- 不够友好:不同机器之间的路径可能不相同
- 如果打的是war包或者jar包,根本找不到路径
- 因此通用的做法是通过项目的类加载器来获取
- org.simpleframework.util.ClassUtil#extractPackageClass,需要实现的功能:
- java.lang.Class#isAssignableFrom:判断某个类是否实现该接口或者是该类的子类
模板方法模式
- 定义:围绕抽象类,实现通用逻辑,定义模板结构,部分逻辑由子类实现(复用代码、反向控制)
- 模板方法种类:
- 模板方法:包括下面三种方法
- 具体方法:模板里自带的实现方法,不改变
- 钩子方法:子类可以依据情况实现的方法
- 抽象方法:必须让子类实现的方法
事件监听器模式
- 回调函数:往组件(new Thread())注册自定义的方法(Runnable#run())以便组件在特定场景下调用(thread.start())
- 事件监听器模式:监听器将监听感兴趣的事件,一旦事件发生,便做出响应。观察者模式的一种实现,解耦。
- 事件源 ( Event Source ):发布事件,注册监听器
- 事件监听器 ( Event Listener )
- 事件对象 ( Event Object )
- 观察者模式:事件源+事件对象=主题主键,事件监听器=观察者
- spring的事件驱动模型
- 事件:ApplicationEvent抽象类
- 事件监听器:ApplicationListner(方式一:实现ApplicationListner接口,方式二:加上
@EventListener
注解) - 事件发布器:Publisher以及Multicaster
- org.springframework.context.ApplicationEventPublisher:实现了这个接口就说明有能力发布容器事件了
- org.springframework.context.event.ApplicationEventMulticaster:多线程执行监听器的方法
- 事件源:ApplicationEvent的souce成员变量(容器本身)
- ApplicationContext使用DefaultListableBeanFactory作为代理,而ApplicationContext的使用ApplicationEventPublisher发布事件,.ApplicationEventMulticaster才真的执行发布事件
- org.springframework.context.PayloadApplicationEvent,
- org.springframework.core.ResolvableType
初体验SpringAOP
容器是OOP的高级工具,以低耦合低侵入的方式打通从上到下的开发通道。
AOP:关注点分离(Concern Separation),不同的问题交给不同的部分去解决,每部分专注解决自己的问题
- Aspect Oriented Programming就是其中一种关注点分离的技术
- 通用化功能的代码实现即切面Aspect
- Aspect之于AOP ,就相当于Class之于OOP , Bean之于Spring
AOP术语
- 切面Aspect :将横切关注点逻辑进行模块化封装的实体对象
- 通知Advice :好比是Class里面的方法,还定义了织入逻辑的时机
- 连接点Joinpoint ,允许使用Advice的地方
- spring aop默认只支持方法级别的jointpoint
- 切入点Pointcut :定义-系列规则对Joinpoint进行筛选
- 目标对象Target :符合Pointcut条件,要被织入横切逻辑的对象
- Advice的种类
- BeforeAdvice:在joinPoint前被执行的advice
- AfterAdvice : 好比try..catch..finaly里面的finally
- AfterReturningAdvice : 在Joinpoint执行流程正常返回后被执行
- AfterThrowingAdvice : Joinpoint执行过程中抛出异常才会触发
- AroundAdvice :在Joinpoint前和后都执行,最常用的Advice
- Introduction(引入型Advice,很少使用):
- 为目标类引入新接口,而不需要目标类做任何实现
- 使得目标类在使用的过程中转型成新接口]对象,调用新接口的方法
- 让任意一组类实现共同的接口,让其有能力转换为执行子类
- AOP是0OP里的”寄生虫”
- AOP需要OOP理解自己的语义,所以并不像单独使用这么灵活
- 织入:将Aspect模块化的横切关注点集成到OOP里
- 织入器:完成织入过程的执行者,如ajc
- Spring AOP则会使用一组类来作为织入器以完成最终的织入操作
单个Aspcet的执行顺序:
多个Aspect的执行顺序
源码分析
/**
* aspect使用大全
*
* @author codingprh
*/
public class ServiceAspect {
public void embed() {
}
public void before(JoinPoint joinPoint) {
System.out.println("开始调用" + joinPoint);
}
public void after(JoinPoint joinPoint) {
System.out.println("结束调用" + joinPoint);
}
public Object aroundme(JoinPoint joinPoint) {
long startTime = System.currentTimeMillis();
Object returnValue = null;
System.out.println("开始计时" + joinPoint);
try {
returnValue = ((ProceedingJoinPoint) joinPoint).proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("执行失败" + joinPoint);
} finally {
System.out.println("耗时=" + (System.currentTimeMillis() - startTime) + "ms");
}
return returnValue;
}
public void afterReturning(JoinPoint joinPoint, Object returnValue) {
System.out.println("无论是空置还是有值返回" + joinPoint + ",返回值是" + returnValue);
}
public void afterException(JoinPoint joinPoint, Exception exception) {
System.out.println("抛出异常通知" + joinPoint + ",异常信息为" + exception.getMessage());
}
public NoahDeclareParents noahDeclareParents;
}
AOP实现原理和代理模式
- 静态代理 :OOP的局限,下面是静态代理改进。
- 溯源ClassLoader
- 通过带有包名的类来获取对应class文件的二 进制字节流
- 根据读取的字节流,将代表的静态存储结构转化为运行时数据结构
- 生成一 个代表该类的Class对象,作为方法区该类的数据访问入口
- 根据一定规则去改动或者生成新的字节流,将切面逻辑织入其中
- 行之有效的方案就是取代被代理类的动态代理机制
- 根据接口或者目标类,计算出代理类的字节码并加载到JVM中去
- 动态代理总结:
- JDK动态代理:基于反射机制实现,要求业务类必须实现接口
- JDK原生,在JVM里运行较为可靠
- 平滑支持JDK版本的升级
- CGLIB :基于ASM机制实现,生成业务类的子类作为代理类
- 被代理对象无需事实现接口,能实现代理类的无侵入
- SpringAop同时使用JDK动态代理和CGLIB,默认策略,Bean实现了接口则用JDK,否则使用CGLIB
- JDK动态代理:基于反射机制实现,要求业务类必须实现接口
SpringAOP的实现原理之JDK动态代理
静态代理是生成.java硬编码生成的代理对象。而JDK动态代理是没生成代理的class对象。
程序要求运行时动态生成类的字节码,并加载到JVM中
要求【被代理的类】必须实现接口
并不要求【代理对象】去实现接口,所以可以服用代理对象的逻辑
java.lang.reflect.Proxy:创建动态代理类
- java.lang.reflect.Proxy#newProxyInstance:创建代理类
- ClassLoader loader:类加载器
- Class<?>[] interfaces:代理那一组接口
- InvocationHandler h:调用横切织入逻辑
- 生成的目标代理对象,对于代理对象对于用户来说是透明的
- java.lang.reflect.Proxy#newProxyInstance:创建代理类
java.lang.reflect.InvocationHandler:
- 等价
@Aspect
功能,只有实现了该接口才具有jdk动态代理的能力。管理横切逻辑 - invoke:唯一的方法
- Object proxy:代理对象
- Method method:目标对象的方法
- 等价
本质原理:生成*$Proxy的字节码,跟注解原理分析一致
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
SpringAOP的实现原理之CGLIB动态代理
- 代码生成库: Code Generation Library
- 不要求被代理类实现接口
- 内部主要封装了ASM Java字节码操控框架(等价使用es,而非使用es的底层lua)
- 动态生成子类以覆盖非final的方法,绑定钩子回调自定义拦截器(net.sf.cglib.proxy.MethodInterceptor)
- net.sf.cglib.proxy.MethodInterceptor:
- 具有回调能力,等价于 InvocationHandler,也就是等价于定义切面逻辑
- 前三个参数和java.lang.reflect.InvocationHandler的invoke一致
- 第四个参数MethodProxy:是动态代理方法(因此这里解决了jdk动态代理为什么要实现接口,还要传入被代理对象的实现)
- net.sf.cglib.proxy.Enhancer:
- 生成代理对象
自研AOP框架
使用CGLIB来实现:不需要业务类实现接口,相对灵活
解决标记的问题,定义横切逻辑的骨架
定义Aspect横切逻辑以及被代理方法的执行顺序
将横切逻辑织入到被代理的对象以生成动态代理对象
实现Aspect横切逻辑以及被代理方法的定序执行
- 创建MethodInterceptor的实现类
- 定义必要的成员变量—被代理的类以及Aspect列表
- 按照Order对Aspect进行排序
- 实现对横切逻辑以及被代理对象方法的定序执行
增强AOP功能,集成AspectJ,更细粒度的控制
- 提供了完整的AOP解决方案,是AOP的Java实现版本
- 定义切面语法以及切面语法的解析机制
- 提供了强大的织入工具
- 图文比较
- AspectJ框架的织入时机:静态织入(前面2中)和LTW(load time weaving)
- 编译时织入 :利用ajc(而不是javac) ,将切面逻辑织入到类里生成class文件
- 编译后织入:利用ajc ,修改javac编译出来的class文件
- 类加载期织入:利用java agent ,在类加载的时候织入切面逻辑
- AspectJ的性能比spring aop要强大,因为即使是CGLIB(底层是ASM)修改字节码也是运行时通过继承生成的代理对象,而JDK动态代理通过接口反射来生成代理对象,但是AspectJ是通过在类加载的时候就完成了织入逻辑。
- springAOP仅仅用到了AspectJ的切面语法,并没有使用ajc编译工具。因为完成动态代理在启动项目加载单例非延迟加载的对象,就完成了bean的代理对象生成,并且加入缓存,能够满足性能要求了。
- 提供了完整的AOP解决方案,是AOP的Java实现版本
适配器模式
org.springframework.aop.framework.adapter.AdvisorAdapter:适配器模式,只有在spring当中才有advisor的概念,通过this这个类转换为外部aop框架能够识别的MethodInterceptor。
一图胜前文
自研SpringMVC框架
- spring-mvc架构图:
- ControllerRequestProcessor实现分析
- 针对特定请求,选择匹配的Controller方法进行处理
- 解析请求里的参数及其对应的值,并赋值给Controller方法的参数
- 选择合适的Render ,为后续请求处理结果的渲染做准备
#SpringMVC源码分析
WebApplicationInitializer
- 架构图:介绍springmvc的tomcat容器和spring容器是怎么初始化的
- AbstractAnnotationConfigDispatcherServletInitializer#createRootApplicationContext:将service和repositories存放到该上下文中。分层设计,解耦。
- AbstractAnnotationConfigDispatcherServletInitializer#createServletApplicationContext:将springMVC相关的存放到该上下文中。
Spring MVC源码分析流程
- 建立请求和Controller方法的映射集合的流程
- 根据请求查找对应的Controller方法的流程
- 请求参数绑定到方法形参,执行方法处理请求,渲染视图的流程
DispatcherServlet
- 架构分析图
RequestMappingHandlerMapping
- 流程架构图:建立请求路径和Controoler方法的映射关系