对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;
……
public String getHeader(String name) {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
return request.getHeader(name);
}
……
org.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);
}
……
org.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);
}
……
在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值。
完毕!!!
欢迎点赞: