Spring AOP快速入门----XML方式和注解方式
一、AOP概念(面向切面编程)
1. AOP优点是在不修改源码的情况下,对方法进行增强,使得代码易于维护,提高了开发效率。
2. Spring的AOP底层原理的是动态代理方式(jdk和cglib两种常用动态代理方式),通过Spring的封装,只需要对Spring核心配置文件applicationContext.xml文件进行相应配置即可实现AOP。
二、AOP相关术语
- Target(目标对象):代理的目标对象;
- Proxy(代理对象):目标对象被AOP织入增强后,产生的结果类;
- JoinPoint(连接点):可以被AOP织入增强的点(方法);
- Pointcut(切入点):从连接点中选取,进行AOP织入增强的点(方法);
- Advice(通知/增强):对连接点(方法)进行的增强操作;
- Aspect(切面):切点+通知;
- Weaving(织入):对切入点进行增强,并创建代理对象的过程;
三、AOP开发过程
1. 编写核心业务代码:目标类的目标方法;
2. 编写切面类,并在切面类中编写增强方法(通知);
3. 在applicationContext.xml中配置织入关系:将增强方法对应到连接点;
【注1: 通过Spring框架监控切入点的方法的执行,使用代理机制,动态创建目标对象的代理对象,根据增强方法的类别(before,after,around…),在代理对象的对应位置,将增强方法织入。】
【注2: 在Spring中,框架会根据目标类是否有接口来决定采用哪种代理方式】
四、基于XML的AOP开发
1. 在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>SpringAOP</groupId>
<artifactId>SpringAOP</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
2.创建目标接口,目标类,目标方法;
package com.aop;
public interface TargetInterface {
void save();
}
package com.aop;
public class Target implements TargetInterface {
public void save() {
System.out.println("<<目标方法执行...>>");
}
}
3. 创建切面类和增强方法;
package com.aop;
public class Aspect {
public void beforeMethod(){
System.out.println("<<前置增强方法...>>");
}
public void afterReturningMethod(){
System.out.println("<<后置增强方法...>>");
}
}
4. 配置织入关系;
<?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 id="target" class="com.aop.Target"></bean>
<!--切面对象-->
<bean id="aspect" class="com.aop.Aspect"></bean>
<!--配置织入-告诉spring框架,哪些方法(切点)需要进行哪些增强-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="aspect">
<!--抽取切点表达式-->
<!--<aop:pointcut id="pointCut" expression="execution(public void com.aop.Target.save())"></aop:pointcut>-->
<aop:before method="beforeMethod" pointcut="execution(public void com.aop.Target.save())"></aop:before>
<aop:after method="afterReturningMethod" pointcut="execution(public void com.aop.Target.save())"></aop:after>
</aop:aspect>
</aop:config>
</beans>
-
切点表达式语法:execution(修饰符 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略;
- 返回值类型,包名,类名,方法名可以使用 * 替代,代表 :任意;
- 包名和类名之间一个 . 代表当前包下的类,两个 . 代表当前包及其子包下的类;
- 参数列表使用两个 . 表示任意类型的参数列表;
- execution(public void com.aop.Target.save());
- execution(* com . aop . *. * ( . . ));
- execution(* com . aop . . * . * (. .));
-
增强(通知)类型:<aop:通知类型 method=“切面类中增强方法名” pointcut=“切点表达式”></aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 | < aop:before /> | 指定增强方法在切入点之前执行 |
后置通知 | < aop:after-returning /> | 指定增强方法在切入点之后执行 |
环绕通知 | < aop:around /> | 指定增强方法在切入点之前和之后都执行 |
异常抛出通知 | < aop:throwing /> | 指定增强方法在目标方法出现异常时执行 |
最终通知 | < aop:after /> | 无论目标方法是否出现异常,都执行 |
- 切点表达式抽取:不同的通知类型对应同一切点表达式时,可对表达式进行抽取;
<aop:pointcut id="pointCut" expression="execution(public void com.aop.Target.save())"></aop:pointcut>-->
5. 测试与结果
package test;
import com.aop.TargetInterface;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
@Test
public void test1(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用TargetInterface 说明被增强后的对象即(proxy对象)和原来的target对象是兄弟关系,所以用父类接口实现
TargetInterface target = (TargetInterface) app.getBean("target");
target.save();
}
}
四、基于注解的AOP开发
1. 创建目标接口,目标类,目标方法(上同);
2. 创建切面类和增强方法(上同);
3. 将目标类和切面类的对象创建权交给spring;
package com.aop;
import org.springframework.stereotype.Component;
@Component("target") //由spring产生bean对象
public class Target implements TargetInterface {
public void save() {
System.out.println("<<目标方法执行...>>");
}
}
4. 在切面类中使用注解配置织入关系;
package com.aop;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component("aspect")
@org.aspectj.lang.annotation.Aspect //标注当前类是切面类
public class Aspect {
// 配置前置通知
@Before(value = "execution(void com.aop.Target.save())")
public void beforeMethod(){
System.out.println("<<前置增强方法...>>");
}
// 配置后置通知
@AfterReturning(value = "pointcut()")
public void afterReturningMethod(){
System.out.println("<<后置增强方法...>>");
}
// 定义切点表达式方法,用于抽取
@Pointcut("execution(void com.aop.Target.save())")
public void pointcut(){
}
}
- 增强(通知)类型:@通知类型(value = “切点表达式”)
名称 | 标签 | 说明 |
---|---|---|
前置通知 | @Befoe | 指定增强方法在切入点之前执行 |
后置通知 | @AfterReturning | 指定增强方法在切入点之后执行 |
环绕通知 | @Around | 指定增强方法在切入点之前和之后都执行 |
异常抛出通知 | @AfterThrowing | 指定增强方法在目标方法出现异常时执行 |
最终通知 | @After | 无论目标方法是否出现异常,都执行 |
5. 在配置文件中开启组件扫描和AOP自动代理;
<?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"
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
">
<!--组件扫描-->
<context:component-scan base-package="com.aop"></context:component-scan>
<!--AOP自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
6. 测试与结果;
package test;
import com.aop.TargetInterface;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
@Test
public void test1(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用TargetInterface 说明被增强后的对象即(proxy对象)和原来的target对象是兄弟关系,所以用父类接口实现
TargetInterface target = (TargetInterface) app.getBean("target");
target.save();
}
}