动态代理

动态代理:


AOP(面向切面编程):

系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

                                    安全      事务         日志

StudentService  ------|----------|------------|-------------

 

CourseService   ------|----------|------------|-------------

 

MiscService     ------|----------|------------|-------------

用具体的程序代码描述交叉业务:

method1         method2          method3

{                        {                           {

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

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

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

}                          }                           }


交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。

可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

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

func1         func2            func3

{                   {                     {

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

}                    }                     }

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

动态代理技术:
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,
 将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,
 所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,
 还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中。



使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

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

接口的类生成动态代理类,那么可以使用CGLIB库。

 创建动态类的实例对象过程:

1、用反射获得构造方法

2、编写一个最简单的InvocationHandler类

3、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

4、打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

5、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼习惯匿名内部类

动态代理类:

public class ProxyTest
{/*
	让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
	1.生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
	2.产生的类字节码必须有个一个关联的类加载器对象;
	3.生成的类中的方法的代码是怎样的,也得由我们提供。
	  把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,
	  它调用我的方法,即相当于插入了我的代码。
	  提供执行代码的对象就是那个InvocationHandler对象,
	  它是在创建动态类的实例对象的构造方法时传递进去的。
	  在上面的InvocationHandler对象的invoke方法中加一点代码,
	  就可以看到这些代码被调用运行了。*/
	public static void main(String[] args) throws Exception
	{	
		//static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 
        // 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。 
		Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		//获取Proxy的构造方法
		Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
		//构造方法:protected  Proxy(InvocationHandler h) //InvocationHandler是接口
        //使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
		//调用构造方法创建对象,构造方法里的指定参数,即InvocationHandler的子类。
		class MyInvocationHander1 implements InvocationHandler
		{
			public Object invoke(Object proxy, Method method, Object[] args)throws Throwable 
			{
				return null;
			}	
		}//因为是一次性使用,其实可以一次性定义匿名内部类。
		Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
		//打印Sop(proxy1)结果为null,有可能是对象创建失败,或者toString()方法,返回为null
		//这个里面是后者toString()方法,返回为null
		proxy1.clear();//可以清除,即对象创建成功。因为没有返回值。即不报错
		//调用调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,
	    //代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
		//proxy1.size();//此方法有返回值。但调用失败,为什么?
		//因为size();方法调用了invoke();invoke();返回了个unll,但size()方法需要int,所以报错。

		
		
		//下面是一次性创建对象。替换上面,用到方法如下:
		//static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
        // 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 
		Collection proxy2 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), 
					new Class[](Collection.class),
				new InvocationHandler()//匿名内部类
				{
					/*调用动态代理对象的时候其实都是调用的invoke方法
					Object invoke(Object proxy, Method method, Object[] args) 
					 在代理实例上处理方法调用并返回结果。*/
					 /*里面没有定义东西
					public Object invoke(Object proxy, Method method, Object[] args)throws Throwable 
					{
						return null;
					}
					*/
					//ArrayList target =new ArrayList();
					public Object invoke(Object proxy, Method method, Object[] args)throws Throwable 
					{
						ArrayList target =new ArrayList();
						long beginTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();
						System.out.println(method.getName() + " running time of " + (endTime - beginTime));
						return retVal;
					}
				}
			);
		//外部调用动态代理对象方法:
		proxy2.add("zxx");
		proxy2.add("lhm");
		proxy2.add("bxd");
		proxy2.size();//当ArrayList在InvocationHandler子类对象的成员上的时候打印为3,在invoke方法里是答应为0
		
	}
}


动态代理运行原理图:


优化代码(将其提取成方法,不理解就先看上面代码):

import java.lang.reflect.Constructor;
public class ProxyTest1
{
	public static void main(String[] args) throws Exception
	{	
		final ArrayList target = new ArrayList();//创建需要传递的对象			
		Collection proxy3 = (Collection)getProxy(target,new MyAdvice());//创建需要传递的系统功能对象
		proxy3.add("zxx");
		proxy3.add("lhm");
		proxy3.add("bxd");

	//传递两个对象,做成框架,加final是为了方法里面能访问到,一个是正常的类对象,一个是需要添加的功能
		private static Object getProxy(final Object target,final Advice advice) {
			Object proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				/*new Class[]{Collection.class},*/
				target.getClass().getInterfaces(),
				new InvocationHandler(){
					public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
					{				
						/*这部分抽取成:要实现的功能,比如日志,异常。
						long beginTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();*/
						//System.out.println(method.getName() + " running time of " + (endTime - beginTime));
						//return retVal;
						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethod(method);
						return retVal;											
					}
				}
			);
		return proxy3;
		}
	}
}
import java.lang.reflect.Method;
public interface Advice //定义一个功能接口。
{
	void beforeMethod(Method method);
	void afterMethod(Method method);
}
import java.lang.reflect.Method;
public class MyAdvice implements Advice//实现此功能的类。需要传递的参数
{
	long beginTime = 0;
	public void afterMethod(Method method)
	{
		System.out.println("从黑马毕业上班啦!");		
		long endTime = System.currentTimeMillis();
		System.out.println(method.getName() + " running time of " + (endTime - beginTime));
	}
	public void beforeMethod(Method method)
	{
		System.out.println("到黑马来学习啦!");
		beginTime = System.currentTimeMillis();
	}
}

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

//注意:Collection程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数,
	//即对应invoke(Object proxy, Method method, Object[] args)
Class Proxy$
{
	add(Object object) 
		{
			return handler.invoke(Object proxy, Method method, Object[] args);
		}
}	

实现类似于Spring的可配置的AOP框架:(和配置文件挂钩)
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。
 其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,
 则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
 #xxx=java.util.ArrayList
 xxx=cn.itcast.ProxyFactoryBean
 xxx.target=java.util.ArrayList
 xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory //判断是否生成代理类
{
	Properties props = new Properties();
	public BeanFactory(InputStream ips)
	{
		try {
			props.load(ips);
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}	
	public Object getBean(String name)
		{
		String className = props.getProperty(name);
		Object bean = null;
		try 
		{
			Class clazz = Class.forName(className);
			bean = clazz.newInstance();
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		} 
		if(bean instanceof ProxyFactoryBean)
		{
			Object proxy = null;
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
			try 
			{
				Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
				Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				proxy = proxyFactoryBean.getProxy();//调用生成代理的方法
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
			}
			return proxy;
		}
		return bean;
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean //代理类生成器,里面定义了生成代理的方法。
{
	private Advice advice;//定义需要传递的两个对象,Bean属性
	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 proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				/*new Class[]{Collection.class},*/
				target.getClass().getInterfaces(),
				new InvocationHandler(){
				
					public Object invoke(Object proxy, Method method, Object[] args)throws Throwable 
					{
						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethod(method);
						return retVal;							
					}
				}
				);
		return proxy3;
	}
}
//下面是需要用到的实例。
import java.lang.reflect.Method
public interface Advice //定义一个功能接口。
{
	void beforeMethod(Method method);
	void afterMethod(Method method);
}
import java.lang.reflect.Method;
public class MyAdvice implements Advice//实现此功能的类。需要传递的参数
{
	long beginTime = 0;
	public void afterMethod(Method method)
	{
		System.out.println("从黑马毕业上班啦!");		
		long endTime = System.currentTimeMillis();
		System.out.println(method.getName() + " running time of " + (endTime - beginTime));
	}
	public void beforeMethod(Method method)
	{
		System.out.println("到黑马来学习啦!");
		beginTime = System.currentTimeMillis();
	}
}
//(String即都是在配置文件里配置)还要定义配置文件config.properties:传入需要转换的类
	#xxx=java.util.ArrayList//#表示屏蔽此对象,就是不传入这个
	xxx=路径.ProxyFactoryBean
	xxx.target=java.util.ArrayList
	xxx.advice=路径.MyAdvice
//下面是测试、
import java.io.InputStream;
import java.util.Collection;
public class AopFrameworkTest
{
	public static void main(String[] args) throws Exception
	{
		InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
		Object bean = new BeanFactory(ips).getBean("xxx");
		System.out.println(bean.getClass().getName());
		((Collection)bean).clear();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值