简介
本文是基于狂神说java的教学视频的,希望记录自己的学习过程,同时也欢迎大家交流。
spring是一个开源且免费的框架,主要用于解决web应用开发。其特点是轻量级且非侵入式。
spring两个核心概念是:支持控制翻转(IOC)和面向切片编程(AOP)。同时,由于这两个特性,使得其对事务的支持性极强。
Spring的组成模块如下:source
我们常用的功能包括:
- spring Boot:
- 快速开发的脚手架
- 构建单个微服务
- spring cloud
- 是基于spring boot实现的
- 协调各个微服务
学习spring和springMVC是掌握spring boot的基础。
核心知识点
在没有学习spring之前就听说过大名鼎鼎的IOC和AOP这两个spring和核心概念。下面来详细了解一下。
1. IOC
所谓控制翻转是指的依赖对象的方式反转了。之前对象的创建是依赖于代码的编写,控制反转的思想就是,将对象创建的过程交给用户,由用户自己来决定使用什么样的对象。作为程序的设计者,只需要为用户提供相应的接口实现即可。其中使用到的是:
- set接口
- 设计思想:工厂模式
(图片来自网络)
IOC保证了我们不需要再对程序进行任何的修改了,只需要修改xml文件,对象的完全托付给spring进行管理。
2. Hello Spring 测试案例
过程说明:
- 使用maven创建项目;
- 导入spring依赖;
- 创建测试实体类;
- 创建spring核心配置文件;
- 测试spring对象管理功能。
项目结果如下:
2.1 使用maven创建项目
使用maven创建一个新的空项目,将项目中的src目录删除,并在项目下新建模块。在子模块的基础上进行测试。
2.2 导入spring依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
</dependency>
</dependencies>
2.3 编写测试实体类
package com.yindarui.POJO;
public class Hello {
private String str;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
", id=" + id +
'}';
}
}
2.4 编写spring的核心配置文件:applicationContext.xml
这里我们只使用property标签来为对象赋值。
<?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">
<!--这个配置文件就是spring IOC的核心,即容器管理对象,将对象创建的过程变得透明-->
<!--id是你绑定的对象在容器中的名称,在使用时我们只需要指定该对象在容器中的名字就可以创建该对象,过程可以理解为如下:
Hello hello = new hello();
-->
<bean id="hello" class="com.yindarui.POJO.Hello">
<!--property是bean的子元素,str就是HelloSpring里的str变量,张三就是给str变量赋的值-->
<property name="str" value="张三"/>
<property name="id" value="123"/>
</bean>
</beans>
2.5 测试类
package HelloSpringTest;
import com.yindarui.POJO.Hello;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1 {
@Test
public void test() {
// 加载配置文件,即获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过context来获取对象
Hello hello = (Hello) context.getBean("hello");
// 查看对象的值是否被修改了
System.out.println(hello.toString());
}
}
2.6 结果
Hello{
str='张三', id=123}
2.7 执行原理
spring创建对象的原理是:
1. 对于一个没有有参构造的类,spring将利用该类无参构造来初始化对象,且利用property标签
调用该类的set方法来为对象赋值。
2. 对于一个含有 有参构造的类,由于有参构造会覆盖无参构造,此时无法正常利用默认无参构造初始化对象,则会报错。此时需要使用spring使用constructor-arg 标签
来创建对象。
<?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">
<!--这个配置文件就是spring IOC的核心,即容器管理对象,将对象创建的过程变得透明-->
<!--id是你绑定的对象在容器中的名称,在使用时我们只需要指定该对象在容器中的名字就可以创建该对象,过程可以理解为如下:
Hello hello = new hello();
-->
<bean id="hello" class="com.yindarui.POJO.Hello">
<!-- name是指定的构造函数中的参数名-->
<constructor-arg name="str" value="李四"/>
<!-- id是指定的构造函数的参数的位置,从零开始-->
<constructor-arg index="1" value="1"/>
<!-- 采用ref的方式使得我们不需要显示的指定构造函数参数的索引或类型-->
<constructor-arg ref="tele"/>
</bean>
<bean id="tele" class="java.lang.String"/>
</beans>
结果:
// 注意这里的tel的默认参数是空字符串,而不是null
Hello{
str='李四', id=1, tel=''}
2.8 对象何时被创建
测试:
- 在实体类的构造函数中添加输出语句;
- 在context对象创建前添加输出语句;
- 在context对象创建后添加输出语句;
测试类:
package HelloSpringTest;
import com.yindarui.POJO.Hello;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
*
*/
public class Test1 {
@Test
public void test() {
System.out.println("context object will be created then!");
// 加载配置文件,即获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("context object is created!");
// 通过context来获取对象
Hello hello = (Hello) context.getBean("hello");
Hello hello2 = (Hello) context.getBean("hello");
System.out.println(hello == hello2 ? "两个引用指向的是同一个对象": "两个引用指向的不是同一个对象");
// 查看对象的值是否被修改了
System.out.println(hello.toString());
}
}
结果:
构造函数被创建
context object is created!
两个引用指向的是同一个对象
Hello{
str='李四', id=1, tel=''}
分析:使用spring管理对象时,对象会在用户加载这个配置文件时被创建,用户使用只需要知道自己要使用的类在容器中的名称即可。同时,每个对象在一个容器中只有一份。
3. 配置文件
3.1 别名
为bean起的别名。
<alias name="hello" alias="hello2"/>
3.2 Bean
<!-- id——bean的唯一标识符
class——bean绑定的类的全限定名
name——起别名,使用空格分隔来以起多个别名 -->
<bean id="h1" class="java.lang.String" name="ss1 ss2" />
3.3 import
将多个编写的applicationContext.xml合并为一个总的。
4. 依赖注入(dependency injection DI)
bean对象的创建依赖于容器,bean对象的所有属性,由容器来注入。其实就是按照配置文件的方式对类进行初始化,根据不同的类型大致有以下几种:
- 基本数据类型
- 引用数据类型
- 集合
- 列表
- map
准备测试的类:
package com.yindarui.POJO;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
*/
public class DependencyInjectionTest {
private int id;
private String name;
private Map<String, Integer> score;
private int[] nums;
private String[] strs;
private List<String> list;
private Set<String> set;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setScore(Map<String, Integer> score) {
this.score = score;
}
public void setNums(int[] nums) {
this.nums = nums;
}
public void setStrs(String[] strs) {
this.strs = strs;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public Map<String, Integer> getScore() {
return score;
}
public int[] getNums() {
return nums;
}
public String[] getStrs() {
return strs;
}
public List<String> getList() {
return list;
}
public Set<String> getSet() {
return set;
}
@Override
public String toString() {
return "DependencyInjectionTest{" +
"id=" + id +
", name='" + name + '\'' +
", score=" + score +
", nums=" + Arrays.toString(nums) +
", strs=" + Arrays.toString(strs) +
", list=" + list +
", set=" + set +
'}';
}
}
4.1 构造器注入
4.2 set方式注入
配置文件:
<?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="testName" class="java.lang.String"/>
<bean id="DITest" class="com.yindarui.POJO.DependencyInjectionTest">
<!-- 1.普通值注入-->
<property name="id" value="1"/>
<!-- 2. bean注入, 注入的是另一个bean ref-->
<property name="name" ref="testName"/>
<!-- 3. 注入map集合,注入的是一个键值对-->
<property name="score">
<props>
<prop key="语文">100</prop>
<prop key="数学">100</prop>
<prop key="英语">60</prop>
</props>
</property>
<!-- 4. 注入基本类型数组-->
<property name="nums">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<property name="strs">
<array>
<!-- 直接赋值-->
<value>"我是一个元素"</value>
<!-- 使用ref赋值-->
<ref bean="listBean"/>
</array>
</property>
<!-- 5. 注入list集合,为引用数据类型-->
<property name="list">
<list>
<value>"好好学习"</value>
<ref bean="listBean"/>
</list>
</property>
<!-- 6. 注入set集合, 为引用数据类型-->
<property name="set">
<set>
<value>2</value>
<value>3</value>
<!-- 这个值是无法加入的,set元素不可重复-->
<value>3</value>
</set>
</property>
</bean>
<bean id="listBean" class="java.lang.String"/>
</beans>
测试方法
@Test
public void testForDI() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"DITest.xml");
DependencyInjectionTest diTest = (DependencyInjectionTest) context.getBean("DITest");
System.out.println(diTest.toString());
System.out.println(diTest.getStrs()[1] == diTest.getList().get(1));
}
测试结果
DependencyInjectionTest{
id=1, name='', score={
数学=100, 语文=100, 英语=60}, nums=[1, 2, 3], strs=["我是一个元素", ], list=["好好学习", ], set=[2, 3]}
// 注意,list中第二个空字符串和strs中第二个空字符串同一个对象
true
4.3 其他方式注入
spring中提供了俩种快捷的注入方式:
- c命名空间注入:要求类中有有参构造。
- p命名空间注入:要求类中有有效的无参构造,代替了property标签,在bean标签中直接为参数注入值;
命名空间的使用要求导入约束:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
5. bean作用域
作用域 | 中文 | 描述 |
---|---|---|
singleton | 单例(默认) | 对于一个对象,整个容器中保留一份 |
prototype | 原型 | 将单个bean定义定义为任意数量的对象实例 |
reques | 请求 | |
session | 一个session中 | |
application | 整个web程序 | |
websocket | 一个socket中 |