注解(也被称为元数据),为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后的谋和时刻非常方便的使用这些数据。比如,当你创建描述符性质的类或接口时,一旦其中包含了重复性工作,那就可以考虑使用注解来简化与自动化该过程。
注解的出现是在jdk1.5中引入的,注解的使用往往会伴随着java反射的使用。接下来介绍jdk中内置的三种标准注解以及四种元注解。
三种标准注解定义在lang包下,分别是:@Override,@Deprecated以及@SuppressWarning。
@Override:表示当前的方法定义将覆盖父类中的方法。
@Deprecated:表示被该注解修饰的方法或者类是过时的。
@SuppressWarning:用来关闭不当的编译器警告信息。
四种元注解:分别是@Target,@Retention,@Documented以及@Inherited。
@Target:表示该方法在哪些地方使用。可能的ElementType参数包括:
1.Constructor:构造器的声明。
2.Field:域声明(包括enum实例)。
3.Local_Variable:局部变量声明。
4.Method:方法声明。
5.Package:包声明。
6.Parameter:参数声明。
7.Type:类,接口(包括注解类型)或enum声明。
@Retention:表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
1.Source:注解将被编译器丢弃。
2.Class:注解在class文件中可用,但会被VM丢弃。
3.Runtime:VM将会保留注解信息,因此可以通过反射获取注解信息。
@Documented:将此注解包含在JavaDoc中。
@Inherited:允许子类继承父类中的注解(仅限类注解,不能继承父类方法中的注解,还有一点,不适用于接口)
接下来代码演示。定义一个注解@interface。注解中注解元素的可用类型仅为:
- 所有基本数据类型
- String
- Class
- enum
- Annotation
- 以上类型的数组形式
package com.example.bootDemo.Anno;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Description {
//如果此处没有任何元素,需要加上默认元素 String value();
//String value();
String name();
int age();
String address() default "xxxx"; //引用注解时不设置值,默认为xxxx
}
package com.example.bootDemo.Anno;
@Description(name = "parent_className",age = 18,address = "PPPP")
public class Parent {
@Description(name = "parent",address = "ppp",age = 50)
public void say(){
}
}
package com.example.bootDemo.Anno;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args){
try {
Class<?> clazz = Class.forName("com.example.bootDemo.Anno.Parent");
//判断类上是否存在注解
boolean exist = clazz.isAnnotationPresent(Description.class);
if(!exist){
return;
}
Description classAnno = clazz.getAnnotation(Description.class);
System.out.println(classAnno.name()+" "+classAnno.age()+" "+classAnno.address());
//判断方法上是否存在注解
Method[] methods = clazz.getMethods();
if(null == methods){
return;
}
for (Method m: methods) {
boolean b = m.isAnnotationPresent(Description.class);
if(!b){
return;
}
Description methodAnno = m.getAnnotation(Description.class);
System.out.println(methodAnno.name()+" "+methodAnno.age()+" "+methodAnno.address());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
我们定义了Description注解,并且只作用于class,method级别,VM运行时不会将注解丢弃以及包含于javaDoc中和允许子类继承父类中的注解。
注解类中定义了3个变量,分别为String类型,int类型以及String类型存在默认值的情况,在解析注解中值的时候,我们使用了反射机制加载类信息。使用isAnnotationPresent()方法判断类、方法上是否存在我们的自定义注解,并且在class,method级别中使用getAnnotation()获取指定注解的信息,并且获取值。
下面实现一个小功能,打印sql语句,使用注解实现:
先定义两个注解@Table用来指定表名(与数据库表名一致),@Colunm用来指定字段名。
package com.example.bootDemo.Anno;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Table {
String value();
}
package com.example.bootDemo.Anno;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Column {
String value();
}
我们尝试在user表中使用我们的自定义注解
package com.example.bootDemo.Anno;
@Table("user")
public class User {
@Column("id")
private Integer id;
@Column("name")
private String name;
@Column("email")
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
测试代码:
package com.example.bootDemo.Anno;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestSql {
//需求目的,传入具体对象,获取相应的等值查询sql
public static void main(String[] args) throws Exception{
User user = new User();
user.setId(2);
String sql1 = querySql(user);
System.out.println(sql1);
user.setName("张华");
String sql2 = querySql(user);
System.out.println(sql2);
user.setEmail("1339148474@qq.com;572212558@qq.com");
String sql3 = querySql(user);
System.out.println(sql3);
}
static String querySql(Object obj) throws Exception{
StringBuffer sb=new StringBuffer();
//此处可以传递任意类型对象
Class<?> clazz = obj.getClass();
//判断类上是否有@Table注解
boolean isTable = clazz.isAnnotationPresent(Table.class);
if(!isTable){
return null;
}
Table table = clazz.getAnnotation(Table.class);
//获取表名
String tableName = table.value();
//部分sql拼接
sb.append("select * from ").append(tableName).append(" where 1=1 ");
//------获取字段上的名称
//获取所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field: fields) {
boolean isColumn = field.isAnnotationPresent(Column.class);
if(!isColumn){
continue;
}
Column fieldAnnotation = field.getAnnotation(Column.class);
//获取字段名称
String fieldName = fieldAnnotation.value();
//此处拼接get方法,通过invoke调用获取字段值
String methodToGetValue="get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1,fieldName.length());
Method method = clazz.getMethod(methodToGetValue);
Object fieldValue = method.invoke(obj);
if(null == fieldValue){
continue;
}
sb.append(" and ");
if(fieldValue instanceof String ){
if(((String) fieldValue).contains(";")){
sb.append(fieldName+" in ("+((String) fieldValue).replaceAll(";",",")+")");
}else {
sb.append(fieldName+"= " + fieldValue);
}
}
if(fieldValue instanceof Integer){
sb.append(fieldName+"= "+fieldValue);
}
}
return sb.toString();
}
}
select * from user where 1=1 and id= 2
select * from user where 1=1 and id= 2 and name= 张华
select * from user where 1=1 and id= 2 and name= 张华 and email in (1339148474@qq.com,572212558@qq.com)
仅仅做一个简单的展示,具体复杂的逻辑还需要大量sql语句的适配