Skip to main content

  1. 按数据流向分为输入流输出流
  2. 按数据类型分为字节流字符流
  3. 按照流的角色来分为节点流处理流

输入流和输出流

字节流和字符流

只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

节点流和处理流

可以从/向一个特定的 IO 设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被成为低级流

处理流是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流

![image-20221220153000400](/asset/java IO 流/pic/image-20221220153000400.png)

![image-20221220153816291](/asset/java IO 流/pic/image-20221220153816291.png)

节点流(FileReader、FileWriter、FileOutputStream、FileInputStream)又叫文件流

两种动态代理

Cglib 和 jdk 动态代理的区别

动态代理解决了方法之间的紧耦合,

IOC 解决了类与类之间的紧耦合!

Cglib 和 jdk 动态代理的区别?

1、Jdk 动态代理:利用拦截器(必须实现 InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理

2、 Cglib 动态代理:利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过修改其字节码生成子类来处理

什么时候用 cglib 什么时候用 jdk 动态代理?

1、目标对象生成了接口 默认用 JDK 动态代理

2、如果目标对象使用了接口,可以强制使用 cglib

3、如果目标对象没有实现接口,必须采用 cglib 库,Spring 会自动在 JDK 动态代理和 cglib 之间转换

JDK 动态代理和 cglib 字节码生成的区别?

1、JDK 动态代理只能对实现了接口的类生成代理,而不能针对类

2、Cglib 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成 final,对于 final 类或方法,是无法继承的

Cglib 比 JDK 快?

1、cglib 底层是 ASM 字节码生成框架,但是字节码技术生成代理类,在 JDL1.6 之前比使用 java 反射的效率要高

2、在 jdk6 之后逐步对 JDK 动态代理进行了优化,在调用次数比较少时效率高于 cglib 代理效率

3、只有在大量调用的时候 cglib 的效率高,但是在 1.8 的时候 JDK 的效率已高于 cglib

4、Cglib 不能对声明 final 的方法进行代理,因为 cglib 是动态生成代理对象,final 关键字修饰的类不可变只能被引用不能被修改

Spring 如何选择是用 JDK 还是 cglib?

1、当 bean 实现接口时,会用 JDK 代理模式

2、当 bean 没有实现接口,用 cglib 实现

3、可以强制使用 cglib(在 spring 配置中加入<aop:aspectj-autoproxy proxyt-target-class="true"/>

一. Cglib 原理

动态生成一个要代理的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,它比 Java 反射的 jdk 动态代理要快

Cglib 是一个强大的、高性能的代码生成包,它被广泛应用在许多 AOP 框架中,为他们提供方法的拦截

最底层的是字节码 Bytecode,字节码是 java 为了保证依次运行,可以跨平台使用的一种虚拟指令格式

在字节码文件之上的是 ASM,只是一种直接操作字节码的框架,应用 ASM 需要对 Java 字节码、class 结构比较熟悉

位于 ASM 上面的是 Cglib,groovy、beanshell,后来那个种并不是 Java 体系中的内容是脚本语言,他们通过 ASM 框架生成字节码变相执行 Java 代码,在 JVM 中程序执行不一定非要写 java 代码,只要能生成 java 字节码,jvm 并不关系字节码的来源

位于 cglib、groovy、beanshell 之上的就是 hibernate 和 spring AOP

最上面的是 applications,既具体应用,一般是一个 web 项目或者本地跑一个程序、

使用 cglib 代码对类做代理?

使用 cglib 定义不同的拦截策略?

构造函数不拦截方法

用 MethodInterceptor 和 Enhancer 实现一个动态代理

Jdk 中的动态代理

JDK 中的动态代理是通过反射类 Proxy 以及 InvocationHandler 回调接口实现的,但是 JDK 中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高

Cglib 实现

使用 cglib 是实现动态代理,不受代理类必须实现接口的限制,因为 cglib 底层是用 ASM 框架,使用字节码技术生成代理类,你使用 Java 反射的效率要高,cglib 不能对声明 final 的方法进行代理,因为 cglib 原理是动态生成被代理类的子类

Cglib 的第三方库提供的动态代理

动态代理: 特点:字节码随用随创建,随用随加载 作用:不修改源码的基础上对方法增强 分类: 基于接口的动态代理 基于子类的动态代理 基于子类的动态代理: 涉及的类:Enhancer 提供者:第三方 cglib 库 如何创建代理对象: 使用 Enhancer 类中的 create 方法 创建代理对象的要求: 被代理类不能是最终类 newProxyInstance 方法的参数:在使用代理时需要转换成指定的对象 ClassLoader:类加载器 他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法 Callback:用于提供增强的代码 他是让我们写如何代理。我们一般写一个该接口的实现类,通常情况加都是匿名内部类,但不是必须的。 此接口的实现类,是谁用谁写。 我们一般写的都是该接口的子接口实现类,MethodInterceptor

com.dynamic.cglib.Producer cglibProducer= (com.dynamic.cglib.Producer) Enhancer.create(
com.dynamic.cglib.Producer.class,
new MethodInterceptor() {
/**
* 执行被代理对象的任何方法都会经过该方法
* @param obj
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param proxy:当前执行方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object returnValue=null;
Float money=(Float)args[0];
if("saleProduct".equals(method.getName())){
returnValue= method.invoke(producer,money*0.8f);
}
return returnValue;
}
}
);
cglibProducer.saleProduct(100.0f);

JDK 本身提供的动态代理实现

/**
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理:
* 涉及的类:proxy
* 提供者:Jdk官方
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:在使用代理时需要转换成指定的对象
* ClassLoader:类加载器
* 他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
* Class[]:字节码数组
* 它是用于让代理对象和被代理对象有相同方法。固定写法
* InvocationHandler:用于提供增强的代码
* 他是让我们写如何代理。我们一般写一个该接口的实现类,通常情况加都是匿名内部类,但不是必须的。
* 此接口的实现类,是谁用谁写。
*/
IProducer proxyProducer= (IProducer) Proxy.newProxyInstance(
producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),

new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象有相同返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 提供增强的代码
// 1、获取方法执行的参数
Object returnValue=null;
Float money=(Float)args[0];
if("saleProduct".equals(method.getName())){
returnValue= method.invoke(producer,money*0.8f);
}
return returnValue;
}
}
);

JDK 和 Cglib 的区别:

CglibJDK
是否提供子类代理
是否提供接口代理是(可强制)
区别必须依赖于 CGLib 的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法实现 InvocationHandler 使用 Proxy.newProxyInstance 产生代理对象 被代理的对象必须要实现接口