1. 基于注解的配置
- 使用注解定义Bean: 如果采用基于注解的配置文件,Bean定义信息通过在Bean实现类上标注注解实现
@Component注解在UserDao类声明处对类进行标注,可以被Spring容器识别,Spring容器自动将POJO(Plain Ordinary Java Object,简单的Java对象)转换为容器管理的Bean
@Component("userDao")
public class UserDao{ ...
}
等效于以下XML配置:
<bean id="userDao" class="com.smart.anno.UserDao" />
扫描注解定义的Bean: Spring提供了一个context命名空间,它提供了通过扫描类包以应用注解定义Bean的方式
注: 在①处声明context命名空间,在 ②中即可通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息如果仅希望扫描特定的类而非基类包下的所有类,可以使用resource-pattern属性过滤出特定的类
<context:component-scan base-package="com.smart" resource-pattern="anno/*.class"/>
<context:include-filter>
表示要包含的目标类,而<context:exclude-filter>
表示要排除的目标类。一个<context:component-scan>
下可以有若干个<context:include-filter>
和<context:exclude-filter>
元素<context:component-scan base-package="com.smart"> <context:include-filter type="regex" expression="com\.smart\.anno.*"/> <context:exclude-filter type="aspectj" expression="com.smart..*Controller+"/> </context:component-scan>
如果想仅扫描@Controller的Bean,use-defaul-filters属性设置为false
自动装配Bean: 使用@Autowired进行自动注入
@Autowired 默认按类型(byType)匹配的方式在容器中查找匹配的Bean,当有且只有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中- required属性 : 如果容器中没有一个和标注变量类匹配的Bean,那么Spring容器启动时将报NoSuchBeanDefinitionException异常。如果希望Spring找不到匹配的Bean完成注入也不要抛出异常,可以使用@Autowired(required=false)进行标注。
@Qualifier: 如果容器中有一个以上匹配的Bean时,可以通过@Qualifier注解限定Bean的名称
对类方法,类集合进行标注
- 对延迟加载注入: Spring支持延迟依赖注入,在Spring容器启动的时候,对于在Bean上标注
@Lazy
及@Autowired注解的属性,不会立即注入属性值,而是延迟到调用此属性的时候才注入属性值 - 对标准注解的支持: 支持JSR-250中定义的
@Resource
和JSR-330中定义的@Inject
注解
- Bean作用范围及及生命过程方法:
- Bean作用范围 : 默认的作用范围都是
singleton
,@Scope
注解,可以显式指定Bean的作用范围
- 生命过程方法 :
- 在使用
<bean>
进行配置时,可以通过init-method和destory-method属性指定Bean的初始化及容器销毁前执行的方法。 - Spring支持JSR-250中定义的
@PostConstruct
和@PreDestory
注解,相当于init-method和destory-method属性的功能
- 在使用
- Bean作用范围 : 默认的作用范围都是
2. 基于 java 类的配置
- 使用Java类提供Bean定义信息: 普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息。每个标注了@Bean的类方法都相当于提供了一个Bean的定义信息
Bean的类型由方法返回的类型决定,名称默认和方法名相同,也可以通过入参显式指定名称,如@Bean(name=”userDao”).
注: Spring容器会自动对@Configuration的类进行AOP增强,以植入Spring容器对Bean的逻辑管理,所以使用基于Java类的配置必须保证Spring.aop类和CGLIB类包加载到类路径中 - 使用基于Java类的配置信息启动Spring容器:
- 通过@Configuration类启动Spring容器 : Srping提供了一个
AnnotationConfigApplicationContext
类,能直接通过标注@Configuration的Java类启动Spring容器
还支持通过编码的方式加载多个@Configuration配置类,然后通过刷新容器应用这些配置类:
可以一个个地配置类,也可以通过@Import
将多个配置组装到一个配置类中,这样仅需注册这个组装好的配置类就可以启动容器:
- 通过XML配置引用@Configuration的配置: 通过XML配置文件启动Spring容器,仅需在XML通过
<context:component-scan>
扫描到相应的配置类即可
- 通过@Configuration配置类引用XML配置信息:
在@Configuration配置类中可以通过@ImportResource引入XML配置文件,在LogonAppConfig配置类中即可直接通过@Autowired引用XML配置文件中定义的Bean:
- 通过@Configuration类启动Spring容器 : Srping提供了一个
3. 基于Groovy DSL的配置:
Groovy是一种j基于JVM的敏捷开发语言。能与Java代码很好地结合和扩展现有代码。
- **使用Groovy DSL提供Bean定义信息 :**Spring支持Groovy DSL进行Bean配置,类似于XML配置,只不过配置信息是由Groovy脚本表达的,可以实现任何复杂的Bean配置
beans {
//①声明context命名空间
xmlns context: "http://www.springframework.org/schema/context" //导入命名空间
//②与注解混合使用,定义注解Bean扫描包路径
context.'component-scan'('base-package': "com.smart.groovy") {
'exclude-filter'('type': "aspectj", 'expression': "com.smart.xml.*")
}
//④读取app-conf.properties
def stream;
def config = new Properties();
try{
stream = new ClassPathResource('conf/app-conf.properties').inputStream
config.load(stream);
}finally {
if(stream!=null)
stream.close()
}
//⑥根据条件注入Bean
if( config.get("dataProvider") == "db"){
userDao(DbUserDao)
}else{
userDao(XmlUserDao)
}
//⑤配置无参构造函数Bean
logDao(LogDao){
bean->
bean.scope = "prototype" //配置当前Bean的作用域
bean.initMethod="init" //配置当前Bean的初始化方法
bean.destroyMethod="destory" //配置当前Bean的销毁方法
bean.lazyInit =true //配置当前Bean的延迟加载
}
//⑦配置有参构造函数注入Bean,参数是userDao
logonService(LogonService,userDao){
logDao = ref("logDao") //⑧配置属性注入,引用Groovy定义Bean
mailService = ref("mailService") //⑨配置属性注入,引用注解定义Bean
}
}
注:
- 在⑤处,logDao表示定义Bean的名称,括号内的LogDao表示要定义Bean的类名称
- 在⑥根据配置问价所配置的dataProvider选项值决定要注入的Bean,是Groovy DSL配置Bean 的一个重要灵活性体现
- 在⑦配置一个有参构造函数Bean,括号内第一个参数表示定义Bean的类名称,第二个表示LogonService构造函数userDao,采用属性注入logDao引用,其中ref()表示要引用容器中定义的Bean,在⑨也通过属性注入mailService,此处引用的Bean 是通过注解定义的
2.通过GenericGroovyApplicationContext启动Spring容器:
public class LogonServiceTest {
@Test
public void getBean(){
//加载指定Groovy Bean配置文件来创建容器
ApplicationContext ctx=
new GenericGroovyApplicationContext("classpath:com/smart/groovy.spring-context.groovy");
//加载Groovy定义的Bean
LogonService logonService=ctx.getBean(LogonService.class);
assertNotNull(logonService);
//加载注解定义的Bean
MailService mailService=ctx.getBean(MailService.class);
assertNotNull(mailService);
//判读注入的是不是DbUserDao
UserDao userDao=ctx.getBean(UserDao.class);
assertTrue(userDao instanceof DbUserDao);
}
}
3.通过编码方式动态添加Bean:
- 通过DefaultListableBeanFactory: DefaultListableBeanFactory实现了ConfigurableListableBeanFactory接口,提供了可扩展配置,循环枚举的功能,可以通过此类实现Bean动态注入。
@Component
public class UserServiceFactoryBean implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
//将ConfigurableListableBeanFactory转换为DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory=(DefaultListableBeanFactory)bf;
//通过BeanDifinitionBuilder创建Bean定义
BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
//设置属性userDao,此属性引用已经定义bean:userDao
beanDefinitionBuilder.addPropertyReference("userDao","userDao");
//注册Bean定义
beanFactory.registerBeanDefinition("userService",beanDefinitionBuilder.getRawBeanDefinition());
//直接注册一个Bean实例
beanFactory.registerSingleton("userService2",new UserService());
}
}
注: 为了实现在Spring容器启动阶段能动态注入自定义Bean,保证动态注入的Bean也能被AOP所增强,需要实现Bean工厂后置器处理接口BeanFactoPostProcessor
2. 扩展自定义标签:
在Spring中,自定标签需以下几个步骤:
- ①. 采用XSD描述自定义标签的元素属性
- ②. 编写Bean定义的解析器
- ③. 注册自定义标签解析器
- ④. 绑定命名空间解析器
第一步是定义标签元素的XML结构,采用XSD描述自定标签的元素属性
userService.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.smart.com/schema/service"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.smart.com/schema/service" ①
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/> ②
<xsd:element name="user-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>
注:
- 在①处指定了一个自定义命名标签的命名空间
- 在②导入Spring本身的beans命名空间
- 在③处定义了一个user-service标签
- 并且④在beans:identifiedType基础上定义了user-service标签的扩展属性”dao”,类似于继承属性
编写用户服务标签解析类,需实现BeanDefinitionParser,注意导入的Element是org.w3c.dom.Element:
import org.w3c.dom.Element;
public class UserServiceDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
//通过BeanDefinitionBuilder创建Bean定义
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
//获取自定义标签的属性
String dao = element.getAttribute("dao");
beanDefinitionBuilder.addPropertyReference("userDao",dao);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
//注册Bean定义
parserContext.registerBeanComponent(new BeanComponentDefinition( beanDefinition,"userService"));
return null;
}
}
注: 在②处通过element元素获取自定义标签的属性dao,并将已定义的userDao以引用的方式动态注入UserService,在③处通过parserContext注册UserService定义
现在可以将UserServiceDefinitionParser解析器注册到Spring命名空间解析器,其继承了NamespaceHandlerSupport:
public class UserServiceNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("user-service", new UserServiceDefinitionParser());
}
}
最后需要告诉Spring如何解析自定义标签,在源码resources目录创建META-INF文件夹,并在其中创建spring.handlers和spring.schemas两个问价,告诉Spring自定义标签的文档接口及解析它的类:
至此,自定义标签工作全部完成,可以在Spring配置文件中使用自定义的标签 :
注: 要使用自定义的标签,首先如①,在beans头部引用自定义标签的命名空间,并设置空间命名前缀“us”,之后就可以使用“us”标签声明定义的组件了
4. 不同配置方式比较
适用场景: