spring4.x(九)之第五章--bean的不同配置方式

1. 基于注解的配置

  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" />
  2. 扫描注解定义的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
      这里写图片描述

    这里写图片描述

  3. 自动装配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注解
  4. Bean作用范围及及生命过程方法:
    1. Bean作用范围 : 默认的作用范围都是singleton@Scope注解,可以显式指定Bean的作用范围
      这里写图片描述
    2. 生命过程方法 :
      • 在使用<bean>进行配置时,可以通过init-method和destory-method属性指定Bean的初始化及容器销毁前执行的方法。
      • Spring支持JSR-250中定义的@PostConstruct@PreDestory注解,相当于init-method和destory-method属性的功能

2. 基于 java 类的配置

  1. 使用Java类提供Bean定义信息: 普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息。每个标注了@Bean的类方法都相当于提供了一个Bean的定义信息
    Bean的类型由方法返回的类型决定,名称默认和方法名相同,也可以通过入参显式指定名称,如@Bean(name=”userDao”).
    注: Spring容器会自动对@Configuration的类进行AOP增强,以植入Spring容器对Bean的逻辑管理,所以使用基于Java类的配置必须保证Spring.aop类和CGLIB类包加载到类路径中
  2. 使用基于Java类的配置信息启动Spring容器:
    1. 通过@Configuration类启动Spring容器 : Srping提供了一个AnnotationConfigApplicationContext类,能直接通过标注@Configuration的Java类启动Spring容器
      这里写图片描述
      还支持通过编码的方式加载多个@Configuration配置类,然后通过刷新容器应用这些配置类:
      这里写图片描述
      可以一个个地配置类,也可以通过@Import将多个配置组装到一个配置类中,这样仅需注册这个组装好的配置类就可以启动容器:
      这里写图片描述
    2. 通过XML配置引用@Configuration的配置: 通过XML配置文件启动Spring容器,仅需在XML通过<context:component-scan>扫描到相应的配置类即可
      这里写图片描述
    3. 通过@Configuration配置类引用XML配置信息:
      这里写图片描述
      在@Configuration配置类中可以通过@ImportResource引入XML配置文件,在LogonAppConfig配置类中即可直接通过@Autowired引用XML配置文件中定义的Bean:
      这里写图片描述

3. 基于Groovy DSL的配置:

Groovy是一种j基于JVM的敏捷开发语言。能与Java代码很好地结合和扩展现有代码。

  1. **使用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:

  1. 通过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. 不同配置方式比较

这里写图片描述
这里写图片描述
这里写图片描述
适用场景:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/yin__ren/article/details/80930215