vlambda博客
学习文章列表

对Java反射的理解,应用举例:修改Tomcat Request Headers

1、什么是反射?

我们在学Java的过程中一定会遇到Java反射,不管是我们常用的框架,还是面试官的问题。多多少少都会牵扯到。那么什么才是反射?很抽象的一个概念!!没有之一,反射的功能很强大,范围很广泛,所以说它不是一句话能概括的了的。我理解反射就是:在程序运行期间,我们通过一系列操作去获取到对象所有信息(如:类、方法、成员变量等等)的操作。(感觉这样定义,把反射说的很狭义)

2、反射应用

日常业务开发很少会用到反射。但是涉及到通用模块、框架开发等肯定会涉及到反射的应用。
如我们配置数据源时通常配置的数据库驱动、Spring配置文件注入Bean时,都是通过反射来实现。

3、经典案例:我们通过反射,修改HttpServletRequest的Header值(Tomcat)


我们知道在Java中HttpServletRequest是一个接口,具体实现是由服务器去实现对应类。这里选择的是Tomcat,如果更换服务器,这个代码是不适用的。

先把全部代码贴出来:



/** * 修改request请求头信息,只适用于Tomcat * @param servletRequest request * @param headerKey 修改HeaderKey * @param headerValue 修改HeaderValue */ private int modifyHeader(HttpServletRequest servletRequest, String headerKey, String headerValue){ try { logger.info("HttpServletRequest implement class is {}", servletRequest.getClass().getName()); //获取org.apache.catalina.connector.RequestFacade类中request字段 Field requestField = servletRequest.getClass().getDeclaredField("request"); //设置字段跳过安全检查 requestField.setAccessible(true); //获取javax.servlet.http.HttpServletRequest实现类中request字段对象:org.apache.catalina.connector.Request Object requestObject = requestField.get(servletRequest);
logger.info("RequestFacade class filed request implement class is {}.", requestObject.getClass().getName()); //获取org.apache.catalina.connector.Request类中coyoteRequest字段 Field coyoteRequestField = requestObject.getClass().getDeclaredField("coyoteRequest"); //设置字段跳过安全检查 coyoteRequestField.setAccessible(true); //获取org.apache.catalina.connector.Request实现类中coyoteRequest字段对象:org.apache.catalina.connector.Request.coyoteRequest Object coyoteRequestObject = coyoteRequestField.get(requestObject);
logger.info("org.apache.coyote.Request class filed coyoteRequest implement class is {}", coyoteRequestObject.getClass().getName()); //获取org.apache.catalina.connector.Request.coyoteRequest类中headers字段 Field headersField = coyoteRequestObject.getClass().getDeclaredField("headers"); //设置字段跳过安全检查 headersField.setAccessible(true); //获取org.apache.catalina.connector.Request.coyoteRequest实现类中headers字段对象实现:org.apache.tomcat.util.http.MimeHeaders Object headersObject = headersField.get(coyoteRequestObject); //确定字段类型为:org.apache.tomcat.util.http.MimeHeaders if(headersObject instanceof MimeHeaders){ //使用MimeHeaders类方法设置Header值 MimeHeaders headers = (MimeHeaders) headersObject; //addValue只添加,setValue会遍历查找,如果不存在此Header再添加// headers.addValue(headerKey).setString(headerValue); headers.setValue(headerKey).setString(headerValue); logger.info("update request Headers success:{}", servletRequest.getHeader(headerKey)); } }catch (Exception e){ logger.error("Update Request Headers Exception; {}", e.getMessage(), e); return 1; } return 0; }




解析:

我们通过getClass().getName()知道Tomcat对HttpServletRequest的实现类是:org.apache.catalina.connector.RequestFacade。查看RequestFacade类getHeader()方法的实现和调用流程如下:


org.apache.catalina.connector.RequestFacade#getHeader方法调用的是org.apache.catalina.connector.Request#getHeader方法:

 …… /** * The wrapped request. */ protected Request request = null; …… @Override public String getHeader(String name) { if (request == null) { throw new IllegalStateException( sm.getString("requestFacade.nullRequest")); } return request.getHeader(name); } ……

对Java反射的理解,应用举例:修改Tomcat Request Headersorg.apache.catalina.connector.Request#getHeader方法调用的是org.apache.coyote.Request#getHeader方法:

 …… /** * Coyote request. */ protected org.apache.coyote.Request coyoteRequest; @Override public String getHeader(String name) { return coyoteRequest.getHeader(name); } ……

对Java反射的理解,应用举例:修改Tomcat Request Headersorg.apache.coyote.Request#getHeader方法调用的是org.apache.tomcat.util.http.MimeHeaders#getHeader方法:

…… private final MimeHeaders headers = new MimeHeaders(); public String getHeader(String name) { return headers.getHeader(name); } ……

对Java反射的理解,应用举例:修改Tomcat Request Headers在org.apache.tomcat.util.http.MimeHeaders#getHeader中我们可以看到Header的键值对是从org.apache.tomcat.util.http.MimeHeaderField中遍历获取。

 /** * The header fields. */ private MimeHeaderField[] headers = new MimeHeaderField[DEFAULT_HEADER_SIZE]; public String getHeader(String name) { MessageBytes mh = getValue(name); return mh != null ? mh.toString() : null; }  public MessageBytes getValue(String name) { for (int i = 0; i < count; i++) { if (headers[i].getName().equalsIgnoreCase(name)) { return headers[i].getValue(); } } return null; }

且在org.apache.tomcat.util.http.MimeHeaders中,我们看到此类提供了org.apache.tomcat.util.http.MimeHeaders#setValue方法返回org.apache.tomcat.util.buf.MessageBytes类,且在org.apache.tomcat.util.buf.MessageBytes中存在org.apache.tomcat.util.buf.MessageBytes#setString方法。

所以我们可以通过反射,获取到org.apache.tomcat.util.http.MimeHeaders对象,然后使用MimeHeaders对象的setValue方法进行设置Header值。


完毕!!!




欢迎点赞: