1. 程式人生 > 其它 >spring之IOC

spring之IOC

IOC(控制反轉)

什麼是IOC(控制反轉)

把物件建立和物件之間的呼叫過程,交給Spring進行管理

使用IOC目的:為了降低耦合度

IOC底層

  • xml解析
  • 工廠模式
  • 反射

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

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

ApplicationContext介面的實現類(具體根據API文件檢視☺)

Bean管理

IOC操作Bean管理就是兩個操作

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

基於XML配置檔案建立物件

set方式

public class Book {
    //建立屬性
    private String bname;
    private String bauthor;
    private String address;
    //spring注入屬性值時呼叫的是屬性的set方法
    public void setBname(String bname) {
        this.bname = bname;
    }
    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void testDemo() {
        System.out.println(bname+"::"+bauthor+"::"+address);
    }


}
<!--
id: 唯一識別符號
class: 全類名
property: 屬性
name: 屬性名
value: 屬性值
-->
<bean id="book" class="com.birdy.bean.Book">
                <property name="bname" value="挪威的森林"></property>
                <property name="bauthor" value="村上春樹"></property>
                <property name="address" value="Japan"></property>
   </bean>

預設呼叫無參構造器

有參構造

//(1)傳統方式:建立類,構建有參函式
public class Orders {
    //屬性
    private String oname;
    private String address;
    //有引數構造
    public Orders(String oname,String address) {
        this.oname = oname;
        this.address = address;
    }
  }

<!--(2)spring方式:有引數構造注入屬性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
    <constructor-arg name="oname" value="Hello"></constructor-arg>
    <constructor-arg name="address" value="China!"></constructor-arg>
</bean>

注入特殊字元

<bean id="book" class="com.atguigu.spring5.Book">
    <!--(1)null值-->
    <property name="address">
        <null/><!--屬性裡邊新增一個null標籤-->
    </property>
    
    <!--(2)特殊符號賦值-->
     <!--屬性值包含特殊符號
    把<>進行轉義 &lt; &gt;或把帶特殊符號內容寫到CDATA
      -->
        <property name="address">
            <value><![CDATA[<<南京>>]]></value>
        </property>
</bean>

特殊字元處理

  • 轉義
  • ![CDATA[]]

注入外部bean屬性

建立兩個類service和dao類

public class UserService {//service類
    //建立UserDao型別屬性,生成set方法
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add() {
        System.out.println("service add...............");
        userDao.update();//呼叫dao方法
    }
}

public class UserDaoImpl implements UserDao {//dao類

    @Override
    public void update() {
        System.out.println("dao update...........");
    }
}

spring配置

<!--1 service和dao物件建立-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
    <!--注入userDao物件
        name屬性:類裡面屬性名稱
        ref屬性:外部物件bean標籤id值
    -->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

測試

@Test
    public void testExternalBean(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();

    }

/*列印:
service add...............
dao update...........
*/

XML注入內部bean和級聯賦值

一對多關係

//部門類
public class Dept {
    private String dname;
    public void setDname(String dname) {
        this.dname = dname;
    }
}

//員工類
public class Emp {
    private String ename;
    private String gender;
    //員工屬於某一個部門,使用物件形式表示
    private Dept dept;
    
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}

<!--內部bean-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--設定兩個普通屬性-->
        <property name="ename" value="Andy"></property>
        <property name="gender" value="女"></property>
        <!--設定物件型別屬性-->
        <property name="dept">
            <bean id="dept" class="com.atguigu.spring5.bean.Dept"><!--內部bean賦值-->
                <property name="dname" value="宣傳部門"></property>
            </bean>
        </property>
    </bean>

注入屬性-級聯賦值

<!--方式一:級聯賦值-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--設定兩個普通屬性-->
        <property name="ename" value="Andy"></property>
        <property name="gender" value="女"></property>
        <!--級聯賦值-->
        <property name="dept" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.atguigu.spring5.bean.Dept">
        <property name="dname" value="公關部門"></property>
    </bean>

 //方式二:生成dept的get方法(get方法必須有!!)
    public Dept getDept() {
        return dept;
    }

 <!--級聯賦值-->
    <bean id="emp" class="com.atguigu.spring5.bean.Emp">
        <!--設定兩個普通屬性-->
        <property name="ename" value="jams"></property>
        <property name="gender" value="男"></property>
        <!--級聯賦值-->
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="技術部門"></property>
    </bean>
    <bean id="dept" class="com.atguigu.spring5.bean.Dept">
    </bean>

xml 注入集合屬性

//學生類
public class Stu {
    //1 陣列型別屬性
    private String[] courses;
    //2 list集合型別屬性
    private List<String> list;
    //3 map集合型別屬性
    private Map<String,String> maps;
    //4 set集合型別屬性
    private Set<String> sets;

    //學生所學多門課程
    private List<Course> courseList;
    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    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 test() {
        System.out.println(Arrays.toString(courses));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(sets);
        System.out.println(courseList);
    }
}
//課程類
public class Course {
    private String cname; //課程名稱
    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Course{" +
                "cname='" + cname + '\'' +
                '}';
    }
}

xml注入

 <bean id="stu" class="com.birdy.bean.Stu">
        <!-- String[] courses;-->
            <property name="courses">
                 <array>
                      <value>spring</value>
                      <value>springmvc</value>
                 </array>
            </property>
     
        <!-- List<String> list-->
            <property name="list">
                 <list>
                     <value>丸子</value>
                     <value>莉香</value>
                 </list>
            </property>
     
        <!-- Map<String,String> maps;-->
            <property name="maps">
                <map>
                    <entry key="key1" value="value1"></entry>
                    <entry key="key2" value="value2"></entry>
                </map>
            </property>
     
<!--            Set<String> sets;-->
            <property name="sets">
                <set>
                    <value>mysql</value>
                    <value>redis</value>
                </set>
            </property>
     
<!--            List<Course> courseList;-->
            <property name="courseList">
                <list>
                    <ref bean="course1"></ref>
                    <ref bean="course2"></ref>
                </list>
            </property>
        </bean>
    <bean id="course1" class="com.birdy.bean.Course">
        <property name="cname" value="spring5"></property>
    </bean>

    <bean id="course2" class="com.birdy.bean.Course">
        <property name="cname" value="springmvc"></property>
    </bean>

xml提取公共注入部分

  1. 引入名稱空間util
  2. 使用 util 標籤完成 注入提取
<!--第一步:在 spring 配置檔案中引入名稱空間 util-->
<?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" <!--新增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">  <!--新增util名稱空間-->
    
<!--第二步:使用 util 標籤完成 list 集合注入提取-->
<!--把集合注入部分提取出來-->
 <!--1 提取list集合型別屬性注入-->
    <util:list id="bookList">
        <value>易筋經</value>
        <value>九陰真經</value>
        <value>九陽神功</value>
    </util:list>

 <!--2 提取list集合型別屬性注入使用-->
    <bean id="book" class="com.atguigu.spring5.collectiontype.Book">
        <property name="list" ref="bookList"></property>
    </bean>

FactoryBean

Spring 有兩種型別 bean,一種普通 bean,另外一種工廠 bean(FactoryBean

  • 普通 bean:在配置檔案中定義 bean 型別就是返回型別
  • 工廠 bean:在配置檔案定義 bean 型別可以和返回型別不一樣

第一步,建立類,讓這個類作為工廠 bean,實現介面 FactoryBean 。第二步,實現接口裡面的方法,在實現的方法中定義返回的 bean 型別

public class MyBean implements FactoryBean<Course> {

    //定義返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }
}

<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
@Test
public void test3() {
     ApplicationContext context =
     new ClassPathXmlApplicationContext("bean3.xml");
     Course course = context.getBean("myBean", Course.class);//返回值型別可以不是定義的bean型別!
     System.out.println(course);
}

bean 作用域

在 Spring 裡面,預設情況下,bean 是單例項物件。在 spring 配置檔案 bean 標籤裡面有屬性scope用於設定單例項還是多例項

scope 屬性值 :

  • 預設值singleton,表示是單例項物件
  • prototype,表示是多例項物件

bean 生命週期

bean 生命週期有七步 ,正常生命週期為五步,而配置後置處理器後為七步
(1)通過構造器建立 bean 例項(無引數構造)
(2)為 bean 的屬性設定值和對其他 bean 引用(呼叫 set 方法)
(3)把 bean 例項傳遞 bean 後置處理器的方法 postProcessBeforeInitialization
(4)呼叫 bean 的初始化的方法(需要進行配置初始化的方法)
(5)把 bean 例項傳遞 bean 後置處理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(物件獲取到了)
(7)當容器關閉時候,呼叫 bean 的銷燬的方法(需要進行配置銷燬的方法)

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("第五步 執行銷燬的方法");
    }
}

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之後執行的方法");
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前執行的方法");
        return bean;
    }
}

  <bean id="order" class="com.birdy.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="膝上型電腦"></property>
    </bean>
    <!--配置後置處理器-->
    <bean id="myBeanPost" class="com.birdy.config.MyBeanPost"></bean>
 @Test
    public void testLife(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Orders order = context.getBean("order", Orders.class);
        System.out.println(order);
        context.close();
    }

自動裝配

自動裝配分為兩種方式:

  • 根據屬性名稱自動注入
  • 根據屬性型別自動注入。
<?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標籤屬性 autowire ,配置自動裝配
    autowire屬性常用的兩個值:
    byName 根據屬性名稱自動注入 ,要注入的bean的id值要和類裡面屬性名稱需一樣
    byType:根據屬性型別自動注入

    -->
    <bean id="emp" class="com.Keafmd.spring5.autowire.Emp" autowire="byType">
        <!--手動裝配-->
<!--        <property name="dept" ref="dept"></property>-->
    </bean>

    <bean id="dept" class="com.Keafmd.spring5.autowire.Dept"></bean>

    <!--根據屬性型別注入,型別匹配到了兩個bean,byName會報錯-->
<!--    <bean id="dept1" class="com.Keafmd.spring5.autowire.Dept"></bean>-->
</beans>

引入外部配置檔案

引入外部依賴,以druid連線池為例

  1. 建立外部配置檔案,properties 格式檔案,配置資料庫資訊jdbc.properties
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
  1. 把外部 properties 屬性檔案引入到 spring 配置檔案中 —— 引入 context 名稱空間
<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名稱空間-->
    
        <!--引入外部屬性檔案-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置連線池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>
    
</beans>

bean管理之基於註解方式

使用註解目的:簡化 xml 配置

所需依賴spring-aop

開啟包掃描

<!--開啟元件掃描
     1 如果掃描多個包,多個包使用逗號隔開
     2 掃描包上層目錄
    -->
    <context:component-scan base-package="com.birdy"></context:component-scan>

自定義包掃描規則

<!--示例 1
 use-default-filters="false" 表示現在不使用預設 
 filter,自己配置 filter
 context:include-filter ,設定掃描哪些內容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
 <context:include-filter type="annotation"

expression="org.springframework.stereotype.Controller"/><!--代表只掃描Controller註解的類-->
</context:component-scan>

<!--示例 2
 context:exclude-filter: 設定哪些內容不進行掃描
-->
<context:component-scan base-package="com.atguigu">
 <context:exclude-filter type="annotation"

expression="org.springframework.stereotype.Controller"/><!--表示Controller註解的類之外一切都進行掃描-->
</context:component-scan>

基於註解方式實現元件注入

  • @Autowired: 根據屬性型別進行自動裝配
  • @Qualifier: 區分注入的元件名稱。因為根據型別注入時可能會有多個元件,需要再用名稱加以區分,跟@Autowired一起使用
  • value: 注入普通型別屬性
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("dao update...........");
    }
}
@Service(value = "userService")
public class UserService {
	//xml方式
    //建立UserDao型別屬性,生成set方法
//    private UserDao userDao;
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }

    @Qualifier(value = "userDao")
    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("service add...............");
        userDao.update();
    }
}

完全註解開發

建立配置類,替代 xml 配置檔案

@Configuration	//表示該類是配置類
@ComponentScan(basePackages = {"com.birdy"}) //配置掃描規則	
public class MyConf {
}

測試方法

@Test
public void testAnno(){
    //指定配置檔案
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConf.class);
    //獲取元件
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

注入屬性

public class User {

    @Value(value = "旅鳥")
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

@Test
public void testValue(){
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConf.class);
    User user = context.getBean("user", User.class);
    System.out.println(user.toString());
}