1. 程式人生 > >JDK代理和Cglib代理實現和區別

JDK代理和Cglib代理實現和區別

一.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方法進行代理