0%

Spring IOC容器之Bean管理

Spring IOC容器之Bean管理

1 基于xml方式

  • 在Spring配置文件中,在bean标签里面添加对应的属性,就可以实现对象的创建。

1.1 创建对象

  • 配置文件,
1
2
3
4
5
6
7
8
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.company.User"></bean>
</beans>
  • 读取配置文件,工厂方法创建对象,
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}
}

1.2 注入属性

1.2.1 set方法

  • 先要定义属性的set方法,
1
2
3
4
5
6
7
public void setName(String name) {
this.name = name;
}

public void setAddress(String address) {
this.address = address;
}
  • 配置文件中在bean标签下使用property标签设置属性,
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.company.User">
<property name="name" value="Xiaohua"></property>
<property name="address" value="Shanghai"></property>
</bean>
</beans>
  • 简化方式:p名字空间注入(不常用),
1
2
3
4
5
6
7
8
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.company.User" p:name="Xiaohua" p:address="Shanghai"></bean>
</beans>
  • 使用null标签注入空值,
1
2
3
<property name="telephone">
<null></null>
</property>
  • 使用xml CDATA注入特殊字符,
1
2
3
<property name="favoriteBook">
<value><![CDATA[《你好,明天!》]]></value>
</property>

1.2.2 有参构造函数

  • 先要定义类的有参构造函数,
1
2
3
4
public User(String name, String address) {
this.name = name;
this.address = address;
}
  • 配置文件中在bean标签下使用constructor-arg标签初始化对象,
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.company.User">
<constructor-arg name="name" value="Lihua"></constructor-arg>
<constructor-arg name="address" value="Beijing"></constructor-arg>
</bean>
</beans>

1.2.3 外部bean

  • 通常一个类会包含对象属性,
1
2
3
4
5
6
7
8
9
10
11
12
public class UserService {
private UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public void update() {
System.out.println("UserService update......");
userDao.update();
}
}

例如上面的代码,UserService类中就包含了一个UserDao对象

  • 如果需要初始化类内的对象属性,可以使用ref以外部bean的形式配置,
1
2
3
4
<bean id="userDao" class="com.company.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.company.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>

1.2.4 内部bean

  • 也可以在property标签内部初始化对象属性,这种方式被称为内部bean,
1
2
3
4
5
<bean id="userService" class="com.company.service.UserService">
<property name="userDao">
<bean id="userDao" class="com.company.dao.UserDaoImpl"></bean>
</property>
</bean>

1.2.5 级联赋值

1
2
3
4
5
6
<bean id="userService" class="com.company.service.UserService">
<property name="userDao">
<bean id="userDao" class="com.company.dao.UserDaoImpl"></bean>
</property>
<property name="userDao.name" value="Lihua"></property>
</bean>
  • 需要提前定义好get和set方法,否则会报错
  • 先在UserService对象中创建UserDao对象,然后调用get方法获得UserDao对象,最后调用setName方法为name属性赋值

1.2.6 集合属性

  • 提前定义好属性的set方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Student {
private String[] courses;
private List<String> nicknames;
private Map<String, String> contactMethod;
private Set<String> friends;

public void setCourses(String[] courses) {
this.courses = courses;
}

public void setNicknames(List<String> nicknames) {
this.nicknames = nicknames;
}

public void setContactMethod(Map<String, String> contactMethod) {
this.contactMethod = contactMethod;
}

public void setFriends(Set<String> friends) {
this.friends = friends;
}

public void show() {
System.out.println(Arrays.toString(courses));
System.out.println(nicknames);
System.out.println(contactMethod);
System.out.println(friends);
}
}

1.2.6.1 数组

1
2
3
4
5
6
7
<property name="courses">
<array>
<value>Java程序设计</value>
<value>汇编语言</value>
<value>操作系统原理</value>
</array>
</property>

1.2.6.2 List集合

1
2
3
4
5
6
<property name="nicknames">
<list>
<value>Xiaoming</value>
<value>Mingming</value>
</list>
</property>

1.2.6.3 Map集合

1
2
3
4
5
6
<property name="contactMethod">
<map>
<entry key="tele" value="123xxxxxxxx"></entry>
<entry key="qq" value="234xxxxx"></entry>
</map>
</property>

1.2.6.4 Set集合

1
2
3
4
5
6
<property name="friends">
<set>
<value>Lihua</value>
<value>Xiaohong</value>
</set>
</property>

1.2.6.5 在集合里面设置对象类型值

  • 有时集合属性中存放的是对象,
1
2
3
4
5
6
7
8
9
10
11
public class BookShelf {
private List<Book> books;

public void setBooks(List<Book> books) {
this.books = books;
}

public void show() {
System.out.println(books);
}
}
  • 使用ref标签初始化集合属性内部存放的对象,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="book1" class="com.company.Book">
<property name="name" value="面向对象程序设计"></property>
</bean>
<bean id="book2" class="com.company.Book">
<property name="name" value="数据库原理"></property>
</bean>

<bean id="bookShelf" class="com.company.BookShelf">
<property name="books">
<list>
<ref bean="book1"></ref>
<ref bean="book2"></ref>
</list>
</property>
</bean>

1.2.6.6 把集合注入部分提取出来

  • 修改xml文件头部的配置,
1
2
3
4
5
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
  • 使用util标签将集合注入部分提取出来,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="book1" class="com.company.Book">
<property name="name" value="面向对象程序设计"></property>
</bean>
<bean id="book2" class="com.company.Book">
<property name="name" value="数据库原理"></property>
</bean>
<bean id="book3" class="com.company.Book">
<property name="name" value="计算机网络"></property>
</bean>

<util:list id="books">
<ref bean="book1"></ref>
<ref bean="book2"></ref>
<ref bean="book3"></ref>
</util:list>
  • 引用被抽取出的集合,
1
2
3
<bean id="bookShelf" class="com.company.BookShelf">
<property name="books" ref="books"></property>
</bean>

1.3 工厂bean

  • 对于工厂bean,配置文件中定义的bean类型可以和返回类型不一样
  • 第一步:创建类,让这个类实现接口FactoryBean,作为工厂bean
  • 第二步:实现接口声明的方法,在实现的方法中定义返回的对象类型,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class FacBean implements FactoryBean<Book> {
@Override
public Book getObject() throws Exception {
Book book = new Book();
book.setName("C++ Primer");
return book;
}

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

@Override
public boolean isSingleton() {
return false;
}
}
  • 配置文件,
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.company.factorybean.FacBean"></bean>
</beans>

bean标签定义的类型为FacBean,但是实际getBean方法返回的对象类型是Book

1.4 bean的作用域

  • 在Spring里面,默认情况下,bean是单实例对象
  • bean标签scope属性的取值,
是否单例 对象创建的时机
singleton Spring加载配置文件时
prototype 调用getBean方法时

1.5 bean的生命周期

1.5.1 过程

  • 通过构造器创建bean实例
  • 调用set方法为bean的属性设置值和对其他bean引用
  • 将bean对象传递给bean后置处理器的postProcessBeforeInitialization方法
  • 调用bean的初始化方法(需要配置初始化方法)
  • 将bean对象传递给bean后置处理器的postProcessAfterInitialization方法
  • bean可以被使用(对象获取到了)
  • 当容器关闭时,调用bean的销毁方法(需要配置销毁的方法)

1.5.2 配置bean的初始化方法

  • 为类定义一个方法,作为bean的初始化方法,
1
2
3
public void initMethod() {
System.out.println("step 4: call init method");
}
  • 在bean标签中使用init-method属性配置bean的初始化方法,
1
2
3
<bean id="order" class="com.company.cycle.Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="laptop"></property>
</bean>

1.5.3 配置bean的销毁方法

  • 为类定义一个方法,作为bean的销毁方法,
1
2
3
public void destroyMethod() {
System.out.println("step 7: call destroy method");
}
  • 在bean标签中使用destroy-method属性配置bean的销毁方法,
1
2
3
<bean id="order" class="com.company.cycle.Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="laptop"></property>
</bean>
  • destroyMethod在容器关闭的时候被调用,
1
((ClassPathXmlApplicationContext)context).close();

1.6 xml自动装配

  • 假设存在Employee类和Department类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Employee {
private String name;
private Department dept;

public void setName(String name) {
this.name = name;
}

public void setDept(Department dept) {
this.dept = dept;
}

@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", dept=" + dept +
'}';
}
}

public class Department {
private String name;

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Department{" +
"name='" + name + '\'' +
'}';
}
}

1.6.1 按名字

  • 在bean标签中将autowire属性配置为byName,
1
2
3
4
5
6
<bean id="employee" class="com.company.Employee" autowire="byName">
<property name="name" value="Lihua"></property>
</bean>
<bean id="dept" class="com.company.Department">
<property name="name" value="Tech"></property>
</bean>

1.6.2 按类型

  • bean标签中将autowire属性配置为byType,
1
2
3
4
5
6
<bean id="employee" class="com.company.Employee" autowire="byType">
<property name="name" value="Lihua"></property>
</bean>
<bean id="dept" class="com.company.Department">
<property name="name" value="Tech"></property>
</bean>

1.7 外部属性文件

  • 使用“user.properties”配置文件初始化User对象,
1
2
3
4
user.name="Lihua"
user.address="Beijing"
user.telephone="123xxxxxxxx"
user.favoriteBook="Hello, world!"
  • 在xml配置文件头部添加context名字空间,
1
2
3
4
5
6
<?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">
  • 引用外部配置文件注入属性,
1
2
3
4
5
6
7
8
<context:property-placeholder location="classpath:user.properties"></context:property-placeholder>

<bean id="user" class="com.company.User">
<property name="name" value="${user.name}"></property>
<property name="address" value="${user.address}"></property>
<property name="telephone" value="${user.telephone}"></property>
<property name="favoriteBook" value="${user.favoriteBook}"></property>
</bean>

使用“$”符号引用外部配置文件的内容,必须保证大括号内和配置文件中的key一致

2 基于注解方式

2.1 Spring创建对象的注解

  • @Component,通用注解
  • @Service,通常用在Service层
  • @Controller,通常用在Web层
  • @Repository,通常用在DAO层

以上四个注解功能是一样的,都可以用来创建bean实例

2.2 创建对象

2.2.1 开启组件扫描

  • 可以使用逗号隔开多个包,
1
<context:component-scan base-package="com.company.dao, com.company.service"></context:component-scan>
  • 也可以将上层的包作为参数(底层的包都会被扫描),
1
<context:component-scan base-package="com.company"></context:component-scan>

2.2.2 添加注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Repository(value = "userDao")
public class UserDao {
@Override
public String toString() {
return "UserDao{}";
}
}

@Service(value = "userService")
public class UserService {
UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
  • 注解括号内的value相当于xml配置文件bean标签的id属性
  • 注解的value值可以不指定,默认值是将类名首字母小写

2.3 组件扫描配置

2.3.1 只扫描指定的注解

1
2
3
4
<context:component-scan base-package="com.company" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
  • 默认情况下,Spring将扫描base-package包下所有的类
  • 如果配置use-default-filters为false,那么Spring将根据配置进行组件扫描
  • 使用context:include-filter标签可以指定将被扫描的注解

2.3.2 不扫描指定的注解

1
2
3
4
<context:component-scan base-package="com.company">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 使用context:exclude-filter标签可以指定某些注解不被扫描

2.4 注入属性

2.4.1 Autowired

  • @Autowired注解根据属性的类型自动装配,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("UserDao update......");
}
}

@Service(value = "userService")
public class UserService {
@Autowired
UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}

在userDao属性上注解@Autowired,Spring将在组件扫描的包内搜寻符合条件的类型创建对象。在这个例子中,Spring找到UserDaoImpl是UserDao的实现类,符合条件

2.4.2 Qualifier

  • 在上面的例子中,如果UserDao接口有多个实现类,仅通过@Autowired注解自动装配会产生冲突,因为Spring不知道该创建哪个实现类的对象
  • 这个时候可以使用@Qualifier注解指定实现类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Repository
public class UserDaoImpl2 implements UserDao{
@Override
public void update() {
System.out.println("UserDao update......");
}
}

@Service(value = "userService")
public class UserService {
@Autowired
@Qualifier(value = "userDaoImpl2")
UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}

@Qualifier注解括号内的value必须和实现类注解括号内的value相同

2.4.3 Value

  • 使用@Value注解可以注入非对象类型的属性,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Repository
public class UserDaoImpl1 implements UserDao{
@Value(value = "Lihua")
private String name;

@Override
public void update() {
System.out.println("UserDao update......");
}

@Override
public String toString() {
return "UserDaoImpl1{" +
"name='" + name + '\'' +
'}';
}
}

2.5 完全注解开发

2.5.1 配置类

  • 创建配置类,替代xml配置文件,
1
2
3
@Configurable
@ComponentScan(basePackages = "com.company")
public class SpringConfig {}

2.5.2 加载配置类,创建对象

1
2
3
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService service = context.getBean("userService", UserService.class);
System.out.println(service);
  • 只需要将context的实现类替换成AnnotationConfigApplicationContext,将配置类的类型作为参数即可