zoukankan      html  css  js  c++  java
  • Spring自定义AOP注解

    本文以验证用户是否登录为例,主要介绍如何自定义一个AOP注解并解析

    功能:用户如果已登录,在http请求头中会包含Authorization头,这个字段内存着用户登录后服务端分配的token,服务端会对Controller中被@CheckLogin修饰的接口进行登录验证

    定义注解

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface CheckLogin {
    }
    

    @Target({ElementType.METHOD}):标识此注解用于方法上
    @Retention(RetentionPolicy.RUNTIME):标识此注解运行时有效
    @Inherited:标识如果方法所在的类被子类继承,此方法同时会继承此注解

    如果未标识@Inherited,会导致后续生成CGLIB代理时拿不到注解

    定义注解BeanPostProcessor

    1. 定义一个名为CheckLoginPostProcessor的类,继承于BeanPostProcessor
    2. 重写postProcessBeforeInitializationpostProcessAfterInitialization方法

    代码如下:

    @Component
    public class CheckLoginPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    	// 在前置初始化操作中不需要操作,直接返回
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    	// 获取bean中所有的方法
            var methods = bean.getClass().getMethods();
            CheckLogin annotation = null;
            for (var m: methods) {
    	    // 获取方法中的@CheckLogin注解,如果不存在返回null
                annotation = AnnotationUtils.getAnnotation(m, CheckLogin.class);
                if (annotation != null) break;
            }
    	// 当前bean不包含CheckLogin注解,直接返回
            if (annotation == null) {
                return bean;
            }
    	// 创建CGLIB操作对象
            var enhancer = new Enhancer();
    	// 设置代理对象继承于bean
            enhancer.setSuperclass(bean.getClass());
    	// 设置代理对象的方法回调,重新定义原方法的执行流程
            enhancer.setCallback(new CheckLoginInterceptor());
    	// 创建对象
            Object proxy = enhancer.create();
            // 因为重新创建了代理对象,会导致Spring原先@Autowired的属性丢失,需要手工重新对所有属性值重新注入
            var fields = proxy.getClass().getSuperclass().getDeclaredFields();
            for (var f: fields) {
                try {
                    var sourceField = bean.getClass().getDeclaredField(f.getName());
    		// @Autowired一般都是private,通过反射读取值需要设置访问权限
                    sourceField.setAccessible(true);
                    f.setAccessible(true);
                    f.set(proxy, sourceField.get(bean));
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
    	// 返回代理对象
            return proxy;
        }
    
        private static class CheckLoginInterceptor implements MethodInterceptor {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getAnnotation(CheckLogin.class) == null) {
                    return methodProxy.invokeSuper(o, objects);
                }
                var attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                var token = attribute.getRequest().getHeader("Authorization");
                if (token.equals("")) {
    		// 验证返回的类型,最好要和原方法相同的返回类型
                    return Result.failed("用户未登录", -25);
                }
                var loginService = (LoginService)ApplicationContextUtil.getApplicationContext().getBean("loginService");
                var isLogin = loginService.isLogin(token);
                if (isLogin) {
                    return methodProxy.invokeSuper(o, objects);
                }
                return Result.failed("用户未登录", -25);
            }
        }
    }
    
    

    使用

    在需要验证登录的接口处,加上@CheckLogin注解即可

    @PostMapping("/api/post/save")
    @CheckLogin
    public Result<String> savePost(@RequestBody PostDTO post) {
        this.service.savePost(post);
        return Result.success("save success");
    }
    
  • 相关阅读:
    让你的qstardict读单词
    IOS6 新特性之UIActivityViewController详解
    NSLayoutConstraint-代码实现自动布局的函数用法说明
    NSLayoutConstraint-代码实现自动布局的函数用法说明
    常见音频格式比较
    常见音频格式比较
    Android || IOS录制mp3语音文件方法
    Android || IOS录制mp3语音文件方法
    ios与android设备即时语音互通的录音格式预研说明
    ios与android设备即时语音互通的录音格式预研说明
  • 原文地址:https://www.cnblogs.com/kakura/p/13626146.html
Copyright © 2011-2022 走看看