Java的註解是什麼?及現實中的應用

1、註解是什麼?

  1. 註解是往源代碼中添加描述信息的機制,這些信息可由編譯器測試和驗證。
  2. 該信息後續可通過反射機制提取。然後根據描述,可定義不同的業務邏輯,實現豐富多彩的功能。
    注意:註解是靜態的,是描述性的,沒有任何功能。是註解處理器給了註解強大的力量。

2、註解怎麼用?

  1. 關鍵詞 @interface,表現也更新一個接口,屬性按方法用。
  2. 註解2要素:要明確@Target(作用在類、還是方法)、@Retention(在哪一級,如SOURCE、CLASS、RUNTIME即程序執行時也可提出)。
  3. 註解中包含一些元素,處理註解時會用到(註解處理器)。
  4. 用註解處理器,根據標籤填寫業務邏輯,很強大。
  5. 註解常用對象:
    Object.getAnnotation():從目標對象獲取註解對象。
    Field.getDeclaredAnnotations():從對象屬性獲取註解對象。
    anns[0] instanceof SQLInteger:註解類型判斷。

如,我們定義一個註解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
//    註解包含2個信息,且標記了類型,可由編譯器檢查,是比外部文檔好用的地方
    public int id();
    public String description() default "no descriptino";
}

請看:

  1. @Target:指這是修飾什麼的註解。這裏是指描述方法的。
  2. @Retention:指註解保存在哪裏。這裏是保存到運行時,也就是會保存到class文件中。
  3. @interface:是關鍵字
  4. name():註解的屬性表現的更新方法。
  5. 但看註解有什麼用呢?沒用!

註解處理器

有個業務場景,我寫了個模塊,需求裏有n個場景需要覆蓋。有沒有辦法檢測我到底覆蓋全場景了嗎?可以使用上述的註解了。
業務代碼:

public class PasswordUtils {
    @UseCase(id = 47, description = "Please must contain at least on numric")
    public boolean validatePassword(String password) {
        return (password.matches("\\w*\\d\\w*"));
    }

    @UseCase(id = 48)
    public String encryptPassword(String password) {
        return new StringBuffer(password).reverse().toString();
    }

    @UseCase(id = 49, description = "New password can't equal previously used ones")
    public boolean checkForNewPassword(List<String> prevPasswords, String password) {
        return !prevPasswords.contains(password);
    }
}

註解是靜態的,還是沒用啊。該註解處理器出廠了。

public class UseCaseTracker {

    public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
//        cl是目標類,找目標類的方法
        for (Method m : cl.getDeclaredMethods()) {
//            找目標類方法上指定類型的註解
            UseCase uc = m.getAnnotation(UseCase.class);
            if (uc != null) {
                System.out.println("Found Use Case:"+uc.id()+" "+uc.description());
                useCases.remove(new Integer(uc.id()));
            }
        }
        for (int i : useCases) {
            System.out.println("Warning:Missing use case=" + i);
        }

        /*通過這個例子,得到:
        1、註解與源代碼關聯起來了
        2、註解有結構化的數據
        3、註解可通過反射機制獲取
        4、註解處理器可以實現業務邏輯,如這裏的代碼覆蓋案例的情況  */

    }

    public static void main(String[] args) {
        List<Integer> useCases=new ArrayList<Integer>();
        Collections.addAll(useCases, 47, 48, 49,50);
        trackUseCases(useCases,PasswordUtils.class);
    }
}

輸出:

Found Use Case:49 New password can't equal previously used ones
Found Use Case:47 Please must contain at least on numric
Found Use Case:48 no descriptino
Warning:Missing use case=50

3、註解的好處,相對xml配置文件?

	1.簡化配置,不需要額外的解析工具
	2.編譯器及可驗證正確,防止xml手誤,排查半天
	缺點:
	1.因爲是編譯進class文件,故修改了文件要重新編譯;xml只重啓應用即可;
	一般:
	1.可能修改的如數據源等,用xml配置;
	2.ioc用註解。

4、註解實例:

4.1.用註解標記測試用例覆蓋度

見上述的例子。

4.2.用註解提起SQL的DDL

註解:

@Target(ElementType.TYPE) //只能註解class
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    public String name() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    String name() default "";
    Constrains constraints() default  @Constrains;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    int value() default 0;
    String name() default "";
    Constrains constraints() default @Constrains;
}

業務代碼或配置:

@DBTable(name = "MEMBER")
public class Member {
    @SQLString(30) String firstName;
    @SQLString(50) String lastName;
    @SQLInteger Integer age;
    @SQLString(value = 30,constraints = @Constrains(primaryKey = true))
    String handle;
    static int memberCount;

    public String getHandle() {
        return handle;
    }

    public String getFirstName() {
        return firstName;
    }
    public String getLastName(){
        return  lastName;
    }
    public String toString(){
        return handle;
    }

    public Integer getAge() {
        return age;
    }
}

註解處理器:

public class TableCreator {


    public static void main(String[] args) throws Exception{
        if (args.length < 1) {
            System.out.println("Arguments:annotaed classes");
            System.exit(0);
        }

        for (String className : args) {
            Class<?> cl = Class.forName(className);
                /*從目標對象cl獲取註解對象*/
                DBTable annoDbTable = cl.getAnnotation(DBTable.class);
                if (annoDbTable == null) {
                    System.out.println("No DBTable annotations in class "+className);
                    continue;
            }

            String tableName=annoDbTable.name();
            if(tableName.length()<1){
                tableName = cl.getName().toUpperCase();
            }
            System.out.println("tableName:"+tableName);


            List<String> columnDefs=new ArrayList<String>();
            for(Field field:cl.getDeclaredFields()){
                String columnName=null;
                Annotation[] anns=field.getDeclaredAnnotations();
                if(anns.length<1)
                    continue;

                if (anns[0] instanceof SQLInteger) {
                    SQLInteger sInt=(SQLInteger)anns[0];
                    if (sInt.name().length() < 1) {
                        columnName = field.getName().toUpperCase();
                    } else {
                        columnName=sInt.name();
                    }
                    columnDefs.add(columnName + " INT" + getConstraints(sInt.constraints()));
                }

                if (anns[0] instanceof SQLString) {
                    SQLString sString=(SQLString)anns[0];
                    if (sString.name().length() < 1) {
                        columnName = field.getName().toUpperCase();
                    } else {
                        columnName=sString.name();
                    }
                    columnDefs.add(columnName + " varchar(" + sString.value() + ")" + getConstraints(sString.constraints()));
                }
                StringBuilder createCommand=new StringBuilder("create table "+tableName+"(");
                for(String columnDef:columnDefs)
                    createCommand.append("\n " + columnDef + ",");

                String tableCreate = createCommand.substring(0, createCommand.length() - 1) + ");";
                System.out.println("Table creation sql for "+className+" is :\n"+tableCreate);

            }

        }
    }

    private static String getConstraints(Constrains con) {
        String constraints="";
        if(!con.allowNull()) constraints += " not null";
        if(!con.primaryKey()) constraints += " primary key";
        if(con.unique()) constraints += " unique";
        return constraints;
    }
}

輸出:

Table creation sql for com.master.Member is :
create table MEMBER(
 FIRSTNAME varchar(30) primary key,
 LASTNAME varchar(50) primary key,
 AGE INT primary key,
 HANDLE varchar(30));

是不配合處理起可以衍生出精彩的功能!

5、註解spring框架中的應用

1.註解實現IOC

註冊Bean

@Component("myServiceB")
public class ServiceB implements IService {
    @Value("serviceB")
    private String name;
    @Value("12")
    private int age;

    public ServiceB() {
        System.out.println("========New a ServiceB without parameters");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public String toString() {
        return this.name+";"+this.age;
    }
}

spring獲取管理的Bean

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        /*IService serviceA=(ServiceA)ac.getBean("myServiceA");
        System.out.println(serviceA);*/
        ServiceB serviceB = (ServiceB) ac.getBean("myServiceB");
        System.out.println(serviceB);
    }
}

就這麼簡單。

2.註解實現IOC的原理

實現過程分解的很細,但大概原理與上邊的例子類似。
簡介下別人的總結:
第一步,初始化時設置了Component類型過濾器;
第二步,根據指定掃描包掃描.class文件,生成Resource對象;
第三步、解析.class文件並註解歸類,生成MetadataReader對象;
第四步、使用第一步的註解過濾器過濾出有@Component類;
第五步、生成BeanDefinition對象;
第六步、把BeanDefinition註冊到Spring容器。以上是@Component註解原理,@Service、@Controller和@Repository上都有@Component修飾,所以原理是一樣的。

參考:

https://www.cnblogs.com/wolf-bin/p/11667208.html
《think in java 》第四版

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章