Spring和SpringBoot相关总结

Spring和SpringBoot相关总结

工作中每天都在使用的Spring、SpringBoot框架,却对其原理不太熟悉,整理一下关于Spring、SpringBoot的一些相关问题,随时补充。

1.对SpringBoot的理解,它有哪些特性(优点)?

Spring Boot 是一个基于 Spring 框架的开源框架,用于简化 Java 应用程序的开发和部署过程。它提供了一种快速、便捷的方式来创建独立的、生产级别的Spring 应用程序。以下是我对 Spring Boot 的理解以及它的一些特性和优点:

  • 简化配置:Spring Boot 提供了自动配置的特性,可以根据项目的依赖和配置文件,自动配置应用程序。这极大地简化了开发人员的配置工作,使得应用程序的开发变得更加高效。
  • 内嵌容器:Spring Boot 内置了常用的 Servlet 容器,如 Tomcat、Jetty,这使得应用程序可以以独立的方式运行,无需额外部署容器。
  • 自动化构建:Spring Boot 集成了 Maven 或 Gradle 等构建工具,可以通过简单的命令快速构建和打包应用程序。
  • 微服务支持:Spring Boot 为微服务架构提供了良好的支持,可以通过 Spring Cloud 来构建和管理微服务应用程序。
  • 监控和管理:Spring Boot 提供了丰富的监控和管理功能,可以轻松地集成监控工具,监控应用程序的运行状态,并进行必要的管理操作。
  • 依赖管理:Spring Boot 管理了大量的依赖库版本,可以避免版本冲突和依赖问题,简化了应用程序的开发和维护。
  • 集成测试:Spring Boot 提供了测试支持,可以方便地编写和运行集成测试,保证应用程序的稳定性和质量。
  • 生态系统:Spring Boot 有一个庞大的生态系统,拥有丰富的扩展和插件,可以满足各种不同场景的需求。
    总的来说,Spring Boot 提供了一种快速、简便的开发方式,使得开发人员可以更加专注于业务逻辑的实现,而不必过多关注底层的配置和细节。它的特性和优点使得它成为了当今 Java 开发中最流行的框架之一。

2.Spring、Spring MVC 和 Spring Boot 联系区别

Spring、Spring MVC 和 Spring Boot 是 Spring Framework 的不同模块,它们在 Java 应用程序开发中扮演着不同的角色,但又有着一定的联系和关联。

  • Spring :
    1.Spring 是一个全面的 Java 开发框架,提供了依赖注入(DI)、面向切面编程(AOP)、事务管理、数据访问等各种功能模块,帮助开发者构建企业级应用程序。
    2.Spring Framework 解决了传统 Java EE 开发中的一些问题,使得开发者可以更轻松地编写可测试、松耦合的代码。
    3.Spring Framework 的核心是 IoC(控制反转)和 DI(依赖注入),它通过容器管理对象的生命周期和依赖关系,使得开发者可以将业务逻辑和底层技术解耦。
  • Spring MVC:
    1.Spring MVC 是 Spring 框架的一个模块,用于构建 Web 应用程序。它基于 MVC(Model-View-Controller)设计模式,将应用程序分为模型、视图和控制器三层。
    2.Spring MVC 提供了一种灵活的方式来处理 HTTP 请求和响应,使得开发者可以轻松地构建 Web 应用程序,并实现 RESTful 服务。
    3.Spring MVC 使用注解来配置和管理控制器、路由和请求参数等,使得开发更加简洁和高效。
  • Spring Boot:
    1.Spring Boot 是 Spring Framework 的一个扩展,旨在简化 Spring 应用程序的开发和部署过程。它通过约定大于配置的方式,提供了一种快速、便捷的方式来创建独立的、生产级别的 Spring 应用程序。
    2.Spring Boot 集成了许多常用的第三方库和工具,如 Tomcat、Jetty、Hibernate、Spring Data 等,使得开发者无需手动配置,即可快速构建和部署应用程序。
    3.Spring Boot 提供了自动配置、内嵌容器、依赖管理、微服务支持等特性,大大简化了 Spring 应用程序的开发和部署流程。

3.SpringBoot的核心注解有哪些?

Spring Boot 中有许多核心注解,它们是开发 Spring Boot 应用程序时经常用到的关键注解,包括:
@SpringBootApplication:
这是 Spring Boot 应用程序的主要注解,通常用于启动类上。它组合了以下三个注解的功能:

  • @Configuration:标识该类是一个配置类,可用于定义 Bean。
  • @EnableAutoConfiguration:启用自动配置,根据 classpath 下的依赖、其他配置以及你自己定义的配置来推断应用程序应该如何配置。
  • @ComponentScan:启用组件扫描,自动发现并注册 Spring Bean。

@RestController:
这个注解用于标识一个类是 RESTful 服务的控制器,相当于@Controller和@ResponseBody的组合。通常用于定义 RESTful API 的处理器。

@RequestMapping:
这个注解用于定义请求的 URL 映射规则,可以标识在类级别和方法级别上。在类级别上标识的 URL 前缀将应用于该类中所有方法的请求映射。

@Autowired:
这个注解用于自动装配 Bean,Spring Boot 将根据类型自动寻找匹配的 Bean 并注入到对应的属性中。通常用于在控制器、服务、组件等类中注入其他 Spring 管理的 Bean。

@Service、@Repository、@Component:
这些注解用于将一个类标识为 Spring Bean,通常用于服务层、数据访问层和其他组件类上。Spring Boot 在启动时会自动扫描并注册标记了这些注解的 Bean。

@Configuration:
这个注解用于标识一个类是配置类,通常与@Bean注解一起使用,用于定义 Bean。

@Bean:
这个注解通常用于方法级别,用于定义一个 Bean,并将其加入到 Spring 容器中。被注解的方法返回的对象将被 Spring 管理。

@Value:
这个注解用于从属性文件中获取值,可以注入到 Spring 管理的 Bean 中。通常用于注入配置文件中的属性值。

这些核心注解在 Spring Boot 应用程序的开发中使用非常频繁,可以用于标识控制器、服务、配置类、数据访问层等不同组件,并帮助 Spring Boot 自动管理和装配这些组件,使得开发更加高效和便捷。

4.Springboot的自动配置原理

Spring Boot 的自动配置是其核心特性之一,它允许开发者快速启动和运行基于 Spring 的应用,而无需手动配置太多的 Spring 应用程序上下文。自动配置原理主要通过利用 Spring 框架的依赖注入和条件注解特性,根据应用的环境和类路径中的类自动配置 Spring 应用程序。这里将详细解释其原理,并针对源码进行讲解。
1.自动配置的实现原理
① 自动配置类 (@Configuration 类):
自动配置是通过带有 @Configuration 注解的类实现的,这些类中通常会包含一系列有条件的 bean 定义。这些条件通常是通过 @Conditional 派生注解来指定的,比如 @ConditionalOnClass@ConditionalOnBean@ConditionalOnMissingBean@ConditionalOnProperty 等。

②条件注解:

  • @ConditionalOnClass: 当类路径上存在指定的类时,条件成立。
  • @ConditionalOnBean: 当上下文中存在指定的 Bean 时,条件成立。
  • @ConditionalOnMissingBean: 当上下文中不存在指定的 Bean 时,条件成立。
  • @ConditionalOnProperty: 根据 Spring 环境中的某个属性来决定条件是否成立。

spring.factories 文件:
所有的自动配置类都会在 META-INF/spring.factories 文件中通过 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的键被 Spring Boot 自动发现。这个文件列出了所有可用的自动配置类。

2.源码解析
DataSourceAutoConfiguration 为例,这是一个典型的自动配置类,自动配置类通常位于 spring-boot-autoconfigure 项目中。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @Conditional(HikariCPCondition.class)
    protected static class Hikari {

        @Bean
        @ConditionalOnMissingBean
        public HikariDataSource dataSource(DataSourceProperties properties) {
            // 返回配置好的 HikariDataSource
        }
    }

    @Configuration(proxyBeanMethods = false)
    @Conditional(TomcatJdbcCondition.class)
    protected static class Tomcat {

        @Bean
        @ConditionalOnMissingBean
        public DataSource dataSource(DataSourceProperties properties) {
            // 返回配置好的 Tomcat 数据源
        }
    }
}

这个配置类首先检查类路径上是否有 DataSourceEmbeddedDatabaseType,如果不存在,则此自动配置不会应用。其次,它通过 @ConditionalOnMissingBean(DataSource.class) 确保如果已经存在 DataSource 类型的 Bean,则不会再创建新的。接着,根据不同的条件,如数据库连接池类型,选择性地配置不同的数据源。

通过上述机制,Spring Boot 能够灵活地根据应用当前的运行环境和依赖情况,自动提供合适的配置,极大简化了 Spring 应用的配置工作。这种自动配置的思想有效地支持了“约定优于配置”的软件开发实践,使开发人员能够更专注于业务逻辑的实现。

5.SpringBoot的启动原理

Spring Boot 是为了简化 Spring 应用的初始搭建以及开发过程而设计的一个框架,它使得创建一个独立的、生产级别的 Spring 应用变得更加简单。Spring Boot 背后的核心特性是自动配置和 Starters,它基于“约定大于配置”的理念减少了项目的配置量。下面我们就详细讲解一下 Spring Boot 的启动原理和详细过程。

启动过程总览
Spring Boot 应用的启动过程主要可以分为以下几个阶段:

  • 初始化 SpringApplication 实例:解析需要的配置以及初始化一些关键组件(如事件发布器)。
  • 运行 SpringApplication 实例:涉及配置环境、创建 ApplicationContext、执行自动配置、初始化和注册所有的 Bean 等关键步骤。
  • 刷新 ApplicationContext:加载所有单例 Bean。
  • 触发应用和命令行运行器:运行所有 CommandLineRunner 和 ApplicationRunner 接口的实现。

1. SpringApplication 实例的初始化
当运行 Spring Boot 应用的 main 方法时,首先会创建一个 SpringApplication 对象。这一过程包括:

  • 识别是否是一个 web 环境(是否依赖于 Spring Web MVC 或 Spring WebFlux)。
  • 设置初始参数。
  • 识别并加载所有的 SpringFactoriesLoader 格式的配置文件。这包括 META-INF/spring.factories 文件中配置的所有 ApplicationContextInitializer、ApplicationListener、AutoConfiguration 等。
public static void main(String[] args) {
    SpringApplication.run(MySpringBootApplication.class, args);
}

2. 运行 SpringApplication
SpringApplication.run(…) 方法的执行包含若干关键步骤:

  • 载入 application.propertiesapplication.yml 等外部配置文件。
  • 创建并配置好合适的 ApplicationContext 实例,根据是否是 web 玛境,决定创建 AnnotationConfigServletWebServerApplicationContext 或 AnnotationConfigApplicationContext。
  • 应用所有的 ApplicationContextInitializer,允许对 ApplicationContext 进行编程式的初始化。
  • 如果是 web 环境,将会实例化一个内嵌的 Web 服务器(如 Tomcat、Jetty 或 Undertow)。
  • 执行自动配置,这是 Spring Boot 的核心特性。通过 @EnableAutoConfiguration 注解,Spring Boot 会自动扫描项目中所有的配置类,并根据类路径下的内容,环境变量,配置文件等条件动态地将 Bean 注入 Spring 应用上下文中。
  • ExecutionContext 刷新,此过程包括实例化所有的 Bean、配置属性绑定、配置 Bean 生命周期事件发布等。
    3. 刷新 ApplicationContext
    该步骤主要完成 Bean 的创建和初始化的过程:
  • 触发任何 BeanFactoryPostProcessor 的处理。
  • 注册任何 BeanPostProcessor 的处理。
  • 初始化单例 Bean。
    4. 触发应用和命令行运行器
    Spring Boot 允许 CommandLineRunner 和 ApplicationRunner 接口的组件在 Spring 应用上下文加载完毕后执行。这对于执行一些初始化逻辑非常有帮助。
    5. 应用准备就绪
    至此,Spring Boot 应用就准备就绪,可以开始接收 HTTP 请求或者执行用户定义的代码了。

Spring Boot 的设计哲学是尽可能地自动配置 Spring 应用,减少开发者的配置负担,使得开发者可以更专注于业务逻辑的开发。通过上述的启动过程,我们可以看到 Spring Boot 做了大量的背后工作来确保这一点。

6.为什么SpringBoot的jar可以直接运行?

Spring Boot 的可执行 Jar 之所以可以直接运行,其实是通过一系列的技术手段和设计实现的。让我们逐步分析源码解析为什么 Spring Boot 的 Jar 可以直接运行:
1.Spring Boot Maven 插件:
首先,Spring Boot 提供了一个 Maven 插件 spring-boot-maven-plugin,这个插件负责将应用程序打包成一个可执行的 Jar 包。在 Maven 的 pom.xml 文件中配置该插件,会生成一个特殊的 Jar 文件,即 Fat Jar。

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.Fat Jar 的构建: (jar包中包含jar)
打包后生成的 Jar 文件包含了应用程序的所有类文件以及所需的依赖的类文件,这样就形成了一个 Fat Jar。这是通过 Maven 插件的特殊配置实现的。

3.Manifest 文件:
在生成的 Jar 包的 MANIFEST.MF 文件中,指定了主类(Main-Class)。Spring Boot 使用的是
org.springframework.boot.loader.JarLauncher作为启动类。这个启动类负责启动 Spring Boot 应用程序。

4.启动过程:
当执行 java -jar your-application.jar 命令时,Java 运行时会读取 Jar 包的 MANIFEST.MF 文件,找到指定的启动类 org.springframework.boot.loader.JarLauncher。然后,JarLauncher 创建一个 LaunchedURLClassLoader 类加载器来加载 Jar 包中的类文件。接着,它会根据 MANIFEST.MF 中的 Start-Class 属性找到应用程序的真正启动类,并以一个新线程启动应用的启动类的Main函数(找到manifest中的Start-Class) 。

通过这种设计,Spring Boot 的可执行 Jar 能够直接运行,无需额外的配置和依赖。这种设计使得 Spring Boot 应用程序的部署和运行变得非常简单和方便。

7.SpringMVC中的Servlet处理流程。

SpringMVC 的核心组件:
DispatcherServlet: 核心的中央处理器,负责接收请求、分发,并给予客户端响应。
HandlerMapping: 处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
HandlerAdapter: 处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
Handler: 请求处理器,处理实际请求的处理器。
ViewResolver: 视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
在这里插入图片描述

  1. 客户端(浏览器)发送请求, DispatcherServlet拦截请求。
  2. DispatcherServlet 根据请求信息调用 HandlerMapping 。 HandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter适配器执行 Handler 。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
  5. ViewResolver 会根据逻辑 View 查找实际的 View。
  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  7. 把 View 返回给请求者(浏览器)。

8.Mybtatis和JDBC的区别

MyBatis 和 JDBC 是用于操作数据库的两种不同技术,它们各有特点和用途。下面我们将详细讨论它们之间的主要区别,并通过例子进行说明。
1. 抽象级别

  • JDBC:Java Database Connectivity (JDBC) 是一个标准的 Java API,用于连接并执行数据库操作。它提供了一种基础方式,直接与数据库交互,执行 SQL 语句,处理结果集等。
  • MyBatis:MyBatis 是一个半ORM(对象关系映射)框架,相比JDBC,它在 JDBC 的基础上进行了封装,提供了更高层次的抽象。它允许将 SQL 语句与程序代码分离,并通过 XML 或注解方式配置。此外,它可以自动将 SQL 执行结果映射到 Java 对象或对象列表。

2. SQL 控制

  • JDBC:在使用 JDBC 时,所有 SQL 语句都是在 Java 代码中直接硬编码完成的,这使得管理 SQL 语句变得困难,尤其是在大型应用中。
  • MyBatis:MyBatis 允许从 Java 代码中分离 SQL 语句,可以将 SQL 语句维护在独立的 XML 文件或通过注解方式。这使得 SQL 管理更加清晰和集中。

3. 参数设置和结果映射

  • JDBC:使用 JDBC 时,开发者需要手动处理 SQL 参数的设置以及结果集的映射。
  • MyBatis:提供了简化这一过程的功能。参数和结果映射简单配置即可,无需手动设置。

4. 异常处理

  • JDBC:需要显式处理 SQLException,这增加了代码的复杂性和冗余。
  • MyBatis:封装了常见的异常,抛出时会转换为运行时异常 (DataAccessException),简化了错误处理。

JDBC 示例
在 JDBC 中,一个典型的查询操作可能包括以下步骤:

public List<User> getUsers() {
    List<User> users = new ArrayList<>();
    Connection connection = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        connection = DriverManager.getConnection("jdbc:url", "username", "password"); 
        String sql = "SELECT * FROM users";
        stmt = connection.prepareStatement(sql);
        rs = stmt.executeQuery();
        while (rs.next()) {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            users.add(user);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (rs != null) rs.close();
            if (stmt != null) stmt.close();
            if (connection != null) connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    return users;
}

MyBatis 示例
在 MyBatis 中,相同的查询步骤可以更简单:

<!-- mybatis-config.xml 中的 mapper 映射 -->
<mapper namespace="mapper.UserMapper">
    <select id="getUsers" resultType="User">
        SELECT * FROM users
    </select>
</mapper>

对应的 Java 映射接口为:

public interface UserMapper {
    List<User> getUsers();
}

在 Service 层中,你可以这样调用:

public List<User> getUsers() {
    SqlSession session = sqlSessionFactory.openSession();
    try {
        UserMapper mapper = session.getMapper(UserMapper.class);
        return mapper.getUsers();
    } finally {
        session.close();
    }
}

在 MyBatis 的配置中,你可以控制事务、缓存策略等高级特性,而这也相对难以使用纯 JDBC 实现。

总结
总的来说,MyBatis 比 JDBC 提供了更高级的抽象,使得数据库操作更简单、更可维护,同时也允许开发者保持对 SQL 的完全控制。这使得 MyBatis 成为在 JDBC 基础上,既提供便利又不失灵活性的解决方案。

9.Mybatis如何防止安全攻击

Mybatis 作为一款流行的持久层框架,主要面临的安全风险是 SQL 注入攻击。SQL 注入是指攻击者通过输入的 SQL 语句片段,篡改后台数据库查询逻辑,从而执行恶意 SQL,获取非法数据或破坏数据。以下是 Mybatis 如何防止 SQL 注入的策略,并且附加示例说明。
1. 使用预处理语句(PreparedStatement)
MyBatis 支持预处理语句,这是防范 SQL 注入攻击的一种非常有效的方式。通过使用预处理语句,可以确保所有输入都会被适当地转义,从而防止恶意输入破坏原有 SQL 查询的结构。
示例:
假设有一个基于用户名称查找用户的功能,如果使用拼接 SQL 方式,代码可能是这样的:

String query = "SELECT * FROM users WHERE name = '" + userName + "'";

如果 userNameadmin'; DROP TABLE users; --,那么 SQL 语句将会变成:

SELECT * FROM users WHERE name = 'admin'; DROP TABLE users; --'

这将导致 SQL 注入攻击,DROP TABLE users 语句会被执行,导致丢失 users 表。

而在使用 Mybatis 时,通过预处理语句的方式:

<select id="findUserByName" parameterType="String" resultType="User">
  SELECT * FROM users WHERE name = #{name}
</select>

这里 #{name} 是 Mybatis 参数占位符,Mybatis 会将其替换为 ?,并通过 PreparedStatement 设置参数 name 的值,从而避免了直接拼接 SQL 而引发的 SQL 注入风险。
2. 数据验证和清理
在将数据传递给 MyBatis 之前,在应用层面进行数据验证和清理亦非常重要。确保输入满足预定义的标准并从中移除或转义潜在的危险字符。
示例:
在 Java 代码中使用正则表达式清理输入:

public String cleanInput(String input) {
    return input.replaceAll("[^\\w\\s]", "");
}

这个简单的函数尝试移除任何非字母数字字符和非空白字符,从而减少恶意注入代码的机会。
3. 限制 API 的数据库权限
确保 MyBatis 使用的数据库连接仅具有执行必要操作的权限。例如,如果某个应用只需读取数据,那么数据库账户不应该有写权限。
示例:
在数据库中设置具有只读权限的用户账户,用于所有查询操作。

CREATE USER 'readonly_user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT ON database_name.* TO 'readonly_user'@'localhost';

4. 使用 MyBatis 的动态 SQL
Mybatis 提供了丰富的动态 SQL 元素,如 <if>, <choose>, <when>, <otherwise>, <foreach> 等。这些元素可以构建复杂的 SQL 语句,同时避免了直接的字符串拼接,减少了 SQL 注入的风险。
示例:

<select id="findUsersByDynamicCondition" parameterType="map" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="email != null">
      AND email = #{email}
    </if>
  </where>
</select>

在这个示例中,即使 nameemail 的值中包含潜在的 SQL 代码,使用 #{} 来引用传入的参数值能够确保它们不会被当作 SQL 代码执行,从而避免 SQL 注入。
小结
综上所述,通过使用预处理语句和动态 SQL,Mybatis 能够有效地防止 SQL 注入攻击。开发者应始终避免 SQL 语句的直接拼接,利用 Mybatis 提供的这些特性来提高应用的安全性。
MyBatis 是一个流行的 SQL 映射框架,它将接口和 XML 配置文件映射到数据库语句上。虽然 MyBatis 简化了数据库操作,但它的使用也可能引发安全问题,其中最常见的就是 SQL 注入攻击。下面将讨论如何通过 MyBatis 避免这些安全问题,特别是 SQL 注入,并提供一些示例。

10.Spring IoC(控制反转)和 DI(依赖注入)详解

控制反转(IoC)和依赖注入(DI)是 Spring 框架的核心概念,它们是实现松耦合和提高可测试性的关键。让我们根据 Spring 框架的源码来详细解释这两个概念。
控制反转(IoC):
在 Spring 中,控制反转指的是将对象的创建和管理交给 Spring 容器,而不是由应用程序自己负责。这样做的好处是,应用程序的组件不再负责管理它们之间的依赖关系,而是由 Spring 容器来管理这些组件的生命周期和依赖关系。
在源码中,控制反转主要体现在 Spring 容器的实现上。Spring 容器通过 BeanFactory 和 ApplicationContext 接口来管理对象的生命周期和依赖关系。当应用程序需要某个对象时,它不是直接创建对象,而是向 Spring 容器请求该对象的实例。Spring 容器根据配置信息(如 XML 文件、注解或 Java 配置类)来创建对象,并将对象的依赖注入到该对象中。
例如,在 XML 配置中定义一个 Bean:

<bean id="userService" class="com.example.UserService">
    <property name="userRepository" ref="userRepository"/>
</bean>

Spring 容器会根据配置信息创建 UserService 对象,并将名为 “userRepository” 的 Bean 注入到 UserService 对象中的 userRepository 属性中。
优点:
松耦合性: IoC 将对象之间的依赖关系交给外部容器管理,使得对象之间的耦合度降低。对象只需关注自身的业务逻辑,而不需要关心依赖的获取和实例化。
可测试性: 由于对象的依赖关系由容器管理,可以通过替换依赖对象来进行单元测试,而不需要修改源代码。
可维护性: IoC 将对象的创建和依赖关系的管理集中在容器中,使得代码更加清晰和易于维护。

缺点:
学习曲线: 初学者可能需要花费一些时间来理解 IoC 的概念和工作原理。
运行时性能开销: 在运行时,Spring 容器需要负责管理对象的创建和依赖注入,可能会增加一些性能开销。

依赖注入(DI):
依赖注入是控制反转的一种具体实现方式,它指的是将一个对象所依赖的其他对象的引用注入到该对象中,而不是由该对象自己创建或查找依赖的对象。这样做的好处是,对象之间的依赖关系变得松散,更易于管理和维护。

在源码中,依赖注入主要体现在 Spring 容器对 Bean 的创建和属性注入过程中。Spring 容器通过反射机制实例化 Bean,并通过 Setter 方法、构造函数或字段注入的方式将依赖对象注入到 Bean 中。
例如,在 Java 配置类中进行依赖注入:

@Configuration
public class AppConfig {
    @Bean
    public UserService userService(UserRepository userRepository) {
        UserService userService = new UserService();
        userService.setUserRepository(userRepository);
        return userService;
    }
}

Spring 容器会根据配置类创建 UserService 对象,并将 UserRepository 对象注入到 UserService 中。

通过控制反转和依赖注入,Spring 实现了松耦合、高内聚的设计原则,使得应用程序更易于扩展、维护和测试。
优点:
解耦合: 依赖注入将对象之间的依赖关系解耦,使得代码更加灵活和可维护。
可扩展性: 通过依赖注入,可以方便地替换或扩展对象的依赖关系,而不需要修改源代码。
可读性: 明确地声明依赖关系,使得代码更加清晰易懂。

缺点:
复杂性: 对于复杂的依赖关系,可能需要编写大量的配置文件或注解,增加了代码的复杂性。
运行时性能开销: 与 IoC 类似,依赖注入也会增加一定的运行时性能开销。

11.处理大量依赖注入的场景时,可以考虑以下几种方法来简化和优化代码:

使用组件扫描和自动装配: Spring 提供了组件扫描和自动装配的功能,可以自动发现和装配标记了特定注解(如 @Component、@Service、@Repository 等)的类。通过合理地组织包结构和使用注解,可以大大减少手动配置依赖注入的工作量。

使用配置类进行配置: 可以使用 Java 配置类(如 @Configuration 注解的类)来配置依赖关系,通过在配置类中使用 @Bean 注解来声明 Bean,然后通过 @Autowired 注解将它们注入到需要的地方。这种方式可以更加清晰地管理依赖关系,并且避免了 XML 配置文件的繁琐。

使用泛型和设计模式: 可以使用泛型来减少重复的代码,将相似的依赖注入逻辑抽象成通用的模板。同时,可以考虑使用设计模式如工厂模式、建造者模式等来管理和创建复杂的依赖关系,从而使代码更加灵活和可维护。

模块化开发: 将系统分解成多个模块或微服务,每个模块负责管理自己的依赖关系。通过模块化的设计,可以降低系统的复杂性,同时提高代码的可维护性和可测试性。
**懒加载:**对于大量的依赖注入,如果不是所有的 Bean 都需要在应用启动时加载,可以考虑将部分 Bean 设置为懒加载。这样可以减少启动时间和内存占用,并且可以根据实际需要进行灵活的加载控制。
在 Spring 中,可以通过在 Bean 的声明中使用 @Lazy 注解来实现懒加载,示例如下:

@Component
@Lazy
public class MyLazyBean {
    // Bean 的定义
}

通过合理地组织代码结构、使用合适的设计模式和技术,可以有效地处理大量的依赖注入,使代码更加清晰、灵活和易于维护。

12.@Autowired 和 @Resource 的区别是什么?

@Autowired@Resource 都是 Spring 框架中用于实现依赖注入的注解,但它们在功能、来源和注入方式上有所区别。以下是这两个注解的详细比较:
@Autowired

  • 来源@Autowired 注解是 Spring 框架自带的注解。
  • 注入方式:默认情况下,@Autowired 通过类型(type)进行自动装配。如果找到多个相同类型的 Bean,则会按照名称(name)来选择。@Autowired 也可以配合 @Qualifier 注解来精确指定要注入的 Bean 的名称。
  • 必须性:默认情况下,@Autowired 注解要求依赖对象必须存在,如果找不到对应的 Bean,Spring 容器启动时将抛出异常。可以通过设置 required 属性为 false 来允许空值,即:@Autowired(required=false)
  • 使用场景:主要用于 Spring 管理的 Bean 之间的依赖注入。

示例

@Component
public class VehicleService {
    
    @Autowired
    private Engine engine; // 通过类型 Engine 注入

    // 使用构造器注入
    private Vehicle vehicle;
    @Autowired
    public VehicleService(Vehicle vehicle) {
        this.vehicle = vehicle;
    }
}

@Resource

  • 来源@Resource 注解是由 JSR-250 规范提供,是 Java 的标准注解,在 Java EE 中被广泛支持。
  • 注入方式@Resource 默认按照名称(name)进行注入。如果没有指定名称,那么它将按照字段名或 setter 方法后的属性名进行匹配。如果找不到对应名称的 Bean,则会回退到按照类型(type)进行匹配。
  • 必须性@Resource 注解也默认要求依赖对象必须存在,如果找不到则在应用启动时抛出异常。
  • 使用场景@Resource 可以用于 Spring 管理的 Bean,也适合在 Java EE 应用中使用。

示例

import javax.annotation.Resource;

@Component
public class CarService {

    @Resource(name="gasEngine")
    private Engine engine; // 通过名称 "gasEngine" 注入

    // 当不指定 name 时,会尝试按字段名 "vehicle" 查找匹配的 Bean 注入
    @Resource
    private Vehicle vehicle;
}

总结

  • 注入方式@Autowired 默认按类型注入;而 @Resource 默认按名称注入。
  • @Autowired 是 Spring 的注解;@Resource 是 Java 标准的注解。
  • 指定 Bean@Autowired 配合 @Qualifier 注解指定具体的 Bean;@Resource 直接通过 name 属性指定。

在实际开发中,选择使用 @Autowired 或者 @Resource 应根据具体场景和个人偏好来决定。如果项目中已经广泛使用了 Spring 相关技术栈且需要更多的灵活性,@Autowired 可能是更好的选择。如果希望依赖更多的 Java 标准,而不是特定于 Spring 的特性,那么 @Resource 可能更适合。

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值