乐趣区

关于java:AutoWired属性上使用的简单实现

IOC 管制反转

本文继 java 的 spring 框架简略实现后,就 Service-ServiceImplDao--DaoImpl之间的依赖关系进行解耦,简略实现 @AutoWired 无关此局部的依赖注入(DI)。

思路

  1. 以 Dao 为例,在申明某个 Dao 时,咱们不再赋初值:
@XxgAutoWired
private UserDao userDao;
  1. UserDaoImpl 类下面退出 @XxgComponent 注解
@XxgComponent
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {...}
  1. 服务器启动,

    3.1 扫描包过程中,退出:判断类中是否有 @XxgComponent 注解,如果有,则退出咱们指定的容器

    3.2 扫描完结之后,给 MethodDefinition 创立 所在类的实例

  2. 利用反射执行 method 时,传递上一步创立的 所在类的实例

所在类的实例阐明:

次要将该实例中有 @AutoWired 注解的属性,赋予初值,每次调用办法都应用该实例,就达到了料想的成果。

赋初值的形式:

  1. Map<Class, ComponentDefinition> componentMap容器的 key 寄存 依赖类实现的接口的 class,value 寄存该类的相干属性。
  2. 获取到有 @AutoWired 注解的属性时,获取该属性的 类型 class作为key,在容器中查找该 key
  3. 容器中查到的数据,即为其实现类的相干属性
  4. 将其赋值给该实现类

问题

这种实现模式,有一个最大的问题:

一个 Dao 的接口,只能有一个对应的实现类。

其次:

依赖注入仅在 Controller 类中应用了才无效

能够应用 xml 文件来存储接口与其实现类的依赖关系。

一个接口有多个实现类时,只在须要的实现类上增加 @XxgComponent 注解

代码实现

1.XxgAutoWired 注解

/**
 * @Auther dbc
 * @Date 2020/10/15 14:40
 * @Description
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxgAutoWired {}

2. XxgComponent 注解

/**
 * @Auther dbc
 * @Date 2020/10/16 15:48
 * @Description
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxgComponent {}

3.MethodDefinition 中增加一个类的实例

private Object parentInstance; // 所属类的实例

4. 扫描类的局部批改

增加 Component 容器

private static Map<Class, ComponentDefinition> componentMap = new ConcurrentHashMap<>(); // component 类容器

ComponentDefinition

/**
 * @Auther dbc
 * @Date 2020/10/16 16:03
 * @Description 对有 @XxgComponent 注解的类 进行包装
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ComponentDefinition {
 private Class typeClazz; // 类对象
 private String typeName; // 类名
 private XxgComponent component; // component 注解
 private Class implClazz; // 实现的父类
}

批改 dealClass 办法

private void dealClass(Class clazz) throws Exception {
 // 一开始就增加
 if (clazz.isAnnotationPresent(XxgComponent.class)) {
 // 将实现类增加到容器中
 addComponent(clazz);
 return ;
 }
 ...
} 

将实现类增加到容器中的办法

​
 /**
 * 将 Component 类增加到容器中
 */
 private void addComponent(Class clazz) throws Exception {ComponentDefinition componentDefinition = new ComponentDefinition();
 componentDefinition.setComponent((XxgComponent) clazz.getAnnotation(XxgComponent.class));
 componentDefinition.setTypeClazz(clazz);
 componentDefinition.setImplClazz(clazz.getInterfaces()[0]);
 componentDefinition.setTypeName(clazz.getName());
 if (componentMap.get(componentDefinition.getImplClazz()) != null) {throw new Exception("已存在雷同的实现类" + componentDefinition.getImplClazz().getName());
 }
 componentMap.put(componentDefinition.getImplClazz(), componentDefinition);
 }
 
 /**
 * 解决 controller 的属性,主动注入值, 返回该类的一个实例
 */
 private Object dealClassField(Class clazz) {Field[] fields = clazz.getDeclaredFields();
 Object instance = null;
 try {instance = clazz.newInstance();
 } catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();
 }
 for(Field field : fields) {if (field.isAnnotationPresent(XxgAutoWired.class)) {
 // 给有 XxgAutoWired 注解的属性, 主动注入值
 field.setAccessible(true);
 try {ComponentDefinition componentDefinition = componentMap.get(field.getType());
 if (componentDefinition != null) {
 // 解决 @XxgComponent 的主动注入
 field.set(instance, dealClassField(componentDefinition.getTypeClazz()));
 }
 } catch (Exception e) {e.printStackTrace();
 }
 }
 }
 return instance;
 }

5. 调用办法时,传递扫描时存入的实例

DispatcherServlet 中, 当获取到申请执行的办法,以及拆卸好参数之后,调用该办法时,传递创立好的实例

try {Object result = methodDefinition.getMethod().invoke(methodDefinition.getParentInstance(), // 所属类的实例
  params.toArray());
  sendResponse(result, req, resp);
} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();
  sendResponse(e.getMessage(), req, resp);
}

后续

真正的 @AutoWired 太强大了,能够用在属性上,还能够用在办法上等,博主就不再深究了。

下文博主将想法设法简略实现 dao 层的动静代理,让业务逻辑与 sql 拆散。

退出移动版