时间:2021-05-20
首先来看下在 Java 中对于泛型类型,比如这样简单的类定义
class Processor<T> {}如果直接初始化时要指定具体类型的话,我们可以这么写
Processor<String> processor = new Processor<>(); //Java 7 及以上版本Spring 对基本泛型的初始化
如果我们要用 Spring 容器来初始化这个类,比如给上面那个类加个 @Named 注解
@Namedclass Processor<T> {}这时候我们通过 beanFactory.getBean(Processor.class) 得到的是一个什么样的实例呢?Spring 怎么知道要指定什么具体类型呢?很简单,任何不确定的情况都是 Object。所以通过容器得到的 Processor 实例相当于用下面代码构造出来的
Processor processor = new Processor(); //更准确来讲是 Processor<Object> processor = new Processor<>();再进一步,对于有上限约束的泛型定义,Spring 才如何应对呢?像
@Namedclass Processor<T extends Number> {}类似的,class Processor<T> 相当于 class Processor<T extends Object> , 因此 Spring 在具体类型未明的情况下也是要用最顶层可接受类型,Spring 将会针对上面的代码实例出下面的对象
Processor<Number> processor = new Processor<>();再复杂一些,泛型的子类型仍然是泛型的情况,如下代码
首先定义了一个泛型接口
public interface Service<T> { String process(T t);}然后要求 Spring 容器来初始下面的 NumberService 实例
@Namedpublic class NumberService<R extends Number> implements Service<R> { @Override public String process(R number) { return "Process Number: " + number; }}Spring 在初始化 NumberService 实例同样是要取用最顶层可接受类型,通过下面的代码来初始化
NumberService<Number> numberService = new NumberService<>();再终极一些,泛型类型并且类型也是泛型的,Spring 该如何拿捏?
@Namedpublic class Processor<T> { @Inject Private Service<T> service;}此时 Spring 该如何确定上面的类型 T 呢?因为有了 Service<T> service 属性的存在而不能再笼统的想像 Spring 会采用下面的代码来初始化 Processor 实例
Processor<Object> processor = new Processor<>();而是 Processor 的具体类型必须通过被注入的 Service<T> 实例的具体类型来推断的,这就取决于在 Spring 容器中存在什么样的 Service<T> 实例。举两个例子
如果 Spring 中有初始化
@Namedpublic class StringService implements Service<String> { @Override public String process(String string) { return "Process String: " + string; }}那么前面的 Processor<T> 实例就相当于
Processor<String> processor = new Processor<>();processor.service = new StringService();如果 Spring 中初始化的 Service<T> 是前面那个 NumberService<R extends Number> implements Service<R> , 那么 Spring 容器中的 Processor<T> 实例相当于
Processor<Number> processor = new Processor<>();processor.service = new NumberService<Number>();那如果前面的 NumberService 和 StringService 同时在 Spring 容器中注册了呢?Spring 同样要为难了,在没有 @Primary 的情况下无法确定使用哪个实例来注入 Service<T> service 属性了,出现类似错误
2016-12-09 00:56:50.922 WARN 4950 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'processor': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cc.unmi.Service<?>' available: expected single matching bean but found 2: numberService,stringService2016-12-09 00:56:50.941 ERROR 4950 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :***************************APPLICATION FAILED TO START***************************Description:Field service in cc.unmi.Processor required a single bean, but 2 were found: - numberService: defined in file [/Users/Yanbin/Workspaces/github/spring-generic-demo/target/classes/cc/unmi/NumberService.class] - stringService: defined in file [/Users/Yanbin/Workspaces/github/spring-generic-demo/target/classes/cc/unmi/StringService.class]Action:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed这和普通属性的注入时有多个可选实例时是一样的错误。
总结一下
如果 Spring 在初始化泛型类时,未提供任何具体类型则会采用最上限的类型来初始化实例
如果泛型类型与被注入的属性的具体类型有关联,则由属性类型推断出主类型
@Named class Processor<T> { @Inject Service<T> service;}此时 Spring 容器中存在 class StringService implements Service<String> 的实例,则会由属性 service(StringService 实例) 推断出 Processor 的具体类型是 Processor<String>
当然这个 Processor 类也是可以定义的稍复杂一些,如
@Named class Processor<T extends Number> { @Inject Service<T> service;}关于本文的示例代码可参考 https://github.com/yabqiu/spring-generic-demo, 请运行 mvn spring-boot:run 查看输出结果来理解 Spring 怎么去初始化泛型类实例的。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者使用Spring能带来一定的帮助,如果有疑问大家可以留言交流。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文接上文“java反射之方法反射的基本操作方法”,利用反射了解下java集合中泛型的本质1、初始化两个集合,一个使用泛型,一个不使用ArrayListlist
初始化类,结构和枚举当Swift声明后准备初始化类实例。初始值被初始化为存储属性,并且新的实例的值也被进一步进行初始化。创建初始化函数的关键字是通过init()
与Objective-C不同,Swift的初始化方法需要保证类型的所有属性都被初始化。所以初始化方法的调用顺序就很有讲究。在某个类的子类中,初始化方法里语句的顺
记住:通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可
一、类的初始化对于类的初始化:类的初始化一般只初始化一次,类的初始化主要是初始化静态成员变量。类的编译决定了类的初始化过程。编译器生成的class文件主要对定义