我是JavaPub,最少必要面试题,助力突击面试。
以下都是Java的基础面试题,相信大家都会有种及眼熟又陌生的感觉、看过可能在短暂的面试后又马上忘记了。JavaPub在这里整理这些容易忘记的重点知识及解答,建议收藏,经常温习查阅
。
说一下Java注解
在Java编程中,注解非常常见,注解的本质是什么?
注解大致分为以下三种:
- Java原生注解 如@Override,@Deprecated 等。大多用于 [标记] 和 [检查] 。
- 第三方注解,如 Spring、Mybatis等定义的注解(@Controller,@Data)。
- 自定义注解。
Java原生除了提供基本注解,还提供了 meta-annotation(元注解)。这些类型和它们所支持的类在java.lang.annotation包中可以找到。
- @Target
- @Retention
- @Documented
- @Inherited
一般比较常用的有 @Target,@Retention。@Target
表示这个注解可以修饰那些地方(比如类、方法、成员变量),@Retention
主要是设置注解的生命周期。
这是你一定会被问,
- 有使用过注解吗?
- 你是怎么使用的?
注解有一个非常常见的使用场景,大家可以用这个来理解学习。
场景一:自定义注解+拦截器 实现登录校验
实现功能:
接下来,我们使用springboot拦截器实现这样一个功能,如果方法上加了@LoginRequired,则提示用户该接口需要登录才能访问,否则不需要登录。
首先定义一个LoginRequired注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired { }
然后写两个简单的接口,访问sourceA,sourceB资源
@RestController public class IndexController { @GetMapping("/sourceA") public String sourceA(){ return "你正在访问sourceA资源"; } @GetMapping("/sourceB") public String sourceB(){ return "你正在访问sourceB资源"; } }
很简单的俩个接口,没添加拦截器之前成功访问
实现 spring 的 HandlerInterceptor 类先实现拦截器,但不拦截,只是简单打印日志,如下:
public class SourceAccessInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入拦截器了"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
实现spring类 WebMvcConfigurer,创建配置类把拦截器添加到拦截器链中
@Configuration public class InterceptorTrainConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**"); } }
拦截成功如下
在 sourceB 方法上添加我们的登录注解 @LoginRequired
@RestController public class IndexController { @GetMapping("/sourceA") public String sourceA(){ return "你正在访问sourceA资源"; } @LoginRequired @GetMapping("/sourceB") public String sourceB(){ return "你正在访问sourceB资源"; } }
简单实现登录拦截逻辑
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入拦截器了"); // 反射获取方法上的LoginRequred注解 HandlerMethod handlerMethod = (HandlerMethod)handler; LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class); if(loginRequired == null){ return true; } // 有LoginRequired注解说明需要登录,提示用户登录 response.setContentType("application/json; charset=utf-8"); response.getWriter().print("你访问的资源需要登录"); return false; }
运行成功,访问sourceB时需要登录了,访问sourceA则不用登录。
场景二:自定义注解+AOP 实现日志打印
先导入切面需要的依赖包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
定义一个注解@MyLog
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyLog { }
定义一个切面类,见如下代码注释理解:
@Aspect // 1.表明这是一个切面类 @Component public class MyLogAspect { // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名 // 切面最主要的就是切点,所有的故事都围绕切点发生 // logPointCut()代表切点名称 @Pointcut("@annotation(com.javapub.blog.MyLog)") public void logPointCut(){}; // 3. 环绕通知 @Around("logPointCut()") public void logAround(ProceedingJoinPoint joinPoint){ // 获取方法名称 String methodName = joinPoint.getSignature().getName(); // 获取入参 Object[] param = joinPoint.getArgs(); StringBuilder sb = new StringBuilder(); for(Object o : param){ sb.append(o + "; "); } System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString()); // 继续执行方法 try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println(methodName + "方法执行结束"); } }
在步骤二中的IndexController写一个sourceC进行测试,加上我们的自定义注解:
@MyLog @GetMapping("/sourceC/{source_name}") public String sourceC(@PathVariable("source_name") String sourceName){ return "你正在访问sourceC资源"; }
启动springboot web项目,输入访问地址