📃个人主页:小韩学长yyds-优快云博客
⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞
箴言:拥有耐心才是生活的关键
目录
Spring 框架简介
Spring 框架是 Java 开发领域的基石,是一个开源的轻量级应用框架,为 Java 企业级应用开发提供了全面的基础架构支持。它就像是一个功能强大的 “瑞士军刀”,涵盖了从对象管理到 Web 开发、事务处理等各个方面,极大地简化了开发过程。
Spring 框架具有诸多显著特性:
- 轻量级:Spring 框架的设计目标之一就是轻量级,它对系统资源的占用极少,无论是在小型应用还是大型企业级项目中,都能高效运行。比如,在一个内存有限的服务器环境中,Spring 应用程序的启动和运行都不会给系统带来过多负担。
- 开源:作为开源框架,Spring 拥有庞大的社区支持。全球各地的开发者都可以参与到 Spring 的开发和改进中,不断丰富其功能和特性。开发者在使用过程中遇到问题时,可以在社区中找到大量的解决方案和相关资料。
- 控制反转(IoC):这是 Spring 框架的核心特性之一。IoC 将对象的创建和管理从应用程序代码中分离出来,交由 Spring 容器负责。例如,在传统的 Java 开发中,我们可能需要手动创建对象并管理它们之间的依赖关系,而在 Spring 中,只需要通过配置或注解,就能让 Spring 容器自动创建和注入所需的对象,大大降低了代码的耦合度。
- 面向切面编程(AOP):AOP 允许将横切关注点(如日志记录、事务管理、权限控制等)与业务逻辑分离,通过在运行时动态地将这些关注点织入到目标对象中,实现代码的复用和模块化。比如,在一个电商系统中,我们可以通过 AOP 统一为所有业务方法添加日志记录功能,而不需要在每个业务方法中重复编写日志代码。
Spring 核心概念
控制反转(IoC)
控制反转(Inversion of Control,IoC)是 Spring 框架的核心思想之一。在传统的 Java 开发中,对象的创建和依赖关系的管理通常由开发者在代码中手动完成。例如,在一个简单的用户管理系统中,如果我们有一个UserService类依赖于UserDao类,在传统方式下,我们会在UserService类中通过new关键字来创建UserDao的实例,如下所示:
public class UserService {
private UserDao userDao = new UserDao();
public void saveUser(User user) {
userDao.save(user);
}
}
这样做会导致UserService与UserDao之间的耦合度非常高,如果需要更换UserDao的实现类,就需要修改UserService的代码。
而在 Spring 中,通过 IoC 机制,对象的创建和管理不再由应用程序自身负责,而是交给了 Spring 容器。Spring 容器负责创建对象、管理对象的生命周期以及维护对象之间的依赖关系。我们只需要在配置文件或使用注解告诉 Spring 容器需要创建哪些对象以及它们之间的依赖关系即可。这样一来,对象之间的耦合度大大降低,代码的可维护性和可扩展性得到了显著提高。例如,使用 Spring 的配置文件来管理UserService和UserDao的依赖关系:
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
在上述配置中,Spring 容器会根据配置文件创建UserDao和UserService的实例,并将UserDao的实例注入到UserService中,从而实现了对象之间依赖关系的管理。
依赖注入(DI)
依赖注入(Dependency Injection,DI)是实现 IoC 的具体方式。它的核心思想是将对象所依赖的外部资源(如其他对象、配置信息等)通过某种方式(如构造函数、Setter 方法等)注入到对象中,而不是由对象自己去创建或查找这些依赖。
Spring 提供了多种依赖注入的实现方式,其中最常用的是构造器注入和属性注入。
构造器注入:通过构造函数来实现依赖注入。当一个类的构造函数参数包含其他依赖对象时,Spring 容器会在创建该类的实例时,将对应的依赖对象作为参数传递给构造函数。例如:
public class OrderService {
private OrderDao orderDao;
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void placeOrder(Order order) {
orderDao.saveOrder(order);
}
}
在 Spring 配置文件中,可以这样配置:
<bean id="orderDao" class="com.example.dao.OrderDaoImpl"/>
<bean id="orderService" class="com.example.service.OrderService">
<constructor-arg ref="orderDao"/>
</bean>
属性注入:通过 Setter 方法来实现依赖注入。Spring 容器会在创建对象后,调用对象的 Setter 方法将依赖对象注入到对象中。例如:
public class ProductService {
private ProductDao productDao;
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void addProduct(Product product) {
productDao.saveProduct(product);
}
}
在 Spring 配置文件中,配置如下:
<bean id="productDao" class="com.example.dao.ProductDaoImpl"/>
<bean id="productService" class="com.example.service.ProductService">
<property name="productDao" ref="productDao"/>
</bean>
面向切面编程(AOP)
面向切面编程(Aspect Oriented Programming,AOP)是一种与面向对象编程(OOP)相辅相成的编程思想。OOP 主要关注的是将业务逻辑封装成一个个独立的对象,通过对象之间的交互来完成业务功能;而 AOP 则是将那些与业务逻辑无关,但又横切于多个业务模块的功能(如日志记录、事务管理、权限控制等)抽取出来,形成独立的切面(Aspect),然后在运行时将这些切面动态地织入到目标对象的方法执行过程中。
在 AOP 中,有几个重要的概念:
- 切面(Aspect):切面是一个关注点的模块化,它将横切关注点(如日志记录、事务管理等)封装成一个独立的模块。例如,我们可以将日志记录功能封装成一个切面。
- 连接点(Joinpoint):连接点是程序执行过程中的某个特定位置,如方法调用、异常抛出等。在 Spring 中,连接点主要指的是方法调用。
- 切入点(Pointcut):切入点是对连接点的进一步筛选,它定义了哪些连接点会被织入切面。例如,我们可以定义一个切入点,只对业务层中所有以save开头的方法进行日志记录。
- 增强(Advice):增强是在切入点处执行的具体操作,它定义了在连接点处要执行的额外功能。常见的增强类型有前置增强(Before Advice)、后置增强(After Advice)、环绕增强(Around Advice)、异常增强(After Throwing Advice)和最终增强(After Finally Advice)。例如,前置增强可以在方法执行前记录日志,后置增强可以在方法执行后记录日志。
通过 AOP,我们可以将业务逻辑和系统服务进行分离,提高代码的复用性和可维护性。例如,在一个电商系统中,我们可以通过 AOP 统一为所有业务方法添加日志记录功能,而不需要在每个业务方法中重复编写日志代码。在 Spring 中,可以通过 XML 配置或注解的方式来实现 AOP。下面是一个使用注解实现 AOP 的简单示例:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method execution: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed();
System.out.println("After method execution: " + joinPoint.getSignature().getName());
return result;
}
}
在上述示例中,LoggingAspect是一个切面类,@Aspect注解表明它是一个切面,@Component注解将其纳入 Spring 容器的管理。@Around注解定义了一个环绕增强,切入点表达式execution(* com.example.service.*.*(..))表示对com.example.service包下所有类的所有方法进行增强。在方法执行前后,会分别打印日志信息。
Spring 入门案例
环境搭建
在开始 Spring 之旅前,我们需要搭建好开发环境。
必备环境:
- JDK:建议使用 JDK 1.8 及以上版本,确保 Java 开发环境的稳定运行。例如,在企业级项目中,JDK 1.8 已经被广泛应用,其强大的功能和稳定性为 Spring 开发提供了坚实的基础。
- Maven:Maven 是项目管理和构建工具,能帮助我们轻松管理项目依赖和构建过程。推荐使用 3.6.3 及以上版本。Maven 通过简单的配置文件(pom.xml),就能让我们快速引入各种依赖库,避免了手动下载和管理 jar 包的繁琐过程。
- 开发工具:可以选择 IntelliJ IDEA、Eclipse 等流行的 Java 开发工具,它们提供了丰富的插件和便捷的开发功能,能大大提高开发效率。以 IntelliJ IDEA 为例,它对 Spring 的支持非常友好,不仅能自动提示 Spring 相关的代码,还提供了强大的代码导航和调试功能。
配置 Maven 项目:
- 打开 IntelliJ IDEA,创建一个新的 Maven 项目。在创建过程中,按照向导提示,填写项目的基本信息,如 GroupId、ArtifactId 和 Version 等。这些信息将唯一标识我们的项目,例如,GroupId 可以设置为公司或组织的域名倒写,ArtifactId 则是项目的名称,Version 用于标识项目的版本号。
- 创建完成后,在项目的 pom.xml 文件中引入 Spring 依赖。我们可以在Maven 仓库中搜索所需的 Spring 依赖,然后将其添加到 pom.xml 文件中。例如,添加 Spring Context 依赖:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.10.RELEASE</version> </dependency> </dependencies>
在上述配置中,<groupId>org.springframework</groupId>表示 Spring 的组织 ID,<artifactId>spring-context</artifactId>表示 Spring 上下文模块的工件 ID,<version>5.3.10.RELEASE</version>表示版本号。引入spring-context依赖后,Spring 的基础依赖就被引入了项目中 。
第一个 Spring 程序
- 创建 Java 类:在项目的src/main/java目录下创建一个简单的 Java 类,例如HelloWorld类:
public class HelloWorld { private String message; public void setMessage(String message) { this.message = message; } public void sayHello() { System.out.println("Message: " + message); } }
在这个类中,我们定义了一个message属性,并提供了setMessage方法用于设置属性值,以及sayHello方法用于输出消息。2. 创建 Spring 配置文件:在src/main/resources目录下创建 Spring 配置文件
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="helloWorld" class="com.example.HelloWorld">
<property name="message" value="Hello, Spring!"/>
</bean>
</beans>
在上述配置中,<bean>标签用于配置 Spring 容器管理的对象,id属性为对象的唯一标识,class属性指定对象的全限定类名。<property>标签用于设置对象的属性,name属性指定属性名,value属性指定属性值。通过这个配置文件,我们告诉 Spring 容器创建一个HelloWorld类的实例,并将message属性设置为Hello, Spring!。
运行和测试
- 运行 Spring 程序:在项目中创建一个测试类,例如HelloWorldTest类:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloWorldTest { public static void main(String[] args) { // 加载Spring配置文件,创建Spring容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 从Spring容器中获取HelloWorld对象 HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld"); // 调用HelloWorld对象的sayHello方法 helloWorld.sayHello(); } }
在上述代码中,首先通过ClassPathXmlApplicationContext类加载 Spring 配置文件applicationContext.xml,创建 Spring 容器。然后使用context.getBean("helloWorld")方法从容器中获取HelloWorld对象,最后调用helloWorld.sayHello()方法输出消息。
- 使用 JUnit 进行测试:为了更方便地测试 Spring 程序,我们可以使用 JUnit 测试框架。首先在 pom.xml 文件中添加 JUnit 依赖:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>
然后创建一个 JUnit 测试类,例如HelloWorldJUnitTest类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.junit.Assert.*;
public class HelloWorldJUnitTest {
@Test
public void testHelloWorld() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
assertNotNull(helloWorld);
helloWorld.sayHello();
}
}
在上述测试类中,使用@Test注解标记测试方法testHelloWorld。在测试方法中,首先创建 Spring 容器,获取HelloWorld对象,然后使用assertNotNull方法断言对象不为空,最后调用sayHello方法输出消息。运行这个测试类,如果一切正常,我们将在控制台看到Message: Hello, Spring!的输出,这表明 Spring 已经成功创建和管理HelloWorld对象,并且对象的属性也被正确设置 。
基于 XML 管理 Bean
环境准备
在使用 XML 配置 Bean 之前,需要确保项目中引入了 Spring 的相关依赖。如果使用 Maven 构建项目,在pom.xml文件中添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10.RELEASE</version>
</dependency>
</dependencies>
引入依赖后,在src/main/resources目录下创建 Spring 的配置文件,通常命名为applicationContext.xml。这个文件将用于配置 Spring 容器管理的 Bean 。
获取 Bean 方式
在 Spring 容器中获取 Bean 有多种方式,其中最常用的是根据 id 获取和根据类型获取。
根据 id 获取:通过ApplicationContext的getBean(String name)方法,传入 Bean 的 id 来获取对应的 Bean 实例。例如:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
在上述代码中,"helloWorld"是配置文件中 Bean 的 id,通过context.getBean("helloWorld")从 Spring 容器中获取HelloWorld类型的 Bean 实例。
根据类型获取:使用ApplicationContext的getBean(Class<T> requiredType)方法,传入 Bean 的类型来获取对应的 Bean 实例。例如:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = context.getBean(HelloWorld.class);
这种方式要求 Spring 容器中该类型的 Bean 是唯一的,否则会抛出NoUniqueBeanDefinitionException异常。
此外,还可以同时根据 id 和类型获取 Bean,使用ApplicationContext的getBean(String name, Class<T> requiredType)方法 :
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
依赖注入方式
基于 setter 依赖注入
基于 setter 的依赖注入是通过调用 Bean 的 setter 方法来实现依赖注入的。在 Spring 配置文件中,使用<property>标签来配置依赖注入。
例如,假设有一个UserService类依赖于UserDao类,UserService类的定义如下:
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(User user) {
userDao.save(user);
}
}
在 Spring 配置文件applicationContext.xml中,配置如下:
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
在上述配置中,<property>标签的name属性指定要注入的属性名,ref属性指定要注入的 Bean 的 id。通过这种方式,Spring 容器在创建userService实例时,会调用setUserDao方法将userDao实例注入到userService中 。
基于构造器依赖注入
基于构造器的依赖注入是通过调用 Bean 的构造函数来实现依赖注入的。在 Spring 配置文件中,使用<constructor-arg>标签来配置构造器参数。
例如,假设有一个OrderService类,其构造函数接受一个OrderDao类型的参数,OrderService类的定义如下:
public class OrderService {
private OrderDao orderDao;
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void placeOrder(Order order) {
orderDao.saveOrder(order);
}
}
在 Spring 配置文件applicationContext.xml中,配置如下:
<bean id="orderDao" class="com.example.dao.OrderDaoImpl"/>
<bean id="orderService" class="com.example.service.OrderService">
<constructor-arg ref="orderDao"/>
</bean>
在上述配置中,<constructor-arg>标签的ref属性指定要注入的 Bean 的 id,Spring 容器在创建orderService实例时,会调用对应的构造函数将orderDao实例注入到orderService中。<constructor-arg>标签还可以通过index属性指定参数的位置索引(从 0 开始),通过type属性指定参数的类型,通过value属性直接赋值 。
特殊值处理注入
字面量赋值
在 Spring 配置文件中,可以使用<property>标签的value属性为 Bean 的属性赋值字面量。例如:
<bean id="person" class="com.example.Person">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
上述配置中,为Person类的name属性赋值为张三,age属性赋值为20。
null 值处理
当需要为 Bean 的属性赋值为null时,可以使用<null/>标签。例如:
<bean id="book" class="com.example.Book">
<property name="author">
<null/>
</property>
</bean>
在上述配置中,book的author属性被赋值为null 。
xml 实体和 CDATA 区的使用
当属性值中包含特殊字符(如<、>、&等)时,如果直接使用这些字符,可能会导致 XML 解析错误。此时可以使用 XML 实体或 CDATA 区来处理。
使用 XML 实体:例如,要表示&字符,可以使用&实体。
<bean id="message" class="com.example.Message">
<property name="content" value="这是一个包含&符号的消息"/>
</bean>
使用 CDATA 区:CDATA 区中的内容会被 XML 解析器原样解析,不会对其中的特殊字符进行转义。例如:
<bean id="message" class="com.example.Message">
<property name="content">
<value><![CDATA[这是一个包含<特殊字符>的消息]]></value>
</property>
</bean>
在上述配置中,content属性的值包含<特殊字符>,使用 CDATA 区确保这些特殊字符不会被 XML 解析器错误解析 。
对象类型属性注入
引用外部 bean
在一个 Bean 中引用另一个外部 Bean 是非常常见的场景。例如,UserService类依赖于UserDao类,UserService类中的userDao属性需要引用外部定义的UserDao Bean。在 Spring 配置文件中,配置如下:
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
在上述配置中,userService的userDao属性通过ref属性引用了userDao Bean,这样 Spring 容器在创建userService实例时,会将userDao实例注入到userService中 。
内部 bean
内部 Bean 是在另一个 Bean 的配置中定义的 Bean,它只能在外部 Bean 中使用,不能被外部直接引用。例如:
<bean id="outerBean" class="com.example.OuterBean">
<property name="innerBean">
<bean id="innerBean" class="com.example.InnerBean"/>
</property>
</bean>
在上述配置中,innerBean是定义在outerBean内部的 Bean,它只能被outerBean使用,其他 Bean 无法直接引用innerBean 。
级联属性赋值
级联属性赋值允许通过一个 Bean 的属性来访问和设置另一个 Bean 的属性。例如,假设有Address类和Person类,Person类包含一个Address类型的属性,并且Address类有city属性。
public class Address {
private String city;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
public class Person {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
在 Spring 配置文件中,可以通过级联属性赋值来设置Person的Address的city属性:
<bean id="address" class="com.example.Address">
<property name="city" value="北京"/>
</bean>
<bean id="person" class="com.example.Person">
<property name="address" ref="address"/>
<!-- 级联属性赋值 -->
<property name="address.city" value="上海"/>
</bean>
在上述配置中,首先通过ref属性将address Bean 注入到person的address属性中,然后通过address.city的方式直接设置address的city属性为上海 。
集合类型属性注入
List 集合
在 Spring 配置文件中,可以使用<list>标签来注入 List 集合类型的属性。例如,假设有一个CourseService类,它有一个List<String>类型的courses属性,用于存储课程名称。
public class CourseService {
private List<String> courses;
public void setCourses(List<String> courses) {
this.courses = courses;
}
public void printCourses() {
for (String course : courses) {
System.out.println(course);
}
}
}
在 Spring 配置文件applicationContext.xml中,配置如下:
<bean id="courseService" class="com.example.CourseService">
<property name="courses">
<list>
<value>Java</value>
<value>Spring</value>
<value>MySQL</value>
</list>
</property>
</bean>
在上述配置中,<list>标签内的<value>标签分别表示 List 集合中的元素,Spring 容器在创建courseService实例时,会将这些元素注入到courses属性中 。
Map 集合
使用<map>标签可以注入 Map 集合类型的属性。例如,假设有一个Student类,它有一个Map<String, Integer>类型的scores属性,用于存储学生的课程成绩。
public class Student {
private Map<String, Integer> scores;
public void setScores(Map<String, Integer> scores) {
this.scores = scores;
}
public void printScores() {
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
在 Spring 配置文件applicationContext.xml中,配置如下:
<bean id="student" class="com.example.Student">
<property name="scores">
<map>
<entry key="数学" value="90"/>
<entry key="语文" value="85"/>
<entry key="英语" value="95"/>
</map>
</property>
</bean>
在上述配置中,<map>标签内的<entry>标签用于定义 Map 集合中的键值对,key属性表示键,value属性表示值 。
引用集合类型 Bean
当集合中的元素是其他 Bean 时,可以使用<ref>标签来引用。例如,假设有Book类和Library类,Library类有一个List<Book>类型的books属性。
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
public class Library {
private List<Book> books;
public void setBooks(List<Book> books) {
this.books = books;
}
public void printBooks() {
for (Book book : books) {
System.out.println(book.getTitle());
}
}
}
在 Spring 配置文件applicationContext.xml中,配置如下:
<bean id="book1" class="com.example.Book">
<property name="title" value="《Spring实战》"/>
</bean>
<bean id="book2" class="com.example.Book">
<property name="title" value="《Effective Java》"/>
</bean>
<bean id="library" class="com.example.Library">
<property name="books">
<list>
<ref bean="book1"/>
<ref bean="book2"/>
</list>
</property>
</bean>
在上述配置中,<list>标签内的<ref>标签分别引用了book1和book2 Bean,将这两个 Bean 注入到library的books属性中 。
p 命名空间
p 命名空间是 Spring 提供的一种简化 Bean 属性配置的方式。使用 p 命名空间可以在<bean>标签内直接通过属性名来设置 Bean 的属性,而不需要使用<property>标签。
首先,需要在 Spring 配置文件的<beans>标签中引入 p 命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
然后,就可以使用 p 命名空间来配置 Bean 的属性。例如,假设有一个Person类:
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在 Spring 配置文件中,可以使用 p 命名空间配置如下:
<bean id="person" class="com.example.Person" p:name="李四" p:age="25"/>
上述配置等价于使用<property>标签的配置:
<bean id="person" class="com.example.Person">
<property name="name" value="李四"/>
<property name="age" value="25"/>
</bean>
使用 p 命名空间可以使配置更加简洁明了 。
引入外部属性文件
在实际开发中,通常会将一些配置信息(如数据库连接信息、系统参数等)外部化,存储在属性文件中,这样便于管理和维护。Spring 提供了引入外部属性文件的功能。
首先,在src/main/resources目录下创建属性文件,例如config.properties,内容如下:
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=123456
然后,在 Spring 配置文件中引入这个属性文件:
<context:property-placeholder location="classpath:config.properties"/>
上述配置中,<context:property-placeholder>标签用于引入属性文件,location属性指定属性文件的位置,classpath:表示从类路径下查找文件。
引入属性文件后,就可以在 Spring 配置中使用属性文件中的值。例如,配置数据源:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
在上述配置中,通过${}占位符的方式引用属性文件中的值,Spring 容器在解析配置文件时,会将占位符替换为实际的值 。
bean 的作用域
Spring 中 Bean 的作用域定义了 Bean 在 Spring 容器中的生命周期和实例数量。Spring 支持多种作用域:
- singleton:单例作用域,这是 Spring 的默认作用域。在这种作用域下,Spring 容器只会创建一个 Bean 实例,所有对该 Bean 的请求都会返回同一个实例。例如,在一个电商系统中,用户管理模块的UserService Bean 通常可以配置为单例作用域,因为整个系统只需要一个UserService实例来处理用户相关的业务逻辑,这样可以节省系统资源,提高性能。
- prototype:原型作用域,每次对该 Bean 的请求都会创建一个新的实例。例如,在一个在线考试系统中,每个考生的答题记录可以使用一个AnswerRecord Bean 来记录,由于每个考生的答题情况不同,所以AnswerRecord Bean 适合配置为原型作用域,每次创建一个新的实例来记录不同考生的答题信息。
- request:请求作用域,在一次 HTTP 请求中,一个 Bean 定义对应一个实例。该作用域仅在基于 Web 的 Spring ApplicationContext 情形下有效。例如,在一个 Web 应用中,处理用户登录请求的 `Login
基于注解管理 Bean
创建子工程
为了深入学习基于注解的 Bean 管理,我们首先创建一个新的 Maven 子工程。以 IDEA 为例,打开 IDEA 后,选择File -> New -> Project,在弹出的窗口中选择Maven,然后点击Next。在后续步骤中,填写 GroupId(如com.example)、ArtifactId(如spring-annotation-demo)等项目信息,最后点击Finish完成项目创建。
创建完成后,在pom.xml文件中添加 Spring 相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10.RELEASE</version>
</dependency>
</dependencies>
开启组件扫描
在 Spring 中,使用注解定义 Bean 的前提是要开启组件扫描功能,这样 Spring 容器才能扫描到被注解标记的类并将其注册为 Bean。
如果使用 XML 配置,在applicationContext.xml文件中添加如下配置:
<context:component-scan base-package="com.example"/>
上述配置表示 Spring 容器会扫描com.example包及其子包下的所有类。
如果使用 Java 配置类,可以创建一个配置类并添加@ComponentScan注解:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.example")
public class AppConfig {
}
在上述代码中,@Configuration注解表示这是一个配置类,@ComponentScan注解指定了扫描的包路径 。
使用注解定义 Bean
Spring 提供了多个注解用于定义 Bean,这些注解都具有特定的语义,有助于提高代码的可读性和可维护性。
- @Component:这是一个通用的组件注解,用于标记一个类为 Spring 管理的组件。例如:
import org.springframework.stereotype.Component; @Component public class UserService { // 业务逻辑代码 }
在上述示例中,UserService类被@Component注解标记,Spring 容器会自动扫描到该类并将其注册为一个 Bean,默认的 Bean 名称为类名首字母小写,即userService。
- @Controller:用于标记 Spring MVC 中的控制器类,主要处理用户请求并返回视图或数据。例如:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class UserController { @GetMapping("/user") @ResponseBody public String getUser() { return "This is a user"; } }
在 Spring MVC 应用中,@Controller注解会将控制器类纳入 Spring 容器管理,@GetMapping注解用于映射 HTTP GET 请求到getUser方法,@ResponseBody注解表示该方法的返回值会直接作为响应体返回给客户端 。
- @Service:用于标记业务层的服务类,封装了具体的业务逻辑。例如:
import org.springframework.stereotype.Service; @Service public class OrderService { // 订单相关业务逻辑代码 }
在实际开发中,OrderService类通常会依赖其他组件(如OrderDao)来完成数据库操作等业务功能 。
- @Repository:用于标记数据访问层(DAO)类,主要负责与数据库进行交互。例如:
import org.springframework.stereotype.Repository; @Repository public class ProductDao { // 数据库操作代码 }
@Repository注解不仅将ProductDao类注册为 Bean,还提供了数据访问异常的处理机制,例如将 JDBC 异常转换为 Spring 的DataAccessException,方便上层业务统一处理异常 。
@Autowired 注入
@Autowired注解是 Spring 提供的用于实现依赖注入的重要注解,它基于类型进行自动装配。
属性注入:在需要注入依赖的属性上直接使用@Autowired注解。例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User getUserById(int id) {
return userDao.findById(id);
}
}
在上述示例中,UserServiceImpl类依赖于UserDao,通过在userDao属性上添加@Autowired注解,Spring 容器会自动将UserDao类型的 Bean 注入到userDao属性中 。
set 注入:通过在 Setter 方法上使用@Autowired注解来实现依赖注入。例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User getUserById(int id) {
return userDao.findById(id);
}
}
在这种方式下,Spring 容器会在创建UserServiceImpl实例后,调用setUserDao方法将UserDao实例注入 。
构造方法注入:在构造方法上使用@Autowired注解。例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private final UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User getUserById(int id) {
return userDao.findById(id);
}
}
使用构造方法注入时,如果当前类只有一个构造方法,@Autowired注解可以省略。这种方式能确保依赖的UserDao在UserServiceImpl实例创建时就被注入,并且可以将userDao属性声明为final,提高代码的安全性和不可变性 。
@Resource 注入
@Resource注解是 Java EE 提供的用于依赖注入的注解,它既可以按名称注入,也可以按类型注入。
与@Autowired不同,@Resource默认按名称注入,如果找不到与名称匹配的 Bean,才会按类型注入。例如:
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
private UserDao userDao;
@Override
public User getUserById(int id) {
return userDao.findById(id);
}
}
在上述示例中,通过@Resource注解的name属性指定要注入的 Bean 名称为userDao,如果 Spring 容器中存在名为userDao的 Bean,则会将其注入到userDao属性中。如果不指定name属性,@Resource会尝试根据属性名查找匹配的 Bean 。
@Resource和@Autowired的主要区别在于:
- 来源不同:@Autowired是 Spring 框架特有的注解,而@Resource是 Java EE 标准注解,在非 Spring 环境中也可以使用。
- 注入方式不同:@Autowired默认按类型注入,@Resource默认按名称注入。
- 功能特性不同:@Autowired更侧重于 Spring 框架内的依赖注入,配合@Qualifier等注解可以更灵活地处理复杂的依赖关系;@Resource在处理与 Java EE 其他技术集成时更为方便 。
Spring 全注解开发
在 Spring 中,除了使用 XML 配置文件外,还可以完全使用注解进行配置,实现零 XML 配置的开发方式。
首先,创建一个配置类,使用@Configuration注解标记它为一个配置类,并在类中使用@Bean注解定义 Bean。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
在上述配置类中,@Bean注解标记的方法返回的对象会被注册为 Spring 容器中的 Bean,方法名即为 Bean 的名称。
在测试类中,可以通过AnnotationConfigApplicationContext来加载配置类并获取 Bean:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
User user = userService.getUserById(1);
System.out.println(user);
}
}
通过这种方式,我们可以完全摆脱 XML 配置文件,仅使用注解来完成 Spring 的配置和 Bean 管理,使代码更加简洁、易读,并且便于维护和扩展 。
结语
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~