引言
在日常的開發過程中,其實每個人都用到了註解,最常見的就是重寫@Override。既然這麼常見爲什麼還要放入不常見的模塊中呢?在本篇博文中會詳細介紹關於註解的概念和各個組成部分,同時會寫出一個demo來說明自定義註解使用的一種情況。筆者目前整理的一些blog針對面試都是超高頻出現的。大家可以點擊鏈接:http://blog.csdn.net/u012403290。
技術點
1、註解
註解也叫元數據,是一種形式化的方法,目的是爲了在代碼中添加信息,使我們可以想用這些信息的時候能快速的使用。註解可以簡化代碼。比如下面就是一個簡單的註解:
package com.brickworkers;
@Retention(RetentionPolicy.RUNTIME)//在運行時期保留,只有這樣纔可以在反射的時候拿到註解信息
@Target(ElementType.METHOD)//註解針對與方法
public @interface UserRole {
}
從上面可以看到,註解看上去很像接口的定義,要注意辨別。註解也會編譯成class文件的。
2、元註解
在java中有四種元註解,上面例子中的@Retention和@Target就是兩個元註解。他們的名稱和用途如下:
@Target源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
@Retention源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
註解的讀取
註解其實就是一個狀態標識,可以標明註解所聲明的範圍狀態。所以我們必須要有一套機制,這套機制用於讀取註解的值。在java中,我們一般都用反射來作爲註解查找器。我們下面用一個例子來標明如何查找註解:
UserRole自定義註解類,標明該用戶的身份:
package com.brickworkers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Brickworker
* Date:2017年5月2日下午1:06:24
* 關於類UserRole.java的描述:自定義註解,用於權限控制
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
@Retention(RetentionPolicy.RUNTIME)//在運行時期保留,只有這樣纔可以在反射的時候拿到註解信息
@Target(ElementType.METHOD)//註解針對於方法
public @interface UserRole {
static enum User{
FATHER, MATHER, SON;
}
User value() default User.SON;
}
大家仔細觀察上面的代碼,我定義了一個內部枚舉,也是爲了偷懶,其實最好是把這個枚舉定義在外部。我賦予了3種身份權限,分別是爸爸,媽媽,兒子。同時這個自定義枚舉的運行規則請參照上面介紹的元註解信息,表示這個註解是定義在方法上的,在運行時期保留的,可以通過反射獲取到註解信息。
同時,如果在方法上使用此註解,如果不添加詳細的身份信息,那麼就默認是SON,比如下面這段代碼:
// 騎玩具車
@UserRole()
public void rideToyCar() {
System.out.println(this.name+"正在騎玩具車");
}
在括號中不添加詳細的身份,那麼按照定義會默認SON。
Family類,家人類,用於表示家人對象和一些操作:
package com.brickworkers;
import com.brickworkers.UserRole.User;
/**
*
* @author Brickworker
* Date:2017年5月2日下午1:35:09
* 關於類Family.java的描述:家人類
* Copyright(c) 2017, brcikworker All Rights Reserved.
*/
public class Family {
private String name;
private UserRole.User role;
public Family(String name, UserRole.User role) {
this.name = name;
this.role = role;
}
public Family() {
this.name = "爸爸";
this.role = User.FATHER;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UserRole.User getRole() {
return role;
}
public void setRole(UserRole.User role) {
this.role = role;
}
// 騎玩具車
@UserRole(UserRole.User.SON)
public void rideToyCar() {
System.out.println(this.name+"正在騎玩具車");
}
// 吸菸
@UserRole(UserRole.User.FATHER)
public void smoke() {
System.out.println(this.name+"正在吸菸");
}
// 打麻將
@UserRole(UserRole.User.MATHER)
public void playMahjong() {
System.out.println(this.name+"正在打麻將");
}
}
上面就是家人類,在這個類中標明瞭家人的身份和狀態,同時提供了3種操作方法,但是不同的人有不同的操作權限。
AnnotationTest類,註解測試類
package com.brickworkers;
import java.lang.reflect.Method;
import com.brickworkers.UserRole.User;
/**
*
* @author Brickworker
* Date:2017年5月2日下午1:07:37
* 關於類AnnotationTest.java的描述:註解測試類
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class AnnotationTest {
//我們通過反射判斷某一個方法是否具有權限
public static boolean permission(String methodName, UserRole.User user) throws ClassNotFoundException, NoSuchMethodException, SecurityException{
Class<?> clazz = Class.forName("com.brickworkers.Operate");
Method method = clazz.getMethod(methodName);
UserRole userRole = method.getAnnotation(UserRole.class);
if(user == userRole.value()){//判斷這個身份是否能操作這個方法
return true;
}
return false;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
//定義一個兒子
Family son = new Family("兒子", UserRole.User.SON);
//定義一個爸爸
Family father = new Family("爸爸", UserRole.User.FATHER);
//兒子去嘗試抽菸
//增加判斷權限
if(permission("smoke", son.getRole())){
son.smoke();
}else{
System.out.println("該身份的用戶不允許抽菸!!");
}
//爸爸嘗試抽菸
if(permission("smoke", father.getRole())){
father.smoke();
}else{
System.out.println("該身份的用戶不允許抽菸!!");
}
}
}
//輸出結果
//該身份的用戶不允許抽菸!!
//爸爸在吸菸
在反射中,我們獲取到即將要執行的方法的註解信息,和目前要執行的用戶身份進行判斷,如果身份一致則返回true,如果身份不一致,那麼就返回false。這也是一種很好的權限驗證機制。
進一步反思
我們前面已經實現了用註解+反射的機制進行簡單的權限控制。回想我以前有一篇文章,詳細介紹了兩種代理方式:JDK動態代理與CGlib代理。(文章鏈接:http://blog.csdn.net/u012403290/article/details/64443021)我們可以控制到方法執行之前和方法執行之後,那麼運用到註解中來,豈不美哉?下面就是我們修改之後的實現。
思考:
①我們沒有使用抽象的接口來描述方法,所以我們直接用CGLib動態代理。
②要實現CGLib動態代理,我們需要實現MethodInterceptor 接口。
如果你嘗試實現本博文的代碼,但是沒有依賴的話,可以去上面提到的博文鏈接中去尋找,我在那篇文章中留了下載CGLib動態代理的依賴jar包的地址。
以下是MyCglib類,實現了MethodInterceptor 接口
package com.brickworkers;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyCglib implements MethodInterceptor {
//目標對象
private Object obj = null;
public Object getProxy(Object obj){
this.obj = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
// 回調方法
enhancer.setCallback(this);
// 創建代理對象
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
//在執行方法之前進行身份判斷
if(obj instanceof Family){
Family family = (Family) obj;
//如果用戶權限符合方法的權限要求
if(method.getAnnotation(UserRole.class).value().equals(family.getRole())){
result = methodProxy.invoke(obj, args);
}else{//如果不符合權限要求則拋出一個錯誤或者生產一個提示
System.err.println(family.getName()+"不具有操作"+method.getName()+"方法的權限");
}
}
return result;
}
}
AnnotationTest類,測試結果:
package com.brickworkers;
import com.brickworkers.UserRole.User;
/**
*
* @author Brickworker
* Date:2017年5月2日下午1:07:37
* 關於類AnnotationTest.java的描述:註解測試類
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class AnnotationTest {
public static void main(String[] args){
MyCglib myCglib = new MyCglib();
Family son = (Family) myCglib.getProxy(new Family("兒子", User.SON));
son.smoke();
son.rideToyCar();
Family father = (Family) myCglib.getProxy(new Family("爸爸", User.FATHER));
father.smoke();
father.rideToyCar();
}
}
//運行結果
//兒子不具有操作smoke方法的權限
//兒子正在騎玩具車
//爸爸不具有操作rideToyCar方法的權限
//爸爸正在吸菸
尾記
註解的知識其實是博大精深的,尤其是在現代的項目開發中。比如說Spring中,hibernate中等等,都用了大量的註解來簡化代碼量。註解使用起來非常方便,比如我們文章中的例子,以後又來一個新的方法,爲了保護方法,我們可以定義好這個方法的執行權限,沒有達到權限的一律屏蔽,可以達到增強系統安全性的作用。
在RestFul框架的開發過程中,我們用HTTP協議暴露接口,我們就可以在每一個接口上定義好一種註解,每種註解可以標明這個方法是給誰提供的,比如說用戶即使知道了管理員的一個接口,但是他仍舊不具備權限訪問。因爲基於HTTP協議,我們通常把註解結合url攔截器共同使用。
希望對大家有所幫助。