Spring AOP
代理目标 (Target) : 谁将被其他对象代理,谁就是代理目标
代理对象(proxy) : 谁将代理其他对象,谁就是代理对象
连接点( Join Point ) : 连接点 = 执行点 + 方位
- 执行点:任意一个可以执行的方法都可以当作一个执行点
- 方位:
- 方法执行前 ( Before ):在方法调用之前调用通知
- 方法正常返回后 (After-returning):在方法执行成功之后调用通知
- 方法抛出异常后 (After-throwing):在方法抛出异常后进行通知
- 方法执行后 (after):在方法完成之后调用通知,无论方法执行成功与否
- 方法执行前和后 (Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
切点 ( Pointcut ) : (确定在哪里加入代码)
- 对连接点进行筛选的条件
- 要指定执行点和方位信息
- 确定在哪里加入代码
Advice:(确定加入什么代码)
- 在指定的切点 所选择的连接点加入的代码
切面(Aspect):切面(Aspect)= 切点(Pointcut)+ Advice
- 在哪里加?加什么代码?
织入( Weaver ):
- 将Advice对应的代码 加入到切点所选择的连接点的过程
- 实现方式:
<1> 编译时织入
<2> 类加载时织入
<3> 运行期织入
引入 ( Introduction ):
Spring AOP 的实现过程
工程一
【1、在parent (父模块) 模块添加依赖】
<!--声明版本-->
<aspectj.version>1.9.2</aspectj.version> <!-- ${aspectj.version} -->
<!--添加依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
Spring AOP 的内部实现需要依赖于 AspectJ
AspectJ 是基于 JVM 的 支持 AOP 操作的一种语言。
【2、新建 aop 模块 】
pom.xm:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
-
man/java 目录下创建 com.itlaobing.aop.schema 包
-
man/resources/ 目录下创建 com/itlaobing/aop/schema
-
test/java/ 目录下创建 com/itlaobing/aop/schema
【3、创建 Swimmable 、Tortoise 、SwimAdvices 】
Swimmable :
package com.itlaobing.aop.schema;
public interface Swimmable {
void swim();
}
Tortoise :
package com.itlaobing.aop.schema;
public class Tortoise implements Swimmable {
@Override
public void swim() {
System.out.println("乌龟在游泳");
}
@Override
public String toString() {
System.out.println( "[ " + this.getClass().getCanonicalName() + " : toString() ]");
return "Tortoise[ " + Integer.toHexString( System.identityHashCode( this ) )+ " ]";
}
}
SwimAdvices :
package com.itlaobing.aop.schema;
public class SwimAdvices {
public void abc(){
System.out.println("即将执行");
}
public void xyz(){
System.out.println("即将结束");
}
}
【4、创建 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"
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">
<!-- 确定需要 添加的代码所在的 bean -->
<bean id="abvices" class="com.itlaobing.aop.schema.SwimAdvices"/>
<!-- 确定代理目标(谁将被代理) -->
<bean id="tortoise" class="com.itlaobing.aop.schema.Tortoise"/>
<!-- 提供 AOP 配置 -->
<aop:config>
<aop:aspect ref="abvices">
<!-- 通过 aop:pointcut 来声明一个【切点】 ( 这个切点在 aop:aspect 内部,所以是局部切点 ) -->
<!-- 通过 aop:pointcut 标签的 expression 来指定切点表达式 -->
<aop:pointcut id="firstPointcut" expression="execution(* com.itlaobing.aop.schema.Tortoise.*() )"/>
<!-- 在 firstPointcut 所选择 【连接点】( 指定的 执行点 执行之前 ) 处 加入 advices 中的 before 方法 对应的代码 -->
<!-- 在 firstPointcut 所选择的那些方法之前前 先执行 advices 对象 before 方法-->
<aop:before pointcut-ref="firstPointcut" method="abc"/>
<aop:after pointcut-ref="firstPointcut" method="xyz"/>
</aop:aspect>
</beans>
【5、测试 】
package com.itlaobing.aop.schema;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestTortoise {
public static void main( String[] args ) {
//指定configguration metadata
String configLocations ="classpath:com/itlaobing/aop/schema/aop-schema.xml";
// 创建 Spring IoC 容器
AbstractApplicationContext container = new ClassPathXmlApplicationContext(configLocations);
// 从容器中获取 指定 id 对应的 bean , 并明确其类型
Object proxy = container.getBean("tortoise");
System.out.println(proxy.getClass());
System.out.println(proxy.toString());
if (proxy instanceof Swimmable){
Swimmable s =(Swimmable)proxy;
s.swim();
}
container.close();
}
}
工程二
【1、创建 Cat、CatAdvices、】
Cat:
package com.itlaobing.aop.schema;
public class Cat {
private String name;
public void eat(String food){
System.out.println( this.name + "吃" + food
}
public int div(int a , int b ){
int c = a / b ;
return c ;
}
public String getName(){
return name;
}
public void setName( String name){
this.name=name;
}
}
CatAdvices:
package com.itlaobing.aop.schema;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class CatAdvices {
/**
* 任意的一个 Advice 对应的方法中,第一个参数都可以是 org.aspectj.lang.JoinPoint <br>
* 通过 JoinPoint 可以在 Advice 对应的方法内部 访问 连接点 的信息 ( 就是可以访问 被拦截的方法的信息 )
*/
public void before( JoinPoint joinPoint ) {
// 获取 连接点 对应的 方法 的 签名
Signature signature = joinPoint.getSignature();
System.out.println("signature : " + signature);
// 获得方法的 修饰符 ( 以整数形式返回 )
int mod = signature.getModifiers();
//将 整数形式表示的 修饰符 解析为 字符串 形式
String modifiers = Modifier.toString(mod);
System.out.println("modifiers: " + modifiers);
// 获取 方法名称
String methodName = signature.getName();
System.out.println("name : " + methodName);
// 获取被拦截的方法在执行时接受的 实参
Object[] args = joinPoint.getArgs();
System.out.println("arguments : " + Arrays.toString(args));
System.out.println("【 " + methodName + " 】方法即将执行");
}
/**
* 环绕( around )
* @param joinPoint 必须是 ProceedingJoinPoint 类型
* @return
* @throws Throwable
*/
public Object around ( ProceedingJoinPoint joinPoint) throws Throwable {
String name = joinPoint.getSignature().getName();
System.out.println( "开始为" + name + "计时" );
long begin = System.nanoTime();
Object result = joinPoint.proceed() ; // 通过 ProceedingJoinPoint 的 proceed 方法让被拦截的方法继续执行
long end = System.nanoTime();
System.out.println( "为" + name + "计时结束" );
System.out.println( "[ " + name + " ]执行耗时 [ " + ( end - begin ) + "ns, ]" );
// 返回 由 被拦截的方法执行后 所返回的值
return result ;
}
public void afterReturn(JoinPoint joinPoint , Object returnValue ){
System.out.println("【" + joinPoint.getSignature().getName() + " 】方法执行后返回了【 " + returnValue +" 】" );
}
public void afterThrow (JoinPoint joinPoint , Throwable ex) {
System.out.println("【" + joinPoint.getSignature().getName() + " 】方法执行时抛出了【 " + ex + " 】" );
}
public void after( JoinPoint joinPoint ) {
Signature signature = joinPoint.getSignature();
String methodName = signature.getName() ;
System.out.println( "【 " + methodName + " 】方法执行结束" );
}
}
【2、创建 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:p="http://www.springframework.org/schema/p"
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">
<!-- 确定需要 添加的代码所在的 bean -->
<bean id="CatAdvices" class="com.itlaobing.aop.schema.CatAdvices"/>
<!-- 确定代理目标(谁将被代理) -->
<bean id="Cat" class="com.itlaobing.aop.schema.Cat" p:name="汤姆"/>
<!-- 提供 AOP 配置 -->
<aop:config>
<aop:aspect ref="CatAdvices">
<!-- 在 包名中使用 .. 表示 多层路径 -->
<!-- 在 () 之前中使用 * 表示 某个类中的所有方法 -->
<!-- 在 参数列表中使用 .. 表示 任意多个参数 ( 可以是 零个、一个、多个) -->
<!-- 在 参数列表中使用 * 表示 至少有一个参数 ( 可以是 一个 或 多个) -->
<aop:pointcut id="pc" expression="execution(* com..schema.Cat.*(..))"/>
<aop:before pointcut-ref="pc" method="before"/>
<aop:around pointcut-ref="pc" method="around"/>
<aop:after-returning pointcut-ref="pc" method="afterReturn" returning="returnValue"/>
<aop:after-throwing pointcut-ref="pc" method="afterThrow" throwing="ex"/>
<aop:after pointcut-ref="pc" method="after"/>
</aop:aspect>
</aop:config>
</beans>