今日目標
學習使用XML配置第三方Bean
掌握純注解開發定義Bean對象
掌握純注解開發IOC模式
1. 第三方資源配置管理
說明:以管理DataSource連接池對象為例講解第三方資源配置管理
1.1 XML管理Druid連接池(第三方Bean)對象【重點】
數據庫準備
--?創建數據庫
create?database?if?not?exists?spring_druid?character?set?utf8;
use?spring_druid;
--?創建表
create?table?if?not?exists?tbl_account(id?int?primary?key?auto_increment,name?varchar(20),money?double
);
--?插入數據
insert?into?tbl_account?values(null,'張三',1000);
insert?into?tbl_account?values(null,'李四',1000);
--?查詢所有
select?*?from?tbl_account;
1.2 代碼實現XML管理Druid連接池對象(第三方Bean)
【第一步】創建12_1_xml_druid項目
【第二步】Pom.xml添加Druid連接池依賴
?<dependencies><!--導入spring的坐標spring-context,對應版本是5.3.15.RELEASE--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.15</version></dependency><!--?mysql?驅動--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--druid包--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!--?導入junit的測試包?--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency>
</dependencies>
【第三步】配置DruidDataSource連接池Bean對象 思考:配置數據庫連接參數時,注入驅動類名是用driverClassName還是driver? 在resources下創建Spring的核心配置文件:application.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.創建連接池對象?DruidDataSource實際:??dataSource?=?new?DruidDataSource();--><bean?id="dataSource"?class="com.alibaba.druid.pool.DruidDataSource"><property?name="driverClassName"?value="com.mysql.jdbc.Driver"/><property?name="url"?value="jdbc:mysql://localhost:3306/spring_druid"/><property?name="username"?value="root"/><property?name="password"?value="root"/></bean>
</beans>
【第四步】在測試類中從IOC容器中獲取連接池對象并打印
package?com.zbbmeta;import?org.junit.jupiter.api.Test;
import?org.springframework.context.support.ClassPathXmlApplicationContext;import?javax.sql.DataSource;
import?java.sql.Connection;
import?java.sql.SQLException;public?class?DataSourceTest?{@Testpublic?void?test()?throws?SQLException?{//目標:從IOC容器中獲取德魯伊連接池對象//1.創建IOC容器ClassPathXmlApplicationContext?ac?=new?ClassPathXmlApplicationContext("application.xml");//2.獲取連接池對象和數據庫連接對象DataSource?dataSource?=?ac.getBean(DataSource.class);Connection?connection?=?dataSource.getConnection();//3.打印對象System.out.println("連接池對象:"+dataSource);System.out.println("連接象地址:"+connection);//4.關閉容器ac.close();}
}
-
控制臺結果:
現在我們的數據庫參數都是寫死在xml文件中的,我們講解druid,是希望大家可以通過這個第三方Bean創建,實現我們舉一反三實現其他第三方Bean的創建
思考:根據上面描述我們向如果有一千個第三方Bean需要創建,那么我們把每一個Bean的參數都寫死在xml里面?
肯定不是的,所以我們要學習如何將參數數據進行提取出來,每一個Bean的參數單獨放一個文件,方便我們查找和修改
1.3 加載properties屬性文件【重點】
目的:將數據庫的連接參數抽取到一個單獨的文件中,與Spring配置文件解耦。
1.3.1 properties基本用法
【第一步】在resources下編寫jdbc.properties屬性文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_druid
username=root
jdbc.password=root
【第二步】在application.xml中開啟開啟context命名空間,加載jdbc.properties屬性文件
<context:property-placeholder?location="jdbc.properties"?/>
【第三步】在配置連接池Bean的地方使用EL表達式獲取jdbc.properties屬性文件中的值
????<!--1.創建連接池對象?DruidDataSource實際:??dataSource?=?new?DruidDataSource();-->
<bean?id="dataSource"?class="com.alibaba.druid.pool.DruidDataSource"><property?name="driverClassName"?value="${jdbc.driver}"/><property?name="url"?value="${jdbc.url}"/><property?name="username"?value="${username}"/><property?name="password"?value="${jdbc.password}"/>
</bean>
【第四步】配置完成之后,運行之前的獲取Druid連接池代碼
思考:會不會運行成功
不會
嚴重:?create?connection?SQLException,?url:?jdbc:mysql://127.0.0.1:3306/spring_druid,?errorCode?1045,?state?28000
java.sql.SQLException:?Access?denied?for?user?'zbb'@'localhost'?(using?password:?YES)
為什么會出現這樣的問題?
因為我們加載了系統的環變量
-
解決1:換一個名稱,例如不叫username,叫jdbc.username。(了解)
【第五步】報錯解決方式:在properties標簽添加屬性
<context:property-placeholder?location="jdbc.properties"?system-properties-mode="NEVER"/>
application.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?https://www.springframework.org/schema/context/spring-context.xsd"><!--加載properties文件,?ctrl?+?z?撤銷${鍵}:?取鍵對應的值system-properties-mode="NEVER":?不使用系統的環境變量location:?指定properties文件的位置location="jdbc.properties,msg.properties":?加載多個properties文件,?使用,逗號分割location="*.properties":?加載所有的properties文件,如果單元測試加載?src/test/resources里面的所有的properties如果main方法運行加載?src/main/resources里面的所有的propertieslocation="classpath:*.properties"?加載類路徑所有的properties文件,?用在Web應用中location="classpath*:*.properties"?加載類路徑和依賴的jar中所有的properties文件,?用在Web應用中--><context:property-placeholder?location="jdbc.properties"?system-properties-mode="NEVER"/><!--1.創建連接池對象?DruidDataSource實際:??dataSource?=?new?DruidDataSource();--><bean?id="dataSource"?class="com.alibaba.druid.pool.DruidDataSource"><property?name="driverClassName"?value="${jdbc.driver}"/><property?name="url"?value="${jdbc.url}"/><property?name="username"?value="${username}"/><property?name="password"?value="${jdbc.password}"/></bean>
<!--????<bean?id="dataSource"?class="com.alibaba.druid.pool.DruidDataSource"><property?name="driverClassName"?value="com.mysql.jdbc.Driver"/><property?name="url"?value="jdbc:mysql://localhost:3306/spring_druid"/><property?name="username"?value="root"/><property?name="password"?value="root"/></bean>-->
</beans>
2.Spring容器
2.1 創建容器
-
方式一:類路徑加載配置文件
ApplicationContext?ctx?=?new?ClassPathXmlApplicationContext("application.xml");
-
方式二:文件路徑加載配置文件
ApplicationContext?ctx?=?new?FileSystemXmlApplicationContext("D:\\application.xml");
-
加載多個配置文件
ApplicationContext?ctx?=?new?ClassPathXmlApplicationContext("bean1.xml",?"bean2.xml");
2.2 Spring容器中獲取bean對象
-
方式一:使用bean名稱獲取
弊端:需要自己強制類型轉換
DataSource?dataSource?=?(DataSource)ac.getBean("dataSource");
-
方式二:使用bean名稱獲取并指定類型
弊端:推薦使用
DataSource?dataSource?=?ctx.getBean("dataSource",?DataSource.class);
-
方式三:使用bean類型獲取
弊端:如果IOC容器中同類型的Bean對象有多個,此處獲取會報錯
DataSource?dataSource?=?ac.getBean(DataSource.class);
2.3 容器類層次結構
-
BeanFactory是IoC容器的頂層接口,初始化BeanFactory對象時,加載的bean延遲加載
-
ApplicationContext接口是Spring容器的核心接口,初始化時bean立即加載
-
ApplicationContext接口提供基礎的bean操作相關方法,通過其他接口擴展其功能
- ApplicationContext接口常用初始化類
-
ClassPathXmlApplicationContext(常用)
-
FileSystemXmlApplicationContext
-
AnnotationConfigApplicationContext
-
3. Spring注解開發
3.1 注解開發定義Bean對象【重點】
目的:xml配置Bean對象有些繁瑣,使用注解簡化Bean對象的定義
3.2代碼實現注解開發
【第一步】創建12_2_annotation_ioc
【第二步】Pom.xml添加依賴
<dependencies><!--?spring容器包?--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><!--?junit?--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency>
</dependencies>
【第三步】在application.xml中開啟Spring注解包掃描
<?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?https://www.springframework.org/schema/context/spring-context.xsd"><!--開啟IOC基礎包掃描:目的是去到指定包下掃描IOC注解進行IOC功能使用spring框架優勢:可插拔白話:使用這個功能就插入,不用這個功能就拔掉那么這個注解掃描的功能就符合可插拔的特性,配置上IOC注解掃描就進行掃描,不配置不會做掃描--><context:component-scan?base-package="com.zbbmeta"?/>
</beans>
【第四步】在類上使用@Component注解定義Bean。
public?interface?StudentDao?{/***?添加學生*/void?save();
}
package?com.zbbmeta.dao.impl;import?com.zbbmeta.dao.StudentDao;
import?org.springframework.stereotype.Component;/***?@Component*?作用:相當于<bean>標簽,用于創建IOC創建對象并加入IOC容器*?使用方法2種格式:*????@Component?創建對象并且設置對象別名為類名小駝峰,*??????????????與<bean?class="com.zbbmeta.dao.impl.StudentDaoImpl"?id="studentDaoImpl"></bean>功能一樣*????@Component("自定義別名")?創建對象并且設置別名加入IOC容器**???IOC創建對象注解還有衍生的3個*??????@Controller?定義表現層的對象*??????@Service?定義業務層的對象*??????@Repository?定義數據訪問層的對象*??????說明:這3個功能與@Component一樣,只是為了增加可讀性*????????????@Component適合在工具類的上面使用創建對象**/
@Component
public?class?StudentDaoImpl?implements?StudentDao?{@Overridepublic?void?save()?{System.out.println("DAO:?添加學生信息到數據庫...");}
}
補充說明:如果@Component注解沒有使用參數指定Bean的名稱,那么類名首字母小寫就是Bean在IOC容器中的默認名稱。例如:StudentDaoImpl對象在IOC容器中的名稱是studentDaoImpl。
【第五步】在測試類中獲取Bean對象
package?com.zbbmeta;import?com.zbbmeta.dao.StudentDao;
import?org.junit.jupiter.api.Test;
import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.ClassPathXmlApplicationContext;public?class?StudentDaoAnnotationTest?{//目標:獲取注解創建的Bean對象@Testpublic?void?testAnnotation(){//1.根據配置文件application.xml創建IOC容器ApplicationContext?ac?=?new?ClassPathXmlApplicationContext("application.xml");//2.從IOC容器里面獲取id="studentDao"對象StudentDao?studentDao?=?(StudentDao)?ac.getBean("studentDaoImpl");System.out.println(studentDao);}
}
運行結果
com.zbbmeta.dao.impl.StudentDaoImpl@7d64e326
注意:每一個人獲取的地址是不一樣的
3.3 @Component三個衍生注解
說明:加粗的注解為常用注解
- Spring提供**
@Component
**注解的三個衍生注解-
**
@Controller
**:用于表現層bean定義 -
**
@Service
**:用于業務層bean定義 -
@Repository
:用于數據層bean定義
-
說明:這3個功能與@Component一樣,只是為了增加可讀性
@Component適合在工具類的上面使用創建對象
@Repository
public?class?StudentDaoImpl?implements?StudentDao?{
}
我們上面代碼雖然類中都使用注解,但是我們還是存在xml,說明現在spring開發還不是完全的注解開發,可以稱為半注解開發
4 Spring純注解開發模式IOC【重點】
問題導入
思考:配置類上使用什么注解進行Spring注解包掃描替代xml中的配置?
4.1 純注解開發模式介紹
-
Spring3.0開啟了純注解開發模式,使用Java類替代配置文件,開啟了Spring快速開發賽道
-
Java類代替Spring核心配置文件
-
@Configuration注解用于設定當前類為配置類
-
@ComponentScan注解用于設定掃描路徑
注意:此注解只能添加一次,多個數據請用數組格式
@ComponentScan({com.zbbmeta.service","com.zbbmeta.dao"})
-
讀取Spring注解配置類初始化容器對象
//加載配置類初始化容器
ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(SpringConfig.class);
4.2 代碼演示
【第零步】創建12_3_full_annotation_ioc項目并添加依賴
依賴和上一個項目相同
【第一步】定義配置類代替配置文件
@Configuration?//?指定這個類為配置類,替代application.xml
@ComponentScan("com.zbbmeta")//代替<context:component-scan?base-package="com.zbbmeta"?/>
//設置bean掃描路徑,多個路徑書寫為字符串數組格式
//@ComponentScan({com.zbbmeta.service","com.zbbmeta.dao"})
public?class?SpringConfig?{
}
【第二步】在測試類中加載配置類,獲取Bean對象并使用
package?com.zbbmeta;import?com.zbbmeta.config.SpringConfig;
import?com.zbbmeta.service.StudentService;
import?org.junit.jupiter.api.Test;
import?org.springframework.context.ApplicationContext;
import?org.springframework.context.annotation.AnnotationConfigApplicationContext;public?class?StudentDaoAnnotationTest?{//目標:獲取注解創建的Bean對象@Testpublic?void?testAnnotation(){//1.AnnotationConfigApplicationContext加載Spring配置類初始化Spring容器ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(SpringConfig.class);StudentService?studentService?=?(StudentService)?ctx.getBean("studentServiceImpl");System.out.println(studentService);//按類型獲取beanStudentService?studentService2?=?ctx.getBean(StudentService.class);System.out.println(studentService2);}
}
4.3 注解開發Bean作用范圍和生命周期管理
問題導入
思考:在類上使用什么注解定義Bean的作用范圍?
4.3.1 bean作用范圍注解配置
-
使用@Scope定義bean作用范圍
@Component
@Scope("singleton")
public?class?StudentUtils?{
}
4.3.2 bean生命周期注解配置
-
使用@PostConstruct、@PreDestroy定義bean生命周期
package?com.zbbmeta.utils;import?org.springframework.context.annotation.Scope;
import?org.springframework.stereotype.Component;
import?javax.annotation.PostConstruct;
import?javax.annotation.PreDestroy;@Component
@Scope("singleton")
public?class?StudentUtil?{public?StudentUtil()?{System.out.println("Student?constructor?...");}@PostConstructpublic?void?init(){System.out.println("Student?init?...");}@PreDestroypublic?void?destroy(){System.out.println("Student?destory?...");}
}
注意:@PostConstruct和@PreDestroy注解是jdk中提供的注解,從jdk9開始,jdk中的javax.annotation包被移除了,也就是說這兩個注解就用不了了,可以額外導入一下依賴解決這個問題。
<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
</dependency>
-
測試類
@Test
public?void?testStudentUtil(){//1.AnnotationConfigApplicationContext加載Spring配置類初始化Spring容器AnnotationConfigApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(SpringConfig.class);//按類型獲取beanStudentUtil?studentUtil?=?ctx.getBean(StudentUtil.class);System.out.println(studentUtil);//關閉容器ctx.close();
}
測試結果: