某些时候我们需要无感的对一些方法或类进行操作,可以使用@Aspect注解即可以找到已知目标位置并实现切面拦截。本文将介绍一种在Spring体系下,未知目标(编写代码时)动态代理的方式,该方式底层基于cglib实现。

在Spring生命周期中的Bean初始化阶段找到代理目标类

在SpringBoot3x框架体系下,可以通过实现接口org.springframework.beans.factory.config.BeanPostProcessor的方法Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException来切入操作Bean初始化阶段。

代码片段:

public abstract class Example implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
		//TODO: 代理操作
        return bean;
    }
}

创建代理类工厂并配置代理相关参数

接入Bean初始化阶段后,我们可以通过判断该Bean是否实现某个接口或继承某个类等等特性来判断该Bean是否我们的代理目标类。如果不是,我们返回原始Bean,如果是代理目标类,我们需要创建一个org.springframework.aop.framework.ProxyFactory来代理原始Bean,并对ProxyFactory进行一些列代理相关配置,并通过ProxyFactory.getProxy()方法获取代理类(被代理覆盖后的新Bean)。其中proxyFactory.setProxyTargetClass(Boolean.TRUE)是关键代码,这句代码决定了当前代理是以方法代理创建还是以类代理创建,以类代理创建默认使用cglib实现。

代码片段:

//创建代理
ProxyFactory proxyFactory = new ProxyFactory(bean);
//启用cglib代理
proxyFactory.setProxyTargetClass(Boolean.TRUE);
String proxyMethodName = "suchSave";
//配置切面事件
proxyFactory.addAdvisor(
        new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName(proxyMethodName), (MethodInterceptor) invocation -> {
            //TODO: 其他操作
            System.out.println("触发方法前操作");
			//执行方法,并获取返回值
            Object ret = invocation.proceed();
            System.out.println("触发方法后操作");
            return ret;
        }));
//获取代理对象
proxyFactory.getProxy();

完整代码

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.NonNull;

import java.util.Arrays;

/**
 * @author tomsean
 */
public abstract class Example implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        boolean isProxy = false;
        Class<?>[] interList = bean.getClass().getInterfaces();
        if (interList.length == 0) {
            return bean;
        }
        String targetTypeName = "xxx"; //目标接口名
        isProxy = Arrays.stream(interList).anyMatch(i -> i.getTypeName().equals(targetTypeName));
        if (!isProxy) {
            //非目标bean,返回原始bean
            return bean;
        }
        //创建代理
        ProxyFactory proxyFactory = new ProxyFactory(bean);
        //启用cglib代理
        proxyFactory.setProxyTargetClass(Boolean.TRUE);
        String proxyMethodName = "suchSave";
        //配置切面事件
        proxyFactory.addAdvisor(
                new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName(proxyMethodName), (MethodInterceptor) invocation -> {
                    //TODO: 其他操作
                    System.out.println("触发方法前操作");
                    Object ret = invocation.proceed();
                    System.out.println("触发方法后操作");
                    return ret;
                }));
		//将代理后的新bean返回
        return proxyFactory.getProxy();
    }
}