天天看点

spring控制反转和依赖注入

文章目录

    • 一、spring
        • 1.1 SpringIO的概念
        • 1.2 Spring IOC理论推导
        • 1.3 spring框架的入门
        • 1.3 spring配置解释
          • 1.3.1 ApplicationContext的三个常用类对象
          • 1.3.2 ApplicationContext和BeanFactory的区别
        • 1.4 IOC 中 bean 标签
          • 1.4.1 bean 标签
          • 1.4.2 创建bean的三种方式
          • 1.4.3 bean的作用范围和生命周期
    • 二、Spring的依赖注入
        • 2.1 构造函数注入
        • 2.2 set方法注入
        • 2.3 使用p标签进行注入
        • 2.4 注入复杂类型(集合)
        • 2.5 构造和set注入的区别

一、spring

1.1 SpringIO的概念

名词解释:

IOC(Inversion of control):控制反转,是一种设计思想。DI(dependence injection):依赖注入是实现IOC的一种方法。

我们之前的编码中,对象的创建与对象间的依赖关系完全硬编码出现在程序总,对象也是由我们自己来创建和控制的。

而控制反转的意思就是将创建对象的权利交给“第三方”,也就是我们可以交给spring框架,spring框架就获得了创建对象的去哪里。对象创建创建的权利发生了改变。

上述的两个名词其实就是一个意思,只是出于不同的角度提出的。

1.2 Spring IOC理论推导

我们在引入IOC思想之前,进行开发使用的模式是MVC,以用户注册为例,一般分为:

  • UserDao 数据访问层接口
  • UserDaoImpl 数据访问层实现类
  • UserService 业务层接口
  • UserService 业务层接口的实现类
  • UserServlet Web层

数据访问层的代码

//数据库访问层代码
public class UserDao{
	public User queryByLoginName(String name){
	}
	public void save(User  user){
	}	
}
           

业务逻辑层代码

//业务逻辑层代码
public class UserService{
	//调用dao,创建dao的对象
	private UserDao userDao=new UserDao();	
	public void userRegister(User user){		
	}
}
           

业务层的代码维护了数据访问层的代码,这样会存在一些问题,就是当我们的需求发生改变时,就需要改变大量的原有代码。

当引入IOC思想之后,我们不在自己new对象了。

业务逻辑层代码:

public class UserService{
	//调用dao,创建dao的对象
	private UserDao userDao;
	public void setUserDao(UserDao userDao){
		this.userDao=userDao;
	}	
	public void userRegister(User user){
		//userDao.任何方法或者属性均会空指针	
	}
}
           

上面的程序中就不需要new对象了。引入set方法,就可以将创建对象的权利转移,之前的是可以主动创建对象,现在呢,程序就变成了被动的传入一个对象。从一定程度上降低了代码的耦合度。有利于业务的实现。

1.3 spring框架的入门

(1)创建一个MAVEN项目,在创建完成之后,点开pom.xml文件,添加spring的依赖。

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
</dependency>
           

(2)在MAVEN项目中创建如下的目录结构,main文件夹下面的resources创建spring的配置文件(这里我命名为:applicationContext.xml)

spring控制反转和依赖注入

(3)让spring管理资源,在配置文件中配置service和dao

<bean id="userService" class="com.gx.Service.UserService">
    </bean>
    <bean id="userDao1" class="com.gx.dao.daoimpl.UserDao1">
    </bean>
           

(4)测试Spring的配置是否成功

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        UserDao1 userDao1 = context.getBean("userDao1", UserDao1.class);
        System.out.println(userDao1);
        System.out.println(userService);
    }
           

1.3 spring配置解释

1.3.1 ApplicationContext的三个常用类对象
  • ClassPathXmlApplicationContext:加载类路径下的配置文件,要求配置文件必须在类路径下
  • FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件(前提要有访问权限)
  • AnnotationConfigApplicationContext:用于读取注解创建Spring容器
1.3.2 ApplicationContext和BeanFactory的区别
BeanFactory 才是 Spring 容器中的顶层接口。
ApplicationContext 是它的子接口。
BeanFactory 和 ApplicationContext 的区别:
创建对象的时间点不一样。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么使用什么时候创建对象。
           

1.4 IOC 中 bean 标签

1.4.1 bean 标签

作用:将创建对象的权力交给Spring容器,默认情况下调用的是无参构造函数,没有无参构造不能创建。

其中的属性:

  • id:对象在spring容器中的唯一标识。用于获取对象
  • class:指定类的全路径类名。使用反射创建对象
  • scope:指定对象的作用范围
  • init-method:类的初始化方法
  • destory-method:类的销毁方法
1.4.2 创建bean的三种方式

一、使用默认的构造方法

<!--在默认情况下:它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。-->
<bean id="userService" class="com.gx.Service.UserService"> </bean>
           

二、spring管理静态工厂,使用静态工厂的方法创建对象,并存入spring容器

获取Runtime的实例必须通过Runtime的静态getRuntime()方法才能够获取。通过spring的配置获取如下

三、spring管理实例工厂(使用某个类中的方法创建对象存入spring容器)

Java代码中,获取FactoryUserService对象的是在ServiceFactoryDemo中的getFactoryUserService方法中获取。

public class ServiceFactoryDemo {
    //创建UserService对象
    public FactoryUserService getFactoryUserService(){
        //System.out.println("创建工厂方法");
        return new FactoryUserService();
    }
}
           

在spring配置中如下:

<bean id="serviceFactoryDemo " class="com.gx.Service.ServiceFactoryDemo" />
<bean id="factoryUserService" factory-method="getFactoryUserService" factory-bean="serviceFactoryDemo ">
           
1.4.3 bean的作用范围和生命周期

范围属性:scope

作用:用于指定bean的作用范围,spring容器会根据我们设置的scope属性来选择创建对象的时机。

取值:

  • singleton :默认值,单例的
  • prototype :多例的.
  • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
  • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
  • WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.

    生命周期:

    (1)单例对象(scope=“singleton”)一个应用只有一个对象

    对象出生:当应用加载,创建容器时,对象就被创建了。

    对象活着:只要容器在,对象一直活着。

    对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

    (2)多例对象(scope=“prototype”)每次访问对象时,都会重新创建对象的实例 

    对象出生:当使用对象时,创建新的对象实例。

    对象活着:只要对象在使用中,就一直活着。

    对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

单例模式:
在代码获取容器的时候,当使用ApplicationContext来接受容器时,不能使用手动关闭close方法,这个方法在destory-method时执行
使用ClassPathXmlApplicationContext来接受时,我们可以调用close方法。
多例模式:
对象的销毁不由我们控制,当对象长时间不使用并且不被其他对象所调用的时候,会被Java垃圾回收器回收

           

二、Spring的依赖注入

  依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。我们的程序在编写时,通过控制反转,把对象的创建

交给了 spring,但是代码中不可能出现没有依赖的情况。

  IOC只是降低他们的依赖关系,但不会消除。

注入的类型

  1. 基本数据类型和String
  2. 其他的bean类型(配置文件或注解中配置过的bean)
  3. 复杂类型/集合

2.1 构造函数注入

通过配置文件的方式,让spring框架为我们注入。使用的标签constructor-arg

标签中的属性:

  • type:指定参数在构造函数中的数据类型
  • name:指定参数在构造函数中的名称
  • index:指定参数在构造函数参数列表的索引位置
  • value:它能赋的值是基本数据类型和 String 类型
  • ref:它能赋的值是其他 bean 类型(配置文件中配置过的 bean)

Java代码:UserService 中进行UserDao 的注入

public class UserService {
    private UserDao userDao1;
    //使用构造方法对对象进行注入
    public  UserService(UserDao userDao){
        this.userDao1=userDao;
    }
}
           
<!--使用构造方法对对象进行注入-->
    <bean id="userService" class="com.gx.Service.UserService">
        <!--构造方法注入的ref就是引用javabean对象在spring容器中的id值-->
        <constructor-arg ref="userDao2"></constructor-arg>
    </bean>
    <bean id="userDao1" class="com.gx.dao.daoimpl.UserDao1">
    </bean>
    <bean id="userDao2" class="com.gx.dao.daoimpl.UserDao2">
    </bean>
           

2.2 set方法注入

使用的标签:property

属性:

  • name:找的是类中 set 方法后面的部分
  • value:给属性赋值是基本数据类型和 string 类型的
  • ref:给属性赋值是其他 bean 类型的

Java代码:ConstructorDemo 中存在3个属性。

public class ConstructorDemo {
    private String name;
    private Integer age;
    private Date birthday;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public void show(){
        System.out.println("show方法调用"+name+","+age+","+birthday);
    }
}
           

配置文件中进行配置:

<bean id="constructorDemo" class="com.gx.Service.ConstructorDemo">
        <property name="name" value="詹姆斯"/>
        <property name="age" value="32"/>
        <property name="birthday" ref="now"/>
     </bean>
 <!--将date对象的创建交给spring容器 ,配置日期对象-->
<bean id="now" class="java.util.Date"></bean>
           

2.3 使用p标签进行注入

Java代码与上面的set注入的相同,只是在配置中会有不一样的地方

<bean id="accountService"
class="com.itheima.service.impl.AccountServiceImpl4"
p:name="test" p:age="21" p:birthday-ref="now"/>
</beans>
<!--将date对象的创建交给spring容器 ,配置日期对象-->
<bean id="now" class="java.util.Date"></bean>
           

2.4 注入复杂类型(集合)

创建一个Java类,其中属性分别为数组以及常见的集合类型。

在这里插入代码片

public class fuzaInjection {
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }
    public void show(){
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
}
           

进行配置:

<!--复杂类型的注入-->
    <bean id="fuzaInjection" class="com.gx.Service.fuzaInjection">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <property name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <property name="myList">
            <list>
                <value>aaa</value>
                <value>BBB</value>
                <value>cccc</value>
            </list>
        </property>
        <property name="myMap">
            <map>
                <entry key="map1" value="Amap"></entry>
                <entry key="map2">
                    <value>Bmap</value>
                </entry>
            </map>
        </property>
        <property name="myProps">
            <props>
                <prop key="prop1">prop1</prop>
                <prop key="prop2">prop2</prop>
                <prop key="prop4">prop3</prop>
            </props>
        </property>
    </bean>
    <bean id="fuzaInjectionConstructor" class="com.gx.Service.fuzaInjectionConstructor">
        <constructor-arg name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </constructor-arg>
        <constructor-arg name="myMap">
            <map>
                <entry key="map1">
                    <value>111</value>
                </entry>
                <entry key="map2" value="222"></entry>
            </map>
        </constructor-arg>
        <constructor-arg name="myProps">
            <props>
                <prop key="prop1">pro1</prop>
                <prop key="prop2">pro2</prop>
                <prop key="prop3">pro3</prop>
            </props>
        </constructor-arg>
    </bean>
           

复杂类型注入的总结:

用于给List结构集合注入的标签:list、array、set

用于给map结构集合注入的标签:map、props

2.5 构造和set注入的区别

构造函数注入:
	优点:注入数据时,必须注入数据,否则无法创建对象。
	缺点:改变了bean对象的实例化方法,造成对象创建时,不使用构造函数中的某一个数据的时候,也必须创建该数据。
set方法注入:
	优点:创建对象的时候没有限制,可以直接使用默认的构造方法创建bean对象。
	缺点:如果某个成员变量必须有值的话,则获取对象时有可能set方法不执行。