最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析spring?AOP原理及源碼

 更新時(shí)間:2022年04月15日 10:39:21   作者:morris131  
這篇文章主要介紹了spring?AOP原理及源碼分析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒,需要的朋友可以參考下

@EnableAspectJAutoProxy

@EnableAspectJAutoProxy注解用于開(kāi)啟AOP功能,那么這個(gè)注解底層到底做了什么呢?

查看@EnableAspectJAutoProxy的源碼,發(fā)現(xiàn)它使用@Import注解向Spring容器中注入了一個(gè)類型為AspectJAutoProxyRegistrar的Bean:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 注入一個(gè)bean名字為org.springframework.aop.config.internalAutoProxyCreator的AspectJAwareAdvisorAutoProxyCreator
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                // proxyTargetClass為true
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                // exposeProxy為true
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

AspectJAutoProxyRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,而ImportBeanDefinitionRegistrar是spring提供的擴(kuò)展點(diǎn)之一,主要用來(lái)向容器中注入BeanDefinition,spring會(huì)根據(jù)BeanDefinion來(lái)生成Bean。

那么AspectJAutoProxyRegistrar到底向容器中注入了什么BeanDefinion呢?

org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(org.springframework.beans.factory.support.BeanDefinitionRegistry)

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    // AnnotationAwareAspectJAutoProxyCreator
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // 注入AspectJAwareAdvisorAutoProxyCreator
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

從源碼可以發(fā)現(xiàn)AspectJAutoProxyRegistrar向容器中注入了一個(gè)類型為AnnotationAwareAspectJAutoProxyCreator的Bean。

那么AnnotationAwareAspectJAutoProxyCreator又是干什么的呢?

AnnotationAwareAspectJAutoProxyCreator主要實(shí)現(xiàn)了三個(gè)接口(由父類AbstractAutoProxyCreator實(shí)現(xiàn)):

  • 實(shí)現(xiàn)了BeanFactoryAware,內(nèi)部持有BeanFactory的引用。
  • 實(shí)現(xiàn)了SmartInstantiationAwareBeanPostProcessor(InstantiationAwareBeanPostProcessor).postProcessBeforeInstantiation,這個(gè)方法在bean的實(shí)例化(bean創(chuàng)建之前)之前執(zhí)行。
  • 實(shí)現(xiàn)了BeanPostProcessor.postProcessBeforeInitialization(),這個(gè)方法在bean的初始化之前(bean創(chuàng)建之后,屬性被賦值之前)執(zhí)行,BeanPostProcessor.postProcessAfterInitialization()在bean的初始化之后執(zhí)行。

AnnotationAwareAspectJAutoProxyCreator的繼承結(jié)構(gòu):

20220406145405976.png

找切面

org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	/**
	 * @see AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors()
	 */
	// 獲取容器中所有的切面Advisor
	// 這里返回的切面中的方法已經(jīng)是有序的了,先按注解順序(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),再按方法名稱
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	// 獲取所有能夠作用于當(dāng)前Bean上的Advisor
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	/**
	 * @see AspectJAwareAdvisorAutoProxyCreator#extendAdvisors(java.util.List)
	 */
	// 往集合第一個(gè)位置加入了一個(gè)DefaultPointcutAdvisor
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
		/**
		 * @see AspectJAwareAdvisorAutoProxyCreator#sortAdvisors(java.util.List)
		 */
		// 這里是對(duì)切面進(jìn)行排序,例如有@Order注解或者實(shí)現(xiàn)了Ordered接口
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {
	// Add all the Spring advisors found according to superclass rules.
	// 獲取容器中所有的切面Advisor
	List<Advisor> advisors = super.findCandidateAdvisors();
	// Build Advisors for all AspectJ aspects in the bean factory.
	if (this.aspectJAdvisorsBuilder != null) {
		// 這里還需要解析@Aspect注解,生成Advisor
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}

org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
	if (candidateAdvisors.isEmpty()) {
		return candidateAdvisors;
	}
	List<Advisor> eligibleAdvisors = new ArrayList<>();
	// InstantiationModelAwarePointcutAdvisorImpl
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
			// IntroductionAdvisor類型為引入切面,具體類型為DeclareParentsAdvisor
			eligibleAdvisors.add(candidate);
		}
	}
	boolean hasIntroductions = !eligibleAdvisors.isEmpty();
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor) {
			// already processed
			continue;
		}
		// PointCut中的ClassFilter.match 匹配類
		// PointCut中的MethodMatcher.match 匹配方法
		if (canApply(candidate, clazz, hasIntroductions)) {
			// @Aspect,類型為InstantiationModelAwarePointcutAdvisorImpl
			eligibleAdvisors.add(candidate);
		}
	}
	return eligibleAdvisors;
}

代理對(duì)象的創(chuàng)建

代理對(duì)象的創(chuàng)建時(shí)機(jī)位于bean的初始化之后,因?yàn)榇韺?duì)象內(nèi)部還是需要去調(diào)用目標(biāo)對(duì)象的方法,所以需要讓目標(biāo)對(duì)象實(shí)例化并完成初始化后才會(huì)創(chuàng)建代理對(duì)象。

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		// 先從緩存中獲取代理對(duì)象
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			// 按需生成代理對(duì)象
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#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.
	/**
	 * @see AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(java.lang.Class, java.lang.String, org.springframework.aop.TargetSource)
	 */
	// 獲取與當(dāng)前Bean匹配的切面
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		// 創(chuàng)建代理
		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;
}

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#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);
	}

	// 創(chuàng)建代理工廠
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		// 進(jìn)來(lái)說(shuō)明proxyTargetClass=false,指定JDK代理
		if (shouldProxyTargetClass(beanClass, beanName)) {
			// 進(jìn)來(lái)這里說(shuō)明BD中有個(gè)屬性preserveTargetClass=true,可以BD中屬性設(shè)置的優(yōu)先級(jí)最高
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			// 這里會(huì)判斷bean有沒(méi)有實(shí)現(xiàn)接口,沒(méi)有就只能使用CGlib
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors); // 切面
	proxyFactory.setTargetSource(targetSource); // 目標(biāo)對(duì)象
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}

	// 使用JDK或者CGlib創(chuàng)建代理對(duì)象
	return proxyFactory.getProxy(getProxyClassLoader());
}

org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)

public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

這里主要看JDK動(dòng)態(tài)代理的實(shí)現(xiàn),Proxy.newProxyInstance()的第三個(gè)參數(shù)為InvocationHandler,而這里傳的是this,也就是當(dāng)前的類肯定實(shí)現(xiàn)了InvocationHandler接口。

代理方法的執(zhí)行

由于是JDK動(dòng)態(tài)代理,那么代理方法的調(diào)用肯定會(huì)進(jìn)入InvocationHandler.invoke()方法中,這里的InvocationHandler的實(shí)現(xiàn)類為org.springframework.aop.framework.JdkDynamicAopProxy。

org.springframework.aop.framework.JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;

	TargetSource targetSource = this.advised.targetSource;
	Object target = null;

	try {
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			// The target does not implement the equals(Object) method itself.
			return equals(args[0]);
		}
		else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
			// The target does not implement the hashCode() method itself.
			return hashCode();
		}
		else if (method.getDeclaringClass() == DecoratingProxy.class) {
			// There is only getDecoratedClass() declared -> dispatch to proxy config.
			return AopProxyUtils.ultimateTargetClass(this.advised);
		}
		else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
				method.getDeclaringClass().isAssignableFrom(Advised.class)) {
			// Service invocations on ProxyConfig with the proxy config...
			return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}

		Object retVal;

		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}

		// Get as late as possible to minimize the time we "own" the target,
		// in case it comes from a pool.
		target = targetSource.getTarget(); // 目標(biāo)對(duì)象
		Class<?> targetClass = (target != null ? target.getClass() : null); // 目標(biāo)對(duì)象的類型

		// Get the interception chain for this method.
		// 這里會(huì)對(duì)方法進(jìn)行匹配,因?yàn)椴皇悄繕?biāo)對(duì)象中的所有方法都需要增強(qiáng)
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		// Check whether we have any advice. If we don't, we can fallback on direct
		// reflective invocation of the target, and avoid creating a MethodInvocation.
		if (chain.isEmpty()) {
			// 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.
			// 沒(méi)有匹配的切面,直接通過(guò)反射調(diào)用目標(biāo)對(duì)象的目標(biāo)方法
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			// We need to create a method invocation...
			MethodInvocation invocation =
					new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			// Proceed to the joinpoint through the interceptor chain.
			/**
			 * @see ReflectiveMethodInvocation#proceed()
			 */
			// 這里才是增強(qiáng)的調(diào)用,重點(diǎn),火炬的傳遞
			retVal = invocation.proceed();
		}

		// Massage return value if necessary.
		Class<?> returnType = method.getReturnType();
		if (retVal != null && retVal == target &&
				returnType != Object.class && returnType.isInstance(proxy) &&
				!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
			// Special case: it returned "this" and the return type of the method
			// is type-compatible. Note that we can't help if the target sets
			// a reference to itself in another returned object.
			retVal = proxy;
		}
		else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
			throw new AopInvocationException(
					"Null return value from advice does not match primitive return type for: " + method);
		}
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			// Must have come from TargetSource.
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

public Object proceed() throws Throwable {
	// We start with an index of -1 and increment early.
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		// 執(zhí)行到最后一個(gè)Advice,才會(huì)到這里執(zhí)行目標(biāo)方法
		return invokeJoinpoint();
	}

	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		// Evaluate dynamic method matcher here: static part will already have
		// been evaluated and found to match.
		// dm.isRuntime()=true的走這
		InterceptorAndDynamicMethodMatcher dm =
				(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
		if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
			return dm.interceptor.invoke(this);
		}
		else {
			// Dynamic matching failed.
			// Skip this interceptor and invoke the next in the chain.
			return proceed();
		}
	}
	else {
		// It's an interceptor, so we just invoke it: The pointcut will have
		// been evaluated statically before this object was constructed.
		// 走這
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

interceptorsAndDynamicMethodMatchers中第一個(gè)advice為org.springframework.aop.interceptor.ExposeInvocationInterceptor。

ExposeInvocationInterceptor#invoke

org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke

private static final ThreadLocal<MethodInvocation> invocation =
 new NamedThreadLocal<>("Current AOP method invocation");

public Object invoke(MethodInvocation mi) throws Throwable {
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
		return mi.proceed();
	}
	finally {
		invocation.set(oldInvocation);
	}
}

ExposeInvocationInterceptor#invoke,只干了一件事就是將MethodInvocation加入到了ThreadLocal中,這樣后續(xù)可以在其他地方使用ExposeInvocationInterceptor#currentInvocation獲取到MethodInvocation,而MethodInvocation中封裝了目標(biāo)對(duì)象,目標(biāo)方法,方法參數(shù)等信息。

環(huán)繞通知的執(zhí)行

org.springframework.aop.aspectj.AspectJAroundAdvice#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
	if (!(mi instanceof ProxyMethodInvocation)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	JoinPointMatch jpm = getJoinPointMatch(pmi);
	return invokeAdviceMethod(pjp, jpm, null, null);
}

這里會(huì)去調(diào)用環(huán)繞通知的增強(qiáng)方法,而環(huán)繞通知的增強(qiáng)方法中會(huì)執(zhí)行proceedingJoinPoint.proceed(),這樣就會(huì)調(diào)用下一個(gè)MethodInterceptor–>MethodBeforeAdviceInterceptor。

前置通知的執(zhí)行

org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
	return mi.proceed();
}

這里又會(huì)調(diào)用MethodInvocation.proceed()傳遞給下一個(gè)MethodInterceptor。

后置通知的執(zhí)行

org.springframework.aop.aspectj.AspectJAfterAdvice#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	finally {
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

先執(zhí)行MethodInvocation.proceed(),最后在finally塊中調(diào)用后置通知的增強(qiáng),不管目標(biāo)方法有沒(méi)有拋出異常,finally代碼塊中的代碼都會(huì)執(zhí)行,也就是不管目標(biāo)方法有沒(méi)有拋出異常,后置通知都會(huì)執(zhí)行。

返回后通知的執(zhí)行

org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
	Object retVal = mi.proceed();
	this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
	return retVal;
}

先執(zhí)行MethodInvocation.proceed(),然后再執(zhí)行返回后通知的增強(qiáng)。

異常通知的執(zhí)行

org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	catch (Throwable ex) {
		if (shouldInvokeOnThrowing(ex)) {
			invokeAdviceMethod(getJoinPointMatch(), null, ex);
		}
		throw ex;
	}
}

先執(zhí)行MethodInvocation.proceed(),如果目標(biāo)方法拋出了異常就會(huì)執(zhí)行異常通知的增強(qiáng),然后拋出異常,所以這時(shí)返回后通知的增強(qiáng)就不會(huì)執(zhí)行了。

總結(jié)各種通知的執(zhí)行順序:

Around begin // 環(huán)繞通知開(kāi)始
Before // 前置通知
UserServiceImpl
 // 目標(biāo)方法的執(zhí)行
AfterReturning
 // 返回后通知
After
 // 后置通知
Around end // 環(huán)繞通知結(jié)束

到此這篇關(guān)于spring AOP原理及源碼分析的文章就介紹到這了,更多相關(guān)spring AOP原理源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Hadoop中的壓縮與解壓縮案例詳解

    Hadoop中的壓縮與解壓縮案例詳解

    壓縮就是通過(guò)某種技術(shù)(算法)把原始文件變下,相應(yīng)的解壓就是把壓縮后的文件變成原始文件,本文給大家分享Hadoop中的壓縮知識(shí),感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Java鏈表使用解讀

    Java鏈表使用解讀

    Java中的鏈表(LinkedList)是一種動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),由一系列節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)包含數(shù)據(jù)和指向下一個(gè)節(jié)點(diǎn)的引用,LinkedList實(shí)現(xiàn)了List和Deque接口,支持高效的插入和刪除操作,Java提供了.standard.util.LinkedList類來(lái)實(shí)現(xiàn)鏈表
    2025-01-01
  • 完整的logback配置示例ELK整合包含生成json日志

    完整的logback配置示例ELK整合包含生成json日志

    這篇文章主要為大家介紹了完整的logback配置示例ELK整合包含生成json日志,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java中OGNL表達(dá)式語(yǔ)言的使用詳解

    Java中OGNL表達(dá)式語(yǔ)言的使用詳解

    本文介紹了OGNL(ObjectGraphNavigationLanguage)表達(dá)式語(yǔ)言,這是一種用于Java語(yǔ)言的對(duì)象圖導(dǎo)航和操作的表達(dá)式語(yǔ)言,它支持訪問(wèn)對(duì)象屬性、調(diào)用對(duì)象方法、執(zhí)行算術(shù)和邏輯運(yùn)算,以及處理集合和數(shù)組等操作,OGNL的語(yǔ)法簡(jiǎn)潔明了
    2024-12-12
  • Spring容器刷新prepareRefresh第一步

    Spring容器刷新prepareRefresh第一步

    這篇文章主要為大家介紹了Spring容器刷新prepareRefresh第一步示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Java線程阻塞工具LockSupport用法詳解

    Java線程阻塞工具LockSupport用法詳解

    Java中的LockSupport是一個(gè)用于線程同步的工具類,它提供了一種基于線程的阻塞和喚醒機(jī)制,LockSupport可以讓線程在特定條件下阻塞掛起,等待其他線程發(fā)送信號(hào)來(lái)喚醒它,本文將通過(guò)一個(gè)小案例給大家介紹一下LockSupport怎么用,讓你永遠(yuǎn)記住它
    2023-08-08
  • springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn)

    springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn)

    本文主要介紹了springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • SpringBoot整合SpringTask實(shí)現(xiàn)定時(shí)任務(wù)的流程

    SpringBoot整合SpringTask實(shí)現(xiàn)定時(shí)任務(wù)的流程

    這篇文章主要介紹了SpringBoot整合SpringTask實(shí)現(xiàn)定時(shí)任務(wù)的流程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • java中BigDecimal的操作方法

    java中BigDecimal的操作方法

    這篇文章主要介紹了java中BigDecimal的操作方法,較為詳細(xì)的分析了BigDecimal類在進(jìn)行商業(yè)計(jì)算時(shí)的應(yīng)用方法,精度以及注意事項(xiàng)等問(wèn)題,需要的朋友可以參考下
    2014-12-12
  • java Volatile與Synchronized的區(qū)別

    java Volatile與Synchronized的區(qū)別

    這篇文章主要介紹了java Volatile與Synchronized的區(qū)別,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12

最新評(píng)論

河池市| 黄浦区| 景宁| 衡山县| 鱼台县| 建湖县| 梓潼县| 新津县| 定安县| 黎川县| 吉木萨尔县| 阿瓦提县| 石河子市| 长顺县| 兴安县| 内乡县| 绥芬河市| 咸丰县| 青岛市| 闵行区| 延长县| 新巴尔虎左旗| 西畴县| 长垣县| 延长县| 招远市| 兴安盟| 英山县| 赤城县| 南阳市| 阿克苏市| 铜鼓县| 怀化市| 拜城县| 香河县| 福安市| 合阳县| 武邑县| 望江县| 梧州市| 海口市|