bean生命周期源码(三)

书接上文

文章目录

    • 一、Bean的销毁逻辑
      • 1. 简介
      • 2. Bean销毁逻辑的注册
      • 3. Bean的销毁过程

一、Bean的销毁逻辑

1. 简介

前面我们已经分析完了Spring创建Bean的整个过程的源码,在创建bean的核心方法中doCreateBean这一个核心方法中,在方法的最后面有这么一段代码:

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

这个registerDisposableBeanIfNecessary方法就是销毁bean的逻辑,下面我们就详细分析一下销毁Bean的过程。

2. Bean销毁逻辑的注册

在使用bean的销毁逻辑时,我们可以像下面代码一样:

@Component
public UserService implements DisposedBean{@Autowiredprivate OrderService orderservice;@Overridepublic void destroy() throws Exception{//业务逻辑}
}@Component
public UserService{@Autowiredprivate OrderService orderservice;@preDestroypublic void destroy() throws Exception{//业务逻辑}
}

上面代码的逻辑就是让我们的UserService实现了DisposedBean接口,并实现了destroy方法,或者直接在指定方法上加上@preDestroy注解,那么这个方法会在UserService被销毁时调用。

注意所谓的销毁并不是JVM将我们的bean对象销毁,我们知道JVM在垃圾回收的过程中,会回收销毁掉垃圾对象,但调用destroy方法在对象销毁时被调用这一个逻辑JVM是不能帮我们实现的,JVM在只会在销毁对象是调用默认的finalize方法,那么这个销毁方法会在什么时候被调用?其实是在我们的Spring容器销毁时被调用。

下面我们给了两种关闭bean容器的方法:

  • 手动关闭
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();applicationContext.close();}

直接调用close()方法实现容器的关闭。

  • 向JVM注册关闭钩子
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();applicationContext.registerShutdownHook();}

applicationContext.registerShutdownHook() 是用来注册一个JVM关闭钩子(shutdown hook)。在Java中,当JVM即将关闭时,会执行一些预定义的动作。通过注册关闭钩子,你可以指定在JVM关闭之前执行一些特定的操作。具体地说,registerShutdownHook()会注册一个钩子,以确保在JVM关闭时,Spring容器执行销毁和清理工作。这包括关闭所有的单例bean、释放资源等。这样可以确保在应用程序关闭时,Spring能够做一些必要的清理,以避免资源泄漏或其他问题。

下面是这两种方式的源码:

@Overridepublic void close() {synchronized (this.startupShutdownMonitor) {doClose();// If we registered a JVM shutdown hook, we don't need it anymore now:// We've already explicitly closed the context.if (this.shutdownHook != null) {try {Runtime.getRuntime().removeShutdownHook(this.shutdownHook);}catch (IllegalStateException ex) {// ignore - VM is already shutting down}}}}@Overridepublic void registerShutdownHook() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {@Overridepublic void run() {synchronized (startupShutdownMonitor) {doClose();}}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}

可以发现这两种方式底层其实都是调用的doClose()方法。

继续回到registerDisposableBeanIfNecessary方法

// 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个参数,分别是beanname,初始化后的bean以及beandefintion,我们进去看看它的源码:

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {if (mbd.isSingleton()) {// Register a DisposableBean implementation that performs all destruction// work for the given bean: DestructionAwareBeanPostProcessors,// DisposableBean interface, custom destroy method.registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}else {// A bean with a custom scope...Scope scope = this.scopes.get(mbd.getScope());if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");}scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}}}

首先if (!mbd.isPrototype() && requiresDestruction(bean, mbd))会判断你的bean是否是单例的,如果是单例的它会执行requiresDestruction(bean, mbd)来判断你这个bean到底需不需要执行销毁前的逻辑。

	protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))));}

上面方法通过DisposableBeanAdapter.hasDestroyMethod(bean, mbd)来判断你这个方法有没有所谓的销毁逻辑。下面我们看看它是怎样去判断的。

	public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {return true;}return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;}

如果你的Bean实现了DisposableBean和AutoClosedable接口,就返回true,否则执行方法inferDestroyMethodIfNecessary

@Nullableprivate static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {//看看你的beanDefinition有没有销毁方法String destroyMethodName = beanDefinition.resolvedDestroyMethodName;if (destroyMethodName == null) {//获得你beanDefinition中配置销毁方法的名称destroyMethodName = beanDefinition.getDestroyMethodName(); ////如果你在beanDefinition中的设置的方法(通常是通过实现BeanPostProcess接口中设置的)名为INFER_METHOD,则执行小面的逻辑if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||(destroyMethodName == null && bean instanceof AutoCloseable)) {destroyMethodName = null;if (!(bean instanceof DisposableBean)) {try {//就会获取你定义的close方法来作为销毁方法destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();}catch (NoSuchMethodException ex) {try {//或者将你定义的ShutDown方法作为销毁方法destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();}catch (NoSuchMethodException ex2) {// no candidate destroy method found}}}}beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");}return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);}

上面代码中的if (destroyMethodName == null) {这个代码块中的逻辑可能比较难以理解,下面我用案例来分析一下,如下面代码:

@Component
public class UserService {@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}public void close(){System.out.println("close方法被调用了");}}

上面我们给我们的UserService加上了一个close()方法,然后我们添加一个类加上MergedBeanDefinitionPostProcessorInstantiationAwareBeanPostProcessor

@Componentpublic class jackBeanPostProcessor implements InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if(beanName.equals("userService")){beanDefinition.setDestroyMethodName("(inferred)");}}
}

上面代码中我们给beanDefinition设置了一个销毁方法,名字为inferred,也就是前面源码中说的AbstractBeanDefinition.INFER_METHOD这个常量。然后执行下面代码,就会自定调用我们名为close()的方法。

public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");applicationContext.close();}
}

在这里插入图片描述
这也就上面那段源码的作用,下面我们接着回到inferDestroyMethodIfNecessary方法,到这里该方法就执行完毕了。然后继续回到requiresDestruction,里面还有一句代码这样的hasDestructionAwareBeanPostProcessors,该方法用来判断容器中有没有bean实现了DestructionAwareBeanPostProcessors接口。

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;default boolean requiresDestruction(Object bean) {return true;}
}

这个接口同样继承了BeanPostProcessor接口,然后它只有两个方法,postProcessBeforeDestruction这个方法就是执行bean销毁前的逻辑,requiresDestruction这个方法就是判断你这个bean需不需要执行销毁方法。

继续回到requiresDestruction方法,如果hasDestructionAwareBeanPostProcessors返回为true,那么就会执行后面的代码DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))

	public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {if (!CollectionUtils.isEmpty(postProcessors)) {for (DestructionAwareBeanPostProcessor processor : postProcessors) {if (processor.requiresDestruction(bean)) {return true;}}}return false;}

首先它会便利我们的destructionAware集合中所有的实现 DestructionAwareBeanPostProcessor接口的bean,然后调用processor.requiresDestruction,判断当前bean需不需要执行销毁方法。

processor.requiresDestruction底层具体实现类,会找到方法前有@preConstruct和@preDestroy注解到方法,然后存入相关的容器。

@Overridepublic boolean requiresDestruction(Object bean) {return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();}private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {if (this.lifecycleMetadataCache == null) {// Happens after deserialization, during destruction...return buildLifecycleMetadata(clazz);}// Quick check on the concurrent map first, with minimal locking.LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {synchronized (this.lifecycleMetadataCache) {metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {metadata = buildLifecycleMetadata(clazz);this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;}private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}List<LifecycleElement> initMethods = new ArrayList<>();List<LifecycleElement> destroyMethods = new ArrayList<>();Class<?> targetClass = clazz;do {final List<LifecycleElement> currInitMethods = new ArrayList<>();final List<LifecycleElement> currDestroyMethods = new ArrayList<>();ReflectionUtils.doWithLocalMethods(targetClass, method -> {if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {LifecycleElement element = new LifecycleElement(method);currInitMethods.add(element);if (logger.isTraceEnabled()) {logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);}}if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {currDestroyMethods.add(new LifecycleElement(method));if (logger.isTraceEnabled()) {logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);}}});// 父类的在前面initMethods.addAll(0, currInitMethods);destroyMethods.addAll(currDestroyMethods);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));}

执行完上面的逻辑,继续回到requiresDestruction方法,这里我们可以总结一下,Spring底层是如何判断我们是否有销毁的逻辑的:

  • 首先判断我们的bean是否实现类AutoCloseableDisposedBean接口,如果实现类了就调用相应的close方法
  • 如果没有实现上面的两个接口,就会判断我们是否修改了BeanDefinition,设置了销毁方法,如果有我们调用自己写的close方法或shutdown方法即可
  • 最后一种逻辑就是,我们时候使用了@PreDestroy注解

如果我们bean具有上面三种中的任何一种逻辑,就说明我们的bean可以执行相应的销毁逻辑,继续回到registerDisposableBeanIfNecessary方法,上面我们已经把if (!mbd.isPrototype() && requiresDestruction(bean, mbd))逻辑执行完了,下面可以进入语句块中了。

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {//如果我们的bean是单例if (mbd.isSingleton()) {registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}else {// A bean with a custom scope...Scope scope = this.scopes.get(mbd.getScope());if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");}scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}}}

然后继续执行registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));},这个方法的参数为beanname和创建了一个DisposableBeanAdapter对象。

DisposableBeanAdapter 是 Spring Framework 中的一个内部类,用于帮助处理 bean 的销毁(disposal)操作。在 Spring 容器关闭时,容器会负责销毁一些 bean 实例以释放资源。这个方法是在 Spring 容器创建 bean 实例的过程中被调用的。

public void registerDisposableBean(String beanName, DisposableBean bean) {synchronized (this.disposableBeans) {this.disposableBeans.put(beanName, bean);}}private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

registerDisposableBean只是将我们的相应的销毁逻辑(DisposableBeanAdapter)封装到了一个集合中(有销毁逻辑的bean的集合)。至此我们的销毁方法的注册的全部流程就已经结束了,下面就可以开始分析在容器关闭的时候是怎么执行我们的这些销毁逻辑的。

3. Bean的销毁过程

前面已经分析了bean销毁逻辑的注册过程,而且前面说到容器关闭有两种方法,分别是注册JVM钩子和调用容器的close方法显示关闭,其实他们的底层都是调用了doclose方法。

protected void doClose() {// Check whether an actual close attempt is necessary...if (this.active.get() && this.closed.compareAndSet(false, true)) {if (logger.isDebugEnabled()) {logger.debug("Closing " + this);}if (!NativeDetector.inNativeImage()) {LiveBeansView.unregisterApplicationContext(this);}try {// Publish shutdown event.publishEvent(new ContextClosedEvent(this));}catch (Throwable ex) {logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);}// Stop all Lifecycle beans, to avoid delays during individual destruction.if (this.lifecycleProcessor != null) {try {this.lifecycleProcessor.onClose();}catch (Throwable ex) {logger.warn("Exception thrown from LifecycleProcessor on context close", ex);}}// Destroy all cached singletons in the context's BeanFactory.destroyBeans();// Close the state of this context itself.closeBeanFactory();// Let subclasses do some final clean-up if they wish...onClose();// Reset local application listeners to pre-refresh state.if (this.earlyApplicationListeners != null) {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Switch to inactive.this.active.set(false);}}

上面代码关于bean销毁所调用的核心方法是destroyBeans

protected void destroyBeans() {getBeanFactory().destroySingletons();}

然后destroyBeans底层调用的是destroySingletons方法

	@Overridepublic void destroySingletons() {super.destroySingletons();// 清空manualSingletonNames集合updateManualSingletonNames(Set::clear, set -> !set.isEmpty());clearByTypeCache();}

继续进入super.destroySingletons方法

public void destroySingletons() {if (logger.isTraceEnabled()) {logger.trace("Destroying singletons in " + this);}synchronized (this.singletonObjects) {this.singletonsCurrentlyInDestruction = true;}String[] disposableBeanNames;synchronized (this.disposableBeans) {disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());}for (int i = disposableBeanNames.length - 1; i >= 0; i--) {destroySingleton(disposableBeanNames[i]);}this.containedBeanMap.clear();this.dependentBeanMap.clear();this.dependenciesForBeanMap.clear();clearSingletonCache();}

有关执行前面的销毁逻辑的代码就是下面两句

//定义一个String数组
String[] disposableBeanNames;
//	private final Map<String, Object> disposableBeans = new LinkedHashMap<>();disposableBeans就是我们前面讲注册销毁逻辑时最后封装的那个集合synchronized (this.disposableBeans) {//然后获得结合的keySet(也就是我们的beanname)disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());}for (int i = disposableBeanNames.length - 1; i >= 0; i--) {//遍历beanname,执行销毁逻辑destroySingleton(disposableBeanNames[i]);}

上面代码就是拿到了我们前面注册销毁逻辑的那个集合,然后遍历执行destroySingleton方法

public void destroySingleton(String beanName) {// Remove a registered singleton of the given name, if any.// 先从单例池中移除掉removeSingleton(beanName);// Destroy the corresponding DisposableBean instance.DisposableBean disposableBean;synchronized (this.disposableBeans) {disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);}destroyBean(beanName, disposableBean);}

然后上面的方法首先将我们的bean从单例池中移除掉,然后将我们的bean同样从this.disposableBeans移除掉,最后执行destroyBean(beanName, disposableBean)

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {// dependentBeanMap表示某bean被哪些bean依赖了// 所以现在要销毁某个bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean// Trigger destruction of dependent beans first...Set<String> dependencies;synchronized (this.dependentBeanMap) {// Within full synchronization in order to guarantee a disconnected Setdependencies = this.dependentBeanMap.remove(beanName);}if (dependencies != null) {if (logger.isTraceEnabled()) {logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);}for (String dependentBeanName : dependencies) {//当前依赖我们这个bean的其他bean也要执行销毁逻辑(如果有销毁逻辑),这里是递归实现destroySingleton(dependentBeanName);}}//开始真正执行我们的销毁逻辑if (bean != null) {try {bean.destroy();}catch (Throwable ex) {if (logger.isWarnEnabled()) {logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);}}}// Trigger destruction of contained beans...Set<String> containedBeans;synchronized (this.containedBeanMap) {// Within full synchronization in order to guarantee a disconnected SetcontainedBeans = this.containedBeanMap.remove(beanName);}if (containedBeans != null) {for (String containedBeanName : containedBeans) {destroySingleton(containedBeanName);}}// Remove destroyed bean from other beans' dependencies.synchronized (this.dependentBeanMap) {for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {Map.Entry<String, Set<String>> entry = it.next();Set<String> dependenciesToClean = entry.getValue();dependenciesToClean.remove(beanName);if (dependenciesToClean.isEmpty()) {it.remove();}}}// Remove destroyed bean's prepared dependency information.this.dependenciesForBeanMap.remove(beanName);}

上面真正执行销毁逻辑的代码就是下面这段代码:

//开始真正执行我们的销毁逻辑if (bean != null) {try {bean.destroy();}catch (Throwable ex) {if (logger.isWarnEnabled()) {logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);}}}

destory方法是DisposableBeanAdapter类的方法:

public DisposableBeanAdapter(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors, AccessControlContext acc) {Assert.notNull(bean, "Disposable bean must not be null");this.bean = bean;this.beanName = bean.getClass().getName();this.invokeDisposableBean = (this.bean instanceof DisposableBean);this.nonPublicAccessAllowed = true;this.acc = acc;this.beanPostProcessors = filterPostProcessors(postProcessors, bean);}

每个DisposableBeanAdapter对象,封装了对应的bean,以及容器中所有的DestructionAwareBeanPostProcessor(这就是销毁方法的逻辑)

public void destroy() {
//如果this.beanPostProcessors集合不为空,调用processor.postProcessBeforeDestruction方法if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {processor.postProcessBeforeDestruction(this.bean, this.beanName);}}if (this.invokeDisposableBean) {if (logger.isTraceEnabled()) {logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");}try {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {((DisposableBean) this.bean).destroy();return null;}, this.acc);}else {((DisposableBean) this.bean).destroy();}}catch (Throwable ex) {String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";if (logger.isDebugEnabled()) {logger.warn(msg, ex);}else {logger.warn(msg + ": " + ex);}}}if (this.destroyMethod != null) {invokeCustomDestroyMethod(this.destroyMethod);}else if (this.destroyMethodName != null) {Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);if (methodToInvoke != null) {invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));}}}

processor.postProcessBeforeDestruction(this.bean, this.beanName);这句代码底层就会根据我们前面判断某个bean有没有销毁逻辑的三种方法去执行我们bean的销毁逻辑的,至此某个bean底层执行我们制定的销毁逻辑的底层原理我们就分析完了。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编程规范:长函数的思考

在工作&#xff0c;我们应该都不想看到非常的长函数。对于一个运行5年左右的项目&#xff0c;极有可能出现这种情况。由于长函数的长、if/else嵌套&#xff0c;导致代码的可读性非常差&#xff0c;这对于项目的维护和开发带来了极大的困难。所以我们应该避免写长函数&#xff0…

智能优化算法应用:基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.法医调查算法4.实验参数设定5.算法结果6.…

Oracle 学习(1)

Oracle简介 Oracle是殷墟&#xff08;yīn Xu&#xff09;出土的甲骨文&#xff08;oracle bone inscriptions&#xff09;的英文翻译的第一个单词&#xff0c;在英语里是“神谕”的意思。Oracle公司成立于1977年&#xff0c;总部位于美国加州&#xff0c;是世界领先的信息管…

C++面试宝典第9题:找出第K大元素

题目 给定一个整数数组a,同时给定它的大小N和要找的K(1 <= K <= N),请根据快速排序的思路,找出数组中第K大的数(保证答案存在)。比如:数组a为[50, 23, 66, 18, 72],数组大小N为5,K为3,则第K大的数为50。 解析 这道题主要考察应聘者对于快速排序的理解,以及实…

配置手工模式链路聚合示例(交换机之间直连)

组网图形 图1 配置手工模式链路聚合组网图 手工模式链路聚合简介配置注意事项组网需求配置思路操作步骤配置文件 手工模式链路聚合简介 以太网链路聚合是指将多条以太网物理链路捆绑在一起成为一条逻辑链路&#xff0c;从而实现增加链路带宽的目的。链路聚合分为手工模式和LA…

java调用GDAL实现栅格数据的重采样的一种方法

目录 1.关于重采样 1.1概念 1.2用途 1.3常见算法 2.关于GDAL 2.1GDAL中的重采样算法 3.实现重采样 3.1思路 3.2完整代码 3.3使用QGIS验证效果 1.关于重采样 1.1概念 重采样是以原始图像的像元值或者导出的值填充到新的图像的每个像元的的过程。 1.2用途 在地理信…

顺序表的介绍与简单运用

1&#xff1a;解释与结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存 储。在数组上完成数据的增删查改。 顺序表一般可分为一下几类 1.1 静态顺序表 概念&#xff1a;使用定长数组存储元素。注意&#xff1a;这种是…

YOLOv8改进 | 主干篇 | 利用SENetV2改进网络结构 (全网首发改进)

一、本文介绍 本文给大家带来的改进机制是SENetV2&#xff0c;其是2023.11月的最新机制(所以大家想要发论文的可以在上面下点功夫)&#xff0c;其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型&#xff0c;而是一个可以和现有的任何…

可靠度理论中“设计基准期”、“设计使用年限”、“使用寿命”几个概念的区分

文章目录 0. 背景1. 重现期2. 设计基准期3. 设计使用年限调整系数4. 一把杆秤5. 调整系数的补充说明Last 0. 背景 在可靠度理论中&#xff0c;经常遇见“设计基准期”、“设计使用年限”、“使用寿命”几个概念。这些概念搞不清楚对于梳理结构荷载组合而言就是致命的。本文也是…

第二十一章博客

计算机应用实现了多台计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是在已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#xff0c;相互之间可以交换数据。编写网络应用程序前&#xff0c;首先必须明确所要使用的网络协议…

C语言中常见的笔试题(二)

题目一&#xff1a; 问题&#xff1a; 在C语言中&#xff0c;const关键字有哪些用途&#xff1f;请列举出至少三种用途&#xff0c;并给出相应的代码示例。 答案&#xff1a; 定义常量&#xff1a;使用const关键字可以定义常量&#xff0c;它们的值在程序运行期间不能被修改…

SpringMVC核心处理流程梳理

1、处理流程图展示 当我拿出这张图&#xff0c;阁下又该如何应对呢&#xff1f;执行流程是不是一目了然了。 2、DispatcherServlet&#xff1a;中央处理器或者中央调度器 下图官方的解释应该最完善了。 3、SpringMVC三大核心组件 HandlerMapping 处理器映射器&#xff0c;…