SpringBoot 开发总结思考(一)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 从面向对象的角度理解XML,是以类和对象作为XML的配置SpringBoot 使用的是配置类加上普通/常规配置的形式,参数不是直接固定在配置类中,而可以写在配置文件中,例如application.properties

SpringBoot 开发总结思考(一)


1、Spring、SpringMVC与SpringBoot的关系与区别


Spring 全称:Spring Framework,当谈论及Spring 的时候,实际上是使用Spring Framework + Mybatis 进行编程,而SpringMvc 是Spring Framework 中专门用于Web的模块


Spring 有两层意义:


.Spring 家族下所有的产品,是一个泛称

.特指 Spring Framework


20200401140646610.jpg


2、Spring Framework 与 SpringBoot 之间的关系


SpringBoot 是新的产品,但它里面用的很多东西还是 Spring Framework,例如 @Component等


Spring 是 SpringBoot 的基础/应用


两者之间的区别肯定是有的,但更多的是上下的关系,SpringBoot是利用SpringFrameWork抽象出来的更好用的一个上层的框架


3、SpringBoot 相对于 SpringFramework 最核心的优势是什么?(自动配置)


自动配置(http://blog.didispace.com/spring-boot-disable-autoconfig/


4、SpringBoot最基础的编程模式@Component+@Autowired


把对象注入到容器的方式有N多种,大体上分为两种


.XML


.注解(Stereotype annotations)


@Component(最基础、用的最多的):在一个类上加此注解,就会被SpringBoot扫描之后,加入到容器当中并负责类的实例化,然后在代码中需要的时候就可以提取出了


.编程方式(但需要结合注解,所以更多时候主要归结到注解)


@Component 示例


新建Diana 类


@Component
public class Diana {
    public void q() {
        System.out.println("Camille 释放了 Q 技能");
    }
    public void w() {
        System.out.println("Camille 释放了 W 技能");
    }
    public void e() {
        System.out.println("Camille 释放了 E 技能");
    }
    public void r() {
        System.out.println("Camille 释放了 R 技能");
    }
}


需要的时候使用属性注入


@Autowired
private Diana diana;
@RestController
@RequestMapping("banner")
public class BannerController {
    @Autowired
    private Diana diana;
    @GetMapping("test")
    public String test(){
        diana.e();
        return "";
    }
}


使用构造函数注入


@RestController
@RequestMapping("banner")
public class BannerController {
    //@Autowired(required = false)
    //private Diana diana;
    public BannerController(Diana diana){
        this.diana = diana;
    }
    @GetMapping("test")
    public String test(){
        diana.e();
        return "";
    }
}


使用Setter注入


@RestController
@RequestMapping("banner")
public class BannerController {
    //@Autowired
    private Diana diana;
    @Autowired
    public void setDiana(Diana diana) {
        this.diana = diana;
    }
    @GetMapping("test")
    public String test(){
        diana.e();
        return "";
    }
}


5、Stereotype Annotations 模式注解(以@Component为基础)


@Component:把一个组件/类/bean 加入到 IOC 中


.@Service

.@Controller

.@Repository

.以上三个注解其实在功能上,目前版本与@Component没什么区别

.如果不是一个服务可以使用@Component,如果是则


@Configuration:把一个组件/类/bean加入到容器的方式与上面的有所区别(未完待续)


6、Spring的实例化和依赖注入时机与延迟实例化


1.使用@AutoWired可以注入一个Bean,但是如果容器中没有这个Bean,程序会报错


.在默认情况下,Spring 无法在容器中找到要注入的Bean,则会报错

.在注入时允许为空,@Autowired(required = false),调用Bean时则会报错


1.IOC 容器的注入时机(IOC是什么时候实例化对象,并且把对象给注入到代码片段中的)


.应用启动时实例化(立即/提前实例化)

.不仅是启动时实例化,并且还会将对象注入到代码片段中(默认机制)


@Lazy 延迟实例化


但在启动时依然会实例化对象,因为Bean不是一个单独的对象,在其他类(例如Controller)中被引用了,也即Controller 依赖于该Bean


所以如果不在依赖方(Controller)加@Lazy ,Bean依然会是默认的实例化流程


7、@Autowired按类型注入,被动推断注入与主动选择注入


Diana


@Component
public class Diana implements ISkill{
    public Diana() {
        System.out.println("Hello, phubing");
    }
    @Override
    public void q() {
        System.out.println("Diana 释放了 Q 技能");
    }
    @Override
    public void w() {
        System.out.println("Diana 释放了 W 技能");
    }
    @Override
    public void e() {
        System.out.println("Diana 释放了 E 技能");
    }
    @Override
    public void r() {
        System.out.println("Diana 释放了 R 技能");
    }
}


Irelia


@Component
public class Irelia implements ISkill{
    public Irelia() {
        System.out.println("Hello, phubing");
    }
    @Override
    public void q() {
        System.out.println("Irelia 释放了 Q 技能");
    }
    @Override
    public void w() {
        System.out.println("Irelia 释放了 W 技能");
    }
    @Override
    public void e() {
        System.out.println("Irelia 释放了 E 技能");
    }
    @Override
    public void r() {
        System.out.println("Irelia 释放了 R 技能");
    }
}


ISkill


public interface ISkill {
    void q();
    void w();
    void e();
    void r();
}


测试类


@RestController
@RequestMapping("banner")
public class BannerController {
    @Autowired(required = false)
    private ISkill diana;
    @GetMapping("test")
    public String test(){
        diana.e();
        return "";
    }
}


此时,程序运行是没问题的,但规范来讲,应该将 ISkill diana; 改为 ISkill iSkill ,这个时候程序运行就会出错


但变量名应该是由开发者定义的,而 Spring 的特点是约定大于配置,所以命名也有可能对代码的正确性造成影响


变量的名字对注入的 bean 是有影响的,如果定义为 ISkill diana 则会注入 Diana 这个类,优先级的规则是什么?


@Autowired 被动注入的方式


by type


.按照类型注入,默认注入形式,

.适用于接口只有一个实现类的情况

.找不到实现该接口的实现类,则会报错

.如果有多个类实现了接口,使用by type 方式注入失败的情况下,会根据命名推断进行注入

.根据类型推断到底应该从容器中注入哪个实现了该接口的 Bean


by name


.按照名字注入


以上都是SpringBoot 被动的进行判断,无论是 by type 还是 by name 都没有显式的指定注入的 Bean


使用  @Qualifier("diana")  显式(主动)指定注入的 Bean


@RestController
@RequestMapping("banner")
public class BannerController {
    @Autowired
    @Qualifier("diana")
    private ISkill iSkill;
    @GetMapping("test")
    public String test(){
        iSkill.e();
        return "";
    }
}


8、面向对象中变化的应对方案


1.指定一个接口,由多个类实现,然后这些类都有共同的接口(策略模式)

2.只有一个实现类,通过更改类的属性来适应变化(类似项目中定义的常量、配置文件)


9、@Configuration 配置类


对于没有明确指向/意义的 Bean 推荐使用@Component


在7的基础上增加一个类  Camille


public class Camille  implements ISkill{
    public Camille() {
        System.out.println("Hello, phubing");
    }
    @Override
    public void q() {
        System.out.println("Camille 释放了 Q 技能");
    }
    @Override
    public void w() {
        System.out.println("Camille 释放了 W 技能");
    }
    @Override
    public void e() {
        System.out.println("Camille 释放了 E 技能");
    }
    @Override
    public void r() {
        System.out.println("Camille 释放了 R 技能");
    }
}


新增一个 HeroConfiguration 配置类


@Configuration
public class HeroConfiguration {
    @Bean
    public ISkill camille(){
        return new Camille();
    }
}


每一个@Bean注解下的方法都要求方法返回一个Bean


测试类


@RestController
@RequestMapping("banner")
public class BannerController {
    @Autowired
    private ISkill camille;
    @GetMapping("test")
    public String test(){
        camille.e();
        return "";
    }
}


10、@Configuration表面意义上的解释(标注了此注解的类称之为  配置类)


已经有了@Component、@Service、@Repository ,Spring 为什么还要增加 @Configuration?


对于一个类来说,除了行为之外,还有特征


.行为:类中的方法就体现了类的行为

.特征:类中定义的属性都是要被赋值的,@Component 只是将Bean加入IOC而不能对属性进行赋值,因为实例化并不是本身去实例化的,所以没办法对属性进行赋值


@Configuration 中是自行实例化的,在实例化时可以传入相关参数,并且可以有多个@Bean


11、@Configuration 是用来替换 bean 的 xml 配置


以前 Spring 使用XML实例化Bean的方式


<?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">
    <!-- 装配Studnt对象到IOC容器中 -->
    <bean id="student" class="com.demo.domain.Student"/>
</beans>


12、为什么Spring 热衷于配置?


OCP 是为了解决变化,变化是不能消除的,只能隔离,或者反映到配置文件中(配置文件中的改动不算变化)


使用@Configuration 和 @Bean注入,改动这个配置类的代码算不算违反OCP?


.如果将配置类看做是以前的xml文件的替代品,那么改动代码是不会违反OCP


为什么要把变化隔离到配置文件里?


.配置文件具有集中性

.配置文件比较清晰,不包含业务逻辑


13、@Configuation和@Bean的真实作用


从面向对象的角度理解XML,是以类和对象作为XML的配置


SpringBoot 使用的是配置类加上普通/常规配置的形式,参数不是直接固定在配置类中,而可以写在配置文件中,例如application.properties


新建一个MySQL类


public class MySQL implements IConnect{
    private String ip;
    private Integer port;
    @Override
    public void connct() {
        System.out.println(this.ip + ", " + this.port);    
    }
    public MySQL(String ip, Integer port) {
        this.ip = ip;
        this.port = port;
    }
    public MySQL() {
    }
    public String getIp() {
        return ip;
    }
    public void setIp(String ip) {
        this.ip = ip;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
}


新建一个IConnect 接口


public interface IConnect {
    void connct();
}


新建一个 DatabaseConfiguration 配置类


@Configuration
public class DatabaseConfiguration {
    @Value("#{mysql.ip}")
    private String ip;
    @Value("#{mysql.port}")
    private Integer port;
    @Bean
    public IConnect mysql(){
        return new MySQL(this.ip, this.port);
    }
}


在 application.properties 配置文件中配置 IP 和 PORT


mysql.port=3306
mysql.ip=127.0.0.1


14、@Configuration 的意义


1.在IOC容器启动时,有选择/有条件地注入某一个Bean


2.对于某一个方向的业务问题,不一定只有一个Bean,可能具有多个Bean,此时就可以通过配置类,把所有需要的配置都导入到IOC容器中,而不是分散到各个Bean


15、几种注入方式


@Autowired :字段/成员变量注入


Setter 注入


构造函数注入


16、属性,到底是什么意思?


面向对象中的一个概念,对于Java来讲,严格意义上是不存在属性这样的一个概念


@Autowired
private ISkill camille;


这种最合适的应该称之为成员变量/字段


17、POJO 与 Bean


https://www.cnblogs.com/aiyowei/p/10443161.html


18、@ComponentScan包扫描机制


注解的基本原理是通过反射机制来实现的,通过反射可以读取到注解的信息


SpringBoot启动时只能扫描当前包以及子包下的类


有些组件/Bean需要复用时,可能是以jar的形式导入的,此时可以在启动类增加@ComponentScan 并指定扫描位置


19、策略模式的几种实现方案


1、切换 Bean 的name


2、使用@Qualifier 注解指定注入的Bean


3、有选择地只注入一个Bean,注释掉某个Bean上的@Component注解


4、使用@Primary注解 提高Bean注入的优先级


20、条件注解@Conditional


顾名思义,当满足某种条件的时候,IOC 才会加载Bean


通过@Conditional + Condition 可以完成条件注解


将 9 中的 HeroConfiguration  更改一下


@Configuration
public class HeroConfiguration {
    @Bean
    public ISkill diana(){
        return new Diana("Diana", 1);
    }
    @Bean
    public ISkill irelia(){
        return new Irelia();
    }
}


Irelia


//@Component
public class Irelia implements ISkill{
    public Irelia() {
        System.out.println("Hello, phubing");
    }
    @Override
    public void q() {
        System.out.println("Irelia 释放了 Q 技能");
    }
    @Override
    public void w() {
        System.out.println("Irelia 释放了 W 技能");
    }
    @Override
    public void e() {
        System.out.println("Irelia 释放了 E 技能");
    }
    @Override
    public void r() {
        System.out.println("Irelia 释放了 R 技能");
    }
}


Diana


@Data
//@Component
@AllArgsConstructor
public class Diana implements ISkill{
    private String name;
    private Integer age;
    public Diana() {
        System.out.println("Hello, phubing");
    }
    @Override
    public void q() {
        System.out.println("Diana 释放了 Q 技能");
    }
    @Override
    public void w() {
        System.out.println("Diana 释放了 W 技能");
    }
    @Override
    public void e() {
        System.out.println("Diana 释放了 E 技能");
    }
    @Override
    public void r() {
        System.out.println("Diana 释放了 R 技能");
    }
}


此时,如果不加条件,那么两个Bean都将加入到IOC中


@Conditional() 需要返回一个元类(条件类)


DianaCondition


public class DianaCondition implements Condition {
    /**
     * 此方法返回true时,满足条件的就会被加入到IOC中
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return false;
    }
}


HeroConfiguration


@Configuration
public class HeroConfiguration {
    @Bean
    @Conditional(DianaCondition.class)
    public ISkill diana(){
        return new Diana("Diana", 1);
    }
    @Bean
    public ISkill irelia(){
        return new Irelia();
    }
}


此时运行就不会有问题了,因为Diana不满足,直接返回的false


21、Condition 接口的 ConditionContext 参数


新增一个Condition:IreliaCondition


public class IreliaCondition implements Condition {
    /**
     * 此方法返回true时,满足条件的就会被加入到IOC中
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return true;
    }
}


application.properties


hero.condition = irelia


通过 ConditionContext  可以获取非常丰富的信息,例如所有的配置信息、所有的Bean、资源加载的信息


IOC 在加载容器的时候并非直接把Bean实例化,当IOC要去加载Bean的时候,首先要生成一个Bean定义,通过Bean定义再来实例化Bean


DianaCondition
public class DianaCondition implements Condition {
    /**
     * 此方法返回true时,满足条件的就会被加入到IOC中
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String name = conditionContext.getEnvironment().getProperty("hero.condition");
        return "diana".equalsIgnoreCase(name);
    }
}


IreliaCondition


public class IreliaCondition implements Condition {
    /**
     * 此方法返回true时,满足条件的就会被加入到IOC中
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String name = conditionContext.getEnvironment().getProperty("hero.condition");
        return "irelia".equalsIgnoreCase(name);
    }
}


任何@Component 、@Configuration 以及其衍生类都可以追加条件注解


22、@ConditionalOnProperty成品条件注解


更新一下:HeroConfiguration,Diana 与 Irelia 的@Component 需要加上


@Configuration
public class HeroConfiguration {
    @Bean
    @ConditionalOnProperty(value = "hero.condition", havingValue = "diana")
    //@Conditional(DianaCondition.class)
    public ISkill diana(){
        return new Diana("Diana", 1);
    }
    @Bean
    @ConditionalOnProperty(value = "hero.condition", havingValue = "irelia")
    //@Conditional(IreliaCondition.class)
    public ISkill irelia(){
        return new Irelia();
    }
}


matchIfMissing 参数:如果Condition 没有读取到对应值(并非条件不成立),那么该参数为true的Bean将会被注入到IOC中,也即指定一个默认值


23、@ConditionalOnBean


当SpringIOC容器内存在指定的Bean的条件


更改 HeroConfiguration  


@Configuration
public class HeroConfiguration {
    @Bean
    @ConditionalOnBean(name = "mysql")
    public ISkill diana(){
        return new Diana("Diana", 1);
    }
    @Bean
    public ISkill irelia(){
        return new Irelia();
    }
}


如果IOC存在mysql这样一个Bean,那么Diana也会随之被加入到IOC中


24、与@ConditionalOnBean相反的注解:@ConditionalOnMissingBean


如果不存在该注解的value指定的Bean,则加了此注解的Bean将会被加入到IOC中


为了保证同一个实现只有一个相同类型的Bean


Bean与Bean之间很有可能存在依赖,比如常规的Controller类,加了RestController,而RestController中包含了@Component,那Controller类就会被当做一个Bean注入到IOC中;同时Controller类还会依赖别的Bean(例如Service接口实现类)

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
8天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
26 0
|
1月前
|
前端开发 Java
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
文章通过一个表白墙/留言墙的初级SpringBoot项目实例,详细讲解了如何进行前后端开发,包括定义前后端交互接口、创建SpringBoot项目、编写前端页面、后端代码逻辑及实体类封装的全过程。
74 3
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
1月前
|
前端开发 Java 数据安全/隐私保护
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
文章通过一个简单的SpringBoot项目,详细介绍了前后端如何实现用户登录功能,包括前端登录页面的创建、后端登录逻辑的处理、使用session验证用户身份以及获取已登录用户信息的方法。
176 2
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的医院门诊预约挂号系统
基于Java+Springboot+Vue开发的医院门诊预约挂号系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的门诊预约挂号管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
136 2
基于Java+Springboot+Vue开发的医院门诊预约挂号系统
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
30 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
19天前
|
JavaScript 前端开发 Java
SpringBoot_web开发-webjars&静态资源映射规则
https://www.91chuli.com/ 举例:jquery前端框架
16 0
|
1月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
43 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
1月前
|
开发框架 Java API
「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架
「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架
53 0
|
1月前
|
机器学习/深度学习 移动开发 自然语言处理
基于人工智能技术的智能导诊系统源码,SpringBoot作为后端服务的框架,提供快速开发,自动配置和生产级特性
当身体不适却不知该挂哪个科室时,智能导诊系统应运而生。患者只需选择不适部位和症状,系统即可迅速推荐正确科室,避免排错队浪费时间。该系统基于SpringBoot、Redis、MyBatis Plus等技术架构,支持多渠道接入,具备自然语言理解和多输入方式,确保高效精准的导诊体验。无论是线上医疗平台还是大型医院,智能导诊系统均能有效优化就诊流程。
|
1月前
|
JavaScript 前端开发 数据可视化
【SpringBoot+Vue项目实战开发】2020实时更新。。。。。。
【SpringBoot+Vue项目实战开发】2020实时更新。。。。。。
48 0