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

Spring详解【1】

1. Spring概述

1.1 为什么需要Spring框架

Spring 出现之前,开发企业级 Java 应用特别是基于 Java EE 的应用是一件非常复杂和繁琐的事情。开发者需要处理大量的样板代码、复杂的配置以及与底层技术的紧密耦合,这使得开发效率低下,代码难以测试和维护。Spring 框架的诞生就是为了解决这些痛点。它的核心理念是让开发者能够专注于业务逻辑,而不是基础设施。🧭Spring 解决的核心痛点如下:

问题Spring解决方案
Java EE 开发繁琐、XML 配置多、耦合强IoC 容器统一对象管理,解耦组件
通用功能如日志、事务、权限分散在各层AOP 抽离横切逻辑,集中管理
数据库操作 DAO 层样板代码多Spring Data 自动生成 Repository
配置复杂、服务部署繁琐Spring Boot 自动配置 + 内嵌容器
微服务治理难、组件搭建慢Spring Cloud 提供标准化组件体系

除此之外,Spring提供了一站式的、全面的生态系统:Spring 不仅仅是一个 IoC 容器,它已经发展成为一个庞大的生态系统,为应用开发提供了全方位的支持。无论你需要什么,几乎都能在 Spring 的生态中找到成熟的解决方案。

  • Web开发:Spring MVCSpring WebFlux
    • 轻松构建 RESTful API 和传统的 Web 应用
    • 提供了强大的MVC架构模式
    • WebFlux 支持响应式编程,适用于高并发、非阻塞的场景
  • 数据访问:Spring Data
    • 极大地简化了与数据库的交互,无论是关系型数据库还是非关系型数据库
    • 你只需定义一个接口,Spring Data 会在运行时自动为你生成实现,无需编写繁琐的 DAO/Repository 代码
  • 安全:Spring Security
    • 一个功能强大且高度可定制的认证和授权框架
    • 可以轻松地为你的应用添加登录、权限控制、OAuth2JWT 等安全功能
  • 简化配置与快速开发:Spring Boot
    • Spring Boot 遵循约定优于配置的原则,提供了大量的自动配置
    • 开发者可以快速启动和运行一个独立的、生产级别的 Spring 应用,而无需进行繁琐的 XML 配置;且内置了 Web 服务器如 Tomcat,使得创建微服务变得异常简单
  • 云原生与微服务:Spring Cloud,基于 Spring Boot,提供了一整套用于构建分布式系统的工具集,如服务发现、配置中心、断路器、网关等,是构建微服务架构的事实标准
  • 测试:Spring Test,提供了顶级的测试支持,可以轻松地进行集成测试和单元测试,加载 Spring 上下文并注入依赖

1.2 Spring的八大模块

Spring 框架的八大核心模块是其分层架构的基础,每个模块专注于特定领域的功能支持,共同构建了灵活的企业级开发体系。以下是各模块的详细说明及相互关系:

模块名称核心功能关键组件/特性
Spring Core提供IoC容器实现,管理Bean的生命周期和依赖注入BeanFactoryApplicationContext、资源加载机制
Spring Context扩展Core模块,提供企业级服务支持国际化、事件传播、资源访问、JavaBeans配置
Spring AOP实现面向切面编程,解耦横切关注点,如日志、事务动态代理JDK/CGLIBAOP联盟兼容API、声明式事务管理
Spring DAO抽象JDBC操作,简化数据库访问JdbcTemplate、统一的数据访问异常体系
Spring ORM集成主流ORM框架支持HibernatMybatis
Spring Web提供Web开发基础支持,如请求处理、文件上传MultipartResolverContextLoaderListener、与Struts/JSF集成能力
Spring Web MVC实现MVC设计模式,构建灵活的前端控制层DispatcherServlet、控制器注解@Controller、视图解析器ViewResolver
Spring Test支持单元测试与集成测试模拟Spring上下文、JUnit/TestNG集成、事务回滚测试

1.3 Spring的特点

📊 轻量级和非侵入式

  • 轻量级:Spring框架的核心容器体积小、启动开销低,可以整合到任何Java应用中,甚至可以只使用其中一部分功能。
  • 非侵入式:Spring应用中的对象不需要依赖Spring特定的API。你的对象是对立的,可以在Spring环境之外进行测试和复用,保持了代码的纯粹性。

📌 控制反转

  • 开发者不再需要通过 new 关键字手动创建对象和管理它们的依赖关系,一切都由容器来负责。
  • 极大地降低了组件之间的耦合度,使得系统更加灵活、易于维护和测试。

🔚 面向切面编程

  • AOP 允许开发者将那些跨越多个应用模块的横切关注点从业务逻辑中分离出来,进行统一管理,如日志记录、事务管理、安全检查。
  • 提高了代码的模块化程度,使得业务代码更加纯粹、简洁,同时也减少了代码冗余。

👉 一站式框架与强大的生态系统强大的集成能力与开放性统一的声明式事务管理

  • 生态系统:Spring 不是一个单一功能的框架,它提供了一整套用于企业开发的解决方案,如Web开发、数据访问、安全等。
  • 事务管理:开发者可以通过简单的注解如 @Transactional来声明性地管理事务,而无需编写繁琐的事务控制代码如 try-catch-finally、提交、回滚。它能够统一管理多种数据访问技术的事务。
  • 集成能力:Spring 并不试图取代所有东西,而是擅长与各种优秀的第三方框架进行无缝集成,如MybatisRabbitMQRedis等等。

2. 控制反转与依赖注入

2.1 控制反转IoC

控制反转 Inversion Of Control 是一种编程思想,也是 Spring 框架中最核心的思想之一,它彻底改变了传统面向对象编程中对象自己创建依赖的做法,将控制权交给第三方容器,实现解耦、灵活、可测试的系统设计。在理解IoC思想之前,需要了解什么是控制?什么是反转?

🧠 控制

在传统的程序设计中,一个对象通常会主动创建或获取它所依赖的对象,这就是所谓的正向控制或传统的控制流程。比如,你需要组装一辆汽车,如果采用正向控制:

  • 你需要一个引擎。于是,你自己去new Engine()来制造一个引擎。
  • 你需要四个轮子。于是,你自己去new Wheel()四次来制造轮子。
  • 你需要一个底盘。于是,你自己去new Chassis()来制造一个底盘。
  • 最后,你自己把这些零件组装起来。

在这个过程中,汽车这个对象掌握着完全的控制权,它精确地知道需要哪个具体型号的引擎、轮子,并亲自负责创建它们。

// 引擎实现
class V8Engine {public void start() {System.out.println("V8 引擎启动!");}
}// 汽车类
class Car {// Car 主动创建并管理它的依赖[Engine]private V8Engine engine = new V8Engine();public void drive() {engine.start();System.out.println("汽车行驶中...");}
}// 使用
public class Main {public static void main(String[] args) {Car myCar = new Car(); // 我们只需要创建 CarmyCar.drive();        // Car 自己解决了内部依赖问题}
}

上述方式导致了紧耦合Car 类与 V8Engine 类仅仅的绑定在了一起,如果某一天想要给这辆车更换一个电能引擎 ElectricEngine,你必须修改 Car类的源代码,这违背了 OCP 原则,且这种修改会引发连锁反应,违背了 DIP 原则,难以维护。

🧠 反转

控制反转就是将这种流程完全颠倒过来,将【对象的创建】和【对象之间的依赖关系】交给第三方容器进行管理。从案例的角度上也就是说:

  • 你不再亲自去制造引擎和轮子。
  • 你只需要告诉一个装配工厂【IoC 容器】:我需要一辆车,它需要一个引擎和一个底盘。这个工厂会根据你的订单【配置】,自动找到合适的引擎、轮子和底盘,并将它们组装好,最后把一辆完整的汽车交给你。

在这个过程中,控制权从汽车转移到了装配工厂,即对象到IoC容器。汽车不再关心引擎怎么来的,它只需要指导自己需要一个引擎,工厂就会提供给它,这就是控制反转。

IoC 思想通过将对象的创建和依赖关系管理外包给容器【容器就是对象的托管者】,极大地降低了代码间的耦合度,使得整个系统更加灵活、可扩展、易于测试和维护。其中,SpringIoC 容器是由 ApplicationContext 负责管理的,功能包括:

功能说明
创建和销毁 Bean 实例生命周期管理
解析依赖并自动注入DI 实现
管理配置【注解 / XML读取配置源
管理 Bean 的作用域、生命周期singletonprototype
提供 AOP 支持入口AOP 协同

2.2 依赖注入DI

依赖注入 Dependency Injection 是实现控制反转最常见的方式:

  • 依赖:一个对象需要另一个对象来完成工作,这就是依赖关系,如汽车依赖引擎。

  • 注入:依赖的对象不是由自己创建,而是由外部【IoC 容器】传递【注入】进来。

    @Component // 告诉Spring管理这个Car类
    class Car {private final Engine engine;@Autowired // 告诉Spring在这里自动注入一个Engine类型的依赖public Car(Engine engine) {this.engine = engine;}// ...
    }@Component // 告诉Spring这是一个Engine的实现
    class ElectricEngine implements Engine {// ...
    }
    

🛠️ IoC 的实现方式:Spring 提供了三种依赖注入方式

  • 构造器注入:推荐使用,保证依赖完整性。
  • Set 注入:选择使用,可注入可选依赖。
  • 字段注入:不推荐使用,随简洁但是不利于单元测试。

在了解Set注入、构造注入、字段注入之前,有必要了解下面三个注解:

  • @Value:主要用于将值注入到 Spring 管理的 Bean 的字段、方法参数或构造函数参数中。它允许你从各种来源获取值,如下:

    // 字面值
    @Value("hello world")
    private String message;@Value("100")
    private int count;
    
    // application.properties 或 application.yml
    app.name=My Spring App
    db.url=jdbc:mysql://localhost:3306/mydb// 属性文件 
    // 当属性文件中找不到对应的值则使用默认值
    @Value("${app.name:Bruce}")
    private String applicationName;@Value("${db.url}")
    private String databaseUrl;
    
    // 调用spEL
    // 访问系统属性
    @Value("#{systemProperties['java.home']}")
    private String javaHome;// 调用方法或访问Bean
    @Value("#{someService.someMethod()}")
    private String dynamicValue;// 注入集合或map
    // 假设properties文件中有 valuesMap={key1: '1', key2: '2'}
    @Value("#{${valuesMap}}")
    private Map<String, Integer> myMap;
    
    // 构造注入
    public AppInfoService(@Value("${app.name}") String applicationName,@Value("${app.version:default}") String applicationVersion, // 提供默认值@Value("AI Assistant") String creator // 字面量) {this.applicationName = applicationName;this.applicationVersion = applicationVersion;this.creator = creator;System.out.println("AppInfoService Constructor: " +"Name=" + applicationName +", Version=" + applicationVersion +", Creator=" + creator);
    
    // set注入
    @Value("${system.config.theme}")
    public void setTheme(String theme) {this.theme = theme;
    }
    
  • @AutowiredSpring 提供的依赖注入DI注解,按类型ByType自动装配 Bean,如果有多个同一类型的Bean,则需要配合@Qulifier指定Bean名称匹配。

    // 构造注入
    @Autowired
    public ProductController(ProductRepository productRepository,NotificationService notificationService) {this.productRepository = productRepository;this.notificationService = notificationService;
    }
    
    // set注入
    @Autowired
    @Qulifier("student1")
    public void setProductRepository(ProductRepository productRepository) {this.productRepository = productRepository;
    }
    
    // 字段注入
    @Autowired
    private NotificationService notificationService;
    
  • @Resource:使用与@Autowired一致,只是查找机制略有不同。

    特性@Value@Autowired@Resource
    作用注入配置值/字面量/SpEL表达式结果注入 Spring 容器中管理的 Bean 实例注入 Spring 容器中管理的 Bean 实例
    注入内容基本类型、包装类、StringClass、枚举等数据任何 Spring Bean 类型任何 Spring Bean 类型
    查找方式直接取值或解析表达式默认按类型byType,可结合 @Qualifier 按名称匹配默认按名称byName,失败时回退到按类型byType,效率比@Autowired更高
    来源Spring FrameworkSpring FrameworkJava EE 规范
    可移植性仅限 Spring 环境仅限 Spring 环境更好
    required无此属性【无法设置非空校验】支持 required 属性【默认 true无此属性通过 name 属性控制查找
    优点简化开发、配置外部化、灵活性Spring原生、对构造注入支持好可移植性好
    使用范围字段注入【推荐】、构造注入、Set注入字段注入、构造注入【推荐】、Set注入字段注入、Set注入、Set注入
    常用场景读取配置、注入常量、动态计算值注入依赖的 ServiceRepositoryBean需明确名称匹配时,或兼容 Java EE 规范的项目

2.2.1 Set注入

Spring框架中,Set注入 是一种基于Setter方法的依赖注入方式,通过调用目标对象的Setter方法将依赖对象注入到属性中。Set注入有以下特点:

  • 可选性:通过 Setter 注入的依赖是可选的。如果某个属性没有 Setter 方法,或者 Spring 容器中没有匹配的 BeanBean 也能被成功创建,只是该属性不会被注入。
  • 可变性:通过 Setter 注入的属性在 Bean 创建后可以被修改。
  • 依赖解耦:与构造器注入一样,它也实现了依赖的解耦,Bean 之间通过接口而不是具体实现进行协作。
  • 避免循环依赖:在某些情况下,如果 A 依赖 BB 依赖 A,且两者都使用构造器注入时,会形成循环依赖,Spring 默认无法解决。此时,将其中一个或两个的依赖改为 Setter 注入,可以打破这种循环,因为 Setter 注入发生在对象实例化之后。

Spring 提供了 XML 配置和注解两种方式来实现 Setter 注入。

  • xml配置:在 SpringXML 配置文件中,使用 <property> 标签来指定要注入的属性。

    // UserService.java
    public class UserService {private UserRepository userRepository;// Setter 方法用于注入依赖// 对于setter方法,命名必须是 set + Xxx,最好使用idea直接生成public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public void doSomething() {System.out.println("UserService is doing something with: " + userRepository.getClass().getSimpleName());}
    }// UserRepository.java
    public class UserRepository {public void findUser() {System.out.println("Finding user in database...");}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userRepository" class="com.example.UserRepository"/><bean id="userService" class="com.example.UserService">// 也可以使用value注入字面量,如字符串、数字等<property name="userRepository" ref="userRepository"/> </bean></beans>
    
  • 注解:最常用的注解是 @Autowired@Resource

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component; // 或 @Service, @Repository// UserService.java
    @Component // 标识为 Spring Bean
    public class UserService {private UserRepository userRepository;// 使用 @Autowired注解在Setter方法上@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public void doSomething() {System.out.println("UserService is doing something with: " + userRepository.getClass().getSimpleName());}
    }// UserRepository.java
    @Component // 标识为 Spring Bean
    public class UserRepository {public void findUser() {System.out.println("Finding user in database...");}
    }
    

    如果使用注解的方式实现依赖注入,则需要开启组件扫描,如下:

    // xml配置
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">// 在指定包下扫描带有Spring注解的类,如@Component注解<context:component-scan base-package="com.example"/>
    </beans>
    
    // 通过java配置类
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;@Configuration // 标识为Spring配置类
    @ComponentScan(basePackages = "com.example")
    public class AppConfig {// 无需显式定义Bean,@ComponentScan会自动发现并注册
    }
    

2.2.2 构造注入

构造注入通过调用 Bean 的构造器来完成依赖的传递和初始化。Spring IoC 容器在创建 Bean 实例时,通过调用带有参数的构造器来将依赖项作为参数传递给 Bean。这意味着 Bean 在被实例化时,其所有必要的依赖都已经被完全提供了。在现代 Spring 应用中,构造器注入通常是首选,便于单元测试。其核心思想如下:

  • 依赖在构造时提供:Bean 在被创建的那一刻就获得了所有必需的依赖。
  • 不可变性:通过构造器注入的依赖通常是不可变的。因为这些依赖是通过 final 关键字修饰的成员变量来存储的,一旦对象被创建,这些依赖就不能再被改变。
  • 强制性依赖:如果一个 Bean 缺少通过构造器注入的依赖,它将无法成功创建。可以避免空指针异常。

同样的,Spring 提供了 XML 配置和注解两种方式来实现构造器注入。

  • xml配置:当有多个构造器参数时,<constructor-arg> 标签的顺序默认对应构造器参数的顺序。为了更明确或处理参数类型模糊的情况,可以使用 indextype 属性。

    // UserRepository.java
    public class UserRepository {public void findUser() {System.out.println("Finding user in database...");}
    }// UserService.java
    public class UserService {private final UserRepository userRepository; // 依赖可以声明为 final// 带有 UserRepository 参数的构造器public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public void performUserOperation() {System.out.println("UserService is performing operation...");userRepository.findUser();}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userRepository" class="com.example.UserRepository"/><bean id="userService" class="com.example.UserService"><constructor-arg ref="userRepository"/></bean>
    </beans>
    
  • 注解:最常用的注解是 @Autowired。当然,仍然需要配置组件扫描。

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service; // 或 @Component, @Repository// UserRepository.java
    @Repository // 标识为 Spring Bean
    public class UserRepository {public void findUser() {System.out.println("Finding user in database...");}
    }// UserService.java
    @Service // 标识为 Spring Bean
    public class UserService {private final UserRepository userRepository; // 推荐声明为 final// 使用 @Autowired 注解在构造器上// Spring 会自动将其视为构造器注入点@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}public void performUserOperation() {System.out.println("UserService is performing operation...");userRepository.findUser();}
    }
    

2.2.3 字段注入

字段注入,也称为属性注入,是指 Spring IoC 容器通过反射机制,直接将依赖注入到 Bean 的私有或保护字段中,而无需通过公共的 Setter 方法或构造器。其特点如下:

  • 直接赋值:Spring 容器在实例化Bean 之后,直接绕过构造器和 Setter 方法,利用反射将依赖赋值给字段。
  • 简洁性:代码看起来非常简洁,无需编写 Setter 方法或冗长的构造器参数。

字段注入主要通过注解【组件扫描】来实现,通常是 @Autowired@Resource@ValueXML 配置无法直接实现字段注入,因为 XML只能配置属性【通过 Setter】或构造器参数。

  • @Autowired
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;// ProductRepository.java
@Repository // 标识为 Spring Bean
public class ProductRepository {public void findProductById(String id) {System.out.println("Finding product with ID: " + id + " in database...");}
}// OrderService.java
@Service // 标识为 Spring Bean
public class OrderService {// 使用 @Autowired 注解直接在字段上@Autowiredprivate ProductRepository productRepository; // 字段可以是 privatepublic void createOrder(String productId) {System.out.println("OrderService is creating an order...");productRepository.findProductById(productId);}
}

2.2.4 Set注入专题

🔄 基本类型和字符串注入:在set注入中,基本类型除了八大基本数据类型外,还可以将 StringClass、枚举和日期时间类型【比较特殊,一般看作引用类型】看作基本类型使用value赋值。

  • xml配置方式

    <!-- applicationContext.xml -->
    <bean id="student" class="com.example.Student"><property name="name" value="Alice"/><property name="age" value="22"/>
    </bean>
    
  • Java注解

    @Component
    public class Student {private String name;private int age;@Value("Alice")public void setName(String name) { this.name = name; }@Value("22")public void setAge(int age) { this.age = age; }
    }
    

🔄 引用类型注入:Bean 的注入

  • xml配置方式

    <bean id="address" class="com.example.Address"><property name="city" value="Beijing"/>
    </bean><bean id="student" class="com.example.Student"><property name="address" ref="address"/>
    </bean>
    
  • Java注解

    @Component
    public class Address {private String city;@Value("Beijing")public void setCity(String city) { this.city = city; }
    }@Component
    public class Student {private Address address;@Autowiredpublic void setAddress(Address address) { this.address = address; }
    }
    

🔄 集合类型注入:ListSetMapProperties

  • xml注解方式

    <bean id="collectionBean" class="com.example.CollectionBean"><property name="list"><list><value>Apple</value><value>Banana</value></list></property><property name="set"><set><value>One</value><value>Two</value></set></property><property name="map"><map><entry key="A" value="Alpha"/><entry key="B" value="Beta"/></map></property><property name="properties"><props><prop key="username">admin</prop><prop key="password">1234</prop></props></property>
    </bean>
    
    public class CollectionBean {private List<String> list;private Set<String> set;private Map<String, String> map;private Properties properties;public void setList(List<String> list) { this.list = list; }public void setSet(Set<String> set) { this.set = set; }public void setMap(Map<String, String> map) { this.map = map; }public void setProperties(Properties properties) { this.properties = properties; }
    }
    
  • Java注解

    @Component
    public class CollectionBean {private List<String> list;private Set<String> set;private Map<String, String> map;private Properties properties;@Autowiredpublic void setList(List<String> list) { this.list = list; }@Autowiredpublic void setSet(Set<String> set) { this.set = set; }@Autowiredpublic void setMap(Map<String, String> map) { this.map = map; }@Autowiredpublic void setProperties(Properties properties) { this.properties = properties; }
    }
    
    @Configuration
    public class AppConfig {@Beanpublic List<String> list() {return Arrays.asList("Apple", "Banana", "Cherry");}@Beanpublic Set<String> set() {return new HashSet<>(Arrays.asList("One", "Two"));}@Beanpublic Map<String, String> map() {Map<String, String> map = new HashMap<>();map.put("A", "Alpha");map.put("B", "Beta");return map;}@Beanpublic Properties properties() {Properties props = new Properties();props.setProperty("username", "admin");props.setProperty("password", "1234");return props;}
    }
    

🔄 注入null

  • xml配置方式

    <bean id="user" class="com.example.User"><property name="nickname"><null/></property>
    </bean>
    
  • Java注解

    @Value("#{null}")
    private String nickname;
    

🔄 注入特殊字符:主要是xml会面临该问题,xml会解析一些特殊字符;注解的方式不存在该问题,@Value 注解通常直接接受一个字符串字面量或者一个 SpEL 表达式。Java 字符串本身就可以包含这些特殊字符,无需像 XML 那样使用 CDATA

<property name="htmlContent"><value><![CDATA[<div><p>Hello, <b>World</b>!</p></div>]]></value>
</property>

3. 简单搭建一个Spring应用

1️⃣ 创建Maven项目,添加核心依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.person</groupId><artifactId>spring</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- 引入spring核心容器 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.2.7</version></dependency></dependencies></project>

2️⃣ 创建 Service 接口和实现

// HelloService.java
package com.example.service;public interface HelloService {void sayHello(String name);
}
// HelloServiceImpl.java
package com.example.service;public class HelloServiceImpl implements HelloService {@Overridepublic void sayHello(String name) {System.out.println("Hello, " + name + "!");}
}

3️⃣ 配置 Spring 容器

  • 使用Java类实现

    // AppConfig.java
    package com.example.config;import com.example.service.HelloService;
    import com.example.service.HelloServiceImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;@Configuration
    public class AppConfig {@Beanpublic HelloService helloService() {return new HelloServiceImpl();}
    }
    
  • 使用xml配置实现:xml文件一般放置在类路径下

    // resources/applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="helloService" class="com.example.service.HelloServiceImpl"/>
    </beans>
    

4️⃣ 编写主类

  • 使用Java类实现

    package com.example;import com.example.config.AppConfig;
    import com.example.service.HelloService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 面向接口编程HelloService helloService = context.getBean(HelloService.class);helloService.sayHello("World");}
    }
    
  • 使用xml配置实现

    package com.example;import com.example.service.HelloService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 如果xml配置文件不在类路径时,很少使用,移植性差// ApplicationContext context = new FileSystemXmlApplicationContext("/opt/config/app-context.xml");// 查找id为"hello"的Bean,再根据类型强制转换HelloService helloService = context.getBean("helloService", HelloService.class);helloService.sayHello("World");}
    }
    

🎯 使用xml配置时获取Bean可以直接通过beanid获取【id不存在时代码报错】,当使用Java类做配置时,又是怎么获取Bean的呢?

  • 使用Java类配置Bean时,Spring注入时根据类型查找。
  • 如果匹配到多个Bean,需要通过 @Primary@Qualifier 消除歧义,否则代码会报错。

🎯 Spring要想实例化对象,必须得有无参构造器吗?答案是否。

  • 默认行为:如果你没有明确指定 Spring 如何实例化一个 BeanSpring 默认会尝试使用其无参构造器来创建实例。

  • 带参构造器【构造注入】:

    public class MyService {private MyRepository myRepository;// 只有一个带参数的构造器,没有无参构造器public MyService(MyRepository myRepository) {this.myRepository = myRepository;}
    }
    
    // 使用Java配置类
    @Configuration
    public class AppConfig {@Beanpublic MyRepository myRepository() {return new MyRepository();}@Beanpublic MyService myService(MyRepository myRepository) {// Spring 会自动将 myRepository Bean 注入到这里return new MyService(myRepository);}
    }
    
    // 使用xml配置
    <bean id="myRepository" class="com.example.MyRepository"/>
    <bean id="myService" class="com.example.MyService"><constructor-arg ref="myRepository"/>
    </bean>
    
    // 组件扫描 / @Component + @Autowired
    @Repository
    public class MyRepository {// ...
    }@Service
    public class MyService {private final MyRepository myRepository;// Spring4.3以后,如果只有一个构造器,@Autowired可以省略// @Autowiredpublic MyService(MyRepository myRepository) {this.myRepository = myRepository;}
    }
    

🎯 如果有多个配置类或配置文件时如何使用?

  • xml文件

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml","datasource.xml","service.xml"
    );
    
  • Java配置类

    // 方式一: 在主配置类中引入其他配置类
    @Configuration
    @Import({DataSourceConfig.class, ServiceConfig.class})
    public class AppConfig {
    }
    
    // 使用 AnnotationConfigApplicationContext 加载多个配置类
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class, DataSourceConfig.class, ServiceConfig.class);
    

4. Spring集成日志框架

Spring中,日志框架是指一套组合:**一个日志门面加上一个具体的日志实现。**主流的日志门面毫无疑问是SLF4J,它提供了一套通用的 API 接口,你的应用程序代码只需要依赖 SLF4JAPI,而不需要直接依赖某个具体的日志实现如 LogbackLog4j2。这意味着你可以在不修改代码的情况下,轻松切换底层的日志实现。

综合来看,在大多数场景下,尤其是在高并发和异步日志方面,Log4j2 的性能通常优于 Logback。但对于大多数项目而言,Logback 的性能已经绰绰有余,且其易用性和与 Spring Boot 的良好集成使其成为一个非常受欢迎的选择。下面在spring中集成log4j2日志框架:

  • 引入相关依赖

    <dependencies><!-- 排除Spring的commons-logging --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>6.2.7</version><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions></dependency><!-- 引入Log4j2 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.17.2</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.2</version></dependency><!-- 桥接SLF4J --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.17.2</version></dependency>
    </dependencies>
    
  • 创建 Log4j2 配置文件:Log4j2 默认会在 classpath 下查找名为 log4j2.xmllog4j2.jsonlog4j2.yaml 的配置文件。推荐使用 log4j2.xml,放在 src/main/resources 目录下。

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN"><Appenders><!-- 控制台输出 --><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></Console><!-- 文件滚动日志 --><RollingFile name="File" fileName="logs/app.log"filePattern="logs/app-%d{yyyy-MM-dd}-%i.log"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/><Policies><TimeBasedTriggeringPolicy interval="1" modulate="true"/><SizeBasedTriggeringPolicy size="100 MB"/></Policies><DefaultRolloverStrategy max="10"/></RollingFile></Appenders><Loggers><!-- 根日志级别 --><Root level="info"><AppenderRef ref="Console"/><AppenderRef ref="File"/></Root><!-- 自定义包日志级别 --><Logger name="com.example.service" level="debug" additivity="false"><AppenderRef ref="File"/></Logger></Loggers>
    </Configuration>
    
  • Spring程序中使用日志

    package service.impl;import org.springframework.stereotype.Component;
    import service.HelloService;
    import org.slf4j.LoggerFactory;
    import org.slf4j.Logger;@Component
    public class HelloServiceImpl implements HelloService {Logger logger = LoggerFactory.getLogger(HelloServiceImpl.class);@Overridepublic void sayHello(String name) {logger.info("Hello, " + name + "!");}
    }
    
    // 结合lombok使用
    @Component
    @Slf4j
    public class HelloServiceImpl implements HelloService {@Overridepublic void sayHello(String name) {log.info("Hello, " + name + "!");}
    }
    
    2025-06-09 11:25:37 [main] INFO  service.impl.HelloServiceImpl - Hello, World!
    
http://www.xdnf.cn/news/13125.html

相关文章:

  • 跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
  • 2.1 状态空间表达式
  • C++课设:实现本地留言板系统(支持留言、搜索、标签、加密等)
  • 中文分词双向匹配
  • PLC入门【3】基本指令1(LD LDI OUT 等)
  • RT-Thread源码阅读(二)
  • 【Fifty Project - D34】
  • C++.OpenGL (19/64)模板测试(Stencil Testing)
  • Vue3监听浏览器刷新/关闭/前进/后退事件【兼容pc+h5】
  • 2.2 传输介质
  • ArcPy扩展模块的使用(3)
  • Niushop商城系统
  • 38.第二阶段x64游戏实战-封包-改造封包劫持工具(一)
  • 若依登录用户名和密码加密
  • 门静脉高压——治疗
  • 【ubuntu24.04】普通用户如何操作samba挂载的文件夹
  • 深入探索CDC之Canal:解锁全量与增量复制的奥秘
  • SmolVLA: A vision-language-action model for affordable and efficient robotics
  • 日拱一卒 | awk的基本操作
  • 从0到1构建我的AI星逻系统: LLM智能控制 + Streamlit前端实战
  • 达梦数据库EXISTS子查询实战指南
  • 鸿蒙图片缓存(二)
  • Day09_刷题niuke20250609
  • riscv操作系统记录(一)
  • 缓存一致性性的 实现等价
  • Element Plus 表单(el-form)中关于正整数输入的校验规则
  • DeepSeek辅助实现的DuckDB copy to自定义函数
  • SHW汽车SAP系统拆分实战:24小时停机完成重组 | SNP全球案例
  • Brup Suite 2025.5简单暴力猜解攻击手记
  • 安装便捷、维护省心,强力巨彩租赁屏助力视觉体验升级