1、BeanDefinition
package com.csdn.myspring; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class BeanDefinition {private String beanName;private Class beanClass; }
2、掃描包的工具類MyTools
package com.csdn.myspring; import java.io.File; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class MyTools {public static Set<Class<?>> getClasses(String pack) {// 第一個class類的集合Set<Class<?>> classes = new LinkedHashSet<Class<?>>();// 是否循環迭代boolean recursive = true;// 獲取包的名字 并進行替換String packageName = pack;String packageDirName = packageName.replace('.', '/');// 定義一個枚舉的集合 并進行循環來處理這個目錄下的thingsEnumeration<URL> dirs;try {dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);// 循環迭代下去while (dirs.hasMoreElements()) {// 獲取下一個元素URL url = dirs.nextElement();// 得到協議的名稱String protocol = url.getProtocol();// 如果是以文件的形式保存在服務器上if ("file".equals(protocol)) {// 獲取包的物理路徑String filePath = URLDecoder.decode(url.getFile(), "UTF-8");// 以文件的方式掃描整個包下的文件 并添加到集合中findClassesInPackageByFile(packageName, filePath, recursive, classes);} else if ("jar".equals(protocol)) {// 如果是jar包文件// 定義一個JarFileSystem.out.println("jar類型的掃描");JarFile jar;try {// 獲取jarjar = ((JarURLConnection) url.openConnection()).getJarFile();// 從此jar包 得到一個枚舉類Enumeration<JarEntry> entries = jar.entries();findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);} catch (IOException e) {// log.error("在掃描用戶定義視圖時從jar包獲取文件出錯");e.printStackTrace();}}}} catch (IOException e) {e.printStackTrace();}return classes;}private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {// 同樣的進行循環迭代while (entries.hasMoreElements()) {// 獲取jar里的一個實體 可以是目錄 和一些jar包里的其他文件 如META-INF等文件JarEntry entry = entries.nextElement();String name = entry.getName();// 如果是以/開頭的if (name.charAt(0) == '/') {// 獲取后面的字符串name = name.substring(1);}// 如果前半部分和定義的包名相同if (name.startsWith(packageDirName)) {int idx = name.lastIndexOf('/');// 如果以"/"結尾 是一個包if (idx != -1) {// 獲取包名 把"/"替換成"."packageName = name.substring(0, idx).replace('/', '.');}// 如果可以迭代下去 并且是一個包if ((idx != -1) || recursive) {// 如果是一個.class文件 而且不是目錄if (name.endsWith(".class") && !entry.isDirectory()) {// 去掉后面的".class" 獲取真正的類名String className = name.substring(packageName.length() + 1, name.length() - 6);try {// 添加到classesclasses.add(Class.forName(packageName + '.' + className));} catch (ClassNotFoundException e) {// .error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");e.printStackTrace();}}}}}}private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {// 獲取此包的目錄 建立一個FileFile dir = new File(packagePath);// 如果不存在或者 也不是目錄就直接返回if (!dir.exists() || !dir.isDirectory()) {// log.warn("用戶定義包名 " + packageName + " 下沒有任何文件");return;}// 如果存在 就獲取包下的所有文件 包括目錄File[] dirfiles = dir.listFiles(// 自定義過濾規則 如果可以循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)(file)-> {return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));});// 循環所有文件for (File file : dirfiles) {// 如果是目錄 則繼續掃描if (file.isDirectory()) {findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);} else {// 如果是java類文件 去掉后面的.class 只留下類名String className = file.getName().substring(0, file.getName().length() - 6);try {// 添加到集合中去// classes.add(Class.forName(packageName + '.' +// className));// 經過回復同學的提醒,這里用forName有一些不好,會觸發static方法,沒有使用classLoader的load干凈classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));} catch (ClassNotFoundException e) {// log.error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");e.printStackTrace();}}}} }
3、MyAnnotationConfigApplicationContext
package com.csdn.myspring; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; public class MyAnnotationConfigApplicationContext {private Map<String, Object> ioc = new HashMap<>();public MyAnnotationConfigApplicationContext() {}public MyAnnotationConfigApplicationContext(String pack) {//遍歷包,找到目標類(原材料)Set<BeanDefinition> beanDefinitions = findBeanDefinitions(pack);//根據原材料獲取beancreateObject(beanDefinitions);//自動裝配autowireObject(beanDefinitions);}public void autowireObject(Set<BeanDefinition> beanDefinitions) {Iterator<BeanDefinition> iterator = beanDefinitions.iterator();while (iterator.hasNext()) {BeanDefinition beanDefinition = iterator.next();Class clazz = beanDefinition.getBeanClass();Field[] declaredFields = clazz.getDeclaredFields();for (Field declaredField : declaredFields) {Autowired annotation = declaredField.getAnnotation(Autowired.class);if (annotation!=null) {Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);if (qualifier!=null) {try {String beanName = qualifier.value();Object bean = getBean(beanName);String fieldName = declaredField.getName();String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);Method method = clazz.getMethod(methodName, declaredField.getType());Object object = getBean(beanDefinition.getBeanName());method.invoke(object, bean);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}Object object = getBean(beanDefinition.getBeanName());}}}}}public Set<BeanDefinition> findBeanDefinitions(String pack) {//獲取包下的所有類Set<Class<?>> classes = MyTools.getClasses(pack);Iterator<Class<?>> iterator = classes.iterator();Set<BeanDefinition> beanDefinitions = new HashSet<>();while (iterator.hasNext()) {//2、遍歷這些類,找到添加了注解的類Class<?> clazz = iterator.next();Component componentAnnotation = clazz.getAnnotation(Component.class);if (componentAnnotation != null) {//獲取Component注解的值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//獲取類名首字母小寫String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");beanName = className.substring(0, 1).toLowerCase() + className.substring(1);}//3、將這些類封裝成BeanDefinition,裝載到集合中beanDefinitions.add(new BeanDefinition(beanName, clazz));}}return beanDefinitions;}public void createObject(Set<BeanDefinition> beanDefinitions) {Iterator<BeanDefinition> iterator = beanDefinitions.iterator();while (iterator.hasNext()) {BeanDefinition beanDefinition = iterator.next();Class clazz = beanDefinition.getBeanClass();String beanName = beanDefinition.getBeanName();try {//創建對象Object object = clazz.getConstructor().newInstance();//完成屬性的賦值Field[] declaredFields = clazz.getDeclaredFields();for (Field declaredField : declaredFields) {Value valueAnnotation = declaredField.getAnnotation(Value.class);if (valueAnnotation!=null) {String value = valueAnnotation.value();String fieldName = declaredField.getName();String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);Method method = clazz.getMethod(methodName, declaredField.getType());//完成數據類型轉換Object val = null;switch (declaredField.getType().getName()) {case "java.lang.Integer" -> val=Integer.parseInt(value);case "java.lang.String"-> val = value;case "java.lang.Float"-> Float.parseFloat(value);}method.invoke(object, val);}}//存入緩存ioc.put(beanName, object);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}}public Object getBean(String beanName) {return ioc.get(beanName);}public static void main(String[] args) {MyAnnotationConfigApplicationContext myAnnotationConfigApplicationContext = new MyAnnotationConfigApplicationContext("com.csdn.myspring");Person bean = (Person) myAnnotationConfigApplicationContext.getBean("b");System.out.println(bean.getName());} }
4、@Component
package com.csdn.myspring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component {String value() default ""; }
5、@Value
package com.csdn.myspring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Value {String value(); }
6、@Autowired
package com.csdn.myspring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { }
7、@Qualifier
package com.csdn.myspring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Qualifier {String value(); }
@Qualifier是一個Spring框架的注解,用于標識一個Bean的特定實例。當有多個Bean實現了同一接口或類時,@Qualifier可以指定要使用的實例。
通常情況下,Spring框架根據類型來自動裝配依賴,但如果有多個 Bean 與依賴的類型匹配,則會產生歧義。這時就需要使用 @Qualifier 來指定具體匹配的 Bean。
例如:
public interface Animal {// ... }@Component @Qualifier("cat") public class Cat implements Animal {// ... }@Component @Qualifier("dog") public class Dog implements Animal {// ... }@Service public class AnimalService {@Autowired@Qualifier("cat")private Animal animal;// ... }
在這個例子中,AnimalService 類需要注入一個 Animal 接口的實例,但有兩個實現類 Cat 和 Dog。使用 @Qualifier 標記 Cat 和 Dog 實例,然后在 AnimalService 中使用 @Autowired 和 @Qualifier("cat") 標記,就可以明確指定注入 Cat 實例了。