類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
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...