Java反射(六)纯面向接口编程的简单框架实践
时间:2020-04-12
本文章向大家介绍Java反射(六)纯面向接口编程的简单框架实践,主要包括Java反射(六)纯面向接口编程的简单框架实践使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
我们知道在使用MyBatis开发时,只需要添加DAO接口和对应的映射XML文件,不需要写DAO的实现类,其实底层是通过动态代理实现。
本文将使用前几篇文章的知识点实现一个纯面向接口编程的简单框架,与MyBatis实现DAO实现类相似,主要采用注解、反射、动态代理、工厂模式等。具体功能:
- 接口添加自定义类注解,动态生成接口的实现类
- 通过可配置的方式实现接口行为,如在网络传输中使用TCP或UDP协议,在数据库中配置不同的数据库类型等
- 方法上添加自定义方法和参数注解,控制接口具体实现
- 底层通过代理模拟网络的创建与关闭(可以修改为数据库操作等其他功能)
1.框架实现
(1)注解定义,包含类注解、方法注解、参数类型注解
/** * 运行的类 */ @Retention(RetentionPolicy.RUNTIME) @interface ToClass{ public Class<?> clazz(); } /** * 执行的方法 */ @Retention(RetentionPolicy.RUNTIME) @interface ToMethod{ public String method(); } /** * 执行方法的参数类型 */ @Retention(RetentionPolicy.RUNTIME) @interface ParamType{ public Class<?>[] param(); }
(2)动态生成接口实现类
动态生成接口实现类,并返回对象实例,开发者可以直接调方法使用。
class MyLocator implements InvocationHandler{ //接口类对象 private Class<?> clazz; private static MyLocator instance = new MyLocator(); public static MyLocator getInstance(){ return instance; } public <T> T lookup(Class<T> clazz){ this.clazz = clazz; //返回动态代理类 return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this); } //根据接口的注解,动态调用不同的实现类,实现不同的逻辑 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ //默认实现类 ToClass classAnno = clazz.getAnnotation(ToClass.class); Class defaultClass = classAnno == null ? null :classAnno.clazz(); //获取具体方法和方法参数类型 String mthd = null; List<Class<?>> paramType = new ArrayList<Class<?>>(); Annotation[] annos = method.getDeclaredAnnotations(); for(Annotation anno : annos){ //类注解 if(anno instanceof ToClass){ defaultClass = ((ToClass) anno).clazz(); } //方法注解 if(anno instanceof ToMethod){ mthd = ((ToMethod) anno).method(); } //参数类型注解 if(anno instanceof ParamType){ Class[] p = ((ParamType) anno).param(); for(Class c : p){ paramType.add(c); } } } // Object realObj = defaultClass.getDeclaredConstructor().newInstance(); if(defaultClass != null && mthd != null){ //通过实例化工厂获取defaultClass对象,该对象是被底层代理后的对象 Object obj = InstanceFactory.getInstance(defaultClass); //获取实际调用的方法。注意:对包含参数的方法,必须声明参数类型,并且需要与方法声明顺序保持一致 Method m = obj.getClass().getDeclaredMethod(mthd, paramType.toArray(new Class[0])); //执行方法调用。注意:第一个参数obj必须是上一步获取方法m的声明类,声明类与代理类是不相同的, //如果使用注掉的realObj获取m,会报该方法不是类声明的方法。 return m.invoke(obj, args); } return null; } }
(3)类实例化工厂
/** * 工厂 */ class InstanceFactory{ private InstanceFactory(){} public static <T> T getInstance(Class<T> clazz){ try{ //代理+反射实例化 return (T)new MyProxy().proxy(clazz.getDeclaredConstructor().newInstance(), clazz); }catch (Exception e){ return null; } } }
(4)底层动态代理类,执行方法具体逻辑
/** * 底层动态代理类,执行方法具体逻辑 */ class MyProxy implements InvocationHandler{ //被代理对象 private Object target; public <T> T proxy(Object target, Class<T> clazz){ this.target = target; return (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ try { if(this.connect()){ //实际方法执行 return method.invoke(target, args); } }finally { this.close(); } return null; } private boolean connect(){ System.out.println("创建连接"); return true; } private void close(){ System.out.println("关闭连接"); } }
2.用例测试
(1)模拟网络传输TCP和UDP方式
interface Msg{ void send(String msg, int seq); String recv(String msg); } class TCPMsgImpl implements Msg{ public void send(String msg,int seq) { System.out.println("TCP消息发送:"+msg); System.out.println("TCP消息序号:"+seq); } public String recv(String msg) { System.out.println("TCP消息接收:"+msg); return msg; } } class UDPMsgImpl implements Msg{ public void send(String msg, int seq) { System.out.println("UDP消息发送:"+msg); System.out.println("UDP消息序号:"+seq); } public String recv(String msg) { System.out.println("UDP消息接收:"+msg); return msg; } }
(2)开发者基于TCP和UDP方式定义消息传输服务接口
@ToClass(clazz = TCPMsgImpl.class) interface IMsgService{ @ToMethod(method = "send") @ParamType(param = {String.class, int.class}) public void send(String text, int seq); @ToClass(clazz =UDPMsgImpl.class) @ToMethod(method = "recv") @ParamType(param = {String.class}) public String recv(String text); }
(3)服务接口调用
public static void main(String[] args) throws Exception{ System.out.println("---------发生信息--------"); MyLocator.getInstance().lookup(IMsgService.class).send("hello", 1); System.out.println("---------接收信息--------"); String r = MyLocator.getInstance().lookup(IMsgService.class).recv("hello world"); System.out.println("---------客户端--------"); System.out.println("客户端接收到的信息:" + r); }
输出:
---------发生信息-------- 创建连接 TCP消息发送:hello TCP消息序号:1 关闭连接 ---------接收信息-------- 创建连接 UDP消息接收:hello world 关闭连接 ---------客户端-------- 客户端接收到的信息:hello world
到此,这是所有关于Java反射的汇总记录,后续如有重要点再补充。
原文地址:https://www.cnblogs.com/shuimuzhushui/p/12686189.html
- 【实战】如何使用 Python 从 Redis 中删除 4000万 KEY
- [多图] DevOps 也要懂点 Excel
- [实战篇] Python 运维中使用并发
- PHP数据结构(十) ——有向无环图与拓扑算法
- PHP数据结构(十一) ——图的连通性问题与最小生成树算法(1)
- 优化 MySQL: 3 个简单的小调整
- PHP数据结构(十一) ——图的连通性问题与最小生成树算法(2)
- 进程间通信的历史与未来
- PHP数据结构(十二) ——静态查找表
- 小程序中滚动条的使用,wx.pageScrollTo和<scroll-view>的对比
- 小程序中tabBar的使用
- ubuntu配置虚拟内存
- PHP数据结构(十三) ——动态查找表(二叉排序树)
- Ubuntu下配置JavaWeb开发环境
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP操作路由器实现方法示例
- python能在浏览器能运行吗
- Python使用OpenPyXL处理Excel表格
- php如何比较两个浮点数是否相等详解
- keras 回调函数Callbacks 断点ModelCheckpoint教程
- Mac下快速搭建PHP开发环境步骤详解
- PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
- Ajax+PHP实现的模拟进度条功能示例
- python实例化对象的具体方法
- PHP5.6.8连接SQL Server 2008 R2数据库常用技巧分析总结
- YII框架关联查询操作示例
- Keras之fit_generator与train_on_batch用法
- django美化后台django-suit的安装配置操作
- python读取excel进行遍历/xlrd模块操作
- 浅谈Keras的Sequential与PyTorch的Sequential的区别