一、Spring 核心技术

1、Spring框架概述

Spring框架采用了控制反转(IoC)和面向切面编程(AOP)的设计理念,简化了企业级Java应用程序的开发过程。

Spring

2、从面试看Spring

Spring热点考题

二、Spring Ioc

1、IOC概述

IoC(Inversion of Control),即控制反转,是一种设计原则。将程序由传统的操作控制流程反转了。

IOC

在Spring中,由IoC容器创建、组装和管理的对象被称为Bean。

IOC容器

2、Spring Ioc示例

(1)导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.11</version>
</dependency>

(2)新建ContextConfig类

/**
 * 创建Spring的配置类
 * 代表Spring Ioc容器
 */

@Configuration //声明这是Spring Ioc容器
@ComponentScan("cn.highedu.entity") //声明到哪里去找具有@Component注解的类
public class ContextConfig {

}

(3)声明cn.highedu.entity包,并在该包下创建Computer类

@Component //将该类交给Spring去管理
//Spring会负责创建该类的对象 并提供全方位的管理
public class Computer {
    @Override
    public String toString() {
        return "Computer{}";
    }
}

(4)在cn.highedu包下创建案例的主类,命名为Application

public class Application {
    public static void main(String[] args) {
        /**
         * 获取spring的容器管理的对象
         * 1、获取容器
         * 2、获取容器中的对象
         */
        //初始化Spring Ioc容器对象 指定管理类
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        //从容器中获取被管理的Bean
        Computer c = context.getBean(Computer.class);
        System.out.println(c);
    }
}

运行结果:

Computer{}

好处:如果spring在那么对象一直就在,不用自己再去创建对象

3、IoC示例运行原理解析

  • @Configuration:用于标记当前类是一个Spring框架的配置类(管理类)
  • @ComponentScan("cn.highedu"):用于通知Spring框架自动扫描和加载指定包下的所有组件并注册到Spring IoC容器中

Computer是一个交给Spring管理的类。

  • @Component:用于将类标识为一个受Spring管理的组件

ApplicationContext是Spring容器中最重要的核心接口之一,它扮演着Spring容器的角色,提供了一种对Spring Bean进行集中式管理的机制。

  • getBean方法用于从IoC容器中获取一个符合条件的Bean对象。

AnnotationConfigApplicationContext是ApplicationContext接口的一个重要实现,它可以通过读取基于Java类的配置以及指定的Component classes来构建Spring IoC容器。

Ioc原理

4、IoC实现解耦示例

本案例通过一个简单的耦合场景,演示Spring IoC为项目带来的好处。

定义一个接口A和它的实现类B,然后定义20个类,分别命名为X1到X20。在X1到X20中都有一个名为method的方法,在方法中创建了B类的实例,并调用了run方法。

此时,X1到X20与接口及其他实现类B存在依赖关系,即存在耦合。如下图所示:

解耦合

此时,有一个程序扩展需求:新建A接口的实现类C,将程序中所有的B类的对象都替换为C类的对象。为实现这一需求,需要逐个修改X1到X20类中的代码,程序的可扩展性和可维护性较差。

如果项目中应用了Spring IoC,则可以极大的简化这一工作。

解耦合示例

示例:

创建接口A:

public interface A {
    void run();
}

创建实现类B:

@Component //对象有标识,默认是类名首字母小写
public class B implements A {
    @Override
    public void run() {
        System.out.println("B开始工作");
    }
}

创建实现类C:

@Component //对象有标识,默认是类名首字母小写
public class C implements A {
    @Override
    public void run() {
        System.out.println("C开始工作");
    }
}

启动类:

public class Application {
    public static void main(String[] args) {
        /**
         * 获取spring的容器管理的对象
         * 1、获取容器
         * 2、获取容器中的对象
         */
        //初始化Spring Ioc容器对象 指定管理类
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        // 通过类型获取对象
        //A a1 = context.getBean(A.class);
        //A a2 = context.getBean(A.class);
        //a1.run();
        //a2.run();

        //通过对象的名字获取对象
        A a1 = context.getBean("b", A.class);
        A a2 = context.getBean("c", A.class);

        a1.run(); //B开始工作

        a2.run(); //C开始工作
    }
}

5、Spring DI

DI是Dependency Injection的缩写,即依赖注入,是一项使一个对象接收到它所依赖的其他对象的技术。DI 的实现通常需要使用注解或 XML 配置文件来声明依赖关系:

依赖关系

依赖注入

1、注解方式

Spring框架支持使用@Autowired、@Resources等注解来实现依赖注入。

  • @Autowired注解可以注入一个具体的类、接口、或者抽象类,一般用于注入当前对象依赖的其他对象
  • @Resources注解与@Autowired注解功能相似,但是执行机制上略有不同,推荐使用@Autowired注解
@Component
public class Student {
    //需要Computer对象
    @Autowired //依赖注入 Spring会把Student类需要的依赖对象Computer加载进来
    private Computer computer;

    public void run() {
        System.out.println(computer);
    }
}

启动类:

public class Application {
    public static void main(String[] args) {
        /**
         * 获取spring的容器管理的对象
         * 1、获取容器
         * 2、获取容器中的对象
         */
        //初始化Spring Ioc容器对象 指定管理类
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        Student student = context.getBean(Student.class);
        student.run(); 
    }
}

三、Spring Ioc进阶

Spring 管理注解:

使用注解来标识Bean,并指定Bean的属性、依赖关系等信息。

Spring管理注解

1、定义Bean

在Spring IoC容器中获取Bean可以通过以下几种方式:

1、context.getBean(Class requiredType):根据类型获取Bean。其中:

  • requiredType为要获取bean的类型
  • 如果容器中有多个实现该类型的Bean,但是没有配置优先使用哪个Bean,则会抛出NoUniqueBeanDefinitionException异常

2、context.getBean(String name):根据名称获取Bean。其中:

  • name为Bean在Spring IoC容器中的名称
  • 默认情况下,Bean的名称是类名进行了首字母小写的处理后的结果
  • 可以通过在Spring注解中设置名称来设置Bean的名称,如:@Component("myBean")

3、context.getBean(String name, Class requiredType):根据Bean名称和类型获取Bean。

  • 该方法相当于在getBean(String name)方法的基础上添加了Bean类型的验证
  • 如果获取的Bean对象与requiredType指定的类型不同,将抛出BeanNotOfRequiredTypeException

4、context.getBeansOfType(Class type):根据类型获取容器中所有实现该类型的Bean。

  • type为要获取bean的类型
  • 该方法返回Map类型,其中key为Bean在容器中的名称,value为Bean实例
/**
 * @document: 管理第三方的组件
 * @Author:SmallG
 * @CreateTime:2023/9/14+14:19
 */

@Configuration //管理类声明
public class BeanDefineConfig {
    /**
     * 被Spring管理一个对象 对象的标识(id)是方法的名字(getName)
     * 对象的类型是String
     * 对象的内容是Tom
     *
     * @return
     */
    @Bean
    public String getName() {
        return "Tom";
    }

    @Bean
    public String[] getNames() {
        String[] names = {"Tom", "Jerry", "Spike"};
        return names;
    }

    @Bean
    public ArrayList<String> names() {
        ArrayList<String> names = new ArrayList<>();
        names.add("张三");
        names.add("李四");
        names.add("王五");
        return names;
    }

    /**
     * 被Spring管理的Bean 标识是list
     * 需要引入Spring IOC容器中其他的Bean
     * 方法中的参数的名字就是要引入的Bean的标识
     * @param names
     * @return
     */
    @Bean
    public ArrayList<String> list(ArrayList<String> names) {
            names.add("关羽");
            names.add("关兴");
            return names;
    }
}

测试类:

public class ApplicationTest {
    private ApplicationContext context;

    @BeforeEach
    public void createContext() {
        context = new AnnotationConfigApplicationContext(BeanDefineConfig.class);
    }

    @Test
    public void test1() {
        //获取被Spring IOC容器管理的第三方组件的对象
        String name = context.getBean("getName", String.class);
        System.out.println(name);
    }

    @Test
    public void test2() {
        String[] names = (String[]) context.getBean("getNames");
        for (String name : names) {
            System.out.println(name);
        }
    }

    @Test
    public void test3(){
        ArrayList<String> names= (ArrayList<String>) context.getBean("names");
        names.forEach(System.out::println);
    }
    @Test
    public void test4(){
        ArrayList<String> names= (ArrayList<String>) context.getBean("list");
        names.forEach(System.out::println);
    }
}

2、Bean的作用域

常用的两种作用域:单例作用域、原型作用域

  • 单例作用域(singleton):Bean的默认作用域,任何时候获得的Bean对象都是同一个实例
  • 原型作用域(prototype):每次引到bean时都会创建新的实例

作用域示例:

在cn.highedu包下新建SingletonBean

/**
 * Spring Bean单例作用域
 * Spring Bean默认的作用域就是单例作用域(singleton)
 * 在初始化Spring IOC容器时 同时初始化所有作用域是单例的Bean
 * 在以后使用的过程中 Spring IOC容器始终管理这个Bean
 */

@Component
public class SingletonBean {
    //一个类被Spring管理 类中要有默认的构造方法

    public SingletonBean() {
        System.out.println("SingletonBean被实例化");
    }

    @Override
    public String toString() {
        return "SingletonBean{}";
    }
}

声明一个管理类:

//声明一个管理类
@Configuration
@ComponentScan("cn.highedu.spring") //扫描spring包里的所有类
public class ScopeConfig {
    
}

对作用域进行测试

public class ScopeTest {
    private ApplicationContext context;

    @BeforeEach
    public void createContext() {
        context = new AnnotationConfigApplicationContext(ScopeConfig.class);
    }

    @Test
    public void test1() {
        SingletonBean bean1 =
                context.getBean("singletonBean", SingletonBean.class);
        SingletonBean bean2 =
                (SingletonBean) context.getBean("singletonBean");

        System.out.println(bean1 == bean2); // true 说明两次获取的是同一个bean
    }
}

声明原型作用域的类:

原型作用域在获取Bean的时候Spring IOC才会创建Bean的示例,Spring IOC不会管理原型作用域的Bean,java垃圾回收机制会回收这样的Bean

/**
 * 原型作用域,非单例模式,每次创建都是新的
 */
@Component
@Scope("prototype") //声明这个bean的作用域是原型作用域
public class PrototypeBean {
    public PrototypeBean() {
        System.out.println("PrototypeBean被实例化");
    }
}

测试作用域:

@Test
public void test2() {
    PrototypeBean bean1 =
            context.getBean("prototypeBean", PrototypeBean.class);
    PrototypeBean bean2 =
            (PrototypeBean) context.getBean("prototypeBean");

    System.out.println(bean1 == bean2); // false 说明两次获取的不是同一个bean
}

Bean作用域

3、依赖注入

Spring框架主要支持三种注入方式:字段注入、构造器注入、setter方法注入

构造方法依赖注入示例:
@Component
public class Student {
    //需要Computer对象
    @Autowired //依赖注入 Spring会把Student类需要的依赖对象Computer加载进来
    private Computer computer;

    public Student() {
        System.out.println("调用了无参构造");
    }

    //构造方法实现依赖注入
    @Autowired
    public Student(Computer computer) {
        System.out.println("调用了有参构造");
        this.computer = computer;
    }

    public void run() {
        System.out.println(computer);
    }
}

测试类:

public class DITest {

    private ApplicationContext context;

    @BeforeEach
    public void createContext() {
        context = new AnnotationConfigApplicationContext(ContextConfig.class);
    }

    @Test
    public void test1(){
        Student student = context.getBean("student",Student.class);
        student.run();
    }
}

测试结果:

调用了有参构造
Computer{}
setter方法实现依赖注入(spring 官方推荐)
@Component
public class Student {
    //需要Computer对象
    @Autowired //依赖注入 Spring会把Student类需要的依赖对象Computer加载进来
    private Computer computer;

    //setter方法实现依赖注入
    @Autowired
    public void setComputer(Computer computer) {
        this.computer = computer;
    }

    public Student() {
        System.out.println("调用了无参构造");
    }

    //构造方法实现依赖注入
    @Autowired
    public Student(Computer computer) {
        System.out.println("调用了有参构造");
        this.computer = computer;
    }

    public void run() {
        System.out.println(computer);
    }
}
最后修改:2023 年 09 月 16 日
如果觉得我的文章对你有用,请随意赞赏