1. IDEA中tomcat部署与运行
采用maven形式
- 右键,new->Module,选择Maven Archetype,Archetype选择:org.apache.maven.archetypes:maven-archetype-webapp,自定义名称等,其它默认,如图:

点击create,创建完成后,进入pom.xml,添加tomcat插件:
1
2
3
4
5
6
7
8
9
10
11<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 端口 -->
<port>8888</port>
<!-- 路径 -->
<path>/</path>
</configuration>
</plugin>打开右侧工具栏中的Maven,点击M(Excute Maven Goal),输入tomcat7:run即可运行项目

2、Servlet
1、导包
在正式使用前,需要在Maven中导入Servlet包 1
2
3
4
5<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
2、使用Servlet
- 编写一个类实现Servlet接口
- 实现Service方法,处理请求,并响应数据
- 在web.xml中配置Servlet程序的访问地址
但在实际使用中,通常不用Servlet接口,而是使用它的实现类的子类HttpServlet,即:
编写Servlet,继承自HttpServlet,重写doPost()、doGet()方法
1
2
3
4
5
6
7
8
9
10public class AddServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fname = req.getParameter("fname");
Integer price = Integer.valueOf(req.getParameter("price"));
Integer fcount = Integer.valueOf(req.getParameter("fcount"));
String remark = req.getParameter("remark");
System.out.println(fname+"--"+price+"--"+fcount+"--"+remark);
}
}配置文件web.xml或者使用注解配置(Servlet从3.0版本开始支持使用注解)
或1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<web-app>
<!-- 给Tomcat配置Servlet程序 -->
<servlet>
<!-- Servlet名称(一般是类名) -->
<servlet-name>AddServlet</servlet-name>
<!-- Servlet全限定类名 -->
<servlet-class>com.zephon.AddServlet</servlet-class>
</servlet>
<!-- 给Servlet程序配置访问地址 -->
<servlet-mapping>
<!-- Servlet名 -->
<servlet-name>AddServlet</servlet-name>
<!-- Servlet对应处理的请求url,以/开头,/表示http://ip:port/工程路径 -->
<url-pattern>/add</url-pattern>
</servlet-mapping>
</web-app>1
2
3
4
5
6
7
8
9
10// @WebServlet("/add") // 表示对应的请求url
public class AddServlet extends HttpServlet {
...
}处理中文乱码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class AddServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置编码处理乱码
req.setCharacterEncoding("utf-8");
String fname = req.getParameter("fname");
Integer price = Integer.valueOf(req.getParameter("price"));
Integer fcount = Integer.valueOf(req.getParameter("fcount"));
String remark = req.getParameter("remark");
System.out.println(fname+"--"+price+"--"+fcount+"--"+remark);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getParameter("fname"));
// req.setCharacterEncoding("utf-8");
// tomcat7及之前的版本中get请求处理乱码的方式
String fname = req.getParameter("fname");
byte[] bytes = fname.getBytes(StandardCharsets.ISO_8859_1);
fname = new String(bytes, StandardCharsets.UTF_8);
System.out.println(fname);
}
}
3、Servlet
继承关系:
- javax.servlet.Servlet接口
- javax.servlet.GenericServlet抽象类
- javax.servlet.http.HttpServlet抽象子类
- javax.servlet.GenericServlet抽象类
生命周期:
出生:init
运行:service
死亡:destroy
默认情况下,第一次接受请求时,Servlet会进行实例化、初始化,调用init()方法,然后服务
之后的每次请求,都会调用service()方法
当容器关闭时,其中所有Servlet实例都会被销毁,会调用destroy()方法
javax.servlet.Servlet主要方法
- void init(ServletConfig config);初始化方法
- void service(ServletRequest req, ServletResponse res);服务方法,前端有请求时调用
- void destroy();销毁方法
javax.servlet.GenericServlet主要方法
实现了Servlet接口,做了很多空的实现,并持有一个ServletConfig类的引用
常用方法
- void service(ServletRequest req, ServletResponse res);服务方法,仍然是抽象的
涉及到的ServletConfig接口
ServletConfig接口:Servlet程序的配置信息接口,也是由Tomcat服务器负责创建实例化,每个Servlet创建时就创建一个对应的ServletConfig实例 ServletConfig类的作用:
- 获取Servlet程序的别名,即web.xml中配置的
的值,对应的方法:config.getServletName(); - 获取初始化参数,即web.xml中配置的
,对应的方法:config.getInitParameter(String s);其中s表示对应参数名
例: 1
2
3
4
5
6
7
8<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>com.zephon.AddServlet</servlet-class>
<init-param>
<param-name>jdbcClass</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</init-param>
</servlet>
- 获取ServletContext对象,对应的方法:ServletContext servletContext = config.getServletContext(); > 注: > 1. 即使在子类中调用父类的getServletConfig()获取ServletConfig对象,其获取的各种属性、参数等仍是子类对应的,即如果在web.xml中的参数定义的是给父类的,则子类获得的对应的值就是null > 1. 当继承了HttpServlet时,重写了init方法,则必须在init方法调用super.init(config),否则其它方法如果调用getServletConfig()时获取的ServletConfig会是空的对象
涉及到的ServletContext接口
- ServletContext是一个接口,表示Servlet上下上文对象
- 一个Web工程只有一个ServletContext对象实例
- ServletContext对象是一个域对象 > 什么是域对象? > 域对象就是可以像Map一样存取数据的对象。这个域指的是存取数据的操作范围。 > 域对象统一存、取、删方法: > 存:setAttribute(String key, Object value) > 取:getAttribute(String key) > 删:removeAttribute(String key)
作用:
- 获取web.xml中配置的上下文参数
,即:context.getInitParameter(String name);
注:不能获取1
2
3
4
5
6
7
8<web-app>
<!-- 上下文参数,属于整个web工程 -->
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
...
</web-app>
- 获取当前的工程路径,即:context.getContextPath(); // 如/hello
- 获取工程部署后在服务器磁盘上绝对路径,即:context.getRealPath("/"); // "/"表示http://ip:端口/工程名,返回值例:D:,最终映射到IDEA中是代码中对应的webapp目录
- 像Map一样存取数据
javax.servlet.http.HttpServlet主要方法
- void service(ServletRequest req, ServletResponse res);服务方法,根据请求类型执行对应的方法,如"POST"类型则执行doPost();
- doGet()、doHead()、doPut()、doDelete()、doOptions()、doTrace()等,分别对不同请求类型进行默认处理,一般就是返回405错误
小结:
上述主要方法中的服务方法,当有请求时,会自动响应,实际上是tomcat容器调用(反射调用)的,然后在HttpServlet重写的服务方法中会对请求类型进行划分处理,执行对应的方法。 Servlet第一次请求时都会实例化、初始化,然后再服务,这样会提高系统的启动速度,但会造成第一次请求耗时会较长;因此,如果需要提高响应速度,可以设置Servlet的初始化时机(在web.xml中设置,
Service的请求参数HttpServletRequest
每次只要有请求进入Tomcat服务器,Tomcat服务器就会将请求过来的HTTP协议信息解析封装到Request对象中,然后传递到service方法中,可以通过HttpServletRequest对象获取所有请求的信息 继承关系:
- javax.servlet.ServletRequest:接口
- javax.servlet.http.HttpServletRequest:子接口
常用方法
- String uri = req.getRequestURI(); // 获取请求的资源路径,如:/hello/MyServlet
- StringBuffer url = req.getRequestURL(); // 获取请求的统一资源定位符,如:http://localhost:80/hello/MyServlet
- String remoteHost = req.getRemoteHost(); // 获取客户端IP地址,如127.0.0.1
- String header = req.getHeader(String name); // 获取请求头,如:name="User-Agent"
- String parameter = req.getParameter(String s); // 获取请求的参数
- String[] parameterValues = req.getParameterValues(String s); // 获取请求的参数(多个值时使用,例如前端数据是一个多选框)
- String method = req.getMethod(); // 获取请求的方式,如:"GET"、"POST"、"PUT等
- req.setAttribute(String key, Object value); // 设置域数据
- Object attribute = req.getAttribute(String key); // 获取域数据
- req.setCharacterEncoding("utf-8"); // 设置请求体的字符集,用于解决请求的中文乱码问题
- req.getServletPath():返回请求URI,即如果请求地址是:http://ip:端口/project/abc,则返回/abc
请求转发相关
请求转发指的是服务器收到请求后,从一个资源跳转到另一个资源的操作。
- RequestDispatcher requestDispatcher = req.getRequestDispatcher(String path); // 获取请求转发对象,path表示新的请求路径,必须以/开始,"/"表示http://ip:port/工程名,映射到IDEA的webapp目录,例:"/servlet2"
- 通常后面需要跟一个转发的方法:requestDispatcher.forward(req, resp);
请求转换的特点:
- 浏览器地址栏没有变化
- 几次请求是同一次请求
- 共享Request域中的数据
- 可以转发到WEB-INF目录下(直接访问则不行),path设置为"/WEB-INF/..."
- 不能访问工程以外的资源,如:path设置为"www.baidu.com"就不行
Service的响应参数HttpServletResponse
HttpServletResponse和HttpServletRequest一样,每次请求进来时,Tomcat服务器会创建一个HttpServletResponse对象传递给Servlet程序使用。HttpServletRequest表示请求过来的信息,HttpServletResponse表示响应的信息
常用方法
- resp.setCharacterEncoding(String s); // 设置服务器字符集编码,配合resp.setHeader("Content-Type", "text/html; charset=UTF-8");(或resp.setContentType("text/html; charset=UTF-8");,使用setContentType时,不用setCharacterEncoding,它会同时设置服务器和客户端字符集,)可解决中文乱码问题,注:必须在获取流对象之前调用
- resp.setContentType(String s);//设置响应体内容类型等
两个响应流
- resp.getOutputStream();获取字节流,常用于下载,传递二进制数据
- resp.getWriter();获取字符流,常用于回传字符串
两个流同时只能使用一个 往客户端回传字符串数据: 1
2
3
4
5resp.setCharacterEncoding("UTF-8");
// 通过响应头设置浏览器使用UTF-8字符集
resp.setHeader("Content-Type", "text/html; charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("响应数据");
请求重定向相关
请求重定向指客户端向服务器发请求,服务器给定新地址,客户端用新地址去访问。
- resp.setStatus(int i); // 设置响应状态码,请求重定向时通常需要设置为302,表示重定向
- resp.setHeader(String name, String value); // 设置响应头,name:响应头名,value:响应头值,例如请求重定向时通常需要设置:resp.setHeader("Location", "http://www.baidu.com");指明新地址
- resp.sendRedirect(String url); // 直接进行请求重定向
请求重定向的两种实现: 方式一: 1
2resp.setStatus(302);
resp.setHeader("Location", "https://www.baidu.com");1
resp.sendRedirect("https://www.baidu.com");
- 浏览器地址栏会发生变化
- 两次请求
- 不共享Request域中的资源
- 不能访问WEB-INF下的资源
- 可以访问工程外的资源
Filter过滤器
- Filter过滤器是JavaWeb的三大组件之一
- Filter过滤器的作用:拦截请求,过滤响应
应用场景:
- 权限检查
- 日记操作
- 事务管理
- ......
使用Filter过滤器
编写一个类去实现javax.servlet.Filter接口
重写接口对应的方法,主要是doFilter方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public class MyFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 过滤登录的案例
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
if(user==null){
req.getRequestDispatcher("/login").forward(servletRequest, servletResponse);
return;
}else{
// 继续经过其它有可能的Filter或正常访问
filterChain.doFilter(servletRequest, servletResponse);
}
}
public void destroy() {
}
}在web.xml中配置,也可使得注解@WebFilter()代替
1
2
3
4
5
6
7
8
9
10<!-- 配置过滤器 -->
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.zephon.MyFilter</filter-class>
</filter>
<!-- 配置过滤路径 -->
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
Filter的生命周期
- 构造器方法
- init()初始化方法
- doFilter()过滤方法
- destroy()销毁方法
其中,1、2在Web工程启动时执行;3是每次拦截到请求时执行;4是停止Web工程时执行并真正销毁Filter
FilterConfig接口
- FilterConfig是Filter过滤器的配置相关接口
- Tomcat每次创建Filter时,也会同时创建一个FilterConfig实现类,包含了Filter配置文件的配置信息
- 作用:获取Filter过滤器的配置内容
- 获取Filter名称
:config.getFilterName(); - 获取Filter的
初始化参数:config.getInitParameter(String name); - 获取ServletContext对象;config.getServletContext();
- 获取Filter名称
FilterChain接口
Filter中的doFilter()中传递的参数的最后一个参数就是一个实现了FilterChain接口的实例,表示过滤器链 FilterChain中doFilter()方法的作用:
- 如果后面还有Filter,则执行下一个Filter
- 如果没有Filter了,则执行访问目标资源
多个Filter的执行过程:
- 客户端发起请求
- Filter1执行doFilter()中的chain1.doFilter()之前的代码
- 执行chain1.doFilter(),通过chain1.doFilter()调用Filter2的doFilter()
- 执行Filter2中doFilter()的chain2.doFilter()之前的代码
- 执行chain2.doFilter(),并通过chain2.doFilter()调用Servlet中对应的doXxx()方法请求资源
- Filter2中执行chain2.doFilter()之后的代码
- Filter1中执行chain1.doFilter()之后的代码
- 返回结果给客户端 > 多个Filter过滤器执行时,执行的优先顺序是由web.xml中从上到下的配置顺序决定
多个Filter过滤器执行的特点:
- 所有Filter和目标资源默认都在同一个线程中执行
- 多个Filter都使用同一个Request对象
Filter的拦截路径配置
精确匹配
1
2<!-- 表示请求地址必须是:http://ip:port/工程路径/target.jsp才会被拦截 -->
<url-pattern>/target.jsp</url-pattern>目录匹配
1
2<!-- 表示请求地址必须是:http://ip:port/工程路径/admin/*才会被拦截 -->
<url-pattern>/admin/*</url-pattern>后缀名匹配(不能以/开头)
> 注:过滤器只关心请求地址是否匹配,不关心请求资源是否存在1
2<!-- 表示请求地址必须以.html结尾才会被拦截 -->
<url-pattern>*.html</url-pattern>
Listener监听器
- Listener监听器是JavaWeb的三大组件之一
- 监听器的作用:监听某种事物的变化,通过回调函数反馈给用户(或程序)做一些相应的处理
继承关系:
- java.util.EventListener接口:是一个标识接口,表明是一个事件监听器
- javax.servlet.ServletContextListener接口,监听ServletContext对象的创建和销毁,监听到之后会分别调用内部对应的方法
- HttpSessionListener接口:监听HttpSession对象的创建和销毁的过程
- ServletRequestListener接口:监听ServletRequest对象的创建和销毁的过程
- ServletContextAttributeListener接口:监听ServletContext中属性的创建(add)、修改(replaced)和销毁(removed)
- HttpSessionAttributeListener接口:监听HttpSession中属性的创建、修改和销毁
- ServletRequestAttributeListener接口:监听ServletRequest中属性的创建、修改和销毁
- HttpSessionBindingListener接口:监听某个对象在Session域中的创建与移除
- HttpSessionActivationListener接口:监听某个对象在Session域中的序列化与反序列化
1
2
3
4public interface ServletContextListener extends EventListener {
void contextInitialized(ServletContextEvent var1); // 在ServletContext对象创建之后调用
void contextDestroyed(ServletContextEvent var1); // 在ServletContext对象销毁之后调用
}
使用ServletContextListener监听器
编写一个类去实现ServletContextListener接口
实现两个回调方法
1
2
3
4
5
6
7
8
9
10
11public class MyListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext被创建");
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext被销毁");
}
}在web.xml中配置监听器或者用注解@WebListener
1
2
3
4<!-- 配置监听器 -->
<listener>
<listener-class>com.zephon.MyListener</listener-class>
</listener>
Cookie
- Cookie是服务器通知客户端保存键值对的一种技术
- 客户端有了Cookie后,每次请求都发送给服务器
- 每个Cookie的大小不能超过4KB
Cookie的创建
- Cookie cookie = new Cookie(String key, String value); // 实例化Cookie
- resp.addCookie(Cookie cookie); // 通知浏览器保存Cookie > 注:创建Cookie可创建多个,将上述代码重复多次即可
1
2
3
4// 1.创建Cookie对象
Cookie cookie = new Cookie("key1", "value1");
// 2.通知客户端保存Cookie
resp.addCookie(cookie);
Cookie的获取
- Cookie[] cookies = req.getCookies(); // 返回所有Cookie
- cookie.getName(); // 返回Cookie的key
- cookie.getValue(); // 返回Cookie的值
Cookie的修改
方案一:
创建一个同名的Cookie对象
在构造器同时赋予新的Cookie值
调用resp.addCookie(cookie);
方案二:1
2Cookie cookie = new Cookie("key1", "newvalue");
resp.addCookie(cookie);找到需要修改的Cookie对象
调用setValue()方法赋予新的Cookie值
调用resp.addCookie(cookie);
1
2
3
4
5
6
7Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
if("key1".equals(cookie.getName())){
cookie.setValue("newvalue");
resp.addCookie(cookie);
}
}
Cookie的生命控制
- cookie.setMaxAge(int maxAge); // 设置cookie的最大生存时间,以秒为单位,默认值为-1
- maxAge为正数表示在指定秒数后过期
- maxAge为负数表示浏览器关闭则Cookie被删除
- maxAge为0时表示马上删除Cookie
Cookie的path属性
- Cookie的path属性可以有效过滤哪些Cookie可以发送给服务器,哪些不发
- path属性是通过请求的地址来进行有效地过滤
- 如CookieA的path为path=/工程路径,CookieB的path为path=/工程路径/abc,则当请求地址为htttp://ip:port/工程路径/index.html时,CookieA发送,CookieB不发送,当请求地址为http://ip:port/工程路径/abc/index.html时,两个Cookie都发送
- cookie.setPath(String uri); // 设置path
- 默认是当前请求路径
- 例:cookie.setPath(req.getContextPath() + "/abc");
Session
- HttpSession session = req.getSession();获取当前会话,没有则创建一个新的会话
- HttpSession session = req.getSession(boolean b);如果b为true,则和无参构造一样;如果b为false则返回null,不会创建新的会话
- HttpSession
- session.getId();获取sessionID,每个会话的ID是唯一的
- session.isNew();判断当前session是否是新创建的
- session.getMaxInactiveInterval();获得session的超时时间,超过指定的时长后Session就会被销毁,
- session.setMaxInactiveInterval(int i);设置session的非激活间隔时长,单位是秒,默认1800秒(30分钟),tomcat的配置文件web.xml中有
中有设置 为30 - 正数表示超时时长
- 负数表示永不超时(极少使用) > 注:Session中的超时,指的是客户端两次请求的最大间隔时长
- session.invalidate();使得会话立即(超时)失效(退出用户时使用)
- session.setAttribute(String key, Object value);向当前Session保存作用域保存数据,key:数据关键字,value:数据值
- session.getAttribute(String key);根据数据关键字从当前Session保存作用域中获取数据值
- session.removeAttribute(String key);根据数据关键字从当前Session保存作用域中删除对应的数据
Session的技术与Cookie的联系
- 服务器每次创建Session会话时,都会创建一个Cookie对象,这个Cookie对象的key永远是"JSESSIONID",value是新创建的Session的id值,然后通过响应将新创建的Cookie返回给客户端
- 浏览器收到数据后,创建Cookie
- 之后的每次请求,浏览器就会将Cookie携带上,即后面每次请求都会把Session的id以Cookie的形式发送服务器
- 然后服务器getSession()时就可以通过客户端发送的session的id找到对应的Session对象并返回
- 因此,如果浏览器将Cookie删除了,或者没有携带上,则服务器就会重新创建一个新的Session
保存作用域
原始情况下,保存作用域可以认为有4个:page(页面级别,现在几乎不用),request、session、application(ServletContext)
- request:一次请求响应的范围
- session:一次会话的范围
- application:整个应用程序范围有效
Thymeleaf
Thymeleaf:视图模板技术
导入对应的包:
1
2
3
4
5
6<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>web.xml中配置
1
2
3
4
5
6
7
8
9
10
11<!-- 配置上下文 -->
<context-param>
<!-- 配置前缀 -->
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/view/</param-value>
</context-param>
<context-param>
<!-- 配置后缀 -->
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>新建一个Servlet类ViewBaseServlet(暂时,以后框架中会自带)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
/**
* 处理模板
* thymeleaf会将这个逻辑视图名称对应到物理视图名称
* 逻辑视图名称: index
* 物理视图名称:view-prefix+逻辑视图名称+view-suffix
* @param templateName 视图名称
* @param req 请求
* @param resp 响应
* @throws IOException
*/
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}自定义类继承自ViewBaseServlet
基本语法
- ${变量名}:表示变量名对应的变量值
- ${}表达式可以从以下元素开始:
- 访问属性域的起点
- 请求域属性名
- session
- application
- param
- 内置对象
- #request
- #session
- #lists
- $strings
- 访问属性域的起点
- ${}表达式可以从以下元素开始:
- @{字符串}:在字符串前附加上下文路径,对于带参数的如:/order/process?id=123,type=a,可用@{order/process(id=\({id}, type=\){type})}表示
常用标签
th:表示名称空间,是在html标签中引入的:](http://www.thymeleaf.org">)
- th:text:设置标签文本值
- th:value:设置指定属性值
- th:if:分支判断
- th:unless:否则
- th:switch:分支
- th:each:循环,如:
- th:href:设置链接,以项目路径作为根路径
- th:insert:将目标的代码片段整个插入到当前标签内部
- th:replace:用目标的代码替换当前标签
- th:include:把目标的代码片段去除最外层标签,然后再插入到当前标签内部
内置对象
基本内置对象
- #ctx
- #vars
- #locale
- #request:HttpRequest对象
- #response:HttpResponse对象
- #session:HttpSession对象
- #servletContext:ServletContext对象
公共内置对象
- #conversion
- #dates
- #calendars
- #numbers
- #strings
- #objects
- #bools
- #arrays
- #lists:List相关方法
- #sets:Set相关方法
- #maps:Map相关方法
- #aggregates
- #ids
从Servlet到Spring过渡
Bilibili:手写SpringMVC和IOC IOC-控制反转:
- Spring前,定义:UserService userService = new UserServiceImpl();这如果出现在控制器某个方法内部则userService的作用域(生命周期)就是方法级别;如果出现在控制器类中,则userService是一个成员变量,其作用域(生命周期)与控制器一样
- Spring后,在applicationContext.xml中定义了userService,然后通过解析XML,产生的UserServiceImpl()实例,存放在beanMap中,而这个beanMap则在一个BeanFactory中。因此,转移(改变)了之前的Service实例、DAO实例等它们的生命周期,其生命周期控制权从程序员转移到了BeanFactory,这个现象就是所谓的控制反转
DI-依赖注入:
- Spring前,控制层定义:UserService userService = new UserServiceImpl();则控制层和Service层存在耦合,同理,Service层和DAO层也存在耦合
- Spring后,控制层定义:UserService userService = null; 然后在配置文件中配置bean,如:
1
2
3<bean id="user" class="com.zephon.controller.FruitController">
<property name="userService" ref="userService"/>
</bean>
自己简单手写SpringMVC(DispatcherServlet类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class DispatcherServlet extends ViewBaseServlet {
// 定义beanMap用来存放Bean中各个对象,其中key对应name,value对应实例化的对象
private Map<String, Object> beanMap = new HashMap<>();
/**
* 在init方法中解析applicationContext.xml文件
*/
public DispatcherServlet() {
try {
// 1. 获取配置文件路径
// ServletContext context = getServletContext();
// String contextConfigLocation = context.getInitParameter("contextConfigLocation");
// System.out.println(contextConfigLocation);
String contextConfigLocation = "applicationContext.xml";
// 2. 获取路径对应的流
InputStream is = getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
// 3. 创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 4. 创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 5. 创建Document对象
Document document = documentBuilder.parse(is);
// 获取所有bean标签对应的节点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
// 获取第i个节点
Node beanNode = beanNodeList.item(i);
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
// 如果是Element节点则强转
Element element = (Element) beanNode;
String beanId = element.getAttribute("id");
String beanClass = element.getAttribute("class");
// 反射创建对象
Object beanObj = Class.forName(beanClass).newInstance();
beanMap.put(beanId, beanObj);
}
}
} catch (ParserConfigurationException | ClassNotFoundException | IllegalAccessException | IOException |
SAXException | InstantiationException e) {
throw new RuntimeException(e);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// 获取请求路径,作为beanMap的key
String path = req.getServletPath();
String controllerName = path.substring(1, path.lastIndexOf(".do"));
System.out.println(controllerName);
Object beanObj = beanMap.get(controllerName);
// operate表示需要调用什么方法 add/update/delete......
String operate = req.getParameter("operate");
Method[] methods = beanObj.getClass().getDeclaredMethods();
try {
for (Method method : methods) {
System.out.println(method.getName());
if(operate.equals(method.getName())){
// 获取统一请求参数
Parameter[] parameters = method.getParameters();
Object[] paramValues = new Object[parameters.length];
method.setAccessible(true);
for (int i = 0; i < parameters.length; i++) {
if(parameters[i].getType() == HttpServletRequest.class){
paramValues[i] = req;
}else if(parameters[i].getType() == HttpServletResponse.class){
paramValues[i] = resp;
}else{
if(parameters[i].getType() == Integer.class){
paramValues[i] = Integer.valueOf(req.getParameter(parameters[i].getName()));
}else {
paramValues[i] = req.getParameter(parameters[i].getName());
}
}
}
System.out.println(Arrays.toString(paramValues));
// 控制器层方法调用
Object returnObj = method.invoke(beanObj, paramValues);
String returnStr = (String) returnObj;
// 视图处理
if(returnStr!=null && returnStr.startsWith("redirect:")){
String s = returnStr.substring("redirect:".length());
resp.sendRedirect(s);
}else{
super.processTemplate(returnStr, req, resp);
}
return;
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}自己简单完成依赖注入功能
BeanFactory和ClassPathXmlApplicationContext
1
2
3
4public interface BeanFactory {
Object getBean(String name);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62public class ClassPathXmlApplicationContext implements BeanFactory{
// 定义beanMap用来存放Bean中各个对象,其中key对应name,value对应实例化的对象
private Map<String, Object> beanMap;
public ClassPathXmlApplicationContext(){
beanMap = new HashMap<>();
try {
String contextConfigLocation = "applicationContext.xml";
// 2. 获取路径对应的流
InputStream is = getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
// 3. 创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 4. 创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 5. 创建Document对象
Document document = documentBuilder.parse(is);
// 获取所有bean标签对应的节点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
// 获取第i个节点
Node beanNode = beanNodeList.item(i);
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
// 如果是Element节点则强转
Element element = (Element) beanNode;
String beanId = element.getAttribute("id");
String beanClass = element.getAttribute("class");
// 反射创建对象
Object beanObj = Class.forName(beanClass).newInstance();
beanMap.put(beanId, beanObj);
}
}
// 组装bean之间的依赖关系
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
Element element = (Element) beanNode;
String beanId = element.getAttribute("id");
NodeList childNodeList = beanNode.getChildNodes();
for (int j = 0; j < childNodeList.getLength(); j++) {
Node node = childNodeList.item(j);
if(node.getNodeType() == Node.ELEMENT_NODE && "property".equals(node.getNodeName())){
Element e = (Element) node;
String name = e.getAttribute("name");
String ref = e.getAttribute("ref");
Object obj = beanMap.get(beanId);
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, beanMap.get(ref));
}
}
}
}
} catch (ParserConfigurationException | ClassNotFoundException | IllegalAccessException | IOException |
SAXException | InstantiationException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
public Object getBean(String id) {
return beanMap.get(id);
}
}DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory;
public DispatcherServlet() {
}
public void init() throws ServletException {
super.init();
beanFactory = new ClassPathXmlApplicationContext();
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// 获取请求路径,作为beanMap的key
String path = req.getServletPath();
String controllerName = path.substring(1, path.lastIndexOf(".do"));
System.out.println(controllerName);
Object beanObj = beanFactory.getBean(controllerName);
// operate表示需要调用什么方法 add/update/delete......
String operate = req.getParameter("operate");
Method[] methods = beanObj.getClass().getDeclaredMethods();
try {
for (Method method : methods) {
System.out.println(method.getName());
if(operate.equals(method.getName())){
// 获取统一请求参数
Parameter[] parameters = method.getParameters();
Object[] paramValues = new Object[parameters.length];
method.setAccessible(true);
for (int i = 0; i < parameters.length; i++) {
if(parameters[i].getType() == HttpServletRequest.class){
paramValues[i] = req;
}else if(parameters[i].getType() == HttpServletResponse.class){
paramValues[i] = resp;
}else{
if(parameters[i].getType() == Integer.class){
paramValues[i] = Integer.valueOf(req.getParameter(parameters[i].getName()));
}else {
paramValues[i] = req.getParameter(parameters[i].getName());
}
}
}
System.out.println(Arrays.toString(paramValues));
// 控制器层方法调用
Object returnObj = method.invoke(beanObj, paramValues);
String returnStr = (String) returnObj;
// 视图处理
if(returnStr!=null && returnStr.startsWith("redirect:")){
String s = returnStr.substring("redirect:".length());
resp.sendRedirect(s);
}else{
super.processTemplate(returnStr, req, resp);
}
return;
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}使用监听器编写ContextLoaderListener进一步改进
将ClassPathXmlApplicationContext中的配置文件地址提取
1
2
3
4
5
6
7
8
9
10
11
12public ClassPathXmlApplicationContext(){
this("applicationContext.xml");
}
public ClassPathXmlApplicationContext(String path){
if(path==null || "".equals(path)){
throw new RuntimeException("配置文件有错误");
}
beanMap = new HashMap<>();
try {
// 2. 获取路径对应的流
InputStream is = getClass().getClassLoader().getResourceAsStream(path);
......将beanFactory实例过过程放到监听器中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ContextLoaderListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext(
String path = servletContext.getInitParameter("contextConfigLocation")
BeanFactory beanFactory = new ClassPathXmlApplicationContext(path);
servletContext.setAttribute("beanFactory", beanFactory);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}修改DispatcherServlet中的init方法
1
2
3
4
5
6
7
8
9
10
11
12
public void init() throws ServletException {
super.init();
beanFactory = new ClassPathXmlApplicationContext();
ServletContext servletContext = getServletContext();
Object obj = servletContext.getAttribute("beanFactory");
if(obj!=null){
beanFactory = (BeanFactory) obj;
}else{
throw new RuntimeException("IOC容器获取失败");
}
}
使用监听器完成自定义的编程式事务管理
ConnectionUtils:
TransactionManager:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class ConnectionUtils {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
public static Connection createConnection() throws SQLException, IOException, ClassNotFoundException {
InputStream is = ConnectionUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(is);
Class.forName(properties.getProperty("driverClass"));
return DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("user"), properties.getProperty("password"));
}
public static Connection getConnection() throws SQLException, IOException, ClassNotFoundException {
Connection connection = threadLocal.get();
if(connection==null){
connection = createConnection();
threadLocal.set(connection);
}
return threadLocal.get();
}
public static void closeConnection() throws SQLException {
Connection connection = threadLocal.get();
if(connection!=null){
if(!connection.isClosed()) {
connection.close();
threadLocal.set(null);
}
}
}
}OpenSessionInViewFilter:1
2
3
4
5
6
7
8
9
10
11
12
13
14// 未考虑事务嵌套等负责情况
public class TransactionManager {
public static void beginTransaction() throws SQLException, IOException, ClassNotFoundException {
ConnectionUtils.getConnection().setAutoCommit(false);
}
public static void commit() throws SQLException, IOException, ClassNotFoundException {
ConnectionUtils.getConnection().commit();
ConnectionUtils.closeConnection();
}
public static void rollback() throws SQLException, IOException, ClassNotFoundException {
ConnectionUtils.getConnection().rollback();
ConnectionUtils.closeConnection();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class OpenSessionInViewFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){
try {
TransactionManager.beginTransaction();
TransactionManager.commit();
} catch (SQLException | IOException | ClassNotFoundException e) {
e.printStackTrace();
try {
TransactionManager.rollback();
} catch (SQLException | IOException | ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
}
public void destroy() {
}
}- 本文链接: http://www.zephon.eu.org/blog/2022/07/30/JavaWeb一览/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC 许可协议。转载请注明出处!