手写Spring-第十二章-Engage!将AOP和Spring生命周期结合起来

前言

这里先说一点题外话。由于明年一月份火焰纹章系列新作-火焰纹章Engage就要发售了,我作为一个老火纹厨表示,这个2022年真的一秒钟也待不下去了(恼)。所以就用了这么个标题。也算是强行契合了这次的主题。Engage,有结合的意思。我们上一章用动态代理,实现了AOP的核心功能。但是这个功能的实现相对比较独立,和前面的内容有很强的割裂感。那我们自然是不能满足于这样的现状的,直接把这样的内容交给用户,是很难使用的。我们需要把它和Spring的生命周期结合起来,以配置的方式,达到润物细无声的效果。さあ、私と、Engage!(快到2023年吧~

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─aop
│  │  │              │  │  AdvisedSupport.java
│  │  │              │  │  Advisor.java
│  │  │              │  │  BeforeAdvice.java
│  │  │              │  │  ClassFilter.java
│  │  │              │  │  MethodBeforeAdvice.java
│  │  │              │  │  MethodMatcher.java
│  │  │              │  │  Pointcut.java
│  │  │              │  │  PointcutAdvisor.java
│  │  │              │  │  TargetSource.java
│  │  │              │  │  
│  │  │              │  ├─aspect
│  │  │              │  │      AspectJExpressionPointcut.java
│  │  │              │  │      AspectJExpressionPointcutAdvisor.java
│  │  │              │  │  
│  │  │              │  └─framework
│  │  │              │      │  AopProxy.java
│  │  │              │      │  Cglib2AopProxy.java
│  │  │              │      │  JdkDynamicAopProxy.java
│  │  │              │      │  ProxyFactory.java
│  │  │              │      │  ReflectiveMethodInvocation.java
│  │  │              │      │  
│  │  │              │      ├─adapter
│  │  │              │      │      MethodBeforeAdviceInterceptor.java
│  │  │              │      │  
│  │  │              │      └─autoproxy
│  │  │              │              DefaultAdvisorAutoProxyCreator.java
│  │  │              │        
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  Aware.java
│  │  │              │      │  BeanClassLoaderAware.java
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  BeanFactoryAware.java
│  │  │              │      │  BeanNameAware.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  DisposableBean.java
│  │  │              │      │  FactoryBean.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  InitializingBean.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanDefinitionRegistryPostProcessor.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      InstantiationAwareBeanPostProcessor.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      DisposableBeanAdapter.java
│  │  │              │      │      FactoryBeanRegistrySupport.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │        
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ApplicationContextAware.java
│  │  │              │  │  ApplicationEvent.java
│  │  │              │  │  ApplicationEventPublisher.java
│  │  │              │  │  ApplicationListener.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  ├─event
│  │  │              │  │      AbstractApplicationEventMulticaster.java
│  │  │              │  │      ApplicationContextEvent.java
│  │  │              │  │      ApplicationEventMulticaster.java
│  │  │              │  │      ContextClosedEvent.java
│  │  │              │  │      ContextRefreshEvent.java
│  │  │              │  │      SimpleApplicationEventMulticaster.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ApplicationContextAwareProcessor.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │    
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │    
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │                
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              │  ApiTest.java
│      │              │  
│      │              └─bean
│      │                      IUserDao.java
│      │                      UserDao.java
│      │                      UserDaoBeforeAdvice.java
│      │                
│      └─resources
│              spring.xml

Advice:收下我的建议吧!

我们讨论AOP,就不能不讨论Advice。如果我们通览一遍代码的话,会发现与Advice相关的内容非常多。那么到底什么是Advice呢?这里的Advice,可以被解释为【通知】。我们知道,实际上AOP就是我们对原有bean的一系列增强,那么Advice就是我们要增强的逻辑。当然就我个人而言,我也倾向于把它感性地解释为【建议】,我给你的建议,就是对你功能的增强,这样去理解。那么接下来,我们就来完成AOP中的Advice系列内容。

这次我们准备做一个方法执行前的切面,所以我们需要有一个BeforeAdvice。

package com.akitsuki.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 14:52
 */
public interface BeforeAdvice extends Advice {
}

可以看到,它继承了一个标记接口Advice,代表我们的BeforeAdvice,也是一个Advice。那么我们还需要更加具体的去描述它,具体到哪里呢?方法。

package com.akitsuki.springframework.aop;

import java.lang.reflect.Method;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:00
 */
public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method method, Object[] args, Object target) throws Throwable;
}

这次我们有了一个方法,before。那么我们将要实现的before方法,就是我们要对真正bean要执行的方法,进行的增强内容了。我们在调用真正的bean方法之前,这里的before会先执行。

有了【建议】,那么提出建议的人是谁?自然是【建议者】了

package com.akitsuki.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 14:56
 */
public interface Advisor {

    Advice getAdvice();
}

可以看到我们通过getAdvice方法,就可以获取到对应的【建议】。我们接下来,还要对这个概念进行细分。建议者,是谁的建议者呢?是【切点】的建议者

package com.akitsuki.springframework.aop;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:02
 */
public interface PointcutAdvisor extends Advisor {

    Pointcut getPointcut();
}

我们上一章中也介绍了 Pointcut,切点。它提供了两个方法,分别是获取类过滤器和方法匹配器。通过这两个方法,我们也可以看出,它的作用是用来判断【应该切谁】这件事情。通过类和方法的筛选,我们就可以确定到一个具体的对象了。

那么有了这么个接口,我们自然需要一个类去实现它,来让它更加具体。我们这次的实现,都是用AspectJ表达式来进行的,所以我们需要一个基于AspectJ的实现类,来实现这个接口。

package com.akitsuki.springframework.aop.aspect;

import com.akitsuki.springframework.aop.Pointcut;
import com.akitsuki.springframework.aop.PointcutAdvisor;
import org.aopalliance.aop.Advice;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:06
 */
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {

    /**
     * 切面
     */
    private AspectJExpressionPointcut pointcut;

    /**
     * 具体的拦截方法
     */
    private Advice advice;

    /**
     * 表达式
     */
    private String expression;

    public void setExpression(String expression) {
        this.expression = expression;
    }

    public void setAdvice(Advice advice) {
        this.advice = advice;
    }

    @Override
    public Advice getAdvice() {
        return advice;
    }

    @Override
    public Pointcut getPointcut() {
        if (null == pointcut) {
            pointcut = new AspectJExpressionPointcut(expression);
        }
        return pointcut;
    }
}

这里有一些我们的老朋友,AspectJExpressionPointcut是我们上一章实现的,它的主要内容是对类过滤器和方法匹配器的管理和使用,用它来判断一个bean是否应该被当前表达式进行切入。expression则是我们的表达式,用来指定要切的位置的属性。

MethodInterceptor:站住别动

MethodInterceptor,方法拦截器。可以把即将要执行的方法拦截下来,在需要的时候再去执行(当然,你也可以不让它执行)。对于我们的Before切面来说,我们需要在方法执行前,执行我们的切面逻辑,然后再执行方法的正常逻辑。所以,我们需要一个Before的方法拦截器。

package com.akitsuki.springframework.aop.framework.adapter;

import com.akitsuki.springframework.aop.MethodBeforeAdvice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:15
 */
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {

    private MethodBeforeAdvice advice;

    public  MethodBeforeAdviceInterceptor() {}

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());
        return methodInvocation.proceed();
    }
}

可以看到,这里的逻辑,就是在方法执行前,调用了advice的before方法,执行了切面逻辑。

ProxyFactory:就让我来生产代理!

我们知道,实现代理有多种方式,所以我们需要一个工厂来帮我们生产对应的代理对象。

package com.akitsuki.springframework.aop.framework;

import com.akitsuki.springframework.aop.AdvisedSupport;
import lombok.AllArgsConstructor;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:19
 */
@AllArgsConstructor
public class ProxyFactory {

    private AdvisedSupport advisedSupport;

    public Object getProxy() {
        return createProxy().getProxy();
    }

    private AopProxy createProxy() {
        if (advisedSupport.isProxyTargetClass()) {
            return new Cglib2AopProxy(advisedSupport);
        }
        return new JdkDynamicAopProxy(advisedSupport);
    }
}

可以看到,我们具体使用什么方法来生产代理对象,是取决于 adviceSupportisProxyTargetClass 的。通过true和false两种状态,来选择 cglibjdk两种方式。我们在使用时,只需要调用 getProxy方法,就可以获得最终的被代理后的对象了。

Engage!让我与你结合!

写到这儿,都快结束了,终于扯到我们的主题了:如何与Spring的生命周期结合起来。我们知道,Spring的核心是Bean。我们生产的代理对象,终究也是Bean。而且,我们的这些Advice、Interceptor、Advisor之类的,也都需要成为一个Bean,才能够方便Spring进行管理。

但是仅仅作为Bean还不够,我们还需要将这个过程变得自动化。让Spring初始化完成这些Bean之后,我们的这一系列功能就顺带着被搞定了。所以这里就要用到我们前面几章学习到的工具:后置处理器PostProcessor。既然要在初始化Bean时候的完成,那么我们自然就要在这一步,加入处理器,来帮我们搞定AOP的一系列内容。

我们需要两样东西:一个处理类,一个后置处理器。处理类是真正干活的,处理器则是用来插入Bean创建过程的。这里我们需要新加入一种后置处理器,它在Bean创建的最开始进行工作,它用于提供一种特殊的Bean实例化过程,通过这种过程,Bean的实例化不需要走默认的那套实例化。

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:46
 */
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * bean实例化之前处理
     * @param beanClass
     * @param beanName
     * @return
     * @throws BeanException
     */
    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException;
}

有了这么一个后置处理器,我们就来实现真正的处理类吧。

package com.akitsuki.springframework.aop.framework.autoproxy;

import com.akitsuki.springframework.aop.*;
import com.akitsuki.springframework.aop.aspect.AspectJExpressionPointcutAdvisor;
import com.akitsuki.springframework.aop.framework.ProxyFactory;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
import com.akitsuki.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:31
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {
        if (isInfrastructureClass(beanClass)) {
            return null;
        }
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) {
                continue;
            }
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = null;
            try {
                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }

            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();
        }
        return null;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }
}

好长好长,我们从上往下分析。

首先我们看到,这个类实现了我们上面的那个处理器,还有BeanFactoryAware接口,以便将BeanFactory注入进来。再往下是一个私有的判断方法,判断是否为这几种AOP中的基本类型。然后由于 InstantiationAwareBeanPostProcessor继承了 BeanPostProcessor,所以也有bean实例化前后的处理方法。不过在这里我们用不到,所以直接返回即可,我们的重头戏在 postProcessBeforeInstantiation的实现。

在方法的开头,就对这些AOP的基本类型进行了判断,如果是这些基本类型,是不走代理的,所以直接返回。接下来是对所有Advisor的一个遍历,主要是为了找到一个满足当前Bean的class的一个Advisor,然后构造出一个AdvisedSupport,将TargetSource、方法拦截器、方法匹配器、代理类型等数据给封装进去。这里需要注意的是,真实对象的创建,是在构建TargetSource的时候,通过反射进行的。这里进行了一些简化处理,只调用了无参的构造方法,实际上这里应该更加完善。最后,新建一个代理工厂,生产出最终的代理对象进行返回,我们的处理就算完成了。

我们要做的最后一步,就是进行织入,结合,Engage!因为我们虽然有了后置处理器,但我们的这个后置处理器实际上还没有发挥作用,我们要将其融入Bean的创建过程中。所以我们还需要回到我们的老朋友 AbstractAutowireCapableBeanFactory那里。

package com.akitsuki.springframework.beans.factory.support;

/**
 * 抽象的实例化Bean类,提供创建Bean的能力,创建完成后,放入单例bean map中进行缓存
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:12
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
        Object bean;
        try {
            bean = resolveBeforeInstantiation(beanName, beanDefinition);
            if (null != bean) {
                return bean;
            }
            bean = createBeanInstance(beanDefinition, beanName, args);
            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean,执行beanPostProcessor的前置和后置方法
            initializeBean(beanName, bean, beanDefinition);
            //注册实现了DisposableBean接口的对象
            registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
            if (beanDefinition.isSingleton()) {
                //创建好的单例bean,放入缓存
                addSingleton(beanName, bean);
            }
        } catch (Exception e) {
            throw new BeanException("创建bean失败", e);
        }
        return bean;
    }

    protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        if (null != bean) {
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                Object result = ((InstantiationAwareBeanPostProcessor) processor).postProcessBeforeInstantiation(beanClass, beanName);
                if (null != result) {
                    return result;
                }
            }
        }
        return null;
    }
}

这里我做了一些精简,只保留了有变化的方法,毕竟这个类经过多次扩充完善,已经变得越来越庞大了。

我们首先看织入点:createBean方法。这里我们其实只需要看前几行就可以了。resolveBeforeInstantiation是我们要做事情的方法,如果这个方法成功返回了一个Bean,那么就可以直接返回了,不需要走下面的那一套标准的Bean创建流程。这就是我们上面说的,提供一个特殊的Bean创建流程。那接下来我们看 resolveBeforeInstantiation的内容。它的内容也很简单,是调用 applyBeanPostProcessorsBeforeInstantiation,同样的,如果成功返回了一个Bean,则去调用实例化完成后置处理器,就完成了。看来我们的真正逻辑,还得跟踪到 applyBeanPostProcessorsBeforeInstantiation中。去看一眼,原来如此,是拿到了所有的Bean后置处理器,然后挑出来 InstantiationAwareBeanPostProcessor类型的,去挨个调用处理方法。如果有一个方法返回的结果不为空(也就意味着成功生产出了Bean),就返回。否则,继续找,直到找到为止,或者循环结束,最后返回null。结合我们上面的处理,可以看出,这里如果返回了null,就会走正常的bean创建逻辑。那么我们的这个处理器的逻辑就算织入完毕了。

测试!我们的结合必将如利剑般撕破黑暗

这个系列写到现在,唯一不变的可能就是各种意义不明的标题了。无奈作者我是一个中二少年(少年?),就算成为了码农,还是会时不时泄露出来一些中二气息(笑。

好了,我们来看测试。上次测试中的接口和Dao,我们不需要修改。这次我们需要实现一个MethodBeforeAdvice,作为我们的切面处理。

package com.akitsuki.springframework.bean;

import com.akitsuki.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/7 10:09
 */
public class UserDaoBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法执行了:" + method);
    }
}

实现了MethodBeforeAdvice接口,并且实现了before方法。可以推断,当方法被拦截下来之后,这里的before方法就会先执行,打印出相应的方法名。

然后,这次我们的配置文件,终于有用武之地了。我已经等不及了,快点端上来罢(无端联想

<beans>
    <bean id="userDao" class="com.akitsuki.springframework.bean.UserDao"/>
    <bean class="com.akitsuki.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    <bean id="beforeAdvice" class="com.akitsuki.springframework.bean.UserDaoBeforeAdvice"/>
    <bean id="methodInterceptor" class="com.akitsuki.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
        <property name="advice" ref="beforeAdvice"/>
    </bean>
    <bean id="pointcutAdvisor" class="com.akitsuki.springframework.aop.aspect.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* com.akitsuki.springframework.bean.IUserDao.*(..))"/>
        <property name="advice" ref="methodInterceptor"/>
    </bean>

</beans>

这次可不得了,一下子配置了好多的bean。首先是我们的老朋友,UserDao。紧接着是我们的处理类,也就是实现了后置处理器,要去生产代理Bean的类,我们的小英雄。再往下,是我们刚才写的 UserDaoBeforeAdvice,也就是真正AOP增强的内容。然后是方法拦截器,有了它我们才能拦截下方法,同时它也配置了一个属性advice,毕竟把方法拦截下来之后,具体做什么,还得看advice,自然就是我们上面的那个advice了。最后是我们的Advisor,它里面配置了表达式,用来表明我们具体要拦截哪个方法。这里的配置是拦截 IUserDao的所有方法。然后它也有一个Advice,是我们的方法拦截器。到这里可能会有点乱,我们理一下。

我们的Advice,有两个:一个是 UserDaoBeforeAdvice,这个是增强的内容。一个是 MethodBeforeAdviceInterceptor,这个是方法拦截器,用来拦下方法的执行。具体的依赖关系是下面这样:

AspectJExpressionPointcutAdvisor通过表达式进行匹配,依赖 MethodBeforeAdviceInterceptor进行方法的拦截。MethodBeforeAdviceInterceptor完成拦截后,则依赖 UserDaoBeforeAdvice来执行增强内容。而这一系列过程,则都是通过 DefaultAdvisorAutoProxyCreator在前置处理器中进行组装完成的。这就是整体的结构。

最后是我们的主要测试类

package com.akitsuki.springframework;

import com.akitsuki.springframework.bean.IUserDao;
import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/5 16:46
 */
public class ApiTest {

    @Test
    public void test() {
        ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
        IUserDao userDao = context.getBean("userDao", IUserDao.class);
        System.out.println(userDao.queryUserName(1L));
    }
}

嗯,非常简单,从这里完全看不出AOP的影子

测试结果

方法执行了:public abstract java.lang.String com.akitsuki.springframework.bean.IUserDao.queryUserName(java.lang.Long)
akitsuki

Process finished with exit code 0

结合的非常好~方法名被成功打印出来了。而且在使用中完全感觉不到AOP的存在,真正的润如细无声,无侵入性。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-12

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值