文章目录
- Spring框架
- 简介
- 创建Spring项目
- 理解IOC和DI:
- IOC控制反转
- 示例
- pom.xml
- Person.java
- applicationContext.xml
- PersonTest.java
- DI依赖注入
- 传统的方式
- GreetingService.java
- GreetingServiceImpl.java
- GreetingTest.java
- 使用DI依赖注入
- GreetingService.java
- GreetingServiceImpl.java
- AppConfig.java
- AppTest.java
Spring框架
spring官网;https://spring.io/
简介
Spring框架是一个开源的Java应用程序框架,它提供了一种全面的编程和配置模型,可用于构建现代化的基于Java的企业级应用程序。
Spring框架提供的核心功能包括控制反转(IoC)、依赖注入(DI)、面向切面编程(AOP)、数据访问、事务管理、Web应用程序开发等方面。它可以与其他技术和框架无缝集成,如Hibernate、MyBatis、Struts、Servlet API,SpringMVC等。
使用Spring框架可以简化Java应用程序的开发过程,减少代码量,提高代码的可重用性和可维护性。它也提供了各种扩展和插件,以满足特定场景下的需求。
Spring框架的设计思想是基于松耦合、可扩展和可重用的原则,使得Java开发者可以更专注于业务逻辑的实现,而不必关注底层技术的细节。它已成为Java企业级应用程序开发中最流行和广泛使用的框架之一。
创建Spring项目
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- xml声明,指定版本和编码 -->
<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"><!-- pom文件的根元素 -->
<!-- 指定pom模型的版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 项目所属组织或公司的唯一标识符 -->
<groupId>com.sin</groupId>
<!-- 项目的唯一标识符 -->
<artifactId>spring_demo</artifactId>
<!-- 项目的版本号 -->
<version>1.0-SNAPSHOT</version>
<!-- 固定项目依赖版本 -->
<properties>
<!-- 指定源代码的字符编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 指定使用的Spring框架版本 -->
<spring.version>5.3.13</spring.version>
</properties>
<dependencies>
<!-- spring-core库的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-context库的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-beans库的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
理解IOC和DI:
在传统的程序
User.java
package com.sin.pojo;
/**
* @createTime 2023/12/29 16:22
* @createAuthor SIN
* @use
*/
public class User {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
UserDao.java
package com.sin.Dao;
import com.sin.pojo.User;
/**
* @createTime 2023/12/29 16:22
* @createAuthor SIN
* @use
*/
public class UserDao {
public User getUser(int userId) {
// 模拟从数据库中获取用户信息的逻辑
// 这里假设直接返回一个User对象
User user = new User();
user.setId(userId);
user.setName("张三");
return user;
}
}
UserService.java
package com.sin.service;
import com.sin.Dao.UserDao;
import com.sin.pojo.User;
/**
* @createTime 2023/12/29 16:21
* @createAuthor SIN
* @use
*/
public class UserService {
private UserDao userDao;
public UserService() {
userDao = new UserDao();
}
/**
* 该方法允许外部设置UserDao对象,这种方式称之为依赖注入
*
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUserInfo(int userId) {
// 通过UserDao获取用户信息
User user = userDao.getUser(userId);
// 处理用户信息
System.out.println("用户ID: " + user.getId());
System.out.println("用户姓名: " + user.getName());
}
}
在编写过程中发现:
- 需要获取数据时,需要创建
UserSercie
对象,每次创建UserService
对象都会创建出一新的UserDao
对象,这样做会导致内存的浪费,因为我们可能只需要一个UserDao
对象,而不是每次都创建一个新的对象。- 这种实现方式还存在对象生命周期的问题。如果
UserService
对象被销毁了,那么与之关联的UserDao
对象也将被销毁,这可能会导致数据丢失或其他意想不到的问题。- 高耦合性:
UserService
和UserDao
之间的关系非常紧密,UserService
需要知道如何创建UserDao
对象,并且需要在方法中直接调用UserDao
的方法。这样做会使得代码高度耦合,难以维护。- 代码重复:如果有多个类需要使用
UserDao
对象进行数据访问,那么就需要在每个类中都写出类似的代码,这将导致大量重复的代码,增加了代码的冗余。- 可测试性差:在单元测试时,我们希望能够对
UserService
进行测试,而不是测试UserDao
的实现。但是,由于UserService
依赖于UserDao
,因此需要在测试中实例化UserDao
对象并设置到UserService
中,这就使得测试变得困难。- 不易扩展:如果我们需要更改
UserDao
的实现方式,比如从数据库中获取用户信息改为从Web服务中获取,那么就需要修改UserService
的代码,这样做会使得代码更加脆弱,不易扩展。
这些问题可以通过使用IOC容器和DI框架来解决。在Spring框架中,我们可以通过配置文件或注解来告诉Spring容器如何创建对象并管理对象之间的依赖关系。Spring容器会在需要时自动创建对象,并确保每个对象只被创建一次,并且可以在整个应用程序中共享。这样做不仅可以提高应用程序的性能,还可以减少内存消耗和对象生命周期的问题。
IOC控制反转
控制反转(Inversion of Control,简称IoC)是一种软件设计原则,也是面向对象编程中的一种设计模式。它的核心思想是将对象的创建和依赖关系的管理交给容器来完成,而不是由对象自身来创建和管理其依赖的对象。
传统的程序设计中,对象之间的依赖关系常常通过对象自身来创建和管理。例如,在一个类中直接使用new
关键字来创建其他类的实例,这样就导致了类与类之间的紧耦合,使得代码难以维护、扩展和测试。
而采用IoC的方式,对象之间的依赖关系被反转了。具体来说,IoC通过引入一个容
器(如Spring框架的ApplicationContext),容器负责创建对象并管理对象之间的依赖关系。在IoC容器中,我们可以配置对象的创建方式、依赖关系以及其他属性,然后由容器来实例化对象,并将所需的依赖注入到对象中。
IoC的优点包括:
-
解耦:IoC可以降低对象之间的耦合度,使得代码更加灵活、可维护和可扩展。
-
便于测试:由于依赖关系由容器管理,我们可以很容易地对对象进行单元测试,而不需要真正地创建依赖对象。
-
可以更好地支持面向接口编程:通过IoC容器可以轻松地切换不同的实现类,从而支持面向接口编程的设计原则。
-
提高了代码的可读性和可维护性:通过配置文件或注解来管理对象的创建和依赖关系,使得代码的逻辑更加清晰、易于理解和维护。
示例
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
spring-context是Spring的核心模块之一,提供了Spring应用程序上下文的基础设施支持,也是Spring框架中最重要的部分之一。它负责管理Spring应用程序中所有bean的声明周期,并提供了依赖注入和面向切面编程等重要的特性。
主要提供的功能:
- ApplicationContext接口:这是Spring应用程序上下文的核心接口,定义了获取bean、注册bean、发布事件等操作的API。
- BeanFactory接口:定义了Spring IoC容器的基本功能,这是Spring应用程序上下文的基础。
- AOP支持:Spring提供了强大的AOP支持,可以在应用程序中轻松实现面向切面编程。
- 事件处理:Spring提供了事件机制,允许应用程序中的各个组件之间进行通信和协作。
- SpEL表达式语言:Spring提供了一种表达式语言,允许开发者使用类似于EL的语法来处理复杂的表达式和逻辑。
- 数据绑定:Spring提供了一种灵活的数据绑定机制,可以将请求参数、属性文件、XML文档等各种数据源与Java对象进行绑定。
Person.java
package com.sin.pojo;
/**
* @createTime 2024/1/2 8:44
* @createAuthor SIN
* @use
*/
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
applicationContext.xml
注意:文件位置必须在src/main/resources目录下。这是默认的资源目录,Spring会自动将其包含在类路径中。
<?xml version="1.0" encoding="UTF-8"?><!-- XML声明,xml版本和编码格式 -->
<!-- spring配置文件起点 -->
<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 : bean名称
class :SpringBean对应的Java对象
-->
<bean id="person" class="com.sin.pojo.Person">
<!-- 对bean的属性进行赋值 -->
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</beans>
bean
: 是指Spring容器管理的Java对象,是Spring框架的核心概念之一。在Spring中Bean是应用程序的基本构建模块,代表着一个可重用的组件,它可以是任何普通的Java对象,也可以是由Spring容易创建和管理的特殊对象。这些对象可以通过Spring容器进行创建,配置和管理,从而实现依赖注入和面向切面编程等功能。
为什么使用Spring配置文件来赋值?
可以将应用程序中各个组件之间的依赖关系和属性值都统一管理,从而实现松耦合的设计和高度可配置性。
PersonTest.java
package com.sin.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @createTime 2024/1/2 8:48
* @createAuthor SIN
* @use 通过Spring容器获取并使用一个被管理的Bean
*/
public class PersonTest {
public static void main(String[] args) {
/**
* 创建ApplicationContext对象,
* ApplicationContext : 是Spring的顶层接口,用于表示整个应用程序的上下文环境
* ClassPathXmlApplicationContext :通过指定文件来获取spring容器
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
/**
* 从容器中获取指定bean
* getBean :返回类型为Object,我们需要将其为具体类型,
*/
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
}
流程图
在整个过程中发现不用创建Person对象了,不需要new Person();减少了内存空间的占用。整个过程将对象的创建交给Spring来管理。
DI依赖注入
依赖注入(Dependency Injection,简称DI)是控制反转(IoC)的一个具体实现方式。它是指通过外部将对象的依赖关系注入到对象中,而不是由对象自身来创建和管理依赖的对象。
在传统的程序设计中,对象通常需要自己创建和管理其所依赖的其他对象。这种方式导致了对象之间的紧耦合,使得代码难以测试、扩展和维护。而采用依赖注入,我们将对象的依赖关系交给容器来管理,对象只需声明自己需要哪些依赖,容器则负责将相应的依赖注入到对象中。
依赖注入可以通过以下几种方式实现:
-
构造函数注入(Constructor Injection):通过对象的构造函数来传递依赖。在对象创建时,容器会根据构造函数的参数类型来自动注入所需的依赖。
-
Setter方法注入(Setter Injection):通过对象的setter方法来注入依赖。容器会调用对象的setter方法,并将相应的依赖作为参数传入。
-
接口注入(Interface Injection):通过对象实现一个特定的接口,该接口定义了注入依赖的方法。容器会在对象创建后,调用接口方法并注入相应的依赖。
依赖注入的优点包括:
-
解耦:通过将对象的依赖关系交给容器管理,实现了对象之间的解耦,使得代码更加灵活、可维护和可扩展。
-
可测试性:由于对象的依赖关系由容器注入,我们可以很容易地使用模拟对象来进行单元测试,而不需要真实的依赖对象。
-
灵活性:依赖注入使得切换和替换不同的依赖实现变得容易,从而支持面向接口编程和可插拔的架构设计。
传统的方式
GreetingService.java
package com.sin.service;
/**
* @createTime 2024/1/2 10:52
* @createAuthor SIN
* @use
*/
public interface GreetingService {
public void sayHello();
}
GreetingServiceImpl.java
package com.sin.service.impl;
import com.sin.service.GreetingService;
import java.awt.*;
/**
* @createTime 2024/1/2 10:52
* @createAuthor SIN
* @use
*/
public class GreetingServiceImpl implements GreetingService {
@Override
public void sayHello() {
System.out.println("hello,world!");
}
}
GreetingTest.java
package com.sin.test;
import com.sin.service.impl.GreetingServiceImpl;
import org.junit.Test;
/**
* @createTime 2024/1/2 10:53
* @createAuthor SIN
* @use
*/
public class GreetingTest {
@Test
public void testGreeting(){
GreetingServiceImpl greetingService = new GreetingServiceImpl();
greetingService.sayHello();
}
}
使用DI依赖注入
GreetingService.java
package com.sin.service;
/**
* @createTime 2024/1/2 10:52
* @createAuthor SIN
* @use
*/
public interface GreetingService {
public void sayHello();
}
GreetingServiceImpl.java
package com.sin.service.impl;
import com.sin.service.GreetingService;
import java.awt.*;
/**
* @createTime 2024/1/2 10:52
* @createAuthor SIN
* @use
*/
public class GreetingServiceImpl implements GreetingService {
@Override
public void sayHello() {
System.out.println("hello,world!");
}
}
AppConfig.java
package com.sin.config;
import com.sin.service.GreetingService;
import com.sin.service.impl.GreetingServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @createTime 2024/1/2 10:56
* @createAuthor SIN
* @use
*/
@Configuration // 标记为一个配置类,该类将提供Bean的定义和配置,
public class AppConfig {
/**
* 定义一个Bean,方法名getGreetingService将作为Bean的名称,默认为方法名(也可以通过name属性指定其他名称)。
* @return 创建GreetingServiceImpl对象, 该对象将被Spring容器管理,
*/
@Bean
public GreetingService getGreetingService() {
return new GreetingServiceImpl();
}
}
AppTest.java
package com.sin.test;
import com.sin.config.AppConfig;
import com.sin.service.GreetingService;
import com.sin.service.impl.GreetingServiceImpl;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @createTime 2024/1/2 10:57
* @createAuthor SIN
* @use
*/
public class AppTest {
@Test
public void test(){
/**
* 创建spring容器并加载配置类
* new AnnotationConfigApplicationContext(AppConfig.class)创建对象时加载配置类
*/
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取依赖注入的Bean
// 通过bean名称来获取
GreetingService greetingService1 =(GreetingService) context.getBean("getGreetingService");
// 通过bean类型来获取
GreetingService greetingService2 =context.getBean(GreetingService.class);
// 使用依赖注入的Bean
greetingService1.sayHello();
greetingService2.sayHello();
}
}
AnnotationConfigApplicationContext是一个基于注解配置方式的Spring容器,它负责加载配置类、注册和管理Bean的定义、提供依赖注入功能以及管理Bean的生命周期。
两种方式都可以用来获取Spring容器中的Bean,如果知道Bean的名称就可以使用Bean的名称作为参数来获取。如果只知道Bean的类型,则使用类型作为参数来获取。
流程图
总结:如果不使用Spring框架的依赖注入,需要手动创建和管理对象,这可能会导致代码冗余和可维护性的问题。但对于简单的应用程序,手动创建对象可能是可行的。
使用Spring框架的依赖注入,发现朱需要创建一次GreetingServiceImpl对象,就可以多次使用。通过声明式配置,简化了对象的创建和组装过程,降低了代码复杂度。Spring容器又可以管理依赖对象的生命周期,避免了内存泄漏的问题,大大减轻了JVM的压力。