简介
这个注解是我之前学反射时写的,代码规范不是很好,有很多需要改进的地方,不过作为学习已经是够用了,也可以用来理解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注解来注入呢