Spring 5.2.2 核心技术(16)

       昨天把四种做用域作了说明,今天对于扩展使用进行深刻讲解,今天的内容相对来讲难,仔细体会其中的奥妙。javascript

做用域bean做为依赖项 java

       Spring IoC容器不只管理对象(bean)的实例化,还管理依赖注入。例如将一个HTTP  request 做用域bean注入到另外一个更持久做用域的bean中,你能够选择注入一个AOP  proxy 来代替做用域bean。就是说须要注入一个代理对象,该对象公开与做用域对象相同的public接口,但也能够从相关做用域(如HTTP request)检索真正的目标对象,并将方法调用委托给真正的对象。spring

     还能够在做用域为singleton的bean之间使用<aop:scoped-proxy/>,而后经过一个可序列化的中间代理,从而可以在反序列化时从新获取目标singleton bean。微信

     当依靠做用域bean 为 prototype声明<aop:scoped-proxy/>时,共享代理上的每一个方法调用都会致使建立一个新的目标实例,而后将调用转发到该实例。session

    另外,scoped 代理并非生命周期中短暂访问bean的惟一方法。还能够将注入点(即构造方法或setter参数或autowired字段)声明为ObjectFactory<MyTargetBean>,容许getObject()调用在每次须要时按需获取当前实例,而无需保留实例或单独存储实例。maven

    做为扩展属性,能够声明ObjectProvider<MyTargetBean>,它提供了几个额外的访问属性,包括getIfAvailable和getIfUnique。ide

    它在JSR-330中称为Provider,用于Provider<MyTargetBean>声明并每次经过相应get()调用。spa


    使用JSR-330 标准注解
.net

从Spring 3.0开始,Spring提供了对JSR-330标准注解(依赖注入)的支持。这些注解的扫描方式与Spring注解相同。要使用它们,须要在类路径中包含相关的jar。prototype

   若是使用Maven,javax.inject工件能够在标准Maven存储库(https://repo1.Maven.org/maven2/javax/inject/javax.inject/1/)中找到。能够将如下依赖项添加到文件pom.xml中:

<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version></dependency>

   如下示例中的配置仅为一行,了解其背后的“why”和“how”很重要:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 一个HTTP Session-scoped bean 做为一个代理暴露在外 --> <bean id="userPreferences" class="com.something.UserPreferences" scope="session">        <!-- 告知或提示容器 代理的bean --> <aop:scoped-proxy/> </bean>
    <!--     一个singleton-scoped的bean被注入到上面bean的代理中--> <bean id="userService" class="com.something.SimpleUserService">        <!-- userPreferences 代理被引用 --> <property name="userPreferences" ref="userPreferences"/> </bean></beans>

      要建立这样的代理,能够将子<aop:scoped-proxy/>嵌入到scoped bean中(请参见选择要建立的代理类型和基于XML模式的配置)。为何在requestsession 和自定义做用域级别定义做用域的bean须要<aop:scoped-proxy/>元素?考虑下面的singleton bean定义,并将其与你须要为上述做用域定义的内容进行对比(注意下面的userPreferences bean是不完整的):

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="userManager" class="com.something.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>

     在前面的例子中,singleton  bean(userManager)被HTTP  Session-scope bean(userPreferences)注入引用。这里的要点是,userManager bean是一个单例:每一个容器只实例化一次,而且它的依赖项(在本例中只有一个,userPreferences bean)也只注入一次。这意味着userManager bean只对彻底相同的userPreferences对象(即最初注入它的对象)操做。

     当一个生命周期较短的做用域bean注入到一个生命周期较长的做用域bean时(例如,将一个HTTP Session做用域的依赖bean做为依赖项注入到singleton bean中),这不是您想要的结果。相反,须要一个single userManager对象,而且在HTTP Session的生命周期中,须要一个指定的HTTP Session的userPreferences对象。所以,容器建立一个对象,该对象公开与UserPreferences类彻底相同的public接口(理想状况下是一个UserPreferences实例的对象),该对象能够从做用域构造(HTTP request, Session等)获取真正的UserPreferences对象。容器将此代理对象注入到userManager bean中,而userManager bean不知道此UserPreferences引用是代理。在本例中,当一个UserManager实例调用依赖注入的UserPreferences对象上的方法时,它其实是在代理上调用一个方法。而后,代理从(在本例中)HTTP会话获取真实的UserPreferences对象,并将方法调用委托给检索到的真实UserPreferences对象。

      所以,在将 request-scoped 和session-scoped的bean注入依赖对象时,须要如下配置(正确且完整),以下例所示:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <aop:scoped-proxy/></bean>
<bean id="userManager" class="com.something.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>


选择建立的代理类型

     默认状况下,当Spring容器为用<aop:scoped-proxy/>标记的bean建立代理时,将建立一个基于CGLIB的类代理。CGLIB代理只拦截公共方法调用!不要对这样的代理调用非公共方法。它们不会委托给做用域的实际目标对象。

     或者,能够配置Spring容器来为此类做用域bean建立标准的基于JDK接口的代理,经过为<aop:scoped-proxy/>proxy-target-class的值指定false。使用基于JDK接口的代理意味着在应用程序classpath 中不须要额外的lib库来影响这样的代理。然而,这也意味着做用域bean的类必须实现至少一个接口,而且做用域bean被注入的全部依赖项必须经过其接口之一引用bean。如下示例显示基于接口的代理:

<!-- DefaultUserPreferences 实现了UserPreferences 接口--><bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/></bean>
<bean id="userManager" class="com.stuff.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>

对代理不理解就须要去看代理实现有几种。  

有关选择基于类或基于接口的代理的详细信息,明天讲。 


关注Spring 中文社区 :


本文分享自微信公众号 - Spring中文社区(gh_81d233bb13a4)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索