spring入门

第一章 Spring入门

1. Spring简介

Spring是由Rod Johnson创建的目前使用非常广泛的开源框架。很难用一句简单的描述概括它的基本功能,Spring家族几乎可以解决我们在开发JavaEE项目中所有的问题,但Spring创建的初衷是为了解决企业级应用开发的复杂性,并致力于全方位简化Java开发。

为了降低Java开发的复杂性,Spring采取了以下4种关键策略:

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

Spring框架通过依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)2项核心技术,来贯彻和执行上述4项策略。在后面的学习中,我们将深入分析DI和AOP是如何简化我们的Java开发的。

2. Spring模块和Spring家族

2.1 Spring模块

在spring4.0版本中,共包含20个不同的模块,可以划分为6类不同的功能

在这里插入图片描述

在这里插入图片描述

  1. Spring核心容器:容器是spring框架最核心的部分,管理着Spring应用中bean的创建、配置和管理。
  2. Spring的AOP模块:在AOP模块中,Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。
  3. 数据访问与集成:Spring的JDBC和DAO(Data Access Object)模块抽象了JDBC的样板式代码,使数据库代码更加简单明了。除了JDBC,Spring还提供了对于ORM(Object-Relational Mapping)的支持,可以集成众多流行的ORM框架,包括MyBatis、Hibernate、JPA等。
  4. Web与远程调用:可与多种流程的MVC框架集成,同时Spring也自带了一个强大的MVC框架。
  5. Instrumentation:该模块提供了为JVM添加代理的功能(较少使用)
  6. 测试:致力于Spring应用的测试。

在后续的项目开发中,我们没必要一开始就添加所有的模块,而是根据项目本身功能的需要,来逐步添加所需的Spring模块。

2.2 Spring家族项目

概括地讲,整个Spring家族几乎为每一个领域的Java开发都提供了Spring编程模型,在Spring官网上可以看到除开核心框架之外的一些项目:

Spring Boot 可以使得我们更简便和快速的构建Spring应用

Spring Cloud Spring微服务

Spring Data 方便在Spring中使用任何数据库

Spring Security 安全框架

Spring家族项目网址:https://spring.io/projects

3. Helloworld

3.1 现有Java程序容易产生的诟病

在使用Spring之前,让我们先来看一份简单的Java程序清单:

//用来封装针孔打印机类型
public class PinPrinter {
	
	public void print() {
		System.out.println("使用针孔打印机进行打印");
	}

}
//用来封装激光打印机类型
public class LaserPrinter {
	
	public void print() {
		System.out.println("使用激光打印机进行打印");
	}

}
//用来封装打印店,提供打印服务
public class PrintHouse {

	private PinPrinter printer;// 1-依赖

	public void service() {
		printer = new PinPrinter();// 2-硬编码造成高耦合
		printer.print();
	}

}

如上代码所述,任何一个有意义的应用,都会由两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作的对象的引用(即它所依赖的对象),这将会导致高度耦合和难以测试的代码。

从以上代码,我们来理解一下耦合(代码关联)的两面性:

  • 一方面,一定程度的耦合是必须的,完全没有耦合的代码什么都做不了。在代码1处,PrintHouse如果不借助PinPrinter类,就无法提供打印功能。
  • 另一方面,紧密耦合的代码难以测试、难以复用。为何这么说呢?在代码2处,由于自行创建了PinPrinter实例,导致PrintHouse和PinPrinter紧密的耦合在了一起,这虽然满足了用户的需求,但在一定程度上也限制了PrintHouse提供更为丰富灵活的功能,比如,万一用户要使用激光打印的话,那么现在PrintHouse如果不修改代码是满足不了需求的。

3.2 使用Spring简化

首先,创建maven项目,并引入Spring类库。

pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.19.RELEASE</version>
</dependency>

打开pom的依赖树状结构图我们可以看到如下关系:

在这里插入图片描述

spring-context这个模块,依赖了spring-aop,spring-beans,spring-core,spring-expression这样一些模块,借助maven的类库依赖管理,这些jar都会一并导入到项目中来,而其中前三个模块,就包含和提供了我们在开篇所提到的spring所具备的两项核心技术:依赖注入DI和面向切面编程AOP。在普通的非web应用中,我们一般只需要引入spring-context这个模块就可以满足基本需求了。

然后,根据之前的功能描述,我们可以从针孔打印机和激光打印机中,把print这个方法单独抽取出来封装为一个接口(抽象类也可以)

public interface Printer {

	void print();

}

public class LaserPrinter implements Printer {

	public void print() {
		System.out.println("使用激光打印机进行打印");
	}

}

public class PinPrinter implements Printer {

	public void print() {
		System.out.println("使用针孔打印机进行打印");
	}

}

public class PrintHouse {

	private Printer printer;

    //声明setter,放遍spring进行属性注入
	public void setPrinter(Printer printer) {
		this.printer = printer;
	}

	public void service() {
		printer.print();
	}
}

最后,以xml配置的方式来声明和装配bean

<?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-->
	<bean id="house" class="com.turing.context.PrintHouse">
        <!--属性注入-->
		<property name="printer" ref="pinPrinter"></property>
	</bean>
    
	<bean id="pinPrinter" class="com.turing.context.PinPrinter"></bean>
	<bean id="laserPrinter" class="com.turing.context.LaserPrinter"></bean>
</beans>

测试代码:

public static void main(String[] args) {
    // 创建spring容器
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("com/turing/context/spring.xml");
    // 根据id从spring中获取对应的bean的实例
    PrintHouse house = (PrintHouse) ctx.getBean("house");
    house.service();
    // 关闭容器
    ctx.close();
}

从上述代码中,我们一起来看看这个一个spring应用中到底发生了什么:

  1. 通过读取配置文件信息,我们首先创建了spring容器,容器是spring框架一个非常重要的组件,它不光创建各个不同的bean(通过bean节点声明)还会创建相互协作的bean之间的关联(通过property节点进行属性的装配)
  2. 我们可以从容器中根据bean的id来获取bean的实例,调用bean的方法
  3. 最后使用完毕后,通过close方法进行容器销毁。

在基于spring的应用中,对象都是生存于容器(container)中,spring容器负责创建对象、装配它们,配置它们并管理它们的整个生命周期,从生存到死亡(从newfinalize())。

上述配置中,我们只需要稍稍修改配置文件,就可以让PrintHouse使用激光打印服务了:

<bean id="house" class="com.turing.context.PrintHouse">
    <property name="printer" ref="laserPrinter"></property>
</bean>

而使用spring的最大好处在于,虽然切换了打印机的实现,但是对于java代码而言是无需修改的。

4. 装配Bean

spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,但是,我们还是需要告诉spring要创建哪些bean并且如何将其装配在一起。spring具有非常大的灵活性,它提供了三种主要的装配机制:

  • 基于Xml的显式配置
  • 基于Java的显式配置
  • 自动化配置

4.1 基于Xml的显式配置

在上述Helloworld中,我们就是使用了xml配置文件进行装配,具体使用了属性的setter方法,接下来我们再补充另一种构造器注入的方式

<bean id="house" class="com.turing.wire1.PrintHouse">
    <constructor-arg index="0" ref="pinPrinter"></constructor-arg>
</bean>
public class PrintHouse {

	private Printer printer;

	public PrintHouse(Printer printer) {
		this.printer = printer;
	}

	public void service() {
		printer.print();
	}
}

4.2 基于Java的显式配置

在使用Java代码进行配置的时候,实际上是使用一个Java类来代替原先的spring.xml

@Configuration//代表这是一个spring配置类
public class BeanConfig {

	@Bean
    // 默认bean的id就是方法名
	// 可以通过name进行修改
	public PinPrinter pinPrinter() {
		return new PinPrinter();
	}

	@Bean
	public LaserPrinter laserPrinter() {
		return new LaserPrinter();
	}

	@Bean
	public PrintHouse printHouse(LaserPrinter printer) {
		return new PrintHouse(printer);
	}

	@Bean
	public PrintHouse printHouseOther(PinPrinter printer) {
		return new PrintHouse(printer);
	}

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class);
		PrintHouse house = (PrintHouse) ctx.getBean("printHouse");
		house.service();
		ctx.close();
	}

}

4.3 自动化配置

使用@Component、@ComponentScan、@Autowired进行自动装配:

@Component
public class LaserPrinter implements Printer {

	public void print() {
		System.out.println("使用激光打印机进行打印");
	}

}
@Component
public class PinPrinter implements Printer {

	public void print() {
		System.out.println("使用针孔打印机进行打印");
	}

}
@Component
public class PrintHouse {

	@Autowired//自动装配
	@Qualifier("laserPrinter")//当有两个以上的bean满足时,通过该注解进行区分
	private Printer printer;

	public void service() {
		printer.print();
	}
}

@Configuration
@ComponentScan
public class Test {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class);
		PrintHouse house = (PrintHouse) ctx.getBean("printHouse");
		house.service();
		ctx.close();
	}

}
  • @Component用来声明哪些bean需要由spring容器进行管理
  • @ComponentScan用来对声明了@Component的类启用组件扫描,spring将会扫描这个包以及这个包下的所有子包
  • @Autowired实现自动装配

onfigApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class);
PrintHouse house = (PrintHouse) ctx.getBean(“printHouse”);
house.service();
ctx.close();
}

}


- @Component用来声明哪些bean需要由spring容器进行管理
- @ComponentScan用来对声明了@Component的类启用组件扫描,spring将会扫描==这个包以及这个包下的所有子包==
- @Autowired实现自动装配

在上述用例中,我们看到了Spring中装配Bean的三种主要方式:自动化配置、基于Java的显式配置以及基于Xml的显式配置。不管采用什么方式,这些技术都描述了Spring应用中的组件以及这些组件之间的关系。同时,建议尽可能使用自动化配置,以避免显式配置所带来的维护成本。从选择配置方式的优先级来看,建议按照以下顺序:==自动化配置-->基于Java的显式配置-->基于Xml的显式配置==。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值