Simple IOC 容器实现-基于注解

上一篇文章介绍了如何基于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 })
@Documented
public @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 })
@Documented
public @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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值