spring实例化bean之实例化

news2024/11/22 9:22:14

1.关键方法createBean

doGetBean中调用getSingleton方法中调用singletonFactory.getObject()触发lambda表达式中的createBean方法

AbstractAutowireCapableBeanFactory#createBean

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

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		//获取到当前类的Class
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		try {
			//处理override属性
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			//给beandefinition的beforeInstantiationResolved属性设置值,布尔类型的,如果需要执行
			//applyBeanPostProcessorsAfterInitialization和applyBeanPostProcessorsBeforeInstantiation
			//就标记为true 然后在doCreateBean方法的initializeBean方法中执行这两个方法
			//AOP相关
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			//关键方法,创建bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

2.关键方法doCreateBean

AbstractAutowireCapableBeanFactory#doCreateBean
创建bean 填充bean 初始化bean

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

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//关键1 创建bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//获取bean
		final Object bean = instanceWrapper.getWrappedInstance();
		//获取bean类型
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// 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 {
			//关键2 填充bean
			populateBean(beanName, mbd, instanceWrapper);
			//关键3 初始化bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

3.关键方法 createBeanInstance

AbstractAutowireCapableBeanFactory#createBeanInstance

	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		//拿到class
		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());
		}
		
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		//如果工厂方法不为空则使用工厂方法初始化策略
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		boolean resolved = false;
		boolean autowireNecessary = false;
		//一个类有多个构造函数,每个构造函数都有不同的参数,所以调用前需要根据参数锁定构造函数或对应的工厂方法
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					//也就是从beanDefinition中拿属性
					resolved = true;
					//是否需要有参构造
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		//如果上面的能获取到 直接调用 否则需要继续解析
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		//获取构造函数
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		// AUTOWIRE_CONSTRUCTOR 就是构造函数注入
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			//有参构造	也会调用默认的构造函数 只是需要解析参数
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		//获取构造器
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			//用获取到的构造器 实例化bean (无参数)
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
		//获取不到就调用默认的构造
		return instantiateBean(beanName, mbd);
	}

默认构造就是调用Class获取构造函数,然后调用newInstance方法创建bean,然后封装成BeanWrapper类型返回

4.默认构造

SimpleInstantiationStrategy#instantiate

	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			final Constructor<?> ctor, Object... args) {

		if (!bd.hasMethodOverrides()) {
			if (System.getSecurityManager() != null) {
				// use own privileged to change accessibility (when security is on)
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(ctor);
					return null;
				});
			}
			//反射创建对象
			return BeanUtils.instantiateClass(ctor, args);
		}
		else {
			//cglib进行动态代理
			return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
		}
	}

5.有参构造

主要工作是解析参数,然后调用默认构造实例化bean

	public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

		BeanWrapperImpl bw = new BeanWrapperImpl();
		this.beanFactory.initBeanWrapper(bw);

		Constructor<?> constructorToUse = null;
		ArgumentsHolder argsHolderToUse = null;
		Object[] argsToUse = null;

		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			Object[] argsToResolve = null;
			synchronized (mbd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached constructor...
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			if (argsToResolve != null) {
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
			}
		}

		if (constructorToUse == null || argsToUse == null) {
			// Take specified constructors, if any.
			Constructor<?>[] candidates = chosenCtors;
			if (candidates == null) {
				Class<?> beanClass = mbd.getBeanClass();
				try {
					candidates = (mbd.isNonPublicAccessAllowed() ?
							beanClass.getDeclaredConstructors() : beanClass.getConstructors());
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Resolution of declared constructors on bean Class [" + beanClass.getName() +
							"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
				}
			}

			if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
				Constructor<?> uniqueCandidate = candidates[0];
				if (uniqueCandidate.getParameterCount() == 0) {
					synchronized (mbd.constructorArgumentLock) {
						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
						mbd.constructorArgumentsResolved = true;
						mbd.resolvedConstructorArguments = EMPTY_ARGS;
					}
					bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
					return bw;
				}
			}

			// Need to resolve the constructor.
			boolean autowiring = (chosenCtors != null ||
					mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
			ConstructorArgumentValues resolvedValues = null;

			int minNrOfArgs;
			if (explicitArgs != null) {
				minNrOfArgs = explicitArgs.length;
			}
			else {
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				resolvedValues = new ConstructorArgumentValues();
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}

			AutowireUtils.sortConstructors(candidates);
			int minTypeDiffWeight = Integer.MAX_VALUE;
			Set<Constructor<?>> ambiguousConstructors = null;
			LinkedList<UnsatisfiedDependencyException> causes = null;

			for (Constructor<?> candidate : candidates) {
				Class<?>[] paramTypes = candidate.getParameterTypes();

				if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {
					// Already found greedy constructor that can be satisfied ->
					// do not look any further, there are only less greedy constructors left.
					break;
				}
				if (paramTypes.length < minNrOfArgs) {
					continue;
				}

				ArgumentsHolder argsHolder;
				if (resolvedValues != null) {
					try {
						String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
						if (paramNames == null) {
							ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
							if (pnd != null) {
								paramNames = pnd.getParameterNames(candidate);
							}
						}
						argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
								getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
					}
					catch (UnsatisfiedDependencyException ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
						}
						// Swallow and try next constructor.
						if (causes == null) {
							causes = new LinkedList<>();
						}
						causes.add(ex);
						continue;
					}
				}
				else {
					// Explicit arguments given -> arguments length must match exactly.
					if (paramTypes.length != explicitArgs.length) {
						continue;
					}
					argsHolder = new ArgumentsHolder(explicitArgs);
				}

				int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
						argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
				// Choose this constructor if it represents the closest match.
				if (typeDiffWeight < minTypeDiffWeight) {
					constructorToUse = candidate;
					argsHolderToUse = argsHolder;
					argsToUse = argsHolder.arguments;
					minTypeDiffWeight = typeDiffWeight;
					ambiguousConstructors = null;
				}
				else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
					if (ambiguousConstructors == null) {
						ambiguousConstructors = new LinkedHashSet<>();
						ambiguousConstructors.add(constructorToUse);
					}
					ambiguousConstructors.add(candidate);
				}
			}

			if (constructorToUse == null) {
				if (causes != null) {
					UnsatisfiedDependencyException ex = causes.removeLast();
					for (Exception cause : causes) {
						this.beanFactory.onSuppressedException(cause);
					}
					throw ex;
				}
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Could not resolve matching constructor " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
			}
			else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Ambiguous constructor matches found in bean '" + beanName + "' " +
						"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
						ambiguousConstructors);
			}

			if (explicitArgs == null && argsHolderToUse != null) {
				argsHolderToUse.storeCache(mbd, constructorToUse);
			}
		}

		Assert.state(argsToUse != null, "Unresolved constructor arguments");
		//解析完参数 调用默认构造
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}

在这里插入图片描述

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

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

相关文章

安装和使用MySQL

文章目录 零、学习目标一、获取MySQL安装程序二、安装MySQL数据库管理系统三、启动并使用MySQL命令行&#xff08;一&#xff09;启动MySQL命令行&#xff08;二&#xff09;在MySQL命令行里操作数据库1、显示数据库2、使用数据库3、查看数据库里的表4、查看表的记录 零、学习目…

还在用InputStream的available()方法获取流的字节数吗?那你可要小心了!

问题说明 因为项目需求&#xff0c;需要从一个url获取文件流然后保存到minio文件服务器中。 先看看下面的代码片段 MinioClient minioClient MinioClient.builder().endpoint(new URL("http://ip:port")).credentials("username", "password"…

关于npm install md5报错the command again as root/Administrator的解决办法

nodejs安装依赖报错 (venv) PS D:\pythonProject> npm install md5 npm ERR! code EPERM npm ERR! syscall mkdir npm ERR! requiredBy: . npm ERR! } npm ERR! npm ERR! The operation was rejected by your operating system. npm ERR! Its possible that the file was…

QQ群内增加ChatGPT机器人

0. 起因 最近弄了一个QQ群&#xff0c;人在逐渐增多&#xff0c;问问题的人也越来越多&#xff0c;有些问题是编程基础问题&#xff0c;2023年了&#xff0c;这些问题都不应该由人类来解决了&#xff0c;交给AI吧。所以&#xff0c;想在QQ群里加个ChatGPT机器人&#xff0c;应…

simplify3d 打印参数设置笔记

专业实用且详细的打印教程 3D打印故障排除&#xff1a;所有问题和解决方案 simplify3d 切片软件使用 初始设置 材料&#xff1a;PLA 喷头温度 215 热床温度 60 导入的模型格式 stl 保存的模型名称可以是中文 但是名称中不允许有空格&#xff0c;否则我这个打印机加热好喷头和…

GORM---初级查询

文章目录 初始数据表一般查询Where条件普通SQL查询查询 Struct & Map Not 条件Or 条件内联条件额外查询选项FirstOrInitAttrsAssign FirstOrCreateAttrsAssign 初始数据表 一般查询 var p1, p2, p3, p4 connect.PersonInfo var ps []connect.PersonInfo// 把主键升序&…

2023年陕西彬州第八届半程马拉松赛153pb完赛

1、赛事背景 2023年6月3日&#xff0c;我参加了2023陕西彬州第八届半程马拉松赛&#xff0c;最终153完赛&#xff0c;PB了5分钟。起跑时间早上7点30分&#xff0c;毕竟6月天气也开始热了。天气预报显示当天还是小到中雨&#xff0c;上次铜川宜君半马也是雨天跑的&#xff0c;阴…

为何选择C语言作为大一编程入门?

C语言&#xff0c;编程之本&#xff0c;源于生命。作为一门最接近底层的高级语言&#xff0c;学习C语言有以下重要原因&#xff1a; 探索底层&#xff1a;C语言为我们提供了理解计算机底层工作原理的机会。通过学习C语言&#xff0c;我们能更容易理解内存管理、指针操作等底层知…

前后端分离项目SpringBoot-Shiro-jwt处理401响应码的方案

问题现象&#xff1a; 现在大多数项目已经实现前后端分离&#xff0c;当采用shiro作为安全框架时&#xff0c;如果请求的token已过期或未认证请求&#xff0c;会得到401的HTTP STATUS。此时在前端还会因为401的错误弹出一个登录认证的弹框。效果如下&#xff1a; 经分析&#…

DataLeap的全链路智能监控报警实践(一):常见问题

随着字节跳动业务的快速发展&#xff0c;大数据开发场景下需要运维管理的任务越来越多&#xff0c;然而普通的监控系统只支持配置相应任务的监控规则&#xff0c;已经不能完全满足当前需求&#xff0c;在日常运维中开发者经常会面临以下几个问题&#xff1a; 任务多&#xff0c…

凝聚全球顶尖力量,助力开源行业发展 | 2023开放原子全球开源峰会开幕式暨高峰论坛亮点抢先看!

亮点攻略 一触即发 6月11-13日&#xff5c;中国北京 作为开源领域一年一度的盛事&#xff0c;由2023全球数字经济大会组委会主办&#xff0c;开放原子开源基金会、北京市经济和信息化局、北京经济技术开发区管理委员会承办的2023开放原子全球开源峰会将于6月11日至13日在北京…

实现Mac使用空格键预览更多文件格式

mac上的快速预览非常好用&#xff0c;但是对于许多格式的不支持造成了体验感的割裂。例如对于使用频繁的.md、.js、.py等格式的文件&#xff0c;无法生成缩略图。可以通过在「Quick Look」中 安装插件的方式来实现预览格式的扩展。 1. 插件的安装 下载的插件一般有两种格式&am…

SpringBoot的高考志愿填报系统(含源码+数据库)

系统获取方式&#xff1a; 1.系统分析 在本系统中&#xff0c;基于SpringBoot的高考志愿填报系统需要满足以下功能需求&#xff1a; 1.用户可以根据自己的院校类型、办学类型、层次类型、地域等因素筛选高校。 2.用户可以查询到所选高校的基本信息&#xff0c;包括学校的概况…

vcsa6.7更换证书

今天一早&#xff0c;有现场反馈&#xff0c;vcenter无法登录&#xff0c;估计到可能是证书到期了 在浏览器中确认下&#xff0c;确实是证书到期的问题 通过查看你sts不过期&#xff0c;直接更新全部证书 To escape to local shell, press CtrlAlt]. VMware vCenter Server Ap…

翻车了,lombok这玩意真坑

目录 背景排查Setter-Getter方法的坑解决原因解决方案原因解决方案 总结 背景 青柠最近在写自己的项目&#xff0c;刚开始就写不下去了&#xff0c;心态崩了&#xff0c;这啥玩意啊&#xff0c;就是找不到问题在哪&#xff1f; 早前&#xff0c;在项目当中引入了Lombok插件&a…

zabbix配置监控日志

目录 一、zabbix日志监控的功能 二、zabbix监控日志的条件 三、日志监控的监控指标 四、日志监控Item的配置案例 一、zabbix日志监控的功能 Zabbix-agent支持对日志文件的监控&#xff0c;可以对日志对的关键字进行监控&#xff0c;然后告警。日志监控支持普通的日志文件&am…

ChatGPT与软件架构(5) - 网络安全

安全是任何系统都无法回避的关键要素&#xff0c;本文介绍了若干利用ChatGPT增强的网络安全攻击&#xff0c;并提出了应对策略。原文: ChatGPT and Cyber Security Benjamin Elliott Unsplash 如果你还不熟悉ChatGPT&#xff0c;那么考虑到这种生成式人工智能服务的快速崛起&am…

Hive SQL 语法大全~

基于语法描述说明 CREATE DATABASE [IF NOT EXISTS] db_name [LOCATION] path; SELECT expr, ... FROM tbl ORDER BY col_name [ASC | DESC] (A | B | C) 如上语法&#xff0c;在语法描述中出现&#xff1a; []&#xff0c;表示可选&#xff0c;如上[LOCATION]表示可写、可不…

神州数码DCRS试题题目合集

2017.11.3 DCRS 在公司总部的DCRS上配置&#xff0c;配置设备enable密码&#xff0c;并且在登录设备时必须正确输入enable密码才能进入交换机的配置模式。 在公司总部的DCRS上配置&#xff0c;在交换设备上开启SSH管理功能&#xff0c;用户名和密码都是DCN&#xff0c;并关闭…

flutter系列之:做一个会飞的菜单

文章目录 简介定义一个菜单项目让menu动起来添加菜单内部的动画总结 简介 flutter中自带了drawer组件&#xff0c;可以实现通用的菜单功能&#xff0c;那么有没有一种可能&#xff0c;我们可以通过自定义动画来实现一个别样的菜单呢&#xff1f; 答案是肯定的&#xff0c;一起…