Java框架(四)--Spring AOP(2)--五种通知及注解配置Spring AOP

第二章、五种通知及注解配置Spring AOP

一、五种通知类型

在这里插入图片描述

1、特殊的“通知” - 引介增强

引介增强(IntroductionInterceptor)是对类的增强,而非方法。它跟通知没有关系,本质是拦截器。
引介增强允许在运行时为目标类增加新属性或方法。
引介增强允许在运行时改变类的行为,让类随运行环境动态变更。
引介增强使用起来比较复杂,在开发中也是比较少用的。

2、代码演示后置通知和返回后通知

在切面类MethodAspect中添加后置通知和返回后通知的切面方法

package com.ql.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;

import java.text.SimpleDateFormat;
import java.util.Date;

//切面类
public class MethodAspect {
    //切面方法,用于扩展额外功能
    //JoinPoint 连接点,通过连接点可以获取目标类/方法的信息

    public void printExecutionTime(JoinPoint joinPoint){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String now = sdf.format(new Date());
        String className = joinPoint.getTarget().getClass().getName();//获取目标类的名称
        String methodName = joinPoint.getSignature().getName();//获取目标方法名称
        System.out.println("-------->"+now+":"+className+"."+methodName);
        Object[] args = joinPoint.getArgs();
        System.out.println("-------->参数个数:"+args.length);
        for (Object arg: args){
            System.out.println("-------->参数:"+arg);
        }
    }

    public void doAfterReturning(JoinPoint joinPoint, Object ret){
        System.out.println("<---------返回后通知:"+ret);
    }
    public void doAfter(JoinPoint joinPoint){
        System.out.println("<---------触发后置通知");
    }
}

然后在applicationContext.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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="userDao" class="com.ql.spring.aop.dao.UserDao"/>
    <bean id="employeeDao" class="com.ql.spring.aop.dao.EmployeeDao"/>
    <bean id="userService" class="com.ql.spring.aop.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="employeeService" class="com.ql.spring.aop.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
    </bean>
    <!--AOP配置-->
    <bean id="methodAspect" class="com.ql.spring.aop.aspect.MethodAspect"/>
    <aop:config>
        <!--PointCut 切点,使用execution表达式描述切面的作用范围-->
        <!--execution(public * com.ql..*.*(..))说明切面作用在com.ql包下的所有类的所有方法上-->
        <aop:pointcut id="pointcut" expression="execution(public * com.ql..*Service.*(..))"/>
        <!--定义切面类-->
        <aop:aspect ref="methodAspect">
            <!--before通知,代表在目标方法前先执行methodAspect.printExecutionTime()-->
            <aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
            <aop:after method="doAfter" pointcut-ref="pointcut"/>
            <aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

运行入口类

package com.ql.spring.aop;

import com.ql.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.createUser();
        userService.generateRandomPassword("MD5",16);
    }
}
/*
-------->2022-07-03 22:20:07 478:com.ql.spring.aop.service.UserService.createUser
-------->参数个数:0
执行创建用户业务逻辑
新增用户数据
<---------触发后置通知
<---------返回后通知:null
-------->2022-07-03 22:20:07 487:com.ql.spring.aop.service.UserService.generateRandomPassword
-------->参数个数:2
-------->参数:MD5
-------->参数:16
按MD5方式生成16位随机密码
<---------触发后置通知
<---------返回后通知:HTYughjd23g
*/

其中后置通知和返回后通知执行的顺序由配置的前后顺序决定的。

3、代码演示异常通知

首先在UserService的createUser方法中抛出一个异常

    public void createUser(){
        if(1==1){
            throw new RuntimeException("用户已存在");
        }
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }

然后在切面类MethodAspect中添加异常通知的切面方法doAfterThrowing

    public void doAfterThrowing(JoinPoint joinPoint, Throwable th){
        System.out.println("<---------异常通知:"+th.getMessage());
    }

然后在配置文件applicationContext.xml中配置异常通知

<!--定义切面类-->
<aop:aspect ref="methodAspect">
    <!--before通知,代表在目标方法前先执行methodAspect.printExecutionTime()-->
    <aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
    <aop:after method="doAfter" pointcut-ref="pointcut"/>
    <aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
    <aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
</aop:aspect>

运行入口类,输出为

/*
-------->2022-07-03 22:26:11 323:com.ql.spring.aop.service.UserService.createUser
-------->参数个数:0
<---------触发后置通知
<---------异常通知:用户已存在
Exception in thread "main" java.lang.RuntimeException: 用户已存在
	at com.ql.spring.aop.service.UserService.createUser(UserService.java:9)
	...
*/

其中后置通知和异常通知执行的顺序同样由配置的前后顺序决定的。

4、代码演示环绕通知 - 方法性能筛选

首先在UserService的createUser方法中延长线程的逻辑

    public void createUser(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }

然后在com.ql.spring.aop.aspect包下新建切面类MethodChecker

package com.ql.spring.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MethodChecker {
    //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
    public Object check(ProceedingJoinPoint pjp) throws Throwable {
        try {
            long startTime = new Date().getTime();
            Object ret = pjp.proceed();//执行目标方法
            long endTime = new Date().getTime();
            long duration = endTime - startTime;//执行时长
            if(duration >= 1000){
                String className = pjp.getTarget().getClass().getName();
                String methodName = pjp.getSignature().getName();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
                String now = sdf.format(new Date());
                System.out.println("======"+now+":"+className+"."+methodName+"("+duration+")");
            }
            return ret;
        } catch (Throwable e) {
            System.out.println("Exception message"+e.getMessage());
            throw e;
        }
    }
}

然后在配置文件applicationContext.xml中配置

    <bean id="methodChecker" class="com.ql.spring.aop.aspect.MethodChecker"/>
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(public * com.ql..*Service.*(..))"/>
        <aop:aspect ref="methodChecker">
            <!--环绕通知-->
            <aop:around method="check" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

运行入口类,输出为

/*
执行创建用户业务逻辑
新增用户数据
======2022-07-03 22:59:35 735:com.ql.spring.aop.service.UserService.createUser(3022)
*/

二、利用注解配置Spring AOP

打开IDEA创建新的maven工程,在pom.xml中引入spring-context和aspectjweaver依赖

<?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.ql.spring</groupId>
    <artifactId>aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--aspectjweaver是Spring AOP的底层依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
</project>

在resources目录下创建配置文件applicationContext.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 https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--初始化IoC容器-->
    <context:component-scan base-package="com.ql"/>
    <!--启动Spring AOP注解模式-->
    <aop:aspectj-autoproxy/>
</beans>

在com.ql.spring.aop.dao包下创建UserDao类

package com.ql.spring.aop.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public void insert(){
        System.out.println("新增用户数据");
    }
}

在com.ql.spring.aop.service包下创建UserService类

package com.ql.spring.aop.service;

import com.ql.spring.aop.dao.UserDao;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {
    @Resource
    private UserDao userDao;
    public void createUser(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }
    public String generateRandomPassword(String type, Integer length){
        System.out.println("按"+type+"方式生成"+length+"位随机密码");
        return "HTYughjd23g";
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

在com.ql.spring.aop.aspect包下创建切面类MethodChecker

package com.ql.spring.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component //标记当前类为组件
@Aspect //说明当前类是切面类
public class MethodChecker {
    //环绕通知,参数为PointCut切点表达式
    @Around("execution(* com.ql..*Service.*(..))")
    //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
    public Object check(ProceedingJoinPoint pjp) throws Throwable {
        try {
            long startTime = new Date().getTime();
            Object ret = pjp.proceed();//执行目标方法
            long endTime = new Date().getTime();
            long duration = endTime - startTime;//执行时长
            if(duration >= 1000){
                String className = pjp.getTarget().getClass().getName();
                String methodName = pjp.getSignature().getName();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
                String now = sdf.format(new Date());
                System.out.println("======"+now+":"+className+"."+methodName+"("+duration+")");
            }
            return ret;
        } catch (Throwable e) {
            System.out.println("Exception message"+e.getMessage());
            throw e;
        }
    }
}

最后在com.ql.spring.aop包下创建入口类SpringApplication并运行

package com.ql.spring.aop;

import com.ql.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.createUser();
        userService.generateRandomPassword("MD5",16);
    }
}
/*
执行创建用户业务逻辑
新增用户数据
======2022-07-03 23:17:40 037:com.ql.spring.aop.service.UserService.createUser(3018)
按MD5方式生成16位随机密码
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值