一、爲什麼要學習註解
①因爲註解現在非常流行,在很多的時候都被應用,特別是框架相關代碼,我們第一步也就是拿到別人用註解寫的代碼做到能看懂
②讓編程更簡潔,代碼更加清晰
③逼格更高...
二、什麼是註解
註解是在jdk1.5之後被引入的,Java提供了一種源程序中的元素關聯任何信息和任何元數據的途徑和方法。
三、Java中的常見註解
1.jdk自帶的註解
@Override @Deprecated @Suppvisewarnings
@Override 覆蓋的意思,實現接口,繼承父類重寫方法時的註解(代碼演示在下方自定義註解的子類方法重寫父類方法)
@Deprecated:表示該方法已過期,在接口裏經常用到,表示該方法已經過期了,但還能用(代碼演示見下面自定義註解中父類定義的一個過期的sing方法,在Test類中顯示過期)@SuppressWarnings("deprecation"):該註解表示忽略了deprecation這樣的一個警告
public class Test {
//該註解表示忽略了deprecation這樣的一個警告
@SuppressWarnings("deprecation")
public void sing(){
Person s = new Son();
//sing()方法已經過時 會有警告
s.sing();
}
}
2.常見的第三方註解
①Spring
@Autowired @Service @Repository
@Autowired:自動生成實例,並注入進去
@InserProvider @UpdateProvider @Opitions
四、註解的分類
1.按照運行機制來分
源碼註解 編譯時註解 運行時註解
①源碼註解:該註解只在源碼中出現,當編譯成.class文件的時候,該註解就木得了
②編譯時註解:該註解在源碼中和.class文件中都存在 jdk自帶的註解都屬於編譯時註解
③運行時註解:在運行時也起作用,甚至會影響運行時的邏輯,比如Spring中的@Autowired
2.按照來源來分
來自jdk自帶的註解 來自第三方的註解 自己定義的註解 元註解:給註解進行註解
五、自定義註解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義註解
* @author zhmm
*
*/
//元註解(註解的註解)
//說明的是該註解的作用域
@Target({ElementType.METHOD,ElementType.TYPE})
//說明的是該註解的生命週期
@Retention(RetentionPolicy.RUNTIME)
//標識性元註解,允許子類繼承,只適用於類繼承,不適用於接口繼承,且只會繼承類的註解,不會繼承方法的註解
@Inherited
//標示性元註解,表示的是生成javadoc的時候會包含此註解
@Documented
//使用@interface關鍵字來定義註解
//成員的類型是受限的除了八種基本數據類型之外其成員類型還可以爲String,Class,Annotation,Enumeration
public @interface Description{
// //成員以無參無異常方式聲明
// String desc();
// String author();
// //可以用default來給成員定義一個默認的值
// int age() default 18;
String value();
}
注意事項:若註解只有一個成員,則該成員必須起名爲value(),在使用的時候可以忽略成員名和賦值號(=)(因爲當註解只一個成員的時候,在使用註解時,直接寫該唯一成員的值就行,不用寫成員名)註解類可以沒有成員,沒有成員的註解被稱爲標示註解。
元註解:就是在定義註解的時候所使用的註解,已在上面的代碼中指出,不作贅述。
五、如何使用註解
1.使用註解的語法
@<註解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,...)
例子:使用上個筆記自定義的註解
@Description(desc="i am eyeColor",author="zhangsan",age=18)
public String eyeColor(){
return "red";
}
2.有關自定義註解的代碼演示
使用的是上面的自定義註解:
@Description("我是父類")
public class Person {
@Description("我是方法")
public String name() {
return null;
}
public int age() {
return 0;
}
//該註解表示該方法已經過時
//適用於接口裏的方法已經不需要用的時候
@Deprecated
public void sing() {
}
}
//@Description("the name class annotation")
public class Son extends Person{
@Override
// @Description("the name method annotation")
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
@Override
public void sing() {
}
}
六、解析註解
其實就是通過反射獲取類、函數或成員上的運行時註解信息,從而實現動態控制程序運行的邏輯
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 解析註解
* @author zhmm
*
*/
public class Test2 {
public static void main(String[] args) {
//1.第一步,使用類加載器加載使用該註解的類
try {
Class c = Class.forName("com.ann.test.Son");
//2.拿到該類上的類註解
boolean isExist = c.isAnnotationPresent(Description.class);
if(isExist){
//3.拿到該註解實例
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.value());
}
//4.找到方法的註解
Method [] ms = c.getMethods();
for(Method m : ms){
boolean isMExist = m.isAnnotationPresent(Description.class);
if(isMExist){
Description d = (Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
//另外一種解析方法註解的方法
for(Method m : ms){
Annotation [] as = m.getAnnotations();
for(Annotation a:as){
if(a instanceof Description){
Description d = (Description) a;
System.out.println(d.value());
}
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
七。項目實戰
1.項目說明:
該項目取自資格公司的持久層框架,用來代替hibernate的解決方案,核心代碼就是通過註解來實現的
2.需求說明:①有一張用戶表,字段包括用戶的ID,用戶名,暱稱,年齡,性別,所在城市,郵箱,手機號
②方便的對每個字段或字段的組合條件進行檢索,並打印出SQL
分析:因爲我們要做持久化操作,所以必然會有一個和表對應的bean,我們給他取名Filter,需要表的註解個字段的註解,也就是兩個註解@Table和@Column,還需要一個測試類來根據我們所輸入的查詢條件動態生成一個SQL語句來返回給數據庫連接完成查詢(因爲和數據庫交互代碼固定,我們只做到動態生成SQL語句即可),下面來看代碼:
package com.ann.project;
/**
* Filter對應的就是我們的bean,
* 它裏面有一些屬性和對應的get set方法
* 這個類用來和數據庫來進行一個映射
* @author zhmm
* 當要查詢的表不一樣的時候我們只需要改一下Table註解的實參
* 還有@Column的實參以及對應bean中字段的信息即可
*
*/
//這個註解表示這個類對應的是數據庫的student表
@Table("student")
public class Filter {
//這個註解表示的是數據庫表中的相關字段
@Column("id")
private int stuId;
@Column("name")
private String stuName;
@Column("email")
private String email;
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package com.ann.project;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
//作用域:類或者接口
@Target({ElementType.TYPE})
//生命週期:運行時
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
//我們只需要一個名字來表示表名即可
public String value();
}
package com.ann.project;
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();
}
package com.ann.project;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 1.項目說明:
* 該項目取自資格公司的持久層框架,用來代替hibernate的解決方案,核心代碼就是通過註解來實現的
* 2.需求說明:
* ①有一張用戶表,字段包括用戶的ID,用戶名,暱稱,年齡,性別,所在城市,郵箱,手機號(由於數據原因我們只謝了ID 姓名和郵箱)
* ②方便的對每個字段或字段的組合條件進行檢索,並打印出SQL
*
*
* 這個測試類其實就是完成查詢的功能
* 根據不同的條件去檢索數據
* @author zhmm
*
*/
public class Test {
public static void main(String[] args) {
//檢索ID爲10的數據
Filter f1 = new Filter();
f1.setStuId(10);
//檢索名字爲小猛同學的數據
Filter f2 = new Filter();
f2.setStuName("小猛同學"); //此處我們進行模糊查詢
//檢索郵箱爲這兩個任意一個的數據
Filter f3 = new Filter();
f3.setEmail("[email protected],[email protected]");
//調用相關的SQL方法
String sql1 = query(f1);
String sql2 = query(f2);
String sql3 = query(f3);
//打印相關的SQL語句
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
}
@SuppressWarnings("unchecked")
private static String query(Filter f) {
StringBuffer sb = new StringBuffer();
//1.獲取到 class
Class c= f.getClass();
//判斷這個註解是否存在
boolean isExist = c.isAnnotationPresent(Table.class);
if (!isExist) {
return null;
}
//2.獲取表名
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
//拼接SQL語句(把表名加入我們的SQL語句)
sb.append(" select * from ").append(tableName).append(" 1=1 ");
//循環遍歷所有的字段名
Field[] fArray = c.getDeclaredFields();
for (Field field : fArray) {
//判斷這個字段是否爲我們想要的字段
boolean fExist = field.isAnnotationPresent( Column.class);
if (!fExist) {
continue;
}
//3.拿到字段名
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
//4.拿到字段的值
//獲取字段的名稱
String fieldName = field.getName();
//拼裝成獲取該字段的get方法
String getMethodName = "get" +fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Object fieldValue=null;
try {
//類反射拿到該get方法
Method getMethod =c.getMethod(getMethodName);
//執行該get方法拿到該字段的值
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
}
//5.拼裝sql
//如果他是null或者當該字段爲int類型爲0的時候不作處理
if (fieldValue == null || (fieldValue instanceof Integer && (Integer)fieldValue == 0) ) {
continue;
}
sb.append(" and ").append( columnName );
if (fieldValue instanceof String){
//如果有,我們說明他是個子查詢就進行子查詢的拼接
if (((String) fieldValue ).contains(",")) {
//字段按,進行分割
String[] values = ((String) fieldValue ).split(",");
//加in
sb.append(" in ( ");
//因爲都是字符串,所以每個都要加''
for (String v : values) {
sb.append("'").append(v).append("',");
}
//刪掉最後一個逗號
sb.deleteCharAt(sb.length()-1);
sb.append(" )");
//是string類型的加''
} else{
sb.append(" = '").append(fieldValue).append("' ");
}
//如果是int類型的不加''
}else if (fieldValue instanceof Integer){
sb.append(" = ").append(fieldValue).append(" ");
}
}
//返回查詢SQL語句
return sb.toString();
}
}