简介
dubbo服务引用有两种方式,饿汉式和懒汉式。饿汉式指在Spring容器调用ReferenceBean的afterPropertiesSet方法时引用服务,懒汉式指在ReferenceBean对应的服务被注入到其他类中时引用。dubbo默认懒汉式,我们按照dubbo的默认配置进行分析,看到服务引用的入口方法 -- ReferenceBean的getObject。
public Object getObject() {
return get();
}
这个方法重写了FactoryBean的getObject,用于返回本工厂创建的对象实例。接下来看具体的源码分析。
1.获取对象实例
获取对象实例获得的是一个代理类,目的是避免框架的代码对用户代码造成入侵,其方法调用关系如下。
ReferenceBean的getObject方法调用了其父类ReferenceConfig的get方法。
public synchronized T get() {
checkAndUpdateSubConfigs();
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
注意这是一个synchronized方法,防止ref(类型为泛型)被重复初始化。首先判断ref是否为null,若为空则调用初始化方法。下面看初始化方法init。
private void init() {
if (initialized) {
return;
}
initialized = true;
checkStubAndLocal(interfaceClass);
checkMock(interfaceClass);
Map map = new HashMap();
map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
appendRuntimeParameters(map);
if (!isGeneric()) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
} else {
map.put("methods", StringUtils.join(new HashSet(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer, Constants.DEFAULT_KEY);
appendParameters(map, this);
Map attributes = null;
if (CollectionUtils.isNotEmpty(methods)) {
attributes = new HashMap();
for (MethodConfig methodConfig : methods) {
appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(methodConfig.getName() + ".retries", "0");
}
}
attributes.put(methodConfig.getName(), convertMethodConfig2AyncInfo(methodConfig));
}
}
String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
ref = createProxy(map);
ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), interfaceClass, ref, interfaceClass.getMethods(), attributes);
ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
}
init方法的主要目的是初始化ref变量。首先判断是否被初始化,若已初始化则直接返回,否则开始初始化。将ApplicationConfig、ModuleConfig等配置信息写入map,然后基于map创建代理类,赋值给ref。关注给ref赋值这步操作,ref获得的其实是Invoker的代理类。这里调用了createProxy方法创建代理类,下面分析这个方法。
private T createProxy(Map map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null && url.length() > 0) { // if a url is specified, don't do local reference
isJvmRefer = false;
} else {
// by default, reference local service if there is
isJvmRefer = InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl);
}
} else {
isJvmRefer = isInjvm();
}
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, Constants.LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // assemble URL from register center's configuration
checkRegistry();
List us = loadRegistries(false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config to your spring config.");
}
}
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List> invokers = new ArrayList>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// use RegistryAwareCluster only when register's cluster is available
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, RegistryAwareCluster.NAME);
// The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // not a registry url, must be direct invoke.
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && !invoker.isAvailable()) {
// make it possible for consumer to retry later if provider is temporarily unavailable
initialized = false;
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
/**
* @since 2.7.0
* ServiceData Store
*/
MetadataReportService metadataReportService = null;
if ((metadataReportService = getMetadataReportService()) != null) {
URL consumerURL = new URL(Constants.CONSUMER_PROTOCOL, map.remove(Constants.REGISTER_IP_KEY), 0, map.get(Constants.INTERFACE_KEY), map);
metadataReportService.publishConsumer(consumerURL);
}
// create service proxy
return (T) proxyFactory.getProxy(invoker);
}
注意这个方法的参数map,其包含了上文提到的ApplicationConfig等配置信息。createProxy方法包含两步:
- 1.获得invoker实例;
- 2.创建Invoker的代理类;
首先分析获得invoker实例。判断服务引用的方式是否为本地引用:injvm值不为null,就根据injvm的值判断;否则,有指定的url,说明不是本地引用。
- case1:是本地引用,根据本地协议、本地主机等新建URL,根据接口和URL通过协议引用服务,获得invoker;
- case2:不是本地引用
-- case2.1:有指定的url,此时可能是服务直连也可能是通过注册中心连接。解析url并且遍历,判断每个url的协议配置是否是registry,然后加入urls。
-- case2.2:没有指定的url,此时是通过注册中心连接。加载RegistryConfig配置获得注册中心的url,遍历url加入urls。
根据urls的列表长度分为两种情况:
-- case1:urls的列表长度为1,说明只有一个服务提供者,直接引用服务获得invoker即可;
-- case2:urls的列表长度大于1,说明存在多个服务提供者,遍历urls的列表,引用每个服务获得invoker并加入invokers列表。然后通过集群管理将invokers列表生成invoker;
然后分析创建Invoker的代理类,代码中调用了proxyFactory.getProxy来创建服务代理。
2.创建Invoker
Invoker 是 Dubbo 的核心模型,代表一个可执行体。在服务提供方,Invoker 用于调用服务提供类。在服务消费方,Invoker 用于执行远程调用。
createProxy代码中通过调用refprotocol.refer创建Invoker,这边分析两种常见的Protocol接口的实现,DubboProtocol和RegistryProtocol。
2.1 DubboProtocol.refer
方法调用关系如下图。
从DubboProtocol的refer开始分析。
public Invoker refer(Class serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
该方法创建了一个DubboInvoker实例并返回,这里关注一下getClients方法。
private ExchangeClient[] getClients(URL url) {
// whether to share connection
boolean useShareConnect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
List shareClients = null;
// if not configured, connection is shared, otherwise, one connection for one service
if (connections == 0) {
useShareConnect = true;
/**
* The xml configuration should have a higher priority than properties.
*/
String shareConnectionsStr = url.getParameter(Constants.SHARE_CONNECTIONS_KEY, (String) null);
connections = Integer.parseInt(StringUtils.isBlank(shareConnectionsStr) ? ConfigUtils.getProperty(Constants.SHARE_CONNECTIONS_KEY,
Constants.DEFAULT_SHARE_CONNECTIONS) : shareConnectionsStr);
shareClients = getSharedClient(url, connections);
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (useShareConnect) {
clients[i] = shareClients.get(i);
} else {
clients[i] = initClient(url);
}
}
return clients;
}
第一步判断是否共享连接,默认不共享。根据url的connections参数值判断,若connections为0,此时共享连接。先从xml配置或者配置文件获取connection,xml配置优先于配置文件,然后获取共享的client。
- 共享连接,返回的ExchangeClient数组的值从共享的client获取;
- 不共享连接,返回的ExchangeClient数组的值由初始化client得到;
接下来先看getSharedClient方法。
private List getSharedClient(URL url, int connectNum) {
String key = url.getAddress();
List clients = referenceClientMap.get(key);
if (checkClientCanUse(clients)) {
batchClientRefIncr(clients);
return clients;
}
locks.putIfAbsent(key, new Object());
synchronized (locks.get(key)) {
clients = referenceClientMap.get(key);
// dubbo check
if (checkClientCanUse(clients)) {
batchClientRefIncr(clients);
return clients;
}
// connectNum must be greater than or equal to 1
connectNum = Math.max(connectNum, 1);
// If the clients is empty, then the first initialization is
if (CollectionUtils.isEmpty(clients)) {
clients = buildReferenceCountExchangeClientList(url, connectNum);
referenceClientMap.put(key, clients);
} else {
for (int i = 0; i < clients.size(); i++) {
ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i);
// If there is a client in the list that is no longer available, create a new one to replace him.
if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) {
clients.set(i, buildReferenceCountExchangeClient(url));
continue;
}
referenceCountExchangeClient.incrementAndGetCount();
}
}
/**
* I understand that the purpose of the remove operation here is to avoid the expired url key
* always occupying this memory space.
*/
locks.remove(key);
return clients;
}
}
根据url的address获取clients。
- case1:clients可用,增加clients服务引用的数量,直接返回clients即可;
- case2:clients不可用,加锁双重检查
-- case2.1 clients为空,创建clients并存入缓存;
-- case2.2 clients非空,遍历clients,遇到空的client则新建,非空的client则增加服务引用计数;
接着看initClient。
private ExchangeClient initClient(URL url) {
// client type setting.
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
// enable heartbeat by default
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// BIO is not allowed since it has severe performance issue.
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: " + str + "," +
" supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
}
ExchangeClient client;
try {
// connection should be lazy
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
client = new LazyConnectExchangeClient(url, requestHandler);
} else {
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
return client;
}
step1:获取url的client参数,即客户端配置。
step2:url添加codec和heartbeat参数。
step3:客户端配置对应的Transporter是否存在,不存在则抛出异常。
step4:创建client实例。这边有两种情况:
- lazy参数为true,创建懒加载ExchangeClient实例;
- lazy参数为false,创建普通ExchangeClient实例;
看下创建普通ExchangeClient实例的情况,是通过Exchangers.connect创建的。
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
return getExchanger(url).connect(url, handler);
}
这个方法获取Exchanger实例,然后调用其connect方法。这边分析Exchanger的默认实现HeaderExchanger
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
该方法返回了HeaderExchangeClient实例,初始化client完成。然后再关注一下调用的Transporters.connect方法。
public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
ChannelHandler handler;
if (handlers == null || handlers.length == 0) {
handler = new ChannelHandlerAdapter();
} else if (handlers.length == 1) {
handler = handlers[0];
} else {
handler = new ChannelHandlerDispatcher(handlers);
}
return getTransporter().connect(url, handler);
}
判断handlers的数量:
- handlers数量为0,新建一个ChannelHandlerAdapter实例赋值给handler;
- handlers数量为1,将该handler赋值;
- handlers数量大于1,创建handler分发器;
最后获取Transporter实例,调用其connect方法,这边分析Transporter接口的NettyTransporter实现。
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
return new NettyClient(url, listener);
}
该方法的作用是创建并返回NettyClient实例。
这边疑惑的是创建了HeaderExchangeClient实例,初始化客户端的任务应该就完成了,为什么还要创建NettyClient实例。因为HeaderExchangeClient不具备通信能力,需要基于底层客户端通信,譬如dubbo默认的NettyClient。
2.2 RegistryProtocol.refer
RegistryProtocol的refer方法相对没那么复杂,这边简单分析一下。
public Invoker refer(Class type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY)).removeParameter(REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
return doRefer(cluster, registry, type, url);
}
step1:获取url的registry参数,将其值赋给protocol参数,移除registry参数。
step2:获取Registry实例(注册中心)。
step3:获取Invoker,有以下几种情况:
- case1:type为RegistryService,从proxyFactory中获取Invoker;
- case2:通过调用doRefer方法获取Invoker实例:
-- case2.1 group="*",doRefer方法的第一个参数Cluster通过getMergeableCluster()获得;
-- case2.2 group="a,b",doRefer方法的第一个参数Cluster即为cluster;
下面分析doRefer方法。
private Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
RegistryDirectory directory = new RegistryDirectory(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map parameters = new HashMap(directory.getUrl().getParameters());
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
registry.register(directory.getRegisteredConsumerUrl());
}
directory.buildRouterChain(subscribeUrl);
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
step1:新建RegistryDirectory实例,设置registry和protocol;
step2:从url获取参数配置,新建服务消费者url;
step3:注册服务消费者,订阅providers/configurators/routers等节点数据;
step4:通过cluster将多个服务提供者合并成一个,生成Invoker;
3.创建代理
方法调用关系如下图。
创建代理的入口方法是ProxyFactory的getProxy(Invoker
public T getProxy(Invoker invoker) throws RpcException {
return getProxy(invoker, false);
}
调用了重载方法getProxy(Invoker
@Override
public T getProxy(Invoker invoker, boolean generic) throws RpcException {
Class>[] interfaces = null;
String config = invoker.getUrl().getParameter(Constants.INTERFACES);
if (config != null && config.length() > 0) {
String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
interfaces = new Class>[types.length + 2];
interfaces[0] = invoker.getInterface();
interfaces[1] = EchoService.class;
for (int i = 0; i < types.length; i++) {
// TODO can we load successfully for a different classloader?.
interfaces[i + 2] = ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = new Class>[]{invoker.getInterface(), EchoService.class};
}
if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
int len = interfaces.length;
Class>[] temp = interfaces;
interfaces = new Class>[len + 1];
System.arraycopy(temp, 0, interfaces, 0, len);
interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
}
return getProxy(invoker, interfaces);
}
这个方法的主要作用是生成接口数组Class>[]。首先获取url的interfaces参数,分为两种情况:
- case1:url的interfaces参数值不为空。接口数组的第一个元素是从invoker获取的Interface,第二个元素是EchoService,其他元素是从interfaces参数值中解析得到的Class。
- case2:url的interfaces参数值为空。接口数组包含两个元素,第一个元素是从invoker获取的Interface,第二个元素是EchoService。
然后判断是否是泛化调用,若是则增加接口数组的最后一个元素GenericService。最后调用抽象方法getProxy,这里分析子类JavassistProxyFactory。
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
这个方法先获取Proxy的子类,然后调用其newInstance方法新建实例。看到Proxy.getProxy
public static Proxy getProxy(Class>... ics) {
return getProxy(ClassHelper.getClassLoader(Proxy.class), ics);
}
这个方法调用了重载方法getProxy(ClassLoader cl, Class>... ics)
public static Proxy getProxy(ClassLoader cl, Class>... ics) {
if (ics.length > Constants.MAX_PROXY_COUNT) {
throw new IllegalArgumentException("interface limit exceeded");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ics.length; i++) {
String itf = ics[i].getName();
if (!ics[i].isInterface()) {
throw new RuntimeException(itf + " is not a interface.");
}
Class> tmp = null;
try {
tmp = Class.forName(itf, false, cl);
} catch (ClassNotFoundException e) {
}
if (tmp != ics[i]) {
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
}
sb.append(itf).append(';');
}
// use interface class name list as key.
String key = sb.toString();
// get cache by class loader.
Map cache;
synchronized (ProxyCacheMap) {
cache = ProxyCacheMap.computeIfAbsent(cl, k -> new HashMap<>());
}
Proxy proxy = null;
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference>) {
proxy = (Proxy) ((Reference>) value).get();
if (proxy != null) {
return proxy;
}
}
if (value == PendingGenerationMarker) {
try {
cache.wait();
} catch (InterruptedException e) {
}
} else {
cache.put(key, PendingGenerationMarker);
break;
}
}
while (true);
}
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try {
ccp = ClassGenerator.newInstance(cl);
Set worked = new HashSet<>();
List methods = new ArrayList<>();
for (int i = 0; i < ics.length; i++) {
if (!Modifier.isPublic(ics[i].getModifiers())) {
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
}
ccp.addInterface(ics[i]);
for (Method method : ics[i].getMethods()) {
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc)) {
continue;
}
worked.add(desc);
int ix = methods.size();
Class> rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++) {
code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
}
code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);");
if (!Void.TYPE.equals(rt)) {
code.append(" return ").append(asArgument(rt, "ret")).append(";");
}
methods.add(method);
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
if (pkg == null) {
pkg = PACKAGE_NAME;
}
// create ProxyInstance class.
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class>[]{InvocationHandler.class}, new Class>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// create Proxy class.
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class> pc = ccm.toClass();
proxy = (Proxy) pc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// release ClassGenerator
if (ccp != null) {
ccp.release();
}
if (ccm != null) {
ccm.release();
}
synchronized (cache) {
if (proxy == null) {
cache.remove(key);
} else {
cache.put(key, new WeakReference(proxy));
}
cache.notifyAll();
}
}
return proxy;
}
step1:遍历Class数组,获取Class的类名并通过类加载器加载该类,验证加载的Class和Class数组中的是否是同一个Class,然后将类名拼接成字符串。
step2:将上一步拼接的字符串作为key,根据类加载器从ProxyCacheMap中获取cache,根据key从cache中获取value
-- case1:value属于Reference类,获取proxy并返回;
-- case2:value等于PendingGenerationMarker,等待(并发控制);
-- case3:value为空,将PendingGenerationMarker放入cache;
step3:动态生成服务接口代理类和Proxy的子类。ccp用于生成服务接口代理类,ccm用于生成Proxy的子类。
总结
至此,服务引用分析完成。值得注意的是获得的服务引用是Invoker的代理类,因此本文的重点放在了Invoker的创建和代理的创建。