Spring实战——无需一行xml配置实现自动化注入

时间:2022-04-22
本文章向大家介绍Spring实战——无需一行xml配置实现自动化注入,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

  已经想不起来上一次买技术相关的书是什么时候了,一直以来都习惯性的下载一份电子档看看。显然,如果不是基于强烈的需求或强大的动力鞭策下,大部分的书籍也都只是蜻蜓点水,浮光掠影。

  就像有位同事说的一样,有些书没有真真切切的经历过,你去看,看了就是看了,只是没有留下多少印象。我回头仔细想了想,大概就是这样,好比你去看设计模式相关的书籍,了解到了适配器模式,但是还是不够深刻。比如说某天你去面试的时候别人会问你,"你了解过适配器模式么,你有过这个模式的开发经历么,你能否画出你使用适配器模式的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 Beatlesrn",
        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的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。