一、注解是什么?
Annotation——注解,是Java中的一种代码辅助工具,其核心作用是用来标注类、方法、变量、参数以及包。我们可以通过反射机制来访问这些标注信息,然后将我们所需要的代码逻辑镶嵌在所标注的元素上,或者改造注解标注的元素来达到我们需要的效果。
这里要做一个区分,注解与注释不同,注释是作者在编辑java代码时对某一段代码或者某一个类、方法等做一个解释。一般用“//这是解释说明内容”、“/*这是解释说明内容/”等方式。注释中的内容是不会被编译进.class文件中的。而注解的内容是可以编译进代码中的。
二、如何创建注解
Java中有很多注解,比如“@Controller”、"@Service"、“@Autowired”等等。这几个注解都是和框架相关的,由框架定义的。大家平时估计用的最多的也是框架提供的注解。但是实际上,我们java开发人员是可以自己定义注解的。定义注解的方式如下:
package com.guo.annotation;
import java.lang.annotation.*;
/**
1. @Author: Jack Guo
2. @Date: 2020/5/14 9:57
3. 版权所有,不得乱改
4. 元注解有以下
5. @Retention 描述注解的生命周期
6. @Documented 文档注解,会被Javadoc文档化
7. @Inherited 是否让子类继承该注解
8. @Target 描述了注解的应用范围
*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface Study {
String name() default "guo";//java基本类型对注解进行修饰
String[] mores();//也可以是数组类型,此处没有给默认值,需要使用时添加值
}
我这里定义了一个@Study注解,定义注解的时候会有几个元注解对新定义的注解的生命周期作用范围等做一个说明。比如元注解@Target()用来描述注解的作用范围,它的参数是可以多选的。它有如下几个参数:
- TYPE 修饰类、接口、注解类型或枚举类型
- PACKAGE 修饰包
- PARAMETER 修饰参数
- ANNOTATION_TYPE 修饰注解类型
- METHOD 修饰方法
- FIELD 修饰属性
- CONSTRUCTOR 修饰构造器
- LOCAL_VARIABLE 修饰局部变量
那么定义好了注解之后,让我们看一下怎么使用吧
@Study(mores = {"hard","easy"})
public class Person {
@Study(mores ={"aa","cc"})
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
注解与反射实现自动生成SQL
接下来我就利用注解和反射来实现类似Hibernate框架的生成SQL的小功能。首先创建两个注解类型:@Table()、@Column()。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value() default "";
}
注意上面的注解定义时有个String value()方法。这个方法的作用结合下面的Order类的定义,就可以一目了然了,比如类Order上面加的@Table(“tb_order”),我们就可以利用value()的值来定义表名。如果没有值默认是“”。那么我就让表名取为类名,同理@Column()注解如果没有参数我就让它所修饰的字段名来充当数据库字段名。
然后创建一个Order类,拥有一些基本的属性:
@Table("tb_order")
public class Order {
@Column
private Long id;
@Column("order_no")
private String orderNo;
@Column("shop_id")
private String shopId;
@Column("user_name")
private String userNaeme;
@Column("user_id")
private int userId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getShopId() {
return shopId;
}
public void setShopId(String shopId) {
this.shopId = shopId;
}
public String getUserNaeme() {
return userNaeme;
}
public void setUserNaeme(String userNaeme) {
this.userNaeme = userNaeme;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
那接下来就是具体怎么实现SQL的自动生成了,这里用到了反射的技术。为了能让大家理解,我多加点注释:
import java.lang.reflect.Field;
/**
* @Author: Jack Guo
* @Date: 2020/5/14 15:47
* 版权所有,不得乱改
*/
public class GenerateSqlUtil {
public String query(Object tab) throws IllegalAccessException {
//这是一个查询语句的生成 select 字段 from 表 where
StringBuffer sb = new StringBuffer();
sb.append("select");
StringBuffer sb2 = new StringBuffer();
sb2.append(" where 1=1 ");
Class<?> tabClass = tab.getClass();
Table annotationTable = tabClass.getAnnotation(Table.class);//获取修饰类的@Table()注解
if (annotationTable==null){
throw new RuntimeException("注解缺失");
}
String tableName = annotationTable.value();//通过注解的参数获取表名
Field[] fields = tabClass.getDeclaredFields();//获取类的属性
for (Field field :fields){
Column c = field.getAnnotation(Column.class);//获取@Column()注解
if (c==null){//如果属性没有被注解修饰,则不生成查询字段
continue;
}
if(c.value().equals("")){//如果注解中没有参数,,字段名和属性名一致
String name =field.getName();
field.setAccessible(true);//将属性设为可见
Object o = field.get(tab);//获取到字段值
if(o!=null&&Integer.parseInt(o.toString())!=0){//如果字段值不为空,则将此字段添加到查询条件语句中
sb2.append(" and "+name+"="+o);
sb.append(" "+name+",");
}
}else {
sb.append(" "+c.value()+",");
field.setAccessible(true);
Object o = field.get(tab);
if(o!=null&&Integer.parseInt(o.toString())!=0){
sb2.append(" and "+c.value()+"="+o);
}
}
}
sb.deleteCharAt(sb.length()-1);
sb.append(" from "+tableName);
sb.append(sb2);
return sb.toString();
}
}
最后进行测试:
public class TableAnnotationTest {
public static void main(String[] args) throws IllegalAccessException {
GenerateSqlUtil generateSqlUtil = new GenerateSqlUtil();
Order order = new Order();
order.setId(1234L);
String s = generateSqlUtil.query(order);
System.out.println(s);
}
}
输出结果如下:
select id, order_no, shop_id, user_name, user_id from tb_order where 1=1 and id=1234