Java学习笔记(10)Spring Bean配置

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor">
<property name="spellChecker" ref="spellChecker" />
<property name="name" value="Generic Text Editor" />
</bean>

<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="autowire.SpellChecker">
</bean>

</beans>

但是,如果你要使用自动装配 “byName”,那么你的 XML 配置文件将成为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor"
autowire="byName">
<property name="name" value="Generic Text Editor" />
</bean>

<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="autowire.SpellChecker">
</bean>

</beans>

Spring自动装配”byType”:

这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。

例如,在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellChecker 属性,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并且用它来设置这个属性。你仍然可以使用 <property> 标签连接其余属性。

示例:

下面是在正常情况下的配置文件 Beans.xml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor">
<property name="spellChecker" ref="spellChecker" />
<property name="name" value="Generic Text Editor" />
</bean>

<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="autowire.SpellChecker">
</bean>

</beans>

但是,如果你要使用自动装配 “byType”,那么你的 XML 配置文件将成为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor"
autowire="byType">
<property name="name" value="Generic Text Editor" />
</bean>

<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="autowire.SpellChecker">
</bean>

</beans>

Spring由构造函数自动装配:

这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。

例如,在配置文件中,如果一个 bean 定义设置为通过构造函数自动装配,而且它有一个带有 SpellChecker类型的参数之一的构造函数,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并用它来设置构造函数的参数。你仍然可以使用 <constructor-arg> 标签连接其余属性。

实例:

TextEditor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package autowire;

public class TextEditor {
private SpellChecker spellChecker;
private String name;

public TextEditor(SpellChecker spellChecker, String name) {
this.spellChecker = spellChecker;
this.name = name;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}

public String getName() {
return name;
}

public void spellCheck() {
spellChecker.checkSpelling();
}
}

SpellChecker.java

1
2
3
4
5
6
7
8
9
10
11
package autowire;

public class SpellChecker {
public SpellChecker() {
System.out.println("Inside SpellChecker constructor.");
}

public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package autowire;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor)context.getBean("textEditor");
te.spellCheck();
}
}

下面是在正常情况下的配置文件 Beans.xml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor">
<constructor-arg ref="spellChecker" />
<constructor-arg value="Generic Text Editor"/>
</bean>

<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="autowire.SpellChecker">
</bean>

</beans>

但是,如果你要使用自动装配 “by constructor”,那么你的 XML 配置文件将成为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="autowire.TextEditor"
autowire="constructor">
<constructor-arg value="Generic Text Editor"/>
</bean>

<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="autowire.SpellChecker">
</bean>

</beans>

Spirng基于注解的配置

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。

在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。

注解连线在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。所以如果你想在 Spring 应用程序中使用的任何注解,可以考虑到下面的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>
<!-- bean definitions go here -->

</beans>

一旦 被配置后,你就可以开始注解你的代码,表明 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package annotation;

import org.springframework.beans.factory.annotation.Required;

public class Student {
private Integer age;
private String name;

public Integer getAge() {
return age;
}

@Required
public void setAge(Integer age) {
this.age = age;
}

public String getName() {
return name;
}

@Required
public void setName(String name) {
this.name = name;
}

}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("Student.xml");
Student student = (Student) context.getBean("student");
System.out.println("Name : " + student.getName() );
System.out.println("Age : " + student.getAge() );
}
}

Student.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>

<!-- Definition for student bean -->
<bean id="student" class="annotation.Student">
<property name="name" value="Zara" />
<property name="age" value="11" />
</bean>

</beans>

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package annotation;


public class Student {
private Integer age;
private String name;

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Profile.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package annotation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Profile {
@Autowired
//在属性中使用 **@Autowired** 注释来除去 setter 方法。
@Qualifier("student2")
//指定Bean
private Student student;

public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("Student.xml");
Profile profile = (Profile)context.getBean("profile");
profile.printName();
profile.printAge();
}
}

Student.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>

<bean id="profile" class="annotation.Profile" >
</bean>

<!-- Definition for student bean -->
<bean id="student1" class="annotation.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>

<bean id="student2" class="annotation.Student">
<property name="name" value="join" />
<property name="age" value="13"/>
</bean>

</beans>

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
2
3
4
5
6
7
8
9
10
11
12
package annotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld() {
return new HelloWorld();
}
}

HelloWorld.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package annotation;

public class HelloWorld {
private String message;

public void setMessage(String message){
this.message = message;
}

public void getMessage(){
System.out.println("Your Message : " + message);
}
}

MainApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp3 {

public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);

HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}
}

注入Bean的依赖性:

当 @Beans 依赖对方时,表达这种依赖性非常简单,只要有一个 bean 方法调用另一个。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}

这里,foo Bean 通过构造函数注入来接收参考基准。

@Import注释:

@import 注解允许从另一个配置类中加载 @Bean 定义。考虑 ConfigA 类,如下所示:

1
2
3
4
5
6
7
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}

你可以在另一个 Bean 声明中导入上述 Bean 声明,如下所示:

1
2
3
4
5
6
7
8
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B a() {
return new A();
}
}

现在,当实例化上下文时,不需要同时指定 ConfigA.class 和 ConfigB.class,只有 ConfigB 类需要提供,如下所示:

1
2
3
4
5
6
7
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}

生命周期回调:

@Bean 注解支持指定任意的初始化和销毁的回调方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和销毁方法的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}

@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public Foo foo() {
return new Foo();
}
}

指定 Bean 的范围:

默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法,如下所示:

1
2
3
4
5
6
7
8
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public Foo foo() {
return new Foo();
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
package annotation;

public class HelloWorld {
private String message;

public void setMessage(String message){
this.message = message;
}

public void getMessage(){
System.out.println("Your Message : " + message);
}
}

CStartEventHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
package annotation;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;

public class CStartEventHandler
implements ApplicationListener<ContextStartedEvent> {
@Override
public void onApplicationEvent(ContextStartedEvent arg0) {
System.out.println("ContextStartedEvent Received");
}
}

CStopEventHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package annotation;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStoppedEvent;

public class CStopEventHandler implements
ApplicationListener<ContextStoppedEvent> {

@Override
public void onApplicationEvent(ContextStoppedEvent arg0) {
System.out.println("ContextStoppedEvent Received");
}
}

MainApp4.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package annotation;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp4 {
public static void main(String[] args) {
ConfigurableApplicationContext context = new
ClassPathXmlApplicationContext("Event.xml");
context.start();

HelloWorld obj = (HelloWorld)context.getBean("helloWorld");
obj.getMessage();

context.stop();
}
}

Event.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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-3.0.xsd">

<bean id="helloWorld" class="annotation.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>

<bean id="cStartEventHandler"
class="annotation.CStartEventHandler"/>

<bean id="cStopEventHandler"
class="annotation.CStopEventHandler"/>
</beans>

运行结果:

1
2
3
ContextStartedEvent Received
Your Message : Hello World!
ContextStoppedEvent Received

Spring中的自定义事件:

这个是 CustomEvent.java 文件的内容:

1
2
3
4
5
6
7
8
9
10
package annotation;
import org.springframework.context.ApplicationEvent;
public class CustomEvent extends ApplicationEvent{
public CustomEvent(Object source) {
super(source);
}
public String toString() {
return "My Custom Event";
}
}

下面是 CustomEventPublisher.java 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package annotation;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
public class CustomEventPublisher
implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public void setApplicationEventPublisher
(ApplicationEventPublisher publisher){
this.publisher = publisher;
}
public void publish() {
CustomEvent ce = new CustomEvent(this);
publisher.publishEvent(ce);
}
}
package com.tutorialspoint;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
public class CustomEventPublisher
implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public void setApplicationEventPublisher
(ApplicationEventPublisher publisher){
this.publisher = publisher;
}
public void publish() {
CustomEvent ce = new CustomEvent(this);
publisher.publishEvent(ce);
}
}

下面是 CustomEventHandler.java 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
package annotation;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStoppedEvent;

public class CStopEventHandler implements
ApplicationListener<ContextStoppedEvent> {

@Override
public void onApplicationEvent(ContextStoppedEvent arg0) {
System.out.println("ContextStoppedEvent Received");
}
}

下面是 MainApp.java 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package annotation;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp5 {

public static void main(String[] args) {
ConfigurableApplicationContext context = new
ClassPathXmlApplicationContext("Custom.xml");
CustomEventPublisher cvp = (CustomEventPublisher)
context.getBean("customEventPublisher");
cvp.publish();
cvp.publish();
}
}

下面是配置文件Custom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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-3.0.xsd">

<bean id="customEventHandler"
class="annotation.CustomEventHandler"/>

<bean id="customEventPublisher"
class="annotation.CustomEventPublisher"/>

</beans>

程序运行结果:

1
2
My Custom Event
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类配置的方式。

坚持原创技术分享,您的支持将鼓励我继续创作!