首页 > 技术文章 > Spring5:控制反转

applesnt 2020-04-01 16:14 原文

二、Spring IOC控制反转

1:IOC推导

》传统业务调用编程

定义一个userDao接口:UserDao

package com.spring;

public interface UserDao {

    public void getUser();
}

定义一个userDao接口实现类(基于mysql):UserMysqlDaoImpl

package com.spring;

public class UserMysqlDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("调用了Mysql数据驱动");
    }
}

定义一个userDao接口实现类(基于oracle):UserOracleDaoImpl

package com.spring;

public class UserOracleDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("调用了oracle数据驱动");
    }
}

定义一个UserService接口:UserService

package com.spring;

public interface UserService {
    public void getUser();
}

定义一个UserService接口实现类:UserServiceImpl

package com.spring;

public class UserServiceImpl implements UserService{

    UserDao userDao = new UserMysqlDaoImpl();//mysql实现
    //UserDao userDao = new UserOracleDaoImpl();//oracle实现

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

编写测试类:

public static void main(String[] args) {
    UserService userService = new UserServiceImpl();
    userService.getUser();
}

解释:
1:如果我们要在UserServiceImpl 调用userDao的业务实现,必须要new 一个对象
2:如果我们要修改UserServiceImpl 调用userDao的业务实现,必须要重新new一个对象
3:以上两点让代码紧耦合,而且对代码的管理增加了难度,要不停的修改代码

》IOC 编码式业务调用编程

基于传统业务调用编程的UserDao 、UserMysqlDaoImpl 、UserOracleDaoImpl、UserService

修改UserService接口实现类:UserServiceImpl

package com.spring;

public class UserServiceImpl implements UserService{
    
    private UserDao userDao;//定义要调用的接口变量

    //给这个变量增加set方法(重点)
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

编写测试类:

public static void main(String[] args) {
   UserService userService = new UserServiceImpl();
   //通过set方法注入不同的业务实现
   ((UserServiceImpl) userService).setUserDao(new UserOracleDaoImpl());
   userService.getUser();
}

》IOC 配置式业务调用编程

基于IOC 编码式业务调用编程的UserDao 、UserMysqlDaoImpl 、UserOracleDaoImpl、UserService、UserServiceImpl

在resources类目录下创建spring配置文件beans.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">

    <!--mysql业务实现-->
    <bean id="userMysqlDao" class="com.spring.UserMysqlDaoImpl"></bean>
    <!--oracle业务实现-->
    <bean id="userOracleDao" class="com.spring.UserOracleDaoImpl"></bean>
	
	<!--ref:引用spring创建好的对象-->
    <bean id="userService" class="com.spring.UserServiceImpl">
        <property name="userDao" ref="userOracleDao"></property>
    </bean>
</beans>

编写测试类:

public static void main(String[] args) {
    //获取spring容器
    ApplicationContext applicationContext = 
            new ClassPathXmlApplicationContext("beans.xml");
    //通过id获取bean
    UserServiceImpl userService = 
            (UserServiceImpl) applicationContext.getBean("userService");

	//调用
    userService.getUser();

    }

2:Spring开始

1:Spring初识

定义一个VO对象:UserVo

package com.spring.Vo;

public class UserVo {

    private String userName;//用户名

    private String userPass;//密码

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPass() {
        return userPass;
    }

    public void setUserPass(String userPass) {
        this.userPass = userPass;
    }
}

通过spring容器实例化UserVo对象:

<!--
	使用spring来创建对象 在spring中称之为bean
	UserVo userVo = new UserVo()
	id = 变量名
	class = new UserVo()
	name = 属性值
	value = 属性赋值
-->
<bean id="userVo" class="com.spring.Vo.UserVo">
    <property name="userName" value="admin"></property>
    <property name="userPass" value="123456"></property>
</bean>

编写测试类:

public static void main(String[] args) {
	  //获取容器
	  ApplicationContext applicationContext =
	          new ClassPathXmlApplicationContext("beans.xml");
	  //获取bean
	  UserVo userVo = (UserVo) applicationContext.getBean("userVo");
	
	  System.out.println(userVo.getUserName()+userVo.getUserPass());
 }

2:构造器

spring实例化bean是基于无参构造方法的,如果加入了有参构造方法,那么set方法就不起作用了,而且bean也会报错(&除非再加一个无参构造器,这样set和构造器实例化bean都可以使用&)!

在UserVo中加入有参构造方法:

public UserVo(String userName, String userPass) {
        this.userName = userName;
        this.userPass = userPass;
    }

sping实例化bean:

<bean id="userVo" class="com.spring.Vo.UserVo">
    <constructor-arg index="0" value="admin"></constructor-arg>
    <constructor-arg index="1" value="123"></constructor-arg>
</bean>

或者

<bean id="userVo" class="com.spring.Vo.UserVo">
    <constructor-arg name="userName" value="admin"></constructor-arg>
    <constructor-arg name="userPass" value="123456"></constructor-arg>
</bean>

3:spring简单配置

1:alias:别名

<!--
 给userVo 起一个别名为myUser
 既可以通过userVo获取到这个bean
 也可以通过myUser获取到这个bean
 -->
 <alias name="userVo" alias="myUser"></alias>

2:bean:实例化对象

<!--
id:bean的唯一标识,相当于类中的变量名
class:bean所对应的的全限定名 包名 + 类名
name:别名 可以定义多个,也可以得到bean对象
-->
<bean id="userVo" class="com.spring.Vo.UserVo" name="u1,u2">
    <property name="userName" value="admin"></property>
    <property name="userPass" value="12345"></property>
</bean>

3:import:合并配置文件

<!--将多个Spring配置文件合并为一个,用于多人开发-->
<import resource="beans.xml"></import>

4:依赖注入 DI

依赖:就是spring创建bean; 注入:就是给bean的属性赋值

一、构造器注入(上面已经讲过.......略)

二、Set注入(重点)
可以通过Set接口注入普通类型、对象类型、数组类型、集合类型、map类型、set类型、null类型、Properties类型。

1:定义一个Address Javabean对象(添加get和set以及tostring方法)

package com.spring.Vo;

public class Address {
    private String address;
}

2:定义一个StudentJavabean对象(添加get和set以及tostring方法)

package com.spring.Vo;

import java.util.*;

public class Student {

    private String name; //普通类型
    private Address address; //对象类型
    private String[] books; //数组类型
    private List<String> hobbys; //集合类型
    private Map<String,String> card; //map类型
    private Set<String> games;//set类型
    private String wife; //null类型
    private Properties info; //Properties类型
}

3:创建spring配置文件

<?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">

    <!--实例化Address类-->
    <bean id="address" class="com.spring.Vo.Address">
        <property name="address" value="北京"></property>
    </bean>

    <!--实例化Student类-->
    <bean id="student" class="com.spring.Vo.Student">
        <!--第一 普通注入:value-->
        <property name="name" value="张三"></property>
        
        <!--第二 bean注入:ref-->
        <property name="address" ref="address"></property>
        
        <!--第三 数组注入:array-->
        <property name="books">
            <array>
                <value>语文</value>
                <value>数学</value>
                <value>英语</value>
            </array>
        </property>
        
        <!--第四 list集合注入:list-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>看书</value>
                <value>跑步</value>

            </list>
        </property>
        
        <!--第五 map集合注入:map-->
        <property name="card">
            <map>
                <entry key="k1" value="v1"></entry>
                <entry key="k2" value="v2"></entry>
            </map>
        </property>
        
        <!--第六 set集合注入:set-->
        <property name="games">
            <set>
                <value>CS</value>
                <value>LoL</value>
            </set>
        </property>
        
        <!--第七 null集合注入:null-->
        <property name="wife">
            <null />
        </property>
        
        <!--第八 Properties集合注入:props-->
        <property name="info">
            <props>
                <prop key="number" >10001</prop>
                <prop key="age">18</prop>
            </props>
        </property>

    </bean>
</beans>

4:编写测试

//获取容器
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
Student student = (Student) applicationContext.getBean("student");

System.out.println(student.toString());

5:查看打印结果

      Student
      {
	      name='张三',
	      address=Address{address='北京'},
	      books=[语文, 数学, 英语],
	      hobbys=[听歌, 看书, 跑步],
	      card={k1=v1, k2=v2},
	      games=[CS, LoL],
	      wife='null',
	      info={age=18, number=10001}
      }

三、命名空间注入
p命名空间:针对set注入方式
c命名空间:针对构造器注入方式

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

1:定义一个UserInfo Javabean对象

package com.spring.Vo;

public class UserInfo {

    private String name;
    private String age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(String age) {
        this.age = age;
    }

    /*无参构造器*/
    public UserInfo() {
    }

    /*有参构造器*/
    public UserInfo(String name, String age) {
        this.name = name;
        this.age = age;
    }

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

2:创建spring配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间:必须有无参构造方法-->
    <bean id="userInfo" class="com.spring.Vo.UserInfo" p:name="admin" p:age="18"></bean>

    <!--c命名空间:必须有有参构造方法-->
    <bean id="userInfo2" class="com.spring.Vo.UserInfo" c:name="zhangsan" c:age="15"></bean>

</beans>

4:编写测试

@Test
public void test01(){
    //获取容器
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取bean(试试userInfo)
    UserInfo userInfo =applicationContext.getBean("userInfo2", UserInfo.class);

    System.out.println(userInfo.toString());
}

四、bean的作用域
1:单例模式(singleton):每次从容器中拿到的对象都是同一个,spring默认值

<bean id="userInfo" class="com.spring.Vo.UserInfo" scope="singleton"></bean>
//获取容器
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
UserInfo userInfo01 =applicationContext.getBean("userInfo", UserInfo.class);
UserInfo userInfo02 =applicationContext.getBean("userInfo", UserInfo.class);

System.out.println(userInfo01 == userInfo02);

/*结果:true */

2:原型模式(prototype):每次从容器中拿到的对象都是新的

<bean id="userInfo" class="com.spring.Vo.UserInfo" scope="prototype"></bean>
//获取容器
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
UserInfo userInfo01 =applicationContext.getBean("userInfo", UserInfo.class);
UserInfo userInfo02 =applicationContext.getBean("userInfo", UserInfo.class);

System.out.println(userInfo01 == userInfo02);

/*结果:false*/

3:其他模式:request、sessoin、application;只能在web开发中使用

五、bean的自动装配

spring会在上下文中自动寻找,并自动给bean装配属性

在spring中有三种装配方式:

1:在xml总显示的配置

2:隐式的自动装配

3:在java中显示装配

场景:一个人拥有两只宠物
创建三个对象:Dog、Cat、People

package com.spring.vo1;

public class Dog {

    /*模拟狗叫*/
    public void shout(){
        System.out.println("wangwang~~~");
    }
}
package com.spring.vo1;

public class Cat {

    /*模拟猫叫*/
    public void shout(){
        System.out.println("miaomiao~~~~");
    }
}
package com.spring.vo1;

public class People {

    private Dog dog;/*宠物狗*/
    private Cat cat;/*宠物猫*/
    private String name;/*人的姓名*/

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

spring实例化三个对象:

<bean id="cat" class="com.spring.vo1.Cat"></bean>
    
<bean id="dog" class="com.spring.vo1.Dog"></bean>

<bean id="people" class="com.spring.vo1.People">
    <property name="name" value="king"></property>
    <property name="cat" ref="cat"></property>
    <property name="dog" ref="dog"></property>
</bean>

测试:

//获取容器
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
People people =applicationContext.getBean("people", People.class);

people.getCat().shout();
people.getDog().shout();

输出结果:

miaomiao~~~~
wangwang~~~

把以上场景改为自动装配:autowire

1:byName:自动在上下文中找 和自己对象中定义的属性名对应的bean id(id必须全局唯一)

<bean id="cat" class="com.spring.vo1.Cat"></bean>

<bean id="dog" class="com.spring.vo1.Dog"></bean>

<!--byName:会自动在上下文中找 和自己对象中定义的属性名对应的bean id-->
<bean id="people" class="com.spring.vo1.People" autowire="byName">
    <property name="name" value="king"></property>
</bean>

2:byType:自动在上下文中找 和自己对象中定义的属性名的类型对应的bean(class必须全局唯一)

<bean id="cat" class="com.spring.vo1.Cat"></bean>

<bean id="dog" class="com.spring.vo1.Dog"></bean>

<!--byType:会自动在上下文中找 和自己对象中定义的属性名的类型对应的bean-->
<bean id="people" class="com.spring.vo1.People" autowire="byType">
    <property name="name" value="king"></property>
</bean>

推荐阅读