动态代理与Spring AOP

Posted ylyzty

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理与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动态代理

Spring AOP

  面向切面编程,在我们的应用中,经常需要做一些事情,但是这些事情与核心业务无关,比如,要记录所有 update 方法的执行时间时间,操作人等等信息,记录到日志, 通过 Spring 的 AOP 技术,就可以在不修改 update 的代码的情况下完成该需求。

  pring AOP 中的动态代理主要有两种方式,JDK 动态代理 和 CGLIB 动态代理。JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类。

  如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

以上是关于动态代理与Spring AOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring框架第五篇之Spring与AOP

jdk动态代理与cglib代理spring aop代理实现原理解析

spring AOP 代理(静态与动态)

Spring AOP 实现原理与 CGLIB 应用

Spring笔记4-AOP

Spring AOP实现原理与CGLIB应用(转)