Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot 通过自定义注解实现AOP切面编程实例

SpringBoot 通过自定义注解实现AOP切面编程实例

作者头像
翎野君
发布于 2023-05-12 12:03:04
发布于 2023-05-12 12:03:04
1.2K00
代码可运行
举报
文章被收录于专栏:翎野君翎野君
运行总次数:0
代码可运行

一直心心念的想写一篇关于AOP切面实例的博文,拖更了许久之后,今天终于着手下笔将其完成。

基础概念

1、切面(Aspect)

首先要理解‘切’字,需要把对象想象成一个立方体,传统的面向对象变成思维,类定义完成之后(封装)。每次实例化一个对象,对类定义中的成员变量赋值,就相当于对这个立方体进行了一个定义,定义完成之后,那个对象就在那里,不卑不亢,不悲不喜,等着被使用,等着被回收。

面向切面编程则是指,对于一个我们已经封装好的类,我们可以在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的方法里面添加(织入)一些新的代码,对原有的方法代码进行一次增强处理。而那些增强部分的代码,就被称之为切面,如下面代码实例中的通用日志处理代码,常见的还有事务处理、权限认证等等。

2、切入点(PointCut)

要对哪些类中的哪些方法进行增强,进行切割,指的是被增强的方法。即要切哪些东西。

3、连接点(JoinPoint)

我们知道了要切哪些方法后,剩下的就是什么时候切,在原方法的哪一个执行阶段加入增加代码,这个就是连接点。如方法调用前,方法调用后,发生异常时等等。

4、通知(Advice)

通知被织入方法,改如何被增强。定义切面的具体实现。那么这里面就涉及到一个问题,空间(切哪里)和时间(什么时候切,在何时加入增加代码),空间我们已经知道了就是切入点中定义的方法,而什么时候切,则是连接点的概念,如下面实例中,通用日志处理(切面),@Pointcut规则中指明的方法即为切入点,@Before、@After是连接点,而下面的代码就是对应通知。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Before("cutMethod()")
public void begin() {
    System.out.println("==@Before== lingyejun blog logger : begin");
}

5、目标对象(Target Object)

被一个或多个切面所通知的对象,即为目标对象。

6、AOP代理对象(AOP Proxy Object)

AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。

7、织入(Weaving)

将切面切入到目标方法之中,使目标方法得到增强的过程被称之为织入。

实例代码

相关依赖包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependencies>
        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.6</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>1.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.2.8.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

定义和实现日志切面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lingyejun.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Author: Lingye
 * @Date: 2018/11/11
 * @Describe:
 * 定义日志切面
 * @Lazy 注解:容器一般都会在启动的时候实例化所有单实例 bean,如果我们想要 Spring 在启动的时候延迟加载 bean,需要用到这个注解
 * value为true、false 默认为true,即延迟加载,@Lazy(false)表示对象会在初始化的时候创建
 *
 * @Modified By:
 */
@Aspect
@Component
@Lazy(false)
public class LoggerAspect {

    /**
     * 定义切入点:对要拦截的方法进行定义与限制,如包、类
     *
     * 1、execution(public * *(..)) 任意的公共方法
     * 2、execution(* set*(..)) 以set开头的所有的方法
     * 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.lingyejun.annotation.Lingyejun)")
    private void cutMethod() {

    }

    /**
     * 前置通知:在目标方法执行前调用
     */
    @Before("cutMethod()")
    public void begin() {
        System.out.println("==@Before== lingyejun blog logger : begin");
    }

    /**
     * 后置通知:在目标方法执行后调用,若目标方法出现异常,则不执行
     */
    @AfterReturning("cutMethod()")
    public void afterReturning() {
        System.out.println("==@AfterReturning== lingyejun blog logger : after returning");
    }

    /**
     * 后置/最终通知:无论目标方法在执行过程中出现一场都会在它之后调用
     */
    @After("cutMethod()")
    public void after() {
        System.out.println("==@After== lingyejun blog logger : finally returning");
    }

    /**
     * 异常通知:目标方法抛出异常时执行
     */
    @AfterThrowing("cutMethod()")
    public void afterThrowing() {
        System.out.println("==@AfterThrowing== lingyejun blog logger : after throwing");
    }

    /**
     * 环绕通知:灵活自由的在目标方法中切入代码
     */
    @Around("cutMethod()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 获取方法传入参数
        Object[] params = joinPoint.getArgs();
        Lingyejun lingyejun = getDeclaredAnnotation(joinPoint);
        System.out.println("==@Around== lingyejun blog logger --》 method name " + methodName + " args " + params[0]);
        // 执行源方法
        joinPoint.proceed();
        // 模拟进行验证
        if (params != null && params.length > 0 && params[0].equals("Blog Home")) {
            System.out.println("==@Around== lingyejun blog logger --》 " + lingyejun.module() + " auth success");
        } else {
            System.out.println("==@Around== lingyejun blog logger --》 " + lingyejun.module() + " auth failed");
        }
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    public Lingyejun getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        Lingyejun annotation = objMethod.getDeclaredAnnotation(Lingyejun.class);
        // 返回
        return annotation;
    }
}

自定义一个注解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lingyejun.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: Lingye
 * @Date: 2018/11/11
 * @Describe: 
 * @Modified By:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lingyejun {

    /**
     * 何种场景下的通用日志打印
     *
     * @return
     */
    String module();
}  

调用切面类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lingyejun.annotation;

import org.springframework.stereotype.Component;

/**
 * @Author: Lingye
 * @Date: 2018/11/11
 * @Describe:
 * @Modified By:
 */
@Component
public class LoggerApply {

    @Lingyejun(module = "http://www.cnblogs.com/lingyejun/")
    public void lingLogger(String event) throws Exception {
        System.out.println("lingLogger(String event) : lingyejun will auth by blog address");
        throw new Exception();
    }
}  

测试代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lingyejun.annotation;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class AnnotationTest {

    @Autowired
    private LoggerApply loggerApply;

    @Test
    public void testAnnotationLogger() {
        try {
            loggerApply.lingLogger("Blog Home");
        } catch (Exception e) {
            System.out.println("a exception be there");
        }
    }
}

效果展示

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring之AOP切面编程
冬天vs不冷
2025/01/21
1080
Spring之AOP切面编程
Spring AOP:面向切面编程的利器
总之,Spring AOP是一种非常强大的编程技术,它可以帮助我们实现代码的解耦和复用,提高代码的可维护性和可扩展性。
小小程序员
2023/03/19
3880
Spring AOP:面向切面编程的利器
Spring-AOP之aspectj注解方式
一、简介 1、AOP用在哪些方面:AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制,异常处理等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。 2、AOP中的概念: Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象. joinpoint(连接点):所谓连接点是指那些被拦截到的点(可以是方法、属性、或者类的初始化时机(可以是A
java达人
2018/01/31
1.3K0
SpringBoot使用AOP
AOP可能对于广大开发者耳熟能详,它是Aspect Oriented Programming的缩写,翻译成中文就是:面向切面编程。这个可能是面试中经常提到的问题,同时它也是Spring框架中一个重大的特性,AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果,对于我们开发中最常见的可能就是日志记录,事务处理,异常处理等等。
dalaoyang
2018/09/18
7560
SpringBoot使用AOP
SpringBoot的AOP实战
切入点表达式格式:execution(访问权限 方法返回值 方法声明(参数) 异常类型)
十玖八柒
2022/08/01
4430
SpringBoot的AOP实战
AOP切面编程
如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。
用户3467126
2019/08/12
6360
【Spring】AOP底层原理(动态代理)-》 AOP概念及术语 -》 AOP实现
AOP —— 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
.29.
2023/10/17
3800
【Spring】AOP底层原理(动态代理)-》 AOP概念及术语 -》 AOP实现
Spring6 面向切面(AOP)
解决这两个问题,核心就是:<font color=red>解耦</font>。需要把附加功能从业务功能代码中抽取出来。
鱼找水需要时间
2023/06/14
3870
Spring6 面向切面(AOP)
SpringBoot:切面AOP实现权限校验:实例演示与注解全解
blog.csdn.net/mu_wind/article/details/102758005
JAVA葵花宝典
2020/11/13
2.6K0
SpringBoot:切面AOP实现权限校验:实例演示与注解全解
面向切面编程
面向切面编程(Aspect Oriented Programming),简称AOP。作为面向对象编程的一个强力补充,在业务系统中很少被关注,却随着Spring的出现而名声鹊起。
李鸿坤
2020/07/18
1.2K0
面向切面编程
【Spring进阶】基于注解的面向切面编程(AOP)详解
面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。
王也518
2024/04/22
1.3K0
SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
知了一笑
2019/07/19
9690
Spring:AOP 面向切面编程
上面的代码事务在 Dao 层,转出转入操作都是一个独立的事务,但实际开发,应该把业务逻辑控制在一个事务中,所以应该将事务挪到 Service 层。
RendaZhang
2020/09/16
1.2K0
Spring:AOP 面向切面编程
spring aop实例讲解_摘要实例
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。 1、导入aop模块:Spring AOP:(spring-aspects) 2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候讲日志进行打印(方法之前、方法运行结束、方法出现异常等) 3、定义一个日志切面类(LOgAspects);切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行对应的切面方法; 通知方法: 前置通知(@Before):logStart:在目标方法div()运行之前运行 后置通知(@After):logEnd:在目标方法div()运行结束之后运行 返回通知(@AfterReturning):logReturn:在目标方法div()正常返回之后运行 异常通知(@AfterThrowing):logException:在目标方法div()出现异常之后运行 环绕通知:动态代理,手动推进目标方法运行(joinPoint.procced()) 4、给切面类的目标方法标注何时何地运行(通知注解) 5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中; 6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect) 7※给配置类中加@EnableAspectJAutoProxy 开启基于注解的AOP模式 在Spring中很多的@EnableXXX都是表示要开启XXX功能
全栈程序员站长
2022/09/22
1K0
SpringBoot系列之使用自定义注解校验用户是否登录
记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了。
一个程序员的成长
2020/11/25
1.1K0
SpringBoot系列之使用自定义注解校验用户是否登录
Spring boot中使用aop详解
aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。
天涯泪小武
2019/01/17
1.4K0
揭秘AOP:切面编程的综合指南
Spring的AOP(面向切面编程)是一种编程范式,它允许开发人员将横切关注点(cross-cutting concerns)从应用程序的主要业务逻辑中分离出来,以便更好地实现代码重用和模块化。横切关注点指的是那些存在于应用程序多个模块中的功能,如日志记录、事务管理、安全性等,它们不属于单个类或对象,而是跨越多个组件的功能。
堆栈哲学
2024/03/23
6020
揭秘AOP:切面编程的综合指南
Spring AOP使用
AOP全程是Aspect—Oriented—Programming,面向切面编程。AOP采用横向抽取机制,将分散在各个方法中的重复代码抽取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。
ha_lydms
2023/08/09
1820
Spring AOP使用
轻松上手Spring AOP,掌握切面编程的核心技巧
Spring框架是我们使用比较多的一个框架,而AOP又是Spring的核心特性之一,本篇文章将介绍一下AOP的切点表达式、通知等特性及如何使用Spring AOP。
索码理
2024/04/15
3240
轻松上手Spring AOP,掌握切面编程的核心技巧
Spring学习总结(三)——Spring实现AOP的多种方式
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术。AOP是OOP的补充,是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高。动态织入则不需要改变目标模块。Spring框架实现了AOP,使用注解配置完成AOP比使用XML配置要更加方便与直观。上一篇随笔中已经详细讲了代理模式。
张果
2022/05/09
4630
Spring学习总结(三)——Spring实现AOP的多种方式
相关推荐
Spring之AOP切面编程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验