单页网站的区别,wordpress怎么做信息流广告,哈尔滨seo服务,展厅搭建公司文章目录 依赖注入基于构造函数的依赖注入基于 Setter 的依赖注入依赖解析过程依赖注入的示例 依赖关系和配置详细信息直接值#xff08;原语、字符串等#xff09;idref标签References to Other Beans #xff08;对其他 Bean的引用#xff09;Inner Beans#xff08;内部… 文章目录 依赖注入基于构造函数的依赖注入基于 Setter 的依赖注入依赖解析过程依赖注入的示例 依赖关系和配置详细信息直接值原语、字符串等idref标签References to Other Beans 对其他 Bean的引用Inner Beans内部beanCollections(集合) Using depends-on使用depends-onLazy-initialized Beans延迟初始化 BeanAutowiring Collaborators 自动装配合作者自动装配具有以下优点自动装配的局限性和缺点从自动装配中排除 Bean 方法注入Lookup Method Injection 查找方法注入任意方法替换 典型的企业应用程序不包含单个对象或 Spring 术语中的 bean。即使是最简单的应用程序也有一些对象一起工作来呈现最终用户所认为的连贯的应用程序。下一节将解释如何从定义多个独立的 bean 定义转变为一个完全实现的应用程序及在该应用程序中的对象协作。
依赖注入
依赖注入 (DI) 是一个过程对象仅通过构造函数参数、工厂方法的参数或对象实例构造后设置的属性来定义其依赖项即与它们一起工作的其他对象。从工厂方法返回。 然后容器在创建 bean 时注入这些依赖项。这个过程从根本上来说是 bean 本身的逆过程因此得名“控制反转”通过使用类的直接构造或服务定位器模式自行控制其依赖项的实例化或位置。
采用 DI 原则代码更加清晰并且当对象提供其依赖项时解耦更加有效。该对象不会查找其依赖项也不知道依赖项的位置或类。因此您的类变得更容易测试特别是当依赖项位于接口或抽象基类上时这允许在单元测试中使用存根或模拟实现。
DI 存在两种主要变体
基于构造函数的依赖注入和 基于 Setter 的依赖注入 。
基于构造函数的依赖注入 基于构造函数的 DI 是通过容器 调用带有多个参数的构造函数来完成的每个参数代表一个依赖项。 与调用具有特定参数的static工厂方法来构造 bean 几乎是等效的并且本讨论以类似方式对待构造函数和static工厂方法的参数。以下示例显示了一个只能通过构造函数注入进行依赖注入的类
public class SimpleMovieLister {// the SimpleMovieLister has a dependency on a MovieFinderprivate final MovieFinder movieFinder;// a constructor so that the Spring container can inject a MovieFinderpublic SimpleMovieLister(MovieFinder movieFinder) {this.movieFinder movieFinder;}// business logic that actually uses the injected MovieFinder is omitted...
}请注意这个类没有什么特别的。它是一个 POJO不依赖于容器特定的接口、基类或注释。
构造函数参数解析: 构造函数参数解析匹配通过使用参数的类型进行。如果 bean 定义的构造函数参数中不存在潜在的歧义则在 bean 定义中定义构造函数参数的顺序就是在实例化 bean 时将这些参数提供给适当的构造函数的顺序。考虑下面的类
package x.y;public class ThingOne {public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {// ...}
}假设ThingTwo和ThingThree类不通过继承相关则不存在潜在的歧义。因此以下配置可以正常工作并且您不需要在 元素中显式指定构造函数参数索引或类型。
beansbean idbeanOne classx.y.ThingOneconstructor-arg refbeanTwo/constructor-arg refbeanThree//beanbean idbeanTwo classx.y.ThingTwo/bean idbeanThree classx.y.ThingThree/
/beans当引用另一个 bean 时类型是已知的并且可以发生匹配如前面示例的情况。 当使用简单类型时例如 valuetrue/valueSpring 无法确定值的类型因此无法在没有帮助的情况下按类型进行匹配。 考虑下面的类
package examples;public class ExampleBean {// Number of years to calculate the Ultimate Answerprivate final int years;// The Answer to Life, the Universe, and Everythingprivate final String ultimateAnswer;public ExampleBean(int years, String ultimateAnswer) {this.years years;this.ultimateAnswer ultimateAnswer;}
}构造函数参数类型匹配——type 在上述场景中如果您使用属性显式指定构造函数参数的类型则容器可以使用简单类型的类型匹配type如下例所示
bean idexampleBean classexamples.ExampleBeanconstructor-arg typeint value7500000/constructor-arg typejava.lang.String value42/
/bean您可以使用index属性显式指定构造函数参数的索引如以下示例所示
bean idexampleBean classexamples.ExampleBeanconstructor-arg index0 value7500000/constructor-arg index1 value42/
/bean除了解决多个简单值的歧义之外指定索引还可以解决构造函数具有两个相同类型的参数时的歧义。
该索引是从 0 开始的。
构造函数参数名称——name 您还可以使用构造函数参数名称来消除值歧义如以下示例所示
bean idexampleBean classexamples.ExampleBeanconstructor-arg nameyears value7500000/constructor-arg nameultimateAnswer value42/
/bean请记住为了使其开箱即用您的代码必须在启用调试标志的情况下进行编译以便 Spring 可以从构造函数中查找参数名称。如果您不能或不想使用调试标志编译代码则可以使用 ConstructorProperties JDK 注释来显式命名构造函数参数。示例类必须如下所示
package examples;public class ExampleBean {// Fields omittedConstructorProperties({years, ultimateAnswer})public ExampleBean(int years, String ultimateAnswer) {this.years years;this.ultimateAnswer ultimateAnswer;}
}基于 Setter 的依赖注入
基于 Setter 的 DI 是通过容器在 调用无参构造函数或无参static工厂方法 来实例化您的 bean 之后调用您的 bean 上的 setter 方法来完成的。
下面的示例显示了一个只能通过使用纯 setter 注入进行依赖注入的类。这个类是传统的Java。它是一个 POJO不依赖于容器特定的接口、基类或注释。
public class SimpleMovieLister {// the SimpleMovieLister has a dependency on the MovieFinderprivate MovieFinder movieFinder;// a setter method so that the Spring container can inject a MovieFinderpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder movieFinder;}// business logic that actually uses the injected MovieFinder is omitted...
}ApplicationContext它所管理的 bean 支持基于构造函数和基于 setter 的 DI 。 在通过构造函数方法注入一些依赖项后它还支持基于 setter 的 DI。 您可以以 a 的形式配置依赖项BeanDefinition将其与实例结合使用PropertyEditor将属性从一种格式转换为另一种格式。 然而大多数 Spring 用户并不直接即以编程方式使用这些类而是使用 XMLbean 定义、带注释的组件即用Component、 Controller等注释的类或Bean基于 Java 的Configuration类中的方法。然后这些源在内部转换为实例BeanDefinition并用于加载整个 Spring IoC 容器实例。 基于构造函数还是基于 setter 的 DI? 由于您可以混合使用基于构造函数和基于 setter 的 DI因此最好的经验法则是使用构造函数来实现强制依赖项并使用 setter 方法或配置方法来实现可选依赖项。 请注意在 setter 方法上使用Autowired 注释可以使该属性成为必需的依赖项 然而带有编程验证参数的构造函数注入是更可取的。 Spring 团队通常提倡构造函数注入因为它允许您将应用程序组件实现为不可变对象并确保所需的依赖项不是null。此外构造函数注入的组件始终以完全初始化的状态返回给客户端调用代码。附带说明一下大量的构造函数参数是一种不好的代码味道这意味着该类可能有太多的职责应该进行重构以更好地解决适当的关注点分离问题。 Setter 注入主要应该仅用于可以在类中分配合理默认值的可选依赖项。否则必须在代码使用依赖项的所有地方执行非空检查。setter 注入的好处之一是 setter 方法使该类的对象可以在以后重新配置或重新注入。因此通过JMX MBean进行管理是 setter 注入的一个引人注目的用例。 使用对特定类最有意义的 DI 样式。有时在处理您没有源代码的第三方类时系统会为您做出选择。例如如果第三方类不公开任何 setter 方法则构造函数注入可能是 DI 唯一可用的形式。 依赖解析过程
容器执行bean依赖解析如下
ApplicationContext使用 所有 描述bean 的配置元数据创建并初始化。配置元数据可以通过 XML、Java 代码或注释来指定。对于每个 bean其依赖项以属性、构造函数参数或静态工厂方法的参数如果您使用它而不是普通构造函数的形式表示。这些依赖关系是在实际创建 bean 时提供给 bean 的。每个属性或构造函数参数都是要设置的值的实际定义或对容器中另一个 bean 的引用。 作为值的每个属性或构造函数参数都会从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下Spring 可以将以字符串格式提供的值转换为所有内置类型例如int、 long、String、boolean等。
Spring 容器在创建容器时验证每个 bean 的配置。但是直到实际创建 bean 后才会设置 bean 属性本身。 单例范围并设置为预实例化默认的 Bean 是在创建容器时创建的。范围在Bean Scopes中定义。 否则仅当请求时才创建 bean。创建 Bean 可能会导致创建 Bean 图形因为创建并分配了 Bean 的依赖项及其依赖项的依赖项等等。请注意这些依赖项之间的解析不匹配可能会在晚些时候出现即在第一次创建受影响的 bean 时出现。 循环依赖 循环依赖是如何出现的 如果主要使用构造函数注入 则可能会创建无法解析的循环依赖场景。 例如A类通过构造函数注入需要B类的实例B类通过构造函数注入需要A类的实例。如果您为类 A 和 B 配置 Bean 以相互注入Spring IoC 容器会在运行时检测到此循环引用并 抛出 BeanCurrentlyInCreationException. 循环依赖是如何出现的如何解决循环依赖问题 一种可能的解决方案是编辑某些类的源代码使其由 setter 而不是构造函数进行配置。 或者避免构造函数注入并仅使用 setter 注入。换句话说虽然不推荐但是可以通过setter注入来配置循环依赖。 与典型情况没有循环依赖不同bean A 和 bean B 之间的循环依赖会强制其中一个 Bean 在完全初始化之前注入另一个 Bean典型的先有鸡还是先有蛋的场景。 您通常可以相信 Spring 会做正确的事情。 它在容器加载时检测配置问题例如对不存在的 bean 的引用和循环依赖项。 当 bean 实际创建时Spring 会尽可能晚地设置属性并解决依赖关系。 这意味着当您请求一个对象时如果创建该对象或其依赖项之一出现问题则已正确加载的 Spring 容器稍后会生成异常 。 例如bean 由于缺失或无效而引发异常。某些配置问题的可见性可能会延迟这就是ApplicationContext默认情况下实现预实例化单例 bean 的原因。在实际需要这些 bean 之前创建这些 bean 需要付出一些前期时间和内存的代价您会在ApplicationContext创建 bean 时发现配置问题而不是稍后。 您仍然可以覆盖此默认行为以便单例 bean 延迟初始化而不是急切地预实例化。
如果不存在循环依赖关系则当将一个或多个协作 Bean 注入到从属 Bean 中时每个协作 Bean 在注入到从属 Bean 中之前就已完成配置。 这意味着如果 bean A 依赖于 bean B则 Spring IoC 容器会在调用 bean A 上的 setter 方法之前完全配置 bean B。换句话说该 bean 会被实例化如果它不是预实例化的单例 设置其依赖项并调用相关的生命周期方法例如配置的 init 方法 或InitializingBean 回调方法。
依赖注入的示例
以下示例将基于 XML 的配置元数据用于基于 setter 的 DI。Spring XML 配置文件的一小部分指定了一些 bean 定义如下所示
bean idexampleBean classexamples.ExampleBean!-- setter injection using the nested ref element --property namebeanOneref beananotherExampleBean//property!-- setter injection using the neater ref attribute --property namebeanTwo refyetAnotherBean/property nameintegerProperty value1/
/beanbean idanotherExampleBean classexamples.AnotherBean/
bean idyetAnotherBean classexamples.YetAnotherBean/以下示例显示了相应的ExampleBean类
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;}
}在前面的示例中setter 被声明为与 XML 文件中指定的属性相匹配。以下示例使用基于构造函数的 DI
bean idexampleBean classexamples.ExampleBean!-- constructor injection using the nested ref element --constructor-argref beananotherExampleBean//constructor-arg!-- constructor injection using the neater ref attribute --constructor-arg refyetAnotherBean/constructor-arg typeint value1/
/beanbean idanotherExampleBean classexamples.AnotherBean/
bean idyetAnotherBean classexamples.YetAnotherBean/以下示例显示了相应的ExampleBean类
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;}
}bean 定义中指定的构造函数参数用作ExampleBean.
现在考虑此示例的一个变体其中不使用构造函数而是告诉 Spring 调用static工厂方法来返回对象的实例
bean idexampleBean classexamples.ExampleBean factory-methodcreateInstanceconstructor-arg refanotherExampleBean/constructor-arg refyetAnotherBean/constructor-arg value1/
/beanbean idanotherExampleBean classexamples.AnotherBean/
bean idyetAnotherBean classexamples.YetAnotherBean/以下示例显示了相应的ExampleBean类
public class ExampleBean {// a private constructorprivate ExampleBean(...) {...}// a static factory method; the arguments to this method can be// considered the dependencies of the bean that is returned,// regardless of how those arguments are actually used.public static ExampleBean createInstance (AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {ExampleBean eb new ExampleBean (...);// some other operations...return eb;}
}static工厂方法的参数由元素constructor-arg/提供与实际使用构造函数完全相同。工厂方法返回的类的类型不必与包含static工厂方法的类具有相同的类型尽管在本例中是这样。实例非静态工厂方法可以以基本相同的方式使用除了使用属性factory-bean而不是class属性因此我们在这里不讨论这些细节。
依赖关系和配置详细信息
您可以将 bean 属性和构造函数参数定义为对其他托管 bean协作者的引用或内联定义的值。
直接值原语、字符串等
value元素的属性将property/属性或构造函数参数指定为人类可读的字符串表示形式。Spring 的 转换服务用于将这些值从 a 转换String为属性或参数的实际类型。以下示例显示了设置的各种值
bean idmyDataSource classorg.apache.commons.dbcp.BasicDataSource destroy-methodclose!-- results in a setDriverClassName(String) call --property namedriverClassName valuecom.mysql.jdbc.Driver/property nameurl valuejdbc:mysql://localhost:3306/mydb/property nameusername valueroot/property namepassword valuemisterkaoli/
/bean以下示例使用p-命名空间来实现更简洁的 XML 配置
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxsi:schemaLocationhttp://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdbean idmyDataSource classorg.apache.commons.dbcp.BasicDataSourcedestroy-methodclosep:driverClassNamecom.mysql.jdbc.Driverp:urljdbc:mysql://localhost:3306/mydbp:usernamerootp:passwordmisterkaoli//beans前面的 XML 更加简洁。但是拼写错误是在运行时而不是设计时发现的除非您使用在创建 bean 定义时支持自动属性完成的IDE例如IntelliJ IDEA或Spring Tools for Eclipse 。强烈推荐此类 IDE 帮助。
您还可以配置java.util.Properties实例如下所示
bean idmappingsclassorg.springframework.context.support.PropertySourcesPlaceholderConfigurer!-- typed as a java.util.Properties --property namepropertiesvaluejdbc.driver.classNamecom.mysql.jdbc.Driverjdbc.urljdbc:mysql://localhost:3306/mydb/value/property
/beanSpring容器使用JavaBeans机制将元素内的文本转换value/为 实例。这是一个很好的快捷方式也是 Spring 团队支持使用嵌套元素而不是属性样式的少数几个地方之一。java.util.PropertiesPropertyEditorvalue/value
idref标签
元素idref只是一种防错方法用于将id容器中另一个 bean 的字符串值 - 而不是引用传递给或 元素。以下示例展示了如何使用它
bean idtheTargetBean class.../bean idtheClientBean class...property nametargetNameidref beantheTargetBean//property
/bean前面的 bean 定义片段在运行时与以下片段完全相同
bean idtheTargetBean class... /bean idclient class...property nametargetName valuetheTargetBean/
/bean第一种形式比第二种更可取因为 使用idref标记可以让容器在部署时验证所引用的命名 bean 是否确实存在。 targetName在第二种变体中不对传递给 bean属性的值执行任何验证client。client仅当bean 实际实例化时才会发现拼写错误很可能导致致命结果 。如果该client bean 是原型bean则这种拼写错误和由此产生的异常可能只有在容器部署很久之后才会被发现。
References to Other Beans 对其他 Bean的引用
Inner Beans内部bean
元素bean/内的元素定义一个内部 bean如以下示例所示
bean idouter class...!-- instead of using a reference to a target bean, simply define the target bean inline --property nametargetbean classcom.example.Person !-- this is the inner bean --property namename valueFiona Apple/property nameage value25//bean/property
/bean内部 bean 定义不需要定义的 ID 或名称。如果指定容器不会使用此类值作为标识符。容器还会忽略scope创建时的标志因为内部 bean 始终是匿名的并且始终与外部 bean 一起创建。无法独立访问内部 Bean 或将它们注入到协作 Bean 中除了封闭 Bean 中。
Collections(集合)
list/, set/, map/和props/元素分别设置 Java的Collection类型List、set、map、和Properties的属性和参数。以下示例展示了如何使用它们CollectionListSetMapProperties
bean idmoreComplexObject classexample.ComplexObject!-- results in a setAdminEmails(java.util.Properties) call --property nameadminEmailspropsprop keyadministratoradministratorexample.org/propprop keysupportsupportexample.org/propprop keydevelopmentdevelopmentexample.org/prop/props/property!-- results in a setSomeList(java.util.List) call --property namesomeListlistvaluea list element followed by a reference/valueref beanmyDataSource //list/property!-- results in a setSomeMap(java.util.Map) call --property namesomeMapmapentry keyan entry valuejust some string/entry keya ref value-refmyDataSource//map/property!-- results in a setSomeSet(java.util.Set) call --property namesomeSetsetvaluejust some string/valueref beanmyDataSource //set/property
/bean映射键或值的值或者集合值也可以是以下任何元素
bean | ref | idref | list | set | map | props | value | nullUsing depends-on使用depends-on
如果一个 bean 是另一个 bean 的依赖项则通常意味着一个 bean 被设置为另一个 bean 的属性。 通常您可以使用基于 XML 的配置元数据中的ref/ 元素来完成此操作。然而 有时 bean 之间的依赖关系不太直接。一个例子是当需要触发类中的静态初始化程序时例如数据库驱动程序注册。 该depends-on属性可以显式强制在使用此元素的 bean 初始化之前初始化一个或多个 bean。以下示例使用该depends-on属性来表达对单个 bean 的依赖关系
bean idbeanOne classExampleBean depends-onmanager/
bean idmanager classManagerBean /要表达对多个 bean 的依赖关系请提供 bean 名称列表作为depends-on属性的值逗号、空格和分号是有效的分隔符
bean idbeanOne classExampleBean depends-onmanager,accountDaoproperty namemanager refmanager /
/beanbean idmanager classManagerBean /
bean idaccountDao classx.y.jdbc.JdbcAccountDao /该depends-on属性可以指定初始化时依赖关系并且在仅单例bean 的情况下还可以指定相应的销毁时依赖关系。定义depends-on与给定 bean 的关系的依赖 bean 在给定 bean 本身被销毁之前首先被销毁。这样depends-on还可以控制关机顺序。
Lazy-initialized Beans延迟初始化 Bean 默认情况下ApplicationContext实现会在初始化过程中急切地创建和配置所有 单例bean。
一般来说这种预实例化是可取的因为配置或周围环境中的错误会立即被发现而不是数小时甚至数天后。 如果不需要这种行为您可以通过将 Bean 定义标记为延迟初始化来防止预实例化单例 Bean。延迟初始化的 bean 告诉 IoC 容器在第一次请求时而不是在启动时创建一个 bean 实例。
lazy-init在 XML 中此行为由元素上的属性控制 如以下示例所示
bean idlazy classcom.something.ExpensiveToCreateBean lazy-inittrue/
bean namenot.lazy classcom.something.AnotherBean/当前面的配置被 ApplicationContext消费时lazybean 在启动时不会被ApplicationContext急切地预实例化而非lazyBean 会被急切地预实例化。
但是当延迟初始化 bean 是未延迟初始化的单例 bean 的依赖项时ApplicationContext会在启动时创建延迟初始化 bean因为它必须满足单例的依赖项。延迟初始化的 bean 被注入到其他未延迟初始化的单例 bean 中。
您还可以使用 元素default-lazy-init上的属性在容器级别控制延迟初始化beans/如以下示例所示
beans default-lazy-inittrue!-- no beans will be pre-instantiated... --
/beansAutowiring Collaborators 自动装配合作者
Spring 容器可以自动装配协作 bean 之间的关系。您可以让 Spring 通过 ApplicationContext 检查 .xml 文件的内容来自动为您的 bean 解析协作者其他 bean。
模式解释no默认没有自动装配。Bean 引用必须由元素定义ref。对于大型部署不建议更改默认设置因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上它记录了系统的结构。byName按属性名称自动装配。Spring 寻找与需要自动装配的属性同名的 bean。例如如果一个 bean 定义设置为按名称自动装配并且它包含一个master属性即它有一个 setMaster(..)方法Spring 会查找名为的 bean 定义master并使用它来设置该属性。byType如果容器中恰好存在一个属性类型的 bean则允许自动装配该属性。如果存在多个则会抛出致命异常这表明您不能byType对该 bean 使用自动装配。如果没有匹配的 bean则不会发生任何事情未设置该属性。constructor类似于byType但适用于构造函数参数。如果容器中不存在一个构造函数参数类型的 bean则会引发致命错误。 自动装配具有以下优点 自动装配可以显着减少指定属性或构造函数参数的需要。本章其他地方讨论的其他机制例如 bean 模板 在这方面也很有价值。 自动装配可以随着对象的发展而更新配置。例如如果您需要向类添加依赖项则可以自动满足该依赖项而无需修改配置。因此自动装配在开发过程中特别有用而不会在代码库变得更加稳定时否定切换到显式装配的选项。
当使用基于 XML 的配置元数据请参阅依赖注入autowire时您可以使用元素的属性 为 bean 定义指定自动装配模式。 自动装配功能有四种模式。您可以为每个 bean 指定自动装配从而可以选择要自动装配的 bean。 下表描述了四种自动装配模式
使用byType自动constructor装配模式您可以连接数组和类型化集合。在这种情况下将提供容器内与预期类型匹配的所有自动装配候选者以满足依赖性。Map如果预期的键类型是 您可以自动装配强类型实例String。自动装配Map 实例的值由与预期类型匹配的所有 bean 实例组成 Map实例的键包含相应的 bean 名称。
自动装配的局限性和缺点
考虑自动装配的局限性和缺点 property和设置中的显式依赖项constructor-arg始终会覆盖自动装配。您无法自动装配简单属性例如基元、 Strings和Classes以及此类简单属性的数组。此限制是设计使然。 自动连线不如显式连线精确。尽管如此正如前面的表中所指出的Spring 会小心地避免猜测以防出现可能产生意外结果的歧义。Spring 管理的对象之间的关系不再明确记录。 接线信息可能无法用于从 Spring 容器生成文档的工具。 容器内的多个 bean 定义可能与要自动装配的 setter 方法或构造函数参数指定的类型匹配。对于数组、集合或 Map实例来说这不一定是问题。但是对于期望单个值的依赖项这种歧义不是任意解决的。如果没有可用的唯一 bean 定义则会引发异常。
在后一种情况下您有多种选择 放弃自动装配转而采用显式装配。 autowire-candidate通过将 bean 的属性设置为 来避免自动装配 bean 定义如下一节false所述。 通过将单个 bean 元素 primary的属性设置为 将其指定为主要候选者true。 通过基于注释的配置实现更细粒度的控制如基于注释的容器配置中所述。
从自动装配中排除 Bean
在每个 bean 的基础上您可以从自动装配中排除 bean。在 Spring 的 XML 格式中将元素bean/ 的autowire-candidate 属性设置为false。容器使特定bean 的定义对自动装配基础设施不可用包括注释样式配置例如Autowired。 该autowire-candidate属性旨在仅影响基于类型的自动装配。它不会影响按名称的显式引用即使指定的 bean 未标记为自动装配候选者也会得到解析。因此如果名称匹配按名称自动装配仍然会注入一个 bean。 您还可以根据与 bean 名称的模式匹配来限制自动装配候选者。顶级元素接受其属性内的一种或多种模式 default-autowire-candidates。例如要将自动装配候选状态限制为名称以 结尾的任何 bean Repository请提供值*Repository。要提供多种模式请在逗号分隔的列表中定义它们。bean 定义的属性的显式值 true或该属性的显式值始终优先。对于此类 Bean模式匹配规则不适用。falseautowire-candidate
这些技术对于您不想通过自动装配注入其他 Bean 的 Bean 非常有用。这并不意味着排除的 bean 本身不能使用自动装配进行配置。相反bean 本身并不是自动装配其他 bean 的候选者。
方法注入
在大多数应用场景中容器中的大部分bean都是单例的。常见的controller、service、componment、configration等注解修饰的默认都是单例。 当一个单例 bean 需要与另一个单例 bean 协作或一个非单例 bean 需要与另一个非单例 bean 协作时通常可以通过将一个 bean 定义为另一个 bean 的属性来处理依赖关系。 当 bean 生命周期不同时就会出现问题。假设单例 bean A 需要使用非单例原型bean B可能是在 A 上的每次方法调用上。容器仅创建单例 bean A 一次因此只有一次设置属性的机会。每次需要 bean B 时容器无法为 bean A 提供新的 bean B 实例。
解决方案是放弃一些控制反转。您可以通过实现接口来使 bean A 知道容器ApplicationContextAware并在每次 bean A 需要时调用getBean(B)容器来请求通常是新的bean B 实例。以下示例展示了这种方法 // 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 CommandCommand command createCommand();// set the state on the (hopefully brand new) Command instancecommand.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;}
}方法注入是 Spring IoC 容器的一项高级功能可让您干净地处理此用例。
Lookup Method Injection 查找方法注入
查找方法注入是 容器重写 容器管理的 Bean 上的方法并返回容器中另一个命名 Bean 的查找结果的能力。 查找通常涉及prototype bean如上一节中描述的场景。Spring框架通过使用CGLIB库的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。 为了使这种动态子类化工作Spring bean 容器子类化的类不能是final并且要重写的方法也不能是final 。 对具有方法的类进行单元测试abstract需要您自己对该类进行子类化并提供该abstract方法的存根实现。 组件扫描也需要具体的方法这需要选取具体的类。 另一个关键限制是查找方法不能与工厂方法一起使用特别是不能与Bean配置类中的方法一起使用因为在这种情况下容器不负责创建实例因此无法在其上创建运行时生成的子类。
对于CommandManager前面的代码片段中的类Spring 容器动态地覆盖该方法的实现createCommand() 。该类CommandManager没有任何 Spring 依赖项如修改后的示例所示
package fiona.apple;// no more Spring imports!public abstract class CommandManager {public Object process(Object commandState) {// grab a new instance of the appropriate Command interfaceCommand command createCommand();// set the state on the (hopefully brand new) Command instancecommand.setState(commandState);return command.execute();}// okay... but where is the implementation of this method?protected abstract Command createCommand();
}
在包含要注入的方法的客户端类中CommandManager本例中为 要注入的方法需要以下形式的签名
public|protected [abstract] return-type theMethodName(no-arguments);如果该方法是abstract则动态生成的子类将实现该方法。否则动态生成的子类将覆盖原始类中定义的具体方法。考虑以下示例
!-- a stateful bean deployed as a prototype (non-singleton) --
bean idmyCommand classfiona.apple.AsyncCommand scopeprototype!-- inject dependencies here as required --
/bean!-- commandProcessor uses statefulCommandHelper --
bean idcommandManager classfiona.apple.CommandManagerlookup-method namecreateCommand beanmyCommand/
/bean标识为 commandManager 的 bean每次需要新的 myCommand 实例时commandManager都会调用自己的createCommand()方法。如果确实需要则必须小心地将 bean myCommand部署为原型。如果它是单例 则每次都会返回相同的 myCommand 实例。
或者在基于注释的组件模型中您可以通过注释声明查找方法Lookup如以下示例所示
public abstract class CommandManager {public Object process(Object commandState) {Command command createCommand();command.setState(commandState);return command.execute();}Lookup(myCommand)protected abstract Command createCommand();
}或者更惯用的是您可以依赖 根据查找方法声明的返回类型解析目标 bean
public abstract class CommandManager {public Object process(Object commandState) {MyCommand command createCommand();command.setState(commandState);return command.execute();}Lookupprotected abstract MyCommand createCommand();
}任意方法替换
与查找方法注入相比方法注入的一种不太有用的形式是能够用另一种方法实现替换托管 bean 中的任意方法。您可以安全地跳过本节的其余部分直到您真正需要此功能为止。
通过基于 XML 的配置元数据您可以使用该replaced-method元素将已部署 bean 的现有方法实现替换为另一个方法实现。考虑下面的类它有一个computeValue我们要重写的方法
public class MyValueCalculator {public String computeValue(String input) {// some real code...}// some other methods...
}实现该org.springframework.beans.factory.support.MethodReplacer 接口的类提供新的方法定义如以下示例所示
/*** meant to be used to override the existing computeValue(String)* implementation in MyValueCalculator*/
public class ReplacementComputeValue implements MethodReplacer {public Object reimplement(Object o, Method m, Object[] args) throws Throwable {// get the input value, work with it, and return a computed resultString input (String) args[0];...return ...;}
}用于部署原始类并指定方法重写的 bean 定义类似于以下示例
bean idmyValueCalculator classx.y.z.MyValueCalculator!-- arbitrary method replacement --replaced-method namecomputeValue replacerreplacementComputeValuearg-typeString/arg-type/replaced-method
/beanbean idreplacementComputeValue classa.b.c.ReplacementComputeValue/您可以使用元素arg-type/中的一个或多个元素replaced-method/ 来指示被重写方法的方法签名。 仅当方法重载并且类中存在多个变体时参数的签名才是必需的。 为了方便起见参数的类型字符串可以是完全限定类型名称的子字符串。例如以下全部匹配 java.lang.String
java.lang.String
String
Str由于参数的数量通常足以区分每个可能的选择因此此快捷方式可以让您仅键入与参数类型匹配的最短字符串从而节省大量打字时间。