中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

做網(wǎng)站布局流程小程序推廣賺傭金平臺(tái)

做網(wǎng)站布局流程,小程序推廣賺傭金平臺(tái),金華網(wǎng)站建設(shè)黃頁,網(wǎng)店怎么開起來文章目錄 1. Spring 是什么?2. IoC3. Spring Demo4. IoC 創(chuàng)建對(duì)象的方式 / DI 方式注入的默認(rèn)參數(shù)在哪里設(shè)定? 5. Spring 配置tx:annotation-driven 用于啟用基于注解的事務(wù)管理 6. Bean的作用域7. 在Spring中有三種自動(dòng)裝配的方式1. 在xml中顯式的配置2. 在java中…

文章目錄

  • 1. Spring 是什么?
  • 2. IoC
  • 3. Spring Demo
  • 4. IoC 創(chuàng)建對(duì)象的方式 / DI 方式
    • 注入的默認(rèn)參數(shù)在哪里設(shè)定?
  • 5. Spring 配置
    • tx:annotation-driven 用于啟用基于注解的事務(wù)管理
  • 6. Bean的作用域
  • 7. 在Spring中有三種自動(dòng)裝配的方式
    • 1. 在xml中顯式的配置
    • 2. 在java中顯式配置
    • 3. 隱式的自動(dòng)裝配bean?!局匾?#xff01;】
      • byName 自動(dòng)裝配
      • byType 自動(dòng)裝配
  • 8. 注解
  • 9. applicationContext.xml與beans.xml有什么區(qū)別
  • 10. Java Config
  • 11. 橫向開發(fā) VS 縱向開發(fā)
  • 12. 代理模式
  • 13. 靜態(tài)代理 VS 動(dòng)態(tài)代理
    • 靜態(tài)代理
    • 動(dòng)態(tài)代理
      • InvocationHandler 與 Proxy
      • 基于接口——JDK動(dòng)態(tài)代理
      • 基于類——CGLIB動(dòng)態(tài)代理
  • 14. AOP
    • 核心概念
    • 實(shí)現(xiàn)
      • 1. 基于 xml 配置類實(shí)現(xiàn)
      • 2. 自定義類
      • 3. 使用注解實(shí)現(xiàn)
  • 15. 靜態(tài)資源過濾問題
  • 16. Spring 整合Mybatis
  • 17. 聲明式事務(wù)
  • 18. spring事務(wù)傳播特性
  • 參考

1. Spring 是什么?

Spring就是一個(gè)輕量級(jí)的控制反轉(zhuǎn)(IOC)和面向切面編程(AOP)的框架。

spring overview

在這里插入圖片描述

在這里插入圖片描述

Spring 框架的核心特點(diǎn)包括:

  1. 依賴注入(Dependency Injection,DI):Spring 提供了一個(gè)容器,負(fù)責(zé)管理應(yīng)用程序中的對(duì)象(bean)以及它們之間的依賴關(guān)系。通過依賴注入,開發(fā)人員可以將對(duì)象的依賴關(guān)系從代碼中解耦,提高了代碼的靈活性和可維護(hù)性。

  2. 面向切面編程(Aspect-Oriented Programming,AOP):Spring 支持 AOP,允許開發(fā)人員在應(yīng)用程序中聲明性地定義橫切關(guān)注點(diǎn)(cross-cutting concerns),如日志記錄、事務(wù)管理等,并將它們與核心業(yè)務(wù)邏輯分開,提高了代碼的模塊化和可維護(hù)性。

  3. 模塊化:Spring 框架被組織為多個(gè)模塊,每個(gè)模塊都提供不同的功能,如核心容器、數(shù)據(jù)訪問、Web 開發(fā)、安全性等。開發(fā)人員可以根據(jù)需要選擇并使用這些模塊,使得 Spring 框架具有高度的可定制性和靈活性。

  4. 聲明式事務(wù)管理:Spring 提供了聲明式事務(wù)管理的支持,開發(fā)人員可以通過配置簡單的元數(shù)據(jù)來管理事務(wù),而無需編寫復(fù)雜的事務(wù)管理代碼。

  5. 面向接口編程:Spring 鼓勵(lì)開發(fā)人員使用接口來編程,而不是直接使用實(shí)現(xiàn)類。這種面向接口的設(shè)計(jì)使得代碼更易于擴(kuò)展和維護(hù),并且使得單元測試更加容易進(jìn)行。

  6. 簡化企業(yè)級(jí)開發(fā):Spring 提供了許多工具和技術(shù),如 Spring MVC、Spring Boot、Spring Data 等,可以幫助開發(fā)人員快速地構(gòu)建企業(yè)級(jí)應(yīng)用程序,并且減少了開發(fā)過程中的樣板代碼。

2. IoC

在這里插入圖片描述

在這里插入圖片描述

  • 控制反轉(zhuǎn)loC(Inversion of Control),是一種設(shè)計(jì)思想,DI(依賴注入)是實(shí)現(xiàn)loC的一種方法

  • 控制反轉(zhuǎn)是一種通過描述(XML或注解)并通過第三方去生產(chǎn)或獲取特定對(duì)象的方式。在Spring中實(shí)現(xiàn)控制反轉(zhuǎn)的是IoC容器,其實(shí)現(xiàn)方法是依賴注入(Dependency Injection,DI)

  • 對(duì)象由Spring 來創(chuàng)建 , 管理 , 裝配 !

控制反轉(zhuǎn)(IoC)是一種軟件設(shè)計(jì)原則,它反轉(zhuǎn)了傳統(tǒng)應(yīng)用程序中對(duì)象的創(chuàng)建和管理流程。在傳統(tǒng)的應(yīng)用程序中,對(duì)象之間的依賴關(guān)系通常由對(duì)象自身來管理或硬編碼。但在IoC容器中,對(duì)象的創(chuàng)建和管理由容器來負(fù)責(zé),它會(huì)自動(dòng)地將依賴關(guān)系注入到對(duì)象中。這種反轉(zhuǎn)的控制權(quán)使得對(duì)象之間的耦合度降低,更容易進(jìn)行測試、維護(hù)和擴(kuò)展。

在Spring框架中,IoC容器負(fù)責(zé)管理應(yīng)用程序中的組件,例如Java類和對(duì)象。Spring的IoC容器利用依賴注入(DI)的機(jī)制來實(shí)現(xiàn)控制反轉(zhuǎn)。通過依賴注入,容器會(huì)在對(duì)象被創(chuàng)建的時(shí)候自動(dòng)將其依賴的其他對(duì)象注入進(jìn)來,從而實(shí)現(xiàn)了解耦和松散耦合的目標(biāo)。

3. Spring Demo

UserService.java

public class UserService {public String getUserInfo() {return "User information from UserService";}
}

UserDao.java

public class UserDAO {public String getUserData() {return "User data from UserDAO";}
}

UserController.java 依賴注入

public class UserController {private UserService userService;private UserDAO userDAO;// 通過構(gòu)造函數(shù)注入 UserService 和 UserDAOpublic UserController(UserService userService, UserDAO userDAO) {this.userService = userService;this.userDAO = userDAO;}public String getUserInfo() {String userInfo = userService.getUserInfo();String userData = userDAO.getUserData();return "User Info: " + userInfo + ", User Data: " + userData;}
}

applicationContext.xml 配置 Spring 容器以管理這些組件之間的依賴關(guān)系

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置 UserService --><bean id="userService" class="com.example.UserService" /><!-- 配置 UserDAO --><bean id="userDAO" class="com.example.UserDAO" /><!-- 配置 UserController,并注入 UserService 和 UserDAO --><bean id="userController" class="com.example.UserController"><constructor-arg ref="userService" /><constructor-arg ref="userDAO" /></bean></beans>

applicationContext.xml / beans.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 第一種:根據(jù)index參數(shù)下標(biāo)設(shè)置 -->
<!--    <bean id="userT" class="com.github.subei.pojo.UserT">-->
<!--        <constructor-arg index="0" value="subeily——"/>-->
<!--    </bean>--><!-- 第二種:根據(jù)參數(shù)類型設(shè)置,不建議使用 -->
<!--    <bean id="userT" class="com.github.subei.pojo.UserT">-->
<!--        <constructor-arg type="java.lang.String" value="subeily2——"/>-->
<!--    </bean>--><!-- 第三種:根據(jù)參數(shù)名字設(shè)置 --><bean id="userT" class="com.github.subei.pojo.UserT"><!-- name指參數(shù)名 --><constructor-arg name="name" value="subeily3——"/></bean></beans>

MainApplication

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MainApp {public static void main(String[] args) {// 加載 Spring 容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 獲取 UserController beanUserController userController = (UserController) context.getBean("userController");// 調(diào)用 getUserInfo 方法String userInfo = userController.getUserInfo();System.out.println(userInfo);}
}

4. IoC 創(chuàng)建對(duì)象的方式 / DI 方式

  1. 構(gòu)造函數(shù)注入(Constructor Injection)
    在構(gòu)造函數(shù)注入中,依賴關(guān)系通過構(gòu)造函數(shù)來注入。IoC容器會(huì)在創(chuàng)建對(duì)象時(shí)調(diào)用其構(gòu)造函數(shù),并將依賴的對(duì)象作為參數(shù)傳遞給構(gòu)造函數(shù)。這樣,對(duì)象在創(chuàng)建時(shí)就能夠獲得它所需要的依賴對(duì)象。
public class MyClass {private MyDependency dependency;public MyClass(MyDependency dependency) {this.dependency = dependency;}
}
  1. Setter方法注入(Setter Injection)
    在Setter方法注入中,依賴關(guān)系通過setter方法來注入。IoC容器會(huì)先創(chuàng)建對(duì)象,然后調(diào)用對(duì)象的setter方法來設(shè)置依賴對(duì)象。這種方式可以使得依賴對(duì)象是可選的,因?yàn)槿绻麤]有設(shè)置相應(yīng)的依賴,對(duì)象也能夠被創(chuàng)建。
public class MyClass {private MyDependency dependency;public void setDependency(MyDependency dependency) {this.dependency = dependency;}
}

或:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="address" class="com.github.subei.pojo.Address"><property name="address" value="成都"/></bean><bean id="student" class="com.github.subei.pojo.Student"><!-- 第一種:普通值注入,value --><property name="name" value="subei"/><!-- 第二種:Bean注入,ref --><property name="address" ref="address"/><!-- 第三種:數(shù)組注入 --><property name="books"><array><value>Mybatis</value><value>Spring</value><value>SpringMVC</value></array></property><!-- 第四種:list注入 --><property name="hobby"><list><value>家里蹲</value><value>精神萎靡</value><value>無法溝通</value></list></property><!-- 第五種:Map注入 --><property name="card"><map><entry key="學(xué)生證" value="20201014"/><entry key="身份證" value="14253686"/></map></property><!-- 第六種:set注入 --><property name="games"><set><value>保衛(wèi)蘿卜1</value><value>保衛(wèi)蘿卜2</value><value>保衛(wèi)蘿卜3</value></set></property><!-- 第七種:null注入 --><property name="wife"><null/></property><!-- 第八種:Properties注入 --><property name="info"><props><prop key="學(xué)號(hào)">20210106</prop><prop key="性別">保密</prop><prop key="姓名">subei</prop></props></property></bean></beans>
3. **接口注入(Interface Injection)**:
在接口注入中,對(duì)象實(shí)現(xiàn)一個(gè)特定的接口,該接口包含一個(gè)方法用于注入依賴。IoC容器通過調(diào)用這個(gè)方法來注入依賴對(duì)象。```java
public interface Injectable {void injectDependency(MyDependency dependency);
}public class MyClass implements Injectable {private MyDependency dependency;@Overridepublic void injectDependency(MyDependency dependency) {this.dependency = dependency;}
}
  1. 注解(Annotation)
    使用注解方式可以更加簡潔地指定依賴注入的方式。在Java中,常用的注解包括@Autowired@Inject等。通過在需要注入的字段或方法上添加注解,IoC容器會(huì)自動(dòng)進(jìn)行依賴注入。
public class MyClass {@Autowiredprivate MyDependency dependency;
}
  1. 拓展方式注入

在Spring Framework中,除了使用常規(guī)的構(gòu)造函數(shù)注入和Setter方法注入外,還可以使用XML配置文件中的p:命名空間和c:命名空間進(jìn)行屬性注入。

  1. p:命名空間
- `p:`命名空間用于簡化在XML配置文件中為Bean屬性進(jìn)行賦值。它允許您在Bean定義中**直接設(shè)置屬性值,而無需顯式調(diào)用相應(yīng)的Setter方法**。- 例如,假設(shè)有一個(gè)名為`Person`的Bean,具有`name`和`age`屬性,您可以使用`p:`命名空間進(jìn)行注入,如下所示:```xml<bean id="person" class="com.example.Person" p:name="John" p:age="30" />```
  1. c:命名空間
- `c:`命名空間用于在XML配置文件中為**構(gòu)造函數(shù)參數(shù)進(jìn)行注入(有參構(gòu)造函數(shù))**。它允許您在Bean定義中直接設(shè)置構(gòu)造函數(shù)參數(shù)值。- 例如,假設(shè)有一個(gè)名為`Person`的Bean,構(gòu)造函數(shù)需要`name`和`age`參數(shù),您可以使用`c:`命名空間進(jìn)行注入,如下所示:```xml<bean id="person" class="com.example.Person" c:_0="John" c:_1="30" />```- 這里`c:_0`表示構(gòu)造函數(shù)的第一個(gè)參數(shù),`c:_1`表示構(gòu)造函數(shù)的第二個(gè)參數(shù),以此類推。

注入的默認(rèn)參數(shù)在哪里設(shè)定?

  1. 通過配置文件
    許多IoC容器允許使用配置文件(如XML、JSON、YAML等)來指定默認(rèn)參數(shù)。在配置文件中,可以為特定的類或?qū)ο笾付J(rèn)參數(shù)值。當(dāng)IoC容器創(chuàng)建對(duì)象時(shí),如果沒有顯式指定參數(shù)值,它將會(huì)使用配置文件中的默認(rèn)值。

例如,在Spring Framework中,可以使用XML配置文件或者基于Java的配置類來指定默認(rèn)參數(shù)值:

<bean id="myBean" class="com.example.MyClass"><constructor-arg value="defaultParameterValue" />
</bean>
  1. 通過注解
    有些IoC容器支持使用注解來指定默認(rèn)參數(shù)。通過在類或方法上添加特定的注解,可以指定默認(rèn)參數(shù)值。

例如,在Spring Framework中,可以使用@Value注解來指定默認(rèn)參數(shù)值:

@Component
public class MyClass {@Value("${my.property:defaultValue}")private String myProperty;
}

在這個(gè)例子中,如果在配置文件中沒有指定my.property的值,則默認(rèn)使用defaultValue。

  1. 通過代碼配置
    一些IoC容器支持通過編程方式來指定默認(rèn)參數(shù)值。開發(fā)者可以在代碼中顯式地為對(duì)象設(shè)置默認(rèn)參數(shù)值。

例如,在Spring Framework中,可以使用Java配置類來指定默認(rèn)參數(shù)值:

@Configuration
public class AppConfig {@Beanpublic MyClass myClass() {MyClass myClass = new MyClass();myClass.setDefaultParameterValue("defaultValue");return myClass;}
}

在這個(gè)例子中,MyClass對(duì)象的默認(rèn)參數(shù)值被顯式地設(shè)置為defaultValue

5. Spring 配置

在Spring中,配置文件通常是XML格式的,可以使用一系列元素來定義和配置應(yīng)用程序的組件。以下是一些常見的配置元素:

  1. <bean>元素<bean>元素用于定義Spring容器中的bean。它包括屬性如id(用于唯一標(biāo)識(shí)bean)、class(指定bean的類)、scope(指定bean的作用域)、init-method(指定bean初始化時(shí)調(diào)用的方法)、destroy-method(指定bean銷毀時(shí)調(diào)用的方法)等。例如:
<bean id="myBean" class="com.example.MyClass" scope="singleton"><property name="propertyName" value="propertyValue" />
</bean>
  1. <alias>元素<alias> 元素用于為現(xiàn)有的 bean 創(chuàng)建一個(gè)別名。這對(duì)于簡化配置或者提供更有意義的名稱很有用。例如:
<alias name="myBean" alias="myAlias" />
  1. <import>元素<import> 元素用于將其他 XML 配置文件導(dǎo)入到當(dāng)前的配置文件中。這使得配置文件可以分解為更小的模塊,更易于管理和維護(hù)。例如:
<import resource="classpath:other-config.xml" />
  1. 命名空間元素
    Spring提供了一系列命名空間,用于簡化配置文件。例如,<context:component-scan>用于掃描指定包下的類,并將其注冊為bean。另一個(gè)例子是<tx:annotation-driven>用于啟用基于注解的事務(wù)管理。例如:
<context:component-scan base-package="com.example" /> 
<tx:annotation-driven />
  1. 注釋元素
    Spring還支持使用注釋來配置bean,包括@Component、@Autowired、@Bean等。這種方式是基于Java的配置方式的一部分,通過在類上添加注釋來告訴Spring容器如何處理這些類。
@Component
public class MyClass {// class definition
}

以上是Spring中常用的配置元素和方式。開發(fā)者可以根據(jù)項(xiàng)目的需要選擇適合的配置方式。

tx:annotation-driven 用于啟用基于注解的事務(wù)管理

<tx:annotation-driven> 是 Spring 中的一個(gè) XML 配置元素,用于啟用基于注解的事務(wù)管理支持。通過在 Spring 配置文件中包含該元素,Spring 容器會(huì)自動(dòng)檢測被 @Transactional 注解標(biāo)記的方法,并在執(zhí)行這些方法時(shí)提供事務(wù)管理支持。

具體來說,<tx:annotation-driven> 主要做了以下幾件事情:

  1. 注冊 TransactionInterceptor bean<tx:annotation-driven> 在 Spring 容器中注冊一個(gè) TransactionInterceptor bean,用于處理 @Transactional 注解。

  2. 啟用事務(wù)注解驅(qū)動(dòng):它告訴 Spring 啟用基于注解的事務(wù)管理支持。

  3. 配置事務(wù)管理器:它會(huì)自動(dòng)檢測當(dāng)前 Spring 上下文中的事務(wù)管理器(例如 DataSourceTransactionManagerJpaTransactionManager),并將其應(yīng)用于被 @Transactional 注解標(biāo)記的方法。

示例用法如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 配置數(shù)據(jù)源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><!-- 數(shù)據(jù)源配置 --></bean><!-- 配置事務(wù)管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 啟用基于注解的事務(wù)管理 --><tx:annotation-driven /><!-- 其他 bean 配置 --></beans>

在這個(gè)示例中,<tx:annotation-driven> 元素啟用了基于注解的事務(wù)管理支持,并且自動(dòng)檢測并使用了名為 transactionManager 的事務(wù)管理器。接下來,你可以在需要事務(wù)管理的方法上使用 @Transactional 注解來啟用事務(wù)管理。例如:

@Service
public class MyService {@Autowiredprivate MyRepository repository;@Transactionalpublic void performTransactionalOperation() {// 執(zhí)行數(shù)據(jù)庫操作repository.save(entity);}
}

這樣,在調(diào)用 performTransactionalOperation() 方法時(shí),Spring 會(huì)自動(dòng)為其創(chuàng)建一個(gè)事務(wù),并在方法執(zhí)行結(jié)束后根據(jù)執(zhí)行情況來提交或回滾事務(wù)。

6. Bean的作用域

在Spring Framework中,"Bean的作用域"指的是Spring容器如何管理和創(chuàng)建Bean實(shí)例以及這些實(shí)例的生命周期。Spring定義了幾種不同的作用域,每種作用域都控制著Bean實(shí)例的創(chuàng)建和銷毀方式,以及在應(yīng)用程序中的可見性。以下是Spring中常見的Bean作用域:

  1. Singleton(單例)
  • 默認(rèn)作用域,每個(gè)Spring容器中只存在一個(gè)Bean實(shí)例。
  • Spring容器在第一次請(qǐng)求該Bean時(shí)創(chuàng)建實(shí)例,并在容器關(guān)閉時(shí)銷毀實(shí)例。
  • 所有對(duì)該Bean的請(qǐng)求都返回同一個(gè)實(shí)例。
  • 適用于狀態(tài)無關(guān)的Bean,如服務(wù)類、數(shù)據(jù)訪問類等。
  1. Prototype(原型)
  • 每次通過容器的getBean()方法請(qǐng)求時(shí)都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例
  • Spring容器不會(huì)管理Prototype作用域的Bean的生命周期,也不負(fù)責(zé)銷毀它們。
  • 每次請(qǐng)求該Bean時(shí)都會(huì)創(chuàng)建一個(gè)新的實(shí)例,并在返回后不再管理它。
  • 適用于狀態(tài)相關(guān)的Bean,如HTTP請(qǐng)求的處理類、會(huì)話Bean等。
  1. Request(請(qǐng)求)
  • 每個(gè)HTTP請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例,該實(shí)例僅在當(dāng)前HTTP請(qǐng)求范圍內(nèi)有效。
  • 僅在Spring Web應(yīng)用程序中有效,在單例Bean中注入Request作用域的Bean時(shí)需要注意。
  • Spring MVC中常用于Web應(yīng)用程序中處理每個(gè)HTTP請(qǐng)求的控制器。
  1. Session(會(huì)話)
  • 每個(gè)HTTP會(huì)話創(chuàng)建一個(gè)新的Bean實(shí)例,該實(shí)例在整個(gè)會(huì)話期間有效。
  • 僅在Spring Web應(yīng)用程序中有效,在單例Bean中注入Session作用域的Bean時(shí)需要注意。
  • 通常用于保存用戶會(huì)話狀態(tài)的Bean。
  1. Application(應(yīng)用)
  • 在ServletContext范圍內(nèi)創(chuàng)建一個(gè)Bean實(shí)例,該實(shí)例在整個(gè)應(yīng)用程序的生命周期內(nèi)有效。
  • 僅在Spring Web應(yīng)用程序中有效,在單例Bean中注入Application作用域的Bean時(shí)需要注意。
  • 通常用于在應(yīng)用程序級(jí)別共享的全局配置信息或狀態(tài)。
  1. WebSocket(WebSocket)
  • 每個(gè)WebSocket連接創(chuàng)建一個(gè)新的Bean實(shí)例,該實(shí)例在WebSocket會(huì)話期間有效。
  • 用于在Spring WebSocket應(yīng)用程序中保存WebSocket連接的狀態(tài)。

選擇適當(dāng)?shù)淖饔糜蛉Q于Bean的性質(zhì)和在應(yīng)用程序中的使用方式。默認(rèn)情況下,建議使用Singleton作用域,除非有特定的需求需要使用其他作用域。

        @Beanpublic SingletonBean singletonBean() {return new SingletonBean();}// Prototype Bean@Bean@Scope("prototype")public PrototypeBean prototypeBean() {return new PrototypeBean();}
@RequestScope
@Component
public class LoginAction {// ...
}
@SessionScope
@Component
public class UserPreferences {// ...
}

或:

    <bean id="user" class="com.github.subei.pojo.User"/><!-- 以下內(nèi)容是等價(jià)的,盡管是多余的(默認(rèn)為單例作用域) --><bean id="user2" class="com.github.subei.pojo.User"scope="singleton"/>
    <bean id="user2" class="com.github.subei.pojo.User"scope="prototype"/>  

7. 在Spring中有三種自動(dòng)裝配的方式

1. 在xml中顯式的配置

這是最傳統(tǒng)的方式,通過在XML配置文件中使用<bean>元素來定義Bean,并在需要注入依賴的地方使用<property><constructor-arg>元素來顯式指定依賴的Bean。這種方式需要開發(fā)人員手動(dòng)配置所有的Bean和它們之間的依賴關(guān)系,不具備自動(dòng)發(fā)現(xiàn)和連接的能力。

<bean id="dependencyBean" class="com.example.DependencyBean" />
<bean id="myBean" class="com.example.MyBean"><property name="dependency" ref="dependencyBean" />
</bean>

2. 在java中顯式配置

使用Java Config(也稱為Java Configuration)的方式,通過在Java類中使用@Configuration注解和@Bean注解來聲明Bean,并通過@Autowired注解來注入依賴關(guān)系。這種方式將Bean的定義和依賴關(guān)系配置移到了Java代碼中,使得配置更加類型安全,并且可以利用Java語言的特性進(jìn)行更靈活的配置。

@Configuration
public class AppConfig {@Beanpublic DependencyBean dependencyBean() {return new DependencyBean();}@Beanpublic MyBean myBean() {return new MyBean(dependencyBean());}
}

3. 隱式的自動(dòng)裝配bean。【重要!】

Spring框架提供了自動(dòng)裝配機(jī)制,可以通過在類的構(gòu)造器、屬性、或方法上使用@Autowired注解來隱式指定Bean之間的依賴關(guān)系。Spring會(huì)自動(dòng)在容器中查找匹配的Bean,并將其注入到目標(biāo)Bean中。這種方式使得開發(fā)人員可以將關(guān)注點(diǎn)集中在業(yè)務(wù)邏輯上,而不需要過多地關(guān)注Bean之間的依賴關(guān)系,從而提高了開發(fā)效率。

第一步:在 beans.xml / applicationContext.xml 導(dǎo)入context 約束,并開啟注解支持

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/context/spring-aop.xsd"><!-- 開啟注解的支持 --><context:annotation-config/><bean id="cat" class="com.github.subei.pojo.Cat"/><bean id="dog" class="com.github.subei.pojo.Dog"/><bean id="people" class="com.github.subei.pojo.People"/></beans>

java:

public class MyBean {private DependencyBean dependency;@Autowiredpublic MyBean(DependencyBean dependency) {this.dependency = dependency;}
}

byName 和 byType 自動(dòng)裝配:

byName 自動(dòng)裝配

  • 在byName自動(dòng)裝配中,Spring 容器會(huì)自動(dòng)將一個(gè) bean 的屬性與另一個(gè) bean 的名稱匹配。如果一個(gè) bean 的屬性名稱與另一個(gè) bean 的名稱相同,則會(huì)將這個(gè) bean 注入到該屬性中。

  • 為了啟用 byName 自動(dòng)裝配,需要在 XML 配置文件中使用?autowire="byName"?屬性,或者在 Java 配置類中使用?@Autowired?注解結(jié)合?@Qualifier?注解。

    @Qualifier?注解: 指定一個(gè)唯一的bean對(duì)象注入

  • 使用 byName 自動(dòng)裝配時(shí),確保目標(biāo) bean 的屬性名稱與依賴 bean 的名稱匹配。

  • byname的時(shí)候,需要保證所有bean的id唯一,并且這個(gè)bean需要和自動(dòng)注入的屬性的set方法的值一致!

<bean id="dependencyBean" class="com.example.DependencyBean" />
<bean id="myBean" class="com.example.MyBean" autowire="byName" />

或者

@Configuration
public class AppConfig {@Beanpublic DependencyBean dependencyBean() {return new DependencyBean();}@Bean@Autowiredpublic MyBean myBean(@Qualifier("dependencyBean") DependencyBean dependency) {return new MyBean(dependency);}
}

byType 自動(dòng)裝配

  • 在byType自動(dòng)裝配中,Spring 容器會(huì)自動(dòng)將一個(gè) bean 的屬性與另一個(gè) bean 的類型匹配。如果一個(gè) bean 的屬性類型與另一個(gè) bean 的類型相同,則會(huì)將這個(gè) bean 注入到該屬性中。
  • 為了啟用 byType 自動(dòng)裝配,需要在 XML 配置文件中使用?autowire="byType"?屬性,或者在 Java 配置類中使用?@Autowired?注解。
  • 使用 byType 自動(dòng)裝配時(shí),確保目標(biāo) bean 的屬性類型與依賴 bean 的類型匹配,且容器中只有一個(gè)與之匹配的 bean。
  • bytype的時(shí)候,需要保證所有bean的class唯一,并且這個(gè)bean需要和自動(dòng)注入的屬性的類型一致!
<bean id="dependencyBean" class="com.example.DependencyBean" />
<bean id="myBean" class="com.example.MyBean" autowire="byType" />

或者

@Configuration
public class AppConfig {@Beanpublic DependencyBean dependencyBean() {return new DependencyBean();}@Bean@Autowiredpublic MyBean myBean(DependencyBean dependency) {return new MyBean(dependency);}
}
  1. byName中bean的名稱是指 bean 的 id 嗎?
  • 是的,byName 自動(dòng)裝配中的名稱指的是 bean 的 id。Spring 容器會(huì)將屬性名稱與其他 bean 的 id 進(jìn)行匹配。
  1. <bean/> 默認(rèn)使用的是byName自動(dòng)裝配嗎?
  • 不是的,默認(rèn)情況下,<bean/> 元素并不會(huì)啟用任何自動(dòng)裝配。你需要顯式地指定 autowire 屬性為 byName 或者 byType 才能啟用對(duì)應(yīng)的自動(dòng)裝配方式。
  1. byType 中 bean 的屬性類型指的是 bean 的 class 嗎?
  • 是的,byType 自動(dòng)裝配中,Spring 容器會(huì)嘗試查找與屬性類型匹配的 bean,并將其注入到對(duì)應(yīng)的屬性中。
  1. @Resource 和@Autowired 分別使用的是 byName 還是 byType?
  • @Resource?注解默認(rèn)使用 byName 自動(dòng)裝配,它會(huì)根據(jù)屬性名稱去查找對(duì)應(yīng)的 bean。你也可以通過?name?屬性指定要注入的 bean 的名稱。
  • @Autowired?注解默認(rèn)使用 byType 自動(dòng)裝配,它會(huì)根據(jù)屬性類型去查找對(duì)應(yīng)的 bean。你也可以結(jié)合?@Qualifier?注解使用 byName 自動(dòng)裝配,或者使用?@Autowired(required = false)?實(shí)現(xiàn)可選的自動(dòng)裝配。
  1. @Resource 和@Autowired的區(qū)別:
  • 都是用來自動(dòng)裝配的,都可以放在屬性字段上!

  • @Autowired 通過byType的方式實(shí)現(xiàn),而且必須要求這個(gè)對(duì)象存在!

  • @Resource默認(rèn)通過byname的方式實(shí)現(xiàn),如果找不到名字,則通過byType實(shí)現(xiàn)!如果兩個(gè)都找不到的倩況下,就報(bào)錯(cuò)!【常用】

  • 執(zhí)行順序不同:@Autowired通過byType的方式實(shí)現(xiàn)。@Resource默認(rèn)通過byname的方式實(shí)現(xiàn)。

8. 注解

第一步:在 beans.xml / applicationContext.xml 導(dǎo)入context 約束,并開啟注解支持

<!-- 開啟注解的支持 --> 
<context:annotation-config/> 
  1. @ComponentScan: 用于啟用組件掃描,自動(dòng)掃描指定包及其子包中的組件(例如,帶有 @Component、@Service、@Repository@Controller 等注解的類),并將其注冊到 Spring 容器中。

applicationContext.xml:

    <!-- 指定要掃描的包,這個(gè)包下的注解就會(huì)生效! --><context:component-scan base-package="com.github.subei.pojo"/>

或者 使用Java配置類(@Configuration),相當(dāng)于 xml 配置文件

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {// 這里可以添加其他配置
}
  1. @Component: 用于標(biāo)識(shí)一個(gè)類作為 Spring 組件,讓 Spring 自動(dòng)掃描并注冊為 bean。

  2. @Service: 通常用于標(biāo)識(shí)服務(wù)層組件,類似于 @Component,但提供了更具體的語義。

  3. @Repository: 通常用于標(biāo)識(shí)數(shù)據(jù)訪問層組件(如 DAO),它們通常用于與數(shù)據(jù)庫或其他持久化機(jī)制交互。

  4. @Controller: 用于標(biāo)識(shí)控制器層組件,通常用于處理 Web 請(qǐng)求。

  5. @Autowired: 用于進(jìn)行自動(dòng)裝配,可以用在構(gòu)造函數(shù)、setter 方法、字段或者在配置類中的方法上。Spring 將根據(jù)類型進(jìn)行匹配,將相應(yīng)的 bean 注入到標(biāo)記了 @Autowired 的屬性中。

  6. @Qualifier:@Autowired 結(jié)合使用,用于指定要注入的具體 bean 的名稱,用以解決多個(gè)候選 bean 的自動(dòng)裝配歧義問題。

  7. @Value: 用于將外部屬性值注入到 bean 的屬性中,可以從屬性文件、環(huán)境變量等地方獲取值。

  8. @Configuration: 用于標(biāo)識(shí)一個(gè)類為配置類,通常與 @Bean 注解一起使用,用于定義 bean。

  9. @Bean: 通常用于在配置類中定義 bean,Spring 容器會(huì)根據(jù)其返回值注冊一個(gè) bean。

  10. @Scope: 用于指定 bean 的作用域,例如 Singleton、Prototype 等。

  11. @PostConstruct 和 @PreDestroy: 用于標(biāo)識(shí)初始化和銷毀回調(diào)方法,分別在 bean 的初始化和銷毀階段執(zhí)行。

9. applicationContext.xml與beans.xml有什么區(qū)別

applicationContext.xmlbeans.xml 是兩種常見的 Spring XML 配置文件,它們在用途和功能上有一些區(qū)別。

  1. applicationContext.xml:
  • applicationContext.xml?是 Spring 應(yīng)用程序上下文的配置文件,通常用于配置整個(gè)應(yīng)用程序的上下文環(huán)境。
  • 在?applicationContext.xml?中,可以定義各種類型的 bean、組件掃描、AOP、事務(wù)管理、國際化配置等等。
  • applicationContext.xml?可以包含多個(gè)模塊或者組件的配置,通常用于配置整個(gè)應(yīng)用程序的所有組件。
  1. beans.xml:
  • beans.xml?通常是用于某個(gè)特定模塊或者組件的配置文件,用于配置該模塊或者組件所需要的 bean。
  • 在?beans.xml?中,通常只包含與特定模塊或者組件相關(guān)的 bean 配置。
  • 使用?beans.xml?可以將應(yīng)用程序的配置模塊化,使得每個(gè)模塊的配置都更加清晰和獨(dú)立。

總的來說,applicationContext.xml 是整個(gè) Spring 應(yīng)用程序的上下文配置文件,用于配置應(yīng)用程序中的所有組件,而 beans.xml 則是針對(duì)某個(gè)特定模塊或者組件的配置文件,用于配置該模塊或者組件所需要的 bean。在實(shí)際開發(fā)中,可以根據(jù)需要選擇使用其中的一個(gè)或者同時(shí)使用兩者。

10. Java Config

  • Java配置類(@Configuration),相當(dāng)于 xml 配置文件

    @Configuration
    @ComponentScan(basePackages = "com.example")
    public class AppConfig {// 這里可以添加其他配置
    }
    
  • 導(dǎo)入其他配置

    package com.github.subei.config;import org.springframework.context.annotation.Configuration;@Configuration //代表這是一個(gè)配置類
    public class SunConfig2 {
    }
    
package com.github.subei.config;import com.github.subei.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;// 這個(gè)也會(huì)Spring容器托管,注冊到容器中,因?yàn)樗緛砭褪且粋€(gè)@Component
// @Configuration代表這是一個(gè)配置類,就和我們之前看的beans.xml
@Configuration  // 代表這是一個(gè)配置類
@ComponentScan("com.github.subei.pojo")
@Import(SunConfig2.class) // 導(dǎo)入合并其他配置類,類似于配置文件中的 inculde 標(biāo)簽
public class SunConfig {// 注冊一個(gè)bean,就相當(dāng)于我們之前寫的一個(gè)bean標(biāo)簽;// 這個(gè)方法的名字,就相當(dāng)bean標(biāo)簽中的id屬性;// 這個(gè)方法的返回值,就相當(dāng)bean標(biāo)簽中的cLass屬性;@Beanpublic User getUser(){return new User(); // 就是返回要注入到bean的對(duì)象}
}

11. 橫向開發(fā) VS 縱向開發(fā)

在這里插入圖片描述

12. 代理模式

代理模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,其目的是通過代理對(duì)象來控制對(duì)其它對(duì)象的訪問。代理對(duì)象通常充當(dāng)了被代理對(duì)象的包裝器或者中間人的角色,從而可以在訪問實(shí)際對(duì)象之前或之后執(zhí)行一些額外的操作。

代理模式主要有三種形式:

  1. 靜態(tài)代理:在編譯時(shí)就已經(jīng)確定代理對(duì)象和被代理對(duì)象的關(guān)系,代理類與被代理類是一對(duì)一的關(guān)系。靜態(tài)代理的優(yōu)點(diǎn)是簡單易懂,缺點(diǎn)是每一個(gè)被代理的類都需要一個(gè)代理類,因此擴(kuò)展性不強(qiáng)。

  2. 動(dòng)態(tài)代理:在運(yùn)行時(shí)動(dòng)態(tài)生成代理類,無需為每個(gè)被代理類編寫一個(gè)代理類。Java 中的動(dòng)態(tài)代理通常使用 JDK 的動(dòng)態(tài)代理或者 CGLIB 等第三方庫來實(shí)現(xiàn)。動(dòng)態(tài)代理的優(yōu)點(diǎn)是可以減少重復(fù)代碼,增強(qiáng)靈活性和可維護(hù)性。

  3. 虛擬代理:延遲創(chuàng)建代理對(duì)象的實(shí)例,直到真正需要使用它時(shí)才進(jìn)行實(shí)例化。虛擬代理通常用于需要消耗大量資源或者初始化較慢的對(duì)象,通過延遲加載來提升性能。

代理模式常見的應(yīng)用場景包括:

  • 遠(yuǎn)程代理:在客戶端和遠(yuǎn)程服務(wù)器之間創(chuàng)建代理對(duì)象,用于隱藏真實(shí)對(duì)象的網(wǎng)絡(luò)細(xì)節(jié)。
  • 虛擬代理:延遲加載大對(duì)象,例如圖片、視頻等資源,在需要時(shí)才真正加載。
  • 安全代理:控制對(duì)真實(shí)對(duì)象的訪問權(quán)限,例如權(quán)限驗(yàn)證、身份驗(yàn)證等。
  • 日志記錄:在訪問真實(shí)對(duì)象前后記錄日志信息,用于調(diào)試或者監(jiān)控。
  • 緩存代理:緩存真實(shí)對(duì)象的結(jié)果,避免重復(fù)計(jì)算,提升性能。

代理模式可以幫助我們實(shí)現(xiàn)一些橫切關(guān)注點(diǎn)(cross-cutting concerns),例如日志記錄、事務(wù)管理等,同時(shí)也可以提高代碼的可維護(hù)性和可擴(kuò)展性。

13. 靜態(tài)代理 VS 動(dòng)態(tài)代理

靜態(tài)代理

靜態(tài)代理是指在編譯期間就已經(jīng)確定代理對(duì)象和被代理對(duì)象的關(guān)系的代理模式。在靜態(tài)代理中,代理類和被代理類是一對(duì)一的關(guān)系,代理類充當(dāng)了被代理類的包裝器,對(duì)外提供與被代理類相同的接口,但在調(diào)用這些接口方法時(shí)可以在前后執(zhí)行一些額外的操作。

靜態(tài)代理的基本結(jié)構(gòu)包括三個(gè)角色:

  1. 抽象接口(Subject):定義了代理類和被代理類共同實(shí)現(xiàn)的接口,代理類通過實(shí)現(xiàn)這個(gè)接口來與被代理類保持一致。

  2. 被代理類(Real Subject):實(shí)際執(zhí)行業(yè)務(wù)邏輯的類,是代理類所代理的對(duì)象。

  3. 代理類(Proxy):持有一個(gè)對(duì)被代理類的引用,同時(shí)實(shí)現(xiàn)了抽象接口,通過調(diào)用被代理類的方法來完成實(shí)際的業(yè)務(wù)邏輯,在方法執(zhí)行前后可以執(zhí)行一些額外的操作,如權(quán)限驗(yàn)證、日志記錄等。

靜態(tài)代理的優(yōu)點(diǎn)包括:

  • 可以在不修改被代理類的情況下對(duì)其進(jìn)行擴(kuò)展或者增強(qiáng),符合開閉原則。
  • 可以控制對(duì)被代理對(duì)象的訪問,提高系統(tǒng)的安全性。
  • 可以將橫切關(guān)注點(diǎn)(如日志記錄、權(quán)限驗(yàn)證等)集中處理,提高代碼的可維護(hù)性。

靜態(tài)代理的缺點(diǎn)包括:

  • 每個(gè)被代理類都需要對(duì)應(yīng)一個(gè)代理類,如果被代理類較多,會(huì)導(dǎo)致類的數(shù)量增加,代碼量增加,不利于系統(tǒng)的維護(hù)和擴(kuò)展。
  • 靜態(tài)代理在編譯時(shí)就已經(jīng)確定了代理對(duì)象和被代理對(duì)象的關(guān)系,不夠靈活,無法動(dòng)態(tài)改變代理對(duì)象。

抽象接口 Subject,其中定義了一個(gè) request() 方法:

// Subject.java
public interface Subject {void request();
}

實(shí)際的被代理類 RealSubject,實(shí)現(xiàn)了 Subject 接口:

// RealSubject.java
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: Handling request.");}
}

代理類 Proxy,也實(shí)現(xiàn)了 Subject 接口:

// Proxy.java
public class Proxy implements Subject {private RealSubject realSubject;public Proxy(RealSubject realSubject) {this.realSubject = realSubject;}@Overridepublic void request() {// 在調(diào)用真實(shí)主題前可以執(zhí)行一些額外的操作System.out.println("Proxy: Pre-processing");// 調(diào)用真實(shí)主題的方法realSubject.request();// 在調(diào)用真實(shí)主題后可以執(zhí)行一些額外的操作System.out.println("Proxy: Post-processing");}
}

測試類來演示如何使用靜態(tài)代理:

// ProxyPatternDemo.java
public class ProxyPatternDemo {public static void main(String[] args) {// 創(chuàng)建真實(shí)主題對(duì)象RealSubject realSubject = new RealSubject();// 創(chuàng)建代理對(duì)象,將真實(shí)主題對(duì)象傳遞給代理對(duì)象Proxy proxy = new Proxy(realSubject);// 通過代理對(duì)象調(diào)用方法proxy.request();}
}

輸出將會(huì)是:

Proxy: Pre-processing
RealSubject: Handling request.
Proxy: Post-processing

動(dòng)態(tài)代理

動(dòng)態(tài)代理分為兩大類:基于接口的動(dòng)態(tài)代理,基于類的動(dòng)態(tài)代理。

  • 基于接口——JDK動(dòng)態(tài)代理

  • 基于類——cglib

基于接口的動(dòng)態(tài)代理是通過 Java 標(biāo)準(zhǔn)庫中的 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口實(shí)現(xiàn)的。它要求被代理的類必須實(shí)現(xiàn)一個(gè)接口,代理類通過實(shí)現(xiàn)同樣的接口,并在 InvocationHandler 的實(shí)現(xiàn)中攔截方法調(diào)用來實(shí)現(xiàn)代理功能。

而基于類的動(dòng)態(tài)代理則是通過第三方庫實(shí)現(xiàn)的,比如 CGLIB。它允許在不需要目標(biāo)對(duì)象實(shí)現(xiàn)接口的情況下創(chuàng)建代理,通過繼承目標(biāo)類并重寫它的方法來實(shí)現(xiàn)代理功能。因?yàn)槭腔诶^承,所以它無法代理被 final 修飾的類和方法。

選擇使用哪種類型的動(dòng)態(tài)代理取決于具體的需求和場景。如果被代理的類已經(jīng)實(shí)現(xiàn)了接口,并且你希望代理類與目標(biāo)類之間存在松耦合的關(guān)系,那么 JDK 動(dòng)態(tài)代理是一個(gè)不錯(cuò)的選擇。但如果被代理的類沒有實(shí)現(xiàn)接口,或者你希望在代理過程中攔截對(duì)被代理類的所有方法調(diào)用,包括 final 方法,那么就需要使用基于類的動(dòng)態(tài)代理,比如 CGLIB。

InvocationHandler 與 Proxy

  1. InvocationHandler(調(diào)用處理器)
  • java.lang.reflect.InvocationHandler?接口是用于實(shí)現(xiàn)動(dòng)態(tài)代理的關(guān)鍵之一。它只定義了一個(gè)方法?invoke(Object proxy, Method method, Object[] args),在代理對(duì)象上調(diào)用方法時(shí)會(huì)觸發(fā)此方法的調(diào)用。在這個(gè)方法中,你可以實(shí)現(xiàn)代理對(duì)象方法的攔截和自定義行為。
  • invoke?方法的參數(shù)解釋:
    • proxy:代理對(duì)象,調(diào)用方法時(shí)會(huì)自動(dòng)傳遞給?invoke?方法。
    • method:被調(diào)用的方法對(duì)象。
    • args:方法調(diào)用時(shí)傳遞的參數(shù)數(shù)組。
  • InvocationHandler?的實(shí)現(xiàn)類實(shí)際上就是在定義代理對(duì)象的行為。你可以在?invoke?方法中添加額外的邏輯,比如記錄日志、執(zhí)行前置/后置操作等。
  1. Proxy(代理類)
  • java.lang.reflect.Proxy?類是 JDK 提供的用于創(chuàng)建動(dòng)態(tài)代理對(duì)象的工具類。它提供了一系列靜態(tài)方法來創(chuàng)建代理類實(shí)例。
  • Proxy?類的靜態(tài)方法?newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)?用于創(chuàng)建代理對(duì)象。
    • loader:類加載器,用于加載代理類。
    • interfaces:被代理類實(shí)現(xiàn)的接口數(shù)組。
    • hInvocationHandler?對(duì)象,用于處理代理對(duì)象方法的調(diào)用。
  • Proxy?類創(chuàng)建的代理對(duì)象實(shí)際上是在運(yùn)行時(shí)動(dòng)態(tài)生成的,它實(shí)現(xiàn)了被代理接口,并且在方法調(diào)用時(shí)會(huì)委托給傳入的?InvocationHandler?實(shí)現(xiàn)類處理。
  • 動(dòng)態(tài)代理的核心就是通過?Proxy?類來創(chuàng)建代理對(duì)象,并且在代理對(duì)象的方法被調(diào)用時(shí),會(huì)自動(dòng)觸發(fā)?InvocationHandler?實(shí)現(xiàn)類中的?invoke?方法。

基于接口——JDK動(dòng)態(tài)代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定義接口
interface Hello {void sayHello();
}// 實(shí)現(xiàn)接口的被代理類
class HelloImpl implements Hello {public void sayHello() {System.out.println("Hello, world!");}
}// 實(shí)現(xiàn) InvocationHandler 接口的代理處理類
class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method invocation");Object result = method.invoke(target, args); // 調(diào)用被代理對(duì)象的方法System.out.println("After method invocation");return result;}
}public class Main {public static void main(String[] args) {// 創(chuàng)建被代理對(duì)象Hello hello = new HelloImpl();// 創(chuàng)建 InvocationHandler 實(shí)現(xiàn)類的實(shí)例MyInvocationHandler handler = new MyInvocationHandler(hello);// 使用 Proxy.newProxyInstance() 創(chuàng)建代理對(duì)象Hello proxyHello = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(),hello.getClass().getInterfaces(),handler);// 通過代理對(duì)象調(diào)用方法proxyHello.sayHello();}
}

基于類——CGLIB動(dòng)態(tài)代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;// 被代理的類,無需實(shí)現(xiàn)接口
class Hello {public void sayHello() {System.out.println("Hello, world!");}
}// 實(shí)現(xiàn) MethodInterceptor 接口的代理處理類
class MyMethodInterceptor implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method invocation");Object result = proxy.invokeSuper(obj, args); // 調(diào)用被代理對(duì)象的方法System.out.println("After method invocation");return result;}
}public class Main {public static void main(String[] args) {// 創(chuàng)建 Enhancer 對(duì)象Enhancer enhancer = new Enhancer();// 設(shè)置被代理類為父類enhancer.setSuperclass(Hello.class);// 設(shè)置回調(diào)函數(shù)enhancer.setCallback(new MyMethodInterceptor());// 創(chuàng)建代理對(duì)象Hello proxyHello = (Hello) enhancer.create();// 通過代理對(duì)象調(diào)用方法proxyHello.sayHello();}
}

14. AOP

AOP(Aspect-Oriented Programming)是一種編程范式,旨在通過橫切關(guān)注點(diǎn)(cross-cutting concerns)的方式來解耦系統(tǒng)中的各個(gè)模塊,使得代碼更加模塊化、可維護(hù)和可復(fù)用。在 AOP 中,橫切關(guān)注點(diǎn)是指在應(yīng)用程序中影響多個(gè)類和方法的功能需求,例如日志記錄、事務(wù)管理、安全性等。

AOP 通過在程序執(zhí)行期間動(dòng)態(tài)地將這些橫切關(guān)注點(diǎn)與核心業(yè)務(wù)邏輯分離開來,從而達(dá)到降低代碼耦合度的目的。它主要通過兩種方式實(shí)現(xiàn):

  1. 基于代理的 AOP:在這種方式下,AOP 框架通過動(dòng)態(tài)代理為目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象,代理對(duì)象包含了額外的橫切邏輯。當(dāng)調(diào)用目標(biāo)對(duì)象的方法時(shí),會(huì)首先執(zhí)行代理對(duì)象中的橫切邏輯,然后再調(diào)用目標(biāo)對(duì)象的方法。

  2. 基于字節(jié)碼操作的 AOP:在這種方式下,AOP 框架通過修改目標(biāo)類的字節(jié)碼來插入橫切邏輯,實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的增強(qiáng)。常見的字節(jié)碼操作工具有 ASM、CGLIB 等。

在這里插入圖片描述

核心概念

  1. 橫切關(guān)注點(diǎn)(Cross-cutting Concerns):這指的是在應(yīng)用程序中涉及多個(gè)模塊的功能需求,例如日志記錄、安全性、事務(wù)管理等。橫切關(guān)注點(diǎn)橫跨應(yīng)用程序的不同部分,并且通常與應(yīng)用程序的核心功能無關(guān)。

  2. 切面(Aspect):切面是 AOP 的核心組件之一,用于封裝橫切關(guān)注點(diǎn)。它定義了何時(shí)何地應(yīng)用橫切邏輯(通知)以及應(yīng)用哪些連接點(diǎn)(切入點(diǎn))。切面將橫切邏輯與應(yīng)用程序的核心業(yè)務(wù)邏輯分離開來。

  3. 通知(Advice):通知是切面中定義的實(shí)際行為,它決定了橫切邏輯在何時(shí)、何地執(zhí)行以及執(zhí)行哪些操作。通知類型包括前置通知(Before advice)、后置通知(After advice)、返回通知(After-returning advice)、異常通知(After-throwing advice)和環(huán)繞通知(Around advice)。

在這里插入圖片描述

  1. 目標(biāo)(Target):目標(biāo)是被切面影響的對(duì)象或類。它通常包含應(yīng)用程序的核心業(yè)務(wù)邏輯。

  2. 代理(Proxy):代理是在目標(biāo)對(duì)象之上創(chuàng)建的對(duì)象,用于控制對(duì)目標(biāo)對(duì)象的訪問。在 AOP 中,代理包含了切面所定義的橫切邏輯,并且在調(diào)用目標(biāo)對(duì)象方法時(shí)會(huì)執(zhí)行這些邏輯。

  3. 切入點(diǎn)(Pointcut):切入點(diǎn)用于定義橫切邏輯在何處執(zhí)行。它是一個(gè)表達(dá)式,指示了在應(yīng)用程序中哪些連接點(diǎn)應(yīng)該被通知影響。

  4. 連接點(diǎn)(Join Point):連接點(diǎn)是在應(yīng)用程序執(zhí)行過程中可以插入橫切邏輯的具體點(diǎn)。這些點(diǎn)可以是方法調(diào)用、異常拋出、字段訪問等。通知可以在連接點(diǎn)處被執(zhí)行。

在這里插入圖片描述

實(shí)現(xiàn)

1. 基于 xml 配置類實(shí)現(xiàn)

UserService.java

package com.fatfish.aop.service;/*** @author fatfish* @version 1.0* @date 2024/6/10 21:13*/
public interface UserService {void add();void delete();void select();void update();
}

UserServiceImpl.java

package com.fatfish.aop.service;/*** @author fatfish* @version 1.0* @date 2024/6/10 21:14*/
public class UserServiceImpl implements UserService{public void add() {System.out.println("增加了一個(gè)用戶");}public void delete() {System.out.println("刪除了一個(gè)用戶");}public void select() {System.out.println("查詢了一個(gè)用戶");}public void update() {System.out.println("更新了一個(gè)用戶");}
}

切面類:

Log.java

package com.fatfish.aop.log;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;/*** @author fatfish* @version 1.0* @date 2024/6/10 21:15*/public class Log implements MethodBeforeAdvice {// method:要執(zhí)行的目標(biāo)對(duì)象的方法// args:參數(shù)// target:目標(biāo)對(duì)象public void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被執(zhí)行了!");}
}

AfterLog.java

package com.fatfish.aop.log;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;/*** @author fatfish* @version 1.0* @date 2024/6/10 21:16*/public class AfterLog implements AfterReturningAdvice {// returnValue:返回值// method:被調(diào)用的方法// args:被調(diào)用的方法的對(duì)象的參數(shù)// target:被調(diào)用的目標(biāo)對(duì)象public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("執(zhí)行了" + method.getName() + target.getClass().getName()+ "的" + "返回結(jié)果為:" + returnValue);}
}

applicationContext.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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 注冊bean --><bean id="userService" class="com.fatfish.aop.service.UserServiceImpl"/><bean id="log" class="com.fatfish.aop.log.Log"/><bean id="afterLog" class="com.fatfish.aop.log.AfterLog"/><!-- 方式一:使用原生Spring API接口 --><!-- 配置AOP:需要導(dǎo)入AOP的約束 --><aop:config><!-- 切入點(diǎn):expression:表達(dá)式 , execution(要執(zhí)行的位置!* * * *) --><aop:pointcut id="pointcut" expression="execution(* com.fatfish.aop.service.*.*(..))"/><!-- 執(zhí)行環(huán)繞增加! advice-ref執(zhí)行方法 , pointcut-ref切入點(diǎn) --><aop:advisor advice-ref="log" pointcut-ref="pointcut"/><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/></aop:config></beans>

測試類:

package com.fatfish.aop;import com.fatfish.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author fatfish* @version 1.0* @date 2024/6/10 21:19*/public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 動(dòng)態(tài)代理代理的是接口UserService userService = context.getBean("userService", UserService.class);userService.select();}
}

結(jié)果:

com.fatfish.aop.service.UserServiceImpl的select方法被執(zhí)行了!
查詢了一個(gè)用戶
執(zhí)行了selectcom.fatfish.aop.service.UserServiceImpl的返回結(jié)果為:null

2. 自定義類

切入類

package com.github.subei.diy;public class DiyPointCut {public void before(){System.out.println("---------方法執(zhí)行前---------");}public void after(){System.out.println("---------方法執(zhí)行后---------");}
}

applicationContext.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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 注冊bean --><bean id="userService" class="com.fatfish.aop.service.UserServiceImpl"/><bean id="log" class="com.fatfish.aop.log.Log"/><bean id="afterLog" class="com.fatfish.aop.log.AfterLog"/><!-- 方式一:使用原生Spring API接口 --><!-- 配置AOP:需要導(dǎo)入AOP的約束 --><aop:config><!-- 切入點(diǎn):expression:表達(dá)式 , execution(要執(zhí)行的位置!* * * *) --><aop:pointcut id="pointcut" expression="execution(* com.fatfish.aop.service.*.*(..))"/><!-- 執(zhí)行環(huán)繞增加! advice-ref執(zhí)行方法 , pointcut-ref切入點(diǎn) --><aop:advisor advice-ref="log" pointcut-ref="pointcut"/><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/></aop:config><!--  第二種:自定義類  --><bean id="diy" class="com.fatfish.aop.log.DiyPointCut"/><aop:config><!--  自定義切面,ref要引用的類  --><aop:aspect ref="diy"><!--  切入點(diǎn)  --><aop:pointcut id="point" expression="execution(* com.fatfish.aop.service.UserServiceImpl.*(..))"/><!--  通知  --><aop:before method="before" pointcut-ref="point" /><aop:after method="after" pointcut-ref="point"/></aop:aspect></aop:config></beans>

測試類與上面一致,測試結(jié)果如下:

com.fatfish.aop.service.UserServiceImpl的select方法被執(zhí)行了!
---------方法執(zhí)行前---------
查詢了一個(gè)用戶
---------方法執(zhí)行后---------
執(zhí)行了selectcom.fatfish.aop.service.UserServiceImpl的返回結(jié)果為:null

3. 使用注解實(shí)現(xiàn)

編寫一個(gè)注解實(shí)現(xiàn)的增強(qiáng)類

package com.fatfish.aop.log;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;/*** @author fatfish* @version 1.0* @date 2024/6/10 22:09*/// 使用注解方式實(shí)現(xiàn)AOP
@Aspect // 標(biāo)注這個(gè)類是一個(gè)切面
public class AnnotationPointCut {@Before("execution(* com.fatfish.aop.service.UserServiceImpl.*(..))")public void before(){System.out.println("---------方法執(zhí)行前---------");}@After("execution(* com.fatfish.aop.service.UserServiceImpl.*(..))")public void after(){System.out.println("---------方法執(zhí)行后---------");}// 在環(huán)繞增強(qiáng)中,我們可以給定一個(gè)參數(shù),代表我們要獲取處理切入的點(diǎn)@Around("execution(* com.fatfish.aop.service.UserServiceImpl.*(..))")public void around(ProceedingJoinPoint jp) throws Throwable {System.out.println("環(huán)繞前");Signature signature = jp.getSignature();System.out.println("簽名:"+ signature); // 獲得簽名// 執(zhí)行目標(biāo)方法:proceedObject proceed = jp.proceed();System.out.println("環(huán)繞后");System.out.println(proceed);}
}

在Spring配置文件中,注冊bean,并增加支持注解的配置。

    <!-- 第三種方法:使用注解方式實(shí)現(xiàn) --><bean id="annotationPointCut" class="com.fatfish.aop.log.AnnotationPointCut"/><!-- 開啟注解支持! --><aop:aspectj-autoproxy/>

測試代碼同上,結(jié)果如下:

22:25:34.076 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@442d9b6e
22:25:34.249 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 12 bean definitions from class path resource [applicationContext.xml]
22:25:34.265 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
22:25:34.345 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
22:25:34.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
22:25:34.378 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#1'
22:25:34.378 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
22:25:34.468 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#1'
22:25:34.482 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.fatfish.aop.log.AnnotationPointCut.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
22:25:34.482 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.fatfish.aop.log.AnnotationPointCut.before()
22:25:34.482 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.fatfish.aop.log.AnnotationPointCut.after()
22:25:34.498 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'log'
22:25:34.498 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'afterLog'
22:25:34.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'diy'
22:25:34.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'annotationPointCut'
com.fatfish.aop.service.UserServiceImpl的select方法被執(zhí)行了!
---------方法執(zhí)行前---------
環(huán)繞前
簽名:void com.fatfish.aop.service.UserService.select()
---------方法執(zhí)行前---------
查詢了一個(gè)用戶
---------方法執(zhí)行后---------
環(huán)繞后
null
---------方法執(zhí)行后---------
執(zhí)行了selectcom.fatfish.aop.service.UserServiceImpl的返回結(jié)果為:null

aop:aspectj-autoproxy——說明
通過aop命名空間的<aop:aspectj-autoproxy />聲明自動(dòng)為spring容器中那些配置@aspectJ切面的bean創(chuàng)建代理,織入切面。當(dāng)然,spring 在內(nèi)部依舊采用AnnotationAwareAspectJAutoProxyCreator進(jìn)行自動(dòng)代理的創(chuàng)建工作,但具體實(shí)現(xiàn)的細(xì)節(jié)已經(jīng)被<aop:aspectj-autoproxy />隱藏起來了

<aop:aspectj-autoproxy />有一個(gè)proxy-target-class屬性,默認(rèn)為false,表示使用jdk動(dòng)態(tài)代理織入增強(qiáng),當(dāng)配為<aop:aspectj-autoproxy poxy-target-class=“true”/>時(shí),表示使用CGLib動(dòng)態(tài)代理技術(shù)織入增強(qiáng)。不過即使proxy-target-class設(shè)置為false,如果目標(biāo)類沒有聲明接口,則spring將自動(dòng)使用CGLib動(dòng)態(tài)代理。

15. 靜態(tài)資源過濾問題

在 Maven 項(xiàng)目中,靜態(tài)資源過濾問題通常指的是在構(gòu)建過程中,Maven 會(huì)對(duì)項(xiàng)目中的資源文件(如文本文件、XML 文件、屬性文件等)進(jìn)行過濾,即在部署或打包之前,將其中的占位符或變量替換為實(shí)際的值。這種過濾通常用于替換項(xiàng)目中的配置文件中的變量,比如將開發(fā)環(huán)境和生產(chǎn)環(huán)境中的配置信息進(jìn)行替換。

然而,有時(shí)候 Maven 的靜態(tài)資源過濾可能會(huì)導(dǎo)致一些問題,比如:

  1. 錯(cuò)誤的變量替換:Maven 可能會(huì)誤將某些內(nèi)容識(shí)別為變量并進(jìn)行替換,導(dǎo)致配置文件中的內(nèi)容被錯(cuò)誤地修改。
  2. 文件類型不匹配:Maven 可能會(huì)對(duì)不應(yīng)該被過濾的文件進(jìn)行了過濾,導(dǎo)致文件內(nèi)容損壞或變得不可用。
  3. 過濾規(guī)則錯(cuò)誤:配置文件中的過濾規(guī)則可能不正確,導(dǎo)致過濾效果不符預(yù)期。

要解決 Maven 靜態(tài)資源過濾問題,可以嘗試以下方法:

  1. 調(diào)整過濾規(guī)則:檢查 Maven 的過濾配置,確保只對(duì)需要過濾的文件進(jìn)行過濾,且過濾規(guī)則設(shè)置正確。
  2. 配置文件排除:如果有些文件不需要過濾,可以在 Maven 的配置中明確排除這些文件,避免對(duì)其進(jìn)行過濾。
  3. 調(diào)整占位符:如果過濾的變量與其他內(nèi)容沖突,考慮調(diào)整變量的占位符或替換方式,確保不會(huì)誤替換其他內(nèi)容。
  4. 使用不同的過濾方式:Maven 默認(rèn)使用的是基于 ${} 形式的過濾方式,你可以嘗試使用其他方式,比如使用 @ 符號(hào)或其他自定義的占位符。
  5. 仔細(xì)檢查配置文件:檢查被過濾的配置文件,確保其中的占位符和變量使用正確,不會(huì)誤被替換。
  <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>

16. Spring 整合Mybatis

Spring 整合 MyBatis 的主要原因之一是為了簡化數(shù)據(jù)訪問層的開發(fā),以及利用 MyBatis 的優(yōu)勢來提高開發(fā)效率和靈活性。MyBatis 提供了一種將 SQL 查詢映射到 Java 對(duì)象的簡單方式,同時(shí)還提供了靈活的 SQL 編寫和參數(shù)處理功能。Spring 則提供了一個(gè)全功能的框架,用于構(gòu)建企業(yè)級(jí) Java 應(yīng)用程序,并提供了諸如依賴注入、事務(wù)管理、AOP 等功能。將兩者結(jié)合可以使開發(fā)人員更輕松地管理數(shù)據(jù)庫操作、事務(wù)和數(shù)據(jù)源配置,同時(shí)也能夠充分利用 Spring 的各種特性和優(yōu)勢。

整合 MyBatis 和 Spring 的一種常見方式是使用 MyBatis-Spring 模塊。這個(gè)模塊提供了一些核心組件,用于將 MyBatis 和 Spring 集成在一起,包括:

  1. SqlSessionFactoryBean: 這個(gè)類是整合的核心。它負(fù)責(zé)創(chuàng)建 MyBatis 的 SqlSessionFactory 實(shí)例,用于創(chuàng)建 SqlSession 對(duì)象,從而執(zhí)行 SQL 查詢。

  2. MapperScannerConfigurer: 這個(gè)類可以幫助自動(dòng)掃描指定包中的 Mapper 接口,并將其注冊為 Spring 的 Bean,使得這些 Mapper 接口可以在 Spring 中被注入和使用。

  3. TransactionManager: MyBatis-Spring 提供了對(duì) Spring 事務(wù)管理器的支持,使得可以在 Spring 中管理 MyBatis 的事務(wù),確保事務(wù)的一致性和完整性。

  4. SqlSessionTemplate: 這個(gè)類提供了一種將 SqlSession 和 Spring 的事務(wù)管理結(jié)合起來的方式。它可以將 SqlSession 綁定到 Spring 的事務(wù)中,并確保在事務(wù)提交或回滾時(shí)正確關(guān)閉 SqlSession。

整合 MyBatis 和 Spring 的過程通常包括以下步驟:

  1. 添加依賴:在項(xiàng)目的 Maven 或 Gradle 配置文件中添加 MyBatis 和 MyBatis-Spring 的依賴。

  2. 配置數(shù)據(jù)源:在 Spring 的配置文件中配置數(shù)據(jù)源(如連接池),并將其注入到 MyBatis 的配置中。

  3. 配置 SqlSessionFactory:使用 SqlSessionFactoryBean 配置類創(chuàng)建 SqlSessionFactory,并將數(shù)據(jù)源等配置信息傳遞給它。

  4. 掃描 Mapper 接口:使用 MapperScannerConfigurer 類自動(dòng)掃描 Mapper 接口,并將其注冊為 Spring 的 Bean。

  5. 配置事務(wù)管理器:將 Spring 的事務(wù)管理器配置為 MyBatis 的事務(wù)管理器,以確保在 Spring 中管理 MyBatis 的事務(wù)。

  6. 使用 Mapper 接口:在業(yè)務(wù)邏輯中注入并使用 Mapper 接口,調(diào)用其中定義的方法執(zhí)行數(shù)據(jù)庫操作。

pom.xml

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version>
</dependency>
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version>
</dependency>
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version>
</dependency>
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.4.200</version><scope>test</scope>
</dependency>

User.java

public class User {private Long id;private String name;// getters and setters
}

UserMapper.java

public interface UserMapper {List<User> getAllUsers();User getUserById(Long id);void insertUser(User user);void updateUser(User user);void deleteUser(Long id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper"><resultMap id="userResultMap" type="com.example.model.User"><id property="id" column="id"/><result property="name" column="name"/></resultMap><select id="getAllUsers" resultMap="userResultMap">SELECT * FROM users</select><select id="getUserById" resultMap="userResultMap" parameterType="java.lang.Long">SELECT * FROM users WHERE id = #{id}</select><insert id="insertUser" parameterType="com.example.model.User">INSERT INTO users (id, name) VALUES (#{id}, #{name})</insert><update id="updateUser" parameterType="com.example.model.User">UPDATE users SET name = #{name} WHERE id = #{id}</update><delete id="deleteUser" parameterType="java.lang.Long">DELETE FROM users WHERE id = #{id}</delete>
</mapper>

applicationContext.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:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 數(shù)據(jù)源配置 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="org.h2.Driver"/><property name="url" value="jdbc:h2:mem:testdb"/><property name="username" value="sa"/><property name="password" value=""/></bean><!-- MyBatis SqlSessionFactory 配置 --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="mapperLocations" value="classpath*:com/example/mapper/*.xml"/></bean><!-- Mapper 接口自動(dòng)掃描 --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.example.mapper"/></bean><!-- 事務(wù)管理器配置 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 開啟注解驅(qū)動(dòng) --><context:annotation-config/><tx:annotation-driven/></beans>

Demo

public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = context.getBean(UserMapper.class);// 插入用戶User user = new User();user.setId(1L);user.setName("Alice");userMapper.insertUser(user);// 查詢所有用戶List<User> users = userMapper.getAllUsers();System.out.println("All Users:");for (User u : users) {System.out.println(u.getId() + ": " + u.getName());}// 更新用戶user.setName("Bob");userMapper.updateUser(user);// 查詢指定用戶User retrievedUser = userMapper.getUserById(1L);System.out.println("Retrieved User: " + retrievedUser.getName());// 刪除用戶userMapper.deleteUser(1L);}
}

17. 聲明式事務(wù)

  • 編程式事務(wù)管理

將事務(wù)管理代碼嵌到業(yè)務(wù)方法中來控制事務(wù)的提交和回滾
缺點(diǎn):必須在每個(gè)事務(wù)操作業(yè)務(wù)邏輯中包含額外的事務(wù)管理代碼

  • 聲明式事務(wù)管理==(交由容器管理事務(wù))

一般情況下比編程式事務(wù)好用。
將事務(wù)管理代碼從業(yè)務(wù)方法中分離出來,以聲明的方式來實(shí)現(xiàn)事務(wù)管理。
將事務(wù)管理作為橫切關(guān)注點(diǎn),通過aop方法模塊化。Spring中通過Spring AOP框架支持聲明式事務(wù)管理。

聲明式事務(wù)是指在軟件開發(fā)中使用一種聲明性方式來定義事務(wù)的行為,而無需顯式地編寫事務(wù)管理代碼。通常,聲明式事務(wù)是通過在配置文件或注解中指定事務(wù)的屬性和行為來實(shí)現(xiàn)的。

在Java領(lǐng)域中,Spring框架提供了聲明式事務(wù)管理的支持。通過Spring的事務(wù)管理模塊,開發(fā)者可以在方法或類級(jí)別聲明事務(wù)屬性,而不必在代碼中編寫顯式的事務(wù)管理邏輯。在Spring中,常用的聲明式事務(wù)管理方式包括基于XML配置基于注解的配置兩種方式。

  • 基于XML配置的聲明式事務(wù)管理通過在Spring配置文件中定義事務(wù)管理器、事務(wù)通知器以及事務(wù)的切入點(diǎn)來實(shí)現(xiàn)。開發(fā)者可以在XML配置文件中指定哪些方法需要被事務(wù)管理,以及事務(wù)的傳播行為、隔離級(jí)別等屬性。

    **配置聲明式事務(wù)**

        <!-- 配置聲明式事務(wù) --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean>

    **配置好事務(wù)管理器后,去配置事務(wù)的通知**

        <!-- 結(jié)合AOP實(shí)現(xiàn)事物的織入 --><!-- 配置事務(wù)的通知: --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- 配置哪些方法使用什么樣的事務(wù),配置事務(wù)的傳播特性 --><tx:method name="add" propagation="REQUIRED"/><tx:method name="delete" propagation="REQUIRED"/><tx:method name="update" propagation="REQUIRED"/><tx:method name="search*" propagation="REQUIRED"/><tx:method name="get" read-only="true"/><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice>

    **配置AOP**

        <!-- 配置事務(wù)的切入 --><aop:config><aop:pointcut id="txPointcut" expression="execution(* com.github.subei.mapper.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/></aop:config>
  • 基于注解的聲明式事務(wù)管理則是通過在方法上使用@Transactional注解來標(biāo)記需要被事務(wù)管理的方法,開發(fā)者可以在注解中指定事務(wù)的屬性,如傳播行為、隔離級(jí)別等。這種方式簡化了配置,使得事務(wù)管理更加靈活和方便。

    applicationContext.xml

    <context:component-scan base-package="com.example.service" /><!-- 配置數(shù)據(jù)源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/mydb" /><property name="username" value="root" /><property name="password" value="password" />
    </bean><!-- 配置事務(wù)管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
    </bean><!-- 開啟對(duì)注解的支持 -->
    <tx:annotation-driven />
    

    UserService.java

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;@Service
    public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser(User user) {userRepository.save(user);}@Transactionalpublic void deleteUser(long userId) {userRepository.deleteById(userId);}@Transactional(readOnly = true)public User getUserById(long userId) {return userRepository.findById(userId).orElse(null);}// 其他方法...
    }

使用聲明式事務(wù)管理的好處包括:

  1. 簡化事務(wù)管理:?開發(fā)者無需編寫大量的事務(wù)管理代碼,而是通過配置文件或注解來定義事務(wù)的行為,減少了重復(fù)代碼的編寫。
  2. 提高可維護(hù)性:?通過將事務(wù)管理與業(yè)務(wù)邏輯分離,代碼更易于理解和維護(hù)。
  3. 降低耦合性:?聲明式事務(wù)將事務(wù)管理從業(yè)務(wù)邏輯中解耦,使得業(yè)務(wù)邏輯更加純粹,更易于單元測試和重用。
  4. 靈活性:?可以通過配置文件或注解來動(dòng)態(tài)調(diào)整事務(wù)的行為,滿足不同業(yè)務(wù)場景下的需求。

18. spring事務(wù)傳播特性

Spring 事務(wù)傳播特性定義了在一個(gè)方法調(diào)用另一個(gè)方法時(shí),當(dāng)前方法的事務(wù)如何傳播給被調(diào)用方法的行為。Spring 提供了多種事務(wù)傳播特性,可以根據(jù)不同的需求選擇合適的特性。以下是常見的 Spring 事務(wù)傳播特性:

  1. PROPAGATION_REQUIRED(默認(rèn))
  • 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
  • 這是最常用的傳播特性,適用于絕大多數(shù)情況。
  1. PROPAGATION_SUPPORTS
  • 支持當(dāng)前事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)方式執(zhí)行。
  • 如果調(diào)用者在事務(wù)中,則被調(diào)用方法也在該事務(wù)中;如果調(diào)用者不在事務(wù)中,則被調(diào)用方法以非事務(wù)方式執(zhí)行。
  1. PROPAGATION_MANDATORY
  • 強(qiáng)制要求當(dāng)前存在事務(wù),否則拋出異常。
  • 被調(diào)用方法必須在調(diào)用者的事務(wù)中執(zhí)行,否則拋出異常。
  1. PROPAGATION_REQUIRES_NEW
  • 創(chuàng)建一個(gè)新的事務(wù),并掛起當(dāng)前事務(wù)(如果存在)。
  • 被調(diào)用方法總是在新事務(wù)中執(zhí)行,不受調(diào)用者事務(wù)的影響。
  1. PROPAGATION_NOT_SUPPORTED
  • 以非事務(wù)方式執(zhí)行,并掛起當(dāng)前事務(wù)(如果存在)。
  • 被調(diào)用方法總是以非事務(wù)方式執(zhí)行,不受調(diào)用者事務(wù)的影響。
  1. PROPAGATION_NEVER
  • 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • 被調(diào)用方法不允許在任何事務(wù)中執(zhí)行,如果調(diào)用者在事務(wù)中,則拋出異常。
  1. PROPAGATION_NESTED
  • 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)中執(zhí)行;如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
  • 如果調(diào)用者在事務(wù)中,則被調(diào)用方法在嵌套事務(wù)中執(zhí)行;如果調(diào)用者不在事務(wù)中,則行為類似于 PROPAGATION_REQUIRED。

這些傳播特性可以在 @Transactional 注解中使用,例如:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {// ...
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {// ...
}

使用適當(dāng)?shù)氖聞?wù)傳播特性可以確保事務(wù)在多個(gè)方法調(diào)用之間正確地傳播和管理,從而保證數(shù)據(jù)的一致性和完整性。

參考

Spring學(xué)習(xí)目錄(6天) - subeiLY - 博客園

http://www.risenshineclean.com/news/30725.html

相關(guān)文章:

  • 政府網(wǎng)站建設(shè)個(gè)人先進(jìn)推薦材料域名權(quán)重
  • 網(wǎng)站必須做公安部備案51鏈
  • 自己的網(wǎng)站怎么做實(shí)時(shí)監(jiān)控seo推廣優(yōu)勢
  • typecho移植wordpress廣州seo招聘
  • 做會(huì)計(jì)一般關(guān)注什么網(wǎng)站關(guān)鍵詞優(yōu)化推廣
  • 網(wǎng)站建設(shè)的完整流程包括哪些軟件開發(fā)流程八個(gè)步驟
  • 建設(shè)網(wǎng)站商城鄭州seo顧問熱狗hotdoger
  • 長春市疫情防控最新政策天津seo實(shí)戰(zhàn)培訓(xùn)
  • seo做子網(wǎng)站網(wǎng)絡(luò)商城應(yīng)該如何推廣
  • 網(wǎng)站做眾籌需哪些條件china東莞seo
  • 網(wǎng)站建設(shè)誤區(qū)圖交易平臺(tái)官網(wǎng)
  • 深圳模板網(wǎng)站建設(shè)設(shè)計(jì)公司排名優(yōu)化方法
  • 網(wǎng)站域名備案查詢官網(wǎng)百度競價(jià)排名的利與弊
  • 上海建溧建設(shè)集團(tuán)有限公司網(wǎng)站百度網(wǎng)頁版登錄
  • wordpress打不開主頁一點(diǎn)優(yōu)化
  • 找做網(wǎng)站的朋友電商數(shù)據(jù)統(tǒng)計(jì)網(wǎng)站
  • 注冊外貿(mào)公司seo咨詢
  • 哈爾濱網(wǎng)站建設(shè)制作價(jià)格如何推廣一款app
  • 豬八戒做網(wǎng)站靠譜嗎國際最新新聞
  • 網(wǎng)站建設(shè)與開發(fā)做什么足球世界排名國家最新
  • 商城購物網(wǎng)站建設(shè)方案短視頻營銷策略
  • 東莞手機(jī)網(wǎng)站建設(shè)網(wǎng)站怎么優(yōu)化關(guān)鍵詞
  • 遵義做什么網(wǎng)站好seo門戶
  • 石家莊網(wǎng)站運(yùn)營公司最新新聞事件
  • 口碑好的常州做網(wǎng)站app開發(fā)用什么軟件
  • 可以充值的網(wǎng)站怎么做互聯(lián)網(wǎng)金融
  • 煙臺(tái)網(wǎng)站推廣排名競價(jià)推廣代運(yùn)營
  • 做一個(gè)類似京東的網(wǎng)站免費(fèi)發(fā)布推廣的平臺(tái)
  • 南京制作網(wǎng)站公司網(wǎng)站seo1視頻發(fā)布會(huì)
  • php動(dòng)態(tài)網(wǎng)站開發(fā)案例教程china東莞seo