一:《spring簡介》
關於Spring
Spring是一個開源框架,是為了解決企業應用程式開發複雜性而建立的。框架的主要優勢之一就是其分層架構,分層架構允許您選擇使用哪一個元件,同時為 J2EE 應用程式開發提供整合的框架。
它是一個全面的、企業應用開發一站式的解決方案,貫穿表現層、業務層、持久層。但是Spring仍然可以和其他的框架無縫整合。
Sping架構
Spring框架是分模組存在,除了最核心的Spring Core Container(即Spring容器)是必要模組之外,其他模組都是可選,視需要而定。大約有20多個模組。
Spring3與Spring4是有區別的,4.0主要是對Java 8的新函式式語法進行支援,還有加強了對網路各種新技術比如http-streaming, websocket的更好的支援。
一般來說,Spring主要分為7個模組:
Spring的主要jar包
常用註解
bean注入與裝配的的方式有很多種,可以通過xml,getset方式,建構函式或者註解等。簡單易用的方式就是使用Spring的註解了,Spring提供了大量的註解方式,讓專案閱讀和開發起來更加方便。
第三方框架整合
Spring框架的開發不是為了替代現有的優秀第三方框架,而是通過整合的方式把它們都連線起來。下面總結了一些常整合的優秀框架。
二:《spring之IOC》
寫過java的都知道:所有的物件都必須建立;或者說:使用物件之前必須先建立。而使用ioc之後,你就可以不再手動建立物件,而是從ioc容器中直接獲取物件。
就好像我們無需考慮物件的銷燬回收一樣,因為java垃圾回收機制幫助我們實現了這個過程;而ioc則是讓我們無需考慮物件的建立過程,由ioc容器幫我們實現物件的建立、注入等過程。
控制反轉
spring ioc容器
在Spring框架中的核心元件只有三個:Core、Context和Bean。它們構建起了整個Spring的骨骼架構,沒有它們就不可能有AOP、Web等特性功能。
如果說在三個核心中再選出一個核心,那就非Bean莫屬了。可以說,Spring就是面向Bean的程式設計,Bean在Spring中才是真正的主角。
Spring為何如此流行?你會發現Spring解決了一個非常關鍵的問題,它可以讓你對物件之間的關係轉而用配置檔案來管理,或者註解,也就是它的依賴注入機制。而這個注入關係在一個叫Ioc的容器中管理。Ioc容器就是被Bean包裹的物件。Spring正是通過把物件包裝在Bean中從而達到管理這些物件及做一些列額外操作的目的。
核心元件協同工作
BeanFactory與ApplacationContext的區別
IOC中最核心的介面是Beanfactory提供IOC的高階服務,而ApplicationContext是建立在BeanFactory基礎之上提供抽象的面向應用的服務。
3種注入方式
在Spring框架中,依賴注入(DI)的設計模式是用來定義物件彼此間的依賴。使用xml配置bean的情況下,它主要有兩種型別:
Setter方法注入
構造器注入
當然,有了註解之後,使用註解的方式更加方便快捷。即自動裝配功能實現屬性自動注入(@autowire)。
寫到這裡,讓我想起了最近在牛客網上看的一道選擇題了:
下面有關spring的依賴注入,說法錯誤的是?
A、依賴注入通常有如下兩種:設定注入和構造注入:
B、構造注入可以在構造器中決定依賴關係的注入順序,優先依賴的優先注入
C、當設值注入與構造注入同時存在時,先執行構造注入,再執行設值注入
D、設值注入是指IoC容器使用屬性的setter方法來注入被依賴的例項。這種注入方式比較簡單、直觀
牛客網給出的答案是選C,不過網友們好像對答案有不同的意見哈。檢視網友評論及答案
原理解析
Spring的程式碼還真是不好讀,分得太細了,文字也是難以描述出來,看了別人有關的部落格,貼了好多程式碼,畫了好多ER圖來描述關鍵介面或類之間的關係。這麼一篇這麼長文章下來,大家也未必會認真讀程式碼,看ER圖,乾脆也不跟風了。就貼了一點在我看來特關鍵的程式碼,嘿嘿。
context的初始化過程
當執行ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 構造方法ClassPathXmlApplicationContext(String configLocation)呼叫了this(new String[] {configLocation}, true, null);, 該構造方法具體程式碼如下。
從時序圖來看啟動上述初始化
三:《spring之MVC》
spring mvc簡介與執行原理
Spring的模型-檢視-控制器(MVC)框架是圍繞一個DispatcherServlet來設計的,這個Servlet會把請求分發給各個處理器,並支援可配置的處理器對映、檢視渲染、本地化、時區與主題渲染等,甚至還能支援檔案上傳。
(1) Http請求:客戶端請求提交到DispatcherServlet。
(2) 尋找處理器:由DispatcherServlet控制器查詢一個或多個HandlerMapping,找到處理請求的Controller。
(3) 呼叫處理器:DispatcherServlet將請求提交到Controller。
(4)(5)呼叫業務處理和返回結果:Controller呼叫業務邏輯處理後,返回ModelAndView。
(6)(7)處理檢視對映並返回模型: DispatcherServlet查詢一個或多個ViewResoler檢視解析器,找到ModelAndView指定的檢視。
(8) Http響應:檢視負責將結果顯示到客戶端。
主要註解
ContextLoaderListener
在講ContextLoaderListener之前,首先來了解一下web.xml的作用。
一個web中可以沒有web.xml檔案,也就是說,web.xml檔案並不是web工程必須的。web.xml檔案是用來初始化配置資訊:比如Welcome頁面、servlet、servlet-mapping、filter、listener、啟動載入級別等。當你的web工程沒用到這些時,你可以不用web.xml檔案來配置你的Application。
當要啟動某個web專案時,伺服器軟體或容器如(tomcat)會第一步載入專案中的web.xml檔案,通過其中的各種配置來啟動專案,只有其中配置的各項均無誤時,專案才能正確啟動。web.xml有多項標籤,在其載入的過程中順序依次為:context-param >> listener >> fileter >> servlet。(同類多個節點以出現順序依次載入)
而spring mvc啟動過程大致分為兩個過程:
ContextLoaderListener初始化,例項化IoC容器,並將此容器例項註冊到ServletContext中。
DispatcherServlet初始化。
其中ContextLoaderListener監聽器它實現了ServletContextListener這個介面,在web.xml配置這個監聽器,啟動容器時,就會預設執行它實現的方法。在ContextLoaderListener中關聯了ContextLoader這個類,所以整個載入配置過程由ContextLoader來完成。
ContextLoaderListener在web.xml中的配置
<!-- 配置contextConfigLocation初始化引數 -->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 配置ContextLoaderListerner -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
複製程式碼
ServletContextListener 介面有兩個方法:contextInitialized,contextDestroyed
DispatcherServlet
Spring MVC框架,與其他很多web的MVC框架一樣:請求驅動;所有設計都圍繞著一箇中央Servlet來展開,它負責把所有請求分發到控制器;同時提供其他web應用開發所需要的功能。不過Spring的中央處理器,DispatcherServlet,能做的比這更多。
下圖展示了Spring Web MVC的DispatcherServlet處理請求的工作流。熟悉設計模式的朋友會發現,DispatcherServlet應用的其實就是一個“前端控制器”的設計模式(其他很多優秀的web框架也都使用了這個設計模式)。
流程圖
在web.xml中的配置
<!-- servlet定義 -->
<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>/</url-pattern>
</servlet-mapping>
複製程式碼
其中
load-on-startup:表示啟動容器時初始化該Servlet;
url-pattern:表示哪些請求交給Spring Web MVC處理, “/” 是用來定義預設servlet對映的。也可以如“*.html”表示攔截所有以html為副檔名的請求。
在Spring MVC中,每個DispatcherServlet都持有一個自己的上下文物件WebApplicationContext,它又繼承了根(root)WebApplicationContext物件中已經定義的所有bean。這些繼承的bean可以在具體的Servlet例項中被過載,在每個Servlet例項中你也可以定義其scope下的新bean。
WebApplicationContext繼承自ApplicationContext,它提供了一些web應用經常需要用到的特性。它與普通的ApplicationContext不同的地方在於,它支援主題的解析,並且知道它關聯到的是哪個servlet(它持有一個該ServletContext的引用)
spring mvc同時提供了很多特殊的註解,用於處理請求和渲染檢視等。DispatcherServlet初始化的過程中會預設使用這些特殊bean進行配置。如果你想指定使用哪個特定的bean,你可以在web應用上下文WebApplicationContext中簡單地配置它們。
其中,常用的ViewResolver的配置。以jsp作為檢視為例
<!-- 對模型檢視名稱的解析,即在模型檢視名稱新增前字尾 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
配置上傳檔案限制MultipartResolver
<!-- 上傳限制 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上傳檔案大小限制為31M,31*1024*1024 -->
<property name="maxUploadSize" value="32505856"/>
</bean>
複製程式碼
applicationContext.xml中的標籤
檔案上傳
前面說到DispatcherServlet中有個特殊的Bean叫MultipartResolver,可用於限制檔案的上傳大小等。當解析器MultipartResolver完成處理時,請求便會像其他請求一樣被正常流程處理。
表單
<form method="post" action="/form" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit"/>
</form>
複製程式碼
控制器
@RequestMapping(path = "/form", method = RequestMethod.POST)
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
複製程式碼
異常處理
先來說下常見的異常處理有幾種方式,如下圖:
Spring的處理器異常解析器HandlerExceptionResolver介面的實現負責處理各類控制器執行過程中出現的異常。也是上面提到的,是DispatcherServlet中的特殊bean,可以自定義配置處理。
某種程度上講,HandlerExceptionResolver與你在web應用描述符web.xml檔案中能定義的異常對映(exception mapping)很相像,不過它比後者提供了更靈活的方式。比如它能提供異常被丟擲時正在執行的是哪個處理器這樣的資訊。
HandlerExceptionResolver 提供resolveException介面
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
複製程式碼
在BaseController中使用 @ExceptionHandler註解處理異常
@ExceptionHandler(Exception.class)
public Object exceptionHandler(Exception ex, HttpServletResponse response,
HttpServletRequest request) throws IOException {
String url = "";
String msg = ex.getMessage();
Object resultModel = null;
try {
if (ex.getClass() == HttpRequestMethodNotSupportedException.class) {
url = "admin/common/500";
System.out.println("--------毛有找到對應方法---------");
} else if (ex.getClass() == ParameterException.class) {//自定義的異常
} else if (ex.getClass() == UnauthorizedException.class) {
url = "admin/common/unauth";
System.out.println("--------毛有許可權---------");
}
String header = req.getHeader("X-Requested-With");
boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header);
String method = req.getMethod();
boolean isPost = "POST".equalsIgnoreCase(method);
if (isAjax || isPost) {
return Message.error(msg);
} else {
ModelAndView view = new ModelAndView(url);
view.addObject("error", msg);
view.addObject("class", ex.getClass());
view.addObject("method", request.getRequestURI());
return view;
}
} catch (Exception exception) {
logger.error(exception.getMessage(), exception);
return resultModel;
} finally {
logger.error(msg, ex);
ex.printStackTrace();
}
}
複製程式碼
在web.xml中處理異常
<!-- 預設的錯誤處理頁面 -->
<error-page>
<error-code>403</error-code>
<location>/403.html</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<!-- 僅僅在除錯的時候注視掉,在正式部署的時候不能註釋 -->
<!-- 這樣配置也是可以的,表示發生500錯誤的時候,轉到500.jsp頁面處理。 -->
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
<!-- 這樣的配置表示如果jsp頁面或者servlet發生java.lang.Exception型別(當然包含子類)的異常 就會轉到500.jsp頁面處理。 -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/500.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/500.jsp</location>
</error-page>
<!-- 當error-code和exception-type都配置時,exception-type配置的頁面優先順序高及出現500錯誤,發生異常Exception時會跳轉到500.jsp-->
複製程式碼
來一個問題:HandlerExceptionResolver和web.xml中配置的error-page會有衝突嗎?
解答:如果resolveException返回了ModelAndView,會優先根據返回值中的頁面來顯示。不過,resolveException可以返回null,此時則展示web.xml中的error-page的500狀態碼配置的頁面。 當web.xml中有相應的error-page配置,則可以在實現resolveException方法時返回null。 API文件中對返回值的解釋: return a corresponding ModelAndView to forward to, or null for default processing.
四:《spring之aop》
什麼是aop
AOP(Aspect-OrientedProgramming,面向方面程式設計),可以說是OOP(Object-Oriented Programing,物件導向程式設計)的補充和完善。OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌程式碼往往水平地散佈在所有物件層次中,而與它所散佈到的物件的核心功能毫無關係。這種散佈在各處的無關的程式碼被稱為橫切(cross-cutting)程式碼,在OOP設計中,它導致了大量程式碼的重複,而不利於各個模組的重用。
而AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。
aop使用場景
aop框架種類
- AspectJ
- JBoss AOP
- Spring AOP
使用aop可以做的事情有很多。
- 效能監控,在方法呼叫前後記錄呼叫時間,方法執行太長或超時報警。
- 快取代理,快取某方法的返回值,下次執行該方法時,直接從快取裡獲取。
- 軟體破解,使用AOP修改軟體的驗證類的判斷邏輯。
- 記錄日誌,在方法執行前後記錄系統日誌。
- 工作流系統,工作流系統需要將業務程式碼和流程引擎程式碼混合在一起執行,那麼我們可以使用AOP將其分離,並動態掛接業務。
- 許可權驗證,方法執行前驗證是否有許可權執行當前方法,沒有則丟擲沒有許可權執行異常,由業務程式碼捕捉。
觀察一下傳統編碼方式與使用aop的區別
核心概念
描述AOP常用的一些術語有通知(Adivce)、切點(Pointcut)、連線點(Join point)、切面(Aspect)、引入(Introduction)、織入(Weaving)、通知(Advice)等。
簡單例子
相比xml配置,基於註解的方式更加簡潔方便。
@Aspect
public class TransactionDemo {
@Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
public void point(){
}
@Before(value="point()")
public void before(){
System.out.println("transaction begin");
}
@AfterReturning(value = "point()")
public void after(){
System.out.println("transaction commit");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("transaction begin");
joinPoint.proceed();
System.out.println("transaction commit");
}
}
複製程式碼
在applicationContext.xml中配置。
<aop:aspectj-autoproxy />
<bean id = "transactionDemo" class = "com.yangxin.core.transaction.TransactionDemo" />
複製程式碼
spring aop原理
通過前面介紹可以知道:AOP 代理其實是由 AOP 框架動態生成的一個物件,該物件可作為目標物件使用。AOP 代理包含了目標物件的全部方法,但 AOP 代理中的方法與目標物件的方法存在差異:AOP 方法在特定切入點新增了增強處理,並回撥了目標物件的方法。
Spring 的 AOP 代理由 Spring 的 IoC 容器負責生成、管理,其依賴關係也由 IoC 容器負責管理。因此,AOP 代理可以直接使用容器中的其他 Bean 例項作為目標,這種關係可由 IoC 容器的依賴注入提供。
aop開發時,其中需要程式設計師參與的只有 3 個部分:
- 定義普通業務元件。
- 定義切入點,一個切入點可能橫切多個業務元件。
- 定義增強處理,增強處理就是在 AOP 框架為普通業務元件織入的處理動作。
為了理清關係,先來個類關係圖。
兩種動態代理方式
Spring預設採取的動態代理機制實現AOP,當動態代理不可用時(代理類無介面)會使用CGlib機制。
Spring提供了兩種方式來生成代理物件: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據AdvisedSupport物件的配置來決定。預設的策略是如果目標類是介面,則使用JDK動態代理技術,否則使用Cglib來生成代理。
JDK動態代理
- JDK動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。InvocationHandler是一個介面,通過實現該介面定義橫切邏輯,並通過反射機制呼叫目標類的程式碼,動態將橫切邏輯和業務邏輯編制在一起。
- Proxy利用InvocationHandler動態建立一個符合某一介面的例項,生成目標類的代理物件。
CGLib動態代理
- CGLib全稱為Code Generation
Library,是一個強大的高效能,高質量的程式碼生成類庫,可以在執行期擴充套件Java類與實現Java介面,CGLib封裝了asm,可以再執行期動態生成新的class。和JDK動態代理相比較:JDK建立代理有一個限制,就是隻能為介面建立代理例項,而對於沒有通過介面定義業務方法的類,則可以通過CGLib建立動態代理。
知識擴充
通過上面的分析,大家是否有種熟悉的感覺,似乎和攔截器、過濾器的功能相似。那麼問題來了,aop與攔截器、過濾器是什麼關係。
先來回顧一下攔截器與過濾器。如下圖一網友的測試,在web.xml中註冊了TestFilter1和TestFilter2。然後在spring的配置檔案中配置了BaseInterceptor和TestInterceptor。得到的結果如下圖所示。從圖中可以看出,攔截器和過濾器都橫切了業務方法,看似符合aop的思想。
Filter過濾器:攔截web訪問url地址。 Interceptor攔截器:攔截以 .action結尾的url,攔截Action的訪問。 Spring AOP攔截器:只能攔截Spring管理Bean的訪問(業務層Service)
五:《spring之cache》
關於快取
快取是實際工作中非常常用的一種提高效能的方法。而在java中,所謂快取,就是將程式或系統經常要呼叫的物件存在記憶體中,再次呼叫時可以快速從記憶體中獲取物件,不必再去建立新的重複的例項。這樣做可以減少系統開銷,提高系統效率。
在增刪改查中,資料庫查詢佔據了資料庫操作的80%以上,而非常頻繁的磁碟I/O讀取操作,會導致資料庫效能極度低下。而資料庫的重要性就不言而喻了:
- 資料庫通常是企業應用系統最核心的部分
- 資料庫儲存的資料量通常非常龐大
- 資料庫查詢操作通常很頻繁,有時還很複雜
在系統架構的不同層級之間,為了加快訪問速度,都可以存在快取
spring cache特性與缺憾
現在市場上主流的快取框架有ehcache、redis、memcached。spring cache可以通過簡單的配置就可以搭配使用起來。其中使用註解方式是最簡單的。
Cache註解
從以上的註解中可以看出,雖然使用註解的確方便,但是缺少靈活的快取策略,
快取策略:
- TTL(Time To Live ) 存活期,即從快取中建立時間點開始直到它到期的一個時間段(不管在這個時間段內有沒有訪問都將過期)
- TTI(Time To Idle) 空閒期,即一個資料多久沒被訪問將從快取中移除的時間
專案中可能有很多快取的TTL不相同,這時候就需要編碼式使用編寫快取。
條件快取
根據執行流程,如下@Cacheable將在執行方法之前( #result還拿不到返回值)判斷condition,如果返回true,則查快取;
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id)
複製程式碼
如下@CachePut將在執行完方法後(#result就能拿到返回值了)判斷condition,如果返回true,則放入快取
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user)
複製程式碼
如下@CachePut將在執行完方法後(#result就能拿到返回值了)判斷unless,如果返回false,則放入快取;(即跟condition相反)
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
public User conditionSave2(final User user)
複製程式碼
如下@CacheEvict, beforeInvocation=false表示在方法執行之後呼叫(#result能拿到返回值了);且判斷condition,如果返回true,則移除快取;
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
public User conditionDelete(final User user)
複製程式碼
小試牛刀,綜合運用:
@CachePut(value = "user", key = "#user.id") public User save(User user) { users.add(user); return user; } @CachePut(value = "user", key = "#user.id") public User update(User user) { users.remove(user); users.add(user); return user; } @CacheEvict(value = "user", key = "#user.id") public User delete(User user) { users.remove(user); return user; } @CacheEvict(value = "user", allEntries = true) public void deleteAll() { users.clear(); } @Cacheable(value = "user", key = "#id") public User findById(final Long id) { System.out.println("cache miss, invoke find by id, id:" + id); for (User user : users) { if (user.getId().equals(id)) { return user; } } return null; } 複製程式碼
配置ehcache與redis
spring cache整合ehcache,spring-ehcache.xml主要內容:
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>${ehcache.version}</version> </dependency> <!-- Spring提供的基於的Ehcache實現的快取管理器 --> <!-- 如果有多個ehcacheManager要在bean加上p:shared="true" --> <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:xml/ehcache.xml"/> </bean> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcacheManager"/> <property name="transactionAware" value="true"/> </bean> <!-- cache註解,和spring-redis.xml中的只能使用一個 --> <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/> 複製程式碼
spring cache整合redis,spring-redis.xml主要內容:
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.1.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.2</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <!-- 注意需要新增Spring Data Redis等jar包 --> <description>redis配置</description> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.pool.maxIdle}"/> <property name="maxTotal" value="${redis.pool.maxActive}"/> <property name="maxWaitMillis" value="${redis.pool.maxWait}"/> <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/> <property name="testOnReturn" value="${redis.pool.testOnReturn}"/> </bean> <!-- JedisConnectionFactory --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.master.ip}"/> <property name="port" value="${redis.master.port}"/> <property name="poolConfig" ref="jedisPoolConfig"/> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connectionFactory-ref="jedisConnectionFactory"> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> </bean> <!--spring cache--> <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:redisOperations-ref="redisTemplate"> <!-- 預設快取10分鐘 --> <property name="defaultExpiration" value="600"/> <property name="usePrefix" value="true"/> <!-- cacheName 快取超時配置,半小時,一小時,一天 --> <property name="expires"> <map key-type="java.lang.String" value-type="java.lang.Long"> <entry key="halfHour" value="1800"/> <entry key="hour" value="3600"/> <entry key="oneDay" value="86400"/> <!-- shiro cache keys --> <entry key="authorizationCache" value="1800"/> <entry key="authenticationCache" value="1800"/> <entry key="activeSessionCache" value="1800"/> </map> </property> </bean> <!-- cache註解,和spring-ehcache.xml中的只能使用一個 --> <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/> 複製程式碼
專案中註解快取只能配置一個,所以可以通過以下引入哪個配置檔案來決定使用哪個快取。
<import resource="classpath:spring/spring-ehcache.xml"/>
<!-- <import resource="classpath:spring/spring-redis.xml"/>-->
複製程式碼
當然,可以通過其他配置搭配使用兩個快取機制。比如ecache做一級快取,redis做二級快取。
更加詳細的使用與配置,可以參考專案中spring-shiro-training中有關spring cache的配置。
推薦一個交流學習群:478030634 裡面會分享一些資深架構師錄製的視訊錄影:有Spring,MyBatis,Netty原始碼分析,高併發、高效能、分散式、微服務架構的原理,JVM效能優化這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多: