spring
1,Spring简介
1.1Spring概述
Spring是最受欢迎的企业级Java应用程序开发架构,数以百万的来自世界各地开发人员使用spring框架来创建性能好、易于测试、可重用的代码。
Spring框架是一个开源的Java平台,它最初是由Rod Johnson编写的,并且于2003年6月首次在Apache2.0许可下发布
Spring是轻量级的框架,其基础版本只有2MB左右大小
Spring框架的核心特性是可以用于开发任何Java应用程序,但是在JavaEE平台上构建web应用程序是需要拓展的。Spring框架的目标是使J2EE开发变得更容易使用,通过启用基于POJO编程模型来促进良好的编程实践。
1.2Spring家族
1.3Spring Framework
Spring基础框架,可以视为Spring基础设备,基本上任何其他Spring项目都是以Spring Framework为基础的。
1.3.1Spring Framework特性
- 非侵入式:使用Spring Framework开发应用程序时,Spring对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于Spring Framework开发应用程序时结构清晰、简洁优雅。
- 控制反转 :IOC–Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
- 面向切面编程:AOP–Aspect Oriented Programming,在不修改源代码的基础上增强代码能力。
- 容器:Spring IOC是一个容器,因为包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不(xu)的搭建超大复杂应用系统
- 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且Spring旗下的项目已经覆盖了广泛领域,很多方面的功能需求都可以在Spring Framework的基础上全部使用Spring来实现
1.3.2Spring Framework五大功能模块
功能模块 | 功能介绍 |
---|---|
Core Container | 核心容器,在Spring环境下使用任何功能都必须基于IOC容器 |
AOP & Aspects | 面向切面编程 |
Testing | 提供了对junit或TestNG测试框架的整合 |
Data Access/Integration | 提供了对数据访问/集成的功能 |
Springmvc | 提供了面向web应用程序的集成功能 |
2,IOC
2.1IOC容器
2.1.1 IOC思想
IOC:Inversion of Control 翻译过来是反转控制
- -获取资源的传统方式
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
- -反转控制方式获取资源
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式,反转了资源的获取方向–改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供资源的方式即可,极大降低了学习成本,提高了开发效率。这种行为也称为查找的被动形式
- -DI
DI是IOC的另一种表达方式:即组件以一些预先定义好的方式(例如:setter方法)接受来自于容器的资源注入。相当于IOC而言,这种表述更直接
结论:IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现
2.1.2 IOC容器再Spring中的实现
Spring的IOC容器就是IOC思想的一个落地的产品实现。IOC容器中管理的组件也叫bean。在创建bean之前,首先需要创建IOC容器。Spring提供了IOC容器的两种实现方式:
- BeanFactory
这是IOC容器的基本实现,是Spring内部使用的接口。面向Spring本身,不提供给开发人员使用。
- ApplicationContext
BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
- ApplicationContext的主要实现类
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的xml格式的配置文件创建IOC容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取XML格式的配置文件创建IOC容器对象 |
ConfigurableApplicationContext | ApplicationContext的子接口,包含一些拓展refresh()和close(),让ApplicationContext具有启动、关闭和刷新上下文的能力 |
WebApplicationContext | 专门为Web应用准备,基于web环境创建IOC容器对象,并将对象引入存入ServletContext域中 |
ctrl+h查看当前类或接口的一个继承实现关系
2.2基于xml管理bean
2.2.1实验一:入门案例
- 创建Maven Module
- 引入依赖
<dependencies>
<!--基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包-->
<dependency>
<groupId>org.sunyur.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
先创建一个类 定义一个sayHello方法
再创建一个spring配置文件 选中resources - new - XML Configuration File - Spring Config->applicationContext.xml
<!--
bean:配置一个bean对象,将对象交给IOC容器管理
属性:
id:bean的唯一标识,不能重复
class:设置bean对象所对应的类型
-->
<bean id="helloworld" class="全类名路径"></bean>
对象交给IOC容器管理,验证下先获取IOC,再获取IOC中对象
测试
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("配置文件路径")
//resources与Java都会被加载到同一路径下,就是类路径下
//获取IOC容器中bean对象 根据bean的id获取bean对象
HelloWorld helloworld=(HelloWorld)ioc.getBean("bean的唯一标识id")//因为我们是通过id获取的,所以并不清楚他是什么类型,可以通过强转改变
helloworld.sayHello()
大部分通过反射创建实例化对象都是使用无参构造方法
删除无参构造运行会报错得到NoSuchMethodException
2.2.2获取bean的三种方式
-
根据bean的id获取 getBean(String name) 返回object
-
根据bean的类型获取 getBean(Class requiredType) 直接传入bean的类型 返回类型 较多使用 一个类型的bean只需要配置一次即可
注意:根据类型获取bean时,要求IOC容器中有且只有一个类型匹配的bean
-
根据bean的类型和id获取getBean(String name,Class requiredType)
第二种情况中applicationContext.xml内有两个或多个bean id的bean,但是类型相同
当使用 getBean(Class requiredType)调用的时候就会出现NoUniqueBeanDefinitionException
第二种情况中applicationContext.xml内一个bean都没有
当使用 getBean(Class requiredType)调用的时候就会出现NoSuchBeanDefinitionException
在applicationContext.xml中bean的参数scope设置单例或者多例
- 拓展
如果组件类实现了接口,根据接口类型可以获取bean吗?
可以,前提是bean是唯一
如果一个接口有多个实现类,这些实现类都配置了bean,根据接口类型可以获取bean吗?
不行,因为bean不唯一
- 结论
根据类型来获取bean时,在满足唯一性的前提下,其实只是看【对象instanceof指定的类型】的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到
即通过bean的类型,bean所继承的类的类型、bean所实现的接口都可以获取bean
2.2.3实验三:依赖注入之setter注入
- 创建学生类Student
public class Student{
private Integer id;
private String name;
private Integer age;
private String sex;
//加上 get/set toString
}
可以看出Student这个类依赖于属性 id name age sex 那么就可以在IOC容器中为它所依赖的属性进行赋值 为当前属性进行赋值就称为依赖注入
注入有set注入与构造器注入
- 设置bean时为属性赋值
<bean id="唯一标识" class="类的全类名路径">
<!--
property:通过成员变量的set方法进行赋值 看到property就是set注入形式
name:设置需要赋值的属性名(和set方法有关)
value:设置为属性所赋的值
-->
<property name="属性名" value="给予的值"></property> //鼠标悬停可以发现name的位置出现了set方法。。。
</bean>
2.2.4实验四:依赖注入之构造器注入
- 在Student类中添加有参构造 也得保证有无参构造方法
<bean id="唯一标识id" class="类的全类名路径">
<!--如果我们实体类中只有一个有参构造的话,可以直接按照参数的顺序去赋值就行了 constructor-->
<constructor-arg value=""></constructor-arg><!--如果这里只填写一个constructor-arg的话,就会去找只要一个参数的构造方法-->
</bean>
<!--如果实体类中有两个构造方法(仅最后一个参数不同),而applicationContext.xml文件中bean中constructor-arg都对应,执行测试会发现获取的是第二个构造器的内容赋值 如果想让获取到另一个构造方法可以使用constructor-arg中name指定-->
2.2.5 实验五:特殊值处理
- 字面量赋值
什么是字面量?
int a = 10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10
而如果a是带引号的:‘a’,那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身
<!--使用value属性给bean的属性赋值时,spring会把value属性的值看作字面量-->
<property name="name" value="张三">
- null值
<property name="name">
<null /> <!--这才是为name属性设置null值-->
</property>
<!--注意-->
<property name="name" value="null"></property> 为name所赋的值是字符串null
<:< >:>大小符号
- xml实体
<!--小号在sml文档中用来定义标签的开始,不能随便使用-->
<!--解决方案一:使用xml实体来代替-->
<property name="expression" value="a < b"/>
- CDATA节
<!--CDATA节其中的内容会原样解析<![CDATA[...内容值]]>-->
<property name="expression">
<!--解决方案二:使用CDATA节-->
<!--CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据-->
<!--xml解释器看到CDATA节就知道这里是纯文本,就不会当作xml标签或属性来解析-->
<!--所以CDATA节中写什么符号都很随意-->
<value>![CDATA[a<b]]</value>
</property>
2.2.6实验六:为类类型属性赋值
- 创建一个班级类Clazz
private Integer cid;
private String cname;
设置有参/无参构造方法 get/set方法 toString方法 对一对应对象 对多对应集合
表示学生所对应的班级,在学生类中加入班级类型的属性 Clazz clazz(不能使用value,value是给字面量初始赋值的)
给类类型赋值的话有三种方式
- 引入外部的bean
<bean id="唯一标识符id class="类的全类名路径">
<property name="属性名" value="字面量赋值"></property>//property要与构造方法匹配 进行set赋值
<property name="clazz" ref="引用clazz的bean id"></property>//ref引用IOC容器中的某个bean的id
</bean>
<bean id="唯一标识id" class="类的全类路径">//Clazz
<property name="" value=""></property>//为clazz中属性赋值set设置值
</bean>
- 级联的方式
<bean id="唯一标识符id class="类的全类名路径">
<property name="属性名" value="字面量赋值"></property>//property要与构造方法匹配 进行set赋值
<property name="clazz" ref="引用clazz的bean id"></property>//ref引用IOC容器中的某个bean的id
<!--级联方式,要保证提前为clazz属性赋值或者实例化-->
<property name="clazz.属性名" value="xxx"></property>//要对应类中的属性数量 这里级联相当于修改了下面赋值的值
</bean>
<bean id="唯一标识id" class="类的全类路径">//Clazz
<property name="" value=""></property>//为clazz中属性赋值set设置值
</bean>
- 内部bean
<bean id="唯一标识符id class="类的全类名路径">
<property name="属性名" value="字面量赋值"></property>//property要与构造方法匹配 进行set赋值
<property name="clazz">
<bean id="唯一标识id" class="类的全类名路径">
<property name="直接填写clazz的属性名不用带clazz" value></property>
</bean>
</property>
</bean>
测试尝试从内部bean能不能通过ioc容器获取 显然不行报 NoSuchBeanDefinitionException
No bean named ‘clazz’ available 没有一个叫clazz的bean可用
内部bean,只能在当前bean的内部使用,不能直接通过IOC容器获取
2.2.7实验七:为数组类型的属性赋值
在Student类中加入String[] hobby,在生成get/set 构造 toString方法
<property name="唯一标识">
<array>
<!--<value></value>如果数组中存储的类型是自变量类型那就使用value
<ref></ref>如果数组中存储的类型是类类型那就使用ref ref表示引用,引用我们ioc容器中某些bean,然后把它放入数组中
-->
<value>赋值</value>
</array>
</property>
2.2.8实验八:为list集合类型的属性赋值
在班级Clazz类中添加 List student 多对一对象 一对多集合
<bean id="唯一标识id" class="类的全类路径">//clazz类
<property name="" value=""></property>//为clazz类中属性赋值
<property name="students">
<list>
<ref bean="引用ioc容器中其他bean"></ref>
</list>
</property>
</bean>
- 配置一个集合类型的bean,需要使用util的约束
<util:list id="唯一标识"><ref bean="ioc中其他bean"></ref></util:list> //引入util list标签 直接用就可以在applicationContext.xml中使用
2.2.9实验九:为map集合类型的属性赋值
新建一个Teacher类 get/set toString 有参/无参构造
在学生类Student类中添加Map<Sring,Teacher> teacherMap; 添加get/set 重写toString
- 第一种方式
//先找到一个学生bean
<property name="属性名">
<map>
<entry key="10086" value-ref="唯一标识1(其他bean id)"></entry>
<entry key="10010" value-ref="唯一标识2(其他bean id)"></entry> key,value要根据map定义的类型决定
</map>
</property>
<bean id="唯一标识1" class="类的全类名路径">
<property name="属性名" value="赋值"></property>