JAVA学习实战(一)登录拦截器实现以及注解LogRequired实现(内含拦截器 HandlerInterceptor 的详细讲解以及注解的具体实现方法)

前几天在学习一个网站项目的时候看到了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渲染了视图之后进行执行,我们可以看到他的主要作用是请空登录信息

以上就是我想要讲述的全部内容,感谢支持,喜欢的小伙伴可以关注一波(逃

你可能感兴趣的:(JAVA学习实战,java,spring,restful)