Spring基础

Spring 基础

学完Spring有大半年了,但是一直没怎么用,复习一下。

Spring是什么?

这个问题,应该是每个人都有每个人的答案,难道仅仅说他就是一个框架?好像不是很对,因为我觉得它不仅仅是一个框架,应该说是一种架构的模式。为了解决程序之间的依赖,简化编程,快速开发具有高拓展性的工程所用的一种的模式。

一个Demo引出的问题

一个很简单的sayhello()的功能:


public class User {
    private String name;
    public void setName(String name){
        this.name=name;
    }
    public void sayHello(){
        System.out.println("Hello:"+name);
    }

}

这是传入名字,输出名字:hello.
主函数测试:

public class Main {
    public static void main(String[] args) {
        User u=new User();
        u.setName("???");
        u.sayHello();
     }
}

这样其实程序不具有可拓展性,因为不能动态的设定name。
如何动态的设定name?

配置文件解决

解决动态的修改名字这个问题,可以采用属性配置文件,新建一个name.properties:
里面填上对应的属性:

name="???"

这样可以通过每次读取配置文件获取信息:


public class Main {
    public static void main(String[] args) {
        User u=new User();
        String name="";
        Properties prop=new Properties();
        try {
            prop.load(Main.class.getClassLoader().getResourceAsStream("name.properties"));
            name=prop.getProperty("name");
        }catch (Exception e){
            e.printStackTrace();
        }
        u.setName(name);
        u.sayHello();
       }
}

但是配置文件只能解决一些简单的问题,一旦涉及到类与类之间的依赖,这种配置文件就很难表示。

问题的本质

究其原因:
(1)首先程序之所以需要配置文件是因为在程序启动时,需要的参数值是不确定的,等到确定的时候,程序已经启动了,或者说在运行了。这样怎么把参数给设定一下呢?
(2)程序的依赖关系,其实指的是当前类需要调用另外一个类的方法,这个被调用的类在程序启动之前也是不确定的,或者说在程序运行期间一直是变化的,这样如何在程序变化的时候动态的修改他们的依赖关系?
这两个问题本质上是一个问题。第一种是数据依赖,第二种是类之间的依赖,本质上来说都是一种问题,为了解决这种依赖关系,大牛们提出了解决方案,并且发明了Spring框架。Spring框架能够很好的解决这个问题,如果说Spring的核心任务,我个人认为就是解耦和

IOC

IOC(Inversion of Control).控制反转,值得是在类之间存在依赖关系时,依赖抽象非具体的类。下面通过一段简单的Spring代码来说明IOC。
Disk这个设备大家很常用,比如简单的就是U盘,还有可移动硬盘等等。电脑通过USB接口和这些外设经行交互,那么电脑对这个接口具有依赖性。显然,电脑只能依赖于一个抽象的接口,不能依赖具体的接口,很容易想到的一个问题,如果依赖U盘实现的接口,那很多其他的外设就无法识别。
下面基于这个场景来用Spring实现。
首先是抽象接口:

package com.spring.TestUSB;
// 只有两个方法,读写。
public interface Disk {
    public void read();
    public  void write();
}

U盘的实现:

package com.spring.TestUSB;

public class Udisk implements Disk{

    @Override
    public void read() {
        System.out.println("read form Udisk");
    }
    @Override
    public void write() {
        System.out.println("Wirte to Udisk");
    }
}

可移动硬盘的实现

package com.spring.TestUSB;

public class MoveDisk implements Disk {

    @Override
    public void read() {
        System.out.println("read from MoveDisk");
    }

    @Override
    public void write() {
        System.out.println("Write to MoveDisk");
    }
}

然后是computer。computer肯定要有一个USB的对象作为自己类的成员,然后调用这个成员的方法。

package com.spring.TestUSB;
public class Computer {
    private Disk d;
    public Disk getDisk(){
        return d;
    }
    public void setDisk(Disk d){
        this.d=d;
    }
    public void test(){
        d.read();
        d.write();
    }
}

r然后是主函数:

package com.spring.TestUSB;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
       Computer com=new Computer();
       Udisk u=new Udisk();
       com.setDisk(u);
       com.test();
    }

}

这样写对吗?显然是错的,因为这在编译期就指定了数据,是无法做到动态修改的。
所以就到了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">
    <bean id="Udisk" class="com.spring.TestUSB.Udisk" />
    <bean id="MoveDisk" class="com.spring.TestUSB.MoveDisk"/>
    
    <bean id="Computer" class="com.spring.TestUSB.Computer">
        <property name="disk" ref="MoveDisk"></property>
    </bean>
</beans>

简答的解读一下这个配置文件,首先所有的类都是bean。然后类的成员变量可以通过property指定,name指的是这个成员变量的名字,ref指的是这个变量的类型。通过这种文件,可以看出来,参数是可以动态的修改的。那么主函数怎么写呢?

package com.spring.TestUSB;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
   public static void main(String[] args) {
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
       Computer com= (Computer) applicationContext.getBean("Computer");
       com.test();
   }

}

可以看出,这个主函数里面没有直接new Computer的对象,直接通过Application对象拿到对象。

这样做的好处

可以看到,这样做之后,参数是可以动态的修改,类直接的依赖关系也是可以通过配置文件指定。也不用自己创建对象。接下来讨论这些机制,首先参数可以动态修改,这个叫做动态注入参数,也叫做DI,依赖反转。不用自己创建类,类是由Spring创建完成,Spring来创建我们需要调用的类,所以把类的创建权交给了Spring,这就是IOC,控制反转。

BeanFactory,IOC容器的理解

什么是容器?在Spring里面可以理解就是管理,存放所有的bean的地方。可以看到,上面得到类的实例,调用的是Application对象,其实Application是Beanfactoy的一个实现。BeanFactory字面意思就是Bean工厂,所有的Bean都是可以通过这里产生的,所以上面的主函数也是可以这样写:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class Main {
    public static void main(String[] args) {
        Resource r=new ClassPathResource("applicationContext.xml");
        BeanFactory factory=new XmlBeanFactory(r);
        Computer com= (Computer) factory.getBean("Computer");
        com.test();
    }

}

因为Application比较好用,所以一般都是用这个

Spring IOC

上面说到,把创建Beans的权利交给了Spring,我们自己不再需要new对象,全部都是Spring帮助我们完成,那么Spring是怎么完成的呢?
其实Spring也是通过调用类的的构造方法完成的。
比如有个people类:


public class People {
   private String name;
   private int age;

   public People(){
       System.out.println("?????");
   };
   public String getName() {
       return name;
   }

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

   public int getAge() {
       return age;
   }

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

}

在默认构造里打印一点东西,然后在配置文件中配置一下,就可的得到:
在这里插入图片描述可以看到,控制台有输出。
并且需要注意的是:Spring调用的默认构造函数来构造对象,如果写了其他构造函数,记得把默认构造加上,否则:
如果是People是这种样子,忘记了某人构造函数:

package BeanFactoryTest;

public class People {
    private String name;
    private int age;

//    public People(){
//        System.out.println("?????");
//    };
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

}

Test一下就会报错:
在这里插入图片描述
显示没有默认构造函数。

Spring 构造出来的对象默认是单例

这就涉及到Spring的作用域了,简单说一点就是Spring构造出来的对象默认都是单例,可以验证:
在这里插入图片描述可以发现打印出来的是true,如果想要多例,在配置文件中修改:
在这里插入图片描述

构建对象的几种方法

除了上面Spring直接使用默认构造函数构造对象,还可以自己指定构造方法,基本上还有其他两种方法

静态方法

在People中加入静态方法,返回该类的对象,其他代码不变
在这里插入图片描述然后在配置文件中指定这个方法:
在这里插入图片描述这个方法也叫静态工厂方法,所以命名被命名factory-method.这个方法其实很常见,是为了返回一个指定的,可控制的对象。有关静态工厂方法,用处很多,详细介绍在Effective JAVA中第一章有详细的介绍,有兴趣的可以去看看。

工厂方法

这个方法采用外部工厂的构建对象,首先新建一个PeopleFactory的类,作为用来构建这个类的工厂:

package BeanFactoryTest;

public class PeopleFactory {
    public  People Create(){
        System.out.println("call factory method!");
        return new People();
    }
}

r然后在配置文件中,首先配置这个工厂:
在这里插入图片描述 完成之后配置该方法,将构造方法指向这个工厂提供的方法:
在这里插入图片描述这样也可以完成构造。

参数注入的方法

// tried,算了,这个比较简单,就是通过配置文件来注入参数,不过分为几种不同类型的参数注入,但是都不难,鸽了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值