高阶面试-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;

    @Autowired
    public 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;

    @Autowired
    public 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 {
    @Autowired
    private 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>() {
        @Override
        public 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;
    }

    @Override
    public 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 {
    @Override
    public 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;
    }

    @Override
    public 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 {

    @Bean
    public MyAspect myAspect() {
        return new MyAspect();
    }

    @Bean
    public 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 {

    @Autowired
    private MyRepository myRepository;

    @Transactional
    public 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 {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void performTransactionalOperation() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected 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 {

    @Bean
    public DataSource dataSource() {
        // configure and return DataSource
    }

    @Bean
    public 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 {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("{noop}password").roles("USER")
                .and()
                .withUser("admin").password("{noop}password").roles("ADMIN");
    }

    @Override
    protected 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 {
    @Override
    public 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 {
    @Bean
    public String conditionalBean() {
        return "This bean is conditionally loaded";
    }
}

public class MyCondition implements Condition {
    @Override
    public 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
    @ConditionalOnMissingBean
    public 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 的功能,而无需手动配置。

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

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

相关文章

VMware--创建Ubuntu虚拟机

原文网址&#xff1a;VMware--创建Ubuntu虚拟机-CSDN博客 简介 本文介绍VMware创建Ubuntu虚拟机的方法。 VMware是最好用的虚拟机软件&#xff0c;安装方法见&#xff1a; 本文安装当前最新的Ubuntu的LTS镜像&#xff1a;ubuntu2022.04.4LTS。 1.下载Ubuntu镜像 下载地址…

电脑技巧:告别卡顿,迎接流畅——Wintune系统优化软件功能详解

目录 一、Wintune介绍 二、Wintune核心功能介绍 2.1 系统优化 2.2 隐私功能 2.3 文件管理模块 2.4 可选选项 2.5 UWP app服务 2.6 startup Manager 2.7、主机编辑 三、总结 电脑是大家目前日常办公娱乐必不可小的工具&#xff0c;软件市场上的系统优化软件层出不穷&a…

一二三应用开发平台应用开发示例(5)——列表视图、树视图、树表视图、树参照视图配置

列表视图 接下来进入列表视图配置&#xff0c;创建的操作方式跟前面相同&#xff0c;如下图所示&#xff1a; 保存后回到列表&#xff0c;点击行记录的配置按钮&#xff0c;进入如下配置页面 可以看到该配置界面相比新增、修改、查看那三个视图要复杂得多&#xff0c;配置项…

帕金森患者的福音,这些食物竟有神奇疗效!

在忙碌的现代生活中&#xff0c;健康问题越来越受到大家的关注。而帕金森病作为一种常见的老年神经系统疾病&#xff0c;更是让许多患者和家庭倍感压力。但是&#xff0c;你知道吗&#xff1f;除了药物治疗和手术干预&#xff0c;日常饮食也能对帕金森病产生积极的影响。今天&a…

开源分享:一套完整的直播购物系统源码

直播购物已经成为一种炙手可热的电商模式&#xff0c;吸引了无数商家和消费者的目光。对于开发者来说&#xff0c;构建一个功能齐全、用户体验优良的直播购物系统是一项复杂的任务。本文将分享一套完整的直播购物系统源码&#xff0c;帮助开发者快速搭建自己的直播购物平台。 …

基于springboot+vue+uniapp的语言课学习系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

在Centos上安装Lua不要用什么curl指令,这样获取到的压缩包不是gzip格式的

Lua 环境安装 | 菜鸟教程 (runoob.com) 在这一篇里&#xff0c;把这一行 换成 wget http://www.lua.org/ftp/lua-5.3.0.tar.gz 再去解压编译安装就对了。

ue5导航网格设置

AI使用导航网格进行移动&#xff0c;所以&#xff0c;先设置导航网格边界体积 2&#xff0c;使导航网格边界体积覆盖AI所需要的场景&#xff08;绿色区域&#xff09;&#xff0c;x,y&#xff0c;z在这里都扩大到原来的10倍 3&#xff0c;打开actor的“启用tick并开始” 4&…

No module named ‘MySQLdb‘

python 运行代码的时候遇到No module named ‘MySQLdb’报错如何解决&#xff1f; 解决办法 如果没有安装可以先安装以下依赖库 pip install PyMySQL如果已经安装了PyMySQL&#xff0c;仍然报MySQLdb模块找不到&#xff0c;可以尝试安装以下依赖库。 pip install mysqlclient

二轴机器人装箱机:重塑物流效率,精准灵活,引领未来装箱新潮流

在现代化物流领域&#xff0c;高效、精准与灵活性无疑是各大企业追求的核心目标。而在这个日益追求自动化的时代&#xff0c;二轴机器人装箱机凭借其较佳的性能和出色的表现&#xff0c;正逐渐成为装箱作业的得力助手&#xff0c;引领着未来装箱新潮流。 一、高效&#xff1a;重…

【12】交易-“未花费交易输出”

1. 未花费交易输出 1.1 概念 未花费交易输出(unspent transactions output, UTXO)。未花费(unspent)指的是这个输出还没有被包含在任何交易的输入中,或者说没有被任何输入引用。 在交易结构示意图中,未花费的输出是:tx1, output 1;tx3, output 0;tx4, output 0。 1…

JavaScript原型对象和对象原型、原型继承、原型链

目录 1. 原型对象和对象原型2. 原型继承3. 原型链 1. 原型对象和对象原型 作用: 以前通过构造函数实例化的对象&#xff0c;每个实例化的对象的属性和方法都是独立的&#xff0c;会造成内存浪费。通过prototype对象原型能实现不同实例化对象共享公用的属性和方法&#xff0c;减…

Android 10.0 关于定制自适应AdaptiveIconDrawable类型的动态日历图标的功能实现系列一

1.前言 在10.0的系统rom定制化开发中,在关于定制动态时钟图标中,原系统是不支持动态日历图标的功能,所以就需要从新 定制动态时钟图标关于自适应AdaptiveIconDrawable类型的样式,就是可以支持当改变系统图标样式变化时,动态日历 图标的背景图形也跟着改变,所以接下来就来…

国产分布式数据库灾备高可用实现

最近在进行核心业务系统的切换演练测试&#xff0c;就在想一个最佳的分布式数据库高可用部署方案是如何保证数据不丢、系统可用的&#xff0c;做到故障时候可切换、可回切&#xff0c;并且业务数据的一致性。本文简要介绍了OceanBase数据库和GoldenDB数据库在灾备高可用的部署方…

Springboot ResourceLoader获取指定package目录下所有的类(get class in jar on Linux)

get class in jar on Linux Springboot ResourceLoader获取指定package目录下所有的类 PathMatchingResourcePatternResolver resolver new PathMatchingResourcePatternResolver();String pattern ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX ClassUtils.convertClas…

《概率论与数理统计》期末复习笔记_下

目录 第4章 随机变量的数字特征 4.1 数学期望 4.2 方差 4.3 常见分布的期望与方差 4.4 协方差与相关系教 第5章 大数定律和中心极限定理 5.1 大数定律 5.2 中心极限定理 第6章 样本与抽样分布 6.1 数理统汁的基本概念 6.2 抽样分布 6.2.1 卡方分布 6.2.2 t分布 6.…

join()方法——连接字符串、元组、列表和字典

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 join()方法用于连接字符串数组。将字符串、元组、列表中的元素以指定的字符&#xff08;分隔符&#xff09;连接生成一个新的字符串&#…

全球点赞第一起名大师颜廷利:是金子总会“花光”的

在物质世界的繁华背后&#xff0c;隐藏着一个深刻的真理&#xff1a;有形之物的分享会逐渐减少&#xff0c;而无形之物的传递却能不断增值。金钱、货币、银两这些商业领域的实体&#xff0c;往往激发出人类对更多财富的渴望和对资源枯竭的恐惧。这种恐惧源于资源的有限性&#…

CSDN写文章时需要上、下标字号怎么输?

上标&#xff1a;​^^&#xff0c;符号中间加字 下标&#xff1a;~~&#xff0c;符号中间加字 前题是用MD编辑器&#xff0c;不然白搭&#xff1a; 我是感觉CSDN这个文本编辑比较拉&#xff0c;非常想吐槽。

10_网络规划和管理

目录 网络拓扑结构 网络拓扑结构分类 层次化局域网模型 建筑物综合布线系统 网络管理命令 其他知识点汇总 网络地址翻译 默认网关 PPP 冲突域和广播域 网络拓扑结构 网络拓扑结构分类 网络拓扑结构按分布范围分类见表 网络拓扑结构按范围分类 网络分类 缩写 分布…