JDK代理和Cglib代理实现和区别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lipp555/article/details/79969941

一.JDK代理实现

接口类

public interface UserManager {
    void addUser(@NotBlank String username, @NotNull(message = "javax.validation.constraints.NotNull.message") String password);

    void delUser(int userId);

    String findUserById(int userId);
}

实现类

public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String username, String password) {
        System.out.println("addUser:" + username + "," + password);
    }

    @Override
    public void delUser(int userId) {
        System.out.println("delUser:" + userId);
    }

    @Override
    public String findUserById(int userId) {
        System.out.println("findUserById:" + userId);
        return String.valueOf(userId);
    }
}

代理类

public class SecurityHandler implements InvocationHandler {
    private Object targetObject;

    public Object createProxyInstanceObject(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        checkSecurity();
        return method.invoke(targetObject,args);
    }

    private void checkSecurity(){
        System.out.println("-----------checkSecurity----------");
    }
}

二.CgLib代理实现

实现类

public class UserService {

    public void addUser(String username, String password) {
        System.out.println("addUser:" + username + "," + password);
    }

    public void delUser(int userId) {
        System.out.println("delUser:" + userId);
    }

    public String findUserById(int userId) {
        System.out.println("findUserById:" + userId);
        return String.valueOf(userId);
    }
}

代理类

public class CglibProxy implements MethodInterceptor {
    private Object targetObject;

    public Object createProxyInstanceObject(Object targetObject) {
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        return  enhancer.create();
        //return targetObject;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = null;
        //obj = methodProxy.invokeSuper(o, objects);
        obj = method.invoke(targetObject, objects);
        after();
        return obj;
    }

    private void before() {
        System.out.println("-----------before----------");
    }

    private void after() {
        System.out.println("-----------after----------");
    }
}

三.测试

public class App {
    public static void proxyManager() {
        SecurityHandler securityHandler = new SecurityHandler();
        UserManager userManager = (UserManager) securityHandler.createProxyInstanceObject(new UserManagerImpl());
        userManager.addUser("张飞", null);
    }

    public static void proxyService() {
        SecurityHandler securityHandler = new SecurityHandler();
        UserService userService = (UserService) securityHandler.createProxyInstanceObject(new UserService());
        userService.addUser("李四", null);
    }

    public static void main(String[] args) {
        cglibProxyManager();
    }

    public static void cglibProxyService() {
        CglibProxy proxy = new CglibProxy();
        UserService userService = (UserService) proxy.createProxyInstanceObject(new UserService());
        userService.findUserById(1234);
    }
    public static void cglibProxyManager() {
        CglibProxy proxy = new CglibProxy();
        UserManager userManager = (UserManager) proxy.createProxyInstanceObject(new UserManagerImpl());
        userManager.findUserById(1234);
    }
}

运行结果会发现方法proxyService报错。

提出问题:

1.Enhancer类的作用

    生成代理类,用来继承被代理对象

2.obj.invoke 和 obj.invokeSuper两种写法的区别

    一般使用invokeSuper(obj,args)方法。执行原始类的方法。方法.invoke,这是执行生成子类的方法。如果传入的obj就是子类的话,会发生内存溢出,因为子类的方法不停的进入intercept方法,而这个方法又去调用子类的方法,两个方法直接循环调用了。
  我们来看看MethodProxy,原始类里每一个方法都会在动态的子类里有一个对应的MethodProxy,而一个MethodProxy又对应了两个动态生成的FastClass类,一个是对应原始方法,一个对应新生成的子类,MethodProxy.invokeSuper就是交给对应原始方法那个FastClass,MethodProxy.invoke交给另一个。

3.proxyService方法为什么会报错

    核心在于ProxyGenerator.generateProxyClass

    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }
private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
1.在需要继承proxy类获得有关方法和InvocationHandler构造方法传参的同时,java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口
2.需要反射获得代理类的有关参数,必须要通过某个类,反射获取有关方法,如本次测试用的 :printSomeThing
3.成功返回的是object类型,要获取原类,只能继承/实现,或者就是那个代理类
4.对具体实现的方法内部并不关心,这个交给InvocationHandler.invoke那个方法里去处理就好了,我只想根据你给我的接口反射出对我有用的东西。
5.考虑到设计模式,以及proxy编者编写代码的逻辑使然

结论:

JDK代理和Cglib代理的区别

 JDK代理Cglib代理
实现

InvocationHandle

底层使用反射机制进行方法的调用r

MethodInterceptor

底层将方法全部存入一个数组中,通过数组索引直接进行方法调用

优点不需要硬编码接口,代码复用率高可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口
缺点只能够代理实现了接口的委托类不能对final类以及final方法进行代理




阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页