servlet是一个java接口,用于处理http请求,生成响应

Servlet 的意义 —— 把“请求处理”做成可管理、可复用的 Java 对象,对一个路径的请求对应一个servlet类

它在 javax.servlet 包里定义,大概长这样:

public interface Servlet {
    void init(ServletConfig config) throws ServletException;
    void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
    void destroy();
    ServletConfig getServletConfig();
    String getServletInfo();
}

service() 是 Servlet 接口里最核心的方法。它的作用是:每次收到一个请求时执行,用来处理请求和返回响应。,Tomcat自动会调用它

doGet() 和 doPost()是在httpservlet里用来处理两种http请求的方法

servlet里:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    resp.getWriter().write("这是GET请求");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
    resp.getWriter().write("这是POST请求");
}

在最早期(没有 Spring 这些框架的时候),开发 Web 应用时,一个 Servlet 既充当了控制器(接收请求),又写了业务逻辑

rep(请求),resp(响应)

httpservletRequest(req)

由浏览器发起的 HTTP 请求报文(包括 URL、参数、请求头、cookie、body 等)

Tomcat 收到浏览器的 HTTP 报文后,会把这些信息解析好,封装成一个 Java 对象 HttpServletRequest,然后交给 Servlet 用。

常用方法:

getparameter("name") 取参数的值

getheader("user-agent") 取请求头,比如浏览器类型ua头

setattribute("user",obj) 往request里存对象

getattribute("user") 取服务器端放的数据

httpservletresponse (resp)

代表“响应对象”,用来构造发回浏览器的 HTTP 响应报文

常用方法:

setcontenttype("text/html;charset=utf-8" ) 告诉浏览器编码类型

getwriter() 获取一个写出工具

getoutputstream() 获取字节流工具(适合图片,文件下载)

getwriter().write("hello") 把字符串写进响应体

tomcat

servlet必须运行在tomcat这种java web容器上,可以把servlet想象成服务员,tomcat就是老板,老板提供服务员工作的地方(餐厅),也指导服务员的工作

它与普通服务器(如apache)的区别在于,apache的主要职责是返回静态资源,但也可以通过扩展模块(mod_php)运行动态语言,我们电脑上下载的apache服务器能跑php就是因为默认已经下好了mod_php,但本质上它更擅长静态文件的转发 ,实际环境中,经常是使用apache把动态请求转发给tomcat处理

在webapps/下上传war包

webapps/
├── ROOT.war        (公司官网)
├── shop.war        (电商系统)  
├── admin.war       (管理系统)
└── blog.war        (博客系统)

tomcat会自动解压为:

webapps/
├── ROOT/           ← http://yoursite.com/
├── shop/           ← http://yoursite.com/shop/
├── admin/          ← http://yoursite.com/admin/  
└── blog/           ← http://yoursite.com/blog/
war

war包就是把 Java Web 应用及其运行所需内容打包成一个文件(包括静态文件,动态文件),便于部署到容器(如 Tomcat)。

jsp

JSP 是一种文件格式(html+java),jsp文件的后缀是.jsp,基于java生态系统

一个 .jsp 文件通常包含三种内容:

  1. 静态内容 → HTML、CSS、JS,这些会原封不动输出到浏览器。
  2. Java 表达式,语句,具体来说,有以下三种:
  • <% ... %> 里放 Java 语句,即一个java片段,里面的内容会被tomcat直接翻译成servlet里的java语句
  • <%= ... %> 里放 Java 表达式,该表达式执行后的结果直接输出到页面
  • <%! ... %> 里放类的声明。 翻译时会放到 Servlet 类的成员变量里。
  • <%@ %>导入包、设置页面属性
  1. JSP 标签 → 例如 <jsp:include .../>,他们是标准化的功能块,比脚本更稳定
  2. EL 表达式 ${user.name},它是一门小语言,专门给 JSP 用的,可以写到sturts标签内,可以看做ognl的简化版,可以访问作用域对象里的数据(request, session, application),访问请求参数(param, header, cookie 等),做简单的运算、逻辑判断(${a + b}, ${a > b ? 'yes' : 'no'})等

常用EL表达式:

${user.name} - 获取user对象的name属性

${param.username} - 获取请求参数username的值

${sessionScope.user} - 获取session中的user对象

${list[0]} - 获取list集合的第一个元素

tomcat在处理 JSP 时,不是直接原样输出,而是把它们转换成servlet类,再执行。

比如写一个hello.jsp:

<html>
<body>
Hello, <%= request.getParameter("name") %>!
</body>
</html>

Tomcat 等容器会把它翻译成一个 Servlet,大概长这样 , hello_jsp.java

//定义一个hello_jsp类
public class hello_jsp extends HttpServlet {
    public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException {
         // 获取输出流
        JspWriter out = response.getWriter();
        // 把 HTML 输出成字符串
        out.write("<html>\n<body>\nHello, ");
        out.write(request.getParameter("name"));  // <%= ... %> 被翻译成 Java 输出
        out.write("!\n</body>\n</html>");
    }
}

HTML 部分 → 被翻译成 out.write("...")。

JSP 表达式 <%= ... %> → 被翻译成 Java 代码并执行,结果写进输出流

所以我们才说,jsp中的:
HTML 是静态部分,直接输出。
JSP 标签/表达式是动态部分,要转成 Java 代码后运行。

浏览器请求http://localhost:8080/hello.jsp?name=Tom时, Tomcat 运行 hello_jsp._jspService(),拼接出完整的 HTML, JSP 本质就是生成 HTML 的模板。

<html>
  <body>
     Hello, Tom!
  </body>
</html>

_jspService() 是一个servlet类的方法,在普通的 Servlet 里,处理请求的方法是 doGet() 或 doPost()。但 JSP 是一种“特殊的 Servlet”,它没有自己写的 doGet/doPost,Tomcat 会帮你生成一个固定的方法:public void _jspService(HttpServletRequest req, HttpServletResponse resp),这个 _jspService 方法就是 JSP 页面的核心逻辑,用来生成 HTML。每次浏览器请求这个 JSP 时,Tomcat 就调用这个方法。

  • 普通 Servlet:容器调用 service() → 里面再分发到 doGet/doPost。
  • JSP Servlet:容器调用 service() → 里面直接执行 _jspService()。
mvc模式

MVC模式是人们为了解决Web应用开发混乱问题而设计出的架构模式 ,把一个web应用分成三部分: Model(bean),负责处理业务逻辑,View(jsp)负责从model中获取数据并生成用户看到的html页面,Controller(servlet)负责接收请求,并调用对应的model

java里实现mvc模式用的往往都是spring生态,因为生态成熟,整合方便,spring生态中,对于web层,使用spring mvc框架实现,对于业务层,主要是写业务逻辑,这里不需要一个专门的“业务框架”,而是需要一个能管理对象、解耦调用的容器。这就是 Spring 核心容器(ApplicationContext) 发挥作用的地方,你写一个 @Service 类,Spring 自动帮你实例化、管理生命周期。你写一个 @Autowired,Spring 自动帮你把需要的依赖对象注入进来。所以 业务层的框架就是 Spring 自身(IoC + AOP)。对于持久层,负责数据库操作,常见框架:Hibernate(ORM),在 Spring 生态里,有 Spring Data 来统一整合持久层框架:

Spring Data JPA → 封装 Hibernate

Spring Data MongoDB → 操作 MongoDB

Spring Data Redis → 操作 Redis

所以持久层既有 Hibernate/MyBatis 这些“外部框架”,也有 Spring 提供的统一封装(Spring Data)。

Spring 生态不是单独在哪一层,而是贯穿三层,

  • Spring MVC 管 Web 层
  • Spring IoC/AOP 管业务层
  • Spring Data 管持久层
  • 再加上 Spring Boot 把它们打包成一个完整的项目启动器
    → 这就是 Spring 生态。
Spring Framework 
    ├── spring-core       (核心功能)
    ├── spring-web        (Web基础)
    ├── spring-webmvc     (MVC框架) ← 你熟悉的
    ├── spring-webflux    (响应式Web)
    ├── spring-data       (数据访问)
    └── ...其他模块
1. 表示层(web层):

Struts2 和 Spring MVC 都是表示层的框架。Struts2 是独立的 Web 框架,不依赖 Spring(但可以整合)。Spring MVC 是 Spring 家族的一部分,本身和 Spring Core 天然契合,所以后来大家更多用 Spring MVC 替代 Struts2,web层不会做一些复杂的逻辑,即设计业务逻辑的部分,他只是取出浏览器传来的数据,再交给业务层实现业务逻辑

有三个重要概念;

前端控制器,他是框架的总入口,在struts2里叫FilterDispatcher,spring mvc里叫DispatcherServlet, DispatcherServlet是一个servlet的实现类(经过了层层继承) ,最初我们要写很多的servlet,接受不同的请求在选择调用get方法还是post方法,但是现在在 Spring MVC 中,只有一个servlet,即dispatcherservlet,所有请求先到 DispatcherServlet,它只负责找到对应的Controller类,执行Controller类,而controller类会调用业务层,再把结果交给 View 解析器,去渲染页面

拦截器,在spring mvc和struts2里都叫intercepter,它运行在控制器前后,常见用途有登录校验(未登录就跳转到登录页),权限检查,记录日志,性能监控(统计接口耗时)等

控制器controller,他是MVC里的C, 负责接收用户请求,然后决定交给哪个 Service 来处理。实际上是一个类,不同url对应不同的controller,也就执行不同的逻辑功能

在不同框架里的表现:

Servlet 时代:你自己写 LoginServlet,就是控制器。

Struts2:框架要求你写一个 Action 类,比如 LoginAction,Struts2 框架会帮你把请求转发到这个 Action 类里。

Spring MVC:着重讲下,

spring mvc里的controller类是靠 @Controller 注解的,它就标明了,这不是一个普通的类,而是一个controller类,这样DispatcherServlet就可以找到了

@Controller
public class UserController {
    @GetMapping("/hello")
    public String hello() {
        return "hello.jsp";
    }
}

但是光有控制器还不够,还得告诉 Spring:请求进来时,要走哪个方法。

这就是 @RequestMapping、@GetMapping、@PostMapping 的作用,他们都是用来标记方法的,当然这些方法都是引进的service的

@RequestMapping —— 通用映射

最早的版本里,Spring 只有这个注解。它可以:

标在类上:给整个类的请求路径加个“前缀”。

标在方法上:给具体方法绑定请求路径。

默认可以处理 GET、POST、PUT、DELETE… 任何请求。

@Controller
@RequestMapping("/user")  // 类级别前缀
public class UserController {

    @RequestMapping("/list")  // 方法级别路径
    public String listUsers() {
        return "userList";   // 访问 /user/list 时,这个方法会被调用,返回视图名
    }
}

@GetMapping —— 标记专门处理 GET 请求的方法

@GetMapping("/list")
public String listUsers() {
    return "userList";
}

@PostMapping —— 标记专门处理 POST 请求的方法

@PostMapping("/save")
public String saveUser(User user) {
    return "success";
}

Spring 容器启动时,容器会找到所有 @Controller 的类(包括所有被标记的类)

自动实例化这些类(new 出对象),并存到 IoC 容器里,Spring 会把这些对象注册成Bean,类似于 Map<String, Object> 这样的存储结构。Bean 的 key 可以是类名(比如 "userController"),value 就是那个对象。DispatcherServlet 调用时,直接从容器里取对象,请求到达 DispatcherServlet,它会解析 URL → 找到对应的 Controller 对象 → 用反射调用其中的方法。

所以,在 Spring 里,不需要写 new UserController(),spring 启动时就帮你 new 好了。

Bean

Bean是一个广泛的概念,只要是被 Spring 容器管理的对象都叫Bean,我们写的普通类,只要交给了 Spring 容器(通过注解 @Component / @Service / @Controller / @Repository,或者 XML 配置),Spring 就会创建它的对象并托管生命周期。

mvc中的其他注解

@Service 注解

用来标记业务层的类。

加了这个注解以后,Spring 会把这个类交给容器管理,生成一个 Bean。

@Service
public class UserService {
    public void addUser() {
        System.out.println("User added.");
    }
}

@Repository注解

用来标记数据访问层(DAO层)的类,告诉spring,这个类是负责访问数据的

@Autowired 注解

用来自动注入依赖。

当一个类需要另一个 Bean 时,Spring 会自动帮你把那个 Bean 放进来。

@Controller
public class UserController {

    @Autowired   // 自动把 UserService 注入进来
    private UserService userService;

    public String add() {
        userService.addUser(); // 不需要手动 new
        return "success";
    }
}

@RestController注解

用于前后端分离,或者 AJAX / Fetch 请求

@Controller返回的是 视图(页面),比如 return "login.jsp";,最后由前端(JSP/Thymeleaf)去渲染页面。而@RestController返回的是数据(JSON / XML),不会去找页面

@RestController = @Controller + @ResponseBody

容器(ApplicationContext)

在 Spring 里,容器就是一个大工厂

它的工作是:

扫描项目中的类(带注解的,比如 @Controller、@Service、@Repository…)。

创建对象(实例化),并管理这些对象。

维护依赖关系,谁需要谁,就自动“注入”进去。

所以,当你启动一个 Spring 应用时,第一件事就是容器启动,它会把所有需要的 Bean 都准备好。

IoC(Inversion of Control,控制反转)

IoC 是一种设计理念:对象的创建和管理由容器负责,而不是由程序自己 new。

Spring 容器就是实现 IoC 的具体工具。

当容器启动时:

扫描类(带注解的,比如 @Controller, @Service 等)

自动创建这些类的对象(实例化)

把对象存到 IoC 容器中 → 这就是“控制反转”:对象不由你主动创建,而是由容器管理。

2. 业务层:

业务层不关心请求,它只负责写业务逻辑,比如“转账”、“下订单”、“权限校验”。创建调用Service对象,即业务逻辑类的java类(比如校验账号密码), Service放在容器里(ApplicationContext),由容器统一管理,他还会创建调用DAO对象,DAO对象是专门访问数据库的类,调用DAO对象即调用持久层,因为dao对象都在持久层,dao会把返回结果给service,再传回web层

web层和业务层靠方法调用和依赖注入(DI/IoC)连接

1,方法调用

  • Web 层(Servlet/Controller)收到请求后,会调用业务层(Service)的某个方法。
  • 就像 Controller 是“前台”,它会说:

userService.login(username, password);

这里的 userService 就是业务层对象。

2.依赖注入(DI)

  • Spring 框架会帮我们“装配”好 Controller 里需要的 Service。
  • 比如:
@Controller
public class UserController {
    @Autowired
    private UserService userService;  // Spring 自动注入

    @PostMapping("/login")
    public String login(String username, String password) {
        return userService.login(username, password);
    }
}

@Autowired 告诉 Spring:帮我把业务层对象放进来。

3. 数据访问层(DAO层)

负责与数据库交互,DAO = Data Access Object(数据访问对象),即是用@Repository标记的类

DAO 层的作用:

接收业务层(Service)的请求

负责执行 SQL(增删改查)

返回结果对象给业务层

为什么要有 DAO?

想象一下:

如果业务层(Service)直接写 SQL,会导致业务逻辑和数据库操作耦合,代码很乱。

有了 DAO:

业务层只关心“我要查某个用户”

DAO 负责“SQL 语句怎么写、JDBC 怎么连数据库”,这样就实现了 职责分离。

而Hibernate就是一个能大大简化这个过程的 ORM 框架。

ORM 即对象关系映射

意思是:

  • 你在 Java 里用对象(Object)来写业务逻辑;
  • 但数据最后要存到关系型数据库(MySQL、PostgreSQL 等)的表里;
  • ORM 框架就帮你把对象和数据库表自动对应起来。

例如:User 类 对应users 表 , user.getName()对应数据库字段 name

我们不需要写sql了,直接用java写ORM对象及方法就可以实现

JSP、Action类、Servlet 在 MVC 框架里是怎么配合工作的 ?

Servlet + JSP

在最原始的 Java Web(没用框架)里:

浏览器请求 → Servlet

Servlet 接收请求,执行业务逻辑。

Servlet 处理完,把数据存进 request / session 域。

Servlet 转发 (forward) 给 JSP。

JSP 填充数据进入html模版(只是显示,不写业务逻辑)。

所以:Servlet 是控制器,JSP 是视图。

Struts2 的流程(Action)

浏览器请求 /login.action。

请求先经过前端控制器(Filter:StrutsPrepareAndExecuteFilter)。

Struts 找到对应的 Action 类(比如 LoginAction)。

执行一系列拦截器(Interceptor链) → 再执行 Action 的 execute() 方法。

Action 处理业务逻辑,返回一个字符串(如 "success")。

Struts2 根据返回值去找对应的结果视图(JSP)。

转发到 success.jsp,显示结果。

在 Struts2 里:

Action 类 = 控制器

JSP = 视图

Servlet(即 StrutsPrepareAndExecuteFilter 内部用到的 Servlet 技术)对开发者是“透明的”,不直接写。

Spring MVC 的流程(Controller)

浏览器请求 /login。

请求进入前端控制器 DispatcherServlet(这是一个 Servlet)。

DispatcherServlet 找到对应的 Controller 类(比如 LoginController)。

调用 Controller 的方法 → 方法返回一个 ModelAndView 或字符串(视图名)。

DispatcherServlet 把数据传给 视图解析器,找到 JSP。

JSP 渲染页面,返回给浏览器。

在 Spring MVC 里:

Controller 类 = 控制器

JSP = 视图

DispatcherServlet = Servlet 前端控制器

核心区别一句话

原始 Java Web:Servlet 直接当控制器。

Struts2:Action 类取代了 Servlet,当控制器。

Spring MVC:Controller 类取代了 Servlet,当控制器,但底层还是 DispatcherServlet 驱动。

ServletContext

ServletContext = Web 应用的全局上下文对象。

ServletContext 本身是接口(javax.servlet.ServletContext)。

Tomcat 之类的容器会给它提供一个实现类。

例如:Tomcat 里有 org.apache.catalina.core.ApplicationContext,它就是实现类。

当你在 Servlet 里调用 getServletContext(),得到的就是这个实现类的对象。

它在整个 Web 应用启动时由服务器(Tomcat)创建,一个 WebApp 只有一个 ServletContext。

用途:

    • 存储全局属性(所有 servlet/jsp 都能访问)
    • 获取资源路径(getResourceAsStream("/WEB-INF/..."))
    • 拿容器信息(服务器版本、路径等)
server.xml

它是 Tomcat 的主配置文件,路径一般在 TOMCAT_HOME/conf/server.xml。

它定义了 整个服务器的组件结构:

  • 端口(如 8080)
  • Service(服务)
  • Connector(连接器,负责监听端口、接收请求)
  • Engine / Host / Context(不同层级的容器)
  • 各种 Valve、Filter(过滤器/日志组件)

里面有一个类AccessLogValue,它专门用来写访问日志,在server.xml里大概长这样:

<Valve className="org.apache.catalina.valves.AccessLogValve"
       directory="logs"
       prefix="localhost_access_log"
       suffix=".txt"
       pattern="%h %l %u %t &quot;%r&quot; %s %b" />

这里面有个suffix属性,是规定日志文件的后缀名的, AccessLogValve 会按日期(默认每天)切分日志文件。 更改后缀名后,会在下一次生成日志文件时生效

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐