Proxy代理模式之应用

           一,定义:  代理模式(Proxy:为其他对象提供一种代理以控制对这个对象的访问。

           二,其类图:



           三,分类一:静态代理

                 1,介绍:也就是需要我们为目标对象编写一个代理对象,在编译期就生成了这个代理对象,然后通过访问这个代理,来实现目标对象某些功能。

                 2,简单应用:在这里我们看一个简单的登录登出的例子:

        登录功能的接口:


		/**
		 * 公共接口,目标对象和代理都来实现
		 */
		public interface ILogin{
		        //登录
			void login();
			//登出
			void logout();
		}

     实现的目标接口:

		/**
		 * 目标对象,实现公共接口,达到登录登出的功能
		 */
		public class Reallogin implements ILogin{
			
			public void login(){
				try {
					Thread.sleep(3200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("登录系统.....");
			}
			
			public void logout(){
				try {
					Thread.sleep(2200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("退出系统....");
			}
		}

           大家看见了,上边的方法中我们加入了线程的睡眠,因为我想通过代理模式来测试登录登出的时间,因为JAVA程序我们遵循OCP(对扩展开放,对修改关闭)原则,所以为了不修改原来代码,我们来采用静态代理模式:

        Proxy(代理对象)的代码:

		/**
		 * 代理对象,代理目标对象Reallogin 
		 */
		public class ProxyLogin implements ILogin{
		
		         //此类中包含了目标对象
			private Reallogin target;
			
			//构造方法
			public ProxyLogin (Reallogin target){
				this.target = target;
			}
			
			@Override
			public void login() {
			         //开始时间
				long begin = System.currentTimeMillis();
				target.login();
				//结束时间
				long end = System.currentTimeMillis();
				System.out.println("耗费时长"+(end-begin)+"毫秒");
			}
		
			@Override
			public void logout() {
				long begin = System.currentTimeMillis();
				target.logout();
				long end = System.currentTimeMillis();
				System.out.println("耗费时长"+(end-begin)+"毫秒");
			}
		
		}



         好,通过代理模式,非常简单的实现了对登录登出时间的捕获,但是,假如客户突然要求我们对所有的类方法的时间进行捕获,那该怎么办呢?总不能每一个类,都写一个代理类,那样太麻烦了吧!怎么呢???


             3,分析:通过这里例子以及扩展我们来看一下静态代理模式的缺点吧:

                     a,如果出现上边的需求,那么势必会出现类爆炸的结果;

                     b,当然捕捉方法执行时间的代码都一样,我们每个方法都写,每个类都写,这也是代码的重复,没有达到代码复用的效果,这也完全违背了面向对象设计的原则。

         

            4,思考:防止出现类爆炸,使代码能够得到复用。我们能不能用一个代理类,来代理所有需要计算方法运行时间呢??? 看下边的动态代理模式。

       

          四,动态代理

             1,介绍:通过反射机制,利用JDK提供的Proxy类,在程序运行的时候在内存中根据目标对象来创建代理对象,避免了类爆炸的出现。代理方法只写一此,使代码得到了复用。


             2,解决上边的问题:

                   a,代理方法的编写:

		/**
		 * 此类需要实现InvocationHandler接口
		 * 调用处理器,当代理对象调用代理方法的时候,注册在调用处理器中的invoke方法会自动调用。
		 */
		public class TimerInvocationHandler implements InvocationHandler {
		    
			//目标对象,通过反射机制获得
			private Object target;
			//构造方法
			public TimerInvocationHandler(Object target){
				this.target = target;
			}
			
			/**
			 *  参数:
			 *  		Object proxy:代理对象的引用,proxy变量中保存代理对象的内存地址(这个参数很少用)
			 *  		Method method:目标对象的目标方法。
			 *  		Object[] args:目标对象的目标方法执行的时候所需要实参。
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
				//开始时间
				long begin = System.currentTimeMillis();
		
				//执行目标对象中的方法
				Object retValue = method.invoke(target, args);
				//结束时间
				long end = System.currentTimeMillis();
				//计算时间
				System.out.println("耗费时长"+(end-begin)+"毫秒");
				return retValue;
			}
		
		}



     b ,注意这里的测试程序的编写:

		/**
		 * 注意:JDK内置的动态代理Proxy只能代理接口
		 *(如果既想代理接口又想代理抽象类需要使用第三方组件:例如cglib)
		 */
		public class Test {
		
			public static void main(String[] args) {
				
				//创建目标对象
				ILogin target = new ProxyLogin();
				
				//创建代理对象:通过JDK内置的动态代理类java.lang.reflect.Proxy完成代理对象的动态创建
				//参数:
                                        ClassLoader loader;
		  			这里的类装载器主要是用来装载在内存中生成的那个临时的字节码,
		  			代理类的类装载器需要和目标类的类装载器一致。
		  
		  		Class[] interfaces;
		  			代理类和目标类必须实现“同一些”接口。(一个类可以同时实现多个接口)
		  
		  		InvocationHandler handler;
		  			当代理对象调用代理方法的时候,“注册”在调用处理器中的invoke方法会自动调用。
				ILogin proxy = (IUserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{IUserService.class},new TimerInvocationHandler(target)); 
				
				//通过执行代理对象的代理方法去执行目标对象的目标方法
				proxy.login();
				proxy.logout();
			}
			
		
		}

       

          3,动态代理模式相对来说比较难了解,因为它运用了反射机制。但是想象现实生活中,还是挺容易理解的,例如,工作中介,相当于代理模式中的代理对象,它可以为不同人找不同的工作,我们可以没有见过咱们生活中每个人都有一个工作中介代理对象吧。所以这里可以理解为功能代理对象,即为所有类代理可以实现同一种功能,例如上边的捕捉时间。

       

     五,动态模式解决Service层的JDBC代码,以及一些重复的代码:

            大家都直到Service层是用来写业务代码的,但是当出现事物时,我们需要在业务层进行事物的开启,提交,回滚,结束,这样就有了JDBC代码了,而且都是重复的,怎么办呢,我们可以为这些利用事物的业务层利用代理模式来解决这个问题。

看一下这个service层中的方法,里边有JDBC代码,而且每个Servlet都需要写,非常不满足规范:

			public boolean saveEnterprise(Enterprise en, List<EnInv> eninvs) throws Exception {
				Connection conn =null;
				int count=0 ;
				try {
		                                  //获取数据连接对象
					conn=DBUtil.getConnection();
					//事物的开始
					DBUtil.beginTransaction(conn);
					count=ienterpriserDao.InsertEnterpriseDao(en, eninvs);
					         //事物的提交
					DBUtil.commitTransaction(conn);
				} catch (Exception e) {
					try {
		                                              //事物的回滚
						DBUtil.rollbackTransaction(conn);
					} catch (SQLException e1) {
						e1.printStackTrace();
					}
					e.printStackTrace();
				}finally{
					try {
		                                             //事物的结束
						DBUtil.endTransaction(conn);
					} catch (SQLException e) {
						e.printStackTrace();
				}
					DBUtil.close(conn, null, null);
				}
				return count==(1+eninvs.size());

}

           通过Prox动态代理:

              代理方法的编写:

		public class TransactionInvcationHandler implements InvocationHandler {
			//目标对象的创建
			private Object target;
			
			//编写构造方法,
			public TransactionInvcationHandler(Object target){
				this.target=target;
			}
			@Override
			/**
			 * 利用事物的操作的调用事物处理
			 */
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				
				Object retValue=null;
				Connection conn=null;
				try{
					conn=DBUtil.getConnection();
					//开启事物
					DBUtil.beginTransaction(conn);
					
					//执行目标对象的方法
					retValue=method.invoke(target, args);
					
					//提交事物
					DBUtil.commitTransaction(conn);
				}catch(Exception e ){
					
					//回滚事物
					DBUtil.rollbackTransaction(conn);
					e.printStackTrace();
				}finally{
					//关闭事物
					DBUtil.endTransaction(conn);
					DBUtil.close(conn, null, null);
				}
				
				
				return retValue;
			}
		
		}

            这样这个Service就只需要写这两句话了:

		public boolean saveEnterprise(Enterprise en, List<EnInv> eninvs) throws Exception {
			int count=0 ;
				count=ienterpriserDao.InsertEnterpriseDao(en, eninvs);
			return count==(1+eninvs.size());

}

           当然Servlet的调用和上边的那个测试程序一样,我就不再写了。总而言之,动态代理模式模仿我们生活中的中介代理,使我们的程序代码达到了非常好的复用和分类清楚,非常实用。

       

          代理模式的其他应用:

              1,远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。

              2,虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。例如,网页中在图片出来以前现出来文字。

              3,安全代理,用来控制真实对象访问时的权限。

              4,智能代理,是指当调用真实的对象时,代理处理另外一些事。

 

            总而言之,这次的学习,有感觉软件和我们生活是息息相关的,善于发现生活的点点滴滴,从软件中联想生活会理解的更深,学习的更好!!!!



  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值