2018-06-08

IOC 容器

IOC容器和beans

Spring实现了IOC (Inversion of Control)(控制反转)功能,IOC的实现手段是依赖注入。
依赖注入,一个类依赖其他类的对象,依赖的对象可以通过构造函数、工厂方法、或者一个在运行时set值的属性来实例化。这个类我们叫做bean,这个bean必须要自己去new一个它依赖的对象,或者是显式地使用一个Service Locator模式来实例化以来的对象。

Spring IOC容器的基础包是org.springframework.beansorg.springframework.contextBeanFactory提供了管理bean对象的高等机制。ApplicationContextBeanFactory的子类,它对Spring的几大特性做了整合:AOP(切面编程)消息资源处理(国际化超有用),事件处理机制以及应用层的特殊上下文如WebApplicationContext
简而言之,BeanFactory提供了配置框架的基本能力,而ApplicationContext提供了更加高级能力。ApplicationContext是BeanFactory的超集。
Spring中,bean就是那些构成了应用程序主干,并且由Spring管理的对象。它由Spring实例化,组装(注入),管理(生命周期、范围)。Spring利用配置元数据,通过反射实例化和组装bean。

容器概览

org.springframework.context.ApplicationContext接口提供了Spring容器的基本能力——实例化、配置、组装beans。Spring提供了配置元数据让我们描述、组装应用以及beans之间丰富的相互依赖性。提供配置元数据的方式除了传统的XML配置文件方式以外,还包括注解基于java代码两种方式。后两者能显著减少代码中xml的重复代码数量。

Spring工作流程.PNG

配置元数据

如上图所示,开发者使用配置元数据来告诉Spring如何实例化、配置和组装beans。最传统的配置元数据书写方式就是XML。

下面是一个基于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
        http://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>

实例化容器

我们可使用ApplicationContext来实例化Spring容器

ApplicationContext context = new ClassPathXmlApplicationContext("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
        http://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
        http://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>

容器的使用

// 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();

beans

bean的配置元数据定义了一个bean的以下几个方面:

  • bean的所属类,当然类要使用全限定名
  • bean的行为元数据,例如bean的类型(单例,原型)、生命周期回掉函数等等。
  • 对其他bean的依赖,这些被依赖的bean可以叫做collaborators 或者 dependencies.
  • 一些基本数值型的属性设置

bean的注册方式很灵活,除了让Spring容器根据配置元数据管理以外,还可以通过BeanFactory来注册。

    BeanFactory bf = ((ClassPathXmlApplicationContext) ac).getBeanFactory();
    ((ConfigurableListableBeanFactory) bf).registerSingleton("customHelloWorld", customHelloWorld);
    HelloWorld myHelloWorld = new HelloWorld();
    myHelloWorld.setHelloProcessor( (Hello) ac.getBean("customHelloWorld") );

bean的命名

bean通常都有一个或多个标识(额外的标识就是别名),bean的标识在持有这个bean的容器中必须唯一。
基于xml的配置中,标识符通过id 或者name来定义,通常使用字母数字组合的字符串(alphanumeric),如('myBean', 'fooService', etc.),官方建议使用驼峰命名法。
bean也可以没有名字,但是这样子的bean无法通过名称来被其他bean引用了,只能作为inner bean 或者是通过自动装配机制来被使用。

bean的别名

有时候,我们需要引用一个外部定义的bean的定义,这时候,往往无法修改外部系统的bean的名称。此时一种解决方案就是我们使用外部定义的bean的id来引用这个bean。Spring提供了别名让我们能够在本系统中给这个外部定义的bean一个别名:

<alias name="fromName" alias="toName"/>

例如,系统A中有一个数据源bean,我们在系统B中要给这个bean一个别名:

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

bean的实例化

Spring使用beanDefinition来创建bean实例。以基于xml的配置来说,Spring提供两种方式来实例化bean:

  • Spring利用反射机制调用构造函数来实例化bean,基本等同于new

  • Srping通过调用class中的静态工厂方法,用返回的对象来作为bean

构造函数实例化
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
工厂方法实例化
<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"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

依赖

    A对象需要使用到B对象和C对象的某些能力,我们一般利用组合来实现这样的功能,因此说,A依赖B和C。
依赖描述了多个对象之间的合作关系。很显然,几乎所有应用程序都有许多彼此依赖的对象相互合作,并提供给最终用户一个连贯的功能。

依赖注入(Dependency Injection)

    对象仅仅通过构造函数的参数、工厂方法的参数、对象本身的属性来声明依赖,而容器会自动生成依赖项的实例并注入到对象中。这个过程就是依赖注入。对象没有自己在运行期间去负责实例化自己的依赖项,而是由容器来负责注入。对象将处理依赖的控制权移交给了容器,这种特性称之为控制反转。
由此可见,依赖注入是实现控制反转的一种手段,它让我们的代码更简洁而且松耦合。
依赖注入的两种主要方式:

  • 基于构造函数的注入
  • 基于setter的注入

基于构造函数的注入

这种注入方式在调用构造函数的时候完成,调用工厂方法的方式与此类似。
如果构造函数存在多个参数,且它们的类型没有继承关系,则它们在构造函数参数中的位置,即可作为xml定义中的位置,如下例子

package x.y;

public class Foo {

    public Foo(Bar bar, Baz baz) {
        // ...
    }
}
<beans>
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
    </bean>

    <bean id="bar" class="x.y.Bar"/>

    <bean id="baz" class="x.y.Baz"/>
</beans>

假设Bar类与Baz类之间没有任何继承关系,在XML配置中,只要保持两个bean的引用顺序与构造函数中两个参数的顺序一致即可。

假如两个类之间有继承关系(或者可以相互转化),则需要在xml配置中显式的使用类型限定,如下例子:

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

或者使用索引

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

当然还可以使用名字

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

如果要使用名字的话,需要编译选项中保持debug标志,否则,需要使用 @ConstructorProperties 注解:

package examples;

public class ExampleBean {

    // Fields omitted

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

使用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...
}

依赖解析的过程

  • bean定义在配置元数据中,ApplicationContext使用配置元数据来创建并初始化。配置元数据的来源有三种:
    a. xml
    b. 基于java代码
    c. 基于注解
  • bean声明了它的依赖(构造函数参数,工厂方法的参数,属性等)。当bean实例化的时候,他依赖的对象会被容器注入。
  • 对象的依赖都是值或者是其他bean
  • 值会被容器尝试转化为声明的类型,Spring支持将String转化为内置类型:intlongStringboolean等等。

一个比较有趣的情况是循环依赖(Circular dependencies)
如果A依赖B,B也依赖A,那么就是循环依赖,如果使用构造函数注入的方式,很显然,要注入的依赖项需要被完全初始化,因此大家都玩不起来,于是Spring抛出一个运行时异常BeanCurrentlyInCreationException
一种常用且有效的解决方案是使用setter注入方式来替代。换句话说,setter注入能支持循环依赖的场景。

当容器启动时,会校验bean的配置。bean的属性在创建时被设置。单例模式的bean默认在容器启动时实例化,其他类型的bean在使用的时候实例化。
预先初始化的方式能够让配置问题提前暴露,减少运行过程中出错的概率。代价是启动时间长,且很多bean在不需要的情况下已经创建好,占用内存。
lazy-initialize这时候很有用。

Bean的定义

直接赋值

<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="masterkaoli"/>
</bean>

也可以使用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
    http://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="masterkaoli"/>

</beans>

properties

如前文提到的,Spring内部会尝试将String转化为属性类型,一种很好用的玩法如下所示:

<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

PropertyPlaceholderConfigurer类的properties属性是java.util.Properties,上面可以直接这样玩。

idref

idref一般用来判断某个bean的id是否真的存在

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

完全等价于下面的写法

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>

不过我觉得这种写法没什么用。

对其他bean的引用

<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>

ref也可以出现在<constructor-arg/>

内部bean(Inner bean)

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

很明显,就这一个bean使用的话,没必要命名了哈哈。

集合

集合超有用

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

集合的合并

集合合并就是子list,map,set,props 覆盖了父list,map,set,props的部分或者全部值。

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>

注意,child bean的props 中有merge="true",因此child bean的adminEmails从parent中继承值,并覆盖了support的值。结果是:

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

parent上的 abstract="true" 意味着这个父型 bean 是个抽象定义,抽象的父bean 往往作为子bean的模板。甚至可以删除parent bean的class属性,这样Spring不会实例化parent bean,只将他作为模板。

集合合并的限制

  • 只支持同类型的合并,如list不可以合并到map上。
  • merge标签必须在子类型上。

强类型集合

java5 以后引入了自动装箱机制,因此数值1, 可以自动装箱为Integeter.valueof(1),看如下例子:

public class Foo {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="foo" class="x.y.Foo">
        <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>

9.99 自动转化为Float。

空字符串和null

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

完全等价于

exampleBean.setEmail("");

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

完全等价于

exampleBean.setEmail(null);

p命名空间

通过在xml中声明

xmlns:p="http://www.springframework.org/schema/p"

即可使用p命名空间来简化property的写法:

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

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="foo@bar.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="foo@bar.com"/>
</beans>
<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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

C命名空间

用来简化constructor-arg元素

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

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- traditional declaration -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="foo@bar.com"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

</beans>

当然也可以使用index的方式:

<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>

设置bean属性的属性的值

<bean id="foo" class="foo.Bar">
    <property name="fred.bob.sammy" value="123" />
</bean>

使用 depends-on

为了处理类之间的依赖关系,我们通常使用ref元素。被依赖的bean就会先初始化好。但有时候,类之间的依赖关系没有这么直接。
比如一个dao对象使用前,需要database driver的注册,就需要database driver的static块先生效。
Spring提供了depends-on元素来处理这种情况。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

当依赖多个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" />

懒初始化 (Lazy-initialized)

前面提到过,单例bean的初始化时机是容器启动时(与先初始化),这样能让配置问题提前暴露。但是有时候这并不适合(不值得),因此可以使用懒加载模式。

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

懒初始化的bean再用到的时候初始化。因此如果一个“懒”bean被一个使用默认预先初始化方式的bean依赖,则它的实例化时机仍然是容器启动时,这似乎并不难理解。
基于xml的配置中,可以用如下方式,让一个config的所有bean都变懒:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

自动装配

为了减少xml配置,也为了增加类属性时不必去修改相关的配置文件,Spring提供了自动注入。只要在bean定义中增加autowire属性即可
autowire的四个值:

  • no, 不自动创配,默认配置就是不自动装配啊!!!
  • byname, 使用属性名自动装配,Spring找和属性同名的bean来自动装配。
  • byType, 使用类型来自动装配,如果多个类型的bean都匹配,Spring抛异常;如果没有匹配的bean,赋值null。
  • constructor, 与类型装配类似,不过只对构造函数的参数生效,如果不能找到一个确定的bean,Spring给一个 错误

自动装配的缺陷

  • 值类型无法自动装配
  • 不够明确
  • Spring容器自动生成文档的工具可能会不可用
  • 可能找到多个匹配的类型,这时候会抛异常或者错误,这时候就需要autowire-candidate来辅助

方法注入

大多数场景中,单例bena很有效,但有时我们需要非单例的bean。如果单例bean依赖一个非单例的bean,由于单例bean的生命周期与非单例bean的生命周期不一致,就会导致问题。
假设一个场景,单例bean 对象A依赖B(B是非单例的),A中每个方法调用的时候,都需要重新获得一个B的新的实例,可是容器只创建A一次,因此只注入B一次,这就导致B的新实例无法获得。
解决方法有两种(说明Spring真的好强):
一种方法一定程度上违背了IOC的思想,因为我们将会自己去实例化B。通过实现ApplicationContextAware接口,Spring容器会负责自动注入一个ApplicationContext给我们,然后我们就可以用它来获得BeanFactory对象,从而获得任意的bean的新实例.

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

这种方法的缺陷很明显:不仅没用上IOC的能力,还要对Spring的接口有一定的了解才行(难不倒我!)。

另一种方法是使用查询方法注入。
注入的方法必须是形如下面的表示:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

然后可以直接在类中调用:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

最后在配置文件中声明这是个查询方法。

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

当调用createCommand方法时,就会返回一个myCommand bean的实例。如果这个bean时非单例的,那么Spring会负责实例化它。

也可以使用注解@Lookup来声明一个查询方法

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract MyCommand createCommand();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,713评论 2 22
  • 本来是准备看一看Spring源码的。然后在知乎上看到来一个帖子,说有一群**自己连Spring官方文档都没有完全读...
    此鱼不得水阅读 6,934评论 4 21
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • 每天通过4个小游戏锻炼身体、大脑、情绪和社交四大意志力可以增加10年的额外寿命是不是超级有诱惑力?研究表明人在经历...
    喵了个喵__阅读 707评论 0 0
  • 关于FFmpeg的资源网上有很多,但是在iOS平台的FFmpeg入门的资源却很少,刚开始学习的时候也是像闷头苍蝇,...
    Aiewing阅读 9,235评论 10 42