适合人群 (掌握java反射.复习java反射,了解SpringBoot框架的自动注入)
该练习的小项目中存在的几处问题(已经知道的)
:::info
-
扫描文件的时候Controller和Service层的顺序必须指定,在项目的结构中Service层必须在Controller层的上面(否则Cotroller层中的Service组件注入不了)
-
在controller层的类中使用的Service属性必须是实现类,不能通过接口去引入,否则报错(静态上下文中,不能引入非静态方法 解决方法:给service接口中的方法添加上static关键字)
-
Controller类中必须有有参构造器(参数类型是Service层中的类或接口) 还必须有无参构造器
-
类全名限定路径的选取的代码,不是很完善 :::
注解层
-
Autowired
-
Controller
-
Service
-
注解的详细代码注释
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 { }
:::danger 在上述代码中,我们 使用了@Target注解来指定该自定义注解只能用于类、接口或枚举类型上, 使用了@Retention注解来指定该注解在运行时可见,即可以通过反射机制获取该注解信息。 还使用了@interface关键字来定义一个注解类型,这个注解类型是一个类似接口的定义,但有一些特殊的限制。( @interface注解是一个特殊的Java注解,它用于定义自定义注解类型。) :::
@interface 注解的解释
:::danger @interface注解的作用是告诉编译器这个接口类型是一个注解类型。注解类型与普通的接口类型的不同在于,在定义注解类型时需要使用一些特殊的注解元素,例如@Target、@Retention、@Documented等,这些注解元素用于指定注解的作用目标、生存周期、注解是否包含在Java文档中等。
使用@interface注解可以定义一个标记型的接口类型,并在类、方法或其他程序元素上标记该接口类型,来表达某些额外的语义信息。例如,你可以定义一个名为@Service的注解类型,并在某个Java类上标记该注解来表示该类是一个服务类。
:::
@Service public class MyService { // 该类的内容 }
:::danger 在上述代码中,通过在MyService类上标记@Service注解,表示该类是一个服务类。在Java中,为了让注解类型生效,需要使用Java中的注解处理器或反射机制来获取注解信息并进行相关的处理。 :::
Controller层
Serrvice层
接口
接口实现类
Model层
依赖注入工具类(主要就是使用反射和注解的联合使用实现属性的注入)
/** *扫描指定包下的所有的类 动态注入 与 依赖的注入 * @param packageName */ public static void scanPackage(String packageName) { //现获取该类所在包的路径 String path = DependencyInjection.class.getResource("/" + packageName.replace(".", "/")).getPath(); System.out.println("项目所在的地址"+path); //将该类所在包下的所有类循环取出 for (File Package : new File(path).listFiles()) { String PackageName = path + "/" + Package.getName(); System.out.println("包所在的地址"+ PackageName); for (File cls : new File(PackageName).listFiles()) { System.out.println("类的名字:"+cls.getName()); String className = packageName + "."+ Package.getName() + "." + cls.getName().replace(".class", ""); System.out.println("类全限定类型名" + className); try{ Class<?> clazz = Class.forName(className); //反射第一步 //判断是否有注解标注 并且进行区分进行分别的处理 if (clazz.isAnnotationPresent(Service.class)) { Object instance = clazz.newInstance(); container.put(clazz.getName(), instance); } if (clazz.isAnnotationPresent(Controller.class)) { //直接创建出来先关的controller的实例 Object instance = clazz.newInstance(); //获取到该类中的全部属性 Field[] fields = clazz.getDeclaredFields(); //循环遍历判断是否存在Autowired注解 实现依赖的注入 for (Field field : fields) { if (field.isAnnotationPresent(Autowired.class)) { // 如果被标注 Autowride的属性是Private私有的 //那么就会通过该方法将该属性的访问级别提高 field.setAccessible(true); /** * 在使用field.getType()方法获取到该成员变量所对应的Class对象后, * 我们可以通过getName()方法获取到该Class对象的全限定类名,即类的包路径和类名的拼接字符串。 */ String fieldName = field.getType().getName(); if (container.get(fieldName) != null) { Object dependency = container.get(fieldName); //实现依赖的注入 /** * 我们需要将该实例对象设置到该对象实例中标记了该字段的位置,以实现依赖注入。 * field.set()方法接受两个参数,分别是需要设置字段值的对象实例和字段值。 * */ field.set(instance, dependency); } } } //将实例放入到容器中去 container.put(clazz.getName(), instance); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { ex.printStackTrace(); } } } } public static Object getBean(String name) { return container.get(name); } }