Spring学习

Spring学习

1.介绍

#优点:
1.Spring是一个开源的免费的容器框架
2.Spring是一个轻量级的、非入侵式的框架
3.Spring是一个IOC(DI)和AOP容器框架

#Springboot
1.一个快速开发的脚手架,快速开发单个微服务
2.约定大于配置
#Spring Cloud
1.SpringCloud激情与SpringBoot实现,是一个微服务治理组件

#组成
1. Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
2. Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
3. Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
4. 其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。

Spring学习_第1张图片

2.核心

#IOC 控制反转
原逻辑:程序主动创建对象,控制在程序员代码里
控制反转:使用set注入,程序不在主动创建对象,而是被动接收对象。大大降低耦合性
控制反转是通过DI(依赖注入)实现的,有三种注入方式:构造方法注入、set注入(最重要)、c命名空间和p命名空间注入(需要在xml文件中头添加相应约束)
##XML配置方式标签
<bean id="person" class="com.lmy.entity.Person">
      <property name="id" value="1"/>
      <property name="name" value="lmy"/>
      #有参构造配置
</bean>
###xml配置方式获取Spring上下文对象,也可以把context理解为一个容器。
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
#getBean会加载bean到容器中,
#即new一个对象到Spring中(如有其他bean,没有get也会同时加载,可以通过无参构造测试)
Person person = (Person) context.getBean("person");
System.out.println(person);
##IOC创建对象的方式
1.通过无参构造方法。(默认方式)
2.通过有参构造(没有无参构造)<bean>中使用<constructor-arg>配置
##Bean的作用域
1.Singleton 单例 2.prototype原型模式(多例) 3.request 4.session 5.application

##**********Bean的自动装配******************
1.含义:自动装配是Spring满足bean依赖的一种方式,Spring会在上下文中自动雪照并被bean装配属性
2.三种方式: xml中装配、java配置文件基于注解装配、隐式的自动装配(重要)
3.结合配置文件使用byName/byType方式自动装配
3.1 <bean id="person" class="com.lmy.entity.Person" autowire="byName/byType"></bean>
3.2 使用注解:@Autowired(只按照byType,Spring注解) 多个bean时结合@Qualifier注解指定name
3.3 使用@Resource(默认byName,找不到也可byType)注解 J2EE注解
使用注解时使用配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       #添加下面的约束#
       xmlns:context="http://www.springframework.org/schema/context"
       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
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 确定使用注解  -->
    <context:annotation-config></context:annotation-config>
    <!-- 自动扫描指定的包 -->
    <context:component-scan base-package="cn.spring.demo"></context:component-scan>
</beans>
3.4 注解说明
 @Component 放在类上,说明这个类被Spring管理了,相当于bean
 	衍生注解:@Controller @Service @Mapper
 @Nullable 字段标记了这个注解,说明这个字段可以为null
 @Value  相当于<property name="id" value="1"/>
 @Import 相当于配置文件中的<import>,代表着把多个配置类合并
4.代码方式进行装配(不再需要配置文件,SpringBoot的最常见方式)
@Configuration+@Bean注解配合使用,代表是一个配置类,方法注册为一个bean,方法返回值就是<bean>标签的class
//注解方式获取Spring上下文对象
ApplicationContext context1 = new AnnotationConfigApplicationContext("com.lmy.*");
Person person1 = (Person) context.getBean("getPerson");//getBean方法有@Bean注解
System.out.println(person1);

#AOP 面向切面编程
 ##代理模式:静态代理和动态代理:提取公共业务,方便扩展
 1.静态代理:抽象角色、代理角色、真实角色、用户角色;
 代理角色代理真实角色向用户角色提供使用,比真实角色增加附加功能
 2.动态代理:代理角色不用自己写,是动态角色(基于反射实现)
 	基于接口:JDK动态代理。基于类:CGLIB动态代理
 	java字节码:javasist JBoss容器使用,Tomcat不用
 2.1 JDK动态代理:InvocationHandler(实现了这个接口)、Proxy(提供静态方法创建代理对象)
 public class ProxyLearn implements InvocationHandler {
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行方法
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}
##AOP 核心:使用代理模式,代理对象为原来的业务。切面、切点
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>
1.xml配置(第一种自定义类;第二种定义一个切面类)
 <bean id="diy" class="com.diy.DiyPointcut">
    </bean>
    #第一种
    #
     #   
    #    
    #    
     #   
     #   
    #
    #第二种
    <aop:config>
        <!--自定义切面-->
         <!--配置aop-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.service.*.*(..))"/>
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
2.基于注解
@Aspect//标注这个类是一个切面
@Order(1)//越小优先级越大
@Component
public class Annotation1 {
    @Before("execution(* com.lmy.proxylearn.*.*(..))")
    public void before(){
        System.out.println("before");
    }
    @After("execution(* com.lmy.proxylearn.*.*(..))")
    public void after(){
        System.out.println("after");
    }
    //在环绕增强中,我们可以给一个参数,代表我们要获取切入的点
    @Around("execution(* com.lmy.proxylearn.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around");
        Object proceed = joinPoint.proceed();
        System.out.println("after around");
        return proceed;
    }
}

3.整合mybatis

<dependency>
     <groupId>mysqlgroupId>
     <artifactId>mysql-connector-javaartifactId>
     <version>5.1.47version>
dependency>
<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatis-springartifactId>
    <version>2.0.4version>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.2.3.RELEASEversion>
dependency>
<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.2version>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>

   <build>
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
            resource>
        resources>
    build>

<configuration>
    <typeAliases>
        <package name="com.pojo"/>
    typeAliases>
<settings>
        
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        
        <setting name="cacheEnabled" value="true"/>
    settings>
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC" />
            
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://192.168.6.128:3306/lmy_test" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper resource="mapper/test/testMapper.xml"/>
    mappers>
configuration>

   

 
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://111.230.212.103:3306/mybatis?userSSL=true&
                userUnicode=true&characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="hdk123"/>
    bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource" />
        
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/mapper/UserMapper.xml"/>
    bean>
    <bean id="userMapper2" class="com.mapper.UserMapperIml2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory">property>
    bean>

    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="datasource" />
    bean>

    
    
    <tx:advice id="tx1" transaction-manager="transactionManager">
        
        
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
        tx:attributes>
    tx:advice>

    
    <aop:config>
        <aop:pointcut id="txpointxut" expression="execution(* com.mapper.*.*(..))"/>
        <aop:advisor advice-ref="tx1" pointcut-ref="txpointxut"/>
    aop:config>

4.Spring相关面试题

1.Spring 如何创建bean对象的?Bean的生命周期?

#调用类的构造方法---->实例化对象(原对象)---->依赖注入---->初始化前---->初始化---->初始化后(AOP出代理对象,其他处理等)---->放入Map单例池---->Bean对象-->销毁
1.1 #调用类的构造方法:多个构造方法首先会判断是否有@Autowired指定构造方法,再看是否有无参构造方法,都没有则报错;只有一个有参的就会调用这个方法;调用有参时会传入参数对象,找到该对象是(先byType后byName)
1.2 #初始化前:@PostConstruct注解方法
1.3 #初始化 :有没有实现initializingBean接口进行自定义初始化
1.4 #初始化后:如果AOP产生了代理对象就会把代理对象放入单例池,没有则放入原对象;注意:生成的代理对象里面的@Autowired并没有没有值(即没有依赖注入到代理对象),而是实例化了一个原对象,直接调用对象的方法
1.5 # Map单例池:所以beanName是唯一的(一级缓存)
1.6 Spring的启动过程
#先进行扫描得到所有的BeanDefinition对象,并存入Map中;然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例的bean是每次使用的时候创建;通过BeanDefinition创建Bean就是Bean的生命周期,创建完成之后发布一个容器启动事件,则结束整个流程
	1.解析Bean配置信息,將配置信息转换为BeanDefinition对象,注册到BeanDefinitionRegistry中。
	2.执行所有的BeanFactoryPostProcessor的postProcessBeanFactory()方法对Bean工厂信息进行修改,包括修改或新增BeanDefinition对象。注意:如果需要控制BeanFactoryPostProcessor的执行顺序需要实现PriorityOrdered接口,getOrder()方法返回的值越小,执行优先级越高。
	3.通过BeanDefinition对象实例化所有Bean,注入依赖。
	4.执行所有BeanPostProcessor对象的postProcessBeforeInitialization()方法。
	5.执行Bean的初始化方法,例如InitializingBean接口的afterPropertiesSet方法,或init-method属性指定的方法。
	6.执行所有BeanPostProcessor对象的postProcessAfterInitialization()方法。


2.Spring怎么解决循环依赖?
#创建bean对象的第一步,使用有参构造方法时,需要一个bean,会在map单例池中寻找,找不到则创建,两个bean都需要另一个,即产生了循环依赖问题:①使用三级缓存解决循环依赖②使用@Lazy注解
2.1 为什么不用二级缓存解决
#二级缓存可以解决循环依赖的问题,但是不能解决aop场景下的循环依赖,这样注入到其他bean的都是原始对象,而不是最终的代理对象
2.2 什么是Spring的三级缓存
#三个Map:源码中保证单例使用的是DCL,双重检查锁
#一级缓存:singletonObjects  存储完整生命周期的Bean
#二级缓存:earlySingletonObjects 存储不完整的Bean并且保证Bean的单例问题
#三级缓存:singletonFactories  value是一个lambda表达式,如果普通对象需要AOP生成代理对象则返回代理对象,如果不需要则直接返回普通对象
#作用:解决循环依赖原理:判断Bean依赖注入时是否需要提前进行AOP,即new出来代理对象放入缓存,同时通过二级缓存初始化后判断是否仍需要AOP,如果提前AOP过,则不再进行。
2.3 @Async为什么会导致循环依赖无法解决
#@Async会生成另一个代理对象,导致两个代理对象,相当于破坏了单例
#解决:在循环依赖中加入@Lazy注解,就不存在循环依赖了(它会直接把代理对象在依赖注入时注入进来)
2.4 有参构造和多例导致的循环依赖?
# 原因:在实例化对象的时候和依赖注入的时候无法获取到循环依赖的Bean。
#解决: 使用@Lazy注解

3.Spring事务的原理
# Spring事务是基于AOP的,配置的事务管理器会新建一个连接,然后把autocommit设置为FALSE进行提交或回滚
4.Spring事务失效原因
#1.本身调用问题。2.异常被捕获。3.数据库不支持事务 4.方法不是public的。5.rollbackFor未捕获到异常。6.没有配置事务管理器

5.@Configuration注解作用是什么
#表明当前类是一个配置类,是方法bean的源:例如数据库配置类,jdbcTemplate Bean和事务管理器Bean都要使用dataSource的Bean,如果没有该注解,则jdbcTemplate Bean 和事务管理器Bean都要创建一个dataSource的Bean,导致在执行Sql的时候jdbcTemplate会在ThreadLocal中获取不到事务管理器创建的连接,而是自己创建一个,导致事务无法生效

6.Spring是什么
#Spring是一个轻量级的开源的容器框架,核心为IOC和AOP

7.Spring的事务传播机制
#7种机制:REQUIRED当前无事务,则新建;当前有事务则加入(默认)
#SUPPORTS 当前存在,则加入;当前不存在,则无事务运行
#MANDATORY 当前存在事务,则加入;不存在则抛出异常
#REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起事务(两个事务独立)
#NOT_SUPPORTED 以非事务运行,存在则挂起原事务
#NEVER 不使用事务 ,存在则抛出异常
#NESTED 如果当前存在事务,则在嵌套事务中执行,否则开启一个事务
7.1 NESTED和REQUIRES_NEW的区别
#REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
#在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务
7.3 NESTED和REQUIRED的区别
#REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚
#而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响

8.Spring中Bean是线程安全的吗?如果不安全要如何处理?
#Spring本身没有提供线程安全策略,因此其Bean存在线程不安全问题
#对于无状态Bean(Controller、Service、Dao)等Bean不保存数据,则线程可以算是安全的
#对于有状态的Bean(有数据存储功能)1.设置Bean为prototype 2.使用ThreadLocal为每个线程保存数据(空间换时间)3.加锁(时间换空间),高并发场景下实际上不可取
8.1 Spring中的Bean作用域
#五种:singleton  prototype  request session Global-session 

9.Spring框架中都使用了那些设计模式
#1.简单工厂(非23之一,具体可以参考23种设计模式详解)--BeanFactory
#2.工厂方法--实现了FactoryBean接口的Bean
#3.单例模式--BeanFactory创建Bean
#4.适配器模式--spring定义了一个适配器接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了
#5.装饰器模式---类名是xxxWrapper或者xxxDecorator都使用了,动态给对象添加额外功能 BeanWrapper
#6.动态代理---AOP
#7.观察者模式---xxxListener类  ApplicationListener
#8.策略模式--通过Resource接口访问底层资源
#9.模板模式--jdbcTemplate
9.1 FactoryBean和BeanFactory区别
#BeanFactory是个bean工厂,是一个工厂类(接口,顶层容器,ApplicationContext继承了它), 它负责生产和管理bean的一个工厂,是ioc 容器最底层的接口,是个ioc容器,是spring用来管理和装配普通bean的ioc容器
#FactoryBean是个bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由spring管理后,生产的对象是由getObject()方法决定的

10.什么是Bean的自动装配?有哪些方式
#自动装配是Spring满足bean依赖的一种方式,Spring会在上下文中自动雪照并被bean装配属性
#三种方式: xml中装配、java配置文件基于注解装配(@Configuration+@Bean)、隐式的自动装配(注解byType/byName)

SpringMVC学习

1.介绍

#MVC Model View Controller 命名规范:xxxVo视图层对象 xxxDto传输层对象
#SpringMVC是Spring Framework的一部分,是基于java实现MVC的轻量级框架
#SpringMVC是基于DispatchServlet设计的,DispatchServlet用于把请求分发到不同的处理器

Spring学习_第2张图片

2.使用


DispatchServlet作为前端控制器-->HandlerMapping为处理映射,由DispatchServlet调用-->HandlerExecution为具体处理器,根据URL查找控制器,返回给DispatcherServlet-->HandlerAdapter为处理适配器,找到对应的Handler-->Handler让具体的Controller执行-->Controller返回HandlerAdapter-->HandlerAdapter将视图返回给DispatcherServlet-->DispatcherServlet调用视图解析器(ViewResolver)-->视图解析器将解析的逻辑视图名传给DispatcherServlet-->DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图-->返回给用户




1.由于Maven可能存在资源过滤的问题,我们将配置完善(过滤一些配置)pom.xml中
<build>
   <resources>
       <resource>
           <directory>src/main/javadirectory>
           <includes>
               <include>**/*.propertiesinclude>
               <include>**/*.xmlinclude>
           includes>
           <filtering>falsefiltering>
       resource>
       <resource>
           <directory>src/main/resourcesdirectory>
           <includes>
               <include>**/*.propertiesinclude>
               <include>**/*.xmlinclude>
           includes>
           <filtering>falsefiltering>
       resource>
   resources>
build>
2.在pom.xml文件引入相关的依赖:主要有Spring框架核心库、Spring MVC、servlet , JSTL等。
3.配置web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
        version="4.0">
   
   <servlet>
       <servlet-name>SpringMVCservlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
       
       <init-param>
           <param-name>contextConfigLocationparam-name>
           <param-value>classpath:springmvc-servlet.xmlparam-value>
       init-param>
       
       <load-on-startup>1load-on-startup>
   servlet>
   
   <servlet-mapping>
       <servlet-name>SpringMVCservlet-name>
       <url-pattern>/url-pattern>
   servlet-mapping>
    
    <filter>
   <filter-name>encodingfilter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
   <init-param>
       <param-name>encodingparam-name>
       <param-value>utf-8param-value>
   init-param>
filter>
<filter-mapping>
   <filter-name>encodingfilter-name>
   <url-pattern>/*url-pattern>
filter-mapping>
web-app>
4.添加Spring MVC配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">
   
   <context:component-scan base-package="com.kuang.controller"/>
   
   <mvc:default-servlet-handler />
   
   <mvc:annotation-driven />
   
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
         id="internalResourceViewResolver">
       
       <property name="prefix" value="/WEB-INF/jsp/" />
       
       <property name="suffix" value=".jsp" />
   bean>
    
    <mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       bean>
       <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               bean>
           property>
       bean>
   mvc:message-converters>
mvc:annotation-driven>
beans>
5.创建Controller
@Component/@Service/@Controller/@Repository是一样的,都是注册为一个组件,只是为了业务区分
6.创建视图层
xxx.jsp,Controller中返回String,例如"hello",就会返回hello.jsp
#RESTFul风格:可以实现相同的请求地址,不同的请求方式不同的结果:例如@GetMapping和@DeleteMapping不同逻辑
原来:http://localhost/add?a=1&b=2 
restful:http://localhost/add/1/2 需要结合@PathVariable注解使用
##@ResponseBody(可以直接使用组合注解@RestController) 默认使用Jackson 
使用该注解,SpringMVC就不会调用视图解析器,而是直接返回字符串
#####拦截器
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
#过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
#过滤器
servlet规范中的一部分,任何java web工程都可以使用
在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
#拦截器 
拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

###自定义拦截器
1.类实现 implements HandlerInterceptor即可
2.在Spring MVC配置文件中配置
<!--关于拦截器的配置-->
<mvc:interceptors>
   <mvc:interceptor>
       <!--/** 包括路径及其子路径-->
       <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
       <!--/admin/** 拦截的是/admin/下的所有-->
       <mvc:mapping path="/**"/>
       <!--bean配置的就是拦截器-->
       <bean class="com.kuang.interceptor.MyInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

3.文件上传与下载

#Content-type说明
application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。
text/plain:除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

#####文件上传
1.导入jar包
<!--文件上传-->
<dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>4.0.1</version>
</dependency>
2.配置bean:multipartResolver
<!--文件上传配置-->
<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
   <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
   <property name="defaultEncoding" value="utf-8"/>
   <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
   <property name="maxUploadSize" value="10485760"/>
   <property name="maxInMemorySize" value="40960"/>
</bean>
3.Controller
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@Controller
public class FileController {
   //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
   //批量上传CommonsMultipartFile则为数组即可
   @RequestMapping("/upload")
   public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
       //获取文件名 : file.getOriginalFilename();
       String uploadFileName = file.getOriginalFilename();
       //如果文件名为空,直接回到首页!
       if ("".equals(uploadFileName)){
           return "redirect:/index.jsp";
      }
       System.out.println("上传文件名 : "+uploadFileName);
       //上传路径保存设置
       String path = request.getServletContext().getRealPath("/upload");
       //如果路径不存在,创建一个
       File realPath = new File(path);
       if (!realPath.exists()){
           realPath.mkdir();
      }
       System.out.println("上传文件保存地址:"+realPath);

       InputStream is = file.getInputStream(); //文件输入流
       OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
       //读取写出
       int len=0;
       byte[] buffer = new byte[1024];
       while ((len=is.read(buffer))!=-1){
           os.write(buffer,0,len);
           os.flush();
      }
       os.close();
       is.close();
       return "redirect:/index.jsp";
  }
}

####文件下载
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
   //要下载的图片地址
   String  path = request.getServletContext().getRealPath("/upload");
   String  fileName = "基础语法.jpg";
   //1、设置response 响应头
   response.reset(); //设置页面不缓存,清空buffer
   response.setCharacterEncoding("UTF-8"); //字符编码
   response.setContentType("multipart/form-data"); //二进制传输数据
   //设置响应头
   response.setHeader("Content-Disposition",
           "attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));
   File file = new File(path,fileName);
   //2、 读取文件--输入流
   InputStream input=new FileInputStream(file);
   //3、 写出文件--输出流
   OutputStream out = response.getOutputStream();

   byte[] buff =new byte[1024];
   int index=0;
   //4、执行 写出操作
   while((index= input.read(buff))!= -1){
       out.write(buff, 0, index);
       out.flush();
  }
   out.close();
   input.close();
   return null;
}

4.SpringMVC相关面试题


SpringBoot学习

1.介绍

#核心思想:约定大于配置:自动装配
#核心pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
#@SpringBootApplication
作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
进入这个注解:可以看到上面还有很多其他注解!

#@ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的元素。
作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
#@SpringBootConfiguration
作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
我们继续进去这个注解查看
这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
#@EnableAutoConfiguration
@EnableAutoConfiguration :开启自动配置功能
以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
点进注解接续查看:
@AutoConfigurationPackage :自动配置包
点进注解接续查看:
@AutoConfigurationPackage注解上注解@import :Spring底层注解@import , 给容器中导入一个组件
Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器;
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector:自动配置导入选择器
1.类中有一个方法:
#获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    #这里的getSpringFactoriesLoaderFactoryClass()方法
    #返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}
2.这个方法又调用了  SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    #//这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
3.我们继续点击查看 loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    #//获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            #//去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            #//将读取到的资源遍历,封装成为一个Properties
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}
4.发现一个多次出现的文件:spring.factories,根据源头打开spring.factories, 看到了很多自动配置的文件;这就是自动配置根源所在!
##########结论:################
1.SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定值
将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作(导入了相应的starter包即生效,否则无效)
2.整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
3.它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 ,并配置好这些组件;
###自动配置核心注解:@Conditional相关注解
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean
@ConditionalOnBean(RedisConnectionFactory.class)就是说如果你配置了redis的相关配置信息那么我就实例化RedisTemplate供你进行操
@ConditionalOnMissingBean 就是不存在某个bean的时候实例化。
@ConditionalOnClass(存在某个类时,才会实例化一个Bean)
@ConditionalOnMissingClass(不存在某个类时,才会实例化一个Bean)
@ConditionalOnProperty(prefix = “syj”, name = “algorithm”, havingValue = “token”)
这个就稍微复杂了一点,它的意思呢就是当存在配置文件中以syj为前缀的属性,属性名称为algorithm,然后它的值为token时才会实例化一个类。
而且这个还有一个比较好的属性
@ConditionalOnProperty(prefix = “syj”, name = “algorithm”, havingValue = “counter”, matchIfMissing = true)
matchIfMissing的意思呢就是说如果所有的都不满足的话就默认实现,不管这个属性syj.algorithm是不是等于counter
多条件时:全部为TRUE才会加载bean
@Conditional({WindowsCondition.class,ObstinateCondition.class})

2.配置文件使用:

#SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的
application.properties
语法结构 :key=value
或者:application.yml
语法结构 :key:空格 value
#基础语法
说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。
#注意
"" 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
比如 :name: "kuang \n shen"   输出 :kuang  换行   shen
'' 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如 :name: ‘kuang \n shen’   输出 :kuang  \n   shen
#对象、Map格式
k: 
    v1:
    v2:
或者:
student: {name: limy,age: 3}
#数组( List、set )
用 - 值表示数组中的一个元素,比如:
pets:
 - cat
 - dog
 - pig
或者:
pets: [cat,dog,pig]

###配置文件自动注入:
1.配合@Value("${name}")注解(作用在属性上)
2.配合@ConfigurationProperties(prefix = "person")(作用在类上)

###其他使用
@PropertySource :加载指定的配置文件;
配置文件中占位符:
person:
    name: limy${random.uuid} # 随机uuid
    a-dog: ${person.name:hello}_旺旺 #没有person.name时输出hello_旺旺
#松散绑定意思:person.aDog可以用,@value必须使用person.a-dog
#JSR303数据校验:@Validated注解配合全局异常处理使用
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY. 
Boolean检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.
日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后 

@Pattern    验证 String 对象是否符合正则表达式的规则
等等,除此以外,我们还可以自定义一些数据校验规则

#Spring boot的默认配置文件:
优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
#yaml的多文档块 --- 相当于有两个文件
server:
  port: 8081
#选择要激活那个环境块
spring:
  profiles:
    active: @deployType@ #dev
---
server:
  port: 8083
spring:
  profiles: dev #配置环境的名称
#pom.xml中添加如下配置,在打包时添加 -dev 或其他参数@deployType@ 即可使用该参数
<profile>
	<id>pre</id>
	<properties>
		<deployType>pre</deployType>
	</properties>
</rofile>

3.自动配置理解

#spring.factories和配置文件的联系:例子:HttpEncodingAutoConfiguration配置类
#从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {
}
#在yaml中配置
spring:
	http:
		.....
1.SpringBoot启动会加载大量的自动配置类
2.看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3.给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
#以上就是自动配置的原理

#我们怎么知道哪些自动配置类生效?
#在配置文件中开启springboot的调试
debug=true
Positive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)

4.SpringBoot Web开发

#springboot的版本不同,以下东西可能会有微变
#静态资源映射规则 resourceProperties类 
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
#默认去这些文件夹中寻找对应的静态资源文件

#自定义静态资源路径(一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了)
spring.resources.static-locations=classpath:/coding/,classpath:/kuang/
#关闭默认图标
spring.mvc.favicon.enabled=false

######Thymeleaf模板引擎(SpringBoot推荐使用)
#1.含义:
	模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎。
#2.使用:
 ##2.1 引入Thymeleaf
<!--thymeleaf-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
##2.2 我们去找一下Thymeleaf的自动配置类:ThymeleafProperties
##需要新建templates文件夹,把文件放到该文件夹即可
@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
}
##2.3Thymeleaf 语法学习
1.$符号取上下文中的变量:
<input type="text" name="userName"  th:value="${user.name}">
2.#符号取thymeleaf工具中的方法、文字消息表达式:
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
3. *{...}选择表达式一般跟在th:object后,直接选择object中的属性
<div th:object="${session.user}">
<p th:text="*{name}"/><div>
####
<!DOCTYPE html>
#需要在html文件中导入命名空间的约束,方便提示
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>狂神说</title>
</head>
<body>
<h1>测试页面</h1>
#
<!--th:text转义特殊字符-->
<div th:text="${msg}"></div>
<!--不转义特殊字符-->
<div th:utext="${msg}"></div>
<!--遍历数据-->
<!--th:each每次遍历都会生成当前这个标签:官网#9-->
<h4 th:each="user:${users}" th:text="${user}"></h4>
</body>
</html>
#请求index.html参数放小括号
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>


#####Spring MVC Auto-configuration: SpringBoot已经帮我们自动配置好了SpringMVC,然后自动配置了哪些东西呢?
1.ContentNegotiatingViewResolver 内容协商视图解析器 
2.转换器和格式化器
3.等等...
#我们如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了!
#比如:自定义一个视图解析器
@Bean //放到bean中
public ViewResolver myViewResolver(){
    return new MyViewResolver();
}
//我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
        return null;
    }
}
#结论:SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;

##########扩展使用SpringMVC###########
编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解
//应为类型要求为WebMvcConfigurer,所以我们实现其接口
//可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/test,就会跳转到test页面;
        registry.addViewController("/test").setViewName("test");
    }
}
#为什么不能标注@EnableWebMvc注解
#1.该注解上有一个@Import(EnableWebMvcConfiguration.class)注解,引入了EnableWebMvcConfiguration类,看一下这个类,它继承了一个父类:WebMvcConfigurationSupport,而WebMvcAutoConfiguration类中有一个注解:@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}),会让springboot的自动配置全部失效
#2.该注解的作用:全面接管SpringMVC,但是所有都是我们自己去配置,不推荐使用

###项目国际化(中英文切换)
#在Spring中有一个国际化的Locale (区域信息对象);里面有一个叫做LocaleResolver (获取区域信息对象)的解析器!
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
    // 容器中没有就自己配,有的话就用用户配置的
    if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    }
    // 接收头国际化分解
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    return localeResolver;
}
#类中有一个方法
public Locale resolveLocale(HttpServletRequest request) {
    Locale defaultLocale = this.getDefaultLocale();
    // 默认的就是根据请求头带来的区域信息获取Locale进行国际化
    if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
        return defaultLocale;
    } else {
        Locale requestLocale = request.getLocale();
        List<Locale> supportedLocales = this.getSupportedLocales();
        if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
            Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
            if (supportedLocale != null) {
                return supportedLocale;
            } else {
                return defaultLocale != null ? defaultLocale : requestLocale;
            }
        } else {
            return requestLocale;
        }
    }
}

##我们可以自定义一个处理的组件类
#可以在链接上携带区域信息
public class MyLocaleResolver implements LocaleResolver {
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault(); // 如果没有获取到就使用系统默认的
        //如果请求链接不为空
        if (!StringUtils.isEmpty(language)){
            //分割请求参数
            String[] split = language.split("_");
            //国家,地区
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
    }
}
#然后再config中添加到spring
@Bean
public LocaleResolver localeResolver(){
    return new MyLocaleResolver();
}

5.自定义starter

//说明:
启动器模块是一个 空 jar 文件,仅提供辅助性依赖管理,这些依赖可能用于自动装配或者其他类库;
命名归约:
//官方提供的 命名:
前缀:spring-boot-starter-xxx
比如:spring-boot-starter-web....
//自定义的 命名:
xxx-spring-boot-starter
比如:mybatis-spring-boot-starter
//编写步骤:
1、在IDEA中新建一个空项目 spring-boot-starter-diy
2、新建一个普通Maven模块:kuang-spring-boot-starter
3、新建一个Springboot模块:kuang-spring-boot-starter-autoconfigure
4、在我们的 starter 中 导入  autoconfigure 的依赖!
<!-- 启动器 -->
<dependencies>
    <!--  引入自动配置模块 -->
    <dependency>
        <groupId>com.kuang</groupId>
        <artifactId>kuang-spring-boot-starter-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>
5、将 autoconfigure 项目下多余的文件都删掉,pom.xml中只留下一个 starter,这是所有的启动器基本配置!
6、我们编写一个自己的服务
public class HelloService {
    HelloProperties helloProperties;
    public HelloProperties getHelloProperties() {
        return helloProperties;
    }
    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }
    public String sayHello(String name){
        return helloProperties.getPrefix() + name + helloProperties.getSuffix();
    }
}
7、编写HelloProperties 配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
// 前缀 kuang.hello
@ConfigurationProperties(prefix = "kuang.hello")
public class HelloProperties {

    private String prefix;
    private String suffix;
    public String getPrefix() {
        return prefix;
    }
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    public String getSuffix() {
        return suffix;
    }
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}
8、编写我们的自动配置类并注入bean,测试!
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication #web应用生效
@EnableConfigurationProperties(HelloProperties.class)#使注解的类生效 使用@ConfigurationProperties注解的类生效
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;

    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }

}
9、在resources编写一个自己的 META-INF\spring.factories
//Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kuang.HelloServiceAutoConfiguration
10、完成之后install到maven中即可使用
11、使用:
11.1 pom.xml中导入我们的启动器jar
11.2 编写配置文件 
	kuang.hello.prefix="ppp"
	kuang.hello.suffix="sss"
11.3 自动注入即可使用
 @Autowired
 HelloService helloService;
//注意
11.4 //@SpringBootApplication 包含@EnableAutoConfiguration和@ComponentScan两个注解
    1) @ComponentScan注解的作用是扫描@SpringBootApplication所在的Application类(即spring-boot项目的入口类)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)标记的bean,并注册到spring容器中
    2)@EnableAutoConfiguration的作用
    包含了@Import(AutoConfigurationImportSelector.class)注解AutoConfigurationImportSelector.class类的getAutoConfigurationEntry方法获取了spring-boot项目中需要自动配置的项(bean)(getCandidateConfigurations方法),//具体实现为读取spring-boot项目的classpath下META-INF/spring.factories的内容,这个文件常常以K/V的形式存储数据 
//PS:自定义starter时,如果starter仅仅为了本公司使用,可以不使用spring.factories,两种情况:1.公司项目的目录接口均为com.lmy,引入starter本来就可以导入 2.可以在启动类配置@ComponentScan扫描到对应的starter

6.SpringBoot集成插件

6.1 集成数据库相关

#######################################整合JDBC###################################
1.新建项目(引入Web-Spring Web和SQL JDBC API/MySQL Driver模块)
2.建立之后会默认导入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
3.编写yaml文件连接数据库
spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
4.配置完这一些东西后,我们就可以直接去使用了,因为SpringBoot已经默认帮我们进行了自动配置;
	在 DataSourceConfiguration 配置类下,可以看出 Spring Boot 2.2.5 默认使用HikariDataSource 数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;
##JDBCTemplate
1、有了数据源(com.zaxxer.hikari.HikariDataSource),然后可以拿到数据库连接(java.sql.Connection),有了连接,就可以使用原生的 JDBC 语句来操作数据库;
2、即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封装,即JdbcTemplate。
3、数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。
4、Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用
5、JdbcTemplate 的自动配置是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 类
    JdbcTemplate主要提供以下几类方法:
    execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
    update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
    query方法及queryForXXX方法:用于执行查询相关语句;
    call方法:用于执行存储过程、函数相关语句。
#使用
@RestController
@RequestMapping("/jdbc")
public class JdbcController {
    /**
     * Spring Boot 默认提供了数据源,默认提供了 org.springframework.jdbc.core.JdbcTemplate
     * JdbcTemplate 中会自己注入数据源,用于简化 JDBC操作
     * 还能避免一些常见的错误,使用起来也不用再自己来关闭数据库连接
     */
    @Autowired
    JdbcTemplate jdbcTemplate;
    //查询employee表中所有数据
    //List 中的1个 Map 对应数据库的 1行数据
    //Map 中的 key 对应数据库的字段名,value 对应数据库的字段值
    @GetMapping("/list")
    public List<Map<String, Object>> userList(){
        String sql = "select * from employee";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
    //修改用户信息
    @GetMapping("/update/{id}")
    public String updateUser(@PathVariable("id") int id){
        //插入语句
        String sql = "update employee set last_name=?,email=? where id="+id;
        //数据
        Object[] objects = new Object[2];
        objects[0] = "秦疆";
        objects[1] = "[email protected]";
        jdbcTemplate.update(sql,objects);
        //查询
        return "updateOk";
    }
}

#########集成Druid  读法:德鲁伊
	Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。
Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。
1、添加上 Druid 数据源依赖。
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>
2、配置使用Druid数据源
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
     #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
 3.自定义config使配置生效,不再使用 Spring Boot 的自动生成了
@Configuration
#指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
#即使@Mapper注解的接口可以被扫描
@MapperScan(basePackages = DruidConfig.PACKAGE, sqlSessionFactoryRef = "primarySqlSessionFactory")
public class DruidConfig {
    /*
       将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
       绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
       @ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
       前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }
#########配置Druid数据源监控
#配置 Druid 监控管理后台的Servlet;
#内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

    // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 
    // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
    initParams.put("loginPassword", "123456"); //后台管理界面的登录密码
    //后台允许谁可以访问
    //initParams.put("allow", "localhost"):表示只有本机可以访问
    //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
    initParams.put("allow", "");
    //deny:Druid 后台拒绝谁访问
    //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问
    //设置初始化参数
    bean.setInitParameters(initParams);
    return bean;
	}
	#配置完毕后,我们可以选择访问 :http://localhost:8080/druid/login.html
	
    #配置 Druid 监控 之  web 监控的 filter
    #WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
    @Bean
    public FilterRegistrationBean webStatFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        #exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
        Map<String, String> initParams = new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
        bean.setInitParameters(initParams);
        //"/*" 表示过滤所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}


############整合MyBatis
1.导入 MyBatis 所需要的依赖
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>
2.spring配置文件,和上面一样
# 还需要Mybatis配置
mybatis:
    mapperLocations: classpath:/mapper/**/*.xml
    configLocation: classpath:/mybatis.xml
3.创建mapper目录以及对应的 Mapper 接口
#@Mapper : 表示本类是一个 MyBatis 的 Mapper
4.创建对应的Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.DepartmentMapper">
  <resultMap id="EmployeeMap" type="Employee">
        <id property="id" column="eid"/>
        <result property="lastName" column="last_name"/>
        <result property="email" column="email"/>
        <result property="gender" column="gender"/>
        <result property="birth" column="birth"/>
        <association property="eDepartment"  javaType="Department">
            <id property="id" column="did"/>
            <result property="departmentName" column="dname"/>
        </association>
    </resultMap>
    <select id="getDepartments" resultType="Department">
       select * from department;
    </select>
</mapper>
5.maven配置资源过滤问题
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

6.2动态数据源

//动态数据源配置 AbstractRoutingDataSource类和使用自定义SqlSessionFactory是关键配置
//1.AbstractRoutingDataSource此类是设置动态数据源的关键,
public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
    }
    /**
	     * 根据Key获取数据源的信息
	     * @return
	     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
//2.切换数据源配置
public class DynamicDataSourceContextHolder {
    private static ThreadLocal<DataEnum> threadLocal = new ThreadLocal<>();
    public static void setDataSourceType(DataEnum type){
        threadLocal.set(type);
        System.out.println("数据库已经切换"+type);
    }
    public static DataEnum getDataSourceType(){
        return threadLocal.get();
    }
    public static void clearDatasourceType(){
        threadLocal.remove();
    }
}
//3.数据库配置文件
@Configuration
@MapperScan(basePackages="datasourc.demo.mapper", sqlSessionFactoryRef="sessionFactory")
public class DadaSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.remote")
    public DataSource remoteDateSource(){

        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.local")
    public DataSource localDateSource(){

        return DataSourceBuilder.create().build();
    }
    @Bean
    public DataSource dynamicDataSource(@Qualifier("remoteDateSource")DataSource remoteDateSource,@Qualifier("localDateSource")DataSource localDateSource){
       Map<Object,Object> datasourceMap = new HashMap<>();
       datasourceMap.put(DataEnum.LOCAL,localDateSource);
       datasourceMap.put(DataEnum.REMOTE,remoteDateSource);
       DynamicDataSource dynamicDataSource = new DynamicDataSource(remoteDateSource, datasourceMap);
       return dynamicDataSource;
    }
    //4.使用SqlSessionFactory自定义factory
    @Bean(name="sessionFactory")
    public SqlSessionFactory sessionFactory(@Qualifier("dynamicDataSource")DataSource dataSource) throws Exception{
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*"));    				//*Mapper.xml位置
        return sessionFactoryBean.getObject();
    }
	@Bean(name = "primaryTransactionManager")
	    public DataSourceTransactionManager primaryTransactionManager(@Qualifier("dynamicDataSource")DataSource dataSource) throws SQLException {
	        return new DataSourceTransactionManager(dataSource);
	    }
	
	}
//5.数据库枚举类
public enum  DataEnum {
    LOCAL,
    REMOTE
}
//6.自定义使用数据库注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    DataEnum value() default DataEnum.LOCAL;
}
//7.AOP配置类
@Aspect
@Order(1)
@Component
public class DataSourceAop {
    @Pointcut("@annotation(datasourc.demo.config.DataSource)")
    public void dsPointCut() {

    }
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value());
        }
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDatasourceType();
        }
    }
}

6.3 安全框架

#shiro的优点
shiro的代码更易于阅读,且使用更加简单;
shiro可以用于非web环境,不跟任何框架或容器绑定,独立运行;
#spring-security的优点
spring-security对spring结合较好,项目是spring-boot等搭建的,使用起来更加方便;
有更好的spring社区进行支持;
支持oauth授权,官方网站:https://spring.io/projects/spring-security-oauth;
#shiro的缺点
授权第三方登录需要手动实现;
#spring-security的缺点
对于shiro比较复杂,没有shiro清晰易懂;

###############集成SpringSecurity框架#######################
##########介绍:
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
##记住几个类:
#WebSecurityConfigurerAdapter:自定义Security授权策略
#AuthenticationManagerBuilder:自定义认证策略
#@EnableWebSecurity:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
#"认证"(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
#"授权"(Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
#######集成
1.引入模块
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.

##################集成Shiro框架############################

7.SpringBoot相关面试题

1.SpringBoot配置文件的加载顺序?
#包下的config目录-->包下的根目录-->项目下的config目录-->项目下的根目录
#*.properties-->*.xml-->*.yml-->*.yaml
#取值时读取配置文件的顺序为:properties->yaml->yml; 
2.为什么 Dao 层不需要加 @Repository 注解?加了 @Repository 注解有什么影响?
#关键在于 ClassPathMapperScanner 对指定包的扫描,并且扫描过程对 Spring 原本的扫描 Bean 的步骤 “加了料” ,Spring 本身只扫实现类,但 MyBatis 的扫描器扫了接口 。并且扫完接口之后,为接口配了个 BeanDefinition ,并且这个 bd 的 BeanClass 是 MapperFactoryBean;
#仅仅只能解决 Intellij 静态查找 bean 的问题,没有实际作用。即使加了注解,比如@Controller,@Service 等等,也会被 Spring 的扫描器给忽略掉,因为扫描器会过滤掉接口
3.SpringSecurity和shiro区别
   #相同点:1.认证功能 2:授权功能 3:加密功能4:会话管理5:缓存支持6:rememberMe功能.......
   #不同:1:Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring Security做权限更加方便,而Shiro需要和Spring进行整合开发;2:Spring Security功能比Shiro更加丰富些,例如安全防护
    #3:Shiro的配置和使用比较简单,Spring Security上手复杂
    #4:Shiro依赖性低,不需要任何框架和容器,可以独立运行,而Spring Security依赖于Spring容器    
4.在spring-boot项目中spring.factories文件的作用是什么?
#其实就是帮助项目包以外的bean注册到spring-boot项目中,而这些项目包以外的bean其实就是pom文件里面添加的依赖中的那些bean
5.SpringBootApplication注解是如何发挥作用的?
#即先是通过@ComponentScan注解扫描spring-boot项目包内的bean并注册到spring容器中,
#然后基于@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。

8.Springboot 其他

###################Springboot接收请求参数的几种常用方式#######################
1.无注解(只能通过url拼接参数接收) #ps:@Validated注解也可以生效
    无注解的参数接收方式,只能通过变量的标识和http参数中的key对应来寻找映射,并且不能指定校验规则等,前端爱传不传,不传就拉倒
    #ps:使用对象接收时自动对应属性
2.@PathVariable(value="aaa")
    带占位符的URL是Spring3.0新增的功能 url中含有/{aaa}
3.@RequestParam #ps:支持’application/json’,也同样支持multipart/form-data请求
    @RequestParam用于接收get请求url中的参数,其规则简单总结一下:
		1)@RequestParam可以指定接收参数名称。例如@RequestParam(“userId”)String uId
		2)@RequestParam写法参数为必传,可以通过require属性进行设置
		3)@RequestParam可以通过@RequestParam(defaultValue = “0”)指定参数默认值
		4)如果接口需要被RPC调用,则不能省略@RequestParam,否则RPC会找不到参数报错
		5)使用对象接收时不会自动对应属性,使用 User user接收时,前台使用user.name,user.password拼接即可
4.@RequestBody
      一般被用来接收http请求中body中的内容,最常用的咱们一般用json传输数据,将数据放置于body中。它会更加json的key值和接收参数对象的key值做映射,通过set方法进行设置
5.@RequestPart #支持multipart/form-data
	传递的json参数,@RequestPart注解可以用对象来接收,@RequestParam只能用字符串接收
6.@RequestHeader
     用于从header中获取数据
7.HttpServletRequest
   直接拿到request对象,可以从对象中灵活的获取参数
   1)参数来源于URL中request.getParameter("msg");
   2)参数来源于URL中request.getParameterMap();
     String[] msgs = parameterMap.get("msg");
	3)通过流获取body中的值,参数来源于请求body中 new BufferedReader(new InputStreamReader(request.getInputStream()));
####################

SpringCloud学习

SpringCloud 中文API文档(官方文档翻译版):https://springcloud.cc/spring-cloud-dalston.html
SpringCloud中国社区:http://springcloud.cn/
SpringCloud中文网:https://springcloud.cc

1.微服务与微服务架构

#个人理解
微服务一般分为服务端和客户端:服务端连接数据库等做实际业务,而客户端与前端打交道不连接数据库
#什么是微服务
	1.就目前而言,对于微服务,业界并没有一个统一的,标准的定义。
	2.但通常而言,微服务架构是一种架构模式,或者说是一种架构风格,它体长将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制(HTTP)互相沟通,每个服务都围绕着具体的业务进行构建,并且能狗被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应该根据业务上下文,选择合适的语言,工具(Maven)对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
	3.再来从技术维度角度理解下:
	微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库
##微服务优缺点
1.优点
	单一职责原则;
	每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
	开发简单,开发效率高,一个服务可能就是专一的只干一件事;
	微服务能够被小团队单独开发,这个团队只需2-5个开发人员组成;
	微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的;
	微服务能使用不同的语言开发;
	易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo;
	微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果,无需通过合作才能体现价值;
	微服务允许利用和融合最新技术;
	微服务只是业务逻辑的代码,不会和HTML,CSS,或其他的界面混合;
	每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一的数据库;
2.缺点
	开发人员要处理分布式系统的复杂性;
	多服务运维难度,随着服务的增加,运维的压力也在增大;
	系统部署依赖问题;
	服务间通信成本问题;
	数据一致性问题;
	系统集成测试问题;
	性能和监控问题;

2.SpringCloud介绍

ps:Lombok中@Accessors注解到类上可以支持链式写法

	1.SpringBoot专注于开苏方便的开发单个个体微服务;
	2.SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务;
	3.SpringBoot可以离开SpringCloud独立使用,开发项目,但SpringCloud离不开SpringBoot,属于依赖关系;
	4.SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架;

	1.最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
	2.严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
	3.严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。

 		
1.导入pom依赖
 
    <packaging>pompackaging>
    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
        <log4j.version>xxxlog4j.version>
    properties>

<dependencyManagement>
    <dependency>dependency>
dependencyManagement>

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-dependenciesartifactId>
    <version>Greenwich.SR1version>
    <type>pomtype>
    <scope>importscope> 
dependency>

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-dependenciesartifactId>
    <version>2.1.4.RELEASEversion>
    <type>pomtype>
    <scope>importscope>
dependency>
 
 <dependency>
     <groupId>mysqlgroupId>
     <artifactId>mysql-connector-javaartifactId>
     <version>5.1.47version>
 dependency>
 <dependency>
     <groupId>com.alibabagroupId>
     <artifactId>druidartifactId>
     <version>1.1.10version>
 dependency>
 
 <dependency>
     <groupId>org.mybatis.spring.bootgroupId>
     <artifactId>mybatis-spring-boot-starterartifactId>
     <version>1.3.2version>
 dependency>
 
 <dependency>
     <groupId>log4jgroupId>
     <artifactId>log4jartifactId>
     <version>${log4j.version}version>
 dependency>
 ###############热部署工具:较慢########################
  
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
    dependency>

3.SpringCloud版本选择

SpringCloud版本 SpringBoot版本
2021.0.1-SNAPSHOT Spring Boot >=2.6.4-SNAPSHOT and <2.7.0-M1
2021.0.0 Spring Boot >=2.6.1 and <2.6.4-SNAPSHOT
2021.0.0-RC1 Spring Boot >=2.6.0-RC1 and <2.6.1
2021.0.0-M3 Spring Boot >=2.6.0-M3 and <2.6.0-RC1
2021.0.0-M1 Spring Boot >=2.6.0-M1 and <2.6.0-M3
2020.0.5 Spring Boot >=2.4.0.M1 and <2.6.0-M1
Hoxton.SR12 Spring Boot >=2.2.0.RELEASE and <2.4.0.M1
Hoxton.BUILD-SNAPSHOT Spring Boot >=2.2.0.BUILD-SNAPSHOT
Hoxton.M2 Spring Boot >=2.2.0.M4 and <=2.2.0.M5
Greenwich.BUILD-SNAPSHO Spring Boot >=2.1.9.BUILD-SNAPSHOT and <2.2.0.M4
Greenwich.SR2 Spring Boot >=2.1.0.RELEASE and <2.1.9.BUILD-SNAPSHOT
Greenwich.M1 Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE
Finchley.BUILD-SNAPSHOT Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3
Finchley.SR4 Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT
Finchley.RC2 Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE
Finchley.RC1 Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE
Finchley.M9 Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE
Finchley.M7 Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2
Finchley.M6 Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1
Finchley.M5 Spring Boot >=2.0.0.M7 and <=2.0.0.M7
Finchley.M4 Spring Boot >=2.0.0.M6 and <=2.0.0.M6
Finchley.M3 Spring Boot >=2.0.0.M5 and <=2.0.0.M5
Finchley.M2 Spring Boot >=2.0.0.M3 and <2.0.0.M5
Edgware.SR5 1.5.20.RELEASE
Edgware.SR5 1.5.16.RELEASE
Edgware.RELEASE 1.5.9.RELEASE
Dalston.RC1 1.5.2.RELEASE

4.Ribbon&&Feign&&OpenFeign

1.Ribbon

#Ribbon是什么
Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具
#集成Ribbon
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
//自定义Spring配置类:ConfigBean.java 配置负载均衡实现RestTemplate
@Configuration
public class ConfigBean {

    @LoadBalanced //配置负载均衡实现RestTemplate
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

//自定义负载均衡规则
@Configuration
public class MyRule{
    /**
     * IRule:
     * RoundRobinRule 默认策略:轮询策略
     * RandomRule 随机策略
     * AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
     * RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
     */
    @Bean
    public IRule myRule() {
        //return new RandomRule();//使用随机策略
        //return new RoundRobinRule();//使用轮询策略
        //return new AvailabilityFilteringRule();//使用轮询策略
        //return new RetryRule();//使用轮询策略
         return new MyRandomRule();//默认是轮询RandomRule,现在自定义为自己的
    }
}
//Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能加载自定义的Ribbon类(自定义的规则会覆盖原有默认的规则)
//MyRule.java不可以被@Componentscan扫描到,即不可与启动类同一目录
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//开启负载均衡,并指定自定义的规则
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}
//自定义实现规则,可以参考其他规则源码
public class MyRandomRule extends AbstractLoadBalancerRule {}

2.Feign

#feign和ribbon区别:调用方式的区别
微服务名字Restful风格 【ribbon】
面向接口和注解 【feign】#Feign默认集成了Ribbon,因此feign的效率也比ribbon低一些
#Feign简介#####
1.Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供负载均衡的http客户端
2.前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一个客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它 (类似以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量
#feign的使用
#1.引入依赖
<!--Feign的依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
/2.启动类中加入注解
@SpringBootApplication
@EnableEurekaClient
// feign客户端注解,并指定要扫描的包以及配置接口DeptClientService
@EnableFeignClients(basePackages = {"com.haust.springcloud"})
// 切记不要加这个注解,不然会出现404访问不到
//@ComponentScan("com.haust.springcloud")
public class FeignDeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(FeignDeptConsumer_80.class, args);
    }
}
///配置FeignClient只需要使用@FeignClient来注解一个interface即可

3.openFeign

#介绍
	OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中
#使用,引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>  
###########ps:springcloud F 及F版本以上 springboot 2.0 以上基本上使用openfeign,openfeign 如果从框架结构上看就是2019年feign停更后出现版本,也可以说大多数新项目都用openfeign ,2018年以前的项目在使用feign
##配置OpenFeignClient只需要使用@FeignClient来注解一个interface即可
#######################@RequesMapping不能在类名上与@FeignClient同时使用#####################
####在SpringCloudGateway中,当一个接口上同时拥有@RequestMapping和@FeignClient注解时, 请求会报异常

@FeignClient(value = "openfeign-server")
#@RequestMapping(path = "/user")
public interface UserClient {
    @GetMapping("getUserName")
    public String getUserName(@RequestParam("userId") String userId);
    @GetMapping("getUserAge")
    public String getUserAge(@RequestParam("userId") String userId, @SpringQueryMap User user);
}

###OpenFeign超时控制
默认Feign客户端只等待一秒钟,但是服务霭处理需要超过1秒,导致Feign客户端不想等待了,直接返回报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
# 设置feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
  # 指的是建立连接后从服务器读取到可用资源所用时间
  ReadTimeout: 5000
  # 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 5000

#######OpenFeign日志增强
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。说白了就是对Feign接口的调用情况进行监控和输出
日志级别
1:NONE:默认的,不显示任何日志;
2:BASIC:仅记录请求方法、URL、 响应状态码及执行时间;
3:HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
4:FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
logging:
  level:
  	#feign日志以什么级别监控那个接口(监控那个Feign接口)
    com.goff.feign.metadataFeignService: debug

5.Nacos

#详见 本人Nacos文章

6.Sentinel

#Sentinel是阿里巴巴开源的面向分布式服务架构的高可用防护组件
#官网https://sentinelguard.io/zh-cn/docs/introduction.html
#可以和 fegin,restTemplate 进行整合
1.引入依赖
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.配置sentinel
	spring:
  		cloud:
    		sentinel:
      			transport:
        			dashboard:
        			client-ip:
        			port: 8719
        			heartbeat-interval-ms: 10000  
3.sentinel 控制台
#簇点链路
	查看服务的API 以及 配置流控规则
#流控规则
	资源名:  API名称(唯一的名称)
	针对来源: 服务名. 不区分为 default
	阈值类型:
		QPS:
		线程数:
	流控模式:
		直接: 
		关联: 关联资源. 当关联资源达到阈值,就限流自己.
		保护关联资源.例如: 一个资源即读又修改.
		链路: 配个 @SentinelSource 注解使用. 设置阈值或者线程数目.进行使用
	流控效果:
		快速失败: 直接抛出异常
		warm Up: 预热.
		排队等待: 只能设置阈值. 如果这个请求过了规定的超时时间,则直接丢弃.
			匀速排队. 请求排队. 
#降级规则
	平均响应时间超出阈值 并且 在时间窗口内通过的请求 >= 5,则触发降级.
降级时间为 5秒.
	1. sentinel 默认最大降级时间为 4900ms
	2. 可通过 -Dcsp.sentinel.statistic.rt=xxx 修改任意值
异常数统计是分钟级别. 时间窗口 < 60 s 会出现异常
#热点规则
##此字符串会出现在 sentinel 控制台中
@SentinelResource("\hot")
1. value="要保护的API名称".
2. blockHandler="资源被限流或降级触发的方法"
3. fallback:  处理降级;
	可以加 Throwable 参数.在 对应的fallback 方法上
	添加 Throwable 参数 去处理异常
4. blockHandler: 

#授权规则
白名单
	资源名 这个API 只允许流控应用微服务 访问
黑名单
	不允许 对应的微服务访问
	
#############整合RestTemplate	#####
1.使用 @SentinelRestTemplate 注解
2.配置
	restTemplate:
  		sentinel:
   		# 关闭 @SentinelTempalte 注解
   		enabled: false//ture
##################Fegin 整合 Sentinel
fegin:
  sentinel:
    enabled: true
    
    
##########规则持久化
	sentinel console 规则是在内存中的
	持久化,保证规则不丢失
1.拉模式
console 推动规则给 微服务. 微服务写入到缓存中,定时持久化到 文件中
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-extension</artifactId>
</dependency>
/**
 * 拉模式规则持久化
 *
 * @author itmuch.com
 */
public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
            flowRulePath,
            flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
            flowRulePath,
            this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
            degradeRulePath,
            degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
            degradeRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
            systemRulePath,
            systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
            systemRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
            authorityRulePath,
            authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
            authorityRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
            paramFlowRulePath,
            paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
            paramFlowRulePath,
            this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference<List<FlowRule>>() {
        }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference<List<DegradeRule>>() {
        }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference<List<SystemRule>>() {
        }
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference<List<AuthorityRule>>() {
        }
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference<List<ParamFlowRule>>() {
        }
    );
    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}
2.推模式 ####推荐
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring:
  cloud:
    sentinel:
      datasource:
        # 名称随意
        flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            # 规则类型,取值见:
            # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule-type: flow
        degrade:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
        system:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            rule-type: system
        authority:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            rule-type: authority
        param-flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: param-flow

        

7.Springcloud Gateway

#详见 本人springcloud gateway文章

8.springCloud相关面试题

##1. Spring Cloud 是什么
	Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
####2.DiscoveryClient的作用
可以从注册中心中根据服务别名获取注册的服务器信息。
###3.Ribbon底层实现原理
Ribbon使用discoveryClient从注册中心读取目标服务信息,对同一接口请求进行计数,使用%取余算法获取目标服务集群索引,返回获取到的目标服务信息。
####4.@LoadBalanced注解的作用
开启客户端负载均衡。
####5.Ribbon和Feign的区别
	Ribbon都是调用其他服务的,但方式不同。2.启动类注解不同,Ribbon是@RibbonClient feign的是@EnableFeignClients 3.服务指定的位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。4.调用方式不同,Ribbon需要自己构建http请求,模拟http请求
##6.说说Spring Boot和Spring Cloud的关系
	SpringBoot专注于快速方便的开发单个个体的微服务
	SpringCloud是关注全局的微服务协调整理治理框架,整合并管理各个微服务,为各个微服务之间提供,配置管理,服务发现,断路器,路由,事件总线等集成服务
	Spring Boot不依赖于Spring Cloud,Spring Cloud依赖于Spring Boot,属于依赖关系
	Spring Boot专注于快速,方便的开发单个的微服务个体,Spring Cloud关注全局的服务治理框架
#####7.说说你对Spring Cloud Gateway的理解
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用

你可能感兴趣的:(java学习,spring,spring,boot,spring,cloud)