• 反射、代理模式、注解


    目录

    一.Java反射

    1.1反射的第一步:获取Class类的对象

    1.2使用反射获取构造器对象并使用

    1.3使用反射获取成员变量对象并使用

    1.4使用反射获取成员方法对象并使用

    二.代理模式

     2.1概述

    2.2代理模式在Java中的应用

    2.3静态代理

    2.4动态代理

    2.4.1JDK动态代理

    ①实现 InvocationHandler 接口,用来做方法拦截

    ②通过 Proxy.newProxyInstance 创建代理实例

    2.4.2cglib动态代理

    ①实现 MethodInterceptor 接口,用来做方法拦截

    ②设置父类字节码、拦截处理,获取代理对象

    三.注解

    3.1元注解

    3.2注解的本质

    3.3注解的解析


    一.Java反射

    反射指的是允许以编程方式访问已加载类的成分(成员变量、方法、构造器等)。

    1.1反射的第一步:获取Class类的对象

    获取Class对象的三种方式:

    • 类名.class
    • Class.forName(全类名,即包名+类名)
    • Object提供的方法:对象.getClass()

    注:class.newInstance() 可以直接调用该类的无参构造函数进行实例化,但JDK9开始已将其标为弃用,如果要用反射创建对象,还是推荐获取构造器对象来创建实例

    1.2使用反射获取构造器对象并使用

    获取到构造器对象的作用:依然是初始化对象

    setAccessible(boolean)表明反射可以破坏封装性,私有的构造器也可以用来初始化对象

    1.3使用反射获取成员变量对象并使用

    获取到成员变量的作用:依然是为对象赋值、取值

    setAccessible(boolean)表明反射可以破坏封装性,私有的属性也可以赋值、取值

    1.4使用反射获取成员方法对象并使用

    获取成员方法的作用:依然是执行方法

    setAccessible(boolean)表明反射可以破坏封装性,私有的方法也可以调用执行

    二.代理模式

     2.1概述

    二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标 方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑 的代码从目标方法中剥离出来——解耦

    意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能。体现了非常重要的编程思想:不能随便修改源码,如果需要修改源码,通过修改代理的方式来实现功能的拓展。

    2.2代理模式在Java中的应用

    • 统一异常处理
    • Mybatis使用了代理
    • Spring AOP实现原理
    • 日志框架

    2.3静态代理

    • 需要手写代理类
    • 代理目标确定

    案例:给添加学生的service里增强事务操作

    IStudentService

    StudentServiceImpl(需要被代理的对象)

    ProxyStudent(代理对象)

    静态代理的缺点

    • 不利于代码拓展,比如接口中新添加一个抽象方法时,所有实现类都需要重新实现,否则报错
    • 代理对象需要创建很多,这种设计很不方便和麻烦

    2.4动态代理

    2.4.1JDK动态代理

    在不改变原有功能代码的前提下,能够动态的实现方法的增强

    ①实现 InvocationHandler 接口,用来做方法拦截

    invoke方法:代理对象要执行的方法,实现目标方法的执行和功能增强

    • proxy:生成的代理对象
    • method:原始(目标)方法的对象
    • args:参数数组

    ②通过 Proxy.newProxyInstance 创建代理实例
    • ClassLoader loader:类加载器,直接通过需要代理的类获取就行
    • Class[] interfaces:目标类所实现的所有接口
    • InvocationHandler:方法拦截处理器,可以在里面实现方法的增强

    saveProxyClass用于 生成代理类的字节码文件

    1. private void saveProxyClass(String path){
    2. byte[] $proxy1s = ProxyGenerator.generateProxyClass("$Proxy1",
    3. StudentServiceImpl.class.getInterfaces());
    4. FileOutputStream outputStream=null;
    5. try {
    6. outputStream=new FileOutputStream(path+"$Proxy1.class");
    7. outputStream.write($proxy1s);
    8. } catch (Exception e) {
    9. e.printStackTrace();
    10. }finally {
    11. if(outputStream!=null){
    12. try {
    13. outputStream.flush();
    14. outputStream.close();
    15. } catch (Exception e) {
    16. e.printStackTrace();
    17. }
    18. }
    19. }
    20. }

    我们可以看一下生成的代理类,可以发现代理类继承了 Proxy 并实现了目标类实现的所有接口,然后通过反射获取了目标方法的对象

    2.4.2cglib动态代理

    JDK动态代理有一个前提,需要代理的类必须实现接口,如果没有实现接口,只能通过CGLIB来实现,其实就是对于JDK动态代理的一个补充

    ①实现 MethodInterceptor 接口,用来做方法拦截

    ②设置父类字节码、拦截处理,获取代理对象

    我们可以看一下生成的代理类

    • 通过继承的方式去获取到目标对象的方法
    • 通过传递方法拦截器 Methodlnterceptor 实现方法拦截,在这里面做具体的增强
    • 调用生成的代理类对象具体执行重写的 save 方法,直接去调用方法拦截器里面的 intercept 方法
    • 前后加上了增强操作,从而实现了不修改目标代码实现业务增强

    三.注解

    Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

    JDK中内置的一些注解:

    • @Override:检测被该注解标注的方法是否是继承自父类(接口)的
    • @Deprecated:该注解标注的内容,表示已过时
    • @Suppresswarnings: 压制警告,一般传递参数 "all"

    自定义注解:

    1. @Target(ElementType.TYPE)//类级别,元注解
    2. @Retention(RetentionPolicy.RUNTIME)//自定义注解的存活范围,元注解
    3. public @interface Check {
    4. public int value() default 1;
    5. }

    3.1元注解

    元注解:用于描述注解的注解

    • @Target:描述注解能够作用的位置
    • @Retention:描述注解被保留的阶段
    • @Documented:描述注解是否被抽取到API文档中
    • @Inherited:描述注解是否被子类继承
    1. RetentionPolicy.SOURCE:这类注解在变成class文件之前就被注解处理器(Annotation Processor)去掉了,等于说不会被编译到class文件中
    2. RetentionPolicy.Class:会被编译到class文件中,不过在加载后该类型的注解就会被丢弃
    3. RetentionPolicy.RUNTIME:不光会被编译到class文件,在加载之后也会被保留,在运行期间可以反射读取对应的一些方法和变量信息

    3.2注解的本质

    我们先用 javac 对 Check.java 进行编译,然后再用 javap 对 Check.class 进行反编译,查看其源代码。

    可以发现,注解的本质上就是一个接口,该接口默认继承 Annotation 接口。注解中的属性即为接口中的抽象方法。

    特别注意注解中的属性返回类型是有限制的,只能是如下的类型:

    • 基本数据类型
    • String
    • 枚举
    • 注解
    • 以上类型的数组

    3.3注解的解析

    注解的解析就是判断是否存在注解,存在注解就解析出内容。注解在哪个成分上,我们就先拿哪个成分对象。

    • 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解  
    • 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解  
    • 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解

    与注解解析相关的接口

    AnnotatedElement:该接口定义了与注解解析相关的解析方法

    所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口,他们都拥有解析注解的能力

    案例:获取自定义注解的className和methodName,执行该方法

    自定义注解 Pro

    获取上边的注解对象,其实就是在内存中生成了一个实现该注解接口的实现对象

    1. public class ProImpl implements Pro{
    2. @Override
    3. public String methodName() {
    4. ......
    5. return "com.annotation.parse.Print";//返回使用注解时传的参数
    6. }
    7. @Override
    8. public String className() {
    9. ......
    10. return "print";//返回使用注解时传的参数
    11. }
    12. }

  • 相关阅读:
    正则表达式匹配 0-10的正整数;使用细节
    nginx tomcat 动静分离
    基于JavaSwing开发超市收银系统的设计与实现+论文+开题报告+任务书+PPT 毕业设计
    LabVIEW性能和内存管理 3
    stp文件转stl
    「Docker」如何在苹果电脑上构建简单的Go云原生程序「MacOS」
    非比较排序------计数排序
    java.sql.SQLFeatureNotSupportedException解决方法
    前端实现echarts折线图堆叠(多条折线)
    水质分析仪MQTT应用案例
  • 原文地址:https://blog.csdn.net/qq_62767608/article/details/133847038