1. 程式人生 > 其它 >Spring-IOC學習筆記

Spring-IOC學習筆記

Spring 是輕量級的開源的 JavaEE 框架。

Spring有兩個核心部分IOC 和 Aop

  1. IOC(Inversion of Control):控制反轉,把建立物件過程交給 Spring 進行管理
  2. Aop(Aspect Oriented Programming):面向切面程式設計,不修改原始碼進行功能增強

Spring特點:

  1. 方便解耦,簡化開發
  2. Aop 程式設計支援
  3. 方便程式測試
  4. 方便和其他框架進行整合
  5. 方便進行事務操作
  6. 降低 API 開發難度

本篇介紹主要介紹IOC

IOC底層原理:

  1. 配置xml檔案,配置自己想要建立的物件
  2. 建立工廠類,在工廠類中通過xml解析獲取配置檔案中的class屬性值,再通過反射機制建立物件從而獲得物件例項。

Spring 提供了兩種 IOC 容器實現方式(兩個介面):

  • BeanFactory:IOC 容器基本實現,是 Spring 內部的使用介面,一般不提供開發人員進行使用(想用也不是不可以拉)
    • 載入配置檔案時候不會建立物件,在獲取物件(使用)才去建立物件
  • ApplicationContext:BeanFactory 介面的子介面,提供更多更強大的功能,一般由開發人員進行使用
    • 載入配置檔案時候就會把在配置檔案物件進行建立

Bean 管理

Bean 管理指的是兩個操作

  1. Spring 建立物件
  2. Spirng 注入屬性

其有兩種實現方式

  • 基於 xml 配置檔案方式實現
  • 基於註解方式實現

基於xml方式的Bean管理

  1. 在 spring 配置檔案中,使用 bean 標籤,標籤裡面新增對應屬性,就可以實現物件建立

  2. 在 bean 標籤有很多屬性,常用的屬性有

    id 屬性:唯一標識

    class 屬性:類全路徑(包類路徑)

  3. 建立物件時候,預設執行無引數構造方法完成物件建立

首先匯入相關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方法進行注入

  1. 建立類,定義屬性和對應的 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;
        }
    }
    
  2. 在 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 把<>進行轉義 &lt;&gt;
            方法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 把<>進行轉義 &lt;&gt;
        方法2 把帶特殊符號的內容寫到CDATA中
    -->
    <property name="baddress">
        <value><![CDATA[<<南京>>]]></value>
    </property>
</bean>

注入屬性-外部bean

這裡以UserServiceImpl類和UserDaoImpl類進行示例

  1. 建立兩個類 UserServiceImpl 類和 UserDaoImpl 類
  2. 在 service 呼叫 dao 裡面的方法
  3. 在 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();
}

測試結果: