SpringFactoriesLoader類作用詳解
SpringFactoriesLoader類
介紹
SpringFactoriesLoader類的主要作用是通過類路徑下的META-INF/spring.factories文件獲取工廠類接口的實現類,初始化并保存在緩存中,以供Springboot啟動過程中各個階段的調用。Spring的自動化配置功能,也與此息息相關。
SpringFactoriesLoader 工廠加載機制是 Spring 內部提供的一個約定俗成的加載方式,只需要在模塊的 META-INF/spring.factories 文件中,以 Properties 類型(即 key-value 形式)配置,就可以將相應的實現類注入 Spirng 容器中。
Properties類型格式:
key:value
- key:是全限定名(抽象類|接口)
- value:是實現類,多個實現類通過逗號進行分割
spring boot類路徑下: META-INFO/spring.factories
方法
返回值 | 方法 | 描述 |
<T>List<T> | loadFactories(Class<T> factoryType,@Nullable ClassLoader classLoader) | 靜態(tài)方法 根據接口獲取其實現類的實例 該方法返回的是實現類對象列表 |
List<String> | loadFactoryNames(Class<?>) factoryType,@Nullable ClassLoader classLoader) | 公共靜態(tài)方法 根據接口獲取其實現類的名稱 該方法返回的是實現類的類名的列表 |
public final class SpringFactoriesLoader { //文件位置,可以存在多個JAR文件中 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; //用來緩存MultiValueMap對象 private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>(); private SpringFactoriesLoader() { } /** * 根據給定的類型加載并實例化工廠的實現類 */ public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null"); //獲取類加載器 ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } //加載類的全限定名 List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } //創(chuàng)建一個存放對象的List List<T> result = new ArrayList<>(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { //實例化Bean,并將Bean放入到List集合中 result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } //對List中的Bean進行排序 AnnotationAwareOrderComparator.sort(result); return result; } /** * 根據給定的類型加載類路徑的全限定名 */ public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { //獲取名稱 String factoryTypeName = factoryType.getName(); //加載并獲取所有META-INF/spring.factories中的value return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //根據類加載器從緩存中獲取,如果緩存中存在,就直接返回,如果不存在就去加載 MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { //獲取所有JAR及classpath路徑下的META-INF/spring.factories的路徑 Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); //遍歷所有的META-INF/spring.factories的路徑 while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); //將META-INF/spring.factories中的key value加載為Prpperties對象 Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { //key名稱 String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { //以factoryTypeName為key,value為值放入map集合中 result.add(factoryTypeName, factoryImplementationName.trim()); } } } //放入到緩存中 cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } //實例化Bean對象 @SuppressWarnings("unchecked") private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) { try { Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader); if (!factoryType.isAssignableFrom(factoryImplementationClass)) { throw new IllegalArgumentException( "Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]"); } return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException( "Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]", ex); } } }
測試
resources下新建META-INF/spring.factories
com.moming.service.IStudentService=\
com.moming.service.impl.StudentServiceImpl1,\
com.moming.service.impl.StudentServiceImpl2
創(chuàng)建業(yè)務層接口及實現類
service/IStudentService
service/impl/StudentServiceImpl1
service/impl/StudentServiceImpl2
測試
@SpringBootApplication public class App{ public static void main(String[] args) { //SpringApplication.run(App.class, args); List<String> names = SpringFactoriesLoader.loadFactoryNames(IStudentService.class, ClassUtils.getDefaultClassLoader()); for (String name : names) { System.out.println(name); } System.out.println("==================================="); List<IStudentService> iStudentServices = SpringFactoriesLoader.loadFactories(IStudentService.class, ClassUtils.getDefaultClassLoader()); for (IStudentService iStudentService : iStudentServices) { System.out.println(iStudentService); System.out.println(iStudentService.game()); } } }
通過以上可以證明,SpringFactoriesLoader會尋找jar包中配置META-INF下的spring.factories配置文件相應Key的value,并根據需要實例化
到此這篇關于SpringFactoriesLoader類作用詳解的文章就介紹到這了,更多相關SpringFactoriesLoader內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java文件下載設置中文名稱的實例(response.addHeader)
下面小編就為大家分享一篇java文件下載設置中文名稱的實例(response.addHeader),具有很好的參考價值,希望對大家有所幫助2017-12-12