Java动态代理类使用
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
一、Interface InvocationHandler:该接口中仅定义了一个方法
Object invoke(Object obj,Method method,Object[] args)
。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。二、Proxy:该类即为动态代理类,其中主要包含以下内容:
Protected Proxy(InvocationHandler h)
:构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoader loader,Class[] interfaces)
:获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
:返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。在使用动态代理类时,我们必须实现InvocationHandler接口.
代理机制及特点
通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
——参考《百度百科》
Proxy类
一般使用Proxy.newProxylnstance(ClassLoader loader,Class[] interfaces,InvocationHandler invocationHandler)
来创建代理对象
- 参数1:
loader
, 类加载器, 动态代理类运行时创建, 任何类都需要类加载器将其加载到内存。
- 一般情况:
被代理类.class.getClassLoader()
或者被代理类实例.getClass().getClassLoader()
- 一般情况:
- 参数2:
interfaces
代理类需要实现的所有接口。
- 方式1:
被代理类实例.getClass().getInterfaces()
- 注意: 只能获得自己接口, 不能获得父元素接口
- 方式2:
new Class[]{ 被代理类.class }
- 例如:jdbc驱动 –> DriverManager 获得接口 Connection
- 方式1:
- 参数3:
invocationHandler
处理接口, 一般采用匿名内部类实现。
- 提供
invoke
方法,代理类的每一个方法执行时, 都将调用一次invoke
方法 Object invoke(Object obj,Method method,Object[] args)
- 参数1:
proxy
: 代理对象 - 参数2:
method
: 代理对象当前执行的方法的描述对象(反射)
- 获取方法名:
method.getName()
- 执行方法:
method.invoke(对象, 实际参数)
- 获取方法名:
- 参数3:
args
: 方法实际参数
- 参数1:
- 提供
使用步骤
- 创建 被增强对象【被代理对象】
- 创建 增强对象 【代理对象】
- 通过被代理对象获取类加载器
被代理对象.getClass().getClassLoader()
- 通过被代理对象获取其所有的接口
被代理对象.getClass().getInterfaces()
- 创建一个处理类 【专门用来增强方法】
- 通过被代理对象获取类加载器
PS:动态代理技术实现的是接口中方法的代理,如果接口中没有的方法,动态代理是没法增强的!
案例:编码问题动态代理解决
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>get请求</h1>
<form action="${pageContext.request.contextPath}/EncodingServlet" method="get">
<input type="text" name="username" /><br/>
<input type="submit" value="提交"/>
</form>
<br/>
<h1>post请求</h1>
<form action="${pageContext.request.contextPath}/EncodingServlet" method="post">
<input type="text" name="username" /><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
EncodingFilter.java
package com.pc.web.filter;
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;
/**
* 编码过滤器,使用动态代理解决编码问题
*
* @author Switch
* @data 2016年10月25日
* @version V1.0
*/
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 处理POST乱码问题
req.setCharacterEncoding("utf-8");
// 处理GET乱码问题
// 创建HttpServletRequest代理对象
// 参数1:ClassLoader loader 该代理类的类加载器
// 参数2:Class<?>[] interfaces 该代理类的接口
// 参数3:InvocationHandler h 代理实例的调用处理程序 实现的接口
HttpServletRequest reqProxyInstance = (HttpServletRequest) Proxy.newProxyInstance(
req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
// 参数1:Object proxy 代理实例
// 参数2:Method method 某一方法
// 参数3:Object[] args 参数列表
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 当当前方法是"getParameter"时,增强该方法
if ("getParameter".equals(method.getName())) {
// 反射获取"getParameter"方法的返回值
String value = (String) method.invoke(req, args);
// GET请求方式则处理乱码问题
if ("GET".equalsIgnoreCase(req.getMethod())) {
value = new String(value.getBytes("iso8859-1"), "utf-8");
}
// 返回该值
return value;
}
// 返回其他方法的值,并没有进行处理(增强)
return method.invoke(req, args);
}
});
// 放行,使用动态代理对象
chain.doFilter(reqProxyInstance, res);
}
public void init(FilterConfig fConfig) throws ServletException {
}
public void destroy() {
}
}
EncodingServlet.java
package com.pc.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 编码测试Servlet
*
* @author Switch
* @data 2016年10月25日
* @version V1.0
*/
public class EncodingServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
System.out.println(username);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
System.out.println(username);
}
}
PS:编码问题的装饰者解决:过滤器Filter