java学习之代理

一.代理的概念与作用

在Java中利用代理(Proxy)可以在运行时创建一个实现了一组给定接口的新类。在系统程序设计中,有时需要面对无法确定接口,却需要构造对象的情况。以前为了解决此问题,有些程序根据动态确定的接口,生成Java类文件,然后调用类 加载器构造该对象,然后使用,这样一来无可避免性能问题。通过代理类,能够在不额外创建Java文件的情况下构造对象及调用该对象方法。

       代理模式一般涉及到的角色有: 

抽象角色:声明真实对象和代理对象的共同接口; 

代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

      代理的架构图:


动态代理的主要作用就是解决aop编程,交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

------------------------------------------------------切面

func1         func2            func3

{             {                {

....            ....              ......

}             }                }

------------------------------------------------------切面

JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

1.在调用目标方法之前;

2.在调用目标方法之后;

3.在调用目标方法前后;

4.在处理目标方法异常的catch块中。

 

二.应用动态代理技术

java中动态代理机制的引入使得代理模式的思想更加完善与进步,它允许动态的创建代理并支持对动态的对所代理的方法进行调用。Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类: 

(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Methodmethod, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。 

(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容: Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。 Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。 

Static ObjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

      实例:创建实现了Collection接口的动态类和查看其名称,列出其所有的方法名称,并创建动态类的实例对象。

package cn.pengpan.day2;

import java.lang.reflect.Constructor;
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.HashMap;
import java.util.LinkedList;

import com.sun.istack.internal.FinalArrayList;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xalan.internal.xsltc.runtime.Parameter;

/*
 * 需求:利用动态代理和反射获取collections接口下的动态类的各种方法和构造方法,要求将参数
 * 类型打印出来,并创建出动态类的实例对象
 * 
 * 
 * */
public class ProxyTest {
	public static void main(String[] args)throws Exception {
		System.out.println("constructor list begin");
		
			Class clazz=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//得到实现接口collection的动态代理类字节码
			Constructor[] constructors=clazz.getConstructors();
			for (Constructor constructor:constructors) {
				StringBuilder sBuilder=new StringBuilder(constructor.getName());/StringBuilder与StringBuffer基本用法一致,StringBuffer往往用在多线程,而StringBuilder往往用在单线程
				Class[] paraClasses=constructor.getParameterTypes();//把每个构造器的参数类型装入一个数组
				sBuilder.append('(');
				for(Class paraClass:paraClasses)
				{
					String paraName=paraClass.getName();
					sBuilder.append(paraName);
					sBuilder.append(',');
				}
				if (paraClasses!=null && paraClasses.length !=0) {
					sBuilder.deleteCharAt(sBuilder.length()-1);//如果得到的数组中为Null或者数组长度是零,则把最后的逗号去掉
				}
				sBuilder.append(')');
				System.out.println(sBuilder.toString());
			}
		System.out.println("constructor list over-----------");
		System.out.println("Method list begin----------------");
		Method[] methods=clazz.getMethods();
		for(Method method:methods)
		{
			StringBuilder sb=new StringBuilder(method.getName());
			sb.append('(');
			Class[] parameters=method.getParameterTypes();
			for(Class parameter:parameters)
			{
				sb.append(parameter.getName()).append(',');
				
			}
			if (parameters!=null&¶meters.length!=0)
			{
				sb.deleteCharAt(sb.length()-1);
			}
			sb.append(')');
			System.out.println(sb.toString());
		}
		System.out.println("Method list over");
		System.out.println("creat object begin");
	Constructor constructor=clazz.getConstructor(InvocationHandler.class);
	Collection collection=(Collection)constructor.newInstance(new InvocationHandler(){
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			System.out.println(method.getName()+"运行了");
			System.out.println(proxy.getClass().getName());
			// TODO Auto-generated method stub
			return null;
		}}
	);
	//collection.size();//调用了代理类的size()方法,因为有返回值,会报空指针异常
	collection.clear();
	System.out.println(collection.toString());
	System.out.println(collection.getClass().getName());
	System.out.println();
	System.out.println();
	/*用Proxy本身的方法newProxyInstance方法实现获取代理对象,并在指定的调用处理程序中加入自己要实现的代码*/
	Collection collection2=(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), 
			new Class[]{Collection.class}, 
			new InvocationHandler() {//invocationhandler是代理实例对象的调用处理程序要实现的接口
				ArrayList arr=new ArrayList();
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					long begintime=System.currentTimeMillis();
					Object object=method.invoke(arr, args);//arr代表了目标类的方法,用反射确保了代理类能够在调用处理程序中执行目标类的代码
					long endtime=System.currentTimeMillis();
					System.out.println(method.getName()+"运行了"+(endtime-begintime));
					System.out.println(object);
					// TODO Auto-generated method stub
					return object;//collection的add方法是有返回值的,所以应当返回一个相同类型的值
				}
			});
	collection2.add("jfdfjds");//代理类对象每执行一次方法,都会调用invocationhandler的子类对象的Invoke方法,所以实现代理功能需要的额外功能代码都在其中
	collection2.add("dfsdfsdf");
	
	/*把代理功能封装成为函数,使之能代理任何类*/
	final LinkedList list=new LinkedList();
	Collection list2=(Collection)getProxy(list,new myAdvice());
	System.out.println(list2.getClass().getName());
	System.out.println("-------------");
	System.out.println();
	Class[] clzz=list2.getClass().getInterfaces();//看看代理类都实现了哪些接口
	for(Class class1:clzz)
	{
		System.out.println(class1.getName());
	}
	list2.add("dfsdfdf");
	}
	public static Object getProxy(final Object target,final Advice myadvice) throws IllegalArgumentException {
		Object collection3=Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						/*long begintime=System.currentTimeMillis();
						Object object=method.invoke(target, args);//arr代表了目标类的方法,用反射确保了代理类能够在调用处理程序中执行目标类的代码
						long endtime=System.currentTimeMillis();
						System.out.println(method.getName()+"运行了"+(endtime-begintime));
						System.out.println(object);
						// TODO Auto-generated method stub
						return object;//collection的add方法是有返回值的,所以应当返回一个相同类型的值
*/		
						myadvice.beforeMethod(method);
						Object object=method.invoke(target, args);
						myadvice.afterMethod(method);
						return object;
						}
				});
		return collection3;
	}
}

总结:建立动态代理类的实例对象的注意事项:

 

1.        生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;

2.        产生的类字节码必须有个一个关联的类加载器对象;

3.        生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口(invocationhandler)的对象(实现invocationhandler的子类实例对象)的方法(invoke())中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

 

三.编写Aop框架

package cn.pengpan.day2.aop;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Proxy;
import java.util.Properties;

import cn.pengpan.day2.Advice;

/*
 * 需求:设计一个AOP框架,使之可以实现读取配置文件,并根据配置文件中的参数字符串生成指定类的代理
 * 思路:此框架要有三个不同的类来实现,BeanFactory用来读取配置文件,判断配置文件中的参数所代表的 对象是否需要生成代理,若是AopBean的对象则生成代理,否则直接返回对象;AopBean是一个javaBean,用来生成代理类对象,并设置目标和通告;AopTest则用来执行客户端的程序,测试程序的功能*/

public class BeanFactory {
	Properties pros=new Properties();
	public BeanFactory(InputStream ins) {//利用构造函数传入读取流对象,用Properties加载
		// TODO Auto-generated constructor stub
		try {
			pros.load(ins);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public Object getBean(){
		Object object=null;
		String nameString=pros.getProperty("xxx");//得到配置参数
		//System.out.println(nameString);
		try {
			Class clazz=Class.forName(nameString);
			object=clazz.newInstance();//得到配置参数所代表的对象
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		if(object instanceof AopBean)
		{
			AopBean aopbean=(AopBean)object;
			Object proxyObject=null;
			String adviceString=pros.getProperty("xxx.advice");
			String targetString=pros.getProperty("xxx.target");
			System.out.println(targetString);
			System.out.println(adviceString);
			try {
				aopbean.setAdvice((Advice)Class.forName(adviceString).newInstance());
				Class clazz3=Class.forName(targetString);
				aopbean.setTarget(clazz3.newInstance());
				proxyObject=aopbean.getProxy();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return proxyObject;
		}
		return object;	
	}
}
package cn.pengpan.day2.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import cn.pengpan.day2.Advice;
import cn.pengpan.day2.myAdvice;

public class AopBean {
	private Advice advice;
	private Object target;
	public Advice getAdvice() {
		return advice;
	}
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
	public Object getTarget() {
		return target;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
	public Object getProxy(){   //此方法不可设置成静态,否则无法访问局部变量
		Object object=Proxy.newProxyInstance(target.getClass().getClassLoader(),//得到代理对象
				target.getClass().getInterfaces(),
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						advice.beforeMethod(method);
						Object object1=method.invoke(target,args);
						advice.afterMethod(method);
						return object1;
					}
				});
		return object;
	}
}
package cn.pengpan.day2.aop;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

import cn.pengpan.day2.ProTest;

public class AopTest  {
	public static void main(String[] args)throws Exception {
		InputStream ips=AopTest.class.getResourceAsStream("config.properties");
		BeanFactory bFactory=new BeanFactory(ips);
		System.out.println(bFactory.getClass().getName());
		/*Collection beanObject=(Collection)bFactory.getBean();
		beanObject.size();
		beanObject.add("df");*/
		Object pro=bFactory.getBean();
		Method[] methods=pro.getClass().getMethods();
		for (Method method:methods) {
			System.out.println(method.getName());
		}
		ProTest proTest=(ProTest)pro;//要用接口的类型获取代理对象
		proTest.sayHello();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值