《Spring实战》笔记(二):装配

1 装配机制

Spring具有非常大的灵活性,它提供了三种主要的装配机制:

  • 在XML中进行显式配置。
  • 在Java中进行显式配置。
  • 隐式的bean发现机制和自动装配。

1.1 自动化装配

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。
    具体步骤如下:
  1. 用@Component注解来标记组件类。
    @Component可以设置value属性为bean的id,如果不设置,默认id为类名的首字母缩写。大多数情况下,@Named同@Component
  2. 在配置类中使用@ComponentScan注解来开启组件扫描(也可以使用xml配置 <context:component-scan>)。
    @ComponentScan默认会扫描所在类的包及其所有子包,若果要指定特定的包,可以通过value属性设置,如果要指定一个或多个包,可以使用basePackage类指定或者使用basePackageClassess来指定(指定基础包中的任一个类即可)
  3. 在要使用组件的类中使用@AutoWired注入组件。
    @AutoWired可以声明在类的属性上,也可以声明在方法上。如果没有配置的bean,Spring会抛出异常,使用@AutoWired的required属性设置为false可以避免该情况,但是被尝试注入的参数将会为null。在大多数情况下,@Inject同@AutoWired

1.2 使用Java代码进行装配

你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。
通过代码声明Bean,首先要创建一个方法用以返回一个实例,然后使用@Bean注解来声明。方法名为该Bean的id,也可以使用@Bean的value属性来设定Bean的id。

    @Bean
    public OneBean oneBean() {
      return new OneBean();
    }

1.3 使用XML进行装配:

  1. 使用<bean>元素声明bean
<bean id="myBean" class="myClass"/>
  1. 使用构造器注入初始化bean
    2.1 注入其他bean
<bean id="myBean" class="myClass">
  <constructor-arg ref="anotherBean">
</bean>

也可以使用c命名空间,但在使用之前要在XML顶部声明其模式。

<bean id="myBean" class="myClass" c:arg-ref="anotherBean"/>

在这里,arg表示构造器参数名,有三种写法:

  • 直接写参数名,同上
  • 使用参数序号
<bean id="myBean" class="myClass" c:_0-ref="anotherBean"/>
  • 如果只有一个参数,可以不标示参数
<bean id="myBean" class="myClass" c:_-ref="anotherBean"/>

2.2 注入字面量

<bean id="myBean" class="myClass">
  <constructor-arg value="anotherBean">
</bean>

使用c命名空间:

<bean id="myBean" class="myClass" c:arg="anotherBean"/>

其他两种方式同理,和注入bean相比只是去掉了-ref

2.3 注入集合(c命名空间不能注入集合)

<bean id="myBean" class="myClass">
  <constructor-arg></null></constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <list>
      <value>xxx</value>
      <value>yyy</value>
    </list>
  </constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <list>
      <ref bean=bean1/>
      <ref bean=bean2/>
    </list>
  </constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <set>
      <value>xxx</value>
      <value>yyy</value>
    </set>
  </constructor-arg>
</bean>
  1. 使用setter注入初始化bean
    使用setter注入与用构造器注入基本类似,只不过换成了<property>和p命名空间
<bean id="myBean" class="myClass" p:literal="literal" p:pBean-ref="pBean">
  <property name="arg" ref="anotherBean"/>
  <property name="literalValue" value="value"/>
  <property name="collection">
    <list>
      <value>xxx</value>
      <value>yyy</value>
    </list>
  </property>
</bean>
  1. 对于集合类,可以使用util命名空间创建对应的集合bean,再被引用。
<bean id="myBean" class="myClass">
  <property name="arg" ref="testList"/>
</bean>
<util:list name="testList">
  <value>xxx</value>
  <value>yyy</value>
</util:list>

1.4 混合配置

  1. JavaConfig引入JavaConfig,使用@Import注解即可
  2. JavaConfig引入XML配置,使用@ImportResource注解
  3. XML配置引入JavaConfig和XML配置
<bean class="JavaConfig"/>
<import resource="abc.xml"/>

2 高级装配

2.1 profile

@Profile可以用来修饰配置类和声明Bean的方法,用value属性来指定profile。在XML配置中,使用<beans>元素的profile属性来设置profile。激活profile是通过spring.profiles.activespring.profiles.default来确定,如果前者没有设置的话,将会查找后者,如果两者都没有设置的话,则没有激活的profile,只有没有指定profile的bean可以被创建。

2.2 条件化配置

@Conditional可以实现条件化地配置bean。@Conditional的value属性为一个实现了Condition接口的类,需要实现mataches()方法。@Profile本身也使用了@Conditional注解。

    @Bean
    @Conditional(DemoCondition.class)
    public DemoBean demoBean() {
        return new DemoBean();
    }
public class DemoCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        if (conditionContext.getEnvironment().containsProperty("demo")) {
            return true;
        }
        return false;
    }
}

2.3 处理自动装配的歧义

@Primary用来修饰bean,如果在装配时发生歧义将会优先选择该Bean。@Qualifier用来指定Bean的限定符,与@AutoWired一起使用的时候,将会选择对应限定符的Bean进行装配(默认的限定符为Bean的id)。@Qualifier用来修饰Bean的时候,将会为Bean设置一个限定符。

2.4 bean的作用域

  1. Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
  • 单例(Singleton):在整个应用中,只创建bean的一个实例。在默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。
  • 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例。
  • 请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
  1. 使用@Scope或者在xml文件中<bean>的scope属性指定作用域。

  2. 使用会话和请求作用域(通过注解)

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
    public DemoBean demoBean() {
        return new DemoBean();
    }

要注意的是,@Scope同时还有一个proxyMode属性,它被设置成了ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。

作用域代理.JPG

如果DemoBean是一个类的话,就不能使用ScopedProxyMode.INTERFACES,而要使用ScopedProxyMode.TARGET_CLASS

  1. 使用会话和请求作用域(通过XML)
<bean id="demo" class="DemoBean" scope="session">
  <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<aop:scoped-proxy/>表示使用作用域代理,默认创建目标类的代理,如果将proxy-target-class设置为false,将生成基于接口的代理

2.5 运行时注入

Spring提供了两种在运行时求值的方式:

  • 属性占位符(Property placeholder)。
  • Spring表达式语言(SpEL)。
2.5.1 属性占位符

@PropertySource可以引入其他的属性文件,并将属性加载到Spring的Environment对象中属性占位符的格式为${xxx},可以在xml文件中引用,在Java文件中,通过@Value(${xxx})注入到参数中
在使用属性占位符之前,需要先配置一个PropertySourcesPlaceholderConfigurer bean:

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
}

如果使用xml配置,则使用 <context:property-holder/>

2.5.2 Spring表达式语言(Spring Expression Language, SpEL)

SpEL使用 #{...}的格式

  1. 表示字面量,直接写。String类型单双引号都可。
#{1} 
#{3.14} 
#{true} 
#{'string'} 
#{"string"}
  1. 引用bean,直接引用bean id即可。链式调用的时候使用 ?. 用以避免空指针异常,如果前一个方法返回为null将不会再调用下一个方法。
#{bean}
#{bean.property1}
#{bean.method1()}
#{bean.method1().method2()}
#{bean.method1()?.method2()}
  1. 访问静态方法和常量,使用T()运算符
#{T(java.util.math).PI}
  1. 运算符
    SpEL运算符.JPG

    条件运算中,ternary表示三元运算符。Evlis表示简化的三元运算符,用以判断空值并用默认值代替空值,如:

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,811评论 6 342
  • 本章内容: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表...
    谢随安阅读 1,188评论 0 5
  • 店里不忙晚上看了一部不错的电影但是看完发现只有前半部分好后面就一般了吃了饺子和鲅鱼遇见了一个和我爸很像的人我妈一眼...
    无可紧要的小事情阅读 134评论 0 0
  • 那天听到老大(易仁永澄)说到:能量低的人会被能量高的人抛弃!这句话顿时让我感觉惊呆了!因为联想到我的经历,我就坚信...
    英伦小兔子阅读 16,552评论 2 8