Java笔记--动态代理

Java动态代理

1.概念

代理:
有时我们并不想直接访问对象A,或者不能直接访问对象A。而是通过访问一个中间对象B,让中间对象B去访问A。这种方式就称为代理

这里的对象A所属的类就为委托类,或者被代理类。
对象B所属的类就是代理类。

使用代理访问的优点

1.隐藏了被代理类的实现代码。
2.解耦,不改变被代理类的代码的情况下做一些额外的处理。(强调的是不改变被代理类的代码)。
比如调用SP的时候总是要获取sp,最后还要commit()。或者数据库操作等。在增删改查的前后总有一些相同的冗余代码。

根据程序运行前代理类是否已经存在,可以把代理分成静态代理和动态代理

静态代理

代理类在成勋运行前就已经存在的代理方式称为静态代理。(由开发人员编写或者编译器生成代理类的方式都属于静态代理

静态代理实例:

//被代理的类
class TobeProxyed {
	public void m1();

	public void m2();

	public void m3();

}

//代理类
class Proxy() {
	//被代理类的对象 
	public class TobeProxyed proxyed;

	public Proxy(TobeProxyed proxyed) {
		this.proxyed = proxyed;
	}

	//代理类直接调用被代理类的方法。并且屏蔽了被代理类的//m3方法
	public void proxyMethod1() {
		proxyed.m1();
	}

	public void proxyMethod2() {
		proxyed.m2();
	}

	
	
}

静态代理中代理类和被代理类经常继承同一个父类或者实现同一个接口。

动态代理

代理类在程序运行前不存在,运行时由程序动态生成的代理方式称为动态代理

静态代理没有什么意思。动态代理的方式更灵活多变也是更需要理解的代理方式。

Java提供了动态代理的实现。可以在运行时动态的生成代理类。这样做代理就有一个非常大的好处————可以方便对代理类的函数做统一或者特殊的处理。如记录所有函数执行时间,所有函数执行前添加验证判断,对某个特殊函数进行特殊操作。不用像静态代理那样修改所有的方法。用静态代理只需要改一处invoke方法。

动态代理的实例:

实现动态代理包括三步:

1.要有被代理类的实例
2.写一个实现InvocationHandler接口的类(这个类是连接代理类和被代理类的中间类必须实现的接口)
3.调用Proxy.newProxyInstance()方法创建代理类对象

现在加入我们想要实现统计某个类所有函数的执行时间。传统的静态代理方式是在类的每个函数前后调用统计方法。
假如有100个方法的话,就要把相同的代码写100次。这时候静态代理的方式就很麻烦。

此时如果使用动态代理的方式就会很简单。只需要写一遍统计代码就OK了。

动态代理实例:

被代理类和被代理类接口

//这是被代理类的接口
public interface ProxyedInterface {
	public void proxyedM1();

	public void proxyedM2();

	public void proxyedM3();

}

//这是被代理类
public class ProxyedClassImpl implements ProxyedInterface {
	@Override
	public void proxyedM1() {
		System.out.println("proxyedM1 invoked");	
	}

	public void proxyedM2() {
		System.out.println("proxyedM2 invoked");	
	}

	public void proxyedM3() {
		System.out.println("proxyedM3 invoked");	
	}
}

中间类实现InvocationHandler接口

有了要被代理的类。下面写一个中间类实现InvocationHandler接口的invoke方法。

//中间代理类
public class MiddleProxyClass implements InvocationHandler {

	//被代理类的对象
	private Object proxyedClassImpl
	public MiddleProxyClass(Object proxyedClassImpl) {
		this.proxyedClassImpl = proxyedClassImpl;
	}

	//调用被代理累的相应方法都会到调用这个方法内,在这里可以在方
	//法的前后做相应的处理,但是无法改变被代理类的方法的内部逻辑
	@Override
	public Object (Object proxy, Method method, Object[] args) throws Throwable {
		long start = System.currentTimeMillis();

		Object o = method.invoke(proxyedClassImpl, args)//调用被代理累proxyedClassImpl的指定方法
		System.out.println(method.getName() + " cost time : " + (System.currentTimeMillis()-start))
		return o;
	}
}

通过 Proxy 类静态函数生成代理对象

public class Main {
    public static void main(String[] args) {
        // create proxy instance
        MiddleProxyClass middleProxyClass = new MiddleProxyClass(new ProxyedClassImpl());

		//在这里动态生成实际的代理类对象 proxyedInterface
        ProxyedInterface proxyedInterface = (ProxyedInterface)(Proxy.newProxyInstance(ProxyedInterface.class.getClassLoader(), new Class[] {ProxyedInterface.class},
                middleProxyClass));

        // call method of proxy instance
        proxyedInterface.proxyedM1();
        System.out.println();
        proxyedInterface.proxyedM2();
        System.out.println();
        proxyedInterface.proxyedM3();
    }
}

每次调用Proxy.newProxyInstance生成的代理类对象的方法,都会调用到中间类对象middleProxyClass的invoke方法。而invoke方法实现中会调用被代理类的指定方法。

生成代理类的静态方法: Proxy.newProxyInstance(…)

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

loader表示类加载器

interfaces表示委托类的接口,生成代理类时需要实现这些接口

h是InvocationHandler实现类对象,负责连接代理类和委托类的中间类

我们可以这样理解,如上的动态代理实现实际是双层的静态代理,开发者提供了委托类 B,程序动态生成了代理类 A。开发者还需要提供一个实现了InvocationHandler的子类 C,子类 C 连接代理类 A 和委托类 B,它是代理类 A 的委托类,委托类 B 的代理类。用户直接调用代理类 A 的对象,A 将调用转发给委托类 C,委托类 C 再将调用转发给它的委托类 B。

posted @ 2016-07-22 12:40  zharma  阅读(169)  评论(0编辑  收藏  举报