Spring框架简单仿写
前言
本次仿写旨在初步理解spring底层和设计模式在框架中的应用
之前跟着视频写了一个极简的版本,IoC的基本原理大致过了一遍,简单的写了一下核心代码,没有太过深入,中间的逻辑没能清晰的理解
所以打算自己捋一捋
下面从无到有记录我整个仿写的过程
包名,类名,方法的实现,不一定是按源码来的,有些实现逻辑太过复杂就自己写了方法来实现,中间可能为了解耦进行了一下结构上的调整,跟源码是比不得的🤡
然后就是前面不懂,后面懂了进行的一些修改,欢迎评论指点
环境:
jdk1.8 + maven
功能介绍:
目标实现简单的IoC依赖注入,简单的aop日志功能
注解实现@ComponentScan扫描,@Component,@Autowired,@Scope
@Aspect,@Before,@After,@AfterReturning,@AfterThrowing(没有实现消息环绕)
未实现循环依赖,jdk动态代理,(以后有机会出个第二阶段)
代码仓库:本项目源码gitee
参考仓库:
- GitHub:https://github.com/fuzhengwei/book-small-spring
- Gitee:https://gitee.com/zxh904582599/imitation-spring-framework?_from=gitee_search
第一阶段结果展示
Step1 基本准备
新建两个文件夹,一个用来放框架源码,一个自己测试
在sy文件夹下创建一个启动类Test
确定要实现的功能
package sy;
import spring.context.AnnotationConfigApplicationContext;
import spring.context.ApplicationContext;
import sy.service.UserService;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
流程:
将配置文件传入,即开始向容器中注册bean
- 创建容器类,继承关系如下,这里只写三层,分析接口中有
getBean(String beanName)
方法
- 创建配置文件AppConfig,使用@ComponentScan的方式注册bean
@ComponentScan("sy.service")
public class AppConfig {
}
- 创建@ComponentScan注解
- 在包下创建UserService,并加上@Component注解
Step2 Bean的三大接口
概述
先扫描注册BeanDefinition,然后创建bean
生命周期:
- 实例化(依赖注入)
- aware回调
- BeanPostProcessor:before
- 初始化
- BeanPostProcessor:after(aop)
关于bean是怎么被创建出来的
使用注解@Component,即标记了该类需要被实例化成一个bean对象,并将该对象存在容器中
首先先要将标记的类进行注册,这里引入了一个类记录了bean的信息
- 创建BeanDefinition类,描述bean的信息,记录了bean所属的类型,方便通过反射实例化,是单例还是原型
类似于Class对象和类型
package spring.beans;
public class BeanDefinition {
private Class clazz;
private String scope;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
将被注解标记的类进行注册,每一个bean类对应一个BeanDefinition对象
- 创建BeanDefinitionRegistry接口,定义了BeanDefinition注册表的行为
具体代码见我源码,beanDefinition最终是保存在map中的,其需要的方法跟map的差不多
package spring.beans;
import spring.beans.config.BeanDefinition;
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
BeanDefinition getBeanDefinition(String beanName);
}
注册成功后,Spring就需要根据BeanDefinition对象来创建
那么,是由谁来创建呢?(自问自答)BeanFactory。
- 创建BeanFactory接口,象征性的写一个异常,得不到就报错
Object getBean(String beanName) throws BeansException;
由BeanFactory创建返回bean实例对象后,那么这个对象性(单例),就真正要被放到单例池中了
-
抽象单例池,贯穿面向抽象编程的思想,这里又是一个接口,SingletonBeanRegistry
单例池也是一个map,详情见源码Object getSingleton(String beanName); void registerSingleton(String beanName, Object singletonObject);
源码类图是这样的,我这里必不可能全写完,有些也不知道是干嘛的
关键是BeanFactory和SingletonBeanRegister之间的关系
也就是BeanFactory和单例池的关系
先不考虑循环依赖的问题,正常考虑
- 用户需要一个bean对象,问BeanFactory要
- BeanFactory根据beanName去BeanDefinition里面查,看看是单例还是原型
- 是单例,首先要判断单例池里面有没有这个bean ,有就从单例池里面拿,然后返回这个单例给用户,没有就报错,说明该单例没有扫进来
- 原型就创建直接返回
Step3 接口的初步实现和分析
借鉴了那本书的类图
详情见我的源码,简单描述一下
DefaultSingletonBeanRegistry:实现了单例注册表,包含单例池
AbstractBeanFactory,定义工厂模板
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory{
public Object getBean(String beanName) {
return doGetBean(beanName);
}
public <T> T getBean(String name, Class<T> requiredType) {
return (T) getBean(name);
}
protected abstract Object doGetBean(String beanName);
protected abstract BeanDefinition getBeanDefinition(String beanName);
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition);
}
AbstractAutowireCapableBeanFactory这个具体等下再说(后面我把createBean的部分放了进来,生命周期在这里定义好了)
DefaultListableBeanFactory:
由上图可看出,该类实现了所有接口和方法,意思就是说,spring容器getBean就是问它要
现在BeanDefinition对象有了,可以生产bean了
根据上面分析的,先写一个简单的逻辑
- 实现BeanDefinitionRegister注册表的方法
- getBean基本逻辑
@Override
public Object getBean(String beanName) {
// 先获得bean定义对象
if (containsBeanDefinition(beanName)) {
BeanDefinition beanDefinition = getBeanDefinition(beanName);
// 是单例还是原型
if (beanDefinition.isSingleton()) {
Object bean;
bean = getSingleton(beanName);
if (bean == null) {
bean = createBean(beanName,beanDefinition);
}
return bean;
} else if (beanDefinition.isPrototype()) {
// 创建bean对象
return createBean(beanName, beanDefinition);
} else {
throw new BeansException("未知scope");
}
}
return null;
}
-
创建bean
protected Object createBean(String beanName, BeanDefinition beanDefinition) { Class clazz = beanDefinition.getClazz(); try { // 根据类型创建实例 Object instance = clazz.getDeclaredConstructor().newInstance(); // 对该类下的成员变量进行依赖注入 // aware回调 // BeanPostProcessor:before // 初始化 // BeanPostProcessor:after // 返回实例 return instance; } catch ... }
Step4 启动类扫描
回到启动类
先看这个AbstractApplicationContext类,这里相当于定义了一个模板
-
容器里要有一个注册表
protected BeanDefinitionRegistry register = new DefaultListableBeanFactory();
-
启动流程的方法
void refresh();
-
重写getBean
@Override public Object getBean(String name) { return ((DefaultListableBeanFactory) register).getBean(name); }
实现类AnnotationConfigApplicationContext
容器启动的时候,根据配置文件进行扫描,注册beanDefinition对象
构造方法如下
public AnnotationConfigApplicationContext(Class configClass) {
reader(configClass);
refresh();
}
详情见源码,看一下自己写的那个深度优先,先注册beanDefinition,BeanPostProcessor后面再说
我这里没有
com
包,就报错了
获得beanName
private String getBeanName(Class<?> clazz) {
if(!clazz.isAnnotationPresent(Component.class)){
throw new BeansException("no such bean");
}
Component component = (Component)clazz.getAnnotation(Component.class);
if ("".equals(component.value())){
// 获得类名
String[] splits = clazz.getName().split("\\.");
String tempBeanName = splits[splits.length-1];
// 首字母小写
char[] nameCharArray = tempBeanName.toCharArray();
nameCharArray[0] += 32;
return new String(nameCharArray);
}
return component.value();
}
简单测试一下
现在beanDefinition对象有了,BeanFactory要开始创建bean实例了
DefaultListableBeanFactory新建方法
public void preInstantiateSingletons(){
for(String beanName: beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = getBeanDefinition(beanName);
if (beanDefinition.isSingleton()) {
Object bean = createBean(beanName,beanDefinition);
System.out.println("注册单例:"+beanName);
registerSingleton(beanName, bean);
}
}
}
抽象容器类中调用beanFactory.preInstantiateSingletons()
测试结果
捋一捋,准备写依赖注入了
Step5 初步依赖注入
初步依赖注入
新建一个类OrderService,注册bean
@Component
public class OrderService {
public void test(){
System.out.println("我是orderService");
}
}
UserService中实现依赖注入,在test中调用orderService的方法
新建注解@Autowired
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test() {
System.out.println("我是userService");
orderService.test();
}
}
遍历该类的每个字段,是否标记Autowired,
如果有则从单例池里获得bean对象
单例池里有则直接返回,没有就创建这个bean,加入单例池,返回(这一部分getBean中已经前面已经写了)
for (Field field : clazz.getDeclaredFields()){
if(field.isAnnotationPresent(Autowired.class)){
Object bean = getBean(field.getName());
field.setAccessible(true);
field.set(instance, bean);
}
}
测试结果
BeanNameAware和初始化
简单写一下,平常用的少
Aware相当于Spring给用户提供的一个接口,用户通过这个接口可以获得Spring容器内部的单例对象
- 新建接口BeanNameAware
public interface BeanNameAware {
void setBeanName(String name);
}
-
现在UserService想获得自己的beanName,这里注入到自己的成员属性中
-
-
在bean声明周期中实现aware接口的回调
if( instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
简单进行输出测试,初始化也差不多,也是实现接口,Spring来回调
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
Step6 BeanPostProcessor
- 创建BeanPostProcessor接口
public interface BeanPostProcessor {
// 没有操作默认返回其本身
default Object postProcessBeforeInitialization(Object bean,String beanName){
return bean;
}
default Object postProcessAfterInitialization(Object bean,String beanName) throws Exception {
return bean;
}
}
BeanPostProcessor是bean被创建出来前,对原始实例提供的一些加工处理,返回的是一个对象(代理对象)
实现起来跟前面的有些不一样
BeanPostProcessor把这种对bean的操作的行为抽象出来作为一个对象
一个BeanPostProcessor对象就代表着对bean的一种操作
所以可以有一组BeanPostProcessor对象
写在createBean方法中就代表着每创建一个bean就要经过BeanPostProcessor的加工
所以BeanPostProcessor的作用范围是全局的,当然也可以在BeanPostProcessor对象内部设置过滤筛选
理解了这一点,后面的aop就好写了
经前面分析可知,BeanPostProcessor对象需要提前被加载进来,那么首先实现BeanPostProcessor接口的类要能被Spring扫描到
-
创建类MyBeanPostProcessor实现BeanPostProcessor接口
package sy.service; import spring.annotation.Component; import spring.beans.BeanPostProcessor; /** * 自定义处理bean */ @Component public class MyBeanPostProcessor implements BeanPostProcessor { }
现在不得不感叹这个BeanDefinition被设计出来真的太绝了,注册和创建分开,中间可以做很多事情
看我之前写的容器类,register方法注册BeanDefinition然后才启动容器(一开始写的方法名是reader)
这样,这样根据beanDefinition对象的Class属性来判断该类是否实现了BeanPostProcessor接口,这样就可以先注册beanPostProcessor对象了,等到真正实例化单例的时候就能保证beanPostProcessor对象池子里是有的
- BeanPostProcessor的注册
这里不得不提一嘴BeanFactoryPostProcessor
根据前面的生命周期图可类比,我们可以对每个类实例加工成bean,比如给这个实例加一些属性啊,自定义一些方法啊之类的
BeanFactoryPostProcessor就是对BeanFactory“做手脚”了,beanFactory被修改,那么就可以扩展多种生产bean的方式,如不同的注册BeanDefinition的方式啦(这里只用一种,中间的读操作就没做抽象),生产特殊功能的bean啦,等等
bean工厂中里添加容器和其对应的方法
private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
从注册的BeanDefinition中找到实现接口的类,在bean工厂中编写一个方法
public ArrayList<String> getBeanNamesForType(Class objClass) {
ArrayList<String> list = new ArrayList<>();
for (String key : beanDefinitionMap.keySet()) {
BeanDefinition value = beanDefinitionMap.get(key);
if (objClass.isAssignableFrom(value.getClazz())) {
list.add(key);
}
}
return list;
}
象征性的写一个委托类 PostProcessorRegistrationDelegate(介绍)
用来注册BeanPostProcessor的,实现了根据优先级对BeanPostProcessor进行排序
创建两个接口
接口中的常量,默认+public+、+static+、+final+
在容器类中调用
private void registerBeanPostProcessors(DefaultListableBeanFactory beanFactory){
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory);
}
- 在生命周期中添加逻辑
运行测试
这里要注意一下,遍历BeanPostProcessor池和创建单例这两个过程是分开的
Step7 AOP
Aop就是在postProcessorAfterInitialzation
这一步执行的,但是Aop不同的一点是针对某一特定的类实现的,不是针对全局。
这一部分的实现对源码的参考相对较少,有一些自己实现的功能只是为了满足当前的需求。
主要逻辑
动态代理有两种方式,jdk和cglib,责任链模式,递归调用(后面详细说)
就是一条路径上的深度优先,数组相当于一个栈来存储通知(后进先出)
那么存放的顺序应该是反过来,前置通知要最后放进去,才会先执行
初步准备
根据需求创建类和注解,配置类添加注解@EnableAspectJAutoProxy
还有一些其他的@Before,
@After` 什么的,我就不写在这里了
根据切面类写出需要的注解,后面少了再补充
@Component
@Aspect
public class LoggerAspect {
@Before("execution(public int sy.aop.Division.*(..))")
public void before(){
System.out.println("计算开始");
}
@AfterReturning("execution(public int sy.aop.Division.*(..))")
public void afterReturning(int result){
System.out.println("计算结果:"+result);
}
@AfterThrowing("execution(public int sy.aop.Division.*(..))")
public void afterThrowing(){
System.out.println("除数不能为0!");
}
@After("execution(public int sy.aop.Division.*(..))")
public void after(){
System.out.println("计算结束");
}
}
这里的扫描跟之前的方式有些许不同,是在已经注册了的BeanDefinition中进行扫描的
下面开始扫描,我之前的逻辑是写在AnnotationConfigApplicationContext(启动容器)里面的
本来有一个专门的类ConfigurationClassPostProcessor来处理像
@Configuration
,@ComponentScan
,@import
这样的顶级注解的,ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,这样容器启动的时候就会通过委派方法调用了
这里@import逻辑我就不实现了,直接写死,自定义一个方法ativieAspect
(如果配置类中存在EnableAspectJAutoProxy这个注解的话,就把AspectJAutoProxyRegistrar这个类放到容器里)
创建AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar接口
/**
* 往IoC容器中添加内容
*/
public interface ImportBeanDefinitionRegistrar {
void registerBeanDefinitions(DefaultListableBeanFactory registry);
}
讲一下这个ImportBeanDefinitionRegistrar
接口,就是往IoC里添加容器的行为,实现了该接口的会被Spring容器回调
总结PostProcessorRegistrationDelegate
接口回调,注册BeanPostProcessor
BeanDefinitionRegistryPostProcessor
ImportBeanDefinitionRegistrar
BeanFactoryPostProcessor
BeanPostProcessor
ApplicationContext的refresh方法中的invokeBeanFactoryPostProcessors里被调用
/**
* 调用一切实现了ImportBeanDefinitionRegistrar接口的注册方法
* @param beanFactory
*/
private void invokeBeanFactoryPostProcessors(DefaultListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory);
}
在委派类中编写方法,详情见源码,这里我是直接getBean的,和BeanPostProcessor一样,也会存在创建两次的问题,在getbean的时候放到单例池里,创建的时候加一个判断
AspectJAutoProxyRegistrar既然实现了ImportBeanDefinitionRegistrar
接口,就可以往IoC容器中注册对象了
在实现AOP的过程中,我们需要获得目标类及其目标方法,要获得切面类获得对应方法增强器。
准备通知者Advisor
获得切点通知者
- 添加接口
Advice
(通知,代理方法,增强器)及其对应的子接口
/**
* 前置增强接口
*/
public interface MethodBeforeAdvice extends Advice {
/**
* 前置增强方法
* @param method 将要被执行的方法
* @param args 执行方法参数
* @param target 执行方法的目标对象
*/
void before(Method method, Object[] args, Object target);
}
- 判断指定的目标类和目标方法
Pointcut设计
/**
* 切点抽象接口
*/
public interface Pointcut {
/**
* 匹配类
* @param targetClass 将被匹配的目标类
* @return true,表示匹配规则;否则返回false。
*/
boolean matchsClass(Class<?> targetClass);
/**
* 匹配方法
* @param method 将要被匹配的方法
* @param targetClass 将要被匹配的目标类
* @return true,表示匹配规则;否则返回false。
*/
boolean matchsMethod(Method method, Class<?> targetClass);
}
没办法,还是引入个依赖吧,毕竟aop的核心不在这一块,不想再浪费时间了,一个小小的功能背后其实很复杂,有兴趣的可以参考这个博客
用来解析aspectj表达式切入点的
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
实现Pointcut接口
使用到了如下工具类
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.ShadowMatch;
/**
*
* AspectJ表达式切点实现类
*/
public class AspectJExpressionPointcut implements Pointcut {
// 先获得切点解析器
private static PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
// 切点表达式的字符串形式
private String expression;
// aspectj中的切点表达式实例pe
private PointcutExpression pe;
public AspectJExpressionPointcut(String expression) {
super();
this.expression = expression;
pe = pp.parsePointcutExpression(expression);
}
@Override
public boolean matchsClass(Class<?> targetClass) {
return pe.couldMatchJoinPointsInType(targetClass);
}
@Override
public boolean matchsMethod(Method method, Class<?> targetClass) {
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
}
public String getExpression() {
return expression;
}
}
- Advisor切点通知者,将前面获得的Advice(增强方法)和目标方法建立联系(Pointcut)
public interface Advisor {
/**
* 通知bean的名称
* @return
*/
String getAdviceBeanName();
/**
* 表达式
* @return
*/
String getExpression();
}
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
实现类
public class AspectJPointcutAdvisor implements PointcutAdvisor {
private String adviceBeanName; // 通知bean名称
private String expression; // 切点表达式
private Pointcut pointcut; // aspectJ切点实例
/**
* 构造函数
* @param adviceBeanName 通知bean名称
* @param expression 切点表达式
*/
public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
this.adviceBeanName = adviceBeanName;
this.expression = expression;
this.pointcut = new AspectJExpressionPointcut(this.expression);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String getAdviceBeanName() {
return this.adviceBeanName;
}
@Override
public String getExpression() {
return this.expression;
}
}
织入
获得Advisors
与目标方法绑定完成动态代理
上面准备工作做完了,进行匹配过滤的时候是需要拿到工厂了的bean定义的
定义BeanFactory注册接口BeanFactoryAware,通过它获得当前的工厂,原理和BeanNameAware一样,实现了BeanFactoryAware接口就能获得当前的bean工厂
public interface BeanFactoryAware {
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
有了工厂就能拿到切面类进行解析了
AspectJAutoProxyRegistrar往容器里注册了AnnotationAwareAspectJAutoProxyCreator,aop的主要实现类
实现接口 BeanPostProcessor, BeanFactoryAware
BeanPostProcessor后置处理器实现aop,BeanFactoryAware获得工厂
关键方法:
根据bean的类筛选出当前类的所有候选者
AnnotationAwareAspectJAutoProxyCreator:
/**
* 实际上继承AbstractAutoProxyCreator
*
*/
public class AnnotationAwareAspectJAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {
// 当前的bean工厂
private DefaultListableBeanFactory beanFactory;
// 所有Advisor
List<Advisor> candidateAdvisors = new ArrayList<>();
// aware接口的实现,获得Bean工厂实例
@Override
public void setBeanFactory(BeanFactory bf) {
this.beanFactory = (DefaultListableBeanFactory) bf;
}
/**
* Bean初始化后进行增强功能。
* 需要在不改变代码的情况实现该功能,则通过代理模式进行增强。
* @param bean 需要增强的bean
* @param beanName bean名称
* @return 最终被增强的的bean,此时的bean已经经过了代理模式的增强。
* @throws Throwable
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return wrapIfNecessary(bean, beanName);
}
/**
* 返回最终代理对象
* @param bean
* @param beanName
* @return
*/
private Object wrapIfNecessary(Object bean, String beanName) {
// 在此判断bean是否需要进行切面增强,及获得增强的通知实现
List<Advisor> matchAdvisors = findEligibleAdvisors(bean.getClass(), beanName);
// 如需要就进行增强,再返回增强的对象。
if (!CollectionUtils.isEmpty(matchAdvisors)) {
// 返回代理对象
// TODO bean = proxy
}
return bean;
}
/**
* 筛选当前类所含有的所有Advisor
* @param beanClass
* @param beanName
* @return
*/
private List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisor = findCandidateAdvisors();
if (CollectionUtils.isEmpty(candidateAdvisor)) {
return null;
}
// 得到类中的所有的方法
List<Method> allMethods = Arrays.asList(beanClass.getDeclaredMethods());
// 存放匹配的Advisor的list
List<Advisor> matchAdvisors = new ArrayList<>();
// 遍历Advisor来找匹配的
for (Advisor ad : candidateAdvisor) {
if (ad instanceof PointcutAdvisor) {
// 筛选类和方法
if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
System.out.println("筛选出来了:"+beanClass+" 方法:"+allMethods);
matchAdvisors.add(ad);
}
}
}
return matchAdvisors;
}
/**
* 获得全局所有的Advisor
* @return
*/
private List<Advisor> findCandidateAdvisors() {
if (this.candidateAdvisors.size() != 0){
return this.candidateAdvisors;
}
List<Advisor> advisors = new ArrayList<>();
// 找到切面类
for (String beanName : beanFactory.getBeanDefinitionNames()){
Class<?> beanClass = beanFactory.getType(beanName);
if (beanClass.isAnnotationPresent(Aspect.class)){
// 遍历所有方法
for (Method m : beanClass.getDeclaredMethods()){
if (m.isAnnotationPresent(spring.annotation.Before.class)){
String expression = m.getAnnotation(Before.class).value();
advisors.add(new AspectJPointcutAdvisor("methodBeforeAdvice",expression));
} else if (m.isAnnotationPresent(AfterThrowing.class)) {
} else if (m.isAnnotationPresent(AfterReturning.class)) {
} else if (m.isAnnotationPresent(After.class)) {
}
}
}
}
System.out.println("我进来了"+ advisors);
this.candidateAdvisors.addAll(advisors);
return this.candidateAdvisors;
}
/**
* 判断指定类中的方法是否有符合切点规则的。
* @param pa 方面信息,带有切点对象
* @param beanClass 指定的类
* @param methods 指定类中的所有方法
* @return
*/
private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
Pointcut p = pa.getPointcut();
// 首先判断类是否匹配
if (!p.matchsClass(beanClass)) {
return false;
}
// 再判断是否有方法匹配
for (Method method : methods) {
if (p.matchsMethod(method, beanClass)) {
return true;
}
}
return false;
}
}
实现动态代理
添加方法createProxy
private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Exception {
return AopProxyFactory
// 根据参数信息,选择创建代理工厂具体实现
.createAopProxy(bean, beanName, matchAdvisors, beanFactory)
// 从选择的代理工厂中,获得代理对象
.getProxy();
}
这里我直接写成默认代理工厂了AopProxyFactory
/**
* AOP代理工厂
*/
public class AopProxyFactory {
public static AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
throws Exception {
Class<?> targetClass = bean.getClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目标类是一个接口或者是 java.lang.reflect.Proxy 的子类 则使用 JDK 动态代理
// 源码中用户可以通过设置参数强制使用Cglib
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
} else {
return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
}
}
}
Cglib动态代理(有些参数没用,我没改了)
public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {
private String beanName;
private Object target;
private List<Advisor> matchAdvisors;
private DefaultListableBeanFactory beanFactory;
public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
this.beanName = beanName;
this.target = target;
this.matchAdvisors = matchAdvisors;
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
@Override
public Object getProxy() throws NoSuchMethodException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
List<MyAdvice> advices = AopProxyUtils.getAdvices(matchAdvisors,beanFactory);
// System.out.println(advices);
return AopProxyUtils.applyAdvices(target, method, args, advices);
}
}
动态代理条件
目标对象,目标方法,方法的执行参数,增强方法
增强方法需要解析提前存到IoC容器中,还要对其进行排序
关于那个怎么注册advice这个我是真没看懂啊,下面是自己写的一个逻辑,跟源码不同,但核心逻辑是一样的
历史性的时刻,记录一下😭
主要是解决advice(增强方法)的注册,我的思路是直接调用切面类的方法
不过切面类的方法参数要严格要求,以后有机会再完善,比如前置通知的参数类型要跟目标方法一致
还是有排序问题,advice根据数组中的顺序执行的,正常顺序是:前置通知——>目标方法——>返回通知/异常通知——>后置通知(环绕通知没有写)
advice抽象类
/**
* 自己写的,可参考下面test的逻辑
* 动态代理的增强方法
*/
public abstract class MyAdvice implements Ordered{
protected Object aspect; //切面类
protected Method method; //增强方法
public MyAdvice(Object aspect, Method method) {
this.aspect = aspect;
this.method = method;
}
/**
* 执行增强方法
* @param chain
* @return
*/
public abstract Object execute(MyChain chain, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
/**
* 自定义后置通知实现类
*/
public class MyAfterAdvice extends MyAdvice implements Ordered {
public MyAfterAdvice(Object target, Method method) {
super(target, method);
}
@Override
public Object execute(MyChain chain,Object[] args ) throws InvocationTargetException, IllegalAccessException {
Object result = chain.invoke();
method.invoke(aspect);
return result;
}
@Override
public int getOrder() {
return 2;
}
}
责任链
public class MyChain {
List<MyAdvice> advices = new ArrayList<>();
int index = -1;
Method method; // 目标方法
Object[] args; // 参数
Object target; // 目标对象
public MyChain(List<MyAdvice> advices, Method method, Object[] args, Object target) {
this.advices = advices;
this.method = method;
this.args = args;
this.target = target;
}
public Object invoke() throws InvocationTargetException, IllegalAccessException {
if (index == advices.size()-1){
return method.invoke(target, args);
}
return advices.get(++index).execute(this, args);
}
}
注册部分,AnnotationAwareAspectJAutoProxyCreator
这里代码有些冗余,以后再完善
// 为了避免循环依赖,我这里就直接new了,解决完循环依赖再来
aspect = beanClass.getConstructor().newInstance();
MyBeforeAdvice beforeAdvice = new MyBeforeAdvice(aspect, m);
beanFactory.registerSingleton(adviceBeanName,beforeAdvice);
在代理类中调用责任类
建一个工具类AopProxyUtils
public class AopProxyUtils {
/**
* 获得增强方法
*/
public static List<MyAdvice> getAdvices(List<Advisor> matchAdvisors, DefaultListableBeanFactory beanFactory) {
List<MyAdvice> advices = new ArrayList<>();
for (Advisor advisor : matchAdvisors){
MyAdvice advice = (MyAdvice) beanFactory.getSingleton(advisor.getAdviceBeanName());
advices.add(advice);
}
advices.sort(Comparator.comparingInt(Ordered::getOrder));
return advices;
}
/**
* 执行责任链
*/
public static Object applyAdvices(Object target, Method method, Object[] args, List<MyAdvice> advices) throws IllegalAccessException, InvocationTargetException {
// 如果没有增强方法
if (CollectionUtils.isEmpty(advices)) {
return method.invoke(target, args);
} else {
// 责任链式执行增强
MyChain chain = new MyChain(advices, method, args,target);
return chain.invoke();
}
}
}
简单测试一下
以后有机会再写循环依赖吧
加油加油,一起变强!🧐