我的编程空间,编程开发者的网络收藏夹
学习永远不晚

spring源码阅读--aop实现原理讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

spring源码阅读--aop实现原理讲解

aop实现原理简介

首先我们都知道aop的基本原理就是动态代理思想,在设计模式之代理模式中有介绍过这两种动态代理的使用与基本原理,再次不再叙述。

这里分析的是,在spring中是如何基于动态代理的思想实现aop的。为了方便了解接下来的源码分析,这里简单化了一个流程图分析aop的基本实现思想。

在这里插入图片描述

so,基于上面的流程,一步步分析spring源码中的aop实现方式。

采用一个简单的aop例子,利用基于注解配置方式的切面配置,分析一个简单的Before AOP例子。在spring boot下运行以下简单例子。

AOP的advisor和advice配置。


@Component
@Aspect
public class AopConfig {
    @Pointcut("execution(* com.garine.debug.testcase.model.AopObject..*(..))")
    public void mypoint(){
        //切面定义
    }
    @Before("mypoint()")
    public void doAround() throws Throwable {
        System.out.println("before logic");
    }
}

AopObject,被代理拦截对象。


@Component
public class AopObject {
    public void aoped(){
        System.out.println("logic");
    }
}

代理实现的处理器(BeanPostProcessor)

首先是第一步内容,对我们在AopConfig中的AOP配置内容进行解析并且保存到BeanFactory中,这个过程就是解析保存切面信息。

代理实现的源头–AnnotationAwareAspectJAutoProxyCreator

经过一遍的代码跟踪,我了解到注解方式的AOP配置,都离不开一个类–AnnotationAwareAspectJAutoProxyCreator,这个类继承了BeanPostProcessor接口,我们都知道BeanPostProcessor的实现类有多个执行处理节点,其中一个执行节点就是在Bean实例化之后。也就是在这个时机AnnotationAwareAspectJAutoProxyCreator拦截bean的初始化过程,根据提前解析得到的切面信息,对bean的方法进行尝试适配,如果有匹配则需要进行代理创建。

这里先分析的就是AnnotationAwareAspectJAutoProxyCreator,在bean实例化第一次查询所有切面信息时,就会解析保存Aop的信息到实例中,跟踪以下代码。

AbstractApplicationContext#refresh (上下文初始化主干方法)

AbstractApplicationContext#registerBeanPostProcessors (执行实例化并保存所有实现BeanPostProcessor接口的类)

按照上面的逻辑,registerBeanPostProcessors 会比一般的bean实例化逻辑要早执行,因此我们接下来只需要分析AnnotationAwareAspectJAutoProxyCreator的初始化过程。

AnnotationAwareAspectJAutoProxyCreator的继承结构

在这里插入图片描述

通过上图可以知道,AnnotationAwareAspectJAutoProxyCreator是继承了BeanfactoryAware接口,所以在实例化时,会执行setFactory方法。而所有切面信息解析的执行者BeanFactoryAspectJAdvisorsBuilderAdapter初始化的时机也是在setFactory方法。

跟踪代码如下。

AbstractAdvisorAutoProxyCreator#setBeanFactory

AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

在这个方法里面会新建一个BeanFactoryAspectJAdvisorsBuilderAdapter,这个对象会根据Beanfactory内的aop配置信息,进行解析保存。但是需要注意,此时虽然新建了BeanFactoryAspectJAdvisorsBuilderAdapter对象.但是此时还不会马上解析aop配置,需要在第一次个普通bean实例化时才执行解析aop配置。解析的方法就是

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors,会在初次执行AnnotationAwareAspectJAutoProxyCreator调用postProcessBeforeInitialization时开始执行。


protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   super.initBeanFactory(beanFactory);
    //aspectJAdvisorsBuilder#buildAspectJAdvisors就是解析配置入口
   this.aspectJAdvisorsBuilder =
         new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}

代理对象(Proxy)的创建

解析并缓存切面

上面提到继承结构图中,AnnotationAwareAspectJAutoProxyCreator是实现了InstantiationAwareBeanPostProcessor接口的,InstantiationAwareBeanPostProcessor接口定义的postProcessBeforeInitialization方法是一个可以对已经注入依赖属性的bean对象实例进行编辑操作的接口,会在

AbstractAutowireCapableBeanFactory#doCreateBean

AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation

方法中执行InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,初次初始化缓存切面信息的话就是在这个方法里面。。

具体的调用链如上所示。这里的postProcessBeforeInstantiation方法实际上是AnnotationAwareAspectJAutoProxyCreator的实例进行调用,AnnotationAwareAspectJAutoProxyCreator实现InstantiationAwareBeanPostProcessor接口。

下面进入InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization方法分析代码。

AbstractAutoProxyCreator#postProcessBeforeInstantiation

AspectJAwareAdvisorAutoProxyCreator#shouldSkip (关键代码)

进入如下代码AbstractAutoProxyCreator,这个实例也就是之前一开始初始化的AnnotationAwareAspectJAutoProxyCreator实例,进入实例的shouldSkip 方法


	@Override
	protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		// TODO: Consider optimization by caching the list of the aspect names
        //预先解析缓存切面信息
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true;
				}
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

方法findCandidateAdvisors代码如下,这里是预先解析缓存所有切面advisor信息,注意这一步操作是在AbstractAutoProxyCreator#postProcessBeforeInitialization处理,也就是开头提到的切面解析操作,解析完成就进行缓存。


@Override
protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
    //这里就是前面提到的BeanFactoryAspectJAdvisorsBuilder解析所有切面信息的调用点
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}

然后继续在这里先提前看一下是如何解析aop配置的。跟踪BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors


public List<Advisor> buildAspectJAdvisors() {
   List<String> aspectNames = null;
   synchronized (this) {
      aspectNames = this.aspectBeanNames;
      if (aspectNames == null) {
         List<Advisor> advisors = new LinkedList<Advisor>();
         aspectNames = new LinkedList<String>();
          //查询出Beanfactory中所有已经注册的BeanName
         String[] beanNames =
               BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
         for (String beanName : beanNames) {
            if (!isEligibleBean(beanName)) {
               continue;
            }
            // We must be careful not to instantiate beans eagerly as in this
            // case they would be cached by the Spring container but would not
            // have been weaved
            Class<?> beanType = this.beanFactory.getType(beanName);
            if (beanType == null) {
               continue;
            }
             //判断Bean是否是切面Bean,isAspect方法判断[标注1]
            if (this.advisorFactory.isAspect(beanType)) {
               aspectNames.add(beanName);
               AspectMetadata amd = new AspectMetadata(beanType, beanName);
               if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                  MetadataAwareAspectInstanceFactory factory =
                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                   //解析aop class的配置,包返回Advisor对象[标注2]
                  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                  if (this.beanFactory.isSingleton(beanName)) {
                     this.advisorsCache.put(beanName, classAdvisors);
                  }
                  else {
                     this.aspectFactoryCache.put(beanName, factory);
                  }
                  advisors.addAll(classAdvisors);
               }
               else {
                  // Per target or per this.
                  if (this.beanFactory.isSingleton(beanName)) {
                     throw new IllegalArgumentException("Bean with name '" + beanName +
                           "' is a singleton, but aspect instantiation model is not singleton");
                  }
                  MetadataAwareAspectInstanceFactory factory =
                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                  this.aspectFactoryCache.put(beanName, factory);
                  advisors.addAll(this.advisorFactory.getAdvisors(factory));
               }
            }
         }
         this.aspectBeanNames = aspectNames;
         return advisors;
      }
   }
   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   List<Advisor> advisors = new LinkedList<Advisor>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

**[标注1]如何判断类是否是aop切面配置类? **

通过以下代码。


@Override
public boolean isAspect(Class<?> clazz) {
   return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
    //包含@Aspect注解
   return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}

[标注2]如何解析为Advisor对象?

ReflectiveAspectJAdvisorFactory#getAdvisors 遍历所有没被@PointCut注解标注的方法,也就是遍历切面内容方法

ReflectiveAspectJAdvisorFactory#getAdvisor 处理所有没被@PointCut注解标注的方法,候选切面内容方法

代码如下。


@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {
   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   //解析判断候选方法是否有@Before,@After,@Around等注解,如果有,就继续执行新建Advisor对象。
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
//创建advisor
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

最终循环解析,@Before,@After,@Around等标注的方法都会新建一个Advisor对象。新建的Advisor对象都保存在BeanFactoryAspectJAdvisorsBuilder#advisorsCache中,当AnnotationAwareAspectJAutoProxyCreator拦截bean的创建过程时,从这里面适配是否有切面可用。

这里解析得到的Advisor,大概有以下信息。下面的信息中,并没有对@PointCut注解做处理,pointCut属性只得出一个"mypoint()",此时还不知道Advisor实际对应的拦截表达式。

在这里插入图片描述

拦截表达式还是空的,会在AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInstantiation第一次执行时解析拦截表达式。

在这里插入图片描述

适配切面

在AbstractAutoProxyCreator#postProcessAfterInitialization执行时,找到上面AbstractAutoProxyCreator#postProcessBeforeInitialization缓存的所有的切面信息,之后是如何进行切面适配,从而决定是否需要进行代理对象的创建呢?

在调用AbstractAutoProxyCreator#postProcessAfterInitialization方法时,进行切面适配,并且会根据适配创建代理对象。根据以下调用链。

AbstractAutoProxyCreator#postProcessAfterInitialization

AbstractAutoProxyCreator#wrapIfNecessary


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   if (beanName != null && 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);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
       //创建代理对象
      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;
}

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

AbstractAdvisorAutoProxyCreator#findEligibleAdvisors


protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //从缓存取出所有切面信息
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //根据advisor信息中的表达式进行方法对class的匹配
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

此时如果是第一次执行适配方法findAdvisorsThatCanApply的话,candidateAdvisors中的拦截表达式还是空的,需要进行表达式获取,也就是@Pointcut的value。spring的操作的在第一次执行findAdvisorsThatCanApply时解析获取拦截表达式的值,获得拦截表达式值之后就跟当前class的方法进行匹配看是否需要进行代理。

继续往下跟踪代码

AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)

AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)

AspectJExpressionPointcut#getClassFilter

AspectJExpressionPointcut#checkReadyToMatch


private void checkReadyToMatch() {
   if (getExpression() == null) {
      throw new IllegalStateException("Must set property 'expression' before attempting to match");
   }
   if (this.pointcutExpression == null) {
      this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
            ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
            ClassUtils.getDefaultClassLoader());
       //解析得到拦截表达式,例如根据@Before的value来关联查询出对应的表达式
      this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
   }
}

最终解析完之后,advisor中的表达式信息结构如下图。包含在pointcut属性中,匹配时就根据pointcutExpression循环进行匹配class的方法。有兴趣的可以继续调试看看是如何实现匹配表达式的。

在这里插入图片描述

##  创建代理对象

如果在上面的匹配切面过程中,发现适配的切面,那就需要进行代理对象的创建了。

我们回到上面的AbstractAutoProxyCreator#wrapIfNecessary,主要看代码如下。


  // Create proxy if we have advice.
    //查找匹配切面
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
       //创建代理对象  
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

所以,继续看

AbstractAutoProxyCreator#createProxy

的创建代理对象方法。设置ProxyFactory创建Proxy需要的一切信息。


protected Object createProxy(
      Class<?> beanClass, String beanName, 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()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
    //设置拦截切面
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   for (Advisor advisor : advisors) {
      proxyFactory.addAdvisor(advisor);
   }
    //设置被代理对象
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
//创建代理对象
   return proxyFactory.getProxy(getProxyClassLoader());
}

下面看ProxyFactory是如何创建代理对象,继续跟踪proxyFactory.getProxy(getProxyClassLoader());


public Object getProxy(ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}

createAopProxy()作用是根据class的种类判断采用的代理方式,看如下实现

DefaultAopProxyFactory#createAopProxy


@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (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.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            //采用jdk动态代理必须基于接口
			return new JdkDynamicAopProxy(config);
		}
        //基于cglib实现代理不需要接口
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

所以在当前调试的例子中,使用cglib代理。所以执行如下代理。


@Override
public Object getProxy(ClassLoader classLoader) {
     //。。。。。。
      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
	//获取拦截回调函数
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);
      // Generate the proxy class and create a proxy instance.
    //返回一个cglib代理对象
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException ex) {
       //、、、、、、
   }
}

getCallbacks(rootClass);在这个获取回调函数的方法中,普通的aop采用的回调函数是如下的方式。


// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

cglib 的aop回调函数如下。


public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Class<?> targetClass = null;
   Object target = null;
   try {
       //这里注入的advised就是之前创建的ProxyFactory对象
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
      target = getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
       //根据切面信息创建切面内容调用链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
          //创建一个方法调用对象,具体调用实现没分析,Before逻辑大概是先调用切面,在反射调用目标方法
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

spring源码阅读--aop实现原理讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何进行Spring源码剖析AOP实现原理

今天就跟大家聊聊有关如何进行Spring源码剖析AOP实现原理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了
2023-06-02

Java Spring AOP源码解析中的事务实现原理是什么

这篇文章将为大家详细讲解有关Java Spring AOP源码解析中的事务实现原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。不用Spring管理事务?让我们先来看一下不用sprin
2023-06-22

Spring基于Aop实现事务管理流程详细讲解

这篇文章主要介绍了Spring基于Aop实现事务管理流程,事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性,感兴趣想要详细了解可以参考下文
2023-05-20

《Spring Boot源码解读与原理分析》书籍推荐

Spring Boot 1.0.0 早在2014年就已经发布,只不过到了提倡“降本增效”的今天,Spring Boot才引起了越来越多企业的关注。Spring Boot是目前Java EE开发中颇受欢迎的框架之一。依托于底层Spring F
2023-08-17

React Context源码实现原理详解

这篇文章主要为大家介绍了React Context源码实现原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

async-validator实现原理源码解析

这篇文章主要为大家介绍了async-validator实现原理源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-11

goslice扩容实现原理源码解析

这篇文章主要为大家介绍了goslice扩容实现原理源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-03

Android刮刮卡实现原理与代码讲解

实现刮刮卡我们可以Get到哪些技能? * 圆形圆角图片的实现原理 * 双缓冲技术绘图 * Bitmap获取像素值数据 * 获取绘制文本的长宽 * 自定义View的掌握 * 获取屏幕密度 * TypeValue.applyDemension
2022-06-06

OpenMPtaskconstruct实现原理及源码示例解析

这篇文章主要为大家介绍了OpenMPtaskconstruct实现原理及源码示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-06

Web端扫码登录的原理和实现讲解

这篇文章主要介绍了Web端扫码登录的原理和实现,扫码登录相比较传统的密码登录更加快捷和安全,今天我们就来讲讲原理与实现,主要分为几种不同的解决方案,需要的朋友可以参考下
2023-03-23

GoResiliency库中timeout实现原理及源码解析

Go-Resiliency库中的timeout是一种基于协程的超时机制,通过创建协程来执行任务并设置超时时间,若任务执行时间超时则中止协程并返回错误,需要详细了解可以参考下文
2023-05-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录