Plusaber's Blog

  • Home

  • Tags

  • Categories

  • Archives

Spring(4)_面向切面编程(AOP)

Posted on 2015-08-02 | In java | Comments:

面向切面编程(AOP)

依赖注入让相互协作的软件组件保持松散耦合,而AOP编程允许把遍布应用的功能分离出来形成可重用的组件。

面向切面编程往往被定义为促使应用程序分离关注点的一项技术。系统通常由许多不同组件组成,每一个组件各负责一块特定功能。一些组件除了实现自身核心的功能之外,还经常承担额外的职责。诸如日志、事务管理和安全之类的系统服务经常融入到有自身核心业务逻辑的组件中去,这些系统业务通常称为横切关注点,因为它们总是跨越系统的多个组件。

如果将这些关注点,分散到多个组件中去,代码将出现双重复杂性:

  • 如果横切关注点实现代码将重复出现在多个组件(模块)中。如果要改变这些关注点的逻辑,则必须修改每个组件的相关代码。即是我们将其抽象成模块,其他模块只是调用其方法,但是方法的调用还是会重复出现在各个模块中。
  • 组件的代码可能会因为处理这些横切关注点的使用(也就是与自身核心业务无关的代码)变得混乱。一个像地址簿增加地址条目的方法应该只关注如何添加地址,而不是同时需要关注是不是安全或者是否需要支持事务。

下面图片展示了这种复杂性。左边的业务对象与系统级服务集合的过于紧密。每个对象不但需要知道它需要日志,安全控制,事务处理,而且还要亲自执行这些服务。

Spring_aop_example1

AOP使这些服务模块化,并以声明的方式讲它们应用到需要影响的组件中去。结果是功能组件具有更高内聚性以及更加关注自身业务,完全不需要了解系统服务的复杂性,从而保持POJO简单。

如下面图片所示,AOP可以让我们使用各种功能层去包裹核心业务。这些功能层也声明的方式应用到系统中,核心业务甚至根本不知道这些服务的存在。使得我们可以将横切关注点和业务逻辑模块分离。

Spring_aop_example2

假设knight在执行任务之前需要辅助者进行一些操作,最直接的方式是由knight自己直接调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.PrintStream;
public class Minstrel {
private PrintStream stream;
public Minstrel(PrintStream stream) {
this.stream = stream;
}
public void singBeforeQuest() {
stream.println("Fa la la, the knight is so brave!");
}
public void singAfterQuest() {
stream.println("Tee hee hee, the brave knight " +
"did embark on a quest!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.springinaction.knights;
public class BraveKnight implements Knight {
private Quest quest;
private Minstrel minstrel;
public BraveKnight(Quest quest, Minstrel minstrel) {
this.quest = quest;
this.minstrel = minstrel;
}
public void embarkOnQuest() throws QuestException {
minstrel.singBeforeQuest();
quest.embark();
minstrel.singAfterQuest();
}
}
//Should a knight manage his own minstrel?

这样做可以达到预期效果,但是knight引入不少与自身业务无关的代码,如果需要更多的战斗前准备,代码将会变得很混乱和难以修改。我们希望kinght只需关注自己的业务,Minstrel能自己判断并在knight战斗前后执行相应操作。

我们可以把Minstrel抽象为一个切面:

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
<?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/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="com.springinaction.knights.SlayDragonQuest"> <constructor-arg value="#{T(System).out}" />
</bean>
<bean id="minstrel" class="com.springinaction.knights.Minstrel">
<constructor-arg value="#{T(System).out}" />
</bean>
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="embark"
expression="execution(* *.embarkOnQuest(..))"/>
<aop:before pointcut-ref="embark"
method="singBeforeQuest"/>
<aop:after pointcut-ref="embark"
method="singAfterQuest"/>
</aop:aspect>
</aop:config>
</beans>

这里使用Spring的AOP配置的命名空间把Minstrel Bean声明为一个切面。首先把其声明一个bean,然后在<aop:aspect>元素中引用该Bean。更加具体的语法说明会在后面介绍。

简而言之,横切关注点可以被描述为影响应用多处的功能,而切面可以帮助我们模块化横切关注点。

上面图片展示了一个被划分为模块的典型应用。每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类是的辅助功能,例如安全和事务管理。

继承和委托是最常见的实现重用通用功能的面向对象技术。但继承往往会导致一个脆弱的对象体系;而使用委托可能需要对委托对象进行复杂的调用。

切面提供了取代继承和委托的另一种选择,而且在很多场景下更清晰简洁。在使用面向切面编程时,我们仍然在一个地方定义通用功能,但是我们可以通过声明的方式定义这个功能以何种方式在何处应用,而无需修改受影响的类。横切关注点可以被模块化为特殊的类,这些类被称为切面。这样做有两个好处:首先,每个关注点现在都只集中于一处,而不是分散到多处代码;其次,服务被服务模块更加简洁,因为它们只包含主要关注点(自身核心功能)的代码,而横切关注点的代码被转移到切面中了。

AOP 术语

在我们开始使用 AOP 工作之前,让我们熟悉一下 AOP 概念和术语。这些术语并不特定于 Spring,而是与 AOP 有关的。

概念

项 描述
Aspect 也就是前面介绍的横切关注点,需要插入到多个其他核心模块中。Aspect是通知和切点的集合。通知和切点定义了切面的全部内容—需要完成什么操作,在何处,何时完成操作。
Advice 定义了切面的要完成的工作以及何时使用,这里的何时使用是相对于切点,如在切点之前调用,或者之后调用,或环绕调用。Spring切面可以应用5种类型的通知。
Join point 程序执行过程中的一个点,这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。
Pointcut 是join point的子集,包含一个或多个连接点,我们会将通知插入这些点执行。如果说Advice定义了切面(Aspect)的”什么”和”何时”,那么切点定义了切面的”何处”,切点定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称模式来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。
Introduction 引用允许你添加新方法或属性到现有的类中。
Target object 被一个或者多个方面所通知的对象,也就是被代理对象。也称为被通知对象。
Weaving Weaving是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被weaving(织入)到目标对象中。在目标对象的生命周期可以有多个点进行织入,包括编译期、类加载时和运行时完成。

通知的类型

Spring Aspect应用5中类型的通知:

通知 描述
Before 在方法被调用之前调用通知
After 在方法完成之后调用通知,无论方法执行是否成功
After-returning 在方法成功执行后调用通知
After-throwing 在方法抛出异常后调用通知
Around 在被通知的方法调用之前和调用之后执行自定义的行为

Spring对AOP的支持

并不是所有的AOP框架都是一样的,它们在连接点模型上可能有强弱之分。有的允许对字段修饰符级别应用通知,而另一些只支持与方法调用相关的连接点。它们织入切面的方式和时机也有所不同。但是无论如何,创建切点来定义切面织入的连接点是AOP框架的基本功能。

AOP框架主要有:

  • AspectJ
  • JBoss AOP
  • Spring AOP

这里主要介绍Spring AOP,Spring提供了4中各具特色的AOP支持:

  • 基于代理的经典AOP;
  • @AspectJ注解驱动的切面;
  • 纯POJO切面;
  • 注入式AspectJ切面

前3种都是Spring基于代理的AOP变体,因此Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单方法拦截的范畴(比如构造器或属性拦截),那么应该考虑在AspectJ里实现切面,利用Spring的DI把Spring Bean注入到AspectJ切面里。

Spring在运行期通知对象

通过在代理类中包括切面,Spring在运行期将切面织入到Spring管理的Bean中。如图所示,代理类封装了目标类,并拦截被通知的方法的调用,再将调用转发给真正的目标Bean。

当拦截到方法调用时,在调用目标Bean方法之前,代理会执行切面逻辑。
直到应用需要被代理的Bean时,Spring才创建代理对象。如果使用的是ApplicationContext,在ApplicationContext从BeanFactory中加载所有bean时,Spring创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译期来织入Spring AOP的切面。

Spring只支持方法连接点

正如前面所提到过的,各种AOP实现支持多种不同的连接点模型。因为Spring基于动态代理,所以Spring只支持方法连接点。这与其他一些AOP框架是不同的,例如AspectJ和Jboss,除了方法切点,它们还提供了字段和构造器接入点。Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。而且Spring也不支持构造器连接点,我们无法在Bean创建时应用通知。

但是方法可以满足绝大部分的需求,如果需要方法连接之外的连接点拦截,我们可以利用Aspect来协助Spring AOP。

Spring中基于XML的AOP

为了在本节的描述中使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述:

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

<!-- bean definition & AOP specific configuration -->
</beans>
AOP configuration element Purpose
<aop:advisor> Defines an AOP advisor.
<aop:after> Defines an AOP after advice (regardless of whether the advised method returns successfully).
<aop:after-returning> Defines an AOP after-returning advice.
<aop:after-throwing> Defines an AOP after-throwing advice.
<aop:around> Defines an AOP around advice.
<aop:aspect> Defines an aspect.
<aop:aspectj-autoproxy> Enables annotation-driven aspects using @AspectJ.
<aop:before> Defines an AOP before advice.
<aop:config> The top-level AOP element. Most \<aop:\*\> elements must be contained within \<aop:config\>.
<aop:declare-parents> Introduces additional interfaces to advised objects that are trans- parently implemented.
<aop:pointcut> Defines a pointcut.

声明一个 aspect

1
2
3
4
5
6
7
8
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

这样,“aBean”将被配置和依赖注入,并在aspect被使用。

声明一个切入点

一个切入点有助于确定使用不同通知执行的感兴趣的连接点(即方法)。在处理基于配置的 XML 架构时,切入点将会按照如下所示定义:

1
2
3
4
5
6
7
8
9
10
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

下面的示例定义了一个名为 “businessService” 的切入点,该切入点将与 com.tutorialspoint 包下的 Student 类中的 getName() 方法相匹配:

1
2
3
4
5
6
7
8
9
10
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.tutorialspoint.Student.getName(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

另外,Spring 2.5还引入了一个新的bean()指示器,该指示器允许我们在切点表达式中使用Bean的ID来表示Bean,用来限制切点只匹配特定的Bean。

1
execution(* com.tutorialspoint.Student.getName(..)) and bean(student1)

或者

1
execution(* com.tutorialspoint.Student.getName(..)) and !bean(student1)

更多的指示器,Spring uses AspectJ’s pointcut expression language to define Spring aspects.

AspectJ designator Description
args() Limits join-point matches to the execution of methods whose arguments are instances of the given types
@args() Limits join-point matches to the execution of methods whose arguments are annotated with the given annotation types
execution() Matches join points that are method executions
this() Limits join-point matches to those where the bean reference of the AOP proxy is of a given type
target() Limits join-point matches to those where the target object is of a given type
@target() Limits matching to join points where the class of the executing object has an annotation of the given type
within() Limits matching to join points within certain types
@within() Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)
@annotation Limits join-point matches to those where the subject of the join point has the given annotation

声明通知

你可以使用 元素在一个中声明五个通知中的任何一个,如下所示:

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
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<!-- a before advice definition -->
<aop:before pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after advice definition -->
<aop:after pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after-returning advice definition -->
<!--The doRequiredTask method must have parameter named retVal -->
<aop:after-returning pointcut-ref="businessService"
returning="retVal"
method="doRequiredTask"/>
<!-- an after-throwing advice definition -->
<!--The doRequiredTask method must have parameter named ex -->
<aop:after-throwing pointcut-ref="businessService"
throwing="ex"
method="doRequiredTask"/>
<!-- an around advice definition -->
<aop:around pointcut-ref="businessService"
method="doRequiredTask"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

Example

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 com.tutorialspoint;
public class Logging {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
*/
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tutorialspoint;
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");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
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
32
33
34
<?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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll"
expression="execution(* com.tutorialspoint.*.*(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll"
returning="retVal"
method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="selectAll"
throwing="ex"
method="AfterThrowingAdvice"/>
</aop:aspect>
</aop:config>

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

<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content

Spring中基于@AspectJ注解的 AOP

@AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格。通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的。

1
<aop:aspectj-autoproxy/>

声明一个 aspect

Aspects 类和其他任何正常的 bean 一样,除了它们将会用 @AspectJ 注释之外,它和其他类一样可能有方法和字段,如下所示:

1
2
3
4
5
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AspectModule {
}

它们将在 XML 中按照如下进行配置,就和其他任何 bean 一样:

1
2
3
<bean id="myAspect" class="org.xyz.AspectModule">
<!-- configure properties of aspect here as normal -->
</bean>

声明一个切入点

一个切入点有助于确定使用不同建议执行的感兴趣的连接点(即方法)。在处理基于配置的 XML 架构时,切入点的声明有两个部分:

一个切入点表达式决定了我们对哪个方法被执行感兴趣。

一个切入点标签包含一个名称和任意数量的参数。方法的真正内容是不相干的,并且实际上它应该是空的。
下面的示例中定义了一个名为 ‘businessService’ 的切入点,该切入点将与 com.tutorialspoint 包下的类中可用的每一个方法相匹配:

1
2
3
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression
private void businessService() {} // signature

下面的示例中定义了一个名为 ‘getname’ 的切入点,该切入点将与 com.tutorialspoint 包下的 Student 类中的 getName() 方法相匹配:

1
2
3
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.tutorialspoint.Student.getName(..))")
private void getname() {}

声明建议

你可以使用 @{ADVICE-NAME} 注释声明五个建议中的任意一个,如下所示。这假设你已经定义了一个切入点标签方法 businessService():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Before("businessService()")
public void doBeforeTask(){
...
}
@After("businessService()")
public void doAfterTask(){
...
}
@AfterReturning(pointcut = "businessService()", returning="retVal")
public void doAfterReturnningTask(Object retVal){
// you can intercept retVal here.
...
}
@AfterThrowing(pointcut = "businessService()", throwing="ex")
public void doAfterThrowingTask(Exception ex){
// you can intercept thrown exception here.
...
}
@Around("businessService()")
public void doAroundTask(){
...
}

Example

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.tutorialspoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
@Aspect
public class Logging {
/** Following is the definition for a pointcut to select
* all the methods available. So advice will be called
* for all the methods.
*/
@Pointcut("execution(* com.tutorialspoint.*.*(..))")
private void selectAll(){}
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
@After("selectAll()")
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
@AfterReturning(pointcut = "selectAll()", returning="retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised by any method.
*/
@AfterThrowing(pointcut = "selectAll()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tutorialspoint;
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");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

<aop:aspectj-autoproxy/>

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

<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>

</beans>

一旦你已经完成的创建了源文件和 bean 配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content

Further

环绕通知的编写

基于@AspectJ注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package concert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Audience {
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones");
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP!!!");
} catch (Throwable e) {
System.out.println("Demanding a refund");
} }
}

基于XML的也是同样配置。

为通知传递参数

基于@AspectJ注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package soundsystem;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TrackCounter {
private Map<Integer, Integer> trackCounts =
new HashMap<Integer, Integer>();
@Pointcut(
"execution(* soundsystem.CompactDisc.playTrack(int)) " +
"&& args(trackNumber)")
public void trackPlayed(int trackNumber) {}
@Before("trackPlayed(trackNumber)")
public void countTrack(int trackNumber) {
int currentCount = getPlayCount(trackNumber);
trackCounts.put(trackNumber, currentCount + 1);
}
public int getPlayCount(int trackNumber) {
return trackCounts.containsKey(trackNumber)
} }

基于XML的配置:

通过切面引入新功能

我们前面做的其实就是为对象已经拥有的方法添加了新功能。同样的,我们也可以为一个对象添加新的方法。利用Spring的Introduction我们可以为Spring Bean添加新方法。

使用Spring AOP,我们可以为Bean引入新的方法。拦截调用并委托给实现该方法的其他对象。实际上,Bean的实现被拆分到了多个类。

1
2
3
4
package concert;
public interface Encoreable {
void performEncore();
}
1
2
3
4
5
6
7
8
9
package concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
@Aspect
public class EncoreableIntroducer {
@DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}

基于XML的配置:

1
2
3
4
5
6
7
<aop:aspect>
<aop:declare-parents
types-matching="concert.Performance+"
implement-interface="concert.Encoreable"
default-impl="concert.DefaultEncoreable"
/>
</aop:aspect>

注入AspectJ切面

Spring(3)_事件处理

Posted on 2015-07-23 | In java | Comments:

From:Spring 中的事件处理

Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。

标准事件

Spring 提供了以下的标准事件:

Spring 内置事件 描述
ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。

ContextStartedEvent|当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。|
|ContextStoppedEvent|当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。|
|ContextClosedEvent|当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。|
|RequestHandledEvent|这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。|

由于 Spring 的事件处理是单线程的,所以如果一个事件被发布,直至并且除非所有的接收者得到的该消息,该进程被阻塞并且流程将不会继续。因此,如果事件处理被使用,在设计应用程序时应注意。|

监听上下文事件

为了监听上下文事件,一个 bean 应该实现只有一个方法 onApplicationEvent() 的 ApplicationListener 接口。因此,我们写一个例子来看看事件是如何传播的,以及如何可以用代码来执行基于某些事件所需的任务。

1
2
3
4
5
6
7
8
9
10
package com.tutorialspoint;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
1
2
3
4
5
6
7
8
9
package com.tutorialspoint;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class CStartEventHandler
implements ApplicationListener<ContextStartedEvent>{
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
1
2
3
4
5
6
7
8
9
package com.tutorialspoint;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStoppedEvent;
public class CStopEventHandler
implements ApplicationListener<ContextStoppedEvent>{
public void onApplicationEvent(ContextStoppedEvent event) {
System.out.println("ContextStoppedEvent Received");
}
}

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

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

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

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

// Let us raise a start event.
context.start();

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

obj.getMessage();

// Let us raise a stop event.
context.stop();
}
}

下面是配置文件 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">

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

<bean id="cStartEventHandler"
class="com.tutorialspoint.CStartEventHandler"/>

<bean id="cStopEventHandler"
class="com.tutorialspoint.CStopEventHandler"/>

</beans>

一旦你完成了创建源和 bean 的配置文件,我们就可以运行该应用程序。如果你的应用程序一切都正常,将输出以下消息:

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

自定义事件

通过扩展 ApplicationEvent,创建一个事件类 CustomEvent。这个类必须定义一个默认的构造函数,它应该从 ApplicationEvent 类中继承的构造函数。

一旦定义事件类,你可以从任何类中发布它,EventClassPublisher 实现了 ApplicationEventPublisherAware。你还需要在 XML 配置文件中声明这个类作为一个 bean,之所以容器可以识别 bean 作为事件发布者,是因为它实现了 ApplicationEventPublisherAware接口。

1
2
3
4
5
6
7
8
9
10
package com.tutorialspoint;
import org.springframework.context.ApplicationEvent;
public class CustomEvent extends ApplicationEvent{
public CustomEvent(Object source) {
super(source);
}
public String toString(){
return "My Custom Event";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}
1
2
3
4
5
6
7
8
package com.tutorialspoint;
import org.springframework.context.ApplicationListener;
public class CustomEventHandler
implements ApplicationListener<CustomEvent>{
public void onApplicationEvent(CustomEvent event) {
System.out.println(event.toString());
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tutorialspoint;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
CustomEventPublisher cvp =
(CustomEventPublisher) context.getBean("customEventPublisher");
cvp.publish();
cvp.publish();
}
}

下面是配置文件 Beans.xml:

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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="customEventHandler"
class="com.tutorialspoint.CustomEventHandler"/>

<bean id="customEventPublisher"
class="com.tutorialspoint.CustomEventPublisher"/>

</beans>

一旦你完成了创建源和 bean 的配置文件后,我们就可以运行该应用程序。如果你的应用程序一切都正常,将输出以下信息:

1
2
My Custom Event
My Custom Event

Immutable Stack and Queue

Posted on 2015-07-16 | In DSA | Comments:

Immutable Stack

最近实验需要使用到immutable stack,直接复制创建新的栈时间和空间效率都十分低,这里对高效实现immutable stack以及queue进行一个简单的学习总结。由于已经有文章很好的介绍了实现思想,主要是利用链表的思想,这里就直接引用了,具体参考Immutable Stack.

Let’s start with something simple: an immutable stack.

Now, immediately I hear the objection: a stack is by its very nature something that changes. A stack is an abstract data type (ADT) with the interface

1
2
3
void Push(T t);
T Pop();
bool IsEmpty { get; }

You push stuff onto it, you pop stuff off of it, it changes. How can it be immutable?

Every time you need to make a data structure immutable, you use basically the same trick: an operation which “changes” the data structure does so by constructing a new data structure. The original data structure stays the same.

How can that possibly be efficient? Surely we’ll be allocating memory all over the place! Well, actually, in this case, no. An immutable stack is every bit as efficient as a mutable stack. Even better: in some cases, it can be considerably more efficient, as we’ll see.

Let’s start by defining an interface for our immutable structure. While we’re at it, we’ll fix a problem with the stack ADT above, namely that you cannot interrogate the stack without changing it. And we’ll make the stack enumerable just for the heck of it:

1
2
3
4
5
6
7
public interface IStack<T> : IEnumerable<T>
{
IStack<T> Push(T value);
IStack<T> Pop();
T Peek();
bool IsEmpty { get; }
}

Pushing and popping give you back an entirely new stack, and Peek lets you look at the top of the stack without popping it.

Now let’s think about constructing one of these things here.

Clearly if we have an existing stack we can construct a new one by pushing or popping it. But we have to start somewhere. Since every empty stack is the same, it seems sensible to have a singleton empty stack.

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
public sealed class Stack<T> : IStack<T>
{
private sealed class EmptyStack : IStack<T>
{
public bool IsEmpty { get { return true; } }
public T Peek() { throw new Exception("Empty stack"); }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IStack<T> Pop() { throw new Exception("Empty stack"); }
public IEnumerator<T> GetEnumerator() { yield break; }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(}
}
private static readonly EmptyStack empty = new EmptyStack();
public static IStack<T> Empty { get { return empty; } }
private readonly T head;
private readonly IStack<T> tail;
private Stack(T head, IStack<T> tail)
{
this.head = head;
this.tail = tail;
}
public bool IsEmpty { get { return false; } }
public T Peek() { return head; }
public IStack<T> Pop() { return tail; }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IEnumerator<T> GetEnumerator()
{
for(IStack<T> stack = this; !stack.IsEmpty ; stack = stack.Pop())
yield return stack.Peek();
}
IEnumerator IEnumerable.GetEnumerator() {return this.GetEnumerator();}

And now we can easily create stacks and push stuff onto them. Notice how the fact that we have immutability means that stacks with the same tail can share state, saving on memory:

1
2
3
4
IStack<int> s1 = Stack<int>.Empty;
IStack<int> s2 = s1.Push(10);
IStack<int> s3 = s2.Push(20);
IStack<int> s4 = s2.Push(30); // shares its tail with s3.

Immutable Queue

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import java.util.NoSuchElementException;
import java.util.Queue;

public class PersistentQueue<E> {

private static class PersistentStack<E> {
private E head;
private PersistentStack<E> tail;
private int size;

private PersistentStack(E obj, PersistentStack<E> tail) {
this.head = obj;
this.tail = tail;
this.size = tail.size + 1;
}

public static PersistentStack emptyStack() {
return new PersistentStack();
}

private PersistentStack() {
this.head = null;
this.tail = null;
this.size = 0;
}

public PersistentStack<E> toReverseStack() {
PersistentStack<E> stack = new PersistentStack<E>();
PersistentStack<E> tail = this;
while (!tail.isEmpty()) {
stack = stack.push(tail.head);
tail = tail.tail;
}
return stack;
}

public boolean isEmpty() {
return this.size == 0;
}

public PersistentStack<E> push(E obj) {
return new PersistentStack<E>(obj, this);
}
}

private PersistentStack<E> order;
private PersistentStack<E> reverse;


private PersistentQueue(PersistentStack<E> order, PersistentStack<E> reverse) {
this.order = order;
this.reverse = reverse;
}

public PersistentQueue() {
this.order = PersistentStack.emptyStack();
this.reverse = PersistentStack.emptyStack();
}

public PersistentQueue<E> enqueue(E e) {
if (null == e)
throw new IllegalArgumentException();
return new PersistentQueue<E>(this.order.push(e), this.reverse);
}

public PersistentQueue<E> dequeue() {
if (this.isEmpty())
throw new NoSuchElementException();
if (!this.reverse.isEmpty()) {
return new PersistentQueue<E>(this.order, this.reverse.tail);
} else {
// revers the ordered stack then "clean" that stack
return new PersistentQueue<E>(PersistentStack.emptyStack(),
this.order.toReverseStack().tail);
}
}

private void balanceQueue() {
this.reverse = this.order.toReverseStack();
this.order = PersistentStack.emptyStack();
}

public E peek() {
if (this.isEmpty())
throw new NoSuchElementException();
if (this.reverse.isEmpty())
balanceQueue();
return this.reverse.head;
}

public boolean isEmpty() {
return size() == 0;
}

public int size() {
return this.order.size + this.reverse.size;
}
}

Creating a simple immutable stack

Last time, I explained the basic meaning of immutability. The simplest useful example of an immutable class is an immutable stack. Immutable stacks work just like regular stacks – with Push(), Pop(), and Peek() methods – except that instead of mutating the original instance, Push() and Pop() return a new, modified, instance.

In code, that looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IStack<T> {
IStack<T> Push(T element);
IStack<T> Pop();
T Peek();
bool IsEmpty { get; }
}

IStack<int> myStack = empty;
myStack = myStack.Push(1).Push(2).Push(3);

while (!myStack.IsEmpty) {
Console.WriteLine(myStack.Peek());
myStack = myStack.Pop();
}

Each implementation of this interface would supply a singleton empty instance; since they are immutable; there is no need to have more than one empty instance. Thus, there is no need for a constructor. Since Pop() needs to return the new stack, it cannot return the removed item; therefore, Peek() is the only way to get the item.

As a side benefit, this pattern naturally enables and encourages fluent interfaces, since each mutation methods returns a new instance. (eg, myStack.Push(1).Push(2).Push(3))

The naive implementation of this interface would use a list, and copy the list when creating a new stack:

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
public class NaiveStack<T> : IStack<T> {
private readonly ReadOnlyCollection<T> items;
private NaiveStack(IList<T> items) {
this.items = new ReadOnlyCollection<T>(items);
}

public static readonly NaiveStack<T> Empty
= new NaiveStack<T>(new T[0]);

public IStack<T> Push(T element) {
var list = new List<T>(items);
list.Add(element);
return new NaiveStack<T>(list);
}

public IStack<T> Pop() {
if (IsEmpty)
throw new InvalidOperationException("Stack is empty");
var list = new List<T>(items);
list.RemoveAt(list.Count - 1);
return new NaiveStack<T>(list);
}

public T Peek() { return items.Last(); }
public bool IsEmpty {
get { return items.Count == 0; }
}
}

The problem with this approach is that each mutation requires an O(n) copy to create the list behind the new instance. This makes Push() and Pop() awfully slow, and vastly increases the memory footprint until the intermediate instances can be GCd.

To solve these problems, we can design the stack as a persistent data structure. Instead of copying anything when pushing on to a stack, we cab make the new stack instance hold only the newly added item, and maintain a reference to the previous instance storing the rest of the items. Popping from the stack can then simply return the previous instance. Basically, the stack becomes an immutable single-linked list.

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
32
33
34
35
36
public abstract class PersistentStack<T> : IStack<T> {
public static readonly PersistentStack<T> Empty = new EmptyNode();

public IStack<T> Push(T element) {
return new LinkNode(this, element);
}

private class EmptyNode : PersistentStack<T> {
public override IStack<T> Pop() {
throw new InvalidOperationException("Stack is empty");
}
public override T Peek() {
throw new InvalidOperationException("Stack is empty");
}
public override bool IsEmpty { get { return true; } }
}

private class LinkNode : PersistentStack<T> {
readonly IStack<T> previous;
readonly T element;

public LinkNode(IStack<T> previous, T element) {
this.previous = previous;
this.element = element;
}

public override IStack<T> Pop() {
return previous;
}
public override T Peek() { return element; }
public override bool IsEmpty { get { return false; } }
}
public abstract IStack<T> Pop();
public abstract T Peek();
public abstract bool IsEmpty { get; }
}

In this implementation, the empty and non-empty nodes are different enough that I decided to use polymorphism rather than if statements. The PersistentStack type contains the only common logic (Push()); everything else is implement by the two concrete classes.

Here, the empty stack truly is a singleton; only one instance will ever be created for each element type. It can only be used as a starting point to create new stacks; the other methods simply throw exceptions. It also serves as the final node in the chain for all non-empty stacks.

Pushing onto any stack (empty or not) returns a new node that holds the new item and points to the previous stack; popping a non-empty stack simply returns that reference.

Like other linked lists, this approach implements both Push() and Pop() in O(1) in both memory and time.

Spring(2)_依赖注入

Posted on 2015-07-02 | In java | Comments:

Spring容器

Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周 期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Be ans,我们将在下一章中进行讨论。
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Ja va 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和 配置元数据来生成完全配置和可执行的系统或应用程序。

Spring_di_example1

Spring容器分为BeanFactory容器和ApplicationContext容器。

Sping 的 BeanFactory 容器

这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor 中被定义。 BeanFactory 和相关的接口,比如,BeanFactoryAware、 Dispo sableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在 一起的第三方框架。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从 一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 Applic ationContext,除非你有更好的理由选择 BeanFactory。

Spring ApplicationContext 容器

Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bea n,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性 文件从解析文本信息和将事件传递给所指定的监听器。这个容器在org.springframework.context.Application Context interface 接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationConte xt 会被推荐使用。BeanFactory 仍然可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。

最常被使用的 ApplicationContext 接口实现:

  • FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供 给构造器 XML 文件的完整路径
  • ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提 供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中 搜索 bean 配置文件。
  • WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 be an。

Bean

被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通 过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,例如,已经在先前章节看到 的,在 XML 的表单中的 定义。

bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:

  • 如何创建一个 bean
  • bean 的生命周期的详细信息
  • bean 的依赖关系

上述所有的配置元数据转换成一组构成每个 bean 定义的下列属性。

属性 描述
class 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域 的章节中进行讨论。
constructor-arg 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创 建一个 bean 实例。
initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的 生命周期章节中进行讨论。
destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章 节中进行讨论。

Spring 配置元数据

Spring IoC 容器完全由实际编写的配置元数据的格式解耦。有下面三个重要的方法把配置元数据提供给Spring容器:

  • 基于 XML 的配置文件。
  • 基于注解的配置
  • 基于Java的配置

Bean作用域

当在 Spring 中定义一个 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产 生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需 要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。

作用域 描述
singleton 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。
prototype 该作用域将单一 bean 的定义限制在任意数量的对象实例。
request 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationCo ntext 的上下文中有效。
session 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationCo ntext的上下文中有效。
global-session 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring Applicatio nContext 的上下文中有效。
1
2
3
4
<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
<!-- collaborators and configuration for this bean go here -->
</bean>

Bean生命周期

在Spring基础中已经介绍过,bean在Spring容器的生命周期为:

为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-metho d 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容 器中移除 bean 之后,才能调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.tutorialspoint;

public class HelloWorld {
private String message;

public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
public void init(){
System.out.println("Bean is going through init.");
}
public void destroy(){
System.out.println("Bean will destroy now.");
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
context.registerShutdownHook();
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="helloWorld"
class="com.tutorialspoint.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</bean>

</beans>

默认的初始化和销毁方法
如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。框架使用 元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
<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"
default-init-method="init"
default-destroy-method="destroy">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

</beans>

另一种方式是实现接口进行回调。

1
2
3
4
5
public class ExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
1
2
3
4
5
public class ExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}

Bean后置处理器

Bean定义继承

依赖注入

每个基于应用程序的 java 都有几个对象,这些对象一起工作来呈现出终端用户所看到的工作的应用程序。当编写 一个复杂的 Java 应用程序时,应用程序类应该尽可能独立于其他 Java 类来增加这些类重用的可能性,并且在 做单元测试时,测试独立于其他类的独立性。依赖注入(或有时称为布线)有助于把这些类粘合在一起,同时保 持他们独立。

依赖注入主要有两种方式:

  • 构造器注入
  • setter注入

当对象与它们的依赖关系被提供时,解耦效果非常明显。对象不查找它的依赖关系,也不 知道依赖关系的位置或类,而这一切都由 Spring 框架控制的。

Spring 基于构造函数的依赖注入

当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。

1
2
3
4
5
6
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
1
2
3
4
5
6
7
8
9
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>

<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>

果你使用 type 属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型。例如:

1
2
3
4
5
6
<beans>
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="2001"/>
<constructor-arg type="java.lang.String" value="Zara"/>
</bean>
</beans>

Spring 基于setter的依赖注入

当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.tutorialspoint;
public class TextEditor {
private SpellChecker spellChecker;
// a setter method to inject the dependency.
public void setSpellChecker(SpellChecker spellChecker) {
System.out.println("Inside setSpellChecker." );
this.spellChecker = spellChecker;
}
// a getter method to return spellChecker
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
1
2
3
4
5
6
7
8
9
package com.tutorialspoint;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.tutorialspoint;
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();
}
}
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">

<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spellChecker"/>
</bean>

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

</beans>

注入内部Beans

Java内部类是在其他类的范围内被定义的,同理,inner beans是在其他bean的范围内定义 的 bean。因此元素内元素被称为内部bean,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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 using inner bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker">
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker"/>
</property>
</bean>

</beans>

注入集合

Spring提供了四种类型的集合的配置元素,如下所示:

元素 描述
它有助于连线,如注入一列值,允许重复。
它有助于连线一组值,但不能重复。
它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。
它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。

两种情况:(a)传递集合中直接的值 (b)传递一个bean的引用作为集合的元素。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.tutorialspoint;
import java.util.*;
public class JavaCollection {
List addressList;
Set addressSet;
Map addressMap;
Properties addressProp;
// a setter method to set List
public void setAddressList(List addressList) {
this.addressList = addressList;
}
// prints and returns all the elements of the list.
public List getAddressList() {
System.out.println("List Elements :" + addressList);
return addressList;
}
// a setter method to set Set
public void setAddressSet(Set addressSet) {
this.addressSet = addressSet;
}
// prints and returns all the elements of the Set.
public Set getAddressSet() {
System.out.println("Set Elements :" + addressSet);
return addressSet;
}
// a setter method to set Map
public void setAddressMap(Map addressMap) {
this.addressMap = addressMap;
}
// prints and returns all the elements of the Map.
public Map getAddressMap() {
System.out.println("Map Elements :" + addressMap);
return addressMap;
}
// a setter method to set Property
public void setAddressProp(Properties addressProp) {
this.addressProp = addressProp;
}
// prints and returns all the elements of the Property.
public Properties getAddressProp() {
System.out.println("Property Elements :" + addressProp);
return addressProp;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.tutorialspoint;
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");
JavaCollection jc=(JavaCollection)context.getBean("javaCollection");
jc.getAddressList();
jc.getAddressSet();
jc.getAddressMap();
jc.getAddressProp();
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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 javaCollection -->
<bean id="javaCollection" class="com.tutorialspoint.JavaCollection">

<!-- results in a setAddressList(java.util.List) call -->
<property name="addressList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</list>
</property>

<!-- results in a setAddressSet(java.util.Set) call -->
<property name="addressSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</set>
</property>

<!-- results in a setAddressMap(java.util.Map) call -->
<property name="addressMap">
<map>
<entry key="1" value="INDIA"/>
<entry key="2" value="Pakistan"/>
<entry key="3" value="USA"/>
<entry key="4" value="USA"/>
</map>
</property>

<!-- results in a setAddressProp(java.util.Properties) call -->
<property name="addressProp">
<props>
<prop key="one">INDIA</prop>
<prop key="two">Pakistan</prop>
<prop key="three">USA</prop>
<prop key="four">USA</prop>
</props>
</property>

</bean>

</beans>

注入Bean引用到集合

下面的 Bean 定义将帮助你理解如何注入 bean 的引用作为集合的元素。甚至你可以将引用和值混合在一起:

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
32
33
34
35
36
37
38
39
40
<?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 Definition to handle references and values -->
<bean id="..." class="...">

<!-- Passing bean reference for java.util.List -->
<property name="addressList">
<list>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistan</value>
</list>
</property>

<!-- Passing bean reference for java.util.Set -->
<property name="addressSet">
<set>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistan</value>
</set>
</property>

<!-- Passing bean reference for java.util.Map -->
<property name="addressMap">
<map>
<entry key="one" value="INDIA"/>
<entry key ="two" value-ref="address1"/>
<entry key ="three" value-ref="address2"/>
</map>
</property>

</bean>

</beans>

注入 null 和空字符串的值

如果你需要传递一个空字符串作为值,那么你可以传递它,如下所示:

1
2
3
<bean id="..." class="exampleBean"> 
<property name="email" value=""/>
</bean>

前面的例子相当于 Java 代码:exampleBean.setEmail("")。 如果你需要传递一个 NULL 值,那么你可以传递它,如下所示:

1
2
3
<bean id="..." class="exampleBean"> 
<property name="email"><null/></property>
</bean>

前面的例子相当于 Java 代码:exampleBean.setEmail(null)。

Beans自动装配

在前面的例子中,我们手动声明了每一个需要装配的bean的依赖关系。当需要装配的bean非常多时,工作量是很大的。

Spring容器可以在不使用和元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。

自动装配模式

下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用 元素的 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 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应 该在使用它们之前考虑到自动装配的局限性和缺点。

限制 描述
重写的可能性 你可以使用总是重写自动装配的 和 设置来指 定依赖关系。
原始数据类型 你不能自动装配所谓的简单类型包括基本类型,字符串和类。
混乱的本质 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。

Spring 自动装配 ‘byName’

这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 属性设 置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹 配项,它将注入这些 beans,否则,它将抛出异常。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.tutorialspoint;
public class TextEditor {
private SpellChecker spellChecker;
private String name;
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
1
2
3
4
5
6
7
8
9
package com.tutorialspoint;
public class SpellChecker {
public SpellChecker() {
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.tutorialspoint;
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="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spellChecker" />
<property name="name" value="Generic Text Editor" />
</bean>

<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.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="com.tutorialspoint.TextEditor"
autowire="byName">
<property name="name" value="Generic Text Editor" />
</bean>

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

</beans>

Spring 自动装配 ‘byType’

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

例如,在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellC hecker 属性,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并且用它来设置这个属性。你仍然可以 使用 标签连接其余属性。下面的例子将说明这个概念,你会发现和上面的例子没有什么区别,除了 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="com.tutorialspoint.TextEditor"
autowire="byType">
<property name="name" value="Generic Text Editor" />
</bean>

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

</beans>

Spring 由构造函数自动装配

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

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

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="com.tutorialspoint.TextEditor"
autowire="constructor">
<constructor-arg value="Generic Text Editor"/>
</bean>

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

</beans>

基于注解的配置

从 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 应该自动连接值到属性,方法和构造函数。几个重要的注解:

注解 描述
@Required @Required注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中(不能自动匹配,也不能不配置),否则容器就会抛出一个 BeanInitializationException 异常。
@Autowired 应用到 bean 属性的 setter方法,非setter方法,构造函数和属性。
@Qualifier 通过指定确切的将被连线的bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。
JSR-250 Annotations Spring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstru ct 和 @PreDestroy 注解。

Spring @Autowired注解

@Autowired注解对在哪里和如何完成自动连接提供了更多的细微的控制。

Setter 方法中的 @Autowired

你可以在 XML 文件中的 setter 方法中使用 @Autowired 注释来除去 元素。当 Spring遇到一个在 setter 方法 中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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 textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>

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

</beans>

属性中的 @Autowired

你可以在属性中使用 @Autowired 注释来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会 将这些传递过来的值或者引用自动分配给那些属性。所以利用在属性中 @Autowired 的用法,你的 TextEdit or.java 文件将变成如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
@Autowired
private SpellChecker spellChecker;
public TextEditor() {
System.out.println("Inside TextEditor constructor." );
}
public SpellChecker getSpellChecker( ){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}

构造函数中的 @Autowired

也可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文 件中没有使用 元素配置 bean ,构造函数也会被自动连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public TextEditor(SpellChecker spellChecker){
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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 textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>

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

</beans>

@Autowired 的(required=false)选项

默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Auto wired 的 (required=false) 选项关闭默认行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
private Integer age;
private String name;
@Autowired(required=false)
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Autowired
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

Spring @Qualifier 注释

可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行 装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装 配来消除混乱。下面显示的是使用 @Qualifier 注释的一个示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Profile {
@Autowired
@Qualifier("student1")
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() );
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.tutorialspoint;
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");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
}
}
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
<?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 profile bean -->
<bean id="profile" class="com.tutorialspoint.Profile">
</bean>

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

<!-- Definition for student2 bean -->
<bean id="student2" class="com.tutorialspoint.Student">
<property name="name" value="Nuha" />
<property name="age" value="2"/>
</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 自动连接语义,如下面的示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.tutorialspoint;
import javax.annotation.Resource;
public class TextEditor {
private SpellChecker spellChecker;
@Resource(name= "spellChecker")
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker(){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}

如果没有明确地指定一个 ‘name’,默认名称源于字段名或者 setter 方法。在字段的情况下,它使用的是字段名;在一个 setter 方法情况下,它使用的是 bean 属性名称。

基于Java的配置

我们已经学习了如何使用 XML 配置文件来配置 Spring bean。如果你熟悉使用 XML 配置,那么我会说,不需要再学习如何进行基于 Java 的配置是,因为你要达到相同的结果,可以使用其他可用的配置。

基于Java的配置选项,可以使你在不用配置XML的情况下编写大多数的Spring,是一种更贴近java语法的配置。

@Configuration 和 @Bean 注解

带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。最简单可行的 @Configuration 类如下所示:

1
2
3
4
5
6
7
8
9
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}

上面的代码将等同于下面的 XML 配置:

1
2
3
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>

在这里,带有 @Bean 注解的方法名称作为 bean 的 ID,它创建并返回实际的 bean。你的配置类可以声明多个 @Bean。一旦定义了配置类,你就可以使用 AnnotationConfigApplicationContext 来加载并把他们提供给 Spring 容器,如下所示:

1
2
3
4
5
6
7
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}

你可以加载各种配置类,如下所示:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}

注入 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()); // here
}
@Bean
public Bar bar() {
return new Bar();
}
}

需要注意的是,在Spring的Java配置中,通过声明方法引用一个Bean并不等同于调用该方法。如果真的这样,每次调用都将得到该Bean的一个新实例。

通过@Bean注解方法,会告知Spring我们希望该方法定义的Bean要被注册进Spring的应用上下文中。因此在其他Bean的声明方法中应用这个方法时,Spring都会拦截该方法的调用,并尝试在应用上下文中查找该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
public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}
1
2
3
4
5
6
7
@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教程。

MathJax使用

Posted on 2015-06-16 | In Blogging | Comments:

MathJax basic tutorial and quick reference

参考MathJax basic tutorial and quick reference.

MacDown无法编辑公式,可以使用Cmd Markdown。

公式标记

希腊字母

使用\alpha, \beta, …, \omega来表示$\alpha$, $\beta$, …, $\omega$,大写使用\Gamma, \Delta, …, \Omega表示$\Gamma$, $\Delta$, …, $\Omega$

上下标表示

使用^和_,如x_i^2表示$x_i^2$

组(Group)

上下标或者其他命令都是以组为单位的,组可以是单个字母,也可是使用{..}围起来的单元,如10^10表示$10^10$,10^{10}表示$10^{10}$,x_i^2表示$xi^2$,`x{i^2}`表示$x_{i^2}$。需要注意的是,命令后面如果是没有{}的group,则需要加空格已区分命令和后面的参数。

括号

可以直接使用()[]表示括号,但是这些括号不会随着公式大小调节。
另一种方法是使用\left(...\right),可以随着公式大小自动调整大小。如(\frac{\sqrt x}{y^3})表示$(\frac{\sqrt x}{y^3})$,\left(\frac{\sqrt x}{y^3}\right)表示$\left(\frac{\sqrt x}{y^3}\right)$。

\left,\right同样可以用于[],{},||。另外特殊的需要专门的标记,如\langle x \rangle来表示$\langle x \rangle$,\lceil x \rceil来表示$\lceil x \rceil$,\lfloor x \rfloor来表示$\lfloor x \rfloor$。

另外还可以通过附加.表示不可见的括号,如\left.\frac12\right\rbrace表示$\left.\frac12\right\rbrace$。

求和和积分

使用\sum和\int表示$\sum$和$\int$,\sum_{i=0}^\infty i^2表示$\sum_{i=0}^\infty i^2$。

另外,\prod表示$\prod$,\bigcup表示$\bigcup$,\bigcap表示$\bigcap$,\iint表示$\iint$。

分数

两种方式,一种方式是\frac ab,表示$\frac ab$,\frac{a+1}{b+1}表示$\frac{a+1}{b+1}$。

另一种方式是{a+1\over b+1},表示${a+1\over b+1}$。

字体

可以在公式中使用多种字体,如\mathbb{CHNQRZ}表示$\mathbb{CHNQRZ}$。还有多种字体可以参考MathJax basic tutorial and quick reference.

求根

使用sqrt,默认是平方求根,可以通过[]添加参数。如\sqrt{x^3}表示$\sqrt{x^3}$,\sqrt[3]{\frac xy}表示$\sqrt[3]{\frac xy}$。更直接的方式是直接使用上标。

一些特殊函数

\lim,\sin,\cos等,虽然可以直接用字母表示,但是只有使用了这些符号才会有符合其特征的表示,如\sin x表示$\sin x$,但是sin x只是表示$sin x$。\lim_{x\to 0}表示。

常用符合和表示

  • \lt \gt \le \ge \neq表示$\lt \gt \le \ge \neq$,可以使用\not取非,如\not\lt表示$\not\lt$。
  • \times \div \pm \mp \cdot表示$\times \div \pm \mp \cdot$
  • \cup \cap \setminus \subset \subseteq \subsetneq \supset \in \notin \emptyset \varnothing表示$\cup \cap \setminus \subset \subseteq \subsetneq \supset \in \notin \emptyset \varnothing$
  • {n+1 \choose 2k} or \binom{n+1}{2k}表示${n+1 \choose 2k}$
  • \to \rightarrow \leftarrow \Rightarrow \Leftarrow \mapsto表示$\to \rightarrow \leftarrow \Rightarrow \Leftarrow \mapsto$
  • \land \lor \lnot \forall \exists \top \bot \vdash \vDash表示$\land \lor \lnot \forall \exists \top \bot \vdash \vDash$
  • \star \ast \oplus \circ \bullet表示$\star \ast \oplus \circ \bullet$
  • \approx \sim \simeq \cong \equiv \prec表示$\approx \sim \simeq \cong \equiv \prec$
  • \infty \aleph_0表示$\infty \aleph_0$
  • \nabla \partial表示$\nabla \partial$
  • \Im \Re表示$\Im \Re$
  • a\equiv b\pmod n表示$a\equiv b\pmod n$
  • \ldots表示$\ldots$
  • \epsilon \varepsilon表示$\epsilon \varepsilon$
  • \phi \varphi表示$\phi \varphi$
  • \ell表示$\ell$
  • \\或者\cr表示new line,Hexo似乎只支持\cr
  • \backslash表示$\backslash$
  • \$表示$$$
  • \{表示${$

空格

需要增加空格,不能通过`,如a b与ab都是$a b$。 可以通过`,\quad,\qquad增加空间。

文本

使用\text{..}。如\{x\in s\mid x\text{ is extra large}\}表示${x\in s\mid x\text{ is extra large}}$。可以在text内部使用。

上标线

  • \hat用于单个字符,\hat x表示$\hat x$
  • \widehat用于多个字符,\widehat{xy}表示$\widehat{xy}$
  • \bar x表示$\bar x$
  • \overline{xyz}表示$\overline{xyz}$
  • \vec x表示$\vec x$
  • \overleftrightarrow{xy}表示$\overleftrightarrow{xy}$
  • \frac d{dx}x\dot x = \dot x^2 + x\ddot x表示$\frac d{dx}x\dot x = \dot x^2 + x\ddot x$

矩阵

Hexo似乎只支持\cr进行换行

  • 使用$$$\begin{matrix}…\end{matrix}$$$,在\begin和\end放入元素,使用&分隔元素,使用\cr换行。
1
2
3
4
5
6
7
$$
\begin{matrix}
1 & x & x^2 \cr
1 & y & y^2 \cr
1 & z & z^2 \cr
\end{matrix}
$$

表示

  • 如果需要使用括号,一样使用\left..\right,不过需要使用字符串符号,如:

with pmatrix,\begin{pmatrix}1&2\cr3&4\cr \end{pmatrix}表示$\begin{pmatrix}1&2\cr3&4\cr \end{pmatrix}$
with bmatrix,\begin{bmatrix}1&2\cr3&4\cr \end{bmatrix}表示$\begin{bmatrix}1&2\cr3&4\cr \end{bmatrix}$
with pmatrix,\begin{Bmatrix}1&2\cr3&4\cr \end{Bmatrix}表示$\begin{Bmatrix}1&2\cr3&4\cr \end{Bmatrix}$
with vmatrix,\begin{vmatrix}1&2\cr3&4\cr \end{vmatrix}表示$\begin{vmatrix}1&2\cr3&4\cr \end{vmatrix}$
with Vmatrix,\begin{Vmatrix}1&2\cr3&4\cr \end{Vmatrix}表示$\begin{Vmatrix}1&2\cr3&4\cr \end{Vmatrix}$

  • 使用\cdots \ddots vdots表示省略。
1
2
3
4
5
6
\begin{pmatrix}
1 & a_1 & a_1^2 & \cdots & a_1^n \cr
1 & a_2 & a_2^2 & \cdots & a_2^n \cr
\vdots & \vdots& \vdots & \ddots & \vdots \cr
1 & a_m & a_m^2 & \cdots & a_m^n
\end{pmatrix}

表示
\begin{pmatrix}
1 & a_1 & a_1^2 & \cdots & a_1^n \cr
1 & a_2 & a_2^2 & \cdots & a_2^n \cr
\vdots & \vdots& \vdots & \ddots & \vdots \cr
1 & a_m & a_m^2 & \cdots & a_m^n
\end{pmatrix}

  • 分割矩阵
1
2
3
4
5
6
$$ \left[
\begin{array}{cc|c}
1&2&3\cr
4&5&6
\end{array}
\right] $$

cc|c表示分割方式。

  • 行内矩阵

使用\bigl(\begin{smallmatrix} ... \end{smallmatrix}\bigr),如$\bigl( \begin{smallmatrix} a & b \cr c & d \end{smallmatrix} \bigr)$,由下面命令生成:

  • Aligned equations

Often people want a series of equations where the equals signs are aligned. To get this, use \begin{align}…\end{align}. Each line should end with \, and should contain an ampersand at the point to align at, typically immediately before the equals sign.

1
2
3
4
5
6
7
\begin{align}
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
\end{align}

Others

Refer to MathJax basic tutorial and quick reference.

1…567…17
Plusaber

Plusaber

Plusaber's Blog
82 posts
12 categories
22 tags
Links
  • LinkedIn
  • Indeed
  • Baito
  • Kaggle
© 2014 – 2019 Plusaber
Powered by Hexo v3.8.0
|
Theme – NexT.Mist v7.1.1