OnWXWWPPXX (function() { window.appData = JSON.parse(decodeURIComponent(“%7B%22me%22%3A%7B%22work_id%22%3A%22222822%22%2C%22avatar_url%22%3A%22https%3A%2F%2Fyuque.antfin-inc.com%2Fr%2Favatar_buc%2F222822%22%2C%22isActive%22%3Atrue%2C%22isInactive%22%3Afalse%2C%22isDeactivated%22%3Afalse%2C%22isTerminated%22%3Afalse%2C%22isBanned%22%3Afalse%2C%22isBlocked%22%3Afalse%2C%22isMuted%22%3Afalse%2C%22isExtcontact%22%3Afalse%2C%22email%22%3A%22gui**%40alibaba-inc.com%22%2C%22mobile%22%3A%22%22%2C%22isPaid%22%3Afalse%2C%22hasPaidBefore%22%3Afalse%2C%22id%22%3A224034%2C%22space_id%22%3A0%2C%22type%22%3A%22User%22%2C%22login%22%3A%22guiling.cyt%22%2C%22name%22%3A%22%E9%AC%BC%E7%81%B5%22%2C%22description%22%3Anull%2C%22avatar%22%3Anull%2C%22owner_id%22%3Anull%2C%22topics_count%22%3A0%2C%22public_topics_count%22%3A0%2C%22members_count%22%3A0%2C%22books_count%22%3A4%2C%22public_books_count%22%3A2%2C%22followers_count%22%3A1%2C%22following_count%22%3A0%2C%22account_id%22%3Anull%2C%22role%22%3A1%2C%22status%22%3A1%2C%22public%22%3A1%2C%22wants_email%22%3Atrue%2C%22wants_marketing_email%22%3Atrue%2C%22topic_updated_at_ms%22%3A0%2C%22deleted_slug%22%3Anull%2C%22language%22%3A%22zh-cn%22%2C%22organization_id%22%3A0%2C%22emp_type%22%3A%22%E6%AD%A3%E5%BC%8F%20-%20%E6%AD%A3%E5%BC%8F%E5%91%98%E5%B7%A5%22%2C%22group_department_updated_at%22%3Anull%2C%22member_level%22%3Anull%2C%22expired_at%22%3Anull%2C%22scene%22%3Anull%2C%22source%22%3Anull%2C%22max_member%22%3Anull%2C%22created_at%22%3A%222019-06-27T05%3A08%3A48.000Z%22%2C%22updated_at%22%3A%222021-04-12T07%3A50%3A11.000Z%22%2C%22grains_sum%22%3A34%2C%22punish_expired_at%22%3Anull%2C%22deleted_at%22%3Anull%2C%22hasPassword%22%3Atrue%2C%22canCreateOrg%22%3Atrue%2C%22hasJoinedQuan%22%3Atrue%2C%22is_admin%22%3Afalse%7D%2C%22settings%22%3A%7B%22head_html%22%3A%22%3Clink%20href%3D%5C%22https%3A%2F%2Fgw.alipayobjects.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fmdap.alipay.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fcdn.nlark.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fcdn.yuque.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fkcart.alipay.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fcdn-pri.nlark.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fg.yuque.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fmdap.yuque.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fintranetproxy.alipay.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fyuque.alibaba-inc.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%5Cn%3Clink%20href%3D%5C%22https%3A%2F%2Fyuque.antfin-inc.com%5C%22%20rel%3D%5C%22dns-prefetch%5C%22%20%2F%3E%22%2C%22disable_quan%22%3Afalse%2C%22quan_nav_list_ids%22%3A%5B%22300001%22%2C%22100001%22%2C%22100002%22%5D%2C%22footer_html%22%3A%22%3Cscript%3E%20window.addEventListener(‘load’%2C%20function()%20%7B%20var%20secScript%20%3D%20document.createElement(‘script’)%3B%20secScript.src%20%3D%20’https%3A%2F%2Frender.alipay.com%2Fp%2Fs%2Fofficerd%2Findex.js’%3B%20document.body.appendChild(secScript)%3B%20%7D)%3B%20%3C%2Fscript%3E%22%2C%22watermark_enable%22%3A%22%22%2C%22home_aside_html%22%3A%22%22%2C%22index_banner_html%22%3A%22%22%2C%22doc_banner_html%22%3A%22%22%2C%22doc_aside_html%22%3A%22%22%2C%22public_space_doc_search_enable%22%3Afalse%2C%22lark_official_labels%22%3A%5B%5D%2C%22lake_enabled_groups%22%3A%22%22%2C%22image_proxy_root%22%3A%22%22%2C%22miniapp_code_enable%22%3Afalse%2C%22max_import_task_count%22%3A5%2C%22validation_code_need_captcha%22%3Afalse%2C%22vms_code_service_off%22%3Afalse%2C%22enable_member_popup_act%22%3Atrue%2C%22fu_que_entry_enabled_pages%22%3A%5B%5D%2C%22enable_search%22%3Atrue%2C%22enable_serviceworker%22%3Atrue%2C%22enable_lazyload_card%22%3A%22codeblock%2Cimage%22%2C%22help_center_group_id%22%3A0%2C%22conference_gift_num%22%3A0%2C%22conference_live_show%22%3Afalse%7D%2C%22env%22%3A%22prod%22%2C%22space%22%3A%7B%22host%22%3A%22https%3A%2F%2Fyuque.antfin.com%22%2C%22displayName%22%3A%22%E8%AF%AD%E9%9B%80%22%2C%22logo_url%22%3A%22https%3A%2F%2Fgw.alipayobjects.com%2Fzos%2Fbasement%2Fskylark%2F0abe7b2714792688448236679d347e%2Favatar%2F512.png%22%2C%22small_logo_url%22%3A%22https%3A%2F%2Fgw.alipayobjects.com%2Fzos%2Fbasement%2Fskylark%2F0abe7b2714792688448236679d347e%2Favatar%2F512.png%3Fx-oss-process%3Dimage%2Fresize%2Cm_fill%2Cw_80%2Ch_80%22%2C%22medium_logo_url%22%3A%22https%3A%2F%2Fgw.alipayobjects.com%2Fzos%2Fbasement%2Fskylark%2F0abe7b2714792688448236679d347e%2Favatar%2F512.png%3Fx-oss-process%3Dimage%2Fresize%2Cm_fill%2Cw_160%2Ch_160%22%2C%22large_logo_url%22%3A%22https%3A%2F%2Fgw.alipayobjects.com%2Fzos%2Fbasement%2Fskylark%2F0abe7b2714792688448236679d347e%2Favatar%2F512.png%3Fx-oss-process%3Dimage%2Fresize%2Cm_fill%2Cw_320%2Ch_320%22%2C%22login%22%3A%22%22%2C%22status%22%3A0%2C%22account_id%22%3A0%2C%22enable_password%22%3Atrue%2C%22enable_watermark%22%3Afalse%2C%22id%22%3A0%2C%22name%22%3A%22%E8%AF%AD%E9%9B%80%22%2C%22description%22%3A%22%22%2C%22hasDepartment%22%3Atrue%7D%2C%22organization%22%3Anull%2C%22isYuque%22%3Afalse%2C%22supportOnlineViewer%22%3Atrue%2C%22isEnterprise%22%3Afalse%2C%22defaultSpaceHost%22%3A%22https%3A%2F%2Fyuque.antfin-inc.com%22%2C%22timestamp%22%3A1618300983068%2C%22traceId%22%3A%220b927cab16183009828538748e51cc%22%2C%22siteTip%22%3A%7B%22id%22%3A700030%2C%22content%22%3A%22%3Cimg%20src%3D%5C%22https%3A%2F%2Fgw.alipayobjects.com%2Fmdn%2Frms%2Fafts%2Fimg%2FA*Fg_TQpx32aUAAAAAAAAAAAAAARQnAQ%5C%22%20width%3D%5C%22100%25%5C%22%20%2F%3E%5Cn%3Cdiv%20style%3D%5C%22margin%3A%2024px%2080px%200%3B%5C%22%3E%5Cn%3Cdiv%20style%3D%5C%22width%3A500px%3B%5C%22%3E%5Cn%3Cdiv%20style%3D%5C%22font-size%3A24px%3Bpadding-bottom%3A8px%3Bpadding-top%3A0px%3Bcolor%3A%23000%3Bfont-weight%3Abold%3B%5C%22%3E%F0%9F%93%A8%20%E6%96%B0%E5%B7%A5%E4%BD%9C%E5%8F%B0%E4%B8%8A%E7%BA%BF%EF%BC%81%3C%2Fdiv%3E%5Cn%3Cdiv%3E%5Cn%3Cp%3E%E6%9B%B4%E5%BF%AB%EF%BC%81%E4%B8%80%E7%A7%92%E6%89%BE%E5%88%B0%E7%9F%A5%E8%AF%86%E5%BA%93%3C%2Fp%3E%5Cn%3Cp%3E%E6%B8%85%E7%88%BD%EF%BC%81%E4%BF%A1%E6%81%AF%E6%B5%81%E4%BA%95%E4%BA%95%E6%9C%89%E6%9D%A1%3C%2Fp%3E%5Cn%3Cp%3E%E6%9B%B4%E9%9D%93%EF%BC%81%E5%85%A8%E6%96%B0%E8%A7%86%E8%A7%89%E9%A3%8E%E6%A0%BC%3C%2Fp%3E%5Cn%5Cn%3C%2Fdiv%3E%5Cn%3Cdiv%20style%3D%5C%22padding-top%3A10px%3B%5C%22%3E%3Cp%3E%3Ca%20href%3D%5C%22https%3A%2F%2Fyuque.antfin.com%2Flark%2Fopen%2Fgv5nfh%5C%22%20target%3D%5C%22_blank%5C%22%3E%20%E4%BA%86%E8%A7%A3%E8%AF%A6%E6%83%85%3C%2Fa%3E%3C%2Fp%3E%3C%2Fdiv%3E%5Cn%3C%2Fdiv%3E%5Cn%3C%2Fdiv%3E%5Cn%3Cdiv%20style%3D%5C%22clear%3Aboth%5C%22%3E%3C%2Fdiv%3E%5Cn%3C%2Fdiv%3E%22%2C%22type%22%3A%22global%22%2C%22user_id%22%3Anull%2C%22created_at%22%3A%222021-01-18T06%3A29%3A00.000Z%22%2C%22updated_at%22%3A%222021-01-18T06%3A29%3A00.000Z%22%7D%2C%22siteName%22%3A%22%E8%AF%AD%E9%9B%80%22%2C%22readTip%22%3A%7B%22global%22%3A700030%2C%22editor%22%3A4%2C%22card%22%3A1%2C%22toc%22%3A1%2C%22doc_editor_help%22%3A1%2C%22toc_editor_help%22%3A1%2C%22invite_register_guide%22%3A1%2C%22feature_new_catalog_2%22%3A1%2C%22doc_editor_collab%22%3A1%2C%22feature_vote%22%3A1%2C%22feature_doc_add_resource%22%3A1%2C%22feature_mind%22%3A1%2C%22feature_book_custom_index_landing%22%3A1%2C%22feature_note_release%22%3A1%7D%2C%22imageServiceDomains%22%3A%5B%22cdn.yuque.com%22%2C%22cdn.nlark.com%22%2C%22img.shields.io%22%2C%22travis-ci.org%22%2C%22api.travis-ci.org%22%2C%22npm.packagequality.com%22%2C%22snyk.io%22%2C%22coveralls.io%22%2C%22badgen.now.sh%22%2C%22badgen.net%22%2C%22packagephobia.now.sh%22%2C%22duing.alibaba-inc.com%22%2C%22npm.alibaba-inc.com%22%2C%22web.npm.alibaba-inc.com%22%2C%22npmjs.com%22%2C%22npmjs.org%22%2C%22npg.dockerlab.alipay.net%22%2C%22cp.alibaba.net%22%2C%22private-alipayobjects.alipay.com%22%2C%22googleusercontent.com%22%2C%22lh3.googleusercontent.com%22%2C%22user-images.githubusercontent.com%22%2C%22jarvis.alipay.net%22%2C%22alipay.net%22%2C%22webmail.alibaba-inc.com%22%2C%22wiki.1verge.test%22%2C%22confluence.lzd.co%22%2C%22lzd.co%22%2C%22doc.ucweb.local%22%2C%22work.alibaba.net%22%2C%22work.alibaba-inc.com%22%2C%22lark-assets-prod-aliyun.oss-accelerate.aliyuncs.com%22%2C%22img01.daily.taobaocdn.net%22%2C%22img02.daily.taobaocdn.net%22%2C%22img03.daily.taobaocdn.net%22%2C%22img04.daily.taobaocdn.net%22%2C%22lh1.googleusercontent.com%22%2C%22lh2.googleusercontent.com%22%2C%22lh3.googleusercontent.com%22%2C%22lh4.googleusercontent.com%22%2C%22lh5.googleusercontent.com%22%2C%22lh6.googleusercontent.com%22%2C%22lh7.googleusercontent.com%22%2C%22lh8.googleusercontent.com%22%2C%22lh9.googleusercontent.com%22%2C%22raw.githubusercontent.com%22%2C%22github.com%22%2C%22doc.alipay.net%22%2C%22wa.ucdns.uc.cn%22%2C%22marketing.aliyun-inc.com%22%2C%22lark-temp.oss-cn-hangzhou.aliyuncs.com%22%2C%22yuque.antfin-inc.com%22%2C%22yuque.antfin.com%22%2C%22cdn.nlark.com%22%5D%2C%22sharePlatforms%22%3A%5B%22dingtalk%22%5D%2C%22locale%22%3A%22zh-cn%22%2C%22sharePage%22%3Atrue%2C%22share%22%3A%7B%22id%22%3A1346364%2C%22token%22%3A%2279669dd7-2707-4655-9fa1-b177a10f2d31%22%2C%22target_type%22%3A%22Doc%22%2C%22target_id%22%3A12071284%2C%22scope%22%3A0%2C%22status%22%3A0%2C%22created_at%22%3A%222020-11-13T02%3A05%3A05.000Z%22%2C%22updated_at%22%3A%222020-11-13T02%3A05%3A05.000Z%22%2C%22password_enable%22%3Afalse%2C%22_serializer%22%3A%22web.share%22%7D%2C%22matchCondition%22%3A%7B%22page%22%3A%22docShare%22%2C%22fileType%22%3A%22Doc%22%7D%2C%22forbidLoginCard%22%3Atrue%2C%22loginCardPV%22%3A0%2C%22group%22%3A%7B%22id%22%3A212690%2C%22type%22%3A%22User%22%2C%22login%22%3A%22xinan.cj%22%2C%22name%22%3A%22%E8%BE%9B%E5%AE%89%22%2C%22description%22%3Anull%2C%22avatar_url%22%3A%22https%3A%2F%2Fyuque.antfin-inc.com%2Fr%2Favatar_buc%2F209015%22%2C%22owner_id%22%3Anull%2C%22books_count%22%3A3%2C%22public_books_count%22%3A1%2C%22topics_count%22%3A0%2C%22public_topics_count%22%3A0%2C%22members_count%22%3A0%2C%22public%22%3A1%2C%22scene%22%3Anull%2C%22created_at%22%3A%222019-05-13T05%3A21%3A18.000Z%22%2C%22updated_at%22%3A%222021-04-09T08%3A45%3A17.000Z%22%2C%22organization_id%22%3A0%2C%22isPaid%22%3Atrue%2C%22member_level%22%3A1%2C%22grains_sum%22%3A0%2C%22status%22%3A1%2C%22organization%22%3Anull%2C%22owners%22%3Anull%2C%22_serializer%22%3A%22web.group%22%7D%2C%22book%22%3A%7B%22content_updated_at%22%3A%222021-04-09T08%3A45%3A46.718Z%22%2C%22toc_yml%22%3A%22-%20type%3A%20META%5Cn%20%20count%3A%2026%5Cn%20%20display_level%3A%201%5Cn%20%20tail_type%3A%20SLUG%5Cn%20%20base_version_id%3A%20101029345%5Cn%20%20max_level%3A%201%5Cn%20%20published%3A%20true%5Cn%20%20last_updated_at%3A%20’2021-04-09T08%3A45%3A46.815Z’%5Cn%20%20version_id%3A%20105686780%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20FY2022%E8%A7%84%E5%88%92%5Cn%20%20uuid%3A%20w27yH4YIOTEAStqU%5Cn%20%20url%3A%20vmmibv%5Cn%20%20prev_uuid%3A%20’’%5Cn%20%20sibling_uuid%3A%20Z2PUXHzZJidUXeHb%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2044148797%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2044148797%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20Hbase%E5%AD%A6%E4%B9%A0%5Cn%20%20uuid%3A%20Z2PUXHzZJidUXeHb%5Cn%20%20url%3A%20ukdghk%5Cn%20%20prev_uuid%3A%20w27yH4YIOTEAStqU%5Cn%20%20sibling_uuid%3A%201jGacBgjuTwN1rlV%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2013246833%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2013246833%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20hbase%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%5Cn%20%20uuid%3A%201jGacBgjuTwN1rlV%5Cn%20%20url%3A%20gf1bhr%5Cn%20%20prev_uuid%3A%20Z2PUXHzZJidUXeHb%5Cn%20%20sibling_uuid%3A%20DlvcyM6kn9yQXcJb%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2025671903%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2025671903%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20Spring%E5%AE%B9%E5%99%A8%5Cn%20%20uuid%3A%20DlvcyM6kn9yQXcJb%5Cn%20%20url%3A%20smndes%5Cn%20%20prev_uuid%3A%201jGacBgjuTwN1rlV%5Cn%20%20sibling_uuid%3A%20yfvYywxUR2kniUoL%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2012071284%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2012071284%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E4%B8%8Epandora%E5%AD%A6%E4%B9%A0%5Cn%20%20uuid%3A%20yfvYywxUR2kniUoL%5Cn%20%20url%3A%20kbthcx%5Cn%20%20prev_uuid%3A%20DlvcyM6kn9yQXcJb%5Cn%20%20sibling_uuid%3A%20rH0xslUSkprW1YFy%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2011409713%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2011409713%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E7%94%9F%E6%84%8F%E5%8F%82%E8%B0%8B%E4%BA%8C%E5%A5%97%E5%9F%9F%E5%90%8D%5Cn%20%20uuid%3A%20rH0xslUSkprW1YFy%5Cn%20%20url%3A%20xd6kt9%5Cn%20%20prev_uuid%3A%20yfvYywxUR2kniUoL%5Cn%20%20sibling_uuid%3A%20H9EJDIQ-DWDyxOCU%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2010847278%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2010847278%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E7%94%9F%E6%84%8F%E5%8F%82%E8%B0%8B%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%89%B4%E6%9D%83%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94%5Cn%20%20uuid%3A%20H9EJDIQ-DWDyxOCU%5Cn%20%20url%3A%20bcstin%5Cn%20%20prev_uuid%3A%20rH0xslUSkprW1YFy%5Cn%20%20sibling_uuid%3A%20snSnvsA6luMNDAca%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2010416690%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2010416690%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E8%BE%9B%E5%AE%89S1%20OKR%5Cn%20%20uuid%3A%20snSnvsA6luMNDAca%5Cn%20%20url%3A%20spz1m0%5Cn%20%20prev_uuid%3A%20H9EJDIQ-DWDyxOCU%5Cn%20%20sibling_uuid%3A%20’377587%3Azhurbmubyfxpc3z7’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%2010140474%5Cn%20%20level%3A%200%5Cn%20%20id%3A%2010140474%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%B9%B3%E5%8F%B0DataWin%5Cn%20%20uuid%3A%20’377587%3Azhurbmubyfxpc3z7’%5Cn%20%20url%3A%20dheflo%5Cn%20%20prev_uuid%3A%20snSnvsA6luMNDAca%5Cn%20%20sibling_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%206395755%5Cn%20%20level%3A%200%5Cn%20%20id%3A%206395755%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20UNCREATED_DOC%5Cn%20%20title%3A%20%E9%A1%B9%E7%9B%AE%5Cn%20%20uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20url%3A%20eai7ok%5Cn%20%20prev_uuid%3A%20’377587%3Azhurbmubyfxpc3z7’%5Cn%20%20sibling_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20child_uuid%3A%20’377587%3Aodmnk8ue03p249n0’%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%20’’%5Cn%20%20level%3A%200%5Cn%20%20id%3A%20’’%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E6%A0%87%E9%A2%98%E4%BC%98%E5%8C%96%E6%80%BB%E7%BB%93%5Cn%20%20uuid%3A%20’377587%3Aodmnk8ue03p249n0’%5Cn%20%20url%3A%20lqzbns%5Cn%20%20prev_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20sibling_uuid%3A%20’377587%3Agbe1b4ycod7ohrf0’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20doc_id%3A%204323472%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204323472%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E5%93%81%E7%B1%BB-%E8%BF%9E%E5%B8%A6%26%E7%AB%9E%E5%93%81%5Cn%20%20uuid%3A%20’377587%3Agbe1b4ycod7ohrf0’%5Cn%20%20url%3A%20vwz6gr%5Cn%20%20prev_uuid%3A%20’377587%3Aodmnk8ue03p249n0’%5Cn%20%20sibling_uuid%3A%20’377587%3Ayhza21r4g5nykbhu’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20doc_id%3A%204647297%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204647297%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E7%94%9F%E6%84%8F%E5%8F%82%E8%B0%8B%E4%B8%BB%E8%A6%81%E5%BA%94%E7%94%A8%E4%B8%8E%E8%B0%83%E7%94%A8%E9%93%BE%E8%B7%AF%5Cn%20%20uuid%3A%20’377587%3Ayhza21r4g5nykbhu’%5Cn%20%20url%3A%20pu9l2d%5Cn%20%20prev_uuid%3A%20’377587%3Agbe1b4ycod7ohrf0’%5Cn%20%20sibling_uuid%3A%20’377587%3Afg5pz5nvc7ymp4hi’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20doc_id%3A%204804501%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204804501%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E5%86%85%E5%AE%B93.0%E6%80%BB%E7%BB%93%5Cn%20%20uuid%3A%20’377587%3Afg5pz5nvc7ymp4hi’%5Cn%20%20url%3A%20rwnili%5Cn%20%20prev_uuid%3A%20’377587%3Ayhza21r4g5nykbhu’%5Cn%20%20sibling_uuid%3A%20’’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20doc_id%3A%204178659%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204178659%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20UNCREATED_DOC%5Cn%20%20title%3A%20%E5%AD%A6%E4%B9%A0%5Cn%20%20uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20url%3A%20xgtwmi%5Cn%20%20prev_uuid%3A%20’377587%3Aoowsl3nxh3r21g1a’%5Cn%20%20sibling_uuid%3A%20’’%5Cn%20%20child_uuid%3A%200XxXZvphZFOXemXO%5Cn%20%20parent_uuid%3A%20’’%5Cn%20%20doc_id%3A%20’’%5Cn%20%20level%3A%200%5Cn%20%20id%3A%20’’%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20dispatchserverlet%5Cn%20%20uuid%3A%200XxXZvphZFOXemXO%5Cn%20%20url%3A%20xqstxy%5Cn%20%20prev_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20sibling_uuid%3A%20Tv8lFbxMLHPz45DK%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%2011988607%5Cn%20%20level%3A%201%5Cn%20%20id%3A%2011988607%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20Spring%20AOP%5Cn%20%20uuid%3A%20Tv8lFbxMLHPz45DK%5Cn%20%20url%3A%20fhf6a4%5Cn%20%20prev_uuid%3A%200XxXZvphZFOXemXO%5Cn%20%20sibling_uuid%3A%20NZYoco9dr8RidQ8p%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%2011988533%5Cn%20%20level%3A%201%5Cn%20%20id%3A%2011988533%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%200%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20Spring%5Cn%20%20uuid%3A%20NZYoco9dr8RidQ8p%5Cn%20%20url%3A%20urrzgo%5Cn%20%20prev_uuid%3A%20Tv8lFbxMLHPz45DK%5Cn%20%20sibling_uuid%3A%20PmwONlnPQxohVBuh%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%2010124010%5Cn%20%20level%3A%201%5Cn%20%20id%3A%2010124010%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E5%8F%8D%E5%B0%84%E4%B8%8E%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%5Cn%20%20uuid%3A%20PmwONlnPQxohVBuh%5Cn%20%20url%3A%20atfzrg%5Cn%20%20prev_uuid%3A%20NZYoco9dr8RidQ8p%5Cn%20%20sibling_uuid%3A%20’377587%3Apmta78zal9hf83mx’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%208647525%5Cn%20%20level%3A%201%5Cn%20%20id%3A%208647525%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20git%5Cn%20%20uuid%3A%20’377587%3Apmta78zal9hf83mx’%5Cn%20%20url%3A%20ndbm4e%5Cn%20%20prev_uuid%3A%20PmwONlnPQxohVBuh%5Cn%20%20sibling_uuid%3A%20’377587%3Adxgqzrycyg9nw1sz’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%206779728%5Cn%20%20level%3A%201%5Cn%20%20id%3A%206779728%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20java%20json%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%20getter%E5%92%8Csetter%E6%96%B9%E6%B3%95%5Cn%20%20uuid%3A%20’377587%3Adxgqzrycyg9nw1sz’%5Cn%20%20url%3A%20ggyage%5Cn%20%20prev_uuid%3A%20’377587%3Apmta78zal9hf83mx’%5Cn%20%20sibling_uuid%3A%20’377587%3Alfrofsglrageh6iy’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%204323474%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204323474%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20%E5%8C%85%E8%A3%85%E7%B1%BB%E4%B8%8E%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E8%A3%85%E7%AE%B1%E6%8B%86%E7%AE%B1%5Cn%20%20uuid%3A%20’377587%3Alfrofsglrageh6iy’%5Cn%20%20url%3A%20tvq9qk%5Cn%20%20prev_uuid%3A%20’377587%3Adxgqzrycyg9nw1sz’%5Cn%20%20sibling_uuid%3A%20’377587%3Axpzgqhpm2fk5whqp’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%204335067%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204335067%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20bpms%E5%B7%A5%E4%BD%9C%E6%B5%81%E5%AE%9E%E6%88%98%5Cn%20%20uuid%3A%20’377587%3Axpzgqhpm2fk5whqp’%5Cn%20%20url%3A%20gsn2xg%5Cn%20%20prev_uuid%3A%20’377587%3Alfrofsglrageh6iy’%5Cn%20%20sibling_uuid%3A%20’377587%3Auf5dz6yyclfzbrpg’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%206675096%5Cn%20%20level%3A%201%5Cn%20%20id%3A%206675096%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20The%20Hadoop%20Distributed%20File%20System%5Cn%20%20uuid%3A%20’377587%3Auf5dz6yyclfzbrpg’%5Cn%20%20url%3A%20cw548i%5Cn%20%20prev_uuid%3A%20’377587%3Axpzgqhpm2fk5whqp’%5Cn%20%20sibling_uuid%3A%20’377587%3Agb105xzr8bt3313g’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%207058065%5Cn%20%20level%3A%201%5Cn%20%20id%3A%207058065%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20oneness%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%5Cn%20%20uuid%3A%20’377587%3Agb105xzr8bt3313g’%5Cn%20%20url%3A%20adtbzs%5Cn%20%20prev_uuid%3A%20’377587%3Auf5dz6yyclfzbrpg’%5Cn%20%20sibling_uuid%3A%20’377587%3Atug7tg2b9lspthqa’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%204776813%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204776813%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn-%20type%3A%20DOC%5Cn%20%20title%3A%20java%E6%B3%9B%E5%9E%8B%5Cn%20%20uuid%3A%20’377587%3Atug7tg2b9lspthqa’%5Cn%20%20url%3A%20xrsoc6%5Cn%20%20prev_uuid%3A%20’377587%3Agb105xzr8bt3313g’%5Cn%20%20sibling_uuid%3A%20’’%5Cn%20%20child_uuid%3A%20’’%5Cn%20%20parent_uuid%3A%20’377587%3Azkkt8ot63me5ea70’%5Cn%20%20doc_id%3A%204647286%5Cn%20%20level%3A%201%5Cn%20%20id%3A%204647286%5Cn%20%20open_window%3A%201%5Cn%20%20visible%3A%201%5Cn%22%2C%22enable_comment%22%3Atrue%2C%22enable_auto_publish%22%3Afalse%2C%22enable_export%22%3Atrue%2C%22enable_visitor_watermark%22%3Afalse%2C%22copyright_watermark%22%3A%22%22%2C%22image_copyright_watermark%22%3A%22%22%2C%22enable_search_engine%22%3Afalse%2C%22enable_announcement%22%3Atrue%2C%22enable_webhook%22%3Atrue%2C%22enable_trash%22%3Atrue%2C%22enable_document_copy%22%3Atrue%2C%22enable_toc%22%3Atrue%2C%22layout%22%3A%22Book%22%2C%22doc_viewport%22%3A%22fixed%22%2C%22doc_typography%22%3Anull%2C%22isBanned%22%3Afalse%2C%22catalog_display_level%22%3A1%2C%22catalog_tail_type%22%3A%22SLUG%22%2C%22enable_catalog_nodes%22%3Atrue%2C%22id%22%3A377587%2C%22space_id%22%3A0%2C%22type%22%3A%22Book%22%2C%22slug%22%3A%22qx9xwp%22%2C%22name%22%3A%22xinan%22%2C%22description%22%3A%22%22%2C%22toc%22%3A%22-%20%5B%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%B9%B3%E5%8F%B0DataWin%5D(dheflo%20%5C%226395755%5C%22)%5Cn-%20%5B%E9%A1%B9%E7%9B%AE%5D(eai7ok)%5Cn%20%20-%20%5B%E6%A0%87%E9%A2%98%E4%BC%98%E5%8C%96%E6%80%BB%E7%BB%93%5D(lqzbns%20%5C%224323472%5C%22)%5Cn%20%20-%20%5B%E5%93%81%E7%B1%BB-%E8%BF%9E%E5%B8%A6%26%E7%AB%9E%E5%93%81%5D(vwz6gr%20%5C%224647297%5C%22)%5Cn%20%20-%20%5B%E7%94%9F%E6%84%8F%E5%8F%82%E8%B0%8B%E4%B8%BB%E8%A6%81%E5%BA%94%E7%94%A8%E4%B8%8E%E8%B0%83%E7%94%A8%E9%93%BE%E8%B7%AF%5D(pu9l2d%20%5C%224804501%5C%22)%5Cn%20%20-%20%5B%E5%86%85%E5%AE%B93.0%E6%80%BB%E7%BB%93%5D(rwnili%20%5C%224178659%5C%22)%5Cn-%20%5B%E5%AD%A6%E4%B9%A0%5D(xgtwmi)%5Cn%20%20-%20%5Bgit%5D(ndbm4e%20%5C%226779728%5C%22)%5Cn%20%20-%20%5Bjava%20json%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%20getter%E5%92%8Csetter%E6%96%B9%E6%B3%95%5D(ggyage%20%5C%224323474%5C%22)%5Cn%20%20-%20%5B%E5%8C%85%E8%A3%85%E7%B1%BB%E4%B8%8E%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E8%A3%85%E7%AE%B1%E6%8B%86%E7%AE%B1%5D(tvq9qk%20%5C%224335067%5C%22)%5Cn%20%20-%20%5Bbpms%E5%B7%A5%E4%BD%9C%E6%B5%81%E5%AE%9E%E6%88%98%5D(gsn2xg%20%5C%226675096%5C%22)%5Cn%20%20-%20%5BThe%20Hadoop%20Distributed%20File%20System%5D(cw548i%20%5C%227058065%5C%22)%5Cn%20%20-%20%5Boneness%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%5D(adtbzs%20%5C%224776813%5C%22)%5Cn%20%20-%20%5Bjava%E6%B3%9B%E5%9E%8B%5D(xrsoc6%20%5C%224647286%5C%22)%22%2C%22body%22%3A%22%7B%5C%22enable_comment%5C%22%3Atrue%2C%5C%22enable_export%5C%22%3Atrue%2C%5C%22catalog_display_level%5C%22%3A1%2C%5C%22catalog_tail_type%5C%22%3A%5C%22SLUG%5C%22%2C%5C%22enable_catalog_nodes%5C%22%3Atrue%7D%22%2C%22user_id%22%3A212690%2C%22creator_id%22%3A212690%2C%22public%22%3A0%2C%22status%22%3A0%2C%22excellent%22%3A0%2C%22menu_type%22%3A0%2C%22items_count%22%3A28%2C%22likes_count%22%3A0%2C%22watches_count%22%3A1%2C%22content_updated_at_ms%22%3A1617957946718%2C%22deleted_slug%22%3Anull%2C%22created_at%22%3A%222019-07-09T06%3A11%3A00.000Z%22%2C%22updated_at%22%3A%222021-04-09T08%3A45%3A46.000Z%22%2C%22pinned_at%22%3Anull%2C%22archived_at%22%3Anull%2C%22collaboration_count%22%3A0%2C%22stack_id%22%3Anull%2C%22rank%22%3Anull%2C%22resource_size%22%3A0%2C%22book_department_updated_at%22%3Anull%2C%22scene%22%3Anull%2C%22source%22%3Anull%2C%22original%22%3Anull%2C%22premium%22%3Anull%2C%22announcement%22%3Anull%2C%22is_trust%22%3A0%2C%22cover%22%3Anull%2C%22index_id%22%3Anull%2C%22toc_updated_at%22%3A%222021-04-09T08%3A45%3A46.000Z%22%2C%22organization_id%22%3A0%2C%22deleted_at%22%3Anull%7D%2C%22doc%22%3A%7B%22meta%22%3A%7B%7D%2C%22content_updated_at%22%3A%222021-03-31T07%3A19%3A43.000Z%22%2C%22body%22%3A%22%22%2C%22body_asl%22%3A%22%22%2C%22body_draft%22%3A%22%22%2C%22body_draft_asl%22%3A%22%22%2C%22premium_body%22%3A%22%22%2C%22premium_body_asl%22%3A%22%22%2C%22premium_body_draft%22%3A%22%22%2C%22premium_body_draft_asl%22%3A%22%22%2C%22isSuspect%22%3Afalse%2C%22full_body%22%3A%22%22%2C%22full_body_asl%22%3A%22%22%2C%22full_body_draft%22%3A%22%22%2C%22full_body_draft_asl%22%3A%22%22%2C%22editor_meta%22%3A%22%7B%5C%22viewport%5C%22%3A%5C%22adapt%5C%22%2C%5C%22typography%5C%22%3A%5C%22classic%5C%22%7D%22%2C%22editor_meta_draft%22%3A%22%7B%5C%22viewport%5C%22%3A%5C%22adapt%5C%22%2C%5C%22typography%5C%22%3A%5C%22classic%5C%22%7D%22%2C%22premium_expired%22%3Afalse%2C%22id%22%3A12071284%2C%22space_id%22%3A0%2C%22type%22%3A%22Doc%22%2C%22sub_type%22%3Anull%2C%22slug%22%3A%22smndes%22%2C%22book_id%22%3A377587%2C%22user_id%22%3A212690%2C%22title%22%3A%22Spring%E5%AE%B9%E5%99%A8%22%2C%22tag%22%3Anull%2C%22cover%22%3A%22https%3A%2F%2Fintranetproxy.alipay.com%2Fskylark%2Flark%2F0%2F2020%2Fpng%2F212690%2F1604286340500-04c33038-e5ec-4785-9320-b3e7a280de44.png%22%2C%22custom_cover%22%3A%22https%3A%2F%2Fintranetproxy.alipay.com%2Fskylark%2Flark%2F0%2F2020%2Fpng%2F212690%2F1603956945170-258b427e-28d7-452e-ac02-d5f330d3970d.png%22%2C%22description%22%3A%22Spring%E5%AE%B9%E5%99%A8%E6%A6%82%E8%BF%B0spring%E7%9A%84%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84%E5%A6%82%E4%B8%8B%EF%BC%8C%E5%85%B6%E4%B8%ADspring%20%E5%AE%B9%E5%99%A8%E4%BD%9C%E4%B8%BAspring%20%E7%9A%84%E5%BA%95%E5%BA%A7%EF%BC%8C%E5%85%B6%E4%BB%96%E5%8A%9F%E8%83%BD%E5%A6%82web%E3%80%81data%20%E7%AD%89%E9%83%BD%E6%98%AF%E5%9F%BA%E4%BA%8Espring%20%E5%AE%B9%E5%99%A8%E5%8F%91%E5%B1%95%E8%B5%B7%E6%9D%A5%EF%BC%8C%E6%9C%AC%E6%96%87%E9%92%88%E5%AF%B9spring%E5%AE%B9%E5%99%A8%E8%BF%9B%E8%A1%8C%E8%AF%A6%E7%BB%86%E7%9A%84%E5%88%86%E6%9E%90%E3%80%82Spring%20%E5%AE%B9%E5%99%A8Bean%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF%E5%AE%9A%E4%B9%89%E4%BA%86Bean%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8F%8A%E4%BE%9D%E8%B5%96%E5%85%B3%E7%B3%BB%EF%BC%8CSpring%E5%AE%B9%E5%99%A8%E6%A0%B9%E6%8D%AE%E5%90%84%E7%A7%8D%E5%BD%A2…%22%2C%22custom_description%22%3Anull%2C%22title_draft%22%3Anull%2C%22format%22%3A%22lake%22%2C%22status%22%3A1%2C%22read_status%22%3A1%2C%22view_status%22%3A0%2C%22public%22%3A1%2C%22comments_count%22%3A0%2C%22likes_count%22%3A0%2C%22collaboration_count%22%3A0%2C%22last_editor_id%22%3A212690%2C%22draft_version%22%3A299%2C%22deleted_slug%22%3Anull%2C%22word_count%22%3A7105%2C%22created_at%22%3A%222020-10-26T01%3A49%3A26.000Z%22%2C%22updated_at%22%3A%222021-03-31T07%3A19%3A44.000Z%22%2C%22published_at%22%3A%222021-03-31T07%3A19%3A43.000Z%22%2C%22first_published_at%22%3A%222020-10-30T10%3A28%3A42.000Z%22%2C%22selected_at%22%3Anull%2C%22pinned_at%22%3Anull%2C%22premium_days_count%22%3Anull%2C%22premium_price%22%3Anull%2C%22premium_started_at%22%3Anull%2C%22premium_expired_at%22%3Anull%2C%22doc_meta%22%3A%22%7B%5C%22viewport%5C%22%3A%5C%22adapt%5C%22%2C%5C%22typography%5C%22%3A%5C%22classic%5C%22%7D%22%2C%22doc_meta_draft%22%3A%22%7B%5C%22viewport%5C%22%3A%5C%22adapt%5C%22%2C%5C%22typography%5C%22%3A%5C%22classic%5C%22%7D%22%2C%22deleted_at%22%3Anull%2C%22body_html%22%3A%22%22%2C%22full_body_html%22%3A%22%22%2C%22abilities%22%3A%7B%22read%22%3Atrue%2C%22update%22%3Afalse%2C%22destroy%22%3Afalse%2C%22read_origin%22%3Afalse%7D%2C%22contributors%22%3A%5B%7B%22work_id%22%3A%22209015%22%2C%22avatar_url%22%3A%22https%3A%2F%2Fyuque.antfin-inc.com%2Fr%2Favatar_buc%2F209015%22%2C%22isActive%22%3Atrue%2C%22isInactive%22%3Afalse%2C%22isDeactivated%22%3Afalse%2C%22isTerminated%22%3Afalse%2C%22isBanned%22%3Afalse%2C%22isBlocked%22%3Afalse%2C%22isMuted%22%3Afalse%2C%22isExtcontact%22%3Afalse%2C%22isPaid%22%3Atrue%2C%22hasPaidBefore%22%3Atrue%2C%22id%22%3A212690%2C%22space_id%22%3A0%2C%22type%22%3A%22User%22%2C%22login%22%3A%22xinan.cj%22%2C%22name%22%3A%22%E8%BE%9B%E5%AE%89%22%2C%22description%22%3Anull%2C%22avatar%22%3Anull%2C%22owner_id%22%3Anull%2C%22topics_count%22%3A0%2C%22public_topics_count%22%3A0%2C%22members_count%22%3A0%2C%22books_count%22%3A3%2C%22public_books_count%22%3A1%2C%22followers_count%22%3A0%2C%22following_count%22%3A0%2C%22account_id%22%3Anull%2C%22role%22%3A1%2C%22status%22%3A1%2C%22public%22%3A1%2C%22wants_email%22%3Atrue%2C%22wants_marketing_email%22%3Atrue%2C%22topic_updated_at_ms%22%3A0%2C%22deleted_slug%22%3Anull%2C%22language%22%3A%22zh-cn%22%2C%22organization_id%22%3A0%2C%22emp_type%22%3A%22%E6%AD%A3%E5%BC%8F%20-%20%E6%AD%A3%E5%BC%8F%E5%91%98%E5%B7%A5%22%2C%22group_department_updated_at%22%3Anull%2C%22member_level%22%3A1%2C%22expired_at%22%3A%222123-02-27T15%3A59%3A59.000Z%22%2C%22scene%22%3Anull%2C%22source%22%3Anull%2C%22max_member%22%3Anull%2C%22created_at%22%3A%222019-05-13T05%3A21%3A18.000Z%22%2C%22updated_at%22%3A%222021-04-09T08%3A45%3A17.000Z%22%2C%22grains_sum%22%3A0%2C%22punish_expired_at%22%3Anull%2C%22deleted_at%22%3Anull%7D%5D%2C%22hits%22%3A6%2C%22token%22%3A%2279669dd7-2707-4655-9fa1-b177a10f2d31%22%2C%22ogpTitle%22%3A%22Spring%E5%AE%B9%E5%99%A8%20%C2%B7%20%E8%AF%AD%E9%9B%80%22%7D%2C%22search%22%3A%7B%22display%22%3Atrue%2C%22scope%22%3A%22%2F%22%7D%2C%22prefetch%22%3A%22fetchShareDocData%22%2C%22paymentInfo%22%3A%7B%7D%2C%22enable_payment%22%3Afalse%2C%22userMemberInfo%22%3A%7B%22usage%22%3A%7B%22attachment_size%22%3A152319570%2C%22image_size%22%3A283465860%2C%22video_size%22%3A0%2C%22max_upload_size%22%3A107374182400%2C%22_serializer%22%3A%22web.user_usage_statistics%22%7D%2C%22expired_at%22%3Anull%2C%22countDownDays%22%3Anull%2C%22isAllowRenew%22%3Afalse%2C%22receipt%22%3Anull%2C%22groupOwners%22%3A%5B%5D%2C%22hasOrder%22%3Afalse%7D%2C%22enable_datasheet%22%3Atrue%2C%22enable_baiyan_work_process%22%3Atrue%2C%22topTip%22%3Anull%2C%22login%22%3A%7B%22loginType%22%3A%22normal%22%2C%22enablePlatforms%22%3A%5B%22aone_teambition%22%5D%2C%22isWechatMobileApp%22%3Afalse%7D%2C%22isDesktopApp%22%3Afalse%2C%22isAlipayApp%22%3Afalse%2C%22isDingTalkMiniApp%22%3Afalse%7D”)); })();
Adblocker
Spring容器
Spring容器概述
spring的整体架构如下,其中spring 容器作为spring 的底座,其他功能如web、data 等都是基于spring 容器发展起来,本文针对spring容器进行详细的分析。
Spring 容器
Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载、实例化Bean,并建立Bean和Bean的依赖关系,最后将这些准备就绪的Bean放到Bean缓存池中,以供外层的应用程序进行调用。Spring容器包括beanFactory 和applicationContext,整个spring容器就是通过beanFactory和applicationContext来完成bean的配置、注册、实例化、初始化、读取等一些列操作。
applicationContext
抽象类AbstractApplicationContext包含了容器的绝大部分功能,spring容器的启动流程就是在这个类的refresh方法中。下面的子类是应对不同的场景,我们在创建一个spring容器的时候通常会这样做。
//编译路径
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//系统文件路径
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");
//mvc启动方式
XmlWebApplicationContext context = new XmlWebApplicationContext("applicationContext.xml");
beanFactory
DefaulistListTableBeanFactory包含了beanFactory的核心能力,容器启动默认创建DefaulistListTableBeanFactory实例。Spring读取bean的配置创建的beanDefinition对象会存放在bean注册表中,根据beanDefinition对象创建的singleton类型的bean实例会存放在bean缓存池中,bean的注册表和bean的缓存池都由DefaulistListTableBeanFactory维护,由此可见DefaulistListTableBeanFactory在spring容器中的地位。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable{}
初始化spring容器
tomcat启动后回调ContextLoaderListener启动spring容器
web容器如tomcat启动时,根据web.xml中的配置,回调监听器ContextLoaderListener ,ContextLoaderListener实现了tomcat的ServletContextListener 接口,通过contextInitialized方法完成webApplicationContext的初始化
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
</web-app>
初始化过程首先创建一个webApplicationContext,如果没有自定义webApplicationContext会使用默认的默认使用XmlWebApplicationContext,之后会判断XmlWebApplicationContext是否被刷新过,如果没有则调用configureAndRefreshWebApplicationContext刷新容器,获取配置文件的位置参数,一般资源文件配置在applicationContext.xml中,这也是webapplicationContext默认的配置文件,初始化资源,最后调用refresh方法,refreshspring容器启动的核心方法,也是spring容器开始启动的入口。容器启动完成后会把spring的webApplicationContext注入到tomcat的servletContext
//ContextLoaderListener.java
//初始化sping 容器
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
//ContextLoader.java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...省略非关键代码
try {
//context为空,则创建一个,默认使用XmlWebApplicationContext
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
//激活context
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//核心方法,下面分析
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//把spring WebApplicationContext 注入到tomcat 的servletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
}
//ContextLoader.java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//设置servletContext
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
//设置context配置位置,一般是applicationContext.xml
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
//初始化资源
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
//spring容器的核心方法,真正启动spring的入口
wac.refresh();
}
Spring容器启动过程
//org/springframework/context/support/AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备启动上下文
prepareRefresh();
// 创建DefaultListableBeanFactory,完成bean配置的读取、解析,核心方法,下面分析
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//设置beanFactory属性,包括classloader、BeanPostProcessor
//注册ApplicationContextAwareProcessor实例实现BeanPostProcessor完成扩展功能(扩展点一节分析)
prepareBeanFactory(beanFactory);
try {
// 调用默认的beanPostProcessor 如 ServletContextAwareProcessor
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//实例化注册的BeanFactoryPostProcessor bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册 beanPostProcessor 用来在bean的初始化前后实现扩展逻辑
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();
// 实例化非懒加载单例bean,核心方法下面分析
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
}
}
bean解析&注册
beanFactory负责管理bean的解析、注册、实例化等操作,所以在bean解析前需要先创建一个beanFactory。如果之前已经创建beanFactory,需要先销毁bean并关闭beanFactory。Spring容器会创建一个DefaultListableBeanFactory实例,这个类非常重要,里面维护了bean的注册表和bean的缓存池,bean的注册表用来存放根据配置解析出的beanDefinition对象;bean的缓存池存放beanDefinition对象实例化出来的bean实例,我们应用平时使用到的bean就是从bean缓存池中取出来的。本小节先介绍bean的解析与注册过程,spring根据配置(xml、注解、java类)解析后的bean会存放在注册表里面,根据注册表实例化bean会存放在缓存池里面。然后在customizeBeanFactory方法中完成一些个性化配置,之后会加载配置的bean到spring的bean注册表
//org/springframework/context/support/AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
//org/springframework/context/support/AbstractRefreshableApplicationContext.java
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//完成bean的解析、注册到spring用起的bean注册表
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
}
读取bean配置,转换成dom对象
首先创建XmlBeanDefinitionReader,完成环境配置与初始化等,核心是loadBeanDefinitions(beanDefinitionReader),它会从配置文件加载bean,首先获取配置文件的位置,然后从配置文件中解析出resource。其中一系列的重载方法目的是把配置文件从location到获取资源到最终解析成document对象,这个转换过程是location->resource->inputstream->inputsource->document。真正解析bean的时候是解析document里面的element元素。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//省略非核心代码
ResourceLoader resourceLoader = getResourceLoader();
//如果是正则匹配
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//从配置文件解析resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
}
}
else {
// Can only load single resources by absolute URL.
//从配置文件解析resource
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
}
}
//对resource进行编码
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//省略非核心代码
try {
//resource -> inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//inputStream->inputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//inputsource -> document
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//bean 解析前处理
preProcessXml(root);
//真正的解析bean
parseBeanDefinitions(root, this.delegate);
//bean解析后处理
postProcessXml(root);
this.delegate = parent;
}
dom对象解析成beanDefinition对象
经过上面的步骤已经把xml文件转成dom 元素,然后对dom元素从root节点开始解析,包括解析默认节点(如bean)和自定义节点(如context节点),默认的节点包括 impot alias bean等,这里重点看下bean节点的解析processBeanDefinition(ele, delegate)。首先把elemetn解析成beanDefinition并然后封装到一个包装器(重点,真正进行bean解析的地方,下面单独分析)
//org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//将element元素解析成对beanDefinition,然后封装一个包装器,bean解析核心方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//注册bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
// Send registration event.
//注册完成后发送事件,对应的监听器收到事件会处理
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
根据element里面的属性来解析bean,包括获取bean 的id 、name 校验id唯一性等准备工作,重点是通过parseBeanDefinitionElement把element解析成beanDefinition。parseBeanDefinitionElement首先是尝试获取element中bean 的classname 和parentName等属性,然后实例化一个GenericBeanDefinition并注入classname,parentName。最后是逐个解析bean的属性和bean的子节点属性并注入到之前实例化的GenericBeanDefinition中。下图中可以看出element对象包含了定义bean节点的各个属性和子节点的属性。
//BeanDefinitionParserDelegate.java
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//省略非核心代码
//获取beanId
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取beanName
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//name支持多个
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//如果id为空,则id取第一个name
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//校验id的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//beanName解析成beanDefinition 重点
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//这里是真正完成bean解析的过程
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
//获取classname和parent
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
创建GenerateBeanDefinition实例,注入classname
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析bean节点下的各个属性,如<init-method>
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析bean的子节点属性,如果配置了。比如metaElement constructor-args,property,qualifier等属性
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
...省略异常
finally {
this.parseState.pop();
}
return null;
}
在xml文件中配置bean的时候可以设置各种各样的属性,如singleton lazy-init init-method等,里面会一一解析。
//BeanDefinitionParserDelegate.java
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
beanDefinition对象注册到spring注册表
然后进行bean的注册,把beanName和beanDefinition放入注册表中,其实就是一个beanDefinitionMap,注册完成后发送事件,对应的监听器收到事件会处理。bean的注册包括beanDefinition的注册和别名的注册。bean注册的过程比较简单,就是把已经解析好的beanDefinition对象存放到注册表beanDefinitionmap中。
//BeanDefinitionReaderUtils.java
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//用primary name 注册bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
//注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
//DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//最关键的就是把beanDefinition存入beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
}
创建bean实例&初始化
refresh()方法中会调用finishBeanFactoryInitialization进行bean的实例化,前面是设置conversion service和注册字符串解析器用来解析配置,设置LoadTimeWeaverAware,停止使用临时类加载器。核心方法是preInstantiateSingletons(),这一步是初始化所有的非lzay-init bean。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
//初始化所有的非lazy-init bean
beanFactory.preInstantiateSingletons();
}
首先获取beanNames,遍历beanNames,对于每一个beanName,先判断是否是factoryBean 实现了factoryBean接口的bean属于factoryBean,获取factoryBean和通常的bean有些不同,需要在beanName前加上’&’前缀 ,例如getBean(&${beanName}),不过通常的bean都不属于factoryBean,我们直接看下面的getBean(beanName)方法。
//org/springframework/beans/factory/support/DefaultListableBeanFactory.java
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断是否factoryBean 实现了factoryBean接口的bean属于factoryBean,获取factoryBean需要在beanName加上'&'前缀
//通常的bean都直接走下面的getBean分支,不属于factoryBean
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
//获取bean,重点
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
首先转换beanName,factoryBean 需要去掉前面的&。然后根据beanName判断bean是否已经被创建,如果创建则直接从内存中返回bean实例。如果没有创建,看是否正在创建bean,是的话抛异常。不是的话则进入bean的创建流程。判断beanFactory是否包含这个beanName所属的beanDefinition,如果不在看是否在parentBeanFactory中,之后合并parent节点,组装完整的beanDefinition。bean在实例化前需要先实例化依赖的bean,这里会涉及到bean 的循环依赖问题。依赖的bean实例化后,完成自己bean的实例化,不同scope的bean实例化的处理方式稍微不同,这里只看单例 bean的实例化createBean(beanName, mbd, args);
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//判断bean是否已被创建
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//正在创建实例则抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 判断beanDefinition是否在beanFactory中,如果不在看是否在parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
//合并parent节点,组装beanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 需要先保障依赖的bean初始化
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
//创建bean实例,重点
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
创建bean实例
确保此时bean class 已经被解析了,拷贝一个beanDefinition以防动态解析的bean class 不能被存储在一个共享的merged bean definition中,然后在拷贝的beanDefinition 设置beanClass。beanPostProcessor可以干预bean的初始化,此处通过resolveBeforeInstantiation可以在bean实例化前返回一个bean实例的代理,如果没有则进行下面的bean实例创建doCreateBean(beanName, mbdToUse, args);
org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
//clone 一个beanDefinition
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 在bean初始化前的beanPostProcessor处理,它可以返回一个bean实例的代理
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
//创建bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
...省略异常处理
}
doCreateBean是真正创建bean实例的地方,首先从缓存中获取bean实例,获取不到则通过反射创建一个bean实例。mergedBeanDefinitionProcessor可以通过回调postProcessMergedBeanDefinition修改merged bean definition。之后进行bean的初始化,最后注册bean为可销毁的。这里重点看下bean的实例创建
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 先从缓存中获取
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//真正创建bean实例,通过反射创建
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
//mergedBeanDefinitionProcessor可以修改merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
//初始化bean实例,后面重点分析
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
首先确保bean class 已经被解析,这样才能实例化对应的对象。实例化bean有多种策略,根据配置选择合适的方法进行实例化
- 工厂方法初始化
- 构造函数自动注入初始化
默认无参构造方法初始化
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Candidate constructors for autowiring? Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // Preferred constructors for default construction? ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
这里选取最简单的默认无参构造方法来分析,主要看instantiate(mbd, beanName, parent)
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, parent), getAccessControlContext()); } else { beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
获取bean class 对象 判断是否是接口,如果是接口抛异常。然后通过反射获取构造方法,最后通过instantiateClass(constructorToUse)方法中的ctor.newInstance(args)完成构造函数的newInstance(),至此bean实例化过程完成。 ```java public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don’t override the class with CGLIB if no overrides. if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse);
} else {
// Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner);
} }
public static
<a name="NJ65E"></a>
### bean的初始化
主要是回调一系列 Aware方法、initMethod方法和 bean post processors对bean初始化进行前处理和后处理。
```java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
扩展点
BeanPostProcessor
beanPostProcessor是spring为修改bean提供的扩展点,postProcessBeforeInitialization在bean实例化后,初始化前调用,postProcessAfterInitialization在bean初始化后调用。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
在spring容器的启动入口refresh()方法中完成beanFactory的实例创建后会调用prepareBeanFactory,prepareBeanFactory主要是设置factoryBean的属性,beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))这行代码会创建ApplicationContextAwareProcesso实例并注册到beanFactory。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
ApplicationContextAwareProcessor实现了BeanPostProcessor重写postProcessBeforeInitialization,里面调用了invokeAwareInterfaces(bean)方法,这里通过回调把spring资源注入到bean。
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
BeanFactoryPostProcessor
BeanFactoryPostProcessor只有postProcessBeanFactory一个接口,postProcessBeanFactory可以在BeanFactory完成实例化后修改容器内部的BeanFactory。在spring容器的启动入口refresh()方法中完成beanFactory的实例创建后会调用,这时候所有的bean都被加载,但是没有bean被初始化。这就允许BeanFactoryPostProcessor重写或者添加配置,甚至可以提前初始化bean。
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
public void refresh(){
// 创建DefaultListableBeanFactory,完成bean配置的读取、解析,核心方法,下面分析
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
}
xxxAware接口
若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖。所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源。
Spring 中对beanFactory容器提供的 Aware 接口有:
- BeanNameAware:注入当前 bean 对应 beanName;
- BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader;
BeanFactoryAware:注入 当前BeanFactory容器 的引用。
//org/springframework/context/support/ApplicationContextAwareProcessor.java private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
Spring对ApplicationContext类型容器也提供了Aware接口,这些 Aware 接口的注入实现,是通过 BeanPostProcessor 的方式注入的,具体过程在beanPostProcessor中已经分析
EnvironmentAware:注入 Enviroment,一般用于获取配置属性;
- EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用于参数解析;
- ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。
//org/springframework/context/support/ApplicationContextAwareProcessor.java private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } }
参考文献
spring源码分析
spring容器的本质
https://spring.io/projects/spring-framework
factoryBean
beanPostProcessor 和beanFactoryPostProcessor
spring bean 生命周期