Servlet入门
1. Web服务器介绍
- Web服务器是基于B/S结构,通过http协议进行数据交互(收发)
报文实例
请求:
GET /mytest HTTP/1.1
Host: www.cnblogs.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; zh-CN; rv:1.8.1) Gecko/20061010 Firefox/2.0
.......
响应:
HTTP/1.1 200 OK
Content-Type:text/html;charset=utf-8
<html>
<body>
Hello http!
</body>
</html>
- 简单selvet实现
http包下
public class MyRequest {
private String method;
private String url;
public MyRequest(InputStream is){
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is));// 01 获取请求报文
String readHeader = br.readLine(); // 02 获取请求报文的第一句
String[] methodAndUrl = readHeader.split(" ");
// GET /mytest HTTP/1.1 03 以空格为条件将方法 地址放入数组
this.method = methodAndUrl[0]; //04 取得请求报文方法
this.url = methodAndUrl[1]; //05 取得请求报文地址
} catch (IOException e) {
e.printStackTrace();
}
}
public String getMethod() { //为了外部取该方法中定义的私有属性method
return method;
}
public String getUrl() { //为了外部取该方法中定义的私有属性url
return url;
}
}
public class MyResponse {
private OutputStream writer;
public static String respHeader = "HTTP/1.1 200 OK\n" +
"Content-Type:text/html;charset=utf-8\n" +
"\n"; //06 响应报文头部内容
public MyResponse(OutputStream os){
this.writer = os; //07
}
public OutputStream getWriter() {
return writer; //07 获取到的输出流对象提供给其它类
}
}
servlet包下
public abstract class MyServlet { //08 定义 规则抽象类 为类对应的方法定义规则
public void doService(MyRequest req, MyResponse resp){
if("GET".equals(req.getMethod())){ //09 直接抽象类判断完后 不用每个方法在重复写进行判断
doGet(req,resp);
}else{
doPost(req,resp);
}
};
public abstract void doGet(MyRequest req, MyResponse resp); //10 定义的请求 报文的"请求方法" 的抽象方法
public abstract void doPost(MyRequest req, MyResponse resp); //10 定义的请求 报文的"请求方法" 的抽象方法
}
//实现登录功能
public class LoginServlet extends MyServlet {
@Override
public void doGet(MyRequest req, MyResponse resp) {
OutputStream out = resp.getWriter(); //11 获得到响应的输出流对象
try {
out.write(resp.respHeader.getBytes()); //12 写入响应报文 头部
out.write("login ok!!!!".getBytes()); //13 接着写响应的页面代码内容
out.flush(); //刷新 关闭
out.close(); //接下来建立对应的properties文件
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest req, MyResponse resp) {
}
}
//实现 吟首诗
public class QueryServlet extends MyServlet{
@Override
public void doGet(MyRequest req, MyResponse resp) {
OutputStream out = resp.getWriter();
try {
out.write(resp.respHeader.getBytes());
out.write("<h1>我的标题</h1><br/><p>床前明月光</p>".getBytes());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest req, MyResponse resp) {
}
}
properties文件
servlet.one.url=/login
servlet.one.class=com.javasm.servlet.LoginServlet
servlet.three.url=/query
servlet.three.class=com.javasm.servlet.QueryServlet
server包下
public class MyServer {
public static Map<String, MyServlet> myMapping = new HashMap<String, MyServlet>();
//14 定义Servlet容器 通过请求 找到指定的servlet
private static Properties prop = new Properties();
//15 初始化properties对象
public static void myInit(){
try {
prop.load(MyServer.class.getResourceAsStream("/MyMapping.properties")); //16 获取properties位置 并加载
Set keys = prop.keySet(); //17 获取properties所有Key
for (Object key: keys){
if(key.toString().endsWith(".url")){
//18 将properties里的地址 对应方法 通过键值对放入myMapping里
String url = prop.getProperty(key.toString());
String classNameKey = key.toString().replace("url","class");
String className = prop.getProperty(classNameKey);
MyServlet ms = (MyServlet)Class.forName(className).newInstance();
myMapping.put(url,ms);
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public static void serverStart(){
try {
ServerSocket s = new ServerSocket(8011); //18 开一个指定的监听的端口8011
System.out.println("服务器开启 监听8011端口 等待访问");
while (true){ // 就是让他一直开着
Socket soc = s.accept();
//19 拿到Socket对象 多线程开就是一个端口等待请求 多线程开启
MyProcess newThread = new MyProcess(soc); //20 请求线程
newThread.start(); //25 开启线程
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyServer.myInit();
MyServer.serverStart();
}
}
public class MyProcess extends Thread{
Socket soc; //定义 Socket对象
public MyProcess(Socket soc){
this.soc = soc;
}
@Override
public void run() {
try{
MyRequest req = new MyRequest(soc.getInputStream()); //21 拿到请求报文
MyResponse resp = new MyResponse(soc.getOutputStream()); //22 拿到响应报文
MyServlet ms = MyServer.myMapping.get(req.getUrl()); //23 通过拿到的对象的网址拿到myMapping对应的方法
if(ms!=null){ //24 判断下如果是空的告诉用户没有该方法
ms.doService(req,resp);
}else{
OutputStream out = resp.getWriter();
out.write(resp.respHeader.getBytes());
out.write("404 not Found!!!".getBytes());
out.flush();
out.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2. Tomcat下载和使用
2.1 Tomcat介绍
下载地址:http://tomcat.apache.org/;
安装:下载到的是一个压缩包,解压缩即可(路径中不要有中文);
2.2 Tomcat目录结构
名称 | 作用 |
---|---|
/bin | 存放Windows或Linux平台上用于启动和停止Tomcat的脚本文件 |
/conf | 存放Tomcat服务器的各种配置文件,其中最重要的是server.xml |
/lib | 存放Tomcat服务器所需的各种JAR文件 //自己创建 |
/logs | 存放Tomcat运行中产生的日志文件 |
/webapps | 存放Tomcat发布的Web应用 |
/work | Tomcat把由JSP生成的Servlet放于此目录下 |
- 启动和停止Tomcat服务器
可通过apache解压目录中的startup.bat/shutdown.bat来启动或停止tomcat
在浏览器地址栏中输入:http://127.0.0.1:端口号或者http://localhost:端口号,页面进入到Tomcat启动成功界面
Tomcat默认端口号为-8080
- 可以在conf中的server.xml中修改tomcat端口号
<Connector port="8080" protocol="HTTP/1.1 "
connectionTimeout="20000" redirectPort="8443 "
/>
2.3 Tomcat在idea中配置
1.创建空项目
2.配置tomcat
右上角 edit configurations defaults 配置模板 选择tomcat真实目录
点加号 用此模板添加一份
3.新建模块 选javaEE 勾选 web application
4.如果默认没创建好项目与tomcat的关联 手动添加artifacts web application war exploded
5.tomcat中加载项目 artifact 改项目名
//
2.4 其他web服务器
- Jetty 更轻量级,更灵活,被一些框架集成,免费
- JBoss JBoss支持JavaEE标准,能够作为JavaEE应用服务器使用,Servlet容器只是其
中一部分,免费
- Weblogic 市场上最好的J2EE工具之一,功能大而全,收费
3. Servlet
3.1 servlet介绍
-
Servlet是JavaEE规范中的Web开发组件;
-
Servlet运行在服务器端,需要Servlet容器的支持,例如Tomcat(运行在tomcat中);
-
可以通过浏览器访问Servlet,Servlet可以生成动态页面返回给浏览器
3.2 编写servlet
public class TestServlet extends HttpServlet {//继承httpservlet 重写doGet doPost方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理代码
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Web.xml中配置
<servlet>
<servlet-name> TestServlet </servlet-name> //name---TestServlet
<servlet-class>com.javasm.servlet. TestServlet </servlet-class>
</servlet>
<servlet-mapping>
<servlet-name> TestServlet </servlet-name> //name---TestServlet
<url-pattern>/TestServlet </url-pattern>
</servlet-mapping>
配置中servlet-name相同作为同一组配置
servlet-class配置完整包名类名
url-pattern配置开放的访问路径 该路径为虚拟的路径
3.3 Servlet实现方式
- 实现Servlet的三种方式:一个实现,两个继承
- 实现Servlet接口
- 继承GenericServlet抽象类 重写service()方法
- 继承HttpServlet抽象类 HttpServlet继承于GenericServlet 重写doGet(),doPost()方法 //最常用
3.4 Servlet生命周期
3.5 Servlet其他配置参数
- servlet中配置初始化参数,可在servlet中直接读取
配置中:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.javasm.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>testParam</param-name>
<param-value>Hello Servlet</param-value>
</init-param>
</servlet>
代码中:
String initParam = getInitParameter("testParam");
//在代码中直接将上面存储的"testParam"的值读取了
//不过只能在 "HelloServlet" Servlet里读取得到
- 配置servlet全局参数,与servlet标签同级,所有servlet都可以读取
配置中: //配置服务器全局参数用
<context-param>
<param-name>contextParam</param-name>
<param-value>Hello Servlet</param-value>
</context-param>
代码中:
String contextParam = getServletContext().getInitParameter("contextParam");
//这个全局都可以得到
- 配置servlet启动顺序load-on-startup
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.javasm.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
注意:如果配置了load-on-startup,servlet实例会在项目启动时就加载,并且按照load-on-startup中配置的顺序加载,如果不加load-on-startup,实例会在有请求访问到该servlet时实例化
3.6 Servlet中处理乱码
Servlet运行在web容器(tomcat)中,tomcat默认对传递的数据使用iso-8859-1进行编码,传递中文无法正常显示。
请求乱码:
如果数据在传递时是正常中文,但是在servlet中取出是乱码,需要在请求对象中设置编码格式,对请求数据进行重新编码。
request.setCharacterEncoding("UTF-8");
响应乱码:
如果数据在servlet中是正常中文,到页面中显示乱码,则需要在响应对象中设置响应头,让页面指定以何种编码格式解析响应数据。
response.setContentType("text/html;charset=UTF-8");
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("运行了");
//获取请求的参数 getParameter
//设置请求参数编码格式setCharacterEncoding 需要在获取之前设置
//设置响应编码格式resp.setContentType("text/html;charset=utf-8");
// 实际上设置在响应头中 先设置响应头 再获取输出流
req.setCharacterEncoding("utf-8"); //将请求格式调整为utf-8格式
String uname = req.getParameter("uname");
String upwd = req.getParameter("upwd");
System.out.println(uname+" ___ "+upwd);
resp.setContentType("text/html;charset=utf-8"); //将响应后的格式调整为utf-8格式
PrintWriter out = resp.getWriter();
out.print("<h1>hello !!! "+uname+"</h1>");
out.flush();
out.close();
}
}
注意:如果浏览器中样式没有起效果 将web文件夹 右键
@WebServlet("/login") //注解: 直接写请求地址 //javax包下 不用大量在web.xml写大量的配置方法;了
public class LoginServlet extends HttpServlet {
}