反射+注解

第一章 反射+注解

第一章 反射+注解



前言

本文主要记录了反射注解相关的知识,看完本文你将理解框架的思想,甚至可以写一个简单的框架(适合有基础的初学者)

反射的的API可以见 https://www.cnblogs.com/zanwanye/p/16039412.html


一、反射

1.1 反射的前置知识

我们简单概述一下Java代码执行过程:.class文件经过类加载器加载到内存生成一个大的Class对象,这个大的Class对象里面可以获取到有getFieldsgetMethods()getConstructors方法等。Jvm通过这个Class对象构造Person()对象。所以我们可不可以不通过new 对象的方式直接在内存中构建Person对象呢?当然可以,反射就是一种方式!
在这里插入图片描述

1.2 反射创建类对象

其实通过反射创建Class对象,就是创建上面的内存中的大Class对象

Class.forName(“类全名”)//这种是基于字节码文件--->写配置文件中常见这种写法下面例子有
Person.class //这种是基于Class对象--->对象传参
new Person().getClass() //这种是基于Person对象--->多用于有Person对象之后

值得注意的是上面的三种方法得到的Class对象都是一样的!

1.3 得到类对象之后

我刚刚学的时候一直搞不懂得到Class对象之后能够做什么呢?不知道你有没有这样的想法,其实Class对象中的方法主要分成四类对应着我们可以获得:Person对象的属性,Person对象的构造方法,Person对象的普通方法,Person对象的类名。我们下面介绍一下这些分类的基本API方法

Fields

//得到person对象的公共Fields
public Field[] getFields()
public Field getField(String name)
//得到person对象的所有Fields
public Field[] getDeclaredFields()
public Field getDeclaredField(String name)

Constructors

//得到person对象的公共Constructors
public Constructor<?>[] getConstructors()
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//得到person对象的所有Constructors
public Constructor<?>[] getDeclaredConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

methods

//得到person对象的公共Methods
public Method[] getMethods()
public Method getMethod(String name, Class<?>... parameterTypes)
//得到person对象中的所有的methods
public Method[] getDeclaredMethods()
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

名字

//得到类的全类名
public String getName()
//得到类名
public String getSimpleName()

介绍完API之后我们想一下我们获得Fields之后我们会想做什么呢?是不是就是获取值get()和设置值set()

public Object get(Object obj)
public void set(Object obj, Object value)

我们获得Constructors之后想要做什么呢?是不是就是要创建实例newInstance()

public T newInstance(Object ... initargs)

得到无参构造器对应使用无参构造创建对象,使用几参构造器使用几参构造创建对象。

我们获得方法之后是不是就要执行invoke()

public Object invoke(Object obj, Object... args)

这样分类来记忆是不是简单多了,这里我需要重点提一下:我们既然可以获得所有的属性和方法,那么肯定包含私有属性和方法那么是不是意味着我们可以操作Person对象里面的私有属性和方法了呢?答案是yes,不过我们需要调用里面的setAccess下面举例说明一下:
Person类

public class Person {                                                                     
                                                                                          
    private String name;                                                                  
    private int age;                                                                      
                                                                                          
    public Person() {                                                                     
                                                                                          
    }                                                                                     
    public Person(String name,int age){                                                   
        this.name = name ;                                                                
        this.age = age;                                                                   
    }                                                                                     
                                                                                          
    public void run(){                                                                    
        System.out.println("我跑的很快");                                                      
    }                                                                                     
                                                                                          
    public String getName() {                                                             
        return name;                                                                      
    }                                                                                     
                                                                                          
    public void setName(String name) {                                                    
        this.name = name;                                                                 
    }                                                                                     
                                                                                          
    public int getAge() {                                                                 
        return age;                                                                       
    }                                                                                     
                                                                                          
    public void setAge(int age) {                                                         
        this.age = age;                                                                   
    }                                                                                     
}                                                                                         

测试类

public class Refelct01 {
    public static void main(String[] args) throws Exception {
        Class<Person> pc = Person.class;
        Field name = pc.getDeclaredField("name");
        Person person = new Person("张三",2);
        name.setAccessible(true);
        name.set(person,"李四");
        System.out.println("张三的名字变成了"+person.getName());
    }
}

name.setAccessible(true);//注意我们如果不加这行代码,那么程序会抛出java.lang.IllegalAccessException异常,因为name属性是私有的,加入这行代码便是暴力反射。是不是感觉暴力反射也没有那么难搞了呢。

1.4 反射的优点

如果你已经看到了这里,那么你一定很好奇?为什么这么麻烦咱们直接new Person().getName()不好吗,我先告诉你反射的最大得优点就是我们可以解耦写框架

假设我们在UserController层里面调用UserService类的对象,正常情况下我们需要在UserController层里面 new UserService()得到userService对象,但是使用Spring框架的时候我们只需要将UserService对象注入到UserController里面就可以了。

好了到这个时候你把思路捋顺一下:我们想一下当项目投产之后这个UserService感觉并不合适因为我们项目的主要用户群体是StudentService,那么这个时候你是不是要在UserController里面重新new StudentService呢?但是在Spring框架中我们只需要将UserService替换成StudentService即可!上面我们简单的描述了为什么框架可以解耦,现在我们用一个例子来写一个简单的框架。

1.5 反射实现框架

这个框架的作用就是可以将我们写的任意一个类中的方法执行:

首先我们来写一个pro.properties文件

className=com.xxx.reflect.Person
methodName=run

然后我们准备一个person类

public class Person {

    private String name;
    private int age;

    public Person() {

    }
    public Person(String name,int age){
        this.name = name ;
        this.age = age;
    }

    public void run(){
        System.out.println("我跑的很快");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

然后我们编写测试代码

public class Reflect02 {
    public static void main(String[] args) throws Exception {
        ResourceBundle resource = ResourceBundle.getBundle("pro");//获得pro.properties文件
        String className = resource.getString("className");//得到pro文件中的className
        String methodname = resource.getString("methodName");//得到pro文件中的methodName
        Class<?> clazz = Class.forName(className);//通过反射获得Class对象
        Method method = clazz.getMethod(methodname);//通过反射获得run方法
        Object person = clazz.newInstance();//通过反射创建对象
        method.invoke(person);//执行person中的run方法
    }
}

我们试想一下如果将Reflect02作为一个框架打成一个jar包,那么使用者是不是只要咋pro.properties里面加上他们自己写的类,那么他们的类就是可以运行的了呢?怎么样,你看你现在已经可以写出来一个框架了是不是很厉害!

二、注解

2.1 自定义注解

注解格式
public @interface read{ }
注解实质
public interface read extends java.lang.annotation.Annotation{} 我们可以看到其实自定义注解就是一个接口而已,接口中属性是没有意义的,只能定义抽象方法,抽象方法又可以省略 public abstract 那么我们注解是不是就可以写成这样的了:

public @interface read {
    String name ();
    int age();
}

注解属性
注解的属性其实就是抽象方法的方法名(我之前学习的时候总是记不住注解的属性怎么定义的,现在发现他是一个接口里面定义的是抽象方法是不是简单多了)

  • 属性的返回值只能是基本类型String注解enum注解以上类型的数组
  • 如果属性使用default默认赋值,可以不进行赋值
  • 如果属性名是value,并且只有一个属性可以将value省略

元注解
简单的理解就是放在注解上面的注解
@Target:描述注解作用的位置@Target(ElementType.TYPE)、@Target(ElementType.METHOD)@Target(ElementType.FIELD)
@Retention描述注解被保留的阶段@Retention(RetentionPolicy.RUNTIME) (JVM可以读到注解,自定义注解一般都是RUNTIME)@Retention(RetentionPolicy.RUNTIME)

其他元注解用到时不懂直接百度,不要背
那么我们现在就自定义一个自己的注解把

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface read {
    String className ();
    String methodName();
}

2.2 解析元注解

我们将上面的使用配置文件反射的方式转换为使用注解的方式

@Read(className="com.xxx.reflect.Person",methodName="run")
public class Reflect02 {
    public static void main(String[] args) throws Exception {
        Read annotation = Reflect02.class.getAnnotation(Read.class);
        String className = annotation.className();
        String methodName = annotation.methodName();
        Class<?> clazz = Class.forName(className);//通过反射获得Class对象
        Method method = clazz.getMethod(methodName);//通过反射获得run方法
        Object person = clazz.newInstance();//通过反射创建对象
        method.invoke(person);//执行person中的run方法
    }
}

2.3 简单框架

这个框架的作用是创建一个check注解,在Caculator的方法里面部分加上@Check注解,加上Check注解的可以执行响应的代码,并且代码出现异常可以抛出异常,没有加check注解的不能被检查

Check注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {

}

Caculator类

public class Caculator {
    @Check
    public void add(){
        System.out.println("1+0="+(1+0));
    }
    @Check
    public void sub(){
        System.out.println("1-0="+(1-0));
    }
    @Check
    public void div() {
        System.out.println("1/0="+(1/0));
    }
}

TestCheck

public class TestCheck {
    public static void main(String[] args) {
        Class<Caculator> clazz = Caculator.class;//获取到Calculator类目的是得到方法上面的注解
        Method[] methods = clazz.getMethods();//获取到Calculator类目的是得到方法
        Caculator caculator = null;
        try {
            caculator = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        for(Method method : methods) {
            Check annotation = method.getAnnotation(Check.class);//判断方法上面时候有check注解,如果有注解的话我们才会将Caculator里面的方法执行
            if(annotation!=null) {
                try {
                    method.invoke(caculator);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

总结

本篇文章主要简述了java基础里面比较难理解的注解和反射的知识,相信大家看完这篇文章已经可以一个简单的框架demo了,继续加油!
如果有问题请评论中指出,希望大家共同进步:simle

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值