动态代理与Spring AOP

JDK 动态代理

目标类

JDK动态代理基于接口创建代理,所以目标类必须至少实现一个接口。

public interface SmsService {
    String send(String message);
    void print();
}


public class SmsServiceImpl implements SmsService{

    @Override
    public String send(String message) {
        // TODO Auto-generated method stub
        System.out.println("send message:" + message);
        return message;
    }

    @Override
    public void print() {
        // TODO Auto-generated method stub
        System.out.println("execute print method!");
    }
}

代理类

JDK动态代理类必须实现 InvocationHandler 接口,重写自己的 invoke() 方法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKProxy implements InvocationHandler{

    private final Object target;    // 被代理对象

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

    /**
     * 当调用被代理对象的方法时, 会自动跳转到代理对象的invoke()方法
     * 可以通过 method.getName() 指定被代理的方法
     * 默认会代理所有方法
     *
     * @param proxy 动态生成的代理对象
     * @param method 实际调用的方法
     * @param args 实际调用方法的入参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        Object result = null;
        if (method.getName().equals("send")) {
            System.out.println("Before: " + method.getName());
            result = method.invoke(target, args);
            System.out.println("After: " + method.getName());
            
        }
        
        else {
            result = method.invoke(target, args);
        }
        
        return result;
    } 
}

Proxy动态创建代理类

利用反射机制创建实现被代理对象接口的代理类,在调用具体方法时,调用 InvocationHandler 处理。

import java.lang.reflect.Proxy;

public class JDKProxyFactory {
    public static Object getJDKProxy(Object target) {
        // 动态创建代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JDKProxy(target));
    }
}

测试JDK动态代理

public class JDKProxyTest {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) JDKProxyFactory.getJDKProxy(new SmsServiceImpl());
        smsService.send("message");
        smsService.print();
    }
}
Before: send
send message:message
After: send
execute print method!
  • sned() 方法被代理增强
  • print() 方法未被代理

Cglib 动态代理

目标类

Cglib动态代理基于子类创建代理,所以对于未实现接口的类可以考虑使用Cglib实现代理。但是如果目标类被 final 关键字修饰则代理无法创建。

public class SmsServiceClass {
    public String send(String message) {
        // TODO Auto-generated method stub
        System.out.println("send message:" + message);
        return message;
    }

    public void print() {
        // TODO Auto-generated method stub
        System.out.println("execute print method!");
    }
}

代理类

Cglib动态代理类则必须实现 MethodInterceptor 接口,并重写自己的 intercept() 方法。

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

     /**
      * @param obj 动态生成的代理对象
      * @param method 实际调用的方法
      * @param args 实际调用方法的入参
      * @param methodProxy Method代理方法
      * @return
      * @throws Throwable
      */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        // 增强 print 方法
        if (method.getName().equals("print")) {
            System.out.println("Before: " + method.getName());
            // 执行被代理类的逻辑
            result = methodProxy.invokeSuper(obj, args);
            System.out.println("After: " + method.getName());
        }
        else {
            result = methodProxy.invokeSuper(obj, args);
        }

        return  result;
    }
}

Enhancer动态创建代理类

利用 org.objectweb.asm 软件包,加载代理对象的class文件,通过修改字节码生成子类实现代理。

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {
    public static Object getCglibProxy(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibProxy());

        return enhancer.create();
    }
}

setSupperClass()方法源码

public void setSuperclass(Class superclass) {
    if (superclass != null && superclass.isInterface()) {
        this.setInterfaces(new Class[]{superclass});
    } else if (superclass != null && superclass.equals(Object.class)) {
        this.superclass = null;
    } else {
        this.superclass = superclass;
    }

}

所以 Cglib 即可以代理未实现接口的类(supperClass),也可以代理实现了接口的类(setInterfaces)。

测试Cglib动态代理

public class CglibTest {
    public static void main(String[] args) {
        // 获取代理类
        SmsServiceClass smsServiceClass = (SmsServiceClass) CglibProxyFactory.getCglibProxy(new SmsServiceClass());
        smsServiceClass.send("message");
        smsServiceClass.print();
    }
}
send message:message
Before: print
execute print method!
After: print
  • send() 方法未被代理
  • print() 被代理增强

小结

代理类型 实现机制 创建方式 应用场景
JDK动态代理 代理类和目标类都实现了同样的接口,代理类委托 InvocationHandler 去调用目标类的原始方法 反射 目标类实现了接口
Cglib动态代理 代理类继承并重写了目标类的方法,通过回调函数 MethodInterceptor 调用父类方法执行原始方法 ASM final类、非final方法

Spring AOP

AOP(Aspect Orient Programming): 面向切面编程,即可以在不修改原有代码的情况下给系统添加额外的功能。AOP可以拦截指定的业务方法,并对其进行增强,而且无需入侵到具体业务代码中,使得业务代码与增强处理逻辑分离。

AOP主要应用体现在:

  • 事务处理
  • 日志管理
  • 权限控制
  • 异常处理

Spring AOP 简单概念理解

  • Advice: 通知,描述切面何时执行以及如何增强处理
  • join point: 连接点,描述可以被动态代理拦截的目标类方法
  • PointCut: 切点,真正被拦截的连接点
  • Aspect: 切面,即通知和切点的结合
  • Weaving: 织入,描述增强逻辑应用到目标类上,生成代理对象的过程

AspectJ基于注解实现切面

创建接口及其实现类

public interface SmsService {
    String send(String message);
}


@Component
public class SmsServiceImpl implements SmsService{
    @Override
    public String send(String message) {
        // TODO Auto-generated method stub
        System.out.println("send message:" + message);
        return message;
    }
}

定义切面类

@Aspect
@Component
public class SmsServiceAspectJ {
    
    // 声明切点表达式
    @Pointcut("execution(String com.lzy.aopdemo.test.SmsService.send(String))")
    public void point(){}
    
    @Before("point()")
    public void beforeMethod() {
        System.out.println("Execute before!");
    }
    
    @After("point()")
    public void afterMethod() {
        System.out.println("Execute after!");
    }
    
    @AfterReturning("point()")
    public void afterReturningMethod() {
        System.out.println("Execute after returning!");
    }
    
    @Around("point()")
    public void aroundMethod(ProceedingJoinPoint pjp) {
        try {
            System.out.println("Around before");
            pjp.proceed();
            System.out.println("Around after");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

切点表达式 execution(* com.lzy.aopdemo.test.SmsService.send(..))

  • execution: 标识方法执行时触发
  • *: 返回任意类型(不关心返回值类型)
  • com.lzy.aopdemo.test.SmsService: 方法所属类或接口
  • send: 特定方法
  • ..: 使用任意参数(不关心入参)

Spring AOP通知类型

通知类型 描述
@Before 在目标方法调用之前执行
@After 在目标方法调用之后执行
@AfterReturning 在目标方法返回后调用
@AfterThrowing 在目标方法抛出异常后调用
@Around 将目标方法封装起来

注意@Around的执行顺序

@Around \(\rightarrow\) @Before \(\rightarrow\) @Around \(\rightarrow\) @After \(\rightarrow\) @AfterReturning

配置启用AOP

proxyTargetClass

  • true: 使用Cglib动态代理
  • false: 使用JDK动态代理
@Configuration
@ComponentScan(basePackageClasses = {com.lzy.aopdemo.test.SmsService.class})
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AOPConfiguration {}

参考文章

  1. Spring AOP —— Spring中面向切面编程
  2. JDK动态代理和CGLIB动态代理
posted @ 2023-04-13 14:51  ylyzty  阅读(8)  评论(0编辑  收藏  举报