代理技术就是用来产生一个对象的代理对象的。
明星和经纪人,经纪人就是明星的代理人。当我们需要找明星表演时,不能直接找到刘德华,而是只能找到刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问。
这个例子中核我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。
代理对象存在的价值主要用于拦截对真实业务对象的访问。
代理对象应该具有和目标对象相同的方法。
java.lang.reflect.Proxy类
jdk1.5以后推出这个类,通过Proxy类提供的一个newProxyInstance方法来创建一个对象的代理对象。
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用来返回一个代理对象,这个方法有3个参数,ClassLoader 用来明确生成代理对象使用哪个类装载器,Class<?>[] interfaces 用来明确生成哪个对象的代理对象,通过接口集合指定,InvocationHandler h用来明确产生的这个代理对象要做什么事情。
要想创建一个对象的代理对象,那么这个对象必须要有一个接口(为了目标对象和代理对象拥有相同的方法)。所以创建一个接口,用来定义这个对象所具有的行为、方法。
1.定义对象的行为接口
package com.david.Proxy; public interface IPerson { String sing(String name); //唱歌 String dance(String name); //跳舞 }
2.定义目标对象类
package com.david.Proxy; public class LiuDeHua implements IPerson { @Override public String sing(String name) { System.out.println("刘德华唱"+name); return "唱完了"; } @Override public String dance(String name) { System.out.println("刘德华跳舞:"+name); return "跳完了"; } }
3.创建生成代理对象的代理类
package com.david.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LiuDeHuaProxy { private IPerson liudehua = new LiuDeHua(); public IPerson GetProxy(){ return (IPerson) Proxy.newProxyInstance(LiuDeHua.class.getClassLoader(), liudehua.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName() == "sing"){ System.out.println("我是经纪人,先给我十万块在唱歌"); return method.invoke(liudehua,args); } if(method.getName() == "dance"){ System.out.println("我是经纪人,跳舞需要二十万"); return method.invoke(liudehua,args); } return null; } }); } }
测试
LiuDeHuaProxy proxy = new LiuDeHuaProxy(); IPerson p = proxy.GetProxy(); String s1 = p.sing("忘情水"); System.out.println(s1); String s2 = p.dance("霹雳舞"); System.out.println(s2);
Proxy类负责创建代理对象时,如果指定了handler,那么不管用户调用代理对象的哪个方法,该方法都是调用处理器的invoke方法。由于invoke方法被调用需要三个参数,代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己在的对象、自己、方法的参数传递进来。
动态代理应用
由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器invoke方法 这相当于invoke方法拦截到了代理对象的方法调用。在拦截的同时还知道用户调的是什么方法,利用这两个特性就可以实现一些特殊需求,如:拦截用户的访问请求,检查用户是否有权限访问,动态为某个对象添加额外的功能。
在字符过滤器中使用动态代理解决中文乱码
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse res = (HttpServletResponse) servletResponse; //解决Post方式 中文乱码问题 req.setCharacterEncoding("UTF-8"); res.setCharacterEncoding("UTF-8"); res.setContentType("text/html;charset=UTF-8"); ServletRequest requestProxy = (ServletRequest) Proxy.newProxyInstance(testFilter.class.getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(req.getMethod().equalsIgnoreCase("get") && method.getName().equalsIgnoreCase("getParameter")){ String value = (String) method.invoke(req,args); if(value == null){ return null; } return new String(value.getBytes("iso8859-1"),"UTF-8"); }else{ return method.invoke(req,args); } } }); filterChain.doFilter(requestProxy,res); }