[框架]Spring框架

news2024/11/20 2:42:21

目录

关于Spring框架

Spring框架创建对象

Spring框架创建对象的方式之一--组件扫描

Spring框架创建对象的方式之二--@Bean方法

Spring框架创建对象的方式的选取

Spring Bean的名称

Spring Bean的作用域

Spring Bean的生命周期

Spring的自动装配

关于为属性注入值的做法

关于IoC与DI

关于Spring AOP


关于Spring框架

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

创建对象比如说之前的controller,我们都没有去new过对象,但可以调用里面的方法区处理请求。还有Impl实现类、jwt过滤器、配置类、全局异常处理器这些也都没有去new,但它们的方法都是正常执行的,这就是因为spring框架去创建了对象。

Spring框架的核心有:Spring IOC、Spring AOP。

Spring框架创建对象

为什么要让spring创建对象?

所有被Spring创建出来的对象都可以称之为:Spring Bean。

所有Spring Bean都会存在于Spring的ApplicationContext中,由于Spring框架会维护这个ApplicationContext容器,所以,Spring框架也通常被称之为“Spring容器”。

 所有Spring Bean都可以被自动装配。用spring创建对象就可以自动装配。

怎么让spring框架去创建的对象?

Spring框架创建对象的方式之一--组件扫描

需要在配置类上添加@ComponentScan注解,以开启组件扫描,则Spring框架会自动扫描添加此注解的类所在的包及其子孙包,检查其中是否包含“组件类”,如果存在,则自动创建出这些“组件类”的对象!  

在Spring Boot项目中,默认就开启了组件扫描,因为Spring Boot项目中的启动类上添加了@SpringBootApplication注解,此注解包含了@ComponentScan。  

提示:可以在@ComponentScan注解上配置参数,以显式的指定扫描哪些包,这样就只扫描这两个包及子孙包,例如:@ComponentScan({"cn.tedu.csmall.product.controller", "cn.tedu.csmall.product.service"})

组件扫描发上在启动过程中,并不是在运行过程中。按理说因该显示的指定扫描哪些包以防止对不必要的包进行扫描影响效率,比如pojo包,但实际情况是往往不显示的指定,因为只影响启动的时候的一秒半秒,对运行不影响。

所有被添加了@Component注解的类,都是“组件类”,反之,则不是!

在Spring框架中,还有一些基于@Component注解的组合注解,添加这些注解,也能将类标记为“组件类”,包括:

  • @Repository:添加在存储库类(用于访问数据的类)上

  • @Service:添加在业务类上

  • @Controller:添加在控制器类上

  • @Configuration

以上注解,除了@Configuration以外,各组件注解的用法、功能是完全相同的,只是语义不同!

在Spring MVC框架中,也有一些基于@Component的组件注解,包括:

  • @RestController

  • @ControllerAdvice

  • @RestControllerAdvice

Spring框架创建对象的方式之二--@Bean方法

在任何配置类中,可以自定义方法,并在方法上添加@Bean注解,则Spring框架会自动调用此方法,并获取此方法返回的对象,将此方法放在Spring容器中。

Spring框架创建对象的方式的选取

 对于自定义的类,建议优先使用组件扫描的做法;对于非定义的类,只能使用@Bean方法的做法!

Spring Bean的名称

当使用组件扫描的方式来创建Spring Bean时,默认情况下,如果类名的第1个字母是大写且第2个字母是小写的,Spring Bean的名称就是将类名的首字母改为小写的名称,如果不满足此条件,则Spring Bean的名称就是类名,例如:AdminController类的Spring Bean的名称默认是adminControllerABController类的Spring Bean的名称默认是ABController

可以通过组件注解的参数来指定Spring Bean的名称,例如:

@Controller("controller")
public class AdminController {}

所有的组件注解都可以使用以上方式来指定Spring Bean的名称。

当使用@Bean方法的方式来创建Spring Bean时,默认情况下,Spring Bean的名称就是方法的名称,也可以配置@Bean注解的value属性来指定名称。

Spring Bean的作用域

默认情况下,Spring Bean都是“单例”的,可以通过@Scope("prototype")将其配置为“非单例”的。

单例就可以在controller中不管是添加请求还是查询请求用的都是同一个controller,单例有一个特点是常驻内存的,一旦创建出来,直到程序结束才结束。如果不是单例的,在第一次添加相册的时候帮你new一个controller,用完之后销毁了,在第二次添加相册的时候有帮你new一个controller,用完之后又销毁了,这样就没有必要。有一点,如果你在单例的比如配置类里面自己new了一个对象,因为配置类是单例的,这个new对象间接也会是单例的效果。

如果使用组件扫描的方式来创建对象,则在类上添加@Scope("prototype")注解;

如果使用@Bean方法的方式来创建对象,则在方法上添加@Scope("prototype")注解。

默认情况下,单例的Spring Bean是“预加载”的(相当于单例模式中的饿汉式模式),在启动的时候就会创建出对象,可以通过@Lazy将其配置为“懒加载”的(相当于单例模式中的懒汉式模式),懒汉式是在什么时候需要什么时候创建。

如果使用组件扫描的方式来创建对象,则在类上添加@Lazy注解;

如果使用@Bean方法的方式来创建对象,则在方法上添加@Lazy注解。

至于是预加载好还是懒加载好?预加载好。在预加载的情况下,所有的组件都是在准备就绪的状态下,当程序在很忙的时候,也不会因为几个关键对象没有创建而去创建,从而导致卡顿。就算是启动会慢一点,但是运行会很顺畅。那么预加载相对于懒加载带来的资源浪费怎么办呢?这是无解的,如果一个程序启动30天,就算用懒加载在使用第10天的时候创建了对象,由于单例的特性,也会有20天的浪费。所以资源浪费是无解的,同时因为现在内存都比较大,不在乎创建的这几个对象,所以预加载是比较划算的做法。

注意:Spring管理的对象的表现可能是单例的(根据你是否修改了默认的配置),但并没有使用单例模式来实现,所以,不要将其与单例模式划等号。  

错误的说法:Spring使用单例模式来管理对象的作用域

错误的说法:Spring就是单例模式的

Spring Bean的生命周期

 Spring框架允许你在组件类中自定义初始化方法和销毁方法,这2个方法应该是:

  • 公有的访问权限

  • 使用void作为返回值类型

  • 方法的名称是自定义的

  • 参数列表为空

在初始化方法上添加@PostConstruct注解,则此方法会在构造方法之后自动被调用;在销毁方法上添加@PreDestroy注解,则此方法会在对象被销毁之前的一刻自动被调用。

如果使用@Bean方法的方式来创建对象,则在@Bean注解中通过initMethod属性来配置初始化方法的名称,通过destroyMethod属性来配置销毁方法的名称。

Spring的自动装配

Spring的自动装配表现为:当某个组件类的属性需要值时,或被Spring自动调用的方法的参数需要值时,Spring框架可以自动的从容器中找到合适的值。

 典型表现为:

@RestController
public class AdminController {
    @Autowired // 自动装配
    private IAdminService adminService; // AdminServiceImpl类的对象
}

关于合适的值:类型匹配的Spring Bean,或当存在多个类型匹配的Spring Bean时,也要考虑Spring Bean的名称。

例如:需要装配private IAdminService adminService;属性,并且,IAdminService有2个实现类都是被Spring管理对象的,默认的Spring Bean名称可能是adminServiceImpl1adminServiceImpl2,则名称也无法匹配,在加载Spring时,就会直接报NoUniqueBeanDefinitionException错误,此时,可以选择:

  • 修改属性名,使之与某个Spring Bean名称相同,例如private IAdminService adminServiceImpl1;

  • 修改某个Spring Bean名称,使之与属性名相同,例如@Serivce("adminService")

  • 在属性上补充@Qualifier注解,指定Spring Bean的名称,例如@Qualifier("adminSesrviceImpl1")

除了使用@Autowired以外,还可以使用@Resource注解添加在属性上,完成属性值的自动装配,例如:

@RestController
public class AdminController {
    @Resource // @Autowired // 自动装配
    private IAdminService adminService; // AdminServiceImpl类的对象
}

 关于@Autowired@Resource的区别:

  • @Autowired是Spring框架的注解,而@Resourcejavax.annotation包中的注解

    • 如果你不使用Spring框架,改为使用其它可以实现自动装配的框架,@Resource仍是有效的

      • 由于Spring框架的应用普级程度非常高,几乎没有Java WEB项目不使用Spring框架,所以,使用@Autowired几乎没有缺点

  • @Resource注解是优先根据名称来匹配的,如果无匹配,再根据类型来匹配;@Autowired注解是优先根据类型来查找匹配的对象,如果存在多个类型匹配的,再根据名称来匹配

 关于@Autowired的具体装配过程,首先,会查询匹配类型的Spring Bean的数量,然后:

  • 0个:取决于@Autowired注解的required属性的值

    • true(默认):无法装配,在加载ApplicationContext时会出现NoSuchBeanDefinitionException

    • false:放弃装配,在加载ApplicationContext不会因为无法装配而报错,在后续使用时可能出现NPE

      • 强烈不推荐

  • 1个:直接装配,且成功

  • 多个:尝试按照名称来装配,如果存在名称匹配的Spring Bean,则装配且成功,如果没有,则在加载ApplicationContext时会出现NoUniqueBeanDefinitionException

关于为属性注入值的做法

当组件类的属性需要值时,可以有3种做法:  

 字段注入:在属性上添加@Autowired / @Resource注解,例如:

@RestController
public class AdminController {
    
    @Autowired
    private IAdminService adminService;
    
}

Setter注入:通过Setter方法为属性赋值,此Setter方法需要添加@Autowired注解,例如:

@RestController
public class AdminController {
    
    private IAdminService adminService;
    
    @Autowired                  
    public void setAdminService(IAdminService adminService) {
        this.adminService = adminService;
    }
    
}

构造方法注入:通过构造方法为属性赋值,例如:

@RestController
public class AdminController {
    
    private IAdminService adminService;
    
    public AdminController(IAdminService adminService) {
        this.adminService = adminService;
    }
    
}

从理论上分析:通过构造方法注入是最安全的做法,而字段注入是最不推荐的做法,所以,在IntelliJ IDEA中,使用字段注入时会有相关提示!

关于Spring调用构造方法:

  • 如果类中没有显式的添加构造方法,Spring会自动调用默认的构造方法

  • 如果类中显式的声明了1个构造方法,无论是否有参数,Spring都会自动尝试调用它

  • 如果类中有多个构造方法,在默认情况下,Spring会自动调用无参数的构造方法(如果存在的话),如果你希望Spring调用特定的构造方法,则需要在构造方法上添加@Autowired注解

关于IoC与DI

 IoCInversion of Control):控制反转,表现为“将对象的控制权交给了Spring框架”

在没有spring的情况下,我作为写代码的人,我是对这个对象有完全的控制权的,就说这个对象从创建到销毁一切都尽在我在的掌握,但有了spring框架之后,这个事就不用我做了,表现为“将对象的控制权交给了Spring框架”,这就是控制反转。

DIDependency Injection):依赖注入,表现为“为依赖项注入值”

我们用的  @Autowired的注入都是DI。

如果要说IoC与DI的关系和区别:

Spring框架负责创建对象、管理对象都是Spring IoC的表现。

Spring框架通过DI完善了IoC,所以,DI是一种实现手段,IoC是需要实现的目标。

在开发实践中我们不仅仅需要spring把对象创建出来,还需要各个属性有值,否则创建出来也没有什么用。所以没有DI,IoC是不完善的。

关于Spring AOP

AOP:面向切面的编程

注意:AOP源自AspectJ,并不是Spring特有的技术,只是Spring很好的支持了AOP

AOP主要解决了“横切关注”的问题,具体为“若干个不同的方法均需要执行相同的任务”的问题!

使用AOP的常用场景有:安全检查、异常处理、事务管理等。

假设存在需求:统计各个Service中的各方法的执行耗时。  

在Spring Boot项目中,当需要使用AOP时,需要添加spring-boot-starter-aop依赖项:

<!-- Spring Boot支持Spring AOP的依赖项,主要解决横切关注问题 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>${spring-boot.version}</version>
</dependency>

在项目的根包下创建aop.TimerAspect切面类,在类上添加@Aspect切面注解@Component组件注解,在类中编写切面方法:

package cn.tedu.csmall.product.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class TimerAspect {

    // 连接点(JoinPoint):程序执行过程中的某个节点,可能是某个方法的调用,或抛了某个异常
    // 切入点(PointCut):选择1个或多个连接点的表达式
    // ------------------------------------------------------------------------
    // 通知(Advice)注解
    // @Around:环绕,包裹了连接点,你的代码可以在连接点之前和之后执行
    // @Before:只能在连接点之前执行
    // @After:只能在连接点之后执行
    // @AfterReturning:只能在成功的执行了连接点之后执行
    // @AfterThrowing:只能在连接点方法抛出异常之后执行
    // 以上各Advice执行情况大致是:
    // @Around开始
    // try {
    //     @Before
    //     执行连接点
    //     @AfterReturning
    // } catch (Throwable e) {
    //     @AfterThrowing
    // } finally {
    //     @After
    // }
    // @Around结束
    // ------------------------------------------------------------------------
    // execution配置的就是切入点表达式,用于匹配某些方法
    // 在切入点表达式中,可以使用通配符:
    // -- 1个星号:任意1次匹配
    // -- 2个连续的小数点:任意n次匹配,只能用于包或和参数列表
    //                 ↓ 返回值类型
    //                   ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 包名
    //                                                  ↓ 类名
    //                                                    ↓ 方法名
    //                                                      ↓↓ 参数列表
    // 另外,在表达式中,在方法的返回值的左侧还可以指定修饰符,修饰符是可选的
    // 注解是典型的修饰符之一
    // 其实,Spring MVC的统一异常处理就是通过AOP实现的,
    // Spring JDBC的事务管理也是如此
    // Spring Security的权限检查也是如此
    @Around("execution(* cn.tedu.csmall.product.service.*.*(..))")
    public Object xxx(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        // 获取匹配的方法的相关信息
        String targetClassName = pjp.getTarget().getClass().getName(); // 获取匹配的方法所在的类
        String signatureName = pjp.getSignature().getName(); // 获取匹配的方法的签名中的方法名称
        Object[] args = pjp.getArgs(); // 方法的参数列表
        System.out.println("类型:" + targetClassName);
        System.out.println("方法名:" + signatureName);
        System.out.println("参数列表:" + Arrays.toString(args));

        // 执行以上表达式匹配的方法,即某个Service的某个方法
        // 注意-1:必须获取调用proceed()方法返回的结果,作为当前切面方法的返回值
        // -- 如果没有获取,或没有作为当前切面方法的返回值,相当于执行了连接点方法,却没有获取返回值
        // 注意-2:调用proceed()时的异常必须抛出,否则,Controller将无法知晓Service抛出过异常,就无法向客户端响应错误信息
        // -- 前提:本例的切面是作用于Service的
        // -- 其实,你也可以使用try...catch捕获调用proceed()时的异常,但必须在catch中也抛出异常
        Object result = pjp.proceed();

        long end = System.currentTimeMillis();
        System.out.println("执行耗时:" + (end - start) + "毫秒");

        // 返回调用proceed()得到的结果
        return result;
    }

}

代码解释:

 

可以看出,我们不管执行什么方法,它的执行耗时都统计出来了,但在刚才整个编写代码的过程中,都没有去修改service的代码,这就是AOP技术最强大的地方,它使得每个service的执行,我们都可以去干预这个执行的过程,但我们却不需要去修改原有的任何代码。

 这些方法可以得到配置的方法的相关信息:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/691466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

stm32或gd32移植libcanard实现UAVCAN协议

一、源码下载 1、git下载 点击我下载 2、csdn下载 自己上传的点击下载 二、源码移植 我自己是使用rt-thread操作系统移植的。但是不局限与操作系统&#xff0c;裸机也可以。 1、首先将源码加入到工程 2、分别实现一个内存的分配与释放函数&#xff0c;他是一个指针函数&…

Keras-深度学习-神经网络-电影评论情感分析模型

目录 模型搭建 模型训练 模型搭建 使用到的数据集为IMDB电影评论情感分类数据集&#xff0c;该数据集包含 50,000 条电影评论&#xff0c;其中 25,000 条用于训练&#xff0c;25,000 条用于测试。每条评论被标记为正面或负面情感&#xff0c;因此该数据集是一个二分类问题。…

AD利用嘉立创的封装

1.首先&#xff0c;打开元件库&#xff0c;搜索元器件 2.点开它的封装&#xff08;符号&#xff09; 3.文件-->导出-->Altium Designer 4.然后在AD上面打开这个文件 5.将其复制&#xff0c;粘贴放到PCB库中 6.然后在原理图中的封装管理器中&#xff0c;添加封装&#xf…

ODrive引脚排列

对引脚配置的更改仅在odrv0.save_configuration()和odrv0.reboot()之后生效 如果 GPIO 设置为不支持的模式,它将保持未初始化状态。 当将 GPIO 设置为特殊用途模式(例如GpioMode.UART_A)时,您还必须启用相应的功能(例如<odrv>.config.enable_uart_a)。 数字模式是一…

如何创新玩转元服务开发-趋势、分类与我们实践的方向!

一、软件发展分类与元服务&#xff08;一&#xff09;软件分类发展简要分析 软件总体分为系统软件和应用软件两大类。用户、设备、操作系统系统软件、流量入口、应用形态应用软件关系及发展见下表—— 从表中分析得知&#xff0c;从互联网时期到移动互联网主导的发展&#xff…

前端开发两年半,我裸辞了

☀️ 前言 一晃两年半过去了&#xff0c;我离开了我的第一份前端开发工作&#xff0c;当你看到这篇文章&#xff0c;我已经离职两个月了&#xff0c;目前仍在艰难求职中&#xff0c;想记录分享一下我的经历&#xff0c;感兴趣的可以继续往下看&#xff0c;希望能给大家一些启示…

学Python能做哪些副业?我一般不告诉别人!建议存好

前两天一个朋友找到我吐槽&#xff0c;说工资一发交完房租水电&#xff0c;啥也不剩&#xff0c;搞不懂朋友圈里那些天天吃喝玩乐的同龄人钱都是哪来的&#xff1f;确实如此&#xff0c;刚毕业的大学生工资起薪都很低&#xff0c;在高消费、高租金的城市&#xff0c;别说存钱&a…

C++继承机制下析构和构造函数的执行分析

析构函数在下边3种情况时被调用&#xff1a; 对象生命周期结束&#xff0c;被销毁时&#xff1b;delete指向对象的指针时&#xff0c;或delete指向对象的基类类型指针&#xff0c;而其基类虚构函数是虚函数时&#xff1b;对象i是对象o的成员&#xff0c;o的析构函数被调用时&a…

SRM 供应商管理系统都有哪些模块?

3k字干货&#xff01; SRM必备6大模块&#xff1a;供应商管理、采购需求管理、采购寻源、采购履约、交付结算。下面针对环节中的核心场景进行讲解。 1、供应商全生命周期管理 过去&#xff0c;企业业务简单&#xff0c;对接供应商数量少&#xff0c;需求供给匹配、价格合适就…

如何使用KoodousFinder搜索和分析Android应用程序中的安全威胁

关于KoodousFinder KoodousFinder是一款功能强大的Android应用程序安全工具,在该工具的帮助下,广大研究人员可以轻松对目标Android应用程序执行安全研究和分析任务,并寻找出目标应用程序中潜在的安全威胁和安全漏洞。 账号和API密钥 在使用该工具之前,我们首选需要访问该…

适合初创企业租赁的办公模式-共享办公室

随着共享经济的兴起&#xff0c;共享办公室已经成为越来越多人的选择。共享办公室提供了一个灵活、高效、舒适的工作环境&#xff0c;能够帮助个人和团队提高工作效率和创造力。下面我将从三个角度来介绍共享办公室。 共享办公室的优势 首先&#xff0c;共享办公室具有成本效益…

合肥先进光源束测步进电机及驱动器的选择

大规模电机控制的方案选择-电机和驱动器篇 在上面文档的系统里选择的是免电池带绝对值编码器的步进伺服电机方案&#xff0c;现在有些场合只是普通的步进电机就好了&#xff0c;同样从电机控制的龙头企业鸣志的产品中选择&#xff0c;依然选择现成熟的ethercat总线技术的驱动器…

深度学习模型在图像识别中的应用:CIFAR-10数据集实践与准确率分析

文章目录 前言导入所需的库忽略证书验证下载并加载 CIFAR-10 数据集数据预处理构建深度学习模型编译模型模型训练模型评估进行图片识别测试图片运行效果完整代码完结 前言 深度学习模型在图像识别领域的应用越来越广泛。通过对图像数据进行学习和训练&#xff0c;这些模型可以自…

ChatGPT独家汇总:发现最优秀的人工智能对话体验

欢迎来到我们的 ChatGPT 镜像网站汇总博客&#xff01;在这个令人激动的人工智能时代&#xff0c;ChatGPT 作为一款顶尖的语言模型&#xff0c;已经引起了全球范围内的热议。但是&#xff0c;您是否曾经为了找到最佳的 ChatGPT 使用体验而苦苦搜寻&#xff1f;别担心&#xff0…

(15)第一人称视角视频

文章目录 前言 15.1 推荐的零件 15.2 连接图示 15.3 通过任务计划器最小化OSD设置 15.4 集成式OSD 15.5 用户视频/博客 15.6 与FPV飞行特别相关的安全警告 15.7 政府/地方法规 前言 第一人称视角在飞行时为你提供了真正的飞行员视角&#xff0c;它将视频摄像机和发射器…

梯度下降求函数极小值

梯度下降求函数极小值 Himmelblau 函数表达式 ∫ ( x , y ) ( x 2 y − 11 ) 2 ( x y 2 − 7 ) 2 \int(x,y)(x^{2}y-11)^{2} (xy^{2}-7)^{2} ∫(x,y)(x2y−11)2(xy2−7)2 Himmelblau.py 绘制 import numpy as np import matplotlib.pyplot as plt# from mpl_toolkits.mp…

Redis中的缓存雪崩、击穿、穿透的原因以及解决办法

缓存雪崩、击穿、穿透一旦发生&#xff0c;会导致大量的请求积压到数据库层。如果请求的并发量很大&#xff0c;就会导致数据库宕机或是故障&#xff0c;这就是很严重的生产事故了。 俗话说&#xff0c;知己知彼&#xff0c;百战不殆。了解了问题的成因&#xff0c;我们就能够…

PentestGPT:一款由ChatGPT驱动的强大渗透测试工具

关于PentestGPT PentestGPT是一款由ChatGPT驱动的强大渗透测试工具,该工具旨在实现渗透测试任务执行过程的自动化。该工具基于ChatGPT实现其功能,允许广大研究人员以交互式的方式使用,并指导渗透测试人员进行渗透测试任务的总体进度调控并执行指定操作。 除此之外,Pentes…

[学习笔记]python的web开发全家桶2(ing)

初识网站 默认编写的静态的效果动态&#xff1a;需要用到Web框架的功能。 对于目前的我们来看&#xff0c;什么可以做数据存储&#xff1a;txt文件excel文件专业的软件&#xff1a;数据库管理系统 MySQL/Oracle/SQLServer/DB2/Access... 7 MysQL 7.1 安装MySQL MySQL&…

#10042. 「一本通 2.1 练习 8」收集雪花

题目描述 不同的雪花往往有不同的形状。在北方的同学想将雪花收集起来&#xff0c;作为礼物送给在南方的同学们。一共有 n 个时刻&#xff0c;给出每个时刻下落雪花的形状&#xff0c;用不同的整数表示不同的形状。在收集的过程中&#xff0c;同学们不希望有重复的雪花。你可以…