Spring框架
第一章 Spring框架介绍
1.1 Spring公司
官网地址:https://spring.io/
1.2 Spring框架介绍
- Spring是一个一站式/full-stack(全栈)的框架。意思是使用Spring框架既可以开发web层、也可以开发service层也可以开发dao层
- 本质:Spring就是一个对象的容器,因为在这个容器中有可以处理请求的对象,所以可以用来写web层,因为有service层对象,所以可以用写service层,因为有dao层对象,所以可以用来操作数据库。
- Spring的开放性和包容性非常强,无缝整合第三方优秀框架,不会重复造轮子。
- Spring的框架结构
1.3 Spring框架的特性
- 方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用 - AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。 - 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。 - 方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。 - 方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。 - 降低JavaEE API的使用难度
Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。 - Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
注意:Spring是一个模块化的框架,Spring功能很多很强大,但是并不要求所有的功能我们都得用上,结构图中每一个圆角矩形都对应一个jar,我们使用什么模块什么功能引入对应模块即可。
1.4 Spring框架发展历程
- 1997年,IBM提出了EJB的思想
- 1998年,SUN制定开发标准规范EJB1.0
- 1999年,EJB1.1发布
- 2001年,EJB2.0发布
- 2003年,EJB2.1发布
- 2006年,EJB3.0发布
- Rod Johnson(spring之父)
- Expert One-to-One J2EE Design and Development(2002)
阐述了J2EE使用EJB开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
- Expert One-to-One J2EE Design and Development(2002)
- 2017年9月份发布了spring的最新版本spring 5.0通用版(GA)
1.5 程序的耦合性
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。 它有如下
- 分类:
- (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
- (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
- (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
- (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
- (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
- (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
- (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
- 总结: 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。 内聚与耦合 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
1.6 自定义Bean工厂解耦合
需求:开发dao层和service层模拟保存账户(Account),不具体操作数据库
- pojo对象
public class Account {
private Integer id;
//账户名
private String name;
//账户金额
private Double money;
}
- service层
public interface AccountService {
public void saveAccount(Account account);
}
public class AccountServiceImpl implements AccountService {
@Override
public void saveAccount(Account account) {
AccountDao accountDao = (AccountDao) BeanFactory.getInstance("accountDao");
accountDao.saveAccount(account);
}
}
- dao层
public interface AccountDao {
void saveAccount(Account account);
}
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount(Account account) {
System.out.println("模拟保存账户");
}
}
- bean工厂
public class BeanFactory{
public static <T> T getInstance(Class<T> clazz){
ResourceBundle resourceBundle = ResourceBundle.getBundle("bean");
String simpleName = clazz.getSimpleName();
String className = resourceBundle.getString(simpleName);
T t = null;
try{
Class clz = Class.forName(className);
t = (T)clz.newInstance();
}catch(Exception ex){}
return t;
}
}
- bean.properties
UserService=com.atguigu.service.impl.UserServiceImpl
UserDao=com.atguigu.dao.impl.UserDaoImpl
第二章 SpringIOC
1.1 IOC容器的概念和作用
注意:IOC和AOP不是从Spring开始才有的,在Spring之前更趋向于理论化,在Spring这才发扬光大的。
- IOC容器的概念
IOC:inverse of control (反转控制/控制反转),我们之前在一个java类中通过new的方式去引入外部资源(其他类等),这叫正向;现在Spring框架帮我们去new,你什么时候需要你去问Spring框架要,这就是反转(创建对象权利的一个反转),我们丧失了一个权利(主动创建对象的权利),但是我们拥有了一个福利(我们不用考虑对象的创建、销毁等问题)。 - IOC的作用:IOC解决程序耦合问题
- 关于程序耦合(有一个认识即可)
- 耦合:耦合度的意思,程序当中:为了实现一个业务功能,不同的模块之间互相调用就会产生耦合
- 注意:耦合是不能避免的,耦合尽可能去降低
- “高内聚、低耦合”,内聚度过高不利于代码复用,内聚度往往通过增加方法来完成(麻烦自己、方便别人)。
1.2 SpringIOC容器的使用
- 导入Spring框架依赖jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
- 定义类放进SpringIOC容器
package com.atguigu.ioc.component;
public class HappyComponent {
public void doWork() {
System.out.println("component do work ...");
}
}
- 创建Spring配置文件 applicationContext.xml
<bean id="happyComponent" class="com.atguigu.ioc.HappyComponent"/>
- bean标签:通过配置bean标签告诉IOC容器需要创建对象的组件是什么
- id属性:bean的唯一标识
- class属性:组件类的全类名
- scpoe:属性
- singleton:默认值,单例,项目全局内存中只有一个对象,一般在使用Spring的时候保持默认值即可
- prototype:原型的意思,在这里是多例,每引用或者getBean一次,Spring框架就返回一个新的对象,比如SqlSession这个特殊的对象
1.3 创建测试类
通过IOC容器,获取对象
@Test
public void testIOC(){
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
HappyComponent happyComponent = (HappyComponent) context.getBean("happyComponent");
happyComponent.doWork();
}
注意,Spring框架使用反射技术创建对象,并将对象存储到IOC容器中,默认使用无参构造方法创建对象,如果HappyComponent类中没有无参数构造方法,抛出异常。
1.4 通过class类型获取
通过类的class文件对象获取,这样可以避免类型的强制转换
public void testIOC(){
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
HappyComponent happyComponent = (HappyComponent) context.getBean(HappyComponent.class);
happyComponent.doWork();
}
通过class类型获取的方式比较常见,但是配置文件中不能出现相同的配置,否则报错
<bean id="happyComponent" class="com.atguigu.ioc.HappyComponent"/>
<bean id="happyComponent2" class="com.atguigu.ioc.HappyComponent"/>
1.5 对象生命周期
init-method="initMethod" destroy-method="destroyMethod"
init-method:对象初始化调用的方法
destroy-method:对象销毁之前调用的方法
第三章 依赖注入
dependency Inject(依赖注入的意思)
IOC和DI的区别
IOC和DI是一个事情,只是描述的角度不同
DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。
1.1 set方法注入
- 添加字段 String componentName
public class HappyComponent {
private String componentName;
public String getComponentName() {
return componentName;
}
public void setComponentName(String componentName) {
this.componentName = componentName;
}
public void doWork() {
System.out.println("component do work ...");
}
}
- 配置Spring的配置文件ApplicationContext.xml
<bean id="happyComponent" class="com.atguigu.ioc.HappyComponent">
<property name="componentName" value="张三"/>
</bean>
1.2 set方法注入自定义对象
- 创建自定义对象
public class HappyMachine {
private String machineName;
public String getMachineName() {
return machineName;
}
public void setMachineName(String machineName) {
this.machineName = machineName;
}
}
<bean id="happyComponent" class="com.atguigu.ioc.component.HappyComponent">
<!-- ref 属性:通过 bean 的 id 引用另一个 bean -->
<property name="happyMachine" ref="happyMachine"/>
</bean>
<bean id="happyMachine" class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="makeHappy"
</bean>
- 测试
@Test
public void test() {
HappyComponent happyComponent = (HappyComponent) iocContainer.getBean("happyComponent");
HappyMachine happyMachine = happyComponent.getHappyMachine();
String machineName = happyMachine.getMachineName();
System.out.println("machineName = " + machineName);
}
1.3 构造方法注入
public class Person{
private Integer id;
private Double money;
private String name;
public Person( Integer id, Double money, String name){
this.id = id;
this.money = money;
this.name = name;
}
public void setId(Integer id) {
this.id = id;
}
public void setMoney(Double money) {
this.money = money;
}
public void setName(String name) {
this.name = name;
}
}
- Spring配置文件 applicationContext.xml
<bean class="com.atguigu.ioc.Person" id="person">
<constructor-arg name="id" value="99"></constructor-arg>
<constructor-arg name="money" value="8.55"></constructor-arg>
<constructor-arg name="name" value="李四"></constructor-arg>
</bean>
1.4 特殊值处理
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="commonValue" value="hello"/>
null值
<property name="commonValue">
<!-- null标签:将一个属性值明确设置为null -->
<null/>
</property>
XML实体
<!-- 实验九 给bean的属性赋值:特殊值处理 -->
<bean id="propValue" class="com.atguigu.ioc.component.PropValue">
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a < b"/>
</bean>
1.5 复杂数据类型注入
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myPro;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyPro(Properties myPro) {
this.myPro = myPro;
}
<!-- 注入数组 -->
<property name="myStrs">
<array>
<value>strs1</value>
<value>strs2</value>
<value>strs3</value>
</array>
</property>
<!-- 注入List集合 -->
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- 注入Set集合 -->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 注入Map集合 -->
<property name="myMap">
<map>
<entry key="map1" value="mapvalue1"></entry>
<entry key="map2" value="mapvalue2"></entry>
<entry key="map3" value="mapvalue3"></entry>
</map>
</property>
<!-- 注入properties集合 -->
<property name="myPro">
<props>
<prop key="pro1">provalue1</prop>
<prop key="pro2">provalue2</prop>
<prop key="pro3">provalue3</prop>
</props>
</property
第四章 Schema约束
对xml的新型约束,控制xml该怎么写。常用的约束有DTD,和Schema,Schema逐步取代DTD
官方约束:限制约束文档xsd怎么写,xsd限制我们自己定义的xml
- Schema约束支持命名空间 namespace,就是java中的import导包
<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">
</beans>
- beans 我们自己的xml文件的根标签
- xmlns = xml namesapce命令空间(包名)
http://www.springframework.org/schema/beans
命名空间的名字,定义的包名
官方要求,命令空间的名字,必须全球唯一性
- xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
官方约束 - xsi:schemaLocation 约束文件的具体位置