【自研框架一】反射、注解、控制反转

反射

允许一个java类获得它所有的成员变量和方法,并且显示出来

允许程序在运行时来进行自我检查并且对内部的成员进行操作

反射主要是指程序可以访问,检测和修改它本身状态或行为的- -种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义

只要给定jav类的package和名字,就可以通过反射获得类的所有信息。

反射机制的作用

◆在运行时判断任意一个对象所属的类
◆在运行时获取类的对象
◆在运行时访问java对象的属性, 方法,构造方法等

反射依赖reflect和Class

java.lang.reflect类库里面主要的类

◆Field :表示类中的成员变量
◆Method :表示类中的方法
◆Constructor :表示类的构造方法
◆Array :该类提供了动态创建数组和访问数组元素的静态方法

反射依赖的Class
用来表示运行时类型信息的对应类

◆每个类都有唯一一个 与之相对应的Class对象
◆Class类为类类型,而Class对象为类类型对象

Class类的特点

◆Class类也是类的一种, class则是关键字
◆Class类只有一个私有的构造函数(无法通过new来获取实例),只有JVM能够创建Class类的实例
◆JVM中只有唯一一个和类相对应的Class对象来描述其类型信息

获取Class对象的三种方式

◆Object 一-> getClass()
◆任何数据类型(包括基本数据类型)都有一个“静态”的class属性
◆通过Class类的静态方法 : forName(String className)(常用)

在运行期间,一个类,只有一个与之相对应的Class对象产生

package demo.reflect;

public class GetClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        GetClassDemo getClassDemo = new GetClassDemo();
        Class class1 = getClassDemo.getClass();
        System.out.println(class1);

        Class class2 = GetClassDemo.class;
        System.out.println(class2);
        System.out.println(class1==class2);

        Class class3 = Class.forName("demo.reflect.GetClassDemo");
        System.out.println(class3);
        System.out.println(class2==class3);

    }
}

我们通过反射读取配置文件,可以得到该类的相关一切信息。

Class对象就像一面镜子,透过这面镜子可以看到类的结构

反射的主要用法

带有Declared都是无视修饰符的,对于私有的不管是构造函数还是成员变量,获取之后都要设置权限

◆如何获取类的构造方法并使用
/*
 * 通过Class对象可以获取某个类中的:构造方法;
 *
 * 获取构造方法:
 *         1).批量的方法:
 *             public Constructor[] getConstructors():所有"公有的"构造方法
              public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
 *         2).获取单个的方法,并调用:
 *             public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 *             public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 *
 *             调用构造方法:
 *             Constructor-->newInstance(Object... initargs)
 */

可以通过反射访问私有的构造方法,不过要设置权限setAccessible(true)

构造方法

package demo.reflect;

public class GetClassDemo {

    //---------构造函数-----------
    //(默认的带参数构造函数)
    GetClassDemo(String str) {
        System.out.println("(默认)的构造方法 s = " + str);
    }

    //无参构造函数
    public GetClassDemo() {
        System.out.println("调用了公有的无参构造方法 。。。");
    }

    //有一个参数的构造函数
    public GetClassDemo(char name) {
        System.out.println("调用了带有一个参数的构造方法,参数值为 " + name);
    }

    //有多个参数的构造函数
    public GetClassDemo(String name, int index) {
        System.out.println("调用了带有多个参数的构造方法,参数值为【目标名】: " + name + " 【序号】" + index);
    }
    //受保护的构造函数
    protected GetClassDemo(boolean n){
        System.out.println("受保护的构造方法 n :" + n);
    }
    //私有的构造函数
    private GetClassDemo(int index){
        System.out.println("私有的构造方法 序号:" + index);
    }


}

调用构造方法

package demo.reflect;

import java.lang.reflect.Constructor;

public class ConstructorCollector {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("demo.reflect.GetClassDemo");
        //1、获取所有的共有构造方法
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        //2、获取所有构造方法(共有、私有。。)
        constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        //3、获得单个单参数的共有方法
        Constructor constructor = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor);

        //4、获得单个私有的构造方法
        constructor = clazz.getDeclaredConstructor(int.class);
        System.out.println(constructor);
        //暴力访问(忽略访问修饰符)
        constructor.setAccessible(true);
        GetClassDemo getClassDemo = (GetClassDemo) constructor.newInstance(1);
    }
}
◆如何获取类的成员变量并使用

image-20220104173130045

上面需要注意的是:

不带Declared修饰的可以获取父类的字段,带它修饰的不能获取到

/*
 * 获取成员变量并调用:
 *
 * 1.批量的
 *      1).Field[] getFields():获取所有的"公有字段"
 *      2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
 * 2.获取单个的:
 *      1).public Field getField(String fieldName):获取某个"公有的"字段;
 *      2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
 *
 *   设置字段的值:
 *      Field --> public void set(Object obj,Object value):
 *                  参数说明:
 *                  1.obj:要设置的字段所在的对象;
 *                  2.value:要为字段设置的值;
 *
 */

实体类

package demo.reflect;

public class GetClassDemo {

    //**************字段*******************//
    public String name;
    protected int index;
    char type;
    private String targetInfo;
    @Override
    public String toString(){
        return "ReflectTarget [name=" + name + ", index=" + index + ", type=" + type
                + ", targetInfo=" + targetInfo + "]";
    }

   
}

给实体获得属性并设置属性值

package demo.reflect;

import java.lang.reflect.Field;

public class FieldController {
    public static void main(String[] args) throws Exception {
        //获得class对象
        Class clazz = Class.forName("demo.reflect.GetClassDemo");

        //1.获取所有公有的字段
        Field[] fields = clazz.getFields();
        //2.获取所有的字段
        Field[] declaredField = clazz.getDeclaredFields();
        //3.获取单个特定公有的field
        Field field = clazz.getField("name");
        //获得实例对象
        GetClassDemo getClassDemo = (GetClassDemo) clazz.getConstructor().newInstance();
        //给实例对象设置name属性值
        field.set(getClassDemo,"haohoa");
        System.out.println(getClassDemo.name);
        //6.获取单个私有的Field
        Field targetInfo = clazz.getDeclaredField("targetInfo");
        targetInfo.setAccessible(true);
        //给实例对象设置targetInfo属性值
        targetInfo.set(getClassDemo,"111111");
        System.out.println(getClassDemo);
    }
}
◆如何获取类的成员方法并使用
/*
 * 获取成员方法并调用:
 *
 * 1.批量的:
 *      public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 *      public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 *      public Method getMethod(String name,Class<?>... parameterTypes):
 *                  参数:
 *                      name : 方法名;
 *                      Class ... : 形参的Class类型对象
 *      public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 *   调用方法:
 *      Method --> public Object invoke(Object obj,Object... args):
 *                  参数说明:
 *                  obj : 要调用方法的对象;
 *                  args:调用方式时所传递的实参;
):
 */

方法和成员变量一样都有继承父类之说,两者都一样。带有Declared修饰的都是不能包含父类的。同时指定使用某个成员变量或者方法时要将变量名称和方法名称传入。同时需要先实例化对象再使用。

定义方法

//***************成员方法***************//
public void show1(String s){
    System.out.println("调用了公有的,String参数的show1(): s = " + s);
}
protected void show2(){
    System.out.println("调用了受保护的,无参的show2()");
}
void show3(){
    System.out.println("调用了默认的,无参的show3()");
}
private String show4(int index){
    System.out.println("调用了私有的,并且有返回值的,int参数的show4(): index = " + index);
    return "show4result";
}

调用方法

package demo.reflect;

import java.lang.reflect.Method;

public class MethodCollector {

    public static void main(String[] args) throws Exception {
        //获得class对象
        Class clazz = Class.forName("demo.reflect.GetClassDemo");
        //2、获取所有公有方法
        Method[] methods = clazz.getMethods();
        //3、获取该类的所有方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        //调用指定的公用方法show1
        Method method = clazz.getMethod("show1", String.class);

        //调用指定的私有方法show4
        Method show4 = clazz.getDeclaredMethod("show4", int.class);
        show4.setAccessible(true);
        //使用实例化对象使用方法(属性和方法的使用都要先实例化对象)
        GetClassDemo getClassDemo = (GetClassDemo) clazz.getConstructor().newInstance();
        String res = (String) show4.invoke(getClassDemo, 20);
        System.out.println("返回值 : " + res);
    }
}

反射的获取源

image-20220104192645827

用注解来保存类相关的信息以供反射调用

image-20220104192730922

注解

提供一种为程序元素设置元数据的方法。

元数据是指数据的数据,即对数据的描述。

◆元数据是添加到程序元素如方法、字段、类和包上的额外信息

◆注解是- -种分散式的元数据设置方式, XML是集中式的设置方式

◆注解不能直接干扰程序代码的运行

通俗的讲注解就是将我们需要的信息保存起来,等我们需要的时候(编译时、运行时)调用

注解的功能

◆作为特定的标记,用于告诉编译器一些信息
◆编译时动态处理,如动态生成代码(例如Lombok的@Data生成实体类的get…方法)

◆运行时动态处理,作为额外信息的载体,如获取注解信息(将路径等信息写入注解类里获取)

注解的分类

◆标准注解: Override、Deprecated(不被鼓励使用的,不在维护了)、 SuppressWarnings(需要忽略某项。。)

◆元注解: @Retention(生命周期)、@Target(作用目标)、 @Inherited、 @Documented

元注解是原来定义annation的annotion

用于修饰注解的注解,通常用在注解的定义上

image-20220104194229779

◆自定义注解

元注解@Target

描述所修饰的注解的使用范围
◆packages、types (类、接口、枚举、Annotation类型 )

◆类型成员(方法、构造方法、成员变量(FIELD)、枚举值)
◆方法参数和本地变量(如循环变量、catch参数 )

元注解@ Retention

标注注解被保留时间的长短

SOURCE:表示注解只能在原文件中保留,在编译好的class文件会被消除

CLASS:表示注解不仅会在原文件中保留,还在编译好的class文件会中

RUNTIME:运行时获得该注解的信息,运行时有效。同时还可以通过反射在注解里面获取其他的有用信息。

◆@Documented :注解是否应当被包含在JavaDoc文档中
◆@Inherited :是否允许子类继承该注解(被该注解修饰的子类自动可以继承父类被注解@Inherited修饰的注解)

自定义注解的实现

image-20220104195838350

访问修饰符只能是public或者缺省(只能在同一个包内访问)

image-20220104200034414

定义注解的时候不能继承其他的注解或者接口。

FIELD:注解只能作用在变量上。

成员变量的定义要按照方法去定义(即变量名后面要加上())

package demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)//只能作用在变量上
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
    //名字
    public String name();
    //年龄
    public int age() default 18;
    //性别
    public String gender() default "女";
    //开发语言
    public String[] language();
}

为了用例覆盖更多,再定义一个注解

TYPE:允许该注解作用在类之上,

package demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})//只能作用在类,方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseInfoAnnotation {
    //课程名称
    public String courseName();
    //课程标签
    public String courseTag();
    //课程简介
    public String courseProfile();
    //课程序号
    public int courseIndex() default 303;
}

使用注解

创建一个类去使用,对注解里面对应的属性赋值,有默认值得可以不用赋值

package demo.annotation;

@CourseInfoAnnotation(courseName = "java",courseTag = "加油",courseProfile = "哈哈")
public class ImoocCourse {

    @PersonInfoAnnotation(name = "haha",language = {"java","c","python"})
    private  String author;

    @CourseInfoAnnotation(courseName = "c",courseTag = "ccc",courseProfile = "呵呵")
    public void getCourseInfo(){

    }
}

调用加了注解得方法,发现加了注解之后并没有什么卵用

image-20220104201946136

isAnnotationPresent:判断指定类型的注解是否存在在此元素上。

getAnnotation:获得对象的单个注解(包含继承)

getAnnotations:获得对象上的所有注解(包含继承)

怎么操作ImoocCourse里面的注解?

解析注解,通过反射获得AnnotatedElement

解析类的注解

package demo.annotation;

import java.lang.annotation.Annotation;

public class AnnotationParser {
    //解析类的注解
    public static void parseTypeAnnotation() throws ClassNotFoundException {
        Class clazz = Class.forName("demo.annotation.ImoocCourse");
        //获得class对象得注解,而不是其里面的方法和成员变量得注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            CourseInfoAnnotation courseInfoAnnotation=(CourseInfoAnnotation)annotation;
            System.out.println(courseInfoAnnotation.courseName()+courseInfoAnnotation.courseTag()
            +courseInfoAnnotation.courseProfile()+courseInfoAnnotation.courseIndex());
        }

    }

    public static void main(String[] args) throws ClassNotFoundException {
        parseTypeAnnotation();
    }
}

解析成员变量的注解标签

1、先获得类对象

2、在获得该类上得所有成员变量

3、遍历成员变量数组

4、判定成员变量上有没有指定得注解

5、有的话就获取注解标签实例,并打印出来

//解析成员变量得注解标签
public static void parseFieldAnnotation() throws ClassNotFoundException {
    Class clazz = Class.forName("demo.annotation.ImoocCourse");
    //反射获得对象得属性
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        //判定成员变量上有没有指定得注解
        boolean b = field.isAnnotationPresent(PersonInfoAnnotation.class);
        if(b){
            PersonInfoAnnotation personInfoAnnotation= field.getAnnotation(PersonInfoAnnotation.class);
            System.out.println(personInfoAnnotation.name()+personInfoAnnotation.age()
            +personInfoAnnotation.gender());

            for (String language : personInfoAnnotation.language()) {//打印数组
                System.out.println(language);
            }
        }
    }
}

解析方法上的注解

//解析方法上得注解
public static void parseMethod() throws ClassNotFoundException {
    Class clazz = Class.forName("demo.annotation.ImoocCourse");
    //通过反射获得对象得方法
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        //判断方法上得注解是否我们想要的
        boolean b = method.isAnnotationPresent(CourseInfoAnnotation.class);
        if(b){
            CourseInfoAnnotation annotation = method.getAnnotation(CourseInfoAnnotation.class);
            System.out.println(annotation.courseName()+annotation.courseIndex()+
                    annotation.courseProfile()+annotation.courseIndex());
        }
    }
}

CLASS:注解出现在编译好的class文件里,但是注解的相关信息不能出现在运行时。

一旦修改注解得生命周期不为RUNTIME,那我们就无法通过反射来获取注解信息。

注解获取属性值得底层实现

JVM会为注解生成代理对象,注解其实也是一个接口,jvm会生成中间代理对象(在程序运行时生成称为动态代理对象)

image-20220105110504794

image-20220105110634036

上述学习得意义

控制反转的基础时需要一个类似工厂得容器来解析和管理项目所需要得实例。如果不用spring得框架,自己研发,需要实现这样子的容器。为实现容器先了解IoC。

对象一般用在存储数据,不同对象之间得相互依赖合作构成软件系统。如果对象的依赖关系靠自己来管理,比较复杂并且耦合度较高,我们就将对象得创建、依赖关系的管理以及生命周期交由IoC容器管理。而对象只需要专心承载和处理数据即可。

控制反转IoC-Inversion of Control

IoC并不能说说是一种技术,而是一种思想,从繁琐的对象交互中解脱,从而专注于对象本身,更进一步突出面向对象。

需要先了解依赖注入DI

依赖注入DI

image-20220105112505976

image-20220105112627432

修改一下轮子得尺寸就需要修改所有得实体类,不可维护

image-20220105112754230

用依赖注入DI实现控制反转

含义:把底层类作为参数传递给上层类,实现上层对下层得控制。

换一种思路:

先设计行李箱得大概样子,根据行李箱设置箱体,,,如果再需要改变轮子的尺寸,那就只需要改变轮子自身即可

image-20220105112929648

即将底层类作为参数传入上层类,实现上层类对下层类得控制

将Tire作为参数直接传入

image-20220105113354615

若要像修改轮子的尺寸

image-20220105113434995

除了构造函数依赖注入还有set和接口传递。

IoC、DI、DL关系

image-20220105113612932

依赖注入的方式
◆Setter
◆Interface
◆Constructor
◆Annotation

依赖倒置原则、IOC、 DI、 IOC容器的关系

image-20220105113858221

IOC容器的优势
◆避免在各处使用new来创建类,并且可以做到统一维护
◆创建实例的时候不需要了解其中的细节
◆反射+工厂模式的合体,满足开闭原则

根据字符串类名来动态的生成对象,这种生成方式可以让对象在生成时决定到底是哪一种对象,可以把IoC得工作模式看作工厂模式得升华,将IoC容器看作一个工厂,根据传入得参数返回对象,但又有人认为是抽象工厂,因为返回得对象是不同类型得。

这个工厂里要生产得对象都要在配置文件中给出定义,利用反射机制,根据配置文件给出的类名,生成相应对象。即IOC将原本在工厂写死得对象生成代码由配置文件来定义,将工厂和对象生成分开。

image-20220105114817913

小节总结:

我们一开始使用工厂来创建对象,来减去客户端创建对象得烦恼,

同时为了软件系统得可扩展性以及遵循开闭原则,比较了简单工厂模式、工厂模式、抽象模式,发现单一的工厂模式无法满足通用得框架需求,需要结合java得反射机制。

了解了反射还需要标记,标记需要反射的目标(xml、注解)

DI离不开IoC容器,IoC创建和管理对象,在客户端需要的时候给其注入,客户端不需要了解对象是怎么创建得,这样就能做到客户端得解耦。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值