天天看点

spring系统学习之控制反转 ioc

  这学期的课程安排,我们可以系统的学习spring框架了。  相比较自己看博客笔记,系统的在课堂上学习还是很有必要。

  spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。 框架的主要优势之一就是利用其分层架构。 分层架构允许选择使用哪一个组件,同时为了J2EE 应用程序开发提供集成的框架。

  spring 的核心是 IOC,  Inverse of control, 控制反转,  和 AOP  , Aspect oriented programming,围绕切面的编程。

  第一步:

    导入maven包:

spring系统学习之控制反转 ioc

       spring含有7个逻辑组件,我们有必要对每个组件的作用进行相应的了解,因为只有了解了每个组件的作用以及它相关的依赖关系,我们在使用时才能左到心里有数,才算得上“学习”吧。

       我在早些时间对spring的4个包进行了源码的遍历,虽然意义不大,但是纠正了我的一些误区。  比如  :  spring-beans,提供了很多的功能,但是它并不是spring提出的逻辑组件。  就像默默无闻的科技工作者一下,虽不被人所知,但是发挥着它强有力的作用!   此外,spring-beans 位于spring-aop逻辑组件中。  但是spring-aop即作为了一个逻辑组件的名称,同时也是一个包。  可谓双赢。  就像 李彦宏,马克扎克伯格之类。  同时spring-context也是被自己遍历过,所以它里面的每个类我都应该不是第一次照面。

         先看书本的理论:   spring-context即spring上下文是一个配置文件,向spring框架提供上下文信息。  spring上下文包括企业服务,如: JNDI,EJB,电子邮件,国际化,检验和调度。 

        理解这个概念的关键在于一个词语:  上下文!   何谓上下文?  

        有道释义:context: 上下文,语境。     

         理解上下文的含义对于初次接触的人来说并不轻松。   先说自己所接触过的上下文吧:   在学习javaweb的时候,首次听说了上下文的概念---->  之后接触到了spring框架,再次接触到了 上下文的说法 ------> 直到最后学习了安卓,又接触到了上下文,并且在里面摸爬滚打了好一阵。   总算是对上下文有一些感性上的认识。

         就我的理解来看,我们直到一个程序要被执行,它需要经过这样一些流程:  源代码(二进制串,被保存在硬盘上)--->被加载进内存--> 运行。  当然它实际上还经历了很多复杂的过程如链接资源文件,内存重定位,权限验证等等。广范一点来说,上下文就是整个程序二进制串在内存中的长度所代表的所有内容。  狭义一点的说,它是程序使用者指定的当前程序的某一段内存长度。(不一定是连贯的,但是它们在逻辑一般具有连贯关系)。   上下文就是这样的一种存在,能够在你当前的环境(进程中)得到你想要得到的。   

         对上下文最简单的一种实现: public static final Map context = new HashMap();  只要它能满足: 单例,生命周期跟进程一样长。  那么他就是当前程序的上下文了。上下文的本质是内存。

          继续回到练习:当我们导入改包了之后,它提供给我们的包结构有:

spring系统学习之控制反转 ioc

         其中属于core逻辑组件的是:  core,logging;  属于aop组件的是:core,beans,aop;  属于expression组件的是:core,beans,aop,expression,context。  总之,就是如果我们用的产品,都要依赖逻辑组件: spring-core,spring-aop,spring-context。 也就是要包含这里的依赖。  当然,如果我们是要基于spring来开发产品,那么依赖可能就会另算。

             ok,那还有个问题,就是我们为什么必需得用到上下文的时候才能IOC呢?

              通过学习的理论知识我们知道,spring提供容器的接口是: BeanFactory,它位于核心组件: spring-aop下,(spring-beans包下)但是我们都知道,接口不能被我们实例化。  或者说只能以全实现的方式实现一个接口,那样的话接口存在的意义就不大了。    我对接口的理解就是,它具有发散性,抽闲类具有内聚性,当我们设计的时候,判断某个逻辑块将来具有发散的趋势就用接口,具有内聚的趋势就用抽象类。   因此,这个BeanFactory不能被我们直接使用,或者不能被我们方便的使用。因此我们需要寻找它的实现类。

             在spring-context中就提供了两个它的实现:

spring系统学习之控制反转 ioc
spring系统学习之控制反转 ioc

   但是它实际应该是有三个实现的,还有一个是XmlWebApplicationContext。  它是与web项目紧密结合的,因此它的实现位于:

spring系统学习之控制反转 ioc

     这里顺便提一下,web项目本身有一个上下文。  是由服务器容器提供的,spring-web核心组件提供了相关的api使得这两个容器在某种程度上能够共享。  应该是共享而不是拷贝,因为无论从性能方面还是设计方面拷贝都不占什么优势,只是在多线程并发上面可能有一点好处。

    嗯嗯 ,它的三个实现类我知道的都写在上面了。  但是去看Beanfactory的实现结构的时候,发现还是有一些非抽象类也实现了它,或许也可以用吧。  等以后去探究探究。

 ----------------------

   到目前为止,我们已经实现了环境的准备。下面就开始使用吧。

   先看ClassPathXmlApplicationContext的用法:

spring系统学习之控制反转 ioc

  它可以通过指定一个父类上下文(相当于父类上下文的一个子类,类似于拷贝吧),单个配置文件,多个配置文件,以及单个配置文件的路径或者多个配置文件的路径。  嗯嗯算是所有的使用方式了呢!

    再看看FileSystemXmlApplicationContext的用法:

spring系统学习之控制反转 ioc

     它能够通过父类上下文,单个配置文件,多个配置文件来进行配置。  它们的使用还是大同小异的。

     我本次练习使用的是ClassPath的实现。

   先定义好一个实体类:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 9:44
 */
public class User {

    private Long id;
    private String username;

    public User() {
    }

    public User(Long id, String username) {
        this.id = id;
        this.username = username;
    }
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}      

    然后建一个配置文件:配置文件的名称随意。  但是必须引入shema的xml约束。  写法再spirng.io可以查到。 但是其实不写这个shema应该也是可以,只是sping官方强制要我们写,不然报错。  因为这样可以提高命中率,不会由于你的xml结果的问题而导致程序异常退出。  因为游戏规则是别人制定的,并且遵守规则对大家都有好处,因此我们按照它的shema来就是。

<?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.automannn.practice.entity.User" />
    
</beans>      

最后就是使用了,最简单的就是新建一个还有main入口方法的函数:

package com.automannn.practice;


import com.automannn.practice.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * @author [email protected]
 * @time 2018/9/19 9:48
 */
public class Main {
    public static void main(String[] args) {
        ApplicationContext context=null;
        //推荐使用
        context= new ClassPathXmlApplicationContext("beans.xml");
        //String[] configs = {"beans1.xml","beans2.xml","beans3.xml"};
        //context = new ClassPathXmlApplicationContext(configs);

        //推荐用于 使用spring 框架的独立的应用程序种
        //context = new FileSystemXmlApplicationContext("配置文件的全路径");
        //String[] configss = {"path1","path2","path3"};
        //context = new FileSystemXmlApplicationContext(configss);

        //spring-web 提供的一个用于 web工程量身定制的一个实现类
        //该静态方法可支持 在jsp 和servlet  种 根据 ServletContext 取得 IOC容器的引用
        //context = WebApplicationContextUtils.getRequiredWebApplicationContext(null);

        User u = (User) context.getBean("user");
        System.out.println(u.toString());

        

    }
}      

最后一步,就是将程序启动起来了:

spring系统学习之控制反转 ioc

   通过截图,我们发现虽然打印了出来我们要的信息,但是这几乎不是我们要的结果。   因为我们用的时候都是:   new User("属性1","属性2");  或者:User u=  new User();  u.setShuxing1("属性1");  u.setShuxing2("属性2");  因此我们有必要了解bean 容器配置文件的相关用法。

  一,bean工厂获得实例的方法:

          1,   使用构造器的方法:

spring系统学习之控制反转 ioc

           2,使用静态工厂实例化的方法:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 10:19
 */
public class UserFactory {
    private static final User user =new User(1L,"小二郎");

    private UserFactory(){}

    public static User getInstance(){
        return user;
    }
    
}      
spring系统学习之控制反转 ioc

      3,使用实例工厂实例化的方法:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 10:19
 */
public class UserFactory {
    private static final User user =new User(1L,"小二郎");

    private UserFactory(){}

    public static User getInstance(){
        return user;
    }

    public User getUser(){
        return  new User(2L,"李四");
    }
}      
spring系统学习之控制反转 ioc

二,依赖注入的配置方法:

        1,property的方式:

spring系统学习之控制反转 ioc

          2,有参构造方法的方式:

spring系统学习之控制反转 ioc
spring系统学习之控制反转 ioc

   当参数类型冲突的时候,需将二者结合使用。

         3.autowire,自动装配的方式:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/10/9 12:42
 */
public class UserComponent {
    User user;

    public User getUser() {
        return user;
    }

    @Override
    public String toString() {
        return user.toString();
    }
}      
spring系统学习之控制反转 ioc

   默认的自动注入的方式是按类型。  因此当我们有多个接口的实现类并且以接口的方式注入的话,就不能用@Autowire,而用@Resource(name="").

   当然这里说到了注解,那么注解又是spring框架提供给我们的另一个强大功能!也就是基于注解的spring容器。

基于注解的spring容器的学习:

     在学习之前我们有必要回顾一些历史:

           java的注解与1.5引入,时间在2004年前后,同时,java1.5之后,Java就成了一门成熟的开发语言,算是正式在语言市场确定自己江湖霸主的地位。虽不能算上武林盟主,但怎么也能算的是华山派啊,少林派啊这等有头有脸的大门派了。

           也就是04年左右,spring框架势如破竹,在江湖上展露头角,并且颇受大宗族的喜爱。一路势如破竹,遂成为现今武林世界不容小觑的一股力量!  当然14年左右springboot也展露了头角,它的未来必定又是一方雄霸。

           因此,spring也好,springboot也好,它们的发展都是与java的脉络发展息息相关的。  同时我们有必要知道,注解并不是spring所带来的功能,而是Java自身的一个功能。 理解这点对我们学习很有必要!

     下面就正式进入注解的学习:

        为什么使用注解?  因为它很好的满足了我们程序开发的两个要求:高内聚,低耦合。就是说,我们希望开发的时候,与一个逻辑相关的所有代码都尽量的放在一起,越紧密越方便自己找到,就能更轻松的实现业务逻辑;但是对于逻辑联系不强烈,或者说将来有很大可能性要修改的逻辑块,我希望它们就最好一点关系都没有,将来修改的时候不会导致牵一发而动全身!

        注解本身不给程序改变,它只是提供了一种新型的方式编程。   就spring的ioc注解而言,它的逻辑任然是一样的,它主要就是以注解去代替了xml配置文件!   注意是代替,所以它们的逻辑功能是一毛一样的。 因为当配置文件大起来,多起来的时候,配合复杂的业务逻辑,足够让我们感到烦躁了!

        bean容器的注解,实际上就一个: @Component

        但是它派生了一些:@Controller,@Service,@Repository

        自动装配的注解:  @Autowire;  此外要注意,自动装配的方式是通过setter访问器写入的,而非使用直接使用反射的方式去设置属性。  大概是因为开销的原因吧。  因此,当我们用@Resource(name="")的时候,即可以写在属性上面(前提是必须是非静态属性,并且有标准的setter访问器),也可以写在setter访问器上的原因。

        使用注解的步骤: 

             spring1.x,需要我们手动的打开注解的功能: 

<context:annotation-config/>      

        配置扫描的包路径:

<context:component-scan base-package="com.automannn.practice"/>      

   如果是spirng2.x及以后的版本,则只需要指定扫描的包路径就可以了。

----最后总结一下,spring容器的使用:

一,基于配置文件

        1,引入spirng-context的依赖

        2,定义一个实体

        3,新建一个配置文件

        4,使用spring容器

二,基于注解:

         1,开启注解扫描的功能