记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了。
所谓AOP也就是面向切面编程,能够让我们在不影响原有业务功能的前提下,
横切
扩展新的功能。这里面有一个比较显眼的词我们需要注意一下,横切,它是基于横切面对程序进行扩展的。
在Spring的AOP中有很多的术语,而且容易混淆,大家一定要先搞清楚这几个概念:
-
连接点(Joinpoint)
:在程序执行过程中某个特定的点,比如类初始化前、类初始化后,方法调用前,方法调用后
;
-
切点(
Pointcut
)
:所谓切点就是你所切取的类中的方法,比如你横切的这个类中有两个方法,那么这两个方法都是连接点,对这两个方法的定位就称之为切点;
-
增强(
Advice
)
:增强是织入到连接点上的一段程序,另外它还拥有连接点的相关信息;
-
目标对象(Target)
:增强逻辑的织入目标类,就是我的增强逻辑植入到什么位置;
-
引介(
Introduction
)
:一种特殊的增强,它可以为类添加一些属性喝方法;
-
织入(
Weaving
)
:织入就是讲增强逻辑添加到目标对象的过程;
-
代理(
Proxy
)
:一个类被AOP织入增强后,就会产生一个结果类,他是融合了原类和增强逻辑的代理类;
-
切面(
Aspect
)
:切面由切点和增强组成,他是横切逻辑定义和连接点定义的组成;
我们这里主要是学习SpringBoot中的一些功能,所以我们这里用的是SpringBoot工程,版本也是最新的2.0.5版本。
创建SpringBoot工程就不说了,我们直接引入Maven的依赖:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.5.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding
>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.6.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration
>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.20version>
<configuration>
<skip>trueskip>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<executions>
executions>
plugin>
plugins>
build>
首先我们来创建一个Controller类:
@RestController
public class LoginController {
@GetMapping(value = "/username")
public String getLoginUserName(String userName, Integer age) {
return userName + " --- " + age;
}
}
创建切面:
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.springbootaop.controller.*.*(..))")
public void loginLog() {
}
@Before("loginLog()")
public void loginBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
System.out.println("请求路径 : " + request.getRequestURL());
System.out.println("请求方式 : " + request.getMethod());
System.out.println("方法名 : " + joinPoint.getSignature().getName());
System.out.println("类路径 : " + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("参数 : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "object", pointcut = "loginLog()")
public void doAfterReturning(Object object) {
System.out.println("方法的返回值 : " + object);
}
@AfterThrowing(throwing = "e",pointcut = "loginLog()")
public void throwsExecute(JoinPoint joinPoint, Exception e) {
System.err.println("方法执行异常 : " + e.getMessage());
}
@After("loginLog()")
public void afterInform() {
System.out.println("后置通知结束");
}
@Around("loginLog()")
public Object surroundInform(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("环绕通知开始...");
try {
Object o = proceedingJoinPoint.proceed();
System.out.println("方法环绕proceed,结果是 :" + o);
return o;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
注解概述:
-
@Apsect
:将当前类标识为一个切面;
-
@Pointcut
:定义切点,这里使用的是条件表达式;
-
@Before
:前置增强,就是在目标方法执行之前执行;
-
@AfterReturning
:后置增强,方法退出时执行;
-
@AfterThrowing
:有异常时该方法执行;
-
@After
:最终增强,无论什么情况都会执行;
-
@Afround
:环绕增强;
测试:
异常测试: