dubbo provider如何对invoker进行export
如何把provider的invoker export出去:1)为原始对象加wrapper,生成invoker;2)给invoker加各种filter,启动监听服务;3)注册服务地址
以HelloService为例:
- public interface HelloService {
- String sayHello();
- }
- <dubbo:service interface="com.zhang.HelloService" ref="helloService" />
ServiceConfig和<dubbo:service>基本对应上了。
- public interface ApplicationListener {
- public void onApplicationEvent(ApplicationEvent event);
- }
- public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, ApplicationContextAware, ApplicationListener, BeanNameAware {
- public void onApplicationEvent(ApplicationEvent event) {
- if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
- if (isDelay()) {
- if (logger.isInfoEnabled()) {
- logger.info("The service ready on spring started. service: " + getInterface());
- }
- export();
- }
- }
- }
- }
- public class ServiceConfig<T> extends AbstractServiceConfig {
- private ProviderConfig provider;
- // 是否暴露
- protected Boolean export;
- private String interfaceName;
- private Class<?> interfaceClass;
- //即<dubbo:service>的ref对象
- private T ref;
- public synchronized void export() {
- if (provider != null) {
- if (export == null) {
- export = provider.getExport();
- }
- if (delay == null) {
- delay = provider.getDelay();
- }
- }
- if (export != null && ! export.booleanValue()) {
- return;
- }
- if (delay != null && delay > 0) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- try {
- Thread.sleep(delay);
- } catch (Throwable e) {
- }
- doExport();
- }
- });
- thread.setDaemon(true);
- thread.setName("DelayExportServiceThread");
- thread.start();
- } else {
- //关于export和delay的配置好像不怎么用,基本走else分支
- doExport();
- }
- }
- protected synchronized void doExport() {
- if (unexported) {
- throw new IllegalStateException("Already unexported!");
- }
- if (exported) {
- return;
- }
- if (interfaceName == null || interfaceName.length() == 0) {
- throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
- }
- checkDefault();
- if (provider != null) {
- if (application == null) {
- application = provider.getApplication();
- }
- if (registries == null) {
- registries = provider.getRegistries();
- }
- if (monitor == null) {
- monitor = provider.getMonitor();
- }
- if (protocols == null) {
- protocols = provider.getProtocols();
- }
- }
- if (application != null) {
- if (registries == null) {
- registries = application.getRegistries();
- }
- if (monitor == null) {
- monitor = application.getMonitor();
- }
- }
- if (ref instanceof GenericService) {
- interfaceClass = GenericService.class;
- generic = true;
- } else {
- try {
- interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
- .getContextClassLoader());
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException(e.getMessage(), e);
- }
- checkInterfaceAndMethods(interfaceClass, methods);
- checkRef();
- generic = false;
- }
- if(local !=null){
- if(local=="true"){
- local=interfaceName+"Local";
- }
- Class<?> localClass;
- try {
- localClass = Class.forName(local);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException(e.getMessage(), e);
- }
- if(!interfaceClass.isAssignableFrom(localClass)){
- throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
- }
- }
- if(stub !=null){
- if(stub=="true"){
- stub=interfaceName+"Stub";
- }
- Class<?> stubClass;
- try {
- stubClass = Class.forName(stub);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException(e.getMessage(), e);
- }
- if(!interfaceClass.isAssignableFrom(stubClass)){
- throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
- }
- }
- checkApplication();
- checkRegistry();
- checkProtocol();
- checkStubAndMock(interfaceClass);
- if (path == null || path.length() == 0) {
- path = interfaceName;
- }
- doExportUrls();
- exported = true;
- }
- }
一、生成invoker
ServiceConfig.doExportUrlsFor1Protocol 代码片段:
- // ref就是原始对象
- Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass,
- registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
- // protocol是Protocol$Adpative
- Exporter<?> exporter = protocol.export(invoker);
- exporters.add(exporter);
以javassist为例,默认从JavassistProxyFactory获取invoker,其实是生成wrapper。
- // JavassistProxyFactory
- public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
- // TODO Wrapper类不能正确处理带$的类名。这里的 T proxy 是原始对象
- final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
- return new AbstractProxyInvoker<T>(proxy, type, url) {
- @Override
- protected Object doInvoke(T proxy, String methodName,
- Class<?>[] parameterTypes,
- Object[] arguments) throws Throwable {
- return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
- }
- };
- }
com.alibaba.dubbo.common.bytecode.Wrapper.makeWrapper(Class<?> c) 方法中生成wrapper类,代码格式化后如下:
- public class com.alibaba.dubbo.common.bytecode.Wrapper1 extends Wrapper {
- public static String[] pns = null;
- public static Map pts = null;
- public static String[] mns = { "sayHello" };
- public static String[] dmns = { "sayHello" };
- public static Class[] mts0 = null;
- public String[] getPropertyNames() {
- return pns;
- }
- public boolean hasProperty(String n) {
- return pts.containsKey($1);
- }
- public Class getPropertyType(String n) {
- return (Class) pts.get($1);
- }
- public String[] getMethodNames() {
- return mns;
- }
- public String[] getDeclaredMethodNames() {
- return dmns;
- }
- public void setPropertyValue(Object o, String n, Object v) {
- com.zhang.HelloServiceImpl w;
- try {
- w = ((com.zhang.HelloServiceImpl) $1);
- } catch (Throwable e) {
- throw new IllegalArgumentException(e);
- }
- throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException(
- "Not found property \""
- + $2
- + "\" filed or setter method in class com.zhang.HelloServiceImpl.");
- }
- public Object getPropertyValue(Object o, String n) {
- com.zhang.HelloServiceImpl w;
- try {
- w = ((com.zhang.HelloServiceImpl) $1);
- } catch (Throwable e) {
- throw new IllegalArgumentException(e);
- }
- throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException(
- "Not found property \""
- + $2
- + "\" filed or setter method in class com.zhang.HelloServiceImpl.");
- }
- public Object invokeMethod(Object o, String n, Class[] p, Object[] v)
- throws java.lang.reflect.InvocationTargetException {
- com.zhang.HelloServiceImpl w;
- try {
- w = ((com.zhang.HelloServiceImpl) $1);
- } catch (Throwable e) {
- throw new IllegalArgumentException(e);
- }
- try {
- if ("sayHello".equals($2) && $3.length == 0) {
- return ($w) w.sayHello();
- }
- } catch (Throwable e) {
- throw new java.lang.reflect.InvocationTargetException(e);
- }
- throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException(
- "Not found method \"" + $2
- + "\" in class com.zhang.HelloServiceImpl.");
- }
- }
二、开始export。贴出Protocol$Adpative的代码,重点关注export方法:
- package com.alibaba.dubbo.rpc;
- import com.alibaba.dubbo.common.extension.ExtensionLoader;
- public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
- public void destroy() {
- throw new UnsupportedOperationException(
- "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
- }
- public int getDefaultPort() {
- throw new UnsupportedOperationException(
- "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
- }
- public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
- com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
- 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) ExtensionLoader
- .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
- .getExtension(extName);
- return extension.refer(arg0, arg1);
- }
- public com.alibaba.dubbo.rpc.Exporter export(
- com.alibaba.dubbo.rpc.Invoker arg0)
- throws com.alibaba.dubbo.rpc.Invoker {
- 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();
- // 根据invoker的url,获取扩展名
- 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) ExtensionLoader
- .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
- .getExtension(extName);
- return extension.export(arg0);
- }
- }
- package com.alibaba.dubbo.rpc.cluster;
- import com.alibaba.dubbo.common.extension.ExtensionLoader;
- public class Cluster$Adpative implements com.alibaba.dubbo.rpc.cluster.Cluster {
- public com.alibaba.dubbo.rpc.Invoker join(
- com.alibaba.dubbo.rpc.cluster.Directory arg0)
- throws com.alibaba.dubbo.rpc.cluster.Directory {
- if (arg0 == null)
- throw new IllegalArgumentException(
- "com.alibaba.dubbo.rpc.cluster.Directory argument == null");
- if (arg0.getUrl() == null)
- throw new IllegalArgumentException(
- "com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
- com.alibaba.dubbo.common.URL url = arg0.getUrl();
- String extName = url.getParameter("cluster", "failover");
- if (extName == null)
- throw new IllegalStateException(
- "Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url("
- + url.toString() + ") use keys([cluster])");
- com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader
- .getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class)
- .getExtension(extName);
- // extension是MockClusterWrapper
- return extension.join(arg0);
- }
- }
从调用栈中能看出,Protocol$Adpative.export调用了两次,在Protocol$Adpative的export函数内部,
第一次获取的protocol是 ProtocolListenerWrapper/ProtocolFilterWrapper/RegistryProtocol,
第二次获取的protocol是 ProtocolListenerWrapper/ProtocolFilterWrapper/DubboProtocol。
第一次:ServiceConfig.doExportUrlsFor1Protocol代码片段:
- if (registryURLs != null && registryURLs.size() > 0
- && url.getParameter("register", true)) {
- for (URL registryURL : registryURLs) {
- url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
- URL monitorUrl = loadMonitor(registryURL);
- if (monitorUrl != null) {
- url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
- }
- if (logger.isInfoEnabled()) {
- logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
- }
- // 获取invoker
- Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
- // protocol是Protocol$Adpative
- // invoker的url:registry://127.0.0.1:2181/xxxxx
- Exporter<?> exporter = protocol.export(invoker);
- exporters.add(exporter);
- }
- } else {
- Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
- Exporter<?> exporter = protocol.export(invoker);
- exporters.add(exporter);
- }
第二次 RegistryProtocol:
- // RegistryProtocol
- private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){
- String key = getCacheKey(originInvoker);
- ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
- if (exporter == null) {
- synchronized (bounds) {
- exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
- if (exporter == null) {
- final Invoker<?> invokerDelegete =
new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));- //protocol是com.alibaba.dubbo.rpc.Protocol$Adpative
- // invoker的url:dubbo://192.168.233.6:20880
- exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
- bounds.put(key, exporter);
- }
- }
- }
- return (ExporterChangeableWrapper<T>) exporter;
- }
暴露服务主要的步骤可能在这儿了:
- // RegistryProtocol
- public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
- //export invoker 开启服务
- final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
- //registry provider 注册服务
- final Registry registry = getRegistry(originInvoker);
- final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
- registry.register(registedProviderUrl);
- // 订阅override数据
- // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
- final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
- final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
- overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
- registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
- //保证每次export都返回一个新的exporter实例
- return new Exporter<T>() {
- public Invoker<T> getInvoker() {
- return exporter.getInvoker();
- }
- public void unexport() {
- try {
- exporter.unexport();
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- try {
- registry.unregister(registedProviderUrl);
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- try {
- overrideListeners.remove(overrideSubscribeUrl);
- registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- }
- };
- }
在DubboProtocol中export,主要是启动服务:
- // DubboProtocol
- public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
- URL url = invoker.getUrl();
- // export service.
- String key = serviceKey(url);
- //DubboExporter封装了invoker,invoker对象是RegistryProtocol$InvokerDelegete
- DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
- //把exporter放入map中
- //key: a/com.zhang.HelloService:20880
- //value: DubboExporter
- exporterMap.put(key, exporter);
- //export an stub service for dispaching event
- Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT);
- Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
- if (isStubSupportEvent && !isCallbackservice){
- String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
- if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){
- if (logger.isWarnEnabled()){
- logger.warn(new IllegalStateException("consumer [" +url.getParameter(Constants.INTERFACE_KEY) +
- "], has set stubproxy support event ,but no stub methods founded."));
- }
- } else {
- stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
- }
- }
- openServer(url);
- return exporter;
- }
- private void openServer(URL url) {
- // find server.
- String key = url.getAddress();
- //client 也可以暴露一个只有server可以调用的服务。
- boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
- if (isServer) {
- ExchangeServer server = serverMap.get(key);
- if (server == null) {
- serverMap.put(key, createServer(url));
- } else {
- //server支持reset,配合override功能使用
- server.reset(url);
- }
- }
- }
- private ExchangeServer createServer(URL url) {
- //默认开启server关闭时发送readonly事件
- url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
- //默认开启heartbeat
- url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
- String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
- if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
- throw new RpcException("Unsupported server type: " + str + ", url: " + url);
- url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
- ExchangeServer server;
- try {
- //requestHandler是DubboProtocol的一个内部类(ExchangeHandlerAdapter)
- server = Exchangers.bind(url, requestHandler);
- } catch (RemotingException e) {
- throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
- }
- str = url.getParameter(Constants.CLIENT_KEY);
- if (str != null && str.length() > 0) {
- Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
- if (!supportedTypes.contains(str)) {
- throw new RpcException("Unsupported client type: " + str);
- }
- }
- return server;
- }
绑定requestHandler
- // Exchangers
- public static ExchangeServer bind(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).bind(url, handler);
- }
- // HeaderExchanger
- public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
- return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
- }
dubbo provider如何对invoker进行export的更多相关文章
- Nodejs“实现”Dubbo Provider
背景 目前nodejs应用越来越广泛,但和java的dubbo体系接入困难,所以我们需要实现node端的dubbo provider逻辑.java的dubbo provider是和consumer在一 ...
- dubbo.provider和dubbo.consumer配置
Configure service provider <?xml version="1.0" encoding="UTF-8"?> <bean ...
- Apache Dubbo Provider默认反序列漏洞复现(CVE-2020-1948)
Apache Dubbo Provider默认反序列漏洞(CVE-2020-1948) 0x01 搭建漏洞环境 漏洞介绍 2020年06月23日, 360CERT监测发现Apache Dubbo 官方 ...
- 【CVE-2020-1948】Apache Dubbo Provider反序列化漏洞复现
一.实验简介 实验所属系列: 系统安全 实验对象:本科/专科信息安全专业 相关课程及专业: 计算机网络 实验时数(学分):2 学时 实验类别: 实践实验类 二.实验目的 Apache Dubbo是一款 ...
- dubbo源码—service export
在应用编写好服务并进行之后,dubbo负责将服务export出去,dubbo export服务的时候主要做了以下几件事: 将服务export到本地(根据scope的配置) 创建Invoker(启动本地 ...
- Dubbo实践(十三)Export
Spring在启动Dubbo服务端应用时,会实例化ServiceBean<T>并设置配置属性,然后调用export方法: @SuppressWarnings({"unchecke ...
- dubbo源码分析14——DubboProtocol的export方法分析
走到了这一步也挺不容易的,把之前的暴露入口代码再列出来回顾一下: //配置为none不暴露 if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase ...
- 【dubbo】dubbo项目基本结构及provider构建
dubbo项目基本结构如下,分别部署于不同服务器: 1.provider(接口API 实现) 2.consumer(web) 3.zookeeper 4.DB provider构建 1.api构建 i ...
- Dubbo源码学习--服务发布(ProxyFactory、Invoker)
上文分析了Dubbo服务发布的整体流程,但服务代理生成的具体细节介绍得还不是很详细.下面将会接着上文继续分析.上文介绍了服务代理生成的切入点,如下: Invoker<?> invoker ...
随机推荐
- Java的各种加密算法
Java的各种加密算法 JAVA中为我们提供了丰富的加密技术,可以基本的分为单向加密和非对称加密 1.单向加密算法 单向加密算法主要用来验证数据传输的过程中,是否被篡改过. BASE64 严格地说,属 ...
- spring mvc @ModelAttribute 每次执行requestmapping前自动执行
在不少应用场景中,我们希望在每次执行requestmapping前自动执行一些操作,比如把某些数据(比如数据字典.系统配置.标准错误号,这在企业应用系统中极为常见)塞到model中供view访问,因为 ...
- logistic回归和softmax回归
logistic回归 在 logistic 回归中,我们的训练集由 个已标记的样本构成:.由于 logistic 回归是针对二分类问题的,因此类标记 . 假设函数(hypothesis functi ...
- 关于定时器、波特率、TH和TL值的计算
假设晶振位6MHZ,定时10ms 单片机系统晶振频率为6mhz,系统时钟频率 (也是计时脉冲频率)为500KHZ,一个脉冲周期2us ,10ms是5000个脉冲,因此TMOD=0X01;TH0=(65 ...
- WinForm画网格并填充颜色
因为研究CodeCombat上的最后一题,自己尝试分解题目,然后想到需要画网格,还有最优化的方法 源代码如下 using System; using System.Collections.Generi ...
- 【第八章】 springboot + mybatis + 多数据源
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseConte ...
- BZOJ5293: [Bjoi2018]求和 树上差分
Description master 对树上的求和非常感兴趣.他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的k 次方和,而且每次的k 可能是不同的.此处节点深度的定义是这个节点 ...
- require的shim解释
通过require加载的模块一般都需要符合AMD规范即使用define来申明模块,但是部分时候需要加载非AMD规范的js,这时候就需要用到另一个功能:shim,shim解释起来也比较难理解,shim直 ...
- [Java] - MySQL数据库的时间设置问题.
之前有朋友做的项目时间格式设置为String,我感觉很不好,随后自己试了试. 首先在设置数据库类型时,选择的是timestamp, 而Java的实体中设置时间的属性类型为Date, (java.uti ...
- java自学入门心得体会 从环境配置开始
java —— 一种可以撰写跨平台应用软件的面向对象的程序设计语言. 很多教程里都要概述java语言的诞生发明.其实像图灵的”图灵机“和”图灵测试“一样,当初的java并不是这样. 是用来操控一些电冰 ...