刚学java半年 自己写个简单的Spring

第一周--- 2020 08 12

刚刚上班基本没事做,太无聊了。也不能连外网,异想天开自己写个Spring?

这是第一天,按照自己的理解简单的实现了注入功能,离目标还有很大差距。

1.新建annotation包,新建如下注解

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

Public @interface AutoWired{}

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

Public @interface Component{}

作用想必没人不知道。注意@Retention必须选择RUNTIME否则反射无法读取。

 

2.新建bean包,新建如下类

Public class BeanDefine{

Private String name;

Private Object bean;

省略了setget和get方法。这是对要注入到容器的bean的一层简单封装。其实就是取代了键值对

 

3.新建utils表,新建如下类

public class StringUtil {

    /**

     * 将一个字符串的首字母变小写

     */

    public static String firstToLowerCase(String str) {

        StringBuilder builder = new StringBuilder();

        char[] arr = str.toCharArray();

        if (arr[0] < 65 || arr[0] > 90) {

            throw new IllegalArgumentException(str);

        }

        arr[0] += 32;

        return builder.append(arr).toString();

    }

}

还有个ReflectUtil,目前只有一个功能就是找出一个包下的所有类。参考自

https://blog.csdn.net/qq_30285985/article/details/102988013

public class ReflectUtil {

    /**

     * find all classes in a pack

     */

    public static Set<Class<?>> getClasses(String pack, boolean recursive) throws Exception {

        Set<Class<?>> classes = new LinkedHashSet<>();

        pack = pack.replace(".", "/");

        Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(pack);

        while (dirs.hasMoreElements()) {

            URL url = dirs.nextElement();

            String protocol = url.getProtocol();

            if ("file".equals(protocol)) {

                String filePath = URLDecoder.decode(url.getFile(), "utf8");

                findClassesByFile(pack, filePath, recursive, classes);

            } else {

                System.out.println("this is jar");

            }

        }

        return classes;

    }

 

    private static void findClassesByFile(String pack, String packPath, final boolean recursive, Set<Class<?>> classes) throws ClassNotFoundException {

        File dir = new File(packPath);

        if (!dir.exists() || !dir.isDirectory()) {

            return;

        }

        File[] dirs = dir.listFiles(pathname -> recursive && pathname.isDirectory() || pathname.getName().endsWith(".class"));

        if (dirs != null) {

            for (File file : dirs) {

                if (file.isDirectory()) {

                    findClassesByFile(pack + "." + file.getName(), file.getAbsolutePath(), recursive, classes);

                } else {

                    String className = file.getName().substring(0, file.getName().length() - 6);

                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(pack.replace("/", ".") + "." + className));

                }

            }

        }

    }

}

4.新建context包,新建如下接口

public interface Context {

    Object getBean(Class<?> c);

 

    Object getBean(String name);

}

包含了基本的取出bean的功能。

在包下新建impl包新建实现类,

private static final Logger logger = LoggerFactory.getLogger(ApplicationContext.class);

    private static final Map<Class<?>, BeanDefine> beanMap = new LinkedHashMap<>();

用map作为容器。

在构造方法中初始化容器。

public ApplicationContext() {

        try {

            Properties prop = new Properties();

            prop.load(ApplicationContext.class.getClassLoader().getResourceAsStream("application.properties"));

            Set<Class<?>> classes = ReflectUtil.getClasses(prop.getProperty("component-scan"), true);

            for (Class<?> aClass : classes) {

                setBean(aClass);

            }

            for (Class<?> aClass : classes) {

                setField(aClass);

            }

        } catch (IllegalArgumentException e) {

            logger.error("***** {}", e.getMessage());

            logger.error("***** help : maybe lost @Component on class needed autowired");

            e.printStackTrace();

        } catch (Exception e) {

            logger.error("***** something wrong in create context : {}", e.getMessage());

            e.printStackTrace();

        }

    }

其中的部分方法如下

private void setBean(Class<?> c) throws IllegalAccessException, InstantiationException {

        if (c.isAnnotationPresent(Component.class)) {

            Object instance = c.newInstance();

            beanMap.put(c, new BeanDefine(StringUtil.firstToLowerCase(c.getSimpleName()), instance));

        }

    }

显然这是向容器中注入带有@Component注解的类的实例。

private void setField(Class<?> c) throws IllegalAccessException {

        Field[] fields = c.getDeclaredFields();

        for (Field field : fields) {

            if (field.isAnnotationPresent(AutoWired.class)) {

                Class<?> type = field.getType();

                Object bean = getBean(type);

                if (bean == null) {

                    throw new IllegalArgumentException("can not autowired not find bean in context classed : " + type.getName());

                }

                field.setAccessible(true);

                field.set(getBean(c), bean);

            }

        }

    }

这是往已经注入容器的bean设置带有@Autowired注解的属性。

@Override

    public Object getBean(Class<?> c) {

        for (Map.Entry<Class<?>, BeanDefine> entry : beanMap.entrySet()) {

            if (entry.getKey().equals(c)) {

                return entry.getValue().getBean();

            }

        }

        return null;

    }

 

    @Override

    public Object getBean(String name) {

        for (Map.Entry<Class<?>, BeanDefine> entry : beanMap.entrySet()) {

            if (name.equals(entry.getValue().getName())) {

                return entry.getValue().getBean();

            }

        }

        return null;

    }

这就是实现接口的通过名称和类型取出bean的两个方法。

5.至此,今天的任务基本完成。spring容器已经有了基本的注入功能。建立test包,新建测试类。

@Component

public class Test {

 

    @AutoWired

    private Wired wired;

 

    public void test() {

        System.out.println(wired);

        wired.wired();

    }

}

 

@Component

public class Wired {

    public void wired() {

        System.out.println("wired success!!!");

    }

}

在ApplicationContext类中新建主方法测试。

public static void main(String[] args) {

        ApplicationContext context = new ApplicationContext();

        System.out.println(ApplicationContext.beanMap);

        Test t1 = (Test) context.getBean("test");

        Test t2 = (Test) context.getBean(Test.class);

        System.out.println(t1 == t2);

        t1.test();

        t2.test();

    }

运行测试

 

成功!!!

遇到异常情况,如Wired类未加@Component注解,也有提示。

 

明天上班再想想可以添加什么,,,,,,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值