spring面试题

spring中bean的生命周期

(1)实例化Bean:调用了构造方法或工厂方法产生了实例

(2)属性赋值:对依赖的实例进行注入(属性注入、setter方法注入、有参构造函数注入),@Value的值赋值给成员变量

(3)初始化:调用我们编写的初始化方法(如果有aop会在初始化之后使用)

(4)销毁:销毁前会调用我们编写的销毁方法

bean生命周期-实例化

spring首先解析配置文件或注解,使用反射获取Bean的定义信息

选择合适的实例化方式(如构造器实例化、静态工厂方法实例化或实例工厂方法实例化)来创建Bean的实例

实例化会把成员变量设置为默认值,如果我们没有指定默认值引用类型设置为null,基本数据类型int 设置为0等。

 

实例化方法的选择:

构造器实例化:当Bean的创建过程相对简单,没有复杂的配置信息及依赖时采用这种方式,在大多数情况下都是采用的这种

静态工厂实例化:静态工厂类提供一个静态方法来创建实例,当Bean的创建过程比较复杂,比如从配置文件、数据库或其他资源中读取配置信息并创建Bean。就可以采用这种

实例工厂实例化:需要首先创建工厂实例,然后再调用该实例上的方法来创建Bean。这种方式通常在工厂对象本身也需要由Spring管理的情况下使用。

对于静态工厂和实例化工厂实例化bean的方式,最终都是调用了bean的构造器方法。只不过这个bean创建时需要获取些配置或依赖一些其他资源通过工厂可以直接封装好。

当对象的创建逻辑相对固定,且不需要频繁更改时,静态工厂方法是一个很好的选择;当对象的创建逻辑依赖的内容多变。比较复杂时采用实例工厂方式更合适。

bean生命周期-属性赋值

属性注入阶段主要是对spring管理的bean注入到该bean的成员变量中。有@Value标记的属性也会被赋值

依赖注入的方式有:

  • 通过属性注入
  • 通过setter方法注入
  • 通过有参构造函数注入

bean生命周期-初始化

这个阶段的主要任务是执行一些自定义的初始化逻辑,或者调用与Bean初始化相关的回调方法。

如果Bean的配置中定义了初始化方法(通过init-method属性在XML配置中指定,或者通过@PostConstruct注解在Java配置中标注)会在这个阶段调用这个方法

如果Bean实现了InitializingBean接口,Spring会调用其afterPropertiesSet()方法。

执行顺序是:首先执行@PostConstruct注解标注的方法,然后执行afterPropertiesSet()方法,最后执行通过init-method属性指定的方法。

bean生命周期-销毁

当Spring容器关闭或销毁某个Bean时,会触发Bean的销毁方法。这通常是通过实现DisposableBean接口或标注@PreDestroy注解来完成的。如果Bean实现了DisposableBean接口,Spring会调用其destroy()方法;如果Bean标注了@PreDestroy注解,Spring会调用该注解所标注的方法。这些方法允许开发者在Bean销毁前执行一些必要的清理工作,比如关闭文件句柄、断开数据库连接、释放网络连接等。

可以在Spring的配置文件中通过<bean>元素的destroy-method属性来指定一个销毁方法。

Bean的销毁阶段只有在Spring容器关闭或显式销毁Bean时才会发生。

spring容器关闭:Web应用程序的服务器关闭时,或者在命令行应用程序执行完毕后;开发者可以通过调用Spring容器的close()destroy()方法来显式地关闭容器。

spring容器启动时会实例化那些类

会实例化那些被标记为@Component, @Service, @Repository, @Controller等注解的类,以及通过XML配置文件或Java配置类注册的Bean。这些类被Spring容器管理,因此在启动时会被实例化。

没有被spring管理的类不会被实例化,被设置为懒加载的类也不会在启动时实例化

用spring来管理的对象的创建时机

默认是在spring容器启动的时候创建(默认不是懒加载)

如果配置文件或者注解指定bean为懒加载在使用的时候创建

什么时候使用懒加载

使用懒加载可以减少程序启动时的资源和时间消耗

如果一个bean实例化过程中需要创建长时间运行的连接,如数据库连接、远程服务连接等。这些连接的创建可能会消耗较多的时间,使用懒加载。

有些功能或服务可能不是每次启动应用程序时都需要的,而是根据用户的操作或请求动态加载的。对于这些功能或服务,可以使用懒加载来优化性能。

什么时候进行依赖注入

实例创建完成后就对里面依赖的对象进行注入,如果采用有参构造函数注入的方式是实例创建时调用构造函数的时候就进行注入。

循环依赖问题

概念:

两个或则两个以上的bean互相持有对方,最终形成闭环

 

出现循环依赖问题时spring的反应:

  • 如果是根据有参构造器来注入bean存在循环依赖的话,启动项目会报错(实例化bean的时候进行依赖注入,因为依赖的对象无法实例化所以无法成功),spring无法处理这样的循环依赖问题

 

  • 如果根据setter方式来注入bean(bean先实例化bean在调用setter方法设置属性),对于单例spring可以通过三级缓存处理循环依赖的问题

注入对象是单例:调用setter方式时发现依赖对象没有就会调用依赖对象的构造方法来实例化,到最后就都注入成功了(本质是是三级缓存发挥作用,对于未完成初始化的实例可以会放到第三级缓存中,获取注入对象时会从一级开始逐层找,最终在第三级缓存找到,找到后会把它放到二级缓存中,初始化完成的bean会放到第一级缓存中)

注入的对象是多例:没有三级缓存结构,如果对象是未初始化状态直接抛异常

 

  • 通过在属性上添加@Autowired注解方式给注入同样道理,单例可以解决循环问题,多例不可以

 

spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的属性可以延后设置

 

三级缓存说明:spring三级缓存是用于解决循环依赖问题的机制

一级缓存,存储beanName(key)和bean实例(value)之间的关系,这里存储的bean实例是已经完全初始化完成的bean实例

二级缓存,也是存储beanName和bean实例之间的关系,在Bean的实例化过程中,如果发现循环依赖,会将尚未完全初始化的Bean放入该缓存中。

三级缓存:用于保存beanName和bean工厂之间的关系。在Bean实例化过程中,如果发现循环依赖,会将Bean的工厂对象放入该缓存中,当三级缓存中的beanName对应的bean创建,创建后会把beanName和bean实例关系放入的二级缓存,beanName从三级中删除。

 

三级缓存的使用过程

  • A在创建过程中需要B,A发现B也需要A,存在循环依赖问题,A先将自己的实例工厂放到三级缓存里面,去实例化B
  • B实例化的时候发现需要A,于是B先查一级缓存,没有再查二级缓存,还是没有,再查三级缓存,找到了A的实例工厂,通过A的实例工厂生成A的代理实例或者A的原始实例放到二级缓存;并删除三级缓存里面的A的实例工厂。
  • B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态);然后回来接着初始化A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成初始化,并将A放入到一级缓存中

 

为什么使用三级缓存

为了设计更加优雅,三级缓存中每一级中存放的都不一样,一级放成品,二级放半成品,三级放有循环依赖问题的工厂。有的说是为了解决代理对象的问题,如果要解决直接生成代理对象到二级缓存中就行,没必要有三级缓存。

 

为什么用有参构造方法注入会报错

根本原因:Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没依赖注入的状态。已经完成了构造函数的调用。

 

没有循环依赖问题会用到三级缓存吗

三级缓存主要用于解决循环依赖问题,确保在存在循环依赖时能够正确地解决依赖关系。对于没有循环依赖的实例创建过程,Spring会根据需要使用一级缓存和二级缓存,二级缓存的作用不仅仅是用于解决循环依赖问题,还可以提高Bean的实例化效率和管理单例Bean的生命周期。

是怎么使用的:在Bean实例化过程中,如果发现Bean存在依赖其他Bean,Spring会将尚未完全初始化的Bean放入二级缓存中(这样可以确保在依赖注入过程中,即使Bean尚未完全初始化,也能提前暴露给其他Bean使用),当Bean实例化完成并初始化后,会将Bean放入一级缓存中,表示该Bean已经完全初始化。

 

出现了spring无法解决的循环依赖问题怎么处理

重新设计,避免出现循环依赖

修改使用有参构造函数依赖注入的方式,修改为使用setter方法或者属性注入的方式

使用@Lazy注解:在其中一个类的构造函数上添加@Lazy注解,延迟加载Bean,以解决循环依赖问题。可以在一定程度上缓解循环依赖的问题,但并不能完全解决它。因为即使使用了@Lazy注解,当真正需要加载该bean时,如果仍然存在循环依赖,仍然会抛出异常。

 

@PostConstruct注解方式:去掉循环依赖中A类的注入,在B类的初始化的过程中在对A类的属性进行注入。这并不是解决循环依赖问题的最佳或推荐的做法。更好的方法是重新设计你的服务以消除或最小化循环依赖。

@PostConstruct注解方式

@Service
public class UserService {
 
    @Autowired
    private DepartmentService departmentService;
 
    @PostConstruct
    public void init() {
        departmentService.setUserService(this);
    }
 
    public DepartmentService getDepartmentService() {
        return departmentService;
    }
}

@Service
public class DepartmentService {
 
    private UserService userService;
     
    private String message = "Hi!";
 
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
     
    public String getMessage() {
        return message;
    }
}

 

spring中bean是线程安全的吗

Spring本身并不保证bean的线程安全性,而是由开发者在设计和实现bean时来确保线程安全性。

Spring默认使用单例模式创建bean。这意味着在整个应用程序中,同一个bean的实例只会被创建一次。如果bean的状态(即它的成员变量)在多个线程之间共享,并且这些线程可能会修改这些状态,那么就需要特别注意线程安全性问题。

如果bean是无状态的,即它不包含任何实例变量或这些变量不会被修改,那么这样的bean通常是线程安全的。

对于有成员变量并且使用过程中可能会修改,就要开发者考虑线程安全问题。比如A类有成员变量int i; 线程1对i进行修改,线程2也对i进行修改就会导致线程不安全问题。

spring与事务

spring事务与mysql

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。

spring事务如何配置

spring支持编程式事务管理声明式事务管理两种方式:

①编程式事务管理使用TransactionTemplate。

②声明式事务管理建立在AOP之上的。

 

spring项目中使用声明式事务管理方式,有两种方式

一种是基于tx和aop名字空间的xml配置文件

另一种是基于@Transactional注解

 

( 

如果事务方法中有redis的操作,redis是不会回滚的,所以最好不要把redis操作放在事务方法执行之后

spring事务本质其实就是数据库对事务的支持会,根据不同的数据源的api对事务进行处理,

 

要处理分布式事务使用spring+jta

jta:Java Transaction API 为J2EE平台提供了分布式事务服务

@Transactional注解说明

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。代理机制通常只代理接口方法或目标类的public方法。因为非public方法通常被认为是在类的内部使用

如果在当前类中进行内部调用方法,即一个类中a方法调用b方法,b方法上加@Transactional 注解,这时不能生效。这是因为事务的功能是靠生成代理对象调用代理方法执行的,它无法拦截同一个对象内部的方法调用。

默认情况下只在运行时异常时会进行回滚事务,可以自己指定回滚的异常和不回滚的异常

如果异常在方法内部被捕获处理掉了,不会回滚事务

 

如果非要让类内方法调用生效,可以自注入

@Service  
public class MyService {  
  
    @Autowired  
    private MyService self; // 自注入  
  
    @Transactional  
    public void transactionalMethod() {  
        // 事务性操作...  
    }  
  
    public void nonTransactionalMethod() {  
        // 类内调用事务方法,通过自注入的字段  
        self.transactionalMethod();  
        // 其他非事务性操作...  
    }  
}

声明式事务如何配置

全xml配置方式:事务管理器+事务规则+aop

<!-- 配置spring事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">       
          <property name="dataSource" ref="dataSource"></property>  
</bean>
<!-- 配置事务规则 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException"/>
        <tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.RuntimeException" />
        <tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.RuntimeException" />
        <tx:method name="create*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.RuntimeException" />
        <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
        <tx:method name="find*" propagation="SUPPORTS" />
        <tx:method name="get*" propagation="SUPPORTS" />
        <tx:method name="select*" propagation="SUPPORTS" />
        <tx:method name="query*" propagation="SUPPORTS" />
    </tx:attributes>
</tx:advice>
<!-- 把事务控制在service层,切入点配置,通知 -->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(public * com.zkzong.service.*.*(..))" />
    <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>

 

注解方式

<!-- 定义spring事务管理器 -->    
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
    <property name="dataSource" ref="dataSource" />    
</bean>    
<!--使用注释事务 -->    
<tx:annotation-driven  transaction-manager="transactionManager" />

 

在service的方法上标注@Transactional并指明事务规则

事务传播行为/级别

事务传播行为是Spring框架提供的一种事务管理方式,它不是数据库提供的。

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

 

七种事务传播行为

Propagation.REQUIRED:必须(默认的传播行为)

如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

 

Propagation.SUPPORTS:支持

如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

 

Propagation.MANDATORY:强制性的

如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

 

 

Propagation.NESTED:嵌套

如果当前存在事务,就在当前事务中嵌套其他事务,如果没有,就新建一个事务;

嵌套事务的概念:是在当前事务内部创建了一个新的保存点,而不是重新建立了一个全新的事务,在嵌套事务中,内部事务可以独立地提交或回滚,而外部事务的状态不受内部事务的影响。如果内部事务回滚,只会回滚到内部事务的保存点,而不会影响外部事务的提交或回滚。如果外部事务回滚,内部事务通常会回滚到内部事务开始的保存点。

 

Propagation.REQUIRES_NEW:需要新的

如果当前存在事务,挂起当前的事务,不管有没有事务都创建一个新的事务。新的事务执行完毕会继续执行之前挂起的事务

 

Propagation.NOT_SUPPORTED:不支持

如果当前存在事务,挂起当前的事务,以非事务的方式运行,

 

Propagation.NEVER:禁止

如果当前存在事务,则抛出异常,不存在事务就以非事务的方式运行

 

使用:

可以使用@Transactional注解时指定传播行为

@Transactional(propagation = Propagation.REQUIRED)

也可以使用xml配置指定

<!-- 事务的传播特性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
     <tx:method name="add*" propagation="REQUIRED"/>
     <tx:method name="del*" propagation="REQUIRED"/>
     <tx:method name="modify*" propagation="REQUIRED"/>
     <tx:method name="*" propagation="REQUIRED" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!-- 哪些类哪些方法使用事务 -->
<aop:config>
    <aop:pointcut expression="execution(* com.service.*.*(..))" id="transactionPC"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPC"/>
</aop:config>

一般配置:

增删改使用REQUIRED,如果有事务就加入事务,没有就新建事务

查询的方法使用SUPPORTS ,有事务就加入事务,没有事务就已非事务方式

 

嵌套事务说明:

嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。

 

如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

 

如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

 

嵌套事务如何提交?

子事务是父事务的一部分,由父事务统一提交。

 

事务隔离级别

spring也可以通过注解或者xml配置文件来配置事务的隔离级别

<tx:advice id="tx" transaction-manager="transactionManager">
	<tx:attributes>
	<!--
	以save开头的方法,采用的传播属性是默认值,隔离机制是默认值,是读写事务
	 -->
		<tx:method
		name="save*"
		propagation="REQUIRED"
		isolation="DEFAULT"
		read-only="false"/>
	</tx:attributes>
</tx:advice>

 

可以配置的隔离级别有:

DEFAULT:使用后端数据库默认的隔离级别。mysql默认可重复读

READ_UNCOMMITED:读未提交:一个事务可以读到另一个事务未提交数据,会有脏读、不可重复读和幻读

READ_COMMITED:读已提交:一个事务提交前,另一个事务读不到数据,会有不可重复读和幻读

REPEATABLE_READ:可重复读:一个事务中多次读取同一行数据都会一样,会有幻读,(每个事务在开始时会创建一个快照来保留一致的数据视图)

SERIALIZABLE:串行化:事务顺序执行,可避免脏读、不可重复读、幻读,但效率最差。因为A事务执行时,要完全锁住在事务中涉及的数据表。

 

脏读:指的是一个事务读取了另一个事务未提交的数据,如果修改事务最终回滚,那么读取事务读取到的数据就是无效的,这会导致数据的不一致性。

不可重复读:指的是在同一个事务中,两次读取同一行数据,但在两次读取之间,另一个事务修改了该行数据,导致两次读取的结果不一致,不可重复读的问题会导致数据的不一致性,因为事务在读取数据时,可能会基于第一次读取的数据做出决策,但在第二次读取时,数据已经发生了变化,导致事务的逻辑出现问题。

幻读:在同一个事务中,两次查询同一个范围的数据,但在两次查询之间,另一个事务插入了新的数据,导致第二次查询结果包含了第一次查询中不存在的数据。换句话说,幻读表示在同一个事务内,由于其他事务的插入或删除操作,导致同一查询的结果集发生变化。

为什么可重复读还会幻读:可重复读隔离级别下,数据库会保证同一个事务内多次读取同一行数据时的一致性,即在同一个事务中,多次读取同一行数据的结果应该保持一致。然而,幻读问题主要涉及到范围查询,即在同一个事务中多次查询同一个范围的数据时可能出现数据不一致的情况。幻读问题的出现与可重复读隔离级别下的行级锁和快照读有关。在可重复读隔离级别下,数据库使用行级锁来保证同一事务内多次读取同一行数据的一致性,但对于范围查询,数据库可能使用快照读(Snapshot Read)而不是行级锁,导致在同一事务内多次查询同一个范围的数据时,其他事务的插入或删除操作会导致结果集发生变化,从而出现幻读问题。

 

spring事务什么时候回滚

只有事务中的代码抛出运行时异常(RuntimeException)或发生Error时,事务才进行回滚

我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked异常不回滚: @Transactional(notRollbackFor=RunTimeException.class)

 

总结:如果异常没有抛出,处理了异常,或者抛出的异常时不受回滚检查也不能回滚,默认只检测运行时异常

 

事务注解说明

属性

类型

描述

value

String

可选的限定描述符,指定使用的事务管理器

propagation

enum: Propagation

可选的事务传播行为设置,默认是必须事务,加入或新建

isolation

enum: Isolation

可选的事务隔离级别设置,mysql默认可重复读

readOnly

boolean

读写或只读事务,默认false表示读写

timeout

int (in seconds granularity)

事务超时时间设置,默认是30秒

rollbackFor

Class对象数组,必须继承自Throwable

导致事务回滚的异常类数组,默认RuntimeException.class

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

类名数组,必须继承自Throwable

导致事务回滚的异常类名字数组,默认RuntimeException,指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})

noRollbackFor

Class对象数组,必须继承自Throwable

不会导致事务回滚的异常类数组

noRollbackForClassName

类名数组,必须继承自Throwable

不会导致事务回滚的异常类名字数组

属性:

 @Transactional 注解在非public的方法上标注时不起作用,所以要标注在public方法上

 

 

放在类上还是方法上:

@Transactional可以定义在类上,方法上,也可以标注在定义的接口和接口方法上。

如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以一般不在接口中标注。

只要加在类上,所有的方法都有读写事务,这样效率比较低,所以一般加在实现类的方法上

在方法上的@Transactional注解会覆盖掉类上的@Transactional。

 

放在dao层还是service层:

在dao层的方法对数据库的操作通常来说是简单的,没业务逻辑性的,所以对于这种加了一个事务也可以,但没必要;

但在service层,里面的方法通常是包含一些复杂逻辑的,一个方法可能就调用了dao的多个方法,所以就必须做到事务管理,要么service方法里面的dao方法全部执行,要么全部撤销。这样才能保证数据库数据的正确性。

所以放在service层更加科学。

 

一般怎么配置

增删改的事务传播行为propagation设置为REQUIRED,表示存在事务就加入,不存在事务就新建事务,readOnly为默认值false支持读写事务

读的事务传播行为SUPPORTS,有事务就加入,没有事务就以非事务方式运行,readOnly设置为true代表只读事务

 

事务超时说明

@Transactional(timeout = 60)

如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数据库的增删改查操作时会报事务超时错误(会回滚)。(因为每一次执行sql,获取到一个statement时会判断是否超时,超时抛异常之后被检测到然后进行回滚)

如果已经过去60秒了还没跑完但是后面已经没有涉及到对数据库的增删改查操作,那么这时不会报事务超时错误(不会回滚)。

单位是秒,默认是-1,永不超时

 

springBoot的事务管理

springBoot已经在TransactionAutoConfiguration自动配置类中对事物配置好了,我们只要在使用事务的地方加上@Transactional注解就可以了

spring常用注解说明

@Lazy

用于延迟实例的创建或属性、方法的初始化。

可标注在类、方法、构造方法、参数、字段上

放在类上:默认spring容器启动时会实例化所有非懒加载的类,放在类上可以让这个类的实例在使用的时候再进行实例化

放在字段上:被第一次访问时再被初始化,而不是在类加载时就初始化。应用场景:如果某些字段的初始化需要进行复杂的计算或者需要进行网络请求等操作,那么在类加载的时候就会显著影响应用程序的性能。此时可以使用@Lazy注解来延迟字段的初始化。@Lazy注解只适用于非基本类型的字段,因为基本类型的字段会被自动初始化为默认值。此外,@Lazy注解不能用于final字段,因为final字段必须在构造函数中初始化。

 

一般很少放在构造方法、方法、参数上

详细说明下Spring Profiles

Spring Profiles是Spring框架的一个核心功能,它可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当应用程序运行在特定的环境中时,才会加载对应的Bean。

在配置类型加上@Profile("dev") 注解,指定环境标识,通过配置文件中或命令行或虚拟机参数指定激活的环境

1、在配置文件中或配置类中指定  spring.profiles.active=dev

2、命令行:命令行指定配置的参数内容

​ java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;

​ 可以直接在测试的时候,配置传入命令行参数,命令行的方式优先级高于配置文件中指定的

​ 3、虚拟机参数:

​ -Dspring.profiles.active=dev

这里的dev指的就是profile的内容,虚拟机方式优先级也高于配置文件

另外还可以在pom文件中配置profile激活环境

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
    </profile>
</profiles>

它的值将用于替换 application.properties 文件中@spring.profiles.active@ 的占位符 :

spring.profiles.active=@spring.profiles.active@

测试中激活环境:@ActiveProfile 注解启用特定profile,测试可以很容易地指定哪些profile处于活动状态:@ActiveProfiles("dev")

默认的 Profile:任何未指定profile的 bean 都属于 default profile。设置默认profile的方法—通过使用 spring.profiles.default 属性。

posted @ 2023-09-26 22:56  星光闪闪  阅读(32)  评论(0)    收藏  举报