美文网首页Java后端
一步一步教你写 SPRING AOP

一步一步教你写 SPRING AOP

作者: 西部小笼包 | 来源:发表于2018-09-01 18:29 被阅读188次

源码地址:
https://github.com/yixuaz/myspring
如看在开始前,你还不知道动态代理在JAVA里的2种实现方式,不知道BEAN POST PROCESSOR的作用。
请先看SPRING AOP 准备工作

我们开始吧。
首先同样我们要定一个目标。
AOP 的XML 应该是什么样的呢?

Step1. 在IOC 的XML 基础上加上这3行, 构建代理接口,实现2种代理

<bean id="autoProxyCreator" class="com.myspring.aop.AspectJAwareAdvisorAutoProxyCreator"></bean>

    <bean id="timeInterceptor" class="com.myspring.aop.TimerInterceptor"></bean>

    <bean id="aspectjAspect" class="com.myspring.aop.AspectJExpressionPointcutAdvisor">
        <property name="advice" ref="timeInterceptor"></property>
        <property name="expression" value="execution(* com.myspring.*.*(..))"></property>
    </bean>

把该有的类创建出来。
现在XML 如下


image.png

这里的TIME INTERCEPTOR, 就是一个代理方法,也称增强方法,它的作用就是让被切到的方法,都做一个计时的增强。
为了方面我们需要引入2个AOP提供的接口。当然自己实现也是可以的。因为是接口。所以我们为了偷懒直接引入
POM.XML 加上

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>2.2</version>
    </dependency>
<dependency>
          <groupId>aopalliance</groupId>
          <artifactId>aopalliance</artifactId>
          <version>1.0</version>
      </dependency>

这个类写出来

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class TimerInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long time = System.nanoTime();
        System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!");
        Object proceed = invocation.proceed();
        System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time)
                + " nanoseconds.");
        return proceed;
    }
}

因为AOP需要代理所以我们定一个代理接口,因为代理有2种实现,一种是JDK,一种是CGLIB。所以我们需要一个接口来屏幕底层实现。因为用户不需要关心底层SPRING用了什么实现。

public interface AopProxy {
    Object getProxy();
}

下面在实现JDK的代理类前我们要思考,因为JDK的实现里,就是INVOKE里面,一个反射调用,在INVOKE方法位置做增强


image.png

我们写AOP为了通用一点,我们应该用methodInterceptor这个接口表示增强方法。然后调用这个INVOKE方法。
那这个方法又是传入一个METHOD INVOCATION,里面封装了,这个增强方法要的一切参数,不如我们先把这个实现类给写掉。

public class ReflectiveMethodInvocation implements MethodInvocation {
    private Object target;

    private Method method;

    private Object[] args;

    public ReflectiveMethodInvocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return args;
    }

    @Override
    public Object proceed() throws Throwable {
        return method.invoke(target, args);
    }

    @Override
    public Object getThis() {
        return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
        return method;
    }
}

解决了INVOCATION的问题,下面看还缺什么
在取得代理对象的时候,我们需要一个TAR 的CLASS 还有 INTERFACES。


image.png

所以增强方法和要代理的元数据,我们可以封装在一起,作为一个类,传入JDK动态代理PROXY里
这个类就叫AdvisedSupport

public class TargetSource {
    private Class targetClass;

    private Class[] interfaces;
    private Object target;

    public TargetSource(Object target, Class<?> targetClass,Class<?>[] interfaces) {
        this.target = target;
        this.targetClass = targetClass;
        this.interfaces = interfaces;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public Object getTarget() {
        return target;
    }

    public Class[] getInterfaces() {
        return interfaces;
    }
}
public class AdvisedSupport {
    MethodInterceptor methodInterceptor;
    TargetSource targetSource;

    public MethodInterceptor getMethodInterceptor() {
        return methodInterceptor;
    }

    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    public TargetSource getTargetSource() {
        return targetSource;
    }

    public void setTargetSource(TargetSource targetSource) {
        this.targetSource = targetSource;
    }
}

有了这个类之后,我们就可以把JDK PROXY写掉了。

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    AdvisedSupport advisedSupport;

    public JdkDynamicAopProxy(AdvisedSupport advisedSupport) {
        this.advisedSupport = advisedSupport;
    }

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(),
                advisedSupport.getTargetSource().getInterfaces()}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInterceptor methodInterceptor = advisedSupport.getMethodInterceptor();
        return methodInterceptor.invoke(new ReflectiveMethodInvocation(advisedSupport.getTargetSource().getTarget(), method, args));
    }
}

来做个简单的测试
为了测试JDK,必须要有INTERFACE,我们就为HELLOWORLD,建一个接口

public interface HelloWorldService {

    void helloWorld();
}

测试

@Test
    public void testInterceptor() throws Exception {
        // --------- helloWorldService without AOP
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        helloWorldService.helloWorld();

        // --------- helloWorldService with AOP
        // 1. 设置被代理对象(Joinpoint)
        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(helloWorldService,  helloWorldService.getClass(), helloWorldService.getClass().getInterfaces());
        advisedSupport.setTargetSource(targetSource);

        // 2. 设置拦截器(Advice)
        TimerInterceptor timerInterceptor = new TimerInterceptor();
        advisedSupport.setMethodInterceptor(timerInterceptor);

        // 3. 创建代理(Proxy)
        JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport);
        HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy();

        // 4. 基于AOP的调用
        helloWorldServiceProxy.helloWorld();

    }

测试成功:
output:Hello World!
Invocation of Method helloWorld start!
output:Hello World!
Invocation of Method helloWorld end! takes 89181 nanoseconds.

当前项目结构:


image.png

Step2 上述代码解决了怎么代理增强的问题,我们下面要解决怎么切的问题

还记得XML里* com.myspring..(..) 吗?
这个就是表示,切面的范围。
对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:ClassFilter和MethodMatcher,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和AspectJ表达式的方式。

根据上述定义,我们先写一个POINTCUT 接口

public interface ClassFilter {
    boolean matches(Class targetClass);
}
public interface MethodMatcher {

    boolean matches(Method method, Class targetClass);
}
public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

}

我们有了切面的接口,下面是怎么切的定义,这就是ADVISE()
在AOP里有环绕通知,前置通知,后置通知,异常通知,返回通知等。
定义一个获取通知类型的接口

import org.aopalliance.aop.Advice;//这里偷懒

public interface Advisor {
    Advice getAdvice();
}

再来一个接口去获取切面和通知的的

public interface PointcutAdvisor extends Advisor{

   Pointcut getPointcut();
}

接口全部定义好啦!

Step3 实现切面

首先把三个接口都实现下。
随后对于这个类来说,我们肯定会拿到一个ASPECJ表达式。所以这个表达式,要注入进来的。加一个expression 变量。
随后,我们要解析这个表达式,需要用到AOP里的PARSER了。
最后匹配方法的时候,要么全MATCH 就OK,其他都返回FALSE。
这个类的大多数代码,都是参考着org.springframework.aop.aspectj.AspectJExpressionPointcut
来写
然后做了简化如下

public class AspectJExpressionPointcut implements PointCut,ClassFilter,MethodMatcher {

    private String expression;
    private PointcutParser pointCutParser;
    private PointcutExpression pointcutExpression;

    public AspectJExpressionPointcut() {
        pointCutParser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
    }

    public String getExpression() {
        return expression;
    }

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

    @Override
    public boolean matches(Class targetClass) {
        checkReady();
        return pointcutExpression.couldMatchJoinPointsInType(targetClass);
    }
    
    private void checkReady() {
        if(pointcutExpression == null)
            pointcutExpression = pointCutParser.parsePointcutExpression(expression);
    }

    @Override
    public boolean matches(Method method, Class targetClass) {
        checkReady();
        ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
        if (shadowMatch.alwaysMatches()) {
            return true;
        }
        return false;
    }

    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
}

然后实现下AspectJExpressionPointcutAdvisor
这个类,主要就是对外提供ADVICE,和我们前面实现好的POINTCUT

public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
    private AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();

    private Advice advice;

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

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


    @Override
    public PointCut getPointCut() {
        return pointcut;
    }

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

下面测试,
当然你可以学一下ASPECTJ EXPRESION,写出更多表达式来测试。
http://sishuok.com/forum/posts/list/281.html

image.png

Step4 实现融入Bean的创建过程,实现自己的BEAN POST PROCESSOR逻辑

现在我们有了Pointcut和Weave技术,一个AOP已经算是完成了,但是它还没有结合到Spring中去。怎么进行结合呢?Spring给了一个巧妙的答案:使用BeanPostProcessor。

BeanPostProcessor是BeanFactory提供的,在Bean初始化过程中进行扩展的接口。只要你的Bean实现了BeanPostProcessor接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。

那么我们的AOP是怎么实现的呢?我们知道,在AOP的xml配置中,我们会写这样一句话:

<aop:aspectj-autoproxy/>
它其实相当于:

<bean id="autoProxyCreator" class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator"></bean>

AspectJAwareAdvisorAutoProxyCreator就是AspectJ方式实现织入的核心。它其实是一个BeanPostProcessor。在这里它会扫描所有Pointcut,并对bean做织入。

先定义出BEAN POST PROCESSOR 这个接口

public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception;

    Object postProcessAfterInitialization(Object bean, String beanName) throws Exception;

}

随后根据我们上篇文章的分析,在实例化BEAN前,要把这个PROCESSOR给注册上去。
所以我们找到AbstractApplicationContext 的 refresh方法 开始加上一步

随后我们要在BEAN FACTORY 里维护所有的BEAN POST PROCESSOR

随后在BEAN 完成doCreateBean() 后 ,还需要做一个initializeBean()

更新后的BEAN FACTORY
1。实现2个新方法
第一个方法是为了提前找到并且初始化一些特定CLASS TYPE的BEAN
第二个就是维护所有的BEAN POST PROCESSOR 的BEAN

public List getBeansForType(Class<?> type) throws Exception {
        List beans = new ArrayList<Object>();
        for (String beanDefinitionName : beanDefinitionMap.keySet()) {
            if (type.isAssignableFrom(beanDefinitionMap.get(beanDefinitionName).getBeanClass())) {
                beans.add(getBean(beanDefinitionName));
            }
        }
        return beans;
    }

    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        beanPostProcessors.add(beanPostProcessor);
    }

随后
修改这边

@Override
    public Object getBean(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition == null)
            throw new IllegalArgumentException("No bean named " + beanName + " is defined");

        Object bean = beanDefinition.getBean();
        if(bean == null){
            bean = doCreateBean(bean,beanDefinition);
            bean = initializeBean(bean, beanName);
            beanDefinition.setBean(bean);
        }


        return bean;
    }

    private Object initializeBean(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
        }

        // TODO:call initialize method
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

完整代码

public abstract class AbstractBeanFactory implements BeanFactory {
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

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

    @Override
    public Object getBean(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition == null)
            throw new IllegalArgumentException("No bean named " + beanName + " is defined");

        Object bean = beanDefinition.getBean();
        if(bean == null){
            bean = doCreateBean(bean,beanDefinition);
            bean = initializeBean(bean, beanName);
            beanDefinition.setBean(bean);
        }


        return bean;
    }

    private Object initializeBean(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
        }

        // TODO:call initialize method
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

    protected abstract  Object doCreateBean(Object bean, BeanDefinition beanDefinition) throws Exception;

    public void preInstantiateSingletons() throws Exception {
        for(String name : beanDefinitionMap.keySet())
            getBean(name);
    }

    public void registerBeanDefinition(String key, BeanDefinition value) {
        beanDefinitionMap.put(key, value);
    }


    public List getBeansForType(Class<?> type) throws Exception {
        List beans = new ArrayList<Object>();
        for (String beanDefinitionName : beanDefinitionMap.keySet()) {
            if (type.isAssignableFrom(beanDefinitionMap.get(beanDefinitionName).getBeanClass())) {
                beans.add(getBean(beanDefinitionName));
            }
        }
        return beans;
    }

    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        beanPostProcessors.add(beanPostProcessor);
    }
}

增加ABSTRACT APPLICATION CONTEXT 逻辑

public abstract class AbstractApplicationContext implements  ApplicationContext {
    protected AbstractBeanFactory beanFactory;

    public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    
    public void refresh() throws  Exception {
        loadBeanDefinitions();
        registerBeanPostProcessors(beanFactory); // new added
        finishBeanFactoryInitialization();
    }

    private void registerBeanPostProcessors(AbstractBeanFactory beanFactory) throws Exception {
        List beanPostProcessors = beanFactory.getBeansForType(BeanPostProcessor.class);
        for (Object beanPostProcessor : beanPostProcessors) {
            beanFactory.addBeanPostProcessor((BeanPostProcessor) beanPostProcessor);
        }
    }

    protected void finishBeanFactoryInitialization() throws Exception {
        beanFactory.preInstantiateSingletons();
    }

    protected abstract void loadBeanDefinitions() throws Exception;

    @Override
    public Object getBean(String beanName) throws Exception {
        return beanFactory.getBean(beanName);
    }
}

Step5 开始实现一开始新建出来一直没实现的BEAN AspectJAwareAdvisorAutoProxyCreator

在实现前,我们再理一下整个流程。

  1. AutoProxyCreator(实现了 BeanPostProcessor 接口)在实例化所有的 Bean 前,最先被实例化。因为registerBeanPostProcessor 在 finishInitiliazingBean 之前。

2.这个时候,他被BEAN FACTORY 通过getBeansForType 找到,并且调用了getBean(beanDefinitionName),最先实例化。

3.随后在getBEAN实例化的时候,他会先doCreateBean,这个时候,他就应该把BEAN FACTORY 给注册进去。因为之后要做postProcessAfterInitialization,来找到所有的切面我们需要用到BEAN FACTORY。
为了实现这个效果。我们搞一个接口出来。

public interface BeanFactoryAware {
    public void setBeanFactory(BeanFactory beanFactory);
}

让AutoProxyCreator实现一下。
随后在doCreateBean 的 applyProPertyValues,注入BEAN FACTORY。
代码如下

 private void applyProPertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
        if(bean instanceof BeanFactoryAware){
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
....

随后要实现BeanPostProcessor 的2个方法。
我们在后置处理器里实现的效果应该是,找到所有‘切面通知BEAN’(实现了这个AspectJExpressionPointcutAdvisor )或者‘’增强方法BEAN’(实现了这个MethodInterceptor),随后依次加载他们。因为他们不要被代理,所以直接RETURN。

@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        if (bean instanceof AspectJExpressionPointcutAdvisor) {
            return bean;
        }
        if (bean instanceof MethodInterceptor) {
            return bean;
        }
        List<AspectJExpressionPointcutAdvisor> advisors = beanFactory
                .getBeansForType(AspectJExpressionPointcutAdvisor.class);
...
  1. 其他普通 Bean 被实例化、初始化,在初始化的过程中,AutoProxyCreator 加载 BeanFactory 中所有的 PointcutAdvisor(这也保证了 PointcutAdvisor 的实例化顺序优于普通 Bean。),然后依次使用 PointcutAdvisor 内置的 ClassFilter,判断当前对象是不是要拦截的类。

  2. 如果是,则生成一个 TargetSource(要拦截的对象和其类型),并取出 AutoProxyCreator 的 MethodMatcher(对哪些方法进行拦截)、Advice(拦截的具体操作),再,交给 AopProxy 去生成代理对象。

//继续上面的代码
for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            if (advisor.getPointCut().getClassFilter().matches(bean.getClass())) {
                AdvisedSupport advisedSupport = new AdvisedSupport();
                advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                advisedSupport.setMethodMatcher(advisor.getPointCut().getMethodMatcher());
//这里往AdvisedSupport  多加个MethodMatcher属性
                TargetSource targetSource = new TargetSource(bean, bean.getClass(),bean.getClass().getInterfaces());
                advisedSupport.setTargetSource(targetSource);

                return new JdkDynamicAopProxy(advisedSupport).getProxy();
            }
        }
        return bean;
}
  1. AopProxy 生成一个 InvocationHandler,在它的 invoke 函数中,首先使用 MethodMatcher 判断是不是要拦截的方法,如果是则交给 Advice 来执行(Advice 由用户来编写,其中也要手动/自动调用原始对象的方法),如果不是,则直接交给 TargetSource 的原始对象来执行。

修改JDKDynamicProxy代码

 @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInterceptor methodInterceptor = advisedSupport.getMethodInterceptor();
        if (advisedSupport.getMethodMatcher() != null
                && advisedSupport.getMethodMatcher().matches(method, advisedSupport.getTargetSource().getTarget().getClass())) {
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advisedSupport.getTargetSource().getTarget(),
                    method, args));
        } else {
            return method.invoke(advisedSupport.getTargetSource().getTarget(), args);
        }
    }

总结:

BeanPostProcessor :在 postProcessorAfterInitialization 方法中,使用动态代理的方式,返回一个对象的代理对象。解决了 在 IoC 容器的何处植入 AOP 的问题。
BeanFactoryAware :这个接口提供了对 BeanFactory 的感知,这样,尽管它是容器中的一个 Bean,却可以获取容器的引用,进而获取容器中所有的切点对象,决定对哪些对象的哪些方法进行代理。解决了 为哪些对象提供 AOP 的植入 的问题。

打开XML的AOP的注释
开始测试
出错了

java.lang.IllegalArgumentException: Can not set com.myspring.OutputService field com.myspring.HelloWorldServiceImpl.outputService to com.sun.proxy.$Proxy2

    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
    at java.lang.reflect.Field.set(Field.java:758)
    at com.myspring.context.AutowireCapableBeanFactory.applyProPertyValues(AutowireCapableBeanFactory.java:40)
    at com.myspring.context.AutowireCapableBeanFactory.doCreateBean(AutowireCapableBeanFactory.java:13)
    at com.myspring.context.AbstractBeanFactory.getBean(AbstractBeanFactory.java:24)
    at com.myspring.context.AbstractBeanFactory.preInstantiateSingletons(AbstractBeanFactory.java:49)
    at com.myspring.context.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:28)
    at com.myspring.context.AbstractApplicationContext.refresh(AbstractApplicationContext.java:17)
    at com.myspring.context.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:19)
    at com.myspring.context.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:13)
    at test.HelloWorldServiceImplTest.helloWorld(HelloWorldServiceImplTest.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

猜测是JDK 动态代理没法代理非接口的OutputService

所以把这个改成接口,成功了。


image.png

Step6 我们不要接口,用CGLIB 来代理实现下吧

用一个代理工厂来智能选择代理

public interface AopProxy {

    Object getProxy();
}

代理工厂,如果一个代理类有接口 就用JDK动态代理。否则就用CGLIB动态代理。

public class ProxyFactory extends AdvisedSupport implements AopProxy {
    @Override
    public Object getProxy() {
        return createAopProxy().getProxy();
    }

    protected final AopProxy createAopProxy() {
        if(getTargetSource().getInterfaces() == null || getTargetSource().getInterfaces().length == 0)
            return new Cglib2AopProxy(this);
        return new JdkDynamicAopProxy(this);
    }
}

根据上一章的知识,这边不难看懂。

public class Cglib2AopProxy implements AopProxy {

    AdvisedSupport advised;

    public Cglib2AopProxy(AdvisedSupport advised) {

        this.advised = advised;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTargetClass());
        enhancer.setInterfaces(advised.getTargetSource().getInterfaces());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        Object enhanced = enhancer.create();
        return enhanced;
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private AdvisedSupport advised;

        private org.aopalliance.intercept.MethodInterceptor delegateMethodInterceptor;

        private DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
            this.delegateMethodInterceptor = advised.getMethodInterceptor();
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (advised.getMethodMatcher() == null
                    || advised.getMethodMatcher().matches(method, advised.getTargetSource().getTargetClass())) {
                return delegateMethodInterceptor.invoke(new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy));
            }
            return new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy).proceed();
        }
    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy) {
            super(target, method, args);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return this.methodProxy.invoke(this.getThis(), this.getArguments());
        }
    }

}

最后修改AspectJAwareAdvisorAutoProxyCreator的这里

for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            if (advisor.getPointCut().getClassFilter().matches(bean.getClass())) {
                ProxyFactory advisedSupport = new ProxyFactory();
                advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                advisedSupport.setMethodMatcher(advisor.getPointCut().getMethodMatcher());

                TargetSource targetSource = new TargetSource(bean, bean.getClass(),bean.getClass().getInterfaces());
                advisedSupport.setTargetSource(targetSource);

                return advisedSupport.getProxy();
            }
        }
        return bean;

去掉OUTPUTSERVICE 这个接口
测试成功,OUTPUT SERVICE 由CGLIB 动态代理
HELLO WORLD SERVICE 由JDK动态代理


image.png

相关文章

网友评论

  • LJ_0106:还是有点厉害的,我之前看那本迷迷糊糊,现在对宏观有点把握了,回头再看下那本书,估计就懂得多了

本文标题:一步一步教你写 SPRING AOP

本文链接:https://www.haomeiwen.com/subject/iztuwftx.html