简介
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
优点:
- 职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务, 通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的作用。
高扩展性
结构 一个是真正的你要访问的对象(目标类),另一个是代理对象, 真正对象与代理对象实现同一个接口,由代理控制目标对象的一切方法。
创建动态代理的步骤
- 目标对象必须要实现一个接口,代理对象实现这个接口从而能够实现这些方法。
- 创建动态代理对象,传入三个参数。
参数1:加载目标类的类加载器。web工程发布后由类加载器web-inf的classes中,从而能够找到该目标类 。
参数2:目标对象实现的接口。
参数3:代理类对目标对象方法的控制器。new InvocationHandler(proxy,method,args);
- new InvocationHandler(Object proxy,Method method,Parameter args)对目标对象方法的控制。在这里可以进行方法增强等操作。
1. 参数1: proxy,当前代理对象的引用。一般不使用它
2. 参数2: method,调用方法时该方法的字节码对象。
3. 参数3: args,调用方法时传递进来的实际参数
例:
1. 要实现的共同接口:
public interface TargetInterface {
public abstract void method1();
public abstract String method2();
public abstract int method3(int num);
}
2. 被代理的目标类
//代理对象和目标对象必须实现同一个接口,即动态代理对象必须成为共同接口的实现类对象,这样就具备了共同的方法
//在jdk中,动态代理和目标对象必须共同实现一个接口,才能实现动态代理
public class Target implements TargetInterface {
public void method1() {
System.out.println("method1");
}
public String method2() {
System.out.println("method2");
return "method2";
}
public int method3(int num) {
System.out.println("method3");
return num;
}
}
3. 代理类并测试
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(//
target.getClass().getClassLoader(), // 要代理的目标对象的类加载器
target.getClass().getInterfaces(), // 和目标对象实现共同的接口
new InvocationHandler() { // 调用目标对象的某个方法
// 参数1:proxy : 动态代理对象
// 参数2:method : 要调用的目标对象的方法的字节码对象
// 参数3:args : 调用目标对象的方法时传递的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理调用目标函数的方法
Object invoke = method.invoke(target, args);
return invoke;// 返回调用目标函数方法后的返回值
}
});
// 使用动态代理对象调用方法
proxy.method1();// 使用目标函数对象,需要将动态代理的类型从Object强转为接口对象的实现子类(多态)
String method2 = proxy.method2();
int method3 = proxy.method3(100);
System.out.println(method2);
System.out.println(method3);
}
}
1.3 使用动态代理解决全站编码问题
1. 分析
- 首先创建一个表单页面和一个servlet小程序用于测试。
- 要对全站编码进行控制,需要经过Filter过滤器进行控制,过滤路径url-pattern设为
/*
即对所有请求进行拦截。 - 需求:我们解决的是servlet中获取参数的getParameter()方法时及servlet写回数据到客户端时的乱码问题。
- 使用动态代理解决,首先找到需要被代理的对象是HttpServletRequest的实现类对象.
- 创建一个动态代理。
- 获取目标对象的类加载器,与目标对象实现同一个接口HttpServletRequest。
- 在new InvocationHandler()方法控制器中找到getParameter()方法进行增强,在这里面解决乱码逻辑。
- 对于其他无需改变的方法,不进行改变,按原来的逻辑返回。
- 放行(这里的参数request要改为动态代理对象proxy了,这样servlet调用getParameter方法才是增强后的)。
2. 代码实现
这里省略jsp页面和servlet程序的代码,就是一个简单的jsp表单提交和回写数据
Filter的动态代理代码这里省略jsp页面和servlet程序的代码,就是一个简单的jsp表单提交和回写数据
Filter的动态代理代码
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
// 需求: 使用动态代理解决全站编码问题
// 1. 强制转换
final HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
response.setContentType("text/html;charset=UTF-8");// 设置服务器写回客户端的编码格式
// 2. 业务逻辑 : 使用动态代理解决全站编码问题
// 2.1 创建动态代理对象,返回的是和目标对象实现了同一接口的动态代理对象
HttpServletRequest proxy = (HttpServletRequest) Proxy.newProxyInstance( //
request.getClass().getClassLoader(), // 参数1:加载目标对象的类加载器
request.getClass().getInterfaces(), // 参数2:与目标对象实现相同的接口
new InvocationHandler() {// 参数3:目标对象的方法控制器,即如何代理?
// 参数1:proxy:就是当前的动态代理对象
// 参数2:method:代理对象调用目标对象的某个方法的字节码对象
// 参数3:args:调用某个方法时传递的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断找出getParameter()方法进行方法曾倩
if ("getParameter".equals(method.getName())) {
// 解决编码集的代码
// 获取表单提交的方式
String requestMethod = request.getMethod();
if ("GET".equalsIgnoreCase(requestMethod)) { // 如果是get方式提交数据
// 获取提交的数据
String parameter = (String) method.invoke(request, args);
// 对获取的数据重新编解码
parameter = new String(parameter.getBytes("ISO-8859-1"), "UTF-8");
return parameter;
} else if ("POST".equalsIgnoreCase(requestMethod)) {
request.setCharacterEncoding("UTF-8");
}
}
// 对于不需增强的方法,按原来的逻辑执行后返回,不做任何修改
return method.invoke(request, args);
}
});
// 3. 放行(将与request实现了同一接口的动态代理作为参数传递放行)
chain.doFilter(proxy, response);
}
评论前必须登录!
注册