黑马程序员--高新技术之动态代理

                                ------- android培训java培训、期待与您交流! ----------

一、关于动态代理的一些基本概念
    在编程中,很多时候我们要为已存在的多个具有相同接口的目标类的各个
    方法增加一些系统功能。例如,异常处理、日志、计算方法的运行时间、事物管理、等等,
    针对这些问题,我们可以编写一个与目标类具有相同接口的代理类,代理类的每个方法调用
    目标类的相同方法,并在调用方法时加上系统的代码。
    代理模式也是常用的一种java设计模式,代理类的对象本身并不是真正实现服务
    而是通过调用委托类的对象的相关方法,来提供服务。
二、代理类的实现
     静态代理:自己手动创建,编写源码,特点:在程序运行前,代理类的.class文件就已经存在了


     动态代理:因为JVM可以在运行期动态生成出类的字节码,所以可以利用jdk的api动态创建代理类
特点:在运行时利用反射机制动态创建代理类。
三、代码实现

静态代理:(在这里我模拟的是向数据库中添加用户的情形,就只能想到这样的例子了)


package cn.yusheng.proxy;  
	/*
	 * 定义类实现接口,这是要被代理的类
	 */
	public class UserImpl implements User {
		//添加用户到数据库
	  public void addUser(){
		  System.out.println("添加用户的方法---"); 
	  }
	} 


	package cn.yusheng.proxy;  
	/*
	 * 定义代理类,实现与被代理类相同的接口,在此处模拟的是
	 * 向数据库中添加用户时进行的事物
	 */
	public class UserProxy implements User{
		//定义一个被代理类的一个实例
		UserImpl userImpl = new UserImpl();
		//添加用户到数据库
	  public void addUser(){
		  System.out.println("事物处理前---"); 
		  //被代理类应该被执行的方法
		  userImpl.addUser();
		  System.out.println("事物处理之后");
	  }
	} 


以上就是静态代理的简单实现,但是要为系统中的各种接口的类增加代理功能,
那将需要太多的代理类,全部采用静态代理方式,正如张老师所说的将是一件非常麻烦的事情。
所以还是利用动态创建代理类的方式比较好。
ps:
   代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中

动态代理:

     JDK中对动态代理中提供了InvocationHandler接口和Proxy类,
     两个非常重要的方法:
     public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
   } 
   参数说明:
   Object proxy:指被代理的对象。
   Method method:要调用的方法
   Object[] args[]:方法调用时所需要的参数
   这个方法存在于InvocationHandler接口中,在实际运用中经常采用此接口,利用匿名内部类的
   方法实现。
      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) throws IllegalArgumentException 


这个方法存在于Proxy类中,用来动态创建代理类的关键方法。
参数介绍:
ClassLoader loder:类加载器,一般情况下是目标类的(要被代理的类)类加载器
Class<?>[] interfaces:目标类的全部接口
InvocationHandler h:得到InvocationHandler接口的子类实例 (最好用匿名类的方式)




ps:在此处介绍下java中想要向一个方法中添加一段代码时的方法:
在js,我们可以用eval(),小括号里面就是要添加的要执行的js代码,
但在java中却没有这样的方法,但是我们可以采用封装代码到对象的形式。
这里举一个简单的例子

package cn.yusheng.proxy; 
	public class TestHello{
		public void printHello(Hello hello){
			hello.print();
		}
		public static void main(String[] args) {  
			//在这里通过将打印“helloworld”的代码封装到Hello对象
			//作为参数传递给printHello()方法
			printHello(new Hello())
		}
	}

	class Hello{
		System.out.println("helloword");
	}

动态代理对这点的应用,就是将要异常处理、日志、计算方法的运行时间、事物管理,这些代码
的方式,封装到对象中特定的方法,在以参数的形式传递给InvocationHandler()方法中所要求的
参数,Class<?>[] interfaces,中。

代码实现:

package com.itheima;
/*
 * 定义要代理类的要添加的代码,有异常处理、
 * 日志、计算方法的运行时间、事物管理
 */
public interface Advice {
	void beforeMethod();
	
	void afterMethod();
}


package cn.itheima.proxy;
/*
 * 要添加的具体的代码的实现
 */
public class MyAdvice implements Advice {
	@Override
	public void beforeMethod() {
		System.out.println("method before");

	}
	@Override
	public void afterMethod() {
		System.out.println("method after");
	}

}

package cn.itheima.proxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class InvocationTest {

	public static void main(String[] args) {
		//创建想要代理的类的对像即target
		List target = new ArrayList();
		Collection proxy1 = (Collection) getProxy(target,new MyAdvice());
		
		//测试动态生成的代理类
		System.out.println(proxy1.getClass());
		proxy1.add("111");
		System.out.println(proxy1.size());
	}
	
	public static Object getProxy(final Object target,final Advice advice) {
		//参数声明为final类型,因为后面的代码要用到
		Object proxy1 =  Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				//这里接收的是数组形式的参数(new Class[]{Collection.class})
				target.getClass().getInterfaces(),
				new InvocationHandler() {
					//List target = new ArrayList();
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						advice.beforeMethod();
						//执行方法,
						Object retVal = method.invoke(target, args);
						advice.afterMethod();
						return retVal;
					}
				}
				
			);
		return proxy1;
	}
}


总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
产生的类字节码必须有个一个关联的类加载器对象;
生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,
把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,
它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,
就可以看到这些代码被调用运行了。

ps:终于写完了第一篇博客,花了整整三个小时啊!刚开始写的时候真是无从下手啊,没写过博客,怕写的不好,
就去看大牛的博客,发现大家特别喜欢用例子来说明知识,就闷头想怎么用一个比较好的例子来说明动态代理的
实在应用,哎,总之花了不少功夫,不过总归是写完了,挺高兴的。


自己对写博客的理解:不要想得太多,就是把自己现阶段看的某些知识点概括下来,对于某些知识理解的不清楚的,
可以去网上查资料,然后就是看老师的ppt总结博客,嘿嘿,就这些了,第一篇博客完工!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值