當前位置:首頁 » 硬碟大全 » aop實現緩存優化
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

aop實現緩存優化

發布時間: 2023-01-14 10:59:50

Ⅰ 「Spring 」「AOP 容器」不看源碼就帶你認識核心流程以及運作原理

前一篇文章主要介紹了 spring 核心特性機制的 IOC 容器機制和核心運作原理,接下來我們去介紹另外一個較為核心的功能,那就是 AOP 容器機制,主要負責承接前一篇代理模式機制中動態代理:JDKProxy 和 CglibProxy 的功能機制之後,我們開始研究一下如何實現一下相關的 AOP 容器代理機制的。

實現的基本實現原理就是後置處理器:BeanPostProcessor 機制,實現動態化植入機制。

bean 在初始化的時候會進行調用對應的 BeanPostProcessor 的對應的方法會進行織入。

主要取決於 wrapIfNecessary 方法:

如果是基礎設施類型,則直接回進行返回該 bean 對象,不會進行相關的初始化對應的 aspectj 的動態織入機制。

會進行尋找相關的 Bean 對應的何時的加強通知類。

則會對該 bean 對象,額外進行增強操作生成相關的代理對象,並返回該執行之後的對象,否則會直接返回該對象即可。

getAdvicesAndAdvisorsForBean 方法是我們篩選 Advice 增強類的核心方法,主要用於過濾和篩選對應該 bean 的何時的增強器數組信息。

主要用於調用 的**findCandidateAdvisors()**方法,其內部會進行先關的核心構建相關的 Aspectj 的類的相關實現操作

advisorsFactory.getAdvisors 獲取通知器

切點類處理操作到此為止,還不完整接下來才是構建動態代理對象的真正執行操作,

擴展相關的篩選出的通知器列表,extendAdvisors 方法,通知器列表首部添加一個 DefaultPointcutAposr 類型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR 的實現機制。

proxy-target-class 的屬性值,代表是否可以支持代理實現類,默認採用的 false 代表著,當 bean 有實現介面的時候,會直接採用 jdk 的動態代理機制生成代理對象,如果是 true,則代表著使用 cglib 進行生成代理對象。

復制代碼

前提是必須要配置相關的 expose-proxy 屬性配置值為 true,才會進行暴露對應的代理機制。

為了解決目標方法調用同對象中的其他方法,其他方法的切面邏輯是無法實現,因為會涉及到相關的 this 操作而不是 proxy 對象機制。

可以實現使用 AopContext.currentProxy()強制轉換為當前的代理對象。

獲取相關的對應方法的攔截器棧鏈路,如果沒有獲取到相關的緩存鏈路,則會直接調用相關的 獲取先關的攔截器鏈。

會進行先關的 PointcutAdvisor 類型通知器,這里會調用相關的通知器所持有的切點(Pointcut)對類和方法進行匹配,匹配沖過這說明相關的向當前的方法進行織入邏輯控制。此外還會通過 geIntercptors()方法對非 MethodIntercptor 類型的通知進行轉換。返回相關的攔截器數組,並且隨後存入緩存中。

則會直接通過代理機制的反射控制進行調用執行即可。

則例如 jdkDynamicAutoProxy 對象進行調用構建 ReflectiveMethodInvocation 對象,例如它的 process 方法啟動攔截器棧的 invoke 方法。

處理返回值,並且返回該值。

Ⅱ ehcache java 對象緩存怎麼實現

EhCache裡面有一個CacheManager類型,它負責管理cache。Cache裡面存儲著Element對象,Element必須是key-value對。Cache是實際物理實現的,在內存中或者磁碟。這些組件的邏輯表示就是下面即將要討論的類。他們的方法提供了可編程的訪問方式。

CacheManager
負責Cache的創建、訪問、移除。

CacheManager創建
CacheManager支持兩種創建模式:單例(Singleton mode)和實例(InstanceMode)。
在2.5之前的版本中,在同一個JVM中允許存在任意數量相同名字的CacheManager。每調用new CacheManager(...)一次,就會產生一個新的CacheManager實例,而不管已經存在多少個。調用CacheManager.create(...),則返回的是已經存在的那個配置對應的單例CacheManager,如果不存在,則創建一個。

2.5之後的版本,不允許在同一個JVM內存在多個具有相同名字的CacheManager。創建非單例實例的CacheManager()構造函數可能會打破這一規則,但是會拋出NPE異常。如果你的代碼要在同一個JVM創建多個同名的實例,請使用靜態方法CacheManager.create(),總是返回對應名的CacheManager(如果已經存在),否則創建一個

Ⅲ Spring AOP 一般用在什麼場景中

AOP,在程序開發中主要用來解決一些系統層面上的問題,比如日誌,事務,許可權等待,Struts2的攔截器設計就是基於AOP的思想,是個比較經典的例子。

在不改變原有的邏輯的基礎上,增加一些額外的功能。代理也是這個功能,讀寫分離也能用aop來做。

(3)aop實現緩存優化擴展閱讀:

AOP/OOP區分

AOP、OOP在字面上雖然非常類似,但卻是面向不同領域的兩種設計思想。OOP(面向對象編程)針對業務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清晰高效的邏輯單元劃分。

而AOP則是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。這兩種設計思想在目標上有著本質的差異。

Ⅳ AOP的應用范圍

很明顯,AOP非常適合開發J2EE容器伺服器,JBoss 4.0正是使用AOP框架進行開發。
具體功能如下:
Authentication 許可權
Caching緩存
Context passing內容傳遞
Error handling 錯誤處理
Lazy loading 延時載入
Debugging 調試
logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
Performance optimization性能優化
Persistence 持久化
Resource pooling資源池
Synchronization 同步
Transactions事務
【AOP有必要嗎?】
當然,上述應用範例在沒有使用AOP情況下,也得到了解決,例如JBoss 3.XXX也提供了上述應用功能,並且沒有使用AOP。
但是,使用AOP可以讓我們從一個更高的抽象概念來理解軟體系統,AOP也許提供一種有價值的工具。可以這么說:因為使用AOP結構,JBoss 4.0的源碼要比JBoss 3.X容易理解多了,這對於一個大型復雜系統來說是非常重要的。
從另外一個方面說,好像不是所有的人都需要關心AOP,它可能是一種架構設計的選擇,如果選擇J2EE系統,AOP關注的上述通用方面都已經被J2EE容器實現了,J2EE應用系統開發者可能需要更多地關注行業應用方面aspect。
傳統的程序通常表現出一些不能自然地適合單一的程序模塊或者是幾個緊密相關的程序模塊的行為,AOP 將這種行為稱為橫切,它們跨越了給定編程模型中的典型職責界限。橫切行為的實現都是分散的,軟體設計師會發現這種行為難以用正常的邏輯來思考、實現和更改。最常見的一些橫切行為如下面這些:
日誌記錄,跟蹤,優化和監控
事務的處理
持久化
性能的優化
資源池,如資料庫連接池的管理
系統統一的認證、許可權管理等
應用系統的異常捕捉及處理
針對具體行業應用的橫切行為
前面幾種橫切行為都已經得到了密切的關注,也出現了各種有價值的應用,但也許今後幾年,AOP 對針對具體行業應用的貢獻會成為令人關注的焦點。

Ⅳ spring aop靜態和動態的區別

原理
AOP(Aspect Oriented Programming),也就是面向方面編程的技術。AOP基於IoC基礎,是對OOP的有益補充。
AOP將應用系統分為兩部分,核心業務邏輯(Core business concerns)及橫向的通用邏輯,也就是所謂的方面Crosscutting enterprise concerns,例如,所有大中型應用都要涉及到的持久化管理(Persistent)、事務管理(Transaction Management)、安全管理(Security)、日誌管理(Logging)和調試管理(Debugging)等。
AOP正在成為軟體開發的下一個光環。使用AOP,你可以將處理aspect的代碼注入主程序,通常主程序的主要目的並不在於處理這些aspect。AOP可以防止代碼混亂。
Spring framework是很有前途的AOP技術。作為一種非侵略性的、輕型的AOP framework,你無需使用預編譯器或其他的元標簽,便可以在Java程序中使用它。這意味著開發團隊里只需一人要對付AOP framework,其他人還是像往常一樣編程。

AOP概念
讓我們從定義一些重要的AOP概念開始。
— 方面(Aspect):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用Spring的Advisor或攔截器實現。
— 連接點(Joinpoint):程序執行過程中明確的點,如方法的調用或特定的異常被拋出。
— 通知(Advice):在特定的連接點,AOP框架執行的動作。各種類型的通知包括「around」、「before」和「throws」通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護一個「圍繞」連接點的攔截器鏈。
— 切入點(Pointcut):指定一個通知將被引發的一系列連接點的集合。AOP框架必須允許開發者指定切入點,例如,使用正則表達式。
— 引入(Introction):添加方法或欄位到被通知的類。Spring允許引入新的介面到任何被通知的對象。例如,你可以使用一個引入使任何對象實現IsModified介面,來簡化緩存。
— 目標對象(Target Object):包含連接點的對象,也被稱作被通知或被代理對象。
— AOP代理(AOP Proxy):AOP框架創建的對象,包含通知。在Spring中,AOP代理可以是JDK動態代理或CGLIB代理。
— 編織(Weaving):組裝方面來創建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

Ⅵ Java開源框架是什麼

Java開源項目
Spring Framework 【Java開源 J2EE框架】
Spring 是一個解決了許多在J2EE開發中常見的問題的強大框架。 Spring提供了管理業務對象的一致方法並且鼓勵了注入對介面編程而不是對類編程的良好習慣。Spring的架構基礎是基於使用JavaBean屬性的 Inversion of Control容器。然而,這僅僅是完整圖景中的一部分:Spring在使用IoC容器作為構建完關注所有架構層的完整解決方案方面是獨一無二的。 Spring提供了唯一的數據訪問抽象,包括簡單和有效率的JDBC框架,極大的改進了效率並且減少了可能的錯誤。Spring的數據訪問架構還集成了 Hibernate和其他O/R mapping解決方案。Spring還提供了唯一的事務管理抽象,它能夠在各種底層事務管理技術,例如JTA或者JDBC事務提供一個一致的編程模型。 Spring提供了一個用標准Java語言編寫的AOP框架,它給POJOs提供了聲明式的事務管理和其他企業事務--如果你需要--還能實現你自己的 aspects。這個框架足夠強大,使得應用程序能夠拋開EJB的復雜性,同時享受著和傳統EJB相關的關鍵服務。Spring還提供了可以和IoC容器集成的強大而靈活的MVC Web框架。【SpringIDE:Eclipse平台下一個輔助開發插件】.
WebWork 【Java開源 Web框架】
WebWork 是由OpenSymphony組織開發的,致力於組件化和代碼重用的拉出式MVC模式J2EE Web框架。WebWork目前最新版本是2.1,現在的WebWork2.x前身是Rickard Oberg開發的WebWork,但現在WebWork已經被拆分成了Xwork1和WebWork2兩個項目。 Xwork簡潔、靈活功能強大,它是一個標準的Command模式實現,並且完全從web層脫離出來。 Xwork提供了很多核心功能:前端攔截機(interceptor),運行時表單屬性驗證,類型轉換,強大的表達式語言(OGNL – the Object Graph Notation Language),IoC(Inversion of Control倒置控制)容器等。 WebWork2建立在Xwork之上,處理HTTP的響應和請求。WebWork2使用ServletDispatcher將HTTP請求的變成 Action(業務層Action類), session(會話)application(應用程序)范圍的映射,request請求參數映射。WebWork2支持多視圖表示,視圖部分可以使用 JSP, Velocity, FreeMarker, JasperReports,XML等。在WebWork2.2中添加了對AJAX的支持,這支持是構建在DWR與Dojo這兩個框架的基礎之上.【EclipseWork:用於WebWork輔助開發的一個Eclipse插件】
Struts 【Java開源 Web框架】
Struts 是一個基於Sun J2EE平台的MVC框架,主要是採用Servlet和JSP技術來實現的。由於Struts能充分滿足應用開發的需求,簡單易用,敏捷迅速,在過去的一年中頗受關注。Struts把Servlet、JSP、自定義標簽和信息資源(message resources)整合到一個統一的框架中,開發人員利用其進行開發時不用再自己編碼實現全套MVC模式,極大的節省了時間,所以說Struts是一個非常不錯的應用框架。【StrutsIDE:用於Struts輔助開發的一個Eclipse插件】
Hibernate 【Java開源 持久層框架】
Hibernate 是一個開放源代碼的對象關系映射框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用對象編程思維來操縱資料庫。 Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序實用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。Eclipse平台下的Hibernate輔助開發工具:【Hibernate Synchronizer】【MiddlegenIDE】
Quartz 【Java開源 Job調度】
Quartz 是OpenSymphony開源組織在Job scheling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或為運行十個,百個,甚至是好幾萬個Jobs這樣復雜的日程序表。Jobs可以做成標準的Java組件或 EJBs。Quartz的最新版本為Quartz 1.5.0。
Velocity 【Java開源 模板引擎】
Velocity 是一個基於java的模板引擎(template engine)。它允許任何人僅僅簡單的使用模板語言(template language)來引用由java代碼定義的對象。當Velocity應用於web開發時,界面設計人員可以和java程序開發人員同步開發一個遵循MVC架構的web站點,也就是說,頁面設計人員可以只關注頁面的顯示效果,而由java程序開發人員關注業務邏輯編碼。Velocity將java代碼從web頁面中分離出來,這樣為web站點的長期維護提供了便利,同時也為我們在JSP和PHP之外又提供了一種可選的方案。 Velocity的能力遠不止web站點開發這個領域,例如,它可以從模板(template)產生SQL和PostScript、XML,它也可以被當作一個獨立工具來產生源代碼和報告,或者作為其他系統的集成組件使用。Velocity也可以為Turbine web開發架構提供模板服務(template service)。Velocity+Turbine提供一個模板服務的方式允許一個web應用以一個真正的MVC模型進行開發。 【VeloEclipse :Velocity在Eclipse平台下的一個輔助開發插件】
IBATIS 【Java開源 持久層框架】
使用ibatis 提供的ORM機制,對業務邏輯實現人員而言,面對的是純粹的Java對象, 這一層與通過Hibernate 實現ORM 而言基本一致,而對於具體的數據操作,Hibernate 會自動生成SQL 語句,而ibatis 則要求開發者編寫具體的SQL 語句。相對Hibernate等 「全自動」ORM機制而言,ibatis 以SQL開發的工作量和資料庫移植性上的讓步,為系統設計提供了更大的自由空間。作為「全自動」ORM 實現的一種有益補充,ibatis 的出現顯 得別具意義。
Compiere ERP&CRM 【Java開源ERP與CRM系統】
Compiere ERP&CRM為全球范圍內的中小型企業提供綜合型解決方案,覆蓋從客戶管理、供應鏈到財務管理的全部領域,支持多組織、多幣種、多會計模式、多成本計算、多語種、多稅制等國際化特性。易於安裝、易於實施、易於使用。只需要短短幾個小時,您就可以使用申購-采購-發票-付款、報價-訂單-發票-收款、產品與定價、資產管理、客戶關系、供應商關系、員工關系、經營業績分析等強大功能了。
Roller Weblogger 【Java開源 Blog博客】
這個weblogging 設計得比較精巧,源代碼是很好的學習資料。它支持weblogging應有的特性如:評論功能,所見即所得HTML編輯,TrackBack,提供頁面模板,RSS syndication,blogroll管理和提供一個XML-RPC 介面。
Eclipse 【Java開源 開發工具】
Eclipse平台是IBM向開放源碼社區捐贈的開發框架,它之所以出名並不是因為IBM宣稱投入開發的資金總數 —4千萬美元,而是因為如此巨大的投入所帶來的成果:一個成熟的、精心設計的以及可擴展的體系結構。
NetBeans 【Java開源 開發工具】
NetBeans IDE 是一個為軟體開發者提供的自由、開源的集成開發環境。您可以從中獲得您所需要的所有工具,用 Java、C/C++ 甚至是 Ruby 來創建專業的桌面應用程序、企業應用程序、web 和移動應用程序。此 IDE 可以在多種平台上運行,包括 Windows、Linux、Mac OS X 以及 Solaris;它易於安裝且非常方便使用。
XPlanner 【Java開源 項目管理】
XPlanner 一個基於Web的XP團隊計劃和跟蹤工具。XP獨特的開發概念如iteration、user stories等,XPlanner都提供了相對應的的管理工具,XPlanner支持XP開發流程,並解決利用XP思想來開發項目所碰到的問題。 XPlanner特點包括:簡單的模型規劃,虛擬筆記卡(Virtual note cards),iterations、user stories與工作記錄的追蹤,未完成stories將自動迭代,工作時間追蹤,生成團隊效率,個人工時報表,SOAP界面支持。
HSQLDB 【Java開源 DBMS資料庫】
HSQLDB(Hypersonic SQL)是純Java開發的關系型資料庫,並提供JDBC驅動存取數據。支持ANSI-92 標准 SQL語法。而且他占的空間很小。大約只有160K,擁有快速的資料庫引擎。
Liferay 【Java開源 Portal門戶】
代表了完整的J2EE應用,使用了Web、EJB以及JMS等技術,特別是其前台界面部分使用Struts 框架技術,基於XML的portlet配置文件可以自由地動態擴展,使用了Web Services來支持一些遠程信息的獲取,使用 Apahce Lucene實現全文檢索功能。
JetSpeed 【Java開源 Portal門戶】
Jetspeed 是一個開放源代碼的企業信息門戶(EIP)的實現,使用的技術是Java和XML。用戶可以使用瀏覽器,支持WAP協議的手機或者其它的設備訪問Jetspeed架設的信息門戶獲取信息。Jetspeed扮演著信息集中器的角色,它能夠把信息集中起來並且很容易地提供給用戶。
JOnAS 【Java開源 J2EE伺服器】
JOnAS 是一個開放源代碼的J2EE實現,在ObjectWeb協會中開發。整合了Tomcat或Jetty成為它的Web容器,以確保符合Servlet 2.3和JSP 1.2規范。JOnAS伺服器依賴或實現以下的Java API:JCA、JDBC、JTA 、JMS、JMX、JNDI、JAAS、JavaMail 。
JFox3.0 【Java開源 J2EE伺服器】
JFox 是 Open Source Java EE Application Server,致力於提供輕量級的Java EE應用伺服器,從3.0開始,JFox提供了一個支持模塊化的MVC框架,以簡化EJB以及Web應用的開發! 如果您正在尋找一個簡單、輕量、高效、完善的Java EE開發平台,那麼JFox正是您需要的。

Ⅶ windows環境下Redis+Spring緩存實例

一、Redis了解

1.1、Redis介紹:

redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字元串)、list(鏈表)、set(集合)、zset(sorted set –有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會周期性的把更新的數據寫入磁碟或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。

Redis資料庫完全在內存中,使用磁碟僅用於持久性。相比許多鍵值數據存儲,Redis擁有一套較為豐富的數據類型。Redis可以將數據復制到任意數量的從伺服器。

1.2、Redis優點:

(1)異常快速:Redis的速度非常快,每秒能執行約11萬集合,每秒約81000+條記錄。

(2)支持豐富的數據類型:Redis支持最大多數開發人員已經知道像列表,集合,有序集合,散列數據類型。這使得它非常容易解決各種各樣的問題,因為我們知道哪些問題是可以處理通過它的數據類型更好。

(3)操作都是原子性:所有Redis操作是原子的,這保證了如果兩個客戶端同時訪問的Redis伺服器將獲得更新後的值。

(4)多功能實用工具:Redis是一個多實用的工具,可以在多個用例如緩存,消息,隊列使用(Redis原生支持發布/訂閱),任何短暫的數據,應用程序,如Web應用程序會話,網頁命中計數等。

1.3、Redis缺點:

(1)單線程

(2)耗內存

二、64位windows下Redis安裝

Redis官方是不支持windows的,但是Microsoft Open Tech group 在 GitHub上開發了一個Win64的版本,下載地址:https://github.com/MSOpenTech/redis/releases。注意只支持64位哈。

小寶鴿是下載了Redis-x64-3.0.500.msi進行安裝。安裝過程中全部採取默認即可。

安裝完成之後可能已經幫你開啟了Redis對應的服務,博主的就是如此。查看資源管理如下,說明已經開啟:

已經開啟了對應服務的,我們讓它保持,下面例子需要用到。如果沒有開啟的.,我們命令開啟,進入Redis的安裝目錄(博主的是C:Program FilesRedis),然後如下命令開啟:

redis-server redis.windows.conf

OK,下面我們進行實例。

三、詳細實例

本工程採用的環境:Eclipse + maven + spring + junit

3.1、添加相關依賴(spring+junit+redis依賴),pom.xml:

4.0.0 com.luo redis_project 0.0.1-SNAPSHOT 3.2.8.RELEASE 4.10 org.springframework spring-core ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-context ${spring.version} org.springframework spring-context-support ${spring.version} org.springframework spring-aop ${spring.version} org.springframework spring-aspects ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-web ${spring.version} junit junit ${junit.version} test org.springframework spring-test ${spring.version} test org.springframework.data spring-data-redis 1.6.1.RELEASE redis.clients jedis 2.7.3

3.2、spring配置文件application.xml:

<"1.0" encoding="UTF-8"> classpath:properties/*.properties

3.3、Redis配置參數,redis.properties:

#redis中心#綁定的主機地址redis.host=127.0.0.1#指定Redis監聽埠,默認埠為6379redis.port=6379#授權密碼(本例子沒有使用)redis.password=123456 #最大空閑數:空閑鏈接數大於maxIdle時,將進行回收redis.maxIdle=100 #最大連接數:能夠同時建立的「最大鏈接個數」redis.maxActive=300 #最大等待時間:單位msredis.maxWait=1000 #使用連接時,檢測連接是否成功 redis.testOnBorrow=true#當客戶端閑置多長時間後關閉連接,如果指定為0,表示關閉該功能redis.timeout=10000

3.4、添加介面及對應實現RedisTestService.Java和RedisTestServiceImpl.java:

package com.luo.service; public interface RedisTestService { public String getTimestamp(String param);}

package com.luo.service.impl; import org.springframework.stereotype.Service;import com.luo.service.RedisTestService; @Servicepublic class RedisTestServiceImpl implements RedisTestService { public String getTimestamp(String param) { Long timestamp = System.currentTimeMillis(); return timestamp.toString(); } }

3.5、本例採用spring aop切面方式進行緩存,配置已在上面spring配置文件中,對應實現為MethodCacheInterceptor.java:

package com.luo.redis.cache; import java.io.Serializable;import java.util.concurrent.TimeUnit;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations; public class MethodCacheInterceptor implements MethodInterceptor { private RedisTemplate redisTemplate; private Long defaultCacheExpireTime = 10l; // 緩存默認的過期時間,這里設置了10秒 public Object invoke(MethodInvocation invocation) throws Throwable { Object value = null; String targetName = invocation.getThis().getClass().getName(); String methodName = invocation.getMethod().getName(); Object[] arguments = invocation.getArguments(); String key = getCacheKey(targetName, methodName, arguments); try { // 判斷是否有緩存 if (exists(key)) { return getCache(key); } // 寫入緩存 value = invocation.proceed(); if (value != null) { final String tkey = key; final Object tvalue = value; new Thread(new Runnable() { public void run() { setCache(tkey, tvalue, defaultCacheExpireTime); } }).start(); } } catch (Exception e) { e.printStackTrace(); if (value == null) { return invocation.proceed(); } } return value; } /** * 創建緩存key * * @param targetName * @param methodName * @param arguments */ private String getCacheKey(String targetName, String methodName, Object[] arguments) { StringBuffer sbu = new StringBuffer(); sbu.append(targetName).append("_").append(methodName); if ((arguments != null) && (arguments.length != 0)) { for (int i = 0; i < arguments.length; i++) { sbu.append("_").append(arguments[i]); } } return sbu.toString(); } /** * 判斷緩存中是否有對應的value * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 讀取緩存 * * @param key * @return */ public Object getCache(final String key) { Object result = null; ValueOperations operations = redisTemplate .opsForValue(); result = operations.get(key); return result; } /** * 寫入緩存 * * @param key * @param value * @return */ public boolean setCache(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations operations = redisTemplate .opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } public void setRedisTemplate( RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; }}

3.6、單元測試相關類:

package com.luo.baseTest; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //指定bean注入的配置文件 @ContextConfiguration(locations = { "classpath:application.xml" }) //使用標準的JUnit @RunWith注釋來告訴JUnit使用Spring TestRunner @RunWith(SpringJUnit4ClassRunner.class) public class SpringTestCase extends { }

package com.luo.service; import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired; import com.luo.baseTest.SpringTestCase; public class RedisTestServiceTest extends SpringTestCase { @Autowired private RedisTestService redisTestService; @Test public void getTimestampTest() throws InterruptedException{ System.out.println("第一次調用:" + redisTestService.getTimestamp("param")); Thread.sleep(2000); System.out.println("2秒之後調用:" + redisTestService.getTimestamp("param")); Thread.sleep(11000); System.out.println("再過11秒之後調用:" + redisTestService.getTimestamp("param")); } }

3.7、運行結果:

四、源碼下載:redis-project().rar

以上就是本文的全部內容,希望對大家的學習有所幫助。

Ⅷ spring為什麼要使用三級緩存解決循環依賴

首先清楚spring中bean 的載入過程:

1 解析需要spring管理的類為beanDefinition

2 通過反射實例化對象

3 反射設置屬性

4初始化,調用initMethod等。(postConstruct也是在這執行)

循環依賴的問題: a依賴b,b依賴a。

在a實例化之後會先將a放入到緩存中,然後給a設置屬性,去緩存中查到b。此時找不到就開始b的創建。b實例化之後,放入到緩存中,需要給a設置屬性,此時去緩存中查到a設置成功。然後初始化。成功後將b放入一級緩存。這個時候a在給自己屬性b設置值的時候就找到了b,然後設置b。完成屬性設置,再初始化,初始化後a放入一級緩存。

解決代理對象(如aop)循環依賴的問題。

例: a依賴b,b依賴a,同時a,b都被aop增強。

首先明確aop的實現是通過 postBeanProcess後置處理器,在初始化之後做代理操作的。

為什麼使用三級緩存原因:

1 只使用二級緩存,且二級緩存緩存的是一個不完整的bean

如果只使用二級緩存,且二級緩存緩存的是一個不完整的bean,這個時候a在設置屬性的過程中去獲取b(這個時候a還沒有被aop的後置處理器增強),創建b的過程中,b依賴a,b去緩存中拿a拿到的是沒有經過代理的a。就有問題。

2 使用二級緩存,且二級緩存是一個工廠方法的緩存

如果二級緩存是一個工廠的緩存,在從緩存中獲取的時候獲取到經過aop增強的對象。可以看到從工廠緩存中獲取的邏輯。

protected ObjectgetEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

Object exposedObject = bean;

  if (!mbd.isSynthetic() && ()) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

if (bpinstanceof ) {

ibp = () bp;

            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

        }

}

}

return exposedObject;

}

a依賴b,b依賴a,c。c又依賴a。a,b,c均aop增強。

載入開始: a實例化,放入工廠緩存,設置b,b實例化,設置屬性,拿到a,此時從工廠緩存中拿到代理後的a。由於a沒載入完畢,不會放入一級緩存。這個時候b開始設置c,c實例化,設置屬性a,又去工廠緩存中拿對象a。這個時候拿到的a和b從工廠緩存不是一個對象。出現問題。

3 使用二級緩存,二級緩存緩存的是增強後的bean。這個與spring載入流程不符合。spring載入流程是:實例化,設置屬性,初始化,增強。在有循環引用的時候,之前的bean並不會增強後放入到二級緩存。

綜上1,2,3 可知二級緩存解決不了有aop的循環依賴。spring採用了三級緩存。

一級緩存  singletonObjects    緩存載入完成的bean。

二級緩存  earlySingletonObjects 緩存從三級緩存中獲取到的bean,此時裡面的bean沒有載入完畢。

三級緩存 singletonFactories 。緩存一個objectFactory工廠。

場景:a依賴b,b依賴a和c,c依賴a。並且a,b,c都aop增強。

載入過程:

a實例化,放入三級工廠緩存,設置屬性b,b實例化放入三級緩存。b設置屬性a,從三級工廠緩存中獲取代理後的對象a,同時,代理後的a放入二級緩存,然後設置屬性c,c實例化放入三級緩存,設置屬性a,此時從二級緩存中獲取到的代理後的a跟b中的a是一個對象,屬性a設置成功。c初始化,然後執行後置處理器。進行aop的增強。增強後將代理的c放入到一級緩存,同時刪除三級緩存中的c。c載入完成,b得到c,b設置c成功。b初始化,然後執行後置處理器,進行aop增強,將增強後的代理對象b放入到一級緩存。刪除三級緩存中的b。此時 a拿到b,設置屬性b成功,開始初始化,初始化後執行後置處理器。在aop的後置處理器中有一個以beanName為key,經過aop增強的代理對象為value的map earlyProxyReferences。

這個時候 後置處理器處理對象a的時候,

public (@Nullable Object bean, String beanName) {

if (bean !=null) {

Object cacheKey = getCacheKey(bean.getClass(), beanName);

      if (this.earlyProxyReferences.remove(cacheKey) != bean) {

return wrapIfNecessary(bean, beanName, cacheKey);

      }

}

return bean;

}

也就是 發現這個beanName已經被代理後就不在代理。這個時候執行後置處理器後,a還是未經代理的對象a。此時a再通過getSingleton 重新從緩存中獲取一下a。

Object earlySingletonReference = getSingleton(beanName, false);

false 表示不從三級緩存中取,只從一級,二級緩存中獲取。

這個時候能拿到二級緩存中的a。二級緩存中的a也是經過代理後的a。

然後將代理後的a放入到一級緩存中。a載入完畢。

放入一級緩存的過程 :

addSingleton(beanName, singletonObject);

從三級工廠緩存中獲取對象:

protected ObjectgetEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

Object exposedObject = bean;

  if (!mbd.isSynthetic() && ()) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

if (bpinstanceof ) {

ibp = () bp;

            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

        }

}

}

return exposedObject;

}

其中 AbstractAutoProxyCreator實現該介面。

public ObjectgetEarlyBeanReference(Object bean, String beanName) {

Object cacheKey = getCacheKey(bean.getClass(), beanName);

  this.earlyProxyReferences.put(cacheKey, bean);

  return wrapIfNecessary(bean, beanName, cacheKey);

}

wrapIfNecessary()就是真正執行代理的。

bean初始化之後執行的後置處理器:

其中AbstractAutoProxyCreator 實現了該介面。

Ⅸ spring—AOP與事務

title: spring——AOP與事務.md
date: 2020-07-14 13:10:16
categories: [Spring]
tags: [AOP,事務]
toc: true

先列出源碼中比較重點的幾個類:

1、<aop:before method="before" pointcut-ref="myMethods"/>包裝成一個advisor
2、,當實例化所有bean都會執行到類
它會檢測bean是否advisor以及advice存在,如果有就說明這個bean有切面,有切面那麼就會生成代理
3、jdk的代理,bean裡面的所有advisor加入到proxyFactory。
4、jdkDynamicProxy invoke,拿到bean裡面的所有Interceptor,會循環proxyFactory裡面的所有advisor
裡面有advice,裡面的advice有兩種類型,要麼是advice,要麼是MethodInterceptor類型的
5、當代理對象調用方式,是一個MethodInterceptor類型的類的鏈式調用過程,直到容器的大小和索引一致的時候調用JoinPoint目標方法

before:this.advice.before(),invocation.processd();
裝配參數,切面裡面before方法的method對象,method.getParamterTypes()[0]

最終會把advice封裝成MethodInterceptor類型的對象

程序執行的某個特定位置:如類開始初始化前、類初始化後、類某個方法調用前、調用後、方法拋出異常後。一個類或一段程序代碼擁有一些具有邊界性質的特定點,這些點中的特定點就稱為「連接點」。Spring僅支持方法的連接點,即僅能在方法調用前、方法調用後、方法拋出異常時以及方法調用前後這些程序執行點織入增強。連接點由兩個信息確定:第一是用方法表示的程序執行點;第二是用相對點表示的方位。

每個程序類都擁有多個連接點,如一個擁有兩個方法的類,這兩個方法都是連接點,即連接點是程序類中客觀存在的事物。AOP通過「切點」定位特定的連接點。連接點相當於資料庫中的記錄,而切點相當於查詢條件。切點和連接點不是一對一的關系,一個切點可以匹配多個連接點。在Spring中,切點通過org.springframework.aop.Pointcut介面進行描述,它使用類和方法作為連接點的查詢條件,Spring AOP的規則解析引擎負責切點所設定的查詢條件,找到對應的連接點。其實確切地說,不能稱之為查詢連接點,因為連接點是方法執行前、執行後等包括方位信息的具體程序執行點,而切點只定位到某個方法上,所以如果希望定位到具體連接點上,還需要提供方位信息。

增強是織入到目標類連接點上的一段程序代碼,在Spring中,增強除用於描述一段程序代碼外,還擁有另一個和連接點相關的信息,這便是執行點的方位。結合執行點方位信息和切點信息,我們就可以找到特定的連接點。

增強邏輯的織入目標類。如果沒有AOP,目標業務類需要自己實現所有邏輯,而在AOP的幫助下,目標業務類只實現那些非橫切邏輯的程序邏輯,而性能監視和事務管理等這些橫切邏輯則可以使用AOP動態織入到特定的連接點上。

引介是一種特殊的增強,它為類添加一些屬性和方法。這樣,即使一個業務類原本沒有實現某個介面,通過AOP的引介功能,我們可以動態地為該業務類添加介面的實現邏輯,讓業務類成為這個介面的實現類。

織入是將增強添加對目標類具體連接點上的過程。AOP像一台織布機,將目標類、增強或引介通過AOP這台織布機天衣無縫地編織到一起。根據不同的實現技術,AOP有三種織入的方式:
a、編譯期織入,這要求使用特殊的Java編譯器。
b、類裝載期織入,這要求使用特殊的類裝載器。
c、動態代理織入,在運行期為目標類添加增強生成子類的方式。
Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。

一個類被AOP織入增強後,就產出了一個結果類,它是融合了原類和增強邏輯的代理類。根據不同的代理方式,代理類既可能是和原類具有相同介面的類,也可能就是原類的子類,所以我們可以採用調用原類相同的方式調用代理類。

切面由切點和增強(引介)組成,它既包括了橫切邏輯的定義,也包括了連接點的定義,Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連接點中。
advisor: pointCut advice

一類功能的增強

around方法裡面代碼切面

事務切面
緩存切面
日誌切面

事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指訪問並可能更新資料庫中各種數據項的一個程序執行單元(unit)。是資料庫操作的最小工作單元,是作為單個邏輯工作單元執行的一系列操作;這些操作作為一個整體一起向系統提交,要麼都執行、要麼都不執行;事務是一組不可再分割的操作集合(工作邏輯單元)。

大致流程形如

資料庫事務擁有幾大特性:

事務的四大特性:

事務是資料庫的邏輯工作單位,事務中包含的各操作要麼都做,要麼都不做

事 務執行的結果必須是使資料庫從一個一致性狀態變到另一個一致性狀態。因此當資料庫只包含成功事務提交的結果時,就說資料庫處於一致性狀態。如果資料庫系統 運行中發生故障,有些事務尚未完成就被迫中斷,這些未完成事務對資料庫所做的修改有一部分已寫入物理資料庫,這時資料庫就處於一種不正確的狀態,或者說是 不一致的狀態。

一個事務的執行不能其它事務干擾。即一個事務內部的操作及使用的數據對其它並發事務是隔離的,並發執行的各個事務之間不能互相干擾。

也稱永久性,指一個事務一旦提交,它對資料庫中的數據的改變就應該是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。

個人理解,事務在Spring中是藉助AOP技術來實現的,可以作為AOP中的一個事務切面。spring源碼對事務的處理邏輯,自己研究吧!

ORM框架中以Mybatis為例,事務處理就是用到了一個類Transaction,部分源碼如下

可以看出Transaction管理的就是一個connection,而connection我們很清楚是與用戶會話掛鉤的。

那麼關系就是Transaction 管理Connection ,而connection與 用戶session一對一存在。

在springBoot中,只需要加入POM就可以了,配合註解使用即可。

接下來就是事務的控制了。

首先事務有幾大傳播屬性:

其中最常見的,用得最多就 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、 PROPAGATION_NESTED 這三種。事務的傳播屬性是 spring 特有的,是 spring 用來控制方法事務的一種手段,說直白點就是用來控制方法是否使用同一事務的一種屬性,以及按照什麼規則回滾的一種手段。

下面用代碼演示這三種屬性的機制:

事務的默認屬性就是required,通過Transactional.java中的Propagation propagation() default Propagation.REQUIRED; 可以看出。

這種情況就是事務1,事務2 都加入到了事務0中。不管是1,2哪個事務拋出異常,事務0都會回滾。數據添加會失敗。

這種情況就是:

事務0(required) {

​ 事務1 (REQUIRES_NEW)

​ 事務2

}

此時。

情況a:

1、如果只是事務2出現了異常,那麼事務1會提交,事務2加入到事務0中會回滾。

2、如果只是事務1出現了異常,那麼事務1會回滾,向上層事務0拋異常,事務2會加入到事務0中,這時都會回滾。

情況b:

如果事務1,事務2都是REQUIRES_NEW傳播屬性。那麼結果就是:

1、如果事務1,拋出了異常,那麼事務2是不會執行的,那麼事務0必然回滾。

2、如果事務2,拋出異常,那麼事務1會提交,表中會有數據。事務2有異常回滾並拋出,事務0回滾。

NESTED屬性其實就是創建了回滾點,有異常時,會回滾到指定的回滾點。

在這通過代碼測試,出現一種情況是,無論事務1,事務2哪個有異常,數據都不會插入成功,原因是,不論是事務1還是事務2都會向事務0拋出異常,事務0捕獲到異常後,執行rollback()方法,這就操作成了,事務的全部回滾。

如果想要事務1和事務2 想要根據自己的回滾點回滾,那麼事務0必須自己處理異常,不讓spring捕獲到這個異常,那麼就滿足了。把代碼改成這種:

Jack大佬提供了,偽代碼分析法。

按照Spring源碼的事務處理邏輯,偽代碼大致為: