本文在建立类的实例时,是直接操做的属性,而没有用setter或者constructor方法,这是不合理的(违背了JavaBean封装的思想)。故建议在阅读本文时,仅关注实现的思路,不要参考具体实现过程,而应该使用spring的setter/constructor两种方法java
本文继java的springMvc框架简单实现后,就Service-ServiceImpl
及Dao--DaoImpl
之间的依赖关系进行解耦,简单实现@AutoWired
有关此部分的依赖注入(DI)。spring
@XxgAutoWired private UserDao userDao;
UserDaoImpl
类上面加入@XxgComponent
注解@XxgComponent public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao { ... }
3.1 扫描包过程当中,加入:判断类中是否有@XxgComponent
注解,若是有,则加入咱们指定的容器segmentfault
3.2 扫描结束以后,给MethodDefinition
建立所在类的实例。服务器
所在类的实例说明:框架
主要将该实例中有@AutoWired
注解的属性,赋予初值,每次调用方法都使用该实例,就达到了预想的效果。spa
赋初值的方式:code
Map<Class, ComponentDefinition> componentMap
容器的key存放依赖类实现的接口的class
,value存放该类的相关属性。@AutoWired
注解的属性时,获取该属性的类型class做为key,在容器中查找该key这种实现模式,有一个最大的问题:component
一个Dao的接口,只能有一个对应的实现类。xml
其次:对象
依赖注入仅在Controller类中使用了才有效
能够使用xml文件来存储接口与其实现类的依赖关系。
一个接口有多个实现类时,只在须要的实现类上添加@XxgComponent注解
/** * @Auther dbc * @Date 2020/10/15 14:40 * @Description */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface XxgAutoWired { }
/** * @Auther dbc * @Date 2020/10/16 15:48 * @Description */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface XxgComponent { }
private Object parentInstance; // 所属类的实例
private static Map<Class, ComponentDefinition> componentMap = new ConcurrentHashMap<>(); // component类容器
/** * @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; // 实现的父类 }
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; }
在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
太强大了,能够用在属性上,还能够用在方法上等,博主就再也不深究了。
下文博主将使用XML配置文件的方式实现依赖注入,并对单例模式、依赖的值类型等进行处理。