一、在Spring之前我们该知道什么?
1、javabean是什么?
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
- 这个Java类必须具有一个无参的构造函数
- 属性必须私有化。
- 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
2、POJO是什么?
POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
3、EJB是什么?
https://blog.youkuaiyun.com/keq_keq/article/details/88565752
二、关于Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。虽然Spring用bean或者JavaBean来表示应用组件, 但并不意味着Spring组件必须要遵循JavaBean规范。所以在Spring中不妨将Javabean看做POJO的同义词,现Spring 可以做非常多的事情。但归根结底,支撑Spring的仅仅是少许的基本理念,所有的理念都可以追溯到Spring 最根本的使命上:简化Java开发。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入(DI,Dependency Injection)和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;
- 通过切面和模板减少样板式代码。
1、从最小侵入性编程开始
首先我们了解到一个概念:依赖注入
从字面意思上看,我们可以很清楚地得到一个依赖关系的概念。一般而言,我们在定义类之间的依赖关系时,很容易直接在类中写入,而依赖注入则是将这种固有关系给改写了,使得代码的复用性提高,并且让代码更加简洁易懂。
举个例子,在紧耦合的依赖关系下:
package sia.knights;
public class DamselRescuingKnight implements Knight {
private RescueDamselQuest quest;
public DamselRescuingKnight() {
this.quest = new RescueDamselQuest(); //与RescueDamselQuest()紧耦合
}
public void embarkOnQuest() {
quest.embark();
}
}
可能你会觉得,这不是一件很理所应当的事情,那么如果我们的骑士下一个任务不是拯救美女呢?
emm,可能你会觉得,那我在来一段代码……
所以当代码在依赖注入的形式下,是怎样的情形呢?
package sia.knights;
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) { //Quest 被注入
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
我们可以看到,不同于之前的 DamselRescuingKnight,BraveKnight没有自行创建探险任务,而是在构造的时候把探险任务作为构造器参数传入。这是依赖注入的方式之一,即构造器注入(constructor injection)。
(另外还有两种注入的方法如:
·使用属性的setter方法注入 ,这是最常用的方式;
·使用Filed注入(用于注解方式)。)
更重要的是,传入的探险类型是Quest,也就是所有探险任务都必须 实现的一个接口。所以,BraveKnight能够响应 RescueDamselQuest、 SlayDragonQuest、 MakeRound TableRounderQuest等任意的Quest实现。
这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对它来说,被要求挑战的探险任务只要实现了Quest接口,那么 具体是哪种类型的探险就无关紧要了。这就是DI所带来的最大收益 ——松耦合。
那么实现松耦合后,该如何实现具体的依赖注入?
假设我们需要执行SlayDragonQuest任务:
package sia.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!");
}
}
因为上述SlayDragonQuest的实现中,SlayDragonQuest用到了PrintStream stream来作为更普遍的流输入,所以当前问题就变成了如何将SlayDragonQuest交给BraveKnight呢?又 如何将PrintStream交给SlayDragonQuest呢?
- 创建应用组件之间协作的行为通常称为装配(wiring)。Spring有多 种装配bean的方式,采用XML是很常见的一种装配方式。
<?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="sia.knights.BraveKnight">
<constructor-arg ref="quest" /> <!-- 注入quest bean-->
</bean>
<bean id="quest" class="sia.knights.SlayDragonQuest"><!-- 创建SlayDragonQuest-->
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>
在这里,BraveKnight和SlayDragonQuest被声明为Spring中的 bean。就BraveKnight bean来讲,它在构造时传入了对 SlayDragonQuest bean的引用,将其作为构造器参数。同 时,SlayDragonQuest bean的声明使用了Spring表达式语言 (Spring Expression Language),将System.out(这是一 个PrintStream)传入到了SlayDragonQuest的构造器中。
- 如果XML配置不符合你的喜好的话,Spring还支持使用Java来描述配置。
package sia.knights.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sia.knights.BraveKnight;
import sia.knights.Knight;
import sia.knights.Quest;
import sia.knights.SlayDragonQuest;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
所以通过上述的依赖注入,我们就可以不改变依赖类,而改变它的依赖关系。不管你使用的是基于XML的配置还是基于Java的配置,DI所带来的收 益都是相同的。尽管BraveKnight依赖于Quest,但是它并不知道 传递给它的是什么类型的Quest,也不知道这个Quest来自哪里。与 之类似,SlayDragonQuest依赖于PrintStream,但是在编码时 它并不需要知道这个PrintStream是什么样子的。只有Spring通过它的配置,才能够了解这些组成部分是如何装配起来的。
现在我们已经声明了了BraveKnight和Quest的关系,接下来我们只需要装载XML配置文件,并把应用启动起来。
package sia.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/knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}
这其中需要注意的有 ClassPathXmlApplicationContext 类
https://blog.youkuaiyun.com/u010466329/article/details/77869311
这里的main()方法基于knights.xml文件创建了Spring应用上下文。随后它调用该应用上下文获取一个ID为knight的bean。得到Knight对象的引用后,只需简单调用embarkOnQuest()方法就可以执行所赋予 的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险 任务,而且完全没有意识到这是由BraveKnight来执行的。只有 knights.xml文件知道哪个骑士执行哪种探险任务。
内容参考自《Spring实战》第4版