【Java】代理模式

来源:互联网 发布:网络安全法与等级保护 编辑:程序博客网 时间:2024/06/15 20:41

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


 

【分类】

         代理模式分为静态代理、动态代理。

         静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

         动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

【静态代理实例】


接口类:

public interface UserManager {/** * 添加 用户 *  * @param userId * @param userName */public void addUser(String userId, String userName);}

委托类(实现类):

public class UserManagerImpl implements UserManager {/** * 添加 用户 *  * @param userId * @param userName */public void addUser(String userId, String userName) {try {System.out.println("UserManagerImpl.addUser() userId-->>" + userId);} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}}}

代理类:

/** * 代理类 *  * @author happy *  */public class UserManagerImplProxy implements UserManager {private UserManager userManager;public UserManagerImplProxy(UserManager userManager) {this.userManager = userManager;}/** * 添加 */public void addUser(String userId, String userName) {try {System.out.println("start-->>addUser() userId-->>" + userId);userManager.addUser(userId, userName);System.out.println("success-->>addUser()");} catch (Exception e) {e.printStackTrace();System.out.println("error-->>addUser()");}}}

客户端类:

public class Client {/** * @param args */public static void main(String[] args) {UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());userManager.addUser("0001", "张三");}}

【问题】

       静态代理存在问题:

第一,需要建立大量的代理类

第二,这只是一个添加方法,如果再加入其它方法,岂不是会有很多重复的代码。

【动态代理实现】

         动态代理,就是在运行过程中动态的创建一个类似于上文静态代理中的代理类的过程。JDK动态代理只能对实现了接口的类进行代理。

        动态代理类是一个实现在创建类时在运行时指定的接口列表的类,代理接口是动态代理类实现的一个接口。代理实例(相当于静态代理中的委托类)是动态代理类的一个实例。每个代理实例都有一个关联的调用处理程序对象(橙汁为Handler类),Handler类可以实现接口InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到代理实例的调用处理程序的Invoke方法,并传递代理实例、识别调用方法的java.lang.reflect.Method对象以及包含参数的Object类型的数组。


接口类和委托类代码如上例静态代理。略。

动态代理类:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class LogHandler implements InvocationHandler {private Object targetObject;public Object newProxyInstance(Object targetObject) {this.targetObject = targetObject;return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),   targetObject.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("start-->>" + method.getName());for (int i=0; i<args.length; i++) {System.out.println(args[i]);}Object ret = null;try {//调用目标方法ret = method.invoke(targetObject, args);System.out.println("success-->>" + method.getName()); }catch(Exception e) {e.printStackTrace();System.out.println("error-->>" + method.getName());throw e;}return ret;}}

客户端类:

public class Client {/** * 动态代理 *  * @param args */public static void main(String[] args) {LogHandler logHandler = new LogHandler();UserManager userManager = (UserManager) logHandler.newProxyInstance(new UserManagerImpl());String name = userManager.findUser("0001");System.out.println("Client.main() --- " + name);}}

动态代理主要用到的两个类,具体方法介绍如下:

1.InvocationHandler

InvocationHandler是代理实例的调用处理程序实现的接口。

2.newProxyInstance

(1)语法结构

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException

(2)详解

①参数:loader定义了代理类的类加载器,interfaces是指本代理类要实现的接口列表,h指派方法调用的调用处理程序,也就是实现动态创建代理类的方法,在本文中的两个实例中都指的是方法本身,故用this来代指。

②返回值:返回一个带有代理类的指定调用处理程序的代理实例,也就是委托类。

3.invoke

(1)语法结构:

Object invoke(Object Proxy,Method method,Object[] args)throws Throwable

(2)详解:

①参数:Proxy是在其上调用方法的代理实例,method相当于代理接口的抽象。args保存的是对象数组,用数组中传递的参数来指定具体的委托类方法。

②返回值:该方法的返回值类型与代理接口方法的返回类型要兼容。

        简单说,就是在invoke方法内,展示我们想要对委托类进行的处理方法,然后根据一些参数什么的,调用相应的委托类方法,然后返回相应的结果。

【动态代理的事务应用】

原添加方法如下:

public void addFlowCard(FlowCard flowCard) throws ApplicationException {Connection conn = null;try {// 取得Connectionconn = ConnectionManager.getConnection();// 开始事务ConnectionManager.beginTransaction(conn);// 生成流向单单号String flowCardVouNo = flowCardDao.generateVouNo();// 添加流向单主信息flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);// 添加流向单明细信息flowCardDao.addFlowCardDetail(flowCardVouNo,flowCard.getFlowCardDetailList());// 提交事务ConnectionManager.commitTransaction(conn);} catch (DaoException e) {// 回滚事务ConnectionManager.rollbackTransaction(conn);throw new ApplicationException("添加流向单失败!");} finally {// 关闭Connection并从ThreadLocal中清除ConnectionManager.closeConnection();}}
同类的方法还有很多,增删改查等,并且都需要就在调用方法前开始事务,在方法正确返回时提交事务等,这时,我们可以使用动态代理对代码进行抽象和隔离。提取一个事务类,如下:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.sql.Connection;/** * 采用动态代理封装事务 * @author Administrator * */public class TransactionHandler implements InvocationHandler {private Object targetObject;public Object newProxyInstance(Object targetObject) {this.targetObject = targetObject;return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),   targetObject.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Connection conn = null;Object ret = null;try {//从ThreadLocal中取得Connectionconn = ConnectionManager.getConnection();if (method.getName().startsWith("add") ||method.getName().startsWith("del") ||method.getName().startsWith("modify")) {//手动控制事务提交ConnectionManager.beginTransaction(conn);}//调用目标对象的业务逻辑方法ret = method.invoke(targetObject, args);if (!conn.getAutoCommit()) {//提交事务ConnectionManager.commitTransaction(conn);}}catch(ApplicationException e) {//回滚事务ConnectionManager.rollbackTransaction(conn);throw e;}catch(Exception e) {e.printStackTrace();if (e instanceof InvocationTargetException) {InvocationTargetException ete = (InvocationTargetException)e;throw ete.getTargetException();}//回滚事务ConnectionManager.rollbackTransaction(conn);throw new ApplicationException("操作失败!");}finally {ConnectionManager.closeConnection();}return ret;}}
同时原添加方法经过修改,也可以只专注于自己的业务啦,如下:

public void addFlowCard(FlowCard flowCard) throws ApplicationException {try {// 生成流向单单号String flowCardVouNo = flowCardDao.generateVouNo();// 添加流向单主信息flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);// 添加流向单明细信息flowCardDao.addFlowCardDetail(flowCardVouNo,flowCard.getFlowCardDetailList());} catch (DaoException e) {throw new ApplicationException("添加流向单失败!");}}

【小结】

        1.代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。

        2.代理类还可以附加一些内务处理,例如对委托类进行预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。具体的例如日志、延迟加载、监视状态等。

1 0
原创粉丝点击