欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring3.X学习笔记-基于注解和Java类的配置

程序员文章站 2022-03-03 13:17:06
...

上一篇文章里我们主要介绍了基于XML Schemal的配置方式。本篇文章里将介绍另外两种提供Bean定义的方式,基于注解和基于Java类的配置。

1、基于注解的配置

不管是XML还是注解,他们都是表达Bean定义的载体,其实质都是为Spring容器提供Bean定义的信息,表现形式上是将XML定义的东西通过类注解进行描述。Spring从2.0开始引入基于注解的配置方式,在3.0时得到进一步的完善。
下面是使用注解定义一个DAO的Bean:

package com.hhxs.bbt.dao;
import org.springframework.stereotype.Component;

// 1.通过Repository定义一个DAO的Bean
@Component("userDao")
public class UserDao {
    ...
}

在上面1处,使用@Component注解在UserDao类声明处对类进行标注,它可以被Spring容器识别,Spring容器自动将POJO转换为容器管理的Bean。它和以下的XML配置是等效的:

<Bean id="userDao" class="com.hhxs.bbt.dao.UserDao">

除了@Component以外,Spring提供了三个功能基本和@Component等效的注解,它们分别用于对DAO、Service及Web层的Controller进行注解,所以也称这些注解为Bean的衍型注解:

  • @Repository:用于对DAO实现类进行标注;
  • @Service:用于对Service实现类进行标注;
  • @Controller:用于对Controller实现类进行标注。

在@Component之外提供这三个注解,是为了让注解类本身的用途更加清晰,此外Spring还赋予了它们一些特殊功能。

1.1 使用注解配置信息启动Spring容器

Spring在2.5 后提供了一个context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式:

<?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-3.1.xsd
    http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.1.xsd>
    <description>Spring公共配置 </description>

    <!-- 1.扫描类包以应用注解定义的Bean -->
    <context:component-scan base-package="com.hhxs.bbt" />
</beans>

在上面1处通过context命名空间的compnent-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。

如果仅希望扫描特定的类而非基包下的所有类,那么可以使用resource-pattern属性过滤特定的类,如下所示:

<context:component-scan base-package="com.hhxs.bbt" resource-pattern="dao/*.class"/>

默认情况下resource-pattern属性的值为**/*.class,即基包里的所有类。这里我们设置为dao/*.class,则Spring仅会扫描基包里dao子包中的类。通过resource-pattern属性仅可按资源名称对基包中的类进行过滤,如果需要更详细的过滤,则需要用到<contex:component-scan>的过滤子元素:

<context:component-scan base-package="com.hhxs.bbt">
    <context:include-filter type="regex" expression="com.\hhxs.\bbt.*"/>
    <context:exclude-filter type="aspectj" expression="com.hhxs.bbt..*Controller+"/>
</context>
  • <context:include-filter>表示要包含的目标类
  • <context:exclude-filter>表示要排除在外的目标类

一个<context:component-scan>下可以包含若干个<context:include-filter><context:exclude-filter>元素。这两个过滤元素均支持多种类型的过滤表达式,如下表所示:

类别 示例 说明
annotation com.hhxs.bbt.XxxAnnotation 所有标注了XxxAnnotation的类。该类型采用目标类是否标注了某个注解进行过滤。
assignable com.hhxs.bbt.XxxService 所有继承或扩展XxxService的类。该类型采用目标类是否继承或扩展某个特定类进行过滤。
aspectj com.hhxs.bbt..*Service+ 所有类名以Service结束的类及继承或扩展它们的类。该类型采用AspectJ表达式进行过滤。
regex com.hhxs.bbt..* 所有com.hhxs.bbt类包下的类。该类型采用正则表达式根据目标类的类名进行过滤。
custom com.hhxs.bbt.XxxTypeFilter 采用XxxTypeFilter通过代码的方式根据过滤规则。该类必须实现org.springframework.core.type.TypeFilter接口

1.2 自动装配Bean

spring使用@Autowired注解实现Bean的依赖注入。@Autowired默认按类型匹配的方式,在容器查找匹配Bean,
**当且仅有一个匹配的**Bean时,Spring将其注入到@Autowired标注的变量中。

package com.hhxs.bbt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LogonService {

    @Autowired
    private LogDao logDao;

    @Autowired
    private UserDao userDao;

    ...
}

如果容器中没有一个和标注变量类型匹配的bean,Spring容器启动时将报NoSuchBeanDefinitionException异常。如果希望不抛出异常可以使用@Autowired(required=false)进行标注。默认情况下,@Autowired的required属性值为true。

如果容器中有一个以上匹配的Bean时,可以通过@Qualifier注解限定Bean的名称。

package com.hhxs.bbt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class LogonService {

    @Autowired
    private LogDao logDao;

    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;

    ...
}

对集合类进行标注

如果对类中集合类的变量和方法入参进行@Autowired标注,Spring会将容器中类型匹配的所有Bean都自动注入进来。这是一个很好的特性,所以在这里介绍下。

package com.hhxs.bbt.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class MyComponent {

    // Spring会将容器中所有类型为Plugin的Bean注入到这个变量中
    @Autowired(required=false)
    private List<Plugin> plugins;

    public List<Plugin> getPlugins() {
        return plugins;
    }
}


Spring如果发现变量是一个集合类,则它会将容器中匹配集合元素类型的所有Bean都注入进来。这里,Plugin为一个接口,它拥有两个实现类,分别是OnePlugin和TwoPlugin,这两个实现类都通过@Component标注为Bean,则Spring会将这两个Bean都注入到plugins中。

2、基于Java类的配置

2.1 使用Java类提供Bean定义信息

JavaConfig是Spring的一个子项目,它旨在通过Java类的方式提供Bean的定义信息。Spring3.0基于Java类配置的核心即取材于JavaConfig。

普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息了,每个标注了@Bean的类方法都相当于提供一个Bean的定义信息。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 1.将一个POJO标注定义为Bean的配置类
@Configuration
public class AppConf {
    // 2.以下两个方法定义了两个Bean,并提供了Bean的实例化逻辑
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
    @Bean
    public LogDao logDao() {
        return new LogDao()
    }

    @Bean
    public LogonService logonService() {
        LogonServcie logonService = new LogonService();
        // 将上面2处定义的Bean注入到logonService的Bean
        logonService.setLogDao(logDao());
        logonService.setUserDao(userDao());
        return logonService;
    }
}

1处在AppConf类的定义处标注了@Configuration注解,说明这个类可用于为Spring提供Bean信息。类的方法出可以标注@Bean注解,Bean的类型由方法返回值类型决定,名称默认和方法名相同,也可通过@Bean(name=”userDao”)显示指定Bean的名称。

上面的配置和以下XML配置等效:

<bean id="userDao" class="com.hhxs.bbt.dao.UserDao" />
<bean id="logDao" class="com.hhxs.bbt.dao.LogDao" />
<bean id="userDao" class="com.hhxs.bbt.conf.LogonService"  
    p:logDao-ref="userDao" p:userDao-ref="logDao" />

2.2 使用基于Java类的配置信息启动Spring容器

2.2.1 直接通过@Configuration类启动Spring容器

Spring提供了一个AnnotationConfigApplicationContext类,它能够直接通过标注@Configuration的Java类启动Spring容器。以下两种方式是等效的:

第一种:

package com.hhxs.bbt.conf;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class JavaConfigTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);
        LogonService logonService = ctx.getBean(logonService.class);
        logonService.printHello();
    }
}


第二种:

package com.hhxs.bbt.conf;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class JavaConfigTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        // 注册多个@Configuration配置类
        ctx.register(DaoConfig.Class);
        ctx.register(ServiceConfig.class);
        // 刷新容器以应用这些注册的配置类
        ctx.refresh();
        LogonService logonService = ctx.getBean(logonService.class);
        logonService.printHello();
    }
}

可以通过代码一个一个注册配置类,也可以通过@Import将多个配置类组装到一个配置类中,这样仅需要注册这个组装好的配置类就可以启动容器了。

package com.hhxs.bbt.conf;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Import;

@Configurable
@Import(DaoConfig.class)
public class ServiceConfig {

    @Bean
    public LogonService logonService() {
        LogonService logonService = new LogonService();
        return logonService;
    }
}

我们既可以在XML配置文件中引用@Configuration的配置,也可以通过Configuration配置类中引用XML配置信息。只要不同形式的Bean定义信息能够加载到Spring容器中,Spring就能足够“智能”的完成Bean之间的装配。

3、不同配置方式比较

配置方式比较:

基于XML配置 基于注解配置 基于Java类配置
Bean定义 <bean class="com.hhxs.bbt.UserDao"> Bean实现类出通过标注
@Component、@Repository、@Service、@Controller
在标注了@Configuration的Java类中,通过在类方法上标注@Bean定义一个Bean。方法必须提供Bean的实例化逻辑。
Bean名称 通过的id或name属性定义 通过注解的value属性定义,如@Component(“userDao”)。默认名称为小写字母打头的类名(不带包名):userDao 通过@Bean的name属性定义,如@Bean(“userDao”),默认名称为方法名。
Bean注入 通过子元素或通过p命名空间的动态属性 通过在成员变量或方法入参出标注@Autowired,按类型匹配自动注入 可以通过在方法处通过@Autowired使方法入参绑定Bean,然后在方法中通过代码进行注入,还可以通过调用配置类的@Bean方法进行注入
Bean生命过程方法 通过的init-method和destory-method属性指定Bean实现类的方法名。最多只能指定一个初始化方法和一个销毁方法 通过在目标方法上标注@PostConstruct和@PreDestroy注解指定初始化或销毁方法,可以定义任意多个方法 通过@Bean的initmethod或destoryMethod指定一个初始化或销毁方法。
Bean作用范围 通过的scope属性指定 通过在类定义出标注@Scope指定 通过在Bean方法定义处标注@Scope指定
Bean延迟初始化 通过的lazy-init属性指定,默认为default,继承与的default-lazy-init设置,该值默认为false 通过在类定义处标注@Lazy指定,如@Lazy(true) 通过在Bean方法定义处标注@Lazy指定


使用场景建议:

基于XML配置 基于注解配置 基于Java类配置
适合场景 1)Bean实现类来源于第三方类库
2)命名空间的配置
Bean实现类是当前项目开发的,
可以直接在Java类中使用基于注解的配置
基于Java类配置的优势在于
可以通过代码方式控制Bean初
始化的整体逻辑。

————本文结束感谢您的阅读————