1. 程式人生 > 其它 >【Spring5】IOC

【Spring5】IOC

1 Spring概念

Spring是輕量級的開源JavaEE框架。可以解決企業開發的複雜性。

Spring有兩個核心部分:IOC和Aop

①IOC控制反轉:把建立物件過程交給Spring管理

②Aop:面向切面,不修改原始碼的情況下進行功能增強

Spring5相關jar包:spring-beans-5.2.6.RELEASE.jar

spring-context-5.2.6.RELEASE.jar

spring-core-5.2.6.RELEASE.jar

spring-expression-5.2.6.RELEASE.jar

通過Spring配置檔案建立物件

    @Test
    public void test() {
//      載入Spring配置檔案
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
//      獲取配置建立的物件
        user u = (user) context.getBean("user");
        u.add();
    }

ClassPathXmlApplicationContext對應src下的檔案

FileSystemXmlApplicationContext對應磁碟路徑

bean1.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">
    <bean id="user" class="com.hikaru.spring5.user"></bean>
</beans>

2 IOC容器

控制反轉:把物件建立和物件之間的呼叫過程,交給Spring進行管理。

IOC底層原理

IOC過程: xml解析+工廠模式+反射

①配置xml檔案,配置建立的對

<bean id="user" class="com.hikaru.spring5.user"></bean>

②建立工廠類

xml解析後通過反射建立物件,進一步降低耦合度。

public class userFactory {
    public static user getUser() {
        String classValue = classuser;
        Class clazz = Class.forName(classValue);

        return clazz.newInstance();
    }
}

比如user類路徑需要修改,那麼只需要修改配置檔案

IOC介面

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

①BeanFactory:IOC容器基本實現,是Spring內部介面,不提供開發人員使用

載入配置檔案的時候不會去建立物件,只有在獲取使用的時候才建立物件

②ApplicationContext:BeanFactory介面的子介面,提供了更多更強大的功能,面向開發人員使用

載入配置檔案的時候就建立物件

web專案一般選擇第二種方式,當伺服器啟動的時候就完成這些耗時的工作

IOC操作Bean管理

兩種實現方式:

①基於配置檔案方式

建立物件

<bean id="user" class="com.hikaru.spring5.user"></bean>
bean標籤的常用屬性
id 標籤的唯一標識,不是例項名
class 安全路徑(包類路徑)
name 與id類似,但是name可以用特殊字元

這種方式建立物件時,預設執行的是無參構造器完成物件建立,如果類中只有有引數構造器沒有無參構造器則會報錯

DI依賴注入(屬性):

①使用set方法注入

<property name="userName" value="劉"></property>

②使用有引數構造器注入

<constructor-arg name="userName" value="ryuu"></constructor-arg>
<constructor-arg index="1" value="9512891"></constructor-arg>

可以通過屬性名、屬性在構造器的索引號注入依賴

③使用p名稱空間注入

首先新增p名稱空間在配置檔案中

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<bean id="user" class="com.hikaru.spring5.user" p:userName="liu" p:pwd="pwd123">
    </bean>
※ xml注入其它型別屬性
  1. 字面量

(1)空值

        <property name="userName">
            <null></null>
        </property>
        <property name="pwd">
            <null></null>
        </property>

(2)包含特殊字元 <![CDATA[...]>

        <property name="userName">
            <value><![CDATA[<><><>]]]></value>
        </property>
  1. 注入外部bean
    <bean id="service" class="com.hikaru.spring5.testDemo.service">
        <property name="d" ref="daoImpl"></property>
    </bean>
    <bean id="daoImpl" class="com.hikaru.spring5.testDemo.daoImpl">
    </bean>

必須對外部bean進行宣告

  1. 注入內部bean
    <bean id="emp" class="com.hikaru.spring5.testDemo.emp">
        <property name="name" value="luffer"></property>

        <property name="d">
            <bean id="d" class="com.hikaru.spring5.testDemo.dept">
                <property name="deptName" value="jishubu"></property>
            </bean>
        </property>
    </bean>

這裡不能使用單元測試,單元測試只允許使用一個構造器

4.級聯賦值

一種是外部bean注入屬性後再注入bean

另一種內部bean注入屬性

5.注入集合屬性

陣列:

        <property name="courses">
            <array>
                <value>Java</value>
                <value>資料庫</value>
            </array>
        </property>

list:

        <property name="list">
            <list>
                <value>張三</value>
                <value>法外狂徒</value>
            </list>
        </property>

set:

        <property name="set">
            <set>
                <value>MySQL</value>
                <value>Redis</value>
            </set>
        </property>

map:

        <property name="map">
            <map>
                <entry key="Java" value="java">
                </entry>

                <entry key="PHP" value="php">
                </entry>
            </map>
        </property>
工廠Bean
public class myBean implements FactoryBean<Course>{

    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("C#");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        Course c= (Course) context.getBean("myBean", Course.class);
        System.out.println(c);
    }
}

工廠Bean區別於普通Bean,xml配置檔案定義的Bean型別與返回型別可以不同

Bean的作用域

1 單例模式與多例模式

    <bean id="myBean" class="myBean" scope="prototype">
    </bean>
    <bean id="myBean" class="myBean" scope="singleton">
    </bean>

Spring中預設情況為單例模式,每次返回的例項都是一個例項

myBean@3d921e20
myBean@36b4cef0

上為多例模式的例項地址

Bean的生命週期

(1)通過構造器建立bean例項(無引數構造,或通過p進行有參構造)

(2)為bean的 屬性 設定值和其它物件的引用(set方法)

(3)呼叫bean的初始化方法(需要進行配置初始化的方法)

(4)獲取到bean物件進行使用

(5)當容器關閉時,呼叫bean的銷燬方法(需要進行配置銷燬的方法)

演示Bean的生命週期
public class Order {
    private String oname;

    public Order() {
        System.out.println("step1");
    }

    public String getOname() {
        return oname;
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("step2");
    }

    public void initMethod() {
        System.out.println("step3:init Bean");
    }


    public void destroyMethod() {
        System.out.println("step5:destroy Eean");
    }

    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Order order = context.getBean("Order", Order.class);
        System.out.println("step4:get bean");

        ((ClassPathXmlApplicationContext) context).close();
    }
}

ApplicationContext作為介面沒有close方法,但是其實現類ClassPathXmlApplicationContext 有,所以這裡需要進行強轉或者開始使用ClassPathXmlApplicationContext生宣告

XML

    <bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="order1"></property>
    </bean>
step1
step1
step2
step3:init Bean
step4:get bean
step5:destroy Eean
bean的後置處理器

在宣告週期的第三步之前和之後:

(1)通過構造器建立bean例項(無引數構造,或通過p進行有參構造)

(2)為bean的 屬性 設定值和其它物件的引用(set方法)

把bean例項傳遞給後置處理器的方法

(3)呼叫bean的初始化方法(需要進行配置初始化的方法)

把bean例項傳遞給後置處理器的方法

(4)獲取到bean物件進行使用

(5)當容器關閉時,呼叫bean的銷燬方法(需要進行配置銷燬的方法)

public class postBean implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before init");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after init");
        return bean;
    }
}

實現BeanPostProcessor介面的bean會被當做後置處理器

    <bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="order1"></property>
    </bean>

    <bean class="postBean"></bean>

後置bean會對xml中的所有bean生效

輸出結果:

step1
step1
step2
before init
step3:init Bean
after init
step4:get bean
step5:destroy Eean
自動裝配

根據指定的裝配規則(屬性名稱或者屬性型別),Spring自動將匹配的屬性值進行自動注入

根據屬性型別裝配

    <bean id="Emp" class="Emp" autowire="byType"></bean>
    <bean class="Dept">
        <property name="dName" value="develop"></property>
    </bean>

這種方式的外部bean不能宣告為相同的型別

根據屬性名稱裝配

        <bean id="Emp" class="Emp" autowire="byName"></bean>
        <bean id="dept" class="Dept">
            <property name="dName" value="develop"></property>
        </bean>

這種方式要求外部bean名稱與bean的屬性名相同

引入外部屬性檔案

首先新增context約束宣告

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="Druid" class="com.alibaba.druid.pool.DruidDataSource" >
        <property name="driverClassName" value="${prop.driverClassName}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>

隨後通過讀取外部properties檔案的方式建立德魯伊資料庫連線池

這裡的外部檔案獲取必須使用${prop.}的形式

②基於註解方式

註解是程式碼的特殊標記,格式:@註解名稱(屬性名稱:屬性值,屬性名稱:屬性值...),註解可以作用在類、方法、屬性上。註解的目的是簡化xml配置。

Spring針對Bean管理中建立物件提供的註解:

(1) @Componet

(2) @Service
(3) @Controller

(4) @Repository

上面四個註解功能是一樣的,都可以用來建立bean例項

步驟:

(1)引入jar包:spring-aop-5.2.6.RELEASE.jar

(2)配置檔案中新增context約束,並在配置檔案中進行元件掃描(告訴Spring哪個包、類中存在註解)

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="service"></context:component-scan>

多個包可以用,隔開 或者使用上層包名

(3)在Bean類上添加註解

@Component
public class UserService {
    public void add() {
        System.out.println("service add...");
    }

或者使用@Component(value="userService"),value值對應xml配置bean的id寫法,value省略時預設為第一個字母小寫的類名

元件過濾掃描
<context:component-scan base-package="service" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

不使用預設掃描,只掃描註解為Component的Bean

<context:component-scan base-package="service" use-default-filters="true">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>

使用預設掃描但不掃描註解為Component的Bean

註解實現屬性注入:只需要在配置檔案設定元件掃描,其他均通過類註解實現

①Autowired:根據屬性型別自動裝配

第一步 在service和dao上新增建立物件註解建立物件

第二步 在service類中的dao屬性上新增Autowired註解注入dao屬性

註解封裝了屬性的set方法,不需要再次建立

@Component
public class UserService {
    @Autowired
    private UserDao userDao;

    public void add() {
        userDao.add();
    }

②Qualifier:根據屬性名稱自動裝配,需要配合Autowired使用

@Component
public class UserService {
    @Autowired
    @Qualifier(value = "userDaoImpl")
    private UserDao userDao;

    public void add() {
        userDao.add();
    }

比如UserDao有多個實現類,所以需要註解指名是哪一個,這裡的userDaoImpl是實現類建立註解預設生成的首字母小寫的類名

③Resource:

    @Resource(name = "userDaoImpl")
    private UserDao userDao;

等價於@Autowired
           @Qualifier(value = "userDaoImpl")

④Value:

    @Value("123")
    private String name;

同樣不需要set方法

完全註解開發

配置註解另類為配置類,替代xml配置檔案

設定自動掃描元件註解

@Configuration
@ComponentScan(basePackages = {"service", "dao"})
public class SpringConfig {

}

載入配置類:AnnotationConfigApplicationContext

        ApplicationContext context =
                new AnnotationConfigApplicationContext(SpringConfig.class);