开发者社区> 范大脚脚> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

代理模式及其在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中重复配置该拦截器,可以将该拦截器配置成一个默认拦截器栈。



本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/4828780.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SpringBoot——SpringBoot集成MyBatis
SpringBoot——SpringBoot集成MyBatis
0 0
redis在springboot中的使用
redis在springboot中的使用
0 0
Spring Boot 应用可视化监控,一目了然!
Spring Boot 应用可视化监控,一目了然!
0 0
Spring Boot 实现定时任务的 4 种方式
定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
0 0
SpringBoot【整合JSP】
SpringBoot中默认是不支持jsp的,但是我们某些情况下可以又需要用到,所以本文记录下SpringBoot整合jsp的过程
0 0
SpringBoot使用RabbitMQ(二)
springboot使用rabbitmq发送回调队列
2878 0
SpringBoot整合elasticsearch
在这一篇文章开始之前,你需要先安装一个ElasticSearch,如果你是mac或者linux可以参考https://www.jianshu.com/p/e47b451375ea,如果是windows可以自定百度一下。
1451 0
springboot(四):thymeleaf使用详解
在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用。thymeleaf 是新一代的模板引擎,在spring4.0中推荐使用thymeleaf来做前端模版引擎。 thymeleaf介绍 简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可
1108 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Java Spring Boot开发实战系列课程【第7讲】:Spring Boot 2.0安全机制与MVC身份验证实战(Java面试题)
立即下载
Java Spring Boot开发实战系列课程【第15讲】:Spring Boot 2.0 API与Spring REST Docs实战
立即下载
Spring框架入门
立即下载