feign的實(shí)現(xiàn)原理以及springboot如何使用feign實(shí)現(xiàn)服務(wù)間調(diào)用的

## 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)單

![image](https://user-gold-cdn.xitu.io/2019/10/30/16e1a5cbcf350d76?w=710&h=1140&f=jpeg&s=112320)

#### 通過(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)

![image](https://user-gold-cdn.xitu.io/2019/10/30/16e1a54c334e7c77?w=1542&h=1134&f=jpeg&s=213169)

**結(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);

}

```

![image](https://user-gold-cdn.xitu.io/2019/10/30/16e1a54c33a60d09?w=1876&h=1232&f=jpeg&s=689073)

![image](https://user-gold-cdn.xitu.io/2019/10/30/16e1a54c33cb7ea1?w=1666&h=940&f=jpeg&s=234671)

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 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 7,295評(píng)論 0 17
  • 自己備忘,隨便寫 android網(wǎng)絡(luò)框架源碼解析及對(duì)比 android常用網(wǎng)絡(luò)框架對(duì)比 Volley: 特點(diǎn) 基于...
    幻海流心閱讀 1,677評(píng)論 0 4
  • 設(shè)計(jì)模式分類 總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類:創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原...
    lifeline丿毅閱讀 1,335評(píng)論 0 2
  • 對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),設(shè)計(jì)模式有時(shí)候就是一道坎,但是設(shè)計(jì)模式又非常有用,過(guò)了這道坎,它可以讓你水平提高一個(gè)檔次。而在a...
    WANKUN閱讀 320評(píng)論 0 2
  • 主要積累一些開(kāi)發(fā)中比較 常用的工具類,部分借鑒于網(wǎng)絡(luò),主要來(lái)源于平時(shí)開(kāi)發(fā)因需求而生的小工具類 13、ArithUt...
    大鴨梨leepear閱讀 725評(píng)論 0 1

友情鏈接更多精彩內(nèi)容