关于Spring的二三事

一、在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版

 

 


 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值