栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

IOC底层原理及如何创建对象、注入属性

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

IOC底层原理及如何创建对象、注入属性

IOC容器
  • 什么是IOC
    • 控制反转,把对象创建和对象之间的调用的过程,交给Spring进行管理
    • 使用IOC目的:为了降低耦合度

IOC底层原理

IOC底层实现原理由三个部分组成:xml解析、工厂模式、反射

画图来展现一下他是如何降低耦合度的

可以看出这两个类之间的耦合度是比较高的,我们在开发过程中讲究的是高内聚、低耦合。我们使用设计模式中的工厂模式,可以降低耦合度

以上我们通过工厂类降低了耦合度,但我们需要做到的是将耦合度降低到最低限度,这时候我们就需要使用IOC容器,通过xml配置文件和反射来处理

以这种形式将耦合度降低到最低限度,这也是IOC的底层原理


IOC(BeanFactory接口)
  • IOC思想是基于IOC容器完成的,IOC容器就是我们上文所讲的对象工厂

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

    • BeanFactory:IOC容器的基本实现,是Spring使用的内部接口,不建议开发人员使用

      在读取配置文件时,加载配置文件并没有创建对象,而是等获取对象的时候再去创建

    • ApplicationContext:BeanFactory的子接口,有着更强大更完善的功能,供开发人员使用

      与BeanFactory不同,加载配置文件就将所有的对象创建完毕

    在实际开发中我们应该选择ApplicationContext,将所有的对象在服务器启动时创建完成,可以减少运行时间

  • ApplicationContext接口的两个实现类

    • FileSystemXmlApplicationContext:配置文件路径指的是盘符路径
    • ClassPathXmlApplicationContext:配置文件路径指的是工程路径

IOC操作Bean管理(基于xml)
  • 基于xml方式创建对象

    • 在spring配置文件中使用bean标签,标签里面添加对应属性,就可以实现对象的创建
    • 创建对象时候,默认也是执行无参数构造方法完成对象的创建
  • 基于xml方式注入属性

    • DI:依赖注入,就是注入属性
  • 第一种注入方式:使用set方法注入

    创建一个User类,使用set方式注入,对应的属性必须有set方法

    public class User {
        private String name;
        private String sex;
        public void setSex(String sex) {
            this.sex = sex;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + ''' +
                    ", sex='" + sex + ''' +
                    '}';
        }
    }
    

    xml文件如下配置

        
        
            
            
            
            
        
    
  • 第二种注入方式:使用构造器注入

    创建一个Book类,使用构造方法注入,pojo类中必须要有相对于的有参构造方法

    public class Book {
        private String bname;
        private String bauthor;
        public Book(String bname, String bauthor) {
            this.bname = bname;
            this.bauthor = bauthor;
        }
        @Override
        public String toString() {
            return "Book{" +
                    "bname='" + bname + ''' +
                    ", bauthor='" + bauthor + ''' +
                    '}';
        }
    }
    

    xml文件如下配置

        
        
            
            
            
        
    
  • 在set注入方式上进行简化:使用p名称空间注入(了解)

    假定创建一个Book类,这里就不重复贴代码了,使用set方法,必须要有对应属性的set方法

    需要在头文件中配置信息:xmlns:p=“http://www.springframework.org/schema/p”

    
    
        
        
        
    
    
    

IOC操作Bean管理(注入其他对象属性)

演示我们均使用set方法

  • 注入空值

        
        
            
            
                
            
        
    
  • 注入带特殊符号的值(比如<<>>)

    一种方法是使用转义字符,这里不做介绍,使用CDATA来解决此问题

        
        
            
                >]]>
            
            
        
    
  • 注入属性(外部Bean)

    我们创建一个Serivce和Dao类,Serivce对象中调用Dao的方法

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

    相当于我们将userDao对象作为一个属性创建,并赋值给UserService中

        
        
            
            
        
        
    
  • 注入属性(内部Bean)

    内部Bean和外部Bean效果是一样的,只不过写法不同

    我们创建一个部门和员工对象,部门为员工属性

    public class Dept {
        private String deptName;
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
        @Override
        public String toString() {
            return "Dept{" +
                    "deptName='" + deptName + ''' +
                    '}';
        }
    }
    public class Emp {
        private String name;
        private Dept dept;
        public void setName(String name) {
            this.name = name;
        }
        public void setDept(Dept dept) {
            this.dept = dept;
        }
        @Override
        public String toString() {
            return "Emp{" +
                    "name='" + name + ''' +
                    ", dept=" + dept +
                    '}';
        }
    }
    

    xml文件如下配置

        
        
            
            
                
                    
                
            
        
    
  • 级联赋值

    级联赋值第一种写法相当于外部Bean,这里主要介绍第二种写法

        
        
            
            
            
            
            
        
        
            
        
    

IOC注入数组集合
  • 普通数组、集合的注入

    编写一个类用于存放这些数组对象

    public class Stu {
        private String[] array;
        private List list;
        private Map maps;
        private Set sets;
        public void setArray(String[] array) {
            this.array = array;
        }
        public void setList(List list) {
            this.list = list;
        }
        public void setMaps(Map maps) {
            this.maps = maps;
        }
        public void setSets(Set sets) {
            this.sets = sets;
        }
        @Override
        public String toString() {
            return "Stu{" +
                    "array=" + Arrays.toString(array) +
                    ", list=" + list +
                    ", maps=" + maps +
                    ", sets=" + sets +
                    '}';
        }
    }
    

    在xml文件中对这些对象属性进行注入

    其中map集合在注入中与其他不同,可以这样理解,map底层实际上是entry数组,所以不用value,使用entry

        
            
            
                
                    数组1
                    数组2
                    数组3
                
            
            
            
                
                    List1
                    List2
                    List3
                
            
            
            
                
                    
                    
                    
                
            
            
            
                
                    set1
                    set2
                    set3
                
            
        
    
  • 数组的类型是对象类型

    在之前的book类之上,在创建一个BookShelf书架类,讲book作为数组对象传入

    public class BookShelf {
        private List books;
        //bookname是为抽取集合部分准备的(这里可以忽略)
        private List bookName;
        public void setBookName(List bookName) {
            this.bookName = bookName;
        }
        public void setBooks(List books) {
            this.books = books;
        }
        @Override
        public String toString() {
            return "BookShelf{" +
                    "books=" + books +
                    ", bookName=" + bookName +
                    '}';
        }
    }
    

    在xml文件中进行如下配置

        
        
            
                
                    
                    
                
            
        
        
            
            
        
        
            
            
        
    
  • 抽取集合注入部分

    在实际开发过程中,可能会有一些固定的属性,所有的bean对象实例都需要,这时候可以将他抽取出来,方便引用

    这里我们需要在头部文件中进行配置,配置以下两条语句

    xmlns:util="http://www.springframework.org/schema/util

    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd

    
    
        
        
            钢铁是怎样练成的
            80天环游世界
        
        
            
        
    
    

IOC操作Bean管理(FactoryBean工厂Bean)

在Bean管理中,Bean分为两大类,一种是普通Bean,另外一种是FactoryBean,之前使用的均为普通Bean,下面介绍两者的区别

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

FactoryBean:返回值类型可以和配置文件中定义的类型不同

  • 如何创建一个工厂Bean

    1. 创建一个类,作为工厂Bean,实现FactoryBean接口
    2. 实现接口里的方法,在getObject方法中定义返回值类型
  • 实现

    public class MyBean implements FactoryBean {
        @Override
        public User getObject() throws Exception {
            User user = new User();
            user.setName("Tom");
            return user;
        }
    
        @Override
        public Class getObjectType() {
            return null;
        }
    }
    

    xml配置文件如下

    
    

IOC操作Bean管理(Bean工作域)

bean的作用域是指在spring中设置Bean实例是单实例还是多实例,默认是单实例

先举一个单实例的例子,依旧还是使用上面讲到的MyBean对象做例子

设置scope属性为singleton,表示为单实例,不设置scope属性,默认是单实例


我们创建两个对象,区分单实例和多实例最简单的办法就是比较他们地址值是否相同,相同则为单实例,反之亦然

    @Test
    public void test5(){
        ApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
        User user1 = context.getBean("myBean", User.class);
        User user2 = context.getBean("myBean", User.class);
        System.out.println(user1 == user2);//true
    }

我们只要将scope属性设置为prototype则代表Bean实例为多实例


    @Test
    public void test5(){
        ApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
        User user1 = context.getBean("myBean", User.class);
        User user2 = context.getBean("myBean", User.class);
        System.out.println(user1 == user2);//false
    }

Bean的生命周期

Bean的生命周期分五步

  1. 空参构造器
  2. 使用set注入属性
  3. Bean的初始化方法(需要配置)
  4. 使用Bean
  5. 销毁Bean(需要配置)

演示

public class Student {
    private String name;

    public Student() {
        System.out.println("第一步:空参构造器");
    }

    public void setName(String name) {
        System.out.println("第二步:set注入属性");
        this.name = name;
    }

    public void initMethod(){
        System.out.println("第三步:Bean初始化方法");
    }

    public void destroyMethod(){
        System.out.println("第五步:销毁Bean");
    }
}
    
        
    
    @Test
    public void test6(){
        //这里使用ClassPathXmlApplicationContext,因为ApplicationContext没有close方法
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
        Student student = context.getBean("student", Student.class);
        System.out.println(student);
        System.out.println("第四步:实例化Bean");
        //手动销毁Bean
        context.close();
    }

测试结果:

第一步:空参构造器
第二步:set注入属性
第三步:Bean初始化方法
com.yellowstar.pojo.Student@c81cdd1
第四步:实例化Bean

在配置了Bean的后置处理器之后,Bean的生命周期会有7步

如何配置Bean的后置处理器

创建一个类实现BeanPostProcessor接口,并实现接口中的两个方法

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean初始化之前的方法");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean初始化之后的方法");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

重新测试test6方法,结果为

第一步:空参构造器
第二步:set注入属性
Bean初始化之前的方法
第三步:Bean初始化方法
Bean初始化之后的方法
com.yellowstar.pojo.Student@c81cdd1
第四步:实例化Bean
第五步:销毁Bean

可以发现这两个方法分别在Bean初始化方法之前和之后


Bean自动装配

自动装配指的是:根据指定装配的规则(属性名或类型),spring自动注入匹配的属性值(个人不太喜欢使用)

使用上文中提到的部门类和员工类

根据属性名自动注入

设置autowire为byName,需要注意的是dept的id值必须跟emp中Dept的属性名一样

    
    
        
    
    
        
    

根据类型自动注入

设置autowire为byType,需要注意的是,xml文件中配置的Dept对象必须只有一个,否则会报错

    
    
        
    
    
        
    

IOC操作Bean管理(外部属性文件)

我们在创建数据库连接池的时候,经常将连接数据库的配置写在一个properties文件中,这时候我们就需要配置操作外部属性文件

我们先使用原始办法注入属性

    
        
        
        
        
    

下面我们将这些属性写道一个jdbc.properties文件夹中,左边的属性名可以自行修改

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456

要想在xml文件中使用外部文件属性,需要在头部文件中进行配置

    
    
    
    
        
        
        
        
    

IOC操作Bean管理(基于注解)
  • 使用注解+xml配置文件创建Bean对象

    相比于之前的jar包,基于注解的方式需要另外导入一个jar包

    我们对xml文件进行如下配置

    
    
        
            
    
    

    这里我们创建一个Teacher类,需要使用注解创建对象,我们需要在类的前面添加以下注解信息,四种注解的效果都一样

    • @Component:推荐在dao层使用
    • @Service:推荐在service层使用
    • @Controller:推荐在web层使用
    • @Repository

    说明注解的value值相当于xml配置的id值,这里也可以忽略不写,系统默认生成单词的首字母小写格式

    @Controller(value = "teacher")
    public class Teacher {
        public void add(){
            System.out.println("add...");
        }
    }
    
  • xml文件中配置过滤器
    use-default-filters:表示不使用系统默认的过滤器

        
        
            
            
        
    
    	
        
            
            
        
    
  • 使用注解注入属性

    • 注入对象属性

      注入对象属性可以使用以下三种方法

      • @Autowired :根据对象类型注入属性
      • @Qualifier:根据对象名注入属性,需配合@Autowired 使用
      • @Resource:可以根据对象类型或对象名注入属性

      创建一个service类和一个dao类,并对这两个类设置注解。

      在类中的属性上设置以上三种注解

    • 注入普通属性

      使用@Value(value = ?)即可

      @Service
      public class StudentService {
      
          @Autowired  //根据类型注入属性
          @Qualifier(value = "studentDaoImpl")  //根据名称注入属性,要配合Autowired使用
          private StudentDao studentDao;
      
       // @Resource  //根据类型注入属性
          @Resource(name = "studentDaoImpl")  //根据名称注入属性
          private StudentDao studentDao1;
      
          @Value(value = "abc")
          private String name;
      
          public void test(){
              System.out.println("StudentDao test... " + name);
              studentDao.add();
              studentDao1.add();
          }
      }
      
      @Component
      public class StudentDaoImpl implements StudentDao {
          @Override
          public void add() {
              System.out.println("StudentDaoImpl add....");
          }
      }
      
  • 完全注解开发

    以上形式还是需要配合xml文件才能使用,可以创建一个配置类,来代替xml配置文件

    1. 创建一个类,设置为配置文件类

      @Configuration:表示该类作为配置文件类来使用

      @ComponentScan:等同于设置xml文件中的basePackages属性

      @Configuration //作为配置文件类
      @ComponentScan(basePackages = {"com.yellowstar"})
      public class SpringConfig {
      }
      
    2. 在测试类中需要更改以下读取配置文件方法

          @Test
          public void test3() {
              //使用AnnotationConfigApplicationContext(配置文件类的class)来创建
              ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
              StudentService service = context.getBean("studentService", StudentService.class);
              service.test();
          }
      
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/275940.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号