利用Spring掃包實現發現具體的註解類
需求描述:
在RocketMQ客戶端開發過程中,發現對於消費端的消費程式客戶端在開發過程中是無法提前預知的,即消費端的消費程式需要具體到業務中去實現。 因此,發現具體的消費方法是RocketMQ客戶端設計過程中一個不得不考慮的問題。為了降低客戶端對上層業務系統的侵入性,計劃採用業務消費類 新增特定“註解”+客戶端“掃包”的方式來發現業務消費程式。由於Spring的掃包方法是經受過普遍考驗的,因此決定在Spring原始碼的基礎上進行 修改實現。
測試程式碼:
package com.abc.lottery.easymq; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.Iterator; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.util.SystemPropertyUtils; import com.google.common.collect.Sets; import com.abc.lottery.easymq.handler.MQConsumerAnnotation; public class ScanPackageTest { private final static Log log = LogFactory.getLog(ScanPackageTest.class); //掃描 scanPackages 下的檔案匹配符 public static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; /** * 找到scanPackages下帶註解annotation的全部類資訊 * @param scanPackages 掃包路徑,多個路徑用","分割 * @param annotation 註解類 */ public static Set<String> findPackageAnnotationClass(String scanPackages, Class<? extends Annotation> annotation) { if (StringUtils.isEmpty(scanPackages)) { return Sets.newHashSet(); } // 排重包路徑,避免父子路徑重複掃描 Set<String> packages = checkPackages(scanPackages); //獲取Spring資源解析器 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); //建立Spring中用來讀取resource為class的工具類 MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver); Set<String> fullClazzSet = Sets.newHashSet(); for (String basePackage : packages) { if (StringUtils.isEmpty(basePackage)) { continue; } String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/" + DEFAULT_RESOURCE_PATTERN; try { //獲取packageSearchPath下的Resource,這裡得到的Resource是Class資訊 Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { //檢查resource,這裡的resource都是class String fullClassName = loadClassName(metadataReaderFactory, resource); if (isAnnotationClass(fullClassName, annotation)) { fullClazzSet.add(fullClassName); } } } catch (Exception e) { log.error("獲取包下面的類資訊失敗,package:" + basePackage, e); } } return fullClazzSet; } /** * 排重、檢測package父子關係,避免多次掃描 * @param scanPackages 掃包路徑 * @return 返回全部有效的包路徑資訊 */ private static Set<String> checkPackages(String scanPackages) { if (StringUtils.isEmpty(scanPackages)) { return Sets.newHashSet(); } Set<String> packages = Sets.newHashSet(); //排重路徑 Collections.addAll(packages, scanPackages.split(",")); for (String packageStr : packages.toArray(new String[packages.size()])) { if (StringUtils.isEmpty(packageStr) || packageStr.equals(".") || packageStr.startsWith(".")) { continue; } if (packageStr.endsWith(".")) { packageStr = packageStr.substring(0, packageStr.length() - 1); } Iterator<String> packageIte = packages.iterator(); boolean needAdd = true; while (packageIte.hasNext()) { String pack = packageIte.next(); if (packageStr.startsWith(pack + ".")) { //如果待加入的路徑是已經加入的pack的子集,不加入 needAdd = false; } else if (pack.startsWith(packageStr + ".")) { //如果待加入的路徑是已經加入的pack的父集,刪除已加入的pack packageIte.remove(); } } if (needAdd) { packages.add(packageStr); } } return packages; } /** * 載入資源,根據resource獲取className * @param metadataReaderFactory spring中用來讀取resource為class的工具 * @param resource 這裡的資源就是一個Class */ private static String loadClassName(MetadataReaderFactory metadataReaderFactory, Resource resource) throws IOException { try { if (resource.isReadable()) { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); if (metadataReader != null) { return metadataReader.getClassMetadata().getClassName(); } } } catch (Exception e) { log.error("根據Spring resource獲取類名稱失敗", e); } return null; } /** * 判斷類是否包含特定的註解 * @param fullClassName * @param annotationClass * @return * @throws ClassNotFoundException */ private static boolean isAnnotationClass(String fullClassName, Class<? extends Annotation> annotationClass) throws ClassNotFoundException { //利用反射,根據類名獲取類的全部資訊 Class<?> clazz = Class.forName(fullClassName); //獲取該類的註解資訊 Annotation annotation = clazz.getAnnotation(annotationClass); if (annotation != null) {//包含annotationClass註解 return true; } return false; } }
輸出結果
public static void main(String[] args) { String packages = "com.abc.lottery.easymq"; System.out.println("annotation class:" + findPackageAnnotationClass(packages, MQConsumerAnnotation.class)); } 輸出結果:annotation class:[com.abc.lottery.easymq.handler.PaySuccessMQHandler]
結果說明
自己定義了一個註解MQConsumerAnnotation @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MQConsumerAnnotation { String topic(); String tags(); } PaySuccessMQHandler類是com.abc.lottery.easymq包路徑下唯一一個新增MQConsumerAnnotation註解的類 @MQConsumerAnnotation(topic = "paySuccessTopic", tags = "tagA") public class PaySuccessMQHandler implements MQRecMsgHandler { public void handle(List<String> msg) throws Exception { System.out.println(msg); } } 因此,掃描com.abc.lottery.easymq包路徑得到唯一的一個類PaySuccessMQHandler的全路徑資訊。後面,可以根據PaySuccessMQHandler的類資訊,通過反射獲取類例項,完成掃包獲取消費方法。
相關推薦
利用Spring掃包實現發現具體的註解類
編輯此區域 需求描述: 在RocketMQ客戶端開發過程中,發現對於消費端的消費程式客戶端在開發過程中是無法提前預知的,即消費端的消費程式需要具體到業務中去實現。 因此,發現具體的消費方法是RocketMQ客戶端設計過程中一個不得不考慮的問題。為了降低客戶端對上層業務
利用Spring IOC DI 實現軟體分層解耦
1.軟體分層思想 在軟體領域有MVC軟體設計思想,指導著軟體開發過程。在javaee開發領域,javaee的經典三層架構MVC設計思想的經典應用。而在軟體設計思想中,追求的是"高內聚 低耦合"的目標,利用Spring的IOC 和 DI 可以非常方便的實現這個需求。 2
SpringBoot第二講利用Spring Data JPA實現資料庫的訪問(二)_分頁和JpaSpecificationExecutor介面介紹
我們繼續研究spring jpa data,首先看看分頁和排序的實現,在原來的程式碼中,我們如果希望實現分頁,首先得建立一個Pager的物件,在這個物件中記錄total(總數),totalPager(總頁數),pageSize(每頁多少條記錄),pageInde
SpringBoot第二講 利用Spring Data JPA實現資料庫的訪問(一)
在基本瞭解了springboot的執行流程之後,我們需要逐個來突破springboot的幾個關鍵性問題,我們首先解決的是springboot訪問資料庫的問題。Java訪問資料庫經歷了幾個階段,第一個階段是直接通過JDBC訪問,這種方式工作量極大,而且會做大量的重複勞動,之
利用php-java-bridge包實現PHP呼叫JAVA類
在工作中需要將office文件軟體轉為pdf檔案,查找了很多技術發現使用openoffice可以完成這個要求,但呼叫它的功能全部是java寫的,所有需要在php中呼叫java類,就有了下面除錯過程。 1、安裝java,這個過程我就不寫了,網上有很多,講的也很細。(其實可以不
包掃描自定義註解類並例項化
1. 新建Maven 專案 annotation 2. pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi
利用java反射機制實現javaweb自動呼叫類的方法
public class BookServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequ
Spring @Qualifier 註解,一個service 介面,有多個實現時,如何在controller 只引入service 介面,進而實現引入具體bean
參考: https://blog.csdn.net/mazuyu408/article/details/79629846 @Qualifier 作用 在只引入service 介面的情況下在,指示spring 引入value 為那個別名的具體service 實現類 場景: 假設有一個場
Spring MVC 註解 initMethod 掃包
springMVC詳解以及註解說明 基於註釋(Annotation)的配置有越來越流行的趨勢,Spring 2.5 順應這種趨勢,提供了完全基於註釋配置 Bean、裝配 Bean 的功能,您可以使用基於註釋的 Spring IoC 替換原來基於 XML 的配置。本文通過例項
spring mvc實現自定義註解
poi org param 運行時 onf dha ogg logs exec 實現方式:使用@Aspect實現: 1. 新建註解接口:CheckSign package com.soeasy.web.utils; import org.springframework.
利用Spring Cloud實現微服務(八)- 熔斷機制
1. 熔斷機制介紹 在介紹熔斷機制之前,我們需要了解微服務的雪崩效應。在微服務架構中,微服務是完成一個單一的業務功能,這樣做的好處是可以做到解耦,每個微服務可以獨立演進。但是,一個應用可能會有多個微服務組成,微服務之間的資料互動通過遠端過程呼叫完成。這就帶來一個問題,假設微服務A呼叫微服務
Spring的AOP(xml和註解實現AOP,以及代理模式)
AOP術語: 連線點(Joinpoint):程式執行的某一個特定位置,如類初始前後,方法的執行前後。而Spring只支援方法的連線點。 切點(Pointcut):切點可以定位到相應的連線點,一個切點可以定位多個連線點。&
Spring Aop底層原理詳解(利用spring後置處理器實現AOP)
寫在前面:對於一個java程式設計師來說,相信絕大多數都有這樣的面試經歷,面試官問:你知道什麼是aop嗎?談談你是怎麼理解aop的?等等諸如此類關於aop的問題。當然對於一些小白可能會一臉懵逼;對於一些工作一兩年的,可能知道,哦!aop就是面向切面變成,列印日誌啊,什麼什麼的,要是有點學
利用xslt與xml實現具體字段字母的大小寫轉換
大小 xsl encoding lca var htm 大小寫 template llc 定義一個全局的變量 <xsl:variable name="smallcase" select="‘abcdefghijklmnopqrstuvwxyz‘" /> <
實現Telnet遠端登入,利用Wireshark抓包分析
一、實驗環境 伺服器:windows server 2008 客戶機:windows 2007 網絡卡連線:NAT(Vmnet 8) 實現客戶機遠端登入伺服器 二、實驗步驟 1、設定IP地址 伺服器:windows server 2008 IP 地 址:192.168.10.1 子
利用spring的AOP來實現Redis快取
為什麼使用Redis 資料查詢時每次都需要從資料庫查詢資料,資料庫壓力很大,查詢速度慢,因此設定快取層,查詢資料時先從redis中查詢,如果查詢不到,則到資料庫中查詢,然後將資料庫中查詢的資料放到redis中一份,下次查詢時就能直接從redis中查到,不需要查
Spring基礎學習(四 AOP 註解實現)
配置XML applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
Spring-boot mongodb ID自增長註解實現 適用於JDK 1.7和JDK 1.8
開發工具Idea ,JDK1.8 Entity類 SeqInfo.java package com.gl.springbootdao.mongodb.entity; import lombok.Getter; import lombok.Setter; import lombok.T
利用Spring框架在前端實現對資料庫的增刪改查
在前端頁面上顯示購物資料庫資料,並且可以這增、刪、改、查 1.首先在WEB 配置檔案 <!-- 配置DispatcherServlet --> <servlet> <servlet-name>springmvc</serv
利用spring-security實現登入
首先建立maven工程(war) pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:sc