JavaWeb学习笔记-part8-Spring(上)

Spring

目录

Spring

1 总体概述

2 IoC控制反转

2.1 对IoC及Spring的概念解释和实现原理

2.2 Spring的第一个程序

2.3 基于XML(配置文件)的DI

2.4 基于注解的DI

2.5 XML和注解该使用哪种方式

2.6 一个小技巧

1 总体概述

出现在2002左右,解决企业开发难度,减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系;能实现模块之间,类之间的解耦合。

  1. spring核心技术:ioc,aop。

  2. 依赖:classA中使用classB的属性和方法,叫做classA依赖classB

  3. 优点:

    • 轻量:spring框架使用jar比较少,核心功能所需jar总共3m左右;运行占用资源少,运行效率高。不依赖其他jar

    • 针对接口编程,解耦合:IOC

    • AOP编程的支持:AOP

    • 方便集成各种优秀框架

  4. Spring体系结构:

    JavaWeb学习笔记-part8-Spring(上)_第1张图片 

2 IoC控制反转

2.1 对IoC及Spring的概念解释和实现原理

IoC(Inversion of Control):控制反转,是一个理论,概念,思想。

作用是:把对象的创建,赋值,管理工作交给代码之外的容器实现,也就是对象的创建是由其他外部资源完成

对几个关键词的描述:

  • 控制:创建对象,对象的属性赋值,对象之间的关系管理。

  • 反转:把原来开发人员管理,创建对象的权限转移给代码之外的容器实现,由容器代替开发人员管理对象。创建对象,给属性赋值。

  • 正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象

  • 容器:是一个服务器软件,一个框架(Spring)

为什么使用IoC:目的就是减少代码的改动,也能实现不同的功能,实现解耦合

IoC的体现:

  • Servlet:

    1. 创建类继承HttpServlet。

    2. 在web.xml注册servlet。

    3. 没有自己创建Servlet对象。

    4. Servlet对象是Tomcat服务器为你创建并管理的。

      Tomcat也称为容器,其中存放着Servlet对象,Listener,Filter对象。

DI(Dependency Injection):

  • DI是IoC的技术实现,即依赖注入。

  • 只需要在程序中提供需要使用的对象名称就可以了,致于对象如何在容器中创建,赋值,茶轴都由容器内部实现

  • Spring使用DI实现了IoC的功能,spring底层创建对象,使用的反射机制。

2.2 Spring的第一个程序

2.2.1 创建maven项目

在学习过程中,我们只需要最基础的部分,因此选择quickstart模板作为maven项目来创建

JavaWeb学习笔记-part8-Spring(上)_第2张图片 

2.2.2 加入maven的依赖

加入Spring的依赖和junit依赖。


        junit
        junit
        4.11
        test
    
    
    
    
        org.springframework
        spring-context
        5.2.5.RELEASE
    

2.2.3 创建类(接口和实现类)

此处我创建了一个名为OneService的接口及其实现类OneServiceImpl。

package com.pjh.service;
​
public interface OneService {
    void doSome();
}
​
//以上接口,分割线,以下实现类
​
package com.pjh.service.impl;
​
import com.pjh.service.OneService;
​
public class OneServiceImpl implements OneService {
    @Override
    public void doSome() {
        System.out.println("doSome已执行...");
    }
}

2.2.4 创建spring需要使用的配置文件

在配置文件中声明类的信息,这些类由spring创建和管理。

spring配置文件应当创建在resource目录下

这里提供一个标准spring配置文件模板(注解内容非必须)(XML)



然后我们需要在配置文件中声明java对象



    
    
    
    

2.2.5 测试spring创建对象

在你的测试类中:

@Test
public void testDoSomeBySpring() {
    //使用spring容器创建的对象
    //1. 指定spring配置文件的名称
    String config = "beans.xml";
    //2. 创建表示spring容器的对象,ApplicationContext
        //ClassPathXmlApplicationContext: 表示从类路径中加载spring的配置文件
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    //3. 从容器中获取某个对象,需要调用getBean方法
        //传入配置文件中的id
    OneService one = (OneService) ac.getBean("oneService");
    //4. 使用创建好的对象
    one.doSome();
}

2.2.6 一些思考

对象是在什么时机创建的?

在执行加载spring的配置文件时创建的,见2.2.5 第8行代码。且会创建配置文件中声明的所有对象。

如何获取容器中对象的信息?

  1. 获取对象数量信息:

    调用getBeanDefinitionCount()方法,返回对象的数量。

  2. 获取所有对象的名称(key):

    调用getBeanDefinitionNames()方法,返回一个字符串数组。

spring能创建非自定义的对象吗?

可以,只需要见类的全限定名称书写正确,就可以创建任意指定的类对象。

2.3 基于XML(配置文件)的DI

即通过配置文件对对象赋值。

DI:依赖注入,表示创建对象,给属性赋值。

其实现语法有两种:

  1. 在spring配置文件中,通过标签和属性完成,叫做基于XML的DI实现(见本章2.3)

  2. 使用spring中的注解,完成属性赋值,叫做基于注解的DI实现(见2.4)

2.3.1 DI的语法分类

  1. set注入:spring调用类的set方法,在set方法可以实现属性的赋值,比较常用

  2. 构造注入:spring调用类的有参数构造方法,创建对象。在构造方法中完成赋值

    
    
        
        
        
        
            
            
        
        
        
            
            
            
        
        
        
            
            
        
    ​
        
            
            
            
        
    

    注意:

    1. set方法必须符合命名规范

    2. 即使没有属性,只要存在setXXX的方法,就可以通过set注入调用

    3. 必须存在set方法,否则无法执行

    4. set注入实际上只是调用了set方法,即使不进行赋值

    5. Spring会先执行无参构造方法,再执行set注入

    6. 在set注入时,所有的值(value)不管是数字还是其他都必须放在引号中,这是XML规范所限制的

    7. 在spring里创建对象无需在意顺序,原理似乎是二次扫描可以保证每个对象都被正确的创建

    8. 为对象赋值时也无需在意赋值顺序

2.3.2 引用类型属性自动注入

  1. byName属性:

    java类中引用类型的属性名和spring容器中(配置文件)的id名称一致,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。

    
        
        
    
        
        
    

  2. byType:java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系,这样的bean可以赋值给引用类型。 同源就是一类的意思:

    1. java类中引用类型的数据类型和class类型的值是一样的

    2. java类中引用类型的数据类型和class类型的值是父子关系

    3. java类中引用类型的数据类型和class的值接口和实现类关系

    语法:

    
        
        
    
    
    
        
        
    

    注意:

    符合条件的对象仅能存在一个,出现多个符合的bean会报错

2.3.3 为什么要使用多个配置文件

  1. 便于管理:

    当项目较大时,往往存在成百上千个对象,如果全部存在一个配置文件里,不仅难以查找修改,文件的大小也会变得十分庞大,打开配置文件和保存配置文件时都会变慢。

  2. 便于多人协同开发:

    避免在多人合作开发时,出现错误覆盖、模块功能冲突等情况。

  3. 因此,在开发大型项目时,可以采用一个模块一个配置文件的做法,不仅可以使配置文件小很多、提高效率,还可以让负责该模块的开发人员不与其他人员产生冲突。

2.3.4 配置文件的包含关系(套娃)

在上一小节中我们讨论了为什么要使用多个配置文件,这一节我们将学习如何管理多个配置文件,于是这里涉及到了关于配置文件的包含关系。

注意:以下代码都仅保留了关键代码作为示例

当前存在两个模块Student模块和School模块,且分别拥有spring_student和spring_school配置文件,两个配置文件中分别声明了一个bean。



    
    

​
​


    
    

我们可以创建一个total的主配置文件来管理这些模块配置文件。





2.4 基于注解的DI

通过注解完成Java对象的创建和属性赋值。

使用注解的步骤:

  1. 加入maven的依赖spring-context,在你加入spring-context的同时,会间接加入spring-aop的依赖 使用注解必须使用spring-aop的依赖

  2. 在类中加入spring的注解(多个不同功能的注解)

  3. 在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置

需要学习的注解:

  1. @Component

  2. @Respotory

  3. @Service

  4. @Controller

  5. @Value

  6. @Autowired

  7. @Resource

2.4.1 @Component的使用

首先创建一个类,并在其中使用注解@Component

package com.pjh.ba01;
​
import org.springframework.stereotype.Component;
​
/**
 * @Component 创建对象的,等同于的功能
 * @value 就是对象的名称,也就是bean的id,其值是唯一的,创建的对象在整个spring容器中就一个
 *
 * 该注解使用在类的上面
 *
 * @Component(value = "myStudent") 等同于
 *     
 */
​
@Component(value = "myStudent")
//当我们只填入value的值,其实是可以省略的,并且在平时更加常见
//OR @Component("myStudent")
​
//我们也可以不指定对象的名称,由spring指定默认名称,其默认值为类名的lowcase格式,即student
//OR @Component
public class Student {
    private String name;
    private String age;
​
    public void setName(String name) {
        this.name = name;
    }
​
    public void setAge(String age) {
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
​
}

然后创建一个配置文件,并在其中使用组件扫描器component-scan。



    
    

此时已经完成了在spring容器中创建java对象的目的,我们可以调用这个对象。

package com.pjh;
​
import com.pjh.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
/**
 * @author yueyinghaibao
 * @date 2021/9/25
 */
public class MyTest {
​
    @Test
    public void test01() {
        String config = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
​
        Student myStudent = (Student) context.getBean("myStudent");
        //OR Student myStudent = (Student) context.getBean("student");
        System.out.println("myStudent = " + myStudent);
    }
}

可以得到myStudent = Student{name='null', age='null'}的结果 顺带一提,此时spring是调用的student类的无参构造方法

2.4.2 与Component具有相同功能的注解

  1. @Repository(放在持久层):放在dao的实现类上,表示创建dao对象,dao对象是能访问数据库的。

  2. @Service(放在业务层):放在service的实现类上,表示创建service对象,service对象是做业务处理, 可以有事务功能。

  3. @Controller(放在控制器):放在controller类上的,表示创建controller对象,controller对象能够接受用户提交的参数,显示请求的处理结果。

以上三个注解的使用语法和Component一样。都能创建对象,但这三个注解有额外功能。

这三个注解是给项目的对象分层的。

非这三类的对象应该使用Component。

2.4.3 component-scan扫描多个包的方式

  1. 多次使用扫描器,指定不同的包

  2. 使用分隔符( ; OR , )分割多个包名

  3. 指定一个父包,可以包含父包中的所有子包

2.4.4 @Value的使用

此标签用于给简单类型的属性赋值。

package com.pjh.ba02;
​
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
​
@Component("myStudent")
public class Student {
    /**
     * @Value 给简单类型赋值
     * 属性:value 是String类型,表示简单类型的属性值
     * 位置:1.在属性定义的上方,无需set方法,推荐使用
     *      2.在set方法上面
     */
    @Value("张飞")
    private String name;
    @Value("20")
    private String age;
​
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public void setAge(String age) {
//        this.age = age;
//    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
​
}

2.4.5 @Autowired的使用

自动注入实现引用类型的属性赋值。

package com.pjh.ba03;
​
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
​
@Component("myStudent")
public class Student {
    @Value("张飞")
    private String name;
    @Value("20")
    private int age;
    /**
     * 引用类型
     * @Autowired spring框架提供的注解,实现引用类型的赋值
     * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName和byType
     * 默认使用byType
     * 位置:1.在属性定义上,无需set方法,推荐使用
     *      2.在set方法上面
     *
     * 属性:required 是一个boolean类型的,默认true
     *      required = true:表示引用类型赋值失败,程序报错,并终止执行。
     *      required = false:表示引用类型赋值失败,程序正常执行,并且赋值为null。
     *
     * 如果要使用byName方式,需要做的是:
     * 1.在属性上面加入@Autowired
     * 2.在属性上面加入@Qualifier(value = "bean的id"):表示使用指定名称的bean完成赋值
     */
//    @Autowired
//    private School school1;
    @Autowired
    @Qualifier("mySchool")
    private School school2;
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school2 +
                '}';
    }
}

注意:

关于required的true OR false问题,更加推荐使用true。原因在于如果你的程序出现问题,会更早暴露你的问题,方便你尽快修改程序问题。

2.4.6 @Resource的使用

该注解是由jdk提供的自动注入注解,作用与autowire一样

package com.pjh.ba06;
​
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
​
import javax.annotation.Resource;
​
@Component("myStudent")
public class Student {
    @Value("张飞")
    private String name;
    @Value("20")
    private int age;
    /**
     * 引用类型
     * @Resource 来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
     *           使用的也是自动注入的原理,支持byName,byType,默认是byName
     * 位置:1.在属性定义的上面,无需set方法,推荐使用
     *      2,在set方法上面
     * 默认使用byName:先使用byName自动注入,如果buName失败,在使用byType
     * 如果想指定只使用byName,需要增加一个属性 name
     * name是bean的id
     */
    @Resource(name = "mySchool")
    private School school;
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

2.5 XML和注解该使用哪种方式

XML的优势:

  1. 使用xml配置文件可以方便的修改属性的值,不需要改动类

XML的劣势:

  1. 需要写的代码太多,开发比较繁琐

  2. 由于赋值和类完全分离,所以在浏览类代码时,完全不清楚其属性值的内容

注解的优势:

  1. 使用方便,不需要写很多的代码

注解的劣势:

  1. 注解和源代码不宜分开,在需要改动时非常麻烦

  2. 注解对源代码的结构是破坏性的(O~O!)

总的来说:如果需要经常变动属性值,推荐用XML;如果属性值相对固定,推荐使用注解

并且当前的趋势是,以注解为主,XML配置文件为辅。

2.6 一个小技巧

在注解中使用 ${},通过properties配置文件实现解耦合。

需要在配置文件中用标签读取配置文件。

你可能感兴趣的:(学习笔记,spring,java,后端)