1. Spring IoC註解式開發無敵詳細(細節豐富)
@
- 1. Spring IoC註解式開發無敵詳細(細節豐富)
- 每博一文案
- 2. 註解回顧
- 3. Spring 宣告Bean的註解
- 3.1 Spring註解的使用
- 3.1.1 特別的:如果要掃描的是多個包
- 3.1.2 Spring 選擇性例項化Bean物件
- 3.2 透過註解實現“Spring的注入”
- 3.2.1 @Value 註解的 Bean 賦值
- 3.2.2 @Autowired 與 @Qualifier
- 3.2.3 @Resource 註解實現賦值操作
- 3.3 Spring 全註解式開發
- 3.1 Spring註解的使用
- 4. 總結:
- 5. 疑問:
- 6. 最後:
每博一文案
讀書,它可以在我生活順遂的時候,看見更大的世界,
在生活不順的時候,依然可以有心態去仰望星空。
無論如果都不要將愛,轉為成了某種恨
就是我過的不好,也希望你過的好,就算我過的不好,我也依舊會幫你過的好
不管你一天,經歷了什麼,天黑了,我帶你回家。
生命是有光的,在我熄滅已前,能夠照亮你一點便是,我所有能做的了。
2. 註解回顧
既然我們要學習:”Spring IoC註解式開發“,自然就是要是用上註解 的,為了方便後面的學習,這裡我們簡單回顧一下註解的內容。更多關於註解方面的內容大家可以移步至:✏️✏️✏️ Java “框架 = 註解 + 反射 + 設計模式” 之 註解詳解-CSDN部落格 。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
String value();
}
以上是自定義了一個註解:Component
該註解上面修飾的註解包括:Target註解和Retention註解,這兩個註解被稱為元註解。
Target註解用來設定Component註解可以出現的位置,以上代表表示Component註解只能用在類和介面上。
Retention註解用來設定Component註解的保持性策略,以上代表Component註解可以被反射機制讀取。
String value(); 是Component註解中的一個屬性。該屬性型別String,屬性名是value。
註解賦值 ——》語法格式:@註解型別名(屬性名=屬性值, 屬性名=屬性值, 屬性名=屬性值......)
userBean為什麼使用雙引號括起來,因為value屬性是String型別,字串。
另外如果屬性名是value,則在使用的時候可以省略屬性名。
為了進一步,運用註解,這裡我們看看下面這個需求。
目前只知道一個包
com.rianbowsea
的名字,掃描這個包下所有的類,當這個類上有@Compoent 註解的時候,例項化該物件,然後放到Map集合中。
package com.rainbowsea;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 使用某個註解的時候,如果註解的屬性值是陣列,並且陣列中只有一個元素,大括號可以省略。
@Target(ElementType.TYPE)
// @Retention 也是一個元註解,用來標註@Component 註解最終保留在class 檔案當中,並且可以被反射機制讀取
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
// 定義註解的屬性
// String 是屬性型別
// value 是屬性名
String value();
}
package com.rainbowsea;
//
//@Component(value = "userBean",屬性名 = 屬性值,屬性名 = 屬性值,屬性名 = 屬性值...)
//@Component(value = "userBean")
// 如果屬性名是 value,value 可以省略
@Component("userBean")
public class User {
}
package com.rainbowsea;
@Component("orderBean")
public class Order {
}
package com.rainbowsea;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ComponentScan {
/*
目前只知道一個包的名字,掃描這個包下所有的類,當這個類上有@Compoent 註解的時候,
例項化該物件,然後放到Map集合中
*/
public static void main(String[] args) {
//定義一個集合儲存其中的例項化物件
Map<String,Object> beanMap = new HashMap<>();
String packageName = "com.rainbowsea";
// 開始掃描程式
// .這個正規表示式表示任意字母,這裡的“.” 必須是一個普通的"."字元,不能是正規表示式中的“.”
// 在正規表示式當中怎麼表示一個普通的"." 字元呢?使用"\",在Java當中一個“/” 要用兩個“//” 表示
String packagePath = packageName.replaceAll("\\.", "/");
//System.out.println(packagePath);
// com 是在類的根路徑下的一個目錄
// url 是一個絕對路徑
URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
String path = url.getPath();
//System.out.println(path);
// 獲取一個絕對路徑下的所有檔案
File file = new File(path);
File[] files = file.listFiles();
Arrays.stream(files).forEach(file1 -> {
//System.out.println(file1);
//System.out.println(file1.getName().split("\\.")[0]);
// 拼接成:全限定類名
String className = packageName + "." + file1.getName().split("\\.")[0];
// 再透過反射機制,解析註解
Class<?> aClass = null;
try {
aClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// 判斷該類是否含有該 Component.class 註解
if(aClass.isAnnotationPresent(Component.class)) {
// 獲取註解
Component annotation = aClass.getAnnotation(Component.class);
// 獲取到該註解的值
String id = annotation.value();
// 有這個註解的都要建立物件
try {
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
Object obj = declaredConstructor.newInstance();
// 將建立好的例項化物件儲存到 Map 當前去。
beanMap.put(id,obj);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
});
System.out.println(beanMap);
}
}
執行結果:
3. Spring 宣告Bean的註解
註解的存在主要是為了簡化XML的配置。Spring6倡導全註解開發。
在Spring 當中,負責宣告 Bean 的註解的,常見的有如下四個:
- @Compoent
- @Controller
- @Service
- @Repository
透過原始碼可以看到,@Controller、@Service、@Repository 這三個註解都是@Component註解的別名。換句話說:這四個註解的功能都一樣。用哪個都可以。
只是為了增強程式的可讀性,建議:
-
控制器類上使用:Controller
-
service類上使用:Service
-
dao類上使用:Repository
他們都是隻有一個value屬性。value屬性用來指定bean的id,也就是bean的名字
3.1 Spring註解的使用
如何使用以上的註解呢?
- 第一步:加入aop的依賴
- 第二步:在配置檔案中新增context名稱空間
- 第三步:在配置檔案中指定掃描的包
- 第四步:在Bean類上使用註解
第一步:加入aop的依賴
還是第一步,透過Maven 匯入相關的 jar 包,在 pom.xml
檔案當中。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rainbowsea.reflect</groupId>
<artifactId>spring6-oo8-anotation-blog</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.11</version>
</dependency>
<!-- junit4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
</project>
我們可以看到當加入spring-context依賴之後,會關聯加入aop的依賴。所以這一步不用做。
第二步:在配置檔案中新增context名稱空間
注意:這裡所說的配置檔案是指,我們配置 bean 物件的那個配置.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"
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">
</beans>
第三步:在配置檔案中指定掃描的包
注意:這裡所說的配置檔案是指,我們配置 bean 物件的那個配置.xml
的配置檔案資訊。如下:
指定掃描的包: 是指明Spring 在那個包路徑下,可以找到要例項化的 Bean 物件。
<!-- 指定掃描的包,-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
<?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"
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">
<!-- 指定掃描的包,-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
</beans>
第四步:在Bean類上使用註解
如下:上面四個註解(@Controller、@Service、@Repository @Component),我們都使用測試上,看看能否例項化成功。
package com.rainbowsea.reflect.sprint.bean;
import org.springframework.stereotype.Service;
//@Service(value = "orderBean")
@Service("os")
public class Order {
}
package com.rainbowsea.reflect.sprint.bean;
import org.springframework.stereotype.Repository;
@Repository("studentBean")
public class Student {
}
/**
* 以上的 @Repository就相當域以下的配置資訊
* <bean id ="student" class="com.ranbowsea.spring.bean.Student"></bean>
*/
package com.rainbowsea.reflect.sprint.bean;
import org.springframework.stereotype.Component;
@Component(value = "userBean")
public class User {
}
package com.rainbowsea.reflect.sprint.bean;
import org.springframework.stereotype.Controller;
@Controller(value = "vipBean")
public class Vip {
}
package com.rainbowsea.test;
import com.rainbowsea.reflect.sprint.bean.Order;
import com.rainbowsea.reflect.sprint.bean.Student;
import com.rainbowsea.reflect.sprint.bean.User;
import com.rainbowsea.reflect.sprint.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCAnnotationTest {
@Test
public void testIoCAnnotation() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Order os = applicationContext.getBean("os", Order.class);
System.out.println(os);
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
}
3.1.1 特別的:如果要掃描的是多個包
特別的: 如果要掃描的是多個包
- 如果你要配置,掃描多個包下的檔案可以使用逗號分隔開來 。
- 或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些。
<!-- 如果要掃描的是多個包,使用逗號隔開-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean,
com.rainbowsea.reflect.sprint.bean2"></context:component-scan>
<?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"
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">
<!-- 如果要掃描的是多個包,使用逗號隔開-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean,
com.rainbowsea.reflect.sprint.bean2"></context:component-scan>
</beans>
測試:
或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些。如下:
<!-- 或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint"></context:component-scan>
<?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"
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">
<!-- 或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint"></context:component-scan>
</beans>
3.1.2 Spring 選擇性例項化Bean物件
假設在某個包下有很多Bean,有的Bean上標註了@Component,有的標註了@Controller,有的標註了@Service,有的標註了@Repository,現在由於某種特殊業務的需要,只允許其中所有的Controller參與Bean管理,其他的都不例項化。這應該怎麼辦呢?
我們可以有一下兩種方案:
第一種方案:
在掃描檔案的:
<context:component-scan>
的標籤當中新增上:use-default-filters="
屬性,並該屬性指為 false 。表示該表明的包下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部失效。不會例項化該包下的 bean 物件。
而只有在
<context:component-scan>
的標籤下的,指明的:<context:include-filter>
說明的註解才會生效,才會例項化該包下的 Bean 物件。注意其中的值是:include-filter,type="annotation",expression="org.springframework.stereotype.Controller" 注意:這個包名路徑不要錯了。org.springframework.stereotype.Controller<?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" 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"> <!-- <context:component-scan base-package="com.rainbowsea.sprint.bean2" use-default-filters="false">--> <!-- 只有@Repository獲取其他的註解被包含進來了,才生效 注意是:include-filter --> <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans>
測試:這裡我們讓註解是:@Service,@Controller 的這個兩個有效,例項化Bean 物件,其他的註解失效,不例項化Bean 物件。
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<?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"
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">
<!-- <context:component-scan base-package="com.rainbowsea.sprint.bean2" use-default-filters="false">-->
<!-- 只有@Repository獲取其他的註解被包含進來了,才生效 注意是:include-filter -->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
</beans>
執行測試:
第二種方案:
use-default-filters="
屬性,並該屬性指為 true (為true 值是預設的,可以省略不寫)。表示該表明的包下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效。會例項化該包下的 bean 物件。
而在
<context:component-scan>
的標籤下的,指明的:<context:exclude-filter
說明的註解會失效,不會例項 化 Bean 物件,注意其中的值是:exclude-filter type="annotation" expression="org.springframework.stereotype.Component" 注意:這個包名路徑不要錯了。org.springframework.stereotype.Controller,同時注意:這裡是 exclude-filter 了。
測試:這裡我們讓註解是:@Service,@Controller 的這個兩個失效 ,不能 例項化Bean 物件,其他的註解可以例項化Bean 物件。
<!-- use-default-filters="true" 如果這個屬性值是true,表示 com.rainbowsea.sprint.bean2-->
<!-- 下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效-->
<!-- use-default-filters="true" 預設值就是 true,不用寫-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="true" >
<!-- 如果為 true ,下面宣告瞭對應的註解表示該,註解下的 Bean 失效,例如這裡的:該
"com.rainbowsea.reflect.sprint.bean 包下的註解為:@Service,@Controller 的Bean失效-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller "/>
</context:component-scan>
<?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"
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">
<!-- use-default-filters="true" 如果這個屬性值是true,表示 com.rainbowsea.sprint.bean2-->
<!-- 下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效-->
<!-- use-default-filters="true" 預設值就是 true,不用寫-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="true" >
<!-- 如果為 true ,下面宣告瞭對應的註解表示該,註解下的 Bean 失效,例如這裡的:該
"com.rainbowsea.reflect.sprint.bean 包下的註解為:@Service,@Controller 的Bean失效-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller "/>
</context:component-scan>
</beans>
執行測試:
3.2 透過註解實現“Spring的注入”
@Controller、@Service、@Repository @Component註解的 宣告後這些Bean將被例項化。接下來我們就需要對這些已經例項化的 Bean 物件進行屬性上的賦值操作了。如何給Bean的屬性賦值。給Bean屬性賦值需要用到這些註解:
-
@Value
-
@Autowired
-
@Qualifier
-
@Resource
3.2.1 @Value 註解的 Bean 賦值
當屬性的型別是簡單型別時,可以使用@Value註解進行注入。
注意:是Spring 認為的簡單型別才可以用 @Value 註解實現 bean 賦值,複雜型別(ref) 是不可以用 @Value 進行bean的賦值操作的,會出錯。
@Value註解可以出現在屬性上、setter方法上、以及構造方法的形參上。可見Spring給我們提供了多樣化的注入。
使用上: 就是註解上的簡單使用,格式即可。如下:測試
@Value 出現在屬性上,完成賦值操作 :
定義一個 Bean 物件,用於測試;
在相關的 spring.xml 檔案上,配置好掃描的包,路徑
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- 配置掃描檔案包-->
<context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
</beans>
執行測試:
package com.rainbowsea.test;
import com.rainbowsea.reflect.sprint.bean.Order;
import com.rainbowsea.reflect.sprint.bean.Student;
import com.rainbowsea.reflect.sprint.bean.User;
import com.rainbowsea.reflect.sprint.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCAnnotationTest {
@Test
public void testIoCAnnotation() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
}
}
@Value 出現在構造方法的形參上,完成賦值操作
public User(@Value(value = "李華") String name, @Value("21") int age, Vip vip) {
this.name = name;
this.age = age;
this.vip = vip;
}
執行測試:
說明:這個 Vip 複雜型別也賦值上了,是因為@Autowired 的自動裝配機制,當對應的 Bean 只有一個構造方法時,會自動裝配。後面會說明的。
@Value 出現在set() 方法上,完成賦值操作 :
執行測試:
注意:是Spring 認為的簡單型別才可以用 @Value 註解實現 bean 賦值,複雜型別(ref) 是不可以用 @Value 進行bean的賦值操作的,會出錯。
3.2.2 @Autowired 與 @Qualifier
@Autowired註解可以用來注入非簡單型別。被翻譯為:自動連線的,或者自動裝配。注意是非簡單型別的,賦值操作。
單獨使用@Autowired註解,預設根據型別裝配。【預設是byType】
看一下它的原始碼:
原始碼中有兩處需要注意:
第一處:該註解可以標註在哪裡?
構造方法上;方法上;形參上;屬性上;註解上
第二處:該註解有一個required屬性,預設值是true,表示在注入的時候要求被注入的Bean必須是存在的,如果不存在則報錯。如果required屬性設定為false,表示注入的Bean存在或者不存在都沒關係,存在的話就注入,不存在的話,也不報錯。
測試:準備工作:
package com.rainbowsea.reflect.sprint.dao;
import org.springframework.stereotype.Controller;
@Controller(value = "orderDao")
public interface OrderDao {
void insert();
}
package com.rainbowsea.reflect.sprint.dao;
import org.springframework.stereotype.Repository;
@Repository(value = "orderDaoImplForMySQL")
public class OrderDaoImplForMySQL implements OrderDao{
@Override
public void insert() {
System.out.println("MySQL資料庫正在儲存訂單資訊");
}
}
package com.rainbowsea.reflect.sprint.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "orderService")
public class OrderService {
private OrderDao orderDao ;
@Autowired
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void generate() {
orderDao.insert();
}
}
在屬性上使用@Autowired註解:
先配置一下,配置的掃描包,因為這裡,我們上面的測試 Bean 是在一個新的包下,建立的。
在set() 方法上使用@Autowired註解自動裝配:
@Autowired // set () 方法上 @Autowired 自動裝配
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
同樣也沒有問題
在無引數構造方法上的引數上,使用@Autowired註解自動裝配:
public OrderService(@Autowired OrderDao orderDao) {
this.orderDao = orderDao;
}
當有引數的構造方法只有一個時,@Autowired註解可以省略,建議不要用這種方式
當然,如果有多個構造方法,@Autowired肯定是不能省略的,會報錯。
比如:這裡我們多新增上一個無引數構造方法 。這樣就有兩個構造方法了。所以是會報錯的。
到此為止,我們已經清楚@Autowired註解可以出現在哪些位置了。
@Autowired註解預設是byType進行注入的,也就是說根據型別注入的,如果以上程式中,UserDao介面還有另外一個實現類,會出現問題嗎?會的,
@Autowired 自動裝配
根本不知道,要裝配的是哪個實現類,因為這裡有兩個類都
實現了 OrderDao 介面,而 Autowired 自動裝配是無法根據
名稱進行指定的。所以報錯。
錯誤資訊中說:不能裝配,UserDao這個Bean的數量大於1.
怎麼解決這個問題呢?當然要byName,根據名稱進行裝配了。
@Autowired註解和@Qualifier註解聯合起來才可以根據名稱進行裝配,透過在@Qualifier註解中指定Bean名稱。 如下:
@Autowired註解和@Qualifier註解聯合起來在屬性上實現名稱裝配
@Qualifier(value = "orderDaoImplForOracle")
@Autowired
private OrderDao orderDao ;
執行測試:
@Autowired註解和@Qualifier註解聯合起來在 set()方法上實現名稱裝配
@Qualifier(value = "orderDaoImplForOracle")
@Autowired
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
執行測試:
3.2.3 @Resource 註解實現賦值操作
@Resource 註解也可以完成非簡單型別 注入。那它和@Autowired 註解有什麼區別?
- @Resource 註解是JDK擴充套件包中的,換句話說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。(JSR-250標準中制定的註解型別,JSR是Java規範。)
- @Autowired 註解是Spring 框架自己的
- @Resource 註解預設根據名稱裝配byName,未指定name時,使用屬性名作為 name,透過name 找不到的話會自動啟動透過型別byType裝配。
- @Autowired 註解預設根據型別裝配byType,如果想根據名稱裝配,需要配合@Qualifier 註解一起用。
- @Resource 註解用在屬性上,setter 方法上。
- @Autowired 註解用在屬性上,setter 方法上,構造方法上,構造方法引數上。
@Resource 註解屬性JDK擴充套件包,所以不再JDK當中,需要額外引入以下依賴:如果是JDK8的話,不需要額外引入依賴,高於
JDK11或低於JDK8
需要引入以下依賴 。這裡,因為要學習Spring6,因為Spring6最低支援的是JDK17,所以我這邊的是 JDK17,需要引入這個jar包。
如何上述的,@Autowried,@Qualifier,@Resource 註解,都不能額外新增有非簡單型別引數的構造方法,不然,編譯無法透過,具體原因是什麼,我目前正在探索中。如有知道的盆友,評論留言,非常非常感謝
<dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency>
說明點:一定要注意:如果你用Spring6,要知道Spring6不再支援JavaEE,它支援的是JakartaEE9。(Oracle把JavaEE貢獻給Apache了,Apache把JavaEE的名字改成JakartaEE了,大家之前所接觸的所有的 javax. 包名統一修改為 jakarta.包名了。),想要了解更多的話,可以移步至:✏️✏️✏️ javaEE Web(Tomcat)深度理解 和 Servlet的本質_eelwxb-CSDN部落格 。如果大家用的是Spring6,就用上面按上面的那個 xml 配置就可以了,而JDK8不需要額外引入依賴。
@Resource註解的原始碼如下:
將 @Resource 運用在類的屬性上進行賦值操作:
@Resource(name = "orderDaoImplForMySQL")
private OrderDao orderDao ;
將 @Resource 運用在類的方法set ()上進行賦值操作:
@Resource(name = "orderDaoImplForOracle")
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
注意:
- 當@Resource註解使用在屬性上時,沒有指定name的時候,還是根據name進行查詢,這個name預設是是屬性名。
- 當@Resource註解使用在set() 方法上時,沒有指定name的時候,還是根據name進行查詢,這個name預設是是去掉set的屬性名,其實還是屬性名。
小總結:
- 透過註解實現“Spring的注入” @Value 註解是簡單型別上 實現對屬性上的賦值
- @Autowired自動裝配(不可根據名稱裝配配合@Qualifier 可以實現根據名稱裝配)是對非簡單型別上的賦值操作。
- @Resource 註解也是對非簡單型別 上的屬性賦值操作,需要匯入特定的jar包,注意不同版本 JDK上的匯入的包的不同。@Resource 註解屬性JDK擴充套件包,所以不再JDK當中,需要額外引入以下依賴:如果是JDK8的話,不需要額外引入依賴,高於
JDK11或低於JDK8
需要引入以下依賴 。這裡,因為要學習Spring6,因為Spring6最低支援的是JDK17,所以我這邊的是 JDK17,需要引入這個jar包。- @Resource註解是JDK擴充套件包中的,也就是說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。所以非簡單型別 的賦值使用,@Resource 註解。
<dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency>
3.3 Spring 全註解式開發
所謂的全註解開發就是不再使用spring.xml
配置檔案了。透過寫一個配置類來代替配置檔案。
透過,下面兩個註解資訊的配置,來代替配置檔案。
- @Configuration
- @ComponentScan()(用
spring.xml
配置檔案指明上面我們的掃描包上的內容。)
使用上:@Configuration,@ComponentScan() 註解編寫配置類,來代替代替配置檔案
package com.rainbowsea.reflect;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = {"com.rainbowsea.reflect.sprint.bean","com.rainbowsea.reflect.sprint.dao"})
public class Spring6Configuration {
}
編寫測試程式:因為,這裡我們已經透過:@Configuration,@ComponentScan() 註解編寫配置類,來代替代替配置檔案所以就不能再使用,new ClassPathXmlApplicationContext()物件的方式了。
要用 new AnnotationConfigApplicationContext(配置類.class) 這個來獲取配置類上的資訊。同樣的該類也是實現了。ApplicationContext 介面的。
編寫測試程式:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
package com.rainbowsea.test;
import com.rainbowsea.reflect.Spring6Configuration;
import com.rainbowsea.reflect.sprint.dao.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCAnnotationTest {
public void testIoCAnnotation() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
}
}
4. 總結:
註解回顧:Target註解和Retention註解,這兩個註解被稱為元註解。
Target註解用來設定Component註解可以出現的位置,以上代表表示Component註解只能用在類和介面上。Retention註解用來設定Component註解的保持性策略,以上代表Component註解可以被反射機制讀取。String value(); 是Component註解中的一個屬性。該屬性型別String,屬性名是value。註解的存在主要是為了簡化XML的配置。Spring6倡導全註解開發。
Spring 宣告註解的使用:
- 第一步:加入aop的依賴
- 第二步:在配置檔案中新增context名稱空間
- 第三步:在配置檔案中指定掃描的包(如果要掃描的是多個包,使用逗號隔開,或者是上一級的父級(查詢效率上,會慢一些))
第四步:在Bean類上使用註解
@Controller、@Service、@Repository 這三個註解都是@Component註解的別名。換句話說:這四個註解的功能都一樣。用哪個都可以。
只是為了增強程式的可讀性,建議:
控制器類上使用:Controller
service類上使用:Service
dao類上使用:Repository
他們都是隻有一個value屬性。value屬性用來指定bean的id,也就是bean的名字
Spring 選擇性例項化Bean物件。兩個相反的方案可以實現。
透過註解實現“Spring的注入” @Value 註解是簡單型別上 實現對屬性上的賦值,@Autowired自動裝配(不可根據名稱裝配配合@Qualifier 可以實現根據名稱裝配)是對非簡單型別上的賦值操作。@Resource 註解也是對非簡單型別 上的屬性賦值操作,需要匯入特定的jar包,注意不同版本 JDK上的匯入的包的不同。@Resource註解是JDK擴充套件包中的,也就是說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。
@Autowried,@Qualifier,@Resource 註解,都不能額外新增有非簡單型別引數的構造方法,不然,編譯無法透過,具體原因是什麼,我目前正在探索中。
Spring 全註解式開發:所謂的全註解開發就是不再使用
spring.xml
配置檔案了。透過寫一個配置類來代替配置檔案。透過,下面兩個註解資訊的配置,來代替配置檔案。
- @Configuration
- @ComponentScan()(用
spring.xml
配置檔案指明上面我們的掃描包上的內容。)
5. 疑問:
在學習Spring 註解開發的過程中,我發現了一個問題,我目前並沒有搞清楚其中的由於:希望廣大朋友可以,幫我解我的疑惑,非常感謝。
疑問:就是為什麼如下,我想要用@Autowired註解和@Qualifier註解聯合起來在 set()方法上實現名稱裝配 。但是我在該裝配的類當中,新增了一個構造方法後,執行時,發現無法編譯透過,而去了,這個構造方法,就可以編譯透過了。
如下是完整的報錯資訊:
5月 05, 2024 2:36:49 下午 org.springframework.core.LocalVariableTableParameterNameDiscoverer inspectClass 警告: Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.rainbowsea.reflect.sprint.dao.OrderService 5月 05, 2024 2:36:49 下午 org.springframework.context.support.AbstractApplicationContext refresh 警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in file [E:\Java\Spring6\spring6-oo8-anotation-blog\target\classes\com\rainbowsea\reflect\sprint\dao\OrderService.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in file [E:\Java\Spring6\spring6-oo8-anotation-blog\target\classes\com\rainbowsea\reflect\sprint\dao\OrderService.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:218)
同樣,使用 @Resource 註解進行一個set() 方法注入
賦值,如果新增了,對應 bean 有引數的構造
方法,同樣也是會報錯,編譯無法透過。
6. 最後:
“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”