java动态代理与cglib代理的简单使用

  1. AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

  2. 那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。

  3. AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

一、定义接口和实现

计算器接口类

package com.kingboy.core;

/**
 * @Author kingboy
 * @Date 2017/6/14 下午2:11
 * @Description Calculator is used to 计算器接口
 */
public interface Calculator {

    int add(int a, int b);

    int reduce(int a, int b);

}

计算器实现类

package com.kingboy.core;

/**
 * @Author kingboy
 * @Date 2017/6/14 下午2:13
 * @Description CalculatorImpl is used to 计算器的具体实现
 */
public class CalculatorImpl implements Calculator {

    @Override
    public int add(int a, int b) {
        System.out.println("------我是add方法------");
        return a + b;
    }

    @Override
    public int reduce(int a, int b) {
        System.out.println("------我是其它方法------");
        return a - b;
    }

}

二、使用Java动态代理

代理类

package com.kingboy.jdkproxy;

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

/**
 * @Author kingboy
 * @Date 2017/6/14 下午2:37
 * @Description CalculatorAOP is used to 计算器切入
 */
public class JavaProxy implements InvocationHandler{

    private Object target;

    public JavaProxy() {}

    public JavaProxy(Object target) {
        this.target = target;
    }
    /**
    *Object proxy:指被代理的对象。 
    *Method method:要调用的方法 
    *Object[] args:方法调用时所需要的参数 
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("add".equals(method.getName())) {
            System.out.println("add方法执行之前的执行操作!");
            Object result = method.invoke(target, args);
            System.out.println("add方法执行之后的执行操作!");
            return result;
        } else {
            //其它方法继续执行
            System.out.println("我是其它方法的前面!!");
            Object result = method.invoke(target, args);
            System.out.println("我是其它方法的后面!!");
            return result;
        }

    }
}

测试主类

package com.kingboy;


import com.kingboy.jdkproxy.JavaProxy;
import com.kingboy.core.Calculator;
import com.kingboy.core.CalculatorImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @Author kingboy
 * @Date 2017/6/14 下午2:45
 * @Description Main is used to 主方法
 */
public class JavaProxyMain {

    public static void main(String[] args) {

        Calculator calculator = new CalculatorImpl();
        InvocationHandler invocationHandler = new JavaProxy(calculator);
        calculator = (Calculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(),
                calculator.getClass().getInterfaces(), invocationHandler);
        calculator.add(1,2);
        calculator.reduce(6, 2);


    }
}

Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 

参数说明:
- ClassLoader loader:类加载器
- Class <\?>[] interfaces:得到全部的接口
- InvocationHandler h:得到InvocationHandler接口的子类实例

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 

输出结果

add方法执行之前的执行操作!
------我是add方法------
add方法执行之后的执行操作!
我是其它方法的前面!!
------我是其它方法------
我是其它方法的后面!!

三、CGLib

  1. Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理。

  2. JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

  3. CGLIB的核心类:

    • net.sf.cglib.proxy.Enhancer
      主要的增强类
    • net.sf.cglib.proxy.MethodInterceptor
      主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    • net.sf.cglib.proxy.MethodProxy
      JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
      Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

    • net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法

public Object intercept(Object object, Method method,Object[] args, MethodProxy proxy)
第一个参数是代理对像
第二和第三个参数分别是拦截的方法和方法的参数。

CGLib代理

package com.kingboy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author kingboy
 * @Date 2017/6/14 下午6:41
 * @Description CGLibProxy is used to cglib代理类
 */
public class CGLibProxy implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before:" + methodProxy.getSuperName());
        System.out.println("methodName:" + method.getName());
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("after:" + methodProxy.getSuperName());
        return o1;
    }
}

cglib代理测试主类

package com.kingboy;

import com.kingboy.cglib.CGLibProxy;
import com.kingboy.core.Calculator;
import com.kingboy.core.CalculatorImpl;
import net.sf.cglib.proxy.Enhancer;

/**
 * @Author kingboy
 * @Date 2017/6/14 下午6:40
 * @Description CGLibMain is used to 测试cglib的类
 */
public class CGLibMain {

    public static void main(String[] args) {
        CGLibProxy cglibProxy = new CGLibProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CalculatorImpl.class);
        enhancer.setCallback(cglibProxy);

        Calculator o = (Calculator)enhancer.create();
        o.add(1, 2);
    }

}

参考资料:Java动态代理的两种实现方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值