• 反射时竟然NoSuchMethodException了!看这篇超详细的解决方案吧


    前几天九哥在讲Servlet时,为了灵活地使用同一个Servlet来处理对同一张表的业务操作请求,我给学生讲解了BaseServlet工具类的封装,基本实现思路有如下几个步骤。

    一. 反射封装BaseServlet工具类

    使用反射封装BaseServlet工具类,无论是哪个Servlet接收到请求,都由该类完成请求分发。因此该类的主要作用就是通过反射机制,确定我们请求的到底是哪个Servlet的哪个方法。

    1. /*
    2.  * BaseServlet 获取客户端请求的是哪个servlet的哪个方法
    3.  * */
    4. public class BaseServlet extends HttpServlet {
    5.     @Override
    6.     protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    7.         //获取客户端发来请求的标识:即要执行的方法名
    8.         String method = req.getParameter("method");
    9.         //获取方法属于哪个Servlet类
    10.         Class<? extends BaseServlet> clazz = this.getClass();
    11.         //通过类字节码对象获取要执行方法的对象
    12.         try {
    13.             Method mh = clazz.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
    14.             //执行方法
    15.             mh.invoke(this,req,resp); // this.insert(req,resp);
    16.         } catch (Exception e) {
    17.             e.printStackTrace();
    18.         }
    19.     }
    20. }

    二. 继承BaseServlet父类

    以后再创建Servlet时,我们要使任意一个Servlet类,不再直接继承HttpServlet,而是要继承统一的BaseServlet,完成请求的分发管理,例如:

    1. @WebServlet("/stuinfo")
    2. public class StuinfoServlet extends BaseServlet {
    3.     //删除方法
    4.     public void delById(HttpServletRequest req, HttpServletResponse resp) {
    5.     }
    6.     //修改学员信息
    7.     public void update(HttpServletRequest req, HttpServletResponse resp){
    8.     }
    9.     //根据学号查询方法
    10.     public void findById(HttpServletRequest req, HttpServletResponse resp)  {
    11.     }
    12.     //查询全部方法
    13.     public void findAll(HttpServletRequest req, HttpServletResponse resp)  {
    14.     }
    15.     //添加方法
    16.     public void insert(HttpServletRequest req, HttpServletResponse resp)  {
    17.     }
    18. }

    三. 异常展现

    然而有个别同学在按照上述思路自己编写代码时,却遇到了下面的NoSuchMethodException异常。他排查许久未果,于是就来找九哥帮他解决。

    四. 异常原因

    起初,九哥以为是学生从客户端发出请求时,未携带执行方法的标识或携带的方法标识与实际方法名不匹配,从而导致通过反射机制获取方法对象时报错。因为我们知道,在通过Methodmh=clazz.getMethod(method,HttpServletRequest.class,HttpServletResponse.class)获取Method对象时,必须保证方法名、参数匹配,才能找到指定的方法,否则就会出现此类异常。

    但经过排查,发现并不是以上原因,该学生的代码如下:

    1. @WebServlet("/stuinfo")
    2. public class StuinfoServlet extends BaseServlet {
    3.     //创建serivce层对象
    4.     private StuinfoService ss = new StuinfoSerivceImpl();
    5.     
    6.     //查询方法
    7.     private void findAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    8.         //调用service层查询方法
    9.         List list = ss.findAll();
    10.         //将list集合数据保存到域对象中
    11.         req.setAttribute("stuList",list);
    12.         //跳转到主页面
    13.         req.getRequestDispatcher("index.jsp").forward(req,resp);
    14.     }
    15. }

    五. 异常解决

    可能你也一眼就看到了,上述代码中,查询方法使用的是private修饰符,而私有成员在该类的外部是不能被访问的!因此我们在利用反射,通过getMethod()方法获取Method对象时就会出现NoSuchMethodException异常。所以现在的解决办法,你是不是立刻就明朗了,我们直接将private改成public就可以了

    六. 暴力反射

    上面的问题是解决了,但大家还要知道,反射机制中还有一种叫暴力反射,听起来是不是很厉害!!利用暴力反射,即使被private修饰也可以进行正常的操作。

    九哥在这里给大家再补上一刀,反射里的Constructor、Field、Method三个类都有getDeclaredXxx方法(这里的Xxx表示Constructor、Field、Method),该方法可以不受权限控制,就能够获取到类中的这些成员信息。如果我们想要使用私有的构造函数、字段、方法,则会自动访问类的isAccessable,其默认值是false,表示在访问成员时需要安全检查,如果发现是私有的则不允许访问。所以,如果我们想要访问类中的私有成员时,需要调用setAccessible(boolean flag)方法,将其改为true。这样,我们就可以对类中的私有成员进行操作了。

     *威哥Java学习交流Q群:691533824
    加群备注:CSDN推荐
          

  • 相关阅读:
    JavaScript核心Web APIs
    springboot竟然有5种默认的加载路径,你未必都知道
    Flink connector Oracle CDC 实时同步数据到MySQL(Oracle19c)
    3S技术在水文、气象、灾害、生态、环境及卫生等领域应用
    解决 CentOS 系统中的“-bash: wget: 未找到命令”问题
    《TCP/IP网络编程》(第十二章)I/O复用(1)
    私有云笔记推荐(obsidian+nas同步)
    别再到处乱放配置文件了!试试我司使用 7 年的这套解决方案,稳的一秕
    如何防止商业秘密泄露(洞察眼MIT系统商业机密防泄密解决方案)
    webpack优化系列四:vue打包后生成的chunk-vendors文件过大,利用SplitChunks插件,分离chunk
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/126782940