1. 程式人生 > 實用技巧 >實現簡單的IOC容器

實現簡單的IOC容器

一:Bean的定義

public interface XBeanDefinition {

    final static String SINGLETON = "singleton"; //單例
    final static String  PROTOTYPE = "prototype";   //原型

    Class<?> getBeanClass();

    boolean isSingleton();

    boolean isPrototype();

    String getInitMethodName();

}
public class GenericBeanDefinition implements
XBeanDefinition{ //初始化方法名 private String initMethodName; // private Class<?> clazz; //預設為單例模式 private String scope = XBeanDefinition.SINGLETON; public void setInitMethodName(String initMethodName) { this.initMethodName = initMethodName; } public Class<?> getClazz() {
return clazz; } public void setClazz(Class<?> clazz) { this.clazz = clazz; } public Class<?> getBeanClass() { return this.clazz; } public boolean isSingleton() { return Objects.equals(this.scope,XBeanDefinition.SINGLETON); } public
boolean isPrototype() { return Objects.equals(this.scope,XBeanDefinition.PROTOTYPE); } public String getInitMethodName() { return null; } }

二:BeanFactory

public interface XBeanFactory {

    Object getBean(String beanName);
}
public interface XBeanDefinitionRegistry {
    //註冊bean到工廠裡
    void registryBeanDefinition(String beanName, XBeanDefinition beanDefinition) throws Exception ;
    //獲得beanDefinition
    XBeanDefinition getBeanDefinition(String beanName);
    //判斷beanDefinition
    boolean containBeanDefinition(String beanName);
}

預設工廠

public class DefaultBeanFactory implements XBeanFactory,XBeanDefinitionRegistry{

    public Map<String,Object> beanMap = new ConcurrentHashMap<>(); //模擬spring中的單例池
    private Map<String,XBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); //儲存benadefinition

    @Override
    public void registryBeanDefinition(String beanName, XBeanDefinition beanDefinition)throws Exception  {
        Objects.requireNonNull(beanName,"beanName不能為空");
        if(beanDefinitionMap.containsKey(beanName))
        {
            throw new Exception("已存在【"+beanName+ "】的bean定義"+getBeanDefinition(beanName));
        }
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    @Override
    public XBeanDefinition getBeanDefinition(String beanName) {

        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containBeanDefinition(String beanName) {

        return beanDefinitionMap.containsKey(beanName);
    }

    /**
     * 獲得bean
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        try {
            return doGetBean(beanName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    private Object doGetBean(String beanName) throws Exception
    {
        Objects.requireNonNull(beanName,"beanName不能為空");
        Object instance = beanMap.get(beanName);
        if(instance != null) //先判斷單例池裡是否存在
        {

            return instance;
        }

        //先獲得beanDefinition
        XBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Class<?> clazz = beanDefinition.getBeanClass();
        //建立例項物件
        instance = clazz.newInstance();
        String methodName = beanDefinition.getInitMethodName();
        if(null != methodName)
        {
            //通過反射執行初始化方法
            Method method = clazz.getMethod(methodName, null);
            method.invoke(instance,null);
        }
        if(beanDefinition.isSingleton())
        {
            beanMap.put(beanName,instance);
        }
        return instance;
    }
}

三:定義幾個簡單的註解

@Target(ElementType.FIELD) //作用在屬性上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XAutowired {
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XBean {
    String value() default ""; //bean id

    String initMethod() default ""; //指定初始化方法名
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XComponent {
    String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XConfiguration {
    String value() default "";
}

四:啟動類

public class XSpringApplication {

    //預設為defaultBeanFactory
    private static DefaultBeanFactory beanFactory = new DefaultBeanFactory();
    /**
     * 初始化方法
     */
    public static void run(String configFilePath)
    {
        //配置檔案載入到記憶體中
        ConfigUtil.loadConfig(configFilePath);
        //獲得需要掃描的路徑
        String scanPackage = ConfigUtil.contextConfig.getProperty("scan-package");
        //掃描類
        ScannerUtil.doScanner(scanPackage);

        resolve(ScannerUtil.classNameList);
    }

    //解析類,看是否有註解
    private static void resolve(List<String> classNameList)
    {
        if(classNameList.isEmpty())
        {
            return;
        }
        try
        {
            for(String className : classNameList)
            {
                Class<?> clazz = Class.forName(className);
                if(clazz.getDeclaredAnnotations().length != 0) //判讀當前類是否有註解
                {

                    Object instance = clazz.newInstance();
                    Method[] methods = clazz.getMethods();//獲得所有方法
                    Field[] fields = clazz.getFields();//獲取所有屬性
                    String beanName = "";
                    if(clazz.isAnnotationPresent(XComponent.class))
                    {
                        beanName = clazz.getAnnotation(XComponent.class).value();

                        if(beanName.equals("")) //如果beanName為""
                        {
                            beanName = toLowerFirstCase(clazz.getSimpleName());
                        }
                    }
                    else if(clazz.isAnnotationPresent(XConfiguration.class))
                    {
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                    beanDefinition.setClazz(clazz);
                    //註冊bean
                    beanFactory.registryBeanDefinition(beanName,beanDefinition);
                    beanFactory.beanMap.put(beanName,instance);
                    listMethod(methods,instance); //遍歷所有方法
                    listFields(fields,instance); //遍歷所有屬性
                }

            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

    }
    public static Object getBean(String beanName)
    {
        return beanFactory.getBean(beanName);
    }

    /**
     * 獲取類的首字母小寫的名稱
     *
     * @param className ClassName
     * @return java.lang.String
     */
    private static String toLowerFirstCase(String className) {
        char[] charArray = className.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }
    private static String getBeanName(String className)
    {
        String[] arr = className.split("\\.");
        return toLowerFirstCase(arr[arr.length-1]);
    }

    private static void listMethod(Method[] methods  , Object instance)
    {
        try{
            for(Method method : methods) //遍歷所有方法,判斷是否使用了@Bane註解
            {
                if(method.isAnnotationPresent(XBean.class)) //判斷方法是否用XBean註解
                {
                    //獲得返回值型別
                    String type = method.getGenericReturnType().getTypeName();
                    String beanName = "";
                    String val = method.getAnnotation(XBean.class).value();
                    if(!"".equals(val))
                    {
                        beanName = val;
                    }
                    else
                    {
                        beanName = getBeanName(type);
                    }
                    Class classN = Class.forName(type);
                    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                    beanDefinition.setClazz(classN);
                    beanFactory.registryBeanDefinition(beanName,beanDefinition);
                    Object bean = method.invoke(instance, method.getParameters());//通過反射呼叫方法示例出bean
                    beanFactory.beanMap.put(beanName,bean);//直接加入beanMap中

                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

    }
    private static void listFields(Field[] fields , Object instance) throws Exception
    {
        for(Field field : fields)
        {
            if(!field.isAnnotationPresent(XAutowired.class))
            {
                continue;
            }
            else
            {
                field.setAccessible(true);
                Class<?> aClass = field.getType(); //獲得需要注入屬性的class物件

                String beanName = toLowerFirstCase(aClass.getSimpleName());

                //先判斷aclass是否已經註冊
                if(!beanFactory.containBeanDefinition(beanName))
                {

                    throw new  Exception("該屬性物件沒有在spring容器中註冊");
                }
                //該aclass已經註冊在spring中
                //判斷spring容器中是否已經存在該aclass的示例物件,如果不存在則會例項出一個物件

                Object bean = beanFactory.getBean(beanName);

                field.set(instance,bean);//注入屬性
            }
        }
    }
}

工具類

public class ConfigUtil {
    public static Properties contextConfig = new Properties();


    /**
     * 載入配置檔案到記憶體中
     * @param configPath 配置檔案路徑
     */
    public static void loadConfig(String configPath)
    {
        InputStream inputStream = ConfigUtil.class.getClassLoader().getResourceAsStream(configPath);
        try {
            contextConfig.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(inputStream != null)
            {
                try {
                    inputStream.close(); //關閉流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class ScannerUtil {

    public static List<String> classNameList = new ArrayList<>();
    /**
     * 掃描類
     * @param scanPackage
     */
    public  static void doScanner(String scanPackage)
    {

        URL resourcePath = ScannerUtil.class.getResource("/" + scanPackage.replaceAll("\\.", "/"));

        if(null == resourcePath)
        {
            return ;
        }
        File classPath = new File(resourcePath.getFile());

        //遍歷
        for(File file : classPath.listFiles())
        {
            if(file.isDirectory()) //資料夾則遞迴查詢
            {
                doScanner(scanPackage+"." + file.getName());
            }
            else
            {
                //反射判斷是否有componton註解
                if(!file.getName().endsWith(".class"))
                {
                    continue;
                }
                String className = (scanPackage + "." + file.getName()).replace(".class","");
                classNameList.add(className);
            }

        }

    }
}

五:測試

實體類

@XComponent(value = "user")
public class User {

private String uid;

private String userName;


public String getUid() {
return uid;
}

public void setUid(String uid) {
this.uid = uid;
}

@Override
public String toString() {
return "User{" +
"uid='" + uid + '\'' +
", userName='" + userName + '\'' +
'}';
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}
}

測試

@XComponent
public class Test {
    public static void main(String[] args) {
        XSpringApplication.run("application.properties");
        User user = (User)XSpringApplication.getBean("user");

//        ConfigurationTest test = (ConfigurationTest)XSpringApplication.getBean("configurationTest");
//        System.out.println(test.user);
    }

測試XBean,把User類註解註釋掉

@XConfiguration
public class ConfigurationTest {

//    @XAutowired
//    public User user;

    @XBean(value = "user")
    public User getUser()
    {
        User user = new User();
        user.setUid("123456");
        user.setUserName("swq");
        return user;
    }

    @XBean
    public Student getStudent()
    {
        return new Student();
    }
}

測試Autowired

@XConfiguration
public class ConfigurationTest {

    @XAutowired
    public User user;

    @XBean(value = "user")
    public User getUser()
    {
        User user = new User();
        user.setUid("123456");
        user.setUserName("swq");
        return user;
    }

    @XBean
    public Student getStudent()
    {
        return new Student();
    }
}
public class Test {
    public static void main(String[] args) {
        XSpringApplication.run("application.properties");
        User user = (User)XSpringApplication.getBean("user");
        System.out.println(user);
        ConfigurationTest test = (ConfigurationTest)XSpringApplication.getBean("configurationTest");
        System.out.println(test.user);
    }
}

application.properties

scan-package=com.swq  //需要掃描的類路徑

簡單實現了一個自己的ioc容器,總體思路無非是掃描類中的註解反射實現類例項,但是對自己學習spring還是很有幫助的。下次實現簡單的aop。

感覺spring中的精髓還是很難掌握,多多學習。

https://gitee.com/swqandlili/xspring