废话不多说直接上代码
实现步骤流程
- 添加包对应的依赖
- 编写包扫描工具
- 编写注解来创建对象
- 编写容器工厂
- 编写处理器的接口
- 编写字段处理器
- 编写方法处理器(可以通过字段或方法注入)
- 编写上下文对象 (进行解耦,不让容器工厂强行依赖某个具体的实现)
- 测试
1、添加包对应的依赖
<dependencies>
<!-- 包扫描工具 -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.149</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
2、编写包扫描工具
public class ScanUtils{
public static ClassInfoList scan(String className){
// 创建对象
ClassGraph cg = new ClassGraph();
// 扫描对应的表返回结果集
ScanResult scanResult = cg.acceptPackages(packageName).scan();
// 返回结果集中所有的类
return scanResult.getAllClasses();
}
}
3、编写注解来创建对象
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
/**
* 这个value属性就是容器中的id
* @return
*/
String value();
/**
* bean的创建方式 (单例还是原型)
* 默认为单例
* @return
*/
String scope() default "singleton";
}
4、编写容器工厂
public class ContainerFactory{
// 容器工厂单例
private Map<String,Object> singletonMap = new HashMap<>();
// 容器工厂原型
private Map<String,Class<?>> prototypeMap = new HashMap<>();
}
5、编写处理器的接口
public interface InjectHandler {
/**
* 对对象进行赋值
* @param target
* @param factory
*/
void handle(Object target,ContainerFactory factory);
}
6、编写字段处理器
public class FieldInjectHandler implements InjectHandler{
/**
* 处理类字段的注入逻辑
* @param target 需要注入的实例
* @Param factory 当前工厂
*/
@Override
public void handle(Object target,ContainerFactory factory){
// 先得到实例的Class对象
Class<?> clazz = target.getClass();
// 获取所有私有字段
Field[] fields = clazz.getDeclaredFields();
// 循环遍历这些字段查看是否带有@Resource注解
for (Field field : fields) {
if (field.isAnnotationPresent(Resource.class)) {
// 找到需要注入的对象的id
String id = field.getAnnotation(Resource.class).name();
// 根据id从容器工厂中取出这个实例并赋值给这个Field字段
Object property = factory.getBean(id);
// 将对象赋值给field完成注入
setField(field,property,target);
}
}
}
/**
*
* @param field
* @param property
* @param target
*/
private void setField(Field field,Object property,Object target){
// 判断访问开放是否打开
if (!field.isAccessible()){
// 打开访问开关
field.setAccessible(true);
}
try {
// 给字段注入
field.set(target,property);
} catch (IllegalAccessException e) {
throw new RuntimeException("Inject property fail",e);
}
}
/*
* private UserService userService;
* 通过set方法赋值
* public void set(Object obj,Object value )
* 参数一: 当前类对象
* 参数二: 需要赋值的属性
* */
}
7、编写方法处理器
public class MethodInjectHandler implements InjectHandler{
@Override
public void handle(Object target, ContainerFactory factory) {
// 获取class对象
Class<?> clazz = target.getClass();
// 获取当前类的所有方法
Method[] methods = clazz.getDeclaredMethods();
// 遍历所有的方法判断是否有@Resource注解
for (Method method : methods){
// 判断method是否有@Resource注解
if (method.isAnnotationPresent(Resource.class)){
// 获取这个方法上注解的所有信息
String name = method.getAnnotation(Resource.class).name();
// 获取注解上值对应的实现类
Object obj =factory.getBean(name);
// 调用方法将值注入进去
setMethod(method,target,obj);
}
}
}
private void setMethod(Method method,Object target,Object prototype){
try {
method.invoke(target,prototype);
} catch (Exception e) {
throw new RuntimeException("method set 参数 fail!",e);
}
}
}
8、编写方法处理器
public class InjectHandlerContext {
/**
* 用于保存 InjectHandler父类的 实现类进行赋值
*/
private List<InjectHandler> handlers = new ArrayList<>();
public InjectHandlerContext(){
// 将处理器
handlers.add(new FieldInjectHandler());
handlers.add(new MethodInjectHandler());
}
/**
*
* @param target
* @param factory
* @return
*/
public void handle(Object target,ContainerFactory factory){
// 通过循环的方式进行赋值 如果类上的字段有@Resource注解则通过字段进行赋值
// 通过循环的方式进行赋值 如果类上的方法有@Resource注解则通过方法进行赋值
for (InjectHandler handle:handlers){
handle.handle(target,factory);
}
};
}
9、测试
此处只给截图了
目录结构
MVC三层架构
Conponent注解的value就是容器中的key,如果没用Scope则是单例。
在字段中设置@Resource注解或在字段中设置@Resource注解都可以 但是name的值必须是serviceImpl的@Component注解的vlaue值 不然会找不到实例,而注入失败,最后这里回事一个递归的操作,从Controller一直到Dao没用需要依赖的字段set方法.......
大概就是这样子要源码的可以私信!!!!!!