前面实现了java包的扫描,扫描以后天然就到了bean的加载,以及spring mvc的一大特性 IoC依赖注入的实现;java
这里则将在以前的基础上,实现bean的加载和依赖注入的实现git
咱们模仿的轮子就是spring mvc,简化一些复杂的场景,这里只实现注解的形式github
Service
, Component
, Repository
, Bean
全部类上有上面注解的,都表示须要实例的beanspring
Autowired
表示该属性用一个bean对象来实例化spring-mvc
经过Class建立Bean对象mvc
扫描每一个Bean的属性,若包含 @Autowired
注解,则用bean进行赋值ui
最多见的根据beanName,bean类型来获取Bean.net
好比业务方依赖第三方的jar包中的某个类,想将它也注册为一个bean,由于不能修改第三方类,因此能够用动态注册的方式来加载bean设计
这个比较简单,直接贴一下几个相关的注解code
几个声明类为Bean的注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Bean { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Component { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Service { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Repository { String value() default ""; }
Autowired.java
, value对应的是业务方定义的beanName
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { String value() default ""; }
咱们定义一个 BeanFactory
对象,来管理class的加载,bean的建立
扫描包基本上就是以前一篇博文的内容,不作多说,直接看Bean的实例化
实现思路比较清晰,大体流程以下;
class
上是否有几个定义为Bean的注解为了不每次使用时都扫描一遍,因此这个扫描的结果会保存下来,放在内存中
/** * 全部自动实例化的bean的映射表, * key为bean name * - (若是注解中有指定value值,则bean name就是value值;若没有指定,则是首字母小写的简单类名) * - bean name 区分大小写 * <p> * 为了不bean name相同的问题,将value也保存为一个Map映射表 */ private Map<String, Map<Class, Object>> nameBeanMap; /** * class到bean的映射表 */ private Map<Class, Object> clzBeanMap;
实际加载过程以下
/** * 实例化自动加载的bean * * @return */ private Map<String, Map<Class, Object>> instanceBean() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { nameBeanMap = new ConcurrentHashMap<>(); clzBeanMap = new ConcurrentHashMap<>(); Annotation[] typeAnos; String tmpBeanName; Method tmpMethod; Object tmpBean; Map<Class, Object> tmpClzMap; for (Class clz : beanClasses) { if (clz.isInterface()) { continue; } // 获取类上注解 typeAnos = clz.getAnnotations(); if (typeAnos.length == 0) { continue; } for (Annotation ano : typeAnos) { if (ano instanceof Bean || ano.annotationType().isAnnotationPresent(Bean.class)) { // 须要加载bean tmpMethod = ano.annotationType().getMethod("value", null); if (tmpMethod != null) { tmpBeanName = (String) tmpMethod.invoke(ano, null); } else { tmpBeanName = null; } if (StringUtils.isEmpty(tmpBeanName)) { tmpBeanName = StrUtil.lowerFirstChar(clz.getSimpleName()); } if (nameBeanMap.containsKey(tmpBeanName)) { tmpClzMap = nameBeanMap.get(tmpBeanName); } else { tmpClzMap = new ConcurrentHashMap<>(); } if (tmpClzMap.containsKey(clz)) { throw new BeanAlreadyDefinedException("bean " + tmpBeanName + " class: " + clz.getName() + " has already defined!"); } tmpBean = clz.newInstance(); tmpClzMap.put(clz, tmpBean); clzBeanMap.put(clz, tmpBean); nameBeanMap.put(tmpBeanName, tmpClzMap); break; } } } return nameBeanMap; }
上面的实现比较简单,惟一须要注意下的是判断是否包含咱们期待的几个注解, 不知道是否有更优雅的写法,下面这种对于自定义一个注解,上面加上 @Service
的状况时,将不太适用
if(ano instanceof Bean || ano.annotationType().isAnnotationPresent(Bean.class)) { // xxx }
其次就是BeanName
的生成规则
这个也比较简单,扫描每一个bean的属性,将拥有 @Autowired
注解的拎出来, 而后查对应的Bean,赋值便可
/** * 依赖注入 */ private void ioc() throws IllegalAccessException { Field[] fields; String beanName; Object bean; for (Object obj : nameBeanMap.values()) { fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { if (!field.isAnnotationPresent(Autowired.class)) { continue; } Autowired autowired = field.getAnnotation(Autowired.class); beanName = StringUtils.isBlank(autowired.value()) ? StrUtil.lowerFirstChar(field.getName()) : autowired.value(); bean = nameBeanMap.get(beanName); if (bean == null) { throw new BeanNotFoundException("bean: " + beanName + " not found! bean class: " + field.getClass().getName()); } field.setAccessible(true); field.set(obj, nameBeanMap.get(beanName)); } } }
属性赋值,关注下两行代码便可
// 强制设置可访问,这样私有的变量也能够修改其内容了 field.setAccessible(true); field.set(obj, nameBeanMap.get(beanName));
查询的几个接口就比较简单了,单纯的从Map中获取对象; 注册也就是向Map中塞对象
源码地址: https://github.com/liuyueyi/quick-mvc
相关博文:
我的博客:一灰的我的博客
公众号获取更多: