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反射的汇总记录,后续如有重要点再补充。
Java反射(六)纯面向接口编程的简单框架实践的更多相关文章
- 深入分析Java反射(六)-反射调用异常处理
前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...
- Java反射机制剖析(三)-简单谈谈动态代理
通过Java反射机制剖析(一)和Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制. 1. 代理模式 代理模式就是为其他对象提供一种代理来 ...
- 编写Java程序,使用面向接口编程模拟不同动物的吼叫声
返回本章节 返回作业目录 需求说明: 使用面向接口编程模拟不同动物的吼叫声 实现思路: 使用面向接口编程模拟不同动物吼叫声的实现思路: 定义发声接口Voice,在其中定义抽象吼叫方法sing(). 分 ...
- 面向接口编程详解-Java篇
相信看到这篇文字的人已经不需要了解什么是接口了,我就不再过多的做介绍了,直接步入正题,接口测试如何编写.那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象.为充分考虑到初学者,所 ...
- Java反射+简单工厂模式总结
除了 new 之外的创建对象的方法 通过 new 创建对象,会使得程序面向实现编程,先举个例子,某个果园里现在有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用get()方法表示即可 ...
- Java基础-面向接口编程-JDBC详解
Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...
- Java进阶(六)Java反射机制可恶问题NoSuchFieldException
作为一种重要特性,Java反射机制在很多地方会用到.在此做一小结,供朋友们参考. 首先从一个问题开始着手. 可恶的问题又来了,NoSuchFieldException,如下图所示: 完全不知道这个qu ...
- java反射机制简单实例
目录 Java反射 简单实例 @(目录) Java反射 Java语言允许通过程序化的方式间接对Class进行操作.Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通 ...
- Java反射机制demo(六)—获得并操作一个类的属性
Java反射机制demo(六)—获得并操作一个类的属性 获得并操作一个类的属性?! 不可思议啊,一个类的属性一般都是私有成员变量啊,private修饰符啊! 但是毫无疑问,这些东西在Java的反射机制 ...
随机推荐
- Vue Snackbar 消息条队列显示,依次动画消失的实现
效果预览 思路 封装 Snackbar 组件: 在根路由页面下建立全局 Snackbar 控制器,统一管理 Snackbar: 通过事件通知全局 Snackbar 控制器显示消息: 实现 1. 封装 ...
- django 用户认证 user对象
django中的用户模型 内部带有很多的属性方法,我们可以直接使用 1 is_staff Boolean.决定用户是否可以访问admin管理界面.默认False. 2 is_active Boolea ...
- List remove ConcurrentModificationException源码分析
代码块 Java Exception in thread "main" java.util.ConcurrentModificationException at j ...
- JAVA 转换 树结构数据
JAVA 转换 树结构数据 第一步:引入fastjson <dependency> <groupId>com.alibaba</groupId> <artif ...
- Natas34 Writeup(闯关结束!)
Natas34: 登录什么都不用做,闯关结束!撒花~~~
- 读书笔记——莫提默·J.艾德勒&查尔斯·范多伦(美)《如何阅读一本书》
第一篇 阅读的层次 第一章 阅读的活力与艺术 阅读的目标:娱乐.获得资讯.增进理解力这本书是为那些想把读书的主要目的当作是增进理解能力的人而写.何谓阅读艺术?这是一个凭借着头脑运作,除了玩味读物中的一 ...
- 项目总结&读书笔记
Python项目 01-函数版ATM 读书笔记 01-Effective Python
- LeetCode--179场周赛题解
水题: class Solution { public: string generateTheString(int n) { string s; string a="a",b=&q ...
- Mac brew命令的使用
mac 终端程序管理工具 能让你更快速的安装你想要的工具.而不用考虑大量的依赖. 安装brew复制下面的命令,终端执行 官网Homebrew /usr/bin/ruby -e "$(cur ...
- Oracle连接别人数据库
方法一:在开始菜单中,找到oracle11g-应用程序开发-SQL PLUS.双击SQL PLUS. 弹出的SQL Plus框中,输入数据库实例的用户名和密码,按enter键. 如果oracle服务器 ...