以 Filter 型内存⻢为例
在进⼊正题之前,先说两个东⻄
java 特性 -- 反射
java 的四⼤特性是,封装,继承,多态,反射,其中灵魂是反射。
这张图正常new⼀个对象的时候,逻辑过程是,把class⽂件加载到 jvm 中,之后才能产⽣ class 对象,但是利⽤反射机制的话,就能够直接在 jvm 中调⽤已经加载好的 class ⽂件,从⽽实现去new⼀个对象。简单的反射获取对象的⽅法有以下⼏种:
类名.class,如:com.anbai.sec.classloader.TestHelloWorld.class
Class.forName("com.anbai.sec.classloader.TestHelloWorld")
classLoader.loadClass("com.anbai.sec.classloader.TestHelloWorld")
常在连接数据库的时候这么用
Class.forName("com.mysql.jdbc.Driver")
①获取⽬标类型的Class对象
②通过Class对象分别获取Constructor类对象、Method类对象&Field类对象
③通过Constructor类对象、Method类对象&Field类对象分别获取类的构造函数、⽅法 &属性的具体信息,并进⾏后续操作
Tomcat 热加载
上⾯说到,根据双亲委派机制,那么什么是双亲委派机制呢?
所以在分析某些类的时候,要按照对应Tomcat⾥⾯去分析。
流程分析
filter也称之为过滤器,过滤器实际上就是对web资源进⾏拦截,做⼀些过滤,权限鉴别等处理后再交给下⼀个过滤器或servlet处理,通常都是⽤来拦截request进⾏处理的,也可以对返回的response进⾏拦截处理。
当多个filter同时存在的时候,组成了filter链。web服务器根据Filter在web.xml⽂件中的注册顺序,决定先调⽤哪个Filter。第⼀个 Filter的doFilter⽅法被调⽤时,web服务器会创建⼀个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的 doFilter方法,则 web 服务器会检查FilterChain对象中是否还有 filter 如果有则调⽤第2个filter 如果没有则调⽤⽬标资源。如果我们动态创建⼀个filter并且将其放在最前⾯,filter就会最先执⾏,当在filter中添加恶意代码,就会进⾏命令执⾏,这样也就成为了⼀个内存Webshell。
Filter生命周期
public void init(FilterConfig filterConfig) throws ServletException //初始化
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, Serv
letException; //拦截请求
public void destroy(); //销毁
Filter 对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在 Web 容器卸载Filter对象之前被调⽤。该⽅法在Filter的⽣命周期中仅执⾏⼀次。在这个⽅法中,可以释放过滤器使⽤的资源。我们可以通过动态注册的⽅法去注册⼀个Filter。
Filter 类的介绍
FilterDefs:存放FilterDef的数组 ,FilterDef中存储着我们过滤器名,过滤器实例等基本信息
FilterConfigs:存放 filterConfig 的数组,在FilterConfig 中主要存放FilterDef和Filter对象等信息
FilterMaps:存放FilterMap的数组,在FilterMap中主要存放了FilterName和对应的URLPattern
FilterChain:过滤器链,该对象上的doFilter方法能依次调⽤链上的Filter
ApplicationFilterChain:调⽤过滤器链
ApplicationFilterConfig:获取过滤器
ApplicationFilterFactory:组装过滤器链
WebXml:存放web.xml中内容的类
ContextConfig:Web应⽤的上下⽂配置类
StandardContext:Context接⼝的标准实现类,⼀个Context代表⼀个 Web应⽤,其下可以包含多个Wrapper
StandardWrapperValve:⼀个 Wrapper的标准实现类,⼀个 Wrapper代表⼀个Servlet
那什么是 Context,wrapper?
Servlet是java最基本的应⽤,而Tomcat是⼀个⼤的Servlet容器,tomcat主要由connector连接器和容器组成。(这四个容器的关系如下),是父子关系,不是平行关系
和Servlet的方式不同,Filter不能通过注解去配置,必须在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">
<filter>
<filter-name>filterDemo</filter-name>
<filter-class>FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filterDemo2</filter-name>
<filter-class>FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这⾥写了两FilterDemo,代码基本相同,主要是为了展示这个Filter过滤链。
import javax.servlet.*;
import java.io.IOException;
public class FilterDemo2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("第⼀个Filter 初始化创建");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System out println("第⼀个Filter执⾏过滤操作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
Tomcat是这样将我们⾃定义的filter调⽤的。
1. 根据请求的URL从FilterMaps中找出与之URL对应的Filter名称
2. 根据Filter名称去FilterConfigs中寻找对应名称的FilterConfig
3. 找到对应的FilterConfig之后添加到FilterChain中,并且返回FilterChain
4. filterChain 中调⽤internalDoFilter遍历获取chain中的FilterConfig,然后从FilterConfig中获取Filter,然后调⽤Filter的doFilter⽅法