前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >啰里吧嗦式讲解java静态代理动态代理模式

啰里吧嗦式讲解java静态代理动态代理模式

原创
作者头像
葫芦娃
修改2019-03-01 11:53:28
7320
修改2019-03-01 11:53:28
举报

一.为啥写这个

文章写的比较啰嗦,有些东西可以不看,因为想看懂框架, 想了解SSH或者SSM框架的设计原理和设计思路,又去重新看了一遍反射和注解, 然后看别人的博客说想要看懂框架得先看懂设计模式,于是遇到了动态代理这个大坑,写博客等于是对自己学习过程的一个回顾和总结

本文主要参考欧阳锋的10分钟看懂动态代理设计模式


二.理解和弄懂代理的前期准备

2.1.什么是代理

简单来说就是有活不自己干,让别人干, 比如你不想写作业, 让同学帮你写,然后写上自己的名字, 这个同学就是你的代理, 帮你处理一些事情

2.2.如何利用代码生成一个java文件

生成一个java文件不过就是字符串的拼接,最后利用流输出到一个目录下面,以.java结尾,不需要从头开始写,有个好用的工具包javaPoet,可以去GitHub上下载,也可以通过maven的方式引入

代码语言:javascript
复制
<dependency>
    <groupId>com.squareup</groupId>
    <artifactId>javapoet</artifactId>
    <version>1.8.0</version>
 </dependency>

2.3.如何使用代码将生成的java文件编译成class文件

代码语言:javascript
复制
//编译桌面 proxy文件夹下的Proxy.java文件  会生成一个Proxy.class文件
JavaCompiler.compile(new File(sourcePath + "/proxy/Proxy.java"));

我的地址(sourcePath)我选择输出在桌面:"C:/Users/你的计算机登陆用户名/Desktop"

2.4.如何利用代码将class 使用 类加载器加载进内存,并创建对象

利用类加载器,将该目录下的class文件加载进内存中

代码语言:javascript
复制
// 使用反射load到内存
URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL("file:C:\\Users\\保密\\Desktop\\") });

Class clazz1 = classLoader.loadClass("proxy.Proxy");
代码语言:javascript
复制
//通过Class的对象 clazz1 的getConstructor方法, 得到类Proxy的构造器对象,  InvocationHandler.class是入参
Constructor constructor = clazz1.getConstructor(InvocationHandler.class);
//通过构造器对象的newInstance 方法, 去创建一个类的对象
Object obj = constructor.newInstance(handler);

2.5.方法的反射知识

java有个Method类,可以通过method.invoke执行该方法

代码语言:javascript
复制
//拿到Person类下面  public void buy(){} 方法对象
Method method = Person.class.getMethod("buy"){};
//通过method方法的invoke方法可以执行该方法, 入参是该方法属于的类的对象,和参数
method.invoke(person,new Object[] {}) 效果等同于new Person.buy()
代码语言:javascript
复制
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Person {
    
    public void buy() {
        
        System.out.println("买买买");
    }
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, 
        IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        new Person().buy();
        Method m = Person.class.getMethod("buy");
        m.invoke(new Person(), new Object[] {});
    }
}
代码语言:javascript
复制
买买买
买买买

写代码的过程中会遇到各种各样的错误,比如什么NoSuchException,遇到这类问题不要慌,大胆的用System.out.println打印每个对象的信息,去分析可能出现的原因

三.静态代理

前面的知识知道也行,不知道也无所谓,不影响了解静态代理

假设一个jar包下面有个接口Servlet, Servlet接口里有个方法比如上传文件,

有个实现类HttpServlet,实现了这个功能

代码语言:javascript
复制
public interface Servlet {
    //假设一个java jar包下面的接口 有这样一个功能就是上传文件 
    void uploadFile();
}

public class HttpServlet implements Servlet{
	
	@Override
	public void uploadFile() {
		// TODO Auto-generated method stub
		System.out.println("我要开始上传文件啦......");
		System.out.println("真的很复杂啦......");
	}
}

你现在不能动jar包的源码, 你想要在上传文件前或者后干点自己的事情,就好比作业让HttpServlet帮你干,但最后写上自己的名字, 这里我们假设你需要计算上传文件的时间那么

代码语言:javascript
复制
思路1
在每个该方法的调用前加入一段记录时间的代码
public static void main(String[] args) {
		long t1 = System.currentTimeMillis();
		new HttpServlet().uploadFile();
		long t2 = System.currentTimeMillis;		
		system.out.println("花费的时间" + t2-t1);
}
我要开始上传文件啦......
真的很复杂啦......
1
------------------
这种方式显而易见的弊端就是,只要调用uploadFile方法,就要加两行代码计算,代码重复了N份,以后如果又不需要计算时间了
改起来会非常麻烦
代码语言:javascript
复制
思路2
通过继承的方式重写uploadFile()的方法
public class HttpServletForCalTime extends HttpServlet{
	
	@Override
	public void uploadFile() {
		
		System.out.println("统计时间....");
		super.uploadFile();
		System.out.println("统计时间....");
	}
	
	public static void main(String[] args) {
		
		HttpServletForCalTime a = new HttpServletForCalTime();
		a.uploadFile();
	}
}
统计时间....
我要开始上传文件啦......
真的很复杂啦......
统计时间....
---------------------
这种写法的好处是统计文件上传时间用HttpServletForCalTime 类就行了,但是弊端就是不好扩展,比如我想在统计时间前
先对文件的关键字进行屏蔽处理,把王八蛋换成空格,那么又得继承
如果我想先计算时间 在屏蔽关键字,在上传文件,那么又得创建一个新的继承类
类无限的扩展,因为继承是一种包裹关系,不够灵活
代码语言:javascript
复制
思路3---静态代理
使用聚合,动态注入所要代理的对象,
原文中有句非常经典的话:其实设计模式,多多少少都跟多态有关
我理解的多态就是java的具体执行哪个方法不是在编译期绑定,而是在运行期动态绑定

//创建一个代理类, 有个成员变量, 执行uploadFile的内容 取决于你传入什么Servlet对象
public class ServletTimeProxy implements  Servlet{
	
	private Servlet servlet;
	
	public ServletTimeProxy(Servlet servlet) {
		this.servlet = servlet;
	}

	@Override
	public void uploadFile() {
		
		System.out.println("计算时间...");
		
		servlet.uploadFile();
		
		System.out.println("计算时间...");
	}
	
}


public class ServletLogProxy implements  Servlet{
	
	private Servlet servlet;
	
	public ServletLogProxy(Servlet servlet) {
		this.servlet = servlet;
	}

	@Override
	public void uploadFile() {
		
		System.out.println("打印日志...");
		
		servlet.uploadFile();
		
		System.out.println("打印日志...");
	}
	
}

执行什么内容取决于你传入什么对象,比较灵活,如果想先计算时间再打印日志
就调个个就行了

活还是HttpServlet代替你干,
public static void main(String[] args) {
		
		HttpServlet impl = new HttpServlet();

		ServletTimeProxy a = new ServletTimeProxy(impl);//统计时间
		ServletLogProxy  b = new ServletLogProxy(a);//打印日志

		b.uploadFile();
}
-----------------------------------------
我要打印日志....
计算时间...
我要开始上传文件啦......
真的很复杂啦......
计算时间...
我要打印日志....

该方法也有弊端,就是扩展性还是不够好,如果jar包里我有N个类的xx方法都需要计算执行时间,打印日志
那么得实现N个jar包里的接口,注入接口,代理该接口的方法

接下来的神仙方法就来了, 我根据你传过来的接口对象,根据你想自定义的执行方法,我给你生成java类,我给你编译Java类,我去将class加载进内存中, 我去执行 你指定的方法, 你就不用写那么多份xx implements xxInterface代码了

java.lang.reflect.Proxy @since 1.3

四.动态代理的原理

没时间的童鞋可以不看,我是被10分钟看懂动态代理给坑了,它让你自己写了java反射类Proxy的大概实现,这时候前面的知识就用得上了

思路1,首先我们想通过代码创建一个java类,它长这样

代码语言:javascript
复制
package proxy;

import java.lang.Override;
import java.lang.System;
import staticProxy.Servlet;

class Proxy implements Servlet {
  private Servlet impl;

  public Proxy(Servlet impl) {
    this.impl = impl;
  }

  @Override
  public void uploadFile() {
    long start = System.currentTimeMillis();

    this.impl.uploadFile();

    long end = System.currentTimeMillis();
    System.out.println("Fly Time =" + (end - start));
  }
}


那么利用javapoet可以这么写,我写Proxy1 是1.0版本
public class Proxy1 {
	 
    public static Object newProxyInstance() throws IOException {
    	
    	//第一步 通过javapoet工具类 创建一个名为Proxy的java类 , 该类实现了Servlet接口
    	//  ==   public class Proxy implement Servlet
    	//这里之前少写一个public 导致后面获取构造器对象时报NoSuchException,
        TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("Proxy")
                .addModifiers(Modifier.PUBLIC)//定义类的修饰符//构造一个类,类名
                .addSuperinterface(Servlet.class);
        
        
        
        //第二步 创建类里面的属性 我们要注入该接口 
        //我们希望的属性是 private Servlet impl
        //所以指定 属性的类型 和 属性的名字 和 属性的范围
        //注意 Modifier包千万不要导错了,import javax.lang.model.element.Modifier;
        //之前报错找了好久
        FieldSpec fieldSpec = FieldSpec.builder(Servlet.class, "impl", Modifier.PRIVATE).build();
        
        //将属性 添加到类中
        typeSpecBuilder.addField(fieldSpec);
        
        
        //我们希望构造方法中 将该接口的实现类 传到构造器里 方便灵活调用要处理的方法
        //Proxy(Servlet impl) {
		   //    this.impl = impl;
	     //  }
        
        MethodSpec constructorMethodSpec = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(Servlet.class, "impl")//构造器传入参数的类型 就是你要创建的java类实现接口的类型, 属性名字
                .addStatement("this.impl = impl")//构造器里面的语句
                .build();
        
        typeSpecBuilder.addMethod(constructorMethodSpec);
        
        //类 ,属性 ,构造器都写完了, 接下来开始写方法 , 你实现的接口有N个方法
        Method[] methods = Servlet.class.getDeclaredMethods();
        for (Method method : methods) {
        	
        	//在文件中写入方法 和 方法内容  方法里有返回值 入参 方法名 
            MethodSpec methodSpec = MethodSpec.methodBuilder(method.getName())
            		 //给方法添加范围
                    .addModifiers(Modifier.PUBLIC)
                     //给方法添加 注解  通常都是实现 x接口的覆盖注解
                    .addAnnotation(Override.class)
                     //给方法添加返回值    通过method.getReturnType能拿到该方法return接收的对象
                    .returns(method.getReturnType())
                     //给方法添加语句  这里是给每个方法都加上了一个计算时间
                    .addStatement("long start = $T.currentTimeMillis()", System.class)
                     //添加换行符
                    .addCode("\n")
                     //添加执行语句  我们希望的执行语句是 执行传过来的接口的某个方法
                     //比如 impl.upload()
                     //所以前面我们属性 FieldSpec fieldSpec定义的属性名是什么就
                     
                     //而要执行的方法名就是我们实现接口的方法名  就是通过反射拿到的每个方法的名字 
                     //即执行的方法名和 实现接口的方法名一致
                    .addStatement("this.impl." + method.getName() + "()")
                    .addCode("\n")
                     //$T可以理解为占位符,javapoet会去找对应的类
                    .addStatement("long end = $T.currentTimeMillis()", System.class)
                    .addStatement("$T.out.println(\"Fly Time =\" + (end - start))", System.class)
                    .build();
            //给每个类加一个方法
            typeSpecBuilder.addMethod(methodSpec);
        }
        
        //生成一个 TypeSpec 前面定义的java文件  第一个参数是包名 这样生成的xx.java文件再次包下
        JavaFile javaFile = JavaFile.builder("proxy", typeSpecBuilder.build()).build();
      
        String sourcePath = "C:/Users/自己登陆的电脑用户名/Desktop";
        
        //在桌面上生成一个Proxy.java的文件
        javaFile.writeTo(new File(sourcePath));
 
        return null;
    }

执行 Proxy1.newProxyInstance()方法,发现确实在桌面上生成了一个proxy文件夹,里面有个proxy.java

1.0版本我们写死了Proxy要实现的接口,现在我们把它作为参数传进去,要改动几个点,升级为2.0

代码语言:javascript
复制
    //代码只贴了改动点
    public class Proxy2 {
     
    public static Object newProxyInstance(Class clazz) throws IOException {
    
              TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("Proxy")
                .addSuperinterface(clazz);
              
              //指定属性 是那种类型 ,代理,代理, 就是代理的你要实现的类的接口, 起码现在看来是这样
              FieldSpec fieldSpec = FieldSpec.builder(clazz, "impl", Modifier.PRIVATE).build();
              
              //方法的构造器
              MethodSpec constructorMethodSpec = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(clazz, "impl")//构造器传入参数的类型 就是你要创建的java类实现接口的类型, 属性名字
                .addStatement("this.impl = impl")//构造器里面的语句
                .build();
    }
    
    执行 Proxy2.newProxyInstance(Servlet.class); 

现在虽然我们能实现代理任意接口了,但是目前只能够再任意接口的 任意方法上面加上时间计算, 如何可以实现自定义执行逻辑呢----代理中的代理 Proxy3.0版本

首先自定义方法,那么之前在文件中写入方法就不能写死,让生成的Proxy.java文件,代理某个接口的某个方法里面

可以获得该方法的对象, 该方法对象通过执行method.invoke 来执行代理方法,虽然我不知道java大神是怎么想到的

(可能就是我定义一个接口,你自己去写接口的实现,我代理我的接口, 你自己的实现代理 你要代理的类)

不过他们是通过定义了一个接口InvocationHandler.java, 接口里有个方法, 凡是用到动态代理的,都需要传入一个代理类的接口, 和自定义的处理接口实现类

代码语言:javascript
复制
//1.凡是要实现自定义方法逻辑处理的都要实现该接口
public interface InvocationHandler {
        void invoke(Object proxy, Method method, Object[] args);
}

//2.然后自定义处理逻辑都实现该接口
package animateProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import staticProxy.Servlet;

public class MyInvocationHandler implements InvocationHandler {

	private Servlet impl; //为了完成对被代理对象的方法拦截,我们需要在InvocationHandler对象中传入被代理对象实例。

	public MyInvocationHandler(Servlet impl) {
		this.impl = impl;
	}
	//参数1  想要、
	
	//参数2  这个参数表示  传入接口  中的所有Method对象
	
	//参数3 这个参数   对应当前method方法   中的参数
	@Override
	public void invoke(Object proxy, Method method, Object[] args) {
		// TODO Auto-generated method stub
		//1.自定义想要加在 动态代理类 的方法的处理 比如 
		//统计想要 代理类的 方法的时间
		long start = System.currentTimeMillis();
		
		System.out.println("proxy:" + proxy);
		
		try {
			//方法的反射操作
			//方法的反射操作是method对象来进行方法调用
			//和 执行  impl.upload() 效果完全相同
			method.invoke(impl, new Object[] {});
			
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		long end = System.currentTimeMillis();
        System.out.println("Fly time = " + (end - start));
	}

}



//3.然后希望我们生成的代理类是这样的
package proxy;

import animateProxy.InvocationHandler;
import java.lang.Override;
import java.lang.reflect.Method;
import staticProxy.Servlet;

public class Proxy implements Servlet {
  private InvocationHandler handler;

  public Proxy(InvocationHandler handler) {
    this.handler = handler;
  }

  @Override
  public void uploadFile() {
    try {
    	Method method = staticProxy.Servlet.class.getMethod("uploadFile");
    	 this.handler.invoke(this, method, null);
    } catch(Exception e) {
    	e.printStackTrace();
    }
  }
}
//通过反射来拿到 代理接口实现类的 方法对象, 如果你传入MyInvocationHandler,执行MyInvocationHandler里的
//invoke方法 将你代理的方法对象作为参数传进去 ---->在自定义处理类里面又注入了Servlet接口----->通过方法的反射
//来执行比如HttpServlet.xxxx()方法的逻辑
现在的流程是 proxy.newProxyInstance ----> Proxy.xxxx()---->自定义的InvocationHandler  ---->具体要代理的类的
方法
调用过程是这样的
    InvocationHandler  handler = new MyInvocationHandler(new HttpServlet());
		 //获得了代理类的对象 
		Servlet a = (Servlet) Proxy3.newProxyInstance(Servlet.class, handler);
		System.out.println("返回的代理对象" + a);
		a.uploadFile();

就是其实我们生成的代理对象Proxy 代理的是InvocationHandler  ,而你自己实现的MyInvocationHandler里
代理了HttpServlet,这中间离不开反射, 这样你只需要 implements InvocationHandler, 然后在invoke方法里
写自己的处理逻辑就可以了

至于为啥一个InvocationHandler接口的方法里  还要一个参数Object proxy,
原博文中说:如果你的接口中有方法需要返回自身,
如果在invoke中没有传入这个参数,将导致实例无法正常返回。在这种场景中,proxy的用途就表现出来了。
简单来说,这其实就是最近非常火的链式编程的一种应用实现
这个暂时没看懂 

针对以上第3点,在此对Proxy代理生成类作修改,定义为Proxy3,

只贴修改的部分

1.我们希望的是代理InvocationHandler, 
FieldSpec fieldSpec = FieldSpec.builder(InvocationHandler.class, "handler", Modifier.PRIVATE).build();
2.构造器也要改造
MethodSpec constructorMethodSpec = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(InvocationHandler.class, "handler")//构造器传入参数的类型 就是你要创建的java类实现接口的类型, 属性名字
                .addStatement("this.handler = handler")//构造器里面的语句
                .build();
3.具体实现方法
Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
        	
        	//在文件中写入方法 和 方法内容  方法里有返回值 入参 方法名 
            MethodSpec methodSpec = MethodSpec.methodBuilder(method.getName())
            		 //给方法添加范围
                    .addModifiers(Modifier.PUBLIC)
                     //给方法添加 注解  通常都是实现 x接口的覆盖注解
                    .addAnnotation(Override.class)
                     //给方法添加返回值    通过method.getReturnType能拿到该方法return接收的对象
                    .returns(method.getReturnType())
                     //给方法添加语句  这里是给每个方法都加上了一个计算时间
                    
                    .addCode("try {\n")
                    
                     //Method method = staticProxy.Servlet.class.getMethod("uploadFile");
    	               //这样写是为了拿到 要代理的那个接口类的 xx 方法
                    .addStatement("\t$T method = " + clazz.getName() + ".class.getMethod(\"" + method.getName() + "\")", Method.class)
                     // 为了简单起见,这里参数直接写死为空
                    
                     //这样写诗为了执行 实现了InvocationHandler接口的 java类自己的处理方法
                     // this.handler.invoke(this, method, null);
                    .addStatement("\t this.handler.invoke(this, method, null)")
                    .addCode("} catch(Exception e) {\n")
                    .addCode("\te.printStackTrace();\n")
                    .addCode("}\n")

                    .build();
            //给每个类加一个方法
            typeSpecBuilder.addMethod(methodSpec);
        }

接下来把编译 ---装载--返回代理类对象都写全了

代码语言:javascript
复制
        String sourcePath = "C:/Users/"+System.getProperties().getProperty("user.name")+"/Desktop";
        
        JavaFile javaFile = JavaFile.builder("proxy", typeSpecBuilder.build()).build();
        // 为了看的更清楚,我将源码文件生成到桌面
       
        javaFile.writeTo(new File(sourcePath));
 
        // 编译
        JavaCompiler.compile(new File(sourcePath + "/proxy/Proxy.java"));
 
        // 使用反射load到内存
        URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL("file:C:\\Users\\"+System.getProperties().getProperty("user.name")+"\\Desktop\\") });

        Object obj = null;
        
        Class clazz1 = classLoader.loadClass("proxy.Proxy");
        
        System.out.println(clazz1);
        System.out.println(clazz1.getDeclaredConstructors().getClass());
        
        //将生成的TimeProxy编译成class 使用类加载器加载进内存中 再通过反射或者该类的构造器 
        //再通过构造器将其代理类 TimeProxy 构造出来
        
        //NoSuchException  打印classz信息 发现 刚开始创建类 没有使用public 
        Constructor constructor = clazz1.getConstructor(InvocationHandler.class);
        System.out.println("constructor" + constructor);
        obj = constructor.newInstance(handler);
        
        return obj;

如果下面代码 运行 出现NoSuchmethod错误 将桌面上生成的Proxy.java放到项目proxy包下面

代码语言:javascript
复制
        Servlet a = (Servlet) Proxy3.newProxyInstance(Servlet.class, handler);
        
        System.out.println("返回的代理对象" + a);//返回的代理对象proxy.TimeProxy@5b2936fa

        //handler.invoke(a, a.getClass().getDeclaredMethod("uploadFile", null), null);
        // 代理了interfaceTimpl类 并且 写了自己的实现
        a.uploadFile();

五.JDK动态代理类的用法

使用动态代理我们在不动本来源码的基础上,添加自己的逻辑,以下场景: 事务提交或者回退,权限管理,Bug修复


六.动态代理的实际运用

好啦,终于花了一下午时间写完了,完结撒花~~~~~~~~

代码语言:javascript
复制
代码来自李阿昀的博客https://blog.csdn.net/yerenyuan_pku/article/details/52598220

在动态代理技术里,由于不管调用被代理对象的什么方法(比如上文中的 public interface Servlet, 当有方法需要 
实现了该接口的对象作为参数, 比如 x.create(HttpServlet s)
使用动态代理 (Servlet)Proxy.newProxyInstance(xx,xx,xx)
这样不论它调用的是 HttpServlet里的什么方法
实际上先执行的都是 实现了InvocationHandler接口的invoke方法
),
都是调用开发人员编写的的invoke方法
(这相当于invoke方法拦截到了代理对象的方法调用)

并且开发人员通过invoke方法的参数,还可以在拦截的同时,
知道用户调用的是什么方法,因此利用这两个特性,
就可以实现一些特殊需求,例如:拦截用户的访问请求,
以检查用户是否有访问权限、动态为某个对象添加额外的功能。

package 动态代理的实际运用;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 利用动态代理技术编写一个用于处理全站中文乱码的过滤器
 * 
 * 实现了对HttpServlet.getParamter方法的拦截
 * 
 * <p>Description: </p>  
 * @author 18045153  
 * @date 2019年2月28日
 */
public class CharacterEncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        request.setCharacterEncoding("UTF-8"); // 解决post方式请求下的中文乱码问题    get:request.getParameter()

        // servlet-----> requestRroxy.getCookie() 
        //传入自定义的 ServletRequest 对象 
        chain.doFilter((ServletRequest) Proxy.newProxyInstance(
        		CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
        	    //类加载器,你可以使用自定义的类加载器                      HttpServletRequest接口 , 这样动态生成的代理类 实现了该接口下的所有方法
        	
        	//传入的method对象 实际就表示是  HttpServletRequest接口下的所有方法的对象
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
                /*
                 * 判断在Servlet那端调用的是不是getParameter方法,
                 * 如不是则不需要拦截,直接放行,直接调用tomcat的request(真的request)帮你把事情干了,
                 * 并且args参数还要接着往下传
                 */
                if (!method.getName().equals("getParameter")) { 
                    return method.invoke(request, args); // 由于是内部类,所以request需要声明为final(但在Java8中没这个必要了)
                }
                // 判断客户端的请求方式是不是get
                if (!request.getMethod().equalsIgnoreCase("get")) {
                    return method.invoke(request, args);
                }
                
                //执行完 HttpServletRequest 的public abstract String getParameter(String s);方法拿到的返回值
                String value = (String) method.invoke(request, args);
                if (value == null) {
                    return null;
                }

                // return new String(value.getBytes("ISO8859-1"), "UTF-8");
                return new String(value.getBytes("UTF-8"), "UTF-8");
            }
        }), response);

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }
    
    
       //在web.xml文件中配置CharacterEncodingFilter。
    //<filter>
   // <filter-name>CharacterEncodingFilter</filter-name>
  //  <filter-class>cn.itcast.web.filter.CharacterEncodingFilter</filter-class>
//</filter>
//<filter-mapping>
  //  <filter-name>CharacterEncodingFilter</filter-name>
 //   <url-pattern>/*</url-pattern>
//</filter-mapping>

}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.为啥写这个
  • 二.理解和弄懂代理的前期准备
    • 2.1.什么是代理
      • 2.2.如何利用代码生成一个java文件
        • 2.3.如何使用代码将生成的java文件编译成class文件
          • 2.4.如何利用代码将class 使用 类加载器加载进内存,并创建对象
            • 2.5.方法的反射知识
            • 三.静态代理
            • 四.动态代理的原理
            • 五.JDK动态代理类的用法
            • 六.动态代理的实际运用
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档