Dubbo-SPI机制详解
一 SPI展示
在正式讲解SPI机制之前,我们先根据Demo去演示Dubbo的SPI机制,它比Java自带的SPI机制强到哪里?
1.1 Java SPI机制
1)在类路径的resources路径下创建METE-INF/services目录并创建文件名称为接口全类路径:
2)在文件里添加接口实现类的全类路径:
com.lx.api.RedCar
com.lx.api.BlackCar
3)调用SPI服务,遍历输出
ServiceLoader<Car> load = ServiceLoader.load(Car.class);
Iterator<Car> iterator = load.iterator();
while (iterator.hasNext()){
Car next = iterator.next();
next.getCarName();
}
运行结果:
可以看出,Java的SPI是直接获取所有的接口实现,遍历执行,而我们如果想要指定要哪个接口实现类呢?那不是就没办法做了,而Dubbo就是对Java SPI的实现做了扩展,支持通过k,v方法获取对应的实现类,这就意味着我们在程序中可以按需加载,进行动态配置,大大提高代码的扩展性,而Dubbo之所以支持那么多协议,离不开自身对SPI机制的扩展,接下来我们一起对Dubbo的SPI机制进行实践吧。
1.2 Dubbo SPI机制
1)类路径的resources路径下创建METE-INF/dubbo目录并创建文件名称为接口全类路径:
2)在文件里添加接口实现类的全类路径,以K-V键值对的方式
red=com.lx.api.RedCar
black=com.lx.api.BlackCar
3)调用SPI服务,遍历输出
这里注意,使用DubboSPI机制接口需要添加SPI注解
@SPI
public interface Car {
/**
* 获取car名称
*/
void getCarName();
}
调用SPI服务
public static void main(String[] args) {
//获取Car接口的扩展类
ExtensionLoader<Car> carExtensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
//获取Red实现类实例
Car red = carExtensionLoader.getExtension("red");
red.getCarName();
Car black = carExtensionLoader.getExtension("black");
black.getCarName();
}
运行结果:
从使用方法可以看出,Dubbo的SPI机制更加灵活,除此之外Dubbo还提供了类似Spirng的依赖注入、AOP等功能,接下来我们再体验一把它的AOP功能。
4)定义一个包装类,也实现Car接口
public class CarWrapper implements Car {
private Car car;
public CarWrapper(Car car) {
this.car = car;
}
@Override
public void getCarName() {
System.out.println("我被包装了");
car.getCarName();
}
}
SPI调用:
public static void main(String[] args) {
ExtensionLoader<Car> carExtensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car red = carExtensionLoader.getExtension("red");
red.getCarName();
Car black = carExtensionLoader.getExtension("black");
black.getCarName();
}
运行结果:
很显然,我们每个实现类被增强了,一起调用了Wrapper类的增强逻辑。除了AOP之外,假如我们另一个接口中也使用了Car服务,那会不会进行依赖注入呢?我们再定义一个Person接口
@SPI
public interface Person {
Car getCar();
}
提供一个Person的实现类:
public class RedPerson implements Person {
private Car car; // Adaptive(代理)
public void setCar(Car car) {
this.car = car;
}
@Override
public Car getCar() {
return car;
}
}
假设我们的RedPerson内部想注入RedCar并调用对应的方法,Dubbo SPI是如何实现呢?
我们修改下Car接口的方法,并提供一个URL参数,这是Dubbo内部实现的,必须要传入,且对应方法需要添加@Adaptive注解
@SPI
public interface Car {
/**
* 获取car名称
*/
@Adaptive
void getCarName(URL url);
}
SPI服务调用
public static void main(String[] args) {
ExtensionLoader<Person> personExtensionLoader = ExtensionLoader.getExtensionLoader(Person.class);
Person redPerson = personExtensionLoader.getExtension("red");
URL url = new URL("x", "localhost", 8080);
url = url.addParameter("car", "red");
redPerson.getCar().getCarName(url);
}
运行结果:
是不是很神奇,我们的RedPerson内部注入了RedCar,并调用了对应的getCarName方法,这都是Dubbo SPI的强大之处,所以我们需要对其底层源码做一个探索。
二 SPI原理
根据上边的Demo,我们直到Dubbo的SPI的ExtensionLoader起了很重要的左右,我们就首先从ExtensionLoader这个类开始分析:
2.1 ExtensionLoader
ExtensionLoader表示某个接口的扩展点加载器,可以用来加载某个扩展点实例。
在ExtensionLoader中,还几个非常重要的属性:
//存放扩展点加载器和扩展点接口的映射
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap();
//存放扩展点接口实现类实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap();
//表示当前ExtensionLoader实例是哪个接口的扩展点加载器
private final Class<?> type;
//扩展点工厂(对象工厂),可以获得某个对象
private final ExtensionFactory objectFactory;
//存放的是扩展点接口实现类的名称key和对应的class映射关系
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
//Dubbo SPI扩展点文件存放路径
private static final String SERVICES_DIRECTORY = "META-INF/services/";
//Dubbo SPI扩展点文件存放路径
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
//Dubbo SPI扩展点文件存放路径
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
ExtensionLoader和ExtensionFactory的区别在于:
- ExtensionLoader最终所得到的对象是Dubbo SPI机制产生的
- ExtensionFactory最终所得到的对象可能是Dubbo SPI机制所产生的,也可能是从Spring容器中所获得的对象
在ExtensionLoader中有三个常用的方法:
- getExtension(“dubbo”):表示获取名字为dubbo的扩展点接口实现类实例
- getAdaptiveExtension():表示获取一个自适应的扩展点实例
- getActivateExtension(URL url, String[] values, String group):表示一个可以被url激活的扩展点实例。
其中,什么是自适应扩展点实例?它其实就是当前这个接口的一个代理对象
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol protocol = extensionLoader.getExtension("dubbo");
当我们调用上述代码,我们会将得到一个DubboProtocol的实例对象,但在getExtension()方法中,Dubbo会对DubboProtocol对象进行依赖注入(也就是自动给属性赋值,属性的类型为一个接口,记为A接口),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进行依赖注入时该找一个什么的的扩展点对象给这个属性,这时就会预先赋值一个A接口的自适应扩展点实例,也就是A接口的一个代理对象。
后续,在A接口的代理对象被真正用到时,才会结合URL信息找到真正的A接口对应的扩展点实例进行调用。
2.2.1 创建扩展点类加载器(getExtensionLoader)
ExtensionLoader<Person> personExtensionLoader = ExtensionLoader.getExtensionLoader(Person.class);
调用这行代码的时候,就是为我们创建一个扩展点接口的加载器,我们看下内部实现
ExtensionLoader.getExtensionLoader(扩展点接口.class)
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
} else if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
} else if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
} else {
ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
}
return loader;
}
}
很显然,前三个判断就是我们当前必须是一个接口,并且接口上有SPI注解修饰,否则不会帮我们创建一个扩展点加载器。经过校验后,通过内部的缓存Map中查到,如果查到了就直接返回,如果没查到则new出来一个,放在缓存中并返回,注意这里new的时候把扩展点接口类对象传入赋值给ExtensionLoader的type变量。
2.2.2 获取实例(createExtension)
根据配置文件的key获取对应的实例
getExtension(String name)
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
} else if ("true".equals(name)) {
//返回默认的扩展点实现
return this.getDefaultExtension();
} else {
Holder<Object> holder = this.getOrCreateHolder(name);
//DCL保证单例
Object instance = holder.get();
if (instance == null) {
synchronized(holder) {
instance = holder.get();
if (instance == null) {
instance = this.createExtension(name);
holder.set(instance);
}
}
}
return instance;
}
}
这里有一个扩展点,我们的key可以传一个true,当传入true的时候,我们可以在接口的SPI主键指定key作为默认扩展点实现,这样在这一步就会调用getDefaultExtension()方法直接创建对应的默认扩展点实现对象返回。这不是主分支,先往下进入else接着看
//获取一个hold对象
Holder<Object> holder = this.getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized(holder) {
instance = holder.get();
if (instance == null) {
//真正创建实现类实例的地方
instance = this.createExtension(name);
holder.set(instance);
}
}
}
//getOrCreateHolderc创建一个hold对象并放在缓存中
private Holder<Object> getOrCreateHolder(String name) {
Holder<Object> holder = (Holder)this.cachedInstances.get(name);
if (holder == null) {
this.cachedInstances.putIfAbsent(name, new Holder());
holder = (Holder)this.cachedInstances.get(name);
}
return holder;
}
这里先获得一个hold对象,为什么要把实例对象通过hold包装一层呢?因为下边用了一个DCL,我们不可能使用同一个锁对象去上锁,比如我这个扩展点接口有两个实现类,我现在只是为了获取其中一个实现类的单例,那就没必要用同一把锁住所有实现类实例的创建,而是通过一个holder包装,一个实现类的实例创建之间进行互斥。接着我们继续看createExtension方法吧
在调用createExtension(String name)方法去创建一个扩展点实例时,要经过以下几个步骤:
- 根据name找到对应的扩展点实现类(这一步要解析配置文件)
- 根据实现类生成一个实例,把实现类和对应生成的实例进行缓存
- 对生成出来的实例进行依赖注入(给实例的属性进行赋值)
- 对依赖注入后的实例进行AOP(Wrapper),把当前接口类的所有的Wrapper全部一层一层包裹在实例对象上,每包裹个Wrapper后,也会对Wrapper对象进行依赖注入
- 返回最终的Wrapper对象
主题代码:
private T createExtension(String name) {
//根据name找到对应的扩展点实现类对象
Class<?> clazz = (Class)this.getExtensionClasses().get(name);
if (clazz == null) {
throw this.findException(name);
} else {
try {
T instance = EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//根据实现类生成一个实例,把**实现类和对应生成的实例**进行缓存
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = EXTENSION_INSTANCES.get(clazz);
}
this.injectExtension(instance);
Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
Class wrapperClass;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
wrapperClass = (Class)var5.next();
}
}
this.initExtension(instance);
return instance;
} catch (Throwable var7) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " + this.type + ") couldn't be instantiated: " + var7.getMessage(), var7);
}
}
}
第一步:根据name获取对应的扩展点实现类Class对象
Class<?> clazz = (Class)this.getExtensionClasses().get(name);
private Map<String, Class<?>> getExtensionClasses() {
//缓存中是否存储
Map<String, Class<?>> classes = cachedClasses.get();
//DCL
if (classes == null) {
synchronized (cachedClasses) {
//再拿一次
classes = cachedClasses.get();
if (classes == null) {
//解析配置文件,获取扩展点类的实现类
classes = loadExtensionClasses();
//放在缓存中
cachedClasses.set(classes);
}
}
}
return classes;
}
loadExtensionClasses 解析配置文件
private Map<String, Class<?>> loadExtensionClasses() {
//设置默认的扩展点实现类名称
cacheDefaultExtensionName();
//解析配置文件,map存储的是所有的扩展点接口的是实现类
Map<String, Class<?>> extensionClasses = new HashMap<>();
// internal extension load from ExtensionLoader's ClassLoader first
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
根据加载文件路径加上扩展点接口的全类路径进行文件解析:
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
//设置文件名
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
//获取类加载器
ClassLoader classLoader = findClassLoader();
//不会走的分支
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
//加载路径
if(urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
//迭代路径
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
//加载类
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
在这里主要获取类加载器以及实现类的URL,然后遍历URL调用loadResource方法通过类加载器获取实现类的Class对象
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) {
line = line.substring(0, ci);
}
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
//key
name = line.substring(0, i).trim();
//value
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//根据类路径加载文件
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
这里就是解析我们的URL,我们文件内容都是K,V键值对的,这里获取到v的值通过类加载器调用loadClass方法进行类的加载
//根据类路径加载文件
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
//当前类是否手工指定了Adaptive类,就把它缓存起来
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
//当前类是否是一个包装类,是的话就缓存起来
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
//如果都不是,调用构造方法。
clazz.getConstructor();
//如果文件中没有指定key,但是通过Extension注解指定了key
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
//缓存一下被Activate注解标注的类
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
//把实现类和key放入map中
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
在loadClass方法中主要做了几件事
1.缓存手工指定Adaptive类,注意这里如果没有手工指定,不缓存,后边Dubbo会为我们自动生成一个代理类进行依赖注入
2.缓存包装类
3.把扩展类接口实现类Class对象放到map中,和key形成映射关系
至此我们的文件解析结束。
第二步:实例化对象,并放入缓存中
我们重新回到createExtension方法继续往下走,经过第一步getExtensionClasses().get(name),我们可以拿到实现类的Class对象,然后就可以进行反射操作创建对象了
//先从缓存中获取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
//如果没有则通过反射获取,并放到缓存中去
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
第三步:依赖注入
实例化对象后会调用injectExtension(instance)方法,这一步就是进行依赖注入的,我们看看代码逻辑
private T injectExtension(T instance) {
//对象工厂是空的直接返回
if (objectFactory == null) {
return instance;
}
try {
//遍历实现类的方法
for (Method method : instance.getClass().getMethods()) {
//不是setXX方法直接跳出
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
//添加DisableInject注解则不允许注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
//拿到set方法的参数Class对象
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
//获取setXxx方法中的Xxx
String property = getSetterProperty(method);
//根据参数类型或属性名,从objectFactory中获取到对象,然后调用set方法进行注入
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
在injectExtension方法中主要是进行依赖注入,如果方法中有set方法,便会尝试从工厂中调用getExtension方法拿到一个该类型的实例进行属性填充,这里有一个扩展点就是如何通过objectFactory获取参数对象,默认实现有SPI或者Spring容器中,
SPI获取对象:
@Override
public <T> T getExtension(Class<T> type, String name) {
// 接口上存在SPI注解
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
//获取要注入的扩展点接口的加载器
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
//通过加载器获取一个自适应代理对象
return loader.getAdaptiveExtension(); // 接口的Adaptive类(代理对象)
}
}
return null;
}
我们再看getAdaptiveExtension方法
public T getAdaptiveExtension() {
//先从缓存中拿
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
//DCL双重检查
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//创建一个自适应代理对象
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
基于DCL的单例模式,在createAdaptiveExtension去创建自适应代理对象,然后再返回给依赖注入的地方
private T createAdaptiveExtension() {
try {
//有可能我这个要注入的对象也需要注入其他对象,所以这里再次进行依赖注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
接着看getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 获取当前接口的所有扩展类
getExtensionClasses(); //
// 缓存了@Adaptive注解标记的类
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 如果某个接口没有手动指定一个Adaptive类,那么就自动生成一个Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
调用createAdaptiveExtensionClass方法真正创建一个Apaptive类
private Class<?> createAdaptiveExtensionClass() {
// cachedDefaultName表示接口默认的扩展类
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
在这里我们是生成了一个代理类,加载到内存中,返回了该代理类的Class对象,至此我们SPI形式的依赖注入完成。
Spring注入方法:
// 从Spring容器中获取bean
// 先根据name拿,再根据类型拿
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//SPI should be get from SpiExtensionFactory
// 如果接口上存在SPI注解,就不从spring中获取对象实例了
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
// 从ApplicationContext中获取bean, byname
for (ApplicationContext context : CONTEXTS) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
if (Object.class == type) {
return null;
}
// byType
for (ApplicationContext context : CONTEXTS) {
try {
return context.getBean(type);
} catch (NoUniqueBeanDefinitionException multiBeanExe) {
logger.warn("Find more than 1 spring extensions (beans) of type " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension of that type.");
} catch (NoSuchBeanDefinitionException noBeanExe) {
if (logger.isDebugEnabled()) {
logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
return null;
}
第四步:AOP
重新回到createExtension方法中,拿到缓存起来的包装类cachedWrapperClasses,这个在解析文件的时候就会判断,如果存在的话对会使用包装类的对象返回,当然如果包装类也需要进行依赖注入的话先进行依赖注入再返回。
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}