我们知道在使用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反射(六)纯面向接口编程的简单框架实践的更多相关文章

  1. 深入分析Java反射(六)-反射调用异常处理

    前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...

  2. Java反射机制剖析(三)-简单谈谈动态代理

    通过Java反射机制剖析(一)和Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制. 1.     代理模式 代理模式就是为其他对象提供一种代理来 ...

  3. 编写Java程序,使用面向接口编程模拟不同动物的吼叫声

    返回本章节 返回作业目录 需求说明: 使用面向接口编程模拟不同动物的吼叫声 实现思路: 使用面向接口编程模拟不同动物吼叫声的实现思路: 定义发声接口Voice,在其中定义抽象吼叫方法sing(). 分 ...

  4. 面向接口编程详解-Java篇

    相信看到这篇文字的人已经不需要了解什么是接口了,我就不再过多的做介绍了,直接步入正题,接口测试如何编写.那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象.为充分考虑到初学者,所 ...

  5. Java反射+简单工厂模式总结

    除了 new 之外的创建对象的方法 通过 new 创建对象,会使得程序面向实现编程,先举个例子,某个果园里现在有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用get()方法表示即可 ...

  6. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  7. Java进阶(六)Java反射机制可恶问题NoSuchFieldException

    作为一种重要特性,Java反射机制在很多地方会用到.在此做一小结,供朋友们参考. 首先从一个问题开始着手. 可恶的问题又来了,NoSuchFieldException,如下图所示: 完全不知道这个qu ...

  8. java反射机制简单实例

    目录 Java反射 简单实例 @(目录) Java反射 Java语言允许通过程序化的方式间接对Class进行操作.Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通 ...

  9. Java反射机制demo(六)—获得并操作一个类的属性

    Java反射机制demo(六)—获得并操作一个类的属性 获得并操作一个类的属性?! 不可思议啊,一个类的属性一般都是私有成员变量啊,private修饰符啊! 但是毫无疑问,这些东西在Java的反射机制 ...

随机推荐

  1. 文本编辑器 - Sublime Text 3 换行无法自动缩进的解决方法

    一.换行无法自动缩进的问题,如图: 稍微查了一下网上的办法,是把汉化文件删除,但是会造成菜单栏混乱,简直无法忍受... 那么这里介绍的是另一种解决办法.在用户的热键配置文件(preferences-k ...

  2. Hadoop集群搭建(二)~centos6.8的安装

    这篇记录在创建好的虚拟机中安装centos6.8 1,在虚拟机界面-选择编辑虚拟机设置 2,CD/DVD,选择使用ISO映像文件,找到安装包的位置,确定 3,回到虚拟机的界面,开启此虚拟机 4,安装 ...

  3. Python生态_turtle库

    Python生态_turtle库: 绘制状态函数: pendown():别名pd(),落下画笔,之后,移动画笔将绘制形状 penup():抬起画笔 pensize():画笔粗细大小 颜色控制函数: p ...

  4. 公钥体系(PKI)等密码学技术基础

    公钥体系(PKI)等密码学技术基础 公钥体系(Public Key Infrastructure, PKI)的一些概念 对称密码算法, 典型算法:DES, AES 加解密方共用一个密钥 加/解密速度快 ...

  5. 【Weiss】【第03章】练习3.21:单数组模拟双栈

    [练习3.21] 编写仅用一个数组而实现两个栈的例程.除非数组的每一个单元都被使用,否则栈例程不能有溢出声明. Answer: 很简单,一个栈从数组头起,一个栈从数组尾起,分别保留左右栈头索引. 如l ...

  6. python-参考书

    真的是找了很多的参考书,但是看懂,上手用的就一本比较好的<编程小白的第1本python入门书>非常的好. 属于那种一看就懂,能说明白的.别的,要么就是翻译的外文的,有点难以理解,要么就是中 ...

  7. Java并发编程之验证volatile不能保证原子性

    Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...

  8. HTML每日学习笔记(2)

    7.16.2019 1.html表单:用于得到用户不同类型的输入 <form>元素定义表单: <form> First name:<br> <input ty ...

  9. 懂一点Python系列——快速入门

    本文面相有 一定编程基础 的朋友学习,所以略过了 环境安装.IDE 搭建 等一系列简单繁琐的事情. 一.Python 简介 Python 英文原意为 "蟒蛇",直到 1989 年荷 ...

  10. vscode不能打开浏览器(Open browser failed!! Please check if you have installed the browser correctly!)

    vscode出现上述问题,我也查了很多相关资料,什么改默认浏览器设置什么的,改配置,改系统环境变量什么的,不但麻烦而且最后都难以成功. 下面分享一个可以解决的最简单办法.那就是:舍弃open in b ...