jdk动态代理与cglib动态代理

Posted liqiliang1437

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk动态代理与cglib动态代理相关的知识,希望对你有一定的参考价值。

动态代理

动态代理23种设计模式之一

动态代理的基于反射的

代理模式:

作用:
	1)功能增强:在原有的功能上,增加了额外的功能,做了功能增强
	2)控制访问:代理不让用户访问目标,比如4S店不让我直接找厂家
开发中,如果A类本来是调用C类的方法,完成某个功能
但是C不让A调用
A不能访问C,然后在A和C之间创建一个B代理类
C让B访问
B是代理类,既能访问C,同时又能增加新的业务功能,增强对C的访问
A通过访问B,B访问C,到了A访问C的结果,虽然是间接访问

有点类似用户和厂家的关系
比如说小李想买辆迈凯伦,但是小李不能直接联系到迈凯伦的厂家,厂家并不提供个人业务,那怎么办呢?只能通过4S店了,
我们去4S店买,4S店帮我们去和厂家订车,提车,并且还能给我们提供优质的服务。所以迈凯伦4S店就好比代理类,不仅帮我们去间接找到了厂家,
还对业务进行增强,为小李提供服务

实现代理的模式:

  • 静态代理:

    ? 1)代理类是自己实现的,自己创建一个java类,表示代理类

    ? 2)并且要代理的目标类也是确定的

  • 动态代理

    ? 1)程序执行过程中,使用JDK的反射机制,创建代理类对象,并动态指定要代理的目标类

动态代理的实现分类:

  • JDK动态代理:使用jdk反射包中的类和接口实现动态代理的功能
  • cglib动态代理:第三方的工具库,cglib的原理是继承,通过继承目标类,创建他的子类,在子类总重写父类中同名的方法,实现功能的修改
因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的
cglib的要求目标类比较宽松,只要能继承就可以了
cglib在很多的框架中使用,比如mybatis,spring框架中都有使用

区别:

JDK动态代理要求目标类必须有接口


复习method方法

public interface Hello {  void sayHello(String name);  }
public class HelloImpl implements Hello {

    @Override
    public void sayHello(String name) {
        System.out.println("你好!"+name);
    }
}
public class Test01 {

    @Test
    public void test01(){
        Hello hello = new HelloImpl();
        hello.sayHello("小李");
        /*
        你好!小李
        */
}
public class Test01 {
    @Test
    public void test01(){
        //通过反射机制来执行sayHello方法      
        Hello hello = new HelloImpl();
        
        try {
            Method method = Hello.class.getDeclaredMethod("sayHello", String.class);
            method.invoke(hello,"启亮");
			
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/*
你好!启亮
*/

技术图片

Object obj : 对象,要执行这个对象的方法

Object... args : 方法执行的参数值

Method是一个独立的方法对象,代表所有sayHello方法的对象,通过invoke就可以执行任意对象的sayHello方法,然后通过invoke中参数指定具体执行那个对象中的sayHello方法,和执行方法锁需要的参数

jdk动态代理的实现

反射包 java.lang.reflect;里面有三个类
InvocationHandler
Method
Proxy

jdk动态代理的使用

1)Proxy类:

? 核心的对象,创建代理对象。之前创建对象都是new类的构造方法

? 现在是使用Proxy类的方法,代替new的使用

方法:newProxyInstance

原型:

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

参数:

ClassLoader loader,  
类加载器(目标对象的类加载器),负责向内存中加载对象,使用反射获取对象的ClassLoader 
Class<?>[] interfaces, 
接口,目标对象实现的接口,也是反射获取的
InvocationHandler h   
我们自己写的,代理类要完成的功能
返回值:就是代理的对象

2)newProxyInstance

InvocationHandler接口
源码:
public interface InvocationHandler {
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;  }

InvocationHandler: 译为:调用处理程序

invoke:表示代理对象要执行的功能代码,代理类要完成的功能就写在这个方法中
		完成的功能:1.调用目标方法,执行目标方法的功能
				  2.功能增强,在目标方法调用时,增加功能
				  
参数说明:
proxy:jdk创建的代理对象,无需赋值
method:目标类中的方法  ,由jdk提供method对象的
args:目标类中方法的参数,由jdk提供

InvocationHandler接口:表示你的代理要干什么

怎么使用:

? 1.既然是接口,先实现接口InvocationHandler

? 2.重写invoke方法,原来静态代理中代理类完成的功能写在这里

3)Method类:

? 表示方法的,具体来说就是目标类中的方法

? 作用:通过Method可以执行某个目标类的方法,Method.invoke();

Method.invoke() ; 中的invoke只是Method类中的方法
public Object invoke(...,...)是InvocationHandler接口中的方法

Method.invoke(目标对象,方法的参数);

通过这个目标对象就可以执行对象的方法,而不需要知道这个方法的名称,因为method方法的值是jdk帮你确定的,这个对象本身就是jdk提供的

动态代理实现步骤

1.创建接口,定义目标类要完成的功能

2.创建目标类实现接口

3.创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

? ● 调用目标方法

? ● 增强功能

4.使用Proxy类的静态方法,创建代理对象,并把返回值转换为接口类型

代码实现:

User.java

public interface User {
    void add();
    void delete();
    void update();
    void find();
}

UserImpl.java

public class UserImpl implements User {
    @Override
    public void add() {
        System.out.println("增");
    }

    @Override
    public void delete() {
        System.out.println("删");
    }

    @Override
    public void update() {
        System.out.println("改");
    }

    @Override
    public void find() {
        System.out.println("查");
    }
}

UserHandler.java

//必须实现InvocationHandler接口,完成代理类要做的功能
//1.调用目标方法  2.功能增强
public class UserHandler implements InvocationHandler {

    private Object target = null;//目标对象

    //动态代理:目标对象是动态的,不是固定的
    //传入的哪个对象,就给哪个对象创建代理
    public UserHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        /*
        target:里面这个对象是动态的,需要传入
        result:是方法执行的返回值,没有可以不加
         */

        System.out.println("前增强");//增强方法

        Object result = method.invoke(target,args);//传入的对象执行的每个方法,都会调用这个

        System.out.println("后增强");//增强方法

        //返回方法执行的返回值
        return result;
    }
}

MyProxy.java

public class MyProxy {

    @Test
    public void test() {
        //1.创建目标对象
        User user = new UserImpl();

        //2.创建InvocationHandler对象
        InvocationHandler handler = new UserHandler(user);

        //3.创建代理对象
        User userProxy = (User) Proxy.newProxyInstance(
             user.getClass().getClassLoader(),//获得这个对象的类加载器才能操作这个对象
             user.getClass().getInterfaces(),
             handler
        );
        //4.把类型转换成代理对象的接口类型,因为目标对象实现了这个接口

        //5.通过代理对象执行方法
        userProxy.add();
        System.out.println("=========");
        userProxy.delete();
        System.out.println("=========");
        userProxy.update();
        System.out.println("=========");
        userProxy.find();

    }
}

打印结果:

前增强
增
后增强
=========
前增强
删
后增强
=========
前增强
改
后增强
=========
前增强
查
后增强

匿名内部类实现:

User.java

public interface User {
    void add();
    void delete();
    void update();
    void select();
}

UserImpl.java

public class UserImpl implements User {
    @Override
    public void add() {
        System.out.println("增");
    }

    @Override
    public void delete() {
        System.out.println("删");
    }

    @Override
    public void update() {
        System.out.println("改");
    }

    @Override
    public void select() {
        System.out.println("查");
    }
}

MyProxyTest.java

public class MyProxyTest {
        private User user = new UserImpl();
    @Test
    public void test01(){
        user.add();
        user.delete();
        user.update();
        user.select();
        System.out.println("================");

        User proxy = (User) Proxy.newProxyInstance(
                user.getClass().getClassLoader(),//被代理对象的类加载器
                user.getClass().getInterfaces(),//获取被代理对象所实现的接口,(有共同的特征)
                new InvocationHandler() {      //就是具体实现代理的逻辑
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("A");
                        //所有代理对象调用的方法都会执行
                        Object invoke = method.invoke(user, args);
                        System.out.println("B");
                        return invoke;//返回给代理对象,有返回值就是此返回值
                    }
                });
        proxy.add();
        System.out.println("*******");
        proxy.delete();
        System.out.println("*******");
        proxy.update();
        System.out.println("*******");
        proxy.select();
    }
}

*

cglib动态代理的实现

动态代理:
 特点:字节码随用随创建,随用随加载
 作用:不修改源码的基础上对方法增强
 分类:
     基于接口的动态代理  jdk
     基于子类的动态代理  cglib
 基于子类的动态代理:
     涉及的类:Enhancer
     提供者:第三方cglib库
 如何创建代理对象:
     使用Enhancer类中的create方法
 创建代理对象的要求:
     被代理类不能是最终类
 create方法的参数:
     Class:字节码
         它是用于指定被代理对象的字节码。

     Callback:用于提供增强的代码
         它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         此接口的实现类都是谁用谁写。
         我们一般写的都是该接口的子接口实现类:MethodInterceptor

依赖:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.1</version>
</dependency>

案例:

Producer.java

/**
 * 一个生产者
 */
public class Producer {

    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到钱:"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服务,并拿到钱:"+money);
    }
}

测试类:

Client.java

/**
 * 模拟一个消费者
 */
public class Client {

    public static void main(String[] args) {
        
        final Producer producer = new Producer();

        Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
             * @param methodProxy :当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供增强的代码
                Object returnValue = null;

                //1.获取方法执行的参数
                Float money = (Float)args[0];
                //2.判断当前方法是不是销售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(10000f);
    }
}
//销售产品,并拿到钱:8000.0



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

JDK动态代理与CGLIB动态代理

JDK动态代理与CGLIB动态代理

JDK 动态代理与 CGLIB 动态代理,它俩真的不一样

JDK的动态代理与cglib动态代理

DK动态代理与CGLib动态代理的区别

Spring AOP JDK动态代理与CGLib动态代理区别