1、ApringAop
AOP(Aspect Oriented Programming),即面向切面编程。可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
2、AOP使用场景
AOP用来封装横切关注点,具体可以在下面的场景中使用
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
3、AOP核心概念
(1)、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
(2)、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
(3)、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
(4)、切入点(pointcut)
对连接点进行拦截的定义
(5)、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
(6)、目标对象
代理的目标对象
(7)、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
(8)、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
4、应用之日志
这个里主要对所有方法进行前置处理,后置处理,记录日志,采用注解方式实现日志处理场景。
(1)依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)定义切点切面,详见代码
package cn.king.boot.aop;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class LogAspect {
@Pointcut(value = "execution(* cn.king.boot.controller.TestController.*(..))")
public void log() {
System.err.println("切点");
}
@Before(value = "log()")
public void beforeLog(JoinPoint joinPoint) {
System.err.println("----------------------------------------");
System.err.println("前置通知=======》》》");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
StringBuffer url = request.getRequestURL();
//请求方法
String method = request.getMethod();
//ip地址
String ip = request.getRemoteAddr();
//
String className = joinPoint.getTarget().getClass().getName();
//方法
String methodName = joinPoint.getSignature().getName();
//参数
Object[] args = joinPoint.getArgs();
//切点
String kind = joinPoint.getKind();
//切面
SourceLocation sourceLocation = joinPoint.getSourceLocation();
StaticPart staticPart = joinPoint.getStaticPart();
System.err.println("url:"+url);
System.err.println("请求方式:"+method);
System.err.println("ip:"+ip);
System.err.println("类路径:"+className);
System.err.println("方法名:"+methodName);
System.err.println("参数:"+Arrays.toString(args));
System.err.println("切点方法名:"+kind);
System.err.println("本地资源:"+sourceLocation);
System.err.println("切面::"+staticPart);
System.err.println("------------------------------------------");
}
@AfterReturning(value = "log()", returning = "obj")
public void afterReturningLog(JoinPoint joinPoint,Object obj) {
System.err.println("后置通知=======》》》");
String returnResult = obj.toString();
System.err.println("返回结果:"+returnResult);
}
}
(3)控制层
@RequestMapping(value = "/aop")
public JSONObject json(String a,String b) {
System.err.println("控制层==="+a+"---"+b);
JSONObject json = jsonService.json(a, b);
return json;
}
(4)Console
2019-04-23 22:45:12.575 INFO 21116 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-04-23 22:45:12.778 INFO 21116 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-04-23 22:45:13.847 INFO 21116 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2019-04-23 22:45:14.003 INFO 21116 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2019-04-23 22:45:14.017 INFO 21116 --- [ main] cn.king.boot.SpringFirstApplication : Started SpringFirstApplication in 10.8 seconds (JVM running for 11.686)
------__-----
2019-04-23 22:45:20.823 INFO 21116 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-04-23 22:45:20.823 INFO 21116 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2019-04-23 22:45:20.871 INFO 21116 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 47 ms
----------------------------------------
前置通知=======》》》
url:http://127.0.0.1:8080/aop
请求方式:POST
ip:127.0.0.1
类路径:cn.king.boot.controller.TestController
方法名:json
参数:[a, b]
切点方法名:method-execution
本地资源:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@cf2c893
切面::execution(JSONObject cn.king.boot.controller.TestController.json(String,String))
------------------------------------------
控制层===a---b
a:a b:b
后置通知=======》》》
返回结果:{"a":"a","b":"b"}