Spring—IoC

目录

1. IoC的提出

2. Spring容器

2.1. Spring容器实现原理

2.2. Spring组件

2.2.1 XML标签方式

2.2.2. 类注解方式

2.2.3. 方法注解方式

2.3. Spring容器分类

2.3.1. BeanFactory容器

2.3.2. ApplicationContext容器

2.3.3. WebApplicationContext容器

3. Spring中的常用注解


1. IoC的提出

       在采用面向对象方法设计的软件系统中,底层实现是由一组对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。对象之间的耦合关系就像手表齿轮的啮合,彼此之间存在复杂依赖关系,往往牵一发而动全身,如图1所示。因此,如何降低系统之间、模块之间和对象之间的耦合度,是软件工程追求的目标之一1996年,软件专家Michael mattson首次提出了控制反转(Inversion of Control, IoC)概念。控制反转的核心思想是:借助于“第三方(IoC容器)” 实现具有依赖关系的对象之间的解耦,如图2所示。

图1. 传统面向对象系统

图2. 使用IoC容器的面向对象系统

        在使用IoC容器的面向对象系统中,全部对象的控制权都交给IoC容器来管理,所以,IoC容器成为整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用。

       2004年,Martin Fowler探讨了同一个问题,既然IoC是控制反转,那么到底是“哪些方面的控制被反转了呢”?最后,他得出结论是“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为由IoC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection, DI)”。所以,依赖注入(DI)和控制反转(IoC)是从不同的角度来描述同一件事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。

2. Spring容器

       Spring容器是Spring框架的核心,它是一种IoC容器,负责管理应用程序中对象的生命周期和依赖关系。Spring容器通过读取Spring组件的元数据((XML文件、注解或Java配置))来创建、配置、装配并管理应用程序中的对象。

2.1. Spring容器实现原理

        实现Spring容器的最主要技术是Java反射编程Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。简单来说,只要给定类的名字,就可以通过反射机制来获得类的所有信息。Spring容器的工作模式可以看作是工厂模式的升华。可以把Spring容器看作是一个对象工厂,这个工厂里要生产的对象都在元数据中给出定义,然后利用反射编程,根据元数据(XML文件、注解或Java配置)中给出的类名生成相应的对象。从实现来看,Spring是把工厂模式里写死的对象生成代码,改变为由元数据来定义,也就是把工厂和对象生成这两者独立离开来,目的是提高灵活性和可维护性。Spring容器的实现原理如图3所示。

  图3. Spring容器实现原理

2.2. Spring组件

      纳入Spring容器管理的Java类称为Spring组件(简称组件),Spring容器对组件进行实例化所创建的对象称为Bean。一个Java类可以标识为多个Spring组件,每个Spring组件可以创建一个或多个Bean。定义Spring组件主要有三种方式:XML标签、类注解和方法注解。

2.2.1 XML标签方式

       该方式通过在XML配置文件中使用<bean>标签来定义Spring组件。下面通过一个例子来说明如何通过<bean>标签来定义组件和创建Bean。

(1)类的定义

          在包com.my.examples.example1中定义Java类Login和User,类的声明如下:

package com.my.examples.example1;

public class Login {
    String username;
    String password;

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
package com.my.examples.example1;

public class User {
    Login login;
    public void print(){
        System.out.println("login user: "+login.getUsername());
    }

    public Login getLogin() {
        return login;
    }

    public void setLogin(Login login) {
        this.login = login;
    }
}

 (2)Spring组件定义

        在项目的resources目录下创建spring-config.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 定义Spring组件 -->
    <bean id="login" class="com.my.examples.example1.Login">
        <!--为属性code和name注入初始值 -->
        <property name="username" value="admin"></property>
        <property name="password" value="123456"></property>
    </bean>
    <bean id="currentUser" class="com.my.examples.example1.User">
        <!-- 为属性login注入引用值(Spring组件login) -->
        <property name="login" ref="login"></property>
    </bean>
</beans>

(3)配置Spring应用上下文

       根据XML配置文件创建Spring容器,利用容器所提供的getBean()方法获取相应的Bean,从容中获取Bean后就可以调用其方法来执行。

package com.my.examples.example1;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //根据XML配置文件创建Spring容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //获取Spring容器中的Bean
        Login login= context.getBean("login", Login.class);
        User user=context.getBean("currentUser", User.class);
        user.print();
    }
}

运行类Test,在控制台中输出如下信息:

login user: admin

2.2.2. 类注解方式

       该方式通过@Component、@Respository、@Service、@Controller注解来定义Spring组件,利用Java配置类(用@Configuration注解定义的类)代替XML配置文件。@Component注解用于标识一个类为Spring组件,当Spring框架扫描到该注解时,会创建该类的实例并纳入Spring容器中管理。@Respository、@Service、@Controller都是@Component注解的特殊形式,分别用于标识数据访问层、服务层和控制层的Spring组件。

      下面将2.1.1节中的例子改为类注解方式。

(1)Spring组件的定义

      在包com.my.examples.example2中定义Java类Login和User,在类上加入@Component注解使其成为Spring组件。

package com.my.examples.example2;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component  //定义Spring组件
@PropertySource("classpath:my-config.properties") //引入外部属性文件
public class Login {
    @Value("${my.username}")  //注入属性文件中的account值
    String username;
    @Value("${my.password}")  //注入属性文件中的password值
    String password;

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
package com.my.examples.example2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component   //定义Spring组件
public class User {
    @Autowired  //注入注入依赖的Spring组件的Bean
    Login login;

    public void print(){
        System.out.println("login user: "+login.getUsername());
    }
}

       @PropertySource注解的功能是引入外部属性文件:my-config.properties,该文件位于项目的resources目录下,文件的内容如下:

my.username=admin
my.password=123456

(2)Java配置类的定义

      在包com.my.examples.example2中定义类Config,在该类上加入@Configuraton注解使其成为Java配置类。在该配置类上加入@ComponentScan注解,其功能是让Spring容器扫描com.my.examples.example2包及其子包下面所有的Spring组件,并将这些组件进行例化为Bean。

package com.my.examples.example2;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration   //定义Java配置类
//扫描包com.my.examples.example2及其子包下面的所有Spring组件
@ComponentScan("com.my.examples.example2")
public class Config {
}

(3)配置Spring应用上下文

      根据Java配置类创建Spring容器,利用容器提供的getBean()方法获取相应的Bean,调用Bean提供的方法来执行任务。

package com.my.examples.example2;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        //根据Java配置类创建Spring容器
        AnnotationConfigApplicationContext context = new
                AnnotationConfigApplicationContext(Config.class);
        //获取容器中的Bean
        Login login=context.getBean(Login.class);
        User user=context.getBean(User.class);
        user.print();
    }
}

运行类Test,在控制台中输出如下内容:

login user: admin

2.2.3. 方法注解方式

       该方式不需要在类上加入@Component等注解来定义Spring组件,而是通过在Java配置类中的方法上加入@Bean注解,该方法的返回值是一个Spring组件的Bean。

      下面把第2.1.2节中的应用改为采用Java配置的方式来实现。

(1)类的定义

       在包com.my.examples.example3中定义Java类Login和User。

package com.my.examples.example3;

public class Login {
    String username;
    String password;

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
package com.my.examples.example3;

public class User {
    Login login;
    public void print(){
        System.out.println("login user: "+login.getUsername());
    }

    public Login getLogin() {
        return login;
    }

    public void setLogin(Login login) {
        this.login = login;
    }
}

(2)定义Java配置类

package com.my.examples.example3;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration   //定义Java配置类
//扫描包com.my.examples.example3及其子包下面的所有Spring组件
@ComponentScan("com.my.examples.example3")
@PropertySource("classpath:my-config.properties") //引入外部属性文件
public class Config {
    @Value("${my.username}")
    String username;

    @Value("${my.password}")
    String password;

    @Bean    //定义Spring组件并返回Bean
    public Login login(){
        Login login = new Login();
        //注入属性值
        login.setUsername(username);
        login.setPassword(password);
        return login;
    }

    @Bean   //定义Spring组件并返回Bean
    public User user(){
        User user = new User();
        //注入组件Login的Bean时,直接调用方法login()
        user.setLogin(login());
        return user;
    }
}

(3)配置Spring应用上下文

       根据Java配置类创建Spring容器,根据容器提供的getBean()方法获取相应的Bean,调用Bean的方法执行任务。

package com.my.examples.example3;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {

    public static void main(String[] args) {
        //根据Java配置类创建Spring容器
        AnnotationConfigApplicationContext context = new
                AnnotationConfigApplicationContext(Config.class);
        //获取容器中的Bean
        Login login = context.getBean(Login.class);
        User user = context.getBean(User.class);

        user.print();
    }
}

运行类Test,在控制台中输出如下内容:

login user: admin

2.3. Spring容器分类

     Spring框架主要提供三种类型的容器:BeanFactory容器、ApplicationContext容器和WebApplicationContext

2.3.1. BeanFactory容器

       BeanFactory是Spring框架中用于管理 Bean的最基本接口,它提供了一系列用于获取Bean和管理 Bean依赖的方法。BeanFactory使用懒加载方式,即只有在需要时才创建和配置bean。

       BeanFactory接口包含的的主要方法:

  • Object getBean(String name):根据bean的名称在Spring容器中获取对应的Bean实例。
  • Object getBean(String name, Object... args):根据 Bean 的名称和给定的构造函数参数获取一个 Bean 实例。
  • boolean containsBean(String name):判断Spring容器中是否包含指定名称的bean。
  • boolean isSingleton(String name):判断指定名称的bean是否是单例。
  • boolean isPrototype(String name):判断指定名称的bean是否是原型。

     BeanFactory接口的主要实现类:

  • XmlBeanFactory:这是一个简单的实现,它从 XML 文件中加载 Bean 定义,从 Spring 3.1 开始,该类已被标记为过时。
  • DefaultListableBeanFactory:它提供了一套完整的服务来管理不同的 Bean,包括注册 Bean、解析依赖、管理 Bean 生命周期、以及提供不同的 Bean 作用域等。

2.3.2. ApplicationContext容器

       ApplicationContext是BeanFactory的子接口,它在BeanFactory的基础上增加了更多的功能。除了提供BeanFactory的所有功能外,ApplicationContext还提供了更多企业特定的功能,如国际化支持、事件发布、应用层特定的上下文等。ApplicationContext在容器启动时就创建和配置所有的单例bean,提供了更多的高级特性。

      ApplicationContext接口的主要实现类:

  • ClassPathXmlApplicationContext:从类路径下的XML文件加载Spring应用上下文。这是在传统的 Spring 应用中常见的一种方式,适合那些配置存储在项目内部的XML文件中的应用。
  • FileSystemXmlApplicationContext:从文件系统指定的位置的XML文件加载Spring应用上下文。这种方式适用于需要从外部文件系统(而非类路径)加载配置的场景。
  • AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。这种方式专门为基于Java注解的配置提供支持。

2.3.3. WebApplicationContext容器

       WebApplicationContext是专门为 Web 应用程序设计的,它扩展了 ApplicationContext 接口,添加了一些针对Web环境的特定功能。WebApplicationContext通常在基于Spring的Web应用程序中使用,特别是在使用Spring MVC框架时,它通常在web.xml文件中配置,或者通过Spring 的WebApplicationInitializer接口在Java配置中启动。

       WebApplicationContext接口的主要实现类:

  • XmlWebApplicationContext:从XML文件加载配置的Web应用上下文,这是一个传统的方法,适合那些已经使用XML配置文件的项目。
  • AnnotationConfigWebApplicationContext:支持基于Java注解的配置,允许通过Java代码直接配置Spring,使得配置更加直观和类型安全。

3. Spring中的常用注解

      在Spring框架中,与Spring容器相关的注解如下表。

注解功能
@Component

通用注解,用于标识一个类为Spring组件,当Spring框架扫描到该注解时,会创建该类的实例并纳入Spring容器管理。

@Respository、@Service、@Controller都是这个注解的特殊形式,分别用于标识数据访问层、服务层和控制层的Spring组件。

@Respository该注解用于标注数据访问层组件,数据访问层组件通常用于数据操作。
@Service

该注解用于标注服务层组件,服务层组件通常用于业务逻辑处理。

@Controller

该注解用于标注控制层组件,控制层组件通常用于处理HTTP请求。

@Autowired

自动装配注解,它可以放在类的成员变量、方法以及构造方法上,用于自动注入依赖,@Autowired是按照类型注入依赖,如果想要按照名称来注入依赖,则需要结合@Qualifier注解一起使用。

@Resource与@Autowired功能一样,区别在于该注解默认是按照名称来注入依赖,只有当找不到与名称匹配的Bean时才会按照类型来注入依赖
@Qualifier与@Autowired一起使用,用于指定要注入的Bean的名称
@Configuration用于定义配置类,可替代XML文件,类中的方法可以使用@Bean注解,以声明Spring容器管理的Bean。
@Bean用于标识一个方法,该方法返回一个Bean实例,并将其注册到Spring容器中。
@Scope

用于指定Bean的作用域,分为如下几种:

1)singleton(单例):只创建一个Bean,这是默认的作用域,适用于无状态Bean,比如服务层Bean、数据访问层Bean;

2)prototype(原型):每次请求(通过getBean方法)都会创建一个新的Bean,适用于有状态的Bean;

3)request(请求):在Web应用中,每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP请求内有效;

4)session(会话):在Web应用中,每个HTTP会话都会创建一个新的Bean,该请求仅在当前HTTP会话内有效;

5)global-session(全局会话):仅在Portlet应用中使用,每个全局HTTP会话会创建一个Bean,适用于需要在全局HTTP会话期间保持状态的Bean。

6)application(应用):在整个ServletContext范围内,Bean都是单例的,适用于需要在整个Web用用范围内共享的Bean。

@Value用于注入简单类型的属性值,如字符串、数字等。
@Async用于标识一个方法或类,表示其中的操作应该异步执行。
@PostConstruct用于标识一个方法,该方法在Bean实例化后自动执行,用于初始化操作。
@PreDestroy用于标识一个方法,该方法在Bean销毁前自动执行,用于清理操作。

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值