泛型与反射机制在JDBC和Servlet编程中的实践

2023-12-05

写在前面:

泛型与反射是java中的两种强大机制,可以很好的提高代码的灵活性和复用性。本篇文章向大家展现在JDBC和Servlet编程场景下反射和泛型技术的实践。通过灵活使用这两种机制打造 高度可复用的JDBC和Servlet代码!

1.JDBC中的实践

使用JDBC连接数据库后我们一般要编写方法对数据库进行CRUD操作。一般的我们需要编写两个方法:

  • 一个负责查询操作,传入SQL,返回实体类对象的列表(对应数据表中的多行)
  • 一个负责增删改操作,传入SQL,返回一个int值表示影响数据表中的行数

仔细思考查询操作对应的方法就会引出一些问题。我们可以发现,对于不同数据表,表的字段数量不同,各字段的数据类型不同,名称不同,我们需要构造的实体类对象不同,返回的对象列表类型也不同......这种种的不同就为查询方法的编写带来了麻烦,难道我们需要为每个表的查询都编写一个查询方法吗?对只有1,2个表的情况当然可以这样做,但当表的数量较多时这显然不是一个可取的做法。

我们期望只写一个高度通用的查询方法,能应对所有数据表的查询(查询一个表中的若干行)。使用泛型与反射机制可以实现这一目的!泛型机制主要用来在方法内部分析实体类对象结构,以及创建实体类对象。

/**
     * 通用的数据表查询方法
     * @param clazz 实体类.class。在该方法中使用反射机制分析其结构
     * @param sql SQL语句结构
     * @param args SQL语句参数
     * @return 查询结果列表
     * @param <T> 实体类类型
     */
    protected  <T> List<T> baseQuery(Class clazz, String sql, Object ... args){
        //将要返回的实体类对象列表
        List<T> list =new ArrayList<>();
        //获取JDBC连接,可专门写一个类对连接进行管理
        Connection connection = ConnectionManager.getConnection();
        PreparedStatement preparedStatement=null;
        ResultSet resultSet =null;
        try {
            // SQL语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置SQL语句上的参数
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i+1,args[i]);
            }
            // 执行数据表查询
            resultSet = preparedStatement.executeQuery();
            //获取列信息(有多少列)
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            // 将结果集通过反射封装成实体类对象
            while (resultSet.next()) {   //while循环遍历行
                //使用反射实例化对象
                Object obj =clazz.getDeclaredConstructor().newInstance();
                for (int i = 1; i <= columnCount; i++) {   //for循环遍历列
                    //属性名
                    String columnName = metaData.getColumnLabel(i);
                    //获取属性值
                    Object value = resultSet.getObject(columnName);
                    //实体类字段名与表中属性名一一对应,通过反射获取之
                    Field field = clazz.getDeclaredField(columnName);
                    //绕开访问权限
                    field.setAccessible(true);
                    //实体类字段赋值
                    field.set(obj,value);
                }
                //加入要返回的实体类对象列表
                list.add((T)obj);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //资源回收
            if (null !=resultSet) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            ConnectionManager.releaseConnection(connection);
        }
        return list;
    }

2.Servlet中的实践

我们使用Servlet类的惯用方式是,自定义一个继承HttpServlet类的类,重写父类的service方法(protected修饰的)(重写doGet/doPost也可以),方法体中编写对请求的处理逻辑。在MVC软件架构模式下,Servlet类一般出现在Controller层。

现在我们构想这样的一个场景,前端需要向后端发请求进行数据库的CRUD操作。试想这样的场景下应该怎样编写Servlet类。为每种操作各编写一个Servlet类并分别配置,让前端去请求不同的Servlet类获取服务?这应该是最容易想到的一种写法,但显然配置这么多Servlet类显然不够科学。现在我们 利用反射机制进行优化。

首先我们创建一个BaseController类,它继承HttpServlet,并重写service方法

public class BaseController extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求映射路径
        String requestURI=req.getRequestURI();   
        //获取请求映射路径中的统配符部分/experiment/*,本示例中*的值为add,del,alter,query 
        //统配符部分的名称与子类中方法名对应
        String[] split=requestURI.split("/");     
        String methodName=split[split.length-1];  
        
        //使用反射机制进行方法调用
        Class clazz=this.getClass();
        try{
            Method declaredMethod=clazz.getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
            declaredMethod.setAccessible(true);
            declaredMethod.invoke(this,req,resp);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

然后创建一个Controller类它继承自BaseController

@WebServlet("/experiment/*")    //add  del  alter query
public class Controller extends BaseController {
    private Service service=new Service();
    public void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
    public void del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
    public void alter(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
    public User query(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
}

有两个关键点要尤为注意:

  1. Controller类注解中请求映射路径的写法
  2. Controller类中方法的名称

映射路径需要写为这种两级路径形式 /experiment/* ,并且第二级采用通配的形式。现在Controller类可以匹配以/experiment/开头的请求,例如/experiment/add,/experiment/del...在本示例设置的场景下,客户端的增删改查请求分别对应/experiment/add,/experiment/del,/experiment/alter,/experiment/query路径,都会匹配到Controller类。注意Controller类是继承自BaseController类的,所以这些不同的请求都会先汇聚到BaseController类的service方法中。

设计的精髓在于,在BaseController的service方法中利用反射机制决定调用子类Controller中的哪个方法处理请求 。为达到这一目的,Controller类中方法的名字就不能乱起,必须与请求路径中的统配符的具体值一一对应。因为在BaseController中就是通过提取请求路径中的统配符值作为方法名使用反射机制进行方法调用的!

好了,现在大家回过头看看上面的两段代码,应该是比较清晰喽!

当然以上这些写法只是为简单场景提供了方便,复杂场景下可能还是需要改进。例如JDBC示例中反射的频繁使用可能会在数据量大时产生性能问题。但是这两个例子对于加深java反射机制的理解以及融汇贯通JDBC, Servlet这样的原生技术还是有很大帮助的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

泛型与反射机制在JDBC和Servlet编程中的实践 的相关文章

  • 上传进度条 Java Servlet?

    我想使用 servlet 显示上传进度条 我尝试过Ajax iFrame 技术 页面没有重新加载 文件也被上传 但是 进度条没有出现 有没有可用于 javaservlts 的 jQuery 进度插件 Thanks 我强烈推荐jQuery 上
  • 在Maven中生成Version.java文件

    我有一个使用 Ant 脚本构建的 Java 项目 我正在尝试将项目转换为 Maven 其中一项任务生成一个名为 Version java 的 Java 源文件 其中包含编译时间戳的静态字符串表示形式 如下所示 package com foo
  • Jackson JSON + Java 泛型

    我正在尝试将以下 JSON 反序列化 映射到List
  • Java将字符串解析为double

    如何解析字符串中的这个 Double 00034800 变成 Double 值 最后两位数字实际上是小数点 所以我正在寻找的结果是348 00 是否有这样的格式可以与十进制格式一起使用 Well String s 00034800 doub
  • 同一服务器上的许多应用程序具有相同的 JMX Mbean 类

    我有超过 5 个 Spring Web 应用程序 它们都在使用另一个通用库 这个公共库有它自己的 MBean 由于强制的唯一 objectName 约束 我的应用程序无法部署在同一服务器上 我使用 MBean 的方式是这样的 Managed
  • java程序有多少种结束方式?

    我知道使用 System exit 0 可以结束一个java程序 例如 如果我有一个JFrame窗口 它会关闭并结束程序 但我想知道还有多少其他方法 可以关闭它并结束程序 包括发生错误时 程序会被关闭 JFrame也会被关闭吗 添加到其他答
  • Java LostFocus 和 InputVerifier,按反向制表符顺序移动

    我有一个 GUI 应用程序 它使用 InputVerifier 在产生焦点之前检查文本字段的内容 这都是很正常的 然而 昨天发现了一个问题 这似乎是一个错误 但我在任何地方都找不到任何提及它的地方 在我将其报告为错误之前 我想我应该问 我在
  • 查看Java Agent修改的Java类的源代码

    我需要了解 Java 代理如何修改我的初始类 以便我能够理解代码的作用 build gradle configurations jar archiveName agent2 jar jar manifest attributes Prema
  • 通过Zuul上传大文件

    我在通过 zuul 上传大文件时遇到问题 我正在使用 apache commons 文件上传 https commons apache org proper commons fileupload https commons apache o
  • 在 Spring 中为 @Pathvariable 添加类级别验证

    在发布这个问题之前 我已经做了很多研究并尝试了很多可用的解决方案 这是我陷入的棘手情况 我有一个 Spring 控制器 它有多个请求映射 它们都有 PathVariables 控制器如下所示 Controller EnableWebMvc
  • 如何更改 Swagger-ui URL 前缀?

    我正在使用 Springfox Swagger2 和 Spring boot 1 5 9 我可以通过此链接访问 swagger UI http localhost 8090 swagger ui html http localhost 80
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • Android 认为我没有关闭数据库!为什么?

    我有一个 SQLiteDatabase 数据成员 我在 onCreate 中初始化它 并在 onPause onStop 和 onDestroy 中调用 close 它在 onResume 中重新初始化 它似乎运行得很好 但当我查看调试器时
  • UseCompressedOops JVM 标志有什么作用以及何时应该使用它?

    HotSpot JVM 标志是什么 XX UseCompressedOops我应该做什么以及什么时候使用它 在 64 位 Java 实例上使用它 与不使用它 时 我会看到什么样的性能和内存使用差异 去年大多数 HotSpot JVM 都默认
  • 数据库中的持久日期不等于检索日期

    我有一个具有 Date 属性的简单实体类 此属性对应于 MySQL 日期时间列 Entity public class Entity Column name start date Temporal TemporalType TIMESTAM
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • 使用 Apache 允许 Glassfish 和 PHP 在同一服务器中协同工作

    是否可以建立从 Java 到 php 文件的桥梁 我有一个用 Java 编写的应用程序 我需要执行http piwik org http piwik org 这是用 PHP 编写的 在服务器中 我正在运行 PHP 但无法从浏览器访问 php
  • Android ScrollView,检查当前是否滚动

    有没有办法检查标准 ScrollView 当前是否正在滚动 方向是向上还是向下并不重要 我只需要检查它当前是否正在滚动 ScrollView当前形式不提供用于检测滚动事件的回调 有两种解决方法可用 1 Use a ListView并实施On
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • 设置 TreeSet 的大小

    有没有办法像数组一样对 Java 集合中的 TreeSet 进行大小限制 例如我们在数组中 anArray new int 10 数组具有固定长度 在创建数组时必须指定该长度 A TreeSet当您向其中添加元素时会自动增长 您无法设置其大

随机推荐