Blade源码深入探索1--注册路由之ioc容器

最近在知乎看到一个GitHub项目,觉得适合自己学习,就打算深入研究,这里是GitHub的地址:https://github.com/biezhi/blade

附带作者大大自己写的说明:https://www.gitbook.com/book/biezhi/blade-in-action/details

依照作者写的教程,可以搭建一个简单的页面,现在开始探索blade源码,从开始注册路由开始解析。。。

根据作者自己的介绍:The Blade is the core operating class of the framework,which can be used to register routes,modify the template engine, set the file list display,static resource directory, and so on.可以知道Blade类是这个框架的核心类,可以注册路由、修改文件引擎、设置静态资源目录之类的操作。其类图大致如下:


先用me()方法创建一个新的Blade对象,blade对象里,有几个重要的属性:

/**
 * Copyright (c) 2017, biezhi 王爵 (biezhi.me@gmail.com)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.blade;

import com.blade.event.BeanProcessor;
import com.blade.event.EventListener;
import com.blade.event.EventManager;
import com.blade.event.EventType;
import com.blade.exception.BladeException;
import com.blade.ioc.Ioc;
import com.blade.ioc.SimpleIoc;
import com.blade.kit.Assert;
import com.blade.kit.BladeKit;
import com.blade.kit.IOKit;
import com.blade.kit.StringKit;
import com.blade.mvc.SessionManager;
import com.blade.mvc.handler.DefaultExceptionHandler;
import com.blade.mvc.handler.ExceptionHandler;
import com.blade.mvc.handler.RouteHandler;
import com.blade.mvc.handler.WebSocketHandler;
import com.blade.mvc.hook.WebHook;
import com.blade.mvc.http.HttpMethod;
import com.blade.mvc.http.HttpSession;
import com.blade.mvc.http.Session;
import com.blade.mvc.route.RouteMatcher;
import com.blade.mvc.ui.template.DefaultEngine;
import com.blade.mvc.ui.template.TemplateEngine;
import com.blade.server.Server;
import com.blade.server.netty.NettyServer;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;

import static com.blade.mvc.Const.*;

/**
 * Blade Core
 * <p>
 * The Blade is the core operating class of the framework,
 * which can be used to register routes,
 * modify the template engine, set the file list display,
 * static resource directory, and so on.
 *
 * @author biezhi 2017/5/31
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Blade {

    /**
     * Project middleware list,
     * the default is empty, when you use the time you can call the use of methods to add.
     * <p>
     * Blade provide you with BasicAuthMiddleware, CsrfMiddleware,
     * you can customize the implementation of some middleware
     */
    private List<WebHook> middleware = new ArrayList<>();

    /**
     * BeanProcessor list, which stores all the actions that were performed before the project was started
     */
    private List<BeanProcessor> processors = new ArrayList<>();

    /**
     * All need to be scanned by the package, when you do not set the time will scan com.blade.plugin package
     */
    private Set<String> packages = new LinkedHashSet<>(PLUGIN_PACKAGE_NAME);

    /**
     * All static resource URL prefixes,
     * defaults to "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/",
     * which are located under classpath
     */
    private Set<String> statics = new HashSet<>(DEFAULT_STATICS);

    /**
     * The default IOC container implementation
     */
    private Ioc ioc = new SimpleIoc();

    /**
     * The default template engine implementation, this is a very simple, generally not put into production
     */
    private TemplateEngine templateEngine = new DefaultEngine();

    /**
     * Event manager, which manages all the guys that will trigger events
     */
    private EventManager eventManager = new EventManager();

    /**
     * Session manager, which manages session when you enable session
     */
    private SessionManager sessionManager = new SessionManager();

    /**
     * Used to wait for the start to complete the lock
     */
    private CountDownLatch latch = new CountDownLatch(1);

    /**
     * Web server implementation, currently only netty
     */
    private Server server = new NettyServer();

    /**
     * A route matcher that matches whether a route exists
     */
    private RouteMatcher routeMatcher = new RouteMatcher();

    /**
     * Blade environment, which stores the parameters of the app.properties configuration file
     */
    private Environment environment = Environment.empty();

    /**
     * Exception handling, it will output some logs when the error is initiated
     */
    private Consumer<Exception> startupExceptionHandler = (e) -> log.error("Start blade failed", e);

    /**
     * Exception handler, default is DefaultExceptionHandler.
     * <p>
     * When you need to customize the handling of exceptions can be inherited from DefaultExceptionHandler
     */
    private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();

    /**
     * Used to identify whether the web server has started
     */
    private boolean started = false;

    /**
     * Project main class, the main category is located in the root directory of the basic package,
     * all the features will be in the sub-package below
     */
    private Class<?> bootClass = null;

    /**
     * Session implementation type, the default is HttpSession.
     * <p>
     * When you need to be able to achieve similar RedisSession
     */
    private Class<? extends Session> sessionImplType = HttpSession.class;

    /**
     * WebSocket path
     */
    private String webSocketPath;

    /**
     * Blade app start banner, default is Const.BANNER
     */
    private String bannerText;

    /**
     * Blade app start thread name, default is Const.DEFAULT_THREAD_NAME
     */
    private String threadName;

    /**
     * WebSocket Handler
     */
    private WebSocketHandler webSocketHandler;

    /**
     * Give your blade instance, from then on will get the energy
     *
     * @return return blade instance
     */
    public static Blade me() {
        return new Blade();
    }

    /**
     * Get blade ioc container, default is SimpleIoc implement.
     * <p>
     * IOC container will help you hosting Bean or component, it is actually a Map inside.
     * In the blade in a single way to make objects reuse,
     * you can save resources, to avoid the terrible memory leak
     *
     * @return return ioc container
     */
    public Ioc ioc() {
        return ioc;
    }

    /**
     * Add a get route to routes
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade get(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.GET);
        return this;
    }

    /**
     * Add a post route to routes
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade post(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.POST);
        return this;
    }

    /**
     * Add a put route to routes
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade put(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.PUT);
        return this;
    }

    /**
     * Add a delete route to routes
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade delete(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.DELETE);
        return this;
    }

    /**
     * Add a before route to routes, the before route will be executed before matching route
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade before(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.BEFORE);
        return this;
    }

    /**
     * Add a after route to routes, the before route will be executed after matching route
     *
     * @param path    your route path
     * @param handler route implement
     * @return return blade instance
     */
    public Blade after(@NonNull String path, @NonNull RouteHandler handler) {
        routeMatcher.addRoute(path, handler, HttpMethod.AFTER);
        return this;
    }

    /**
     * Setting blade mvc default templateEngine
     *
     * @param templateEngine TemplateEngine object
     * @return blade
     */
    public Blade templateEngine(@NonNull TemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
        return this;
    }

    /**
     * Get TemplateEngine, default is DefaultEngine
     *
     * @return return TemplateEngine
     */
    public TemplateEngine templateEngine() {
        return templateEngine;
    }

    /**
     * Get RouteMatcher
     *
     * @return return RouteMatcher
     */
    public RouteMatcher routeMatcher() {
        return routeMatcher;
    }

    /**
     * Register bean to ioc container
     *
     * @param bean bean object
     * @return blade
     */
    public Blade register(@NonNull Object bean) {
        ioc.addBean(bean);
        return this;
    }

    /**
     * Register bean to ioc container
     *
     * @param cls bean class, the class must provide a no args constructor
     * @return blade
     */
    public Blade register(@NonNull Class<?> cls) {
        ioc.addBean(cls);
        return this;
    }

    /**
     * Add multiple static resource file
     * the default provides the static, upload
     *
     * @param folders static resource directory
     * @return blade
     */
    public Blade addStatics(@NonNull String... folders) {
        statics.addAll(Arrays.asList(folders));
        return this;
    }

    /**
     * Set whether to show the file directory, default doesn't show
     *
     * @param fileList show the file directory
     * @return blade
     */
    public Blade showFileList(boolean fileList) {
        this.environment(ENV_KEY_STATIC_LIST, fileList);
        return this;
    }

    /**
     * Set whether open gzip, default disabled
     *
     * @param gzipEnable enabled gzip
     * @return blade
     */
    public Blade gzip(boolean gzipEnable) {
        this.environment(ENV_KEY_GZIP_ENABLE, gzipEnable);
        return this;
    }

    /**
     * Get ioc bean
     *
     * @param cls bean class type
     * @return return bean instance
     */
    public Object getBean(@NonNull Class<?> cls) {
        return ioc.getBean(cls);
    }

    /**
     * Get ExceptionHandler
     *
     * @return return ExceptionHandler
     */
    public ExceptionHandler exceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Set ExceptionHandler, when you need a custom exception handling
     *
     * @param exceptionHandler your ExceptionHandler instance
     * @return return blade instance
     */
    public Blade exceptionHandler(ExceptionHandler exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
        return this;
    }

    /**
     * Get current is developer mode
     *
     * @return return true is developer mode, else not.
     */
    public boolean devMode() {
        return environment.getBoolean(ENV_KEY_DEV_MODE, true);
    }

    /**
     * Whether encoding setting mode for developers
     * The default mode is developers
     *
     * @param devMode developer mode
     * @return blade
     */
    public Blade devMode(boolean devMode) {
        this.environment(ENV_KEY_DEV_MODE, devMode);
        return this;
    }

    public Class<?> bootClass() {
        return this.bootClass;
    }

    /**
     * Set whether to enable cors
     *
     * @param enableCors enable cors
     * @return blade
     */
    public Blade enableCors(boolean enableCors) {
        this.environment(ENV_KEY_CORS_ENABLE, enableCors);
        return this;
    }

    /**
     * Get blade statics list.
     * e.g: "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/"
     *
     * @return return statics
     */
    public Set<String> getStatics() {
        return statics;
    }

    /**
     * When set to start blade scan packages
     *
     * @param packages package name
     * @return blade
     */
    public Blade scanPackages(@NonNull String... packages) {
        this.packages.addAll(Arrays.asList(packages));
        return this;
    }

    /**
     * Get scan the package set.
     *
     * @return return packages set
     */
    public Set<String> scanPackages() {
        return packages;
    }

    /**
     * Set to start blade configuration file by default
     * Boot config properties file in classpath directory.
     * <p>
     * Without setting will read the classpath -> app.properties
     *
     * @param bootConf boot config file name
     * @return blade
     */
    public Blade bootConf(@NonNull String bootConf) {
        this.environment(ENV_KEY_BOOT_CONF, bootConf);
        return this;
    }

    /**
     * Set the environment variable for global use here
     *
     * @param key   environment key
     * @param value environment value
     * @return blade
     */
    public Blade environment(@NonNull String key, @NonNull Object value) {
        environment.set(key, value);
        return this;
    }

    public Environment environment() {
        return environment;
    }

    /**
     * Set to start the web server to monitor port, the default is 9000
     *
     * @param port web server port
     * @return blade
     */
    public Blade listen(int port) {
        Assert.greaterThan(port, 0, "server port not is negative number.");
        this.environment(ENV_KEY_SERVER_PORT, port);
        return this;
    }

    /**
     * Set to start the web server to listen the IP address and port
     * The default will listen 0.0.0.0:9000
     *
     * @param address ip address
     * @param port    web server port
     * @return blade
     */
    public Blade listen(@NonNull String address, int port) {
        Assert.greaterThan(port, 0, "server port not is negative number.");
        this.environment(ENV_KEY_SERVER_ADDRESS, address);
        this.environment(ENV_KEY_SERVER_PORT, port);
        return this;
    }

    /**
     * The use of multiple middleware, if any
     *
     * @param middleware middleware object array
     * @return blade
     */
    public Blade use(@NonNull WebHook... middleware) {
        if (!BladeKit.isEmpty(middleware)) {
            this.middleware.addAll(Arrays.asList(middleware));
        }
        return this;
    }

    /**
     * Get middleware list
     *
     * @return return middleware list
     */
    public List<WebHook> middleware() {
        return this.middleware;
    }

    /**
     * Set in the name of the app blade application
     *
     * @param appName application name
     * @return blade
     */
    public Blade appName(@NonNull String appName) {
        this.environment(ENV_KEY_APP_NAME, appName);
        return this;
    }

    /**
     * Add a event listener
     * When the trigger event is executed eventListener
     *
     * @param eventType     event type
     * @param eventListener event listener
     * @return blade
     */
    public Blade event(@NonNull EventType eventType, @NonNull EventListener eventListener) {
        eventManager.addEventListener(eventType, eventListener);
        return this;
    }

    /**
     * Get session implements Class Type
     *
     * @return return blade Session Type
     */
    public Class<? extends Session> sessionType() {
        return this.sessionImplType;
    }

    /**
     * Set session implements Class Type, e.g: RedisSession
     *
     * @param sessionImplType Session Type implement
     * @return return blade instance
     */
    public Blade sessionType(Class<? extends Session> sessionImplType) {
        this.sessionImplType = sessionImplType;
        return this;
    }

    /**
     * Event on started
     *
     * @param processor bean processor
     * @return return blade instance
     */
    public Blade onStarted(@NonNull BeanProcessor processor) {
        processors.add(processor);
        return this;
    }

    /**
     * Get processors
     *
     * @return return processors
     */
    public List<BeanProcessor> processors() {
        return processors;
    }

    /**
     * Get EventManager
     *
     * @return return EventManager
     */
    public EventManager eventManager() {
        return eventManager;
    }

    /**
     * Get SessionManager
     *
     * @return return SessionManager
     */
    public SessionManager sessionManager() {
        return sessionManager;
    }

    /**
     * Disable session, default is open
     *
     * @return return blade instance
     */
    public Blade disableSession() {
        this.sessionManager = null;
        return this;
    }

    /**
     * Start blade application.
     * <p>
     * When all the routing in the main function of situations you can use,
     * Otherwise please do not call this method.
     *
     * @return return blade instance
     */
    public Blade start() {
        return this.start(null, null);
    }

    /**
     * Start blade application
     *
     * @param mainCls main Class, the main class bag is basic package
     * @param args    command arguments
     * @return return blade instance
     */
    public Blade start(Class<?> mainCls, String... args) {
        return this.start(mainCls, DEFAULT_SERVER_ADDRESS, DEFAULT_SERVER_PORT, args);
    }

    /**
     * Start the blade web server
     *
     * @param bootClass Start the boot class, used to scan the class in all of the packages
     * @param address   web server bind ip address
     * @param port      web server bind port
     * @param args      launch parameters
     * @return blade
     */
    public Blade start(Class<?> bootClass, @NonNull String address, int port, String... args) {
        try {
            environment.set(ENV_KEY_SERVER_ADDRESS, address);
            Assert.greaterThan(port, 0, "server port not is negative number.");
            this.bootClass = bootClass;
            eventManager.fireEvent(EventType.SERVER_STARTING, this);
            Thread thread = new Thread(() -> {
                try {
                    server.start(Blade.this, args);
                    latch.countDown();
                    server.join();
                } catch (Exception e) {
                    startupExceptionHandler.accept(e);
                }
            });

            String threadName = null != this.threadName ? this.threadName : environment.get(ENV_KEY_APP_THREAD_NAME, null);
            threadName = null != threadName ? threadName : DEFAULT_THREAD_NAME;

            thread.setName(threadName);
            thread.start();
            started = true;
        } catch (Exception e) {
            startupExceptionHandler.accept(e);
        }
        return this;
    }

    /**
     * Await web server started
     *
     * @return return blade instance
     */
    public Blade await() {
        if (!started) {
            throw new IllegalStateException("Server hasn't been started. Call start() before calling this method.");
        }
        try {
            latch.await();
        } catch (Exception e) {
            log.error("await error", e);
            Thread.currentThread().interrupt();
        }
        return this;
    }

    /**
     * Stop current blade application
     * <p>
     * Will stop synchronization waiting netty service
     */
    public void stop() {
        eventManager.fireEvent(EventType.SERVER_STOPPING, this);
        server.stopAndWait();
        eventManager.fireEvent(EventType.SERVER_STOPPED, this);
    }

    /**
     * Register WebSocket path
     *
     * @param path    websocket path
     * @param handler websocket handler
     * @return return blade instance
     */
    public Blade webSocket(@NonNull String path, @NonNull WebSocketHandler handler) {
        if (null != this.webSocketHandler) {
            throw new BladeException(500, "There is already a WebSocket path.");
        }
        this.webSocketPath = path;
        this.webSocketHandler = handler;
        System.out.println(String.format("\n\t\t\t\t\t\t\t\t\t\t\t\t\t" +
                "\t\t\t\t\t Register WebSocket Path: %s\n", path));
        return this;
    }

    /**
     * Get webSocket path
     *
     * @return return websocket path
     */
    public String webSocketPath() {
        return webSocketPath;
    }

    /**
     * Set blade start banner text
     *
     * @param bannerText banner text
     * @return return blade instance
     */
    public Blade bannerText(String bannerText) {
        this.bannerText = bannerText;
        return this;
    }

    /**
     * Get banner text
     *
     * @return return blade start banner text
     */
    public String bannerText() {
        if (null != bannerText) return bannerText;
        String bannerPath = environment.get(ENV_KEY_BANNER_PATH, null);
        if (StringKit.isNotBlank(bannerPath) && Files.exists(Paths.get(bannerPath))) {
            try {
                bannerText = IOKit.readToString(bannerPath);
            } catch (Exception e) {
            }
            return bannerText;
        }
        return null;
    }

    /**
     * Set blade start thread name
     *
     * @param threadName thread name
     * @return return blade instance
     */
    public Blade threadName(String threadName) {
        this.threadName = threadName;
        return this;
    }

    /**
     * Get WebSocket Handler
     *
     * @return return websocket handler
     */
    public WebSocketHandler webSocketHandler() {
        return webSocketHandler;
    }

}

先分析Blade中构建Ioc容器的过程。在代码第99行中,有一句 private Ioc ioc = new SimpleIoc();

Blade里实现一个默认的ioc容器,将一些默认的对象存储到ioc容器中,再新建一个SimpleIoc对象,其类图如下:



SimpleIoc类

package com.blade.ioc;

import com.blade.ioc.bean.BeanDefine;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * The default IOC container implementation
 *
 * @author <a href="mailto:biezhi.me@gmail.com" target="_blank">biezhi</a>
 * @since 1.5
 */
@Slf4j
public class SimpleIoc implements Ioc {

    private final Map<String, BeanDefine> pool = new HashMap<>(32);

    /**
     * Add user-defined objects
     */
    @Override
    public void addBean(Object bean) {
        addBean(bean.getClass().getName(), bean);
    }

    /**
     * Add user-defined objects
     */
    @Override
    public void addBean(String name, Object bean) {
        BeanDefine beanDefine = new BeanDefine(bean);
        addBean(name, beanDefine);
        // add interface
        Class<?>[] interfaces = beanDefine.getType().getInterfaces();
        if (interfaces.length > 0) {
            for (Class<?> interfaceClazz : interfaces) {
                this.addBean(interfaceClazz.getName(), beanDefine);
            }
        }
    }

    /**
     * Update BeanDefine
     */
    @Override
    public void setBean(Class<?> type, Object proxyBean) {
        BeanDefine beanDefine = pool.get(type.getName());
        if (beanDefine != null) {
            beanDefine.setBean(proxyBean);
        } else {
            beanDefine = new BeanDefine(proxyBean, type);
        }
        pool.put(type.getName(), beanDefine);
    }

    /**
     * Register @Bean marked objects
     */
    @Override
    public <T> T addBean(Class<T> type) {
        Object bean = addBean(type, true);
        return type.cast(bean);
    }

    @Override
    public <T> T getBean(Class<T> type) {
        Object bean = this.getBean(type.getName());
        try {
            return type.cast(bean);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Object getBean(String name) {
        BeanDefine beanDefine = pool.get(name);
        if (beanDefine == null) {
            return null;
        }
        return beanDefine.getBean();
    }

    @Override
    public List<BeanDefine> getBeanDefines() {
        return new ArrayList<>(pool.values());
    }

    @Override
    public BeanDefine getBeanDefine(Class<?> type) {
        return this.getBeanDefine(type, true);
    }

    @Override
    public List<Object> getBeans() {
        Set<String>  beanNames = this.getBeanNames();
        List<Object> beans     = new ArrayList<>(beanNames.size());
        for (String beanName : beanNames) {
            Object bean = this.getBean(beanName);
            if (null != bean) {
                beans.add(bean);
            }
        }
        return beans;
    }

    @Override
    public Set<String> getBeanNames() {
        return pool.keySet();
    }

    @Override
    public void remove(String beanName) {
        pool.remove(beanName);
    }

    @Override
    public void remove(Class<?> type) {
        pool.remove(type.getSimpleName());
    }

    @Override
    public void clearAll() {
        pool.clear();
    }

    /**
     * Add user-defined objects
     */
    private void addBean(String name, BeanDefine beanDefine) {
        if (pool.put(name, beanDefine) != null) {
            log.warn("Duplicated Bean: {}", name);
        }
    }

    /**
     * Register @Bean marked objects
     */
    private Object addBean(Class<?> type, boolean singleton) {
        return addBean(type.getName(), type, singleton);
    }

    /**
     * Register @Bean marked objects
     */
    private Object addBean(String name, Class<?> beanClass, boolean singleton) {
        BeanDefine beanDefine = this.getBeanDefine(beanClass, singleton);

        if (pool.put(name, beanDefine) != null) {
            log.warn("Duplicated Bean: {}", name);
        }

        // add interface
        Class<?>[] interfaces = beanClass.getInterfaces();
        if (interfaces.length > 0) {
            for (Class<?> interfaceClazz : interfaces) {
                if (null != this.getBean(interfaceClazz)) {
                    break;
                }
                this.addBean(interfaceClazz.getName(), beanDefine);
            }
        }

        return beanDefine.getBean();
    }

    private BeanDefine getBeanDefine(Class<?> beanClass, boolean singleton) {
        try {
            Object object = beanClass.newInstance();
            return new BeanDefine(object, beanClass, singleton);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}


其中BeanDefine类定义了一个ioc的目标

package com.blade.ioc.bean;

/**
 * Bean Define, IOC to define a target
 *
 * @author <a href="mailto:biezhi.me@gmail.com" target="_blank">biezhi</a>
 * @since 1.5
 */
public class BeanDefine {

    private Object bean;
    private Class<?> type;
    private boolean isSingle;

    public BeanDefine(Object bean) {
        this(bean, bean.getClass());
    }

    public BeanDefine(Object bean, Class<?> type) {
        this.bean = bean;
        this.type = type;
        this.isSingle = true;
    }

    public BeanDefine(Object bean, Class<?> type, boolean isSingle) {
        this.bean = bean;
        this.type = type;
        this.isSingle = isSingle;
    }

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public boolean isSingle() {
        return isSingle;
    }

    public void setSignle(boolean isSingle) {
        this.isSingle = isSingle;
    }

}

又因为SimpleIoc类中,私有属性:

private final Map<String, BeanDefine> pool = new HashMap<>(32);

所以在启动的时候,SimpleIoc对象会先创建一个ioc的池子pool,用来实现ioc的存储Bean对象。每个pool里的对象,有三个属性:bean的名称、class类型的type、还是一个布尔值。



接下来再回到Blade中看代码,框架中先注册一个Object对象到ioc容器中,调用SimpleIoc中的addBean(Object bean)方法:

public void addBean(Object bean) {
        addBean(bean.getClass().getName(), bean);
    }
再调用addBean(String name, Object bean)方法:

public void addBean(String name, Object bean) {
        BeanDefine beanDefine = new BeanDefine(bean);
        addBean(name, beanDefine);
        // add interface
        Class<?>[] interfaces = beanDefine.getType().getInterfaces();
        if (interfaces.length > 0) {
            for (Class<?> interfaceClazz : interfaces) {
                this.addBean(interfaceClazz.getName(), beanDefine);
            }
        }
    }
先创建一个BeanDefine对象,然后调用addBean(String name, BeanDefine beanDefine)方法,向一开始定义的IOC容器pool池子中添加用户定义的对象。

再通过for循环获得这个对象所有接口,然后把这些再次用addBean(String name, BeanDefine beanDefine)方法添加进去。

那么这个方法就完成了,继续

接下来又注册一个任意类型的class,调用addBean(Class<T> type)方法:

public <T> T addBean(Class<T> type) {
        Object bean = addBean(type, true);
        return type.cast(bean);
    }
返回通过此Class对象所表示的类或接口 调用addBean(Class<?> type, boolean singleton)方法:

private Object addBean(Class<?> type, boolean singleton) {
        return addBean(type.getName(), type, singleton);
    }
再通过addBean(String name, Class<?> beanClass, boolean singleton)方法:

private Object addBean(String name, Class<?> beanClass, boolean singleton) {
        BeanDefine beanDefine = this.getBeanDefine(beanClass, singleton);

        if (pool.put(name, beanDefine) != null) {
            log.warn("Duplicated Bean: {}", name);
        }

        // add interface
        Class<?>[] interfaces = beanClass.getInterfaces();
        if (interfaces.length > 0) {
            for (Class<?> interfaceClazz : interfaces) {
                if (null != this.getBean(interfaceClazz)) {
                    break;
                }
                this.addBean(interfaceClazz.getName(), beanDefine);
            }
        }

        return beanDefine.getBean();
    }
此方法中先调用getBeanDefine(Class<?> beanClass, boolean singleton)方法:

private BeanDefine getBeanDefine(Class<?> beanClass, boolean singleton) {
        try {
            Object object = beanClass.newInstance();
            return new BeanDefine(object, beanClass, singleton);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
先将一开始从Blade传递过来的Class<?>对象,创建此Class对象所表示的类的新实例 再返回一个BeanDefine对象,

再在addBean()方法中,先检验pool中有没有这个bean,然后获取Blade传递过来的Class<?>所有的实例,然后把这些再次用addBean(String name, BeanDefine beanDefine)方法添加进IOC容器的pool池子中去。


这样这个IOC容器就可以完成存储对象的功能,其他的下次再进行分析了。。。

不过从代码中看出作者使用Java反射机制完成设计ioc,改天去研究jvm的反射机制吧。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值