Spring基础特性总结一--核心组件Bean的使用

来源:互联网 发布:三星的电脑怎么样知乎 编辑:程序博客网 时间:2024/04/28 13:51

文章内容

0.Spring整体特性
1.Bean组件的介绍
2.Bean的定义、创建、解析
3.Bean的使用

0.Spring整体特性

这里写图片描述

Spring的整体架构可划分为两部分:核心组件和特性功能;
其中核心组件功能包括Core、Bean、Context,Core在Spring框架中用于提供基础操作的工具组件;Bean则用于封装Object来进行一系列操作和管理;Context是一个Bean关系的集合,也叫Ioc容器;

特性功能包括AOP、JDBC、Transaction等,主要是基于Spring三大核心组件之上实现的机制;

1.Bean组件的介绍

Bean作为Spring框架中最重要的组件,正是由于Bean的定义和实现才使得Spring可以实现Bean的自动创建和注入,简化了日常开发过程中对依赖关系的维护,都交由Spring来完成;

Bean是对Object的封装,使得Object的创建、销毁、具体操作都可以通过统一的接口来完成;

2.Bean的定义、创建、解析

2.1.Bean的定义

这里写图片描述

定义接口:org.springframework.beans.factory.config.BeanDefinition

这里写图片描述

从接口包含的方法可以看出,Bean封装了Object的类名、依赖、构造函数以及相关的配置信息(模式和作用域等);

2.2.Bean的创建

Spring Bean 的创建时典型的工厂模式,顶级接口是BeanFactory;
非顶级的BeanFactory,如ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory;
这里写图片描述

需要说明的是,这些非顶级的接口定义了新生成的Bean的不同方便的特性;例如,如果这个Bean是由实现了ListableBeanFactory接口的BeanFactory生成的,那么他是可列表的,如果这个BeanFactory还实现了AutowireCapableBeanFactory,那么这个Bean将有自己的装配规则;而在默认情况下,BeanFactory的最终实现类为DefaultListableBeanFactory,而它实现了所有接口,所以通过默认的BeanFactory产生的Bean具有Bean的几乎所有特性

默认BeanFactory实现:org.springframework.beans.factory.support.DefaultListableBeanFactory

这里写图片描述

分析其中通过类类型获取Bean的方法:
(Assert是一个可以借鉴的设计,文章最后介绍)

public <T> T getBean(Class<T> requiredType) throws BeansException {    //字段验证    Assert.notNull(requiredType, "Required type must not be null");    //获取Bean的名称    String[] beanNames = getBeanNamesForType(requiredType);    if (beanNames.length > 1) {        ArrayList<String> autowireCandidates = new ArrayList<String>();        for (String beanName : beanNames) {            if (getBeanDefinition(beanName).isAutowireCandidate()) {                autowireCandidates.add(beanName);            }        }        if (autowireCandidates.size() > 0) {            beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);        }    }    if (beanNames.length == 1) {        return getBean(beanNames[0], requiredType);    }    else if (beanNames.length == 0 && getParentBeanFactory() != null) {        return getParentBeanFactory().getBean(requiredType);    }    else {        throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " +                beanNames.length + ": " + StringUtils.arrayToCommaDelimitedString(beanNames));    }}//逐层向上分析//AbstractBeanFactory::---->getBean()--->doGetBean()--->getSingleton()--->默认Singleton模式,通过返回的实例是否为null判断是否获取Prototype实例//DefaultSingletonBeanRegistry::--->getSingleton()//典型的单例模式protected Object getSingleton(String beanName, boolean allowEarlyReference) {    Object singletonObject = this.singletonObjects.get(beanName);    if (singletonObject == null) {        synchronized (this.singletonObjects) {            singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {                ObjectFactory singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {                    singletonObject = singletonFactory.getObject();                    this.earlySingletonObjects.put(beanName, singletonObject);                    this.singletonFactories.remove(beanName);                }            }        }    }    return (singletonObject != NULL_OBJECT ? singletonObject : null);}

2.3.Bean的解析

上面已经讲过Bean的定义和创建,BeanFactory通过BeanDefinition生成对应的Bean实例;

这里写图片描述

上图,给出了Bean创建的过程,Bean的解析主要完成对XML中的Bean相关节点的识别和转换为对应的BeanDefinition对象,这块的工作是很清晰的,只是设计到XML解析的相关操作,会比较复杂;整个解析过程主要通过下图中的类来完成:

这里写图片描述

3.Bean的使用

我们在使用Bean时主要关心的如何配置一个Bean,是的Context在初始化时能正确的初始化一个Bean;
首先,IoC容器Bean配置3种方式:
①基于XML文件进行配置(XML文件)
②基于注解进行配置(spring-aop)
③基于java进行配置(利用几个特殊的注解、不常用)

其次,要配置一个完整的Bean,只要关注以下几个方面:依赖注入的方式(初始化)、复杂Bean的配置和Bean的获取(装配);

3.1.依赖注入

依赖注入的方式主要有三种(最常使用的是① ②):
①使用set方法注入
②使用构造器注入
③接口注入

首先创建一个简单的Bean:

public class People {    private String name;    private String sex;    private int    age;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString(){        return "{ name:"+getName()+";sex:"+getSex()+";age:"+getAge()+"; }";    }}

测试代码:

//获取ioc容器ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");//根据id获取到bean对象Object bean = ioc.getBean("people");System.out.println(bean);

3.1.1.使用Set方法注入

通过Set注入是指通过属性的set方法初始化Context中的Bean:

<bean....>    <property name="set方法后面部分名称" value="初始化的值"></property></bean>

具体配置实例:

<!-- applicationContext.xml --><bean id="people" class="com.aaron.spring.bean.People">    <property name="name" value="小明"></property>    <property name="sex" value="man"></property>    <property name="age" value="18"></property></bean>

3.1.2.使用构造器注入

通过构造函数注入和通过setter的方法的主要不同是Bean需要构造函数;

在People中增加构造函数:

public People(){}public People(String name, String sex, int age){    this.name = name;    this.sex  = sex;    this.age  = age;}
<bean id="people2" class="com.aaron.spring.bean.People">    <constructor-arg value="小王" name="name"/>    <constructor-arg value="man" name="sex"/>    <constructor-arg value="18" name="age"/></bean>

需要注意的是:
setter方法调用的是无参构造函数,通过构造函数注入的方式,需要参数和构造函数一一对应,可以通过index/type/name来配置参数之间的对应关系;
其次通过setter和构造函数的方式在调用的时间不同,setter是先创建实例,在调用setter方法,构造函数的方式则是在构造函数被调用时进行初始化;
推荐使用,setter的方式进行注入,通过构造函数的方式在存在复杂依赖关系的时候配置会非常复杂;

3.1.3.接口注入

接口注入的方式使用很少,主要通过

Context.lookup(ServletContext.getXXX)

获取当前COntext中符合条件的接口的实现,并进行实例化;这种方式和Context当前的环境有关,不同的Context获取到的对应实现类可能是不同的;

3.2.复杂Bean的配置

复杂Bean的配置包括:Bean之间关系的配置以及集合类型属性的赋值;

3.2.1.Bean之间关系:继承和依赖

如果存在两个Bean的大部分属性一致,可以通过继承来减少重复配置相同参数:

<bean id="people3"  class="com.aaron.spring.bean.People" parent="people">    <property name="name" value="小红"></property></bean>

依赖关系主要用于一个Bean如果有使用其他的Bean,则必须创建这样的依赖关系,在Context初始化时检查对应ID的Bean是否存在:

<bean id="people3"  class="com.aaron.spring.bean.People" parent="people" depends-on="people4"><property name="name" value="小红"></property></bean>Exception:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'people4' is defined

3.2.2.集合类型属性的赋值

举个例子就行了

private List<Book> list;
<property name="list">    <!-- 给List集合属性赋值 -->    <list>        <!-- 可以是内部bean -->        <bean p:title="内部Bean" class="com.atguigu.beans.Book"></bean>        <!-- 可以是String字符串 -->        <value>String字符串</value>        <!-- 可以是一个引用 -->        <ref bean="book01"/>    </list></property>
private Map<String,Car> map;
<property name="map">    <map>        <entry key="免费书籍" value-ref="book01"></entry>        <entry key="2">            <bean p:title="鹿鼎记(内部bean)" class="com.atguigu.beans.Book"></bean>        </entry>        <entry key="3" value="String字符串"></entry>    </map></property>
private Properties prop;
<property name="properties"> <props>          <prop key="url">jdbc:mysql//localhost:8080/test</prop>          <prop key="root">root</prop>          <prop key="password">123456</prop>      </props>  </property>

3.3.Bean的获取

相面主要讨论如何虫Context中获取Bean,要获取Bean首先需要区分id和name的区别,id有要个的命名规范,且不可重复;name可以使用更多的字符且可以重复,配置文件中的同名Bean后面的会覆盖前面的,name默认为类的全名;所以,一般使用id来标识一个唯一的Bean;

获取Bean的方法主要有两种:显式调用getBean和注解的方式;

3.3.1.通过getBean获取Bean

主要是通过类名,id和name,其中参数name包括id和name;

这里写图片描述

3.3.1.通过注解获取Bean

使用Spring的注解方式配置,需要引入spring-aop包,关键的注解为
@Autowired
* @Qualifier(“Bean ID”)*

@Autowired:通过匹配当前的类型找到对应的Bean,并进行初始化赋值;默认required=true,即如果找不到对应的Bean则报错;

@Qualifier(“Bean ID”):用于指定Bean的ID,用于有多个实现类的情况唯一标识一个Bean,如果不存在冲突则可以不设置;

使用的方式主要有以下两种:

首先添加xml配置:

<context:component-scan base-package="Service包名"></context:component-scan>

新增TestService类,用于获取Bean

@Service("peopleService")public class TestService {    //方式一    @Autowired(required=false)    @Qualifier("people")    private People people1;    private People people2;    public People getPeople1() {        return people1;    }    public void setPeople1(People people1) {        this.people1 = people1;    }    //方式二    @Autowired    public void setPeople2(@Qualifier("people")People people2) {        this.people2 = people2;    }    public People getPeople2(){        return this.people2;    }}

Main方法使用:

TestService service = (TestService) ioc.getBean("peopleService");System.out.println(service.getPeople1());

参考文章:

(Spring 框架的设计理念与设计模式分析–许 令波)[https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle]