Spring简单仿写

本文详细记录了一次Spring框架核心功能的简单仿写过程,包括IoC容器的创建、BeanDefinition的注册、依赖注入、BeanPostProcessor以及AOP的实现。通过这个过程,作者深入理解了Spring框架的设计模式和工作原理。
摘要由CSDN通过智能技术生成

Spring框架简单仿写

前言

本次仿写旨在初步理解spring底层和设计模式在框架中的应用
之前跟着视频写了一个极简的版本,IoC的基本原理大致过了一遍,简单的写了一下核心代码,没有太过深入,中间的逻辑没能清晰的理解
所以打算自己捋一捋

下面从无到有记录我整个仿写的过程
包名,类名,方法的实现,不一定是按源码来的,有些实现逻辑太过复杂就自己写了方法来实现,中间可能为了解耦进行了一下结构上的调整,跟源码是比不得的🤡

然后就是前面不懂,后面懂了进行的一些修改,欢迎评论指点

环境:

jdk1.8 + maven

功能介绍:

目标实现简单的IoC依赖注入,简单的aop日志功能

注解实现@ComponentScan扫描,@Component,@Autowired,@Scope
@Aspect,@Before,@After,@AfterReturning,@AfterThrowing(没有实现消息环绕)

未实现循环依赖,jdk动态代理,(以后有机会出个第二阶段)

代码仓库:本项目源码gitee

参考仓库:

  1. GitHub:https://github.com/fuzhengwei/book-small-spring
  2. 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

  1. 创建容器类,继承关系如下,这里只写三层,分析接口中有getBean(String beanName)方法

请添加图片描述

  1. 创建配置文件AppConfig,使用@ComponentScan的方式注册bean
@ComponentScan("sy.service")
public class AppConfig {
}
  1. 创建@ComponentScan注解
  2. 在包下创建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对象和类型
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对象

  1. 创建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。

  1. 创建BeanFactory接口,象征性的写一个异常,得不到就报错
Object getBean(String beanName) throws BeansException;

由BeanFactory创建返回bean实例对象后,那么这个对象性(单例),就真正要被放到单例池中了

  1. 抽象单例池,贯穿面向抽象编程的思想,这里又是一个接口,SingletonBeanRegistry
    单例池也是一个map,详情见源码

    Object getSingleton(String beanName);
    void registerSingleton(String beanName, Object singletonObject);
    

源码类图是这样的,我这里必不可能全写完,有些也不知道是干嘛的

请添加图片描述

关键是BeanFactory和SingletonBeanRegister之间的关系

也就是BeanFactory和单例池的关系

先不考虑循环依赖的问题,正常考虑

  1. 用户需要一个bean对象,问BeanFactory要
  2. BeanFactory根据beanName去BeanDefinition里面查,看看是单例还是原型
  3. 是单例,首先要判断单例池里面有没有这个bean ,有就从单例池里面拿,然后返回这个单例给用户,没有就报错,说明该单例没有扫进来
  4. 原型就创建直接返回

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了

根据上面分析的,先写一个简单的逻辑

  1. 实现BeanDefinitionRegister注册表的方法
  2. 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;
    }
  1. 创建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类,这里相当于定义了一个模板

  1. 容器里要有一个注册表

    protected BeanDefinitionRegistry register = new DefaultListableBeanFactory();
    
  2. 启动流程的方法

    void refresh();
    
  3. 重写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容器内部的单例对象

  1. 新建接口BeanNameAware
public interface BeanNameAware {
    void setBeanName(String name);
}
  1. 现在UserService想获得自己的beanName,这里注入到自己的成员属性中

  2. 请添加图片描述

  3. 在bean声明周期中实现aware接口的回调

if( instance instanceof BeanNameAware) {
    ((BeanNameAware) instance).setBeanName(beanName);
}

简单进行输出测试,初始化也差不多,也是实现接口,Spring来回调

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

Step6 BeanPostProcessor

  1. 创建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扫描到

  1. 创建类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对象池子里是有的

  1. 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);
}
  1. 在生命周期中添加逻辑

请添加图片描述

运行测试

请添加图片描述
这里要注意一下,遍历BeanPostProcessor池和创建单例这两个过程是分开的

Step7 AOP

Aop就是在postProcessorAfterInitialzation这一步执行的,但是Aop不同的一点是针对某一特定的类实现的,不是针对全局。

这一部分的实现对源码的参考相对较少,有一些自己实现的功能只是为了满足当前的需求。

参考博客1参考博客2

主要逻辑

动态代理有两种方式,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

获得切点通知者

  1. 添加接口Advice(通知,代理方法,增强器)及其对应的子接口
/**
 * 前置增强接口
 */
public interface MethodBeforeAdvice extends Advice {
    /**
     * 前置增强方法
     * @param method 将要被执行的方法
     * @param args 执行方法参数
     * @param target 执行方法的目标对象
     */
    void before(Method method, Object[] args, Object target);
}
  1. 判断指定的目标类和目标方法

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;
	}

}
  1. 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();
        }
    }
}

简单测试一下

请添加图片描述

请添加图片描述

以后有机会再写循环依赖吧

加油加油,一起变强!🧐

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值