zoukankan      html  css  js  c++  java
  • 代理模式及其在spring与struts2中的体现

    代理模式

     

    代理模式有三个角色组成:

      1.抽象主题角色:声明了真实主题和代理主题的共同接口。

      2.代理主题角色:内部包含对真实主题的引用,并且提供和真实主题角色相同的接口。

      3.真实主题角色:定义真实的对象。

    我们先来看传统方式下一个Proxy的实现实例。

    假设我们有一个UserDAO接口及其实现类UserDAOImp:

    UserDAO.java:

    public interface UserDAO {

      public void saveUser(User user);

    }

    UserDAOImp.java:

    public class UserDAOImp implements UserDAO{

      public void saveUser(User user) {

       ……

      }

    }

    如果我们希望在UserDAOImp.saveUser方法执行前后追加一些处理过程,如启动/

    提交事务,而不影响外部代码的调用逻辑,那么,增加一个Proxy类是个不错的选择:

    UserDAOProxy.java

    public class UserDAOProxy implements UserDAO {

      private UserDAO userDAO;

      public UserDAOProxy(UserDAO userDAO) {

            this.userDAO = userDAO;

      }

      public void saveUser(User user) {

        UserTransaction tx = null;

       try {

          tx = (UserTransaction) ( new InitialContext().lookup("java/tx") );

         

          userDAO.saveUser(user);

         

          tx.commit();

        } catch (Exception ex) {

         if (null!=tx){

           try {

              tx.rollback();

           }catch(Exception e) {

            }

          }

        }

      }

    }

    UserDAOProxy同样是UserDAO接口的实现,对于调用者而言,saveUser方法的使

    用完全相同,不同的是内部实现机制已经发生了一些变化――我们在UserDAOProxy中为

    UserDAO.saveUser方法套上了一个JTA事务管理的外壳。

    上面是静态Proxy模式的一个典型实现。

    现在假设系统中有20个类似的接口,针对每个接口实现一个Proxy,实在是个繁琐无

    味的苦力工程。

    动态代理

    Dynamic Proxy的出现,为这个问题提供了一个更加聪明的解决方案。

    我们来看看怎样通过Dynamic Proxy解决上面的问题:

    public class TxHandler implements InvocationHandler {

      private Object originalObject;

      public Object bind(Object obj) {

       this.originalObject = obj;

       return Proxy.newProxyInstance(

          obj.getClass().getClassLoader(),

          obj.getClass().getInterfaces(), this);

    }

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

       throws Throwable {

        Object result = null;

       if (!method.getName().startsWith("save")) {

          UserTransaction tx = null;

         try {

            tx = (UserTransaction) ( new InitialContext().lookup("java/tx") );

            result = method.invoke(originalObject, args);

            tx.commit();

          } catch (Exception ex) {

           if (null != tx) {

             try {

                tx.rollback();

              } catch (Exception e) {

              }

            }

          }

        } else {

          result = method.invoke(originalObject, args);

        }

       return result;

      }

    }

    首先注意到,上面这段代码中,并没有出现与具体应用层相关的接口或者类引用。也就

    是说,这个代理类适用于所有接口的实现。

    其中的关键在两个部分:

             1.

    return Proxy.newProxyInstance(

          obj.getClass().getClassLoader(),

          obj.getClass().getInterfaces(), this);

    java.lang.reflect.Proxy.newProxyInstance方法根据传入的接口类型

    (obj.getClass().getInterfaces()) 动态构造一个代理类实例返回, 这个代理类是JVM

    在内存中动态构造的动态类,它实现了传入的接口列表中所包含的所有接口。

             2

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

       throws Throwable {

    ……

        result = method.invoke(originalObject, args);

       ……

    return result;

      }

      InvocationHandler.invoke方法将在被代理类的方法被调用之前触发。通过这个方

    法中,我们可以在被代理类方法调用的前后进行一些处理,如代码中所示,

    InvocationHandler.invoke方法的参数中传递了当前被调用的方法(Method) ,以及被

    调用方法的参数。

      同时,我们可以通过Method.invoke方法调用被代理类的原始方法实现。这样,我们

    就可以在被代理类的方法调用前后大做文章。

      在示例代码中,我们为所有名称以“save”开头的方法追加了JTA事务管理。

    Spring中AOP实现

    其中最典型的就是Spring中的配置化事务管理,先看一个例子

    <beans>

      <bean id="dataSource"

       class="org.apache.commons.dbcp.BasicDataSource"

       destroy-method="close">

       <property name="driverClassName">

         <value>org.gjt.mm.mysql.Driver</value>

       </property>

       <property name="url">

         <value>jdbc:mysql://localhost/sample</value>

       </property>

       <property name="username">

         <value>user</value>

       </property>

       <property name="password">

         <value>mypass</value>

       </property>

      </bean>

      <bean id="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

       <property name="dataSource">

         <ref local="dataSource" />

       </property>

      </bean>

      <bean id="userDAO" class="net.xiaxin.dao.UserDAO">

       <property name="dataSource">

         <ref local="dataSource" />

       </property>

      </bean>

      <bean id="userDAOProxy"

      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

       <property name="transactionManager">

         <ref bean="transactionManager" />

       </property>

       <property name="target">

         <ref local="userDAO" />

       </property>

       <property name="transactionAttributes">

         <props>

           <prop key="insert*">PROPAGATION_REQUIRED</prop>

           <prop key="get*">

    PROPAGATION_REQUIRED,readOnly

    </prop>

         </props>

       </property>

      </bean>

    </beans>

    看看例子当中的userDAOProxy,想必大家已经猜测到Spring事务管理机制的实现原理。

      是的,只需通过一个Dynamic Proxy对所有需要事务管理的Bean进行加载,并根据配

    置,在invoke方法中对当前调用的方法名进行判定,并为其加上合适的事务管理代码,那

    么就实现了Spring式的事务管理。

      当然,Spring中的AOP实现更为复杂和灵活,不过基本原理一致。

    Stuts2中的AOP实现

    拦截器是Strut2的一个重要组成部分,对于Strut2框架而言,可以将其理解为一个空的容器,正是大量的内建拦截器完成该框架的大部分操作。比如,params拦截器将http请求中的参数解析出来,设置成action的属性;servlet-config直接将http请求中的httpServletRequest实例和httpServletRespon实例传给action;fileUpload拦截器负责解析请求当中的文件域,并将文件域设置为action的属性。。。。。。

    对于Strut2的拦截器体系而言,当我们需要某个拦截器的时候,只需要在配置文件中应用该拦截器即可,反之亦然。不管是否应用某个拦截器,对于整个Strut2框架都不会影响,这种设计哲学,是一种可插拔的设计,具有非常好的可扩展性。

    Strut2中的拦截器体系是一种AOP设计哲学,它允许开发人员以一种简单的方式来进行AOP方式的开发。

    下面以一个例子来介绍Strut2中的AOP。

    使用拦截器完成权限控制

    l         实现拦截器

    大部分web应用都涉及权限控制,当浏览者需要执行某个操作时,应用需要先检查浏览者是否登录,以及是否有足够的权限来执行该操作。

    本示例要求用户登录,必须为制定用户名才可以查看系统中某个视图资源,否则直接转入登录页面。对于上面的需求,可以在每个action执行业务逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用。因为大部分action中检查权限代码都大同小异,故将这些代码逻辑放在拦截器中,将会更加优雅。

    检查用户是否登录,通常都是跟踪用户的Session来完成的,通过ActionContext即可访问到Session中的属性,拦截器的intercep(ActionInvocation invocation)方法的invocation参数可以很轻易的访问到请求相关的ActionContex实例。

    //权限检查拦截器继承AbstractIntercept

    Public class AuthorityInterceptor extends AbstractInterceptor{

       //拦截action处理的拦截方法

       Public String intercept(ActionInvocation invocation){

          //取得请求相关的ActionContex实例

           ActionContext ctx = invocation.getInvocationContext();

           Map session = ctx.getSession();

           //取出名为user的session属性

           String user = (String)session.get(“User”);

           If(user !=null && user.equals(“scott”) ){

    Return invocation.invoke();

    }

    Ctx.put(“tip”,”您还没有登录,请输入scott、tiger登录”);

    Return Action.Login;

    }

    上面的拦截器代码非常简单,先从ActionInvocation 取得用户的session实例,然后从中取出user属性,通过判断属性值确定用户是否登录,从而判断是否转入登录页面。

    l         配置拦截器

    一旦实现了拦截器,就可以在所有需要实现权限控制的action中复用上面的拦截器。

    为了使用拦截器,首先需要在struts.xml文件中定义该拦截器,定义拦截器的配置片段如下:

    <interceptors>

         <interceptor name=”authority” class=”qj.AuthorityInterceptor” />

    </interceptors>

    定义了拦截器后,可以在action中应用该拦截器,应用拦截器的配置片段如下:

    <action name=”viewBook”>

       <result>/WEB-INF/jsp/viewBook.jsp</result>

       <interceptor-ref  name=”defaultStack” />

       <interceptor-ref  name=”authority” />

    </action>

    这种通过拦截器控制权限的方式,显然具有更好的代码复用。

    如果为了简化struts.xml文件的配置,避免在每个action中重复配置该拦截器,可以将该拦截器配置成一个默认拦截器栈。

  • 相关阅读:
    PAT 1006 Sign In and Sign Out
    PAT 1004. Counting Leaves
    JavaEE开发环境安装
    NoSql数据库探讨
    maven的配置
    VMWARE 下使用 32位 Ubuntu Linux ,不能给它分配超过3.5G 内存?
    XCODE 4.3 WITH NO GCC?
    在苹果虚拟机上跑 ROR —— Ruby on Rails On Vmware OSX 10.7.3
    推荐一首让人疯狂的好歌《Pumped Up Kicks》。好吧,顺便测下博客园可以写点无关技术的帖子吗?
    RUBY元编程学习之”编写你的第一种领域专属语言“
  • 原文地址:https://www.cnblogs.com/yaowen/p/4828780.html
Copyright © 2011-2022 走看看