Proxy、Bean工厂和AOP框架

来源:互联网 发布:wms仓储管理系统 php 编辑:程序博客网 时间:2024/04/26 11:15


Proxy

代理类的作用:可以为多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理、日子、计算运行时间,事务处理等。

1、必须和目标类实现相同的接口。

2、代理类的每个方法都必须调用目标类的对应相同的方法

3、在调用相同的方法时,加上系统功能的代码(就是添加新功能)

原来由客户端直接调用目标类,变为由客户端引用目标类和代理类相同的接口来调用代理类(多态)。

如果采用工厂模式和配置文件的方式管理,就只需要修改配置文件中是哟个的类,就可以实现目标类与代理类的切换。

AOP (Aspect Oriented Program):面向方面编程

交叉业务:某些功能会贯穿多个模块当中,如安全、事务、日子等。这些就是交叉业务。

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

method1         method2          method3

{                      {                       {

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

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

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

}                       }                       }


AOP的目的就是使交叉业务模块化。把这些功能抽取放在代理类当中。有不同用模块的代理类中切换来实现。交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示

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

func1         func2            func3

{             {                {

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

}             }                }

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


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

我们手动为一个个功能编写代理类,即为静态代理,只能通过源码修改的代理类。

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情,因为写成百上千个代理类。

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

1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中

创建动态代理类的实力对象的两种方法:

1、使用Proxy的getProxyClass(接口类的加载器,接口类)获得代理类,再通过反射获得代理类的构造函数创建对象。

2、直接使用Proxy的newProxyInstance(接口类的加载器,接口类数组,InvocationHandler对象)创建实例对象。

InvocationHandler是一个接口,只能以匿名对象或者子类对象传递。

内部只有一个invoke方法要覆盖。参数有三个:

Object  proxy:代表创建的动态代理类的实例对象

Method method:动态代理类实例对象调用方法的方法对象

Object[] args:动态代理类实例对象调用方法的参数值列表

new InvocationHandler(){ArrayList al = new ArrayList();//以ArrayList作为目标类@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable{return method.invoke(al,args);}}
InvocationHandler的invoke返回值只能是method调用invoke的返回值。
invoke中的对象参数不能是proxy参数,不然会无限循环。因为代理对象的方法都是通过InvocationHandler中的invoke实现的。

因此invoke中的对象参数通常是代理类的目标类的实例对象。这个对象创建必须找invoke方法外面,不然方法调用一次就创建一次。

    public static void main(String[] args) throws Exception, IllegalAccessException{// TODO Auto-generated method stub//获取动态代理类Class clazzProxy = Proxy.getProxyClass(List.class.getClassLoader(), List.class);System.out.println("-----------------begin Constructor List-----------------");//获取动态代理类的所有构造方法Constructor[] constructors = clazzProxy.getConstructors();//用代理类建立List的子类对象List obj = (List)constructors[0].newInstance(new InvocationHandler(){ArrayList al = new ArrayList();//以ArrayList作为目标类@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable{ Object retVal = method.invoke(al,args); return retVal;}});obj.add("haha");obj.add("hehe");obj.add("hihi");System.out.println(obj);System.out.println(obj.getClass().getName());}
需要添加的功能就添加在Objce retVal = method.invoke(al,args)前后和它的catch块当中。

把所需要的功能(日子、安全等)封装成对象,就是把交叉(切面)所需的代码以对象的形式进行封装,然后以对象的形式进行传递,让调用这执行对象,就相当于执行切面的代码。



Spring的两大核心:Bean工厂和AOP框架

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


为了提高代码的复用性,把获取目标类的动态代理实例对象,创建InvocationHandler的匿名对象,复写其中invoke方法的所有代码抽取成为静态方法,方便调用。即为getProxy(目标类对象,约束类对象即为通知)

这两个参数由于是内部类使用,必须是final;约束类对象就是功能的抽取Adlvice

约束类有程序员编写的接口,然后根据目标类的作用和需要的功能再进行实例化。一般有四种抽象方法,在method方法执行:前、后、前后都有、catch块中。


返回就是代理的实例对象。

public class BeanFactory//工厂类{Properties prop = new Properties();BeanFactory(InputStream ips){try{prop.load(ips);} catch (IOException e){e.printStackTrace();}}public Object getBean(String name){Object bean = null;try{String clazzName = (String) prop.get(name);Class clazz = Class.forName(clazzName);bean = clazz.newInstance();} catch (Exception e){e.printStackTrace();}if(bean instanceof ProxyFactoryBean){Object retProxy = null;try{final Object targe = Class.forName((String) prop.get(name+".target")).newInstance();final Advice myAdvice = (Advice) Class.forName((String) prop.get(name+".advice")).newInstance();retProxy = ProxyFactoryBean.getProxy(targe,myAdvice);} catch (Exception e){// TODO Auto-generated catch blocke.printStackTrace();} return retProxy;}return bean;}}


public class ProxyFactoryBean//代理工厂,aop小框架{Object targe;Advice advice;public Object getTarge(){return targe;}public void setTarge(Object tage){this.targe = tage;}public Advice getAdvice(){return advice;}public void setAdvice(Advice advice){this.advice = advice;}public static Object getProxy(final Object targe,final Advice advice){return Proxy.newProxyInstance(targe.getClass().getClassLoader(), targe.getClass().getInterfaces(), new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable{advice.beforeMethod(method);Object retVal = method.invoke(targe, args);advice.afterMethod(method);return retVal;}});}}

public class myAdvice implements Advice//约束类{private long start;@Overridepublic void afterMethod(Method method){long end = System.currentTimeMillis();System.out.println("计时结束:"+(end-start));}@Overridepublic void beforeMethod(Method method){System.out.println("计时开始:");start = System.currentTimeMillis();}}

public class Aopframework//测试{public static void main(String[] args) throws FileNotFoundException{InputStream ips = Aopframework.class.getResourceAsStream("config.properties");Object obj = new BeanFactory(ips).getBean("xxx");System.out.println(obj.getClass().getName());}}