手写Spring---AOP面向切面编程(4)

1,804 阅读13分钟

接上一篇《手写Spring---AOP面向切面编程(3)》继续更新

补充:扩展的Advisor

在上一篇中已经讲到了Advisor包下的Advisor.java,也就是一个通知者和一个基于AspectJ语法实现的Advisor,由于通知者也可分为许多不同种类的通知者,所以我们在进行扩展的时候,需要通过不同的实现方式定义不同的通知者。

现在看看这两种不同的设计方式

第二种相比第一种多了一层抽象,把相同的属性方法抽离掉然后减少了冗余代码, 此时pointcut的生成必须由抽象类AbstractPointcutAdvisor的子类去实现生成不同的 pointcut,是AspectJ的生成AspectJ的,是正则Reg的生成正则的。

一、织入Weaving的实现

① 织入要完成什么?

将用户提供的增强功能加到指定的方法上

② 在什么时候进行织入?

创建bean实例,在bean初始化后进行增强

③ 如何确定bean需要增强

对bean类及方法进行遍历,匹配用户指定的切面

④ 如何进行织入

代理!

二、织入Weaving的设计

此时我们回顾一下AOP的使用流程

用户---> 配置并注册切面
我们---> 织入流程:初始化bean---判断是否需要增强 ? 代理增强---返回实例 : 返回实例

① 用户到哪里去注册切面?

② 判断匹配,织入逻辑写在哪里

③ 此时我们是否需要在bean创建的过程中加一层处理?

还记得我们之前实现的DefaultBeanFactory吗?
在它的doGetBean方法中,初始化Bean前增加一个增强逻辑即可

后续我们在bean创建过程中还会再加入更多不同的处理,如果直接在BeanFactory中实现,会导致BeanFactory代码爆炸性增长,而且也不容易进行扩展,此时我们该如何处理呢?

此时我们再回顾一下3周前的那篇《手写Spring---IOC容器(1)》中讲到的Bean的产出过程

1.创建Bean定义---① 2.注册Bean定义②---③ 3.创建Bean实例④---⑤ 4.初始化Bean实例⑥

在此4个节点中,每一项过程的前后阶段都可能增加各种各样的处理逻辑
为了让我们更加灵活,设计好BeanFactory后不需要修改代码,
我们需要在各个节点中加入扩展点(使用了 **① ~ ⑥** 标识,
比如**①**的意思为注册bean定义前,**②**为注册Bean定义后)和注册机制

此时我们需要会想到观察者模式(监听模式),需要六个扩展点,也就是六个观察者


三、应用观察者模式加入我们的AOP织入

此时我们不推荐仅定义一个接口去完成所有扩展点的监听,因为它们的用途不一样且实现的时候需要一下子实现6个功能,此时我们定义一个监听接口BeanPostProcessor来监听Bean初始化前后过程,具体流程参照下图:

① 监听者模式实现AOP流程图

② 代码实现

1.BeanPostProcessor.java

这里默认什么都不干,定义的是default默认方法,这里是为了方便选择只进行某一个的扩展点的处理,比如我们之后就只选择进行初始化后的处理

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean,String beanName) throws Throwable{
        return bean;
    }
    default Object postProcessAfterInitialization(Object bean,String beanName) throws Throwable{
        return bean;
    }
}

2.AdvisorRegistry.java

提供对于Advisor的注册和获取

public interface AdvisorRegistry {
    public void registAdvisor(Advisor ad);
    public List<Advisor> getAdvisors();
}

3.BeanFactory.java

此时我们创建好实例之后需要放入Bean工厂里面,还需要在BeanFactory.java中加入注册监听的方法

public interface BeanFactory {
    Object getBean(String name) throws Throwable;
    //新加入的注册方法
    void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);
}

4.DefaultBeanFactory.java

这里是BeanFactory的默认实现,在此提供registerBeanPostProcessor的实现

(1)定义一个集合

private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());

(2)注册方法(仅仅为添加进线程同步集合beanPostProcessors中)

@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    this.beanPostProcessors.add(beanPostProcessor);
    if (beanPostProcessor instanceof BeanFactoryAware){
        ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
    }
}

5.Aware和BeanFactoryAware

如何去使用观察者模式,还必须提供唤醒的功能

(1)此时在Bean初始化前和初始化后加入处理(DefaultBeanFactory中的doGetBean方法)

// 应用bean初始化前的处理
    instance = this.applyPostProcessBeforeInitialization(instance, beanName);

    // 执行初始化方法
    this.doInit(bd, instance);

    // 应用bean初始化后的处理
    instance = this.applyPostProcessAfterInitialization(instance, beanName);

(2)applyPostProcessBeforeInitialization()和applyPostProcessAfterInitialization()方法其实就是遍历你的所有注册进来的processor,然后一个一个调用它们的方法,按照这个规范来扩展多少功能都可以

// 应用bean初始化前的处理
private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessBeforeInitialization(bean, beanName);
    }
    return bean;
}

// 应用bean初始化后的处理
private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessAfterInitialization(bean, beanName);
    }
    return bean;
}

6.AdvisorAutoProxyCreator的实现

AOP 织入功能 applyPostProcessAfterInitialization 肯定在此实现

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

}

③ 实现织入:判断bean是否需要增强

回忆这个过程: 织入流程:初始化bean---判断是否需要增强 ? 代理增强---返回实例 : 返回实例

1. 如何判断bean实例是否要增强?

1.获取bean的类及所有方法(通过反射getMethods()和getDeclareMethods())
2.遍历Advisor,取advisor中的Pointcut来匹配类和方法
Pointcut中的两个匹配方法:
    boolean matchClass(Class<?> targetClass);
    boolean matchMethod(Method method,Class<?> targetClass);
    
即是用前面通过反射获得的方法和matchMethod()来比较

2. 此时我们需要确定getMethods()和getDeclareMethods()可以取到所有的方法吗?

如果阅读过源码中的说明,就会发现getMethods()方法返回所有的公开方法,
无法取得私有和保护类型的方法,getDeclareMethods()无法取得父类
的继承方法(第一小段最后一句but excluding inherited methods)

所以这两个方法仍然不够,我们到时候会直接使用spring里面内置的方法

3. 回到AdvisorAutoProxyCreator.java中的postProcessAfterInitialization()方法

// 第一步:在此判断bean是否需要进行切面增强
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);

// 第二步:如需要就进行增强,创建代理对象,进行代理增强。再返回增强的对象。
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }

4. 编写(3)提到的getMatchedAdvisors()方法

private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {
    if (CollectionUtils.isEmpty(advisors)) {
        return null;
    }

    // 得到类、所有的方法
    Class<?> beanClass = bean.getClass();
    List<Method> allMethods = this.getAllMethodForClass(beanClass);

    // 存放匹配的Advisor的list
    List<Advisor> matchAdvisors = new ArrayList<>();
    // 遍历Advisor来找匹配的
    for (Advisor ad : this.advisors) {
        if (ad instanceof PointcutAdvisor) {
            if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                matchAdvisors.add(ad);
            }
        }
    }

    return matchAdvisors;
}

5. 在(4)中得到所有方法的getAllMethodForClass()

此时我们需要用到Spring自带的ClassUtils和ReflectionUtils,ps:可以直接在maven中添加SpringBoot的依赖之后无须再进行导包。

这里的逻辑为创建一个集合,使用ClassUtils自带的遍历方法getAllInterfacesForClassAsSet()遍历所有接口,之后把自身beanClass也一起丢进这个集合中,之后来一个循环把接口中的所有方法依次遍历出来

private List<Method> getAllMethodForClass(Class<?> beanClass) {
    List<Method> allMethods = new LinkedList<>();
    Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
    classes.add(beanClass);
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method m : methods) {
            allMethods.add(m);
        }
    }

    return allMethods;
}

6. 在getMatchedAdvisors中匹配Advisor和bean的所有方法

private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
    Pointcut p = pa.getPointcut();

    // 首先判断类是否匹配
    // 注意之前说过的AspectJ情况下这个匹配是不可靠的,需要通过方法来匹配
    //这里的判断仅仅起到过滤作用,类不匹配的前提下直接跳过
    if (!p.matchClass(beanClass)) {
        return false;
    }

    // 再判断是否有方法匹配
    for (Method method : methods) {
        if (p.matchMethod(method, beanClass)) {
            return true;
        }
    }
    return false;
}

7.postProcessAfterInitialization()方法的实现

此时我们已经得到了匹配上的方法,可以对这些方法进行代理增强操作了

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

    // 在此判断bean是否需要进行切面增强
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);
    // 如需要就进行增强,再返回增强的对象。
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }
    return bean;
}

8.创建代理对象

private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable {
    // 通过AopProxyFactory工厂去完成选择、和创建代理对象的工作。
    return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory)
            .getProxy();
}

④ 代理增强的实现

这部分涉及的内容比较多,建议先理清思路然后看懂逻辑即可,因为我们的主要出发点在于以后能够理解源码而不是真的要自己完整实现一个spring

1. 代理增强的逻辑是怎样的?

代理可分为jdk动态代理(使用invoke方法增强)和cglib动态代理(使用intercept方法增强),判断代理生成方式后返回相应代理对象即可

2. jdk动态代理和cglib动态代理的两者抽象

我们知道,要实现jdk动态代理,就要实现InvocationHandler接口,要实现cglib动态代理,就要实现MethodInteceptor接口,我们可以对这两者进行抽象,抽成一个AopProxy接口,里面提供获取代理对象的方法即可

public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}

3. jdk动态代理的invoke和cglib动态代理intercept逻辑一样吗?

一样,都是使用Advice进行增强
---> 从Advisor中找出对当前方法进行增强的Advice
---> 是否有匹配的Advice?责任链式应用Advice增强:执行被代理对象的方法
---> 返回结果

jdk要生成代理对象,完成织入增强,需要以下数据
    要实现的接口,目标对象,匹配的Advisors,可能还需要BeanFactory
cglib需要的数据:
    要继承的类,实现的接口,构造参数类型,构造参数,目标对象,
    匹配的Advisor和BeanFactory
    
    为何需要构造参数和构造参数类型,因为扩展目标类如果没提供无参构造方法,那它的子类创建时必须调用父类的有参构造方法

4. JdkDynamicAopProxy和CglibDynamicAopProxy的设计

AopProxyUtils

先不看JdkDynamicAopProxy和CglibDynamicAopProxy的具体实现,我们先看它们的结构,因为前面我们也提到了,它们俩做的事情,功能其实是一样的,所以可以提取相同的功能实现部分为一个通用方法applyAdvices()

我们可以直接定义一个工具类AopProxyUtils来储存这样的通用方法

public static Object applyAdvices(Object target, Method method, Object[] args, 
    List<Advisor> matchAdvisors,Object proxy, BeanFactory beanFactory) throws Throwable {
    // 这里要做什么?
    // 1、获取要对当前方法进行增强的advice
    List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors,
            beanFactory);
    // 2、如有增强的advice,责任链式增强执行
    if (CollectionUtils.isEmpty(advices)) {
        return method.invoke(target, args);
    } else {
        // 责任链式执行增强
        AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
        return chain.invoke();
    }
}

public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors,
    BeanFactory beanFactory) throws Throwable {
        if (CollectionUtils.isEmpty(matchAdvisors)) {
            return null;
        }
        List<Object> advices = new ArrayList<>();
        for (Advisor ad : matchAdvisors) {
            if (ad instanceof PointcutAdvisor) {
                if (((PointcutAdvisor) ad).getPointcut().matchMethod(method, beanClass)) {
                    advices.add(beanFactory.getBean(ad.getAdviceBeanName()));
                }
            }
        }

        return advices;
}

如何来进行责任链的增强,注意我们必须定义一个责任链记录索引,比如下方代码中我们就使用i作为索引,在invoke()方法中,先判断i是否小于advices的size,如果小于,那就是还有增强未执行,之后的判断逻辑很简单,如果这个增强是前置增强,那就是直接执行即可,

如果是环绕增强return ((MethodSurroudAdvice) advice).invoke(invokeMethod, null, this),参考MethodSurroudAdvice中的invoke()的参数,此时我们需要理解,这里的invoke方法中的invokeMethod对象取得的就是public Object invoke() throws Throwable这个方法本身,null表示无参数,this表示自身

后置增强是返回结果后增强,所以先去调用invoke---Object returnValue = this.invoke();,然后再增强

如果i<advices.size()不成立,那就执行目标方法return method.invoke(target, args);

public class AopAdviceChainInvocation {

    private static Method invokeMethod;
    static {
        try {
            invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    private Object proxy;
    private Object target;
    private Method method;
    private Object[] args;
    private List<Object> advices;

    public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
        super();
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.args = args;
        this.advices = advices;
    }

    // 责任链执行记录索引号
    private int i = 0;

    public Object invoke() throws Throwable {
        if (i < this.advices.size()) {
            Object advice = this.advices.get(i++);
            if (advice instanceof MethodBeforeAdvice) {
                // 执行前置增强
                ((MethodBeforeAdvice) advice).before(method, args, target);
            } else if (advice instanceof MethodSurroudAdvice) {
                // 执行环绕增强和异常处理增强。注意这里给入的method 和 对象 是invoke方法和链对象
                return ((MethodSurroudAdvice) advice).invoke(invokeMethod, null, this);
            } else if (advice instanceof AfterReturningAdvice) {
                // 当是后置增强时,先得得到结果,再执行后置增强逻辑
                Object returnValue = this.invoke();
                ((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
                return returnValue;
            }
            return this.invoke();
        } else {
            return method.invoke(target, args);
        }
    }
}
JdkDynamicAopProxy和CglibDynamicAopProxy的代码实现

jdk:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

    private String beanName;
    private Object target;
    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建代理。");
        }
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }

}

cglib:

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {

    private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class);
    private static Enhancer enhancer = new Enhancer();

    private String beanName;
    private Object target;

    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);
        Constructor<?> constructor = null;
        try {
            constructor = superClass.getConstructor(new Class<?>[] {});
        } catch (NoSuchMethodException | SecurityException e) {

        }
        if (constructor != null) {
            return enhancer.create();
        } else {
            BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

}

两个动态代理也只是用了它们本身的一些api而已,我们只需要看懂我们的逻辑即可,注意的是cglib的动态代理中我们必须做到传递构造参数类型和构造参数,此时我们又有了新的问题

如何传递创建bean实例时获得的数据到初始化后的aop中

创建时,是在defaultBeanFactory来进行操作
增强时,经过了AdvisorAutoProxyCreator
使用时,又在CglibDynamicAopProxy

此时我们考虑将参数放在bean定义中使用ThreadLocal持有参数值,
因为BeanDefinition是同一个类的多个实例。
特别是prototype的时候,是共享的,
我们需要保证每一个创建流程中都是使用不同线程的数据

我们在BeanDefinition中加入了两个方法:因为我们本身在BeanDefiniion中就已经有了构造器的缓存(public Constructor getConstructor(); public void setConstructor(Constructor constructor);),参数类型我们可以通过它们来获得

List<?> getConstructorArgumentValues();
public Object[] getConstructorArgumentRealValues();

我们可以通过CglibDynamicAopProxy中的getProxy方法来更好理解

else {
    BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
    return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
}

这里就是使用了getConstructor()来取得构造器,然后调用 getParameterTypes()来取得了构造参数的类型

⑤ 如何在Creator中使用AopProxy

应用工厂模式把选择的逻辑交给工厂

public interface AopProxyFactory {
    AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
            throws Throwable;

    /**
     * 获得默认的AopProxyFactory实例
     *
     * @return AopProxyFactory
     */
    static AopProxyFactory getDefaultAopProxyFactory() {
        return (AopProxyFactory) new DefaultAopProxyFactory();
    }
}

之后我们实现了一个默认的工厂,这里做了判断是使用jdk还是cglib,我们省略了判断而直接使用了cglib,spring源码里面的判断也是如此,但是源码还另外存在某些机制来判断,在这里我们也不再过多阐述了,在以后源码解读的时候会再展开

public class DefaultAopProxyFactory implements AopProxyFactory{
    @Override
    public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
            throws Throwable {
        // 是该用jdk动态代理还是cglib?
        if (shouldUseJDKDynamicProxy(bean, beanName)) {
            return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
        } else {
            return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
        }
    }

    private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
        // 如何判断?有实现接口就用JDK,没有就用cglib?
        return false;
    }
}

四、下一篇依旧是Spring的配置,之后会是源码

测试代码相对还是较多,就不粘贴出来了,AOP这一篇的代码很多,而且比较凌乱(自己都这么觉得),需要多花时间去整理总结,有必要的话在之后会再一次进行一次更加详细点的总结篇,把一些过程再讲述清楚点。有问题或者是建议可在留言区提出让我改进。共勉,谢谢。