概念
- 代理(proxy)模式:为其他对象提供一种代理,以控制对这个对象的访问。从字面上理解就是代替别人(接口)做某件事;
- 为什么需要代理模式? 当某些接口需要部分调用者,在调用前后处理额外操作时(如:操作权限、事务等)代理模式是一种扩展能力非常强的,使用灵活的方式。
- 理解了代理模式,那么动态代理就不难理解了,所谓动态代理就是代理对象,可以通过调用者提供的参数,动态选择被代理对象。这种设计模式在AOP、RPC等应用中是必须的。
UML
如图中所示,Client可以直接调用RealSubject,也可以通过Proxy调用RealSubject,并且我们可以在Proxy中增加一些RealSubiject之外的操作,如访问权限等。
普通代理模式实现
在这里我们通过模拟文章保存后put到缓存,首先定义两个接口一个用来保存文章,一个来保存缓存、通过代理模式将其组合起来。这样做的好处是,我们可以选择用或不用缓存
- 文章实体
public class Article{
private Long id;
private String title;
private String content;
// getters/setters略
}
- 接口定义、实现
// 文章新增
public interface ArticleService {
String add(Article article);
}
public class ArticleServiceImpl implements ArticleService{
@Override
public String add(Article article){
System.out.println("保存文章" + article.getTitle());
}
}
// 缓存接口
public interface CacheService {
void put(Object obj);
}
public class CacheServiceImpl implements CacheService {
@Override
public void put(Object obj){
System.out.println("保存文章到缓存");
}
}
- 代理类实现
public class ArticleProxy implements ArticleService{
private CacheService cache;
private ArticleService articleService;
public ArticleProxy(ArticleService articleService, CacheService cache){
this.articleService = articleService;
this.cache= cache;
}
@Override
public String save(Article article){
super.save(article);
cache.put(article);
}
public static void main(String[] args) {
ArticleService articleService = new ArticleProxy(new ArticleServiceImpl(), new CacheServiceImpl());
Article article = new Article();
article.setTitle("hello proxy");
articleService.save(article);
}
}
JDK动态代理实现
Java动态代理需要实现java.lang.reflect.InvocationHandler接口来完成缓存,然后通过java.lang.reflect.Proxy.newProxyInstance(loader, interfaces, h)把被代理对象来完成,具体实现如下:
//改写ArticleProxy
public class ArticleProxy implements InvocationHandler{
private CacheService cache;
private Object targert;
public ArticleProxy(CacheService cache, Object targert){
this.cache = cache;
this.targert = targert;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = method.invoke(targert, args);
cache.put(obj);
return null;
}
//main方法调用
public static void main(String[] args) {
ArticleService articleService = (ArticleService) Proxy.newProxyInstance(ArticleService.class.getClassLoader(),
new Class[]{ArticleService.class}, new ArticleProxy(new CacheServiceImpl(), new ArticleServiceImpl()));
articleService.save();
}
}
通过上面代码修改,我们可以发现 ArticleProxy 不仅仅可以处理文章缓存,其他类型实体也一样可以使用,这就是我们为什么要使用动态代理了。
Cglib动态代理
JDK动态代理,有一个限制,只能支持interface的代理,不支持class代理,要打破这个限制就需要使用到Cglib框架,下面通过代码演示Cglib。
// 修改ArticleServiceImpl类,去除ArticleService接口
public class ArticleServiceImpl{
public String add(Article article){
System.out.println("保存文章" + article.getTitle());
}
}
// 修改ArticleProxy 类,实现InvocationHandler接口
public class ArticleProxy implements InvocationHandler{
private CacheService cache;
private Object targert;
public ArticleProxy(CacheService cache, Object targert){
this.cache = cache;
this.targert = targert;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = method.invoke(targert, args);
cache.put(obj);
return null;
}
public static void main(String[] args) {
ArticleService articleService = (ArticleService) Proxy.newProxyInstance(ArticleService.class.getClassLoader(),
new ArticleServiceImpl().getClass().getInterfaces(), new ArticleProxy(new CacheServiceImpl(), new ArticleServiceImpl()));
articleService.save();
}
}
Cglib下载地址:http://central.maven.org/maven2/cglib/cglib/2.2.2/cglib-2.2.2.jar
// maven :
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
总结
- 通过实现InvocationHandler接口创建调用处理器
- 通过Proxy.newProxyInstance(loader, interfaces, h)为指定的接口创建代理
- 上面我们所使用的是JDK动态代理,JDK代理机制只支持interface的代理,并不支持单纯class的代理;如果要实现class代理可以选择cglib来实现