购物网站后台管理系统网上做广告怎么收费
文章目录
- 类直接实现Servlet接口的弊端
- Servlet接口的方法
- 适配器设计模式
- 适配器对象的改造
- 关于init方法的ServletConfig对象来源
- 使用模板方法设计模式改造init方法
- GenericServlet内置抽象类
- ServletConfig接口
- ServletConfig接口简介
- 测试
- 再谈GenericServlet抽象类
类直接实现Servlet接口的弊端
Servlet接口的方法
上面是jakarta.servlet.Servlet
接口中的方法
- init(): 初始化Servlet对象的相关信息
- service(): 业务的核心方法, 也是Tomcat调用该对象实现逻辑的入口
- destroy(): 销毁Servlet对象的信息
- getServletConfig(): 获取
ServletConfig
对象(这个下面再说) - getServletInfo(): 获取一些无用的信息(作者, 版本号之类的)
适配器设计模式
为什么类不能直接实现Servlet接口呢, 是因为对于一个Servlet对象来说, 除了service
方法, 其他的对象都是不经常用的, 如果我任意一个类都去实现Servlet接口中的所有方法, 那最后的结果就是代码十分的冗余…
适配器设计模式可以类比为手机不能直接插在220V电源上, 需要一个适配器的充电头进行转换…
所以我们创建一个适配器(也就是一个抽象类), 实现这个Servlet接口中的大部分方法, 只把一些需要子类重定义的方法抽象出来, 这样就可以使得代码的冗余度大大降低, 这其实就是适配器设计模式的核心
我们创建一个适配器的抽象类代码如下
import jakarta.servlet.*;import java.io.IOException;/*** 适配器设计模式* 因为我们并不是所有的Servlet对象实现的时候并不需要所有的Servlet方法* 需要我们添加一个适配器, 实现里面的大部分方法即可(除了Service)*/
public abstract class ServletAdapt implements Servlet{@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;@Overridepublic String getServletInfo() {return "";}@Overridepublic void destroy() {}
}
下面我们创建一个子类实现这个适配器中的抽象方法
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;import java.io.IOException;public class UserServlet extends ServletAdapt{@Overridepublic void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {// 重写适配器的service方法对外提供服务即可System.out.println("UserServlet service method called");}
}
适配器对象的改造
我们官方都把适配器对象称之为GenericServlet
(标题的由来)
关于init方法的ServletConfig对象来源
思考这个ServletConfig对象是谁传过来的, 我们大致说一下底层的Tomcat伪代码
class Tomcat{public static void main(String[] args) {// 首先通过反射机制拿到相关类对象Class clazz = Class.forName("全限定类名");// 构造出该类的一个实例Object object = clazz.getConstructor().newInstance();// 向下转型成为Servlet对象Servlet servletImp = (Servlet) object; // 创建一个ServletConfig对象ServletConfig servletConfig = new ServletConfigImp();// 初始化这个类的实例servletImp.init(servletConfig);// 使用service方法提供服务servletImp.service(ServletRequest request, ServletResponse response);}
}
所以我们的ServletConfig
对象, 是 Tomcat服务器
创建出来的
我们想查看以下这个实现ServletConfig
接口的对象的信息(改造init方法)
在web.xml
文件中添加映射信息如下
在浏览器中输入URL访问…
上面我们说了, 这个对象是 Tomcat 服务器实现的, 我们现在找到上次下载的关于 Tomcat 服务器的源码找到这个类进行分析
可以发现这个类实现了jakarta.servlet.ServletConfig
接口, 这同时也证明了, 我们的Tomcat
服务器实现了 Servlet规范
使用模板方法设计模式改造init方法
关于模板方法设计模式, 核心总结就是下面的一句话
- 对拓展开放, 对修改关闭
假如, 我们想要这个 ServletConfig 对象那应该怎么办呢
init
方法的其中一个参数就是ServletConfig
, 所以我们可以创建一个实例变量接住这个临时变量…
但是假如子类继承了这个适配器, 并且尝试对其中的init
方法进行重写, 那我们的config
对象不就有变为空了吗,
所以一个简单的策略是直接把init
方法使用final
修饰, 此时不允许子类进行重写…
但是还有一个问题, 假设我们的子类真的想要重写init
方法, 但是又不想config
对象变成空, 那我们就可以采用模板方法设计模式
这样就会在不破坏原有代码的基础上进行代码能力的扩展, 这就是模板方法设计模式
可以看到, 在源代码基本功能不变的基础上, 重写的方法正常执行了…
GenericServlet内置抽象类
其实我们上面的分析出来的内容, 实际上内置的类已经实现了相关方法了
GenericServlet
实现了Servlet接口 & ServletConfig接口
下面是我们的GenericServlet
的源码解析
可以看到里面init
方法正是使用了模板方法设计模式
进行的设计, 十分巧妙…
完整源码
package jakarta.servlet;import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {private static final long serialVersionUID = 1L;private transient ServletConfig config;public GenericServlet() {}public void destroy() {}public String getInitParameter(String name) {return this.getServletConfig().getInitParameter(name);}public Enumeration<String> getInitParameterNames() {return this.getServletConfig().getInitParameterNames();}public ServletConfig getServletConfig() {return this.config;}public ServletContext getServletContext() {return this.getServletConfig().getServletContext();}public String getServletInfo() {return "";}public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}public void init() throws ServletException {}public void log(String message) {ServletContext var10000 = this.getServletContext();String var10001 = this.getServletName();var10000.log(var10001 + ": " + message);}public void log(String message, Throwable t) {this.getServletContext().log(this.getServletName() + ": " + message, t);}public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;public String getServletName() {return this.config.getServletName();}
}
我们先介绍其中的一部分方法, 等下学习完ServletConfig
之后把全部的方法介绍一下…
ServletConfig接口
ServletConfig接口简介
我们上面学习Servlet接口
的时候, 知道有一个方法getServletConfig
, 这个方法的作用就是返回一个ServletConfig对象
其实也就是上面我们分析的Tomcat服务器创建传入的那个对象
- 一个
Servlet对象
有且仅有唯一一个ServletConfig对象
- 保存的是该
Servlet对象在web.xml中配置的servlet标签一些信息
下面是ServletConfig接口
中的常见的方法
- getInitParameter(String name): 返回一个name对应的参数值
- getInitParameterNames(): 返回一个集合保存所有的name值
- getServletContext(): 返回一个ServletContext对象
- getServletName(): 返回配置的servlet-name
测试
我们在web.xml
中配置的相关信息如下
<servlet><servlet-name>user</servlet-name><servlet-class>com.qnn.servlet.UserServlet</servlet-class><!--下面配置的相关参数信息, 可以通过ServletConfig对象中的方法拿到--><init-param><param-name>userName</param-name><param-value>qiannian</param-value></init-param><init-param><param-name>account</param-name><param-value>root</param-value></init-param><init-param><param-name>password</param-name><param-value>123456</param-value></init-param></servlet>
关于我们Servlet对象实现的service()
方法的内容如下
@Override// 测试一下ServletConfig对象中保存的相关信息public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {// 改变输出的方法, 并获取一个输出流对象response.setContentType("text/html");PrintWriter out = response.getWriter();// 通过Servlet接口中的方法获得一个ServletConfig对象ServletConfig servletConfig = this.getServletConfig();// 利用ServletConfig对象中的相关的方法来输出我们当前的Servlet对象在web.xml中配置的一些信息// 这里有一个小点就是, 这个集合类其中的元素不可以使用foreach来进行循环的遍历...out.print("<h3>" + servletConfig.getServletName() + "</h3>");out.print("<br/>");Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();while (initParameterNames.hasMoreElements()) {String initParameterName = initParameterNames.nextElement();out.print("<br/>");out.print("<h3>" + initParameterName + " : " + servletConfig.getInitParameter(initParameterName) + "</h3>");}}
结果如下
再谈GenericServlet抽象类
有了上面的基础, 我们想要理解GenericServlet
抽象类中的内容就更容易了…具体不再说了…