欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

獲取JAVA接口的所有實現(xiàn)類

本文價值在于 包掃描的原理探究和實現(xiàn)

創(chuàng)新互聯(lián)公司是一家專注于做網(wǎng)站、網(wǎng)站制作與策劃設(shè)計,龍門網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)十多年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:龍門等地區(qū)。龍門做網(wǎng)站價格咨詢:13518219792

一、背景

項目開發(fā)中,使用Netty做服務(wù)端,保持長連接與客戶端(agent)通訊。Netty服務(wù)端需要根據(jù)不同消息類型,加載對應(yīng)的Processer(消息處理器)對消息進(jìn)行處理。問題就出現(xiàn)了,Processer會隨著消息業(yè)務(wù)類型增多進(jìn)行擴展,每一次增加Processer都需要手動new出來一個實例,放到Map里(key為消息類型碼,value為Processer實例),供調(diào)度程序(ProcesserManager)根據(jù)端消息類型調(diào)度,顯然這是件很麻煩的一件事,不僅操作瑣碎,也不符合低耦合、模塊化的設(shè)計思想。

二、解決思路

我們所寫的每一個Processer都是IProcessor這個接口的實現(xiàn):

public interface IProcessor {

void process(BaseMsgWrapper msg) throws Exception;

EventEnum getType();

default String getIpFromChannelContext(ChannelHandlerContext ctx){

String[] ipPort = ctx.channel().remoteAddress().toString().split(":");

return ipPort[0].substring(1);

}

}

其中:

void process(BaseMsgWrapper msg) 為消息處理方法

void getIpFromChannelContext (BaseMsgWrapper msg) 為獲取客戶端ip的默認(rèn)方法

假如我們在Netty服務(wù)端啟動時,能獲取該接口的所有實現(xiàn)類,然后把這些實現(xiàn)類分別new出來,放到Map中,那么這個工作就可以自動化掉了。

最終實現(xiàn)的效果就是 消息處理器只要 implements IProcessor接口,就會被自動加載調(diào)用,而不再需要手動寫到Map中。這樣就將ProcesserManager 與 Processer解耦開了。

為此,IProcessor接口需要增加一個方法

EventEnum getType();

即需要Processer表明自己對應(yīng)的消息類型,沒這個方法之前,我們都是在put進(jìn)Map的時候,手動把消息類型寫進(jìn)去的(可以想象之前的做法多么的low)

三、實現(xiàn)過程

想法是很好,但實現(xiàn)不是那么容易,踩了很多坑。

首先是網(wǎng)上查資料,看看其他人都怎么做的,有沒有做好的輪子。

(Java – 獲取指定接口的所有實現(xiàn)類或獲取指定類的所有繼承類)

這篇博客提供的大致思路:

1) 獲取當(dāng)前線程的ClassLoader

2) 通過ClassLoader獲取當(dāng)前工作目錄,對目錄下的文件進(jìn)行遍歷掃描。

3) 過濾出以.class為后綴的類文件,并加載類到list中

4) 對list中所有類進(jìn)行校驗,判斷是否為指定接口的實現(xiàn)類,并排除自身。

5) 返回所有符合條件的類。

這個思路是對的,但是考慮不全,不能拿來工程應(yīng)用,另外博文中提供的源碼應(yīng)該只是一個實驗代碼,有不少缺陷。

1)這個方?jīng)]有考慮不同的文件格式。當(dāng)程序打成jar包,發(fā)布運行時,上述的這種遍歷file的操作 就失效了。

2)局限性。只能掃描到當(dāng)前方法的同級目錄及其子目錄。無法覆蓋整個模塊。

3)遍歷文件的邏輯太啰嗦,可以簡化。

4)通過ClassLoader獲取當(dāng)前工作目錄時,使用了“…/bin/”這么一個固定的目錄名。

Enumeration enumeration = classLoader.getResources("…/bin/" + path)

事實上,不同的IDE(主要是eclipse 和 idea)項目的資源目錄,在這一點上是不同的。

(獲取全部子類或接口的全部實現(xiàn))

這篇博客考慮到了在運行環(huán)境中,需要通過JarFile工具類進(jìn)行單獨處理。

局限性:

需要手動指定要掃描的Jar文件或目錄,沒有通過ClassLoader 自動獲取當(dāng)前運行的上下文。

此外classLoader.getResource 獲得的 資源目錄 是個URL對象,如何轉(zhuǎn)換成JarFile對象 花費了我不少時間求索:

JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();

JarFile jarFile = jarURLConnection.getJarFile();

綜合上述思路和自己的試驗研究,得出獲取接口所有實現(xiàn)類的算法流程如下:

四、代碼實現(xiàn)

package com.hikvision.hummer.pandora.gateway.proc;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.File;

import java.net.JarURLConnection;

import java.net.URL;

import java.util.ArrayList;

import java.util.Enumeration;

import java.util.List;

import java.util.jar.JarEntry;

import java.util.jar.JarFile;

/**

獲取接口的所有實現(xiàn)類 理論上也可以用來獲取類的所有子類

查詢路徑有限制,只局限于接口所在模塊下,比如pandora-gateway,而非整個pandora(會遞歸搜索該文件夾下所以的實現(xiàn)類)

路徑中不可含中文,否則會異常。若要支持中文路徑,需對該模塊代碼中url.getPath() 返回值進(jìn)行urldecode.

Created by wangzhen3 on 2017/6/23.

*/

public class ClassUtil {

private static final Logger LOG = LoggerFactory.getLogger(ClassUtil.class);

public static ArrayList getAllClassByInterface(Class clazz) {

ArrayList list = new ArrayList<>();

// 判斷是否是一個接口

if (clazz.isInterface()) {

try {

ArrayList allClass = getAllClass(clazz.getPackage().getName());

/**

* 循環(huán)判斷路徑下的所有類是否實現(xiàn)了指定的接口 并且排除接口類自己

/

for (int i = 0; i < allClass.size(); i++) {

/*

* 判斷是不是同一個接口

*/

// isAssignableFrom:判定此 Class 對象所表示的類或接口與指定的 Class

// 參數(shù)所表示的類或接口是否相同,或是否是其超類或超接口

if (clazz.isAssignableFrom(allClass.get(i))) {

if (!clazz.equals(allClass.get(i))) {

// 自身并不加進(jìn)去

list.add(allClass.get(i));

}

}

}

} catch (Exception e) {

LOG.error(“出現(xiàn)異常{}”,e.getMessage());

throw new RuntimeException(“出現(xiàn)異?!?e.getMessage());

}

}

LOG.info(“class list size :”+list.size());

return list;

}

/**

從一個指定路徑下查找所有的類

@param packagename

*/

private static ArrayList getAllClass(String packagename) {

LOG.info(“packageName to search:”+packagename);

List classNameList = getClassName(packagename);

ArrayList list = new ArrayList<>();

for(String className : classNameList){

try {

list.add(Class.forName(className));

} catch (ClassNotFoundException e) {

LOG.error(“l(fā)oad class from name failed:”+className+e.getMessage());

throw new RuntimeException(“l(fā)oad class from name failed:”+className+e.getMessage());

}

}

LOG.info(“find list size :”+list.size());

return list;

}

/**

獲取某包下所有類

@param packageName 包名

@return 類的完整名稱

*/

public static List getClassName(String packageName) {

List fileNames = null;

ClassLoader loader = Thread.currentThread().getContextClassLoader();

String packagePath = packageName.replace(".", “/”);

URL url = loader.getResource(packagePath);

if (url != null) {

String type = url.getProtocol();

LOG.debug("file type : " + type);

if (type.equals(“file”)) {

String fileSearchPath = url.getPath();

LOG.debug(“fileSearchPath: “+fileSearchPath);

fileSearchPath = fileSearchPath.substring(0,fileSearchPath.indexOf(”/classes”));

LOG.debug("fileSearchPath: "+fileSearchPath);

fileNames = getClassNameByFile(fileSearchPath);

} else if (type.equals(“jar”)) {

try{

JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();

JarFile jarFile = jarURLConnection.getJarFile();

fileNames = getClassNameByJar(jarFile,packagePath);

}catch (java.io.IOException e){

throw new RuntimeException(“open Package URL failed:”+e.getMessage());

}

}else{

throw new RuntimeException("file system not support! cannot load MsgProcessor!");

}

}

return fileNames;

}

/**

從項目文件獲取某包下所有類

@return 類的完整名稱

*/

private static List getClassNameByFile(String filePath) {

List myClassName = new ArrayList();

File file = new File(filePath);

File[] childFiles = file.listFiles();

for (File childFile : childFiles) {

if (childFile.isDirectory()) {

myClassName.addAll(getClassNameByFile(childFile.getPath()));

} else {

String childFilePath = childFile.getPath();

if (childFilePath.endsWith(".class")) {

childFilePath = childFilePath.substring(childFilePath.indexOf("\classes") + 9, childFilePath.lastIndexOf("."));

childFilePath = childFilePath.replace("\", “.”);

myClassName.add(childFilePath);

}

}

}

return myClassName;

}

/**

從jar獲取某包下所有類

@return 類的完整名稱

*/鄭州人流醫(yī)院哪家好 http://www.89906662.com/

private static List getClassNameByJar(JarFile jarFile ,String packagePath) {

List myClassName = new ArrayList();

try {

Enumeration entrys = jarFile.entries();

while (entrys.hasMoreElements()) {

JarEntry jarEntry = entrys.nextElement();

String entryName = jarEntry.getName();

//LOG.info(“entrys jarfile:”+entryName);

if (entryName.endsWith(".class")) {

entryName = entryName.replace("/", “.”).substring(0, entryName.lastIndexOf("."));

myClassName.add(entryName);

//LOG.debug(“Find Class :”+entryName);

}

}

} catch (Exception e) {

LOG.error(“發(fā)生異常:”+e.getMessage());

throw new RuntimeException(“發(fā)生異常:”+e.getMessage());

}

return myClassName;

}

}

五、項目應(yīng)用

接口IProcessor

*/

public interface IProcessor {

void process(BaseMsgWrapper msg) throws Exception;

EventEnum getType();

default String getIpFromChannelContext(ChannelHandlerContext ctx){

String[] ipPort = ctx.channel().remoteAddress().toString().split(":");

return ipPort[0].substring(1);

}

}

接口實現(xiàn)類HeartBeatMsgProcessor, 主要 加了注解@Component

@Component

public class HeartBeatMsgProcessor implements IProcessor {

private static final HikGaLogger logger = HikGaLoggerFactory.getLogger(HeartBeatMsgProcessor.class);

@Override

public EventEnum getType(){

return EventEnum.HEART_BEAT;

}

private BaseMsg bmsg = new BaseMsg( "requestId-null", EventEnum.HEART_BEAT.getEventType(),1L, Constants.ASYN_INVOKE,

"pong", "uuid-null", Constants.ZH_CN);

@Override

public void process(BaseMsgWrapper msg) throws Exception {

Assert.notNull(msg);

logger.debug("ping from [{}]", msg.getCtx().channel().remoteAddress().toString());

msg.getCtx().writeAndFlush(bmsg);

}

}

調(diào)用ClassUtil 獲取接口的所有類,并根據(jù)查找到的類從spring容器中取出bean.

private ProcessorManager(){

List classList = ClassUtil.getAllClassByInterface(IProcessor.class);

LOG.info("processor num :"+classList.size());

for(Class classItem : classList){

IProcessor msgProcessor = null;

try{

msgProcessor = (IProcessor) AppContext.getBean(classItem);

processorMap.put(msgProcessor.getType(),msgProcessor);

}catch (Exception e){

LOG.error("加載腳本處理器:[{}]失敗:[{}]!",classItem.getName(),e.getMessage());

throw new RuntimeException("加載腳本處理器"+classItem.getName()+"失敗");

}

LOG.info("加載腳本處理器成功:[{}] MsgType:[{}] ", classItem.getName(), msgProcessor.getType());

}

LOG.info("腳本處理器加載完成!");

}

代碼中AppContext是對springContext 的封裝,實現(xiàn)了ApplicationContextAware接口,目的是從Spring容器取出指定類的實例。代碼見附錄1.

六、更進(jìn)一步

本文通過研究Java接口實現(xiàn)類的自動掃描加載,達(dá)成接口與調(diào)度程序的解耦,拓展了Java接口在代碼解耦方面的應(yīng)用價值。

雖然是獲取接口所有實現(xiàn)類,但對獲取類的所有子類,同樣適用。

另外基于此功能,可以通過反射分析掃描上來的Class, 獲知哪些類使用了自定義注解,然后應(yīng)用注解處理器,完成注解處理器的自動執(zhí)行。

七、最簡單的實現(xiàn)方法

ApplicationContext 的 getBeansOfType 方法已經(jīng)封裝了該實現(xiàn),可以直接調(diào)用。

AppContext 見附錄1

IProcessor 為接口。Map processorBeanMap 為返回值,key 為beanName ,value為 bean.

接口IProcessor實現(xiàn)類上 要加上注解@Component

Map processorBeanMap = null;

try {

processorBeanMap = AppContext.getContext().getBeansOfType(IProcessor.class);

}catch (BeansException e){

throw new RuntimeException(“加載腳本處理器Bean失敗!”);

}

調(diào)用AppContext 時,AppContex 必須已經(jīng)被容器優(yōu)先注入,否則可能會出現(xiàn)applicaitonContext未注入的報錯。

可以在調(diào)用類上,加上注解 @DependsOn(“appContext”) 來控制appContext 優(yōu)先加載。

八.借助ServiceLoader類

ServiceLoader是JDK自帶的一個類加載器,位于java.util包當(dāng)中,作為 A simple service-provider loading facility. 具體使用方式如下:

1.在META-INF/services/目錄下用你的接口全路徑名稱命名一個文件(不加后綴),然后在該文件中一行一個添加你的接口實現(xiàn)類的全路徑名。

2.通過load方法來加載出所有的接口實現(xiàn)類

附錄1 AppContext

package com.hikvision.hummer.pandora.common.context;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.stereotype.Component;

@Component

public class AppContext implements ApplicationContextAware {

private static ApplicationContext context = null;

/**

* 實現(xiàn)ApplicationContextAware接口的context注入函數(shù), 將其存入靜態(tài)變量

*/

public void setApplicationContext(ApplicationContext context) {

AppContext.setContext(context);

}

/**

* 取得存儲在靜態(tài)變量中的ApplicationContext.

*/

public static ApplicationContext getContext() {

if (context == null)

throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義AppContext");

return context;

}

/**

* 存儲靜態(tài)變量中的ApplicationContext.

*/

public static void setContext(ApplicationContext context) {

AppContext.context = context;

}

/**

* 從靜態(tài)變量ApplicationContext中取得Bean, 自動轉(zhuǎn)型為所賦值對象的類型

*/

@SuppressWarnings("unchecked")

public static T getBean(String name) {

if (context == null)

throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義AppContext");

try {

return (T) context.getBean(name);

} catch (BeansException e) {

e.printStackTrace();

}

return (T) null;

}

/**

* 從靜態(tài)變量ApplicationContext中取得Bean, 自動轉(zhuǎn)型為所賦值對象的類型

*/

@SuppressWarnings("unchecked")

public static T getBean(Class tClass) {

if (context == null)

throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義AppContext");

try {

return context.getBean(tClass);

} catch (BeansException e) {

e.printStackTrace();

}

return null;

}

}

當(dāng)前題目:獲取JAVA接口的所有實現(xiàn)類
網(wǎng)頁URL:http://chinadenli.net/article24/jsioce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、手機網(wǎng)站建設(shè)、微信公眾號、企業(yè)建站網(wǎng)站建設(shè)、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
极品少妇嫩草视频在线观看| 丰满少妇被粗大猛烈进出视频| 白白操白白在线免费观看 | 欧美日韩视频中文字幕| 国产日韩欧美国产欧美日韩| 午夜日韩在线观看视频| 国产不卡最新在线视频| 少妇人妻一级片一区二区三区| 亚洲国产中文字幕在线观看| 亚洲一区二区精品免费视频| 加勒比人妻精品一区二区| 99久久人妻中文字幕| 免费福利午夜在线观看| 欧美国产极品一区二区| 国产传媒免费观看视频| 亚洲精品黄色片中文字幕 | 国产一级一片内射视频在线| 九九热这里只有精品视频| 欧美一二三区高清不卡| 久久精品国产99精品亚洲| 福利在线午夜绝顶三级| 亚洲中文字幕高清乱码毛片| 欧美日韩久久精品一区二区| 亚洲二区欧美一区二区| 国产成人精品一区二区三区| 亚洲在线观看福利视频| 黄片美女在线免费观看| 欧美精品亚洲精品日韩专区| 激情五月天深爱丁香婷婷| 午夜亚洲少妇福利诱惑| 一区二区三区日韩中文| 好东西一起分享老鸭窝| 日韩精品综合免费视频| 欧美黑人巨大一区二区三区| 国产亚洲精品岁国产微拍精品| 国产91色综合久久高清| 亚洲国产精品久久琪琪| 中文字幕不卡欧美在线| 色婷婷国产熟妇人妻露脸| 亚洲美女国产精品久久| 中文字幕日韩欧美理伦片|