2 dubbo与spring接入
dubbo的官方文档也说明了,dubbo可以不依赖任何Spring。这一块日后再详细说明,目前先介绍dubbo与Spring的集成。与spring的集成是基于Spring的Schema扩展进行加载
2.1 Spring对外留出的扩展
用过Spring就知道可以在xml文件中进行如下配置:
<context:component-scan base-package="com.demo.dubbo.server.serviceimpl"/>
<context:property-placeholder location="classpath:config.properties"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
Spring是如何来解析这些配置呢?如果我们想自己定义配置该如何做呢?
对于上述的xml配置,分成三个部分
- 命名空间namespace,如tx、context
- 元素element,如component-scan、property-placeholder、annotation-driven
- 属性attribute,如base-package、location、transaction-manager
Spring定义了两个接口,来分别解析上述内容:
- NamespaceHandler:注册了一堆BeanDefinitionParser,利用他们来进行解析
- BeanDefinitionParser: 用于解析每个element的内容
来看下具体的一个案例,就以Spring的context命名空间为例,对应的NamespaceHandler实现是ContextNamespaceHandler:
public classContextNamespaceHandlerextendsNamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
注册了一堆BeanDefinitionParser,如果我们想看"component-scan"是如何实现的,就可以去看对应的ComponentScanBeanDefinitionParser的源码了
如果自定义了NamespaceHandler,如何加入到Spring中呢?
Spring默认会在加载jar包下的 META-INF/spring.handlers文件下寻找NamespaceHandler,默认的Spring文件如下:
文件内容如下:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
相应的命名空间使用相应的NamespaceHandler
2.2 dubbo的接入实现
dubbo就是自定义类型的,所以也要给出NamespaceHandler、BeanDefinitionParser。NamespaceHandler是DubboNamespaceHandler:
public classDubboNamespaceHandlerextendsNamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
}
}
给出的BeanDefinitionParser全部是DubboBeanDefinitionParser,如果我们想看看<dubbo:registry>是怎么解析的,就可以去看看DubboBeanDefinitionParser的源代码。
而dubbo的jar包下,存在着META-INF/spring.handlers文件,内容如下:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
具体解析过程就不再说明了。结果就是不同的配置分别转换成Spring容器中的一个bean对象。
- application对应ApplicationConfig
- registry对应RegistryConfig
- monitor对应MonitorConfig
- provider对应ProviderConfig
- consumer对应ConsumerConfig
- protocol对应ProtocolConfig
- service对应ServiceConfig
- reference对应ReferenceConfig
上面的对象不依赖Spring,也就是说你可以手动去创建上述对象。
为了在Spring启动的时候,也相应的启动provider发布服务注册服务的过程:又加入了一个和Spring相关联的ServiceBean,继承了ServiceConfig
为了在Spring启动的时候,也相应的启动consumer发现服务的过程:又加入了一个和Spring相关联的ReferenceBean,继承了ReferenceConfig
利用Spring就做了上述过程,得到相应的配置数据,然后启动相应的服务。如果想剥离Spring,我们就可以手动来创建上述配置对象,通过ServiceConfig和ReferenceConfig的API来启动相应的服务
3 服务的发布过程
3.1 案例介绍
从上面知道,利用Spring的解析收集到很多一些配置,然后将这些配置都存至ServiceConfig中,然后调用ServiceConfig的export()方法来进行服务的发布与注册
先看一个简单的服务端例子,dubbo配置如下:
<dubbo:application name="helloService-app" />
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" />
<dubbo:service interface="com.demo.dubbo.service.HelloService" ref="helloService" />
<bean id="helloService" class="com.demo.dubbo.server.serviceimpl.HelloServiceImpl"/>
- 有一个服务接口,HelloService,以及它对应的实现类HelloServiceImpl
- 将HelloService标记为dubbo服务,使用HelloServiceImpl对象来提供具体的服务
- 使用zooKeeper作为注册中心
3.2 服务发布过程
一个服务可以有多个注册中心、多个服务协议
多注册中心信息:
首选根据注册中心配置,即上述的ZooKeeper配置信息,将注册信息聚合在一个URL对象中,registryURLs内容如下:
[registry://192.168.1.104:2181/com.alibaba.dubbo.registry.RegistryService?application=helloService-app&localhost=true®istry=zookeeper]
多协议信息:
由于上述我们没有配置任何协议信息,就会使用默认的dubbo协议,开放在20880端口,也就是在该端口,对外提供上述的HelloService服务,注册的协议信息也转化成一个URL对象,如下:
dubbo://192.168.1.104:20880/com.demo.dubbo.service.HelloService?anyhost=true&application=helloService-app&dubbo=2.0.13&interface=com.demo.dubbo.service.HelloService&methods=hello&prompt=dubbo&revision=
依据注册中心信息和协议信息的组合起来,依次来进行服务的发布。整个过程伪代码如下:
List<URL> registryURLs = loadRegistries();
for (ProtocolConfig protocolConfig : protocols) {
//根据每一个协议配置构建一个URL
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
for (URL registryURL : registryURLs) {
String providerURL = url.toFullString();
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(RpcConstants.EXPORT_KEY, providerURL));
Exporter<?> exporter = protocol.export(invoker);
}
}
所以服务发布过程大致分成两步:
- 第一步:通过ProxyFactory将HelloServiceImpl封装成一个Invoker
- 第二步:使用Protocol将invoker导出成一个Exporter
这里面就涉及到几个大的概念。ProxyFactory、Invoker、Protocol、Exporter。下面来一一介绍
3.3 概念介绍
分别介绍下Invoker、ProxyFactory、Protocol、Exporter的概念
3.3.1 Invoker概念
Invoker: 一个可执行的对象,能够根据方法名称、参数得到相应的执行结果。接口内容简略如下:
public interfaceInvoker<T> {
Class<T> getInterface();
URL getUrl();
Result invoke(Invocation invocation)throws RpcException;
voiddestroy();
}
而Invocation则包含了需要执行的方法、参数等信息,接口定义简略如下:
public interface Invocation {
URL getUrl();
String getMethodName();
Class<?>[] getParameterTypes();
Object[] getArguments();
}
目前其实现类只有一个RpcInvocation。内容大致如下:
public classRpcInvocationimplementsInvocation, Serializable {
private String methodName;
private Class<?>[] parameterTypes;
private Object[] arguments;
private transient URL url;
}
仅仅提供了Invocation所需要的参数而已,继续回到Invoker
这个可执行对象的执行过程分成三种类型:
-
类型1:本地执行类的Invoker
-
类型2:远程通信执行类的Invoker
-
类型3:多个类型2的Invoker聚合成的集群版的Invoker
以HelloService接口方法为例:
-
本地执行类的Invoker: server端,含有对应的HelloServiceImpl实现,要执行该接口方法,仅仅只需要通过反射执行HelloServiceImpl对应的方法即可
-
远程通信执行类的Invoker: client端,要想执行该接口方法,需要需要进行远程通信,发送要执行的参数信息给server端,server端利用上述本地执行的Invoker执 行相应的方法,然后将返回的结果发送给client端。这整个过程算是该类Invoker的典型的执行过程
-
集群版的Invoker:client端,拥有某个服务的多个Invoker,此时client端需要做的就是将这个多个Invoker聚 合成一个集群版的Invoker,client端使用的时候,仅仅通过集群版的Invoker来进行操作。集群版的Invoker会从众多的远程通信类型 的Invoker中选择一个来执行(从中加入负载均衡策略),还可以采用一些失败转移策略等
所以来看下Invoker的实现情况:
3.3.2 ProxyFactory概念
对于server端,主要负责将服务如HelloServiceImpl统一进行包装成一个Invoker,这些Invoker通过反射来执行具体的HelloServiceImpl对象的方法。
接口定义如下:
@Extension("javassist")
public interfaceProxyFactory{
//针对client端,创建出代理对象
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker)throws RpcException;
//针对server端,将服务对象如HelloServiceImpl包装成一个Invoker对象
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url)throws RpcException;
}
ProxyFactory的接口实现有JdkProxyFactory、JavassistProxyFactory,默认是JavassistProxyFactory, JdkProxyFactory内容如下:
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
};
}
可以看到是创建了一个AbstractProxyInvoker(这类就是本地执行的Invoker),它对Invoker的Result invoke(Invocation invocation)实现如下:
public Result invoke(Invocation invocation)throws RpcException {
try {
return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
} catch (InvocationTargetException e) {
return new RpcResult(e.getTargetException());
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy " + invocation + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
综上所述,服务发布的第一个过程就是:
使用ProxyFactory将HelloServiceImpl封装成一个本地执行的Invoker。
3.3.3 Protocol概念
从上面得知服务发布的第一个过程就是:
使用ProxyFactory将HelloServiceImpl封装成一个本地执行的Invoker。
执行这个服务,即执行这个本地Invoker,即调用这个本地Invoker的invoke(Invocation invocation)方法,方法的执行过程就是通过反射执行了HelloServiceImpl的内容。现在的问题是:这个方法的参数 Invocation invocation的来源问题。
针对server端来说,Protocol要解决的问题就是:根据指定协议对外公布这个HelloService服务,当客户端根据协议调用这个服 务时,将客户端传递过来的Invocation参数交给上述的Invoker来执行。所以Protocol加入了远程通信协议的这一块,根据客户端的请求 来获取参数Invocation invocation。
先来看下Protocol的接口定义:
@Extension("dubbo")
public interfaceProtocol{
intgetDefaultPort();
//针对server端来说,将本地执行类的Invoker通过协议暴漏给外部。这样外部就可以通过协议发送执行参数Invocation,然后交给本地Invoker来执行
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker)throws RpcException;
//这个是针对客户端的,客户端从注册中心获取服务器端发布的服务信息
//通过服务信息得知服务器端使用的协议,然后客户端仍然使用该协议构造一个Invoker。这个Invoker是远程通信类的Invoker。
//执行时,需要将执行信息通过指定协议发送给服务器端,服务器端接收到参数Invocation,然后交给服务器端的本地Invoker来执行
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url)throws RpcException;
voiddestroy();
}
我们再来详细看看服务发布的第二步:
Exporter<?> exporter = protocol.export(invoker);
protocol的来历是:
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
我们从第一篇文章dubbo源码分析系列(1)扩展机制的实现,可以知道上述获取Protocol protocol的原理,这里就不再多说了,直接贴出最终的Protocol的实现代码:
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException{
if (arg0 == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) {
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException{
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) {
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
export(Invoker invoker)的过程即根据Invoker中url的配置信息来最终选择的Protocol实现,默认实现是"dubbo"的扩展实现即 DubboProtocol,然后再对DubboProtocol进行依赖注入,进行wrap包装。先来看看Protocol的实现情况:
可以看到在返回DubboProtocol之前,经过了ProtocolFilterWrapper、ProtocolListenerWrapper、RegistryProtocol的包装。
所谓的包装就是如下类似的内容:
package com.alibaba.xxx;
import com.alibaba.dubbo.rpc.Protocol;
public class XxxProtocolWrapper implemenets Protocol {
Protocol impl;
public XxxProtocol(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public Exporter<T> export(final Invoker<T> invoker) {
//... 一些操作
impl .export(invoker);
// ... 一些操作
}
// ...
}
使用装饰器模式,类似AOP的功能。
下面主要讲解RegistryProtocol和DubboProtocol,先暂时忽略ProtocolFilterWrapper、ProtocolListenerWrapper
所以上述服务发布的过程
Exporter<?> exporter = protocol.export(invoker);
会先经过RegistryProtocol,它干了哪些事呢?
-
利用内部的Protocol即DubboProtocol,将服务进行导出,如下
exporter = protocol.export(new InvokerWrapper<T>(invoker, url));
-
根据注册中心的registryUrl获取注册服务Registry,然后将serviceUrl注册到注册中心上,供客户端订阅
Registry registry = registryFactory.getRegistry(registryUrl); registry.register(serviceUrl)
来详细看看上述DubboProtocol的服务导出功能:
-
首先根据Invoker的url获取ExchangeServer通信对象(负责与客户端的通信模块),以url中的host和port作 为key存至Map<String, ExchangeServer> serverMap中。即可以采用全部服务的通信交给这一个ExchangeServer通信对象,也可以某些服务单独使用新的 ExchangeServer通信对象。
String key = url.getAddress(); //client 也可以暴露一个只有server可以调用的服务。 boolean isServer = url.getParameter(RpcConstants.IS_SERVER_KEY,true); if (isServer && ! serverMap.containsKey(key)) {
serverMap.put(key, getServer(url));
}
-
创建一个DubboExporter,封装invoker。然后根据url的port、path(接口的名称)、版本号、分组号作为 key,将DubboExporter存至Map<String, Exporter<?>> exporterMap中
key = serviceKey(url); DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap); exporterMap.put(key, exporter);
现在我们要搞清楚我们的目的:通过通信对象获取客户端传来的Invocation invocation参数,然后找到对应的DubboExporter(即能够获取到本地Invoker)就可以执行服务了。
上述每一个ExchangeServer通信对象都绑定了一个ExchangeHandler requestHandler对象,内容简略如下:
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
public Object reply(ExchangeChannel channel, Object message)throws RemotingException {
if (message instanceof Invocation) {
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
return invoker.invoke(inv);
}
throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
};
可以看到在获取到Invocation参数后,调用getInvoker(channel, inv)来获取本地Invoker。获取过程就是根据channel获取port,根据Invocation inv信息获取要调用的服务接口、版本号、分组号等,以此组装成key,从上述Map<String, Exporter<?>> exporterMap中获取Exporter,然后就可以找到对应的Invoker了,就可以顺利的调用服务了。
而对于通信这一块,接下来会专门来详细的说明。
3.3.4 Exporter概念
负责维护invoker的生命周期。接口定义如下:
public interfaceExporter<T> {
Invoker<T> getInvoker();
voidunexport();
}
包含了一个Invoker对象。一旦想撤销该服务,就会调用Invoker的destroy()方法,同时清理上述exporterMap中的数据。对于RegistryProtocol来说就需要向注册中心撤销该服务。
4 结束语
本文简略地介绍了接入Spring过程的原理,以及服务发布过程中的几个概念。接下来的打算是:
- 客户端订阅服务与使用服务涉及的概念
- 注册中心模块
- 客户端与服务器端网络通信模块
相关推荐
《Dubbo源码分析系列》是一份深入探讨Java开源框架Dubbo核心原理和技术细节的资料。Dubbo,作为阿里巴巴的一款高性能、轻量级的服务治理框架,它为分布式系统提供了服务发现、调用、负载均衡、容错等关键功能。这份...
接下来,“dubbo源码分析系列.pdf”则可能更侧重于实际案例和实践,它可能会包含: 1. **实战案例**:通过具体的项目实例,展示如何在实际开发中使用Dubbo,以及如何解决遇到的问题。 2. **源码解读**:针对Dubbo...
《深入剖析Dubbo源码系列》 Dubbo作为国内最广泛使用的分布式服务框架,其设计思想和实现机制对于理解和构建自己的分布式服务框架至关重要。本文将基于当当网维护的dubbox版本,逐步解析Dubbo的核心模块,以帮助...
《Dubbo源码解析:深度探索高性能服务框架》 Dubbo作为阿里巴巴开源的一款高性能、轻量级的服务治理框架,其源码的深入理解和分析对于Java开发者来说,具有极高的学习价值。本文将针对提供的dubbo-2.4.3-sources....
【Dubbo源码视频教程】是一系列深入解析著名开源框架Dubbo源代码的教程资源,旨在帮助开发者深入了解Dubbo的工作原理、设计模式以及关键模块的实现。Dubbo是中国阿里巴巴公司开源的一款高性能、轻量级的Java服务治理...
在深入理解Dubbo源码之前,需要掌握一系列基础知识,这将有助于更好地理解Dubbo的设计与实现。 1. **Java语言编程**:熟悉Java语言的基本语法和面向对象特性,对于理解Dubbo中的各种类、接口以及方法非常重要。 2. ...
Dubbo作为一个由阿里巴巴开源的高性能服务框架,被广泛应用于微服务架构中,它提供了包括服务注册、服务发现、服务调用、负载均衡以及服务监控等一系列功能。本教程将深入讲解如何利用Dubbo、Zookeeper和SpringMVC...
2. **源码示例**:可能包含了使用Java编写的Dubbo服务端和客户端示例代码,展示了如何实现服务的发布、订阅和调用。 3. **配置文件**:如XML或properties格式的配置文件,展示如何配置服务注册中心(如Zookeeper)、...
在【Java】平台上,Dubbo通过简单的API,使得开发者可以方便地实现服务的发布和消费,从而构建高可用、高扩展性的分布式系统。Java作为广泛使用的编程语言,其丰富的库和社区支持为Dubbo的使用提供了坚实的基础。 ...
Dubbo是由阿里巴巴开源的一款高性能、轻量级的Java RPC框架,它提供了服务发布、消费、调用拦截、负载均衡、容错处理等一系列完整的服务治理功能。Dubbo的核心设计目标是面向接口的编程,通过代理模式实现了服务的...
Dubbo是阿里巴巴开源的一款高性能、轻量级的Java远程服务调用框架,而dubbo-admin则是其配套的监控和管理工具,允许开发者对服务进行注册、发现、调用跟踪、性能分析等一系列操作。 【描述】提到"同github上的一样...
【标题】:“dubbo源码”意味着我们要探讨的是著名开源框架Dubbo的核心代码。Dubbo是阿里巴巴开源的一个高性能、轻量级的Java分布式服务框架,它致力于提供面向接口的透明化远程调用、智能容错和负载均衡能力,以及...
首先,Dubbo是阿里巴巴开源的一款高性能、轻量级的服务治理框架,它主要应用于Java环境,用于实现服务的发布、发现、调用、负载均衡以及监控等。Dubbo的核心组件包括服务提供者、服务消费者、注册中心和服务监控。在...
基于Spring Boot的微服务开发框架,包含了服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、配置中心(Config)等一系列组件,构建完整的分布式系统。 3. **gRPC**: Google推出的高性能、开源和通用...
通过源码分析,你可以更深入地理解这些组件的工作原理,例如Nacos的服务发现机制、Sentinel的流量控制算法、Dubbo的RPC通信过程等。这将有助于你在实际开发中更好地优化系统性能和稳定性。 总的来说,《Spring ...
#### 十三、源码分析 1. **Spring启动容器** - 通过`AnnotationConfigApplicationContext.refresh()`方法实现。 - 核心步骤包括环境配置、BeanFactory创建、BeanFactory预处理、Web生命周期管理等。
7. **源码分析**: - **阿里巴巴开源项目**:手册可能包含了阿里巴巴内部开源项目的源码解析,如Dubbo、RocketMQ、Druid等,帮助开发者理解大型项目的设计思路。 通过遵循《阿里巴巴Java开发手册》中的指导,...
Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来...