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