## 1.feign是什么(what)
>feign是java實(shí)現(xiàn)的一個(gè)聲明式的http客戶端,
## 2.feign的使用(how)
#### 簡(jiǎn)單使用
1. maven pom.xml
```
? ? <dependency>
? ? ? ? <groupId>com.netflix.feign</groupId>
? ? ? ? <artifactId>feign-core</artifactId>
? ? ? ? <version>${feign.version}</version>
? ? </dependency>
```
2. 定義http調(diào)用接口
```
package top.soft1010.feign;
import feign.*;
import java.util.Map;
/**
* Created by zhangjifu on 19/10/25.
*/
public interface TestService {
? ? //直接url path中添加參數(shù)
? ? @RequestLine("GET /feign/get/{id}")
? ? String test1(@Param("id") Integer id);
? ? //url帶參數(shù)
? ? @RequestLine("GET /feign/get?id={id}")
? ? String test2(@Param("id") Integer id);
? ? //add header 注意id: {id}中間的空格不能省略
? ? @RequestLine("GET /feign/getHeader")
? ? @Headers(value = {"id: {id}"})
? ? String test3(@Param("id") String id);
? ? //headerMap 添加多個(gè)header
? ? @RequestLine("GET /feign/getHeaders")
? ? String test4(@HeaderMap Map<String, Object> headers);
? ? //QueryMap 添加多個(gè)查詢條件
? ? @RequestLine("GET /feign/queryMap")
? ? String test5(@QueryMap Map<String, Object> params);
? ? //指定json串 注意這里最外層的括號(hào)需要轉(zhuǎn)義 很無(wú)語(yǔ)是不是
? ? @RequestLine("POST /feign/json")
? ? @Body("%7B\"id\":{id},\"name\":\"{name}\"%7D")
? ? @Headers("Content-Type: application/json")
? ? String test6(@Param("id") Integer id, @Param("name") String name);
? ? //這里直接將json字符串作為一個(gè)整體
? ? @RequestLine("POST /feign/json")
? ? @Body("{jsonBody}")
? ? @Headers("Content-Type: application/json")
? ? String test7(@Param("jsonBody") String jsonBody);
}
```
3. 調(diào)用方法
```
import com.google.gson.Gson;
import feign.Feign;
import org.junit.Test;
import top.soft1010.feign.JsonPo;
import top.soft1010.feign.TestService;
import java.util.HashMap;
import java.util.Map;
/**
* Created by zhangjifu on 19/10/25.
*/
public class FeignTestServiceTest {
? ? @Test
? ? public void test1() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? String ret = testService.test1(1);
? ? ? ? System.out.println(ret);
? ? }
? ? @Test
? ? public void test2() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? String ret = testService.test2(1);
? ? ? ? System.out.println(ret);
? ? }
? ? @Test
? ? public void test3() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? String ret = testService.test3("2");
? ? ? ? System.out.println(ret);
? ? }
? ? @Test
? ? public void test4() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? Map<String, Object> headerMap = new HashMap<String, Object>();
? ? ? ? headerMap.put("header1", "header1Value");
? ? ? ? headerMap.put("header2", "header2Value");
? ? ? ? String ret = testService.test4(headerMap);
? ? ? ? System.out.println(ret);
? ? }
? ? @Test
? ? public void test5() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? Map<String, Object> queryMap = new HashMap<String, Object>();
? ? ? ? queryMap.put("param1", "params1Value");
? ? ? ? queryMap.put("param2", "params2Value");
? ? ? ? String ret5 = testService.test5(queryMap);
? ? ? ? System.out.println(ret5);
? ? }
? ? @Test
? ? public void test6() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? String ret6 = testService.test6(1, "zhang");
? ? ? ? System.out.println(ret6);
? ? }
? ? @Test
? ? public void test7() {
? ? ? ? TestService testService = Feign.builder().target(TestService.class, "http://127.0.0.1:8080");
? ? ? ? JsonPo jsonPo = new JsonPo();
? ? ? ? jsonPo.setName("zhang");
? ? ? ? jsonPo.setId(1);
? ? ? ? String ret7 = testService.test7(new Gson().toJson(jsonPo));
? ? ? ? System.out.println(ret7);
? ? }
}
```
可以看到使用feign調(diào)用http接口相當(dāng)簡(jiǎn)單,沒(méi)有多余的代碼
#### feign的豐富功能
```
import feign.*;
import feign.codec.Decoder;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import org.junit.Test;
import top.soft1010.feign.TestService;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhangjifu on 19/10/25.
*/
public class ComplexFeignTest {
? ? @Test
? ? public void test() {
? ? ? ? /**
? ? ? ? * 包含更多
? ? ? ? * 攔截器 編碼 解碼 options(超時(shí)時(shí)間) 重試 日志
? ? ? ? */
? ? ? ? List<RequestInterceptor> requestInterceptors = new ArrayList<RequestInterceptor>();
? ? ? ? requestInterceptors.add(new MyRequestInterceptor());
? ? ? ? TestService testService = Feign.builder().
? ? ? ? ? ? ? ? requestInterceptors(requestInterceptors).
? ? ? ? ? ? ? ? decoder(new MyDecoder()).
? ? ? ? ? ? ? ? encoder(new MyEncoder()).
? ? ? ? ? ? ? ? options(new Request.Options(10000, 60000)).
? ? ? ? ? ? ? ? retryer(new MyRetryer(1000, 10000, 3)).
? ? ? ? ? ? ? ? logger(new MyLogger()).
? ? ? ? ? ? ? ? logLevel(Logger.Level.BASIC).
? ? ? ? ? ? ? ? target(TestService.class, "http://127.0.0.1:8080/");
? ? ? ? testService.test1(1);
? ? }
? ? static class MyRequestInterceptor implements RequestInterceptor {
? ? ? ? public void apply(RequestTemplate template) {
? ? ? ? ? ? System.out.println("MyRequestInterceptor? " + System.currentTimeMillis() + " 發(fā)起請(qǐng)求" + template.url());
? ? ? ? }
? ? }
? ? static class MyEncoder extends Encoder.Default {
? ? ? ? public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
? ? ? ? ? ? System.out.println("MyEncoder " + type.toString() + "? " + o.toString());
? ? ? ? }
? ? }
? ? static class MyDecoder extends Decoder.Default {
? ? ? ? public Object decode(Response response, Type type) throws IOException, FeignException {
? ? ? ? ? ? if (response.status() == 200) {
? ? ? ? ? ? ? ? System.out.println("MyDecoder 請(qǐng)求成功");
? ? ? ? ? ? }
? ? ? ? ? ? return super.decode(response, type);
? ? ? ? }
? ? }
? ? static class MyLogger extends Logger {
? ? ? ? @Override
? ? ? ? protected void log(String configKey, String format, Object... args) {
? ? ? ? ? ? System.out.println(String.format("MyLogger " + methodTag(configKey) + format + "%n", args));
? ? ? ? }
? ? ? ? @Override
? ? ? ? protected void logRequest(String configKey, Level logLevel, Request request) {
? ? ? ? ? ? if (logLevel.equals(Level.BASIC)) {
? ? ? ? ? ? ? ? super.logRequest(configKey, logLevel, request);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? static class MyRetryer implements Retryer {
? ? ? ? int maxAttempts;
? ? ? ? long period;
? ? ? ? long maxPeriod;
? ? ? ? int attempt;
? ? ? ? long sleptForMillis;
? ? ? ? public MyRetryer(long period, long maxPeriod, int maxAttempts) {
? ? ? ? ? ? this.period = period;
? ? ? ? ? ? this.maxAttempts = maxAttempts;
? ? ? ? ? ? this.maxPeriod = maxPeriod;
? ? ? ? }
? ? ? ? protected long currentTimeMillis() {
? ? ? ? ? ? return System.currentTimeMillis();
? ? ? ? }
? ? ? ? public void continueOrPropagate(RetryableException e) {
? ? ? ? ? ? if (this.attempt++ >= this.maxAttempts) {
? ? ? ? ? ? ? ? throw e;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? long interval;
? ? ? ? ? ? ? ? if (e.retryAfter() != null) {
? ? ? ? ? ? ? ? ? ? interval = e.retryAfter().getTime() - this.currentTimeMillis();
? ? ? ? ? ? ? ? ? ? if (interval > this.maxPeriod) {
? ? ? ? ? ? ? ? ? ? ? ? interval = this.maxPeriod;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (interval < 0L) {
? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? interval = this.nextMaxInterval();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(interval);
? ? ? ? ? ? ? ? } catch (InterruptedException var5) {
? ? ? ? ? ? ? ? ? ? Thread.currentThread().interrupt();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? this.sleptForMillis += interval;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? long nextMaxInterval() {
? ? ? ? ? ? long interval = (long) ((double) this.period * Math.pow(1.5D, (double) (this.attempt - 1)));
? ? ? ? ? ? return interval > this.maxPeriod ? this.maxPeriod : interval;
? ? ? ? }
? ? ? ? public Retryer clone() {
? ? ? ? ? ? System.out.println("MyRetryer clone");
? ? ? ? ? ? return new MyRetryer(this.period, this.maxPeriod, this.maxAttempts);
? ? ? ? }
? ? }
}
```
可以看到feign調(diào)用http接口,支持?jǐn)r截器,編解碼,重試等豐富的功能
## 3.feign的實(shí)現(xiàn)原理(why)
feign的核心jar包只有一個(gè) **feign-core-8.18.0.jar**
看一下整個(gè)jar包下面的類結(jié)構(gòu),相對(duì)來(lái)說(shuō)比較簡(jiǎn)單

#### 通過(guò)源碼解析實(shí)現(xiàn)原理
```?
? ? Feign.builder()
? ? .requestInterceptors(requestInterceptors)
? ? .decoder(new MyDecoder())
? ? .encoder(new MyEncoder())
? ? .options(new Request.Options(10000, 60000))
? ? .retryer(new MyRetryer(1000, 10000, 3))
? ? .logger(new MyLogger())
? ? .logLevel(Logger.Level.BASIC)
? ? .target(TestService.class, "http://127.0.0.1:8080/");? ?
```
1. 通過(guò)**構(gòu)造器模式**構(gòu)造Feign.Builder對(duì)象。
2. target()方法最后調(diào)用了ReflectiveFeign.newInstance()方法
3. 核心代碼 通過(guò)**動(dòng)態(tài)代理**將feign模板化的接口實(shí)例化
```
? ? InvocationHandler handler = factory.create(target, methodToHandler);
? ? T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
```
4. 根據(jù)動(dòng)態(tài)代理的原理,**InvocationHandler**這個(gè)接口的實(shí)現(xiàn)既是具體調(diào)用邏輯所在
```
static class FeignInvocationHandler implements InvocationHandler {
? ? private final Target target;
? ? private final Map<Method, MethodHandler> dispatch;
? ? FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
? ? ? this.target = checkNotNull(target, "target");
? ? ? this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
? ? }
? ? @Override
? ? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? if ("equals".equals(method.getName())) {
? ? ? ? try {
? ? ? ? ? Object
? ? ? ? ? ? ? otherHandler =
? ? ? ? ? ? ? args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
? ? ? ? ? return equals(otherHandler);
? ? ? ? } catch (IllegalArgumentException e) {
? ? ? ? ? return false;
? ? ? ? }
? ? ? } else if ("hashCode".equals(method.getName())) {
? ? ? ? return hashCode();
? ? ? } else if ("toString".equals(method.getName())) {
? ? ? ? return toString();
? ? ? }
? ? ? //看看這個(gè)dispatct.get()是如何實(shí)現(xiàn)的?
? ? ? return dispatch.get(method).invoke(args);
? ? }
```
```
@Override
? public Object invoke(Object[] argv) throws Throwable {
? ? RequestTemplate template = buildTemplateFromArgs.create(argv);
? ? Retryer retryer = this.retryer.clone();
? ? while (true) {
? ? ? try {
? ? ? ? return executeAndDecode(template);
? ? ? } catch (RetryableException e) {
? ? ? ? retryer.continueOrPropagate(e);
? ? ? ? if (logLevel != Logger.Level.NONE) {
? ? ? ? ? logger.logRetry(metadata.configKey(), logLevel);
? ? ? ? }
? ? ? ? continue;
? ? ? }
? ? }
? }
```
最終調(diào)用了Client類中的默認(rèn)實(shí)現(xiàn)

**結(jié)論:feign最核心的邏輯還是利用了放射&動(dòng)態(tài)代理設(shè)計(jì)模式最終還是調(diào)用了java API實(shí)現(xiàn)http接口的調(diào)用。**
## 4.feign與其他http客戶端對(duì)比
直接通過(guò)代碼比較一下幾個(gè)http調(diào)用
#### java原生調(diào)用http接口
```
public static String sendGet(String url, String param) {
? ? ? ? String result = "";
? ? ? ? BufferedReader in = null;
? ? ? ? try {
? ? ? ? ? ? String urlNameString = url + "?" + param;
? ? ? ? ? ? URL realUrl = new URL(urlNameString);
? ? ? ? ? ? // 打開(kāi)和URL之間的連接
? ? ? ? ? ? URLConnection connection = realUrl.openConnection();
? ? ? ? ? ? // 設(shè)置通用的請(qǐng)求屬性
? ? ? ? ? ? connection.setRequestProperty("accept", "*/*");
? ? ? ? ? ? connection.setRequestProperty("connection", "Keep-Alive");
? ? ? ? ? ? connection.setRequestProperty("user-agent",
? ? ? ? ? ? ? ? ? ? "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
? ? ? ? ? ? // 建立實(shí)際的連接
? ? ? ? ? ? connection.connect();
? ? ? ? ? ? // 獲取所有響應(yīng)頭字段
? ? ? ? ? ? Map<String, List<String>> map = connection.getHeaderFields();
? ? ? ? ? ? // 遍歷所有的響應(yīng)頭字段
? ? ? ? ? ? for (String key : map.keySet()) {
? ? ? ? ? ? ? ? System.out.println(key + "--->" + map.get(key));
? ? ? ? ? ? }
? ? ? ? ? ? // 定義 BufferedReader輸入流來(lái)讀取URL的響應(yīng)
? ? ? ? ? ? in = new BufferedReader(new InputStreamReader(
? ? ? ? ? ? ? ? ? ? connection.getInputStream()));
? ? ? ? ? ? String line;
? ? ? ? ? ? while ((line = in.readLine()) != null) {
? ? ? ? ? ? ? ? result += line;
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? System.out.println("發(fā)送GET請(qǐng)求出現(xiàn)異常!" + e);
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? // 使用finally塊來(lái)關(guān)閉輸入流
? ? ? ? finally {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? if (in != null) {
? ? ? ? ? ? ? ? ? ? in.close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e2) {
? ? ? ? ? ? ? ? e2.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return result;
? ? }
```
#### httpclient
```
CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
? ? ? ? URI uri = new URIBuilder().
? ? ? ? ? ? ? ? setScheme("https").
? ? ? ? ? ? ? ? setHost("www.google.com").
? ? ? ? ? ? ? ? setPath("/search").
? ? ? ? ? ? ? ? setParameter("q", "張").
? ? ? ? ? ? ? ? setParameter("btnG","Google Search").
? ? ? ? ? ? ? ? setParameter("oq","").
? ? ? ? ? ? ? ? setParameter("aq","f").
? ? ? ? ? ? ? ? build();
? ? ? ? CloseableHttpResponse closeableHttpResponse = null;
? ? ? ? try {
? ? ? ? ? ? HttpGet httpGet = new HttpGet(uri);
? ? ? ? ? ? System.out.println(httpGet.getURI().toString());
? ? ? ? ? ? closeableHttpResponse = closeableHttpClient.execute(httpGet);
? ? ? ? ? ? System.out.println(closeableHttpResponse.getStatusLine().getStatusCode());
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? if (closeableHttpResponse != null) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? closeableHttpClient.close();
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
```
#### okhttp
```
? ? ? ? String url = "http://127.0.0.1:8080/feign/get/1";
? ? ? ? OkHttpClient client = new OkHttpClient();
? ? ? ? Request request = new Request.Builder()
? ? ? ? ? ? ? ? .url(url)
? ? ? ? ? ? ? ? .addHeader("1", "2")
? ? ? ? ? ? ? ? .build();
? ? ? ? String ret = "";
? ? ? ? try (Response response = client.newCall(request).execute()) {
? ? ? ? ? ? ret = response.body().string();
? ? ? ? }
? ? ? ? System.out.println(ret);
```
不同的http調(diào)用的對(duì)比
|原生API|httpClient|okhttp|feign|
|---|---|---|---|
|代碼量最多|對(duì)比代碼量比原生好很多,支持所有http調(diào)用|相對(duì)httpclient代碼量更少|(zhì)代碼量最少,書寫最簡(jiǎn)單,但是僅支持最常用的調(diào)用|
## 5.feign在spring boot中的應(yīng)用及如何運(yùn)行的
#### feign在spring boot中的應(yīng)用
1. 直接寫對(duì)應(yīng)的模板化接口,使用注解添加對(duì)應(yīng)url以及參數(shù)等信息
```
package top.soft1010.feign.client;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by zhangjifu on 19/10/28.
*/
@FeignClient(name = "test-feign-client", url = "http://127.0.0.1:8080/")
public interface FeignConsumer {
? ? @RequestMapping(value = "/feign/get/{id}")
? ? String test1(@PathVariable(value = "id") Integer id);
? ? @RequestMapping(value = "/feign/get/")
? ? String test2(@RequestParam(value = "id") Integer id);
}
```
2. 直接注入接口bean就可以想使用本地方法一樣使用了。
```
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import top.soft1010.feign.client.FeignConsumer;
import top.soft1010.feign.client.TestAppApplication;
/**
* Created by zhangjifu on 19/10/28.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestAppApplication.class)
public class SpringFeignTest {
? ? @Autowired
? ? FeignConsumer feignConsumer;
? ? @Test
? ? public void test() {
? ? ? ? System.out.println(feignConsumer.test1(1));
? ? }
? ? @Test
? ? public void test2() {
? ? ? ? System.out.println(feignConsumer.test2(1));
? ? }
}
```
#### spring boot(cloud)是如何集成feign,并且像使用本地方法一下實(shí)現(xiàn)遠(yuǎn)程調(diào)用的。
下面跟著源碼走進(jìn)feign
1. spring boot啟動(dòng) SpringApplication.run()
```
package top.soft1010.feign.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import java.util.Arrays;
/**
* Created by zhangjifu on 19/10/25.
*/
@SpringBootApplication
@EnableFeignClients
@ComponentScan(basePackages = "top.soft1010")
public class TestAppApplication {
? ? final static Logger logger = LoggerFactory.getLogger(TestAppApplication.class);
? ? public static void main(String[] args) {
? ? ? ? // 啟動(dòng)Sprign Boot
? ? ? ? ApplicationContext ctx = SpringApplication.run(TestAppApplication.class, args);
? ? ? ? String[] beanNames = ctx.getBeanDefinitionNames();
? ? ? ? Arrays.sort(beanNames);
? ? ? ? for (String beanName : beanNames) {
? ? ? ? ? ? logger.info(beanName);
? ? ? ? }
? ? }
}
```
2. 啟動(dòng)過(guò)程中核心方法 refresh()方法
-? refresh()方法在啟動(dòng)過(guò)程中會(huì)執(zhí)行兩次,第一次是environmentPrepared的時(shí)候執(zhí)行,我們關(guān)注的是第二次執(zhí)行。
-? 其中invokeBeanFactoryPostProcessors(beanFactory)這個(gè)方法注冊(cè)BeanDefinition到beanFactory
-? finishBeanFactoryInitialization(beanFactory)初始化bean實(shí)例,但是這里不一定會(huì)實(shí)例化,一般都是懶加載
```
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
```
3. 如何注冊(cè)BeanDefinition到beanFactory
一直debug進(jìn)去發(fā)現(xiàn)與這樣一段代碼,簡(jiǎn)單點(diǎn)說(shuō)就是ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類會(huì)產(chǎn)生對(duì)應(yīng)的BeanDefinition到beanFactory
```
? ? ? ? ? ? ? ? else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
```
FeignClientsRegistrar就是實(shí)現(xiàn)了這個(gè)接口
```
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//這里, FeignClientFactoryBean.class 聲明的@FeignClient的接口最后就是實(shí)例化成這個(gè)類的對(duì)象
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
```


4. bean Initialization
FeignClientFactoryBean實(shí)現(xiàn)了FactoryBean接口,這個(gè)接口實(shí)現(xiàn)方法getObject()是返回spring bean調(diào)用的方法
```
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
```
可以看到這個(gè)實(shí)現(xiàn)是不是很熟悉,就是通過(guò)動(dòng)態(tài)代理代理調(diào)用具體方法的過(guò)程。
```
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
```
## 6.延伸知識(shí)點(diǎn)
#### 反射
[參考](https://juejin.im/post/5d9ecb9c5188256eee163fb3)
#### 動(dòng)態(tài)代理
- 代理類在程序運(yùn)行時(shí)創(chuàng)建
- 使用步驟
>步驟1:通過(guò)實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器;
步驟2:通過(guò)為Proxy類指定ClassLoader對(duì)象和一組interface來(lái)創(chuàng)建動(dòng)態(tài)代理類;
步驟3:通過(guò)反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型;
步驟4:通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理對(duì)象,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入。
還有更簡(jiǎn)便的動(dòng)態(tài)代理實(shí)現(xiàn)方法,Proxy的靜態(tài)方法newProxyInstance已經(jīng)為我們封裝了步驟2到步驟4的過(guò)程。
## 7.思考
**mybatis也是通過(guò)寫一個(gè)mapper接口&對(duì)應(yīng)的xml配置就可以直接使用bean調(diào)用具體方法,這個(gè)實(shí)現(xiàn)是否相同???**
- MapperScannerRegistrar類 實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口
- MapperFactoryBean 實(shí)現(xiàn)FactoryBean接口
```
@Override
? public T getObject() throws Exception {
? ? return getSqlSession().getMapper(this.mapperInterface);
? }
```
## 8.遺留問(wèn)題
feign整合httpclient 或者 okhttp ?