【Spring框架--快速了解IOC和AOP 面试不慌】

发布于:2023-01-24 ⋅ 阅读:(18) ⋅ 点赞:(0) ⋅ 评论:(0)

1.Spring框架是什么?

简介:

Spring就像是整个项目中装配bean(对象)的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地new一个对象,而是让Spring框架帮你来完成这一切。(包括:对象的创建、对象之间的关系)

总结:Spring框架,可以理解为是一个管理者:管理整个分层架构(MVC)中的每一个对象;(每一个对象称之为bean)

2.Spring框架的优缺点

2.1优点

  • 轻量:Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar
  • 针对接口编程,解耦合:Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
  • AOP 编程的支持:通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
  • 方便集成各种优秀框架:Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。

2.2缺点

  • 最大的缺点就是:配置繁琐,启动慢
  • 经常要考虑第三方工具的兼容性
  • 项目的依赖管理耗时耗力

3.框架学习什么?

Spring最主要的学习内容:两个核心Ioc+Aop
Spring框架是一个软件,其它人写好的软件,所以我们要掌握的就是:

  • 1.IOC :Inversion Of Control 控制反转
    2.DI:Dependency Injection 依赖注入: 本质就是给属性赋值
    DI是依附在IOC的基础上 才得以实现
    3.AOP Aspect Oriented Programming 面向切面的编程
  • 知道框架能做什么, mybatis–访问数据库, 对表中的数据执行增删改查。
  • 框架的语法, 框架要完成一个功能,需要一定的步骤支持的,
  • 框架的内部实现, 框架内部怎么做。 原理是什么。
  • 通过学习,可以实现一个框架

4.Spring框架做什么?

目的:管理对象

Spring的核心思想是IOC(控制反转),把对象的控制权给了Spring,(底层是通过反射)即:不再需要程序员去显式地new一个对象,而是让Spring框架帮你来完成这一切。

IOC控制反转:(Inversion Of Control),即就是:将对象的控制权从自己new改为跟Spring要;
  • 最常见的方式:DI:依赖注入Dependency Injection;
  • 还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

5.Spring之IoC

5.1核心概念

  • IoC (Inversion of Control) :控制反转, 是一个理论,概念,思想。描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
    • 控制: 创建对象,对象的属性赋值,对象之间的关系管理。
    • 反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
    • 正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
  • 容器:是一个服务器软件, 一个框架(spring就是一个容器,帮我们创建对象,管理对象之间的关系)

5.2 IoC的技术实现

DI 是ioc的技术实现

  • DI(Dependency Injection):依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,赋值,查找都由容器内部实现。
  • spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。因此只需要传一个类名,Spring就可以创建对象。
  • spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。

5.3 完成IoC的步骤

在这里插入图片描述

  1. 添加依赖。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

2.创建类、接口、实现类。Spring在MVC三层架构中是针对service层的框架。

​ 3.Spring配置文件。通常命名为:applicationContext.xml。该文件类似Servlet的web.xml,在配置文件中注册类,这样Spring才能完成IOC

<?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">

    <!--告诉spring创建对象
        声明bean , 就是告诉spring要创建某个类的对象
        id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
        class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

        spring就完成 SomeService someService = new SomeServiceImpl();
        spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
           springMap.put(id的值, 对象);
           例如 springMap.put("someService", new SomeServiceImpl());

        一个bean标签声明一个对象。
    -->
    <bean id="someService" class="com.xd.service.impl.SomeServiceImpl"></bean>

    <!--
        bean标签的scope属性:
            prototype:表示可以一次创建多个该类型对象,即多例
            singleton:默认值,表示一次只能创建一个该类型对象,即单例
            单例模式下,对象是在工厂初始化的时候创建的。多例模式是在工厂初始化之后,获取对象的时候创建的。
    -->
    <bean id="someService1" class="com.xd.service.impl.SomeServiceImpl" scope="prototype"></bean>

    <!--
       spring能创建一个非自定义类的对象吗, 创建一个存在的某个类的对象。
    -->
    <bean id="myDate" class="java.util.Date"></bean>
</beans>
<!--    
   spring的配置文件
   1.beans : 是根标签,spring把java对象成为bean。
   2.spring-beans.xsd 是约束文件,和mybatis指定  dtd是一样的。都是用来控制标签里面出现的内容
-->

4. 测试类中创建Spring容器,调用Spring容器中的方法可以创建对象、获取对象的信息。

@Test
public void test02(){
    //使用spring容器创建的对象
    //1.指定spring配置文件的名称
    String config="applicationContext.xml";
    //2.创建表示spring容器的对象, ApplicationContext
    // ApplicationContext就是表示Spring容器,通过容器获取对象了
    // ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);

    //从容器中获取某个对象, 你要调用对象的方法
    //getBean("配置文件中的bean的id值")
    SomeService service = (SomeService) ac.getBean("someService");

    //使用spring创建好的对象
    service.doSome();
}

/*
以下代码的执行原理:通过ClassPathXmlApplicationContext创建容器对象,创建容器对象的同时,ClassPathXmlApplicationContext会根据配置文件的路径去解析配置文件,解析的过程会将配置文件中所有<bean>标签中的类创建对象,默认调用无参构造方法,之后将创建好的对象以及bean标签的id保存在map中,其中id为key。在测试文件中可以调用容器对象ac的getBean方法通过id获取创建好的对象,要强转,然后调用对应方法。
*/
public void test06(){
	String path = "applicationContext.xml";
    ApplicationContext ac = new ClassPathXmlApplicationContext(path);
    Date nowTime = (Date)ac.getBean("myDate");
    System.out.println(nowTime);
}

注意:

  • ApplicationContext是一个接口,有多个实现类,通常使用ClassPathXmlApplicationContext。
  • spring默认创建对象的时间:创建spring的容器时,会创建配置文件中的所有的对象。并把这些对象放到springMap中,之后通过bean标签中的id获取创建好的对象 spring创建对象:默认调用的是无参数构造方法。

5.4 Spring容器对象的方法

//创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//根据bean标签的id获取创建好的对象,getBean返回Object类型,要强转。通过service调用该类的方法
SomeService service = (SomeService) ac.getBean("someService");
//获取容器中定义的对象的数量
int nums = ac.getBeanDefinitionCount();
//容器中每个定义的对象的名称,知道了对象的名称,就可以通过getBean获取创建好的对象。
String[] names = ac.getBeanDefinitionNames();

5.5 IoC的依赖注入:DI

  • 依赖就是属性,注入就是赋值。
  • 注入的方式有set注入以及构造注入,也就是对应正转里的通过类的set方法给对象赋值,以及通过构造方法给类属性赋值。
  • 使用set注入,Spring是根据属性名去寻找对应的set方法并执行,与有没有属性,set方法中写什么内容无关。Spring只是负责找到set方法并执行。set注入也分为简单类型的注入以及引用类型的注入。
    在这里插入图片描述

具体实现:

 <?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"
  <!--声明student对象
        注入:就是赋值的意思
        简单类型: spring中规定java的基本数据类型和String都是简单类型。
        di:给属性赋值
        1. set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值
         1)简单类型的set注入
            <bean id="xx" class="yyy">
               <property name="属性名字" value="此属性的值"/>
               一个property只能给一个属性赋值
               <property....>
            </bean>

         2) 引用类型的set注入 : spring调用类的set方法
           <bean id="xxx" class="yyy">
              <property name="属性名称" ref="bean的id(对象的名称)" />
           </bean>

        2.构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。
          构造注入使用 <constructor-arg> 标签
          <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。
          <constructor-arg> 标签属性:
             name:表示构造方法的形参名
             index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序
             value:构造方法的形参类型是简单类型的,使用value
             ref:构造方法的形参类型是引用类型的,使用ref

        注意:Spring的配置文件中不用考虑bean标签的顺序,因为Spring很智能,会二次扫描
    -->
	
    <!--set注入-->
    <bean id="myStudent" class="com.xd.ba02.Student">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <property name="email" value="zhangsan@qq.com"/>
        <!--引用类型,注意引用注入,要先要在bean中定义一个引用对象,将该对象id作为ref的值。-->
        <property name="school" ref="mySchool"/><!--setSchool(mySchool)-->
    </bean>

    <!--声明School对象-->
    <bean id="mySchool" class="com.xd.ba02.School">
        <property name="name" value="西安电子科技大学"/>
        <property name="address" value="西安市雁塔区"/>
    </bean>
    
    <!--使用name属性实现构造注入-->
    <bean id="myStudent" class="com.xd.ba03.Student">
        <!--
            实现原理:Spring扫描到该bean,看到他里面是constructor-arg标签,并且是三个该标签
            就知道是构造注入,并且去com.xd.ba03.Student类中找含有三个参数的有参构造方法,进行赋值。
            与set注入同理:Spring只是负责找有参构造方法,然后去执行,但是构造方法的声明以及方法体是由我们写的。
            有参构造方法声明了只能保证Spring找到了,不会报异常
            但是如果方法体中没有赋值,结果就是:Student对象:Student{name='null', age=0, school=null}
        -->
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>

    <!--使用index属性-->
    <bean id="myStudent1" class="com.xd.ba03.Student">
        <constructor-arg index="0" value="李四"/>
        <constructor-arg index="1" value="22"/>
        <constructor-arg index="2" ref="mySchool"/>
    </bean>

    <!--省略index-->
    <bean id="myStudent2" class="com.xd.ba03.Student">
        <constructor-arg value="李四"/>
        <constructor-arg value="22"/>
        <constructor-arg ref="mySchool"/>
    </bean>

    <!--声明School对象-->
    <bean id="mySchool" class="com.xd.ba03.School">
        <property name="name" value="西安xxx大学"/>
        <property name="address" value="西安市雁塔区"/>
    </bean>

    <!--创建File,使用构造注入-->
    <!--构造注入创建文件对象-->
    <bean id="myFile" class="java.io.File">
        <constructor-arg name="parent" value="F:\计算机\Java学习\SSM\04-Spring\源码\ch02-di-xml"/>
        <constructor-arg name="child" value="readme.txt"/>
    </bean>

5.6 基于注解的di(重点)

  • 基于注解的di: 通过注解完成java对象创建,属性赋值
  • 使用注解的步骤:
    1. 加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。使用注解必须使用spring-aop依赖
    2. 在类中加入spring的注解(多个不同功能的注解)
    3. 在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
  • 组件扫描器(component-scan):组件就是java对象。使用方式实在spring配置文件中引入组件扫描器。
<!--声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你的项目中的包名。
        component-scan工作方式: spring会扫描遍历base-package指定的包,
           把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。

       加入了component-scan标签,配置文件的变化:
        1.加入一个新的约束文件spring-context.xsd
        2.给这个新的约束文件起个命名空间的名称
    -->
<context:component-scan base-package="com.xd.ba01"/>
  • 学习的注解:@Component、@Respotory、@Service、@Controller、@Value、@Autowired、@Qualifier、@Resource

5.6.1 @Component注解

@Component: 创建对象的, 等同于的功能。

  • 语法格式:@Component(value = “myStudent”) 等同于
    • 属性:value 就是对象的名称,也就是bean的id值,value的值是唯一的,创建的对象在整个spring容器中就一个;
    • 位置:在类的上面
//使用value属性,指定对象名称
//@Component(value = "myStudent")
//不指定对象名称,由spring提供默认名称: 类名的首字母小写
//@Component

@Component("myStudent")  //常用
public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("student无参构造方法执行");
    }

	setter

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

5.6.2 @Respotory、@Service、@Controller 注解

  • @Respotory、@Service、@Controller这三个注解与Component功能类似,都是创建对象。但是又有其特殊的用途,是给项目的对象分层的。
  • @Repository(用在持久层类的上面) : 放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
  • @Service(用在业务层类的上面):放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务等功能的。
  • @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果。
  • 对于@Component、@Respotory、@Service、@Controller。毋庸置疑的是,持久层dao用@Respotory、业务层用@Service、页面层用@Controller。对于其它不在这三层的类,但是又想创建对象,就用@Component注解。

5.6.3 @Value

  • @Value注解的作用是给对象简单类型属性赋值
  • 属性:@Value注解里的值必须是String类型,赋的值要用双引号括起来
  • 位置:
    1. 在属性定义的上面,无需set方法,推荐使用。底层用反射快。
    2. 在set方法的上面使用,调set方法。
@Component("myStudent")
public class Student {
    @Value("张三")
    private String name;
    
    @Value("20")
    private int age;
    
    //@Value("20") 调set方法
    public void setAge(int age) {
        System.out.println("student的set方法执行了");
        this.age = age;
    }
}

5.6.4 @Autowired

  • @Autowired注解的作用是给对象引用类型属性赋值。使用的是自动注入原理 ,支持byName, byType。默认byType。

  • 位置:

    1. 在属性定义的上面,无需set方法, 推荐使用
    2. 在set方法的上面
    @Autowired  //按byType注入
    private School school;
    
  • 如果要使用byName方式,需要做的是:

    1. 在属性上面加入@Autowired
    2. 在属性上面加入@Qualifier(value=“bean的id”) :表示使用指定名称的bean完成赋值。
    @Component("mySchool")
    public class School {
    }
    
    @Autowired
    @Qualifier(value = "mySchool") //mySchool为id
    private School school;
    
  • @Autowired注解的request属性,是一个boolean类型的,默认true。一般使用true,方便排错。

    • required=true:表示引用类型赋值失败,程序报错,并终止执行。
    • required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null,不建议用,因为赋值为null以后会出空指针异常。
    //@Autowired(required = true) 默认
    //@Autowired(required = false)不报错,不建议用
    @Autowired
    @Qualifier(value = "mySchool")
    private School school;
    

5.6.5 @Resource

  • @Resource: 来自jdk中的注解,spring框架提供了对这个注解的功能支持,完成引用类型的自动注入,支持byName,byType。默认byName

  • 位置:

    1. 在属性定义的上面,无需set方法,推荐使用。
    2. 在set方法的上面
    @Component("school")
    public class School {
    }
        
    @Resource //默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType
    private School school;
    
  • @Resource只使用byName方式,需要增加一个属性 name,name的值是bean的id(名称)

    @Resource(name = "mySchool")
    private School school;