<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Springboot啟動(dòng)原理

          共 18439字,需瀏覽 37分鐘

           ·

          2020-09-01 10:50

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??eternal_heathens?

          來源 |? urlify.cn/RFzYF3

          66套java從入門到精通實(shí)戰(zhàn)課程分享?

          public?static?void?main(String[] args)?{
          ????????//xxx.class:主配置類,(可以傳多個(gè))
          ????????SpringApplication.run(xxx.class, args);
          ????}

          1. 從run方法開始,創(chuàng)建SpringApplication,然后再調(diào)用run方法

          /**
          ?* ConfigurableApplicationContext(可配置的應(yīng)用程序上下文)
          ?*/

          public?static?ConfigurableApplicationContext run(Class primarySource, String... args) {
          ????//調(diào)用下面的run方法
          ????return?run(new?Class[]{primarySource}, args);
          }

          public?static?ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
          ????//primarySources:主配置類
          ????return?(new?SpringApplication(primarySources)).run(args);
          }

          2. 創(chuàng)建SpringApplication

          public?SpringApplication(Class... primarySources) {
          ????//調(diào)用下面構(gòu)造方法
          ????this((ResourceLoader) null, primarySources);
          }


          public?SpringApplication(ResourceLoader resourceLoader, Class... primarySources)?{
          ???//獲取類加載器
          ???this.resourceLoader = resourceLoader;
          ???//查看類加載器是否為空
          ???Assert.notNull(primarySources, "PrimarySources must not be null");
          ???// 保存主配置類的信息到一個(gè)哈希鏈表集合中,方便查詢調(diào)用增刪
          ???this.primarySources = new?LinkedHashSet<>(Arrays.asList(primarySources));
          ???//獲取當(dāng)前應(yīng)用的類型,是不是web應(yīng)用,見2.1
          ???this.webApplicationType = WebApplicationType.deduceFromClasspath();
          ???//從類路徑下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起來,見2.2
          ???setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
          ???//從類路徑下找到META‐INF/spring.factories配置的所有spring.ApplicationListener;然后保存起來,原理同上
          ???setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          ???//從多個(gè)SpringApplication中找到有main方法的主SpringApplication(在調(diào)run方法的時(shí)候是可以傳遞多個(gè)配置類的)
          ???//只記錄有main方法的類名,以便下一步的run
          ???this.mainApplicationClass = deduceMainApplicationClass();
          }

          2.1 deduceFromClasspath

          static?WebApplicationType deduceFromClasspath() {
          ???if?(ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
          ?????????&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
          ??????return?WebApplicationType.REACTIVE;
          ???}
          ???for?(String className : SERVLET_INDICATOR_CLASSES) {
          ??????if?(!ClassUtils.isPresent(className, null)) {
          ?????????return?WebApplicationType.NONE;
          ??????}
          ???}
          ???return?WebApplicationType.SERVLET;
          }

          2.2 getSpringFactoriesInstances

          private? Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
          ???ClassLoader classLoader = getClassLoader();
          ???// Use names and ensure unique to protect against duplicates
          ???// 配置的所有ApplicationContextInitializer等分別到一個(gè)集合中以便查詢使用,其中l(wèi)oadFactoryNames方法從類路徑下找到META‐INF/spring.factories中傳入的parameterTypes
          ???Set<String> names = new?LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
          ???// 實(shí)例化傳入的類
          ???List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
          ???// 排序以便提高針對(duì)他執(zhí)行操作的效率
          ???AnnotationAwareOrderComparator.sort(instances);
          ???return?instances;
          }

          2.3 deduceMainApplicationClass

          private?Class deduceMainApplicationClass() {
          ???try?{
          ???????// 查詢當(dāng)前的虛擬機(jī)的當(dāng)前線程的StackTrace信息
          ??????StackTraceElement[] stackTrace = new?RuntimeException().getStackTrace();
          ??????for?(StackTraceElement stackTraceElement : stackTrace) {
          ??????????// 查看當(dāng)前線程中已加載的類中有沒有main方法
          ?????????if?("main".equals(stackTraceElement.getMethodName())) {
          ???????????//有則返回該類的類名
          ????????????return?Class.forName(stackTraceElement.getClassName());
          ?????????}
          ??????}
          ???}
          ???catch?(ClassNotFoundException ex) {
          ??????// Swallow and continue
          ???}
          ???return?null;
          }


          StackTrace簡(jiǎn)述

          1 StackTrace用棧的形式保存了方法的調(diào)用信息.

          2 怎么獲取這些調(diào)用信息呢?

          可用Thread.currentThread().getStackTrace()方法得到當(dāng)前線程的StackTrace信息.該方法返回的是一個(gè)StackTraceElement數(shù)組.

          3 該StackTraceElement數(shù)組就是StackTrace中的內(nèi)容.

          4 遍歷該StackTraceElement數(shù)組.就可以看到方法間的調(diào)用流程.

          比如線程中methodA調(diào)用了methodB那么methodA先入棧methodB再入棧.

          5 在StackTraceElement數(shù)組下標(biāo)為2的元素中保存了當(dāng)前方法的所屬文件名,當(dāng)前方法所屬的類名,以及該方法的名字

          除此以外還可以獲取方法調(diào)用的行數(shù).

          6 在StackTraceElement數(shù)組下標(biāo)為3的元素中保存了當(dāng)前方法的調(diào)用者的信息和它調(diào)用時(shí)的代碼行數(shù).

          3. 調(diào)用SpringApplication對(duì)象的run方法

          public?ConfigurableApplicationContext run(String... args) {
          ????????//Simple stop watch, allowing for timing of a number of tasks, exposing totalrunning time and running time for each named task.簡(jiǎn)單來說是創(chuàng)建ioc容器的計(jì)時(shí)器
          ????????StopWatch stopWatch = new?StopWatch();
          ????????stopWatch.start();
          ????????//聲明IOC容器
          ????????ConfigurableApplicationContext context = null;
          ????????//異常報(bào)告存儲(chǔ)列表
          ????????Collection exceptionReporters = new?ArrayList();
          ????????//加載圖形化配置
          ????????this.configureHeadlessProperty();
          ????????//從類路徑下META‐INF/spring.factories獲取SpringApplicationRunListeners,原理同2中獲取ApplicationContextInitializer和ApplicationListener
          ????????SpringApplicationRunListeners listeners = this.getRunListeners(args);
          ????????//遍歷上一步獲取的所有SpringApplicationRunListener,調(diào)用其starting方法
          ????????listeners.starting();

          ????????Collection exceptionReporters;
          ????????try?{
          ????????????//封裝命令行
          ????????????ApplicationArguments applicationArguments = new?DefaultApplicationArguments(args);
          ????????????//準(zhǔn)備環(huán)境,把上面獲取到的SpringApplicationRunListeners傳過去,見3.1
          ????????????ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
          ????????????
          ????????????this.configureIgnoreBeanInfo(environment);
          ????????????//打印Banner,就是控制臺(tái)那個(gè)Spring字符畫
          ????????????Banner printedBanner = this.printBanner(environment);
          ????????????//根據(jù)當(dāng)前環(huán)境利用反射創(chuàng)建IOC容器,見3.2
          ????????????context = this.createApplicationContext();
          ????????//從類路徑下META‐INF/spring.factories獲取SpringBootExceptionReporter,原理同2中獲取ApplicationContextInitializer和ApplicationListener
          ????????????exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new?Class[]{ConfigurableApplicationContext.class}, context);
          ????????????//準(zhǔn)備IOC容器,見3.3
          ????????????this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          ????????????//刷新IOC容器,可查看配置嵌入式Servlet容器原理,所有的@Configuration和@AutoConfigutation等Bean對(duì)象全在此時(shí)加入容器中,并依據(jù)不同的選項(xiàng)創(chuàng)建了不同功能如服務(wù)器/數(shù)據(jù)庫等組件。見3.4
          ????????????//可以說@SpringbootApplication中自動(dòng)掃描包和Autoconfiguration也是在此時(shí)進(jìn)行的
          ????????????this.refreshContext(context);
          ????????????//這是一個(gè)空方法
          ????????????this.afterRefresh(context, applicationArguments);
          ????????????//停止計(jì)時(shí),打印時(shí)間
          ????????????stopWatch.stop();
          ????????????if?(this.logStartupInfo) {
          ????????????????(new?StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
          ????????????}
          ????????????//調(diào)用所有SpringApplicationRunListener的started方法
          ????????????listeners.started(context);
          ????????????//見3.5 ,從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進(jìn)行回調(diào)ApplicationRunner先回調(diào),CommandLineRunner再回調(diào)。
          ????????????this.callRunners(context, applicationArguments);
          ????????} catch?(Throwable var10) {
          ????????????this.handleRunFailure(context, var10, exceptionReporters, listeners);
          ????????????throw?new?IllegalStateException(var10);
          ????????}

          ????????try?{
          ????????????//調(diào)用所有SpringApplicationRunListener的running方法
          ????????????listeners.running(context);
          ????????????//返回創(chuàng)建好的ioc容器,啟動(dòng)完成
          ????????????return?context;
          ????????} catch?(Throwable var9) {
          ????????????this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
          ????????????throw?new?IllegalStateException(var9);
          ????????}
          ????}

          3.1?prepareEnvironment

          private?ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)?{
          ????????//獲取或者創(chuàng)建環(huán)境,有則獲取,無則創(chuàng)建
          ????????ConfigurableEnvironment environment = this.getOrCreateEnvironment();
          ????????//配置環(huán)境
          ????????this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
          ????????ConfigurationPropertySources.attach((Environment)environment);
          ????????//創(chuàng)建環(huán)境完成后,ApplicationContext創(chuàng)建前,調(diào)用前面獲取的所有SpringApplicationRunListener的environmentPrepared方法,讀取配置文件使之生效
          ????????listeners.environmentPrepared((ConfigurableEnvironment)environment);
          ????????// 環(huán)境搭建好后,需依據(jù)他改變Apllication的參數(shù),將enviroment的信息放置到Binder中,再由Binder依據(jù)不同的條件將“spring.main”SpringApplication更改為不同的環(huán)境,為后面context的創(chuàng)建搭建環(huán)境
          ????????//為什么boot要這樣做,其實(shí)咱們啟動(dòng)一個(gè)boot的時(shí)候并不是一定只有一個(gè)Application且用main方式啟動(dòng)。這樣子我們可以讀取souces配置多的Application
          ????????this.bindToSpringApplication((ConfigurableEnvironment)environment);
          ????????if?(!this.isCustomEnvironment) {
          ????????????environment = (new?EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
          ????????}

          ????????ConfigurationPropertySources.attach((Environment)environment);
          ????????//回到3,將創(chuàng)建好的environment返回
          ????????return?(ConfigurableEnvironment)environment;
          ????}

          3.2?createApplicationContext

          protected?ConfigurableApplicationContext createApplicationContext() {
          ????????//獲取當(dāng)前Application中ioc容器類
          ????????Class contextClass = this.applicationContextClass;
          ????????//若沒有則依據(jù)該應(yīng)用是否為web應(yīng)用而創(chuàng)建相應(yīng)的ioc容器
          ????????//除非為其傳入applicationContext,不然Application的默認(rèn)構(gòu)造方法并不會(huì)創(chuàng)建ioc容器
          ????????if?(contextClass == null) {
          ????????????try?{
          ????????????????switch(this.webApplicationType) {
          ????????????????case?SERVLET:
          ????????????????????contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
          ????????????????????break;
          ????????????????case?REACTIVE:
          ????????????????????contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
          ????????????????????break;
          ????????????????default:
          ????????????????????contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
          ????????????????}
          ????????????} catch?(ClassNotFoundException var3) {
          ????????????????throw?new?IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
          ????????????}
          ????????}
          ????//用bean工具類實(shí)例化ioc容器對(duì)象并返回。回到3
          ????????return?(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
          ????}

          3.3?prepareContext

          private?void?prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
          ????????//將創(chuàng)建好的環(huán)境放到IOC容器中
          ????????context.setEnvironment(environment);
          ????????//處理一些組件,沒有實(shí)現(xiàn)postProcessA接口
          ????????this.postProcessApplicationContext(context);
          ????????//獲取所有的ApplicationContextInitializer調(diào)用其initialize方法,這些ApplicationContextInitializer就是在2步驟中獲取的,見3.3.1
          ????????this.applyInitializers(context);
          ????????//回調(diào)所有的SpringApplicationRunListener的contextPrepared方法,這些SpringApplicationRunListeners是在步驟3中獲取的
          ????????listeners.contextPrepared(context);

          ????????//打印日志
          ????????if?(this.logStartupInfo) {
          ????????????this.logStartupInfo(context.getParent() == null);
          ????????????this.logStartupProfileInfo(context);
          ????????}

          ????????ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
          ????????//將一些applicationArguments注冊(cè)成容器工廠中的單例對(duì)象
          ????????beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
          ????????if?(printedBanner != null) {
          ????????????beanFactory.registerSingleton("springBootBanner", printedBanner);
          ????????}

          ????????if?(beanFactory instanceof?DefaultListableBeanFactory) {
          ????????????((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
          ????????}
          ????
          ????????//若是延遲加載,則在ioc容器中加入LazyInitializationBeanFactoryPostProcessor,
          ????????if?(this.lazyInitialization) {
          ????????????context.addBeanFactoryPostProcessor(new?LazyInitializationBeanFactoryPostProcessor());
          ????????}

          ????????//獲取所有報(bào)告primarySources在內(nèi)的所有source
          ????????Set<Object> sources = this.getAllSources();
          ????????Assert.notEmpty(sources, "Sources must not be empty");
          ????????//加載容器
          ????????this.load(context, sources.toArray(new?Object[0]));
          ????????//回調(diào)所有的SpringApplicationRunListener的contextLoaded方法
          ????????listeners.contextLoaded(context);
          ????}

          3.3.1?applyInitializers

          protected?void?applyInitializers(ConfigurableApplicationContext context)?{
          ????????Iterator var2 = this.getInitializers().iterator();

          ????????while(var2.hasNext()) {
          ????????????//將之前的所有的ApplicationContextInitializer遍歷
          ????????????ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
          ????????????//解析查看之前從spring.factories調(diào)用的ApplicationContextInitializer是否能被ioc容器調(diào)用
          ????????????Class requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
          ????????????Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
          ????????????//可以調(diào)用則對(duì)ioc容器進(jìn)行初始化(還沒加載我們自己配置的bean和AutoConfiguration那些)
          ????????????initializer.initialize(context);
          ????????}

          ????}//返回3.3

          3.4?refreshment(context)

          @Override?
          //詳情見內(nèi)置Servlet的啟動(dòng)原理,最后是用ApplicationContext的實(shí)現(xiàn)類的refresh()方法,若是web Application則為ServletWebServerApplicationContext
          public?void?refresh()?throws?BeansException, IllegalStateException {
          ???synchronized?(this.startupShutdownMonitor) {
          ??????// Prepare this context for refreshing.
          ??????prepareRefresh();

          ??????// Tell the subclass to refresh the internal bean factory.
          ??????ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

          ??????// Prepare the bean factory for use in this context.
          ??????prepareBeanFactory(beanFactory);

          ??????try?{
          ?????????// Allows post-processing of the bean factory in context subclasses.
          ?????????postProcessBeanFactory(beanFactory);

          ?????????// Invoke factory processors registered as beans in the context.
          ?????????invokeBeanFactoryPostProcessors(beanFactory);

          ?????????// Register bean processors that intercept bean creation.
          ?????????registerBeanPostProcessors(beanFactory);

          ?????????// Initialize message source for this context.
          ?????????initMessageSource();

          ?????????// Initialize event multicaster for this context.
          ?????????initApplicationEventMulticaster();

          ?????????// Initialize other special beans in specific context subclasses.
          ?????????onRefresh();

          ?????????// Check for listener beans and register them.
          ?????????registerListeners();

          ?????????// Instantiate all remaining (non-lazy-init) singletons.
          ?????????finishBeanFactoryInitialization(beanFactory);

          ?????????// Last step: publish corresponding event.
          ?????????finishRefresh();
          ??????}

          ??????catch?(BeansException ex) {
          ?????????if?(logger.isWarnEnabled()) {
          ????????????logger.warn("Exception encountered during context initialization - "?+
          ??????????????????"cancelling refresh attempt: "?+ ex);
          ?????????}

          ?????????// Destroy already created singletons to avoid dangling resources.
          ?????????destroyBeans();

          ?????????// Reset 'active' flag.
          ?????????cancelRefresh(ex);

          ?????????// Propagate exception to caller.
          ?????????throw?ex;
          ??????}

          ??????finally?{
          ?????????// Reset common introspection caches in Spring's core, since we
          ?????????// might not ever need metadata for singleton beans anymore...
          ?????????resetCommonCaches();
          ??????}
          ???}
          }

          3.5?callRunners

          private?void?callRunners(ApplicationContext context, ApplicationArguments args) {
          ????????List<Object> runners = new?ArrayList();
          ????????//將ioc容器中的的ApplicationRunner和CommandLineRunner(),在創(chuàng)建ioc容器時(shí)創(chuàng)建的
          ????????runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
          ????????runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
          ????????AnnotationAwareOrderComparator.sort(runners);
          ????????Iterator var4 = (new?LinkedHashSet(runners)).iterator();
          ????
          ????????//調(diào)用ApplicationRunner和CommandLineRunner的run方法
          ????????while(var4.hasNext()) {
          ????????????Object?runner = var4.next();
          ????????????if?(runner instanceof?ApplicationRunner) {
          ????????????????this.callRunner((ApplicationRunner)runner, args);
          ????????????}

          ????????????if?(runner instanceof?CommandLineRunner) {
          ????????????????this.callRunner((CommandLineRunner)runner, args);
          ????????????}
          ????????}

          ????}

          配置在META-INF/spring.factories

          • ApplicationContextInitializer(在我們將enviroment綁定到application后可以用來創(chuàng)建不同類型的context)

          • SpringApplicationRunListener(在Application 創(chuàng)建/運(yùn)行/銷毀等進(jìn)行一些我們想要的特殊配置)

          只需要放在ioc容器中

          • ApplicationRunner(加載沒有在ApplicationContext中的bean時(shí)讓bean能加載)

          • CommandLineRunner(命令行執(zhí)行器)

          Application初始化組件測(cè)試

          1.創(chuàng)建ApplicationContextInitializer和SpringApplicationRunListener的實(shí)現(xiàn)類,并在META-INF/spring.factories文件中配置

          public?class?TestApplicationContextInitializer?implements?ApplicationContextInitializer?{

          ????@Override
          ????public?void?initialize(ConfigurableApplicationContext configurableApplicationContext)?{
          ????????System.out.println("TestApplicationContextInitializer.initialize");
          ????}
          }


          public?class?TestSpringApplicationRunListener?implements?SpringApplicationRunListener?{
          ????@Override
          ????public?void?starting()?{
          ????????System.out.println("TestSpringApplicationRunListener.starting");
          ????}

          ????@Override
          ????public?void?environmentPrepared(ConfigurableEnvironment environment)?{
          ????????System.out.println("TestSpringApplicationRunListener.environmentPrepared");
          ????}

          ????@Override
          ????public?void?contextPrepared(ConfigurableApplicationContext context)?{
          ????????System.out.println("TestSpringApplicationRunListener.contextPrepared");
          ????}

          ????@Override
          ????public?void?contextLoaded(ConfigurableApplicationContext context)?{
          ????????System.out.println("TestSpringApplicationRunListener.contextLoaded");
          ????}

          ????@Override
          ????public?void?started(ConfigurableApplicationContext context)?{
          ????????System.out.println("TestSpringApplicationRunListener.started");
          ????}

          ????@Override
          ????public?void?running(ConfigurableApplicationContext context)?{
          ????????System.out.println("TestSpringApplicationRunListener.running");
          ????}

          ????@Override
          ????public?void?failed(ConfigurableApplicationContext context, Throwable exception)?{
          ????????System.out.println("TestSpringApplicationRunListener.failed");
          ????}
          }


          org.springframework.context.ApplicationContextInitializer=\
          cn.clboy.springbootprocess.init.TestApplicationContextInitializer

          org.springframework.boot.SpringApplicationRunListener=\
          cn.clboy.springbootprocess.init.TestSpringApplicationRunListener

          啟動(dòng)報(bào)錯(cuò):說是沒有找到帶org.springframework.boot.SpringApplication和String數(shù)組類型參數(shù)的構(gòu)造器,給TestSpringApplicationRunListener添加這樣的構(gòu)造器

          public?TestSpringApplicationRunListener(SpringApplication application,String[] args)?{
          ????}


          2.創(chuàng)建ApplicationRunner實(shí)現(xiàn)類和CommandLineRunner實(shí)現(xiàn)類,因?yàn)槭菑膇oc容器完成創(chuàng)建后中提取存放在里面的這兩個(gè)Runner,因此可以直接添加到容器中,最后callRunner使用

          @Component
          public?class?TestApplicationRunner?implements?ApplicationRunner?{

          ????@Override
          ????public?void?run(ApplicationArguments args)?throws?Exception {
          ????????System.out.println("TestApplicationRunner.run\t--->"+args);
          ????}
          }


          @Component
          public?class?TestCommandLineRunn?implements?CommandLineRunner?{

          ????@Override
          ????public?void?run(String... args)?throws?Exception {
          ????????System.out.println("TestCommandLineRunn.runt\t--->"+ Arrays.toString(args));
          ????}
          }



          粉絲福利:108本java從入門到大神精選電子書領(lǐng)取

          ???

          ?長(zhǎng)按上方鋒哥微信二維碼?2 秒
          備注「1234」即可獲取資料以及
          可以進(jìn)入java1234官方微信群



          感謝點(diǎn)贊支持下哈?

          瀏覽 82
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操精品| 午夜色视频 | 日本免费爱爱视频 | 欧美性 XX XX | 青青草在线免费观看视频 |