注解面试题-请了解下

金三银四,三四月是找工作最好的时期。错过了三月千万别放弃四月。

在面试的时候,有些面试官会问注解相关的问题, 注解最典型的代表框架就是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 @interface EnableAuth {
}

注解中可以定义成员变量,用于信息的描述,跟接口中方法的定义类似,代码如下:

public @interface EnableAuth {
    String name();
}

还可以添加默认值:

public @interface EnableAuth {
    String name() default "猿天地";
}

上面的介绍只是完成了自定义注解的第一步,开发中日常使用注解大部分是用在类上,方法上,字段上,示列代码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableAuth {
}
  • 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权限认证
 * @author yinjihuan
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableAuth {
}

在需要认证的接口上增加注解

@EnableAuth
@GetMapping("/userCollectCityInfo")
@ApiOperation(value="获取登录用户关注的城市信息", notes="获取登录用户关注的城市信息", produces = "application/json")
@ApiResponses(
        @ApiResponse(response = UserCollectCityInfoDto.class, code = 200, message = "")
)
public Response getUserCollectCityInfos(HttpServletRequest request) {
    try {
        Long uid = UserInfoUtils.getLoginUserId(request);
        List<CityCollect> citys = cityCollectService.findAllByUid(uid);
        List<UserCollectCityInfoDto> results = citys.stream().map(this::ofCityInfo)
                    .sorted((l1, l2) -> l1.getRangeLevel().compareTo(l2.getRangeLevel()))
                    .collect(Collectors.toList());
        return Response.ok(results);
    } catch (Exception e) {
        logger.error("获取登录用户关注的城市信息异常", e);
        return Response.fail("获取登录用户关注的城市信息异常");
    }
}

在拦截器中进行拦截,拦截需要知道当前请求的接口是不是需要拦截的,我们可以在启动时将所有增加了@EnableAuth的接口信息保存起来,这样在拦截器中就知道哪个接口是需要认证。

初始化需要认证的接口信息代码如下:

/**
 * API 验证数据初始化
 * @author yinjihuan
 *
 */
@Component
@Configuration
public class ApiAuthDataInit implements ApplicationContextAware {
    public static List<String> checkApis = new ArrayList<String>();
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        Map<String, Object> beanMap = ctx.getBeansWithAnnotation(RestController.class);
        if (beanMap != null) {
            for (Object bean : beanMap.values()) {
                Class<?> clz = bean.getClass();
                Method[] methods = clz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(EnableAuth.class)) {
                        String uri = getApiUri(clz, method);
                        checkApis.add(uri);
                    }
                }
            }
        }
    }
    private String 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]);
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            uri.append(method.getAnnotation(PostMapping.class).value()[0]);
        } else if (method.isAnnotationPresent(RequestMapping.class)) {
            uri.append(method.getAnnotation(RequestMapping.class).value()[0]);
        }
        return uri.toString();
    }
}

实现ApplicationContextAware接口,然后通过getBeansWithAnnotation获取所有接口的bean信息,通过RestController注解来获取,也就是说只要class上增加了RestController注解,这边就都能获取到。

然后通过反射获取bean中所有的方法,如果有增加EnableAuth的话就获取接口的uri存储到map中,这样过滤器中就可以根据map中的值来判断是不是需要进行权限认证了。

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2018-04-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Kirito的技术分享

解析Spring中的ResponseBody和RequestBody

spring,restful,前后端分离这些关键词都是大家耳熟能详的关键词了,一般spring常常需要与前端、第三方使用JSON,XML等形式进行交互,你也一定...

3.6K17
来自专栏微服务生态

Flume-NG源码分析-整体结构及配置载入分析

终于开始Flume源码的分析研究工作了,我也是边学边和大家分享,内容上难免有不足之处,望大家见谅。

1434
来自专栏程序猿DD

【译】Spring 官方教程:创建批处理服务

原文:Creating a Batch Service 译者:Mr.lzc 校对:lexburner 本指南将引导你完成创建基本的批处理驱动解决方案的过程。 你...

5467
来自专栏小勇DW3

AOP中使用Aspectj对接口访问权限进行访问控制

只配置这段会报:The prefix "aop" for element "aop:aspectj-autoproxy" is not bound.

1774
来自专栏玩转JavaEE

Spring RestTemplate中几种常见的请求方式

在Spring Cloud中服务的发现与消费一文中,当我们从服务消费端去调用服务提供者的服务的时候,使用了一个很好用的对象,叫做RestTemplate,当时我...

9986
来自专栏java相关

SpringBoot中Async异步方法和定时任务介绍

2094
来自专栏Java爬坑系列

【Java入门提高篇】Day5 Java中的回调(二)

  Java中有很多个Timer,常用的有两个Timer类,一个java.util包下的Timer,一个是javax.swing包下的Timer,两个Timer...

1949
来自专栏生信宝典

Python学习教程(五)

作业(二) 将 “作业(一)” 中的程序块用函数的方式重写,并调用执行 def func(para1,para2,…): func(para1,para2,…)...

2089
来自专栏移动开发

利用Gson将对象转换成json,忽略某些字段的方法

有时我们将对象转换成json格式的字符串时,并不希望里面所有的字段都转换,我们可能希望忽略某些.默认的gson是全部转换的,为此要给gson做一些配置.其实Gs...

5293
来自专栏技术墨客

Java数据校验详解

一个健壮的系统都要对外部提交的数据进行完整性、合法性的校验。即使开发一个不面对最终用户的工具包,也需要对传入的数据进行缜密的校验来防止引发底层难以追踪的问题。各...

2801

扫码关注云+社区

领取腾讯云代金券