黑马程序员-JAVA基础加强-代理

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

代  理
代理的概念与作用
      要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
    如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
代理架构图
       
        客户端原来调用的是目标类,而现在调用代理类,代理类与目标来都实现了相同的接口,对外具有相同的方法,在客户端编写程序时,不是直接引用目标,也不是直接引用代理,而是用接口进行引用,这样可以在不用改源代码的情况下进行切换,但是在代理类方法里面一定要调用目标对应的方法,并且可以在目标类前后加上一些系统功能。
AOP
        AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
                         安全        事务        日志
     StudentService ------|----------|------------|-------------
     CourseService  ------|----------|------------|-------------
     MiscService    ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
     method1 method2 method3
     {       {       {
     ------------------------------------------------------切面
     .... .... ......
     ------------------------------------------------------切面
     }       }        }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
     ------------------------------------------------------切面
     func1 func2 func3
     {     {    {
      .... .... ......
     }     }    }
     ------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

动态代理技术
    1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
    2、JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
    3、JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
    4、CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
    5、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
      1)在调用目标方法之前
      2)在调用目标方法之后
      3)在调用目标方法前后
      4)在处理目标方法异常的catch块中


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

猜想分析动态生成的类的内部代码
    1.动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
    2.构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
    3.实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:


动态代理的工作原理图


动态代理类的建立
    1.获取代理类的字节码对象,传入与目标类的一致的接口类加载器和接口字节码
    2.实现invocationHandler接口,复写invoke方法
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
	{	
		final ArrayList target = new ArrayList();			
		Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
		proxy3.add("zxx");
		proxy3.add("lhm");
		proxy3.add("bxd");
		System.out.println(proxy3.size());
		System.out.println(proxy3.getClass().getName());
	}
	//传入的目标对象、系统功能接口 ,就能生成一个代理,调用目标并在目标的前后周围执行系统代码
	private static Object getProxy(final Object target,final Advice advice)
	{
	Object proxy3 = Proxy.newProxyInstance(
		target.getClass().getClassLoader(),/*指定代理类的类加载器与目标类一致*/
		target.getClass().getInterfaces(),/*指定代理类实现的接口与目标类一致*/
		new InvocationHandler()/*代理类有参数的构造方法里面有InvocationHandler接口,匿名内部类子类实现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;
         }
}

创建功能类接口

package cn.itcast.day3;

import java.lang.reflect.Method;

public interface Advice {
	void beforeMethod(Method method);
	void afterMethod(Method method);
}
实现接口
package cn.itcast.day3;

import java.lang.reflect.Method;

public class MyAdvice implements Advice 
	{
	long beginTime = 0;
	public void afterMethod(Method method)
		{
		// TODO Auto-generated method stub
		System.out.println("从传智播客毕业上班啦!");		
		long endTime = System.currentTimeMillis();
		System.out.println(method.getName() + " running time of " + (endTime - beginTime));

		}

	public void beforeMethod(Method method) 
		{
		// TODO Auto-generated method stub
		System.out.println("到传智播客来学习啦!");
		beginTime = System.currentTimeMillis();
		}

	}
实现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获取对象

package cn.itcast.day3.aopframework;

import java.io.InputStream;
import java.util.Collection;

public class AopFrameworkTest {
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		//给予指定资源路径的输入流
		InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
		Object bean = new BeanFactory(ips).getBean("xxx");
		//打印类名看到底是目标还是代理
		System.out.println(bean.getClass().getName());
		((Collection)bean).clear();
	}
}
配置文件config.properties
//name    value两个属性
xxx=java.util.ArrayList
xxx=cn.itcast.day3.aopframework.ProxyFactoryBean
xxx.advice=cn.itcast.day3.MyAdvice
xxx.target=java.util.ArrayList
BeanFactory.java文件
	package cn.itcast.day3.aopframework;

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

	import cn.itcast.day3.Advice;

	public class BeanFactory {
		Properties props = new Properties();
		//创建配置文件其实就是想得到键值对的对象,让流与集合联系起来
		public BeanFactory(InputStream ips)//通过流指定配置文件
		{
			try {
				props.load(ips);//将配置文件的内容存入集合
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		public Object getBean(String name)//该功能返回类的名字
		{
			String className = props.getProperty(name);//根据名字拿到对应的类名
			Object bean = null;
			try 
			{
				Class clazz = Class.forName(className);//创建类的字节码对象
				bean = clazz.newInstance();//创建对象,对于javabean来说必须要有一个不带参数的构造方法
			} catch (Exception e)
				{
				// TODO Auto-generated catch block
				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)
					{
					// TODO Auto-generated catch block
					e.printStackTrace();
					}
				return proxy;
			}
			return bean;
		}
	}
ProxyFactoryBean.java文件
package cn.itcast.day3.aopframework;

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

import cn.itcast.day3.Advice;

public class ProxyFactoryBean 
	{
	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 proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),//指定类加载器			
				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;
	}

}
------- android培训java培训、期待与您交流! ---------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值