基于java反射手写iset注解,实现自动new

简介

这个注解是我之前学反射时写的,代码规范不是很好,有很多需要改进的地方,不过作为学习已经是够用了,也可以用来理解spring的依赖注入

1,新建maven工程

1.1,添加lombok依赖,简化开发

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

2,注解

写一个注解类,代码如下

@Target(ElementType.FIELD) // 作用在字段上
@Retention(RetentionPolicy.RUNTIME) // 运行时使用
public @interface iset {
    // 注入单个属性
    String setName() default "";//要设置的属性名
    String setData() default "";//要设置的属性值
    // 注入多个属性
    // 两个数组一一对应,到时候逐个赋值
    String[] setNames() default {};
    String[] setDatas() default {};
} 

说明一下:

@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Target - 标记使用这个注解的应该是哪种 Java 成员。

而我们的目标是声明字段(一个类的实例),然后通过iset来为这个字段创建实例和注入属性

// 注入单个字段 
@iset(setName = "age" , setData = "8" )
tStu stu2;

// 注入多个字段
@iset(setNames = {"name", "age","email","id"}, setDatas = {"李四", "18","lisi@qq.com","200"})
User user;

3,核心代码逻辑

我们这里希望怎么做呢, 就是定义一个类,提供方法,将当前所有标记了@iset注解的字段创建, 并将其属性赋值, 然后放入一个公共map<类实例字段的字段名,类实例>, 想要类实例只需要从公共map里取就行了

3.1,iConstruct

package org.malred.iset.utils;

import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author malguy-wang sir
 * @create ---
 */
public class iConstruct {
    final static Logger logger = LoggerFactory.getLogger(iConstruct.class);
    /**
     * a map to save all class (保存所有创建的类)
     */
    private static Map<Object, Object> dogs;

    /**
     * 获取所有创建好的类
     */
    public static Map<Object, Object> getDogs() {
        return dogs;
    }

    /**
     * 传入当前类的路径,创建新的实例
     */
    public static Map<Object, Object> generateDogs(String targetClass) throws Exception {
        logger.warn("\t\t==========>>> iset运行开始 <<<==========");
        //路径改为全类名
        targetClass = iUtils.getClassName(targetClass);
        //寻找并加载要创建的类
        Class clazz = Class.forName(targetClass);
        //准备返回的类
        Map<Object, Object> u = new HashMap<>();
        //获取当前类所有的全类名
        String[] classes = new String[clazz.getDeclaredFields().length];
        for (int i = 0; i < clazz.getDeclaredFields().length; i++) {
            classes[i] = clazz.getDeclaredFields()[i].toString();
            // 会带有当前类和全类名+空格+作为属性的类的全类名
            // private org.malred.iset.entitys.User org.malred.iset.itest.t.user
            logger.warn("获取当前类所有的全类名 => " + classes[i]);
        }
        // 获取类实例的字段名称 ( org.malred.iset.itest.t.user => user )
        for (int i = 0; i < classes.length; i++) {
            logger.warn("解析类实例字段名 => " + classes[i].toString());
            classes[i] = classes[i].substring(
                    classes[i].toString().lastIndexOf(" ") + 1, classes[i].length());
            classes[i] = classes[i].substring(
                    classes[i].toString().lastIndexOf(".") + 1, classes[i].length());
            logger.warn("解析结果 => " + classes[i]);
        }
        // 根据解析完成的类实例字段名,获取类实例需要的字段,然后赋值
        for (int i = 0; i < classes.length; i++) {
            logger.warn("为 => { "+classes[i]+" } 类实例字段,注入属性值开始");
            Field obj = clazz.getDeclaredField(classes[i]); // 获取所有字段
            // isAnnotationPresent(iset.class));//判断有没有加注解
            if (obj.isAnnotationPresent(iset.class)) {
                iset is = obj.getAnnotation(iset.class); // 获取注解
                Class<?> type = clazz.getDeclaredField(classes[i]).getType();//获取类
                Object iu = type.getConstructor().newInstance();//创建实例
                Field[] fields = iu.getClass().getDeclaredFields();//获取属性名
                String[] iargs = new String[fields.length]; // 属性名数组
                // 解析属性名(全类名 -> 字段名)
                for (int j = 0; j < fields.length; j++) {
                    iargs[j] = fields[j].toString().substring(
                            // 属性会带全类名,比如org.malred.iset.itest.t.user
                            fields[j].toString().lastIndexOf(".") + 1,
                            fields[j].toString().length());
                    logger.warn("获取类属性名称 => " + iargs[j].toString());
                }
                // 注入单个属性时
                for (int k = 0; k < fields.length; k++) {
                    if (is.setName().equals(iargs[k])) {
                        logger.warn("设置参数 { " + iargs[k] + " } 为 { " + is.setData() + " } 中");
                        Field declaredField = iu.getClass().getDeclaredField(iargs[k]);
                        // 设置可访问(私有字段也能被访问)
                        declaredField.setAccessible(true);
                        // 传入字段和属性,赋值
                        if (iUtils.isNumber(is.setData().toString())) {
                            // 如果是数值类型需要转int
                            declaredField.set(iu, Integer.parseInt(is.setData().toString()));
                        } else {
                            declaredField.set(iu, is.setData().toString());
                        }
                        logger.warn("设置参数 => " + iargs[k] + " 完毕");
                    }
                }
                // 注入属性
                for (int l = 0; l < fields.length; l++) {
                    for (int j = 0; j < is.setNames().length; j++) {
                        if (is.setNames()[j].toString().equals(iargs[l])) {
                            logger.warn("设置参数 { " + iargs[l] + " } 为 { "
                                    + is.setDatas()[j].toString() + " } 中");
                            // iu是创建了的实例
                            Field declaredField = iu.getClass().getDeclaredField(iargs[l]);
                            declaredField.setAccessible(true);
                            // 如果是数值类型需要转int
                            if (iUtils.isNumber(is.setDatas()[j].toString())) {
                                declaredField.set(iu, Integer.parseInt(is.setDatas()[j].toString()));
                            } else {
                                declaredField.set(iu, is.setDatas()[j].toString());
                            }
                            logger.warn("设置参数 => " + iargs[l] + " 完毕");
                        }
                    }
                }
                logger.warn("为 => { "+classes[i]+" } 类实例字段,注入属性值完毕");
                logger.warn("将 { " + classes[i] + " } 放入map中");
                // 放入dogs集合
                u.put(classes[i], iu);
            }
        }
        logger.warn("\t\t==========>>> iset运行结束 <<<==========");
        return u;
    }

    /**
     * 生成类,并获取所有创建好的类(map)
     *
     * @param nowClass 当前类的路径或全类名
     * @return
     * @throws Exception
     */
    public static Map<Object, Object> getDogs(String nowClass) throws Exception {
        dogs = generateDogs(nowClass);
        return dogs;
    }

    /**
     * 通过id(字段名)获取类实例
     *
     * @param className
     * @return
     * @throws Exception
     */
    public static Object getDogByName(String className) throws Exception {
        return dogs.get(className);
    }

    //just new class , than U can get a map named dogs which have all class you need
    public iConstruct(String nowClass) throws Exception {
        getDogs(nowClass);
    } 
} 

3.2,iUtils 工具类

package org.malred.iset.utils; 
/**
 * 工具类
 *
 * @author malguy-wang sir
 * @create ---
 */
public class iUtils {
    //判断字符串是不是数字
    public static boolean isNumber(String s) {
        for (int i = 0; i < s.length(); i++) {
            if (!Character.isDigit(s.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    //传入来自源根的路径,返回全类名
    public static String getClassName(String path) {
        path = path.replace("/", ".");
        path = path.replace("\\", ".");
        path = path.replace(".java", "");
        return path;
    }
} 

4,测试

4.1,实体类

package org.malred.iset.entitys; 
import lombok.Data; 
@Data
public class User {
  private long id;
  private String name;
  private long age;
  private String email;
}  
package org.malred.iset.entitys; 
import lombok.Data; 
@Data
public class tStu {
    private String name;
    private int age;
} 

4.2,测试

public class t {
    @iset(setNames = {"name", "age", "email", "id"}, setDatas = {"李四", "18", "lisi@qq.com", "200"})
    private User user;
    @iset(setNames = {"name", "age"}, setDatas = {"张三", "56"})
    tStu stu;
    @iset(setNames = {"name", "age"}, setDatas = {"王五", "8"})
    tStu stu1;
    @iset(setName = "age" , setData = "8" )
    tStu stu2;

    public void t1() throws Exception {
        Map iuser = iConstruct.
                generateDogs("org/malred/iset/itest/t.java");
        System.out.println(iuser);
    }

    public static void main(String[] args) throws Exception {
        new iConstruct(t.class.getName());
        Map map = iConstruct.getDogs();
        System.out.println(map.get("user"));
        System.out.println(map.get("stu"));
        System.out.println(map.get("stu1"));
        System.out.println(map.get("stu2"));
    }
}

4.3,结果

"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" -Dvisualvm.id=218153308319900 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.1\lib\idea_rt.jar=50078:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_241\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\rt.jar;C:\Users\13695\Desktop\造轮子\代码\手写造轮子\iset(反射小练习)\myConstruct\target\classes;D:\java_maven\maven_repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar" org.malred.iset.itest.t
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:         ==========>>> iset运行开始 <<<==========
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取当前类所有的全类名 => private org.malred.iset.entitys.User org.malred.iset.itest.t.user
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取当前类所有的全类名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取当前类所有的全类名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu1
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取当前类所有的全类名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu2
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析类实例字段名 => private org.malred.iset.entitys.User org.malred.iset.itest.t.user
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析结果 => user
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析类实例字段名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析结果 => stu
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析类实例字段名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu1
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析结果 => stu1
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析类实例字段名 => org.malred.iset.entitys.tStu org.malred.iset.itest.t.stu2
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 解析结果 => stu2
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { user } 类实例字段,注入属性值开始
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => id
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => name
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => age
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => email
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { id }{ 200 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => id 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { name }{ 李四 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => name 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { age }{ 18 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => age 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { email }{ lisi@qq.com } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => email 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { user } 类实例字段,注入属性值完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:{ user } 放入map中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu } 类实例字段,注入属性值开始
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => name
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => age
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { name }{ 张三 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => name 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { age }{ 56 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => age 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu } 类实例字段,注入属性值完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:{ stu } 放入map中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu1 } 类实例字段,注入属性值开始
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => name
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => age
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { name }{ 王五 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => name 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { age }{ 8 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => age 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu1 } 类实例字段,注入属性值完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:{ stu1 } 放入map中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu2 } 类实例字段,注入属性值开始
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => name
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 获取类属性名称 => age
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 { age }{ 8 } 中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告: 设置参数 => age 完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:=> { stu2 } 类实例字段,注入属性值完毕
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:{ stu2 } 放入map中
一月 30, 2023 8:18:23 下午 com.sun.org.slf4j.internal.Logger warn
警告:         ==========>>> iset运行结束 <<<==========
User(id=200, name=李四, age=18, email=lisi@qq.com)
tStu(name=张三, age=56)
tStu(name=王五, age=8)
tStu(name=null, age=8)

进程已结束,退出代码为 0 

*.问题和拓展

git仓库

https://github.com/malred/myConstruct.git
https://gitee.com/malguy/myConstruct.git

*.1,对象

我这里没有考虑类里面有对象类型的属性的情况,如果要创建的类需要一个对象属性,如何实现?

*.2,循环依赖

如果有user和stu类,user类有一个属性是stu类,而stu类也有一个user属性,那么,就会互相依赖,无法创建,如何解决?

*.3,扫描

当前需要手动传入当前类路径,能不能实现像spring的scan一样的扫描,将某个实现@Scan注解的类的子目录全部扫描,并根据有无@compnent+@iset注解来注入呢


 上一篇
手写vue2 手写vue2
搭建环境rollup 开发环境搭建npm init -y # 工具库一般用rollup打包,babel则是编译高级语法(->低级语法),来适配不同浏览器 npm i rollup rollup-plugin-babel @babel/cor
2023-02-05
下一篇 
jdk11缺失依赖报错 jdk11缺失依赖报错
当jdk8升级到jdk11,Java EE相关模块默认不在Java包里面了,相关的类需要增加额外依赖或者替换成其他的类,如果你直接运行,可能会出现这个问题: error: package javax.xml.bind does not e
2022-10-16 malred
  目录