您的位置:首页 > 编程语言 > Java开发

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

2017-09-03 12:36 435 查看

文章内容

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]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: