Spring Beans自动装配
Beans自动装配:
Spring 容器可以在不使用<constructor-arg>
和<property>
元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。
自动装配模式:
下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用<bean>
元素的 autowire 属性为一个 bean 定义指定自动装配模式。
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
可以使用 byType 或者 constructor 自动装配模式来连接数组和其他类型的集合。
自动装配的局限性:
当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不使用自动装配,它可能会使开发人员混淆的使用它来连接只有一个或两个 bean 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应该在使用它们之前考虑到自动装配的局限性和缺点。
限制 | 描述 |
---|---|
重写的可能性 | 你可以使用总是重写自动装配的 <constructor-arg>和 <property> 设置来指定依赖关系。 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
Spring自动装配”byName”:
这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为自动装配 byName,并且它包含 spellChecker 属性(即,它有一个 setSpellChecker(…) 方法),那么 Spring 就会查找定义名为 spellChecker 的 bean,并且用它来设置这个属性。你仍然可以使用
示例:
下面是在正常情况下的配置文件 Beans.xml 文件:
1 |
|
但是,如果你要使用自动装配 “byName”,那么你的 XML 配置文件将成为如下:
1 |
|
Spring自动装配”byType”:
这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellChecker 属性,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并且用它来设置这个属性。你仍然可以使用 <property> 标签连接其余属性。
示例:
下面是在正常情况下的配置文件 Beans.xml 文件:
1 |
|
但是,如果你要使用自动装配 “byType”,那么你的 XML 配置文件将成为如下:
1 |
|
Spring由构造函数自动装配:
这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为通过构造函数自动装配,而且它有一个带有 SpellChecker类型的参数之一的构造函数,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并用它来设置构造函数的参数。你仍然可以使用 <constructor-arg> 标签连接其余属性。
实例:
TextEditor.java
1 | package autowire; |
SpellChecker.java
1 | package autowire; |
MainApp.java
1 | package autowire; |
下面是在正常情况下的配置文件 Beans.xml 文件:
1 |
|
但是,如果你要使用自动装配 “by constructor”,那么你的 XML 配置文件将成为如下:
1 |
|
Spirng基于注解的配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。
注解连线在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。所以如果你想在 Spring 应用程序中使用的任何注解,可以考虑到下面的配置文件。
1 |
|
一旦 被配置后,你就可以开始注解你的代码,表明 Spring 应该自动连接值到属性,方法和构造函数。让我们来看看几个重要的注解,并且了解它们是如何工作的:
序号 | 注解 & 描述 |
---|---|
1 | @Required@Required 注解应用于 bean 属性的 setter 方法。 |
2 | @Autowired@Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。 |
3 | @Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。 |
4 | JSR-250 AnnotationsSpring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。 |
Spring @Required注释:
@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。下面显示的是一个使用 @Required 注释的示例。
示例:
Student.java
1 | package annotation; |
MainApp.java
1 | package annotation; |
Student.xml
1 |
|
Spring @Autowired注释:
@Autowired 注释对在哪里和如何完成自动连接提供了更多的细微的控制。
@Autowired 注释可以在 setter 方法中被用于自动连接 bean,就像 @Autowired 注释,容器,一个属性或者任意命名的可能带有多个参数的方法。
Setter方法中的@autowired:
可以在 XML 文件中的 setter 方法中使用 @Autowired 注释来除去 元素。当 Spring遇到一个在 setter 方法中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。
属性中的@autowired:
可以在属性中使用 @Autowired 注释来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。
构造函数中的@Autowired:
可以在构造函数中使用 @Autowired。一个构造函数@Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用元素配置 bean ,构造函数也会被自动连接。
@Autowired的(required=false)选项:
默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired的 (required=false) 选项关闭默认行为。
@Autowired(required=false)
Spring @Qualifier注释:
可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。
示例:
Student.java
1 | package annotation; |
Profile.java
1 | package annotation; |
MainApp.java
1 | package annotation; |
Student.xml
1 |
|
Spring JSR-250注释:
Spring还使用基于 JSR-250 注释,它包括 @PostConstruct, @PreDestroy 和 @Resource 注释。
@PostConstruct 和 @PreDestroy 注释:
为了定义一个 bean 的安装和卸载,我们使用 init-method 和/或 destroy-method 参数简单的声明一下 。init-method 属性指定了一个方法,该方法在 bean 的实例化阶段会立即被调用。同样地,destroy-method 指定了一个方法,该方法只在一个 bean 从容器中删除之前被调用。
你可以使用 @PostConstruct 注释作为初始化回调函数的一个替代,@PreDestroy 注释作为销毁回调函数的一个替代。
@Resource 注释:
可以在字段中或者 setter 方法中使用 @Resource 注释,它和在 Java EE 5 中的运作是一样的。@Resource 注释使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入。你可以说,它遵循 by-name 自动连接语义。
Spring基于Java的配置:
基于 Java 的配置选项,可以使在不用配置 XML 的情况下编写大多数的 Spring,但是一些有帮助的基于 Java 的注解,解释如下:
@Configuration 和 @Bean 注解
带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。
示例:
HelloWorldConfig.java
1 | package annotation; |
HelloWorld.java
1 | package annotation; |
MainApp.java
1 | package annotation; |
注入Bean的依赖性:
当 @Beans 依赖对方时,表达这种依赖性非常简单,只要有一个 bean 方法调用另一个。如下所示:
1 | package com.tutorialspoint; |
这里,foo Bean 通过构造函数注入来接收参考基准。
@Import注释:
@import 注解允许从另一个配置类中加载 @Bean 定义。考虑 ConfigA 类,如下所示:
1 |
|
你可以在另一个 Bean 声明中导入上述 Bean 声明,如下所示:
1 |
|
现在,当实例化上下文时,不需要同时指定 ConfigA.class 和 ConfigB.class,只有 ConfigB 类需要提供,如下所示:
1 | public static void main(String[] args) { |
生命周期回调:
@Bean 注解支持指定任意的初始化和销毁的回调方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和销毁方法的属性:
1 | public class Foo { |
指定 Bean 的范围:
默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法,如下所示:
1 |
|
Spring中的事件处理:
Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
Spring 提供了以下的标准事件:
序号 | Spring 内置事件 & 描述 |
---|---|
1 | ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。 |
2 | ContextStartedEvent 当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
3 | ContextStoppedEvent 当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。 |
4 | ContextClosedEvent 当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
5 | RequestHandledEvent 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。 |
由于 Spring 的事件处理是单线程的,所以如果一个事件被发布,直至并且除非所有的接收者得到的该消息,该进程被阻塞并且流程将不会继续。因此,如果事件处理被使用,在设计应用程序时应注意。
监听上下文事件:
为了监听上下文事件,一个 bean 应该实现只有一个方法 onApplicationEvent() 的 ApplicationListener 接口。因此,我们写一个例子来看看事件是如何传播的,以及如何可以用代码来执行基于某些事件所需的任务。
HelloWorld.java
1 | package annotation; |
CStartEventHandler.java
1 | package annotation; |
CStopEventHandler.java
1 | package annotation; |
MainApp4.java
1 | package annotation; |
Event.xml
1 |
|
运行结果:
1 | ContextStartedEvent Received |
Spring中的自定义事件:
这个是 CustomEvent.java 文件的内容:
1 | package annotation; |
下面是 CustomEventPublisher.java 文件的内容:
1 | package annotation; |
下面是 CustomEventHandler.java 文件的内容:
1 | package annotation; |
下面是 MainApp.java 文件的内容:
1 | package annotation; |
下面是配置文件Custom.xml:
1 |
|
程序运行结果:
1 | My Custom Event |
不同配置方式的比较
基于XML配置 | 基于注解配置 | 基于Java类配置 | |
---|---|---|---|
Bean定义 | 在XML文件中通过<bean>元素定义Bean | 在Bean实现类处通过注解@Component等定义Bean | 在标注了@Configuration的Java类中,在类方法上标注@Bean定义Bean |
Bean名称 | 通过<Bean>的id或name属性定义 | 通过注解的value属性定义,如@Component(”userDao”) | 通过@Bean的name属性定义,如@Bean(”userDao”) |
Bean注入 | 通过<property>子元素或通过p命名空间的动态属性注入 | 通过标出@autowired,按类型匹配自动注入,可配合使用@Qualifier按名称匹配注入 | 1. 方法处通过@autowired是方法入参绑定Bean。 2,通过调用配置类的@bean方法进行注入 |
Bean生命过程方法 | 通过<bean>的init-method和destory-method属性指定Bean实现类方法名 | 通过在目标方法上标注@postConstruct和@PreDestroy注解指定 | 通过@Bean的initMethod或destoryMethod指定相应方法 |
Bean作用范围 | 通过<bean>的Scope属性指定 | 通过在类定义出标注@Scope指定 | 通过Bean方法定义出标签@Scope指定 |
Bean延迟初始化 | 通过<bena>的lazy-init属性指定,默认为default | 通过在类定义标注@lazy指定,如@Lazy(true) | 通过在Bean方法定义处标注@Lazy指定 |
三种的使用场景:
基于XML的配置:
第三方类库:如DataSource、JdbcTemplate等。
命名空间,如aop,context等。
基于注解的配置:
Bean的实现类是当前项目开发的,可直接在Java类中使用注解配置。
基于Java类的配置:
对于实例化Bean的逻辑比较复杂,则比较使用与基于Java类配置的方式。