欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java静态代理、jdk动态代理、cglib动态代理简单示例和讲解

程序员文章站 2022-06-09 21:39:45
...

参考资料:https://www.cnblogs.com/ygj0930/p/6542259.html

 一:静态代理

          业务实现类、业务代理类 两部分组成。业务实现类 负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理,主要是在方法中首先进行预处理动作,然后调用业务实现类的方法,还可以规定调用后的操作。我们在需要调用业务时,不是直接通过业务实现类来调用的,而是通过业务代理类的同名方法来调用被代理类处理过的业务方法。

1:提供接口Human.java

package com.example.invocation;

/**
 * Created by [email protected] on 2019/4/22
 */
public interface Human {

    public void eat(String food);
}

2:提供接口实现类HumanImpl.java

package com.example.invocation;

/**
 * Created by [email protected] on 2019/4/22
 */
public class HumanImpl implements Human {
    @Override
    public void eat(String food) {
        System.out.println("eat food:"+food);
    }
}

3:提供静态代理类HumanProxy

package com.example.invocation;

/**
 * 静态代理类
 * 也需要实现接口
 * Created by [email protected] on 2019/4/22
 * 添加代理类HumanProxy实现Human
 * 提供Human的私有对象
 * 提供实例化HumanImpl对象的返回HumanProxy类型构造器
 * 在HumanProxy重载方法中调用Human对象操作另一个实例类来操作
 */
public class HumanProxy implements Human{


    private Human human;

    public HumanProxy(){
        human=new HumanImpl();
    }

    @Override
    public void eat(String food) {
        before();
        human.eat(food);
        after();
    }

    public void before(){
        System.out.println("before...");
    }
    public void after(){
        System.out.println("after...");
    }

    public static void main(String[] args) {
        Human human=new HumanProxy();
        human.eat("noods");
    }
}

接口提供方法、实现类提供方法具体实现、代理类中创建一个业务实现类对象来调用具体的业务方法;并且可以在执行方法前后做其他逻辑操作。

静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。

二:JDK动态代理(就是jdk里边提供的动态代理模式)

    JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。

提供jdk代理实现类

package com.example.invocation.jdkProxy;

import com.example.invocation.Human;
import com.example.invocation.HumanImpl;

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

/**
 * Created by [email protected] on 2019/4/22
 * jdk动态代理类
 * 1:实现InvocationHandler接口,实现invoke方法
 * 2:提供绑定业务对象(定义private Object target,提供构造器this.target=target)方法和返回代理类方法(可以二合一一个build方法)
 * 3:客户端直接调用build方法传入业务对象(new HumanImpl),获取代理对象Human
 * 4:代理对象直接调用接口方法
 */
public class DynamicProxy implements InvocationHandler {
    private Object target;//这其实业务实现类对象,用来调用具体的业务方法

    /**
     * 绑定业务对象并返回一个代理类
     */
    public Object build(Object target){
        this.target=target;//接收业务实现类对象参数
        //通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
        //创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("预处理操作——————");
        Object object= method.invoke(target,args);
        System.out.println("调用后处理——————");
        return object;
    }



    public static void main(String[] args) {
        DynamicProxy dynamicProxy=new DynamicProxy();
        Human humanProxy= (Human) dynamicProxy.build(new HumanImpl());
//        Human humanProxy= (Human) Proxy.newProxyInstance(
//                human.getClass().getClassLoader(),
//                human.getClass().getInterfaces(),
//                dynamicProxy
//        );



        humanProxy.eat("noods");
    }

}

 

 JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

三:CGlib动态代理(springframework)

package com.example.invocation.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


/**
 * cglib动态代理类
 * 可以代理非实现接口类
 * final修饰的方法不能被代理
 * Created by [email protected] on 2019/4/22
 * 1:实现MethodInterceptor接口,intercept
 * 2:提供绑定业务对象(定义private Object target,提供构造器this.target=target)方法和返回代理类方法(可以二合一一个getInstance方法)
 * 3:客户端直接调用getInstance方法传入业务对象(new Person),获取代理对象Person
 * 4:代理对象直接调用接口方法
 */
public class MyCglibInterceptor implements MethodInterceptor {

    private Object target;//业务对象
    //相当于JDK动态代理中的绑定
    public Object getInstance(Object target){
        //绑定业务对象
        this.target=target;
        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(this.target.getClass());
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        // 创建动态代理类对象并返回
        return enhancer.create();
    }

    /**
     * 实现方法回调
     * o: cglib生成的代理对象
     * method: 目标方法
     * objects: 目标方法的参数
     * methodProxy:代理方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里调用切面方法before
        System.out.println("方法执行前");
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object object = method.invoke(this.target,objects);//调用目标类的目标方法
        System.out.println("方法执行后");
        //这里调用切面方法after
        return object;
    }

    public static void main(String[] args) {
        MyCglibInterceptor myInterceptor = new MyCglibInterceptor();
        Person personProxy = (Person) myInterceptor.getInstance(new Person());
        personProxy.eat("noods");
        personProxy.sayOthers("1");
//        //实践证明final方法没有被代理
//        方法执行前
//        i'm a person ,i'm eatting noods
//                方法执行后
//        i'm a person ,i'm eatting other 1
    }
}




cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 

总结:

 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

    JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

    CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

1.JDK动态代理是实现了被代理对象的接口,HumapProxy.java。cglib是继承了被代理对象。MyInterceptor.java
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。