DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory 接口,可以通过这个类来动态注入 Bean。为了保证注入的 Bean 也能被 AOP 增强,我们需要实现 Bean 的工厂后置处理器接口 BeanFactoryPostProcessor。
需要动态注入的 Bean:
public class BookService {
BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public BookDao getBookDao() {
return bookDao;
}
}
实现 Bean 的工厂后置处理器接口:
@Component
public class BookServiceFactoryBean implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
//Bean 定义
BeanDefinitionBuilder builder=BeanDefinitionBuilder.genericBeanDefinition
(BookService.class);
//设置属性
builder.addPropertyReference("bookDao","bookDao");
//注册 Bean 定义
factory.registerBeanDefinition("bookService1",builder.getRawBeanDefinition());
//注册 Bean 实例
factory.registerSingleton("bookService2",new net.deniro.spring4.dynamic.BookService());
}
}
这里假设 bookDao 已注入容器(XML 或 注解方式)。
在此,我们既可以注册 Bean 的定义,也可以直接注册 Bean 的实例。
配置:
<context:component-scan base-package="net.deniro.spring4.dynamic"
/>
单元测试:
BookService bookService1 = (BookService) context.getBean("bookService1");
assertNotNull(bookService1);
assertNotNull(bookService1.getBookDao());
BookService bookService2 = (BookService) context.getBean("bookService2");
assertNotNull(bookService2);
为了更好地封装组件,增强组件的易用性,我们会将组件定义为标签。
自定义标签步骤为:
1. 采用 XSD 描述自定义标签的元素属性。
2. 编写 Bean 定义的解析器。
3. 注册自定义标签的解析器。
4. 绑定命名空间解析器。
在 resources 中的 schema 文件夹下创建 bookservice.xsd
<xsd:schema xmlns="http://www.deniro.net/schema/service"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.deniro.net/schema/service"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
>
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="book-service">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="dao" type="xsd:string" use="required"/>
xsd:extension>
xsd:complexContent>
xsd:complexType>
xsd:element>
xsd:schema>
接着定义服务标签解析器:
public class BookServiceDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
//创建 Bean 定义
BeanDefinitionBuilder builder=BeanDefinitionBuilder.genericBeanDefinition
(BookService.class);
//注入自定义的标签属性
String dao=element.getAttribute("dao");
builder.addPropertyReference("bookDao",dao);
//注册 Bean 定义
parserContext.registerBeanComponent(new BeanComponentDefinition(builder
.getRawBeanDefinition(),"bookService"));
return null;
}
}
然后把刚刚定义好的解析器注册到命名空间:
public class BookServiceNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("book-service",new BookServiceDefinitionParser());
}
}
接着在 resources 中创建 META-INF 文件夹,并新建 spring.schemas 与 spring.handlers,这两个文件分别用于配置自定义标签的文档结构文件路径以及解析自定义命名空间的解析器。
spring.handlers:
http\://www.deniro.net/schema/service=net.deniro.spring4.dynamic.BookServiceNamespaceHandler
spring.schemas:
http\://www.deniro.net/schema/service.xsd=schema/bookservice.xsd
注意: xsd 文件必须放在 resources 的子孙目录下。
引用自定义标签:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:me="http://www.deniro.net/schema/service"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.deniro.net/schema/service http://www.deniro.net/schema/service.xsd
">
<bean id="bookDao" class="net.deniro.spring4.dynamic.BookDao"/>
<me:book-service dao="bookDao"/>
beans>
这里,我们在头部引用了自定义标签,并命名为 “me”,然后就可以使用它咯O(∩_∩)O~