在使用SpringBoot作為Web敏捷開發(fā)的框架之后,SpringBoot除了自動(dòng)裝配配置的便捷之外,在很多時(shí)候需要基于注解來開發(fā)。注解不僅增加了代碼的可讀性,還增加了開發(fā)的速度。這篇文章主要講述Java 注解。
創(chuàng)新互聯(lián)2013年開創(chuàng)至今,先為晉安等服務(wù)建站,晉安等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為晉安企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
元注解
元注解用于注解其他注解的。Java 5.0定義了4個(gè)標(biāo)準(zhǔn)的元注解,如下:
@Target
@Retention
@Documented
Inherited
現(xiàn)在來說說這四個(gè)元注解有什么作用。
@Target
@Target注解用于聲明注解的作用范圍,例如作用范圍為類、接口、方法等。它的取值以及值所對(duì)應(yīng)的范圍如下:
CONSTRUCTOR:用于描述構(gòu)造器
FIELD:用于描述域
LOCAL_VARIABLE:用于描述局部變量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述參數(shù)
TYPE:用于描述類、接口(包括注解類型) 或enum聲明
@Retention
該注解聲明了注解的生命周期,即注解在什么范圍內(nèi)有效。
SOURCE:在源文件中有效
CLASS:在class文件中有效
RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
大多數(shù)注解都為RUNTIME
@Documented
是一個(gè)標(biāo)記注解,有該注解的注解會(huì)在生成 java 文檔中保留。
@Inherited
該注解表明子類是有繼承了父類的注解。比如一個(gè)注解被該元注解修飾,并且該注解的作用在父類上,那么子類有持有該注解。如果注解沒有被該元注解修飾,則子類不持有父類的注解。
自定義注解
在Java開發(fā)者,JDK自帶了一些注解,在第三方框架Spring 帶了大量的注解,這些注解稱為第三方注解。在很多實(shí)際開發(fā)過程中,我們需要定義自己的注解。那么現(xiàn)在以案例的方式來講解自定義注解。
在注解中,需要使用四種元注解來聲明注解的作用范圍、生命周期、繼承,是否生成文檔等。另外在注解中也可以有自己的成員變量,如果一個(gè)注解沒有成員變量則稱為標(biāo)記注解。注解的成員變量,只支持原始類型、Class、Enumeration、Annoation。
現(xiàn)在定義一個(gè)@Writer注解,該注解被Retention、Documented、Inherited、Target修飾,表明該注解的作用范圍為類、接口和方法,生命周期為運(yùn)行時(shí)、該注解生成文檔,并且子類可繼承該注解。該注解有2個(gè)成員變量,一個(gè)為name一個(gè)為 age,代碼如下:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Writer {
? ? String name();
? ? int age();
}
那么有了該注解,怎么用呢?
該注解的作用范圍為類、方法,寫一個(gè)WriterTest,代碼如下:
@Writer(name = "forezp", age = 12)
public class WriterTest {
? ? @Writer(name = "miya", age = 10)
? ? public void writeBlog() {
? ? ? ? System.out.println("writing blog");
? ? }
? ??
? }
?
該類有了這個(gè)注解有何用?
一般來說,用該類修飾的類,需要通過反射來做一下邏輯的開發(fā)的工作,可廣泛用于AOP、程序的配置等。現(xiàn)在寫一個(gè)方法通過反射來解析該注解:
? public static void main(String[] args) throws ClassNotFoundException {
? ? ? ? Class c = Class.forName("com.forezp.annotation.WriterTest");
? ? ? ? if (c.isAnnotationPresent(Writer.class)) {
? ? ? ? ? ? Writer w = (Writer) c.getAnnotation(Writer.class);
? ? ? ? ? ? System.out.println("name:" + w.name() + "? ?age:" + w.age());
? ? ? ? }
? ? ? ? Method[] methods = c.getMethods();
? ? ? ? for (Method method : methods) {
? ? ? ? ? ? if (method.isAnnotationPresent(Writer.class)) {
? ? ? ? ? ? ? ? Writer w = method.getAnnotation(Writer.class);
? ? ? ? ? ? ? ? System.out.println("name:" + w.name() + "? ?age:" + w.age());
? ? ? ? ? ? }
? ? ? ? }
? ? }
這些代碼基本為反射的內(nèi)容,因?yàn)榉瓷湓诹硪黄恼乱呀?jīng)詳細(xì)講述過,不再重復(fù),運(yùn)行該Main方法,控制臺(tái)打印出如下內(nèi)容:
name:forezp age:12
name:miya age:10
案例實(shí)戰(zhàn)
有了上述的講解,你可能對(duì)注解有所了解,但是對(duì)注解的具體應(yīng)用并不是很深刻?,F(xiàn)在以一個(gè)案例來詳細(xì)講述。
大家都對(duì)ORM框架Mybitis都非常的熟悉,在這個(gè)框架中用了大量的注解。現(xiàn)在模仿這個(gè)框架,通過自定義注解,來解析sql 的查詢語句。實(shí)現(xiàn)過程大概如下:
定義@Table @Colum注解
定義一個(gè)實(shí)體User,定義一些基本的字段,并用注解修飾
用User類new對(duì)象,給對(duì)象的某些字段賦值
通過反射和注解來生成sql 的查詢語句
首先定義個(gè)一個(gè)Table注解,它的作用范圍為類,代碼如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface Table {
? ? String value() default "";
}
定義一個(gè)Column注解,作用范圍為字段,代碼如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Column {
? ? String value();
}
定義一個(gè)User類,在該類的加上@Table注解,在具體的字段上 @Column注解,代碼如下:
@Table("user")
public class User {
? ? @Column("id")
? ? private int id;
? ? @Column("name")
? ? private String name;
? ? @Column("age")
? ? private int age;
? ? @Column("address")
? ? private String address;
? ? ..//省略getter setter
? ?}
寫一個(gè)生成sql語句的類,它是通過反射來獲取表名、字段名,加上判斷實(shí)體對(duì)象的字段值來生成 查詢的 sql 語句的。代碼如下:
public class GenUserSql {
? ? public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
? ? ? ? User u1 = new User();
? ? ? ? User u2 = new User();
? ? ? ? u1.setId(1);
? ? ? ? u2.setName("forezp");
? ? ? ? genSql(u2);
? ? ? ? genSql(u1);
? ? }
? ? private static void genSql(User user) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
? ? ? ? Class c = user.getClass();
? ? ? ? StringBuilder stringBuilder = new StringBuilder();
? ? ? ? stringBuilder.append("select * from ");
? ? ? ? if (c.isAnnotationPresent(Table.class)) {
? ? ? ? ? ? Table table = (Table) c.getAnnotation(Table.class);
? ? ? ? ? ? String tableName = table.value();
? ? ? ? ? ? stringBuilder.append(tableName).append(" where 1=1 and ");
? ? ? ? }
? ? ? ? Field[] fields = c.getDeclaredFields();
? ? ? ? for (Field field : fields) {
? ? ? ? ? ? String columnName;
? ? ? ? ? ? if (field.isAnnotationPresent(Column.class)) {
? ? ? ? ? ? ? ? Column column = field.getAnnotation(Column.class);
? ? ? ? ? ? ? ? columnName = column.value();
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? String fieldName = field.getName();
? ? ? ? ? ? String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
? ? ? ? ? ? ? ? ? ? ? ?Method method = c.getMethod(getMethodName);
? ? ? ? ? ? Object fieldValue = method.invoke(user);
? ? ? ? ? ? if (fieldValue == null || ((fieldValue instanceof Integer) && (Integer) fieldValue == 0)) {
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? if (fieldValue instanceof Integer) {
? ? ? ? ? ? ? ? stringBuilder.append(columnName + "=" + fieldValue);
? ? ? ? ? ? }
? ? ? ? ? ? if (fieldValue instanceof String) {
? ? ? ? ? ? ? ? stringBuilder.append(columnName + "=" + "'" + fieldValue + "'");
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? System.out.println(stringBuilder.toString());
? ? }
}
運(yùn)行程序,控制臺(tái)打印如下:
select * from user where 1=1 and name=‘forezp’
select * from user where 1=1 and id=1