主頁 > 後端開發 > Spring原始碼:Bean生命周期(四)

Spring原始碼:Bean生命周期(四)

2023-05-16 10:24:51 後端開發

前言

在之前的文章中,我們介紹了 Bean 的核心概念、Bean 定義的決議程序以及 Bean 創建的準備作業,在今天的文章中,我們將深入探討 Bean 的創建程序,并主要講解 createBean 方法的實作,在這個程序中,我們將了解 Bean 的實體化、屬性注入、初始化和銷毀等步驟,以及各個步驟的具體實作細節,通過本文的學習,讀者將能夠更深入地理解 Spring 框架中 Bean 的創建程序,從而為后續的學習和實踐打下堅實的基礎,好了,我們開始!

createBean

前面我們說過,最開始的bean定義(合并后的),決議類的元資料時,用到的是ASM技術并不會真正開始決議class檔案,所以也只是提取出來bean的name值作為beanClass屬性,知道這個前提,那么這一步就好說了,下面是他的原始碼:

	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		RootBeanDefinition mbdToUse = mbd;
		
		// 馬上就要實體化Bean了,確保beanClass被加載了
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		try {
			mbdToUse.prepareMethodOverrides();
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			// 實體化前
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}

		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			......
			return beanInstance;
		}
	}
  1. resolveBeanClass:真正的開始加載bean,
  2. mbdToUse.prepareMethodOverrides();和@lookUp注解有關系,不看
  3. resolveBeforeInstantiation:實體化前的BeanPostProcessors,如果初始化了那么就回傳了,不走其他創建邏輯了,
  4. doCreateBean:正常開始實體化、初始化bean,

resolveBeanClass

如果當前bean被加載了,那么直接回傳了,如果沒加載那么開始決議當前bean

	@Nullable
	protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)
			throws CannotLoadBeanClassException {

		try {
			// 如果beanClass被加載了
			if (mbd.hasBeanClass()) {
				return mbd.getBeanClass();
			}

			// 如果beanClass沒有被加載
			if (System.getSecurityManager() != null) {
				return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)
						() -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
			}
			else {
				return doResolveBeanClass(mbd, typesToMatch);
			}
		}
	}

是否已經加載的判斷依據就是我說的,是否是class,正常下我們的beanClass為字串,也就是beanname,看下原始碼:

public boolean hasBeanClass() {
		return (this.beanClass instanceof Class);
	}

doResolveBeanClass

真正開始加載class,如果需要加載class那肯定離不開類加載器,看下原始碼:

	@Nullable
	private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
			throws ClassNotFoundException {

		ClassLoader beanClassLoader = getBeanClassLoader();
		ClassLoader dynamicLoader = beanClassLoader;
		boolean freshResolve = false;

		if (!ObjectUtils.isEmpty(typesToMatch)) {
			// When just doing type checks (i.e. not creating an actual instance yet),
			// use the specified temporary class loader (e.g. in a weaving scenario).
			ClassLoader tempClassLoader = getTempClassLoader();
			if (tempClassLoader != null) {
				dynamicLoader = tempClassLoader;
				freshResolve = true;
				if (tempClassLoader instanceof DecoratingClassLoader) {
					DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
					for (Class<?> typeToMatch : typesToMatch) {
						dcl.excludeClass(typeToMatch.getName());
					}
				}
			}
		}

		String className = mbd.getBeanClassName();
		if (className != null) {
			// 決議Spring運算式,有可能直接回傳了一個Class物件
			Object evaluated = evaluateBeanDefinitionString(className, mbd);
			if (!className.equals(evaluated)) {
				// A dynamically resolved expression, supported as of 4.2...
				if (evaluated instanceof Class) {
					return (Class<?>) evaluated;
				}
				else if (evaluated instanceof String) {
					className = (String) evaluated;
					freshResolve = true;
				}
				else {
					throw new IllegalStateException("Invalid class name expression result: " + evaluated);
				}
			}
			if (freshResolve) {
				// When resolving against a temporary class loader, exit early in order
				// to avoid storing the resolved Class in the bean definition.
				if (dynamicLoader != null) {
					try {
						return dynamicLoader.loadClass(className);
					}
					catch (ClassNotFoundException ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
						}
					}
				}
				return ClassUtils.forName(className, dynamicLoader);
			}
		}

		// Resolve regularly, caching the result in the BeanDefinition...
		return mbd.resolveBeanClass(beanClassLoader);
	}

我們自己的bean走不了這么多邏輯,我們既沒有傳typesToMatch,也沒有寫Spring運算式,所以就是拿了一個類加載器和使用類加載器加載class,如果我們沒有自定義類加載器那么使用默認的,看下原始碼:

	@Nullable
	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;

		// 優先獲取執行緒中的類加載器
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}

		// 執行緒中類加載器為null的情況下,獲取加載ClassUtils類的類加載器
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				// 加入ClassUtils是被Bootstrap類加載器加載的,則獲取系統類加載器
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}
  1. 優先獲取執行緒中的類加載器
  2. 執行緒中類加載器為null的情況下,獲取加載ClassUtils類的類加載器,這里Spring注意到了java的boostrap加載器,所以會有為null的情況
  3. 如果為null,那么使用ClassUtils當前工具類使用的是哪個加載器
  4. 假如ClassUtils是被Bootstrap類加載器加載的,則獲取系統類加載器
	public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
		String className = getBeanClassName();
		if (className == null) {
			return null;
		}
		Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
		this.beanClass = resolvedClass;
		return resolvedClass;
	}
	public String getBeanClassName() {
		Object beanClassObject = this.beanClass;
		if (beanClassObject instanceof Class) {
			return ((Class<?>) beanClassObject).getName();
		}
		else {
			return (String) beanClassObject;
		}
	}

通過這一步也可以看出bean定義中最初的beanClass屬性,都是String型別的beanname

resolveBeforeInstantiation

這一步走的是實體化前的作業,當然如果你想在這一步中直接回傳物體類也可,而且最離譜的是Spring并沒有校驗你回傳的類是否是當前beanname的類,可以看下原始碼:

	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			// synthetic表示合成,如果某些Bean式合成的,那么則不會經過BeanPostProcessor的處理
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}
  1. hasInstantiationAwareBeanPostProcessors:直接從快取list中獲取有關實體化的BeanPostProcessors,這里是一個優化,要不然每次獲取有關實體化的BeanPostProcessors都是遍歷整個BeanPostProcessors再加個校驗
  2. determineTargetType:獲取類
  3. applyBeanPostProcessorsBeforeInstantiation:執行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的方法,該方法可以回傳bean,
  4. postProcessAfterInstantiation:執行BeanPostProcessor的postProcessAfterInstantiation的方法,正常我們的bean不會走到這里,因為實體化前根本沒有創建出來bean,所以也就是bean != null一直為false

當然除非你自己寫一個InstantiationAwareBeanPostProcessors,其實真沒看見這么玩的,主要是沒有啥意義,比如這樣:

@Component
public class MyInstantiationAwareBeanPostProcessors implements InstantiationAwareBeanPostProcessor {

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		if (beanName.equals("userService")) {
			System.out.println("MyInstantiationAwareBeanPostProcessors.postProcessBeforeInstantiation");
			return new First();
		}
		return null;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (beanName.equals("userService")) {
			System.out.println("MyInstantiationAwareBeanPostProcessors.postProcessAfterInitialization");
			return new Second();
		}
		return bean;
	}
}

再堅持一下,讓我把實體化程序先講完!

現在的邏輯已經走完了實體化前的postProcessBeforeInstantiation方法,那么現在我們的bean要進行實體化了,

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// 實體化bean
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			// 有可能在本Bean創建之前,就有其他Bean把當前Bean給創建出來了(比如依賴注入程序中)
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 創建Bean實體
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// 后置處理合并后的BeanDefinition
		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// 為了解決回圈依賴提前快取單例創建工廠
		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			// 回圈依賴-添加到三級快取
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// 屬性填充
			populateBean(beanName, mbd, instanceWrapper);
      ......
		return exposedObject;
	}

跟這篇無關的內容能洗掉的都洗掉了,主要有這幾步我們需要注意下:

  1. createBeanInstance:創建實體,前提是之前沒有創建過
  2. applyMergedBeanDefinitionPostProcessors:找到注入點,比如AutowiredAnnotationBeanPostProcessor(@Autowired、@Value、@Inject)和CommonAnnotationBeanPostProcessor(@Resource),這在實體化前和實體化后方法中間夾了一個處理合并bean定義的邏輯,注意一下
  3. addSingletonFactory:添加快取,用來解決回圈依賴,以后單獨講解
  4. populateBean:這一方法主要是屬性填充也就是依賴注入的,但是官方把實體化后的PostProcessors方法寫到這里了,所以也得貼出來,但是我們只看實體化相關的,

createBeanInstance

	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		// BeanDefinition中添加了Supplier,則呼叫Supplier來得到物件
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		// @Bean對應的BeanDefinition
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
    ......
		return instantiateBean(beanName, mbd);
	}
  1. resolveBeanClass:之前講解過了,不重復講了,就是拿到class
  2. obtainFromSupplier:通過Supplier函式獲取bean,前提是你得宣告bean定義
  3. instantiateUsingFactoryMethod:這種是使用@Bean方法實體化物件,
  4. 后面省略了推斷構造方法進行實體化物件,以后單獨講解推斷構造方法

obtainFromSupplier

這一步其實我們用到的很少,主要是考慮到Spring自動注入的開銷,我們自己可以就行實體化而已,比如我們這樣寫照樣可以獲取bean,但是不會由Spring幫我們注入,得靠自己了:

//		 創建一個Spring容器
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition.setBeanClass(UserService.class);
		beanDefinition.setInstanceSupplier(() -> new UserService());
		applicationContext.registerBeanDefinition("userService", beanDefinition);
		UserService userService = (UserService) applicationContext.getBean(UserService.class);
		userService.test();

其實用法和@bean注解相似,除了減少Spring自動注入的開銷,實在沒想到有啥用

instantiateUsingFactoryMethod

該方法內部邏輯很多,為了更加直觀的展現,只貼出關鍵代碼:

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, final Method factoryMethod, Object... args) {

		try {
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(factoryMethod);
					return null;
				});
			}
			else {
				ReflectionUtils.makeAccessible(factoryMethod);
			}

			Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
			try {
				currentlyInvokedFactoryMethod.set(factoryMethod);
				// factoryBean就是AppConfig的代理物件(如果加了@Configuration)
				// factoryMethod就是@Bean修飾的方法
				Object result = factoryMethod.invoke(factoryBean, args);
				if (result == null) {
					result = new NullBean();
				}
				return result;
			}
			finally {
				if (priorInvokedFactoryMethod != null) {
					currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
				}
				else {
					currentlyInvokedFactoryMethod.remove();
				}
			}
		}
		......
	}

比如我們定義的配置類中有很多@Bean形式的方法,最終Spring會直接invoke呼叫被@Bean修飾的方法從而實作實體化物件,

applyMergedBeanDefinitionPostProcessors

這里關于MergedBeanDefinitionPostProcessors的實作類不全講解了,主要講解下作業常用的注解AutowiredAnnotationBeanPostProcessor,他是用來決議@Autowired、@Value、@Inject,看下他的默認原始碼:

public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		}
	}

看下他主要做了那些作業,關鍵代碼附上:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
	// 如果一個Bean的型別是String...,那么則根本不需要進行依賴注入
	if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}

	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

		// 遍歷targetClass中的所有Field
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			// field上是否存在@Autowired、@Value、@Inject中的其中一個
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
				// static filed不是注入點,不會進行自動注入
				if (Modifier.isStatic(field.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static fields: " + field);
					}
					return;
				}

				// 構造注入點
				boolean required = determineRequiredStatus(ann);
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});

		// 遍歷targetClass中的所有Method
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {

			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			// method上是否存在@Autowired、@Value、@Inject中的其中一個
			MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				// static method不是注入點,不會進行自動注入
				if (Modifier.isStatic(method.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}
				// set方法最好有入參
				if (method.getParameterCount() == 0) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
					}
				}
				boolean required = determineRequiredStatus(ann);
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return InjectionMetadata.forElements(elements, clazz);
}
  1. 如果一個Bean的型別是String,那么則根本不需要進行依賴注入
  2. 遍歷targetClass中的所有Field,是否存在@Autowired、@Value、@Inject中的其中一個,如果是static欄位則不注入否則記錄構造注入點
  3. 遍歷targetClass中的所有Method,是否存在@Autowired、@Value、@Inject中的其中一個,如果是static欄位則不注入否則記錄構造注入點

populateBean

這個方法主要是屬性填充,也就是所說的依賴注入的程序,我們不講解這一部分,只講解關于實體化最后的階段postProcessAfterInstantiation方法,方法進來第一步就是呼叫postProcessAfterInstantiation方法,但是只看Spring原始碼的話,其實并沒有太多實作,都是默認實作方法:

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}

總結

在本文中,我們深入探討了 Spring 框架中 Bean 的實體化程序,關于某些細節以后我會單獨拿出一篇文章單獨講解,我們來總結下實體化都做了哪些事情:

  1. 先從bean定義中加載當前類,因為最初Spring使用ASM技術決議元資料時只獲取了當前類的名稱
  2. 尋找所有InstantiationAwareBeanPostProcessors實作類,并呼叫實體化前的方法postProcessBeforeInstantiation
  3. 進行實體化,這里會使用構造方法進行實體化
  4. 呼叫applyMergedBeanDefinitionPostProcessors找到所有MergedBeanDefinitionPostProcessors的實作類,比如我們的注入點(@Autowired等)
  5. 尋找所有InstantiationAwareBeanPostProcessors實作類,并呼叫實體化后的方法postProcessAfterInstantiation

通過本文的學習,讀者將能夠更深入地了解 Spring 框架中 Bean 的實體化程序,為后續的學習和實踐打下堅實的基礎,下一篇文章,我們將深入探討 Bean 的初始化程序,

公眾號 ps:以上內容,純屬個人見解,有任何問題下方評論!關注博主公眾號,原始碼專題、面試精選、AI最新擴展等你來看!原創撰寫不易,轉載請說明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552487.html

標籤:其他

上一篇:< Python全景系列-2 > Python資料型別大盤點

下一篇:返回列表

標籤雲
其他(159048) Python(38137) JavaScript(25421) Java(18044) C(15226) 區塊鏈(8265) C#(7972) AI(7469) 爪哇(7425) MySQL(7186) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5871) 数组(5741) R(5409) Linux(5340) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4572) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2433) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1973) 功能(1967) Web開發(1951) HtmlCss(1936) python-3.x(1918) C++(1917) 弹簧靴(1913) xml(1889) PostgreSQL(1876) .NETCore(1860) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Spring原始碼:Bean生命周期(四)

    在本文中,我們深入探討了 Spring 框架中 Bean 的實體化程序,關于某些細節以后我會單獨拿出一篇文章單獨講解,我們來總結下實體化都做了哪些事情:先從bean定義中加載當前類,因為最初Spring使用ASM技術決議元資料時只獲取了當前類的名稱尋找所有InstantiationAwareBean... ......

    uj5u.com 2023-05-16 10:24:51 more
  • < Python全景系列-2 > Python資料型別大盤點

    Python作為一門強大且靈活的編程語言,擁有豐富的資料型別系統。本文詳細介紹了Python中的每一種資料型別,包括數值、序列、映射、集合、布爾和None型別。每種資料型別的特性、使用方式,以及在實際問題中的應用都將被深入探討。此外,我們還將探討Python的動態型別特性,以及如何在實際編程中充分利... ......

    uj5u.com 2023-05-16 10:13:02 more
  • Pytest - 概述&入門

    Pytest - 概述&入門 簡介 Pytest是一款強大的python自動化測驗工具,可以勝任各種型別或者級別的軟體測驗作業; pytest提供了豐富的功能,包括assert重寫,第三方插件,以及其他測驗工具無法比擬的fixture模型; pytest是一個軟體測驗框架,是一款命令列工具,可以自動 ......

    uj5u.com 2023-05-16 10:12:56 more
  • 裝飾器擴展類功能

    對類中屬性訪問,并修改他的行為 可以直接使用類裝飾器 """ 類裝飾器擴展 類功能 """ def log_getattribute(cls): old_getattribute = cls.__getattribute__ def new_getattribute(self, name): pri ......

    uj5u.com 2023-05-16 10:12:51 more
  • Python工具箱系列(三十二)

    Elasticsearch是一個基于Lucene的搜索引擎。它提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful 的API介面。Elasticsearch是用Java語言開發的,并作為Apache許可條款下的開放原始碼發布,是非常流行的企業級搜索引擎。 ......

    uj5u.com 2023-05-16 10:12:46 more
  • Springboot通過谷歌Kaptcha 組件,生成圖形驗證碼

    圖形驗證碼屬于老生常談了,具體細節這里就不說了。生成圖形驗證碼的辦法非常多,今天講解一種通過Kaptcha組件快速生成圖形驗證碼的方法。Kaptcha是谷歌開源的一款簡單實用的圖形驗證碼組件。我個人推薦它的最大原因是容易上手,采用約定大于配置的方式,快速契合到專案中。話不多說,我們看看該如何使用它: ......

    uj5u.com 2023-05-16 10:12:40 more
  • Python忽略NoData計算多張遙感影像的像元平均值:whitebox庫

    本文介紹基于Python中whitebox模塊,對大量長時間序列柵格遙感影像的每一個像元進行忽略NoData值的多時序平均值求取~ ......

    uj5u.com 2023-05-16 10:11:04 more
  • 序列檢測器(兩種設計方法和四種檢測模式|verilog代碼|Testbench|

    數字IC經典電路設計 經典電路設計是數字IC設計里基礎中的基礎,蓋大房子的第一部是打造結實可靠的地基,每一篇筆者都會分門別類給出設計原理、設計方法、verilog代碼、Testbench、仿真波形。然而實際的數字IC設計程序中考慮的問題遠多于此,通過本系列希望大家對數字IC中一些經典電路的設計有初步 ......

    uj5u.com 2023-05-16 10:05:24 more
  • AccessToken、for_user、get_token

    在Django REST framework的SimpleJWT庫中,AccessToken是一個類,用于表示一個JSON Web Token (JWT)中的訪問令牌部分。訪問令牌是一種常見的身份驗證令牌,通常用于保護API端點。 通過SimpleJWT庫,您可以使用AccessToken類創建、解 ......

    uj5u.com 2023-05-16 09:59:47 more
  • TokenObtainPairSerialize和TokenObtainPairView

    TokenObtainPairSerializer和TokenObtainPairView是Django REST framework的SimpleJWT庫提供的兩個相關的類。 TokenObtainPairSerializer是一個用于序列化和驗證用戶憑證以生成JSON Web Token(JWT ......

    uj5u.com 2023-05-16 09:59:43 more