小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本次我们一起来聊一下 Spring Aop 的原理。本文主要是通过一个简单的 Aop 案例,然后结合 Spring 的源码进行分析, Spring Aop 介入时机; Spring Aop 初始化等。 环境介绍:
- JDK 17
- Spring 6.0.0-SNAPSHOT
案例介绍
我们在业务中可能用到 Redis 作为分布式锁,如果我们手动去 lock, unLock。 这样做的缺点就是代码量比较大,结合我们当前的 Aop 原理背景, 我们可以对我们业务开发中的 Lock 做一个简单的封装。
案例代码
代码如下所示:
@Component
@Aspect
public class DistributedLockAspect {
private static final Logger logger = LoggerFactory.getLogger(DistributedLockAspect.class);
@Pointcut("@annotation(com.summer.test.distributedlock.DistributedLock)")
public void distributedLockPointCut() {
// distributedLockPointCut
}
@Around("distributedLockPointCut()")
public Object distributedLockAround(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
DistributedLock ub = method.getAnnotation(DistributedLock.class);
String lockKey = lockKey(ub.key(), pjp);
long timeOut = ub.timeOut();
logger.debug("Start Redis Distributed Lock, Lock key:{}", lockKey);
try {
// todo redis lock
Object result = pjp.proceed();
logger.debug("End Redis Distributed Lock, Lock key:{}", lockKey);
return result;
} catch (Throwable ex) {
logger.warn("Error Redis Distributed Lock, Lock key:{}", lockKey);
throw ex;
}
}
private String lockKey(String key, ProceedingJoinPoint joinPoint) {
SpelExpressionParser parser = new SpelExpressionParser();
DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
// 通过joinPoint获取被注解方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
String[] paramNames = nameDiscoverer.getParameterNames(method);
// 解析过后的Spring表达式对象
Expression expression = parser.parseExpression(key);
// spring的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 通过joinPoint获取被注解方法的形参
Object[] args = joinPoint.getArgs();
// 给上下文赋值
if (paramNames != null) {
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
}
// 表达式从上下文中计算出实际参数值
return Objects.requireNonNull(expression.getValue(context)).toString();
}
}
案例总结
在上述代码中我有用到 @Aspect
、@Pointcut
、 @Around
这些概念我先不展开介绍。我们先看看如何使用。具体的概念介绍可以参考 Spring 官网, 或者我之前的文章 Spring Aop 详解 。
@DistributedLock(key = "'user_service:user_opt:' + #userModel.name")
public void save(UserModel userModel) {
// todo
}
通过 @DistributedLock
降低了代码的复杂度,而且这样做也利于我们公共组件的封装。
Spring Aop 介入时机
方法入口
Spring Aop 是在 Spring 依赖注入完成之后,初始化 Bean 方法AbstractAutowireCapableBeanFactory.java#initializeBean�
中执行的。我们先来看看具体的代码。
// bean 初始化方法
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 初始化之前
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 执行初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 初始化之后,这里会判断是否需要执行 Aop 代理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
核心处理类
核心处理类是 AbstractAutoProxyCreator
它是实现了 BeanPostProcessor
我们可以先看看它的 postProcessAfterInitialization
方法
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;
}
到了这里我们就可以很容易知道 wrapIfNecessary
就是核心的逻辑处理。
Spring Aop 初始化
我们先来看看 wrapIfNecessary
的具体实现:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 判断是否需要进行 Aop
// Object[] DO_NOT_PROXY = null;
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
// new SingletonTargetSource(bean) 被代理对象,就是 Bean
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
看到上面的代码我们先梳理一下 这个代理的过程:
- 首先获取 advice
- 然后判断是否需要代理
- 然后执行代理
获取 Advice
获取的入口方法是 getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 寻找匹配的 advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
然后通过 findEligibleAdvisors
寻找匹配的 advisor
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到所有的 Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 筛选
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
在 findEligibleAdvisors
中会有三个步骤:
- 找到所有的 Advisor
- 筛选符合条件的 Advisor
- 对结果进行排序
我们先来看看 findCandidateAdvisors
是如何查找所有的 Advisor
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
// 核心是调用
// BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
// 获取所有的 advisorNames
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
// advisor 的所有 bean 查找
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
// 获取所有的 advisors 实例
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
然后接下来就是对 Advice 进行筛选
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
// 核心是调用 AopUtils.findAdvisorsThatCanApply 方法
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
最后在对这些 Advisor 进行排序
生成代理对象
获取到当前 bean 符合条件的 Advice 过后,Spring 就会进行 Bean 代理对象的创建
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
核心就是 createProxy
方法
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
else {
// No proxyTargetClass flag enforced, let's apply our default checks...
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 构造 advisor, 在里面会进行二次匹配
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 添加通知
proxyFactory.addAdvisors(advisors);
// 被代理对象
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
// 获取代理对象
return proxyFactory.getProxy(classLoader);
}
选择动态代理
Spring 在进行动态代理的时候会进行选择 GCLIB 或者 JDK 动态代理, 具体在 DefaultAopProxyFactory
中实现的逻辑。 简单的理解为如果 bean 实现了接口就采用 JDK 代理, 如果没有实现就采用 GCLIB 代理,但是会有一些的参数可以特殊控制如: proxyTargetClass,optimize 等 。 代码如下:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果实现接口,采用 JDK 代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否者采用 Cglib 代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 采用 JDK 代理
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
最后通过动态的方式完成代理对象的创建。
参考文档
- gitee.com/zhengsh/spr…
- [spring.io](● spring.io)