【刨根问底系列】一、Tomcat启动的如何加载Spring容器

传统企业项目部署采用方式位打war,后部署到tomcat中,而且SpringBoot的横空出世,以jar包方式直接部署企业应用的,开始流行。

身为一个java开发者,对于为什么jar包的方式也可以直接启动web服务,很是费解。通过仔细研究,然后得出下列结论。注:如有读者感觉有问题,欢迎指证,我会第一时间修改。

  1. Tomcat启动war服务是会 执行下列指令
[root@localhost tomcat]# ./bin/startup.sh

一、 查看 /bin/startup.sh 文件,内容如下图:

在这里插入图片描述
如上图红框标识,startup.sh 继续运行 catalina.sh。
二、查看 catalina.sh 文件
在这里插入图片描述
查看catalina.sh 文件得知,此脚本

    "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
    //运行方法Bootstrap 类中main方法携带的参数
      -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ 
      -Djava.security.manager \
      -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
      -Dcatalina.base="$CATALINA_BASE" \
      -Dcatalina.home="$CATALINA_HOME" \
      -Djava.io.tmpdir="$CATALINA_TMPDIR" \
      org.apache.catalina.startup.Bootstrap "$@" start \
      //此方法打印日志保存到
      >> "$CATALINA_BASE"/logs/catalina.out 2>&1 &

三、反编译 tomcat/bin/bootstrap.jar ,获得org.apache.catalina.startup.Bootstrap类
查看此类代码:
1.类中main方法

 //Bootstrap类中main, 上图shell脚本会执行这个方法,并传入参数。
  public static void main(String[] args) {
        if (daemon == null) {
            daemon = new Bootstrap();

            try {
                //BootStrap类初始化信息
                daemon.init();
            } catch (Throwable var3) {
                var3.printStackTrace();
                return;
            }
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[0] = "start";
                //启动线程对象加载参数
                daemon.load(args);
                //tomcat开始启动
                daemon.start();
            } else if (command.equals("stopd")) {
                args[0] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }

2.daemon.init();方法具体内容

    public void init() throws Exception {
         //设置Catalina的路径到系统属性
        this.setCatalinaHome();
        this.setCatalinaBase();
        //初始化类加载器
        this.initClassLoaders();
        //类加载器设置到当前线程
        Thread.currentThread().setContextClassLoader(this.catalinaLoader);
        SecurityClassLoad.securityClassLoad(this.catalinaLoader);
        if (log.isDebugEnabled()) {
            log.debug("Loading startup class");
        }

        Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();
        if (log.isDebugEnabled()) {
            log.debug("Setting startup class properties");
        }

        String methodName = "setParentClassLoader";
        Class[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
        Object[] paramValues = new Object[]{this.sharedLoader};
        Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
        //使用反射机制设置父 类加载器
        method.invoke(startupInstance, paramValues);
        this.catalinaDaemon = startupInstance;
    }
  1. daemon.start(); 方法内容
    public void start() throws Exception {
        if (this.catalinaDaemon == null) {
            this.init();
        }
        //catalinaDaemon 对象 对应init方法中实例化的 org.apache.catalina.startup.Catalina
        //使用反射调用Catalina 类中中 start()方法
        Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
        method.invoke(this.catalinaDaemon, (Object[])null);
    }

4.查看Catalina类中的start方法

 public void start() {
        if (this.server == null) {
           //服务加载
            this.load();
        }

        long t1 = System.currentTimeMillis();
        if (this.server instanceof Lifecycle) {
            try {
                ((Lifecycle)this.server).start();
            } catch (LifecycleException var7) {
                log.error("Catalina.start: ", var7);
            }
        }

        long t2 = System.currentTimeMillis();
        if (log.isInfoEnabled()) {
            log.info("Server startup in " + (t2 - t1) + " ms");
        }

        try {
            if (this.useShutdownHook) {
                if (this.shutdownHook == null) {
                    this.shutdownHook = new Catalina.CatalinaShutdownHook();
                }

                Runtime.getRuntime().addShutdownHook(this.shutdownHook);
            }
        } catch (Throwable var6) {
        }

        if (this.await) {
            this.await();
            this.stop();
        }

    }

4.查看Catalina类中的load方法

    public void load() {
        this.initDirs();
        this.initNaming();
        Digester digester = this.createStartDigester();
        long t1 = System.currentTimeMillis();
        Exception ex = null;
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;

        try {
            file = this.configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource("file://" + file.getAbsolutePath());
        } catch (Exception var14) {
        }

        if (inputStream == null) {
            try {
                inputStream = this.getClass().getClassLoader().getResourceAsStream(this.getConfigFile());
                inputSource = new InputSource(this.getClass().getClassLoader().getResource(this.getConfigFile()).toString());
            } catch (Exception var13) {
            }
        }

        if (inputStream == null) {
            try {
                inputStream = this.getClass().getClassLoader().getResourceAsStream("server-embed.xml");
                inputSource = new InputSource(this.getClass().getClassLoader().getResource("server-embed.xml").toString());
            } catch (Exception var12) {
            }
        }

        if (inputStream == null && file != null) {
            log.warn("Can't load server.xml from " + file.getAbsolutePath());
        } else {
            try {
                inputSource.setByteStream((InputStream)inputStream);
                digester.push(this);
                digester.parse(inputSource);
                ((InputStream)inputStream).close();
            } catch (Exception var11) {
                log.warn("Catalina.start using " + this.getConfigFile() + ": ", var11);
                return;
            }

            this.initStreams();
            if (this.server instanceof Lifecycle) {
                try {
                   //tomcat服务初始化
                   //此处为调用Catalina父类Embedded的父类StandardService 中 initialize 方法 
                   //public class Catalina extends Embedded {
                   //public class Embedded extends StandardService implements Lifecycle {
                    this.server.initialize();
                } catch (LifecycleException var10) {
                    log.error("Catalina.start", var10);
                }
            }

            long t2 = System.currentTimeMillis();
            if (log.isInfoEnabled()) {
                log.info("Initialization processed in " + (t2 - t1) + " ms");
            }

        }
    }

5.查看 org.apache.catalina.core.StandardService;类中 initialize() 方法

 public void initialize() throws LifecycleException {
        if (this.initialized) {
            if (log.isInfoEnabled()) {
                log.info(sm.getString("standardService.initialize.initialized"));
            }

        } else {
            this.initialized = true;
            if (this.oname == null) {
                try {
                    Container engine = this.getContainer();
                    this.domain = engine.getName();
                    this.oname = new ObjectName(this.domain + ":type=Service,serviceName=" + this.name);
                    this.controller = this.oname;
                    //服务器注册主键
                    Registry.getRegistry((Object)null, (Object)null).registerComponent(this, this.oname, (String)null);
                } catch (Exception var4) {
                    log.error(sm.getString("standardService.register.failed", this.domain), var4);
                }
            }

            if (this.server == null) {
                ServerFactory.getServer().addService(this);
            }

            synchronized(this.connectors) {
                for(int i = 0; i < this.connectors.length; ++i) {
                    this.connectors[i].initialize();
                }

            }
        }
    }

查看 Registry.getRegistry((Object)null, (Object)null).registerComponent(this, this.oname, (String)null);

 public void registerComponent(Object bean, ObjectName oname, String type)
           throws Exception
    {
        if( log.isDebugEnabled() ) {
            log.debug( "Managed= "+ oname);
        }

        if( bean ==null ) {
            log.error("Null component " + oname );
            return;
        }

        try {
            if( type==null ) {
                type=bean.getClass().getName();
            }

            ManagedBean managed = findManagedBean(null, bean.getClass(), type);

            // The real mbean is created and registered
            DynamicMBean mbean = managed.createMBean(bean);

            if(  getMBeanServer().isRegistered( oname )) {
                if( log.isDebugEnabled()) {
                    log.debug("Unregistering existing component " + oname );
                }
                getMBeanServer().unregisterMBean( oname );
            }
            //这里会解析web.xml,并且会找到其中配置
            /*
                <listener>
        			<listener-class>org.springframework.web.context.ContextLoaderListener
      			  </listener-class>
   			 </listener>
             */
            getMBeanServer().registerMBean( mbean, oname);
        } catch( Exception ex) {
            log.error("Error registering " + oname, ex );
            throw ex;
        }
    }

下一篇会继续tomcat的加载过程的验证,并会陆续更新所有的spring容器加载的过程,希望读者可以仔细阅读,如果感觉描述不对的,请指正,会及时修改。
本人希望通过一步一步看源码然后,再得知整体的框架的设计的原理,这种方法虽然比较浪费时间,但是还是可以学习到知识。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值