1、 创建maven的jar工程,导入依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>
2、 编写业务层的接口
public interface IAccountService { /** * 模拟保存账户 */ void saveAccount(); }
3、编写业务层接口的实现类:
@Service("accountService") public class AccountServiceImpl implements IAccountService{ @Override public void saveAccount() { System.out.println("执行了保存"); // int i=1/0; } }
4、 创建一个具有公共代码的类
@Component("logger") // 要将通知bean交个spring来管理,否则通知bean不生效 @Aspect//表示当前类是一个切面类
public class Logger { @Pointcut("execution(* com.itheima.service.impl.*.*(..))") private void pt1(){} //前置通知 @Before("pt1()") public void beforePrintLog(){ System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。"); } //后置通知 @AfterReturning("pt1()") public void afterReturningPrintLog(){ System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。"); } // 异常通知 @AfterThrowing("pt1()") public void afterThrowingPrintLog(){ System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。"); } //最终通知 @After("pt1()") public void afterPrintLog(){ System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。"); } }
在Logger类上面写@Aspect注解,表示当前类是一个切面类。
前置通知用@Before("pt1()")注解,
后置通知用@AfterReturning("pt1()")注解,
异常通知用@AfterThrowing("pt1()")注解,
最终通知用@After("pt1()")注解,
环绕通知用@Around("pt1()")注解
在Logger类中定义一个方法pt1,使用@Pointcut注解指定切入点表达式
注意引用切入点表达式时,@Before、@AfterReturning、@AfterThrowing、@After注解中的pt1后面的()一定要加上,否则会报错。
5、编写配置文件bean.xml,配置spring创建容器时要扫描的包
<?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.itheima"></context:component-scan> <!-- 配置spring开启注解AOP的支持--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
6、测试
public class AOPTest {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// ApplicationContext ac = new AnnotationConfigApplicationContext(Logger.class);
//2.获取对象
IAccountService as = (IAccountService)ac.getBean("accountService");
//3.执行方法
as.saveAccount();
}
}
结果:
发现最终通知在后置通知之前先执行,
发现最终通知在异常通知之前先执行,
Spring基于注解的AOP中,这四个通知类型确实有顺序调用的问题,所以在实际开发中一般选择用环绕通知。因为环绕通知不存在这个问题,因为代码是我们自己写的,我们想让他们什么时候执行就什么时候执行,故如果是基于注解的AOP,建议用环绕通知,因为前面四个通知在执行调用顺序上有问题。
不使用XML而是使用纯注解实现AOP:
1、注释掉bean.xml中的
2、给Logger类添加注解
@Component("logger") @Aspect//表示当前类是一个切面类 @ComponentScan(basePackages = "com.itheima") @EnableAspectJAutoProxy public class Logger { @Pointcut("execution(* com.itheima.service.impl.*.*(..))") private void pt1(){} //前置通知 @Before("pt1()") public void beforePrintLog(){ System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。"); } //后置通知 @AfterReturning("pt1()") public void afterReturningPrintLog(){ System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。"); } // 异常通知 @AfterThrowing("pt1()") public void afterThrowingPrintLog(){ System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。"); } //最终通知 @After("pt1()") public void afterPrintLog(){ System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。"); } }
使用注解@ComponentScan(basePackage=“com.itheima”)替换<context:component-scan>标签
使用@EnableAspectJAutoProxy替换<aop:aspectj-autoproxy>标签。
3、测试:
public class AOPTest { public static void main(String[] args) { //1.获取容器 // ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); ApplicationContext ac = new AnnotationConfigApplicationContext(Logger.class); //2.获取对象 IAccountService as = (IAccountService)ac.getBean("accountService"); //3.执行方法 as.saveAccount(); } }
结果: