spring-aop 两种代理方式 JDK动态代理和CGLIB动态代理

spring-aop 两种代理方式 JDK动态代理和CGLIB动态代理

Spring的两大特性是IOC和AOP:
Spring的核心特性就是IOC和AOP,IOC(Inversion of Control),即“控制反转”;AOP(Aspect-OrientedProgramming),即“面向切面编程”。下面分享我个人对AOP特性的理解。
AOP的实际应用:
面向切面编程,通常被定义为系统关注点的一种分离技术。系统是许多不同组件组成,组件的作用除了要实现自身的业务逻辑,通常都会被要求添加日志,事务管理,和缓存或安全等问题。这些系统服务进程被称为横切关注点,在系统中多个组件都会相继使用。

AOP实现的关键在于AOP框架自动创建AOP代理,AOP代理主要分为静态代理(AspectJ)和动态代理(StringAop)
AspectJ:会被称为编译时增强,AspectJ是静态代理的增强,采用编译时生成AOP代理类,会具有更好的性能,就是需要使用特定的编译器去处理,使用不太方便。

Spring-Aop:是以动态代理的方式,运行时生成AOP代理类,不会改变字节码,在内存中生成一个方法的AOP临时对象。在运行过程中需要每次生成AOP代理,性能会相对受到影响。
Spring-Aop动态代理实现有两种方式,jdk动态代理和CGIB动态代理。

JDK动态代理:是通过反射接收被代理的类。
一般主要涉及到以下两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法
(2)Proxy:该类即为动态代理类,(详情看实例代码注解)

下面就来演示下jdk代理的实现原理。
1、jdk动态实现aop拦截
自定义要实现的接口MyInv,需要实现的方法add();

/**
 * Created by zzm on 2020/04/19
 * desc: jdk动态aop代理需要实现的接口
 */
public interface MyInv {
    public void add();
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

用要代理的目标类MyInvImpl实现上面我们定义的接口,在目标类的add方法的前后实现拦截,加入自定义切面逻辑。这就是aop的魅力所在:代码与代码之间没有耦合。

/**
 * Created by zzm on 2020/04/19
 * desc: 被代理的类,即目标类target
 */
public class MyInvImpl implements MyInv {
    @Override
    public void add() {
        System.out.println("目标类的add方法");
        System.out.println("时间和机会才是最后的本钱!");
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

用MyInvoQuote类去实现InvocationHandler接口,并且实现接口中的invoke方法。在该方法中加入切面逻辑,实现自己的目标接口是在method.invoke里完成。


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

/**

  • Created by zzm on 2020/04/19

  • desc:这里加入切面实现InvocationHandler 接口的类
    */
    public class MyInvoQuote implements InvocationHandler {

    private Object target;

    public MyInvoQuote(Object target) { // 构造函数
    this.target = target;
    }
    /**

    • 这个抽象方法在代理类中动态实现。
    • @param proxy 第一个参数obj一般是指代理类
    • @param method method是被代理的方法
    • @param args args为该方法的参数数组
    • @return
    • @throws Throwable
      */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println(“before—》切面执行逻辑”);
      Object invoke = method.invoke(target, args);//通过反射执行,目标类的方法
      System.out.println(“after—》切面执行逻辑”);
      return invoke;
      }
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

最后写个测试方法。实现aop方式之一的:jdk动态代理


public class test {
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    MyInvImpl myInvImpl <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyInvImpl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 实现需要代理的类</span>
    MyInvoQuote handler <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyInvoQuote</span><span class="token punctuation">(</span>myInvImpl<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">//构造实现InvocationHandler接口的实现类</span>
    <span class="token comment">// Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例</span>
    <span class="token comment">//这里的proxyInstance就是我们目标类的增强代理类</span>
    MyInv proxyInstance <span class="token operator">=</span> <span class="token punctuation">(</span>MyInv<span class="token punctuation">)</span> Proxy<span class="token punctuation">.</span><span class="token function">newProxyInstance</span><span class="token punctuation">(</span>myInvImpl<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// getClassLoader() 取得该Class对象的类装载器</span>
            myInvImpl<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">getInterfaces</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//getInterfaces()获取该class对象下的方法可通过索引找到对应方法</span>
                                                <span class="token comment">// 列: myInvImpl.getClass().getInterfaces()[0] 获得myInvImpl对象所实现的第一个接口</span>
    <span class="token comment">//Proxy.newProxyInstance()返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)</span>
    proxyInstance<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//打印增强过的类类型</span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"打印增强过的类类型:"</span> <span class="token operator">+</span> proxyInstance<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

结果在这里插入图片描述
在实现过程中证实了jdk动态代理的核心是InvocationHandler接口和Proxy类
注意:jdk动态代理的应用前提,反射机制在生成类的过程中比较高效,不过必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性

cglib动态代理:底层则是借助asm来实现的
下面演示下cglibProxy动态代理
目标类,cglib不需要定义目标类的统一接口


/**
 * Created by zzm on 2020/04/19
 * desc: 被代理的类,即目标类target
 */
public class MyInvImpl  {
    public void myBase() {
        System.out.println("目标类的myBase方法");
        System.out.println("时间和机会才是最后的本钱!");
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在myBase方法前后加入了自定义的切面逻辑,目标类myBase方法执行语句为proxy.invokeSuper(object, args);


import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**

  • Created by zzm on 2020/04/19

  • desc:这里加入切面实现MethodInterceptor 接口的类
    */
    public class MyInvoQuote implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println(“before添加切面,方法执行前”);
    methodProxy.invokeSuper(o, objects);
    System.out.println(“after添加切面,方法执行后”);
    return null;
    }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

测试类:


public class test {
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    MyInvoQuote proxy <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyInvoQuote</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//构造实现 MethodInterceptor接口的类</span>
    Enhancer enhancer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Enhancer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//设置代理目标</span>
    enhancer<span class="token punctuation">.</span><span class="token function">setSuperclass</span><span class="token punctuation">(</span>MyInvImpl<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//回调方法的参数为代理类对象CglibProxy,</span>
    <span class="token comment">// 最后增强目标类调用的是代理类对象CglibProxy中的intercept方法</span>
    enhancer<span class="token punctuation">.</span><span class="token function">setCallback</span><span class="token punctuation">(</span>proxy<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//此刻,base不是单车的目标类,而是增强过的目标类</span>
    MyInvImpl base <span class="token operator">=</span> <span class="token punctuation">(</span>MyInvImpl<span class="token punctuation">)</span> enhancer<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    base<span class="token punctuation">.</span><span class="token function">myBase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Class<span class="token operator">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">MyInvImpl</span><span class="token operator">&gt;</span> baseClass <span class="token operator">=</span> base<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">//java 泛型 上界通配符(Upper Bounds Wildcards)</span>
    <span class="token comment">//查看增强过的类的父类是不是未增强的Base类</span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"增强过的类的父类:"</span><span class="token operator">+</span>baseClass<span class="token punctuation">.</span><span class="token function">getSuperclass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"============打印增强过的类的所有方法=============="</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    FanSheUtils<span class="token punctuation">.</span><span class="token function">printMethods</span><span class="token punctuation">(</span>baseClass<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">//自定义获取类下面方法的工具类</span>

// 没有被增强过的base类
MyInvImpl base2 = new MyInvImpl();
System.out.println(“未增强过的类的父类:”+base2.getClass().getSuperclass().getName());
System.out.println("=打印增未强过的目标类的方法===");
FanSheUtils.printMethods(base2.getClass());//打印没有增强过的类的所有方法
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

查看结果

before添加切面,方法执行前
目标类的myBase方法
时间和机会才是最后的本钱!
after添加切面,方法执行后
增强过的类的父类:com.billiards.service.impl.MyInvImpl
============打印增强过的类的所有方法==============
  public final equals(java.lang.Object);
  public final toString();
  public final hashCode();
 CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;);
  public static CGLIB$SET_STATIC_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;);
  public getCallback(int);
  public getCallbacks();
  public static CGLIB$findMethodProxy(org.springframework.cglib.core.Signature);
  final CGLIB$equals$1(java.lang.Object);
  final CGLIB$myBase$0();
  final CGLIB$hashCode$3();
  final CGLIB$clone$4();
  final CGLIB$toString$2();
  static CGLIB$STATICHOOK1();
  private static final CGLIB$BIND_CALLBACKS(java.lang.Object);
未增强过的类的父类:java.lang.Object
=============打印增未强过的目标类的方法===============
  public myBase();

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

自此,cglib动态代理实现的AOP拦截机制已经基本实现
asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

spring aop的两个核心方式已经简单实现。后续实现Spring-boot中使用aop切面,使用注解的形式更加简化了代码量,通过注解是使用springaop两种形式中的(AspectJ)方式实现的。掌握原理在使用注解的形式去做,才不会一头雾水。

                                </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值