前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从实践中理解Java的反射

从实践中理解Java的反射

作者头像
马拉松程序员
发布2024-04-17 15:41:33
420
发布2024-04-17 15:41:33
举报
问题背景

话接上回,到新公司也有一个月了,慢慢开始接手和熟悉项目,也开始分配给我一些二次开发的内容。这也是大多数入职到新公司后,从熟悉到接手项目的常规流程。这个时候,你就能发现,这个职位是不是招你来填坑的,以及当前项目的前辈们的代码质量咋样,你有没有在这里发现一点亮眼的代码。

这次的问题背景大概是这样的,公司新开发了一个票据业务相关的项目,这个业务分为4个子项目,4个子项目呢,其实是围绕着票据的一生,从开始到结束。理论上对于票据这个主体的内容是相同,只不过是票据的不同生命周期有不同的功能(业务)。

我接手的时候,这块内容的1.0版本已经开发完了,而且是之前两个人开发的,果不其然也是,4个子项目,4个Service,4个Mappper,其实4+个model/entity。而给我的新任务,是在票据业务中加入审批流,也就是4个子项目分别有申请,审批等等,然后再开发一个任务中心(类似OA审批)的功能。

对于审批来说,Java有现成的审批流应用框架,包括activiti和flowable等,这块功能有其他同事来做,我只需要负责调用方法就可以。

解决思路

我的任务中心这个功能,负责查看4个子业务下申请、待办和已办事项。按照常规的方式,新建一个任务中心service,引入4个mapper,然后分不同的接口,复用现有的mapper中的方法。这其实就是“正射”,我在查询之前知道我使用那个mapper去查那张表,也是常规的问题解决思路。

但是呢,这会产生一个问题,大同小异的代码需要写4次,而且在后期调试的时候,某一个地方改动,比如说,额外加一个查询参数等等。那么如果把4个大同小异的内容,整成一个呢,这里就是用反射。

毕竟现在已经有了4个子业务的mapper,里面都写好了完善的CURD功能。那我就可以把不同的功能作为不同的参数,按照不同的参数使用不同的mapper来执行查询(list和count)。

考虑细节

这里在具体写的时候,还需要注意几个地方:

1.需要自己管理SqlSession,按照“正射”的方式,通常就是service的impl实现类中,使用@Autowired 注入mapper对象,这其实用spring来管理。自行管理SqlSession,要注意使用完成后及时释放Session链接。

2.Dao层的Mapper类通常都是接口,在反射的时候,需要使用代理类,这地方是面试经常问的考点,接口的反射需要使用JDK动态代理。

实现代码

代码语言:javascript
复制
/**
  TaskConfig对象,存查询的mapper类名,对象的列表查询方法,数量查询方法。
 */
TaskConfig config = configMap.get(reqDto.getFuncCode());

// 拿到SqlSessionFactory,创建SqlSession,SpringUtils是封装的工具。
// 通常使用springboot开发的话,多数使用配置Bean获取
SqlSessionFactory sqlSessionFactory = SpringUtils.getBean("sqlSessionFactory");

// 接收返回结果的对象
PageResultDto<DataWrapperDto> result = null;
// 待反射生成的类
Class interfaceImpl = null;
//查询列表方法
Method listMethod = null;
//查询数量方法
Method numMethod = null;
SqlSession sqlSession = null;
try {
            //打开一个sqlSession ,数据库会话
            sqlSession = sqlSessionFactory.openSession();
            // 获取mapper类,ClassName需要写全名,就是从com.xx.xxxx.mapper.ss
            interfaceImpl = Class.forName(config.getClassName());//这里要写全类名,从包写起,不然找不到classname
            // 获取代理对象
            Object instance = Proxy.newProxyInstance(interfaceImpl.getClassLoader(), new Class[]{interfaceImpl}, new MyInvocationHandler(sqlSession.getMapper(interfaceImpl)));
             // 查询参数的vo对象
            Class searchVoClass = Class.forName(config.getSearchVo());
            Object searchVoObject = searchVoClass.newInstance();

            listMethod = instance.getClass().getMethod(config.getListMethod(), searchVoClass, Pageable.class);
            numMethod = instance.getClass().getMethod(config.getNumMethod(), searchVoClass);

            BeanUtils.copyProperties(reqDto, searchVoObject);
            //需要处理查询参数的key不一样的问题
            setQueryParam(reqDto, searchVoObject);
            // 这是执行查询方法
            List<Object> list = (List<Object>) listMethod.invoke(instance, searchVoObject, pageable);
            Object num = numMethod.invoke(instance, searchVoObject);
            // 分页
            pageable.setTotalCount((Integer) num);
            // .....后续对象操作。

}
catch (InstantiationException e) {
            throw new RuntimeException(e);
        } finally {
        // 一定释放链接,不然数据库链接满了,就卡死了。
            if (sqlSession != null) {
                sqlSession.close();
            }
}

接口代理类:

代码语言:javascript
复制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @ClassName: MyInvocationHandler

 * @Description: TODO MyInvocationHandler 为mapper提供代理对象
 */
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //  这里可以实现方法的增强
          //  方法前,比如织入日志功能 打印查询参数
         Object  object   method.invoke(target, args);
          //  方法后,比如打印查询结果
         return object;

    }
}

注意以上内容是核心伪代码,并不能直接run,仅仅是表达下解决思路。具体还会有很多的问题,比如不同的人封装的查询对象不一样,同一个参数的名字也不一样。同一个参数,有的用String,有的用Integer,类型不同、名称不同,内容一样还好,难受的是,有的用“A1,A2”,有的用1、2、3,这就整起来很难受。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 马拉松程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档