# Spring仿写 **Repository Path**: swis/spring-imitation ## Basic Information - **Project Name**: Spring仿写 - **Description**: 之前写了一个极简的,感觉有点乱,这个稍微完整一点,主要是学习学习代码的设计思想,其中设计模式的运用 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: step7完成 - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-04-16 - **Last Updated**: 2023-05-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring框架简单仿写 ## 前言 本次仿写旨在初步理解spring底层和设计模式在框架中的应用 之前跟着视频写了一个极简的版本,IoC的基本原理大致过了一遍,简单的写了一下核心代码,没有太过深入,中间的逻辑没能清晰的理解 所以打算自己捋一捋 下面从无到有记录我整个仿写的过程 包名,类名,方法的实现,不一定是按源码来的,有些实现逻辑太过复杂就自己写了方法来实现,中间可能为了解耦进行了一下结构上的调整,跟源码是比不得的:clown_face: 然后就是前面不懂,后面懂了进行的一些修改,欢迎评论指点 环境: jdk1.8 + maven 功能介绍: 目标实现简单的IoC依赖注入,简单的aop日志功能 注解实现@ComponentScan扫描,@Component,@Autowired,@Scope @Aspect,@Before,@After,@AfterReturning,@AfterThrowing(没有实现消息环绕) 未实现循环依赖,jdk动态代理,(以后有机会出个第二阶段) 参考仓库: 1. GitHub:https://github.com/fuzhengwei/book-small-spring 2. Gitee:https://gitee.com/zxh904582599/imitation-spring-framework?_from=gitee_search 第一阶段展示 ![image-20230422124527386](assets/image-20230422124527386.png) ## Step1 基本准备 新建两个文件夹,一个用来放框架源码,一个自己测试 ![image.png](assets/结构.png) 在sy文件夹下创建一个启动类Test 确定要实现的功能 ```java 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 1. 创建容器类,继承关系如下,这里只写三层,分析接口中有`getBean(String beanName)`方法 ![image.png](assets/容器类结构.png) 2. 创建配置文件AppConfig,使用@ComponentScan的方式注册bean ```java @ComponentScan("sy.service") public class AppConfig { } ``` 3. 创建@ComponentScan注解 4. 在包下创建UserService,并加上@Component注解 ## Step2 Bean的三大接口 概述 先扫描注册BeanDefinition,然后创建bean 生命周期: 1. 实例化(依赖注入) 2. aware回调 3. BeanPostProcessor:before 4. 初始化 5. BeanPostProcessor:after(aop) 关于bean是怎么被创建出来的 使用注解@Component,即标记了该类需要被实例化成一个bean对象,并将该对象存在容器中 首先先要将标记的类进行注册,这里引入了一个类记录了bean的信息 1. 创建BeanDefinition类,描述bean的信息,记录了bean所属的类型,方便通过反射实例化,是单例还是原型 类似于Class对象和类型 ```java 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对象 2. 创建BeanDefinitionRegistry接口,定义了BeanDefinition注册表的行为 具体代码见我源码,beanDefinition最终是保存在map中的,其需要的方法跟map的差不多 ```java package spring.beans; import spring.beans.config.BeanDefinition; public interface BeanDefinitionRegistry { void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); BeanDefinition getBeanDefinition(String beanName); } ``` 注册成功后,Spring就需要根据BeanDefinition对象来创建 那么,是由谁来创建呢?(自问自答)BeanFactory。 3. 创建BeanFactory接口,象征性的写一个异常,得不到就报错 ```java Object getBean(String beanName) throws BeansException; ``` 由BeanFactory创建返回bean实例对象后,那么这个对象性(单例),就真正要被放到**单例池**中了 4. 抽象单例池,贯穿面向抽象编程的思想,这里又是一个接口,SingletonBeanRegistry 单例池也是一个map,详情见源码 ```java Object getSingleton(String beanName); void registerSingleton(String beanName, Object singletonObject); ``` 源码类图是这样的,我这里必不可能全写完,有些也不知道是干嘛的 ![image.png](assets/源码.png) 关键是BeanFactory和SingletonBeanRegister之间的关系 也就是BeanFactory和单例池的关系 先不考虑循环依赖的问题,正常考虑 1. 用户需要一个bean对象,问BeanFactory要 2. BeanFactory根据beanName去BeanDefinition里面查,看看是单例还是原型 3. 是单例,首先要判断单例池里面有没有这个bean ,有就从单例池里面拿,然后返回这个单例给用户,没有就报错,说明该单例没有扫进来 4. 原型就创建直接返回 ## Step3 接口的初步实现和分析 借鉴了那本书的类图 image-20230419111036968 详情见我的源码,简单描述一下 DefaultSingletonBeanRegistry:实现了单例注册表,包含单例池 AbstractBeanFactory,定义工厂模板 ```java public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory{ public Object getBean(String beanName) { return doGetBean(beanName); } public T getBean(String name, Class 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了 根据上面分析的,先写一个简单的逻辑 1. 实现BeanDefinitionRegister注册表的方法 2. getBean基本逻辑 ```java @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; } ``` 3. 创建bean ```java 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类,这里相当于定义了一个模板 1. 容器里要有一个注册表 ```java protected BeanDefinitionRegistry register = new DefaultListableBeanFactory(); ``` 2. 启动流程的方法 ```java void refresh(); ``` 3. 重写getBean ```java @Override public Object getBean(String name) { return ((DefaultListableBeanFactory) register).getBean(name); } ``` 实现类AnnotationConfigApplicationContext 容器启动的时候,根据配置文件进行扫描,注册beanDefinition对象 构造方法如下 ```java public AnnotationConfigApplicationContext(Class configClass) { reader(configClass); refresh(); } ``` 详情见源码,看一下自己写的那个深度优先,先注册beanDefinition,BeanPostProcessor后面再说 ![image-20230417202941553](assets/image-20230417202941553.png) > 我这里没有`com`包,就报错了 获得beanName ```Java 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(); } ``` 简单测试一下 ![image-20230417205833021](assets/image-20230417205833021.png) 现在beanDefinition对象有了,BeanFactory要开始创建bean实例了 DefaultListableBeanFactory新建方法 ```java 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()` 测试结果 image-20230417213711383 复盘一下,准备写依赖注入了,脑子笨,得一步一步来 image-20230417224607898 ## Step5 初步依赖注入 #### 初步依赖注入 新建一个类OrderService,注册bean ```java @Component public class OrderService { public void test(){ System.out.println("我是orderService"); } } ``` UserService中实现依赖注入,在test中调用orderService的方法 新建注解@Autowired ```java @Component public class UserService { @Autowired private OrderService orderService; public void test() { System.out.println("我是userService"); orderService.test(); } } ``` image-20230417225736932 遍历该类的每个字段,是否标记Autowired, 如果有则从单例池里获得bean对象 单例池里有则直接返回,没有就创建这个bean,加入单例池,返回(这一部分getBean中已经前面已经写了) ```java for (Field field : clazz.getDeclaredFields()){ if(field.isAnnotationPresent(Autowired.class)){ Object bean = getBean(field.getName()); field.setAccessible(true); field.set(instance, bean); } } ``` 测试结果 image-20230417233458709 #### BeanNameAware和初始化 简单写一下,平常用的少 Aware相当于Spring给用户提供的一个接口,用户通过这个接口可以获得Spring容器内部的单例对象 1. 新建接口BeanNameAware ```java public interface BeanNameAware { void setBeanName(String name); } ``` 2. 现在UserService想获得自己的beanName,这里注入到自己的成员属性中 image-20230418124429666 3. 在bean声明周期中实现aware接口的回调 ```java if( instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } ``` 简单进行输出测试,初始化也差不多,也是实现接口,Spring来回调 ```java public interface InitializingBean { void afterPropertiesSet() throws Exception; } ``` ## Step6 BeanPostProcessor 1. 创建BeanPostProcessor接口 ```java 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扫描到 2. 创建类MyBeanPostProcessor实现BeanPostProcessor接口 ```java package sy.service; import spring.annotation.Component; import spring.beans.BeanPostProcessor; /** * 自定义处理bean */ @Component public class MyBeanPostProcessor implements BeanPostProcessor { } ``` 现在不得不感叹这个BeanDefinition被设计出来真的太绝了,注册和创建分开,中间可以做很多事情 看我之前写的容器类,register方法注册BeanDefinition然后才启动容器(一开始写的方法名是reader) image-20230425002007308 这样,这样根据beanDefinition对象的Class属性来判断该类是否实现了BeanPostProcessor接口,这样就可以先注册beanPostProcessor对象了,等到真正实例化单例的时候就能保证beanPostProcessor对象池子里是有的 3. BeanPostProcessor的注册 > 这里不得不提一嘴**BeanFactoryPostProcessor** > 根据前面的生命周期图可类比,我们可以对每个类实例加工成bean,比如给这个实例加一些属性啊,自定义一些方法啊之类的 > BeanFactoryPostProcessor就是对BeanFactory“做手脚”了,beanFactory被修改,那么就可以扩展多种生产bean的方式,如不同的注册BeanDefinition的方式啦(这里只用一种,中间的读操作就没做抽象),生产特殊功能的bean啦,等等 bean工厂中里添加容器和其对应的方法 ```java private List beanPostProcessors = new ArrayList<>(); ``` image-20230418210557298 从注册的BeanDefinition中找到实现接口的类,在bean工厂中编写一个方法 ```java public ArrayList getBeanNamesForType(Class objClass) { ArrayList list = new ArrayList<>(); for (String key : beanDefinitionMap.keySet()) { BeanDefinition value = beanDefinitionMap.get(key); if (objClass.isAssignableFrom(value.getClazz())) { list.add(key); } } return list; } ``` 象征性的写一个委托类 [PostProcessorRegistrationDelegate](https://andyboke.blog.csdn.net/article/details/78530137?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-78530137-blog-120347401.235%5Ev29%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-78530137-blog-120347401.235%5Ev29%5Epc_relevant_default_base3) 用来注册BeanPostProcessor的,实现了根据优先级对BeanPostProcessor进行排序 创建两个接口 image-20230418211014142 > 接口中的常量,默认+public+、+static+、+final+ 在容器类中调用 ```java private void registerBeanPostProcessors(DefaultListableBeanFactory beanFactory){ PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory); } ``` 运行测试 4. 在生命周期中添加逻辑 image-20230418222305071 运行测试 这里要注意一下,遍历BeanPostProcessor池和创建单例这两个过程是分开的 image-20230418223042366 ## Step7 AOP Aop就是在`postProcessorAfterInitialzation`这一步执行的,但是Aop不同的一点是针对某一特定的类实现的,不是针对全局。 这一部分的实现对源码的参考相对较少,有一些自己实现的功能只是为了满足当前的需求。 >[参考博客1](https://blog.csdn.net/a1533588867/article/details/122187388),[参考博客2](https://blog.csdn.net/Maybe_9527/article/details/112652118?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168196149416800180624769%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168196149416800180624769&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-112652118-null-null.142^v85^insert_down1,239^v2^insert_chatgpt&utm_term=%E6%89%8B%E5%86%99springaop&spm=1018.2226.3001.4187) 主要逻辑 动态代理有两种方式,jdk和cglib,责任链模式,递归调用(后面详细说) image-20230419094525490 image-20230419102714324 就是一条路径上的深度优先,数组相当于一个栈来存储通知(后进先出) 那么存放的顺序应该是反过来,前置通知要最后放进去,才会先执行 #### 初步准备 根据需求创建类和注解,配置类添加注解`@EnableAspectJAutoProxy` 还有一些其他的`@Before,`@After` 什么的,我就不写在这里了 根据切面类写出需要的注解,后面少了再补充 ```java @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(启动容器)里面的 image-20230419162425649 > 本来有一个专门的类ConfigurationClassPostProcessor来处理像`@Configuration`,`@ComponentScan`,`@import`这样的顶级注解的,ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,这样容器启动的时候就会通过委派方法调用了 这里@import逻辑我就不实现了,直接写死,自定义一个方法`ativieAspect` (如果配置类中存在EnableAspectJAutoProxy这个注解的话,就把AspectJAutoProxyRegistrar这个类放到容器里) image-20230419174742022 创建AspectJAutoProxyRegistrar类实现了**ImportBeanDefinitionRegistrar**接口 ```java /** * 往IoC容器中添加内容 */ public interface ImportBeanDefinitionRegistrar { void registerBeanDefinitions(DefaultListableBeanFactory registry); } ``` 讲一下这个`ImportBeanDefinitionRegistrar`接口,就是往IoC里添加容器的行为,实现了该接口的会被Spring容器回调 #### 总结PostProcessorRegistrationDelegate 接口回调,注册BeanPostProcessor ```java BeanDefinitionRegistryPostProcessor ImportBeanDefinitionRegistrar BeanFactoryPostProcessor BeanPostProcessor ``` ![image-20230420185500656](assets/image-20230420185500656.png) ApplicationContext的refresh方法中的**invokeBeanFactoryPostProcessors**里被调用 image-20230420180855272 ```java /** * 调用一切实现了ImportBeanDefinitionRegistrar接口的注册方法 * @param beanFactory */ private void invokeBeanFactoryPostProcessors(DefaultListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory); } ``` 在委派类中编写方法,详情见源码,这里我是直接getBean的,和BeanPostProcessor一样,也会存在创建两次的问题,在getbean的时候放到单例池里,创建的时候加一个判断 AspectJAutoProxyRegistrar既然实现了`ImportBeanDefinitionRegistrar`接口,就可以往IoC容器中注册对象了 在实现AOP的过程中,我们需要获得目标类及其目标方法,要获得切面类获得对应方法增强器。 #### 准备通知者Advisor 获得切点通知者 1. 添加接口`Advice`(通知,代理方法,增强器)及其对应的子接口 ```java /** * 前置增强接口 */ public interface MethodBeforeAdvice extends Advice { /** * 前置增强方法 * @param method 将要被执行的方法 * @param args 执行方法参数 * @param target 执行方法的目标对象 */ void before(Method method, Object[] args, Object target); } ``` 2. 判断指定的目标类和目标方法 Pointcut设计 image-20230420201434939 ```java /** * 切点抽象接口 */ 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的核心不在这一块,不想再浪费时间了,一个小小的功能背后其实很复杂,有兴趣的可以参考这个[博客](https://blog.csdn.net/m0_51545690/article/details/125549292?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-4-125549292-blog-112652118.235^v31^pc_relevant_default_base3&spm=1001.2101.3001.4242.3&utm_relevant_index=7) 用来解析aspectj表达式切入点的 ```xml org.aspectj aspectjweaver 1.9.7 ``` 实现Pointcut接口 使用到了如下工具类 ```java import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParser; import org.aspectj.weaver.tools.ShadowMatch; ``` ```java /** * * 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; } } ``` 3. Advisor切点通知者,将前面获得的Advice(增强方法)和目标方法建立联系(Pointcut) image-20230420131739724 ```java public interface Advisor { /** * 通知bean的名称 * @return */ String getAdviceBeanName(); /** * 表达式 * @return */ String getExpression(); } ``` ```java public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(); } ``` 实现类 ```java 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工厂 ```java public interface BeanFactoryAware { void setBeanFactory(BeanFactory beanFactory) throws BeansException; } ``` 有了工厂就能拿到切面类进行解析了 AspectJAutoProxyRegistrar往容器里注册了AnnotationAwareAspectJAutoProxyCreator,aop的主要实现类 实现接口 BeanPostProcessor, BeanFactoryAware BeanPostProcessor后置处理器实现aop,BeanFactoryAware获得工厂 关键方法: 根据bean的类筛选出当前类的所有候选者 image-20230421100344952 AnnotationAwareAspectJAutoProxyCreator: ```java /** * 实际上继承AbstractAutoProxyCreator * */ public class AnnotationAwareAspectJAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware { // 当前的bean工厂 private DefaultListableBeanFactory beanFactory; // 所有Advisor List 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 matchAdvisors = findEligibleAdvisors(bean.getClass(), beanName); // 如需要就进行增强,再返回增强的对象。 if (!CollectionUtils.isEmpty(matchAdvisors)) { // 返回代理对象 // TODO bean = proxy } return bean; } /** * 筛选当前类所含有的所有Advisor * @param beanClass * @param beanName * @return */ private List findEligibleAdvisors(Class beanClass, String beanName) { List candidateAdvisor = findCandidateAdvisors(); if (CollectionUtils.isEmpty(candidateAdvisor)) { return null; } // 得到类中的所有的方法 List allMethods = Arrays.asList(beanClass.getDeclaredMethods()); // 存放匹配的Advisor的list List 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 findCandidateAdvisors() { if (this.candidateAdvisors.size() != 0){ return this.candidateAdvisors; } List 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 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 ```java private Object createProxy(Object bean, String beanName, List matchAdvisors) throws Exception { return AopProxyFactory // 根据参数信息,选择创建代理工厂具体实现 .createAopProxy(bean, beanName, matchAdvisors, beanFactory) // 从选择的代理工厂中,获得代理对象 .getProxy(); } ``` 这里我直接写成默认代理工厂了AopProxyFactory ```java /** * AOP代理工厂 */ public class AopProxyFactory { public static AopProxy createAopProxy(Object bean, String beanName, List 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动态代理(有些参数没用,我没改了) ```java public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor { private String beanName; private Object target; private List matchAdvisors; private DefaultListableBeanFactory beanFactory; public CglibDynamicAopProxy(String beanName, Object target, List 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 advices = AopProxyUtils.getAdvices(matchAdvisors,beanFactory); // System.out.println(advices); return AopProxyUtils.applyAdvices(target, method, args, advices); } } ``` 动态代理条件 目标对象,目标方法,方法的执行参数,增强方法 增强方法需要解析提前存到IoC容器中,还要对其进行排序 关于那个怎么注册advice这个我是真没看懂啊,下面是自己写的一个逻辑,跟源码不同,但核心逻辑是一样的 历史性的时刻,记录一下,(虽然很菜):sob: image-20230421202114492 主要是解决advice(增强方法)的注册,我的思路是直接调用切面类的方法 不过切面类的方法参数要严格要求,以后有机会再完善,比如前置通知的参数类型要跟目标方法一致 还是有排序问题,advice根据数组中的顺序执行的,正常顺序是:前置通知——>目标方法——>返回通知/异常通知——>后置通知(环绕通知没有写) advice抽象类 ```java /** * 自己写的,可参考下面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; } ``` ```java /** * 自定义后置通知实现类 */ 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; } } ``` 责任链 ```java public class MyChain { List advices = new ArrayList<>(); int index = -1; Method method; // 目标方法 Object[] args; // 参数 Object target; // 目标对象 public MyChain(List 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 这里代码有些冗余,以后再完善 ```java // 为了避免循环依赖,我这里就直接new了,解决完循环依赖再来 aspect = beanClass.getConstructor().newInstance(); MyBeforeAdvice beforeAdvice = new MyBeforeAdvice(aspect, m); beanFactory.registerSingleton(adviceBeanName,beforeAdvice); ``` 在代理类中调用责任类 建一个工具类AopProxyUtils ```java public class AopProxyUtils { /** * 获得增强方法 */ public static List getAdvices(List matchAdvisors, DefaultListableBeanFactory beanFactory) { List 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 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(); } } } ``` 简单测试一下 image-20230422110434674 image-20230422110447495 ## 整理,添加日志 一下类图只适用于本项目,跟spring源码的类图还是有很大出入的。 bean工厂的简单类图 bean工厂的简单类图 bean生命周期依赖关系 bean生命周期依赖关系 容器类的简单类图 容器类的简单类图(事实上应该是继承关系,这里就没用继承了) aop实现的简单类图 aop实现的简单类图(一些方法和类被省略) ## Step8 循环依赖(未完成) 以后有机会再写吧