动态代理以及AOP代理

1.使用Proxy和InvocationHandler创建动态代理

    Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。这里主要讲解创建动态代理实例。

      使用Proxy和InvocationHandler创建动态代理,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。因此可以直接使用static  Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler  h)。

因此创建一个动态代理对象的步骤如下:

InvocationHandler handler = new MyInvocationHandler(…);

Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[]{Foo.class},handler);

然后就可以通过动态代理对象调用接口的方法了,而动态代理对象的所有方法都会被替换成调用该invoke()方法。

Object invoke(Object proxy,Method method,Object[] args)

proxy:代表动态代理对象

method:代表正在执行的方法

args:代表调用目标方法时传入的参数

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Person{
	void work();
	void sayHello(String name,int age);
}
interface Foo{
	void doWork(String str);
}

class MyInvokationHandler implements InvocationHandler{

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("----正在执行的方法:"+method);
		if(args!=null) {
			System.out.println("下面是执行该方法时传入的实参为:");
			for(Object val : args) {
				System.out.println(val);
			}
		}
		else {
			System.out.println("调用该方法没有实参!");
		}
		return null;
	}
	
}

public class ProxyTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InvocationHandler handler = new MyInvokationHandler();
		Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] {Person.class,Foo.class}, handler);
		p.work();
		p.sayHello("孙悟空",500);
		

	}

}

    通过以上例子比较难理解代理的精髓,接下来讲的是框架中常用到的AOP,这也是一种动态代理,接下来通过例子来讲解它的实现过程,就可以体会AOP带来的好处。

2.动态代理和AOP

       AOP中使用Proxy生成一个动态代理时,往往不会凭空产生一个代理,而是为指定的目标对象生成动态代理。AOP代理可代替目标对象,包含了目标对象的全部方法。

       先创建一个Animal接口,因为Proxy和InvocationHandler创建的JDK动态代理只能为接口创建动态代理。

package proxy;

public interface Animal {
	void info() throws InterruptedException;
	void run() throws InterruptedException;

}

        接下来提供两个实现类:

package proxy;

public class GunDog implements Animal {

	@Override
	public void info() throws InterruptedException {
		// TODO Auto-generated method stub
		System.out.println("我是一只猎狗");
		Thread.sleep(1000);

	}

	@Override
	public void run() throws InterruptedException {
		// TODO Auto-generated method stub
		System.out.println("我奔跑迅速");
		Thread.sleep(1000);

	}


}
package proxy;

public class Cat implements Animal {

	@Override
	public void info() throws InterruptedException {
		// TODO Auto-generated method stub
		System.out.println("我是一只花猫");
		Thread.sleep(1000);

	}

	@Override
	public void run() throws InterruptedException {
		// TODO Auto-generated method stub
		System.out.println("我反应敏捷");
		Thread.sleep(1000);

	}

}

      接下来是拦截器方法,是执行对象的每个方法前后调用的拦截器方法。该方法为了获取对象方法执行前后的时间。

import java.util.Calendar;

public class AnimalUtil {
	//第一个拦截器方法
	public void method1() {
		Calendar c = Calendar.getInstance();
		System.out.println("执行当前方法前的时间是:"+c.get(c.YEAR)+"年"+(c.get(c.MONTH)+1)+"月"+c.get(c.DATE)+"日"+c.get(c.HOUR_OF_DAY)+"时"+c.get(c.MINUTE)+"分"+c.get(c.SECOND)+"秒");
	}
	
	//第二个拦截器方法
	public void method2() {
		Calendar c = Calendar.getInstance();
		System.out.println("执行当前方法后的时间是:"+c.get(c.YEAR)+"年"+(c.get(c.MONTH)+1)+"月"+c.get(c.DATE)+"日"+c.get(c.HOUR_OF_DAY)+"时"+c.get(c.MINUTE)+"分"+c.get(c.SECOND)+"秒");
	}
	
}

为了避免以硬编码的方式将拦截器方法插入到代理对象的方法中。可以使用Proxy和InvocationHandler来实现将拦截器方法自动插入到对象的方法序列中执行。即实现InvocationHandler接口,并实现invoke()方法。利用执行动态代理对象的所有方法时,都会被替换成执行invoke()方法,可以将需要插入的拦截器逻方法与代理对象方法的关系在invoke()方法中实现。

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
	public Object target;
	public void setTarget(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		AnimalUtil au = new AnimalUtil();
		
		au.method1();
		//以target作为主调来执行method方法
		Object result = method.invoke(target, args);
		au.method2();
		return result;
	}

}

    上面invoke()方法中Object result = method.invoke(target,args);则通过反射的方式以target作为主调来执行method方法,即回调了target对象的原有方法。

接下来创建一个工厂类,该对象专为指定的target生成动态代理实例。

import java.lang.reflect.Proxy;
//使用工厂类也可以复用代理类创建过程。
public class MyProxyFactory {
	public static Object getProxy(Object target) {
		MyInvocationHandler handler = new MyInvocationHandler();
		//为MyInvocationHandler设置target对象
		handler.setTarget(target);
		//创建并返回一个动态代理
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
	}

}

接下来进行测试:

import java.util.Calendar;

public class Test {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Animal target = new GunDog();
		Animal dog = (Animal)MyProxyFactory.getProxy(target);
		dog.info();
		dog.run();
		Animal target1= new Cat();
		Animal cat = (Animal)MyProxyFactory.getProxy(target1);
		cat.info();
		cat.run();
		
	}

}

结果如下:

执行当前方法前的时间是:2018年8月21日16时57分8秒
我是一只猎狗
执行当前方法后的时间是:2018年8月21日16时57分9秒
执行当前方法前的时间是:2018年8月21日16时57分9秒
我奔跑迅速
执行当前方法后的时间是:2018年8月21日16时57分10秒
执行当前方法前的时间是:2018年8月21日16时57分10秒
我是一只花猫
执行当前方法后的时间是:2018年8月21日16时57分11秒
执行当前方法前的时间是:2018年8月21日16时57分11秒
我反应敏捷
执行当前方法后的时间是:2018年8月21日16时57分12秒

        当动态代理对象代替target对象时,方法被替换成invoke()方法后,可以在invoke()方法实现更复杂的拦截逻辑,使得不需要用硬编码的形式为target对象ch插入拦截方法。

你可能感兴趣的:(设计模式,动态代理)