一、本次学习的软件版本介绍
JDK 1.8+
Maven 3.5+
IDEA2018+
SpringFramework 5.1.4
二、环境搭建
- Spring 的 jar 包
咱们能够通过 maven 的核心仓库来获取对应版本的依赖 (上网找 - 搜 spring- 看到 springcontext)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
- Spring 的配置文件
1. 配置文件的搁置地位: 任意地位 没有硬性要求
2. 配置文件的命名: 没有硬性要求, 不过官网倡议:applicationContext.xml
思考: 你不通知 spring 你的配置文件在哪,它怎么去拉配置呢?所以咱们前面要配置对应【配置文件】的门路
备注: 咱们能够通过下图所示创立对应的配置文件
三、Spring 的外围 API
- ApplicationContext
作用:Spring 提供的的这个 ApplicationContext 这个工厂,最重要的是为了对象的创立
益处: 解耦合
- ApplicationContext 接口类型
> 为什么是接口?因为接口的目标是为了屏蔽实现的差别 (谁实现我,谁就必须实现我所申明的办法)
> 那么 Spring 的接口实现类有哪些呢?非 WEB 环境:ClassPathXmlApplicationContext (比方 main junit 等办法)
WEB 环境: XmlWebApplicationContext
> 如何证实下面说的是正确的呢?首先为了保障有 WEB 环境的实现类,咱们先导入上面这个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
第二步:咱们应用 CTRL+H 去查看该类的继承关系,那么咱们就找到了如下图
- Application 的实现类都是重量级资源
》毕竟属于创建对象的重要办法,所以其肯定占用了大量的内存》不会频繁地创建对象:一个利用只会创立一个工厂对象》在多线程并发拜访的时候肯定是线程平安的
- 开发你的第一个 Spring 程序吧!
其实和第一节的通过工厂形式获取对象的形式一样,咱们通过在配置文件获得惟一的类的全限定名来获取对象,上面为开发步骤
1》创立类:须要什么类,就配置什么类
2》配置 applicationContext.xml 文件
<bean id="user" class="cn.paul.spring.demo.hellospring.entity.User"/>
这里的 id 肯定是惟一的,class 是类的权限定名,这就像咱们结尾所学习的配置文件的 k - v 对一样
3》通过工厂,取得对象
ApplicationContext
|- ClassPathXmlApplicationContext(Junit 或 main 等非 web 的环境下应用)|- XmlWebApplicationContext(在 web 环境下应用)
4》demo 展现
public class TestSpring {
@Test
void test01(){// 获取 Spring 工厂 ( 传入对应的文件门路)
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 通过 Spring 工厂创建对象
User user = ctx.getBean("user", User.class);
System.out.println("user:"+user);
}
}
备注: 对于非 WEB 的环境,你须要告知 Spring 你的配置文件在哪里!!
- 第一个程序后,你应该要对上面这些细节有理解哦
第一点,通过下面的 getBean 办法传入的是配置文件的 id 和对应的类名称,那么很显著
通过这种形式,能很好地去让 Spring 确定你须要创立哪个 Bean(在 Spring 中也叫 component)
第二点,咱们还有一种 getBean(User.class),通过这种形式来创建对象,比拟好的一点就是
开发者能够略微懒一下,然而这是有限度的,如果你在配置文件中有申明到两个 bean,而这
两个 bean 所对应的 Class 名称都一样,都为这个同一个类的全限定名,那么在运行的时候就会
报 NoUniqueBeanDefinitionException 这个异样
// 通过这种形式取得对象,就不须要强制类型转换
Person person = ctx.getBean("Person",Person.class);
// 以后 Spring 的配置文件中,只能有一个 <bean class 是 Person 类型
Person person = ctx.getBean(Person.class);
// 获取的是 Spring 工厂配置文件中所有的 bean 标签的 id 值(看英文的复数便知)String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
// 依据 class 的类型取得 Spring 配置文件中对应的 id 值
String[] beanNamesForType = ctx.getBeanNamesFortype(Person.class);
// 上面两个办法都能用于判断是否存在指定 id 的 bean
ctx.containsBeanDefinition("指定的 id");
ctx.containsBean("指定的 id")
- 配置文件中须要留神的细节
a) 咱们先来思考一个问题,如果咱们只是配置了一个只含有 class 标签的 bean,Spring 会帮
咱们创立吗?如下
<bean class="cn.paul.spring.demo.hellospring.entity.User"/>
答案是:能够的,然而必须保障如果你想理论地应用上该对象,那么这个 bean 要保障惟一
第二问题: 既然上面对 bean 的定义是能够,那么 Spring 是否有给它定义“id 值”呢?咱们能够通过 ctx.getBeanDefinitionNames() 来获取对应的 id 值以做出判断
咱们会发现其实 Spring 其实会给这个只申明了 class 名称的 bean 对应的 id 值的,其命名规定为: 类的全限定名 + # + 下标
如:cn.paul.spring.demo.hellospring.entity.User#0
咱们注意一下最初一个下标,为什么下标是数组 0 呢?莫非还有 1,2,3.....?
其实我揣测可能是这样的,咱们能够在配置文件配置多个只带 class 名称的 bean
Spring 也会给他们对应的默认 id 值 (前面下标扭转即可)
b) 只定义 class 名称的 bean 的利用场景
1> 如果这个 bean 只须要应用一次,那么就能够省略 id 值
2> 如果这个 bean 会应用屡次,或者被其余 bean 援用则须要设置 id 值
c) name 的属性
除了在 bean 标签内定义 id 和 class 属性外,还能够定义 name 属性
作用: 用于在 Spring 的配置文件中,为 bean 对象设置别名 (小名)
依照咱们的生存教训,咱们晓得本名只能有一个 (就像这里的 id 属性),然而咱们能够有
很多个别名小名 (就像这里的 name 属性,然而留神是指只针对一个 bean 对象)
问题: 它和 id 属性有什么区别呢?
先说相同点:
1.ctx.getBean("id|name") --> Object(通过 id 或 name,都能够创建对象)然而留神: 能够定义多个 name,之间用逗号宰割即可,然而不能在不同的 bean 应用同一个别名
2. <bean id=""class="" />
等效
<bean name=""class="" />
3. 为什么要有 name 这个属性呢?这是因为在晚期 Spring 与 Struts1 整合的时候,对于 bean 的命名必须是以 / 结尾的
然而晚期的 xml 是不容许 id 以 / 结尾的,所以导致说可用 name 来匹配这种非凡的命名
最初随着技术的倒退,当初 xml 也容许 id 以 / 结尾了,所以说 name 这个属性多多少少
带有一点历史的痕迹
4. 对于 ctx 工厂提供的办法
ctx.containsBeanDefinition("字符串名称")只能判断是否存在指定 id 的 bean
不能判断 name 值
但
ctx.containsBean("字符串名称")既能够判断是否存在指定 id 值的 bean,也能够
判断 name 值 (所以当前尽量用这个吧,比拟好记)
- Spring 工厂对上述的底层实现原理 (简易版)
1.Spring 框架通过 ClassPathXmlApplicationContext 工厂读取配置文件
applicationContext.xml
2. 获取到配置文件标签的相干信息,比方 id 的值 =account,class 的值 =cn.paul.entity.User
3. 通过反射去创建对象
Class<?> clazz = Class.forName("class 的值");
User user = clazz.newInstance();
4. 留神一个问题,通过反射能够创立以任何修饰符润饰的构造方法的类的对象 (回顾爆破)
同时要晓得反射的底层其实是调用了该类的构造方法,并且首先调用的是无参结构
Class<?> clazz = Class.forName("class 的值");
User user = clazz.newInstance();
等效于
User user = new User();
- 思考:在 spring 的开发中,是不是所有的对象,都会交由 Spring 工厂来创立呢
实践上来说,因为应用 Spring 工厂能解耦合的起因,的确是能够通过工厂来创立的
然而有特例,比方操作数据库的长久层 DAO,或者叫 entity 是不会交给 Spring 创立的
而是交给长久层框架比方 mybatis 进行创立,毕竟 entity 生来是操作数据库的,和数据库做映射关系