高新技术五(动态代理类,代理类的作用与原理,AOP框架应用)

 

一.代理类

 

1.代理类概述

1)代理的作用与理解:

要为已存在的多个具体相同接口的目标类的各个方法增加一些系统功能,

例如:异常处理,日志,计算机方法的运行时间,事务管理,等等,那么就用到了代理类, 

代理类使用过程 :让代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码,

代理是实现AOP编程的核心和关键技术。

2)动态代理:

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,

将会非常麻烦,要写百上千个代理类。

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

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

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

2.程序代理代码举例:

     class X

     {

    void sayHello()

    {

     System.out.println(“hello,itcast”);

    }

     }

     class XProxy

     {

    void sayHello()

    {

     int starttime;

     X.sayHello();

     int endtime;

    }

  }

3.程序代理架构图:

    Client                         接口

   客户端调用程序                  doSomeThing()

     |     /    ^

     |             /              |

     |         /                    |

     v    /                         |

     Proxy                          Target

    代理类   ——————>   目标类

     doSomeThing()          doSomeThing()

     {                                  {

      //前置系统功能代码                  //业务功能代码

      目标对象.doSomeThing() —->     }

      //后置系统功能代码

     }

 4.AOP

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

      安全     事务    日志

  StudentService—-|——–|——-|——

  CourseService —-|——–|——-|——

  MiscService   —-|——–|——-|——

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

  method1  method2  method3

  {   {   {

  ———————————————–切面

   ……   ……   ……

  ———————————————–切面

  }   }   }

  3)交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),

  AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,

  这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

  ———————————————-切面

  func1  func2  func3

  {   [   {

   …..   ….   ….

  }   }   }

  ———————————————-切面

  4)安全,事务,日志等功能要贯穿到好多个模块中,所以,它们是交叉业务。

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

  

 5.创建动态类及查看其方法列表信息

 代码示例:

 

public class ProxyTest {

/**
* @param args
*/
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub

//得到Collection接口的动态代理类
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

//遍历打印构造方法
Constructor[] constructors  = clazzProxy1.getConstructors();
for(Constructor constructor : constructors){
String name = constructor.getName();
StringBuilder sb = new StringBuilder(name);
sb.append("(");
Class[] claszzParams  = constructor.getParameterTypes();
for(Class classparam : claszzParams){
sb.append(classparam.getName()+",");
}
if(claszzParams!= null && claszzParams.length!=0)
sb.deleteCharAt(sb.length()-1);
sb.append(")");
System.out.println(sb.toString());
}

//遍历打印Collection集合中方法
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String methodname = method.getName();
StringBuilder sb = new StringBuilder(methodname);
sb.append("(");
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sb.append(clazzParam.getName()+",");

}
if(clazzParams!= null && clazzParams.length!=  0)
sb.deleteCharAt(sb.length()-1);
sb.append(")");
System.out.println(sb.toString());
}

 

 

二.InvocationHandler

 

 

1.接口 InvocationHandler(java.lang.reflect) 

  1)InvocationHandler 是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的

  调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 

  invoke 方法。 

  2)方法摘要 

  Object invoke(Object proxy, Method method, Object[] args) 

    在代理实例上处理方法调用并返回结果。 

2.使用InvocationHandler对象

 

//先得到代理类的构造函数再实例化,因为Inovacation没有默认构造器,不能使用clazzProxy1.newInstance();
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);

class MyInvocationHandler implements InvocationHandler{


@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}

Collection col = (Collection)constructor.newInstance(new MyInvocationHandler());
System.out.println(col);
col.clear();
// col.size();


               //用匿名内部类来实现InvocationHandler接口传递给newInstance的参数。
Collection col2 = (Collection)constructor.newInstance(new InvocationHandler(){


@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}

});


             ////创建动态代理类的第二种方法,直接调用代理类的 newProxyInstance方法。


              Object proxy3 = Proxy.newProxyInstance( 
target.getClass().getClassLoader(), 
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
Object retVal = method.invoke(target, args);
return retVal;

}
} 

2.分析InvocationHandler对象的运行原理

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

 1).动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和

   一个如下接受InvocationHandler参数的构造方法。

 2)..构造方法接收一个invocationHandler对象,接收对象了要干什么?该方法内部的代码会是怎样的呢?

 

  $Proxy0 implements Collection

  { 

    //拥有InvocationHandler的引用 

   InvocationHandler handler;

   public $Proxy0(InvocationHandler handler)

   {

    this.handler = handler;

   }

  }

 3)..实现的Collection接口中的各个方法的代码又是怎样的呢?

  //生成的Collection接口中的方法的运行原理

   int size()

   {

    return handler.invoke(this,this.getClass().getMethod(“size”),null);

   }

   void clear()

   {

    handler.invoke(this,this.getClass().getMethod(“clear”),null);

   }

 4)..InvocationHandler接口中定义的invoke方法接收的三个参数又是什么意思?图解说明如下:

  Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象,add方法,”abc”参数

   

   Class Proxy$

   {

    add(Object object)

    {

     return handler.invoke(Object proxy,Method method,Object[] args);

    }

   }

5)分析为什么动态类的实例对象的getClass()方法返回了正确结果? 

   调用代理实例对像从Object类继承的hashCode,equals,或toString这几个方法时,代理对象将调用

  请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。

3.编写可生成代理和插入通告的通用方法。

代码示例:

 

ArrayList target = new ArrayList();//目标类 

Collection proxy3 = (Collection)getProxy(target,new MyAdvice());//代理类

proxy3.add("zxx");  //每调用一次方法就会去调用InvocationHandler的invoke方法。
proxy3.add("lxc");
proxy3.add("lxm");

proxy3.getClass().getName();//在Object类上继承的方法,只有hashCode,equals,toString()会委托InvocationHandler。
System.out.println(proxy3.size());
}


private static Object getProxy(final Object target,final Advice advice) {
Object proxy3 = Proxy.newProxyInstance( 
target.getClass().getClassLoader(), 
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()+"run of time"+(endTime- beginTime));
return retVal;*/

advice.beforeMethod(method);//通告
Object retVal = method.invoke(target, args);//调用目标类方法。
advice.afterMethod(method);//通告

return retVal;
}
}
);
return proxy3;
}
}

 

 

三.实现类似spring的可配置的AOP框架

 

 

 1.实现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充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?

  <1>目标

  <2>通知

  4)编写客户端应用:

  <1>编写实现Advice接口的类和在配置文件中进行配置

  <2>调用BeanFactory获取对象

2.通过实验了解的知识点

1)Spring的精髓 AOP框架和Bean工厂 。

2)Properties类中方法:

   String getProperty(String key) 

    用指定的键在此属性列表中搜索属性。 

   void load(InputStream inStream) 

    从输入流中读取属性列表(键和元素对)。 

3)JavaBean必须有一个不带参数的构造方法

4) InputStream getResourceAsStream(String name) 

    把指定文件加载到输入流.

 

3.代码示例:

 

/Bean工厂 专用于生产Bean
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;
try {
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
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)bean).getProxy();
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}

return bean;
}
}

--------------------------------------
//专用于操作代理类
public class ProxyFactoryBean {
private Object target;
private Advice advice;


public void setTarget(Object target) {
this.target = target;
}

public Object getTarget() {
return target;
}


public void setTarget(String target) {
this.target = target;
}


public Advice getAdvice() {
return advice;
}


public void setAdvice(Advice advice) {
this.advice = advice;
}


public Object getProxy() {
// TODO Auto-generated method stub
Object proxy = 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 proxy;
}
}


-----------------------
测试类
public class AopFrameworkTest {/**
* @param args
*/
public static void main(String[] args) {
// 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();

}
}

 

 

 

    原文作者:AOP
    原文地址: https://blog.csdn.net/bysir520/article/details/9003196
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞