注解,对于我们开发人员来说无疑是再熟悉不过了,尤其是开发过程中通过一个简简单单的注解就可以省去很多繁琐的配置,可以说注解帮我们简化了很多开发流程,用起来方便快捷。但这一切都是框架或者说是jdk源码人家配置好我们直接拿来用的,我们自己是否也可以自定义一个属于我们自己的注解,开发中将一些重复的属性或配置通过注解的形式注入进去呢?好了,废话不多说,下面直接上干活,看看一个简单的自定义注解是如何一步步实现的。
接下来我将以一个动态拼接sql的例子来讲解自定义注解的实现,主要核心还是用到了java的反射机制,不熟悉java反射机制最好还是先熟悉一下比较好理解
1.定义一个名为Table的注解,该注解主要是注入我们sql中的table表
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Table {
String value();
}
简单解释一下几个注解:
@Target:表示注解的作用范围,例如是在包上,还是类上,还是类的属性上等等。
@Retention:表示注解的生命周期,常用的有三个,源文件SOURCE、编译CLASS、运行时RUNTIME
@Inherited:表示子类可以继承父类的注解
@Documented:表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
(需要注意的是当注解中只有一个成员的时候,成员名必须位value())
2.定义一个名为Column的注解,该注解主要是注入我们sql中的where条件的拼接
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Column {
String value();
}
基本与Table注解一致,不同的是注解的作用范围,Table注解是作用在类上,用的是TYPE,而Column注解作用在属性上,用的是FIELD
3.定义一个实体类(User),类上加上我们自定义的Table注解,属性上加上我们自定义的Column注解
@Table("user")
public class User {
@Column("id")
private Integer id;
@Column("username")
private String username;
@Column("age")
private Integer age;
@Column("address")
private String address;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
4.定义一个测试类,并调用封装好的动态拼接sql的方法用来测试我们自定义的注解是否好用,代码中基本每一行都解释说明了此步的作用,这里就不再重复阐述,详细请看如下代码:
public class TestUser {
public static void main(String[] args) {
User user=new User();
user.setUsername("张三");
user.setAddress("上海");
user.setAge(25);
String sql=query(user);
System.out.println("sql:"+sql);
}
//封装拼接sql查询的方法
private static String query(User user) {
//用来拼接sql
StringBuffer stringBuffer=new StringBuffer();
//反射机制通过对象获取类的全路径
Class<? extends User> aClass = user.getClass();
//判断注解类是否存在
boolean annotationPresent = aClass.isAnnotationPresent(Table.class);
if (!annotationPresent){
return null;
}
//获取注解Table的全路径
Table annotation = aClass.getAnnotation(Table.class);
//获取Table注解的具体值
String tableName = annotation.value();
//拼接sql语句
stringBuffer.append("select * from ").append(tableName).append(" where 1=1 ");
//新加遍历所有字段(后面拼接where条件使用)
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field:declaredFields) {
//首先判断该字段的属性上是否有注解
boolean annotationPresent1 = field.isAnnotationPresent(Column.class);
if (!annotationPresent){
return null;
}
//获得注解Column的全路径
Column annotation1 = field.getAnnotation(Column.class);
//获得注解Column具体的值
String column = annotation1.value();
//获得实体类中所有的属性名字
String fieldName = field.getName();
//获得所有属性对应的get方法
String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
//获取对应属性设置的实际值
Object fieldValue =null;
try {
Method method = aClass.getMethod(getMethodName);
fieldValue =method.invoke(user);
} catch (Exception e) {
e.printStackTrace();
}
//判断哪些属性没有设置值则不参与where条件的拼接
if(fieldValue==null || (fieldValue instanceof Integer && (Integer)fieldValue==0)) {
continue;
}
//拼接where条件中的注解名称
stringBuffer.append(" and ").append(column);
//拼接where条件中的注解值
if(fieldValue instanceof String) {
stringBuffer.append(" = ").append("'").append(fieldValue).append("'");
}else if(fieldValue instanceof Integer) {
stringBuffer.append(" = ").append(fieldValue);
}
}
//返回字符串sql
return stringBuffer.toString();
}
最后附上一张效果图:
好了,自定义注解介绍完毕,希望可以对广大程序小白的学习有所帮助。