spring-ioc笔记

JAVA学习网 2020-08-09 10:46:02

1、Spring-IOC

程序的耦合:

  • 耦合:程序间的依赖关系。包括:

    • 类之间的依赖

    • 方法间的依赖

  • 解耦:降低程序间的依赖关系

  • 实际开发:

    • 应该做到,编译期不依赖,运行时才依赖

  • 解耦思路:

    • 第一步:使用反射来创建对象,而避免使用new关键字。

    • 第二步:通过读取配置文件来获取要创建的对象全限定类名。

 

1.1、bean工厂

自己建立一个简单的beanfactory(bean工厂)

  1. BeanFactory image-20200803221707382

     /**
      * 一个创建Bean对象的工厂
      *
      * Bean:在计算机英语中,有可重用组件的含义。
      * JavaBean:用java语言编写的可重用组件。
      *     javvabean >> 实体类(javabean远远大于实体类)
      * 他就是创建我们的service和dao对象的。
      *
      * 第一个:需要一个配置文件来配置我们的service和dao
      *     配置的内容:唯一标识=全限定类名(key=value)
      * 第二个:通过读取配置文件中配置内容,反射创建对象
      *
      * 我的配置文件可以是xml也可以是properties
      */
     public class BeanFactory {
         //定义一个Properties对象
         private static Properties props;
     
         //使用静态代码块位properties对象赋值
         static{
             try {
                 //实例化对象
                 props = new Properties();
                 //获取properties文件的流对象
                 //InputStream in = new FileInputStream("");     //这是要输入具体地址,一般要采用相对地址,也就是下面这个
                 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
                 props.load(in);
            }catch (Exception e){
                 System.out.println("导入错误");
            }
        }
     
         public static Object getBean(String beanName){
             Object bean = null;
             try {
                 String beanPath = props.getProperty(beanName);
                 bean = Class.forName(beanPath).newInstance();  //每次都会调用默认构造函数创建对象
            }catch (Exception e){
                 System.out.println("初始化错误");
            }
             return bean;
        }
     
     }
     

     

  2. UserMapper接口(相当与UserDao) image-20200803222045191

     public interface UserMapper {
         void queryUser();
     }

    UserMapperImpl(相当与UserDaoImpl)

     public class UserMapperImpl implements UserMapper {
         public void queryUser() {
             System.out.println("查询");
        }
     }

     

  3. UserService接口 image-20200803222054959

     public interface UserService {
         void queryUser();
     }

    UserServiceImpl

     public class UserServiceImpl implements UserService {
         //private UserMapper userMapper = new UserMapperImpl();
         private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
         public void queryUser() {
             userMapper.queryUser();
        }
     }

     

  4. Client(测试类) image-20200803222108718

     public class Client {
     
         public static void main(String[] args) {
             //UserService userService = new UserServiceImpl();
             UserService userService = (UserService)BeanFactory.getBean("UserService");
             userService.queryUser();
        }
     
     }

     

  5. bean.properties image-20200803222435042

     UserService=com.gzk.service.UserServiceImpl
     UserMapper=com.gzk.mapper.UserMapperImpl

     

  6. 运行Client

image-20200803222248856

1.2、单例,原型

我们的1.1中的bean工厂是单例的还是原型的呢?

有代码来解释:

 public class UserServiceImpl implements UserService {
     private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
     private int i = 1;
     public void queryUser() {
         userMapper.queryUser();
         System.out.println(i++);
    }
 }

 

 public class Client {
 
     public static void main(String[] args) {
         for(int i=0;i<5;i++) {
             UserService userService = (UserService) BeanFactory.getBean("UserService");
             System.out.println(userService);
             userService.queryUser();
        }
    }
 }

image-20200803223455708

由代码测试可得,我们这个工厂是原型的

原型(也就是说,每调用一次就new一个对象【一般用在存在线程安全问题的情况下使用】)

单例(也就是说,无论调用多少次都只有一个实例对象【只被创建你一次,对象只会初始化一侧】)

对象被创建多次,执行效率就会比较低,所以我们一般情况采用单例

所以我们要将这个工厂改造成单例的

 /**
  * 一个创建Bean对象的工厂
  *
  * Bean:在计算机英语中,有可重用组件的含义。
  * JavaBean:用java语言编写的可重用组件。
  *     javvabean >> 实体类(javabean远远大于实体类)
  * 他就是创建我们的service和dao对象的。
  *
  * 第一个:需要一个配置文件来配置我们的service和dao
  *     配置的内容:唯一标识=全限定类名(key=value)
  * 第二个:通过读取配置文件中配置内容,反射创建对象
  *
  * 我的配置文件可以是xml也可以是properties
  */
 public class BeanFactory {
     //定义一个Properties对象
     private static Properties props;
     //创建一个HashMap容器
     private static Map<String ,Object> beans;
 
     //使用静态代码块位properties对象赋值
     static{
         try {
             //实例化对象
             props = new Properties();
             //获取properties文件的流对象
             //InputStream in = new FileInputStream("");     //这是要输入具体地址,一般要采用相对地址,也就是下面这个
             InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
             props.load(in);
             //实例化容器
             beans = new HashMap<String, Object>();
             //取出配置文件中所有的key
             Enumeration keys = props.keys();
             //遍历枚举
             while(keys.hasMoreElements()){
                 //取出每一个key
                 String key = keys.nextElement().toString();
                 //根据key获取value
                 String beanPath = props.getProperty(key);
                 //通过反射创建对象
                 Object value = Class.forName(beanPath).newInstance();
                 //把key和value存入容器中
                 beans.put(key ,value);
            }
        }catch (Exception e){
             System.out.println("错误");
        }
    }
 
     public static Object getBean(String beanName){
         return beans.get(beanName);
    }
 
 }

image-20200803230351756

1.3、IOC控制反转

传统方式

image-20200803234422204

IOC方式

image-20200803234345298

控制反转(Inversion of Control,英文缩写IOC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专业术语,它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

除main函数之外,其他代码都在【1.1、bean工厂】中。

Client

 public class Client {
 
     /**
      * 获取spring的IOC核心容器,并根据id获取对象
      *
      * ApplicationContext的三个常用实现类
      *     ClassPathXmlApplicationContext     --> 它可以加载路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了【相对路径】
      *     FileSystem.XmlApplicationContext   --> 它可以加载磁盘任意路径下的配置文件(必须要有访问权限)【绝对路径】
      *     AnnotationConfigApplicationContext --> 它是用于读取注解创建容器的
      *
      * 核心容器的两个接口引发的问题
      * ApplicationContext:
      *     它在构建核心容器时,创建对象采取的策略时采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。(单例时)
      *     它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。(原型时)
      * BeanFactory:
      *     它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
      * @param args
      */
 
     public static void main(String[] args) {
         //1.获取核心容器对象
         //ApplicationContext context = new FileSystemXmlApplicationContext("D:\\idea-workspace\\my-site\\spring02-study\\spring-01\\src\\main\\resources\\bean.xml");
         ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
         //2.根据id查找对应的bean实例
         //UserService userService = (UserService) context.getBean("userService");
         UserService userService = context.getBean("userService", UserService.class);
         userService.queryUser();
    }
 
 }

 

 <?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
         https://www.springframework.org/schema/beans/spring-beans.xsd">
     <bean id="userMapper" class="com.gzk.mapper.UserMapperImpl"/>
 </beans>

1.4、bean的三种创建方式

 <?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
         https://www.springframework.org/schema/beans/spring-beans.xsd">
 
     <!-- 把对象的创建交给spring来管理 -->
     <!--
         spring对bean的管理细节
         1.创建bean的三种方式
         2.bean对象的作用范围
         3.bean对象的生命周期
     -->
     <!-- 创建bean的三种方式 -->
     <!--
         1.使用默认构造函数创建
             在spring配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,
             采用的时默认构造函数(无参构造)创建bean对象,若没有无参构造函数,则对象无法创建
         <bean id="userMapper" class="com.gzk.mapper.UserMapperImpl"/>
     -->
 
     <!--
         2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
         <bean id="instanceFactory" class="com.gzk.factory.InstanceFactory"/>
         <bean id="name" factory-bean="instanceFactory" factory-method="getAccountService"/>
     -->
 
 
     <!--
         3.使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器
         <bean id="name" class="com.gzk.factory.StaticFactory" factory-method="getAccountService"/>
     -->
 
 
 </beans>

1.5、bean对象的作用范围

bean标签的scopc属性:

  • 作用:用于指定bean的作用范围

  • 取值:

    • singleton:单例的(默认值)

    • prototype:原型的

    • request:作用于web应用的请求范围

    • session:作用于web应用的会话范围

    • global-session:作用于集群环境的会话范围【当有多台服务器时(全局会话范围)】,当不是集群环境时,它就是session

 <bean id="userService" class="com.gzk.service.UserServiceImpl" scope="prototype"/>

 

bean对象的生命周期

  • 单例对象:

    • 出生:当容器创建时对象出生

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

    • 死亡:容器销毁,对象消亡

    • 总结:单例对象的生命周期和容器相同

  • 原型对象(多例):

    • 出生:当我们使用对象时spring框架为我们创建

    • 活着:对象只要是在使用过程中就一直活着

    • 死亡:当对象长时间不用,且没有别的对象引用时,由Java垃圾回收器回收

 public class AccountServiceImpl implements AccountService {
 
     public AccountServiceImpl(){
         System.out.println("AccountServiceImpl的无参构造函数");
    }
 
     public void init(){
         System.out.println("对象初始化!");
    }
     public void getAccountService(){
         System.out.println("方法执行中");
    }
     public void destroy(){
         System.out.println("对象销毁!");
    }
 }
 public interface AccountService {
     void init();
     void getAccountService();
     void destroy();
 }
 
 public class Client {
     public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
         AccountService accountService = null;
         for(int i=0;i<5;i++) {
             accountService = context.getBean("accountService", AccountService.class);
             System.out.println(accountService);
             accountService.getAccountService();
             System.out.println();
        }
 }

 

 <!--单例-->
 <bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
 <!--原型-->
 <!--<bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="prototype" init-method="init" destroy-method="destroy"/>-->
 

 

 

单例:

image-20200804104107871

原型:

image-20200804104009413

1.6、spring的依赖注入

依赖注入:Dependency Injection

IOC的作用:

  • 降低程序间的耦合(依赖关系)

依赖关系的管理:

  • 以后都交给spring来维护

在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明

依赖注入:

  • 能注入的数据:有三类

    • 基本类型和String

    • 其他bean类型(在配置文件中或者注解配置过的bean)

    • 复杂类型/集合类型

  • 注入的方式:有三种

    • 使用构造函数

    • 使用set方法

    • 使用注解

1.6.1、构造函数注入:

  • 使用的标签:constructor-arg

  • 标签出现的位置:bean标签的内部

  • 标签中的属性

    • type:用于指定要注入大的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型

    • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始

    • name:用于指定给构造函数中指定名称的参数赋值 =====以上三个用于指定给构造函数中某些参数赋值=====

    • value:用于提供基本类型和String类型的数据

    • ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象

  • 优势:

    • 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。

 <bean id="constructorService" class="com.gzk.service.ConstructorService">
         <constructor-arg index="0" value="111"/>
         <constructor-arg index="1" value="张三"/>
         <constructor-arg index="2" ref="dateTime"/>
     </bean>
 
     <bean id="dateTime" class="java.util.Date"/>

 

1.6.2、set方法注入

  • 使用的标签:property

  • 标签出现的位置:bean标签的内部

  • 标签中的属性

    • name:用于指定注入时所调用的set方法

    • value:用于提供基本类型和String类型的数据

    • ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象

  • 优势

    • 创建对象时,没有明确的限制

  • ss

  •  

 <bean id="constructorService2" class="com.gzk.service.ConstructorService2">
         <property name="id" value="111"/>
         <property name="name" value="张三"/>
         <property name="dateTime" ref="dateTime"/>
     </bean>
     
     <bean id="dateTime" class="java.util.Date"/>

 

  • 复杂类型注入/集合类型注入

    • 用于给List结构【set<T>,List<T>,String[]】集合注入的标签:

      • list array set【三者可以混用,无明确规定】

    • 用于给Map结构【Map<K ,V>,Properties】集合注入的标签:

      • map props【二者可以混用,无明确规定】

 public class SetService {
     private String name;
     private String[] stringList;
     private List<String> list;
     private Set<String> set;
     private Map<String ,Object> map;
     private Properties properties;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setStringList(String[] stringList) {
         this.stringList = stringList;
    }
 
     public void setList(List<String> list) {
         this.list = list;
    }
 
     public void setSet(Set<String> set) {
         this.set = set;
    }
 
     public void setMap(Map<String, Object> map) {
         this.map = map;
    }
 
     public void setProperties(Properties properties) {
         this.properties = properties;
    }
 
     @Override
     public String toString() {
         return "SetService{" +
                 "name='" + name + '\'' +
                 ", stringList=" + Arrays.toString(stringList) +
                 ", list=" + list +
                 ", set=" + set +
                 ", map=" + map +
                 ", properties=" + properties +
                 '}';
    }
 }

 

 <bean id="setService" class="com.gzk.service.SetService">
     <property name="name" value="张三"/>
     <property name="stringList">
         <list>
             <value>AAA</value>
             <value>BBB</value>
             <value>CCC</value>
         </list>
 
     </property>
 
     <property name="list">
         <array>
             <value>AAA</value>
             <value>BBB</value>
             <value>CCC</value>
         </array>
     </property>
 
     <property name="set">
         <set>
             <value>AAA</value>
             <value>BBB</value>
             <value>CCC</value>
         </set>
     </property>
 
     <property name="map">
         <props>
             <prop key="1">DDD</prop>
             <prop key="2">EEE</prop>
             <prop key="3">FFF</prop>
         </props>
 
     </property>
 
     <property name="properties">
         <map>
             <entry key="1" value="DDD"/>
             <entry key="2">
                 <value>
阅读(2517) 评论(0)