代理&动态代理

  代理是学习Spring的一个重要基础,今天就来探讨一下这个技术。

理解代理

  代理这个词对于广告满天飞的现代社会应该是很常见了,和它具有相同意义还有中介、经纪人等词。我们就以找影视明星拍片为例来理解代理。
  假如你有一个很好的剧本,现在想找A明星来做主角,可以两种方式:第一种是直接找A明星本人,第二种是找A明星的经纪人。但是明星的主要功能是拍戏,如果诸如报酬、档期、宣传等工作也让A明星来负责,势必会减少他/她的拍戏时间,所以更好的方式是去寻求他/她的经纪人,让经纪人来做这些拍戏以外的工作
  但同时经济人也得有和明星A一样的功能,比如A会演戏、唱歌、跳舞,那么经纪人也得有这些功能,只不过经纪人的功能是让明星A去完成的,经纪人本身只提供这种服务的接口。而我们所说的明星A便是目标对象,经纪人便是代理对象。
  这个和我们的代理有很多相似的地方,假如有一个方法(设为M1)的功能是把UTF-8编码下的字符转化成GBK编码下的字符。那么这个方法的功能就是进行转化、对于判断传入的是不是UTF-8编码下的字符这种事情应该传入之前就处理完成,所以这时候就要有一个代理方法(设为M2)在M1执行之前做个处理。当然M1执行完毕之后M2也可能做一些处理。

代理对象的要点

1.代理对象存在的价值主要用于拦截对目标对象的访问。
2.代理对象应该具有和目标对象相同的方法。

动态代理

  在我们刚才的解释中,每个目标对象都要有一个实在的代理对象,但如果说能在程序运行期间给我们动态生成一个代理对象可以大大减小编写的代码的压力。所以动态代理的概念就是:不用手动编写一个代理对象,不需要编写与目标对象相同的方法,运行时在内存中 动态生成代理对象(字节码对象级别的代理对象)。

JDK提供的动态代理

  基于万物皆对象的思想,JDK1.5之后为我们提供了用于专用于动态生成代理对象的类:java.lang.reflect.Proxy。有一个很重要的静态方法:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHander h)

  在解释参数之前,我们先看一个接口InvocationHander,JDK对它的解释是:Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the method of its invocation handler.(每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,将方法调用编码并调度到其调用处理程序的方法。) 它只有一个invoke()方法。我们之后执行时真正起作用的也是这个方法。

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

  对newProxyInstance()方法参数的解释在下面的例子中。

  • 明星接口
public interface BrightStar {
    public void sing();
    public String dancing();
    public String ShootFilm(String filmName);
}
  • A明星类
public class StarA implements BrightStar{
    @Override
    public void sing() {
        System.out.println("A is singing...");
    }
    @Override
    public String dancing() {
        return "Hai cao wu";
    }
    @Override
    public String ShootFilm(String filmName) {
        return filmName;
    }
}
  • 测试动态代理
public class Test {
    public static void main(String[] args) {
        StarA starA = new StarA();
        BrightStar newProxyInstance = (BrightStar)Proxy.newProxyInstance(
                //代理类的类加载器,获取目标类加载器即可
                StarA.class.getClassLoader(), 
                //代理类应该实现的接口,由于代理类和目标类需要继承相同的接口,使用目标类的接口即可
                StarA.class.getInterfaces(), 
                //使用匿名内部类传入InvocationHandler的实例
                new InvocationHandler() {
                    /*
                     * proxy:传入代理对象。
                     * method:被执行的方法。
                     * args:传入的参数。
                     * 例子:  newProxyInstance.ShootFilm("我不是药神");
                     *      proxy:newProxyInstance;      method:ShootFilm;       args:"我不是药神"
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //执行前的操作
                        System.out.println("before");
                        Object invoke = method.invoke(starA, args);
                        //执行后的操作
                        System.out.println("after");
                        return invoke;
                    }
                });
        
        //调用方法
        newProxyInstance.sing();
        System.out.println("------------------------------------");
        String dancing = newProxyInstance.dancing();
        System.out.println(dancing);
        System.out.println("------------------------------------");
        String film = newProxyInstance.ShootFilm("我不是药神");
        System.out.println(film);
        /*
            Console : 
                before
                A is singing...
                after
                ------------------------------------
                before
                after
                Hai cao wu
                ------------------------------------
                before
                after
                我不是药神
         */
    }
}

  下面介绍一个经典案例,使用动态代理解决Web工程的全局编码问题。

  • 前端代码
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert_Title_Here</title>
    </head>
    <style>
        
    </style>
    <body>
        <!--主体部分-->
        <h2>get方式</h2>
        <form action="/ProxySolveCoding/test" method="get">
            <input name="name" type="text"/>
            <input type="submit" value="提交"/>
        </form>
        <h2>post方式</h2>
        <form action="/ProxySolveCoding/test" method="post">
            <input name="name" type="text"/>
            <input type="submit" value="提交"/>
        </form>
    </body>
</html>
  • Servlet
@WebServlet("/test")
public class Test extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        System.out.println(name);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • filter
@WebFilter(urlPatterns="/*")
public class FilterCoding implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException { }
    public void destroy() { }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest)request;
        HttpServletRequest proReq = (HttpServletRequest)Proxy.newProxyInstance(
                req.getClass().getClassLoader(),
                req.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if(method.getName().equalsIgnoreCase("getParameter")) {
                            String gm = req.getMethod();
                            if(gm.equalsIgnoreCase("get")) {
                                //处理get方式的请求
                                String before = (String) method.invoke(req, args);
                                System.out.println(before);
                                String after = new String(before.getBytes("iso-8859-1"), "utf-8");
                                return after;
                            }else {
                                //处理post形式的请求
                                req.setCharacterEncoding("utf-8");
                            }
                        }
                        return method.invoke(req, args);
                    }
                });
        chain.doFilter(proReq, response);
    }
}

  但是小码农这个代码在自己电脑是有问题的。在我电脑上的URL默认编码是UTF-8,因为我之前调过,大家使用应该是没有问题的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,796评论 6 342
  • 简介 brew 又叫Homebrew,是Mac OSX上的软件包管理工具,能在Mac中方便的安装软件或者卸载软件,...
    youngyunxing阅读 3,133评论 0 2
  • isdigit判断字符串是否全由数字组成 isalnum判断字符串是否全由数字和字母组成
    顾慎为阅读 243评论 0 1
  • 最近这几天,我们娘俩共同商议,贴画得分制度。女儿表现还不错。 茹茹前段时间一直迷恋...
    巾茹妈妈阅读 137评论 0 0