JDK中的动态代理和CGLIB

代理模式

1.什么是动态代理?

代理模式 区别 局限
静态代理 编译时生成class文件 代理类实现的接口和方法固定
动态代理 运行时动态创建代理类 ---
静态代理类维持一个真实对象的引用,代理真实对象的方法

2.动态代理的两种实现

  • JDK中的动态代理: 通过反射类Proxy以及InvocationHandler回调接口实现的,

JDK动态代理缺点: JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

  • CGLIB

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB优点:它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB缺点:对于final方法,无法进行代理

2. Java静态代理实例

还是贴一下静态代理的代码实例作为对比

代理方法 : 购买化妆品

抽象主题角色:商家

public interface Business {

      public void sell();   // 卖东西
}

真实目标类:香港化妆品商

public class HongKongBusiness implements Business{


    public void sell() {
        System.out.println("========我出售香港化妆品!价格比大陆便宜得多^-^");

        ExecutorService pool = Executors.newSingleThreadExecutor();

        Future   ft = pool.submit(new Callable() {
            public Boolean call() throws Exception {
                for (int i = 0; i < 5; i++) {
                    System.out.print(">>");
                    Thread.sleep(500);
                }
                return true;
            }
        });

        try {
            if(ft.get()){
                System.out.println("=========商品已交货,祝您购物愉快。");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        pool.shutdown();
    }
}

代理类 : 代购服务商

public class ProxyBusiness implements Business{
    HongKongBusiness  hongKongBusiness  = new HongKongBusiness();   // 维持对真实目标类的引用


    public void sell() {
        System.out.println("=========我是香港代购商家,代跑腿。");

        System.out.println(">>>>>以下将执行代购方法,我的用户不需要具体关心,我一手包办服务到位。");
        hongKongBusiness.sell();
    }
}

测试类,用户类 :需要代购服务的用户

public class UserClient {
    public static void main(String[] args) {
        Business  proxy = new ProxyBusiness();
        proxy.sell();
    }
}

3. Java动态代理实例

3.1定义接口

通过JDK实现的代理对象必须是一个接口的实现。

package com.kang.proxy;

//需要动态代理的接口 
public interface Subject {

    public void hello(String name);
    public String bye();

}
3.2、被代理的类(实际处理业务的类)

这个类实现上面定义的接口。

package com.kang.proxy;

//被代理类
public class RealSubject implements Subject{

    @Override
    public void hello(String name) {
        System.out.println("hello "+name);
    }

    @Override
    public String bye() {
        System.out.println("bye");
        return "bye";
    }

}
3.3、InvocationHandler实现类
package com.kang.proxy;

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

//每次生成动态代理类对象时都需要指定一个实现了InvocationHandler接口的调用处理器对象 
public class InvocationHandlerImpl implements InvocationHandler {

    private Object subject; // 这个就是我们要代理的真实对象,也就是真正执行业务逻辑的类

    public InvocationHandlerImpl(Object subject) {// 通过构造方法传入这个被代理对象
        this.subject = subject;
    }

    /**
     * 该方法负责集中处理动态代理类上的所有方法调用。 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行**/

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("可以在调用实际方法前做一些事情");
        System.out.println("当前调用的方法是" + method.getName());
        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        result = method.invoke(subject, args);// 需要指定被代理对象和传入参数
        System.out.println(method.getName() + "方法的返回值是" + result);
        System.out.println("可以在调用实际方法后做一些事情");
        System.out.println("------------------------");
        return result;// 返回method方法执行后的返回值
    }

}
3.4 测试类
package Proxy.dynamicProxy;

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

/**
 * Created by bruce_shan on 2018/6/13 14:41.
 * Description :
 */
public class Test {

    public static void main(String[] args) {
        // 被代理的对象
        Subject realSubject = new RealSubject();

        /**
         * 通过InvocationHandlerImpl的构造器生成一个InvocationHandler对象,
         * 需要传入被代理对象作为参数
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);

        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();

        // 需要指定类装载器、一组接口及调用处理器生成动态代理类实例

        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

        System.out.println("动态代理对象的类型:" + subject.getClass().getName());
        subject.work();
    }
}

4. CGlib动态代理实例

//商人类

public class Boss{
    public void work(){
        System.out.println("-------->我是香港化妆品店老板,每天开店卖东西");
    }
}
//代理类 输出日志

import java.lang.reflect.Method;

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

public class BossProxy implements MethodInterceptor {
//通过Enhancer 创建代理对象
    private Enhancer enhancer = new Enhancer();

    //通过Class对象获取代理对象
    public Object getProxy(Class c){
        //设置创建子类的类
        enhancer.setSuperclass(c);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("日志开始...");
        //代理类调用父类的方法
        proxy.invokeSuper(obj, args);
        System.out.println("日志结束...");
        return null;
    }
}
public class TestCgibl {
    public static void main(String[] args) {
        //创建我们的代理类
        BossProxy Proxy = new BossProxy();
        Boss boss = (Boss)Proxy.getProxy(Boss.class);
        boss.work();
    }
}

你可能感兴趣的:(JDK中的动态代理和CGLIB)