一、Spring AOP概述
1.AOP是什么
AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善
2.AOP应用场景
AOP目的:基于OCP(开闭原则)在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以控制对象的执行。
应用场景示例:AOP常应用于日志的处理,事务处理,权限处理,缓存处理等
3.AOP实现原理
AOP底层基于代理机制实现:
- 被代理对象实现了接口时,采用JDK动态代理机制:代理类和被代理类默认实现相同接口;
- 被代理对象未实现接口时,采用CGLIB代理机制:代理类默认继承被代理类;
4.AOP相关术语
- 切面aspect:用于封装扩展业务的类的对象;
- 连接点joinpoint
- 切入点pointcut
- 通知advice:切面扩展业务中的一个操作(方法);
二、Spring AOP基于XML配置及应用
1.配置Spring AOP依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架(AOP框架)
所以要引用1.8.5有问题 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
2.添加Spring配置文件
- 向IOC注入核心业务类HelloServiceImpl.java
- 向IOC注入扩展业务类LoggingAspect.java
- 增加AOP配置<aop:config>
- 含切入点配置<aop:pointcut >
- 含<aop:aspect>....</aop:aspect>含<aop:before method="..." pointcut-ref="..."/>等
- 增加AOP配置<aop:config>
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 核心业务对象 -->
<bean id="helloService"
class="spring.beans.HelloServiceImpl"/>
<!-- 配置非核心业务对象(日志处理对象):切面 -->
<bean id="log"
class="spring.aop.LoggingAspect"/>
<!-- AOP配置(切入点,切面) -->
<aop:config>
<!-- 配置切入点(bean,@annotation,within) -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)"
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="log" >
<aop:before method="beforeMethod"
pointcut-ref="logPointCut"/>
<aop:after method="afterMethod"
pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>
3.Spring AOP应用
- 业务层接口HelloService--核心业务接口
public interface HelloService {
void sayHello(String msg);
}
- 业务层接口实现类HelloServiceImpl--核心业务实现类
public class HelloServiceImpl implements HelloService {
public void sayHello(String msg) {
//假设这条语句是我们系统中的核心业务
System.out.println(msg);
}
}
- 日志处理类LoggingAspect:通过此横切面实现扩展业务
public class LoggingAspect {
public void beforeMethod(){
System.out.println("method start");
}
public void afterMethod(){
System.out.println("method end");
}
}
- 利用测试类测试效果
public class TestAOP01 {
private ClassPathXmlApplicationContext ctx;
@Before
public void init(){
ctx=new ClassPathXmlApplicationContext("spring-configs.xml");
}
@Test
public void testSayHello(){
HelloService helloService=
ctx.getBean("helloService",HelloService.class);
helloService.sayHello("cgb1712");
}
@After
public void destory(){
ctx.close();
}
}
- 以上简单应用的原理分析
三、Spring AOP基于注解配置及应用
1.pom.xml略
2.核心业务订单接口+实现类,这里实现类为展示
public interface OrderService {
public void saveOrder();
public void deleteOrder();
}
3.时间处理类:作为核心业务功能扩展<横切面>
- 切面@Aspect 修饰一个AOP横切面对象
- 切入点@Pointcut("bean(orderService)") 定义本类中的切入点
- 前置通知@Before
- 后置通知@After
@Aspect
@Service
public class TimeAspect {
@Pointcut("bean(orderService)")
public void pointcut(){}
/**增强功能:前置通知(业务方法执行之前执行)*/
@Before("pointcut()")
public void begin(){
System.out.println("start:"+System.nanoTime());
}
/**增强功能:最终通知(业务方法执行最后执行,
*无论业务方法是否执行成功,此功能都要执行)*/
@After("pointcut()")
public void end(){
System.out.println("end:"+System.nanoTime());
}
}
4.基于注解方式配置AOP实现两种方式
1.基于xml方式配置对注解的应用
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置对类组件的扫描 -->
<context:component-scan base-package="com.spring"/>
<!-- 启用AOP注解(自动为目标对象创建代理对象) -->
<aop:aspectj-autoproxy/>
</beans>
2.在类中基于注解方式的配置
@ComponentScan("com.spring.beans")
@EnableAspectJAutoProxy
public class AppConfig {
}
5.利用测试类测试效果
1.基于xml方式注解配置的测试实现:ClassPathXmlApplicationContext初始化
public class TestAOP01 {
public static void main(String[] args) {
//1.初始化容器
ClassPathXmlApplicationContext ctx=
new ClassPathXmlApplicationContext(
"applicationContext.xml");
//2.获取bean对象
OrderService os=(OrderService)
ctx.getBean("orderService",
OrderService.class);
//3.执行业务方法
os.saveOrder();
os.deleteOrder();
//4.释放资源
ctx.close();
}
}
2.基于类中注解方式配置的测试实现:AnnotationConfigApplicationContext初始化
public class TestAOP02 {
public static void main(String[] args) {
//1.初始化容器对象
AnnotationConfigApplicationContext ctx=
new AnnotationConfigApplicationContext(
AppConfig.class);
//2.获取Bean对象
OrderService orderService=
ctx.getBean("orderService", OrderService.class);
//3.执行业务
orderService.saveOrder();
//orderService.deleteOrder();
//4.释放资源
ctx.close();
}
}
四、Spring AOP切面表达式+切面通知+切面执行顺序
1.切面表达式
指示符 | 作用 |
bean | 匹配指定bean id的方法执行 |
within | 指定包下类型内的方法执行 |
@annotation | 指定注解修饰的方法执行 |
execution | 细粒度方法匹配执行具体业务 |
1.bean应用于类级别,实现粗粒度的控制:
bean("userServiceImpl")) |
指定一个类 |
bean("*ServiceImpl") |
指定所有的后缀为service的类 |
2.within应用于类级别,实现粗粒度的切面表达式定义:
within("aop.service.UserServiceImpl") |
指定类,只能指定一个类 |
within("aop.service.*") |
只包括当前目录下的类 |
within("aop.service..*") |
指定当前目录包含所有子目录中的类 |
3.execution方法级别,细粒度的控制:
语法:execution(返回值类型 包名.类名.方法名(参数列表))
execution(void aop.service.UserServiceImpl.addUser()) |
匹配方法 |
execution(void aop.service.PersonServiceImpl.addUser(String)) |
方法参数必须为字符串 |
execution(* aop.service..*.*(..)) |
万能配置 |
4.@annotaion应用于方法级别,实现细粒度的控制:
@annotation("com.jt.common.anno.RequestLog")) |
指定一个需要实现增强功能的方法 |
2.切面通知
1.在AOP编程中有五种类型的通知:
- 前置通知 (@Before) 方法执行之前执行
- 返回通知 (@AfterReturning) 方法return之后执行
- 异常通知 (@AfterThrowing) 方法出现异常之后执行
- 后置通知 (@After) : 又称之为最终通知(finally)
- 环绕通知 (@Around)
2.五种类型可以通过try...catch...finally...来模拟
A.try{ @Before核心业务 @AfterReturning }catch(Exception e){ ... @AfterThrowing }finally{ @After }
B.以上四个一起使用时可以直接用@Around通知替换
3.xml方式通知配置
A.切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:before method="beforeMethod" pointcut-ref="pc"/>
<aop:after method="afterMethod" pointcut-ref="pc"/>
<aop:after-returning method="returnMethod"
pointcut-ref="pc"/>
<aop:after-throwing method="throwMethod"
pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
B.切入点及环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
4.注解方式通知配置
A.切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
@Aspect
@Service
public class LogAspect {
@Pointcut("bean(orderServiceImpl)")
public void doLog(){}
@Before("doLog()")
public void doBefore(){
System.out.println("log before");
}
@After("doLog()")
public void doAfter(){
System.out.println("log after");
}
/**核心业务正常结束时执行
* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doLog()")
public void doAfterReturning(){
System.out.println("log doAfterReturning");
}
/**核心业务出现异常时执行
说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doLog()")
public void doAfterThrowing(){
System.out.println("log doAfterThrowing");
}
}
B.切入点及环绕通知
@Component
@Aspect
public class TxManager {
@Pointcut("execution(com.company.spring.service..*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
try{
System.out.println("事务开始");
Object result = joinPoint.proceed();//执行目标方法
System.out.println("提交事务");
return result;
}catch(Exception e){
System.out.println("回滚事务");
throw e;
}finally{
System.out.println("释放资源");
}
}
}
5.切面执行顺序配置
实际项目中可能会有多个切面,切面之间的执行可能需要一定的顺序。
A.xml方式配置执行顺序
<aop:config>
<aop:pointcut id="pc"
expression="execution(*
com.company.spring.service..*.*(..))"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
<aop:aspect ref="txManager" order="2">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
B.注解方式配置执行顺序:借助@Order注解
@Order(1)
@Aspect
@Component
public class TxManager {
@Pointcut("execution(* com.company.spring.service..*.(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
System.out.println("事务开始");
Object result = joinPoint.proceed();
System.out.println("事务结束");
return result;
}
}
@Order(2)
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.company.spring.service..*.(..))")
public void pointCut() {}
@Before("pointCut()")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@After("pointCut()")
public void afterMethod() {
System.out.println("afterMethod");
}
}
五、AOP相关知识
- OCP原则(开闭原则):允许扩展,不允许修改
- 单一职责原则(一个类或者接口的职责不要太多)
- Spring AOP配置方式xml和注解
- Spring AOP通知基本类型
- Spring AOP为Bean对象创建代理对象代码实现
- 用JDK代理方式为某类创建代理对象
- 用CGLIB代理方式为某类创建代理对象(依赖于cglib库)