Spring AOP的实现——动态代理机制

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

在java的动态代理中,有两个重要的类或者接口,一个是InvocationHandler(Interface)、另一个是Proxy(Class),这一个类和接口是实现动态代理所必须的。

InvocationHandler:每一个动态代理类都必须实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。

我们来看看invoke这个方法:

<span style="font-size:14px;">Object invoke(Object proxy,Method method,Object[] args)</span>
proxy:指代我们所代理的真实对象

method:指代的是我们所要调用真实对象的某个方法的Method对象

args:指代的是调用真实对象某个方法时接收的参数

Proxy:这个类的作用就是用来动态的创建一个代理对象的类,其接受三个参数

<span style="font-size:14px;">public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException</span>
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成的代理对象进行加载

interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。

h:一个InvocationHandler对象,表示的是当我这个动态代理在调用方法的时候,会关联到哪一个InvocationHandler对象上


下面我们通过实例看看动态代理模式是什么:

首先,定义一个Subject接口,并且定义两个方法:

<span style="font-size:14px;">public interface Subject {

	public void rent();
	public void hello(String str);
}
</span>
接着,定义一个类来实现上面的接口,这个类就是我们的真实对象:

<span style="font-size:14px;">public class RealSubject implements Subject{

	@Override
	public void rent() {
		System.out.println("I want rent my house");
	}

	@Override
	public void hello(String str) {
		System.out.println("hello:"+str);
	}

}
</span>
下一步,定义动态代理类:

<span style="font-size:14px;">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class Dynamicproxy implements InvocationHandler{
	
	//这就是我们代理的真实对象
	private Object subject;
	
	//构造方法,给我们要代理的真实对象赋值
	public Dynamicproxy(Object subject){
		this.subject = subject;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//在代理真实对象前我们可以添加一些自己的操作
		System.out.println("before rent house");
		System.out.println("Method:"+method);
		
		//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
		method.invoke(subject, args);
		
		//在代理真实对象后我们可以添加一些自己的操作
		System.out.println("after rent house");
		return null;
	}

}</span>
最后,是Client类:

<span style="font-size:14px;">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {

	public static void main(String[] args) {
	
		//我们要代理的真实对象
		Subject realSubject = new RealSubject();
		
		//我们要代理哪个真实对象,就将该对象传进去,最后通过该真实的对象调用该方法
		InvocationHandler handler = new Dynamicproxy(realSubject);
		
		Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
		System.out.println(subject.getClass().getName());
		subject.rent();
		subject.hello("world");
	}
}
</span>
控制台输出如下:

com.sun.proxy.$Proxy0
before rent house
Method:public abstract void com.by.proxyTest.Subject.rent()
I want rent my house
after rent house
before rent house
Method:public abstract void com.by.proxyTest.Subject.hello(java.lang.String)
hello:world
after rent house

首先,我们看看这条语句:

<span style="font-size:14px;">Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); 
</span>
为什么我们这里可以将其转化为Subject类型的对象?

原因在于newProxyInstance这个方法的第二个参数上,我们给代理对象提供什么接口,那么我们这个代理对象就会实现这个接口,这个时候我们可以将这个代理对象强制转换成Subject类型。

使用Proxy.newProxyInstance()创建的代理对象是在JVM上运行时动态产生的一个对象,它并不是InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行时动态产生的一个对象。并且命名方式是以$开头的,proxy中最后一个数字表示对象的标号。

subject.rent();
subject.hello("world");
这里是通过对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的handler中的invoke方法去执行,而我们这个handler对象又接受了一个RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用handler中的invoke方法去执行:

<span style="font-size:14px;">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class Dynamicproxy implements InvocationHandler{
	
	//这就是我们代理的真实对象
	private Object subject;
	
	//构造方法,给我们要代理的真实对象赋值
	public Dynamicproxy(Object subject){
		this.subject = subject;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//在代理真实对象前我们可以添加一些自己的操作
		System.out.println("before rent house");
		System.out.println("Method:"+method);
		
		//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用(通过烦反射执行某个类的某方法)
		method.invoke(subject, args);
		
		//在代理真实对象后我们可以添加一些自己的操作
		System.out.println("after rent house");
		return null;
	}

}
</span>
我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们这个method对象是这样:

<span style="font-family:Microsoft YaHei;font-size:14px;">public abstact void com.xiaoluo.dynamicproxy.Subject.rent()
public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)</span>
正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的handler对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用。

动态代理的作用:

主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke这个方法中,你可以直接获取正在调用方法对应的Method对象,具体应用,比如,添加日志,做事物控制等。












 


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