动态代理在WEB与JDBC开发中的应用(WEB篇)

WEB案例

目前有一个2005年开始,基于Struts1的Web项目A,其验证部分依赖于主站的SSO(单点登录)。在请求站点A的时候,用户会被强制带去做SSO验证,通过身份验证后后,主站会自动地把请求转发至A站点,并在request header中添加了用于保存登录用户ID的新属性SM_USER,然后A站点根据用户ID提供相应的服务。由于该项目是一个既存项目,所以其中残余大量像下面一样的测试代码。

String user_id = request.getHeader("XX_USER");
if (user_id == null) {
    user_id = "my_hard_coded_user_id";
}
UserProfile userProfile = new BizDao().getUserProfile(user_id);

其根本原因是在本地测试的时候无法于生产环境的SSO对接,只有把代码提交至公共的DEV服务器、UAT服务器或PROD服务器后,才能享受SSO为A站所提供的XX_USER数据,所以程序员在无法取得header数据的情况下,直接简单粗暴地对本地环境进行了硬编码处理。在只有几个人的小团队,这样的处理可能看起来也无所谓,但长年下来经手的人数也相当可观了,很多人都习惯使用自己的ID做测试,所以在SVN的历史版本中,硬编码的ID从A改到B、改到C、改到D……

问题分析

每个人都根据自己的喜好选择了使用自己的ID或是他人的ID,那么是否有一种办法可以统一接口一劳永逸呢?可能最容易想到的就是HttpServletRequest.setHeader,可惜HttpServletRequest并没有这样的API,为啥?个人猜测可能是因为request源于客户端,服务器端应该保持请求的原始性、纯净性和无毒性吧,而HttpServletResponse(http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html)则是在应用程序的控制之下,所以程序员可以对其任意践踏,setHeader、addHeader以及getHeader、getHeaders、getHeaderNames。既然没有API可用,直接进行包装代理吧,在Google的帮助下我们可以找到现成的方案来对request中的header进行重新定制,重点是HttpServletRequestWrapper类的实现。

http://vangjee.wordpress.com/2009/02/25/how-to-modify-request-headers-in-a-j2ee-web-application/

其实这个方案是在我实现动态代理方案之后找到的,两者思路几乎是一样的,都是对原始request进行包装代理,重新实现getHeader方法。

解决方案

不废话,直接上代码!

private static class RequestInvocationHandler implements InvocationHandler {
    private HttpServletRequest wrappedRequest;
    public RequestInvocationHandler(HttpServletRequest r) {
        wrappedRequest = r;
    }
    public static String dummyData = "my_hard_coded_id";
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getHeader".equals(method.getName()) && args.length == 1 && "SM_USER".equals(args[0])) {
        	return dummyData;
        }
        return method.invoke(wrappedRequest, args);
    }
    public static HttpServletRequest createRequestWapper(HttpServletRequest r) {
    	if (null != r.getParameter("u")) {
    		dummyData = r.getParameter("u");
    	}
        return (HttpServletRequest)(Proxy.newProxyInstance(HttpServletRequest.class.getClassLoader(),
            new Class[] {HttpServletRequest.class},
                new RequestInvocationHandler(r)));
    }
}

上面代码实现了对Request对象的代理,所有针对Request对象的调用都需经过invoke方法。在invoke方法中,我们可以针对不同的方法签名进行更为细粒度的控制。比如在WEB案例中提到的问题,可以专门针对getHeader方法进行重新定制,本来Request Header中并无该数据,但我们可以硬生生地“造”出数据。另外除了可以“造”出测试数据外,还可以通过请求中携带的参数“u”动态地进行数据修改,这样就实现了用户切换的功能。

既然代理类的问题解决了,下面该谈谈应在何时何地植入代理对象了。何时何地可以理解为切入时机,其实最初的是在org.apache.struts.action.RequestProcessor中植入代理对象的,但是在使用过程中发现该实现有一个弊端,那就是在非Struts请求时无法使用代理对象,比如直接访问JSP文件或是其它Servlet所提供的服务路径。这时自然而然地想到Filter,而使用Filter也是一痛,加入一个全新的Filter吧,有点破坏的整体设计的感觉,而放入其它Filter之中的话,看起来又不伦不类。但好歹是测试用,这两个方案可以折衷选择其一,具体实现如下。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
	request = RequestInvocationHandler.createRequestWapper((HttpServletRequest)request);
	chain.doFilter(request, response);
}

整体工作流程如下,用户首先发出请求,然后request对象在filter中被替换成代理对象,替换后的request被传入doFilter方法,之后不论是Servlet、Struts还是JSP,它们用到的都是我们定制的Request对象。

           +---------+         +---------+
           |         |         |         |
 Request   |         | +-----> | Servlet |
+--------> |  Filter |         |         |
           | +----+  |         |  Struts |
           | Request |         |         |
 Response  |      is |         |         |
<--------+ | wrapped |         |         |
           |    here | <-----+ |         |
           |         |         |         |
           +---------+         +---------+

后记

当然,如果想要修改request对象不止这一种方法,比如正统的方式是定义一个HttpServletRequestWrapper来重新定义request,具体案例请参考http://vangjee.wordpress.com/2009/02/25/how-to-modify-request-headers-in-a-j2ee-web-application/

时间: 2024-08-28 17:32:53

动态代理在WEB与JDBC开发中的应用(WEB篇)的相关文章

动态代理在WEB与JDBC开发中的应用(JDBC篇)

背景描述 如果之前看过<动态代理在WEB与JDBC开发中的应用(WEB篇)>,这篇的内容可以全当是另一种应用的进阶举例,而在实现上确实没有太多进步的地方.我们先看一下项目所面临问题以及期望解决方案.在作者所接触的这个项目中,直接使用原始JDBC技术,java.sql.PreparedStatement和java.sql.ResultSet几乎占领了数据访问层,没有半点OR Mapping的迹象,看起来是不是很悲催?命啊-在项目开发至一半的时候,突然发现要对日文字符进行支持,而在之前一直使用英文

动态代理在WEB与JDBC开发中的应用

WEB案例 目前有一个2005年开始,基于Struts1的Web项目A,其验证部分依赖于主站的SSO(单点登录).在请求站点A的时候,用户会被强制带去做SSO验证,通过身份验证后后,主站会自动地把请求转发至A站点,并在request header中添加了用于保存登录用户ID的新属性SM_USER,然后A站点根据用户ID提供相应的服务.由于该项目是一个既存项目,所以其中残余大量像下面一样的测试代码. [java] view plain copy String user_id = request.g

Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只能为接口创建代理实例. 如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生成被代理接口的新的匿名实现类. JDK动态代理具体实现原理: 通过实现InvocationHandlet接口创建自己的调用处理器: 通过为Proxy类指定ClassLoader对象和一组in

web多终端开发学习系列(四)--- web图表插件

对于数据的显示除了可以用表格外,还可以使用图表来更好.更直观地表达数据,比如数据的趋势可以用折线图,数据的比例可以用饼图等等.所以在web的开发过程中图表的应用非常广泛,对于图表的js框架我找到了Chart.js和Highcharts.Chart支持响应式布局,而Highcharts不支持,但是Highcharts的功能更全面. 介绍 Chart.js的官网是:http://www.bootcss.com/p/chart.js/,Chart.js不依赖于第三方类库,只需导入Chart.js即可.

spring,web,java 开发中乱码解决方案

修改HTML/JSP页面编码格式: <meta charset="UTF-8">或<%@ page contentType="text/html; charset=UTF-8"%> web.xml中的编码设置(  需要设置forceEncoding参数值为true,强制以目标编码为编码格式 ) <filter>        <filter-name>CharacterEncoding</filter-name&

在Filter中使用动态代理解决请求中文乱码问题

使用动态代理解决请求中文乱码问题 1.增强一个类我们常用的几种解决方案: 1.继承 a) 优点简单 b) 必须有被增强类的实现类 2.装饰者模式 a) 要求:1实现相同的接口.2持有被增强的对象 b) 优点:不必知道被增强的实现是谁 c) 缺点:必须实现所有没被增强方法的原始对象的原样调用 3.动态代理 a) 要求:1实现相同接口,2持有被增强的对象 b) 优点:不必手动实现所有不增强方法的原样调用.对方法进行增强时有类似过滤器的功能. c) 缺点:学习成本高. 代理(Proxy): 一个代理对

java中的动态代理(一)

今天我们来学习java中的另一个重要的特性叫做动态代理.所谓动态代理是可以在java运行过程中动态的创建一个类去实现一个或多个接口,可以在不修改原有类代码的基础上动态的添加功能和方法.正是因为这个特性使动态代理被java的许多框架中被广泛的使用. 在介绍动态代理之前我需要先介绍一下什么是静态代理.在设计模式那一章我曾经介绍过一种设计模式叫做代理模式.在代理模式中有一个公共的接口,代理对象和实际的对象都需要去实现这个接口.在代理对象中有一个成员属性变量指向实际对象并在代理对象的构造函数中初始化,在

MyBatis - Mapper动态代理开发

采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法. - Mapper接口开发需要遵循以下规范: ① Mapper.xml文件中的namespace与mapper接口的全类名相同. ② Mapper接口方法名和Mapper.xml中定义的statement的id相同. ③ Mapper接口方法的输入参数类型和mapper.xml中定义的statement的paramet

Java中动态代理技术生成的类与原始类的区别 (转)

用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后来定义的逻辑.这样就达到了动态的在原有类上增加一些功能.比如日志打印,拦截信息等. 这里只关心动态代理技术生成新的类,先不管虚拟机是如何去生成类,用了什么字节码生成技术,怎么产生字节码等这一系列动作.现在只关心最后生成的新类长什么样,它和老类有什么区别.为了获取到生成后的代理类的字节码并且反编译成我