动态代理
特点:无侵入式的给代码增加额外的功能
在Java中,动态代理是一种在运行时动态生成代理对象的机制,而不需要在编译时确定代理类。它允许在运行时创建一个实现了一组特定接口的代理类,这些代理类可以将方法调用转发到实际对象,并且可以在调用前后添加额外的逻辑。动态代理通常与反射机制结合使用。
Java中实现动态代理的主要类和接口有两个:java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。
-
java.lang.reflect.Proxy
类:Proxy
类是用于创建动态代理类和实例的工具类。Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
方法用于创建代理对象。- 参数
loader
指定了用于加载代理类的类加载器,interfaces
指定了代理类要实现的接口,handler
指定了代理对象的调用处理程序。
-
java.lang.reflect.InvocationHandler
接口:InvocationHandler
是一个函数式接口,它只包含一个方法invoke(Object proxy, Method method, Object[] args)
,在代理对象的方法被调用时会被调用。proxy
参数是代理对象本身,method
参数是被调用的方法对象,args
参数是方法的参数。
使用动态代理的步骤通常如下:
- 定义一个接口,该接口表示要被代理的对象的功能。
- 实现
InvocationHandler
接口,编写代理对象的调用处理逻辑。 - 使用
Proxy.newProxyInstance()
方法创建代理对象。 - 通过代理对象调用方法,代理对象会将方法调用转发给
InvocationHandler
的invoke()
方法进行处理。
动态代理常用于以下情况:
- AOP(面向切面编程):在方法执行前后添加日志、性能监控等功能。
- RPC(远程过程调用):通过动态代理实现远程接口的调用。
- 事件监听器:动态生成事件监听器的代理对象来处理事件。
总的来说,动态代理使得编程更加灵活,可以在运行时动态地创建代理对象,并且可以在方法调用前后添加额外的逻辑,从而实现一些高级功能。
1.Java提供了什么API帮我们创建代理?
Java提供了java.lang.reflect.Proxy
类来帮助创建代理对象。这个类提供了一个静态方法newProxyInstance()
,可以用来创建动态代理对象。具体来说,Proxy.newProxyInstance()
方法允许你传入一个类加载器(ClassLoader)、一组接口(Interface)以及一个InvocationHandler
对象,然后它会返回一个实现了指定接口的代理对象。
这个方法的签名如下:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
loader
参数是用来加载代理类的类加载器。interfaces
参数是一个接口数组,指定了代理类要实现的接口。handler
参数是一个InvocationHandler
对象,用于处理代理对象的方法调用。
通过Proxy.newProxyInstance()
方法创建的代理对象会实现指定的接口,并且会将方法调用转发给传入的InvocationHandler
对象的invoke()
方法进行处理。
2.newlProxylnstance方法在创建代理时,需要接几个参数,每个参数的含义是什么?
Proxy.newProxyInstance()
方法在创建代理时需要接收三个参数,每个参数的含义如下:
-
ClassLoader loader
:用于加载代理类的类加载器。这个参数指定了在运行时动态生成代理类的类加载器。通常情况下,你可以传入你的类的类加载器,例如YourClass.class.getClassLoader()
,或者传入系统类加载器,例如ClassLoader.getSystemClassLoader()
。 -
Class<?>[] interfaces
:一个接口数组,指定了代理类要实现的接口。代理类会实现这些接口中的方法,并将方法调用转发给InvocationHandler
对象进行处理。 -
InvocationHandler handler
:一个InvocationHandler
对象,用于处理代理对象的方法调用。当代理对象的方法被调用时,会调用InvocationHandler
对象的invoke()
方法进行处理。在invoke()
方法中,你可以添加额外的逻辑,比如日志记录、权限检查等。
3.通过invokehandler的invoke方法指定代理干的事时,这个invoke会被谁调用? 要接哪几个参数?
InvocationHandler
接口中的 invoke()
方法在代理对象的方法被调用时会被代理对象自动调用。这个方法会接收以下几个参数:
-
Object proxy
:代理对象本身。在invoke()
方法中,你可以通过这个参数来调用被代理对象的方法。 -
Method method
:被调用的方法对象。通过这个参数,你可以获取方法的名称、参数类型等信息。 -
Object[] args
:方法的参数数组。通过这个参数,你可以获取方法的参数值。
在 invoke()
方法中,你可以根据需要处理代理对象的方法调用,比如记录日志、添加权限检查等,然后决定是否调用被代理对象的方法。最后,你需要返回方法的执行结果(如果有)给调用者。
学习代码:
测试类
public class Test {public static void main(String[] args) {//获取代理的对象BigStar bigStar = new BigStar("蔡徐坤");Star proxy = ProxyUtil.createProxy(bigStar);//再调用代理的唱歌方法String name = proxy.sing("只因你太美");System.out.println(name);//调用跳舞的方法proxy.dance();}
}
明星类
public class BigStar implements Star{private String name;//唱歌 跳舞public String sing(String name){System.out.println(this.name+"正在唱"+name);return "谢谢";}public void dance(){System.out.println(this.name+"正在跳舞");}public BigStar(String name) {this.name = name;}public BigStar() {}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "BigStar{" +"name='" + name + '\'' +'}';}
}
明星接口类
public interface Star {public abstract String sing(String name);public abstract void dance();
}
代理类
//类的作用:创建一个代理
public class ProxyUtil {/* 方法的作用:给一个明星的对象,创建一个代理形参:被代理的明星对象返回值:给明星创建的代理*/public static Star createProxy(BigStar bigStar){Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), //参数loader指定了用于加载代理类的类加载器new Class[]{Star.class}, //interfaces指定了代理类要实现的接口new InvocationHandler() { //handler指定了代理对象的调用处理程序@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("sing".equals(method.getName())){System.out.println("准备唱歌,收钱");} else if ("dance".equals(method.getName())) {System.out.println("准备跳舞,收钱");}return method.invoke(bigStar,args);}});return star;}
}
(代码仅供参考)