Spring Framework 5 框架系列
概念
-
什么是Spring?
- Spring 是一个轻量级的(对象)控制翻转(IOC/DI)和面向切面编程(AOP)的对象容器框架;
- 极大简化开发过程,降低开发难度;
-
什么是控制翻转(IOC)?
Spring 用户相当于窃取 Spring 成果的存在
举例:
我 想要生一个孩子
我 需要付出的代价是:生孩子、喂奶、教他读书……
我 生孩子的目的是:玩儿、洗完、赚钱、养老……
现在有一个第三方机构,可以帮我完成生孩子所付出的代价,也就是可以替我生产、喂奶、教学
所以我只要等着玩孩子、让他洗碗、赚钱就可以了
-
什么是 依赖注入(DI) ?
- 对象
new
出来之后,由Spring
来决定set
跟get
IOC
是思想,DI
是解决方案
- 对象
-
下载
- 官网(文档最底部)下载最新版的
dist
压缩包
- 官网(文档最底部)下载最新版的
五个核心 jar 包
commons-logging-1.2.jar
spring-beans-5.1.7.RELEASE.jar
spring-context-5.1.7.RELEASE.jar
spring-core-5.1.7.RELEASE.jar
spring-expression-5.1.7.RELEASE.jar
commons-lang.3.3.9.jar 需要自己下载
控制翻转 IOC 依赖注入 DI
- 引用核心类
public class TestGetBean{
public static void main(String[] args){
// 原始方法 创建对象
Person p = new Person();
p.setAge(18);
// 控制翻转后 创建对象
ClsssPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// ↑ ↑ 通过 ClassPath 查找 xml 文件 applicationContext.xml
// ↑ ↑ 通过 ApplicationContext(应用程序上下文) 查找 应用程序上下文
// 有了上面两个就可以通过 ctx 进行 getBean 了
Object bean = (Person)ctx.getBean("person");
bean.setAge(18);
bean.setName("张三");
// -------- 分割线 ---------
Sout(bean.getName());
Sout(bean.getAge() );
}
}
- 配置文件 applicationContext.xml 中
// 添加
<beans …… >
<bean id="person" class="com.…….Person"></bean>
</beans>
解耦合 && 属性
@Data // Lombok 注释:替代set / get / toString 方法
public class Person{
private Food foot;
}
@Data
public class Food{
private int hungry;
}
<beans …… >
<bean id="person" class="…….Person">
<constructor-arg name="name" value="zhangsan"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="food" ref="food"></constructor-arg>
</bean>
</beans>
<constructor-arg name="name" value="zhangsan"></constructor-arg>
其中:constructor-arg是属性
,value是值
,ref代表引用其他的类
Food food = ctx.getBean("food",Food.class);
String a = ToStringBuilder.reflectionToString(person);
Sout(a);
ApplicationContext.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">
<modelVersion>4.0.0</modelVersion>
xml文件的定义
- 加载多个配置文件
// 文件一: ApplicationContext.xml
// 文件二: ApplicationContext-service.xml
// 调用:
// 方法一:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml","ApplicationContext-service.xml");
// 方法二:
//将字符串定义成一个数组
// 方法三:在一个 XML 文件中使用
<import resource="applicationContext-service1.xml">
<import resource="applicationContext-service2.xml">
// 优化
<import resource="application-*.xml">
// ………………
总结
- 将
new
换成<bean id="xxx" class="xxx.class"></bean>
- 将
set
换成<constructor-arg name="xxx" value="xxx"></constructor-arg>
- 配置文件的开头部分的功能
- 引用 一个 / 多个 配置文件的方式
使用 Spring
- 引用核心类
public class TestGetBean{
public static void main(String[] args){
ClsssPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// ↑ ↑ 通过 ClassPath 查找 xml 文件 applicationContext.xml
// ↑ ↑ 通过 ApplicationContext(应用程序上下文) 查找 应用程序上下文
// 有了上面两个就可以通过 ctx 进行 getBean 了
Object bean = (Person)ctx.getBean("person");
bean.setAge(18);
bean.setName("张三");
// -------- 分割线 ---------
Sout(bean.getName());
Sout(bean.getAge() );
}
}
- 配置文件 applicationContext.xml 中
// 添加
<beans …… >
<bean id="person" class="com.…….Person"></bean>
</beans>
补充学习
什么是 单例?
- 应用程序在运行的时候只出来一个对象;
- 单例对象( 顺便 复习一下 饱汉/饿汉 模式)
什么是 Bean?
bean ,作为一个英文单词的意思是豆子,在 Spring 框架中,Bean 的意思就是一个实体,好比我创建一个 Son.java 如下:
@Data
public class Son{
private int age;
private String name;
}
那么这个类就是一个 Bean 对象,你创建的所有实体,每一个实体都是一个 Bean;
MVC 简介( 什么是MVC?)
- 缘起:
- 最初没有 MVC 的时候,只是一个简单的基于请求 / 响应的流模式
- Serviet(request / response) 【请求】 → Service【处理业务逻辑】 → jsp【展示】
- 【Model 控制层】一个业务逻辑都有一个 具体的类 来处理,将所有的 类 整合起来,称作
Service
- 【View 视图层】展示 结果 的一个层,jsp、下载 等
- 【Controller 接收层】接收前端给的数据层,接收之后给 Model 层来处理
SSM 结构
Spring / SpringMVC / Mybatis
- Spring 作用:
new
对象- DI 依赖注入(Controller 需要 Service,每次
new
一个十分消耗性能,DI
可以优化性能);service
(单例) - 处理逻辑DAO
(单例) - 处理数据库pojo
(多例) - 不归Spring
容器管理
- DI 依赖注入(Controller 需要 Service,每次
- SpringMVC :
- 一个MVC框架
- MyBatis:
- (使用 xml 文件)操作数据库
为什么用单例?
- 性能好(节省内存);
- 迸发情况下,容易产生 线程安全 问题;
- 容器(如:Spring)接到请求,每一个请求都是一个 线程 ;
单例类的注意事项:
- 不能有状态数据,如果有要非常小心;
- 一个线程改了,另一个线程读了这个样子;
ThreadLocal
用来做线程隔离的,线程 与 线程之间不共享,但是 某些实体类或者某些方法中写了private
的属性(可以使用set
来修改),一个刚set
完(或者还没set
结束),另一个就也来set
了;
Spring 的作用域
这三个 Person
完全一模一样,存储地址也一样;
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person1 = ctx.getBean("person",Person.class);
Person person2 = ctx.getBean("person",Person.class);
Person person3 = ctx.getBean("person",Person.class);
6 种(单例)作用域
-
singleton scope 单例作用域
-
prototype scope 原型作用域
-
websocket scope 基于连接 连接断开的时候对象就消失
-
request 基于请求的,请求打过来,对象就出来(只出来一次)
-
session 基于登录的,对象登陆了,就 new 一个对象(只有一个)
-
application 基于应用程序,应用程序启动,就 new 一个对象
ApplicationContext.xml 文件中加入 作用域的方式
做了以上更改之后,new
出来的三个对象地址不一样了;
<bean id="person" scope="prototype" class="com.…….Person">
<property name="name" value="和树不困"></property>
</bean>
Spring 对象生产的两种方式(只有两种)
- 单例:singleton (每次去的都是同一个),包括
- websocket
- request
- session
- application
- 以上四种绑定了生命周期,在一个生命周期内不会有两个不同的对象,getBean 的时候永远都是同一个对象;
- new 出来: (scope = "prototype")
当 scope = "prototype" 的时候,每次 getBean 都是新的 Bean,否则都是一个 Bean;
工厂模式
创建一个工厂接口,需要什么直接传参生成;
public interface Car{
public String getName();
public String getPrice();
}
public class AoDi implements Car{
public String getName(){
return "奥迪A7";
}
public String getPrice(){
return "100w";
}
}
public class BaoMa implements Car{
public String getName(){
return "宝马R7";
}
public String getPrice(){
return "120w";
}
}
// 工厂方法
public class CarFactory{
public Car getCar(String name){
if(name.equals("Aodi")){
retrun new Aodi();
}else if{
retrun new BaoMa();
}else{
throw new Exception("无法生产这辆车");
}
}
}
public static void main(String[] args){
Car car = new CarFac
}
- 在 ApplicationContext.xml 中定义这个工厂
<bean id="carFactory" class="com.…….carFactory"></bean>
<bean id="car" factory-bean="carFactory">
<!--需要 set 方法-->
<property name="name" value="Aodi"></property>
</bean>
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = ctx.getBean("car",Car.class);
循环引用(强引用)
A引用B / B引用C / C引用A: JVM计数器,当检测到还在被引用的时候就不会销毁它,所以这三个永远都不会被销毁,容易造成内存溢出;
- 在 Spring 中可以检测这种错误的做法,并作出提醒
- 单例模式是可以允许这样引用的,但是 new 或者 prototype 是不允许的
- 同时有 单例模式 & prototype 模式是允许的, 并且单例对象的引用对象也变成了单例的。
Spring 初始化 Bean 的四种方式(初始化顺序)
- 按照 Bean 标签的顺序
// ApplicationContext.cml
<bean id="a" class="………….A.class"></bean>
<bean>
标签的位置越靠前,则初始化越早;
- 先加载 depends-on=" " 依赖于:
// ApplicationContext.cml
<bean id="a" class="………….A.class" depends-on="B"></bean>
A depends-on B,则在初始化 A 之前一定会初始化 B;
- 懒加载
<lazy-init="true">
,用到哪个加载哪个,不用到不加载。
<bean id="a" class="…….class" lazy-init="true" >
</bean>
- 强 / 弱引用
强引用:A完全持有一个类B,如果A不初始化,则这个类完全不能运行;
弱引用:A在一个方法里面用到了B的某一个属性值,不初始化B顶多A 的方法会报废;
<bean id="a" class="…….class" lazy-init="true">
<property name="name">
<value></value>
</property>
</bean>
<bean id="b" class="…….class" lazy-init="true">
<property name="name">
<null></null>
</property>
</bean>
<bean id="b" class="…….class" lazy-init="true">
</bean>
// 运行结果(TtoStringBuilder):
b.getName = null // 空指针
a.getName = // 空字符串
a.getName().equals("");// true
b.getName().equals("");// 报错
自动装配
byName 匹配 ID ,A 里面引用的是 b 所以 B 的 id="b"可以,但是byType引用的是 C 这个类型;
<bean id="A" class="…….A" autowire="byName"></bean>
<bean id="b" class="…….B" autowire="byType"></bean>
<bean id="C" class="…….C" autowire="byName"></bean>
- 配置默认装配的类型
<beans ……………… default-autowire="byType">
<bean> …… </bean>
<bean> …… </bean>
<bean> …… </bean>
</beans>
学习要求
- 掌握作用域
- 掌握单例
- 理解 MVC 与 SSM
- 掌握工厂方式
总结:
Spring 的执行流程
我们从前端访问Controller层,又从Controller层访问数据库,就可以到达一个简单的 Spring 框架,但是这样执行的话 Controller 层既要接收前端数据 又要 处理数据 还要对接数据库,所以将“处理数据”这一部分单独划分出去,作为 Service 层,将 “ 对接数据库 ” 这一部分 分离出去,所谓,Mapper层,也就是常说的 Dao 层。
由于前端给出的访问不可能一次只有一个,所以我们实例化的对象不能是单例的,否则ABCD同时登陆的话,登录的可能是同一个用户。
为了让代码看起来更简洁,所以可以在 Application.xml 中写入 Bean 对象,直接实例化便可以 创建 / 引用 一个对象;
由于我们在编程的时候广泛使用注解,所以我们将逐个注解细分,常用注解如下:
注解名称 | 用途 | 备注 |
---|---|---|
@Controller | Controller层 | 用于标注当前是Controller层 |
@Service | Service层 | 用于标注当前是Service层 |
@Mapper | Mapper层 | 用于标注当前是Mapper层 |
@autowire | 任意层 | 用于自动装配,下方举例子 |
@value | 测试时给初值 | |
@Data | get / set / toString 方法的汇总 | 在实体类的最上方写上这个方法则不用再写set / get 方法,当然你可以对方法进行重写 |
@Override | 方法重写的时候标注在上面,可以自动检错; |
- autowire 说明
//在Mapper层创建一个 SonService.java 的接口,用于连接数据库读取 Son 这个实体
public class SonController{ // Son 的 Controller 层
@autowire Sonservice sonService // 这样就创建了一个 SonService 的接口
List<son> sonList = sonService.list(); //这样就能获取 son 数据库中所有的数据
}