Spring学习总结(一)- Spring的核心特性之依赖注入(DI)总结

1、Spring容器

在了解依赖注入(DI)详情之前,需要先了解Spring的容器。

在基于Spring的应用中,应用对象生存于Spring容器(container)中。Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡(在这里,可能就是new到finalize())。

在后面,我们将了解如何配置Spring,从而让它知道该创建、配置和组装哪些对象。但首先,最重要的是了解容纳对象的容器。理解容器将有助于理解对象是如何被管理的。

Spring容器并不是只有一个。Spring自带了多个容器实现,可以归为两种不同的类型。

  • bean工厂: 由org.springframework.beans.factory.eanFactory接口定义,是最简单的容器,提供基本的DI支持。
  • 应用上下文: 由org.springframework.context.ApplicationContext接口定义,基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。

这篇文章主要把精力集中在应用上下文的使用上。

1.1、使用应用上下文

Spring自带了多种类型的应用上下文。下面罗列的几个是你最有可能遇到的。

  • AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
  • AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring
    Web应用上下文。
  • ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
  • FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
  • XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。

例如,如下代码展示了如何加载一个FileSystemXmlApplicationContext:

ApplicationContext context = new FileSystemXmlApplicationContext("D://config.xml");

类似地,你可以使用ClassPathXmlApplicationContext从应用的类路径下加载应用上下文:

ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");

使用FileSystemXmlApplicationContext和使用ClassPathXmlApp-licationContext的区别在于:

  • FileSystemXmlApplicationContext是在指定的文件系统路径下查找knight.xml文件。
  • ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件)下查找 knight.xml文件。

如果想从Java配置中加载应用上下文,那么可以使用AnnotationConfig-ApplicationContext:

AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext(JavaConfig.class);

应用上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取bean。

2、装配Bean

创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。

Spring配置的三种装配机制

  • 隐式的bean发现机制和自动装配。
  • 在Java中进行显式配置。
  • 在XML中进行显式配置。

2.1、自动化装配bean

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

2.1.1、创建可被发现的bean

使用@Component注解。这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean
例如:

@Component
public class Person {}

Person 的具体内容并不重要。需要注意的就是 Person 类上使用了@Component注解。

不过,组件扫描默认是不启用的 。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

2.1.2、@ComponentScan注解启用组件扫描

例如:

@Configuration
@ComponentScan
public class JavaConfig {}

Configuration注解表明这个类是一个配置类

JavaConfig 类使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描。

如果没有其他额外配置的话,@ComponentScan默认会扫描与配置类相同的包。假设 JavaConfig 类位于 com.springLearn 包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。

如果 Person 类也在 com.springLearn包下,这样就能发现 Person ,并且会在Spring中自动为其创建一个bean。

如果想要使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的 < context:component-scan>元素。
例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

        <context:component-scan base-package="com.springLearn"/>
    
</beans>

2.1.3、为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID。Spring会根据类名为其指定一个ID。具体来讲,这个bean所给定的ID是将类名的第一个字母变为小写。 (请记住这句话,非常重要!)

如果想为这个bean设置不同的ID,所要做的就是将期望的ID作为值传递给@Component注解。
例如:

@Component("myPerson")
public class Person {}

2.1.4、设置组件扫描的基础包

到现在为止,我们没有为 @ComponentScan 设置任何属性。这意味着,按照默认规则,它会以配置类所在的包作为基础包(basepackage)来扫描组件。但是,如果想扫描不同的包,那该怎么办呢?或者,如果想扫描多个基础包,那又该怎么办呢?

要满足这样的需求其实也完全没有问题!为了指定不同的基础包,所需要做的就是在@ComponentScan的value属性中指明包的名称:
例如:

@Configuration
@ComponentScan("com.otherPackages")
public class JavaConfig {}

如果你想更加清晰地表明你所设置的是基础包,那么你可以通过basePackages属性进行配置:

@Configuration
@ComponentScan(basePackages="com.otherPackages")
public class JavaConfig {}

注意:basePackages属性使用的是复数形式。如果想要设置多个基础包,只需要将basePackages属性设置为要扫描包的一个数组即可:

@Configuration
@ComponentScan(basePackages={"com.otherPackage1", "com.otherPackage2", ......})
public class JavaConfig {}

除此之外@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:

@Configuration
@ComponentScan(basePackageClasses={"Person.class", "Foots.class", ......})
public class JavaConfig {}

2.1.5、通过为bean添加注解实现自动装配

自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。
例如:

@Component
public class Person {

    @Autowired
    private Foots foots;
	
}

这种写法等同于下面这种写法:

@Component
public class Person {

    private Foots foots;

    @Autowired
    public Person(Foots foots) {
        this.foots = foots;
    }

}

这就是@Autowired注解的构造器注入方式。

@Component
public class Person {

    private Foots foots;

    @Autowired
    public void setFoots(Foots foots) {
        this.foots = foots;
    }
	
}

@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。

不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。
假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。

为了避免异常的出现,你可以将@Autowired的为了避免异常的出现,你可以将@Autowired的required属性设置为false:

@Component
public class Person {

    @Autowired(required = false)
    private Foots foots;
	
}

将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。

但是,把required属性设置为false时,需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。

2.2、通过Java代码装配bean

2.2.1、创建配置类

在上文中,已经用到了JavaConfig类,现在来重温一下。

@Configuration
public class JavaConfig {}

Configuration注解表明这个类是一个配置类

2.2.2 声明简单的bean

要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解

@Bean
public Person person(){
	return new Person();
}

@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。

默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是person。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字

@Bean(name = "myPerson")
public Person person(){
	return new Person();
}

2.2.3、借助JavaConfig实现注入

@Bean
public Person person(Foots foots){
	return new Person(foots);
}

在这里,person()方法请求一个Foots作为参数。当Spring调用person()创建Person bean的时候,它会自动装配一个Foots到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,person()方法也能够将Foots注入到Person的构造器中
前提是Person 类必须有对应的构造函数才行。

2.3、通过XML装配bean

2.3.1、声明一个简单的< bean>

要在基于XML的Spring配置中声明一个bean,我们要使用springbeans模式中的另外一个元素:< bean>。< bean>元素类似于JavaConfig中的@Bean注解。

< bean class="com.springLearn.Person"/>

因为没有明确给定ID,所以这个bean将会根据全限定类名来进行命名。
在本例中,bean的ID将会是“com.springLearn.Person#0”。其中,“#0”是一个计数的形式,用来区分相同类型的其他bean。如果你声明了另外一个Person,并且没有明确进行标识,那么它自动得到的ID将会是“com.springLearn.Person#1”。

因此,通常来讲更好的办法是借助id属性,为每个bean设置一个你自己选择的名字:

< bean id="person" class="com.springLearn.Person"/>

2.3.2、借助构造器注入初始化bean

在Spring XML配置中,只有一种声明bean的方式:使用< bean>元素并指定class属性。Spring会从这里获取必要的信息来创建bean。

构造器注入bean引用使用< constructor-arg>元素,在此之前先多声明一个Foots的Bean:

<bean id="foots" class="com.Foots"/>

<bean id="person" class="com.Person">
		<constructor-arg ref="foots"/>
</bean>

3、总结

Spring框架的核心是Spring容器。容器负责管理应用中组件的生命周期,它会创建这些组件并保证它们的依赖能够得到满足,这样的话,组件才能完成预定的任务。
在本文中,在Spring中装配bean的三种主要方式:自动化配置、基于Java的显式配置以及基于XML的显式配置。不管采用什么方式,这些技术都描述了Spring应用中的组件以及这些组件之间的关系。
我个人比较喜欢使用自动化配置,第一简单,第二是避免显式配置所带来的维护成本。但是,如果你确实需要显式配置Spring的话,应该优先选择基于Java的配置,它比基于XML的配置更加强大、类型安全并且易于重构

该文中只学习了DI的一部分,关于高级装配中的profile、条件化的Bean、Bean的作用域、运行时值的注入和SpEL这些内容会在后面的高级装配文章中涉及。


技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值