Spring Framework精讲之二:Spring Bean

什么是Spring Bean?

接着上面一讲,我们讲到ApplicaitonContext,大家可以把ApplicationContext想象为一个factory,我们可以看到在它里面配置的方法getBean可以获得Datasource和UserDAO的实例,这些从ApplicationContext中“产生”出的实例(instances)就被称为“Spring Bean”。

什么是Spring Bean的scopes?

Spring Scopes的实际是指ApplicationContext能产生多少个比如一个DAOs的实例。

我们看一下官方文档,实际有这么多种类的scopes:

ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototypeScopes a single bean definition to any number of object instances.
requestScopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

基本大致可以分为三类:singleton, prototype和其它。以我们的例子举例说明:

Singleton就是所有的DAOs共享一个Datasource;prototype就是指所有的DAOs有自己的Datasource;其它情况略微复杂,是指每个HttpRequest,还是每个HttpSession,还是每个websocket有自己的Datasource。一般情况我们用到的更多的事singleton,其它情景大家需要牢记,并根据自己的实际情况来使用不同的scope。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyApplicationContextConfiguration {

    @Bean
    @Scope("singleton")
// @Scope("prototype") etc.public DataSource dataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("s3cr3t");
        dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
        return dataSource;
    }
}

如上@Scope注解来定义一个bean的scope。

  • Scope('singleton') → 你的 bean 将是一个单例,只有一个实例。
  • Scope('prototype') → 每当有人需要引用你的 bean 时,Spring 就会创建一个新的。
  • Scope('session') → 将为每个用户 HTTP 会话创建一个 Bean。

什么是Spring的Java配置方式?

到目前为止,我们借助 @Bean 注解的 方式在Java 的代码里,也就是在 ApplicationContext中显式地配置了 Bean,传统上这些配置是在XML文件中完成的,所以实际上我们有两种 bean的配置方式一种是XML,一种是纯Java。这两种实际是都是有价值的,即便新代码常常用Java方式。这是因为我们依然有大量的XML配置方式在运行当中,而程序员的重要工作之一是维护已有代码,另外XML的方式在辅助我们理解整个架构方面有很重要的意义。

注解@ComponentScan的功能

对比如下两段程序,后一段我们加了@ComponentScan注解,删除了方法中的标注注解@Bean 的UserDao方法。大家仔细对比一下这两段程序。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyApplicationContextConfiguration {

    @Bean
    public DataSource dataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("s3cr3t");
        dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
        return dataSource;
    }

    @Bean
    public UserDao userDao() { //(1)return new UserDao(dataSource());
    }

}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyApplicationContextConfiguration {

    @Bean
    public DataSource dataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("s3cr3t");
        dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
        return dataSource;
    }

    @Bean
    public UserDao userDao() { //(1)return new UserDao(dataSource());
    }

}

注解@ComponentScan告诉Spring,在启动的时候扫描当前(如果没有带路径参数)目录及所属子目录,把标记为@Component注解的所有类生成Spring bean。

结合注解@Component,我们看看@Autowired的功能

如下面的这段程序:

import javax.sql.DataSource;
import org.springframework.stereotype.Component;

@Component
public class UserDao {


    private DataSource dataSource;

    public UserDao(DataSource dataSource) { //(1)this.dataSource = dataSource;
    }
}

这个类它就在当前目录或其子目录或指定目录中(ComponentScan可以用参数指定目录),被扫描到后就会生成UserDao Spring Bean。这里我们必须强调的是,大家进一步学习构成当中遇到的@Controller,@Service @Repository等注解,其实都是Component的一种具体形式。

那么还有一个问题,就是当我们引用一个同名类时,Spring怎么知道是普通类还是一个Spring Bean的实例?这就是@Autowired注解要解决的问题,如下面这段代码:

import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;

@Component
public class UserDao {

    private DataSource dataSource;

    public UserDao(@Autowired DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

我们简单在补充解释一下上面这段程序:

  • UserDAO 使用 @Component 注解 → Spring 将创建它
  • UserDAO 有一个 @Autowired 构造函数参数 → Spring 将自动注入通过 @Bean 方法配置的 DataSource
  • 如果您的任何 Spring 配置中都没有配置 DataSource,您将在运行时收到 NoSuchBeanDefinition 异常。

上面这段程序我们还有强调的是:新版的Spring中,这种通过构造函数注入一个bean的方式以及可以把此处的@Autowired加以省略。

什么是Field和Setter Injection?

处理上面这段我们举例的叫constrcutor injection外,我们还有两类的injection方式,分别叫field和setter的注入,代码说明了一切,如下:

import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;

@Component
public class UserDao {

    @Autowired
    private DataSource dataSource;

}
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;

@Component
public class UserDao {

    private DataSource dataSource;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

}

对于上面三段代码,这三种注入方式:constructor,field,setter起的作用是完全一样的。

关于构造函数注入和字段注入的说明

关于构造函数注入和字段注入哪个更好,网上有很多争论,甚至有一些强烈的声音声称字段注入是有害的。

近年来,我在各种项目中使用过这两种风格:构造函数注入和字段注入。仅根据个人经验,我并不真正偏爱其中一种风格。

一致性是王道:不要对 80% 的 Bean 使用构造函数注入,不要对 10% 使用字段注入,对其余 10% 使用方法注入。

Spring官方文档中的方法似乎是明智的:使用构造函数注入来实现强制依赖,使用setter/字段注入来实现可选依赖。

关于Srping IoC’ container的小结

到目前为止,您应该了解有关 Spring 依赖容器的几乎所有内容。

当然还有更多内容,但是如果您很好地掌握了 ApplicationContext、Bean、依赖项和依赖项注入的不同方法,那么您已经走上了一条好的道路。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值