概念
AOP是一种思想,而Spring AOP是对这种思想的具体实现,他只是将jdk动态代理和cglib代理整合到一块
简单来说:将程序中重复的代码抽取出来,在程序执行的时候,利用动态代理技术,在不修改源码的基础上,对现有的功能进行增强。
实现方式
JDK动态代理+cglib代理
术语
- Joinpoint(连接点)
目标对象中有可能被增强的方法(final修饰的方法可以被jdk动态代理) - Pointcut(切入点)
目标对象中已经被增强的方法 - Advice(通知/增强)
增强的代码,一般分为:前置通知before
、后置通知after-returning
、最终通知after
、异常通知after-throwing
、环绕通知around
- Target(目标)
被代理的对象(切入点/连接点所在的类) - Weaving(织入)
将通知应用到切入点的过程(会产生一个代理对象) - Proxy(代理)
将通知植入到目标对象后形成的代理对象 - Aspect(切面)
切入点+通知
代码实现
准备
-
目标对象(接口和实现类)
package com.spring.test04; public interface IStudentService { void saveStudent(); void updateStudent(); void deleteStudent(); }
package com.spring.test04; public class StudentServiceImpl implements IStudentService{ public void saveStudent() { System.out.println("新增学生"); } public void updateStudent() { System.out.println("更新学生"); } public void deleteStudent() { System.out.println("删除学生"); } }
-
切面类
package com.spring.test04.aspect; import org.aspectj.lang.ProceedingJoinPoint; public class StudentAspect { public void beforeAspect(){ System.out.println("前置通知"); } public void afterReturningAspect() { System.out.println("后置通知"); } public void afterAspect(){ System.out.println("最终通知"); } public void afterThrowingAspect() { System.out.println("异常通知"); } public void aroundAspect(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕通知---前"); Object[] args = joinPoint.getArgs(); joinPoint.proceed(args); System.out.println("环绕通知---后"); } }
基于xml
创建配置文件spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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="stu" class="com.spring.test04.StudentServiceImpl"/>
<!-- 切面 -->
<bean id="stuAspect" class="com.spring.test04.aspect.StudentAspect"/>
<aop:config>
<!-- 切入点 expression切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.spring.test04..*.*(..))"/>
<!-- 配置切面 -->
<aop:aspect id="aspect" ref="stuAspect">
<aop:before method="beforeAspect" pointcut-ref="pt1"/>
<aop:after-returning method="afterReturningAspect" pointcut-ref="pt1"/>
<aop:after method="afterAspect" pointcut-ref="pt1"/>
<aop:after-throwing method="afterThrowingAspect" pointcut-ref="pt1"/>
<aop:around method="aroundAspect" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
</beans>
测试
public void test03() {
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
IStudentService
stu = (IStudentService) app.getBean("stu");
stu.saveStudent();
System.out.println("-----------------");
stu.deleteStudent();
}
基于注解
- 接口不变,目标类(实现类对象)加注解
@Service
- 切面类
package com.spring.test04.annotation; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class StudentAspectAnno { //声明执行切入点,方便后续的使用,减少代码量 @Pointcut("execution(* com.spring.test04..*.*(..))") public void pointCut() {} @Before("execution(* com.spring.test04..*.*(..))") public void beforeAspect(){ System.out.println("前置通知"); } @AfterReturning("pointCut()") public void afterReturningAspect() { System.out.println("后置通知"); } @After("pointCut()") public void afterAspect(){ System.out.println("最终通知"); } @AfterThrowing("pointCut()") public void afterThrowingAspect() { System.out.println("异常通知"); } @Around("pointCut()") public void aroundAspect(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕通知---前"); Object[] args = joinPoint.getArgs(); joinPoint.proceed(args); System.out.println("环绕通知---后"); } }
- 增加配置类
package com.spring.test04.annotation; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("com.spring.test04") @EnableAspectJAutoProxy public class AspectConfig { }
如果配置类中没有加注解
@EnableAspectJAutoProxy
,则需要在配置文件中加上<aop:aspectj-autoproxy />
测试
public void test04() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AspectConfig.class);
IStudentService stu = (IStudentService) app.getBean("studentServiceImpl");
stu.saveStudent();
}
切入点表达式
切入点指示符
用来指示切入点表达式
SpringAOP支持的AspectJ切入点指示符有:
- execution:用于匹配符合的方法
- within:用于匹配指定的类及其子类中的所有方法
- @Annotation:用于匹配持有指定注解的方法
类型匹配语法
* :匹配任何数量字符
.. :匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数
+ :匹配指定类型的子类型,仅能作为后缀放在类型模式后
切入点使用语法
-
execution
语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数) 异常类型)
修饰符、异常类型可省略
返回值类型可用*表示任意返回值 -
within
语法:within(包名.类名)
-
@annotation
语法:@annotation(注解全限定类名)
这篇文章切入点表达式来自 spring aop切入点表达式详解 这里只取了其中一部分内容,感兴趣可点击链接了解