代理模式

来源:互联网 发布:c语言参考手册 pdf 编辑:程序博客网 时间:2024/06/05 10:47

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口,为什么要继承这么一个抽象类(或接口),原因是为了保证代理对象提供足够的方法调用真实对象(至少要实现接口的所有抽象方法,否则实例化的时候会报错)
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如果真实对象有自定义的构造方法,最好代理也有一个自定义的构造方法,然后在里边调用真实对象的构造方法,这样创建代理实例时便能创建真实对象实例。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

例如,Jack想追求隔壁班的一个女生(Girl),但是两者并不认识,所以Jack请好朋友Proxy帮忙,Jack要Proxy帮忙做几件事:送花、送洋娃娃、送巧克力,实现代码如下:
代理接口(抽象角色)

public interface GiveGift {

  publicvoid GiveDolls();

  publicvoid GiveFlowers();

  publicvoid GiveChocolate();

}

Jack真实对象

public class Jack implements GiveGift{

   Girlgirl;

  public Jack(Girl girl){

     this.girl = girl;

   }

  @Override

  publicvoid GiveDolls() {

      System.out.println(""+girl.getGrade()+""+girl.getName()+"送洋娃娃");

   }

  @Override

  publicvoid GiveFlowers() {

      System.out.println(""+girl.getGrade()+""+girl.getName()+"送花");

   }

  @Override

  publicvoid GiveChocolate() {

      System.out.println(""+girl.getGrade()+""+girl.getName()+"送巧克力");

   } 

}

代理

public class Proxy implements GiveGift{

   Jackjack;

  public Proxy(Girl girl){

     jack =new Jack(girl);

   }

  @Override

  public void GiveDolls() {

     //还可以添加其它操作

     jack.GiveDolls();

   }

  @Override

  public void GiveFlowers() {

     //还可以添加其它操作

     jack.GiveFlowers();

   }

  @Override

  public void GiveChocolate() {

     //还可以添加其它操作

     jack.GiveChocolate();

   }

  //还可以提供更多的方法,比如花和巧克力一起送

}

Girl这个类只是用来封装参数的,你也可以不使用Girl这个对象,分别传递年级和姓名

publicclass Girl {

  private Stringname;

  private Stringgrade;

  public String getName() {

     returnname;

   }

  publicvoid setName(String name) {

     this.name = name;

   }

  public String getGrade() {

     returngrade;

   }

  publicvoid setGrade(String grade) {

     this.grade = grade;

   }

}

测试代码:

publicclass Test {

  publicstaticvoid main(String[]args){

      Girl girl =new Girl();

      girl.setGrade("高二三班");

      girl.setName("小红红");

      Proxy proxy =new Proxy(girl);

      proxy.GiveChocolate();

      proxy.GiveDolls();

      proxy.GiveFlowers();

   }

}

上面代理模式属于静态代理,下面结合java的反射机制实现动态代理

动态代理(利用Java反射机制)
1、学生接口类

package com.dao;

public interface StudentDao {

  publicvoid saveStudent(String name,intage);

  public String queryStudent();

}

2、学生实现类

package com.dao.impl;

import com.dao.StudentDao;

publicclass StudentDaoImpl implements StudentDao{

  @Override

  publicvoid saveStudent(String name,int age){

      System.out.println("保存学生:name:"+name+",age:"+age);

   }

  @Override

  public String queryStudent() {

      System.out.println("查询学生...");

     return"查询结果";

   }

}

3、代理类

package com.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

//1、实现InvocationHandler接口

publicclass DAOProxy implements InvocationHandler{

  privateObjectoriginalObject;

  publicObject bind(Object obj){

     this.originalObject = obj;

      //2、返回代理实例,newProxyInstance是Proxy类的一个静态方法,参数分别是:实现类的类加载器、实现类的接口类、InvocationHandler对象

     return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);

   }

  @Override

  //3、实现invoke方法(执行任何一个被代理的实体类方法都会默认自动调用invoke方法)

  publicObject invoke(Object proxy, Method method,Object[] param) throws Throwable {

     Object result =null;

      System.out.println("调用实现类方法之前所做的操作....");

      result = method.invoke(this.originalObject, param);

      System.out.println("调用实现类方法之后所做的操作....");

     return result;

   }

}

说明:

1、method.invoke(this.originalObject, param):

执行这句代码时,便真正执行对应的实现类的对应方法,该句代码的返回值便是对应的实现类的方法的返回值,是一个Object类型,可以转换成任意其它类型。可以在执行该语句之前和之后做一些其它操作,比如过滤等。

2、如果有更多的接口类和实现类,比如TeacherDao、TeacherDaoImpl1、TeacherDaoImpl2、StudentImpl2、StudentImpl3等,代理类DAOProxy都不需要修改,DAOProxy可以代理任何类

4、测试:

package com.main;

import com.dao.StudentDao;

import com.dao.impl.StudentDaoImpl;

import com.proxy.DAOProxy;

publicclass Main {

  publicstaticvoid main(String[]args){

      StudentDao sDao =new StudentDaoImpl();

       DAOProxy proxy =new DAOProxy();

      //proxy.bind()返回了一个代理实例

       sDao = (StudentDao)proxy.bind(sDao);

       sDao.saveStudent("吴帝永",27);

       String result = (String)sDao.queryStudent();

       System.out.println(result);

   }

}

输出:
调用实现类方法之前所做的操作....
保存学生:name:吴帝永,age:27
调用实现类方法之后所做的操作....
调用实现类方法之前所做的操作....
查询学生...
调用实现类方法之后所做的操作....
查询结果

注意:
上面例子利用Java自带的放射机制实现动态代理,被代理的类一定要实现一个接口,否则无法被代理,因为Proxy.newProxyInstance()的第2个参数是被代理的类的接口,所以一个普通的类(没有实现任何接口的类)无法用上面的方法来实现动态代理,下面的cglib可以解决这个问题

动态代理(利用cglib
Cglib通过继承目标类来实现动态代理,所以被final修饰的类不能利用Cglib来实现动态代理,cglib通过反汇编来修改目标类的功能,这与AOP(面向切面编程)相似,都是通过反汇编来修改类的功能,它们都用到字节码处理框架ASM。
利用cglib实现动态代理需要导入相应的jar包,Spring从3.0开始便把cglib包整合到core核心包里了,3.0之前cglib包单独出来的,所以,我们导入Spring3.0的核心包便可,或者单独导入cglib包也可以。代码如下:
1、Student类,可以不用抽象类,只要不是final便可

package com.dao;

public abstract class StudentDao {

   public abstract void saveStudent(String name,int age);

   public abstract String queryStudent();

}

package com.dao.impl;

import com.dao.StudentDao;

publicclass StudentDaoImpl extends StudentDao {

  @Override

  publicvoid saveStudent(String name, int age) {

      System.out.println("保存学生:name:" + name + ",age:" + age);

   }

  @Override

  public String queryStudent() {

      System.out.println("查询学生...");

     return"查询结果";

   }

}

StudentDao类也可以删掉
2、代理类

package com.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

//1、实现MethodInterceptor接口

publicclass DAOCglibProxy implements MethodInterceptor{

  private ObjectoriginalObject;//好像用不上哦

  public Object bind(Object obj){

     this.originalObject = obj;

      Enhancer enhancer =new Enhancer();

     //2、设置父类

      enhancer.setSuperclass(obj.getClass());

     //3、设置回调函数

      enhancer.setCallback(this);

     return enhancer.create();

   }

  @Override

  //4、重写拦截器

  public Object intercept(Object obj, Method method, Object[] params,MethodProxy proxy) throws Throwable {

      Object object =null;

      System.out.println("调用之前....");

      object = proxy.invokeSuper(obj, params);

      System.out.println("调用之后....");

     return object;

   }

}

3、测试
与上面的测试类一模一样


Spring默认使用反射机制实现动态代理,如果需要利用cglib,可以在配置文件里配置,上面代码手动调用cglib实现动态代理,并不依赖于Spring,所以也就无需修改配置文件。
0 0
原创粉丝点击