Android 基于注解IOC組件化/模塊化的架構實踐

當前參與的項目歷史也很久遠,第一行代碼據說是寫于2014年的某一天,那時Android用的ide還是Eclipse、那時Android還沒有很好的架構指導(mvp、mvvm)、那時Android最新的版本是5.0、那時Android的Material Design還沒流行……


背景

隨著業務和產品發展,目前參與的項目apk有2~10個Android開發人員(注:開發人員數回浮動,不是因為離職,而是是因為當前項目團隊在承接多個項目的并行開發)在進行迭代和維護。當前技術部移動團隊有30+開發人員,有多個不同的項目在并行開發,但是卻沒有架構組(底層碼農管不了組織的事,只能埋頭敲代碼),沒有架構組的最直接的問題是沒有一個組織來統一各個項目的技術選型和技術方案。

今天帶來自己寫的一個組件化框架?XModulable

XModulable使用:

1. 添加依賴配置

android {defaultConfig {...javaCompileOptions {annotationProcessorOptions {arguments = [ XModule : project.getName() ]}}}
}dependencies {// gradle3.0以上建議使用implementation(或者api) 'com.xpleemoon.xmodulable:XModulable-api:x.x.x'compile 'com.xpleemoon.xmodulable:XModulable-api:x.x.x'annotationProcessor 'com.xpleemoon.xmodulable:XModulable-compiler:x.x.x'...
}復制代碼


2. 實現組件

@XModule(name = "XX組件名")
public class XXModule implements IModule{}復制代碼


3. 初始化sdk

if (isDebug) {XModulable.openDebug();
}
XModulable.init(this);復制代碼


4. 獲取組件

組件獲取有兩種方式:依賴注入和手動查詢獲取。

依賴注入:

public class TestActivity extends BaseActivity {@InjectXModule(name = "xxx")XXModule mXXModule;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);XModulable.inject(this);}
}復制代碼


手動獲取:

XModulable.getInstance().getModule("XX組件名")復制代碼


5. 添加混淆規則

-keep class * implements com.xpleemoon.xmodulable.api.template.XModuleLoader
-keep class * implements com.xpleemoon.xmodulable.api.IModule
-keep class **$$XModulableInjector { *; }復制代碼

原理介紹:

組件化/模塊化

  • 組件:基于可重用的目的,對功能進行封裝,一個功能就是一個組件,例如網絡、IO、圖片加載等等這些都是組件

  • 模塊:基于業務獨立的目的,對一系列有內聚性的業務進行整理,將其與其他業務進行切割、拆分,從主工程或原所在位置抽離為一個相互獨立的部分

由于模塊是獨立解耦可重用的特性,在實施組件化/模塊化的過程中,我們需要解決三個主要問題:

  1. 1. 模塊通信——因為業務模塊是相互隔離的,它們完全不知道也無法感知其他業務模塊是否存在,所以需要一種盡最大可能的隔離、耦合度相對最低、代價相對最小的可行方案來實現通信

  2. 2. 模塊獨立運行——在后續迭代維護的過程中,各個業務線的人員能夠職責更加清晰

  3. 3. 模塊靈活組合運行——能夠適應產品需求,靈活拆分組合打包上線


NOTE組件化/模塊化這一節將會以XModulable為例進行解釋它是如何進行組件化/模塊化:闡述和理解一個程序問題,最直接的方式是寫一個小的demo演示和show關鍵代碼。本文可能有些地方講的不夠詳細,強烈建議拉下XModulable運行看看。



XModulable架構圖.png
XModulable工程結構.png



解決拋出的三個問題之前,先過下[XModulable]的工程結構圖和架構圖,上圖中的module對應層級:

  • app殼層——依賴業務層,可靈活組合業務層模塊

  • 業務層——im、live和main,面向common層實現業務層服務接口,向common注冊和查詢業務模塊

  • common層——依賴基礎組件層;承接業務層,暴露業務層服務接口,同時為業務層提供模塊路由服務

  • basic層——basicRes和basicLib

    • basicRes——包含通用資源和各UI組件

    • basicLib——包含網路組件、圖片加載組件、各種工具等功能組件

  • XModulable
    只是一個小的demo而已,而圖中展示的是我對于每一層的完整構想,所以當去源碼的時候發現有些是缺失的:common缺失了AOP代碼、basciRes缺失了UI組件,basicLib缺失了幾乎所有的組件。

  • XModulable-annoation
    XModulable-api
    XModulable -compiler
    屬于
    XModulable SDK

  • XModulable SDK
    主要用于
    業務模塊的注冊(sdk在執行初始化的時候,會自動進行注冊)和獲取(依賴注入和手動獲取)
    。這里對
    XModulable Sdk
    不做具體技術分析,對于依賴注入和注解的編譯期處理不了解或者感興趣的可移步我以前寫的
    編譯時(Compile time)處理,擼一個簡易版的ButterKnife


1. 模塊通信

模塊化的通信(UI跳轉和數據傳遞),需要抓住幾個基本點:隔離解耦代價小(易維護)、傳遞復雜數據(Fragment、View、File……)。實現獨立互不依賴模塊的通信,很容易能夠想到以下幾種方式:

  • Android傳統通信(比如aidl、廣播、自定義url……)

    • 無法避免高度耦合、以及隨著項目擴張導致難以維護的問題

    • 還有另外一關鍵個問題就是只能進行一些非常簡單的數據傳遞,像Fragment、View、File……這些數據(或者叫對象也行),完全無法通信傳遞,但是這些數據在實際的app中恰恰是組成一個app的關鍵節點。比如說app的主站中有一個MainActivity,它是一個ViewPager+TabLayout的結構,其中的每一個頁面都是來自于不同模塊的Fragment,這個時候我們的通信就完全無法滿足了。

  • 第三方通信(比如EventBus、RxBus……)

    • 容易陷入茫茫的event通知和接收中,增加調試和維護的成本

    • 能夠傳遞一些復雜的數據,通過event事件來攜帶其它數據對象,但是代碼耦合性相應的會增加

  • 第三方路由庫(比如ARouter、OkDeepLink、DeepLinkDispatch……)基本都能夠實現隔離解耦代價小(易維護)。至于數據傳遞的話默認只支持一些簡單數據,但是我們可以結合面向接口編程,公共層暴露接口,業務層面向公共層的接口去實現對應的接口方法(UI跳轉、數據讀寫……),最后當業務層使用的時候只需要通過路由到接口,就可以完成復雜數據的通信。以ARouter為例,可以在common層暴露業務模塊的服務接口(IProvider,ARouter提供的服務接口,只要實現了該接口的自定義服務,ARouter都能進行路由操作),然后交由對應的業務模塊去實現common層對應的服務接口,最后在業務模塊中使用ARouter進行路由其他業務模塊暴露的服務接口來實現。

從上面的分析來看,路由+面向接口編程是實現組件化/模塊化的不二之選,但是這里又有一個問題——假設哪天抽風想要更換路由庫或者可能某種特殊需求不同的業務模塊使用了不容的路由庫,那怎么辦呢?沒關系,我們這時候需要對路由庫做一層封裝,使業務模塊內的路由都相互隔離,也就是一個業務模塊內部的路由操作對其他業務模塊來說是一個黑箱操作。我的封裝思路是這樣的:加一個XModule(可以把它想象成一個容器)的概念,在common層暴露服務接口的同時暴露XModule(它的具體實現也是有對應的業務模塊決定的),每一業務模塊都對應一個XModule,用于承載common層暴露的服務接口,業務模塊之間的通信第一步必須先獲取XModule,然后再通過這個容器去拿到服務。

綜上所述,最終的組件化/模塊化采用的是封裝+路由+面向接口編程。以live業務模塊為例,從源碼的角度看下它們是實現這套思路的。在common層把live業務模塊想要暴露給其他業務模塊的服務LiveService進行了暴露,同時在common層暴露了一個LiveModule(live業務模塊的服務容器,承載了LiveService),l,live業務模塊面向common層對應的接口進行實現(LiveModuleImpl和LiveServiceImpl)。這樣的話,上層業務就可以通過XModulable SDK獲取到LiveModule,然后通過LiveModule承載的服務進行調用。

// common層live暴露的XModule(LiveModule)和服務接口(LiveService)
public abstract class LiveModule extends BaseModule {public abstract LiveService getLiveService();
}
public interface LiveService extends BaseService {Fragment createLiveEntranceFragment();void startLive();
}
// 業務模塊層——live針對common層暴露的實現LiveModuleImpl和LiveServiceImpl
@XModule(name = ModuleName.LIVE)
public class LiveModuleImpl extends LiveModule {@AutowiredLiveService liveService;@Overridepublic LiveService getLiveService() {return liveService;}
}
@Route(path = PathConstants.PATH_SERVICE_LIVE)
public class LiveServiceImpl implements LiveService {@Overridepublic void init(Context context) {}@Overridepublic Fragment createLiveEntranceFragment() {return new LiveEntranceFragment();}@Overridepublic void startLive() {ARouter.getInstance().build(PathConstants.PATH_VIEW_LIVE).navigation();}
}復制代碼


2. 模塊獨立運行

業務模塊在Android Studio中其實就是一個module,從gradle的角度來說,module不是以application plugin方式運行,就是以library plugin方式運行,所以為了業務模塊也能夠獨立運行,就需要控制gradle能夠在application plugin和library plugin兩種形式下切換,同時還要提供單獨運行時的源碼。

首先在項目的build.gradle中創建業務模塊配置,isStandAlone表示業務模塊是否獨立運行:

ext {applicationId = "com.xpleemoon.sample.modulable"// 通過更改isStandalone的值實現業務模塊是否獨立運行,以及app殼工程對組件的靈活依賴modules = [main: [isStandalone : false,applicationId: "${applicationId}.main",],im  : [isStandalone : false,applicationId: "${applicationId}.im",],live: [isStandalone : true,applicationId: "${applicationId}.live"],]
}復制代碼


然后設置對應業務模塊的build.gradle:

def currentModule = rootProject.modules.live
// isStandalone的值決定了當前業務模塊是否獨立運行
if (currentModule.isStandalone) {apply plugin: 'com.android.application'
} else {apply plugin: 'com.android.library'
}android {
省略...defaultConfig {if (currentModule.isStandalone) {// 當前組件獨立運行,需要設置applicationIdapplicationId currentModule.applicationId}省略...def moduleName = project.getName()// 業務組件資源前綴,避免沖突resourcePrefix "${moduleName}_"javaCompileOptions {annotationProcessorOptions {arguments = [// ARouter處理器所需參數moduleName   : moduleName,// XModulable處理器所需參數XModule: moduleName]}}}
省略...sourceSets {main {// 單獨運行所需要配置的源碼文件if (currentModule.isStandalone) {manifest.srcFile 'src/standalone/AndroidManifest.xml'java.srcDirs = ['src/main/java/', 'src/standalone/java/']res.srcDirs = ['src/main/res', 'src/standalone/res']}}}
}
省略...復制代碼

最后,在業務模塊中編寫build.gradle中sourceSets聲明單獨運行所需要的額外源碼文件,比如Application、SplashActivity和Manifest。

完成上面的過程后,就可以選擇對應的業務模塊live運行


3. 模塊靈活組合運行

模塊的靈活組合,其實也非常簡單,只需要更改業務模塊配置在項目build.gradle的isStandalone值,然后在app殼的build.gradle中通過業務模塊的isStandalone來決定是否依賴就行,關鍵代碼如下:

dependencies {
省略...def modules = rootProject.modulesdef isMainStandalone = modules.main.isStandalonedef isIMStandalone = modules.im.isStandalonedef isLiveStandalone = modules.live.isStandalone// 判斷業務組件是否獨立運行,實現業務組件的靈活依賴if (isMainStandalone && isIMStandalone && isLiveStandalone) {api project(':common')} else {if (!isMainStandalone) {implementation project(':main')}if (!isIMStandalone) {implementation project(':im')}if (!isLiveStandalone) {implementation project(':live')}}
}復制代碼


產品技術債

OK,現在已經把組件化/模塊化所面臨的問題消滅了,那就回過頭來整理現有產品的技術債:

  1. 代碼耦合、臃腫、混亂

  2. 模塊層級不合理

    1. 業務模塊相互依賴耦合

    2. 業務模塊拆分粒度不夠,某些模塊像個大雜燴

    3. 業務模塊無法單獨編譯運行,業務模塊之間無法靈活組合成apk

  3. 基礎組件無法快速提取,以供給其他工程使用

上述問題直接導致新來同事無法快速理清工程結構,以及無法快速進入開發。

若團隊后續擴張的話,勢必會按照業務功能模塊劃分獨立的業務小組,那么會導致人員組織架構和工程組織架構上打架

對癥下藥

(一)控制代碼質量

團隊內部人員需要有代碼質量意識,否則,容易出現有一波人在重構優化,而另一波人卻在寫bug、寫爛代碼,這樣就完全失去了重構的意義。所以,在進入重構之前務必要明確傳達控制代碼質量

  1. 控制公共分支(master、develop和版本分支)權限,將公共分支的權限集中在少數人手里,可避免代碼沖突、分支不可控

  • 非項目負責人只有develop權限——無法merge遠端倉庫的master、develop和版本分支

制定git flow和code review流程,提高團隊協作效率

  • 項目負責人從master(或者develop分支,視自身的項目管理而定)遷出版本分支

  • 開發人員從版本分支遷出個人的開發分支

  • 開發人員在個人的開發分支上進行開發工作

  • 開發人員在個人分支上開發完成后需要push到遠端,

  • 開發人員在遠端(我們用的是gitlab)創建merge request(Source branch:個人分支,Target branch:版本分支),同時指派給項目負責人并@相關開發人人員

  • 執行code review

  • review完成,由負責人進行遠端的分支合并

(二) 合理的模塊層級

首先來看下模塊層級架構圖:



在原有app的層級上,重新劃分模塊層級,這是很吃力的一件事情。因為一個項目經常是有多人并行的開發迭代的,當你已經切割或者規劃出模塊層級了,但是其它成員卻在反其道而行之,必然會導致實施過程中進行代碼合并時有茫茫多的沖突需要解決和返工,所以我們在這里還需要灌輸模塊層級思想和規劃。

  1. 劃分層級,從上到依次為:app殼層、業務層、common層、basic層,它們的職責如下

  • app殼層——直接依賴業務模塊

  • 業務層——項目中各個獨立的業務功能的聚合,由多個業務模塊構成業務層

  • common層——承上啟下:承接上層業務,提供業務模塊路由服務;依賴底層basic,統一提供給上層使用

  • basic層——basicRes和basicLib

    • basicRes——包含通用資源和各UI組件

    • basicLib——包含網路組件、圖片加載組件、各種工具等功能組件

業務模塊提取通用代碼、組件、公共資源進行下沉

  • 通用代碼下沉到common,可能涉及到BaseAplication、BaseActivity、廣播通知事件(也可能是EventBus相關事件,具體視自身而定)

  • ui組件和基礎資源下沉到basicRes

  • 網路組件、圖片加載組件、各種工具等功能組件下沉到basicLib

大雜燴模塊拆分獨立。以主業務模塊為例,包含了推送、分享、更新、地圖、用戶中心、二手房、新房、租房……,如此臃腫的模塊不可能一次性拆分完成,所以必須制定一個計劃,有節奏的進行推進。我們的規劃是按照業務關聯性由低到高的原則依次拆分:

  • 分享、更新下沉到basicLib

  • 推送、地圖下沉到basicLib

  • 用戶中心獨立成業務模塊

  • 二手房、新房、租房獨立成業務模塊

業務模塊獨立運行;業務模塊之間靈活組合成apk

(三) 基礎組件內網maven依賴

基礎組件拆分完成后,如果直接進行module依賴的話,會導致重復編譯和無法靈活供給其它app使用的問題。所以我們需要將基礎組件上傳內網maven,然后通過maven進行依賴。

  1. basicRes和basicLib作為基礎資源組件和基礎功能組件上傳到內網maven

  2. 對basicRes和basicLib根據組件細粒度拆分上傳內網maven,方便其他工程能夠根據實際需求靈活依賴

設定節奏和目標

制定重構節奏和流程如下,將規劃合理分配到各個版本中去,在保證產品迭代的同時,能夠穩步推進基于組件化/模塊化的重構探索實踐。

節奏

目標

執行范圍

第一期

控制代碼質量

1. 控制公共分支(master、develop和版本分支)權限;2. 制定git flow和code review流程

第二期

合理的模塊層級(現有層級分割下沉)

1. 劃分層級;2. 業務模塊提取通用代碼、組件、公共資源進行下沉

第三期

合理的模塊層級(大雜燴模塊拆分獨立1)

分享、更新下沉到basicLib

第四期

合理的模塊層級(大雜燴模塊拆分獨立2)

推送、地圖下沉到basicLib

第五期

合理的模塊層級(大雜燴模塊拆分獨立3)

用戶中心獨立成業務模塊

第六期

合理的模塊層級(大雜燴模塊拆分獨立4)

二手房、新房、租房獨立成業務模塊

第七期

合理的模塊層級(業務模塊獨立運行和靈活組合)

業務模塊獨立運行,業務模塊之間靈活組合成apk

第八期

基礎組件內網maven依賴

1. basicRes和basicLib上傳到內網maven;2. 對basicRes和basicLib根據組件細粒度拆分上傳內網maven


源碼
https://github.com/xpleemoon/XModulable


作者:xpleemoon。平安好房Android高級工程師

相關推薦

App組件化與業務拆分那些事

熱修復原理之熱修復框架對比和代碼修復

Android架構之路-三步實現MVP架構(上)


喜歡可關注:



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452004.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452004.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452004.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡爬蟲--14.【糗事百科實戰】

文章目錄一. 要求二. 參考代碼一. 要求 爬取糗事百科段子,假設頁面的URL是 http://www.qiushibaike.com/8hr/page/1 使用requests獲取頁面信息,用XPath / re 做數據提取 獲取每個帖子里的用戶頭像鏈接、用戶姓名、段子內容、點贊次數和評論次數 保存到…

bzoj 5369: [Pkusc2018]最大前綴和

Description 小C是一個算法競賽愛好者,有一天小C遇到了一個非常難的問題:求一個序列的最大子段和。 但是小C并不會做這個題,于是小C決定把序列隨機打亂,然后取序列的最大前綴和作為答案。 小C是一個非常有自知之明的人&#xff0c…

微軟:軟件帝王的復興之路

可以說在過去的兩個月IT界所發生的一切都非同尋常,喬布斯辭職了,Google把Motorola并購了,微軟炫了一下Windows 8,還宣布開始用ARM了,Google開始和英特爾合作了,AT&T與T-Mobile的并購也在緊密鑼鼓進行中…

jdbc和odbc區別

ODBC(Open Database Connectivity,開放數據庫互連)是微軟公司開放服務結構(WOSA,Windows Open Services Architecture)中有關數據庫的一個組成部分,它建立了一組規范,并提供了一組對數據庫訪問的標準API(應用程序編程接…

事務相關、不可重復讀與幻讀的區別

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 事務內嵌套事務: 1) 都用spring事務時,取決spring采用的事務的隔離級別。 這個默認隔離級別是與具體的數據…

onload事件

onload事件它只支持少量標簽<body>, <frame>, <iframe>, <img>, <input type"image">, <link>, <script>, <style> 不支持<div>,<p>標簽等 所以&#xff0c;在div使用onload事件時該怎么辦呢。。。轉載…

Eclipse GBK批量轉UTF-8插件(轉)

最近需要把Android項目轉Android Studio&#xff0c;由于之前是eclipse開發&#xff0c;而且坑爹的是編碼還是GBK的&#xff0c;轉到Android Studio中文都是亂碼&#xff0c;如果一個文件一個文件ctrlc的話&#xff0c;想想就累&#xff0c;幾經Google&#xff0c;發現一個很好…

網絡爬蟲--15.【糗事百科實戰】多線程實現

文章目錄一. Queue&#xff08;隊列對象&#xff09;二. 多線程示意圖三. 代碼示例一. Queue&#xff08;隊列對象&#xff09; Queue是python中的標準庫&#xff0c;可以直接import Queue引用;隊列是線程間最常用的交換數據的形式 python下多線程的思考 對于資源&#xff0…

淺談:國內軟件公司為何無法做大做強?

縱覽,國內比較大的軟件公司(以下統一簡稱"國軟"),清一色都是做政府項目的(他們能做大的原因我就不用說了吧),真正能做大的國軟又有幾家呢?這是為什么呢? 今天風吹就給大家簡單分析下: 1."作坊"式管理 "作坊"往往是效率最高的,國軟幾乎都是從作…

Java SE、Java EE、Java ME三者的區別

說得簡單點 Java SE 是做電腦上運行的軟件。 Java EE 是用來做網站的-&#xff08;我們常見的JSP技術&#xff09; Java ME 是做手機軟件的。 1. Java SE&#xff08;Java Platform&#xff0c;Standard Edition&#xff09;。Java SE 以前稱為 J2SE。它允許開發和部署在桌面、…

FileBeats安裝

FileBeats安裝 FileBeats官方下載鏈接&#xff1a; https://www.elastic.co/downloads/beats/filebeat 也可以直接使用以下命令下載&#xff08;文章下載目錄一概為/home/tools, 解壓后文件夾放到 /home/apps下&#xff09; wget https://artifacts.elastic.co/downloads/beats…

《程序員代碼面試指南》第三章 二叉樹問題 二叉樹節點間的最大距離問題

題目 二叉樹節點間的最大距離問題 java代碼 package com.lizhouwei.chapter3;/*** Description:二叉樹節點間的最大距離問題* Author: lizhouwei* CreateDate: 2018/4/16 19:33* Modify by:* ModifyDate:*/ public class Chapter3_20 {public int maxDistance(Node head) {int[…

MySQL中函數CONCAT及GROUP_CONCAT 對應oracle中的wm_concat

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、CONCAT&#xff08;&#xff09;函數 CONCAT&#xff08;&#xff09;函數用于將多個字符串連接成一個字符串。 使用數據表Info作為…

網絡爬蟲--16.BeautifulSoup4

文章目錄一. BeautifulSoup4二. 解析實例三. 四大對象種類1. Tag2. NavigableString3. BeautifulSoup4. Comment四. 遍歷文檔樹1.直接子節點 &#xff1a;.contents .children 屬性1). .contents2). .children2. 所有子孫節點: .descendants 屬性3. 節點內容: .string 屬性五. …

Intel MKL 多線程設置

對于多核程序&#xff0c;多線程對于程序的性能至關重要。 下面&#xff0c;我們將對Intel MKL 有關多線程方面的設置做一些介紹&#xff1a; 我們提到MKL 支持多線程&#xff0c;它包括的兩個概念&#xff1a; 1>MKL 是線程安全的&#xff1a; MKL在設計時&#xff0c;就保…

【LA3415 訓練指南】保守的老師 【二分圖最大獨立集,最小割】

題意 Frank是一個思想有些保守的高中老師。有一次&#xff0c;他需要帶一些學生出去旅行&#xff0c;但又怕其中一些學生在旅行中萌生愛意。為了降低這種事情發生的概率&#xff0c;他決定確保帶出去的任意兩個學生至少要滿足下面四條中的一條。 1.身高相差大于40厘米 2.性別相…

行車記錄儀穩定方案:TC358778XBG:RGB轉MIPI DSI芯片,M-Star標配IC

原廠&#xff1a;Toshiba型號&#xff1a;TC358778XBG功能&#xff1a;TC358778XBG是一顆將RGB信號轉換成MIPI DSI的芯片&#xff0c;最高分辨率支持到1920x1200&#xff0c;其應用圖如下&#xff1a;產品特征&#xff1a;MIPI接口&#xff1a;&#xff08;1&#xff09;、支持…

java.sql.SQLException: 無法轉換為內部表示之解決

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 這個錯是因為 數據庫中字段類型和程序中該字段類型不一致。 比如程序將某字段當做Integer類型&#xff0c; 而數據庫存儲又使用另外一…

網絡爬蟲--17.【BeautifuSoup4實戰】爬取騰訊社招

文章目錄一.要求二.代碼示例一.要求 以騰訊社招頁面來做演示&#xff1a;http://hr.tencent.com/position.php?&start10#a 使用BeautifuSoup4解析器&#xff0c;將招聘網頁上的職位名稱、職位類別、招聘人數、工作地點、發布時間&#xff0c;以及每個職位詳情的點擊鏈接…

public static void main(String[] args)的理解

public:權限修飾符&#xff0c;權限最大。static:隨著MianDemo類的加載而加載&#xff0c;消失而消失。void: 沒有返回值main: 函數名&#xff0c;jvm識別的特殊函數名(String[] args):定義了一個字符串數組參數。這個字符串數組是保存運行main函數時輸入的參數的