什么是OOP?
面向对象编程,也称为OOP(即Object Oriented Programming),通过的是继承、封装和多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合,最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的。
什么是AOP?
面向切面编程,也称为AOP(即Aspect Oriented Programming),指的是将一定的切面逻辑按照一定的方式编织到指定的业务模块中,从而将这些业务模块的调用包裹起来。OOP从纵向上区分出一个个的类来,而AOP则从横向上向对象中加入特定的代码。AOP采用"横切"的技术,解剖开封装的对象内部,将影响了多个类的公共行为封装到一个可重用模块。将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP相关术语
1.切面(Aspect)
指定了当前切面逻辑所要包裹的业务模块的范围大小,定义了切面的全部内容——它是什么,在何时和何处完成其功能。
2.通知(Advice)
通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。
- @Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;
- @AfterReturning:该注解标注的方法在业务模块代码执行之后执行;
- @AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;
- @After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常;
- @Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;
- @DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
3.连接点(Joint point)
是指在程序执行期间的一个点,比如某个方法的执行或者是某个异常的处理。在Spring AOP中,一个连接点往往代表的是一个方法执行
。
4.切入点(Pointcut)
是指匹配连接点的一个断言。通知是和一个切入点表达式关联的,并且在任何被切入点匹配的连接点上运行(举例,使用特定的名字执行某个方法)。AOP的核心就是切入点表达式匹配连接点的思想。
切点表达式
- execution:Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的,execution表达式的语法:
-
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern:方法的可见性,如public,protected;
- ret-type-pattern:方法的返回值类型,如int,void等;
- declaring-type-pattern:方法所在类的全路径名,如com.example.demo.controller;
- name-pattern:方法名类型,如EatWMZ();
- param-pattern:方法的参数类型,如java.lang.String;
- throws-pattern:方法抛出的异常类型,如java.lang.Exception;
如:
execution(public * com.example.demo.controller.AopController.*(..)))
2.within:within表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。
如:
within( com.example.demo.controller.AopController)
表示匹配com.example.demo.controller.AopController 下的所有方法
5.
引入(Introduction)
代表了对一个类型额外的方法或者属性的声明。Spring AOP允许引入新接口到任何被通知对象(以及一个对应实现)。比如,可以使用一个引入去使一个bean实现IsModified
接口,从而简化缓存机制。(在AspectJ社区中,一个引入也称为一个inter-type declaration类型间声明)
6.目标对象(Target object)
是指被一个或多个切面通知的那个对象。也指被通知对象("advised object"
),由于Spring AOP是通过运行时代理事项的,这个目标对象往往是一个代理对象
。
7.AOP 代理(AOP proxy)
是指通过AOP框架创建的对象,用来实现切面合约的(执行通知方法等等)。在Spring框架中,一个AOP代理是一个JDK动态代理
或者是一个CGLIB代理
。
8.织入(Weaving)
将切面和其他应用类型或者对象连接起来,创建一个被通知对象。这些可以在编译时(如使用AspectJ编译器)、加载时或者运行时完成。Spring AOP,比如其他纯Java AOP框架一般是在运行时
完成织入。
SpringBoot中集成AOP
1.pom.xml引入
maven依赖添加如下
<!--引入SpringBoot的Web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
2.定义切面类
@Aspect 注解 使之成为切面类
@Component 注解 把切面类加入到IOC容器中
package com.example.demo.aop;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 饿鸟嘛配送
**/
@Aspect
@Component
public class EatAop {
/**
*@description 定义切入点,切入点为com.example.demo.controller.AopController中的所有函数
*@description 通过@Pointcut注解声明频繁使用的切点表达式
* */
@Pointcut("execution(public * com.example.demo.controller.AopController.*(..)))")
public void EatAop()
{
}
/**
* @description 在连接点执行之前执行的通知
*/
@Before("EatAop()")
public void doBeforeEat(){
System.out.println("饿鸟嘛配送员登录接单系统为接单做准备!");
}
/**
* @description 在连接点执行之后执行的通知(返回通知和异常通知的异常)
*/
@After("EatAop()")
public void doAfterEat(){
System.out.println("饿鸟嘛配送员为今天的外卖单量感到异常吃惊!");
}
/**
* @description 在连接点执行之后执行的通知(返回通知)
*/
@AfterReturning("EatAop()")
public void doAfterReturningEat(){
System.out.println("返回的通知:饿鸟嘛配送员接单完毕,并会心一笑百媚生!");
}
/**
* @description 在连接点执行之后执行的通知(异常通知)
*/
@AfterThrowing("EatAop()")
public void doAfterThrowingGame(){
System.out.println("异常通知:服务错误,请联系饿鸟嘛系统管理员!");
}
}
3.新建AopController
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class AopController {
@RequestMapping(value = "/EatWMZ")
public void EatWMZ(){
System.out.println("王麻子点了一份瓜皮香锅");
}
@RequestMapping(value = "/EatZS")
public void EatZS(){
System.out.println("张三点了一份爆浆牛丸");
}
}
访问http://127.0.0.1:8080/api/EatWMZ:
小结
本文首先对AOP进行了简单介绍,然后介绍了切面中的相关术语,举例说明切点表达式其中常用的两种,最后做了简单的demo旨在说明切面逻辑(advice)下各个类型执行的顺序。