spring框架学习-aop-注解方式

前言

注解方式的aop,需要用到几个新的注解

1.@Aspect//表明这是一个切面类
2.@Pointcut("execution(* com.cong.service.AccountServiceImpl.*(..))")//切入点表达式
  private void accountPointcut(){}
3.@Before(value = "accountPointcut()")//前置通知注解,accountPointcut()的括号不能去掉
  类似的还有AfterReturning,AfterThrowing,Around
4.还有在配置文件中添加一句,告知spring开启注解aop的支持
    <!-- 告知spring,开启注解aop的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

1.创建maven工程,添加相关依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cong</groupId>
    <artifactId>spring_aop_annotation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- aop -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
    </dependencies>
</project>

2.在java目录下创建com.cong.pojo.Account类,通过注解创建对象,注入数据,并且添加到ioc容器中

package com.cong.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component//注入到ioc容器中
public class Account {
    @Value("1")
    private int id;
    @Value("cong")
    private String name;
    @Value("100")
    private float money;
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

3.在java目录下创建com.cong.service包,并且创建接口和类

package com.cong.service;

import com.cong.pojo.Account;

public interface AccountService {
    void saveAccount(Account account);
}



package com.cong.service;

import com.cong.pojo.Account;
import org.springframework.stereotype.Service;

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount(Account account) {
        System.out.println("save account..." + account.toString());
//        int i = 1/0;
    }
}

4.在java目录下创建com.cong.utils.MyTransaction通知类

先注释掉环绕通知的注解

package com.cong.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component//
@Aspect//表明这是一个切面类
public class MyTransaction {
    @Pointcut("execution(* com.cong.service.AccountServiceImpl.*(..))")//切入点表达式
    private void accountPointcut(){}
    //前置通知
    @Before(value = "accountPointcut()")//前置通知注解
    public void beforeTransaction(){
        System.out.println("前置通知MyTransaction类中的beforeTransaction方法执行了...");
    }
    //后置通知
    @AfterReturning("execution(* com.cong.service.AccountServiceImpl.*(..))")//如果没有配置切入点表达式
    public void afterReturningTransaction(){
        System.out.println("后置通知MyTransaction类中的afterReturningTransaction方法执行了...");
    }
    //异常通知
    @AfterThrowing("accountPointcut()")
    public void afterThrowingTransaction(){
        System.out.println("异常通知MyTransaction类中的afterThrowingTransaction方法执行了...");
    }
    //最终通知
    @After("accountPointcut()")
    public void afterTransaction(){
        System.out.println("最终通知MyTransaction类中的afterTransaction方法执行了...");
    }
    //环绕通知
    //@Around("accountPointcut()")
    public Object aroundTransaction(ProceedingJoinPoint proceedingJoinPoint){
        Object res = null;
        try {
            Object args [] = proceedingJoinPoint.getArgs();
            beforeTransaction();
            proceedingJoinPoint.proceed(args);
            afterReturningTransaction();
            return res;
        }catch (Throwable throwable){
            afterThrowingTransaction();
            return new RuntimeException(throwable);
        }finally {
            afterTransaction();
        }
    }
}

5.在resources目录下创建bean.xml文件

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置spring要扫描的包 -->
    <context:component-scan base-package="com.cong"></context:component-scan>
    <!-- 告知spring,开启注解aop的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

6.在test,java目录下创建测试类

import com.cong.pojo.Account;
import com.cong.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopAnnotationTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        AccountService accountService = (AccountService)context.getBean("accountService");
        Account account = (Account)context.getBean("account");
        accountService.saveAccount(account);
    }
}

7.运行结果,与我们想象的有些出入

后置通知和异常 通知的顺序明显不同

这是spring基于注解的aop的确有顺序调用的问题,所以实际开发中的选择应该有一个慎重的考虑

信息: Loading XML bean definitions from class path resource [bean.xml]
前置通知MyTransaction类中的beforeTransaction方法执行了...
save account...Account{id=1, name='cong', money=100.0}
最终通知MyTransaction类中的afterTransaction方法执行了...
后置通知MyTransaction类中的afterReturningTransaction方法执行了...

8.将前置通知,后置通知,异常通知,最终通知的注解注释掉,取消环绕通知的注解

    //前置通知
    //@Before(value = "accountPointcut()")//前置通知注解
    public void beforeTransaction(){
        System.out.println("前置通知MyTransaction类中的beforeTransaction方法执行了...");
    }
    //后置通知
    //@AfterReturning("execution(* com.cong.service.AccountServiceImpl.*(..))")//如果没有配置切入点表达式
    public void afterReturningTransaction(){
        System.out.println("后置通知MyTransaction类中的afterReturningTransaction方法执行了...");
    }
    //异常通知
    //@AfterThrowing("accountPointcut()")
    public void afterThrowingTransaction(){
        System.out.println("异常通知MyTransaction类中的afterThrowingTransaction方法执行了...");
    }
    //最终通知
    //@After("accountPointcut()")
    public void afterTransaction(){
        System.out.println("最终通知MyTransaction类中的afterTransaction方法执行了...");
    }
    //环绕通知
    @Around("accountPointcut()")
    public Object aroundTransaction(ProceedingJoinPoint proceedingJoinPoint){
        Object res = null;
        try {
            Object args [] = proceedingJoinPoint.getArgs();
            beforeTransaction();
            proceedingJoinPoint.proceed(args);
            afterReturningTransaction();
            return res;
        }catch (Throwable throwable){
            afterThrowingTransaction();
            return new RuntimeException(throwable);
        }finally {
            afterTransaction();
        }
    }

9.再执行一遍,这样调用的顺序就没有问题了,毕竟这个全都是我们自己写的代码

信息: Loading XML bean definitions from class path resource [bean.xml]
前置通知MyTransaction类中的beforeTransaction方法执行了...
save account...Account{id=1, name='cong', money=100.0}
后置通知MyTransaction类中的afterReturningTransaction方法执行了...
最终通知MyTransaction类中的afterTransaction方法执行了...

猜你喜欢

转载自www.cnblogs.com/ccoonngg/p/11235483.html