1、什么是反射?

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

2、反射应用

我们在Coding过程中获取一个对象的方法、属性等通常使用对象+"."的方式,我们需要一个对象的时候,通常使用new的方式去创建对象,但如果一个接口,面向无数个实现类的时候,我们并不知道使用那个类,以上情况下,使用反射来操作。
日常业务开发很少会用到反射。但是涉及到通用模块、框架开发等肯定会涉及到反射的应用。如我们配置数据源时通常配置的数据库驱动、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);
    }
    ……
  • 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值。

完毕!!!