黑马程序员-代理(高新技术)

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

OOP:面向对象的编程

AOP:面向方面的编程

 

Java虚拟机可以在运行期间动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类

Java虚拟机生成的动态类必须实现一个或多个接口(生成的动态类中的方法全部来自接口),所以,JVM生成的动态类只能用作具有相同接口的类的代理

如果一个目标类没有实现接口,那么也想生成代理,那么该如何做?

答:CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类动态生成动态代理类,那么可以使用CGLIB

 

代理类的各个方法中通常除了要调用目标相对应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码

1):在调用目标方法之前

2):在代用目标方法之后

3):在调用目标方法前后

4):在处理目标方法异常的catch块中(重点理解)

 

示例:

 

class X
{
     void sayHello()
     {
         System.out.println(“hello”);
     }
}

XProxy
{
     X x = null;
     XProxy(X x)//代理类的构造参数必须接收参数
     {
        this.x = x;
}

     void sayHello()
     {
         starttime;//获取开始时系统时间
         x.sayHello();
         endtime();//获取结束时系统时间
     }
}

上述就使用了代理类,为目标X类加上了一段系统功能,客户端加载运行的时候就调用XProxy类就可以了

 

动态创建代理类

Proxy类提供了一个方法:返回代理类的字节码对象,并向其提供类加载器和接口数组

static Class<?>getProxyClass(ClassLoader loader, Class<?>... interfaces)

每一个字节码都必须具有一个类加载器加载进内存,如果没有,则给指定一个

 

需求:使用java虚拟机动态生成代理类

列出代理类中的所有构造方法和参数类型

列出代理类中定义的所有方法和参数类型

 

代码示例:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;

public class ProxyTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//取得代理类的字节码对象,Proxy.getProxyClass()是一个静态方法,通过类名Proxy调用
		@SuppressWarnings("rawtypes")
		Class clazzProxy1 = 
Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//用JVM创建代理类需要传入一个类加载器,和一个接口
		//得到该代理类所属的类的名字
		System.out.println(clazzProxy1.getName());
		
		showConstructor(clazzProxy1);
		showMethods(clazzProxy1);
	}

	@SuppressWarnings("rawtypes")
	private static void showConstructor(Class clazzProxy1) {
		System.out.println("begin Constructors------------");
		//取得所有的构造方法,返回一个数组
		Constructor[] Constructor = clazzProxy1.getConstructors();
		//遍历数组,取出构造方法
		for(Constructor constructors:Constructor)
		{
			//得到该构造方法的名字
			String name = constructors.getName();
			//把构造方法的名字添加到StringBuilder
			StringBuilder sconstructors = new StringBuilder(name);
			sconstructors.append("(");
			//取得构造方法的参数列表,返回一个装有参数列表的数组
			Class[] clazzParams = constructors.getParameterTypes();
			//遍历该参数数组,取得参数
			for(Class clazzParam:clazzParams)
			{//参数的的名字添加到StringBuilder
				sconstructors.append(clazzParam.getName()).append(",");
			}
			if(clazzParams!=null&&clazzParams.length != 0)
			{
				//去掉最后一个“,”
				sconstructors.deleteCharAt(sconstructors.length()-1);
			}
			sconstructors.append(")");
			System.out.println(sconstructors.toString());
		}
	}
	
	@SuppressWarnings({ "rawtypes", "rawtypes", "rawtypes" })
	private static void showMethods(Class clazzProxy1) {
		System.out.println("begin Methods------------");
		//取得所有的构造方法,返回一个数组
		Method[] method = clazzProxy1.getMethods();
		//遍历数组,取出构造方法
		for(Method methods:method)
		{
			//得到该构造方法的名字
			String name = methods.getName();
			//System.out.println(name);
			//把构造方法的名字添加到StringBuilder
			StringBuilder sMethods = new StringBuilder(name);
			sMethods.append("(");
			//取得构造方法的参数列表,返回一个装有参数列表的数组
			Class[] clazzParams = methods.getParameterTypes();
			//遍历该参数数组,取得参数
			for(Class clazzParam:clazzParams)
			{//参数的的名字添加到StringBuilder
				sMethods.append(clazzParam.getName()).append(",");
			}
			if(clazzParams!=null&&clazzParams.length != 0)
			{
				//去掉最后一个“,”
				sMethods.deleteCharAt(sMethods.length()-1);
			}
			sMethods.append(")");
			System.out.println(sMethods.toString());
			
		}
	}
}


 

输出结果:

com.sun.proxy.$Proxy0

beginConstructors------------

com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler,)

begin Methods------------

add(java.lang.Object)

remove(java.lang.Object)

equals(java.lang.Object)

toString()

显示一部分结果

 

JVM创建动态代理类的要点

1):Proxy.getProxyClass()方法是一个通过类名调用的静态方法,能够返回一个代理类的字节码对象,这里需要传入两个参数,以为在内存中动态创建字节码,需要让该字节码具有一个类加载器,这是java规定的,每个字节码都是由一个加载器加载器加载进来的,如果没有,就指定一个,除此之外,还要传入一个接口类型,告诉虚拟机这个代理类实现了哪个接口

 

创建动态类的实例对象

注意事项:

1)不能直接通过classProxy1.newStance()创建对象,因为这种创建对象的方式会调用类的无参构造方法,因为代理类不存在无参的构造方法,所以编译会报错(传入的参数是为了关联目标类,和指定目标类的方法和该方法接受的参数)

必须先得到构造方法,再指定参数调用构造方法:

Constructor con = clazzProxy1.getConstroctor(InvocationHandle.class

附:InvocationHandle.class是参数的类型,但是InvocationHandle是一个接口

如果要传入InvocationHandle对象参数去创建代理类的实例对象,那么必须要先创建一个类实现InvocationHandle接口并实现里面的方法(可以用最简单的实现,空的方法体)

class invo implements InvocationHandle

{

Invoke(Object proxy,Method method,Object[]args)

{}//最简单的实现是空方法体

}

Invoke(Object proxy,Method method,Object[] args)参数的意义

Object proxy:表示调用的是哪个代理对象

Method method:表示哪个方法时要的是代理对象的哪个方法

Object[] args:表示的是调用要传入的对象,有可能是多个对象,用数组接收

 

但是也可以直接以InvocationHandle的匿名内部类形式传入参数

Constructor con = clazzProxy1.getConstroctor(new InvocationHandle()
{
     Invoke(Object proxy,Method method,Object[] args)
     {
          return null;
     }
})

上述方法创建对象是分开两步来写的,此外,java还提供了一个一步到位,创建代理对象的方法,使用一个Proxy的静态方法(传入参数类加载器,实现的接口,InvocationHandler

newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h);

示例:

Collection proxy = 
(Collection)Proxy. newProxyInstance(Collection.getClassLoader(), 
new Class[]{Collection},//可能是有多个接口,用数组
new InvocationHandle()
{
Invoke(Object proxy,Method method,Object[] args)
{
return null;
}
})

分析动态生成类的内部代码

Proxy implements Collection
{
    InvocationHandle handler
public Proxy(InvocationHandle handler)
{
        this. Handler = handler; 
}
}

Proxy.add(“abc”);对应的是要代理的目标对象,add方法,往方法里所传参数
Class Proxy implements Collection
{
    InvocationHandle handler
public Proxy(InvocationHandle handler)
{
        this. handler = handler; 
}
add(Object obj)
{
    return handler.invoke(Object proxy,Method method,Object[] args);
}
}  





 

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值