【黑马程序员】java中的--------------代理类与动态代理技术

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

一、代理的理解:

1、生活中的代理;就好比我们买电脑,我们不必亲自去厂家购买,只有去代理商店去买就可以了,既方便,而且还可以享受代理商店的一些特有的服务。比如:厂家,代理商,客户这三者这间的关系客户买产品并不直接与生产商打交道,也不用知道产品是如何产生的,客户只与中间商打交道,而中间商就可以对产品进行一些包装,提供一些售后的服务。


2、程序中的代理:编写一个与目标类具有相同的接口的代理类,代理类的每个方法调用目标类的相同的方法,并在调用方法是加是系统功能代码。


3、如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置目标类或者代理类,这样可以很容易切换

比如:想要日志功能是就配置代理类 ,不想要时 就配置目标类,这,增加系统功能很容易,同时去掉系统的功能也很容易

4、代理模式结构图:


二.  AOP应用

1.      简述:AOPAspectOriented Program)即面向方面的编程。

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

                                               安全      事务         日志

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

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

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

安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务。

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

A.     交叉业务的代码实现

                method1         method2          method3

                {                     {                     {

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

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

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

                }                     }                      }

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

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

                func1         func2            func3

                {                {                   {

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

                }                }                    }

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

因此使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术,只要是用到面向方面的编程,就涉及到代理

三、动态代理

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

而且生成的动态代理类必须实现一个或者多个接口,所以,jvm生成的动态类只能用作相同的接口的目标类的代理


理解总结:代理类中有一个静态方法Proxy.newProxyInstance(loader,interfaces,h);一步到位,返回一个proxy代理类,参数接收一个类加载器,一个接口,

和一个实现接口:Invokehander接口的实例对象,覆盖其中的invoke()方法,方法中创建目标类的代理,并且在代理类的前后添加功能代码,

返回目标类的代理类。

2、  CGLIB库概念

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

3、   增加附加系统功能

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

A.   在调用目标方法之前

B.    在调用目标方法之后

C.    在调用目标方法前后

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


代码练习:创建代理类

package cn.itcast.day3;

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;


public class ProxyTest {

	

	
	public static void main(String[] args) throws Exception
	{
		// TODO Auto-generated method stub
		Class clazProxy=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
		
		System.out.println("--------------------通过反射获取所有的构造函数:-------------");
		Constructor[] constructors=clazProxy.getConstructors();
		for(Constructor constructor:constructors){
			
			String name=constructor.getName();
			StringBuilder sb=new StringBuilder(name);
			sb.append("(");
			Class[] clazparams=constructor.getParameterTypes();
			for(Class clazparam:clazparams){
				sb.append(clazparam.getName()).append(",");
				
			}
			if(clazparams!=null&&clazparams.length!=0)
				sb.deleteCharAt(sb.length()-1);
			sb.append(")");
			System.out.println(sb.toString());
		}/**/
		
		System.out.println("--------------------通过反射获取所有的方法:-------------");
		Method[] methods=clazProxy.getMethods();
		for(Method method:methods){
			
			String name=method.getName();
			StringBuilder sb=new StringBuilder(name);
			sb.append("(");
			Class[] clazparams=method.getParameterTypes();
			for(Class clazparam:clazparams){
				sb.append(clazparam.getName()).append(",");
				
			}
			if(clazparams!=null&&clazparams.length!=0)
				sb.deleteCharAt(sb.length()-1);
			sb.append(")");
			System.out.println(sb.toString());
		}
		System.out.println("-----------通过字节码创建代理类的实例----------------------");
		//获取构造方法
		Constructor constructor=(Constructor) clazProxy.getConstructor(InvocationHandler.class);
		//实现IncocationHandler接口
		class MyInvocationHandler1 implements InvocationHandler{
			/**
			 * @param proxy
			 * @param method
			 * @param args
			 * @return
			 * @throws Throwable
			 */
			
			@Override//覆盖父类的抽象方法
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// TODO Auto-generated method stub
				return null;
			}
		}
		//成功创建代理类对象
/*	 	Collection collection1=(Collection) constructor.newInstance(new MyInvocationHandler1());
		
	 	System.out.println(collection1);
		//第二种创建代理类对象,应用匿名内部类
		Collection collection2=(Collection)constructor.newInstance(new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// TODO Auto-generated method stub
				return null;
			}
		});
	
		
		System.out.println(collection2);
	//第三种创建代理类的方式
		//代理类中有一个静态方法Proxy.newProxyInstance(loader,interfaces,h);一步到位,返回一个proxy代理类

						
						Collectiont proxyCollection=(Collection)Proxy.newProxyInstance(
 
							Collection.getClass().getClassLoader(),
new Class[]{Collection.class},new InvocationHandler(){//目标类@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stubSystem.out.println("-------------程序马上开始------------------"); start=System.currentTimeMillis();
										Object revalue=method.invoke(target,args);
System.out.println("-------------程序运行结束------------------");
										long end=System.currentTimeMillis();
										System.out.println(method.getName()+"::运行耗费时间-----"+(end-start));
return revalue;}});return proxyCollection ;}proxyCollection.add("zxxxx");proxyCollection.add("hcccccc");System.out.println("代理类的toString:"+proxyCollection.toString());System.out.println("代理类的hashCode:"+proxyCollection.hashCode());System.out.println("代理类的equals:"+proxyCollection.equals(proxyCollection));System.out.println("代理类的名字:"+proxyCollection.getClass().getName());

四、 分析动态代理类的原理和结构


怎样将目标传进去:

1.直接在InvocationHandler实现类中创建目标类的实例对象,可看运行效果和加入日志代码,但是没有实际意义。

2.InvocationHandler实现类注入目标的实例对象,不能采用匿名内部类的形式了。

3.让匿名内部类的InvocationHandler实现类访问外面的方法中的目标类实例对象的final类型的引用变量。

动态代理的工作原理:

A.
Client(客户端)调用代理,代理的构造方法接收一个InvocationHandlerclient调用代理的各个方法,代理的各个方法请求转发给刚才通过构造方法传入的handler对象,又把各请求分发给目标的相应的方法。




练习代码:
public class ProxyTest {

	

	
	public static void main(String[] args) throws Exception
	{
		final ArrayList target=new ArrayList();//目标类
		MyAdvice myAdvice=new MyAdvice();  //系统功能类(代理类特有的功能方法)
		
		Collection proxyCollection = (Collection)getProxy(target,myAdvice);
		proxyCollection.add("zxxxx");
		proxyCollection.add("hcccccc");
		
		System.out.println("代理类的toString:"+proxyCollection.toString());
		System.out.println("代理类的hashCode:"+proxyCollection.hashCode());
		System.out.println("代理类的equals:"+proxyCollection.equals(proxyCollection));
	
		System.out.println("代理类的名字:"+proxyCollection.getClass().getName());
		
	}
	//重构成一个方法
	private static Object  getProxy(final Object target,final MyAdvice advice) {
		Object proxyCollection=Proxy.newProxyInstance(
								target.getClass().getClassLoader(),
								
								//new Class[]{Collection.class},
								target.getClass().getInterfaces(),
								new InvocationHandler(){
									//目标类
									
									@Override
									public Object invoke(Object proxy, Method method, Object[] args)
											throws Throwable {
										// TODO Auto-generated method stub
										
										
										advice.start(method);
										Object revalue=method.invoke(target,args);
										advice.end(method);
										
										
										return revalue;
									}
								}
								);
		return proxyCollection ;
	}

}

package cn.itcast.day3;



public class MyAdvice implements Advice {

	long start=0;
	
	@Override
	public void end(java.lang.reflect.Method method) {
		// TODO Auto-generated method stub
		System.out.println("-------------程序运行结束------------------");
		long end=System.currentTimeMillis();
		System.out.println(method.getName()+"::运行耗费时间-----"+(end-start));
	
	}

	@Override
	public void start(java.lang.reflect.Method method) {
		// TODO Auto-generated method stub
		System.out.println("-------------程序马上开始------------------");
		 start=System.currentTimeMillis();
	
	}


}

五、实现AOP功能的封装与配置


1、工厂类BeanFactory

BeanFactory类负责创建目标类或者代理类的实例对象并通过配置文件实现切换。



2.getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean

则直接返回该类的实例对象,否则返回该类示例对象的getProxy方法返回的对象。

3.     BeanFactory的构造方法接收代表配置文件的输入流对象的配置文件格式如下:

xxx=java.util.ArrayList

#xxx=com.AOPFrame.ProxyFactoryBean

xxx.Advice=com.AOPFrame.MyAdvice

xxx.Target=java.util.ArrayList

4、ProxyFactoryBean 充当封装生成动态代理的工厂,需为工厂提供的配置参数信息包括 :

目标(target  类

通告(advice)类

5.     BeanFactoryProxyFactoryBean

A.   BeanFactory是一个纯粹的bean工程,就是创建bean即相应的对象的工厂。

B.    ProxyfactoryBeanBeanFactory中的一个特殊的Bean,是创建代理的工厂。

6.     实现类似spring的可配置的AOP框架的思路

A.     创建BeanFactory

构造方法接受一个字节输入流获取配置文件信息,通过Properties对象加载InputStream流对象获得。

创建getBean(String name)方法,接收Bean的名字,从上面加载后的对象获得。通过其字节码对象创建实例对象bean


B.     判断bean是否是特殊的BeanProxyFactoryBean,如果是,就要创建代理类,并设置目标和通告,

分别得到各自的实例对象,并返回代理类实例对象。如果不是在返回普通类的实例对象。

创建ProxyFactoryBean(接口),此处直接定义为类做测试,其中有一个getProxy方法,

用于获得代理类对象。编写实现Advice接口的类和在配置文件中进行配置。

练习代码:
package cn.itcast.day3;

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

public class AopFrameWorkTest {

	/**
	 * @param args
	 * @throws Exception 
	 * @throws SecurityException 
	 */
	public static void main(String[] args) throws SecurityException, Exception {
		// TODO Auto-generated method stub
		InputStream in=AopFrameWorkTest.class.getResourceAsStream("config.properties");
		
		//Object bean=new BeanFactory(in).getBean("xxx");
		Object bean=new BeanFactory(in).getBean("xxx");
		System.out.println(bean.getClass().getName());
		
		//ArrayList li=(ArrayList)bean;
		((Collection) bean).add(5);
		((Collection) bean).clear();
		
		
		//通过反射获取集合中的元素
		Method get=((Collection) bean).getClass().getMethod("get", int.class);
		Collection c=(Collection)bean;
		c.add(9);
		System.out.println(get.invoke(c, new Object[]{0}));
	}

}
package cn.itcast.day3;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
	Properties pro=new Properties();
	BeanFactory(InputStream in){	//构造函数接收一个读取流,读取配置文件
		try {
			pro.load(in);
		} catch (IOException e) {
			
			e.printStackTrace();
		}
	}
	public Object getBean(String name){	//定义一个方法,接收配置文件中的:键(String)
		
		String className=pro.getProperty(name);	//	通过键获取对应的值
		
		Object bean=null;
		try {
			Class clas=Class.forName(className);	//获取字节码对象
			 bean=clas.newInstance();		//new实例对象
		} catch (Exception e) {
			
			e.printStackTrace();
		} 
		if(bean instanceof ProxyFactroyBean){	//判断实例对象是否为:ProxyFactroyBean类型
			
			Object proxy=null;
			ProxyFactroyBean proxyFactoryBean=((ProxyFactroyBean) bean);
			
			try {
				Advice advice=(Advice)Class.forName(pro.getProperty(name+".advice")).newInstance();	//创建功能类的实例对象
				Object target=Class.forName(pro.getProperty(name+".target")).newInstance();		//创建目标类的对象
				
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				
				proxy=proxyFactoryBean.getProxy();	//创建一个代理的实例
			} catch (Exception e) {
				
				e.printStackTrace();
			}
			
			
			
			return  proxy;		//返回代理
		}
		return bean;		
	}
}
package cn.itcast.day3;

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

public class ProxyFactroyBean {	
		
		private Object target;//目标类
		private Advice advice;//特有的功能
		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 proxy=Proxy.newProxyInstance(
						target.getClass().getClassLoader(),
						
						//new Class[]{Collection.class},
						target.getClass().getInterfaces(),
						new InvocationHandler(){
							//目标类
							
							@Override
							public Object invoke(Object proxy, Method method, Object[] args)
									throws Throwable {
								// TODO Auto-generated method stub
								
								
								advice.start(method);
								Object revalue=method.invoke(target,args);
								advice.end(method);
								
								
								return revalue;
							}
						}
						);
				return proxy ;

				
			}
}







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值