V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
onice
V2EX  ›  Java

请教大家使用 Java 反射封装 Servlet 的问题

  •  
  •   onice · 2016-05-17 19:16:23 +08:00 · 4224 次点击
    这是一个创建于 3103 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果一个请求对应一个 Servlet 的话,工程大了, Servlet 的类就会很多,不方便管理。 虽然可以使用条件判断将多个请求处理写到一个 Servlet 类中,但这样代码太不美观。

    Servlet 的请求是由 service 方法接收,然后再根据请求的类型转给 doGet 和 doPost 等方法。今天看到一种基于反射的写法:这种写法覆盖了 service 方法,在 Service 方法中利用 Java 反射的机制改变请求的转向。

    贴代码:

    package com.hack4b.servlet;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 封装 Servlet ,完成对任意用户的请求进行处理
     */
    
    public class BaseServlet extends HttpServlet {
    
        /**
         * 
         */
        private static final long serialVersionUID = -1521560009181973222L;
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置编码
            req.setCharacterEncoding("UTF-8");
            //定义用户请求参数
            String v =req.getParameter("v");
            //定义响应的方法
            Method method = null;
            try {
                //得到方法
                method = this.getClass().getMethod(v, HttpServletRequest.class,HttpServletResponse.class);
            } catch (NoSuchMethodException | SecurityException e) {
                System.out.println("反射错误!程序已退出!");
                e.printStackTrace();
                return;
            }
            try {
                //获取方法的执行结果
                String result = (String)method.invoke(this,req,resp);
                //处理结果
                if(result!=null&&!result.trim().isEmpty()){
                    int index = result.indexOf(":");
                    String param = result.substring(0,index);
                    String path = result.substring(index+1);
                    if(param.equals("f")){
                        req.getRequestDispatcher(path).forward(req, resp);
                    }else if(param.equals("r")){
                        resp.sendRedirect(path);
                    }
                }
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                System.out.println("方法执行失败!");
                e.printStackTrace();
            }
        }
    }
    

    其他的Servlet就继承这个类,在访问 Servlet 的时候加上参数,例如: http://localhost:8080/users.do?v=queryId 其中, queryId 是继承上述代码的一个子类中的方法。

    按照这么写的话,这样子实际上是使用 Java 的反射机制去调用了子类的方法。

    于是我自写了一个 Demo , Demo 中由两个类,一个是 Base 基类,一个是继承 Base 的 Sub 派生类,然后再进行反射机制的调用,结果发现使用 Java 反射不能去调子类的方法。 抛出异常: java.lang.NoSuchMethodException: com.hack4b.test.Base.testSubMethod() 意思就是说找不到 Base 子类的方法。

    我就纳闷了,,既然反射不能去调派生类的方法,那么那个用反射实现的 Servlet 怎么可以?

    第 1 条附言  ·  2016-05-17 20:35:17 +08:00

    附上我自己写的Demo代码:

    package com.hack4b.test;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Base {
    	
    	public void m01(){
    		try {
    			Method m = this.getClass().getMethod("testSubMethod");
    			m.invoke(this);
    		} catch (NoSuchMethodException | SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalArgumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (InvocationTargetException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    	}
    	
    }
    
    package com.hack4b.test;
    
    public class Sub extends Base {
    
    	public void testSubMethod(){
    		System.out.println("testMethod runed.");
    	}
    }
    
    package com.hack4b.test;
    
    public class Main {
    	
    	public static void main(String[] args) {
    		Base base = new Base();
    		base.m01();
    	}
    	
    }
    
    14 条回复    2016-06-20 19:33:33 +08:00
    sagnitude
        1
    sagnitude  
       2016-05-17 20:18:08 +08:00
    1. demo 呢?
    2. 反射可以调子类方法
    3. 如果你用的是 getMethod ,试试 getDeclaredMethod
    4. 如果你用的不是 this.getClass(),改成这个
    5. 别忘了加上 method.setAccessible(true)
    6. this.getClass()要求在实例方法里运行,如果是 static 方法,需要想办法得到子类的 Class 对象,比如传进来一个 instance 再 getClass(),或者用泛型,然后 getGenericSuperclass(),然后 getActualTypeArguments()然后 getRawType()
    sagnitude
        2
    sagnitude  
       2016-05-17 20:19:57 +08:00
    murmur
        3
    murmur  
       2016-05-17 20:20:27 +08:00
    你都想到了这一层了 说明你需要 springmvc 了
    KuroNekoFan
        4
    KuroNekoFan  
       2016-05-17 20:26:08 +08:00 via iPhone
    aop
    onice
        5
    onice  
    OP
       2016-05-17 20:35:46 +08:00
    @sagnitude 已附上我的 Demo ,请指教
    Comdex
        6
    Comdex  
       2016-05-17 20:45:16 +08:00
    用 getDeclaredMethod 和 method.setAccessible(true)
    sagnitude
        7
    sagnitude  
       2016-05-17 20:46:08 +08:00   ❤️ 1
    @onice 你这个 Base 和 Sub 没有继承关系啊……
    而且
    ```
    Base base = new Base();
    ```
    应该是
    ```
    Base base = new Sub();
    ```
    然后就可以了……
    sagnitude
        8
    sagnitude  
       2016-05-17 20:47:31 +08:00
    @onice 修正,我看错了…是继承的,拷代码的时候漏了……
    Comdex
        9
    Comdex  
       2016-05-17 20:48:22 +08:00
    @sagnitude 同 7 楼,这是个 bug
    onice
        10
    onice  
    OP
       2016-05-17 21:49:44 +08:00
    @sagnitude 看你这么写,瞬间秒懂了。。。
    pixstone
        11
    pixstone  
       2016-05-17 22:32:31 +08:00
    = =为什么要用反射?直接 一个 Map ,
    Key 为 Method , Value 为 执行流程的 Handler 对象即可。
    至于 Map 的构建可以用 Spring 注入,不用人工写。
    当然用上了 Spring 就直接 SpringMVC 吧,基本上就是你设想的方案来处理的。只是具体实现上用 Spring 的 IoC 来注入,不是直接走原始的反射方案。
    sunyue
        12
    sunyue  
       2016-05-18 10:21:50 +08:00
    感觉楼主这个思路和 DWR 没什么区别啊?
    zhen14
        13
    zhen14  
       2016-06-08 17:09:49 +08:00
    最近的一个功能中跟 lz 用相同的思路
    @sunyue DWR 有什么优势吗?
    sunyue
        14
    sunyue  
       2016-06-20 19:33:33 +08:00
    @zhen14 简单说就是直接使用 js 代码调用后台代码,在代码的书写上是比较方便的。主要就是这一点吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1761 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:46 · PVG 00:46 · LAX 08:46 · JFK 11:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.