Spring之IOC

目录

1、IOC容器

2、IOC接口

3、IOC 操作 Bean 管理

4、基于 XML 方式

5、Bean 作用域和生命周期

6、注解方式



一、IOC 容器

IOC 的概念原理

IOC 是什么?

  • 控制反转,把对象的创建和对象之间的调用过程,都交给 Spring 进行管理

  • 使用 IOC 的目的:降低耦合性

IOC 底层实现

  • xml 解析

  • 工厂模式

  • 反射技术



二、IOC 接口

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

2)Spring 提供了IOC 容器实现的两种方式(即两个接口)

  • BeanFactory

    • IOC 容器基本实现

    • 加载配置文件时不会创建对象,使用对象时才会创建对象

  • ApplicationContext

    • BeanFactory的子接口,提供更多功能,提供给开发人员使用

    • 加载配置文件就创建对象

BeanFactory

BeanFactory接口的子接口和实现类

  • ConfigurableApplicationContext:包含一些相关的扩展功能

ApplicationContext

ApplicationContext接口的实现类

  • FileSystemXmlApplicationContext:xml 配置文件在系统盘中的文件全路径名

    new FileSystemXmlApplicationContext("D:\workspace\NOTE_Spring\src\bean1.xml");
  • ClassPathXmlApplicationContext:xml 配置文件在项目 src 下的相对路径名

    new ClassPathXmlApplicationContext("bean1.xml");


三、IOC 操作 Bean 管理

Bean 管理是什么

Bean 管理指的是两个操作

  • Spring 创建对象

  • Spring 注入属性

public class User{
    private String userName;
​
    public void setUserName(String userName){
        this.userName = userName;
    }
}

Bean 管理实现方式

  • 基于 XML 配置文件方式实现

  • 基于注解方式实现



四、基于 XML 方式

创建对象

<!--配置User对象-->
<bean id="user" class="com.zking.spring.User"></bean>

1)在 Spring 配置文件中,使用bean标签,标签里添加对应属性,就可以实现对象的创建

2)bean标签中有很多属性,介绍常用的属性

  • id属性:唯一标识

  • class属性:类全限定名、类全路径

  • name属性:了解即可,早期为Struts框架服务,现已“退役”,作用跟id属性一样

  • 其他属性:后面再做介绍...

3)创建对象时,默认执行无参构造方法

如果只提供一个有参构造方法,如下

public class User {
    private String userName;
​
    public User(String userName) {
        this.userName = userName;
    }
​
    // ...
}

仍然按照之前获取 User 对象创建方式,即

// 1、加载自定义的Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 2、获取配置的User对象
User user = context.getBean("user", User.class);

则会报错,其中主要报错信息

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in class path resource [bean1.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zking.spring.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.zking.spring.User.<init>()

就是说:初始化 User 对象失败,因为没有找到默认构造,所以抛出了一个NoSuchMethodException异常,即 User 中没有<init>()方法

注入属性

DI:依赖注入,就是注入属性(但需要在创建对象基础上进行)

IOCDI的区别:DIIOC的一种具体实现

两种注入方式:Setter 方式、有参构造方法

  • 第一种注入方式:通过 Setter 方式进行注入

    public class Book{
        private String bname;
        
        // Setter 方法注入
        public void setBname(String bname){
            this.bname = bname;
        }
        
        public static void main(String[] args){
            Book book = new Book();
            book.setBname("book1");
        }
    }
  • 第二种注入方式:通过有参构造方法进行注入

    public class Book{
        private String bname;
        
        // 有参构造注入
        public Book(String bname){
            this.bname = bname;
        }
        
        public static void main(String[] args){
            Book book = new Book("book1");
        }
    }
通过 Setter 方式注入

① 创建类,定义属性和 Setter 方法

public class Book {
    private String bname;
    private String bauthor;
​
    public void setBname(String bname) {
        this.bname = bname;
    }
    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }
}

② 在 Spring 配置文件中配置对象创建,配置属性注入

<!-- 2、Setter方法注入属性 -->
<bean id="book" class="com.zking.spring.Book">
    <!-- 使用property完成属性注入
            name: 类中属性名称
            value: 向属性中注入的值
    -->
    <property name="bname" value="Spring实战 第5版"></property>
    <property name="bauthor" value="克雷格·沃斯(Craig Walls)"></property>
</bean>
通过有参构造注入

① 创建类,定义属性,创建属性对应有参构造方法

public class Orders {
    private String oname;
    private String address;
​
    public Orders(String oname, String address) {
        this.oname = oname;
        this.address = address;
    }
}

② 在 Spring 配置文件中配置对象创建,配置有参构造注入

<!-- 3、有参构造注入属性 -->
<bean id="orders" class="com.zking.spring.Orders">
    <constructor-arg name="oname" value="Spring微服务实战"></constructor-arg>
    <constructor-arg name="address" value="[美]约翰·卡内尔(John Carnell)"></constructor-arg>
</bean>

或者使用index属性代替name属性,索引值大小是几就表示有参构造中的第几个参数(索引从0开始)

<!-- 3、有参构造注入属性 -->
<bean id="orders" class="com.zking.spring.Orders">
    <constructor-arg index="0" value="冰墩墩"></constructor-arg>
    <constructor-arg index="1" value="Peking"></constructor-arg>
</bean>

注入其他类型属性

字面量
  • null值:使用<null>

<bean id="book2" class="com.zking.spring.Book">
    <property name="bname" value="Spring实战 第5版"></property>
    <property name="bauthor">
        <null></null>
    </property>
</bean>
  • 属性值包含特殊符号:有两种方式

    • 使用转义字符,如&lt;&gt;标识<>

<!-- 字面量:property方式注入含有特殊符号的属性 -->
<bean id="book3" class="com.zking.spring.Book">
    <property name="bname" value="Spring实战 第5版"></property>
    <property name="bauthor" value="&lt;Test&gt;"</property>
</bean>
  • 使用CDATA结构,如<![CDATA[<Test>]]>

<!-- 字面量:property方式注入含有特殊符号的属性 -->
<bean id="book4" class="com.zking.spring.Book">
    <property name="bname" value="Spring实战 第5版"></property>
    <property name="bauthor">
        <value><![CDATA[<Test>]]></value>
    </property>
</bean>
外部 Bean

① 创建两个类:Service类和Dao

public interface UserDao {
    void update();
}
public class UserDaoImpl implements UserDao{
    @Override
    public void update() {
        System.out.println("dao update...");
    }
}

② 在Service中调用Dao中的方法

public class UserService {
    private UserDao userDao;
​
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
​
    public void updateUser(){
        System.out.println("service update...");
        userDao.update();
    }
}

③ 在 Spring 配置文件中进行配置

<!--  1、配置service和dao创建  -->
<bean id="userService" class="com.zking.spring.service.UserService">
    <!-- 2、注入userDao对象
               name属性:类里面属性名称
               ref属性:创建userDao对象bean标签id值
       -->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.zking.spring.dao.UserDaoImpl"></bean>
内部 Bean

① 一对多关系:部门和员工。部门里有多个员工,一个员工属于一个部门。部门是一的一方,员工是多的一方

② 在实体类之间表示一对多关系。在员工类中使用对象类型表示所属部门

public class Dept {
    private String dname;
    
    public String getDname() {
        return 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 String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}

③ 在 Spring 配置文件中进行配置

<bean id="emp1" class="com.zking.spring.bean.Emp">
    <property name="ename" value="Rose"></property>
    <property name="gender" value="female"></property>
    <property name="dept">
        <bean id="dept1" class="com.zking.spring.bean.Dept">
            <property name="dname" value="Develop Department"></property>
        </bean>
    </property>
</bean>

当希望一个bean只被某一个类使用时,我们可以使用内部bean。内部bean此时作为某一个类的属性,只能通过该类实例化。

注入集合属性

  • 注入数组类型属性

  • 注入 List 集合类型属性

  • 注入 Map 集合类型属性

自动装配

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

XML 实现自动装配:bean标签中有个属性autowire,里面有两个常用的属性值

  • byName根据属性名称注入,要求注入值beanid值和类中属性名称一致

  • byType根据属性类型注入,要求注入值bean的类型在配置文件中只存在一份

1)根据属性名称进行自动注入

<bean id="emp" class="com.zking.spring.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.zking.spring.autowire.Dept"></bean>

2)根据属性类型进行自动注入

<bean id="emp" class="com.zking.spring.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.zking.spring.autowire.Dept"></bean>

外部属性文件

1、直接配置数据库信息

  • 引入Druid连接池依赖jar

  • 配置Druid连接池

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

2、引入外部属性文件配置数据库连接池

  • 1)创建properties格式的外部属性文件,配置数据库连接信息

mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306
mysql.username=root
mysql.password=root
  • 2)将外部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:property-placeholder location="classpath:jdbc.properties"/>
    <!--使用Spring表达式配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"></property>
        <property name="url" value="${mysql.url}"></property>
        <property name="username" value="${mysql.username}"></property>
        <property name="password" value="${mysql.password}"></property>
    </bean>
</beans>

FactoryBean

Spring 有两种类型 Bean,一种是普通 Bean,另外一种是工厂 Bean(FactoryBean)

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

  • 工厂 Bean:在配置文件中定义的 Bean 类型可以和返回类型不一致

上述的例子都是普通 Bean 的类型,那么工厂 Bean 该怎么实现呢?

  • 1)创建类,实现 FactoryBean 接口,使其作为一个工厂 Bean

  • 2)实现接口中的方法,在实现方法中定义返回的 Bean 类型

  • 3)在 Spring 配置文件中进行配置



五、Bean 作用域和生命周期

Bean 作用域

在 Spring 里面,可以设置创建 Bean 的实例是单实例还是多实例,默认情况下是单实例

<bean id="book" class="com.zking.spring.collectiontype.Book"></bean>

如何设置单实例多实例?

在 Spring 配置文件中 bean 标签里scope属性用于设置单实例还是多实例

  • 1)singleton,单实例,默认情况下不写也是它

  • 2)prototype,多实例

singletonprototype的区别

singletonprototype除了单实例和多实例的差别之外,还有以下区别

  • 设置scope值是singleton时,加载 Spring 配置文件时就会创建单实例对象

  • 设置scope值是prototype时,加载 Spring 配置文件时不会创建对象,而是在调用getBean方法时创建多实例对象

scope的其他值

scope的属性值除了singletonprototype之外,其实还有一些属性值,如

  • request,每个request创建一个新的 bean

  • session,同一session中的 bean 是一样的

Bean 生命周期

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

Bean 生命周期

  1. 通过构造器创建 Bean 实例(无参构造)

  2. 为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)

  3. 调用 Bean 的初始化方法(需要进行配置初始化方法)

  4. Bean 就可以使用了(对象获取到了)

  5. 当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)

Spring 配置文件中的配置

<bean id="orders" class="com.zking.spring.bean.Orders" init-method="initMethod"
      destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>

Bean 后置处理器

加上 Bean 后置处理器,Bean 生命周期如下

  1. 通过构造器创建 Bean 实例(无参构造)

  2. 为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)

  3. 把 Bean 的实例传递给 Bean 后置处理器的postProcessBeforeInitialization方法

  4. 调用 Bean 的初始化方法(需要进行配置初始化方法)

  5. 把 Bean 的实例传递给 Bean 后置处理器的postProcessAfterInitialization方法

  6. Bean 就可以使用了(对象获取到了)

  7. 当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)



六、注解方式

什么是注解

  • 注解是一种代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)

  • 注解作用:在类上面,方法上面,属性上面

  • 注解目的:简化 XML 配置

创建对象

  • @Component

  • @Service

  • @Controller

  • @Repository

上面四个注解功能是一样的,都可以用来创建 Bean 实例

  • 1)引入依赖

spring-aop.xxx.jar
  • 2)开启组件扫描

<?xml version="1.0" encoding="UTF-8"?>
<!--引入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:component-scan base-package="com.zking.spring.dao,com.zking.spring.service"></context:component-scan>
​
</beans>
  • 3)创建类,在类上添加创建对象注解

/**
 * value可省略,默认值为类名首字母小写
 */
@Component(value = "userService")
public class UserService {
    public void add(){
        System.out.println("UserService add...");
    }
}

组件扫描配置

设置扫描
  • use-default-filters表示现在不使用默认filter,自己配置filter

  • include-filter设置扫描哪些内容

<context:component-scan
            base-package="com.zking.spring" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
设置不扫描
  • 配置扫描包下所有内容

  • exclude-filter设置不扫描哪些内容

<context:component-scan
            base-package="com.zking.spring">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

属性注入

  • @Autowired根据属性类型进行自动装配

  • @Qualifier根据属性名称进行注入,需要和@Autowired一起使用

  • @Resource可以根据类型和名称注入

  • @Value根据普通类型注入

完全注解开发

  • 创建配置类,替代 XML 配置文件

//标注当前类为配置类
@Configuration
//开启组件扫描
@ComponentScan({"com.zking.spring"})
//引入外部属性文件
@PropertySource({"classpath:config.properties"})
public class SpringConfig {
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值