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带来的好处。
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插入拦截方法。