zoukankan      html  css  js  c++  java
  • 动态代理JDK和CGLIB的二进宫

    1、JDK的动态代理,是对目标对象的代理,对象的类必须有接口,实现核心入口反射包里的Proxy类,通过Proxy.newInstance生成一个代理对象,其实现了所传入的接口,该接口与被代理对象实现的相同.

       JDK的代理是针对对象的,不是类,所以,我们最终是需要把被代理的对象传入代理类的,因为一个接口可以同时被多个类实现,所以JVM也无法判断当前代理对象想要代理哪个目标对象。

    样例1:

    public class JdkProxyMain {
        public static void main(String[] args) {
            /**
             * JDK代理的核心就是反射包里的Proxy类,通过newProxyInstance方法生成动态代理类
             */
            UserService proxied = new UserServiceImpl();
            UserService userService = (UserService)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                    new Class[]{UserService.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // proxy是动态生成代理对象,不要使用它里面的方法,会形成无限递归
                            System.out.println("进入JDK代理,被代理方法:"+method.getName()+",参数:"+ Arrays.toString(args));
                            // invoke调用的第一个参数是被代理的对象,传入的方式无所谓,本例中使用了匿名类,所以必须从外部进入。
                            Object result = method.invoke(proxied, args);
                            System.out.println("完成代理,返回值:"+result);
                            return result;
                        }
                    });
    
            User user = new User();
            user.setUserId(1L);
            user.setUserName("admin");
            userService.addUser(user);
            userService.updateUser(user);
            userService.deleteUser(user.getUserId());
        }
    }

    样例2,包装一个可接受参数的代理类,通过getProxyInstance返回代理对象

    public class ProxyByJDK implements InvocationHandler {
        // 被代理对象,外部传入
        private Object proxied;
    
        /**
         *
         * @param proxied 被代理的对象
         * @return 动态生成的代理对象
         */
        public Object getProxyInstance(Object proxied){
            this.proxied = proxied;
            return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                    proxied.getClass().getInterfaces(),
                    this);
        }
    
        /**
         *
         * @param clazz 被代理的类
         * @return 代理对象
         * @throws IllegalAccessException
         * @throws InstantiationException
         */
        public Object getProxyInstance(Class<?> clazz) throws IllegalAccessException, InstantiationException {
            Object proxied = clazz.newInstance();
            this.proxied = proxied;
            return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                    proxied.getClass().getInterfaces(),
                    this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("进入JDK代理,被代理方法:"+method.getName()+",参数:"+ Arrays.toString(args));
            Object result = method.invoke(proxied, args);
            System.out.println("完成代理,返回值:"+result);
            return result;
        }
    }
    public class JdkProxyMain2 {
        public static void main(String[] args) {
            UserService userService = (UserService)new ProxyByJDK().getProxyInstance(new UserServiceImpl());
            User user = new User();
            user.setUserId(1L);
            user.setUserName("admin");
            userService.addUser(user);
            userService.updateUser(user);
            userService.deleteUser(user.getUserId());
        }
    }

    2、CGLIB动态代理是通过生成目标类的子类方式实现动态代理,与接口没有关系,private和finall修饰的类和方法无法增强。

         CGLIB不用传入被代理对象,只需传入类类型,由CGLIB生成被代理的子类实例。

    CGLIB代理模式,核心入口增强类Enhancer,重点Enhance的setSuperClass和setCallback。

    setSuperClass用于设置被代理的类

    setCallback用于设置代理执行的回调接口

    样例1:

    public class CglibProxyMain2 {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(LoginService.class);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("进入cglib代理,被代理方法:"+method.getName()+",参数:"+ Arrays.toString(args));
                    // obj是代理对象,所以必须用invokeSuper调用父类的方法
                    // 如果是proxy.invoke,那么入参必须是被代理对象
                    Object result = proxy.invokeSuper(obj, args);
                    System.out.println("代理结束,返回值:"+result);
                    return result;
                }
            });
            LoginService loginService = (LoginService) enhancer.create();
            loginService.Login("admin","123");
            loginService.logout("admin");
            System.out.println();
            System.out.println("-----------------");
    
    
            enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("进入cglib代理,被代理方法:"+method.getName()+",参数:"+ Arrays.toString(args));
                    Object result = proxy.invokeSuper(obj, args);
                    System.out.println("代理结束,返回值:"+result);
                    return result;
                }
            });
            UserService userService = (UserService) enhancer.create();
            User user = new User();
            user.setUserName("admin");
            userService.addUser(user);
            userService.updateUser(user);
            userService.deleteUser(123L);
        }
    }

    样例2:

    public class ProxyByCGLIB implements MethodInterceptor {
    
        public Object getProxyInstance(Class<?> clazz){
            Enhancer enhancer = new Enhancer();
            // 设置要代理的类
            enhancer.setSuperclass(clazz);
            // 设置回调的对象
            enhancer.setCallback(this);
            return enhancer.create();
        }
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("进入cglib代理,被代理方法:"+method.getName()+",参数:"+ Arrays.toString(args));
            // 如果MethodProxy调用参数第一个是代理对象本身,需要调用其父类的方法,因为cglib是动态生成一个子类
            Object result = proxy.invokeSuper(obj, args);
            // 注意与上面方法入参的区别, proxy.invoke的入参必须是被代理的对象,这种方式耦合度太高,而且多创建了一个对象
    //        Object result2 = proxy.invoke(proxied, args);
    
            System.out.println("代理结束,返回值:"+result);
            return result;
        }
    }
    public class CglibProxyMain {
        public static void main(String[] args) {
            LoginService loginService = (LoginService) new ProxyByCGLIB().getProxyInstance(LoginService.class);
            loginService.Login("admin","123");
            loginService.logout("admin");
    
        }
    }

    3、一个通过CGLIB实现动态代理鉴权的样例

    3.1、注解

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Permission {
        String[] value();
    }

    3.2、权限工具类

    public class AuthUtils {
        public static String[] getAuthorities(){
            // 模拟当前用户所具备的权限
            return new String[]{"user:add","user:update"};
        }
    
        // 判断是否具备方法权限
        public static boolean hasAnyPermissions(Method method){
            List<String> currentAuthorites = null;
            if (null != method.getAnnotation(Permission.class)) {
                currentAuthorites = Arrays.asList(method.getAnnotation(Permission.class).value());
            }
            if(currentAuthorites == null || currentAuthorites.size() ==0) return true;
    
            List<String> hasPermissions = Arrays.asList(getAuthorities());
            System.out.println("权限检查,需要权限:"+currentAuthorites);
            return hasPermissions.stream().anyMatch(currentAuthorites::contains);
        }
    }

    3.3、业务类

    public class UserServiceImpl implements UserService {
    
        @Override
        @Permission("user:add")
        public User addUser(User user) {
            System.out.println("addUser --> "+user);
            user.setPassword("addUser");
            return user;
        }
    
        @Override
        @Permission("user:update")
        public User updateUser(User user) {
            System.out.println("updateUser --> "+user);
            user.setPassword("updateUser");
            return user;
        }
    
        @Override
        @Permission("user:delete")
        public boolean deleteUser(Long userId) {
            System.out.println("deleteUser --> "+userId);
            return true;
        }
    
        @Override
        public User getUser(Long userId) {
            User user = new User();
            user.setUserId(userId);
            user.setUserName("xu.dm");
            user.setPassword("123456");
            return user;
        }
    }

    3.4、权限代理类

    public class ProxyAuthority implements MethodInterceptor {
    
        public Object getProxyInstance(Class<?> clazz){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            boolean hasAnyPermissions = AuthUtils.hasAnyPermissions(method);
            if(hasAnyPermissions){
                System.out.println("通过权限审核..."+method.getName());
                Object result = proxy.invokeSuper(obj,args);
                return result;
            }else {
                System.out.println("没有权限调用该方法..."+method.getName());
                return null;
            }
        }
    }

    3.5、main类

    public class CglibAuthProxyMain {
        public static void main(String[] args) {
            // 实际操作中,这里应该使用工厂模式屏蔽UserService的获取细节
    //        UserService userService = (UserService) new ProxyAuthority().getProxyInstance(UserServiceImpl.class);
            UserService userService = UserServiceFactory.getUserServiceProxy();
            User user = new User();
            user.setUserId(123L);
            user.setUserName("admin");
            user.setPassword("123456");
            userService.addUser(user);
            userService.updateUser(user);
            userService.deleteUser(123L);
            userService.getUser(456L);
        }
    }

    工厂类

    public class UserServiceFactory {
        public static UserService getUserServiceProxy(){
            UserService userService = (UserService) new ProxyAuthority().getProxyInstance(UserServiceImpl.class);
            return userService;
        }
    }

    结果:

    权限检查,需要权限:[user:add]
    通过权限审核...addUser
    addUser --> User{userId=123, userName='admin', password='123456'}
    权限检查,需要权限:[user:update]
    通过权限审核...updateUser
    updateUser --> User{userId=123, userName='admin', password='addUser'}
    权限检查,需要权限:[user:delete]
    没有权限调用该方法...deleteUser
    通过权限审核...getUser

    完整代码:https://github.com/asker124143222/myJava

  • 相关阅读:
    JBoss 系列十八:使用JGroups构建块RpcDispatcher构建群组通信应用
    TJU Easier Done than Said?
    [置顶] 程序员面试之道(《程序员面试笔试宝典》)之如何回答系统设计题?
    百度2014校园招聘笔试题 ——深度学习算法研发工程师.
    SpringCloud常用注解
    @PrePersist
    【转】http_load压力测试过程和使用方式
    handlermethodargumentresolver
    云计算的三种服务模式:IaaS,PaaS和SaaS
    pip install 升级时候 出现报asciii码错误的问题。
  • 原文地址:https://www.cnblogs.com/asker009/p/13874068.html
Copyright © 2011-2022 走看看