博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
02-Spring与IOC
阅读量:4460 次
发布时间:2019-06-08

本文共 8732 字,大约阅读时间需要 29 分钟。

二、Spring与IOC

1. 控制反转IOC的概述

控制反转(IOC:Inversion of Control)是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。

IOC是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入的方式应用更加广泛。

  • 依赖注入【DI:Dependency Injection,DI】:是指在程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
  • 依赖查找【DL:Dependency Lookup,DL】:容器提供回调接口和上下文环境给组件,程序代码则需要提供具体的查找方式。比较典型的是依赖与JNDI服务接口(Java Naming and Directory Interface)的查找。
    依赖注入是目前最优秀的解耦方式。依赖注入让Spring的bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。

总结:

IOC控制反转就是将对象的创建全给了Spring容器。
DI依赖注入的前提是有IOC的环境,Spring创建这个类的过程中,Spring将类依赖的属性设置进去。

2. Spring的工厂类

Bean组件主要解决:Bean的定义,Bean的创建以及对Bean的解析。

开发者关心Bean的创建,其他由Spring内部帮你完成。
(1)Bean的创建是典型的工厂模式,它的顶级接口是BeanFactory,

ApplicationContext工厂:

ApplicationContext用于加载Spring的配置文件,在程序中充当“容器”的角色,其实现类有两个,通过ctrl+t查看。

A:配置文件在类路径下:使用ClassPathXmlApplicationContext实现类进行加载。
B:配置文件在本地目录中或者在项目根路径下:使用FileSystemXmlApplicationContext实现类进行加载。
1498293-20190228133559298-1839207226.png

@Test    public void classPathXmlTest() {        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        IStudentService studentService = (IStudentService) ac.getBean("studentService");        studentService.some();    }    @Test    public void FileSystemXmlTest() {        ApplicationContext ac = new FileSystemXmlApplicationContext("d:/applicationContext.xml");        IStudentService studentService = (IStudentService) ac.getBean("studentService");        studentService.some();    }
BeanFactory工厂:

BeanFactory接口对象也可作为Spring容器出现。BeanFactory接口是ApplicationContext接口的父类。

若要创建BeanFactory容器,需要使用其实现类XmlBeanFactory,该类可以加载Spring配置文件。而Spring配置文件以资源Resource的形式出现在XmlBeanFactory类的构造器参数中。Resource是一个接口,其具体有两个实现类:
A:ClassPathResource:指定类路径下的资源文件
B:FileSystemResource:指定项目根路径或者本地磁盘路径下的资源文件。
1498293-20190228133632196-1082601483.png

@Test    public void BeanFactoryTest() {        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));        IStudentService studentService = (IStudentService) factory.getBean("studentService");        studentService.some();    }
BeanFactory和ApplicationContext的区别:

虽然这两个接口容器所要加载的Spring配置文件是同一个文件,但在代码中的这两个容器对象却不是同一个对象,即不是同一个容器:它们对于容器内对象的装配(创建)时机是不同的。

ApplicationContext容器中对象的装配时机:

ApplicationContext容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可,执行效率比较高,但占用内存。

ApplicationContext容器中对象的装配时机:

BeanFactory容器,对容器中对象的装配与加载采用延迟加载策略,即在第一次调用getBean()时,才真正装配该对象。

@Test    public void BeanFactoryTest() {        // 获取容器:此时容器中的对象还未进行装配        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));        // 执行下面语句时,会对studentService对象进行装配        IStudentService studentService = (IStudentService) factory.getBean("studentService");        studentService.some();    }

3. Bean的装配

Bean的装配,即Bean对象的创建。容器根据代码要求创建Bean对象后再传递给代码的过程,称为Bean的装配。

装配的方式:默认形式、

  • 默认装配方式
    代码通过getBean()方法从容器中获取指定的Bean实例,容器首先会调用Bean类的无参构造器,利用反射,创建空值的实例对象。
  • 动态工厂Bean
    有些时候,项目中需要通过工厂类来创建Bean实例,而不能像前面例子中似的,直接由Spring容器来装配Bean实例。使用工厂模式创建Bean实例,就会使工厂类与要创建的Bean类耦合到一起。
    (1)动态工厂Bean作为普通Bean使用
    将动态工厂Bean作为普通Bean来使用是指,在配置文件中注册动态工厂Bean后,测试类直接通过getBean()获取到工厂对象,再由工厂对象调用其相应方法创建相应的目标对象。配置文件中无需注册目标对象的Bean。因为目标对象的创建不由Spring容器来管理。
    但这样做的缺点是:不仅工厂类与目标类耦合到一起了,测试类与工厂类也耦合到一起了。
public class ServiceFactory{    public IUserService getUserService(){        return new UserServiceImpl();    }}
@Testpublic void factoryTest(){    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");    // 从Spring容器中获取factory    ServiceFactory factroy = ac.getBean("factory");    IUserService userService = factory.getUserService();    userService.doSome();}

(2)使用Spring的动态工厂Bean

Spring对于使用动态工厂来创建的Bean,由专门的属性定义。
factory-bean指定相应的工厂Bean,由factory-method指定创建所用方法。此时配置文件中至少会有两个bean的定义:工厂类的bean,与工厂类所要创建的目标类Bean。而测试类中不再需要获取工厂Bean对象了,可以直接获取目标Bean对象,实现测试类与工厂类间的解耦。

@Testpublic void springFactoryTest(){    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");    IUserService userService = ac.getBean("userService");    userService.doSome();}

(3)静态工厂Bean

使用工厂模式中的静态工厂来创建实例Bean。
此时需要注意:静态工厂无需工厂实例,所以不再需要定义静态工厂。
而对于工厂所要创建的Bean,其实不是由自己的类创建的,所以无需指定自己的类。但其实是由工厂类创建的,所以需要指定所用工厂类,故class属性指定的是工厂类而非自己的类。当然,还需要通过factory-method属性指定工厂方法。

public class ServiceFactory{    public static IUserService getUserService(){        return new UserServiceImpl();    }}

4. 容器中Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean的实例化,还可以通过scope属性,为Bean指定特定的作用域。Spring支持五种作用域。

(1)singleton:单例模式。即在整个Spring容器中,使用Singleton定义的Bean将是单例的,只有一个实例,默认为单例的。
(2)prototype:多例模式。即每次使用getBean()方法获取实例都是一个新的实例。
(3)request:对于每次HTTP请求,都将会产生一个不同的Bean实例。
(4)session:对于每个不同的HTTP session,都将会产生一个不同的Bean实例。
(5)global-session:

注意:

1.对于scope的值为request、session、global-session,只有在Web应用是使用Spring时,该作用于才有效。
2.对于scope的值为singleton的单例模式,该bean是在容器被创建时就被装配好了。
3.对于scope的值为prototype的原型模式,Bean实例是在代码中使用该Bean实例时才进行装配的。

5. Bean的后处理器

Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,均会自动执行该类的两个方法。由于该Bean是由其他Bean自动调用执行,不是程序员手工调用,故此Bean无需id属性。

需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可实现对指定的Bean的指定方法进行功能扩展与增强。方法返回地Bean对象,即是增强过的对象。
代码中需要自定义Bean后处理器类。该类是实现了接口BeanPostProcessor的类。该接口中包含两个方法,分别在目标Bean初始化完毕之前与之后执行。它们的返回值为:功能被扩展或增强后的Bean对象。
Bean初始化完毕有一个标志,一个方法将被执行。即当该方法被执行时,表示该Bean被初始化完毕。所以,Bean后处理器中两个方法的执行,是在这个方法之前之后执行。这个方法在后面将会讲到。
public Object postProcessBeforeInitialzation(Object bean,String beanId) throws BeansException
该方法会在目标Bean初始化完毕之前由容器自动调用。
public Object postProcessAfterInitialzation(Object bean,String beanId) throws BeansException
该方法会在目标Bean初始化完毕之后由容器自动调用。
它们的参数是:第一个参数是系统即将初始化的Bean实例,第二个参数是该Bean实例的id属性值。若Bean没有id就是name属性值。

举例:

程序中有一个业务接口IService,其中两个业务方法some()和other()。有两个Bean:StudentServiceImpl和TeacherServiceImpl(),均实现了IService接口。
要求:对StudentServiceImpl的some()方法进行增强,输出其开始执行时间与执行结束时间。

public interface IService{    void some();    void other();}public class StudentServiceImpl implements IService{    @Override    public void some(){        System.out.println(this.getClass().getSimpleName()+",执行some()方法");    }    @Override    public void other(){        System.out.println(this.getClass().getSimpleName()+",执行other()方法");    }}public class MyBeanPostProcessor implements BeanPostProcessor{    @Override     public Object postProcessBeforeInitialization(Object bean,String beanName){        System.out.println("执行postProcessBefreInitialization()");        // 即使不对bean进行增强,也要使用方法返回bean,不能为默认的null,否则将抛出空指针异常。        return bean;    }       @Override     public Object postProcessAfterInitialization(final Object bean,String beanName){        if("studentService".equals(beanName)){            Proxu.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),new InvocationHandler(){    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{        if("some".equals(method.getName())){            System.out.println("目标方法执行开始时间:"+System.currentTimeMillis());            // 执行目标方法            Object result = method.invoke(bean,args);            System.out.println("目标方法执行结束时间:"+System.currentTimeMillis());            return result;        }        return method.invoke(bean,args);    }});return proxy;        }    return bean;    } }
@Testpublic void factoryTest(){    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");    // 从Spring容器中获取factory    IService studentService= (IService)ac.getBean("studentService");    studentService.some();    studentService.other();    IService teacherService= (IService)ac.getBean("teacherService");    teacherService.some();    teacherService.other();}

6. 定制Bean的生命始末

可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。

步骤:

  1. 这些方法需要在Bean类中实现定义好:方法名是随意的public void方法。
  2. 其次在配置文件的标签中添加如下属性
    • init-method:指定初始化方法的方法名
    • destory-method:指定销毁方法的方法名

注意:如果希望看到Bean的destory-method的执行结果,需要满足两个条件:

(1)Bean为singleton,即单例的。
(2)要确保容器关闭,接口ApplicationContext没有close()方法,但其实现类有。所以,可以将ApplicationContext强转为其实现类对象,或者直接创建的就是实现类对象。

@Testpublic void destoryMethodTest(){    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");    IUserService userService = (IUserService) ac.getBean("userService");    userService.doSome();    userService.doOther();    // 关闭容器对象    ((ClassPathXmlApplicationContext)ac).close();}

7. Bean的生命周期

Bean实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。

  1. 调用无参构造器,创建实例对象。
  2. 调用参数的setter,为属性注入值。
  3. 若Bean实现了BeanNameAware接口,则会执行接口方法setBeanName(String beanId),使bean类可以获取其在容器中的id名称。
  4. 若Bean实现了BeanFactoryAware接口,则执行接口方法setBeanFactory(BeanFactory factory),使Bean类可以获取到BeanFactory对象。
  5. 若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessBeforeInitialization()。
  6. 若Bean实现了InitializingBean接口,则执行接口afterPropertiesSet()。该方法在Bean的所有属性的set方法执行完毕后执行,是Bean初始化结束的标志,即Bean实例化结束。
  7. 若设置了init-method方法,则执行。
  8. 若定义并注册了Bean后处理器BeanPostProcssor,则执行接口方法postProcessAfterInitialization()。
  9. 执行业务方法。
  10. 若Bean实现了DisposableBean接口,则执行接口方法destory()。
  11. 若设置了destory-method方法,则执行。

转载于:https://www.cnblogs.com/zhy0720/p/10449864.html

你可能感兴趣的文章
记录安装oracle的那些事(二)之双系统安装
查看>>
c3po数据库连接池中取出连接
查看>>
bootstrap-table 分页
查看>>
使用本机IP调试web项目
查看>>
【Java面试题】58 char型变量中能不能存贮一个中文汉字?为什么?
查看>>
C++ Primer 第六章 函数
查看>>
交互设计算法基础(3) - Quick Sort
查看>>
Ubuntu各种软件的安装
查看>>
智能社的邀请码
查看>>
算法与分析 统计数字问题和整数因子分解问题?
查看>>
变量提升
查看>>
谜题88:原生类型的处理
查看>>
ajax 415 错误 $.ajax 中的contentType
查看>>
【CodeForces】191C Fools and Roads
查看>>
enum hack
查看>>
2017.2.7 开涛shiro教程-第六章-Realm及相关对象(三)
查看>>
Visual Studio 2008切换到设计视图卡死解决办法-Troubleshooting "Visual Studio 2008 Design view hangs" issues...
查看>>
数据库设计范式
查看>>
sql2005-数据库备份方案 (转载)
查看>>
centos中安装jdk的操作
查看>>