記事ディレクトリ
スプリングコア(コアコンテナ)
Spring コアは、IOC、DI、および Bean 設定ロード作成のコア実装を提供します。 コア
概念: Bean、BeanFactory、BeanDefinitions、ApplicationContext
- spring-core: IOC と DI の基本的な実装
- spring-beans: BeanFactory および Bean アセンブリ管理 (BeanFactory)
- spring-context: Spring コンテキスト コンテキスト、つまり IOC コンテナ (AppliactionContext)
- spring-expression: 春の式言語
1.IOCコンテナ
1. 制御の反転 (ioc)
- 制御の反転とは、
思想
- 制御の反転は、プログラムの結合を減らし、プログラムのスケーラビリティを向上させることを目的としています。
- 制御の反転、反転とは何ですか?
对象的创建
第三者コンテナに権利を譲渡し、責任を譲渡する对象和对象之间关系
メンテナンス権限をサードパーティのコンテナに引き渡す
- 制御の反転のアイデアをどのように実現するか?
- DI (Dependency Injection): 依存性注入
2. 依存性の注入
DI(Dependency Injection):依存性注入、依存性注入は制御の逆転の考え方を実現する
依赖属性
Spring でオブジェクトを作成し、構成を通じてオブジェクトを構成するプロセスを指します。注入
- 依存関係注入には 2 つの一般的な実装があります。
- セットインジェクション
- コンストラクターインジェクション
- Bean 管理とは、Bean オブジェクト
创建
、および Bean オブジェクト属性的赋值
(または Bean オブジェクト間の関係の維持)を指します。
結論としては、IOC は制御の逆転のアイデアであり、DI は IoC を具体的に実現したものです。
3. SpringでのIoCコンテナの実装
- Spring の IoC コンテナは、IoC のアイデアを製品実装したものです。
- IoCコンテナで管理されるコンポーネントはBeanとも呼ばれます
- Bean を作成する前に、まず IoC コンテナを作成する必要があります
- Spring は、IoC コンテナの 2 つの実装を提供します。
- ビーンファクトリー
- これは IoC コンテナの
基本实现
- Spring が内部的に使用するインターフェイスです
- 開発者が使用するためではなく、Spring 自体用
- これは IoC コンテナの
- アプリケーションコンテキスト
BeanFactory 的子接口,提供了更多高级特性
- Springユーザー向け
- ほとんどの場合、基になる BeanFactory の代わりに ApplicationContext が使用されます。
- ビーンファクトリー
ApplicationContextの主な実装クラス
型名 | 序章 |
---|---|
ClassPathXmlApplicationContext | クラスパスの下にある XML 形式の構成ファイルを読み取って、IOC コンテナ オブジェクトを作成します。 |
FileSystemXmlApplicationContext | ファイル システム パスを介して XML 形式の構成ファイルを読み取ることにより、IOC コンテナ オブジェクトを作成します。 |
構成可能なアプリケーションコンテキスト | ApplicationContext のサブインターフェイスには、いくつかの拡張メソッド Refresh() および close() が含まれており、これにより、ApplicationContext がコンテキストを開始、閉じ、更新できるようになります。 |
Webアプリケーションコンテキスト | Web アプリケーション用に特別に準備され、Web 環境に基づいて IOC コンテナ オブジェクトを作成し、そのオブジェクトを ServletContext ドメインにインポートして保存します。 |
2、XML 管理 Bean に基づく
プロジェクトを構築する
- 依存関係を追加する
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.3</version>
</dependency>
- Javaクラスをインポートする
public class User {
}
- 豆.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1 获取bean演示,user对象创建-->
<bean id="user" class="com.xc.spring6.iocxml.bean.User"></bean>
</beans>
1.豆を入手する
方法1.idによる取得
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 根据id获取bean
User user = (User) context.getBean("user");
}
}
方法2. 種類ごとに取得する
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean(User.class);
}
}
方法 3. ID とタイプに従って Bean を取得する
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user",User.class);
}
}
知らせ
- タイプ別に Bean を取得する場合、
指定类型
IOC コンテナ内の Bean には 1 つだけ必要があります。 - IOCコンテナに合計2つ設定した場合
<bean id="user" class="com.xc.spring6.iocxml.bean.User"></bean>
<bean id="user1" class="com.xc.spring6.iocxml.bean.User"></bean>
型に応じて取得した場合は例外がスローされます:NoUniqueBeanDefinitionException(独自Bean定義例外なし)
拡大
- コンポーネントクラスがインターフェースを実装していれば
根据接口类型
Beanを取得できるのでしょうか?(可以
、Bean が一意である場合) - インターフェースがある場合
多个实现类
、これらの実装クラスはBeanで構成されますが、インターフェースの種類に応じてBeanを取得できますか? (不行
、Bean は一意ではないため)
2. 依存性注入のセッター注入
- 学生クラスを作成する
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
}
- Bean を構成するときにプロパティに値を割り当てる
<bean id="studentOne" class="com.xc.spring6.bean.Student">
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
<!-- value属性:指定属性值 -->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
- テスト
@Test
public void testDIBySet(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
Student studentOne = ac.getBean("studentOne", Student.class);
System.out.println(studentOne);
}
3. 依存性注入のコンストラクター注入
- Student クラスにパラメータ化された構造を追加する
public Student(Integer id, String name, Integer age, String sex) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
- 構成 Bean
<bean id="studentTwo" class="com.xc.spring6.bean.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
注:
constructor-arg タグには、コンストラクターのパラメーターを詳しく説明する 2 つの属性もあります。
- Index 属性: パラメーターが配置されているインデックスを指定します (0 から始まります)。
- name 属性: パラメータ名を指定します
- テスト
@Test
public void testDIByConstructor(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
Student studentOne = ac.getBean("studentTwo", Student.class);
System.out.println(studentOne);
}
4. 特殊な値の処理
リテラル代入
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
NULL値
<property name="name">
<null />
</property>
XMLエンティティ
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a < b"/>
CDATA セクション
<property name="expression">
<!-- 解决方案二:使用CDATA节 -->
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>
5. オブジェクトタイプのプロパティに値を割り当てる
- 従業員と部門
// 部门累
@Data
public class Dept {
private String dname;
}
// 员工类
@Data
public class Emp {
//对象类型属性:员工属于某个部门
private Dept dept;
//员工名称
private String ename;
//员工年龄
private Integer age;
}
方法 1: 外部 Bean を参照する
<bean id="dept" class="com.xc.spring6.iocxml.ditest.Dept">
<property name="dname" value="安保部"></property>
</bean>
ref属性
Emp クラスの部門属性割り当てオブジェクトを使用する
<bean id="emp" class="com.xc.spring6.iocxml.ditest.Emp">
<!--注入对象类型属性
private Dept dept;
-->
<property name="dept" ref="dept"></property>
<!--普通属性注入-->
<property name="ename" value="lucy"></property>
<property name="age" value="50"></property>
</bean>
方法 2: 内部 Bean
<bean id="emp2" class="com.xc.spring6.iocxml.ditest.Emp">
<!--普通属性注入-->
<property name="ename" value="mary"></property>
<property name="age" value="20"></property>
<!--内部bean-->
<property name="dept">
<bean id="dept2" class="com.xc.spring6.iocxml.ditest.Dept">
<property name="dname" value="财务部"></property>
</bean>
</property>
</bean>
方法 3: カスケード属性の割り当て
- 名前属性 = オブジェクト.属性名
<bean id="dept3" class="com.xc.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术研发部"></property>
</bean>
<bean id="emp3" class="com.xc.spring6.iocxml.ditest.Emp">
<property name="ename" value="tom"></property>
<property name="age" value="30"></property>
<property name="dept" ref="dept3"></property>
<property name="dept.dname" value="测试部"></property>
</bean>
6. 配列型プロパティに値を代入する
- 趣味属性の配列を Emp 従業員クラスに追加します
//爱好
private String[] loves;
<bean id="emp" class="com.xc.spring6.iocxml.ditest.Emp">
<!--普通属性-->
<property name="ename" value="lucy"></property>
<property name="age" value="20"></property>
<!--对象类型属性-->
<property name="dept" ref="dept"></property>
<!--数组类型属性-->
<property name="loves">
<array>
<value>吃饭</value>
<value>睡觉</value>
<value>敲代码</value>
</array>
</property>
</bean>
7. コレクション型プロパティに値を割り当てる
- 部門クラスが従業員コレクションを追加します
//一个部门有很多员工
private List<Emp> empList;
<bean id="empone" class="com.xc.spring6.iocxml.ditest.Emp">
<property name="ename" value="lucy"></property>
<property name="age" value="20"></property>
</bean>
<bean id="emptwo" class="com.xc.spring6.iocxml.ditest.Emp">
<property name="ename" value="mary"></property>
<property name="age" value="30"></property>
</bean>
<bean id="dept" class="com.xc.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术部"></property>
<property name="empList">
<list>
<ref bean="empone"></ref>
<ref bean="emptwo"></ref>
</list>
</property>
</bean>
Set コレクション タイプ属性に値を割り当てる場合は、その中のリスト タグを次
set标签
のように変更するだけで済みます。
8. XMLに基づいた自動アセンブリ
- 指定されたポリシーに従って IOC コンテナ内の Bean を照合します。
- 指定された Bean が依存するクラス タイプまたはインターフェイス タイプのプロパティに値を自動的に割り当てます。
- XML自動アセンブリに基づいて、最下層はセットインジェクションを使用します
Bean クラス
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUserService() {
userDao.addUserDao();
}
}
bean.xml を設定する
- 自動組み立て方法:byType
类型匹配
byType: IOC コンテナ内の互換性のあるタイプの Beanに従って、プロパティに値が自動的に割り当てられます。- IOC に、プロパティに値を割り当てることができる互換性のあるタイプの Bean がない場合、プロパティはアセンブルされません。つまり、デフォルト値は null です。
- IOC に、プロパティに値を割り当てることができる互換性のあるタイプの Bean が複数ある場合、例外 NoUniqueBeanDefinitionException がスローされます。
<bean id="userService" class="com.xc.spring6.autowire.service.impl.UserServiceImpl"
autowire="byType"></bean>
<bean id="userDao" class="com.xc.spring6.autowire.dao.impl.UserDaoImpl"></bean>
- 自動アセンブリ方法: byName
属性名
byName: IOC コンテナ内の対応する Bean と一致するように、自動アセンブリ属性を Bean ID として割り当てます。
3. アノテーションに基づいて Bean を管理する ( ☆ )
1. 構成クラスは bean.xml を置き換えます。
@Configuration
//@ComponentScan({"com.xc.spring6.controller", "com.xc.spring6.service","com.xc.spring6.dao"})
@ComponentScan("com.xc.spring6")
public class Spring6Config {
}
2. @Autowired インジェクション
- @Autowired アノテーションを単独で使用します。
默认根据类型装配
- 注釈のソースコードを表示する
@Target({
ElementType.CONSTRUCTOR, ElementType.METHOD,
ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- 注釈で位置をマークできます
- 施工方法について
- 方法
- 形参上
- 属性
- 注意事項
- このアノテーションには必須の属性があります
- デフォルト値は true で、注入された Bean が注入中に存在する必要があり、存在しない場合はエラーが報告されることを示します。
- required 属性が false に設定されている場合、注入された Bean が存在するかどうかは関係ありません。
方法 1: 属性の挿入 ( 最常用
)
- アノテーションベースの自動アセンブリ、最下層はリフレクションインジェクションを使用するため、setメソッドは必要ありません
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
}
方法2、セットインジェクション
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
メソッド 3、コンストラクター インジェクション
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
方法 4. 仮パラメータの注入
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}
}
方法 5. コンストラクターは 1 つだけであり、コンストラクターを介した注入にはアノテーションは必要ありません
- パラメーターを持つコンストラクターが 1 つだけの場合、 @Autowired アノテーションは省略できます。
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
方法 6、@Autowired アノテーションと @Qualifier アノテーションの組み合わせ
- UserDao インターフェースに
两个实现类
- @Autowired 経由で注入すると例外がスローされます (単一の一致する Bean が予期されましたが、2 つ見つかりました: xx1、xx2)
- この時点で、@Qualifier アノテーションを使用して特定の Bean の名前を指定できます。
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl") // 指定bean的名字
private UserDao userDao;
}
3. @リソース注入
@Resource アノテーションによってプロパティの注入を完了することもできます。では、これと @Autowired アノテーションの違いは何でしょうか?
- @Resource アノテーションは JDK 拡張パッケージに含まれており、これは JDK の一部であることを意味します。
- したがって、このアノテーションは標準的なアノテーションであり、より多用途です。
- JSR-250 標準で指定されたアノテーション タイプ。JSR は Java 仕様提案です
- @Autowired アノテーションは Spring フレームワーク独自のものです
- @Resource アノテーションは、デフォルトでは byName によって名前によってアセンブルされます。名前によって見つからない場合は、自動的にタイプ byType によってアセンブルが開始されます。
- @Autowired アノテーションはデフォルトでタイプごとにアセンブルされますが、名前でアセンブルしたい場合は @Qualifier アノテーションと併用する必要があります。
- @Resource アノテーションはプロパティとセッター メソッドで使用されます
- @Autowired アノテーションは、プロパティ、セッター メソッド、構築メソッド、および構築メソッドのパラメータで使用されます。
@Resource アノテーションは JDK 拡張パッケージに属するため、JDK には含まれておらず、以下の依存関係を追加で導入する必要がありますが、
JDK8 の場合は追加の依存関係を導入する必要はありません。JDK11 以降または JDK8 未満では、次の依存関係を導入する必要があります。
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
方法 1. 名前に従って注入する
@Service
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
private UserDao myUserDao;
}
方法 2: 不明な名前の挿入
- 名前を指定せずに @Resource アノテーションが使用された場合でも、属性名である名前に従って検索されます。
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
}
方法3、名前が見つからない場合
- 明らかに、名前が見つからない場合は、注入のために byType で自然に開始されます。
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDaoNotFound ;
}
4. 原則 - 手書き IoC
マークされた Bean の @Bean アノテーションと依存関係注入の @Di アノテーションを定義します。
- @Bean は @Component と同等です
- @Di は @Autowired と同等です
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
@Target({
ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
Beanコンテナインターフェースを定義する
public interface ApplicationContext {
Object getBean(Class<?> clazz);
}
Bean コンテナ インターフェイスと依存関係注入の実装に注釈を付ける
public class AnnotationApplicationContext implements ApplicationContext {
//创建map集合,放bean对象
private final Map<Class<?>, Object> beanFactory = new HashMap<>();
private static String rootPath;
//返回对象
@Override
public Object getBean(Class<?> clazz) {
return beanFactory.get(clazz);
}
//创建有参数构造,传递包路径,设置包扫描规则
//当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化
public AnnotationApplicationContext(String basePackage) {
// com.xc
try {
//1 把.替换成\
String packagePath = basePackage.replaceAll("\\.", "/");
//2 获取包绝对路径
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
//获取包前面路径部分,字符串截取
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//包扫描
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//属性注入
loadDi();
}
//包扫描过程,实例化
private void loadBean(File file) throws Exception {
//1 判断当前是否文件夹
if (file.isDirectory()) {
//2 获取文件夹里面所有内容
File[] childrenFiles = file.listFiles();
//3 判断文件夹里面为空,直接返回
if (childrenFiles == null || childrenFiles.length == 0) {
return;
}
//4 如果文件夹里面不为空,遍历文件夹所有内容
for (File child : childrenFiles) {
//4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归
if (child.isDirectory()) {
//递归
loadBean(child);
} else {
//4.2 遍历得到File对象不是文件夹,是文件,
//4.3 得到包路径+类名称部分-字符串截取
String pathWithClass = child.getAbsolutePath().substring(rootPath.length());
//4.4 判断当前文件类型是否.class
if (pathWithClass.contains(".class")) {
//4.5 如果是.class类型,把路径\替换成. 把.class去掉
// com.xc.service.UserServiceImpl
String allName = pathWithClass.replaceAll("/", ".").replace(".class", "");
//4.6 判断类上面是否有注解 @Bean,如果有实例化过程
//4.6.1 获取类的Class
Class<?> clazz = Class.forName(allName);
//4.6.2 判断不是接口
if (!clazz.isInterface()) {
//4.6.3 判断类上面是否有注解 @Bean
if (clazz.isAnnotationPresent(Bean.class)) {
//4.6.4 实例化
Object instance = clazz.getConstructor().newInstance();
//4.7 把对象实例化之后,放到map集合beanFactory
//4.7.1 判断当前类如果有接口,让接口class作为map的key
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
beanFactory.put(clazz, instance);
}
}
}
}
}
}
}
}
//属性注入
private void loadDi() {
//实例化对象在beanFactory的map集合里面
//1 遍历beanFactory的map集合
Set<Map.Entry<Class<?>, Object>> entries = beanFactory.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
//2 获取map集合每个对象(value),每个对象属性获取到
Object obj = entry.getValue();
//获取对象Class
Class<?> clazz = obj.getClass();
//获取每个对象属性获取到
Field[] declaredFields = clazz.getDeclaredFields();
//3 遍历得到每个对象属性数组,得到每个属性
for (Field field : declaredFields) {
//4 判断属性上面是否有@Di注解
if (field.isAnnotationPresent(Di.class)) {
//如果私有属性,设置可以设置值
field.setAccessible(true);
//5 如果有@Di注解,把对象进行设置(注入)
try {
field.set(obj, beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
テストクラス
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao.......");
}
}
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
public class SpringIocTest {
@Test
public void testIoc() {
ApplicationContext applicationContext = new AnnotationApplicationContext("com.xc");
UserService userService = (UserService)applicationContext.getBean(UserService.class);
userService.add();
System.out.println("run success");
}
}
出力結果
service.......
dao.......
run success