2.1 BeanFactory接口

Spring 的核心功能就是实现对 Bean 的管理,比如 Bean 的注册、注入、依赖等。而Spring容器提供了依赖注入这个特征,以实现 Spring 容器对 Bean的管理,而且使用 IoC实现了对Bean的配置与实际应用代码的隔离。其中,Core Container模块的核心概念就是BeanFactory,它是所有Spring应用的核心。因为Spring的核心模型就是Bean模型,所以需要在管理Spring Bean的基础上保证Spring应用的运行。

BeanFactory接口是Bean容器设计中基本的职责定义接口,定义了按照名称、参数、类型等几个维度获取、判断Bean实例的职能。

HierarchicalBeanFactory只是对BeanFactory进行了扩展,定义了父容器(Parent BeanFactory)及判断当前Bean的名称是否在当前Bean工厂中等。

ConfigurableBeanFactory 提供了设置父容器接口、指定类加载器的职能,并且为当前容器工厂设计Bean的定制型的解析处理器、类型处理器等,主要目的是实现对BeanFactory的可配置性。

AutowireCapableBeanFactory提供了 Bean的创建、注入职能,并且提供了对 Bean初始化前后的扩展性处理职能,主要职责是处理在当前工厂中注册的 Bean 实例并使其达到可用状态。

ListableBeanFactory实现了对Bean实例的枚举,以及对有某些共同特征的 Bean的管理,并且按照Bean名称、Bean实例、Bean类型获取Bean实例。

ConfigurableListableBeanFactory 除了集成了 ListBeanFactoryAutowireCapableBeanFactoryConfigurableBeanFactory 这些接口的所有职能,还扩展了修改 Bean 定义信息和分析Bean的功能,并且实现了预实例化单例Bean及冻结当前工厂配置等功能。

AbstractBeanFactory实现了对基本容器功能定义的模板式封装和实现,同时实现了对Bean信息的注册,但是对 Bean 的创建及 Bean 定义描述信息相关的处理使用了抽象化处理的方式并交由继承者实现。

AbstractAutowireCapableBeanFactory 主要解决 Bean 之间的依赖和注入问题,其中实现了Bean的创建方法。因为Bean并不是孤立存在的,很有可能存在 Bean的相互依赖关系,所以只有在解决 Bean 的依赖的前提下,才能实现 Bean 实例的创建。这也就是AbstractBeanFactory不能直接创建Bean方法的原因。

DefaultListableBeanFactory 提供了对 Bean 容器的完全成熟的默认实现,可以直接对外使用。

在 Spring Context 模块中,许多依赖 BeanFactory 的场景都通过 DefaultListableBeanFactory类来实现对Bean的管理、注入、依赖解决、创建、销毁等功能。

XmlBeanFactory 继承 DefaultListableBeanFactory 并且内部持有 XmlBeanDefinition Reader属性(该属性用来实现对 XML 文件定义的 Bean 描述信息的加载、解析和处理)的Bean工厂容器。

2.2 装配Bean

在Spring 中允许我们通过XML 或者Java 配置文件装配Bean .

Spring容器不单单只有一个,可以归为两种类型

  • Bean工厂,BeanFactory【功能简单】
  • 应用上下文,ApplicationContext【功能强大,一般我们使用这个】

2.2.1通过Resource获取BeanFactory

步骤

  • 加载Spring配置文件
  • 通过XmlBeanFactory+配置文件来创建IOC容器
//加载Spring的资源文件
Resource resource = new ClassPathResource("applicationContext.xml");

//创建IOC容器对象【IOC容器=工厂类+applicationContext.xml】
BeanFactory beanFactory = new XmlBeanFactory(resource);


2.2.2类路径下XML获取ApplicationContext

直接通过ClassPathXmlApplicationContext对象来获取

// 得到IOC容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

System.out.println(ac);


在Spring中总体来看可以通过四种方式来配置对象:

  • 使用XML文件配置
  • 使用注解来配置
  • 使用JavaConfig来配置
  • groovy脚本 DSL

2.3 XML配置方式

在上面我们已经可以得到IOC容器对象了。接下来就是在applicationContext.xml文件中配置信息【让IOC容器根据applicationContext.xml文件来创建对象】

首先我们先有个JavaBean的类:

public class User {

private String id;
private String username;


public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}
}

以前我们是通过new User的方法创建对象的....

User user = new User();

现在我们有了IOC容器,可以让IOC容器帮我们创建对象了。在applicationContext.xml文件中配置对应的信息就行了

<!--使用bean节点来创建对象id属性标识着对象name属性代表着要创建对象的类全名-->
<bean id="user" class="User"/>

通过IOC容器对象获取对象:在外界通过IOC容器对象得到User对象

// 得到IOC容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

User user = (User) ac.getBean("user");

System.out.println(user);

上面我们使用的是IOC通过无参构造函数来创建对象,我们来回顾一下一般有几种创建对象的方式:

  • 无参构造函数创建对象
  • 带参数的构造函数创建对象
  • 工厂创建对象
    • 静态方法创建对象
    • 非静态方法创建对象

使用无参的构造函数创建对象我们已经会了,接下来我们看看使用剩下的IOC容器是怎么创建对象的。

2.3.1 带参数的构造函数创建对象

首先,JavaBean就要提供带参数的构造函数:

public User(String id, String username) {
this.id = id;
this.username = username;
}

接下来,关键是怎么配置applicationContext.xml文件了。

<bean id="user" class="User">

<!--通过constructor这个节点来指定构造函数的参数类型、名称、第几个-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg>
</bean>

在constructor上如果构造函数的值是一个对象,而不是一个普通类型的值,我们就需要用到ref属性了,而不是value属性

比如说:我在User对象上维护了Person对象的值,想要在构造函数中初始化它。因此,就需要用到ref属性了

<bean id="person" class="Person"></bean> 

<bean id="user" class="User" >

<!--通过constructor这个节点来指定构造函数的参数类型、名称、第几个-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
</bean>

2.3.2 工厂静态方法创建对象

首先,使用一个工厂的静态方法返回一个对象

public class Factory {

public static User getBean() {

return new User();
}

}

配置文件中使用工厂的静态方法返回对象

<!--工厂静态方法创建对象,直接使用class指向静态类,指定静态方法就行了-->
<bean id="user" class="Factory" factory-method="getBean" >

</bean>

2.3.3 工厂非静态方法创建对象

首先,也是通过工厂的非非静态方法来得到一个对象

public class Factory {


public User getBean() {

return new User();
}


}

配置文件中使用工厂的非静态方法返回对象

<!--首先创建工厂对象-->
<bean id="factory" class="Factory"/>

<!--指定工厂对象和工厂方法-->
<bean id="user" class="User" factory-bean="factory" factory-method="getBean"/>

2.3.4 c名称空间

我们在使用XML配置创建Bean的时候,如果该Bean有构造器,那么我们使用<constructor-arg>这个节点来对构造器的参数进行赋值...

<constructor-arg>未免有点太长了,为了简化配置,Spring来提供了c名称空间...

要想c名称空间是需要导入xmlns:c="http://www.springframework.org/schema/c"

<bean id="userService" class="bb.UserService" c:userDao-ref="">

</bean>

c名称空间有个缺点:不能装配集合,当我们要装配集合的时候还是需要<constructor-arg>这个节点

2.3.5 装载集合

如果对象上的属性或者构造函数拥有集合的时候,而我们又需要为集合赋值,那么怎么办?

在构造函数上,普通类型

<bean id="userService" class="bb.UserService" >
<constructor-arg >
<list>
//普通类型
<value></value>
</list>
</constructor-arg>
</bean>

在属性上,引用类型

<property name="userDao">

<list>
<ref></ref>
</list>
</property>

2.4 注解方式

自从jdk5有了注解这个新特性,我们可以看到Struts2框架、Hibernate框架都支持使用注解来配置信息...

通过注解来配置信息就是为了简化IOC容器的配置,注解可以把对象添加到IOC容器中、处理对象依赖关系,我们来看看怎么用吧:

使用注解步骤:

  • 1)先引入context名称空间
    • xmlns:context="http://www.springframework.org/schema/context"
  • 2)开启注解扫描器
    • <context:component-scan base-package=""></context:component-scan>
    • 第二种方法:也可以通过自定义扫描类以@CompoentScan修饰来扫描IOC容器的bean对象。如下代码:
//表明该类是配置类
@Configuration

//启动扫描器,扫描bb包下的
//也可以指定多个基础包
//也可以指定类型
@ComponentScan("bb")
public class AnnotationScan {

}

在使用@ComponentScan()这个注解的时候,在测试类上需要@ContextConfiguration这个注解来加载配置类...

  • @ContextConfiguration这个注解又在Spring的test包下..

创建对象以及处理对象依赖关系,相关的注解:

  • @ComponentScan扫描器
  • @Configuration表明该类是配置类
  • @Component 指定把一个对象加入IOC容器--->@Name也可以实现相同的效果【一般少用】
  • @Repository 作用同@Component; 在持久层使用
  • @Service 作用同@Component; 在业务逻辑层使用
  • @Controller 作用同@Component; 在控制层使用
  • @Resource 依赖关系
    • 如果@Resource不指定值,那么就根据类型来找,相同的类型在IOC容器中不能有两个
    • 如果@Resource指定了值,那么就根据名字来找

测试代码:UserDao

package aa;

import org.springframework.stereotype.Repository;



//把对象添加到容器中,首字母会小写
@Repository
public class UserDao {

public void save() {
System.out.println("DB:保存用户");
}


}

userService

package aa;


import org.springframework.stereotype.Service;

import javax.annotation.Resource;


//把UserService对象添加到IOC容器中,首字母会小写
@Service
public class UserService {

//如果@Resource不指定值,那么就根据类型来找--->UserDao....当然了,IOC容器不能有两个UserDao类型的对象
//@Resource

//如果指定了值,那么Spring就在IOC容器找有没有id为userDao的对象。
@Resource(name = "userDao")
private UserDao userDao;

public void save() {
userDao.save();
}
}

userAction

package aa;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;



//把对象添加到IOC容器中,首字母会小写
@Controller
public class UserAction {

@Resource(name = "userService")
private UserService userService;

public String execute() {
userService.save();
return null;
}
}

测试

package aa;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {

// 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("aa/applicationContext.xml");

UserAction userAction = (UserAction) ac.getBean("userAction");

userAction.execute();
}
}

2.5 通过JavaConfig方式

怎么通过java代码来配置Bean呢?

  • 编写一个java类,使用@Configuration修饰该类
  • 被@Configuration修饰的类就是配置类

编写配置类:

@org.springframework.context.annotation.Configuration
public class Configuration {

}

使用配置类创建bean:

  • 使用@Bean来修饰方法,该方法返回一个对象。
  • 不管方法体内的对象是怎么创建的,Spring可以获取得到对象就行了。
  • Spring内部会将该对象加入到Spring容器中
  • 容器中bean的ID默认为方法名
@org.springframework.context.annotation.Configuration
public class Configuration {

@Bean
public UserDao userDao() {

UserDao userDao = new UserDao();
System.out.println("我是在configuration中的"+userDao);
return userDao;
}

}
  • 测试代码:要使用@ContextConfiguration加载配置类的信息【引入test包】
package bb;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;


@ContextConfiguration(classes = Configuration.class)
public class Test2 {

@Test
public void test33() {

ApplicationContext ac =
new ClassPathXmlApplicationContext("bb/bean.xml");

UserDao userDao = (UserDao) ac.getBean("userDao");

System.out.println(userDao);
}
}

2.6 三种方式混合使用?

注解和XML配置是可以混合使用的,JavaConfig和XML也是可以混合使用的...

如果JavaConfig的配置类是分散的,我们一般再创建一个更高级的配置类(root),然后使用@Import来将配置类进行组合 如果XML的配置文件是分散的,我们也是创建一个更高级的配置文件(root),然后使用<import>来将配置文件组合

在JavaConfig引用XML

  • 使用@ImportResource()

在XML引用JavaConfig

  • 使用<bean>节点就行了