0%

1
2
3
4
5
6
7
8
9
10
11
12
//获取配置文件
InputStream in= Resources.class.getResourceAsStream(config);
//创建sqlsessionFactory对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//创建sqlsessionfactory对象,重量级对象,程序创建一个对象耗时比较长,使用资源比较多
SqlSessionFactory factory=builder.build(in);
//获取sqlsession对象,从中获取sqlsession,SqlSession session= factory.openSession(true);表示事务可以自动提交
SqlSession session= factory.openSession();
//指定执行语句的表示,sql映射文件的namespace+.+id
String sqlId="com.zss.dao.StudentDAO"+"."+"selectStudents";
//执行sql语句,同时还拥有许多的数据库接口方法,不是线程安全的,在方法内部使用,在执行语句之前,需要进行关闭,这样保证是线程安全的
List<Student> studentList=session.selectList(sqlId);

工具类

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
package utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

/**
* @author zss
*/
public class MyBaitsUtils {
//获取sqlsession,静态代码块只执行一次,所以我们重量级的类放入到静态代码块中即可
private static SqlSessionFactory sqlSessionFactory;
static {
String config= "/mybaits.xml";
InputStream in= Resources.class.getResourceAsStream(config);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(in);
}

public static SqlSession getSqlSession(){
SqlSession sqlSession=null;
if (sqlSessionFactory!=null){
sqlSession=sqlSessionFactory.openSession();
}
return sqlSession;
}
}

动态代理

条件分析

我们可以发现有了工具类,但是整个过程还是拥有许多的重复代码,而真正改变的只是,即我们的执行的sqlId与执行的方法

1
2
String sqlId="com.zss.dao.StudentDAO"+"."+"selectStudents";
List<Student> studentList=session.selectList(sqlId);

而且同时我们查看xml文件,这些值均可以在xml文件获取得到,而并非是我们手写

1
2
3
4
5
6
7
8
<mapper namespace="com.zss.dao.StudentDAO">
<select id="selectStudents" resultType="com.zss.pojo.Student">
select * from student order by id;
</select>
<insert id="insertStudent" >
insert into student values (#{id},#{name},#{email},#{age})
</insert>
</mapper>

同时我们也可以通过dao方法的返回值,判断是哪一种方法

mybaits动态代理

mubaits根据dao的方法调用,执行获取sql语句的信息,mybaits根据你dao接口,创建一个dao接口的实现类,并创建这个类的对象,完成sqlSession的调用,访问数据库

也就是说,dao的实现方法并不需要我们进行完成了,而是mybaits进行完成

1
2
3
4
5
6
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
List<Student> studentList=dao.selectStudents();
System.out.println(studentList);
}

动态代理会帮助我们创建一个实现类,而不需要我们手动创建

插眼,动态代理懂

问题

现在需要查询员工列表,但是员工列表拥有属性Department,而对于department拥有两种命名方法,

命名一

该方法将属性直接写出,与employee没有耦合

image-20220521104459258

命名二

直接引用该对象,以Department对象的方式传输进入,这种方法虽然增加了耦合性,但是我们可以间接获得其department的所有属性高

image-20220521104752507

动态sql的内容是变化的,可以根据条件获取到不同的sql语句,主要是where部分发生变化,实现是使用mybaits提供的标签,,,

条件判断

语法:

1
2
3
<if test="判断Java对象的属性值">
sql语句
</if>
1
2
3
4
5
6
7
如果表中数据含name不为空,那么我就查询name为张三的人
<select id="selectStudent" resultType="com.zss.pojo.Student" >
select * from student where
<if test="name!=null and name!=''">
name=#{name}
</if>
</select>
1
2
3
4
5
6
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
List<Student> student=dao.selectStudent("张三");
System.out.println(student);
}

标签

where标签可以用来包含if,当多个if有一个成立时,会自动增加一个where关键字,并且会去除and,or等语句,防止报错。如果多个都不符合,where语句也会自动删除

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectStudent" resultType="com.zss.pojo.Student" >
select * from student
<where>
<if test="name!=null and name!=''">
name=#{name}
</if>
<if test="age>0">
and age=#{age}
</if>
</where>

</select>

循环标签

主要使用在sql的in语句的

1
select * from where id in100110021003);

那么怎么传输后面的值就成为了问题

1
2
3
4
5
6
7
8
<select id="selectForeach" resultType="com.zss.pojo.Student" ><!--collection表示的是方法参数的类型,如果是数组使用array,如果是list集合使用list
open是开始,close表示结束,separator表示分隔符-->

select * from student where id in
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</select>
1
2
3
4
5
6
7
8
9
10
11
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(1003);
List<Student> studentList=dao.selectForeach(list);
for (Student student:studentList){
System.out.println(student);
}
}

image-20220502210124191

当然我们也可以传输对象,但是里面的只要

1
2
3
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{student.myid}
</foreach>

当然里面的的小括号也可以自己完成

1
2
3
4
5
6
7
<select id="selectForeach" resultType="com.zss.pojo.Student" >

select * from student where id in(
<foreach collection="list" item="myid" separator=",">
#{myid}
</foreach>)
</select>

代码片段

有一些sql语句需要我们需要重复使用,一次又一次去写又太过于麻烦

  1. 先定义sql语句,表名,字段等等
  2. 在使用
1
2
3
4
5
6
7
8
9
10
11
<!--定义sql片段-->
<sql id="studentSql">
select * from student
</sql>
<select id="selectForeach" resultType="com.zss.pojo.Student" >
<!--替换掉原来的语句-->
<include refid="studentSql"></include> where id in(
<foreach collection="list" item="myid" separator=",">
#{myid}
</foreach>)
</select>

数据库属性配置文件

将数据库的信息保存在一个单独的文件中,和mybaits主配置文件分开,便于修改,保存。

  1. 在resources目录中定义一个属性配置文件,xx.properties,
    1. 在配置文件中,定义数据格式为key=value
    2. key一般使用.做多级目录的,例如jdbc.mysql.cj.
  2. 在mybaits主配置文件中使用 指定位置

记录:

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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--从类路径的根考试寻找-->
<properties resource="jdbc.properties"/>

<!--输出日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

<!--环境配置,default必须与一个environment的id相似,表示链接哪一个库-->
<environments default="development">
<!--id表示唯一值-->
<environment id="development">
<!--表示事务类型,type="JDBC"表示使用jdbc中的connection的commit,rollback做事务处理-->
<transactionManager type="JDBC"/>
<!--表示数据源-->
<dataSource type="POOLED">
<!--下面的值name是固定的,无法进行更改-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--指出映射文件的位置-->
<mapper resource="com/zss/dao/Student.xml"/>
</mappers>
</configuration>

指定多个mapper配置文件

如果有多个需要写很多行

1
2
3
4
<mappers>
<!--指出映射文件的位置-->
<mapper resource="com/zss/dao/Student.xml"/>
</mappers>

但是我们还是可以扫描包,扫描包的时候需要注意:

mapper文件名称需要和接口名称一样,区分大小写,mapper文件和dao接口在同一目录下

1
2
3
<mappers>
<package name="com.zss.dao"/>
</mappers>

PageHelper

数据分页

导入jar包

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>

配置插件

1
2
3
4
5
6
7
8
9
<!--输出日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>


<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

java文件配置

1
2
3
4
5
6
7
8
9
10
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
//加入pageHelper的方法,分页
//pageNum第几页,从1开始
//pageSize:一页中含有多少数据
PageHelper.startPage(1,2);
List<Student> studentList=dao.selectStudents();
System.out.println(studentList);
}

而我们最终也可以得到最终的行数等等信息image-20220502215118145

ResultType

ResultType结果类型,sql语句执行完毕之后,转换的Java对象,但是Java的类型是任意的,只要能够对应结果的参数即可,比如我们对表查询一个student对象,既有姓名,email和电话等等,而我们使用另外一个people类进行接受,但是people仅仅有姓名和电话,这样仍旧可以接受,只要参数名称类型对应即可

mybaits执行sql语句,mybaits调用类的无参构造函数,创建对象,mybaits把resultset指定列值付给同名的属性

我们返回的people名称,但是执行的是查询student表格,而student表格还是拥有好多的其他的people没有拥有的属性,但是还是可以进行转换

1
2
3
<select id="selectPeople" resultType="com.zss.pojo.People">
select * from student ;
</select>

返回简单类型

比如我们查询列数,则可以使用int来进行接受

1
2
3
<select id="selectCount" resultType="int" >
select count(*) from student ;
</select>
1
2
3
4
5
6
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
int people=dao.selectCount();
System.out.println(people);
}

自定义别名

我们在resultType=”com.zss.pojo.People”中有时候会很长,而且在多个之中可能都要同时用到,那么我们可以在主配置文件中自定义别名

1
2
3
4
5
<!--自定义别名-->
<typeAliases>
<!--type表示全限定名称,而alias表示自定义的别名-->
<typeAlias type="com.zss.pojo.Student" alias="stu"/>
</typeAliases>

但是上面的这种方式我们使用多了就容易造成混淆,所以我们可以采用将包名提前注释的方式,这样我们就可以在ResultType中使用类名

1
2
<!--提前说明包名-->
<package name="com.zss.pojo"/>
1
2
3
<select id="selectStudent"  resultType="Student">
select * from student where id=#{arg0} and name=#{arg1}
</select>

查询返回Map

但是不建议使用map,map由于其中的key能重复,那么当我们使用map的时候,最多仅仅查询一行数据

resultMap

结果映射,指定列明和Java对象属性的对应关系

  1. 自定义列值赋值给哪一个属性
  2. 当你的列名和属性名不一样时
1
2
3
4
5
6
7
8
9
10
11
12
<!--定义resultmap
id自定义名称,表示你定义的这个resultMap
type表示Java的全限定名称-->
<resultMap id="studentMap" type="com.zss.pojo.Student">
<!--如是主键,则使用id进行命名-->
<id column="id" property="id"/>
<!--非主键使用result,将得到的name赋值给Java对象的myname-->
<result column="name" property="myname"/>
</resultMap>
<select id="selectStudent" resultMap="studentMap">
select * from student
</select>

当然我们也可以在sql查询的时候,使用as方法直接将将值给予转换

模糊查询

方式1

1
2
3
4
5
6
public static void main( String[] args ) throws IOException {
SqlSession sqlSession= MyBaitsUtils.getSqlSession();
StudentDAO dao=sqlSession.getMapper(StudentDAO.class);
List<Student> student=dao.selectLikeStudent("%张%");
System.out.println(student);
}
1
2
3
<select id="selectLikeStudent" resultType="com.zss.pojo.Student" >
select * from student where name like #{name};
</select>

方式2

在mybaits的执行语句中进行拼接

1
select * from student where name like % #{name} %;

三层架构

界面层(controller—springmvc):和用户打交道,接受用户的请求参数,显示处理的结果

业务逻辑层(service—spring):接受页面的数据,计算逻辑,调用数据库,获取数据

访问数据层(dao—mybaits):访问数据库,实现增删改查

mybaits:

sql mapper:sql映射

​ 可以把数据库中一行数据,映射为一个Java对象,一行数据可以看作一个Java对象,操作这个对象,就相当于操作表中的数据。

Data Access Obiects: 数据访问,对数据库实现增删改查

功能:

  1. ​ 提供了创建connection,statement等能力
  2. 提供了执行sql语句的能力
  3. 提供的sql循环,把sql结果转换为Java对象,list集合的能力
  4. 提供了关闭资源的能力

入门案例

数据库建立表

导入jar包

书写student类

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
package com.zss.pojo;

/**
* @author zss
*/
public class Student {
Integer id;
String name;
String email;
Integer age;

public Student() {
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

添加xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<!--指定约束文件,mybatis-3-mapper.dtd是约束文件的名称,扩展是dtd文件-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--是当前文件的跟标签,namespace叫做空间命名,可以是自定义的,要求使用dao接口的全名称-->

<mapper namespace="com.zss.dao.StudentDAO">
<!--表示执行查询语句,id表示执行sql语句的唯一标识,resultType=""表示该语句执行成功之后转变的Java对象的类-->
<select id="selectStudents" resultType="com.zss.pojo.Student">
select * from student order by id;
</select>
</mapper>

写入数据库链接

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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--环境配置,default必须与一个environment的id相似,表示链接哪一个库-->
<environments default="development">
<!--id表示唯一值-->
<environment id="development">
<!--表示事务类型,type="JDBC"表示使用jdbc中的connection的commit,rollback做事务处理-->
<transactionManager type="JDBC"/>
<!--表示数据源-->
<dataSource type="POOLED">
<!--下面的值name是固定的,无法进行更改-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/school"/>
<property name="username" value="root"/>
<property name="password" value="200101"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--指出映射文件的位置-->
<mapper resource="com/zss/dao/Student.xml"/>
</mappers>
</configuration>

dao类添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.zss.dao;

import com.zss.pojo.Student;

import java.util.List;

/**
* @author zss
*/
public interface StudentDAO {

//查询表的所有数据
public List<Student> selectStudents();


}

实现类

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
package com.zss;

import com.zss.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws IOException {
//访问mybaits的数据文件
//定义mybaits的主配置文件的名称,从类路径的根开始
String config= "/mybaits.xml";
//读取这个文件
InputStream in= Resources.class.getResourceAsStream(config);
//创建sqlsessionFactory对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//创建sqlsessionfactory对象
SqlSessionFactory factory=builder.build(in);
//获取sqlsession对象,从中获取sqlsession
SqlSession session= factory.openSession();
//指定执行语句的表示,sql映射文件的namespace+.+id
String sqlId="com.zss.dao.StudentDAO"+"."+"selectStudents";
//执行sql语句
List<Student> studentList=session.selectList(sqlId);
System.out.println(studentList);
//关闭对象
session.close();

}
}

插入操作实例

1
2
3
4
5
6
7
8
//指定执行语句的表示,sql映射文件的namespace+.+id
String sqlId="com.zss.dao.StudentDAO"+"."+"insertStudent";
//执行sql语句
// List<Student> studentList=session.selectList(sqlId);
int flag=session.insert(sqlId,new Student(1003,"haha","zss@sina.com",25));
System.out.println(flag);
//注意mybaits默认不会自动提交事务
session.commit();

image-20220502093648550

概念

Mybatis的逆向工程(原理+详细操作)_曾吹雨的博客-CSDN博客_mybatis逆向工程原理

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。
  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
    Java实体类
    Mapper接口
    Mapper配置文件

步骤

导入jar包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.1</version>
</dependency>

配置文件

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/school"
userId="root"
password="200101" />

<!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时 把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>

<!-- targetProject:生成POJO类的位置 -->
<javaModelGenerator
targetPackage="com.zss.pojo" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>

<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>

<!-- targetProject:mapper接口生成的的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.zss.dao" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>

<!-- 指定数据表 -->
<table tableName="tb_emp" domainObjectName="Employee"/>
<table tableName="tb_dept" domainObjectName="Department"/>

</context>

</generatorConfiguration>

运行程序

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
package com.zss.Test;

import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 指定配置文件
File configFile = new File("src/mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}

// 执行main方法以生成代码
public static void main(String[] args) {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}

image-20220510105547736

Javaweb基础知识

CS与BS

**CS:**客户端服务器架构模式

优点:充分利用客户机的资源,减轻服务器的压力(一部分安全要求不高的计算任务放在客户端执行,不需要把所有的计算和存储都在服务器端执行,从而能够减轻服务器的压力,也能够减轻网络的负荷)

缺点:需要安装,升级维护成本高

BS:浏览器服务器架构模式

优点:客户端不需要安装,维护成本低

缺点:服务器端符合比较重,在服务器计算完成之后把所有的结果在传输给客户端,因此服务端与浏览器端进行非常频繁的数据通信,网络负荷比较重。

Tomcat

image-20220331204115250

bin为可执行文件目录

conf为配置文件目录

lib为存放依赖的目录

logs为日志文件目录

webapps项目部署目录

work工作目录

temp临时目录

新建web项目并且部署

在webapp下新建test文件夹,然后在里面再次新建WEB-INF文件,即可形成一个简单的项目,通过相应的路径即可访问

image-20220331204129288

image-20220331204143857

web项目的目录

image-20220331204155385

Servlet入门

servlet(服务端应用程序)

image-20220331204206035

我们在编写addServlet组件

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
package com.zss.servlets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author zss
*/
public class AddServlet extends HttpServlet {
/**此方法会响应post方法
* req表示请求,而会将请求封装成一个req对象
*
* */
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fName=req.getParameter("fName");
Integer price=Integer.parseInt(req.getParameter("price"));
String fCount=req.getParameter("fCount");
String remark=req.getParameter("remark");
System.out.println(fName+price+fCount+remark);

}
}

那么我们的add又是如何知道add对应于addServlet??

在web.xml中进行配置

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>com.zss.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>

1.用户发送请求,action=add

2.项目中web.xml,找到url-pattern=/add,然后再次找到servlet-name中AddServlet,在Servlet-name中找到对应的,并且找到对应的文件

3.用户发送的post请求,因此tomcat会执行dopost方法

image-20220331204222527

我们可以发现输出台可以进行正常的接受与输出

请求的地址与方法等状态码等

image-20220331204230629

请求的数据

image-20220331204241866

通过Servlet实现数据与前端的交互

通过前端向数据库中添加水果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加水果</title>
</head>
<body>
<form action="add" method="post">
名称<input type="text" name="fName"><br>
单价<input type="text" name="price"><br>
库存<input type="text" name="fCount"><br>
备注<input type="text" name="remark"><br>
序号<input type="text" name="fid"><br>
<input type="submit" value="添加水果">
</form>
</body>
</html>

AddServlet

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
package com.zss.servlets;


import com.zss.impl.FruitDAOImpl;
import com.zss.object.Fruit;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
* @author zss
*/
public class AddServlet extends HttpServlet {
/**此方法会响应post方法
* req表示请求,而会将请求封装成一个req对象
*
* */
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//注意要设置编码格式,非常重要,post方式要进行设置
req.setCharacterEncoding("UTF-8");
String fName=req.getParameter("fName");
int price=Integer.parseInt(req.getParameter("price"));
int fCount=Integer.parseInt(req.getParameter("fCount"));
String remark=req.getParameter("remark");
Integer fid=Integer.parseInt(req.getParameter("fid"));


FruitDAOImpl fruitDAO=new FruitDAOImpl();
try {
fruitDAO.addFruit(new Fruit(fName,price,fCount,remark,fid));
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
}

}
}

通过web.xml文件添加两个方式之间的映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>com.zss.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>
</web-app>

水果对象

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
package com.zss.object;

/**
* @author zss
*/
public class Fruit {
private String fName;
private int price;
private int fCount;
private String remark;
private Integer fid;

public Fruit(String fName, int price, int fCount, String remark, Integer fid) {
this.fName = fName;
this.price = price;
this.fCount = fCount;
this.remark = remark;
this.fid=fid;
}

public String getfName() {
return fName;
}

public void setfName(String fName) {
this.fName = fName;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

public int getfCount() {
return fCount;
}

public void setfCount(int fCount) {
this.fCount = fCount;
}

public String getRemark() {
return remark;
}

public void setRemark(String remark) {
this.remark = remark;
}

public Integer getFid() {
return fid;
}

public void setFid(Integer fid) {
this.fid = fid;
}
}

用于存储增删改查的方法区

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
package com.zss.dao;

import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
* @author zss
*/
public class BaseDAO {
private final String URL="jdbc:mysql://localhost:3306/fruits";
private final String USER="root";
private final String PASSWORD="200101";
private final String DRIVER="com.mysql.cj.jdbc.Driver";

protected Connection connection;
protected PreparedStatement preparedStatement;
protected ResultSet resultSet;

/**得到链接*/
protected Connection getConnection(){
try{
Class.forName("com.mysql.cj.jdbc.Driver");
connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/fruits?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai","root","200101");
return connection;
}catch (ClassNotFoundException |SQLException e){
e.printStackTrace();
System.out.println("链接出现问题");
return null;
}
}

/**关闭链接*/
protected void closeConnection(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){

if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

/**增加水果的方法*/
public int addFruitCount(String sql,Object ...placeHolder) throws ClassNotFoundException, SQLException {
int count;
connection=getConnection();
preparedStatement= connection.prepareStatement(sql);

for (int i=0;i<placeHolder.length;i++){
preparedStatement.setObject(i+1,placeHolder[i]);
}
count=preparedStatement.executeUpdate();
closeConnection(connection,preparedStatement,resultSet);
return count;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zss;

import com.zss.object.Fruit;

import java.sql.SQLException;

/**
* @author zss
*/
public interface FruitDAO {

/**增加水果的方法*/
public boolean addFruit(Fruit fruit) throws SQLException, ClassNotFoundException;

}

上面是用接口类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.zss.impl;

import com.zss.FruitDAO;
import com.zss.dao.BaseDAO;
import com.zss.object.Fruit;

import java.sql.SQLException;

/**
* @author zss
*/
public class FruitDAOImpl extends BaseDAO implements FruitDAO {

@Override
public boolean addFruit(Fruit fruit) throws SQLException, ClassNotFoundException {
String sql="insert into t_fruit values (?,?,?,?,?)";
int count=addFruitCount(sql,fruit.getfName(),fruit.getPrice(),fruit.getfCount(),fruit.getRemark(),fruit.getFid());
System.out.println(count);
return count>0;
}
}

最终实现的类与方法

引入

前面的小小水果摊 虽然完成了全部功能,但是整体结构比较复杂,一个页面对应的请求需要多个Servlet进行响应,代码量重复复杂。

image-20220406195831872

优化1

将所有的代码集成为私有函数,页面都对同一个fruit.do进行响应,但是同时传入一个operate操作符,用于判断对页面的操作,同时利用反射,优化调用函数的代码量,将所有的反射方法调用到dispatchServlet中,同时将一个负责判断转发的操作交给该类进行,而fruitservlet变为控制器,只保存一些调用方法

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
/**对所有的.do结尾的进行响应,fruit.do,user.do等等都可以*/
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {

private Map<String ,Object> map=new HashMap<>();
//初始化方法,在xml文件中通过反射获得响应的类,利用map存储关系
@Override
public void init() {
//需要同时也初始化ViewBaseServlet的方法,否则themeleaf无法进行使用
super.init();
try {

InputStream inputStream=getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder=documentBuilderFactory.newDocumentBuilder();
//获取document对象
Document document= documentBuilder.parse(inputStream);
//获取所有bean节点
NodeList beanNodeList=document.getElementsByTagName("bean");
for (int i=0;i<beanNodeList.getLength();i++){
Node beanNode=beanNodeList.item(i);
if (beanNode.getNodeType()== Node.ELEMENT_NODE){
Element beanElement=(Element) beanNode;
String beamId=beanElement.getAttribute("id");
String className=beanElement.getAttribute("class");
Class clas=Class.forName(className);
Object beanObj=clas.newInstance();

map.put(beamId,beanObj);
}
}
} catch (ParserConfigurationException | ClassNotFoundException | InstantiationException | IllegalAccessException | SAXException | IOException e) {
e.printStackTrace();
}


}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException{
req.setCharacterEncoding("UTF-8");
//如果说url是:http://localhost:8080/fruit/fruit.do
//servletPath是: /fruit.do
String servletPath=req.getServletPath();
//而我接下来需要得到fruit/fruit.do中第二个fruit
//先找到最后一个/的索引位置
int lastslash=servletPath.lastIndexOf("/")+1;
//获得最后一个点的位置
int lastpoint=servletPath.lastIndexOf(".");
servletPath=servletPath.substring(lastslash,lastpoint);

//使fruit与fruitcontroller对应,先得到controller
Object controllerObj=map.get(servletPath);
System.out.println(servletPath);
String operate =req.getParameter("operate");
if (operate == null | Objects.equals(operate, "")) {
operate="inquire";
}
try {
//传入参数,得到对应的方法
Method method=controllerObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class);
method.setAccessible(true);
//获得参数返回的对象
Object obj=method.invoke(controllerObj,req);
String meth=(String) obj;
//判定是组件内转发,还是重新定向
if (meth.startsWith("sendRedirect")){
String sendRedStr=meth.substring("sendRedirect".length());
resp.sendRedirect(sendRedStr);
}
else {
super.processTemplate(servletPath+"/"+meth,req,resp);
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("operate非法");
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<xml version="1.0" encodeing="utf-8">
<!--因为是可扩展的标签,所以任何标签都可以自己定义-->
<beans>
<!--下面代表fruit与该controller相对应-->
<bean id="fruit" class="com.zss.myssm.myspringmvc.controller.FruitController"/>

</beans>
<!--
html是超文本标记语言
而xml是可扩展的标记语言

xml包含三个
1.xml声明,而且必须在第一啊很难过
2.DTD支持文档类型
3.XML正文
-->

</xml>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private String  inquire(HttpServletRequest req) throws IOException {
req.setCharacterEncoding("UTF-8");
int pageNum;
String pagePer=req.getParameter("pagePer");
if (pagePer == null|pagePer==""){
pagePer="1";
}
pageNum=Integer.parseInt(pagePer);
List<Fruit> list=fruitDAO.inquFruit(new Fruit(),pageNum);
int lastPageNum=((fruitDAO.getColumn()-1)/5)+1;
HttpSession session=req.getSession();
session.setAttribute("lastPage",lastPageNum);
session.setAttribute("pageNo",pageNum);
session.setAttribute("fruitList",list);
//给中央控制器传入是转发,还是重定向操作
return "FruitShow";
}

优化2

虽然我们将这些方法进行了集成,但是可以发现,我们的fruitcontroller中仍旧还是有重复的的参数需要获取,而且每一个参数都不相同,假如我们有user,还需要给他单独设置参数,那么就不能在设置转发方法的时候,同时完成参数的转发吗???
于是我们更改service方法部分代码

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
try {
Method[] methods = controllerObj.getClass().getDeclaredMethods();
for (Method method : methods) {

if (method.getName().equals(operate)) {
//获取所有的参数
Parameter[] parameters = method.getParameters();
//设计一个用于存放参数值的数组
Object []objectParameters=new Object[parameters.length];
for (int i=0;i<objectParameters.length;i++ ){
Parameter parameter=parameters[i];
if ("req".equals(parameter.getName())){
objectParameters[i]=req;
continue;
}
//获取网页端的值
String parameValue=req.getParameter(parameter.getName());
//将参数赋值
objectParameters[i]=parameValue;
}
method.setAccessible(true);
Object obj = method.invoke(controllerObj, objectParameters);
String meth = (String) obj;
if (meth.startsWith("sendRedirect")) {
String sendRedStr = meth.substring("sendRedirect".length());
resp.sendRedirect(sendRedStr);
} else {
super.processTemplate(servletPath + "/" + meth, req, resp);
}
}

}
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}

servlet拥有四个声明周期:实例化,初始化,服务,销毁

init初始化

有参数

1
2
3
4
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}

无参数

1
2
public void init() throws ServletException {
}

在servlet初始化时做一些工作,我们重写init方法,并且可以获得初始化的数据

首先获取config对象,其次再后去初始化值

1
2
3
4
5
6
7
8
9
10
11
public class Demo01Servlet extends HttpServlet {

@Override
public void init(){
//获取config对象
ServletConfig servletConfig=getServletConfig();
String initStr=servletConfig.getInitParameter("参数");
System.out.println(initStr);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet>
<servlet-name>Demo01</servlet-name>
<servlet-class>com.zss.servlets.Demo01Servlet</servlet-class>
<init-param>
<param-name>参数</param-name>
<param-value>我是初始化参数</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>Demo01</servlet-name>
<url-pattern>/Demo01</url-pattern>
</servlet-mapping>

image-20220408172332726

下面的这种方式亦可以进行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet(urlPatterns = {"/Demo01"},
initParams = {
@WebInitParam(name = "参数",value = "我是参数")
}
)
public class Demo01Servlet extends HttpServlet {

@Override
public void init(){
//获取config对象
ServletConfig servletConfig=getServletConfig();
String initStr=servletConfig.getInitParameter("参数");
System.out.println(initStr);
}

}

而同样我们也可以获取到上下文的配置

1
2
3
4
<context-param>
<param-name>参数1</param-name>
<param-value>我是参数1</param-value>
</context-param>

image-20220408173417402

1
2
3
ServletContext servletContext=getServletContext();
String text=servletContext.getInitParameter("参数1");
System.out.println(text);

业务层

MVC

MVC=Model(模型)+View(视图)+Controller(控制器)

视图层:御用做数据展示和用户交互的界面

控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件完成

模型层:包括pojo,业务模型组件,数据访问层组件

​ opjo:值对象

​ DAO:数据访问对象,是一个单精度对象,一个方法只考虑一个操作,比如添加,修改等等

​ BO:业务对象,实际业务方法是比较复杂的,因此业务方法的粒度是比较粗的

比如:注册属于业务功能,属于业务方法,而这个业务方法中包含了多个DAO方法

IOC

IoC 容器

Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans,我们将在下一章中进行讨论。

通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置和可执行的系统或应用程序。

IOC 容器具有依赖注入功能的容器,它可以创建对象,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而”控制反转”是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者

  • 耦合/依赖:层层之间存在相互的依赖,但是我们的设计规则是:高内聚,低耦合,层内部的组成应该是高度聚合的,而层层之间的关系应该是低耦合的。

    而我们为了降低耦合度,比如controller于serviceimpl之间的关系,这两层之间是相互影响的,而为了使我们下一层的改动不会影响到上一层,

    控制反转:FruitServiceImpl fruitService=new FruitServiceImpl();如果在方法中,属于方法级别(结束之后会被销毁),如果属于成员变量,则其属于servlet的作用域(整个生命周期)但是在后面在spplicationContext中.xml中,通过解析xml文件,产生实例,存放在beanMap中,而beanMap在一个BeanFactory之中,也就是说其实例的生命周期放在容器中了,控制权交给容器,这个现象为控制反转

    **依赖注入:**FruitServiceImpl fruitService=new FruitServiceImpl();存在耦合,而后面该为null,在xml文件中进行配置

1
FruitServiceImpl fruitService=new FruitServiceImpl();

这一层重新定义实例化了一个FruitServiceImpl();,而我们为了使其之间没有相互影响,我们应该

1
FruitServiceImpl fruitService=null;

但是这样会出现空指针错误,那么我们可以在xml文件进行设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--比如说通过fruit进行init初始化,同时调用了ClassPathXmlApplicationContext() 构造器
在map中存储了键值对,而检测到property,比如fruitServiceImpl,则去寻找
com.zss.myssm.BaseDAO.impl.FruitServiceImpl并且利用反射将fruitServiceImpl赋值
其余也相同
-->
<beans>
<bean id="baseDAO" class="com.zss.myssm.BaseDAO.dao.BaseDAO"/>
<bean id="fruitServiceImpl" class="com.zss.myssm.BaseDAO.impl.FruitServiceImpl">
<!--说明含有那些依赖关系,property表示属性,name表示属性名字,而ref表示引用那个的属性名-->
<property name="baseDAO" ref="baseDAO"/>
</bean>
<bean id="fruit" class="com.zss.myssm.myspringmvc.controller.FruitController">
<property name="fruitServiceImpl" ref="fruitServiceImpl"/></bean>

<bean id="fruitServiceImpl" class="com.zss.myssm.BaseDAO.impl.FruitServiceImpl"/>
</beans>
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
public class ClassPathXmlApplicationContext implements BeanFactory{
private Map<String ,Object> beanMap=new HashMap<>();

public ClassPathXmlApplicationContext() {
try {

InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//获取document对象
Document document = documentBuilder.parse(inputStream);
//获取所有bean节点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beamId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class<?> clas = Class.forName(className);
Object beanObj = clas.newInstance();
beanMap.put(beamId, beanObj);

}

}
//组装bean之间的依赖关系
/*Node节点包含:
* Element:元素节点
* Text:文本节点
* 其实下面还是拥有三个子节点,拥有两个空白的节点(或者说换行节点)
*
* */
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
NodeList beanChildNodeList=beanElement.getChildNodes();
for (int j=0;j<beanChildNodeList.getLength();j++){
Node beanChildNode=beanChildNodeList.item(j);
if (beanNode.getNodeType() == Node.ELEMENT_NODE&& "property".equals(beanChildNode.getNodeName())) {
String beamId = beanElement.getAttribute("id");
Element propertyElement=(Element) beanChildNode;
String propertyName=propertyElement.getAttribute("name");
String propertyRef=propertyElement.getAttribute("ref");
Object refObj=beanMap.get(propertyRef);
Object beanObj=beanMap.get(beamId);
//设置到对应的实例中
Class<?> beanCla=beanObj.getClass();
Field propertyFiled=beanCla.getDeclaredField(propertyName);
propertyFiled.setAccessible(true);
//通过反射设置值
propertyFiled.set(beanObj, refObj);
}
}
}
}


} catch (ParserConfigurationException | ClassNotFoundException | InstantiationException | IllegalAccessException | SAXException | IOException | NoSuchFieldException e) {
e.printStackTrace();
}

}

@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}
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
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory;

@Override
public void init() {
super.init();
beanFactory=new ClassPathXmlApplicationContext();
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
//如果说url是:http://localhost:8080/fruit/fruit.do
//servletPath是: /fruit.do
String servletPath = req.getServletPath();
//而我接下来需要得到fruit/fruit.do中第二个fruit
//先找到最后一个/的索引位置
int lastslash = servletPath.lastIndexOf("/") + 1;
//获得最后一个点的位置
int lastpoint = servletPath.lastIndexOf(".");
servletPath = servletPath.substring(lastslash, lastpoint);

//使fruit与fruitcontroller对应,先得到controller
Object controllerObj = beanFactory.getBean(servletPath);
String operate = req.getParameter("operate");
if (operate == null | Objects.equals(operate, "")) {
operate = "inquire";
}
try {
Method[] methods = controllerObj.getClass().getDeclaredMethods();
for (Method method : methods) {

if (method.getName().equals(operate)) {
//获取所有的参数
Parameter[] parameters = method.getParameters();
//设计一个用于存放参数值的数组
Object []objectParameters=new Object[parameters.length];
for (int i=0;i<objectParameters.length;i++ ){
Parameter parameter=parameters[i];
if ("req".equals(parameter.getName())){
objectParameters[i]=req;
continue;
}
String parameValue=req.getParameter(parameter.getName());
objectParameters[i]=parameValue;
}
method.setAccessible(true);
Object obj = method.invoke(controllerObj, objectParameters);
String meth = (String) obj;
if (meth.startsWith("sendRedirect")) {
String sendRedStr = meth.substring("sendRedirect".length());
resp.sendRedirect(sendRedStr);
} else {
super.processTemplate(servletPath + "/" + meth, req, resp);
}
}

}
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

Servlet的继承关系

image-20220331223030179

可以看出Servlet下面有GenericServlet,而HttpServlet继承了GenericServlet

Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package javax.servlet;

import java.io.IOException;

public interface Servlet {
void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();
}

service发起请求该方法会被自动调用,而在器Httpservlet中service方法中方法不是抽象的,而在 service方法中,会首先获取表单请求的方法,进行判断调用相关的方法

image-20220402102213041

doget与dopost方法

1
2
3
4
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}

而在调用的sendMethodNotAllowed的方法,会给出响应的操作

image-20220402102433764

而对于405方法,httpsServlet中这些do方法都是405的实现风格,否则会报405错误,因此在新建servlet时,我们会区重写do方法。

Servlet的生命周期

首先我们的Servlet方法是Tomact容器去调用的,而servlet的生命周期init,service,destory正是者三个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.zss.servlets;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;

/**
* @author zss
*/
public class TestServlet extends HttpServlet {
@Override
public void init(){
System.out.println("正在初始化");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse response){
System.out.println("正在服务中");
}
@Override
public void destroy(){
System.out.println("销毁");
}
}

image-20220402104250832

当我们启动tomcat时,并且发送请求,容器自动初始化,并且开启服务,而当我们刷新了页面,相当于我们重新提交了一次请求,会再一次调用service方法,当我们销毁的时候,也就是tomcat关闭的时候,会调用destroy方法。

而其实在底层时tomcat服务器通过反射给与我们进行实例化,所有的请求只会通过一个实例进行操作。

这样的好处是,我们的系统初始化比较快,但是第一次请求比较慢,如果需要提高servlet的效应速度,则应该初始化响应的时机

Servlet的初始化时机

  1. 默认第一次进行初始化
  2. 通过servletload进行初始化,值最小,初始化越前,tomcat会自动进行服务,最小值为0
1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.zss.servlets.TestServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>

Servlet在容器中是单例的,线程不安全的,因为所有的实例都是通过一个实例进行请求,所以尽量不要在servlet中设置成员变量,也不要通过servlet的值进行一些逻辑判断