动态代理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

 

posted @ 2020-10-25 17:02  我是属车的  阅读(101)  评论(0编辑  收藏  举报