Spring源碼:Bean生命周期(四)

来源:https://www.cnblogs.com/guoxiaoyu/archive/2023/05/15/17402789.html
-Advertisement-
Play Games

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


前言

在之前的文章中,我們介紹了 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最新擴展等你來看!原創編寫不易,轉載請說明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.下載maven 方式一: 官網下載所需要的版本,官網地址:https://maven.apache.org/ 方式二: 百度網盤鏈接下載3.6.1版,鏈接:鏈接:https://pan.baidu.com/s/16IuluK4oo3K8kMG9B_SV3Q?pwd=35un 提取碼:35un 下 ...
  • 教程簡介 Java併發入門教程 - 從簡單的步驟瞭解Java併發,從基本到高級概念,包括概述,環境設置,主要操作,線程通信,同步,死鎖,ThreadLocal,ThreadLocalRandom,Lock,ReadWriteLock,Condition,AtomicInteger, AtomicLo ...
  • TokenObtainPairSerializer和TokenObtainPairView是Django REST framework的SimpleJWT庫提供的兩個相關的類。 TokenObtainPairSerializer是一個用於序列化和驗證用戶憑證以生成JSON Web Token(JWT ...
  • 我們在使用mq的時候,就會很自然思考一個問題:怎麼保證數據不丟失? 現在austin接入層是把消息發到mq,下發邏輯層從mq消費數據,隨後調用對應渠道介面來下發消息。 消息推送平臺🔥推送下發【郵件】【簡訊】【微信服務號】【微信小程式】【企業微信】【釘釘】等消息類型。 https://gitee.c ...
  • 教程簡介 IDEA 全稱 IntelliJ IDEA,是java編程語言的集成開發環境。IntelliJ在業界被公認為最好的Java開發工具,尤其在智能代碼助手、代碼自動提示、重構、JavaEE支持、各類版本工具(git、svn等)、JUnit、CVS整合、代碼分析、 創新的GUI設計等方面的功能可 ...
  • 元語言抽象就是建立新的語言。它在工程設計的所有分支中都扮演著重要的角色,在電腦程式設計領域更是特別重要。因為這個領域中,我們不僅可以設計新的語言,還可以通過構造求值器的方式實現這些語言。對某個程式設計語言的求值器(或者解釋器)也是一個過程,在應用於這個語言的一個表達式時,它能夠執行求值這個表達式所... ...
  • 本文將深入探討 AM 向 RM 申請並獲得 Container 資源後,在 NM 節點上如何啟動和清理 Container。將詳細分析整個過程的源碼實現。 ...
  • voidint/g g 是一個 Linux、macOS、Windows 下的命令行工具,可以提供一個便捷的多版本 go 環境的管理和切換。 在這裡我們介紹一下在 windows 下的使用,涉及到我們開發所需要用到的 幾個 go 項目層環境變數它們分別是 GOPATH,GOPROXY,GO111MOD ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...