spring核心技术梳理之依赖注入

一、Ioc容器:

  1. org.springframwork.beanorg.springframwork.context这两个包里的接口与实现类提供了基础的控制反转(转化)工具,其中BeanFactory(实例工厂)提供先进的配置机制,并有能力管理任何类型的对象。

  2. ApplicationContextBeanFactory的子接口,它增加了:
    2-1):容易整合spring的aop的特征
    2-2):消息资源处理(用于初始化)
    2-3):事件发布
    2-4):应用程序层特定上下文,例如WebApplicationContext,被用于广域网应用

  3. 对象实例是应用的脊梁骨,会被bean 的spring的ioc容器来管理,一个bean是一个对象,它可被初始化,被装配,被管理于控制转化容器。

  4. org.springframwork.context.ApplicationContext接口代表Ioc容器,有责任去初始化,装配,管理bean,而这些操作是根据容器的指令通过读取配置元数据来完成的,配置元数据是通过xml,java注解或者java代码来表示的,表示这些对象构成了我们的对象

  5. 数个ApplicationContext接口的实现类是通过spring来提供的,在单独的应用中,它是公有地,去创建ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext类的对象,当xml已经用过渡格式定义了元数据,你仍然可以命令容器去使用少量的xml配置声明的方式,例如通过java注解或者java代码来声明或定义元数据格式

  6. 在很多应用脚本里面,都不被要求用易于理解的用户代码来例示一个或多个ioc容器的对象,例如:在支持广域网内访问的网络应用脚本来说,web应用描述文件web.xml的8行代码的样板文件就足以满足引用示例对象的需求,如果你使用了spring tools for eclipse这个工具,可以更方便的创建样板文件,让工具完成一些本应该是我们需要编写代码完成的工作,从而提高开发效率。

  7. 你的业务对象结合配置文件,在ApplicationContext对象创建和初始化之后,你将拥有完整的配置和执行系统或应用。

二、配置元数据:

  1. 配置元数据是过渡提供一个简单直观的xml格式的配置,但这并不意味这只支持基于xml的元数据,spring的ioc容器提倡完全减少被编写的元数据格式,现如今,多数开发者喜欢使用基于java注解的方式为他们的spring应用提供元数据。
  2. spring配置由一个,通常定义多个bean,由ioc容器来管理基于xml的配置元数据,配置<bean>节点元素,其在父元素<beans>内定义。java配置通常使用在带@Configuratioin注解的类里的带@Bean注解方法,在配置文件里定义的bean相当于真实的对象从而构成你的应用,通常,你定义服务层对象、数据访问对象、描述对象,例如像:struts的 Action 对象,基础对象如Hibernate的 SessionFactories,JMS的Queues诸如此类。在ioc容器内不能配置一个细粒度的对象,因为此类对象拥有业务逻辑需要创建和加载范围内的对象,然而可以通过整合spring的aop框架aspectj来配置已经在外部被创建的ioc容器的控制对象。
  • 基于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>

① id属性是一个字符串标识,用于识别单独、独特、个人的bean定义,即bean的唯一标识符
② class属性定义完整类名及bean的类型

三、初始化容器
通过地址路径提供一个上下文构造器对象ApplicationContext,即将资源用字符串标识,从而让ioc容器从多种多样的外部资源加载配置元数据,例如从本地文件系统、java类路径CLASSPATH等。

ApplicationContext context = new ClassPathXmlApplicationContext("mahmut.xml","dawut.xml");

如下展示服务层对象以及数据访问对象的配置文件(services.xml与daos.xml):

services.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">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

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

</beans>

daos.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="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

如上配置文件描述的一样,服务层对象由PetStoreServiceImpl 实现类和两个数据访问对象JpaAccountDaoJpaItemDao(基于JPA的 对象-关系 映射标准)组成,property name节点元素与属性指向javabean属性,ref属性指向依赖的协作对象

3-1 基于xml的配置元数据的构成
场景:项目中的多个xml文件,每个都代表着单独的业务逻辑或者模块,加载这些多个xml文件可以传入多个xml文件路径给上下文构造器对象的实现类,ioc容器加载之后会对其中的bean初始化,装配,管理这些过渡期间提供功能的对象,或者可以通过配置节点元素<import/>(需在顶层父元素节点<beans/>内配置)从而引入其它配置文件到当前配置文件中,示例如下:

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

resource属性的值使用了斜杠,我们最好提供相对路径,而且不使用斜杠,因为使用斜杠文件位置会受限,比如services.xml必须与当前执行import命令的xml文件处于同一个目录下,二themeSource.xml则需处于当前文件路径之下的某个目录内,因此最好提供相对路径来引入其它xml文件合适,当然提供完整路径是更好的选择。

四、使用容器
使用ApplicationContext的实现类完成了容器的初始化之后,使用ApplicationCotext的T getBean(String name, Class<T> requiredType)方法来获取bean的实例对象,并使用该实例,示例如下:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

虽然可以这么获取容器中的bean的实例,但你一般并不会这么使用,也不会去调用getBean()方法,因为还有其他相对比较简便的方法,spring通过整合spring的web框架提供依赖注入,比如为不同的spring框架的模块提供依赖注入,比如为 controllers 模块,声明特定的bean(通过使用 自动注入 注解)。

五、Bean 概述

5-1:命名bean
(1)使用id标识唯一性
(2)为了统一格式使更易读、更易理解,因此命名使用驼峰命名法,例如:accountManager,accountService,userDao等

5-2:给bean取别名
应用场景:多个组件需要用到公共的依赖bean,这时就需要让多个别名指向此公共bean,需要用到<alias/>节点元素完成此需求,示例如下:

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

5-3:初始化bean

- 5-3-1:使用构造器初始化
(1):在开发bean中声明的类时不被要求实现指定的接口或以某种方式编写代码的特殊要求,取决于你使用的是什么类型的ioc容器,可以简单的使用类的默认构造器来进行初始化。若构造器未提供入参(即未初始化对象属性),则必须声明一个无参构造器。因为spring会利用反射原理来构造一个bean实例,示例如下:(没有通过构造器初始化属性,只是单纯的构造一个bean实例)

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

- 5-3-2:使用静态的工厂方法初始化
(1):使用class属性标识的类内需要包含一个静态的工厂方法,返回值是一个活的对象,示例如下:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

需要强调的是使用属性factory-method声明的该工厂方法 createInstance() 必须是静态的,这是spring要求的机制。

上述定义的bean与工厂方法示例如下:

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

- 5-3-3:使用实例工厂方法初始化

跟静态工厂方法类似,实例工厂方法会在实例中编写好一个非静态的工厂方法,为了使用这个初始化机制,我们需要在元数据配置文件中的bean节点元素继续保留class属性,配置完工厂实例对象之后,这时我们会发现我们并未提供有参构造器,也未提供无参构造器,因此这时spring是没有办法对我们刚配置的bean进行初始化的,因此紧接着我们需要配置一个bean节点关联上面的bean节点,告诉它你要用我下面声明的非静态的工厂方法来对你的bean实例进行初始化,元数据配置示例如下:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

如上所示的bean节点的factory-bean属性需要相对应先前定义的bean节点所定义的工厂类的唯一标识,然后还需要声明factory-method属性需要声明工厂类提供的非静态的工厂方法名,这时spring才可以通过factory-bean的factory-method方法来初始化一个bean实例。

如下是相关的类定义:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

六、依赖注入

依赖注入使得项目代码结构简洁,省去了许多冗余代码,有效的实现了解耦,使得实例不需要查找依赖,并且不需知道类地址,这样也便于测试

6-1基于构造器的依赖注入:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
  • 6-1-1构造器的参数处理

示例类如下:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

元数据配置如下:

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

在用构造器依赖注入的时候,有参数的时候,如果是类类型,我们就不用指定参数类型,spring可以自己进行匹配,但是一些基本类型的参数,例如:<value>true</value>,这种入参若不指明,spring匹配不了值类型,因此基本类型应该使用type属性标识数据类型,示例类如下:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

元数据配置如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

如果构造器里有两个相同数据类型的参数,初始化时可以使用index属性来避免歧义,示例如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

也可以通过声明参数名来让spring区分以避免歧义,示例如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

6-2 通过setter方法依赖注入

ioc容器先调用setter方法,然后再调用空参的构造器或者无参的静态工厂方法

示例如下:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

基于xml配置的,基于setter方法依赖注入的配置元数据示例如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

示例配置元数据的对于java示例类如下:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

为了与基于构造器的依赖注入进行比较,提供如下示例配置元数据如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

基于构造器依赖注入的配置元数据相对应的示例类如下:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

也可以使用p命名空间,示例如下:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="misterkaoli"/>

</beans>

同上目的:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>

6-3依赖注入详解之注入协作对象:

对构造器注入、setter方法注入统一使用ref标签或者ref属性来指向所需注入的协作对象,
ref元素节点需要声明在<constractor-arg/><property/>节点元素内。

基于setter方法依赖注入元数据配置示例如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>


---------------------------------------------------------------------------------------------
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>

基于构造器依赖注入元数据配置示例如下:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

大多数情况下依赖注入我们会使用ref标签,使用bean属性指向需要依赖的对象,bean属性值与依赖对象的id或name属性一致即可,示例如下:

<ref bean="accountService"/>

ref 标签也可以使用parent属性指向,但spring4.0之后不再提供支持了,所以我们应该使用ref标签的bean属性来引用依赖对象

6-4 : bean对象的属性可以是强类型集合:
强类型数据类型,可以使用spring的类型转换支持来识别这些强类型集合数据类型,示例如下:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

配置元数据值为空或者NULL的情况,示例如下:

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

如上两个配置等用于java代码中的如下代码:
exampleBean.setEmail("");
exampleBean.setEmail(null);

6-5 : 使用depends-on

通常情况下,类之间有协作者类,我们会使用<ref/>节点,使用bean属性指向协作者类,但是我们大多数情况下类之间很少有直接的联系,意思是我们访问数据库的类程序属性与数据库驱动注册程序没有直接的属性关联关系,也没有父子包含关系,只不过我的类程序运行前需要触发调用数据库驱动,只有先触发了数据库驱动类,我的类程序才能正常访问数据库,并进行相关操作。
示例如下:

多个协作者类可以通过逗号(,)、分号(;)或者用空格( )隔开
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

6-6:懒加载
为了提升运行效率,减少内存占用,一些类我们不需要最开始就加载,我们可以在第一次发请求的时候去加载它,可以通过配置 bean节点的 lazy-init属性值为true,配置示例如下:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

6-7:自动装配依赖协作者类

  • 使用自动装配可以显著的减少配置文件和构造器初始化参数
  • 如果需要新增依赖,可以不修改配置文件,实现自动装配
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值