Spring:基于xml和注解的aop

1、导入坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<!--用于解析切入点表达式的jar包-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

2、建立通知类

将方法(主要是业务逻辑层的方法)中的共性代码抽取出来放到通知类中统一管理。

3、配置通知

配置文件中添加aop的约束。先配置ioc(spring的其它功能都需要基于ioc),再配置aop。

<?xml version="1.0" encoding="UTF-8"?>
<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
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--先配置service的ioc-->
    <bean id="accountService" class="cao.zhanpeng.service.impl.AccountServiceImpl"/>

    <!--配置通知类的ioc-->
    <bean id="log" class="cao.zhanpeng.utils.Logger"/>

    <!--开始配置aop-->
    <aop:config>
        <!--配置切面,id表示该切面唯一标识符,ref用于指定通知类bean的id-->
        <aop:aspect id="logShow" ref="log">
            <!--配置通知类型,aop:before表示前置通知
            method指定通知类中哪个方法是前置通知(哪个是通知?)
            pointcut指定切入点表达式,表示对业务层的哪些方法进行通知(对谁通知?)
            -->
            <aop:before method="printLog" pointcut="execution(public void cao.zhanpeng.service.impl.AccountServiceImpl.saveMoney())"/>
        </aop:aspect>
    </aop:config>
</beans>

可以将切入点表达式独立出来

<!--将切入点表示式独立出来
如果放在切面内,只能当前切面引用
如果放在切面外,可以多个切面共用
-->
<aop:pointcut id="pt1" expression="execution(* cao.zhanpeng.service.impl.*.*(..))"/>
<!--再引用-->
<aop:before method="printLog" pointcut-ref="pt1"/>

切入点表达式

  • 关键字:execution(表达式)
  • 表达式:访问修饰符 返回值 通知目标类的全类名.方法名(参数列表)
  • 访问修饰符可以省略
  • 通常会将范围锁定到业务层的实现类包下
<!--星号表示任意通配符,双点..可以表示任意数量的通配符-->
* com.zanpng.service.impl.*.*(..)

 全通配写法如下(了解)

* *..*.*(..)

通知类型

aop:before,前置通知
aop:after-returning,后置通知
aop:after-throwing,异常通知,和后置通知互斥
aop:after,最终通知
aop:around,环绕通知

注解实现aop

1、配置环境

需要在applicationContext.xml文件导入context约束,配置ioc注解扫描,并直接使用以下标签开启aop注解。

<aop:aspectj-autoproxy/>

2、使用注解

在通知类上除了写上ioc注解,还要加上@Aspect注解,表示当前类是一个通知类。还要在对应的方法上使用如下注解

@Before注解,前置通知
@AfterRunning注解,后置通知
@AfterThrowing注解,异常通知
@After注解,最终通知
@Around注解,环绕通知

定义一个空语句体的方法,使用@Pointcut注解传递切入点表达式,并且在其他注解中传递该方法,注意括号别忘了。

@Pointcut("execution(* cao.zhanpeng.service.impl.*.*(..))")
private void pt1(){}

@Before("pt1()")
public void beforePrintLog(){……}

在使用注解实现aop时,spring会出现一些执行顺序上的问题。所以如果要用注解,还是使用环绕通知来手动指定较好。环绕通知需要自己实现动态代理的代码,根据位置手动指定某个通知为何种类型。

  • spring框架提供了一个接口ProceedingJoinPoint,该接口可以作为通知类中环绕通知方法的参数,执行时spring会自动帮我们创建实现类对象,比如叫pjp。
  • pjp.getArgs()可以获取方法执行所需的参数。
  • ProceedingJoinPoint接口中有一个方法proceed(),此方法可以明确调用切入点方法(对应反射中的invoke方法),传递获取的参数即可。
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
    Object rtValue = null;
    try{
        Object[] args = pjp.getArgs();
        ……前置通知代码块……
        rtValue = pjp.proceed(args)
        ……后置通知代码块……
    }catch{
        ……异常通知代码块……
    }finally{
        ……最终通知代码块……
    }
}

有了环绕通知,就不能让其他的通知起作用了,否则会产生冲突。

发布了70 篇原创文章 · 获赞 1 · 访问量 2255

猜你喜欢

转载自blog.csdn.net/caozp913/article/details/103774850