JavaWeb -----代理 Proxy

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaozhegaa/article/details/61616424

(一)引入:为什么会有代理!!
原因是:阻止对目标对象的直接访问,或者就是在执行目标对象时,在执行目标对象前后进行一系列的操作。举个例子来说,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是比较宏观,代理比较细节。所以两者配合使用效果会更好

阅读更多
换一批

没有更多推荐了,返回首页