前几天在学习一个网站项目的时候看到了Java的注解这个东西的实现感觉挺有意思的,想结合项目进行一个简单的讲解,希望能够帮助自己复习巩固一遍,同时也能够帮助更多学习过的人
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
如图,上面是一个注解的实现,其实实现方法很简单,只需要加上@interface即可
@interface用来定义一个自定义注解
在Java中,定义注解其实和定义接口很相似,
如上图所示,即定义了一个注解LogRequired
,那么如果我们想要给注解中传入某些值其实也很简单,只需要如下定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
String val1;
String val2;
}
这样如果要使用时,只需要@LogRequired(val1=“111”,val2=“222”)即可
当我们定义一个注解的时候,需要了解到这个注解的生命周期以及需要用到哪些地方,这就用到了注解的注解(笑),元注解,其中@Retention用来确定这个注解的生命周期,@Target指定注解的使用目标范围
@Target : Target翻译中文为目标,即该注解可以声明在哪些目标元素之前,也可理解为注释类型的程序元素的种类。
ElementType.PACKAGE:该注解只能声明在一个包名前。
ElementType.ANNOTATION_TYPE:该注解只能声明在一个注解类型前。
ElementType.TYPE:该注解只能声明在一个类前。
ElementType.CONSTRUCTOR:该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:该注解只能声明在一个局部变量前。
ElementType.METHOD:该注解只能声明在一个类的方法前。
ElementType.PARAMETER:该注解只能声明在一个方法参数前。
ElementType.FIELD:该注解只能声明在一个类的字段前。
@Retention :Retention 翻译成中文为保留,可以理解为如何保留,即告诉编译程序如何处理,也可理解为注解类的生命周期。
RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS : 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME : 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
那么通过学习以上知识,相信各位已经了解了注解的具体使用方法,下面我们来看如何使用该注解去执行拦截的工作的
还记得我们之前说过的拦截器吗
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
preHandle是在Controller处理之前进行调用,可以存在多个拦截器,因为MVC中的拦截器是链式调用的,会根据声明的先后顺序进行执行,但是链式调用时可以中断的,因此如果我们想要执行一个登录拦截,我们只需要判断一下是否符合登录条件,如果不符合的话直接return false即可拦截掉后续的操作
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
//如果添加了logrequired注解并且未登录
if (loginRequired != null && hostHolder.getUser() == null) {
response.sendRedirect(request.getContextPath() + "/login");
//还记得返回false意味着什么吗,终止调用链
return false;
}
}
return true;
}
}
关于拦截器的使用我也是第一次学习,中间也对于三个参数较为疑惑,查询后大概明白了具体的用法:
request : 是指经过spring封装的请求对象, 包含请求地址, 头, 参数, body(流)等信息.
response:是指经过spring封装的响应对象, 包含输入流, 响应body类型等信息.
handler,是指controller的@Controller注解下的"完整"方法名, 是包含exception等字段信息的.
如果还不明白的话我觉得可以看这两篇帖子:
SpringMVC里拦截器preHandle里的参数究竟是什么意思
以及
HttpServletRequest接口详解
anyway,虽然这篇文章用不到,还是简单介绍下HandlerInterceptor的另外两个方法:
postHandle:这个方法只会在preHandle返回为true的情况下才会执行,他是在处理器进行处理之后执行,也就是在Controller之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说可以对MoedlAndView进行操作,可以往里加一些东西,为了便于理解,我也找到了一个用到了postHandel的方法
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从cookie中获取凭证
String ticket = CookieUtil.getValue(request, "ticket");
if (ticket != null) {
// 查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
// 检查凭证是否有效
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
// 根据凭证查询用户
User user = userService.findUserById(loginTicket.getUserId());
// 在本次请求中持有用户
hostHolder.setUser(user);
// 构建用户认证的结果,并存入SecurityContext,以便于Security进行授权.
Authentication authentication = new UsernamePasswordAuthenticationToken(
user, user.getPassword(), userService.getAuthorities(user.getId()));
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if (user != null && modelAndView != null) {
modelAndView.addObject("loginUser", user);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
SecurityContextHolder.clearContext();
}
}
可以看到这里的postHandle可以往modelAndView中添加信息
当然afterCompletion其实也是类似的,他需要执行preHandle为true才执行,但是他是在DispatcherServlet渲染了视图之后进行执行,我们可以看到他的主要作用是请空登录信息
以上就是我想要讲述的全部内容,感谢支持,喜欢的小伙伴可以关注一波(逃