已经想不起来上一次买技术相关的书是什么时候了,一直以来都习惯性的下载一份电子档看看。显然,如果不是基于强烈的需求或强大的动力鞭策下,大部分的书籍也都只是蜻蜓点水,浮光掠影。
就像有位同事说的一样,有些书没有真真切切的经历过,你去看,看了就是看了,只是没有留下多少印象。我回头仔细想了想,大概就是这样,好比你去看设计模式相关的书籍,了解到了适配器模式,但是还是不够深刻。比如说某天你去面试的时候别人会问你,"你了解过适配器模式么,你有过这个模式的开发经历么,你能否画出你使用适配器模式的UML图..." 如果当时看书的时候没有动手,你可能会先“让懵逼飞一会儿”, 然后一直懵逼-_-! 设想面试之后,你便迫不及待的回去重新翻了一遍什么是设配器模式,有哪几种适配器模式,连抄代写的画出了自己的UML图。这时候书上的适配器模式就成了你自己的适配器模式。
上面大概说了下电子档书籍可能会让你带来看书上面的懈怠,毕竟大部分电子档书籍都是唾手可得的,近乎免费(偶尔有些网站需要积分来兑现),而人却又这样的一个认知惯性,就是这样越容易得到的往往不太会珍惜,所以读书往往不能物尽其用。当然了,电子档数据有其先天的优势,比如便宜,比如方便易携带,比如你猛地一天想起一个知识点,就可以全局搜索一把来精准定位等等。说到这些优点也真是纸质书籍相形见绌的,一本800页的大部头书籍让你装在包里用来乘坐地铁时打发时间,我想能坚持下来的体力应该都还不错。对于动辄就是好几十甚至好几百的书籍,再查看下这个月的消费账单,想想还是先填饱自己的肚子吧。或者哪天你突然想起来需要找到一句在书中出现的至理名言,好吧,准备瓶眼药水可劲的找吧,但愿你当时做了笔记或者折了书角。但是纸质书籍也有他的好,首先不上眼,其次可以做笔记,当你真真切切的翻过某一页的时候,你可能会谨慎的问自己,过去的一夜我真的懂了么。
说了这么多闲话,只是为了引出——我买了一本《Spring 实战(第四版)》
理由很简单:一是学习需要;二是支持下知识产权。
看了第一章和第二章,发现其实在Spring 4版本中有很多的新特性了,但是网上流传的还是传统的用法,所以决定有必要梳理分享下。
环境介绍
Intellij Idea:14.0.2
Gradle:2.7
JDK:1.8.0_60
Spring-framework: 4.0.7.RELEASE
这篇开始主要讲的是依赖注入,值得一提的是,从作者的文字看来是极力推崇自动化装配方案的,而不是稍显臃肿的基于xml配置。
环境搭建
Gradle
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。
这个东东以前从来没有接触过,但是因为Spring提供的代码是基于这个构建工具来解决依赖管理的,我暂且认为它和maven的作用等同,某种程度上来说,比maven来的要简洁(源于maven是基于xml方式配置依赖的),具体看后面就知道是不是名副其实的简洁style了。真的看下来,其实也没有什么特别的地方,起码示例项目跑起来并不那么复杂。
首先来一睹Spring实战的某个项目中用于自动化构建的文件
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
jar {
baseName = 'stereo-autoconfig'
version = '0.0.1-SNAPSHOT'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile "org.springframework:spring-context:${springVersion}"
testCompile "org.springframework:spring-test:${springVersion}"
testCompile "com.github.stefanbirkner:system-rules:${systemRulesVersion}"
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
这里的repositories实际上除了添加maven仓库以外,还支持Ivy和Ant仓库
dependencies中就是你们熟悉的maven配置,乍一看,着实简洁了太多,不用在写GroupId,ArtifactId,Version等等。这个简洁范儿,我喜欢。
还有就是gradleVersion,就是Gradle版本。
备注:本来准备自己机子上装个Gradle,但是发现机子上不知什么时候已经装好了,好吧,就不再介绍安装了,网上一查资料很多跟配置Maven差不多。
项目导入
1. 打开Intellij Idea,选择File->Import Project
2.选择导入的项目
3. 选择Gradle项目类型
4. 进入下一步选择Gradle点击finish
5. 完成build compile等步骤后,完成项目导入
6. 项目目录结构
备注:在构建项目的过程中遇到类似这样的错误
19:25:19: Executing external task 'build'...
:compileJava
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':compile'.
> Could not resolve org.springframework:spring-context:4.0.7.RELEASE.
Required by:
:stereo-mixedconfig:unspecified
> Could not HEAD 'http://maven.oschina.net/content/groups/public/org/springframework/spring-context/4.0.7.RELEASE/spring-context-4.0.7.RELEASE.pom'.
> Connection to http://maven.oschina.net refused
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 24.733 secs
Connection timed out: connect
19:25:45: External task execution finished 'build'.
主要就是中央仓库慢或者ping不通,无法下载依赖的jar包,最后通过更改中央仓库的地址为
repositories {
mavenLocal()
maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
}
Spring装配Bean的方式有三种:
在XML中进行显示配置;
在Java中进行显示配置;
隐式的bean发现机制和自动装配。
书中作者推荐的顺序依次是隐式的bean发现机制和自动装配->在Java中进行显示配置->在XML中进行显示配置,理由也很简单,尽可能的减少显示配置,好比在XML文件中声明这种。这里首先来介绍这种自动化装配方式(基于XML的方式已经在《学习Spring——依赖注入》中介绍过了)。
Spring实现自动化化装配需要从以下两个方面:
组件扫描(component scanning): Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring):Spring自动满足bean之间的依赖。
代码
这里的应用场景是CD机播放光盘,所以需要准备几个bean。类的关系图如下
首先需要CompactDisc类,再次一个CDPlayer类,我们需要Spring将CompactDisc bean注入CDPlayer来实现真正的播放音乐。
public interface CompactDisc {
void play();
}
这是一个接口,其定义了CD播放机对于光盘的操作。
这时候我们还有一个实现CompactDisc接口的实现类SgtPeppers(《Sgt. Pepper's Lonely Hearts Club Band》 是英国摇滚乐队The Beatles发行于1967年6月1日的第8张录音室专辑)。
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
这个@Component注解很关键,有了这个注解,好比该类自动告诉Spring,SgtPeppers就是一个组件类,Spring会自动为其创建bean。
使用@Component注解,Spring会默认为其创建名为sgtPeppers的bean(第一个字母小写),你也可以自定义bean的名字比如通过配置@Component("lonelyHeartsClub“”)就能创建名为lonelyHeartsClub的bean。
关于Spring会自动为其创建bean,并不完全正确,因为我还需要多做一步才能真正达到这样的效果。我们需要让Spring的组件扫描配置并开启,使得Spring能够扫描这些带有@Component或者其他注解的类。
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
@Configuration表明了它是一个配置类;
@ComponentScan表明该类启用Spring的组件扫描机制,或许你看到最多或者用的最多的应该是这种<context:component-scan base-package="soundsystem">,有了@ComponentScan,我们就不需要这种方式了,是不是简洁了很多。
另外,@ComponentScan默认是扫描与配置类在相同包下面的类,当然,你也可以自定义扫描一个或多个包路径。比如配置如下
@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig {
}
也可以指定扫描一个或多个类和接口比如
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig {
}
下面来看看Spring是如何将CompactDisc注入到CDPlayer中的
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
首先必不可少需要一个@Component注解以示该类收Spring管辖。
再者,我们看到在CDPlayer的构造函数上,我们使用了@Autowired注解,该注解是用来在Spring为CDPlayer创建bean的时候,通过这个构造器传入一个可设置给CompactDisc的bean,从而解决CDPlayer类对于CompactDisc的依赖问题。
除了通过构造函数注入bean还有通过setter方法注入,比如
@Autowired
publicvoidsetCompactDisc(CompactDisc cd) {
this.cd = cd;
}
甚至还可以通过将@Autowired注解用在任何类的方法上来实现依赖注入问题。
测试验证
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired
private MediaPlayer player;
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
@Test
public void play() {
player.play();
assertEquals(
"Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n",
log.getLog());
}
}
这一个测试类基本就能验证上面的知识点。
首先运行cdShouldNotBeNull()方法得到结果如下
该测试方法,表明通过自动化装配的方法,注入到CDPlayerTest类中的CompactDisc确实是经过Spring实例化后的bean,而不是空对象。
运行play()方法
该测试方法表明,注入到测试方法类中的MediaPlayer也是被Spring实例化过后的bean,而且可以调用该类中的play方法。
以上测试说明,Spring的自动化装配就是这么简洁高效。
另外我觉得将CDPlayer类改写如下
@Component
public class CDPlayer implements MediaPlayer {
@Autowired
private CompactDisc cd;
public CDPlayer() {
}
public void play() {
cd.play();
}
}
也是可行的,并且也通过了测试,这种方式是工作中做常见的场景之一。
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。