Spring5 IOC(二)

250 阅读9分钟

1. 什么是IOC

  • IOC是控制反转,可以把对象创建和对象之间的调用交给Spring进行管理
  • 使用IOC的目的:降低耦合度

2. IOC的底层原理

xml解析、工厂模式、反射

画图讲解IOC的原理

3. IOC接口

3.1 IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

3.2 Spring提供IOC容器的两种方式

BeanFactory:IOC容器的基本实现,是Spring内部使用的容器,一般不提供开发人员

加载配置文件的时候不会创建对象,在获取对象(使用时才创建)

ApplicationContext:是BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员

加载配置文件的时候就会把配置文件对象进行创建

ClassPathApplication是ApplicationContext的主要实现类

4. IOC操作Bean管理

  • Bean管理值两个操作
  • Spring创建对象
  • Spring注入属性

5. 基于XML配置文件方式实现Bean管理

5.1 创建对象

  • 在spring配置文件中配置bean标签,在标签中加入属性
  • 常用的属性: (1)id-唯一标识 (2)class-全限定名
  • 创建对象时,默认执行无参构造器

5.2基于xml方式的注入属性

DI:依赖注入 注入属性

  • set方法注入 ---类中创建属性的set方法
  • 有参构造器注入 ---类中创建带属性的构造方法

  • p命名空间注入(了解) 命名空间添加如下图

5.3 xml设置其他类型属性

1.字面量--基础类型+String类+null

(1)null <property name="name" > <null/> </property>

(2)包含特殊符号

解决方法

a.使用转义(用的少)

b.CDATA

<property name="name" >
            <value>
                <![CDATA[<<什么鬼>>]]>
            </value>
        </property>

2.外部Bean 创建一个Book类,在user类中创建一个Book的属性

3.内部Bean和级联赋值

一个用户可以有多本书,将user类的中book删除,在Book类中添加User属性

可以通过级联赋值将成员的属性修改

5.4 集合类型属性注入

1.集合属性注入入门

先定义一个类

import lombok.Data;
import lombok.ToString;

import java.util.List;
import java.util.Map;
import java.util.Set;

@Data
@ToString
public class Stu {
    private String[] courses;
    private List<String> list;
    private Map<String,String> map;
    private Set<String> set;
}

配置文件

 <bean id="stu" class="com.bin.spring5.collection.Stu">
        <property name="courses">
            <array>
                <value>数学</value>
                <value>语文</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>小斌</value>
                <value>大斌</value>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="java" value="java"></entry>
                <entry key="php" value="最好的语言"></entry>
            </map>
        </property>
        <property name="set">
            <set>
                <value>吃饭</value>
                <value>喝酒</value>
            </set>
        </property>
    </bean>

2.集合中存入对象

创建一个course类

package com.bin.spring5.collection;

import lombok.Data;

@Data
public class Course {
    private String name;
}

在stu类中加入新的属性

private List<Course> courseList;

配置文件中添加

<bean id="course1" class="com.bin.spring5.collection.Course">
        <property name="name" value="python"></property>
    </bean>
    <bean id="course2" class="com.bin.spring5.collection.Course">
        <property name="name" value="redis"></property>
    </bean>

在stu的bean中添加

  <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>

3.把集合注入部分提取出来

现在XML文件中引入一个util的命名空间

  <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       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">

配置文件如此设置

<bean id="stu2" class="com.bin.spring5.collection.Stu">
        <property name="list" ref="list"></property>
    </bean>
    <util:list id="list">
        <value>数学</value>
        <value>语文</value>
    </util:list>

5.5 IOC中的FactoryBean

Spring中有两种类型的bean,一种普通bean,一种工厂bean(FactoryBean)

  • 普通bean:在配置文件中定义bean的类型就是返回类型

  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样

第一步:创建一个类,让泪作为工厂bean,实现接口FactoryBean。

第二步: 实现接口里的方法,实现方法中定义返回bean的类型。

写一个类实现接口,注意接口的泛型就是返回Bean的类型


import com.bin.spring5.Book;
import org.springframework.beans.factory.FactoryBean;

public class BookFac implements FactoryBean<Book> {
    //定义返回的bean
    @Override
    public Book getObject() throws Exception {
        Book book = new Book();
        book.setBookName("java从入门到如土");
        return book ;
    }

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

配置文件中加入

       <bean id="bookFac" class="com.bin.spring5.factoryBean.BookFac"></bean>

测试方法

public void factoryBean(){
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");
        Book book = (Book) applicationContext.getBean("bookFac");
        System.out.println(book);
    }

5.6 IOC中bean的作用域

在spring中可以设置设置bean时单例还是多例的,默认是单例的singleton

如果需要是多例的,在bean标签里面设置一个属性scope="prototype"

singleton和prototype的区别

  • singleton单实例,prototype多实例
  • scope是singleton时,在加载Spring文件时创建对象;scope是prototype时,在调用getBean方法时创建多实例对象。

另外还有request和session 和global-session三种

5.7 IOC的bean的生命周期

生命周期:对象从创建到对象销毁的过程

5.7.1 bean的生命周期-基础版

  • 通过构造器出创建bean实例(无参构造器)
  • 为bean的属性设置值和其他bean引用(set方法)
  • 调用bean的初始化方法(需要进行配置)
  • bean可以使用(对象获取到了)
  • 当容器关闭时候,掉哦越难过bean的销毁方法(需要进行配置销毁方法)

创建一个类来体现生命周期

public class Orders {
    public Orders() {
        System.out.println("调用无参构造器");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("调用set方法设置值");
        this.name = name;
    }

    //创建一个执行的初始化方法
    public void init(){
        System.out.println("初始化方法");
    }

    //创建一个销毁的方法
    public void  destroy(){
        System.out.println("销毁的方法");
    }

    private String name;
}

配置文件

<bean id="orders" class="com.bin.spring5.bean.Orders" init-method="init" destroy-method="destroy">
        <property name="name" value="huaweiphone"></property>
    </bean>

测试方法

public void beanLife(){
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");
        Orders orders = (Orders) applicationContext.getBean("orders");
        System.out.println(orders);
        System.out.println("使用了orders");
        ((ClassPathXmlApplicationContext)applicationContext).close();
    }

5.7.2 后置处理器

  • 通过构造器出创建bean实例(无参构造器)
  • 为bean的属性设置值和其他bean引用(set方法)
  • 把bean的实例传递给后置处理的postProcessBeforeInitialization方法
  • 调用bean的初始化方法(需要进行配置)
  • 把bean的实例传递给后置处理的postProcessAfterInitialization方法
  • 当容器关闭时候,掉哦越难过bean的销毁方法(需要进行配置销毁方法)

演示后置处理器 创建一个类去实现后置处理器

//后置处理器
public class PostProcess 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;
    }
}

在配置文件中配置后置处理器,之后所有的对象生成都会执行后置处理器

<!--配置后置处理器-->
    <bean id="postProcess" class="com.bin.spring5.bean.PostProcess" ></bean>

执行测试方法 结果如下

5.8 基于xml的自动装配

根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性进行注入

创建两个类

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Emp {
    private Dept dept;
}

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Emp {
    private Dept dept;
}

配置文件

<!--测试自动装配-->
    <bean id="emp" class="com.bin.spring5.Autowire.Emp" autowire="byType"></bean>
    <bean id="dept" class="com.bin.spring5.Autowire.Dept">
        <property name="name" value="xiaobin"></property>
    </bean>

自动装配在配置bean标签中使用autowird配置自动装配,该属性有两个常用的值

  • byType,根据属性类型注入
  • byName,根据属性名称注入,注入bean的id和类属性名称一样

5.9 外部属性文件

创建一个外部properties文件

prop.driverClass=com.mysql.jdbc.Driver
prop.username=root
prop.password=root
prop.url=jdbc:mysql:///db

引入名称空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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/util http://www.springframework.org/schema/util/spring-util.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

在配置文件中使用标签引入外部文件

<!--驱动名称-原方式-->
    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="url" value="jdbc:mysql:///db"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    </bean>
<!--  引入外部文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="driverClassName" value="${prop.driverClass}"></property>
    </bean>

6. 基于注解的方式实现

6.1 什么是注解

  • 注解是代码的一种特殊标记,格式:@注解名称(属性=属性值,属性=属性值)
  • 注解可以在写在类上,方法上,属性上
  • 使用注解的目的,简化XML配置

6.2 SPring针对Bean管理中创建对象提供的注解

  • @compoment
  • @Controller
  • @Service
  • @Repository 上述4个注解功能一样,在类上创建Bean实例

1.需要先导入一个AOP的依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

2.开启注解扫描 在配置文件中添加

<context:component-scan base-package="com.bin.spring"></context:component-scan>

3.创建类并使用注解 默认的对象名称是类名首字母小写

import org.springframework.stereotype.Component;

@Component
public class MyComponent {
}

4.测试代码

@org.junit.Test
    public void anno(){
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");
        MyComponent myComponent = applicationContext.getBean("myComponent", MyComponent.class);
        System.out.println(myComponent);
    }

6.3 基于注解方式实现属性注入

  • @Autowired:根据属性类型注入
  • @Qualifier:根据属性名称注入
  • @Resource:可以根据类型注入,也可以根据名称注入
  • @Value:注入普通属性类型

6.3.1 @Autowired

@Repository
public class MyDao {
    public void a(){
        System.out.println("dao执行了A方法");
    }
}

@Service
public class MyService {

    @Autowired
    private  MyDao dao;

    public void b(){
        System.out.println("执行了b方法");
        dao.a();
    }
}

测试方法

@org.junit.Test
    public void anno2(){
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");
        MyService myService = applicationContext.getBean("myService", MyService.class);
        System.out.println(myService);
        myService.b();
    }

6.3.2 @Qualifier

@Repository("dao")
public class MyDao {
    public void a(){
        System.out.println("dao执行了A方法");
    }
}

@Service
public class MyService {
    @Qualifier("dao ")
    @Autowired
    private  MyDao dao;

    public void b(){
        System.out.println("执行了b方法");
        dao.a();
    }
}

测试方法同上

6.3.3 @Resource

//    @Resource //根据类型注入
    @Resource(name ="dao") //根据名称注入
    private  MyDao dao;

6.3.4 @Value

    @Value("name")
    private String name;

6.4 纯注解开发

纯注解开发的话 这个也不能要

<context:component-scan base-package="com.bin.spring"></context:component-scan>

所以我们需要使用java配置类

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration//声明是一个配置类
@ComponentScan(basePackages = "com.bin.spring5")//需要扫的包
public class SpringConfig {
}

测试类

    @org.junit.Test
    public void anno3(){
        ApplicationContext applicationContext =new AnnotationConfigApplicationContext(SpringConfig.class);
        MyService myService = applicationContext.getBean("myService", MyService.class);
        System.out.println(myService);
        myService.b();
    }