網站首頁 編程語言 正文
Spring-IOC—基于注解配置Bean
1.基本使用
1.說明
● 基本介紹
基于注解的方式配置 bean, 主要是項目開發中的組件,比如 Controller、Service、和 Dao.
● 組件注解的形式有
-
@Component 表示當前注解標識的是一個組件
-
@Controller 表示當前注解標識的是一個控制器,通常用于 Servlet
-
@Service 表示當前注解標識的是一個處理業務邏輯的類,通常用于 Service 類
-
@Repository 表示當前注解標識的是一個持久化層的類,通常用于 Dao 類
2.快速入門
● 應用實例
使用注解的方式來配置 Controller / Service / Respository / Component
● 代碼實現
- 引入 spring-aop-5.3.8.jar , 在 spring/libs 下拷貝即可
- 創建 UserAction.java UserService.java, UserDao.java MyComponent.java
/**
* 用來標識該類是一個組件,是一個通用的注解
*/
@Component
public class MyComponent {
}
/**
* 標識該類是一個控制器
*/
@Controller
public class UserController {
}
/**
* 使用@Repository標識該類是一個持久層的類/對象
*/
@Repository
public class UserDao {
}
/**
* 標識是一個Service類
*/
@Service
public class UserService {
}
beans.xml
<context:component-scan base-package="com.llp.spring.component"/>
測試
/**
* 通過注解方式配置bean
*/
@Test
public void setBeanByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
UserDao userDao = ioc.getBean(UserDao.class);
UserService userService = ioc.getBean(UserService.class);
UserController userController = ioc.getBean(UserController.class);
MyComponent myComponent = ioc.getBean(MyComponent.class);
System.out.println(userDao);
System.out.println(userService);
System.out.println(userController);
System.out.println(myComponent);
}
3.注意事項和細節說明
1.需要導入 spring-aop-5.3.8.jar
2.必須在 Spring 配置文件中指定"自動掃描的包",IOC 容器才能夠檢測到當前項目中哪些類被標識了注解, 注意到導入 context 名稱空間
xmlns:context=“http://www.springframework.org/schema/context”
<context:component-scan base-package="com.llp.spring.component"/>
可以使用通配符 * 來指定 ,比如 com.llp.spring.* 表示
提問: com.hspedu.spring.component 會不會去掃描它的子包? 會的
3.Spring 的 IOC 容器不能檢測一個使用了@Controller 注解的類到底是不是一個真正的控制器。注解的名稱是用于程序員自己識別當前標識的是什么組件。
其它的@Service、@Repository 也是一樣的道理 [也就是說spring的IOC容器只要檢查到注解就會生成對象,但是這個注解的含義 spring 不會識別,注解是給程序員編程方便看的]
4.resource-pattern=“User*.class”: 表示只掃描滿足User打頭的類.[使用的少,不想掃描,不寫注解就可以, 知道這個知識點即可]
<context:component-scan base-package="com.llp.spring.component" resource-pattern="User*.class"/>
5.排除哪些類 , 以 annotaion 注解為例,如下就是排除@Service注解和@Repository類
<context:component-scan base-package="com.llp.spring.component">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
context:component-scan>
6.指定自動掃描哪些注解類
<context:component-scan base-package="com.llp.spring.component" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
context:component-scan>
debug查看beanFactory中SingletonObjects對象中table屬性的值
7.默認情況:標記注解后,類名首字母小寫作為 id 的值。也可以使用注解的 value 屬性指定 id 值,并且 value 可以省略。[代碼演示]
/**
* 標識該類是一個控制器
*/
@Controller(value = "user")
public class UserController {
}
/**
* 通過注解方式配置bean
*/
@Test
public void setBeanByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
//在獲取userController時 id變成對應的value值
UserService userService = ioc.getBean("user",UserService.class);
System.out.println(userController);
}
2.手動開發-簡單的Spring基于注解配置的程序
1.需求說明
- 自己寫一個簡單的 Spring 容器, 通過讀取類的注解(@Component @Controller @Service @Reponsitory),將對象注入到 IOC 容器
- 也就是說,不使用 Spring 原生框架,我們自己使用 IO+Annotaion+反射+集合 技術實現, 打通 Spring 注解方式開發的技術痛點
2.思路分析
-
思路分析+程序結構
1)我們使用注解方式完成, 這里2不用 xml 來配置
2) 程序框架圖
● 應用實例
- 手動實現注解的方式來配置 Controller / Service / Respository / Component
- 我們使用自定義注解來完成.
3.代碼實現
1.搭建基本結構并獲取掃描包
2.獲取掃描包下所有.class文件
3.獲取全類名 反射對象放入容器
自定義包掃描注解 @ComponentScan
//注解指定運行的類型
@Target(ElementType.TYPE)
//應用范圍
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ComponentScan {
String value() default "";
}
配置類
//配置類, 作用類似我們原生spring的 beans.xml 容器配置文件
@ComponentScan(value = "com.llp.spring.component")
public class LlpSpringConfig {
}
自定義容器-LlpSpringApplicationContext
public class LlpSpringApplicationContext {
private Class aClass;
//ioc我存放的就是通過反射創建的對象(基于注解方式)
private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();
public LlpSpringApplicationContext(Class aClass) {
//傳入LlpSpringConfig
this.aClass = aClass;
//根據LlpSpringConfig類獲取到@ComponentScan注解
ComponentScan componentScan = (ComponentScan) aClass.getDeclaredAnnotation(ComponentScan.class);
//獲取@ComponentScan注解配置的包路徑
//basePackage=com.llp.spring.component
String basePackage = componentScan.value();
System.out.println("basePackage=" + basePackage);
ClassLoader classLoader = LlpSpringApplicationContext.class.getClassLoader();
//path=com/llp/spring/component
String path = basePackage.replace(".", "/");
System.out.println("path=" + path);
//根據包路徑獲取目錄的絕對路徑
// file:/C:/ide/IdeaProjects/llp-spring/out/production/llp-spring/com/llp/spring/component
URL resource = classLoader.getResource(path);
System.out.println(resource);
//從URL對象中獲取file 目錄
File file = new File(resource.getFile());
//判斷文件是否時一個目錄
if (file.isDirectory()) {
//獲取目錄下所有的文件
File[] files = file.listFiles();
//遍歷
for (File f : files) {
System.out.println("=========");
//獲取目錄下每個文件的絕對路徑
String absolutePath = f.getAbsolutePath();
//C:\ide\IdeaProjects\llp-spring\out\production\llp-spring\com\llp\spring\component\MyComponent.class
System.out.println(f.getAbsolutePath());
//1.獲取類名
String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf(".class"));
System.out.println(className);
//2.獲取類的全類名
//com.llp.spring.component
String classFullName = path.replace("/", ".") + "." + className;
System.out.println(classFullName);
//3.進行過濾,只處理文件目錄下的.class文件
if (absolutePath.endsWith(".class")) {
//4.判斷是不是需要注入到Spirng容器中,看該類是不是有注解@Component @Service
try {
//1. Class clazz = Class.forName(classFullName) 可以反射加載類
//2. classLoader.loadClass(classFullName); 可以反射類的Class
//3. 區別是 : 上面方式后調用來類的靜態方法, 下面方法不會
//4. aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @Component
//得到com.llp.spring.component包下的類的Class對象
Class<?> cls = classLoader.loadClass(classFullName);
//判斷該類上是否有@Component、@Controller、@Service、@Repository注解
if (cls.isAnnotationPresent(Component.class) ||
cls.isAnnotationPresent(Controller.class) ||
cls.isAnnotationPresent(Service.class) ||
cls.isAnnotationPresent(Repository.class)) {
Class<?> clazz = Class.forName(classFullName);
Object o = clazz.newInstance();
String id = null;
if(cls.isAnnotationPresent(Component.class)){
Component component = cls.getAnnotation(Component.class);
id = component.value();
className = id;
}
//StringUtils.uncapitalize(className)將類的首字母小寫
ioc.put(id==null? StringUtils.uncapitalize(className):id,o);
System.out.println(ioc);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String name){
return ioc.get(name);
}
}
測試類
public class LlpSpringApplicationContextTest {
public static void main(String[] args) {
LlpSpringApplicationContext ioc = new LlpSpringApplicationContext(LlpSpringConfig.class);
UserService userService = (UserService)ioc.getBean("userService");
UserDao userDao = (UserDao)ioc.getBean("userDao");
UserController userController = (UserController)ioc.getBean("userController");
MyComponent myComponent = (MyComponent)ioc.getBean("myComponent");
}
}
測試結果
3.自動裝配
1.基本說明
● 基本說明
-
基于注解配置 bean,也可實現自動裝配,使用的注解是:@AutoWired 或者 @Resource
-
@AutoWired 的規則說明
1)在 IOC 容器中查找待裝配的組件的類型,如果有唯一的 bean 匹配,則使用該 bean 裝配
2)如待裝配的類型對應的 bean 在 IOC 容器中有多個,則使用待裝配的屬性的屬性名作為 id 值再進行查找, 找到就裝配,找不到就拋異常
-
@Resource 的規則說明
1)@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將@Resource 注解的 name 屬性解析為 bean 的名字,而 type 屬性則解析為 bean 的類型.所以如果使用 name 屬 性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略
2)如果@Resource 沒有指定 name 和 type ,則先使用byName注入策略, 如果匹配不上, 再使用 byType 策略, 如果都不成功,就會報錯
-
建議,不管是@Autowired 還是 @Resource 都保證屬性名是規范的寫法就可以 注入
2.注意事項和細節說明
- 如待裝配的類型對應的 bean 在 IOC 容器中有多個,則使用待裝配的屬性的屬性名作 為 id 值再進行查找, 找到就裝配,找不到就拋異常
4.泛型依賴注入
1.泛型依賴解釋
● 基本說明
- 為了更好的管理有繼承和相互依賴的 bean 的自動裝配,spring 還提供基于泛型依賴的 注入機制
- 在繼承關系復雜情況下,泛型依賴注入就會有很大的優越性
2.應用實例
● 應用實例需求
- 各個類關系圖
- 傳統方法是將 PhoneDao /BookDao 自動裝配到 BookService/PhoneSerive 中,當這 種繼承關系多時,就比較麻煩,可以使用 spring 提供的泛型依賴注入
● 應用實例-代碼實現
//自定義的泛型類
public abstract class BaseDao<T> {
public abstract void save();
}
//自定義泛型類
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
public class Book {
}
public class Phone {
}
@Repository
public class BookDao extends BaseDao<Book>{
@Override
public void save() {
System.out.println("BookDao 的 save()..");
}
}
@Repository
public class PhoneDao extends BaseDao<Phone>{
@Override
public void save() {
System.out.println("PhoneDao save()");
}
}
@Service
public class BookService extends BaseService<Book>{
//并沒有寫屬性
}
@Service
public class PhoneService extends BaseService<Phone>{
}
//通過泛型依賴來配置Bean
@Test
public void setProByDependencyInjection() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans07.xml");
PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
phoneService.save();
System.out.println("ok");
}
原文鏈接:https://blog.csdn.net/qq_44981526/article/details/124831734
相關推薦
- 2022-07-26 讓一個有寬高的盒子垂直水平居中
- 2022-10-26 Golang?Mutex?原理詳細解析_Golang
- 2023-06-03 C++一個函數如何調用其他.cpp文件中的函數_C 語言
- 2022-09-04 使用Docker容器部署MongoDB并支持遠程訪問及遇到的坑_docker
- 2022-07-03 python編碼格式導致csv讀取錯誤問題(csv.reader,?pandas.csv_read)
- 2022-07-03 el-form表單新增表單項動態校驗;el-form校驗動態表單v-if不生效;
- 2022-04-08 Python裝飾器中@property使用詳解_python
- 2022-07-21 微信小程序使用vant weapp報錯
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支