很久前写的小demo了可能有不少bug 代码逻辑方法不太精简优雅 也没有做三级缓存解决循环依赖的问题
先创建点自定义注解
自动注入注解 标记型的即可
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutoWired {
}
标记型注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRepository {
}
配置类注解 要启动bean实例化和注入必须加一个这个
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConfiguration {
}
包扫描注解 用来决定哪个包及其子包的类可以被实例化
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponentScan {String value() default "";
}
属性注入注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {String value() default "";
}
配置类 必须要加 不加无法实例化类 扫描也必须加 不然也无法实例化 其实这里可以改成springboot类型的自动获取当前包自动初始化不用扫描的懒得改了
定义几个类
beanA 有个成员属性beanB 属性选择了指定值注入和配置文件注入
@MyController
public class BeanA {public void print(){System.out.println("回忆是一条没有归途的路");beanB.print();}public String getName() {return name;}@MyValue("${joker.name}")@MyAutoWiredprivate String name;public String getLover() {return dream;}@MyValue("毁掉一切")@MyAutoWiredprivate String dream;@MyAutoWiredBeanB beanB;
}
beanB
@MyComponent
public class BeanB {//绝对自律带来绝对残忍public void print(){System.out.println("百年之后没有你也没有我");}public String getName() {return name;}@MyAutoWired@MyValue("${猪脑过载.name}")private String name;public Integer getAge() {return age;}@MyValue("${猪脑过载.age}")@MyAutoWiredprivate Integer age;
}
book
@MyRepository
public class Book {//BookInfo@MyValue("${jis.bookName}")@MyAutoWiredprivate String bookName;@MyValue("${jis.author}")@MyAutoWiredprivate String author;@MyValue("${jis.price}")@MyAutoWiredprivate Double price;@MyValue("${jis.description}")@MyAutoWiredprivate String description;@Overridepublic String toString() {return "Book{" +"bookName='" + bookName + ''' +", author='" + author + ''' +", price=" + price +", description='" + description + ''' +'}';}public String getDescription() {return description;}public Double getPrice() {return price;}public String getAuthor() {return author;}public String getBookName() {return bookName;}}
beanC不在扫描包底下 所以无法初始化
接口和其实现类 接口的话只要有实现类就可以实例化实现类 即通过接口也可以获得实现类对象
properties文件 主要是bean的属性注入用 名字随意 只要@value填写对应的文件名.key即可注入value
public final class MyAnnotationConfigApplicationContext {static Map<Class<?>, Object> beans = new LinkedHashMap<>();static HashSet<String> propList = new HashSet<>();static Properties props = new Properties();static String packagePath;static String parentPath;//私有构造private MyAnnotationConfigApplicationContext() {}static {
// SAXReader saxReader = new SAXReader();File checkPackage = new File(Thread.currentThread().getContextClassLoader().getResource("").getPath());getPackageName(checkPackage);
// 纯注解 不用读取配置文件中的信息了 所以读取配置文件的不需要了parentPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();//获取需要初始化bean的pathpackagePath = parentPath + place(".", "/");//根据path创建File对象File file = new File(packagePath);//获取指定包下所有类对象的全类名getClass(file);//读取配置文件getProperties();//自动注入autoWired();}//判断是否有配置类且配置类是否指定了要初始化bean的包名 必须配置配置类和扫描MyComponentScan指定初始化bean的包名才可以正常运行 否则报错private static void getPackageName(File file) {File[] files = file.listFiles();for (File file1 : files) {if (file1.isDirectory()) {getPackageName(file1);} else {if (Name().endsWith(".class")) {String absolutePath = AbsolutePath();String classes = absolutePath.split("classes")[1];classes = classes.substring(1);classes = place("\", ".");classes = classes.substring(0, classes.lastIndexOf("."));try {Class<?> aClass = Class.forName(classes);//是注解配置类 并且配置了包扫描注解 则初始化bean 否则失败if (aClass.isAnnotationPresent(MyConfiguration.class) && aClass.isAnnotationPresent(MyComponentScan.class)) {//获取扫描注所指定的包packagePath = Annotation(MyComponentScan.class).value();return;}} catch (ClassNotFoundException e) {e.printStackTrace();}}}}}//获取指定包下所有类对象的全类名private static void getClass(File file) {File[] files = file.listFiles();for (File file1 : files) {if (file1.isDirectory()) {getClass(file1);} else if (Name().endsWith(".class")) {String path = file + "\" + Name().replace(".class", "");path = path.split("classes")[1];path = place("\", ".").substring(1);//判断类对象是否有MYComponent MYController MYService MYRepository四种注解checkAnnotation(path);}}}//判断类对象是否有MYComponent MYController MYService MYRepository四种注解 有创建对象放入集合private static void checkAnnotation(String path) {try {//反射根据全类名创建对象Class<?> aClass = Class.forName(path);//检查注解if (aClass.isAnnotationPresent(MyComponent.class) ||aClass.isAnnotationPresent(MyService.class) ||aClass.isAnnotationPresent(MyController.class) ||aClass.isAnnotationPresent(MyRepository.class)) {//加了注解就创建对象 存到beans集合中 key为全类名 value是对象Object obj = wInstance();beans.put(aClass, obj);//如果这是一个实现类,则找出其实现的接口 用接口的全类名做key再存一份实现类对象 这样可以使用接口的全类名来获取实现类对象Class<?>[] interfaces = Interfaces();if (interfaces != null && interfaces.length > 0) {for (Class<?> anInterface : interfaces) {beans.put(anInterface, obj);}}}} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}//获取当前模块下resources下所有的properties文件private static void getProperties() {String path = ClassLoader().getResource("").getPath();path = path.split("target")[0];path = path + "src/main/resources";File file = new File(path);File[] files = file.listFiles();for (File file1 : files) {if (Name().endsWith("properties")) {//每找到一个文件就把其名字存入set集合中propList.Name());}}}//获取bean对象public static Object getBean(Class<?> clazz) {if ((clazz) != null) {(clazz);}return null;}//自动注入private static void autoWired() {//循环遍历整个beans集合beans.forEach((clazz, obj) -> {//拿到每个类对象中的成员属性数组Field[] declaredFields = DeclaredFields();//遍历成员属性数组for (Field field : declaredFields) {field.setAccessible(true);//判断成员属性上是否有自动注入的注解if (field.isAnnotationPresent(MyAutoWired.class)) {// 目前只做到int double和String类用value赋值// 引用数据类型类 接口 只能自动注入当前模块下存在的类 集合数组等无法注入if (field.isAnnotationPresent(MyValue.class)) {String value = Annotation(MyValue.class).value();//判断是用占位符指向properties文件中的数值还是value直接赋值if (value.startsWith("${") && dsWith("}")) {value = value.substring(2);value = value.substring(0, value.length() - 1);String propName = value.split("\.")[0];String propValue = value.split("\.")[1];propName = propName + ".properties";readProp(propName, propValue, obj, field);} else {try {if (Type().equals(String.class)) {field.set(obj, value);} else if (Type().equals(Integer.class)) {int i = Integer.parseInt(value);field.set(obj, i);} else if (Type().equals(Double.class)) {double d = Double.parseDouble(value);field.set(obj, d);}else Type().equals(Long.class)){long l = Long.parseLong(value);field.set(obj, l);}else Type().equals(Boolean.class)){boolean b = Boolean.parseBoolean(value);field.set(obj, b);} else {field.set(obj, value);}} catch (IllegalAccessException e) {e.printStackTrace();}}//是类或者接口的情况} else {String fieldName = Type().getName();try {//看看集合中有没有这个类或者接口对应的对象if (ainsKey(Class.forName(fieldName))) {field.set(obj, (Class.forName(fieldName)));}} catch (ClassNotFoundException | IllegalAccessException e) {e.printStackTrace();}}}}});}private static void readProp(String propName, String annoValue, Object obj, Field field) {for (String prop : propList) {field.setAccessible(true);//如果成员属性使用占位符指向了当前模块下的.properties文件if (propName.equals(prop)) {//读取这个文件InputStream ras = ClassLoader().getResourceAsStream(propName);try {//将文件中的内容读取到Properties集合中props.load(ras);//获取这个成员属性在文件中的key所对应的value值String property = Property(annoValue);//String value = new Property(annoValue).getBytes("ISO-8859-1"), "utf-8");//再次判断下数值类型 然后根据类型注入属性if (Type().equals(String.class)) {field.set(obj, property);} else if (Type().equals(Integer.class)) {int i = Integer.parseInt(property);field.set(obj, i);} else if (Type().equals(Double.class)) {double d = Double.parseDouble(property);field.set(obj, d);}else Type().equals(Long.class)){long l = Long.parseLong(property);field.set(obj, l);}else Type().equals(Boolean.class)){boolean b = Boolean.parseBoolean(property);field.set(obj, b);}//注入完成后清空这个Properties集合 为其他类的属性注入做准备props.clear();break;} catch (IOException | IllegalAccessException e) {e.printStackTrace();}}}}
}
beanA有成员属性beanB也会被注入 可以调用beanB的方法
Bean不在扫描范围内 所有不会被实例化
思路:
根据当前线程获取当前项目文件的绝对路径
通过递归查找所有.class文件反射创建类对象查看类上是否有MyConfiguration和MyComponentScan两个注解来获取需要初始化bean的包名
获取后递归查找这个包及其子包下的所有.class文件
反射创建类对象查看是否有MyComponent等几个注解 这里当初写的不好 可以和上面查找配置类一块递归,弄一个集合存有MyComponent这几个注解然后需要实例化的类对象保存起来 小伙伴可以自己改改 我懒得改了
有注解就创建对象 创建了不注入属性值 存到beans集合中 key为全类名 value是对象 如果是接口就找实现类放入 因为接口拿的肯定是实现类
创建完成后读取配置文件通过递归读取resources文件底下所有的properties文件这里当成也偷懒了没有写@PropertySource扫描注解指定读取properties文件 直接读取了所有的
然后进行属性注入 注入属性[判断是否加了MyValue注解才注入,然后判断是否用${}占位符指定了从配置文件中读取 如果是的话就从配置文件中找对应的key将值注入 只写了几个基本类型String Integer Double这些 判断是不是这几个类型然后注入
最后getBean方法获取对象
本文发布于:2024-02-01 01:46:09,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170672316932967.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |