SpringAOP从入门到源码分析大全(二)熟悉ProxyFactory

114 阅读11分钟

@[TOC]

系列文档索引

SpringAOP从入门到源码分析大全(一)熟悉动态代理 SpringAOP从入门到源码分析大全(二)熟悉ProxyFactory SpringAOP从入门到源码分析大全(三)ProxyFactory源码分析 SpringAOP从入门到源码分析大全(四)SpringAOP的源码分析 SpringAOP从入门到源码分析大全(五)手写一个编程式AOP

四、Spring AOP的使用入门

1、激活AspectJ模块

激活AspectJ模块分两种方式,一种是使用注解方式,一种使用xml方式。

• 注解激活 - @EnableAspectJAutoProxy • XML 配置 - <aop:aspectj-autoproxy/>

(1)注解激活

import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Aspect        // 声明为 Aspect 切面
@Configuration // Configuration class
@EnableAspectJAutoProxy // 激活 Aspect 注解自动代理
public class AspectJAnnotationDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AspectJAnnotationDemo.class);
        context.refresh();

        AspectJAnnotationDemo aspectJAnnotationDemo = context.getBean(AspectJAnnotationDemo.class);

        System.out.println(aspectJAnnotationDemo.getClass());
        context.close();
    }
}

以上是使用注解激活的实例,使用@EnableAspectJAutoProxy即可激活Aspect注解自动代理。

该AspectJAnnotationDemo类使用@Configuration标注代表是一个配置类,Spring中的配置类都会被代理,默认是使用CGLIB代理。

@Aspect表示该类是一个切面类。

(2)XML激活

在xml文件中使用该标签,即可激活Aspect自动代理:

<aop:aspectj-autoproxy/>

该标签与注解的@EnableAspectJAutoProxy效果相同。

2、创建 @AspectJ 代理(了解)

(1)编程方式创建 @AspectJ 代理实例

实现类: org.springframework.aop.aspectj.annotation.AspectJProxyFactory

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.framework.AopContext;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class AspectJAnnotationUsingAPIDemo {

    public static void main(String[] args) {

        // 通过创建一个 HashMap 缓存,作为被代理对象
        Map<String, Object> cache = new HashMap<>();
        // 创建 Proxy 工厂(AspectJ)
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory(cache);
        // 增加 Aspect 配置类,暂时不需要
//        proxyFactory.addAspect(AspectConfiguration.class);
        // 设置暴露代理对象到 AopContext
        proxyFactory.setExposeProxy(true);
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                if ("put".equals(method.getName()) && args.length == 2) {
                    Object proxy = AopContext.currentProxy();
                    System.out.printf("[MethodBeforeAdvice] 当前存放是 Key: %s , Value : %s ," +
                            "代理对象:%s\n", args[0], args[1], proxy);
                }
            }
        });

        // 添加 AfterReturningAdvice
        proxyFactory.addAdvice(new AfterReturningAdvice() {

            @Override
            public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
                    throws Throwable {
                if ("put".equals(method.getName()) && args.length == 2) {
                    System.out.printf("[AfterReturningAdvice] 当前存放是 Key: %s , 新存放的 Value : %s , 之前关联的 Value : %s\n ",
                            args[0],    // key
                            args[1],    // new value
                            returnValue // old value
                    );
                }
            }
        });

        // 通过代理对象存储数据
        Map<String, Object> proxy = proxyFactory.getProxy();
        proxy.put("1", "A");
        proxy.put("1", "B");
        System.out.println(cache.get("1"));

    }
}

(2)XML 配置创建 AOP 代理

<aop:aspectj-autoproxy/>

<bean id="echoService" class="com.demo.DefaultEchoService"></bean>

<!--拦截器-->
<bean id="echoServiceMethodInterceptor"
      class="com.demo.interceptor.EchoServiceMethodInterceptor"/>

<bean id="echoServiceProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="echoService"/> <!--指定targetName为需要代理的bean的Id-->
    <property name="interceptorNames"> <!--指定拦截器-->
        <value>echoServiceMethodInterceptor</value>
    </property>
</bean>
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;

public class EchoServiceMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        System.out.println("拦截 EchoService 的方法:" + method);
        return invocation.proceed();
    }
}

import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Aspect        // 声明为 Aspect 切面
@Configuration // Configuration class
public class ProxyFactoryBeanDemo {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/spring-aop-context.xml");

        EchoService echoService = context.getBean("echoServiceProxyFactoryBean", EchoService.class);

        System.out.println(echoService.echo("Hello,World"));

        System.out.println(echoService.getClass());
        context.close();
    }
}

我们发现,这种方式与编程的方式创建代理是差不多的,只不过是对象的创建交给Spring来处理了。

(3)标准代理工厂 API

实现类 - org.springframework.aop.framework.ProxyFactory

上面XML配置的方式创建ProxyFactoryBean,更多用于Spring中的应用,而ProxyFactory更偏底层。

import org.springframework.aop.framework.ProxyFactory;

/**
 * ProxyFactory使用示例
 */
public class ProxyFactoryDemo {

    public static void main(String[] args) {
        DefaultEchoService defaultEchoService = new DefaultEchoService();
        // 注入目标对象(被代理)
        ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);
        //proxyFactory.setTargetClass(DefaultEchoService.class);
        // 添加 Advice 实现 MethodInterceptor < Interceptor < Advice
        proxyFactory.addAdvice(new EchoServiceMethodInterceptor());
        // 获取代理对象
        EchoService echoService = (EchoService) proxyFactory.getProxy();
        System.out.println(echoService.echo("Hello,World"));
    }
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;

public class EchoServiceMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        System.out.println("拦截 EchoService 的方法:" + method);
        return invocation.proceed();
    }
}

3、Pointcut 使用

Spring中的Pointcut只支持方法级别的定义,也就是说只支持Bean的方法拦截。

Pointcut 只是一个筛选,并没有具体的动作。 Advice才是具体的动作,一个Pointcut 可以对应多个Advice。

(1)Pointcut 指令与表达式

Spring AOP 之 aspect表达式详解

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * Pointcut 示例
 */
@Configuration // Configuration class
@EnableAspectJAutoProxy // 激活 Aspect 注解自动代理
public class AspectJAnnotatedPointcutDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AspectJAnnotatedPointcutDemo.class,
                AspectConfiguration.class);
        context.refresh();

        AspectJAnnotatedPointcutDemo aspectJAnnotationDemo = context.getBean(AspectJAnnotatedPointcutDemo.class);

        aspectJAnnotationDemo.execute();

        context.close();
    }

    public void execute() {
        System.out.println("execute()...");
    }
}
/**
 * Aspect 配置类
 */
@Aspect
public class AspectConfiguration {

    @Pointcut("execution(public * *(..))") // 匹配 Join Point
    private void anyPublicMethod() { // 方法名即 Pointcut 名
        System.out.println("@Pointcut at any public method."); // 方法通常设置为空的,不会有具体的动作
    }

    @Before("anyPublicMethod()")          // Join Point 拦截动作
    public void beforeAnyPublicMethod() {
        System.out.println("@Before any public method.");
    }

}

Pointcut定义在切面类的私有方法上,该方法没有任何的动作,只是筛选要切入的方法。该方法名即为Pointcut名。

(2)XML 配置 Pointcut

<!--定义配置类-->
<bean id="aspectXmlConfig" class="com.demo.AspectXmlConfig"/>

<aop:config>
    <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig"><!--引用配置类-->
        <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/><!--Pointcut-->
        <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/><!--配置类的方法-->
    </aop:aspect>
</aop:config>
public class AspectXmlConfig {

    public void beforeAnyPublicMethod() {
        System.out.println("@Before any public method.");
    }
}

(3)API 实现 Pointcut

核心 API - org.springframework.aop.Pointcut • org.springframework.aop.ClassFilter • org.springframework.aop.MethodMatcher

适配实现 - DefaultPointcutAdvisor

public static void main(String[] args) {
	EchoServicePointcut echoServicePointcut = new EchoServicePointcut("echo", EchoService.class);
	
	// 将 Pointcut 适配成 Advisor
	DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(echoServicePointcut, new EchoServiceMethodInterceptor());
	
	DefaultEchoService defaultEchoService = new DefaultEchoService();
	ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);
	// 添加 Advisor
	proxyFactory.addAdvisor(advisor);
	
	// 获取代理对象
	EchoService echoService = (EchoService) proxyFactory.getProxy();
	System.out.println(echoService.echo("Hello,World"));
}
public class EchoServicePointcut extends StaticMethodMatcherPointcut {

    private String methodName;

    private Class targetClass;

    public EchoServicePointcut(String methodName, Class targetClass) {
        this.methodName = methodName;
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return Objects.equals(methodName, method.getName())
                && this.targetClass.isAssignableFrom(targetClass);
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
    }
}

public class EchoServiceMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        System.out.println("拦截 EchoService 的方法:" + method);
        return invocation.proceed();
    }
}

4、拦截动作

Around环绕动作。

Before前置动作。

After后置动作: • 方法返回后:AfterReturning • 异常发生后:AfterThrowing • finally 执行:After

(1)注解方式实现

@Aspect
@Order
public class AspectConfiguration {

    @Pointcut("execution(public * *(..))") // 匹配 Join Point
    private void anyPublicMethod() { // 方法名即 Pointcut 名
        System.out.println("@Pointcut at any public method.");
    }

    @Around("anyPublicMethod()")         // Join Point 拦截动作
    public Object aroundAnyPublicMethod(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("@Around any public method.");
        return pjp.proceed();
    }

    @Before("anyPublicMethod()")          // Join Point 拦截动作
    public void beforeAnyPublicMethod() {
        Random random = new Random();

        if (random.nextBoolean()) {
            throw new RuntimeException("For Purpose.");
        }
        System.out.println("@Before any public method.");
    }


    @After("anyPublicMethod()")
    public void finalizeAnyPublicMethod() {
        System.out.println("@After any public method.");
    }

    @AfterReturning("anyPublicMethod()")
    // AspectJAfterReturningAdvice is AfterReturningAdvice
    // 一个 AfterReturningAdviceInterceptor 关联一个 AfterReturningAdvice
    // Spring 封装 AfterReturningAdvice -> AfterReturningAdviceInterceptor
    // AfterReturningAdviceInterceptor is MethodInterceptor
    // AfterReturningAdviceInterceptor
    //  -> AspectJAfterReturningAdvice
    //      -> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs
    public void afterAnyPublicMethod() {
        System.out.println("@AfterReturning any public method.");
    }

    @AfterThrowing("anyPublicMethod()")
    public void afterThrowingAnyPublicMethod() {
        System.out.println("@AfterThrowing any public method");
    }


    public String toString() {
        return "AspectConfiguration";
    }

    private int getValue() {
        return 0;
    }
}

(2)XML配置方式实现

<aop:config>
    <!--        <aop:pointcut id="allPointcut" expression="execution(* * *(..))"/>-->
    <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig">
        <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/>
        <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        <aop:around method="aroundAnyPublicMethod" pointcut="execution(public * *(..))"/>
        <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/>
        <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
    </aop:aspect>
</aop:config>
public class AspectXmlConfig {

    public Object aroundAnyPublicMethod(ProceedingJoinPoint pjp) throws Throwable {
        Random random = new Random();
        if (random.nextBoolean()) {
            throw new RuntimeException("For Purpose from XML configuration.");
        }
        System.out.println("@Around any public method : " + pjp.getSignature());
        return pjp.proceed();
    }

    public void beforeAnyPublicMethod() {
        System.out.println("@Before any public method.");
    }

    public void finalizeAnyPublicMethod() {
        System.out.println("@After any public method.");
    }

    public void afterAnyPublicMethod() {
        System.out.println("@AfterReturning any public method.");
    }

    public void afterThrowingAnyPublicMethod() {
        System.out.println("@AfterThrowing any public method.");
    }
}

(3)API方式实现

为什么 Spring AOP 不需要设计 Around Advice • AspectJ @Around 与 org.aspectj.lang.ProceedingJoinPoint 配合执行被代理方法 • ProceedingJoinPoint#proceed() 方法类似于 Java Method#invoke(Object,Object...) • Spring AOP 底层 API ProxyFactory 可通过 addAdvice 方法与 Advice 实现关联 • 接口 Advice 是 Interceptor 的父亲接口,而接口 MethodInterceptor 又扩展了 Interceptor • MethodInterceptor 的invoke 方法参数 MethodInvocation 与 ProceedingJoinPoint 类似

API 实现 Before Advice

核心接口 - org.springframework.aop.BeforeAdvice

类型:标记接口,与 org.aopalliance.aop.Advice 类似

方法 JoinPoint 扩展 - org.springframework.aop.MethodBeforeAdvice

接受对象 - org.springframework.aop.framework.AdvisedSupport • 基础实现类 - org.springframework.aop.framework.ProxyCreatorSupport • 常见实现类:org.springframework.aop.framework.ProxyFactory;org.springframework.aop.framework.ProxyFactoryBean;org.springframework.aop.aspectj.annotation.AspectJProxyFactory

API 实现三种 After Advice

核心接口 - org.springframework.aop.AfterAdvice

类型:标记接口,与 org.aopalliance.aop.Advice 类似

扩展 • org.springframework.aop.AfterReturningAdvice • org.springframework.aop.ThrowsAdvice

接受对象 - org.springframework.aop.framework.AdvisedSupport • 基础实现类 - org.springframework.aop.framework.ProxyCreatorSupport • 常见实现类:org.springframework.aop.framework.ProxyFactory;org.springframework.aop.framework.ProxyFactoryBean;org.springframework.aop.aspectj.annotation.AspectJProxyFactory

// 通过创建一个 HashMap 缓存,作为被代理对象
Map<String, Object> cache = new HashMap<>();
// 创建 Proxy 工厂(AspectJ)
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(cache);
// 增加 Aspect 配置类,此处不需要
// proxyFactory.addAspect(AspectConfiguration.class);
// 设置暴露代理对象到 AopContext
proxyFactory.setExposeProxy(true);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
   @Override
   public void before(Method method, Object[] args, Object target) throws Throwable {
       if ("put".equals(method.getName()) && args.length == 2) {
           Object proxy = AopContext.currentProxy();
           System.out.printf("[MethodBeforeAdvice] 当前存放是 Key: %s , Value : %s ," +
                   "代理对象:%s\n", args[0], args[1], proxy);
       }
   }
});

// 添加 AfterReturningAdvice
proxyFactory.addAdvice(new AfterReturningAdvice() {

   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
           throws Throwable {
       if ("put".equals(method.getName()) && args.length == 2) {
           System.out.printf("[AfterReturningAdvice] 当前存放是 Key: %s , 新存放的 Value : %s , 之前关联的 Value : %s\n ",
                   args[0],    // key
                   args[1],    // new value
                   returnValue // old value
           );
       }
   }
});

// 通过代理对象存储数据
Map<String, Object> proxy = proxyFactory.getProxy();
proxy.put("1", "A");
proxy.put("1", "B");
System.out.println(cache.get("1"));

(4)同一个@Before执行顺序

注解驱动:使用@Order注解标注切面类,同一类中的顺序无法保证。

XML驱动:按照先后顺序执行。

5、补充:Spring的ProxyFactory集成了jdk与cglib

import org.springframework.aop.framework.ProxyFactory;

public class Test {


    public static void main(String[] args) {

        UserService userService = new UserService();
        // spring 将cglib和jdk动态代理合二为一了,如果有接口,就会走jdk代理,如果只有类,就会走cglib代理
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(userService);
        // 可以设置多个Advice,会形成代理链
        proxyFactory.addAdvice(new MyBeforeAdvice());
        proxyFactory.addAdvice(new MyAroundAdvice());
        proxyFactory.addAdvice(new MyAfterAdvice());

        UserService proxy = (UserService) proxyFactory.getProxy();

        proxy.test();
    }
}

public class UserService {


    public String test() {
        System.out.println("test");
        return "success";
    }
}

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法执行前执行");
    }
}


import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class MyAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("返回之后执行");
    }
}


import org.springframework.aop.ThrowsAdvice;

public class MyThrowAdvice implements ThrowsAdvice {

    /**
     * 可以写这些方法:拦截指定的异常
     *
     *     public void afterThrowing(Exception ex)
     *     public void afterThrowing(RemoteException)
     *     public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
     *     public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
     */
}

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class MyAroundAdvice implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
        System.out.println("around 前");


        // 执行目标方法,或者执行下一个代理链(如果有的话)
        Object proceed = invocation.proceed();
        System.out.println("around 后");



        return proceed;
    }
}

6、补充:Advisor的理解

Advisor = pointcut + advice。

跟Advice类似的还有一个Advisor的概念,一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子,这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法,比如:

  UserService target = new UserService();

  ProxyFactory proxyFactory = new ProxyFactory();
  proxyFactory.setTarget(target);
  proxyFactory.addAdvisor(new PointcutAdvisor() {
   @Override
   public Pointcut getPointcut() {
    return new StaticMethodMatcherPointcut() {
     @Override
     public boolean matches(Method method, Class<?> targetClass) {
      return method.getName().equals("testAbc");
     }
    };
   }

   @Override
   public Advice getAdvice() {
    return new MethodInterceptor() {
     @Override
     public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("before...");
      Object result = invocation.proceed();
      System.out.println("after...");
      return result;
     }
    };
   }

   @Override
   public boolean isPerInstance() {
    return false;
   }
  });

  UserInterface userService = (UserInterface) proxyFactory.getProxy();
  userService.test();

上面代码表示,产生的代理对象,只有在执行testAbc这个方法时才会被增强,会执行额外的逻辑,而在执行其他方法时是不会增强的。

7、补充:ProxyFactoryBean

上面介绍了Spring中所提供了ProxyFactory、Advisor、Advice、PointCut等技术来实现代理对象的创建,但是我们在使用Spring时,我们并不会直接这么去使用ProxyFactory,比如说,我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象,而这些,Spring都是支持的,只不过,作为开发者的我们肯定得告诉Spring,那些类需要被代理,代理逻辑是什么。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    @Bean
    public ProxyFactoryBean userServiceProxy(){
        UserService userService = new UserService();

        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        });
        return proxyFactoryBean;
    }
}

通过这种方法来定义一个UserService的Bean,并且是经过了AOP的。但是这种方式只能针对某一个Bean。它是一个FactoryBean,所以利用的就是FactoryBean技术,间接的将UserService的代理对象作为了Bean。

ProxyFactoryBean还有额外的功能,比如可以把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置

@Bean
public MethodInterceptor aroundAdvise(){
 return new MethodInterceptor() {
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
   System.out.println("before...");
   Object result = invocation.proceed();
   System.out.println("after...");
   return result;
  }
 };
}

@Bean
public ProxyFactoryBean userService(){
 UserService userService = new UserService();
 
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
 proxyFactoryBean.setTarget(userService);
 proxyFactoryBean.setInterceptorNames("aroundAdvise");
 return proxyFactoryBean;
}

8、补充:BeanNameAutoProxyCreator

ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理。

@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
 BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
 beanNameAutoProxyCreator.setBeanNames("userSe*"); // beanName的前缀
 beanNameAutoProxyCreator.setInterceptorNames("aroundAdvise"); // 指定advise的beanName
 beanNameAutoProxyCreator.setProxyTargetClass(true);
 return beanNameAutoProxyCreator;
}

通过BeanNameAutoProxyCreator可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advise,前提条件是这个Advise也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理的Bean。

BeanNameAutoProxyCreator 是一个BeanPostProcessor,在对应的方法中进行处理的。

9、补充:DefaultAdvisorAutoProxyCreator

@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
 NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
 pointcut.addMethodName("test");

 DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
 defaultPointcutAdvisor.setPointcut(pointcut);
 defaultPointcutAdvisor.setAdvice(new MyReturningAdvise());
 return defaultPointcutAdvisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
 
 DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
 return defaultAdvisorAutoProxyCreator;
}

DefaultAdvisorAutoProxyCreator 是一个BeanPostProcessor,在初始化后的方法中,会加载所有的DefaultPointcutAdvisor 类型的Bean,会匹配指定的Pointcut,来判断是否需要进行代理。

但是,我们发现,通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或者Pointcut,那么这个步骤能不能更加简化一点呢? ​

对的,通过注解!

比如我们能不能只定义一个类,然后通过在类中的方法上通过某些注解,来定义PointCut以及Advice,可以的,比如:

@Aspect
@Component
public class MyAspect {

 @Before("execution(public void com.service.UserService.test())")
 public void zhouyuBefore(JoinPoint joinPoint) {
  System.out.println("myBefore");
 }

}

通过上面这个类,我们就直接定义好了所要代理的方法(通过一个表达式),以及代理逻辑(被@Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象,具体怎么解析这些注解就是**@EnableAspectJAutoProxy注解**所要做的事情了,后面详细分析。

而@EnableAspectJAutoProxy注解,就是加了一个AnnotationAwareAspectJAutoProxyCreator。

10、补充:Introduction(@DeclareParents)

AOP的Introduction功能,是一个小的功能但是实际用的很少,因为代码可读性实在是太差了。

它旨在给原对象添加一个接口和一个默认的实现类,这样原对象就相当于直接添加了方法。

举个例子就明白了:

public class UserService {

    public String test() {
        System.out.println("test");
        return "success";
    }
}

public interface UserInterface {

    void newMethod();
}

public class UserDefaultService implements UserInterface{
    @Override
    public void newMethod() {
        System.out.println("1111");
    }
}

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@Aspect
public class Test {

    // 手动给UserService加上UserInterface接口,默认实现类为UserDefaultService
    @DeclareParents(value = "com.test.UserService", defaultImpl = UserDefaultService.class)
    private UserInterface userInterface;

    @Bean
    public UserService userService() {
        return new UserService();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
        // 获取userService 的bean,但是由于我们给UserService手动添加了一个接口,所以可以强转为UserInterface
        UserInterface userService = (UserInterface) context.getBean("userService");
        // 最终执行的是UserDefaultService的newMethod方法
        userService.newMethod();
    }
}

我们发现,就像是给UserService手动添加了一个方法一样。

未完待续

参考资料

极客时间《小马哥讲 Spring AOP 编程思想》