SSM 框架学习:SpringFramework 介绍和 IoC 容器 Published on Dec 21, 2023 in 日常 with 0 comment Java SpringFramework IoC ### 一. 技术体系架构 #### 1.1 单一架构 单一架构:一般情况下,就是指一个项目,一个工程,导出为一个 war 包,在 1 个 Tomcat 上运行。 #### 1.2 分布式架构 分布式架构:就是指一个项目,拆分成很多模块,每个模块都相当于是 IDEA 中的 1 个 module 。每个工程都运行在自己的 Tomcat 上,模块间可以相互调用,每个模块内部都可以看成是一个单一架构的应用。 ### 二. Spring Framework 介绍 #### 2.1 广义的 Spring 通常指 Spring 技术栈。泛指以 Spring Framework 为基础的 Spring 技术栈,经过十多年的发展,Spring 已经不是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目组成的成熟技术,例如 Spring MVC,Spring Cloud,Spring Cloud 等,其中 Spring Framework 是其他子项目的基础。 #### 2.2 狭义的 Spring 指Spring Framework 基础框架。它是一个开源的应用程序框架,由 SpringSource 公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供很多功能,例如,依赖注入、AOP 等。 ### 三. SpringIoC 容器和核心概念 #### 3.1 组件和组件管理概念  **(1)Spring 可以充当组件管理角色 IoC** 组件可以完全交给 Spring 进行管理,其代替了程序员原有的 new 对象和对象属性赋值等动作,我们只需要编写元数据告知 Spring 管理哪些类组件和它们的关系即可。 其中:组件是映射到应用程序中所有可重用组件的 Java 对象。 - 组件一定是对象 - 对象不一定是组件 综上所述,Spring 充当一个组件容器,创建、管理和存储组件,减轻编码压力,使开发人员更加专注于业务编写。 **(2)Spring 管理的优势** - 降低了组件之间的耦合性:Spring IoC 容器通过依赖诸如机制,削弱了组件之间的依赖关系。 - 提高代码的可重用性和可维护性。 - 方便配置和管理。 - 交给 Spring 管理的组件,可以享受 Spring 框架的其他功能。 #### 3.2 Spring IoC 容器和容器的实现 ##### 3.2.1 SpringIoC 容器介绍 Spring IoC 容器,负责实例化、配置和组装 Bean,容器通过读取配置元数据来获取相关要实例化、配置和组装组件的指令。配置元数据以 XML、Java注解或者 Java代码的形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。 ##### 3.2.2 SpringIoC 容器具体接口和实现类 **SpringIoC 的容器接口** `BeanFactory` 接口提供了一种高级配置机制,能够管理任何类型的对象,它是 SpringIoC 容器标准化超接口。 `ApplicationContext` 是 `BeanFactory` 的子接口,拓展了以下功能: - 更容易和 Spring 的 AOP 功能集成。 - 消息资源处理(用于国际化)。 - 特定于应用程序给与此接口实现。 **常用的 ApplicationContext 容器实现类** (1)`ClassPathXmlApplicationContext` 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象。 (2)`FileSystemXmlApplicationContext` 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象。 (3)`AnnotationConfigApplicationContext` 通过读取 Java 配置类创建 IOC 容器对象。 (4)`WebApplicationContext` 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入到 ServletContext 域中。 总的来说,Spring 框架提供了多种配置方式,包括 XML 、注解和 Java 配置类。其中: - XML方式:最早的配置方式之一,通过 XML 文件定义 Bean 及其依赖关系、作用于等信息,让 IoC 容器来管理 Bean 之间的依赖关系,从第一版开始支持。 - 注解方式:从 2.5 版本开始支持,可以在 Bean 类上使用注解代替 XML 配置文件中的配置信息,通过在 Bean 类上添加如 `@Component`、`@Service`等将 Bean 注册到 Spring IoC 容器中。 - Java 配置类:从 3.0 版本开始支持,通过定义 Java 类来定义 Bean、Bean之间的依赖关系和配置信息,从而代替了 XML 方式。Java 配置类是一种使用 Java 编写配置信息的方式,通过 `@Configuration`、`@Bean`等注解实现配置。 #### 3.3 概念总结 ##### 3.3.1 IoC (Inversion of Control) 控制反转,IoC主要是针对对象的创建和调用控制而言的,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是反转了控制权。这种方式基本是通过依赖查找方式实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。 ##### 3.3.2 DI (Dependency Injection) 依赖注入,指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必再应用程序中硬编码对象间的依赖关系,实现了解耦。在 Spring 中,DI 通过 XML 或注解实现,提供了三类注入方式:构造函数注入、Setter 方法注入和接口注入。 ### 四. Spring IoC 实践和应用 ### 4.1 基于 XML 方式的组件管理 ##### 4.1.1 Bean 信息声明配置 IoC (1)准备工作,创建一个新的工程并在父工程 pom.xml 中导入依赖: ```xml org.springframework spring-context 6.0.6 org.junit.jupiter junit-jupiter-api 5.3.1 ``` (2)基于无参构造函数创建 ```java package com.alen.ioc01; public class TestComponent { // 默认包含无参构造函数 public void doWork(){ System.out.println("This is TestComponent.doWork\n"); } } ``` (3)引入 xml 配置文件,利用 IDEA 可以快速创建。 其中,在 `` 中填写组件信息,包括 id 和 class,id 作为组件标识,需要唯一,方便后期读取。 ```java ``` ##### 4.1.2 组件 Bean 依赖注入配置 DI (1)基于构造函数的依赖注入 a. 准备组件类 ```java public class UserDao { } public class UserService { private UserDao userDao; public UserService(UserDao userDao){this.userDao = userDao;} } ``` b. 编写配置文件 ```java ``` 如果存在多个构造参数,可以编写多个 constructor-arg,可以按照顺序填写值。也可以通过 name 字段来指定所需填充的构造参数的名字,这种方式比较推荐。 (2)基于 Setter 方法依赖注入 a. 准备组件类 ```java public class MovieFinder { } public class SimpleMovieLister { private MovieFinder movieFinder; private String movieName; public void setMovieFinder(MovieFinder movieFinder){ this.movieFinder = movieFinder; } public void setMovieName(String movieName){ this.movieName = movieName; } } ``` b. 编写配置文件 ```java ``` ##### 4.1.3 IoC 容器的创建和使用 ```java public class SpringIocTest { // 创建 IOC 容器并读取配置文件 @Test public void createIoC(){ // 创建容器 选择合适的容器实现即可 // 直接创建容器并且指定配置文件即可 [推荐] ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring01.xml"); } // 在IOC容器中获取组件 Bean @Test public void getBeanFromIoC(){ // 1、创建 IoC 容器对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(); applicationContext.setConfigLocations("spring01.xml"); applicationContext.refresh(); // 2、获取 IoC 容器的组件 // a. 直接根据 beanID 获得,返回值为 Object 需要强转,不推荐 TestComponent testComponent0 = (TestComponent) applicationContext.getBean("testComponent"); // b. 根据 beanID 且指定 bean 类型 TestComponent testComponent1 = applicationContext.getBean("testComponent", TestComponent.class); // c. 直接根据类型获取 TestComponent testComponent2 = applicationContext.getBean(TestComponent.class); testComponent1.doWork(); testComponent2.doWork(); testComponent0.doWork(); } } ``` ##### 4.1.4 IoC 容器的创建和使用 a、周期方法的概念 我们可以在组件类中定义方法,然后在 IoC 容器实例化和销毁组件对象的时候进行调用。这两个方法,我们称之为生命周期方法。 b、周期方法声明 ```java public class JavaBean { public void init(){ /** * 必须是 public 必须是 void 必须是无参数的,命名随意 */ System.out.println("JavaBean.Init"); } public void clear(){ System.out.println("JavaBean.Destroy"); } } ``` c、周期方法配置 ```xml ``` d、测试函数和结果 ```java @Test public void test_04(){ // 创建 IoC 容器就会进行组件对象实例化,触发 init ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring04.xml"); // 触发 destroy applicationContext.close(); } ``` ![] ##### 4.1.5 基于 XML 方式整合三层架构组件实验  a、准备数据库材料 ``` CREATE TABLE `info` ( `id` int NOT NULL, `name` varchar(50) NOT NULL, `age` int DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ```  b、创建项目并导入依赖 ```xml org.springframework spring-context 5.3.22 mysql mysql-connector-java 8.0.28 com.alibaba druid 1.2.8 org.springframework spring-jdbc 5.3.22 org.junit.jupiter junit-jupiter-api 5.3.1 ``` c、准备实体类 ```java public class Person { private int id; private String name; private int age; public int getAge() { return age; } public int getId() { return id; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } ``` d、体验用 IoC 容器实例化对象和 Java 实例化对象的异同 > 用 Java 方式实例化 ```java public void testForJava(){ // 0、创建一个连接池对象 DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql:///ssm"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUsername("root"); dataSource.setPassword("123456"); // 1、实例化对象 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); // 2、调用方法 /** * jdbcTemplate.update () 非查询语句 * jdbcTemplate.queryForObject() 查询单个对象 * jdbcTemplate.query() 查询集合 */ } ``` > IoC 方式实例化 ```java ``` e、创建 DAO 层接口和实现类 ```java /** 数据层的接口*/ public interface PersonDao { // 数据库查询全部学生数据 List queryAll(); } ``` 实现类 ```java public class PersonDaoImpl implements PersonDao { private JdbcTemplate jdbcTemplate; // 注入我们的 JdbcTemplate 对象 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public List queryAll() { // 我们用 jdbcTemplate 进行数据库查询,通过 IoC容器进行装配,不用自己去实例化 String sql = "select id,name,age from info"; List person = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Person.class)); System.out.println("PersonDao =" + person); return person; } } ``` f、创捷业务层接口和实现类 ```java /** 创建人员业务接口 */ public interface PersonService { // 查询所有人员数据业务 List findAll(); } ``` 实现类: ```java public class PersonServiceImpl implements PersonService { private PersonDao personDao; public void setPersonDao(PersonDao personDao) { this.personDao = personDao; } @Override public List findAll() { List personList = personDao.queryAll(); System.out.println("PersonList= " + personList); return personList; } } ``` g、创建 controller 层 ```java public class PersonController { private PersonService personService; public void setPersonService(PersonService personService) { this.personService = personService; } public void findAll(){ List all = personService.findAll(); System.out.println("最终人员数据为: " + all); } } ``` h、 编辑配置文件 ```java ``` 测试一下,看看效果: ```java // 从 IoC 容器获取 Controller 并调用业务 @Test public void testQueryAll(){ // 创建 IoC 容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring02.xml"); // 获取组件对象 PersonController controller = applicationContext.getBean(PersonController.class); // 使用组件对象 controller.findAll(); } ```  ##### 4.1.6 XML IoC 方式问题总结 a、注入的属性必须添加 setter 方法,导致代码结构比较乱。 b、配置文件和Java代码分离,编写不是很方便。 c、XML 配置文件解析效率低。 ### 4.2 基于 注解 的方式管理 Bean ##### 4.2.1 准备 Spring 项目和组件 a、准备项目 pom.xml,这里子工程自动引入。 b、准备组件类: > 普通组件 ```java // description:普通的组件 public class CommonComponent { } ``` > Controller 组件 ```java public class TController { } ``` > Dao 组件 ```java public class TDao { } ``` > Service 组件 ```java public class TService { } ``` c、组件添加标记注解 |注解 |说明 | | :------------ | :------------ | | @Component |该注解用于描述Spring中的Bean,仅表示容器中的一个组件,并且可以作用在应用的任何层次,例如Dao层等 | | @Repository |该注解用于将数据访问层,即Dao层的类标识为Bean | | @Service |该注解通常作用在业务层 | | @Controller|该注解通常作用在控制层 | d、配置文件确定扫描范围 (1)基本扫描配置 ```xml ``` 看下效果: ```java public class SpringIoCTest { @Test public void testIoC_01() { // 创建 IoC 容器 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring01.xml"); // 获取组件 TDao bean = applicationContext.getBean(TDao.class); System.out.println(bean); } } ```  (2) 排除指定包的注解 ```java ``` 看下效果:  ##### 4.2.2 组件 Bean 作用域和周期方法注解 a、周期方法声明 ```java @Component public class JavaBean { // 周期方法命名随意, 但必须是 public void 没有形参 @PostConstruct public void init(){ System.out.println("JavaBean init"); } @PreDestroy public void destroy(){ System.out.println("JavaBean destroy"); } } ``` b、修改 xml 文件 ```java ``` c、测试一下 ```java @Test public void test_02(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring02.xml"); JavaBean bean = applicationContext.getBean(JavaBean.class); applicationContext.close(); } ```  ##### 4.2.3 Bean 属性赋值,引用类型自动装配 DI a、搭建场景,准备 UserController,UserService的实现类和接口 ```java @Controller public class UserController { // Autowired 是自动装配注解,相当于 xml 中的 property // DI 会查找 IoC 容器中符合类型的组件对象/实现类,然后设置给当前属性 @Autowired private UserService userService; public void show(){ // 调用业务层的 show 方法 userService.show(); System.out.println(show); } } public interface UserService { public String show(); } @Controller public class UserServiceImpl implements UserService{ @Override public String show() { return "UserServiceImpl show()"; } } ``` b、编辑 xml 文件 ```xml ``` c、测试一下 ```java @Test public void test_03(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring03.xml"); UserController userController = applicationContext.getBean(UserController.class); userController.show(); System.out.println(userController); } ```  值得注意的是,在上述案例中,IoC 容器中有一个 UserService 接口对应的实现类对象,因而顺利执行。但是如果在使用 `@Autowired` 注解时,没有 bean 则会报错。同样的,如果同一个类型有多个对应的组件,比如一个接口有多个实现类,可以使用` @Qualifier` 注解指定bean id来实现。 ##### 4.2.4 Bean 属性赋值,基本类型属性赋值 DI ```java @Component public class JavaBean { // 方案一:直接赋值 private String name = "1111"; //方案二:可以通过注解赋值,但这种方式一般是用于从外部读取数据,例如数据库 @Value("19") private int age; } ``` ### 4.3 基于 配置类 方式管理 Bean Spring 完全注解开发是指通过 Java 配置类代码来配置 Spring 应用程序,使用注解来替代原本在 XML 配置文件中的配置,相对于 XML 配置,完全注解配置具有更强的类型安全性和更好的可读性。 ##### 4.3.1 配置类和扫描注解 创建一个新的工程,并编写对应的 Controller 和 Service。 ```java @Controller public class StudentController { @Autowired private StudentService studentService; } @Service public class StudentService { } ``` 创建配置类: ```java // 配置类 代替 XML 文件,可以实现 1、包扫描注解配置 2、引用外部配置文件 3、声明第三方依赖的bean组件 import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @ComponentScan(value = "org.alen") //如果是多个包的话,则是 {..,...,..} 形式 // @PropertySource(value = "classpath:jdbc.properties") 引入外部配置文件 @Configuration public class JavaConfiguration { } ``` 测试一下: ```java public class springIoCTest { @Test public void test(){ // 创建 IoC 容器 ApplicationContext iocContainerAnnotation = new AnnotationConfigApplicationContext(JavaConfiguration.class); // 获取 bean StudentController studentController = iocContainerAnnotation.getBean(StudentController.class); System.out.println("bean" + studentController); } } ```  ##### 4.3.2 @bean 标签实现第三方依赖组件 代码参考: ```java @ComponentScan(value = "org.alen") //如果是多个包的话,则是 {..,...,..} 形式 // @PropertySource(value = "classpath:jdbc.properties") 引入外部配置文件 @Configuration public class JavaConfiguration { // 方法的返回值类型等于 bean 组件的类型或者它的接口和父类 // 方法的名字等于 bean id,方法体自定义实现过程 // 最后要加上 @Bean ,会真正让配置类方法存储到 IoC容器 @Bean public DruidDataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(); dataSource.setDriverClassName(); dataSource.setUsername(); dataSource.setPassword(); } } ``` 如果需要其他第三方组件的,例如 jdbcTemplate 需要 dataSource,只要其他组件也是 @Bean 方法,那么直接调用就可以从 IoC 容器中获取该组件。 ### 4.4 三种配置方式总结 ##### 4.4.1 XML 配置方式 1、所有内容写到 XML 格式的配置文件中。 2、声明 bean 通过 `` 标签。 3、`` 标签包含基本信息和属性信息。 4、引入外部的 properties 文件可以通过 `` 。 5、IoC 具体容器的实现选择 ClassPathXmlApplicationContext 对象。 ##### 4.4.2 XML+注解配置方式 1、注解负责标记 IoC 的类和进行属性装配 2、xml 文件依然重要,需要通过`` 标签指定注解范围。 3、标记 IoC 注解。 4、标记 DI 注解,如 @Autowired 等。 5、IoC 具体容器的实现选择 ClassPathXmlApplicationContext 对象。 ##### 4.4.3 完全注解配置总结 1、使用配置类和注解进行实现。 2、xml 文件替换成使用 @Configuration 注解标记的类。 3、标记 IoC 注解。 4、标记 DI 注解。 5、以往的``标签通过 `@ComponentScan` 指定注解范围。 6、通过` @ PropertySource("classpath:application.properties")`实现对外部配置文件的引入。 7、bean 标签使用` @Bean `注解和方法实现。 8、IoC 具体容器的选择使用 `AnnotationConfigApplicationContext` 对象。 本文由 Alen 创作,采用 知识共享署名4.0 国际许可协议进行许可本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名最后编辑时间为: Feb 28, 2024 at 05:52 pm