专栏首页bingfeng-技术SpringBoot系列之使用自定义注解校验用户是否登录

SpringBoot系列之使用自定义注解校验用户是否登录

阅读本文大概需要13分钟

记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了。

1、到底是什么是AOP?

所谓AOP也就是面向切面编程,能够让我们在不影响原有业务功能的前提下,横切扩展新的功能。这里面有一个比较显眼的词我们需要注意一下,横切,它是基于横切面对程序进行扩展的。

2、AOP相关术语

在Spring的AOP中有很多的术语,而且容易混淆,大家一定要先搞清楚这几个概念:

  • 连接点(Joinpoint):在程序执行过程中某个特定的点,比如类初始化前、类初始化后,方法调用前,方法调用后;
  • 切点(Pointcut):所谓切点就是你所切取的类中的方法,比如你横切的这个类中有两个方法,那么这两个方法都是连接点,对这两个方法的定位就称之为切点;
  • 增强(Advice):增强是织入到连接点上的一段程序,另外它还拥有连接点的相关信息;
  • 目标对象(Target):增强逻辑的织入目标类,就是我的增强逻辑植入到什么位置;
  • 引介(Introduction):一种特殊的增强,它可以为类添加一些属性喝方法;
  • 织入(Weaving):织入就是讲增强逻辑添加到目标对象的过程;
  • 代理(Proxy):一个类被AOP织入增强后,就会产生一个结果类,他是融合了原类和增强逻辑的代理类;
  • 切面(Aspect):切面由切点和增强组成,他是横切逻辑定义和连接点定义的组成;

3、AOP功能实践

我们这里主要是学习SpringBoot中的一些功能,所以我们这里用的是SpringBoot工程,版本也是最新的2.0.5版本。

创建SpringBoot工程就不说了,我们直接引入Maven的依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <!-- 引入AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                </executions>
            </plugin>
        </plugins>
    </build>

首先我们来创建一个Controller类:

@RestController
public class LoginController {

    @GetMapping(value = "/username")
    public String getLoginUserName(String userName, Integer age) {
        
        return userName + " --- " + age;
    }
}

创建切面:

@Aspect
@Component
public class LogAspect {

    /**
     * 功能描述: 拦截对这个包下所有方法的访问
     *
     * @param:[]
     * @return:void
     **/
    @Pointcut("execution(* com.example.springbootaop.controller.*.*(..))")
    public void loginLog() {
    }

    // 前置通知
    @Before("loginLog()")
    public void loginBefore(JoinPoint joinPoint) {

        // 我们从请求的上下文中获取request,记录请求的内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        HttpServletRequest request = attributes.getRequest();

        System.out.println("请求路径 : " + request.getRequestURL());
        System.out.println("请求方式 : " + request.getMethod());
        System.out.println("方法名 : " + joinPoint.getSignature().getName());
        System.out.println("类路径 : " + joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("参数 : " + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "object", pointcut = "loginLog()")
    public void doAfterReturning(Object object) {

        System.out.println("方法的返回值 : " + object);
    }

    // 方法发生异常时执行该方法
    @AfterThrowing(throwing = "e",pointcut = "loginLog()")
    public void throwsExecute(JoinPoint joinPoint, Exception e) {

        System.err.println("方法执行异常 : " + e.getMessage());
    }

    // 后置通知
    @After("loginLog()")
    public void afterInform() {

        System.out.println("后置通知结束");
    }

    // 环绕通知
    @Around("loginLog()")
    public Object surroundInform(ProceedingJoinPoint proceedingJoinPoint) {

        System.out.println("环绕通知开始...");

        try {
            Object o =  proceedingJoinPoint.proceed();
            System.out.println("方法环绕proceed,结果是 :" + o);
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}

注解概述:

  • @Apsect:将当前类标识为一个切面;
  • @Pointcut:定义切点,这里使用的是条件表达式;
  • @Before:前置增强,就是在目标方法执行之前执行;
  • @AfterReturning:后置增强,方法退出时执行;
  • @AfterThrowing:有异常时该方法执行;
  • @After:最终增强,无论什么情况都会执行;
  • @Afround:环绕增强;

测试:

异常测试:

4、定义自定义注解

应用场景:在我之前上个项目的时候,有这样一个注解,就是在访问其他接口的时候必须要登录,那么这个时候我们就定义一个注解,让它去对用户是否登录进行校验,那么基于这样的一个场景,我们来定义一个校验登录的注解。

创建一个注解:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {

    String desc() default "验证是否登录";
}

创建一个AOP切面:

@Aspect
@Component
public class LoginAspect {

    @Pointcut(value = "@annotation(com.example.springbootaop.annotation.Auth)")
    public void access() {
    }

    @Before("access()")
    public void before() {

        System.out.println("开始验证用户是否登录...");
    }

    @Around("@annotation(auth)")
    public Object around(ProceedingJoinPoint pj, Auth auth) {

        // 获取注解中的值
        System.out.println("注解中的值 : " + auth.desc());
        try {

            // 检验是否登录 true 已经登录  false 未登录
            Boolean flag = false;

            if (flag == true) {
                return "登录成功";
            } else {
                return "未登录";
            }
        } catch (Throwable throwable) {
            return null;
        }
    }
}

测试未登录:

测试登录:

这样我们就可以简单的实现了一个登录校验的注解。

通过今天的分享你会使用AOP和自定义注解了吗?我把源码的地址放在下面,有兴趣的朋友可以看看。

GitHub地址:https://github.com/liangbintao/SpringBootIntegration.git

原创不易,如果感觉不错,给个?顺便扩散一下吧!

本文分享自微信公众号 - 一个程序员的成长(xiaozaibuluo),作者:一个程序员的成长

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-17

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java重点基础:反射机制

    Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对...

    一个程序员的成长
  • 别在看不起女程序媛了,一个高颜值女程序媛的日常

    今天这篇文,意义特殊,是我的一个迷妹程序媛-祈澈姑娘写的,她发给我后,我看了通篇,感觉写的很真实,而且又是记录女程序媛的日常,比较少见,所以我很有兴趣,相信大家...

    一个程序员的成长
  • 设计模式之 - 策略落实

    策略模式:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式的变化,不会影响到使用算法的客户。

    一个程序员的成长
  • python数字图像处理-图像噪声与去噪

    概述: 椒盐噪声(salt & pepper noise)是数字图像的一个常见噪声,所谓椒盐,椒就是黑,盐就是白,椒盐噪声就是在图像上随机出现黑色白色的像素。椒...

    py3study
  • Spring - 通过注解配置Bean(8)

    特定组件包括: Component:基本注解,标识了一个受Spring管理的组件 Respository:标识持久层组件 Service: 标识服务层(业...

    桑鱼
  • C#2C++数据类型对照

    Pulsar-V
  • 第3章 System V IPC

    System V IPC: ·System V消息队列 ·System V信号量 ·System V共享内存区 ?

    _gongluck
  • WPF 开机启动因为触摸初始化锁住界面显示

    现象是设置 WPF 开机启动的时候,概率界面不显示,进程已经起来,同时占用内存极小。通过 dump 或附加调试可以看到主进程带等待触摸线程的回应

    林德熙
  • PHP中empty,is_null,isset的区别

    isset 判断变量是否已存在,如果变量存在则返回 TRUE,否则返回 FALSE。

    用户7657330
  • spring中的多线程aop方法拦截

    日常开发中,常用spring的aop机制来拦截方法,记点日志、执行结果、方法执行时间啥的,很是方便,比如下面这样:(以spring-boot项目为例)

    菩提树下的杨过

扫码关注云+社区

领取腾讯云代金券