什么是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 方法中出现异常