閱讀275 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Spring攻略(第2版)》——1.14 從Classpath中掃描組件

本節書摘來自異步社區《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 
     
     autowire="byType" />

   <bean 
      />
</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元素context:component-scan,要求Spring掃描這些注解。在這個元素中,你必須指定掃描組件所用的包。然後指定的包和子包都將被掃描。你可以使用分號來分隔多個掃描包。

前麵的模式足以使用Bean。Spring將把類名第一個字符小寫,對其餘部分采用Camel- cased命名法1組成Bean名稱。因此,下麵的語句是有效的(假定你已經實例化了一個包含context:component-scan元素的應用上下文)。

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

注意,這個元素還將注冊一個AutowiredAnnotationBeanPostProcessor實例,這個實例能夠自動裝配帶有@Autowired注解的屬性。

<beans xmlns="https://www.springframework.org/schema/beans"
   xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="https://www.springframework.org/schema/context"
   xsi:schemaLocation="https://www.springframework.org/schema/beans
     https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     https://www.springframework.org/schema/context
     https://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接口,並在context:component-scan元素的name-generator屬性中指定自己的命名策略。

最後更新:2017-05-31 16:01:34

  上一篇:go  《Spring攻略(第2版)》——1.15 小結
  下一篇:go  ECS 8080端口連接拒絕問題排查