目录 什么是spring AOP AOP的优势 AOP的底层原理 AOP的相关术语 AOP的应用 代码示例 1. 导入Maven项目依赖 2. 准备一个实体类(先定义接口再实现) 3. 定义切面类 4. 准备配置文件 5. 准备测试类 6. 测试结果
什么是spring AOP
AOP(Aspect Oriented Programming)又称为面向切面开发 ,是一种编程范式,是OOP(Object Oriented Programming)的延续,也是Spring框架中函数式编程的一种衍生,利用AOP可以对业务层逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的重用性 。AOP采取横向抽取机制,取代传统纵向继承体系。
AOP的优势
在运行期间,可以不修改源代码对已有的方法进行增强。 提高了开发效率。 方便后期维护。
AOP的底层原理
实现方式 采用技术 接口 JDK动态代理技术 类继承 cglib代理技术
AOP的相关术语
名称 含义 Joinpoint(连接点) 指被拦截到的点(可以用的),在spring中指的是方法。因为Spring只支持方法类型的连接点 Pointcut(切入点) 指的是要对哪些Joinpoint进行拦截的定义(要用的) Advice(通知 | 增强) 指拦截到Joinpoint之后所要做的事情,通知分为前置通知 、后置通知 、异常通知 、最终通知 和环绕通知 (切面类要完成的功能) Target(目标对象) 代理的目标对象 Weaving(织入) 把通知应用到目标对象来创建新的代理对象的过程 Proxy(代理) 一个类被AOP织入增强后,就产生了一个结果 Aspect(切面) 是切入点 + 通知的结合
AOP的应用
切入点表达式
格式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
。例如:<aop:before method="log1" pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save())"></aop:before>
注意问题
修饰符 可以省略,返回值类型 不能省略,不确定时用*来代替,包名、类名、方法名 都不能省略,可以使用*来代替或部分代替,参数如果只有一个可以用*代替,如果像代表任意参数用…表示。因此,比较通用的表达式为:execution(* com.qcby.*.*ServiceImpl.save*(…)) (需要注意包的层次)
通知类型
通知类型 解释 前置通知 目标方法执行前进行增强 后置通知 目标方法执行后无论失败与否都进行增强 异常通知 目标方法执行失败抛出异常后进行增强 最终通知 目标方法执行成功后最后进行增强 环绕通知 目标方法执行前后都可以进行增强,目标对象的方法需要手动执行
代码示例
实例代码设置了一个UserService
实现类,并对这个类的方法进行增强,具体增强的方法为:通过AOP调用配置好的切面类MyAspect
的方法 。从而使得在UserService
执行自身的方法时,可以完成切面方法的调用。
1. 导入Maven项目依赖
< dependencies> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-context</ artifactId> < version> 5.0.2.RELEASE</ version> </ dependency> < dependency> < groupId> commons-logging</ groupId> < artifactId> commons-logging</ artifactId> < version> 1.2</ version> </ dependency> < dependency> < groupId> log4j</ groupId> < artifactId> log4j</ artifactId> < version> 1.2.12</ version> </ dependency> < dependency> < groupId> junit</ groupId> < artifactId> junit</ artifactId> < version> 4.12</ version> < scope> test</ scope> </ dependency> < dependency> < groupId> com.alibaba</ groupId> < artifactId> druid</ artifactId> < version> 1.1.10</ version> </ dependency> < dependency> < groupId> mysql</ groupId> < artifactId> mysql-connector-java</ artifactId> < version> 5.1.6</ version> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-test</ artifactId> < version> 5.0.2.RELEASE</ version> < scope> test</ scope> </ dependency> < dependency> < groupId> aopalliance</ groupId> < artifactId> aopalliance</ artifactId> < version> 1.0</ version> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-aspects</ artifactId> < version> 5.0.2.RELEASE</ version> </ dependency> < dependency> < groupId> org.aspectj</ groupId> < artifactId> aspectjweaver</ artifactId> < version> 1.8.3</ version> </ dependency>
</ dependencies>
2. 准备一个实体类(先定义接口再实现)
在实体类的方法中存放一个异常:int aa = 1/0;
,用来测试异常通知。
package com. qcby. service ; public interface UserService { public void save ( ) ;
}
package com. qcby. service. impl ; import com. qcby. service. UserService ;
import org. springframework. stereotype. Service ; public class UserServiceImpl implements UserService { public void save ( ) {
System . out. println ( "业务层:Hello World!" ) ; }
}
3. 定义切面类
package com. qcby. util ; import org. aspectj. lang. ProceedingJoinPoint ;
import org. aspectj. lang. annotation. * ;
import org. springframework. stereotype. Component ; public class MyAspect { public void log1 ( ) { System . out. println ( "我是前置增强方法!" ) ; } public void log2 ( ) { System . out. println ( "我是后置增强方法!" ) ; } public void log3 ( ) { System . out. println ( "我是异常增强方法!" ) ; } public void log4 ( ) { System . out. println ( "我是最终增强方法!" ) ; } public void aroundLog ( ProceedingJoinPoint point) { try { log1 ( ) ; point. proceed ( ) ; log4 ( ) ; } catch ( Throwable e) { log3 ( ) ; e. printStackTrace ( ) ; } finally { log2 ( ) ; } }
}
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: 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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd" > < bean id = " us" class = " com.qcby.service.impl.UserServiceImpl" > </ bean> < bean id = " myAspect" class = " com.qcby.util.MyAspect" > </ bean> < aop: config>
< aop: aspect ref = " myAspect" >
< aop: before method = " log1" pointcut = " execution(* com.qcby.*.*.*ServiceImpl.save*(..))" > </ aop: before>
< aop: after method = " log2" pointcut = " execution(public void com.qcby.service.impl.UserServiceImpl.save())" > </ aop: after>
< aop: after-throwing method = " log3" pointcut = " execution(public void com.qcby.service.impl.UserServiceImpl.save())" > </ aop: after-throwing>
< aop: after-returning method = " log4" pointcut = " execution(public void com.qcby.service.impl.UserServiceImpl.save())" > </ aop: after-returning> < aop: around method = " aroundLog" pointcut = " execution(* com.qcby.*.*.*ServiceImpl.save*(..))" /> </ aop: aspect> </ aop: config>
</ beans>
5. 准备测试类
import com. qcby. service. UserService ;
import com. qcby. util. SpringConfig ;
import org. junit. Test ;
import org. junit. runner. RunWith ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. test. context. ContextConfiguration ;
import org. springframework. test. context. junit4. SpringJUnit4ClassRunner ; @RunWith ( SpringJUnit4ClassRunner . class )
@ContextConfiguration ( "classpath:applicationContext.xml" )
public class UserServiceTest { @Autowired private UserService userService; @Test public void run ( ) { userService. save ( ) ; }
}
6. 测试结果
6.1 方法正常运行
6.2 方法中出现异常