1.Spring框架
1.1简介
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
1.2 优点
- 开源, 免费的框架(容器)!
- 轻量级的非入侵式框架!
- 控制翻转(IOC), 面向切面编程(AOP)
- 支持事务的处理, 对框架整合的支持
总结: Spring是一个轻量级的IOC和AOP框架.
1.3 组成

1.4 拓展
构建一切, 协调一切, 连接一切
- Spring Boot
- 一个快速开发的脚手架
- 基于它可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于Spring Boot实现的
学习SpringBoot, 必须完全掌握Spring和SpringMVC
弊端: 发展太久后, 配置地狱了,
2.IOC理论推导
在我们之前的业务中, 用户的需求可能会影响我们原来的代码, 我们需要根据用户的需求去修改源代码, 如果代码量十分大,修改一次的成本代价比较昂贵.
我们使用一个Set接口实现, 已经发生了革命性的变化.
- 之前, 程序是主动创建对象, 控制权在程序员手上.
- 使用set注入后, 程序不再具有主动性, 而是变成了被动的接受对象.
这种思想, 从本质上解决了问题, 我们不需要去管理对象的创建了. 系统耦合性大大降低.可以更专注于业务的实现, 这是IOC的原型.
package com.chen.service;
import com.chen.dao.UserDao;
import com.chen.dao.UserDaoImpl;
import com.chen.dao.UserDaoOracleImpl;
import com.chen.dao.UserDaoMysqlImpl;
public class UserServiceImpl implements UserService{
// 导入Dao, 业务层调用Dao层的方法.
// 使用private 对用户隐藏调用机制
// private UserDao userDao = new UserDaoMysqlImpl();
// 只有修改 UserDaoImpl 才能调用对应的方法.
// 既要修改业务层, 也要增加Dao层的代码
// 所以要将业务层这里的选择交给用户来, 而不是由程序员来完成.
private UserDao userDao;
// 利用set进行动态实现值的注入.
public void setUserDao(UserDao userDao){
// this.userDao 指向上方已经创建的userDao
// userDao指向形参.
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser(); // 方法名是一样的, 方便业务层调用.
}
}


IoC是Spring的核心内容.


3.HelloSpring
对象由Spring来创建管理装配
4.IOC创建对象的方式
- 使用无参构造创建对象,默认
- 假设我们要使用有参构造赋值
<bean id="user" class="com.chen.pojo.User">
<!-- 第零种, 无参赋值-->
<!-- <property name="name" value="陈浩"/>-->
<!-- 第一种, 下标赋值 -->
<!-- <constructor-arg index="0" value="chenhao"/>-->
<!-- 第二种, 类型赋值 -->
<!-- <constructor-arg type="java.lang.String" value="chenhao"/>-->
<!-- 第三种, 参数名赋值 这和默认的很像啊 比较推荐这种 -->
<constructor-arg name="name" value="chenhao"/>
</bean>
总结: 在xml加载的时候, 容器中管理的对象就已经初始化了.
5.Spring配置
5.1 别名
<!-- 如果添加了别名, 我们也可以用它 -->
<alias name="user" alias="user2"/>
5.2 Bean的配置
<!-- id: bean的唯一标识符, 也就是相当于对象名字 -->
<!-- class: bean 对象所对应的全限定名: 包名+类型 -->
<!-- name: 也是别名,而且可以取多个名字-->
<bean id="user2" class="com.chen.pojo.UserT" name="t,diff t2;df"> <!-- 这里的name也是别名 -->
<property name="name" value="陈浩"/>
</bean>
5.3 import
一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设现在团队中多人开发, 这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的.
使用的时候, 使用总的配置就可以了.
- applicationContext.xml
- beans1.xml
- beans2.xml
- beans3.xml
6.依赖注入
6.1 构造器注入
前面已经说过了
6.2 Set方式注入(重点)
- 依赖注入: Set注入!
- 依赖: bean对象的创建依赖于容器(spring)
- 注入: bean对象中的所有属性由容器来注入!
[环境搭建]
1.复杂类型
package com.chen.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2.真实测试对象
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
3.beans.xml
<bean id="student" class="com.chen.pojo.Student" name="st"> <!-- 这里的name也是别名 -->
<property name="name" value="陈豪"/>
<!-- 如何一次性大量注册 -->
</bean>
4.测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getAddress());
}
}
5.完善注入信息
<?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="address" class="com.chen.pojo.Address">
<property name="address" value="Fuzhou"/>
</bean>
<bean id="student" class="com.chen.pojo.Student" name="st"> <!-- 这里的name也是别名 -->
<!--普通值注入, value-->
<property name="name" value="陈豪"/>
<!--Bean注入, ref-->
<property name="address" ref="address"/>
<!--数组注入, ref-->
<!--数组-->
<property name="books" >
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--list-->
<property name="hobbys" >
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!--map-->
<property name="card" >
<map>
<entry key="身份证" value="111111111122223333"/>
<entry key="银行卡" value="666611122233445"/>
</map>
</property>
<!--set-->
<property name="games" >
<set>
<value>CSGO</value>
<value>RA2</value>
<value>PUBG</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--properties-->
<!--key=value-->
<property name="info">
<props>
<prop key="学号">20220115</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
</bean>
</beans>
6.3其他方式注入(拓展)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--c命名空间注入-->
xmlns:p="http://www.springframework.org/schema/p"
<!--c命名空间注入-->
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入: 即Properties, 可以直接注入属性的值-->
<bean id="user" class="com.chen.pojo.User" p:name="陈浩" p:age="12"/>
<!--c命名空间注入: 即Constructor-args, 可以通过构造器注入-->
<bean id="user2" class="com.chen.pojo.User" c:age="18" c:name="小俊俊"/>
</beans>
测试:
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class); // 增加后面一个参数就不用强转了
System.out.println(user.toString());
}
注意点: P命名和C命名空间不能直接使用, 需要导入xml约束.
6.4 bean的作用域
重点掌握singleton和prototype
-
单例模式(Spring默认机制)
无论怎样, 都是拿的同一个实例. -
原型模式(每次从容器中get的时候都会产生新对象)
<bean id="user2" class="com.chen.pojo.User" c:age="18" c:name="小俊俊" scope="prototype"/>
- 其余的request session application这些只能在web开发中使用
7.Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性.
在Spring中, 有三种装配的方式
- 在xml中显式配置
- 在java中显式配置
- 隐式的自动装配bean [重要]
7.1测试
环境搭建 一个人有两只宠物!
7.2 ByName自动装配
<bean id="cat" class="com.chen.pojo.Cat"/>
<bean id="dog" class="com.chen.pojo.Dog"/>
<bean id="people" class="com.chen.pojo.People" autowire="byName">
<property name="name" value="chenhao"/>
<!-- <property name="dog" ref="dog"/>-->
<!-- <property name="cat" ref="cat"/>-->
</bean>
7.3 ByType自动装配
<bean class="com.chen.pojo.Cat"/>
<bean class="com.chen.pojo.Dog"/>
<bean id="people" class="com.chen.pojo.People" autowire="byType">
<property name="name" value="chenhao"/>
<!-- <property name="dog" ref="dog"/>-->
<!-- <property name="cat" ref="cat"/>-->
</bean>
小结:
- byName的时候, 需要保证所有bean的ID唯一, 并且这个bean需要和自动注入的属性的set方法的值一致
- byType的时候, 需要保证所有bean的Class唯一, 并且这个bean需要和自动注入的属性的类型一致
7.4 注解自动装配
基于注释的配置的引入提出了这样一个问题:这种方法是否比XML“更好”。
要使用注解须知:
- 导入约束 context约束
- 配置注解的支持 context:annotation-config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired
加入该注解后,该属性可以不需要添加set方法
直接在属性上或者set方法上使用
默认通过byType注入, 找不到再通过byName注入
可以忽略set方法
如果显式的定义了Autowired的default为False, 那么说明这个对象可以为Null
@Nullable 说明这个字段可以为Null
@Autowired
private Cat cat;
@Autowired
private Dog dog;
如果自动装配环境比较复杂, 自动装配无法只通过一个注解完成, 要借助@Qualified: 若Autowired按byName匹配,则可以配合@Qualifier 注解指定实现类id
@Resource 是一个组合注解, 先byName再byType
@Resource // 是一个组合注解, 先byName再byType
private Cat cat;
// @Autowired
// @Qualifier(value = "dog")
@Resource(name="dog")
private Dog dog;
小结:
@Resource 和 @Autowired的区别:
- 都是用来自动装配的, 都可以放在属性字段上的
- @Resource 是一个组合注解, 先byName再byType
- @Autowired默认通过byType注入, 找不到再通过byName注入
- 执行顺序不同
8.使用注解开发
使用注解开发, 必须导入AOP包
使用注解后需导入context约束, 增加注解支持
@Component 说明这个属性被spring托管了
1.bean
2.属性如何注入
@Component //组件, 等价于 <bean id="user" class="com.chen.pojo.User"/>
public class User {
public String name;
// 相当于 <property name="name" value="chenhao"/>
@Value("chenhao")
public void setName(String name) {
this.name = name;
}
// public String name = "chenhao";
}
3.衍生注解
@component 有几个衍生注解, 我们在web开发中, 会按照mvc三层架构分层!
- dao @Repository
- service @Service
- controller @Controller
这四个注解功能一样, 都是代表将某个类注册到Spring中, 装配Bean
4.自动装配(has been introduced)
5.作用域
@Scope
可实现单例和原型模式
@Component //组件, 等价于 <bean id="user" class="com.chen.pojo.User"/>
@Scope("prototype")
public class User {
public String name;
// 相当于 <property name="name" value="chenhao"/>
@Value("chenhao")
public void setName(String name) {
this.name = name;
}
// public String name = "chenhao";
}
6.小结
xml和注解:
- xml 更万能, 适合任何场合, 维护简单方便
- 注解不是自己的类使用不了, 维护相对复杂
最佳实践: - xml用来管理bean
- 注解只负责属性的注入
- 我们在使用中,只需要注意一个问题: 必须让注解生效, 就需要开启注解的支持:
9.JavaConfig
使用java的方式配置spring
将xml配置交给java来做.
JavaConfig是Spring的一个子项目, 是核心子功能.
实体类
package com.chen.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 这个注解的意思, 说明这个类被Spring托管了, 注册到容器里面/
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("chenhao") // 属性注入
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类
package com.chen.config;
import com.chen.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration // 这个也会被Spring容器托管, 注册到容器中, 因为他本来就是一个Component. 代表这是一个配置类,
@ComponentScan("com.chen.pojo") // 不加这个扫描也可以是因为 他在这个类里面加了@Bean注解 已经把这个注入到spring了 不需要通过扫描了 扫描是配合@Component使用的 在User类上面加入了这个注解
@Import(ChenConfig.class) // 合并
public class ChenConfig2 {
// 这个方法的名字就相当于bean中的id属性
// 返回值, 就相当于bean中的class属性
@Bean
public User getUser(){
return new User();
}
}
测试类
import com.chen.config.ChenConfig;
import com.chen.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test1() {
// 如果完全使用了配置类方式来做, 那只能通过AnnotationConfig 上下文来获取容器, 通过配置类的class对象加载.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ChenConfig.class);
User getUser = context.getBean("getUser", User.class);
System.out.println(getUser.toString());
}
}
这种纯java的配置方式, 在SpringBoot中随处可见.
10.代理模式
为什么有代理模式? 因为这是Spring中AOP的底层
代理模式的分类:
- 静态代理
- 动态代理
10.1 静态代理
角色分析:
- 抽象角色: 共同做的事情, 一般使用接口或抽象类解决
- 真实角色: 被代理的角色
- 代理角色: 代理真实角色, 代理真实角色后, 一般会做一些附属操作
- 客户: 访问代理对象的人.
代码步骤:
- 接口
public interface Rent {
public void rent();
}
- 真实角色
// 房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
- 代理角色
// 代理 帮房东租房子
public class Proxy implements Rent{
private Host host; // 组合范式
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
}
// 看房
public void seeHouse(){
System.out.println("中介带你看房");
}
// 合同
public void makeContract(){
System.out.println("签租赁合同");
}
// 收费用
public void collectFare(){
System.out.println("中介收中介费");
}
}
- 客户端访问代理角色
public class Client {
public static void main(String[] args) {
// 房东要租房子
Host host = new Host();
// 代理, 中介帮房东租房子, 但是代理角色会有一些附属操作
Proxy proxy = new Proxy(host);
// 你不用面对房东, 直接找中介租房即可.
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹, 不用关注一些公共业务
- 公共业务交给代理色, 实现业务分工.
- 公共业务发生扩展时, 方便集中管理!
缺点: - 一个真实角色会产生一个代理角色; 代码量会翻倍, 开发效率变低.
10.2加深理解
代码:对应08-demo02

10.3 动态代理
可以解决静态代理的缺点
- 动态代理和静态代理角色一致
- 动态代理的代理类是动态生成的,不是我们直接写好的.
- 动态代理分为两大类: 基于接口的动态代理, 基于类的动态代理
- 基于接口—JDK动态代理[在这里使用]
- 基于类: cglib
- java字节码实现:javasist
需要了解两个类: Proxy:代理, InvocationHandler:调用处理程序
InvocationHandler
动态代理的好处: 有静态代理的所有好处
一个动态代理类代理的是一个接口, 一般就是对应的一类业务.
一个动态代理类可以代理多个类, 只要是实现的同一个接口.
11.AOP
11.1什么是?


11.2Aop在Spring中的作用
11.3使用Spring实现Aop
[重点]使用AOP织入, 需导入一个依赖包
依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
<!-- <scope>runtime</scope>-->
</dependency>
方式一: 使用Spring的API接口[主要SpringAPI接口实现]
方式二: 自定义实现AOP[主要是切面定义]
方式三: 使用注解实现!
对应spring09_AOP代码
12.整合Mybatis

12.1 回忆Mybatis
1.编写实体类
2.编写核心配置文件
3.编写接口
4.编写Mapper.xml
5.测试
12.2 Mybatis-Spring
1.编写数据源配置
2.sqlSessionFactory
3.sqlSessionTemplate
4.需要给接口加实现类
5.将自己写的实现类,注入到Spring中
6,测试即可
13.声明式事务
13.1回顾事务
- 把一组业务当成一个业务来做;要么都成功,要么都失败
- 事务在项目开发中, 十分的重要, 涉及到数据的一致性问题,不能马虎
- 确保完整性和一致性
事务ACID原则:
- 原子性: 确保要么成功要么失败
- 一致性:
- 隔离性: 确保多个程序可以操作同一个资源
- 持久性: 事务一旦完成, 无论系统发生什么问题, 结果不会再被影响. 被持久化写入存储器中
13.2 Spring中的事务管理
- 声明式事务: aop
- 编程式事务: 需要在代码中 进行事务管理
Spring中七种Propagation类的事务属性详解:
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
REQUIRED用于增删改, SUPPORTS用于查询,REQUIRES_NEW用于日志等操作,其他不用记!!
execution(* com.chen.mapper..(…))
第一个星号代表 com.chen 下的所有包!
第二个星号代表 com.chen.* 下所有的类!
第三个星号代表 com.chen.* 下所有类中的所有方法,(…)代表无论什么参数都适用
思考:为什么需要事务
- 如果不配置事务, 可能存在数据提交不一致的情况;
- 如果我们不再Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题, 不容马虎!