Spring

Spring Framework

Spring 简介

Spring 框架即以 interface 21 框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0版本。创始人是 Rod Johnson
Spring 是一个轻量级__控制反转(IoC)__和__面向切面(AOP)__的容器框架。
Spring 框架由 7 部分组成: Spring CoreSpring AOPSpring ROMSpring DAOSpring WebSpring ContextSpring Web MVC
Spring 框架所需要的 Jar 包

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.3</version>
    </dependency>
</dependencies>

控制反转(IoC)

IoC 是 Inversion of Control 的缩写,多数书籍翻译成“控制反转”。1996年,Michael Mattson 在一篇有关探讨面向对象框架的文章中,首先提出了 IoC 这个概念。
IoC 理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦。
对象获得依赖对象的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

控制反转(IoC)与依赖注入(DI)

2004年,Martin Fowler 探讨了一个问题,既然 IoC 是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由 IoC 容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现 IoC 的方法:注入。所谓依赖注入,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中。

所以,依赖注入(DI)和控制反转(IoC)是从不同的角度描述的同一件事情,就是指通过引入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦。

IoC 中最基本的技术就是“反射(Reflection)”编程。

Spring 的 xml 配置文件

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

注:

  1. 这个 id 属性是标识单个 bean 定义的字符串。
  2. 这个 class 属性定义 bean 的类型,需要使用完全限定的类名。

依赖注入(DI)的方式

基于构造函数的依赖注入
package cn.worstone.bean;

public class User {

    private String name;
    private int age;

    public User(){
        super();
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  1. 构造函数参数解析

    <bean id="user" class="cn.worstone.bean.User" name="userBean">
        <constructor-arg type="int" value="26"></constructor-arg>
        <constructor-arg type="java.lang.String" value="XXX"></constructor-arg>
    </bean>
    
  2. 构造函数参数类型匹配

    <bean id="user" class="cn.worstone.bean.User" name="userBean">
        <constructor-arg index="1" value="26"></constructor-arg>
        <constructor-arg index="0" value="XXX"></constructor-arg>
    </bean>
    
基于 Setter 的依赖注入
package cn.worstone.bean;

import java.util.*;

public class User {

    private String name;
    private int age;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String, String> cards;
    private Set<String> games;
    private Properties properties;

    public User(){
        super();
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public void setCards(Map<String, String> cards) {
        this.cards = cards;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

}

package cn.worstone.bean;

public class Address {
    private String address;

    public void setAddress(String address) {
        this.address = address;
    }

}

<bean id="user" class="cn.worstone.bean.User" name="userBean">
    <property name="age" value="26"></property>
    <property name="name" value="孙传振"></property>
    <property name="address" ref="address"></property>
    <property name="books">
        <array>
            <value>《红楼梦》</value>
            <value>《水浒传》</value>
            <value>《西游记》</value>
            <value>《三国演义》</value>
        </array>
    </property>
    <property name="hobbies">
        <list>
            <value>绘画</value>
            <value>编程</value>
            <value>音乐</value>
            <value>跑步</value>
        </list>
    </property>
    <property name="cards">
        <map>
            <entry key="身份证" value="210381199502280000"></entry>
            <entry key="银行卡" value="690000000000000000"></entry>
        </map>
    </property>
    <property name="games">
        <set>
            <value>王者荣耀</value>
            <value>英雄联盟</value>
            <value>刺客信条</value>
        </set>
    </property>
    <property name="properties">
        <props>
            <prop key="username">first</prop>
            <prop key="password">19326</prop>
        </props>
    </property>
</bean>

<bean id="address" class="cn.worstone.bean.Address">
    <property name="address" value="辽宁省海城市"></property>
</bean>
XML 快捷方式
  1. 具有 p-namespace 的 XML 快捷方式

    使用 p-namespace,您可以使用 bean 元素的属性(而不是嵌套 <property/> 元素)来协作描述 bean 的属性值。
    需要添加 XML 文件约束

    xmlns:p="http://www.springframework.org/schema/p" 
    
    <bean id="user" class="cn.worstone.bean.User" name="userBean" p:age="26" p:address-ref="address">
    	<property name="name" value="XXX"></property>
    </bean>
    
  2. 具有 c-namespace 的 XML 快捷方式

    使用 c-namespace,您可以使用 bean 元素的属性(而不是嵌套 <constructor-arg/> 元素)来协作描述 bean 的属性值。
    需要添加 XML 文件约束

    xmlns:c="http://www.springframework.org/schema/c" 
    
    <bean id="user" class="cn.worstone.bean.User" name="userBean" c:age="26" c:name="XXX" c:address-ref="address">
        <constructor-arg name="age" value="26"></constructor-arg>
        <constructor-arg name="name" value="XXX"></constructor-arg>
        <constructor-arg name="address" ref="address"></constructor-arg>
    </bean>
    
    <bean id="user" class="cn.worstone.bean.User" name="userBean" c:_0="XXX" c:_1="26" c:_2-ref="address">
        <constructor-arg index="0" value="XXX"></constructor-arg>
        <constructor-arg index="1" value="26"></constructor-arg>
        <constructor-arg index="2" ref="address"></constructor-arg>
    </bean>
    

在 Spring 中使用 ApplicationContext 去获取 bean 的时候,在加载 xml 配置文件的时候,会创建配置文件中所有的 bean。

Bean Scopes

scopedescription
singleton将单个 bean 定义范围限定为单个对象实例。(默认值)
prototype将单个 bean 定义的作用域限定为任意数量的对象实例。
request将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义的后面创建的 bean 实例.仅在支持 web 的 Spring ApplicationContext 上下文中有效。
session将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在支持 web 的 Spring ApplicationContext 上下文中有效。
application将单个 bean 定义的作用域限定为 ServletContext 的生命周期。仅在支持 web 的 Spring ApplicationContext 上下文中有效。
websocket将单个 bean 定义的作用域限定为 WebSocket 的生命周期。仅在支持 web 的 Spring ApplicationContext 上下文中有效。

Bean 生命周期回调

从Spring 2.5开始,可以使用三个方式来控制 bean 生命周期行为:

  1. 实现 InitializingBeanDisposableBean 回调接口。

  2. 自定义 init()destroy() 方法。

  3. 通过 @PostConstruct@PreDestroy 注解。

通常,@PostConstruct@PreDestroy 注解被认为是在现代 Spring 应用程序中接收生命周期回调的最佳方式。使用这些注解意味着 bean 没有耦合到特定的 Spring 接口。如果不希望使用注解,但仍然要删除耦合,可以考虑使用 <bean/> 标签的 init-methoddestroy-method 属性自定义 init()destroy() 方法。

为同一个 bean 初始化方法的调用顺序为:

  1. 用注解 @PostConstruct 的方法
  2. 重写由 InitializingBean 回调接口定义的 afterPropertiesSet() 方法
  3. 自定义的 init() 方法

销毁方法的调用顺序相同:

  1. 用注解 @PreDestroy 的方法
  2. 重写由 DisposableBean 回调接口定义的 destroy() 方法
  3. 自定义的 destroy() 方法
初始化回调
  1. 自定义的 init() 方法
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}
  1. 实现 InitializingBean 回调接口(该方法不推荐,会让 bean 耦合到 Spring 的接口中)
public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}
销毁回调
  1. 自定义的 destroy() 方法
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}
  1. 实现 DisposableBean 回调接口(该方法不推荐,会让 bean 耦合到 Spring 的接口中)
public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
默认初始化方法和销毁方法

如果生命周期回调方法的名称在整个项目中标准化,所有开发人员都使用相同的方法名称并确保一致性。则可以将 Spring 容器配置为查找在每个 bean 初始化以及销毁时的回调方法名称。

public class DefaultUserService implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.userDao == null) {
            throw new IllegalStateException("The [userDao] property must be set.");
        }
    }
}
<beans default-init-method="init">
    <bean id="userService" class="com.something.DefaultUserService">
        <property name="userDao" ref="userDao" />
    </bean>
</beans>

<beans/> 标签的 default-init-method 属性使 Spring IoC 容器将 bean 中的init() 方法识别为 bean 初始化回调的方法。在创建和组装 bean 时,如果 bean 有 init() 方法,则会在适当的时间调用它。

也可以使用 <beans/> 标签的 default-destroy-method 属性,以类似的方式(在XML中)配置 destroy() 方法。

自动装配

Spring容器可以自动装配协作bean之间的关系。您可以通过检查的内容,让Spring为您的bean自动解决协作者(其他bean)ApplicationContext。自动装配具有以下优点:

  • 自动装配可以大大减少指定属性或构造函数参数的需要。
  • 随着对象的发展,自动装配可以更新配置。例如,如果您需要向类中添加依赖项,则无需修改配置即可自动满足该依赖项。因此,自动装配在开发过程中特别有用,而不必担心在代码库变得更稳定时切换到显式接线的选择。

使用基于XML的配置元数据时,您可以使用元素的autowire属性为 bean定义指定自动装配模式<bean/>。自动装配功能具有四种模式。您可以为每个bean指定自动装配,因此可以选择要自动装配的装配。下表描述了四种自动装配模式:

模式解释
no(默认)无自动装配。Bean引用必须由ref元素定义。对于大型部署,不建议更改默认设置,因为明确指定协作者可提供更大的控制力和清晰度。在某种程度上,它记录了系统的结构。
byName按属性名称自动布线。Spring寻找与需要自动装配的属性同名的bean。例如,如果一个bean定义被设置为按名称自动装配,并且包含一个master属性(即它具有一个 setMaster(..)方法),那么Spring将查找一个名为的bean定义,master并使用它来设置该属性。
byType如果容器中恰好存在一个该属性类型的bean,则使该属性自动装配。如果存在多个错误,则将引发致命异常,这表明您可能无法byType对该bean使用自动装配。如果没有匹配的bean,则什么都不会发生(未设置该属性)。
constructor类似于byType但适用于构造函数参数。如果容器中不存在构造函数参数类型的一个bean,则将引发致命错误。

使用byTypeconstructor自动装配模式,您可以装配阵列和键入的集合。在这种情况下,将提供容器中与期望类型匹配的所有自动装配候选,以满足相关性。Map如果期望的密钥类型为,则可以自动连接强类型实例String。自动装配Map 实例的值包括与期望类型匹配的所有Bean实例,并且 Map实例的键包含相应的Bean名称。

自动接线的局限性和缺点

当在项目中一致使用自动装配时,自动装配效果最佳。如果通常不使用自动装配,则可能使开发人员仅使用自动装配来连接一个或两个bean定义感到困惑。

考虑自动装配的局限性和缺点:

  • 显式依赖项propertyconstructor-arg设置始终会覆盖自动装配。您不能自动装配简单的属性,例如基元 Strings,和Classes(以及此类简单属性的数组)。此限制是设计使然。
  • 自动装配不如显式接线精确。尽管如前所述,Spring还是谨慎地避免在可能导致意外结果的含糊不清的情况下进行猜测。Spring管理的对象之间的关系不再明确记录。
  • 接线信息可能不适用于可能会从Spring容器生成文档的工具。
  • 容器中的多个bean定义可能与要自动装配的setter方法或构造函数参数指定的类型匹配。对于数组,集合或 Map实例,这不一定是问题。但是,对于需要单个值的依赖项,不会任意解决此歧义。如果没有唯一的bean定义可用,则会引发异常。

在后一种情况下,您有几种选择:

  • 放弃自动布线,转而使用明确的布线。
  • 通过将其autowire-candidate属性设置为来避免自动装配bean定义。false
  • 通过将primary<bean/>元素的属性设置为,将单个Bean定义指定为主要候选者 true
  • 通过基于注释的配置实现更细粒度的控件。

基于注释的容器配置

在配置Spring时,注释是否比XML更好?

基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”。简短的答案是“取决于情况”。长的答案是每种方法都有其优缺点,通常,由开发人员决定哪种策略更适合他们。由于定义方式的不同,注释在声明中提供了很多上下文,从而使配置更短,更简洁。但是,XML擅长连接组件而不接触其源代码或重新编译它们。一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注释的类不再是POJO,而且,配置变得分散并且难以控制。

无论选择如何,Spring都可以容纳这两种样式,甚至可以将它们混合在一起。值得指出的是,通过其JavaConfig选项,Spring允许以非侵入方式使用注释,而无需接触目标组件的源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值