最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

天天最资讯丨动态代理与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: sendsend message:messageAfter: sendexecute 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:messageBefore: printexecute print method!After: print
  • send()方法未被代理
  • print()被代理增强

小结

代理类型实现机制创建方式应用场景
JDK动态代理代理类和目标类都实现了同样的接口,代理类委托 InvocationHandler去调用目标类的原始方法反射目标类实现了接口
Cglib动态代理代理类继承并重写了目标类的方法,通过回调函数 MethodInterceptor调用父类方法执行原始方法ASMfinal类、非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);}@Componentpublic class SmsServiceImpl implements SmsService{    @Override    public String send(String message) {        // TODO Auto-generated method stub        System.out.println("send message:" + message);        return message;    }}

定义切面类

@Aspect@Componentpublic 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动态代理

关键词: