@Aspect//表示当前类是一个切面类
1.配置前置通知 在切入点方法执行前执行
<aop:before method="" pointcut-ref=""></aop:before>
配置后置通知 在切入点方法正常执行后执行,他和异常通知只能执行一个
<aop:after-returning method="" pointcut-ref=""></aop:after-returning>
配置异常通知 在切入点方法正常执行产生异常后执行,他和后置通知只能执行一个
<aop:after-throwing method="" pointcut-ref=""></aop:after-throwing>
配置最终通知 无论切入点方法是否正常执行 他都在其后面执行
<aop:after method="" pointcut-ref=""></aop:after>
配置环绕通知 详细注释请看Logger类中
<aop:around method="" pointcut-ref=""></aop:around>
配置切入点表达式, id属性用于指定表达式唯一标识。execution属性用于指定表达式内容
注意:此标签写在aop:aspect标签内部时,且只有当前切面使用。
此标签写在aop:aspect标签外部时,此时就变成了所以切面都可用。
但是注意:这里xml导入了约束 放外面 要放在aop:aspect标签之前
pom.xml:
<?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.benti</groupId>
<artifactId>day03_eesy_04_adviceType</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<!--一个语言的软件联盟,能够给我们解析出bean.xml配置中的切入点表达式-->
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
IAccountService:接口
package com.benti.service;
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
/**
* 模拟更新账户
* @param i
*/
void updateAccount(int i);
/**
* 删除账户
* @return
*/
int deleteAccount();
}
AccountServiceImpl:
/**
* 账户的业务层实现类
*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Override
public void saveAccount() {
System.out.println("执行了保存");
// int i= 1/0;//手动写入错误,使异常通知执行 而跳过后置通知
}
@Override
public void updateAccount(int i) {
System.out.println("执行了更新"+i);
}
@Override
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
Logger:
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog() {
System.out.println("前置通知的beforePrintLog方法开始记录日志了");
}
/**
* 后置通知
*/
public void afterReturningPrintLog() {
System.out.println("后置通知的afterReturningPrintLog方法开始记录日志了");
}
/**
* 异常通知
*/
public void afterThrowingPrintLog() {
System.out.println("异常通知的afterThrowingPrintLog方法开始记录日志了");
}
/**
* 最终通知
*/
public void afterPrintLog() {
System.out.println("最终通知的afterPrintLog方法开始记录日志了");
}
/**
* 环绕通知
* 问题: 当我们正置了环经通知之后,切入点方法没有执行,而通知方法执行。
* 分析: 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有
* 解决:
* Spring框架为我们提供了一个接口: ProceedingJoinPoint。 该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。|
*
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*
*/
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了............前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了........后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了..........异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了.......最终");
}
}
}
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"
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">
<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.benti.service.impl.AccountServiceImpl"></bean>
<!--配置Logger类-->
<bean id="logger" class="com.benti.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置前置通知 在切入点方法执行前执行
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>-->
<!--配置后置通知 在切入点方法正常执行后执行,他和异常通知只能执行一个
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning> -->
<!--配置异常通知 在切入点方法正常执行产生异常后执行,他和后置通知只能执行一个
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
<!--配置最终通知 无论切入点方法是否正常执行 他都在其后面执行
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
<!--这里注释了其他四种通知 使用环绕-->
<!--配置环绕通知 详细注释请看Logger类中-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
<!--配置切入点表达式,见开头-->
<aop:pointcut id="pt1" expression="execution(* com.benti.service.impl.*.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
AOPTest:
public class AOPTest {
public static void main(String[] args) {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
IAccountService as = (IAccountService) ac.getBean("accountService");
//执行方法
as.saveAccount();
}
}
测试结果:
Logger类中的aroundPrintLog方法开始记录日志了............前置
执行了保存
Logger类中的aroundPrintLog方法开始记录日志了........后置
Logger类中的aroundPrintLog方法开始记录日志了.......最终