zoukankan      html  css  js  c++  java
  • 20191103 《Spring5高级编程》笔记-第3章

    第3章 在Spring中引入IoC和DI

    依赖注入是IOC的一种特殊形式,尽管这两个术语经常可以互换使用。

    3.1 控制反转和依赖注入

    IOC的核心是DI,旨在提供一种更简单的机制来设置组件依赖项,并在整个生命周期中管理这些依赖项。需要某些依赖项的组件通常被称为依赖对象,或者在IOC的情况下被称为目标对象。

    IOC可以分解为两种子类型:依赖注入(DI)和依赖查找。

    3.2 控制反转的类型

    使用依赖查找时,组件必须获取对依赖项的引用,而是用依赖注入时,依赖项将通过IOC容器注入组件。

    依赖查找有两种类型:依赖拉取(dependency pull,DL)和上下文依赖查找(contextualized dependency lookup,CDL)。

    依赖注入有两种风格:构造函数和setter依赖注入。

    3.2.6 setter注入与构造函数注入

    使用接口来定义依赖项的能力是setter注入的一个经常被提及的好处,但实际上,应该尽量保持setter仅用于诸如接口。除非完全确定特定业务接口的所有实现都需要一个特定的依赖项,否则让每个实现类定义自己的依赖项并为业务方法保留业务接口。

    虽然不应该在业务接口中放置用于依赖项的setter方法,但在业务接口中放置用来获取配置参数的setter和getter方法却是一个好主意。可以将配置参数视为依赖项的特例。

    应该根据使用情况选择诸如类型。基于setter的注入预序在不创建新对象的情况下交换依赖项,并且还可以让类选择适当的默认值,而无需显式注入对象。但想要确保将依赖项传递给组件和设计不可变对象时,构造函数注入是一个不错的选择。

    3.4 Spring中的依赖注入

    3.4.1 bean和BeanFactory

    Spring的依赖注入容器的核心是BeanFactory接口。BeanFactory负责管理组件,包括依赖项以及它们的生命周期。在Spring中,术语bean用于引用由容器管理的任何组件,bean配置由实现BeanDefinition接口的类的实例表示。bean配置不仅存储有关bean本身的信息,还存储有关它所依赖的bean的信息。

    3.4.2 BeanFactory实现

    在声明Spring XSD位置时,最好不要包含版本号。这个问题已经由Spring代为处理,因为版本化的XSD文件是通过spring.schema文件的指针配置的。该文件驻留在定义为项目依赖项的spring-beans模块中。这样做也可以防止升级到新版本的Spring时对所有的bean文件进行修改。

    3.4.3 ApplicationContext

    在Spring中,ApplicationContext接口是BeanFactory的一个扩展。在开发基于Spring的应用程序时,建议通过ApplicationContext接口与Spring进行交互。

    3.5 配置ApplicationContext

    3.5.2 基本配置概述

    <context:component-scan>标记告诉Spring扫描代码,从而找到使用@Component@Controller@Service@Repository注解的注入bean以及支持在指定包(及其所有子包)下使用@Autowired@Inject@Resource注解的bean。在<context:component-scan>标记中,可以使用逗号、分号和空格作为分隔符来定义多个包。此外,该标记支持组件扫描的包含和排除,从而实现更细粒度的控制。

    3.5.3 声明Spring组件

    @ComponentScan注解与<context:component-scan>标签等效。

    配置类可以使用@ImportResource从一个或多个XML文件中导入bean定义。

    p名称空间没有在XSD文件中定义,而只存在于Spring Core中;因此,在schemaLocation属性中没有声明XSD。

    c名称空间没有在XSD文件中定义,而只存在于Spring Core中;因此,在schemaLocation属性中没有声明XSD。

    当有多个构造函数参数或者你的类有多个构造函数时,需要为每个<constructor-arg>标记指定一个index属性,以指定构造函数签名中参数的索引,从0开始。

    对于注解式的构造函数注入,可以通过将注解@Value直接应用于目标构造函数方法来避免混淆。

    @Autowired注解只能应用于一个构造函数方法,如果将该注解应用于多个构造函数方法,Spring会在启动时报错。

    Spring中支持的第三种依赖注入被称为字段注入。依赖项直接注入字段中,不需要构造函数或setter。字段注入通过使用@Autowired注解来注解类成员完成。

    通过分别使用<property><constructor-args>标记,可以将注入参数用于setter注入和构造函数注入。

    @Valuea可以直接应用于属性声明语句。

    @Value("tc..p1")
    private String p1;
    

    被注入的类型不一定时目标bean上所定义的确切类型:类型只需要兼容即可。兼容意味着如果目标bean上声明的类型是一个接口,那么注入类型必须实现此接口。如果声明的类型是一个类,那么注入类型必须是相同类型或子类型。

    注入和ApplicationContext嵌套

    Spring支持ApplicationContext的层次结构,一个上下文可以成为另一个上下文的父级。

    当父子上下文中存在名称相同的bean时,<ref>标签通过bean属性获得子上下文中的bean,通过parent属性获取父上下文中的bean。

    注入集合

    可以选择<list><map><set><props>来表示ListMapSetProperties实例。<props>标记只允许将String作为值传入。

    利用Spring提供的util名称空间来声明用来存储集合属性的bean,可以极大地简化配置。

    对于集合类型注入,必须明确指示Spring通过指定@Resource注解支持的bean名称来执行注入。因为@Autowired注解的语义定义方式是,始终将数组、集合和映射视为相应bean的集合,而目标bean类型从声明的集合值类型派生。例如,如果一个类具有List<ContentHolder>类型的属性并且定义了@Autowired注解,那么Spring会尝试将当前ApplicationContext中所有ContentHolder类型的bean注入该属性。

    3.5.4 使用方法注入

    除了构造函数注入和setter注入,Spring提供的另一个不常用的DI功能是方法注入。Spring方法注入功能有两种形式:查找方法注入(Lookup Method Injection)和方法替换(Method Replacement)。查找方法注入提供了一种机制,bean可以获得它的一个依赖项;而方法替换允许随意替换bean上任何方法的实现,而无需修改原始代码。

    <bean id="abstractLookupDemoBean" class="study.hwj.chapter03.lookup.AbstractLookupDemoBean">
        <lookup-method name="getMySinger" bean="singer"/>
    </bean>
    

    StopWatch类是Spring提供的一个实用类。用来执行简单的性能测试。

    @Scope注解:

    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class Singer {
        ...
    }
    

    @Lookup注解:

    @Lookup("singer")
    public void sing() {}
    

    查找方法注入的注意事项

    当想要使用两个具有不同生命周期的bean时,可以使用查找方法注入。当bean共享相同的生命周期时,应该避免使用查找方法注入,尤其当这些bean都是单例时。

    基于XML配置的查找方法没必要一定抽象化,抽象化可以防止忘记配置查找方法,也可以使用空白的实现。基于注解的配置强制方法的空白实现;否则,不会创建bean。

    方法替换

    通过使用方法替换,可以任意替换任何bean的任何方法的实现,而无需更改所修改bean的源代码。

    Spring在内部使用CGLIB将想要替换的方法的调用重定向为另一个实现了MethodReplacer接口的bean。

    在有重载方法的情况下,可以使用<arg-type>标记指定要匹配的方法签名。<arg-type>标记支持模式匹配,因此String可以与java.lang.String以及java.lang.StringBuffer匹配。

    <bean id="methodReplacer" class="study.hwj.chapter03.methodreplace.FormatMessageReplacer"/>
    
    <bean id="replacementTarget" class="study.hwj.chapter03.methodreplace.ReplacementTarget">
        <replaced-method name="formatMessage" replacer="methodReplacer">
            <arg-type>String</arg-type>
        </replaced-method>
    </bean>
    

    建议为每个方法或一组重载的方法使用一个MethodReplacer。应该避免针对多个不相关的方法使用单个MethodReplacer

    3.5.5 了解bean命名

    每个bean在其所在的ApplicationContext中至少包含一个唯一的名称。如果为<bean>标记赋予一个id属性,那么该属性的值将用作名称。如果没有指定id属性,Spring会查找name属性,如果定义了name属性,那么将使用name属性中定义的第一个名称(name属性可以定义多个名称)。如果既没有id,也没有name,那么Spring使用该bean的类名作为名称。如果声明了多个没有ID或名称的相同类型的bean,那么Spring将在ApplicationContext初始化期间,在注入时抛出异常。

    作为一种惯例,应该使用id属性作为bean的名称,然后使用名称别名将bean与其他名称相关联。

    bean名称别名

    Spring允许一个bean拥有多个名称。可以通过在<bean>的name属性中指定以空格、逗号或分号分隔的名称列表来实现多个名称。name属性可以替代id属性,或者组合使用这两个属性。除了使用name属性,还可以使用<alias>标记定义别名。

    可以通过调用ApplicationContext.getAliases(String)并传入任何bean的名称或id来获取bean的别名列表。别名列表以字符串数组返回(不包含传入的名称)。

    Spring IOC对name和id属性值的处理方式不同。

    <!-- 这个bean的id为【jon johnny,jonathan;jim】 -->
    <bean id="jon johnny,jonathan;jim" class="java.lang.String"/>
    

    使用注解配置的bean命名

    当使用注解声明bean定义时,bean命名与XML稍有不同。

    @Component注解没有任何参数,在这种情况下遵循的惯例是将bean命名为类本身,但是将首字母小写,其他构造型注解也遵循该惯例。使用@Component("singer")相当于使用@Component注解Singer类。如果项以不同的方式命名bean,@Component注解必须接受bean名称作为参数。由于@Component的参数成为bean的唯一标识符,以这种方式声明bean时不可以使用别名。

    使用@Bean注解方法声明时,没有为@Bean提供任何参数时,方法名就是bean的id。要声明别名,可以使用name属性,如果该属性的值是要给字符串数组,第一个数组值成为id,其他值成为别名。

    在Spring4.2中引入了@AliasFor注解。该注解用于为注解属性声明别名。还可以声明元注解属性的别名。

    使用@AliasFor为注解的属性创建别名是有一定局限性的。@AliasFor不能用于任何构造型注解(@Component及其特例),还有@Qualifier注解,因为出现的晚。

    3.5.6 了解bean实例化模式

    选择实例化模式

    关于<bean>scope属性:

    单例(singleton)应该在下列情况下使用:

    1. 没有状态的共享对象;
    2. 具有只读状态的共享对象;
    3. 具有共享状态的共享对象;
    4. 具有可写状态的高通量(hign-throughput)对象;

    非单例(prototype):

    1. 具有可写状态的对象;
    2. 具有私有状态的对象;

    实现bean作用域

    Spring从版本4开始支持以下bean作用域:

    1. 单例作用域:默认;
    2. 原型作用域;
    3. 请求作用域:用于Web应用程序,针对每个HTTP请求;
    4. 会话作用域:用于Web应用程序,针对每个HTTP会话;
    5. 全局会话作用域:用于基于Portlet的Web应用程序。带有全局会话作用域的bean可以在同一个Spring MVC驱动的Web应用程序的所有Portlet之间共享;
    6. 线程作用域:当一个新线程请求bean实例时,Spring将创建一个新的bean实例,而对于同一个线程,返回相同的实例。线程作用域默认情况下未注册;
    7. 自定义作用域:通过实现 org.springframework.beans.factory.config.Scope 接口创建自定义作用域,并在Spring配置中注册自定义作用域(对于XML,使用 org.springframework.beans.factory.config.CustomScopeConfigurer );

    3.6 解析依赖项

    可以使用<bean>depends-on属性为Spring提供有关bean的依赖项的附加信息。

    ApplicationContextAware 接口是一个特定于Spring的接口,它为 ApplicationContextAware 对象强制实现一个setter。Spring IOC容器会自动进行检测,并且注入bean段创建时所在的 ApplicationContextAware 。这一切都是在调用bean的构造函数之后完成的,因此在构造函数中使用 ApplicationContextAware 将导致 NullPointerException 异常。

    当开发应用程序时,应该避免应用程序使用此功能;相反,应该通过setter和构造函数注入协定来定义依赖项。但是如果将Spring与遗留代码集成在一起,可能会发现代码中定义的依赖项要求向Spring框架提供额外的信息。

    3.7 自动装配bean

    Spring支持五种自动装配模式:

    1. byName模式:Spring尝试将每个属性连接到同名的bean。如果目标bean中具有名为foo的属性并且在ApplicationContext中定义了foo bean,那么foo bean将被分配给目标bean的foo属性;
    2. byType模式:Spring通过在ApplicationContext中自动使用相同类型的bean来尝试连接目标bean模式的每个属性;
    3. 构造函数模式:与byType模式功能上相同。Spring试图匹配构造函数中最大数量的参数;
    4. 默认模式:Spring将自动在构造函数模式和byType模式之间进行选择。如果bean有一个默认的(无参)构造函数,那么就是用byType模式;否则,使用构造函数模式;
    5. 无:默认。

    使用注解定义bean时,默认的自动装配模式是byType。

    当有多个相同类型的bean时,可以使用primaryqualifier指定。

    在大多数情况下,不应该使用自动装配。

    3.8 设置bean继承

    如果使用共享的属性值声明大量具有相同值的bean,则应该避免使用复制粘贴来共享值,而是应该在配置中设置集成层次结构。

    bean继承不必与Java继承层次结构相匹配。与其说bean继承是一项继承功能,还不如说更像是模板功能。如果要更改子bean的类型,该类型必须扩展父bean的类型。

  • 相关阅读:
    groovy脚本语言基础1
    014.Ansible Playbook Role 及调试
    013.Ansible Playbook include
    012.Ansible高级特性
    011.Ansible条件语句
    010.Ansible_palybook 循环语句
    009.Ansible模板管理 Jinja2
    008.Ansible文件管理模块
    007.Ansible变量Fact,魔法变量和lookup生成变量
    006.Ansible自定义变量
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/11788914.html
Copyright © 2011-2022 走看看