(一)引入:为什么会有代理!!
原因是:阻止对目标对象的直接访问,或者就是在执行目标对象时,在执行目标对象前后进行一系列的操作。举个例子来说,1:在执行一个方法之前必须要对该“方法”做“日记”,记录每个操作,这样可以在执行目标对象之前,先执行一个的操作,然后执行该方法。2:又如明星唱歌例子,商家首先找到该明星的代理公司,谈妥后再找该明星去唱歌,中间代理起拦截等的操作
(二)代理类别:步步推进,
静态代理:手动添加被代理对象的方法
开发步骤:
1)写一个普通类,继承目标对象的接口
2)写一个实例变量,记住代理谁,即目标对象
3)使用构造方法为实例变量赋值,或者可以直接赋值
4)写一个普通方法,该方法的返回值是接口,该接口是目标对象的实现接口
//Star是接口,LiuDeHuaProxy是代理,LiuDeHua是目标对象。最终目的是为了调用LiuDeHua 的sing方法
// (1) 继承了目标对象的接口
public class LiuDeHuaProxy implements Star {
//实例化了目标对象,是为了提供给别人使用
private LiuDeHua LiuDeHua= new LiuDeHua();
//代理对象不会唱歌方法,实际还是调用了目标对象的唱歌方法
public void sing() {
liyuchun.sing();
}
//基于接口编程:提供的Star。这里也就是在提供目标接口时进行一系列操作。比如可以说if的过滤条件,做日志也可以
public Star getProxy() {
if(钱不够){
system.out.println("钱不够,不能唱歌")
}else{
return LiuDeHua;
}
}
}
这种静态代理的方法不推荐使用,分析一下,如果说,star里面有多少个方法,在代理对象的类中就有多少个方法。不利于扩展。举个例子,如果说在A类是目标对象,B类是代理对象,A类每个方法执行前都需要做”日志“操作,那么代理对象需要扩展A类所有方法,在执行前+上日志的代码。当A类增加方法时,B类仍然要手动添加一个方法做”日志“。所以这里不推荐使用
动态代理:自动生成被代理对象的方法,无须手动生成
开发步骤:
1)写一个普通类,无需任何继承或实现
2)写一个实例变量,记住代理谁,即目标对象
3)使用构造方法为实例变量赋值
4)写一个普通方法,该方法的返回值是接口,该接口是目标对象的实现接口
//代理类
public class LiyuchunProxy{
//代理谁,也可以通过构造方法赋值
private Liyuchun liyuchun = new Liyuchun();
//动态产生代理对象
public Star getProxy(){、
/*这里主要是学习Proxy类。可以去查询一下手册
* Proxy
* static newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 第一个参数是:loader表示动态代理对象由哪个类加载器完成的 ,这里是LiyuchunProxy
* 第二个参数是:interface表示动态代理对象和目标对象有一样的接口的方法。
* 第三个参数是:动态代理对象的拦截方法,即每次都会执行该方法
*/
return (Star) Proxy.newProxyInstance(
LiyuchunProxy.class.getClassLoader(),
liyuchun.getClass().getInterfaces(),
new InvocationHandler(){
/*InvocationHandler 是一个接口,只有一个invoke方法。可以看手册
*
*第一个参数是:动态产生代理对象本身
*第二个参数是:表示方法
*第三个参数是:表示方法参数,是一个数据,从0-n
*/
public Object invoke(
Object proxy,
Method method,
Object[] args) throws Throwable {
String money = (String) args[0];
//拦截sing方法,如果是sing方法,做什么,如果是其他方法做什么
if("sing".equals(method.getName())){
if("3".equals(money)){
//调用春哥的sing()方法
return method.invoke(liyuchun,args);
}else{
System.out.println("不够出场费");
}
}else if("dance".equals(method.getName())){
if("5".equals(money)){
return method.invoke(liyuchun,args);
}else{
System.out.println("不够出场费");
}
}else if("eat".equals(method.getName())){
System.out.println("春哥今天有事,不能来");
}
return null;
}
});
}
}
总结来说:1)推荐使用动态代理的方法。
2)如果说掌握的spring框架的朋友,可以使用spring框架所带的注解的方式去代理,这样会更简单,以后有机会再见
3)学好代理的两个重要的点
//1:代理谁
设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
//2:如何生成代理对象(invoke方法)
设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)
//备注,下面有三个例子,会画图具体分析,耐心看完下面一点点点
(三)案例说明
1. 全站字符编码过滤(request代理)
2. 全站压缩流输出(response代理)
3. 自定义数据库连接池(connection代理)
由于篇幅的原因,这里只能列出第一个案例的源码,其他的一些源码我放在word文档里面,需要的可以执行下载。word里面有配图
分析:
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
MyRequest myRequest = new MyRequest(request);
//放行资源
chain.doFilter(myRequest.getProxy(),response);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//使用动态代理方式产生代理对象 动态代理步骤一:写一个普通的方法,无需要任何的继承
class MyRequest{
//动态代理步骤二:这是一个目标对象实例变量,也就是HttpServletRequest上面分析的
private HttpServletRequest request;
//动态代理步骤三:通过构造方法给实力变量赋值
public MyRequest(HttpServletRequest request){
this.request = request;
}
//写一个普通方法,返回值是接口,提供目标对象的方法
public HttpServletRequest getProxy(){
//三大参数,写法差不多
return (HttpServletRequest) Proxy.newProxyInstance(
MyRequest.class.getClassLoader(),
request.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(
Object proxy,
Method method,
Object[] args) throws Throwable {
if("getParameter".equals(method.getName())){
//取得请求的类型[POST或GET]
String m = request.getMethod();
//如果是GET请求类型
if("get".equalsIgnoreCase(m)){
String value = request.getParameter((String)args[0]);//乱码
byte[] buf = value.getBytes("ISO8859-1");
value = new String(buf,"UTF-8");//正码
return value;
}else if("post".equalsIgnoreCase(m)){
request.setCharacterEncoding("UTF-8");
String value = request.getParameter((String)args[0]);//正码
return value;
}
}else{
//放行
return method.invoke(request,args);
}
return null;
}
});
}
}
其他的代码有必要下载word文档咯
下载:http://download.csdn.net/detail/xiaozhegaa/9778527
(四)总结:细心的人会发现好像代理跟filter过滤有一点的相似。还是有一定的相似之处。可以说filter是大局,而代理是细节。举个例子来说,上述的filter过滤的是LoginServlet,也可以在dochain.filter(request,response)放行之前编码。但是这样有点粗糙,如果request中不执行getParameter方法时也会有编码。而代理是细节,精细到request里面的一个方法,当执行这个方法时才会进行设置编码,否则不执行。可以看出。filter是比较宏观,代理比较细节。所以两者配合使用效果会更好