关于JAVA的反射机制,动态代理是一大重要的功能,今天主要就来说一说动态代理。提到代理,想必大家都不陌生,简单的理解就是代理充当着代替办某件事的一个角色,既然有动态代理,那就有静态代理,先看看一个比较简单的静态代理:
假设我们有一个名为Consumer消费者的接口,其中包含购买饮料食物buyDrink和购买食物buyFood的方法:
public interface Consumer {
public void buyFood();
public void buyDrink();
}
这时有一个消费者A需要购买进口食品和饮料:
public class ConsumerA implements Consumer {
@Override
public void buyFood() {
System.out.println("买进口牛肉");
}
@Override
public void buyDrink() {
System.out.println("买进口果汁");
}
}
购买进口食品需要通过代购,假设有一个消费者A的代理类:
public class ConsumerAProxy implements Consumer {
private ConsumerA consumer;
public ConsumerAProxy(){
consumer=new ConsumerA();
}
@Override
public void buyFood() {
System.out.println("-----开始代理-----");
consumer.buyFood();
System.out.println("-----结束代理-----");
}
@Override
public void buyDrink() {
System.out.println("-----开始代理-----");
consumer.buyDrink();
System.out.println("-----结束代理-----");
}
}
接下来写一个测试类,运行即可:
public static void main(String[] args) {
ConsumerAProxy cap=new ConsumerAProxy();
cap.buyFood();
cap.buyDrink();
}
运行结果:
这是一个简单的静态代理,在此过程中就会出现一个问题:
假设现有一个消费者B,需要购买其他进口产品,比如进口牛奶等等,那么如果按照这种模式,我们需要再次创建一个新的消费者B类实现消费者接口,并且重写方法,由于购买的东西不同,代理对象(即代购类)也需要重新编写一个,那么如果有多个消费者,需求也不同,就得编写多套不同的类,这样很明显是麻烦的。那如果我们能将代理对象写成一个动态的,即能根据消费者的需求完成相应的代购,就可以解决这个问题了,这就是动态代理,通过反射可以实现。
首先编写一个动态代理类:
public class DynamicProxy implements InvocationHandler
要想实现动态代理,该类必须继承InvocationHandler接口,而该接口中只有一个方法invoke,后面我们慢慢解释该方法。
既然是代理,那么我们需要有一个代理的目标对象,即我们想要对哪一名消费者进行代理,由于该代理是动态的,不确定,我们用Object类表示:
//目标对象
private Object target;
接下来是生成相应的代理对象,编写一个返回值为object类,参数为目标对象的方法(目标对象可变,生成的代理对象也不确定,就用Object类表示):
public Object newProxyInstance(Object target)
将传入的目标对象target赋给本类的目标对象target,这样就能确定为谁代理:
this.target=target;
下一步返回代理类Proxy中newProxyInstance()方法产生的实例:
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
我们可以先看看该方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
该方法包含三个参数:
其中第一个参数代表目标对象的类加载器,即我们想指定生成哪个类的实例
而第二个参数代表目标对象实现的接口,这里我们可以看之前的静态代理:
比如消费者A的代理实现了消费者接口,这一步实际上是确定了该对象会包含哪些方法
第三个参数代表实现了InvocationHandler接口的实例(通常就是当前类),目的是为了执行接口中的invoke()方法。
这样一个生成代理对象的方法就完成了,接下来我们看重写的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
同样该方法需要三个参数:
第一个参数代表我们传入的代理对象,第二个参数代表我们要代理执行的方法,第三个则是执行方法需要的参数。
接下来对方法进行执行:
method.invoke(target,args);
invoke()方法在反射时已经说过,第一个参数代表指定调用哪个实例的方法,第二个参数代表执行该方法时的参数,该方法返回值为Object:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----代理开始------");
Object object= method.invoke(target,args);
System.out.println("-----代理结束------");
return object;
}
这样就完成了一个简单的动态代理,在执行时我们只需这样写即可:
DynamicProxy dp=new DynamicProxy();
Consumer consumer= (Consumer) dp.newProxyInstance(new ConsumerA());
consumer.buyDrink();
consumer.buyFood();
需要对哪个对象进行代理,我们直接创建代理对象实例的方法中传入想要代理的对象即可,而在上述
invoke方法中我们也能发现:
实际上这三步除了中间的invoke方法中的参数不确定外,上下两行打印语句都是一成不变的,这也是AOP的一次体现