Java --代理

Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。

JDK 动态接口代理

  1. JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类: Proxy 和 InvocationHandler 。
    InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动 态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实 例,生成目标类的代理对象。
    CGLib 动态代理
  2. : CGLib 全称为 Code Generation Library ,是一个强大的高性能,高质量的代码生成类库,可以 在运行期扩展 Java 类与实现 Java 接口, CGLib 封装了 asm ,可以再运行期动态生成新的 class 。 和 JDK 动态代理相比较: JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通 过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。

静态代理

前提声明一个接口TargetInterface,Target类实现这个接口,并实现接口方法,而后TargetProxy也实现这个接口,并实例化一个Target类—target,然后在实现的接口方法中调用target的方法,并在调用其书写执行前后的语句。

具体代码
接口类

public interface TargetInteface {
    void method1();
    void method2();
    int method3(Integer i);
}

实现类

public class Target implements TargetInteface {
    @Override
    public void method1() {
        System.out.println(" Target method1 running ...");
    }

    @Override
    public void method2() {
        System.out.println("Target method2 running ...");
    }

    @Override
    public int method3(Integer i) {
        System.out.println("Target  method3 running ...");
        return i;
    }
}

代理类

public class TargetProxy implements TargetInteface {

    private Target target =new Target();
    @Override
    public void method1() {
        System.out.println("执行方法前...");
        target.method1();
        System.out.println("执行方法后...");
    }

    @Override
    public void method2() {
        System.out.println("执行方法前...");
        target.method2();
        System.out.println("执行方法后...");
    }

    @Override
    public int method3(Integer i) {
        System.out.println("执行方法前...");
        int method3 = target.method3(i);
        System.out.println("执行方法后...");
        return method3;
    }
}

测试类

public class TargetUser {

    public static void main(String[] args) {
        TargetInteface target = new TargetProxy();
        target.method1();
        System.out.println("-----------------------------");
        target.method2();
        System.out.println("-----------------------------");
        System.out.println(target.method3(3));
    }
}

结果输出
在这里插入图片描述
总结
静态代理非常简单,但是它的缺陷也是显然的,因为静态代理的代理关系在 IDE 编译时就确定了,如果接口改变了,不仅实现类要改变,代理类也要改变,代理类和接口之间的耦合非常严重。所以在现实实践中很少用到

JDK动态代理

JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类: Proxy 和 InvocationHandler 。
InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动 态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实 例,生成目标类的代理对象。

实例代码

主要修改的代理类和测试类

代理类


public class TargetProxy {
    public static  <T> Object getTarget(T t) {
        return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
                        //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                        System.out.println("执行方法前...");
                        Object invoke = method.invoke(t, args);
                        System.out.println("执行方法后...");
                        return invoke;
            }
        });
    }
}

测试类

public class TargetUser {

    public static void main(String[] args) {
        TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
        target.method1();
        System.out.println("-----------------------------");
        target.method2();
        System.out.println("-----------------------------");
        System.out.println(target.method3(3));
    }

}

测试结果
在这里插入图片描述
总结
利用JDK实现动态代理,本质就是利用 Java 的反射机制在程序运行期动态的创建接口的实现类,并生产代理对象而已

涉及两个类InvocationHandler 和 Proxy

  • Proxy 类的作用是动态创建一个代理对象,也就是代理对象不需要我们自己写。用的最多的是 newProxyInstance
    • 其中第一个参数是被代理的类的加载器,传入的目的是告诉 JDK 由哪个类加载器对生成的代理进行加载。其实就是真实的类(实现类)的对象的加载器。
    • 第二个参数是代理类需要实现的接口,可以多个。其实就是接口 TargetInterface,很好理解,在静态代理模式中,我们就需要手动实现这个接口,来实现代理类。
    • 第三个参数就是实现了InvocationHandler接口的类即可,原因是此类里有 invoke 方法,而通过 Proxy 的 newProxyInstance 方法生成的代理类去调用接口方法(dosth)时,对方法(dosth)的调用会自动委托给 InvocationHandler 接口的 invoke 方法,这样也就实现了代理模式。
  • InvocationHandler 接口只有一个方法 —— invoke,参数分别是:
    proxy:代理的真实对象,也就是实现类的对象
    method:要调用真实对象的某个方法的Method对象,也就是会调用 dosth 的方法的对象
    args:调用真实对象某个方法时接受的参数,没有就是空数组

缺点:JDK 动态代理这种方式只能代理通过接口定义业务方法的类,这是其缺陷

CGLib

CGLib 全称为 Code Generation Library ,是一个强大的高性能,高质量的代码生成类库,可以 在运行期扩展 Java 类与实现 Java 接口, CGLib 封装了 asm ,可以在运行期动态生成新的 class 。 和 JDK 动态代理相比较: JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通 过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。

实例代码

首先,CGLib 代理的类不需要实现接口,代理类使用Enhancer生成代理对象,并指定目标类以及代理的行为


public class TargetProxy {

    public static <T> Object getProxy(T t) {
        Enhancer en = new Enhancer(); //帮我们生成代理对象
        en.setSuperclass(t.getClass());//设置要代理的目标类
        en.setCallback(new MethodInterceptor() {//代理要做什么
            @Override
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("执行方法前。。。");
                //调用原有方法  
                Object invoke = methodProxy.invokeSuper(object, args);
                // Object invoke = method.invoke(t, args);// 作用等同与上面。
                System.out.println("执行方法后。。。");
                return invoke;
            }
        });
        return en.create();
    }
}

测试


public class TargetUser {

    public static void main(String[] args) {
        Target target = (Target) TargetProxy.getProxy(new Target());
        System.out.println(target.getClass().getName());
        target.method1();
    }

}

结果输出
在这里插入图片描述
总结

  1. CGLib 底层是利用 asm 字节码框架实现的,该框架可以在 Java 程序运行时对字节码进行修改和动态生成,故它可以代理普通类,具体细节是通过继承和重写需要被代理的类(NoInterfaceReal)来实现。
  2. CGLib 可以实现对方法的代理,即可以实现拦截(只代理)某个方法。
  3. 通过CGLib 的 Enhancer 类来create 代理对象。而对这个对象所有非final方法的调用都会委托给 MethodInterceptor 接口的 intercept,我们可以在该方法内部写拦截代码,最后在通过调用MethodProxy 对象的 invokeSuper() 方法,把调用转发给真实对象

不足:无法对 final 类、或者 final 方法进行代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值