黑马程序员--09.动态与代理AOP--06【动态代理实例化的过程升级--目标对象+系统功能的参数化】【实现类似Spring的可配置AOP框架】

来源:互联网 发布:电商和微商的区别 知乎 编辑:程序博客网 时间:2024/05/22 07:09

动态代理与AOP----6

动态代理实例化的过程升级--目标对象+系统功能的参数化

实现类似Spring的可配置AOP框架

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

上次课基本上推理出动态生成的代理类的结构以及InvocationHandler的结构。这次针对InvocationHandler的结构进行改进。

1.    动态代理类实例化过程升级--目标对象+系统功能的参数化

1). 现有InvocationHandler实现类存在的问题

(1). 现有InvocationHandler的实现类的一般结构

[1]. InvocationHandler实现类的代码如下

Object proxyInstance=con.newInstance(new InvocationHandler() {    private ArrayList target =new ArrayList();    public Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable {       //交叉业务       Object retVal = method.invoke(target, args);       //交叉业务       return retVal;}});

(2). 存在的问题如下:

[1]. 目标类对象已经固化到代码中没有办法用户传入进行设置

[2]. 交叉业务也同样被固化到代码中,无法由用户自由配置。【交叉业务也叫系统功能

目标:将目标类对象移动到匿名局部类newInvocationHandler(){}的外部进行配置

         将系统功能 (交叉业务) 通过参数传入InvocationHandler局部匿名类

2). 目标对象和系统功能的参数化

(1). 目标对象和系统功能独立应注意的问题

[1]. 目标类对象InvocationHandler的匿名局部类独立出来

注意局部类只能访问外部final变量

[2]. 系统功能InvocationHandler的匿名局部类独立出来

{1}. 系统功能代码块,要独立出去并且以参数的形式传入InvocationHandlerinvoke方法,就必须要包装独立的对象的方法才可以。

{2}. 系统功能独立对象的方法被封装到InvocationHandler匿名局部类的外面之后,又需要这个匿名局部类引用。那么封装着系统功能的对象必须final的。

(2). 封装系统功能对象所属的类的设计思路

{1}. 系统功能位置

[1]. 前面[2].后面[3].前面+后面[4].catch块中

四个位置的代码一定都要被封装成四个方法并且一定要实现。因此这是一种强制性的工作并且系统功能并不是确定的。所以向上抽取成有含有四个方法的接口合理

{1}. 接口命名

Spring将系统功能看做是用户对代码的建议,所以接口命名成Advice

【注意】这里面仅仅定义两个方法作为示例方法前 + 方法后

{2}. 接口方法命名

[1]. 目标类对象代码前面--- beforeMethod

[2]. 目标类对象代码后面--- afterMethod

{3}. 接口方法参数列表

Spring规定封装业务代码接口的方法应该传入目标类对象目标类对象调用的方法目标类对象调用的方法对应的参数

[3]. 对交叉业务 (系统功能)封装的接口代码如下

public interface Advice {    //目标代码之前的交叉业务    void beforeMethod(Object target, Methodmethod, Object[] args);       //目标代码之后的交叉业务    void afeterMethod(Object target, Methodmethod, Object[] args);}

(3). 构造既可以生成代理类对象又可以插入系统功能的通用方法

[1]. 构建封装了交叉业务的接口Advice的实现类MyAdvice

实现Advice的接口要求具体交叉业务 (系统功能)

这里假设交叉业务的功能是计算目标方法运行的时间

public class MyAdvice implements Advice{    private long beginTime;    private long endTime;       @Override    public void beforeMethod(Object target, Methodmethod, Object[] args) {       //交叉业务       System.out.println("到黑马学习啦!");       beginTime =System.currentTimeMillis();    }     @Override    public void afeterMethod(Object target, Methodmethod, Object[] args) {       //交叉业务       System.out.println("从黑马马上毕业了!");       endTime =System.currentTimeMillis();       System.out.println(method.getName() +" using "+ (endTime -beginTime)/1000+ "s");    } }

[2]. 编写可以插入系统功能目标类对象的代理类实例生成器

public static Object getProxy(final Object target, final Advice advice){    ClassLoaderloader =target.getClass().getClassLoader();    Class<?>[]interfaces =target.getClass().getInterfaces();    InvocationHandlerh =new InvocationHandler() {       @Override       public Object invoke(Object proxy, Methodmethod, Object[] args)              throws Throwable {           advice.beforeMethod(target,method, args);                     ObjectretVal =method.invoke(target, args);                     advice.afterMethod(target,method, args);           return retVal;       }    };       ObjectproxyObj =Proxy.newProxyInstance(loader, interfaces, h);    return proxyObj;}

[3]. 测试

Collection target =new ArrayList();Advice advice =new MyAdvice(); Collection proxyInstance =(Collection)getProxy(target,advice);proxyInstance.add("123");System.out.println("*************************");proxyInstance.add("456"); System.out.println("*************************");System.out.println(proxyInstance.size());

【打印结果】


总结

{1}. 将目标系统功能抽取到外面之后,如果再向生成代理类实例的话,直接传入目标类对象封装有交叉业务的对象。

{2}. getProxy()方法可以固定下来做框架使用。

经验】使用Spring仅仅需要做的事情:在配置文件中配置目标对象 + 在Advice的方法实现中编写业务代码。

2.    实现类似Spring的可配置的AOP框架

需求:通过配置文件配置出想要实例化的对象所属类,通过反射技术代码中获取配置文件的信息实例化要配置的类

附加需求】实例化的对象可以是字节码的文件存在的预先通过编译的类对象,可以是没有字节码文件动态生成代理类对象

1). 配置文件的设计

[1]. 动态生成类普通类生成区别

{1}. 普通类直接给出类名就可以在程序中实例化这个类的对象

{2}. 动态代理类的生成需要InvocationHandler实现子类 (封装了系统功能的类 ) 代理的目标类两个额外的信息生成动态代理类对象

[2]. 现在采用简单工厂模式生成普通类的实例。为了统一起见将生成动态代理类对象任务交给指定名称自定义的普通的Java类来实现。

{1}. 这个指定的普通类名spring.ProxyFactoryBean

一旦在配置的类名这个类的名字,那么工厂类就会实例化ProxyFactoryBean实例。这个类被实例化之后,就要调用内部编好的方法(直接可以调用第一部分的getProxy方法)实例化动态代理类对象

{2}. 如果是其他的类名,就直接实例化就可以了。

[3]. 配置文件内容构成

{1}. 普通类类名:设置为beanName

{2}. 如果普通类的类名是spring.ProxyFactoryBean,标志着这个实例对象是用来生成动态代理类的普通类。此时要额外配置代理类需要的代理类目标类类名和封装业务代码的Advice子类的类名

{2}1. 代理类代理的目标类类名:beanName.target

{2}2. 代理类需要的交叉业务所在的类名:beanName.advice

[4]. 配置文件举例如下:


2). Bean工厂的设计 ----BeanFactory

通过字符串封装的类名来获取类对象 ----getBean(String className)

无论是普通类对象还是动态代理类对象都通过这个类BeanFactory的getBean(String)来获取相应的对象

流程】. 这样无论是代理类对象的实例化还是普通类对象的实例化都首先经过Class对象进行实例化。一旦出现指定的普通类ProxyFactoryBean出现在配置文件中创建Class对象实例化之后,就要调用这个ProxyFactoryBean类实例的方法生成动态代理类对象。

3). 工程测试

(1). 工程配置


(2). ProxyFactoryBean类代码示例

package spring; importjava.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyFactoryBean {    private Advice advice;    private Object target;     //生成动态代理类对象    public Object getProxyBean(){       ObjectproxyInstance =Proxy.newProxyInstance(              target.getClass().getClassLoader(),              target.getClass().getInterfaces(),              new InvocationHandler(){                  @Override                  public Object invoke(Object proxy, Methodmethod,                         Object[]args) throws Throwable {                     advice.beforeMethod(target, method, args);                     ObjectretVal =method.invoke(target,args);                     advice.afterMethod(target, method, args);                     return retVal;                  }                               });       return proxyInstance;    }       //Setter & Getter    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;    }}

(3). BeanFactory示例代码

package spring; import java.io.IOException;import java.io.InputStream;import java.util.Properties; public class BeanFactory {    Propertiesprops =new Properties();       public BeanFactory(InputStream ips){             try {           props.load(ips);       }catch (IOException e) {           e.printStackTrace();       }          }       public Object getBean(String name) throws ClassNotFoundException,InstantiationException, IllegalAccessException{       StringbeanName =props.getProperty(name);       ClassbeanClass =Class.forName(beanName);             //JavaBean必须有一个无参的构造函数       Objectbean =beanClass.newInstance();             //看看是否是动态代理类的普通标志对象       if(bean instanceof ProxyFactoryBean){           //从配置文件中获取交叉业务的配置           StringadviceName =props.getProperty(name+".advice");                     //从配置文件中获取目标类           StringtargetName =props.getProperty(name+".target");                     Adviceadvice =(Advice)Class.forName(adviceName).newInstance();           Objecttarget =Class.forName(targetName).newInstance();                     ProxyFactoryBeanproxyFactoryBean =(ProxyFactoryBean)bean;           proxyFactoryBean.setAdvice(advice);           proxyFactoryBean.setTarget(target);                     //实例化动态代理类对象           ObjectproxyBean =proxyFactoryBean.getProxyBean();           return proxyBean;       }             return bean;    }}

(4). 测试类代码

package spring; import java.io.InputStream;import java.util.Collection; //客户端程序public class AOPFrameWorkTest {    public static void main(String[] args) {       InputStreamips =null;       try {           ips=AOPFrameWorkTest.class.getResourceAsStream("config.properties");           Collectionbean =(Collection)new BeanFactory(ips).getBean("beanName");                     StringclassName =bean.getClass().getName();           if(className.equals("$Proxy0"))              System.out.println("动态代理对应的普通类:"+ className);           else              System.out.println("普通类:"+className);           //客户端调用功能           bean.add("Benjamin");           System.out.println("******");           bean.add("Zhang");           System.out.println("******");           bean.add("will finish the blog stagein some days....");           System.out.println("******");           System.out.println(bean.size());System.out.println("******");           System.out.println(bean);       } catch (ClassNotFoundException e) {           // TODO Auto-generated catch block           e.printStackTrace();       }catch (InstantiationException e) {           // TODO Auto-generated catch block           e.printStackTrace();       }catch (IllegalAccessException e) {           // TODO Auto-generated catch block           e.printStackTrace();       }          }}

(5). 测试I ----普通类

[1]. config.properties配置文件内容

beanName=java.util.ArrayList#beanName=spring.ProxyFactoryBean #The following items used forcreating dynamic proxy objectsbeanName.target=java.util.ArrayListbeanName.advice=spring.MyAdvice

[2]. 测试结果


(6). 测试II ----动态代理类

[1]. config.properties配置文件内容

beanName=spring.ProxyFactoryBean#beanName=java.util.ArrayList #The following items used forcreating dynamic proxy objectsbeanName.target=java.util.ArrayListbeanName.advice=spring.MyAdvice

[2]. 测试结果


【解释】原本就是Collection实例的add和获取集合元素个数size()

但是这回在这些方法之前增加了交叉业务:打印系统信息并且计算目标方法的运行时间所以打印结果如上所示。

(7). Spring的两大核心

[1]. Bean工厂

[2]. AOP框架

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

原创粉丝点击