chapter11_实现AOP切面

框架都是基于JDK的,看源码一直追溯到JDK才算是合格

一、前置知识

  • 代理模式(Proxy Pattern):是23种设计模式中的一种
  • 编程思想:不能随便修改源码,如果需要修改源码,通过代理的方式来拓展

1.1 静态代理

静态代理是基于接口,需要自己手写代理类

代理对象,引用真实对象

缺点:每次修改接口,需要改动代理对象,不灵活

@Slf4j
public class StaticProxyStudent implements IStudent {
    
    private IStudent target;

    public StaticProxyStudent(IStudent target) {
        this.target = target;
    }

    @Override
    public void insert() {
        log.info("前置增强逻辑");
        target.insert();
        log.info("后置增强逻辑");
    }
}

1.2 JDK动态代理

自己写一个方法拦截器Handler,实现增强逻辑

@Slf4j
public class TransactionHandler implements InvocationHandler {

    private Object target;

    public TransactionHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;

        //判断方法是否要增强的逻辑
        if ("insert".equals(method.getName())) {
            log.info("前置增强逻辑");
            result = method.invoke(target, args);
            log.info("后置增强逻辑");
        } else {
            result = method.invoke(target, args);
        }

        return result;
    }
}

使用Proxy,生成代理对象

@Test
public void JDKProxyTest() {
    //真实对象
    IStudentService target = new StudentServiceImpl();
    //方法拦截处理器
    TransactionHandler handler = new TransactionHandler(target);
    //代理对象
    IStudentService jdkProxy = (IStudentService) Proxy.newProxyInstance(
            ProxyTest.class.getClassLoader(),
            new Class[]{IStudentService.class},
            handler);
    jdkProxy.insert();
}

1.3 CGLIB动态代理

如果类没有实现接口,就只能用CGLIB动态代理

@Test
public void CglibProxyTest() {
    TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(StudentServiceImpl.class);
    enhancer.setCallback(transactionInterceptor);
    IStudentService cglibProxy = (IStudentService) enhancer.create();
    cglibProxy.insert();
}

方法拦截器

@Slf4j
public class TransactionInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        log.info("前置增强");
        //o是代理对象,o的super就是真实对象
        //spring中的cglib,会用target指向真实对象,代理对象的属性都为null
        Object result = methodProxy.invokeSuper(o, objects);
        log.info("后置增强");
        return result;
    }
}

1.4 Spring中的代理

  • Spring中,无论JDK还是CGLIB,统一使用target指向真实对象。因为依赖注入过于复杂,统一在真实对象中处理,代理对象中的属性为null
  • 类和方法都不能加final

二、入门案例

与最简单的JDK代理相比,做了两个扩展

  • 方法匹配器,扩展点一,底层使用了AspectJ
  • 反射调用,扩展点二,使用了AopAlliance做统一包装
@Test
public void test_proxy_jdk_alliance() {
    // 目标对象(可以替换成任何的目标对象)
    Object targetObj = new UserService();
    // AOP 代理
    IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() {
        // 方法匹配器,扩展点一,底层使用了AspectJ
        MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.shopifymall.springframework.test.bean.IUserService.*(..))");
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (methodMatcher.matches(method, targetObj.getClass())) {
                // 方法拦截器
                MethodInterceptor methodInterceptor = invocation -> {
                    long start = System.currentTimeMillis();
                    try {
                        // 最终还是要直接调用原始方法
                        return invocation.proceed();
                    } finally {
		                    // AOP逻辑,计算方法执行时间
                        System.out.println("监控 - Begin By AOP");
                        System.out.println("方法名称:" + invocation.getMethod().getName());
                        System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
                        System.out.println("监控 - End\r\n");
                    }
                };
                // 反射调用,扩展点二,使用了AopAlliance做统一包装
                return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
            }
            // 方法未匹配切点,直接调用
            return method.invoke(targetObj, args);
        }
    });
    String result = proxy.queryUserInfo();
    System.out.println("测试结果:" + result);
}

执行结果,打印出了方法的执行耗时

模拟调用dao,查询user信息
监控 - Begin By AOP
方法名称:queryUserInfo
方法耗时:1ms
监控 - End

测试结果:查询完毕

三、定义要素

3.1 AspectJ包装

类匹配接口

  • 通知是否应适用于给定的目标类别
public interface ClassFilter {

    /**
     * Should the pointcut apply to the given interface or target class?
     * @param clazz the candidate target class
     * @return whether the advice should apply to the given target class
     */
    boolean matches(Class<?> clazz);

}

方法匹配接口

  • 通知是否应适用于给定的方法
public interface MethodMatcher {

    /**
     * Perform static checking whether the given method matches. If this
     * @return whether this method matches statically
     */
    boolean matches(Method method, Class<?> targetClass);

}

切点接口

  • 返回类匹配器和方法匹配器
public interface Pointcut {

    /**
     * Return the ClassFilter for this pointcut.
     * @return the ClassFilter (never <code>null</code>)
     */
    ClassFilter getClassFilter();

    /**
     * Return the MethodMatcher for this pointcut.
     * @return the MethodMatcher (never <code>null</code>)
     */
    MethodMatcher getMethodMatcher();

}

切点表达式最终实现

  • 借助AspectJ实现,完成了扩展点一
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher {

    /**
     * 底层用来描述切入点原语
     */
    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();

    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
    }

    /**
     * 保存解析后的切点表达式
     */
    private final PointcutExpression pointcutExpression;

    /**
     * 构造方法,解析传入的切点表达式
     * @param expression
     */
    public AspectJExpressionPointcut(String expression) {
        PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
        pointcutExpression = pointcutParser.parsePointcutExpression(expression);
    }

    /**
     * 类型匹配
     * @param clazz the candidate target class
     * @return
     */
    @Override
    public boolean matches(Class<?> clazz) {
        return pointcutExpression.couldMatchJoinPointsInType(clazz);
    }

    /**
     * 方法匹配
     * @param method
     * @param targetClass
     * @return
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
    }

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

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

}

3.2 Aop_Alliance包装

打包AOP相关的所有信息

  • 目标对象,被代理的真实对象
  • 方法拦截器,使用aop_alliance提供的接口做统一包装,需要用户自己实现(扩展二)
  • 方法匹配器,使用了AspectJ最终实现
@Data
public class AdvisedSupport {
    // 目标对象
    private TargetSource targetSource;

    // 增强逻辑
    private MethodInterceptor methodInterceptor;

    // 方法匹配器
    private MethodMatcher methodMatcher;
}

目标对象类

public class TargetSource {

    private final Object target;

    public TargetSource(Object target) {
        this.target = target;
    }

    public Class<?>[] getTargetClass(){
        return this.target.getClass().getInterfaces();
    }

    public Object getTarget(){
        return this.target;
    }
}

反射方法调用类

  • 实现MethodInvocation接口,由aop_alliance提供的接口做统一包装
  • 最终会调用proceed,调用真实对象的方法
public class ReflectiveMethodInvocation implements MethodInvocation {

    // 目标对象
    protected final Object target;
    // 方法
    protected final Method method;
    // 入参
    protected final Object[] arguments;

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

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

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

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

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

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

}

3.3 代理实现

代理接口

  • 定义一个标准接口,用于获取代理类。因为具体实现代理的方式可以有 JDK 方式,也可以是 Cglib 方式
/**
 * Aop接口,JDK CGLIB两种实现
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/1/5 7:20
 * @Version 1.0
 */
public interface AopProxy {
    Object getProxy();
}

JDK实现类

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    /**
     * 切面
     */
    private final AdvisedSupport advised;

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

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //看看切点是否匹配
        if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
            MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
            //MethodInterceptor和MethodInvocation是AopAlliance包下的
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
        }
        //不匹配,直接用原始对象执行方法
        return method.invoke(advised.getTargetSource().getTarget(), args);
    }
}

CGLIB实现类

public class Cglib2AopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public Cglib2AopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
            if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
                //AopAlliance的包装类
                return advised.getMethodInterceptor().invoke(methodInvocation);
            }
            return methodInvocation.proceed();
        }
    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

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

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

    }

}

JDK与CGLIB的相似点

  • 调用代理方法,都是使用aop_alliance提供的MethodInterceptor.invoke(MethodInvocation invocation)调用

四、测试

新建Eat接口

public interface Eat {
    String eatFish(String fishName);
}

改造Cat类,实现Eat接口

@Slf4j
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Cat implements Eat{
    private String name;
    private int weight;

    @Override
    public String eatFish(String fishName) {
        log.info("猫正在吃鱼");
        return "吃了一条" + fishName;
    }
}

新建方法拦截器,提供代理逻辑

public class CatInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            System.out.println("监控 - Begin By AOP");
            System.out.println("方法名称:" + invocation.getMethod());
            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
            System.out.println("监控 - End\r\n");
        }
    }
}

测试结果

  • 可以看到方法耗时
02:10:31.331 [main] INFO cn.shopifymall.springframework.test.bean.Cat - 猫正在吃鱼
监控 - Begin By AOP
方法名称:public abstract java.lang.String cn.shopifymall.springframework.test.bean.Eat.eatFish(java.lang.String)
方法耗时:4ms
监控 - End

02:10:31.334 [main] INFO cn.shopifymall.springframework.test.ApiTest - 测试结果:吃了一条大马哈鱼
02:10:31.391 [main] INFO cn.shopifymall.springframework.test.bean.Cat - 猫正在吃鱼
监控 - Begin By AOP
方法名称:public java.lang.String cn.shopifymall.springframework.test.bean.Cat.eatFish(java.lang.String)
方法耗时:9ms
监控 - End

02:10:31.392 [main] INFO cn.shopifymall.springframework.test.ApiTest - 测试结果:吃了一条三文鱼
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值