代理模式

一、定义

    代理模式是一种很常见的设计模式。它使用代理对象完成用户请求,屏蔽用户对真是对象的访问。就如同现实生活中的代理一样,代理人被授权执行当事人的一些事宜,而无需当事人亲自出面,从第三方的角度看,似乎当事人并不存在,因为第三方只和代理人直接通信。

二、分类

    (1)静态代理:

                由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

    (2)动态代理:

                在程序运行时,运用反射机制动态创建代理类。

三、目的

    在软件设计中,使用代理的意图很多,主要有三个:

    (1)安全原因:屏蔽第三方直接与真是对象进行通信。

    (2)RMI中:需要使用代理类处理远程方法调用的技术细节。

    (3)提升系统性能,延迟加载。

      这里重点讲一下延迟加载是怎么体现的。假设一个客户端软件,有根据用户请求进行数据库查询的功能。那么在查询数据之前,必须得获得数据库连接。那么这个获取数据库连接的操作应该放在什么时候进行呢?假如放在软甲开启时,就去获取数据库的连接,有些人可能会说获取一个连接而已,并不是什么耗时的操作,没有影响。但是,软件并不是这么简单的,它有大量类似的初始化操作,比如xml的解析等等,单独一个数据库连接不耗时,但是大量的这种操作呢,如果都放在软件启动时,那么势必会影响软件的性能和启动速度。因此,我们引入了代理模式,使用代理对当事人进行封装,当系统启动时,仅仅初始化代理类,并不进行数据库连接等初始化操作,仅当进行真正的数据库查询操作时,才会去获取数据库连接。
    在系统启动时,将消耗资源最多的方法都使用代理模式分离,就可以加快系统的启动速度,减少用户的等待时间。而在用户真正做查询操作时,再由代理类,单独去加载真实的数据库查询类,完成用户请求。这里说的可能比较抽象,下面会举例说明。

四、四个角色

    (1)主题接口:定义代理类和真实主题的公共接口

    (2)真实主题:真正实现业务逻辑的类

    (3)代理类:用来代理和封装真实主题

    (4)Main:客户端,第三方,与代理类进行直接通信,完成一些工作。

五、实现

    (1)静态代理:

/*
 * 静态代理
 * 每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理
 */
public class LearnProxy0 {
	public static void main(String[] args) {
		IDBQuery dbQuery=new DBQueryProxy();
		dbQuery.select();

	}
}
interface IDBQuery{
	public void select();
}
class DBQuery implements IDBQuery{
	public  DBQuery() {
		try {
			//可能包含数据库连接等耗时操作,这里仅仅是模拟
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Override
	public void select() {
		// TODO Auto-generated method stub
		System.out.println("进行数据库查询操作...");
	}
}
class DBQueryProxy implements IDBQuery{
	//初始值设置为空,即在软件启动时,不进行初始化操作
	private IDBQuery dbQuery=null;  

	@Override
	public void select() {
		// TODO Auto-generated method stub
		//只有在真正需要的时候,才创建真正的对象
		if (dbQuery==null) {
			dbQuery=new DBQuery();
		}
		System.out.println("查询操作前...");
		dbQuery.select();
		System.out.println("查询操作后...");
	}
}


    (2)JDK动态代理:

/*
 * JDK的动态代理
 */
public class LearnProxy1 {

	public static void main(String[] args) {
		DynamicProxy proxy=new DynamicProxy();
		Sport sportProxy=(Sport)proxy.bind(new Basketball());
		sportProxy.play();
	}
}

interface Sport{
	public void play();
}

class Basketball implements Sport{

	public void play() {
		System.out.println("i am playing basketball");
	}
	
}
class DynamicProxy implements InvocationHandler{
	//要代理的接口,因此动态代理可以代理多个不同接口
	private Object target;
	public Object bind(Object target){  //设置目标代理对象,并且返回代理
		this.target=target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), //注意第二个参数是接口的列表,代理实例实现了所有接口
				target.getClass().getInterfaces(), this);        //因此,我们可以像使用接口的方式一样使用代理来调用方法
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("方法执行前");
		method.invoke(target, args);    //通过反射机制调用真实主题的目标方法
		System.out.println("方法执行后");
		return null;
	}
}


    (3)cglib动态代理:

/*
 * JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了
 * 
 * cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的
 * 是继承,所以不能对final修饰的类进行代理
 */
public class LearnProxy2 implements MethodInterceptor{
	//
	private Object target;
	//设置被代理的对象,并且返回一个动态代理
	public Object bind(Object target){
		this.target=target;
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		enhancer.setCallback((Callback) this);	
		return enhancer.create();
	}


	@Override
	public Object intercept(Object target, Method method, Object[] arg2,
			MethodProxy proxy) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("方法执行前...");
		proxy.invokeSuper(target, arg2);
		System.out.println("方法执行后");
		return null;
	}
	public static void main(String[] args) {
		LearnProxy2 proxy=new LearnProxy2();
		Actor actorProxy=(Actor)proxy.bind(new Actor());
		actorProxy.play();
	}
}

class Actor{
	public void play() {
		System.out.println("表演....");
	}
}


    使用JDK创建代理有一个限制,即他只能为接口创建代理实例。但是现实情况是,有的类并没有实现接口(现实中存在一些并没有使用接口的项目),但是它也需要动态代理,那怎么办呢?这个时候就需要使用CGLIB啦。

    CGLIB采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并且顺势织入横切逻辑。

    另外,CGLIB创建的动态代理的性能要比JDK创建的代理的性能好很多。但是,CGLIB创建代理对象花费的时间要比JDK多。因此,我们建议,对于singleton的代理对象,因为不需要频繁创建,最好使用CGLIB技术创建代理,反之,适合使用JDK代理技术。还要注意的是,由于CGLIB采用动态创建子类的方式生成代理对象,所以不能对目标类中的final和private方法进行代理。

    下面篇,我会讲解一下动态代理在AOP中的应用。

六、上述实现方法存在的问题

(1)我们为所有的方法都添加了横切逻辑,但是,有时候这并不是我们想要的,我们只希望对业务类的某几个特定方法加上横切逻辑。
(2)我们通过硬编码的方法指定了织入横切逻辑的织入点,即在业务方法的执行前和执行后织入了代码,不具有灵活性。
    这几个问题都会在Spring AOP中得到解决。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值