动态代理在WEB与JDBC开发中的应用(WEB篇)
2014-09-02 15:07
330 查看
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/相关文章推荐
- 动态代理在WEB与JDBC开发中的应用(JDBC篇)
- 动态代理在WEB与JDBC开发中的应用
- 动态代理在WEB与JDBC开发中的应用(JDBC篇)
- MSSQL2000的官方JDBC在应用动态代理机制遇到的异常问题解决
- 【Java EE 学习 24 下】【注解在数据库开发中的使用】【反射+注解+动态代理在事务中的应用service层】
- 动态代理在JDBC的DataSource中的应用
- 在Web应用中动态创建PDF文件-Java基础-Java-编程开发
- 分布式Web应用----基于Socket+动态代理实现简单RPC 生产者消费者模型
- MSSQL2000的官方JDBC在应用动态代理机制遇到的异常问题解决
- 《精通Spring4.X企业应用开发实战》读后感第七章(AOP基础知识、jdk动态代理,CGLib动态代理)
- JavaWeb开发知识总结(annotation,Servlet3.0,文件上传,动态代理)
- JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射
- 【框架】[Spring] 基于Spring框架的Web应用演示(附带cglib工具进行动态代理)
- 2014-07-15 Java Web的学习(12)-----JDBC数据源的详解以&包装模式&动态代理
- Web表格应用系统开发套件
- 架构Web Service:基于Web服务的应用、解决方案和开发平台
- Servlet开发中JDBC的高级应用
- AJAX开发的性能冲击:使用AJAX提高WEB应用的带宽使用率
- [原创]ASP Snail - WEB应用快速开发框架
- Servlet开发中JDBC的高级应用