面试笔记系列三之spring基础知识点整理及常见面试题

目录

如何实现一个IOC容器?

说说你对Spring 的理解?

你觉得Spring的核心是什么?

说一下使用spring的优势?

Spring是如何简化开发的?

IOC 运行时序

prepareRefresh()  初始化上下文环境

obtainFreshBeanFactory()   创建并初始化 BeanFactory

prepareBeanFactory(beanFactory)  填充 BeanFactory 功能

postProcessBeanFactory()  提供子类覆盖的额外处理

invokeBeanFactoryPostProcessors()    激活各种BeanFactory处理器

registerBeanPostProcessors

initMessageSource

initApplicationEventMulticaster

onRefresh

registerListeners

finishBeanFactoryInitialization

finishRefresh

Spring的AOP的底层实现原理

说说你对Aop的理解?

说说你对IOC的理解?

依赖注入DI 的概念

谈一下spring IOC的底层实现

BeanFactory和ApplicationContext有什么区别

简述spring bean的生命周期?

spring支持的bean作用域有哪些?

Spring框架中的单例Bean是线程安全的么?

spring框架中使用了哪些设计模式及应用场景

spring事务的实现方式原理是什么?

Transaction失效

spring事务的隔离级别有哪些?

spring的事务传播机制是什么?

spring事务什么时候会失效?

什么的是bean的自动装配,它有哪些方式?

spring是如何解决循环依赖的

不能解决的几种循环依赖

pring中常见的几种注解

spring、springmvc、springboot的区别是什么?

springmvc工作流程是什么?

springmvc的九大组件有哪些?

Spring框架中有哪些不同类型的事件


如何实现一个IOC容器?

要实现一个简单的Spring框架IOC容器,您可以遵循以下步骤:

  1. 定义Bean注解: 创建一个自定义的注解,例如@MyBean,用于标识需要被IOC容器管理的Bean类。

  2. 扫描并解析Bean定义:(BeanDefinition) 创建一个Bean解析器,用于扫描应用程序中的所有类,解析带有@MyBean注解的类,并存储Bean的定义信息,如类名、属性等。

  3. 创建Bean实例: 根据Bean定义信息,使用反射机制实例化Bean,并进行依赖注入。遍历所有的Bean定义,根据类名使用反射创建Bean实例,并解析其属性依赖关系,将相应的依赖注入到实例中。

  4. 实现依赖注入: 当创建Bean实例时,解析Bean的属性依赖关系,并根据依赖注入的方式,通过反射或者setter方法将需要的依赖注入到Bean实例中。

  5. 管理Bean的生命周期: 可以在Bean中定义初始化和销毁方法,并在容器中调用这些方法来管理Bean的生命周期。可以使用@PostConstruct注解标注初始化方法,使用@PreDestroy注解标注销毁方法。

  6. 提供获取Bean的方法: 在IOC容器中提供一个方法,根据Bean的名称获取对应的实例。可以使用Map来存储Bean实例,key为Bean的名称,value为实例对象。

  7. 实现配置文件加载: 可以实现一个简单的配置文件加载机制,从配置文件中读取需要注入的属性值,例如使用properties文件或者XML文件。

以上是一个简单的实现Spring框架IOC容器的步骤。在实际的Spring框架中,还有更多复杂的功能和特性,如AOP、循环依赖处理、Bean的作用域、配置文件的自动装配等。这些是更高级的功能,需要更复杂的设计和实现。但通过上述步骤,您可以实现一个简单的IOC容器,理解IOC的基本概念和实现原理。

说说你对Spring 的理解?

官网地址:Spring Framework

压缩包下载地址:JFrog

源码地址:GitHub - spring-projects/spring-framework: Spring Framework

Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’s needs. As of Spring Framework 5.1, Spring requires JDK 8+ (Java SE 8+) and provides out-of-the-box support for JDK 11 LTS. Java SE 8 update 60 is suggested as the minimum patch release for Java 8, but it is generally recommended to use a recent patch release.
​
Spring supports a wide range of application scenarios. In a large enterprise, applications often exist for a long time and have to run on a JDK and application server whose upgrade cycle is beyond developer control. Others may run as a single jar with the server embedded, possibly in a cloud environment. Yet others may be standalone applications (such as batch or integration workloads) that do not need a server.
​
Spring is open source. It has a large and active community that provides continuous feedback based on a diverse range of real-world use cases. This has helped Spring to successfully evolve over a very long time.
​
Spring 是一个开源的轻量级企业应用开发框架,它提供了广泛的基础设施支持和生命周期管理,用于帮助开发人员构建企业级应用程序。以下是我对 Spring 的理解:

1. IoC(Inverse of Control,控制反转)容器: Spring 使用 IoC 容器管理应用程序中的对象依赖关系,通过将对象的创建、配置和组装过程由开发者转移到 Spring 容器,降低了组件之间的耦合度。

2. AOP(Aspect-Oriented Programming,面向切面编程)支持: Spring 提供了 AOP 配置和支持,可以将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来,实现了横切关注点的重用和集中管理。

3. **声明式事务管理:** Spring 提供了强大的事务管理支持,可以通过声明式的方式管理事务,简化了事务管理的实现,同时提供了灵活性和可定制性。

4. 模块化设计: Spring 框架被设计为多个模块组成,每个模块都提供了特定的功能,开发者可以根据需求选择使用相应模块,从而减少了不必要的资源浪费。

5. 简化企业级开发: Spring 提供了大量的企业级功能的封装和支持,如数据访问、事务管理、安全性、消息传递等,大大简化了开发人员构建企业级应用程序的工作量。

6. 持续演进和创新: Spring 框架不断进行演进和创新,持续引入新的功能和特性,以适应不断变化的开发需求和技术趋势。

总的来说,Spring 框架提供了一套全面且灵活的解决方案,帮助开发人员简化企业级应用程序的开发和维护工作,提高了代码的可维护性、可测试性和整体质量。通过合理使用 Spring 框架,开发人员可以专注于业务逻辑的实现,而不必过多关注底层技术细节。

你觉得Spring的核心是什么?

Spring是一个开源的Java框架,用于构建企业级的应用程序。它提供了一个容器,用于管理应用程序中的对象(如依赖注入)并提供了许多常用的功能和扩展,使开发人员能够更加专注于业务逻辑的实现而不必关注太多的底层细节。

Spring的核心特性包括:

  1. 控制反转(IoC):Spring使用控制反转来实现依赖注入。控制反转意味着对于对象的创建和依赖关系的管理由容器来完成,而不是由开发人员手动处理。这种方式可以降低组件之间的耦合度,并且使得代码更加灵活和易于扩展。

  2. 面向切面编程(AOP):Spring提供了面向切面编程的支持,可以通过AOP将横切关注点(如日志、事务管理)从业务逻辑中分离出来,使得代码更具可维护性和可读性。

  3. 集成框架:Spring可以与各种其他框架和技术进行集成,如持久化框架(Hibernate、MyBatis)、消息队列(ActiveMQ、RabbitMQ)、Web框架(Spring MVC)等,以便于开发人员在项目中使用更多的功能和工具。

总之,Spring框架提供了一种灵活、高效的开发方式,可以帮助开发人员构建可扩展、可维护的企业级应用程序。它的特性和扩展使得它在Java开发社区中非常流行和广泛使用。

说一下使用spring的优势?

使用Spring框架具有许多优势,它是一个强大的企业级应用程序开发框架,提供了广泛的功能和特性。下面是一些使用Spring框架的优势:

  1. 面向切面编程(AOP):Spring支持AOP,使你能够以声明性的方式处理跨多个模块的关注点(例如事务管理、安全性、日志记录等),提高了代码的模块化和可维护性。

  2. 依赖注入(DI):Spring的核心功能是依赖注入,它将组件之间的依赖关系转移给容器来管理。这种松耦合的设计使得代码更灵活、可测试性更强,并降低了代码的耦合度。

  3. 面向接口编程:Spring鼓励使用面向接口的编程风格,通过接口定义了各种组件之间的契约,使得代码更易于扩展和修改,并提供了解耦的效果。

  4. 组件化开发:Spring鼓励使用组件化开发的思想,将应用程序拆分为功能模块化的组件,每个组件负责特定的功能。这种模块化的设计使得应用程序更容易理解、扩展和维护。

  5. 轻量级和可扩展性:Spring框架本身非常轻量级,不需要很大的资源开销。同时,Spring提供了许多可选的模块和扩展,可以根据需求进行选择和集成,实现高度可定制的应用程序开发。

  6. 宽松的集成:Spring集成了许多其他流行的开源框架和技术,例如Hibernate、JPA、MyBatis、Spring MVC、Spring Boot等。这种宽松的集成能力使得开发人员可以根据自己的需求选择不同的技术和工具。

  7. 安全性:Spring提供了一套全面的安全性功能,包括身份认证、访问控制和加密等。这些功能可以帮助开发人员构建安全可靠的应用程序。

总的来说,Spring框架的优势在于提供了一种灵活、模块化、可扩展和高度定制的方式来开发企业级应用程序。它促进了良好的设计原则和最佳实践,提高了开发效率和代码质量。同时,Spring拥有庞大的社区支持和广泛的文档资源,使开发人员能够轻松学习和解决问题。

Spring是如何简化开发的?

Spring框架通过依赖注入、面向切面编程、组件化开发、技术整合和丰富的功能模块,简化了企业级应用程序的开发过程。它提供了一种灵活、模块化、可扩展和高度定制的方式来开发应用程序,提高了开发效率和代码质量。

IOC 运行时序

当调用 ApplicationContext 的 refresh() 方法时,Spring 容器将执行以下一系列步骤以完成初始化过程:

原文链接:【死磕 Spring】----- 深入分析 ApplicationContext 的 refresh()_为什么applicationcontext1.refresh();-CSDN博客

当调用 ApplicationContext 的 refresh() 方法时,Spring 容器将执行以下一系列步骤以完成初始化过程:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新上下文环境
        prepareRefresh();
​
        // 创建并初始化 BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
​
        // 填充BeanFactory功能
        prepareBeanFactory(beanFactory);
​
        try {
            // 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
            postProcessBeanFactory(beanFactory);
​
            // 激活各种BeanFactory处理器
            invokeBeanFactoryPostProcessors(beanFactory);
​
            // 注册拦截Bean创建的Bean处理器,即注册 BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
​
            // 初始化上下文中的资源文件,如国际化文件的处理等
            initMessageSource();
​
            // 初始化上下文事件广播器
            initApplicationEventMulticaster();
​
            // 给子类扩展初始化其他Bean
            onRefresh();
​
            // 在所有bean中查找listener bean,然后注册到广播器中
            registerListeners();
​
            // 初始化剩下的单例Bean(非延迟加载的)
            finishBeanFactoryInitialization(beanFactory);
​
            // 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
            finishRefresh();
        }
​
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
​
            //  销毁已经创建的Bean
            destroyBeans();
​
            // 重置容器激活标签
            cancelRefresh(ex);
​
            // 抛出异常
            throw ex;
        }
​
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

prepareRefresh()  初始化上下文环境

初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验,如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,重写initPropertySources方法就好了

该方法主要是做一些准备工作,如:

  1. 设置 context 启动时间

  2. 设置 context 的当前状态

  3. 初始化 context environment 中占位符

  4. 对属性进行必要的验证

obtainFreshBeanFactory()   创建并初始化 BeanFactory

创建并初始化 BeanFactory

核心方法就在 refreshBeanFactory() ,该方法的核心任务就是创建 BeanFactory 并对其就行一番初始化。如下:

protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

判断当前容器是否存在一个 BeanFactory,如果存在则对其进行销毁和关闭 调用 createBeanFactory() 创建一个 BeanFactory 实例,其实就是 DefaultListableBeanFactory 自定义 BeanFactory 加载 BeanDefinition loadBeanDefinitions() 是定义在 BeanDefinitionReader

将创建好的 bean 工厂的引用交给的 context 来管理

prepareBeanFactory(beanFactory)  填充 BeanFactory 功能

上面获取获取的 BeanFactory 除了加载了一些 BeanDefinition 就没有其他任何东西了,这个时候其实还不能投入生产,因为还少配置了一些东西,比如 context的 ClassLoader 和 后置处理器等等。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 设置beanFactory的classLoader
	beanFactory.setBeanClassLoader(getClassLoader());

	// 设置beanFactory的表达式语言处理器,Spring3开始增加了对语言表达式的支持,默认可以使用#{bean.xxx}的形式来调用相关属性值
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	// 为beanFactory增加一个默认的propertyEditor
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// 添加ApplicationContextAwareProcessor
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	// 设置忽略自动装配的接口
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

	// 设置几个自动装配的特殊规则
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// Register early post-processor for detecting inner beans as ApplicationListeners.
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// 增加对AspectJ的支持
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

	// 注册默认的系统环境bean
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}

postProcessBeanFactory()  提供子类覆盖的额外处理

提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
​
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

添加 ServletContextAwareProcessor 到 BeanFactory 容器中,该 processor 实现 BeanPostProcessor 接口,主要用于将ServletContext 传递给实现了 ServletContextAware 接口的 bean 忽略 ServletContextAware、ServletConfigAware 注册 WEB 应用特定的域(scope)到 beanFactory 中,以便 WebApplicationContext 可以使用它们。比如 “request” , “session” , “globalSession” , “application” 注册 WEB 应用特定的 Environment bean 到 beanFactory 中,以便WebApplicationContext 可以使用它们。如:“contextParameters”, “contextAttributes”

invokeBeanFactoryPostProcessors()    激活各种BeanFactory处理器

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
​
    // 定义一个 set 保存所有的 BeanFactoryPostProcessors
    Set<String> processedBeans = new HashSet<>();
​
    // 如果当前 BeanFactory 为 BeanDefinitionRegistry
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        // BeanFactoryPostProcessor 集合
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        // BeanDefinitionRegistryPostProcessor 集合
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
​
        // 迭代注册的 beanFactoryPostProcessors
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            // 如果是 BeanDefinitionRegistryPostProcessor,则调用 postProcessBeanDefinitionRegistry 进行注册,
            // 同时加入到 registryProcessors 集合中
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                // 否则当做普通的 BeanFactoryPostProcessor 处理
                // 添加到 regularPostProcessors 集合中即可,便于后面做后续处理
                regularPostProcessors.add(postProcessor);
            }
        }
​
        // 用于保存当前处理的 BeanDefinitionRegistryPostProcessor
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
​
        // 首先处理实现了 PriorityOrdered (有限排序接口)的 BeanDefinitionRegistryPostProcessor
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
​
        // 排序
        sortPostProcessors(currentRegistryProcessors, beanFactory);
​
        // 加入registryProcessors集合
        registryProcessors.addAll(currentRegistryProcessors);
​
        // 调用所有实现了 PriorityOrdered 的 BeanDefinitionRegistryPostProcessors 的 postProcessBeanDefinitionRegistry()
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
​
        // 清空,以备下次使用
        currentRegistryProcessors.clear();
​
        // 其次,调用是实现了 Ordered(普通排序接口)的 BeanDefinitionRegistryPostProcessors
        // 逻辑和 上面一样
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
​
        // 最后调用其他的 BeanDefinitionRegistryPostProcessors
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            // 获取 BeanDefinitionRegistryPostProcessor
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
​
                // 没有包含在 processedBeans 中的(因为包含了的都已经处理了)
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
​
            // 与上面处理逻辑一致
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
​
        // 调用所有 BeanDefinitionRegistryPostProcessor (包括手动注册和通过配置文件注册)
        // 和 BeanFactoryPostProcessor(只有手动注册)的回调函数(postProcessBeanFactory())
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
​
    else {
        // 如果不是 BeanDefinitionRegistry 只需要调用其回调函数(postProcessBeanFactory())即可
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
​
    //
    String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
​
    // 这里同样需要区分 PriorityOrdered 、Ordered 和 no Ordered
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        // 已经处理过了的,跳过
        if (processedBeans.contains(ppName)) {
            // skip - already processed in first phase above
        }
        // PriorityOrdered
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        // Ordered
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        // no Ordered
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
​
    // First, PriorityOrdered 接口
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
​
    // Next, Ordered 接口
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
​
    // Finally, no ordered
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
​
    // Clear cached merged bean definitions since the post-processors might have
    // modified the original metadata, e.g. replacing placeholders in values...
    beanFactory.clearMetadataCache();
}

上述代码较长,但是处理逻辑较为单一,就是对所有的 BeanDefinitionRegistryPostProcessors 、手动注册的 BeanFactoryPostProcessor 以及通过配置文件方式的 BeanFactoryPostProcessor 按照 PriorityOrdered 、 Ordered、no ordered 三种方式分开处理、调用。

registerBeanPostProcessors

注册拦截Bean创建的Bean处理器,即注册 BeanPostProcessor

与 BeanFactoryPostProcessor 一样,也是委托给 PostProcessorRegistrationDelegate 来实现的。

public static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
​
    // 所有的 BeanPostProcessors
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
​
    // 注册 BeanPostProcessorChecker
    // 主要用于记录一些 bean 的信息,这些 bean 不符合所有 BeanPostProcessors 处理的资格时
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
​
    // 区分 PriorityOrdered、Ordered 、 no ordered
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    // MergedBeanDefinition
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
​
    // First, PriorityOrdered
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
​
    // Next, Ordered
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);
​
    // onOrdered
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
​
    // Finally, all internal BeanPostProcessors.
    sortPostProcessors(internalPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, internalPostProcessors);
​
    // 重新注册用来自动探测内部ApplicationListener的post-processor,这样可以将他们移到处理器链条的末尾
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

initMessageSource

初始化上下文中的资源文件,如国际化文件的处理等

initApplicationEventMulticaster

初始化上下文事件广播器

onRefresh

给子类扩展初始化其他Bean

预留给 AbstractApplicationContext 的子类用于初始化其他特殊的 bean,该方法需要在所有单例 bean 初始化之前调用。

registerListeners

在所有 bean 中查找 listener bean,然后注册到广播器中

protected void registerListeners() {
    // 注册静态 监听器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }
​
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
​
    // 至此,已经完成将监听器注册到ApplicationEventMulticaster中,下面将发布前期的事件给监听器。
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

finishBeanFactoryInitialization

初始化剩下的单例Bean(非延迟加载的)

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 初始化转换器
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
​
    // 如果之前没有注册 bean 后置处理器(例如PropertyPlaceholderConfigurer),则注册默认的解析器
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }
​
    // 初始化 Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }
​
    // 停止使用临时的 ClassLoader
    beanFactory.setTempClassLoader(null);
​
    //
    beanFactory.freezeConfiguration();
​
    // 初始化所有剩余的单例(非延迟初始化)
    beanFactory.preInstantiateSingletons();
}

finishRefresh

完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人

主要是调用 LifecycleProcessor#onRefresh() ,并且发布事件(ContextRefreshedEvent)。

protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();
​
    // Initialize lifecycle processor for this context.
    initLifecycleProcessor();
​
    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();
​
    // Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));
​
    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}

Spring的AOP的底层实现原理

bean的创建过程中有一个步骤可以对bean进行扩展实现,aop本身就是一个扩展功能,所以在BeanPostProcessor的后置处理方法中来进行实现

不论使用哪种方式,Spring AOP的实现过程都包括以下几个关键步骤:

  1. 定义切点(Pointcut):切点定义了在何处应用切面逻辑的具体位置。通过表达式或注解等方式,确定应该被代理的目标方法或目标类。

  2. 编写切面(Aspect):切面是具体的横切逻辑实现,包括在目标方法执行前后执行的操作(通知,Advices)。切面可以包括一个或多个通知的定义。

  3. 创建代理对象:Spring根据切点和切面信息,生成动态代理或字节码增强的代理对象。代理对象在执行目标方法时,会根据切点和切面定义,执行相应的切面逻辑。

总结来说,Spring AOP的底层实现原理是通过动态代理或字节码生成,将切面逻辑织入到目标对象或目标类中,实现横切关注点的解耦和重用。

3.在执行方法调用的时候,会调用到生成的字节码文件中,直接回找到DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行

4.根据之前定义好的通知来生成拦截器

5.从拦截器链中依次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个CglibMethodInvocation的对象,找的时候是从-1的位置依次开始查找并且执行的。

说说你对Aop的理解?

AOP全称叫做 Aspect Oriented Programming 面向切面编程。它是为解耦而生的,解耦是程序员编码开发过程中一直追求的境界,AOP在业务类的隔离上,绝对是做到了解耦,在这里面有几个核心的概念:

  • 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schema-based approach)或者在普通类中以@Aspect注解(@AspectJ 注解方式)来实现。(Aspect作为一个模块来组织Join point , PointCut来筛选要执行的连接点,Advice在切面的某个特定的连接点上执行的动作),(一个Aspect对应多个join point,一个joinPoint 有多个 advice)

  • 连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。

  • 通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”, “before” and “after”等等。通知的类型将在后面的章节进行讨论。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。

  • 切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。

  • 引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。

  • 目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。

  • AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

  • 织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。

    这些概念都太学术了,如果更简单的解释呢,其实非常简单:

    任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码冗余太多,因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去,AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。

说说你对IOC的理解?

在 Spring 中,IoC 是 Inversion of Control(控制反转)的缩写,是 Spring 框架的一个重要特性。IoC 的本质是将对对象的控制权从应用程序本身转移到了框架或容器中,由容器来负责对象的创建、管理和注入依赖对象,从而降低了组件之间的耦合度,提高了代码的可重用性和可测试性。

在 Spring 中,IoC 容器负责管理应用程序中的对象及其之间的依赖关系,开发人员不再需要手动创建对象和管理对象之间的关联关系,而是通过配置文件或注解告诉容器如何创建和组装对象。IoC 容器负责实例化对象、注入依赖、生命周期管理等工作,开发人员只需要关注业务逻辑的实现,将对象的创建和依赖关系的管理交由容器来完成。

在 Spring 中,IoC 容器主要有两种实现方式:BeanFactory 和 ApplicationContext。

1. **BeanFactory:** 是 Spring 框架的基础容器,提供了基本的 IoC 功能,可以延迟加载(Lazy Loading)和按需注入(On-demand Injection)Bean,适合在资源有限的环境下使用。

2. **ApplicationContext:** 是 BeanFactory 的扩展,提供了更多的企业级功能和特性,如国际化支持、事件传播、AOP、声明式事务管理等,适合大多数企业级应用程序使用。

通过 IoC 容器,Spring 管理了应用程序中的所有对象,并负责解决对象之间的依赖关系,从而实现了代码的松耦合、易于维护和测试的优势。开发人员可以通过配置文件或注解,实现对象之间的依赖注入,让 IoC 容器来负责对象创建和管理,提高了应用程序的灵活性和可扩展性。

依赖注入DI 的概念

DI 是 Dependency Injection(依赖注入)的缩写,它是面向对象编程中的一种设计模式,在 Spring 框架中被广泛应用。DI 是 IoC(控制反转)的一种实现方式,通过依赖注入的方式来实现对象之间的解耦和管理依赖关系。

在 DI 中,对象不再负责创建或查找依赖的对象,而是将依赖关系注入到对象之中。这样做的好处是提高了代码的可维护性、可测试性和可扩展性,降低了对象之间的耦合度,使得代码更加灵活和易于重用。

在 Spring 框架中,依赖注入可以通过构造函数注入、Setter 方法注入或字段注入等方式实现。开发人员可以通过配置文件(如 XML 配置文件)或注解来指定依赖关系,让 Spring IoC 容器来实现依赖注入的过程。

依赖注入的三种主要类型包括:

1. **构造函数注入(Constructor Injection):** 通过在构造函数中注入依赖对象来实现依赖注入。

2. **Setter 方法注入(Setter Injection):** 通过 Setter 方法来设置依赖对象,实现依赖注入。

3. **字段注入(Field Injection):** 通过字段(属性)注入的方式来注入依赖对象。

依赖注入能够让开发人员专注于业务逻辑的实现,而不必担心对象之间的依赖关系,提高了代码的可维护性和可测试性。通过 DI,Spring 框架实现了 IoC 容器的依赖管理功能,让开发人员将对象之间的关系交给容器管理,实现了控制反转的思想。

谈一下spring IOC的底层实现

底层实现:工作原理,过程,数据结构,流程,设计模式,设计思想

你对他的理解和你了解过的实现过程

反射,工厂,设计模式,关键的几个方法

createBeanFactory , getBean ,doGetBean , createBean , doCreateBean,createBeanInstance(getDeclaredConstructor(),newinstance),populateBean

1.先通过createBeanFactory 创建一个Bean工厂(DefaultListableBeanFactory)

2.开始循环创建对象,因为容器中的bean默认都是单例的,所以优先通过getBean,doGetBean从容器中查找,找不到的话,

3.通过createBean,doCreateBean方法,以反射的方式创建对象,一般情况下使用的是无参的构造器(getDeclaredConstructor(),newinstance)

4.进行对象的属性填充populateBean

5.进行其他的初始化操作(initializingBean)

BeanFactory和ApplicationContext有什么区别

相同:

  • Spring提供了两种不同的IOC 容器,一个是BeanFactory,另外一个是ApplicationContext,它们都是Java interface,ApplicationContext继承于BeanFactory(ApplicationContext继承ListableBeanFactory。

  • 它们都可以用来配置XML属性,也支持属性的自动注入。

  • 而ListableBeanFactory继承BeanFactory),BeanFactory 和 ApplicationContext 都提供了一种方式,使用getBean("bean name")获取bean。

不同:

BeanFactory和ApplicationContext是Spring框架中的两个核心接口,它们具有以下区别:

  1. 功能:

    • BeanFactory是Spring的最基本的接口,提供了IoC容器的基本功能,即管理和获取Bean。

    • ApplicationContext是对BeanFactory的扩展,提供了更多的功能,如国际化支持、事件发布、资源加载、AOP等。

  2. 预加载:

    • BeanFactory是延迟加载的,即在调用getBean()方法时才会创建和初始化Bean。

    • ApplicationContext可以通过配置进行预加载,即在容器启动时就实例化和初始化所有的单例Bean。

  3. 配置元数据的处理:

    • BeanFactory使用BeanDefinition来管理和解析配置元数据,Bean的创建由BeanDefinition中的信息描述。

    • ApplicationContext在BeanFactory的基础上进一步扩展了对配置元数据的处理,提供更高级的功能,如自动扫描、注解驱动等。

  4. Spring AOP:

    • BeanFactory需要手动将AspectJ代理工厂(AspectJProxyFactory)应用于Bean,才能支持Spring AOP。

    • ApplicationContext自动为Bean提供代理,支持Spring AOP。

  5. 国际化支持:

    • ApplicationContext提供了更方便的国际化(i18n)支持,可以基于ResourceBundle实现多语言切换。

总的来说,BeanFactory是更基础的接口,提供了最基本的IoC容器功能;而ApplicationContext是BeanFactory的扩展,提供了更多的功能和便利性,是在实际应用中更常用的接口。如果需要更完整的功能,如AOP、国际化、事件发布等,那么建议使用ApplicationContext。但是,如果对资源要求较低,且只需要基本的Bean管理功能,可以使用BeanFactory来获得更轻量级的容器。

简述spring bean的生命周期?

  1. 实例化(Instantiation):

    • Bean的实例化是通过构造函数或工厂方法创建的。

    • 可以在此阶段进行一些前置处理,容器会在初始化Bean之前先调用BeanPostProcessor的postProcessBeforeInitialization()方法。

  2. 属性设置(Population):

    • Spring通过依赖注入(DI)将Bean的属性进行设置。

    • 可以在此阶段进行一些属性相关的操作,如使用@Autowired@Value等注解进行属性注入和赋值。

  3. 初始化(Initialization):

    • 在Bean的所有属性被设置完后,可以执行自定义的初始化逻辑。

    • 可以通过实现InitializingBean接口的afterPropertiesSet方法或使用@PostConstruct注解进行初始化操作。

    • 可以在此阶段进行一些初始化相关的操作,如连接数据库、读取配置文件等。

    • BeanPostProcessor的后置处理:如果在容器中定义了BeanPostProcessor,容器会在初始化Bean之后调用BeanPostProcessor的postProcessAfterInitialization()方法。

  4. 使用(In Use):

    • Bean可以在应用程序中正常使用,处理业务逻辑。

    • Bean会一直处于使用状态,直到应用程序关闭或Bean被销毁。

  5. 销毁(Destruction):

    • 当应用程序关闭或手动销毁Bean时,会执行销毁阶段的逻辑。

    • 可以通过实现DisposableBean接口的destroy方法或使用@PreDestroy注解进行销毁操作。

    • 可以在此阶段进行一些资源释放、清理工作,如关闭数据库连接、释放文件句柄等。

值得注意的是,对于单例作用域的Bean,默认情况下,Spring容器在启动时会将所有单例Bean进行实例化和初始化,并在容器关闭时销毁它们;而对于原型作用域的Bean,Spring容器仅负责实例化和初始化,不负责销毁,需要由调用者自行管理。

通过在Bean上实现相应的接口或使用注解,可以对Bean的生命周期进行自定义操作,从而满足应用程序的需求。

spring支持的bean作用域有哪些?

  1. Singleton(默认):

    • 单例作用域,每个Spring容器只创建一个实例。

    • 多个Bean引用同一个实例。

    • 适用于无状态的Bean和共享资源的情况。

  2. Prototype:

    • 原型作用域,每次从容器中获取Bean时都会创建一个新的实例。

    • 每个Bean引用不同的实例。

    • 适用于有状态的Bean和非共享资源的情况。

  3. Request:

    • 请求作用域,每个HTTP请求都会创建一个新的实例。

    • 每个请求期间的所有Bean引用同一个实例。

    • 适用于Web应用程序中需要在每个请求期间共享数据的情况。

  4. Session:

    • 会话作用域,每个用户会话都会创建一个新的实例。

    • 每个用户会话期间的所有Bean引用同一个实例。

    • 适用于Web应用程序中需要在每个用户会话期间共享数据的情况。

  5. Global session(仅适用于Portlet环境):

    • 全局会话作用域,每个Portlet应用程序的全局会话都会创建一个新的实例。

    • 每个全局会话期间的所有Bean引用同一个实例。

  6. WebSocket(仅适用于WebSocket环境):

    • WebSocket作用域,每个WebSocket会话都会创建一个新的实例。

    • 每个WebSocket会话期间的所有Bean引用同一个实例。

可以通过在Spring的Bean声明中使用@Scope注解或在XML配置文件中使用<bean>元素的scope属性来设置Bean的作用域。例如:

@Component
@Scope("prototype")
public class MyPrototypeBean {
    // ...
}

或者在XML配置中:

<bean id="myPrototypeBean" class="com.example.MyPrototypeBean" scope="prototype"/>

根据应用程序的需求,选择适合的作用域可以更好地控制Bean的生命周期和资源管理。

Spring框架中的单例Bean是线程安全的么?

spring只是一中容器,需要看有没有对bean进行多线程处理

Spring中的Bean对象默认是单例的,框架并没有对bean进行多线程的封装处理

如果Bean是有状态的

无状态就是不会存储数据,你想一下,我们的controller,service和dao本身并不是线程安全的,只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制遍历,这是自己线程的工作内存,是最安全的。

因此在进行使用的时候,不要在bean中声明任何有状态的实例变量或者类变量,如果必须如此,也推荐大家使用ThreadLocal把变量变成线程私有,如果bean的实例变量或者类变量需要在多个线程之间共享,那么就只能使用synchronized,lock,cas等这些实现线程同步的方法了。

spring框架中使用了哪些设计模式及应用场景

1.工厂模式,在各种BeanFactory以及ApplicationContext创建中都用到了

2.模版模式,在各种BeanFactory以及ApplicationContext实现中也都用到了

3.代理模式,Spring AOP 利用了 AspectJ AOP实现的! AspectJ AOP 的底层用了动态代理

4.策略模式,加载资源文件的方式,使用了不同的方法,比如:ClassPathResourece,FileSystemResource,ServletContextResource,UrlResource但他们都有共同的借口Resource;在Aop的实现中,采用了两种不同的方式,JDK动态代理和CGLIB代理

5.单例模式,比如在创建bean的时候。

6.观察者模式,spring中的ApplicationEvent,ApplicationListener,ApplicationEventPublisher

7.适配器模式,MethodBeforeAdviceAdapter,ThrowsAdviceAdapter,AfterReturningAdapter

8.装饰者模式,源码中类型带Wrapper或者Decorator的都是

spring事务的实现方式原理是什么?

在使用Spring框架的时候,可以有两种事务的实现方式,一种是编程式事务,有用户自己通过代码来控制事务的处理逻辑,还有一种是声明式事务,通过@Transactional注解来实现。

在 Spring 中,事务的实现方式主要依赖于两个核心概念:事务管理器(Transaction Manager)和代理模式。

  1. 事务管理器(Transaction Manager):Spring 提供了多个事务管理器的实现,例如 DataSourceTransactionManager、JpaTransactionManager 等。事务管理器负责管理事务的开始、提交、回滚等操作。它与底层的数据库、ORM 框架或其他事务资源进行交互,确保事务的正确执行。

  2. 代理模式:Spring 使用 AOP(面向切面编程)和代理模式来实现事务。当方法被标注为事务性时,Spring 会动态地生成一个代理类来包装该方法,代理类负责增加额外的逻辑,如事务的管理。

工作原理如下:

  1. 通过配置事务管理器,将其与当前的数据源或其他事务资源进行关联。

  2. 在配置文件或注解中对需要进行事务管理的方法进行标注 @Transactional。

  3. 当标注的方法被调用时,Spring 会通过动态代理机制生成一个代理类,并在代理类中添加事务管理的逻辑。代理类会拦截方法的调用,并在方法执行前后执行事务的开启、提交或回滚等操作。

  4. 根据配置的事务属性,事务管理器会根据方法的执行结果决定事务的提交或回滚。

具体的实现机制包括:

  • 事务的划分:通过注解或配置方式,标识哪些方法需要进行事务管理。

  • 事务的边界控制:Spring 会在适当的时机开始一个事务(如方法开始时)和结束一个事务(如方法结束时)。

  • 事务的隔离级别与传播行为控制:通过设置事务的隔离级别(如读未提交、读已提交等)和传播行为(如支持当前事务、新建事务等),对事务进行精确控制。

  • 异常处理与事务回滚:对于出现异常的情况,根据配置的事务属性决定是否回滚事务。如果方法内部抛出了受检查异常,Spring 默认会回滚事务;如果抛出非受检查异常,则根据异常类型决定是否回滚。

通过以上的机制和流程,Spring 能够提供基于代理的事务管理功能,将事务的管理与具体业务逻辑解耦,使得事务管理更加灵活、方便和可控。

Transaction失效

1、bean对象没有被spring容器管理

2、方法的访问修饰符不是public

3、自身调用问题

4、数据源没有配置事务管理器

5、数据库不支持事务

6、异常被捕获

7、异常类型错误或者配置错误

spring事务的隔离级别有哪些?

spring中的事务隔离级别就是数据库的隔离级别,有以下几种:

read uncommitted

read committed

repeatable read

serializable

在进行配置的时候,如果数据库和spring代码中的隔离级别不同,那么以spring的配置为主。

spring的事务传播机制是什么?

Spring的事务传播(Transaction Propagation)机制定义了在多个事务方法相互调用时,事务是如何传播和管理的。事务传播机制主要用于控制事务的边界和隔离级别,以保证数据的一致性和完整性。

Spring框架定义了以下几种事务传播行为:

1、propagation_required:支持当前事务,无事务,另起新事物
2、propagation_required-new:新建事务,若有旧事务,挂起。
3、propagation_supports:支持当前事务,无事务,以非事务执行
4、propagation_mandatory:以事务方式执行,无事务,抛异常
5、propagation_not_supported:不支持事务,如有事务,挂起
6、propagation_never:以非事务执行,有事务,抛异常
7、propagation_nested:内切事务

这些事务传播行为可以通过在方法上使用 @Transactional 注解来指定。例如:

@Transactional(propagation = Propagation.REQUIRED)
public void doSomething() {
    // 执行业务逻辑
}

需要注意的是,事务的传播行为只在嵌套方法调用中生效,对于同一个类中的不同方法调用,事务传播行为不起作用。在开发过程中,根据业务需求和实际情况选择适合的事务传播行为可以有效地保证数据的一致性和完整性。

NESTED和REQUIRED_NEW的区别:

REQUIRED_NEW是新建一个事务并且新开始的这个事务与原有事务无关,而NESTED则是当前存在事务时会开启一个嵌套事务,在NESTED情况下,父事务回滚时,子事务也会回滚,而REQUIRED_NEW情况下,原有事务回滚,不会影响新开启的事务

NESTED和REQUIRED的区别:

REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一个事务,那么被调用方出现异常时,由于共用一个事务,所以无论是否catch异常,事务都会回滚,而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不会回滚。

spring事务什么时候会失效?

在Spring框架中,事务的失效可能由以下情况引起:

  1. 未开启事务:在方法上没有使用@Transactional注解或在XML配置中未配置事务管理器和事务拦截器时,事务将不会开启,即使方法中出现数据库操作也不会有事务支持。

  2. 异常未被捕获:当在事务范围内的方法抛出未被捕获的异常时,默认情况下事务将回滚。但是,如果异常被捕获并处理(例如通过try-catch块),那么事务将不会回滚。

  3. 不受事务管理的方法调用:如果在具有事务的方法内部调用了另一个没有使用@Transactional注解的方法,则被调用方法不会继承事务,即事务将被挂起,它将在调用返回后恢复。

  4. 事务边界不一致:当在一个方法中通过注解配置的事务与使用编程式事务管理方式(通过TransactionTemplatePlatformTransactionManager)管理的事务混合使用时,事务可能会失效。这是因为Spring无法同时应用两种不同的事务管理方式。

  5. ThreadLocal问题:如果使用了并发编程中的ThreadLocal,例如在事务中使用了ThreadLocal保存状态,那么在多线程环境下,由于线程切换可能导致线程间的状态共享问题,从而破坏了事务的一致性。

  6. 方法级别的调用问题:如果在同一个类中的一个使用@Transactional注解的方法调用了同一个类中另一个没有使用注解的方法,事务将无法正常生效,因为Spring使用代理对象来管理事务,而调用同一个类中的方法将不会经过代理,导致事务失效。

需注意的是,事务失效并不是Spring框架的设计缺陷,而是由于使用不当或特定的情况下引起的。为了确保事务的正常工作,请确保在使用事务时遵循相关的最佳实践,并注意上述可能导致事务失效的情况。

什么的是bean的自动装配,它有哪些方式?

Bean的自动装配是指Spring容器在创建Bean时,自动地根据特定规则在上下文中寻找匹配的依赖项,并将其注入到Bean中。这样可以减少配置文件的编写,提高开发效率,减少开发人员的工作量。

Spring框架支持以下几种Bean的自动装配方式:

1. **no(默认):** 不使用自动装配,手动指定依赖注入的方式(通过构造器、setter方法等)。

2. **byName:** 根据bean的名称进行自动装配,Spring容器在上下文中查找与属性名称匹配的bean,然后进行注入。

3. **byType:** 根据bean的类型进行自动装配,Spring容器在上下文中查找与属性类型匹配的bean,然后进行注入。如果匹配的bean有多个,则抛出异常。

4. **constructor:** 类似于byType,但是是应用于构造器参数的自动装配方式。

5. **autodetect:** Spring根据情况自动选择合适的自动装配方式,该方式会先byType,如果找不到则会byName。

自动装配可以通过在XML配置文件中的`<bean>`标签的`autowire`属性来指定,也可以通过使用注解(如`@Autowired`、`@Resource`)来实现。在XML配置文件中使用自动装配的示例如下:

```xml
<bean id="beanA" class="com.example.BeanA" autowire="byName|byType|constructor|autodetect"/>
```

使用注解来实现自动装配的示例如下:


@Component
public class BeanA {
    @Autowired
    private BeanB beanB;

    // Getter and Setter
}

通过配置自动装配方式,Spring容器能够根据预定义的规则来自动解决Bean之间的依赖关系,简化了配置工作。开发人员可以根据实际情况选择合适的自动装配方式,提高了代码的灵活性和可维护性。

spring是如何解决循环依赖的

Spring 在解决循环依赖时使用了三级缓存,分别是 singletonFactories、earlySingletonObjects 和 singletonObjects。这些缓存的使用可以帮助Spring在解决循环依赖问题时进行控制和管理。

  1. singletonFactories:在单例模式下,当 Spring 创建 Bean 时,如果发现该 Bean 正在创建中,它会将这个创建中的 Bean 存储在 singletonFactories 缓存中。这样可以用来检测循环依赖。

  2. earlySingletonObjects:当 Bean 的实例化完成但尚未完成初始化时,该 Bean 会被存储在 earlySingletonObjects 中。这样可以确保其他 Bean 在初始化时可以引用到这些尚未初始化的 Bean。

  3. singletonObjects:一旦 Bean 创建完成并完成初始化后,Spring 将会将其存储在 singletonObjects 缓存中。这样其他的 Bean 可以直接引用到已经初始化完成的 Bean 实例。

Spring 解决循环依赖的方法是通过依赖注入的过程中使用对象的代理。当发现循环依赖时,Spring 会创建一个提前暴露的、尚未完全初始化的代理对象作为依赖项,以满足对象之间的交叉引用。

具体的解决流程如下:

  1. 当 Spring 容器初始化时,创建 Bean A 的过程中发现了循环依赖,需要解决 A 依赖于 B,而 B 又依赖于 A 的情况。

  2. 将正在创建 Bean A 的对象实例放入 singletonFactories 缓存中,表示该 Bean A 目前正在创建中。

  3. 接着创建 Bean B 的过程中,发现 B 依赖于 A,但 A 正在创建中,因此从 singletonFactories 缓存中获取正在创建的 Bean A 的对象实例。

  4. 当 Bean A 创建完成但尚未完成初始化时,将其放入 earlySingletonObjects 缓存中,表示 Bean A 已经实例化但尚未初始化。

  5. 继续创建 Bean B,并注入 Bean A 的未完成初始化的实例。

  6. 最后完成 Bean A 和 Bean B 的创建和初始化后,将 Bean A 和 Bean B 放入 singletonObjects 缓存中,表示它们是已经创建完成和初始化的 Bean 实例。

需要注意的是,虽然 Spring 能够解决大多数情况下的循环依赖,但如果存在循环链,即 A 依赖于 B,B 依赖于 C,C 又依赖于 A,则 Spring 将无法解决这种循环依赖关系,会抛出 BeanCurrentlyInCreationException 异常。

总结一下,Spring 解决循环依赖的方式是通过提前暴露半初始化的代理对象,以满足循环依赖关系中的引用,然后在完成所有 Bean 的创建和初始化后,将代理对象替换为实际对象。这样就能够成功解决循环依赖的问题。

不能解决的几种循环依赖

  1. 构造函数循环依赖:如果Bean之间存在构造函数注入,且存在循环依赖,Spring容器无法解决这种循环依赖,会抛出BeanCurrentlyInCreationException异常。

  2. 原型Bean循环依赖:如果两个原型Bean之间存在循环依赖,Spring容器无法解决这种循环依赖,因为原型Bean每次获取都会创建新的对象实例,无法缓存起来,也无法通过代理对象解决。

  3. @Async增强的bean循环依赖

pring中常见的几种注解

Spring 框架中常见的注解包括但不限于:

  1. @Component:用于表示一个类是 Spring 容器管理的组件,通常与其他注解配合使用,如 @Repository@Service@Controller 等。

  2. @Autowired:标记在字段、构造器、方法或者参数上,用于实现自动装配,通过类型匹配进行依赖注入。

  3. @Qualifier:与 @Autowired 配合使用,在存在多个具有相同类型的实例时,通过指定名称来选择具体的实例进行注入。

  4. @Value:用于注入简单属性值,支持 SpEL 表达式,例如 @Value("${app.title}")

  5. @Configuration:标记在配置类上,表示该类是 Spring 的配置类,会告诉 Spring 应用上下文去加载 Bean 定义。

  6. @Bean:标记在方法上,将方法的返回对象注册为一个 Bean,该对象会被 Spring IoC 容器管理。

  7. @ComponentScan:配置包扫描路径,用于自动扫描并注册标记有 @Component 的类作为 Spring Bean。

  8. @RequestMapping:定义请求 URL 映射,用于处理 Web 请求,通常用在 Controller 类的方法上。

  9. @Service@Repository@Controller:用于添加更多细分类型的组件注解,让 Spring 在扫描注解时能更清楚地区分各种组件类型。

  10. @Scope:定义 Bean 的作用域,例如 Singleton、Prototype、Request、Session 等。

  11. @Aspect@Pointcut@Before@After@Around@AfterReturning@AfterThrowing:用于定义切面和通知的注解,实现面向切面编程(AOP)。

  12. @Transactional:用于声明事务处理的方法或类,标注在方法或类上,指明该方法或类需要进行事务处理。

这些注解是 Spring 框架中常见且重要的注解,它们能够帮助我们简化配置、实现依赖注入、定义 Bean、处理 Web 请求、实现事务管理等。

当然,还有更多在 Spring 框架中常见和常用的注解:

  1. @RequestMapping:在控制器类或方法上使用,用于映射 Web 请求的 URL 路径和处理方法。

  2. @PathVariable:在方法参数上使用,用于从请求的 URL 中获取路径变量的值。

  3. @RequestParam:在方法参数上使用,用于从请求的参数中获取值。

  4. @RequestHeader:在方法参数上使用,用于从请求头中获取值。

  5. @ResponseBody:通常在控制器方法上使用,用于将方法返回的对象转换为指定的响应格式(如 JSON)并作为响应返回。

  6. @ModelAttribute:用于绑定请求参数到方法参数或方法的返回对象。

  7. @Valid:通常结合 JSR-303 标准的验证注解一起使用,用于验证方法参数或方法返回对象的数据的合法性。

  8. @InitBinder:用于配置用于表单数据绑定的 WebDataBinder,通常用于注册自定义的属性编辑器或验证器。

  9. @RestController:一个方便的组合注解,结合了 @Controller@ResponseBody,用于定义 RESTful 风格的控制器。

  10. @ConfigurationProperties:用于将属性绑定到配置类中的属性上,可以用于统一管理项目的配置参数。

  11. @Async:标记在方法上,允许方法异步执行,可以提升系统的性能和并发处理能力。

  12. @Enable* 系列注解:如 @EnableCaching@EnableScheduling@EnableAsync 等,用于启用相应的特性或功能。

这些注解在不同的场景中扮演着重要的角色,让我们能够更便捷地进行开发和配置,减少了大量的样板代码和配置工作。它们为 Spring 提供了更强大的功能和灵活性,简化了开发过程。

spring、springmvc、springboot的区别是什么?

spring和springMvc:

  1. spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案;

  2. springMvc是spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于spring框架中WEB层开发的一部分;

springMvc和springBoot:

1、springMvc属于一个企业WEB开发的MVC框架,涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等,XML、config等配置相对比较繁琐复杂;

2、springBoot框架相对于springMvc框架来说,更专注于开发微服务后台接口,不开发前端视图,同时遵循默认优于配置,简化了插件配置流程,不需要配置xml,相对springmvc,大大简化了配置流程;

总结:

1、Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring的ioc、aop等. ioc 提供了依赖注入的容器, aop解决了面向横切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能;

2、springMvc主要解决WEB开发的问题,是基于Servlet 的一个MVC框架,通过XML配置,统一开发前端视图和后端逻辑;

3、由于Spring的配置非常复杂,各种XML、JavaConfig、servlet处理起来比较繁琐,为了简化开发者的使用,从而创造性地推出了springBoot框架,默认优于配置,简化了springMvc的配置流程;但区别于springMvc的是,springBoot专注于单体微服务接口开发,和前端解耦,虽然springBoot也可以做成springMvc前后台一起开发,但是这就有点不符合springBoot框架的初衷了;

springmvc工作流程是什么?

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

1、DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

2、HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

3、返回处理器执行链,根据url查找控制器,并且将解析后的信息传递给DispatcherServlet

4、HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

5、执行handler找到具体的处理器

6、Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

7、HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

8、DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。 9、视图解析器将解析的逻辑视图名传给DispatcherServlet。

10、DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图,进行试图渲染

11、将响应数据返回给客户端

springmvc的九大组件有哪些?

Spring MVC(Model-View-Controller)是一个基于Java的MVC框架,用于开发Web应用程序。下面是Spring MVC的九大组件:

  1. DispatcherServlet(调度器):是Spring MVC的核心组件,接收客户端的请求并将其分派给相应的处理器。

  2. HandlerMapping(处理器映射器):负责根据请求的URL映射到相应的处理器(Controller)。

  3. HandlerAdapter(处理器适配器):将处理器(Controller)适配为一个能够处理请求的处理器对象,并调用其处理方法。

  4. HandlerInterceptor(处理器拦截器):对请求进行预处理和后处理。在请求被调度到处理器之前和之后,可以执行一些公共的任务,如身份验证、日志记录等。

  5. ViewResolver(视图解析器):根据逻辑视图名(View Name)解析视图对象,可以是JSP、Thymeleaf、Freemarker等等。

  6. View(视图):负责渲染模型数据,并将其呈现给用户。常见的视图技术包括JSP、HTML、JSON等。

  7. ModelAndView(模型和视图):将模型数据与视图对象封装在一起,以便向视图传递数据并指示要呈现的视图。

  8. HandlerExceptionResolver(异常解析器):处理请求过程中产生的异常。将异常转换为统一的错误视图或错误消息。

  9. MultipartResolver(多部分解析器):处理文件上传请求,解析多部分请求,获取上传的文件和表单数据。

这些组件共同协作,使得Spring MVC能够接收、处理和响应客户端的请求,并以MVC模式将应用程序的逻辑、数据和视图进行了清晰的分离。

Spring框架中有哪些不同类型的事件

Spring 提供了以下5种标准的事件:

上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

瑞_23种设计模式_外观模式

文章目录 1 外观模式&#xff08;Facade Pattern&#xff09;1.1 介绍1.2 概述1.3 外观模式的结构 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 jdk源码解析 &#x1f64a; 前言&#xff1a;本文章为瑞_系列专栏之《23种设计模式》的外观模式篇。本文中的部分…

如何在Windows部署TortoiseSVN客户端并实现公网连接内网VisualSVN服务端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

Flutter开发之Slider

Flutter开发之Slider 本文是关于介绍Slider相关属性的含义。 class SliderThemeData {/// slider轨道的高度 final double? trackHeight; /// 滑块滑过的轨道颜色 final Color? activeTrackColor; /// 滑块未滑过的轨道颜色 final Color? inactiveTrackColor; /// 滑块滑过…

JavaEE——简单认识JavaScript

文章目录 一、简单认识 JavaScript 的组成二、基本的输入输出和简单语法三、变量的使用四、JS 中的动态类型图示解释常见语言的类型形式 五、JS中的数组六、JS 中的函数七、JS 中的对象 一、简单认识 JavaScript 的组成 对于 JavaScript &#xff0c;其中的组成大致分为下面的…

多线程如何设计?一对多/多对一/多对多

二、14个多线程设计模式 参考原文&#xff1a;https://www.cnblogs.com/rainbowbridge/p/17443503.html single Thread 模式 一座桥只能通过一个人 Single Thread模式是一种单线程设计模式&#xff0c;即在一个应用程序中只有一个主线程、一个事件循环&#xff0c;对外只提…

【C语言基础】:深入理解指针(一)

文章目录 一、内存和地址1. 内存2. 如何理解编址 二、指针变量和地址2.1 取地址操作符(&)2.2 指针变量和解引用操作符(*)2.2.1 指针变量2.2.2 如何拆解指针变量2.2.3 解引用操作符 2.3 指针变量的大小 三、指针变量类型的意义3.1 指针的解引用3.2 指针 - 整数3.3 void*指针…

什么是物联网?

今天这篇文章写的相关内容就是带领大家了解什么是物联网&#xff0c;之前写的文章大多都是一些物联网的未来&#xff0c;行业的解决方案等&#xff1b;话不多说开始进入正题吧! 物联网(IoT)是一个包罗万象的术语&#xff0c;指的是越来越多的电子产品&#xff0c;它们不是传统的…

vue2+elementui上传照片(el-upload 超简单)

文章目录 element上传附件&#xff08;el-upload 超详细&#xff09;代码展示html代码data中methods中接口写法 总结 element上传附件&#xff08;el-upload 超详细&#xff09; 这个功能其实比较常见的功能&#xff0c;后台管理系统基本上都有&#xff0c;这就离不开element的…

计算机组成原理4-存储器的层次结构与程序访问的局部性原理

1. 磁盘 1.磁盘的结构 磁盘由盘片构成&#xff0c;每个盘片包含两面 每面由一组称为磁道的同心圆组成 每个磁道划分为一组扇区&#xff0c;扇区之间由间隙隔开 同一半径上的所有磁道组成一个柱面2.磁盘的容量 容量&#xff1a;磁盘上可以存储的最大位数。 决定因素&#xff1a…

【玩转408数据结构】线性表——双链表、循环链表和静态链表(线性表的链式表示 下)

知识回顾 在前面的学习中&#xff0c;我们已经了解到了链表&#xff08;线性表的链式存储&#xff09;的一些基本特点&#xff0c;并且深入的研究探讨了单链表的一些特性&#xff0c;我们知道&#xff0c;单链表在实现插入删除上&#xff0c;是要比顺序表方便的&#xff0c;但是…

IDEA利用鼠标调整字体大小

就可以按住ctrl和鼠标调节代码字体的大小啦&#xff01; 如果有用&#xff0c;记得给我来个赞~ 谢啦&#xff01;

【python基础学习07课_函数基础课】

一、函数的基础知识 一、函数的作用是用来干什么的&#xff1f; 函数在编程中是一个组织好的、可重复使用的代码块&#xff0c;用于执行一个特定的任务。具体来说&#xff0c;函数的常见作用包括&#xff1a;1、执行计算或数据处理。 2、控制程序的流程&#xff0c;如条件判断…

Java+SpringBoot+Vue+MySQL:员工健康管理技术新组合

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

压缩式 交换式 碎片整理 :(使碎片减少或没有)

交换式碎片整理 首先流程 是 p3这个程序在运行&#xff0c;p1p2p4 的话在等待 &#xff0c;然后p3这时要多用3个内存块&#xff0c;这是 p4 通过拷贝&#xff0c;将内存拷贝到磁盘上&#xff0c;对应的数据也是从主存中cp到磁盘此时主存多出3个内存块给p3继续使用 2.压缩式碎片…

QML中动态增加表格数据

1.QML中的表格实现 import QtQuick 2.15 import QtQuick.Window 2.15import QtQuick.Controls 2.0 import Qt.labs.qmlmodels 1.0 import QtQuick.Layouts 1.15Window {width: 640height: 480visible: truetitle: qsTr("Hello World")TableModel{id:table_modelTabl…

Transformer之self-attention

注意力是一个有助于提高神经机器翻译应用程序性能的概念。在这篇文章中&#xff0c;我们将看看Transformer&#xff0c;一个使用注意力来提高这些模型训练速度的模型。Transformer在特定任务中优于谷歌神经机器翻译模型。最大的好处来自于Transformer如何使自己适合并行化。 在…

135.乐理基础-半音是小二度吗?全音是大二度吗?三全音

内存参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;134.乐理基础-音程名字的简写-CSDN博客 上一个内容里练习的答案&#xff1a; 半音可以与小二度划等号吗&#xff1f;全音可以和大二度划等号吗&#xff1f; 严格来说它们是不能划等号的&#xff0c;半音与全音是侧…

Android Studio level过滤查看各个等级的日志

Android Studio level过滤查看各个等级的日志 旧版as可以在下方的日志输出框选择debug、info&#xff0c;warn、error日志&#xff0c;新版的需要通过在过滤框手动/联想输入 level:xxx&#xff0c;过滤相应等级的日志&#xff0c;如图&#xff1a; android studio/idea返回/前进…

vue使用gitshot生成gif

vue使用gitshot生成gif 问题背景 本文将介绍vue中使用gitshot生成gif。 问题分析 解决思路&#xff1a; 使用input组件上传一个视频&#xff0c;获取视频文件后用一个video组件进行播放&#xff0c;播放过程进行截图生成图片数组。 demo演示上传一个视频&#xff0c;然后生…

Python 从文件中读取JSON 数据并解析转存

文章目录 文章开篇Json简介Json数据类型Json硬性规则Json数据转化网站Json和Dict类型转换json模块的使用Python数据和Json数据的类型映射json.dumps1.字典数据中含有**存在中文**2.json数据通过缩进符**美观输出**3.对Python数据类型中键进行**排序输出**4.json数据**分隔符的控…
最新文章