Java培训、Android培训/iOS培训–静态代理1

——Java培训、Android培训、iOS培训、.Net培训,期待与您交流! ——-

一、静态代理

1、生活中的代理:

四川人从成都代理商那里购买联想电脑,直接到北京传智播客去联想总部购买电脑。您认为最终的主要业务目标有什么不同吗?基本一样,都解决了核心问题动态代理中 匿名内部类,但是没有区别吗?从代理那里购买真的没有好处吗?虽然贵了几百元,但不用开车去北京,省时又省钱。

2、程序中的代理:

需求:在同一个接口的多个目标类的现有方法中添加一些系统功能,如异常处理、日志记录、计算方法的运行时间、事务管理等,你打算怎么做?

解决方案:编写一个与目标类具有相同接口的代理类动态代理中 匿名内部类,代理类的每个方法调用目标类的相同方法,并在调用方法时添加系统函数代码。

如果使用工厂模式和配置文件进行管理,无需修改客户端程序,在配置文件中配置使用目标类还是代理类,方便切换以后,比如想要日志功能的时候就配置代理类,否则配置目标类。这样既方便添加系统功能,也方便运行一段时间后删除系统功能。

代码示例:

[java] view plaincopy
package cn.itcast.proxy;  
public class Test {   
    public static void main(String[] args) {  
        Subject subject = new ProxyRealSubject();  
        subject.action();  
    }  
}  
//接口  
interface Subject {  
    void action();  
}  
//被代理类  
class RealSubject implements Subject {  
    @Override  
    public void action() {  
        System.out.println("------------");  
        System.out.println("------------");  
        System.out.println("这是被代理的类");  
        System.out.println("------------");  
        System.out.println("------------");  
    }  
}  
//代理类:  
class ProxyRealSubject implements Subject{  
    Subject subject;  
    public ProxyRealSubject() {  
        System.out.println("这是代理类");  
        subject = new RealSubject();  
    }  
    @Override  
    public void action() {  
        System.out.println("代理类开始");  
        subject.action();  
        System.out.println("代理类结束");  
    }  
}  

面向方面的编程:

1.系统中有交叉服务,交叉服务是切入系统的一个切面

2.跨业务的编程问题是面向方面的程序(AOP),AOP的目标是使跨业务模块化。可以在原始方法周围移动切面代码,这与直接在方法中编写切面代码相同。

3.使用代理技术正好可以解决这个问题。 Proxy是实现AOP功能的核心和关键技术。

二、动态代理:

知名框架spring中的核心技术aop采用动态代理技术。

出现原因:给系统中的各种接口类添加代理功能,需要的代理类太多了,所有的静态代理方法都会是一件很麻烦的事情!写成百上千个代理类是不是太累了!

概念:JVM可以在运行时动态生成类的字节码。这个动态生成的类经常被用作代理类,即动态代理类。

1、JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能作为具有相同接口的目标类的代理。

2、CGLIB库可以动态生成类的子类,类的子类也可以作为类的代理,所以如果要为类生成动态代理类不实现一个接口,你可以使用CGLIB库。

3、代理类的各个方法中,除了调用目标的对应方法并返回目标返回的结果外,还可以在代理方法中的以下四个位置添加系统功能代码:

1.调用目标方法之前

2.调用目标方法后

3.调用目标方法前后

4.在处理目标方法异常的catch块中

创建动态类的实例对象的步骤:

1、使用反射获取构造函数

2、编写一个简单的 InvocationHandler 类

3、调用构造函数创建动态类的实例对象,并传入其中写的InvocationHandler类的实例对象

4、打印创建的对象并调用该对象的无返回值方法和getClass方法,并演示调用其他有返回值的方法报异常。

5、将创建动态类的实例对象的代理改为匿名内部类,训练大家习惯匿名内部类。

总结思考:创建动态类及其实例对象需要提供哪些信息给jvm?

三个方面:

1、生成的类中有哪些方法,它实现了哪些接口来告知;

2、生成的类字节码必须有关联的类加载器对象;

3、生成类中方法的代码也是我们提供的。把我们的代码写在一个约定好的接口对象的方法里,把对象传给它,它就调用我的方法,相当于插入我的代码。提供执行代码的对象是InvocationHandler对象,在创建动态类的实例对象的构造函数时传入。在上面InvocationHandler对象的invoke方法中添加一点代码,可以看到这些代码被调用并运行了。

题目:为ArrayList类编写一个代理,实现与ArrayList完全相同的功能,并且可以计算每个方法的运行时间。

package com.itheima;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class Test4 {
    /**
     * 4、 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能, 并可以计算每个方法运行的时间。
     * 
     * 思路: 1.写一个获得代理类的通用方法: public static Object getProxy(final Object target,
     * final Advice advice){} target是被代理的类,Advice是需要插入系统中的功能接口。
     * 
     * 2.定义一个接口Advice,为插入的系统功能定义规则interface Advice{}
     * 
     * 3. 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。 class MyAdvice implements
     * Advice{实现方法}
     * 
     * @author 鲁中需
     */
    public static void main(String[] args) {
        // 获取ArrayList的代理
        final ArrayList list = new ArrayList();
        Collection proxy = (Collection) getProxy(list, new MyAdvice());
        // 演示效果:
        proxy.add("flx");
        proxy.add("bxd");
        proxy.add("zxx");
        proxy.add("lxz");
        System.out.println(list.size());
    }
    // 获得代理类的通用方法:
    public static Object getProxy(final Object target, final Advice advice) {
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在调用方法前,调用beforeMethod(Method method)
                advice.beforeMethod(method);
                Thread.sleep(1000);
                Object retVal = method.invoke(target, args);
                // 在调用方法后,调用afterMethod(Method method)
                advice.afterMethod(method);
                return retVal;
            }
        });
        return proxy;
    }
}
// 定义一个接口Advice,为插入的系统功能定义规则
interface Advice {
    public abstract void beforeMethod(Method method);
    public abstract void afterMethod(Method method);
}
// 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。
class MyAdvice implements Advice {
    long beginTime = 0;
    @Override
    public void beforeMethod(Method method) {
        System.out.println("调用方法前的功能");
        beginTime = System.currentTimeMillis();
    }
    @Override
    public void afterMethod(Method method) {
        System.out.println("调用方法后的功能");
        long costTime = System.currentTimeMillis() - beginTime;
        System.out.println("方法:" + method + " running time of " + costTime + "毫秒!");
    }
}

1、动态生成的类实现了Collection接口(可以实现几个接口)。生成的类具有Collection接口中的所有方法和一个接受InvocationHandler参数的构造函数,如下所示。

2、构造函数接受一个 InvocationHandler 对象。接受对象的目的是什么?这个方法里面的代码是什么样子的?

动态类中实现Collection接口的各个方法的代码是什么? InvocationHandler接口中定义的invoke方法接受的三个参数是什么意思?

3.为什么分析之前打印的动态类的实例对象时结果为null?为什么在调用具有原始返回值的方法时会收到 NullPointerException?

分析为什么动态类的实例对象的getClass()方法返回正确的结果?

调用继承自Object类的代理对象的hashCode、equals、toString方法时,代理对象将调用请求转发给InvocationHandler对象。其他方法不转发调用请求。

© 版权声明
THE END
喜欢就支持一下吧
点赞70赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容