AOP简介
这里回答以下几个问题:
- 为什么需要AOP?
AOP(Aspect-Oriented Programming)是面向切面编程的一种方式,面向对象的一种补充。实际的应用有:日志,事务,统一异常处理,锁,权限控制等等。 - AOP的底层实现?
AOP底层实现有2种,JDK基于接口的实现通过实现类的方式对代码增强,CGlib对于类的实现通过生成子类的方式对代码增强。 - AOP包含哪些核心概念?
● 切面Aspect: 需要增强的某一个方法。
● 通知Advice: 实现增强的代码逻辑,有前置,后置,环绕,异常,正常行为后的通知等。
● 切点Pointcut: 用来需要增强的切面。
Spring AOP应用
方法出入口日志统一输出
import com.github.yn.framework.common.HandleResult;
import com.github.yn.framework.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
/**
* 日志切面逻辑处理
*
* @author chenjn
* @version LogAspect.java, v 0.1 2022-11-25 9:40 PM chenjn
*/
@Slf4j
@Aspect
@Order(2)
public class LogAspect {
public LogAspect() {
}
// -- 声明切点
@Pointcut("@within(com.github.yn.framework.common.annotation.YnLog) && execution(public * *(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {
HandleResult<Object> handleResult = LogTemplate.get(joinPoint::proceed, joinPoint.getArgs());
if (handleResult.isFail()) {
throw BusinessException.of(handleResult.getMessage());
}
return handleResult.getData();
}
}
// --- YnLog
/**
* 日志标记
*
* @author chenjn
* @Classname YnAlias
* @Date 2020/10/16
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YnLog {
}
// ----- LogTemplate
import com.alibaba.fastjson.JSON;
import com.github.yn.framework.common.HandleResult;
import com.github.yn.framework.common.SupplierThrow;
import com.github.yn.framework.common.exception.BaseException;
import com.github.yn.framework.common.exception.BusinessException;
import com.github.yn.framework.common.exception.E;
import com.github.yn.framework.common.exception.EMessage;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Function;
/**
*
* 日志模板
*
* @author chenjn
* @version LogTemplate.java, v 0.1 2022-11-26 8:38 AM chenjn
*/
@Slf4j
public class LogTemplate {
public final static <T,R> HandleResult<R> get(SupplierThrow<R> supplier, T... param) {
long startTime = System.currentTimeMillis();
long endTime = startTime;
boolean fail = false;
R result = null;
E err = null;
try {
log.info("LogTemplate[]exec start param:{}", JSON.toJSONString(param));
result = supplier.get();
} catch (BaseException e) {
log.warn("LogTemplate[]exec base fail,e",e);
err = e.getELatest();
fail = true;
} catch (Throwable e) {
log.warn("LogTemplate[]exec unknow fail,e",e);
err = EMessage.SYSTEM_WARNING;
fail = true;
} finally {
endTime = System.currentTimeMillis();
}
HandleResult handleResult = fail ? HandleResult.fail(err)
: HandleResult.ok(result);
log.info("LogTemplate[]exec end handleResult:{},COST:{} ms",JSON.toJSONString(handleResult),endTime - startTime);
return handleResult;
}
}
Spring AOP方案设计
代理对象创建入口
代理对象是在创建Spring Bean的时候,有个初始化Bean的过程,在那里创建代理对象来顶替Spring bean。具体实现的类是AnnotationAwareAspectJAutoProxyCreator。
上图是AnnotationAwareAspectJAutoProxyCreator的一个关系,其中比较重要的另外两个就是AbstractAutoProxyCreator实现了postProcessAfterInitialization具体实现逻辑,BeanPostProcessor定义了postProcessAfterInitialization方法。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 返回一个代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
代理对象创建
首先,声明AopProxyFactory,主要用来生成代理对象总的入口。
其次,通过声明AopProxy,来各自实现JDK动态代理,以及CGlib动态代理2种方式。
小结
首先,是对AOP的一个简介,他是对面向切面编程,是对面向对象的一个补充。他有切面,切点,通知等核心概念。接着是一个日志输入输出的应用实现,最后是SpringAOP逻辑的实现,比如代理对象创建入口,代理对象创建的方式等。
补充说明
- 如何看源码?
● 在postProcessAfterInitialization打一个断点,在本地启动的时候,看下调用栈等。
● 看下接口定义了要做的事情。然后看下有哪些不同实现。 - 通知执行顺序
● 如果多个通知命中同一个切面,那么这个切面执行的时候,会有多个通知执行先后顺序的问题。Spring 生成通知数组的时候,会排序。