天天看點

注解面試題-請了解下

在面試的時候,有些面試官會問注解相關的問題, 注解最典型的代表架構就是Spring了,特别是Spring Boot出來之後,用注解代替了XML的配置,非常友善,今天我們就來聊聊注解相關的面試回答。

面試官的問法可能千奇百怪,我在這邊總結幾個常見的問題:

注解是什麼?

注解(Annotation),也叫中繼資料。一種代碼級别的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。

簡單來說注解其實就是代碼中的特殊标記,這些标記可以在編譯、類加載、運作時被讀取,并執行相對應的處理。

JDK内置了哪些注解?

Overried

Overried是告訴編譯器要檢查該方法是實作父類的方法。

Deprecated

Deprecated用于标記一些過時的代碼。

SuppressWarnings

SuppressWarnings用于消除一些警告資訊,使用集合的時候,如果沒有指定泛型,IDE會提示安全檢查的警告。

FunctionalInterface

FunctionalInterface是JDK8中的注解,用來指定該接口是函數式接口。

SafeVarargs

SafeVarargs是JDK 7中的注解,主要目的是處理可變長參數中的泛型,此注解告訴編譯器:在可變長參數中的泛型是類型安全的。

怎麼自定義一個注解?

在Java中,類使用class定義,接口使用interface定義,注解和接口的定義差不多,增加了一個@符号,即@interface,代碼如下:

public@interfaceEnableAuth{

}

注解中可以定義成員變量,用于資訊的描述,跟接口中方法的定義類似,代碼如下:

Stringname();

還可以添加預設值:

Stringname()default"猿天地";

上面的介紹隻是完成了自定義注解的第一步,開發中日常使用注解大部分是用在類上,方法上,字段上,示列代碼如下:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public@interfaceEnableAuth {

Target

用于指定被修飾的注解修飾哪些程式單元,也就是上面說的類,方法,字段

Retention

用于指定被修飾的注解被保留多長時間,分别SOURCE(注解僅存在于源碼中,在class位元組碼檔案中不包含),CLASS(預設的保留政策,注解會在class位元組碼檔案中存在,但運作時無法擷取),RUNTIME(注解會在class位元組碼檔案中存在,在運作時可以通過反射擷取到)三種類型,如果想要在程式運作過程中通過反射來擷取注解的資訊需要将Retention設定為RUNTIME

Documented

用于指定被修飾的注解類将被javadoc工具提取成文檔

Inherited

用于指定被修飾的注解類将具有繼承性

如何擷取注解中的值?

可以通過反射來判斷類,方法,字段上是否有某個注解以及擷取注解中的值, 擷取某個類中方法上的注解代碼示例如下:

Class clz = bean.getClass();

Method[] methods = clz.getMethods();

for(Method method : methods) {

if(method.isAnnotationPresent(EnableAuth.class)) {

String name = method.getAnnotation(EnableAuth.class).name();

通過isAnnotationPresent判斷是否存在某個注解,通過getAnnotation擷取注解對象,然後擷取值

工作中經常接觸的注解有哪些?

注解在很多架構中都應用非常多,這個問題你可以說下Spring中的即可,大概的解釋下每個注解的含義和用途,除了Spring還有很多别的架構中也有使用注解,比如Swagger, Lombok,JPA,Spring Data等

Component

Controller

Repository

Service

RequestMapping

RequestParam

RequestAttribute

RequestBody

ResponseBody

……

注解的使用場景?

生成文檔

Swagger中就是通過注解對接口,實體類中的字段進行描述生成可視化的文檔

代替配置檔案

Spring中Bean的裝載注入

導出資料

可以寫一個統一的導出工具類,傳入一個List<實體類>進去即可導出Excel檔案,Excel的表頭可以用注解加載字段上

架構層面的統一處理

注解在底層架構中用的比較多,在架構中需要考慮到通用性,能用注解做很多事情,比如對API進行權限控制,限流等操作都可以通過自定義注解來辨別是否需要進行認證,限流等,還有資料的緩存,典型的就是@Cacheable,還有異步方法的調用@Async,ORM架構中的使用,可以用注解辨別表名,字段名,JPA中,Spring Data架構中都有使用

權限控制詳細講解

比如我們有的接口需要認證才能調用,有的不需要,簡單的做法就是用配置的方式,将需要認證的接口配置好,然後進行攔截過濾,缺點是需要經常維護配置資訊,用注解可以避免這個情況。

可以自定義一個注解,隻要加了這個注解我們就對這個接口進行認證攔截操作,接下裡詳細的講解下這個功能實作。

定義開啟認證的注解,作用在方法上,運作時可擷取注解資訊

/**

  • 開啟API權限認證

*@authoryinjihuan

*

*/

在需要認證的接口上增加注解

@EnableAuth

@GetMapping("/userCollectCityInfo")

@ApiOperation(value="擷取登入使用者關注的城市資訊", notes="擷取登入使用者關注的城市資訊", produces ="application/json")

@ApiResponses(

@ApiResponse(response = UserCollectCityInfoDto.class, code = 200, message ="")

)

publicResponse getUserCollectCityInfos(HttpServletRequest request) {

try{

Longuid = UserInfoUtils.getLoginUserId(request);

List citys = cityCollectService.findAllByUid(uid);

List results = citys.stream().map(this::ofCityInfo)

.sorted((l1, l2) -> l1.getRangeLevel().compareTo(l2.getRangeLevel()))

.collect(Collectors.toList());

returnResponse.ok(results);

}catch(Exception e) {

logger.error("擷取登入使用者關注的城市資訊異常", e);

returnResponse.fail("擷取登入使用者關注的城市資訊異常");

在攔截器中進行攔截,攔截需要知道目前請求的接口是不是需要攔截的,我們可以在啟動時将所有增加了@EnableAuth的接口資訊儲存起來,這樣在攔截器中就知道哪個接口是需要認證。

初始化需要認證的接口資訊代碼如下:

  • API 驗證資料初始化

@Component

@Configuration

publicclassApiAuthDataInitimplementsApplicationContextAware{

publicstatic List checkApis = new ArrayList();

@Override

publicvoid setApplicationContext(ApplicationContext ctx) throws BeansException {

Map beanMap = ctx.getBeansWithAnnotation(RestController.class);

if(beanMap !=null) {

for(Object bean : beanMap.values()) {

String uri = getApiUri(clz, method);

checkApis.add(uri);

privateString getApiUri(Class clz, Method method) {

StringBuilder uri = new StringBuilder();

uri.append(clz.getAnnotation(RequestMapping.class).value()[0]);

if(method.isAnnotationPresent(GetMapping.class)) {

uri.append(method.getAnnotation(GetMapping.class).value()[0]);

}elseif(method.isAnnotationPresent(PostMapping.class)) {

uri.append(method.getAnnotation(PostMapping.class).value()[0]);

}elseif(method.isAnnotationPresent(RequestMapping.class)) {

uri.append(method.getAnnotation(RequestMapping.class).value()[0]);

returnuri.toString();

實作ApplicationContextAware接口,然後通過getBeansWithAnnotation擷取所有接口的bean資訊,通過RestController注解來擷取,也就是說隻要class上增加了RestController注解,這邊就都能擷取到。

然後通過反射擷取bean中所有的方法,如果有增加EnableAuth的話就擷取接口的uri存儲到map中,這樣過濾器中就可以根據map中的值來判斷是不是需要進行權限認證了。