Java-动态代理技术

1.程序中的代理

   为具有相同接口的目标类的各个方法,添加一些系统功能,如日志,异常处理,计算方法运行的

   时间,事务管理等等,都可以交给另一个类去实现这些功能,该类称为代理类。

   注意:为了让代理类共享目标类中的各个方法,可以让代理类实现和目标类相同的接口。

  public class AProxy {
    //AProxy类为A的代理类,可以计算sayHi方法的运行时间
    public  void getTime()
    {
        //方法开始前时间
         new A().sayHi();
        //方法结束后时间
    }
}
class A
{
    void sayHi()
    {
        System.out.println("hi,everyone!");
    }
}

 

2.代理架构图

  

   它采用工厂模式和配置文件的方式进行管理,这样就不需要修改客户端程序,通过

   配置文件指定使用目标类,还是代理类。这样做添加/去掉系统功能变得简单。

   当我们需要测试系统的性能时,可以使用代理类,客户要使用时可以使用目标类

3.AOP(Aspect oriented program)

   交叉业务:要处理不同层面的业务(service),称为交叉业务,如安全,事务,日志等。

                                  安全       事务         日志
    StudentService  ------|----------|------------|-------------
    CourseService   ------|----------|------------|-------------
    MiscService       ------|----------|------------|-------------

   用具体的程序代码描述交叉业务:
     method1         method2          method3
     {                      {                       {
       ------------------------------------------------------切面
          ....            ....              ......
       ------------------------------------------------------切面
      }                       }                       }

   面向方面的编程(简称AOP),AOP的目标就是要使交叉业务模块化。

   可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
       ------------------------------------------------------切面
       func1         func2            func3
        {             {                {
          ....            ....              ......
         }             }                }
      ------------------------------------------------------切面

    这里就是使用代理技术解决这种问题,代理实现AOP功能的核心和关键技术。
4.动态代理技术

  •   要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!
  •   JVM提供了动态代理类(Proxy)。即可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类。这里是仅指具有接口的代理类和目标类。
  •   对于没有接口的目标类,CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

   代理方法中的有如下四个位置加上系统功能代码:

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

class proxy{
    void sayHello(){
        ………//1
        try{
            target.sayHello();
        }catch(Exception e){
            ………//4
        }
        …………//2
    }
}

 

5.Proxy(代理类)

      |-java.lang.reflect.Proxy

      Proxy提供了创建动态代理类和实例的静态方法,它也是这些创建出来类的超(父)类。

      构造方法:

            protected  Proxy(InvocationHandler  h);//使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例

      成员方法:

            static Class<?> getProxyClass(ClassLoader loader,Class<?> interfaces);//创建一个具有接口的动态代理类。

            static Object newProxyInstance (ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);//返回一个代理类实例对象

  创建代理类及其实例对象:

  法一:先创建代理类,再用代理类的构造方法创建实例对象

public static void main(String[] args) {
        // 获得一个集合接口的代理类,为其指定接口和类加载器
        //通常loader,interface是一样的字节码产生的
        Class  clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
        System.out.println(clazzProxy1.getName());
        //创建一个代理类实例
        //注意:clazzProxy1.newInstance()调用的是它的无参构造方法创建实例,但是它没有无参的构造方法
        //通过有参构造方法创建实例
        Constructor ct=clazzProxy1.getConstructor(InvocationHandler.class);
        class MyinvocationHandler implements InvocationHandler
        {
             @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                 return null;
            }
         }
        Collection proxy1=(Collection) ct.newInstance(new MyinvocationHandler());
     }}

 

   法二:用new方法直接创建代理类实例对象

Object target=new Object ();
Object proxy1=Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler(){

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

Class<?> []cs={Collection.class};
Collection proxy3=(Collection)Proxy.newProxyInstance
        (Collection.class.getClassLoader(),
          cs,new MyinvocationHandler());
Collection proxy4=(Collection)Proxy.newProxyInstance
        (Collection.class.getClassLoader(),
         new Class[] {Collection.class},
         new MyinvocationHandler());

6.InvocationHandler(接口)

   成员方法:

        Object invoke(Object proxy, Method method, Object[] args);//在代理实例上处理方法调用并返回结果。

        事实上,Proxy代理类对象调用(该接口上的)方法时,其实是去调用了handler的

        invoke方法。对于不是该接口上的方法,如getClass(),则调用它本身的,不用委托handler去调用invoke方法。

7.代理类的模式

   以面向对象的思想处理AOP(面向方面编程)的业务,即在实现代理的系统功能时,将目标类对象和接口(该接口通常以实现接口某个类的方式传入,实现接口里的方法(系统功能)的类)作为参数传入一个要实现系统功能的方法中。

        ArrayList al=new ArrayList();
        Collection proxy4=(Collection)getProxy(al,new Myadvice());
        proxy4.add("hq");
    //以下是封装了实现代理功能的一个方法
    //参数:目标类对象,和接口(实现代理功能的类,类里有实现功能的方法),该接口通常以实现接口某个类的方式传入。
    private static Object getProxy(final Object target,final interfaceAdvice advice) {
         Object proxy=Proxy.newProxyInstance
                (target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(),
                 new InvocationHandler(){
                    
                    @Override
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                         advice.beforeMethod(method);
                         Object obj=method.invoke(target, args);
                         advice.afterMethod(method);
                         return obj;
                    }
                });
         return proxy;
    }

 

8.实现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充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标:target
通知:advice
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象

 

posted @ 2015-03-04 22:39  beyondbycyx  阅读(283)  评论(0编辑  收藏  举报