Spring源码篇(十)@Bean怎么注册一个bean

news2024/11/23 20:06:39

文章目录

  • 前言
  • 配置类里的@Bean解析
  • sourceClass是什么
  • 解析@Bean方法
  • 添加@Bean注解的方法信息
  • 注册
  • 总结
    • @Bean注册的过程
    • 注意点

前言

配置类的解析之前有聊过,这篇也会涉及到一部分,因为@Bean本身也是配置类里的一个东西,本篇会着重解析@Bean注册bean的过程。

配置类里的@Bean解析

位置:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

这一部分就是解析配置类的流程

image-20230926101731272

底层的解析逻辑在:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

这里它解析了Component、componentScan、import、importSource、PropertySource、bean,那我们只看解析bean的部分.

sourceClass是什么

image-20230926102606825

这里我们会看到有一个sourceClass的对象,这个对象保存了配置类的资源,以及注解信息;

它怎么来的?

在外面那一层方法创建的

image-20230926161426852

image-20230926161907364

image-20230926162540389

上面这段,它做了一个判断,如果是java后缀结尾的化,就以Class.forName的方式加载class,然后解析得到StandardAnnotationMetadata对象,否则就是ASM根据类名解析class;

可是,在第二张图种,先判断了是否是StandardAnnotationMetadata类型,这个是因为,springBoot启动时我们的主函数类会进行注册,并且会通过JVM加载,而且只要是通过@Import引入的都会被JVM加载,也会变成StandardAnnotationMetadata,所以只要是直接引用了类,都会变成StandardAnnotationMetadata

sourceClass有两个类型:

  1. StandardAnnotationMetadata:标准的注解元数据信息,这个是由反射操作的,所以,它会被加载到JVM中;

  2. SimpleMetadataReader:这个是由ASM技术解析字节码得到的,不会加载到JVM中;

测试示例:

@Component
public class Test {

    @Bean
    public Amend amend() {
        return new Amend();
    }
}

public class Test2 {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {

        // ASM解析元数据
        MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        MetadataReader simpleMetadata = metadataReaderFactory.getMetadataReader("com.meiya.whalex.ay.datajoinup.Test");

        Set<MethodMetadata> simpleMetas = simpleMetadata.getAnnotationMetadata().getAnnotatedMethods("org.springframework.context.annotation.Bean");

        System.out.println("是否加载 Test.class :" + haveClass("com.meiya.whalex.ay.datajoinup.Test"));

        // 标准元数据获取
        AnnotatedTypeMetadata standardMetadata = AnnotationMetadata.introspect(Test.class);

        System.out.println("是否加载 Test.class :" + haveClass("com.meiya.whalex.ay.datajoinup.Test"));

        Set<MethodMetadata> standardMetas = ((StandardAnnotationMetadata) standardMetadata).getAnnotatedMethods("org.springframework.context.annotation.Bean");

        System.out.println();
    }

    private static boolean haveClass(String className) throws NoSuchFieldException, IllegalAccessException {
        Field classes = ClassLoader.class.getDeclaredField("classes");
        classes.setAccessible(true);
        Vector<Class> o = (Vector)classes.get(ClassLoader.getSystemClassLoader());
        System.out.println("包含class:" + o.size() +"个");
        for (int i = 0; i < o.size(); i++) {
            Class<?> o1 = o.get(i);
            if (o1.getName().equals(className)) {
                return true;
            }
        }
        return false;
    }
}

image-20230926155525283

这两种方式获取得到的注解信息差不多,都能获取到注解,方法,返回类型等信息,不同的点在于是否加载到JVM,通过上面的示例,可以看到,使用ASM技术解析的并不会加载class;

解析@Bean方法

进入retrieveBeanMethodMetadata方法,这里是对这个元数据进行解析,刚刚说过,这个sourceClass是包含注解元数据信息的,那么这里的这个方法就是解析含有@Bean注解的方法,位置:

org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    // 获取配置类里的注解元数据信息
		AnnotationMetadata original = sourceClass.getMetadata();
    // 获取标注有@Bean注解的方法元数据信息列表
		Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    // 存在@Bean标注的方法,并且,是属于是通过JVM加载的类才会进去
		if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
	// 这里就是ASM解析class(spring中解析class都是ASM,没有加载到JVM)
    // 因为判断中包含 StandardAnnotationMetadata 类型判断,这个类型是JVM加载后的类型
    // 虽然StandardAnnotationMetadata也能获取到注解元数据信息,但是因为JVM反射回来的元数据信息是无顺序的,在spring中@Bean注解是有顺序的,所以这里它做了ASM解析
			try {
				AnnotationMetadata asm =
						this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
				Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
				if (asmMethods.size() >= beanMethods.size()) {
					Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
					for (MethodMetadata asmMethod : asmMethods) {
						for (MethodMetadata beanMethod : beanMethods) {
							if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
								selectedMethods.add(beanMethod);
								break;
							}
						}
					}
					if (selectedMethods.size() == beanMethods.size()) {
						// All reflection-detected methods found in ASM method set -> proceed
						beanMethods = selectedMethods;
					}
				}
			}
			catch (IOException ex) {
				logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
				// No worries, let's continue with the reflection metadata we started with...
			}
		}
		return beanMethods;
	}

添加@Bean注解的方法信息

然后再回到一开始的那个地方:

位置:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

image-20230926163620569

这里可以看到,它再拿到@Bean注解标注的方法元数据后就直接添加到配置类的beanMethod集合中,并没有再进一步的操作;

上面这是一种情况,另外还有一种情况,就是接口上的@Bean

比如说像这样:

public interface Test6 {

    @Bean
    default Log og(){
        return new Log();
    }
}

其实这Log也能被注册上去的,如下:

image-20230927154232334

	private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        // 获取到配置类的接口列表,然后遍历
		for (SourceClass ifc : sourceClass.getInterfaces()) {
            // retrieveBeanMethodMetadata 和上面一样,就是解析@Bean的方法,然后生成beanMethod
			Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
			for (MethodMetadata methodMetadata : beanMethods) {
				if (!methodMetadata.isAbstract()) {
					// 添加非抽象的方法
					configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
				}
			}
			processInterfaces(configClass, ifc);
		}
	}

注册

再所有的配置类都解析完后,后面还有一个loadBeanDefinitions的操作,这个操作会加载由@Import,@ImportRsource,@Import(ImportBeanRegister)导入的类;

位置:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

image-20230926171415370

底层位置:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

        // 判断是否需要跳过,判断是否有@Import导入的类和@Conditional一类的条件注解,还有是否有@Bean的方法信息
		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

        // 这里是判断@Import导入的类是否存在
		if (configClass.isImported()) {
// 存在就注册
            registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
        // 这里是按顺序对@Bean注册类
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}


 // @ImportResource注册     
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        // @Import注册ImportBeanDefinitionRegistrar时
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

着重看下loadBeanDefinitionsForBeanMethod

它有几个步骤:

  1. 获取基础信息:配置类、@Bean方法元数据、方法名

  2. 是否跳过:Conditional判断

  3. 获取beanName:取@Bean属性name,如果没有,就取方法名作为beanName

  4. 注册别名:同样是@Bean属性name,如果存在就注册

  5. 是否覆盖的校验,它覆盖的规则是:

    1. 同样由@Bean方式注册的(ConfigurationClassBeanDefinition),同一个配置类中,beanName相同,最先注册的,也就是写在最前面的为准,写在后面的都不会注册
    2. 如果存在相同的bean,如果先注册的是通过class扫描注册的,继续注册
    3. 如果存在相同的bean,其权限不是应用级别(role != 0),继续注册
    4. 如果存在相同的bean,beanFactory的allowBeanDefinitionOverriding=false,默认false,那么报错
  6. 创建beanDefinition(ConfigurationClassBeanDefinition

    1. 设置方法元数据
    2. beanClassName(实例方法时设置)或者是beanClass(静态方法时设置)
    3. 工厂方法名
    4. 自动注入模式为构造器注入
    5. @Required校验开关,打开(true)
    6. 公共注解@Lazy,@Primary,@DependsOn,@Role,@Description这些注解的设置,设置到beanDefinition
    7. 设置是否自动注入autowire,默认不自动注入
    8. 设置initMethod``destroyMethod方法
    9. 设置代理模式,然后如果要进行代理,怎会创建一个proxy的beanDefinition
  7. 注册beanDefinition

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    // 1. 获取基础信息
    // 获取@Bean的配置类 
		ConfigurationClass configClass = beanMethod.getConfigurationClass();
    // 获取方法元数据信息
		MethodMetadata metadata = beanMethod.getMetadata();
    // 获取方法名
		String methodName = metadata.getMethodName();

		// 2. 是否需要跳过,也是Conditional判断
		if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
			configClass.skippedBeanMethods.add(methodName);
			return;
		}
		if (configClass.skippedBeanMethods.contains(methodName)) {
			return;
		}
    // 3. 获取beanName
		// 获取注解@Bean的属性信息
		AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
		Assert.state(bean != null, "No @Bean annotation attributes");

		// 找出name属性的值
    // 注意,@Bean的name属性是一个数组,也就是对于这个bean可以有多个别名
		List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    // 取name数组中的第一个为beanName,或者取方法名
		String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    // 4. 注册别名:为这个bean注册别名
		for (String alias : names) {
			this.registry.registerAlias(beanName, alias);
		}
	// 5. 是否覆盖的校验,它覆盖的规则是:
    // 判断已存在的bean否是配置类ConfigurationClassBeanDefinition(由@Bean注册的),且对应的配置类的全类名相同 return true(不继续注册)
    // 判断已存在的bean是否是扫描得到的beanDefinition(ScannedGenericBeanDefinition),return false(继续注册)
    // 判断已存在的bean是否是应用级别的bean(role == 0),role != 0  return false(继续注册)
    // 判断已存在的bean是否是bean工厂(DefaultListableBeanFactory),当allowBeanDefinitionOverriding=true时,return true, 默认false,(继续注册)
		if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
            // @Bean方法的beanName == 这个配置类的beanName
            // beanName不唯一,因为是同一个类里出现beanName不唯一的请情况,所以直接抛出异常
			if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
				throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
						beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
						"' clashes with bean name for containing configuration class; please make those names unique!");
			}
			return;
		}
	// 6. 创建beanMethod的beanDefinition
    // 6.1 设置方法元数据
    // 创建一个配置类的bean定义(ConfigurationClassBeanDefinition)
		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    // 这里其实就是用metadata进行赋值
		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
	// 6.2 beanClassName(实例方法时设置)或者是beanClass(静态方法时设置)
    // 6.3 设置工厂方法名
    // 从这里可以看出@Bean支持静态方法
		if (metadata.isStatic()) {
			// 设置配置类的class,或者是className,就看是否用ASM解析
			if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
				beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
			}
			else {
				beanDef.setBeanClassName(configClass.getMetadata().getClassName());
			}
            // 设置唯一的工厂方法名,也就是@Bean的方法名称
			beanDef.setUniqueFactoryMethodName(methodName);
		}
		else {
			// 非静态方法的@Bean方法
            // 设置工厂bean名称为配置类的名称
			beanDef.setFactoryBeanName(configClass.getBeanName());
            // 设置唯一的工厂方法名,也就是@Bean的方法名称
			beanDef.setUniqueFactoryMethodName(methodName);
		}

    // 这里是JVM加载后才会走的
		if (metadata instanceof StandardMethodMetadata) {
			beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
		}
	// 6.4 自动注入模式为构造器注入
// 设置注入模式为构造器注入
    beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		
    // 6.5 @Required校验开关,打开(true)
 // 指定RequiredAnnotationBeanPostProcessor后置处理器的校验跳过,
    // 也就是@Required的检查,不过这个注解后面是要被废弃的,我看的版本是5.2.6
    beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
				SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
// 6.6 设置公用注解的配置
    // @Lazy,@Primary,@DependsOn,@Role,@Description这些注解的设置,设置到beanDefinition
    
		AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

    // 6.7 @Bean里的autowire属性,默认不自动注入
    // 一般我们写@Bean的时候也是直接设置好了属性,但是这里可以通过设置@Bean的autowire属性,完成自动注入,但这样对于自定义的bean可能还是有限制
		Autowire autowire = bean.getEnum("autowire");
		if (autowire.isAutowire()) {
			beanDef.setAutowireMode(autowire.value());
		}

   // 可作为其他bean的注入的候选bean对象,@Bean中,这个属性默认为true
    // 所以,在某些场景中,我们可以设置这个属性为false,使之不能被其他bean注入
		boolean autowireCandidate = bean.getBoolean("autowireCandidate");
		if (!autowireCandidate) {
			beanDef.setAutowireCandidate(false);
		}
	// 6.8 设置`initMethod``destroyMethod`方法
    // 设置initMethod方法,这个是在bean生命周期中,在bean初始化后会调用的一个方法
    // 可以理解为一个bean创建的回调方法
		String initMethodName = bean.getString("initMethod");
		if (StringUtils.hasText(initMethodName)) {
			beanDef.setInitMethodName(initMethodName);
		}

    // 设置destroyMethod方法,同样是在bean生命周期中会被调用的一个方法
		String destroyMethodName = bean.getString("destroyMethod");
		beanDef.setDestroyMethodName(destroyMethodName);

    // 6.9 设置代理模式
		// ScopedProxyMode代理模式,可以设置jdk动态代理,cglib动态代理
    // 默认是不进行代理
		ScopedProxyMode proxyMode = ScopedProxyMode.NO;
    // 如果这个@Bean的方法有配置@Scope注解,那么会获取到,然后赋值
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
		if (attributes != null) {
            // 存在@Scope,获取值并设置到beanDefinition
			beanDef.setScope(attributes.getString("value"));
            // 赋值
			proxyMode = attributes.getEnum("proxyMode");
			if (proxyMode == ScopedProxyMode.DEFAULT) {
				proxyMode = ScopedProxyMode.NO;
			}
		}

		// 如果需要代理,那么就会创建一个proxy的BeanDefinition
		BeanDefinition beanDefToRegister = beanDef;
		if (proxyMode != ScopedProxyMode.NO) {
			BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
					new BeanDefinitionHolder(beanDef, beanName), this.registry,
					proxyMode == ScopedProxyMode.TARGET_CLASS);
			beanDefToRegister = new ConfigurationClassBeanDefinition(
					(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
		}

		if (logger.isTraceEnabled()) {
			logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
					configClass.getMetadata().getClassName(), beanName));
		}
    // 7 进行注册
		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
	}

总结

@Bean注册的过程

@Bean注入类的过程:

  1. 配置类解析,解析@ComponentScan,@ImportSource,@Import,@PropertySource,@Bean

  2. 添加:添加解析@Bean得到的BeanMethod到配置类中,包含接口里的默认方法

  3. 注册:在所有的配置类都解析完后,执行loadBeanDefinitions,遍历beanMethod

    1. 获取基础信息:配置类、@Bean方法元数据、方法名

    2. 是否跳过:Conditional判断

    3. 获取beanName:取@Bean属性name,如果没有,就取方法名作为beanName

    4. 注册别名:同样是@Bean属性name,如果存在就注册

    5. 是否覆盖的校验,它覆盖的规则是:

      1. 同样由@Bean方式注册的(ConfigurationClassBeanDefinition),同一个配置类中,beanName相同,最先注册的,也就是写在最前面的为准,写在后面的都不会注册
      2. 如果存在相同的bean,如果先注册的是通过class扫描注册的,继续注册
      3. 如果存在相同的bean,其权限不是应用级别(role != 0),继续注册
      4. 如果存在相同的bean,beanFactory的allowBeanDefinitionOverriding=false,默认false,那么报错
    6. 创建beanMethod的beanDefinition(ConfigurationClassBeanDefinition

      1. 设置方法元数据
      2. beanClassName(实例方法时设置)或者是beanClass(静态方法时设置)
      3. 工厂方法名
      4. 自动注入模式为构造器注入
      5. @Required校验开关,打开(true)
      6. 公共注解@Lazy,@Primary,@DependsOn,@Role,@Description这些注解的设置,设置到beanDefinition
      7. 设置是否自动注入autowire,默认不自动注入
      8. 设置initMethod``destroyMethod方法
      9. 设置代理模式,然后如果要进行代理,怎会创建一个proxy的beanDefinition
    7. 注册beanDefinition

  4. bean生命周期 -> 实例化、初始化、代理、添加单例池

注意点

  1. @Bean标注的方法都在同一个配置类中,出现相同beanName,那么优先级以最先写的方法为准,后面的方法不会进行注册

  2. @Bean标注的方法不再同一个配置中,相同的beanName,属于重写注册的bean,需要设置spring.main.allowBeanDefinitionOverriding=true

  3. @Bean标注的方法可以时static修饰的(静态方法)

  4. @Bean默认是不自动注入的,但是它可以通过设置属性autowire完成自动注入

  5. 如果配置类实现里某个接口,而这个接口存在默认方法,且有@Bean注解,也会被注册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1050266.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

区块链(6):p2p去中心化介绍

1 互联网中中心化的服务和去中心化服务的概念介绍 目前的互联网公司大都是中心化的 区块链网络大多是去中心化的 去中心化 2 p2p的简单介绍 java 网络编程:socket编程,netty编程,websoket简单介绍 2.1 节点是如何提供服务的(web编程实现)

qml保姆级教程一:布局组件

&#x1f482; 个人主页:pp不会算法v &#x1f91f; 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 QML系列教程 QML教程一&#xff1a;布局组件 文章目录 锚布局anchors属…

前端开发 vs. 后端开发:编程之路的选择

文章目录 前端开发&#xff1a;用户界面的创造者1. HTML/CSS/JavaScript&#xff1a;2. 用户体验设计&#xff1a;3. 响应式设计&#xff1a;4. 前端框架&#xff1a; 后端开发&#xff1a;数据和逻辑的构建者1. 服务器端编程&#xff1a;2. 数据库&#xff1a;3. 安全性&#…

CISSP学习笔记:人员安全和风险管理概念

第二章 人员安全和风险管理概念 2.1 促进人员安全策略 职责分离: 把关键的、重要的和敏感工作任务分配给若干不同的管理员或高级执行者&#xff0c;防止共谋工作职责:最小特权原则岗位轮换:提供知识冗余&#xff0c;减少伪造、数据更改、偷窃、阴谋破坏和信息滥用的风险&…

CSS详细基础(四)显示模式

本帖开始介绍CSS中更复杂的内容 目录 一.显示模式 1.行内元素 2.块级元素 3.行内块元素 二.背景样式 一.显示模式 顾名思义&#xff0c;在CSS中&#xff0c;元素主要有3种显示模式&#xff1a;行内元素、块级元素、行内块元素~ 所谓块级元素&#xff0c;指的是该元素在…

Springboot+vue的企业人事管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频: Springbootvue的企业人事管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的企业人事管理系统&#xff0c;采用M&#xff08;model&am…

【kylin】【ubuntu】搭建本地源

文章目录 一、制作一个本地源仓库制作ubuntu本地仓库制作kylin本地源 二、制作内网源服务器ubuntu系统kylin系统 三、使用内网源ubuntukylin 一、制作一个本地源仓库 制作ubuntu本地仓库 首先需要构建一个本地仓库&#xff0c;用来存放软件包 mkdir -p /path/to/localname/pac…

基于微信小程序的手机在线商城小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

摩根大通限制英国客户购买加密货币,市场掀起涟漪!

摩根大通旗下英国数字银行部门宣布&#xff0c;从下个月开始&#xff0c;将禁止客户进行加密货币交易。这一决定归因于人们越来越担心与加密货币相关的诈骗和欺诈行为的增加。 正如该银行周二表示的那样&#xff0c;从10月16日起&#xff0c;该银行的客户将不再可以选择通过借…

Bee2.1.8支持Spring Boot 3.0.11,active命令行选择多环境,多表查改增删(bee-spring-boot发布,更新maven)

天下大势&#xff0c;分久必合&#xff01; Hibernate/MyBatis plus Sharding JDBC Jpa Spring data GraphQL App ORM (Android, 鸿蒙) Bee Spring Cloud 微服务使用数据库更方便&#xff1a;Bee Spring Boot; 轻松支持多数据源&#xff0c;Sharding, Mongodb. 要整合一堆的…

数据大帝国:大数据与人工智能的巅峰融合

文章目录 大数据与人工智能&#xff1a;概念解析大数据与人工智能的融合1. 数据驱动的决策2. 自然语言处理&#xff08;NLP&#xff09;3. 图像识别与计算机视觉4. 智能推荐系统5. 医疗诊断和生命科学 数据大帝国的未来展望1. 智能城市2. 区块链和数据安全3. 自动化和机器人4. …

分布式搜索引擎es-3

文章目录 数据聚合聚合的种类RestAPI实现聚合 数据聚合 什么是聚合&#xff1f; 聚合可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f;这些手机的平均价格、最高价格、最低价格&#xff1f;这些手机每月的销售情况如…

Linux CentOS7 vim临时文件

在vim中&#xff0c;由于断网、停电、故意退出、不小心关闭终端等多种原因&#xff0c;正在编辑的文件没有保存&#xff0c;系统将会为文件保存一个交换文件&#xff0c;或称临时文件&#xff0c;或备份文件。 如果因某种原因产生了交换文件&#xff0c;每次打开文件时&#x…

多线程总结(线程池 线程安全 常见锁)

本篇文章主要是对线程池进行详解。同时引出了单例模式的线程池&#xff0c;也对线程安全问题进行了解释。其中包含了智能指针、STL容器、饿汉模式的线程安全。也对常见的锁&#xff1a;悲观锁&#xff08;Pessimistic Locking&#xff09;、乐观锁&#xff08;Optimistic Locki…

使用GDIView排查GDI对象泄漏导致的程序UI界面绘制异常问题

目录 1、问题说明 2、初步分析 3、查看任务管理器&#xff0c;并使用GDIView工具分析 4、GDIView可能对Win10兼容性不好&#xff0c;显示的GDI对象个数不太准确 5、采用历史版本比对法&#xff0c;确定初次出现问题的时间点&#xff0c;并查看前一天的代码修改记录 6、将…

visual studio下载安装

一、官网下载 地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/ 点击免费visual studio 二、安装 运行下载好的exe文件&#xff0c;自定义安装目录 三、选择需要的组件安装 只需要选择标记组件&#xff0c;然后点击安装 等待安装完成就行 四、重启电脑 安装完之后…

windows系统关闭软件开机自启的常用两种方法

win10中安装软件时经常会默认开机自启动&#xff0c;本文主要介绍两种关闭软件开机自启动方法。 方法1 通过任务管理器设置 1.在任务管理器中禁用开机自启动&#xff1a;打开任务管理器&#xff0c;右键已启动的软件&#xff0c;选择禁用。 方法2 通过windows服务控制开机自启…

SpringBoot——常用注解

Spring Web MVC与Spring Bean注解 Controller/RestController Controller是Component注解的一个延伸&#xff0c;Spring 会自动扫描并配置被该注解标注的类。此注解用于标注Spring MVC的控制器。 Controller RequestMapping("/api/v1") public class UserApiContr…

ssm+vue的OA办公管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的OA办公管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&am…

【Java 集合】准备知识

目录 初识集合框架 什么是集合框架 包装器 1. 基本数据类型和对应的包装器 2. 装箱和拆箱 3. 自动装箱和拆箱 4. Integer 存储机制 5. 包装器的作用 泛型 1. 什么是泛型 2. 引出泛型 2.1 泛型语法 3. 泛型类的使用 4. Java泛型实现的机制 -- 擦除机制 5. 泛型的…