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.rhkb.cn/news/223980.html

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

相关文章

pycharm修改项目文件夹名称

目录 1 修改项目文件夹名称 2 修改代码中的项目名称 1 修改项目文件夹名称 选中项目文件夹&#xff0c;右键&#xff0c;选择refactor-rename。 选择rename project&#xff1a; 然后输入新的项目名称。 此时进入资源管理器&#xff0c;修改项目文件夹的名字&#xff0c;完成…

spring aop实际开发中怎么用,Spring Boot整合AOP,spring boot加spring mvc一起使用aop,项目中使用aop

前言&#xff1a;本文不介绍 AOP 的基本概念、动态代理方式实现 AOP&#xff0c;以及 Spring 框架去实现 AOP。本文重点介绍 Spring Boot 项目中如何使用 AOP&#xff0c;也就是实际项目开发中如何使用 AOP 去实现相关功能。 如果有需要了解 AOP 的概念、动态代理实现 AOP 的&…

Spark集群部署与架构

在大数据时代&#xff0c;处理海量数据需要分布式计算框架。Apache Spark作为一种强大的大数据处理工具&#xff0c;可以在集群中高效运行&#xff0c;处理数十TB甚至PB级别的数据。本文将介绍如何构建和管理Spark集群&#xff0c;以满足大规模数据处理的需求。 Spark集群架构…

LLM微调(四)| 微调Llama 2实现Text-to-SQL,并使用LlamaIndex在数据库上进行推理

Llama 2是开源LLM发展的一个巨大里程碑。最大模型及其经过微调的变体位居Hugging Face Open LLM排行榜&#xff08;https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard&#xff09;前列。多个基准测试表明&#xff0c;就性能而言&#xff0c;它正在接近GPT-3.5…

光耦继电器

光耦继电器(光电继电器) AQW282SX 282SZ 280SX 280SZ 284SX 284SZ 212S 212SX 21 2SZ 文章目录 光耦继电器(光电继电器)前言一、光耦继电器是什么二、光耦继电器的类型三、光电耦合器的应用总结前言 光耦继电器在工业控制、通讯、医疗设备、家电及汽车电子等领域得到广泛应…

【隐私保护】Presidio简化了PII匿名化

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

YOLOv8改进 | 2023注意力篇 | MSDA多尺度空洞注意力(附多位置添加教程)

一、本文介绍 本文给大家带来的改进机制是MSDA&#xff08;多尺度空洞注意力&#xff09;发表于今年的中科院一区(算是国内计算机领域的最高期刊了)&#xff0c;其全称是"DilateFormer: Multi-Scale Dilated Transformer for Visual Recognition"。MSDA的主要思想是…

STM32F407-14.3.10-表73具有有断路功能的互补通道OCx和OCxN的输出控制位-1x111

如上表所示&#xff0c;MOE1&#xff0c;OSSR1&#xff0c;CCxE1&#xff0c;CCxNE1时&#xff0c;OCx与OCxN对应端口的输出状态取决于OCx_REF与极性选择&#xff08;CCxP&#xff0c;CCxNP&#xff09; 死区。 -------------------------------------------------------------…

记pbcms网站被攻击,很多标题被篡改(1)

记得定期打开网站看看哦! 被攻击后的网站异常表现:网页内容缺失或变更,页面布局破坏,按钮点击无效,...... 接着查看HTML、CSS、JS文件,发现嵌入了未知代码! 攻击1:index.html 或其他html模板页面的标题、关键词、描述被篡改(俗称,被挂马...),如下: 攻击2:在ht…

【PostGIS】PostgreSQL15+对应PostGIS安装教程及空间数据可视化

一、PostgreSQL15与对应PostGIS安装 PostgreSQL15安装&#xff1a;下载地址PostGIS安装&#xff1a;下载地址&#xff08;选择倒数第二个&#xff09; 1、PostgreSQL安装 下载安装包&#xff1b;开始安装&#xff0c;这里使用默认安装&#xff0c;一直next直到安装完成&…

ubuntu下docker安装,配置python运行环境

参考自: 1.最详细ubuntu安装docker教程 2.使用docker搭建python环境 首先假设已经安装了docker&#xff0c;卸载原来的docker 在命令行中运行&#xff1a; sudo apt-get updatesudo apt-get remove docker docker-engine docker.io containerd runc 安装docker依赖 apt-get…

饥荒Mod 开发(二一):超大便携背包,超大物品栏,永久保鲜

饥荒Mod 开发(二十)&#xff1a;显示打怪伤害值 饥荒Mod 开发(二二)&#xff1a;显示物品信息 源码 游戏中的物品栏容量实在太小了&#xff0c;虽然可以放在箱子里面但是真的很不方便&#xff0c;外出一趟不容易看到东西都不能捡。实在是虐心。 游戏中的食物还有变质机制&#…

SSTI模板注入基础(Flask+Jinja2)

文章目录 一、前置知识1.1 模板引擎1.2 渲染 二、SSTI模板注入2.1 原理2.2 沙箱逃逸沙箱逃逸payload讲解其他重要payload 2.3 过滤绕过点.被过滤下划线_被过滤单双引号 "被过滤中括号[]被过滤关键字被过滤 三、PasecaCTF-2019-Web-Flask SSTI参考文献 一、前置知识 1.1 模…

力扣:51. N 皇后

题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的…

多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测

多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测 目录 多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现…

ubuntu22.04 下载路径

ftp下载路径 csdn下载 ubuntu22.04下载路径ubuntu-22.04-desktop-amd64.7z.001资源-CSDN文库 ubuntu22.04下载路径ubuntu-22.04-desktop-amd64.7z.002资源-CSDN文库 【免费】ubuntu-22.04-desktop-amd64.7z.003资源-CSDN文库 【免费】ubuntu-22.04-desktop-amd64.7z.004资源-…

大数据应用开发1——配置基础环境

一、基础环境配置 1.配置虚拟网络 1.1、点击1、编辑2和3&#xff0c; 1.2、点开4&#xff0c;编辑网关 2、配置虚拟机环境 1.1、安装一台虚拟机&#xff0c;使用root用户登录&#xff0c;打开终端 1.2修改主机名 终端输入&#xff1a; vim /etc/hostname使用vim编辑/etc/ho…

linux异步IO的几种方法及重点案例

异步IO的方法 在Linux下&#xff0c;有几种常见的异步I/O&#xff08;Asynchronous I/O&#xff09;机制可供选择。以下是其中一些主要的异步I/O机制&#xff1a; POSIX AIO&#xff08;Asynchronous I/O&#xff09;&#xff1a;POSIX AIO是一种标准的异步I/O机制&#xff0c…

三道C语言中常见的笔试题及答案(一)

题目一&#xff1a; 问题&#xff1a; 解释以下代码中的#define预处理指令的作用&#xff0c;并说明其优点和缺点。 #include <stdio.h> #define PI 3.14159 #define CALCULATE_AREA(r) (PI * r * r) int main() { double radius 5.0; double area CALCULATE_AREA(r…

基于STM32的DS1302实时时钟模块应用

DS1302是一款低功耗的实时时钟芯片&#xff0c;被广泛应用于各种电子产品中。它具有准确计时、多种时间格式表示、定时报警等功能&#xff0c;适用于记录时间、日期和闹钟。在本文中&#xff0c;我们将介绍如何在基于STM32的开发环境中使用DS1302实时时钟模块&#xff0c;并给出…