【java全端课24】# 一、框架概述
1.1 框架概念
目前市面主流框架有很多,本阶段主要学习:SSM
- Spring:为所有bean(组件)提供管理解决方案
- SpringMVC:为解决表述层(控制层)常见问题,而提供的一整套解决方案,如:处理请求数据,响应数据,RESTFul等问题
- Mybatis:为解决数据访问层(Dao层),而提供一整套解决方案,如:简化传统JDBC代码,优化入参出参等问题。
1.2 组件与容器概念
组件:具有一定功能的对象,常见组件如下:
- Controller层组件
- Service层组件
- Dao层组件
容器:管理组件的对象,包括组件的创建,获取及销毁等
- 程序中容器
- 简单容器:数组,集合等
- 复杂容器:Spring框架
二、Spring Framework简介
2.1 Spring广义与狭义
spring官网:spring.io/
广义的 Spring:Spring 技术栈(全家桶)
广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈。
-
经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础。
-
这些子项目涵盖了从企业级应用开发到云计算等各方面的内容,能够帮助开发人员解决软件发展过程中不断产生的各种实际问题,给开发人员带来了更好的开发体验。
狭义的 Spring:Spring Framework(基础框架)
-
狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架。
-
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。
-
Spring全家桶的其他框架都是以SpringFramework框架为基础!
2.2 Spring Framework概述
Spring是基于IOC和AOP的容器框架
IoC容器 | 核心容器
Spring IoC 容器,负责实例化、配置和组装 bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。
IoC(Inversion of Control)控制反转
IoC 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
DI (Dependency Injection) 依赖注入
DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入。
管理对象中属性
AOP:Aspect Oriented Programming面向切面编程思想,它可以在不修改源代码的情况下,给程序动态统一添加额外功能,AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
-
主要功能模块如下
功能模块 功能介绍 Core Container 核心容器,控制反转和依赖注入 AOP&Aspects 面向切面编程 TX 声明式事务管理 Testing 快速整合测试环境 Data Access/Integration 提供了对数据访问/集成的功能。 Spring MVC 提供了面向Web应用程序的集成功能。
2.3 注意
Spring 使创建 Java 企业应用程序变得容易。从Spring Framework 6.0开始,Spring 需要 Java 17+。
2.4 Spring Framework底层实现
Spring框架底层使用BeanFactory接口实现的,具体相关接口及实现类如下:
BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口!
ApplicationContext是BeanFactory的子接口。它补充说:
- 更容易与 Spring 的 AOP 功能集成
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现,例如Web 应用程序的
WebApplicationContext简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多特定于企业的功能。ApplicationContext是BeanFactory的完整超集!
三、SpringIOC/DI基本实现
实现SpringIOC核心思想
- 将组件装配到SpringIOC容器对象中
- 创建SpringIOC容器对象
- 通过SpringIOC容器对象获取组件
实现Spring框架搭建方式
- 方式一:纯XML方式
- 方式二:XML+注解方式
- 方式三:配置类+注解方式
实现Spring框架环境准备
3.1 基于XML方式装配组件
3.1.1 环境准备
<dependencies>
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
</dependencies>
3.1.2 实现关键步骤
- 准备POJO类:Student
POJO(Plain Old Java Object)“普通的Java对象”
```java
package com.mytest.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer stuId;
private String stuName;
private Integer stuAge;
}
```
-
创建配置文件:spring.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"> <!-- 将Student装配到IOC容器--> <bean id="zs" class="com.mytest.bean.Student"> <property name="stuId" value="1001"></property> <property name="stuName" value="zs"></property> <property name="stuAge" value="18"></property> </bean> </beans> -
测试类
@Test
public void testStudent(){
//创建容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
//从容器获取Student
Student bean = ioc.getBean(Student.class);
System.out.println("bean = " + bean);
}
3.1.3 xml方式完成三层搭建
之前:
之后:
不再需要 new
3.2 基于配置类装配组件
约定 > 注解 > 配置 > 代码
3.2.1 环境准备(同上)
- 全类名 = 注解所在的类
- id = 默认类名(首字母小写)
- 或者
value=""定义,参数只有一个value,可以省略,直接写""
- 或者
进阶版,完全不用xml,使用配置类来代替xml。如下:3.2.2
3.2.2 实现关键步骤
-
准备POJO类:Student
- 代码同上
-
创建配置类:SpringConfig
- @Value
- property 的注解是成员变量上的
@Value("") - 注意:只能为字面量类型注入数值
- 字面量:Java基本数据类型+String
- @Autowired 为对象中属性自动注入(非字面量,自动装配)
- @Autowired 装配规则:先byType 再byId
- @Qualifier 为对象中属性自动注入(非字面量,自动装配)
- 不能单独使用,一般匹配@Autowired共同使用
- 目的为自动注入字段设置id
- @Resource 与 @Autowired 一样,不过装配规则相反
package com.mytest;
import com.mytest.bean.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //标识当前类是一个[配置类:代替xml配置文件]
public class SpringConfig {
//将student对象装配到IOC容器中,id名称=方法名称
@Bean
public Student student(){
return new Student(1001, "zhangsan", 18);
}
}
-
测试类
@Test public void testSpring(){ //创建容器对象(基于配置类) ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class); Student bean = ioc.getBean(Student.class); System.out.println("bean = " + bean); }
3.3 @Bean注解详解
在Spring框架中,
@Bean注解是用于方法级别的注解,它告诉Spring容器这是一个bean的定义,并且该方法会返回一个对象,这个对象应该被注册为Spring应用上下文中的一个bean。@Bean注解通常与@Configuration类一起使用,以声明性的方式配置Spring IoC容器。
3.3.1 @Bean源码
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
@Bean注解中属性说明
name:指定bean的名称。如果不提供,默认会采用方法名作为bean的名称。value和name属性是别名关系,即它们代表相同的配置项。initMethod:指定初始化方法,在bean注入完成之后执行。destroyMethod:指定销毁方法,在包含bean的应用上下文关闭时调用。autowireCandidate:定义了是否将此bean视为自动装配的候选者。默认值为true,意味着除非明确设置为false,否则该bean是可以作为其他bean依赖注入的候选对象。
3.3.2 使用@Bean装配DruidDataSource(第三方Bean)
- 导入DruidDataSource依赖
<!--DuirdDataSource坐标 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<!--DruidDataSource启动器坐标(与上面坐标二选一即可)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
- 编写配置文件:application.properties
#配置DruidDataSource
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/0923_demo
jdbc.username=root
jdbc.password=root
-
编写配置类:
- 未用属性类
package com.mytest.config; import com.alibaba.druid.pool.DruidDataSource; import com.mytest.properties.JdbcProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class DruidConfig { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } }- 使用属性类:
package com.mytest.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component("jdbcProperties")
@ConfigurationProperties(prefix = "jdbc")
@Data
public class JdbcProperties {
private String driverClassName;
private String url;
private String username;
private String password;
}
package com.mytest.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.mytest.properties.JdbcProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DruidConfig {
@Autowired
@Qualifier("jdbcProperties")
private JdbcProperties jdbcProperties;
@Bean
public DataSource dataSource(/*JdbcProperties jdbcProperties*/){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(jdbcProperties.getDriverClassName());
ds.setUrl(jdbcProperties.getUrl());
ds.setUsername(jdbcProperties.getUsername());
ds.setPassword(jdbcProperties.getPassword());
return ds;
}
}
package com.mytest.day13_springboot.property;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
//@Data
@Component("jdbcProperties")
//定义属性类,可以指定属性前缀(prefix)
@ConfigurationProperties(prefix = "jdbc")
public class JdbcProperties {
// @Value("${jdbc.driverClassName}")
private String driverClassName;
private String url;
private String username;
private String password;
@Override
public String toString() {
return "JdbcProperties{" +
"driverClassName='" + driverClassName + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.mytest.day13_springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.mytest.day13_springboot.property.JdbcProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DruidConfig {
// @Value("${jdbc.driverClassName}")
// private String driverClassName;
// @Value("${jdbc.url}")
// private String url;
// @Value("${jdbc.username}")
// private String username;
// @Value("${jdbc.password}")
// private String password;
@Autowired
@Qualifier("jdbcProperties")
private JdbcProperties jdbcProperties;
@Bean("druidDataSource")
public DruidDataSource getDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(jdbcProperties.getDriverClassName());
druidDataSource.setUrl(jdbcProperties.getUrl());
druidDataSource.setUsername(jdbcProperties.getUsername());
druidDataSource.setPassword(jdbcProperties.getPassword());
return druidDataSource;
}
}
package com.mytest.day13_springboot;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.sql.SQLException;
@SpringBootApplication
public class Day13SpringbootApplication {
public static void main(String[] args) throws SQLException {
//springboot启动器,默认返回容器对象
ConfigurableApplicationContext ioc = SpringApplication.run(Day13SpringbootApplication.class, args);
//获取容器对象
// ApplicationContext context = new AnnotationConfigApplicationContext();
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
DruidPooledConnection connection = dataSource.getConnection();
System.out.println("connection = " + connection);
}
}
3.3.3 Bean生命周期
- 生命周期的六个阶段
-
阶段一:加载Bean定义:
- Spring容器读取
XML文件或其他配置文件,解析配置信息。 - 将解析后的配置信息转换为Spring内部数据结构(
BeanDefinition对象)。 - 存储
BeanDefinition对象,待进行组件实例化。
- Spring容器读取
-
阶段二:实例化Bean组件:
- 根据
BeanDefinition中的信息,实例化Bean对象。 - 如果有依赖其他Bean的情况,先实例化被依赖的Bean。
- 此步骤单纯实例化Bean和依赖的Bean组件,不会进行属性赋值。
- 根据
-
阶段三:设置Bean属性: - Spring容器将根据
BeanDefinition中的配置,通过setter方法或字段直接注入属性值。 - Spring容器属性和实例化过程是分离的,所有在配置的时候,组件声明和引用不分先后顺序。 -
阶段四:调用Bean的初始化方法:
- 如果Bean实现了
InitializingBean接口,Spring将调用其afterPropertiesSet()方法。 - 如果在XML配置中定义了
init-method,则执行该方法。 - 如果Bean使用了
@PostConstruct注解,则执行被注解的方法。 - 此阶段调用自定义初始化方法,可以进行相关的初始化工作,类似:
Servlet的init方法。
- 如果Bean实现了
-
阶段五:Bean可以使用:
- 此时Bean已经初始化完成,可以被其他Bean引用或者容器直接使用。
-
阶段六:调用Bean的销毁方法阶段(仅适用于单例Bean):
- 如果Bean实现了
DisposableBean接口,Spring将调用其destroy()方法。 - 如果在XML配置中定义了
destroy-method,则执行该方法。 - 如果Bean使用了
@PreDestroy注解,则在销毁之前执行被注解的方法。 - 此阶段调用自定义销毁方法,可以进行相关的初始化工作,类似:
Servlet的destroy方法。
- 如果Bean实现了
-
-
案例代码
-
POJO类:Student
package com.mytest.bean; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; public class Student { private Integer stuId; private String stuName; private Integer stuAge; public Student() { System.out.println("Student==>构造器!!!"); } public Student(Integer stuId, String stuName, Integer stuAge) { this.stuId = stuId; this.stuName = stuName; this.stuAge = stuAge; } public Integer getStuId() { return stuId; } public void setStuId(Integer stuId) { System.out.println("==>Student->setId()!!!"); this.stuId = stuId; } public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } public Integer getStuAge() { return stuAge; } public void setStuAge(Integer stuAge) { this.stuAge = stuAge; } public void initStudent() { System.out.println("==>Student->init-method()!!!"); } public void destroyStudent() { System.out.println("==>Student->destroy-method()!!!"); } } -
配置类:SpringConfig
package com.mytest; import com.mytest.bean.Student; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration //标识当前类是一个[配置类:代替xml配置文件] public class SpringConfig { @Bean(value = "student", initMethod = "initStudent", destroyMethod = "destroyStudent") public Student student(){ Student student = new Student(); student.setStuId(101); return student; } } -
测试类
public class TestSpring { @Test public void testSpring(){ //创建容器对象(基于配置类) ConfigurableApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class); //从容器中获取Student对象 Student bean = ioc.getBean(Student.class); System.out.println("bean = " + bean); //关闭IOC容器对象 ioc.close(); }
-
3.3.4 Bean作用域
-
Bean作用域概念
在Spring框架中,bean的作用域(@Scope)决定了该bean的生命周期和可见性。Spring提供了多种作用域来满足不同的需求。
-
常用作用域
取值 含义 创建对象的时机 默认值 singleton 在 IOC 容器中,这个 bean 的对象始终为单实例 IOC 容器初始化时 是 prototype 这个 bean 在 IOC 容器中有多个实例 获取 bean 时 否 - 如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值 含义 创建对象的时机 默认值 request 请求范围内有效的实例 每次请求 否 session 会话范围内有效的实例 每次会话 否 -
案例代码
-
POJO类:Student
package com.mytest; import com.mytest.bean.Student; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration //标识当前类是一个[配置类:代替xml配置文件] public class SpringConfig { @Bean(value = "student", initMethod = "initStudent", destroyMethod = "destroyStudent") // @Scope(value = "prototype") @Scope(value = "singleton") public Student student(){ Student student = new Student(); student.setStuId(101); return student; } } -
测试类
@Test public void testSpring(){ //创建容器对象(基于配置类) ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class); Student bean = ioc.getBean(Student.class); Student bean2 = ioc.getBean(Student.class); System.out.println("是否为同一对象:" + (bean==bean2)); }
-