在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 总的来说,AOP就是一编程规范,目的是在不改变方法源代码的基础上对方法进行功能增强。
在pom.xml中导入aop坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.31</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
BookDao接口中:
package com.lcyy.dao;
public interface BookDao {
void read();
}
实现类:注意要在BookDaoImpl方法上加入注解@Repository表示为交给Spring的一个组件
import com.lcyy.dao.BookDao;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void read() {
System.out.println("BookDao AOP 实现");
}
}
建立一个aop的包,在包下建立一个MyAdvice的通知类
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component//放在普通类上,交给ioc容器管理
@Aspect//定义这个类为切面类
public class MyAdvice {
//定义连接点
@Pointcut("execution(void com.lcyy.dao.BookDao.read())")
private void pc(){}
//定义通知
@Before("pc()")
public void testAdvice(){
System.out.println("执行前的时间毫秒值:");
System.out.println(System.currentTimeMillis());
}
}
注意: 1.在MyAdvice类上要加入注解:@Aspect表示定义为一个切面类,@Component表示交给ioc容器管理。 2.@Pointcut注解要求配置在方法上方,("execution(void com.lcyy.dao.BookDao.read())")为切入点表达式,execution为关键字, void为返回值 com.lcyy.dao.BookDao.read()为com.lcyy包下的dao包下的BookDao接口中的read()方法。 3.@Before("pc()") 其中@Before为前置通知,表示为在方法执行前执行。关于通知的几种类型,下面会详细讲解。其中 "pc()" 表示将连接点和通知捆绑起来,注意方法名必须相同。
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.lcyy")
@EnableAspectJAutoProxy//开启aop
public class SpringConfig {
}
其中注解 @EnableAspectJAutoProxy 表示开启AOP通知,交给IOC容器管理
import com.lcyy.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BookDaoTest {
public static void main(String[] args) {
//获取容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.read();
}
}
测试结果:
验证AOP的代理对象,在刚才测试类中获取
import com.lcyy.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BookDaoTest {
public static void main(String[] args) {
//获取容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.read();
System.out.println(bookDao.getClass());
}
}
测试结果:
其中:class com.sun.proxy.$Proxy19 就是为代理对象的类名
描述方法一:执行com.lcyy.dao包下的BookDao接口中的无参数read方法
execution(void com.lcyy.dao.BookDao.read())
描述方法二:执行com.lcyy.dao包下的BookDaoImpl实现类中的无参数read方法
execution(void com.lcyy.dao.BookDaoImpl.read())
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
execution(public User com.itheima.service.UserService.findById(int)) 其中:
(*):单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
(..):多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
常用的通配符表达式为:
@Pointcut("execution(* com.lcyy.dao.*.*(..)") 表示任意的访问修饰符 在com.lcyy.dao包下的任意类中的所有方法(..)表示可以有形参也可以没有形参。
//前置通知
@Before("execution(void com.lcyy.dao.BookDao.write())")
public void testAdvice2(){
System.out.println("我是前置通知");
}
名称:@AfterReturning
类型:==方法注解==
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
案例代码:
@AfterReturning("execution(void com.lcyy.dao.BookDao.write())")
public void testAdvice4(){
System.out.println("我是后置通知");
}
@After("execution(void com.lcyy.dao.BookDao.write())")
public void testAdvice3(){
System.out.println("我是最终通知");
}
@AfterThrowing("execution(void com.lcyy.dao.BookDao.write())")
public void testAdvice5(){
System.out.println("我是异常通知");
}
在以上四个通知后,我们在测试类中测试以上通知
注意:书写了异常通知,却没有执行,是因为简单的代码实现没有异常。
测试异常通知:
在BookDaoImpl 的实现类中书写测试异常的代码
@Override
public void write() {
System.out.println("BookDao AOP 实现");
int a = 10/0;
System.out.println("a = " + a);
}
测试结果:
@Around("execution(void com.lcyy.dao.BookDao.jiyi())")
public Object testJiyi(ProceedingJoinPoint PJ) throws Throwable {
System.out.println("我时前置通知");
Object Oj = PJ.proceed();
System.out.println("我是后置通知");
return Oj;
}
测试结果:
注意:
AOP在spring中是一个很重要的概念,理解和运用AOP在以后的业务开发中有很重要的作用!