简单使用可以参考这篇文章:
https://blog.csdn.net/the_one_and_only/article/details/105177506
基本原理就是将Tomcat对象中的Host和Connector中的port设置成相应的host:port,配置Tomcat上下文(环境配置),最后加入Servlet,然后跑起来。
Tomcat是这样的:
Tomcat源码注释:
翻译:
用于嵌入/单元测试的最小tomcat启动器。
Tomcat支持多种类型的配置和startup,最常见和稳定的是基于server.xml的,在org.apache.catalina.startup.Bootstrap中实现。
这个类用于嵌入tomcat的应用程序。
所有tomcat类(可能还有servlet)都在类路径中。(例如所有的都在一个大的罐子里,或在eclipse CP里,或在任何其他组合)
我们需要一个临时目录来存放工作文件
不需要配置文件。该类提供方法如果你有一个带有web.xml文件的webapp,但它确实是optional,你可以使用你自己的servlet。
有各种各样的“添加”方法来配置servlet和webapps。这些方法,默认情况下,创建一个简单的内存安全域并应用它。如果你需要更复杂的安全处理,你可以定义这个类。
这个类提供了main()和一些简单的CLI参数,参见设置文件。它可以用于简单的测试和演示。
start是这样启动的:
public void start() throws LifecycleException {
getServer();
server.start();
}
public Server getServer() {
if (server != null) {
return server;
}
System.setProperty("catalina.useNaming", "false");
server = new StandardServer();
initbaseDir();
// Set configuration source
ConfigFileLoader.setSource(new CatalinabaseConfigurationSource(new File(basedir), null));
server.setPort( -1 );
Service service = new StandardService();
service.setName("Tomcat");
server.addService(service);
return server;
}
要注意这个StandardService和StandardServer
public class StandardService extends LifecycleMBeanbase implements Service public final class StandardServer extends LifecycleMBeanbase implements Server
我们从LifecycleMBeanbase 字面上可以看出,这是一个跟Tomcat组件生命周期有关的类
详情可以参考:https://blog.csdn.net/wojiushiwo945you/article/details/73331057、
也就是说,到这里,我们已经知道Tomcat如何启动了。但是Tomcat里边有什么呢?
Tomcat是Servlet和JSP支持的容器,我们不管是传统的javaweb还是springboot都离不开Servlet。
在一开始的例子中,我们可以看到这两行代码:
Wrapper wrapper = tomcat.addServlet("/", "DemoServlet", new EasyServlet());
wrapper.addMapping("/embeddedTomcat");
其实就是在tomcat容器中丢入Servlet,并且配置路径映射。Servlet一般继承自HttpServlet。
HttpServlet:
意思就是说,这是一个用来创建Http站点的类的抽象。一般要至少重写里边的一些方法,如doGet、doPost、doPut、doDelete等方法。
servlet通常运行在多线程服务器上,所以要知道servlet必须处理并发请求,并注意同步访问共享资源。共享资源包括内存中的数据(如实例或类变量)和外部对象(例如文件、数据库连接和网络连接)。
我们来看其中一个doGet方法,如果我们不重写它,那么就是这样:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
注释中有这么一段:
翻译:
覆盖此方法以支持GET请求,也自动支持HTTP HEAD请求(HEAD就是一个GET请求,但是在响应报文中,只有请求报头字段)。
查看其子类:
随便看一个DefaultServlet:大多数web应用默认的资源服务servlet,用于提供静态资源,如HTML页面和图像。
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, including the data content
serveResource(request, response, true, fileEncoding);
}
那我们探究了Tomcat、Servlet,那么Servlet放入Tomcat的具体流程呢?
重新来看这两行代码:
Wrapper wrapper = tomcat.addServlet("/", "DemoServlet", new EasyServlet());
wrapper.addMapping("/embeddedTomcat");
addServlet的源码是这样的:
public Wrapper addServlet(String contextPath,
String servletName,
Servlet servlet) {
Container ctx = getHost().findChild(contextPath);
return addServlet((Context) ctx, servletName, servlet);
}
public static Wrapper addServlet(Context ctx,
String servletName,
Servlet servlet) {
// will do class for name and set init params
Wrapper sw = new ExistingStandardWrapper(servlet);
sw.setName(servletName);
ctx.addChild(sw);
return sw;
}
tomcat在同一个Host下创建了子容器ctx ,而这个容器ctx将Servlet的包装ExistingStandardWrapper给add进来,也就是tomcat容器中放进了Servlet。就如上诉那些图讲诉的那样。
至于addMapping,就是添加映射:
@Override
public void addMapping(String mapping) {
mappingsLock.writeLock().lock();
try {
mappings.add(mapping);
} finally {
mappingsLock.writeLock().unlock();
}
if(parent.getState().equals(LifecycleState.STARTED)) {
fireContainerEvent(ADD_MAPPING_EVENT, mapping);
}
}
mappingsLock是ReentrantReadWriteLock(可重入读写锁)
mappings是ArrayList
@Override
public void fireContainerEvent(String type, Object data) {
if (listeners.size() < 1) {
return;
}
ContainerEvent event = new ContainerEvent(this, type, data);
// Note for each uses an iterator internally so this is safe
for (ContainerListener listener : listeners) {
listener.containerEvent(event);
}
}
通知所有容器事件监听器,说某个特定事件的在此容器发生。默认实现执行使用调用线程同步通知。
listeners是CopyOnWriteArrayList
也就是写入时复制的ArrayList,存的是ContainerListener。
public interface ContainerListener {
public void containerEvent(ContainerEvent event);
}
所有的ContainerListener通过以下方法加进来:
@Override
public void addContainerListener(ContainerListener listener) {
listeners.add(listener);
}
所以说我们这些监听器从哪里加过来,又监听哪些东西呢?
我们从Tomcat.addServlet一路追踪下去,看到了这么一个包装器的类,这也是我们之前所用到的Wrapper的最终实现。
public static class ExistingStandardWrapper extends StandardWrapper {
private final Servlet existing;
@SuppressWarnings("deprecation")
public ExistingStandardWrapper( Servlet existing ) {
this.existing = existing;
if (existing instanceof javax.servlet.SingleThreadModel) {
singleThreadModel = true;
instancePool = new Stack<>();
}
this.asyncSupported = hasAsync(existing);
}
private static boolean hasAsync(Servlet existing) {
boolean result = false;
Class> clazz = existing.getClass();
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws != null) {
result = ws.asyncSupported();
}
return result;
}
@SuppressWarnings("deprecation")
@Override
public synchronized Servlet loadServlet() throws ServletException {
if (singleThreadModel) {
Servlet instance;
try {
instance = existing.getClass().getConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new ServletException(e);
}
instance.init(facade);
return instance;
} else {
if (!instanceInitialized) {
existing.init(facade);
instanceInitialized = true;
}
return existing;
}
}
@Override
public long getAvailable() {
return 0;
}
@Override
public boolean isUnavailable() {
return false;
}
@Override
public Servlet getServlet() {
return existing;
}
@Override
public String getServletClass() {
return existing.getClass().getName();
}
}
首先,我们传给它我们之前创建的Servlet,然后它会判断是否是singleThreadModel(单线程模型),是的话把instancePool这个Stack
loadServlet(),看名字就是加载Servlet,用的是instance.init(facade);这行代码。
我们可以猜出,就是使用的设计模式中的Facade模式(包装器模式)。
facade是StandardWrapperFacade类对象,用来包装this,提供一些配置。
那么init方法呢?
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
// NOOP by default
}
也就是说,这里并不提供Servlet的初始化(毕竟它也只是个抽象类)
那我们依旧来看DefaultServlet。其中init方法太多内容,大多数的任务就是将对象中的属性赋值。
我们选其中重要的一行分析:
// Load the web resources
resources = (WebResourceRoot) getServletContext().getAttribute(Globals.RESOURCES_ATTR);
就是将上下文中的org.apache.catalina.resources域中的相关部分提取到resources中。
那么这个resources什么时候被用到呢?
doGet方法,定位到serveResource方法,有这么几行:
protected void serveResource(HttpServletRequest request,
HttpServletResponse response,
boolean content,
String inputEncoding) {
// ...
// Identify the requested resource path
String path = getRelativePath(request, true);
// ...
WebResource resource = resources.getResource(path);
// ...
ostream = response.getOutputStream();
// ...
writer = response.getWriter();
// ...
copy(...)
资源只有接口类型,不知道使用哪个实现类的方法,就用instanceOf来识别,以调用同样接口的不同解决方法。
很粗略,细节不继续探究。
我们回想一下,一开始的Context context = tomcat.addContext(host, “/”, classpath),或许也变成了后来DefaultServlet的资源路径上下文(有待考究)。
@_@
既然来了,觉得不错的话,点个赞或者关注呗



