【Springboot】关于Spring和SpringBoot的那些事儿

参考javaguide的学习笔记~

1 怎么那么多名字呀~

一开始看到这个图太劝退了,但实际上一开始只需要理解它是一个框架,包含很多个模块,能够简化开发。

使得接收HTTP请求,查数据库,写业务逻辑三部分能够分开。

并且能很方便地完成一些共同的逻辑。

【Springboot】关于Spring和SpringBoot的那些事儿_第1张图片

2 IOC

2.1 Why?为什么要交给外部管理?

IoC(Inversion of Control)的思想是,将原本在程序中手动创建对象的控制权,交给Spring框架来管理。

  • 控制:某一个类A创建另一个类的对象B,并对其进行管理的权力
  • 反转:把这种控制权交给外部环境,示例化和管理由外部环境完成(Spring框架、IoC容器)

【Springboot】关于Spring和SpringBoot的那些事儿_第2张图片

如上图所示,传统的开发方式往往是在类A中通过new关键字来new一个B的对象出来。但按IoC的思想,IoC容器可以帮我们完成实例化对象,我们不必一遍又一遍地生产出Wheel类的示例,要使用某个对象时,直接从IoC容器里拿出来即可。

【Springboot】关于Spring和SpringBoot的那些事儿_第3张图片

比如,考虑这样一种情况,假如我们的车厂跟旧的车轮供应商闹掰了,想要把所有的轮子,全换成另一个供应商的轮子。按照旧的实现方式,我们需要把车厂中的所有车的旧轮子实例化改过来。

如果有很多个类都引用了OldWheel的具体实例化,修改起来将会非常复杂和痛苦:你不仅需要知道哪些地方引用了OldWheel,还需要知道NewWheel的构造函数需要什么参数,而更糟糕的是,这些参数有可能又有它们自己的构造函数。

【Springboot】关于Spring和SpringBoot的那些事儿_第4张图片

而使用IoC的思想,我们把对象的控制权交给IoC容器管理,在使用时直接向IoC要就好了,小车类中的代码没有改动与变化。它把你从复杂的依赖关系中拯救出来,你完全不用考虑对象是如何被创建的。

3 Spring Bean是什么东东?

简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。

我们需要通知 IoC 容器帮助我们管理哪些对象,这种“通知”可以通过 XML 文件、注解或者 Java 配置类实现。

【我还是没有搞清楚BeanFactory巴拉巴拉的工作流程,但是啊,但是,我感觉框架存在的意义,就是我没必要知道这些东西是怎么工作的啊】

3.1 将一个类声明为 Bean 的注解有哪些?

@Component@Repository@Service@Controller
  • @Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。

  • @Repository : 对应持久层 (Dao 层) ,主要用于数据库相关操作。可以看到其实现方式与@Component相同,只是明确这个类对数据库有CRUD的功能。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Repository {
        @AliasFor(
            annotation = Component.class
        )
        String value() default "";
    }
    
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。

  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

3.2 @Mapper和@Repository的区别?

@Mapper是Mybatis自带的一个注解。不需要在spring配置中设置扫描地址,通过mapper.xml里面的namespace属性对应相关的mapper类,Spring将动态生成Bean后注入到ServiceImpl中。@MapperScan 加在启动类上可以替代 @Mapper,把一个包里的内容都声明为Mapper

@Repository是Spring提供的一个注解,用于声明一个Bean。在直接使用JDBC开发时可能会用到(然鹅现在大多都直接MyBatis)。

简单理解就是:@Mapper = @MapperScan(自动扫描配置)+Repository(可省略)

3.3 @Component和@Bean的区别?

@Component 注解作用于类,而@Bean注解作用于方法。

  • @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径)。
  • @Bean 注解告诉了 Spring 这个方法将返回一个对象,这个对象要注册为Spring中的Bean,当我需要用它的时候还给我。

@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

@Bean注解使用示例,按条件注入组件:

 	@Configuration
    public class MyConfiguration {
        @Bean
        public User user() {
        	int i = 10;
        	if(i < 7) {
        		return new User("jack", 20);
      		} else {
      			return new User("david", 18);
      		}
        }
    }
	@Autowired
	private User user; // 将在IOC容器中寻找User对象

3.4 注入Bean的注解有哪些?

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource@Inject 都可以用于注入 Bean。

Annotaion Package Source
@Autowired org.springframework.bean.factory Spring 2.5+ byType
@Resource javax.annotation Java JSR-250 byName
@Inject javax.inject Java JSR-330

@Autowired@Resource使用的比较多一些。

3.5 @Autowired 和 @Resource 的区别是什么?

  • @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
  • Autowired 默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。
  • 当一个接口存在多个实现类的情况下,@Autowired@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。

1 @Autowired -> byType

Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。

这会有什么问题呢? 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。

这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService 就是我这里所说的名称,这样应该比较好理解了吧。

// smsService 就是我们上面所说的名称
@Autowired
private SmsService smsService;

举个例子,SmsService 接口有两个实现类: SmsServiceImpl1SmsServiceImpl2,且它们都已经被 Spring 容器所管理。

// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;

// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;

// 正确注入  SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

我们还是建议通过 @Qualifier 注解来显式指定名称而不是依赖变量的名称。

2 @Autowired -> byType

@Resource属于 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType

@Resource 有两个比较重要且日常开发常用的属性:name(名称)、type(类型)。

  • 如果仅指定 name 属性则注入方式为byName
  • 如果仅指定type属性则注入方式为byType
  • 如果同时指定nametype属性(不建议这么做)则注入方式为byType+byName
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;

// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;

// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

3.6 Bean 的作用域有哪些?

可以通过@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)注解配置Bean的作用域。

Spring 中 Bean 的作用域通常有下面几种:

  • singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
  • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
  • application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

3.7 Bean 的生命周期?

  • Bean 容器找到配置文件中 Spring Bean 的定义。
  • Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
  • 如果涉及到一些属性值 利用 set()方法设置一些属性值。
  • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
  • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
  • 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
  • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
  • 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
  • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
  • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

【Springboot】关于Spring和SpringBoot的那些事儿_第5张图片

4 Spring MVC的工作原理

Spring MVC 下我们一般把后端项目分为 :

  • Controller 层(控制层,返回数据给前台页面)
  • Service 层(处理业务)
  • Dao 层(数据库操作)
  • Entity 层(实体类)

【Springboot】关于Spring和SpringBoot的那些事儿_第6张图片

  1. 用户发送Request请求, 被前置控制器DispatcherServlet接收。

    • DispatcherServlet核心的中央处理器,接收请求、分发请求、响应结果,返回结果可以是json、String等数据类型,也可以是页面。
  2. DispatcherServlet 根据请求信息调用 HandlerMappingHandlerMapping 根据 url 去匹配查找能处理的 Handler(即Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。

    • mappedHandler = this.getHandler(processedRequest);
      
      HandlerExecutionChain handler = mapping.getHandler(request);
      
  3. DispatcherServlet 调用 HandlerAdapter适配器执行 Handler

    • 使用适配器就可以执行多种不同的Handler啦。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet

    • ModelAndView 包含了数据模型以及相应的视图的信息。

    • mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      
  5. 视图解析器ViewResolver 根据ModelAndView 来解析实际的 View,返回给DispatherServlet

    view = mv.getView();
    
  6. DispaterServlet 渲染 View并返回给请求者(浏览器)

    view.render(mv.getModelInternal(), request, response);
    

下图展示了调用某个Controller时的栈,可以看到上述提及的各个类。

【Springboot】关于Spring和SpringBoot的那些事儿_第7张图片

你可能感兴趣的:(Java,spring,spring,boot,java)