Simple IOC 容器实现-基于注解

来源:互联网 发布:淘宝货源外国 编辑:程序博客网 时间:2024/04/30 15:37

上一篇文章介绍了如何基于XML文件去实现一个简单的IOC容器。但Spring不仅支持XML文件配置方式,还支持通过注解进行配置,本篇重点介绍如何通过注解来实现一个简单的IOC容器。

使用

首先,看看如何基于注解来进行依赖注入,代码如下:

import com.ricky.framework.ioc.bind.annotation.Inject;import com.ricky.ioc.sample.model.Student;import com.ricky.ioc.sample.service.StudentService;@Bean(id="studentController")public class StudentController {    @Inject//  @Inject(name="studentService")    private StudentService studentService;    public String find(long id){        Student stu = studentService.find(id);        System.out.println("stu:"+stu);        return "fin    }}

StudentService接口

import com.ricky.ioc.sample.model.Student;public interface StudentService {    public Student find(long id);}

StudentService接口实现类

import com.ricky.framework.ioc.bind.annotation.Bean;import com.ricky.framework.ioc.bind.annotation.Inject;import com.ricky.ioc.sample.dao.StudentDao;import com.ricky.ioc.sample.model.Student;import com.ricky.ioc.sample.service.StudentService;@Bean(id="studentService")public class StudentServiceImpl implements StudentService {    @Inject    private StudentDao studentDao;    @Override    public Student find(long id) {        return studentDao.find(id);    }}


以上使用了两个自定义注解:@Bean 与 @Inject。在 StudentServiceImpl 类上标注了 @Bean 注解,表示该类会交给“容器”处理,IOC容器生成的bean的id为@Bean 注解的id值。在 studentService字段上标注了 @Inject 注解,表示该字段将会被注入进来,而无需 new StudentServiceImpl (),@Inject 注解默认根据字段的名称来查找bean,同时@Inject 注解也支持通过name属性指定bean。


然后,我们需要在beans.xml配置文件中配置 自动注解 属性:

<?xml version="1.0" encoding="UTF-8"?><beans>    <!-- 要扫描的包 -->    <context:component-scan base-package="com.ricky.ioc.sample" />      <bean id="userDao" class="com.ricky.framework.ioc.dao.UserDaoImpl"></bean>    <bean id="userService" class="com.ricky.framework.ioc.service.UserServiceImpl">        <property name="userDao" ref="userDao"></property>    </bean>    <bean id="userController" class="com.ricky.framework.ioc.controller.UserController">        <property name="userService" ref="userService"></property>    </bean></beans>


到此,基于注解的IOC容器已经配置完毕了,跟Spring IOC的使用完全一致,接下来看看这个功能到底是怎样实现的吧!


实现

@Bean注解

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE })@Documentedpublic @interface Bean { // 类似spring配置文件中的bean    String id();}

@Inject注解

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.FIELD, ElementType.METHOD })@Documentedpublic @interface Inject {    public String name() default "";}

最后,我们扫描当前classpath下所有带@Bean注解的类,然后利用反射构造Bean实例对象并完成依赖注入。整个代码如下:

import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.io.IOException;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;import com.ricky.framework.ioc.bind.annotation.Bean;import com.ricky.framework.ioc.bind.annotation.Inject;import com.ricky.framework.ioc.util.ClassDetector;public class AnnotationIocContainer {    protected Map<String, Object> beanInstanceMap = new HashMap<String, Object>();    public void bind(String packageName) {        initializeBean(packageName);        inject();    }    /**     * 实例化Bean     */    private void initializeBean(String packageName) {        try {            List<Class<?>> list = new ClassDetector(packageName).detect(Bean.class);;            for (Class<?> clazz : list) {                Bean beanAnnotation = clazz.getAnnotation(Bean.class);                System.out.println("class="+clazz.getName()+",bean_id="+beanAnnotation.id());                Object bean = clazz.newInstance();  //如需通过构造函数注入,需要在此处理                beanInstanceMap.put(beanAnnotation.id(), bean);            }        } catch (IOException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }    private void inject(){        for(String beanName : beanInstanceMap.keySet()){              Object bean = beanInstanceMap.get(beanName);              if(bean!=null){                  processSetterAnnotation(bean);                processFieldAnnotation(bean);            }        }    }    /**     * 处理field上的注解     * @param bean     */      protected void processFieldAnnotation(Object bean){        try {              Field[] fields = bean.getClass().getDeclaredFields();            for(Field field : fields){                  if(field!=null && field.isAnnotationPresent(Inject.class)){                      Inject resource = field.getAnnotation(Inject.class);                      String name = resource.name();                      Object injectBean = null;                      if(name!=null&&!"".equals(name)){                          injectBean = beanInstanceMap.get(name);                      }else{                          for(String key : beanInstanceMap.keySet()){                              //判断当前属性所属的类型是否在配置文件中存在                              if(field.getType().isAssignableFrom(beanInstanceMap.get(key).getClass())){                                  //获取类型匹配的实例对象                                  injectBean = beanInstanceMap.get(key);                                  break;                            }                        }                    }                    if(injectBean!=null){                        //允许访问private字段                          field.setAccessible(true);                          //把引用对象注入属性                          field.set(bean, injectBean);                      }else{                        System.out.println("field inject failed,name="+name);                    }                }            }        } catch (Exception e) {              e.printStackTrace();        }      }    /**     * 处理set方法上的注解     * @param bean     */      protected void processSetterAnnotation(Object bean){         try {            //获取bean的属性描述器            PropertyDescriptor[] ps =                   Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();              for(PropertyDescriptor pd : ps){                  Method setter = pd.getWriteMethod();                  if(setter!=null && setter.isAnnotationPresent(Inject.class)){                      //获取当前注解,并判断name属性是否为空                      Inject resource = setter.getAnnotation(Inject.class);                      String name = resource.name();                    Object injectBean = null;                    if(name!=null&&!"".equals(name)){                        injectBean = beanInstanceMap.get(name);                    }else{ //如果当前注解没有指定name属性,则根据类型进行匹配                          for(String key : beanInstanceMap.keySet()){                            if(pd.getPropertyType().isAssignableFrom(beanInstanceMap.get(key).getClass())){                                  injectBean = beanInstanceMap.get(key);                                 break;                            }                        }                    }                    if(injectBean!=null){                        //允许访问private方法                          setter.setAccessible(true);                        //把引用对象注入属性                        setter.invoke(bean, injectBean);                    }else{                        System.out.println("setter inject failed,name="+name);                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }      }}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果手机邮件无法连接服务器怎么办 好记星平板电脑开不了机怎么办 邮箱里的文件过期了怎么办 小米手机邮件发不了怎么办 有人加我qq邮箱怎么办 文件太大发邮件太慢怎么办 爱又米发信息怎么办 手机上电子邮件己停止运行怎么办 苹果电子邮件密码忘了怎么办 玩游戏时电脑烫怎么办 qq邮箱独立密码忘记了怎么办 qq邮箱中转站容量不足怎么办 qq邮箱忘记密码了怎么办 qq邮箱超大附件过期怎么办 忘记qq邮箱独立密码怎么办 网易邮箱账号忘了怎么办 微信登录密码忘了怎么办 微信太久没登录登录不上怎么办 邮箱独立密码忘记了怎么办 苹果设置id没有邮箱怎么办 苹果手机设置id没有邮箱怎么办 邮箱的附件过期了怎么办 邮箱里的附件过期了怎么办 邮箱中附件过期了怎么办 扣扣邮箱附件过期怎么办 公司网页版邮箱进不去了怎么办 农行客户端密码忘记了怎么办 中国银行客户端密码忘记了怎么办 建行客户端登录密码忘记了怎么办 中国移动客户端密码忘记了怎么办 再歪一点授权码绑定怎么办 网易邮箱号忘了怎么办 忘记支付宝账号和密码怎么办 支付宝账号密码忘了怎么办 发邮箱文件超2g怎么办 报考计算机二级邮箱不存在怎么办 苹果邮箱登录要imap密码怎么办 注销微信支付后怎么办 手机卡注销后支付宝怎么办 12306手机邮箱都换了怎么办 网易手机邮箱手机换了怎么办