作者 :孙玉昌,昵称【一一哥 】,另外【壹壹哥】也是我哦
千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者
前言
在上一篇文章中,壹哥 给大家讲解了反射的基本使用,但受限于篇幅,我们并没有把反射的知识全都学完。在今天的这篇文章中,壹哥会继续往下讲解反射的高级玩法,希望大家集中精力,继续认真学习哦。
------------------------------前戏已做完,精彩即开始----------------------------
全文大约【4200】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......
配套开源项目资料
Github: github.com/SunLtd/Lear...
Gitee: gitee.com/sunyiyi/Lea...
一. 操作注解
我们知道,注解是一种特殊的代码标记,它们可以被附加到类、方法、变量等代码元素上,用于提供额外的信息和描述。实际上,注解也可以通过反射来获取到,并根据注解的信息来实现相应的功能。接下来壹哥就给大家设计一个简单的案例,看看反射是如何操作注解的。
1. 定义注解
首先,我们来定义一个注解,这里我们定义下面这样一个@MyAnnotation注解:
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
//注解的属性值
String value() default "default value";
}
在上述代码中,@MyAnnotation注解的生命周期使用@Retention来指定,作用目标使用@Target来指定。我们把@MyAnnotation注解的生命周期限定为RUNTIME,表示该注解可以在运行时被反射技术获取。大家注意,要想在反射中使用该注解,必须把生命周期限定为RUNTIME。
另外我们还使用@Target(ElementType.METHOD)来限定@MyAnnotation的作用目标是在方法上。并且@MyAnnotation注解还有一个value属性,类型是String,默认值为"default value"。
2. 使用注解
定义了注解之后,我们就可以在代码中使用它了。例如,我们可以给一个方法添加@MyAnnotation注解:
java
import java.lang.reflect.Method;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
public class Demo04 {
// 给该方法添加一个自定义的注解
@MyAnnotation("hello,一一哥")
public void myMethod() {
System.out.println("反射加注解,法力大无边");
}
}
上述代码中,我们在myMethod()方法上添加了@MyAnnotation("hello,一一哥")注解,表示该方法可以使用@MyAnnotation注解提供的功能。
3. 反射获取注解
使用注解之后,我们就可以通过反射技术来获取注解里的信息,并根据注解的信息来实现相应的功能了。例如,我们可以使用反射类java.lang.reflect.Method的getAnnotation()方法,来获取该方法上的@MyAnnotation注解:
java
import java.lang.reflect.Method;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
public class Demo04 {
// 给该方法添加一个自定义的注解
@MyAnnotation("hello,一一哥")
public void myMethod() {
System.out.println("反射加注解,法力大无边");
}
public static void main(String[] args) {
try {
Demo04 obj = new Demo04();
// 获取到obj对象里的myMethod方法
Method method = obj.getClass().getMethod("myMethod");
// 获取该方法上的MyAnnotation注解
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
if (annotation != null) {// 判断注解是否为空
// 获取注解的value属性值
String value = annotation.value();
//value= hello,一一哥
System.out.println("value= " + value);
}
} catch (SecurityException | IllegalArgumentException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
上述代码,我们首先创建了一个Demo04对象,并通过反射获取到了myMethod()方法的Method对象。然后,我们使用getAnnotation(MyAnnotation.class)方法,获取到了该方法上的@MyAnnotation注解。如果该方法上没有@MyAnnotation注解,则返回null。如果存在该注解,就可以通过注解对象中的方法来获取注解的信息了。
4. 反射解析注解
我们除了可以获取注解信息之外,还可以使用反射技术来解析注解,并根据注解的信息来实现相应的功能。例如,我们可以使用反射类java.lang.reflect.Method的isAnnotationPresent()方法,来判断某个方法上是否包含@MyAnnotation注解:
java
import java.lang.reflect.Method;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
public class Demo04 {
// 给该方法添加一个自定义的注解
@MyAnnotation("hello,一一哥")
public void myMethod() {
System.out.println("反射加注解,法力大无边");
}
public static void main(String[] args) {
try {
Demo04 obj = new Demo04();
// 获取到obj对象里的myMethod方法
Method method = obj.getClass().getMethod("myMethod");
//反射解析注解
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
// 获取注解的value属性值
String value = annotation.value();
System.out.println("value= " + value);
}
} catch (SecurityException | IllegalArgumentException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们首先创建了一个Demo04对象,并获取到了myMethod()方法的Method对象。然后,我们又使用isAnnotationPresent(MyAnnotation.class)方法,来判断该方法上是否包含@MyAnnotation注解。如果存在该注解,则使用getAnnotation(MyAnnotation.class)方法来获取注解对象,并根据注解对象的方法来实现相应的功能。
这样,通过以上这个案例,壹哥 就带大家实现了反射对注解的操作,你学会了吗?接下来壹哥再给大家设计一个更高级的案例,咱们来定义一个自己的ORM框架,看看框架都是怎么开发的。
二. ORM框架简介
1. 概念
ORM(Object-Relational Mapping)是一种可以将对象模型(比如实体类)映射到关系模型(比如数据库表)的技术, 目前常见的Java ORM框架有Hibernate、MyBatis、Spring Data JPA、Mybatis-Plus等,这些框架在我们的线下课程中都有详细讲解。这种框架允许我们使用面向对象的方式来操作数据库,不需要编写任何的SQL语句,从而提高了开发效率和代码可维护性,大大简化了数据库编程的复杂性。
通常,ORM框架会给我们提供一个持久化层,负责将Java对象映射到数据库表,并提供一些常见的CRUD(Create, Read, Update, Delete)操作。从底层实现来看,ORM框架通常是使用反射和注解来实现的。
所以现在我们其实已经具备了编写一个简单ORM框架的技术基础了,但在具体实现之前,壹哥还要给大家梳理一下ORM框架要具备的功能。
2. 设计ORM框架
其实做软件开发,首先得知道自己想要一个什么东西,如果你连自己想要什么都不知道,那又开发什么呢?所以在设计ORM框架之前,我们需要明确ORM框架应该具备的功能,一个基本的ORM框架通常需要实现以下功能:
- 连接和关闭数据库;
- 数据库的事务管理;
- 对象与数据库表的映射;
- 数据库的查询和更新;
- 数据库结果集与对象之间的映射
明确了ORM框架应该具备的基本功能之后,后面我们就可以进行开发了。
3. 分析ORM框架实现思路
接下来壹哥再给大家分析一下ORM框架的实现思路。
3.1 连接数据库和关闭
我们可以使用JDBC来连接MySQL数据库,并在框架初始化时完成数据库连接。在框架关闭时,需要关闭数据库连接。
关于JDBC,如果你是初学者,可能对此还不太清楚,后面壹哥 会给大家再开辟一个MySQL数据库专栏,到时候会专门讲解JDBC的操作,欢迎持续关注壹哥哦,本文请各位简单跟着学习即可。
3.2 事务管理
我们可以使用JDBC的事务管理来实现ORM框架的事务管理。在框架中,我们可以定义一个@Transactional注解,表示该方法需要在事务中执行。在执行该方法时,首先需要开启事务,然后执行方法体,如果方法执行成功,则提交事务,否则回滚事务。
因为大家现在还没有学习数据库的知识,所以今天的文章就不给大家实现事务功能了。
3.3 对象到数据库表的映射
我们可以自定义注解来实现对象与数据库表的映射。在框架中,我们可以定义一个@Table注解和一个@Column注解,分别表示数据库表和表字段。在实体类中,我们可以使用@Table注解来指定该类对应的数据库表,使用@Column注解来指定该属性对应的数据库字段。
3.4 数据库查询和更新
我们可以使用JDBC来实现ORM框架的数据库查询和更新。在框架中,我们可以定义一个QueryRunner类,来封装JDBC的查询和更新操作。在执行查询和更新时,我们可以使用反射来获取实体类对应的表名和字段名,然后生成相应的SQL语句。
3.5 数据库结果集与对象之间的映射
我们也可以使用反射来实现数据库结果集与对象之间的映射。在框架中,我们可以定义一个ResultSetHandler类,来封装JDBC的结果集处理操作。在处理结果集时,我们可以使用反射来获取实体类对应的属性名和类型,然后将结果集中的数据按照属性名和类型设置到实体类的属性中。
在本文中,壹哥 只给大家实现添加操作,其余的功能当做作业,留给大家自行实现。如果你在实现的过程中有什么问题,可以在评论区给壹哥留言或私信,我看到了就会给大家及时解答。
三. 实现ORM框架
接下来壹哥就给大家讲一下ORM框架的具体实现过程,核心代码如下。
1. 必要准备
为了方便我们后面的测试,大家可以提前自行准备好一个数据库及对应的表格,壹哥在MySQL中准备了一个db1数据库,并在其中创建了user表。
除此之外,大家还有先下载一个mysql的依赖包,并导入到自己的项目中,如下图所示:

2. 定义注解
接着,我们就要定义两个注解:@Table和@Column,用于指定实体类对应的数据库表和表字段。
2.1 @Table
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 一一哥Sun
* @company 千锋教育
* 自定义注解--获取表名
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
//关联表名
String name();
}
@Table注解用于标记实体类对应的数据库表,其中name()方法用于指定数据库表名。
2.2 @Column
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 一一哥Sun
* @company 千锋教育 自定义注解--获取列名
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
// 关联列名
String name();
}
@Column注解用于标记实体类属性对应的数据库字段,其中name()方法用于指定数据库字段名。
3. 定义实体类
然后,我们需要定义实体类,使用@Table注解指定实体类对应的数据库表,使用@Column注解指定实体类属性对应的数据库字段。
java
import demo37.orm.annotation.Column;
import demo37.orm.annotation.Table;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
@Table(name = "user")//关联自定义注解@Table,将类名与表名建立映射
public class User {
//关联自定义注解@Column,将属性名与列名建立映射
@Column(name = "id")
private int id;
@Column(name = "username")
private String username;
@Column(name = "age")
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", age=" + age + "]";
}
}
上述代码定义了一个User类,其中@Table(name = "user")注解指定了该类对应的数据库表为users,@Column(name = "id")、@Column(name = "username")、@Column(name = "age")注解指定了该类属性对应的数据库字段为id、username、age。
4. 定义ORM框架
我们可以定义一个ORM类,用于实现将对象映射到数据库表中的功能。
java
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import demo37.orm.annotation.Column;
import demo37.orm.annotation.Table;
/**
* @author 一一哥Sun
* @company 千锋教育
* ORM框架的核心类
*/
public class ORM {
private Connection conn;
//构造方法中设置数据库的url、用户名和密码
public ORM(String url, String user, String password) {
try {
//通过反射的第三种获取字节码方式来获取字节码对象
//加载Driver驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库Connection连接对象
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 定义一个数据库添加数据的方法
* @param obj,实体类对象
*/
public void save(Object obj) {
Class clazz = obj.getClass();
//得到某个实体对象上绑定的Table注解
Table table = (Table) clazz.getAnnotation(Table.class);
//得到实体类绑定的表名
String tableName = table.name();
//拼接insert into插值语句
StringBuilder sb = new StringBuilder("INSERT INTO ");
sb.append(tableName).append(" (");
//获取实体类中每个属性上关联的列名
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
//将列名拼接到insert语句中
sb.append(column.name()).append(",");
}
}
//去掉insert语句中多余的","
sb.deleteCharAt(sb.length() - 1).append(") VALUES (");
//在insert语句中,拼接values语句后面的值
for (Field field : fields) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
//对私有属性设置可访问性
field.setAccessible(true);
try {
sb.append("'").append(field.get(obj)).append("',");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//去除insert语句中最后多余的","
sb.deleteCharAt(sb.length() - 1).append(")");
try {
//执行SQL语句
Statement stmt = conn.createStatement();
stmt.executeUpdate(sb.toString());
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭数据库连接
*/
public void close() {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代码定义了一个ORM类,其中save()方法用于将对象保存到数据库表中,close()方法用于关闭数据库连接。save()方法首先使用反射获取对象的类信息和注解信息,然后根据注解信息生成SQL语句并执行插入操作。
5. 使用ORM框架
现在,我们可以使用ORM框架将实体类对象映射到数据库表中。
java
import demo37.orm.core.ORM;
import demo37.orm.entity.User;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
public class Demo05 {
public static void main(String[] args) {
//传入自己的数据库表的地址与账户信息
ORM orm = new ORM("jdbc:mysql://localhost:3306/db1?useSSL=false", "root", "syc");
//创建一个用户对象
User user = new User();
user.setId(55);
user.setUsername("一一哥666");
user.setAge(18);
//调用ORM框架的插入操作
orm.save(user);
//关闭数据库连接
orm.close();
}
}
上述代码首先创建了一个ORM对象,并使用save()方法将一个User对象保存到数据库表中。最后,需要调用close()方法关闭数据库连接。
6. 完整项目结构
至此,壹哥就带大家自定义了一个简单的ORM框架,完整的项目结构如下图所示:

实际上,我们这个ORM框架是比较简单的,大家可以根据壹哥的实现过程,再把修改、查询、删除等功能实现出来。
------------------------------正片已结束,来根事后烟----------------------------
四. 结语
本文重点给大家介绍了如何使用反射和注解来实现一个简单的ORM框架,并将其用于对象和数据库表的映射,现在你对ORM了解了吗?
虽然ORM框架可以大大简化数据库编程的复杂性,提高代码的灵活性和可扩展性。但是在实际开发中,我们要特别注意ORM框架的性能和可读性问题,避免过度使用反射和注解。另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。