欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring的@Configuration使用與原理

 更新時(shí)間:2023年05月12日 08:51:44   作者:morris131  
這篇文章主要介紹了Spring的@Configuration使用與原理,@Configuration用于定義配置類,可替換xml配置文件,被注解的類內(nèi)部包含有一個(gè)或多個(gè)被@Bean注解的方法,需要的朋友可以參考下

@Configuration的使用

@Configuration是一個(gè)被@Component注解修飾的注解,先來看一個(gè)現(xiàn)象:

package com.morris.spring.config;
import com.morris.spring.entity.Message;
import com.morris.spring.entity.Order;
import com.morris.spring.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
//@Component
@Configuration
public class UserConfig {
	@Bean
	public User user() {
		System.out.println("create user");
		return new User();
	}
	@Bean
	public Message message() {
		return new Message(user());
	}
}

現(xiàn)象:

  1. UserConfig類上面使用@Configuration,user()只會(huì)被調(diào)用一次。
  2. UserConfig類上面使用@Component,user()會(huì)被調(diào)用兩次,也就是會(huì)創(chuàng)建兩個(gè)user對(duì)象,破壞了Spring中Bean的單例。

為什么會(huì)出現(xiàn)上面這種現(xiàn)象呢?初步猜測(cè)使用@Configuration會(huì)使用動(dòng)態(tài)代理,下面從源碼來分析原理。

@Configuration的源碼分析

標(biāo)識(shí)@Configuration為full

在BeanDefinition會(huì)有一個(gè)屬性來標(biāo)識(shí)是否有@Configuration注解:

org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate

public static boolean checkConfigurationClassCandidate(
		BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
... ...
	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
		// 這里對(duì)@Configuration做了特殊處理,往BD中設(shè)置了屬性full
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (config != null || isConfigurationCandidate(metadata)) {
		// 帶有@Component、@ComponentScan、@Import、@ImportResource、@Bean注解的BD,設(shè)置屬性lite
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}
	// It's a full or lite configuration candidate... Let's determine the order value, if any.
	Integer order = getOrder(metadata);
	if (order != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, order);
	}
	return true;
}

@Bean注解的收集

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

...
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
...

將@Bean的方法封裝為MethodMetadata對(duì)象放入到ConfigClass中。

解析BeanMethod注入BeanDefinition

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
	ConfigurationClass configClass = beanMethod.getConfigurationClass();
	MethodMetadata metadata = beanMethod.getMetadata();
	String methodName = metadata.getMethodName();
... ...
	if (metadata.isStatic()) {
		// static @Bean method
		// 靜態(tài) @Bean method
		if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
			beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
		}
		else {
			beanDef.setBeanClassName(configClass.getMetadata().getClassName());
		}
		beanDef.setUniqueFactoryMethodName(methodName);
	}
	else {
		// 實(shí)例 @Bean method
		// instance @Bean method
		beanDef.setFactoryBeanName(configClass.getBeanName());
		beanDef.setUniqueFactoryMethodName(methodName);
	}
...	...
	this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

從上面的源碼可以發(fā)現(xiàn)@Bean注解注入的BeanDefinition都會(huì)有個(gè)FactoryMethodName,也就是后面實(shí)例化時(shí)將會(huì)使用factory-method流程進(jìn)行實(shí)例化。

替換BeanClass為CGlib代理類

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	int factoryId = System.identityHashCode(beanFactory);
	if (this.factoriesPostProcessed.contains(factoryId)) {
		throw new IllegalStateException(
				"postProcessBeanFactory already called on this post-processor against " + beanFactory);
	}
	this.factoriesPostProcessed.add(factoryId);
	if (!this.registriesPostProcessed.contains(factoryId)) {
		// BeanDefinitionRegistryPostProcessor hook apparently not supported...
		// Simply call processConfigurationClasses lazily at this point then.
		processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
	}
	// 對(duì)@Configuration注解的類做增強(qiáng),CGlib代理
	enhanceConfigurationClasses(beanFactory);
	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
	Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
	// 收集所有帶有@Configuration的BD,也就是有個(gè)attribute為full的屬性
	for (String beanName : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
		Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
		MethodMetadata methodMetadata = null;
		if (beanDef instanceof AnnotatedBeanDefinition) {
			methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
		}
		if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
			// Configuration class (full or lite) or a configuration-derived @Bean method
			// -> resolve bean class at this point...
			AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
			if (!abd.hasBeanClass()) {
				try {
					abd.resolveBeanClass(this.beanClassLoader);
				}
				catch (Throwable ex) {
					throw new IllegalStateException(
							"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
				}
			}
		}
		if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
			if (!(beanDef instanceof AbstractBeanDefinition)) {
				throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
						beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
			}
			else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
				logger.info("Cannot enhance @Configuration bean definition '" + beanName +
						"' since its singleton instance has been created too early. The typical cause " +
						"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
						"return type: Consider declaring such methods as 'static'.");
			}
			configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
		}
	}
	if (configBeanDefs.isEmpty()) {
		// nothing to enhance -> return immediately
		return;
	}
	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	// 遍歷所有帶有@Configuration的BD
	for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
		AbstractBeanDefinition beanDef = entry.getValue();
		// If a @Configuration class gets proxied, always proxy the target class
		beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
		// Set enhanced subclass of the user-specified bean class
		Class<?> configClass = beanDef.getBeanClass();
		// 生成CGlib代理類
		Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
		if (configClass != enhancedClass) {
			if (logger.isTraceEnabled()) {
				logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
						"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
			}
			beanDef.setBeanClass(enhancedClass);
		}
	}
}

org.springframework.context.annotation.ConfigurationClassEnhancer#enhance

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
	if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Ignoring request to enhance %s as it has " +
					"already been enhanced. This usually indicates that more than one " +
					"ConfigurationClassPostProcessor has been registered (e.g. via " +
					"<context:annotation-config>). This is harmless, but you may " +
					"want check your configuration and remove one CCPP if possible",
					configClass.getName()));
		}
		return configClass;
	}
	// 創(chuàng)建代理類
	Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
	if (logger.isTraceEnabled()) {
		logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
				configClass.getName(), enhancedClass.getName()));
	}
	return enhancedClass;
}
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(configSuperClass);
	enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
	enhancer.setUseFactory(false);
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
	// 這里會(huì)注入幾個(gè)Callback
	enhancer.setCallbackFilter(CALLBACK_FILTER);
	enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
	return enhancer;
}
private Class<?> createClass(Enhancer enhancer) {
	Class<?> subclass = enhancer.createClass();
	// Registering callbacks statically (as opposed to thread-local)
	// is critical for usage in an OSGi environment (SPR-5932)...
	Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
	return subclass;
}
private static final Callback[] CALLBACKS = new Callback[] {
		new BeanMethodInterceptor(),
 // 重點(diǎn)
		new BeanFactoryAwareMethodInterceptor(),
		NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

上面一堆代碼就是為了給@Configuration注解的類生成CGlib代理類,所以關(guān)鍵就是這個(gè)類長什么樣,是怎么做增強(qiáng)的,重點(diǎn)就在上面的BeanMethodInterceptor中。

@Bean方法的調(diào)用

org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
			MethodProxy cglibMethodProxy) throws Throwable {
	ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
	String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
	// Determine whether this bean is a scoped-proxy
	if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
		String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
		if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
			beanName = scopedBeanName;
		}
	}
	// To handle the case of an inter-bean method reference, we must explicitly check the
	// container for already cached instances.
	// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
	// proxy that intercepts calls to getObject() and returns any cached bean instance.
	// This ensures that the semantics of calling a FactoryBean from within @Bean methods
	// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
	if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
			factoryContainsBean(beanFactory, beanName)) {
		Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
		if (factoryBean instanceof ScopedProxyFactoryBean) {
			// Scoped proxy factory beans are a special case and should not be further proxied
		}
		else {
			// It is a candidate FactoryBean - go ahead with enhancement
			return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
		}
	}
	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		// The factory is calling the bean method in order to instantiate and register the bean
		// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
		// create the bean instance.
		if (logger.isInfoEnabled() &&
				BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
			logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
							"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
							"result in a failure to process annotations such as @Autowired, " +
							"@Resource and @PostConstruct within the method's declaring " +
							"@Configuration class. Add the 'static' modifier to this method to avoid " +
							"these container lifecycle issues; see @Bean javadoc for complete details.",
					beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
		}
		// 第一次調(diào)用走這
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}
	// 第二次調(diào)用走這
	return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

isCurrentlyInvokedFactoryMethod何時(shí)返回true?

private boolean isCurrentlyInvokedFactoryMethod(Method method) {
	// SimpleInstantiationStrategy這個(gè)值哪里設(shè)置進(jìn)去的
	/**
	 * @see SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object...)
	 */
	Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
	return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
			Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}

第一次調(diào)用發(fā)生在Spring容器啟動(dòng)時(shí)實(shí)例化user對(duì)象,@Bean注解注入的對(duì)象會(huì)通過factory-method進(jìn)行實(shí)例化,此處是UserConfig.user(),調(diào)用此方法前會(huì)往ThreadLocal中放入當(dāng)前的方法:

org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate

...
try {
	// 放入ThreadLocal,@Bean走cglib代理會(huì)使用這個(gè)ThreadLocal
	currentlyInvokedFactoryMethod.set(factoryMethod);
	Object result = factoryMethod.invoke(factoryBean, args);
	if (result == null) {
		result = new NullBean();
	}
	return result;
}
...

然后再反射調(diào)用factoryMethod,此時(shí)調(diào)用的是代理對(duì)象,進(jìn)入BeanMethodInterceptor#intercept,然后判斷當(dāng)前ThreadLocal中有FactoryMethod,就會(huì)調(diào)用父類也就是目標(biāo)類的目標(biāo)方法,此處是UserConfig.user()創(chuàng)建對(duì)象。

第二次進(jìn)入BeanMethodInterceptor#intercept時(shí),是實(shí)例化Message,此時(shí)雖然ThreadLocal中有FactoryMethod,但是這個(gè)Method是message(),而不是user(),所以會(huì)調(diào)用下面的方法從BeanFactory中獲得對(duì)象,而不是調(diào)用FactoryMethod創(chuàng)建對(duì)象。

org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
		ConfigurableBeanFactory beanFactory, String beanName) {
	// The user (i.e. not the factory) is requesting this bean through a call to
	// the bean method, direct or indirect. The bean may have already been marked
	// as 'in creation' in certain autowiring scenarios; if so, temporarily set
	// the in-creation status to false in order to avoid an exception.
	boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
	try {
		if (alreadyInCreation) {
			beanFactory.setCurrentlyInCreation(beanName, false);
		}
		boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
		if (useArgs && beanFactory.isSingleton(beanName)) {
			// Stubbed null arguments just for reference purposes,
			// expecting them to be autowired for regular singleton references?
			// A safe assumption since @Bean singleton arguments cannot be optional...
			for (Object arg : beanMethodArgs) {
				if (arg == null) {
					useArgs = false;
					break;
				}
			}
		}
		// 第二次調(diào)用直接從BeanFactory中取
		Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
				beanFactory.getBean(beanName));
		if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
			// Detect package-protected NullBean instance through equals(null) check
			if (beanInstance.equals(null)) {
				if (logger.isDebugEnabled()) {
					logger.debug(String.format("@Bean method %s.%s called as bean reference " +
							"for type [%s] returned null bean; resolving to null value.",
							beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
							beanMethod.getReturnType().getName()));
				}
				beanInstance = null;
			}
			else {
				String msg = String.format("@Bean method %s.%s called as bean reference " +
						"for type [%s] but overridden by non-compatible bean instance of type [%s].",
						beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
						beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
				try {
					BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
					msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Ignore - simply no detailed message then.
				}
				throw new IllegalStateException(msg);
			}
		}
		Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
		if (currentlyInvoked != null) {
			String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
			beanFactory.registerDependentBean(beanName, outerBeanName);
		}
		return beanInstance;
	}
	finally {
		if (alreadyInCreation) {
			beanFactory.setCurrentlyInCreation(beanName, true);
		}
	}
}

如果將user()方法改為static,那么又會(huì)創(chuàng)建兩個(gè)user對(duì)象,因?yàn)閟pring并沒有為@Configuration修飾的類創(chuàng)建代理,只是為@Configuration修飾的類的對(duì)象創(chuàng)建了代理。

FactoryBean的代理

如果@Bean注入的是一個(gè)FactoryBean,Spring會(huì)為FactoryBean生成代理,保證getObject()方法只會(huì)被調(diào)用一次。

package com.morris.spring.config;
import com.morris.spring.entity.Message;
import com.morris.spring.entity.UserFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserFactoryBeanConfig {
	@Bean
	public UserFactoryBean userFactoryBean() {
		System.out.println("userFactoryBean");
		return new UserFactoryBean();
	}
	@Bean
	public Message message() throws Exception {
		return new Message(userFactoryBean().getObject());
	}
}

關(guān)鍵源碼如下: org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept

...
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
		factoryContainsBean(beanFactory, beanName)) {
	Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
	if (factoryBean instanceof ScopedProxyFactoryBean) {
		// Scoped proxy factory beans are a special case and should not be further proxied
	}
	else {
		// It is a candidate FactoryBean - go ahead with enhancement
		// 為FactoryBean創(chuàng)建代理
		return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
	}
}
...

org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#enhanceFactoryBean

private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
		final ConfigurableBeanFactory beanFactory, final String beanName) {
	try {
		Class<?> clazz = factoryBean.getClass();
		boolean finalClass = Modifier.isFinal(clazz.getModifiers());
		boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
		if (finalClass || finalMethod) {
			if (exposedType.isInterface()) {
				if (logger.isTraceEnabled()) {
					logger.trace("Creating interface proxy for FactoryBean '" + beanName + "' of type [" +
							clazz.getName() + "] for use within another @Bean method because its " +
							(finalClass ? "implementation class" : "getObject() method") +
							" is final: Otherwise a getObject() call would not be routed to the factory.");
				}
				// 創(chuàng)建jdk動(dòng)態(tài)代理
				return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Unable to proxy FactoryBean '" + beanName + "' of type [" +
							clazz.getName() + "] for use within another @Bean method because its " +
							(finalClass ? "implementation class" : "getObject() method") +
							" is final: A getObject() call will NOT be routed to the factory. " +
							"Consider declaring the return type as a FactoryBean interface.");
				}
				return factoryBean;
			}
		}
	}
	catch (NoSuchMethodException ex) {
		// No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
	}
	// 使用cglib創(chuàng)建代理
	return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}

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

相關(guān)文章

  • SpringBoot Jpa企業(yè)開發(fā)示例詳細(xì)講解

    SpringBoot Jpa企業(yè)開發(fā)示例詳細(xì)講解

    這篇文章主要介紹了SpringBoot Jpa企業(yè)開發(fā)示例,Jpa可以通過實(shí)體類生成數(shù)據(jù)庫的表,同時(shí)自帶很多增刪改查方法,大部分sql語句不需要我們自己寫,配置完成后直接調(diào)用方法即可,很方便
    2022-11-11
  • 基于SpringMVC實(shí)現(xiàn)網(wǎng)頁登錄攔截

    基于SpringMVC實(shí)現(xiàn)網(wǎng)頁登錄攔截

    SpringMVC的處理器攔截器類似于Servlet開發(fā)中的過濾器Filter,用于對(duì)處理器進(jìn)行預(yù)處理和后處理。因此,本文將為大家介紹如何通過SpringMVC實(shí)現(xiàn)網(wǎng)頁登錄攔截功能,需要的小伙伴可以了解一下
    2021-12-12
  • 8個(gè)Spring事務(wù)失效場(chǎng)景詳解

    8個(gè)Spring事務(wù)失效場(chǎng)景詳解

    相信大家對(duì)Spring種事務(wù)的使用并不陌生,但是你可能只是停留在基礎(chǔ)的使用層面上。今天,我們就簡單來說下Spring事務(wù)的原理,然后總結(jié)一下spring事務(wù)失敗的場(chǎng)景,并提出對(duì)應(yīng)的解決方案,需要的可以參考一下
    2022-12-12
  • SpringCloud?OpenFeign概述與使用教程

    SpringCloud?OpenFeign概述與使用教程

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成
    2023-02-02
  • IntelliJ IDEA 詳細(xì)圖解最常用的配置(適合剛剛用的新人)

    IntelliJ IDEA 詳細(xì)圖解最常用的配置(適合剛剛用的新人)

    這篇文章主要介紹了IntelliJ IDEA 詳細(xì)圖解最常用的配置,本篇教程非常適合剛剛用的新人,本文圖文并茂給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 簡單了解java volatile關(guān)鍵字實(shí)現(xiàn)的原理

    簡單了解java volatile關(guān)鍵字實(shí)現(xiàn)的原理

    這篇文章主要介紹了簡單了解volatile關(guān)鍵字實(shí)現(xiàn)的原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • flink進(jìn)階富函數(shù)生命周期介紹

    flink進(jìn)階富函數(shù)生命周期介紹

    這篇文章主要為大家介紹了flink進(jìn)階富函數(shù)生命周期的舉例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Java手動(dòng)創(chuàng)建線程池代碼實(shí)例

    Java手動(dòng)創(chuàng)建線程池代碼實(shí)例

    這篇文章主要介紹了Java手動(dòng)創(chuàng)建線程池代碼實(shí)例,FixedThreadPool或者SingleThreadPool,允許的請(qǐng)求隊(duì)列長度為Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致OOM,需要的朋友可以參考下
    2023-12-12
  • Java過濾器doFilter里chain.doFilter()函數(shù)的理解

    Java過濾器doFilter里chain.doFilter()函數(shù)的理解

    這篇文章主要介紹了Java過濾器doFilter里chain.doFilter()函數(shù)的理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java面向?qū)ο笤O(shè)計(jì)原則之接口隔離原則示例詳解

    java面向?qū)ο笤O(shè)計(jì)原則之接口隔離原則示例詳解

    這篇文章主要為大家介紹了java面向?qū)ο笤O(shè)計(jì)原則之接口隔離原則的示例詳解,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-10-10

最新評(píng)論