静态代理
创建接口
创建被代理类
实现接口
创建代理类
也要实现同样的接口,同时还要持有被代理对象的引用
代理类发挥作用
代理类执行方法(其实真正执行的核心方法是被代理类的方法)
interface Print{//接口
public void print();
}
class Printer implements Print{//被代理类
@Override
public void print() {
System.out.println("打印中。。。");
}
}
class ProxyPrinter implements Print{//代理类
private Printer printer;//要得到被代理类的引用
ProxyPrinter(Printer printer) {
this.printer = printer;
}
@Override
public void print() {
System.out.println("打印前。。。");
printer.print();//调用被代理类的方法
System.out.println("打印后。。。");
}
}
public class Main {
public static void main(String[] args){
Print p = new ProxyPrinter(new Printer());//这里用了向上转型
p.print();
}
}
打印前。。。
打印中。。。
打印后。。。
动态代理
和静态代理相比,创建接口、创建真实的被代理类都不变。
- 创建接口
- 创建被代理类
实现接口
- 创建一个实现InvocationHandler接口的类(为了好记,我叫它拦截类,可以拦截被代理对象的方法)
跟静态代理里面代理类一样,要获得被代理类对象的引用(但不是代理类)
还必须实现invoke()方法,这里面可以对被代理类的方法进行增强
- 如何运用?
创建拦截类对象,传入真实的被代理类对象
由Proxy类的静态方法_newProxyInstance(3个参数)获得真正的代理对象,并向上转型为接口类型_
代理类对象执行方法
interface Print{//接口
public void print();
}
class Printer implements Print{//被代理类
@Override
public void print() {
System.out.println("打印中。。。");
}
}
class ProxyInvocationHandler implements InvocationHandler{
/**
* 被代理的引用
* 这里用Printer printer也可以运行,但没有用object有意义
* 这样更灵活,才能体现动态代理的动态
*/
private Object object;
ProxyInvocationHandler(Object object) {
this.object = object;
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打印前。。。");
Object result = method.invoke(object,args);
System.out.println("打印后。。。");
return result;
}
}
public class Main {
public static void main(String[] args){
ProxyInvocationHandler pih = new ProxyInvocationHandler(new Printer());
Print printer = (Print)Proxy.newProxyInstance(
Printer.class.getClassLoader(),Printer.class.getInterfaces(),pih);
printer.print();
}
}
打印前。。。
打印中。。。
打印后。。。
对于动态代理,重点要理解下面2个方法:
🔨Proxy.newProxyInstance(3个参数)方法
此方法用于生成真正的代理对象
static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
3个参数:
- loader 被代理类(目标对象)的类加载器
- interface 目标对象实现的接口
- h “拦截类”对象
注意返回的真正的代理对象需要转型。
🔨“拦截类”里必须重写的invoke(参数)方法
其实就是要理解method.invoke(参数),利用了反射,可以简单地认为用来执行某个的对象的目标方法
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
ClassLoader是干什么的?
它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class等。字节码的本质就是一个字节数组 []byte。
每个 Class 对象里面都有一个 classLoader 属性记录了当前的类是由谁来加载的。
✨3个重要的ClassLoader
JVM 中内置了三个重要的 ClassLoader,分别是
BootstrapClassLoader
ExtensionClassLoader
AppClassLoader。
BootstrapClassLoader 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 java.util.、java.io.、java.nio.、java.lang. 等等。这个 ClassLoader 比较特殊,它是由 C 代码实现的,我们将它称之为「根加载器」。
💤
ExtensionClassLoader 负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。
💦
AppClassLoader 才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。
双亲委派
每个 ClassLoader 都很懒,尽量把工作交给父亲做,父亲干不了了自己才会干。每个 ClassLoader 对象内部都会有一个 parent 属性指向它的父加载器。
🛴
AppClassLoader 在加载一个未知的类名时,它并不是立即去搜寻 Classpath,它会首先将这个类名称交给 ExtensionClassLoader 来加载,如果 ExtensionClassLoader 可以加载,那么 AppClassLoader 就不用麻烦了。否则它就会搜索 Classpath 。
而 ExtensionClassLoader 在加载一个未知的类名时,它也并不是立即搜寻 ext 路径,它会首先将类名称交给 BootstrapClassLoader 来加载,如果 BootstrapClassLoader 可以加载,那么 ExtensionClassLoader 也就不用麻烦了。否则它就会搜索 ext 路径下的 jar 包。
getInterfaces()
以下是**java.lang.Class.getInterfaces()**方法的声明
public Class<?>[] getInterfaces()
此方法返回这个类中实现接口的数组。
>> Home