AOP:面向切面编程。简单说,就是把一些业务逻辑中的相同的代码抽取到一个独立的模块中,让业务逻辑更加清爽。
横向抽取
具体来说,假如我现在要crud写一堆业务,可是如何业务代码前后前后进行打印日志和参数的校验呢?
我们可以把日志记录
和数据校验
可重用的功能模块分离出来,然后在程序的执行的合适的地方动态地植入这些代码并执行。这样就简化了代码的书写。
编辑
AOP应用示例
业务逻辑代码中没有参和通用逻辑的代码,业务模块更简洁,只包含核心业务代码。实现了业务逻辑和通用逻辑的代码分离,便于维护和升级,降低了业务逻辑和通用逻辑的耦合性。
AOP 可以将遍布应用各处的功能分离出来形成可重用的组件。在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能。从而实现对业务逻辑的隔离,提高代码的模块化能力。
编辑
Java语言执行过程
AOP 的核心其实就是动态代理,如果是实现了接口的话就会使用 JDK 动态代理,否则使用 CGLIB 代理,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。
AOP有哪些核心概念?
AOP有哪些环绕方式?
AOP 一般有 5 种环绕方式:
编辑
环绕方式
多个切面的情况下,可以通过 @Order 指定先后顺序,数字越小,优先级越高。
PS:这道题老三的同事面试候选人的时候问到了,候选人说了一堆AOP原理,同事就势来一句,你能现场写一下AOP的应用吗?结果——场面一度很尴尬。虽然我对面试写这种百度就能出来的东西持保留意见,但是还是加上了这一问,毕竟招人最后都是要撸代码的。
这里给出一个小例子,SpringBoot项目中,利用AOP打印接口的入参和出参日志,以及执行时间,还是比较快捷的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WebLog {
}
@Aspect
@Component
public class WebLogAspect {
private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
/**
* 以自定义 @WebLog 注解为切点
**/
@Pointcut("@annotation(cn.fighter3.spring.aop_demo.WebLog)")
public void webLog() {}
/**
* 在切点之前织入
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
logger.info("========================================== Start ==========================================");
// 打印请求 url
logger.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
logger.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
logger.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
logger.info("Request Args : {}",new ObjectMapper().writeValueAsString(joinPoint.getArgs()));
}
/**
* 在切点之后织入
* @throws Throwable
*/
@After("webLog()")
public void doAfter() throws Throwable {
// 结束后打个分隔线,方便查看
logger.info("=========================================== End ===========================================");
}
/**
* 环绕
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//开始时间
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 打印出参
logger.info("Response Args : {}", new ObjectMapper().writeValueAsString(result));
// 执行耗时
logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
return result;
}
}
@GetMapping("/hello")
@WebLog(desc = "这是一个欢迎接口")
public String hello(String name){
return "Hello "+name;
}
编辑
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。