【spring源码系列-04】注解方式启动spring时refresh的前置工作

news2024/11/23 15:03:44

Spring源码系列整体栏目


内容链接地址
【一】spring源码整体概述https://blog.csdn.net/zhenghuishengq/article/details/130940885
【二】通过refresh方法剖析IOC的整体流程https://blog.csdn.net/zhenghuishengq/article/details/131003428
【三】xml配置文件启动spring时refresh的前置工作https://blog.csdn.net/zhenghuishengq/article/details/131066637
【四】注解方式启动spring时refresh的前置工作https://blog.csdn.net/zhenghuishengq/article/details/131113249

注解方式启动spring时refresh的前置工作

  • 一,注解的方式启动spring时refresh的前置工作
    • 1,this()
    • 2,register(annotatedClasses)
    • 3,总结

一,注解的方式启动spring时refresh的前置工作

上一篇中提到了xml的方式启动spring,接下来通过注解方式来剖析spring内部的启动流程。

在这里插入图片描述

通过注解获取上下文的方式如下,随后定义一个AnnotationConfig的配置类,通过@Bean的方式构建实例。这里依旧推荐使用debug的方式,从上往下看。

ApplicationContext ac = new AnnotationConfigApplicationContext(AnnotationConfig.class);

随后会进入AnnotationConfigApplicationContext 的构造方法,并且可以发现支持传多个参数。由于这里也是剖析refresh的前置工作,因此这里重点主要是查看this()register 这两个方法。

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	this();      //调用构造函数
	register(annotatedClasses);   //注册我们的配置类
	refresh();   //IOC容器刷新接口
}

1,this()

首先会调用 AnnotationConfigApplicationContext 自身的构造方法,也可以通过上图的执行流程进行对照。

public AnnotationConfigApplicationContext() {
    //创建一个读取注解的Bean定义读取器
    this.reader = new AnnotatedBeanDefinitionReader(this);
    //创建类路径下的BeanDefinition扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

在分析这个AnnotationConfigApplicationContext类之前,先来看一下这个类的类图,发现也是众多接口的具体的子类,同时也说明该类的功能非常的齐全和强大

在这里插入图片描述

1,在实例化AnnotationConfigApplicationContext类之前,需要先实例化其父类。由于这个ResourceLoader 是一个接口,没有对应的构造方法,根据断点也能发现,会要优先加载DefaultResourceLoader的构造器。在这个构造方法中,可以发现里面会调用一个getDefaultClassLoader方法

public class DefaultResourceLoader implements ResourceLoader {
    ...
	public DefaultResourceLoader() {
		this.classLoader = ClassUtils.getDefaultClassLoader();
	}
}

getDefaultClassLoader 顾名思义,就是获取默认的类加载器。在jvm中,类加载器主要有引导类加载器、扩展类加载起、系统类加载器和自定义类加载器。获取已有的类加载器,如果获取的类加载器为空,则设置默认的类加载器为系统类加载器

public static ClassLoader getDefaultClassLoader() {
    ClassLoader cl = null;
	//获取当前线程的类加载器
    cl = Thread.currentThread().getContextClassLoader();
    if (cl == null) {
        //如果获取到的值为空,则将类加载器设置为系统类加载器
        cl = ClassLoader.getSystemClassLoader();
    }
}

2,获取到类加载器之后,根据debug的断点继续往下走,可以发现下一步是去获取一个 LogFactory

public static Log getLog(Class<?> clazz) {
   return getLog(clazz.getName());
}

随后进入具体获取日志的方法,创建具体的日志,其内部流程也非常复杂

public static Log getLog(String name) {
	switch (logApi) {
		case LOG4J:
			return Log4jDelegate.createLog(name);
		case SLF4J_LAL:
			return Slf4jDelegate.createLocationAwareLog(name);
		case SLF4J:
			return Slf4jDelegate.createLog(name);
		default:
			return JavaUtilDelegate.createLog(name);
	}
}

系统默认使用的是 LOG4J ,接下来进入这个 Log4jDelegate.createLog 这个方法,里面会创建并返回一个Log4jLog 对象

return new Log4jLog(name);

而在这个Log4jLog 类中,其构造方法如下,即会调用这个 getLogger 方法

//获取日志上下文
public Log4jLog(String name) {
	this.logger = loggerContext.getLogger(name);
}

随后进入真正的获取日志的方法

public ExtendedLogger getLogger(final String name, final MessageFactory messageFactory) {
    //获取日志工厂,无则创建,有则获取
	final ExtendedLogger extendedLogger = loggerRegistry.getLogger(name, messageFactory);
	if (extendedLogger != null) {
        //检查创建的日志工厂个给定的消息工厂是否一致
		AbstractLogger.checkMessageFactory(extendedLogger, messageFactory);
		return extendedLogger;
	}
    //如果获取的工厂为空,
	final SimpleLogger simpleLogger = new SimpleLogger(name, defaultLevel, showLogName, showShortName, showDateTime,
		showContextMap, dateTimeFormat, messageFactory, props, stream);
	loggerRegistry.putIfAbsent(name, messageFactory, simpleLogger);
	return loggerRegistry.getLogger(name, messageFactory);
}

如果获取到的日志工厂为空,则会调用这这个 SimpleLogger 方法

在这里插入图片描述

随后调用这个super父类方法,会判断当前的消息工厂是否为空,为空则创建一个默认的消息工厂

//初始化父类
public AbstractLogger(final String name, final MessageFactory messageFactory) {
	this.name = name;
    //不为空则获取,为空则创建一个默认的
	this.messageFactory = messageFactory == null ? createDefaultMessageFactory() : narrow(messageFactory);
	this.flowMessageFactory = createDefaultFlowMessageFactory();
}

其创建方式如下,通过构造器的反射的方式创建一个消息的日志

private static MessageFactory2 createDefaultMessageFactory() {
    try {
        final MessageFactory result = DEFAULT_MESSAGE_FACTORY_CLASS.newInstance();
        return narrow(result);
    } catch (final InstantiationException | IllegalAccessException e) {
        throw new IllegalStateException(e);
    }
}

3,在调完super之后,随后就是一个获取系统属性的一个方法

final String lvl = props.getStringProperty(SimpleLoggerContext.SYSTEM_PREFIX + name + ".level");

接下来继续往下走,可以发现是一个PropertiesUtil工具类里面的这个getStringProperty方法,而里面的重点就是这个environment 对象的由来

public String getStringProperty(final String name) {
    return environment.get(name);
}

可以发现这个event环境对象,是通过这个实例化Environment对象而来

public PropertiesUtil(final Properties props) {
    this.environment = new Environment(new PropertiesPropertySource(props));
}

public PropertiesUtil(final String name) {
	this.environment = new Environment(new PropertyFilePropertySource(name));
}

再进入到这个Environment 的构造方法中,可以发现在这一步,就开始获取系统的变量和实现

private Environment(final PropertySource propertySource) {
	sources.add(propertySource);
	for (final ClassLoader classLoader: LoaderUtil.getClassLoaders()) {
		try {
			for (final PropertySource source: ServiceLoader.load(PropertySource.class, classLoader)) {
				sources.add(source);
			}
		} catch (final Throwable ex) {
		}
	}

	reload();
}

并且在这个类中,有着四个属性,literal存储集合存储系统的属性,tokenized存储系统的环境变量,normalized存储日志相关的参数。继续往下走可以看到这些参数对应的值

在这里插入图片描述

Set<PropertySource> sources = new TreeSet<>(new PropertySource.Comparator());
Map<CharSequence, String> literal = new ConcurrentHashMap<>();
Map<CharSequence, String> normalized = new ConcurrentHashMap<>();
Map<List<CharSequence>, String> tokenized = new ConcurrentHashMap<>();

环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMjEF0JT-1686218443841)(img/1686195259391.png)]

系统属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyNLSiZa-1686218443842)(img/1686195284361.png)]

日志属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X27OwWkx-1686218443842)(img/1686195303051.png)]

在获取到这些属性之后,会对刚刚设置的值进行判断,看设置的值是包含在系统参数中,如果包含,就将这些参数加入到缓存中。

public static List < CharSequence > tokenize(final CharSequence value) {
	if (CACHE.containsKey(value)) {
		return CACHE.get(value);
	}
	final List < CharSequence > tokens = new ArrayList < > ();
	final Matcher matcher = PROPERTY_TOKENIZER.matcher(value);
	while (matcher.find()) {
		tokens.add(matcher.group(1).toLowerCase());
	}
    //存入缓存
	CACHE.put(value, tokens);
	return tokens;
}

4,在获取完系统环境和属性之后,通过打断点继续走,再次回到了这个AbstractApplicationContext 类中,获取当前对象的hashcode对应的value

private String id = ObjectUtils.identityToString(this);

identityToString方法中可以发现,就是一字符串的形式返回对象的标识,就是通过对象的全路径名称的类名加上一个@一个该类对应的hashcode

public static String identityToString(@Nullable Object obj) {
	if (obj == null) {
		return EMPTY_STRING;
	}
    //对象名称 + @ + 获取到的hashcode对应的十六进制的值
	return obj.getClass().getName() + "@" + getIdentityHexString(obj);
}

将对象的hashcode的值先进行一个十六进制的转换,后面将值给返回

//将对象的hashcode转换成十六进制,并将值返回
public static String getIdentityHexString(Object obj) {
	return Integer.toHexString(System.identityHashCode(obj));
}

如下图,这个id的值就是当前对象的唯一标识,就是当前对象对应的唯一值hashcode哈希码,这样对象的哈希code对应的哈希value的值就有了,即这一步的主要作用是给当前对象的hashcode生成一个hash值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlaH3V2m-1686218443843)(img/1686190434912.png)]

获取完这个唯一标识哈希值后,会接着获取一个名称,该名称和上面的id值一模一样

private String displayName = ObjectUtils.identityToString(this);

5,跟着断点继续往下走,接下来就是实例化这个构造方法,到了这一步和解析xml启动流程的一样,也是有一个默认的AntPathMatcher进行路径匹配

protected ResourcePatternResolver getResourcePatternResolver() {
	return new PathMatchingResourcePatternResolver(this);
}

这里主要是为了获取资源处理器和加载器,用于加载一些资源和加载一些类加载器等

private PathMatcher pathMatcher = new AntPathMatcher();
//获取资源加载器
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
	Assert.notNull(resourceLoader, "ResourceLoader must not be null");
	this.resourceLoader = resourceLoader;
}

6,接下来继续往下debug,可以发现会进入 GenericApplicationContext 这个构造方法,从这里开始,就构建了这默认的bean工厂DefaultListableBeanFactory

public GenericApplicationContext() {
    //构建默认的Bean工程
	this.beanFactory = new DefaultListableBeanFactory();
}

其类图如下,可以发现其功能是有多么的强大

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8U5ureQx-1686218443844)(img/1685349283411.png)]

这个默认的BeanFactory的构造方法如下,只是初始化了父类的构造方法

public DefaultListableBeanFactory() {
	super();
}

接下来查看其父类的构造方法,除了忽略一些依赖接口之外,也是只初始化了父类

public AbstractAutowireCapableBeanFactory() {
	super();
    //忽略的依赖接口
	ignoreDependencyInterface(BeanNameAware.class);
	ignoreDependencyInterface(BeanFactoryAware.class);
	ignoreDependencyInterface(BeanClassLoaderAware.class);
}

随后调用的父类的构造方法如下,该类是一个抽象类

public AbstractBeanFactory() {
}

随后再次根据断点进入 DefaultSingletonBeanRegistry 这个类里面,里面包含存储对象缓存池

//一级缓存 这个就是我们大名鼎鼎的单例缓存池 用于保存我们所有的单实例bean
private final Map < String, Object > singletonObjects = new ConcurrentHashMap < > (256);
//三级缓存 该map用户缓存 key为 beanName  value 为ObjectFactory(包装为早期对象)
private final Map < String, ObjectFactory << ? >> singletonFactories = new HashMap < > (16);
//二级缓存 ,用户缓存我们的key为beanName value是我们的早期对象(对象属性还没有来得及进行赋值) 
private final Map < String, Object > earlySingletonObjects = new HashMap < > (16);
//已注册的单例名称set
private final Set < String > registeredSingletons = new LinkedHashSet < > (256);
//该集合用户缓存当前正在创建bean的名称
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
//排除当前创建检查的
	private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
...

至此,bean工厂就初始化完成

7,AnnotationConfigApplicationContext 的父类构造器全部执行完成之后,就会再次回到当前类的构造器

public AnnotationConfigApplicationContext() {
    //创建一个读取注解的Bean定义读取器
    //完成了spring内部BeanDefinition的注册(主要是后置处理器)
	this.reader = new AnnotatedBeanDefinitionReader(this);
	//创建BeanDefinition扫描器
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}

8,先看这个AnnotatedBeanDefinitionReader ,见名知意就知道这是创建一个bean定义的读取器

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
	this(registry, getOrCreateEnvironment(registry));
}

接下来进入这个this方法,熟悉获取环境环节又出现了,在上面就已经获取到了系统的全部环境个属性,所以这里获取返回即可。

private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    //如果Environment存在,则获取直接返回
	if (registry instanceof EnvironmentCapable) {
		return ((EnvironmentCapable) registry).getEnvironment();
	}
    //不存在则创建一个标准的环境
	return new StandardEnvironment();
}

如何创建一个标准环境,获取全部的系统环境和系统属性,在xml分析流程的时候详细的讲过。获取完标准环境之后,会将这个获取到的上下文的对象,赋值给read读取器,并处理一些条件注解,以及一些后置处理器等

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	Assert.notNull(environment, "Environment must not be null");
	//把ApplicationContext对象赋值给AnnotatedBeanDefinitionReader
	this.registry = registry;
	//用户处理条件注解 @Conditional os.name
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
	//注册一些内置的后置处理器
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

条件处理,如一些条件判断等,判断一些环境设置,资源加载对象,类加载器对象等

public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
	@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
	//ioc 容器applicationContext对象
	this.registry = registry;
	//bean工厂对象
	this.beanFactory = deduceBeanFactory(registry);
	//设置环境对象
	this.environment = (environment != null ? environment : deduceEnvironment(registry));
	//资源加载对象
	this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
	//类加载器对象
	this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
}

这里就是去注册一些后置处理器

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
	registerAnnotationConfigProcessors(registry, null);
}

如注册实现Order接口,加了@Lazy的注解,加了@Autowired注解,@Required属性,JSR规范的注解等等。会将实现了这些注解的接口,或者有这些属性的参数等,会注册成一个 BeanDefinition 先存在Set集合中

在这里插入图片描述

10,再看这个 ClassPathBeanDefinitionScanner 对象,首先会调用他的构造方法实例化对象

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
	this(registry, true);
}

一直到调用四个参数的构造方法

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
	Environment environment, @Nullable ResourceLoader resourceLoader) {
	this.registry = registry;
	if (useDefaultFilters) {
		registerDefaultFilters();
	}
	//设置环境对象
	setEnvironment(environment);
	//设置资源加载器
	setResourceLoader(resourceLoader);
}

首先会注册一个默认的过滤器registerDefaultFilters 如下

在这里插入图片描述

在这个类中,会定义两个集合,这个就和springboot的主启动器那里的一样,可以排除哪些类不被扫描

//包含的集合
private final List<TypeFilter> includeFilters = new LinkedList<>();
//排除的集合
private final List<TypeFilter> excludeFilters = new LinkedList<>();

在这个 registerDefaultFilters 方法中,第一句就是将有Component注解的类加进来,所以在这一步全部加了这个注解的都会被扫描,并生成bean定义,其他的就是一些JSR规范等

//加入扫描我们的@Component的
this.includeFilters.add(new AnnotationTypeFilter(Component.class));

注册完默认的过滤器之后,接下来就是设置环境对象,设置加载资源等,这些前面都已经拿到。至此整个扫描阶段完成。除了这个@Component这个注解会被扫描,@Service @Respository @Controller等这些注解在这个类下面都会被扫描

至此,第一阶段super阶段结束,这个阶段主要做的事情总结如下:获取整个系统的类加载器,注册factoryLog日志工厂,获取整个系统的环境和属性,创建对象的哈希码,构建默认的bean工厂,将一些条件注解、后置处理器等读取成beanDefinition,将一些常用的注解扫描成bean定义

2,register(annotatedClasses)

上面通过这个reader读取器和scan扫描器将一些注解都转换成了beanDefinition,接下来要做的事情就是将这些bean定义注册。

//定义读取器
private final AnnotatedBeanDefinitionReader reader;
public void register(Class<?>... annotatedClasses) {
	Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
	this.reader.register(annotatedClasses);
}

其注册方式如下,会遍历所有的这个注解类

public void register(Class<?>... annotatedClasses) {
	for (Class<?> annotatedClass : annotatedClasses) {
		registerBean(annotatedClass);
	}
}

public void registerBean(Class<?> annotatedClass) {
	doRegisterBean(annotatedClass, null, null, null);
}

接下来进入真正的注册方法 doRegisterBean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B6CixSkX-1686218443845)(img/1686216325343.png)]

首先第一步会创建一个 AnnotatedGenericBeanDefinition 的类,用于存储@Configuration注解的类

public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
	setBeanClass(beanClass);
	this.metadata = new StandardAnnotationMetadata(beanClass, true);
}

随后会判断是否要跳过注解,如一些不满足条件的,@Condition注解

//判断是否需要跳过注解,spring中有一个@Condition注解,当不满足条件,这个bean就不会被解析
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
	return;
}

随后设置Bean的作用域,默认为单例

//解析bean的作用域,如果没有设置的话,默认为单例
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());

随后解析一些通用注解,将这些注解填充到原先生成的bean定义当中

//解析通用注解,填充到AnnotatedGenericBeanDefinition,
//解析的注解为Lazy,Primary,DependsOn,Role,Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

最后一步进行注册操作,

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

其注册成bean定义的方式如下,

//将bean定义注册到bean工厂中
public static void registerBeanDefinition(
	BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

	// 在主名称中注册bean定义
	String beanName = definitionHolder.getBeanName();
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// 通过别名注册成bean定义
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias: aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

注册bean定义的registerBeanDefinition方法如下

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {
    ...
}

如果这个bean定义已经存在,其内部又大量的条件判断,如bean定义的名称不能相同,权限大的优先存在不能被覆盖等等

在这里插入图片描述

如果bean定义不存在,会将这个beanDefinition加入到 beanDefinitionMap 集合中

this.beanDefinitionMap.put(beanName, beanDefinition);

在这里插入图片描述

如果当前map集合中没有这个bean定义,且这个bean定义是单例的,则会将之前的reset,就是将之前的bean定义给删除,如下,会现将以前的单例bean定义删除,随后再将这个最新的单例bean定义加入到map集合中

protected void resetBeanDefinition(String beanName) {
	clearMergedBeanDefinition(beanName);
	destroySingleton(beanName);

	// Reset all bean definitions that have the given bean as parent (recursively).
	for (String bdName : this.beanDefinitionNames) {
		if (!beanName.equals(bdName)) {
			BeanDefinition bd = this.beanDefinitionMap.get(bdName);
			if (beanName.equals(bd.getParentName())) {
				resetBeanDefinition(bdName);
			}
		}
	}
}

至此,beanDefinition的注册阶段完成。注册阶段的总结如下:就是将一些beanDefinition进行一些解析,设置一些属性和作用域,对一些bean定义进行验证是否需要注册,注册时需要验证会不会覆盖问题,注册后将beanDefinition存储到beanDefinitionMap中

3,总结

通过上面两步可以发现,用注解的方式来作为获取上下文并且启动spring,其复杂度远远超过使用xml的方式,并且xml方式里面有的步骤这里面全有。

这两部总结到一起如下:获取整个系统的类加载器,注册factoryLog日志工厂,获取整个系统的环境和属性,构建默认的bean工厂,将一些条件注解、后置处理器等读取成beanDefinition,将一些常用的注解扫描成beanDefinition,随后将这些beanDefinition进行解析,设置属性和作用域,验证是否需要注册,注册时需要验证会不会覆盖问题等操作,注册后将beanDefinition存储到beanDefinitionMap中

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

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

相关文章

第五十回:TabBarView Widget

文章目录 概念介绍使用方法示例代码综合使用 我们在上一章回中介绍了DefaultTabBarController Widget相关的内容,本章回中将介绍 TabBarView Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们这里介绍的TabBarView类似前面章回中介绍过的PageView组件&a…

应对数据不平衡和过拟合的分类模型优化策略

不平衡分类 数据类别不平衡问题是指数据集中各类别样本数量不对等的情况。 基于抽样的方法 在处理这类问题时&#xff0c;可以采用基于抽样的方法来解决。以下是几种常见的基于抽样的方法&#xff1a; 两阶段学习 两阶段学习是一种解决不平衡分类问题的方法&#xff0c;包括…

软件测试 之Web项目实战解析(附全套实战项目教程+视频+源码)

软件测试之web项目实战 按顺序依次为&#xff1a;【搭建测试环境】、【需求评审】、【编写测试计划】、【分析测试点.编写测试用例】、【用例评审】、【执行用例提bug】、【测试报告】 一&#xff1a;搭建测试环境 (1) 搭建测试环境之 【常见项目结构模式】 &#xff08;2&am…

【大数据之路3】分布式协调系统 Zookeeper

3. 分布式协调系统 Zookeeper 1. Zookeeper 概述1. Zookeeper 介绍2. Zookeeper 结构/功能【重点】1. 文件系统 ZNode1. ZNode 特点2. ZNode 功能3. ZNode 介绍【非常重要】 2. 监听机制 3. 典型应用场景1. 命名服务2. 配置管理3. 集群管理4. 分布式锁5. 队列管理 2. 架构与原理…

MaskRCNN与注意力机制

Mask RCNN---two stage mask rcnn是一个分割算法(实例分割)&#xff0c;可用于&#xff1a; 目标检测 实例分割 关键点检测 本质上&#xff0c;mask R-CNN是在faster rcnn的基础上&#xff0c;加入了FCN模块&#xff0c;得到最终的分割结果。 先检测&#xff0c;再分割。不…

实战:在Docker上部署Springboot项目(附源码)

实战&#xff1a;在Docker上部署Springboot项目&#xff08;附源码&#xff09; 1、docker的基本使用 1、为什么使用docker 2、docker的介绍 3、docker安装 https://www.codezhou.top/article/docker%E4%BD%BF%E7%94%A8 2、dockers安装mysql 拉取 Mysql 5.7.31 镜像 dock…

手工测试没有前途,自动化测试会取代手工测试?

在测试行业&#xff0c;一个一直被讨论的问题就是&#xff1a;手工测试没有前途&#xff0c;自动化测试会取代手工测试&#xff1f; 首先说结论&#xff1a;自动化测试不会取代手工测试&#xff0c;这完全是两个维度的事情。为什么不会呢&#xff1f;我们需要从本源上说起。 什…

【数据挖掘实战】——舆情分析:对微博文本进行情绪分类

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机科学与技术研究生在读 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4a…

Tomcat部署及多实例部署

Tomcat部署及多实例部署 一、什么是Tomcat二、Tomcat核心组件1.什么是servlet2.什么是 JSP 三、Tomcat 功能组件结构1.Connector2.Container2.1Container 包含四个子容器 3.Service 四、Tomcat 请求过程五、Tomcat 服务部署1.关闭防火墙2.上传jdk包&#xff0c;查看jdk版本&…

SpringCloud Gateway网关多路由配置访问404解决方案

文章目录 一、问题描述&#xff1a;SpringCloud GateWay Eureka访问出现404&#xff0c;Not Found二、解决方案:1、 配置 filters: - StripPrefix12、删除冲突依赖3、检查启动类4、检查配置文件 一、问题描述&#xff1a;SpringCloud GateWay Eureka访问出现404&#xff0c…

如何用新范式解决安全难题?数字安全免疫力研讨论坛给你答案!

6月13日&#xff0c;腾讯安全、腾讯研究院将联动IDC、《中国信息安全》杂志社、CIO 时代、新基建创新研究院等多家行业机构、媒体共同发起「数字安全免疫力」研讨论坛&#xff0c;汇聚产学研各界专家&#xff0c;研判安全态势、分享最佳实践&#xff0c;碰撞新一代的安全理念&a…

【Java基础学习打卡02】计算机硬件与软件

目录 引言一、硬件组成二、软件组成三、软硬件工作流程四、性能指标五、选购建议总结 引言 本小节将认识计算机硬件与软件&#xff0c;以及软硬件工作流程&#xff0c;还要知道计算机性能指标&#xff0c;并可以指导我们购买电脑。还是那句话&#xff0c;了解计算机工作流程对…

Qt6之样式表

Qt的样式表主要是受到CSS的启发&#xff0c;通过调用QWidget::setStyleSheet()或QApplication::setStyleSheet()&#xff0c;你可以为一个独立的子部件、整个窗口&#xff0c;甚至是整个应用程序指定一个样式表。样式表由影响窗口部件绘制的样式规则组成。这些规则都是普通文本…

stable-diffusion领域prompt集合

有什么写实的stable diffusion模型&#xff1f; - 知乎试了试这个模型&#xff0c;感觉勉强&#xff0c;大佬们知道有没有更写实的模型&#xff1f;https://huggingface.co/CompVis/stable-diff…https://www.zhihu.com/question/567026134Stable Diffusion好看的御姐风AI美女P…

数据仓库分析工具Hive

数据仓库分析工具Hive 概述Hive简介Hive与Hadoop生态系统中其他组件的关系Hive与传统数据库的对比 Hive系统架构概述Hive组成模块Hive工作原理SQL语句转换成MapReduce的基本原理Hive中SQL查询转换成MapReduce作业的过程 从外部访问Hive的典型方式 Hive的应用Hive在报表中心的应…

jmeter性能测试实战--web程序

目录 前言&#xff1a; 项目背景 测试步骤 前言&#xff1a; JMeter是开源的Java性能测试工具&#xff0c;广泛应用于Web、移动应用程序等领域的性能测试中。在Web应用程序中&#xff0c;JMeter能够模拟多用户并发请求&#xff0c;验证系统在高负载情况下的性能&#xff0c…

【王道考研】王道数据结构与算法详细笔记(全)

目录 第一章 数据结构绪论 1.1 数据结构的基本概念 1.2 数据结构的三要素 1.2.1. 数据的逻辑结构 1.2.2. 数据的存储结构&#xff08;物理结构&#xff09; 1.2.3. 数据的运算 1.2.4. 数据类型和抽线数据类型 1.3 算法的基本概念 1.4 算法的时间复杂度 1.5 算法的空…

再一次安装anygrasp

1&#xff0c;anaconda 2&#xff0c;新建py3.6.2的环境 因为anygrasp 要求 pytorch 1.6 太老了&#xff0c;而且对应的cuda 都是cuda 11以下的版本 我是笔记本带3060&#xff0c;只能cuda11以上。 为了解决这个问题&#xff0c;感谢史驭舒大佬提供的思路 他复现用的环境是…

代码随想录刷题第48天|LeetCode198打家劫舍、LeetCode213打家劫舍II、LeetCode337打家劫舍III

1、LeetCode198打家劫舍 题目链接&#xff1a;198、打家劫舍 1、dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i]。 2、递推公式&#xff1a; 如果偷第i房间&#xff0c;那么dp[i] dp[i - 2] nums[i] &#xf…

cvte 前端一面 凉经

cvte 前端一面 凉经 原文面试题地址&#xff1a;https://www.nowcoder.com/discuss/353159272857018368?sourceSSRsearch 1. vuex原理 和vuerouter的原理差不多 2. vuerouter的原理 ​ 首先在main.js中&#xff0c;import router from ‘./router’ 引入在router文件夹下面…