Spring學習筆記
1 spring介紹
1)為什么學習spring
? 1. Spring技術是JavaEE開發必備技能,企業開發技術選型命中率>90%
? 2. 簡化開發,降低企業級開發的復雜性
? 3. 框架整合,高效整合其他技術,提高企業級應用開發與運行效率
? 作為一個java程序員, spring必學., 是必經之路!
2)spring框架學什么?
? 1. IOC
? 2. AOP
? 3. 整合
3)spring中的設計模式
? 學習spring的, 先了解下spring中主要的設計模式:
1. 工廠模式
? 1.簡單工廠
? 簡單工廠模式是一種創建型設計模式,用于根據給定的類型實例化不同的類
? 例如: 不同的動物類型創建對應的動物實例。
public interface Animal {
void speak();
}
public class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
}
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow");
}
}
然后,創建一個名為AnimalFactory的簡單工廠類,它負責根據給定的類型實例化相應的動物實例:
public class AnimalFactory {
public enum AnimalType {
DOG,
CAT
}
public static Animal createAnimal(AnimalType type) {
switch (type) {
case DOG:
return new Dog();
case CAT:
return new Cat();
default:
throw new IllegalArgumentException("Invalid animal type: " + type);
}
}
}
最后,我們可以在主方法中使用這個簡單工廠類來創建不同類型的動物實例:
public class Main {
public static void main(String[] args) {
Animal dog = AnimalFactory.createAnimal(AnimalFactory.AnimalType.DOG);
dog.speak();
Animal cat = AnimalFactory.createAnimal(AnimalFactory.AnimalType.CAT);
cat.speak();
}
}
? 2.抽象工廠
? 抽象工廠模式是一種創建型設計模式,它允許您根據不同的族系或者平臺來實例化一組相關的對象;
? 首先,我們定義兩個接口,Animal和Plant
public interface Animal {
void speak();
}
public interface Plant {
void grow();
}
接下來,為不同族系創建具體的Animal和Plant實現:
// 陸地生物
public class LandAnimal implements Animal {
@Override
public void speak() {
System.out.println("I am a land animal.");
}
}
public class LandPlant implements Plant {
@Override
public void grow() {
System.out.println("I am a land plant.");
}
}
// 水生生物
public class AquaticAnimal implements Animal {
@Override
public void speak() {
System.out.println("I am an aquatic animal.");
}
}
public class AquaticPlant implements Plant {
@Override
public void grow() {
System.out.println("I am an aquatic plant.");
}
}
然后,創建一個名為NatureFactory的抽象工廠接口:
public interface NatureFactory {
Animal createAnimal();
Plant createPlant();
}
接著,為不同族系創建具體的工廠實現:
public class LandFactory implements NatureFactory {
@Override
public Animal createAnimal() {
return new LandAnimal();
}
@Override
public Plant createPlant() {
return new LandPlant();
}
}
public class AquaticFactory implements NatureFactory {
@Override
public Animal createAnimal() {
return new AquaticAnimal();
}
@Override
public Plant createPlant() {
return new AquaticPlant();
}
}
最后,在主方法中,我們可以使用這些抽象工廠類來創建不同族系的Animal和Plant實例:
public class Main {
public static void main(String[] args) {
NatureFactory landFactory = new LandFactory();
Animal landAnimal = landFactory.createAnimal();
Plant landPlant = landFactory.createPlant();
landAnimal.speak();
landPlant.grow();
NatureFactory aquaticFactory = new AquaticFactory();
Animal aquaticAnimal = aquaticFactory.createAnimal();
Plant aquaticPlant = aquaticFactory.createPlant();
aquaticAnimal.speak();
aquaticPlant.grow();
}
}
通過使用抽象工廠模式,您可以輕松地在不同的族系之間切換,同時保持代碼的可擴展性和封裝
2.單例模式
? 1.餓漢式(可用)
? 餓漢式單例模式是一種創建型設計模式,它在類加載時就創建了對象實例,并確保只有一個實例存在.
public class Singleton {
// 在類加載時創建唯一的實例
private static final Singleton INSTANCE = new Singleton();
// 將構造方法設為私有,防止外部創建新實例
private Singleton() {}
// 提供獲取唯一實例的公共方法
public static Singleton getInstance() {
return INSTANCE;
}
}
public class Main {
public static void main(String[] args) {
Singleton singletonInstance = Singleton.getInstance();
}
}
餓漢式單例模式在類加載時就創建了實例,這意味著即使您從未使用過該實例,它也會占用內存。這可能導致資源浪費,特別是在實例創建成本較高的情況下.
? 2.懶漢式(不可用,線程不安全)
? 懶漢式單例模式是一種創建型設計模式,它在第一次請求實例時才創建唯一的實例,并確保整個應用程序中僅存在一個實例.
public class Singleton {
// 初始化唯一實例為空
private static Singleton instance = null;
// 將構造方法設為私有,防止外部創建新實例
private Singleton() {}
// 提供獲取唯一實例的公共方法
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
? 3.懶漢式(鎖方法, 不推薦使用)
? 同上, 在創建對象的靜態方法上加上同步鎖synchronized .
? 4.雙重檢查(推薦使用)
? 雙重檢查鎖定(Double-Checked Locking,DCL)是一種用于確保在多線程環境下懶漢式單例模式的線程安全性的方法.
public class Singleton {
// 使用volatile關鍵字確保內存可見性
private static volatile Singleton instance = null;
// 將構造方法設為私有,防止外部創建新實例
private Singleton() {}
// 提供獲取唯一實例的公共方法,并使用雙重檢查鎖定
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
? 首先,我們進行第一次檢查:如果instance為空,說明實例尚未創建,此時需要獲取Singleton.class的鎖。當線程成功獲得鎖后,進行第二次檢查:確保在其他線程獲得鎖之前實例仍未被創建。如果instance仍然為空,則創建新的實例;否則,直接返回已創建的實例。
? 此實現在多線程環境中是線程安全的,并且相對于每次都獲取鎖的懶漢式單例模式具有更好的性能
? 5.靜態內部類(推薦使用)
public class Singleton {
// 將構造方法設為私有,防止外部創建新實例
private Singleton() {}
// 靜態內部類負責創建唯一實例
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 提供獲取唯一實例的公共方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
當Singleton類加載時, 由于靜態內部類在外部內加載時并不會加載, 而時在靜態內部內調用時才加載, 所以該方法并不會像餓漢式那樣浪費資源, 同時Java規范保證了靜態初始化是線程安全的,因此在類加載時會創建唯一的實例, 所以次方法比較推薦;
? 6.枚舉(推薦使用)
public enum Singleton {
INSTANCE;
// 可以添加其他方法和屬性
public void doSomething() {
System.out.println("Singleton enum is doing something");
}
}
public class Main {
public static void main(String[] args) {
Singleton singletonInstance = Singleton.INSTANCE;
singletonInstance.doSomething();
}
}
使用枚舉實現單例模式是一種簡潔且安全的方法。在Java中,枚舉類型可以確保線程安全和實例唯一性;
3.代理模式
? 代理模式是一種結構型設計模式,其目的是通過代理來控制對實際對象的訪問。代理通常與實際對象具有相同的接口,并將請求轉發給實際對象,可能會在轉發之前或之后執行其他操作;
public interface Subject {
void operation();
}
public class RealSubject implements Subject {
@Override
public void operation() {
System.out.println("RealSubject is performing the operation");
}
}
public class ProxySubject implements Subject {
private final RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void operation() {
preOperation();
realSubject.operation();
postOperation();
}
private void preOperation() {
System.out.println("Executing pre-operation tasks in proxy");
}
private void postOperation() {
System.out.println("Executing post-operation tasks in proxy");
}
}
public class Main {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
// 使用代理執行操作,它將執行預/后操作并調用RealSubject的operation()
proxySubject.operation();
}
}
在實際應用中,代理模式可以被用于實現許多功能,例如安全性檢查、日志記錄、緩存和遠程訪問等.
4)了解Spring大家族
? Spring發展到今天已經形成了一種開發的生態圈,Spring提供了若干個項目,每個項目用于完成特定的功能;官網: Spring | Home
1.spring framework
? 也就是我們經常說的spring框架,主要分為IOC和AOP兩大塊;
2.spring boot
? 它的目標是簡化Spring應用和服務的創建、開發與部署,簡化了配置文件,使用嵌入式web服務器,含有諸多開箱即用的微服務功能,可以和spring cloud聯合部署。
? Spring Boot的核心思想是約定大于配置,應用只需要很少的配置即可,簡化了應用開發模式。
3.Spring Data
? 是一個數據訪問及操作的工具集,封裝了多種數據源的操作能力,包括:jdbc、Redis、MongoDB等
4.Spring Cloud
? 是一套完整的微服務解決方案,是一系列不同功能的微服務框架的集合
5. Spring Security
? 主要用于快速構建安全的應用程序和服務, 負責進行應用程序的認證, 授權;
5)spring framework組成
? spring框架分為兩大塊, IOC和AOP;
? spring框架包含一下幾大模塊:

1、 Core Container(核心容器)
? Beans模塊:提供了BeanFactory,是工廠模式的經典實現,Spring將管理對象稱為Bean。
? Core核心模塊:提供了Spring框架的基本組成部分,包括IoC和DI功能。
? Context上下文模塊:建立在Core和Beans模塊的基礎之上,它是訪問定義和配置的任何對象的媒介。
? SpEL模塊:它提供了Spring Expression Language支持,是運行時查詢和操作對象圖的強大的表達式語言
2、Data Access/Integration(數據訪問/集成)
? JDBC模塊:提供了一個JDBC的抽象層,大幅度地減少了在開發過程中對數據庫操作的編碼。
? ORM模塊:對流行的對象關系映射API,包括JPA、JDO和Hibernate提供了集成層支持。
? OXM模塊:提供了一個支持對象/ XML映射的抽象層實現,如JAXB、Castor、XMLBeans、JiBX和XStream。
? JMS 模塊:指 Java 消息傳遞服務,包含使用和產生信息的特性,自 4.1 版本后支持與Spring-message模塊的集成。
? Transactions事務模塊:支持對實現特殊接口以及所有POJO類的編程和聲明式的事務管理。
3、 Web
? WebSocket模塊:Spring 4.0以后新增的模塊,它提供了WebSocket 和SockJS的實現,以及對STOMP的支持。
? Servlet模塊:也稱為Spring-webmvc模塊,包含了Spring的模型—視圖—控制器(MVC)和REST Web Services實現的Web應用程序。
? Web模塊:提供了基本的Web開發集成特性,例如:多文件上傳功能、使用Servlet監聽器來初始化IoC容器以及Web應用上下文。
? Portlet模塊:提供了在Portlet環境中使用MVC實現,類似Servlet模塊的功能。
4、 其他模塊
? AOP 模塊:提供了面向切面編程實現,允許定義方法攔截器和切入點,將代碼按照功能進行分離,以降低耦合性。
? Aspects 模塊:提供了與AspectJ的集成功能,AspectJ是一個功能強大且成熟的面向切面編程(AOP)框架。
? Instrumentation 模塊:提供了類工具的支持和類加載器的實現,可以在特定的應用服務器中使用。
? Messaging模塊:Spring 4.0以后新增的模塊,它提供了對消息傳遞體系結構和協議的支持。
? Test模塊:提供了對單元測試和集成測試的支持
2 IoC和DI
1)IoC介紹和使用
? IoC(Inversion of Control)控制反轉, 它將對象之間的依賴關系與對象本身的創建和管理分離開來。這有助于降低程序中各個組件之間的耦合度,并提高代碼可維護性、可測試性以及擴展性。
? 在Spring IoC容器中,對象的生命周期由容器進行管理,而不是由對象自己管理。IoC容器負責實例化、配置和裝配Bean,然后將這些Bean注入到需要的組件中。使用IoC可以使您專注于業務邏輯的實現,而不必關心底層的對象創建和依賴解析工作。
? Spring IoC容器主要包括兩種類型:
? 1.BeanFactory:BeanFactory是一個簡單的容器,它負責實例化、定位、配置應用程序中的對象,并解決它們之間的依賴關系。XmlBeanFactory是最常用的BeanFactory實現。
? 2.ApplicationContext:ApplicationContext是BeanFactory的子接口,它為應用程序提供了更多的企業級特性,如消息資源處理、事件發布、聲明式事務處理等。通常,我們會使用ApplicationContext作為Spring IoC容器,常見的實現有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等
IOC簡單示例
導入jar;

public interface GreetingService {
void sayHello();
}
public class Student {
public void sayHello() {
System.out.println("Hello, Spring IoC!");
}
}
<?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">
<bean id="student" class="com.qs.spring.Student"></bean>
</beans>
public static void main(String[] args) {
// 加載Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 獲取Bean并使用sayHello()方法
Student student = (Student) context.getBean("student");
student.sayHello();
}
bean的作用范圍
<bean class="com.qs.spring.entity.Student" scope="prototype">
<property name="sid" value="1001"></property>
<property name="sname" value="張三"></property>
<property name="gender" value="男"></property>
</bean>
當scope為prototype, 是多例;為singleton或者默認都是單例;
bean的生命周期
- 實例化:Spring根據配置文件或注解定義創建Bean實例。
- 屬性填充:Spring通過依賴注入將指定的屬性值和引用設置到Bean的屬性中,這可以是基于構造方法、setter方法或字段的注入。
- 容器回調(Aware接口):如果Bean實現了某些特定的
Aware接口(如BeanNameAware、BeanFactoryAware等),Spring會自動調用相應的回調方法,將容器相關的信息傳遞給Bean。 - BeanPostProcessor前置處理:Spring會調用已注冊的所有
BeanPostProcessor接口實現類的postProcessBeforeInitialization方法,允許您進行自定義處理,例如修改Bean屬性或改變Bean引用。 - 初始化:當Bean的所有屬性都設置好后,Spring會檢查Bean是否實現了
InitializingBean接口,如果實現了該接口,Spring會調用afterPropertiesSet()方法。此外,如果Bean定義中包含init-method屬性(XML配置)或@PostConstruct注解(注解配置),也會執行對應的初始化方法。 - BeanPostProcessor后置處理:Spring會調用已注冊的所有
BeanPostProcessor接口實現類的postProcessAfterInitialization方法,同樣允許您進行自定義處理。 - Bean準備就緒:至此,Bean已經準備好了,可以被應用程序使用。
- 銷毀:當容器關閉時,Spring會檢查Bean是否實現了
DisposableBean接口,如果實現了該接口,Spring會調用destroy()方法。此外,如果Bean定義中包含destroy-method屬性(XML配置)或@PreDestroy注解(注解配置),也會執行對應的銷毀方法。
注意:在整個生命周期中,BeanPostProcessor前置/后置處理和Aware接口回調等特性可以通過編程方式自定義,以滿足具體需求。
2)DI的介紹和使用
? 依賴注入(Dependency Injection,DI)是Spring框架的核心概念之一。它是一種將組件之間的依賴關系從組件內部移除的設計模式,使得組件之間的耦合度降低,代碼更加靈活和可維護.
? 在Spring框架中,DI通過控制反轉(Inversion of Control,IoC)容器實現。IoC容器負責管理應用程序中的對象(稱為Bean)以及它們之間的依賴關系。簡單來說,DI允許我們將對象的創建和配置委托給IoC容器,并在需要時將這些對象注入其它對象.
? Spring提供了以下幾種主要的依賴注入方式:
- 基于構造函數的注入:通過構造函數將依賴項傳遞給需要它們的類。在XML配置中,使用
<constructor-arg>元素實現;而在基于注解的配置中,可以在構造函數上添加@Autowired注解。 - 基于Setter方法的注入:通過調用setter方法將依賴項傳遞給需要它們的類。在XML配置中,使用
<property>元素實現;而在基于注解的配置中,可以在setter方法上添加@Autowired注解。 - 基于字段的注入:直接向類的字段注入依賴項,而無需使用構造函數或setter方法。在基于注解的配置中,可以在字段上添加
@Autowired注解。
基于構造函數的注入:
? 導入jar;
? 
?
? 需要進行注入的實體類;
public class Student {
private Integer sid;
private String sname;
private String gender;
public Student(Integer sid, String sname, String gender) {
this.sid = sid;
this.sname = sname;
this.gender = gender;
}
}
? xml文件中構造注入:
<bean class="com.qs.spring.entity.Student" id="student">
<constructor-arg name="gender" value="男"></constructor-arg>
<constructor-arg name="sid" value="1001"></constructor-arg>
<constructor-arg name="sname" value="張三"></constructor-arg>
</bean>
?
基于Setter方法的注入:
public class Student {
private Integer sid;
private String sname;
private String gender;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
<bean class="com.qs.spring.entity.Student">
<property name="sid" value="1001"></property>
<property name="sname" value="張三"></property>
<property name="gender" value="男"></property>
</bean>
? 屬性值為對象的注入:
public class Student {
...
...
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
...
...
}
<bean class="com.qs.spring.entity.Student">
<property name="sid" value="1001"></property>
<property name="sname" value="張三"></property>
<property name="gender" value="男"></property>
<property name="clazz" ref="clazz"></property>
</bean>
<bean class="com.qs.spring.entity.Clazz" id="clazz">
<property name="cid" value="1001"></property>
<property name="cname" value="DT117"></property>
</bean>
? 自動注入: 當需要注入的值是對象并且IOC容器中也存在該對象時, 可以選擇自動注入: autowire;
byType: 根據類型自動注入;
byName: 根據id的值和屬性名稱自動注入;
<bean class="com.qs.spring.entity.Student" autowire="byType">
<property name="sid" value="1001"></property>
<property name="sname" value="張三"></property>
<property name="gender" value="男"></property>
</bean>
<bean class="com.qs.spring.entity.Clazz" id="clazz">
<property name="cid" value="1001"></property>
<property name="cname" value="DT117"></property>
</bean>
? 屬性值為數組, 集合等注入:
public class Student {
...
...
private List<Integer> list;
private Set<Integer> set;
private Map<String,String> map;
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public Set<Integer> getSet() {
return set;
}
public void setSet(Set<Integer> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
...
...
}
<bean class="com.qs.spring.entity.Student" autowire="byName">
<property name="sid" value="1001"></property>
<property name="sname" value="張三"></property>
<property name="gender" value="男"></property>
<property name="list">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
</list>
</property>
<property name="set">
<set>
<value>444</value>
<value>555</value>
<value>666</value>
</set>
</property>
<property name="map">
<map>
<entry key="aaa" value="123"></entry>
<entry key="bbb" value="111"></entry>
<entry key="ccc" value="321"></entry>
</map>
</property>
</bean>
基于字段的注入
? 直接在需要注入的屬性上添加注解實現;
@Autowired
private Clazz clazz;
? @Autowired: 在IoC容器中根據類型查找, 然后注入到指定的屬性中;
? @Resource: 該注解并非Spring注解, 而是java中的注解,作用和 @Autowired類似, 是在根據名稱進行注入;
?
3 Aop
? Spring AOP(Aspect-Oriented Programming,面向切面編程)是Spring框架的一個重要特性,它允許將與業務邏輯無關的橫切關注點(如日志記錄、安全檢查、事務管理等)從應用程序代碼中分離出來。這有助于提高代碼的模塊化程度,使得應用程序更加易于維護和擴展。
? 在Spring AOP中,主要涉及以下幾個核心概念:
- 切面(Aspect):封裝了橫切關注點的模塊化代碼,通常包含一個或多個通知(Advice)以及切入點(Pointcut)的定義。
- 通知(Advice):切面在特定連接點執行的操作。根據執行時機不同,通知可以分為前置通知(Before)、后置通知(After)、返回通知(After-returning)、異常通知(After-throwing)和環繞通知(Around)。
- 切入點(Pointcut):描述了通知應該織入的一系列連接點。切入點通過類和方法簽名的模式匹配定義,例如匹配特定包下所有類的所有方法。
- 連接點(Join point):程序執行過程中可以插入切面操作的點,例如方法調用、構造函數調用、字段賦值等。Spring AOP僅支持方法執行連接點。
- 引入(Introduction):允許為現有類添加新方法或屬性的一種方式。
- 織入(Weaving):將切面與目標對象組合在一起,創建代理對象的過程。織入可以在編譯時、類加載時或運行時完成。Spring AOP默認使用運行時織入。
?
1)使用配置文件實現:
? 導入jar;
? 
? 創建業務類接口:
public interface GoodsService {
void sayHello(String name);
}
? 實現接口:
public class GoodsServiceImpl implements GoodsService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
? 創建增強處理的類和方法:
public class myLogger {
//JoinPoint 可以獲取到業務方法的相關的信息
//前置通知
public void beforeLogging(JoinPoint point){
System.out.println("------------------------------進入前置通知---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println(".------------------------------離開前置通知---------------------");
}
//后置通知
public void afterLogging(JoinPoint point,Object result){
System.out.println("------------------------------進入后置通知---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println("---------------------返回值:"+result);
System.out.println(".------------------------------離開后置通知---------------------");
}
//環繞通知
public Object aroundLogging(ProceedingJoinPoint point) throws Throwable {
System.out.println("------------------------------進入環繞通知---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
//手動執行業務方法 并且接收返回值
Object result = point.proceed();
System.out.println("---------------------返回值:"+result);
System.out.println(".------------------------------離開環繞通知---------------------");
return result;
}
//異常通知
public void execptionLogging(JoinPoint point,Exception e){
System.out.println("------------------------------進入異常通知---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println("---------------------發生了異常:"+e.getMessage());
System.out.println(".------------------------------離開異常通知---------------------");
}
//最終通知
public void finallyLogging(JoinPoint point){
System.out.println("------------------------------進入最終通知---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println(".------------------------------離開最終通知---------------------");
}
}
<bean class="com.qs.spring.service.GoodsService"></bean>
<bean class="com.qs.spring.aop.MyLogger" id="myLogger"></bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.qs.spring.service.*.*(..))"/>
<aop:aspect ref="myLogger">
<aop:before method="beforeLogging" pointcut-ref="pt"></aop:before>
<aop:after-returning method="afterLogging" pointcut-ref="pt" returning="result"></aop:after-returning>
<aop:around method="aroundLogging" pointcut-ref="pt"></aop:around>
<aop:after-throwing method="execptionLogging" pointcut-ref="pt" throwing="e"></aop:after-throwing>
<aop:after method="finallyLogging" pointcut-ref="pt"></aop:after>
</aop:aspect>
</aop:config>
2)使用注解實現:
? 1) 給創建增強處理的類和方法加上指定的注解:
? @Aspect: 用在增加處理類上,聲明該類為aop的一個切面類;
? @Pointcut("execution(* cn.guotu.service..(..))"), 用在方法上, 指定一個切入點
? @Before(), 用在方法上, 代表該方法是一個前置增強方法
? @Around(), 用在方法上, 代表該方法是一個環繞增強方法
? @AfterReturning(), 用在方法上, 代表該方法是一個后置增強方法
? @AfterThrowing(), 用在方法上, 代表該方法是一個異常增強方法
? @After(), 用在方法上, 代表該方法是一個異常增強方法
@Aspect
public class MyLoggerAspacj {
//切入點
@Pointcut("execution(* cn.guotu.service.*.*(..))")
public void pt(){
}
@Before("pt()")
public void beforeLogging(JoinPoint point){
System.out.println("------------------------------進入前置增強---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println(".------------------------------離開前置增強---------------------");
}
//環繞增強
@Around("pt()")
public Object aroundLogging(ProceedingJoinPoint point) throws Throwable {
System.out.println("------------------------------進入環繞增強---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
//手動執行業務方法 并且接收返回值
Object result = point.proceed();
System.out.println("---------------------返回值:"+result);
System.out.println(".------------------------------離開環繞增強---------------------");
return result;
}
@AfterReturning(pointcut = "pt()",returning="result")
public void afterLogging(JoinPoint point,Object result){
System.out.println("------------------------------進入后置增強---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println("---------------------返回值:"+result);
System.out.println(".------------------------------離開后置增強---------------------");
}
//異常增強
@AfterThrowing(pointcut = "pt()",throwing = "e")
public void execptionLogging(JoinPoint point,Exception e){
System.out.println("------------------------------進入異常增強---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println("---------------------發生了異常:"+e.getMessage());
System.out.println(".------------------------------離開異常增強---------------------");
}
@After("pt()")
public void finallyLogging(JoinPoint point){
System.out.println("------------------------------進入最終增強---------------------");
System.out.println("---------------------進入了類:"+point.getTarget().getClass().getName());
System.out.println("---------------------進入了方法:"+point.getSignature().getName());
System.out.println("---------------------傳入了參數:"+point.getArgs()[0]);
System.out.println(".------------------------------離開最終增強---------------------");
}
}
<bean class="com.qs.spring.service.GoodsService"></bean>
<bean class="com.qs.spring.aop.MyLogger" id="myLogger"></bean>
<!--打開aop的注解掃描-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3 spring整合mybatis
? 1)添加jar

? 2) 創建配置文件:

datasources.properties
url=jdbc:mysql://127.0.0.1:3306/goods?serverTimezone=Asia/Shanghai
driver=com.mysql.cj.jdbc.Driver
user=root
password=root
mybatis-config.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
spring-dao.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!--加載db.properties-->
<context:property-placeholder location="db.properties"/>
<!--配置連接環境-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="password" value="${password}"></property>
<property name="username" value="${user}"></property>
<property name="url" value="${url}"></property>
<property name="driverClassName" value="${driver}"></property>
</bean>
<!-- sqlsessionfactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="mybatis-config.xml"></property>
</bean>
<!--配置mapper掃描的包-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.guotu.mapper"></property>
</bean>
</beans>
spting-service.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!--開啟service包的掃描-->
<context:component-scan base-package="cn.guotu.service"></context:component-scan>
</beans>
實體類: Goods.java(略)
Mapper接口: GoodsMapper.java(略)
服務層: GoodsService.java(略)
4 spring事務管理
1)基于配置文件的事務
當在service的業務方法中, 對數據庫有1個以上的操作時, 如果方法內任何一個地方出現異常, 那么對數據的所有操作都需要回滾, 要么全部都執行成功, 要么都執行后失敗!
spring-dao.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!--開啟service包的掃描-->
<context:component-scan base-package="cn.guotu.service"></context:component-scan>
<!-- 配置事務管理器 (增強處理類) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置哪些方法需要進行事務管理 (切入點)-->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="change*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* cn.guotu.service.*.*(..))"/>
<aop:advisor pointcut-ref="pt" advice-ref="advice"></aop:advisor>
</aop:config>
</beans>
GoodsService.java
public void insert(){
Goods goods = new Goods();
goods.setGname("國土1");
goods.setGcount(100);
goods.setGprice(5.6);
goods.setCreateTime(new Date());
goods.setTid(1);
int i = goodsMapper.insert(goods);
int a = 10/0;
Goods goods1 = new Goods();
goods1.setGname("國土2");
goods1.setGcount(100);
goods1.setGprice(5.6);
goods1.setCreateTime(new Date());
goods1.setTid(1);
int i1 = goodsMapper.insert(goods1);
System.out.println(i1);
}
2)基于注解實現事務
spring-dao.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!--開啟service包的掃描-->
<context:component-scan base-package="cn.guotu.service"></context:component-scan>
<!-- 配置事務管理器 (增強處理類) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 開始事務的注解掃描 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
GoodsService.java
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE)
public void insert(){
Goods goods = new Goods();
goods.setGname("國土1");
goods.setGcount(100);
goods.setGprice(5.6);
goods.setCreateTime(new Date());
goods.setTid(1);
int i = goodsMapper.insert(goods);
int a = 10/0;
Goods goods1 = new Goods();
goods1.setGname("國土2");
goods1.setGcount(100);
goods1.setGprice(5.6);
goods1.setCreateTime(new Date());
goods1.setTid(1);
int i1 = goodsMapper.insert(goods1);
System.out.println(i1);
}
3)事務的傳播機制
? Propagation:
1.REQUIRED
如果當前沒有事務,就創建一個新事務,如果當前存在事務,就加入該事務,該設置是最常用的默認設置。

調用methdoA,如果methodB發生異常,觸發事務回滾,也會methodA中的也會回滾
2.SUPPORTS
支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。

如果調用methodA,再調用methodB,MehtodB會加入到MethodA的開啟的當前事務中。 如果直接調用methodB,當前沒有事務,就以非事務執行。
3.MNDATORY
支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就拋出異常。

如果調用methodA,再調用methodB,MehtodB會加入到MethodA的開啟的當前事務中。 如果直接調用methodB,當前沒有事務,就會拋出異常。
4.REQUIRES_NEW
創建新事務,無論當前存不存在事務,都創建新事務。

調用methodA,會先開啟事務1,執行A的something pre的代碼。再調用methodB,methdoB會開啟一個事務2,再執行自身的代碼。最后在執行methodA的something post。如果method發生異常回滾,只是methodB中的代碼回滾,不影響methodA中的代碼。如果methodA發生異常回滾,只回滾methodA中的代碼,不影響methodB中的代碼。
5.NOT_SUPPORTED
以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

調用methodA,再調用methodB,methodA開啟的事務會被掛起,即在methodB中不齊作用,相當于沒有事務,methodB內部拋出異常不會回滾。methodA內的代碼發生異常會回滾。 直接調用methodB,不會開啟事務。
6.NEVER
以非事務方式執行操作,如果當前存在事務,則拋出異常。

?
7.NESTED
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則按REQUIRED屬性執行。

調用methodA,開啟一個事務,執行something pre的代碼,設置回滾點savepoint,再調用methodB的代碼,如果methodB里拋出異常,此時回滾到之前的saveponint。再然后執行methodA里的something post的代碼,最后提交或者回滾事務。 嵌套事務,外層的事務如果回滾,會導致內層的事務也回滾;但是內層的事務如果回滾,僅僅是回滾自己的代碼,不影響外層的事務的代碼。
4)事務的隔離級別
? 1) 讀未提交(臟讀) (不加任何鎖)
? 當一個事務開啟后, 修改了數據, 但是未提交事務之前, 另外一個事務來讀取數據, 可以讀到未提交的數據;
? 由于讀的數據是未提交的, 那么萬一事務回滾, 數據就讀到的是錯的; 會產生臟數據;
? 2)讀提交(不可重復讀) (寫鎖(部分行))
? 如果是一個讀事務(線程),則允許其他事務讀寫,如果是寫事務將會禁止其他事務訪問該行數據,該隔離級別避免了臟讀,但是可能出現不可重復讀。事務A事先讀取了數據,事務B緊接著更新了數據,并提交了事務,而事務A再次讀取該數據時,數據已經發生了改變
? 3)可重復讀(讀寫鎖(部分行))
? 可重復讀取是指在一個事務內,多次讀同一個數據,在這個事務還沒結束時,其他事務不能訪問該數據(包括了讀寫),這樣就可以在同一個事務內兩次讀到的數據是一樣的,因此稱為是可重復讀隔離級別;
? 讀取數據的事務將會禁止寫事務,寫事務則禁止任何其他事務(包括了讀寫),這樣避免了不可重復讀和臟讀,但是有時可能會出現幻讀。
? 4)串行話(讀寫鎖(表))
這是最高的隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上鎖。在這個級別,可能導致大量的超時現象和鎖競爭;
| 事務隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
|---|---|---|---|
| 讀未提交 (read-uncommitted) | 是 | 是 | 是 |
| 讀提交(read-committed) | 否 | 是 | 是 |
| 可重復讀(repeatable-read) | 否 | 否 | 是 |
| 串行化(serializable) | 否 | 否 | 否 |
5 其余常用注解
- @Component:將類標記為Spring管理的Bean。@Service、@Repository和@Controller都是特定類型的@Component注解,分別表示服務層、數據訪問層和控制層組件。
- @Autowired:用于自動裝配Bean之間的依賴關系。可以用于構造函數、setter方法或字段上。從Spring 4.3開始,如果只有一個構造函數,可以省略@Autowired。
- @Qualifier:當有多個相同類型的Bean時,使用@Qualifier指定特定的Bean進行注入。通常與@Autowired一起使用。
- @Value:用于向Bean的屬性注入基本類型或String類型的值,支持SpEL表達式。
- @Scope:定義Bean的作用域,如singleton(默認)、prototype、request、session和global session。
- @PostConstruct:標記一個方法作為初始化方法,該方法會在Bean實例化并設置完所有屬性后調用。
- @PreDestroy:標記一個方法作為銷毀方法,在容器關閉前調用。
- @Configuration:將類標記為Spring Java配置類,用于定義和注冊Bean。
- @Bean:用于@Configuration類中的方法上,聲明一個Bean。方法返回的對象將由Spring容器管理。
- @ComponentScan:用于Java配置類上,聲明啟用組件掃描。可以指定掃描的基礎包和過濾條件。

浙公網安備 33010602011771號