说说代理--动态代理
动态代理:
我们都知道,接口是不能被new的,只有类才能被new的。
我们来看看Person接口,和实现了Person接口的Student类,到底有什么区别:
public class Test01 { public static void main(String[] args) { Class<Person> personClass = Person.class; Constructor<?>[] constructors = personClass.getConstructors(); System.out.println("===Person接口的构造器==="); for (Constructor c: constructors) { System.out.println(c); } System.out.println("===Person接口的方法==="); Method[] methods = personClass.getMethods(); for (Method m: methods) { System.out.println(m); } Class<Student> studentClass = Student.class; Constructor<?>[] constructors2 = studentClass.getConstructors(); System.out.println("===实现Person接口的Student类的构造器==="); for (Constructor c: constructors2) { System.out.println(c); } System.out.println("===实现Person接口的Student类的方法==="); Method[] methods2 = studentClass.getMethods(); for (Method m: methods2) { System.out.println(m); } } }
运行结果:
Person接口,没有构造器,所以Person不能被new。
Student类有构造器,所以可以被new。
接口中只有2个方法
实现类有2个方法和父类Object类的一些方法
也就是说除了构造器之外,接口跟实现类的结构差不多。
什么是动态代理:
通过上一篇我们介绍完静态代理(见上文:https://www.cnblogs.com/takeyblogs/p/14035553.html)的优缺点之后,我们来用动态代理来弥补静态代理的缺点。
通过查看API,我们发现Proxy类有一个静态方法:
Proxy.getProxyClass(ClassLoader loader,Class<?>... interfaces):我们传入一个接口的Class对象,就可以给我返回一个代理类。
上代码:
public class Test { public static void main(String[] args) { Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class); System.out.println(proxyClass.getName()); System.out.println("-----打印代理对象的构造器-----"); Constructor<?>[] constructors = proxyClass.getConstructors(); for (Constructor c: constructors) { System.out.println(c); } System.out.println("-----打印代理对象的方法-----"); Method[] methods = proxyClass.getMethods(); for (Method m: methods) { System.out.println(m); } } }
输出结果:
通过Proxy.getProxyClass(),我们可以获得一个增强类,即包含了构造器,也包含了各种方法。与上文的对比,我们多了一个重要的构造器。
简单梳理一下:
1.我们一开始需要直接根据Person接口得到代理对象,但是我们发现了,接口没有构造器,没办法new出对象。
2.我们假象,能不能构造出一个类,即有构造器,又包含Person接口的方法。
3.我们发现java自带的Proxy类中有一个getProxyClass()方法,我们传入Person接口,它可以给我们返回一个带构造器又包含Person接口方法的Class对象。
那我们现在获取到了代理对象了。我们能不能生成代理实例呢?
public class Test { public static void main(String[] args) { try { Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class); System.out.println(proxyClass.getName()); Object o = proxyClass.newInstance(); System.out.println(o); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
结果:
我们发现直接去实例化是会报错的,因为newInstance()调用的是类的无参构造器,但是我们再上面看到的是,代理类的构造器是有参数的:
那我们就给他传入一个参数:
public class Test {
public static void main(String[] args) throws Exception{
Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
//通过反射创建对象
Person PersonProxy = (Person)constructor.newInstance(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
System.out.println(PersonProxy.dance(1,2));
}
}
至此,我们就创建出来了一个代理对象了。
执行上面代码,结果:
发现报了空指针异常。
如果我们修改了invoke方法的返回值:
我们会发现结果返回666。
那么我们不难猜到,结果跟返回值有关系:
我们知道,在实例化代理对象的时候,我们传入一个参数InvocationHandler,我们不难发现,代理对象中肯定有一个属性,来维护这个传入的对象,就像
静态代理一样,我们传入一个目标对象,然后代理对象有一个属性来维护这个目标对象。
为什么要这么设计呢?
为了解耦,为了通用性。
JVM生成代理对象,只生成一个空壳的方法,把具体的逻辑留给InvocationHandler去实现。
API还有另一方法:
public class Test { public static void main(String[] args) throws Exception{ Student student = new Student(); Person proxy = (Person)getProxy(student); proxy.sing(); proxy.dance(2,3); } public static Object getProxy(final Object target) throws Exception{ Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法开始执行"); Object invoke = method.invoke(target, args); System.out.println(invoke); System.out.println(method.getName() + "方法结束"); return invoke; } } ); return proxy; } }
执行结果: