Spring框架

第一章 Spring框架介绍

1.1 Spring公司

img001.png

官网地址:https://spring.io/

1.2 Spring框架介绍

  • Spring是一个一站式/full-stack(全栈)的框架。意思是使用Spring框架既可以开发web层、也可以开发service层也可以开发dao层
  • 本质:Spring就是一个对象的容器,因为在这个容器中有可以处理请求的对象,所以可以用来写web层,因为有service层对象,所以可以用写service层,因为有dao层对象,所以可以用来操作数据库。
  • Spring的开放性和包容性非常强,无缝整合第三方优秀框架,不会重复造轮子。
  • Spring的框架结构

1.jpg

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技术的最佳实践的范例。

2.jpg

注意: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雏形)
  • 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对象
  1. public class Account {
  2. private Integer id;
  3. //账户名
  4. private String name;
  5. //账户金额
  6. private Double money;
  7. }
  • service层
  1. public interface AccountService {
  2. public void saveAccount(Account account);
  3. }
  1. public class AccountServiceImpl implements AccountService {
  2. @Override
  3. public void saveAccount(Account account) {
  4. AccountDao accountDao = (AccountDao) BeanFactory.getInstance("accountDao");
  5. accountDao.saveAccount(account);
  6. }
  7. }
  • dao层
  1. public interface AccountDao {
  2. void saveAccount(Account account);
  3. }
  1. public class AccountDaoImpl implements AccountDao {
  2. @Override
  3. public void saveAccount(Account account) {
  4. System.out.println("模拟保存账户");
  5. }
  6. }
  • bean工厂
  1. public class BeanFactory{
  2. public static <T> T getInstance(Class<T> clazz){
  3. ResourceBundle resourceBundle = ResourceBundle.getBundle("bean");
  4. String simpleName = clazz.getSimpleName();
  5. String className = resourceBundle.getString(simpleName);
  6. T t = null;
  7. try{
  8. Class clz = Class.forName(className);
  9. t = (T)clz.newInstance();
  10. }catch(Exception ex){}
  11. return t;
  12. }
  13. }
  • bean.properties
  1. UserService=com.atguigu.service.impl.UserServiceImpl
  2. 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 &lt; 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 命名空间的名字,定义的包名

官方要求,命令空间的名字,必须全球唯一性