Spring-IOC學習筆記
Spring 是輕量級的開源的 JavaEE 框架。
Spring有兩個核心部分IOC 和 Aop
- IOC(Inversion of Control):控制反轉,把建立物件過程交給 Spring 進行管理
- Aop(Aspect Oriented Programming):面向切面程式設計,不修改原始碼進行功能增強
Spring特點:
- 方便解耦,簡化開發
- Aop 程式設計支援
- 方便程式測試
- 方便和其他框架進行整合
- 方便進行事務操作
- 降低 API 開發難度
本篇介紹主要介紹IOC
IOC底層原理:
- 配置xml檔案,配置自己想要建立的物件
- 建立工廠類,在工廠類中通過xml解析獲取配置檔案中的class屬性值,再通過反射機制建立物件從而獲得物件例項。
Spring 提供了兩種 IOC 容器實現方式(兩個介面):
- BeanFactory:IOC 容器基本實現,是 Spring 內部的使用介面,一般不提供開發人員進行使用(想用也不是不可以拉)
- 載入配置檔案時候不會建立物件,在獲取物件(使用)才去建立物件
- ApplicationContext:BeanFactory 介面的子介面,提供更多更強大的功能,一般由開發人員進行使用
- 載入配置檔案時候就會把在配置檔案物件進行建立
Bean 管理
Bean 管理指的是兩個操作
- Spring 建立物件
- Spirng 注入屬性
其有兩種實現方式
- 基於 xml 配置檔案方式實現
- 基於註解方式實現
基於xml方式的Bean管理
-
在 spring 配置檔案中,使用 bean 標籤,標籤裡面新增對應屬性,就可以實現物件建立
-
在 bean 標籤有很多屬性,常用的屬性有
id 屬性:唯一標識
class 屬性:類全路徑(包類路徑)
-
建立物件時候,預設執行無引數構造方法完成物件建立
首先匯入相關jar包,實際版本以自己的spring版本為最終結果,我的Spring版本是5.2.6
spring-expression-5.2.6.RELEASE.jar
commons-logging-1.1.1.jar
spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
使用set方法進行注入
-
建立類,定義屬性和對應的 set 方法
public class Book { //建立屬性 private String bname; private String bauther; private String baddress; //建立屬性對應的set方法 public void setBname(String bname) { this.bname = bname; } public void setBauther(String bauther) { this.bauther = bauther; } public void setBaddress(String baddress) { this.baddress = baddress; } }
-
在 spring 配置檔案配置建立物件和注入的屬性
<bean id="book" class="com.hnust.spring5.Book"> <!--使用property完成屬性的注入 name:類裡面屬性的名稱 value:向屬性注入的值 --> <property name="bname" value="易筋經"></property> <property name="bauther" value="達摩老祖"></property> <!--設定一個空值--> <property name="baddress"> <null/> </property>--> <!--屬性包含特殊符號 方法1 把<>進行轉義 <> 方法2 把帶特殊符號的內容寫到CDATA中 --> <property name="baddress"> <value><![CDATA[<<南京>>]]></value> </property> </bean>
注入其他型別的屬性:
注入屬性-空值::
<bean id="book" class="com.hnust.spring5.Book">
<!--設定一個空值-->
<property name="baddress">
<null/>
</property>-->
</bean>
注入屬性-包含特殊符號:
<bean id="book" class="com.hnust.spring5.Book">
<!--屬性包含特殊符號
方法1 把<>進行轉義 <>
方法2 把帶特殊符號的內容寫到CDATA中
-->
<property name="baddress">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
注入屬性-外部bean
這裡以UserServiceImpl類和UserDaoImpl類進行示例
- 建立兩個類 UserServiceImpl 類和 UserDaoImpl 類
- 在 service 呼叫 dao 裡面的方法
- 在 spring 配置檔案中進行配置
public class UserServiceImpl implements UserService{
//建立UserDao型別屬性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service add ..........");
userDao.update();
}
}
<bean id="userService" class="com.hnust.spring5.service.UserServiceImpl">
<!--注入userDao物件
name屬性值:類裡的屬性名稱
ref屬性:建立userDao物件bean標籤的id值,引入外部bean
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.hnust.spring5.dao.UserDaoImpl"></bean>
注入屬性-內部bean
這裡以部門(Dept)類和員工(Emp)類進行示例
//部門類
public class Dept {
private String danme;
public String getDanme() {
return danme;
}
public void setDanme(String danme) {
this.danme = danme;
}
}
//員工類
public class Emp {
private String ename;
private String egender;
//員工屬於某一個部門,使用物件的形式進行表示
private Dept dept;
public Dept getDept() {
return dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setEgender(String egender) {
this.egender = egender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
<bean id="emp" class="com.hnust.spring5.bean.Emp">
<!--設定兩個普通屬性-->
<property name="ename" value="lucy"></property>
<property name="egender" value="woman"></property>
<!--設定物件型別的屬性-->
<property name="dept" >
<bean id="dept" class="com.hnust.spring5.bean.Dept">
<property name="danme" value="AC"></property>
</bean>
</property>
</bean>
注入屬性-集合:
public class Stu {
//陣列型別屬性
private String[] courses;
//List集合型別的屬性
private List<String> list;
//Map集合型別的屬性
private Map<String, String> maps;
//set集合型別的屬性
private Set<String> sets;
//Course型別的屬性
private List<Course> courseList;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
}
<!--集合型別屬性注入-->
<bean id="stu" class="com.hnust.spring5.Stu">
<!--陣列型別屬性注入-->
<property name="courses">
<array>
<value>java</value>
<value>c++</value>
</array>
</property>
<!--list型別屬性注入-->
<property name="list">
<list>
<value>張三</value>
<value>小張</value>
</list>
</property>
<!--map型別屬性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set型別屬性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
<!--注入list集合型別,值是物件-->
<property name="courseList">
<list>
<ref bean="course1"/>
<ref bean="course2"/>
</list>
</property>
</bean>
<!--建立多個course物件-->
<bean id="course1" class="com.hnust.spring5.Course">
<property name="cname" value="Spring5"></property>
</bean>
<bean id="course2" class="com.hnust.spring5.Course">
<property name="cname" value="SpringMVC"></property>
</bean>
對於需要反覆使用的屬性可以提取出來,以List為例
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--1 提取list集合型別屬性-->
<util:list id="bookList">
<value>易筋經</value>
<value>九陰真經</value>
<value>九陽神功</value>
</util:list>
<!--2 提取list集合型別屬性注入使用-->
<bean id="book" class="com.hnust.spring5.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
</beans>
xml自動裝配
<!--實現自動裝配
autowire屬性常用兩個值:
byName根據屬性名稱注入,注入bean的id值要和類屬性名稱一樣
byType根據屬性型別注入,相同型別的bean不能定義多個,不然會報錯
-->
<bean id="emp" class="com.hnust.spring5.autowrite.Emp" autowire="byName"></bean>
<bean id="dept" class="com.hnust.spring5.autowrite.Dept"></bean>
xml引入外部屬性檔案
以資料庫配置為例,jdbc.properties的路徑在src下,其內容如下
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
則在xml檔案中,先引入context名稱空間,再引入外部屬性檔案,如下所示
<?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:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
</bean>
</beans>
基於註解方式的Bean管理
註解是程式碼特殊標記,格式:@註解名稱(屬性名稱=屬性值, 屬性名稱=屬性值...)
註解作用在類上面,方法上面,屬性上面
使用註解目的:簡化 xml 配置
有4個註解,分別為(1)@Component (2)@Service (3)@Controller (4)@Repository
以上四個註解功能是一樣的,都可以用來建立 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"
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">
<!--開啟元件掃描
1 如果掃描多個包,則在多個包中間使用逗號隔開
2 或者寫這些包的上層目錄
-->
<context:component-scan base-package="com.hnust.spring5"></context:component-scan>
<!--示例1
use-default-filters="false" 表示現在不使用預設filter而使用中間配置的filter
context:include-filter 設定掃描內容,該示例只掃描Controller註解
-->
<context:component-scan base-package="com.hnust.spring5" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例2
use-default-filters="false" 表示現在不使用預設filter而使用中間配置的filter
context:include-filter 設定不進行掃描的內容,該示例不掃描Controller註解,剩下全部掃描
-->
<context:component-scan base-package="com.hnust.spring5">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
//在註解裡面的value屬性值可以省略不寫,預設值是類名稱並將其首字母小寫
@Service(value = "userService") //與<bean id="userService" class="包的路徑"/>寫法類似
public class UserService {
//普通屬性注入
@Value(value = "abc")
private String name;
//定義Dao型別的屬性,不需要新增set方法,整個過程已經將set方法封裝好了
//添加註入屬性註解
@Autowired //根據型別進行自動注入
@Qualifier(value = "UserDaoImpl1")//根據名稱進行注入,和@Autowired搭配使用
private UserDao userDao;
public void add(){
System.out.println("service add ........"+name);
userDao.add();
}
}
完全使用註解,不使用xml配置檔案
新建配置類,類名隨意,新增兩個註解,如下所示
@Configuration//把當前類作為配置類,代替xml配置檔案
@ComponentScan(basePackages = {"com.hnust.spring5"})//開啟元件掃描
public class SpringConfig {
}
使用xml配置時,獲取上下文物件的語句為ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
將其修改為ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
即可
Bean的生命週期
bean物件從誕生到銷燬總共經歷了7步,若去掉bean的後置處理器,則有5步。
示例程式碼如下,首先定義Orders類
public class Orders {
public Orders() {
System.out.println("第一步 執行無引數構造器建立bean的例項");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 呼叫set方法設定屬性值");
}
//建立執行的初始化方法
public void initMethod(){
System.out.println("第四步 執行初始化方法");
}
//建立執行銷燬方法
public void destroyMethod(){
System.out.println("第七步 執行銷燬方法");
}
}
定義bean的後置處理器:
public class MyBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第三步 在初始化之前執行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步 在初始化之後執行的方法");
return bean;
}
}
配置xml:
<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">
<bean id="orders" class="com.hnust.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手機"></property>
</bean>
<!--配置後置處理器-->
<bean id="myBeanPost" class="com.hnust.spring5.bean.MyBean"></bean>
</beans>
測試程式碼:
@Test
public void testBean3(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第六步 獲取到了建立的bean例項物件");
//手動讓bean例項銷燬
context.close();
}
測試結果: