很久前写的小demo了可能有不少bug 代码逻辑方法不太精简优雅 也没有做三级缓存解决循环依赖的问题
代码:https://gitee.com/remedios0904/ByRemedios.git
先创建点自定义注解
自动注入注解 标记型的即可
@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}")
@MyAutoWired
private String name;
public String getLover() {
return dream;
}
@MyValue("毁掉一切")
@MyAutoWired
private String dream;
@MyAutoWired
BeanB 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}")
@MyAutoWired
private Integer age;
}
book
@MyRepository
public class Book {
//BookInfo
@MyValue("${jis.bookName}")
@MyAutoWired
private String bookName;
@MyValue("${jis.author}")
@MyAutoWired
private String author;
@MyValue("${jis.price}")
@MyAutoWired
private Double price;
@MyValue("${jis.description}")
@MyAutoWired
private String description;
@Override
public 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
初始化bean和属性注入的类
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的path
packagePath = parentPath + packagePath.replace(".", "/");
//根据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 (file1.getName().endsWith(".class")) {
String absolutePath = file1.getAbsolutePath();
String classes = absolutePath.split("classes")[1];
classes = classes.substring(1);
classes = classes.replace("\\", ".");
classes = classes.substring(0, classes.lastIndexOf("."));
try {
Class<?> aClass = Class.forName(classes);
//是注解配置类 并且配置了包扫描注解 则初始化bean 否则失败
if (aClass.isAnnotationPresent(MyConfiguration.class) && aClass.isAnnotationPresent(MyComponentScan.class)) {
//获取扫描注所指定的包
packagePath = aClass.getAnnotation(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 (file1.getName().endsWith(".class")) {
String path = file + "\\" + file1.getName().replace(".class", "");
path = path.split("classes")[1];
path = path.replace("\\", ".").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 = aClass.newInstance();
beans.put(aClass, obj);
//如果这是一个实现类,则找出其实现的接口 用接口的全类名做key再存一份实现类对象 这样可以使用接口的全类名来获取实现类对象
Class<?>[] interfaces = aClass.getInterfaces();
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 = MyAnnotationConfigApplicationContext.class.getClassLoader().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 (file1.getName().endsWith("properties")) {
//每找到一个文件就把其名字存入set集合中
propList.add(file1.getName());
}
}
}
//获取bean对象
public static Object getBean(Class<?> clazz) {
if (beans.get(clazz) != null) {
return beans.get(clazz);
}
return null;
}
//自动注入
private static void autoWired() {
//循环遍历整个beans集合
beans.forEach((clazz, obj) -> {
//拿到每个类对象中的成员属性数组
Field[] declaredFields = clazz.getDeclaredFields();
//遍历成员属性数组
for (Field field : declaredFields) {
field.setAccessible(true);
//判断成员属性上是否有自动注入的注解
if (field.isAnnotationPresent(MyAutoWired.class)) {
// 目前只做到int double和String类用value赋值
// 引用数据类型类 接口 只能自动注入当前模块下存在的类 集合数组等无法注入
if (field.isAnnotationPresent(MyValue.class)) {
String value = field.getAnnotation(MyValue.class).value();
//判断是用占位符指向properties文件中的数值还是value直接赋值
if (value.startsWith("${") && value.endsWith("}")) {
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 (field.getType().equals(String.class)) {
field.set(obj, value);
} else if (field.getType().equals(Integer.class)) {
int i = Integer.parseInt(value);
field.set(obj, i);
} else if (field.getType().equals(Double.class)) {
double d = Double.parseDouble(value);
field.set(obj, d);
}else if(field.getType().equals(Long.class)){
long l = Long.parseLong(value);
field.set(obj, l);
}else if(field.getType().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 = field.getType().getName();
try {
//看看集合中有没有这个类或者接口对应的对象
if (beans.containsKey(Class.forName(fieldName))) {
field.set(obj, beans.get(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 = MyAnnotationConfigApplicationContext.class.getClassLoader().getResourceAsStream(propName);
try {
//将文件中的内容读取到Properties集合中
props.load(ras);
//获取这个成员属性在文件中的key所对应的value值
String property = props.getProperty(annoValue);
//String value = new String(props.getProperty(annoValue).getBytes("ISO-8859-1"), "utf-8");
//再次判断下数值类型 然后根据类型注入属性
if (field.getType().equals(String.class)) {
field.set(obj, property);
} else if (field.getType().equals(Integer.class)) {
int i = Integer.parseInt(property);
field.set(obj, i);
} else if (field.getType().equals(Double.class)) {
double d = Double.parseDouble(property);
field.set(obj, d);
}else if(field.getType().equals(Long.class)){
long l = Long.parseLong(property);
field.set(obj, l);
}else if(field.getType().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方法获取对象
还有很多优化空间 写的菜 随便看看 一起加油提升!!!