Bean

Bean配置

Bean中可以通过name属性给该Bean添加别名,可通过别名引用Bean,name可以有多个,可用逗号分号或空格分开。

1
2
<!--name别名-->
<bean id="bookDao" name="book book1,book2;book3" class="dao.impl.BookDaoImpl"/>
1
2
3
4
5
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao book = (BookDao) ctx.getBean("book");
BookDao newBook = (BookDao) ctx.getBean("book1");
System.out.println(book);
System.out.println(newBook);

控制台输出为:

1
2
dao.impl.BookDaoImpl@60dcc9fe
dao.impl.BookDaoImpl@60dcc9fe

可见Spring在创建Bean的时候默认使用的是单例创建,若需要非单例创建,则需要在Bean中添加属性scope,scope默认为singleton为单例创建,prototype则为非单例创建

1
<bean id="bookService" class="service.impl.BookServiceImpl" scope="prototype"/>

为什么Spring默认为单例创建?

适合交给容器管理的都应该是只创建一次可以复用的对象,例如表现层对象,业务层对象,数据层对象,工具对象。封装实体的域对象不应交给容器管理。

Bean实例化

构造方法

Spring创建Bean时需要使用反射调用构造方法,且必须是无参构造方法。

无参构造方法如果不存在,将抛出异常BeanCreationException。

静态工厂

若需要使用静态工厂方式获得实现类对象,则bean的配置中需加入factory-method属性。

1
2
3
4
5
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
1
<bean id="orderDao" class="service.OrderDaoFactory" factory-method="getOrderDao"/>

实例工厂与FactoryBean

原始方法

若工厂中使用的不是静态方法

1
2
3
4
5
public class OrderDaoFactory {
public OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}

则bean配置中需要加入factory-bean属性

1
2
<bean id="orderDaoFactory" class="service.OrderDaoFactory"/>
<bean id="orderDao" factory-bean="orderDaoFactory" factory-method="getOrderDao"/>

代替方法

可以通过创建一个FactoryBean对象来替代上述两个步骤。

1
2
3
4
5
6
7
8
9
10
11
public class OrderDaoFactoryBean implements FactoryBean<OrderDao> {
@Override
public OrderDao getObject() throws Exception {
return new OrderDaoImpl();
}

@Override
public Class<?> getObjectType() {
return OrderDao.class;
}
}

配置中只需加入一条即可。

1
<bean id="orderDao" class="service.OrderDaoFactoryBean"/>

如果需要更改bean是否是单例实现,只需在FactoryBean对象中多重写一个方法。(默认就为单例)

1
2
3
4
@Override
public boolean isSingleton() {
return true;
}

Bean的生命周期

可以在实现类中配置方法,并在xml文件中添加属性。

1
<bean id="bookDao" class="dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>

也可以在实现类中实现两个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
@Override
public void save() {
System.out.println("book dao save...");
}

@Override
public void destroy() throws Exception {
System.out.println("destroy...");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init...");
}
}

Bean注入

setter注入

在最开始的DI案例中,若注入的是一个简单类型,也是需要提供一个set方法,而xml配置中则需要的是value属性而非ref属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BookServiceImpl implements BookService {

private BookDao bookDao;
private String databaseName;

@Override
public void save() {
System.out.println("book service save..." + databaseName);
bookDao.save();
}

// 提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}

public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
}
1
2
3
4
5
6
7
<bean id="bookDao" class="dao.impl.BookDaoImpl"/>

<bean id="bookService" class="service.impl.BookServiceImpl">
<!--注意这里-->
<property name="bookDao" ref="bookDao"></property>
<property name="databaseName" value="sql"></property>
</bean>

构造器注入

还可以提供构造方法来注入,xml中需要加入constructor-arg标签,这种方式一样可以注入简单类型,将ref改为value即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BookServiceImpl implements BookService {

private BookDao bookDao;

public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}

@Override
public void save() {
System.out.println("book service save...");
bookDao.save();
}
}
1
2
3
4
5
6
<bean id="bookDao" class="dao.impl.BookDaoImpl"/>

<bean id="bookService" class="service.impl.BookServiceImpl">
<!--这里的name指的是构造方法中形参的名字-->
<constructor-arg name="bookDao" ref="bookDao"></constructor-arg>
</bean>

以上constructor-arg中的name也可以替换成type或index,用来指定构造方法中的形参。

依赖自动装配

如果提供了set方法,Spring可以自动装配注入,只需要添加autowire属性即可,但只能用于引用类型。

1
2
3
<!--id是可以省略的-->
<bean class="dao.impl.BookDaoImpl"/>
<bean id="bookService" class="service.impl.BookServiceImpl" autowire="byType"/>

加载properties文件

开启context命名空间,location属性中加逗号可以加载多个文件,也可以直接使用classpath*:*.properties加载所有。可以在context中加属性system-properties-mode=”NEVER”避免系统变量优先级更高问题。

1
2
3
4
5
6
7
8
9
10
11
<?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">
<!--classpath*代表除了当前工程,也加载所有jar包的文件-->
<context:property-placeholder location="classpath*:*.properties,jdbc.properties" system-properties-mode="NEVER"/>

使用context命名空间加载指定properties文件,使用${}读取加载的属性值。

1
2
3
4
5
6
7
<!--使用属性占位符${}读取propertie中的属性-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>