动态代理模式是对方法的横向增强,它不同于继承,继承通过对父类方法的覆盖,重新对方法进行定义,而横向增强是对方法执行前和执行后的一个增加,不重新定义方法。
代理方式有两种:
jdk代理模式:对接口或实现接口的类进行代理
CGLib代理模式:对类进行代理
jdk动态代理机制中必不可少的类和接口,一个是InvocationHandler(接口)、另一个是Proxy(类)。
必须有一个类实现InvocationHandler接口,从而实现其中的invoke()方法,在invoke()方法中对代理的方法进行增强。我们来看看API文档中对这个类是怎么描述的:
InvocationHandler is the interface implemented by the
invocation handler of a proxy instance.
Each proxy instance has an associated invocation
handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the
invoke method of its invocation handler.
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
我们来看看Proxy类的描述:
Proxy provides static methods for creating dynamic proxy
classes and instances, and it is also the superclass of
all dynamic proxy classes created by those methods.
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代
理对象进行加载,可以是实现类的ClassLoader也可以是接口的ClassLoader
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提
供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该
接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的
时候,会关联到哪一个InvocationHandler对象上
这个方法的作用就是得到一个动态代理的对象,通过这个对象可以调用类中被增强后的方法,实际就是调用h中的invoke()方法。
示例代码:
//接口
package com.zhangyike.proxy;
public interface Person {
public void speak();
public void eat();
}
//接口的实现类,可有可无
package com.zhangyike.proxy;
public class ImpPerson implements Person {
@Override
public void speak() {
System.out.println("实现类中说话");
}
@Override
public void eat() {
System.out.println("实现类中吃");
}
}
//InvocationHander的实现类
package com.zhangyike.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Hander implements InvocationHandler {
Object t;
Hander(Object t){
this.t = t;
}
Hander(){
}
public Object creatImplementsClass(){
//对接口的实现类对方法进行增强,参数将接口、接口的实现类与InvocationHandler对象关联,也就是说调用代理类中的方法就是调用InvocationHandler实现类中的invoke()
Object instance = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), this);
return instance;
}
public Object creatInterfaceClass(Class<?> ifClass){
//只有接口没有接口实现类去得到代理对象。将接口与InvocationHandler对象关联,也就是说调用代理类中的方法就是调用InvocationHandler实现类中的invoke()
Object instance = Proxy.newProxyInstance(ifClass.getClassLoader(), new Class[]{ifClass}, this);
return instance;
}
//调用该方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String name = method.getName();
if (name.equals("speak")) {//对说方法增强
System.out.println("人在说话前");
//t!=null表示有实现接口的类,只提供接口不能调用该方法
if (t != null) {
method.invoke(t, args);
}
System.out.println("人在说话后");
}else{//对吃方法增强
if (t != null) {
method.invoke(t, args);
}
System.out.println(name);
}
return null;
}
}
Demo测试类:
package com.zhangyike.proxy;
public class DemoTest {
public static void main(String[] args) {
test1();
test();
}
public static void test(){
Hander hander = new Hander();
//获取没有接口实现类的对象
Person person = (Person)hander.creatInterfaceClass(Person.class);
System.out.println("对接口增强:");
person.speak();
System.out.println("------------------------------------------------------");
person.eat();
}
public static void test1(){
ImpPerson impPerson = new ImpPerson();
Hander hander = new Hander(impPerson);
//获取没有接口实现类的对象
Person person = (Person) hander.creatImplementsClass();
System.out.println("对实现类增强:");
person.speak();
System.out.println("*******************************************************");
person.eat();
}
}
注意事项:
在测试类的test1()方法中,获取代理类后,将代理对象转换,只能转换为接口类,不能转换为接口的实现类,否则会出现异常:
Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.zhangyike.proxy.ImpPerson
at com.zhangyike.proxy.DemoTest.test1(DemoTest.java:23)
at com.zhangyike.proxy.DemoTest.main(DemoTest.java:5)
这个也很好理解:JDK动态代理必须要有接口,对接口中的方法进行增强,而实现类中有他特有的方法,代理类无法对他进行增强,通过Proxy对象只能得到接口对象,不能得到实现类对象。