代理

来源:互联网 发布:新西兰网络制式 编辑:程序博客网 时间:2024/04/27 15:22

用代码来简单看看代理模式

★有个类

class A{

 void sayHello(){

     System.out.println("we like ZhangXiaoXiang teacher");----------------------位置1

 }

}

现在我想在位置1的代码打印输出前或后,打印点其他的比如System.out.println("we like HeiMaXuenLianYing!");,但是这时候我没有源文件(.java文件)改不了源码,我只能调用A类中的sayHello方法,那怎么办呢?

★这时候我们就可以用代理模式带实现这一需求,重新定义一个类(A对象的代理类)

package shipin49;

/**
 * A类的代理
 *
 * @author Terry
 * @date 2014-5-29
 *
 */
public class AProxy {
 public void sayHello(){
  A a = new A();
  //附加的代码
  System.out.println("we like HeiMaXuenLianYing1!");
  a.sayHello();
  System.out.println("we like HeiMaXuenLianYing2!");
 }
}

★在main方法中运行如下代码

public static void main(String[] args) {
  // TODO Auto-generated method stub
  //客户端不直接找被代理的对象(厂家),而是找代理商
  AProxy aproxy = new AProxy();
  aproxy.sayHello();
  //直接找厂家失去了代理意义
  System.out.println("--------------------------------");
  A a = new A();
  a.sayHello();
 }

★输出结果

we like HeiMaXuenLianYing1!
we like ZhangXiaoXiang teacher
we like HeiMaXuenLianYing2!
--------------------------------
we like ZhangXiaoXiang teacher

★感觉代理模式和装饰者模式很相像。装饰者模式重点在于添加附加行为修饰被装饰者,而代理模式的重点则是代替本人作业,减少对实际对象的操作。


AOP(面向切面编程)

★什么是AOP?

1、AOP

      Aspect(方面 )Oriented(导向)Programming(程序设计):意为:面向切面(行为)编程,通过预编译方式和运行期动态代理来实现程序功能的统一维护的一种技术。是函数式编程的一种衍生泛型。AOP在其他领域有其他含义。它是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。[1]

2、函数式编程(直白的说函数式编程就是方法编程----这是我的理解)   

      函数式编程是种编程典范,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。

3、更加详细的AOP参照http://baike.baidu.com/subview/73626/13548606.htm?fr=aladdin(百度百科)

★张孝祥老师视频中的AOP解说

   ●系统中存在着许多交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示


                                                              安全                       事物                          日志

      StudentService-----------------------------|------------------------|--------------------------|-----------------------------

      管理学生信息的类(管理学生模块)  |                             |                               |

      CourseService------------------------------|------------------------|--------------------------|-----------------------------

      管理课程信息的类 (管理课程模块) |                             |                               |

      ClassroomService--------------------------|------------------------|------------------------- |-----------------------------

      管理教室信息的类(管理教室模块)

    上面的这3个类,他们各管各的事,互不往来,但是他们都要有安全,都要处理事物,都要写日志,而安全、事物、日志都贯穿到了许多个模块当中,于是这3个功能就成了模块的交叉业务  。它们就是交叉业务,穿插到了许多的类当中。

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

     method1  (模块1)                     method2 (模块2)                            method3(模块3)

     {                                                    {                                                           { 

     -------------------------------------------------------------------------------------------------------切面 (在方法的这个位置,都要他们有日志功能或安全功能亦或其他的功能)

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

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

     }                                                    }                                                           }          

    ●交叉业务的编程问题即为面向方面的编程,AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

    -------------------------------------------------------------------------------------------------------切面 (在方法的这个位置,都要他们有日志功能或安全功能亦或其他的功能)

    method1  (模块1)                     method2 (模块2)                              method3(模块3)

     {                                                    {                                                           { 

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

     }                                                    }                                                           }            

     -------------------------------------------------------------------------------------------------------切面 (在方法的这个位置,都要他们有日志功能或安全功能亦或其他的功能)

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

动态代理技术

1、★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

★要为系统中的各种接口增加代理功能,如果用静态代理,那将需要再增加和系统中的接口一样多的代理类,那样工程量很大,也太麻烦。

★JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用做代理,既动态代理。

★JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类代理。(如果不实现一个或多个接口,那JVM生成的动态类就没有方法,一个没有方法的类那来做什么呢?这样意义不大。)

★如果有一个类它没有实现任何接口,那此时要动态的生成此类的代理,但是JVM生成的又必须要实现一个或多个接口,这时候怎么办呢?可以使用第三方的CGLIB库来动态生成此类的代理。

★代理的内容可以放在目标方法的什么位置呢?

    void proxyMothod(){

            位置1----------------在调用目标方法之前

            try{

                 targetMothod();

            }catch(Exception e){

            位置2-----------------在调用目标方法异常的cath语句中

            }

             位置3----------------在调用目标方法之后

    }

★打印动态代理生成的字节码中的方法

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  //一般情况下类加载器都是用和接口一样的,但是你可以用其他的类加载器类,clazzProxy1动态代理加载的类
  Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
  System.out.println("字节码名字:"+clazzProxy1.getName());
  //获得动态代理生成的类的构造方法
  Constructor[] constructors = clazzProxy1.getConstructors();
  for (Constructor constructor : constructors) {
   StringBuilder sb = new StringBuilder();
   sb.append("构造方法:"+constructor.getName()+"(");
   for (Class clazz : constructor.getParameterTypes()) {
    sb.append(clazz.getName().substring(clazz.getName().lastIndexOf(".")+1)+",");
   }
   sb.deleteCharAt(sb.lastIndexOf(","));
   sb.append(");");
   System.out.println(sb);
  }
  /**
   * 打印其他方法
   */
  for (Method constructor : clazzProxy1.getMethods()) {
   StringBuilder sb = new StringBuilder();
   sb.append("其他方法:"+constructor.getName()+"(");
   for (Class clazz : constructor.getParameterTypes()) {
    sb.append(clazz.getName().substring(clazz.getName().lastIndexOf(".")+1)+",");
   }
   if(sb.lastIndexOf(",")!=-1){
    sb.deleteCharAt(sb.lastIndexOf(","));
   }
   sb.append(");");
   System.out.println(sb);
  }
 }

运行输出:

$Proxy0
构造方法:$Proxy0(InvocationHandler);
其他方法:add(Object);
其他方法:equals(Object);
其他方法:toString();
..........

2、创建动态类的实例对象

问题一:

public static void main(String[] args) throws Exception {

Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
          clazz.newInstance();
}

上面的代码可以运行吗?

答:不可以,因为clazz没有无参的构造函数方法。

示例代码:

/**
   * 内部类
   * Invocation(调用)Handler(处理程序)
   * @author Terry
   * @date 2014-5-29
   *
   */
  class MyInvocationHander1 implements InvocationHandler{

   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable {
    // TODO Auto-generated method stub
    return null;-------------------------------------------------------------------------3
   }
   
  }
  //获得动态代理的字节码,Collection是个接口
  Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
  //获得构造函数,不能使用clazz.newInstance()因为clazz没有无参的构造函数
  Constructor con = clazz.getConstructor(InvocationHandler.class);
  //用构造函数创建一个新的对象new MyInvocationHander1()自定义的InvocationHandler
  Collection collection = (Collection)con.newInstance(new MyInvocationHander1());
  Collection test = null;
  System.out.println(collection);//这里的结果为空,但是有2中情况,一种是对象为空,一种是toString()的返回值为空
  System.out.println(test);
//  System.out.println(collection.toString());-------------------------------------1
//  System.out.println(test.toString());---------------------------------------------2

在main方法中运行输出:

null
null

★将位置1的代码取消注释运行输出:

null
null
null

★将位置2的代码取消注释运行报空指针异常

★注销位置2的代码,更改位置3的代码为:return "wahaha";运行输出:

wahaha
null
wahaha

★判断对象是否为空System.out.println(collection==null);运行输出:false;

匿名内部类的写法:

//new InvocationHandler();new InvocationHandler(){},大括号的意义,不是很懂
  Collection collection2 = (Collection)con.newInstance(new InvocationHandler(){
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable {
    // TODO Auto-generated method stub
    return null;
   }
   
 });

Proxy另一种newProxyInstance方法

static ObjectnewProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

这方法和上面的示例代码的效果是一样的。

方法一、

public void dynamicProxy(){
  Object object = Proxy.newProxyInstance(System.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() {
   
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable {
    // TODO Auto-generated method stub
    return null;
   }
  });  
  printMethodName(object.getClass());
 }

方法二、
 /**
  * 打印一个类中的所有方法
  * @param clazz1
  */
 public static void printMethodName(Class clazz1){
  for (Method constructor : clazz1.getMethods()) {
   StringBuilder sb = new StringBuilder();
   sb.append("其他方法:"+constructor.getName()+"(");
   for (Class clazz : constructor.getParameterTypes()) {
    sb.append(clazz.getName().substring(clazz.getName().lastIndexOf(".")+1)+",");
   }
   if(sb.lastIndexOf(",")!=-1){
    sb.deleteCharAt(sb.lastIndexOf(","));
   }
   sb.append(");");
   System.out.println(sb);
  }
 }

方法三、

在main方法中调用:

ProxyTest pt = new ProxyTest();
  pt.dynamicProxy();

运行输出:

其他方法:add(Object);
其他方法:equals(Object);
其他方法:toString();
其他方法:hashCode();
其他方法:clear();

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

问题二、Object object = Proxy.newProxyInstance(System.class.getClassLoader(), new Class[]{Collection.class,Date.class}, new InvocationHandler()...将代码改成这个程序报错,原因仔细看了帮助文档后明白了。

示例代码:

public void dynamicProxy(){
  Collection collectionProxy = (Collection)Proxy.newProxyInstance(
    System.class.getClassLoader(),
    new Class[]{Runnable.class,Comparable.class,Collection.class},
    new InvocationHandler() {
    ArrayList target = new ArrayList();//被代理的对象,Collection间接的实现类
   @Override
   /**
    *
    * @param proxy:代理对象(JVM动态生成的)
    * @param method:被调用的方法
    * @param args:方法中的参数

    * @return :返回方法的返回值
    */
   public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("=====================================================");
//    ArrayList target = new ArrayList();//被代理的对象,Collection间接的实现类
//    System.out.println(proxy);
    System.out.println(method.getName());
    if(args!=null){
     for (Object object : args) {
      System.out.println(object);
     }
    }   
    //在调用目标之前做点什么
    System.out.println("附加的内容1");
    Object objct = method.invoke(target, args);
    //在调用目标之后做点什
    System.out.println("附加的内容2");
    return objct;
   }
  });
//  System.out.println(object);
//  printMethodName(object.getClass());
  collectionProxy.add("zxx");
  collectionProxy.add("lhm");
  collectionProxy.remove("lhm");
  collectionProxy.add(new ArrayList().add(new ArrayList().add("hahahaha")));-----------------------------------这里输出true的原因是,方法添加成功所以为true
  System.out.println("大小是多少:" + collectionProxy.size());
 }

在main方法中运行:

ProxyTest pt = new ProxyTest();
  pt.dynamicProxy();输出结果:

=====================================================
add
zxx
附加的内容1
附加的内容2
=====================================================
add
lhm
附加的内容1
附加的内容2
=====================================================
remove
lhm
附加的内容1
附加的内容2
=====================================================
add
true
附加的内容1
附加的内容2
=====================================================
size
附加的内容1
附加的内容2
大小是多少:2

自己写的一个小型的通用性动态代理框架

★接口

/**
 * 功能程序的接口
 *
 * @author Terry
 * @date 2014-5-30
 *
 */
public interface Advice {
 public void before();
 public void laster();
}

★接口的实现类

类1

/**
 * 功能程序1
 *
 * @author Terry
 * @date 2014-5-30
 *
 */
public class MyAdvice1 implements Advice {

 @Override
 public void before() {
  // TODO Auto-generated method stub
  System.out.println("MyAdvice1开始");
 }

 @Override
 public void laster() {
  // TODO Auto-generated method stub
  System.out.println("MyAdvice1结束");
 }

}

类2

/**
 * 功能程序2
 *
 * @author Terry
 * @date 2014-5-30
 *
 */
public class MyAdvice2 implements Advice {

 @Override
 public void before() {
  // TODO Auto-generated method stub
  System.out.println("MyAdvice2开始");
 }

 @Override
 public void laster() {
  // TODO Auto-generated method stub
  System.out.println("MyAdvice2结束");
 }

}

通用性动态代理框架

/**
  * 自己写的一个小型的通用性动态代理框架
  * @param target:被代理的目标类
  * @param advice:忠告,建议的意思。这里是代理目标方法调所附加的代码。(安全、事务、日志等)
  * @return 返回目标方法返回的结果
  */
 private static Object getProxy(final Object target,final Advice advice) {
  Object proxy = (Object)Proxy.newProxyInstance(
    System.class.getClassLoader(),
    target.getClass().getInterfaces(), 
     new  InvocationHandler() {
     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
       throws Throwable {
      // TODO Auto-generated method stub
      advice.before();
      System.out.println("方法:"+method.getName()+":参数"+args[0]);
      Object object1 = method.invoke(target, args);
      advice.laster();
      return object1;
     }
    });
  return proxy;
 }

在main方法中运行如下代码:

Collection proxy1 =  (Collection)getProxy(new ArrayList(),new MyAdvice1()); 
  Serializable proxy3 =  (Serializable)getProxy(new ArrayList(),new MyAdvice1());//proxy1,proxy3为什么对的?Serializable和Collection是ArrayList的接口
  //ArrayList proxy4 =   (ArrayList)getProxy(new ArrayList(),new MyAdvice1());//错误为什么?

  //System.out.println(proxy1.getClass());输出打印:class $Proxy0,由此可以知道getProxy方法返回的是一个JVM动态生成的代理类,而此类继承了Serializable和Collection接口,在上面的框架方法中target.getClass().getInterfaces(), 有所体现,但是此类没有继承ArrayList,因为JVM动态生成的代理类必须继承接口。

proxy3.equals("22");
  System.out.println(proxy1.getClass());
  proxy1.add("zxx");
  proxy1.equals("333");
  Runnable proxy2 = (Runnable) getProxy(new Thread(),new MyAdvice2());
  proxy2.equals("aaa");

运行输出:

MyAdvice1开始
方法:equals:参数22
MyAdvice1结束
class $Proxy0
MyAdvice1开始
方法:add:参数zxx
MyAdvice1结束
MyAdvice1开始
方法:equals:参数333
MyAdvice1结束
MyAdvice2开始
方法:equals:参数aaa
MyAdvice2结束

模拟Sptring框架(Spring的两大核心Bean工厂和AOP框架)

类1、

package shipin56;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import shipin49.Advice;/** * Java,BeanFactory工厂类:用来创建动态代理 * 这里与之相关的应该是简单工厂设计模式吧 * @author Terry * @date 2014-5-30 * */public class BeanFactory {Properties props = new Properties();/** * 将xml文件以流的方式传进来 * @param ips,键值对的流 */public BeanFactory(InputStream ips){try {props.load(ips);} catch (IOException e) {// TODO Auto-generated catch block//做商业性项目要做异常处理e.printStackTrace();}}/** * 获得Bean对象,如果找到的类是ProxyFactoryBean类型的,这返回此代理工厂生产的代理类JavaBean * @param name,在xml中键值对的key值 * @return JavaBean */public Object getBean(String name){//获取xml中,key键为name的值String className = props.getProperty(name);Object bean = null;try {Class clazz = Class.forName(className);bean = clazz.newInstance();//如果获得的bean对象是ProxyFactoryBean代理工厂类型的if (bean instanceof ProxyFactoryBean){ProxyFactoryBean pfb = (ProxyFactoryBean) bean;Advice advice = (Advice) Class.forName(props.getProperty(name+".advice")).newInstance();Object target = Class.forName(props.getProperty(name+".target")).newInstance();//设置切面对象(安全、事务、日志)pfb.setAdvice(advice);//设置被代理的目标pfb.setTarget(target);Object proxy = pfb.getProxy();//返回代理对象return proxy;}} catch (Exception e) {}//返回其他对象return bean;}}

类2

package shipin56;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import shipin49.Advice;/** * 在Spring中,这里用的是接口,为了灵活吧 * 代理工厂类 * @author Terry * @date 2014-5-30 * */public class ProxyFactoryBean {/** * 获得代理对象 * @return 代理对象 */public Object getProxy() {Object proxy = (Object)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 stubadvice.before();System.out.println("我在InvocationHandler类中的invoke()方法中:"+proxy.getClass().getName());System.out.println("代理:"+proxy.getClass().getName()+"方法:"+method.getName()+":参数"+args[0]);Object object1 = method.invoke(target, args);advice.laster();return object1;}});return proxy;}//目标类private Object target;//切面对象private Advice advice;public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Advice getAdvice() {return advice;}public void setAdvice(Advice advice) {this.advice = advice;}}


类3:main方法

package shipin56;import java.io.IOException;import java.io.InputStream;import java.util.Collection;public class Main {/** * @param args * @throws IOException  */public static void main(String[] args) throws IOException {// TODO Auto-generated method stubInputStream ips = Main.class.getResourceAsStream("config.properties");Object bean = new BeanFactory(ips).getBean("xxx");Collection col = (Collection)bean;col.add("aaa");}}

接口类

package shipin49;/** * 功能程序的接口 *  * @author Terry * @date 2014-5-30 * */public interface Advice {public void before();public void laster();}

实现类1

package shipin49;/** * 功能程序1 * * @author Terry * @date 2014-5-30 * */public class MyAdvice1 implements Advice {@Overridepublic void before() {// TODO Auto-generated method stubSystem.out.println("MyAdvice1开始");}@Overridepublic void laster() {// TODO Auto-generated method stubSystem.out.println("MyAdvice1结束");}}

实现类2

package shipin49;/** * 功能程序2 * * @author Terry * @date 2014-5-30 * */public class MyAdvice2 implements Advice {@Overridepublic void before() {// TODO Auto-generated method stubSystem.out.println("MyAdvice2开始");}@Overridepublic void laster() {// TODO Auto-generated method stubSystem.out.println("MyAdvice2结束");}}

XML配置,XML文件取名config.properties

#xxx=java.util.ArrayList
xxx=shipin56.ProxyFactoryBean
xxx.advice=shipin49.MyAdvice1
xxx.target=java.util.ArrayList

运行程序输出结果:

MyAdvice1开始
我在InvocationHandler类中的invoke()方法中:$Proxy0
代理:$Proxy0方法:add:参数aaa
MyAdvice1结束

将XML里面的配置中的xxx.advice的值改为xxx.advice=shipin49.MyAdvice2运行输出:

MyAdvice2开始
我在InvocationHandler类中的invoke()方法中:$Proxy0
代理:$Proxy0方法:add:参数aaa
MyAdvice2结束

知识点:

Proxy类

static Class<?>getProxyClass(ClassLoader loader,Class<?>... interfaces)
          返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。static ObjectnewProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

Invocation(调用)Handler(处理程序)类

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

Properties类

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

Class类

 URLgetResource(String name)
          查找带有给定名称的资源。


23中常用的设计模式之-----------------------红酒经销:代理模式







0 0
原创粉丝点击