说说代理--动态代理
动态代理:
我们都知道,接口是不能被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;
}
}
执行结果:


浙公网安备 33010602011771号