類spring ioc 泛型保留

来源:https://www.cnblogs.com/loveheihei/archive/2019/08/22/11397506.html
-Advertisement-
Play Games

類spring ioc 泛型保留 什麼是泛型擦除 Java並不會傳遞泛型類,舉個直觀的慄子: 這裡 嘗試列印泛型類型, 泛型指定了 類,來個測試看看 是否能被獲取到? 依賴腳本build.gradle 運行可以看到結果是,spring ioc並不能註入獲取泛型 自定義IOC泛型註入 在解決sprin ...


類spring ioc 泛型保留

什麼是泛型擦除

Java並不會傳遞泛型類,舉個直觀的慄子:

@Component
public class BaseProvider<T>
{ 
    public void doSomething()
    { 
        Class<?> clazz = getClass();
        System.out.println(clazz.getName()+" doSomething");
        Type superclass = clazz.getGenericSuperclass();
        System.out.println("superclass="+superclass);
    }
}

這裡doSomething嘗試列印泛型類型,

@Component
public class TestService
{
    @Autowired
    private BaseProvider<User> userProvdier; 
    public void doSomething()
    {
        userProvdier.doSomething();
    } 
}

BaseProvider<User>泛型指定了User類,來個測試看看User是否能被獲取到?

 @ComponentScan 
public class Main
{
    public static void main(String[] args)
    { 
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 
        TestService service = context.getBean(TestService.class);
        System.out.println("service=" + service);
        service.doSomething();
    }
}

依賴腳本build.gradle

dependencies {
    String springVersion="5.1.9.RELEASE"
    implementation "org.springframework:spring-context:$springVersion" 
}

運行可以看到結果是,spring ioc並不能註入獲取泛型

service=TestService@a68df9
BaseProvider doSomething
superclass=class java.lang.Object

自定義IOC泛型註入

在解決spring泛型問題之前,先做一個簡單的IOC泛型註入框架,來瞭解其原理

    TestService service = Context.getBean(TestService.class);
}

這裡將自寫一個Context類,理解為上下文也好,BeanFactory也好,其原理不過是創建管理對象的東西

public class Context
{
    public static <T> T getBean(Class<T> clazz)
    {
        return createBean(clazz, null, null, null);
    }
    private static <T> T createBean(Class<T> clazz, Type type, Object target, Field source)
    {
        //...
    }
    //....
}

設計createBean創建bean對象,依賴於bean類型,泛型類型,上級調用對象,來源位置(欄位或方法)。再一個緩存bean對象,基本功能:

private static final Map<String, Object> beanCache = new HashMap<>();
@SuppressWarnings("unchecked")
private static <T> T createBean(Class<T> clazz, Type type, Object target, Member source)
{
    try
    {
        String beanName = getBeanName(clazz, type, target, source);
        if (beanCache.containsKey(beanName))
        {
            return (T) beanCache.get(beanName);
        }
        if (type != null && type instanceof ParameterizedType)
        {
                //創建泛型對象代理類
        }
        Constructor<T> cts = clazz.getDeclaredConstructor();
        T obj = cts.newInstance();// 創建object
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields)
        {
            if (field.getAnnotation(Autowried.class) != null)
            {
                setField(field, obj, createBean(field.getType(), field.getGenericType(), obj, field));
            }
        }
        beanCache.put(beanName, obj);
        return obj;
    } catch (Exception e)
    {
        e.printStackTrace();
    }
    return null;
}
private static String getBeanName(Class<?> clazz, Type type, Object target, Member source)
{ 
    if (type != null && type instanceof ParameterizedType)
    {
        ParameterizedType pt = (ParameterizedType) type;
        StringJoiner sb = new StringJoiner("_");
        for (Type p : pt.getActualTypeArguments())
        {
            sb.add(p.getTypeName().replaceAll("\\.", "_"));
        }
        return clazz.getName() + "_$_" + sb.toString() + "_Proxy";
    }
    return clazz.getName();
}

getBeanName規定了beanname的生成規則,createBean中創建泛型代理的部分,這裡使用javassist去生成動態代理類

  implementation 'org.javassist:javassist:3.25.0-GA' 

生成泛型子類

ClassPool pool = ClassPool.getDefault();
ParameterizedType pt = (ParameterizedType) type; 
CtClass subClass = pool.get(clazz.getName());
StringJoiner genericSignature=new StringJoiner(",","L"+getClassPath(subClass.getName())+"<",">;");
Type[] ptts = pt.getActualTypeArguments(); 
for(int i=0;i<ptts.length;i++) { 
    genericSignature.add("L"+getClassPath(ptts[i].getTypeName())+";");
} 
CtClass proxyClass = pool.makeClass( beanName,subClass);  
proxyClass.setGenericSignature( genericSignature.toString());   
clazz = (Class<T>) proxyClass.toClass();

getClassPath替換classname為class路徑

private static String getClassPath(String name)
{
    return name.replaceAll("\\.", "/");
}

再來看看運行效果

service=TestService@5d6f64b1
BaseProvider_$_User_Proxy doSomething
superclass=BaseProvider<User>

泛型<User>能夠獲取出來

為什麼要泛型註入

泛型註入的應用場景,Java獲取泛型一直是個很頭疼的是,如果能夠輕鬆獲取泛型,就能夠減少大量的代碼。舉個慄子,寫一個常用的資料庫操作工具類,不能獲取泛型情況下,就必須傳入Class<T> beanClazz參數:

public class DbUtil<T>{
    List<T> getAll(Class<T> beanClazz){
        //....
    }
}

思考

方法的泛型應該如何去獲取?

預告

將在下篇文章中講解如何在spring 中解決泛型問題


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 一> 本書目的。 這是一本思想層面的書,主要是向讀者展示,專業程式員是如何面向對象編程的?設計師是如何面向設計編 程的?逐步引導讀者從控制項編程到對象編程再到業務設計。 二>內容結構。 同事跟我說過一句話,所謂門坎,跨過了就是門,跨不過就是坎。在介紹本書內容之前,先帶領大家瞭解一下從拖拉控制項編程 ...
  • 1. SqlSessionFactory 與 SqlSession. 通過前面的章節對於mybatis 的介紹及使用,大家都能體會到SqlSession的重要性了吧, 沒錯,從錶面上來看,咱們都是通過SqlSession去執行sql語句(註意:是從錶面看,實際的待會兒就會講)。那麼咱們就先看看是怎麼 ...
  • <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.d... ...
  • 作為編程語言屆的老大哥,學習JAVA的人數不勝數,在這裡分享一些學習JAVA的技巧以及方法,當然,這些技巧及方法使用範圍包含但不限於JAVA. ① 筆記軟體 印象筆記:多端互通很方便(https://www.yinxiang.com/) 應用場景(只描述編程學慣用的到的部分): eDiary: 一款 ...
  • 前後端分離的工作模式於今是非常流行了,前後端工作的對接,就離開不了API文檔的輔助。 根據自己以往的工作經歷,以及瞭解的一些資訊,API文檔的建立,無非以下幾種方式: 1. word文檔模板 2. 第三方平臺,類如postman、showdoc等 3. 框架內單獨自定義一套綁定路由的結構,再... ...
  • 一、線程常用屬性 1.threading.currentThread:返回當前線程變數 2.threading.enumerate:返回一個包含正在運行的線程的list,正在運行的線程指的是線程啟動後,結束前的狀態 3.threading.activeCount:返回正在運行的線程數量,效果跟len ...
  • > **微信公眾號【Java技術江湖】一位阿裡 Java 工程師的技術小站。(關註公眾號後回覆”Java“即可領取 Java基礎、進階、項目和架構師等免費學習資料,更有資料庫、分散式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送作者原創的Java學習指南、Java程式員面試指南等 ...
  • 前段時間和室友一起給某個公司做了一個管理系統,每個人分2W多。這裡和大家分享一下做完項目後一點點感受,想到啥就說點啥。 核心競爭力 兩個月就掙了2W塊,掙了我爸媽兩個人一年的收入,每天還賊辛苦,披星戴月的感覺,我還沒睡醒,我爸媽就出去大早上賣菜去了,等我睡醒了,還沒有回來(你站在別動,我去買個橘子) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...