目录
1. 背景概念
1.1 定义
AOP (Aspect Orient Programming), 直译过来就是 面向切面编程。
AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。
1.2 AOP的本质
AOP的本质就是动态代理。Spring提供了面向切面编程的丰富支持,是通过动态代理实现的。
即:我们的初始class,经过动态代理,生成了新的class,这些class里织入@advice(方法)
advice() 就是模板模式
advice1()
func
advice2()
1.3 AOP的实现
因为利用了AOP的框架,我们实现了某些接口,当系统调用了这些方法时(因为有接口的限制,所以系统能够适时的决定调用哪个方法),这些方法自然就会被实现,不需要我们来管。
第一步:需要在配置文件中,注入我们的类(bean)
第二步:进行配置
1.4 各个名称的概念详解
横切关注点:跨越应用程序多个模块的方法或功能。即是与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
通知(Advice):AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。它是类中的一个方法。通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。Spring AOP 中有 5 中通知类型,分别如下(关系图):
@Before:通知方法会在目标方法调用之前执行
@After:通知方法会在目标方法返回或异常后调用
@AfterReturning:通知方法会在目标方法返回后调用
@AfterThrowing:通知方法会在目标方法抛出异常后调用
@Around:通知方法会将目标方法封装起来
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
连接点(JointPoint):表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
切入点(PointCut):可以插入增强处理的连接点。即织入点要对哪些方法起作用。在bean.xml中配置。
引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程即织入。
2. 示例工程
2.1 maven的配置文件: pom.xml
注意这里AOP需要依赖:org.aspectj
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.my.test</groupId>
<artifactId>SpringAOPDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
</project>
2.2 bean配置文件: bean_aop.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<!--注册bean-->
<bean id="userServiceImpl" class="com.my.test.UserServiceImpl"/>
<bean id="beforeLog" class="com.my.test.BeforeLog"/>
<bean id="afterLog" class="com.my.test.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法. 即before和after对下面这些方法起作用-->
<aop:pointcut id="pointcut" expression="execution(* com.my.test.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
2.3 接口和类的代码
2.3.1 接口:UserService
package com.my.test;
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
2.3.2 实现接口的类:UserServiceImpl
package com.my.test;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加用户信息");
}
public void delete() {
System.out.println("删除用户信息");
}
public void update() {
System.out.println("更改用户信息");
}
public void search() {
System.out.println("搜索用户信息");
}
}
2.3.3 Before类:BeforeLog
package com.my.test;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
//注意这里继承的接口MethodBeforeAdvice, AOP框架所有
public class BeforeLog implements MethodBeforeAdvice{
//method : 要执行的目标对象的方法
//agrs : 被调用的方法的参数
//target : 目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println( target.getClass().getName() + "的" + method.getName() + "方法准备执行了");
}
}
2.3.4 After类:AfterLog
package com.my.test;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
//注意这里继承的接口AfterReturningAdvice, AOP框架所有
public class AfterLog implements AfterReturningAdvice{
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName() +"的"+method.getName()+"方法," + "返回值为:"+returnValue);
}
}
2.4 测试类
package com.my.test.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.my.test.UserService;
public class SpringAOPDemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean_aop.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.search();
userService.delete();
}
}
输出结果:
com.my.test.UserServiceImpl的search方法准备执行了
搜索用户信息
执行了com.my.test.UserServiceImpl的search方法,返回值为:null
com.my.test.UserServiceImpl的delete方法准备执行了
删除用户信息
执行了com.my.test.UserServiceImpl的delete方法,返回值为:null
结果解析:
可以看到,每个方法的前后,都被织入了before和after类中的信息。
这个思想用在获取多个程序运行日志的操作上,很有用。