一、Spring是什么?
轻量级一站式框架
简化JavaEE开发,涵盖Web、数据访问、事务管理等各层技术,无需依赖重量级服务器(如EJB)。核心优势
解耦:通过IoC容器管理对象依赖
高效整合:无缝集成MyBatis等主流框架
模块化设计:按需选择Spring模块(Core、AOP、Data等)
和SpringBoot的关系
核心目标
- Spring:提供 IoC(控制反转)和 AOP(面向切面编程)等基础功能,解决企业级开发的复杂性。
- SpringBoot:简化 Spring 应用的搭建和配置,减少 XML 和样板代码,实现快速启动和开箱即用。
依赖关系
- Spring 应用需手动配置大量组件(如数据源、MVC、安全等),而 SpringBoot 通过自动配置简化这一过程。
- SpringBoot基于 Spring 框架构建,必须依赖 Spring 的核心模块(如spring-context、spring-beans)。
开发体验
- Spring:需编写大量配置类或 XML 文件,依赖管理繁琐。
- SpringBoot:通过@SpringBootApplication和Starter 依赖自动装配组件,实现零配置启动。
- Spring生态定位
Spring Framework是基石
Spring Boot简化配置
Spring Cloud构建微服务
二、IoC与DI
1. 核心思想
IoC:将对象的创建权从程序员反转给Spring容器
DI:由容器动态建立对象间的依赖关系(Setter/构造器注入)
<!-- 传统方式:硬编码创建 --> BookDao bookDao = new BookDaoImpl(); <!-- IoC方式:容器创建 --> <bean id="bookDao" class="com.example.BookDaoImpl"/>2.IOC(Inversion of Control)控制反转
(1)什么是控制反转呢?
使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到
外部,此思想称为控制反转。
- 业务层要用数据层的类对象,以前是自己new的
- 现在自己不new了,交给别人[外部]来创建对象
- 别人[外部]就反转控制了数据层对象的创建权
- 这种思想就是控制反转
(2)Spring和IOC之间的关系是什么呢?
- Spring技术对IOC思想进行了实现
- Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"
- IOC思想中的别人[外部]指的就是Spring的IOC容器
(3)IOC容器的作用以及内部存放的是什么?
- IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
- 被创建或被管理的对象在IOC容器中统称为Bean
- IOC容器中放的就是一个个的Bean对象
(4)当IOC容器中创建好service和dao对象后,程序能正确执行么?
- 不行,因为service运行需要依赖dao对象
- IOC容器中虽然有service和dao对象
- 但是service对象和dao对象没有任何关系
- 需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系
3.DI(Dependency Injection)依赖注入
(1)什么是依赖注入呢?
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
- 业务层要用数据层的类对象,以前是自己new的
- 现在自己不new了,靠别人[外部其实指的就是IOC容器]来给注入进来
- 这种思想就是依赖注入
(2)IOC容器中哪些bean之间要建立依赖关系呢?
- 这个需要程序员根据业务需求提前建立好关系,如业务层需要依赖数据层,service就要和dao建立依赖关系
4.具体实现
介绍完Spring的IOC和DI的概念后,我们会发现这两个概念的最终目标就是充分解耦,具体实现靠:
- 使用IOC容器管理bean(IOC)
- 在IOC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.
5. 依赖注入方式
| 类型 | 配置示例 | 适用场景 |
|---|---|---|
| Setter注入 | <property name="dao" ref="bookDao"/> | 灵活,推荐首选 |
| 构造器注入 | <constructor-arg ref="bookDao"/> | 强制依赖场景 |
| 自动装配 | <bean autowire="byType"/> | 简化配置,按类型匹配 |
关于Setter注入:
- 对于引用数据类型使用的是<property name="" ref=""/>
- 对于简单数据类型使用的是<property name="" value=""/>
配置文件的编写配置相对麻烦,因此可以采用自动装配。
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式有哪些?
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
三、Bean
0.Bean基础配置
<bean id="bean的唯一标识" class="bean的类全名" scope="bean的作用范围,有singleton(默认)和prototype" name="为bean取的别名"/>整个配置中最常用的就两个属性id和class
1. Bean作用域
<bean scope="singleton"/> <!-- 默认单例,复用对象 --> <bean scope="prototype"/> <!-- 非单例,每次请求新对象 -->
单例优点:减少对象创建开销,提升性能
多例场景:有状态对象(如含成员变量的域对象)
思考
为什么bean默认为单例?
- bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
- bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
- 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
- 因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
- 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
- 因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
哪些bean对象适合交给容器进行管理?
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
哪些bean对象不适合交给容器进行管理?
- 封装实例的域对象,因为会引发线程安全问题,所以不适合。
2. Bean生命周期
初始化顺序
1. 构造方法 → 2. 属性注入 → 3. InitializingBean.afterPropertiesSet() → 4. 自定义init-method
触发销毁
ctx.close(); // 立即关闭容器 ctx.registerShutdownHook(); // JVM关闭前自动回调销毁顺序
1. DisposableBean.destroy() → 2. 自定义destroy-method
3. Bean实例化三种方式
| 方式 | 实现 |
|---|---|
| 构造方法(默认) | <bean class="com.example.BookDaoImpl"/> |
| 静态工厂 | <bean factory-method="create" class="com.example.Factory"/> |
| 实例工厂(FactoryBean接口) | 实现FactoryBean<T>,重写getObject()(整合第三方框架常用) |
(1)构造方法
测试函数
public interface BookDao { public void save(); } public class BookDaoImpl implements BookDao { public BookDaoImpl() { System.out.println("book dao constructor is running ...."); } public void save() { System.out.println("book dao save ..."); } }可将public BookDaoImpl()改成private BookDaoImpl(),也能执行成功。
说明内部走的依然是构造函数,能访问到类中的私有构造方法。
显而易见 Spring底层用的是反射。
applicationContext.xml配置Spring容器
<?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="bookDao" class="com.mytest.dao.impl.BookDaoImpl"/> </beans>编写运行程序
public class AppForInstanceBook { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); } }输出
因为每一个类默认都会提供一个无参构造函 数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。
(2)静态工厂
在spring的配置文件application.properties中添加以下内容:
<bean id="orderDao" class="com.mytest.factory.OrderDaoFactory" factory-method="getOrderDao"/>class:工厂类的类全名
factory-mehod:具体工厂类中创建对象的方法名
在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少,比如日志等业务操作。
具体实现代码略,这种方式一般是用来兼容早期的一些老系统,所以了解为主。
(3)实例工厂(FactoryBean)
实例工厂实例化配置的过程还是比较复杂,所以spring为了简化这种配置方式就提供了一种叫 FactoryBean的方式来简化开发。
创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> { //代替原始实例工厂中创建对象的方法 public UserDao getObject() throws Exception { return new UserDaoImpl(); } //返回所创建类的Class对象 public Class<?> getObjectType() { return UserDao.class; } }在Spring的配置文件中进行配置,之后AppForInstanceUser运行类不用做任何修改,直接运行即可。
<bean id="userDao" class="com.mytest.factory.UserDaoFactoryBean"/>查看源码会发现,FactoryBean接口其实会有三个方法,分别是:
//方法一:getObject(),被重写后,在方法中进行对象的创建并返回 T getObject() throws Exception; //方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象 Class<?> getObjectType(); //方法三:默认单例,将return重写false即可改成非单例 default boolean isSingleton() { return true; }这些方式中,重点掌握构造方法和FactoryBean即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使 用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
四、依赖注入高级技巧
0.相关属性
1. 集合注入
<!-- List注入 --> <property name="list"> <list> <value>Java</value> <ref bean="obj1"/> <!-- 引用其他Bean --> </list> </property> <!-- Map注入 --> <property name="map"> <map> <entry key="port" value="8080"/> <entry key="db" value-ref="dataSource"/> </map> </property>
- property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签
- List的底层也是通过数组实现的,所以<list>和<array>标签是可以混用
- 集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少
2. 解决依赖冲突
按类型自动装配:容器中同类型Bean必须唯一
按名称装配:属性名需与Bean的id匹配(耦合度高,慎用)
五、小结
(1)什么IOC/DI思想?
- IOC:控制反转,控制反转的是对象的创建权
- DI:依赖注入,绑定对象与对象之间的依赖关系
(2)什么是IOC容器?
- Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器
(3)什么是Bean?
- 容器中所存放的一个个对象就叫Bean或Bean对象。