代理模式,JDK/Cglib动态代理

原创
2017/05/14 15:14
阅读数 276

概念

  • 代理(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>

总结

  1. 通过实现InvocationHandler接口创建调用处理器
  2. 通过Proxy.newProxyInstance(loader, interfaces, h)为指定的接口创建代理
  3. 上面我们所使用的是JDK动态代理,JDK代理机制只支持interface的代理,并不支持单纯class的代理;如果要实现class代理可以选择cglib来实现
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
2 收藏
1
分享
返回顶部
顶部