SpringAOP,IOC, DI
- 和mybatis整合 XML 管理sql --> xml 對映 用介面配合註解 管理SQL --> 介面對映
@Insert(“sql”) @Update(“sql”) @Delete(“sql”) @Select(“sql”)
@Insert(“insert into user(username,password) values(#{屬性名}, #{屬性名})”) public void insert(User user);
- 介面mapper的名稱 要和 xml 檔名稱一致 src/java/UserMapper.java src/resources/UserMapper.xml
- 包名和介面類名要與 xml namespace
- 介面的方法名對應 xml中的id
- 介面方法名不能重複
- 宣告式事務管理
<tx:annotation-driven/> 啟用宣告式事務管理和 @Transactional
配置事務管理器, 完成事務的具體操作(開始事務,提交,操作)
<bean id="transationManager" class="DataSourceTransationManager">
<property name="dataSource" ref="連線池的id"/>
</bean>
把@Transactional註解加在需要事務控制方法上 方法正常,事務提交 方法出現執行時異常,事務回滾 方法出現了Exception或子類(檢查異常)事務仍提交 @Transactional(rollbackFor=異常.class) 控制哪類異常回滾
@Transactional(noRollbackFor=異常.class) 控制哪類異常不回滾 @Transactional(timeout=超時時間) 超過了時間,事務回滾 @Transactional(readOnly=true|false) 如果事務內僅有查詢,設為readOnly=true能夠提高一些效率 @Transactional(isolation=隔離級別) 髒讀,不可重複讀,幻讀 mysql預設 (可重複讀隔離級別)能夠避免髒讀,不可重複讀,部分幻讀 oracle預設 (提交讀) 只能避免髒讀 @Transactional(propagation=傳播行為) (預設)required 必須的(必須有一個事務,原來沒有,開始新的;原來有,就用原來的) supports 支援的(可選的,原來沒有,不用新的;原來有,就用原來的) requries_new 需要新的, 每次執行此方法時都開始新事務
反射知識
-
用另外的手段來建立物件、呼叫物件方法、使用物件的屬性,常用在框架程式設計
-
獲取類物件(*) Class<?> c = Class.forName(“包.類”); // 不需要事先import Class<?> c = 類.class; // 需要事先import
-
建立物件例項(*) c.newInstance(); // 呼叫無參構造,建立物件 c.getConstructor(構造方法的引數型別).newInstance(構造方法引數)
-
獲取方法(*) c.getMethods(); // 得到的是本類以及繼承的所有public的方法 c.getDeclaredMethods(); // 得到本類的所有方法(不考慮訪問修飾符) c.getMethod(方法名字, 方法的引數型別…) c.getDeclaredMethod(方法名字, 方法的引數型別…)
-
獲取屬性 c.getFields(); // 得到的是本類以及繼承的所有public的屬性 c.getDeclaredFields(); // 得到本類的所有屬性 c.getField(“屬性名”); c.getDeclaredField(“屬性名”);
-
方法物件 Method(*) 物件.方法名(引數); // 正常呼叫方法 方法.invoke(物件, 引數); // 反射呼叫方法
-
屬性物件 Field 物件.屬性 = 值; // 正常賦值 物件.屬性; // 正常取值
屬性.set(物件, 值); // 反射賦值 屬性.get(物件); // 反射取值
-
突破private的限制 方法物件 Method.setAccessible(true); // 允許私有方法可以訪問 屬性物件 Field.setAccessible(true); // 允許私有屬性可以訪問
=========================================================================
1.實現事務程式碼重用
思路: 1) 提供了一個代理類(Proxy) 呼叫通知類的invoke方法 獲取方法物件和方法實際引數 與目標要實現相同的介面(目的是讓使用者察覺不出是代理替換了原來的目標) 2) 提供了一個通知類(Advice) 實現了重複程式碼(事務的重複程式碼) 反射呼叫了目標物件的方法 把重複程式碼和目標方法聯絡在了一起
jdk提供了一個通用的介面 InvocationHandler (通知介面)
2.動態代理
剛才自己實現的代理類稱之為靜態代理
, jdk 還提供了動態代理技術, 它是指在程式執行期間,
由jdk生成這個代理類和他的例項物件.
正常使用類:*.java -> javac -> .class -> java -> 載入該class到虛擬機器 動態代理 直接生成了.class位元組碼, 載入該class到虛擬機器
Proxy.newProxyInstance(類載入器, 要實現的介面陣列, InvocationHandler);
3. spring的aop
3.1 pom.xml 新增依賴
<!-- spring aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!-- 第三方 aop依賴 aspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
3.2 在spring的配置檔案中加入aop的相關標籤
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 目標類 -->
<bean id="userService" class="spring.service.UserServiceTarget">
</bean>
<!-- 通知類 -->
<bean id="transactionAdvice" class="spring.advice.TransactionAdvice">
</bean>
<!-- 讓相關注解生效, 還能夠幫我們生成底層的代理物件 -->
<aop:aspectj-autoproxy/>
</beans>
3.3 實現通知類
// 切面
@Aspect
public class TransactionAdvice {
@Around("within(spring.service.UserServiceTarget)")
// 通過切點表示式,把通知和目標結合在一起
// 作用當UserServiceTarget類中任意一個方法執行時(biz1,biz2) 就會和下面的transaction方法結合在一起了
// 通知型別:環繞通知,決定了這個通知方法會和哪些目標方法結合
// 這個方法稱為通知方法,它其中包含了一些重複邏輯,另外它負責呼叫目標方法
public Object transaction(ProceedingJoinPoint pjp) { // pjp 內部去呼叫目標的方法
System.out.println("開始事務");
Object r = null;
try {
// 呼叫目標
r = pjp.proceed(); // 目標 biz1, biz2...
System.out.println("提交事務");
} catch (Throwable e) {
System.out.println("回滾事務");
}
return r;
}
}
3.4 使用
使用上,儘量實現了無侵入的效果,原來的程式碼不受影響
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("spring.xml");
// getBean方法返回的不是target物件,而是由spring容器生成的代理物件,底層產生代理的技術就是:jdk動態代理技術
UserService service = context.getBean(UserService.class);
service.biz1();
// 代理物件內部呼叫了通知TransactionAdvice物件, 檢查是否和通知方法上的切點表示式符合
// 如果符合,就執行該通知方法, 通知方法內部再呼叫目標
System.out.println("service真正型別是:"+service.getClass());
aop 的概念
代理 proxy 目標 target 被代理的物件 通知 advice 包含了可重用程式碼(例如事務程式碼),並呼叫目標 切點 pointcut 把通知和目標結合一起,它定義的是一種匹配規則 切面 aspect 切面=通知+切點
aop (aspect切面 oriented 面向 programming 程式設計) 面向切面程式設計 oop 面向物件程式設計
切點 pointcut
aspectj中定義的一些切點表示式
- within(包名.類名) 這個類中所有方法執行時,都會應用通知方法
- execution(訪問修飾符 返回值型別 包名.類名.方法名( 引數型別… ))
注意
*
可以匹配任意型別, 可以出現在方法返回值,方法名,類名當中 注意..
可以匹配方法引數,表示引數的個數和型別都是任意的 - @annotation(包名.註解名) 它是根據方法上的註解進行匹配, 如果有註解則匹配
通知型別
環繞通知: @Around(切點表示式) 加在通知方法上(功能最全的), 通知方法環繞著目標方法 前置通知: @Before 通知程式碼只會出現在目標方法之前 正常結束通知: @AfterReturning 在目標方法正常結束後,呼叫通知方法 異常通知: @AfterThrowing 在目標方法出現異常後,呼叫通知方法 結束通知: @After 在目標方法結束後(正常或異常),總會呼叫的通知
統計每個業務方法的執行時間
呼叫流程
容器啟動時
- spring會檢查容器,看是否需要為這些bean建立代理物件
- 檢查所有的切點表示式,如果匹配到了目標,就為該目標建立代理物件
獲取物件時(getBean, 依賴注入) 會檢查這個型別是否有代理物件,如果有,優先返回代理物件
呼叫方法時 呼叫了代理物件的方法,代理物件會首先經過通知類(多個)
檢查切點,如果切點匹配,再進行下面的通知呼叫
根據不同的通知型別進行呼叫: 例如前置通知先調通知,再調目標 如果是環繞通知,先呼叫通知,通知內部呼叫目標 …
概念
面向切面程式設計:就是把重複的邏輯抽取為一個通知方法, 然後通過切點匹配來決定哪些目標要應用這些通知。 其中利用了代理的技術,在代理中檢查切點是否匹配,呼叫通知(多個),最後呼叫目標
生成代理的常見技術
jdk的動態代理, 只能針對介面實現代理, jdk動態代理在高版本中效能優於cglib cglib動態代理, 既可以針對介面實現代理,也可以生成子類充當目標父類的代理
spring預設使用jdk動態代理,如果沒有實現介面,就使用cglib代理
回顧
- 代理技術 Proxy.newProxyInstance(類載入器, 介面陣列, InvocationHandler);
// 代理類中任何方法呼叫,都會進入InvocationHandler的invoke方法 class 通知類 implements InvocationHandler { public Object invoke(代理物件, 方法物件, 方法實際引數陣列){ // 事務開始 反射呼叫目標方法 // 事務提交,回滾 } } // 程式碼得到重用
- spring的aop 代理proxy 通知advice 切點pointcut 目標target 切面aspect = 通知+切點
@Aspect public class 切面類(通知類) { @Around(“切點表示式–匹配哪些目標方法要執行下面的通知”) public Object 通知方法名(ProceedingJoinPoint pjp) { 重複邏輯(例如事務、統計執行時間、許可權控制…); if(許可權檢查通過) return pjp.proceed(); // 執行了目標方法 else { throw 沒有許可權; } } }
在配置檔案中啟用aop程式設計 <aop:aspectj-autoproxy/>
-
切點表示式 within(包名.類名) 匹配此類中所有的方法 execution(public 返回型別 包名.類名.方法名(引數型別)) 匹配方法的各個要素
- 表示匹配任意的返回型別、類名、方法名… … 加在方法引數上,表示引數的型別、個數任意 @annotation(包名.註解類名) 匹配方法上是否有這個註解
-
通知型別 @Around 環繞通知 @Before 前置通知 @After 最終通知 @AfterReturning 正常結束通知 @AfterThrowing 異常通知
-
代理建立方式 jdk動態代理 – 限制:只能針對介面生成代理 cglib代理 – 既可以針對介面生成代理,也可以生成一個子類充當代理 spring會優先使用jdk動態代理, 不能用jdk動態代理的,再用cglib
===============================================================
spring-mvc
基於spring的, ioc控制反轉, di依賴注入, aop面向切面程式設計
model 模型 - 資料和操作資料的邏輯(狹義的就是資料) 包括了實體類和業務類(例如 User,UserService) view 檢視 - 資料展現, 包括(jsp, jstl, el) controller 控制器 把模型和檢視關聯在一起, 包括servlet
讓程式的各個部分分工清晰,各司其職。讓程式的可維護性提高。
使用步驟:
-
在pom.xml檔案中新增 spring-webmvc等6個依賴 注意pom.xml檔案中需要新增
<packaging>war</packaging>
(決定了是maven的web專案) 然後手動補充一個src/main/webapp目錄, src/main/webapp/WEB-INF/web.xml 在配置tomcat時選擇專案名:war exploded
-
在spring-mvc.xml中新增mvc的xml名稱空間
-
前控制器 – 統一的入口 它是由spring-mvc 提供的的一個servlet: DispatcherServlet 在web.xml 對它進行配置, maven 的目錄結構中,該檔案的路徑是 src/main/webapp/WEB-INF/web.xml
<!-- 職責:
1.作為統一入口
2.建立spring容器
3.在tomcat啟動時,就把spring容器建立好
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指明瞭spring配置檔案的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 在tomcat啟動時,就建立這個servlet,並初始化該servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
<!-- 給 DispatcherServlet 指定路徑
假設瀏覽器 /hello 沒有,會找 / 這個路徑
/s1 沒有,會找 / 這個路徑
只要沒有其他servlet能夠精確匹配這個請求路徑,這個請求都會被 / 的這個servlet來處理
-->
</servlet-mapping>
-
控制器 @Controller public class 控制器類{
@RequestMapping("/請求路徑") public String 控制器方法(){
} } // 控制器類要交給spring容器管理 並spring-mvc中加入:
<mvc:annotation-driven/>
來啟用mvc的相關功能 -
處理檢視 控制器方法的返回值是檢視名 字首+檢視名+字尾就構成了最終jsp檢視路徑, 控制器會根據檢視路徑用請求轉發跳轉過去
<mvc:view-resolvers>
<mvc:jsp prefix="/" suffix=".jsp"/>
</mvc:view-resolvers>
-
請求被處理的流程
- tomcat先將該路徑與servlet進行匹配,結果沒有精確的地址與之匹配,就找到了 / 這個地址 / 地址對應的是DispatcherServlet,由它來處理請求
- DispatcherServlet 再到spring容器中找控制器類 把每個控制器中帶有@RequestMapping的方法路徑與請求路徑進行匹配,
- 匹配到了就執行該方法, 如果匹配不到報404錯誤
- 根據方法的返回結果找到jsp檢視
- 由jsp檢視生成html程式碼作為響應返回給瀏覽器
-
以maven外掛方式執行tomcat 在pom.xml中加入外掛資訊:
<plugin> <!-- 配置一個內嵌的tomcat -->
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port> <!-- 埠號 -->
<path>/</path> <!-- 專案在tomcat中的訪問路徑 -->
<uriEncoding>utf-8</uriEncoding> <!-- 設定get請求中解碼的字符集,解決get請求中的中文亂碼問題 -->
</configuration>
</plugin>
然後在右側的maven選單中找到tomcat7外掛,執行tomcat7:run這個命令
- 接收請求引數
-
把方法引數和請求引數直接對應
-
寫一個實體型別,把實體的屬性與請求引數對應
-
路徑引數(把引數值不是寫在?之後,而是包含在路徑當中) /deleteUser?id=1 傳統寫法 /deleteUser/1 路徑引數 要刪除1號使用者 /deleteUser/2 要刪除2號使用者
路徑引數可以通過@PathVariable 與方法引數一一對應
- 使用模型資料 Model 介面, 代表了模型資料, 它也是加在控制器方法之上 可以直接使用它的實現類ModelMap或Map 向模型新增資料: model.addAttribute(“變數名”, 值); model中的資料在轉發之前,都會被存入request作用域
springmvc中 常見錯誤: 400 錯誤:發生在請求引數賦值給方法引數時,發生了型別轉換問題 例如:字串轉整數 字串轉日期 對於日期需要加 @DateTimeFormat(pattern=“日期格式”) 中文亂碼問題: 可以配置spring提供的字元編碼過濾器
<!-- spring 提供的字元編碼過濾器 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/ *</url-pattern>
</filter-mapping>
靜態資源的 404 錯誤
靜態資源是指 圖片、css、html、js
原因是DispatcherServlet的路徑是/ 跟tomcat中處理圖片的Servlet路徑衝突了
所以所有圖片的請求被DispatcherServlet所處理,把圖片的路徑當做了控制器路徑
解決辦法:
在spring配置檔案中加入:`<mvc:default-servlet-handler/>`
讓tomcat能夠熱部署,讓類的修改不用重啟伺服器就可以生效
- 首先保證 打包方式是 war exploded
- 其次,啟動tomcat使用debug方式啟動
- 在tomcat設定時,on frame deactivation 選中 update classes and resources 選項 當游標從idea切換至瀏覽器時,就會更新修改 限制:只能針對當前存在的類生效,如果是新增的類檢測不到 配置檔案的改動檢測不到
- json格式的響應 js 代表javascript object 物件, notation 標記
之前返回的大部分響應型別 text/html json也是一種響應格式:也是文字格式的,更類似於javascript的物件寫法 { “username”:“張三”, “age”:18 } 如果轉換json時出現了錯誤: No converter found for return value of type: 是因為json轉換的jar包沒有加, 可以在pom檔案中加入相應的依賴:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
在控制器方法上新增@ResponseBody註解,它可以把方法的返回值轉換成json字串
java中 map以及實體類 會被轉換為json中的物件 {屬性名:屬性值} java中 list或陣列 會被轉換為json中的陣列 [元素1, 元素2, …]
jackson常見註解: @JsonFormat 可以對日期型別進行格式化 @JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”) 注意加8小時才是北京時間 @JsonIgnore 可以在轉換時忽略加了@JsonIgnore的屬性 @JsonProperty(“新的屬性名”) 可以在轉換時改變屬性的名字
- 把springmvc的控制器和之前的 service, mapper 結合起來 UserController @Autowired private UserService service; UserService @Autowired private UserMapper mapper; UserMapper 介面 @Insert public void insert(User user);
做一個查詢所有使用者的例子,把流程調通.
- 用aop的思想完成方法的記時 UserService foo 要求記時 bar insert 要求記時 update 如何完成某幾個方法的記時