Dubbo 확장 SPI 소스 분석

106677 단어 Dubbo분포 식자바
제목: 글 내용 출력 원: 라 체크 교육 자바 고 임금 훈련 소.이 글 은 Dubbo 학습 과정의 일부분 이다.
Dubbo 확장 SPI 소스 분석
SPI 는 이전에 사용 한 적 이 있 는데 그 중에서 가장 중요 한 종 류 는 Extension Loader 입 니 다. 모든 Dubbo 에서 SPI 의 입구 입 니 다.org. apache. dubbo. comon. extension. Extension Loader. getExtension Loader 와 org. apache. dubbo. comon. extension. Extension Loader. getExtension 방법 을 구체 적 으로 소개 합 니 다.getExtensionLoader 는 확장 점 로 더 를 가 져 오고 해당 하 는 모든 확장 점 을 불 러 옵 니 다. getExtension 은 name 에 따라 확장 점 을 가 져 옵 니 다.
1. getExtensionLoader 로 딩 과정
(1) ExtensionLoader 를 어떻게 예화 하 는 지
private static <T> boolean withExtensionAnnotation(Class<T> type) {
      
	//   `@SPI`       
	return type.isAnnotationPresent(SPI.class); 
}
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
      
	//        
	if (type == null) {
      
		throw new IllegalArgumentException("Extension type null"); 
	}
	//         
	if (!type.isInterface()) {
      
		throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); 
	}
	//     SPI    
	if (!withExtensionAnnotation(type)) {
      
		throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); 
	}
	//                (    ) 
	ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 
	if (loader == null) {
      
		//       ,       ,          
		EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 
		loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 
	}
	return loader; 
}

(2) ExtensionLoader 의 구조 기 함 수 를 구체 적 으로 살 펴 보면 그의 실현 은 비교적 간단 하고 많은 조작 을 하지 않 았 다.주로 type 에 값 을 부여 하고 ExtensionFactory 대상 을 가 져 옵 니 다.
private ExtensionLoader(Class<?> type) {
      
	this.type = type; 
	//                 ,                 
	objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); 
}

(3) Extension Factory 가 무엇 에 사용 되 는 지 구체 적 으로 살 펴 보 자. 여기 서 우 리 는 그 가 확장 점 유형 과 진정한 이름 을 입력 하여 확장 을 얻 는 것 을 대충 알 수 있다.여 기 는 우리 SPI 의 구체 적 인 명칭 실현 과 연 결 됩 니 다.
@SPI 
public interface ExtensionFactory {
      
	/**
	* Get extension. 
	* @param type object type. 
	* @param name object name. 
	* @return object instance. 
	*/ 
	<T> T getExtension(Class<T> type, String name); 
}

(4) dubbo - common / src / main / resources / META - INF / dubbo / internal / org. apache. dubbo. comon. extension. Extension Factory 에서 볼 수 있 습 니 다. 그 는 기본적으로 세 가지 실현 가능 한 제공 이 있 습 니 다.
spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory 
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory 
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory

(5) Adaptive Extension Factory 에서 @ Adaptive 로 표 시 된 것 을 볼 수 있 습 니 다.여기 서 유형 명 을 통 해 알 수 있 듯 이 그의 가장 중요 한 역할 은 다른 Extension Factory 를 대리 하 는 것 이다.그 중에서 가장 중요 한 방법 은 getSupported Extensions 방법 으로 모든 지원 하 는 확장 정 보 를 얻 는 것 입 니 다.
@Adaptive 
public class AdaptiveExtensionFactory implements ExtensionFactory {
      
	private final List<ExtensionFactory> factories; 
	public AdaptiveExtensionFactory() {
      
		//     ExtensionFactory      
		ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); 
		List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); 
		//         
		for (String name : loader.getSupportedExtensions()) {
      
			//     ExtensionFactory     
			list.add(loader.getExtension(name)); 
		}
		factories = Collections.unmodifiableList(list); 
	}
	@Override 
	public <T> T getExtension(Class<T> type, String name) {
      
		for (ExtensionFactory factory : factories) {
      
			//        ExtensionFactory    
			T extension = factory.getExtension(type, name); 
			if (extension != null) {
      
				return extension; 
			} 
		}
		return null; 
	} 
}

(6) 지원 하 는 모든 확장 정 보 를 가 져 옵 니 다. ExtensionLoader. getSupported Extensions. 여기 서 볼 수 있 습 니 다. 사실 가장 중요 한 방법 은 getExtensionClasses 방법 입 니 다.
public Set<String> getSupportedExtensions() {
      
	//            
	Map<String, Class<?>> clazzes = getExtensionClasses(); 
	//            
	return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet())); 
}

(7) getExtensionClasses 의 실현 을 관찰 하면 여기 서 알 수 있 듯 이 주로 하 는 일 은 중복 로드 를 방지 하 는 것 이기 때문에 진정한 실현 은 loadExtensionClasses 방법 을 전문 적 으로 살 펴 봐 야 한다.확장 클래스 를 이름 으로 가 져 오기 전에 확장 클래스 이름 에서 확장 클래스 까지 의 맵 관계 표 classes 를 설정 파일 에 따라 분석 한 다음 확장 항목 이름 에 따라 맵 관계 표 에서 해당 하 는 확장 클래스 를 가 져 오 면 됩 니 다.관련 프로 세 스 코드 분석 은 다음 과 같다.
private Map<String, Class<?>> getExtensionClasses() {
      
	//               
	Map<String, Class<?>> classes = cachedClasses.get(); 
	//      
	if (classes == null) {
      
		//     ,   ,          
		synchronized (cachedClasses) {
      
			classes = cachedClasses.get(); 
			if (classes == null) {
      
				//              
				classes = loadExtensionClasses(); 
				cachedClasses.set(classes); 
			} 
		} 
	}
	return classes; 
}

(8) loadExtensionClasses 방법 을 관찰 하여 실현 한다.이곳 에 서 는 주로 두 가지 일 을 했다.1: 현재 SPI 의 기본 값 을 불 러 옵 니 다.2: 이 종류의 모든 확장 점 을 불 러 오고 name 과 Class 대상 의 형식 으로 저장 합 니 다.다음은 cache Default Extension Name 과 loadDirectory 방법 에 대한 설명 입 니 다.
private Map<String, Class<?>> loadExtensionClasses() {
      
	//             
	cacheDefaultExtensionName(); 
	//                 classes 
	//                 
	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; 
}

cache Default Extension Name 방법 을 관찰 합 니 다.이 안 은 실행 이 비교적 간단 합 니 다. 주로 주석 에서 value 값 을 읽 어서 기본 이름 을 가 져 오 는 데 사 용 됩 니 다.
private void cacheDefaultExtensionName() {
      
	//          SPI  ,                
	final SPI defaultAnnotation = type.getAnnotation(SPI.class); 
	if (defaultAnnotation == null) {
      
		return; 
	}
	//      value ,             SPI        
	//   LoadBalance       random。            
	String value = defaultAnnotation.value(); 
	if ((value = value.trim()).length() > 0) {
      
		String[] names = NAME_SEPARATOR.split(value); 
		if (names.length > 1) {
      
			throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); 
		}
		if (names.length 1) {
      
			cachedDefaultName = names[0]; 
		} 
	} 
}

loadDirectory 방법 을 관찰 하여 실현 합 니 다.이 폴 더 에서 실제 파일 목록 을 찾 고 그 중의 파일 내용 을 분석 하여 extensionClasses Map 에 넣 는 것 이 주요 기능 입 니 다. 파일 의 내용 을 구체 적 으로 분석 하고 loadResource 를 참고 하여 실현 해 야 합 니 다.
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
      
	//       :   /  .    
	String fileName = dir + type; 
	try {
     
		//   classloader url   
		Enumeration<java.net.URL> urls = null; 
		ClassLoader classLoader = findClassLoader(); 
		// try to load from ExtensionLoader's ClassLoader first 
		//       ,         ClassLoader    
		if (extensionLoaderClassLoaderFirst) {
      
			ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); 
			if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
      
				urls = extensionLoaderClassLoader.getResources(fileName); 
			} 
		}
		//         URL  ,            ClassLoader    
		if(urls == null || !urls.hasMoreElements()) {
      
			if (classLoader != null) {
      
				urls = classLoader.getResources(fileName); 
			} else {
      
				urls = ClassLoader.getSystemResources(fileName); 
			} 
		}
		//          
		if (urls != null) {
      
			while (urls.hasMoreElements()) {
      
				//          ,           extensionClasses,              
				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); 
	} 
}

(9) loadResource 의 실현 을 관찰 합 니 다. 주로 파일 작업 을 읽 고 loadClass 에 방법 을 맡 겨 클래스 정 보 를 불 러 옵 니 다.클래스 정 보 를 불 러 오 는 것 도 가장 중요 한 방법 이다.
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 {
     
						//      key=value      
						String name = null; 
						int i = line.indexOf('='); 
						if (i > 0) {
      
							name = line.substring(0, i).trim(); 
							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); 
	} 
}

(10) loadclass 류 의 실현 을 관찰 하면 최종 적 으로 클래스 맵 을 완성 하 는 곳 임 을 알 수 있다.Adaptive 의 클래스 실현 원리 에 대해 우 리 는 이 장 에서 의 편 뒤에 놓 고 자세하게 이야기 합 니 다.
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  ,               ,         ,    cachedAdaptiveClass 
	if (clazz.isAnnotationPresent(Adaptive.class)) {
      
		cacheAdaptiveClass(clazz); 
	} else if (isWrapperClass(clazz)) {
      
		//      wapper  ,                  
		// wrapper      ,                
		cacheWrapperClass(clazz); 
	} else {
      
		clazz.getConstructor(); 
		//              ,             ,          org.apache.dubbo.common.Extension  ,            ,                 
		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); 
			} 
		}
		//     ,       class      
		String[] names = NAME_SEPARATOR.split(name); 
		if (ArrayUtils.isNotEmpty(names)) {
      
			//        Activate  ,        cachedActivates   ,         
			cacheActivateClass(clazz, names[0]); 
			//          
			for (String n : names) {
      
				cacheName(clazz, n); 
				saveInExtensionClass(extensionClasses, clazz, n); 
			} 
		} 
	} 
}

이 몇 가지 방법 을 실행 한 후에 몇 개의 필드 를 업데이트 합 니 다: cached Adaptive Class: 현재 Extension 형식 에 대응 하 는 Adaptive Extension 형식 (하나만 가능) cached Wrapper Classes: 현재 Extension 형식 에 대응 하 는 모든 Wrapper 구현 유형 (순서 없 음) cached Activates: 현재 Extension 은 자동 으로 활성화 되 어 캐 시 (map, 무질서) 를 실현 합 니 다.cachedNames: 확장 점 구현 클래스 에 대응 하 는 이름 (여러 이름 을 설정 하면 첫 번 째 값)
2. name 에 따라 확장 점 을 가 져 오 는 방법 getExtension
(1) getExtension 방법 실현.이 안 역시 name 에 따라 확장 점 을 처리 하고 자 물 쇠 를 추가 하여 실제 인용 을 만 드 는 데 주요 역할 을 합 니 다. 그 중에서 도 캐 시 를 사용 하여 처리 합 니 다.
public T getExtension(String name) {
      
	if (StringUtils.isEmpty(name)) {
      
		throw new IllegalArgumentException("Extension name null"); 
	}
	//     SPi         
	if ("true".equals(name)) {
      
		return getDefaultExtension(); 
	}
	//       holder,     cachedClasses     ,                 
	final Holder<Object> holder = getOrCreateHolder(name); 
	Object instance = holder.get(); 
	if (instance == null) {
      
		synchronized (holder) {
      
			instance = holder.get(); 
			if (instance == null) {
      
				//          
				instance = createExtension(name); 
				holder.set(instance); 
			} 
		} 
	}
	return (T) instance; 
}

(2) getOrCreate Holder 가 캐 시 를 어떻게 보장 하 는 지 살 펴 보 자.
private Holder<Object> getOrCreateHolder(String name) {
      
	//           Holder      
	Holder<Object> holder = cachedInstances.get(name); 
	if (holder == null) {
      
		//        ,   putIfAbsent         ,                        ,        
		cachedInstances.putIfAbsent(name, new Holder<>()); 
		//      holder    
		holder = cachedInstances.get(name); 
	}
	return holder; 
}

(3) 그리고 createExtension 의 실현 을 살 펴 보 겠 습 니 다. 그 는 확 장 된 class 이름 에 따라 인 스 턴 스 를 만 드 는 클래스 입 니 다.여기 도 확장 점 류 를 만 드 는 주요 실현 입 니 다.다음은 우리 도 다른 확장 점 등록 방법 에 대해 설명 한다.
private T createExtension(String name) {
      
	//                                    
	Class<?> clazz = getExtensionClasses().get(name); 
	if (clazz == null) {
      
		throw findException(name); 
	}
	try {
     
		//            
		T instance = (T) EXTENSION_INSTANCES.get(clazz); 
		if (instance == null) {
      
			//     ,    putIfAbsent                   
			EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 
			instance = (T) EXTENSION_INSTANCES.get(clazz); 
		}
		//           ,                 
		injectExtension(instance); 
		//             ,              ,         
		Set<Class<?>> wrapperClasses = cachedWrapperClasses; 
		if (CollectionUtils.isNotEmpty(wrapperClasses)) {
      
			for (Class<?> wrapperClass : wrapperClasses) {
      
				//               
				instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 
			} 
		}
		//             
		initExtension(instance);
		return instance; 
	} catch (Throwable t) {
      
		throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); 
	} 
}

(4) injectExtension 방법 관찰
private T injectExtension(T instance) {
      
	if (objectFactory null) {
      
		return instance; 
	}
	try {
     
		//           
		for (Method method : instance.getClass().getMethods()) {
      
			//    set   
			// 1.  "set"   
			// 2.      1 
			// 3.        
			if (!isSetter(method)) {
      
				continue; 
			}
			/**
			* Check {@link DisableInject} to see if we need auto injection for this property
			*/ 
			//          ,       
			if (method.getAnnotation(DisableInject.class) != null) {
      
				continue; 
			}
			//       ,       (String, Integer   ) 
			Class<?> pt = method.getParameterTypes()[0]; 
			if (ReflectUtils.isPrimitives(pt)) {
      
				continue; 
			}
			try {
     
				//     set       
				String property = getSetterProperty(method); 
				//  ExtensionLoader          
				//         setRandom(LoadBalance loadBalance),                  random     
				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; 
}

3. Adaptive 기능 실현 원리
Adaptive 의 주요 기능 은 모든 확장 점 을 하나의 클래스 로 밀봉 하고 URL 을 통 해 매개 변 수 를 입력 할 때 사용 할 확장 점 을 동적 으로 선택 하 는 것 입 니 다.그 밑바닥 의 실현 원 리 는 바로 동적 대리 이다. 여기 서 우 리 는 소스 코드 의 형식 을 통 해 그 가 어떻게 동적 대 리 를 통 해 로드 하 는 지 알려 줄 것 이다.(1) 여기 서 우리 getAdaptive Extension 방법 에 대해 말하자면 이 안 은 바로 이 종 류 를 진정 으로 얻 는 것 이다.ExtensionLoader 에 서 는 Holder 와 자 물 쇠 를 추가 하 는 방식 으로 유일 하 게 만 들 었 음 을 알 수 있다.
public T getAdaptiveExtension() {
      
	//           ,  Holder                 
	Object instance = cachedAdaptiveInstance.get(); 
	if (instance == null) {
      
		//                 ,         ,           
		if (createAdaptiveInstanceError != null) {
      
			throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); 
		}
		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; 
}

(2) 여기 서 우 리 는 createAdaptive Extension 에서 계속 실현 을 살 펴 본다.이곳 은 주로 몇 가지 방법 으로 포장 되 었 다.
private T createAdaptiveExtension() {
      
	try {
     
		//     `getAdaptiveExtensionClass`               
		//         class  ,    injectExtension     
		return injectExtension((T) getAdaptiveExtensionClass().newInstance()); 
	} catch (Exception e) {
      
		throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); 
	} 
}
private Class<?> getAdaptiveExtensionClass() {
      
	//                 
	getExtensionClasses(); 
	//         ,      
	if (cachedAdaptiveClass != null) {
      
		return cachedAdaptiveClass; 
	}
	//          
	return cachedAdaptiveClass = createAdaptiveExtensionClass(); 
}

(3) createAdaptive ExtensionClass 방법 을 구체 적 으로 살 펴 본다.Adaptive 코드 를 만 들 고 컴 파일 하여 class 를 생 성 합 니 다.
private Class<?> createAdaptiveExtensionClass() {
      
	//        Adaptive      ,         
	String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); 
	//        
	ClassLoader classLoader = findClassLoader(); 
	//      ,     ,    Java       Javassist    ,        
	org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.cla ss).getAdaptiveExtension(); 
	//       class 
	return compiler.compile(code, classLoader); 
}

(4) 구체 적 으로 Adaptive ClassLoader CodeGenerator. generate 방법 을 통 해 진정한 코드 생 성 을 실현 한다.
public String generate() {
      
	//            Adaptive,      
	if (!hasAdaptiveMethod()) {
      
		throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!"); 
	}
	//        
	StringBuilder code = new StringBuilder(); 
	//       
	code.append(generatePackageInfo()); 
	//        
	code.append(generateImports()); 
	//      
	code.append(generateClassDeclaration()); 
	//         
	Method[] methods = type.getMethods(); 
	for (Method method : methods) {
      
		code.append(generateMethod(method)); 
	}
	//        "}"       
	code.append("}"); 
	if (logger.isDebugEnabled()) {
      
		logger.debug(code.toString()); 
	}
	return code.toString(); 
}

(5) 여 기 는 주로 그 중의 모든 방법 에 대해 처리한다.구체 적 으로 generateMethod 를 보 는 방법 입 니 다.이곳 의 많은 방법 은 주로 반사 메커니즘 에 의존 하여 방법 을 봉인 하고 최종 문자열 로 연결 하 는 것 이다.그 중에서 가장 관건 적 인 방법 은 generateMethodContent 방법 으로 대리 기능 을 생 성 하 는 것 이다.
private String generateMethod(Method method) {
      
	//        
	String methodReturnType = method.getReturnType().getCanonicalName(); 
	//      
	String methodName = method.getName(); 
	//        
	String methodContent = generateMethodContent(method); 
	//        
	String methodArgs = generateMethodArguments(method); 
	//         
	String methodThrows = generateMethodThrows(method); 
	//           
	return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); 
}

(6) generateMethodContent 방법 해석.이 부분 은 debug 형식 으로 들 어 오 는 것 을 추천 합 니 다. 코드 를 보 는 것 도 더욱 직접적 입 니 다.이 부분 도 전체 Adaptive 에서 가장 핵심 적 인 코드 로 확장 점 이름 을 가 져 오고 실행 하 는 것 을 포함한다.
private String generateMethodContent(Method method) {
      
	//   Adaptive  ,     Adaptive       
	Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); 
	StringBuilder code = new StringBuilder(512); 
	if (adaptiveAnnotation == null) {
      
		//      ,       
		// throw new UnsupportedOperationException 
		return generateUnsupported(method); 
	} else {
      
		//   URL        
		int urlTypeIndex = getUrlTypeIndex(method);
		if (urlTypeIndex != -1) {
      
			//     url       
			code.append(generateUrlNullCheck(urlTypeIndex)); 
		} else {
      
			//                
			//           "get"     ,      URL  
			//            ,       
			code.append(generateUrlAssignmentIndirectly(method)); 
		}
		//            
		String[] value = getMethodAdaptiveValue(adaptiveAnnotation); 
		//         Invocation  
		//            ,  Invocation ,               
		//   Invocation ,  getMethodParameter,    getParameter    
		// getMethodParameter dubboURL    ,   "test.a"   "testA"    
		boolean hasInvocation = hasInvocationArgument(method); 
		//    Invocation         
		code.append(generateInvocationArgumentNullCheck(method)); 
		//              
		code.append(generateExtNameAssignment(value, hasInvocation)); 
		//           
		code.append(generateExtNameNullCheck(value)); 
		//         
		code.append(generateExtensionAssignment()); 
		//             
		code.append(generateReturnAndInvocation(method)); 
	}
	return code.toString(); 
}

이만
마지막.
이 지식 이 비용 을 지불 하 는 시대 에 기술 공 유 를 사랑 하고 직필 하 는 모든 사람들 은 우리 가 존경 할 만하 다!그 러 니 손 에 쥐 고 있 는 마 우 스 를 아 끼 지 말고 왼쪽 단 추 를 누 르 고 작은 편집 에 좋아요 를 눌 러 주세요.더 많은 내용 은 위 챗 공식 번호: 구조 적 시각 에 주목 하 세 요.
특별히 사 의 를 표 하 다
성도 선생님 의 재 미 있 고 유머 러 스 한 설명 에 감 사 드 립 니 다. 저 는 배 운 지식 에 대해 기억 에 남 습 니 다!무궁화 스승 님 의 진지 함 과 책임감 에 감 사 드 립 니 다. 매번 숙제 평 가 는 제 가 나 아 가 는 원동력 입 니 다!담임 필 선생님 의 책임 과 인내심 에 감 사 드 립 니 다. 매번 지루 하지 않 은 수업 통 지 는 제 가 초심 을 잊 지 않 고 좋 은 학습 상 태 를 유지 하 는 정신 적 지주 입 니 다!체크 교육 플랫폼 에 감 사 드 립 니 다. 저 에 게 이번에 적은 돈 을 쓰 면 1 기 체크 훈련 캠프 에 지원 할 수 있 고 심층 적 인 기술 정 수 를 배 울 수 있 는 기 회 를 많이 줄 수 있 습 니 다.그리고 공부 하 는 과정 에서 많은 기술 자 를 알 게 되 었 고 그들 에 게 몇 가지 문 제 를 물 어 볼 수 있 습 니 다. 예 를 들 어 장 씨 어른, 노 씨 어른, 우 생 큰 사람 등 입 니 다.

좋은 웹페이지 즐겨찾기