JAVA入门基础_JAVAWEB
1、前端知识的学习
HTML(Hyper Text Markup Language)超文本标记语言
常见标签
-
<html></html>
-
<head></head>
-
<meta charset="utf-8"/>
(包裹在head标签中) -
<body></body>
-
<footer></footer>
-
<hr/>
-
<br/>
-
<table></table>
、<tr></tr>
、<th></th>
、<td></td>
(不常用) -
<frameset></frameset>
、<frame></frame>
、<iframe></iframe>
文字、段落标签
-
<h1></h1> ~ <h6></h6>
-
 
、©
、<
、≤
、>
、≤
、≥
-
<p></p>
、<span></span>
、<u></u>
、<i></i>
、<b></b>
、<sub></sub>
、<sup></sup>
图片标签
<img src="图片地址" alt="当图片无法显示时显示" width="宽度" height="高度" title="鼠标悬浮显示的文字" />
列表标签
-
有序列表
<ol type="A|a|I|i|1" start="n 从几开始"> </ol>
<li></li>
<li></li>
-
无序列表
<ul type="dist|square|circle"> </ul>
<li></li>
<li></li>
-
自定义列表
<dl></dl>
<dt></dt>
<dd></dd>
表单
-
<form ation="需要提交到的地址" method="post|get(最大4kb,显示在地址栏)"></form>
<input type="输入框的类型" name="提交时的key" value="提交时的值"></input>
输入框<input type="text">
普通文本框<input type="textarea"></input>"
多行文本框<input type="password">
密码框<input type="radio" name="gender">
单选框,根据name完成互斥<input type="checkbox" name="hobby" checked>"
多选框,checked意思是默认选中<input type="select"><option selected></option></input>"
下拉框<input type="submit">"
提交按钮<input type="reset">"
充值按钮<input type="button">"
普通按钮
-
提示:input标签中的一些属性解释
- type:类型
- name:相当于发送数据时的key
- value :相当于发送数据时的value
- checked:默认选中
- selected:默认选择
a标签
<a></a>
href="链接地址"
target="_self|_blank|_parent|_top|framename"
Tomcat新建项目-部署-运行-访问
CS/BS架构的区别
-
CS:客户端服务器架构模式
- 优点:可以将一部分安全要求不高的计算存储任务交由客户端进行,能够减轻服务器的压力以及网络负荷
- 缺点:需要安装、维护成本高
-
BS:浏览器服务端架构模式
- 优点:不需要安装,打开浏览器即可,维护成本低。
- 缺点:服务端需要承载所有的压力,服务端的负载较重,并且由于所有的计算、存储任务都由服务端处理好了再返回给客户端,因此需要频繁的进行网络交互,网络负荷高
下载安装Tomcat、Tomcat的文件目录解析
-
安装Tomcat,我这下载的是压缩版本,因此解压即可。注意:配置JAVA_HOME以及Tomcat解压目录不能有中文
-
由于Tomcat是用JAVA跟C语言写的,因此Tomcat运行过程中也需要JAVA的虚拟机来运行,因此需要配置JAVA_HOME
创建一个最原始的JAVAWEB项目到webapps目录下
启动Tomcat并通过(context root)访问项目
context root:项目名称,一般为webapps目录下的根目录名,我这里是fristWebProject
- 启动Tomcat,通过双击bin目录下的startup.bat文件
使用IDEA工具完成WEB项目的创建、部署、运行
创建一个WEB项目
- file -> new -> project
- 注意这个web包的颜色
将一个普通项目修改为一个WEB项目
-
假如我们创建了一个普通的JAVA项目,那么此时就只有一个src目录,并没有web项目,我们想要修改成一个web项目
-
File -> Project Structure -> Facts -> 点击+(加号) -> WEB
-
修改Deployment Descriptors 下的Path目录,需要指定到web.xml文件
-
修改WEB Resource Directories下的Web Resource Directory的路径,需要指定到web目录
导入一个WEB项目时,web目录没有变颜色时
- 跟将一个普通项目修改为一个WEB项目的操作有点类似,不过第三步和第四步不再是修改,而是添加。
部署WEB项目并运行
配置一个Tomcat模板,可以考虑添加自动更新类和资源
-
(1)点击页面中的Add Configuration,点击Templates下的TomcatServer下的Local
-
(2)在页面右侧配置Tomcat的路径,注意只保持到Tomcat安装路径即可
-
(3)看情况选择如下功能
部署项目(添加artifact后添加全局lib)
-
(1)配置好了之后,再次点击Add Configuration,点击左侧的+(加号)添加一个Tomcat
-
(2)配置一个artifact(解压后的web项目),此时也可以修改一下context root路径
-
(3)如果想要创建一个lib而让所有模块都可用的话,就在父级项目中创建一个lib放jar包
- 其他的模块可以通过Project Structure -> Modules -> 选择需要导入jar包的项目 -> 选择Dependencies -> 添加一个Library
直接点击Run或者Debug运行
Idea使用Tomcat运行时需要注意的点以及项目的打包
-
IDEA在Tomcat项目运行时,会自动通过我们部署的Tomcat路径作为基础,复制一个新的Tomcat并将项目放在该新的Tomcat目录下运行。
-
Exploded的是创建解压后的war包,Archive的是war包(打包的时候用,添加一个Archive之后可以使用Idea的Build功能打包)
Servlet 服务端程序(Server Applet)
创建一个最简单的Servlet
创建一个JAVA类,继承HttpServlet
配置web.xml文件
设置一个页面的表单提交地址为web.xml文件中配置的url-pattern
直接访问页面即可
Servlet中获取参数(预防乱码)
-
获取参数的语法:
request.getParameter(String var1);
-
Tomcat8解决乱码问题get、post
-
get请求乱码(配置文件中是server.xml已经默认了字符集为UTF-8,无需解决)
-
post,在获取参数时,设置获取参数时采用的编码。
request.setCharacterEncoding("UTF-8");
-
-
Tomcat7解决乱码问题
- get请求乱码的2种解决方式
- (1)修改server.xml文件,在Connector 标签 中添加
URIEncoding="utf-8"
- (2)String(Request.getParameter("username").getBytes("ISO8859-1"), "UTF-8");
- (1)修改server.xml文件,在Connector 标签 中添加
- post请求乱码的解决方式同Tomcat8解决方式一致
- get请求乱码的2种解决方式
Servlet的继承关系以及service方法
-
Servlet中的继承关系图
-
其中在接口Servlet中,看到了提供服务的service方法。
- GenericServlet抽象类实现了Servlet接口,但是并没有重写service方法
- HttpServlet抽象类继承了GenericServlet,重写了service方法
- 实质上每次提供服务的都是service方法,(重写doGet和doPost方法也能够接收到请求的原因是因为HttpServlet中的service方法会通过请求方式调用doGet、doPost等方法)
- GenericServlet抽象类实现了Servlet接口,但是并没有重写service方法
Servlet的生命周期和Servlet的单例问题
-
初始化状态,对应init()方法。注意:初始化前会通过反射创建Servlet对象,如果自定义的Servlet中将空参构造方法进行了私有,将会直接抛出异常。
-
服务状态,对应service()方法。
-
销毁状态,对应destory()方法
-
在web.xml文件当中将同一个Servlet配置了多次,那么该Servlet对象就会被加载多次,变成多例模式。
-
若是在web.xml文件中配置了一个Servlet并且仅配置了一个,那么就是单例模式,该Servlet的实例只会创建一次(需要注意线程安全问题,可以的话不要定义成员变量)。
Servlet的创建时机
-
默认Servlet在第一次被访问时,才会进行初始化操作
-
优点:提高了服务器的启动速度
-
缺点:会影响第一次访问时的响应速度
-
修改Servlet的初始化时机为跟随Tomcat容器运行时一起创建,修改web.xml,在servlet标签中添加
<load-on-startup>1</load-on-startup>
:该值最小为0,随意写一个数值,数值越小,创建的优先级越高
HTTP协议的请求信息与响应信息
- HTTP协议是什么?
HTTP超文本传输协议其实就是请求与相应的一个规则,请求方的请求按照这个协议规则,响应方的响应也按照这个协议规则。
请求信息包含3个部分
-
请求行
- 1、请求的方式
- 2、请求的URL
- 3、请求的协议(一般都是HTTP1.1)
-
请求消息头:包含客户端要告诉服务器的信息
- 浏览器型号、版本、能接收的内容类型、能接收的语言等等
-
请求体(3种情况)
- get(querystring)
- post(form data)
- json(request payload)
响应信息包含3个部分
-
响应行
- 1、协议
- 2、响应状态码
- 3、响应状态(ok等)
-
响应消息头:包含服务器的信息,内容的媒体类型、编码、内容长度等
-
响应体:响应的实际内容,比如html、js、css等资源
HTTP协议无状态问题的解决方案Session
-
什么是无状态
无状态:无论客户端访问了多少次服务端,服务端都无法识别客户端的身份。 -
如何解决HTTP无状态的问题,通过Session会话跟踪技术
-
当客户端第一次访问服务端的时候,将会为客户端分配一个唯一的SessionID
-
当客户端再次访问服户端时,就会携带着这个SessionId访问,服务端就可以通过获取Session来判断客户端的身份
-
-
获取Session的常用API
- 获取Session,
request.getSession();
,当前客户端没有Session时为其创建一个 - 获取Session,
request.getSession(true);
,当前客户端没有Session时为其创建一个 - 获取Session,
request.getSession(false);
,当前客户端没有Session时返回null
- 获取Session,
请求转发与重定向
-
请求转发
- 客户端:只发送了一次请求
- 服务端:内部可以进行多次请求转发
-
重定向
- 客户端:对于客户端来说,不止发送了一个请求
- 服务端:相当于直接让客户端重新发送一个请求到指定的URL
Thymeleaf的快速入门
引入jar包
编写一个Servlet视图解析器
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
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);
}
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());
}
}
创建一个Servlet继承该视图解析器
public class ThymeleafDemoServlet extends ViewBaseServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 直接跳转界面,这里相当于 /hello.html,因为刚刚配置视图解析器的时候有前缀和后缀
super.processTemplate("hello", req, resp);
}
}
使用视图解析器跳转的页面就可以使用thymeleaf标签了
-
html文档的开头
<html xmlns:th="http://www.thymeleaf.org">
-
常用的几个标签
1. 获取作用域中的变量
${request.变量值}、${session.变量值}
2. 编写路径
@{/css/a.css} 相当于找到项目web根路径下的css文件夹下的a.css
3. each循环
<tr th:each="user,userStat : ${list}">
<td th:text="${user.userName}"></td>
<td th:text="${user.email}"></td>
<td th:text="${user.isAdmin}"></td>
<th th:text="${userStat.index}">当前迭代对象的index(从0开始计算)</th>
<th th:text="${userStat.count}"> 当前迭代对象的index(从1开始计算)</th>
<th th:text="${userStat.current.userName}">当前迭代变量</th>
<th th:text="${userStat.even}">是否偶数</th>
<th th:text="${userStat.odd}">是否偶数</th>
<th th:text="${userStat.first}">当前循环是否是第一个</th>
<th th:text="${userStat.last}">当前循环是否是最后一个</th>
</tr>
自定义究极简版的SpringMvc与Spring
自定义SpringMVC的DispatchServlet类
DispatchServlet所需要实现的功能
(1)接收**所有的客户端**请求
(2)通过客户端**请求路径**找到对应的控制器和方法,完成调用
(3)获取控制器执行方法后的返回值(String类型),进行**视图解析**工作
整体的思路如下:
(1)创建一个DispatchServlet类,继承上面写的试图解析器ViewBaseServlet()
(2)编写一个成员变量, 该变量为BeanFactory,作用是获取Spring容器中的bean
(2)重写init()方法
- 调用父类的init()方法完成视图解析器的初始化
- 完成BeanFactory的初始化,`beanFactory = new ClassPathXmlApplication();`
(3)重写service(HttpServletRequest request,HttpServletResponse response)方法
- 通过request.getPathInfo()方法获取客户端的请求路径
- 通过自定义的规则分割出请求的控制器以及方法
- 通过beanFactory获取到需要请求的控制器
- 再通过该控制器获取到所有的方法
- 使用客户端请求中分割出的方法名与控制器中的**各方法名进行匹配**,如果匹配到了,则获取该方法中的**所有参数列表**(需要添加`-parameters`JAVA编译参数),通过参数列表中的名称和类型来进行**参数获取**后存储到一个Object数组中
(4)进行方法的调用
(5)获取到方法的返回值,通过该返回值进行视图解析
代码实现
import com.codestarts.io.BeanFactory;
import com.codestarts.io.ClassPathXmlApplication;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* @author codeStars
* @date 2022/8/25 15:05
* 自定义SpringMvc的核心控制器DispatchServlet
*/
@WebServlet("/codestarts/*")
public class DispatchServlet extends ViewBaseServlet {
private BeanFactory beanFactory;
@Override
public void init() throws ServletException {
// 初始化模板引擎
super.init();
// 初始化spring容器
beanFactory = new ClassPathXmlApplication();
}
/**
* 注意,访问时: /控制器bean/方法名?参数即可,传参只考虑单值,以及Integer的判断
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 获取客户端发送请求中的地址
String pathInfo = request.getPathInfo();
// 获取到需要访问的控制器
int start = pathInfo.indexOf("/") + 1;
int end = pathInfo.lastIndexOf("/");
String controllerName = pathInfo.substring(start, end);
// 获取需要调用的方法
String methodName = pathInfo.substring(end + 1);
// 通过容器获取对应的控制器完成调用
Object controller = beanFactory.getBean(controllerName);
// 获取到控制器所有的方法
Method[] methods = controller.getClass().getMethods();
for (Method method : methods) {
if(method.getName().equals(methodName)) {
// 1. 获取参数,使用 Object[] params来获取
Parameter[] parameters = method.getParameters();
Object[] params = new Object[parameters.length];
// 获取参数
for (int i = 0; i < parameters.length; i++) {
// 获取单独的一个参数
Parameter parameter = parameters[i];
// 获取参数名
String paramName = parameter.getName();
// 判断参数名,根据参数名获取参数
if ("request".equals(paramName)) {
params[i] = request;
}else if ("response".equals(paramName)) {
params[i] = response;
}else if ("session".equals(paramName)) {
params[i] = request.getSession();
}else {
// 获取到请求中传递的参数
String paramValue = request.getParameter(paramName);
Object param = paramValue;
// 如果是Integer类型,则将String转换为Integer
if ("java.lang.Integer".equals(parameter.getType().getName())){
if (param != null) {
param = Integer.parseInt(paramValue);
}
}
params[i] = param;
}
}
// 2. 执行方法
Object returnObj = method.invoke(controller, params);
// 3. 进行视图解析
String path = "";
if (returnObj != null) {
path = (String) returnObj;
}
if (path.startsWith("redirect:")) {
response.sendRedirect(path.substring("redirect:".length()));
}else {
super.processTemplate(path, request, response);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
自定义Spring容器,完成IOC控制反转与DI依赖注入
Spring容器需要实现的功能
(1)有一个Map容器,Key的类型为String,Value的类型为Object
(2)在src路径下创建一个applicationContext.xml文件,只需要在该xml文件中配置组件以及组件之间的依赖关系即可
(3)解析xml文件,将各个组件都加载进Map容器当中
(4)通过一个getBean(String id)的方法获取组件
整体编写思路
(1)创建一个BeanFactory接口,里面只有一个`Object getBean(String id)`的方法
(2)创建一个构造函数,在构造函数中完成数据的初始化
- 通过DocumentBuilderFactory来获取到解析Xml的对象
- 获取到Xml中的所有bean节点,再根据bean节点中记录的id,class来完成bean容器的第一次初始化
- 再次获取到Xml中的所有bean节点,通过bean节点获取到其所有的子节点进行遍历,若是找到元素名为property的节点,则获取到其中的name跟ref完成父节点的依赖注入。
代码实现
Xml文件格式
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id="fruitDao" class="com.codestarts.dao.impl.FruitDaoImpl"></bean>
<bean id="index" class="com.codestarts.controller.IndexController">
<property name="fruitDao" ref="fruitDao"></property>
</bean>
</beans>
BeanFactory与ClassPathXmlApplication的实现
- BeanFactory.java
public interface BeanFactory {
Object getBean(String id);
}
- ClassPathXmlApplication.java
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author codeStars
* @date 2022/8/26 9:30
* 自定义Spring容器
*/
public class ClassPathXmlApplication implements BeanFactory {
private static Map<String, Object> beanMap = new HashMap<>();
public ClassPathXmlApplication () {
try {
InputStream applicationContextXml = this.getClass().getClassLoader().getResourceAsStream("application.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document parseXml = documentBuilder.parse(applicationContextXml);
// 获取到所有的bean节点
NodeList nodeList = parseXml.getElementsByTagName("bean");
// 遍历所有的节点,并进行控制反转
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
// 判断是否为元素节点
if(item.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap attributes = item.getAttributes();
String beanId = attributes.getNamedItem("id").getNodeValue();
Object bean = Class.forName(attributes.getNamedItem("class").getNodeValue()).newInstance();
beanMap.put(beanId, bean);
}
}
// 再次遍历所有的bean节点,进行依赖注入
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
// 获得所有bean节点下的子节点,判断为元素节点时,进行依赖注入
NodeList childNodes = item.getChildNodes();
for (int y = 0; y < childNodes.getLength(); y++) {
Node childNode = childNodes.item(y);
if(childNode.getNodeType() == Node.ELEMENT_NODE && "property".equals(childNode.getNodeName())) {
// 获取到bean节点id
String currentBeanId = item.getAttributes().getNamedItem("id").getNodeValue();
NamedNodeMap attributes = childNode.getAttributes();
// 需要注入的属性名
String name = attributes.getNamedItem("name").getNodeValue();
// 需要注入的beanId
String ref = attributes.getNamedItem("ref").getNodeValue();
// 获取到需要被依赖注入的bean,以及需要被依赖注入的bean的属性
Object beanNode = beanMap.get(currentBeanId);
Field field = beanNode.getClass().getDeclaredField(name);
field.setAccessible(true);
// 获取到要被注入的组件
Object beanNodeElementObj = beanMap.get(ref);
// 注入进去,再重新存到Map容器
field.set(beanNode, beanNodeElementObj);
beanMap.put(currentBeanId, beanNode);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}
如何使用呢?
- 编写一个Controller
public class IndexController {
private FruitDao fruitDao = null;
public void add(HttpServletRequest request, HttpServletResponse response) {
System.out.println("index: add");
}
}
- 如果想要访问该控制器的add方法,则只需要在浏览器中
服务器路径/codestarts/index/add
即可
Servlet的常用api
Servlet的初始化方法(2个)
-
Servlet的初始化方法有2个,分别为public void init() throws ServletException(),该方法用于我们进行一些自定义初始化操作
-
还有一个init()方法,该方法会自动调用init()方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
获取自定义Servlet、ServletContext的配置信息
自定义Servlet
-
- 通过注解或xml的方式定义参数
<servlet>
<servlet-name>name</servlet-name>
<init-param>
<param-name>param1</param-name>
<param-value>oneParam</param-value>
</init-param>
</servlet>
或者
@WebServlet(initParams = { @WebInitParam(name = "param1",value = "onwParam") } )
- 在Servlet获取到配置的参数
String param1 = this.getInitParameter("param1");
String param2 = this.getInitParameter("param2");
获取ServletContext的配置信息
- 在web.xml文件中进行参数的配置
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
- 在Servlet中获取参数
ServletContext servletContext1 = this.getServletContext();
servletContext1.getInitParameter("view-prefix");
理解MVC
-
Model 模型,其中包含pojo/vo模型、业务层BO模型、持久层DAO、数据传输模型DTO等
-
View视图,用于将结果显示到页面上,完成与客户端的交互
-
Controller控制器,用于接收客户端的请求
业务层与持久层的关系及作用
-
业务层与持久层的关系
业务层BO中的一个业务方法,可能会调用多个持久层DAO中的方法来完成一个业务功能。 -
业务层的作用
业务层用于处理一个个的业务单元,例如一个业务叫做注册,一个业务叫做登录。而一个个的业务功能中都可能会调用多个不同的DAO,(此时应该考虑事物的原子性问题) -
持久层的作用
持久层中的一个个不同的DAO通常用于执行一个个的单元操作,一个DAO方法只做一件事,那就是新增或修改、删除或更新。
IOC控制反转与DI依赖注入
-
IOC控制反转
在以往项目当中,我们一个个类对象都是由我们程序员来控制它的生命周期的,例如定义为局部变量,则方法执行完毕销毁,定义为成员变量,则该类的示例没有引用指向时销毁。
而IOC控制反转则是通过将对需要操作的对象(这里称之为一个个的bean)将其存储到一个容器中,又这个容器来对我们需要操作的bean进行统一的管理,我们将这种行为称之为IOC控制反转。 -
DI依赖注入
通常与IOC控制反转一同运用,在将我们需要操作的bean对象都交于IOC容器管理时,一个个bean对象中的成员变量的引用可能是其他在容器中的bean对象。
那么能不能程序在将bean对象添加到IOC容器的时候,顺便将一个个成员变量赋值呢,我们将这种行为称之为依赖注入。
Filter过滤器
过滤器的作用
在一个JAVAWEB程序中,我们需要进行一些容易的行为时,可以采用过滤器。过滤器可以在调用指定的Servlet之前、以及之后进行一系列的行为。注意是在Servlet调用之前拦截到、以及Servlet返回结果到View视图层时再次拦截。
过滤器的使用
编写一个JAVA类,实现Filter接口(注意是在Servlet包下的)。重写其中的3个方法。
/**
* @author codeStars
* @date 2022/8/26 13:29
* 拦截/codestarts/开头的所有请求
*/
@WebFilter(urlPatterns = "/codestarts/*")
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("这是初始化方法");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Servlet调用之前");
// 放行到下一个Filter、如果没有下一个Filter时,则到了Servlet调用的时候了
filterChain.doFilter(servletRequest, servletResponse);
// 方法调用之后
System.out.println("Servlet调用之后");
}
@Override
public void destroy() {
System.out.println("这是销毁方法");
}
}
过滤链
- 如果项目中存在多个Filter过滤器,并且都拦截了同一个请求,那么他们的执行顺序是怎样的呢?
-
如果是以注解的方式配置,则会通过全类名来进行字符串大小排序,从小到大依次拦截。
-
如果是xml配置的方式,则会根据配置的先后顺序进行拦截。
-
事物管理
思考事物管理的代码应该写在哪
-
首先是想到写在DAO持久层,但是DAO仅仅是单纯的增删改查中的任意一项,并且一个个业务代码的编写应该是在业务层,一个功能很可能需要使用多个DAO,因此DAO层不考虑
-
其次是想到写在service业务层中编写事物管理的代码,这个时候可以解决事物的原子性问题,在执行多个DAO之前,进行事物的管理。注意要让所有的DAO都使用同一个连接。但是问题在于:难道每一个业务方法都进行事务的管理吗?因此业务层进行事物管理也不太理想。
-
最终想到了定义一个OpenSessionInViewFilter过滤器,在该过滤器中统一进行事物的管理。
实现如上设想的前提
-
需要让一个事物中的所有Dao都使用同一个连接(使用ThreadLocal)
-
不能让业务层、持久层对自身的异常直接进行try catch内部处理,不然会导致Filter过滤器无法捕获到异常
-
实现思路
(1)定义一个OpenSessionInViewFilter过滤器,doFilter()方法中进行所有代码的try catch捕获,放行前开启事物、放行后提交事物、捕获到异常则回滚事物
(2)创建一个TransactionManager类,其中进行事物的开启、关闭、回滚
(3)创建一个ConnUtils类,用于通过数据源获取连接等,每个线程需要唯一的一个连接(ThreadLocal)。
监听器 & Cookie & kaptcha验证码插件
一共有8个监听器(了解)
ServletContextListener 监听Application的创建与销毁
HttpSessionListener 监听Session的创建与销毁
ServletRequestListener 监听Request对象的创建与销毁
ServletContextAttributeListener 监听Application作用域添加、删除、修改元素
HttpSessionAttributeListener 监听Session作用域添加、删除、修改元素
ServletRequestAttributeListener 监听request作用域添加、删除、修改元素
httpSessionActivationListener 监听session作用域中对象的钝化(序列化)与活化(反序列化)
httpSessionBindingListener 监听session与对象的绑定
ServletContextListener的应用(创建SpringIOC容器)
@WebListener
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 获取到ServletContext,通过web.xml文件中的配置获取xml配置文件的名称
ServletContext servletContext = servletContextEvent.getServletContext();
String applicationXmlName = servletContext.getInitParameter("contextConfigLocation");
// 创建IOC容器
BeanFactory beanFactory = new ClassPathXmlApplication(applicationXmlName);
// 添加到Application作用域
servletContext.setAttribute("beanFactory", beanFactory);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {}
}
Cookie的作用
Cookie用于给客户端发送一部分数据,存储到客户端当中,之后客户端再次请求服务器时,可以携带这部分数据访问,服务器可以根据这部分数据进行一系列的判断等。
-
应用场景
- 记住密码,客户端在第一次登录时,将客户端发送的用户名和密码响应给客户端,保存到客户端的Cookie当中,下一次客户端再次访问时,服务器就可以获取到用户名和密码,从而直接展示在页面当中。
- 十天免登录
-
使用演示
// 创建1个Cookie,响应给客户端
Cookie usernameCookie = new Cookie("username","张三");
// 设置超时时间,以秒为单位,10天
usernameCookie.setMaxAge(60 * 60 * 24 * 10);
// 设置指定路径客户端才会发送该Cookie
usernameCookie.setPath("/codestarts/");
// 添加cookie到响应中
response.addCookie(usernameCookie);
// 获取客户端发送过来的Cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + "--" + cookie.getValue());
}
// 随便响应点内容
response.setCharacterEncoding("UTF-8");
response.getWriter().println("Cookie保存成功了");
kaptcha验证码的使用
-
引入jar包,下载地址
-
在web.xml文件中配置(其实就是一个Servlet)
<servlet>
<servlet-name>kaptcha</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>kaptcha</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
- 在web界面中的编写一个img标签,src写Servlet的请求路径
<img src="kaptcha.jpg" alt="">
- 在Servlet当中可以通过Session作用域获取
Object kaptcha_session_key = session.getAttribute("KAPTCHA_SESSION_KEY");
Vue的快速入门
绑定元素属性
- 插值表达式{{}}与元素绑定
-- javascript代码
<script type="text/javascript" src="js/vue.js"></script>
<script>
window.onload = function(){
var app = new Vue({
el: "#app",
data: {
msg: "张三",
msg2: "李四"
}
});
}
</script>
-- html代码
<div id="app">
<p>{{msg}}</p>
这是一个文本框:<input type="text" v-bind:value="msg2">
</div>
双向绑定数据
-- javascript代码
<script type="text/javascript" src="js/vue.js"></script>
<script>
var app;
window.onload = function(){
app = new Vue({
el: "#app",
data: {
msg: "张三",
}
});
}
function alertMsg() {
alert(app.msg);
}
</script>
-- html代码
<div id="app">
这是一个文本框:<input type="text" v-model:value="msg"><br>
<input type="button" value="点我即可查看当前msg的值" onclick="alertMsg()">
</div>
条件渲染
// javascript代码
window.onload = function(){
var app = new Vue({
el: "#app",
data: {
num: 1,
}
});
}
-- html代码
<div id="app">
这是一个文本框:<input type="text" v-model:value="num"><br>
<!-- 如果num=1,则显示该行,注意会删除该div,注意if和else之间的不能有其他的节点 -->
<div v-if="num==1" style="width: 40px;height: 40px;background: #1E9FFF"></div>
<div v-else style="width: 40px;height: 40px;background: #00FF00"></div>
<!-- 如果num=1,则显示该行,通过节点的display属性控制 -->
<div v-show="num==1" style="width: 40px;height: 40px;background: #5FB878"></div>
</div>
列表渲染
// javascript代码
window.onload = function(){
var app = new Vue({
el: "#app",
data: {
userList: [
{uid:1, uname:"张三", pwd:"123"},
{uid:2, uname:"王五", pwd:"333"},
{uid:3, uname:"赵六", pwd:"222"},
{uid:4, uname:"二花", pwd:"111"}
]
}
});
}
// html代码
<div id="app">
<ul v-for="user in userList">
<li>{{user.uid}}</li>
<li>{{user.uname}}</li>
<li>{{user.pwd}}</li>
</ul>
</div>
事件驱动
// javascript代码
window.onload = function(){
var app = new Vue({
el: "#app",
methods: {
firstVueMethod: function () {
alert("学习vue写的第一个方法");
}
}
});
}
// html 代码
<div id="app">
<input type="button" v-on:click="firstVueMethod" value="点我即可调用方法"/>
</div>
侦听属性
// 这是javascript代码
window.onload = function(){
var app = new Vue({
el: "#app",
data: {
msg: "我是信息"
},
watch: {
msg: function (newMsg) {
alert("msg发生了改变,新的值为:" + newMsg);
}
}
});
}
// 这是html代码
<div id="app">
这里是用双向绑定,绑定了msg属性:<input type="text" v-model:value="msg">
</div>
Vue对象生命周期(钩子函数)
// javascript代码
window.onload = function(){
var app = new Vue({
el: "#app",
data: {
msg: "我是信息"
},
beforeCreate: function () {
console.log("Vue对象创建之前:" + this.msg);
},
created: function () {
console.log("Vue对象创建之后:" + this.msg);
},
// 这里可以通过浏览器的调试工具验证
beforeMount: function () {
console.log("将data数据中的值挂载到页面之前" + this.msg);
},
mounted: function () {
console.log("将data数据中的值挂载到页面之后" + this.msg);
},
beforeUpdate:function () {
console.log("数据被更新之前" + this.msg);
},
updated:function () {
console.log("数据被更新之后" + this.msg);
}
});
}
-- html代码
<div id="app">
看看值的变化:<h2>{{msg}}</h2>
<input type="text" v-model="msg">
</div>