Spring個人筆記
課程流程
- Spring概念
- IOC容器
- AOP
- JDBCTemplate
- 事務管理
- Spring5新特性
Spring框架概述
-
Spring是輕量級的,開源的JaveEE框架
-
Spring可以解決企業應用開發的複雜性
-
Spring有兩個核心部分:AOC和AOP
- IOC:控制反轉,把建立物件過程交給Spring進行管理
- AOP:面向切面,不修改原始碼的情況下進行功能增強
-
Spring特點
- 方便解耦,簡化開發
- AOP程式設計的支援
- 方便程式的測試
- 方便整合整合其它框架
- 方便進行事務的操作
- 降低API開發難度
入門案例
Spring5模組
-
匯入jar包,使用maven
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
建立普通類,在這個類建立普通方法
package com.yang; public class User { public void add(){ System.out.println("add..."); } }
-
建立spring配置檔案,在配置檔案中配置建立的物件
-
spring配置檔案使用xml格式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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"> <!--配置User類物件建立--> <bean id="user" class="com.yang.User"/> </beans>
-
-
進行測試程式碼編寫
public class TestSpring5 { @Test public void testAdd() { //載入spring的配置檔案 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //傳值為xml中的id值 User user = applicationContext.getBean("user", User.class); user.add(); //獲取配置建立的物件 } }
IOC容器(控制反轉)
1. IOC底層原理
- xml解析
- 工廠模式
- 反射
概念和原理
什麼是IOC
- 控制反轉,把物件的建立和物件之間的呼叫過程,交給Spring進行管理
- 使用IOC目的:為了耦合度降低
- 做入門的案例就是IOC實現
工廠模式
目的:降低耦合度到最低限度
IOC過程,進一步降低耦合度
-
xml配置檔案,配置建立的物件
<bean id="dao" class="com.yang.UserDao"/>
-
第二步 有service和dao類,建立工廠類
class UserFactory{ public static UserDao getDao{ String classValue = class屬性值;//1.xml解析得到 //2.通過反射得到物件 Class clazz = Class.forName(classValue); return (UserDao)clazz.newInstance(); } }
2. IOC介面(BeanFactory)
- IOC思想基於IOC容器完成,IOC容器底層就是物件工廠
- Spring提供IOC容器實現的兩種方式:(兩個介面)
- BeanFactory:IOC容器基本實現,是Spring內部使用的介面,不提供給開發人員使用,載入配置檔案的時候不會建立物件,獲取或使用時才去建立物件。
- ApplicationContext: BeanFactory介面的子介面,提供更強大的功能。在載入配置檔案的時候就會把配置物件進行建立。
- ApplicationContext介面有實現類
3. IOC操作,Bean管理(基於xml)
什麼是Bean管理?包括建立物件與注入屬性
- Spring建立物件
- Spring注入屬性
3.1 基於xml方式建立物件
<bean id="user" class="com.yang.User"/>
- 在spring配置檔案中,使用bean標籤,標籤中新增上對應屬性,就可以實現物件建立
- 在bean標籤中有很多屬性,介紹常用屬性
- id屬性:唯一標識
- class屬性:類的全路徑
- 建立物件的時候預設執行無參的構造方法
3.2 基於xml方式注入屬性
DI:依賴注入,就是注入屬性,DI是IOC的一種實現方式
第一種注入方式,使用set注入
-
實體類建立
package com.yang; public class Book { private String bname; private String author; //set方法注入 public void setBname(String bname, String author) { this.bname = bname; this.author = author; } public Book() { } public void setBname(String bname) { this.bname = bname; } public void setAuthor(String author) { this.author = author; } //有參構造注入 public Book(String bname) { this.bname = bname; } }
-
bean檔案配置
<!-- set方法注入屬性--> <bean id="book" class="com.yang.Book"> <!-- 使用property完成屬性注入 name屬性代表欄位名稱 value為注入的值 --> <property name="bname" value="天龍八部"/> <property name="author" value="楊劍"/> </bean>
第二種注入方式,使用有參構造注入
-
建立類,定義屬性,建立構造方法
public class Orders { private String name; private String address; public Orders(String name, String address) { this.name = name; this.address = address; } }
-
在spring配置檔案中配置
<bean id="orders" class="com.yang.Orders"> <constructor-arg name="name" value="電腦"/> <constructor-arg name="address" value="中國"/> </bean>
第三種注入方式:p名稱空間注入(瞭解即可)
-
使用p名稱空間注入可以簡化基於xml的配置方式
-
在配置檔案中新增p名稱空間
xmlns:p="http://www.springframework.org/schema/p"
-
進行屬性注入,在bean標籤裡進行操作
<bean id="book" class="com.yang.Book" p:bname="名字" p:author="作者"/>
IOC操作Bean管理(XML注入其他型別屬性)
- 字面量
- 注入屬性-外部bean
- 注入屬性-內部bean
- 級聯賦值
字面量
-
空值
<property name="author"> <null/> </property>
-
屬性值包含特殊符號
-
轉義:><
-
CDATE
<property name="author"> <value> <![CDATA[ <<南京>> ]]> </value> </property>
-
注入屬性-外部bean
-
建立兩個類service和dao類
-
在service呼叫dao裡面的方法
-
在Spring配置檔案中進行配置
<!-- service和dao物件的建立--> <bean id="userService" class="com.yang.service.UserService"> <!-- 注入userDao物件 name:屬性值:類裡面的屬性名稱 --> <property name="userDao" ref="userDaoImpl"/> </bean> <bean id="userDaoImpl" class="com.yang.dao.UserDaoImpl"/>
注入屬性-內部bean和級聯賦值
-
一對多關係:部門和員工
一個部門有多個員工,一個員工屬於某一個部門
-
在實體類中體現一對多關係
員工表示所屬部門,用物件表示
//部門類 public class Department { private String dName; public void setdName(String dName) { this.dName = dName; } }
//員工類 public class Emp { private String ename; private String gender; //員工屬於某一個部門,使用物件形式表示 private Department department; public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } }
-
在spring配置檔案中進行相關配置
<!--內部bean--> <bean id="emp" class="com.yang.bean.Emp"> <!-- 先設定兩個普通屬性--> <property name="ename" value="ya"/> <property name="gender" value="男"/> <!-- 物件型別屬性--> <property name="department"> <bean class="com.yang.bean.Department"> <property name="dName" value="公司"/> </bean> </property> </bean>
注入屬性:級聯賦值
-
第一種方式
<bean id="emp" class="com.yang.bean.Emp">
<!-- 先設定兩個普通屬性-->
<property name="ename" value="ya"/>
<property name="gender" value="男"/>
<!-- 物件型別屬性-->
<!-- 級聯賦值-->
<property name="department" ref="deparment"/>
</bean>
<bean id="deparment" class="com.yang.bean.Department">
<property name="dName" value="財務部"/>
</bean>
- 第二種方式,需要生成deparment的get方法
<bean id="emp" class="com.yang.bean.Emp">
<!-- 先設定兩個普通屬性-->
<property name="ename" value="ya"/>
<property name="gender" value="男"/>
<!-- 物件型別屬性-->
<!-- 級聯賦值-->
<property name="department" ref="department"/>
<property name="department.dName" value="cai"/>
</bean>
<bean id="department" class="com.yang.bean.Department">
<property name="dName" value="財務部"/>
</bean>
xml注入集合屬性
- 注入陣列型別屬性
- 注入List集合
- 注入Map集合型別的屬性
-
建立類,定義陣列, list,map,set集合屬性,生成對應的set方法
-
在spring配置檔案中配置
<bean id="student" class="com.yang.collectionType.Student"> <!-- 陣列型別--> <property name="courses"> <array> <value>java</value> <value>資料庫</value> </array> </property> <!-- list型別--> <property name="list"> <list> <value>張三</value> <value>小三</value> </list> </property> <!-- Map型別--> <property name="maps"> <map> <entry key="Java" value="java"/> <entry key="PHP" value="php"/> </map> </property> <!-- set--> <property name="set"> <set> <value>123</value> <value>mysql</value> </set> </property> </bean>
-
在集合裡設定物件型別值
<property name="courseList"> <list> <ref bean="course1"/> <ref bean="course2"/> </list> </property>
<bean id="course1" class="com.yang.collectionType.Course"> <property name="cname" value="Spring5框架"/> </bean> <bean id="course2" class="com.yang.collectionType.Course"> <property name="cname" value="MyBatis框架"/> </bean>
-
把集合注入部分提取出來
-
在Spring配置檔案中引入名稱空間 util
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd ">
-
使用util標籤提取
<util:list id="bookList"> <value>三國</value> <value>水滸</value> </util:list> <bean id="book" class="com.yang.collectionType.Book"> <property name="list" ref="bookList"/> </bean>
-
IOC操作Bean管理(FactoryBean)
-
Spring有兩種型別的Bean,一種普通Bean,另一種:工廠Bean(FactoryBean)
-
普通Bean:在配置檔案中定義的Bean型別就是你的返回型別
-
工廠Bean:在配置檔案中定義的Bean型別可以和返回型別不一樣
第一步:讓這個類作為工廠Bean,實現介面FactoryBean
<bean id="myBean" class="com.yang.factorybean.MyBean"> </bean>
第二步:實現接口裡的方法,在實現的方法中定義返回的bean型別
public class MyBean implements FactoryBean<Course> { //定義返回Bean public Course getObject() throws Exception { Course course = new Course(); course.setCname("abc"); return course; } public Class<?> getObjectType() { return null; } public boolean isSingleton() { return false; } }
IOC操作Bean管理(Bean作用域)
-
在Spring裡面,設定建立bean例項是單例項還是多例項
-
在Spring裡面,預設情況下,bean是單例項物件
-
如何設定單例項還是多例項
-
spring配置檔案中bean標籤裡有屬性(scope)用於設定單例項還是多例項
-
scope屬性值
第一個值 預設值,singleton,表示單例項物件
第二個值 prototype,多例項物件
-
scope和prototype的區別
第一:singleton:單例項
prototype:多例項
第二:設定scope為singleton的時候,載入配置檔案的時候就會建立一個單例項物件
設定scope值是prototype的時候,不是載入配置檔案時建立物件,在呼叫getBean方法的時候去建立多例項物件
還有request,session,一般不用,瞭解即可
生命週期: 從物件建立到物件銷燬的的過程
bean生命週期
- 通過構造器建立bean例項(無參構造)
- 為bean的屬性設定值和對其他bean的引用(呼叫set方法)
- 呼叫bean的初始化的方法(需要進行配置)
- bean可以使用了(物件獲取到了)
- 當容器在關閉的時候,呼叫bean的銷燬方法(需要進行配置銷燬的方法)
演示bean的生命週期
package com.yang.bean;
public class Orders {
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二部,呼叫set方法設定屬性值");
}
public Orders(){
System.out.println("第一步,無參構造建立bean例項");
}
//建立執行的初始化方法
public void initMethod(){
System.out.println("第三步,執行初始化的方法");
}
//建立銷燬的方法
public void destroyMethod(){
System.out.println("第五步,執行銷燬的方法");
}
}
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四部,獲取建立bean例項的物件");
System.out.println(orders);
//手動銷燬bean例項
context.close();
}
bean的後置處理器,bean生命週期變為七步
bean生命週期
- 通過構造器建立bean例項(無參構造)
- 為bean的屬性設定值和對其他bean的引用(呼叫set方法)
- 把bean例項傳給bean後置處理器的方法postProcessBeforeInitialization
- 呼叫bean的初始化的方法(需要進行配置)
- 把bean例項傳給bean後置處理器的方法postProcessAfterInitialization
- bean可以使用了(物件獲取到了)
- 當容器在關閉的時候,呼叫bean的銷燬方法(需要進行配置銷燬的方法)
新增後置處理器效果
-
建立類,實現介面BeanPostProcessor,建立後置處理器
package com.yang.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPost implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前執行的方法"); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之後執行的方法"); return bean; } }
-
在bean中配置,自動為所有bean加上
<bean id="myBeanPost" class="com.yang.bean.MyBeanPost"/>
xml自動裝配
什麼是自動裝配?
根據指定裝配規則(屬性名稱或屬性型別),Spring自動將匹配的屬性值進行注入
演示自動裝配過程
-
根據屬性名稱進行自動注入 byName
<bean id="emp" class="com.yang.autowire.Emp" autowire="byName"> <!-- <property name="dept" ref="dept"/>--> </bean> <bean id="dept" class="com.yang.autowire.Dept"/>
-
根據屬性型別進行自動注入 byType
<bean id="emp" class="com.yang.autowire.Emp" autowire="byType"> <!-- <property name="dept" ref="dept"/>--> </bean> <bean id="dept" class="com.yang.autowire.Dept"/>
外部屬性檔案
-
直接配置資料庫資訊,配置druid連線池
-
配置druid連線池
-
引入druid連線池依賴
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
-
-
引入外部屬性檔案配置資料庫連線池
-
建立外部屬性檔案,properties格式檔案,寫資料庫資訊
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/mybatis?useSSL=true prop.userName=root prop.password=123456
-
把外部properties屬性檔案引入到Spring配置檔案中
引入context名稱空間
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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">
在spring配置檔案中使用標籤引入外部屬性檔案
寫入
<context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"/> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.userName}"/> <property name="password" value="${prop.password}"/> </bean>
-
4. IOC操作,Bean管理(基於註解)
- 什麼是註解
- 使用註解的目的:簡化xml操作
spring針對Bean管理中建立物件提供註解
-
@Component
-
@Service
-
@Controller
-
@Respository
上面的四個註解功能是一樣的,都可以用來建立bean例項
基於註解方式實現物件的建立
-
引入AOP依賴
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
開啟元件掃描
<!-- 開啟元件掃描 1.如果掃描多個包,多個包用逗號隔開 2.掃描上層目錄 --> <context:component-scan base-package="com.yang"/>
-
建立類,在類上面新增物件註解
//在註解裡的value屬性值可以省略不寫 //預設值是類的名稱,把首字母小寫 @Service(value = "userService") //<bean id="userService" class=""/> public class UserService { public void add() { System.out.println("service add.............."); } }
開啟元件掃描細節配置
<!-- 示例1
use-default-filters="false" 表示現在不使用預設的filter,而是使用自己配置的filter
context:include-filter:設定要掃描哪些內容
-->
<context:component-scan base-package="com.yang" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 事例2:
context:exclude-filter 設定哪些內容不進行掃描
-->
<context:component-scan base-package="com.yang">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
註解方式屬性注入
- @Autowired:根據屬性型別進行自動裝配
- @Qualifier:根據屬性名稱進行注入
- @Resource:可以根據型別注入,也可以根據名稱注入
- @Value:注入普通型別屬性
@Autowired
-
把service和dao物件建立,在service和dao類新增建立物件的註解
-
在service注入dao,在service類新增dao型別的屬性,在屬性上面使用註解
//在註解裡的value屬性值可以省略不寫 //預設值是類的名稱,把首字母小寫 @Service(value = "userService") //<bean id="userService" class=""/> public class UserService { //定義dao型別屬性 //不需要新增set方法 //新增屬性註解 @Autowired //根據型別進行注入 private UserDao userDao; public void add() { System.out.println("service add.............."); userDao.add(); } }
@Repository public class UserDaoImpl implements UserDao{ public void add() { System.out.println("dao add...."); } }
@Qualifier:根據屬性名稱進行注入
-
這個@Qualifier註解的使用要和上面的@Autowired一起使用
@Repository(value = "userDaoImpl1") public class UserDaoImpl implements UserDao{ public void add() { System.out.println("dao add...."); } }
@Autowired //根據型別進行注入 @Qualifier(value = "userDaoImpl1") //根據名稱及逆行注入
@Resource:可以根據型別注入,也可以根據名稱注入,包:javax.annotation.Resource
@Service(value = "userService") //<bean id="userService" class=""/>
public class UserService {
//@Resource //根據型別進行注入
@Resource(name = "userDaoImpl1")//根據名稱進行注入
private UserDao userDao;
public void add() {
System.out.println("service add..............");
userDao.add();
}
}
@Value:注入普通屬性
@Value(value = "yj")
private String name;
完全註解開發
-
建立配置類,替代xml配置檔案
@Configuration //作為配置類 ,替代xml配置檔案 @ComponentScan(basePackages = {"com.yang"}) public class SpringConfig { }
-
測試類
@Test public void test2(){ //載入配置類 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.add(); }
AOP(面向切面程式設計)
概念:面向切面程式設計(面向方面程式設計)利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
通俗描述:不通過修改原始碼方式,在主幹功能裡新增新功能
底層原理
- AOP底層使用動態代理方式
- 有兩種情況的動態代理
- 有介面情況,使用JDK動態代理
- 沒有介面情況,使用CGLIB動態代理
有介面情況,使用JDK動態代理
建立UserDao介面實現類代理物件,增強類的方法
沒有介面情況,使用CGLIB動態代理
建立當前類子類的代理物件
AOP(JDK動態代理)
- 使用JDK動態代理,使用Proxy類裡面的方法建立代理物件
- 呼叫newProxyInstance方法來實現,有三個引數
- 類載入器
- 增強方法所在的類,類實現的介面,支援多個介面
- 實現這個介面InvocationHandler,建立代理物件,寫增強的方法
-
編寫JDK動態代理程式碼
-
建立介面,定義方法
public interface UserDao { int add(int a,int b); String update(String id); }
-
建立介面實現類,實現方法
public class UserDaoImpl implements UserDao{ public int add(int a, int b) { return a+b; } public String update(String id) { return id; } }
-
使用Proxy建立介面的代理物件
public class JDKProxy { public static void main(String[] args) { //建立介面實現類的代理物件 UserDao userDao = new UserDaoImpl(); Class[] interfaces = {UserDao.class}; UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int res = dao.add(1, 2); System.out.println("result = "+res); } } //建立代理物件程式碼 class UserDaoProxy implements InvocationHandler { //把建立的代理物件的本體傳遞過來 //有慘構造進行傳遞 private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } //增強的邏輯 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法前執行 : " + method.getName() + "傳遞的引數:" + Arrays.toString(args)); //被增強的方法執行 Object res = method.invoke(obj, args); //方法之後 System.out.println("方法後執行 : " + obj); return res; } }
-
AOP術語
- 連線點,類裡面哪些方法可以被增強,這些方法稱為連線點
- 切入點,實際被增強的方法,稱為切入點
- 通知(增強),
- 實際增強的邏輯部分稱為通知(增強)
- 通知有多種型別,
前置通知
,後置通知
,環繞通知
,異常通知
,最終通知
(finally)
- 切面:把通知應用到切入點的過程
AOP操作(準備)
-
Spring框架中,一般基於AspectJ實現AOP操作
AspectJ:不是spring組成部分,獨立AOP框架,一半把AspectJ與Spring框架一起使用。進行AOP相關操作
-
基於AspectJ實現AOP操作
- 基於xml配置檔案
- 基於註解方式(使用)
-
在專案工程中引入AOP相關依賴
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
切入點表示式
- 切入點表示式的作用:知道對哪個類裡面的哪個方法進行增強
- 語法結構
execution([許可權修飾符][返回型別][全類名][方法名稱]([引數列表])
舉例1:對com.yang.dao.Book類裡面的add進行增強
execution(* com.yang.dao.Book.add(..))
舉例2:對com.yang.dao.Book類裡面的所有方法進行增強
execution(* com.yang.dao.Book.*(..))
舉例3:對com.yang.dao包裡面的所有類裡面的所有包進行增強
execution(* com.yang.dao.*.*(..))
AOP操作(AspectJ註解)
-
建立類,在類裡面定義方法,
public class User { public void add(){ System.out.println("add,,,,"); } }
-
建立增強類(編寫增強的邏輯)
在增強類裡面建立方法,讓不同方法代表不同通知型別
//增強的類 public class UserProxy { //前置通知 public void before(){ System.out.println("before........"); } }
-
進行通知的配置
-
在Spring配置檔案中,開啟註解掃描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--開啟註解掃描--> <context:component-scan base-package="com.yang.aopanno"/> </beans>
-
使用註解建立Userh和UserProxy物件
package com.yang.aopanno; import org.springframework.stereotype.Component; //被增強類 @Component public class User { public void add(){ System.out.println("add,,,,"); } }
package com.yang.aopanno; import org.springframework.stereotype.Component; //增強的類 @Aspect @Component public class UserProxy { //前置通知 public void before(){ System.out.println("before........"); } }
-
在增強類上面添加註解@Aspect
-
在Spring配置檔案中,開啟生成代理物件
<aop:aspectj-autoproxy/>
-
配置不同型別的通知
在增強類的裡面,在作為通知方法上面新增通知型別註解,使用切入點表示式
-
package com.yang.aopanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增強的類
@Component
@Aspect //生成代理物件
public class UserProxy {
//前置通知
//@Before註解表示作為前置通知
@Before(value = "execution(* com.yang.aopanno.User.add(..))")
public void before() {
System.out.println("before........");
}
//最終通知
@After(value = "execution(* com.yang.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//後置通知(返回通知),有異常不執行
@AfterReturning(value = "execution(* com.yang.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning....");
}
@AfterThrowing(value = "execution(* com.yang.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing....");
}
@Around(value = "execution(* com.yang.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("環繞之前。。。");
proceedingJoinPoint.proceed();
System.out.println("環繞之後。。。");
}
}
公共切入點提取
//相同切入點抽取
@Pointcut(value = "execution(* com.yang.aopanno.User.add(..))")
public void pointDemo(){
}
@Before(value = "pointDemo()")
public void before() {
System.out.println("before........");
}
有多個增強類對同一個方法進行增強,設定增強類優先順序
-
在增強類上面添加註解
@Order(數字型別值)
,數字型別值越小,優先順序越高@Component//物件建立 @Aspect//代理物件建立 @Order(1) public class PersonProxy { @Before(value = "execution(* com.yang.aopanno.User.add(..))") public void before(){ System.out.println("Person before......"); } }
-
@ComponentScan(basePackages = {"com.yang"}) @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class MyConfig { }
AOP操作(AspectJ配置檔案)
-
建立兩個類,增強類和被增強類,建立方法
-
在spring配置檔案中建立兩個類物件
<!-- 建立物件--> <bean id="book" class="com.yang.aopxml.Book"/> <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/>
-
在spring配置檔案中配置切入點
<!-- 建立物件--> <bean id="book" class="com.yang.aopxml.Book"/> <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/> <!-- 配置aop增強--> <aop:config> <!-- 配置切入點--> <aop:pointcut id="p" expression="execution(* com.yang.aopxml.Book.buy(..))"/> <!-- 配置切面--> <aop:aspect ref="bookProxy"> <!-- 增強在具體的方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
JDBCTemplate
概念和準備
Spring框架對JDBC進行封裝,使用JDBCTemplate方便實現對資料庫操作
-
引入依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.orm</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency>
-
在Spring配置檔案配置連線池
<context:property-placeholder location="classpath:database.properties"/> <!-- 資料庫連線池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.userName}"/> <property name="password" value="${prop.password}"/> <property name="driverClassName" value="${prop.driverClass}"/> </bean>
-
配置JdbcTemplate物件,注入dataSource
<!-- 建立JDBCTemplate物件--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入dataSource--> <property name="dataSource" ref="dataSource"/> </bean>
-
建立service類與dao類,在dao注入jdbctemplate物件
配置檔案中開啟元件掃描
<context:component-scan base-package="com.yang"/>
Dao層
@Repository public class BookDaoImpl implements BookDao{ //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; }
Service層
@Service public class BookService { //注入Dao @Autowired private BookDao bookDao; }
JdbcTemplate操作資料庫(新增)
-
建立實體類
package com.yang.entity; public class User { private String id; private String name; private String pwd; @Override public String toString() { return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
-
編寫service和dao
-
在dao進行資料庫新增操作
兩個引數:1. sql語句
2.可變長引數
@Repository public class BookDaoImpl implements BookDao{ //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; public void add(Book book) { // 1.建立sql語句 String sql = "insert into mybatis.book values(?,?,?)"; // 2.呼叫方法實現 Object[] args = {book.getId(), book.getName(), book.getPwd()}; int update = jdbcTemplate.update(sql,args); System.out.println(update); } }
-
修改與刪除操作
public void updateBook(Book book) {
String sql = "update mybatis.book set name=?,pwd=? where id=?";
Object[] args = {book.getName(),book.getPwd(),book.getId()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
public void deleteBook(Integer id) {
String sql = "delete from mybatis.book where id = ?";
Object[] args = {id};
int delete = jdbcTemplate.update(sql,args);
System.out.println(delete);
}
查詢返回某個值
-
查詢表裡有多少條記錄,返回某個值
-
使用JDBCTemplate實現查詢返回某個值
兩個引數:sql語句,返回型別的Class
public int findCount() { String sql = "select count(*) from mybatis.book"; int count = jdbcTemplate.queryForObject(sql,Integer.class); return count; }
查詢返回物件
第一個引數:sql語句
第二個引數:RowMapper,是介面,返回不同型別的資料,使用這個接口裡面的實現類可以完成資料封裝
第三個引數:可變長引數
查詢返回集合
- 查詢圖書列表分頁
- 呼叫JDBCTemplate方法實現查詢返回集合
批量操作
-
JdbcTemplate實現批量新增的操作
有兩個引數
第一個引數:sql語句
第二個引數:List集合,新增多條記錄資料
public void batchAddBook(List<Object[]> batchArgs) { String sql = "insert into mybatis.book values (?,?,?)"; int[] res = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(res)); }
實現批量修改操作
注意:在傳值的時候要按照問號的順序傳
@Override
public void batchUpdate(List<Object[]> batchArgs) {
String sql = "update mybatis.book set name=?,pwd=? where id=?";
int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(res));
}
實現批量刪除操作
@Override
public void batchDelete(List<Object[]> batchArgs) {
String sql = "delete from mybatis.book where id = ?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
事務操作
概念
什麼是事務
- 事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗,所有操作都失敗
- 典型場景:銀行轉賬
事務四個特性(ACID):
- 原子性:不可分割
- 一致性:總量不變
- 隔離性:兩個人同時操作,不會對彼此產生影響
- 永續性:提交後表中資料發生真正變化
搭建環境
-
建立資料庫表,新增記錄
-
建立service,搭建dao,完成物件的建立和注入。
- service注入dao
- dao注入jdbctemplate,在JDBCTemplate注入dataSource
- 在dao建立兩個方法,多錢和少錢,在service建立轉賬方法
- 問題,在有異常時一半的方法執行
事務操作過程
try {
userDao.reduceMoney();
// 模擬異常
int i = 1 / 0;
userDao.addMoney();
//沒有發生異常,事務提交
} catch (Exception e) {
//出現了異常,事務回滾
e.printStackTrace();
}
Spring事務管理介紹
- 事務一般新增在javaee三層結構裡的Service層
- 在Spring進行事務管理操作有兩種方式
- 程式設計式事務管理
- 宣告式事務管理(使用)基於註解方式,基於xml配置檔案方式
宣告式事務管理
Spring進行宣告式事務管理,底層使用AOP
Spring事務管理API
1. 提供了一個介面,代表事務管理器,這個介面針對不同的框架提供了不同的實現類
註解方式實現宣告式事務管理
-
在Spring配置檔案中配置事務管理器
<!-- 配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
在spring配置檔案中開啟事務註解
-
在spring配置檔案中引入名稱空間tx
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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">
-
開啟事務
<!-- 開啟事務註解--> <tx:annotation-driven transaction-manager="transactionManager"/>
-
-
在Service類上面(或者service類方法上面)新增事務註解
-
@Transactional
加在類和方法上均可 -
如果新增在類上面,代表類裡面的所有方法都添加了事務
-
如果放在方法上面,只是為這個方法新增事務
@Service(value = "userServiceImpl") @Transactional //加在類和方法上均可 public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDaoImpl") private UserDao userDao; public void accountMoney() { userDao.reduceMoney(); int i = 1 / 0; userDao.addMoney(); } }
-
宣告式事務管理引數配置
-
在service類上面添加註解
@Transactional
,在這個註解裡面可以配置事務相關引數 -
propagation:事務傳播行為
多事務方法之間執行呼叫,這個過程中事務是如何進行管理的
事務方法:對資料庫表進行變化的操作
七種傳播行為
-
isolation:事務的隔離級別
事務有特性:隔離性,多事務操作之間不會產生影響。不考慮隔離性會產生很多問題
有三個讀的問題,髒讀,不可重複讀,虛(幻)讀
髒讀:一個未提交事務讀取到另一個未提交事務的資料
不可重複讀:一個未提交事務讀取到一個提交事務修改資料
虛讀:一個未提交事務讀取到另一提交事務新增資料
通過設定事務隔離級別,就能解決三個讀的問題
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
-
timeout超時時間。
事務需要在一定時間內進行提交,如果不提交,就會回滾
預設值是-1,設定時間以秒(s)為單位
-
readonly:是否只讀
讀:查詢操作,寫:新增修改刪除
readOnly預設為false
設定為true後,只能查詢
-
rollbackfor,回滾
設定出現哪些異常進行事務回滾
-
norollbackfor:不回滾
設定出現哪些異常不進行回滾
XML宣告式事務管理
-
在Spring配置檔案中進行配置
第一步:配置事務管理器
第二步:配置通知
第三步:配置切入點和切面
<!-- 配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置通知--> <tx:advice id="txadvice"> <!-- 配置事務相關引數--> <tx:attributes> <!-- 指定在哪種規則的方法上新增事務--> <tx:method name="accountMoney" propagation="REQUIRED"/> <!-- <tx:method name="account*"/>--> </tx:attributes> </tx:advice> <!-- 配置切入點和切面--> <aop:config> <!-- 配置切入點--> <aop:pointcut id="pt" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/> <!-- 配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
完全註解開發
-
建立配置類,使用配置類替代xml配置檔案
@Configuration @ComponentScan(basePackages = "com.yang") @EnableTransactionManagement //開啟事務 public class TxConfig { //建立資料庫連線池 @Bean public DruidDataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=Asia/Shanghai&CharacterEncoding=utf-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } //建立jdbcTemplate模板物件 @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { //到ioc容器中根據型別找到dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } //建立事務管理器 @Bean public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }
Spring5框架新功能
- 整個Spring5框架基於java8,執行時相容jdk9,許多不建議使用的類和方法在程式碼庫中刪除
- spring5框架自帶了通用的日誌封裝
- Spring5已經移除了Log4jConfigListener,官方建議使用Log4j2
Spring5框架整合Log4j2
-
引入依賴
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency> </dependencies>
-
建立log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <!--<Configuration status="WARN" monitorInterval="30"> --> <Appenders> <!--*********************控制檯日誌***********************--> <console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </Appenders> <Loggers> <!-- 根日誌設定 --> <root level="info"> <appender-ref ref="Console"/> </root> </Loggers> </Configuration>
Spring5框架核心容器支援@Nullable註解
@Nullable
可以使用在方法上,屬性上面,引數上面
- 用在方法上,表示方法返回值可以為空
- 註解用在方法引數裡面,表示引數值可以為空
- 用在屬性上,屬性值可以為空
- Spring5核心容器裡面支援函式式風格
支援整合JUnit5
第一步 引入Spring相關針對測試依賴