SSM知識總結

一.Spring

 

1.創建容器

spring有兩個父子容器

Servlet WebApplicationContext:控制器(controller)、視圖解析器(view resolvers)等相關的bean。通過spring mvc中提供的DispatchServlet來加載配置

Root WebApplicationContext:service層、dao層進行配置,如業務bean,數據源(DataSource)等。一般通過ContextLoaderListener來加載(或者是ClassPathXml或者FileSystemXml)

ContextLoaderListener用於創建ROOT WebApplicationContext,如果我們沒有通過ContextLoaderListener來創建Root WebApplicationContext,那麼Servlet WebApplicationContext的parent就是null,也就是沒有parent context。

創建成功都要調用ServletContext的setAttribute

爲什麼要有父子容器:

作用: 父子容器的作用在於,當我們嘗試從child context(即:Servlet WebApplicationContext)中獲取一個bean時,如果找不到,則會委派給parent context (即Root WebApplicationContext)來查找

在service層我們一般使用spring框架, 而在web層則有多種選擇,如spring mvc、struts等,如果現在我們想把web層從spring mvc替換成struts,那麼只需要將spring-servlet.xml替換成Struts的配置文件struts.xml即可

如果你的項目確定了只使用spring和spring mvc的話,可以沒有Root WebApplicationContext,只有Servlet WebApplicationContext

 

2.實例化bean

Sping提供了兩種類型的IOC 容器實現:BeanFactory和ApplicationContext(BeanFactory的子類接口),BeanFactory可以通過XmlBeanFactory加載配置文件,Spring Bean一般也是設置爲單例模式

bean什麼時候實例化

1.如果你使用BeanFactory作爲Spring Bean的工廠類,則所有的bean都是在第一次使用該Bean的時候實例化

2.如果你使用ApplicationContext作爲Spring Bean的工廠類

如果bean的scope是singleton的,lazy-init爲false(默認爲false),則 ApplicationContext啓動的時候就實例化該Bean,並且將實例化的Bean放在一個map結構的緩存中,下次再使用該Bean的時候, 直接從這個緩存中取

如果bean的scope是singleton的,lazy-init爲true,則該Bean的實例化是在第一次使用該Bean的時候進行實例化

bean的scope是prototype的,則該Bean的實例化是在第一次使用該Bean的時候進行實例化

 

實例化對象過程:實例化對象,設置對象屬性,檢查Aware相關接口並設置相關依賴(@Autowired,不是用的時候才設進去,而是在對象實例化時就把該對象設置到對應的類中)

注:如果一個bean的lazy-init爲false,而它所依賴的bean的lazy-init爲true,那麼它必須也確保所有上述singleton 依賴bean也被預先初始化,當然也包括設置爲延遲實例化的bean

如果使用的是註解配置bean(@Component),需要使用<context:component-scan>掃描該註解

 

3.spring相關事務

事務(都需要配置DataSourceTransactionManager):

1.java中有JdbcTemplate(封裝了JDBC API,需要配置數據源拿到數據庫連接connection),通過它可以進行CRUD(相當於stament和preparestament解析sql語句)

如果用了mybatis就不用JdbcTemplate,mybatis會有SqlSessionDaoTemplate(SqlSession的實現類)進行CRUD

2.編程式事務

基於底層API,主要有DataSourceTransactionManager,如果沒有mybatis,還要JdbcTemplate

基於transactionTemplate,需要配置transactionTemplate,如果沒有mybatis,還要JdbcTemplate

3.聲明式事務

基於xml配置,配置 <tx:advice>和 <aop:config>,如果沒有mybatis,還要JdbcTemplate

基於註解, 配置<tx:annotation-driven>,在service上加 @Transactional則所有方法都時事務

爲什麼要用事務:數據庫操作要麼都發生,要麼都不發生,如果業務邏輯很多,中間出現bug會導致前面的數據CRUD,後面失敗,導致數據不一致,用了事務就會回滾。

 

4.Aop

AOP實現:

AOP代理主要分爲靜態代理和動態代理,靜態代理的代表爲AspectJ(編譯時織入,基於繼承的機制);而動態代理則以Spring AOP爲代表(運行時織入,基於反射技術)

以下的AspectJ是spring爲了ioc的結合使用的AspectJ,與完整的AspectJ有差別,底層依是動態代理技術

1.Spring AOP : 通過配置proxyfactorybean,代理對象實現methodinterceptor(方法攔截器,與CGLIB實現不是同一個類),覆寫invoke方法

2.基於Aspect Spring AOP: 基於aop-config的xml配置或者使用aspectj註解,沒有使用它的編譯器和織入器

區別:AspectJ是一個比較牛逼的AOP框架,他可以對類的成員變量,方法進行攔截(跟spring沒有什麼關係,性能快),編譯器時增強

Spring的AOP技術只能是對方法進行攔截,運行時增強

使用時:一般都是用AspectJ註解,Spring AOP要實現很多類繁瑣,而且性能比AspectJ慢,只能用在方法上。

 

 

二.SpringMVC

 

1. 執行過程

springmvc工作原理

DispatcherServlet前端控制器接收發過來的請求,交給HandlerMapping處理器映射器,HandlerMapping處理器映射器,根據請求路徑找到相應的HandlerAdapter處理器適配器(處理器適配器就是那些攔截器或Controller),HandlerAdapter處理器適配器,處理一些功能請求,返回一個ModelAndView對象(包括模型數據、邏輯視圖名),ViewResolver視圖解析器,先根據ModelAndView中設置的View解析具體視圖,然後再將Model模型中的數據渲染到View上

 

2.springmvc文件配置

xml配置:控制層實現Controller接口,xml文件中配置爲name="/login",接收login的請求

註解:控制層類或者方法使用@RequestMapping(@RequestMapping 註解的方法叫做 Handler Method - 處理器方法),xml文件配置<mvc:annotation-driven/> (這是SpringMVC爲@Controller分發請求所必需的,幫助註冊MVC的各種處理器,這個是SpringMVC必須要配置的,因爲它聲明瞭@RequestMapping、@RequestBody、@ResponseBody等。並且,該配置默認加載很多的參數綁定方法,比如json轉換解析器等)

<mvc:annotation-driven/> 會自動註冊RequestMappingHandlerMapping、RequestMappingHandlerAdapter與ExceptionHandlerExceptionResolver三個bean,其還將提供如下支持:

支持使用ConversionService實例對錶單參數進行類型轉換,該標籤會註冊一個默認的ConverService,即FormattingConversionServiceFactoryBean。如果需要使用自定義的ConverService轉換類,需要顯示定義一個ConverService來覆蓋之前的默認實現類

支持使用@NumberFormat和@DateTimeFormat註解完成數據類型的格式化;

支持使用@Valid註解對JavaBean實例進行JSR 303驗證;

支持使用@RequestBody和@ResponseBody註解

 

3.url地址解析

@requestmapping里加斜槓和不加斜槓沒有大致區別,只不過\代表絕對路徑(\項目名\),但這裏使用相對路徑相當於\項目名\

所以也是對的,一般\符合規範一些(如果時前端頁面加斜槓是絕對路徑localhost:8080後,沒有項目名)

 

4.接收參數

1.方法中直接傳實體類(Servlet只能傳入request和response,不能直接傳實體類,只能通過request獲取,而mvc就可以通過反射set方法把值注入到實體類中)。bean的屬性名稱和請求參數名稱相同

2.通過傳具體屬性如String name,和請求參數名稱相同

3.通過傳HttpRequest獲取數據(servlet常用方式),適用於get,post提交方式

4.通過@pathVariable獲取url的數據,URL 中的 {xxx} 佔位符可以通過@PathVariable(“xxx“) 綁定到操作方法的入參中

5.通過@RequestParam獲取數據,@RequestParam可以接收某個名字(默認是參數名稱),默認請求中一定要有相應的參數,否則將報404錯誤碼

6.通過@ModelAttribute(相當於model.addAttribute())

運用在參數上,會將客戶端傳遞過來的參數按名稱注入到指定對象中,並且會將這個對象自動加入ModelMap中,便於View層使用

運用在一個非請求的方法上,會在每一個@RequestMapping標註的方法前執行,如果有返回值,則自動將該返回值加入到ModelMap中。

注:如果@ModelAttribute(“xxx”)則以xxx爲鍵值,jsp取值通過它,若沒有取名,則用參數實體的名稱,如:

@ModelAttribute User u,鍵值名稱爲user

 

5.返回數據

1.放model域中

在方法參數中加Model model,方法中可以用model.addAttribute添加數據(servlet是httpServeletRequest)

或者在方法中new ModelAndView,添加數據到model裏。

model和和request區別:

爲什麼大多程序在controller中給jsp傳值時使用model.addAttribute()而不使用 httpServeletRequest.setAttribute()?

事實上model數據,最終spring也是寫到HttpServletRequest屬性中,只是用model更符合mvc設計,減少各層間耦合,減少侵入性(因爲Model是Spring的組件,Request是J2EE的組件,使用Model而不去使用Request可以減少對J2EE的依賴,也便於調試),

其中model本生就是一個Map的實現類的子類。視圖解析器將model中的每個元素都通過request.setAttribute(name, value);添加request請求域中

Model只是用來傳輸數據的,並不會進行業務的尋址。但是,ModelAndView卻是可以進行業務尋址的,就是設置對應的要請求的靜態文件,這裏的靜態文件指的是類似jsp的文件。當然,兩者還有一個最大的區別,那就是Model是每一次請求都必須會帶着的(在前端向後臺請求時,spring會自動創建Model),但是ModelAndView是需要我們自己去新建的。

我通過${test}這個方式取值,優先取Model和ModelMap的,Model和ModelMap是同一個東西,誰最後賦值的就取誰的,然後是request,最後是從session中獲取(一般都是把值放到model域中)。

 

2.傳json

通過@ResponseBody方式,返回的是json對象,而不是json字符串,返回null時ajax會報錯,把datatype去掉(返回爲空對象時不是json對象就會跳轉到error,返回類型不一致)或者自己封裝成json字符串

通過Gson 的toJson方法把對象轉換成json字符串,通過response.getWriter()流到jsp頁面

或者JsonObject的fromObject方法把對象轉換成json字符串,通過response.getWriter()流到jsp頁面

傳json對象和json字符串區別:

傳json對象,不能是null,否則返回到ajax是error是undefined,除非把dataType去掉,返回的數據是new User()(裏面數據都沒賦值)那麼ajax接收的纔是null。

而傳json字符串,傳null的話ajax也會成功接收到顯示的是null,如果是沒數據那麼顯示的是""。

JsonObject和Gson區別:

JSONObject在解析的過程中會對get方法進行解析(獲取值),而Gson直接通過屬性來獲取值

後臺@requestbody可以把json字符串轉換爲實體類,後臺@responsebody可以把實體類轉換成json對象

 

6.返回頁面方式

默認是轉發,可以配置視圖解析器(因爲要傳數據到前端,所以最好不要更改返回頁面方式)

1. 轉發方式,return"forward:/xxx.jsp"

2.重定向方式,return"redirect:/xxx.jsp"

 

7.視圖渲染

視圖渲染的過程是在獲取到ModelAndView後的過程

1.把modelview的值放入html中

2.jstl獲得值

3.spring表單input標籤等其他換爲input或者其他,path轉爲id和name

 

8.mvc常用配置

 

1.類型轉換和格式化

對屬性對象的輸入輸出進行格式化,從其本質上講依然屬於"類型轉換"的範疇,所以這兩個放一起

servlet沒有類型轉換,轉換操作全都要自己手工完成,異常繁瑣。

而Spring MVC框架有許多內置的格式化轉換器完成常用數據類型轉換。

如:NumberFormatter,DateFormatter,PercentFormatter等等

這些都不要我們去寫自定義類型轉換器類,只要在配置文件中加<mvc:annotation-driven conversion-service="conversionService"/>和bean id="conversionService" ,配置內置的轉換器類(如果要加自定義的格式

p:pattern="yyyy/MM/dd",需引入xmlns:p="http://www.springframework.org/schema/p")。

但有時需要編寫具有特定功能的類型轉換器,就需要我們自定義類型轉換器

Converter<S,T>是一個可以將一種數據類型轉換成另一種數據類型的接口,S爲源類型,T爲目標類型

Formatter<T>是一個可以將一種數據類型轉換成另一種數據類型的接口,S源類型必須爲String,T爲目標類型

一般都用Formatter自定義類型轉換器,請求數據都是以String類型獲取,所以Web應用使用Formatter更合理。

 

xml需要配置:

ConversionService:只有數據轉換功能;

FormattingConversionService:具有數據轉換和數據格式化功能(默認)

常用的自定義類型轉換器是日期的,因爲springmvc默認的日期轉換格式是yyyy/MM/dd,所以你想要其他格式的則需要自定義。在mvc:annotation-driven標籤裏使用conversion-service屬性引用我們自定義的參數轉換器。

當然也可以使用註解方式,可以放屬性上或者set和get方法上

@DatetimeFormat(pattern="yyyy-MM-dd")是將String轉換成Date,主要是前臺給後臺傳值時用

pattern 屬性值指定的日期時間格式並不是將要轉換成的日期格式,這個指定的格式是和傳入的參數對應的

@JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") 將時間數據轉爲json數據 ,一般後臺傳值給前臺時

@JsonFormat 註解不是 Spring 自帶的註解,所以使用該註解前需要添加 jackson 相關的依賴包,按照國際標準時間GMT進行格式化的,而在國內默認時區使用的是CST時區,兩者相差8小時

@NumberFormat,一般用來格式化有關錢的數據,會自動將你的String類型轉爲double類型

這個也需要在配置文件中加<mvc:annotation-driven/>

 

展現在jsp頁面也需要格式化,採用jstl的fmt標籤<fmt:formatDate value="${ }" pattern="" />

還有其他方式,雖然可以解決日期數據轉換問題,但不太符合規範,簡單瞭解一下。

因爲請求中都是String類型,所以可以之間就定義日期爲String,或者也可以定義爲Date,我們知道mvc是通過反射set方法把值注入到實體類中,通過get方法獲取值,所以可以直接修改Date的set方法的參數爲string,裏面在通過SimpleDateFormat轉換成date,get方法返回數據類型爲String,這樣jsp頁面也不需格式化。

 

總的來說:可以直接使用spring內置轉換器,或者自定義類型轉換器,或者使用格式化註解,還有@InitBinder(可以對 WebDataBinder 對象進行初始化),當然最後提的修改set方法覺得可以也可以加上。

 

2.表單標籤庫

jsp開頭需要引入<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

form標籤,input標籤,password標籤,hidden標籤,textarea標籤,checkbox標籤等等。

用法:標籤元素的 id 屬性對應 modelAttribute,標籤 元素的 name 屬性對應 model 中的屬性

現在都不用這個,用的都是html標籤,因爲用了這個在視圖渲染中還要轉換成html的標籤,性能肯定比直接用html的慢,input標籤沒有html的input屬性功能多,而且現在前端頁面都很少用jsp編寫,儘量不使用。

 

3.攔截器

與servlet的過濾器類似,過濾器和攔截器的區別:

①攔截器是基於java的反射機制的,而過濾器是基於函數回調。

②攔截器不依賴與servlet容器,過濾器依賴與servlet容器。

③攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。

④攔截器可以訪問action上下文、值棧裏的對象,而過濾器不能訪問。

⑤在action的生命週期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次。

⑥攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器裏注入一個service,可以調用業務邏輯

 

一般ssm項目的web.xml需要配置過濾器解決post的亂碼問題。spring MVC實現攔截器兩種方式,實現HandlerInterceptor和WebRequestInterceptor。主要用HandlerInterceptor來實現攔截器,覆寫preHandle(處理請求之前)和postHandle(處理請求之後)和afterCompletion(視圖渲染之後)。

springmvc.xml配置interceptor

運行過程:首先運行preHandle方法,如果preHandle方法返回true則處理請求之後運行postHandle返回視圖運行afterCompletion,否則中斷執行。

多個攔截器之間:preHandle方法看配置文件的配置interceptor順序,postHandle方法和afterCompletion方法則按照配置反過來順序。

HandlerInterceptor和WebRequestInterceptor區別:

1.WebRequestInterceptor的入參WebRequest是包裝了HttpServletRequest 和HttpServletResponse的,通過WebRequest獲取Request中的信息更簡便。

2.WebRequestInterceptor的preHandle是沒有返回值的,說明該方法中的邏輯並不影響後續的方法執行,所以這個接口實現就是爲了獲取Request中的信息,或者預設一些參數供後續流程使用。

3.HandlerInterceptor的功能更強大也更基礎,可以在preHandle方法中就直接拒絕請求進入controller方法

 

4.數據驗證

數據驗證分爲客戶端驗證和服務端驗證。

客戶端主要是過濾正常用戶的誤操作,通過JavaScript來實現(可以用validate插件),服務端驗證是整個應用阻止非法數據的最後防線。攻擊者可以繞過客戶端驗證直接進行非法輸入(或者直接模擬一個假的請求發來),這樣可能會引起系統的異常,爲了確保數據的合法性,防止用戶通過非正常手段提交錯誤信息,所以必須加上服務器端驗證。

先進行的是數據轉換,數據轉換成功後才進行數據驗證。

兩種實現方式:spring自帶的驗證數據和JSR 303數據驗證。

 

1.spring自帶的驗證數據

spring自帶的驗證數據需要實現Validator接口,驗證數據的類可以藉助ValidationUtils實現驗證

supports(Class):表示這個Validator是否支持該Class的實例?

validate(Object, org.springframework.validation.Errors):對提供的對象進行校驗,並將校驗的錯誤註冊到傳入的Errors 對象中,jsp通過<form:errors path="*"/>取出錯誤信息,可以通過Spring配置消息屬性文件,bean id="messageSource"

 

2.JSR 303數據驗證

一個是Hibernate Validator,一個爲Apache BVal,我只要用Hibernate Validator(與Hibernate無關,主要是用來數據驗證)

需要配置bean id="validator"和<mvc:annotation-driven validator="validator" />

在需要數據驗證的實體類在屬性使用相應的註解,controller層接收的參數使用@valid,也可以配置消息屬性文件,如@NotBlank(message = "{}")

 

5.統一異常處理

1.簡單異常處理SimpleMappingExceptionResolver

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >

定義異常處理頁面用來獲取異常信息的變量名,默認名爲exception

<property name="exceptionAttribute" value="ex" />

<!-- 定義需要特殊處理的異常,用類名或完全路徑名作爲key,異常頁文件名作爲值,將不同的異常映射到不同的頁面上。--> <property name="exceptionMappings"> <props> <prop key=" "> </prop> </props> </property>

</bean>

2.實現 HandlerExceptionResolver 接口

這種方式只需要實現 resolveException 方法,該方法返回一個 ModelAndView 對象,在方法內部對異常的類型進行判斷,然後返回合適的 ModelAndView 對象

配置<bean class="自定義類全稱" />

3.使用 @ ExceptionHandler 註解在方法上面

使用該註解有一個不好的地方就是:進行異常處理的方法必須與出錯的方法在同一個Controller裏面

 

6.國際化

java國際化思想是將程序中的信息放在資源文件中,程序根據支持的國家及語言環境,讀取相應資源文件。資源文件是Key-Value形式,每個key的資源文件是不變的,當value是隨國家語言變化而變化。

主要通過Locale和ResourceBundle兩個類實現

Locale:用於提供本地信息,稱爲語言環境,不同國家或者地區採用不同的Locale

ResourceBundle:資源包,包含特定於語言環境的資源對象,與特定語言環境相關的信息通過資源包來提供

spring MVC實現國際化底層則就是用的java國際化。

但這裏不能用ResourceBundle加載文件,而是把國際化文件配置bean id="messageSource"里加載,

jsp使用<spring:message code=" " arguments=" ">,code爲國際消息的key,arguments爲替換消息的佔位符

 

7.文件的上傳與下載

servlet可以通過通過getInputStream()取得上傳文件二進制流,或者把請求封裝成Part,通過getPart()、getParts()取得上傳文件,或者使用ServletFileUpload類

 

spring MVC基於commons-fileupload,使用MultipareFile接口,表單文件上傳j配置bean id="multipartResolver",多文件採用集合的形式接收,通過request.getServletContext().getRealPath("upload")獲得上傳文件的虛擬路徑位置,一般都是在tomact目錄下,但這個文件路徑每次都會因爲重新部署到tomact上而會丟失以前上傳的文件,所以應該放在該項目路徑外。

下載要設置response.setContentType("application/octet-stream;charset=utf-8"),告訴瀏覽器所輸出的內容不是普通文本文件或者html文件,而是一個保存到本地的下載文件

response.setHeader("Content-Disposition", "attachment;fileName=" + name),不直接處理相應的實體內容,而是用戶選擇將實體內容保存到一個文件中,也就是說點擊下載會出現下載框,後面fileName爲文件下載名稱,但文件名稱會出現亂碼,因爲瀏覽器默認編碼爲ISO8859-1,所以需要轉碼new String(fileName.getBytes("UTF-8"),"ISO-8859-1")。

有時候名字用時間戳表示,因爲防止用戶上傳同樣的文件會覆蓋掉以前的,如果想保留原文件名也可以在數據庫加一個字段。

多文件上傳也可以藉助於前端ueditor插件或者webuploader 插件。

 

三.Mybatis

 

1.mybatis執行過程

1.加載mapper文件,通過mybatis-config.xml的mappers,或者sqlSessionFactory配置mapperLocation,MapperScannerConfigurer去掃描basePackage接口時,在一個包下的xml也會掃描

2.通過SqlSessionFactory配置數據源(或者加載mybatis-config.xml,相當於連接池)獲得SqlSession(相當於connection)實現類,只有mapperxml文件由SqlSession操作mapper文件(xml中的命名空間+select的id)進行CRUD。

有mapper接口可以是通過getMapper的代理類(每次都是獲取新的代理類),獲得接口代理來進行CRUD操作(命名空間要和mapper接口類名相同,select的id要和接口方法相同),其底層還是依賴於最原始的SqlSession的使用方法

3.另外在mybatis-spring結合中,mapper接口在生成動態代理類會自動加上@Component交給spring管理(所以可以不用加@Repository,因爲生成的代理類會自動給IOC,所以可以不用去掃描該包下的註解context:component-scan,但如果想自定義一個bean名字@Repository還是可以通過它另取),這樣service層就可以通過自動注入進去。

4.MapperScannerConfigurer根據basePackage去掃描該包下的類設置成MapperFactoryBean,通過getObject方法並生成相應的代理類,就不用自己每個去getmapper獲取,所以只要dao方法調用,實際會通過代理類的invoke方法調用MapperMethod的execute方法,MappedStatement解析了各種xml的標籤語句,通過SqlSession的crud實際調用。

5.mapper接口方法參數:

在mapper接口中加上@param("xxx")註解,則在配置文件中直接用,不加@param用#{index},是第幾個就用第幾個的索引,索引從0開始,使用List封裝參數mapper配置文件使用foreach標籤循環list,使用Map封裝參數,直接在配置文件引用#{key}即可

resultType默認實體屬性和數據庫表的屬性名稱相同,如果不同,則通過resultMap去一個個對應,通過set,get方法映射到實體類,若沒找到相應的方法,則通過屬性來映射

 

2.具體生成mapper接口代理類

1.mybatis和spring結合生成代理類

MapperScannerConfigurer或者@MapperScan:自動掃描數據映射器接口(同個包下的mapper.xml也會掃描mapperRegister註冊添加進去),生成代理類、並注入到Spring的容器中(不然要自己去getmapper生成代理類)。

mapper接口的定義在bean加載階段會被替換成MapperFactoryBean類型,在spring容器初始化的時候會給我們生成,     MapperFactoryBean類型的對象,在該對象生成的過程中調用afterPropertiesSet()方法,爲我們生成了一個MapperProxyFactory類型的對象存放於Configuration裏的MapperRegistry對象中,同時解析了mapper接口對應的xml文件,把每一個方法解析成一個MappedStatement對象,存放於Configuration裏的mappedStatements。

具體把mapper接口設置成MapperFactoryBean類型:

@MapperScan 獲取basePackage或者根據@Mapper獲取所在packages,之後通過 ClassPathMapperScanner去掃描包,獲取所有Mapper接口類的BeanDefinition,之後具體配置,設置beanClass爲MapperFactoryBean,

設置MapperFactoryBean的構造器參數爲實際的Mapper接口類,通過ClassPathBeanDefinitionScanner父類進行bean註冊,當Mapper接口注入的時候,實際調用的是MapperFactoryBean中的getObject()獲取特定的mapper實例(實際上調用getSqlSession().getMapper)

注:MapperScannerConfigurer不用這個類,就必須每個mapper接口自己配置成MapperFactoryBean,把SqlSessionFactory屬性傳進去

 

2.mybatis生成代理類

mybatis通過getSqlSession().getMapper方法獲得代理類,實際上MapperFactoryBean獲取代理類也是通過這個方法。

getmapper對應的configuration.getMapper,configuration裏parse方法解析映射文件的根節點mapper元素

bindMapperForNamespace是獲取namespace屬性值對應的Class對象後交給MapperRegistry

MapperRegistry對象維護了所有要生成動態代理類的XxxMapper接口信息,MapperRegistry的addMapper方法是針對這個接口,生成一個MapperProxyFactoy,MapperRegistry的getMapper方法是獲取該MapperProxyFactoy創建新的代理類

MapperProxyFactoy實現了InvocationHandler ,所以每次調用mapper接口方法都會回調invoke方法,如果調用的是Object類中定義的方法,直接通過反射調用即可,否則調用mapperMethod.execute

MapperMethod的構造方法中,有兩個對象SqlCommand、MethodSignature(MapperMethod的內部類),sqlCommand的getType方法,判斷要執行的sql類型INSERT、UPDATE、DELETE、SELECT、FLUSH

(因爲內部有個MappedStatement,每個<insert>等標籤都會被解析成一個MappedStatement對象,並保存到Configuration類中的mappedStatements 屬性)

 

3.mapper文件的關聯關係

1.一對一

一個人對應一張身份證

實體類設置另一個類爲屬性

 

mapper實現方法:

嵌套查詢 <association select="" column=""> 裏的column字段值作爲裏面select的傳入值,這種方法必須是另一張有個id和這張表一樣,如設置了外鍵。

嵌套結果<association><id property="id" column="id"/> <result property="" column=""/></association>

使用POJO存儲結果,新建一個新的實體類,把兩個類的屬性都放到這個實體類(兩張表屬性太多就不太實用)

 

2.一對多

數據庫需要在對應多的那方設置外鍵

對應一的實體類設置多的一方集合類爲屬性

如user裏要有List<Orders> ordersList

<collection select="" column="id">(id是對應一的那方的id)裏的column字段值作爲裏面select的傳入值,只需要一張表select * from user where uid = #{id}

<collection ><id property="id" column="id"/><result property="" column=""/></collection>,需要連接兩張表

新建一個新的實體類,把兩個類的屬性都放到這個實體類,需要連接兩張表(兩張表屬性太多就不太實用)

 

3.多對多

數據庫創建第三張表存兩個實體類的id

實體類都設置另一個實體類爲集合屬性

<collection select="">因爲沒在另張表設置外鍵,這個方法不行

一般都是用這種

<collection ><id property="id" column="id"/><result property="" column=""/></collection>,三張表做連接查詢

 

4.mybatis知識點

1.mapper文件的namespace是xml的命名空間也是標識,如果有對應的接口類的全路徑與namespace屬性值完全相同,如果有,就生成這個接口的動態代理類

2.可以typeAliasesPackage屬性沒model取一個別名,這樣每個mapper文件返回數據不用都寫全稱,另外配置SqlSessionFactory要用sqlSessionFactoryBeanName,因爲dataSource還沒加載完,SqlSessionFactory就配置進去了,導致dataSource佔位符爲空報錯

3.@Mapper註解標識爲這是mapper接口,一般都不用,MapperFactoryBean的構造器參數設置爲實際的標註了@Mapper的接口

4.mapper配置文件的增刪改查是通過屬性名來設置值,如果沒這屬性名則找該名字的getset方法,兩個都有是按屬性名來設置。

5.使用#傳入參數是,sql語句解析是會加上"",當成字符串來解析,這樣相比於$的好處是比較明顯對的吧,#{}傳參能防止sql注入,如果你傳入的參數爲 單引號',那麼如果使用${},這種方式 那麼是會報錯的

6.@repository有時候不用寫mapperscannerconfigurer的屬性basepackage掃描dao接口

它會去將dao這個層中的mapper(也就是我們的接口)都生成實現類,自動生成的實現類中,自動幫我們添加了@Component註解,然後交給spring管理(因爲mybatis.xml文件我們最終還是導入了spring容器中)

具體實現dao實體類:

7.配置數據源,BasicDataSource提供了close()方法關閉數據源,設定destroy-method=”close”屬性, 以便Spring容器關閉時,數據源能夠正常關閉;銷燬方法調用close(),是將連接關閉,並不是真正的把資源銷燬。而c3p0可以不配置,dbcp沒有自動的去回收空閒連接的功能,c3p0有自動回收空閒連接功能,不過dbcp效率比較高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章