概要
Spring システムの概要
- Spring は、2003 年に登場したフルスタックの軽量 Java オープン ソース フレームワークです。ロッド ジョンソンによって作成されました。Spring を使用すると、Java プログラミングをより高速、簡単、安全に行うことができます。
- Spring はエコシステム、または超接着プラットフォームです。一般的な Spring プロジェクトには、Spring Boot、Spring Framework、Spring Data、Spring Cloud、Spring Cloud Data Flow、Spring Security、Spring GraphQL、Spring Session、Spring Web Services などが含まれます。
- Spring は、プレゼンテーション層の Spring MVC、永続層の Spring JDBC、ビジネス層のトランザクション管理など、多くのエンタープライズ レベルのアプリケーション テクノロジを提供します。Spring は、オープンソースの世界で多数のサードパーティ フレームワークやクラス ライブラリを統合することもでき、徐々に Java EE エンタープライズ アプリケーションで最も広く使用されるオープンソース フレームワークになりました。
- Spring は Spring Framework (Spring Framework) と同等ではありません。これはよくある誤解です
Spring フレームワーク (Spring フレームワーク)
概要
- Spring フレームワーク、つまりSpring Framework フレームワークは、Spring エコロジーの重要なプロジェクトの 1 つであり、他の Spring ファミリ バケット (SpringMVC、SpringBoot、SpringCloud、SpringData など) の基盤およびコアでもあります。
- Spring フレームワークはモジュールに分割されており、アプリケーションは必要なモジュールを選択できます。Spring フレームワークのコアは、構成モデルと依存関係注入メカニズムを含む Core Container (コア コンテナ) モジュールです。
- Spring Framework は、メッセージング、トランザクション データと永続性、Web などのさまざまなアプリケーション アーキテクチャの基本的なサポートを提供します。
- Spring Framework には、サーブレットベースの Spring MVC Web フレームワークと、並列 Spring WebFlux リアクティブ Web フレームワークも含まれています
- Spring フレームワークは、IOC (Inversion of Control) と AOP (Aspect-Oriented Programming) をコアとした、階層化された JavaSE/EE ワンストップの軽量オープンソース フレームワークです。
Spring フレームワークの機能
- 便利な切り離しと簡素化された開発: すべてのオブジェクトの作成と依存関係のメンテナンスが Spring 管理に引き渡されます。
- さまざまな優れたフレームワークの便利な統合: Spring はさまざまな優れたフレームワーク (Struts2、Hibernate、MyBatis) を直接サポートします。
- Java EE API の使用の難しさを軽減: JAVA EE 開発における一部の API (JDBC、JavaMail、リモート呼び出し) のカプセル化を提供します。
- 便利なプログラム テスト: JUnit4 をサポートし、アノテーションを通じて Spring プログラムを簡単にテストできます。
- AOPに対応:プログラムのパーミッション傍受や動作監視などの機能を便利に実現できるアスペクト指向プログラミング
- 宣言型トランザクション: プログラミングを行わずに設定だけでトランザクション管理を完了できます。
Spring フレームワークのアーキテクチャとモジュールの説明
Spring フレームワークの主要モジュール
-
コアコンテナ(コアコンテナ)
これは他のモジュールの基盤であり、Beans モジュール、Core コア モジュール、Context コンテキスト モジュール、SpEL 式言語モジュールで構成されます。
-
コア (spring-core): Spring フレームワークの基礎となる部分 (リソース アクセス、型変換、Spring フレームワークの基本的なコア ツール クラスなど) をカプセル化します。
他の Spring コンポーネントは、他のコンポーネントの基本コアであるこのパッケージ内のクラスを使用する必要があります。また、これらのツール クラスを独自のアプリケーション システムで使用することもできます。
- 外部依存関係: Commons Logging (Log4J)
- jar包:org.springframework spring-core 4.3.7.RELEASE
-
Beans (spring-beans): アクセス構成ファイル、制御の反転 (IOC)、依存関係注入 (DI) などのフレームワークの基本部分を提供します。
- 外部依存関係: spring-core
- jar包:org.springframework spring-beans 4.3.7.RELEASE
-
SpEL (spring-expression): 実行時にオブジェクト グラフのクエリと処理を行うための強力な式言語サポートを提供します。この言語は、プロパティ値の設定と取得、プロパティの割り当て、メソッドの呼び出し、配列の内容へのアクセス、コレクションとインデクサー、論理演算と算術演算、名前付き変数、Spring の IOC コンテナのモニカからの取得をサポートしており、リストの選択と射影、および一般的なリストの集計もサポートしています。
- jar包:org.springframework spring-expression 4.3.7.RELEASE
-
コンテキスト (spring-context): コンテキスト モジュールは、コア モジュールと Beans モジュールに基づいており、Beans モジュールを統合し、リソース バインディング、データ検証、国際化、Java EE サポート、コンテナ ライフ サイクル、イベント伝播などを追加します。
Spring コアに多数の拡張機能を提供します。Spring ApplicationContext 機能を使用するときに必要なすべてのクラス、JDNI で必要なすべてのクラス、インスツルメンテーション コンポーネント、および検証に関連する関連クラスを見つけることができます。
- 外部依存関係: spring-core、spring-beans、spring-expression、spring-aop
- jar包:org.springframework spring-context 4.3.7.RELEASE
依存関係:
-
-
データ アクセス/統合 (データ アクセスと統合)
含まれるモジュール: JDBC、ORM、OXM、JMS、トランザクション
-
JDBC (spring-jdbc): JDBC データへの Spring のアクセスをカプセル化するすべてのクラスが含まれます。
- jar包:org.springframework spring-jdbc 4.3.7.RELEASE
-
ORM (spring-orm): JPA、JDO、Hibernate、MyBatis などを含む「オブジェクト リレーショナル」マッピング フレームワークと統合された API を提供します。
-
OXM (spring-oxm): JAXB、Castor、XMLBean、JiBX、XStream などのオブジェクト/XML マッピングの抽象層実装を提供します。
Java オブジェクトを XML データにマップする、または XML データを Java オブジェクトにマップする
-
JMS (spring-jms): Java メッセージ サービス。「メッセージ プロデューサ、メッセージ コンシューマ」テンプレートのセットを提供します。JMS は、2 つのアプリケーション間で、または非同期通信用の分散システムでメッセージを送信するために使用されます。
-
トランザクション (spring-tx): トランザクション制御、JDBC、Hibernate、JDO、JPA、Bean などの一貫した宣言型およびプログラムによるトランザクション管理のサポート。
- jar包:org.springframework spring-tx 4.3.7.RELEASE
依存関係:
-
-
ウェブ
含まれるモジュール: Web、サーブレット、WebSocket、ポートレット
-
Web (spring-web): マルチファイルのアップロード、サーブレット リスナーを使用した IOC コンテナの初期化、Web アプリケーション コンテキストなどの基本的な Web 統合機能を提供します。
Web アプリケーションの開発時に Spring フレームワークを使用するときに必要なコア クラスが含まれます。これには、Web ApplicationContext 機能を自動的にロードするクラス、Struts および JSF 統合クラス、ファイル アップロード サポート クラス、Filter クラス、および多数のツール補助クラスが含まれます。
- jar包:org.springframework spring-web 4.3.7.RELEASE
-
サーブレット (spring-webmvc): SpringMVC Web フレームワークの実装を提供します。SpringMVC は、アノテーションベースのリクエストリソースインジェクション、よりシンプルなデータバインディング、データ検証など、および非常にシンプルな JSP タグのセットを提供します。
フレームワーク サーブレット、Web MVC フレームワーク、コントローラー、ビューのサポートなど、Spring MVC フレームワークに関連するすべてのクラスが含まれます。
アプリケーションがスタンドアロン MVC フレームワークを使用する場合、この JAR ファイル内のクラスは必要ありません
- jar包:org.springframework spring-webmvc 4.3.7.RELEASE
-
WebSocket: シンプルなインターフェースを提供します。ユーザーはインターフェースを実装するだけで WebSocket サーバーを迅速に構築し、双方向通信を実現します。
-
ポートレット (spring-webmvc-portlet): ポートレット環境での MVC 実装を提供します。
依存関係:
-
-
AOP、アスペクト、スプリングインストゥルメント、メッセージング
aop 部分には 4 つのモジュールが含まれています
-
AOP (spring-aop): アスペクト指向プログラミングを提供し、ロギング、アクセス制御、パフォーマンス統計などの共通機能やビジネスロジック分離技術などの技術を提供し、これらの機能を必要なコードに動的に追加できます。
- jar包:org.springframework spring-aop 4.3.7.RELEASE
-
アスペクト (spring-aspects): 強力で成熟したアスペクト指向プログラミング フレームワークである AspectJ との統合を提供します。
アスペクト指向の機能を Eclipse AJDT などの IDE に簡単に統合するため
- jar包:org.springframework spring-aspects 4.3.7.RELEASE
-
InStrumentation (spring-instrument): 検出、クラス ツールのサポートとクラス ローダーの実装の提供
-
メッセージング: メッセージング アーキテクチャとプロトコルのサポートを提供します。
依存関係:
-
-
テスト: Spring は Junit および TestNG テスト フレームワークをサポートし、いくつかの追加の Spring ベースのテスト機能も提供します。
SpringフレームワークのAPIシステム
Spring Framework の API システムは非常に大規模であり、現時点では BeanFactory と ApplicationContext のみが導入されています。
-
ビーンファクトリー
- BeanFactory は Spring の「心臓部」であり、IOC コンテナのコア インターフェイスであり、IOC の基本機能を定義します。
- Spring はこれを使用して、ドキュメントの構成、Bean のロードの管理、Bean 間の依存関係のインスタンス化と維持、Bean のライフサイクルの責任を負います。
-
アプリケーションコンテキスト
- ApplicationContext は BeanFactory から派生しており、Spring の本体と比較できます。
- ApplicationContext は、BeanFactory に基づいて多くの関数を追加します。
- aop機能とWebアプリケーションをサポート
- MessageSource、国際化されたメッセージへのアクセスを提供します
- BeanFactoryでコーディングすることでしか実現できない多くの機能をコンフィグレーションにより実現します
- ApplicationContext の一般的な実装クラス:
- ClassPathXmlApplicationContext: クラスパス ディレクトリから構成ファイルを読み取ります
- FileSystemXmlApplicationContext: ファイル システムまたは URL から構成ファイルを読み取ります
- AnnotationConfigApplicationContext: 注釈を読み取ります。
アノテーションを使用してコンテナ オブジェクトを構成する場合、このクラスを使用してスプリングコンテナを作成する必要があります
BeanFactory と ApplicationContext の違い:
-
beanFactory は主に Spring フレームワークのインフラストラクチャ、つまり Spring 自体の内部呼び出し用です。
Applicationcontext は主に Spring ユーザー向けです
-
BeanFactroy は、Bean が初めて使用されるときに (getBean() メソッドを呼び出して) Bean をロードしてインスタンス化します。
ApplicationContext は、コンテナの起動時にすべての Bean を一度に作成してロードします。
Spring Boot フレームワークの概要
-
Spring Boot は Spring エコシステムの中でも非常に重要なプロジェクトでもあり、Spring Framework の拡張機能であり、そのフレームワークを提供するフレームワークとも言えます。新しい Spring アプリケーションの初期構築と開発プロセスを簡素化するように設計されています。
-
Spring Boot のサービス範囲は、構成ファイルを簡素化し、Spring アプリケーションのセットアップに必要な XML 構成を排除すること、つまり、Spring アプリケーションを可能な限り自動的に構成することです。
-
Spring Boot は、Tomcat、Jetty、または Undertow を直接埋め込み (WAR ファイルをデプロイする必要はありません)、メトリクス、ヘルスチェック、外部化された構成などの本番環境に対応した機能を提供し、より迅速かつ効率的なアプリケーション開発への道を開きます。
IOC (制御の反転)
IOCの概要、IOCコンテナの動作原理
IOC (Inversion of Control) は設計上のアイデアであり、その目的は、より疎結合なプログラムの設計をガイドすることです。
- Control: オブジェクト制御権を指します。これは、Java におけるオブジェクトの制御権限 (オブジェクトの作成、破棄、およびその他の権限など) として単純に理解できます。
- 反転: クラス内の元のプログラマのアクティブなコントロールから Spring コンテナのコントロールへのオブジェクトのコントロールの反転を指します。
- 主な機能:デカップリング
Spring IOC コンテナは、IOC のアイデアを実装したものです。
- オブジェクトの作成は Spring フレームワークによって管理され、オブジェクトが必要な場合は Spring IOC コンテナから取得できます。
- 基礎となる原則:反省
Spring の IOC コンテナは次のように動作します。
-
Spring の IOC コンテナがロードされると、設定ファイル内の多くの Bean 設定が読み取られます。
-
Bean クラスの値に従って、対応するクラス バイトコード ファイルを検索します。
-
反射技術によりオブジェクトを一つ一つ創り出す
-
作成されたオブジェクトは内部の Map 構造に保存され、使用されるのを待ちます。
-
特定のオブジェクトを使用する必要がある場合、それらを手動で作成する必要はなく、Spring の IOC コンテナから直接作成します。
Spring IOC の概要 (xml)
ケース: Spring の組み込みコンテナーを介してオブジェクトを取得する
手順:
- プロジェクトのインポート依存関係を作成する
- 構成インターフェースと実装クラス
- Spring 構成ファイルを書き込む
- テスト: コンテナからオブジェクトを取得します
プロジェクトのインポート依存関係を作成する
<dependencies>
<!--spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
Daoインターフェースと実装クラスの作成(省略)
Spring構成ファイルを作成する
- Spring 構成ファイルの規則は、一般に applicationContext.xml として知られています。
- Spring 構成ファイルは、リソース ディレクトリに配置されます。
- Spring 構成ファイルには名前空間 (制約) を導入する必要があります。
- Spring 構成ファイル内のタグを使用して、
<bean>
実装クラスのオブジェクト ID と完全なクラス名を定義します。
リソース ディレクトリに 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: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">
<!--
定义配置信息
id:唯一标志(获取的时候,调用容器的getBean("id"))
class:实现类的全限定类名
-->
<!--把数据库连接池对象放入IOC容器-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!--把QueryRunner放入到IOC容器中-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!--把dao对象交给IOC容器-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"/>
</bean>
<!--把service交给IOC容器-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
テスト
import cn.test.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试从容器中获取对象
*/
public class UserDaoTest {
public static void main(String[] args) {
//1、根据配置文件获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、调用容器的方法,获取对象
UserDao userDao = (UserDao)ac.getBean("userDao");
//3、测试
userDao.save();
}
}
プロセス分析を実行する
Spring Beanオブジェクト(xml)の初期化
方法 1: デフォルトのパラメーターなしコンストラクター
IOC コンテナはリフレクションを通じて呼び出され、デフォルトの引数なしのコンストラクターに従ってクラス オブジェクトが作成されます。Bean にデフォルトの引数なしのコンストラクターがない場合、作成は失敗します。
<bean id="userDao" class="cn.test.dao.impl.UserDaoImpl"></bean>
方法 2: ファクトリ モードでオブジェクトを作成する
Spring のファクトリ パターンを通じてオブジェクトを作成することもできます。ファクトリ パターンには次の 2 種類があります。
- 静的ファクトリ: ファクトリのインスタンスを生成せず、ファクトリの静的メソッドを直接呼び出してオブジェクトを作成します。
- インスタンス ファクトリ: まずファクトリのインスタンスを生成し、次にファクトリ インスタンスのメソッドを呼び出してオブジェクトを作成します。
(1) Javaコード記述ファクトリークラス
public class FactroyCreateBean {
// 静态工厂
public static UserDao createUserDao(){
return new UserDaoImpl();
}
// 实例工厂
public UserDao createUserDaoSimple(){
return new UserDaoImpl();
}
}
(2) XML設定ファイルの構成
<!--使用静态工厂创建对象-->
<bean id="userDao1" class="cn.test.factory.FactroyCreateBean" factory-method="createUserDao"/>
<!--使用实例工厂创建对象-->
<bean id="factroyCreateBean" class="cn.test.factory.FactroyCreateBean"/>
<bean id="userDao2" factory-bean="factroyCreateBean" factory-method="createUserDaoSimple"/>
Spring Beanのライフサイクル
概要、ライフサイクルフローチャート
Beanオブジェクトのライフサイクルとは、Beanの作成から破棄までの期間を指します。
大まかなライフサイクル:
-
春のシングルトン オブジェクトのライフ サイクルは次のとおりです。
Born: IOC コンテナがロードされたときに誕生します。
生存: IOC コンテナが実行されているときの生存
死亡: IOC コンテナが破壊されたときに死亡
-
春のオブジェクトの複数のインスタンスのライフサイクルは次のとおりです。
Birth: オブジェクト使用時の誕生
サバイバル:常に生きている
死亡: Java ガベージ コレクターによって否定されました
きめ細かいライフサイクル:
誕生の過程
-
Bean オブジェクトをインスタンス化する [IOC]
-
オブジェクト属性に値を割り当てる [DI]
-
実装された Aware インターフェイスを処理する
① BeanがBeanNameAwareインタフェースを実装している場合、実装されているsetBeanName(String beanId)メソッドが呼び出され、Spring設定ファイルのBeanのid値が渡されます。
② Bean が BeanFactoryAware インターフェースを実装している場合、Bean が実装している setBeanFactory() メソッドを呼び出し、Spring ファクトリ自体を渡します。
③ Bean が ApplicationContextAware インターフェースを実装している場合、setApplicationContext(ApplicationContext) メソッドを呼び出して Spring コンテキストを渡します。
-
BeanPostProcessor インターフェースの postProcessBeforeInitialization メソッドを通じて Bean オブジェクトを前処理します。
-
InitializingBean インターフェースの afterPropertiesSet メソッドを通じて Bean オブジェクトを処理します。
-
init-method メソッドを指定して Bean オブジェクトを処理する
-
Bean オブジェクトは、BeanPostProcessor インターフェースの postProcessAfterInitialization メソッドを通じて後処理されます。このステップでは、Bean オブジェクトは完全に正常に作成されており、キャッシュと同様の作業を実行できます。
死のプロセス
- Bean が DisposableBean インターフェースを実装している場合、実装されている destroy() メソッドが呼び出されます。
- destroy-method メソッドを指定すると、Bean オブジェクトが破棄される前に自動的に実行できます。
ライフサイクルのフローチャート
テストケース:
public class UserDaoImpl implements UserDao, BeanNameAware, BeanFactoryAware,ApplicationContextAware, InitializingBean, DisposableBean {
public UserDaoImpl() {
System.out.println("IOC");
}
private Integer id;
public void setId(Integer id) {
System.out.println("DI");
this.id = id;
}
@Override
public void setBeanName(String s) {
System.out.println("BeanNameAware:"+s);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean的afterPropertiesSet");
}
public void initMethod(){
System.out.println("init-method");
}
@Override
public void save() {
System.out.println("保存成功!");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean的destroy");
}
public void destroyMethod(){
System.out.println("destroy-method");
}
}
public class MyBeanProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor的before");
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor的after");
return bean;
}
}
<?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">
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" init-method="initMethod" destroy method="destroyMethod">
<property name="id" value="1"/>
</bean>
<bean class="com.itheima.processor.MyBeanProcessor"/>
</beans>
// 测试类
public class UserDaoImplTest {
@Test
public void save() {
ClassPathXmlApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = ac.getBean(UserDao.class);
userDao.save();
ac.close();
}
}
Spring Beanの範囲
参考:https://blog.csdn.net/weixin_38676276/article/details/90382350
-
シングルトン: シングルトン。Spring IOC コンテナには共有 Bean インスタンスが 1 つだけあります。デフォルト
Spring が applicationContext コンテナを作成するとき、Spring はスコープのすべてのインスタンスを初期化します。さらに、lazy-init は前処理を回避できます。
-
プロトタイプ: プロトタイプ (複数のインスタンス)
Bean がコンテナから呼び出されるたびに、新しいインスタンスが返されます。つまり、getBean() の各呼び出しは new XxxBean() を実行するのと同じです。
作成後は、Spring がそれを管理できなくなります
(以下はWebプロジェクト下でのみ使用されるスコープです)
-
request : 現在の HTTP リクエスト内でのみ有効です
HTTP リクエストごとに新しい Bean が作成されます
スプリングの作成後、スプリングはリッスンし続け、リクエストが処理されるとインスタンスは破棄されます。
-
session : 現在の HTTP セッション内でのみ有効
同じ HTTP セッションは Bean を共有し、異なるセッションは異なる Bean を使用します。
Spring が作成された後も listen を続け、HTTP セッションが最終的に破棄されると、HTTP セッションのスコープ内の Bean も破棄されます。
-
global-session : グローバル Web ドメイン。サーブレットのアプリケーションに似ています。通常、ポートレットアプリケーション環境で使用されます
Bean構成情報(xml)の定義
-
id: 固有のフラグ (取得時にコンテナの getBean("id") を呼び出します)
-
class: 実装クラスの完全修飾クラス名
-
スコープ: オブジェクト スコープ (シングルトン (デフォルト) | プロトタイプ | リクエスト | セッション | グローバルセッション)
-
init-method: オブジェクトが正常に作成された後、指定された初期化メソッド
-
destroy-method: コンテナがオブジェクトを閉じて破棄する前に実行される破棄メソッド
スコープ=singleton (singleton) の場合のみ有効です
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="prototype"
init-method="init" destroy-method="destroy"/>
依存関係の注入 (xml)
依存性注入: 依存性注入 (DI)。これは、Spring フレームワークのコア IOC の特定の実装です。プログラムを作成するとき、制御の反転によってオブジェクトの作成が Spring に渡され、コード内でオブジェクトが必要になったときに依存オブジェクトが挿入されます。
IOC には依存関係ルックアップ、依存関係注入という 2 つの機能があります。
本質: オブジェクトのプライベート プロパティへの値の割り当て
-
施工方法
-
setメソッド呼び出し
コンストラクターインジェクション
- パラメーター化されたコンストラクターをオブジェクトに追加する
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
public UserServiceImpl(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public void save() {
System.out.println(name + age);
}
}
- Spring 構成ファイルで、Bean タグを使用してオブジェクトの作成を構成します (コンストラクター パラメーターを追加する必要があります)。
<!--
constructor-arg 设置对象的构造方法参数 (一个参数配置一个constructor-arg标签)
name:构造参数名
type:构造参数类型
index:构造参数,参数索引位置(从0开始)
以上三个属性,用于定位构造方法参数位置(三选一即可)
value: 对基本数据类型的参数赋值(8大数据类型 + String)
ref: 对对象属性的参数赋值。即必须得是在配置文件中配置过的bean
以上两个属性,用于对构造参数赋值
-->
<bean id="userService" class="cn.test.service.impl.UserServiceImpl">
<constructor-arg name="age" value="12"></constructor-arg>
<constructor-arg name="name" value="王者荣耀"></constructor-arg>
</bean>
セットメソッドインジェクション
-
プロパティのsetメソッドを提供します
-
Spring 構成ファイルでは、Bean とプロパティ構成を組み合わせて set メソッドが呼び出されます。
<bean id="book" class="com.itheima.spring.Book"> <!-- name:找的是类中 set 方法后面的部分 ref:给属性赋值(其他bean类型) value:给属性赋值(8大基本数据类型 + string类型) --> <property name="name" value="程序员" /> <property name="price" value="1" /> <property name="publishDate" ref="date" /> </bean>
複合型 (コレクション) を注入する
クラス内のコレクションメンバーに値を渡すにはsetメソッドインジェクション方式も使いますが、変数のデータ型はすべてコレクションです。ここでは、配列、リスト、セット、マップ、プロパティの挿入について説明します。
(1) 配列データの注入
セットメソッドを設定する
public class UserServiceImpl implements UserService {
/**
* 注入数组(array数组,list集合,set集合)
*/
private String [] names;
private List<String> lists;
private Set<String> sets;
public void setNames(String[] names) {
this.names = names;
}
public void setLists(List<String> lists) {
this.lists = lists;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
@Override
public void save() {
}
}
スプリング構成ファイル
<bean id="book" class="com.itheima.spring.Book">
<!-- List -->
<property name="list">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<!--Set-->
<property name="set">
<set>
<value>3</value>
<value>4</value>
</set>
</property>
<!--数组-->
<property name="array">
<array>
<value>5</value>
<value>6</value>
</array>
</property>
<!--Map-->
<property name="map">
<map>
<entry key="7" value="7-1" />
<entry key="8" value="8-1" />
</map>
</property>
<!--Properties-->
<property name="properties">
<props>
<prop key="9">9-1</prop>
<prop key="10">10-1</prop>
</props>
</property>
</bean>
Spring フレームワークのアノテーション構成
導入、アノテーション、XML設定の対応
-
アノテーションとxmlはSpringが提供する設定形式であり、機能は全く同じで、どちらもプログラム間の結合を軽減するためのものです。
-
アノテーションのメリットは設定が簡単なこと、xmlのメリットはソースコードの変更が不要であり、エンタープライズ開発において柔軟に利用できることです。
-
Spring のアノテーション構成
- 注釈 + XML 構成 (注釈サポートを有効にする)
- 純粋なアノテーション (Spring Boot + クラウド))
-
注: Spring でアノテーション開発を使用するには、アノテーション (ioc アノテーション、aop アノテーション、トランザクション アノテーション) の機能サポートを有効にする必要があります。
アノテーションと XML 設定の対応:
XML設定 | アノテーションの設定 | 説明する |
---|---|---|
< bean id=”” class=”” > | @コンポーネント @コントローラー @サービス @リポジトリ | インスタンス化されたクラスのオブジェクトを Spring コンテナ管理に配置します。 |
< プロパティ名 = “” ref = “”> | @Autowired @Qualifier @Resource | Springコンテナからオブジェクトインジェクションプロパティを取得する |
< プロパティ名 = “” 値 = “”> | @価値 | Bean の単純なプロパティ注入 |
< Bean スコープ=””> | @範囲 | Bean のスコープを制御する |
< Bean init-method=“init” destroy method=“destory” /> |
@PostConstruct @PreDestroy | Bean の作成後、破棄前に呼び出されるメソッド |
パッケージ スキャンを有効にする (IOC アノテーション サポートを有効にする)
パッケージのスキャンを有効にします (つまり、IOC アノテーションのサポートを有効にします)。
- 指定されたパッケージ内のすべての Java クラスの Spring アノテーションをスキャンします
- クラスの IOC アノテーションがスキャンされると、現在のクラスは管理のために IOC コンテナに渡されます。コンテナが起動すると、オブジェクトが自動的に作成され、コンテナに保存されます。
- スキャンによってプロパティに DI アノテーションがあることが検出された場合は、依存関係注入のルールに従ってプロパティに値を注入します。
方法 1: XML 構成モードでパッケージ スキャンを有効にする
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 开启包扫描
- base-package:包名(自动扫描此包以及此包下的所有子包)
-->
<context:component-scan base-package="cn.test"></context:component-scan>
</beans>
方法 2: クラス アノテーションを構成してパッケージ スキャンを有効にする
/**
* 1、声明配置类:@Configuration
* 2、开启包扫描:@ComponentScan
*/
@Configuration
@ComponentScan(basePackages = "cn.test")
public class SpringConfig {
}
IOC アノテーション (オブジェクト作成用のアノテーション)
IOC アノテーション (オブジェクト作成用のアノテーション) の説明:
-
IOC コンテナによって管理されるクラスにマークが付けられ、オブジェクトの作成がコンテナ管理に引き継がれることを示します。
-
Spring コンテナが起動すると、パッケージ スキャン設定に従って IOC アノテーションが自動的にスキャンされ、リフレクションによってアノテーション付きオブジェクトが作成され、コンテナに格納され、コンテナ管理が委任されます。
-
デフォルトでコンテナに格納されるid(一意の識別子)は、現在のクラス名の頭文字の小文字です。value 属性を使用して、コンテナに保存されているオブジェクトの ID をカスタマイズできます。
全部で 4 つあります。
-
**@Controller**:一般标注在表现层(web层)的类上
-
**@Service**: 一般标注在业务层(service层)的类上
-
**@Repository**: 一般标注在持久层(dao层)的类上
-
**@Component**:组件, 非三层模式范围的类上使用
XML の次の構成と同等
<bean id="userDaoImpl" class="cn.test.dao.impl.UserDaoImpl"></bean>
ライフサイクルに関する注意事項
-
@Scope : クラス上のオブジェクトのスコープを構成します。
value 属性でスコープ (singleton|prototype) を指定します。デフォルトは singleton (singleton) です。
-
@PostConstruct : 構成オブジェクト作成後のトリガーアクション
オブジェクトの作成時に自動的に実行されるメソッド。XML 設定ファイルの init-method と同等
-
@PreDestroy : 構成オブジェクトが破棄される前のトリガーアクション (スコープ = シングルトンの場合のみ有効)
コンテナが閉じられ、オブジェクトが破棄される前に実行されるメソッド。XML 設定ファイルの destroy-method と同等
例:
@Repository
@Scope(value="singleton")
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("创建UserDaoImpl");
}
@Override
public void save() {
System.out.println("调用dao保存数据");
}
//初始化方法:在对象创建完成之后执行
@PostConstruct
public void init() {
System.out.println("执行init方法");
}
//销毁方法:在容器关闭对象销毁之前执行
@PreDestroy
public void destory() {
System.out.println("执行destory方法");
}
}
DI アノテーション (依存性注入のアノテーション)
DI アノテーションは、set メソッドやコンストラクターに頼らずに、属性に値を直接割り当てることと同等です。
@Autowired
使用法 1: 属性にマークを付ける
-
値をプロパティに直接割り当てます (@Autowired 依存関係注入を介して、set メソッドを構成する必要はありません)。
-
デフォルトでは、タイプ別の形式で(タイプ、つまりインターフェイスのタイプに従って)IOCコンテナからオブジェクトを検索し、プロパティに値を注入します。
-
IOCコンテナ内に属性と同じ型のオブジェクトが複数ある場合(1つのインターフェースに複数の実装クラスがある場合)、
-
一意の識別子としての属性名 (名前による) に従ってコンテナからオブジェクトを検索し、値を属性に注入します。
-
@Qualifierアノテーションと併用して、コンテナからオブジェクトを検索し、プロパティに値を注入するための一意のフラグを指定することもできます。
- 値: IOC コンテナ内のオブジェクトの一意の識別子 (id) を指定します。
注: @Qualifier は @Autowired と組み合わせてのみ使用できます
-
使用法 2: メソッドにマーク
- 現在のメソッドが自動的に実行されることを示します。メソッドにパラメータがある場合、IOC コンテナから同じタイプのオブジェクトを自動的に見つけてパラメータに値を渡します。
- @Qualifier("IOC コンテナ内のオブジェクト ID") アノテーションをパラメータに追加して、名前でオブジェクトを検索し、値をパラメータに渡すこともできます。
@Autowired を使用する場合の注意事項
-
@Autowired アノテーションは、Spring コンテナーによって管理されるクラス (@Controller などの IOC アノテーションが付けられている) でのみ使用できます。
-
自動挿入は権限修飾子とは何の関係もありません。プライベートに変更されたフィールドも自動的に挿入できます。
-
デフォルトでは、 @Autowired アノテーションを使用して自動的に挿入されたプロパティをアセンブルする必要があります (Spring コンテナー ホスティング)
注入用のコンテナ内にこのタイプの Bean が見つからない場合は、エラーが報告されます。
アセンブルが許可されていない場合は、 @Autowired の required 属性を false に設定できます。
-
@Autowired はタイプベースのインジェクションであり、現在のタイプ プロパティがコンテナ内に Bean を 1 つだけ持つ場合、プロパティ名は制限されませんが、一般にクラス名の最初の文字を小文字にするルールに従うことをお勧めします
-
現在のプロパティ タイプのコンテナ内に複数の Bean がある場合、Bean 名をプロパティ名または @Qualifier アノテーションで同時に指定する必要があります
例:
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier(value = "userDao2") // 从容器中获取指定唯一标志的对象
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
@価値
プロパティで構成されます。と同等の単純なデータ型の注入に使用できます< property name="" value="" >
が、通常はこの方法では使用されません。
通常、プロパティ構成ファイルまたはレジストリ構成ファイルの内容を解析するために使用されます。
- キー値、構文規則に従って対応する値を取得します: @Value(“${key}”)
- 使用手順:
- プロパティ設定ファイルは管理のために Spring コンテナに渡されます。
- @Value を通じて、コンテナから構成アイテムを取得し、それを注入します
例:
(1) jdbc.propertiesファイルの設定
jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///heima23
(2) 設定ファイルをSpringに引き渡す
スプリング構成ファイル applicationContext.xml を変更します。
<!--将properties文件,交给spring管理-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
(3) 属性インジェクション
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
public void save() {
System.out.println(username);
System.out.println(password);
System.out.println(driver);
System.out.println(url);
System.out.println("调用dao11111完成保存");
}
}
@リソース (理解)
jdk によって提供される依存関係注入アノテーション。jdk9 以降では削除されました。
- 属性にのみ配置できます
- 属性名が最初に IOC コンテナ内のオブジェクト ID と照合され、[名前によって] 属性に値が挿入されることを示します。
- 成功しない場合は、IOC コンテナ内の同じタイプのオブジェクトと一致するように、現在の属性のタイプに従って値を注入し続けます [タイプ別]
- 名前属性 @Resource(name = "object id") が指定されている場合、値はオブジェクト ID に従ってのみ注入できます。
例:
@Service
public class UserServiceImpl implements UserService {
@Resource(name = "userDao1")
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
構成構成クラス (アノテーション)
構成クラスの一般的なアノテーション:
-
@Configuration : クラスにアノテーションが付けられ、クラスを Spring 構成クラスとして宣言します
Spring が起動すると、すべての設定クラスを自動的にスキャンしてロードし、Spring コンテナ (アプリケーション コンテキスト) を設定し、設定クラスの Bean をコンテナ管理に配置します。
-
@Bean : Spring 構成クラスのメソッドにアノテーションが付けられ、Bean オブジェクトを IOC コンテナに登録します
- name属性: 生成された Bean の一意のフラグを指定します
Spring コンテナが起動すると、@Bean で設定されたすべてのメソッドが自動的にスキャンおよび実行され、戻り値が Spring コンテナに格納されます。
知らせ:
- マークされたメソッドはインスタンスを返す必要があります
- マークされたメソッドは依存属性パラメータを設定でき、Spring はコンテナから依存オブジェクトを自動的に取得し、メソッドを自動的に呼び出します。
-
@ComponentScan : パッケージ スキャン (Spring IOC アノテーションでサポート) をオンにし、デフォルトで現在のパッケージとサブパッケージの下にあるすべてのクラスをスキャンします。
- basePackage属性: スキャンされたパッケージのパスを指定します。ロード時間を短縮できる
クラスの IOC アノテーションがスキャンされると、現在のクラスは管理のために IOC コンテナに渡されます。コンテナが起動すると、オブジェクトが自動的に作成され、コンテナに保存されます。
スキャンによってプロパティに DI アノテーションがあることが検出された場合は、依存関係注入のルールに従ってプロパティに値を注入します。
-
@PropertySource : ローカル プロパティ ファイルを Spring コンテナ管理にロードします
- value属性: ローカル プロパティのパスを指定します
-
@Import : 他の構成クラスのコンテンツを構成クラスにインポートします
- value属性: 他の構成クラスのクラスのクラスパスを指定します。
Spring は複数の構成クラス (構成クラスのモジュール) をサポートしています。構成クラスが肥大化している場合は、構成クラスを分割して、メイン構成クラスにサブ構成クラスを導入できます (サブ構成クラスに構成アノテーションは必要ありません)。
例:
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
@Configuration // 声明配置类
@ComponentScan(basePackages = "cn.test") // 开启包扫描,并指定包扫描路径
@PropertySource(value="jdbc.properties") // 通过注解将此文件交给spring容器管理
@Import(value=DataSourceConfig.class) // 引入其他的配置类
public class SpringConfig {
@Bean
public QueryRunner getQueryRunner(DataSource dataSource) {
QueryRunner qr = new QueryRunner(dataSource);
return qr;
}
}
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
// 子配置类
public class DataSourceConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driver}")
private String driver;
@Bean
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
return dataSource;
}
}
# properties文件
jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///heima23
AOP (アスペクト指向プログラミング)
コンセプト
AOP ((アスペクト指向プログラミング) アスペクト指向プログラミング
- アイデアであり、ソースコードを変更せずに元の機能を強化することが目的です
- プリコンパイルとランタイムダイナミックプロキシによりプログラムの機能を一元的に保守する技術
- AOP は OOP の後継となるプログラミング パラダイムであり、OOP に基づいて水平開発が行われます。
- AOP が研究するのは、各層をどのように開発するかではなく、同じ層にあるモジュール間の共通機能です。例: トランザクション、ログ、統計
Spring AOPはAOPの考え方を実装したもので、業務には関係ないが業務モジュールで共有されるロジックをカプセル化し、システム内のコードの重複を減らし、モジュール間の結合を減らします。さらに、AOP は、ログ、トランザクション、権限などのシステム レベルの問題も解決できます。
Spring の最下層は、動的プロキシを通じて実装された AOP です。jdk と cglib ダイナミック プロキシを同時にサポートします。Spring は、プロキシされたクラスにインターフェイスがあるかどうかに応じて、プロキシ メソッドを自動的に選択します。
-
インターフェースがある場合は、JDK 動的プロキシを使用します (CGLIB 動的プロキシの使用を強制することもできます)
-
インターフェースがない場合は、CGLIB動的プロキシ方式を使用します。
Spring AOP ワークフロー:
開発フェーズで個別に開発し、運用フェーズで組み立てて実行する
-
開発フェーズ (開発者によって完了)
-
共通機能の開発と機能強化
-
非共通関数を開発してポイントカットにする
-
設定ファイルで、ポイントカットと拡張機能の関係、つまりアスペクトを宣言します。
-
-
実行フェーズ/コンテナ開始フェーズ (AOP 完了)
- Springは設定ファイル内のアスペクト情報を読み込み、アスペクト内の記述に従い、対象オブジェクトのポイントカットメソッドに拡張機能を追加し、動的にプロキシオブジェクトを作成します。
- 最後に、プロキシ オブジェクトをコンテナに配置します (注: 動的プロキシ オブジェクトはコンテナに保存されます)。
用語と説明
-
ターゲット オブジェクト(target): 拡張する必要があるオブジェクト、つまりポイントカット メソッドが配置されているオブジェクト
-
接続ポイント(jointPoint): プロキシ オブジェクト内のメソッド
-
PointCut : 接続点をカット (一致) する AOP のルールに従って、一致した点は pointCut と呼ばれます。つまり、拡張する必要があるメソッドです。
-
ポイントカット式:実行(メソッド修飾子の戻り値型パッケージ名.クラス名.メソッド名(パラメータ))
ポイントカット式の役割: 接続ポイント (すべてのメソッド) でポイントカット (拡張メソッド) を選択するための一連のルールを定義します。
ワイルドカードがサポートされています。
* // 一个或多个任意字符 .. // 配置到参数上,标识任意参数
- メソッド修飾子は省略可能
- メソッドの戻り値は、* によって任意の戻り値の型を識別できます。
- パッケージ名は * によって任意のパッケージを識別できます。
- クラス名は * によって任意のクラスを識別できます。
- メソッド名は * によって任意のメソッドを識別できます。
- 引数は任意の引数で識別できます
// 切入点的全统配方式,表示整个项目中所有类中所有方法都要被增强 execution(* *..*.*(..)) // 最常用的切入点表达式:精确到具体包路径 execution(* com.test.controller.*.*(..))
-
-
強化 (アドバイス): 特定の強化。拡張メソッドはポイントカット メソッドのどこで実行されますか
Spring AOP アドバイス (拡張) は 5 つのタイプに分かれています。
-
事前アドバイス (前): ポイントカットが実行される前に実行します。
-
復帰後:ポイントカットの通常動作終了後に実行
-
例外通知(スロー後):ポイントカットで例外が発生した場合に実行されます。
-
最終アドバイス (後): ポイントカットの最終実行時
-
サラウンド通知 (around): 1 つのメソッドで複数の拡張ロジックを定義する特別な通知 (動的プロキシを手動で定義するのと同様)
通知をラップアラウンドするカスタム メソッド:
- パラメータ ProceedingJoinPoint: プロキシ オブジェクトのメソッド
- 戻り値: 拡張メソッドの戻り値
-
-
プロキシ オブジェクト(プロキシ): ターゲット オブジェクトがプロキシ オブジェクトになるように拡張されます。
-
アスペクト:アドバイスとポイントカットを含むデザインコンセプトです。アスペクト = エントリーポイント + エンハンスメント
Advice は Aspect のタスクとそれをいつ実行するかを定義し、Pointcut はどこに割り込むかを定義します。
つまり、Aspect はどのような拡張機能を定義し、どのコアメソッドのどこに切り込むのかということです。
-
織る:行為。拡張コードをコア コードに追加するプロセスはウィービングと呼ばれます
Spring AOP の構成 (xml)
XML ファイル構成のエントリ ポイント
- <aop:ポイントカット
- id: 現在のポイントカットの一意の符号
- 式: ポイントカット式
XMLファイル構成の側面
- <aop:aspect : アスペクトを構成する
- id: 現在のアスペクトの一意の識別子
- ref: 現在のアスペクトに対してどのアドバイスを使用するかを指定します
XML ファイル構成通知タイプ
- <aop:before: ポイントカット メソッドでアドバイスが実行される場所を指定します
- Method : アスペクトクラスの拡張メソッド名
- pointcut-ref: ポイントカットの ID
XML ファイル構成 AOP の例
<!--声明AOP配置-->
<aop:config>
<!-- 配置切入点(被增强的方法) -->
<aop:pointcut id="pt" expression="execution(* cn.test.dao.impl.*.*(..))"/>
<!--配置切面-->
<aop:aspect ref="logger">
<!-- 配置通知类型 -->
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pt"></aop:before>
<!-- 后置通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pt"></aop:after>-->
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"></aop:around>
</aop:aspect>
</aop:config>
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类:此类中具有所有的增强代码逻辑
*/
public class Logger {
/**
* 前置通知:执行被代理对象方法之前执行
* 方法:无参数,无返回值
*/
public void before() {
System.out.println("执行前置通知");
}
/**
* 后置后置:正常执行被代理对象方法获取返回值之后执行
*/
public void afterReturning() {
System.out.println("执行后置通知");
}
/**
* 异常通知:执行代码抛出异常的时候执行
*/
public void afterThrowing() {
System.out.println("执行异常通知");
}
/**
* 最终通知:finally代码块中执行的逻辑
*/
public void after() {
System.out.println("执行最终通知");
}
/**
* 环绕通知:在一个方法中定义多个增强逻辑
*/
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object obj = null;
try {
System.out.println("执行前置通知");
//执行被代理对象方法
obj = pjp.proceed();
System.out.println("执行后置通知");
}catch (Exception e){
System.out.println("执行异常通知");
}finally {
System.out.println("执行最终通知");
}
return obj;
}
Spring AOP の構成 (アノテーション)
AOP アノテーションには 2 つのタイプがあります。
- XMLとアノテーションを組み合わせた構成方法
- 純粋なアノテーションに基づく設定方法
AOP アノテーションのサポートを有効にする (xml モード)
XML設定ファイル
- IOC アノテーション、パッケージ スキャンのサポートを有効にする
- カスタマイズされたオブジェクト、オブジェクトの作成、IOC アノテーションによる依存関係の注入
- サードパーティのオブジェクト、XML 構成によるオブジェクトの作成および依存関係の挿入
- AOP アノテーションのサポート、自動プロキシを有効にする
<?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="cn.test"></context:component-scan>
<!--开启对AOP注解的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--在切面类中通过注解完成AOP配置-->
</beans>
AOPアノテーションサポートを有効にする(設定方法)
- @EnableAspectJAutoProxy : aop アノテーションのサポートを有効にするために構成クラスでマークされています
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 配置类
*/
@Configuration
@ComponentScan(basePackages = "cn.test")
@EnableAspectJAutoProxy
public class SpringConfig {
}
アスペクトクラス(アノテーション)
-
@Aspect : カスタム クラスにアノテーションが付けられ、アスペクト クラスを宣言します
注: アスペクト クラスにも IOC アノテーション (@Component) を付け、Spring コンテナ管理に引き渡す必要があります。
-
アスペクト クラスの通知 (拡張) メソッドのアノテーションによって通知タイプを構成します。
- @Before : 事前アドバイス
- @AfterReturning : 通知を投稿
- @AfterThrowing : 例外通知
- @After : 最終通知
- @Around : 通知の周囲
通知アノテーションの属性:
-
value / argNames 属性: @Pointcut() によって注釈が付けられたポイントカット式またはメソッド名
@Around("pt()") public Object around(ProceedingJoinPoint pjp)
-
@Pointcut : アスペクト クラスの空のメソッドに注釈が付けられ、パブリック ポイントカット式を抽出します
- value / argNames 属性: ポイントカット式
- 通知アノテーション設定メソッド名 () でパブリック ポイントカット式を導入できる
@Pointcut(value="execution(* cn.test.service.impl.*.*(..))") public void pt() { }
/**
* 切面类:此类中具有所有的增强代码逻辑
*/
@Component
@Aspect
public class Logger {
/**
* 前置通知:执行被代理对象方法之前执行
* 方法:无参数,无返回值
*/
//@Before(value="execution( * cn.test.dao.impl.*.*(..) )")
public void before() {
System.out.println("执行前置通知");
}
/**
* 后置通知:正常执行被代理对象方法获取返回值之后执行
*/
//@AfterReturning(value="execution( * cn.test.dao.impl.*.*(..) )")
public void afterReturning() {
System.out.println("执行后置通知");
}
/**
* 异常通知:执行代码抛出异常的时候执行
*/
// @AfterThrowing("execution( * cn.test.dao.impl.*.*(..) )")
public void afterThrowing() {
System.out.println("执行异常通知");
}
/**
* 最终通知:finally代码块中执行的逻辑
*/
//@After("execution( * cn.test.dao.impl.*.*(..) )")
public void after() {
System.out.println("执行最终通知");
}
/**
* 环绕通知:在一个方法中定义多个增强逻辑
*/
//@Around("execution( * cn.test.dao.impl.*.*(..) )")
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object obj = null;
try {
System.out.println("1执行前置通知");
//执行被代理对象方法
obj = pjp.proceed();
System.out.println("2执行后置通知");
}catch (Exception e){
System.out.println("3执行异常通知");
}finally {
System.out.println("4执行最终通知");
}
return obj;
}
// @Pointcut:抽取公共的切入点表达式
@Pointcut(value="execution(* cn.test.service.impl.*.*(..))")
public void pt() {
}
}
4 つの通知の実行順序
参考:https://blog.csdn.net/qq_45193304/article/details/109430545
Spring AOP の 4 つの主要な通知 (事前、事後、例外、最終通知) の実行シーケンスは非常に複雑で、Spring のバージョン、設定シーケンス、設定方法 (xml | アノテーション) に関連するため、サラウンド通知を使用することをお勧めします。
xml メソッドは、aop の実行順序を正しい順序で構成します。
try {
// 前置通知(before) : 在切点运行之前执行
// 切点执行,被代理对象方法调用
// 后置通知(after-returning): 在切点正常运行结束之后执行
}catch (Exception e){
// 异常通知(after-throwing): 在切点发生异常的时候执行
}finally {
// 最终通知(after): 在切点的最终执行
}
アノテーション メソッドは、aop の実行順序を正しい順序で構成します。
try{
try{
//@Before -- 首先执行前置通知
method.invoke(..); -- 然后执行切入点方法
}finally{
//@After -- 再而肯定会执行最终通知 --- 注解配置的注意点
}
//@AfterReturning -- 如果没有异常,则继续执行后置通知
return; -- 返回结果
}catch(){
//@AfterThrowing -- 如果有异常,则执行异常通知
}
Spring のトランザクション管理
Spring は、プログラムによるトランザクションと宣言的なトランザクションという 2 種類のトランザクション管理をサポートしています。当局者は宣言型トランザクションの使用を強く推奨しています。
-
プログラムによるトランザクション: ビジネス コードとトランザクション コードを一緒に記述します。結合度が高すぎるため、開発では使用されません。
-
宣言的トランザクション:
-
宣言的トランザクションは AOP に基づいています。
その本質は、メソッドの前後でインターセプトし、メソッドの開始前にトランザクションを作成し、メソッドの終了後に実行ステータスに応じてトランザクションをコミットまたはロールバックすることです。
-
宣言型トランザクションの利点: トランザクション コードとビジネス コードを完全に分離して開発し、コンフィギュレーションを通じて実行時のアセンブリと操作を実現できます。
-
宣言型トランザクションの不十分さ: メソッド レベルにのみ適用でき、プログラム トランザクションのようなコード ブロック レベルには適用できません。
-
Springのトランザクション管理に関するAPI
Spring のトランザクション制御は主に次の 3 つの API を通じて実装されます。
- PlatformTransactionManager インターフェイス: トランザクション管理を担当するトランザクション マネージャー、およびそのサブクラスは特定の作業を担当します
- TransactionDefinition インターフェイス: トランザクションのいくつかの関連パラメーターを定義します。
- TransactionStatus インターフェイス: トランザクション操作のリアルタイムのステータスを表します。
3 つの関係:トランザクション マネージャーは、トランザクション定義パラメーターを読み取ってトランザクション管理を実行し、一連のトランザクション状態を生成します。
PlatformTransactionManagerインターフェイス:
-
Springトランザクション管理のルートインタフェースであり、その実装クラスをトランザクションマネージャとして利用します(トランザクション処理機能の強化)
-
インターフェース方式:
// 获取事务的状态信息 TransactionStatus getTransaction(TransactionDefinition def) // 提交事务 void commit(TransactionStatus status) // 回滚事务 void rollback(TransactionStatus status)
-
一般的な実装クラス:
- DataSourceTransactionManager : JDBC と MyBatis を使用してデータを永続化するときに使用されます。
- JpaTransactionManager : 永続化に JPA を使用する場合に使用されます (jpa、hibernate)
- HibernateTransactionManager : Hibernate を使用してデータを永続化するときに使用されます。
- JtaTransactionManager : トランザクションが複数のトランザクション管理ソースにまたがる場合に使用されます [分散トランザクション]
TransactionStatusインターフェイス:
-
トランザクション実行のリアルタイムの状態
-
インターフェースメソッド
// 是否是新事物 boolean isNewTransaction() // 是否有回滚点 boolean hasSavepoint() // 设置为只回滚事务 void setRollbackOnly() // 是否是只回滚事务 boolean isRollbackOnly() // 刷新事务状态 void flush() // 事务是否完成 boolean isCompleted()
トランザクション定義インターフェイス:
- トランザクション定義情報 (トランザクション分離レベル、伝播動作、読み取り専用トランザクション、タイムアウトなど)
-
トランザクション分離レベル
-
Spring でトランザクションを構成し、4 つの分離レベルすべてをサポートする
-
デフォルト値: 現在のデータベースに適切な構成項目を自動的に選択します。
-
// 事务隔离级别相关【不设置事务隔离级别,可能引发脏读、不可重复读、幻读】 ISOLATION_READ_UNCOMMITTED 读未提交 mysq1支持四种,默认可重复度 ISOLATION_READ COMMITTED 读已提交 oracle支持两种(读己提交和串行化),默认是读已提交 ISOLATION_REPEATABLE READ 可重复度 ISOLATION SERIALIZABLE 串行化
-
-
**トランザクションの伝播動作:** 複数のメソッドのネストされた呼び出しを記述する場合、呼び出されたメソッドはトランザクションをサポートします。
-
PROPAGATION_REQUIRED = 0 (トランザクションは必須です。これがデフォルトです)
トランザクションがある場合は、現在のトランザクションに参加します。トランザクションがない場合は、新しいトランザクションを開始します。
-
PROPAGATION_SUPPORTS = 1 (トランザクションをサポート)
トランザクションがある場合は、現在のトランザクションに参加します。トランザクションがない場合は非トランザクションで実行されます。
-
PROPAGATION_MANDATORY = 2 (必須トランザクション。自分で作成する責任はありません)
トランザクションがある場合は、現在のトランザクションに参加します。トランザクションがない場合は例外がスローされます。
-
PROPAGATION_REQUIRES_NEW = 3 (新規である必要があります)
常に新しいトランザクションを開始してください。トランザクションがある場合は、既存のトランザクションを一時停止し、新しいトランザクションを再度開始します。
-
PROPAGATION_NOT_SUPPORTED = 4 (トランザクションをサポートしません)
常に非トランザクションで実行され、既存のトランザクションは一時停止されます。
-
PROPAGATION_NEVER = 5 (トランザクションを強制しません。一時停止する責任はありません)
常に非トランザクションで実行され、アクティブなトランザクションがある場合は例外をスローします。
-
PROPAGATION_NESTED = 6 (ネストされたトランザクション)
アクティブなトランザクションが存在する場合、ネストされたトランザクションで実行されます。アクティブなトランザクションがない場合は、新しいトランザクションが開始されます。
内部トランザクションは外部トランザクションに依存します。外側のトランザクションが失敗すると、内側のトランザクションによって実行されたアクションはロールバックされます。
内部トランザクション操作が失敗しても、外部トランザクションはロールバックされません。
-
-
読み取り専用トランザクションかどうか
isReadOnly : true | false (デフォルト)
読み取り専用トランザクション: クエリのみで、追加、削除、変更はできません。読み取り専用トランザクションはクエリ メソッドでのみ使用できます
-
トランザクションタイムアウト
TIMEOUT_DEFAULT = -1 : トランザクションのタイムアウト期間。この構成を使用するには、基礎となるデータベースのサポートが必要です。デフォルト値は -1 で、無制限を意味します。
タイムアウト: デフォルト値を使用します
宣言型トランザクション(XML設定トランザクション)
XML に基づいた完全な宣言型トランザクション構成:
- Springコンテナ(アスペクトクラス)で管理されるようにトランザクションマネージャーを設定する
- トランザクション通知の構成
- 構成トランザクション用の AOP
<?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/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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--包扫描-->
<context:component-scan base-package="cn.test"></context:component-scan>
<!--自定义的java对象:注解-->
<!--第三方jar包中的对象:xml-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///heima23"></property>
</bean>
<!--配置Spring中的事务-->
<!--1、事务管理器交给容器管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2、配置事务通知。配置service层中所有类中所有方法,对事务的要求(支持)
id="advice" :表示IOC容器中真正的通知对象的id
transaction-manager="transactionManager" :表示指定当前要对哪个事务管理器进行配置
如果事务管理器在IOC容器中的id为transactionManager,此配置可以省略。
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
<tx:method :指定目标对象中切入点的方法名指定方法对事务的要求
name :方法名称。支持通配符 *
isolation :事务的隔离级别
timeout :超时时间
propagation :传播行为(REQUIRED)
read-only :是否只读事务(false)
-->
<tx:method name="save*" propagation="REQUIRED" read-only="false"></tx:method>
<tx:method name="update*"></tx:method>
<tx:method name="delete*"></tx:method>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
<!--3、事务的AOP配置-->
<aop:config>
<!--切入点表达式-->
<aop:pointcut id="pt" expression="execution(* cn.test.service.impl.*.*(..))"/>
<!--配置切面。<aop:advisor只有在spring的声明式事务配置时才能使用-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
宣言的トランザクション (アノテーション)
トランザクション注釈サポートを有効にする (XML モード)
- XML 構成ファイルで、トランザクション アノテーションのサポートを有効にします: トランザクション アノテーション ドリブン
- XML 設定ファイルで、コンテナ管理用のトランザクション マネージャーを作成します。
- トランザクションを必要とするクラスまたはメソッドでは、 @Transactional アノテーションを使用してトランザクションを構成します
<?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/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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--包扫描-->
<context:component-scan base-package="cn.test"></context:component-scan>
<!--开启事务注解的支持-->
<tx:annotation-driven></tx:annotation-driven>
<!--事务管理器交给容器管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--自定义的java对象:注解-->
<!--第三方jar包中的对象:xml-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///test"></property>
</bean>
</beans>
トランザクションアノテーションサポートを有効にする(設定方法)
- @EnableTransactionManagement : トランザクション アノテーション サポートを有効にするために構成クラスでマークされています
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan(basePackages = "cn.test")
@EnableTransactionManagement
public class SpringConfig {
/**
* 创建datasource
*/
@Bean
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql:///test");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
/**
* 创建jdbctemplate
* 1、从容器中获取datasource
* 2、调用方法
*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
/**
* 创建事务管理器
*/
@Bean
public PlatformTransactionManager getManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
宣言的なトランザクションアノテーションの使用
-
@Transactional : トランザクションを設定します
共通の属性:
-
rollbackFor属性: ロールバックする必要がある例外クラスの配列を設定します。指定された例外配列内の例外がメソッド内でスローされた場合、トランザクションはロールバックされます。
-
単一の例外クラスを指定します: @Transactional(rollbackFor=Exception.class)
-
複数の例外クラスを指定します: @Transactional(rollbackFor={RuntimeException.class, Exception.class})
-
-
readOnly属性: トランザクションを読み取り専用にするかどうか ( true | false (デフォルト値))
-
伝播属性: トランザクション伝播動作 ( SUPPORTS | REQUIRED (デフォルト値) )
-
transactionManager属性: Springコンテナで複数のトランザクションマネージャをホストする場合、トランザクションマネージャのBean名を指定します
-
分離属性: 複数のトランザクションの同時実行を処理するために使用される、基礎となるデータベースのトランザクション分離レベルを設定します。
通常はデータベースのデフォルトの分離レベルを使用でき、基本的に設定する必要はありません。
-
noRollbackFor属性:ロールバックする必要のない例外クラスの配列を設定します指定した例外配列内の例外がメソッド内でスローされた場合、トランザクションはロールバックされません
ラベルの位置の説明:
- クラスにマーク: このクラスのすべてのメソッドは同じトランザクション構成を持っています
- メソッドに注釈が付けられています: メソッドにはトランザクション構成があります
- クラスとメソッドを同時にマーク: 近接性の原理 (メソッドのトランザクション構成が有効になります)
-
import cn.test.dao.AccountDao;
import cn.test.domain.Account;
import cn.test.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional
//@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public void transfer(String sourceName, String targetName, float money) throws Exception {
//1、根据账户名称查询两个账户
Account sourceAccount = accountDao.findByName(sourceName); //转出账户
Account targetAccount = accountDao.findByName(targetName); //转入账户
//2、操作金额转换(转出账户扣除金额,转入账户添加金额)
sourceAccount.setMoney(sourceAccount.getMoney() - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
//3、更新账户
accountDao.update(sourceAccount);
int i=1/0;
accountDao.update(targetAccount);
}
}
拡大
Spring 統合単体テスト
単体テストで「実行」をクリックすると、基礎となる作業は実際にはランナーです。デフォルトは、junit によって提供される ParentRunner ランナーです。これは Spring 環境を認識しません。つまり、Spring コンテナーから Bean を取得できません。
Spring のコンテナからオブジェクトを取得したい場合は、Spring が提供するランナーを使用する必要があります。
-
依存関係を導入する
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
<!--spring-junit 整合单元测试--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.6.RELEASE</version> </dependency>
-
@RunWithアノテーション: 単体テストのランナーを設定し、value 属性を通じて単体テストの実行環境を指定します。
- JUnit4.class: JUnit4 を使用して実行するように指定します。
- SpringJUnit4ClassRunner.class : Spring テスト環境
- SpringRunner.class : Spring テスト環境
ノート:
-
SpringRunner は SpringJUnit4ClassRunner.class を拡張します
-
SpringJUnit4ClassRunnerは、JUnit4.12以降のSpringRunnerを使用する場合に使用できます
ただし、SpringRunner、最終タイプ、安全なものを使用することをお勧めします。
-
JUnit 4.12 より前のバージョンでは SpringJUnit4ClassRunner のみを使用できます
-
@ContextConfigurationアノテーション: コンテナの構成情報を指定します
- localtions 属性: 設定ファイルのパス
- クラス属性: 構成クラス
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountJunitTest {
@Autowired
private AccountService accountService;
//测试保存
@Test
public void testInsert() {
//3、调用方法保存
Account account = new Account();
account.setMoney(100f);
account.setName("小李1");
accountService.saveAccount(account);
}
}
設定ファイルのモジュール化
すべての構成が 1 つの applicationContext.xml ファイルで構成されている場合、開発者が多すぎる場合、すべての Bean が同じ構成ファイルで構成されていると、ファイルが巨大になり、保守が不便になります。この問題に対応して、Spring では複数の設定ファイルを使用する方法、いわゆる設定ファイルのモジュール化が提供されています。
- 複数の構成ファイルを並行して複数の構成ファイル (Beans1.xml、beans2.xml など) を直接書き込み、
ApplicationContext の作成時に
複数の構成ファイルを直接渡します。
ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
- マスター/スレーブ構成ファイル
最初にマスター構成ファイルを添付し、次にその中に他の構成ファイルをインポートします。
<import resource="beans1.xml" />
<import resource="beans2.xml" />
<!--拓展:引入本地properties配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
予防:
- 同じ名前の Bean を同じ XML ファイル内に出現させることはできません。出現した場合はエラーが報告されます。
- 複数の XML ファイルに同じ名前の Bean がある場合、エラーは報告されませんが、後でロードされた Bean は以前にロードされた Bean を上書きするため、エンタープライズ開発中は Bean 名が一意であることを確認するようにしてください
。
Spring Bean シングルトンの同時実行性の高いセキュリティの問題
Spring の Bean はデフォルトですべてシングルトンです。場合によっては、シングルトンは同時実行にとって安全ではありません。コントローラーを例に取ると、問題の根本は、コントローラーにメンバー変数が定義されている場合、複数のリクエストが来て、すべてが同じシングルトンのコントローラー オブジェクトに入るということです。
問題を提起します
この URL に複数回アクセスすると、結果が毎回自己インクリメントされることがわかります。そのため、このようなコードは明らかに同時実行に対して安全ではありません。
@Controller
public class HomeController {
private int i;
@GetMapping("testsingleton1")
@ResponseBody
public int test1() {
return ++i;
}
}
解決
解決策 1: メンバー変数の使用を避けるようにする
ビジネス上の状況では、メンバー変数の使用を避け、メソッド内でローカル変数を使用するようにしてください。
解決策 2: 同時実行安全なクラスを使用する
高機能プログラミング言語として、Java には豊富な API が用意されています。シングルトン Bean でメンバー変数を使用する必要がある場合は、ConcurrentHashMap、ConcurrentHashSet などの同時実行性が安全なコンテナーの使用を検討し、メンバー変数 (通常は現在実行中のタスク リストなどの変数) をこれらの同時実行性が安全なコンテナーにラップして管理できます。
シナリオ 3: 分散サービスまたはマイクロサービスの同時実行の安全性
マイクロサービスまたは分散サービスの影響をさらに考慮する場合、解決策 2 では十分ではないため、特定の情報を共有できる Redis などの分散キャッシュ ミドルウェアを使用して、同じサービスの異なるサービス インスタンスが同じ共有情報 (現在実行中のタスク リストやその他の変数など) を持つようにすることができます。
解決策 4: シングルトン変数プロトタイプ
Web プロジェクトの場合は、注釈 @Scope("prototype") または @Scope("request") を Controller クラスに追加できます。また、非 Web プロジェクトの場合は、注釈 @Scope("prototype") を Component クラスに追加できます。
利点: 実装が簡単
欠点: Bean の作成、インスタンス化、破棄に伴うサーバー リソースのオーバーヘッドが大幅に増加します。
使用できない解決策: スレッド分離クラス ThreadLocal
Web サーバーのデフォルトのリクエスト スレッド プール サイズは 10 で、これら 10 個のコア スレッドは、後でさまざまな Http リクエストで再利用できます。
ThreadLocal メソッドはスレッドの分離を実現できますが、同時実行の安全性はまだ実現できません。
@Autowired アノテーションを使用して静的変数に値を割り当てる
説明:
Ioc コンテナ内のオブジェクトは一部のツール クラスで使用される場合があり、ツール クラスのメンバ変数は静的な場合が多く、このときアノテーションが@Autowired
表示されますNullpointerException
(null ポインタ例外)。
原理分析:
静的変数やクラス変数はオブジェクトの属性ではなくクラスの属性なので、静的メソッドはクラスに属し、通常のメソッドはエンティティオブジェクト(つまりNewからのオブジェクト)に属しますが、Springインジェクションではオブジェクトをコンテナ内でインスタンス化するため、静的メソッドは使用できません。
静的変数やクラス変数を利用することで、静的メソッドの利用範囲が広がります。Spring では静的メソッドは推奨されません。依存関係注入の主な目的は、コンテナーにオブジェクトのインスタンスを生成させ、ライフサイクル全体でそれらを使用できるようにすることであり、テスト作業も容易になります。
静的メソッドを使用すると、このクラスのインスタンスを生成する必要がなく、テストがより困難になります。同時に、インジェクションによって、特定のクラスに対して異なる依存環境を持つ複数のインスタンスを生成することはできません。この静的フィールドは暗黙的に共有され、グローバルなグローバル状態になります。Spring もこれを推奨しません。
**解決策 1: ** @Autowire アノテーションを set メソッドに追加します
@Component
public class Test {
private static SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
Test.sessionFactory = sessionFactory;
}
}
**解決策 2:** @PostConstruct でアノテーションを付ける
@Component
public class Test {
private static SessionFactory sessionFactory;
@Autowired
private SessionFactory sessionFactory2;
@PostConstruct
public void beforeInit() {
SessionFactory = sessionFactory2;
}
}
**解決策 3: ** @Autowire アノテーションをコンストラクターに追加します
@Component
public class Test {
private static SessionFactory sessionFactory;
@Autowired
public Test(SessionFactory sessionFactory) {
Test.SessionFactory = SessionFactory;
}
}
非コンテナ内のクラスがコンテナ内のクラスを呼び出す
説明:
@Autowired を使用してオブジェクトを注入する場合、通常、注入されたクラスには @Coponent、@Controller、@Service、@repository などの注釈が付けられます。注入されたクラスと注入されたクラスは両方とも Spring によって管理され、呼び出すことができます。ただし、非コンテナ クラス (上記のアノテーションなし) が @Autowired を使用してコンテナ内のクラスを呼び出すと、挿入されたオブジェクトは空になり、null ポインタ例外が報告されます。
解決:
ツール クラス BeanUtils を作成します。このツール クラスの getBean はコンテナ内のクラスを取得し、それを非コンテナ クラスで使用できます。
@Component
public class BeanUtils implements ApplicationContextAware {
/**
* 以静态变量保存ApplicationContext,可在任意代码中取出ApplicaitonContext.
*/
private static ApplicationContext context;
/**
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
*/
@Override
public void setApplicationContext(ApplicationContext context) {
BeanUtils.context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型. 方法返回值的类型由调用者决定
*/
public static <T> T getBean(String name) {
return (T) context.getBean(name);
}
/// 获取当前环境
public String getActiveProfile() {
return context.getEnvironment().getActiveProfiles()[0];
}
}
非コンテナ クラスでのコンテナ クラスの使用
public class StationFactory {
Map<String, StationOperation> map = new HashMap<>();
{
map.put("定损中心主管指标表", BeanUtils.getBean("leadDSZXOperation"));
map.put("定损中心员工指标表", BeanUtils.getBean("empDSZXOperation"));
map.put("视频查勘中心主管指标表", BeanUtils.getBean("leadVideoSurveyCenterOperation"));
map.put("视频查勘中心员工指标表", BeanUtils.getBean("empVideoSurveyCenterOperation"));
map.put("视频定损中心主管指标表", BeanUtils.getBean("leadVideoDSCenterOperation"));
}
}