Bean是组成项目的基础组件,在Spring探索(一)-- 基于控制反转的容器(IoC)一文中简单介绍了基于XML的bean的注册。Spring提供了多种途径的bean注册,包括基于XML配置
、基于Java配置
、基于注解配置
以及自动配置(只适用于SpringBoot)
。
本文将以下两个方面展开介绍:
- 配置方式的介绍及实例
- 组合使用不同配置方式
本文相关代码参看GitHub
Bean的配置方式
为了方便演示,我们通过创建一个班级服务来比较不同配置方式之间的区别,ClassMode
表示班级实例,IClassDao
和IClassService
分别是数据层和服务层的接口。
public class ClassModel {
private long id;
private String name;
// 省略getter setter toString等方法
}
public interface IClassDao {
ClassModel getById(long id);
}
public interface IClassService {
ClassModel getById(long id);
}
基于XML配置
首先创建基于XML的班级服务所需要的类XmlBasedClassDao
和XmlBasedClassService
。
public class XmlBasedClassDaoImpl implements IClassDao {
@Override
public ClassModel getById(long id) {
return new ClassModel(1, "XmlBasedClassDaoImpl");
}
}
public class XmlBasedConstructBasedClassServiceImpl implements IClassService {
private IClassDao classDao;
@Override
public ClassModel getById(long id) {
return classDao.getById(id);
}
}
public class XmlBasedPropertyBasedClassServiceImpl implements IClassDao {
private IClassDao classDao;
@Override
public ClassModel getById(long id) {
return classDao.getById(id);
}
}
配置的步骤如下:
- 将
XmlBasedClassDaoImpl
、XmlBasedConstructBasedClassServiceImpl
和XmlBasedPropertyBasedClassServiceImpl
注册为IoC容器中的bean - 将
XmlBasedClassDaoImpl
注入到XmlBasedConstructBasedClassServiceImpl
和XmlBasedPropertyBasedClassServiceImpl
中
XML文件中注册Bean
在项目的applications.xml
文件中添加如下内容
其中id
指bean的标志,class是bean对应的对象的类型(类全名)
Bean的注入
bean的注入有如下三种方式:
- 构造函数
- 属性
- 静态工厂方法(不常见,不做介绍本文)
- 基于构造函数的注入
需要在类中定义一个该类型的变量,然后将该变量通过构造函数赋值,最后在applications.xml
中配置该类的注入的方式为构造函数方式。
XmlBasedConstructBasedClassServiceImpl
的修改如下:
```java
public class XmlBasedConstructBasedClassServiceImpl implements IClassService {
private IClassDao classDao;
// 添加构造函数
public XmlBasedConstructBasedClassServiceImpl(IClassDao classDao) {
this.classDao = classDao;
}
// 省略其他
}
```
applications.xml
做如下修改:
```xml
```
constructor-arg
表示采用构造函数的方式注入bean,构造函数中变量的名字为name
的值,需要注入的bean通过ref
指定,ref
的值为其他bean的id
或name
。
- 基于属性的注入
基于属性的注入通过类变量对应的setter方法将bean注入到类中。修改applications.xml
中xmlBasedPropertyBasedClassService
注入的方式为基于属性注入,同时在XmlBasedPropertyBasedClassServiceImpl
中提供classDao
的setter方法。
public class XmlBasedPropertyBasedClassServiceImpl implements IClassService {
private IClassDao classDao;
// 省略其他
public void setClassDao(IClassDao classDao) {
this.classDao = classDao;
}
}
基于Java编码的配置
基于Java编码的配置方式在类上添加@Configuration
注解、在类方法上添加@Bean
表明该方法用与注册bean、在方法体中创建bean对象并处理相关依赖项。方法的返回值类型是bean的类型,方法名默认是bean的名称。@Configuration
相当于XML文件中的beans
节点,@Bean
相当于XML文件中的beans
节点。
还是从基于构造函数
和基于属性
两种方法演示基于Java的bean的配置方式。
- 基于构造函数
通过构造函数注入IClassDao
对象
public class JavaBasedConstructBasedClassServiceImpl implements IClassService {
private IClassDao classDao;
public JavaBasedConstructBasedClassServiceImpl(IClassDao classDao) {
this.classDao = classDao;
}
@Override
public ClassModel getById(long id) {
return classDao.getById(1);
}
}
在类ApplicationConfig
中配置JavaBasedClassDaoImpl
和JavaBasedConstructBasedClassServiceImpl
。
```java
@Configuration
public class ApplicationConfig {
@Bean
public JavaBasedClassDaoImpl javaBasedClassDao() {
return new JavaBasedClassDaoImpl();
}
@Bean
public JavaBasedConstructBasedClassServiceImpl javaBasedConstructBasedClassService() {
return new JavaBasedConstructBasedClassServiceImpl(javaBasedClassDao());
}
}
```
当需要注入bean到其他bean的时候,可以通过待注入bean对应的函数调用进行注入,例如这里通过构造函数将`javaBasedClassDao()`注入到`JavaBasedConstructBasedClassServiceImpl`中。
- 基于属性注入
唯一和基于构造函数的方式不同的是----通过类字段对应的setter
方式注入依赖依赖而不是构造函数。
通过setter方法注入依赖
public class JavaBasedPropertyBasedClassServiceImpl implements IClassService {
private IClassDao classDao;
@Override
public ClassModel getById(long id) {
return classDao.getById(1);
}
public void setClassDao(IClassDao classDao) {
this.classDao = classDao;
}
}
注入JavaBasedClassDaoImpl
到JavaBasedPropertyBasedClassServiceImpl
@Configuration
public class ApplicationConfig {
@Bean
public JavaBasedClassDaoImpl javaBasedClassDao() {
return new JavaBasedClassDaoImpl();
}
@Bean
public JavaBasedPropertyBasedClassServiceImpl javaBasedPropertyBasedClassService() {
JavaBasedPropertyBasedClassServiceImpl javaBasedPropertyBasedClassService = new JavaBasedPropertyBasedClassServiceImpl();
javaBasedPropertyBasedClassService.setClassDao(javaBasedClassDao());
return javaBasedPropertyBasedClassService;
}
}
若不做其他配置,spring并不能直接识别出@Configuration
和@Bean
标注,可以开启spring的自动扫描让spring能够识别@Configuration
和@Bean
注解。
```xml
```
在applications.xml
中添加context:component-scan
即可开启自动扫描注册,base-package
表示要扫描的基础包,将扫描该包下所有spring能够识别的标注。然后再通过ClassPathXmlApplicationContext
获取容器来获取bean。
```java
public class JavaBasedConstructBasedClassServiceImplTest {
private static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applications.xml");
private static IClassService javaBasedConstructBasedClassService = applicationContext.getBean("javaBasedConstructBasedClassService", JavaBasedConstructBasedClassServiceImpl.class);
public static void main(String[] args) {
System.out.println(javaBasedConstructBasedClassService.getById(1));
}
}
```
基于注解配置
在基于Java编码的配置一节中提到了spring的自动扫描注册功能,基于注解的配置同样依赖于这个功能。@Service
、@Repository
、@Component
和@Controller
等标注可以被spring自动扫描注册(前提是设定了扫描的基础包base package
,可以同时设置多个包),@Resource
和。通过AnnotationConfigApplicationContext
而不是ClassPathXmlApplicationContext
获取容器对象,AnnotationConfigApplicationContext
的构造函数basePackages
的参数可以需要扫描的包名。
-
@Repository
标注dao
@Repository(value = "annotationBasedClassDao")
public class AnnotationBasedClassDaoImpl implements IClassDao {
@Override
public ClassModel getById(long id) {
return new ClassModel(3, "AnnotationBasedClassDaoImpl");
}
}
@Repository
的value
类似于xml中bean的id属性,用于指明bean的名称
-
@Service
注册Service并且通过@Resource
注入依赖
@Service(value = "annotationBasedClassService")
public class AnnotationBasedClassServiceImpl implements IClassService {
@Resource(name = "annotationBasedClassDao")
private IClassDao classDao;
@Override
public ClassModel getById(long id) {
return classDao.getById(id);
}
}
@Service
的value
类似于xml中bean的id属性,用于指明bean的名称。@Resource
的name
指需要注入的bean的名称。
-
AnnotationConfigApplicationContext
获取bean
public class AnnotationBasedClassServiceImplTest {
private static ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.alex.springdemo");
private static IClassService classService = applicationContext.getBean("annotationBasedClassService", AnnotationBasedClassServiceImpl.class);
public static void main(String[] args) {
System.out.println(classService.getById(1));
}
}
自动配置
自动配置是指当spring能够自动的注册maven中引入的第三方依赖,这个特性目前只存在于SpringBoot
项目中,这里不做介绍。
混合配置
一般情况下项目中只需要一个ApplicationContext
对象,上文介绍了ClassPathXmlApplicationContext
和AnnotationConfigApplicationContext
两种容器的实现。由于XML配置是spring的传统配置方式,而java和注解是spring推荐的配置方式。因此在这些项目中,可能出现xml和java并存的情况,需要混合使用上诉配置方式。
常见的混合配置的情况有:
- 基于
AnnotationConfigApplicationContext
的配置导入基于XML的配置 - 基于
ClassPathXmlApplicationContext
的配置导入基于java的配置
- 对于基于注解的配置,只需要在xml或者AnnotationConfigApplicationContext中指定base package即可。
- 基于
ClassPathXmlApplicationContext
的配置只需要开启包自动扫描即可导入基于java和注解的配置- 因此下面只介绍基于
AnnotationConfigApplicationContext
的配置导入基于XML的配置
基于AnnotationConfigApplicationContext
的配置导入基于XML的配置
- 首先需要将xml文件中的bean导入到
@Configuration
注解的类中
@Configuration
@ImportResource(locations = "classpath:applications.xml")
public class ApplicationConfig {
// 省略其他
}
@ImportResource
用来导入xml文件中的配置,从locations
可以看出可以同时导入多个文件。
- 然后就可以在其他bean中注入xml中配置的bean了
@Service(value = "fixAnnotationAndXmlClassService")
public class FixAnnotationAndXmlClassServiceImpl implements IClassService {
@Resource(name = "studentDao")
private StudentDao studentDao;
@Resource(name = "annotationBasedClassDao")
private IClassDao classDao;
@Override
public ClassModel getById(long id) {
System.out.println(studentDao.getById(1));
return classDao.getById(id);
}
}
3.最后通过AnnotationConfigApplicationContext
获取bean
总结
本文首先分别介绍了基于XML、基于java和基于注解三种bean的配置方式,并且讨论并分析了如何混合使用这三种配置方式。