Spring注解方式实现AOP

1、AspectJ的通知类型

  • 前置通知:在目标方法调用前被调用的通知
  • 后置通知:在目标方法调用后被调用的通知
  • 异常通知:在目标方法执行出现异常时被调用的通知
  • 最终通知:无论是否出现异常,最后都会被调用的通知
  • 环绕通知:可以用来替代前置和后置

2、AspectJ的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型如下:
execution(modifiers-pattern? ret-type-pattern 
declaring-type-pattern?name-pattern(param-pattern)
 throws-pattern?)
 说明:
     modifiers-pattern  访问权限类型
    ret-type-pattern  返回值类型
    declaring-type-pattern 包名类名
    name-pattern(param-pattern) 方法名(参数类型和参数个数)
    throws-pattern 抛出异常类型
    ?表示可选的部分

以上表达式共四个部分:

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中就是方法的签名。

PS:表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:

符号意义
*0-多个任意字符
..用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径
+用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类

示例:
execution(* com.lina.service.*.*(..)) 
指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.lina.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。

execution(* com.lina.service.IUserService+.*(..)) 
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。

3、注解方式实现AOP

注解方式实现AOP,只需要给切面类和核心业务类表明注解,然后交给Spring即可,Spring会根据注解的内容去创建对应的代理类,底层的实现原理就是动态代理。

3.1、引入依赖

 <dependencies>
            <!--spring 核心依赖-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.13.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.2.13.RELEASE</version>
            </dependency>
            <!--测试依赖-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <!--编译插件-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>

3.2、xml配置文件引入约束

开启包扫描以及注解AOP的使用,都要在beas标签里面配一下context和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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--包扫描-->
    <context:component-scan base-package="com.AE.service, com.AE.aop"/>

    <!--开启注解AOP的使用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <!--aop:aspectj-autoproxy的底层就是由AnnotationAwareAspectjAutoProxyCreator
    是基于AspectJ的注解适配自动代理生成器.通过注解扫描找到@Aspect定义的切面类,再由切面类切入点找到目标类的目标方法,再由通知类型找到切入的时间-->
</beans>

3.3、创建核心业务类

注意使用@Service注解,将该类的创建权限交给Spring。

public interface IService {
    void add(int id, String name);
    boolean update(int num);
}
@Service
public class TeamService implements IService{
    @Override
    public void add(int id, String name) {
        System.out.println("TeamService---add---");
    }

    @Override
    public boolean update(int num) {
        System.out.println("TeamService---update---");
        if(num < 666) return true;
        return false;
    }
}
@Service("nbaService")
public class NBAService implements IService{
    @Override
    public void add(int id, String name) {
        System.out.println("NBAService---add---");
    }

    @Override
    public boolean update(int num) {
        System.out.println("NBAService---update---");
        if(num > 666) return true;
        return false;
    }
}

3.4、创建切面类

用@Component 注解将该类的创建交给Spring。

用@Aspect 注解表示当前类是一个切面类

用@Before 注解来表示前置通知调用的方法

用@AfterReturning 注解来表示后置通知调用的方法

用@AfterThrowing 注解来表示异常通知调用的方法

用@After 注解来表示最终通知调用的方法

用@Round 注解来表示环绕通知调用的方法

用@Pointcut 注解来表示切入点表达式

@Component // 切面对象的创建对象的权限也交给了spring
@Aspect // aspectj 框架的注解 表示当前类是一个切面类
public class MyAspect {
    /**
     * 当很多的增强方法使用相同的切入点表达式的时候,编写和后期的维护比较麻烦
     * @Pointcut 注解是aspectj提供的定义切入点表达式
     */
    @Pointcut("execution(* com.AE.service..*.*(..))")
    private void pointCut(){};

    /**
     * 声明的前置通知
     */
    // @Before("execution(* com.AE.service..*.*(..))")
    @Before("pointCut()")
    public void before(JoinPoint jp){
        System.out.println("前置通知:在目标方法之前被调用的通知");
        String name = jp.getSignature().getName();
        System.out.println("拦截的方法名称: " + name);
        Object[] args = jp.getArgs();
        System.out.println("方法的参数个数:" + args.length);
        System.out.println("参数列表:");
        for(Object i : args){
            System.out.println("\t" + i);
        }
    }

    /**
     * 后置通知
     * @param result 得到方法的返回值
     * @return
     */
    // @AfterReturning(value = "execution(* com.AE.service..*.*(..))", returning = "result")
    @AfterReturning(value = "pointCut()", returning = "result")
    public Object afterReturn(Object result) {
        //这里的逻辑之事告知大家切面中可以修改方法的返回值
        if(result != null) {
            boolean res = (boolean) result;
            if(res) {
                result = false;
            }
        }
        System.out.println("后置通知:在目标方法之后被调用,返回值result=" + result);
        return result;
    }
    // @Around(value = "execution(* com.AE.service..*.*(..))")
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("方法执行之前执行-----");
        Object proceed = pjp.proceed();
        System.out.println("方法执行之后执行-----");
        return proceed;
    }

    /**
     * 异常通知
     * @param jp 获取异常方法名
     * @param t 获取异常信息
     */
    //@AfterThrowing(value = "execution(* com.AE.service..*.*(..))", throwing="t")
     @AfterThrowing(value = "pointCut()", throwing="t")
    public void exception(JoinPoint jp, Throwable t) {
        // 一般吧异常的时间、位置、原因都记录
        System.out.println("异常通知:在目标方法执行出现异常的时候才会调用的通知,否则不执行。");
        System.out.println(jp.getSignature().getName() + "方法出现异常" + t.getMessage());
    }

    /**
     * 最终通知
     */
    // @After(value = "execution(* com.AE.service..*.*(..))")
    @After(value = "pointCut()")
    public void myfinally() {
        System.out.println("最终通知:无论是否出现异常,最后都会被调用的通知");
    }
}

3.5、测试类

public void test01() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        TeamService teamService = (TeamService) ac.getBean("teamService");
        teamService.add(12, "AE");
        System.out.println("--------------");
        System.out.println("update结果=" + teamService.update(12));
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        NBAService nbaService = (NBAService) ac.getBean("nbaService");
        nbaService.add(12, "AC");
        System.out.println("update结果=" + nbaService.update(12));
    }

4、xml形式实现AOP

核心业务类和注解方式相同

4.1、定义切面类

/**
 * 定义切面类
 */
@Component // 切面对象的创建对象的权限也交给了spring
@Aspect // aspectj 框架的注解 表示当前类是一个切面类
public class MyAop {
    public void before(JoinPoint jp){
        System.out.println("前置通知:在目标方法之前被调用的通知");
    }

    public Object afterReturn(Object result) {
        System.out.println("后置通知:在目标方法之后被调用,返回值result=" + result);
        return result;
    }
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("方法执行之前执行-----");
        Object proceed = pjp.proceed();
        System.out.println("方法执行之后执行-----");
        return proceed;
    }

    public void exception(JoinPoint jp, Throwable t) {
        // 一般吧异常的时间、位置、原因都记录
        System.out.println("异常通知:在目标方法执行出现异常的时候才会调用的通知,否则不执行。");
        System.out.println(jp.getSignature().getName() + "方法出现异常" + t.getMessage());
    }

    public void myfinally() {
        System.out.println("最终通知:无论是否出现异常,最后都会被调用的通知");
    }
}

4.2、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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--包扫描-->
    <context:component-scan base-package="com.AE.service, com.AE.aop"/>

    <!--开启注解AOP的使用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <!--aop:aspectj-autoproxy的底层就是由AnnotationAwareAspectjAutoProxyCreator
    是基于AspectJ的注解适配自动代理生成器.通过注解扫描找到@Aspect定义的切面类,再由切面类切入点找到目标类的目标方法,再由通知类型找到切入的时间-->
    <aop:config>
        <!-- aop:pointcut 相当于是用来定义切入点表达式的变量       -->
        <aop:pointcut id="pt1" expression="execution(* com.AE.service..*.add*(..))"/>
        <aop:pointcut id="pt2" expression="execution(* com.AE.service..*.update*(..))"/>
        <aop:pointcut id="pt3" expression="execution(* com.AE.service..*.del*(..))"/>
        <!-- 用来绑定对应的切面类       -->
        <aop:aspect ref="myAop">
            <!-- before前置通知 method为切面类中前置通知方法名 pointcut为切入点表达式           -->
            <aop:before method="before" pointcut="execution(* com.AE.service..*.add*(..))"/>
            <!-- 假如由参数,则需要设置对应的参数           -->
            <aop:after-returning method="afterReturn" pointcut-ref="pt2" returning="result"/>
            <aop:after-throwing method="exception" pointcut-ref="pt1" throwing="t"/>
            <aop:after method="myfinally" pointcut-ref="pt1"/>
            <aop:around method="around" pointcut-ref="pt3"/>
        </aop:aspect>
    </aop:config>
</beans>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值