diff --git a/README.md b/README.md index 8a42f115..ae9d585f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ -## Java +## Java(必看:+1: ) ### 基础 @@ -156,12 +156,11 @@ ## 系统设计 -### 编码规范 +### 编码之道(必看 :+1:) -1. [RestFul API 简明教程](docs/system-design/restful-api.md) +1. [RestFul API 简明教程](docs/system-design/coding-way/RESTfulAPI简明教程.md) 2. [Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md) 3. [Java 命名之道](docs/system-design/naming.md) -4. [设计模式系列文章](docs/system-design/设计模式.md) ### 常用框架 @@ -176,11 +175,9 @@ **重要知识点详解:** -1. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/spring-annotations.md)** -2. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)** +1. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md)** (必看 :+1:) +2. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)** (必看 :+1:) 3. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md) -4. [Spring 中 Bean 的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md) -5. [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md) #### MyBatis diff --git a/docs/system-design/restful-api.md b/docs/system-design/coding-way/RESTfulAPI简明教程.md similarity index 100% rename from docs/system-design/restful-api.md rename to docs/system-design/coding-way/RESTfulAPI简明教程.md diff --git a/docs/system-design/framework/spring/SpringBean.md b/docs/system-design/framework/spring/SpringBean.md deleted file mode 100644 index 3291e7fa..00000000 --- a/docs/system-design/framework/spring/SpringBean.md +++ /dev/null @@ -1,451 +0,0 @@ - - -- [前言](#前言) -- [一 bean的作用域](#一-bean的作用域) - - [1. singleton——唯一 bean 实例](#1-singleton——唯一-bean-实例) - - [2. prototype——每次请求都会创建一个新的 bean 实例](#2-prototype——每次请求都会创建一个新的-bean-实例) - - [3. request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效](#3-request——每一次http请求都会产生一个新的bean,该bean仅在当前http-request内有效) - - [4. session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效](#4-session——每一次http请求都会产生一个新的-bean,该bean仅在当前-http-session-内有效) - - [5. globalSession](#5-globalsession) -- [二 bean的生命周期](#二-bean的生命周期) - - [initialization 和 destroy](#initialization-和-destroy) - - [实现*Aware接口 在Bean中使用Spring框架的一些对象](#实现aware接口-在bean中使用spring框架的一些对象) - - [BeanPostProcessor](#beanpostprocessor) - - [总结](#总结) - - [单例管理的对象](#单例管理的对象) - - [非单例管理的对象](#非单例管理的对象) -- [三 说明](#三-说明) - - - -# 前言 -在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。 - -**Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?** 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? **Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。** - -在大多数情况下。单例 bean 是很理想的方案。不过,有时候你可能会发现你所使用的类是易变的,它们会保持一些状态,因此重用是不安全的。在这种情况下,将 class 声明为单例的就不是那么明智了。因为对象会被污染,稍后重用的时候会出现意想不到的问题。所以 Spring 定义了多种作用域的bean。 - -# 一 bean的作用域 - -创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。Spring Framework支持五种作用域,分别阐述如下表。 - - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/1188352.jpg) - -五种作用域中,**request、session** 和 **global session** 三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。 - - - -### 1. singleton——唯一 bean 实例 - -**当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。** singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,但我们可以指定Bean节点的 `lazy-init=”true”` 来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。注意,singleton 作用域是Spring中的缺省作用域。要在XML中将 bean 定义成 singleton ,可以这样配置: - -```xml - -``` - -也可以通过 `@Scope` 注解(它可以显示指定bean的作用范围。)的方式 - -```java -@Service -@Scope("singleton") -public class ServiceImpl{ - -} -``` - -### 2. prototype——每次请求都会创建一个新的 bean 实例 - -**当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。** **prototype 作用域的 bean 会导致在每次对该 bean 请求**(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法**)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。** 在 XML 中将 bean 定义成 prototype ,可以这样配置: - -```java - - 或者 - -``` -通过 `@Scope` 注解的方式实现就不做演示了。 - -### 3. request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效 - -**request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。** 在 XML 中将 bean 定义成 request ,可以这样配置: - -```java - -``` - -### 4. session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效 - -**session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。** - -```xml - -``` - -### 5. globalSession - -global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portle t所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。 - -```xml - -``` - -# 二 bean的生命周期 - -Spring Bean是Spring应用中最最重要的部分了。所以来看看Spring容器在初始化一个bean的时候会做那些事情,顺序是怎样的,在容器关闭的时候,又会做哪些事情。 - -> spring版本:4.2.3.RELEASE -鉴于Spring源码是用gradle构建的,我也决定舍弃我大maven,尝试下洪菊推荐过的gradle。运行beanLifeCycle模块下的junit test即可在控制台看到如下输出,可以清楚了解Spring容器在创建,初始化和销毁Bean的时候依次做了那些事情。 - -``` -Spring容器初始化 -===================================== -调用GiraffeService无参构造函数 -GiraffeService中利用set方法设置属性值 -调用setBeanName:: Bean Name defined in context=giraffeService -调用setBeanClassLoader,ClassLoader Name = sun.misc.Launcher$AppClassLoader -调用setBeanFactory,setBeanFactory:: giraffe bean singleton=true -调用setEnvironment -调用setResourceLoader:: Resource File Name=spring-beans.xml -调用setApplicationEventPublisher -调用setApplicationContext:: Bean Definition Names=[giraffeService, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#0, com.giraffe.spring.service.GiraffeServicePostProcessor#0] -执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=giraffeService -调用PostConstruct注解标注的方法 -执行InitializingBean接口的afterPropertiesSet方法 -执行配置的init-method -执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=giraffeService -Spring容器初始化完毕 -===================================== -从容器中获取Bean -giraffe Name=李光洙 -===================================== -调用preDestroy注解标注的方法 -执行DisposableBean接口的destroy方法 -执行配置的destroy-method -Spring容器关闭 -``` - -先来看看,Spring在Bean从创建到销毁的生命周期中可能做得事情。 - - -### initialization 和 destroy - -有时我们需要在Bean属性值set好之后和Bean销毁之前做一些事情,比如检查Bean中某个属性是否被正常的设置好值了。Spring框架提供了多种方法让我们可以在Spring Bean的生命周期中执行initialization和pre-destroy方法。 - -**1.实现InitializingBean和DisposableBean接口** - -这两个接口都只包含一个方法。通过实现InitializingBean接口的afterPropertiesSet()方法可以在Bean属性值设置好之后做一些操作,实现DisposableBean接口的destroy()方法可以在销毁Bean之前做一些操作。 - -例子如下: - -```java -public class GiraffeService implements InitializingBean,DisposableBean { - @Override - public void afterPropertiesSet() throws Exception { - System.out.println("执行InitializingBean接口的afterPropertiesSet方法"); - } - @Override - public void destroy() throws Exception { - System.out.println("执行DisposableBean接口的destroy方法"); - } -} -``` -这种方法比较简单,但是不建议使用。因为这样会将Bean的实现和Spring框架耦合在一起。 - -**2.在bean的配置文件中指定init-method和destroy-method方法** - -Spring允许我们创建自己的 init 方法和 destroy 方法,只要在 Bean 的配置文件中指定 init-method 和 destroy-method 的值就可以在 Bean 初始化时和销毁之前执行一些操作。 - -例子如下: - -```java -public class GiraffeService { - //通过的destroy-method属性指定的销毁方法 - public void destroyMethod() throws Exception { - System.out.println("执行配置的destroy-method"); - } - //通过的init-method属性指定的初始化方法 - public void initMethod() throws Exception { - System.out.println("执行配置的init-method"); - } -} -``` - -配置文件中的配置: - -``` - - -``` - -需要注意的是自定义的init-method和destroy-method方法可以抛异常但是不能有参数。 - -这种方式比较推荐,因为可以自己创建方法,无需将Bean的实现直接依赖于spring的框架。 - -**3.使用@PostConstruct和@PreDestroy注解** - -除了xml配置的方式,Spring 也支持用 `@PostConstruct`和 `@PreDestroy`注解来指定 `init` 和 `destroy` 方法。这两个注解均在`javax.annotation` 包中。为了注解可以生效,需要在配置文件中定义org.springframework.context.annotation.CommonAnnotationBeanPostProcessor或context:annotation-config - -例子如下: - -```java -public class GiraffeService { - @PostConstruct - public void initPostConstruct(){ - System.out.println("执行PostConstruct注解标注的方法"); - } - @PreDestroy - public void preDestroy(){ - System.out.println("执行preDestroy注解标注的方法"); - } -} -``` - -配置文件: - -```xml - - - -``` - -### 实现*Aware接口 在Bean中使用Spring框架的一些对象 - -有些时候我们需要在 Bean 的初始化中使用 Spring 框架自身的一些对象来执行一些操作,比如获取 ServletContext 的一些参数,获取 ApplicaitionContext 中的 BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean 可以获取到框架自身的一些对象,Spring 提供了一组名为*Aware的接口。 - -这些接口均继承于`org.springframework.beans.factory.Aware`标记接口,并提供一个将由 Bean 实现的set*方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。 -网上说,这些接口是利用观察者模式实现的,类似于servlet listeners,目前还不明白,不过这也不在本文的讨论范围内。 -介绍一些重要的Aware接口: - -- **ApplicationContextAware**: 获得ApplicationContext对象,可以用来获取所有Bean definition的名字。 -- **BeanFactoryAware**:获得BeanFactory对象,可以用来检测Bean的作用域。 -- **BeanNameAware**:获得Bean在配置文件中定义的名字。 -- **ResourceLoaderAware**:获得ResourceLoader对象,可以获得classpath中某个文件。 -- **ServletContextAware**:在一个MVC应用中可以获取ServletContext对象,可以读取context中的参数。 -- **ServletConfigAware**: 在一个MVC应用中可以获取ServletConfig对象,可以读取config中的参数。 - -```java -public class GiraffeService implements ApplicationContextAware, - ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware, - BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{ - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - System.out.println("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName()); - } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - System.out.println("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService")); - } - @Override - public void setBeanName(String s) { - System.out.println("执行setBeanName:: Bean Name defined in context=" - + s); - } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - System.out.println("执行setApplicationContext:: Bean Definition Names=" - + Arrays.toString(applicationContext.getBeanDefinitionNames())); - } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - System.out.println("执行setApplicationEventPublisher"); - } - @Override - public void setEnvironment(Environment environment) { - System.out.println("执行setEnvironment"); - } - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - Resource resource = resourceLoader.getResource("classpath:spring-beans.xml"); - System.out.println("执行setResourceLoader:: Resource File Name=" - + resource.getFilename()); - } - @Override - public void setImportMetadata(AnnotationMetadata annotationMetadata) { - System.out.println("执行setImportMetadata"); - } -} -``` - -### BeanPostProcessor - -上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程, -Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行。 - -例子如下: - -```java -public class CustomerBeanPostProcessor implements BeanPostProcessor { - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName); - return bean; - } - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName); - return bean; - } -} -``` - -要将BeanPostProcessor的Bean像其他Bean一样定义在配置文件中 - -```xml - -``` - -### 总结 - -所以。。。结合第一节控制台输出的内容,Spring 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属性,执行指定的方法。 - -用图表示一下(图来源:http://www.jianshu.com/p/d00539babca5): - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/48376272.jpg) - -与之比较类似的中文版本: - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/5496407.jpg) - - -**其实很多时候我们并不会真的去实现上面说描述的那些接口,那么下面我们就除去那些接口,针对bean的单例和非单例来描述下bean的生命周期:** - -### 单例管理的对象 - -当scope=”singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置: - -```xml - -``` - -如果想对所有的默认单例bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示: - -```xml - -``` - -默认情况下,Spring 在读取 xml 文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用 init-method 属性值中所指定的方法。对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下: - -```java -public class LifeBean { - private String name; - - public LifeBean(){ - System.out.println("LifeBean()构造函数"); - } - public String getName() { - return name; - } - - public void setName(String name) { - System.out.println("setName()"); - this.name = name; - } - - public void init(){ - System.out.println("this is init of lifeBean"); - } - - public void destory(){ - System.out.println("this is destory of lifeBean " + this); - } -} -``` - life.xml配置如下: - -```xml - -``` - -测试代码: - -```java -public class LifeTest { - @Test - public void test() { - AbstractApplicationContext container = - new ClassPathXmlApplicationContext("life.xml"); - LifeBean life1 = (LifeBean)container.getBean("life"); - System.out.println(life1); - container.close(); - } -} -``` - -运行结果: - -``` -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@573f2bb1 -…… -this is destory of lifeBean com.bean.LifeBean@573f2bb1 -``` - -### 非单例管理的对象 - -当`scope=”prototype”`时,容器也会延迟初始化 bean,Spring 读取xml 文件的时候,并不会立刻创建对象,而是在第一次请求该 bean 时才初始化(如调用getBean方法时)。在第一次请求每一个 prototype 的bean 时,Spring容器都会调用其构造器创建这个对象,然后调用`init-method`属性值中所指定的方法。对象销毁的时候,Spring 容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。 - -为了测试prototype bean的生命周期life.xml配置如下: - -```xml - -``` - -测试程序: - -```java -public class LifeTest { - @Test - public void test() { - AbstractApplicationContext container = new ClassPathXmlApplicationContext("life.xml"); - LifeBean life1 = (LifeBean)container.getBean("life_singleton"); - System.out.println(life1); - - LifeBean life3 = (LifeBean)container.getBean("life_prototype"); - System.out.println(life3); - container.close(); - } -} -``` - -运行结果: - -``` -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@573f2bb1 -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@5ae9a829 -…… -this is destory of lifeBean com.bean.LifeBean@573f2bb1 -``` - -可以发现,对于作用域为 prototype 的 bean ,其`destroy`方法并没有被调用。**如果 bean 的 scope 设为prototype时,当容器关闭时,`destroy` 方法不会被调用。对于 prototype 作用域的 bean,有一点非常重要,那就是 Spring不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。** 不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。**清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责**(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用)。谈及prototype作用域的bean时,在某些方面你可以将Spring容器的角色看作是Java new操作的替代者,任何迟于该时间点的生命周期事宜都得交由客户端来处理。 - -**Spring 容器可以管理 singleton 作用域下 bean 的生命周期,在此作用域下,Spring 能够精确地知道bean何时被创建,何时初始化完成,以及何时被销毁。而对于 prototype 作用域的bean,Spring只负责创建,当容器创建了 bean 的实例后,bean 的实例就交给了客户端的代码管理,Spring容器将不再跟踪其生命周期,并且不会管理那些被配置成prototype作用域的bean的生命周期。** - - -# 三 说明 - -本文的完成结合了下面两篇文章,并做了相应修改: - -- https://blog.csdn.net/fuzhongmin05/article/details/73389779 -- https://yemengying.com/2016/07/14/spring-bean-life-cycle/ - -由于本文非本人独立原创,所以未声明为原创!在此说明! diff --git a/docs/system-design/framework/spring/spring-annotations.md b/docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md similarity index 99% rename from docs/system-design/framework/spring/spring-annotations.md rename to docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md index 610c0908..0cdfb260 100644 --- a/docs/system-design/framework/spring/spring-annotations.md +++ b/docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md @@ -315,7 +315,7 @@ public class UserRegisterRequest { 这样我们的后端就可以直接把 json 格式的数据映射到我们的 `UserRegisterRequest` 类上。 -![](https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-08/663d1ec1-7ebc-41ab-8431-159dc1ec6589.png) +![](images/spring-annotations/@RequestBody.png) 👉 需要注意的是:**一个请求方法只可以有一个`@RequestBody`,但是可以有多个`@RequestParam`和`@PathVariable`**。 如果你的方法必须要用两个 `@RequestBody`来接受数据的话,大概率是你的数据库设计或者系统设计出问题了! diff --git a/docs/system-design/framework/spring/SpringMVC-Principle.md b/docs/system-design/framework/spring/SpringMVC-Principle.md deleted file mode 100644 index 0a2cc5f4..00000000 --- a/docs/system-design/framework/spring/SpringMVC-Principle.md +++ /dev/null @@ -1,269 +0,0 @@ -> 本文整理自网络,原文出处暂不知,对原文做了较大的改动,在此说明! - -### 先来看一下什么是 MVC 模式 - -MVC 是一种设计模式. - -**MVC 的原理图如下:** - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/60679444.jpg) - - - -### SpringMVC 简单介绍 - -SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是 DispatcherServlet,它是一个 Servlet,顶层是实现的Servlet接口。 - -### SpringMVC 使用 - -需要在 web.xml 中配置 DispatcherServlet 。并且需要配置 Spring 监听器ContextLoaderListener - -```xml - - - org.springframework.web.context.ContextLoaderListener - - - - springmvc - org.springframework.web.servlet.DispatcherServlet - - - - contextConfigLocation - classpath:spring/springmvc-servlet.xml - - 1 - - - springmvc - / - - -``` - -### SpringMVC 工作原理(重要) - -**简单来说:** - -客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器来处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Model)->将得到视图对象返回给用户 - - - -**如下图所示:** -![SpringMVC运行原理](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49790288.jpg) - -上图的一个笔误的小问题:Spring MVC 的入口函数也就是前端控制器 DispatcherServlet 的作用是接收请求,响应结果。 - -**流程说明(重要):** - -(1)客户端(浏览器)发送请求,直接请求到 DispatcherServlet。 - -(2)DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。 - -(3)解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。 - -(4)HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑。 - -(5)处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。 - -(6)ViewResolver 会根据逻辑 View 查找实际的 View。 - -(7)DispaterServlet 把返回的 Model 传给 View(视图渲染)。 - -(8)把 View 返回给请求者(浏览器) - - - -### SpringMVC 重要组件说明 - - -**1、前端控制器DispatcherServlet(不需要工程师开发),由框架提供(重要)** - -作用:**Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。** - -**2、处理器映射器HandlerMapping(不需要工程师开发),由框架提供** - -作用:根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler即处理器(Controller),SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 - -**3、处理器适配器HandlerAdapter** - -作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler -通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 - -**4、处理器Handler(需要工程师开发)** - -注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler -Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 -由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。 - -**5、视图解析器View resolver(不需要工程师开发),由框架提供** - -作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) -View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。 -一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。 - -**6、视图View(需要工程师开发)** - -View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...) - -**注意:处理器Handler(也就是我们平常说的Controller控制器)以及视图层view都是需要我们自己手动开发的。其他的一些组件比如:前端控制器DispatcherServlet、处理器映射器HandlerMapping、处理器适配器HandlerAdapter等等都是框架提供给我们的,不需要自己手动开发。** - -### DispatcherServlet详细解析 - -首先看下源码: - -```java -package org.springframework.web.servlet; - -@SuppressWarnings("serial") -public class DispatcherServlet extends FrameworkServlet { - - public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; - public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; - public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; - public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; - public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; - public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; - public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; - public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; - public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager"; - public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; - public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; - public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; - public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; - public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; - public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; - public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; - public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION"; - public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; - private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; - protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); - private static final Properties defaultStrategies; - static { - try { - ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); - defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); - } - catch (IOException ex) { - throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); - } - } - - /** Detect all HandlerMappings or just expect "handlerMapping" bean? */ - private boolean detectAllHandlerMappings = true; - - /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */ - private boolean detectAllHandlerAdapters = true; - - /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */ - private boolean detectAllHandlerExceptionResolvers = true; - - /** Detect all ViewResolvers or just expect "viewResolver" bean? */ - private boolean detectAllViewResolvers = true; - - /** Throw a NoHandlerFoundException if no Handler was found to process this request? **/ - private boolean throwExceptionIfNoHandlerFound = false; - - /** Perform cleanup of request attributes after include request? */ - private boolean cleanupAfterInclude = true; - - /** MultipartResolver used by this servlet */ - private MultipartResolver multipartResolver; - - /** LocaleResolver used by this servlet */ - private LocaleResolver localeResolver; - - /** ThemeResolver used by this servlet */ - private ThemeResolver themeResolver; - - /** List of HandlerMappings used by this servlet */ - private List handlerMappings; - - /** List of HandlerAdapters used by this servlet */ - private List handlerAdapters; - - /** List of HandlerExceptionResolvers used by this servlet */ - private List handlerExceptionResolvers; - - /** RequestToViewNameTranslator used by this servlet */ - private RequestToViewNameTranslator viewNameTranslator; - - private FlashMapManager flashMapManager; - - /** List of ViewResolvers used by this servlet */ - private List viewResolvers; - - public DispatcherServlet() { - super(); - } - - public DispatcherServlet(WebApplicationContext webApplicationContext) { - super(webApplicationContext); - } - @Override - protected void onRefresh(ApplicationContext context) { - initStrategies(context); - } - - protected void initStrategies(ApplicationContext context) { - initMultipartResolver(context); - initLocaleResolver(context); - initThemeResolver(context); - initHandlerMappings(context); - initHandlerAdapters(context); - initHandlerExceptionResolvers(context); - initRequestToViewNameTranslator(context); - initViewResolvers(context); - initFlashMapManager(context); - } -} - -``` - -DispatcherServlet类中的属性beans: - -- HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。 -- HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序。- - - -- ViewResolver:根据实际配置解析实际的View类型。 -- ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局。 -- MultipartResolver:解析多部分请求,以支持从HTML表单上传文件。- -- FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向。 - -在Web MVC框架中,每个DispatcherServlet都拥自己的WebApplicationContext,它继承了ApplicationContext。WebApplicationContext包含了其上下文和Servlet实例之间共享的所有的基础框架beans。 - -**HandlerMapping** - -![HandlerMapping](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/96666164.jpg) - -HandlerMapping接口处理请求的映射HandlerMapping接口的实现类: - -- SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。 -- DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类。 - -**HandlerAdapter** - - -![HandlerAdapter](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/91433100.jpg) - -HandlerAdapter接口-处理请求映射 - -AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上。 - -**HandlerExceptionResolver** - - -![HandlerExceptionResolver](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/50343885.jpg) - -HandlerExceptionResolver接口-异常处理接口 - -- SimpleMappingExceptionResolver通过配置文件进行异常处理。 -- AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。 - -**ViewResolver** - -![ViewResolver](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49497279.jpg) - -ViewResolver接口解析View视图。 - -UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。 diff --git a/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png b/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png new file mode 100644 index 00000000..75d43a72 Binary files /dev/null and b/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png differ diff --git a/docs/system-design/设计模式.md b/docs/system-design/设计模式.md deleted file mode 100644 index c7a86716..00000000 --- a/docs/system-design/设计模式.md +++ /dev/null @@ -1,84 +0,0 @@ -# Java 设计模式 - -下面是自己学习设计模式的时候做的总结,有些是自己的原创文章,有些是网上写的比较好的文章,保存下来细细消化吧! - -**系列文章推荐:** - -## 创建型模式 - -### 创建型模式概述 - -- 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。 -- 创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。 - -![创建型模式](https://user-gold-cdn.xitu.io/2018/6/16/1640641afcb7559b?w=491&h=241&f=png&s=51443) - -### 常见创建型模式详解 - -- **单例模式:** [深入理解单例模式——只有一个实例](https://blog.csdn.net/qq_34337272/article/details/80455972) -- **工厂模式:** [深入理解工厂模式——由对象工厂生成对象](https://blog.csdn.net/qq_34337272/article/details/80472071) -- **建造者模式:** [深入理解建造者模式 ——组装复杂的实例](http://blog.csdn.net/qq_34337272/article/details/80540059) -- **原型模式:** [深入理解原型模式 ——通过复制生成实例](https://blog.csdn.net/qq_34337272/article/details/80706444) - -## 结构型模式 - -### 结构型模式概述 - -- **结构型模式(Structural Pattern):** 描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构 -![结构型模式(Structural Pattern)](https://user-gold-cdn.xitu.io/2018/6/16/164064d6b3c205e3?w=719&h=233&f=png&s=270293) -- **结构型模式可以分为类结构型模式和对象结构型模式:** - - 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。 - - 对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。 - -![结构型模式](https://user-gold-cdn.xitu.io/2018/6/16/1640655459d766d2?w=378&h=266&f=png&s=59652) - -### 常见结构型模式详解 - -- **适配器模式:** - - [深入理解适配器模式——加个“适配器”以便于复用](https://segmentfault.com/a/1190000011856448) https://blog.csdn.net/carson_ho/article/details/54910430 - - [适配器模式原理及实例介绍-IBM](https://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html) -- **桥接模式:** [设计模式笔记16:桥接模式(Bridge Pattern)](https://blog.csdn.net/yangzl2008/article/details/7670996) -- **组合模式:** [大话设计模式—组合模式](https://blog.csdn.net/lmb55/article/details/51039781) -- **装饰模式:** [java模式—装饰者模式](https://www.cnblogs.com/chenxing818/p/4705919.html)、[Java设计模式-装饰者模式](https://blog.csdn.net/cauchyweierstrass/article/details/48240147) -- **外观模式:** [java设计模式之外观模式(门面模式)](https://www.cnblogs.com/lthIU/p/5860607.html) -- **享元模式:** [享元模式](http://www.jasongj.com/design_pattern/flyweight/) -- **代理模式:** - - [代理模式原理及实例讲解 (IBM出品,很不错)](https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/index.html) - - [轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350) - - [Java代理模式及其应用](https://blog.csdn.net/justloveyou_/article/details/74203025) - - -## 行为型模式 - -### 行为型模式概述 - -- 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。 -- 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。 -- 通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。 - -**行为型模式分为类行为型模式和对象行为型模式两种:** - -- **类行为型模式:** 类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。 -- **对象行为型模式:** 对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。 - -![行为型模式](https://user-gold-cdn.xitu.io/2018/6/28/164467dd92c6172c?w=453&h=269&f=png&s=63270) - -- **职责链模式:** -- [Java设计模式之责任链模式、职责链模式](https://blog.csdn.net/jason0539/article/details/45091639) -- [责任链模式实现的三种方式](https://www.cnblogs.com/lizo/p/7503862.html) -- **命令模式:** 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。 -- **解释器模式:** -- **迭代器模式:** -- **中介者模式:** -- **备忘录模式:** -- **观察者模式:** -- **状态模式:** -- **策略模式:** - -策略模式作为设计原则中开闭原则最典型的体现,也是经常使用的。下面这篇博客介绍了策略模式一般的组成部分和概念,并用了一个小demo去说明了策略模式的应用。 - -[java设计模式之策略模式](https://blog.csdn.net/zlj1217/article/details/81230077) - -- **模板方法模式:** -- **访问者模式:** -