Spring初探-核心特性与解耦

Spring初探-核心特性与解耦

Spring用JavaBean来表示应用组件,JavaBean在此处指的是POJO,即简单老式java对象。

Spring通过以下四个方面简化java开发:

  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码

依赖注入:DI 面向切面编程:AOP

Spring不强迫使用接口或类,在Spring应用中通常看不到使用了Spring的痕迹

用依赖注入让对象间松散耦合:

为了摆脱对象间的紧耦合,使用DI,依赖关系将被自动注入到需要他们的对象中。
构造器注入,也就是构造器传入一个大的类的对象,具有该类接口的类就可以传入(注入),不用调用其他对象。使用时用传入的就可以。以书上代码为例,两个类分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.springinaction.knights;

public class BraveKnight implements Knight {

private Quest quest;

public BraveKnight(Quest quest) {
this.quest = quest;
}

public void embarkOnQuest() {
quest.embark();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.springinaction.knights;

import java.io.PrintStream;

public class SlayDragonQuest implements Quest {

private PrintStream stream;

public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}

public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}

如果一个对象只通过接口(而不是具体实现或初始化过程)来表明依赖关系,呢么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。

使用依赖注入需要两个额外文件,一个是表明协作关系的装配文件(xml或java),另一个是应用上下文Main,用来创建和组装对象。

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.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>
</beans>

二者都被声明bBean,在这里的BraveKnight在构造时传入了对SlayDragonQuest bean的引用,作为构造器的参数。

疑惑 如果id唯一,怎么实现的不同的依赖注入,一个id可以对应多个class?或者说不同的类用同一个借口进行依赖注入是否必要?

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

import org.springframework.context.support.
ClassPathXmlApplicationContext;

public class KnightMain {

public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(
"META-INF/spring/knights.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}

}

这是基于XML文件创建的应用上下文。调用该应用上下文获得了一个ID为knight的bean,这个类只是用来执行embarkOnQuest()方法,不知道是哪个对象来执行。(这说的还是有点绕啊。。)

面向切片编程,将服务模块化

在实际使用中,有很多服务如安全、日志、事务管理都是所有应用要用到的。面向切片编程(AOP)可以把它们变成切面覆盖在组件上,以声明的方式存在。

骑士不该去管理吟游诗人。吟游诗人该做好自己的事。

在配置文件声明切面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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>

先定义一个bean,再aop命名空间定义切点,切点前通知,切点后通知

此外还有通过模块封装来消除样板代码

比如JDBC的样板代码

结语

参考:Spring实战(第四版) 人民邮电出版社

具体的Spring学习在之后更新。(希望如此)

关于配置环境:有了maven后果然简单多了,参照Spring官网:https://spring.io/guides/gs/intellij-idea/

遇到了非常坑的一件事情:我的mavensetting中用了镜像但却在注释里。。。于是怎么都装不上依赖包。

最后附上Spring框架的结构图