1、註解是什麼?
- 註解是往源代碼中添加描述信息的機制,這些信息可由編譯器測試和驗證。
- 該信息後續可通過反射機制提取。然後根據描述,可定義不同的業務邏輯,實現豐富多彩的功能。
注意:註解是靜態的,是描述性的,沒有任何功能。是註解處理器給了註解強大的力量。
2、註解怎麼用?
- 關鍵詞 @interface,表現也更新一個接口,屬性按方法用。
- 註解2要素:要明確@Target(作用在類、還是方法)、@Retention(在哪一級,如SOURCE、CLASS、RUNTIME即程序執行時也可提出)。
- 註解中包含一些元素,處理註解時會用到(註解處理器)。
- 用註解處理器,根據標籤填寫業務邏輯,很強大。
- 註解常用對象:
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";
}
請看:
- @Target:指這是修飾什麼的註解。這裏是指描述方法的。
- @Retention:指註解保存在哪裏。這裏是保存到運行時,也就是會保存到class文件中。
- @interface:是關鍵字
- name():註解的屬性表現的更新方法。
- 但看註解有什麼用呢?沒用!
註解處理器
有個業務場景,我寫了個模塊,需求裏有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 》第四版