- 浏览: 69222 次
- 性别:
- 来自: 成都
-
最新评论
-
qiaohhgz:
public static void String chang ...
一图和几句话解决java参数传值问题 -
ocaicai:
虽然我看懂了你的意思,但是我依然觉得表达得有些欠妥!
一图和几句话解决java参数传值问题 -
ql0722:
特别赞同第二条第三条
如果我再年轻几岁 -
zzc_zj:
很好的知识点,学习了
数据库查询select原理 -
dai03070609:
[url][/url][flash=200,200][/fla ...
数据库查询select原理
Request Objects
在默认的连接器中org.apache.catalina.Request接口代表了一个HTTP请求对象。HttpRequest的父类—RequestBase类直接实现了这个接口。最终的实现是HttpRequestImpl,它继承HttpRequest。在第三章中,有facade类:RequstFacade和HttpRequestFacade。
Response Objects
Processing Requests
现在,你已经了解了request、response对象和HttpConnector对象怎么创建它们。这部分我们关注HttpProcessor类的process方法,这个方法是HttpProcessor类的被指配了一个socket后的run方法里调用。这个process方法处理一下工作:
解析连接
解析请求
解析头部信息
process方法使用boolean变量ok来表示在处理过程中是否出错和使用boolean变量finishResponse来表示Response接口的finishResponse方法应该被调用。
此外,http11.keepAlive表明了这个连接时持久的,stopped表明HttpProcessor实例连接器
process方法也应该停止,http11表明一个web客户端到来HTTP请求从支持HTTP1.1。
象第三章,一个SocketInputStream实例被用来包装socket的输入流。注意:SocketInputStream的构造函数也传递从连接器得到的buffer size,不是从HttpProcessor类的一个局部变量得到的。这是因为HttpProcessor不能被默认连接器的用户访问。通过把buffer size放进Connector接口,这就允许任何一个人使用连接器来设置buffer size。
然后,有一个while循环保持读取输入流,知道HttpProcessor停止、一个异常抛出或者是连接被关闭。
在这个while循环里,process方法在finishResponse被设为true、获取输出流、处理一些request和resposne对象的初始化后开始执行。
后来,process方法通过调用parseConnection,parseRequest和parseHeader方法来解析到来的HTTP请求。
parseConnection方法获取协议的值。可以使:HTTP0.9,HTTP1.0或者是HTTP1.1。如果协议是HTTP1.0,keepAlive的boolean值设置成false,因为HTTP1.0不支持持久连接。parseHeader方法设置sendAck的boolean值为true(但在100-continue头信息被HTTP请求发现除外)
如果协议时HTTP1.1,如果web客户端发送了100—continue头部信息,它还要检查块编码是否允许。
AckRequest方法检查sendAck的值和在sendAckweitrue时发送下面的字符串:
HTTP/1.1 100 Continue\r\n\r\n
在解析HTTP请求期间,可能会抛出异常。任何异常将设置ok或finishResponse为false。在解析后,process方法把request和response对象传递给容器的invoke方法。
最后,如果finishResponse任然为true,调用response对象的finishResponse方法和request对象的finishRequest方法,把输出flush。
while循环的最后一部分是检查response的Connection头部信息在servlet或HTTP1.0协议中是否被设置成colse。如果是这种情况,keepAlive被设置成false。同样的,request和response对象被回收。
While循环如果keepAlive是true,在之前的解析和容器的invoke方法没有错误,或者HttpProcessor实例没有停止。shutdownInput方法被调用,关闭socket。
shutdownInput方法检查是否有没有读取的字节。如果有,跳过这些字节。
Parsing the Connection
parseConnection方法包含来自socket的Internet地址,并把它指配给HttpRequestImpl对象。它也检查代理是否被使用,并把它指配给request对象。
Parsing the Request
parseRequest方法跟第三章的方法相似。
Parsing Headers
在默认连接器的parseHeaders方法使用org.apache.catalina.connector.http包中的HttpHeader和DefaultHeaders类。HttpHeader类代表一个HTTP请求头部信息。第三章使用字符串。HttpHeader类使用字符数组来避免字符串操作的开销。DefaultHeaders类是一个final类,它在字符数组中包含标准的HTTP请求头部信息。
parseHeaders方法包含一个while循环,保持读取HTTP请求,直到没有更多的头部信息可读取。while循环调用request对象allocateHeader方法来获取一个空的HttpHeader实例后开始。这个实例被传递给SocketInputStream的readHeader方法。
如果所有头部信息被读取,readHeader方法
如果有一个header name,这里就会有一个header value与之对应。
String value = new String(header.value, 0, header.valueEnd);
接下来,就象第三章,parseHeaders方法让header name和在DefaultHeaders的标准name相比较。
注意:是在两个字符数组比较,而不是字符串。
The Simple Container Application
ex04.pyrmont.core.SimpleContainer和ex04 pyrmont.startup.Bootstrap。
这里只提供了SimpleContainer类的invoke方法的实现。因为默认的连接器将调用这个方法。invoke方法创建一个类加载器,加载servlet类,调用它的servlet的service方法。
Bootstrap类的main方法构建了一个org.apache.catalina.connector.http.HttpConnector的实例和一个SimpleContainer接口。然后调用连接器的setContainer方法把连接器和容器联系起来。接下来,它调用连接器的initialize和start方法。它将让连接器处理在8080端口上的任何HTTP请求。
第四章 完
在默认的连接器中org.apache.catalina.Request接口代表了一个HTTP请求对象。HttpRequest的父类—RequestBase类直接实现了这个接口。最终的实现是HttpRequestImpl,它继承HttpRequest。在第三章中,有facade类:RequstFacade和HttpRequestFacade。
![](http://dl.iteye.com/upload/attachment/354797/4450f554-f185-33be-90bb-18641a566ea9.jpg)
Response Objects
![](http://dl.iteye.com/upload/attachment/354799/9d93724a-b493-37bc-9973-7c17ac09b1d9.jpg)
Processing Requests
现在,你已经了解了request、response对象和HttpConnector对象怎么创建它们。这部分我们关注HttpProcessor类的process方法,这个方法是HttpProcessor类的被指配了一个socket后的run方法里调用。这个process方法处理一下工作:
解析连接
解析请求
解析头部信息
process方法使用boolean变量ok来表示在处理过程中是否出错和使用boolean变量finishResponse来表示Response接口的finishResponse方法应该被调用。
boolean ok = true; boolean finishResponse = true;
此外,http11.keepAlive表明了这个连接时持久的,stopped表明HttpProcessor实例连接器
process方法也应该停止,http11表明一个web客户端到来HTTP请求从支持HTTP1.1。
象第三章,一个SocketInputStream实例被用来包装socket的输入流。注意:SocketInputStream的构造函数也传递从连接器得到的buffer size,不是从HttpProcessor类的一个局部变量得到的。这是因为HttpProcessor不能被默认连接器的用户访问。通过把buffer size放进Connector接口,这就允许任何一个人使用连接器来设置buffer size。
OutputStream output = null; // Construct and initialize the objects we will need try { input = new SocketInputStream(socket.getInputstream(), connector.getBufferSize()); } catch (Exception e) { ok = false; }
然后,有一个while循环保持读取输入流,知道HttpProcessor停止、一个异常抛出或者是连接被关闭。
keepAlive = true; while (!stopped && ok && keepAlive) { ... }
在这个while循环里,process方法在finishResponse被设为true、获取输出流、处理一些request和resposne对象的初始化后开始执行。
finishResponse = true; try { request.setStream(input); request.setResponse(response); output = socket.getOutputStream(); response.setStream(output); response.setRequest(request); ((HttpServletResponse) response.getResponse()).setHeader ("Server", SERVER_INFO); } catch (Exception e) { log("process.create", e); //logging is discussed in Chapter 7 ok = false; }
后来,process方法通过调用parseConnection,parseRequest和parseHeader方法来解析到来的HTTP请求。
try { if (ok) { parseConnection(socket); parseRequest(input, output); if (!request.getRequest().getProtocol() .startsWith("HTTP/0")) parseHeaders(input);
parseConnection方法获取协议的值。可以使:HTTP0.9,HTTP1.0或者是HTTP1.1。如果协议是HTTP1.0,keepAlive的boolean值设置成false,因为HTTP1.0不支持持久连接。parseHeader方法设置sendAck的boolean值为true(但在100-continue头信息被HTTP请求发现除外)
如果协议时HTTP1.1,如果web客户端发送了100—continue头部信息,它还要检查块编码是否允许。
if (http11) { // Sending a request acknowledge back to the client if // requested. ackRequest(output); // If the protocol is HTTP/1.1, chunking is allowed. if (connector.isChunkingAllowed()) response.setAllowChunking(true); }
AckRequest方法检查sendAck的值和在sendAckweitrue时发送下面的字符串:
HTTP/1.1 100 Continue\r\n\r\n
在解析HTTP请求期间,可能会抛出异常。任何异常将设置ok或finishResponse为false。在解析后,process方法把request和response对象传递给容器的invoke方法。
try { ((HttpServletResponse) response).setHeader ("Date", FastHttpDateFormat.getCurrentDate()); if (ok) { connector.getContainer().invoke(request, response); } }
最后,如果finishResponse任然为true,调用response对象的finishResponse方法和request对象的finishRequest方法,把输出flush。
if (finishResponse) { ... response.finishResponse(); ... request.finishRequest(); ... output.flush();
while循环的最后一部分是检查response的Connection头部信息在servlet或HTTP1.0协议中是否被设置成colse。如果是这种情况,keepAlive被设置成false。同样的,request和response对象被回收。
if ( "close".equals(response.getHeader("Connection")) ) { keepAlive = false; } // End of request processing status = Constants.PROCESSOR_IDLE; // Recycling the request and the response objects request.recycle(); response.recycle(); }
While循环如果keepAlive是true,在之前的解析和容器的invoke方法没有错误,或者HttpProcessor实例没有停止。shutdownInput方法被调用,关闭socket。
try { shutdownInput(input); socket.close(); } ...
shutdownInput方法检查是否有没有读取的字节。如果有,跳过这些字节。
Parsing the Connection
parseConnection方法包含来自socket的Internet地址,并把它指配给HttpRequestImpl对象。它也检查代理是否被使用,并把它指配给request对象。
private void parseConnection(Socket socket) throws IOException, ServletException { if (debug >= 2) log(" parseConnection: address=" + socket.getInetAddress() + ", port=" + connector.getPort()); ((HttpRequestImpl) request).setInet(socket.getInetAddress()); if (proxyPort != 0) request.setServerPort(proxyPort); else request.setServerPort(serverPort); request.setSocket(socket); }
Parsing the Request
parseRequest方法跟第三章的方法相似。
Parsing Headers
在默认连接器的parseHeaders方法使用org.apache.catalina.connector.http包中的HttpHeader和DefaultHeaders类。HttpHeader类代表一个HTTP请求头部信息。第三章使用字符串。HttpHeader类使用字符数组来避免字符串操作的开销。DefaultHeaders类是一个final类,它在字符数组中包含标准的HTTP请求头部信息。
static final char[] AUTHORIZATION_NAME = "authorization".toCharArray(); static final char[] ACCEPT_LANGUAGE_NAME = "accept-language".toCharArray(); static final char[] COOKIE_NAME = "cookie".toCharArray(); ...
parseHeaders方法包含一个while循环,保持读取HTTP请求,直到没有更多的头部信息可读取。while循环调用request对象allocateHeader方法来获取一个空的HttpHeader实例后开始。这个实例被传递给SocketInputStream的readHeader方法。
HttpHeader header = request.allocateHeader(); // Read the next header input.readHeader(header);
如果所有头部信息被读取,readHeader方法
if (header.nameEnd == 0) { if (header.valueEnd == 0) { return; } else { throw new ServletException (sm.getString("httpProcessor.parseHeaders.colon")); } }
如果有一个header name,这里就会有一个header value与之对应。
String value = new String(header.value, 0, header.valueEnd);
接下来,就象第三章,parseHeaders方法让header name和在DefaultHeaders的标准name相比较。
注意:是在两个字符数组比较,而不是字符串。
if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) { request.setAuthorization(value); } else if (header.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) { parseAcceptLanguage(value); } else if (header.equals(DefaultHeaders.COOKIE_NAME)) { // parse cookie } else if (header.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) { // get content length } else if (header.equals(DefaultHeaders.CONTENT_TYPE_NAME)) { request.setContentType(value); } else if (header.equals(DefaultHeaders.HOST_NAME)) { // get host name } else if (header.equals(DefaultHeaders.CONNECTION_NAME)) { if (header.valueEquals(DefaultHeaders.CONNECTION_CLOSE_VALUE)) { keepAlive = false; response.setHeader("Connection", "close"); } } else if (header.equals(DefaultHeaders.EXPECT_NAME)) { if (header.valueEquals(DefaultHeaders.EXPECT_100_VALUE)) sendAck = true; else throw new ServletException(sm.getstring ("httpProcessor.parseHeaders.unknownExpectation")); } else if (header.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) { //request.setTransferEncoding(header); } request.nextHeader();
The Simple Container Application
ex04.pyrmont.core.SimpleContainer和ex04 pyrmont.startup.Bootstrap。
Listing 4.3: The SimpleContainer class package ex04.pyrmont.core; import java.beans.PropertyChangeListener; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; import java.io.File; import java.io.IOException; import javax.naming.directory.DirContext; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Cluster; import org.apache.catalina.Container; import org.apache.catalina.ContainerListener; import org.apache.catalina.Loader; import org.apache.catalina.Logger; import org.apache.catalina.Manager; import org.apache.catalina.Mapper; import org.apache.catalina.Realm; import org.apache.catalina.Request; import org.apache.catalina.Response; public class SimpleContainer implements Container { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; public SimpleContainer() { } public String getInfo() { return null; } public Loader getLoader() { return null; } public void setLoader(Loader loader) { } public Logger getLogger() { return null; } public void setLogger(Logger logger) { } public Manager getManager() { return null; } public void setCluster(Cluster cluster) { } public String getName() { return null; } public void setName(String name) { } public Container getParent() { return null; } public void setParent(Container container) { } public ClassLoader getParentClassLoader() { return null; } public void setParentClassLoader(ClassLoader parent) { } public Realm getRealm() { return null; } public void setRealm(Realm realm) { } public DirContext getResources() { return null; } public void setResources(DirContext resources) { } public void addChild(Container child) { } public void addContainerListener(ContainerListener listener) { } public void addMapper(Mapper mapper) { } public void addPropertyChangeListener( PropertyChangeListener listener) { } public Container findchild(String name) { return null; } public Container[] findChildren() { return null; } public ContainerListener[] findContainerListeners() { return null; } public Mapper findMapper(String protocol) { return null; } public Mapper[] findMappers() { return null; } public void invoke(Request request, Response response) throws IoException, ServletException { string servletName = ( (Httpservletrequest) request).getRequestURI(); servletName = servletName.substring(servletName.lastIndexof("/") + 1); URLClassLoader loader = null; try { URL[] urls = new URL[1]; URLStreamHandler streamHandler = null; File classpath = new File(WEB_ROOT); string repository = (new URL("file",null, classpath.getCanonicalpath() + File.separator)).toString(); urls[0] = new URL(null, repository, streamHandler); loader = new URLClassLoader(urls); } catch (IOException e) { System.out.println(e.toString() ); } Class myClass = null; try { myClass = loader.loadclass(servletName); } catch (classNotFoundException e) { System.out.println(e.toString()); } servlet servlet = null; try { servlet = (Servlet) myClass.newInstance(); servlet.service((HttpServletRequest) request,(HttpServletResponse) response); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } } public Container map(Request request, boolean update) { return null; } public void removeChild(Container child) { } public void removeContainerListener(ContainerListener listener) { } public void removeMapper(Mapper mapper) { } public void removeMapper(Mapper mapper) { } public void removoPropertyChangeListener( PropertyChangeListener listener) { } }
这里只提供了SimpleContainer类的invoke方法的实现。因为默认的连接器将调用这个方法。invoke方法创建一个类加载器,加载servlet类,调用它的servlet的service方法。
Listing 4.4: The ex04.pyrmont.startup.Bootstrap class package ex04.pyrmont.startup; import ex04.pyrmont.core.simplecontainer; import org.apache.catalina.connector.http.HttpConnector; public final class Bootstrap { public static void main(string[] args) { HttpConnector connector = new HttpConnector(); SimpleContainer container = new SimpleContainer(); connector.setContainer(container); try { connector.initialize(); connector.start(); // make the application wait until we press any key. System in.read(); } catch (Exception e) { e.printStackTrace(); } } }
Bootstrap类的main方法构建了一个org.apache.catalina.connector.http.HttpConnector的实例和一个SimpleContainer接口。然后调用连接器的setContainer方法把连接器和容器联系起来。接下来,它调用连接器的initialize和start方法。它将让连接器处理在8080端口上的任何HTTP请求。
第四章 完
发表评论
-
MyEclipse插件安装
2012-03-22 10:05 4321:先把plugin的jar文件复制到一个文件夹下 如:C:/ ... -
Hibernate框架使用技术简述
2011-03-24 10:14 1138(1)持久化对象的操 ... -
Coder 爱翻译 How Tomcat Works 第九章 第二部分
2011-01-24 15:20 1201The ManagerBase Class ManagerB ... -
Coder 爱翻译 How Tomcat Works 第九章 第一部分
2010-12-16 20:40 1283Chapter 9: Session Management ... -
Coder 爱翻译 How Tomcat Works 第八章 第二部分
2010-12-12 18:31 1527The Loader Interface 在web应 ... -
Coder 爱翻译 How Tomcat Works 第八章 第一部分
2010-12-06 11:14 1102Chapter 8: Loader 在前几章 ... -
Coder 爱翻译 How Tomcat Works 第七章
2010-12-05 16:29 1221Chapter 7: Logger 日志是一 ... -
Coder 爱翻译 How Tomcat Works 第六章
2010-12-04 22:09 1189Chapter 6: Lifecycle Catalina是 ... -
Coder 爱翻译 How Tomcat Works 第五章 第三部分
2010-12-03 13:31 1186The Context Application 这章的第一个 ... -
Coder 爱翻译 How Tomcat Works 第五章 第二部分
2010-12-03 12:33 3141The Pipeline Interface 我们提到的Pi ... -
Coder 爱翻译 How Tomcat Works 第五章 第一部分
2010-11-28 16:36 1195Chapter 5: Container 一个容器是一个为s ... -
Coder 爱翻译 How Tomcat Works 第四章 第一部分
2010-11-25 16:38 958Chapter 4: Tomcat Default Conne ... -
PreparedStatement字符串拼接
2010-11-18 17:21 1413这在里求JDBC中PreparedStatement的实现,我 ... -
HelloWorld的javap -verbose HelloWorld 字节码初探
2010-11-17 12:20 3135基本的HelloWorld类: public class ... -
How Tomcat Works 简单目录
2010-11-16 14:51 1462第1章:通过一个简单的HTTP服务器开始这本书的内容。构建一个 ... -
Coder 爱翻译 How Tomcat Works 第三章 第三部分
2010-11-15 19:24 1061Parsing Headers 一个HttpHeader类 ... -
Coder 爱翻译 How Tomcat Works 第三章 第二部分
2010-11-14 20:13 1111The Connector(连接器) HttpConnect ... -
回应某面试题
2010-11-10 21:31 1337上午看了一JAVAEYE的一个上机题:http://www.i ... -
quartz简单应用
2010-11-10 11:49 1011Job类:实现Job接口,接口中有一个execute()方法, ... -
coder 爱翻译 How Tomcat Works 第三章 第一部分
2010-11-05 11:41 1165第三章: Connector 在正式 ...
相关推荐
适合读者 1.jsp/servlet 开发人员,想了解 tomcat 内部机制的 coder;...2.想加入 tomcat 开发团队的 coder; 3.web 开发人员,但对软件开发很有兴趣的 coder; 4.想要对 tomcat 进行定制的 coder。
Bad Programming Practices 101 Become a Better Coder by Learning How (Not) to Program 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书
基于 DeepSeek-Coder 实现翻译功能的 Python 源码。将待翻译的文本和翻译提示组合成输入文本,输入到模型中,模型会尝试进行翻译。最后打印出原文和翻译结果。
- **插件扩展**:PHPCoder支持第三方插件,可以通过社区下载安装增强功能。 - **代码格式化**:使用“编辑”>“格式化代码”对代码进行整理,保持良好的编码风格。 - **搜索与替换**:利用查找和替换功能,可以在...
mediacoder 5685专业版,无普通版的限制
2. 高质量编码:MediaCoder允许用户自定义编码参数,如比特率、分辨率、帧率等,以实现高质量的编码输出,满足不同应用场景的需求,比如视频压缩、在线流媒体、移动设备播放等。 3. 强大的硬件加速:MediaCoder利用...
#### 二、Simulink HDL Coder 的主要功能特点 1. **模型到 HDL 的转换**:Simulink HDL Coder 支持将 Simulink 模型转换成 VHDL 或 Verilog HDL 代码。这些模型可以包括数学运算、信号处理、通信协议等多种类型的...
Simulink PLC Coder 支持 130 多个 Simulink 模块,包括所有的 Stateflow 结构以及大部分嵌入式 MATLAB 函数。 Simulink PLC Coder 提供了优化策略,使得内存大小减少,并且所生成的结构化文本的执行速度也得以...
Embedded Coder用于产生嵌入式处理器、目标快速原型板和大规模生产中使用的微处理器的可读的、紧凑的、快速的C和C++代码。Embedded Coder支持附加的MATLAB Coder™和Simulink Coder™配置选项,以及对生成代码的功能...
**Mediacoder基础教程** Mediacoder是一款强大的多媒体编码工具,专为音频和视频转换而设计,支持多种格式,如MP4、AVI、MKV等。本教程将深入讲解如何利用Mediacoder进行视频压制,优化视频质量,同时合理权衡码率...
Martin, "The Clean Coder: A Code of Conduct for Professional Programmers" Prentice Hall | 2011 | ISBN: 0137081073 | 256 pages | PDF | 6 MB Programmers who endure and succeed amidst swirling ...
虽然CoolCoder能够自动化大部分工作,但开发者依然可以自定义模板,对生成的代码进行微调,以满足特定项目的需求。这种灵活性确保了生成的代码既高效又符合项目风格。 6. **提高开发效率**: 使用CoolCoder,...
MediaCoder是一款功能强大的多媒体转换工具,它支持广泛的音频和视频编码格式,使用户能够轻松地在不同设备之间转换媒体文件。这款软件适用于个人用户、专业音频和视频制作人员,以及那些希望在各种设备上享受多媒体...
PHPCoder用于快速开发和调试PHP应用程序,它很容易扩展和定制,完全能够符合开发者的个性要求.PHPCoder是一个非常实用的,功能强大的编程环境,而且它是免费的!
### HDL-Coder详细教程知识点概述 #### 一、生成HDL代码前的准备工作 在开始从Simulink模型生成HDL代码之前,需要完成一系列的准备工作,确保模型能够顺利生成高质量的代码。 ##### 1.1 使用`hdlsetup`进行模型...
### MATLAB Coder 基本函数教程 #### MATLAB Coder 概述 MATLAB Coder 是一款能够将 MATLAB 代码转换成独立的 C 或 C++ 代码的强大工具。这一过程对于那些希望在非 MATLAB 环境下部署 MATLAB 代码的应用开发者来说...
texasinstrumentsc2000.mlpkginstall 支持TI的C2000系列工具包,要求MATLAB R2017a及其以上版本。 安装方法:打开matlab,调整路径到mlpkginstall文件所在目录;在current folder窗口里双击mlpkginstall文件即可开始...
MediaCoder行业版一款针对VOD及KTV视频点播行业开发的,用于转换和处理带有多音轨内容的视频节目的软件。它具备业界领先的视频编码引擎,在高性能转码的同时保持高画质,并通过丰富的视频滤镜增强画面视觉效果。作为...
MediaCoder使用说明文档, mediaCoder usermanual,
开源的AI自动生成SQL语句源代码,这款SQLCoder-70B-Alpha在文本到SQL的转换能力上超越了包括GPT-4在内的所有通用模型,它能更准确地理解你的需求,并生成相应的SQL查询。SQLCoder2和SQLCoder-7B模型已经向公众开放,...