Android 系统启动 <System server> 服务 [3]

Read The Fucking Source Code. —— Linus

站在’巨人’的肩膀上开始自己的旅途。—— 佚名

愉快的周末,从打开💻开始,到骑行归来结束。—— 佚名

在这里插入图片描述

文章系列

注: 本系列文章源码基于 Android 11-r21 master 分支

相关文件

  • /framework/base/service/java/com/android/server/SystemServer.java
  • /framework/base/service/java/com/android/server/SystemServiceManager.java
  • /framework/base/service/java/com/android/server/WatchDog.java
  • /frameworks/base/core/java/com/android/server/SystemConfig.java
  • … etc

执行 System server

经过前两篇的系统文章,已经完成了 init、zygote 进程的创建和初始化,即将启动系统各大服务,各项服务由服务管理者 System server 完成创建和启动,那么赶紧进入瞧瞧去/framework/base/service/java/com/android/server/SystemServer.java

//SystemServer.java
public final class SystemServer implements Dumpable {

    //列举几个认为重要的成员
    
   //类似这样的服务名称不少于 50 个
    private static final String WIFI_SERVICE_CLASS =
            "com.android.server.wifi.WifiService";                  //Wi-Fi服务
    private static final String WIFI_SCANNING_SERVICE_CLASS =
            "com.android.server.wifi.scanner.WifiScanningService";  //Wi-Fi 扫描
    private static final String ALARM_MANAGER_SERVICE_CLASS =
            "com.android.server.alarm.AlarmManagerService";         //闹钟
    ... etc
    
    //系统默认的对话框等主题
    private static final int DEFAULT_SYSTEM_THEME =
           com.android.internal.R.style.Theme_DeviceDefault_System;
    
    //很重要的一个主角,系统服务管理者
    private SystemServiceManager mSystemServiceManager;

    //具体服务实现类
    private ActivityManagerService mActivityManagerService;  //Activity 管理
    private PackageManagerService mPackageManagerService;    //安装包管理
    private ContentResolver mContentResolver;                //内容解析——内容提供者数据解析
    private DisplayManagerService mDisplayManagerService;    //设备显示相关

    //看变量名就很清晰,application 错误报告信息记录
    private static LinkedList<Pair<String, ApplicationErrorReport.CrashInfo>> sPendingWtfs;

    //memtracker:memory tracker 【参考链接】
    private static native void startMemtrackProxyService();

    //Hidl Hardware Interface Definition Language 硬件抽象层语言【参考链接】
    private static native void startHidlServices();

    //调试模式下我们应用进程能够 dump head,dump file 是进程的内存镜像,把进程当前保存的状态保存到 dump 文件
    //head dump 其实利用 Android studio 内置的工具(Android profile、Memory profile)也是可以生成的,直接帮你把文件可视化
    //生成的文件保存路径 /data/system/heapdump/
    private static native void initZygoteChildHeapProfiling();


    //从主函数开始执行
    public static void main(String[] args) {
        new SystemServer().run();
    }
}
//SystemServer
public SystemServer() {
    //记录性信息,不影响继续阅读吧,主要还是后面的 run 方法

    mFactoryTestMode = FactoryTest.getMode();
    mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1;
    mRuntimeStartElapsedTime = SystemClock.elapsedRealtime();
    mRuntimeStartUptime = SystemClock.uptimeMillis();
    Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime);

    mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
}
//SystemServer.java
private void run() {

    //通过设置系统属性,记录进程启动信息,启动次数等
    SystemProperties.set(SYSPROP_START_COUNT,String.valueOf(mStartCount));
    //如果当前时区无效将被设置为默认值 GMT —— 格林威治时间
    SystemProperties.set("persist.sys.timezone", "GMT");
    //如果知道了语言,尝试通过语言设置地区属性
    //【presist 前缀的系统属性是可写的,ro 前缀的系统属性是只读的】
    if (!SystemProperties.get("persist.sys.language").isEmpty()) {
        final String languageTag = Locale.getDefault().toLanguageTag();
        SystemProperties.set("persist.sys.locale", languageTag); 
    }
    
    /*前面都是在设置一些系统属性,接下来将要真正启动服务*/
    
    //SystemClock.elapsedRealtime();  获取设备启动到当前的毫秒值
    //清理一次内存,为 application 可以分配得到更多内存
    VMRuntime.getRuntime().clearGrowthLimit();

    //启动准备线程策略 THREAD_PRIORITY_FOREGROUND:应用程序不得更改线程优先级、不得更改线程数量,这些将由系统自动调整
    android.os.Process.setThreadPriority(
        android.os.Process.THREAD_PRIORITY_FOREGROUND); 
    //这是对前一条语句设置前台进程的限制 THREAD_PRIORITY_FOREGROUND,这里传入 false,如果此前设置的线程策略是‘后台进程组’将抛出异常
    android.os.Process.setCanSelfBackground(false);

    //Looper 是线程的消息循环机制,这里的线程自然是执行的主线程 Main-Thread,也就是 application 所在线程
    //这里有一个经典八股文😊:自定义子线程 Looper 需要手动执行 prepareLooper,为什么我们在使用主线程的 Looper 前不需要先调用 prepare Looper?
    //       A答:调用时肯定要调用的。这不是废话吗,我们不调用那肯定是‘别人’已经在我们使用前就调用了————那这个‘别人’其实就是 Android env 在创建时候调用了
    //这个 application main loop 被系统缓存在 Looper.java 中(static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();)
    //我们平时使用是通过: Looper.getMainLooper()
    Looper.prepareMainLooper();
    Looper.getMainLooper().setSlowLogThresholdMs(
        SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);

    //创建系统上下文 mSystemContext 以及设置上述的默认主题
    createSystemContext();
    //继续
    mSystemServiceManager = new SystemServiceManager(mSystemContext);

    //启动读取系统全局配置的线程池,下面启动服务启动时候将会用到
    //什么时候关闭线程池呢?if (phase == SystemService.PHASE_BOOT_COMPLETED){SystemServerInitThreadPool.shutdown();}
    SystemServerInitThreadPool tp = SystemServerInitThreadPool.start();

    //【重点、重点、重点】
    //启动各种 Service
    startBootstrapServices(t);  //启动服务
    startCoreServices(t);       //核心服务
    startOtherServices(t);      //其他服务
    
    //一个检测工具
    StrictMode.initVmDefaults(null);
    //进入一个无眠的世界默默`打工`,为‘人民’服务
    /*
        1、获得当前线程的消息队列 MessageQueue
        2、在 for(;;) 循环中不断从消息队列取出消息,queue.next(),这是一个阻塞的过程
        3、如果获取到可用的消息则进行分发 msg.target.dispatchMessage(msg)
        4、消息分发之后进行回收或清理 msg.recycleUnChecked()
    */
    Looper.loop();
}

启动 Bootstrap 服务

//SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {

    //【1】watchdog 看门🐶?看着是有这个意思,大概就是监控服务
    /*
        1、它运行在一个单独的线程中 mThread = new Thread(this::run, "watchdog");
        2、主要是检查一些线程的运行状态和调度情况,比如检查的线程有前台线程、IO、UI、main、display、animation、surface animation 等线程
    */
    final Watchdog watchdog = Watchdog.getInstance();
    watchdog.start();

    //【2】加载全局系统配置信息
    /*
        1、此线程池在 SystemServer 启动时候执行
        2、在 SystemServer 启动完成之后关闭 SystemService.PHASE_BOOT_COMPLETE
        3、调用 submit 方法真正执行系统全局配置读取的方法在哪里?线程池提交之后执行的当然是 run 方法。谁的 run 方法?SystemConfig::getInstance 又是啥,在哪里?【不解,知者欢迎评论】
        4、SystemConfig::getInstance:Java 能够使用双引号访问静态方法,在此之前我只知道 cpp 是可以这样的,后来查了一下似乎是 lambada 的语法糖😺不知者无罪
        5、readPublicNativeLibrariesList();//String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc","vendor/etc"};读取此目录下 public.libraries- 开头,.txt 结尾的配置文件
        6、readAllPermissions();//解析根目录、Vendor目录等 etc/sysconfig、etc/permission 下 XML 权限文件
    */
    final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
    SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);

    //【3】公共服务
    /*
        1、将来 ActivityManagerService、PackageManagerService ...etc 会使用
        2、调用 addService 一看流程最终到了 IServiceManager.cpp 藏得这么深,真是服了这个老 six,sp<AidlServiceManager> mTheRealServiceManager;   
    */
    PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
    ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
    ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
        new PlatformCompatNative(platformCompat));

    //【4】文件完整性校验相关服务
    mSystemServiceManager.startService(FileIntegrityService.class);

    //【5】应用程序安装相关服务
    Installer installer = mSystemServiceManager.startService(Installer.class);

    //【6】设备标识访问策略服务
    /*
        1、获取手机序列号:getSerial(),系统属性对应键 ro.serialn。调用时
elephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers
        2、指定包名获取序列号 getSerialForPackage,其中再根据包名 + 当前用户ID获取调用此类的 UID,所以猜测还有有不少限制的,比如限制非 root 用户、限制非系统应用
    */
    SystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
    
    //【7】URI 授权管理服务 (关于 Uri 可参考链接)
    mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
    
    //【8】电池相关服务
    /*
        1、电量🔋触发器BatteryTrigger mBatteryTrigger,通过广播监听电量变化,当电量下降1%将会接收到广播,似乎只做一件事:就是更新最新电量信息
        2、IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 通过 context 注册广播
    */
    mSystemServiceManager.startService(PowerStatsService.class);

    //【9】内存分析服务  (native 调用)
    startMemtrackProxyService();
    
    //【10】AMS 服务(终于看到一个比较常见的🐶)
    /*
        1、高版本任务栈管理类似乎被分离出来了,由 ActivityTaskManagerService 实现,内容太多,下次一定看看
    */
    ActivityTaskManagerService atm = mSystemServiceManager.startService(
        ActivityTaskManagerService.Lifecycle.class).getService();
    mActivityManagerService = ActivityManagerService.Lifecycle.startService(
        mSystemServiceManager, atm);

    //【11】数据加载
    mDataLoaderManagerService = mSystemServiceManager.startService(
        DataLoaderManagerService.class);
        
    //【12】电源管理服务,之前那个时电池状态管理(只做了一件事:监听电量变化)
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);

    //【13】系统恢复服务,我们刷机常见的 Recover 模式
    mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);

    //【14】安装包管理服务,这是个大类,三万行呢,下次一定看看
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
        mOnlyCore);
        
    //【15】传感器服务
    mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext));
    mSystemServiceManager.startService(SensorService.class);
}

启动服务到这里就结束啦,只是部分列举,并不完整,服务是如何运行的?具体都在做了些什么?等等!!!

这些执行细节希望在之后的系列文章进一步深入(内容实在是太多了😭)

启动 Core 服务

//SystemServer.java
private void startCoreServices(@NonNull TimingsTraceAndSlog t) {
    //【1】主要负责读取系统配置信息
    mSystemServiceManager.startService(SystemConfigService.class);
    
    //【2】电量跟踪
    mSystemServiceManager.startService(BatteryService.class);
    
    //【3】应用使用状态跟踪
    mSystemServiceManager.startService(UsageStatsService.class);
    
    //【4】监控设备是否充电、屏幕是否亮起
    //(通过高优先级的广播监听📢,指定特定的 intentfilter——ACTION_SCREEN_ON/ACTION_SCREEN_OFF/ACTION_BATTERY_CHANGED)
    mSystemServiceManager.startService(CachedDeviceStateService.class);
    
    //【5】应用程序回滚???
    mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);

    //【6】tombstone 墓碑,记录进程被杀死前的一些信息,比如调用栈、内存使用情况、CPU 使用情况、backtrace 等等,主要是监控和记录 native 崩溃信息(获取这个崩溃日志需要 root 权限)
    mSystemServiceManager.startService(NativeTombstoneManagerService.class);
    
    //【7】Android 错误报告生成,应用崩溃时候查看这个报告还是很有用的(前提是你能够看懂报告)
    // 可以同 adb bugreport 获取错误报告(Android 版本之间获取方式稍有区别,根据 adb bugreport 提示操作即可)
    mSystemServiceManager.startService(BugreportManagerService.class);

    //【8】主要还是监视和收集 GPU 信息
    mSystemServiceManager.startService(GpuService.class);    
}

核心服务不是很多,主要是信息记录相关,必不可少、确实很是关键。

启动 Other 服务

//SystemServer.java
 private void startOtherServices(@NonNull TimingsTraceAndSlog t) {

        try {
            //闹钟服务⏰
            mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS);
            //WMS 服务
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);

            //蓝牙服务
            if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                Slog.i(TAG, "No Bluetooth Service (factory test)");
            } else if (!context.getPackageManager().hasSystemFeature
                    (PackageManager.FEATURE_BLUETOOTH)) {
                Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
            } else {
                mSystemServiceManager.startService(BluetoothService.class);
            }

            //网络列表监控服务
            mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class);

            //输入法管理服务
            if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
                mSystemServiceManager.startService(
                        MultiClientInputMethodManagerService.Lifecycle.class);
            } else {
                mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
            }
            
            //辅助功能
            try {
                mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS);
            } catch (Throwable e) {
                reportWtf("starting Accessibility Manager", e);
            }
            
            //开发者选项中,OEM 解锁还记得吗
            if (hasPdb || OemLockService.isHalPresent()) {
                mSystemServiceManager.startService(OemLockService.class);
            }

            //状态栏    
            try {
                statusBar = new StatusBarManagerService(context);
                ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
             } catch (Throwable e) {
             }
             


            startContentCaptureService(context, t);
            startAttentionService(context, t);
            startRotationResolverService(context, t);
            startSystemCaptionsManagerService(context, t);
            //文字语音转换
            startTextToSpeechManagerService(context, t);

            //系统语音识别
            mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
            
            //智慧空间?记得华为手机搜索页面是这个名称,有的叫‘智慧场景’
            mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);

            //网络
            try {
                networkManagement = NetworkManagementService.create(context);
                ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
            } catch (Throwable e) {
            }
            
            //字体
            mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode));

            //Wi-Fi
            if (context.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_WIFI)) {
                mSystemServiceManager.startServiceFromJar(
                        WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                mSystemServiceManager.startServiceFromJar(
                        WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
            }
            if (context.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_WIFI_RTT)) {
                mSystemServiceManager.startServiceFromJar(
                        WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
            }
            if (context.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_WIFI_AWARE)) {
                mSystemServiceManager.startServiceFromJar(
                        WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
            }
            if (context.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_WIFI_DIRECT)) {
                mSystemServiceManager.startServiceFromJar(
                        WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
            }
            
            //VPN
            try {
                vpnManager = VpnManagerService.create(context);
                ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager);
            } catch (Throwable e) {
            }
            t.traceEnd();

            t.traceBegin("StartVcnManagementService");
            try {
                vcnManagement = VcnManagementService.create(context);
                ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
            } catch (Throwable e) {
                reportWtf("starting VCN Management Service", e);
            }
            t.traceEnd();

            //系统更新
            try {
                ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
                        new SystemUpdateManagerService(context));
            } catch (Throwable e) {
            }
            
            //通知栏
            mSystemServiceManager.startService(NotificationManagerService.class);
            SystemNotificationChannels.removeDeprecated(context);
            SystemNotificationChannels.createAll(context);
            notification = INotificationManager.Stub.asInterface(
                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));

            //壁纸
            if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) {
                mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
            } else {
            }

            //音量
            if (!isArc) {
                mSystemServiceManager.startService(AudioService.Lifecycle.class);
            } else {
                String className = context.getResources()
                        .getString(R.string.config_deviceSpecificAudioService);
                try {
                    mSystemServiceManager.startService(className + "$Lifecycle");
                } catch (Throwable e) {
                }
            }
            
            //无限广播
            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) {
                t.traceBegin("StartBroadcastRadioService");
                mSystemServiceManager.startService(BroadcastRadioService.class);
                t.traceEnd();
            }
        
            //adb 调试
            try {
                mSystemServiceManager.startService(ADB_SERVICE_CLASS);
            } catch (Throwable e) {
                Slog.e(TAG, "Failure starting AdbService");
            }
            
            //app 启动
        mSystemServiceManager.startService(LauncherAppsService.class);
            
            //启动密码锁
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY);
        
        //紧接着会有各项之前启动的服务调用 systemReady() 方法,指定服务准备完毕,即将进入下一个阶段
        mPackageManagerService.systemReady();
        mDisplayManagerService.systemReady(safeMode, mOnlyCore);
        ... etc
        
        //等待服务准备完毕
        mPackageManagerService.waitForAppDataPrepared();

        //各项服务调用 systemRunning()、start() 方法开始运行服务自身
        countryDetectorF.systemRunning();
        networkTimeUpdaterF.systemRunning();
        inputManagerF.systemRunning();
        telephonyRegistryF.systemRunning();
        mmsServiceF.systemRunning();
        ... etc
        
        //启动系统界面服务
        /*
            1、通过 intent 指定特定的组件 context.startServiceAsUser(intent, UserHandle.SYSTEM);
            2、startServiceAsUser 更上层的代码对我们看来像是调用 context.startService
            3、指定的启动的服务组件 pm.getSystemUiServiceComponent() 是什么呢?
                PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
                PackageManagerInternal 是一个抽象类,实现类是 PackageManagerInternalImpl
                而 PackageManagerInternalImpl 是 PackageManager 的内部类,所属成员是 private final PackageManagerInternal mPmInternal;
                而 getSystemUiServiceComponent 就是获取一个 string 资源com.android.internal.R.string.config_systemUIServiceComponent
            4、资源文件所在路径:frameworks/base/core/res/res/values/config.xml【看这个文件有好多服务的 component】
            5、资源内容 
            <!-- SystemUi service component -->
            <string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>
        */
        startSystemUi(context, windowManagerF);
    }
}

启动 SystemUI 服务

//SystemServer.java
private static void startSystemUi(Context context, WindowManagerService windowManager) {
    PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
    Intent intent = new Intent();
    /*
        1、很明显这是一个服务 component: com.android.systemui/com.android.systemui.SystemUIService
        2、此服务 Java 实现类所在路径是 /framework/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
        3、这个类比较简洁,主要有三个成员分别负责不同的事情
            mainHandle  :主线程通讯,明知故问
            dumpHandle  :当前线程运行状态信息输出
            logBufferFreezer   :负责错误报告日志相关【错误报告-参考链接】
            
        4、system server 进程启动的 UI 服务:来到了 SystemUIApplication.java 这个类 ((SystemUIApplication) getApplication()).startServicesIfNeeded();
            所有的 UI 服务包含哪些?服务名称列表哪里来?又是一个字符串数组资源 config_systemUIServiceComponents R.array.config_systemUIServiceComponentsPerUser
            资源所在路径是 /framework/base/packages/SystemUI/res/value/config.xml
            
            4.1【服务列表参考下文】
            4.2 通过反射创建服务 Class.forname(serviceName) 调用指定构造函数 newInstance
            4.3 启动服务 mServices[i].start(); 接下来就不具体看了,以后具体服务具体分析
            
        5、SysteUIApplication 他是一个 Application,所以在此之前创建该实例是会先执行 onCreate 方法,
            这里有调用一个重要的方法,对于非私有的非系统用户将执行 startSecondaryUserServicesIfNeeded();
            获取的服务列表是 R.array.config_systemUIServiceComponentsPerUser 查看只有一个服务
            
            5.1 com.android.systemui.util.NotificationChannels
            
    intent.setComponent(pm.getSystemUiServiceComponent());
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    context.startServiceAsUser(intent, UserHandle.SYSTEM);
    windowManager.onSystemUiStarted();
}

system UI 服务列表

//通知渠道服务(Android 低版本的通知创建是不需要设置通知渠道,后来高版本引入通知渠道并且必须设置,否则通知显示存在异常)
<item>com.android.systemui.util.NotificationChannels</item> 
<item>com.android.systemui.keyguard.KeyguardViewMediator</item> 
//应用最近任务列表
<item>com.android.systemui.recents.Recents</item> 
//音量
<item>com.android.systemui.volume.VolumeUI</item> 
<item>com.android.systemui.stackdivider.Divider</item> 
//状态栏
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item> 
<item>com.android.systemui.power.PowerUI</item> 
<item>com.android.systemui.media.RingtonePlayer</item> 
//键盘
<item>com.android.systemui.keyboard.KeyboardUI</item> 
<item>com.android.systemui.pip.PipUI</item> 
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> 
<item>@string/config_systemUIVendorServiceComponent</item> 
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item> 
<item>com.android.systemui.LatencyTester</item> 
<item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.ScreenDecorations</item> 
<item>com.android.systemui.biometrics.AuthController</item> 
<item>com.android.systemui.SliceBroadcastRelayHandler</item> 
<item>com.android.systemui.SizeCompatModeActivityController</item> 
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> 
<item>com.android.systemui.theme.ThemeOverlayController</item> 
<item>com.android.systemui.accessibility.WindowMagnification</item> 
<item>com.android.systemui.accessibility.SystemActions</item> 
<item>com.android.systemui.toast.ToastUI</item>

System Server 大概是启动完成,那么接下来又去哪里运行了呢???或者下一步我们继续看那个点比较合适呢???

之前我们只是粗略浏览,中间忽略了很多,下一步的入口点可能要返回之前的代码重新阅读发现合适的切入点,SystemServer 也已经进入了‘永久的循环’,等待的就是接受外部‘信号’做相应处理、继续分发到具体执行。

那么,我们下周再见😊

附加

如何快速搜索

Android 项目中如何快速搜索某关键字?

AOSP 整个项目是很庞大的,不仅仅是包含 java 代码,就拿当前我下载的 Android 11-r21 分支来说,我是通过 git 下载在没有指定 single-branch dept=1 参数下,整个过程下载完毕占用大约 430G 存储空间。

一开始我把源码存储在机械硬盘,通过 VSCode 打开机存在械硬盘的中的项目(整个 AOSP),比如搜索某个关键字,那个速度堪比龟速;后来把项目拷贝到笔记本 SSD 固态硬盘,搜索速度确实有了明显的提高,但整个项目搜索还是比较慢,不是十分满意;如果是单独打开某个模块——— framework 模块、framework base 模块等等,搜索速度还可以接受。但如果要找的代码根本不在当前模块,比如你打开 framework base 模块,但实际代码在 framework service 模块,这样是搜索不到结果的,因此还得把搜索范围扩大,引入的模块多了速度终是会变慢。

搜索能够快速找到目标,是不是要借助一个东西————索引,如果有工具把 AOSP 整个项目预先建立索引,然后再打开项目搜索,下次搜索时无需重新创建索引,通过索引搜索不得要起飞。与搜索相关索引确实是个好东西。

最后

推荐使用已有的在线网站辅助搜索:基于 opengrok 的 AOSPXRef

以搜索 SystemUIService config_systemUIServiceComponent为例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWaOCdnl-1659507116952)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f6d6877806a144cfb2a73a40aa569c01~tplv-k3u1fbpfcp-watermark.image?)]

最后的最后

ASOPXRef 现有的项目是较少的,也就是几个特定的版本,如果能满足自己的需求刚好,要是想看的源码版本不是已存在的建议还是自己通过 opengrok 引擎搭建一个服务。
Oracle opengrok:快速且可用的源代码搜索和交叉引用引擎

参考链接

  • WTF:What a Terrible Failure —— Android 系统错误记录的一种
  • Memtrack:内存分析 https://zhuanlan.zhihu.com/p/168361476
  • Hidl:硬件抽象层,在较低 Android 版本可能还在使用 HAL (hardware abstract layer)https://zhuanlan.zhihu.com/p/28256541
  • Android Uri:https://www.cnblogs.com/bhlsheji/p/4246580.html
  • Android 错误报告:https://developer.android.com/studio/debug/bug-report ,https://source.android.com/source/read-bug-reports.html
  • Android 墓碑:https://source.android.com/devices/tech/debug
现在我将给你个子服务依赖,一个是父项目的依赖文件,帮我看一下为啥我子服务还需要配置数据库才能启动 子服务依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.picc.xzpt.ai</groupId> <artifactId>piccxzpt_ai</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <parent> <groupId>com.picc.xzpt</groupId> <artifactId>piccxzpt</artifactId> <version>0.0.1</version> <relativePath>../pom.xml</relativePath> </parent> <dependencies> <dependency> <groupId>com.bes.appserver</groupId> <artifactId>bes-lite-spring-boot-2.x-starter</artifactId> <version>9.5.5.013</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.3</version> </dependency> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <!-- <version>3.0.4</version>--> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.22.0</version> <scope>compile</scope> </dependency> <!-- <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.8.0</version> </dependency>--> <!-- <dependency>--> <!-- <groupId>org.mybatis.spring.boot</groupId>--> <!-- <artifactId>mybatis-spring-boot-starter</artifactId>--> <!-- <version>1.3.1</version>--> <!-- <exclusions>--> <!-- <exclusion>--> <!-- <groupId>ch.qos.logback</groupId>--> <!-- <artifactId>logback-classic</artifactId>--> <!-- </exclusion>--> <!-- <exclusion>--> <!-- <groupId>org.mybatis</groupId>--> <!-- <artifactId>mybatis</artifactId>--> <!-- </exclusion>--> <!-- </exclusions>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>org.mybatis</groupId>--> <!-- <artifactId>mybatis</artifactId>--> <!-- <version>3.5.6</version>--> <!-- </dependency>--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.13</version> </dependency> <!--基础库--> <dependency> <groupId>com.picc.xzpt.baselib</groupId> <artifactId>piccxzpt_baselib</artifactId> <version>0.0.75</version> <exclusions> <exclusion> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> <!-- 添加以下排除项 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </exclusion> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.3.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- 热部署启动 --> <fork>true</fork> </configuration> </plugin> <!-- <plugin>--> <!-- <groupId>org.mybatis.generator</groupId>--> <!-- <artifactId>mybatis-generator-maven-plugin</artifactId>--> <!-- <version>1.3.5</version>--> <!-- <configuration>--> <!-- <!– generator 工具配置文件的位置 –>--> <!-- <configurationFile>src/main/resources/xml/generatorConfig.xml</configurationFile>--> <!-- <verbose>true</verbose>--> <!-- <overwrite>true</overwrite>--> <!-- </configuration>--> <!-- <dependencies>--> <!-- <!– oracle –>--> <!-- <dependency>--> <!-- <groupId>com.ibm.informix</groupId>--> <!-- <artifactId>jdbc</artifactId>--> <!-- <version>4.10.8.1</version>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>org.mybatis.generator</groupId>--> <!-- <artifactId>mybatis-generator-core</artifactId>--> <!-- <version>1.3.5</version>--> <!-- </dependency>--> <!-- </dependencies>--> <!-- </plugin>--> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build> </project> 父项目依赖 <groupId>com.picc.xzpt</groupId> <artifactId>piccxzpt</artifactId> <packaging>pom</packaging> <version>0.0.1</version> <!-- <parent>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-starter-parent</artifactId>--> <!-- <version>2.5.15</version>--> <!-- <relativePath/>--> <!-- </parent>--> <parent> <groupId>pdfc</groupId> <artifactId>pdfc-parent</artifactId> <version>4.2.8.4</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- <spring-cloud.version>2020.0.5</spring-cloud.version>--> <pdfc.version>4.2.8.4</pdfc.version> </properties> <dependencies> <!-- Web类型 --> <dependency> <groupId>pdfc</groupId> <artifactId>pdfc-web</artifactId> <version>${pdfc.version}</version> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> <exclusion> <groupId>com.squareup.okio</groupId> <artifactId>okio</artifactId> </exclusion> <exclusion> <groupId>com.squareup.okio</groupId> <artifactId>okio-jvm</artifactId> </exclusion> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> </exclusion> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> </exclusion> <exclusion> <groupId> org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> <exclusion> <groupId> org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </exclusion> <exclusion> <groupId>io.github.classgraph</groupId> <artifactId>classgraph</artifactId> </exclusion> <exclusion> <groupId>org.owasp.antisamy</groupId> <artifactId>antisamy</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.squareup.okio</groupId> <artifactId>okio</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>com.squareup.okio</groupId> <artifactId>okio-jvm</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.78.1</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> <version>1.78.1</version> </dependency> <dependency> <groupId>io.github.classgraph</groupId> <artifactId>classgraph</artifactId> <version>4.8.112</version> </dependency> <dependency> <groupId>org.owasp.antisamy</groupId> <artifactId>antisamy</artifactId> <version>1.7.5</version> </dependency> <!-- 微服务应用 --> <dependency> <groupId>pdfc</groupId> <artifactId>pdfc-cloud</artifactId> <version>${pdfc.version}</version> <exclusions> <exclusion> <artifactId>pdfc-config</artifactId> <groupId>pdfc</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.20</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> <exclusion> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.13</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <!-- <version>2.5.15</version>--> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 热更新 开始 --> <!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-devtools</artifactId>--> <!--</dependency>--> <!-- 热更新 结束--> <!--sswagger2开始--> <!-- <dependency>--> <!-- <groupId>io.springfox</groupId>--> <!-- <artifactId>springfox-swagger2</artifactId>--> <!-- <version>2.9.2</version>--> <!-- <exclusions>--> <!-- <exclusion>--> <!-- <groupId>com.google.guava</groupId>--> <!-- <artifactId>guava</artifactId>--> <!-- </exclusion>--> <!-- </exclusions>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>com.google.guava</groupId>--> <!-- <artifactId>guava</artifactId>--> <!-- <version>32.0.1-android</version>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>io.springfox</groupId>--> <!-- <artifactId>springfox-swagger-ui</artifactId>--> <!-- <version>2.10.0</version>--> <!-- </dependency>--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.3</version> </dependency> <!--sswagger2结束--> <!--缓存方案--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--缓存方案结束--> <!-- 加密的依赖 开始--> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> </dependency> <!-- 加密的依赖 结束--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>9.0.86</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>9.0.90</version> </dependency> <!-- 链路追踪 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> <version>4.11</version> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </exclusion> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.7.2</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.13</version> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency> <dependency> <groupId>com.ibm.informix</groupId> <artifactId>jdbc</artifactId> <version>4.10.8.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency> <!--基础库--> <dependency> <groupId>com.picc.xzpt.baselib</groupId> <artifactId>piccxzpt_baselib</artifactId> <version>0.0.97</version> <exclusions> <exclusion> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> </exclusion> <exclusion> <groupId>org.apache.tika</groupId> <artifactId>tika-core</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.tika</groupId> <artifactId>tika-core</artifactId> <version>1.28.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>1.10.0</version> </dependency> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.12.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.3.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.owasp.esapi/esapi --> <dependency> <groupId>org.owasp.esapi</groupId> <artifactId>esapi</artifactId> <version>2.5.3.0</version> <exclusions> <exclusion> <groupId>org.owasp.antisamy</groupId> <artifactId>antisamy</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
最新发布
11-19
### 分析子服务需要配置数据库的原因 根据您提供的父子项目 `pom.xml` 配置,**子服务需要配置数据库的根本原因是:父项目的依赖管理中包含了多个强制引入数据源的组件**,即使子项目中注释掉了相关依赖,这些依赖仍然通过 `<dependencyManagement>` 和继承机制被激活。 #### 核心问题定位 ```xml <!-- 父项目中的致命依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>com.ibm.informix</groupId> <artifactId>jdbc</artifactId> <version>4.10.8.1</version> </dependency> ``` 这三个依赖会触发 Spring Boot 的自动配置机制,导致必须配置 `DataSource`。 --- ### 完整解决方案 #### 1. 子项目 POM 修改方案(推荐) ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.picc.xzpt</groupId> <artifactId>piccxzpt</artifactId> <version>0.0.1</version> <relativePath>../pom.xml</relativePath> </parent> <dependencies> <!-- 必须显式排除父项目传递的数据库依赖 --> <dependency> <groupId>pdfc</groupId> <artifactId>pdfc-web</artifactId> <exclusions> <!-- 排除可能包含的数据源依赖 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </exclusion> </exclusions> </dependency> <!-- 基础库排除更多数据库组件 --> <dependency> <groupId>com.picc.xzpt.baselib</groupId> <artifactId>piccxzpt_baselib</artifactId> <version>0.0.75</version> <exclusions> <!-- 新增关键排除项 --> <exclusion> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </exclusion> <exclusion> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </exclusion> <exclusion> <groupId>com.ibm.informix</groupId> <artifactId>jdbc</artifactId> </exclusion> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </exclusion> </exclusions> </dependency> <!-- 其他非数据库依赖保持不变 --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.22.0</version> </dependency> <!-- ... 其他依赖 --> </dependencies> <!-- 关键:覆盖父项目的依赖管理 --> <dependencyManagement> <dependencies> <!-- 强制禁用数据库相关的starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.4</version> <scope>provided</scope> <!-- 设为provided防止初始化 --> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> <scope>provided</scope> </dependency> </dependencies> </dependencyManagement> </project> ``` #### 2. 应用程序启动类配置 ```java @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, MyBatisAutoConfiguration.class, DruidDataSourceAutoConfigure.class }) public class AiApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(AiApplication.class); // 强制禁用数据源检查 System.setProperty("spring.datasource.initialization-mode", "never"); System.setProperty("spring.datasource.type", "com.zaxxer.hikari.HikariDataSource"); app.run(args); } } ``` #### 3. application.yml 配置(无需真实数据库) ```yaml # 即使不使用数据库,也需要这些配置来绕过检查 spring: datasource: url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE username: sa password: driver-class-name: org.h2.Driver initialization-mode: never jpa: database-platform: org.hibernate.dialect.H2Dialect h2: console: enabled: false # 或者更简单的方案 - 完全关闭自动配置 spring.autoconfigure.exclude=\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration ``` #### 4. 验证依赖树的脚本 ```bash #!/bin/bash # 检查哪些依赖引入了数据源 mvn dependency:tree | grep -E &#39;jdbc|jpa|datasource|mybatis|druid|hibernate&#39; # 查看具体的依赖路径 mvn dependency:tree -Dverbose -Dincludes=druid,mybatis,jdbc ``` --- ### 问题根源详解 | 问题点 | 说明 | |-------|------| | **Druid Starter** | `druid-spring-boot-starter` 会强制触发 `DataSource` 自动配置 | | **MyBatis Starter** | 即使版本不同,也会激活数据访问层自动配置 | | **Informix JDBC** | 直接的数据库驱动,会被自动配置机制检测到 | | **继承机制** | 父项目的 `<dependencies>` 会被子项目无条件继承 | --- ### 替代解决方案(如果无法修改父POM) #### 方案A:使用 profiles 控制 ```xml <profiles> <profile> <id>no-database</id> <dependencies> <!-- 提供空实现覆盖原依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.4</version> <scope>provided</scope> </dependency> </dependencies> </profile> </profiles> ``` #### 方案B:代码层面屏蔽 ```java @Configuration @ConditionalOnProperty(name = "app.database.enabled", havingValue = "false") class DisableDatabaseConfig { @Bean @Primary public DataSource dataSource() { return new DriverManagerDataSource(); // 空数据源 } } ``` --- ### 最终建议 1. **优先联系架构团队**:建议将数据库相关依赖从父POM移到专门的 `data-parent` 中 2. **使用 dependencyManagement**:父项目应该只声明版本,不直接引入依赖 3. **建立规范**:创建 `no-database-profile` 统一处理这类场景
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值