動態代理系列(四)RPC中動態代理
看過上一講動態代理換種玩法,我們知道通過變形的動態代理,可以不需要目標類,就能生成代理類。
這種技術,在我們今天的RPC框架中可以說被廣泛的使用。

RPC呼叫.jpg
下面我就通過我寫的一個簡單的 ofollow,noindex">RPC元件 來介紹它是如何實現。( 注意:當前版本可能為快照版本,程式碼在dev分支,未合併到master分支 )
Spring的Autowired裝配
在Spring中,我們可以通過 Autowired 方式進行物件的裝配注入,也就是說欄位要被注入,需要被註解標記。那麼我們可以猜想,Spring一定有一段邏輯,來操作這個注入裝配的過程。
並且,Spring為了好的可擴充套件性,以外掛化的方式對裝配Bean的過程進行補充,這些外掛就叫後處理器。而對 Autowired 進行組裝的後處理器就是: AutowiredAnnotationBeanPostProcessor ,而具體的實現方法就是 postProcessPropertyValues() 。有興趣的可以自行閱讀。
RPC呼叫
我們知道本地呼叫肯定是通過物件完成,僅僅介面,如果沒有例項物件,是要報 空指標 的,但是RPC是遠端的過程呼叫,是呼叫遠端的物件,遠端的方法,本地是沒有例項物件存在的,那不就矛盾了嗎?
毫無疑問,呼叫,肯定就有物件,只不過,這個物件我們沒有手工編碼,看到這裡,可能有些同學已經想到什麼了。沒錯,就是上節介紹的動態代理為介面偽裝出一個實現類。
物件可以通過動態代理生成,那麼如何把生成的物件如同Autowired註解標記的欄位一樣進行裝配呢?
實現裝配
既然我們知道了 Autowired 註解裝配的原理—通過後處理器完成。那麼最簡單的方式,我們就仿照寫一個註解,一個處理器不就成了。
註解
我們寫的註解是 Reference ,在SpringBean中被使用,看下面程式碼:
@RestController public class DemoController { @Reference(contract = IntfDemo.class, implCode = "abc") private IntfDemo intfDemo; @RequestMapping(path = "f1") public String f1() { return intfDemo.name(); } }
裝配處理器
寫一個後處理,來處理被 Reference 註解標記的欄位,具體實現如下:
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.reflect.FieldUtils; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; /** * 處理SpringBean的{@link Reference} 屬性的注入 * * @author: guanjie */ @Component @Slf4j public class ReferenceBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, final Object bean, final String beanName) throws BeansException { ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { FieldUtils.writeField(field, bean, ServerProxy.getProxy(field), true); } }, new ReflectionUtils.FieldFilter() { @Override public boolean matches(Field field) { return field.isAnnotationPresent(Reference.class); } }); return pvs; } }
看上面功能程式碼,不超過20行,就把這個功能完成了,而Spring會自動發現這個處理器,並在裝配階段對每個Bean使用。
ServerProxy.getProxy(field)和上節中生成代理類的方式幾乎一樣,只不過把處理邏輯換成了IO通訊而已。
原理懂了其實還是挺簡單的,想了解的更多,可以拉下程式碼研究,歡迎感興趣的同學一起來進行程式碼的維護和後續的補充。