我们知道在使用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. Java集合04——fail-fast&fail-safe 详解

    在前几个回合中,我们已经详细了解过了 Java 集合中的List.Set 和 Map,对这部分内容感兴趣的朋友可以关注我的公众号「Java面典」了解.今天我们将为各位介绍集合的失败机制--fail-f ...

  2. ggplot2(4) 用图层构建图像

    4.1 简介 qplot()的局限性在于它只能使用一个数据集和一组图形属性映射,解决这个问题的办法就是使用图层.每个图层可以有自己的数据集和图形属性映射,附加的数据元素可通过图层添加到图形中. 一个图 ...

  3. C++ 读取配置文件结束指定进程

    #define _CRT_SECURE_NO_WARNINGS #include <string> #include <windows.h> #include <stdi ...

  4. python3.4.3 调用http接口 解析response xml后插入数据库

    工作中需要调用一个http的接口,等不及java组开发,就试着用python去调用.Python版本3.4.3 完整的流程包括:从sqlServer取待调用的合同列表 -> 循环调用http接口 ...

  5. JavaScript 预编译与作用域

    JavaScript 预编译与作用域 JavaScript 预编译的过程和作用域的分析步骤是 JS 学习中重要的一环,能够帮助我们知道代码的执行顺序,更好理解闭包的概念 预编译 JavaScript ...

  6. SpringMVC框架——原理及使用

    核心组件 1. DispatcherServlet:前端控制器器,负责调度其他组件的执行,可降低不同组件之间的耦合性,是Spring MVC 的核心模块. 2. Handler:处理器,完成具体的业务 ...

  7. Lua Table pairs输出顺序问题 (版本差异 解决数字索引间断并兼容字符串索引)

    问题标签: Lua Table 迭代器;Lua Table 输出顺序; Lua Table 顺序输出;Lua Table 数字索引 字符串索引;Lua Table pairs; 问题背景: 使用pai ...

  8. 工作中常用到的Java集合类有哪些?

    前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y Java集合是我认为在Java基础中最最重要的知 ...

  9. no parameterless constructor define for type 解决一例

    在生成根据模型和上下文生成带增删查改操作的视图的控制器时,提示上述信息,网上查找了资料也没有解决,突然想起该项目是连接MSSQL数据库和Redis数据库的,并且已经依赖注入了,而Redis数据库的服务 ...

  10. 第十一周Java实验作业

    实验十一   集合 实验时间 2018-11-8 1.实验目的与要求 (1) 掌握Vetor.Stack.Hashtable三个类的用途及常用API: Vector类类似长度可变的数组,其中只能存放对 ...