深入浅出:Java中的代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象,用来在系统中对原始的接口进行替换,然后在内部做一些增强处理。代理模式不会破坏系统的原有接口,不去修改原有的代码,做到添加或者删除一些功能。

Java的代理又分静态代理和动态代理。

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

缺点:如果接口修改了,需要修改代理类,不够灵活。

public interface UserService { void createUser(); List<User> findAllUser(); }

/** * 基于接口的静态代理实现 */ private static class StaticProxy implements UserService{ private UserService proxyUserService; public StaticProxy(UserService proxyUserService){ this.proxyUserService=proxyUserService; } @Override public void createUser() { log.info("static proxy call: createUser"); proxyUserService.createUser(); } @Override public List<User> findAllUser() { log.info("static proxy call: findAllUser"); return proxyUserService.findAllUser(); } } /** * 静态代理模式 * @return */ @GetMapping("/static") public int staticProxy(){ UserService proxy=new StaticProxy(this.userService); return proxy.findAllUser().size(); }

动态代理

动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象在运行时会为我们创建。

缺点: 基于接口实现,如果没有接口,则无法生成代理类。

JDK动态代理

/** * jdk的动态代理 * @return */ @GetMapping("/jdk") public int jdkProxy(){ log.info("call for jdk proxy start"); ClassLoader classLoader=this.getClass().getClassLoader(); Class[] interfaces=new Class[]{UserService.class}; UserService userService=(UserService) Proxy.newProxyInstance(classLoader,interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("proxy called: {}",method); return Collections.singletonList(new User("xxx","yyy",111)); } }); log.info("service proxy class is instanceof UserServiceImpl:{}",userService instanceof UserServiceImpl); List<User> users=userService.findAllUser(); log.info("call for jdk proxy end"); return users.size(); }

上面代码核心类是

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

loader: 定义代理类的类加载器。

interfaces: 代理类要实现的接口。

h: 代理类的回调接口。

Cglib动态代理

Cglib动态代理是基于使用子类继承的原理来实现的,所以要求父类不能是final的,并且,它只能代理能被子类重写的方法。带有final, static, private修饰的方法都不能被cglib代理到。

/** * cglib的动态代理 * @return */ @GetMapping("/cglib") public int cglibProxy(){ log.info("call for cglib proxy start"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { log.info("proxy called: {}",method); return Collections.singletonList(new User("xxx","yyy",111)); } }); UserService userService=(UserService)enhancer.create(); List<User> users=userService.findAllUser(); log.info("service proxy class is instanceof UserServiceImpl:{}",userService instanceof UserServiceImpl); log.info("call for cglib proxy end"); return users.size(); }

聊聊Spring AOP

我们使用spring最大的作用就是在于其强大的AOP。AOP的技术原理就是基于动态代理。我们拿事务注解来举例子。

@Autowired private UserService userService; @GetMapping("/create") public int createUser(){ try { log.info("service class {}",userService.getClass()); log.info("service proxy class is instanceof UserServiceImpl:{}",userService instanceof UserServiceImpl); userService.createUser(); }catch (Exception e){ } List<User> result= userService.findAllUser(); return result.size(); }

访问

GET :8080/user/create

019-03-25 13:23:47.592INFO 605 --- [nio-8080-exec-1] c.e.wechat.proxy.web.UserController: service class class com.eappcat.wechat.proxy.service.UserServiceImpl$$EnhancerBySpringCGLIB$$a6c6302a

2019-03-25 13:23:47.592INFO 605 --- [nio-8080-exec-1] c.e.wechat.proxy.web.UserController: service proxy class is instanceof UserServiceImpl:true

2019-03-25 13:23:47.610INFO 605 --- [nio-8080-exec-1] c.e.w.proxy.service.UserServiceImpl: class:class com.eappcat.wechat.proxy.service.UserServiceImpl

可以看到,默认条件下,Spring使用Cglib来代理事务。

我们修改下application.yml:

spring.aop: proxy-target-class: false

重新访问测试地址,

2019-03-25 13:26:41.801INFO 615 --- [nio-8080-exec-1] c.e.wechat.proxy.web.UserController: service class class com.sun.proxy.$Proxy85

2019-03-25 13:26:41.801INFO 615 --- [nio-8080-exec-1] c.e.wechat.proxy.web.UserController: service proxy class is instanceof UserServiceImpl:false

2019-03-25 13:26:41.812INFO 615 --- [nio-8080-exec-1] c.e.w.proxy.service.UserServiceImpl: class:class com.eappcat.wechat.proxy.service.UserServiceImpl

看到这时候已经是使用JDK动态代理来实现了。