当前位置: 首页 > news >正文

高阶面试-spring的部分

spring的诞生

为什么需要spring?spring之前人们是怎么开发的,用的主流框架是什么,spring解决了什么痛点?

Enterprise JavaBeans (EJB),企业级开发框架,里面就提出bean的概念了,为啥不用呢?配置复杂、重量级、灵活性差。

给了spring机会。

EJB只适合大型企业,时代变了,互联网时代,讲究敏捷、快速,spring应运而生。

spring的优势

更简单、易用的框架,采用IOC和AOP等,简化开发流程。开发人员只需要专注业务逻辑,类之间的依赖由容器解耦,

BeanFactory和ApplicationContext的区别

都是管理Bean的容器接口,ApplicationContext是BeanFactory的子接口(ApplicationContext – ListableBeanFactory – BeanFactory),功能更多,区别:

  • applicationContext立即初始化,容器启动创建所有单例bean,beanFactory延迟初始化
  • ApplicationContext实现MessageResource,支持国际化
  • ApplicationContext实现ApplicationEventPublisher,提供事件机制
  • ApplicationContext内置AOP的支持,BeanPostProcessor处理Bean后期初始化工作
  • ApplicationContext实现自动装配,@ComponentScan注解或<context:component-scan> 元素,spring扫描指定的包及其子包的类,将带有特定注解如 @Component@Service@Repository@Controller)的类注册为容器的bean

依赖注入(DI)和控制反转(IoC)有什么区别?

控制反转(IoC)

控制反转 是一种设计原则,它描述了程序结构的控制权从应用程序代码转移到框架或容器的过程。传统的编程模式中,应用程序代码控制着依赖对象的创建和管理。在 IoC 模式中,这种控制权被反转,交给了 IoC 容器或框架。

  • 目的:降低代码的耦合性,提高模块的可重用性和可测试性。
  • 实现方式:通过将对象的创建和依赖管理交给 IoC 容器或框架。

依赖注入(DI)

依赖注入 是实现控制反转的一种具体方式。它通过将对象的依赖关系注入到对象中,而不是由对象自己去创建或查找依赖。DI 可以有多种形式,包括构造函数注入、Setter 方法注入和接口注入。

  • 目的:将依赖关系的管理职责从对象自身转移出去,使得对象更加专注于自身的功能,提高代码的可测试性和可维护性。
  • 实现方式:通过构造函数、Setter 方法或接口将依赖注入到对象中。

Spring支持哪些类型的依赖注入?

主要的依赖注入方式包括构造函数注入、Setter 方法注入和字段注入

构造函数注入

构造函数注入是通过类的构造函数将依赖项传递给类的实例。这种方式在类的实例化时就完成了依赖注入,因此可以确保依赖项在对象创建时就被正确地初始化。

@Component
public class ServiceA {private final ServiceB serviceB;@Autowiredpublic ServiceA(ServiceB serviceB) {this.serviceB = serviceB;}public void performTask() {serviceB.doSomething();}
}@Component
public class ServiceB {public void doSomething() {System.out.println("ServiceB is doing something");}
}

Setter 方法注入

Setter 方法注入是通过类的 Setter 方法将依赖项传递给类的实例。这种方式允许在对象创建之后设置或更改依赖项。

@Component
public class ServiceA {private ServiceB serviceB;@Autowiredpublic void setServiceB(ServiceB serviceB) {this.serviceB = serviceB;}public void performTask() {serviceB.doSomething();}
}@Component
public class ServiceB {public void doSomething() {System.out.println("ServiceB is doing something");}
}

字段注入

字段注入是通过直接在类的字段上使用 @Autowired 注解进行依赖注入。这种方式简洁,但在测试和重构时可能不如构造函数注入和 Setter 方法注入灵活。

@Component
public class ServiceA {@Autowiredprivate ServiceB serviceB;public void performTask() {serviceB.doSomething();}
}@Component
public class ServiceB {public void doSomething() {System.out.println("ServiceB is doing something");}
}

5. 注解配置

Spring 提供了 @Autowired 注解来标识需要注入的依赖项,@Qualifier 注解来解决多个相同类型 Bean 的注入冲突,以及 @Resource 注解用于 JSR-250 标准的依赖注入。

使用 @Qualifier

当有多个相同类型的 Bean 时,可以使用 @Qualifier 指定注入的 Bean

IOC容器

IoC 容器是一个管理 Bean 的工厂,它根据配置元数据(如 XML 文件、注解或 Java 配置类)创建、配置和管理 Bean。Spring 提供了两种主要的 IoC 容器实现:

  1. BeanFactory:基本的 IoC 容器,提供基础的 DI 功能。
  2. ApplicationContext:比 BeanFactory 更高级的容器,提供了更多的企业级特性,如事件发布、国际化、AOP 集成等。

beanDefinition

BeanDefinition(Bean 定义)是描述 Spring 容器中的 Bean 实例的元数据对象。它定义了 Bean 的各种属性,如类名、作用域、构造方法参数、属性值、初始化方法、销毁方法等信息。通过 BeanDefinition,Spring 容器可以了解如何创建、配置和管理 Bean 实例。

bean生命周期的整个过程

![[Pasted image 20240623185824.png]]

  • 实例化(Instantiation): 在 Spring 容器中,Bean 的生命周期始于实例化。这意味着 Spring 根据 Bean 的定义(如配置文件、注解等)创建 Bean 的实例。

  • 依赖注入(Dependency Injection): 一旦 Bean 实例化完成,Spring 将会注入所需的依赖。这包括构造函数注入、Setter 方法注入以及字段注入(通过反射或者代理等方式)。

  • 初始化(Initialization): 初始化阶段是 Bean 生命周期中的一个重要部分。在初始化阶段,Spring 容器会调用特定的初始化回调方法(如果有的话),例如通过 init-method 属性指定的初始化方法,或者使用 @PostConstruct 注解标注的方法。

  • 使用(In Use): 一旦 Bean 初始化完成并且依赖注入完成,Bean 就可以被 Spring 容器管理和使用了。在这个阶段,Bean 处于活跃状态,可以被其他 Bean 或者应用程序代码引用和调用。

  • 销毁(Destruction): 当不再需要 Bean 时,Spring 容器会释放它。这个阶段涉及到 Bean 的销毁工作,例如调用销毁回调方法(通过 destroy-method 属性指定的方法或者使用 @PreDestroy 注解标注的方法)

BeanDefinition->实例化

  • BeanFactoryPostProcessor#postProcessBeanFactory bean创建前修改bean的定义信息
  • BeanPostProcessor#postProcessBeforeInstantiation 实例化前,返回对象不为null,替换原本的bean

实例化->依赖注入DI

  • BeanPostProcessor#postProcessAfterInstantiation 实例化后,在 Bean 的实例化之后对其进行进一步处理或检查,false跳过DI
  • BeanPostProcessor#postProcessProperties DI前如@Autowired @Value @Resource

依赖注入DI->初始化

  • 各种Aware接口 BeanNameAware#setBeanName() BeanClassLoaderAware#setBeanClassLoader() BeanFactoryAware#setBeanFactory()
  • BeanPostProcessor#beforeInitialization后置处理器的前置过程 初始化前,如@PostConstruct @ConfigurationProperties

初始化->完整对象

  • InitializingBean#afterPropertiesSet() `的init-method属性的初始化方法
  • BeanPostProcessor#afterInitialization后置处理器的后置过程 初始化后,返回对象会替换原本的bean,如AOP

![[Pasted image 20240623215800.png]]

三级缓存解决循环依赖

https://www.51cto.com/article/747437.html

循环依赖:A的属性是B,B的属性是A

首先是加一层缓存,key为beanName,value是对象实例,由于实例化和初始化分开的,A实例化后会放入缓存,A polulateBean() 依赖B,然后B实例化、填充属性的时候就从缓存中获取到A,解决了循环依赖。

但是有个问题,bean有可能创建完成、有可能正在创建还没注入依赖,参杂到一起,因此需要搞个二级缓存。

  • 新增二级缓存,存放刚实例化的bean
  • 当bean完成初始化,放入一级缓存,删掉二级缓存,只保留完整的bean即可

但是java有代理,怎么区分代理对象和普通对象?如果存在代理对象的循环依赖怎么处理?
按二级缓存的,最终在一级缓存中的对象A是一个proxy_A,但是对象B依赖的对象A却是一个普通A

需要三级缓存。
![[Pasted image 20240623233646.png]]

与二级缓存的不同之处:

  • 在获取到A的时候创建proxy_A。同时将其加入二级缓存,并返回给B,B就依赖了proxy_A
  • A初始化过程会创建代理对象,查二级缓存有没得proxy_A,有的话选择二级缓存的proxy_A存入一级缓存并返回

spring的更屌,第三级缓存不仅仅是存入了实例化的对象,而是存入了一个匿名类ObjectFactory,getEarlyBeanReference函数的实现中会调用BeanPostProcessor执行用户自定义的逻辑。


boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// addSingletonFactory方法是将bean加入三级缓存中// 三级缓存会被ObjectFactory包装// getEarlyBeanReference方法会执行Bean的后置处理器addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);}}return exposedObject;}

AOP

Spring AOP(Aspect-Oriented Programming,面向切面编程)是一种用于实现横切关注点(cross-cutting concerns)的技术,如日志记录、事务管理、安全检查等。Spring AOP 允许在不改变业务逻辑代码的情况下,将这些横切关注点分离出来,提升代码的可维护性和复用性。

Spring AOP 的原理

Spring AOP 的实现基于代理模式,主要使用了两种代理机制:

  1. JDK 动态代理:用于代理实现了接口的类。
  2. CGLIB 代理:用于代理没有实现接口的类,通过生成子类来实现代理。

核心概念

  • 切面(Aspect):切面是横切关注点的模块化。
  • 连接点(JoinPoint):程序执行过程中的某个点,如方法调用。
  • 通知(Advice):切面在连接点执行的动作(如前置通知、后置通知、异常通知等)。
  • 切入点(Pointcut):定义了横切关注点应该应用到哪些连接点上。
  • 引入(Introduction):在不修改现有类的前提下,向类中添加新的方法或属性。
  • 织入(Weaving):将切面应用到目标对象,创建代理对象的过程。

实现机制

Spring AOP 主要通过 AopProxy 接口及其实现类来创建代理对象。

JDK 动态代理

当目标类实现了接口时,Spring 使用 JDK 动态代理来创建代理对象。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkDynamicProxy implements InvocationHandler {private final Object target;public JdkDynamicProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method execution");Object result = method.invoke(target, args);System.out.println("After method execution");return result;}public static void main(String[] args) {TargetInterface target = new TargetClass();TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new JdkDynamicProxy(target));proxy.targetMethod();}
}interface TargetInterface {void targetMethod();
}class TargetClass implements TargetInterface {@Overridepublic void targetMethod() {System.out.println("Target method execution");}
}
CGLIB 代理

当目标类没有实现接口时,Spring 使用 CGLIB 来生成目标类的子类作为代理对象。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxy implements MethodInterceptor {private final Object target;public CglibProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method execution");Object result = proxy.invokeSuper(obj, args);System.out.println("After method execution");return result;}public static void main(String[] args) {TargetClass target = new TargetClass();TargetClass proxy = (TargetClass) Enhancer.create(target.getClass(),new CglibProxy(target));proxy.targetMethod();}
}class TargetClass {public void targetMethod() {System.out.println("Target method execution");}
}

Spring AOP 配置

Spring AOP 可以通过 XML 配置和注解配置两种方式来定义切面和通知。

XML 配置
<!-- Spring AOP 配置 -->
<aop:config><aop:aspect id="myAspect" ref="myAspectBean"><aop:pointcut id="myPointcut" expression="execution(* com.example.service.*.*(..))"/><aop:before pointcut-ref="myPointcut" method="beforeAdvice"/><aop:after pointcut-ref="myPointcut" method="afterAdvice"/></aop:aspect>
</aop:config><bean id="myAspectBean" class="com.example.aspect.MyAspect"/>
注解配置
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;@Aspect
public class MyAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeAdvice() {System.out.println("Before method execution");}@After("execution(* com.example.service.*.*(..))")public void afterAdvice() {System.out.println("After method execution");}
}@Configuration
@EnableAspectJAutoProxy
public class AppConfig {@Beanpublic MyAspect myAspect() {return new MyAspect();}@Beanpublic MyService myService() {return new MyService();}
}

AOP 代理的创建时机

  1. BeanDefinition 解析阶段:Spring 在解析 Bean 定义时,根据切面配置(如 @Aspect 或 XML 配置)生成相关的 BeanDefinition
  2. Bean 实例化阶段:在 Bean 实例化时,Spring 判断是否需要为该 Bean 创建代理对象。如果需要,Spring 使用 AopProxy 创建代理对象并替换原始 Bean。
  3. 应用通知(Advice):代理对象调用目标方法前后,执行相应的通知(如前置通知、后置通知)。

spring的事务管理

1. 声明式事务管理

声明式事务管理是通过配置或注解来管理事务,而无需在代码中显式地进行事务管理操作。Spring 提供了 @Transactional 注解来实现这一点。

@Service
public class MyService {@Autowiredprivate MyRepository myRepository;@Transactionalpublic void performTransactionalOperation() {myRepository.save(new Entity());// other database operations}
}

在上述代码中,performTransactionalOperation 方法被标记为事务性操作。Spring 将确保这个方法在事务上下文中执行,任何异常都会导致事务回滚。

2. 事务传播行为

Spring 支持多种事务传播行为,这些行为定义了事务方法如何与现有的事务关联。主要的传播行为包括:

  • REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • REQUIRES_NEW:总是创建一个新的事务。如果当前存在事务,则将当前事务挂起。
  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • NESTED:如果当前存在事务,则在嵌套事务中执行。如果当前没有事务,则创建一个新的事务。
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  • NOT_SUPPORTED:总是以非事务方式执行。如果当前存在事务,则将当前事务挂起。
  • NEVER:总是以非事务方式执行。如果当前存在事务,则抛出异常。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {// transactional code
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {// transactional code
}

3. 编程式事务管理

编程式事务管理允许开发者在代码中显式地控制事务。Spring 提供了 TransactionTemplatePlatformTransactionManager 来实现编程式事务管理。

@Service
public class MyService {@Autowiredprivate TransactionTemplate transactionTemplate;public void performTransactionalOperation() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {// transactional code}});}
}

4. 事务回滚规则

默认情况下,Spring 只在遇到未检查(运行时)异常时回滚事务。可以通过 @Transactional 注解的 rollbackFornoRollbackFor 属性来指定其他回滚规则。

@Transactional(rollbackFor = Exception.class)
public void performTransactionalOperation() throws Exception {// transactional code
}

5. 事务管理器

Spring 支持多种事务管理器,以满足不同的数据访问技术需求。常用的事务管理器包括:

  • DataSourceTransactionManager:用于管理 JDBC 事务。
  • JpaTransactionManager:用于管理 JPA 事务。
  • HibernateTransactionManager:用于管理 Hibernate 事务。
  • JtaTransactionManager:用于分布式事务管理。
@Configuration
@EnableTransactionManagement
public class AppConfig {@Beanpublic DataSource dataSource() {// configure and return DataSource}@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

6. 事务拦截器

Spring 使用 AOP(Aspect-Oriented Programming)来实现声明式事务管理。@Transactional 注解的处理实际上是通过事务拦截器实现的。事务拦截器会在方法调用前后应用事务逻辑,确保事务的正确开始和结束。

Spring MVC中的DispatcherServlet的作用是什么

![[Pasted image 20240624073531.png]]

DispatcherServlet 是核心组件,负责将 HTTP 请求分发给合适的处理器(Controller)。它是整个 Spring MVC 框架的前端控制器(Front Controller),起着中央调度器的作用。

DispatcherServlet 的工作流程

DispatcherServlet 的工作流程可以分为以下几个步骤:

  1. 接收请求

    • DispatcherServlet 接收客户端发送的 HTTP 请求。
  2. 处理器映射

    • DispatcherServlet 根据请求的 URL,利用 HandlerMapping 来找到对应的处理器(Controller)。
    • HandlerMapping 通过配置的映射规则来匹配请求和处理器。
  3. 调用处理器

    • 找到合适的处理器(Controller)后,DispatcherServlet 调用处理器的方法来处理请求。
    • 处理器返回一个 ModelAndView 对象,其中包含了模型数据和视图名称。
  4. 视图解析

    • DispatcherServlet 利用 ViewResolver 解析 ModelAndView 中的视图名称,找到对应的视图对象。
    • 常用的视图解析器包括 InternalResourceViewResolver(解析 JSP)、ThymeleafViewResolver(解析 Thymeleaf 模板)等。
  5. 视图渲染

    • 将模型数据传递给视图对象,并将视图渲染成 HTML 响应。
    • DispatcherServlet 将生成的 HTML 响应返回给客户端。

DispatcherServlet 的配置

在 Spring MVC 中,DispatcherServlet 通常在 web.xml 文件中配置,但在 Spring Boot 中,可以通过注解和配置类来自动配置。

<web-app><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>

Spring Boot 自动配置 DispatcherServlet,因此通常不需要手动配置。只需在应用程序主类上使用 @SpringBootApplication 注解,并确保 spring-boot-starter-web 依赖已包含在项目中

Spring框架中的@Value注解是如何注入配置值的?

在 Spring 框架中,@Value 注解用于将外部配置文件中的值或者其他 Spring 管理的 Bean 的属性值注入到当前的 Bean 的字段、方法参数或构造函数参数中。通过 @Value 注解,可以方便地将配置值注入到 Spring 管理的组件中,例如 @Component@Service@Controller 等。

如果配置文件中的值是一个逗号分隔的字符串(如 myapp.names=John,Doe),Spring 将会自动将其转换为数组或集合类型,并注入到相应的字段中

@Component
public class MyComponent {@Value("${myapp.names}")private String[] names;@Value("${myapp.emails}")private List<String> emails;// Getter and Setter
}
myapp.username=admin
myapp.password=secret
myapp.names=John,Doe
myapp.emails=example1@example.com,example2@example.com

Spring框架中的Spring Security是如何提供安全性的?

Spring Security 是 Spring 框架中专门用于提供身份认证(Authentication)和授权(Authorization)功能的安全性框架。它能够有效地保护应用程序的安全性,防止恶意攻击和非法访问。以下是 Spring Security 提供安全性的主要特点和功能:

主要功能和特点:

  1. 身份认证(Authentication)

    • Spring Security 提供了多种身份认证方式,包括基于表单登录、HTTP Basic 认证、HTTP Digest 认证、LDAP 认证等。
    • 可以通过配置简单的用户名密码认证,也可以扩展到多种复杂的认证机制,例如 OAuth2、OpenID Connect 等。
  2. 授权(Authorization)

    • 通过角色(Roles)或权限(Permissions)控制对应用程序资源的访问权限。
    • 可以基于注解、XML 配置或者编程方式实现细粒度的权限控制,如 @Secured@PreAuthorize@PostAuthorize 等。
  3. 安全配置

    • 可以通过 Java 配置或者 XML 配置对安全规则进行灵活配置,定义哪些 URL 需要保护、哪些请求需要认证、如何处理登录和注销等行为。
  4. Session 管理

    • Spring Security 提供了对 Session 的管理,包括限制并发登录数、Session 超时处理、Session 注销和过期处理等功能,防止会话劫持和滥用。
  5. CSRF 防护

    • 提供了 CSRF(Cross-Site Request Forgery)攻击防护机制,通过生成和验证 CSRF Token 防止跨站请求伪造攻击。
  6. 密码加密

    • 提供了密码编码器(PasswordEncoder)接口和多种实现,支持密码的加密和验证,确保用户密码的安全存储和传输。
  7. 集成 Spring 框架

    • 与 Spring 框架完全集成,可以方便地与 Spring MVC、Spring Boot 等其他 Spring 组件和框架无缝协作。

工作原理:

Spring Security 的核心是一系列的过滤器链(Filter Chain),每个过滤器负责不同的安全任务,例如身份验证、授权、会话管理等。在应用程序启动时,Spring Security 会自动装配这些过滤器,并根据配置对请求进行安全性检查和处理。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER").and().withUser("admin").password("{noop}password").roles("ADMIN");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasRole("USER").anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().logoutUrl("/logout").permitAll();}
}

springboot是什么

spring下的子项目,14年发布,基于spring4.0设计,通过简化配置进一步简化spring应用的开发过程,解决的痛点:

  • 复杂的配置管理 传统spring程序需要编写大量的 XML 配置文件来管理各种 Bean、数据库连接、事务管理等,配置繁琐且容易出错 springboot通过自动配置(Auto Configuration)和约定优于配置(Convention over Configuration)的理念,减少了手动配置的工作量
  • 依赖管理 传统项目需要手动管理大量的依赖项,有各种依赖冲突和版本问题,springboot提供一系列starter,简化依赖管理
  • 部署,传统项目需要打war包,部署到tomcat,过程复杂;springboot内置tomcat等servlet容器,项目打成jar包,直接java -jar
  • 其他,如环境变量profile、监控actuator等

spring boot自动装配

Spring Boot自动配置主要是@EnableAutoConfiguration实现的

核心:两个注解

@Import注解: spring3.0

用于导入一个或多个配置类或组件类,以便将它们注册到当前的 Spring 应用程序上下文中,在需要将外部配置类或第三方库中的配置类集成到现有应用程序中

使用场景

  1. 模块化配置:将应用程序的配置分成多个模块,并通过 @Import 将它们组合起来。
  2. 第三方库集成:将第三方库的配置类导入到应用程序上下文中。
  3. 条件配置:根据特定条件有选择地导入配置类。

动态导入,可以实现 ImportSelector 接口,来根据动态条件选择导入的配置类

public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[] {"com.example.FirstConfig","com.example.SecondConfig"};}
}@Import(MyImportSelector.class)
public class MainConfig {
}

也可以结合spring4.0的@Conditional注解基于条件导入

@Configuration
@Conditional(MyCondition.class)
public class ConditionalConfig {@Beanpublic String conditionalBean() {return "This bean is conditionally loaded";}
}public class MyCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 自定义的条件逻辑,返回 true 表示条件满足,返回 false 表示条件不满足// 例如,可以根据环境变量、系统属性等来判断String myProperty = context.getEnvironment().getProperty("my.property");return "enabled".equals(myProperty);}
}@Import(ConditionalConfig.class)
public class MainConfig {
}# application.properties
my.property=enabled

my.property 的值为 “enabled” 时,Spring 容器会加载 conditionalBean

springboot的条件注解增强

@ConditionalOnClass
当类路径中存在指定的类时,自动配置生效
@ConditionalOnClass(name = "org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType")

@ConditionalOnMissingClass
当类路径中不存在指定的类时,自动配置生效
@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")

@ConditionalOnBean
当容器中存在指定的 Bean 时,自动配置生效。
@ConditionalOnBean(DataSource.class)

@ConditionalOnMissingBean
当容器中不存在指定的 Bean 时,自动配置生效
@ConditionalOnMissingBean(DataSource.class)

@ConditionalOnProperty
当配置文件中指定的属性存在并且值符合条件时,自动配置生效
@ConditionalOnProperty(name = "myapp.feature.enabled", havingValue = "true")

@ConditionalOnResource
当指定的资源存在时,自动配置生效。
@ConditionalOnResource(resources = "classpath:data.sql")

自动装配

主要由@EnableAutoConfiguration实现,添加了@EnableAutoConfiguration注解,会导入AutoConfigurationImportSelector类,里面的selectImports方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有含有META-INF/spring.factoriesjar包,将对应key@EnableAutoConfiguration注解全名对应的value类全部装配到IOC容器中

自动配置的工作流程

  1. SpringApplication 初始化:当应用程序启动时,SpringApplication 类被初始化。

  2. 加载 spring.factories 文件:Spring Boot 会扫描所有位于类路径下的 META-INF/spring.factories 文件,加载所有配置的自动配置类。

  3. 条件匹配:对于每个自动配置类,Spring Boot 会根据类上标注的条件注解(如 @ConditionalOnClass@ConditionalOnMissingBean 等)进行条件匹配。如果条件满足,则创建对应的 Bean 并注入应用上下文中。

  4. 应用自动配置:符合条件的自动配置类将被应用,自动配置相应的 Bean 和属性。

以redis-starter为例,在你的 pom.xml 文件中添加 spring-boot-starter-data-redis 依赖。Spring Boot 会自动扫描项目中所有的 META-INF/spring.factories 文件,并根据这些文件中的配置加载相应的自动配置类。

对于 Redis,这个文件的内容可能包含如下行

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

3. Redis 自动配置类

RedisAutoConfiguration 类是自动配置 Redis 所需的核心类。这个类位于 org.springframework.boot.autoconfigure.data.redis 包中,并包含了许多与 Redis 相关的 Bean 定义和配置。

@Configuration
@ConditionalOnClass(RedisTemplate.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate")public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);}
}

4. 条件注解的作用

自动配置类使用了多个条件注解来确保在合适的环境中进行配置。

  • @ConditionalOnClass(RedisTemplate.class):确保只有在类路径中存在 RedisTemplate 类时才进行配置。
  • @ConditionalOnMissingBean(name = "redisTemplate"):确保只有在没有定义名为 redisTemplate 的 Bean 时才创建 RedisTemplate Bean。

5. 配置属性的绑定

自动配置类通过 @EnableConfigurationProperties(RedisProperties.class) 注解来绑定 Redis 配置属性,这些属性通常在 application.propertiesapplication.yml 文件中定义。

spring.redis.host=localhost
spring.redis.port=6379
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {private String host;private int port;// getters and setters
}

6. 连接工厂的配置

RedisAutoConfiguration 类会导入 LettuceConnectionConfigurationJedisConnectionConfiguration,这些类负责配置具体的 Redis 连接工厂,如 Lettuce 或 Jedis

@Configuration
@ConditionalOnClass(RedisClient.class)
@ConditionalOnMissingBean(RedisConnectionFactory.class)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {@Bean@ConditionalOnMissingBean(RedisConnectionFactory.class)public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources, ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider) {// 配置 Lettuce 连接工厂}
}

7. Bean 的创建和初始化

在自动配置类中定义的 Bean,如 RedisTemplateStringRedisTemplate,会在应用启动时根据条件自动创建和初始化。这些 Bean 通过 Redis 连接工厂与 Redis 服务器进行交互。

8. 使用 Redis

一旦自动配置完成,你就可以在你的应用中直接使用 Redis 的功能,而不需要手动配置

当你在 Spring Boot 项目中添加 spring-boot-starter-data-redis 依赖时,Spring Boot 会通过以下步骤实现自动装配:

  1. 加载 spring.factories 文件中定义的自动配置类 RedisAutoConfiguration
  2. RedisAutoConfiguration 类会根据条件注解和属性配置创建必要的 Redis 相关 Bean。
  3. 应用启动时,这些 Bean 会被自动创建和初始化,使得你可以直接使用 Redis 的功能,而无需手动配置。

相关文章:

  • Android Style 使用指南
  • 汽车电子行业知识:什么是车载智能座舱
  • Spring Boot配置文件properties/yml/yaml
  • Pytest+Allure+Yaml+PyMsql+Jenkins+Gitlab接口自动化(四)Jenkins配置
  • 【Arduino】XIAOFEIYU实验ESP32实验热敏电阻(图文)
  • 23- Redis 主从复制是怎么实现的?
  • 【ai】ubuntu18.04 找不到 nvcc --version问题
  • pdf文档翻译有哪些好的推荐
  • Spring @Cacheable缓存注解用法说明
  • 【Web3】Web3.js 启动!并解决Web3 is not a constructor报错
  • 第十一节:学习通过动态调用application.properties参数配置实体类(自学Spring boot 3.x的第二天)
  • PostgreSQL主从同步
  • 安装node软件 文件夹里没有node_modules 包 是怎么回事
  • 突破SaaS产品运营困境:多渠道运营如何集中管理?
  • MasterLogic-200 PLC可以与Honeywell 的 DCS系统 Experion PKS、SCADA系统Experion HS 系统
  • 网络传输文件的问题
  • 77. Combinations
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • httpie使用详解
  • Idea+maven+scala构建包并在spark on yarn 运行
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Linux Process Manage
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • npx命令介绍
  • vue-loader 源码解析系列之 selector
  • 飞驰在Mesos的涡轮引擎上
  • 记一次删除Git记录中的大文件的过程
  • 技术发展面试
  • 聚簇索引和非聚簇索引
  • 前端js -- this指向总结。
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 网页视频流m3u8/ts视频下载
  • 微服务入门【系列视频课程】
  • 写给高年级小学生看的《Bash 指南》
  • FaaS 的简单实践
  • postgresql行列转换函数
  • 数据可视化之下发图实践
  • ​水经微图Web1.5.0版即将上线
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #HarmonyOS:Web组件的使用
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #微信小程序:微信小程序常见的配置传值
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (PADS学习)第二章:原理图绘制 第一部分
  • (算法)Game
  • (循环依赖问题)学习spring的第九天
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转载)PyTorch代码规范最佳实践和样式指南
  • ***监测系统的构建(chkrootkit )
  • 、写入Shellcode到注册表上线