spring面试题


前言

虽然说是为了面试,但是呢我们也得不断学习,先学习思想很重要,只有思想到了,技术什么的我理解用一用,整理一下怎么使用就OK了。
若是还有什么新的面试题,我会补充,也希望大家能留言共同维护

当然可以私聊,我希望能结交一些志同道合的朋友
在这里插入图片描述


一、面试题

1、springIOC是什么?

1、是一种思想,(Inversion of Control:控制反转)而不是一个具体的技术实现
2、IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理,但并不是spring独有
3、简单理解就是在一个Map容器中(并非真的Map)存储了要被调用的类的对象,使用的时候直接用就行,不需要进行new;

2、springIOC产生的原因:背景

历史:

在类A中我们调用类B中的方法:
但是类A中存在很多方法假设10个,在没有spring的时候我们需要再每个方法中
B b = new B(); 在通过b.方法()来实现具体调用的方法,这样我们就需要写十个。

问题:

  1. 代码冗余:每个需要类B的方法都要重复创建实例的代码,导致相同的代码散布在多个位置。
  2. 维护成本高:每次修改类B的构造函数或依赖时,所有创建了类B实例的地方都可能需要更新,这增加了维护成本并且容易引入错误。
  3. 耦合度高:类A直接依赖于类B的具体实现,这导致两者之间的耦合度增加,如果要替换类B或其生成逻辑,会涉及到对类A的大量更改。
  4. 其他:在对象.方法1的时候并不会涉及到说对方法1进行修改,所以这里也算是springIOC为什么能出现的一个小原因,但是我不知道怎样描述才能更准确。

为了解决这些问题,Spring框架引入了IoC(控制反转)原则。借助于IoC容器,我们可以将对象的创建和管理从程序代码中抽象出来,交由容器负责。为了使类A能够使用类B而无需直接实例化,我们要在类A中注入一个类B的引用:

@Service
public class B {
    // 类B的实现
}
@Component
public class A {
    @Autowired
    private B b; // Spring容器注入类B的实例
    // 类A的方法可以使用注入的b引用来调用方法
}

@Service 和 @Component 注解告诉Spring容器,它们修饰的类需要被实例化并纳入Spring容器的管理。
这通常意味着将它们作为Bean进行创建和配置。
@Autowired 注解用于自动注入依赖:告诉Spring容器,在创建类A的实例时,应当自动寻找类型为B的实例,并将其注入到类A的b字段中。
如此,所有类A的方法都能重用这一个由Spring容器提供的类B实例。
另一个选择是@Resource注解,该注解通常按名称进行依赖注入,但也可以配置按类型注入。
使用注解和IoC容器的好处是,我们可以通过简单地更改配置来更换依赖的实现,而无需修改业务逻辑代码。
这降低了类之间的耦合,使应用程序的扩展、维护以及测试更加简便。

3、spring 中Bean的生命周期:

1、Bean定义的解析:

容器通过读取配置来源(如XML文件、注解或者Java配置类)来查找并加载所有的Bean定义(Bean Definitions)。

2、Bean实例化:

容器使用Java反射API创建Bean的实例(通常是通过调用默认构造函数)。

3、依赖注入:

容器为Bean实例的属性注入相应的依赖。

4、Aware接口的处理:

  • setBeanName(String): 设定Bean的名称。
  • BeanFactoryAware.setBeanFactory(BeanFactory): 设定BeanFactory引用。
  • ApplicationContextAware.setApplicationContext(ApplicationContext): 设定ApplicationContext引用。
  • 以及其他的Aware接口,如 EnvironmentAware, ResourceLoaderAware 等,容器会调用对应的setXxx方法。

5、BeanPostProcessor前置处理:

对实现了 BeanPostProcessor 接口的Bean,容器调用
postProcessBeforeInitialization(Object bean, String beanName)方法进行前置处理。
此外,BeanPostProcessors 也实现了 postProcessBeforeInstantiation() 和 postProcessAfterInstantiation() 方法,它们分别在实例化前后调用

6、初始化:

  • InitializingBean.afterPropertiesSet(): 如果Bean实现了 InitializingBean 接口,则调用此方法。
  • 自定义初始化方法:如果Bean定义了init-method属性,容器调用指定的自定义初始化方法。

7、BeanPostProcessor后置处理:

对实现了 BeanPostProcessor 接口的Bean,容器调用
postProcessAfterInitialization(Object bean, String beanName) 方法进行后置处理。
此外,BeanPostProcessors 也实现了
postProcessBeforeInstantiation() 和
postProcessAfterInstantiation() 方法,它们分别在实例化前后调用

8、Bean的使用:

完成上述步骤后,Bean就可以被应用程序使用了。

9、销毁前处理:

对实现了 DestructionAwareBeanPostProcessor 接口的Bean,容器调用
postProcessBeforeDestruction(Object bean, String beanName) 方法。

10、销毁处理:

DisposableBean.destroy(): 如果Bean实现了 DisposableBean 接口,则在容器关闭时调用此方法。
自定义销毁方法:如果Bean定义了destroy-method属性,容器在关闭时调用指定的自定义销毁方法。

11、容器关闭:

最后,当应用结束,容器被关闭时,所有单例Bean的清理工作将会被触发。

请注意,以上描述的是Bean的典型生命周期,但实际过程可能会因使用不同的Bean作用域(如单例、原型等)而有所差别。
在原型作用域中,Spring容器不管理一个Bean的完整生命周期,容器会创建Bean实例,进行初始化,然后将其交给客户端代码,不负责销毁。
而在单例作用域中,上述所描述的生命周期是完整的。

4、springAOP:

建议看这篇文章:动态代理——既有落地也有思想
先理解动态代理是什么在学习这个
简而言之就是:
1、动态代理
2、什么时候植入代码:编译时植入、运行时植入等等
3、怎么使用其实就是那几个注解:
看这篇文章:springAOP落地实现

5、Spring框架中的设计模式-太多了

1、单例模式(Singleton Pattern):

  • 在Spring中,单例模式通过Bean的默认作用域实现。每个Spring管理的Bean默认为单例,意味着每次请求都会获得相同的实例。

  • 源码体现:DefaultSingletonBeanRegistry 类中的 singletonObjects 缓存存放着Bean的单例实例。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry {
    // 单例对象的高速缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 省略其他部分

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        // 省略事务代码
        return singletonObject;
    }
}

2、工厂模式(Factory Pattern):

Spring使用工厂模式用于Bean的创建。BeanFactory 是一个工厂接口,它定义了获取Bean的方法。

源码体现:BeanFactory 接口定义了获取Bean的操作。

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    // 省略其他部分
}

3、代理模式(Proxy Pattern):

  • Spring AOP使用了代理模式。它通过JDK代理或CGLIB代理在运行时创建Bean的代理,以增加切面逻辑。

  • 源码体现:JdkDynamicAopProxy 类使用了JDK动态代理技术。

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    // 使用JDK代理实现AOP的相关代码
    
    public Object getProxy(ClassLoader classLoader) {
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在调用具体的目标方法之前或之后加入增强的代码
    }
}

4、模板方法模式(Template Method Pattern):

  • Spring中的JdbcTemplate和HibernateTemplate等类使用了模板方法模式。它们定义了算法的骨架,将算法的一些步骤延迟到子类中实现。

  • 源码体现:JdbcTemplate 中的 execute 方法。

public abstract class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        // 初始化和清理代码
        try {
            Statement stmt = con.createStatement();
            return action.doInStatement(stmt);
        } finally {
            // 资源释放代码
        }
    }
    // 省略其他部分
}

5、观察者模式(Observer Pattern):

  • Spring事件处理机制使用观察者模式,允许Bean监听应用事件。

  • 源码体现:ApplicationEventMulticaster 用于事件的广播。

public interface ApplicationEventMulticaster {
    void addApplicationListener(ApplicationListener<?> listener);
    void multicastEvent(ApplicationEvent event);
}

6、策略模式(Strategy Pattern):

  • Spring中资源访问策略,如资源加载Resource和资源解析ResourceLoader。Spring允许不同的策略用于不同类型的资源访问。

  • 源码体现:ResourceLoader 接口和它的实现类。

public interface ResourceLoader {
    Resource getResource(String location);
}

public class DefaultResourceLoader implements ResourceLoader {
    public Resource getResource(String location) {
        if (location.startsWith("/")) {
            return new UrlResource(location);
        } else {
            return new FileSystemResource(location);
        }
    }
}

上面仅列出了Spring框架中使用的一些设计模式,并给出了简略的代码示例。在Spring源码的众多部分都可以发现这些设计模式的身影,它们是理解和扩展Spring框架的重要基础。要深入了解这些模式在Spring源码中的应用,建议直接阅读Spring源码,并结合实际的项目场景来加深理解。

spring的三级缓存:解决循环依赖问题

说明:图是我忘了在什么地方截取的了,在我的个人笔记中,今天借鉴一下,若是有人看见可以告诉我一下。侵权则请联系我,我删除
在这里插入图片描述
1、什么叫循环依赖?

public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public void setA(A a) {
        this.a = a;
    }
}

流程简述:

创建A:将A放入三级缓存中,这时发现需要创建B,
创建B:这时B需要A发现三级缓存中有A,将A拿出放入二级缓存中并将三级缓存中的A删掉,
B创建完成,然后接着
再创建A:A创建完成将A放入一级缓存,将二级缓存中的A删掉。

详细一些:

1、开始创建A:

  • Spring容器开始创建Bean A。
  • 在Bean A的创建过程中,容器会将创建A的ObjectFactory放入三级缓存中。

2、处理A的依赖B:

  • 在A的处理过程中,发现A依赖于B,因此容器开始创建Bean B。

3、开始创建B:

  • Spring容器开始创建Bean B。
  • 在Bean B的创建过程中,容器会将创建B的ObjectFactory放入三级缓存中。

4、处理B的依赖A:

  • 在B的处理过程中,发现B依赖于A,容器尝试获取Bean A的实例。
  • 容器在一级缓存中查找Bean A的实例,但此时并未找到。
  • 容器向三级缓存查询,并找到了负责创建A的ObjectFactory。
  • ObjectFactory负责返回A的早期引用(可能是通过代理进行的),这个早期引用随后被放入二级缓存中。
  • 注:三级缓存中的ObjectFactory不会在此时删除。它将保留直到A完全创建好,并初始化完成。

5、B创建完成:

  • B的依赖注入完成后,B的实例将会被放入一级缓存中。

6、继续创建A:

  • A创建过程继续,完成依赖注入(注入B)。

7、A创建完成:

  • 当A完全创建并完成初始化后,它将被放入一级缓存中。
  • 此时A已经在二级缓存中拥有早期引用,那么一级缓存中添加完成后,该早期引用会从二级缓存移除。
  • 最终当A从二级缓存移动到一级缓存后,A在三级缓存中的ObjectFactory也会被移除。

通过上述机制,Spring容器能够解决大多数单例Bean的循环依赖问题。但记住,这种处理方式仅适用于Spring中的单例Bean,且仅限于字段注入(setter注入),对于构造器注入的循环依赖,默认情况下Spring容器无法解决。

源码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 尝试从singletonObjects缓存中获取Bean实例
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果一级缓存中不存在,并且当前Bean正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 二级缓存中获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 如果二级缓存中也没有,并且允许提前暴露引用
            if (singletonObject == null && allowEarlyReference) {
                // 从三级缓存中获取对应的ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过ObjectFactory获取Bean的早期引用
                    singletonObject = singletonFactory.getObject();
                    // 将早期引用放入二级缓存,并从三级缓存中移除
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

spring事务:一个又真又假的命题

概念:

在计算机科学中,事务是指一些列操作的执行单元,这些操作要么全部完成,要不全部不完成,这确保了数据的完整性。事务有如下四大特效

  • 原子性(Atomicity): 事务内的所有操作都是一个不可分割的工作单元,它们要么全部成功,要么全部失败回滚。
  • 一致性(Consistency): 事务的执行从一个一致性状态转换到另一个一致性状态,意味着数据库中的数据将始终保持一致性规则。
  • 隔离性(Isolation): 提供了事务的隔离级别,它定义了一个事务可能受其他并发事务影响的程度。
  • 持久性(Durability): 一旦事务提交,则其对数据库的更改应该是永久的,即使系统发生故障也不会丢失。

Spring中的事务管理解决的问题:

  1. 简化事务管理:Spring提供了一致的事务管理接口,可以在不同的事务管理API(如JTA, JDBC, Hibernate等)之间进行切换而不影响业务代码。
  2. 声明式事务和编程式事务:声明式事务通过配置和注解就可以实现,从而使得业务代码不必与事务管理代码耦合。编程式事务则涉及到使用事务管理API编写更加灵活的事务代码,虽然灵活但代码侵入性较高。
  3. 事务同步:Spring事务管理保证了在一个事务上下文中的所有操作都是同步的。
  4. 事务的回滚:Spring能够自动回滚标记为@Transactional并在执行过程中抛出unchecked异常时的事务。

Spring事务中的概念

  • 声明式事务管理:使用注解(@Transactional)或XML配置来管理事务。
  • 编程式事务管理:使用TransactionTemplate或PlatformTransactionManager的API在代码中直接管理事务。
  • 事务传播行为:定义了当一个事务方法被另一个事务方法调用时,事务如何传播(如:REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER, NESTED)。
  • 事务隔离级别:定义了一个事务可能受其他并发事务影响的程度(如: READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE)。
  • 回滚规则:定义了导致事务回滚的异常类型。

容易让人混淆的概念:

  • 事务传播行为和隔离级别:这两个概念虽然都与事务管理的详细配置有关,但它们解决的问题不同。传播行为关注的是与其他事务的交互;隔离级别关注的是在并发条件下的数据可见性和一致性。
  • 声明式事务管理和编程式事务管理:声明式事务管理隐藏了事务管理相关的代码,因此显得简洁;而编程式事务管理需要显式调用事务API,给了开发者更高的控制力。容易混淆的点在于它们的使用场景和优势。
  • @Transactional注解的继承性:在类或接口上使用@Transactional注解,并不意味着它的所有子类或实现都会继承这个事务属性,实际行为可能取决于Spring版本和配置。
  • Spring支持的回滚行为:默认情况下,Spring只会在遇到运行时异常(unchecked exceptions)时回滚事务,如果要在受检异常(checked exceptions)发生时也进行回滚,需要额外配置。
  • 多个数据源和分布式事务:处理多个数据库连接和分布式事务时,Spring事务管理比较复杂,可能需要集成JTA或相应的分布式事务管理器。
  • spring整合MySQL 其spring本身并不具有事务这个概念,它仅仅是封装了(如JDBC、Hibernate、JPA等)的事务概念,其实际上常用的也仅仅是他的原子性和持久性,当然在特殊场景下也会用到执行和隔离性,但是一般都是默认

总结

补充

Spring IOC 常用注解以及含义:

@Component:

  • 含义:这个注解表明一个类将被Spring IoC容器作为组件类处理,即该类的实例可以由容器创建和管理。
  • 使用:通常标注在类上,作为通用的组件标识,通常与@Autowired结合使用进行依赖注入。
  • 示例:
@Component
public class MyComponent {
    // ...
}

@Service

  • 含义:标注在服务层(业务逻辑层)的类上,其继承自@Component,表明这是一个服务组件。
  • 使用:与@Component作用类似,但意图更明确的指向服务层组件。
  • 示例:
@Service
public class MyService {
    // ...
}

@Repository

  • 含义:标注在DAO(数据访问层)层的类上,用于指示类提供了数据仓库的功能,也继承自@Component。
  • 使用:可以让DAO层的异常透明地转换为Spring的数据访问异常。
  • 示例:
@Repository
public class MyRepository {
    // ...
}

@Controller

  • 含义:标注在MVC控制器类上,通常用于处理web请求,也是@Component的一个特化。
  • 使用:用在Spring MVC中,与@RequestMapping结合使用。
  • 示例:
@Controller
public class MyController {
    // ...
}

@Autowired

  • 含义:标注在字段、构造器或方法上,用于自动装配Bean。
  • 使用:该注解让Spring完成Bean的自动注入。如果有多个相同类型的Bean,可以配合@Qualifier使用来选择具体注入哪一个。
  • 示例:
@Service
public class MyService {
    @Autowired
    private MyRepository repository;
}

@Qualifier

  • 含义:与@Autowired结合使用,指定注入Bean的名称。
  • 使用:当存在多个同类型Bean时,用于消除自动装配过程中的歧义。
  • 示例:
@Autowired
@Qualifier("specificBean")
private MyInterface myBean;

@Resource

  • 含义:和@Autowired相似,用于自动装配Bean,但它是JSR-250的注解。
  • 使用:默认按照名称装配,如果找不到与名称匹配的bean,则按类型装配。
  • 示例:
@Resource(name = "myBean")
private MyInterface myBean;

@Bean

  • 含义:标注在方法上,表明该方法产生一个Bean,并且交给Spring容器管理。方法返回的实例会被注册为一个Bean。
  • 使用:通常用在配置类(带有@Configuration注解的类)中的方法上。
  • 示例:
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

@Configuration

  • 含义:标注在类上,指示一个类提供Spring框架的Bean定义信息。
  • 使用:用于定义配置类,配置类可以包含一个或多个@Bean注解方法。
  • 示例:
@Configuration
public class AppConfig {
    // ...
}

这些注解本身体现了Spring IoC的原则以及Spring 框架中的"约定大于配置"的理念,使用者可以通过简单的注解而不是复杂的XML配置来进行Spring的依赖注入和组件声明。

Spring事务注解@Transactional的属性和属性值:

1、value(或者叫transactionManager):

  • 指定使用的事务管理器bean的名字。

2、propagation: 用来设置事务的传播行为。

  • Propagation.REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,就新建一个事务。这是最常见的选择。
  • Propagation.SUPPORTS:如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务的方式执行。
  • Propagation.MANDATORY:如果当前存在事务,就加入该事务;如果当前没有事务,就抛出异常。
  • Propagation.REQUIRES_NEW:新建事务,如果当前存在事务,就把当前事务挂起。
  • Propagation.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • Propagation.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则表现如同REQUIRED。

3、isolation: 定义了数据的隔离级别。

  • Isolation.DEFAULT:使用底层数据存储的默认隔离级别。
  • Isolation.READ_UNCOMMITTED:允许读取尚未提交的变更,可能会导致脏读,不可重复读和幻线问题。
  • Isolation.READ_COMMITTED:保证一个事务修改的数据提交后才能被另一事务读取,避免脏读。
  • Isolation.REPEATABLE_READ:保证在同一个事务里多次读取同样记录的结果是一致的,避免不可重复读。
  • Isolation.SERIALIZABLE:所有的事务串行执行,避免脏读,不可重复读和幻读。

4、timeout: 指定强制回滚之前可以占用的时间。

  • 默认值为-1(使用底层存储的默认值,通常意味着没有超时限制)。
  • 设置为正整数,表示事务可以占用的最大时间(以秒为单位)。

5、readOnly: 指定当前事务是否只读。

  • true:表示这个事务只读取数据但不更新数据,可以帮助数据库引擎优化事务。
  • false:表示这个事务可能会进行数据更新。

6、rollbackFor: 指定哪些异常会触发事务回滚。

7、rollbackForClassName: 与rollbackFor相同,但是指定的是类的全限定名的字符串表示形式。

8、noRollbackFor: 指定哪些异常不会触发事务回滚。

9、noRollbackForClassName: 与noRollbackFor相同,但是指定的是类的全限定名的字符串表示形式。

了解@Transactional注解的这些属性和值,将有助于在开发过程中合理规划事务的边界和行为,实现有效且安全的数据操作。这些属性的组合可以解决不同事务需要之间的冲突,增强了事务管理的灵活性和功能性。

具体代码case

转账操作的例子,使其更加详尽和符合实际场景。考虑到数据库操作,我们将使用Spring Data JPA来定义实体和存储库接口。我们假设有Account实体和对应的repository用于简化数据库操作。

以下是代码示例,包含异常类、实体类、repository接口以及事务性转账服务的详细实现:


// 自定义的业务异常类,表示账户余额不足
public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

// 实体类,表示账户
@Entity
public class Account {
    @Id
    private Long id;

    private BigDecimal balance;

    // 省略了getter和setter方法

    public void withdraw(BigDecimal amount) throws InsufficientFundsException {
        if (balance.compareTo(amount) < 0) {
            throw new InsufficientFundsException("Insufficient funds");
        }
        balance = balance.subtract(amount);
    }

    public void deposit(BigDecimal amount) {
        balance = balance.add(amount);
    }
}

// Spring Data JPA repository,用于处理Account实体的CRUD操作
public interface AccountRepository extends JpaRepository<Account, Long> {
    // 这里可以自定义一些查询方法,但基本CRUD操作已由JpaRepository提供
}

// 服务类,包含转账的业务逻辑
@Service
public class AccountService {
    @Autowired
    private AccountRepository accountRepository;

    /**
     * 事务性的转账操作
     */
    @Transactional(rollbackFor = InsufficientFundsException.class)
    public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) throws InsufficientFundsException {
        Account fromAccount = accountRepository.findById(fromAccountId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid fromAccountId"));
        Account toAccount = accountRepository.findById(toAccountId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid toAccountId"));

        fromAccount.withdraw(amount);
        toAccount.deposit(amount);

        accountRepository.save(fromAccount);
        accountRepository.save(toAccount);
    }
}

在这个例子中,transfer方法负责处理两个账户之间的资金转移。其使用@Transactional注解来确保操作的原子性,即:

  • 如果withdraw方法因账户余额不足抛出InsufficientFundsException,整个转账操作应回滚,即不会有任何变更持久化到数据库。
  • 如果操作成功,即withdraw和deposit方法都没有抛出异常,那么这些变更将被保存到数据库中,并且整个事务将被成功提交。
    为了演示事务失败导致的回滚,你可以在转账操作中加入一些临时逻辑,如故意抛出异常,以观察事务的回滚效果。

请注意,为了这个例子的有效执行,你还需要一个配置Spring Data JPA和数据库连接的Spring Boot应用程序,并且Account实体与您的数据库模式相匹配。

最后,我再次强调,事务的管理和回滚行为取决于你的配置和事务管理器。为了使事务回滚正常工作,你还需要在Spring配置文件中确保事务管理器被正确设置。如果使用Spring Boot,默认情况下会自动配置事务管理器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值