Spring-AOP-java应用

通过xml的aspect实现

导入AOP的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.7.4</version>
</dependency>

————————————————
版权声明:本文为CSDN博主「David宫洪深」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36020545/article/details/53000896

xml中导入命名空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/aop 
                           https://www.springframework.org/schema/aop/spring-aop.xsd">

编写aop配置

切入点表达式入门简介:
例如:

execution(public void com.test.aop.aspectTest.service.AopTestService.add(int))

1.execution :以此法编写的切入点表达式,将使用方法定位的模式匹配连接点。
用 execution 写出来的表达式,都是直接声明到类中的方法的。
2.public :限定只切入 public 类型的方法。
3.void :限定只切入返回值类型为 void 的方法。
4.com.test.aop.aspectTest.service.AopTestService :限定只切入 AopTestService 这个类的方法。
5.add :限定只切入方法名为 add 的方法。
6.(int) :限定只切入方法的参数列表为一个参数,且类型为 int的方法。

通过<aop:config>来在配置AOP

<bean id="loggerAdvice" class="com.test.aop.aspectTest.component.Logger"/>

<aop:config>
    <aop:aspect id="loggerAspect" ref="loggerAdvice">
        <aop:before method="beforePrint"
                    pointcut="execution(public void com.test.aop.aspectTest.service.AopTestService.add(int)"/>
    </aop:aspect>
</aop:config>

<aop:aspect>定义切面。
<aop:before>定义通知方法类型。before是前置通知,在目标方法执行前调用增强逻辑。这个标签中的method为增强逻辑的方法名,pointcut为切入点表达式(定义哪些方法逻辑需要增强)。

基于注解的AOP配置

1.对于自定义的切面类(aspect)

切面类标注注解`@Aspect`,`@Component`。

2.在切面类中。可以在方法上标注对应的通知注解。

@Before:前置通知
@After:后置通知
@AfterReturning:返回通知
@AfterThrowing:异常通知
@Around:环绕通知

3.通知注解中可以加切入点表达式

例如:

 @Before("execution(public * com.test.aop.aspectTest.service.testService.*(..))")
 public void beforePrint() {
        System.out.println("Logger beforePrint run ......");
    }

4.配置类中引入@EnableAspectJAutoProxy

注解含义:开启AOP。

@Configuration
@ComponentScan("com.test.aop.aspectText")
@EnableAspectJAutoProxy
public class AspectJAOPConfiguration {
    
}

注:在xml配置文件中配置<aop:aspectj-autoproxy/>,效果一样。
注意点:同一个切面类中,环绕通知的执行时机比单个通知要早

5.@Pointcut注解

切入点表达式注解。
可以在切面类(aspect)中,定义一个空方法。标注注解@Pointcut。里面配置通用的切入点表达式。
在其他通知注解上,传入这个空方法即可。
例如:

@Pointcut("execution(* com.test.aop.aspectTest.service.*.*(String)))")
public void defaultPointcut() {

}
@Before("defaultPointcut()")
public void afterPrint() {
    System.out.println("Logger afterPrint run ......");
}

在xml配置文件,如何抽取通用的切入点表达式?

<aop:config>
<aop:aspect id = "apsectId" ref = "切面类id">
    <aop:pointcut id="pointcutId" expression= "切入点表达式" >
    <aop:before method = "beforeTest"
       pointcut-ref="pointcutId"/>
       <!--定义其他的通知 -->
</aop:aspect>    
</aop:config>

6.@annotation的使用

除了 execution 之外,还有一种切入点表达式也比较常用:@annotation()
它的使用方式就非常简单了,只需要在括号中声明注解的全限定名即可。
1.声明一个注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
    
}

2.切入点表达式中添加声明
@annotation(com.test.aop.aspectTest.component.Log)

// 指定注解
    @Pointcut("@annotation(com.test.aop.aspectTest.component.Log)")
    public void withAnnotation() {}

含义:会搜索整个 IOC 容器中标注了 @Log 注解的所有 bean 全部增强

通知方法中的特殊参数

JoinPoint
JoinPoint.getTarget可以返回目标对象。
JoinPoint.getThis可以返回代理对象。
JoinPoint.getArgs可以获取被拦截的方法的参数列表。
JoinPoint.getSignature()可以获取被拦截的签名。

@Before("execution(public * com..TestService.add(..))")
public void beforePrint(JoinPoint joinPoint) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    //获取方法名称
    Method method = signature.getMethod();
    System.out.println(method.getName());
    System.out.println("Logger beforePrint run ......");
}

ProceedingJoinPoint
ProceedingJoinPoint是对JoinPoint的扩展,扩展了proceed方法。

ProceedingJoinPoint 有一个 proceed 方法,执行了它,就相当于之前咱在动态代理中写的 method.invoke(target, args); 方法了。

返回通知和异常通知的特殊参数
通知如果要获取返回结果或者异常。
1.首先在方法的参数列表中声明一个 result 或者 e :

@AfterReturning("execution(* com..TestService.subtract(double))")
public void afterReturningPrint(Object retval) {
    System.out.println("Logger afterReturningPrint run ......");
    System.out.println("返回的数据:" + retval);
}

@AfterThrowing("defaultPointcut()")
public void afterThrowingPrint(Exception e) {
    System.out.println("Logger afterThrowingPrint run ......");
}

2.通知注解中增加对应的属性。
返回通知增加属性returning:

@AfterReturning(value = "execution(* com..TestService.subtract(double))", returning = "retval")
public void afterReturningPrint(Object retval) {
    System.out.println("Logger afterReturningPrint run ......");
    System.out.println("返回的数据:" + retval);
}

异常通知增加属性throwiing:

@AfterThrowing(value = "defaultPointcut()", throwing = "e")
public void afterThrowingPrint(Exception e) {
    System.out.println("Logger afterThrowingPrint run ......");
    System.out.println("抛出的异常:" + e.getMessage());
}

AOP联盟指定的通知类型

在没有整合aspectj之前,spring框架有一套原生的实现。
AOP联盟就制定了规范,总结出了5中通知类型。

通知类型:
1.前置通知
2.返回通知
3.异常通知
4.环绕通知
5.引介通知

与AspectJ的区别:多了一个引介通知,少了一个后置通知。

spring框架中的通知接口如下

前置通知:org.springframework.aop.MethodBeforeAdvice
返回通知:org.springframework.aop.AfterReturningAdvice
异常通知:org.springframework.aop.ThrowsAdvice
环绕通知:org.aopalliance.intercept.MethodInterceptor
引介通知:org.springframework.aop.IntroductionAdvisor

注意:环绕通知的接口是 AOP 联盟原生定义的接口(不是 cglib 的那个 MethodInterceptor )。

多个切面执行顺序

默认的切面执行顺序,是根据切面类的unicode编码,按照十六进制排序得来的。按照字母表的顺序来的。

也可以声明执行顺序通过实现Ordered接口。实现Ordered接口的默认排序是Integer的最大值。设置值越小,先执行。

也可以通过注解@Order,注解属性中填写值,越小对应的切面类越先执行。

那么,同一个切面类中的多个相同通知的执行顺序呢?
  答:都是根据unicode编码顺序(字典表顺序)来的。
注意:同一切面类中相同通知**不能通过@Order注解排序**。

代理对象调用自身方法

有一种场景:
我们产生的代理对象,调用拦截的目标对象方法中,调用了另外一个目标对象的方法。这种情况下,代理个性化逻辑只会执行一次。(调用了目标对象的两个方法,自定义代理逻辑执行性一次)

解决方案:AopContext
1.在配置类中开启AOP注解中开启exposeproxy属性

2.如果代理的方法中调用目标对象的另外一个方法,都需要执行代理逻辑,使用AopContext.currentProxy() 方法就取到代理对象的 this,然后再调用另外一个方法。

@Configuration
@ComponentScan("com.test.aop.testAop")
@EnableAspectJAutoProxy(exposeProxy = true) // 暴露代理对象
public class AopContextConfiguration {
    
}
@Service
public class TestTarget{
    public void update(String id,String name) {
           //这里调用另外一个get方法
           get(id);
           //进行修改操作
    }
    public String get(String id) {
       //返回查询结果
       return "";
   }
}
@Aspect
@Component
public class AspectTest{
    @Before("execution(* com.test.aop.testAop.service.TestTarget.*(..))")
    public void beforePrint() {
        System.out.println("LogAspect 前置通知 ......");
    }
}

修改后的Service类:

@Service
public class TestTarget{
    public void update(String id,String name) {
           //这里调用另外一个get方法
           //get(id);
           ((TestTarget)AopContext.currentProxy()).get(id);
           //进行修改操作
    }
    public String get(String id) {
       //返回查询结果
       return "";
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值