目录
什么是Servlet?
Servlet是一种用于接收web网页传回和输出到web的一个Java类,根据不同的实际需要,实现不同的Servlet来对网页数据进行处理。
Servlet的基本处理流程
在网页发起请求之后,编译器首先去寻找web网页的配置文件,在配置文件中指定该请求网页对应的Servlet类,以此建立连接,随后进行相应地数据操作
如何配置Servlet和web网页相互对应?
- 首先创建相应的Servlet类,继承servlet.jar中的HttpServlet,并在其中实现doPost或者doGet方法
- 在web项目中的WEB-INF文件夹下找到web.xml文件,在其中添加
<servlet> <servlet-name>AddServlet(类名)</servlet-name> <servlet-class>Servlet类的相对路径</servlet-class> </servlet> <servlet-mapping> <servlet-name>AddServlet(和上述的类名保持一致)</servlet-name> <url-pattern>/add(发送请求的action)</url-pattern> </servlet-mapping>
这里我是拿来举例子,实际上的类名根据需要填写,但是格式差不多。一个servlet可以对应多个servlet-mapping -
执行的顺序是:首先查看url-pattern,定位到用户发送的请求action相同的url-pattern,随后找到和servlet-mapping中的servlet-name相同的servlet中的servlet-name,再根据servlet-class路径找到对应的类来处理数据,最后根据用户选择的method方法选择调用doPost还是doGet方法
现在都是使用注解的方式配置Servlet了,不用上述的方法啦
在这里不妨我们再来看一看一个超链接每个部分访问的范围
以http://localhost:8080/web-info/demo1 为例
- http://localhost:8080--访问的是web服务器
- web-info--访问的是web项目
- demo1--访问的是Servlet对象
事实上Servlet对象是由服务器创建的,而这个Servlet就是用来处理响应和请求的规范接口
我们知道tomcat用来解析浏览器传过来的数据,那么doPost等方法里的req和resp对象也就应该是tomcat来创建并且封装数据到里面来给我们或者浏览器使用。
如何解决Servlet中Post方法的中文乱码问题?
Servlet类中有两类参数,一类是request,用于包含用户传过来的所有信息,一类是response ,用于响应web网页,传回数据给web。
处理数据都是用request,在request中有设置post下的编码方法:request.setCharacterEncoding("UTF-8");
注意:这个方法必须放在获取数据之前,最好放在doPost方法内的第一行
通过id获取数据:request.getParament("id");
Servlet的继承关系
HttpServlet->GenericServlet->Servlet
Servlet核心方法:
- init()---初始化
- service()---服务方法--重点
- destroy()--销毁
在服务方法中,当有请求过来的时候,service方法会自动响应,(其实是Tomat调用),在HttpServlet中分析到底是哪种请求,get ,post,head等等。再决定调用哪种do方法,默认在HttpServlet中用405的实现风格,若是没有请求对应的do方法就会报错405
Servlet生命周期
默认情况下:
第一次接受请求的时候,当前的Servlet才会实例化并初始化,并且一个Servlet只会被实例化一次;
从第二次请求开始就是服务;
只有在容器被销毁,也就是Tomat关闭的时候会被销毁。
可以在<servlet>中使用<load-on-startup>标签设置启动的顺序,越小启动的时机越靠前,最小数字设置为0。这样就可以在请求的时候缩短第一次请求时间,只是启动容器需要花费多一点时间而已。
Servlet是单实例的,所以它是线程不安全的。最好不要在Servlet中定义成员变量的值
HTTP协议
Tomcat封装Request对象并传入后台,那么这对象里面有些什么呢?
HTTP请求数据解析
请求行
包含请求方式,访问地址url,HTTP协议版本,请求参数
请求头
包含很多客户端信息需要告诉服务器的内容,如浏览器版本,文本,我能接收的类型
请求主体
get方式,没有请求体,但有一个queryString
post方式,有请求体,form data
json格式,有请求体,request payload
响应
响应也有三部分:响应行,响应头,响应体
响应行包含三个信息:协议,响应状态码,响应状态
响应头:包含服务器信息和服务器发送给浏览器的信息
响应体:响应的具体内容
Session会话跟踪技术
解决的问题:当服务器无法判断两个请求是否是同一个客户端发过来的,还是不同客户端发过来的,这称为HTTP的无状态问题
通过
request.getSession(/true/false);(第一种和第二种参数效果相同,第三种参数若没有Session会返回null并不会创建新的Session)方法来创建一个Session或者获取一个Session,当Servlet第一次发请求的时候,由于客户端此时没有Session,服务器会给他创建一个Session,在下一次的请求时客户端会将这个Session的ID号一起发送给服务端,这样就可以让服务端识别出来这次的请求时哪个客户端发送的。
- session.getId()--获取SessionID
- session.isNew()--判断当前的session是否是新的
- session.getMaxInactiveInterval()--获取会话持续最大时间
Session的作用域:每一个session都会在服务器端有一个专门的容器来保存数据,根据SessionID来区分是哪个session的作用域。
服务器端内部转发请求和客户端重定向
- 内部转发:(一次请求过程)request.getRequestDispatcher("要转到的页面名字").forward(request,response);
- 重定向:302(两次请求过程)response.sendRedirect("...");
Mybatis
什么是Mybatis?
是一种用于简化JDBC操作的持久层框架
Mybatis基本配置
Mybatis中的核心是两个配置文件,一个是SqlSessionFactory实例,我们一般通过xml文件进行配置,在这个文件中填写连接数据库的基本信息和映射文件。
<mappers>
<!-- 加载映射文件,这个映射文件里面包含了各种sql操作,需要自己写出来之后把这个文件路径放在这里,这样
就可以在java代码中调用了-->
<mapper resource="JobsMapper.xml"/>
</mappers>
这个例子中的mappers标签中写的就是映射文件
第二个核心文件就是这个映射文件,这个映射文件中封装着各种sql语句,用于数据库查询等操作。一般而言,一个表对应一个映射文件。在映射文件中也有着mapper标签,这个标签中存放具体的sql语句,其中,namespace代表命名空间,包名类似,而这个Id代表这条语句的名称,也是sql语句的唯一标识符,这代表这个Id不可以重复。而resultType表示返回的类型,也就是你需要把从数据库拿来的数据包装成什么样子,一般而言都是建立一个java类来封装这些数据,所以这里需要填的一般都是java类的相对路径。
<mapper namespace="test">
<select id="selectALL" resultType="com.mybatis_first.Jobs">
select * from jobs;
</select>
</mapper>
如何使用?
注意,在maven项目中要导入mybatis的jar包
// 第一步加载mybatis的核心配置文件
String resource="mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 第二步,获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第三步,执行sql
List<Object> list = sqlSession.selectList("test.selectALL");
System.out.println(list);
sqlSession.close();
Mapper代理开发技术
解决的问题
在上述的mybatis开发中,不难发现代码中存在硬编码的问题,也就是用字符串写出所需操作再获取对应的结果。事实上这在后期写代码的时候是非常不方便的,并且在维护过程中也很难处理,所以出现了Mapper代理开发技术。只要使用这种技术,就可以把上述的字符调用方法的过程封装成一个接口,这个接口对应该sql对应的映射文件,也就是说我们可以通过这个接口来调用映射文件中的sql方法,解决了硬编码的问题,并且IDE的代码补全功能可以提示你有些什么方法,总之还是很方便的。
如何实现?
- 首先需要定义一个和映射文件同名的接口,并且要将接口和映射文件放在同一个目录下\
- 需要将映射文件中的命名空间改成接口名。
- 在接口中定义方法,方法名就是映射文件中的id,并且参数和返回值类型要保持一致
注意:在resources命名和java中一样的包结构时,需要使用 / 而不是 . 如com/mapper/Usemapper
<!-- 在使用了Mapper代理的方式之后,就可以通过包扫描的方式加载映射文件了,不需要一个一个写 -->
<package name="com.mapper"/>
这个包是java包下的mapper接口包,在使用了mapper代理之后,只要扫描这个包就可以同时把这些接口绑定的包一起拉进来
Mybatis核心配置
-
<environments default="development">--这里代表的是数据库,可以通过切换不同的default来切换不同的数据库,并且可以连接多个数据库
-
<typeAliases>--用于给包下的类起别名,这样在配置resultType的时候就不用把报名也一起写出来了
<typeAliases> <package name="..."> </typeAliases>
需要注意的是,在配置信息的时候,要遵循如下顺序
-
介绍一个插件:MybatisX 这是一个帮助写mybatis的插件,效率还是挺高的,可以帮助我们在写接口方法的时候对应到映射文件里的Id。
但是数据库字段名称和实体类名称不一致的时候不能自动封装数据
解决方法:
- 起别名
- 使用sql片段
- resultMap--映射
<resultMap id="JobResultMap" type="com.mybatis_first.pojo.Jobs"> <!-- id标签对应的是主键属性--> <!-- <id column="" property=""></id>--> <!-- result标签对应的是一般属性--> <id column="job_id" property="jobId"/> <result column="job_title" property="jobTitle"/> <result column="min_salary" property="minSalary"/> <result column="max_salary" property="maxSalary"/> </resultMap> <select id="selectALL" resultMap="JobResultMap"> select * from jobs; </select>
Mybatis日志
日志就像是日记本一样,记录着程序在底层执行的时候的各种操作,学会分析日志能够帮助我们更好的了解程序的运行状态是否和我们的逻辑一致。
如何实现日志?
不少应用服务器(如 Tomcat 和 WebShpere)的类路径中已经包含 Commons Logging。注意,在这种配置环境下,MyBatis 会把 Commons Logging 作为日志工具。这就意味着在诸如 WebSphere 的环境中,由于提供了 Commons Logging 的私有实现,你的 Log4J 配置将被忽略。这个时候你就会感觉很郁闷:看起来 MyBatis 将你的 Log4J 配置忽略掉了(其实是因为在这种配置环境下,MyBatis 使用了 Commons Logging 作为日志实现)。
如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志实现,你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择其它日志实现。
setting配置日志的有效取值(value的值)
可选的值有: SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了 org.apache.ibatis.logging.Log 接口,且构造方法以字符串为参数的类完全限定名。而name要填写lohImpl
注意在使用上述值的时候要保证你有拉取到这些日志的Jar包。
具体的日志信息可以看以下的博客
slf4j-log4j2日志框架具体实现步骤
- 首先在pom文件下拉取所需要的jar包,一个都不能少!
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 引入slf4j对应log4j的桥接器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>runtime</scope>
<version>2.11.0</version>
</dependency>
<!-- log4j2日志实面(日志实现框架) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>runtime</scope>
<version>2.11.0</version>
</dependency>
<!--导入log4j2日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
- 然后去mybatis-config.xml文件中添加信息,需要注意一下标签的顺序(在上面有说)
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
如下给出一份log4j2.xml配置文件 ,有需要就拿去用吧
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
(网上信息都是散的,我看了好多好多博客才解决这个日志正常显示问题,不过也是看了不少博客,才明白这个日志还挺复杂,要学的东西挺多的,一点点来吧,真心希望我这个过程能够帮助刚入门了解日志的人先正常显示日志,不会一直报错)💕