美文网首页
尝试实现IoC和AOP

尝试实现IoC和AOP

作者: Lnstark | 来源:发表于2020-03-28 23:55 被阅读0次

尝试以实现一下IoC容器和AOP的方式来学习Spring,以对Spring有更深的理解。
这只是基于当前对Spring的认识写的,实现的还是很粗糙的,感兴趣的同学可以交流一下。
IoC大致类图如下:


IoC类图

仿照SpringBoot的启动方式,在主类里

Context ctx = Application.run(App.class);

启动,run方法点进来,

    public static Context run(Class<?> clz) {
        return new Application(clz).run();
    }

先看构造方法

    private Application(Class<?> clz) {
        // 扫描包的路径
        path = clz.getResource("").getFile();
        path = path.replaceAll("%20", " ");// 替换空格
        config = new ConfigurationResolver();

        packageName = clz.getPackage().getName();
        this.clz = clz;

        context = new WebContext();
        ContextAware.setContext(context);
    }
  1. 获取启动类的路径和包名
  2. 加载配置文件,就是读取yml文件。
  3. 初始化上下文context

上下文context主要写在AbstractContext里,注入的对象就直接放在一个叫beans的HashMap里了。
这里就一些bean的add和get方法。
在看run方法:

    public Context run() {
        Scanner.getInstance().scanBeans(path, packageName);
        startServer();
        return context;
    }
  1. 扫描并注入对象
  2. 启动服务

启动服务直接用的是embed tomcat包,主要写在TomcatServer的runService方法里

     public void runService() {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(port);
        String tmpDirPath = System.getProperty("user.dir") + File.separator + WEBAPP_PATH;
        org.apache.catalina.Context ctxt = tomcat.addContext(contextPath, tmpDirPath);
        Tomcat.addServlet(ctxt, "servlet", new DispatcherServlet());
        ctxt.addServletMappingDecoded("/", "servlet");
        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
        tomcat.getServer().await();
    }

好我们主要看对象的注入,scanBeans方法里:

    public void scanBeans(String basePath, String packageName) {
        scanFiles(basePath, packageName);
        AnalyzerFactory.getAnalyzer(ClassAnalyzer.class).loadClasses(classNames);
    }
  1. scanFiles获取包下面所有的类名,放在classNames数组里
  2. 获取类解析器ClassAnalyzer并解析所有类

loadClass点进去:

public void loadClasses(List<String> classNames) {
        Class<?> clazz = null;
        // 先加载所有类,再逐个解析类的属性和方法
        for (String className : classNames) {
            try {
                clazz = loader.loadClass(className);// 默认初始化
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (clazz == null || clazz.isInterface() || clazz.isAnnotation())
                continue;
            // load class
            analyzeClass(clazz);
        }

        for (Map.Entry<Class<?>, List<Object>> entry : clzInstanceMap.entrySet()) {
            Class clz = entry.getKey();
            for (Object o : entry.getValue()) {
                analyzeFieldMethod(clz, o);// 现在就写了bean注入解析
            }
        }

        // 解析controller
        for (Object o : context.getAll()) {
            Class<?> clz = o.getClass();//entry.getKey();
            Controller c = clz.getAnnotation(Controller.class);
            if(c != null)
                MethodMappingResolver.getInstance().resolveController(clz);// 解析controller方法
        }

        // 解析AOP
        AopAnalyzer aopAnalyzer = AnalyzerFactory.getAnalyzer(AopAnalyzer.class);
        aopAnalyzer.analyze();
    }

先过一遍所有的类,把类上带注解的注入进来,然后过一遍方法,把带@Bean的注入进来。
然后是解析controller和AOP。
analyzeClass方法:

    public void analyzeClass(Class<?> clazz) {
        Annotation[] classAnnotations = clazz.getAnnotations();
        Object instance = null;
        boolean inject = false;
        String aValue = "";
        for (Annotation a : classAnnotations) {
            if (a instanceof Component) {
                aValue = ((Component) a).value();
                inject = true;
            } else if (a instanceof Controller) {
                aValue = ((Controller) a).value();
                inject = true;
            }
        }
        if(inject) {
            String name = aValue.equals("") ? firstLetterToLower(clazz.getSimpleName()) : aValue;
            instance = newInstance(clazz);
            clzInstanceMap.add(clazz, instance);
            context.addBean(name, instance);
            log.info("--------------" + name + "injected----------------");
        }
    }

这里其实不应该写判断"a instanceof Controller", 而应该判断a有没有Component注解,待修改。

github: https://github.com/Ltyro/MySpring

相关文章

网友评论

      本文标题:尝试实现IoC和AOP

      本文链接:https://www.haomeiwen.com/subject/fxseuhtx.html