Spring Framework

Spring Framework

关于Spring

Spring框架主要解决了创建对象、管理对象的问题。

在传统的开发中,当需要某个对象时,使用new关键字及类型的构造方法即可创建对象,例如:

Random random = new Random();

如果以上代码存在于某个方法中。则random就只是个局部变量,当方法运行结束,此变量就会被销毁!

在实际项目开发,许多对象被创建出来之后,应该长期存在于内存中,而不应该销毁,当需要使用这些对象时,通过某种方式获取对象即可,而不应该重新创建对象!

除了对象存在的时间(时长)以外,在实际项目开发中,还需要关注各个类型之间的依赖关系!例如:

public class UserMapper {
    public void insert() {
        // 向数据表中的“用户表”中插入数据
    }
}
public class UserController {
    public UserMapper userMapper;
    public void reg() {
        userMapper.insert();
    }
}

在以上示例代码中,可视为“UserController是依赖于UserMapper的”,也可以把UserMapper称之为“UserController的依赖项”。

当需要创建以上类型时,如果只是单纯的把UserController创建出来了,却没有关注其内部userMapper属性的值,甚至该属性没有值,则是错误的做法!

如果要使得UserController中的UserMapper属性是有值的,也非常简单,例如:

public class UserController {
    public UserMapper userMapper = new UserMapper();
    public void reg() {
        userMapper.insert();
    }
}

但是,在实际项目中,除了UserController以外,还会有其它的组件也可能需要使用到UserMapper,如果每个组件中都使用new UserMapper()的语法来创建对象,就不能保证UserMapper对象的唯一性,就违背了设计初衷。

为了更好的创建对象和管理对象,应该使用Spring框架!

创建基于Spring的工程

当某个项目需要使用Spring框架时,推荐使用Maven工程。

创建新的的Maven工程,创建完成后,会自动打开pom.xml文件,首先,应该在此文件中添加节点,此节点是用于添加依赖项的:

<dependencies>

</dependencies>

然后,Spring框架的依赖项的代码需要编写在以上节点的子级,而依赖项的代码推荐从 https://mvnrepository.com/ 网站查询得到,Spring的依赖项名称是spring-context,则在此网站搜索该名称,以找到依赖项的代码,代码示例:

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

将以上依赖项的代码复制到节点之下即可。

然后,点击IDEA中悬浮的刷新Maven的图标,或展开右侧的Maven面板点击刷新按钮,即可自动下载所需要的jar包文件(这些文件会被下载到本机的Maven仓库中,同一个依赖项的同一个版本只会下载一次)。

通过Spring创建对象–通过@Bean方法(显式配置)

操作步骤:

  • 创建cn.tedu.spring.SpringBeanFactory类
  • 在类中添加方法,方法的返回值类型就是你希望Spring创建并管理的对象的类型,并在此方法中自行编写返回有效对象的代码
  • 在此类上添加@Configuration注解,表明该类是一个配置类
  • 在此方法上添加@Bean注解
    以上步骤的示例代码:
package cn.tedu.spring;

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

import java.util.Random;

@Configuration
public class SpringBeanFactory {

    @Bean
    public Random random() {
        return new Random();
    }

}

接下来,创建某个类用于执行:

public class SpringRunner {

    public static void main(String[] args) {
        // 1. 加载Spring
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringBeanFactory.class);

        // 2. 从Spring中获取对象
        Random random = (Random) ac.getBean("random");

        // 3. 测试使用对象,以便于观察是否获取到了有效的对象
        System.out.println("random > " + random);
        System.out.println("random.nextInt() > " + random.nextInt());

        // 4. 关闭
        ac.close();
    }

}

接下来,运行SpringRunner类的main()方法即可看到执行效果如下:

random > java.util.Random@61009542
random.nextInt > 1384314073

关于以上代码:

  • 在AnnotationConfigApplicationContext的构造方法中,应该将SpringBeanFactory.class作为参数传入,否则就不会加载SpringBeanFactory类中内容
    • 其实,在以上案例中,SpringBeanFactory类上的@Configuration注解并不是必须的
  • 在getBean()时,传入的字符串参数"random"是SpringBeanFactory类中的方法的名称
  • 在SpringBeanFactory类中的方法必须添加@Bean注解,其作用是使得Spring框架自动调用此方法,并管理此方法返回的结果
  • 关于getBean()方法,此方法被重载了多次,有以下三种方法:
    1、直接写上BeanId,但是需要强转
    Random random=(Random)ac.getBean("random");
    
    2、写上Bean的类名.class,只能在类中有且只有一个对象时使用
    如果没有匹配类型的对象,将导致NoSuchBeanDefinitionException
    如果有2个,将导致NoUniqueBeanDefinitionException
     Random random=ac.getBean(Random.class);
     
    3BeanIdBeanClass一起写,根据BeanId查找,然后根据后面的BeanClass进行类型转换
    Random random=ac.getBean("random",Random.class);

通过Spring创建对象–通过@Component组件扫描(隐式配置)

自行创建某个类,例如创建cn.tedu.entity.Loo类,并在类的声明之前添加@Component注解

package cn.tedu.entity;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class Loo {
    @Override
    public String toString() {
        return "Hello World!";
    }
}

可以在配置类中通过@ComponentScan来指定组件扫描的包,并在加载Spring时,传入此配置类即可,例如:

package cn.tedu.spring;

import cn.tedu.entity.Loo;  //导入类Loo的包
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration  //设置为配置类
@ComponentScan("cn.tedu.entity")  //在此使用@ComponentScan指定扫描的包
public class SpringConfig {
    @Bean
    public Loo loo(){
        return new Loo();
    }
}
  • 在使用@ComponentScan时,也可以传入多个包名:
    例如:@ComponentScan({“cn.tedu.spring.controller”, “cn.tedu.spring.service”})
    注:推荐传入的包名是更加具体的,但不需要特别精准,只需要保证不会扫描到非自定义的包即可

在AnnotationConfigApplicationContext中传入配置类的类名.class

package cn.tedu.spring;

import cn.tedu.entity.Loo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringRunner {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac 
        = new AnnotationConfigApplicationContext(SpringConfig.class);
        Loo loo=ac.getBean("loo",Loo.class);
        System.out.println(loo);
        ac.close();
    }
}

成功得到输出结果:

Hello World!

Process finished with exit code 0
关于组件:
  • 在Spring框架中,可用的组件注解有:
    • @Component:通用组件注解
    • @Controller:应该添加在“控制器类”上
    • @Service:应该添加在“业务类”上
    • @Repository:应该添加在“数据存取类”上
    • @Configuration:是一种特殊的组件,应该添加在“配置类”上,当执行组件扫描时,添加了@Configuration注解的类也会被创建对象

关于2种通过Spring创建对象的做法

  • 以上分别介绍了使用@Bean方法和使用组件扫描的方式使得Spring创建对象的做法,在实际应用中:

    • 使用@Bean方法可用在所有场景,但是使用过程相对繁琐
    • 使用组件扫描的做法只适用于自定义的类型(这些类是你自己编写出来的),使用过程非常便捷
  • 所以,当需要被Spring创建对象的类型是自定义的,应该使用组件扫描的做法,如果不是自定义的,只能使用@Bean方法。这2种做法在实际的项目开发中都会被使用到!

Spring管理的对象的作用域

  • 由Spring管理的对象的作用域默认是单例的(并不是单例模式),对于同一个Bean,无论获取多少次,得到的都是同一个对象!如果 希望某个被Spring管理的对象不是单例的 ,可以在类上添加 @Scope(“prototype”)注解

  • 并且,在单例的情况下,默认不是懒加载的,还可以通过 @Lazy注解 控制它是否为懒加载模式!所谓的懒加载,就是“不要逼不得已不创建对象”

Spring管理的对象的生命周期

  • 由Spring创建并管理对象,则开发人员就没有了对象的控制权,无法对此对象的历程进行干预,而Spring允许在类中自定义最多2个方法,分别表示初始化方法和销毁方法,并且,Spring会在创建对象之后就执行初始化方法,在销毁对象之前执行销毁方法。
  • 关于这2个方法,你可以按需添加,例如,当你只需要干预销毁过程时,你可以只定义销毁的方法,不需要定义初始化方法。
  • 关于这2个方法的声明:
    • 访问权限:推荐使用public
    • 返回值类型:推荐使用void
    • 方法名称:自定义
    • 参数列表:推荐为空
  • 然后,需要在 初始化方法 的声明之前添加 @PostConstruct注解,在 销毁方法 的声明之前添加 @PreDestroy 注解。
    例如:
    在Loo类中添加初始化方法和构造方法
package cn.tedu.entity;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class Loo {
    @PostConstruct
    public void init(){
        System.out.println("初始化方法");
    }

    @Override
    public String toString() {
        return"Hello World!";
    }

    @PreDestroy
    public void destroy(){
        System.out.println("销毁方法");
    }
}

运行后出现如下结果:
初始化方法和销毁方法成功运行

初始化方法
Hello World!
销毁方法

Process finished with exit code 0

注意:仅当类的对象被Spring管理且是单例的,才有讨论生命周期的价值,否则,不讨论生命周期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值