searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Spring AOP详解

2024-04-26 01:07:23
6
0
一、AOP

AOP(Aspect Oriented Programing),即面向切面编程,可以说是OOP(面向对象编程)的补充和完善。OOP引入封装、继承、多台等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP不适合定义横向的关系,比如日志功能。日志代码往往横向地散步在所有对象层次中,而与它对应的对象的核心功能毫无关系。这种散步在各处的无关的代码被称为横切,在OOP设计中,它导致大量代码的重复,不利于各个模块重用。

AOP技术恰恰相反,它利用一种称为横切的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。

二、AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切切点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态的添加一些方法或字段

三、Spring对AOP的支持

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其他bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

1、默认使用JAVA动态代理来创建AOP代理,这样就可以为任何借口实例创建代理了

2、当需要代理的类不是代理接口时,Spring会切换为使用GCLIB代理,也可以强制使用CGLIB

AOP编程的步骤:

1、定义普通业务类

2、定义切入点,一个切入点可能横切多个业务类

3、定义增强处理

四、Spring boot中AOP的使用

1、引入依赖

 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
 

2、创建切面类,可以通过PointCut定义切入点,也可以直接在通知上设置切入点

 
@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);
    /**
     * 定义入切点
     */
    @Pointcut("execution(* cn.draven.aop.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint jp){
        this.logger.info("前置通知.");
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        this.logger.info("后置通知.");
    }

    @Around("execution(* cn.draven.aop.service.*.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        this.logger.info("环绕前...");
        proceedingJoinPoint.proceed();
        this.logger.info("环绕后...");
    }
}
 

3、编写业务类,上面的切入点定义包名为cn.draven.aop.service.*的所有方法都进行拦截

4、进行单元测试

 
 @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class AopTestCase {
    @Resource
    private ILogService iLogService;
    @Test
    public void test(){
        this.iLogService.logTest();
    }
}
 
 

 

五、切点表达式

1、分类

  • 匹配方法 execution
  • 匹配注解 @within、@target、@args、@annotation
  • 匹配包/类型 within()
  • 匹配对象 target、this
  • 匹配参数 args
类型 描述
execution() 用于匹配方法执行的连接点
within() 用于匹配指定的类及其子类中的所有方法
this() 匹配可以向上转型为this指定的类型的代理对象中的所有方法
target() 匹配可以向上转型为target指定的类型的目标对象中的所有方法
args() 用于匹配运行时传入的参数列表的类型为指定的参数列表类型的方法
@within() 用于匹配持有指定注解的类的所有方法
@target() 用于匹配的持有指定注解目标对象的所有方法
@args() 用于匹配运行时传入的参数列表的类型持有注解列表对应的注解的方法
@annotation() 用于匹配持有指定注解的方法
bean bean(Bean的id或名字通配符)匹配特定名称的Bean对象

2、三种通配符

通配符 说明
* 匹配任何数量字符
.. 匹配任何数量字符的重复,如匹配任何数量子包,在方法匹配中匹配任何数量参数
+ 匹配指定类型的子类型;仅能作为后缀放在类型模式后

3、操作符

操作符 说明
&&或者and 与操作符
`或者or 或操作符
!或者not 非操作符

4、使用实例

  • execution
execution([方法可见性] 返回类型 [方法所在类的全路径名] 方法名(参数类型列表) [方法抛出的异常类型])
例如、匹配所有目标类的public方法
execution(public * * (..))
  • within
within(方法所在类的全路径名)
例如、匹配com.spring.service包及子包下的所有类
within(com.spring.service..*)
  • args
args(参数类型列表)
例如、第一个参数为java.lang.String,最后一个参数为java.lang.Integer
args(java.lang.String,..,java.lang.Integer)
  • this和target
this(方法所在类的全路径名)target(方法所在类的全路径名)
例如、匹配名为MyObject的代理对象
this(com.draven.MyObject)
例如、匹配名为MyObject的目标对象
target(com.draven.MyObject)
  • @within
@within(注解的全路径名)
例如、匹配使用com.spring.annotation.BusinessAspect注解标注的类
@Within(com.spring.annotation.BusinessAspect)
  • @annotation
@annotation(注解的全路径名)
例如、匹配使用com.spring.annotation.Body注解标注的方法
@annotation(com.spring.annotation.Body)
  • @args
@args(注解的全路径名)
例如、匹配使用com.spring.annotation.Body注解的类作为参数的方法
@args(com.spring.annotation.Body)
public void test(Apple apple){}
@Body
public class Apple{}
0条评论
0 / 1000
x****n
7文章数
0粉丝数
x****n
7 文章 | 0 粉丝
x****n
7文章数
0粉丝数
x****n
7 文章 | 0 粉丝
原创

Spring AOP详解

2024-04-26 01:07:23
6
0
一、AOP

AOP(Aspect Oriented Programing),即面向切面编程,可以说是OOP(面向对象编程)的补充和完善。OOP引入封装、继承、多台等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP不适合定义横向的关系,比如日志功能。日志代码往往横向地散步在所有对象层次中,而与它对应的对象的核心功能毫无关系。这种散步在各处的无关的代码被称为横切,在OOP设计中,它导致大量代码的重复,不利于各个模块重用。

AOP技术恰恰相反,它利用一种称为横切的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。

二、AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切切点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态的添加一些方法或字段

三、Spring对AOP的支持

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其他bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

1、默认使用JAVA动态代理来创建AOP代理,这样就可以为任何借口实例创建代理了

2、当需要代理的类不是代理接口时,Spring会切换为使用GCLIB代理,也可以强制使用CGLIB

AOP编程的步骤:

1、定义普通业务类

2、定义切入点,一个切入点可能横切多个业务类

3、定义增强处理

四、Spring boot中AOP的使用

1、引入依赖

 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
 

2、创建切面类,可以通过PointCut定义切入点,也可以直接在通知上设置切入点

 
@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);
    /**
     * 定义入切点
     */
    @Pointcut("execution(* cn.draven.aop.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint jp){
        this.logger.info("前置通知.");
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        this.logger.info("后置通知.");
    }

    @Around("execution(* cn.draven.aop.service.*.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        this.logger.info("环绕前...");
        proceedingJoinPoint.proceed();
        this.logger.info("环绕后...");
    }
}
 

3、编写业务类,上面的切入点定义包名为cn.draven.aop.service.*的所有方法都进行拦截

4、进行单元测试

 
 @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class AopTestCase {
    @Resource
    private ILogService iLogService;
    @Test
    public void test(){
        this.iLogService.logTest();
    }
}
 
 

 

五、切点表达式

1、分类

  • 匹配方法 execution
  • 匹配注解 @within、@target、@args、@annotation
  • 匹配包/类型 within()
  • 匹配对象 target、this
  • 匹配参数 args
类型 描述
execution() 用于匹配方法执行的连接点
within() 用于匹配指定的类及其子类中的所有方法
this() 匹配可以向上转型为this指定的类型的代理对象中的所有方法
target() 匹配可以向上转型为target指定的类型的目标对象中的所有方法
args() 用于匹配运行时传入的参数列表的类型为指定的参数列表类型的方法
@within() 用于匹配持有指定注解的类的所有方法
@target() 用于匹配的持有指定注解目标对象的所有方法
@args() 用于匹配运行时传入的参数列表的类型持有注解列表对应的注解的方法
@annotation() 用于匹配持有指定注解的方法
bean bean(Bean的id或名字通配符)匹配特定名称的Bean对象

2、三种通配符

通配符 说明
* 匹配任何数量字符
.. 匹配任何数量字符的重复,如匹配任何数量子包,在方法匹配中匹配任何数量参数
+ 匹配指定类型的子类型;仅能作为后缀放在类型模式后

3、操作符

操作符 说明
&&或者and 与操作符
`或者or 或操作符
!或者not 非操作符

4、使用实例

  • execution
execution([方法可见性] 返回类型 [方法所在类的全路径名] 方法名(参数类型列表) [方法抛出的异常类型])
例如、匹配所有目标类的public方法
execution(public * * (..))
  • within
within(方法所在类的全路径名)
例如、匹配com.spring.service包及子包下的所有类
within(com.spring.service..*)
  • args
args(参数类型列表)
例如、第一个参数为java.lang.String,最后一个参数为java.lang.Integer
args(java.lang.String,..,java.lang.Integer)
  • this和target
this(方法所在类的全路径名)target(方法所在类的全路径名)
例如、匹配名为MyObject的代理对象
this(com.draven.MyObject)
例如、匹配名为MyObject的目标对象
target(com.draven.MyObject)
  • @within
@within(注解的全路径名)
例如、匹配使用com.spring.annotation.BusinessAspect注解标注的类
@Within(com.spring.annotation.BusinessAspect)
  • @annotation
@annotation(注解的全路径名)
例如、匹配使用com.spring.annotation.Body注解标注的方法
@annotation(com.spring.annotation.Body)
  • @args
@args(注解的全路径名)
例如、匹配使用com.spring.annotation.Body注解的类作为参数的方法
@args(com.spring.annotation.Body)
public void test(Apple apple){}
@Body
public class Apple{}
文章来自个人专栏
Spring
5 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0