Spring AOP入门
相关依赖
spring的context依赖,context依赖aop,aspectj为spring支持的第三方aop依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--aspectj的织入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
代码实践
XML配置AOP
相关Java类代码:
public interface TargetInterface {
public void save();
}
public class Target implements TargetInterface {
public void save() {
System.out.println("save running.....");
int i = 1/0; //定义除零运算错误
}
}
public class MyAspect {
public void before(){
System.out.println("前置增强..........");
}
public void afterReturning(){
System.out.println("后置增强..........");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强....");
return proceed;
}
public void afterThrowing(){
System.out.println("异常抛出增强..........");
}
public void after(){
System.out.println("最终增强..........");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test(){
target.save();
}
}
使用配置绑定的applicationContext.xml文件进行XML方式的AOP开发
- 在xml中bean注入目标类target,切面类myAspect(以下为示范代码,根据个人名称定义,包名位置不同而不同)
<!--目标对象-->
<bean id="target" class="cn.ethan.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="cn.ethan.aop.MyAspect"></bean>
- 导入aop命名空间
- 配置织入关系
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--抽取切点表达式-->
<aop:pointcut id="myPointcut" expression="execution(* cn.ethan.aop.*.*(..))"></aop:pointcut>
<!--切面:切点+通知-->
<!--<aop:before method="before" pointcut="execution(public void Target.save())"/>-->
<!--<aop:before method="before" pointcut="execution(* cn.ethan.aop.*.*(..))"/>
<aop:after-returning method="afterReturning" pointcut="execution(* cn.ethan.aop.*.*(..))"/>-->
<!--<aop:around method="around" pointcut="execution(* cn.ethan.aop.*.*(..))"/>
<aop:after-throwing method="afterThrowing" pointcut="execution(* cn.ethan.aop.*.*(..))"/>
<aop:after method="after" pointcut="execution(* cn.ethan.aop.*.*(..))"/>-->
<aop:around method="around" pointcut-ref="myPointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
问题
1. xml异常抛出通知顺序与配置顺序的关系
<aop:around method="around" pointcut-ref="myPointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut"/>
如配置aroud、after-throwing、after通知为上所示,执行结果如下:
若颠倒after-throwing、after通知顺序,执行结果如下:
因此基本可以得出结论,异常抛出通知、最终通知的实际顺序受到xml配置顺序的影响,且与配置顺序一致。
注解配置AOP
相关Java代码:
public interface TargetInterface {
public void save();
}
@Component("target")
public class Target implements TargetInterface {
public void save() {
System.out.println("save running.....");
//int i = 1/0;
}
}
即使使用注解也仍然需要xml配置与spring联系
- 配置文件中开启组件扫描和AOP 的自动代理,引入AOP命名空间(实质上包扫描,用注解代替了xml手动的bean注入)
<!--组件扫描-->
<context:component-scan base-package="cn.ethan.anno"/>
<!--aop自动代理-->
<aop:aspectj-autoproxy/>
- 使用注解等效xml的织入绑定
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
//配置前置通知
//@Before("execution(* cn.ethan.anno.*.*(..))")
public void before(){
System.out.println("前置增强..........");
}
public void afterReturning(){
System.out.println("后置增强..........");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
//@Around("execution(* cn.ethan.anno.*.*(..))")
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强....");
return proceed;
}
public void afterThrowing(){
System.out.println("异常抛出增强..........");
}
//@After("execution(* cn.ethan.anno.*.*(..))")
@After("cn.ethan.anno.MyAspect.pointcut()")
public void after(){
System.out.println("最终增强..........");
}
//定义切点表达式
@Pointcut("execution(* cn.ethan.anno.*.*(..))")
public void pointcut(){}
}
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {
@Autowired
private TargetInterface target;
@Test
public void test(){
target.save();
}
}