Spring中@Service 、@Component、@Repository等注解区别是什么?

典型回答 在Spring框架中,有很多用来声明Spring管理的bean的常用注解。它们都是@Component的特化形式,用于指定不同类型的组件,主要有以下几个: @Component:是一个通用的组件声明注解,表示该类是一个Spring组件。它可以用于任何Spring管理的组件。 @Service:通常用于标记服务层的组件。虽然它本质上与@Component相同,但这个注解表示该类属于服务层,这有助于区分不同层次的组件。 @Repository:用于标记数据访问层的组件,即DAO(Data Access Object)层。这个注解除了将类标识为Spring组件之外,还能让Spring为它提供一些持久化特定的功能,比如异常转换。 @Controller:用于标记控制层的组件,特别是在Spring MVC中用于定义控制器类。这个注解通知Spring该类应当作为控制器处理HTTP请求。 ** 这些注解在Spring框架中的主要区别在于它们的语义意图,在功能上几乎没有差异!只是为了让我们识别出我们标注的Bean到底是个什么角色,是一个Service、还是一个Repository、又或者是一个Controller。**

March 22, 2026 · 1 min · santu

如何在Spring启动过程中做缓存预热

典型回答 这个问题其实是考察如何在Spring启动过程中做额外操作,其实有挺多做法的,这里就介绍几种比较常用的: ApplicationReadyEvent 在应用程序启动时,可以通过监听应用启动事件,或者在应用的初始化阶段,将需要缓存的数据加载到缓存中。 ApplicationReadyEvent 是 Spring Boot 框架中的一个事件类,它表示应用程序已经准备好接收请求,即应用程序已启动且上下文已刷新。这个事件是在 ApplicationContext 被初始化和刷新,并且应用程序已经准备好处理请求时触发的。 基于ApplicationReadyEvent,我们可以在应用程序完全启动并处于可用状态后执行一些初始化逻辑。使用 @EventListener 注解或实现 ApplicationListener 接口来监听这个事件。例如,使用 @EventListener 注解: 1 2 3 4 5 @EventListener(ApplicationReadyEvent.class) public void preloadCache() { // 在应用启动后执行缓存预热逻辑 // ... } Runner 如果你不想直接监听**ApplicationReadyEvent,在SpringBoot中,也可以通过****CommandLineRunner 和 ApplicationRunner** 来实现这个功能。 CommandLineRunner 和 ApplicationRunner 是 Spring Boot 中用于在应用程序启动后执行特定逻辑的接口。这解释听上去就像是专门干这个事儿的。 ...

March 22, 2026 · 2 min · santu

Spring 中的 Bean 是线程安全的吗?

典型回答 Spring的Bean是否线程安全,这个要取决于他的作用域。Spring的Bean有多种作用域,其中用的比较多的就是Singleton和Prototype。 默认情况下,Spring Bean 是单例的(Singleton)。这意味着在整个 Spring 容器中只存在一个 Bean 实例。如果将 Bean 的作用域设置为原型的(Prototype) ,那么每次从容器中获取 Bean 时都会创建一个新的实例。 对于Prototype这种作用域的Bean,他的Bean 实例不会被多个线程共享,所以不存在线程安全的问题。 但是对于Singleton的Bean,就可能存在线程安全问题了,但是也不绝对,要看这个Bean中是否有共享变量。 ✅能不能谈谈你对线程安全的理解? 如以下Bean: 1 2 3 4 5 6 7 8 @Service public class CounterService { private int count = 0; public int increment() { return ++count; } } 默认情况下,Spring Bean 是单例的,count字段是一个共享变量,那么如果多个线程同时调用 increment 方法,可能导致计数器的值不正确。那么这段代码就不是线程安全的。 我们通常把上面这种Bean叫做有状态的Bean,有状态的Bean就是非线程安全的,我们需要自己来考虑他的线程安全性问题。 那如果一个Singleton的Bean中是无状态的,即没有成员变量,或者成员变量只读不写,那么他就是个线程安全的。 1 2 3 4 5 6 7 @Service public class CounterService { public int increment(int a) { return ++a; } } 所以,总结一下就是: ...

March 22, 2026 · 1 min · santu

Spring中如何实现多环境配置?

典型回答 我们平常在开发的时候,会有多套环境,开发环境、测试环境、预发布环境以及生产环境,不同的环境中应用的配置可能不太一样。那么在如何利用Spring来管理这些配置呢? Spring 的 Profile 是一种通过在不同环境中使用不同配置的方式来实现条件化配置。Profile 允许你为不同的环境定义特定的配置。 使用 Profile 的步骤: 定义 Profiles:在配置类或配置文件中定义不同的 Profiles,例如使用 @Profile(“dev”) 在 Java 配置类上。 1 2 3 4 5 6 7 8 9 10 11 @Configuration @Profile("dev") public class DevConfig { // 开发环境的特定配置 } @Configuration @Profile("prod") public class ProdConfig { // 生产环境的特定配置 } 激活 Profile:可以通过多种方式激活 Profile,如设置环境变量、JVM 参数、Web 应用的上下文参数或在 application.properties 文件中使用 spring.profiles.active 属性。 1 2 ● 环境变量:SPRING_PROFILES_ACTIVE=prod ● JVM 参数:-Dspring.profiles.active=prod 这样,启动应用时,Spring 将根据激活的 Profile 加载相应的配置。在这个例子中,如果激活的是 prod Profile,将创建并使用 ProdConfig 中的 Bean; ...

March 22, 2026 · 1 min · santu

Spring的事务在多线程下生效吗?为什么?

典型回答 Spring 的事务有多种实现,主要包括了声明式事务和编程式事务。 ✅Spring中如何开启事务? 如果是我们常用的@Transactional这种声明式事务的话,在多线程情况下是无法生效的。主要是因为@Transactional 的事务管理使用的是 ThreadLocal 机制来存储事务上下文,而** ThreadLocal 变量是线程隔离的**,即每个线程都有自己的事务上下文副本。因此,在多线程环境下,Spring 的声明式事务会“失效”,即新线程中的操作不会被包含在原有的事务中。 ✅父子线程之间怎么共享数据? 不过,如果需要管理跨线程的事务,我们可以使用编程式事务,即自己用 TransactionTemplate 或PlatformTransactionManager 来控制事务的提交。 扩展知识 源码解析 @Transactional 的事务管理入口在TransactionManager的实现中,如我们看一下DataSourceTransactionManager类的实现。 看一下他的doBegin方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @Override protected void doBegin(Object transaction, TransactionDefinition definition) { // 将传入的事务对象转换为 DataSourceTransactionObject DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { // 如果当前事务对象没有持有连接,或者持有的连接已经与事务同步,则获取一个新的数据库连接 if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = obtainDataSource().getConnection(); // 从数据源获取新的连接 if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } // 设置当前事务对象持有的连接 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } // 标记连接已经与事务同步 txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); // 获取当前持有的连接 // 准备连接的事务设置,比如隔离级别 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); txObject.setReadOnly(definition.isReadOnly()); // 设置事务是否只读 // 如果需要,切换连接为手动提交模式。这在某些 JDBC 驱动中可能代价很高,因此不是非必要不进行设置 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); // 关闭自动提交 } // 准备事务性连接,可能包括设置保存点等 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true); // 标记事务为活跃状态 // 设置事务超时时间 int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // 如果是新的连接持有者,则将连接持有者绑定到当前线程 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { // 如果在尝试开始事务过程中出现异常,并且是新的连接持有者,则释放连接并清理连接持有者 if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } // 抛出无法创建事务的异常 throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } 重点是上面的第47-49行代码,这里是把一个connection和当前线程进行绑定。看下绑定代码的实现: ...

March 22, 2026 · 2 min · santu

Spring的@Autowired能用在Map上吗?

典型回答 可以的。 在Spring框架中,@Autowired 注解不仅可以用于单个bean的注入,还可以用于注入复杂的集合类型,如List、Map、Set等。这种机制非常有用,尤其是当你需要注入同一类型的多个bean时。 List 当你使用@Autowired在一个List字段上时,Spring会将所有匹配的bean类型注入到这个列表中。这是自动按类型注入的一个例子。这意味着如果你有多个bean都是同一接口的实现,Spring会将它们全部收集起来,注入到这个List中。 1 2 @Autowired private List<HollisService> services; // 将注入所有HollisService类型的bean Map 使用Map时,key通常是bean的名称,value是bean的实例。这允许你不仅按类型注入,还可以按名称引用具体的bean。这在你需要根据名称动态选择bean时非常有用。 1 2 @Autowired private Map<String, HollisService> servicesMap; // 键是bean的名称,值是HollisService类型的实例 这通常用在工厂模式中,如: ✅你在工作中是如何使用设计模式的? Set 与List类似,使用Set可以注入所有匹配的bean类型,但注入到Set中的bean实例将是唯一的(无重复元素),这依赖于bean的equals和hashCode实现。 1 2 @Autowired private Set<HollisService> servicesSet; // 将注入所有HollisService类型的bean,但每个实例只出现一次 数组 你也可以使用数组类型来注入。这与使用List类似,Spring会注入所有匹配类型的bean到数组中。 1 2 @Autowired private HollisService[] servicesArray; // 将注入所有HollisService类型的bean 注意事项 当使用这些集合类型注入时,如果没有找到任何匹配的bean,Spring默认的行为是抛出异常。你可以通过设置@Autowired(required = false)来避免这种情况,这样如果没有找到匹配的bean,Spring就不会注入任何值(字段将保持为null)。

March 22, 2026 · 1 min · santu

如何根据配置动态生成Spring的Bean?

典型回答 在 Spring 应用中,根据运行时的配置(比如数据库配置、配置文件、配置中心等)动态生成 Spring Bean 是一种常见需求,特别是在面对多环境配置或者需要根据不同条件创建不同实例时。 Spring 提供了几种方式来实现这一需求。 基于条件的 Bean 注册 Spring提供了@Conditional(或者@ConditionalOnProperty)注解,允许你在满足特定条件时才创建Bean。你可以定义自己的条件,这些条件实现了Condition接口。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("my.condition.enabled", Boolean.class, false); } } @Configuration public class MyConfiguration { @Bean @Conditional(MyCondition.class) public MyBean myBean() { return new MyBean(); } } my.condition.enabled可以通过配置文件或者配置中心进行配置,然后当my.condition.enabled属性为true时,MyBean才会被创建。 ...

March 22, 2026 · 2 min · santu

Spring 中注入 Bean 有几种方式

典型回答 注入 Bean,就是说已经有一个 Bean 了,我想在其他的 Bean 中使用它,就要注入。比如场景的我们在 Service 中注入Dao。 在 Spring 中,Bean 的注入也是非常常见的操作了,有以下几种形式。 使用@Autowired 注解 @Autowired 注解是Spring框架的一部分,用于自动装配(自动注入)Spring Bean到其他Bean中。 他可以标注在字段(成员变量)、构造器、以及 setter 方法上。 字段注入 1 2 3 4 5 @Component public class HollisService { @Autowired private HollisRepository hollisRepository; } ✅为什么Spring不建议使用基于字段的依赖注入? 构造器注入 1 2 3 4 5 6 7 8 9 10 @Component public class HollisService { private HollisRepository hollisRepository; @Autowired public HollisService(HollisRepository hollisRepository){ this.hollisRepository = hollisRepository; } } setter 注入 1 2 3 4 5 6 7 8 9 10 @Component public class HollisService { private HollisRepository hollisRepository; @Autowired public void setHollisRepository(HollisRepository hollisRepository){ this.hollisRepository = hollisRepository; } } 使用 @Resource 和 @Inject 注解 Bean的注入,除了用 Spring 中提供的注解以外,还可以用 JDK 给我们提供的注解,包括了@Resource 和 @Inject 。 ...

March 22, 2026 · 2 min · santu

Spring中创建Bean有几种方式_

典型回答 Spring 的 Bean 的创建有以下几种方式,从常见到不常见开始逐一列举: 通过@Component系列注解 Spring 中提供了很多注解,可以直接把一个类的实例定义成 Bean。常见的有: @Component @Service @Repository @Controller ✅Spring中@Service 、@Component、@Repository等注解区别是什么? 代码实现如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Service public class HollisService { public String helloWorld(String name) { return "Hello, " + name + "!"; } } @Component public class HollisInvokeHandler { public String helloWorld(String name) { return "Hello, " + name + "!"; } } @Controller public class HollisController { public String helloWorld(String name) { return "Hello, " + name + "!"; } } @Repository public class HollisRepository { public String helloWorld(String name) { return "Hello, " + name + "!"; } } 通过@Bean 注解 在 SpringBoot 的应用中,我们通常会见到通过@Bean 注解来定义 Bean 的代码,尤其是在我们自己需要封装 Starter 的时候。 ...

March 22, 2026 · 2 min · santu

同时使用 @Transactional 与 @Async 时,事务会不会生效?

典型回答 这个问题其实和下面这个是类似的问题,要考察的内容是一样的。但是我们的这个问题要更加复杂一些。 ✅Spring的事务在多线程下生效吗?为什么? 因为要回答这个问题,需要分情况讨论的。网上很多文章说会失效,但是其实不对的。我们分情况讨论: 1、如果@Transactional 与 @Async加在同一个方法上,那么事务会生效的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * 用户服务 * * @author hollis */ @Service public class UserService { @Transactional @Async public void register(String telephone, String inviteCode) { userMapper.save(user); userStreamMapper.insertStream(user, UserOperateTypeEnum.REGISTER); throw new UnsupportedOperationException(""); } } 以上代码,在执行后,事务会因为捕获到UnsupportedOperationException er 导致回滚,即 user 表和 userStream 表都没有变更。 ...

March 22, 2026 · 2 min · santu

留言给博主