《Spring攻略(第2版)》——1.14 从Classpath中扫描组件

    xiaoxiao2024-04-18  7

    本节书摘来自异步社区《Spring攻略(第2版)》一书中的第1章,第1.14节,作者: 【美】Gary Mak , Josh Long , Daniel Rubio著,更多章节内容可以访问云栖社区“异步社区”公众号查看

    1.14 从Classpath中扫描组件

    1.14.1 问题为了便于Spring IoC容器对组件的管理,你需要在Bean配置中逐个声明它们。但是,如果Spring能够自动地监测你的组件而不需要手工配置,将会大大地节省你的工作量。

    1.14.2 解决方案Spring提供一个强大的功能——组件扫描。这个功能能够利用特殊的典型化注解,从classpath中自动地扫描、检测和实例化你的组件。指示Spring管理组件的基本注解是@Component。其他特殊的典型化注解包括@Repository、@Service和 @Controller。它们分别指示持续层、服务层和表现层中的组件。

    1.14.3 工作原理假定你被要求使用数据库序列开发序列生成器应用,将每个序列的前缀和后缀存储在一个表中。首先,你创建一个域类Sequence,包含id、Prefix和suffix属性。

    package com.apress.springrecipes.sequence; public class Sequence {    private String id;    private String prefix;    private String suffix;    // Constructors, Getters, and Setters    ... }

    然后,你为数据访问对象(DAO)创建一个接口,负责从数据库访问数据。getSequence()方法从表中按照ID装入Sequence对象,而getNextValue()方法读取特定数据库序列的下一个值。

    package com.apress.springrecipes.sequence; public interface SequenceDao {    public Sequence getSequence(String sequenceId);    public int getNextValue(String sequenceId); }

    在生产应用中,你应该使用某种数据访问技术如JDBC或者对象/关系映射实现这个DAO接口。但是为了测试的目的,我们使用Map来存储序列实例和值。

    package com.apress.springrecipes.sequence; ... public class SequenceDaoImpl implements SequenceDao {    private Map<String, Sequence> sequences;    private Map<String, Integer> values;    public SequenceDaoImpl() {      sequences = new HashMap<String, Sequence>();      sequences.put("IT", new Sequence("IT", "30", "A"));      values = new HashMap<String, Integer>();      values.put("IT", 100000); }    public Sequence getSequence(String sequenceId) {      return sequences.get(sequenceId);    }    public synchronized int getNextValue(String sequenceId) {      int value = values.get(sequenceId);      values.put(sequenceId, value + 1);      return value;    } }

    你还需要一个服务对象作为外观(Façade),提供序列生成服务。在内部,这个服务对象将与DAO交互,处理序列生成请求。所以它要求对DAO的引用。

    package com.apress.springrecipes.sequence; public class SequenceService {    private SequenceDao sequenceDao;    public void setSequenceDao(SequenceDao sequenceDao) {      this.sequenceDao = sequenceDao;    }    public String generate(String sequenceId) {      Sequence sequence = sequenceDao.getSequence(sequenceId);      int value = sequenceDao.getNextValue(sequenceId);      return sequence.getPrefix() + value + sequence.getSuffix();    } }

    最后,你必须在Bean配置文件中配置这些组件,使序列生成器应用正常工作。你可以自动装配组件以减少配置量。

    <beans ...>    <bean id="sequenceService"      class="com.apress.springrecipes.sequence.SequenceService"      autowire="byType" />    <bean id="sequenceDao"      class="com.apress.springrecipes.sequence.SequenceDaoImpl" /> </beans>

    然后,你可以用下列的Main类测试前述的组件:

    package com.apress.springrecipes.sequence; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main {    public static void main(String[] args) {      ApplicationContext context =         new ClassPathXmlApplicationContext("beans.xml");      SequenceService sequenceService =         (SequenceService) context.getBean("sequenceService");      System.out.println(sequenceService.generate("IT"));      System.out.println(sequenceService.generate("IT"));    } }

    自动扫描组件从Spring 2.5版本开始提供的组件扫描功能能够自动地从Classpath中扫描、检测和实例化你的组件。默认情况下,Spring可以检测所有带有典型化注解的组件。指示Spring管理组件的基本注解是@Component。你可以将其应用到SequenceDaoImpl类。

    package com.apress.springrecipes.sequence; import org.springframework.stereotype.Component; import java.util.Map; @Component public class SequenceDaoImpl implements SequenceDao {    ... }

    你也可以将这种典型化注解应用到SequenceService类,让Spring检测它。此外,应用@Autowired注解到DAO字段,让Spring按照类型进行自动装配。注意,因为你在一个字段上使用注解,所以不需要设值方法。

    package com.apress.springrecipes.sequence; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class SequenceService {    @Autowired    private SequenceDao sequenceDao;    ... }

    有了应用到组件类的典型化注解,就能通过声明一个XML元素,要求Spring扫描这些注解。在这个元素中,你必须指定扫描组件所用的包。然后指定的包和子包都将被扫描。你可以使用分号来分隔多个扫描包。

    前面的模式足以使用Bean。Spring将把类名第一个字符小写,对其余部分采用Camel- cased命名法1组成Bean名称。因此,下面的语句是有效的(假定你已经实例化了一个包含元素的应用上下文)。

    SequenceService sequenceService = (SequenceService) context.getBean("sequenceService");

    注意,这个元素还将注册一个AutowiredAnnotationBeanPostProcessor实例,这个实例能够自动装配带有@Autowired注解的属性。

    <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.0.xsd      http://www.springframework.org/schema/context      http://www.springframework.org/schema/context/spring-context-3.0.xsd">    <context:component-scan base-package="com.apress.springrecipes.sequence" /> </beans>

    @Component注解是指示一般用途的组件的基本典型化注解。实际上,还有其他具体的典型化注解,指示不同层次中的组件。首先,@Repository典型化注解指示持续层中的一个DAO组件。

    package com.apress.springrecipes.sequence; import org.springframework.stereotype.Repository; @Repository public class SequenceDaoImpl implements SequenceDao {    ... }

    然后,@Service典型化注解指示服务层中的一个服务组件。

    package com.apress.springrecipes.sequence; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class SequenceService {    @Autowired    private SequenceDao sequenceDao;    ... }

    另一个组件典型化注解@Controller指示表现层中的一个控制器组件。在第8章“Spring @MVC”中还将介绍。

    过滤扫描的组件默认情况下,Spring将检测所有用@Component、@Repository、@Service、@Controller或者本身加上@Component注解的自定义注解类型。你可以应用一个或多个包含/排除过滤器自定义这一扫描。

    Spring支持4种过滤器表达式。annotation和assignable类型用于指定过滤的注解类型和类/接口。regex和aspectj类型允许指定正则表达式和AspectJ切入点表达式匹配类。你还可以用use-default-filters属性禁用默认过滤器。

    例如,下面的组件扫描包含了所有名称中包含Dao或Service的类,排除带有@Controller注解的类:

    <beans ...>    <context:component-scan base-package="com.apress.springrecipes.sequence">      <context:include-filter type="regex"         expression="com\.apress\.springrecipes\.sequence\..*Dao.*" />      <context:include-filter type="regex"         expression="com\.apress\.springrecipes\.sequence\..*Service.*" />      <context:exclude-filter type="annotation"         expression="org.springframework.stereotype.Controller" />    </context:component-scan> </beans>

    因为你已经应用了include过滤器检测所有名称包含Dao或者Service的类,SequenceDaoImpl和SequenceService组件就能在没有典型化注解的情况下被自动检测出来。

    命名检测到的组件默认情况下,Spring将非限定类名的第一个字符改为小写来命名检测到的组件。例如,SequenceService类将被命名为sequenceService。你可以在典型化注解值中显式地指定组件的名称。

    package com.apress.springrecipes.sequence; ... import org.springframework.stereotype.Service; @Service("sequenceService") public class SequenceService {    ... } package com.apress.springrecipes.sequence; import org.springframework.stereotype.Repository; @Repository("sequenceDao") public class SequenceDaoImpl implements SequenceDao {    ... }

    你可以实现BeanNameGenerator接口,并在元素的name-generator属性中指定自己的命名策略。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)