zl程序教程

您现在的位置是:首页 >  后端

当前栏目

深入Java设计模式之代理模式

2023-09-11 14:20:01 时间

代理模式的分类

       我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的

 

静态代理

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。 

 

动态代理

动态代理分为Cglib动态代理和JDK动态代理

优点:大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度

缺点:代码复杂度增加

JDK动态代理

package com.lf.shejimoshi.proxy.jdk;

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

import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
//JDK动态代理实现InvocationHandler接口
public class JdkProxy implements InvocationHandler {
    private Object target ;//需要代理的目标对象
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理,监听结束!");
        return result;
    }
    //定义获取代理对象方法
    private Object getJDKProxy(Object targetObject){
        //为目标对象target赋值
        this.target = targetObject;
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }
    
    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
        user.addUser("admin", "123123");//执行新增方法
    }
    
}

Proxy.newProxyInstance( ClassLoader paramClassLoader,  Class<?>[] paramArrayOfClass,  InvocationHandler paramInvocationHandler)进行观察,简单来看就是先生成新的class文件,然后加载到jvm中,然后使用反射,先用class取得他的构造方法,然后使用构造方法反射得到他的一个实例。

标红的是最复杂的。然后cglib的实现原理基本一致,唯一的区别在于生成新的class文件方式和结果不一样。

Cglib代理

package com.lf.shejimoshi.proxy.cglib;

import java.lang.reflect.Method;

import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;

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

//Cglib动态代理,实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
    private Object target;//需要代理的目标对象
    
    //重写拦截方法
    @Override
    public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
        System.out.println("Cglib动态代理,监听开始!");
        Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组
        System.out.println("Cglib动态代理,监听结束!");
        return invoke;
    }
    //定义获取代理对象方法
    public Object getCglibProxy(Object objectTarget){
        //为目标对象target赋值
        this.target = objectTarget;
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(objectTarget.getClass());
        enhancer.setCallback(this);// 设置回调 
        Object result = enhancer.create();//创建并返回代理对象
        return result;
    }
    
    public static void main(String[] args) {
        CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象
        UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());//获取代理对象
        user.delUser("admin");//执行删除方法
    }
    
}

动态代理的结果问题

Cglib和JDK动态代理的区别?

 

Cglib

JDK

是否提供子类代理

是否提供接口代理

是(可强制)

区别

必须依赖于CGLib的类库,利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理

实现InvocationHandler 

使用Proxy.newProxyInstance产生代理对象(匿名类)

被代理的对象必须要实现接口

在调用具体方法前调用InvokeHandler来处理

什么时候用Cglib什么时候用JDK动态代理?

1、目标对象生成了接口 默认用JDK动态代理

2、如果目标对象使用了接口,可以强制使用cglib

3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换

JDK动态代理和Cglib字节码生成的区别?

1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类

2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的

Cglib比JDK快?

测试代码如下:

测试环境jdk1.8

https://gitee.com/lzhcode/maven-parent/tree/master/lzh-technology/src/main/java/com/lzhsite/technology/designPattern/proxy/dynamicProxy

createJdkProxy:76
JdkProxy class:com.sun.proxy.$Proxy0
callJdkProxy:117
createCglibProxy:1195
CglibProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery$$EnhancerByCGLIB$$136e9b81
callCglibProxy:26
createJavassistDynProxy:464
JavassistDynProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery_$$_javassist_0
callJavassistDynProxy:6346
createJavassistBytecodeDynamicProxy:1080
JavassistBytecodeDynamicProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQueryJavaassistBytecodeProxy
callJavassistBytecodeDynamicProxy:24

可以看出jdk的创建速度优于cglib,cglib的调用速度优于jdk

Spring如何选择是用JDK还是cglib?

1、当bean实现接口时,会用JDK代理模式

2、当bean没有实现接口,用cglib实现

3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)

 

参考文章:

https://blog.csdn.net/doujinlong1/article/details/80680149

http://www.manongjc.com/article/51229.html