注解开发应用
注解
在Java基础中我们都学习过简单的注解开发,但并不没有意识到重要性,直到使用了SpringBoot后,我们看到了注解相比原先配置文件的大量定义的简洁与优雅。在测试中我们也常用注解:@Test ,在方法定义时加上该注解,我们便可以执行这个方法,这到底怎么做的呢? 本文将回顾注解基础,并衍生到具有应用:
基础知识
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
上面的代码大家都很熟悉,当我们子类重新父类的方法时,会带有该注解。而我们在声明一个注解时,
- 首先需要使用 @Interface 来定义,这和我们定义 class,Interface 一样,最终编译时也会生成一个 class 文件
- 其次需要 @Target 和 @Retention 来定义该注解的作用域,这也被称为元注解,下面我们看看它们常用的范围
public enum ElementType {
/** 类, 接口 (包含注解), 枚举
TYPE,
/** 字段 */
FIELD,
/** 方法 */
METHOD,
/** 参数 */
PARAMETER,
/** 构造器 */
CONSTRUCTOR
}
public enum RetentionPolicy {
/**
* 被编译器丢弃
*/
SOURCE,
/**
* 在class文件中可用,但被vm丢弃
*/
CLASS,
/**
* 运行时可用,可通过反射读取
*/
RUNTIME
}
注解的作用是什么?
可以提供用来完整描述程序所需要的信息,而这些信息无法用Java来表达,或者是不方便表达。
如何使用呢?
如果你学过Mybatis这种orm框架,会知道想将数据库字段与Java类相映射,我们需要使用XML文件来做配对,比如:
<resultMap id="userResultMap" type="User">
<result column="user_id" property="id"/>
<result column="user_name" property="name"/>
<result column="user_age" property="age"/>
</resultMap>
看着很直观,但有个问题,如果我的User类中的字段名要修改,我还需要再XML文件中再调整。但如果使用注解
@Table(name = "User")
public class User {
@Id
@Column(name = "user_id")
private Integer id;
@Column(name = "user_name")
private String name;
@Column(name = "user_age")
private Integer age;
}
关系更加清晰,且调整更加灵活。所以我们就可以定义以下这几个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String name();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
}
注解处理器
有了注解后,并标识到类或方法中,如果不进行处理,则不会有任何影响,这里我们需要借助到反射机制,在运行时获取到这些信息,并自动生成对应的SQL。也就是编写注解处理器。
public class makeSQL {
public static void main(String[] args) {
Class<Person> aClass = Person.class;
Table table = aClass.getAnnotation(Table.class);
StringBuilder builder = new StringBuilder();
if (table != null) {
builder.append("creat table ");
builder.append(table.tableName()).append(" {");
}
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
Id id = field.getAnnotation(Id.class);
if (id != null) {
builder.append("id Bigint, ");
continue;
}
Column column = field.getAnnotation(Column.class);
if (column != null) {
builder.append(column.name()).append(" varchar(10) , ");
}
}
String sql = builder.substring(0, builder.length() - 3);
sql = sql + "};";
// 最终生成: creat table Person {id Bigint, userName varchar(10) , userAge varchar(10)};
System.out.println(sql);
}
}
MyTest 测试注解编写

对于上面的测试方法,相信大家都使用过,那为什么我们给方法上加入 @Test
这个方法就能被执行呢?一个类中不是只有 main方法会被执行吗?这里还是使用了反射的知识点,实际上通过反射获取一个类中拥有 @Test
的方法,然后在运行执行目标方法。下面我们用代码说话:
- 这里我们定义MyTest注解: 这里并没有写注解题,因为我们只打算起一个标识的作用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {}
- 创建单元测试类:这里我们test1加入了注解,但test2没有加入
public class TestJunit {
@MyTest
public void test1() {
System.out.println("this is test1");
}
public void test2() {
System.out.println("this is test2");
}
}
- 编写注解处理器:
public class MyTestDemo {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<TestJunit> aClass = TestJunit.class;
// 通过反射实例化对象
TestJunit instance = aClass.getDeclaredConstructor().newInstance();
// 获取该类的所有方法
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
// 找到带有 MyTest注解的 方法
MyTest myTest = method.getAnnotation(MyTest.class);
if (myTest != null) {
// 执行
method.invoke(instance);
}
}
}
}
最终打印:this is test1
其实JUnit也是使用类似的方式进行执行,再配合Idea的插件,所以可以直接点击按钮执行该测试方法。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 玲辰书斋!