精华内容
下载资源
问答
  • 控制反转
    2022-06-04 17:04:46

    Java中自带的函数或对象都是编写软件的时候写的,当它需要调用你自己编写的数据的时候,它如何又不知道你定义的类(对象)是什么,里面有什么成员,它无法调用你的代码,这个时候就需要用到控制反转了。
    简单来说,控制反转是java编写时留的一个接口,它可以通过这个接口来调用你自己写的代码。

    如下代码是控制反转的一个示例,代码定义了一个按钮,将按钮添加进窗口中,
    当点击时触发addActionListener(控制反转)

    package test;
    
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
     
    
    public class test extends JFrame{     //需要继承JFrame
    	
        public test(String title)
        {
    
            JFrame frame = new JFrame(title);    
            JButton bnt3 = new JButton("Hello,Java");
            JLabel lab1 = new JLabel("Hello,label");
            frame.add(bnt3, BorderLayout.SOUTH);
            frame.add(lab1);
            frame.setBounds(200,200,300,200); //设置窗口的属性 窗口位置以及窗口的大小
    //      frame.pack();
            frame.setVisible(true);//设置窗口可见
            bnt3.addActionListener(new ActionListener() {
            	
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				PrintPrompt();			//按钮调用类中的成员
    				lab1.setText("btn3");
    				frame.repaint();
    			}
    		});
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
        
        public void PrintPrompt() {
        	System.out.println("this is a function");
        }
        
        public static void main(String[] args) {
            new test("窗口");        //创建窗口
            
        }
    }
    

    JButton类是很早就写好的,在编写时不可能想到后面我写了“test”类,它就需要进行控制反转。

    JButton类知道自己什么时候被按下去了,JButton类有一个接口ActionListener,接口中只有一个函数:actionPerformed()。
    JButton还有一对函数:addActionListener和removeActionLister。

    如果你对JButton被按下去感兴趣(按下去执行操作),就实现一个ActionListener接口的子类,这样你定义出来的类也会有actionPerformed()的函数(继承),你可以对子类中的actionPerformed()函数进行各种操作,如调用外部类的成员(如果是内部类)、输出一个数值、改变窗口的显示等等。
    操作完成后通过addActionListener将子类的actionPerformed()传递给JButton,JButton再通过actionPerformed返回来调用你传进来的成员。
    因为数据(函数)是通过actionPerformed()传给JButton,所有格式肯定符合actionPerformed的规范,可以被JButton使用。
    请添加图片描述

    更多相关内容
  • 主要介绍了Java spring webmvc如何实现控制反转,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 控制反转

    2021-02-13 10:03:02
    控制反转 我最近在一个新的移动应用程序项目中工作,我使用依赖项服务在android中获取版本号,然后在项目中的任何地方使用它。 已创建一个接口以解耦版本号功能。 可以通过适当的调用在项目中的任何地方使用它。 ...
  • c# 依赖注入 控制反转

    2017-08-14 15:24:53
    控制反转 依赖注入的c#实现,很好的教程。
  • IoC——Inversion of Control 控制反转 1、参与者都有谁?  答:一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。又要名词解释一下,某个对象指的就是任意的、普通的Java对象...
  • 本文介绍了Spring框架中的控制反转IOC和依赖注入DI,欢迎阅读,共同学习,一起进步。 Spring框架基础参考:深入学习Spring基础 文章目录一.入门程序和IOC简介二.IOC-控制反转三.DI-依赖注入四. IOC和DI五.Spring...
  • 他是指一种特定的的解耦形式,使得高层次的模块不依赖低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象. 该原则规定: 高层次的模块不应该依赖低层次模块,二者都应该...
  • 首先我们来吹吹牛,什么叫IoC,控制反转(Inversion of Control,英文缩写为IoC),什么意思呢? 就是你一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗~~ IoC的原则是:...
  • 主要介绍了springboot通过面向接口编程对控制反转IOC的理解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
  • 主要为大家详细介绍了Java使用IOC控制反转的三种设计模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些力量。...
  • 在Java开发中,IoC意 味着将你设计好的类交给系统去控制,而不是在你的类内部控制。这称为控制反转。下文给大家介绍Java面试和开发中的IoC(控制反转)知识,需要的朋友参考下吧
  • 最近在使用ThinkPHP5框架,看了下他的源码,发现有很多地方也用到了依赖注入(控制反转),觉得有必要和大家简单聊一聊什么是依赖注入以及怎么使用它。
  • 本文主要介绍了PHP控制反转(IOC)和依赖注入(DI)的相关知识。具有很好的参考价值。下面跟着小编一起来看下吧
  • 主要给大家介绍了关于ASP.NET Core依赖注入系列教程之控制反转(IoC)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 依赖倒置 控制反转 依赖注入 面向
  • IOC控制反转

    2018-03-09 14:33:28
    本文文档中有 控制反转 DI依赖注入,希望可以给大家在技术上有一点的帮助
  • Spring IOC 控制反转

    2018-10-24 20:12:18
    Spring IOC 控制反转简单过程 1: 导入需要报 2:构建spring的配合文件 3: 将我们创建的类交给spring容器管理 4: 实例化容器 5: 从容器中获取实例
  • 是关于java Spring框架中“耦合性和控制反转(IOC)、依赖注入、属性注入、构造函数注入”等原理。 pring框架的原理和开发流程。
  • IoC控制反转

    千次阅读 2022-05-25 00:48:50
    IoC控制反转 一、IoC概念和原理 1. 什么是IoC 2. IoC底层原理 二、IoC过程 1. xml配置文件,配置创建的对象 2. 有UserService和UserDao类,创建工厂类 三、IoC接口 1. IoC思想基于IoC容器完成,IoC容器底层就是Bean...

    一、IoC概念和原理

    1. 什么是IoC

    (1) 控制反转:把对象创建和对象之间的调用过程,交给Spring进行管理。
    (2)使用IoC目的:降低耦合性

    2. IoC底层原理

    (1)xml解析、工厂模式、反射

    二、IoC过程

    1. xml配置文件,配置创建的对象

    <!--配置创建的对象-->
    <bean id="dao" class="com.jxk.UerDao"></bean>
    

    2. 有UserService和UserDao类,创建工厂类

    class UserFactory{
    	public static UserDao getDao(){
    		String classValue = class属性值;//1.xml解析
    		Class clazz = Class.forName(classValue);//2.通过反射创建对象
    		return (UserDao)clazz.newInstance();
    	}
    }
    

    三、IoC接口

    1. IoC思想基于IoC容器完成,IoC容器底层就是Bean工厂

    2. Spring提供实现IoC容器的两种方式(两个接口)

    1)BeanFactory :IoC容器基本实现,是Spring内部使用的接口,不提供开发人员使用。
    BeanFactory加载配置文件时不创建对象,在获取对象(使用)时才创建
    2)ApplicationContext :实际是BeanFactory的子接口,提供更强大的功能,提供给开发人员使用。
    ApplicationContext加载配置文件时就创建对象

    3. ApplicationContext接口的实现类

    在这里插入图片描述

    四、IoC操作

    1.Bean管理

    0)什么是Bean管理:Bean管理指的是两个操作
    1)Spring创建对象
    2)Spring注入属性

    2. Bean管理操作的两种方式

    1)基于xml配置文件实现
    2)基于注解方式实现

    3. IoC操作Bean管理(基于xml方式实现)

    基于xml方式创建对象

    <!--配置创建的对象-->
    <bean id="user" class="com.jxk.UserDao"></bean>
    

    1) 在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。
    2) 常用属性:
    id:唯一标识
    class:类全路径(包类名)
    3) 创建对象时,默认调用无参构造函数进行创建

    基于xml方式注入属性

    DI:依赖注入,就是注入属性
    第一种注入方式:set方法
    首先创建类,定义属性和对应的set方法
    在这里插入图片描述
    然后在Spring配置文件中配置创建对象配置属性注入(基于set方法)
    在这里插入图片描述
    第二种注入方式:使用有参构造函数
    首先创建类,定义属性并生成相应的有参构造函数:

    /**
     * 使用有参构造注入属性
     */
    public class Orders {
    
        //定义属性
        private String oname;
        private String address;
    
        //生成有参构造函数
        public Orders(String oname, String address) {
            this.oname = oname;
            this.address = address;
        }
    
        //测试函数
        public void OrdersTest(){
            System.out.println(oname+"::"+address);
        }
    }
    

    然后在Spring的xml配置文件中注入属性(有参构造方法)

    <!-- 使用有参构造函数注入属性 -->
        <bean id="orders" class="com.jxk.Spring5.Orders">
            <constructor-arg name="oname" value="电脑"></constructor-arg>
            <constructor-arg name="address" value="China"></constructor-arg>
        </bean>
    

    bean作用域

    在Spring里,可以设置创建的实例是单实例还是多实例。
    scope属性值:第一个值 默认值,singleton,表示单实例对象;第二个值 prototype 表示多实例对象。
    singleton和prototype区别:
    singleton:加载配置文件时就会创建单实例对象
    prototype:在调用getBean方法时创建多实例对象。
    request
    session

    bean生命周期

    1. 无参构造器创建bean实例
    2. 使用类中的set方法设置属性值和对其他bean引用
    3. 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
    4. 调用bean的初始化方法(需要进行配置初始化的方法InitMethod)
    5. 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
    6. bean可以使用了(获取到创建的对象之后)
    7. 当容器关闭的时候,调用bean的销毁的方法(需要进行配置销毁的方法DestroyMethod)

    xml自动装配

    1. 什么是自动装配?根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
    2. 实现自动装配:
      bean标签属性autowire,配置自动装配
      autowire属性常用两个值:byName,byType

    外部属性文件

    第一种方法: 直接配置连接池
    第二种方法:引入外部属性文件配置数据库连接池(1. 创建外部属性文件,properties格式文件,写数据库信息;2. 把外部properties属性文件引入到Spring配置文件中,即引入context标签,然后使用标签引入外部属性文件)

    4. IoC操作Bean管理(FactoryBean)

    1. Spring有两种类型bean,一种普通bean,另外一种工厂bean
    2. 普通bean:在配置文件中定义bean类型就是返回类型
    3. 工厂bean:在配置文件中定义bean类型可以和返回类型不同
      第一步:创建类,让这个类作为工厂bean,实现接口FatoryBean
      第二步:实现接口里面的方法,在实现的方法中定义返回的bean类型

    5. IoC操作Bean管理(基于注解方式)

    1. 什么是注解?
      注解是代码的特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)
      使用注解,作用在类、方法、属性上面
      目的:简化xml配置
    2. Spring针对Bean管理中创建对象提供注解
      (1)@Component
      (2)@Service
      (3)@Controller
      (4)@repository
    3. 提供注解步骤
      1)引入AOP依赖 jar包
      2)开启组件扫描:如果扫描多个包,多个包使用逗号隔开;
    <context:component-scan base-package="com.jxk"></context:component-scan>
    

    3)创建类,在类上卖弄添加创建对象注解
    //在注解里面value属性值可以省略不写
    //默认值是类名称,首字母小写
    4. 开启组件扫描细节配置(include、exclude)
    5. 基于注解方式实现属性的注入
    (1)@Autowired:根据属性类型进行自动装配

    (2)@Qualifier:根据属性名称进行注入(与Autowired一起使用)
    (3)@Resource :可以根据类型注入,可以根据名称注入
    (4)@Value:注入普通类型
    6. 完全注解开发步骤
    1)创建配置类,替代xml配置文件

    @Configuration//作为配置类,替代xml文件
    @ComponentScan(basePackages={"com.jxk"}

    2)编写测试类

    //1. 加载配置类
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    //2. 获取配置创建的对象
    UserService userService = context.getBean("userService",UserService.class);
    userService.add();
    
    展开全文
  • 文章目录前言例子控制反转DI的方式构造函数注入set注入接口注入Service Locator学习资源 前言 前段时间看了Spring源码,有许多概念不是很清晰,所以看了一些资料,有不少收获,在此总结一下Inversion of Control ...

    前言

    前段时间看了Spring源码,有许多概念不是很清晰,所以看了一些资料,有不少收获,在此总结一下Inversion of Control Containers and the Dependency Injection pattern 这篇文章。

    目前java的轻量级容器非常流行,可以把不同项目的组件装配起来,进而成为一个独立的应用。而这些容器底层都是用同一种模式来进行组件的装配,也就是“IOC(Inversion of Control/控制反转)”,更具体来说叫“DI(Dependency Injection/依赖注入)”。下文将主要对比IOC/DI和Service Locator(服务定位器)的不同之处,当然他们也有共同的作用:将组件的配置和使用分离

    关键代码

    有一个非常好用的电影查询组件。

     /**
     * 查询电影列表接口:findAll方法用于查询电影列表。
     */
    public interface MovieFinder
    {
        List findAll();
    }
     
     /**
     * 查询电影列表实现类:用于适配不同的存储方式:txt、xml文件、数据库等。
     * 下面省略get set 构造方法。
     */
    class MovieFinderImpl implements MovieFinder{  
        public MovieFinderImpl(String filename)
        {
            this.filename = filename;
        }
        public List findAll(){
    		//具体实现
    	}
    }
    
    /**
    * moviesDirectedBy可以根据电影导演名从列表中筛选电影。
    * 控制反转的目的是让查询和存储解耦,下面省略get set 构造方法。
    */
    class MovieLister{  
      private MovieFinder finder;
      
      public MovieLister() {
        finder = new MovieFinderImpl("movies1.txt");
      }
    
      public Movie[] moviesDirectedBy(String arg) {
          List allMovies = finder.findAll();
          for (Iterator it = allMovies.iterator(); it.hasNext();) {
              Movie movie = (Movie) it.next();
              if (!movie.getDirector().equals(arg)) it.remove();
          }
          return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
      }
    }
    
    

    这个组件如果是个人使用,完全是没有问题的。但是如果其他人觉得好用,而他们的电影列表存储方式和我不同的话,就得重新提供一个 MovieFinder的实现类。所以说最终这个组件最终会有多个MovieFinder的实现类,而我们希望MovieLister类能够与MovieFinder的任何实现类配合工作,并且允许在运行期插入具体的实现类,插入动作完全脱离插件作者的控制。这里的问题就是:如何设计这个连接过程,使MovieLister类在不知道实现类细节的前提下与其实例协同工作。生产环境和测试环境可能会选择不同的部署方案,也就可能选择不同的接口实现类。这些新兴的轻量级荣日就是帮我们将这些插件组装成一个应用程序。这是这种新型轻量级容器面临的主要问题之一,并且普遍使用控制反转来实现。

    控制反转

    所以有一个问题:它们反转了哪方面的控制?来举两个例子对比一下吧:

    1 普通方式:
    早期的用户界面(控制台)完全由应用程序来控制的,你预先设计一系列命令,例如输入身高、输入体重等,应用程序逐条输出提示信息,并取回用户的响应,最后显示结果。

    2 控制反转:
    图形用户界面环境下,UI框架将负责执行一个主循环,你的应用程序只需为屏幕的各个区域提供事件处理函数即可。

    看以看出图形用户界面下,程序的主控制权从程序移到了框架,也就是控制权发生了反转。 对于像Spring这样的轻量级容器,他的控制指的是确定接口的具体实现。 在上面那个demo中,MovieLister类负责确定MovieFinder的具体实现,然后MovieFinder也就不成其为一个插件了,因为它并不是在运行期插入应用程序中的。而这些轻量级容器则使用了更为灵活的办法,只要插件遵循一定的规则,一个独立的组装模块就能够将插件的具体实现注射到应用程序中。这个模式后来被称作"依赖注入"(Dependency Injection)。需要注意的是,为了消除程序和插件实现的依赖,除了DI还可以用ServiceLocater。

    DI的方式

    DI的思想简单来说就是有独立的类、装配类,装配类用于选择合适的接口实现,并填充对应的字段,如下图所示(引用的图):
    在这里插入图片描述
    下面将通过Spring举例三种依赖注入的方式,文件目录如下图,。
    在这里插入图片描述

    构造函数注入

    xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
        <bean id="movieLister" class="MovieLister">
            <constructor-arg ref="movieFinder"/>   写法不唯一,这里不展开了
        </bean>
    </beans>
    

    测试

    public class Application {
        public static void main(String[] args){
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
    
            MovieFinderImpl finder = applicationContext.getBean("movieFinder",MovieFinderImpl.class);
            System.out.println(finder.toString());
    
        }
    }
    

    结果
    在这里插入图片描述

    set注入

    xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
        <bean id="movieFinder" class="MovieFinderImpl">
            <property name="fileName" value="电影名单.txt"></property>    这里是set方法注入
        </bean>
        <bean id="movieLister" class="MovieLister">
            <constructor-arg ref="movieFinder"/>
        </bean>
    </beans>
    
    

    测试

    public class Application {
        public static void main(String[] args){
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
    
            MovieLister lister = applicationContext.getBean("movieLister",MovieLister.class);
            System.out.println(lister.toString());
    
        }
    }
    

    结果
    在这里插入图片描述

    接口注入

    查了不少博客,看到**从注入方式的使用上来说,接口注入是现在不提倡的一种方式,因为它强制被注入对象实现不必要的接口,带有侵入性。**为了演示,我们需要在实例代码中新增IMovieLister接口,并且让MovieLister实现它

    public interface IMovieLister {
        abstract MovieFinder createMovieFinder();
    }
    
    /**
    * 注意这里改成了抽象类。构造函数,get,set,toString方法省略
    */
    public abstract class MovieLister implements IMovieLister {
    
        private MovieFinder finder;
        
        public Movie[] moviesDirectedBy(String arg) {
            List allMovies = finder.findAll();
            for (Iterator it = allMovies.iterator(); it.hasNext(); ) {
                Movie movie = (Movie) it.next();
                if (!movie.getDirector().equals(arg)) it.remove();
            }
            return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
        }
        
        @Override
        public abstract MovieFinder createMovieFinder();
    }
    

    配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
        <bean id="movieFinder" class="MovieFinderImpl">
            <property name="fileName" value="电影名单.txt"></property>
        </bean>
        <bean id="movieLister" class="MovieLister">
            <lookup-method name="createMovieFinder" bean="movieFinder"></lookup-method>
            <constructor-arg ref="movieFinder"/>
        </bean>
    </beans>
    

    测试代码

    public class Application {
        public static void main(String[] args){
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
            MovieLister lister = applicationContext.getBean("movieLister",MovieLister.class);
            System.out.println("接口注入和构造方法注入比较: "+ (lister.createMovieFinder()==lister.getFinder()) );
            System.out.println(lister.toString());
        }
    }
    
    
    

    结果可以看出,两种方式注入的是同一个bean。
    在这里插入图片描述

    Service Locator

    依赖注入解决了MovieLister对MovieFinder实现类的依赖,这样MovieLister的作者就可以把工具分享给其他人,使用者可以根据自身的情况完成MovieFinder的实现类。打破这种依赖的方法不止一种,ServiceLocater也可以做到。

    Service Locator 基本思想就是有一个Service Locator ,他知道如何获取应用程序可能需要的所有服务。 当然这只是把问题转移了,我们仍然需要将Service Locator 放入MovieLister中,依赖关系如下:
    在这里插入图片描述

    简单例子

    
    public class MovieLister {
        private MovieFinder finder = ServiceLocator.MovieFinder();  //从ServiceLocator获取实现类
    
        public Movie[] moviesDirectedBy(String arg) {
            List allMovies = finder.findAll();
            for (Iterator it = allMovies.iterator(); it.hasNext(); ) {
                Movie movie = (Movie) it.next();
                if (!movie.getDirector().equals(arg)) it.remove();
            }
            return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
        }
    
        @Override
        public String toString() {
            return "MovieLister{" +
                    "finder=" + finder +
                    '}';
        }
    }
    
    public class ServiceLocator {
        private static ServiceLocator soleInstance;  
        private MovieFinder movieFinder;
    
        public static MovieFinder MovieFinder() {  //获取接口实例
            return soleInstance.getMovieFinder();
        }
    
        public MovieFinder getMovieFinder() {   
            return movieFinder;
        }
    
        public static void load(ServiceLocator serviceLocator) {  
            soleInstance = serviceLocator;
        }
    
        public ServiceLocator(MovieFinder movieFinder) {   
            this.movieFinder = movieFinder;
        }
    }
    

    测试代码

    public class Application {
        public static void main(String[] args){
            configureServiceLocator(); //当然也可以用配置文件读取配置
            MovieLister lister = new MovieLister();
            System.out.println(lister);
        }
    
        private static void configureServiceLocator() {
            ServiceLocator.load(new ServiceLocator(new MovieFinderImpl("movies1.txt")));
        }
    }
    

    运行结果
    在这里插入图片描述
    对于更复杂的场景,我们可以通过新增ServiceLocator的子类;可以通过调用实例的方法而不是直接获取实例中的属性;可以通过进程级别的存储实现进程级别的ServiceLocator。

    独立接口方式

    上文中我们只需要ServiceLocator的MovieFinder方法,但是却直接引入了ServiceLocator,我们可以通过定义接口来做好隔离。具体的代码如下:

    定义接口

    public interface MovieFinderLocator {
        MovieFinder MovieFinder();
    }
    

    接口实现

    public class ServiceLocator implements MovieFinderLocator...
        private static ServiceLocator soleInstance;
        private MovieFinder movieFinder;
        
        public  MovieFinder MovieFinder() {
            return soleInstance.getMovieFinder();
        }
        public static MovieFinderLocator locator() {
            return soleInstance;
        }
        public static void load(ServiceLocator serviceLocator) {
            soleInstance = serviceLocator;
        }
    

    赋值

    public class MovieLister...
        private MovieFinderLocator locator = ServiceLocator.locator();
        private MovieFinder finder = locator.MovieFinder();
    

    代码测试

    public class Application {
        public static void main(String[] args){
            configureServiceLocator();
            MovieLister lister = new MovieLister();
            System.out.println(lister);
        }
    
        private static void configureServiceLocator() {
            ServiceLocator.load(new ServiceLocator(new MovieFinderImpl("movies1.txt")));
        }
    }
    
    

    运行结果
    在这里插入图片描述

    动态ServiceLocator

    上面的例子中,你需要的每一个服务都是静态的定义好的,当然我们也可以采用HashMap动态地存取Service。

    class ServiceLocator...
      private static ServiceLocator soleInstance;
      private Map services = new HashMap();
      
      public static Object getService(String key){
          return soleInstance.services.get(key);
      }
      public void loadService (String key, Object service) {
          services.put(key, service);
      }
    

    配置

      private void configure() {
          ServiceLocator locator = new ServiceLocator();
          locator.loadService("MovieFinder", new MovieFinderImlp("movies1.txt"));
          ServiceLocator.load(locator);
      }
    

    获取

    class MovieLister...
      MovieFinder finder = (MovieFinder) ServiceLocator.getService("MovieFinder");
    

    模式选择

    DI / ServiceLocator

    这两个模式都提供了基本的解耦合能力,使得应用程序代码都不依赖于服务接口的具体实现。两者之间最重要的区别在于:具体实现以什么方式提供给应用程序代码。 使用Service Locator模式时,应用程序代码直接向ServiceLocator显式要求服务的实现;使用Dependency Injection模式时,应用程序代码不发出显式的请求,服务的实现被注入在应用程序中,发生了控制反转。

    人们倾向于使用DI模式的一个常见理由是:它简化了测试工作。这里的关键是:出于测试的需要,你必须能够轻松地在真实的服务实现与供测试用的伪组件之间切换。但是,如果单从这个角度来考虑,Dependency Injection模式和Service Locator模式其实并没有太大区别。

    在前面的例子中,作者要把 MovieLister 类交给朋友去用,这种情况下使用服务定位器就很好:朋友们只需要对定位器做一点配置,使其提供合适的服务实现就可以了。在这种情况下,我看不出 Dependency Injection 模式提供的控制反转有什么吸引人的地方。

    但是,如果把 MovieLister 看作一个组件,要将它提供给别人写的应用程序去使用,情况就不同了,作者无法预测使用者会使用什么样的服务定位器API,每个使用者都可能有自己的服务定位器,而且彼此之间无法兼容。一种解决办法是为每项服务提供单独的接口,使用者可以编写一个适配器,让我的接口与他们的服务定位器相配合。但即便如此,我仍然需要到服务定位器中寻找我规定的接口。而且一旦用上了适配器,ServiceLocator所提供的简单性就被大大削弱了。

    所以,主要的问题在于:代码的作者是否希望自己编写的组件能够脱离自己的控制、被使用在另一个应用程序中。如果答案是肯定的,那么他就不要对服务定位器做任何假设——哪怕最小的假设也会给使用者带来麻烦。

    构造器注入 / set方法注入

    上文提到过,接口注入的侵略性比较强,比起Service Locator模式的优势也不那么明显。所以,我们一般选择构造器注入或者set方法注入,下面将仔细对比这两种方法。

    构造函数初始化的另一个好处是:你可以隐藏任何不可变的字段——只要不为它提供set方法就行了。如果某个字段是不应该被改变的,没有针对该字段的set方法就很清楚地说明了这一点。如果通过set方法完成初始化,暴露出来的设值方法很可能会造成一些问题。

    1. 如果参数太多,构造函数会显得凌乱不堪。如果有不止一种的方式可以构造一个合法的对象,也很难通过构造函数描述这一信息,因为构造函数之间只能通过参数的个数和类型加以区分(Factory Method模式可以解决)。

    2. 如果要传入的参数是像字符串这样的简单类型,构造函数注入也会带来一些麻烦。使用设值方法注入时,你可以在每个设值方法的名字中说明参数的用途;而使用构造函数注入时,你只能靠参数的位置来决定每个参数的作用,而记住参数的正确位置显然要困难得多。

    3. 如果对象有多个构造函数,对象之间又存在继承关系,事情就会变得特别讨厌。为了让所有东西都正确地初始化,你必须将对子类构造函数的调用转发给超类的构造函数,然后处理自己的参数。这可能造成构造函数规模的进一步膨胀。

    尽管有这些缺陷,但仍然建议你首先考虑构造函数注入。不过,一旦面对上面提到的问题,你就应该准备转为使用set方法注入。

    代码/配置文件

    有些时候直接在程序代码中实现装配会更简单。譬如一个简单的应用程序,也没有很多部署上的变化,这时用几句代码来配置就比XML 文件要清晰得多。

    有时应用程序的组装非常复杂,涉及大量的条件步骤。一旦编程语言中的配置逻辑开始变得复杂,你就应该用一种合适的语言来描述配置信息,使程序逻辑变得更清晰。然后,你可以编写一个构造器类来完成装配工作。如果使用构造器的情景不止一种,你可以提供多个构造器类,然后通过一个简单的配置文件在它们之间选择。

    总结

    在时下流行的轻量级容器都使用了一个共同的模式来组装应用程序所需的服务,称为Dependency Injection,它可以有效地替代Service Locator模式。在开发应用时,两者各有千秋,Service Locator模式的行为方式更为直观。但是,如果你开发的组件要交给多个应用程序去使用,那么Dependency Injection模式会是更好的选择。

    如果你决定使用Dependency Injection模式,建议你首先考虑构造函数注入;如果遇到了某些特定的问题,再改用设值方法注入。如果你要选择一个容器,在其之上进行开发,我建议你选择同时支持这两种注入方式的容器。

    学习资源

    Inversion of Control Containers and the Dependency Injection pattern
    跟我学Sprig
    Spring的依赖注入(接口注入)

    展开全文
  • NULL 博文链接:https://slnddd.iteye.com/blog/1756411
  • 一、Spring概述: Spring是一个开源框架,其存在的根本使命就是简化JAVA开发。为了降低JAVA开发的复杂性,Spring... (1) IOC: 控制反转,把创建对象过程交给Spring进行管理; (2) AOP: 面向切面,不修改源代码,...

    一、Spring概述:

            Spring是一个开源框架,其存在的根本使命就是简化JAVA开发。为了降低JAVA开发的复杂性,Spring采取了以下四种关键策略:

    • 基于POJO的最轻量级和最小侵入性编程;
    • 通过依赖注入和面向接口实现松耦合;
    • 基于切面和惯例进行声明式编程
    • 通过切面和模板减少样板式代码。

    1.1 Spring有两个核心部分:IOC和AOP:

        (1) IOC: 控制反转,把创建对象过程交给Spring进行管理;

        (2) AOP: 面向切面,不修改源代码,进行功能增强。

    1.2 Spring特点:

    1. 方便解耦,简化开发;(IOC)
    2. AOP编程支持;
    3. 方便程序测试;
    4. 方便和其他框架进行整合;
    5. 方便进行事务操作;
    6. 降低API开发难度。

    1.3  Spring组成

            Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 。

            组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

    • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

    • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

    • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。

    • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

    • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

    • Spring MVC 框架MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

    二、IOC容器:

            控制反转,就是将对象创建和对象之间的调用过程,交给Spring进行管理,使用IOC的目的就是降低耦合度。

            ​​​​​​​控制反转通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

    2.1  spring示例:

    创建一个普通类:

    public class User {
        private String name;
        private String password;
    
        public void setName(String name) {
            this.name = name;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public String getName() {
            return name;
        }
        public String getPassword() {
            return password;
        }
        @Override
        public String toString(){
            return "name:"+name+" ,"+"password:"+password;
        }
    }

    创建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">
    
        <bean id="user" class="com.wyf.beantest.User">
            <property name="name" value="wyf"></property>
            <property name="password" value="pwd"></property>
        </bean>
    </beans>

    编写测试代码:

        @Test
        public void userTest(){
            //加载spring配置文件
            ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
            User user = context.getBean("user",User.class);
            System.out.println(user.toString());
        }

    上例是一个IOC控制反转的例子,其中:

    • user对象是由Spring创建
    • user对象的属性也是由spring容器进行设置

    以上过程充分体现了控制反转,也是spring思想的精髓 : 

    • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring进行创建。

    • 反转 : 程序本身不创建对象 , 而变成被动的接收对象

    2.2 IOC底层原理:

    IOC的底层主要用到了三个技术,分别是:

    • XML解析
    • 工厂模式
    • 反射

    2.2.1 工厂模式解耦 

          如下图,我们有两个类,分别为UserService类(包含方法execute)和UserDao(包含方法add),我们在UserService类中调用UserDao类的add方法,传统方法使用new的方式,在UserService类中创建UserDao对象,然后调用UserDao的add方法。这种方式虽然能够达到目的,但是UserService类和UserDao类的关联度太高了,也就是耦合度太高了。UserDao类的改变会导致UserService类也必须做出相应的变化。

             针对传统模式存在的耦合度高的问题,我们可以采用工厂模式,一定程度上降低耦合,如下图所示。我们依然存在两个类,UserService类(包含方法execute)和UserDao(包含方法add),同样在UserService类中调用UserDao的add方法,采用传统方式,耦合度太高。我们可以创建一个工厂类UserFactory,该类中包含一个返回值为UserDao的静态方法,当UserService需要UserDao时,可以通过调用工厂类UserFactory中的方法获得,这样,我们就降低了UserService类和UserDao类之间的耦合度。这种方式虽然将UserService和UserDao进行了解耦,但是,也存在新的问题,就是将UserService和UserFactory进行了耦合,实际中不可能消除耦合,我们最终的目的是将耦合度降到最低。该例还可以继续降低耦合,就是IOC控制反转。

     2.2.2 IOC解耦

            IOC的解耦过程如下所示: 

             IOC进行解耦时,第一步我们需要在xml配置文件中配置创建的对象(如上例中所示的配置文件),第二步,当我们需要某个配置文件中的类时,我们可以创建一个工厂类,在工厂类中我们首先通过解析XML文件,获取类的配置信息,然后通过反射创建并获取到这个类。

    2.3 IOC接口:

             IOC思想基于IOC容器完成,IOC容器底层(本质上)就是对象工厂。spring提供了两种方式IOC容器的实现(两个接口):

    • BeanFactory接口:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用 。 * 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象 
    • ApplicationContext接口:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。* 加载配置文件时候就会把在配置文件对象进行创建 

    ApplicationContext接口的实现类:

    三、结束

            本文简单介绍了spring框架,并简单介绍了IOC控制反转的底层原理,其主要基于XML解析工厂模式以及反射实现。

            

    展开全文
  • Spring依赖注入和控制反转

    千次阅读 2022-04-30 13:45:14
    学习依赖注入和控制反转的概念,然后借助代码示例了解 Spring 框架如何支持它们。 控制反转 在我们开始做任何事情之前,让我们先了解一下什么是控制反转控制反转是面向对象编程中使用的术语,通过该术语,对象...
  • NULL 博文链接:https://ysj5125094.iteye.com/blog/1606928
  • Spring之控制反转

    2022-06-08 17:06:04
    控制反转(Inversion of Control,缩写为IoC),是面向对象编程的一种设计原则,可以用来减低计算机代码之间的耦合度。​ 常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫依赖查找。​ ...
  • 火龙果软件工程技术中心 本文内容包括:使用值对象作为方法参数使用接口改善这种情况例子:定价和交货期工厂方法接口和方法不匹配另一个例子结束语控制反转(IoC)模式通常用于组件。本文描述了如何对方法签名使用该...
  • 浅谈控制反转(IoC)

    千次阅读 2022-03-10 20:16:00
    什么是控制反转控制反转是指程序的流程控制权相对于传统的面向过程编程而言发生了反转。下面是维基百科的描述 In software engineering, inversion of control (IoC) is a programming principle. IoC inverts ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 159,864
精华内容 63,945
关键字:

控制反转