diff --git a/ReadMe.md b/ReadMe.md index dc53ae6..84572da 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -29,227 +29,333 @@

- - - -## 目录 - -- [Java基础](#Java基础) - - [基础知识](#基础知识) - - [容器](#容器) - - [设计模�](#设计模�) -- [JavaWeb](#JavaWeb) - - [Spring](#Spring) - - [SpringMVC](#SpringMVC) - - [SpringBoot](#SpringBoot) -- [Java进阶](#Java进阶) - - [并�](#并�) - - [JVM](#JVM) - - [Java网络编程](#Java网络编程) -- [计算机基础](#计算机基础) - - [计算机网络](#计算机网络) - - [�作系统](#�作系统) - - [Linux相关](#linux相关) - - [数�结构与算法](#数�结构与算法) - - [数�结构](#数�结构) - - [算法](#算法) -- [数�库](#数�库) - - [MySQL](#MySQL) -- [缓存](#缓存) - - [Redis](#Redis) -- [消�队列](#消�队列) - - [Kafka](#Kafka) -- [大�端](#大�端) -- [分布�](#分布�) - - [�论](#�论) - - [实战](#实战) -- [�试指�](#�试指�) - - [校招指�](#校招指�) - - [��](#��) -- [工具](#工具) - - [Git](#git) -- [资料](#资料) - - [书�](#书�) -- [待办](#待办) -- [说明](#说明) -- [微信公众�](#微信公众�) - -## Java基础 - -### 基础知识 - -* [��对象基础](docs/java/basic/��对象基础.md) -* [Java基本数�类型](docs/java/basic/Java基本数�类型.md) -* [string和包装类](docs/java/basic/string和包装类.md) -* [final关键字特性](docs/java/basic/final关键字特性.md) - -* [Java类和包](docs/java/basic/Java类和包.md) -* [抽象类和接�](docs/java/basic/抽象类和接�.md) -* [代��和代�执行顺�](docs/java/basic/代��和代�执行顺�.md) -* [Java自动拆箱装箱里��的秘密](docs/java/basic/Java自动拆箱装箱里��的秘密.md) -* [Java中的Class类和Object类](docs/java/basic/Java中的Class类和Object类.md) -* [Java异常](docs/java/basic/Java异常.md) -* [解读Java中的回调](docs/java/basic/解读Java中的回调.md) -* [�射](docs/java/basic/�射.md) -* [泛型](docs/java/basic/泛型.md) -* [枚举类](docs/java/basic/枚举类.md) -* [Java注解和最佳实践](docs/java/basic/Java注解和最佳实践.md) -* [JavaIO�](docs/java/basic/JavaIO�.md) -* [多线程](docs/java/basic/多线程.md) -* [深入�解内部类](docs/java/basic/深入�解内部类.md) -* [javac和javap](docs/java/basic/javac和javap.md) -* [Java8新特性终�指�](docs/java/basic/Java8新特性终�指�.md) -* [�列化和��列化](docs/java/basic/�列化和��列化.md) -* [继承�装多�的实现原�](docs/java/basic/继承�装多�的实现原�.md) - -### 容器 - -* [Java集�类总结](docs/java/collection/Java集�类总结.md) -* [Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�](docs/java/collection/Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�.md) -* [Java集�详解:Queue和LinkedList](docs/java/collection/Java集�详解:Queue和LinkedList.md) -* [Java集�详解:Iterator,fail-fast机制与比较器](docs/java/collection/Java集�详解:Iterator,fail-fast机制与比较器.md) -* [Java集�详解:HashMap和HashTable](docs/java/collection/Java集�详解:HashMap和HashTable.md) -* [Java集�详解:深入�解LinkedHashMap和LRU缓存](docs/java/collection/Java集�详解:深入�解LinkedHashMap和LRU缓存.md) -* [Java集�详解:TreeMap和红黑树](docs/java/collection/Java集�详解:TreeMap和红黑树.md) -* [Java集�详解:HashSet,TreeSet与LinkedHashSet](docs/java/collection/Java集�详解:HashSet,TreeSet与LinkedHashSet.md) -* [Java集�详解:Java集�类细节精讲](docs/java/collection/Java集�详解:Java集�类细节精讲.md) - -### 设计模� - -* [设计模�学习总结](docs/java/design-parttern/设计模�学习总结.md) -* [�探Java设计模�:创建型模�(工厂,�例等).md](docs/java/design-parttern/�探Java设计模�:创建型模�(工厂,�例等).md) -* [�探Java设计模�:结构型模�(代�模�,适�器模�等).md](docs/java/design-parttern/�探Java设计模�:结构型模�(代�模�,适�器模�等).md) -* [�探Java设计模�:行为型模�(策略,观察者等).md](docs/java/design-parttern/�探Java设计模�:行为型模�(策略,观察者等).md) -* [�探Java设计模�:JDK中的设计模�.md](docs/java/design-parttern/�探Java设计模�:JDK中的设计模�.md) -* [�探Java设计模�:Spring涉�到的�设计模�.md](docs/java/design-parttern/�探Java设计模�:Spring涉�到的�设计模�.md) - -## JavaWeb - -* [走进JavaWeb技术世界:JavaWeb的由�和基础知识](docs/java-web/走进JavaWeb技术世界:JavaWeb的由�和基础知识.md) -* [走进JavaWeb技术世界:JSP与Servlet的曾�与现在](docs/java-web/走进JavaWeb技术世界:JSP与Servlet的曾�与现在.md) -* [走进JavaWeb技术世界:JDBC的进化与连接池技术](docs/java-web/走进JavaWeb技术世界:JDBC的进化与连接池技术.md) -* [走进JavaWeb技术世界:Servlet工作原�详解](docs/java-web/走进JavaWeb技术世界:Servlet工作原�详解.md) -* [走进JavaWeb技术世界:�探Tomcat的HTTP请求过程](docs/java-web/走进JavaWeb技术世界:�探Tomcat的HTTP请求过程.md) -* [走进JavaWeb技术世界:Tomcat5总体架构剖�](docs/java-web/走进JavaWeb技术世界:Tomcat5总体架构剖�.md) -* [走进JavaWeb技术世界:Tomcat和其他WEB容器的区别](docs/java-web/走进JavaWeb技术世界:Tomcat和其他WEB容器的区别.md) -* [走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程](docs/java-web/走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程.md) -* [走进JavaWeb技术世界:Java日志系统的诞生与�展](docs/java-web/走进JavaWeb技术世界:Java日志系统的诞生与�展.md) -* [走进JavaWeb技术世界:从JavaBean讲到Spring](docs/java-web/走进JavaWeb技术世界:从JavaBean讲到Spring.md) -* [走进JavaWeb技术世界:�元测试框架Junit](docs/java-web/走进JavaWeb技术世界:�元测试框架Junit.md) -* [走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven](docs/java-web/走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven.md) -* [走进JavaWeb技术世界:Hibernate入门�典与注解�开�](docs/java-web/走进JavaWeb技术世界:Hibernate入门�典与注解�开�.md) -* [走进JavaWeb技术世界:Mybatis入门](docs/java-web/走进JavaWeb技术世界:Mybatis入门.md) -* [走进JavaWeb技术世界:深入浅出Mybatis基本原�](docs/java-web/走进JavaWeb技术世界:深入浅出Mybatis基本原�.md) -* [走进JavaWeb技术世界:�简�置的SpringBoot](docs/java-web/走进JavaWeb技术世界:�简�置的SpringBoot.md) - -### Spring - -* [Spring��剖�:Spring概述](docs/java-web/Spring/Spring��剖�:Spring概述.md) -* [Spring��剖�:�探SpringIOC核心�程](docs/java-web/Spring/Spring��剖�:�探SpringIOC核心�程.md) -* [Spring��剖�:SpringIOC容器的加载过程 ](docs/java-web/Spring/Spring��剖�:SpringIOC容器的加载过程.md) -* [Spring��剖�:懒加载的�例Bean获�过程分�](docs/java-web/Spring/Spring��剖�:懒加载的�例Bean获�过程分�.md) -* [Spring��剖�:JDK和cglib动�代�原�详解 ](docs/java-web/Spring/Spring��剖�:JDK和cglib动�代�原�详解.md) -* [Spring��剖�:SpringAOP概述](docs/java-web/Spring/Spring��剖�:SpringAOP概述.md) -* [Spring��剖�:AOP实现原�详解 ](docs/java-web/Spring/Spring��剖�:AOP实现原�详解.md) -* [Spring��剖�:Spring事务概述](docs/java-web/Spring/Spring��剖�:Spring事务概述.md) -* [Spring��剖�:Spring事务��剖�](docs/java-web/Spring/Spring��剖�:Spring事务��剖�.md) - -### SpringMVC - -* [SpringMVC��分�:SpringMVC概述](docs/java-web/SpringMVC/SpringMVC��分�:SpringMVC概述.md) -* [SpringMVC��分�:SpringMVC设计�念与DispatcherServlet](docs/java-web/SpringMVC/SpringMVC��分�:SpringMVC设计�念与DispatcherServlet.md) -* [SpringMVC��分�:DispatcherServlet的�始化与请求转� ](docs/java-web/SpringMVC/SpringMVC��分�:DispatcherServlet的�始化与请求转�.md) -* [SpringMVC��分�:DispatcherServlet如何找到正确的Controller ](docs/java-web/SpringMVC/SpringMVC��分�:DispatcherServlet如何找到正确的Controller.md) -* [SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解](docs/java-web/SpringMVC/SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解.md) -* [SpringMVC��分�:SpringMVC的视图解�原� ](docs/java-web/SpringMVC/SpringMVC��分�:SpringMVC的视图解�原�.md) - -### SpringBoot -todo - -### SpringCloud - -todo - -## Java进阶 - -### 并� - -* [Java并�指�:并�基础与Java多线程](docs/java/concurrency/Java并�指�:并�基础与Java多线程.md) -* [Java并�指�:深入�解Java内存模型JMM](docs/java/concurrency/Java并�指�:深入�解Java内存模型JMM.md) -* [Java并�指�:并�三大问题与volatile关键字,CAS�作](docs/java/concurrency/Java并�指�:并�三大问题与volatile关键字,CAS�作.md) -* [Java并�指�:Java中的�Lock和synchronized](docs/java/concurrency/Java并�指�:Java中的�Lock和synchronized.md) -* [Java并�指�:JMM中的final关键字解�](docs/java/concurrency/Java并�指�:JMM中的final关键字解�.md) -* [Java并�指�:Java内存模型JMM总结](docs/java/concurrency/Java并�指�:Java内存模型JMM总结.md) -* [Java并�指�:JUC的核心类AQS详解](docs/java/concurrency/Java并�指�:JUC的核心类AQS详解.md) -* [Java并�指�:AQS中的公平�与�公平�,Condtion](docs/java/concurrency/Java并�指�:AQS中的公平�与�公平�,Condtion.md) -* [Java并�指�:AQS共享模�与并�工具类的实现](docs/java/concurrency/Java并�指�:AQS共享模�与并�工具类的实现.md) -* [Java并�指�:Java读写�ReentrantReadWriteLock��分�](docs/java/concurrency/Java并�指�:Java读写�ReentrantReadWriteLock��分�.md) -* [Java并�指�:解读Java阻塞队列BlockingQueue](docs/java/concurrency/Java并�指�:解读Java阻塞队列BlockingQueue.md) -* [Java并�指�:深度解读java线程池设计�想���实现](docs/java/concurrency/Java并�指�:深度解读Java线程池设计�想���实现.md) -* [Java并�指�:Java中的HashMap和ConcurrentHashMap全解�](docs/java/concurrency/Java并�指�:Java中的HashMap和ConcurrentHashMap全解�.md) -* [Java并�指�:JUC中常用的Unsafe和Locksupport](docs/java/concurrency/Java并�指�:JUC中常用的Unsafe和Locksupport.md) -* [Java并�指�:ForkJoin并�框架与工作窃�算法剖�](docs/java/concurrency/Java并�指�:ForkJoin并�框架与工作窃�算法剖�.md) -* [Java并�编程学习总结](docs/java/concurrency/Java并�编程学习总结.md) - -### JVM - -* [JVM总结](docs/java/jvm/JVM总结.md) -* [深入�解JVM虚拟机:JVM内存的结构与消失的永久代](docs/java/jvm/深入�解JVM虚拟机:JVM内存的结构与消失的永久代.md) -* [深入�解JVM虚拟机:JVM垃圾回收基本原�和算法](docs/java/jvm/深入�解JVM虚拟机:JVM垃圾回收基本原�和算法.md) -* [深入�解JVM虚拟机:垃圾回收器详解](docs/java/jvm/深入�解JVM虚拟机:垃圾回收器详解.md) -* [深入�解JVM虚拟机:Javaclass介�与解�实践](docs/java/jvm/深入�解JVM虚拟机:Java字节�介�与解�实践.md) -* [深入�解JVM虚拟机:虚拟机字节�执行引擎](docs/java/jvm/深入�解JVM虚拟机:虚拟机字节�执行引擎.md) -* [深入�解JVM虚拟机:深入�解JVM类加载机制](docs/java/jvm/深入�解JVM虚拟机:深入�解JVM类加载机制.md) -* [深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现](docs/java/jvm/深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现.md) -* [深入了解JVM虚拟机:Java的编译期优化与�行期优化](docs/java/jvm/深入�解JVM虚拟机:Java的编译期优化与�行期优化.md) -* [深入�解JVM虚拟机:JVM监控工具与诊断实践](docs/java/jvm/深入�解JVM虚拟机:JVM监控工具与诊断实践.md) -* [深入�解JVM虚拟机:JVM常用�数以�调优实践](docs/java/jvm/深入�解JVM虚拟机:JVM常用�数以�调优实践.md) -* [深入�解JVM虚拟机:Java内存异常原�与实践](docs/java/jvm/深入�解JVM虚拟机:Java内存异常原�与实践.md) -* [深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战](docs/java/jvm/深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战.md) -* [深入�解JVM虚拟机:�谈四�引用�GC实践](docs/java/jvm/深入�解JVM虚拟机:�谈四�引用�GC实践.md) -* [深入�解JVM虚拟机:GC调优�路与常用工具](docs/java/jvm/深入�解JVM虚拟机:GC调优�路与常用工具.md) - -### Java网络编程 - -* [Java网络编程和NIO详解:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程与NIO详解:JAVA中原生的socket通信机制.md) -* [Java网络编程与NIO详解:JAVA NIO 一步步构建IO多路�用的请求模型](docs/java/network-programming/Java网络编程与NIO详解:JavaNIO一步步构建IO多路�用的请求模型.md) -* [Java网络编程和NIO详解:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程与NIO详解:IO模型与Java网络编程模型.md) -* [Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector](docs/java/network-programming/Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector.md) -* [Java网络编程和NIO详解:Java�阻塞IO和异步IO](docs/java/network-programming/Java网络编程与NIO详解:Java�阻塞IO和异步IO.md) -* [Java网络编程与NIO详解:LinuxEpoll实现原�详解](docs/java/network-programming/Java网络编程与NIO详解:LinuxEpoll实现原�详解.md.md) -* [Java网络编程与NIO详解:浅谈Linux中Selector的实现原�](docs/java/network-programming/Java网络编程与NIO详解:浅谈Linux中Selector的实现原�.md) -* [Java网络编程与NIO详解:浅�mmap和DirectBuffer](docs/java/network-programming/Java网络编程与NIO详解:浅�mmap和DirectBuffer.md) -* [Java网络编程与NIO详解:基于NIO的网络编程框架Netty](docs/java/network-programming/Java网络编程与NIO详解:基于NIO的网络编程框架Netty.md) -* [Java网络编程与NIO详解:Java网络编程与NIO详解](docs/java/network-programming/Java网络编程与NIO详解:深度解读Tomcat中的NIO模型.md) -* [Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO)](docs/java/network-programming/Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO).md) - -## 计算机基础 - -### 计算机网络 +

+ +# Java基础 + +## 基础知识 +* [��对象基础](docs/Java/basic/��对象基础.md) +* [Java基本数�类型](docs/Java/basic/Java基本数�类型.md) +* [string和包装类](docs/Java/basic/string和包装类.md) +* [final关键字特性](docs/Java/basic/final关键字特性.md) +* [Java类和包](docs/Java/basic/Java类和包.md) +* [抽象类和接�](docs/Java/basic/抽象类和接�.md) +* [代��和代�执行顺�](docs/Java/basic/代��和代�执行顺�.md) +* [Java自动拆箱装箱里��的秘密](docs/Java/basic/Java自动拆箱装箱里��的秘密.md) +* [Java中的Class类和Object类](docs/Java/basic/Java中的Class类和Object类.md) +* [Java异常](docs/Java/basic/Java异常.md) +* [解读Java中的回调](docs/Java/basic/解读Java中的回调.md) +* [�射](docs/Java/basic/�射.md) +* [泛型](docs/Java/basic/泛型.md) +* [枚举类](docs/Java/basic/枚举类.md) +* [Java注解和最佳实践](docs/Java/basic/Java注解和最佳实践.md) +* [JavaIO�](docs/Java/basic/JavaIO�.md) +* [多线程](docs/Java/basic/多线程.md) +* [深入�解内部类](docs/Java/basic/深入�解内部类.md) +* [javac和javap](docs/Java/basic/javac和javap.md) +* [Java8新特性终�指�](docs/Java/basic/Java8新特性终�指�.md) +* [�列化和��列化](docs/Java/basic/�列化和��列化.md) +* [继承�装多�的实现原�](docs/Java/basic/继承�装多�的实现原�.md) + +## 集�类 + +* [Java集�类总结](docs/Java/collection/Java集�类总结.md) +* [Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�](docs/Java/collection/Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�.md) +* [Java集�详解:Queue和LinkedList](docs/Java/collection/Java集�详解:Queue和LinkedList.md) +* [Java集�详解:Iterator,fail-fast机制与比较器](docs/Java/collection/Java集�详解:Iterator,fail-fast机制与比较器.md) +* [Java集�详解:HashMap和HashTable](docs/Java/collection/Java集�详解:HashMap和HashTable.md) +* [Java集�详解:深入�解LinkedHashMap和LRU缓存](docs/Java/collection/Java集�详解:深入�解LinkedHashMap和LRU缓存.md) +* [Java集�详解:TreeMap和红黑树](docs/Java/collection/Java集�详解:TreeMap和红黑树.md) +* [Java集�详解:HashSet,TreeSet与LinkedHashSet](docs/Java/collection/Java集�详解:HashSet,TreeSet与LinkedHashSet.md) +* [Java集�详解:Java集�类细节精讲](docs/Java/collection/Java集�详解:Java集�类细节精讲.md) + +# JavaWeb + +* [走进JavaWeb技术世界:JavaWeb的由�和基础知识](docs/JavaWeb/走进JavaWeb技术世界:JavaWeb的由�和基础知识.md) +* [走进JavaWeb技术世界:JSP与Servlet的曾�与现在](docs/JavaWeb/走进JavaWeb技术世界:JSP与Servlet的曾�与现在.md) +* [走进JavaWeb技术世界:JDBC的进化与连接池技术](docs/JavaWeb/走进JavaWeb技术世界:JDBC的进化与连接池技术.md) +* [走进JavaWeb技术世界:Servlet工作原�详解](docs/JavaWeb/走进JavaWeb技术世界:Servlet工作原�详解.md) +* [走进JavaWeb技术世界:�探Tomcat的HTTP请求过程](docs/JavaWeb/走进JavaWeb技术世界:�探Tomcat的HTTP请求过程.md) +* [走进JavaWeb技术世界:Tomcat5总体架构剖�](docs/JavaWeb/走进JavaWeb技术世界:Tomcat5总体架构剖�.md) +* [走进JavaWeb技术世界:Tomcat和其他WEB容器的区别](docs/JavaWeb/走进JavaWeb技术世界:Tomcat和其他WEB容器的区别.md) +* [走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程](docs/JavaWeb/走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程.md) +* [走进JavaWeb技术世界:Java日志系统的诞生与�展](docs/JavaWeb/走进JavaWeb技术世界:Java日志系统的诞生与�展.md) +* [走进JavaWeb技术世界:从JavaBean讲到Spring](docs/JavaWeb/走进JavaWeb技术世界:从JavaBean讲到Spring.md) +* [走进JavaWeb技术世界:�元测试框架Junit](docs/JavaWeb/走进JavaWeb技术世界:�元测试框架Junit.md) +* [走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven](docs/JavaWeb/走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven.md) +* [走进JavaWeb技术世界:Hibernate入门�典与注解�开�](docs/JavaWeb/走进JavaWeb技术世界:Hibernate入门�典与注解�开�.md) +* [走进JavaWeb技术世界:Mybatis入门](docs/JavaWeb/走进JavaWeb技术世界:Mybatis入门.md) +* [走进JavaWeb技术世界:深入浅出Mybatis基本原�](docs/JavaWeb/走进JavaWeb技术世界:深入浅出Mybatis基本原�.md) +* [走进JavaWeb技术世界:�简�置的SpringBoot](docs/JavaWeb/走进JavaWeb技术世界:�简�置的SpringBoot.md) + +# Java进阶 + +## 并�编程 + +* [Java并�指�:并�基础与Java多线程](docs/Java/concurrency/Java并�指�:并�基础与Java多线程.md) +* [Java并�指�:深入�解Java内存模型JMM](docs/Java/concurrency/Java并�指�:深入�解Java内存模型JMM.md) +* [Java并�指�:并�三大问题与volatile关键字,CAS�作](docs/Java/concurrency/Java并�指�:并�三大问题与volatile关键字,CAS�作.md) +* [Java并�指�:Java中的�Lock和synchronized](docs/Java/concurrency/Java并�指�:Java中的�Lock和synchronized.md) +* [Java并�指�:JMM中的final关键字解�](docs/Java/concurrency/Java并�指�:JMM中的final关键字解�.md) +* [Java并�指�:Java内存模型JMM总结](docs/Java/concurrency/Java并�指�:Java内存模型JMM总结.md) +* [Java并�指�:JUC的核心类AQS详解](docs/Java/concurrency/Java并�指�:JUC的核心类AQS详解.md) +* [Java并�指�:AQS中的公平�与�公平�,Condtion](docs/Java/concurrency/Java并�指�:AQS中的公平�与�公平�,Condtion.md) +* [Java并�指�:AQS共享模�与并�工具类的实现](docs/Java/concurrency/Java并�指�:AQS共享模�与并�工具类的实现.md) +* [Java并�指�:Java读写�ReentrantReadWriteLock��分�](docs/Java/concurrency/Java并�指�:Java读写�ReentrantReadWriteLock��分�.md) +* [Java并�指�:解读Java阻塞队列BlockingQueue](docs/Java/concurrency/Java并�指�:解读Java阻塞队列BlockingQueue.md) +* [Java并�指�:深度解读java线程池设计�想���实现](docs/Java/concurrency/Java并�指�:深度解读Java线程池设计�想���实现.md) +* [Java并�指�:Java中的HashMap和ConcurrentHashMap全解�](docs/Java/concurrency/Java并�指�:Java中的HashMap和ConcurrentHashMap全解�.md) +* [Java并�指�:JUC中常用的Unsafe和Locksupport](docs/Java/concurrency/Java并�指�:JUC中常用的Unsafe和Locksupport.md) +* [Java并�指�:ForkJoin并�框架与工作窃�算法剖�](docs/Java/concurrency/Java并�指�:ForkJoin并�框架与工作窃�算法剖�.md) +* [Java并�编程学习总结](docs/Java/concurrency/Java并�编程学习总结.md) + +## JVM + +* [JVM总结](docs/Java/JVM/JVM总结.md) +* [深入�解JVM虚拟机:JVM内存的结构与消失的永久代](docs/Java/JVM/深入�解JVM虚拟机:JVM内存的结构与消失的永久代.md) +* [深入�解JVM虚拟机:JVM垃圾回收基本原�和算法](docs/Java/JVM/深入�解JVM虚拟机:JVM垃圾回收基本原�和算法.md) +* [深入�解JVM虚拟机:垃圾回收器详解](docs/Java/JVM/深入�解JVM虚拟机:垃圾回收器详解.md) +* [深入�解JVM虚拟机:Javaclass介�与解�实践](docs/Java/JVM/深入�解JVM虚拟机:Java字节�介�与解�实践.md) +* [深入�解JVM虚拟机:虚拟机字节�执行引擎](docs/Java/JVM/深入�解JVM虚拟机:虚拟机字节�执行引擎.md) +* [深入�解JVM虚拟机:深入�解JVM类加载机制](docs/Java/JVM/深入�解JVM虚拟机:深入�解JVM类加载机制.md) +* [深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现](docs/Java/JVM/深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现.md) +* [深入了解JVM虚拟机:Java的编译期优化与�行期优化](docs/Java/JVM/深入�解JVM虚拟机:Java的编译期优化与�行期优化.md) +* [深入�解JVM虚拟机:JVM监控工具与诊断实践](docs/Java/JVM/深入�解JVM虚拟机:JVM监控工具与诊断实践.md) +* [深入�解JVM虚拟机:JVM常用�数以�调优实践](docs/Java/JVM/深入�解JVM虚拟机:JVM常用�数以�调优实践.md) +* [深入�解JVM虚拟机:Java内存异常原�与实践](docs/Java/JVM/深入�解JVM虚拟机:Java内存异常原�与实践.md) +* [深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战](docs/Java/JVM/深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战.md) +* [深入�解JVM虚拟机:�谈四�引用�GC实践](docs/Java/JVM/深入�解JVM虚拟机:�谈四�引用�GC实践.md) +* [深入�解JVM虚拟机:GC调优�路与常用工具](docs/Java/JVM/深入�解JVM虚拟机:GC调优�路与常用工具.md) + +## Java网络编程 + +* [Java网络编程和NIO详解:JAVA 中原生的 socket 通信机制](docs/Java/network/Java网络编程与NIO详解:JAVA中原生的socket通信机制.md) +* [Java网络编程与NIO详解:JAVA NIO 一步步构建IO多路�用的请求模型](docs/Java/network/Java网络编程与NIO详解:JavaNIO一步步构建IO多路�用的请求模型.md) +* [Java网络编程和NIO详解:IO模型与Java网络编程模型](docs/Java/network/Java网络编程与NIO详解:IO模型与Java网络编程模型.md) +* [Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector](docs/Java/network/Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector.md) +* [Java网络编程和NIO详解:Java�阻塞IO和异步IO](docs/Java/network/Java网络编程与NIO详解:Java�阻塞IO和异步IO.md) +* [Java网络编程与NIO详解:LinuxEpoll实现原�详解](docs/Java/network/Java网络编程与NIO详解:LinuxEpoll实现原�详解.md.md) +* [Java网络编程与NIO详解:浅谈Linux中Selector的实现原�](docs/Java/network/Java网络编程与NIO详解:浅谈Linux中Selector的实现原�.md) +* [Java网络编程与NIO详解:浅�mmap和DirectBuffer](docs/Java/network/Java网络编程与NIO详解:浅�mmap和DirectBuffer.md) +* [Java网络编程与NIO详解:基于NIO的网络编程框架Netty](docs/Java/network/Java网络编程与NIO详解:基于NIO的网络编程框架Netty.md) +* [Java网络编程与NIO详解:Java网络编程与NIO详解](docs/Java/network/Java网络编程与NIO详解:深度解读Tomcat中的NIO模型.md) +* [Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO)](docs/Java/network/Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO).md) + +# Spring全家桶 + +## Spring + +* [SpringAOP的概念与作用](docs/Spring全家桶/Spring/Spring常�注解.md) +* [SpringBean的定义与管�(核心)](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中对于数�库的访问](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中对于校验功能的支�](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的Environment环境��](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的事件处�机制](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的资�管�](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的�置元数�(管��置的基本数�)](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring事务基本用法](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring�集](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring容器与IOC](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring常�注解](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring概述](docs/Spring全家桶/Spring/Spring常�注解.md) +* [第一个Spring应用](docs/Spring全家桶/Spring/Spring常�注解.md) + +## Spring��分� + +### 综� +* [Spring��剖�:�探SpringIOC核心�程](docs/Spring全家桶/Spring��分�/Spring��剖�:�探SpringIOC核心�程.md) +* [Spring��剖�:SpringIOC容器的加载过程 ](docs/Spring全家桶/Spring��分�/Spring��剖�:SpringIOC容器的加载过程.md) +* [Spring��剖�:懒加载的�例Bean获�过程分�](docs/Spring全家桶/Spring��分�/Spring��剖�:懒加载的�例Bean获�过程分�.md) +* [Spring��剖�:JDK和cglib动�代�原�详解 ](docs/Spring全家桶/Spring��分�/Spring��剖�:JDK和cglib动�代�原�详解.md) +* [Spring��剖�:SpringAOP概述](docs/Spring全家桶/Spring��分�/Spring��剖�:SpringAOP概述.md) +* [Spring��剖�:AOP实现原�详解 ](docs/Spring全家桶/Spring��分�/Spring��剖�:AOP实现原�详解.md) +* [Spring��剖�:Spring事务概述](docs/Spring全家桶/Spring��分�/Spring��剖�:Spring事务概述.md) +* [Spring��剖�:Spring事务��剖�](docs/Spring全家桶/Spring��分�/Spring��剖�:Spring事务��剖�.md) + +### AOP +* [AnnotationAwareAspectJAutoProxyCreator 分�(上)](docs/Spring全家桶/Spring��分�/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分�(上).md) +* [AnnotationAwareAspectJAutoProxyCreator 分�(下)](docs/Spring全家桶/Spring��分�/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分�(下).md) +* [AOP示例demo�@EnableAspectJAutoProxy](docs/Spring全家桶/Spring��分�/SpringAOP/AOP示例demo�@EnableAspectJAutoProxy.md) +* [SpringAop(四):jdk 动�代�](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(四):jdk动�代�.md) +* [SpringAop(五):cglib 代�](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(五):cglib代�.md) +* [SpringAop(六):aop 总结](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(六):aop总结.md) + +### 事务 +* [spring 事务(一):认识事务组件](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(一):认识事务组件.md) +* [spring 事务(二):事务的执行�程](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(二):事务的执行�程.md) +* [spring 事务(三):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(三):事务的隔离级别与传播方�的处�01.md) +* [spring 事务(四):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(四):事务的隔离级别与传播方�的处�02.md) +* [spring 事务(五):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(五):事务的隔离级别与传播方�的处�03.md) +* [spring 事务(六):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(六):事务的隔离级别与传播方�的处�04.md) + +### �动�程 +* [spring�动�程(一):�动�程概览](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(一):�动�程概览.md) +* [spring�动�程(二):ApplicationContext 的创建](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(二):ApplicationContext的创建.md) +* [spring�动�程(三):包的扫��程](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(三):包的扫��程.md) +* [spring�动�程(四):�动�的准备工作](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(四):�动�的准备工作.md) +* [spring�动�程(五):执行 BeanFactoryPostProcessor](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(五):执行BeanFactoryPostProcessor.md) +* [spring�动�程(六):注册 BeanPostProcessor](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(六):注册BeanPostProcessor.md) +* [spring�动�程(七):国际化与事件处�](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(七):国际化与事件处�.md) +* [spring�动�程(八):完� BeanFactory 的�始化](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(八):完�BeanFactory的�始化.md) +* [spring�动�程(�):�例 bean 的创建](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�):�例bean的创建.md) +* [spring�动�程(�):�动完�的处�](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�):�动完�的处�.md) +* [spring�动�程(�一):�动�程总结](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�一):�动�程总结.md) + +### 组件分� +* [spring 组件之 ApplicationContext](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之ApplicationContext.md) +* [spring 组件之 BeanDefinition](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanDefinition.md) +* [Spring 组件之 BeanFactory](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanFactory.md) +* [spring 组件之 BeanFactoryPostProcessor](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanFactoryPostProcessor.md) +* [spring 组件之 BeanPostProcessor](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanPostProcessor.md) + +### ��机制探秘 + +* [ConfigurationClassPostProcessor(一):处� @ComponentScan 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(一):处�@ComponentScan注解.md) +* [ConfigurationClassPostProcessor(三):处� @Import 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(三):处�@Import注解.md) +* [ConfigurationClassPostProcessor(二):处� @Bean 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(二):处�@Bean注解.md) +* [ConfigurationClassPostProcessor(四):处� @Conditional 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(四):处�@Conditional注解.md) +* [Spring 探秘之 AOP 的执行顺�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之AOP的执行顺�.md) +* [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之Spring事件机制.md) +* [spring 探秘之循环�赖的解决(一):�论基石](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之循环�赖的解决(一):�论基石.md) +* [spring 探秘之循环�赖的解决(二):��分�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之循环�赖的解决(二):��分�.md) +* [spring 探秘之监�器注解 @EventListener](docs/Spring全家桶/Spring��分�/Spring��机制探秘/spring探秘之监�器注解@EventListener.md) +* [spring 探秘之组�注解的处�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之组�注解的处�.md) + +## SpringMVC + +* [SpringMVC中的国际化功能](docs/Spring全家桶/SpringMVC/SpringMVC中的国际化功能.md) +* [SpringMVC中的异常处�器](docs/Spring全家桶/SpringMVC/SpringMVC中的异常处�器.md) +* [SpringMVC中的拦截器](docs/Spring全家桶/SpringMVC/SpringMVC中的拦截器.md) +* [SpringMVC中的视图解�器](docs/Spring全家桶/SpringMVC/SpringMVC中的视图解�器.md) +* [SpringMVC中的过滤器Filter](docs/Spring全家桶/SpringMVC/SpringMVC中的过滤器Filter.md) +* [SpringMVC基本介�与快速入门](docs/Spring全家桶/SpringMVC/SpringMVC基本介�与快速入门.md) +* [SpringMVC如何实现文件上传](docs/Spring全家桶/SpringMVC/SpringMVC如何实现文件上传.md) +* [SpringMVC中的常用功能](docs/Spring全家桶/SpringMVC/SpringMVC中的常用功能.md) + +## SpringMVC��分� + +* [SpringMVC��分�:SpringMVC概述](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:SpringMVC概述.md) +* [SpringMVC��分�:SpringMVC设计�念与DispatcherServlet](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:SpringMVC设计�念与DispatcherServlet.md) +* [SpringMVC��分�:DispatcherServlet的�始化与请求转� ](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:DispatcherServlet的�始化与请求转�.md) +* [SpringMVC��分�:DispatcherServlet如何找到正确的Controller ](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:DispatcherServlet如何找到正确的Controller.md) +* [SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解](docs/Spring全家桶/SpringMVC/SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解.md) +* [DispatcherServlet �始化�程 ](docs/Spring全家桶/SpringMVC��分�/DispatcherServlet�始化�程.md) +* [RequestMapping �始化�程 ](docs/Spring全家桶/SpringMVC��分�/RequestMapping�始化�程.md) +* [Spring 容器�动 Tomcat ](docs/Spring全家桶/SpringMVC��分�/Spring容器�动Tomcat.md) +* [SpringMVC demo 与@EnableWebMvc 注解 ](docs/Spring全家桶/SpringMVC��分�/SpringMVC的Demo与@EnableWebMvc注解.md) +* [SpringMVC 整体��结构总结 ](docs/Spring全家桶/SpringMVC��分�/SpringMVC整体��结构总结.md) +* [请求执行�程(一)之获� Handler ](docs/Spring全家桶/SpringMVC��分�/请求执行�程(一)之获�Handler.md) +* [请求执行�程(二)之执行 Handler 方法 ](docs/Spring全家桶/SpringMVC��分�/请求执行�程(二)之执行Handler方法.md) + +## SpringBoot + +* [SpringBoot系列:SpringBoot的�世今生](docs/Spring全家桶/SpringBoot/SpringBoot的�世今生.md) +* [给你一份SpringBoot知识清�.md](docs/Spring全家桶/SpringBoot/给你一份SpringBoot知识清�.md) +* [Spring常�注解使用指�(包�Spring+SpringMVC+SpringBoot)](docs/Spring全家桶/SpringBoot/Spring常�注解使用指�(包�Spring+SpringMVC+SpringBoot).md) +* [SpringBoot中的日志管�](docs/Spring全家桶/SpringBoot/SpringBoot中的日志管�.md) +* [SpringBoot常�注解](docs/Spring全家桶/SpringBoot/SpringBoot常�注解.md) +* [SpringBoot应用也�以部署到外部Tomcat](docs/Spring全家桶/SpringBoot/SpringBoot应用也�以部署到外部Tomcat.md) +* [SpringBoot生产环境工具Actuator](docs/Spring全家桶/SpringBoot/SpringBoot生产环境工具Actuator.md) +* [SpringBoot的Starter机制](docs/Spring全家桶/SpringBoot/SpringBoot的Starter机制.md) +* [SpringBoot的�世今生](docs/Spring全家桶/SpringBoot/SpringBoot的�世今生.md) +* [SpringBoot的基本使用](docs/Spring全家桶/SpringBoot/SpringBoot的基本使用.md) +* [SpringBoot的�置文件管�](docs/Spring全家桶/SpringBoot/SpringBoot的�置文件管�.md) +* [SpringBoot自带的热部署工具](docs/Spring全家桶/SpringBoot/SpringBoot自带的热部署工具.md) +* [SpringBoot中的任务调度与@Async](docs/Spring全家桶/SpringBoot/SpringBoot中的任务调度与@Async.md) +* [基于SpringBoot中的开�监控工具SpringBootAdmin](docs/Spring全家桶/SpringBoot/基于SpringBoot中的开�监控工具SpringBootAdmin.md) + +## SpringBoot��分� +* [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot��解�/@SpringBootApplication注解.md) +* [springboot web应用(一):servlet 组件的注册�程](docs/Spring全家桶/SpringBoot��解�/SpringBootWeb应用(一):servlet组件的注册�程.md) +* [springboot web应用(二):WebMvc 装�过程](docs/Spring全家桶/SpringBoot��解�/SpringBootWeb应用(二):WebMvc装�过程.md) + +* [SpringBoot �动�程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(一):准备SpringApplication.md) +* [SpringBoot �动�程(二):准备�行环境](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(二):准备�行环境.md) +* [SpringBoot �动�程(三):准备IOC容器](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(三):准备IOC容器.md) +* [springboot �动�程(四):�动IOC容器](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(四):�动IOC容器.md) +* [springboot �动�程(五):完��动](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(五):完��动.md) +* [springboot �动�程(六):�动�程总结](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(六):�动�程总结.md) + +* [springboot 自动装�(一):加载自动装�类](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(一):加载自动装�类.md) +* [springboot 自动装�(二):�件注解](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(二):�件注解.md) +* [springboot 自动装�(三):自动装�顺�](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(三):自动装�顺�.md) + +## SpringCloud +* [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) +* [Spring Cloud Config](docs/Spring全家桶/SpringCloud/SpringCloudConfig.md) +* [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/SpringCloudConsul.md) +* [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/SpringCloudEureka.md) +* [Spring Cloud Gateway](docs/Spring全家桶/SpringCloud/SpringCloudGateway.md) +* [Spring Cloud Hystrix](docs/Spring全家桶/SpringCloud/SpringCloudHystrix.md) +* [Spring Cloud LoadBalancer](docs/Spring全家桶/SpringCloud/SpringCloudLoadBalancer.md) +* [Spring Cloud OpenFeign](docs/Spring全家桶/SpringCloud/SpringCloudOpenFeign.md) +* [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/SpringCloudRibbon.md) +* [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/SpringCloudSleuth.md) +* [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/SpringCloudZuul.md) + +## SpringCloud ��分� +* [Spring Cloud Config��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudConfig��分�.md) +* [Spring Cloud Eureka��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudEureka��分�.md) +* [Spring Cloud Gateway��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudGateway��分�.md) +* [Spring Cloud Hystrix��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudHystrix��分�.md) +* [Spring Cloud LoadBalancer��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudLoadBalancer��分�.md) +* [Spring Cloud OpenFeign��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudOpenFeign��分�.md) +* [Spring Cloud Ribbon��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudRibbon��分�.md) + +## SpringCloud Alibaba +* [SpringCloud Alibaba概览](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibaba概览.md) +* [SpringCloud Alibaba nacos](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaNacos.md) +* [SpringCloud Alibaba RocketMQ](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md) +* [SpringCloud Alibaba sentinel](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md) +* [SpringCloud Alibaba skywalking](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md) +* [SpringCloud Alibaba seata](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSeata.md) + +## SpringCloud Alibaba��分� +* [Spring Cloud Seata��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudSeata��分�.md) +* [Spring Cloud Sentinel��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudSentinel��分�.md) +* [SpringCloudAlibaba nacos��分�:概览](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:概览.md) +* [SpringCloudAlibaba nacos��分�:�务�现](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�务�现.md) +* [SpringCloudAlibaba nacos��分�:�务注册](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�务注册.md) +* [SpringCloudAlibaba nacos��分�:�置中心](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�置中心.md) +* [Spring Cloud RocketMQ��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudRocketMQ��分�.md) + +# 设计模� + +* [设计模�学习总结](docs/Java/design-parttern/设计模�学习总结.md) +* [�探Java设计模�:创建型模�(工厂,�例等).md](docs/Java/design-parttern/�探Java设计模�:创建型模�(工厂,�例等).md) +* [�探Java设计模�:结构型模�(代�模�,适�器模�等).md](docs/Java/design-parttern/�探Java设计模�:结构型模�(代�模�,适�器模�等).md) +* [�探Java设计模�:行为型模�(策略,观察者等).md](docs/Java/design-parttern/�探Java设计模�:行为型模�(策略,观察者等).md) +* [�探Java设计模�:JDK中的设计模�.md](docs/Java/design-parttern/�探Java设计模�:JDK中的设计模�.md) +* [�探Java设计模�:Spring涉�到的�设计模�.md](docs/Java/design-parttern/�探Java设计模�:Spring涉�到的�设计模�.md) + + +# 计算机基础 + +## 计算机网络 todo -### �作系统 +## �作系统 todo -#### Linux相关 +## Linux相关 todo -### 数�结构与算法 +## 数�结构与算法 todo -#### 数�结构 +## 数�结构 todo -#### 算法 +## 算法 todo -## 数�库 +# 数�库 todo -### MySQL +## MySQL * [Mysql原�与实践总结](docs/database/Mysql原�与实践总结.md) * [�新学习Mysql数�库:无废�MySQL入门](docs/database/�新学习MySQL数�库:无废�MySQL入门.md) * [�新学习Mysql数�库:『浅入浅出�MySQL和InnoDB](docs/database/�新学习MySQL数�库:『浅入浅出�MySQL和InnoDB.md) @@ -266,9 +372,9 @@ todo * [�新学习Mysql数�库:Mysql主从�制,读写分离,分表分库策略与实践](docs/database/�新学习MySQL数�库:Mysql主从�制,读写分离,分表分库策略与实践.md) -## 缓存 +# 缓存 -### Redis +## Redis * [Redis原�与实践总结](docs/cache/Redis原�与实践总结.md) * [探索Redis设计与实现开篇:什么是Redis](docs/cache/探索Redis设计与实现开篇:什么是Redis.md) * [探索Redis设计与实现:Redis的基础数�结构概览](docs/cache/探索Redis设计与实现:Redis的基础数�结构概览.md) @@ -287,27 +393,46 @@ todo * [探索Redis设计与实现:Redis事务浅�与ACID特性介�](docs/cache/探索Redis设计与实现:Redis事务浅�与ACID特性介�.md) * [探索Redis设计与实现:Redis分布��进化� ](docs/cache/探索Redis设计与实现:Redis分布��进化�.md ) -## 消�队列 - -### Kafka - -## 大�端 -* [�端技术�谈开篇:云计算,大数�与AI的故事](docs/big-backEnd/�端技术�谈开篇:云计算,大数�与AI的故事.md) -* [�端技术�谈:�索引擎基础倒排索引](docs/big-backEnd/�端技术�谈:�索引擎基础倒排索引.md) -* [�端技术�谈:�索引擎工作原�](docs/big-backEnd/�端技术�谈:�索引擎工作原�.md) -* [�端技术�谈:Lucene基础原�与实践](docs/big-backEnd/�端技术�谈:Lucene基础原�与实践.md) -* [�端技术�谈:Elasticsearch与solr入门实践](docs/big-backEnd/�端技术�谈:Elasticsearch与solr入门实践.md) -* [�端技术�谈:云计算的�世今生](docs/big-backEnd/�端技术�谈:云计算的�世今生.md) -* [�端技术�谈:白�虚拟化技术](docs/big-backEnd/�端技术�谈:白�虚拟化技术.md ) -* [�端技术�谈:OpenStack的基石KVM](docs/big-backEnd/�端技术�谈:OpenStack的基石KVM.md) -* [�端技术�谈:OpenStack架构设计](docs/big-backEnd/�端技术�谈:OpenStack架构设计.md) -* [�端技术�谈:先�懂Docker核心概念�](docs/big-backEnd/�端技术�谈:先�懂Docker核心概念�.md) -* [�端技术�谈:Docker 核心技术与实现原�](docs/big-backEnd/�端技术�谈:Docker%核心技术与实现原�.md) -* [�端技术�谈:�分钟�解Kubernetes核心概念](docs/big-backEnd/�端技术�谈:�分钟�解Kubernetes核心概念.md) -* [�端技术�谈:�一�大数�研�的基本概念](docs/big-backEnd/�端技术�谈:�一�大数�研�的基本概念.md) - -## 分布� -### �论 +# 消�队列 + +## Kafka +* [消�队列kafka详解:Kafka快速上手(Java版)](docs/mq/kafka/消�队列kafka详解:Kafka快速上手(Java版).md) +* [消�队列kafka详解:Kafka一�消�存到broker的过程](docs/mq/kafka/消�队列kafka详解:Kafka一�消�存到broker的过程.md) +* [消�队列kafka详解:消�队列kafka详解:Kafka介�](docs/mq/kafka/消�队列kafka详解:Kafka介�.md) +* [消�队列kafka详解:Kafka原�分�总结篇](docs/mq/kafka/消�队列kafka详解:Kafka原�分�总结篇.md) +* [消�队列kafka详解:Kafka常�命令��置总结](docs/mq/kafka/消�队列kafka详解:Kafka常�命令��置总结.md) +* [消�队列kafka详解:Kafka架构介�](docs/mq/kafka/消�队列kafka详解:Kafka架构介�.md) +* [消�队列kafka详解:Kafka的集群工作原�](docs/mq/kafka/消�队列kafka详解:Kafka的集群工作原�.md) +* [消�队列kafka详解:Kafka��知识点+�试题大全](docs/mq/kafka/消�队列kafka详解:Kafka��知识点+�试题大全.md) +* [消�队列kafka详解:如何实现延迟队列](docs/mq/kafka/消�队列kafka详解:如何实现延迟队列.md) +* [消�队列kafka详解:如何实现死信队列](docs/mq/kafka/消�队列kafka详解:如何实现死信队列.md) + +## RocketMQ +* [RocketMQ系列:事务消�(最终一致性)](docs/mq/RocketMQ/RocketMQ系列:事务消�(最终一致性).md) +* [RocketMQ系列:基本概念](docs/mq/RocketMQ/RocketMQ系列:基本概念.md) +* [RocketMQ系列:广播与延迟消�](docs/mq/RocketMQ/RocketMQ系列:广播与延迟消�.md) +* [RocketMQ系列:批���与过滤](docs/mq/RocketMQ/RocketMQ系列:批���与过滤.md) +* [RocketMQ系列:消�的生产与消费](docs/mq/RocketMQ/RocketMQ系列:消�的生产与消费.md) +* [RocketMQ系列:环境�建](docs/mq/RocketMQ/RocketMQ系列:环境�建.md) +* [RocketMQ系列:顺�消费](docs/mq/RocketMQ/RocketMQ系列:顺�消费.md) + +# 大�端 +* [�端技术�谈开篇:云计算,大数�与AI的故事](docs/backend/�端技术�谈开篇:云计算,大数�与AI的故事.md) +* [�端技术�谈:�索引擎基础倒排索引](docs/backend/�端技术�谈:�索引擎基础倒排索引.md) +* [�端技术�谈:�索引擎工作原�](docs/backend/�端技术�谈:�索引擎工作原�.md) +* [�端技术�谈:Lucene基础原�与实践](docs/backend/�端技术�谈:Lucene基础原�与实践.md) +* [�端技术�谈:Elasticsearch与solr入门实践](docs/backend/�端技术�谈:Elasticsearch与solr入门实践.md) +* [�端技术�谈:云计算的�世今生](docs/backend/�端技术�谈:云计算的�世今生.md) +* [�端技术�谈:白�虚拟化技术](docs/backend/�端技术�谈:白�虚拟化技术.md ) +* [�端技术�谈:OpenStack的基石KVM](docs/backend/�端技术�谈:OpenStack的基石KVM.md) +* [�端技术�谈:OpenStack架构设计](docs/backend/�端技术�谈:OpenStack架构设计.md) +* [�端技术�谈:先�懂Docker核心概念�](docs/backend/�端技术�谈:先�懂Docker核心概念�.md) +* [�端技术�谈:Docker 核心技术与实现原�](docs/backend/�端技术�谈:Docker%核心技术与实现原�.md) +* [�端技术�谈:�分钟�解Kubernetes核心概念](docs/backend/�端技术�谈:�分钟�解Kubernetes核心概念.md) +* [�端技术�谈:�一�大数�研�的基本概念](docs/backend/�端技术�谈:�一�大数�研�的基本概念.md) + +# 分布� +## 分布��论 * [分布�系统�论基础:一致性PC和PC ](docs/distributed/basic/分布�系统�论基础:一致性PC和PC.md) * [分布�系统�论基础:CAP ](docs/distributed/basic/分布�系统�论基础:CAP.md) * [分布�系统�论基础:时间时钟和事件顺�](docs/distributed/basic/分布�系统�论基础:时间时钟和事件顺�.md) @@ -316,22 +441,15 @@ todo * [分布�系统�论基础:RaftZab ](docs/distributed/basic/分布�系统�论基础:RaftZab.md) * [分布�系统�论进阶:Paxos��和优化 ](docs/distributed/basic/分布�系统�论进阶:Paxos��和优化.md) * [分布�系统�论基础:zookeeper分布��调�务 ](docs/distributed/basic/分布�系统�论基础:zookeeper分布��调�务.md) +* [分布��论总结](docs/distributed/分布�技术实践总结.md) -* [分布�技术实践总结](docs/distributed/分布��论总结.md) - -### 技术 +## 分布�技术 * [�懂分布�技术:分布�系统的一些基本概念](docs/distributed/practice/�懂分布�技术:分布�系统的一些基本概念.md ) * [�懂分布�技术:分布�一致性�议与Paxos,Raft算法](docs/distributed/practice/�懂分布�技术:分布�一致性�议与Paxos,Raft算法.md) * [�懂分布�技术:�探分布��调�务zookeeper](docs/distributed/practice/�懂分布�技术:�探分布��调�务zookeeper.md ) * [�懂分布�技术:ZAB�议概述与选主�程详解](docs/distributed/practice/�懂分布�技术:ZAB�议概述与选主�程详解.md ) * [�懂分布�技术:Zookeeper的�置与集群管�实战](docs/distributed/practice/�懂分布�技术:Zookeeper的�置与集群管�实战.md) * [�懂分布�技术:Zookeeper典型应用场景�实践](docs/distributed/practice/�懂分布�技术:Zookeeper典型应用场景�实践.md ) - -[//]: # (* [�懂分布�技术:负载�衡概念与主�方案]&#;docs/distributed/practice/�懂分布�技术:负载�衡概念与主�方案.md&#;) - -[//]: # (* [�懂分布�技术:负载�衡原�剖� ]&#;docs/distributed/practice/�懂分布�技术:负载�衡原�剖�.md &#;) - -[//]: # (* [�懂分布�技术:Nginx负载�衡原�与实践 ]&#;docs/distributed/practice/�懂分布�技术:Nginx负载�衡原�与实践.md&#;) * [�懂分布�技术:LVS实现负载�衡的原�与实践 ](docs/distributed/practice/�懂分布�技术:LVS实现负载�衡的原�与实践.md ) * [�懂分布�技术:分布�session解决方案与一致性hash](docs/distributed/practice/�懂分布�技术:分布�session解决方案与一致性hash.md) * [�懂分布�技术:分布�ID生�方案 ](docs/distributed/practice/�懂分布�技术:分布�ID生�方案.md ) @@ -344,31 +462,31 @@ todo * [�懂分布�技术:使用RocketMQ事务消�解决分布�事务 ](docs/distributed/practice/�懂分布�技术:使用RocketMQ事务消�解决分布�事务.md ) * [�懂分布�技术:消�队列因何而生](docs/distributed/practice/�懂分布�技术:消�队列因何而生.md) * [�懂分布�技术:浅谈分布�消�技术Kafka](docs/distributed/practice/�懂分布�技术:浅谈分布�消�技术Kafka.md ) +* [分布�技术实践总结](docs/distributed/分布��论总结.md) -* [分布��论总结](docs/distributed/分布�技术实践总结.md) -## �试指� +# �试指� todo -### 校招指� +## 校招指� todo -### �� +## �� todo -## 工具 +# 工具 todo -## 资料 +# 资料 todo -### 书� +## 书� todo -## 待办 +# 待办 springboot和springcloud -## 微信公众� +# 微信公众� -### Java技术江湖 +## Java技术江湖 如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/Javatutorial.jpeg) diff --git "a/docs/java/jvm/JVM\346\200\273\347\273\223.md" "b/docs/Java/JVM/JVM\346\200\273\347\273\223.md" similarity index 99% rename from "docs/java/jvm/JVM\346\200\273\347\273\223.md" rename to "docs/Java/JVM/JVM\346\200\273\347\273\223.md" index 9a1cdad..60b89e6 100644 --- "a/docs/java/jvm/JVM\346\200\273\347\273\223.md" +++ "b/docs/Java/JVM/JVM\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [JVM介�和��](#jvm介�和��) * [JVM内存模型](#jvm内存模型) diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" similarity index 98% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" index 341f675..ceda305 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" @@ -1,4 +1,23 @@ # 目录 + +* [核心概念(Core Concepts)](#核心概念core-concepts) + * [Latency(延迟)](#latency延迟) + * [Throughput(���)](#throughput���) + * [Capacity(系统容�)](#capacity系统容�) +* [相关示例](#相关示例) + * [Tuning for Latency(调优延迟指标)](#tuning-for-latency调优延迟指标) + * [Tuning for Throughput(���调优)](#tuning-for-throughput���调优) + * [Tuning for Capacity(调优系统容�)](#tuning-for-capacity调优系统容�) + * [6\. GC 调优(工具篇) - GC�考手册](#6-gc-调优工具篇---gc�考手册) + * [JMX API](#jmx-api) + * [JVisualVM](#jvisualvm) + * [jstat](#jstat) + * [GC日志(GC logs)](#gc日志gc-logs) + * [GCViewer](#gcviewer) + * [分�器(Profilers)](#分�器profilers) + * [hprof](#hprof) + * [Java VisualVM](#java-visualvm) + * [AProf](#aprof) * [�考文章](#�考文章) @@ -18,8 +37,6 @@ -## 5\. GC 调优(基础篇) - GC�考手册 - > **说明**: > > **Capacity**: 性能,能力,系统容�; 文中翻译为�**系统容�**“; �为硬件�置。 diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" similarity index 99% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" index b48b79b..6c1b066 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" @@ -1,7 +1,7 @@ # 目录 * [打破�亲委派模型](#打破�亲委派模型) * [JNDI](#jndi) - * [[JNDI 的�解](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fwww.cnblogs.com%2Fzhchoutai%2Fp%2F7389089.html)](#[jndi-的�解]httpsyqaliyuncomgoarticlerenderredirecturlhttps3a2f2fwwwcnblogscom2fzhchoutai2fp2f7389089html) + * [JNDI 的�解](#[jndi-的�解]) * [OSGI](#osgi) * [1.如何正确的�解和认识OSGI技术?](#1如何正确的�解和认识osgi技术?) * [Tomcat类加载器以�应用间class隔离与共享](#tomcat类加载器以�应用间class隔离与共享) diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" similarity index 99% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" index 65e4869..8048fb5 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" @@ -14,7 +14,7 @@ * [垃圾回收中实例的终结](#垃圾回收中实例的终结) * [对象什么时候符�垃圾回收的�件?](#对象什么时候符�垃圾回收的�件?) * [GC Scope 示例程�](#gc-scope-示例程�) - * [[JVM GC算法](https://www.cnblogs.com/wupeixuan/p/8670341.html)](#[jvm-gc算法]httpswwwcnblogscomwupeixuanp8670341html) + * [JVM GC算法](#JVM GC算法) * [JVM垃圾判定算法](#jvm垃圾判定算法) * [引用计数算法(Reference Counting)](#引用计数算法reference-counting) * [�达性分�算法(根�索算法)](#�达性分�算法(根�索算法)) diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" similarity index 98% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" index 987466f..caac027 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" @@ -1,24 +1,21 @@ +# 目录 + * [JVM优化的必�性](#jvm优化的必�性) + * [JVM调优原则](#jvm调优原则) + * [JVM�行�数设置](#jvm�行�数设置) + * [JVM性能调优工具](#jvm性能调优工具) + * [常用调优策略](#常用调优策略) + * [六�JVM调优实例](#六�jvm调优实例) + * [七�一个具体的实战案例分�](#七�一个具体的实战案例分�) + * [�考资料](#�考资料) + * [�考文章](#�考文章) -![](https://pic.rmb.bdstatic.com/bjh/down/97522789c423e19931adafa65bd5424d.png) - -**目录** - -一. JVM优化的必�性 - -二. JVM调优原则 -三. JVM�行�数设置 -四. JVM性能调优工具 -五. 常用调优策略 - -六. JVM调优实例 - -七. 一个具体的实战案例分� +![](https://pic.rmb.bdstatic.com/bjh/down/97522789c423e19931adafa65bd5424d.png) -### JVM优化的必�性 +## JVM优化的必�性 **1.1: 项目上线�,什么原因使得我们需�进行jvm调优** @@ -58,7 +55,7 @@ Jvm堆内存�能设置太大,�则会导致寻�垃圾的时间过长, * 系统���与�应性能�高或下�。 -### JVM调优原则 +## JVM调优原则 ![](https://pic.rmb.bdstatic.com/bjh/down/7cd4f5c19ab4f5f5d3087422097ee931.png) @@ -254,13 +251,13 @@ TLAB空间一般�会太大,当大对象无法在TLAB分�时,则会直接 -### 四.JVM性能调优工具 +## JVM性能调优工具 这个篇幅在这里就�过多介�了,�以�照: 深入�解JVM虚拟机——Java虚拟机的监控�诊断工具大全 -### 五.常用调优策略 +## 常用调优策略 这里还是��一下,�时确定�进行JVM调优,也��陷入“知�障�,进行分�之�,�现�以通过优化程���性能,�然首选优化程�。 @@ -410,7 +407,7 @@ CPU多核,关注用户�顿时间,JDK1.8�以上,JVM�用内存6G以上 > XX:MaxDirectMemorySize -### 六�JVM调优实例 +## 六�JVM调优实例 整�的一些JVM调优实例: @@ -533,7 +530,7 @@ CPU多核,关注用户�顿时间,JDK1.8�以上,JVM�用内存6G以上 系统对外�供��账�鉴��务,使用时�现系统�常�务��用,通过 Zabbix 的监控平�监控�现系统频��生长时间 Full GC,且触�时�年代的堆内存通常并没有�满,�现原�是业务代�中调用了 -### 七�一个具体的实战案例分� +## 七�一个具体的实战案例分� **7.1 典型调优�数设置** diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" similarity index 96% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" index 0a1c3ac..58b8166 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" @@ -1,5 +1,5 @@ # 目录 - * [[java编译期优化](https://www.cnblogs.com/LeonNew/p/6187411.html)](#[java编译期优化]httpswwwcnblogscomleonnewp6187411html) + * [java编译期优化](#[java编译期优化]) * [早期(编译期)优化](#早期(编译期)优化) * [泛型与类型擦除](#泛型与类型擦除) * [自动装箱�拆箱与�历循环](#自动装箱�拆箱与�历循环) @@ -41,7 +41,7 @@ java语言的编译期其实是一段�确定的�作过程,因为它�以 2.�端编译:把字节�转�为机器� 3.����编译:直接把*.java文件编译�本地机器代� 从JDK1.3开始,虚拟机设计团队就把对性能的优化集中到了�端的�时编译中,这样�以让那些�是由Javac产生的Class文件(如JRuby�Groovy等语言的Class文件)也能享�到编译期优化所带�的好处 -**Java中�时编译在�行期的优化过程对于程��行�说更��,而�端编译期在编译期的优化过程对于程�编��说关系更加密切    ** +**Java中�时编译在�行期的优化过程对于程��行�说更��,而�端编译期在编译期的优化过程对于程�编��说关系更加密切** ### 早期(编译期)优化 @@ -102,7 +102,7 @@ public static void main(String[] args) { ##### 解释器与编译器 -Java程�最�是通过解释器进行解释执行的,当程�需�迅速�动和执行时,解释器�以首先�挥作用,�去编译时间,立�执行;当程��行�,��时间的推移,编译期���挥作用,把越�越多的代�编译�本地代�,获得更高的执行效率。**解释执行节约内存,编译执行��效率。** �时,解释器�以作为编译器激进优化时的一个“逃生门�,让编译器根�概率选择一些大多数时候都能���行速度的优化手段,当激进优化的�设��立,则通过逆优化退回到解释状�继续执行。 +Java程�最�是通过解释器进行解释执行的,当程�需�迅速�动和执行时,解释器�以首先�挥作用,�去编译时间,立�执行;当程��行�,��时间的推移,编译期���挥作用,把越�越多的代�编译�本地代�,获得更高的执行效率。**解释执行节约内存,编译执行��效率。**�时,解释器�以作为编译器激进优化时的一个“逃生门�,让编译器根�概率选择一些大多数时候都能���行速度的优化手段,当激进优化的�设��立,则通过逆优化退回到解释状�继续执行。 HotSpot虚拟机中内置了两个�时编译器,分别称为Client Compiler(C1编译器)和Server Compiler(C2编译器),默认采用解释器与其中一个编译器直接��的方�工作,使用哪个编译器�决于虚拟机�行的模�,也�以自己去指定。若强制虚拟机�行与“解释模��,编译器完全�介入工作,若强制虚拟机�行于“编译模��,则优先采用编译方�执行程�,解释器�然�在编译无法进行的情况下介入执行过程。 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222130.png) diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" similarity index 99% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" index 08cc449..cca62d4 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" @@ -32,7 +32,7 @@ ## 一�背景 -Java的内存回收�需�程�员负责,JVM会在必�时�动Java GC完�垃圾回收。Java以便我们控制对象的生存周期,�供给了我们四�引用方�,引用强度从强到弱分别为:强引用�软引用�弱引用�虚引用。  +Java的内存回收�需�程�员负责,JVM会在必�时�动Java GC完�垃圾回收。Java以便我们控制对象的生存周期,�供给了我们四�引用方�,引用强度从强到弱分别为:强引用�软引用�弱引用�虚引用。 ## 二�简介 diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" rename to "docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" diff --git "a/docs/java/basic/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" "b/docs/Java/basic/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" similarity index 100% rename from "docs/java/basic/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" rename to "docs/Java/basic/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" diff --git "a/docs/java/basic/JavaIO\346\265\201.md" "b/docs/Java/basic/JavaIO\346\265\201.md" similarity index 100% rename from "docs/java/basic/JavaIO\346\265\201.md" rename to "docs/Java/basic/JavaIO\346\265\201.md" diff --git "a/docs/java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" "b/docs/Java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" similarity index 100% rename from "docs/java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" rename to "docs/Java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" diff --git "a/docs/java/basic/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/docs/Java/basic/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" similarity index 100% rename from "docs/java/basic/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" rename to "docs/Java/basic/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" diff --git "a/docs/java/basic/Java\345\274\202\345\270\270.md" "b/docs/Java/basic/Java\345\274\202\345\270\270.md" similarity index 100% rename from "docs/java/basic/Java\345\274\202\345\270\270.md" rename to "docs/Java/basic/Java\345\274\202\345\270\270.md" diff --git "a/docs/java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/Java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" similarity index 99% rename from "docs/java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" rename to "docs/Java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" index aace19c..435e587 100644 --- "a/docs/java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" +++ "b/docs/Java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" @@ -403,8 +403,8 @@ public class TheClass { 你�以�这样�访问��的注解: ``` -Field field = ... //获�方法对象 -

Annotation[] annotations = field.getDeclaredAnnotations();
+Field field = ... //获�方法对象目录
+Annotation[] annotations = field.getDeclaredAnnotations();
 
 for(Annotation annotation : annotations){
  if(annotation instanceof MyAnnotation){
@@ -418,8 +418,8 @@ for(Annotation annotation : annotations){
 你�以�这样访问指定的��注解:
 
 ```
-Field field = ...//获�方法对象
-
+Field field = ...//获�方法对象目录
+
 Annotation annotation = field.getAnnotation(MyAnnotation.class);
 
 if(annotation instanceof MyAnnotation){
diff --git "a/docs/java/basic/Java\347\261\273\345\222\214\345\214\205.md" "b/docs/Java/basic/Java\347\261\273\345\222\214\345\214\205.md"
similarity index 100%
rename from "docs/java/basic/Java\347\261\273\345\222\214\345\214\205.md"
rename to "docs/Java/basic/Java\347\261\273\345\222\214\345\214\205.md"
diff --git "a/docs/java/basic/Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md" "b/docs/Java/basic/Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md"
similarity index 100%
rename from "docs/java/basic/Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md"
rename to "docs/Java/basic/Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md"
diff --git "a/docs/java/basic/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" "b/docs/Java/basic/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md"
similarity index 100%
rename from "docs/java/basic/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md"
rename to "docs/Java/basic/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md"
diff --git "a/docs/java/basic/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" "b/docs/Java/basic/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md"
similarity index 100%
rename from "docs/java/basic/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md"
rename to "docs/Java/basic/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md"
diff --git "a/docs/java/basic/javac\345\222\214javap.md" "b/docs/Java/basic/javac\345\222\214javap.md"
similarity index 98%
rename from "docs/java/basic/javac\345\222\214javap.md"
rename to "docs/Java/basic/javac\345\222\214javap.md"
index 5f85010..f797af7 100644
--- "a/docs/java/basic/javac\345\222\214javap.md"
+++ "b/docs/Java/basic/javac\345\222\214javap.md"
@@ -18,14 +18,7 @@
       * [-encoding](#-encoding)
       * [-verbose](#-verbose)
       * [其他命令](#其他命令)
-  * [使用javac构建项目](#使用javac构建项目)
-                    * [](#)
-* [java文件列表目录](#java文件列表目录)
-                        * [放入列表文件中](#放入列表文件中)
-                * [生�bin目录](#生�bin目录)
-                * [列表](#列表)
-    * [通过-cp指定所有的引用jar包,将src下的所有java文件进行编译](#通过-cp指定所有的引用jar包,将src下的所有java文件进行编译)
-    * [通过-cp指定所有的引用jar包,指定入�函数�行](#通过-cp指定所有的引用jar包,指定入�函数�行)
+    * [使用javac构建项目](#使用javac构建项目)
   * [javap 的使用](#javap-的使用)
   * [�考文章](#�考文章)
 
diff --git "a/docs/java/basic/string\345\222\214\345\214\205\350\243\205\347\261\273.md" "b/docs/Java/basic/string\345\222\214\345\214\205\350\243\205\347\261\273.md"
similarity index 100%
rename from "docs/java/basic/string\345\222\214\345\214\205\350\243\205\347\261\273.md"
rename to "docs/Java/basic/string\345\222\214\345\214\205\350\243\205\347\261\273.md"
diff --git "a/docs/java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" "b/docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md"
similarity index 98%
rename from "docs/java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md"
rename to "docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md"
index 2e51bc0..7ebb784 100644
--- "a/docs/java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md"
+++ "b/docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md"
@@ -38,7 +38,7 @@
 ## Java中的构造方法
 ### 构造方法简介
 
-构造方法是类的一�特殊方法,用��始化类的一个新的对象。[Java](http://c.biancheng.net/java/) 中的�个类都有一个默认的构造方法,它必须具有和类�相�的�称,而且没有返回类型。构造方法的默认返回类型就是对象类型本身,并且构造方法�能被 static�final�synchronized�abstract 和 native 修饰。
+构造方法是类的一�特殊方法,用��始化类的一个新的对象。[Java](http://c.biancheng.net/java/)中的�个类都有一个默认的构造方法,它必须具有和类�相�的�称,而且没有返回类型。构造方法的默认返回类型就是对象类型本身,并且构造方法�能被 static�final�synchronized�abstract 和 native 修饰。
 
 �示:构造方法用于�始化一个新对象,所以用 static 修饰没有�义;构造方法�能被�类继承,所以用 final 和 abstract 修饰没有�义;多个线程�会�时创建内存地�相�的�一个对象,所以用 synchronized 修饰没有必�。
 
diff --git "a/docs/java/basic/\345\217\215\345\260\204.md" "b/docs/Java/basic/\345\217\215\345\260\204.md"
similarity index 100%
rename from "docs/java/basic/\345\217\215\345\260\204.md"
rename to "docs/Java/basic/\345\217\215\345\260\204.md"
diff --git "a/docs/java/basic/\345\244\232\347\272\277\347\250\213.md" "b/docs/Java/basic/\345\244\232\347\272\277\347\250\213.md"
similarity index 98%
rename from "docs/java/basic/\345\244\232\347\272\277\347\250\213.md"
rename to "docs/Java/basic/\345\244\232\347\272\277\347\250\213.md"
index e40b5d0..35e3ada 100644
--- "a/docs/java/basic/\345\244\232\347\272\277\347\250\213.md"
+++ "b/docs/Java/basic/\345\244\232\347\272\277\347\250\213.md"
@@ -80,7 +80,7 @@ Java 给多线程编程�供了内置的支�。 一�线程指的是进程
 
 *   **新建状�:**
 
-    使用 **new** 关键字和 **Thread** 类或其�类建立一个线程对象�,该线程对象就处于新建状�。它��这个状�直到程� **start()** 这个线程。
+    使用**new**关键字和**Thread**类或其�类建立一个线程对象�,该线程对象就处于新建状�。它��这个状�直到程�**start()**这个线程。
 
 *   **就绪状�:**
 
@@ -88,7 +88,7 @@ Java 给多线程编程�供了内置的支�。 一�线程指的是进程
 
 *   **�行状�:**
 
-    如果就绪状�的线程获� CPU 资�,就�以执行 **run()**,此时线程便处于�行状�。处于�行状�的线程最为��,它�以�为阻塞状��就绪状�和死亡状�。
+    如果就绪状�的线程获� CPU 资�,就�以执行**run()**,此时线程便处于�行状�。处于�行状�的线程最为��,它�以�为阻塞状��就绪状�和死亡状�。
 
 *   **阻塞状�:**
 
diff --git "a/docs/java/basic/\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md" "b/docs/Java/basic/\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md"
similarity index 100%
rename from "docs/java/basic/\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md"
rename to "docs/Java/basic/\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md"
diff --git "a/docs/java/basic/\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md" "b/docs/Java/basic/\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md"
similarity index 100%
rename from "docs/java/basic/\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md"
rename to "docs/Java/basic/\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md"
diff --git "a/docs/java/basic/\346\236\232\344\270\276\347\261\273.md" "b/docs/Java/basic/\346\236\232\344\270\276\347\261\273.md"
similarity index 100%
rename from "docs/java/basic/\346\236\232\344\270\276\347\261\273.md"
rename to "docs/Java/basic/\346\236\232\344\270\276\347\261\273.md"
diff --git "a/docs/java/basic/\346\263\233\345\236\213.md" "b/docs/Java/basic/\346\263\233\345\236\213.md"
similarity index 100%
rename from "docs/java/basic/\346\263\233\345\236\213.md"
rename to "docs/Java/basic/\346\263\233\345\236\213.md"
diff --git "a/docs/java/basic/\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md" "b/docs/Java/basic/\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md"
similarity index 100%
rename from "docs/java/basic/\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md"
rename to "docs/Java/basic/\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md"
diff --git "a/docs/java/basic/\347\273\247\346\211\277\343\200\201\345\260\201\350\243\205\343\200\201\345\244\232\346\200\201\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/docs/Java/basic/\347\273\247\346\211\277\343\200\201\345\260\201\350\243\205\343\200\201\345\244\232\346\200\201\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
similarity index 100%
rename from "docs/java/basic/\347\273\247\346\211\277\343\200\201\345\260\201\350\243\205\343\200\201\345\244\232\346\200\201\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
rename to "docs/Java/basic/\347\273\247\346\211\277\343\200\201\345\260\201\350\243\205\343\200\201\345\244\232\346\200\201\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
diff --git "a/docs/java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md" "b/docs/Java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md"
similarity index 99%
rename from "docs/java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md"
rename to "docs/Java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md"
index 8a672ed..57e5237 100644
--- "a/docs/java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md"
+++ "b/docs/Java/basic/\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md"
@@ -3,9 +3,8 @@
   * [多线程中的“回调�](#多线程中的回调)
   * [Java回调机制实战](#java回调机制实战)
     * [实例一 : �步调用](#实例一-:-�步调用)
-      * [1.1 �步调用代�](#11-�步调用代�)
-  * [实例二:由浅入深](#实例二:由浅入深)
-  * [实例三:Tom�题](#实例三:tom�题)
+    * [实例二:由浅入深](#实例二:由浅入深)
+    * [实例三:Tom�题](#实例三:tom�题)
   * [�考文章](#�考文章)
 
 
diff --git "a/docs/java/basic/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" "b/docs/Java/basic/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md"
similarity index 100%
rename from "docs/java/basic/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md"
rename to "docs/Java/basic/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashMap\345\222\214HashTable.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashMap\345\222\214HashTable.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashMap\345\222\214HashTable.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashMap\345\222\214HashTable.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashSet\357\274\214TreeSet\344\270\216LinkedHashSet.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashSet\357\274\214TreeSet\344\270\216LinkedHashSet.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashSet\357\274\214TreeSet\344\270\216LinkedHashSet.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232HashSet\357\274\214TreeSet\344\270\216LinkedHashSet.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md"
similarity index 99%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md"
index c89b6ae..95169c6 100644
--- "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md"
+++ "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Iterator\357\274\214fail-fast\346\234\272\345\210\266\344\270\216\346\257\224\350\276\203\345\231\250.md"
@@ -6,12 +6,12 @@
       * [fail-fast示例](#fail-fast示例)
       * [fail-fast产生原因](#fail-fast产生原因)
       * [fail-fast解决办法](#fail-fast解决办法)
-* [Comparable 和 Comparator](#comparable-和-comparator)
-  * [Comparable](#comparable)
-    * [Comparator](#comparator)
-    * [Java8中使用lambda实现比较器](#java8中使用lambda实现比较器)
-  * [总结](#总结)
-  * [�考文章](#�考文章)
+  * [Comparable 和 Comparator](#comparable-和-comparator)
+    * [Comparable](#comparable)
+      * [Comparator](#comparator)
+      * [Java8中使用lambda实现比较器](#java8中使用lambda实现比较器)
+    * [总结](#总结)
+    * [�考文章](#�考文章)
 
 
 本文�考 cmsblogs.com/p=1185
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Java\351\233\206\345\220\210\347\261\273\347\273\206\350\212\202\347\262\276\350\256\262.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Java\351\233\206\345\220\210\347\261\273\347\273\206\350\212\202\347\262\276\350\256\262.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Java\351\233\206\345\220\210\347\261\273\347\273\206\350\212\202\347\262\276\350\256\262.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Java\351\233\206\345\220\210\347\261\273\347\273\206\350\212\202\347\262\276\350\256\262.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Queue\345\222\214LinkedList.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Queue\345\222\214LinkedList.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Queue\345\222\214LinkedList.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232Queue\345\222\214LinkedList.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232TreeMap\345\222\214\347\272\242\351\273\221\346\240\221.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232TreeMap\345\222\214\347\272\242\351\273\221\346\240\221.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232TreeMap\345\222\214\347\272\242\351\273\221\346\240\221.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232TreeMap\345\222\214\347\272\242\351\273\221\346\240\221.md"
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md"
similarity index 98%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md"
index e228969..ed04968 100644
--- "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md"
+++ "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\344\270\200\346\226\207\350\257\273\346\207\202ArrayList,Vector\344\270\216Stack\344\275\277\347\224\250\346\226\271\346\263\225\345\222\214\345\256\236\347\216\260\345\216\237\347\220\206.md"
@@ -13,9 +13,8 @@
     * [�始容�和扩容](#�始容�和扩容)
     * [线程安全](#线程安全-1)
   * [Stack](#stack)
-* [Stack](#stack-1)
-  * [三个集�类之间的区别](#三个集�类之间的区别)
-  * [�考文章](#�考文章)
+    * [三个集�类之间的区别](#三个集�类之间的区别)
+    * [�考文章](#�考文章)
 
 
 本文�考多篇优质技术�客,�考文章请在文末查看
@@ -464,11 +463,11 @@ vector大部分方法都使用了synchronized修饰符,所以他是线层安
 
 # Stack
 
-    如果我们去查jdk的文档,我们会�现stack是在java.util这个包里。它对应的一个大致的类关系图如下:
+  如果我们去查jdk的文档,我们会�现stack是在java.util这个包里。它对应的一个大致的类关系图如下:
 
 ![](http://dl.iteye.com/upload/attachment/0081/2496/006da63f-388e-3669-b57f-1cdd1909d5f8.jpg)
 
-    通过继承Vector类,Stack类�以很容易的实现他本身的功能。因为大部分的功能在Vector里�已��供支�了。
+  通过继承Vector类,Stack类�以很容易的实现他本身的功能。因为大部分的功能在Vector里�已��供支�了。
 在Java中Stack类表示�进先出(LIFO)的对象堆栈。栈是一��常常�的数�结构,它采用典型的先进�出的�作方�完�的。
 
 
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243LinkedHashMap\345\222\214LRU\347\274\223\345\255\230.md" "b/docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243LinkedHashMap\345\222\214LRU\347\274\223\345\255\230.md"
similarity index 100%
rename from "docs/java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243LinkedHashMap\345\222\214LRU\347\274\223\345\255\230.md"
rename to "docs/Java/collection/Java\351\233\206\345\220\210\350\257\246\350\247\243\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243LinkedHashMap\345\222\214LRU\347\274\223\345\255\230.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md"
similarity index 99%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md"
index 7cacaae..eb10601 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md"
@@ -8,8 +8,8 @@
     * [5\. 唤醒�检查中断状�](#5-唤醒�检查中断状�)
     * [6\. 获�独��](#6-获�独��)
     * [7\. 处�中断状�](#7-处�中断状�)
-    * [* 带超时机制的 await](#-带超时机制的-await)
-    * [* �抛出 InterruptedException 的 await](#-�抛出-interruptedexception-的-await)
+    * [带超时机制的 await](#-带超时机制的-await)
+    * [�抛出 InterruptedException 的 await](#-�抛出-interruptedexception-的-await)
   * [AbstractQueuedSynchronizer 独��的�消排队](#abstractqueuedsynchronizer-独��的�消排队)
   * [�说 java 线程中断和 InterruptedException 异常](#�说-java-线程中断和-interruptedexception-异常)
     * [线程中断](#线程中断)
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md"
similarity index 92%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md"
index ca072a2..74fa6a0 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md"
@@ -27,7 +27,7 @@ Fork/Join框架是Java7�供的一个用于并行执行任务的框架, 是
 
 ## 简介
 
-通常,使用Java�开�一个简�的并�应用程�时,会创建一些**Runnable**对象,然�创建对应的**Thread** 对象�控制程�中这些线程的创建�执行以�线程的状�。自从Java 5开始引入了**Executor**和**ExecutorService**接�以�实现这两个接�的类(比如**ThreadPoolExecutor**)之�,使得Java在并�支�上得到了进一步的��。
+通常,使用Java�开�一个简�的并�应用程�时,会创建一些**Runnable**对象,然�创建对应的**Thread**对象�控制程�中这些线程的创建�执行以�线程的状�。自从Java 5开始引入了**Executor**和**ExecutorService**接�以�实现这两个接�的类(比如**ThreadPoolExecutor**)之�,使得Java在并�支�上得到了进一步的��。
 
 **执行器框架(Executor Framework)**将任务的创建和执行进行了分离,通过这个框架,�需�实现**Runnable**接�的对象和使用**Executor**对象,然�将**Runnable**对象��给执行器。执行器�负责�行这些任务所需�的线程,包括线程的创建,线程的管�以�线程的结�。
 
@@ -37,7 +37,7 @@ Fork/Join框架是Java7�供的一个用于并行执行任务的框架, 是
 
 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404203219.png)
 
-没有固定的公��决定问题的**�考大�(Reference Size)**,从而决定一个任务是需�进行拆分或�需�拆分,拆分与��是�赖于任务本身的特性。�以使用在任务中将�处�的元素的数目和任务执行所需�的时间�决定�考大�。测试��的�考大��决定解决问题最好的一个方案,将**ForkJoinPool**类看作一个特殊的 **Executor** 执行器类型。这个框架基于以下两��作。
+没有固定的公��决定问题的**�考大�(Reference Size)**,从而决定一个任务是需�进行拆分或�需�拆分,拆分与��是�赖于任务本身的特性。�以使用在任务中将�处�的元素的数目和任务执行所需�的时间�决定�考大�。测试��的�考大��决定解决问题最好的一个方案,将**ForkJoinPool**类看作一个特殊的**Executor**执行器类型。这个框架基于以下两��作。
 
 *   **分解(Fork)**�作:当需�将一个任务拆分�更�的多个任务时,在框架中执行这些任务;
 *   **�并(Join)**�作:当一个主任务等待其创建的多个�任务的完�执行。
@@ -46,7 +46,7 @@ Fork/Join框架是Java7�供的一个用于并行执行任务的框架, 是
 
 为了达到这个目标,通过**Fork/Join框架**执行的任务有以下�制。
 
-*   任务�能使用**fork()**和**join()** �作当作�步机制。如果使用其他的�步机制,工作者线程就�能执行其他任务,当然这些任务是在�步�作里时。比如,如果在**Fork/Join 框架**中将一个任务休眠,正在执行这个任务的工作者线程在休眠期内�能执行�一个任务。
+*   任务�能使用**fork()**和**join()**�作当作�步机制。如果使用其他的�步机制,工作者线程就�能执行其他任务,当然这些任务是在�步�作里时。比如,如果在**Fork/Join 框架**中将一个任务休眠,正在执行这个任务的工作者线程在休眠期内�能执行�一个任务。
 *   任务�能执行I/O�作,比如文件数�的读�与写入。
 *   任务�能抛出��行时异常(Checked Exception),必须在代�中处�掉这些异常。
 
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md"
similarity index 96%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md"
index 2d825d4..c9a13f8 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md"
@@ -1,3 +1,12 @@
+# 目录
+
+  * [一�properly constructed / this对象逸出](#一�properly-constructed--this对象逸出)
+  * [二�对象的安全�布](#二�对象的安全�布)
+  * [三� final 关键字的内存语义](#三�-final-关键字的内存语义)
+  * [四�HotSpot VM中对final内存语义的实现](#四�hotspot-vm中对final内存语义的实现)
+* [�考文献](#�考文献)
+
+
 **本文转载自互�网,侵删**
 
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
@@ -215,4 +224,4 @@ JSR 133 (Java Memory Model) FAQ
 Java Concurrency in Practice
 
 The JSR-133 Cookbook for Compiler Writers
-Intel® 64 and IA-32 ArchitecturesvSoftware Developer’s Manual Volume 3A: System Programming Guide, Part 1
\ No newline at end of file
+Intel® 64 and IA-32 ArchitecturesvSoftware Developer’s Manual Volume 3A: System Programming Guide, Part 1
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md"
similarity index 99%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md"
index 6314e77..dff6cc3 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md"
@@ -345,7 +345,7 @@ public V put(K key, V value) {
 
 第一层皮很简�,根� hash 值很快就能找到相应的 Segment,之�就是 Segment 内部的 put �作了。
 
-Segment 内部是由 **数组+链表** 组�的。
+Segment 内部是由**数组+链表**组�的。
 
 ```
 final V put(K key, int hash, V value, boolean onlyIfAbsent) {
@@ -640,11 +640,11 @@ public V get(Object key) {
 
 ## Java8 HashMap
 
-Java8 对 HashMap 进行了一些修改,最大的��就是利用了红黑树,所以其由 **数组+链表+红黑树** 组�。
+Java8 对 HashMap 进行了一些修改,最大的��就是利用了红黑树,所以其由**数组+链表+红黑树**组�。
 
-根� Java7 HashMap 的介�,我们知�,查找的时候,根� hash 值我们能够快速定�到数组的具体下标,但是之�的�,需�顺�链表一个个比较下去�能找到我们需�的,时间��度�决于链表的长度,为 **O(n)**。
+根� Java7 HashMap 的介�,我们知�,查找的时候,根� hash 值我们能够快速定�到数组的具体下标,但是之�的�,需�顺�链表一个个比较下去�能找到我们需�的,时间��度�决于链表的长度,为**O(n)**。
 
-为了�低这部分的开销,在 Java8 中,当链表中的元素达到了 8 个时,会将链表转�为红黑树,在这些�置进行查找的时候�以�低时间��度为 **O(logN)**。
+为了�低这部分的开销,在 Java8 中,当链表中的元素达到了 8 个时,会将链表转�为红黑树,在这些�置进行查找的时候�以�低时间��度为**O(logN)**。
 
 �一张图简�示�一下�:
 
@@ -654,7 +654,7 @@ Java8 对 HashMap 进行了一些修改,最大的��就是利用了红黑
 
 下�,我们还是用代��介��,个人感觉,Java8 的���读性�差一些,�过精简一些。
 
-Java7 中使用 Entry �代表�个 HashMap 中的数�节点,Java8 中使用 **Node**,基本没有区别,都是 key,value,hash 和 next 这四个属性,�过,Node �能用于链表的情况,红黑树的情况需�使用 **TreeNode**。
+Java7 中使用 Entry �代表�个 HashMap 中的数�节点,Java8 中使用**Node**,基本没有区别,都是 key,value,hash 和 next 这四个属性,�过,Node �能用于链表的情况,红黑树的情况需�使用**TreeNode**。
 
 我们根�数组元素中,第一个节点数�类型是 Node 还是 TreeNode �判断该�置下是链表还是红黑树的。
 
@@ -1145,7 +1145,7 @@ private final void tryPresize(int size) {
 
 这个方法的核心在于 sizeCtl 值的�作,首先将其设置为一个负数,然�执行 transfer(tab, null),�下一个循环将 sizeCtl 加 1,并执行 transfer(tab, nt),之��能是继续 sizeCtl 加 1,并执行 transfer(tab, nt)。
 
-所以,�能的�作就是执行 **1 次 transfer(tab, null) + 多次 transfer(tab, nt)**,这里怎么结�循环的需�看完 transfer ���清楚。
+所以,�能的�作就是执行**1 次 transfer(tab, null) + 多次 transfer(tab, nt)**,这里怎么结�循环的需�看完 transfer ���清楚。
 
 #### 数��移:transfer
 
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md"
similarity index 95%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md"
index 50c20d6..a357d88 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md"
@@ -91,7 +91,7 @@ ReentrantReadWriteLock 分为读�和写�两个实例,读�是共享�,
 
 很清楚了,ReadLock 和 WriteLock 中的方法都是通过 Sync 这个类�实现的。Sync 是 AQS 的�类,然��派生了公平模�和�公平模�。
 
-从它们调用的 Sync 方法,我们�以看到: **ReadLock 使用了共享模�,WriteLock 使用了独�模�**。
+从它们调用的 Sync 方法,我们�以看到:**ReadLock 使用了共享模�,WriteLock 使用了独�模�**。
 
 等等,**�一个 AQS 实例怎么�以�时使用共享模�和独�模�**???
 
@@ -99,7 +99,7 @@ ReentrantReadWriteLock 分为读�和写�两个实例,读�是共享�,
 
 ![13](https://www.javadoop.com/blogimages/reentrant-read-write-lock/13.png)
 
-AQS 的精髓在于内部的属性 **state**:
+AQS 的精髓在于内部的属性**state**:
 
 1.  对于独�模��说,通常就是 0 代表�获��,1 代表�被别人获�了,�入例外
 2.  而共享模�下,�个线程都�以对 state 进行加��作
@@ -168,9 +168,9 @@ abstract static class Sync extends AbstractQueuedSynchronizer {
 
 1.  state 的高 16 �代表读�的获�次数,包括�入次数,获�到读�一次加 1,释放掉读�一次� 1
 2.  state 的低 16 �代表写�的获�次数,因为写�是独��,�时�能被一个线程获得,所以它代表�入次数
-3.  �个线程都需�维护自己的 HoldCounter,记录该线程获�的读�次数,这样�能知�到底是�是读��入,用 ThreadLocal 属性 **readHolds** 维护
-4.  **cachedHoldCounter** 有什么用?其实没什么用,但能�示性能。将最�一次获�读�的线程的 HoldCounter 缓存到这里,这样比使用 ThreadLocal 性能�好一些,因为 ThreadLocal 内部是基于 map �查询的。但是 cachedHoldCounter 这一个属性毕竟�能缓存一个线程,所以它�起��性能作用的��就是:通常读�的获�紧��就是该读�的释放。我这里�能表达�太好,但是大家应该是懂的�。
-5.  **firstReader** 和 **firstReaderHoldCount** 有什么用?其实也没什么用,但是它也能�示性能。将"第一个"获�读�的线程记录在 firstReader 属性中,这里的**第一个**�是全局的概念,等这个 firstReader 当�代表的线程释放掉读�以�,会有��的线程�用这个属性的。**firstReader 和 firstReaderHoldCount 使得在读��产生竞争的情况下,记录读��入次数�常方便快速**
+3.  �个线程都需�维护自己的 HoldCounter,记录该线程获�的读�次数,这样�能知�到底是�是读��入,用 ThreadLocal 属性**readHolds**维护
+4.  **cachedHoldCounter**有什么用?其实没什么用,但能�示性能。将最�一次获�读�的线程的 HoldCounter 缓存到这里,这样比使用 ThreadLocal 性能�好一些,因为 ThreadLocal 内部是基于 map �查询的。但是 cachedHoldCounter 这一个属性毕竟�能缓存一个线程,所以它�起��性能作用的��就是:通常读�的获�紧��就是该读�的释放。我这里�能表达�太好,但是大家应该是懂的�。
+5.  **firstReader**和**firstReaderHoldCount**有什么用?其实也没什么用,但是它也能�示性能。将"第一个"获�读�的线程记录在 firstReader 属性中,这里的**第一个**�是全局的概念,等这个 firstReader 当�代表的线程释放掉读�以�,会有��的线程�用这个属性的。**firstReader 和 firstReaderHoldCount 使得在读��产生竞争的情况下,记录读��入次数�常方便快速**
 6.  如果一个线程使用了 firstReader,那么它就�需��用 cachedHoldCounter
 7.  个人认为,读写���中最让�学者头疼的就是这几个用于��性能的属性了,使得大家看得云里雾里的。主�是因为 ThreadLocal 内部是通过一个 ThreadLocalMap ��作的,会增加检索时间。而很多场景下,执行 unlock 的线程往往就是刚刚最�一次执行 lock 的线程,中间�能没有其他线程进行 lock。还有就是很多�怎么会�生读�竞争的场景。
 
@@ -498,7 +498,7 @@ protected final boolean tryAcquire(int acquires) {
 }
 ```
 
-下�看一眼 **writerShouldBlock()** 的判定,然�你�回去看一篇写�获�过程。
+下�看一眼**writerShouldBlock()**的判定,然�你�回去看一篇写�获�过程。
 
 ```
 static final class NonfairSync extends Sync {
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md"
similarity index 99%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md"
index 0abdbe8..60a9c3e 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md"
@@ -9,7 +9,6 @@
     * [volatile写-读的内存语义](#volatile写-读的内存语义)
     * [volatile内存语义的实现](#volatile内存语义的实现)
     * [JSR-133为什么�增强volatile的内存语义](#jsr-133为什么�增强volatile的内存语义)
-                * [](#)
     * [引言](#引言)
     * [术语定义](#术语定义)
     * [3 处�器如何实现原��作](#3-处�器如何实现原��作)
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md"
similarity index 98%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md"
index 0856914..a9aad6e 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md"
@@ -176,7 +176,7 @@ Java 语言在�循内存模型的基础上推出了 JMM 规范,目的是解
 
 我们知�,Java程�是需��行在Java虚拟机上�的,**Java内存模型(Java Memory Model ,JMM)就是一�符�内存模型规范的,�蔽了��硬件和�作系统的访问差异的,��了Java程�在��平�下对内存的访问都能��效果一致的机制�规范。**
 
-�到Java内存模型,一般指的是JDK 5 开始使用的新的内存模型,主�由[JSR-133: JavaTM Memory Model and Thread Specification](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf) �述。感兴趣的�以�看下这份PDF文档([http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf))
+�到Java内存模型,一般指的是JDK 5 开始使用的新的内存模型,主�由[JSR-133: JavaTM Memory Model and Thread Specification](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf)�述。感兴趣的�以�看下这份PDF文档([http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf))
 
 Java内存模型规定了所有的��都存储在主内存中,��线程还有自己的工作内存,线程的工作内存中�存了该线程中是用到的��的主内存副本拷�,线程对��的所有�作都必须在工作内存中进行,而�能直接读写主内存。��的线程之间也无法直接访问对方工作内存中的��,线程间��的传递�需�自己的工作内存和主存之间进行数��步进行。
 
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md"
similarity index 98%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md"
index b318132..aa025c9 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md"
@@ -92,7 +92,7 @@ public interface Executor {
 }
 ```
 
-我们�以看到 Executor 接��常简�,就一个 `void execute(Runnable command)` 方法,代表�交一个任务。为了让大家�解 java 线程池的整个设计方案,我会按照 Doug Lea 的设计�路�多说一些相关的东西。
+我们�以看到 Executor 接��常简�,就一个`void execute(Runnable command)`方法,代表�交一个任务。为了让大家�解 java 线程池的整个设计方案,我会按照 Doug Lea 的设计�路�多说一些相关的东西。
 
 我们�常这样�动一个线程:
 
@@ -171,7 +171,7 @@ class SerialExecutor implements Executor {
 }
 ```
 
-当然了,Executor 这个接��有�交任务的功能,太简�了,我们想�更丰富的功能,比如我们想知�执行结果�我们想知�当�线程池有多少个线程活��已�完�了多少任务等等,这些都是这个接�的�足的地方。接下�我们�介�的是继承自 `Executor` 接�的 `ExecutorService` 接�,这个接��供了比较丰富的功能,也是我们最常使用到的接�。
+当然了,Executor 这个接��有�交任务的功能,太简�了,我们想�更丰富的功能,比如我们想知�执行结果�我们想知�当�线程池有多少个线程活��已�完�了多少任务等等,这些都是这个接�的�足的地方。接下�我们�介�的是继承自`Executor`接�的`ExecutorService`接�,这个接��供了比较丰富的功能,也是我们最常使用到的接�。
 
 ## ExecutorService
 
@@ -277,13 +277,13 @@ public interface Callable {
 
 在这里,就�展开说 FutureTask 类了,因为本文篇幅本�就够大了,这里我们需�知�怎么用就行了。
 
-下�,我们�看看 `ExecutorService` 的抽象实现 `AbstractExecutorService` 。
+下�,我们�看看`ExecutorService`的抽象实现`AbstractExecutorService`。
 
 ## AbstractExecutorService
 
 AbstractExecutorService 抽象类派生自 ExecutorService 接�,然�在其基础上实现了几个实用的方法,这些方法�供给�类进行调用。
 
-这个抽象类实现了 invokeAny 方法和 invokeAll 方法,这里的两个 newTaskFor 方法也比较有用,用于将任务包装� FutureTask。定义于最上层接� Executor中的 `void execute(Runnable command)` 由于�需�获�结果,�会进行 FutureTask 的包装。
+这个抽象类实现了 invokeAny 方法和 invokeAll 方法,这里的两个 newTaskFor 方法也比较有用,用于将任务包装� FutureTask。定义于最上层接� Executor中的`void execute(Runnable command)`由于�需�获�结果,�会进行 FutureTask 的包装。
 
 > 需�获�结果(FutureTask),用 submit 方法,�需�获�结果,�以用 execute 方法。
 
@@ -600,7 +600,7 @@ public  Future submit(Callable task) {
 
 当然,上图没有考虑队列是�有界,�交任务时队列满了怎么办?什么情况下会创建新的线程?�交任务时线程池满了怎么办?空闲线程怎么关掉?这些问题下�我们会一一解决。
 
-我们�常会使用 `Executors` 这个工具类�快速构造一个线程池,对于�学者而言,这�工具类是很有用的,开�者�需�关注太多的细节,��知�自己需�一个线程池,仅仅�供必需的�数就�以了,其他�数都采用作者�供的默认值。
+我们�常会使用`Executors`这个工具类�快速构造一个线程池,对于�学者而言,这�工具类是很有用的,开�者�需�关注太多的细节,��知�自己需�一个线程池,仅仅�供必需的�数就�以了,其他�数都采用作者�供的默认值。
 
 ```
 public static ExecutorService newFixedThreadPool(int nThreads) {
@@ -659,7 +659,7 @@ public static ExecutorService newCachedThreadPool() {
 
 *   keepAliveTime
 
-    > 空闲线程的�活时间,如果�线程的空闲时间超过这个值都没有任务给它�,那么�以被关闭了。注�这个值并�会对所有线程起作用,如果线程池中的线程数少于等于核心线程数 corePoolSize,那么这些线程�会因为空闲太长时间而被关闭,当然,也�以通过调用 `allowCoreThreadTimeOut(true)`使核心线程数内的线程也�以被回收。
+    > 空闲线程的�活时间,如果�线程的空闲时间超过这个值都没有任务给它�,那么�以被关闭了。注�这个值并�会对所有线程起作用,如果线程池中的线程数少于等于核心线程数 corePoolSize,那么这些线程�会因为空闲太长时间而被关闭,当然,也�以通过调用`allowCoreThreadTimeOut(true)`使核心线程数内的线程也�以被回收。
 
 *   threadFactory
 
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md"
similarity index 100%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\227\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md"
diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/Java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md"
similarity index 99%
rename from "docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md"
rename to "docs/Java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md"
index f5ab376..98f3e38 100644
--- "a/docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md"
+++ "b/docs/Java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md"
@@ -9,7 +9,6 @@
   * [Lockç±»](#lockç±»)
     * [AQS](#aqs)
     * [�Lock和Conditon](#�lock和conditon)
-          * [](#)
   * [并�工具类](#并�工具类)
   * [原�数�类型](#原�数�类型)
   * [�步容器](#�步容器)
diff --git "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md"
similarity index 76%
rename from "docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md"
rename to "docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md"
index dc643c3..ab7b901 100644
--- "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232JDK\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md"
@@ -1,3 +1,34 @@
+# 目录
+
+  * [**一,结构型模�**](#一,结构型模�)
+    * [**1,适�器模�**](#1,适�器模�)
+    * [**2,桥接模�**](#2,桥接模�)
+    * [**3,组�模�**](#3,组�模�)
+    * [**4,装饰者模�**](#4,装饰者模�)
+    * [**5,门�模�**](#5,门�模�)
+    * [**6,享元模�**](#6,享元模�)
+    * [**7,代�模�**](#7,代�模�)
+  * [**二,创建模�**](#二,创建模�)
+    * [**1,抽象工厂模�**](#1,抽象工厂模�)
+    * [**2,建造模�(Builder)**](#2,建造模�builder)
+    * [**3,工厂方法**](#3,工厂方法)
+    * [**4,原型模�**](#4,原型模�)
+    * [**5,�例模�**](#5,�例模�)
+  * [**三,行为模�**](#三,行为模�)
+    * [**1,责任链模�**](#1,责任链模�)
+    * [**2,命令模�**](#2,命令模�)
+    * [**3,解释器模�**](#3,解释器模�)
+    * [**4,迭代器模�**](#4,迭代器模�)
+    * [**5,中介者模�**](#5,中介者模�)
+    * [**6,备忘录模�**](#6,备忘录模�)
+    * [**7,空对象模�**](#7,空对象模�)
+    * [**8,观察者模�**](#8,观察者模�)
+    * [**9,状�模�**](#9,状�模�)
+    * [**10,策略模�**](#10,策略模�)
+    * [**11,模�方法模�**](#11,模�方法模�)
+    * [**12,访问者模�**](#12,访问者模�)
+
+
 
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
 
@@ -12,9 +43,9 @@
 本文转自https://www.cnblogs.com/LinkinPark/p/5233075.html
 
 
-**一,结构型模�**
+## **一,结构型模�**
 
-**1,适�器模�**
+### **1,适�器模�**
 
 **用�把一个接�转化��一个接�**
 
@@ -30,7 +61,7 @@
 >
 > **javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()**
 
-**2,桥接模�**
+### **2,桥接模�**
 
 **这个模�将抽象和抽象�作的实现进行了解耦,这样使得抽象和实现�以独立地�化**
 
@@ -38,7 +69,7 @@
 >
 > **JDBC**
 
-**3,组�模�**
+### **3,组�模�**
 
 **使得客户端看��个对象和对象的组�是�等的。���说,�个类型的方法�时也接�自身类型作为�数**
 
@@ -52,7 +83,7 @@
 >
 > **java.util.Set#addAll(Collection)**
 
-**4,装饰者模�**
+### **4,装饰者模�**
 
 **动�的给一个对象附加�外的功能,这也是�类的一�替代方�。�以看到,在创建一个类型的时候,�时也传入�一类型的对象。这在JDK里�处��,你会�现它无处�在,所以下�这个列表�是一�部分**
 
@@ -64,9 +95,9 @@
 >
 > **java.util.zip.ZipOutputStream(OutputStream)**
 >
-> **java.util.Collections#checked**
+> **java.util.Collections#checked**
 
-**5,门�模�**
+### **5,门�模�**
 
 **给一组组件,接�,抽象,或者�系统�供一个简�的接�**
 
@@ -74,7 +105,7 @@
 >
 > **javax.faces.webapp.FacesServlet**
 
-**6,享元模�**
+### **6,享元模�**
 
 **使用缓存�加速大��对象的访问时间**
 
@@ -86,7 +117,7 @@
 >
 > **java.lang.Character#valueOf(char)**
 
-**7,代�模�**
+### **7,代�模�**
 
 **代�模�是用一个简�的对象�代替一个��的或者创建耗时的对象。**
 
@@ -94,11 +125,11 @@
 >
 > **RMI**
 
-** **
+****
 
-**二,创建模�**
+## **二,创建模�**
 
-**1,抽象工厂模�**
+### **1,抽象工厂模�**
 
 **抽象工厂模��供了一个�议�生�一系列的相关或者独立的对象,而�用指定具体对象的类型。它使得应用程�能够和使用的框架的具体实现进行解耦。这在JDK或者许多框架比如Spring中都�处��。它们也很容易识别,一个创建新对象的方法,返回的�是接�或者抽象类的,就是抽象工厂模�了**
 
@@ -118,7 +149,7 @@
 >
 > **javax.xml.transform.TransformerFactory#newInstance()**
 
-**2,建造模�(Builder)**
+### **2,建造模�(Builder)**
 
 **定义了一个新的类�构建�一个类的实例,以简化��对象的创建。建造模�通常也使用方法链接�实现**
 
@@ -130,7 +161,7 @@
 >
 > **javax.swing.GroupLayout.Group#addComponent()**
 
-**3,工厂方法**
+### **3,工厂方法**
 
 **就是一个返回具体对象的方法**
 
@@ -148,7 +179,7 @@
 >
 > **java.lang.Class#forName()**
 
-**4,原型模�**
+### **4,原型模�**
 
 **使得类的实例能够生�自身的拷�。如果创建一个对象的实例�常��且耗时时,就�以使用这�模�,而��新创建一个新的实例,你�以拷�一个对象并直接修改它**
 
@@ -156,7 +187,7 @@
 >
 > **java.lang.Cloneable**
 
-**5,�例模�**
+### **5,�例模�**
 
 **用�确�类�有一个实例。Joshua Bloch在Effetive Java中建议到,还有一�方法就是使用枚举**
 
@@ -168,11 +199,11 @@
 >
 > **java.awt.Desktop#getDesktop()**
 
-** **
+****
 
-**三,行为模�**
+## **三,行为模�**
 
-**1,责任链模�**
+### **1,责任链模�**
 
 **通过把请求从一个对象传递到链�中下一个对象的方�,直到请求被处�完毕,以实现对象间的解耦**
 
@@ -180,7 +211,7 @@
 >
 > **javax.servlet.Filter#doFilter()**
 
-**2,命令模�**
+### **2,命令模�**
 
 **将�作�装到对象内,以便存储,传递和返回**
 
@@ -188,7 +219,7 @@
 >
 > **javax.swing.Action**
 
-**3,解释器模�**
+### **3,解释器模�**
 
 **这个模�通常定义了一个语言的语法,然�解�相应语法的语�**
 
@@ -198,7 +229,7 @@
 >
 > **java.text.Format**
 
-**4,迭代器模�**
+### **4,迭代器模�**
 
 **�供一个一致的方法�顺�访问集�中的对象,这个方法与底层的集�的具体实现无关**
 
@@ -206,7 +237,7 @@
 >
 > **java.util.Enumeration**
 
-**5,中介者模�**
+### **5,中介者模�**
 
 **通过使用一个中间对象�进行消�分�以��少类之间的直接�赖**
 
@@ -218,7 +249,7 @@
 >
 > **java.lang.reflect.Method#invoke()**
 
-**6,备忘录模�**
+### **6,备忘录模�**
 
 **生�对象状�的一个快照,以便对象�以��原始状�而�用暴露自身的内容。Date对象通过自身内部的一个long值�实现备忘录模�**
 
@@ -226,7 +257,7 @@
 >
 > **java.io.Serializable**
 
-**7,空对象模�**
+### **7,空对象模�**
 
 **这个模�通过一个无�义的对象�代替没有对象这个状�。它使得你�用�外对空对象进行处�**
 
@@ -236,7 +267,7 @@
 >
 > **java.util.Collections#emptySet()**
 
-**8,观察者模�**
+### **8,观察者模�**
 
 **它使得一个对象�以�活的将消���给感兴趣的对象**
 
@@ -248,7 +279,7 @@
 >
 > **javax.faces.event.PhaseListener**
 
-**9,状�模�**
+### **9,状�模�**
 
 **通过改�对象内部的状�,使得你�以在�行时动�改�一个对象的行为**
 
@@ -256,7 +287,7 @@
 >
 > **javax.faces.lifecycle.LifeCycle#execute()**
 
-**10,策略模�**
+### **10,策略模�**
 
 **使用这个模��将一组算法�装�一系列对象。通过传递这些对象�以�活的改�程�的功能**
 
@@ -266,7 +297,7 @@
 >
 > **javax.servlet.Filter#doFilter()**
 
-**11,模�方法模�**
+### **11,模�方法模�**
 
 **让�类�以�写方法的一部分,而�是整个�写,你�以控制�类需��写那些�作**
 
@@ -278,7 +309,7 @@
 >
 > **java.util.AbstractList#indexOf()**
 
-**12,访问者模�**
+### **12,访问者模�**
 
 **�供一个方便的�维护的方���作一组对象。它使得你在�改��作的对象��下,�以修改或者扩展对象的行为**
 
diff --git "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232Spring\346\266\211\345\217\212\345\210\260\347\232\204\347\247\215\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232Spring\346\266\211\345\217\212\345\210\260\347\232\204\347\247\215\350\256\276\350\256\241\346\250\241\345\274\217.md"
similarity index 100%
rename from "docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232Spring\346\266\211\345\217\212\345\210\260\347\232\204\347\247\215\350\256\276\350\256\241\346\250\241\345\274\217.md"
rename to "docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232Spring\346\266\211\345\217\212\345\210\260\347\232\204\347\247\215\350\256\276\350\256\241\346\250\241\345\274\217.md"
diff --git "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\345\210\233\345\273\272\345\236\213\346\250\241\345\274\217\357\274\210\345\267\245\345\216\202\357\274\214\345\215\225\344\276\213\347\255\211\357\274\211.md" "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\345\210\233\345\273\272\345\236\213\346\250\241\345\274\217\357\274\210\345\267\245\345\216\202\357\274\214\345\215\225\344\276\213\347\255\211\357\274\211.md"
similarity index 100%
rename from "docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\345\210\233\345\273\272\345\236\213\346\250\241\345\274\217\357\274\210\345\267\245\345\216\202\357\274\214\345\215\225\344\276\213\347\255\211\357\274\211.md"
rename to "docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\345\210\233\345\273\272\345\236\213\346\250\241\345\274\217\357\274\210\345\267\245\345\216\202\357\274\214\345\215\225\344\276\213\347\255\211\357\274\211.md"
diff --git "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\347\273\223\346\236\204\345\236\213\346\250\241\345\274\217\357\274\210\344\273\243\347\220\206\346\250\241\345\274\217\357\274\214\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\347\255\211\357\274\211.md" "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\347\273\223\346\236\204\345\236\213\346\250\241\345\274\217\357\274\210\344\273\243\347\220\206\346\250\241\345\274\217\357\274\214\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\347\255\211\357\274\211.md"
similarity index 100%
rename from "docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\347\273\223\346\236\204\345\236\213\346\250\241\345\274\217\357\274\210\344\273\243\347\220\206\346\250\241\345\274\217\357\274\214\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\347\255\211\357\274\211.md"
rename to "docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\347\273\223\346\236\204\345\236\213\346\250\241\345\274\217\357\274\210\344\273\243\347\220\206\346\250\241\345\274\217\357\274\214\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\347\255\211\357\274\211.md"
diff --git "a/docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\350\241\214\344\270\272\345\236\213\346\250\241\345\274\217\357\274\210\347\255\226\347\225\245\357\274\214\350\247\202\345\257\237\350\200\205\347\255\211\357\274\211.md" "b/docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\350\241\214\344\270\272\345\236\213\346\250\241\345\274\217\357\274\210\347\255\226\347\225\245\357\274\214\350\247\202\345\257\237\350\200\205\347\255\211\357\274\211.md"
similarity index 100%
rename from "docs/java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\350\241\214\344\270\272\345\236\213\346\250\241\345\274\217\357\274\210\347\255\226\347\225\245\357\274\214\350\247\202\345\257\237\350\200\205\347\255\211\357\274\211.md"
rename to "docs/Java/design-parttern/\345\210\235\346\216\242Java\350\256\276\350\256\241\346\250\241\345\274\217\357\274\232\350\241\214\344\270\272\345\236\213\346\250\241\345\274\217\357\274\210\347\255\226\347\225\245\357\274\214\350\247\202\345\257\237\350\200\205\347\255\211\357\274\211.md"
diff --git "a/docs/java/design-parttern/\350\256\276\350\256\241\346\250\241\345\274\217\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/Java/design-parttern/\350\256\276\350\256\241\346\250\241\345\274\217\345\255\246\344\271\240\346\200\273\347\273\223.md"
similarity index 100%
rename from "docs/java/design-parttern/\350\256\276\350\256\241\346\250\241\345\274\217\345\255\246\344\271\240\346\200\273\347\273\223.md"
rename to "docs/Java/design-parttern/\350\256\276\350\256\241\346\250\241\345\274\217\345\255\246\344\271\240\346\200\273\347\273\223.md"
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md" "b/docs/Java/network/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md"
similarity index 97%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md"
index 634e1c6..15c003a 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\344\270\216NIO\346\200\273\347\273\223.md"
@@ -1,3 +1,17 @@
+# 目录
+
+* [目录](#目录)
+  * [Java IO](#java-io)
+  * [Socket编程](#socket编程)
+  * [客户端,�务端的线程模型](#客户端,�务端的线程模型)
+  * [IO模型](#io模型)
+  * [NIO](#nio)
+  * [AIO](#aio)
+  * [Tomcat中的NIO模型](#tomcat中的nio模型)
+  * [Tomcat的container](#tomcat的container)
+  * [netty](#netty)
+
+
 # 目录
   * [Java IO](#java-io)
   * [Socket编程](#socket编程)
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md"
similarity index 93%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md"
index 8bdf054..deca42f 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md"
@@ -1,3 +1,21 @@
+# 目录
+
+* [目录](#目录)
+  * [IO模型介�](#io模型介�)
+    * [阻塞 I/O(blocking IO)](#阻塞-io(blocking-io))
+    * [�阻塞 I/O(nonblocking IO)](#�阻塞-io(nonblocking-io))
+    * [I/O 多路�用( IO multiplexing)](#io-多路�用(-io-multiplexing))
+    * [异步 I/O(asynchronous IO)](#异步-io(asynchronous-io))
+  * [阻塞IO,�阻塞IO 与 �步IO, 异步IO的区别和�系](#阻塞io�阻塞io-与-�步io-异步io的区别和�系)
+    * [IO模型的形象举例](#io模型的形象举例)
+    * [Select/Poll/Epoll 轮询机制](#selectpollepoll-轮询机制)
+  * [Java网络编程模型](#java网络编程模型)
+    * [BIO](#bio)
+    * [NIO](#nio)
+    * [AIO](#aio)
+    * [对比](#对比)
+
+
 # 目录
   * [IO模型介�](#io模型介�)
     * [阻塞 I/O(blocking IO)](#阻塞-io(blocking-io))
@@ -90,7 +108,7 @@ asynchronous IO
 
 **_当一个read�作�生时,它会�历两个阶段:_**
 
-*   等待数�准备,比如accept(), recv()等待数� `(Waiting for the data to be ready)`
+*   等待数�准备,比如accept(), recv()等待数�`(Waiting for the data to be ready)`
 *   将数�从内核拷�到进程中, 比如 accept()接�到请求,recv()接收连接��的数��需��制到内核,�从内核�制到进程用户空间`(Copying the data from the kernel to the process)`
 
 **_对于socket�而言,数�的���历两个阶段:_**
@@ -206,7 +224,7 @@ linux下的asynchronous IO其实用得很少。先看一下它的�程:
 �步IO VS 异步IO:
 
 概念:
-�步与异步�步和异步关注的是___消�通信机制 ___(synchronous communication/ asynchronous communication)所谓�步,就是在�出一个_调用_时,在没有得到结果之�,该_调用_就�返回。但是一旦调用返回,就得到返回值了。���说,就是由_调用者_主动等待这个_调用_的结果。而异步则是相�,_调用_在�出之�,这个调用就直接返回了,所以没有返回结果。���说,当一个异步过程调用�出�,调用者�会立刻得到结果。而是在_调用_�出�,_被调用者_通过状��通知�通知调用者,或通过回调函数处�这个调用。
+�步与异步�步和异步关注的是___消�通信机制___(synchronous communication/ asynchronous communication)所谓�步,就是在�出一个_调用_时,在没有得到结果之�,该_调用_就�返回。但是一旦调用返回,就得到返回值了。���说,就是由_调用者_主动等待这个_调用_的结果。而异步则是相�,_调用_在�出之�,这个调用就直接返回了,所以没有返回结果。���说,当一个异步过程调用�出�,调用者�会立刻得到结果。而是在_调用_�出�,_被调用者_通过状��通知�通知调用者,或通过回调函数处�这个调用。
 
 典型的异步编程模型比如Node.js举个通俗的例�:你打电�问书店��有没有《分布�系统》这本书,如果是�步通信机制,书店��会说,你�等,�我查一下",然�开始查啊查,等查好了(�能是5秒,也�能是一天)告诉你结果(返回结果)。而异步通信机制,书店��直接告诉你我查一下啊,查好了打电�给你,然�直接挂电�了(�返回结果)。然�查好了,他会主动打电�给你。在这里��通过“回电�这�方��回调。
 
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md"
similarity index 97%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md"
index 32a6684..04f25ab 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md"
@@ -1,3 +1,13 @@
+# 目录
+
+  * [当�环境](#当�环境)
+    * [处� socket 输入输出�](#处�-socket-输入输出�)
+    * [结果展示](#结果展示)
+  * [请求模型优化](#请求模型优化)
+  * [补充1:TCP客户端与�务端](#补充1:tcp客户端与�务端)
+  * [补充2:UDP客户端和�务端](#补充2:udp客户端和�务端)
+
+
 本文转自:https://github.com/jasonGeng88/blog
 
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md"
similarity index 98%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md"
index 65d0917..47e6d17 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md"
@@ -1,3 +1,13 @@
+# 目录
+
+  * [当�环境](#当�环境)
+  * [代�地�](#代�地�)
+  * [知识点](#知识点)
+    * [获� socket 连接](#获�-socket-连接)
+    * [完整示例](#完整示例)
+    * [处�连接就绪事件](#处�连接就绪事件)
+
+
 本文转载自:[https://github.com/jasonGeng88/blog](https://github.com/jasonGeng88/blog)
 
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
@@ -474,4 +484,4 @@ public class NioNonBlockingHttpClient {
 ````
 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405094343.png)
 
-本文从 nio 的阻塞方�讲起,介�了阻塞 I/O 与�阻塞 I/O 的区别,以�在 nio 下是如何一步步构建一个 IO 多路�用的模型的客户端。文中需��解的内容比较多,如果有�解错误的地方,欢迎指正~
\ No newline at end of file
+本文从 nio 的阻塞方�讲起,介�了阻塞 I/O 与�阻塞 I/O 的区别,以�在 nio 下是如何一步步构建一个 IO 多路�用的模型的客户端。文中需��解的内容比较多,如果有�解错误的地方,欢迎指正~
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md"
similarity index 94%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md"
index 4ec65bc..35be566 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md"
@@ -1,3 +1,18 @@
+# 目录
+
+* [目录](#目录)
+  * [阻塞模� IO](#阻塞模�-io)
+  * [�阻塞 IO](#�阻塞-io)
+  * [NIO.2 异步 IO](#nio2-异步-io)
+    * [1�返回 Future 实例](#1�返回-future-实例)
+    * [2��供 CompletionHandler 回调函数](#2��供-completionhandler-回调函数)
+    * [AsynchronousFileChannel](#asynchronousfilechannel)
+    * [AsynchronousServerSocketChannel](#asynchronousserversocketchannel)
+    * [AsynchronousSocketChannel](#asynchronoussocketchannel)
+    * [Asynchronous Channel Groups](#asynchronous-channel-groups)
+  * [�结](#�结)
+
+
 # 目录
   * [阻塞模� IO](#阻塞模�-io)
   * [�阻塞 IO](#�阻塞-io)
@@ -32,7 +47,7 @@
 
 上一篇文章介�了 Java NIO 中 Buffer�Channel 和 Selector 的基本�作,主�是一些接��作,比较简�。
 
-本文将介�**�阻塞 IO** 和**异步 IO**,也就是大家耳熟能详的 NIO 和 AIO。很多�学者�能分�清楚异步和�阻塞的区别,�是在��场�能�到**异步�阻塞**这个�。
+本文将介�**�阻塞 IO**和**异步 IO**,也就是大家耳熟能详的 NIO 和 AIO。很多�学者�能分�清楚异步和�阻塞的区别,�是在��场�能�到**异步�阻塞**这个�。
 
 本文会先介�并演示阻塞模�,然�引入�阻塞模��对阻塞模�进行优化,最��介� JDK7 引入的异步 IO,由于网上关于异步 IO 的介�相对较少,所以这部分内容我会介�得具体一些。
 
@@ -160,7 +175,7 @@ select 和 poll 都有一个共�的问题,那就是**它们都�会告诉
 
 **epoll**:2002 年� Linux 内核 2.5.44 �布,epoll 能直接返回具体的准备好的通�,时间��度 O(1)。
 
-除了 Linux 中的 epoll,2000 年 FreeBSD 出现了 **Kqueue**,还有就是,Solaris 中有 **/dev/poll**。
+除了 Linux 中的 epoll,2000 年 FreeBSD 出现了**Kqueue**,还有就是,Solaris 中有**/dev/poll**。
 
 > ��说了那么多实现,但是没有出现 Windows,Windows 平�的�阻塞 IO 使用 select,我们也�必觉得 Windows 很��,在 Windows 中 IOCP �供的异步 IO 是比较强大的。
 
@@ -239,13 +254,13 @@ More New IO,或称 NIO.2,� JDK 1.7 �布,包括了引入异步 IO 接
 
 **在 Unix/Linux 等系统中,JDK 使用了并�包中的线程池�管�任务**,具体�以查看 AsynchronousChannelGroup 的��。
 
-在 Windows �作系统中,�供了一个�� [I/O Completion Ports](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198.aspx) 的方案,通常简称为 **IOCP**,�作系统负责管�线程池,其性能�常优异,所以**在 Windows 中 JDK 直接采用了 IOCP 的支�**,使用系统支�,把更多的�作信�暴露给�作系统,也使得�作系统能够对我们的 IO 进行一定程度的优化。
+在 Windows �作系统中,�供了一个��[I/O Completion Ports](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198.aspx)的方案,通常简称为**IOCP**,�作系统负责管�线程池,其性能�常优异,所以**在 Windows 中 JDK 直接采用了 IOCP 的支�**,使用系统支�,把更多的�作信�暴露给�作系统,也使得�作系统能够对我们的 IO 进行一定程度的优化。
 
 > 在 Linux 中其实也是有异步 IO 系统实现的,但是�制比较多,性能也一般,所以 JDK 采用了自建线程池的方�。
 
 本文还是以实用为主,想�了解更多信�请自行查找其他资料,下�对 Java 异步 IO 进行实践性的介�。
 
-总共有三个类需�我们关注,分别是 **AsynchronousSocketChannel**,**AsynchronousServerSocketChannel** 和 **AsynchronousFileChannel**,��过是在之�介�的 FileChannel�SocketChannel 和 ServerSocketChannel 的类�上加了个�缀 **Asynchronous**。
+总共有三个类需�我们关注,分别是**AsynchronousSocketChannel**,**AsynchronousServerSocketChannel**和**AsynchronousFileChannel**,��过是在之�介�的 FileChannel�SocketChannel 和 ServerSocketChannel 的类�上加了个�缀**Asynchronous**。
 
 Java 异步 IO �供了两�使用方�,分别是返回 Future 实例和使用回调函数。
 
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md"
similarity index 97%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md"
index 8a4ec27..41852cd 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md"
@@ -1,3 +1,14 @@
+# 目录
+
+  * [为什么� I/O 多路�用](#为什么�-io-多路�用)
+    * [select](#select)
+    * [poll](#poll)
+    * [epoll](#epoll)
+      * [epoll_create 用�创建一个 epoll �述符:](#epoll_create-用�创建一个-epoll-�述符:)
+      * [epoll_ctl 用�增/删/改内核中的事件表:](#epoll_ctl-用�增删改内核中的事件表:)
+      * [epoll_wait 用�等待事件](#epoll_wait-用�等待事件)
+
+
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
 > https://github.com/h2pl/Java-Tutorial
 
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md"
similarity index 99%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md"
index 2a3740d..7404a6e 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232Tomcat\344\270\255\347\232\204Connector\346\272\220\347\240\201\345\210\206\346\236\220\357\274\210NIO\357\274\211.md"
@@ -1,3 +1,17 @@
+# 目录
+
+* [目录](#目录)
+  * [�言](#�言)
+  * [��环境准备](#��环境准备)
+  * [endpoint](#endpoint)
+  * [init 过程分�](#init-过程分�)
+  * [start 过程分�](#start-过程分�)
+  * [Acceptor](#acceptor)
+  * [Poller](#poller)
+  * [processKey](#processkey)
+  * [总结](#总结)
+
+
 # 目录
   * [�言](#�言)
   * [��环境准备](#��环境准备)
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md"
similarity index 98%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md"
index e21ceef..f2d39e8 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md"
@@ -1,3 +1,23 @@
+# 目录
+
+* [目录](#目录)
+  * [Netty概述](#netty概述)
+  * [Netty简介](#netty简介)
+    * [Netty都有哪些组件?](#netty都有哪些组件?)
+    * [Netty是如何处�连接请求和业务逻辑的呢?](#netty是如何处�连接请求和业务逻辑的呢?)
+    * [如何�置一个Netty应用?](#如何�置一个netty应用?)
+    * [Netty是如何处�数�的?](#netty是如何处�数�的?)
+    * [如何处�我们的业务逻辑?](#如何处�我们的业务逻辑?)
+    * [ByteBuf](#bytebuf)
+    * [Channel](#channel)
+    * [ChannelHandler](#channelhandler)
+    * [ChannelPipeline](#channelpipeline)
+    * [EventLoop](#eventloop)
+    * [Bootstrap](#bootstrap)
+    * [Echo示例](#echo示例)
+  * [�考文献](#�考文献)
+
+
 # 目录
   * [Netty概述](#netty概述)
   * [etty简介](#etty简介)
@@ -169,7 +189,7 @@ Encoderså’ŒDecoders
 
 因为我们在网络传输时�能传输字节�,因此,在��数�之�,我们必须把我们的message型转�为bytes,与之对应,我们在接收数��,必须把接收到的bytes�转��message。我们把bytes to message这个过程称作Decode(解��我们�以�解的),把message to bytes这个过程�为Encode。
 
-Netty中�供了很多现�的编�/解�器,我们一般从他们的�字中便�知�他们的用途,如**ByteToMessageDecoder**�**MessageToByteEncoder**,如专门用�处�Google Protobuf�议的**ProtobufEncoder**� **ProtobufDecoder**。
+Netty中�供了很多现�的编�/解�器,我们一般从他们的�字中便�知�他们的用途,如**ByteToMessageDecoder**�**MessageToByteEncoder**,如专门用�处�Google Protobuf�议的**ProtobufEncoder**�**ProtobufDecoder**。
 
 我们��说过,具体是哪�Handler就�看它们继承的是InboundAdapter还是OutboundAdapter,对于**Decoders**,很容易便�以知�它是继承自**ChannelInboundHandlerAdapter**或 ChannelInboundHandler,因为解�的��是把ChannelPipeline**传入的bytes解��我们�以�解的message**(�Java Object),而ChannelInboundHandler正是处�Inbound Event,而Inbound Event中传入的正是字节�。Decoder会覆盖其中的“ChannelRead()�方法,在这个方法中�调用具体的decode方法解�传递过�的字节�,然�通过调用ChannelHandlerContext.fireChannelRead(decodedMessage)方法把编�好的Message传递给下一个Handler。与之类似,Encoder就�必多少了。
 
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md"
similarity index 96%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md"
index 020f65f..5d47624 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md"
@@ -1,3 +1,22 @@
+# 目录
+
+* [目录](#目录)
+  * [Buffer](#buffer)
+    * [position�limit�capacity](#position�limit�capacity)
+    * [�始化 Buffer](#�始化-buffer)
+    * [å¡«å…… Buffer](#å¡«å……-buffer)
+    * [�� Buffer 中的值](#��-buffer-中的值)
+    * [mark() & reset()](#mark--reset)
+    * [rewind() & clear() & compact()](#rewind--clear--compact)
+  * [Channel](#channel)
+    * [FileChannel](#filechannel)
+    * [SocketChannel](#socketchannel)
+    * [ServerSocketChannel](#serversocketchannel)
+    * [DatagramChannel](#datagramchannel)
+  * [Selector](#selector)
+  * [�结](#�结)
+
+
 # 目录
   * [Buffer](#buffer)
     * [position�limit�capacity](#position�limit�capacity)
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md"
similarity index 95%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md"
index f50c0df..8256703 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md"
@@ -1,3 +1,30 @@
+# 目录
+
+* [目录](#目录)
+* [mmap基础概念](#mmap基础概念)
+* [mmap内存映射原�](#mmap内存映射原�)
+* [mmap和常规文件�作的区别](#mmap和常规文件�作的区别)
+* [mmap优点总结](#mmap优点总结)
+* [mmap使用细节](#mmap使用细节)
+    * [堆外内存](#堆外内存)
+    * [在讲解DirectByteBuffer之�,需�先简�了解两个知识点](#在讲解directbytebuffer之�,需�先简�了解两个知识点)
+      * [java引用类型,因为DirectByteBuffer是通过虚引用(Phantom Reference)�实现堆外内存的释放的。](#java引用类型,因为directbytebuffer是通过虚引用phantom-reference�实现堆外内存的释放的。)
+      * [关于linux的内核�和用户�](#关于linux的内核�和用户�)
+    * [DirectByteBuffer ———— 直接缓冲](#directbytebuffer--直接缓冲)
+    * [DirectByteBuffer堆外内存的创建和回收的��解读](#directbytebuffer堆外内存的创建和回收的��解读)
+      * [堆外内存分�](#堆外内存分�)
+      * [Bits.reserveMemory(size, cap) 方法](#bitsreservememorysize-cap-方法)
+      * [堆外内存回收](#堆外内存回收)
+      * [通过�置�数的方��回收堆外内存](#通过�置�数的方��回收堆外内存)
+    * [堆外内存那些事](#堆外内存那些事)
+      * [使用堆外内存的原因](#使用堆外内存的原因)
+      * [什么情况下使用堆外内存](#什么情况下使用堆外内存)
+      * [堆外内存 VS 内存池](#堆外内存-vs-内存池)
+      * [堆外内存的特点](#堆外内存的特点)
+      * [堆外内存的一些问题](#堆外内存的一些问题)
+  * [�考文章](#�考文章)
+
+
 # 目录
 * [mmap基础概念](#mmap基础概念)
 * [mmap内存映射原�](#mmap内存映射原�)
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
similarity index 97%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
index 02dc4e4..550f7ab 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md"
@@ -1,3 +1,22 @@
+# 目录
+
+* [目录](#目录)
+  * [概述](#概述)
+  * [Selector的中的��属性](#selector的中的��属性)
+  * [Selector ��解�](#selector-��解�)
+    * [1�Selector的构建](#1�selector的构建)
+          * [接下�看下 selector.open():](#接下�看下-selectoropen:)
+    * [EPollSelectorImpl](#epollselectorimpl)
+    * [EPollArrayWrapper](#epollarraywrapper)
+    * [ServerSocketChannel的构建](#serversocketchannel的构建)
+    * [将ServerSocketChannel注册到Selector](#将serversocketchannel注册到selector)
+    * [EPollSelectorImpl. implRegister](#epollselectorimpl-implregister)
+    * [Selection�作](#selection�作)
+    * [epoll原�](#epoll原�)
+  * [�记](#�记)
+  * [�考文章](#�考文章)
+
+
 # 目录
   * [概述](#概述)
   * [Selector的中的��属性](#selector的中的��属性)
diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md" "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md"
similarity index 95%
rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md"
rename to "docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md"
index ad909d5..65f5206 100644
--- "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md"
+++ "b/docs/Java/network/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\243\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Tomcat\344\270\255\347\232\204NIO\346\250\241\345\236\213.md"
@@ -1,3 +1,16 @@
+# 目录
+
+* [目录](#目录)
+  * [一�I/O�用模型解读](#一�io�用模型解读)
+  * [二�TOMCAT对IO模型的支�](#二�tomcat对io模型的支�)
+  * [三�TOMCAT中NIO的�置与使用](#三�tomcat中nio的�置与使用)
+  * [四�NioEndpoint组件关系图解读](#四�nioendpoint组件关系图解读)
+  * [五�NioEndpoint执行�列图](#五�nioendpoint执行�列图)
+  * [六�NioEndpoint��解读](#六�nioendpoint��解读)
+  * [七�关于性能](#七�关于性能)
+  * [八�总结](#八�总结)
+
+
 # 目录
   * [一�I/O�用模型解读](#一�io�用模型解读)
   * [二�TOMCAT对IO模型的支�](#二�tomcat对io模型的支�)
diff --git "a/docs/java-web/JavaWeb\346\212\200\346\234\257\346\200\273\347\273\223.md" "b/docs/JavaWeb/JavaWeb\346\212\200\346\234\257\346\200\273\347\273\223.md"
similarity index 100%
rename from "docs/java-web/JavaWeb\346\212\200\346\234\257\346\200\273\347\273\223.md"
rename to "docs/JavaWeb/JavaWeb\346\212\200\346\234\257\346\200\273\347\273\223.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md"
similarity index 99%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md"
index e9ff30d..76dfece 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Hibernate\345\205\245\351\227\250\347\273\217\345\205\270\344\270\216\346\263\250\350\247\243\345\274\217\345\274\200\345\217\221.md"
@@ -5,9 +5,6 @@
 * [相关类](#相关类)
 * [扩展](#扩展)
 * [�考文章](#�考文章)
-* [微信公众�](#微信公众�)
-    * [个人公众�:程�员黄�斜](#个人公众�:程�员黄�斜)
-    * [技术公众�:Java技术江湖](#技术公众�:java技术江湖)
 
 
 本文转载自互�网,侵删
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JDBC\347\232\204\350\277\233\345\214\226\344\270\216\350\277\236\346\216\245\346\261\240\346\212\200\346\234\257.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JDBC\347\232\204\350\277\233\345\214\226\344\270\216\350\277\236\346\216\245\346\261\240\346\212\200\346\234\257.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JDBC\347\232\204\350\277\233\345\214\226\344\270\216\350\277\236\346\216\245\346\261\240\346\212\200\346\234\257.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JDBC\347\232\204\350\277\233\345\214\226\344\270\216\350\277\236\346\216\245\346\261\240\346\212\200\346\234\257.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JSP\344\270\216Servlet\347\232\204\346\233\276\347\273\217\344\270\216\347\216\260\345\234\250.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JSP\344\270\216Servlet\347\232\204\346\233\276\347\273\217\344\270\216\347\216\260\345\234\250.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JSP\344\270\216Servlet\347\232\204\346\233\276\347\273\217\344\270\216\347\216\260\345\234\250.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JSP\344\270\216Servlet\347\232\204\346\233\276\347\273\217\344\270\216\347\216\260\345\234\250.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JavaWeb\347\232\204\347\224\261\346\235\245\345\222\214\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JavaWeb\347\232\204\347\224\261\346\235\245\345\222\214\345\237\272\347\241\200\347\237\245\350\257\206.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JavaWeb\347\232\204\347\224\261\346\235\245\345\222\214\345\237\272\347\241\200\347\237\245\350\257\206.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232JavaWeb\347\232\204\347\224\261\346\235\245\345\222\214\345\237\272\347\241\200\347\237\245\350\257\206.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Java\346\227\245\345\277\227\347\263\273\347\273\237\347\232\204\350\257\236\347\224\237\344\270\216\345\217\221\345\261\225.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Java\346\227\245\345\277\227\347\263\273\347\273\237\347\232\204\350\257\236\347\224\237\344\270\216\345\217\221\345\261\225.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Java\346\227\245\345\277\227\347\263\273\347\273\237\347\232\204\350\257\236\347\224\237\344\270\216\345\217\221\345\261\225.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Java\346\227\245\345\277\227\347\263\273\347\273\237\347\232\204\350\257\236\347\224\237\344\270\216\345\217\221\345\261\225.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md"
similarity index 98%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md"
index 77b8926..bc23655 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Mybatis\345\205\245\351\227\250.md"
@@ -18,9 +18,6 @@
         * [3.会�工厂与会�](#3会�工厂与会�)
         * [4.�行�程](#4�行�程)
     * [测试工程�建](#测试工程�建)
-    * [微信公众�](#微信公众�-1)
-        * [个人公众�:程�员黄�斜](#个人公众�:程�员黄�斜)
-        * [技术公众�:Java技术江湖](#技术公众�:java技术江湖)
 
 
 本文转载自互�网,侵删
@@ -252,7 +249,7 @@ Mybatis通过执行器与Mappered Statement的结�实现与数�库的交互
 
 ## 测试工程�建
 
-1. 新建maven工程
+1.新建maven工程
 
 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/577318-20180702181320571-1138935145.png)
 
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md"
similarity index 98%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md"
index fa1490e..5937ec0 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat5\346\200\273\344\275\223\346\236\266\346\236\204\345\211\226\346\236\220.md"
@@ -1,3 +1,9 @@
+# 目录
+
+  * [连接器(Connector)](#连接器(connector))
+  * [容器(Container)](#容器(container))
+
+
  
 本文转载自互�网,侵删
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
@@ -187,4 +193,4 @@ Container[] conHosts = engine.findChildren();
 
 定� Servlet 的�程图:
 
-![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405153609.png)
\ No newline at end of file
+![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405153609.png)
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat\345\222\214\345\205\266\344\273\226WEB\345\256\271\345\231\250\347\232\204\345\214\272\345\210\253.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat\345\222\214\345\205\266\344\273\226WEB\345\256\271\345\231\250\347\232\204\345\214\272\345\210\253.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat\345\222\214\345\205\266\344\273\226WEB\345\256\271\345\231\250\347\232\204\345\214\272\345\210\253.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232Tomcat\345\222\214\345\205\266\344\273\226WEB\345\256\271\345\231\250\347\232\204\345\214\272\345\210\253.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216JavaBean\350\256\262\345\210\260Spring.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216JavaBean\350\256\262\345\210\260Spring.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216JavaBean\350\256\262\345\210\260Spring.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216JavaBean\350\256\262\345\210\260Spring.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md"
similarity index 96%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md"
index d04ef0d..d6cfa8c 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\344\273\216\346\211\213\345\212\250\347\274\226\350\257\221\346\211\223\345\214\205\345\210\260\351\241\271\347\233\256\346\236\204\345\273\272\345\267\245\345\205\267Maven.md"
@@ -9,8 +9,6 @@
     * [maven目录](#maven目录)
     * [Maven常用命令说明](#maven常用命令说明)
     * [Maven使用](#maven使用)
-  * [[](http://tengj.top/2018/01/01/maven/#%E4%BE%9D%E8%B5%96%E7%9A%84%E9%85%8D%E7%BD%AE "�赖的�置")�赖的�置](#[]httptengjtop20180101mavene4be9de8b596e79a84e9858de7bdae-�赖的�置�赖的�置)
-  * [[](http://tengj.top/2018/01/01/maven/#%E4%BE%9D%E8%B5%96%E8%8C%83%E5%9B%B4 "�赖范围")�赖范围](#[]httptengjtop20180101mavene4be9de8b596e88c83e59bb4-�赖范围�赖范围)
     * [传递性�赖](#传递性�赖)
     * [�赖范围](#�赖范围)
   * [Maven和Gradle的比较](#maven和gradle的比较)
@@ -90,7 +88,7 @@ Maven�仅是构建工具,还是一个�赖管�工具和项目管�工具
 
 [![image.png](http://www.pianshen.com/images/370/fb1719c12ec1fec62d766168eb5fb2d2.png "image.png")](http://upload-images.jianshu.io/upload_images/5811881-7482108a7ff71031.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 "image.png")
 
-三:最�设置一下环境��,将Maven安装�置到�作系统环境中,主�就是�置M2_HOME 和PATH两项,如图
+三:最�设置一下环境��,将Maven安装�置到�作系统环境中,主�就是�置M2_HOME和PATH两项,如图
 [![image.png](http://www.pianshen.com/images/162/46a29661ccbce3f798e931c61c9b39aa.png "image.png")](http://upload-images.jianshu.io/upload_images/5811881-ffdf167e64415703.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 "image.png")
 
 都�定�,验�一下,打开doc输入 mvn -v如何得到下�信�就说明�置�功了
@@ -187,7 +185,7 @@ name元素生命了一个对于用户更为�好的项目�称,虽然这�
 �赖范围就是用�控制�赖和三�classpath(编译classpath,测试classpath��行classpath)的关系,Maven有如下几��赖范围:
 
 *   compile:编译�赖范围。如果没有指定,就会默认使用该�赖范围。使用此�赖范围的Maven�赖,对于编译�测试��行三�classpath都有效。典型的例�是spring-code,在编译�测试和�行的时候都需�使用该�赖。
-*   test: 测试�赖范围。使用次�赖范围的Maven�赖,�对于测试classpath有效,在编译主代�或者�行项目的使用时将无法使用此�赖。典型的例�是Jnuit,它�有在编译测试代���行测试的时候�需�。
+*   test:测试�赖范围。使用次�赖范围的Maven�赖,�对于测试classpath有效,在编译主代�或者�行项目的使用时将无法使用此�赖。典型的例�是Jnuit,它�有在编译测试代���行测试的时候�需�。
 *   provided:已�供�赖范围。使用此�赖范围的Maven�赖,对于编译和测试classpath有效,但在�行时候无效。典型的例�是servlet-api,编译和测试项目的时候需�该�赖,但在�行项目的时候,由于容器以��供,就�需�Maven��地引入一�。
 *   runtime:�行时�赖范围。使用此�赖范围的Maven�赖,对于测试和�行classpath有效,但在编译主代�时无效。典型的例�是JDBC驱动实现,项目主代�的编译�需�JDK�供的JDBC接�,�有在执行测试或者�行项目的时候�需�实现上述接�的具体JDBC驱动。
 *   system:系统�赖范围。该�赖与三�classpath的关系,和provided�赖范围完全一致,但是,使用system范围的�赖时必须通过systemPath元素显示地指定�赖文件的路径。由于此类�赖�是通过Maven仓库解�的,而且往往与本机系统绑定,�能构�构建的��移�,因此应该谨慎使用。systemPath元素�以引用环境��,如:
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\210\235\346\216\242Tomcat9\347\232\204HTTP\350\257\267\346\261\202\350\277\207\347\250\213.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\210\235\346\216\242Tomcat9\347\232\204HTTP\350\257\267\346\261\202\350\277\207\347\250\213.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\210\235\346\216\242Tomcat9\347\232\204HTTP\350\257\267\346\261\202\350\277\207\347\250\213.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\210\235\346\216\242Tomcat9\347\232\204HTTP\350\257\267\346\261\202\350\277\207\347\250\213.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md"
similarity index 99%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md"
index 0732005..ee6dffc 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\345\215\225\345\205\203\346\265\213\350\257\225\346\241\206\346\236\266Junit.md"
@@ -16,9 +16,6 @@
         * [5.1 示例一:简�的 JUnit 3.X 测试](#51-示例一:简�的-junit-3x-测试)
     * [6 个人建议](#6-个人建议)
 * [8 大�元测试框架](#8-大�元测试框架)
-* [微信公众�](#微信公众�)
-    * [个人公众�:程�员黄�斜](#个人公众�:程�员黄�斜)
-    * [技术公众�:Java技术江湖](#技术公众�:java技术江湖)
 
 
 
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md"
similarity index 94%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md"
index 8384ba1..e98f6e8 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\236\201\347\256\200\351\205\215\347\275\256\347\232\204SpringBoot.md"
@@ -15,9 +15,6 @@
     * [Spring Boot 支� JSP](#spring-boot-支�-jsp)
     * [集� MyBatis](#集�-mybatis)
 * [springMVC和springboot的区别](#springmvc和springboot的区别)
-* [微信公众�](#微信公众�)
-    * [个人公众�:程�员黄�斜](#个人公众�:程�员黄�斜)
-    * [技术公众�:Java技术江湖](#技术公众�:java技术江湖)
 
 
 本文转载自互�网,侵删  
@@ -120,7 +117,7 @@ public class HelloController {
     @RequestMapping("/hello")    public String hello() {        return "Hello Spring Boot!";    }}  
 ```  
 
-*   **@RestController 注解:** 该注解是 @Controller 和 @ResponseBody 注解的�体版
+*   **@RestController 注解:**该注解是 @Controller 和 @ResponseBody 注解的�体版
 
 #### 第三步:利用 IDEA �动 Spring Boot
 
@@ -163,7 +160,7 @@ public class HelloController {
                                         org.springframework.boot                spring-boot-maven-plugin                          
 ```  
 
-我们�以看到一个比较陌生一些的标签 `` ,这个标签是在�置 Spring Boot 的父级�赖:
+我们�以看到一个比较陌生一些的标签``,这个标签是在�置 Spring Boot 的父级�赖:
 
 ```  
   
@@ -176,12 +173,12 @@ public class HelloController {
 
 #### 应用入�类
 
-Spring Boot 项目通常有一个�为 *Application 的入�类,入�类里有一个 main 方法, **这个 main 方法其实就是一个标准的 Javay 应用的入�方法。**
+Spring Boot 项目通常有一个�为 *Application 的入�类,入�类里有一个 main 方法,**这个 main 方法其实就是一个标准的 Javay 应用的入�方法。**
 
-**@SpringBootApplication** 是 Spring Boot 的核心注解,它是一个组�注解,该注解组�了:**@Configuration�@EnableAutoConfiguration�@ComponentScan;** 若�是用 @SpringBootApplication 注解也�以使用这三个注解代替。
+**@SpringBootApplication**是 Spring Boot 的核心注解,它是一个组�注解,该注解组�了:**@Configuration�@EnableAutoConfiguration�@ComponentScan;**若�是用 @SpringBootApplication 注解也�以使用这三个注解代替。
 
 *   其中,**@EnableAutoConfiguration 让 Spring Boot 根�类路径中的 jar 包�赖为当�项目进行自动�置**,例如,添加了 spring-boot-starter-web �赖,会自动添加 Tomcat 和 Spring MVC 的�赖,那么 Spring Boot 会对 Tomcat 和 Spring MVC 进行自动�置。
-*   **Spring Boot 还会自动扫� @SpringBootApplication 所在类的�级包以�下级包里的 Bean** ,所以入�类建议就�置在 grounpID + arctifactID 组�的包�下(这里为 cn.wmyskxz.springboot 包)
+*   **Spring Boot 还会自动扫� @SpringBootApplication 所在类的�级包以�下级包里的 Bean**,所以入�类建议就�置在 grounpID + arctifactID 组�的包�下(这里为 cn.wmyskxz.springboot 包)
 
 #### Spring Boot 的�置文件
 
@@ -209,7 +206,7 @@ Spring Boot 的全局�置文件的作用是对一些默认�置的�置值
 
 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-c85216e8ea7910f0.png)
 
-*   **注�:** 我们并没有在 yml 文件中注明属性的类型,而是在使用的时候定义的。
+*   **注�:**我们并没有在 yml 文件中注明属性的类型,而是在使用的时候定义的。
 
 你也�以在�置文件中使用当��置:
 
@@ -219,7 +216,7 @@ Spring Boot 的全局�置文件的作用是对一些默认�置的�置值
 
 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-eabc3cd39b44fd0d.png)
 
-*   **问题:** 这样写�置文件��而且�能会造�类的臃肿,因为有许许多多的 @Value 注解。
+*   **问题:**这样写�置文件��而且�能会造�类的臃肿,因为有许许多多的 @Value 注解。
 
 > *   �装�置信�
 
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\265\205\346\236\220Tomcat\350\257\267\346\261\202\345\244\204\347\220\206\346\265\201\347\250\213\344\270\216\345\220\257\345\212\250\351\203\250\347\275\262\350\277\207\347\250\213.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\265\205\346\236\220Tomcat\350\257\267\346\261\202\345\244\204\347\220\206\346\265\201\347\250\213\344\270\216\345\220\257\345\212\250\351\203\250\347\275\262\350\277\207\347\250\213.md"
similarity index 100%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\265\205\346\236\220Tomcat\350\257\267\346\261\202\345\244\204\347\220\206\346\265\201\347\250\213\344\270\216\345\220\257\345\212\250\351\203\250\347\275\262\350\277\207\347\250\213.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\265\205\346\236\220Tomcat\350\257\267\346\261\202\345\244\204\347\220\206\346\265\201\347\250\213\344\270\216\345\220\257\345\212\250\351\203\250\347\275\262\350\277\207\347\250\213.md"
diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md"
similarity index 97%
rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md"
rename to "docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md"
index 1b37c95..7556dc4 100644
--- "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md"
+++ "b/docs/JavaWeb/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\214\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md"
@@ -1,5 +1,4 @@
 # 目录
-
 * [引言](#引言)
 * [工作原�原型图](#工作原�原型图)
 * [工作原�解�](#工作原�解�)
@@ -8,9 +7,6 @@
 * [MyBatis的�置](#mybatis的�置)
 * [MyBatis的主��员](#mybatis的主��员)
 * [�考文章](#�考文章)
-* [微信公众�](#微信公众�)
-    * [个人公众�:程�员黄�斜](#个人公众�:程�员黄�斜)
-    * [技术公众�:Java技术江湖](#技术公众�:java技术江湖)
 
 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看
 
diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringAOP\347\232\204\346\246\202\345\277\265\344\270\216\344\275\234\347\224\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringAOP\347\232\204\346\246\202\345\277\265\344\270\216\344\275\234\347\224\250.md"
new file mode 100644
index 0000000..26e828a
--- /dev/null
+++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringAOP\347\232\204\346\246\202\345\277\265\344\270\216\344\275\234\347\224\250.md"
@@ -0,0 +1,621 @@
+# Spring ¿ò¼ÜµÄ AOP
+
+## Spring ¿ò¼ÜµÄ AOP
+
+Spring ¿ò¼ÜµÄÒ»¸ö¹Ø¼ü×é¼þÊÇ**ÃæÏòÇÐÃæµÄ±à³Ì**(AOP)¿ò¼Ü¡£ÃæÏòÇÐÃæµÄ±à³ÌÐèÒª°Ñ³ÌÐòÂß¼­·Ö½â³É²»Í¬µÄ²¿·Ö³ÆÎªËùνµÄ¹Ø×¢µã¡£¿çÒ»¸öÓ¦ÓóÌÐòµÄ¶à¸öµãµÄ¹¦Äܱ»³ÆÎª**ºáÇйØ×¢µã**£¬ÕâЩºáÇйØ×¢µãÔÚ¸ÅÄîÉ϶ÀÁ¢ÓÚÓ¦ÓóÌÐòµÄÒµÎñÂß¼­¡£ÔÚÈí¼þ¿ª·¢¹ý³ÌÖÐÓи÷ÖÖ¸÷ÑùµÄºÜºÃµÄÇÐÃæµÄÀý×Ó£¬ÈçÈÕÖ¾¼Ç¼¡¢É󼯡¢ÉùÃ÷ʽÊÂÎñ¡¢°²È«ÐԺͻº´æµÈ¡£
+
+ÔÚ OOP ÖУ¬¹Ø¼üµ¥ÔªÄ£¿é¶ÈÊÇÀ࣬¶øÔÚ AOP Öе¥ÔªÄ£¿é¶ÈÊÇÇÐÃæ¡£ÒÀÀµ×¢Èë°ïÖúÄã¶ÔÓ¦ÓóÌÐò¶ÔÏóÏ໥½âñîºÏ£¬AOP ¿ÉÒÔ°ïÖúÄã´ÓËüÃÇËùÓ°ÏìµÄ¶ÔÏóÖжԺáÇйØ×¢µã½âñî¡£AOP ÏñÊDZà³ÌÓïÑԵĴ¥·¢ÎÈç Perl£¬.NET£¬Java »òÕ߯äËûÓïÑÔ¡£
+
+Spring AOP Ä£¿éÌṩÀ¹½ØÆ÷À´À¹½ØÒ»¸öÓ¦ÓóÌÐò£¬ÀýÈ磬µ±Ö´ÐÐÒ»¸ö·½·¨Ê±£¬Äã¿ÉÒÔÔÚ·½·¨Ö´ÐÐ֮ǰ»òÖ®ºóÌí¼Ó¶îÍâµÄ¹¦ÄÜ¡£
+
+## AOP ÊõÓï
+
+ÔÚÎÒÃÇ¿ªÊ¼Ê¹Óà AOP ¹¤×÷֮ǰ£¬ÈÃÎÒÃÇÊìϤһÏ AOP ¸ÅÄîºÍÊõÓï¡£ÕâЩÊõÓï²¢²»Ìض¨ÓÚ Spring£¬¶øÊÇÓë AOP Óйصġ£
+
+| Ïî | ÃèÊö |
+| --- | --- |
+| Aspect | Ò»¸öÄ£¿é¾ßÓÐÒ»×éÌṩºáÇÐÐèÇóµÄ APIs¡£ÀýÈ磬һ¸öÈÕ־ģ¿éΪÁ˼ǼÈÕÖ¾½«±» AOP ·½Ãæµ÷Óá£Ó¦ÓóÌÐò¿ÉÒÔÓµÓÐÈÎÒâÊýÁ¿µÄ·½Ã棬ÕâÈ¡¾öÓÚÐèÇó¡£ |
+| Join point | ÔÚÄãµÄÓ¦ÓóÌÐòÖÐËü´ú±íÒ»¸öµã£¬Äã¿ÉÒÔÔÚ²å¼þ AOP ·½Ãæ¡£ÄãÒ²ÄÜ˵£¬ËüÊÇÔÚʵ¼ÊµÄÓ¦ÓóÌÐòÖУ¬ÆäÖÐÒ»¸ö²Ù×÷½«Ê¹Óà Spring AOP ¿ò¼Ü¡£ |
+| Advice | ÕâÊÇʵ¼ÊÐж¯Ö®Ç°»òÖ®ºóÖ´Ðеķ½·¨¡£ÕâÊÇÔÚ³ÌÐòÖ´ÐÐÆÚ¼äͨ¹ý Spring AOP ¿ò¼Üʵ¼Ê±»µ÷ÓõĴúÂë¡£ |
+| Pointcut | ÕâÊÇÒ»×éÒ»¸ö»ò¶à¸öÁ¬½Óµã£¬Í¨ÖªÓ¦¸Ã±»Ö´ÐС£Äã¿ÉÒÔʹÓñí´ïʽ»òģʽָ¶¨ÇÐÈëµãÕýÈçÎÒÃǽ«ÔÚ AOP µÄÀý×ÓÖп´µ½µÄ¡£ |
+| Introduction | ÒýÓÃÔÊÐíÄãÌí¼Óз½·¨»òÊôÐÔµ½ÏÖÓеÄÀàÖС£ |
+| Target object | ±»Ò»¸ö»òÕß¶à¸ö·½ÃæËù֪ͨµÄ¶ÔÏó£¬Õâ¸ö¶ÔÏóÓÀÔ¶ÊÇÒ»¸ö±»´úÀí¶ÔÏó¡£Ò²³ÆÎª±»Í¨Öª¶ÔÏó¡£ |
+| Weaving | Weaving °Ñ·½ÃæÁ¬½Óµ½ÆäËüµÄÓ¦ÓóÌÐòÀàÐÍ»òÕß¶ÔÏóÉÏ£¬²¢´´½¨Ò»¸ö±»Í¨ÖªµÄ¶ÔÏó¡£ÕâЩ¿ÉÒÔÔÚ±àÒëʱ£¬Àà¼ÓÔØÊ±ºÍÔËÐÐʱÍê³É¡£ |
+
+## ֪ͨµÄÀàÐÍ
+
+Spring ·½Ãæ¿ÉÒÔʹÓÃÏÂÃæÌáµ½µÄÎåÖÖ֪ͨ¹¤×÷£º
+
+| ֪ͨ | ÃèÊö |
+| --- | --- |
+| ǰÖÃ֪ͨ | ÔÚÒ»¸ö·½·¨Ö´ÐÐ֮ǰ£¬Ö´ÐÐ֪ͨ¡£ |
+| ºóÖÃ֪ͨ | ÔÚÒ»¸ö·½·¨Ö´ÐÐÖ®ºó£¬²»¿¼ÂÇÆä½á¹û£¬Ö´ÐÐ֪ͨ¡£ |
+| ·µ»Øºó֪ͨ | ÔÚÒ»¸ö·½·¨Ö´ÐÐÖ®ºó£¬Ö»ÓÐÔÚ·½·¨³É¹¦Íê³Éʱ£¬²ÅÄÜÖ´ÐÐ֪ͨ¡£ |
+| Å׳öÒì³£ºó֪ͨ | ÔÚÒ»¸ö·½·¨Ö´ÐÐÖ®ºó£¬Ö»ÓÐÔÚ·½·¨Í˳öÅ׳öÒ쳣ʱ£¬²ÅÄÜÖ´ÐÐ֪ͨ¡£ |
+| »·ÈÆÍ¨Öª | ÔÚ½¨Òé·½·¨µ÷ÓÃ֮ǰºÍÖ®ºó£¬Ö´ÐÐ֪ͨ¡£ |
+
+## ʵÏÖ×Ô¶¨Òå·½Ãæ
+
+Spring Ö§³Ö **@AspectJ annotation style** µÄ·½·¨ºÍ**»ùÓÚģʽ**µÄ·½·¨À´ÊµÏÖ×Ô¶¨Òå·½Ãæ¡£ÕâÁ½ÖÖ·½·¨ÒѾ­ÔÚÏÂÃæÁ½¸ö×Ó½Ú½øÐÐÁËÏêϸ½âÊÍ¡£
+
+| ·½·¨ | ÃèÊö |
+| --- | --- |
+| [XML Schema based](https://www.w3cschool.cn/wkspring/omps1mm6.html) | ·½ÃæÊÇʹÓ󣹿ÀàÒÔ¼°»ùÓÚÅäÖÃµÄ XML À´ÊµÏֵġ£ |
+| [@AspectJ based](https://www.w3cschool.cn/wkspring/k4q21mm8.html) | @AspectJ ÒýÓÃÒ»ÖÖÉùÃ÷·½ÃæµÄ·ç¸ñ×÷Ϊ´øÓÐ Java 5 ×¢Ê͵ij£¹æ Java Àà×¢ÊÍ¡£ |
+
+
+
+## Spring ÖлùÓÚ AOP µÄ XML¼Ü¹¹
+
+ΪÁËÔÚ±¾½ÚµÄÃèÊöÖÐʹÓà aop ÃüÃû¿Õ¼ä±êÇ©£¬ÄãÐèÒªµ¼Èë spring-aop ¼Ü¹¹£¬ÈçÏÂËùÊö£º
+
+```
+
+
+
+   
+
+
+
+```
+
+Ä㻹ÐèÒªÔÚÄãµÄÓ¦ÓóÌÐòµÄ CLASSPATH ÖÐʹÓÃÒÔÏ AspectJ ¿âÎļþ¡£ÕâЩ¿âÎļþÔÚÒ»¸ö AspectJ ×°ÖÃµÄ ¡®lib¡¯ Ŀ¼ÖÐÊÇ¿ÉÓõ쬷ñÔòÄã¿ÉÒÔÔÚ Internet ÖÐÏÂÔØËüÃÇ¡£(×¢£ºaspectjweaver.jar ÒѰüº¬ÆäËû°ü)
+
+*   aspectjrt.jar
+
+*   aspectjweaver.jar
+
+*   aspectj.jar
+
+*   aopalliance.jar
+
+## ¡¡ÉùÃ÷Ò»¸ö aspect
+
+Ò»¸ö **aspect** ÊÇʹÓÃ ÔªËØÉùÃ÷µÄ£¬Ö§³ÖµÄ bean ÊÇʹÓà **ref** ÊôÐÔÒýÓõģ¬ÈçÏÂËùʾ£º
+
+```
+
+   
+   ...
+   
+
+
+...
+
+```
+
+ÕâÀ¡°aBean¡± ½«±»ÅäÖúÍÒÀÀµ×¢È룬¾ÍÏñÇ°ÃæµÄÕ½ÚÖÐÄã¿´µ½µÄÆäËûµÄ Spring bean Ò»Ñù¡£
+
+## ÉùÃ÷Ò»¸öÇÐÈëµã
+
+Ò»¸ö**ÇÐÈëµã**ÓÐÖúÓÚÈ·¶¨Ê¹Óò»Í¬½¨ÒéÖ´ÐеĸÐÐËȤµÄÁ¬½Óµã£¨¼´·½·¨£©¡£ÔÚ´¦Àí»ùÓÚÅäÖÃµÄ XML ¼Ü¹¹Ê±£¬ÇÐÈëµã½«»á°´ÕÕÈçÏÂËùʾ¶¨Ò壺
+
+```
+
+   
+   
+   ...
+   
+
+
+...
+
+```
+
+ÏÂÃæµÄʾÀý¶¨ÒåÁËÒ»¸öÃûΪ ¡°businessService¡± µÄÇÐÈëµã£¬¸ÃÇÐÈëµã½«Óë com.tutorialspoint °üÏ嵀 Student ÀàÖÐµÄ getName() ·½·¨ÏàÆ¥Å䣺
+
+```
+
+   
+   
+   ...
+   
+
+
+...
+
+```
+
+## ÉùÃ÷½¨Òé
+
+Äã¿ÉÒÔÔÚÖÐʹÓÃÔªËØÉùÃ÷ÈÎÒâÎåÖÖÀàÐ͵Ä֪ͨ£¬ÈçÏ£º
+
+```
+
+   
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+   ...
+   
+
+
+...
+
+```
+
+Äã¿ÉÒÔ¶Ô²»Í¬µÄ½¨ÒéʹÓÃÏàͬµÄ **doRequiredTask** »òÕß²»Í¬µÄ·½·¨¡£ÕâЩ·½·¨½«»á×÷Ϊ aspect Ä£¿éµÄÒ»²¿·ÖÀ´¶¨Òå¡£
+
+## »ùÓÚ AOP µÄ XML ¼Ü¹¹µÄʾÀý
+
+ΪÁËÀí½âÉÏÃæÌáµ½µÄ»ùÓÚ AOP µÄ XML ¼Ü¹¹µÄ¸ÅÄÈÃÎÒÃDZàдһ¸öʾÀý£¬¿ÉÒÔʵÏÖ¼¸¸ö½¨Ò顣ΪÁËÔÚÎÒÃǵÄʾÀýÖÐʹÓü¸¸ö½¨Ò飬ÈÃÎÒÃÇʹ Eclipse IDE ´¦ÓÚ¹¤×÷״̬£¬È»ºó°´ÕÕÈçϲ½Öè´´½¨Ò»¸ö Spring Ó¦ÓóÌÐò£º
+
+| ²½Öè | ÃèÊö |
+| --- | --- |
+| 1 | ´´½¨Ò»¸öÃûΪ _SpringExample_ µÄÏîÄ¿£¬²¢ÇÒÔÚËù´´½¨ÏîÄ¿µÄ **src** Îļþ¼ÐÏ´´½¨Ò»¸öÃûΪ _com.tutorialspoint_ µÄ°ü¡£ |
+| 2 | ʹÓà _Add External JARs_ Ñ¡ÏîÌí¼ÓËùÐèµÄ Spring ¿âÎļþ£¬¾ÍÈçÔÚ _Spring Hello World Example_ Õ½ÚÖнâÊ͵ÄÄÇÑù¡£ |
+| 3 | ÔÚÏîÄ¿ÖÐÌí¼Ó Spring AOP Ö¸¶¨µÄ¿âÎļþ **aspectjrt.jar£¬ aspectjweaver.jar** ºÍ **aspectj.jar**¡£ |
+| 4 | ÔÚ _com.tutorialspoint_ °üÏ´´½¨ Java Àà **Logging**£¬ _Student_ ºÍ _MainApp_¡£ |
+| 5 | ÔÚ **src** Îļþ¼ÐÏ´´½¨ Beans ÅäÖÃÎļþ _Beans.xml_¡£ |
+| 6 | ×îºóÒ»²½ÊÇ´´½¨ËùÓÐ Java ÎļþºÍ Bean ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢ÇÒ°´ÈçϽâÊ͵ÄÄÇÑùÔËÐÐÓ¦ÓóÌÐò¡£ |
+
+ÕâÀïÊÇ **Logging.java** ÎļþµÄÄÚÈÝ¡£Õâʵ¼ÊÉÏÊÇ aspect Ä£¿éµÄÒ»¸öʾÀý£¬Ëü¶¨ÒåÁËÔÚ¸÷¸öµãµ÷Óõķ½·¨¡£
+
+```
+package com.tutorialspoint;
+public class Logging {
+   /** 
+    * This is the method which I would like to execute
+    * before a selected method execution.
+    */
+   public void beforeAdvice(){
+      System.out.println("Going to setup student profile.");
+   }
+   /** 
+    * This is the method which I would like to execute
+    * after a selected method execution.
+    */
+   public void afterAdvice(){
+      System.out.println("Student profile has been setup.");
+   }
+   /** 
+    * This is the method which I would like to execute
+    * when any method returns.
+    */
+   public void afterReturningAdvice(Object retVal){
+      System.out.println("Returning:" + retVal.toString() );
+   }
+   /**
+    * This is the method which I would like to execute
+    * if there is an exception raised.
+    */
+   public void AfterThrowingAdvice(IllegalArgumentException ex){
+      System.out.println("There has been an exception: " + ex.toString());   
+   }  
+}
+```
+
+ÏÂÃæÊÇ **Student.java** ÎļþµÄÄÚÈÝ£º
+
+```
+package com.tutorialspoint;
+public class Student {
+   private Integer age;
+   private String name;
+   public void setAge(Integer age) {
+      this.age = age;
+   }
+   public Integer getAge() {
+      System.out.println("Age : " + age );
+      return age;
+   }
+   public void setName(String name) {
+      this.name = name;
+   }
+   public String getName() {
+      System.out.println("Name : " + name );
+      return name;
+   }  
+   public void printThrowException(){
+       System.out.println("Exception raised");
+       throw new IllegalArgumentException();
+   }
+}
+```
+
+ÏÂÃæÊÇ **MainApp.java** ÎļþµÄÄÚÈÝ£º
+
+```
+package com.tutorialspoint;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+public class MainApp {
+   public static void main(String[] args) {
+      ApplicationContext context = 
+             new ClassPathXmlApplicationContext("Beans.xml");
+      Student student = (Student) context.getBean("student");
+      student.getName();
+      student.getAge();      
+      student.printThrowException();
+   }
+}
+```
+
+ÏÂÃæÊÇÅäÖÃÎļþ **Beans.xml**£º
+
+```
+
+
+
+   
+      
+         
+         
+         
+         
+         
+      
+   
+
+   
+   
+      
+            
+   
+
+   
+    
+
+
+
+```
+
+Ò»µ©ÄãÒѾ­Íê³ÉµÄ´´½¨ÁËÔ´ÎļþºÍ bean ÅäÖÃÎļþ£¬ÈÃÎÒÃÇÔËÐÐÒ»ÏÂÓ¦ÓóÌÐò¡£Èç¹ûÄãµÄÓ¦ÓóÌÐòÒ»Çж¼Õý³£µÄ»°£¬Õ⽫»áÊä³öÒÔÏÂÏûÏ¢£º
+
+```
+Going to setup student profile.
+Name : Zara
+Student profile has been setup.
+Returning:Zara
+Going to setup student profile.
+Age : 11
+Student profile has been setup.
+Returning:11
+Going to setup student profile.
+Exception raised
+Student profile has been setup.
+There has been an exception: java.lang.IllegalArgumentException
+.....
+other exception content
+```
+
+ÈÃÎÒÃÇÀ´½âÊÍÒ»ÏÂÉÏÃæ¶¨ÒåµÄÔÚ com.tutorialspoint ÖÐ Ñ¡ÔñËùÓз½·¨µÄ ¡£ÈÃÎÒÃǼÙÉèһϣ¬ÄãÏëÒªÔÚÒ»¸öÌØÊâµÄ·½·¨Ö®Ç°»òÕßÖ®ºóÖ´ÐÐÄãµÄ½¨Ò飬Äã¿ÉÒÔͨ¹ýÌæ»»Ê¹ÓÃÕæÊµÀàºÍ·½·¨Ãû³ÆµÄÇÐÈëµã¶¨ÒåÖеÄÐǺţ¨*£©À´¶¨ÒåÄãµÄÇÐÈëµãÀ´Ëõ¶ÌÄãµÄÖ´ÐС£
+
+```
+
+
+
+   
+   
+      
+      
+      
+   
+   
+
+   
+   
+      
+            
+   
+
+   
+    
+
+
+
+```
+
+Èç¹ûÄãÏëÒªÖ´ÐÐͨ¹ýÕâЩ¸ü¸ÄÖ®ºóµÄʾÀýÓ¦ÓóÌÐò£¬Õ⽫»áÊä³öÒÔÏÂÏûÏ¢£º
+
+```
+Going to setup student profile.
+Name : Zara
+Student profile has been setup.
+Age : 11
+Exception raised
+.....
+other exception content
+```
+
+
+
+## Spring ÖлùÓÚ AOP µÄ @AspectJ
+
+@AspectJ ×÷Ϊͨ¹ý Java 5 ×¢ÊÍ×¢ÊÍµÄÆÕͨµÄ Java À࣬ËüÖ¸µÄÊÇÉùÃ÷ aspects µÄÒ»ÖÖ·ç¸ñ¡£Í¨¹ýÔÚÄãµÄ»ùÓڼܹ¹µÄ XML ÅäÖÃÎļþÖаüº¬ÒÔÏÂÔªËØ£¬@AspectJ Ö§³ÖÊÇ¿ÉÓõġ£
+
+```
+
+```
+
+Ä㻹ÐèÒªÔÚÄãµÄÓ¦ÓóÌÐòµÄ CLASSPATH ÖÐʹÓÃÒÔÏ AspectJ ¿âÎļþ¡£ÕâЩ¿âÎļþÔÚÒ»¸ö AspectJ ×°ÖÃµÄ ¡®lib¡¯ Ŀ¼ÖÐÊÇ¿ÉÓõģ¬Èç¹ûûÓУ¬Äã¿ÉÒÔÔÚ Internet ÖÐÏÂÔØËüÃÇ¡£
+
+*   aspectjrt.jar
+
+*   aspectjweaver.jar
+
+*   aspectj.jar
+
+*   aopalliance.jar
+
+## ¡¡ÉùÃ÷Ò»¸ö aspect
+
+Aspects ÀàºÍÆäËûÈκÎÕý³£µÄ bean Ò»Ñù£¬³ýÁËËüÃǽ«»áÓà @AspectJ ×¢ÊÍÖ®Í⣬ËüºÍÆäËûÀàÒ»Ñù¿ÉÄÜÓз½·¨ºÍ×ֶΣ¬ÈçÏÂËùʾ£º
+
+```
+package org.xyz;
+import org.aspectj.lang.annotation.Aspect;
+@Aspect
+public class AspectModule {
+}
+```
+
+ËüÃǽ«ÔÚ XML Öа´ÕÕÈçϽøÐÐÅäÖã¬¾ÍºÍÆäËûÈκΠbean Ò»Ñù£º
+
+```
+
+   
+
+
+```
+
+## ÉùÃ÷Ò»¸öÇÐÈëµã
+
+Ò»¸ö**ÇÐÈëµã**ÓÐÖúÓÚÈ·¶¨Ê¹Óò»Í¬½¨ÒéÖ´ÐеĸÐÐËȤµÄÁ¬½Óµã£¨¼´·½·¨£©¡£ÔÚ´¦Àí»ùÓÚÅäÖÃµÄ XML ¼Ü¹¹Ê±£¬ÇÐÈëµãµÄÉùÃ÷ÓÐÁ½¸ö²¿·Ö£º
+
+*   Ò»¸öÇÐÈëµã±í´ïʽ¾ö¶¨ÁËÎÒÃǸÐÐËȤµÄÄĸö·½·¨»áÕæÕý±»Ö´ÐС£
+
+*   Ò»¸öÇÐÈëµã±êÇ©°üº¬Ò»¸öÃû³ÆºÍÈÎÒâÊýÁ¿µÄ²ÎÊý¡£·½·¨µÄÕæÕýÄÚÈÝÊDz»Ïà¸ÉµÄ£¬²¢ÇÒʵ¼ÊÉÏËüÓ¦¸ÃÊǿյġ£
+
+ÏÂÃæµÄʾÀýÖж¨ÒåÁËÒ»¸öÃûΪ ¡®businessService¡¯ µÄÇÐÈëµã£¬¸ÃÇÐÈëµã½«Óë com.xyz.myapp.service °üϵÄÀàÖпÉÓõÄÿһ¸ö·½·¨ÏàÆ¥Å䣺
+
+```
+import org.aspectj.lang.annotation.Pointcut;
+@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression 
+private void businessService() {}  // signature
+```
+
+ÏÂÃæµÄʾÀýÖж¨ÒåÁËÒ»¸öÃûΪ ¡®getname¡¯ µÄÇÐÈëµã£¬¸ÃÇÐÈëµã½«Óë com.tutorialspoint °üÏ嵀 Student ÀàÖÐµÄ getName() ·½·¨ÏàÆ¥Å䣺
+
+```
+import org.aspectj.lang.annotation.Pointcut;
+@Pointcut("execution(* com.tutorialspoint.Student.getName(..))") 
+private void getname() {}
+```
+
+## ÉùÃ÷½¨Òé
+
+Äã¿ÉÒÔʹÓà @{ADVICE-NAME} ×¢ÊÍÉùÃ÷Îå¸ö½¨ÒéÖеÄÈÎÒâÒ»¸ö£¬ÈçÏÂËùʾ¡£Õâ¼ÙÉèÄãÒѾ­¶¨ÒåÁËÒ»¸öÇÐÈëµã±êÇ©·½·¨ businessService()£º
+
+```
+@Before("businessService()")
+public void doBeforeTask(){
+ ...
+}
+@After("businessService()")
+public void doAfterTask(){
+ ...
+}
+@AfterReturning(pointcut = "businessService()", returning="retVal")
+public void doAfterReturnningTask(Object retVal){
+  // you can intercept retVal here.
+  ...
+}
+@AfterThrowing(pointcut = "businessService()", throwing="ex")
+public void doAfterThrowingTask(Exception ex){
+  // you can intercept thrown exception here.
+  ...
+}
+@Around("businessService()")
+public void doAroundTask(){
+ ...
+}
+```
+
+Äã¿ÉÒÔΪÈÎÒâÒ»¸ö½¨Ò鶨ÒåÄãµÄÇÐÈëµãÄÚÁª¡£ÏÂÃæÊÇÔÚ½¨Òé֮ǰ¶¨ÒåÄÚÁªÇÐÈëµãµÄÒ»¸öʾÀý£º
+
+```
+@Before("execution(* com.xyz.myapp.service.*.*(..))")
+public doBeforeTask(){
+ ...
+}
+```
+
+## »ùÓÚ AOP µÄ @AspectJ ʾÀý
+
+ΪÁËÀí½âÉÏÃæÌáµ½µÄ¹ØÓÚ»ùÓÚ AOP µÄ @AspectJ µÄ¸ÅÄÈÃÎÒÃDZàдһ¸öʾÀý£¬¿ÉÒÔʵÏÖ¼¸¸ö½¨Ò顣ΪÁËÔÚÎÒÃǵÄʾÀýÖÐʹÓü¸¸ö½¨Ò飬ÈÃÎÒÃÇʹ Eclipse IDE ´¦ÓÚ¹¤×÷״̬£¬È»ºó°´ÕÕÈçϲ½Öè´´½¨Ò»¸ö Spring Ó¦ÓóÌÐò£º
+
+| ²½Öè | ÃèÊö |
+| --- | --- |
+| 1 | ´´½¨Ò»¸öÃûΪ _SpringExample_ µÄÏîÄ¿£¬²¢ÇÒÔÚËù´´½¨ÏîÄ¿µÄ **src** Îļþ¼ÐÏ´´½¨Ò»¸öÃûΪ _com.tutorialspoint_ µÄ°ü¡£ |
+| 2 | ʹÓà _Add External JARs_ Ñ¡ÏîÌí¼ÓËùÐèµÄ Spring ¿âÎļþ£¬¾ÍÈçÔÚ _Spring Hello World Example_ Õ½ÚÖнâÊ͵ÄÄÇÑù¡£ |
+| 3 | ÔÚÏîÄ¿ÖÐÌí¼Ó Spring AOP Ö¸¶¨µÄ¿âÎļþ **aspectjrt.jar£¬ aspectjweaver.jar** ºÍ **aspectj.jar**¡£ |
+| 4 | ÔÚ _com.tutorialspoint_ °üÏ´´½¨ Java Àà **Logging**£¬ _Student_ ºÍ _MainApp_¡£ |
+| 5 | ÔÚ **src** Îļþ¼ÐÏ´´½¨ Beans ÅäÖÃÎļþ _Beans.xml_¡£ |
+| 6 | ×îºóÒ»²½ÊÇ´´½¨ËùÓÐ Java ÎļþºÍ Bean ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢ÇÒ°´ÈçϽâÊ͵ÄÄÇÑùÔËÐÐÓ¦ÓóÌÐò¡£ |
+
+ÕâÀïÊÇ **Logging.java** ÎļþµÄÄÚÈÝ¡£Õâʵ¼ÊÉÏÊÇ aspect Ä£¿éµÄÒ»¸öʾÀý£¬Ëü¶¨ÒåÁËÔÚ¸÷¸öµãµ÷Óõķ½·¨¡£
+
+```
+package com.tutorialspoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Around;
+@Aspect
+public class Logging {
+   /** Following is the definition for a pointcut to select
+    *  all the methods available. So advice will be called
+    *  for all the methods.
+    */
+   @Pointcut("execution(* com.tutorialspoint.*.*(..))")
+   private void selectAll(){}
+   /** 
+    * This is the method which I would like to execute
+    * before a selected method execution.
+    */
+   @Before("selectAll()")
+   public void beforeAdvice(){
+      System.out.println("Going to setup student profile.");
+   }
+   /** 
+    * This is the method which I would like to execute
+    * after a selected method execution.
+    */
+   @After("selectAll()")
+   public void afterAdvice(){
+      System.out.println("Student profile has been setup.");
+   }
+   /** 
+    * This is the method which I would like to execute
+    * when any method returns.
+    */
+   @AfterReturning(pointcut = "selectAll()", returning="retVal")
+   public void afterReturningAdvice(Object retVal){
+      System.out.println("Returning:" + retVal.toString() );
+   }
+   /**
+    * This is the method which I would like to execute
+    * if there is an exception raised by any method.
+    */
+   @AfterThrowing(pointcut = "selectAll()", throwing = "ex")
+   public void AfterThrowingAdvice(IllegalArgumentException ex){
+      System.out.println("There has been an exception: " + ex.toString());   
+   }  
+}
+```
+
+ÏÂÃæÊÇ **Student.java** ÎļþµÄÄÚÈÝ£º
+
+```
+package com.tutorialspoint;
+public class Student {
+   private Integer age;
+   private String name;
+   public void setAge(Integer age) {
+      this.age = age;
+   }
+   public Integer getAge() {
+      System.out.println("Age : " + age );
+      return age;
+   }
+   public void setName(String name) {
+      this.name = name;
+   }
+   public String getName() {
+      System.out.println("Name : " + name );
+      return name;
+   }
+   public void printThrowException(){
+      System.out.println("Exception raised");
+      throw new IllegalArgumentException();
+   }
+}
+```
+
+ÏÂÃæÊÇ **MainApp.java** ÎļþµÄÄÚÈÝ£º
+
+```
+package com.tutorialspoint;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+public class MainApp {
+   public static void main(String[] args) {
+      ApplicationContext context = 
+             new ClassPathXmlApplicationContext("Beans.xml");
+      Student student = (Student) context.getBean("student");
+      student.getName();
+      student.getAge();     
+      student.printThrowException();
+   }
+}
+```
+
+ÏÂÃæÊÇÅäÖÃÎļþ **Beans.xml**£º
+
+```
+
+
+
+    
+
+   
+   
+      
+            
+   
+
+   
+    
+
+
+
+```
+
+Ò»µ©ÄãÒѾ­Íê³ÉµÄ´´½¨ÁËÔ´ÎļþºÍ bean ÅäÖÃÎļþ£¬ÈÃÎÒÃÇÔËÐÐÒ»ÏÂÓ¦ÓóÌÐò¡£Èç¹ûÄãµÄÓ¦ÓóÌÐòÒ»Çж¼Õý³£µÄ»°£¬Õ⽫»áÊä³öÒÔÏÂÏûÏ¢£º
+
+```
+Going to setup student profile.
+Name : Zara
+Student profile has been setup.
+Returning:Zara
+Going to setup student profile.
+Age : 11
+Student profile has been setup.
+Returning:11
+Going to setup student profile.
+Exception raised
+Student profile has been setup.
+There has been an exception: java.lang.IllegalArgumentException
+.....
+other exception content
+```
+
+
+
+
+
+# ²Î¿¼ÎÄÕÂ
+https://www.w3cschool.cn/wkspring
+https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html
+http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring
+https://dunwu.github.io/spring-tutorial
+https://mszlu.com/java/spring
+http://c.biancheng.net/spring/aop-module.html
\ No newline at end of file
diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringBean\347\232\204\345\256\232\344\271\211\344\270\216\347\256\241\347\220\206\357\274\210\346\240\270\345\277\203\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringBean\347\232\204\345\256\232\344\271\211\344\270\216\347\256\241\347\220\206\357\274\210\346\240\270\345\277\203\357\274\211.md"
new file mode 100644
index 0000000..c4de431
--- /dev/null
+++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/SpringBean\347\232\204\345\256\232\344\271\211\344\270\216\347\256\241\347\220\206\357\274\210\346\240\270\345\277\203\357\274\211.md"
@@ -0,0 +1,183 @@
+### ÒÀÀµ×¢Èë
+
+Spring¿ò¼ÜµÄºËÐŦÄÜÓÐÁ½¸ö£º
+
+*   SpringÈÝÆ÷×÷Ϊ³¬¼¶´ó¹¤³§£¬¸ºÔð´´½¨¡¢¹ÜÀíËùÓеÄJava¶ÔÏó£¬ÕâЩJava¶ÔÏó±»³ÆÎªBean¡£
+*   SpringÈÝÆ÷¹ÜÀíÈÝÆ÷ÖÐBeanÖ®¼äµÄÒÀÀµ¹ØÏµ£¬SpringʹÓÃÒ»ÖÖ±»³ÆÎª"ÒÀÀµ×¢Èë"µÄ·½Ê½À´¹ÜÀíBeanÖ®¼äµÄÒÀÀµ¹ØÏµ¡£
+
+ʹÓÃÒÀÀµ×¢È룬²»½ö¿ÉÒÔΪBean×¢ÈëÆÕͨµÄÊôÐÔÖµ£¬»¹¿ÉÒÔ×¢ÈëÆäËûBeanµÄÒýÓá£ÒÀÀµ×¢ÈëÊÇÒ»ÖÖÓÅÐãµÄ½âñʽ£¬Æä¿ÉÒÔÈÃBeanÒÔÅäÖÃÎļþ×éÖ¯ÔÚÒ»Æð£¬¶ø²»ÊÇÒÔÓ²±àÂëµÄ·½Ê½ñîºÏÔÚÒ»Æð¡£
+
+#### Àí½âÒÀÀµ×¢Èë
+
+Rod JohnsonÊǵÚÒ»¸ö¸ß¶ÈÖØÊÓÒÔÅäÖÃÎļþÀ´¹ÜÀíJavaʵÀýµÄЭ×÷¹ØÏµµÄÈË£¬Ëû¸øÕâÖÖ·½Ê½ÆðÁËÒ»¸öÃû×Ö£º¿ØÖÆ·´×ª£¨Inverse of Control£¬IoC£©¡£ºóÀ´Martine FowlerΪÕâÖÖ·½Ê½ÆðÁËÁíÒ»¸öÃû³Æ£ºÒÀÀµ×¢È루Dependency Injection£©£¬Òò´Ë²»¹ÜÊÇÒÀÀµ×¢È룬»¹ÊÇ¿ØÖÆ·´×ª£¬Æäº¬ÒåÍêÈ«Ïàͬ¡£µ±Ä³¸öJava¶ÔÏ󣨵÷ÓÃÕߣ©ÐèÒªµ÷ÓÃÁíÒ»¸öJava¶ÔÏ󣨱»ÒÀÀµ¶ÔÏ󣩵ķ½·¨Ê±£¬ÔÚ´«Í³Ä£Ê½ÏÂͨ³£ÓÐÁ½ÖÖ×ö·¨£º
+
+1.  ԭʼ×ö·¨: µ÷ÓÃÕß**Ö÷¶¯**´´½¨±»ÒÀÀµ¶ÔÏó£¬È»ºóÔÙµ÷Óñ»ÒÀÀµ¶ÔÏóµÄ·½·¨¡£
+2.  ¼òµ¥¹¤³§Ä£Ê½: µ÷ÓÃÕßÏÈÕÒµ½±»ÒÀÀµ¶ÔÏóµÄ¹¤³§£¬È»ºó**Ö÷¶¯**ͨ¹ý¹¤³§È¥»ñÈ¡±»ÒÀÀµ¶ÔÏó£¬×îºóÔÙµ÷Óñ»ÒÀÀµ¶ÔÏóµÄ·½·¨¡£
+
+×¢ÒâÉÏÃæµÄ**Ö÷¶¯**¶þ×Ö£¬Õâ±ØÈ»»áµ¼Öµ÷ÓÃÕßÓë±»ÒÀÀµ¶ÔÏóʵÏÖÀàµÄÓ²±àÂëñîºÏ£¬·Ç³£²»ÀûÓÚÏîÄ¿Éý¼¶µÄά»¤¡£Ê¹ÓÃSpring¿ò¼ÜÖ®ºó£¬µ÷ÓÃÕßÎÞÐè**Ö÷¶¯**»ñÈ¡±»ÒÀÀµ¶ÔÏ󣬵÷ÓÃÕßÖ»Òª**±»¶¯**½ÓÊÜSpringÈÝÆ÷Ϊµ÷ÓÃÕߵijÉÔ±±äÁ¿¸³Öµ¼´¿É£¬Óɴ˿ɼû£¬Ê¹ÓÃSpringºó£¬µ÷ÓÃÕß»ñÈ¡±»ÒÀÀµ¶ÔÏóµÄ·½Ê½ÓÉÔ­À´µÄÖ÷¶¯»ñÈ¡£¬±ä³ÉÁ˱»¶¯½ÓÊÜ¡ª¡ªËùÒÔRod Johnson³ÆÖ®Îª¿ØÖÆ·´×ª¡£
+
+ÁíÍâ´ÓSpringÈÝÆ÷µÄ½Ç¶ÈÀ´¿´£¬SpringÈÝÆ÷¸ºÔ𽫱»ÒÀÀµ¶ÔÏó¸³Öµ¸øµ÷ÓÃÕߵijÉÔ±±äÁ¿¡ª¡ªÏ൱ÓÚΪµ÷ÓÃÕß×¢ÈëËüÒÀÀµµÄʵÀý£¬Òò´ËMartine Fowler³ÆÖ®ÎªÒÀÀµ×¢Èë¡£
+
+#### ÉèÖµ×¢Èë
+
+ÉèÖµ×¢ÈëÊÇÖ¸IoCÈÝÆ÷ͨ¹ý³ÉÔ±±äÁ¿µÄsetter·½·¨À´×¢Èë±»ÒÀÀµ¶ÔÏó¡£ÕâÖÖ×¢È뷽ʽ¼òµ¥¡¢Ö±¹Û£¬Òò¶øÔÚSpringµÄÒÀÀµ×¢ÈëÀï´óÁ¿Ê¹Óá£
+
+#### ¹¹Ôì×¢Èë
+
+ÀûÓù¹ÔìÆ÷À´ÉèÖÃÒÀÀµ¹ØÏµµÄ·½Ê½£¬±»³ÆÎª¹¹Ôì×¢È롣ͨË×À´Ëµ£¬¾ÍÊÇÇý¶¯SpringÔڵײãÒÔ·´É䷽ʽִÐдøÖ¸¶¨²ÎÊýµÄ¹¹ÔìÆ÷£¬µ±Ö´Ðдø²ÎÊýµÄ¹¹ÔìÆ÷ʱ£¬¾Í¿ÉÀûÓù¹ÔìÆ÷²ÎÊý¶Ô³ÉÔ±±äÁ¿Ö´Ðгõʼ»¯¡ª¡ªÕâ¾ÍÊǹ¹Ôì×¢ÈëµÄ±¾ÖÊ¡£
+
+#### Á½ÖÖ×¢È뷽ʽµÄ¶Ô±È
+
+ÉèÖµ×¢ÈëÓÐÈçÏÂÓŵ㣺
+
+*   Ó봫ͳµÄJavaBeanµÄд·¨¸üÏàËÆ£¬³ÌÐò¿ª·¢ÈËÔ±¸üÈÝÒ×Àí½â¡¢½ÓÊÜ¡£Í¨¹ýsetter·½·¨É趨ÒÀÀµ¹ØÏµÏԵøü¼ÓÖ±¹Û¡¢×ÔÈ»¡£
+*   ¶ÔÓÚ¸´ÔÓµÄÒÀÀµ¹ØÏµ£¬Èç¹û²ÉÓù¹Ôì×¢È룬»áµ¼Ö¹¹ÔìÆ÷¹ýÓÚÓ·Ö×£¬ÄÑÒÔÔĶÁ¡£SpringÔÚ´´½¨BeanʵÀýʱ£¬ÐèҪͬʱʵÀý»¯ÆäÒÀÀµµÄÈ«²¿ÊµÀý£¬Òò¶øµ¼ÖÂÐÔÄÜϽµ¡£¶øÊ¹ÓÃÉèÖµ×¢È룬ÔòÄܱÜÃâÕâЩÎÊÌâ¡£
+*   ÓÈÆäÔÚijЩ³ÉÔ±±äÁ¿¿ÉÑ¡µÄÇé¿öÏ£¬¶à²ÎÊýµÄ¹¹ÔìÆ÷¸ü¼Ó±¿ÖØ¡£
+
+¹¹Ôì×¢ÈëÓÅÊÆÈçÏ£º
+
+*   ¹¹Ôì×¢Èë¿ÉÒÔÔÚ¹¹ÔìÆ÷Öоö¶¨ÒÀÀµ¹ØÏµµÄ×¢Èë˳Ðò£¬ÓÅÏÈÒÀÀµµÄÓÅÏÈ×¢Èë¡£
+*   ¶ÔÓÚÒÀÀµ¹ØÏµÎÞÐè±ä»¯µÄBean£¬¹¹Ôì×¢Èë¸üÓÐÓô¦¡£ÒòΪûÓÐsetter·½·¨£¬ËùÓеÄÒÀÀµ¹ØÏµÈ«²¿ÔÚ¹¹ÔìÆ÷ÄÚÉ趨£¬ÎÞÐëµ£ÐĺóÐøµÄ´úÂë¶ÔÒÀÀµ¹ØÏµ²úÉúÆÆ»µ¡£
+*   ÒÀÀµ¹ØÏµÖ»ÄÜÔÚ¹¹ÔìÆ÷ÖÐÉ趨£¬ÔòÖ»ÓÐ×é¼þµÄ´´½¨Õß²ÅÄܸıä×é¼þµÄÒÀÀµ¹ØÏµ£¬¶Ô×é¼þµÄµ÷ÓÃÕß¶øÑÔ£¬×é¼þÄÚ²¿µÄÒÀÀµ¹ØÏµÍêȫ͸Ã÷£¬¸ü·ûºÏ¸ßÄÚ¾ÛµÄÔ­Ôò¡£
+
+**_×¢Ò⣺_**
+½¨Òé²ÉÓÃÉèÖµ×¢ÈëΪÖ÷£¬¹¹Ôì×¢ÈëΪ¸¨µÄ×¢Èë²ßÂÔ¡£¶ÔÓÚÒÀÀµ¹ØÏµÎÞÐë±ä»¯µÄ×¢È룬¾¡Á¿²ÉÓù¹Ôì×¢È룻¶øÆäËûÒÀÀµ¹ØÏµµÄ×¢È룬Ôò¿¼ÂDzÉÓÃÉèÖµ×¢Èë¡£
+
+### SpringÈÝÆ÷ÖеÄBean
+
+¶ÔÓÚ¿ª·¢ÕßÀ´Ëµ£¬¿ª·¢ÕßʹÓÃSpring¿ò¼ÜÖ÷ÒªÊÇ×öÁ½¼þÊ£º¢Ù¿ª·¢Bean£»¢ÚÅäÖÃBean¡£¶ÔÓÚSpring¿ò¼ÜÀ´Ëµ£¬ËüÒª×öµÄ¾ÍÊǸù¾ÝÅäÖÃÎļþÀ´´´½¨BeanʵÀý£¬²¢µ÷ÓÃBeanʵÀýµÄ·½·¨Íê³É"ÒÀÀµ×¢Èë"¡ª¡ªÕâ¾ÍÊÇËùνIoCµÄ±¾ÖÊ¡£
+
+#### ÈÝÆ÷ÖÐBeanµÄ×÷ÓÃÓò
+
+µ±Í¨¹ýSpringÈÝÆ÷´´½¨Ò»¸öBeanʵÀýʱ£¬²»½ö¿ÉÒÔÍê³ÉBeanʵÀýµÄʵÀý»¯£¬»¹¿ÉÒÔΪBeanÖ¸¶¨Ìض¨µÄ×÷ÓÃÓò¡£SpringÖ§³ÖÈçÏÂÎåÖÖ×÷ÓÃÓò£º
+
+1.  singleton: µ¥Àýģʽ£¬ÔÚÕû¸öSpring IoCÈÝÆ÷ÖУ¬singleton×÷ÓÃÓòµÄBean½«Ö»Éú³ÉÒ»¸öʵÀý¡£
+2.  prototype: ÿ´Îͨ¹ýÈÝÆ÷µÄgetBean()·½·¨»ñÈ¡prototype×÷ÓÃÓòµÄBeanʱ£¬¶¼½«²úÉúÒ»¸öеÄBeanʵÀý¡£
+3.  request: ¶ÔÓÚÒ»´ÎHTTPÇëÇó£¬request×÷ÓÃÓòµÄBean½«Ö»Éú³ÉÒ»¸öʵÀý£¬ÕâÒâζ×Å£¬ÔÚͬһ´ÎHTTPÇëÇóÄÚ£¬³ÌÐòÿ´ÎÇëÇó¸ÃBean£¬µÃµ½µÄ×ÜÊÇͬһ¸öʵÀý¡£Ö»ÓÐÔÚWebÓ¦ÓÃÖÐʹÓÃSpringʱ£¬¸Ã×÷ÓÃÓò²ÅÕæÕýÓÐЧ¡£
+4.  session£º¸Ã×÷ÓÃÓò½« bean µÄ¶¨ÒåÏÞÖÆÎª HTTP »á»°¡£ Ö»ÔÚweb-aware Spring ApplicationContextµÄÉÏÏÂÎÄÖÐÓÐЧ¡£
+5.  global session: ÿ¸öÈ«¾ÖµÄHTTP Session¶ÔÓ¦Ò»¸öBeanʵÀý¡£ÔÚµäÐ͵ÄÇé¿öÏ£¬½öÔÚʹÓÃportlet contextµÄʱºòÓÐЧ£¬Í¬ÑùÖ»ÔÚWebÓ¦ÓÃÖÐÓÐЧ¡£
+
+Èç¹û²»Ö¸¶¨BeanµÄ×÷ÓÃÓò£¬SpringĬÈÏʹÓÃsingleton×÷ÓÃÓò¡£prototype×÷ÓÃÓòµÄBeanµÄ´´½¨¡¢Ïú»Ù´ú¼Û±È½Ï´ó¡£¶øsingleton×÷ÓÃÓòµÄBeanʵÀýÒ»µ©´´½¨³É¹û£¬¾Í¿ÉÒÔÖØ¸´Ê¹Óá£Òò´Ë£¬Ó¦¸Ã¾¡Á¿±ÜÃ⽫BeanÉèÖóÉprototype×÷ÓÃÓò¡£
+
+### ʹÓÃ×Ô¶¯×°Åä×¢ÈëºÏ×÷ÕßBean
+
+SpringÄÜ×Ô¶¯×°ÅäBeanÓëBeanÖ®¼äµÄÒÀÀµ¹ØÏµ£¬¼´ÎÞÐëʹÓÃrefÏÔʽָ¶¨ÒÀÀµBean£¬¶øÊÇÓÉSpringÈÝÆ÷¼ì²éXMLÅäÖÃÎļþÄÚÈÝ£¬¸ù¾ÝijÖÖ¹æÔò£¬Îªµ÷ÓÃÕßBean×¢Èë±»ÒÀÀµµÄBean¡£
+Spring×Ô¶¯×°Åä¿Éͨ¹ý``ÔªËØµÄ`default-autowire`ÊôÐÔÖ¸¶¨£¬¸ÃÊôÐÔ¶ÔÅäÖÃÎļþÖÐËùÓеÄBeanÆð×÷Óã»Ò²¿Éͨ¹ý¶Ô``ÔªËØµÄ`autowire`ÊôÐÔÖ¸¶¨£¬¸ÃÊôÐÔÖ»¶Ô¸ÃBeanÆð×÷Óá£
+
+`autowire`ºÍ`default-autowire`¿ÉÒÔ½ÓÊÜÈçÏÂÖµ£º
+
+*   `no`: ²»Ê¹ÓÃ×Ô¶¯×°Åä¡£BeanÒÀÀµ±ØÐëͨ¹ýrefÔªËØ¶¨Òå¡£ÕâÊÇĬÈÏÅäÖã¬ÔڽϴóµÄ²¿Êð»·¾³Öв»¹ÄÀø¸Ä±äÕâ¸öÅäÖã¬ÏÔʽÅäÖúÏ×÷ÕßÄܹ»µÃµ½¸üÇåÎúµÄÒÀÀµ¹ØÏµ¡£
+*   `byName`: ¸ù¾Ýsetter·½·¨Ãû½øÐÐ×Ô¶¯×°Åä¡£SpringÈÝÆ÷²éÕÒÈÝÆ÷ÖÐÈ«²¿Bean£¬ÕÒ³öÆäidÓësetter·½·¨ÃûÈ¥µôsetǰ׺£¬²¢Ð¡Ð´Ê××ÖĸºóͬÃûµÄBeanÀ´Íê³É×¢Èë¡£Èç¹ûûÓÐÕÒµ½Æ¥ÅäµÄBeanʵÀý£¬ÔòSpring²»»á½øÐÐÈκÎ×¢Èë¡£
+*   `byType`: ¸ù¾Ýsetter·½·¨µÄÐβÎÀàÐÍÀ´×Ô¶¯×°Åä¡£SpringÈÝÆ÷²éÕÒÈÝÆ÷ÖеÄÈ«²¿Bean£¬Èç¹ûÕýºÃÓÐÒ»¸öBeanÀàÐÍÓësetter·½·¨µÄÐβÎÀàÐÍÆ¥Å䣬¾Í×Ô¶¯×¢ÈëÕâ¸öBean£»Èç¹ûÕÒµ½¶à¸öÕâÑùµÄBean£¬¾ÍÅ׳öÒ»¸öÒì³££»Èç¹ûûÓÐÕÒµ½ÕâÑùµÄBean£¬Ôòʲô¶¼²»»á·¢Éú£¬setter·½·¨²»»á±»µ÷Óá£
+*   `constructor`: ÓëbyTypeÀàËÆ£¬Çø±ðÊÇÓÃÓÚ×Ô¶¯Æ¥Åä¹¹ÔìÆ÷µÄ²ÎÊý¡£Èç¹ûÈÝÆ÷²»ÄÜÇ¡ºÃÕÒµ½Ò»¸öÓë¹¹ÔìÆ÷²ÎÊýÀàÐÍÆ¥ÅäµÄBean£¬Ôò»áÅ׳öÒ»¸öÒì³£¡£
+*   `autodetect`: SpringÈÝÆ÷¸ù¾ÝBeanÄÚ²¿½á¹¹£¬×ÔÐоö¶¨Ê¹ÓÃconstructor»òbyType²ßÂÔ¡£Èç¹ûÕÒµ½Ò»¸öĬÈϵĹ¹Ô캯Êý£¬ÄÇô¾Í»áÓ¦ÓÃbyType²ßÂÔ¡£
+
+**µ±Ò»¸öBean¼ÈʹÓÃ×Ô¶¯×°ÅäÒÀÀµ£¬ÓÖʹÓÃrefÏÔʽָ¶¨ÒÀÀµÊ±£¬ÔòÏÔʽָ¶¨µÄÒÀÀµ¸²¸Ç×Ô¶¯×°ÅäÒÀÀµ£»¶ÔÓÚ´óÐ͵ÄÓ¦Ó㬲»¹ÄÀøÊ¹ÓÃ×Ô¶¯×°Åä¡£ËäȻʹÓÃ×Ô¶¯×°Åä¿É¼õÉÙÅäÖÃÎļþµÄ¹¤×÷Á¿£¬µ«´ó´ó½«ËÀÁËÒÀÀµ¹ØÏµµÄÇåÎúÐÔºÍ͸Ã÷ÐÔ¡£ÒÀÀµ¹ØÏµµÄ×°ÅäÒÀÀµÓÚÔ´ÎļþµÄÊôÐÔÃûºÍÊôÐÔÀàÐÍ£¬µ¼ÖÂBeanÓëBeanÖ®¼äµÄñîºÏ½µµÍµ½´úÂë²ã´Î£¬²»ÀûÓڸ߲ã´Î½âñî¡£**
+
+        
+
+## ´´½¨BeanµÄ3ÖÖ·½Ê½
+
+### ʹÓù¹ÔìÆ÷´´½¨BeanʵÀý
+
+ʹÓù¹ÔìÆ÷À´´´½¨BeanʵÀýÊÇ×î³£¼ûµÄÇé¿ö£¬Èç¹û²»²ÉÓù¹Ôì×¢È룬Springµ×²ã»áµ÷ÓÃBeanÀàµÄÎÞ²ÎÊý¹¹ÔìÆ÷À´´´½¨ÊµÀý£¬Òò´ËÒªÇó¸ÃBeanÀàÌṩÎÞ²ÎÊýµÄ¹¹ÔìÆ÷¡£
+
+²ÉÓÃĬÈϵĹ¹ÔìÆ÷´´½¨BeanʵÀý£¬Spring¶ÔBeanʵÀýµÄËùÓÐÊôÐÔÖ´ÐÐĬÈϳõʼ»¯£¬¼´ËùÓеĻù±¾ÀàÐ͵ÄÖµ³õʼ»¯Îª0»òfalse£»ËùÓеÄÒýÓÃÀàÐ͵ÄÖµ³õʼ»¯Îªnull¡£
+
+### ʹÓþ²Ì¬¹¤³§·½·¨´´½¨Bean
+
+ʹÓþ²Ì¬¹¤³§·½·¨´´½¨BeanʵÀýʱ£¬classÊôÐÔÒ²±ØÐëÖ¸¶¨£¬µ«´ËʱclassÊôÐÔ²¢²»ÊÇÖ¸¶¨BeanʵÀýµÄʵÏÖÀ࣬¶øÊǾ²Ì¬¹¤³§À࣬Springͨ¹ý¸ÃÊôÐÔÖªµÀÓÉÄĸö¹¤³§ÀàÀ´´´½¨BeanʵÀý¡£
+
+³ý´ËÖ®Í⣬»¹ÐèҪʹÓÃfactory-methodÊôÐÔÀ´Ö¸¶¨¾²Ì¬¹¤³§·½·¨£¬Spring½«µ÷Óþ²Ì¬¹¤³§·½·¨·µ»ØÒ»¸öBeanʵÀý£¬Ò»µ©»ñµÃÁËÖ¸¶¨BeanʵÀý£¬SpringºóÃæµÄ´¦Àí²½ÖèÓë²ÉÓÃÆÕͨ·½·¨´´½¨BeanʵÀýÍêȫһÑù¡£Èç¹û¾²Ì¬¹¤³§·½·¨ÐèÒª²ÎÊý£¬ÔòʹÓÃ``ÔªËØÖ¸¶¨¾²Ì¬¹¤³§·½·¨µÄ²ÎÊý¡£
+
+### µ÷ÓÃʵÀý¹¤³§·½·¨´´½¨Bean
+
+ʵÀý¹¤³§·½·¨Ó뾲̬¹¤³§·½·¨Ö»ÓÐÒ»¸ö²»Í¬£ºµ÷Óþ²Ì¬¹¤³§·½·¨Ö»ÐèʹÓù¤³§À༴¿É£¬¶øµ÷ÓÃʵÀý¹¤³§·½·¨ÔòÐèÒª¹¤³§ÊµÀý¡£Ê¹ÓÃʵÀý¹¤³§·½·¨Ê±£¬ÅäÖÃBeanʵÀýµÄ``ÔªËØÎÞÐëclassÊôÐÔ£¬ÅäÖÃʵÀý¹¤³§·½·¨Ê¹ÓÃ`factory-bean`Ö¸¶¨¹¤³§ÊµÀý¡£
+²ÉÓÃʵÀý¹¤³§·½·¨´´½¨BeanµÄ``ÔªËØÊ±ÐèÒªÖ¸¶¨ÈçÏÂÁ½¸öÊôÐÔ£º
+
+*   factory-bean: ¸ÃÊôÐÔµÄֵΪ¹¤³§BeanµÄid¡£
+*   factory-method: ¸ÃÊôÐÔÖ¸¶¨ÊµÀý¹¤³§µÄ¹¤³§·½·¨¡£
+
+Èôµ÷ÓÃʵÀý¹¤³§·½·¨Ê±ÐèÒª´«Èë²ÎÊý£¬ÔòʹÓÃ``ÔªËØÈ·¶¨²ÎÊýÖµ¡£
+
+## Эµ÷×÷ÓÃÓò²»Í¬²½µÄBean
+
+µ±singleton×÷ÓÃÓòµÄBeanÒÀÀµÓÚprototype×÷ÓÃÓòµÄBeanʱ£¬»á²úÉú²»Í¬²½µÄÏÖÏó£¬Ô­ÒòÊÇÒòΪµ±SpringÈÝÆ÷³õʼ»¯Ê±£¬ÈÝÆ÷»áÔ¤³õʼ»¯ÈÝÆ÷ÖÐËùÓеÄ`singleton Bean`£¬ÓÉÓÚ`singleton Bean`ÒÀÀµÓÚ`prototype Bean`£¬Òò´ËSpringÔÚ³õʼ»¯`singleton Bean`֮ǰ£¬»áÏÈ´´½¨`prototypeBean`¡ª¡ªÈ»ºó²Å´´½¨`singleton Bean`£¬½ÓÏÂÀォ`prototype Bean`×¢Èë`singleton Bean`¡£
+½â¾ö²»Í¬²½µÄ·½·¨ÓÐÁ½ÖÖ£º
+
+*   ·ÅÆúÒÀÀµ×¢Èë: singleton×÷ÓÃÓòµÄBeanÿ´ÎÐèÒªprototype×÷ÓÃÓòµÄBeanʱ£¬Ö÷¶¯ÏòÈÝÆ÷ÇëÇóеÄBeanʵÀý£¬¼´¿É±£Ö¤Ã¿´Î×¢ÈëµÄ`prototype Bean`ʵÀý¶¼ÊÇ×îеÄʵÀý¡£
+*   ÀûÓ÷½·¨×¢Èë: ·½·¨×¢Èëͨ³£Ê¹ÓÃlookup·½·¨×¢È룬ʹÓÃlookup·½·¨×¢Èë¿ÉÒÔÈÃSpringÈÝÆ÷ÖØÐ´ÈÝÆ÷ÖÐBeanµÄ³éÏó»ò¾ßÌå·½·¨£¬·µ»Ø²éÕÒÈÝÆ÷ÖÐÆäËûBeanµÄ½á¹û£¬±»²éÕÒµÄBeanͨ³£ÊÇÒ»¸ö`non-singleton Bean`¡£Springͨ¹ýʹÓÃJDK¶¯Ì¬´úÀí»òcglib¿âÐ޸Ŀͻ§¶ËµÄ¶þ½øÖÆÂ룬´Ó¶øÊµÏÖÉÏÊöÒªÇó¡£
+
+½¨Òé²ÉÓõڶþÖÖ·½·¨£¬Ê¹Ó÷½·¨×¢È롣ΪÁËʹÓÃlookup·½·¨×¢È룬´óÖÂÐèÒªÈçÏÂÁ½²½£º
+
+1.  ½«µ÷ÓÃÕßBeanµÄʵÏÖÀඨÒåΪ³éÏóÀ࣬²¢¶¨ÒåÒ»¸ö³éÏó·½·¨À´»ñÈ¡±»ÒÀÀµµÄBean¡£
+2.  ÔÚ``ÔªËØÖÐÌí¼Ó``×ÓÔªËØÈÃSpringΪµ÷ÓÃÕßBeanµÄʵÏÖÀàʵÏÖÖ¸¶¨µÄ³éÏó·½·¨¡£
+
+**_×¢Ò⣺_**
+
+> Spring»á²ÉÓÃÔËÐÐʱ¶¯Ì¬ÔöÇ¿µÄ·½Ê½À´ÊµÏÖ``ÔªËØËùÖ¸¶¨µÄ³éÏó·½·¨£¬Èç¹ûÄ¿±ê³éÏóÀàʵÏÖ¹ý½Ó¿Ú£¬Spring»á²ÉÓÃJDK¶¯Ì¬´úÀíÀ´ÊµÏָóéÏóÀ࣬²¢ÎªÖ®ÊµÏÖ³éÏó·½·¨£»Èç¹ûÄ¿±ê³éÏóÀàûÓÐʵÏÖ¹ý½Ó¿Ú£¬Spring»á²ÉÓÃcglibʵÏָóéÏóÀ࣬²¢ÎªÖ®ÊµÏÖ³éÏó·½·¨¡£Spring4.0µÄspring-core-xxx.jar°üÖÐÒѾ­¼¯³ÉÁËcglibÀà¿â¡£
+
+## Á½ÖÖºó´¦ÀíÆ÷
+
+SpringÌṩÁËÁ½ÖÖ³£Óõĺó´¦ÀíÆ÷£º
+
+*   Beanºó´¦ÀíÆ÷: ÕâÖÖºó´¦ÀíÆ÷»á¶ÔÈÝÆ÷ÖÐBean½øÐкó´¦Àí£¬¶ÔBean½øÐжîÍâ¼ÓÇ¿¡£
+*   ÈÝÆ÷ºó´¦ÀíÆ÷: ÕâÖÖºó´¦ÀíÆ÷»á¶ÔIoCÈÝÆ÷½øÐкó´¦Àí£¬ÓÃÓÚÔöÇ¿ÈÝÆ÷¹¦ÄÜ¡£
+
+### Beanºó´¦ÀíÆ÷
+
+Beanºó´¦ÀíÆ÷ÊÇÒ»ÖÖÌØÊâµÄBean£¬ÕâÖÖÌØÊâµÄBean²¢²»¶ÔÍâÌṩ·þÎñ£¬ËüÉõÖÁ¿ÉÒÔÎÞÐëidÊôÐÔ£¬ËüÖ÷Òª¸ºÔð¶ÔÈÝÆ÷ÖÐµÄÆäËûBeanÖ´Ðкó´¦Àí£¬ÀýÈçΪÈÝÆ÷ÖеÄÄ¿±êBeanÉú³É´úÀíµÈ£¬ÕâÖÖBean³ÆÎªBeanºó´¦ÀíÆ÷¡£Beanºó´¦ÀíÆ÷»áÔÚBeanʵÀý´´½¨³É¹¦Ö®ºó£¬¶ÔBeanʵÀý½øÐнøÒ»²½µÄÔöÇ¿´¦Àí¡£Beanºó´¦ÀíÆ÷±ØÐëʵÏÖ`BeanPostProcessor`½Ó¿Ú£¬Í¬Ê±±ØÐëʵÏָýӿڵÄÁ½¸ö·½·¨¡£
+
+1.  `Object postProcessBeforeInitialization(Object bean, String name) throws BeansException`: ¸Ã·½·¨µÄµÚÒ»¸ö²ÎÊýÊÇϵͳ¼´½«½øÐкó´¦ÀíµÄBeanʵÀý£¬µÚ¶þ¸ö²ÎÊýÊǸÃBeanµÄÅäÖÃid
+2.  `Object postProcessAfterinitialization(Object bean, String name) throws BeansException`: ¸Ã·½·¨µÄµÚÒ»¸ö²ÎÊýÊÇϵͳ¼´½«½øÐкó´¦ÀíµÄBeanʵÀý£¬µÚ¶þ¸ö²ÎÊýÊǸÃBeanµÄÅäÖÃid
+
+ÈÝÆ÷ÖÐÒ»µ©×¢²áÁËBeanºó´¦ÀíÆ÷£¬Beanºó´¦ÀíÆ÷¾Í»á×Ô¶¯Æô¶¯£¬ÔÚÈÝÆ÷ÖÐÿ¸öBean´´½¨Ê±×Ô¶¯¹¤×÷£¬Beanºó´¦ÀíÆ÷Á½¸ö·½·¨µÄ»Øµ÷ʱ»úÈçÏÂͼ£º
+
+![bean-post-process](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e8970a58f831cd7613b680ef0541e7c7.jpg)
+
+×¢ÒâÒ»µã£¬Èç¹ûʹÓÃ`BeanFactory`×÷ΪSpringÈÝÆ÷£¬Ôò±ØÐëÊÖ¶¯×¢²áBeanºó´¦ÀíÆ÷£¬³ÌÐò±ØÐë»ñÈ¡Beanºó´¦ÀíÆ÷ʵÀý£¬È»ºóÊÖ¶¯×¢²á¡£
+
+BeanPostProcessor bp =  (BeanPostProcessor)beanFactory.getBean("bp"); beanFactory.addBeanPostProcessor(bp);  Person p =  (Person)beanFactory.getBean("person");
+
+### ÈÝÆ÷ºó´¦ÀíÆ÷
+
+Beanºó´¦ÀíÆ÷¸ºÔð´¦ÀíÈÝÆ÷ÖеÄËùÓÐBeanʵÀý£¬¶øÈÝÆ÷ºó´¦ÀíÆ÷Ôò¸ºÔð´¦ÀíÈÝÆ÷±¾Éí¡£ÈÝÆ÷ºó´¦ÀíÆ÷±ØÐëʵÏÖ`BeanFactoryPostProcessor`½Ó¿Ú£¬²¢ÊµÏָýӿڵÄÒ»¸ö·½·¨`postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)`ʵÏָ÷½·¨µÄ·½·¨Ìå¾ÍÊǶÔSpringÈÝÆ÷½øÐеĴ¦Àí£¬ÕâÖÖ´¦Àí¿ÉÒÔ¶ÔSpringÈÝÆ÷½øÐÐ×Ô¶¨ÒåÀ©Õ¹£¬µ±È»Ò²¿ÉÒÔ¶ÔSpringÈÝÆ÷²»½øÐÐÈκδ¦Àí¡£
+
+ÀàËÆÓÚ`BeanPostProcessor`£¬`ApplicationContext`¿É×Ô¶¯¼ì²âµ½ÈÝÆ÷ÖеÄÈÝÆ÷ºó´¦ÀíÆ÷£¬²¢ÇÒ×Ô¶¯×¢²áÈÝÆ÷ºó´¦ÀíÆ÷¡£µ«ÈôʹÓÃ`BeanFactory`×÷ΪSpringÈÝÆ÷£¬Ôò±ØÐëÊÖ¶¯µ÷ÓøÃÈÝÆ÷ºó´¦ÀíÆ÷À´´¦Àí`BeanFactory`ÈÝÆ÷¡£
+
+## SpringµÄ"ÁãÅäÖÃ"Ö§³Ö
+
+### ËÑË÷BeanÀà
+
+SpringÌṩÈçϼ¸¸öAnnotationÀ´±ê×¢Spring Bean£º
+
+*   `@Component`: ±ê×¢Ò»¸öÆÕͨµÄSpring BeanÀà
+*   `@Controller`: ±ê×¢Ò»¸ö¿ØÖÆÆ÷×é¼þÀà
+*   `@Service`: ±ê×¢Ò»¸öÒµÎñÂß¼­×é¼þÀà
+*   `@Repository`: ±ê×¢Ò»¸öDAO×é¼þÀà
+
+ÔÚSpringÅäÖÃÎļþÖÐ×öÈçÏÂÅäÖã¬Ö¸¶¨×Ô¶¯É¨ÃèµÄ°ü£º
+
+
+
+### ʹÓÃ@ResourceÅäÖÃÒÀÀµ
+
+`@Resource`λÓÚ`javax.annotation`°üÏ£¬ÊÇÀ´×ÔJavaEE¹æ·¶µÄÒ»¸ö`Annotation`£¬SpringÖ±½Ó½è¼øÁ˸Ã`Annotation`£¬Í¨¹ýʹÓøÃ`Annotation`ΪĿ±êBeanÖ¸¶¨Ð­×÷ÕßBean¡£Ê¹ÓÃ`@Resource`Óë``ÔªËØµÄrefÊôÐÔÓÐÏàͬµÄЧ¹û¡£
+`@Resource`²»½ö¿ÉÒÔÐÞÊÎsetter·½·¨£¬Ò²¿ÉÒÔÖ±½ÓÐÞÊÎʵÀý±äÁ¿£¬Èç¹ûʹÓÃ`@Resource`ÐÞÊÎʵÀý±äÁ¿½«»á¸ü¼Ó¼òµ¥£¬´ËʱSpring½«»áÖ±½ÓʹÓÃJavaEE¹æ·¶µÄField×¢È룬´ËʱÁ¬setter·½·¨¶¼¿ÉÒÔ²»Òª¡£
+
+### ʹÓÃ@PostConstructºÍ@PreDestroy¶¨ÖÆÉúÃüÖÜÆÚÐÐΪ
+
+`@PostConstruct`ºÍ`@PreDestroy`ͬÑùλÓÚjavax.annotation°üÏ£¬Ò²ÊÇÀ´×ÔJavaEE¹æ·¶µÄÁ½¸öAnnotation£¬SpringÖ±½Ó½è¼øÁËËüÃÇ£¬ÓÃÓÚ¶¨ÖÆSpringÈÝÆ÷ÖÐBeanµÄÉúÃüÖÜÆÚÐÐΪ¡£ËüÃǶ¼ÓÃÓÚÐÞÊη½·¨£¬ÎÞÐëÈκÎÊôÐÔ¡£ÆäÖÐǰÕßÐÞÊεķ½·¨Ê±BeanµÄ³õʼ»¯·½·¨£»¶øºóÕßÐÞÊεķ½·¨Ê±BeanÏú»Ù֮ǰµÄ·½·¨¡£
+
+### Spring4.0ÔöÇ¿µÄ×Ô¶¯×°ÅäºÍ¾«È·×°Åä
+
+SpringÌṩÁË`@Autowired`×¢½âÀ´Ö¸¶¨×Ô¶¯×°Å䣬`@Autowired`¿ÉÒÔÐÞÊÎsetter·½·¨¡¢ÆÕͨ·½·¨¡¢ÊµÀý±äÁ¿ºÍ¹¹ÔìÆ÷µÈ¡£µ±Ê¹ÓÃ`@Autowired`±ê×¢setter·½·¨Ê±£¬Ä¬ÈϲÉÓÃbyType×Ô¶¯×°Åä²ßÂÔ¡£ÔÚÕâÖÖ²ßÂÔÏ£¬·ûºÏ×Ô¶¯×°ÅäÀàÐ͵ĺòÑ¡BeanʵÀý³£³£Óжà¸ö£¬Õâ¸öʱºò¾Í¿ÉÄÜÒýÆðÒì³££¬ÎªÁËʵÏÖ¾«È·µÄ×Ô¶¯×°Å䣬SpringÌṩÁË`@Qualifier`×¢½â£¬Í¨¹ýʹÓÃ`@Qualifier`£¬ÔÊÐí¸ù¾ÝBeanµÄidÀ´Ö´ÐÐ×Ô¶¯×°Åä¡£
+
+# ²Î¿¼ÎÄÕÂ
+
+https://www.w3cschool.cn/wkspring
+https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html
+http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring
+https://dunwu.github.io/spring-tutorial
+https://mszlu.com/java/spring
+http://c.biancheng.net/spring/aop-module.html
\ No newline at end of file
diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\225\260\346\215\256\345\272\223\347\232\204\350\256\277\351\227\256.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\225\260\346\215\256\345\272\223\347\232\204\350\256\277\351\227\256.md"
new file mode 100644
index 0000000..69bfc96
--- /dev/null
+++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\225\260\346\215\256\345\272\223\347\232\204\350\256\277\351\227\256.md"
@@ -0,0 +1,1183 @@
+
+## ÅäÖÃÊý¾ÝÔ´
+
+### Spring ÅäÖÃÊý¾ÝÔ´
+
+Spring ÅäÖÃÊý¾ÝÔ´ÓжàÖÖ·½Ê½£¬ÏÂÃæÒ»Ò»Áо٣º
+
+#### [#](https://dunwu.github.io/spring-tutorial/pages/1b774c/#%E4%BD%BF%E7%94%A8-jndi-%E6%95%B0%E6%8D%AE%E6%BA%90)ʹÓà JNDI Êý¾ÝÔ´
+
+Èç¹û Spring Ó¦Óò¿ÊðÔÚÖ§³Ö JNDI µÄ WEB ·þÎñÆ÷ÉÏ£¨Èç WebSphere¡¢JBoss¡¢Tomcat µÈ£©£¬¾Í¿ÉÒÔʹÓà JNDI »ñÈ¡Êý¾ÝÔ´¡£
+
+
+
+```
+
+
+
+  
+  
+    
+  
+
+  
+  
+
+
+```
+
+
+
+#### [#](https://dunwu.github.io/spring-tutorial/pages/1b774c/#%E4%BD%BF%E7%94%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0)ʹÓÃÊý¾Ý¿âÁ¬½Ó³Ø
+
+Spring ±¾Éí²¢Ã»ÓÐÌṩÊý¾Ý¿âÁ¬½Ó³ØµÄʵÏÖ£¬ÐèÒª×ÔÐÐÑ¡ÔñºÏÊʵÄÊý¾Ý¿âÁ¬½Ó³Ø¡£ÏÂÃæÊÇÒ»¸öʹÓà [Druid (opens new window)](https://github.com/alibaba/druid)×÷ΪÊý¾Ý¿âÁ¬½Ó³ØµÄʾÀý£º
+
+
+
+```
+
+    
+    
+    
+    
+
+    
+    
+    
+    
+
+    
+    
+
+    
+    
+
+    
+    
+
+    
+
+    
+    
+    
+
+    
+    
+    
+
+    
+
+    
+
+    
+    
+    
+  
+
+```
+
+
+
+#### [#](https://dunwu.github.io/spring-tutorial/pages/1b774c/#%E5%9F%BA%E4%BA%8E-jdbc-%E9%A9%B1%E5%8A%A8%E7%9A%84%E6%95%B0%E6%8D%AE%E6%BA%90)»ùÓÚ JDBC Çý¶¯µÄÊý¾ÝÔ´
+
+
+
+```
+
+  
+  
+  
+  
+
+```
+
+
+#### ʹÓÃJDBC
+
+×îºó¸üÐÂ: 2022/11/16 20:10 / ÔĶÁ: 946668
+
+* * *
+
+
+
+ÎÒÃÇÔÚÇ°Ãæ½éÉÜ[JDBC±à³Ì](https://www.liaoxuefeng.com/wiki/1252599548343744/1255943820274272)ʱÒѾ­½²¹ý£¬Java³ÌÐòʹÓÃJDBC½Ó¿Ú·ÃÎʹØÏµÊý¾Ý¿âµÄʱºò£¬ÐèÒªÒÔϼ¸²½£º
+
+*   ´´½¨È«¾Ö`DataSource`ʵÀý£¬±íʾÊý¾Ý¿âÁ¬½Ó³Ø£»
+*   ÔÚÐèÒª¶ÁдÊý¾Ý¿âµÄ·½·¨ÄÚ²¿£¬°´Èçϲ½Öè·ÃÎÊÊý¾Ý¿â£º
+    *   ´ÓÈ«¾Ö`DataSource`ʵÀý»ñÈ¡`Connection`ʵÀý£»
+    *   ͨ¹ý`Connection`ʵÀý´´½¨`PreparedStatement`ʵÀý£»
+    *   Ö´ÐÐSQLÓï¾ä£¬Èç¹ûÊDzéѯ£¬Ôòͨ¹ý`ResultSet`¶ÁÈ¡½á¹û¼¯£¬Èç¹ûÊÇÐ޸ģ¬Ôò»ñµÃ`int`½á¹û¡£
+
+ÕýÈ·±àдJDBC´úÂëµÄ¹Ø¼üÊÇʹÓÃ`try ... finally`ÊÍ·Å×ÊÔ´£¬Éæ¼°µ½ÊÂÎñµÄ´úÂëÐèÒªÕýÈ·Ìá½»»ò»Ø¹öÊÂÎñ¡£
+
+ÔÚSpringʹÓÃJDBC£¬Ê×ÏÈÎÒÃÇͨ¹ýIoCÈÝÆ÷´´½¨²¢¹ÜÀíÒ»¸ö`DataSource`ʵÀý£¬È»ºó£¬SpringÌṩÁËÒ»¸ö`JdbcTemplate`£¬¿ÉÒÔ·½±ãµØÈÃÎÒÃDzÙ×÷JDBC£¬Òò´Ë£¬Í¨³£Çé¿öÏ£¬ÎÒÃÇ»áʵÀý»¯Ò»¸ö`JdbcTemplate`¡£¹ËÃû˼Ò壬Õâ¸öÀàÖ÷ҪʹÓÃÁË[Templateģʽ](https://www.liaoxuefeng.com/wiki/1252599548343744/1281319636041762)¡£
+
+±àдʾÀý´úÂë»òÕß²âÊÔ´úÂëʱ£¬ÎÒÃÇÇ¿ÁÒÍÆ¼öʹÓÃ[HSQLDB](http://hsqldb.org/)Õâ¸öÊý¾Ý¿â£¬ËüÊÇÒ»¸öÓÃJava±àдµÄ¹ØÏµÊý¾Ý¿â£¬¿ÉÒÔÒÔÄÚ´æÄ£Ê½»òÕßÎļþģʽÔËÐУ¬±¾ÉíÖ»ÓÐÒ»¸öjar°ü£¬·Ç³£ÊʺÏÑÝʾ´úÂë»òÕß²âÊÔ´úÂë¡£
+
+ÎÒÃÇÒÔʵ¼Ê¹¤³ÌΪÀý£¬ÏÈ´´½¨Maven¹¤³Ì`spring-data-jdbc`£¬È»ºóÒýÈëÒÔÏÂÒÀÀµ£º
+
+*   org.springframework:spring-context:6.0.0
+*   org.springframework:spring-jdbc:6.0.0
+*   jakarta.annotation:jakarta.annotation-api:2.1.1
+*   com.zaxxer:HikariCP:5.0.1
+*   org.hsqldb:hsqldb:2.7.1
+
+ÔÚ`AppConfig`ÖУ¬ÎÒÃÇÐèÒª´´½¨ÒÔϼ¸¸ö±ØÐëµÄBean£º
+
+```
+@Configuration
+@ComponentScan
+@PropertySource("jdbc.properties")
+public class AppConfig {
+
+    @Value("${jdbc.url}")
+    String jdbcUrl;
+
+    @Value("${jdbc.username}")
+    String jdbcUsername;
+
+    @Value("${jdbc.password}")
+    String jdbcPassword;
+
+    @Bean
+    DataSource createDataSource() {
+        HikariConfig config = new HikariConfig();
+        config.setJdbcUrl(jdbcUrl);
+        config.setUsername(jdbcUsername);
+        config.setPassword(jdbcPassword);
+        config.addDataSourceProperty("autoCommit", "true");
+        config.addDataSourceProperty("connectionTimeout", "5");
+        config.addDataSourceProperty("idleTimeout", "60");
+        return new HikariDataSource(config);
+    }
+
+    @Bean
+    JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
+        return new JdbcTemplate(dataSource);
+    }
+}
+
+```
+
+ÔÚÉÏÊöÅäÖÃÖУº
+
+1.  ͨ¹ý`@PropertySource("jdbc.properties")`¶ÁÈ¡Êý¾Ý¿âÅäÖÃÎļþ£»
+2.  ͨ¹ý`@Value("${jdbc.url}")`×¢ÈëÅäÖÃÎļþµÄÏà¹ØÅäÖã»
+3.  ´´½¨Ò»¸öDataSourceʵÀý£¬ËüµÄʵ¼ÊÀàÐÍÊÇ`HikariDataSource`£¬´´½¨Ê±ÐèÒªÓõ½×¢ÈëµÄÅäÖã»
+4.  ´´½¨Ò»¸öJdbcTemplateʵÀý£¬ËüÐèҪעÈë`DataSource`£¬ÕâÊÇͨ¹ý·½·¨²ÎÊýÍê³É×¢ÈëµÄ¡£
+
+×îºó£¬Õë¶ÔHSQLDBдһ¸öÅäÖÃÎļþ`jdbc.properties`£º
+
+```
+# Êý¾Ý¿âÎļþÃûΪtestdb:
+jdbc.url=jdbc:hsqldb:file:testdb
+
+# HsqldbĬÈϵÄÓû§ÃûÊÇsa£¬¿ÚÁîÊÇ¿Õ×Ö·û´®:
+jdbc.username=sa
+jdbc.password=
+
+```
+
+¿ÉÒÔͨ¹ýHSQLDB×Ô´øµÄ¹¤¾ßÀ´³õʼ»¯Êý¾Ý¿â±í£¬ÕâÀïÎÒÃÇдһ¸öBean£¬ÔÚSpringÈÝÆ÷Æô¶¯Ê±×Ô¶¯´´½¨Ò»¸ö`users`±í£º
+
+```
+@Component
+public class DatabaseInitializer {
+    @Autowired
+    JdbcTemplate jdbcTemplate;
+
+    @PostConstruct
+    public void init() {
+        jdbcTemplate.update("CREATE TABLE IF NOT EXISTS users (" //
+                + "id BIGINT IDENTITY NOT NULL PRIMARY KEY, " //
+                + "email VARCHAR(100) NOT NULL, " //
+                + "password VARCHAR(100) NOT NULL, " //
+                + "name VARCHAR(100) NOT NULL, " //
+                + "UNIQUE (email))");
+    }
+}
+
+```
+
+ÏÖÔÚ£¬ËùÓÐ×¼±¸¹¤×÷¶¼ÒÑÍê±Ï¡£ÎÒÃÇÖ»ÐèÒªÔÚÐèÒª·ÃÎÊÊý¾Ý¿âµÄBeanÖУ¬×¢Èë`JdbcTemplate`¼´¿É£º
+
+```
+@Component
+public class UserService {
+    @Autowired
+    JdbcTemplate jdbcTemplate;
+    ...
+}
+
+```
+
+### JdbcTemplateÓ÷¨
+
+SpringÌṩµÄ`JdbcTemplate`²ÉÓÃTemplateģʽ£¬ÌṩÁËһϵÁÐÒԻص÷ÎªÌØµãµÄ¹¤¾ß·½·¨£¬Ä¿µÄÊDZÜÃâ·±ËöµÄ`try...catch`Óï¾ä¡£
+
+ÎÒÃÇÒÔ¾ßÌåµÄʾÀýÀ´ËµÃ÷JdbcTemplateµÄÓ÷¨¡£
+
+Ê×ÏÈÎÒÃÇ¿´`T execute(ConnectionCallback action)`·½·¨£¬ËüÌṩÁËJdbcµÄ`Connection`¹©ÎÒÃÇʹÓãº
+
+```
+public User getUserById(long id) {
+    // ×¢Òâ´«ÈëµÄÊÇConnectionCallback:
+    return jdbcTemplate.execute((Connection conn) -> {
+        // ¿ÉÒÔÖ±½ÓʹÓÃconnʵÀý£¬²»ÒªÊÍ·ÅËü£¬»Øµ÷½áÊøºóJdbcTemplate×Ô¶¯ÊÍ·Å:
+        // ÔÚÄÚ²¿ÊÖ¶¯´´½¨µÄPreparedStatement¡¢ResultSet±ØÐëÓÃtry(...)ÊÍ·Å:
+        try (var ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
+            ps.setObject(1, id);
+            try (var rs = ps.executeQuery()) {
+                if (rs.next()) {
+                    return new User( // new User object:
+                            rs.getLong("id"), // id
+                            rs.getString("email"), // email
+                            rs.getString("password"), // password
+                            rs.getString("name")); // name
+                }
+                throw new RuntimeException("user not found by id.");
+            }
+        }
+    });
+}
+
+```
+
+Ò²¾ÍÊÇ˵£¬ÉÏÊö»Øµ÷·½·¨ÔÊÐí»ñÈ¡Connection£¬È»ºó×öÈκλùÓÚConnectionµÄ²Ù×÷¡£
+
+ÎÒÃÇÔÙ¿´`T execute(String sql, PreparedStatementCallback action)`µÄÓ÷¨£º
+
+```
+public User getUserByName(String name) {
+    // ÐèÒª´«ÈëSQLÓï¾ä£¬ÒÔ¼°PreparedStatementCallback:
+    return jdbcTemplate.execute("SELECT * FROM users WHERE name = ?", (PreparedStatement ps) -> {
+        // PreparedStatementʵÀýÒѾ­ÓÉJdbcTemplate´´½¨£¬²¢Ôڻص÷ºó×Ô¶¯ÊÍ·Å:
+        ps.setObject(1, name);
+        try (var rs = ps.executeQuery()) {
+            if (rs.next()) {
+                return new User( // new User object:
+                        rs.getLong("id"), // id
+                        rs.getString("email"), // email
+                        rs.getString("password"), // password
+                        rs.getString("name")); // name
+            }
+            throw new RuntimeException("user not found by id.");
+        }
+    });
+}
+
+```
+
+×îºó£¬ÎÒÃÇ¿´`T queryForObject(String sql, RowMapper rowMapper, Object... args)`·½·¨£º
+
+```
+public User getUserByEmail(String email) {
+    // ´«ÈëSQL£¬²ÎÊýºÍRowMapperʵÀý:
+    return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email = ?",
+            (ResultSet rs, int rowNum) -> {
+                // ½«ResultSetµÄµ±Ç°ÐÐÓ³ÉäΪһ¸öJavaBean:
+                return new User( // new User object:
+                        rs.getLong("id"), // id
+                        rs.getString("email"), // email
+                        rs.getString("password"), // password
+                        rs.getString("name")); // name
+            },
+            email);
+}
+
+```
+
+ÔÚ`queryForObject()`·½·¨ÖУ¬´«ÈëSQLÒÔ¼°SQL²ÎÊýºó£¬`JdbcTemplate`»á×Ô¶¯´´½¨`PreparedStatement`£¬×Ô¶¯Ö´Ðвéѯ²¢·µ»Ø`ResultSet`£¬ÎÒÃÇÌṩµÄ`RowMapper`ÐèÒª×öµÄÊÂÇé¾ÍÊǰÑ`ResultSet`µÄµ±Ç°ÐÐÓ³Éä³ÉÒ»¸öJavaBean²¢·µ»Ø¡£Õû¸ö¹ý³ÌÖУ¬Ê¹ÓÃ`Connection`¡¢`PreparedStatement`ºÍ`ResultSet`¶¼²»ÐèÒªÎÒÃÇÊÖ¶¯¹ÜÀí¡£
+
+`RowMapper`²»Ò»¶¨·µ»ØJavaBean£¬Êµ¼ÊÉÏËü¿ÉÒÔ·µ»ØÈκÎJava¶ÔÏó¡£ÀýÈ磬ʹÓÃ`SELECT COUNT(*)`²éѯʱ£¬¿ÉÒÔ·µ»Ø`Long`£º
+
+```
+public long getUsers() {
+    return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", (ResultSet rs, int rowNum) -> {
+        // SELECT COUNT(*)²éѯֻÓÐÒ»ÁУ¬È¡µÚÒ»ÁÐÊý¾Ý:
+        return rs.getLong(1);
+    });
+}
+
+```
+
+Èç¹ûÎÒÃÇÆÚÍû·µ»Ø¶àÐмǼ£¬¶ø²»ÊÇÒ»ÐУ¬¿ÉÒÔÓÃ`query()`·½·¨£º
+
+```
+public List getUsers(int pageIndex) {
+    int limit = 100;
+    int offset = limit * (pageIndex - 1);
+    return jdbcTemplate.query("SELECT * FROM users LIMIT ? OFFSET ?",
+            new BeanPropertyRowMapper<>(User.class),
+            limit, offset);
+}
+
+```
+
+ÉÏÊö`query()`·½·¨´«ÈëµÄ²ÎÊýÈÔÈ»ÊÇSQL¡¢SQL²ÎÊýÒÔ¼°`RowMapper`ʵÀý¡£ÕâÀïÎÒÃÇÖ±½ÓʹÓÃSpringÌṩµÄ`BeanPropertyRowMapper`¡£Èç¹ûÊý¾Ý¿â±íµÄ½á¹¹Ç¡ºÃºÍJavaBeanµÄÊôÐÔÃû³ÆÒ»Ö£¬ÄÇô`BeanPropertyRowMapper`¾Í¿ÉÒÔÖ±½Ó°ÑÒ»ÐмǼ°´ÁÐÃûת»»ÎªJavaBean¡£
+
+Èç¹ûÎÒÃÇÖ´ÐеIJ»ÊDzéѯ£¬¶øÊDzåÈë¡¢¸üкÍɾ³ý²Ù×÷£¬ÄÇôÐèҪʹÓÃ`update()`·½·¨£º
+
+```
+public void updateUser(User user) {
+    // ´«ÈëSQL£¬SQL²ÎÊý£¬·µ»Ø¸üеÄÐÐÊý:
+    if (1 != jdbcTemplate.update("UPDATE users SET name = ? WHERE id = ?", user.getName(), user.getId())) {
+        throw new RuntimeException("User not found by id");
+    }
+}
+
+```
+
+Ö»ÓÐÒ»ÖÖ`INSERT`²Ù×÷±È½ÏÌØÊ⣬ÄǾÍÊÇÈç¹ûijһÁÐÊÇ×ÔÔöÁУ¨ÀýÈç×ÔÔöÖ÷¼ü£©£¬Í¨³££¬ÎÒÃÇÐèÒª»ñÈ¡²åÈëºóµÄ×ÔÔöÖµ¡£`JdbcTemplate`ÌṩÁËÒ»¸ö`KeyHolder`À´¼ò»¯ÕâÒ»²Ù×÷£º
+
+```
+public User register(String email, String password, String name) {
+    // ´´½¨Ò»¸öKeyHolder:
+    KeyHolder holder = new GeneratedKeyHolder();
+    if (1 != jdbcTemplate.update(
+        // ²ÎÊý1:PreparedStatementCreator
+        (conn) -> {
+            // ´´½¨PreparedStatementʱ£¬±ØÐëÖ¸¶¨RETURN_GENERATED_KEYS:
+            var ps = conn.prepareStatement("INSERT INTO users(email, password, name) VALUES(?, ?, ?)",
+                    Statement.RETURN_GENERATED_KEYS);
+            ps.setObject(1, email);
+            ps.setObject(2, password);
+            ps.setObject(3, name);
+            return ps;
+        },
+        // ²ÎÊý2:KeyHolder
+        holder)
+    ) {
+        throw new RuntimeException("Insert failed.");
+    }
+    // ´ÓKeyHolderÖлñÈ¡·µ»ØµÄ×ÔÔöÖµ:
+    return new User(holder.getKey().longValue(), email, password, name);
+}
+
+```
+
+`JdbcTemplate`»¹ÓÐÐí¶àÖØÔØ·½·¨£¬ÕâÀïÎÒÃDz»Ò»Ò»½éÉÜ¡£ÐèҪǿµ÷µÄÊÇ£¬`JdbcTemplate`Ö»ÊǶÔJDBC²Ù×÷µÄÒ»¸ö¼òµ¥·â×°£¬ËüµÄÄ¿µÄÊǾ¡Á¿¼õÉÙÊÖ¶¯±àд`try(resource) {...}`µÄ´úÂ룬¶ÔÓÚ²éѯ£¬Ö÷Ҫͨ¹ý`RowMapper`ʵÏÖÁËJDBC½á¹û¼¯µ½Java¶ÔÏóµÄת»»¡£
+
+ÎÒÃÇ×ܽáÒ»ÏÂ`JdbcTemplate`µÄÓ÷¨£¬ÄǾÍÊÇ£º
+
+*   Õë¶Ô¼òµ¥²éѯ£¬ÓÅÑ¡`query()`ºÍ`queryForObject()`£¬ÒòΪֻÐèÌṩSQLÓï¾ä¡¢²ÎÊýºÍ`RowMapper`£»
+*   Õë¶Ô¸üвÙ×÷£¬ÓÅÑ¡`update()`£¬ÒòΪֻÐèÌṩSQLÓï¾äºÍ²ÎÊý£»
+*   Èκθ´ÔӵIJÙ×÷£¬×îÖÕÒ²¿ÉÒÔͨ¹ý`execute(ConnectionCallback)`ʵÏÖ£¬ÒòΪÄõ½`Connection`¾Í¿ÉÒÔ×öÈκÎJDBC²Ù×÷¡£
+
+ʵ¼ÊÉÏÎÒÃÇʹÓÃ×î¶àµÄÈÔÈ»ÊǸ÷ÖÖ²éѯ¡£Èç¹ûÔÚÉè¼Æ±í½á¹¹µÄʱºò£¬Äܹ»ºÍJavaBeanµÄÊôÐÔÒ»Ò»¶ÔÓ¦£¬ÄÇôֱ½ÓʹÓÃ`BeanPropertyRowMapper`¾ÍºÜ·½±ã¡£Èç¹û±í½á¹¹ºÍJavaBean²»Ò»ÖÂÔõô°ì£¿ÄǾÍÐèÒªÉÔ΢¸Äдһϲéѯ£¬Ê¹½á¹û¼¯µÄ½á¹¹ºÍJavaBean±£³ÖÒ»Ö¡£
+
+ÀýÈ磬±íµÄÁÐÃûÊÇ`office_address`£¬¶øJavaBeanÊôÐÔÊÇ`workAddress`£¬¾ÍÐèÒªÖ¸¶¨±ðÃû£¬¸Äд²éѯÈçÏ£º
+
+```
+SELECT id, email, office_address AS workAddress, name FROM users WHERE email = ?
+```
+
+ʹÓÃ`JdbcTemplate`µÄʱºò£¬ÎÒÃÇÓõÃ×î¶àµÄ·½·¨¾ÍÊÇ`List query(String, RowMapper, Object...)`¡£Õâ¸ö`RowMapper`µÄ×÷ÓþÍÊǰÑ`ResultSet`µÄÒ»ÐмǼӳÉäΪJava Bean¡£
+
+ÕâÖְѹØÏµÊý¾Ý¿âµÄ±í¼Ç¼ӳÉäΪJava¶ÔÏóµÄ¹ý³Ì¾ÍÊÇORM£ºObject-Relational Mapping¡£ORM¼È¿ÉÒ԰ѼǼת»»³ÉJava¶ÔÏó£¬Ò²¿ÉÒÔ°ÑJava¶ÔÏóת»»ÎªÐмǼ¡£
+
+ʹÓÃ`JdbcTemplate`ÅäºÏ`RowMapper`¿ÉÒÔ¿´×÷ÊÇ×îԭʼµÄORM¡£Èç¹ûҪʵÏÖ¸ü×Ô¶¯»¯µÄORM£¬¿ÉÒÔÑ¡Ôñ³ÉÊìµÄORM¿ò¼Ü£¬ÀýÈç[Hibernate](https://hibernate.org/)¡£
+
+ÎÒÃÇÀ´¿´¿´ÈçºÎÔÚSpringÖм¯³ÉHibernate¡£
+
+Hibernate×÷ΪORM¿ò¼Ü£¬Ëü¿ÉÒÔÌæ´ú`JdbcTemplate`£¬µ«HibernateÈÔÈ»ÐèÒªJDBCÇý¶¯£¬ËùÒÔ£¬ÎÒÃÇÐèÒªÒýÈëJDBCÇý¶¯¡¢Á¬½Ó³Ø£¬ÒÔ¼°Hibernate±¾Éí¡£ÔÚMavenÖУ¬ÎÒÃǼÓÈëÒÔÏÂÒÀÀµÏ
+
+*   org.springframework:spring-context:6.0.0
+*   org.springframework:spring-orm:6.0.0
+*   jakarta.annotation:jakarta.annotation-api:2.1.1
+*   jakarta.persistence:jakarta.persistence-api:3.1.0
+*   org.hibernate:hibernate-core:6.1.4.Final
+*   com.zaxxer:HikariCP:5.0.1
+*   org.hsqldb:hsqldb:2.7.1
+
+ÔÚ`AppConfig`ÖУ¬ÎÒÃÇÈÔÈ»ÐèÒª´´½¨`DataSource`¡¢ÒýÈëJDBCÅäÖÃÎļþ£¬ÒÔ¼°ÆôÓÃÉùÃ÷ʽÊÂÎñ£º
+
+```
+@Configuration
+@ComponentScan
+@EnableTransactionManagement
+@PropertySource("jdbc.properties")
+public class AppConfig {
+    @Bean
+    DataSource createDataSource() {
+        ...
+    }
+}
+
+```
+
+ΪÁËÆôÓÃHibernate£¬ÎÒÃÇÐèÒª´´½¨Ò»¸ö`LocalSessionFactoryBean`£º
+
+```
+public class AppConfig {
+    @Bean
+    LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource) {
+        var props = new Properties();
+        props.setProperty("hibernate.hbm2ddl.auto", "update"); // Éú²ú»·¾³²»ÒªÊ¹ÓÃ
+        props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
+        props.setProperty("hibernate.show_sql", "true");
+        var sessionFactoryBean = new LocalSessionFactoryBean();
+        sessionFactoryBean.setDataSource(dataSource);
+        // ɨÃèÖ¸¶¨µÄpackage»ñÈ¡ËùÓÐentity class:
+        sessionFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
+        sessionFactoryBean.setHibernateProperties(props);
+        return sessionFactoryBean;
+    }
+}
+
+```
+
+×¢ÒâÎÒÃÇÔÚ[¶¨ÖÆBean](https://www.liaoxuefeng.com/wiki/1252599548343744/1308043627200545)Öн²µ½¹ý`FactoryBean`£¬`LocalSessionFactoryBean`ÊÇÒ»¸ö`FactoryBean`£¬Ëü»áÔÙ×Ô¶¯´´½¨Ò»¸ö`SessionFactory`£¬ÔÚHibernateÖУ¬`Session`ÊÇ·â×°ÁËÒ»¸öJDBC `Connection`µÄʵÀý£¬¶ø`SessionFactory`ÊÇ·â×°ÁËJDBC `DataSource`µÄʵÀý£¬¼´`SessionFactory`³ÖÓÐÁ¬½Ó³Ø£¬Ã¿´ÎÐèÒª²Ù×÷Êý¾Ý¿âµÄʱºò£¬`SessionFactory`´´½¨Ò»¸öеÄ`Session`£¬Ï൱ÓÚ´ÓÁ¬½Ó³Ø»ñÈ¡µ½Ò»¸öеÄ`Connection`¡£`SessionFactory`¾ÍÊÇHibernateÌṩµÄ×îºËÐĵÄÒ»¸ö¶ÔÏ󣬵«`LocalSessionFactoryBean`ÊÇSpringÌṩµÄΪÁËÈÃÎÒÃÇ·½±ã´´½¨`SessionFactory`µÄÀà¡£
+
+×¢Òâµ½ÉÏÃæ´´½¨`LocalSessionFactoryBean`µÄ´úÂ룬Ê×ÏÈÓÃ`Properties`³ÖÓÐHibernate³õʼ»¯`SessionFactory`ʱÓõ½µÄËùÓÐÉèÖ㬳£ÓõÄÉèÖÃÇë²Î¿¼[HibernateÎĵµ](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#configurations)£¬ÕâÀïÎÒÃÇÖ»¶¨ÒåÁË3¸öÉèÖãº
+
+*   `hibernate.hbm2ddl.auto=update`£º±íʾ×Ô¶¯´´½¨Êý¾Ý¿âµÄ±í½á¹¹£¬×¢Òâ²»ÒªÔÚÉú²ú»·¾³ÖÐÆôÓã»
+*   `hibernate.dialect=org.hibernate.dialect.HSQLDialect`£ºÖ¸Ê¾HibernateʹÓõÄÊý¾Ý¿âÊÇHSQLDB¡£HibernateʹÓÃÒ»ÖÖHQLµÄ²éѯÓï¾ä£¬ËüºÍSQLÀàËÆ£¬µ«ÕæÕýÔÚ¡°·­Ò롱³ÉSQLʱ£¬»á¸ù¾ÝÉ趨µÄÊý¾Ý¿â¡°·½ÑÔ¡±À´Éú³ÉÕë¶ÔÊý¾Ý¿âÓÅ»¯µÄSQL£»
+*   `hibernate.show_sql=true`£ºÈÃHibernate´òÓ¡Ö´ÐеÄSQL£¬Õâ¶ÔÓÚµ÷ÊԷdz£ÓÐÓã¬ÎÒÃÇ¿ÉÒÔ·½±ãµØ¿´µ½HibernateÉú³ÉµÄSQLÓï¾äÊÇ·ñ·ûºÏÎÒÃǵÄÔ¤ÆÚ¡£
+
+³ýÁËÉèÖÃ`DataSource`ºÍ`Properties`Ö®Í⣬עÒâµ½`setPackagesToScan()`ÎÒÃÇ´«ÈëÁËÒ»¸ö`package`Ãû³Æ£¬ËüָʾHibernateɨÃèÕâ¸ö°üÏÂÃæµÄËùÓÐJavaÀ࣬×Ô¶¯ÕÒ³öÄÜÓ³ÉäΪÊý¾Ý¿â±í¼Ç¼µÄJavaBean¡£ºóÃæÎÒÃÇ»á×ÐϸÌÖÂÛÈçºÎ±àд·ûºÏHibernateÒªÇóµÄJavaBean¡£
+
+½ô½Ó×Å£¬ÎÒÃÇ»¹ÐèÒª´´½¨`HibernateTransactionManager`£º
+
+```
+public class AppConfig {
+    @Bean
+    PlatformTransactionManager createTxManager(@Autowired SessionFactory sessionFactory) {
+        return new HibernateTransactionManager(sessionFactory);
+    }
+}
+
+```
+
+`HibernateTransactionManager`ÊÇÅäºÏHibernateʹÓÃÉùÃ÷ʽÊÂÎñËù±ØÐëµÄ¡£µ½´ËΪֹ£¬ËùÓеÄÅäÖö¼¶¨ÒåÍê±Ï£¬ÎÒÃÇÀ´¿´¿´ÈçºÎ½«Êý¾Ý¿â±í½á¹¹Ó³ÉäΪJava¶ÔÏó¡£
+
+¿¼²ìÈçϵÄÊý¾Ý¿â±í£º
+
+```
+CREATE TABLE user
+    id BIGINT NOT NULL AUTO_INCREMENT,
+    email VARCHAR(100) NOT NULL,
+    password VARCHAR(100) NOT NULL,
+    name VARCHAR(100) NOT NULL,
+    createdAt BIGINT NOT NULL,
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `email` (`email`)
+);
+
+```
+
+ÆäÖУ¬`id`ÊÇ×ÔÔöÖ÷¼ü£¬`email`¡¢`password`¡¢`name`ÊÇ`VARCHAR`ÀàÐÍ£¬`email`´øÎ¨Ò»Ë÷ÒýÒÔÈ·±£Î¨Ò»ÐÔ£¬`createdAt`´æ´¢ÕûÐÍÀàÐ͵Äʱ¼ä´Á¡£ÓÃJavaBean±íʾÈçÏ£º
+
+```
+public class User {
+    private Long id;
+    private String email;
+    private String password;
+    private String name;
+    private Long createdAt;
+
+    // getters and setters
+    ...
+}
+
+```
+
+ÕâÖÖÓ³Éä¹ØÏµÊ®·ÖÒ×¶®£¬µ«ÎÒÃÇÐèÒªÌí¼ÓһЩע½âÀ´¸æËßHibernateÈçºÎ°Ñ`User`ÀàÓ³Éäµ½±í¼Ç¼£º
+
+```
+@Entity
+public class User {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(nullable = false, updatable = false)
+    public Long getId() { ... }
+
+    @Column(nullable = false, unique = true, length = 100)
+    public String getEmail() { ... }
+
+    @Column(nullable = false, length = 100)
+    public String getPassword() { ... }
+
+    @Column(nullable = false, length = 100)
+    public String getName() { ... }
+
+    @Column(nullable = false, updatable = false)
+    public Long getCreatedAt() { ... }
+}
+
+```
+
+Èç¹ûÒ»¸öJavaBean±»ÓÃÓÚÓ³É䣬ÎÒÃǾͱê¼ÇÒ»¸ö`@Entity`¡£Ä¬ÈÏÇé¿öÏ£¬Ó³ÉäµÄ±íÃûÊÇ`user`£¬Èç¹ûʵ¼ÊµÄ±íÃû²»Í¬£¬ÀýÈçʵ¼Ê±íÃûÊÇ`users`£¬¿ÉÒÔ×·¼ÓÒ»¸ö`@Table(name="users")`±íʾ£º
+
+```
+@Entity
+@Table(name="users)
+public class User {
+    ...
+}
+
+```
+
+ÿ¸öÊôÐÔµ½Êý¾Ý¿âÁеÄÓ³ÉäÓÃ`@Column()`±êʶ£¬`nullable`ָʾÁÐÊÇ·ñÔÊÐíΪ`NULL`£¬`updatable`ָʾ¸ÃÁÐÊÇ·ñÔÊÐí±»ÓÃÔÚ`UPDATE`Óï¾ä£¬`length`ָʾ`String`ÀàÐ͵ÄÁеij¤¶È£¨Èç¹ûûÓÐÖ¸¶¨£¬Ä¬ÈÏÊÇ`255`£©¡£
+
+¶ÔÓÚÖ÷¼ü£¬»¹ÐèÒªÓÃ`@Id`±êʶ£¬×ÔÔöÖ÷¼üÔÙ×·¼ÓÒ»¸ö`@GeneratedValue`£¬ÒÔ±ãHibernateÄܶÁÈ¡µ½×ÔÔöÖ÷¼üµÄÖµ¡£
+
+ϸÐĵÄͯЬ¿ÉÄÜ»¹×¢Òâµ½£¬Ö÷¼ü`id`¶¨ÒåµÄÀàÐͲ»ÊÇ`long`£¬¶øÊÇ`Long`¡£ÕâÊÇÒòΪHibernateÈç¹û¼ì²âµ½Ö÷¼üΪ`null`£¬¾Í²»»áÔÚ`INSERT`Óï¾äÖÐÖ¸¶¨Ö÷¼üµÄÖµ£¬¶øÊÇ·µ»ØÓÉÊý¾Ý¿âÉú³ÉµÄ×ÔÔöÖµ£¬·ñÔò£¬HibernateÈÏΪÎÒÃǵijÌÐòÖ¸¶¨ÁËÖ÷¼üµÄÖµ£¬»áÔÚ`INSERT`Óï¾äÖÐÖ±½ÓÁгö¡£`long`ÐÍ×Ö¶Î×ÜÊǾßÓÐĬÈÏÖµ`0`£¬Òò´Ë£¬Ã¿´Î²åÈëµÄÖ÷¼üÖµ×ÜÊÇ0£¬µ¼Ö³ýµÚÒ»´ÎÍâºóÐø²åÈë¶¼½«Ê§°Ü¡£
+
+`createdAt`ËäÈ»ÊÇÕûÐÍ£¬µ«ÎÒÃDz¢Ã»ÓÐʹÓÃ`long`£¬¶øÊÇ`Long`£¬ÕâÊÇÒòΪʹÓûù±¾ÀàÐͻᵼÖÂfindByExample²éѯ»áÌí¼ÓÒâÍâµÄÌõ¼þ£¬ÕâÀïÖ»ÐèÀμǣ¬×÷ΪӳÉäʹÓõÄJavaBean£¬ËùÓÐÊôÐÔ¶¼Ê¹Óðü×°ÀàÐͶø²»ÊÇ»ù±¾ÀàÐÍ¡£
+
+ʹÓÃHibernateʱ£¬²»ÒªÊ¹Óûù±¾ÀàÐ͵ÄÊôÐÔ£¬×ÜÊÇʹÓðü×°ÀàÐÍ£¬ÈçLong»òInteger¡£
+
+ÀàËÆµÄ£¬ÎÒÃÇÔÙ¶¨ÒåÒ»¸ö`Book`Àࣺ
+
+```
+@Entity
+public class Book {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(nullable = false, updatable = false)
+    public Long getId() { ... }
+
+    @Column(nullable = false, length = 100)
+    public String getTitle() { ... }
+
+    @Column(nullable = false, updatable = false)
+    public Long getCreatedAt() { ... }
+}
+
+```
+
+Èç¹û×Ðϸ¹Û²ì`User`ºÍ`Book`£¬»á·¢ÏÖËüÃǶ¨ÒåµÄ`id`¡¢`createdAt`ÊôÐÔÊÇÒ»ÑùµÄ£¬ÕâÔÚÊý¾Ý¿â±í½á¹¹µÄÉè¼ÆÖкܳ£¼û£º¶ÔÓÚÿ¸ö±í£¬Í¨³£ÎÒÃÇ»áͳһʹÓÃÒ»ÖÖÖ÷¼üÉú³É»úÖÆ£¬²¢Ìí¼Ó`createdAt`±íʾ´´½¨Ê±¼ä£¬`updatedAt`±íʾÐÞ¸Äʱ¼äµÈͨÓÃ×ֶΡ£
+
+²»±ØÔÚ`User`ºÍ`Book`ÖÐÖØ¸´¶¨ÒåÕâЩͨÓÃ×ֶΣ¬ÎÒÃÇ¿ÉÒÔ°ÑËüÃÇÌáµ½Ò»¸ö³éÏóÀàÖУº
+
+```
+@MappedSuperclass
+public abstract class AbstractEntity {
+
+    private Long id;
+    private Long createdAt;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(nullable = false, updatable = false)
+    public Long getId() { ... }
+
+    @Column(nullable = false, updatable = false)
+    public Long getCreatedAt() { ... }
+
+    @Transient
+    public ZonedDateTime getCreatedDateTime() {
+        return Instant.ofEpochMilli(this.createdAt).atZone(ZoneId.systemDefault());
+    }
+
+    @PrePersist
+    public void preInsert() {
+        setCreatedAt(System.currentTimeMillis());
+    }
+}
+
+```
+
+¶ÔÓÚ`AbstractEntity`À´Ëµ£¬ÎÒÃÇÒª±ê×¢Ò»¸ö`@MappedSuperclass`±íʾËüÓÃÓڼ̳С£´ËÍ⣬עÒâµ½ÎÒÃǶ¨ÒåÁËÒ»¸ö`@Transient`·½·¨£¬Ëü·µ»ØÒ»¸ö¡°ÐéÄ⡱µÄÊôÐÔ¡£ÒòΪ`getCreatedDateTime()`ÊǼÆËãµÃ³öµÄÊôÐÔ£¬¶ø²»ÊÇ´ÓÊý¾Ý¿â±í¶Á³öµÄÖµ£¬Òò´Ë±ØÐëÒª±ê×¢`@Transient`£¬·ñÔòHibernate»á³¢ÊÔ´ÓÊý¾Ý¿â¶ÁÈ¡ÃûΪ`createdDateTime`Õâ¸ö²»´æÔÚµÄ×ֶδӶø³ö´í¡£
+
+ÔÙ×¢Òâµ½`@PrePersist`±êʶµÄ·½·¨£¬Ëü±íʾÔÚÎÒÃǽ«Ò»¸öJavaBean³Ö¾Ã»¯µ½Êý¾Ý¿â֮ǰ£¨¼´Ö´ÐÐINSERTÓï¾ä£©£¬Hibernate»áÏÈÖ´Ðи÷½·¨£¬ÕâÑùÎÒÃǾͿÉÒÔ×Ô¶¯ÉèÖúÃ`createdAt`ÊôÐÔ¡£
+
+ÓÐÁË`AbstractEntity`£¬ÎÒÃǾͿÉÒÔ´ó·ù¼ò»¯`User`ºÍ`Book`£º
+
+```
+@Entity
+public class User extends AbstractEntity {
+
+    @Column(nullable = false, unique = true, length = 100)
+    public String getEmail() { ... }
+
+    @Column(nullable = false, length = 100)
+    public String getPassword() { ... }
+
+    @Column(nullable = false, length = 100)
+    public String getName() { ... }
+}
+
+```
+
+×¢Ò⵽ʹÓõÄËùÓÐ×¢½â¾ùÀ´×Ô`jakarta.persistence`£¬ËüÊÇJPA¹æ·¶µÄÒ»²¿·Ö¡£ÕâÀïÎÒÃÇÖ»½éÉÜʹÓÃ×¢½âµÄ·½Ê½ÅäÖÃHibernateÓ³Éä¹ØÏµ£¬²»ÔÙ½éÉÜ´«Í³µÄ±È½Ï·±ËöµÄXMLÅäÖá£Í¨¹ýSpring¼¯³ÉHibernateʱ£¬Ò²²»ÔÙÐèÒª`hibernate.cfg.xml`ÅäÖÃÎļþ£¬ÓÃÒ»¾ä»°×ܽ᣺
+
+ʹÓÃSpring¼¯³ÉHibernate£¬ÅäºÏJPA×¢½â£¬ÎÞÐèÈκζîÍâµÄXMLÅäÖá£
+
+ÀàËÆ`User`¡¢`Book`ÕâÑùµÄÓÃÓÚORMµÄJava Bean£¬ÎÒÃÇͨ³£³ÆÖ®ÎªEntity Bean¡£
+
+×îºó£¬ÎÒÃÇÀ´¿´¿´Èç¹û¶Ô`user`±í½øÐÐÔöɾ¸Ä²é¡£ÒòΪʹÓÃÁËHibernate£¬Òò´Ë£¬ÎÒÃÇÒª×öµÄ£¬Êµ¼ÊÉÏÊǶÔ`User`Õâ¸öJavaBean½øÐС°Ôöɾ¸Ä²é¡±¡£ÎÒÃDZàдһ¸ö`UserService`£¬×¢Èë`SessionFactory`£º
+
+```
+@Component
+@Transactional
+public class UserService {
+    @Autowired
+    SessionFactory sessionFactory;
+}
+
+```
+
+### Insert²Ù×÷
+
+Òª³Ö¾Ã»¯Ò»¸ö`User`ʵÀý£¬ÎÒÃÇÖ»Ðèµ÷ÓÃ`persist()`·½·¨¡£ÒÔ`register()`·½·¨ÎªÀý£¬´úÂëÈçÏ£º
+
+```
+public User register(String email, String password, String name) {
+    // ´´½¨Ò»¸öUser¶ÔÏó:
+    User user = new User();
+    // ÉèÖúø÷¸öÊôÐÔ:
+    user.setEmail(email);
+    user.setPassword(password);
+    user.setName(name);
+    // ²»ÒªÉèÖÃid£¬ÒòΪʹÓÃÁË×ÔÔöÖ÷¼ü
+    // ±£´æµ½Êý¾Ý¿â:
+    sessionFactory.getCurrentSession().persist(user);
+    // ÏÖÔÚÒѾ­×Ô¶¯»ñµÃÁËid:
+    System.out.println(user.getId());
+    return user;
+}
+
+```
+
+### Delete²Ù×÷
+
+ɾ³ýÒ»¸ö`User`Ï൱ÓÚ´Ó±íÖÐɾ³ý¶ÔÓ¦µÄ¼Ç¼¡£×¢ÒâHibernate×ÜÊÇÓÃ`id`À´É¾³ý¼Ç¼£¬Òò´Ë£¬ÒªÕýÈ·ÉèÖÃ`User`µÄ`id`ÊôÐÔ²ÅÄÜÕý³£É¾³ý¼Ç¼£º
+
+```
+public boolean deleteUser(Long id) {
+    User user = sessionFactory.getCurrentSession().byId(User.class).load(id);
+    if (user != null) {
+        sessionFactory.getCurrentSession().remove(user);
+        return true;
+    }
+    return false;
+}
+
+```
+
+ͨ¹ýÖ÷¼üɾ³ý¼Ç¼ʱ£¬Ò»¸ö³£¼ûµÄÓ÷¨ÊÇÏȸù¾ÝÖ÷¼ü¼ÓÔØ¸Ã¼Ç¼£¬ÔÙɾ³ý¡£×¢Òâµ½µ±¼Ç¼²»´æÔÚʱ£¬`load()`·µ»Ø`null`¡£
+
+### Update²Ù×÷
+
+¸üмǼÏ൱ÓÚÏȸüÐÂ`User`µÄÖ¸¶¨ÊôÐÔ£¬È»ºóµ÷ÓÃ`merge()`·½·¨£º
+
+```
+public void updateUser(Long id, String name) {
+    User user = sessionFactory.getCurrentSession().byId(User.class).load(id);
+    user.setName(name);
+    sessionFactory.getCurrentSession().merge(user);
+}
+
+```
+
+Ç°ÃæÎÒÃÇÔÚ¶¨Òå`User`ʱ£¬¶ÔÓеÄÊôÐÔ±ê×¢ÁË`@Column(updatable=false)`¡£HibernateÔÚ¸üмǼʱ£¬ËüÖ»»á°Ñ`@Column(updatable=true)`µÄÊôÐÔ¼ÓÈëµ½`UPDATE`Óï¾äÖУ¬ÕâÑù¿ÉÒÔÌṩһ²ã¶îÍâµÄ°²È«ÐÔ£¬¼´Èç¹û²»Ð¡ÐÄÐÞ¸ÄÁË`User`µÄ`email`¡¢`createdAt`µÈÊôÐÔ£¬Ö´ÐÐ`update()`ʱ²¢²»»á¸üжÔÓ¦µÄÊý¾Ý¿âÁС£µ«Ò²±ØÐëÀμǣºÕâ¸ö¹¦ÄÜÊÇHibernateÌṩµÄ£¬Èç¹ûÈÆ¹ýHibernateÖ±½Óͨ¹ýJDBCÖ´ÐÐ`UPDATE`Óï¾äÈÔÈ»¿ÉÒÔ¸üÐÂÊý¾Ý¿âµÄÈÎÒâÁеÄÖµ¡£
+
+×îºó£¬ÎÒÃDZàдµÄ´ó²¿·Ö·½·¨¶¼ÊǸ÷ÖÖ¸÷ÑùµÄ²éѯ¡£¸ù¾Ý`id`²éѯÎÒÃÇ¿ÉÒÔÖ±½Óµ÷ÓÃ`load()`£¬Èç¹ûҪʹÓÃÌõ¼þ²éѯ£¬ÀýÈ磬¼ÙÉèÎÒÃÇÏëÖ´ÐÐÒÔϲéѯ£º
+
+```
+SELECT * FROM user WHERE email = ? AND password = ?
+
+```
+
+ÎÒÃÇÀ´¿´¿´¿ÉÒÔʹÓÃʲô²éѯ¡£
+
+### ʹÓÃHQL²éѯ
+
+Ò»ÖÖ³£ÓõIJéѯÊÇÖ±½Ó±àдHibernateÄÚÖõÄHQL²éѯ£º
+
+```
+List list = sessionFactory.getCurrentSession()
+        .createQuery("from User u where u.email = ?1 and u.password = ?2", User.class)
+        .setParameter(1, email).setParameter(2, password)
+        .list();
+
+```
+
+ºÍSQLÏà±È£¬HQLʹÓÃÀàÃûºÍÊôÐÔÃû£¬ÓÉHibernate×Ô¶¯×ª»»ÎªÊµ¼ÊµÄ±íÃûºÍÁÐÃû¡£ÏêϸµÄHQLÓï·¨¿ÉÒԲο¼[HibernateÎĵµ](https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#query-language)¡£
+
+³ýÁË¿ÉÒÔÖ±½Ó´«ÈëHQL×Ö·û´®Í⣬Hibernate»¹¿ÉÒÔʹÓÃÒ»ÖÖ`NamedQuery`£¬Ëü¸ø²éѯÆð¸öÃû×Ö£¬È»ºó±£´æÔÚ×¢½âÖС£Ê¹ÓÃ`NamedQuery`ʱ£¬ÎÒÃÇÒªÏÈÔÚ`User`Àà±ê×¢£º
+
+```
+@NamedQueries(
+    @NamedQuery(
+        // ²éѯÃû³Æ:
+        name = "login",
+        // ²éѯÓï¾ä:
+        query = "SELECT u FROM User u WHERE u.email = :e AND u.password = :pwd"
+    )
+)
+@Entity
+public class User extends AbstractEntity {
+    ...
+}
+
+```
+
+×¢Òâµ½ÒýÈëµÄ`NamedQuery`ÊÇ`jakarta.persistence.NamedQuery`£¬ËüºÍÖ±½Ó´«ÈëHQLÓе㲻ͬµÄÊÇ£¬Õ¼Î»·ûʹÓÃ`:e`ºÍ`:pwd`¡£
+
+ʹÓÃ`NamedQuery`Ö»ÐèÒªÒýÈë²éѯÃûºÍ²ÎÊý£º
+
+```
+public User login(String email, String password) {
+    List list = sessionFactory.getCurrentSession()
+        .createNamedQuery("login", User.class) // ´´½¨NamedQuery
+        .setParameter("e", email) // °ó¶¨e²ÎÊý
+        .setParameter("pwd", password) // °ó¶¨pwd²ÎÊý
+        .list();
+    return list.isEmpty() ? null : list.get(0);
+}
+
+```
+
+Ö±½ÓдHQLºÍʹÓÃ`NamedQuery`¸÷ÓÐÓÅÁÓ¡£Ç°Õß¿ÉÒÔÔÚ´úÂëÖÐÖ±¹ÛµØ¿´µ½²éѯÓï¾ä£¬ºóÕß¿ÉÒÔÔÚ`User`Ààͳһ¹ÜÀíËùÓÐÏà¹Ø²éѯ¡£
+
+ÉÏÒ»½ÚÎÒÃǽ²ÁËÔÚSpringÖм¯³ÉHibernate¡£HibernateÊǵÚÒ»¸ö±»¹ã·ºÊ¹ÓõÄORM¿ò¼Ü£¬µ«ÊǺܶàС»ï°é»¹Ìý˵¹ýJPA£ºJava Persistence API£¬ÕâÓÖÊÇɶ£¿
+
+ÔÚÌÖÂÛJPA֮ǰ£¬ÎÒÃÇҪעÒâµ½JavaEEÔçÔÚ1999Äê¾Í·¢²¼ÁË£¬²¢ÇÒÓÐServlet¡¢JMSµÈÖî¶à±ê×¼¡£ºÍÆäËûƽ̨²»Í¬£¬JavaÊÀ½çÔçÆÚ·Ç³£ÈÈÖÔÓÚ±ê×¼ÏÈÐУ¬¸÷¼Ò¸ú½ø£º´ó¼ÒÏÈ×øÏÂÀ´°Ñ½Ó¿Ú¶¨ÁË£¬È»ºó£¬¸÷×ԻؼҸɻîȥʵÏÖ½Ó¿Ú£¬ÕâÑù£¬Óû§¾Í¿ÉÒÔÔÚ²»Í¬µÄ³§¼ÒÌṩµÄ²úÆ·½øÐÐÑ¡Ôñ£¬»¹¿ÉÒÔËæÒâÇл»£¬ÒòΪÓû§±àд´úÂëµÄʱºòÖ»ÐèÒªÒýÓýӿڣ¬²¢²»ÐèÒªÒýÓþßÌåµÄµ×²ãʵÏÖ£¨ÏëÏëJDBC£©¡£
+
+JPA¾ÍÊÇJavaEEµÄÒ»¸öORM±ê×¼£¬ËüµÄʵÏÖÆäʵºÍHibernateûɶ±¾ÖÊÇø±ð£¬µ«ÊÇÓû§Èç¹ûʹÓÃJPA£¬ÄÇôÒýÓõľÍÊÇ`jakarta.persistence`Õâ¸ö¡°±ê×¼¡±°ü£¬¶ø²»ÊÇ`org.hibernate`ÕâÑùµÄµÚÈý·½°ü¡£ÒòΪJPAÖ»Êǽӿڣ¬ËùÒÔ£¬»¹ÐèҪѡÔñÒ»¸öʵÏÖ²úÆ·£¬¸úJDBC½Ó¿ÚºÍMySQLÇý¶¯Ò»¸öµÀÀí¡£
+
+ÎÒÃÇʹÓÃJPAʱҲÍêÈ«¿ÉÒÔÑ¡ÔñHibernate×÷Ϊµ×²ãʵÏÖ£¬µ«Ò²¿ÉÒÔÑ¡ÔñÆäËüµÄJPAÌṩ·½£¬±ÈÈç[EclipseLink](https://www.eclipse.org/eclipselink/)¡£SpringÄÚÖÃÁËJPAµÄ¼¯³É£¬²¢Ö§³ÖÑ¡ÔñHibernate»òEclipseLink×÷ΪʵÏÖ¡£ÕâÀïÎÒÃÇÈÔÈ»ÒÔÖ÷Á÷µÄHibernate×÷ΪJPAʵÏÖΪÀý×Ó£¬ÑÝʾJPAµÄ»ù±¾Ó÷¨¡£
+
+ºÍʹÓÃHibernateÒ»Ñù£¬ÎÒÃÇÖ»ÐèÒªÒýÈëÈçÏÂÒÀÀµ£º
+
+*   org.springframework:spring-context:6.0.0
+*   org.springframework:spring-orm:6.0.0
+*   jakarta.annotation:jakarta.annotation-api:2.1.1
+*   jakarta.persistence:jakarta.persistence-api:3.1.0
+*   org.hibernate:hibernate-core:6.1.4.Final
+*   com.zaxxer:HikariCP:5.0.1
+*   org.hsqldb:hsqldb:2.7.1
+
+ʵ¼ÊÉÏÎÒÃÇÕâÀïÒýÈëµÄÒÀÀµºÍÉÏÒ»½Ú¼¯³ÉHibernateÒýÈëµÄÒÀÀµÍêȫһÑù£¬ÒòΪHibernate¼ÈÌṩÁËËü×Ô¼ºµÄ½Ó¿Ú£¬Ò²ÌṩÁËJPA½Ó¿Ú£¬ÎÒÃÇÓÃJPA½Ó¿Ú¾ÍÏ൱ÓÚͨ¹ýJPA²Ù×÷Hibernate¡£
+
+È»ºó£¬ÔÚ`AppConfig`ÖÐÆôÓÃÉùÃ÷ʽÊÂÎñ¹ÜÀí£¬´´½¨`DataSource`£º
+
+```
+@Configuration
+@ComponentScan
+@EnableTransactionManagement
+@PropertySource("jdbc.properties")
+public class AppConfig {
+    @Bean
+    DataSource createDataSource() { ... }
+}
+
+```
+
+ʹÓÃHibernateʱ£¬ÎÒÃÇÐèÒª´´½¨Ò»¸ö`LocalSessionFactoryBean`£¬²¢ÈÃËüÔÙ×Ô¶¯´´½¨Ò»¸ö`SessionFactory`¡£Ê¹ÓÃJPAÒ²ÊÇÀàËÆµÄ£¬ÎÒÃÇÒ²´´½¨Ò»¸ö`LocalContainerEntityManagerFactoryBean`£¬²¢ÈÃËüÔÙ×Ô¶¯´´½¨Ò»¸ö`EntityManagerFactory`£º
+
+```
+@Bean
+public LocalContainerEntityManagerFactoryBean createEntityManagerFactory(@Autowired DataSource dataSource) {
+    var emFactory = new LocalContainerEntityManagerFactoryBean();
+    // ×¢ÈëDataSource:
+    emFactory.setDataSource(dataSource);
+    // ɨÃèÖ¸¶¨µÄpackage»ñÈ¡ËùÓÐentity class:
+    emFactory.setPackagesToScan(AbstractEntity.class.getPackageName());
+    // ʹÓÃHibernate×÷ΪJPAʵÏÖ:
+    emFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
+    // ÆäËûÅäÖÃÏî:
+    var props = new Properties();
+    props.setProperty("hibernate.hbm2ddl.auto", "update"); // Éú²ú»·¾³²»ÒªÊ¹ÓÃ
+    props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
+    props.setProperty("hibernate.show_sql", "true");
+    emFactory.setJpaProperties(props);
+    return emFactory;
+}
+
+```
+
+¹Û²ìÉÏÊö´úÂ룬³ýÁËÐèҪעÈë`DataSource`ºÍÉ趨×Ô¶¯É¨ÃèµÄ`package`Í⣬»¹ÐèÒªÖ¸¶¨JPAµÄÌṩÉÌ£¬ÕâÀïʹÓÃSpringÌṩµÄÒ»¸ö`HibernateJpaVendorAdapter`£¬×îºó£¬Õë¶ÔHibernate×Ô¼ºÐèÒªµÄÅäÖã¬ÒÔ`Properties`µÄÐÎʽעÈë¡£
+
+×îºó£¬ÎÒÃÇ»¹ÐèҪʵÀý»¯Ò»¸ö`JpaTransactionManager`£¬ÒÔʵÏÖÉùÃ÷ʽÊÂÎñ£º
+
+```
+@Bean
+PlatformTransactionManager createTxManager(@Autowired EntityManagerFactory entityManagerFactory) {
+    return new JpaTransactionManager(entityManagerFactory);
+}
+
+```
+
+ÕâÑù£¬ÎÒÃǾÍÍê³ÉÁËJPAµÄÈ«²¿³õʼ»¯¹¤×÷¡£ÓÐЩͯЬ¿ÉÄÜ´ÓÍøÉÏËÑË÷µÃÖªJPAÐèÒª`persistence.xml`ÅäÖÃÎļþ£¬ÒÔ¼°¸´ÔÓµÄ`orm.xml`Îļþ¡£ÕâÀïÎÒÃǸºÔðµØ¸æËß´ó¼Ò£¬Ê¹ÓÃSpring+Hibernate×÷ΪJPAʵÏÖ£¬ÎÞÐèÈκÎÅäÖÃÎļþ¡£
+
+ËùÓÐEntity BeanµÄÅäÖúÍÉÏÒ»½ÚÍêÈ«Ïàͬ£¬È«²¿²ÉÓÃAnnotation±ê×¢¡£ÎÒÃÇÏÖÔÚÖ»Ðè¹ØÐľßÌåµÄÒµÎñÀàÈçºÎͨ¹ýJPA½Ó¿Ú²Ù×÷Êý¾Ý¿â¡£
+
+»¹ÊÇÒÔ`UserService`ΪÀý£¬³ýÁ˱ê×¢`@Component`ºÍ`@Transactional`Í⣬ÎÒÃÇÐèҪעÈëÒ»¸ö`EntityManager`£¬µ«ÊDz»ÒªÊ¹ÓÃ`Autowired`£¬¶øÊÇ`@PersistenceContext`£º
+
+```
+@Component
+@Transactional
+public class UserService {
+    @PersistenceContext
+    EntityManager em;
+}
+
+```
+
+ÎÒÃǻعËÒ»ÏÂJDBC¡¢HibernateºÍJPAÌṩµÄ½Ó¿Ú£¬Êµ¼ÊÉÏ£¬ËüÃǵĹØÏµÈçÏ£º
+
+| JDBC | Hibernate | JPA |
+| --- | --- | --- |
+| DataSource | SessionFactory | EntityManagerFactory |
+| Connection | Session | EntityManager |
+
+`SessionFactory`ºÍ`EntityManagerFactory`Ï൱ÓÚ`DataSource`£¬`Session`ºÍ`EntityManager`Ï൱ÓÚ`Connection`¡£Ã¿´ÎÐèÒª·ÃÎÊÊý¾Ý¿âµÄʱºò£¬ÐèÒª»ñȡеÄ`Session`ºÍ`EntityManager`£¬ÓÃÍêºóÔٹرա£
+
+µ«ÊÇ£¬×¢Òâµ½`UserService`×¢ÈëµÄ²»ÊÇ`EntityManagerFactory`£¬¶øÊÇ`EntityManager`£¬²¢ÇÒ±ê×¢ÁË`@PersistenceContext`¡£ÄѵÀʹÓÃJPA¿ÉÒÔÔÊÐí¶àÏ̲߳Ù×÷ͬһ¸ö`EntityManager`£¿
+
+ʵ¼ÊÉÏÕâÀï×¢ÈëµÄ²¢²»ÊÇÕæÕýµÄ`EntityManager`£¬¶øÊÇÒ»¸ö`EntityManager`µÄ´úÀíÀ࣬Ï൱ÓÚ£º
+
+```
+public class EntityManagerProxy implements EntityManager {
+    private EntityManagerFactory emf;
+}
+
+```
+
+SpringÓöµ½±ê×¢ÁË`@PersistenceContext`µÄ`EntityManager`»á×Ô¶¯×¢Èë´úÀí£¬¸Ã´úÀí»áÔÚ±ØÒªµÄʱºò×Ô¶¯´ò¿ª`EntityManager`¡£»»¾ä»°Ëµ£¬¶àÏß³ÌÒýÓõÄ`EntityManager`ËäÈ»ÊÇͬһ¸ö´úÀíÀ࣬µ«¸Ã´úÀíÀàÄÚ²¿Õë¶Ô²»Í¬Ï̻߳ᴴ½¨²»Í¬µÄ`EntityManager`ʵÀý¡£
+
+¼òµ¥×ܽáһϣ¬±ê×¢ÁË`@PersistenceContext`µÄ`EntityManager`¿ÉÒÔ±»¶àḬ̈߳²È«µØ¹²Ïí¡£
+
+Òò´Ë£¬ÔÚ`UserService`µÄÿ¸öÒµÎñ·½·¨Àֱ½ÓʹÓÃ`EntityManager`¾ÍºÜ·½±ã¡£ÒÔÖ÷¼ü²éѯΪÀý£º
+
+```
+public User getUserById(long id) {
+    User user = this.em.find(User.class, id);
+    if (user == null) {
+        throw new RuntimeException("User not found by id: " + id);
+    }
+    return user;
+}
+
+```
+
+ÓëHQL²éѯÀàËÆ£¬JPAʹÓÃJPQL²éѯ£¬ËüµÄÓï·¨ºÍHQL»ù±¾²î²»¶à£º
+
+```
+public User fetchUserByEmail(String email) {
+    // JPQL²éѯ:
+    TypedQuery query = em.createQuery("SELECT u FROM User u WHERE u.email = :e", User.class);
+    query.setParameter("e", email);
+    List list = query.getResultList();
+    if (list.isEmpty()) {
+        return null;
+    }
+    return list.get(0);
+}
+
+```
+
+ͬÑùµÄ£¬JPAÒ²Ö§³Ö`NamedQuery`£¬¼´Ïȸø²éѯÆð¸öÃû×Ö£¬ÔÙ°´Ãû×Ö´´½¨²éѯ£º
+
+```
+public User login(String email, String password) {
+    TypedQuery query = em.createNamedQuery("login", User.class);
+    query.setParameter("e", email);
+    query.setParameter("pwd", password);
+    List list = query.getResultList();
+    return list.isEmpty() ? null : list.get(0);
+}
+
+```
+
+`NamedQuery`ͨ¹ý×¢½â±ê×¢ÔÚ`User`ÀàÉÏ£¬ËüµÄ¶¨ÒåºÍÉÏÒ»½ÚµÄ`User`ÀàÒ»Ñù£º
+
+```
+@NamedQueries(
+    @NamedQuery(
+        name = "login",
+        query = "SELECT u FROM User u WHERE u.email=:e AND u.password=:pwd"
+    )
+)
+@Entity
+public class User {
+    ...
+}
+
+```
+
+¶ÔÊý¾Ý¿â½øÐÐÔöɾ¸ÄµÄ²Ù×÷£¬¿ÉÒÔ·Ö±ðʹÓÃ`persist()`¡¢`remove()`ºÍ`merge()`·½·¨£¬²ÎÊý¾ùΪEntity Bean±¾Éí£¬Ê¹Ó÷dz£¼òµ¥£¬ÕâÀï²»ÔÙ¶àÊö¡£
+
+#### ¼¯³ÉMyBatis
+
+×îºó¸üÐÂ: 2022/11/16 21:07 / ÔĶÁ: 601258
+
+* * *
+
+
+
+ʹÓÃHibernate»òJPA²Ù×÷Êý¾Ý¿âʱ£¬ÕâÀàORM¸ÉµÄÖ÷Òª¹¤×÷¾ÍÊǰÑResultSetµÄÿһÐбä³ÉJava Bean£¬»òÕß°ÑJava Bean×Ô¶¯×ª»»µ½INSERT»òUPDATEÓï¾äµÄ²ÎÊýÖУ¬´Ó¶øÊµÏÖORM¡£
+
+¶øORM¿ò¼ÜÖ®ËùÒÔÖªµÀÈçºÎ°ÑÐÐÊý¾ÝÓ³Éäµ½Java Bean£¬ÊÇÒòΪÎÒÃÇÔÚJava BeanµÄÊôÐÔÉϸøÁË×ã¹»µÄ×¢½â×÷ΪԪÊý¾Ý£¬ORM¿ò¼Ü»ñÈ¡Java BeanµÄ×¢½âºó£¬¾ÍÖªµÀÈçºÎ½øÐÐË«ÏòÓ³Éä¡£
+
+ÄÇô£¬ORM¿ò¼ÜÊÇÈçºÎ¸ú×ÙJava BeanµÄÐ޸ģ¬ÒÔ±ãÔÚ`update()`²Ù×÷ÖиüбØÒªµÄÊôÐÔ£¿
+
+´ð°¸ÊÇʹÓÃ[Proxyģʽ](https://www.liaoxuefeng.com/wiki/1252599548343744/1281319432618017)£¬´ÓORM¿ò¼Ü¶ÁÈ¡µÄUserʵÀýʵ¼ÊÉϲ¢²»ÊÇUserÀ࣬¶øÊÇ´úÀíÀ࣬´úÀíÀà¼Ì³Ð×ÔUserÀ࣬µ«Õë¶Ôÿ¸ösetter·½·¨×öÁ˸²Ð´£º
+
+```
+public class UserProxy extends User {
+    boolean _isNameChanged;
+
+    public void setName(String name) {
+        super.setName(name);
+        _isNameChanged = true;
+    }
+}
+
+```
+
+ÕâÑù£¬´úÀíÀà¿ÉÒÔ¸ú×Ùµ½Ã¿¸öÊôÐԵı仯¡£
+
+Õë¶ÔÒ»¶Ô¶à»ò¶à¶ÔÒ»¹ØÏµÊ±£¬´úÀíÀà¿ÉÒÔÖ±½Óͨ¹ýgetter·½·¨²éѯÊý¾Ý¿â£º
+
+```
+public class UserProxy extends User {
+    Session _session;
+    boolean _isNameChanged;
+
+    public void setName(String name) {
+        super.setName(name);
+        _isNameChanged = true;
+    }
+
+    /**
+     * »ñÈ¡User¶ÔÏó¹ØÁªµÄAddress¶ÔÏó:
+     */
+    public Address getAddress() {
+        Query q = _session.createQuery("from Address where userId = :userId");
+        q.setParameter("userId", this.getId());
+        List
list = query.list(); + return list.isEmpty() ? null : list(0); + } +} + +``` + +ΪÁËʵÏÖÕâÑùµÄ²éѯ£¬UserProxy±ØÐë±£´æHibernateµÄµ±Ç°Session¡£µ«ÊÇ£¬µ±ÊÂÎñÌá½»ºó£¬Session×Ô¶¯¹Ø±Õ£¬´ËʱÔÙ»ñÈ¡`getAddress()`½«ÎÞ·¨·ÃÎÊÊý¾Ý¿â£¬»òÕß»ñÈ¡µÄ²»ÊÇÊÂÎñÒ»ÖµÄÊý¾Ý¡£Òò´Ë£¬ORM¿ò¼Ü×ÜÊÇÒýÈëÁËAttached/Detached״̬£¬±íʾµ±Ç°´ËJava Beanµ½µ×ÊÇÔÚSessionµÄ·¶Î§ÄÚ£¬»¹ÊÇÍÑÀëÁËSession±ä³ÉÁËÒ»¸ö¡°ÓÎÀ롱¶ÔÏ󡣺ܶà³õѧÕßÎÞ·¨ÕýÈ·Àí½â״̬±ä»¯ºÍÊÂÎñ±ß½ç£¬¾Í»áÔì³É´óÁ¿µÄ`PersistentObjectException`Òì³£¡£ÕâÖÖÒþʽ״̬ʹµÃÆÕͨJava BeanµÄÉúÃüÖÜÆÚ±äµÃ¸´ÔÓ¡£ + +´ËÍ⣬HibernateºÍJPAΪÁËʵÏÖ¼æÈݶàÖÖÊý¾Ý¿â£¬ËüʹÓÃHQL»òJPQL²éѯ£¬¾­¹ýÒ»µÀת»»£¬±ä³ÉÌØ¶¨Êý¾Ý¿âµÄSQL£¬ÀíÂÛÉÏÕâÑù¿ÉÒÔ×öµ½ÎÞ·ìÇл»Êý¾Ý¿â£¬µ«ÕâÒ»²ã×Ô¶¯×ª»»³ýÁËÉÙÐíµÄÐÔÄÜ¿ªÏúÍ⣬¸øSQL¼¶±ðµÄÓÅ»¯´øÀ´ÁËÂé·³¡£ + +×îºó£¬ORM¿ò¼Üͨ³£ÌṩÁË»º´æ£¬²¢ÇÒ»¹·ÖΪһ¼¶»º´æºÍ¶þ¼¶»º´æ¡£Ò»¼¶»º´æÊÇÖ¸ÔÚÒ»¸öSession·¶Î§ÄڵĻº´æ£¬³£¼ûµÄÇé¾°ÊǸù¾ÝÖ÷¼ü²éѯʱ£¬Á½´Î²éѯ¿ÉÒÔ·µ»ØÍ¬Ò»ÊµÀý£º + +``` +User user1 = session.load(User.class, 123); +User user2 = session.load(User.class, 123); + +``` + +¶þ¼¶»º´æÊÇÖ¸¿çSessionµÄ»º´æ£¬Ò»°ãĬÈϹرգ¬ÐèÒªÊÖ¶¯ÅäÖ᣶þ¼¶»º´æ¼«´óµÄÔö¼ÓÁËÊý¾ÝµÄ²»Ò»ÖÂÐÔ£¬Ô­ÒòÔÚÓÚSQL·Ç³£Áé»î£¬³£³£»áµ¼ÖÂÒâÍâµÄ¸üС£ÀýÈ磺 + +``` +// Ïß³Ì1¶ÁÈ¡: +User user1 = session1.load(User.class, 123); +... +// Ò»¶Îʱ¼äºó£¬Ïß³Ì2¶ÁÈ¡: +User user2 = session2.load(User.class, 123); + +``` + +µ±¶þ¼¶»º´æÉúЧµÄʱºò£¬Á½¸öÏ̶߳ÁÈ¡µÄUserʵÀýÊÇÒ»ÑùµÄ£¬µ«ÊÇ£¬Êý¾Ý¿â¶ÔÓ¦µÄÐмǼÍêÈ«¿ÉÄܱ»Ð޸ģ¬ÀýÈ磺 + +``` +-- ¸øÀÏÓû§Ôö¼Ó100»ý·Ö: +UPDATE users SET bonus = bonus + 100 WHERE createdAt <= ? + +``` + +ORMÎÞ·¨ÅжÏ`id=123`µÄÓû§ÊÇ·ñÊܸÃ`UPDATE`Óï¾äÓ°Ïì¡£¿¼Âǵ½Êý¾Ý¿âͨ³£»áÖ§³Ö¶à¸öÓ¦ÓóÌÐò£¬´ËUPDATEÓï¾ä¿ÉÄÜÓÉÆäËû½ø³ÌÖ´ÐУ¬ORM¿ò¼Ü¾Í¸ü²»ÖªµÀÁË¡£ + +ÎÒÃǰÑÕâÖÖORM¿ò¼Ü³ÆÖ®ÎªÈ«×Ô¶¯ORM¿ò¼Ü¡£ + +¶Ô±ÈSpringÌṩµÄJdbcTemplate£¬ËüºÍORM¿ò¼ÜÏà±È£¬Ö÷ÒªÓм¸µã²î±ð£º + +1. ²éѯºóÐèÒªÊÖ¶¯ÌṩMapperʵÀýÒÔ±ã°ÑResultSetµÄÿһÐбäΪJava¶ÔÏó£» +2. Ôöɾ¸Ä²Ù×÷ËùÐèµÄ²ÎÊýÁÐ±í£¬ÐèÒªÊÖ¶¯´«È룬¼´°ÑUserʵÀý±äΪ[user.id, user.name, user.email]ÕâÑùµÄÁÐ±í£¬±È½ÏÂé·³¡£ + +µ«ÊÇJdbcTemplateµÄÓÅÊÆÔÚÓÚËüµÄÈ·¶¨ÐÔ£º¼´Ã¿´Î¶ÁÈ¡²Ù×÷Ò»¶¨ÊÇÊý¾Ý¿â²Ù×÷¶ø²»ÊÇ»º´æ£¬ËùÖ´ÐеÄSQLÊÇÍêȫȷ¶¨µÄ£¬È±µã¾ÍÊÇ´úÂë±È½Ï·±Ëö£¬¹¹Ôì`INSERT INTO users VALUES (?,?,?)`¸üÊǸ´ÔÓ¡£ + +ËùÒÔ£¬½éÓÚÈ«×Ô¶¯ORMÈçHibernateºÍÊÖдȫ²¿ÈçJdbcTemplateÖ®¼ä£¬»¹ÓÐÒ»ÖÖ°ë×Ô¶¯µÄORM£¬ËüÖ»¸ºÔð°ÑResultSet×Ô¶¯Ó³Éäµ½Java Bean£¬»òÕß×Ô¶¯Ìî³äJava Bean²ÎÊý£¬µ«ÈÔÐè×Ô¼ºÐ´³öSQL¡£[MyBatis](https://mybatis.org/)¾ÍÊÇÕâÑùÒ»ÖÖ°ë×Ô¶¯»¯ORM¿ò¼Ü¡£ + +ÎÒÃÇÀ´¿´¿´ÈçºÎÔÚSpringÖм¯³ÉMyBatis¡£ + +Ê×ÏÈ£¬ÎÒÃÇÒªÒýÈëMyBatis±¾Éí£¬Æä´Î£¬ÓÉÓÚSpring²¢Ã»ÓÐÏñHibernateÄÇÑùÄÚÖöÔMyBatisµÄ¼¯³É£¬ËùÒÔ£¬ÎÒÃÇÐèÒªÔÙÒýÈëMyBatis¹Ù·½×Ô¼º¿ª·¢µÄÒ»¸öÓëSpring¼¯³ÉµÄ¿â£º + +* org.mybatis:mybatis:3.5.11 +* org.mybatis:mybatis-spring:3.0.0 + +ºÍÇ°ÃæÒ»Ñù£¬ÏÈ´´½¨`DataSource`ÊDZز»¿ÉÉٵģº + +``` +@Configuration +@ComponentScan +@EnableTransactionManagement +@PropertySource("jdbc.properties") +public class AppConfig { + @Bean + DataSource createDataSource() { ... } +} + +``` + +ÔٻعËÒ»ÏÂHibernateºÍJPAµÄ`SessionFactory`Óë`EntityManagerFactory`£¬MyBatisÓëÖ®¶ÔÓ¦µÄÊÇ`SqlSessionFactory`ºÍ`SqlSession`£º + +| JDBC | Hibernate | JPA | MyBatis | +| --- | --- | --- | --- | +| DataSource | SessionFactory | EntityManagerFactory | SqlSessionFactory | +| Connection | Session | EntityManager | SqlSession | + +¿É¼û£¬ORMµÄÉè¼ÆÌ×·¶¼ÊÇÀàËÆµÄ¡£Ê¹ÓÃMyBatisµÄºËÐľÍÊÇ´´½¨`SqlSessionFactory`£¬ÕâÀïÎÒÃÇÐèÒª´´½¨µÄÊÇ`SqlSessionFactoryBean`£º + +``` +@Bean +SqlSessionFactoryBean createSqlSessionFactoryBean(@Autowired DataSource dataSource) { + var sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(dataSource); + return sqlSessionFactoryBean; +} + +``` + +ÒòΪMyBatis¿ÉÒÔÖ±½ÓʹÓÃSpring¹ÜÀíµÄÉùÃ÷ʽÊÂÎñ£¬Òò´Ë£¬´´½¨ÊÂÎñ¹ÜÀíÆ÷ºÍʹÓÃJDBCÊÇÒ»ÑùµÄ£º + +``` +@Bean +PlatformTransactionManager createTxManager(@Autowired DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); +} + +``` + +ºÍHibernate²»Í¬µÄÊÇ£¬MyBatisʹÓÃMapperÀ´ÊµÏÖÓ³É䣬¶øÇÒMapper±ØÐëÊǽӿڡ£ÎÒÃÇÒÔ`User`ÀàΪÀý£¬ÔÚ`User`ÀàºÍ`users`±íÖ®¼äÓ³ÉäµÄ`UserMapper`±àдÈçÏ£º + +``` +public interface UserMapper { + @Select("SELECT * FROM users WHERE id = #{id}") + User getById(@Param("id") long id); +} + +``` + +×¢Ò⣺ÕâÀïµÄMapper²»ÊÇ`JdbcTemplate`µÄ`RowMapper`µÄ¸ÅÄËüÊǶ¨Òå·ÃÎÊ`users`±íµÄ½Ó¿Ú·½·¨¡£±ÈÈçÎÒÃǶ¨ÒåÁËÒ»¸ö`User getById(long)`µÄÖ÷¼ü²éѯ·½·¨£¬²»½öÒª¶¨Òå½Ó¿Ú·½·¨±¾Éí£¬»¹ÒªÃ÷ȷд³ö²éѯµÄSQL£¬ÕâÀïÓÃ×¢½â`@Select`±ê¼Ç¡£SQLÓï¾äµÄÈκβÎÊý£¬¶¼Óë·½·¨²ÎÊý°´Ãû³Æ¶ÔÓ¦¡£ÀýÈ磬·½·¨²ÎÊýidµÄÃû×Öͨ¹ý×¢½â`@Param()`±ê¼ÇΪ`id`£¬ÔòSQLÓï¾äÀォÀ´Ìæ»»µÄռλ·û¾ÍÊÇ`#{id}`¡£ + +Èç¹ûÓжà¸ö²ÎÊý£¬ÄÇôÿ¸ö²ÎÊýÃüÃûºóÖ±½ÓÔÚSQLÖÐд³ö¶ÔÓ¦µÄռλ·û¼´¿É£º + +``` +@Select("SELECT * FROM users LIMIT #{offset}, #{maxResults}") +List getAll(@Param("offset") int offset, @Param("maxResults") int maxResults); + +``` + +×¢Ò⣺MyBatisÖ´Ðвéѯºó£¬½«¸ù¾Ý·½·¨µÄ·µ»ØÀàÐÍ×Ô¶¯°ÑResultSetµÄÿһÐÐת»»ÎªUserʵÀý£¬×ª»»¹æÔòµ±È»Êǰ´ÁÐÃûºÍÊôÐÔÃû¶ÔÓ¦¡£Èç¹ûÁÐÃûºÍÊôÐÔÃû²»Í¬£¬×î¼òµ¥µÄ·½Ê½ÊDZàдSELECTÓï¾äµÄ±ðÃû£º + +``` +-- ÁÐÃûÊÇcreated_time£¬ÊôÐÔÃûÊÇcreatedAt: +SELECT id, name, email, created_time AS createdAt FROM users + +``` + +Ö´ÐÐINSERTÓï¾ä¾ÍÉÔ΢Âé·³µã£¬ÒòΪÎÒÃÇÏ£Íû´«ÈëUserʵÀý£¬Òò´Ë£¬¶¨ÒåµÄ·½·¨½Ó¿ÚÓë`@Insert`×¢½âÈçÏ£º + +``` +@Insert("INSERT INTO users (email, password, name, createdAt) VALUES (#{user.email}, #{user.password}, #{user.name}, #{user.createdAt})") +void insert(@Param("user") User user); + +``` + +ÉÏÊö·½·¨´«ÈëµÄ²ÎÊýÃû³ÆÊÇ`user`£¬²ÎÊýÀàÐÍÊÇUserÀ࣬ÔÚSQLÖÐÒýÓõÄʱºò£¬ÒÔ`#{obj.property}`µÄ·½Ê½Ð´Õ¼Î»·û¡£ºÍHibernateÕâÑùµÄÈ«×Ô¶¯»¯ORMÏà±È£¬MyBatis±ØÐëд³öÍêÕûµÄINSERTÓï¾ä¡£ + +Èç¹û`users`±íµÄ`id`ÊÇ×ÔÔöÖ÷¼ü£¬ÄÇô£¬ÎÒÃÇÔÚSQLÖв»´«Èë`id`£¬µ«Ï£Íû»ñÈ¡²åÈëºóµÄÖ÷¼ü£¬ÐèÒªÔÙ¼ÓÒ»¸ö`@Options`×¢½â£º + +``` +@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") +@Insert("INSERT INTO users (email, password, name, createdAt) VALUES (#{user.email}, #{user.password}, #{user.name}, #{user.createdAt})") +void insert(@Param("user") User user); + +``` + +`keyProperty`ºÍ`keyColumn`·Ö±ðÖ¸³öJavaBeanµÄÊôÐÔºÍÊý¾Ý¿âµÄÖ÷¼üÁÐÃû¡£ + +Ö´ÐÐ`UPDATE`ºÍ`DELETE`Óï¾äÏà¶Ô±È½Ï¼òµ¥£¬ÎÒÃǶ¨Òå·½·¨ÈçÏ£º + +``` +@Update("UPDATE users SET name = #{user.name}, createdAt = #{user.createdAt} WHERE id = #{user.id}") +void update(@Param("user") User user); + +@Delete("DELETE FROM users WHERE id = #{id}") +void deleteById(@Param("id") long id); + +``` + +ÓÐÁË`UserMapper`½Ó¿Ú£¬»¹ÐèÒª¶ÔÓ¦µÄʵÏÖÀà²ÅÄÜÕæÕýÖ´ÐÐÕâЩÊý¾Ý¿â²Ù×÷µÄ·½·¨¡£ËäÈ»¿ÉÒÔ×Ô¼ºÐ´ÊµÏÖÀ࣬µ«ÎÒÃdzýÁ˱àд`UserMapper`½Ó¿ÚÍ⣬»¹ÓÐ`BookMapper`¡¢`BonusMapper`¡­¡­Ò»¸öÒ»¸öд̫Âé·³£¬Òò´Ë£¬MyBatisÌṩÁËÒ»¸ö`MapperFactoryBean`À´×Ô¶¯´´½¨ËùÓÐMapperµÄʵÏÖÀà¡£¿ÉÒÔÓÃÒ»¸ö¼òµ¥µÄ×¢½âÀ´ÆôÓÃËü£º + +``` +@MapperScan("com.itranswarp.learnjava.mapper") +...ÆäËû×¢½â... +public class AppConfig { + ... +} + +``` + +ÓÐÁË`@MapperScan`£¬¾Í¿ÉÒÔÈÃMyBatis×Ô¶¯É¨ÃèÖ¸¶¨°üµÄËùÓÐMapper²¢´´½¨ÊµÏÖÀà¡£ÔÚÕæÕýµÄÒµÎñÂß¼­ÖУ¬ÎÒÃÇ¿ÉÒÔÖ±½Ó×¢È룺 + +``` +@Component +@Transactional +public class UserService { + // ×¢ÈëUserMapper: + @Autowired + UserMapper userMapper; + + public User getUserById(long id) { + // µ÷ÓÃMapper·½·¨: + User user = userMapper.getById(id); + if (user == null) { + throw new RuntimeException("User not found by id."); + } + return user; + } +} + +``` + +¿É¼û£¬ÒµÎñÂß¼­Ö÷Òª¾ÍÊÇͨ¹ý`XxxMapper`¶¨ÒåµÄÊý¾Ý¿â·½·¨À´·ÃÎÊÊý¾Ý¿â¡£ + +### XMLÅäÖà + +ÉÏÊöÔÚSpringÖм¯³ÉMyBatisµÄ·½Ê½£¬ÎÒÃÇÖ»ÐèÒªÓõ½×¢½â£¬²¢Ã»ÓÐÈκÎXMLÅäÖÃÎļþ¡£MyBatisÒ²ÔÊÐíʹÓÃXMLÅäÖÃÓ³Éä¹ØÏµºÍSQLÓï¾ä£¬ÀýÈ磬¸üÐÂ`User`ʱ¸ù¾ÝÊôÐÔÖµ¹¹Ô춯̬SQL£º + +``` + + UPDATE users SET + + name = #{user.name} + hobby = #{user.hobby} + summary = #{user.summary} + + WHERE id = #{user.id} + + +``` + +±àдXMLÅäÖõÄÓŵãÊÇ¿ÉÒÔ×é×°³ö¶¯Ì¬SQL£¬²¢ÇÒ°ÑËùÓÐSQL²Ù×÷¼¯ÖÐÔÚÒ»Æð¡£È±µãÊÇÅäÖÃÆðÀ´Ì«·±Ëö£¬µ÷Ó÷½·¨Ê±Èç¹ûÏë²é¿´SQL»¹ÐèÒª¶¨Î»µ½XMLÅäÖÃÖС£ÕâÀïÎÒÃDz»½éÉÜXMLµÄÅäÖ÷½Ê½£¬ÐèÒªÁ˽âµÄͯЬÇë×ÔÐÐÔĶÁ[¹Ù·½Îĵµ](https://mybatis.org/mybatis-3/zh/configuration.html)¡£ + +ʹÓÃMyBatis×î´óµÄÎÊÌâÊÇËùÓÐSQL¶¼ÐèҪȫ²¿ÊÖд£¬ÓŵãÊÇÖ´ÐеÄSQL¾ÍÊÇÎÒÃÇ×Ô¼ºÐ´µÄSQL£¬¶ÔSQL½øÐÐÓÅ»¯·Ç³£¼òµ¥£¬Ò²¿ÉÒÔ±àдÈÎÒ⸴ÔÓµÄSQL£¬»òÕßʹÓÃÊý¾Ý¿âµÄÌØ¶¨Óï·¨£¬µ«Çл»Êý¾Ý¿â¿ÉÄܾͲ»Ì«ÈÝÒס£ºÃÏûÏ¢ÊǴ󲿷ÖÏîÄ¿²¢Ã»ÓÐÇл»Êý¾Ý¿âµÄÐèÇó£¬ÍêÈ«¿ÉÒÔÕë¶Ôij¸öÊý¾Ý¿â±àд¾¡¿ÉÄÜÓÅ»¯µÄSQL¡£ + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\240\241\351\252\214\345\212\237\350\203\275\347\232\204\346\224\257\346\214\201.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\240\241\351\252\214\345\212\237\350\203\275\347\232\204\346\224\257\346\214\201.md" new file mode 100644 index 0000000..de8dba7 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\345\257\271\344\272\216\346\240\241\351\252\214\345\212\237\350\203\275\347\232\204\346\224\257\346\214\201.md" @@ -0,0 +1,713 @@ +# Spring УÑé + +Java API ¹æ·¶(`JSR303`)¶¨ÒåÁË`Bean`УÑéµÄ±ê×¼`validation-api`£¬µ«Ã»ÓÐÌṩʵÏÖ¡£`hibernate validation`ÊǶÔÕâ¸ö¹æ·¶µÄʵÏÖ£¬²¢Ôö¼ÓÁËУÑé×¢½âÈç`@Email`¡¢`@Length`µÈ¡£`Spring Validation`ÊǶÔ`hibernate validation`µÄ¶þ´Î·â×°£¬ÓÃÓÚÖ§³Ö`spring mvc`²ÎÊý×Ô¶¯Ð£Ñé¡£ + +## [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8)¿ìËÙÈëÃÅ + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96)ÒýÈëÒÀÀµ + +Èç¹û spring-boot °æ±¾Ð¡ÓÚ 2.3.x£¬spring-boot-starter-web »á×Ô¶¯´«Èë hibernate-validator ÒÀÀµ¡£Èç¹û spring-boot °æ±¾´óÓÚ 2.3.x£¬ÔòÐèÒªÊÖ¶¯ÒýÈëÒÀÀµ£º + + + +``` + + org.hibernate.validator + hibernate-validator-parent + 6.2.5.Final + + +``` + + + +¶ÔÓÚ web ·þÎñÀ´Ëµ£¬Îª·ÀÖ¹·Ç·¨²ÎÊý¶ÔÒµÎñÔì³ÉÓ°Ï죬ÔÚ Controller ²ãÒ»¶¨Òª×ö²ÎÊýУÑéµÄ£¡´ó²¿·ÖÇé¿öÏ£¬ÇëÇó²ÎÊý·ÖΪÈçÏÂÁ½ÖÖÐÎʽ£º + +* POST¡¢PUT ÇëÇó£¬Ê¹Óà requestBody ´«µÝ²ÎÊý£» +* GET ÇëÇó£¬Ê¹Óà requestParam/PathVariable ´«µÝ²ÎÊý¡£ + +ʵ¼ÊÉÏ£¬²»¹ÜÊÇ requestBody ²ÎÊýУÑ黹ÊÇ·½·¨¼¶±ðµÄУÑ飬×îÖÕ¶¼Êǵ÷Óà Hibernate Validator Ö´ÐÐУÑ飬Spring Validation Ö»ÊÇ×öÁËÒ»²ã·â×°¡£ + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E6%A0%A1%E9%AA%8C%E7%A4%BA%E4%BE%8B)УÑéʾÀý + +£¨1£©ÔÚʵÌåÉϱê¼ÇУÑé×¢½â + + + +``` +@Data +@NoArgsConstructor +@AllArgsConstructor +public class User implements Serializable { + + @NotNull + private Long id; + + @NotBlank + @Size(min = 2, max = 10) + private String name; + + @Min(value = 1) + @Max(value = 100) + private Integer age; + +} + +``` + + + +£¨2£©ÔÚ·½·¨²ÎÊýÉÏÉùÃ÷УÑé×¢½â + + + +``` +@Slf4j +@Validated +@RestController +@RequestMapping("validate1") +public class ValidatorController { + + /** + * {@link RequestBody} ²ÎÊýУÑé + */ + @PostMapping(value = "save") + public DataResult save(@Valid @RequestBody User entity) { + log.info("±£´æÒ»Ìõ¼Ç¼£º{}", JSONUtil.toJsonStr(entity)); + return DataResult.ok(true); + } + + /** + * {@link RequestParam} ²ÎÊýУÑé + */ + @GetMapping(value = "queryByName") + public DataResult queryByName( + @RequestParam("username") + @NotBlank + @Size(min = 2, max = 10) + String name + ) { + User user = new User(1L, name, 18); + return DataResult.ok(user); + } + + /** + * {@link PathVariable} ²ÎÊýУÑé + */ + @GetMapping(value = "detail/{id}") + public DataResult detail(@PathVariable("id") @Min(1L) Long id) { + User user = new User(id, "ÀîËÄ", 18); + return DataResult.ok(user); + } + +} + +``` + + + +£¨3£©Èç¹ûÇëÇó²ÎÊý²»Âú×ãУÑ鹿Ôò£¬Ôò»áÅ׳ö `ConstraintViolationException` »ò `MethodArgumentNotValidException` Òì³£¡£ + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86)ͳһÒì³£´¦Àí + +ÔÚʵ¼ÊÏîÄ¿¿ª·¢ÖУ¬Í¨³£»áÓÃͳһÒì³£´¦ÀíÀ´·µ»ØÒ»¸ö¸üÓѺõÄÌáʾ¡£ + + + +``` +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + /** + * ´¦ÀíËùÓв»¿ÉÖªµÄÒì³£ + */ + @ResponseBody + @ResponseStatus(HttpStatus.OK) + @ExceptionHandler(Throwable.class) + public Result handleException(Throwable e) { + log.error("δ֪Òì³£", e); + return new Result(ResultStatus.HTTP_SERVER_ERROR.getCode(), e.getMessage()); + } + + /** + * ͳһ´¦ÀíÇëÇó²ÎÊýУÑéÒì³£(ÆÕͨ´«²Î) + * + * @param e ConstraintViolationException + * @return {@link DataResult} + */ + @ResponseBody + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({ ConstraintViolationException.class }) + public Result handleConstraintViolationException(final ConstraintViolationException e) { + log.error("ConstraintViolationException", e); + List errors = new ArrayList<>(); + for (ConstraintViolation violation : e.getConstraintViolations()) { + Path path = violation.getPropertyPath(); + List pathArr = StrUtil.split(path.toString(), ','); + errors.add(pathArr.get(0) + " " + violation.getMessage()); + } + return new Result(ResultStatus.REQUEST_ERROR.getCode(), CollectionUtil.join(errors, ",")); + } + + /** + * ´¦Àí²ÎÊýУÑéÒì³£ + * + * @param e MethodArgumentNotValidException + * @return {@link DataResult} + */ + @ResponseBody + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({ MethodArgumentNotValidException.class }) + private Result handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { + log.error("MethodArgumentNotValidException", e); + List errors = new ArrayList<>(); + for (ObjectError error : e.getBindingResult().getAllErrors()) { + errors.add(((FieldError) error).getField() + " " + error.getDefaultMessage()); + } + return new Result(ResultStatus.REQUEST_ERROR.getCode(), CollectionUtil.join(errors, ",")); + } + +} + +``` + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E8%BF%9B%E9%98%B6%E4%BD%BF%E7%94%A8)½ø½×ʹÓà + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%88%86%E7%BB%84%E6%A0%A1%E9%AA%8C)·Ö×éУÑé + +ÔÚʵ¼ÊÏîÄ¿ÖУ¬¿ÉÄܶà¸ö·½·¨ÐèҪʹÓÃͬһ¸ö DTO ÀàÀ´½ÓÊÕ²ÎÊý£¬¶ø²»Í¬·½·¨µÄУÑ鹿ÔòºÜ¿ÉÄÜÊDz»Ò»ÑùµÄ¡£Õâ¸öʱºò£¬¼òµ¥µØÔÚ DTO ÀàµÄ×Ö¶ÎÉϼÓÔ¼Êø×¢½âÎÞ·¨½â¾öÕâ¸öÎÊÌâ¡£Òò´Ë£¬spring-validation Ö§³ÖÁË·Ö×éУÑéµÄ¹¦ÄÜ£¬×¨ÃÅÓÃÀ´½â¾öÕâÀàÎÊÌâ¡£ + +£¨1£©¶¨Òå·Ö×é + + + +``` +@Target({ ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface AddCheck { } + +@Target({ ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EditCheck { } + +``` + + + +£¨2£©ÔÚʵÌåÉϱê¼ÇУÑé×¢½â + + + +``` +@Data +public class User2 { + + @NotNull(groups = EditCheck.class) + private Long id; + + @NotNull(groups = { AddCheck.class, EditCheck.class }) + @Size(min = 2, max = 10, groups = { AddCheck.class, EditCheck.class }) + private String name; + + @IsMobile(message = "²»ÊÇÓÐЧÊÖ»úºÅ", groups = { AddCheck.class, EditCheck.class }) + private String mobile; + +} + +``` + + + +£¨3£©ÔÚ·½·¨Éϸù¾Ý²»Í¬³¡¾°½øÐÐУÑé·Ö×é + + + +``` +@Slf4j +@Validated +@RestController +@RequestMapping("validate2") +public class ValidatorController2 { + + /** + * {@link RequestBody} ²ÎÊýУÑé + */ + @PostMapping(value = "add") + public DataResult add(@Validated(AddCheck.class) @RequestBody User2 entity) { + log.info("Ìí¼ÓÒ»Ìõ¼Ç¼£º{}", JSONUtil.toJsonStr(entity)); + return DataResult.ok(true); + } + + /** + * {@link RequestBody} ²ÎÊýУÑé + */ + @PostMapping(value = "edit") + public DataResult edit(@Validated(EditCheck.class) @RequestBody User2 entity) { + log.info("±à¼­Ò»Ìõ¼Ç¼£º{}", JSONUtil.toJsonStr(entity)); + return DataResult.ok(true); + } + +} + +``` + + + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%B5%8C%E5%A5%97%E6%A0%A1%E9%AA%8C)ǶÌ×УÑé + +Ç°ÃæµÄʾÀýÖУ¬DTO ÀàÀïÃæµÄ×ֶζ¼ÊÇ»ù±¾Êý¾ÝÀàÐÍºÍ String ÀàÐÍ¡£µ«ÊÇʵ¼Ê³¡¾°ÖУ¬ÓпÉÄÜij¸ö×Ö¶ÎÒ²ÊÇÒ»¸ö¶ÔÏó£¬ÕâÖÖÇé¿öÏÈ£¬¿ÉÒÔʹÓÃǶÌ×УÑé¡£ post ±ÈÈ磬ÉÏÃæ±£´æ User ÐÅÏ¢µÄʱºòͬʱ»¹´øÓÐ Job ÐÅÏ¢¡£ÐèҪעÒâµÄÊÇ£¬´Ëʱ DTO ÀàµÄ¶ÔÓ¦×ֶαØÐë±ê¼Ç@Valid ×¢½â¡£ + + + +``` +@Data +public class UserDTO { + + @Min(value = 10000000000000000L, groups = Update.class) + private Long userId; + + @NotNull(groups = {Save.class, Update.class}) + @Length(min = 2, max = 10, groups = {Save.class, Update.class}) + private String userName; + + @NotNull(groups = {Save.class, Update.class}) + @Length(min = 6, max = 20, groups = {Save.class, Update.class}) + private String account; + + @NotNull(groups = {Save.class, Update.class}) + @Length(min = 6, max = 20, groups = {Save.class, Update.class}) + private String password; + + @NotNull(groups = {Save.class, Update.class}) + @Valid + private Job job; + + @Data + public static class Job { + + @Min(value = 1, groups = Update.class) + private Long jobId; + + @NotNull(groups = {Save.class, Update.class}) + @Length(min = 2, max = 10, groups = {Save.class, Update.class}) + private String jobName; + + @NotNull(groups = {Save.class, Update.class}) + @Length(min = 2, max = 10, groups = {Save.class, Update.class}) + private String position; + } + + /** + * ±£´æµÄʱºòУÑé·Ö×é + */ + public interface Save { + } + + /** + * ¸üеÄʱºòУÑé·Ö×é + */ + public interface Update { + } +} +¸´ÖÆ´úÂë + +``` + + + +ǶÌ×УÑé¿ÉÒÔ½áºÏ·Ö×éУÑéÒ»ÆðʹÓ᣻¹ÓоÍÊÇǶÌ×¼¯ºÏУÑé»á¶Ô¼¯ºÏÀïÃæµÄÿһÏî¶¼½øÐÐУÑ飬ÀýÈç`List`×ֶλá¶ÔÕâ¸ö list ÀïÃæµÄÿһ¸ö Job ¶ÔÏó¶¼½øÐÐУÑé + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%A1%E9%AA%8C%E6%B3%A8%E8%A7%A3)×Ô¶¨ÒåУÑé×¢½â + +£¨1£©×Ô¶¨ÒåУÑé×¢½â `@IsMobile` + + + +``` +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) +@Retention(RUNTIME) +@Constraint(validatedBy = MobileValidator.class) +public @interface IsMobile { + + String message(); + + Class[] groups() default {}; + + Class[] payload() default {}; + +} + +``` + + + +£¨2£©ÊµÏÖ `ConstraintValidator` ½Ó¿Ú£¬±àд `@IsMobile` УÑé×¢½âµÄ½âÎöÆ÷ + + + +``` +import cn.hutool.core.util.StrUtil; +import io.github.dunwu.spring.core.validation.annotation.IsMobile; +import io.github.dunwu.tool.util.ValidatorUtil; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class MobileValidator implements ConstraintValidator { + + @Override + public void initialize(IsMobile isMobile) { } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + if (StrUtil.isBlank(s)) { + return false; + } else { + return ValidatorUtil.isMobile(s); + } + } + +} + +``` + + + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%A1%E9%AA%8C)×Ô¶¨ÒåУÑé + +¿ÉÒÔͨ¹ýʵÏÖ `org.springframework.validation.Validator` ½Ó¿ÚÀ´×Ô¶¨ÒåУÑé¡£ + +ÓÐÒÔÏÂÒªµã + +* ʵÏÖ `supports` ·½·¨ +* ʵÏÖ `validate` ·½·¨ + * ͨ¹ý `Errors` ¶ÔÏóÊÕ¼¯´íÎó + * `ObjectError`£º¶ÔÏó£¨Bean£©´íÎó£º + * `FieldError`£º¶ÔÏó£¨Bean£©ÊôÐÔ£¨Property£©´íÎó + * ͨ¹ý `ObjectError` ºÍ `FieldError` ¹ØÁª `MessageSource` ʵÏÖ»ñÈ¡×îÖյĴíÎóÎݸ + + + +``` +package io.github.dunwu.spring.core.validation; + +import io.github.dunwu.spring.core.validation.annotation.Valid; +import io.github.dunwu.spring.core.validation.config.CustomValidatorConfig; +import io.github.dunwu.spring.core.validation.entity.Person; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Component +public class CustomValidator implements Validator { + + private final CustomValidatorConfig validatorConfig; + + public CustomValidator(CustomValidatorConfig validatorConfig) { + this.validatorConfig = validatorConfig; + } + + /** + * ±¾Ð£ÑéÆ÷Ö»Õë¶Ô Person ¶ÔÏó½øÐÐУÑé + */ + @Override + public boolean supports(Class clazz) { + return Person.class.equals(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + ValidationUtils.rejectIfEmpty(errors, "name", "name.empty"); + + List fields = getFields(target.getClass()); + for (Field field : fields) { + Annotation[] annotations = field.getAnnotations(); + for (Annotation annotation : annotations) { + if (annotation.annotationType().getAnnotation(Valid.class) != null) { + try { + ValidatorRule validatorRule = validatorConfig.findRule(annotation); + if (validatorRule != null) { + validatorRule.valid(annotation, target, field, errors); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + private List getFields(Class clazz) { + // ÉùÃ÷FieldÊý×é + List fields = new ArrayList<>(); + // Èç¹ûclassÀàÐͲ»Îª¿Õ + while (clazz != null) { + // Ìí¼ÓÊôÐÔµ½ÊôÐÔÊý×é + Collections.addAll(fields, clazz.getDeclaredFields()); + clazz = clazz.getSuperclass(); + } + return fields; + } + +} + +``` + + + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%BF%AB%E9%80%9F%E5%A4%B1%E8%B4%A5-fail-fast)¿ìËÙʧ°Ü(Fail Fast) + +Spring Validation ĬÈÏ»áУÑéÍêËùÓÐ×ֶΣ¬È»ºó²ÅÅ׳öÒì³£¡£¿ÉÒÔͨ¹ýһЩ¼òµ¥µÄÅäÖ㬿ªÆô Fali Fast ģʽ£¬Ò»µ©Ð£Ñéʧ°Ü¾ÍÁ¢¼´·µ»Ø¡£ + + + +``` +@Bean +public Validator validator() { + ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) + .configure() + // ¿ìËÙʧ°Üģʽ + .failFast(true) + .buildValidatorFactory(); + return validatorFactory.getValidator(); +} + +``` + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#spring-%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86)Spring УÑéÔ­Àí + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#spring-%E6%A0%A1%E9%AA%8C%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)Spring УÑéʹÓó¡¾° + +* Spring ³£¹æÐ£Ñ飨Validator£© +* Spring Êý¾Ý°ó¶¨£¨DataBinder£© +* Spring Web ²ÎÊý°ó¶¨£¨WebDataBinder£© +* Spring WebMVC/WebFlux ´¦Àí·½·¨²ÎÊýУÑé + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#validator-%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1)Validator ½Ó¿ÚÉè¼Æ + +* ½Ó¿ÚÖ°Ôð + * Spring ÄÚ²¿Ð£ÑéÆ÷½Ó¿Ú£¬Í¨¹ý±à³ÌµÄ·½Ê½Ð£ÑéÄ¿±ê¶ÔÏó +* ºËÐÄ·½·¨ + * `supports(Class)`£ºÐ£ÑéÄ¿±êÀàÄÜ·ñУÑé + * `validate(Object,Errors)`£ºÐ£ÑéÄ¿±ê¶ÔÏ󣬲¢½«Ð£Ñéʧ°ÜµÄÄÚÈÝÊä³öÖÁ Errors ¶ÔÏó +* ÅäÌ××é¼þ + * ´íÎóÊÕ¼¯Æ÷£º`org.springframework.validation.Errors` + * Validator ¹¤¾ßÀࣺ`org.springframework.validation.ValidationUtils` + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#errors-%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1)Errors ½Ó¿ÚÉè¼Æ + +* ½Ó¿ÚÖ°Ôð + * Êý¾Ý°ó¶¨ºÍУÑé´íÎóÊÕ¼¯½Ó¿Ú£¬Óë Java Bean ºÍÆäÊôÐÔÓÐÇ¿¹ØÁªÐÔ +* ºËÐÄ·½·¨ + * `reject` ·½·¨£¨ÖØÔØ£©£ºÊÕ¼¯´íÎóÎݸ + * `rejectValue` ·½·¨£¨ÖØÔØ£©£ºÊÕ¼¯¶ÔÏó×Ö¶ÎÖеĴíÎóÎݸ +* ÅäÌ××é¼þ + * Java Bean ´íÎóÃèÊö£º`org.springframework.validation.ObjectError` + * Java Bean ÊôÐÔ´íÎóÃèÊö£º`org.springframework.validation.FieldError` + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#errors-%E6%96%87%E6%A1%88%E6%9D%A5%E6%BA%90)Errors ÎݸÀ´Ô´ + +Errors ÎݸÉú³É²½Öè + +* Ñ¡Ôñ Errors ʵÏÖ£¨È磺`org.springframework.validation.BeanPropertyBindingResult`£© +* µ÷Óà reject »ò rejectValue ·½·¨ +* »ñÈ¡ Errors ¶ÔÏóÖÐ ObjectError »ò FieldError +* ½« ObjectError »ò FieldError ÖÐµÄ code ºÍ args£¬¹ØÁª MessageSource ʵÏÖ£¨È磺`ResourceBundleMessageSource`£© + +### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#spring-web-%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86)spring web УÑéÔ­Àí + +#### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#requestbody-%E5%8F%82%E6%95%B0%E6%A0%A1%E9%AA%8C%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)RequestBody ²ÎÊýУÑéʵÏÖÔ­Àí + +ÔÚ spring-mvc ÖУ¬`RequestResponseBodyMethodProcessor` ÊÇÓÃÓÚ½âÎö `@RequestBody` ±ê×¢µÄ²ÎÊýÒÔ¼°´¦Àí`@ResponseBody` ±ê×¢·½·¨µÄ·µ»ØÖµµÄ¡£ÆäÖУ¬Ö´ÐвÎÊýУÑéµÄÂß¼­¿Ï¶¨¾ÍÔÚ½âÎö²ÎÊýµÄ·½·¨ `resolveArgument()` ÖУº + + + +``` +@Override +public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { + + parameter = parameter.nestedIfOptional(); + Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); + String name = Conventions.getVariableNameForParameter(parameter); + + if (binderFactory != null) { + WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); + if (arg != null) { + // ³¢ÊÔ½øÐвÎÊýУÑé + validateIfApplicable(binder, parameter); + if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { + // Èç¹û´æÔÚУÑé´íÎó£¬ÔòÅ׳ö MethodArgumentNotValidException + throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); + } + } + if (mavContainer != null) { + mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); + } + } + + return adaptArgumentIfNecessary(arg, parameter); +} + +``` + + + +¿ÉÒÔ¿´µ½£¬resolveArgument()µ÷ÓÃÁË validateIfApplicable()½øÐвÎÊýУÑé¡£ + + + +``` +protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { + // »ñÈ¡²ÎÊý×¢½â£¬Èç @RequestBody¡¢@Valid¡¢@Validated + Annotation[] annotations = parameter.getParameterAnnotations(); + for (Annotation ann : annotations) { + // Ïȳ¢ÊÔ»ñÈ¡ @Validated ×¢½â + Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); + // Èç¹û±ê×¢ÁË @Validated£¬Ö±½Ó¿ªÊ¼Ð£Ñé¡£ + // Èç¹ûûÓУ¬ÄÇôÅжϲÎÊýǰÊÇ·ñÓÐ Valid ¿ªÍ·µÄ×¢½â¡£ + if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { + Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); + Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); + // Ö´ÐÐУÑé + binder.validate(validationHints); + break; + } + } +} + +``` + + + +ÒÔÉÏ´úÂ룬¾Í½âÊÍÁË Spring ΪʲôÄÜͬʱ֧³Ö `@Validated`¡¢`@Valid` Á½¸ö×¢½â¡£ + +½ÓÏÂÀ´£¬¿´Ò»Ï WebDataBinder.validate() µÄʵÏÖ£º + + + +``` +@Override +public void validate(Object target, Errors errors, Object... validationHints) { + if (this.targetValidator != null) { + processConstraintViolations( + // ´Ë´¦µ÷Óà Hibernate Validator Ö´ÐÐÕæÕýµÄУÑé + this.targetValidator.validate(target, asValidationGroups(validationHints)), errors); + } +} + +``` + + + +ͨ¹ýÉÏÃæ´úÂ룬¿ÉÒÔ¿´³ö Spring УÑéʵ¼ÊÉÏÊÇ»ùÓÚ Hibernate Validator µÄ·â×°¡£ + +#### [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E6%96%B9%E6%B3%95%E7%BA%A7%E5%88%AB%E7%9A%84%E5%8F%82%E6%95%B0%E6%A0%A1%E9%AA%8C%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)·½·¨¼¶±ðµÄ²ÎÊýУÑéʵÏÖÔ­Àí + +Spring Ö§³Ö¸ù¾Ý·½·¨È¥½øÐÐÀ¹½Ø¡¢Ð£Ñ飬ԭÀí¾ÍÔÚÓÚÓ¦ÓÃÁË AOP ¼¼Êõ¡£¾ßÌåÀ´Ëµ£¬ÊÇͨ¹ý `MethodValidationPostProcessor` ¶¯Ì¬×¢²á AOP ÇÐÃæ£¬È»ºóʹÓà `MethodValidationInterceptor` ¶ÔÇе㷽·¨Ö¯ÈëÔöÇ¿¡£ + + + +``` +public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessorimplements InitializingBean { + @Override + public void afterPropertiesSet() { + // ΪËùÓÐ @Validated ±ê×¢µÄ Bean ´´½¨ÇÐÃæ + Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true); + // ´´½¨ Advisor ½øÐÐÔöÇ¿ + this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator)); + } + + // ´´½¨ Advice£¬±¾ÖʾÍÊÇÒ»¸ö·½·¨À¹½ØÆ÷ + protected Advice createMethodValidationAdvice(@Nullable Validator validator) { + return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor()); + } +} + +``` + + + +½Ó×Å¿´Ò»Ï `MethodValidationInterceptor`£º + + + +``` +public class MethodValidationInterceptor implements MethodInterceptor { + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + // ÎÞÐèÔöÇ¿µÄ·½·¨£¬Ö±½ÓÌø¹ý + if (isFactoryBeanMetadataMethod(invocation.getMethod())) { + return invocation.proceed(); + } + // »ñÈ¡·Ö×éÐÅÏ¢ + Class[] groups = determineValidationGroups(invocation); + ExecutableValidator execVal = this.validator.forExecutables(); + Method methodToValidate = invocation.getMethod(); + Set> result; + try { + // ·½·¨Èë²ÎУÑ飬×îÖÕ»¹ÊÇίÍиø Hibernate Validator À´Ð£Ñé + result = execVal.validateParameters( + invocation.getThis(), methodToValidate, invocation.getArguments(), groups); + } + catch (IllegalArgumentException ex) { + ... + } + // ÓÐÒì³£Ö±½ÓÅ׳ö + if (!result.isEmpty()) { + throw new ConstraintViolationException(result); + } + // ÕæÕýµÄ·½·¨µ÷Óà + Object returnValue = invocation.proceed(); + // ¶Ô·µ»ØÖµ×öУÑ飬×îÖÕ»¹ÊÇίÍиøHibernate ValidatorÀ´Ð£Ñé + result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); + // ÓÐÒì³£Ö±½ÓÅ׳ö + if (!result.isEmpty()) { + throw new ConstraintViolationException(result); + } + return returnValue; + } +} + +``` + + + +ʵ¼ÊÉÏ£¬²»¹ÜÊÇ requestBody ²ÎÊýУÑ黹ÊÇ·½·¨¼¶±ðµÄУÑ飬×îÖÕ¶¼Êǵ÷Óà Hibernate Validator Ö´ÐÐУÑ飬Spring Validation Ö»ÊÇ×öÁËÒ»²ã·â×°¡£ + +## [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E9%97%AE%E9%A2%98)ÎÊÌâ + +**Spring ÓÐÄÄЩУÑéºËÐÄ×é¼þ**£¿ + +* ¼ìÑéÆ÷£º`org.springframework.validation.Validator` +* ´íÎóÊÕ¼¯Æ÷£º`org.springframework.validation.Errors` +* Java Bean ´íÎóÃèÊö£º`org.springframework.validation.ObjectError` +* Java Bean ÊôÐÔ´íÎóÃèÊö£º`org.springframework.validation.FieldError` +* Bean Validation ÊÊÅ䣺`org.springframework.validation.beanvalidation.LocalValidatorFactoryBean` + +## [#](https://dunwu.github.io/spring-tutorial/pages/fe6aad/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)²Î¿¼×ÊÁÏ + +* [Spring ¹Ù·½ÎĵµÖ® Core Technologies(opens new window)](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans) +* [¡¶Ð¡Âí¸ç½² Spring ºËÐıà³Ì˼Ïë¡·(opens new window)](https://time.geekbang.org/course/intro/265) +* https://juejin.cn/post/6856541106626363399 + + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204Environment\347\216\257\345\242\203\345\217\230\351\207\217.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204Environment\347\216\257\345\242\203\345\217\230\351\207\217.md" new file mode 100644 index 0000000..99987fa --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204Environment\347\216\257\345\242\203\345\217\230\351\207\217.md" @@ -0,0 +1,849 @@ + + + + +Èç½ñ£¬ÖÂÁ¦ÓÚ°ïÖú¿ª·¢ÕßÓøüÉٵĴúÂë¡¢¸ü¿ìµØÐ´³öÉú²ú¼¶ÏµÍ³µÄ Spring Boot ÒÑÈ»³ÉΪ Java Ó¦Óÿª·¢µÄÊÂʵ±ê×¼¡£ÔÚ Spring Boot ÌṩµÄÖÚ¶àÌØÐÔÖУ¬**×Ô¶¯ÅäÖÃ**ÎÞÒÉÊǶÔÌáÉý¿ª·¢ÌåÑé×îÏÔÖøµÄÒ»¸öÌØÐÔ£¬Spring Boot »ùÓÚÕâÒ»ÌØÐÔΪ¿ª·¢ÈËÔ±×Ô¶¯ÉùÃ÷ÁËÈô¸É¿ªÏä¼´Óᢾ߱¸Ä³Ò»¹¦ÄÜµÄ Bean¡£´ó¶àÊýÇé¿öÏ£¬×Ô¶¯ÅäÖÃµÄ Bean ¸ÕºÃÄÜÂú×ã´ó¼ÒµÄÐèÇ󣬵«ÔÚijЩÇé¿öÏ£¬²»µÃ²»ÍêÕûµØ¸²¸ÇËüÃÇ£¬Õâ¸öʱºòÖ»ÐèÒªÖØÐÂÉùÃ÷Ïà¹ØÀàÐ굀 Bean ¼´¿É£¬ÒòΪ¾ø´ó¶àÊý×Ô¶¯ÅäÖÃµÄ Bean ¶¼»áÓÉ`@ConditionalOnMissingBean`×¢½âÐÞÊΡ£ÐÒÔ˵ÄÊÇ£¬Èç¹ûÖ»ÊÇÏë΢µ÷һЩϸ½Ú£¬±ÈÈç¸Ä¸Ä¶Ë¿ÚºÅ (server.port) ºÍÊý¾ÝÔ´ URL (spring.datasource.url) £¬ÄÇѹ¸ùû±ØÒªÖØÐÂÉùÃ÷`ServerProperties`ºÍ`DataSourceProperties`ÕâÁ© Bean À´¸²¸Ç×Ô¶¯ÅäÖÃµÄ Bean¡£ Spring Boot Ϊ×Ô¶¯ÅäÖÃµÄ Bean ÌṩÁË1000¶à¸öÓÃÓÚ΢µ÷µÄÊôÐÔ£¬µ±ÐèÒªµ÷ÕûÉèÖÃʱ£¬Ö»ÐèÒªÔÚ»·¾³±äÁ¿¡¢ÃüÁîÐвÎÊý»òÅäÖÃÎļþ (application.properties/application.yml) ÖнøÐÐÖ¸¶¨¼´¿É£¬Õâ¾ÍÊÇ Spring Boot µÄ`Externalized Configuration` (ÅäÖÃÍ⻯) ÌØÐÔ¡£ + +µ±È»£¬ÍⲿÅäÖÃÔ´²¢²»¾ÖÏÞÓÚ»·¾³±äÁ¿¡¢ÃüÁîÐвÎÊýºÍÅäÖÃÎļþÕâÈýÖÖ£¬¸ÐÐËȤµÄ¶ÁÕß¿ÉÒÔ×ÔÐÐÔĶÁ Spring Boot ¹Ù·½Îĵµ¡£ÔÚ Spring ÖУ¬`BeanFactory`°çÑÝ×Å Bean ÈÝÆ÷µÄ½ÇÉ«£¬¶ø`Environment`ͬÑù¶¨Î»ÎªÒ»¸öÈÝÆ÷£¬¼´ÍⲿÅäÖÃÔ´ÖеÄÊôÐÔ¶¼»á±»Ìí¼Óµ½ _Environment_ ÖС£**ÔÚ΢·þÎñ´óÐÐÆäµÀµÄ½ñÌ죬ÍⲿÅäÖÃÔ´ÓÖÑÜÉú³öÁË_Disconf_¡¢_Apollo_ ºÍ _Nacos_ µÈ·Ö²¼Ê½ÅäÖÃÖÐÐÄ£¬µ«ÔÚ Spring µÄµØÅÌ£¬»¹ÊÇÒªÈëÏçËæË×£¬´ÓÅäÖÃÖÐÐÄÖжÁÈ¡µ½µÄÊôÐÔÒÀÈ»»á±»×·¼Óµ½ _Environment_ ÖÐ**¡£ + +±ÊÕßÖ®ËùÒÔдÕâÆªÎÄÕ£¬ÊÇÊÜ`jasypt`×é¼þµÄÆô·¢¡£µÚÒ»´Î½Ó´¥ËüÊÇÔÚ2018Ä꣬µ±Ê±¾ÍºÜºÃÆæÕâÍæÒâ¶ù¾¿¾¹ÊÇÈçºÎʵÏÖ¶ÔÃô¸ÐÊôÐÔ¼Ó½âÃܵģ»ÏÖÔÚÀ´¿´£¬ÒªÏëʵÏÖÕâôһ¸ö¶«¶«£¬²»½öÐèÒªÊìϤ Bean µÄÉúÃüÖÜÆÚ¡¢IoC ÈÝÆ÷ÍØÕ¹µã (IoC Container Extension Points) ºÍ Spring Boot µÄÆô¶¯Á÷³ÌµÈ֪ʶ£¬»¹ÐèÒªÕÆÎÕ _Environment_¡£ + +> jasypt ÉÏÊÖÊ®·Ö¼òµ¥¡£Ê×ÏÈͨ¹ý`jasypt-maven-plugin`ÕâÒ» maven ²å¼þΪÃô¸ÐÊôÐÔÖµÉú³ÉÃÜÎÄ£¬È»ºóÓÃ`ENC(ÃÜÎÄ)`Ìæ»»Ãô¸ÐÊôÐÔÖµ¼´¿É¡£ÈçÏ£º +> +> ``` +> jasypt.encryptor.password=crimson_typhoon +> +> spring.datasource.url=jdbc:mysql://HOST:PORT/db_sql_boy?characterEncoding=UTF-8 +> spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver +> spring.datasource.hikari.username=root +> spring.datasource.hikari.password=ENC(qS8+DEIlHxvhPHgn1VaW3oHkn2twrmwNOHewWLIfquAXiCDBrKwvIhDoqalKyhIF) +> ¸´ÖÆ´úÂë +> ``` + +## 1 ÈÏʶ Environmnent + +ÔÚʵ¼Ê¹¤×÷ÖУ¬ÎÒÃÇÓë _Environment_ ´ò½»µÀµÄ»ú»á²¢²»¶à£»Èç¹ûÒµÎñ Bean ȷʵÐèÒª»ñÈ¡ÍⲿÅäÖÃÔ´ÖеÄijһÊôÐÔÖµ£¬¿ÉÒÔÊÖ¶¯½« _Environment_ ×¢Èëµ½¸ÃÒµÎñ Bean ÖУ¬Ò²¿ÉÒÔÖ±½ÓʵÏÖ`EnvironmentAware`½Ó¿Ú£¬µÃµ½ _Environment_ ÀàÐ굀 Bean ʵÀýÖ®ºó¿ÉÒÔͨ¹ý`getProperty()`»ñÈ¡¾ßÌåÊôÐÔÖµ¡£_Environment_ ½Ó¿ÚÄÚÈÝÈçÏÂËùʾ£º + +``` +public interface Environment extends PropertyResolver { + String[] getActiveProfiles(); + String[] getDefaultProfiles(); + boolean acceptsProfiles(Profiles profiles); +} + +public interface PropertyResolver { + boolean containsProperty(String key); + String getProperty(String key); + String getProperty(String key, String defaultValue); + T getProperty(String key, Class targetType); + T getProperty(String key, Class targetType, T defaultValue); + String resolvePlaceholders(String text); +} +¸´ÖÆ´úÂë +``` + +**´ó¼Ò²»ÒªÊÜ _Environment_ ÖÐ _getProperty()_ ·½·¨µÄÎóµ¼£¬ÍⲿÅäÖÃÔ´ÖеÄÊôÐÔ²¢²»ÊÇÒÔµ¥¸öÊôÐÔΪά¶È±»Ìí¼Óµ½ _Environment_ Öе쬶øÊÇÒÔ`PropertySource`Ϊά¶È**¡£_PropertySource_ ÊǶÔÊôÐÔÔ´Ãû³ÆºÍ¸ÃÊôÐÔÔ´ÖÐÒ»×éÊôÐԵijéÏó£¬`MapPropertySource`ÊÇÒ»ÖÖ×î¼òµ¥µÄʵÏÖ£¬Ëüͨ¹ý _Map_ À´³ÐÔØÏà¹ØµÄÊôÐÔ¡£_PropertySource_ ÄÚÈÝÈçÏ£º + +``` +public abstract class PropertySource { + protected final String name; + protected final T source; + + public PropertySource(String name, T source) { + this.name = name; + this.source = source; + } + + public String getName() { return this.name; } + public T getSource() { return this.source; } + public abstract Object getProperty(String name); +} +¸´ÖÆ´úÂë +``` + +´ÓÉÏÊö _PropertySource_ ÄÚÈÝÀ´¿´£¬_PropertySource_ ×ÔÉíÊǾ߱¸¸ù¾ÝÊôÐÔÃû»ñÈ¡ÊôÐÔÖµÕâÒ»ÄÜÁ¦µÄ¡£ + +#### ****getProperty()ÄÚ²¿Ö´ÐÐÂß¼­**** + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/QQ%E6%88%AA%E5%9B%BE20230416193319.jpg) + +Ò»°ã£¬_Environment_ ʵÏÖÀàÖлá³ÖÓÐÒ»¸ö`PropertyResolver`ÀàÐ͵ijÉÔ±±äÁ¿£¬½ø¶ø½»ÓÉ _PropertyResolver_ ¸ºÔðÖ´ÐÐ _getProperty()_ Âß¼­¡£_PropertyResolver_ ʵÏÖÀàÖÐÓÖ»á³ÖÓÐÁ½¸ö³ÉÔ±±äÁ¿£¬·Ö±ðÊÇ£º`ConversionService`Óë`PropertySources`£»Ê×ÏÈ£¬_PropertyResolver_ ±éÀú `PropertySources` ÖÐµÄ _PropertySource_£¬»ñȡԭÉúÊôÐÔÖµ£»È»ºóίÅÉ _ConversionService_ ¶ÔÔ­ÉúÊôÐÔÖµ½øÐÐÊý¾ÝÀàÐÍת»» (Èç¹ûÓбØÒªµÄ»°)¡£**ËäÈ» PropertySource ×ÔÉíÊǾ߱¸¸ù¾ÝÊôÐÔÃû»ñÈ¡ÊôÐÔÖµÕâÒ»ÄÜÁ¦µÄ£¬µ«²»¾ß±¸Õ¼Î»·û½âÎöÓëÀàÐÍת»»ÄÜÁ¦£¬ÓÚÊÇÔÚÖмäÒýÈë¾ß±¸ÕâÁ½ÖÖÄÜÁ¦µÄ PropertyResolver£¬ ÕâÒ²Ó¡Ö¤ÁËÒ»¸ö¶Î×Ó£ºÔÚ¼ÆËã»ú¿ÆÑ§ÖУ¬Ã»ÓÐʲôÎÊÌâÊÇÔÚÖмä¼ÓÒ»²ã½â¾ö²»Á˵ģ¬Èç¹ûÓУ¬ÄǾÍÔÙ¼ÓÒ»²ã**¡£ + +#### ****PropertySourceÄÚ²¿¸üÐÂÂß¼­**** + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/QQ%E6%88%AA%E5%9B%BE20230416193319.jpg) + +_Environment_ ʵÏÖÀàÖгýÁ˳ÖÓÐ`PropertyResolver`ÀàÐ͵ijÉÔ±±äÁ¿Í⣬»¹ÓÐÒ»¸ö`MutablePropertySources`ÀàÐ͵ijÉÔ±±äÁ¿£¬µ«²¢²»Ìṩֱ½Ó²Ù×÷¸Ã _MutablePropertySources_ µÄ·½·¨£¬ÎÒÃÇÖ»ÄÜͨ¹ý`getPropertySources()`·½·¨»ñÈ¡ _MutablePropertySources_ ʵÀý£¬È»ºó½èÖú _MutablePropertySources_ ÖеÄ`addFirst()`¡¢`addLast()`ºÍ`replace()`µÈ·½·¨È¥¸üР_PropertySource_¡£_MutablePropertySources_ ÊÇ _PropertySources_ Ψһһ¸öʵÏÖÀ࣬ÈçÏÂͼËùʾ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416193420.png) + +×ܵÄÀ´Ëµ£¬_Environment_ ÊÇ¶Ô _PropertySource_ ºÍ _Profile_ µÄ¶¥¼¶³éÏó£¬ÏÂÃæ½éÉÜ _Profile_ µÄ¸ÅÄî¡£µ±Ó¦ÓóÌÐòÐèÒª²¿Êðµ½²»Í¬µÄÔËÐл·¾³Ê±£¬Ò»Ð©ÊôÐÔÏîͨ³£»áÓÐËù²»Í¬£¬±ÈÈ磬Êý¾ÝÔ´ URL ÔÚ¿ª·¢»·¾³ºÍ²âÊÔ»·¾³¾Í»á²»Ò»Ñù¡£Spring ´Ó3.1°æ±¾¿ªÊ¼Ö§³Ö»ùÓÚ _Profile_ µÄÌõ¼þ»¯ÅäÖᣠ+ +**Profile in Spring 3.1** + +ÔÚ Spring ·¢²¼3.1°æ±¾Ê±£¬Spring Boot »¹Î´ÎÊÊÀ£¬¿ÉÒÔ˵´ËʱµÄ _Profile_ ÌØÐÔ»¹ÊÇÓÐЩ**覴Ã**µÄ£¬µ«è¦²»ÑÚ褡£Ö÷ÒªÌåÏÖÔÚ£ºÕë¶ÔͬһÀàÐ굀 Bean£¬±ØÐëÉùÃ÷¶à´Î¡£Ò»ÆðÀ´¸ÐÊÜÏÂÕâÖÖС覴㺠+ +``` +@Configuration(proxyBeanMethods = false) +public class DataSourceConfig { + @Bean + @Profile("dev") + public DataSource devDataSource () { + return DataSourceBuilder.create() + .driverClassName("com.mysql.jdbc.Driver") + .url("jdbc:mysql://DEV_HOST:PORT/db_sql_boy?characterEncoding=UTF-8") + .username("dev") + .password("dev") + .build(); + } + + @Bean + @Profile("test") + public DataSource testDataSource () { + return DataSourceBuilder.create() + .driverClassName("com.mysql.jdbc.Driver") + .url("jdbc:mysql://TEST_HOST:PORT/db_sql_boy?characterEncoding=UTF-8") + .username("test") + .password("test") + .build(); + } +} +¸´ÖÆ´úÂë +``` + +**Profile in Spring Boot** + +Spring Boot ·¢²¼ºó£¬`@Profile`×¢½â¿ÉÒÔÈÓµ½¾ÅÏöÔÆÍâÁË¡£¹Ù·½¿ª·¢´óÀп϶¨Ò²Òâʶµ½ _Profile in Spring 3.1_ ÖÐÕâÖÖ覴ã¬ÓÚÊÇÔÚ Spring Boot µÄµÚÒ»°æ±¾ _(1.0.0.RELEASE)_ ÖÐ¾ÍÆÈ²»¼°´ýµØÖ§³ÖΪ _application.properties_ ºÍ _application.yml_ ÀïµÄÊôÐÔÏîÅäÖà _Profile_ ÁË¡£»»¸ö¿Ú棬һÆðÀ´¸ÐÊÜÏÂÕâÖÖÓÅÑÅ£º + +``` +@Configuration(proxyBeanMethods = false) +public class DataSourceConfig { + @Bean + public DataSource devDataSource (DataSourceProperties dataSourceProperties) { + return DataSourceBuilder.create() + .driverClassName(dataSourceProperties.getDriverClassName()) + .url(dataSourceProperties.getUrl()) + .username(dataSourceProperties.getUsername()) + .password(dataSourceProperties.getPassword()) + .build(); + } +} +¸´ÖÆ´úÂë +``` + +_application-dev.properties_ ÄÚÈÝÈçÏ£º + +``` +spring.datasource.url=jdbc:mysql://DEV_HOST:PORT/db_sql_boy?characterEncoding=UTF-8 +spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.hikari.password=dev +spring.datasource.hikari.username=dev +¸´ÖÆ´úÂë +``` + +_application-test.properties_ ÄÚÈÝÈçÏ£º + +``` +spring.datasource.url=jdbc:mysql://TEST_HOST:PORT/db_sql_boy?characterEncoding=UTF-8 +spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.hikari.password=test +spring.datasource.hikari.username=test +¸´ÖÆ´úÂë +``` + +ÔÚÔ­Éú Spring 3.1 ºÍ Spring Boot ÖУ¬¾ùÊÇͨ¹ý`spring.profiles.active`À´Îª _Environment_ Ö¸¶¨¼¤»îµÄ _Profile_£¬·ñÔò_Environment_ ÖÐĬÈϼ¤»îµÄ _Profile_ Ãû³ÆÎª`default`¡£Ð´µ½ÕâÀ±ÊÕßÄÔº£ÖÐÉÁÏÖÒ»¸öÎÊÌ⣺һ°ã£¬`@Profile` ×¢½âÖ÷ÒªÓë _@Configuration_ ×¢½â»ò _@Bean_ ×¢½â´îÅäʹÓã¬Èç¹û _spring.profiles.active_ µÄֵΪ _dev_ ʱ£¬ÄÇôÄÇЩÓÉ _@Configuration_ »ò _@Bean_ ×¢½â±ê¼Ç (µ«Ã»ÓÐ`@Profile`×¢½âµÄÉíÓ°¹þ) µÄ Bean »¹»á±»½âÎöΪÈô¸É`BeanDefinition`ʵÀýÂ𣿴ð°¸ÊÇ»áµÄ¡£`ConfigurationClassPostProcessor`¸ºÔ𽫠_@Configuration_ ÅäÖÃÀà½âÎöΪ _BeanDefinition_£¬Ôڴ˹ý³ÌÖлáÖ´ÐÐ`ConditionEvaluator`µÄ`shouldSkip()`·½·¨£¬Ö÷ÒªÄÚÈÝÈçÏ£º + +``` +public class ConditionEvaluator { + public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationCondition.ConfigurationPhase phase) { + if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { + return false; + } + + if (phase == null) { + if (metadata instanceof AnnotationMetadata && + ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { + return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.PARSE_CONFIGURATION); + } + return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN); + } + + List conditions = new ArrayList<>(); + for (String[] conditionClasses : getConditionClasses(metadata)) { + for (String conditionClass : conditionClasses) { + Condition condition = getCondition(conditionClass, this.context.getClassLoader()); + conditions.add(condition); + } + } + + AnnotationAwareOrderComparator.sort(conditions); + + for (Condition condition : conditions) { + ConfigurationCondition.ConfigurationPhase requiredPhase = null; + if (condition instanceof ConfigurationCondition) { + requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); + } + if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { + return true; + } + } + + return false; + } +} +¸´ÖÆ´úÂë +``` + +`shouldSkip()`·½·¨µÚÒ»ÐÐ _if_ Óï¾ä¾ÍÊǴ𰸣¬`@Profile`×¢½âÓÉ`@Conditional(ProfileCondition.class)`ÐÞÊΣ¬ÄÇÈç¹ûÒ»¸öÅäÖÃÀàÍ·ÉÏûÓÐ`Condition`µÄÉíÓ°£¬Ö±½Ó·µ»Ø`false`ÁË£¬ÄǾÍÊDz»Ìø¹ý¸ÃÅäÖÃÀàµÄÒâ˼ඣ¡ + +_Environment_ ÖеÄÕâЩ _PropertySource_ ¾¿¾¹ÓÐɶÓ𡣿µ±È»ÊÇΪÁËÌî³ä _Bean_ à¶£¬·Ï»°²»¶à˵£¬ÉÏͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416193443.png) + +> ±ÊÕßÒÔǰ¶¼ÊÇÓà visio ºÍ processOn »­Í¼£¬µÚÒ»´ÎÌåÑé draw.io£¬Ã»Ïëµ½Èç´ËÓÅÐ㣬ǿÁÒ°²ÀûÒ»²¨£¡ + +## 2 Environmnent ³õʼ»¯Á÷³Ì + +±¾½ÚÖ÷Òª½éÉÜ Spring Boot ÔÚÆô¶¯¹ý³ÌÖÐÏò _Environmnt_ Öо¿¾¹×¢²áÁËÄÄЩ _PropertySource_¡£Æô¶¯Èë¿ÚλÓÚ`SpringApplication`ÖеÄ`run(String... args)`·½·¨£¬ÈçÏ£º + +``` +public class SpringApplication { + public ConfigurableApplicationContext run(String... args) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + DefaultBootstrapContext bootstrapContext = createBootstrapContext(); + ConfigurableApplicationContext context = null; + configureHeadlessProperty(); + SpringApplicationRunListeners listeners = getRunListeners(args); + listeners.starting(bootstrapContext, this.mainApplicationClass); + try { + ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); + ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); + configureIgnoreBeanInfo(environment); + Banner printedBanner = printBanner(environment); + context = createApplicationContext(); + context.setApplicationStartup(this.applicationStartup); + prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); + refreshContext(context); + afterRefresh(context, applicationArguments); + stopWatch.stop(); + if (this.logStartupInfo) { + new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); + } + listeners.started(context); + callRunners(context, applicationArguments); + } catch (Throwable ex) { + handleRunFailure(context, ex, listeners); + throw new IllegalStateException(ex); + } + + try { + listeners.running(context); + } catch (Throwable ex) { + handleRunFailure(context, ex, null); + throw new IllegalStateException(ex); + } + return context; + } +} +¸´ÖÆ´úÂë +``` + +¿ÉÒÔÃ÷ÏÔ¿´³ö£¬_Environmnt_ µÄ³õʼ»¯ÊÇÔÚ`refreshContext(context)`֮ǰÍê³ÉµÄ£¬ÕâÊǺÁÎÞÒÉÎʵġ£_run()_ ·½·¨ºÜ¸´ÔÓ£¬µ«Óë±¾ÎÄÖ÷ÌâÆõºÏµÄÂß¼­Ö»ÓÐ**Ò»**´¦£º + +``` +prepareEnvironment(listeners, bootstrapContext, applicationArguments); +¸´ÖÆ´úÂë +``` + +ÏÂÃæ·Ö±ð·ÖÎöÕâÁ½´¦ºËÐÄÂß¼­¡£ + +### 2.1 prepareEnvironment() + +ÏÔÈ»£¬ºËÐÄÄÚÈݶ¼ÔÚ`prepareEnvironment()`·½·¨ÄÚ£¬ÏÂÃæ·ÖС½ÚÖðÒ»·ÖÎö¡£ + +``` +public class SpringApplication { + private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, + DefaultBootstrapContext bootstrapContext, + ApplicationArguments applicationArguments) { + // 2.1.1 + ConfigurableEnvironment environment = getOrCreateEnvironment(); + // 2.1.2 + configureEnvironment(environment, applicationArguments.getSourceArgs()); + // 2.1.3 + ConfigurationPropertySources.attach(environment); + // 2.1.4 + listeners.environmentPrepared(bootstrapContext, environment); + DefaultPropertiesPropertySource.moveToEnd(environment); + bindToSpringApplication(environment); + ConfigurationPropertySources.attach(environment); + return environment; + } +} +¸´ÖÆ´úÂë +``` + +#### 2.1.1 getOrCreateEnvironment() + +`getOrCreateEnvironment()`Ö÷Òª¸ºÔð¹¹½¨ _Environment_ ʵÀý¡£Èç¹ûµ±Ç°Ó¦ÓÃÊÇ»ùÓÚ`ͬ²½×èÈûI/O`Ä£Ð͵ģ¬Ôò _Environment_ Ñ¡ÓÃ`ApplicationServletEnvironment`£»Ïà·´µØ£¬Èç¹ûµ±Ç°Ó¦ÓÃÊÇ»ùÓÚ`Òì²½·Ç×èÈûI/O`Ä£Ð͵ģ¬Ôò _Environment_ Ñ¡ÓÃ`ApplicationReactiveWebEnvironment`¡£ÎÒÃǹ¤×÷Öлù±¾¶¼ÊÇ»ùÓÚ Spring MVC ¿ª·¢Ó¦Óã¬Spring MVC ÊÇÒ»¿î¹¹½¨ÓÚ`Servlet API`Ö®ÉÏ¡¢»ùÓÚͬ²½×èÈû I/O Ä£Ð͵ÄÖ÷Á÷ Java Web ¿ª·¢¿ò¼Ü£¬ÕâÖÖ I/O Ä£ÐÍÒâζ×ÅÒ»¸ö HTTP ÇëÇó¶ÔÓ¦Ò»¸öỊ̈߳¬¼´Ã¿Ò»¸ö HTTP ÇëÇó¶¼ÊÇÔÚ¸÷×ÔÏß³ÌÉÏÏÂÎÄÖÐÍê³É´¦ÀíµÄ¡£_ApplicationServletEnvironment_ ¼Ì³Ð¹ØÏµÈçÏÂͼËùʾ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416193515.png) + +´ÓÉÏͼ¿ÉÒÔ¿´³ö _ApplicationServletEnvironment_ ¼Ò×åÏ൱ÅÓ´ó£¬ÔÚÖ´ÐÐ _ApplicationServletEnvironment_ ¹¹Ôì·½·¨µÄʱºò±ØÈ»»á´¥·¢¸÷¼¶¸¸À๹Ôì·½·¨ÖеÄÂß¼­£¬**ÒÀ´ÎΪ**£º + +``` +public abstract class AbstractEnvironment implements ConfigurableEnvironment { + public AbstractEnvironment() { + this(new MutablePropertySources()); + } + + protected AbstractEnvironment(MutablePropertySources propertySources) { + this.propertySources = propertySources; + // createPropertyResolver(propertySources) + // |___ ConfigurationPropertySources.createPropertyResolver(propertySources) + // |___ new ConfigurationPropertySourcesPropertyResolver(propertySources) + this.propertyResolver = createPropertyResolver(propertySources); + customizePropertySources(propertySources); + } +} +¸´ÖÆ´úÂë +``` + +``` +public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment { + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new StubPropertySource("servletConfigInitParams")); + propertySources.addLast(new StubPropertySource("servletContextInitParams")); + super.customizePropertySources(propertySources); + } +} +¸´ÖÆ´úÂë +``` + +``` +public class StandardEnvironment extends AbstractEnvironment { + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast( + new PropertiesPropertySource("systemProperties", (Map) System.getProperties())); + propertySources.addLast( + new SystemEnvironmentPropertySource("systemEnvironment", (Map) System.getenv())); + } +} +¸´ÖÆ´úÂë +``` + +Ëæ×Å _ApplicationServletEnvironment_ ¹¹Ôì·½·¨µÄÖ´ÐУ¬´ËʱÔÚ _Environment_ Àï _MutablePropertySources_ ÀàÐ͵ijÉÔ±±äÁ¿`propertySources`ÖÐÒѾ­ÓÐÁË**ËÄ**¸ö _PropertySource_ ÁË£¬Ãû³ÆÒÀ´ÎÊÇ£º`servletConfigInitParams`¡¢`servletContextInitParams`¡¢`systemProperties`ºÍ`systemEnvironment`¡£´ËÍ⣬ҲҪ¼Çס _ApplicationServletEnvironment_ ÖеÄÁ½¸öÖØÒª³ÉÔ±±äÁ¿£¬¼´`MutablePropertySources`ºÍ`ConfigurationPropertySourcesPropertyResolver`¡£ + +#### 2.1.2 configureEnvironment() + +`configureEnvironment()`·½·¨ÖеÄÂß¼­Ò²ºÜ¼òµ¥¹þ¡£Ê×ÏÈ£¬Îª _Environment_ ÖÐµÄ _PropertySourcesPropertyResolver_ É趨 _ConversionService_£»È»ºó£¬Ïò _Environment_ ÖÐµÄ _MutablePropertySources_ ×·¼ÓÒ»¸öÃû³ÆÎª`commandLineArgs`µÄ _PropertySource_ ʵÀý£¬×¢ÒâʹÓõÄÊÇ`addFirst()`·½·¨Å¶£¬ÕâÒâζ×ÅÕâ¸öÃû³ÆÎª`commandLineArgs`µÄ _PropertySource_ ÓÅÏȼ¶ÊÇ×î¸ßµÄ¡£Ö÷ÒªÂß¼­ÈçÏ£º + +``` +public class SpringApplication { + protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + if (this.addConversionService) { + environment.getPropertyResolver().setConversionService(new ApplicationConversionService()); + } + if (this.addCommandLineProperties && args.length > 0) { + MutablePropertySources sources = environment.getPropertySources(); + sources.addFirst(new SimpleCommandLinePropertySource(args)); + } + } +} +¸´ÖÆ´úÂë +``` + +¼ÌÐø`SimpleCommandLinePropertySource`£º + +``` +public class SimpleCommandLinePropertySource extends CommandLinePropertySource { + public SimpleCommandLinePropertySource(String... args) { + // Æä¸¸À๹Ôì·½·¨Îª£ºsuper("commandLineArgs", source) + super(new SimpleCommandLineArgsParser().parse(args)); + } +} +¸´ÖÆ´úÂë +``` + +ÃüÁîÐвÎÊý»¹ÊDZȽϳ£Óõ쬱ÈÈçÎÒÃÇÔÚÆô¶¯ Spring Boot Ó¦ÓÃʱ»áÕâÑùÉùÃ÷ÃüÁîÐвÎÊý£º`java -jar app.jar --server.port=8088`¡£ + +#### 2.1.3 ConfigurationPropertySources.attach() + +`attach()`·½·¨Ö÷Òª¾ÍÊÇÔÚ _Environment_ ÖÐ _MutablePropertySources_ µÄÍ·²¿Î»ÖòåÈë¼ÓÒ»¸öÃû³ÆÎª`configurationProperties`µÄ _PropertySource_ ʵÀý¡£Ö÷ÒªÂß¼­ÈçÏ£º + +``` +public final class ConfigurationPropertySources { + public static void attach(org.springframework.core.env.Environment environment) { + MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources(); + PropertySource attached = getAttached(sources); + if (attached != null && attached.getSource() != sources) { + sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); + attached = null; + } + if (attached == null) { + sources.addFirst(new ConfigurationPropertySourcesPropertySource("configurationProperties", new SpringConfigurationPropertySources(sources))); + } + } + + static PropertySource getAttached(MutablePropertySources sources) { + return (sources != null) ? sources.get("configurationProperties") : null; + } +} +¸´ÖÆ´úÂë +``` + +±ÊÕß¶¢×ÅÕâÍæÒâ¶ù¿´Á˺þã¬Ñ¹¸ùû¿´³öÕâ¸öÃû³ÆÎª`configurationProperties`µÄ _PropertySource_ ¾¿¾¹ÓÐɶÓá£×îºó£¬»¹ÊÇÔÚ¹Ù·½ÎĵµÖйØÓÚ`Relaxed Binding` (¿íËɰó¶¨) µÄÃèÊöÖв³öÁËЩ¶ËÄß¡£»¹ÊÇͨ¹ý´úÂëÀ´½â¶Á±È½ÏÖ±½Ó¡£Ê×ÏÈ£¬ÔÚ _application.properties_ ÖÐ×·¼ÓÒ»¸öÅäÖÃÏ`a.b.my-first-key=hello spring environment`£»È»ºó£¬Í¨¹ý _Environment_ È¡³öÕâ¸öÅäÖÃÏîµÄÖµ£¬ÈçÏ£º + +``` +@SpringBootApplication +public class DemoApplication { + public static void main(String[] args) { + ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(DemoApplication.class, args); + ConfigurableWebEnvironment environment = (ConfigurableWebEnvironment) + configurableApplicationContext.getBean(Environment.class); + System.out.println(environment.getProperty("a.b.my-first-key")); + } +} +¸´ÖÆ´úÂë +``` + +Æô¶¯Ó¦Óú󣬿ØÖÆÌ¨´òÓ¡³öÁË _hello spring environment_ ×ÖÑù£¬ÕâÓëÔ¤ÆÚÊÇÏà·ûµÄ¡£¿Éµ±ÎÒÃÇͨ¹ý`environment.getProperty("a.b.myfirstkey")`»òÕß`environment.getProperty("a.b.my-firstkey")`ÒÀÈ»Äܹ»»ñÈ¡µ½ÅäÖÃÏîµÄÄÚÈÝ¡£`a.b.myfirstkey`ºÍ`a.b.my-firstkey`²¢²»ÊÇÅäÖÃÎļþÖеÄÊôÐÔÃû³Æ£¬Ö»ÊÇÏàËÆ¶øÒÑ£¬ÕâµÄÈ·ºÜ**¿íËÉ**°¡£¬¹þ¹þ¡£¸ÐÐËȤµÄ¶ÁÕß¿ÉÒÔ×ÔÐÐ DEBUG ¿´¿´ÆäÖеÄÔ­Àí¡£ + +#### 2.1.4 listeners.environmentPrepared() + +Çúڰ壬¸÷λ´óÀУ¬Õâ¸öÒª¿¼µÄ £¡`environmentPrepared()`·½·¨»á¹ã²¥Ò»¸ö`ApplicationEnvironmentPreparedEvent`ʼþ£¬½Ó×ÅÓÉ`EnvironmentPostProcessorApplicationListener`ÏìÓ¦¸Ãʼþ£¬ÕâÓ¦¸ÃÊǵäÐ͵Ä**¹Û²ìÕßģʽ**¡£Ö÷ÒªÄÚÈÝÈçÏ£º + +``` +public class SpringApplicationRunListeners { + private final List listeners; + + void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { + doWithListeners("spring.boot.application.environment-prepared", + (listener) -> listener.environmentPrepared(bootstrapContext, environment)); + } + + private void doWithListeners(String stepName, Consumer listenerAction) { + StartupStep step = this.applicationStartup.start(stepName); + this.listeners.forEach(listenerAction); + step.end(); + } +} + +public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { + @Override + public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, + ConfigurableEnvironment environment) { + this.initialMulticaster.multicastEvent( + new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment)); + } +} + +public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { + @Override + public void multicastEvent(ApplicationEvent event) { + multicastEvent(event, resolveDefaultEventType(event)); + } + + @Override + public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { + ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); + Executor executor = getTaskExecutor(); + for (ApplicationListener listener : getApplicationListeners(event, type)) { + if (executor != null) { + executor.execute(() -> invokeListener(listener, event)); + } else { + invokeListener(listener, event); + } + } + } +} +¸´ÖÆ´úÂë +``` + +ÏÂÃæÀ´¿´Ò»ÏÂ`EnvironmentPostProcessorApplicationListener`µÄÂ®É½ÕæÃæÄ¿£º + +``` +public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered { + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); + } + if (event instanceof ApplicationPreparedEvent) { + onApplicationPreparedEvent(); + } + if (event instanceof ApplicationFailedEvent) { + onApplicationFailedEvent(); + } + } + private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { + ConfigurableEnvironment environment = event.getEnvironment(); + SpringApplication application = event.getSpringApplication(); + for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) { + postProcessor.postProcessEnvironment(environment, application); + } + } +} +¸´ÖÆ´úÂë +``` + +`EnvironmentPostProcessor`ÊÇ Spring Boot Ϊ _Environment_ Á¿Éí´òÔìµÄÀ©Õ¹µã¡£ÕâÀïÒýÓùٷ½ÎĵµÖбȽϾ«Á¶µÄÒ»¾ä»°£º_Allows for customization of the application's Environment prior to the application context being refreshed_¡£_EnvironmentPostProcessor_ ÊÇÒ»¸öº¯ÊýÐÔ½Ó¿Ú£¬ÄÚÈÝÈçÏ£º + +``` +public interface EnvironmentPostProcessor { + void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application); +} +¸´ÖÆ´úÂë +``` + +ÔÚÉÏÊö _EnvironmentPostProcessorApplicationListener_ ʼþ´¦ÀíÂß¼­ÖУ¬`getEnvironmentPostProcessors`¸ºÔð¼ÓÔØ³öËùÓÐµÄ _EnvironmentPostProcessor_ ¡£¿´Ò»ÏÂÄÚ²¿¼ÓÔØÂß¼­£º + +``` +public interface EnvironmentPostProcessorsFactory { + static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { + return new ReflectionEnvironmentPostProcessorsFactory( + classLoader, + SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader) + ); + } +} +¸´ÖÆ´úÂë +``` + +¼ÌÐø½øÈë`SpringFactoriesLoader`һ̽¾¿¾¹£º + +``` +public final class SpringFactoriesLoader { + + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + + public static List loadFactoryNames(Class factoryType, ClassLoader classLoader) { + ClassLoader classLoaderToUse = classLoader; + if (classLoaderToUse == null) { + classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); + } + String factoryTypeName = factoryType.getName(); + return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); + } + + private static Map> loadSpringFactories(ClassLoader classLoader) { + Map> result = cache.get(classLoader); + if (result != null) { + return result; + } + + result = new HashMap<>(); + try { + Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + UrlResource resource = new UrlResource(url); + Properties properties = PropertiesLoaderUtils.loadProperties(resource); + for (Map.Entry entry : properties.entrySet()) { + String factoryTypeName = ((String) entry.getKey()).trim(); + String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); + for (String factoryImplementationName : factoryImplementationNames) { + result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) + .add(factoryImplementationName.trim()); + } + } + } + result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() + .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); + cache.put(classLoader, result); + } catch (IOException ex) { + throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); + } + return result; + } +} +¸´ÖÆ´úÂë +``` + +> **Spring SPI** +> +> > _SpringFactoriesLoader_ ÕâÒ»Ì×Âß¼­¾ÍÊÇ Spring ÖеÄ`SPI`»úÖÆ£»Ö±°×µã˵£¬¾ÍÊÇ´Ó`classpath`ϵÄ`META-INF/spring.factories` ÎļþÖмÓÔØ _EnvironmentPostProcessor_ £¬Èç¹û´ó¼ÒÓÐÐèÇó¾Í½«×Ô¼ºÊµÏÖµÄ _EnvironmentPostProcessor_ ·Åµ½¸ÃÎļþÖоÍÐÐÁË¡£ÆäʵÓë`JDK`ÖеÄ`SPI`»úÖÆºÜÀàËÆ¹þ¡£ + +ÔÚµ±Ç°°æ±¾£¬Spring Boot ÄÚÖÃÁË7¸ö _EnvironmentPostProcessor_ ʵÏÖÀà¡£½ÓÏÂÀ´Ìô¼¸¸ö±È½ÏµäÐ͵ķÖÎöÏ¡£ + +**RandomValuePropertySourceEnvironmentPostProcessor** + +`RandomValuePropertySourceEnvironmentPostProcessor`Ïò _Environment_ ÖÐ×·¼ÓÁËÒ»¸öÃû³ÆÎª`random`µÄ _PropertySource_£¬¼´`RandomValuePropertySource`¡£ÄÚÈÝÈçÏ£º + +``` +public class RandomValuePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 1; + private final Log logger; + + public RandomValuePropertySourceEnvironmentPostProcessor(Log logger) { + this.logger = logger; + } + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + RandomValuePropertySource.addToEnvironment(environment, this.logger); + } +} +¸´ÖÆ´úÂë +``` + +ÄÇôÕâ¸ö _RandomValuePropertySource_ ÓÐɶ×÷ÓÃÄØ£¿Ö÷Òª¾ÍÊÇÓÃÓÚÉú³ÉËæ»úÊý£¬±ÈÈ磺`environment.getProperty("random.int(5,10)")`¿ÉÒÔ»ñȡһ¸öËæ»úÊý¡£ÒÔ`random.int`ΪÊôÐÔÃû¿ÉÒÔ»ñȡһ¸ö _int_ ÀàÐ͵ÄËæ»úÊý£»ÒÔ`random.long`ΪÊôÐÔÃû¿ÉÒÔ»ñȡһ¸ö _long_ ÀàÐ͵ÄËæ»úÊý£»ÒÔ`random.int(5,10)`ΪÊôÐÔÃû¿ÉÒÔ»ñȡһ¸ö _[5, 10}_ Çø¼äÄÚ _int_ ÀàÐ͵ÄËæ»úÊý£¬¸ü¶àÍæ·¨´ó¼Ò×ÔÐÐ̽Ë÷¡£ + +_SystemEnvironmentPropertySourceEnvironmentPostProcessor_ + +µ±Ç°£¬_Environment_ ÖÐÒѾ­´æÔÚÒ»¸öÃû³ÆÎª`systemEnvironment`µÄ _PropertySource_£¬¼´`SystemEnvironmentPropertySource`¡£`SystemEnvironmentPropertySourceEnvironmentPostProcessor`ÓÃÓÚ½«¸Ã _SystemEnvironmentPropertySource_ Ìæ»»Îª`OriginAwareSystemEnvironmentPropertySource`£¬Õ¦Óе㡰ÍÑ¿ã×Ó·ÅÆ¨£¬¶à´ËÒ»¾Ù¡±µÄ¸Ð¾õÄØ£¬¹þ¹þ¡£ + +``` +public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + public static final int DEFAULT_ORDER = SpringApplicationJsonEnvironmentPostProcessor.DEFAULT_ORDER - 1; + private int order = DEFAULT_ORDER; + + @Override + public int getOrder() { + return this.order; + } + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String sourceName = "systemEnvironment"; + PropertySource propertySource = environment.getPropertySources().get(sourceName); + if (propertySource != null) { + replacePropertySource(environment, sourceName, propertySource, application.getEnvironmentPrefix()); + } + } + private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, + PropertySource propertySource, String environmentPrefix) { + Map originalSource = (Map) propertySource.getSource(); + SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource, environmentPrefix); + environment.getPropertySources().replace(sourceName, source); + } +} +¸´ÖÆ´úÂë +``` + +**SpringApplicationJsonEnvironmentPostProcessor** + +ÎÒÃÇÔÚͨ¹ý`java -jar -Dspring.application.json={"name":"duxiaotou"} app.jar`Æô¶¯ Spring Boot Ó¦ÓõÄʱºò£¬¸ÃÊôÐԻᱻ×Ô¶¯Ìí¼Óµ½ JVM ϵͳÊôÐÔÖÐ (Æäʵ _-Dkey=value_ ÕâÖÖÐÎʽµÄÊôÐÔ¾ùÊÇÈç´Ë)£¬ÆäµÈЧÓÚ`System.setProperty(key, value)`£»¶øµ±´æÔÚ`SPRING_APPLICATION_JSON`Õâһϵͳ±äÁ¿Ê±£¬×ÔȻҲ»áÔÚ`System.getenv()`ÖгöÏÖ¡£Ç°ÃæÔø¾­Ìáµ½¹ý`System.getProperties()`´ú±íµÄÊÇ`systemProperties`ÕâÒ» _PropertySource_£¬¶ø`System.getenv()`Ôò´ú±íµÄÊÇ`systemEnvironment`ÕâÒ» _PropertySource_¡£`SpringApplicationJsonEnvironmentPostProcessor`¾ÍÊÇÓÃÓÚ´ÓÕâÁ½¸ö _PropertySource_ ÖгéÈ¡³ö _spring.application.json_ »ò _SPRING_APPLICATION_JSON_ µÄ _JSON_ ´®£¬½ø¶øµ¥¶ÀÏò _Environment_ ÖÐ×·¼ÓÒ»¸öÃû³ÆÎª`spring.application.json`µÄ _PropertySource_£¬¼´`JsonPropertySource`¡£ + +**ConfigDataEnvironmentPostProcessor** + +`ConfigDataEnvironmentPostProcessor`¸ºÔð½«`optional:classpath:/`¡¢`optional:classpath:/config/`¡¢`optional:file:./`¡¢`optional:file:./config/`ºÍ`optional:file:./config/*/`ÕâЩĿ¼Ï嵀 _application.properties_ ÅäÖÃÎļþ¼ÓÔØ³öÀ´£»Èç¹û»¹Ö¸¶¨ÁË _spring.profiles.active_µÄ»°£¬Í¬Ê±Ò²»á½«ÕâЩĿ¼Ï嵀 _application-{profile}.properties_ ÅäÖÃÎļþ¼ÓÔØ³öÀ´¡£×îÖÕ£¬_ConfigDataEnvironmentPostProcessor_ ½«»áÏò _Environment_ ÖÐ×·¼ÓÁ½¸ö`OriginTrackedMapPropertySource`£¬ÕâÁ© _PropertySource_ λÓÚ _Environment_ µÄβ²¿£»ÆäÖÐ _application-{profile}.properties_ Ëù´ú±íµÄ _OriginTrackedMapPropertySource_ ÊÇÅÅÔÚ _application.properties_ Ëù´ú±íµÄ _OriginTrackedMapPropertySource_ Ç°ÃæµÄ£¬ÕâÒ»µãÍ¦ÖØÒª¡£ + +## 3 jasypt ºËÐÄÔ­Àí½â¶Á + +> `jasypt`»ù´¡×é¼þ¿âÓë`jasypt-spring-boot-starter`ÊDz»Í¬×÷ÕßдµÄ£¬ºóÕßÖ»ÊÇΪ jasypt ×é¼þ¿ª·¢ÁË Spring Boot µÄÆð²½ÒÀÀµ×é¼þ¶øÒÑ¡£±¾ÎÄËù·ÖÎöµÄÆäʵ¾ÍÊÇÕâ¸öÆð²½ÒÀÀµ×é¼þ¡£ + +_application.properties_ ÅäÖÃÎļþÖйØÓÚÊý¾ÝÔ´µÄÃÜÂëÊÇÒ»¸ö¼ÓÃܺóµÄÃÜÎÄ£¬ÈçÏ£º + +``` +spring.datasource.hikari.password=ENC(4+t9a5QG8NkNdWVS6UjIX3dj18UtYRMqU6eb3wUKjivOiDHFLZC/RTK7HuWWkUtV) +¸´ÖÆ´úÂë +``` + +µ±`HikariDataSource`Íê³ÉÊôÐÔÌî³ä²Ù×÷ºó£¬¸Ã Bean ÖÐ _password_ ×ֶεÄÖµÕ¦¾Í±äΪ½âÃܺóµÄ _qwe@1234_ ÕâÒ»Ã÷ÎÄÁËÄØ£¿ÏÔÈ»£¬Spring Boot Ϊ _Environment_ ÌṩµÄ`EnvironmentPostProcessor`ÕâÒ»ÍØÕ¹µã¿ÉÒÔʵÏÖ͵Ìì»»ÈÕ£¡µ«×÷ÕßûÓÐÓÃËü£¬¶øÊÇʹÓÃÁË Spring ÖеÄÒ»¸ö _IoC ÍØÕ¹µã_£¬¼´`BeanFactoryPostProcessor`£¬ÕâÒ²ÊÇÍêÈ«¿ÉÒԵģ¬ÒòΪµ±Ö´Ðе½ _BeanFactoryPostProcessor_ ÖеÄ`postProcessBeanFactory()`Âß¼­Ê±£¬Ö»ÊÇÍê³ÉÁËËùÓÐ`BeanDefinition`µÄ¼ÓÔØ£¬µ«»¹Ã»ÓÐʵÀý»¯ _BeanDefinition_ ¸÷×ÔËù¶ÔÓ¦µÄ Bean¡£ + +ÏÂÃæ¿´Ò»ÏÂ`EnableEncryptablePropertiesBeanFactoryPostProcessor`ÖеÄÄÚÈÝ£º + +``` +public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { + + private final ConfigurableEnvironment environment; + private final EncryptablePropertySourceConverter converter; + + public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) { + this.environment = environment; + this.converter = converter; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + MutablePropertySources propSources = environment.getPropertySources(); + converter.convertPropertySources(propSources); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE - 100; + } +} +¸´ÖÆ´úÂë +``` + +ÉÏÊöÔ´Âë±íÃ÷¸Ã _BeanFactoryPostProcessor_ ½èÖú`EncryptablePropertySourceConverter`¶Ô _MutablePropertySources_ ×öÁËÒ»²ãת»»£¬ÄÇôת»»³ÉɶÁËÄØ£¿ + +½Ó×Å£¬¸ú½ø _EncryptablePropertySourceConverter_£¬ºËÐÄÄÚÈÝÈçÏ£º + +``` +public class EncryptablePropertySourceConverter { + + public void convertPropertySources(MutablePropertySources propSources) { + propSources.stream() + .filter(ps -> !(ps instanceof EncryptablePropertySource)) + .map(this::makeEncryptable) + .collect(toList()) + .forEach(ps -> propSources.replace(ps.getName(), ps)); + } + + public PropertySource makeEncryptable(PropertySource propertySource) { + if (propertySource instanceof EncryptablePropertySource + || skipPropertySourceClasses.stream().anyMatch(skipClass -> skipClass.equals(propertySource.getClass()))) { + return propertySource; + } + PropertySource encryptablePropertySource = convertPropertySource(propertySource); + return encryptablePropertySource; + } + + private PropertySource convertPropertySource(PropertySource propertySource) { + PropertySource encryptablePropertySource; + if (propertySource instanceof SystemEnvironmentPropertySource) { + encryptablePropertySource = (PropertySource) new EncryptableSystemEnvironmentPropertySourceWrapper((SystemEnvironmentPropertySource) propertySource, propertyResolver, propertyFilter); + } else if (propertySource instanceof MapPropertySource) { + encryptablePropertySource = (PropertySource) new EncryptableMapPropertySourceWrapper((MapPropertySource) propertySource, propertyResolver, propertyFilter); + } else if (propertySource instanceof EnumerablePropertySource) { + encryptablePropertySource = new EncryptableEnumerablePropertySourceWrapper<>((EnumerablePropertySource) propertySource, propertyResolver, propertyFilter); + } else { + encryptablePropertySource = new EncryptablePropertySourceWrapper<>(propertySource, propertyResolver, propertyFilter); + } + return encryptablePropertySource; + } +} +¸´ÖÆ´úÂë +``` + +ÏÔÈ»£¬Ëü½«Ïà¹ØÔ­Éú _PropertySource_ ת»»ÎªÁËÒ»¸ö`EncryptablePropertySourceWrapper`£¬ÄÇÕâ¸ö¿Ï¶¨¿ÉÒÔʵÏÖÃÜÎĽâÃÜ£¬±ØÐëµÄ£¡ + +¼ÌÐø£¬¸ú½ø`EncryptablePropertySourceWrapper`£¬ÄÚÈÝÈçÏ£º + +``` +public class EncryptablePropertySourceWrapper extends PropertySource implements EncryptablePropertySource { + private final CachingDelegateEncryptablePropertySource encryptableDelegate; + + public EncryptablePropertySourceWrapper(PropertySource delegate, EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter) { + super(delegate.getName(), delegate.getSource()); + encryptableDelegate = new CachingDelegateEncryptablePropertySource<>(delegate, resolver, filter); + } + + @Override + public Object getProperty(String name) { + return encryptableDelegate.getProperty(name); + } + + @Override + public PropertySource getDelegate() { + return encryptableDelegate; + } +} +¸´ÖÆ´úÂë +``` + +ʧÍû£¡Ã»¿´³öɶ½âÃÜÂß¼­£¬µ«´ÓÆä _getProperty_ ·½·¨À´¿´£¬½«¾ßÌå½âÎöÂß¼­Î¯ÅɸøÁË`CachingDelegateEncryptablePropertySource`¡£ + +û°ì·¨£¬Ö»Äܵ½ _CachingDelegateEncryptablePropertySource_ ÖÐһ̽¾¿¾¹ÁË£º + +``` +public class CachingDelegateEncryptablePropertySource extends PropertySource implements EncryptablePropertySource { + private final PropertySource delegate; + private final EncryptablePropertyResolver resolver; + private final EncryptablePropertyFilter filter; + private final Map cache; + + public CachingDelegateEncryptablePropertySource(PropertySource delegate, EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter) { + super(delegate.getName(), delegate.getSource()); + this.delegate = delegate; + this.resolver = resolver; + this.filter = filter; + this.cache = new HashMap<>(); + } + + @Override + public PropertySource getDelegate() { + return delegate; + } + + @Override + public Object getProperty(String name) { + if (cache.containsKey(name)) { + return cache.get(name); + } + synchronized (name.intern()) { + if (!cache.containsKey(name)) { + Object resolved = getProperty(resolver, filter, delegate, name); + if (resolved != null) { + cache.put(name, resolved); + } + } + return cache.get(name); + } + } +} +¸´ÖÆ´úÂë +``` + +ÖÕÓÚ£¬¸ú½øµ½`EncryptablePropertySource`Öп´µ½Á˽âÃܵÄ×îÖÕÂß¼­¡£ÆäÖУ¬`EncryptablePropertyDetector`¸ºÔð̽²âÏà¹ØÊôÐÔÊÇ·ñÐèÒª¶ÔÆä½âÃÜ£¬Ö÷Ҫͨ¹ýÅжϸÃÊôÐÔÖµÊÇ·ñÓÉ`ENC()`°ü¹ü¡£ + +``` +public interface EncryptablePropertySource extends OriginLookup { + default Object getProperty(EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter, PropertySource source, String name) { + Object value = source.getProperty(name); + if (value != null && filter.shouldInclude(source, name) && value instanceof String) { + String stringValue = String.valueOf(value); + return resolver.resolvePropertyValue(stringValue); + } + return value; + } +} + +public class DefaultPropertyResolver implements EncryptablePropertyResolver { + + private final Environment environment; + private StringEncryptor encryptor; + private EncryptablePropertyDetector detector; + + @Override + public String resolvePropertyValue(String value) { + return Optional.ofNullable(value) + .map(environment::resolvePlaceholders) + .filter(detector::isEncrypted) + .map(resolvedValue -> { + try { + String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); + String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty); + return encryptor.decrypt(resolvedProperty); + } catch (EncryptionOperationNotPossibleException e) { + throw new DecryptionException("Unable to decrypt property: " + value + " resolved to: " + resolvedValue + ". Decryption of Properties failed, make sure encryption/decryption " + + "passwords match", e); + } + }) + .orElse(value); + } +} +¸´ÖÆ´úÂë +``` + +## 4 ×ܽá + +×ܽáÐÔµÄÎÄ×־Ͳ»ÔÙ˵ÁË£¬±ÊÕßÏÖÔÚÎÄ˼Ȫӿ£¬·ñÔòÓÖÄÜË®300×Ö¡£×îºó£¬Ï£Íû´ó¼Ò¼ÇסÔÚµ±Ç° Spring Boot °æ±¾ÖУ¬ÓÉ`ApplicationServletEnvironment`°çÑÝ _Environment_£¬Æä×îÖÕ½«Î¯ÅÉ`ConfigurationPropertySourcesPropertyResolver`È¥»ñÈ¡ÊôÐÔÖµ¡£ + + + +×÷Õߣº³ÌÐòÔ³¶ÅСͷ +Á´½Ó£ºhttps://juejin.cn/post/7098299623759937543 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + +# ²Î¿¼ÎÄÕ + +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\344\272\213\344\273\266\345\244\204\347\220\206\346\234\272\345\210\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\344\272\213\344\273\266\345\244\204\347\220\206\346\234\272\345\210\266.md" new file mode 100644 index 0000000..98c55c3 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\344\272\213\344\273\266\345\244\204\347\220\206\346\234\272\345\210\266.md" @@ -0,0 +1,270 @@ + + + + +# Spring ÖеÄʼþ´¦Àí + + + +2022-05-16 15:29 ¸üР+ + + + + + + + + +## Spring ÖеÄʼþ´¦Àí + +ÄãÒѾ­¿´µ½ÁËÔÚËùÓÐÕ½ÚÖÐ Spring µÄºËÐÄÊÇ **ApplicationContext**£¬Ëü¸ºÔð¹ÜÀí beans µÄÍêÕûÉúÃüÖÜÆÚ¡£µ±¼ÓÔØ beans ʱ£¬ApplicationContext ·¢²¼Ä³Ð©ÀàÐ͵Äʼþ¡£ÀýÈ磬µ±ÉÏÏÂÎÄÆô¶¯Ê±£¬ContextStartedEvent ·¢²¼£¬µ±ÉÏÏÂÎÄֹͣʱ£¬ContextStoppedEvent ·¢²¼¡£ + +ͨ¹ý ApplicationEvent ÀàºÍ ApplicationListener ½Ó¿ÚÀ´ÌṩÔÚ ApplicationContext Öд¦Àíʼþ¡£Èç¹ûÒ»¸ö bean ʵÏÖ ApplicationListener£¬ÄÇôÿ´Î ApplicationEvent ±»·¢²¼µ½ ApplicationContext ÉÏ£¬ÄǸö bean »á±»Í¨Öª¡£ + +Spring ÌṩÁËÒÔϵıê׼ʼþ£º + +| ÐòºÅ | Spring ÄÚÖÃʼþ & ÃèÊö | +| --- | --- | +| 1 | **ContextRefreshedEvent**ApplicationContext ±»³õʼ»¯»òË¢ÐÂʱ£¬¸Ãʼþ±»·¢²¼¡£ÕâÒ²¿ÉÒÔÔÚ ConfigurableApplicationContext ½Ó¿ÚÖÐʹÓà refresh() ·½·¨À´·¢Éú¡£ | +| 2 | **ContextStartedEvent**µ±Ê¹Óà ConfigurableApplicationContext ½Ó¿ÚÖÐµÄ start() ·½·¨Æô¶¯ ApplicationContext ʱ£¬¸Ãʼþ±»·¢²¼¡£Äã¿ÉÒÔµ÷²éÄãµÄÊý¾Ý¿â£¬»òÕßÄã¿ÉÒÔÔÚ½ÓÊܵ½Õâ¸öʼþºóÖØÆôÈκÎÍ£Ö¹µÄÓ¦ÓóÌÐò¡£ | +| 3 | **ContextStoppedEvent**µ±Ê¹Óà ConfigurableApplicationContext ½Ó¿ÚÖÐµÄ stop() ·½·¨Í£Ö¹ ApplicationContext ʱ£¬·¢²¼Õâ¸öʼþ¡£Äã¿ÉÒÔÔÚ½ÓÊܵ½Õâ¸öʼþºó×ö±ØÒªµÄÇåÀíµÄ¹¤×÷¡£ | +| 4 | **ContextClosedEvent**µ±Ê¹Óà ConfigurableApplicationContext ½Ó¿ÚÖÐµÄ close() ·½·¨¹Ø±Õ ApplicationContext ʱ£¬¸Ãʼþ±»·¢²¼¡£Ò»¸öÒѹرյÄÉÏÏÂÎĵ½´ïÉúÃüÖÜÆÚÄ©¶Ë£»Ëü²»Äܱ»Ë¢Ð»òÖØÆô¡£ | +| 5 | **RequestHandledEvent**ÕâÊÇÒ»¸ö web-specific ʼþ£¬¸æËßËùÓÐ bean HTTP ÇëÇóÒѾ­±»·þÎñ¡£ | + +ÓÉÓÚ Spring µÄʼþ´¦ÀíÊǵ¥Ï̵߳ģ¬ËùÒÔÈç¹ûÒ»¸öʼþ±»·¢²¼£¬Ö±ÖÁ²¢ÇÒ³ý·ÇËùÓеĽÓÊÕÕߵõ½µÄ¸ÃÏûÏ¢£¬¸Ã½ø³Ì±»×èÈû²¢ÇÒÁ÷³Ì½«²»»á¼ÌÐø¡£Òò´Ë£¬Èç¹ûʼþ´¦Àí±»Ê¹Óã¬ÔÚÉè¼ÆÓ¦ÓóÌÐòʱӦעÒâ¡£ + +## ¼àÌýÉÏÏÂÎÄʼþ + +ΪÁ˼àÌýÉÏÏÂÎÄʼþ£¬Ò»¸ö bean Ó¦¸ÃʵÏÖÖ»ÓÐÒ»¸ö·½·¨ **onApplicationEvent()** µÄ ApplicationListener ½Ó¿Ú¡£Òò´Ë£¬ÎÒÃÇдһ¸öÀý×ÓÀ´¿´¿´Ê¼þÊÇÈçºÎ´«²¥µÄ£¬ÒÔ¼°ÈçºÎ¿ÉÒÔÓôúÂëÀ´Ö´ÐлùÓÚijЩʼþËùÐèµÄÈÎÎñ¡£ + +ÈÃÎÒÃÇÔÚÇ¡µ±µÄλÖÃʹÓà Eclipse IDE£¬È»ºó°´ÕÕÏÂÃæµÄ²½ÖèÀ´´´½¨Ò»¸ö Spring Ó¦ÓóÌÐò£º + +| ²½Öè | ÃèÊö | +| --- | --- | +| 1 | ´´½¨Ò»¸öÃû³ÆÎª SpringExample µÄÏîÄ¿£¬²¢ÇÒÔÚ´´½¨ÏîÄ¿µÄ **src** Îļþ¼ÐÖд´½¨Ò»¸ö°ü com.tutorialspoint¡£ | +| 2 | ʹÓà Add External JARs Ñ¡ÏÌí¼ÓËùÐèµÄ Spring ¿â£¬½âÊͼû Spring Hello World Example Õ½ڡ£ | +| 3 | ÔÚ com.tutorialspoint °üÖд´½¨ Java Àà HelloWorld¡¢CStartEventHandler¡¢CStopEventHandler ºÍ MainApp¡£ | +| 4 | ÔÚ **src** Îļþ¼ÐÖд´½¨ Bean µÄÅäÖÃÎļþ Beans.xml¡£ | +| 5 | ×îºóÒ»²½ÊÇ´´½¨µÄËùÓÐ Java ÎļþºÍ Bean ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢ÔËÐÐÓ¦ÓóÌÐò£¬½âÊÍÈçÏÂËùʾ¡£ | + +ÕâÀïÊÇ **HelloWorld.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +public class HelloWorld { + private String message; + public void setMessage(String message){ + this.message = message; + } + public void getMessage(){ + System.out.println("Your Message : " + message); + } +} +``` + +ÏÂÃæÊÇ **CStartEventHandler.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextStartedEvent; +public class CStartEventHandler + implements ApplicationListener{ + public void onApplicationEvent(ContextStartedEvent event) { + System.out.println("ContextStartedEvent Received"); + } +} +``` + +ÏÂÃæÊÇ **CStopEventHandler.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextStoppedEvent; +public class CStopEventHandler + implements ApplicationListener{ + public void onApplicationEvent(ContextStoppedEvent event) { + System.out.println("ContextStoppedEvent Received"); + } +} +``` + +ÏÂÃæÊÇ **MainApp.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class MainApp { + public static void main(String[] args) { + ConfigurableApplicationContext context = + new ClassPathXmlApplicationContext("Beans.xml"); + + // Let us raise a start event. + context.start(); + + HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); + + obj.getMessage(); + + // Let us raise a stop event. + context.stop(); + } +} +``` + +ÏÂÃæÊÇÅäÖÃÎļþ **Beans.xml** Îļþ£º + +``` + + + + + + + + + + + + + +``` + +Ò»µ©ÄãÍê³ÉÁË´´½¨Ô´ºÍ bean µÄÅäÖÃÎļþ£¬ÎÒÃǾͿÉÒÔÔËÐиÃÓ¦ÓóÌÐò¡£Èç¹ûÄãµÄÓ¦ÓóÌÐòÒ»Çж¼Õý³££¬½«Êä³öÒÔÏÂÏûÏ¢£º + +``` +ContextStartedEvent Received +Your Message : Hello World! +ContextStoppedEvent Received +``` + + + + + + + +## Spring ÖеÄ×Ô¶¨Òåʼþ + +±àдºÍ·¢²¼×Ô¼ºµÄ×Ô¶¨ÒåʼþÓÐÐí¶à²½Öè¡£°´ÕÕÔÚÕâÒ»Õ¸ø³öµÄ˵Ã÷À´±àд£¬·¢²¼ºÍ´¦Àí×Ô¶¨Òå Spring ʼþ¡£ + +| ²½Öè | ÃèÊö | +| --- | --- | +| 1 | ´´½¨Ò»¸öÃû³ÆÎª SpringExample µÄÏîÄ¿£¬²¢ÇÒÔÚ´´½¨ÏîÄ¿µÄ **src** Îļþ¼ÐÖд´½¨Ò»¸ö°ü com.tutorialspoint¡£ | +| 2 | ʹÓà Add External JARs Ñ¡ÏÌí¼ÓËùÐèµÄ Spring ¿â£¬½âÊͼû Spring Hello World Example Õ½ڡ£ | +| 3 | ͨ¹ýÀ©Õ¹ **ApplicationEvent**,´´½¨Ò»¸öʼþÀà CustomEvent¡£Õâ¸öÀà±ØÐ붨ÒåÒ»¸öĬÈϵĹ¹Ô캯Êý£¬ËüÓ¦¸Ã´Ó ApplicationEvent ÀàÖм̳еĹ¹Ô캯Êý¡£ | +| 4 | Ò»µ©¶¨ÒåʼþÀ࣬Äã¿ÉÒÔ´ÓÈκÎÀàÖз¢²¼Ëü£¬¼Ù¶¨ EventClassPublisher ʵÏÖÁË ApplicationEventPublisherAware¡£Ä㻹ÐèÒªÔÚ XML ÅäÖÃÎļþÖÐÉùÃ÷Õâ¸öÀà×÷Ϊһ¸ö bean£¬Ö®ËùÒÔÈÝÆ÷¿ÉÒÔʶ±ð bean ×÷Ϊʼþ·¢²¼Õߣ¬ÊÇÒòΪËüʵÏÖÁË ApplicationEventPublisherAware ½Ó¿Ú¡£ | +| 5 | ·¢²¼µÄʼþ¿ÉÒÔÔÚÒ»¸öÀàÖб»´¦Àí£¬¼Ù¶¨ EventClassHandler ʵÏÖÁË ApplicationListener ½Ó¿Ú£¬¶øÇÒʵÏÖÁË×Ô¶¨ÒåʼþµÄ onApplicationEvent ·½·¨¡£ | +| 6 | ÔÚ **src** Îļþ¼ÐÖд´½¨ bean µÄÅäÖÃÎļþ Beans.xml ºÍ MainApp À࣬Ëü¿ÉÒÔ×÷Ϊһ¸ö Spring Ó¦ÓóÌÐòÀ´ÔËÐС£ | +| 7 | ×îºóÒ»²½ÊÇ´´½¨µÄËùÓÐ Java ÎļþºÍ Bean ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢ÔËÐÐÓ¦ÓóÌÐò£¬½âÊÍÈçÏÂËùʾ¡£ | + +Õâ¸öÊÇ **CustomEvent.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ApplicationEvent; +public class CustomEvent extends ApplicationEvent{ + public CustomEvent(Object source) { + super(source); + } + public String toString(){ + return "My Custom Event"; + } +} + +``` + +ÏÂÃæÊÇ **CustomEventPublisher.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +public class CustomEventPublisher + implements ApplicationEventPublisherAware { + private ApplicationEventPublisher publisher; + public void setApplicationEventPublisher + (ApplicationEventPublisher publisher){ + this.publisher = publisher; + } + public void publish() { + CustomEvent ce = new CustomEvent(this); + publisher.publishEvent(ce); + } +} +``` + +ÏÂÃæÊÇ **CustomEventHandler.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ApplicationListener; +public class CustomEventHandler + implements ApplicationListener{ + public void onApplicationEvent(CustomEvent event) { + System.out.println(event.toString()); + } +} +``` + +ÏÂÃæÊÇ **MainApp.java** ÎļþµÄÄÚÈÝ£º + +``` +package com.tutorialspoint; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +public class MainApp { + public static void main(String[] args) { + ConfigurableApplicationContext context = + new ClassPathXmlApplicationContext("Beans.xml"); + CustomEventPublisher cvp = + (CustomEventPublisher) context.getBean("customEventPublisher"); + cvp.publish(); + cvp.publish(); + } +} +``` + +ÏÂÃæÊÇÅäÖÃÎļþ **Beans.xml**£º + +``` + + + + + + + + + +``` + +Ò»µ©ÄãÍê³ÉÁË´´½¨Ô´ºÍ bean µÄÅäÖÃÎļþºó£¬ÎÒÃǾͿÉÒÔÔËÐиÃÓ¦ÓóÌÐò¡£Èç¹ûÄãµÄÓ¦ÓóÌÐòÒ»Çж¼Õý³££¬½«Êä³öÒÔÏÂÐÅÏ¢£º + +``` +My Custom Event +My Custom Event +``` + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\350\265\204\346\272\220\347\256\241\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\350\265\204\346\272\220\347\256\241\347\220\206.md" new file mode 100644 index 0000000..09874af --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\350\265\204\346\272\220\347\256\241\347\220\206.md" @@ -0,0 +1,309 @@ +## Resource ½Ó¿Ú + +Ïà¶Ô±ê×¼ URL ·ÃÎÊ»úÖÆ£¬Spring µÄ `org.springframework.core.io.Resource` ½Ó¿Ú³éÏóÁ˶Եײã×ÊÔ´µÄ·ÃÎʽӿڣ¬ÌṩÁËÒ»Ì׸üºÃµÄ·ÃÎÊ·½Ê½¡£ + + + +``` +public interface Resource extends InputStreamSource { + + boolean exists(); + + boolean isReadable(); + + boolean isOpen(); + + boolean isFile(); + + URL getURL() throws IOException; + + URI getURI() throws IOException; + + File getFile() throws IOException; + + ReadableByteChannel readableChannel() throws IOException; + + long contentLength() throws IOException; + + long lastModified() throws IOException; + + Resource createRelative(String relativePath) throws IOException; + + String getFilename(); + + String getDescription(); +} + +``` + + + +ÕýÈç `Resource` ½Ó¿ÚµÄ¶¨ÒåËùʾ£¬ËüÀ©Õ¹ÁË `InputStreamSource` ½Ó¿Ú¡£`Resource` ×îºËÐĵķ½·¨ÈçÏ£º + +* `getInputStream()` - ¶¨Î»²¢ÇÒ´ò¿ªµ±Ç°×ÊÔ´£¬·µ»Øµ±Ç°×ÊÔ´µÄ `InputStream`¡£Ã¿´Îµ÷Óö¼»á·µ»ØÒ»¸öÐ嵀 `InputStream`¡£µ÷ÓÃÕßÐèÒª¸ºÔð¹Ø±ÕÁ÷¡£ +* `exists()` - Åжϵ±Ç°×ÊÔ´ÊÇ·ñÕæµÄ´æÔÚ¡£ +* `isOpen()` - Åжϵ±Ç°×ÊÔ´ÊÇ·ñÊÇÒ»¸öÒÑ´ò¿ªµÄ `InputStream`¡£Èç¹ûΪ true£¬Ôò `InputStream` ²»Äܱ»¶à´Î¶ÁÈ¡£¬±ØÐëÖ»¶Áȡһ´ÎÈ»ºó¹Ø±ÕÒÔ±ÜÃâ×ÊԴй©¡£¶ÔËùÓг£ÓÃ×ÊԴʵÏÖ·µ»Ø false£¬`InputStreamResource` ³ýÍâ¡£ +* `getDescription()` - ·µ»Øµ±Ç°×ÊÔ´µÄÃèÊö£¬µ±´¦Àí×ÊÔ´³ö´íʱ£¬×ÊÔ´µÄÃèÊö»áÓÃÓÚ´íÎóÐÅÏ¢µÄÊä³ö¡£Ò»°ãÀ´Ëµ£¬×ÊÔ´µÄÃèÊöÊÇÒ»¸öÍêÈ«ÏÞ¶¨µÄÎļþÃû³Æ£¬»òÕßÊǵ±Ç°×ÊÔ´µÄÕæÊµ URL¡£ + +³£¼û Spring ×ÊÔ´½Ó¿Ú£º + +| ÀàÐÍ | ½Ó¿Ú | +| --- | --- | +| ÊäÈëÁ÷ | `org.springframework.core.io.InputStreamSource` | +| Ö»¶Á×ÊÔ´ | `org.springframework.core.io.Resource` | +| ¿Éд×ÊÔ´ | `org.springframework.core.io.WritableResource` | +| ±àÂë×ÊÔ´ | `org.springframework.core.io.support.EncodedResource` | +| ÉÏÏÂÎÄ×ÊÔ´ | `org.springframework.core.io.ContextResource` | + + + +![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20221223155859.png) + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E5%86%85%E7%BD%AE%E7%9A%84-resource-%E5%AE%9E%E7%8E%B0)ÄÚÖÃµÄ Resource ʵÏÖ + +Spring °üÀ¨¼¸¸öÄÚÖÃµÄ Resource ʵÏÖ£º + +| ×ÊÔ´À´Ô´ | ǰ׺ | ˵Ã÷ | +| --- | --- | --- | +| [`UrlResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-urlresource) | `file:`¡¢`https:`¡¢`ftp:` µÈ | `UrlResource` ·â×°ÁËÒ»¸ö `java.net.URL` ¶ÔÏó£¬**ÓÃÓÚ·ÃÎÊ¿Éͨ¹ý URL ·ÃÎʵÄÈκζÔÏó**£¬ÀýÈçÎļþ¡¢HTTPS Ä¿±ê¡¢FTP Ä¿±êµÈ¡£ËùÓÐ URL ¶¼¿ÉÒÔͨ¹ý±ê×¼»¯µÄ×Ö·û´®ÐÎʽ±íʾ£¬Òò´Ë¿ÉÒÔʹÓÃÊʵ±µÄ±ê×¼»¯Ç°×ºÀ´Ö¸Ê¾Ò»ÖÖ URL ÀàÐÍÓëÁíÒ»ÖÖ URL ÀàÐ͵ÄÇø±ð¡£ Õâ°üÀ¨£º`file`£ºÓÃÓÚ·ÃÎÊÎļþϵͳ·¾¶£»`https`£ºÓÃÓÚͨ¹ý HTTPS ЭÒé·ÃÎÊ×ÊÔ´£»`ftp`£ºÓÃÓÚͨ¹ý FTP ·ÃÎÊ×ÊÔ´µÈµÈ¡£ | +| [`ClassPathResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-classpathresource) | `classpath:` | `ClassPathResource` **´ÓÀà·¾¶ÉϼÓÔØ×ÊÔ´**¡£ËüʹÓÃÏß³ÌÉÏÏÂÎļÓÔØÆ÷¡¢¸ø¶¨µÄÀà¼ÓÔØÆ÷»òÖ¸¶¨µÄ class ÀàÐÍÖеÄÈÎÒâÒ»¸öÀ´¼ÓÔØ×ÊÔ´¡£ | +| [`FileSystemResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-filesystemresource) | `file:` | `FileSystemResource` **ÊÇ `java.io.File` µÄ×ÊԴʵÏÖ**¡£Ëü»¹Ö§³Ö `java.nio.file.Path` £¬Ó¦Óà Spring µÄ±ê×¼¶Ô×Ö·û´®Â·¾¶½øÐÐת»»¡£`FileSystemResource` Ö§³Ö½âÎöΪÎļþºÍ URL¡£ | +| [`PathResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-pathresource) | ÎÞ | `PathResource` ÊÇ `java.nio.file.Path` µÄ×ÊԴʵÏÖ¡£ | +| [`ServletContextResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-servletcontextresource) | ÎÞ | `ServletContextResource` **ÊÇ `ServletContext` µÄ×ÊԴʵÏÖ**¡£Ëü±íʾÏàÓ¦ Web Ó¦ÓóÌÐò¸ùĿ¼ÖеÄÏà¶Ô·¾¶¡£ | +| [`InputStreamResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-inputstreamresource) | ÎÞ | `InputStreamResource` **ÊÇÖ¸¶¨ `InputStream` µÄ×ÊԴʵÏÖ**¡£×¢Ò⣺Èç¹û¸Ã `InputStream` Òѱ»´ò¿ª£¬Ôò²»¿ÉÒÔ¶à´Î¶ÁÈ¡¸ÃÁ÷¡£ | +| [`ByteArrayResource`(opens new window)](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations-bytearrayresource) | ÎÞ | `ByteArrayResource` ÊÇÖ¸¶¨µÄ¶þ½øÖÆÊý×éµÄ×ÊԴʵÏÖ¡£Ëü»áΪ¸ø¶¨µÄ×Ö½ÚÊý×é´´½¨Ò»¸ö `ByteArrayInputStream`¡£ | + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#resourceloader-%E6%8E%A5%E5%8F%A3)ResourceLoader ½Ó¿Ú + +`ResourceLoader` ½Ó¿ÚÓÃÓÚ¼ÓÔØ `Resource` ¶ÔÏ󡣯䶍ÒåÈçÏ£º + + + +``` +public interface ResourceLoader { + + Resource getResource(String location); + + ClassLoader getClassLoader(); +} + +``` + + + +Spring ÖÐÖ÷ÒªµÄ ResourceLoader ʵÏÖ£º + +![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20221223164745.png) + +Spring ÖУ¬ËùÓÐµÄ `ApplicationContext` ¶¼ÊµÏÖÁË `ResourceLoader` ½Ó¿Ú¡£Òò´Ë£¬ËùÓÐ `ApplicationContext` ¶¼¿ÉÒÔͨ¹ý `getResource()` ·½·¨»ñÈ¡ `Resource` ʵÀý¡£ + +¡¾Ê¾Àý¡¿ + + + +``` +// Èç¹ûûÓÐÖ¸¶¨×ÊԴǰ׺£¬Spring »á³¢ÊÔ·µ»ØºÏÊʵÄ×ÊÔ´ +Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); +// Èç¹ûÖ¸¶¨ classpath: ǰ׺£¬Spring »áÇ¿ÖÆÊ¹Óà ClassPathResource +Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); +// Èç¹ûÖ¸¶¨ file:¡¢http µÈ URL ǰ׺£¬Spring »áÇ¿ÖÆÊ¹Óà UrlResource +Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); +Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); + +``` + + + +ϱíÁоÙÁË Spring ¸ù¾Ý¸÷ÖÖλÖ÷¾¶¼ÓÔØ×ÊÔ´µÄ²ßÂÔ£º + +| ǰ׺ | ÑùÀý | ˵Ã÷ | +| --- | --- | --- | +| `classpath:` | `classpath:com/myapp/config.xml` | ´ÓÀà·¾¶¼ÓÔØ | +| `file:` | `file:///data/config.xml` | ÒÔ URL ÐÎʽ´ÓÎļþϵͳ¼ÓÔØ | +| `http:` | `http://myserver/logo.png` | ÒÔ URL ÐÎʽ¼ÓÔØ | +| ÎÞ | `/data/config.xml` | ÓɵײãµÄ ApplicationContext ʵÏÖ¾ö¶¨ | + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#resourcepatternresolver-%E6%8E%A5%E5%8F%A3)ResourcePatternResolver ½Ó¿Ú + +`ResourcePatternResolver` ½Ó¿ÚÊÇ `ResourceLoader` ½Ó¿ÚµÄÀ©Õ¹£¬ËüµÄ×÷ÓÃÊǶ¨Òå²ßÂÔ£¬¸ù¾ÝλÖÃģʽ½âÎö `Resource` ¶ÔÏó¡£ + + + +``` +public interface ResourcePatternResolver extends ResourceLoader { + + String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; + + Resource[] getResources(String locationPattern) throws IOException; +} + +``` + + + +`PathMatchingResourcePatternResolver` ÊÇÒ»¸ö¶ÀÁ¢µÄʵÏÖ£¬¿ÉÒÔÔÚ `ApplicationContext` Ö®ÍâʹÓã¬Ò²¿ÉÒÔ±» `ResourceArrayPropertyEditor` ÓÃÓÚÌî³ä `Resource[]` bean ÊôÐÔ¡£`PathMatchingResourcePatternResolver` Äܹ»½«Ö¸¶¨µÄ×ÊԴλÖ÷¾¶½âÎöΪһ¸ö»ò¶à¸öÆ¥ÅäµÄ `Resource` ¶ÔÏó¡£ + +> ×¢Ò⣺Èκαê×¼ `ApplicationContext` ÖеÄĬÈÏ `ResourceLoader` ʵ¼ÊÉÏÊÇ `PathMatchingResourcePatternResolver` µÄÒ»¸öʵÀý£¬ËüʵÏÖÁË `ResourcePatternResolver` ½Ó¿Ú¡£ + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#resourceloaderaware-%E6%8E%A5%E5%8F%A3)ResourceLoaderAware ½Ó¿Ú + +`ResourceLoaderAware` ½Ó¿ÚÊÇÒ»¸öÌØÊâµÄ»Øµ÷½Ó¿Ú£¬ÓÃÀ´±ê¼ÇÌṩ `ResourceLoader` ÒýÓõĶÔÏó¡£`ResourceLoaderAware` ½Ó¿Ú¶¨ÒåÈçÏ£º + + + +``` +public interface ResourceLoaderAware { + void setResourceLoader(ResourceLoader resourceLoader); +} + +``` + + + +µ±Ò»¸öÀàʵÏÖ `ResourceLoaderAware` ²¢²¿Êðµ½Ó¦ÓóÌÐòÉÏÏÂÎÄÖУ¨×÷Ϊ Spring ¹ÜÀíµÄ bean£©Ê±£¬Ëü»á±»Ó¦ÓóÌÐòÉÏÏÂÎÄʶ±ðΪ `ResourceLoaderAware`£¬È»ºó£¬Ó¦ÓóÌÐòÉÏÏÂÎÄ»áµ÷Óà `setResourceLoader(ResourceLoader)`£¬½«×ÔÉí×÷Ϊ²ÎÊýÌṩ£¨Çë¼Çס£¬Spring ÖеÄËùÓÐÓ¦ÓóÌÐòÉÏÏÂÎĶ¼ÊµÏÖ `ResourceLoader` ½Ó¿Ú£©¡£ + +ÓÉÓÚ `ApplicationContext` ÊÇÒ»¸ö `ResourceLoader`£¬¸Ã bean »¹¿ÉÒÔʵÏÖ `ApplicationContextAware` ½Ó¿Ú²¢Ö±½ÓʹÓÃÌṩµÄÓ¦ÓóÌÐòÉÏÏÂÎÄÀ´¼ÓÔØ×ÊÔ´¡£ µ«ÊÇ£¬Ò»°ãÀ´Ëµ£¬Èç¹ûÄúÖ»ÐèÒªÕâЩ£¬×îºÃʹÓÃרÃÅµÄ `ResourceLoader` ½Ó¿Ú¡£ ¸Ã´úÂ뽫½öñîºÏµ½×ÊÔ´¼ÓÔØ½Ó¿Ú£¨¿ÉÒÔ±»ÊÓΪʵÓóÌÐò½Ó¿Ú£©£¬¶ø²»ñîºÏµ½Õû¸ö Spring `ApplicationContext` ½Ó¿Ú¡£ + +ÔÚÓ¦ÓóÌÐòÖУ¬»¹¿ÉÒÔʹÓà `ResourceLoader` µÄ×Ô¶¯×°Åä×÷ΪʵÏÖ `ResourceLoaderAware` ½Ó¿ÚµÄÌæ´ú·½·¨¡£´«Í³µÄ¹¹Ô캯ÊýºÍ `byType` ×Ô¶¯×°ÅäģʽÄܹ»·Ö±ðΪ¹¹Ô캯Êý²ÎÊý»ò setter ·½·¨²ÎÊýÌṩ `ResourceLoader`¡£ ΪÁË»ñµÃ¸ü´óµÄÁé»îÐÔ£¨°üÀ¨×Ô¶¯×°Åä×ֶκͶà²ÎÊý·½·¨µÄÄÜÁ¦£©£¬Ç뿼ÂÇʹÓûùÓÚ×¢½âµÄ×Ô¶¯×°Å书ÄÜ¡£ ÔÚÕâÖÖÇé¿öÏ£¬`ResourceLoader` »á×Ô¶¯Á¬½Óµ½ÐèÒª `ResourceLoader` ÀàÐ͵Ä×ֶΡ¢¹¹Ô캯Êý²ÎÊý»ò·½·¨²ÎÊýÖУ¬Ö»ÒªÏà¹Ø×ֶΡ¢¹¹Ô캯Êý»ò·½·¨´øÓÐ `@Autowired` ×¢½â¼´¿É¡£ + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E8%B5%84%E6%BA%90%E4%BE%9D%E8%B5%96)×ÊÔ´ÒÀÀµ + +Èç¹û bean ±¾ÉíҪͨ¹ýijÖÖ¶¯Ì¬¹ý³ÌÀ´È·¶¨ºÍÌṩ×ÊԴ·¾¶£¬ÄÇô bean ¿ÉÒÔʹÓà `ResourceLoader` »ò `ResourcePatternResolver` ½Ó¿ÚÀ´¼ÓÔØ×ÊÔ´¡£ ÀýÈ磬¿¼ÂǼÓÔØÄ³ÖÖÄ£°å£¬ÆäÖÐËùÐèµÄÌØ¶¨×ÊÔ´È¡¾öÓÚÓû§µÄ½ÇÉ«¡£ Èç¹û×ÊÔ´ÊǾ²Ì¬µÄ£¬ÍêÈ«Ïû³ý `ResourceLoader` ½Ó¿Ú£¨»ò `ResourcePatternResolver` ½Ó¿Ú£©µÄʹÓã¬Èà bean ¹«¿ªËüÐèÒªµÄ `Resource` ÊôÐÔ£¬²¢ÆÚÍû½«ËüÃÇ×¢ÈëÆäÖÐÊÇÓÐÒâÒåµÄ¡£ + +ʹעÈëÕâЩÊôÐÔ±äµÃ¼òµ¥µÄÔ­ÒòÊÇËùÓÐÓ¦ÓóÌÐòÉÏÏÂÎͼע²á²¢Ê¹ÓÃÒ»¸öÌØÊâµÄ JavaBeans `PropertyEditor`£¬Ëü¿ÉÒÔ½« `String` ·¾¶×ª»»Îª `Resource` ¶ÔÏó¡£ ÀýÈ磬ÏÂÃæµÄ MyBean ÀàÓÐÒ»¸ö `Resource` ÀàÐ͵ÄÄ£°åÊôÐÔ¡£ + +¡¾Ê¾Àý¡¿ + + + +``` + + + + +``` + + + +Çë×¢Ò⣬ÅäÖÃÖÐÒýÓõÄÄ£°å×ÊԴ·¾¶Ã»ÓÐǰ׺£¬ÒòΪӦÓóÌÐòÉÏÏÂÎı¾Éí½«ÓÃ×÷ `ResourceLoader`£¬×ÊÔ´±¾Éí½«¸ù¾ÝÐèҪͨ¹ý `ClassPathResource`£¬`FileSystemResource` »ò ServletContextResource ¼ÓÔØ£¬¾ßÌåÈ¡¾öÓÚÉÏÏÂÎĵÄÈ·ÇÐÀàÐÍ¡£ + +Èç¹ûÐèÒªÇ¿ÖÆÊ¹ÓÃÌØ¶¨µÄ×ÊÔ´ÀàÐÍ£¬Ôò¿ÉÒÔʹÓÃǰ׺¡£ ÒÔÏÂÁ½¸öʾÀýÏÔʾÈçºÎÇ¿ÖÆÊ¹Óà `ClassPathResource` ºÍ `UrlResource`£¨ºóÕßÓÃÓÚ·ÃÎÊÎļþϵͳÎļþ£©¡£ + + + +``` + + + +``` + + + +¿ÉÒÔͨ¹ý `@Value` ×¢½â¼ÓÔØ×ÊÔ´Îļþ `myTemplate.txt`£¬Ê¾ÀýÈçÏ£º + + + +``` +@Component +public class MyBean { + + private final Resource template; + + public MyBean(@Value("${template.path}") Resource template) { + this.template = template; + } + + // ... +} + +``` + + + +Spring µÄ `PropertyEditor` »á¸ù¾Ý×ÊÔ´ÎļþµÄ·¾¶×Ö·û´®£¬¼ÓÔØ `Resource` ¶ÔÏ󣬲¢½«Æä×¢Èëµ½ MyBean µÄ¹¹Ôì·½·¨¡£ + +Èç¹ûÏëÒª¼ÓÔØ¶à¸ö×ÊÔ´Îļþ£¬¿ÉÒÔʹÓà `classpath*:` ǰ׺£¬ÀýÈ磺`classpath*:/config/templates/*.txt`¡£ + + + +``` +@Component +public class MyBean { + + private final Resource[] templates; + + public MyBean(@Value("${templates.path}") Resource[] templates) { + this.templates = templates; + } + + // ... +} + +``` + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E5%BA%94%E7%94%A8%E4%B8%8A%E4%B8%8B%E6%96%87%E5%92%8C%E8%B5%84%E6%BA%90%E8%B7%AF%E5%BE%84)Ó¦ÓÃÉÏÏÂÎĺÍ×ÊԴ·¾¶ + +### [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E6%9E%84%E9%80%A0%E5%BA%94%E7%94%A8%E4%B8%8A%E4%B8%8B%E6%96%87)¹¹ÔìÓ¦ÓÃÉÏÏÂÎÄ + +Ó¦ÓÃÉÏÏÂÎĹ¹Ô캯Êý£¨Õë¶ÔÌØ¶¨µÄÓ¦ÓÃÉÏÏÂÎÄÀàÐÍ£©Í¨³£½«×Ö·û´®»ò×Ö·û´®Êý×é×÷Ϊ×ÊÔ´µÄλÖ÷¾¶£¬ÀýÈç¹¹³ÉÉÏÏÂÎ͍ÒåµÄ XML Îļþ¡£ + +¡¾Ê¾Àý¡¿ + + + +``` +ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); +ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml"); +ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); +ApplicationContext ctx = new ClassPathXmlApplicationContext( + new String[] {"services.xml", "daos.xml"}, MessengerService.class); + +``` + + + +### [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E4%BD%BF%E7%94%A8%E9%80%9A%E9%85%8D%E7%AC%A6%E6%9E%84%E9%80%A0%E5%BA%94%E7%94%A8%E4%B8%8A%E4%B8%8B%E6%96%87)ʹÓÃͨÅä·û¹¹ÔìÓ¦ÓÃÉÏÏÂÎÄ + +ApplicationContext ¹¹ÔìÆ÷µÄÖеÄ×ÊԴ·¾¶¿ÉÒÔÊǵ¥Ò»µÄ·¾¶£¨¼´Ò»¶ÔÒ»µØÓ³É䵽Ŀ±ê×ÊÔ´£©£»Ò²¿ÉÒÔÊÇͨÅä·ûÐÎʽ¡ª¡ª¿É°üº¬ classpath*£ºÒ²¿ÉÒÔÊÇǰ׺»ò ant ·ç¸ñµÄÕýÔò±í´ïʽ£¨Ê¹Óà spring µÄ PathMatcher À´Æ¥Å䣩¡£ + +ʾÀý£º + + + +``` +ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); + +``` + + + +ʹÓà `classpath*` ±íʾÀà·¾¶ÏÂËùÓÐÆ¥ÅäÎļþÃû³ÆµÄ×ÊÔ´¶¼»á±»»ñÈ¡(±¾ÖÊÉϾÍÊǵ÷ÓÃÁË ClassLoader.getResources(¡­) ·½·¨£©£¬½Ó׎«»ñÈ¡µ½µÄ×ÊÔ´×é×°³É×îÖÕµÄÓ¦ÓÃÉÏÏÂÎÄ¡£ + +ÔÚλÖ÷¾¶µÄÆäÓಿ·Ö£¬`classpath*:` ǰ׺¿ÉÒÔÓë PathMatcher ½áºÏʹÓã¬È磺`classpath*:META-INF/*-beans.xml`¡£ + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E9%97%AE%E9%A2%98)ÎÊÌâ + +Spring ÅäÖÃ×ÊÔ´ÖÐÓÐÄÄЩ³£¼ûÀàÐÍ£¿ + +* XML ×ÊÔ´ +* Properties ×ÊÔ´ +* YAML ×ÊÔ´ + +## [#](https://dunwu.github.io/spring-tutorial/pages/a1549f/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)²Î¿¼×ÊÁÏ + +* [Spring ¹Ù·½ÎĵµÖ® Core Technologies(opens new window)](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans) +* [¡¶Ð¡Âí¸ç½² Spring ºËÐıà³Ì˼Ïë¡·](https://time.geekbang.org/course/intro/265) + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\351\205\215\347\275\256\345\205\203\346\225\260\346\215\256\357\274\210\347\256\241\347\220\206\351\205\215\347\275\256\347\232\204\345\237\272\346\234\254\346\225\260\346\215\256\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\351\205\215\347\275\256\345\205\203\346\225\260\346\215\256\357\274\210\347\256\241\347\220\206\351\205\215\347\275\256\347\232\204\345\237\272\346\234\254\346\225\260\346\215\256\357\274\211.md" new file mode 100644 index 0000000..4515cad --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\270\255\347\232\204\351\205\215\347\275\256\345\205\203\346\225\260\346\215\256\357\274\210\347\256\241\347\220\206\351\205\215\347\275\256\347\232\204\345\237\272\346\234\254\346\225\260\346\215\256\357\274\211.md" @@ -0,0 +1,298 @@ + + +# Spring ÅäÖÃÔªÊý¾Ý + + + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)Spring ÅäÖÃÔªÐÅÏ¢ + +* Spring Bean ÅäÖÃÔªÐÅÏ¢ - BeanDefinition +* Spring Bean ÊôÐÔÔªÐÅÏ¢ - PropertyValues +* Spring ÈÝÆ÷ÅäÖÃÔªÐÅÏ¢ +* Spring Íⲿ»¯ÅäÖÃÔªÐÅÏ¢ - PropertySource +* Spring Profile ÔªÐÅÏ¢ - @Profile + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-bean-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)Spring Bean ÅäÖÃÔªÐÅÏ¢ + +Bean ÅäÖÃÔªÐÅÏ¢ - BeanDefinition + +* GenericBeanDefinition£ºÍ¨ÓÃÐÍ BeanDefinition +* RootBeanDefinition£ºÎÞ Parent µÄ BeanDefinition »òÕߺϲ¢ºó BeanDefinition +* AnnotatedBeanDefinition£º×¢½â±ê×¢µÄ BeanDefinition + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-bean-%E5%B1%9E%E6%80%A7%E5%85%83%E4%BF%A1%E6%81%AF)Spring Bean ÊôÐÔÔªÐÅÏ¢ + +* Bean ÊôÐÔÔªÐÅÏ¢ - PropertyValues + * ¿ÉÐÞ¸ÄʵÏÖ - MutablePropertyValues + * ÔªËØ³ÉÔ± - PropertyValue +* Bean ÊôÐÔÉÏÏÂÎÄ´æ´¢ - AttributeAccessor +* Bean ÔªÐÅÏ¢ÔªËØ - BeanMetadataElement + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-%E5%AE%B9%E5%99%A8%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)Spring ÈÝÆ÷ÅäÖÃÔªÐÅÏ¢ + +Spring XML ÅäÖÃÔªÐÅÏ¢ - beans ÔªËØÏà¹Ø + +| beans ÔªËØÊôÐÔ | ĬÈÏÖµ | ʹÓó¡¾° | +| --- | --- | --- | +| profile | null£¨Áô¿Õ£© | Spring Profiles ÅäÖÃÖµ | +| default-lazy-init | default | µ± outter beans ¡°default-lazy-init¡± ÊôÐÔ´æÔÚʱ£¬¼Ì³Ð¸ÃÖµ£¬·ñÔòΪ¡°false¡± | +| default-merge | default | µ± outter beans ¡°default-merge¡± ÊôÐÔ´æÔÚʱ£¬¼Ì³Ð¸ÃÖµ£¬·ñÔòΪ¡°false¡± | +| default-autowire | default | µ± outter beans ¡°default-autowire¡± ÊôÐÔ´æÔÚʱ£¬¼Ì³Ð¸ÃÖµ£¬·ñÔòΪ¡°no¡± | +| default-autowire-candidates | null£¨Áô¿Õ£© | ĬÈÏ Spring Beans Ãû³Æ pattern | +| default-init-method | null£¨Áô¿Õ£© | ĬÈÏ Spring Beans ×Ô¶¨Òå³õʼ»¯·½·¨ | +| default-destroy-method | null£¨Áô¿Õ£© | ĬÈÏ Spring Beans ×Ô¶¨ÒåÏú»Ù·½·¨ | + +Spring XML ÅäÖÃÔªÐÅÏ¢ - Ó¦ÓÃÉÏÏÂÎÄÏà¹Ø + +| XML ÔªËØ | ʹÓó¡¾° | +| --- | --- | +| `` | ¼¤»î Spring ×¢½âÇý¶¯ | +| `` | Spring @Component ÒÔ¼°×Ô¶¨Òå×¢½âɨÃè | +| `` | ¼¤»î Spring LoadTimeWeaver | +| `` | ±©Â¶ Spring Beans ×÷Ϊ JMX Beans | +| `` | ½«µ±Ç°Æ½Ì¨×÷Ϊ MBeanServer | +| `` | ¼ÓÔØÍⲿ»¯ÅäÖÃ×ÊÔ´×÷Ϊ Spring ÊôÐÔÅä | +| `` | ÀûÓÃÍⲿ»¯ÅäÖÃ×ÊÔ´¸²¸Ç Spring Êô | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-xml-%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD-spring-bean-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)»ùÓÚ XML Îļþ×°ÔØ Spring Bean ÅäÖÃÔªÐÅÏ¢ + +µ×²ãʵÏÖ - XmlBeanDefinitionReader + +| XML ÔªËØ | ʹÓó¡¾° | +| --- | --- | +| `` | µ¥ XML ×ÊԴϵĶà¸ö Spring Beans ÅäÖà | +| `` | µ¥¸ö Spring Bean ¶¨Ò壨BeanDefinition£©ÅäÖà | +| `` | Ϊ Spring Bean ¶¨Ò壨BeanDefinition£©Ó³Éä±ðÃû | +| `` | ¼ÓÔØÍⲿ Spring XML ÅäÖÃ×ÊÔ´ | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-properties-%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD-spring-bean-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)»ùÓÚ Properties Îļþ×°ÔØ Spring Bean ÅäÖÃÔªÐÅÏ¢ + +µ×²ãʵÏÖ - PropertiesBeanDefinitionReader + +| Properties ÊôÐÔÃû | ʹÓó¡¾° | +| --- | --- | +| `class` | Bean ÀàÈ«³ÆÏÞ¶¨Ãû | +| `abstract` | ÊÇ·ñΪ³éÏóµÄ BeanDefinition | +| `parent` | Ö¸¶¨ parent BeanDefinition Ãû³Æ | +| `lazy-init` | ÊÇ·ñΪÑÓ³Ù³õʼ»¯ | +| `ref` | ÒýÓÃÆäËû Bean µÄÃû³Æ | +| `scope` | ÉèÖà Bean µÄ scope ÊôÐÔ | +| ${n} | n ±íʾµÚ n+1 ¸ö¹¹ÔìÆ÷²ÎÊý | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-java-%E6%B3%A8%E8%A7%A3%E8%A3%85%E8%BD%BD-spring-bean-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)»ùÓÚ Java ×¢½â×°ÔØ Spring Bean ÅäÖÃÔªÐÅÏ¢ + +Spring ģʽע½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| `@Repository` | Êý¾Ý²Ö´¢Ä£Ê½×¢½â | 2.0 | +| `@Component` | ͨÓÃ×é¼þģʽע½â | 2.5 | +| `@Service` | ·þÎñģʽע½â | 2.5 | +| `@Controller` | Web ¿ØÖÆÆ÷ģʽע½â | 2.5 | +| `@Configuration` | ÅäÖÃÀàģʽע½â | 3.0 | + +Spring Bean ¶¨Òå×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| `@Bean` | Ìæ»» XML ÔªËØ `` | 3.0 | +| `@DependsOn` | Ìæ´ú XML ÊôÐÔ `` | 3.0 | +| `@Lazy` | Ìæ´ú XML ÊôÐÔ `` | 3.0 | +| `@Primary` | Ìæ»» XML ÔªËØ `` | 3.0 | +| `@Role` | Ìæ»» XML ÔªËØ `` | 3.1 | +| `@Lookup` | Ìæ´ú XML ÊôÐÔ `` | 4.1 | + +Spring Bean ÒÀÀµ×¢Èë×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| `@Autowired` | Bean ÒÀÀµ×¢È룬֧³Ö¶àÖÖÒÀÀµ²éÕÒ·½Ê½ | 2.5 | +| `@Qualifier` | ϸÁ£¶ÈµÄ @Autowired ÒÀÀµ²éÕÒ | 2.5 | + + + +| Java ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| @Resource | ÀàËÆÓÚ @Autowired | 2.5 | +| @Inject | ÀàËÆÓÚ @Autowired | 2.5 | + +Spring Bean Ìõ¼þ×°Åä×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| @Profile | ÅäÖû¯Ìõ¼þ×°Åä | 3.1 | +| @Conditional | ±à³ÌÌõ¼þ×°Åä | 4.0 | + +Spring Bean ÉúÃüÖÜÆÚ»Øµ÷×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| @PostConstruct | Ìæ»» XML ÔªËØ »ò InitializingBean | 2.5 | +| @PreDestroy | Ìæ»» XML ÔªËØ »ò DisposableBean | 2.5 | + +Spring BeanDefinition ½âÎöÓë×¢²á + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| XML ×ÊÔ´ | XmlBeanDefinitionReader | 1.0 | +| Properties ×ÊÔ´ | PropertiesBeanDefinitionReader | 1.0 | +| Java ×¢½â | AnnotatedBeanDefinitionReader | 3.0 | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-bean-%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0)Spring Bean ÅäÖÃÔªÐÅÏ¢µ×²ãʵÏÖ + +### [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-xml-%E8%B5%84%E6%BA%90-beandefinition-%E8%A7%A3%E6%9E%90%E4%B8%8E%E6%B3%A8%E5%86%8C)Spring XML ×ÊÔ´ BeanDefinition ½âÎöÓë×¢²á + +ºËÐÄ API - XmlBeanDefinitionReader + +* ×ÊÔ´ - Resource +* µ×²ã - BeanDefinitionDocumentReader + * XML ½âÎö - Java DOM Level 3 API + * BeanDefinition ½âÎö - BeanDefinitionParserDelegate + * BeanDefinition ×¢²á - BeanDefinitionRegistry + +### [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-properties-%E8%B5%84%E6%BA%90-beandefinition-%E8%A7%A3%E6%9E%90%E4%B8%8E%E6%B3%A8%E5%86%8C)Spring Properties ×ÊÔ´ BeanDefinition ½âÎöÓë×¢²á + +ºËÐÄ API - PropertiesBeanDefinitionReader + +* ×ÊÔ´ + * ×Ö½ÚÁ÷ - Resource + * ×Ö·ûÁ÷ - EncodedResouce +* µ×²ã + * ´æ´¢ - java.util.Properties + * BeanDefinition ½âÎö - API ÄÚ²¿ÊµÏÖ + * BeanDefinition ×¢²á - BeanDefinitionRegistry + +### [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#spring-java-%E6%B3%A8%E5%86%8C-beandefinition-%E8%A7%A3%E6%9E%90%E4%B8%8E%E6%B3%A8%E5%86%8C)Spring Java ×¢²á BeanDefinition ½âÎöÓë×¢²á + +ºËÐÄ API - AnnotatedBeanDefinitionReader + +* ×ÊÔ´ + * Àà¶ÔÏó - java.lang.Class +* µ×²ã + * Ìõ¼þÆÀ¹À - ConditionEvaluator + * Bean ·¶Î§½âÎö - ScopeMetadataResolver + * BeanDefinition ½âÎö - ÄÚ²¿ API ʵÏÖ + * BeanDefinition ´¦Àí - AnnotationConfigUtils.processCommonDefinitionAnnotations + * BeanDefinition ×¢²á - BeanDefinitionRegistry + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-xml-%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD-spring-ioc-%E5%AE%B9%E5%99%A8%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)»ùÓÚ XML Îļþ×°ÔØ Spring IoC ÈÝÆ÷ÅäÖÃÔªÐÅÏ¢ + +Spring IoC ÈÝÆ÷Ïà¹Ø XML ÅäÖà + +| ÃüÃû¿Õ¼ä | ËùÊôÄ£¿é | Schema ×ÊÔ´ URL | +| --- | --- | --- | +| beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd | +| context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd | +| aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd | +| tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd | +| util | spring-beans | beans https://www.springframework.org/schema/util/spring-util.xsd | +| tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-java-%E6%B3%A8%E8%A7%A3%E8%A3%85%E8%BD%BD-spring-ioc-%E5%AE%B9%E5%99%A8%E9%85%8D%E7%BD%AE%E5%85%83%E4%BF%A1%E6%81%AF)»ùÓÚ Java ×¢½â×°ÔØ Spring IoC ÈÝÆ÷ÅäÖÃÔªÐÅÏ¢ + +Spring IoC ÈÝÆ÷×°Åä×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| @ImportResource | Ìæ»» XML ÔªËØ `` | 3.0 | +| @Import | µ¼Èë Configuration Class | 3.0 | +| @ComponentScan | ɨÃèÖ¸¶¨ package ϱê×¢ Spring ģʽע½âµÄÀà | 3.1 | + +Spring IoC ÅäÊôÊôÐÔ×¢½â + +| Spring ×¢½â | ³¡¾°ËµÃ÷ | Æðʼ°æ±¾ | +| --- | --- | --- | +| @PropertySource | ÅäÖÃÊôÐÔ³éÏó PropertySource ×¢½â | 3.1 | +| @PropertySources | @PropertySource ¼¯ºÏ×¢½â | 4.0 | + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-extensible-xml-authoring-%E6%89%A9%E5%B1%95-springxml-%E5%85%83%E7%B4%A0)»ùÓÚ Extensible XML authoring À©Õ¹ SpringXML ÔªËØ + +Spring XML À©Õ¹ + +* ±àд XML Schema Îļþ£º¶¨Òå XML ½á¹¹ +* ×Ô¶¨Òå NamespaceHandler ʵÏÖ£ºÃüÃû¿Õ¼ä°ó¶¨ +* ×Ô¶¨Òå BeanDefinitionParser ʵÏÖ£ºXML ÔªËØÓë BeanDefinition ½âÎö +* ×¢²á XML À©Õ¹£ºÃüÃû¿Õ¼äÓë XML Schema Ó³Éä + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#extensible-xml-authoring-%E6%89%A9%E5%B1%95%E5%8E%9F%E7%90%86)Extensible XML authoring À©Õ¹Ô­Àí + +### [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E8%A7%A6%E5%8F%91%E6%97%B6%E6%9C%BA)´¥·¢Ê±»ú + +* AbstractApplicationContext#obtainFreshBeanFactory + * AbstractRefreshableApplicationContext#refreshBeanFactory + * AbstractXmlApplicationContext#loadBeanDefinitions + * ... + * XmlBeanDefinitionReader#doLoadBeanDefinitions + * ... + * BeanDefinitionParserDelegate#parseCustomElement + +### [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E6%A0%B8%E5%BF%83%E6%B5%81%E7%A8%8B)ºËÐÄÁ÷³Ì + +BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, BeanDefinition) + +* »ñÈ¡ namespace +* ͨ¹ý namespace ½âÎö NamespaceHandler +* ¹¹Ôì ParserContext +* ½âÎöÔªËØ£¬»ñÈ¡ BeanDefinintion + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-properties-%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE)»ùÓÚ Properties Îļþ×°ÔØÍⲿ»¯ÅäÖà + +×¢½âÇý¶¯ + +* @org.springframework.context.annotation.PropertySource +* @org.springframework.context.annotation.PropertySources + +API ±à³Ì + +* org.springframework.core.env.PropertySource +* org.springframework.core.env.PropertySources + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%9F%BA%E4%BA%8E-yaml-%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE)»ùÓÚ YAML Îļþ×°ÔØÍⲿ»¯ÅäÖà + +API ±à³Ì + +* org.springframework.beans.factory.config.YamlProcessor + * org.springframework.beans.factory.config.YamlMapFactoryBean + * org.springframework.beans.factory.config.YamlPropertiesFactoryBean + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E9%97%AE%E9%A2%98)ÎÊÌâ + +**Spring ƒÈ½¨ XML Schema ³£¼ûÓÐÄÄЩ**£¿ + +| ÃüÃû¿Õ¼ä | ËùÊôÄ£¿é | Schema ×ÊÔ´ URL | +| --- | --- | --- | +| beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd | +| context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd | +| aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd | +| tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd | +| util | spring-beans | beans https://www.springframework.org/schema/util/spring-util.xsd | +| tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd | + +**Spring ÅäÖÃÔªÐÅÏ¢¾ßÌåÓÐÄÄЩ**£¿ + +* Bean ÅäÖÃÔªÐÅÏ¢£ºÍ¨¹ýý½é£¨Èç XML¡¢Proeprties µÈ£©£¬½âÎö BeanDefinition +* IoC ÈÝÆ÷ÅäÖÃÔªÐÅÏ¢£ºÍ¨¹ýý½é£¨Èç XML¡¢Proeprties µÈ£©£¬¿ØÖÆ IoC ÈÝÆ÷ÐÐΪ£¬±ÈÈç×¢½âÇý¶¯¡¢AOP µÈ +* Íⲿ»¯ÅäÖãºÍ¨¹ý×ÊÔ´³éÏó£¨Èç Proeprties¡¢YAML µÈ£©£¬¿ØÖÆ PropertySource +* Spring Profile£ºÍ¨¹ýÍⲿ»¯ÅäÖã¬ÌṩÌõ¼þ·ÖÖ§Á÷³Ì + +**Extensible XML authoring µÄȱµã**£¿ + +* ¸ß¸´ÔÓ¶È£º¿ª·¢ÈËÔ±ÐèÒªÊìϤ XML Schema£¬spring.handlers£¬spring.schemas ÒÔ¼° Spring API +* ǶÌ×ÔªËØÖ§³Ö½ÏÈõ£ºÍ¨³£ÐèҪʹÓ÷½·¨µÝ¹é»òÕ߯äǶÌ×½âÎöµÄ·½Ê½´¦ÀíǶÌ×£¨×Ó£©ÔªËØ +* XML ´¦ÀíÐÔÄܽϲSpring XML »ùÓÚ DOM Level 3 API ʵÏÖ£¬¸Ã API ±ãÓÚÀí½â£¬È»¶øÐÔÄܽϲî +* XML ¿ò¼ÜÒÆÖ²ÐԲºÜÄÑÊÊÅä¸ßÐÔÄܺͱãÀûÐ﵀ XML ¿ò¼Ü£¬Èç JAXB + +## [#](https://dunwu.github.io/spring-tutorial/pages/55f315/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)²Î¿¼×ÊÁÏ + +* [Spring ¹Ù·½ÎĵµÖ® Core Technologies(opens new window)](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans) +* [¡¶Ð¡Âí¸ç½² Spring ºËÐıà³Ì˼Ïë¡·(opens new window)](https://time.geekbang.org/course/intro/265) + + + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\272\213\345\212\241\345\237\272\346\234\254\347\224\250\346\263\225.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\272\213\345\212\241\345\237\272\346\234\254\347\224\250\346\263\225.md" new file mode 100644 index 0000000..c247c82 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\344\272\213\345\212\241\345\237\272\346\234\254\347\224\250\346\263\225.md" @@ -0,0 +1,292 @@ +# SpringÊÂÎñºËÐÄ½Ó¿Ú + +[Spring](http://www.voidme.com/spring) µÄÊÂÎñ¹ÜÀíÊÇ»ùÓÚ AOP ʵÏֵ쬶ø AOP ÊÇÒÔ·½·¨Îªµ¥Î»µÄ¡£Spring µÄÊÂÎñÊôÐÔ·Ö±ðΪ´«²¥ÐÐΪ¡¢¸ôÀë¼¶±ð¡¢Ö»¶ÁºÍ³¬Ê±ÊôÐÔ£¬ÕâЩÊôÐÔÌṩÁËÊÂÎñÓ¦Óõķ½·¨ºÍÃèÊö²ßÂÔ¡£ + +ÔÚ [Java](http://www.voidme.com/java) EE ¿ª·¢¾­³£²ÉÓõķֲãģʽÖУ¬Spring µÄÊÂÎñ´¦ÀíλÓÚÒµÎñÂß¼­²ã£¬ËüÌṩÁËÕë¶ÔÊÂÎñµÄ½â¾ö·½°¸¡£ + +ÔÚ Spring ½âѹ°üµÄ libs Ŀ¼ÖУ¬°üº¬Ò»¸öÃû³ÆÎª spring-tx-3.2.13.RELEASE.jar µÄÎļþ£¬¸ÃÎļþÊÇ Spring ÌṩµÄÓÃÓÚÊÂÎñ¹ÜÀíµÄ JAR °ü£¬ÆäÖаüÀ¨ÊÂÎñ¹ÜÀíµÄÈý¸öºËÐĽӿڣºPlatformTransactionManager¡¢TransactionDefinition ºÍ TransactionStatus¡£ + +½«¸Ã JAR °üµÄºó׺Ãû jar ¸Ä³É zip µÄÐÎʽºó£¬½âѹѹËõ°ü£¬½øÈë½âѹÎļþ¼ÐÖÐµÄ \org\springframework\transaction Ŀ¼ºó£¬¸ÃĿ¼ÖеÄÎļþÈçͼ 1 Ëùʾ¡£ + +![ÊÂÎñ¹ÜÀíºËÐĽӿÚ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/760_a12_405.png) +ͼ 1 ÊÂÎñ¹ÜÀíºËÐÄ½Ó¿Ú + +ÔÚͼ 1 ÖУ¬·½¿òËù±ê×¢µÄÈý¸öÎļþ¾ÍÊDZ¾½Ú½«Òª½²½âµÄºËÐĽӿڡ£ÕâÈý¸öºËÐĽӿڵÄ×÷Óü°ÆäÌṩµÄ·½·¨ÈçÏ¡£ + +#### 1\. PlatformTransactionManager + +PlatformTransactionManager ½Ó¿ÚÊÇ Spring ÌṩµÄƽ̨ÊÂÎñ¹ÜÀíÆ÷£¬ÓÃÓÚ¹ÜÀíÊÂÎñ¡£¸Ã½Ó¿ÚÖÐÌṩÁËÈý¸öÊÂÎñ²Ù×÷·½·¨£¬¾ßÌåÈçÏ¡£ + +* TransactionStatus getTransaction£¨TransactionDefinition definition£©£ºÓÃÓÚ»ñÈ¡ÊÂÎñ״̬ÐÅÏ¢¡£ +* void commit£¨TransactionStatus status£©£ºÓÃÓÚÌá½»ÊÂÎñ¡£ +* void rollback£¨TransactionStatus status£©£ºÓÃÓڻعöÊÂÎñ¡£ + +ÔÚÏîÄ¿ÖУ¬Spring ½« xml ÖÐÅäÖõÄÊÂÎñÏêϸÐÅÏ¢·â×°µ½¶ÔÏó TransactionDefinition ÖУ¬È»ºóͨ¹ýÊÂÎñ¹ÜÀíÆ÷µÄ getTransaction() ·½·¨»ñµÃÊÂÎñµÄ״̬£¨TransactionStatus£©£¬²¢¶ÔÊÂÎñ½øÐÐÏÂÒ»²½µÄ²Ù×÷¡£ + +#### 2\. TransactionDefinition + +TransactionDefinition ½Ó¿ÚÊÇÊÂÎñ¶¨Ò壨ÃèÊö£©µÄ¶ÔÏó£¬ËüÌṩÁËÊÂÎñÏà¹ØÐÅÏ¢»ñÈ¡µÄ·½·¨£¬ÆäÖаüÀ¨Îå¸ö²Ù×÷£¬¾ßÌåÈçÏ¡£ + +* String getName()£º»ñÈ¡ÊÂÎñ¶ÔÏóÃû³Æ¡£ +* int getIsolationLevel()£º»ñÈ¡ÊÂÎñµÄ¸ôÀë¼¶±ð¡£ +* int getPropagationBehavior()£º»ñÈ¡ÊÂÎñµÄ´«²¥ÐÐΪ¡£ +* int getTimeout()£º»ñÈ¡ÊÂÎñµÄ³¬Ê±Ê±¼ä¡£ +* boolean isReadOnly()£º»ñÈ¡ÊÂÎñÊÇ·ñÖ»¶Á¡£ + +ÔÚÉÏÊöÎå¸ö·½·¨µÄÃèÊöÖУ¬ÊÂÎñµÄ´«²¥ÐÐΪÊÇÖ¸ÔÚͬһ¸ö·½·¨ÖУ¬²»Í¬²Ù×÷ǰºóËùʹÓõÄÊÂÎñ¡£´«²¥ÐÐΪµÄÖÖÀàÈç±í 1 Ëùʾ¡£ + +| ÊôÐÔÃû³Æ | Öµ | Ãè Êö | +| --- | --- | --- | +| PROPAGATION_REQUIRED | required | Ö§³Öµ±Ç°ÊÂÎñ¡£Èç¹û A ·½·¨ÒѾ­ÔÚÊÂÎñÖУ¬Ôò B ÊÂÎñ½«Ö±½ÓʹÓ᣷ñÔò½«´´½¨ÐÂÊÂÎñ | +| PROPAGATION_SUPPORTS | supports | Ö§³Öµ±Ç°ÊÂÎñ¡£Èç¹û A ·½·¨ÒѾ­ÔÚÊÂÎñÖУ¬Ôò B ÊÂÎñ½«Ö±½ÓʹÓ᣷ñÔò½«ÒÔ·ÇÊÂÎñ״ִ̬ÐÐ | +| PROPAGATION_MANDATORY | mandatory | Ö§³Öµ±Ç°ÊÂÎñ¡£Èç¹û A ·½·¨Ã»ÓÐÊÂÎñ£¬ÔòÅ׳öÒì³£ | +| PROPAGATION_REQUIRES_NEW | requires_new | ½«´´½¨ÐµÄÊÂÎñ£¬Èç¹û A ·½·¨ÒѾ­ÔÚÊÂÎñÖУ¬Ôò½« A ÊÂÎñ¹ÒÆð | +| PROPAGATION_NOT_SUPPORTED | not_supported | ²»Ö§³Öµ±Ç°ÊÂÎñ£¬×ÜÊÇÒÔ·ÇÊÂÎñ״ִ̬ÐС£Èç¹û A ·½·¨ÒѾ­ÔÚÊÂÎñÖУ¬Ôò½«Æä¹ÒÆð | +| PROPAGATION_NEVER | never | ²»Ö§³Öµ±Ç°ÊÂÎñ£¬Èç¹û A ·½·¨ÔÚÊÂÎñÖУ¬ÔòÅ׳öÒì³£ | +| PROPAGATION.NESTED | nested | ǶÌ×ÊÂÎñ£¬µ×²ã½«Ê¹Óà Savepoint ÐγÉǶÌ×ÊÂÎñ | + +ÔÚÊÂÎñ¹ÜÀí¹ý³ÌÖУ¬´«²¥ÐÐΪ¿ÉÒÔ¿ØÖÆÊÇ·ñÐèÒª´´½¨ÊÂÎñÒÔ¼°ÈçºÎ´´½¨ÊÂÎñ¡£ + +ͨ³£Çé¿öÏ£¬Êý¾ÝµÄ²éѯ²»»á¸Ä±äÔ­Êý¾Ý£¬ËùÒÔ²»ÐèÒª½øÐÐÊÂÎñ¹ÜÀí£¬¶ø¶ÔÓÚÊý¾ÝµÄÔö¼Ó¡¢Ð޸ĺÍɾ³ýµÈ²Ù×÷£¬±ØÐë½øÐÐÊÂÎñ¹ÜÀí¡£Èç¹ûûÓÐÖ¸¶¨ÊÂÎñµÄ´«²¥ÐÐΪ£¬Ôò Spring3 ĬÈϵĴ«²¥ÐÐΪÊÇ required¡£ + +#### 3\. TransactionStatus + +TransactionStatus ½Ó¿ÚÊÇÊÂÎñµÄ״̬£¬ËüÃèÊöÁËijһʱ¼äµãÉÏÊÂÎñµÄ״̬ÐÅÏ¢¡£ÆäÖаüº¬Áù¸ö²Ù×÷£¬¾ßÌåÈç±í 2 Ëùʾ¡£ + +±í 2 ÊÂÎñµÄ²Ù×÷ +| Ãû³Æ | ˵Ã÷ | +| --- | --- | +| void flush() | Ë¢ÐÂÊÂÎñ | +| boolean hasSavepoint() | »ñÈ¡ÊÇ·ñ´æÔÚ±£´æµã | +| boolean isCompleted() | »ñÈ¡ÊÂÎñÊÇ·ñÍê³É | +| boolean isNewTransaction() | »ñÈ¡ÊÇ·ñÊÇÐÂÊÂÎñ | +| boolean isRollbackOnly() | »ñÈ¡ÊÇ·ñ»Ø¹ö | +| void setRollbackOnly() | ÉèÖÃÊÂÎñ»Ø¹ö | + +# SpringÉùÃ÷ʽÊÂÎñ¹ÜÀí£¨»ùÓÚXML·½Ê½ÊµÏÖ£© + +[Spring](http://www.voidme.com/spring) µÄÊÂÎñ¹ÜÀíÓÐÁ½ÖÖ·½Ê½£ºÒ»ÖÖÊÇ´«Í³µÄ±à³ÌʽÊÂÎñ¹ÜÀí£¬¼´Í¨¹ý±àд´úÂëʵÏÖµÄÊÂÎñ¹ÜÀí£»ÁíÒ»ÖÖÊÇ»ùÓÚ AOP ¼¼ÊõʵÏÖµÄÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£ÓÉÓÚÔÚʵ¼Ê¿ª·¢ÖУ¬±à³ÌʽÊÂÎñ¹ÜÀíºÜÉÙʹÓã¬ËùÒÔÎÒÃÇÖ»¶Ô Spring µÄÉùÃ÷ʽÊÂÎñ¹ÜÀí½øÐÐÏêϸ½²½â¡£ + +Spring ÉùÃ÷ʽÊÂÎñ¹ÜÀíÔڵײã²ÉÓÃÁË AOP ¼¼Êõ£¬Æä×î´óµÄÓŵãÔÚÓÚÎÞÐëͨ¹ý±à³ÌµÄ·½Ê½¹ÜÀíÊÂÎñ£¬Ö»ÐèÒªÔÚÅäÖÃÎļþÖнøÐÐÏà¹ØµÄ¹æÔòÉùÃ÷£¬¾Í¿ÉÒÔ½«ÊÂÎñ¹æÔòÓ¦Óõ½ÒµÎñÂß¼­ÖС£ + +Spring ʵÏÖÉùÃ÷ʽÊÂÎñ¹ÜÀíÖ÷ÒªÓÐÁ½ÖÖ·½Ê½£º + +* »ùÓÚ XML ·½Ê½µÄÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£ +* ͨ¹ý Annotation ×¢½â·½Ê½µÄÊÂÎñ¹ÜÀí¡£ + +±¾½Úͨ¹ýÒøÐÐתÕ˵ݸÀý½²½âÈçºÎʹÓà XML µÄ·½Ê½ÊµÏÖ Spring µÄÉùÃ÷ʽÊÂÎñ´¦Àí¡£ + +#### 1\. ´´½¨ÏîÄ¿ + +ÔÚ MyEclipse Öд´½¨Ò»¸öÃûΪ springDemo03 µÄ Web ÏîÄ¿£¬½« Spring Ö§³ÖºÍÒÀÀµµÄ JAR °ü¸´ÖƵ½ Web ÏîÄ¿µÄ lib Ŀ¼ÖУ¬²¢Ìí¼Óµ½Àà·¾¶Ï¡£ËùÌí¼ÓµÄ JAR °üÈçͼ 1 Ëùʾ¡£ + +![ÐèÒªµ¼ÈëµÄJAR°ü](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/761_732_fcf.png) +ͼ 1 ÐèÒªµ¼ÈëµÄJAR°ü + +´Óͼ 1 ÖпÉÒÔ¿´³ö£¬ÕâÀïÔö¼Óµ¼ÈëÁË spring-tx-3.2.13.RELEASE.jar£¨ÊÂÎñ¹ÜÀí£©£¬ÒÔ¼° [MySQL](http://www.voidme.com/mysql) Çý¶¯¡¢JDBC ºÍ C3P0 µÄ JAR °ü¡£ + +#### 2\. ´´½¨Êý¾Ý¿â¡¢±íÒÔ¼°²åÈëÊý¾Ý + +ÔÚ MySQL Öд´½¨Ò»¸öÃûΪ spring µÄÊý¾Ý¿â£¬È»ºóÔÚ¸ÃÊý¾Ý¿âÖд´½¨Ò»¸ö account ±í£¬²¢Ïò±íÖвåÈëÁ½ÌõÊý¾Ý£¬Æä SQL Ö´ÐÐÓï¾äÈçÏÂËùʾ£º + +CREATE DATABASE spring; +USE spring; +CREATE TABLE account ( +id INT (11) PRIMARY KEY AUTO_INCREMENT, +username VARCHAR(20) NOT NULL, +money INT DEFAULT NULL +); +INSERT INTO account VALUES (1,'zhangsan',1000); +INSERT INTO account VALUES (2,'lisi',1000); + +Ö´ÐкóµÄ account ±íÖеÄÊý¾ÝÈçͼ 2 Ëùʾ¡£ + +![Ö´Ðнá¹û](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/762_276_8a5.PNG) +ͼ 2 Ö´Ðнá¹û + +#### 3\. ´´½¨ c3p0-db.properties + +ÔÚÏîÄ¿µÄ src Ï´´½¨Ò»¸öÃûΪ c3p0-db.properties µÄÅäÖÃÎļþ£¬ÕâÀïʹÓà C3P0 Êý¾ÝÔ´£¬ÐèÒªÔÚ¸ÃÎļþÖÐÌí¼ÓÈçÏÂÅäÖ㺠+```` +jdbc.driverClass = com.mysql.jdbc.Driver +jdbc.jdbcUrl = jdbc:mysql://localhost:3306/spring +jdbc.user = root +jdbc.password = root +```` +#### 4\. ʵÏÖ DAO + +#### 1£©´´½¨ AccountDao ½Ó¿Ú + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨Ò»¸öÃûΪ com.mengma.dao µÄ°ü£¬ÔڸðüÏ´´½¨Ò»¸ö½Ó¿Ú AccountDao£¬²¢ÔÚ½Ó¿ÚÖд´½¨»ã¿îºÍÊÕ¿îµÄ·½·¨£¬ÈçÏÂËùʾ¡£ +```` +package com.mengma.dao; +public interface AccountDao { + // »ã¿î + public void out(String outUser, int money); + // ÊÕ¿î + public void in(String inUser, int money);} ```` +ÉÏÊö´úÂëÖУ¬¶¨ÒåÁË out() ºÍ in() Á½¸ö·½·¨£¬·Ö±ðÓÃÓÚ±íʾ»ã¿îºÍÊÕ¿î¡£ + +#### 2£©´´½¨DAO²ã½Ó¿ÚʵÏÖÀà + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨Ò»¸öÃûΪ com.mengma.dao.impl µÄ°ü£¬ÔڸðüÏ´´½¨ÊµÏÖÀà AccountDaoImpl£¬ÈçÏÂËùʾ¡£ +```` +package com.mengma.dao.impl; + +import org.springframework.jdbc.core.JdbcTemplate; +import com.mengma.dao.AccountDao; + +public class AccountDaoImpl implements AccountDao { +private JdbcTemplate jdbcTemplate; +public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } +// »ã¿îµÄʵÏÖ·½·¨ +public void out(String outUser, int money) { this.jdbcTemplate.update("update account set money =money-?" + "where username =?", money, outUser); } +// ÊÕ¿îµÄʵÏÖ·½·¨ +public void in(String inUser, int money) { this.jdbcTemplate.update("update account set money =money+?" + "where username =?", money, inUser); }} ```` +ÉÏÊö´úÂëÖУ¬Ê¹Óà JdbcTemplate ÀàµÄ update() ·½·¨ÊµÏÖÁ˸üвÙ×÷¡£ + +#### 5\. ʵÏÖ Service + +#### 1£©´´½¨ Service ²ã½Ó¿Ú + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨Ò»¸öÃûΪ com.mengma.service µÄ°ü£¬ÔڸðüÏ´´½¨½Ó¿Ú AccountService£¬ÈçÏÂËùʾ¡£ +```` +package com.mengma.service; + +public interface AccountService { + // תÕË + public void transfer(String outUser, String inUser, int money);} ```` +#### 2£©´´½¨ Service ²ã½Ó¿ÚʵÏÖÀà + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨Ò»¸öÃûΪ com.mengma.service.impl µÄ°ü£¬ÔڸðüÏ´´½¨ÊµÏÖÀà AccountServiceImpl£¬ÈçÏÂËùʾ¡£ +```` +package com.mengma.service.impl; + +import com.mengma.dao.AccountDao; + +public class AccountServiceImpl { +private AccountDao accountDao; +public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } +public void transfer(String outUser, String inUser, int money) { this.accountDao.out(outUser, money); this.accountDao.in(inUser, money); }} ```` +ÉÏÊö´úÂëÖпÉÒÔ¿´³ö£¬¸ÃÀàʵÏÖÁË AccountService ½Ó¿Ú£¬²¢¶ÔתÕ˵ķ½·¨½øÐÐÁËʵÏÖ£¬¸ù¾Ý²ÎÊýµÄ²»Í¬µ÷Óà DAO ²ãÏàÓ¦µÄ·½·¨¡£ + +#### 6\. ´´½¨ Spring ÅäÖÃÎļþ + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨ Spirng ÅäÖÃÎļþ applicationContext.xml£¬±à¼­ºóÈçÏÂËùʾ¡£ +```` + + + ```` +ÉÏÊö´úÂëÖУ¬Ê×ÏÈÔÚ ±ê¼ÇµÄµÚ 6¡¢13 ºÍ 14 ÐдúÂë·Ö±ðÌí¼ÓÁË AOP ËùÐèµÄÃüÃû¿Õ¼äÉùÃ÷¡£µÚ 42¡«50 ÐдúÂëʹÓà ±ê¼ÇÅäÖÃÊÂÎñ֪ͨÄÚÈÝ¡£ + +µÚ 52¡«58 ÐдúÂëʹÓà ±ê¼Ç¶¨ÒåÇÐÃæ£¬ÆäÖÐµÚ 54 ÐдúÂëÓ¦ÓÃÁË AspectJ ±í´ïʽ£¬´ú±í com.mengma.service °üÏÂËùÓÐÀàµÄËùÓз½·¨¶¼Ó¦ÓÃÊÂÎñ¹æÔò£¬µÚ 57 ÐдúÂëʹÓà ±ê¼Ç½«ÇÐÈëµãÓëÊÂÎñ֪ͨÕûºÏ£¬»ùÓÚ AOP µÄÉùÃ÷ʽÊÂÎñÅäÖÃÍê³É¡£ + +#### 7\. ´´½¨²âÊÔÀà + +ÔÚÏîÄ¿µÄ src Ŀ¼Ï´´½¨ com.mengma.test µÄ°ü£¬ÔڸðüÏ´´½¨²âÊÔÀà AccountTest£¬ÈçÏÂËùʾ¡£ +```` +package com.mengma.test; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import com.mengma.service.AccountService; +public class AccountTest { +@Test public void test() { // »ñµÃSpringÈÝÆ÷£¬²¢²Ù×÷ +String xmlPath = "applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext( xmlPath); AccountService accountService = (AccountService) applicationContext .getBean("accountService"); accountService.transfer("zhangsan", "lisi", 100); }} ```` +ÉÏÊö´úÂëÖÐÄ£ÄâÁËÒøÐÐתÕËÒµÎñ£¬´Ó zhangsan µÄÕË»§Ïò lisi µÄÕË»§ÖÐתÈë 100 Ôª¡£Ê¹Óà JUnit ²âÊÔÔËÐÐ test() ·½·¨£¬ÔËÐгɹ¦ºó£¬²éѯ account ±í£¬Èçͼ 3 Ëùʾ¡£ + +´Óͼ 3 µÄ²éѯ½á¹ûÖпÉÒÔ¿´³ö£¬zhangsan ³É¹¦Ïò lisi תÕË 100 Ôª¡£ + +![²éѯ½á¹û](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/763_9ae_de5.PNG) +ͼ 3 ²éѯ½á¹û + +ÏÂÃæÍ¨¹ýÐ޸ݸÀýÄ£ÄâתÕËʧ°ÜµÄÇé¿ö£¬ÔÚµÄ transfer() ·½·¨ÖÐÌí¼ÓÒ»ÐдúÂë¡°int i=1/0£»¡±Ä£Äâϵͳ¶ÏµçµÄÇé¿ö£¬¾ßÌå´úÂëÈçÏÂËùʾ£º +```` + public void transfer(String outUser, String inUser, int money) { this.accountDao.out(outUser, money); //Ä£Äâ¶Ïµç + int i = 1/0; this.accountDao.in(inUser, money); + } +```` + +ÖØÐ²âÊÔÔËÐÐ test() ·½·¨£¬JUnit ¿ØÖÆÌ¨Êä³öµÄÐÅÏ¢Èçͼ 4 Ëùʾ¡£ + +![¿ØÖÆÌ¨Êä³ö½á¹û](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/764_7a3_f52.png) +ͼ 4 ¿ØÖÆÌ¨Êä³ö½á¹û + +´Óͼ 4 ÖпÉÒÔ¿´³ö£¬ÔÚÖ´ÐвâÊÔ·½·¨Ê±£¬³öÏÖÁ˳ýÒÔ 0 µÄÒì³£ÐÅÏ¢¡£´ËʱÔٴβéѯ account ±í£¬Æä²éѯ½á¹ûÈçͼ 5 Ëùʾ¡£ + +´Óͼ 5 µÄ²éѯ½á¹ûÖпÉÒÔ¿´³ö£¬±íÖеÄÊý¾Ý²¢Ã»Óз¢Éú±ä»¯¡£ÓÉÓÚ³ÌÐòÔÚÖ´Ðйý³ÌÖÐÅ׳öÁËÒì³££¬ÊÂÎñ²»ÄÜÕý³£±»Ìá½»£¬ËùÒÔתÕËʧ°Ü¡£ÓÉ´Ë¿ÉÖª£¬Spring µÄÊÂÎñ¹ÜÀíÉúЧÁË¡£ + +![²éѯ½á¹û](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/763_9ae_de5.PNG) +ͼ 5 ²éѯ½á¹û + +# SpringÉùÃ÷ʽÊÂÎñ¹ÜÀí£¨»ùÓÚAnnotation×¢½â·½Ê½ÊµÏÖ£© + +ÔÚ [Spring](http://www.voidme.com/spring) ÖУ¬³ýÁËʹÓûùÓÚ XML µÄ·½Ê½¿ÉÒÔʵÏÖÉùÃ÷ʽÊÂÎñ¹ÜÀíÒÔÍ⣬»¹¿ÉÒÔͨ¹ý Annotation ×¢½âµÄ·½Ê½ÊµÏÖÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£ + +ʹÓà Annotation µÄ·½Ê½·Ç³£¼òµ¥£¬Ö»ÐèÒªÔÚÏîÄ¿ÖÐ×öÁ½¼þÊ£¬¾ßÌåÈçÏ¡£ + +#### 1£©ÔÚ Spring ÈÝÆ÷ÖÐ×¢²áÇý¶¯£¬´úÂëÈçÏÂËùʾ£º +```` + +```` +#### 2£©ÔÚÐèҪʹÓÃÊÂÎñµÄÒµÎñÀà»òÕß·½·¨ÖÐÌí¼Ó×¢½â @Transactional£¬²¢ÅäÖà @Transactional µÄ²ÎÊý¡£¹ØÓÚ @Transactional µÄ²ÎÊýÈçͼ 1 Ëùʾ¡£ + +![@Transactional²ÎÊýÁбí](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/765_7af_ff7.png) +ͼ 1 @Transactional²ÎÊýÁбí + +ÏÂÃæÍ¨¹ýÐ޸ġ¶ [Spring»ùÓÚXMLʵÏÖÊÂÎñ¹ÜÀí](http://www.voidme.com/spring/spring-transaction-management-by-xml)¡·½Ì³ÌÖÐÒøÐÐתÕ˵ݸÀý½²½âÈçºÎʹÓà Annotation ×¢½âµÄ·½Ê½ÊµÏÖ Spring ÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£ + +#### 1\. ×¢²áÇý¶¯ + +ÐÞ¸Ä Spring ÅäÖÃÎļþ applicationContext.xml£¬Ð޸ĺóÈçÏÂËùʾ¡£ + + +```` + + + + ```` +```` +ÉÏÊö´úÂëÖпÉÒÔ¿´³ö£¬ÓëÔ­À´µÄÅäÖÃÎļþÏà±È£¬ÕâÀïÖ»ÐÞ¸ÄÁËÊÂÎñ¹ÜÀíÆ÷²¿·Ö£¬ÐÂÌí¼Ó²¢×¢²áÁËÊÂÎñ¹ÜÀíÆ÷µÄÇý¶¯¡£ + +ÐèҪעÒâµÄÊÇ£¬ÔÚѧϰ AOP ×¢½â·½Ê½¿ª·¢Ê±£¬ÐèÒªÔÚÅäÖÃÎļþÖпªÆô×¢½â´¦ÀíÆ÷£¬Ö¸¶¨É¨ÃèÄÄЩ°üϵÄ×¢½â£¬ÕâÀïûÓпªÆô×¢½â´¦ÀíÆ÷ÊÇÒòΪÔÚµÚ 33¡«35 ÐÐÊÖ¶¯ÅäÖÃÁË AccountServiceImpl£¬¶ø @Transactional ×¢½â¾ÍÅäÖÃÔÚ¸ÃÀàÖУ¬ËùÒÔ»áÖ±½ÓÉúЧ¡£ + +#### 2\. Ìí¼Ó @Transactional ×¢½â + +ÐÞ¸Ä AccountServiceImpl£¬ÔÚÎļþÖÐÌí¼Ó @Transactional ×¢½â¼°²ÎÊý£¬Ìí¼ÓºóÈçÏÂËùʾ¡£ +```` +package com.mengma.service.impl; + +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import com.mengma.dao.AccountDao; + +@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) +public class AccountServiceImpl { + private AccountDao accountDao; + public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } + public void transfer(String outUser, String inUser, int money) { this.accountDao.out(outUser, money); // Ä£Äâ¶Ïµç + int i = 1 / 0; this.accountDao.in(inUser, money); +}} +```` + +ÐèҪעÒâµÄÊÇ£¬ÔÚʹÓà @Transactional ×¢½âʱ£¬²ÎÊýÖ®¼äÓá°£¬¡±½øÐзָô¡£ + +ʹÓà JUnit ²âÊÔÔÙ´ÎÔËÐÐ test() ·½·¨Ê±£¬¿ØÖÆÌ¨Í¬Ñù»áÊä³öÈçͼ 2 ËùʾµÄÒì³£ÐÅÏ¢£¬Õâ˵Ã÷ʹÓûùÓÚ Annotation ×¢½âµÄ·½Ê½Í¬ÑùʵÏÖÁË Spring µÄÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£Èç¹û×¢Ê͵ôÄ£Äâ¶ÏµçµÄ´úÂë½øÐвâÊÔ£¬ÔòתÕ˲Ù×÷¿ÉÒÔÕý³£Íê³É¡£ + +![ÔËÐнá¹û](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/764_7a3_f52.png) +ͼ 2 ÔËÐнá¹û + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\220\210\351\233\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\220\210\351\233\206.md" new file mode 100644 index 0000000..931264e --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\220\210\351\233\206.md" @@ -0,0 +1,22 @@ +# Spring + + +# SpringÔ´Âë·ÖÎö + +# SpringMVC + +# SpringMVC Ô´Âë·ÖÎö + +# SpringBoot +## springbootµÄǰÊÀ½ñÉú +## springbootµÄ»ù±¾Ê¹Óà +## springbootµÄ³£¼û×¢½â +## springbootµÄºËÐÄ×é¼þ +## springbootµÄ»ù±¾Ô­Àí +## springbootÔ´Âë½âÎö + +# SpringBoot Ô´Âë·ÖÎö + +# SpringCloud +# SpringCloud Ô´Âë·ÖÎö + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\256\271\345\231\250\344\270\216IOC.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\256\271\345\231\250\344\270\216IOC.md" new file mode 100644 index 0000000..35623db --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\256\271\345\231\250\344\270\216IOC.md" @@ -0,0 +1,157 @@ +IoC ÊÇ Inversion of Control µÄ¼òд£¬ÒëΪ¡°¿ØÖÆ·´×ª¡±£¬Ëü²»ÊÇÒ»Ãż¼Êõ£¬¶øÊÇÒ»ÖÖÉè¼ÆË¼Ï룬ÊÇÒ»¸öÖØÒªµÄÃæÏò¶ÔÏó±à³Ì·¨Ôò£¬Äܹ»Ö¸µ¼ÎÒÃÇÈçºÎÉè¼Æ³öËÉñîºÏ¡¢¸üÓÅÁ¼µÄ³ÌÐò¡£ + +Spring ͨ¹ý IoC ÈÝÆ÷À´¹ÜÀíËùÓÐ Java ¶ÔÏóµÄʵÀý»¯ºÍ³õʼ»¯£¬¿ØÖƶÔÏóÓë¶ÔÏóÖ®¼äµÄÒÀÀµ¹ØÏµ¡£ÎÒÃǽ«ÓÉ IoC ÈÝÆ÷¹ÜÀíµÄ Java ¶ÔÏó³ÆÎª Spring Bean£¬ËüÓëʹÓùؼü×Ö new ´´½¨µÄ Java ¶ÔÏóûÓÐÈκÎÇø±ð¡£ + +IoC ÈÝÆ÷ÊÇ Spring ¿ò¼ÜÖÐ×îÖØÒªµÄºËÐÄ×é¼þÖ®Ò»£¬Ëü¹á´©ÁË Spring ´Óµ®Éúµ½³É³¤µÄÕû¸ö¹ý³Ì¡£ + +## ¿ØÖÆ·´×ª£¨IoC£© + +ÔÚ´«Í³µÄ Java Ó¦ÓÃÖУ¬Ò»¸öÀàÏëÒªµ÷ÓÃÁíÒ»¸öÀàÖеÄÊôÐÔ»ò·½·¨£¬Í¨³£»áÏÈÔÚÆä´úÂëÖÐͨ¹ý new Object() µÄ·½Ê½½«ºóÕߵĶÔÏó´´½¨³öÀ´£¬È»ºó²ÅÄÜʵÏÖÊôÐÔ»ò·½·¨µÄµ÷Óá£ÎªÁË·½±ãÀí½âºÍÃèÊö£¬ÎÒÃÇ¿ÉÒÔ½«Ç°Õß³ÆÎª¡°µ÷ÓÃÕß¡±£¬½«ºóÕß³ÆÎª¡°±»µ÷ÓÃÕß¡±¡£Ò²¾ÍÊÇ˵£¬µ÷ÓÃÕßÕÆÎÕ×ű»µ÷ÓÃÕß¶ÔÏó´´½¨µÄ¿ØÖÆÈ¨¡£ + +µ«ÔÚ Spring Ó¦ÓÃÖУ¬Java ¶ÔÏó´´½¨µÄ¿ØÖÆÈ¨ÊÇÕÆÎÕÔÚ IoC ÈÝÆ÷ÊÖÀïµÄ£¬Æä´óÖ²½ÖèÈçÏ¡£ + +¿ª·¢ÈËԱͨ¹ý XML ÅäÖÃÎļþ¡¢×¢½â¡¢Java ÅäÖÃÀàµÈ·½Ê½£¬¶Ô Java ¶ÔÏó½øÐж¨Ò壬ÀýÈçÔÚ XML ÅäÖÃÎļþÖÐʹÓà ±êÇ©¡¢ÔÚ Java ÀàÉÏʹÓà @Component ×¢½âµÈ¡£ +Spring Æô¶¯Ê±£¬IoC ÈÝÆ÷»á×Ô¶¯¸ù¾Ý¶ÔÏó¶¨Ò壬½«ÕâЩ¶ÔÏó´´½¨²¢¹ÜÀíÆðÀ´¡£ÕâЩ±» IoC ÈÝÆ÷´´½¨²¢¹ÜÀíµÄ¶ÔÏó±»³ÆÎª Spring Bean¡£ +µ±ÎÒÃÇÏëҪʹÓÃij¸ö Bean ʱ£¬¿ÉÒÔÖ±½Ó´Ó IoC ÈÝÆ÷ÖлñÈ¡£¨ÀýÈçͨ¹ý ApplicationContext µÄ getBean() ·½·¨£©£¬¶ø²»ÐèÒªÊÖ¶¯Í¨¹ý´úÂ루ÀýÈç new Obejct() µÄ·½Ê½£©´´½¨¡£ + +IoC ´øÀ´µÄ×î´ó¸Ä±ä²»ÊÇ´úÂë²ãÃæµÄ£¬¶øÊÇ´Ó˼Ïë²ãÃæÉÏ·¢ÉúÁË¡°Ö÷´Ó»»Î»¡±µÄ¸Ä±ä¡£Ô­±¾µ÷ÓÃÕßÊÇÖ÷¶¯µÄÒ»·½£¬ËüÏëҪʹÓÃʲô×ÊÔ´¾Í»áÖ÷¶¯³ö»÷£¬×Ô¼º´´½¨£»µ«ÔÚ Spring Ó¦ÓÃÖУ¬IoC ÈÝÆ÷ÕÆÎÕ×ÅÖ÷¶¯È¨£¬µ÷ÓÃÕßÔò±ä³ÉÁ˱»¶¯µÄÒ»·½£¬±»¶¯µÄµÈ´ý IoC ÈÝÆ÷´´½¨ËüËùÐèÒªµÄ¶ÔÏó£¨Bean£©¡£ + +Õâ¸ö¹ý³ÌÔÚÖ°Ôð²ãÃæ·¢ÉúÁË¿ØÖÆÈ¨µÄ·´×ª£¬°ÑÔ­±¾µ÷ÓÃÕßͨ¹ý´úÂëʵÏֵĶÔÏóµÄ´´½¨£¬·´×ª¸ø IoC ÈÝÆ÷À´°ïæʵÏÖ£¬Òò´ËÎÒÃǽ«Õâ¸ö¹ý³Ì³ÆÎª Spring µÄ¡°¿ØÖÆ·´×ª¡±¡£ + +## ÒÀÀµ×¢È루DI£© + +ÔÚÁ˽âÁË IoC Ö®ºó£¬ÎÒÃÇ»¹ÐèÒªÁ˽âÁíÍâÒ»¸ö·Ç³£ÖØÒªµÄ¸ÅÄÒÀÀµ×¢Èë¡£ + +ÒÀÀµ×¢È루Denpendency Injection£¬¼òдΪ DI£©ÊÇ Martin Fowler ÔÚ 2004 ÄêÔÚ¶Ô¡°¿ØÖÆ·´×ª¡±½øÐнâÊÍʱÌá³öµÄ¡£Martin Fowler ÈÏΪ¡°¿ØÖÆ·´×ª¡±Ò»´ÊºÜ»Þɬ£¬ÎÞ·¨ÈÃÈ˺ÜÖ±½ÓµÄÀí½â¡°µ½µ×ÊÇÄÄÀﷴתÁË¡±£¬Òò´ËËû½¨ÒéʹÓá°ÒÀÀµ×¢È롱À´´úÌæ¡°¿ØÖÆ·´×ª¡±¡£ + +ÔÚÃæÏò¶ÔÏóÖУ¬¶ÔÏóºÍ¶ÔÏóÖ®¼äÊÇ´æÔÚÒ»ÖÖ½Ð×ö¡°ÒÀÀµ¡±µÄ¹ØÏµ¡£¼òµ¥À´Ëµ£¬ÒÀÀµ¹ØÏµ¾ÍÊÇÔÚÒ»¸ö¶ÔÏóÖÐÐèÒªÓõ½ÁíÍâÒ»¸ö¶ÔÏ󣬼´¶ÔÏóÖдæÔÚÒ»¸öÊôÐÔ£¬¸ÃÊôÐÔÊÇÁíÍâÒ»¸öÀàµÄ¶ÔÏó¡£ + +ÀýÈ磬ÓÐÒ»¸öÃûΪ B µÄ Java À࣬ËüµÄ´úÂëÈçÏ¡£ + + + + + + + +public class B { +String bid; +A a; +} + + + + + + + +´Ó´úÂë¿ÉÒÔ¿´³ö£¬B ÖдæÔÚÒ»¸ö A ÀàÐ͵ĶÔÏóÊôÐÔ a£¬´ËʱÎÒÃǾͿÉÒÔ˵ B µÄ¶ÔÏóÒÀÀµÓÚ¶ÔÏó a¡£¶øÒÀÀµ×¢Èë¾ÍÊǾÍÊÇ»ùÓÚÕâÖÖ¡°ÒÀÀµ¹ØÏµ¡±¶ø²úÉúµÄ¡£ + +ÎÒÃÇÖªµÀ£¬¿ØÖÆ·´×ªºËÐÄ˼Ïë¾ÍÊÇÓÉ Spring ¸ºÔð¶ÔÏóµÄ´´½¨¡£ÔÚ¶ÔÏó´´½¨¹ý³ÌÖУ¬Spring »á×Ô¶¯¸ù¾ÝÒÀÀµ¹ØÏµ£¬½«ËüÒÀÀµµÄ¶ÔÏó×¢Èëµ½µ±Ç°¶ÔÏóÖУ¬Õâ¾ÍÊÇËùνµÄ¡°ÒÀÀµ×¢È롱¡£ + +ÒÀÀµ×¢Èë±¾ÖÊÉÏÊÇ [Spring Bean ÊôÐÔ×¢Èë](http://c.biancheng.net/spring/attr-injection.html)µÄÒ»ÖÖ£¬Ö»²»¹ýÕâ¸öÊôÐÔÊÇÒ»¸ö¶ÔÏóÊôÐÔ¶øÒÑ¡£ + +## IoC µÄ¹¤×÷Ô­Àí + +ÔÚ Java Èí¼þ¿ª·¢¹ý³ÌÖУ¬ÏµÍ³Öеĸ÷¸ö¶ÔÏóÖ®¼ä¡¢¸÷¸öÄ£¿éÖ®¼ä¡¢Èí¼þϵͳºÍÓ²¼þϵͳ֮¼ä£¬»ò¶à»òÉÙ¶¼´æÔÚÒ»¶¨µÄñîºÏ¹ØÏµ¡£ + +ÈôÒ»¸öϵͳµÄñîºÏ¶È¹ý¸ß£¬ÄÇô¾Í»áÔì³ÉÄÑÒÔά»¤µÄÎÊÌ⣬µ«ÍêȫûÓÐñîºÏµÄ´úÂ뼸ºõÎÞ·¨Íê³ÉÈκι¤×÷£¬ÕâÊÇÓÉÓÚ¼¸ºõËùÓеŦÄܶ¼ÐèÒª´úÂëÖ®¼äÏ໥Э×÷¡¢Ï໥ÒÀÀµ²ÅÄÜÍê³É¡£Òò´ËÎÒÃÇÔÚ³ÌÐòÉè¼ÆÊ±£¬Ëù±ü³ÐµÄ˼ÏëÒ»°ã¶¼ÊÇÔÚ²»Ó°Ïìϵͳ¹¦ÄܵÄǰÌáÏ£¬×î´óÏ޶ȵĽµµÍñîºÏ¶È¡£ + +IoC µ×²ãͨ¹ý¹¤³§Ä£Ê½¡¢Java µÄ·´Éä»úÖÆ¡¢XML ½âÎöµÈ¼¼Êõ£¬½«´úÂëµÄñîºÏ¶È½µµÍµ½×îµÍÏÞ¶È£¬ÆäÖ÷Òª²½ÖèÈçÏ¡£ + +ÔÚÅäÖÃÎļþ£¨ÀýÈç Bean.xml£©ÖУ¬¶Ô¸÷¸ö¶ÔÏóÒÔ¼°ËüÃÇÖ®¼äµÄÒÀÀµ¹ØÏµ½øÐÐÅäÖã» +ÎÒÃÇ¿ÉÒÔ°Ñ IoC ÈÝÆ÷µ±×öÒ»¸ö¹¤³§£¬Õâ¸ö¹¤³§µÄ²úÆ·¾ÍÊÇ Spring Bean£» +ÈÝÆ÷Æô¶¯Ê±»á¼ÓÔØ²¢½âÎöÕâЩÅäÖÃÎļþ£¬µÃµ½¶ÔÏóµÄ»ù±¾ÐÅÏ¢ÒÔ¼°ËüÃÇÖ®¼äµÄÒÀÀµ¹ØÏµ£» +IoC ÀûÓà Java µÄ·´Éä»úÖÆ£¬¸ù¾ÝÀàÃûÉú³ÉÏàÓ¦µÄ¶ÔÏ󣨼´ Spring Bean£©£¬²¢¸ù¾ÝÒÀÀµ¹ØÏµ½«Õâ¸ö¶ÔÏó×¢Èëµ½ÒÀÀµËüµÄ¶ÔÏóÖС£ + +ÓÉÓÚ¶ÔÏóµÄ»ù±¾ÐÅÏ¢¡¢¶ÔÏóÖ®¼äµÄÒÀÀµ¹ØÏµ¶¼ÊÇÔÚÅäÖÃÎļþÖж¨ÒåµÄ£¬²¢Ã»ÓÐÔÚ´úÂëÖнôÃÜñîºÏ£¬Òò´Ë¼´Ê¹¶ÔÏó·¢Éú¸Ä±ä£¬ÎÒÃÇÒ²Ö»ÐèÒªÔÚÅäÖÃÎļþÖнøÐÐÐ޸ļ´¿É£¬¶øÎÞÐë¶Ô Java ´úÂë½øÐÐÐ޸ģ¬Õâ¾ÍÊÇ Spring IoC ʵÏÖ½âñîµÄÔ­Àí¡£ + +## IoC ÈÝÆ÷µÄÁ½ÖÖʵÏÖ + +IoC ˼Ïë»ùÓÚ IoC ÈÝÆ÷ʵÏֵģ¬IoC ÈÝÆ÷µ×²ãÆäʵ¾ÍÊÇÒ»¸ö Bean ¹¤³§¡£Spring ¿ò¼ÜΪÎÒÃÇÌṩÁËÁ½ÖÖ²»Í¬ÀàÐÍ IoC ÈÝÆ÷£¬ËüÃÇ·Ö±ðÊÇ BeanFactory ºÍ ApplicationContext¡£ + +### BeanFactory + +BeanFactory ÊÇ IoC ÈÝÆ÷µÄ»ù±¾ÊµÏÖ£¬Ò²ÊÇ Spring ÌṩµÄ×î¼òµ¥µÄ IoC ÈÝÆ÷£¬ËüÌṩÁË IoC ÈÝÆ÷×î»ù±¾µÄ¹¦ÄÜ£¬ÓÉ org.springframework.beans.factory.BeanFactory ½Ó¿Ú¶¨Òå¡£ + +BeanFactory ²ÉÓÃÀÁ¼ÓÔØ£¨lazy-load£©»úÖÆ£¬ÈÝÆ÷ÔÚ¼ÓÔØÅäÖÃÎļþʱ²¢²»»áÁ¢¿Ì´´½¨ Java ¶ÔÏó£¬Ö»ÓгÌÐòÖлñÈ¡£¨Ê¹Óã©Õâ¸ö¶Ô¶ÔÏóʱ²Å»á´´½¨¡£ + +#### ʾÀý 1 + +ÏÂÃæÎÒÃÇͨ¹ýÒ»¸öʵÀýÑÝʾ£¬À´ÑÝʾÏ BeanFactory µÄʹÓᣠ+ +1\. ÔÚ HelloSpring ÏîÄ¿ÖУ¬½« MainApp µÄ´úÂëÐÞ¸ÄΪʹÓà BeanFactory »ñÈ¡ HelloWorld µÄ¶ÔÏ󣬾ßÌå´úÂëÈçÏ¡£ + + + + + + + +public static void main(String[] args) { +BeanFactory context = new ClassPathXmlApplicationContext("Beans.xml"); +HelloWorld obj = context.getBean("helloWorld", HelloWorld.class); +obj.getMessage(); +} + + + + + + + +2. ÔËÐÐ MainApp.java£¬¿ØÖÆÌ¨Êä³öÈçÏ¡£ + + message : Hello World! + +> ×¢Ò⣺BeanFactory ÊÇ Spring ÄÚ²¿Ê¹Óýӿڣ¬Í¨³£Çé¿öϲ»Ìṩ¸ø¿ª·¢ÈËԱʹÓᣠ+ +### ApplicationContext + +ApplicationContext ÊÇ BeanFactory ½Ó¿ÚµÄ×Ó½Ó¿Ú£¬ÊÇ¶Ô BeanFactory µÄÀ©Õ¹¡£ApplicationContext ÔÚ BeanFactory µÄ»ù´¡ÉÏÔö¼ÓÁËÐí¶àÆóÒµ¼¶µÄ¹¦ÄÜ£¬ÀýÈç AOP£¨ÃæÏòÇÐÃæ±à³Ì£©¡¢¹ú¼Ê»¯¡¢ÊÂÎñÖ§³ÖµÈ¡£ + +ApplicationContext ½Ó¿ÚÓÐÁ½¸ö³£ÓõÄʵÏÖÀ࣬¾ßÌåÈçÏÂ±í¡£ + +| ʵÏÖÀà | ÃèÊö | ʾÀý´úÂë | +| --- | --- | --- | +| ClassPathXmlApplicationContext | ¼ÓÔØÀà·¾¶ ClassPath ÏÂÖ¸¶¨µÄ XML ÅäÖÃÎļþ£¬²¢Íê³É ApplicationContext µÄʵÀý»¯¹¤×÷ | ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation); | +| FileSystemXmlApplicationContext | ¼ÓÔØÖ¸¶¨µÄÎļþϵͳ·¾¶ÖÐÖ¸¶¨µÄ XML ÅäÖÃÎļþ£¬²¢Íê³É ApplicationContext µÄʵÀý»¯¹¤×÷ | ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation); | + +> ÔÚÉϱíµÄʾÀý´úÂëÖУ¬²ÎÊý configLocation ÓÃÓÚÖ¸¶¨ Spring ÅäÖÃÎļþµÄÃû³ÆºÍλÖã¬Èç Beans.xml¡£ + +#### ʾÀý 2 + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´ÑÝʾ ApplicationContext µÄʹÓᣠ+ +1. ÐÞ¸Ä HelloSpring ÏîÄ¿ MainApp ÀàÖÐ main() ·½·¨µÄ´úÂ룬¾ßÌå´úÂëÈçÏ¡£ + + + + + + +```` +public static void main(String[] args) { +//ʹÓà FileSystemXmlApplicationContext ¼ÓÔØÖ¸¶¨Â·¾¶ÏµÄÅäÖÃÎļþ Bean.xml +BeanFactory context = new FileSystemXmlApplicationContext("D:\\eclipe workspace\\spring workspace\\HelloSpring\\src\\Beans.xml"); +HelloWorld obj = context.getBean("helloWorld", HelloWorld.class); +obj.getMessage(); +} +```` + + + + + + +2. ÔËÐÐ MainApp.java£¬¿ØÖÆÌ¨Êä³öÈçÏ¡£ + + message : Hello World! + +# ²Î¿¼ÎÄÕ +https://www.w3cschool.cn/wkspring +https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html +http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring +https://dunwu.github.io/spring-tutorial +https://mszlu.com/java/spring +http://c.biancheng.net/spring/aop-module.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\270\270\350\247\201\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\270\270\350\247\201\346\263\250\350\247\243.md" new file mode 100644 index 0000000..c5b2238 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\345\270\270\350\247\201\346\263\250\350\247\243.md" @@ -0,0 +1,724 @@ +# Spring×¢½â +## 1 ¸ÅÊö + +ÎÒÃǶ¼ÖªµÀSpring×îºËÐĵÄÌØÐÔ¾ÍÊÇIOC+AOP£¬IOCµÄÔ­Àí¾ÍÊÇʵÏÖÁËÒ»¸öSpringÈÝÆ÷£¬ÓÃÀ´¹ÜÀíËùÓÐSpring BeanʵÀý +DI£¬Ò²¾ÍÊÇÒÀÀµ×¢È룬ÊÇÎÒÃÇ×÷Ϊ¿ª·¢ÕßÐèÒª¹ØÐĵĺËÐÄ»°Ì⣬ÈçºÎ×¢ÈëÒÀÀµ£¬×¢ÈëÄĸöÒÀÀµ£¬ÊÇÎÒÃÇÐèÒªÃ÷È·ÖªµÀµÄ¡£ +ÔÚÒÔǰ£¬ÎÒÃÇϰ¹ßÓÚÓÃxmlÅäÖÃÎļþÀ´ÉùÃ÷Ò»¸öbean£¬µ«ÏÖÔÚ£¬ÎÒÃǸü¶àµØÊ¹ÓÃ×¢½âºÍ´úÂëÀ´Íê³ÉDIµÄ¹ý³Ì + + +ÎÒÃÇ¿ÉÒÔʹÓà org.springframework.beans.factory.annotation ºÍ org.springframework.context.annotation °üÖеÄ×¢ÊÍÀ´ÆôÓà Spring DI ÒýÇæµÄ¹¦ÄÜ¡£ + +ÎÒÃÇͨ³£½«ÕâЩ³ÆÎª¡°Spring ºËÐÄ×¢ÊÍ¡±£¬ÎÒÃǽ«ÔÚ±¾½Ì³ÌÖÐ¶ÔÆä½øÐлعˡ£ + + +## 2 DIÏà¹Ø×¢½â + +### 2.1 @Autowired + +ÎÒÃÇ¿ÉÒÔʹÓà @Autowired À´±ê¼Ç Spring ½«Òª½âÎöºÍ×¢ÈëµÄÒÀÀµÏî¡£ +ÎÒÃÇ¿ÉÒÔ½«´Ë×¢ÊÍÓë¹¹Ô캯Êý¡¢setter »ò×Ö¶Î×¢ÈëÒ»ÆðʹÓᣠ+Constructor injection: + +**¹¹ÔìÆ÷×¢Èë** + +```` +class Car { + Engine engine; + + @Autowired + Car(Engine engine) { + this.engine = engine; + } +} +```` + +**Setter×¢Èë** +```` +class Car { + Engine engine; + + @Autowired + void setEngine(Engine engine) { + this.engine = engine; + } +} +```` +**±äÁ¿×¢Èë** +```` +class Car { + @Autowired + Engine engine; +} +```` + +@Autowired ÓÐÒ»¸öÃûΪ required µÄ²¼¶û²ÎÊý£¬Ä¬ÈÏֵΪ true¡£ + +µ±ËüÕÒ²»µ½ºÏÊ浀 bean À´Á¬½Óʱ£¬Ëü»áµ÷Õû Spring µÄÐÐΪ¡£ µ±Îª true ʱ£¬½«Å׳öÒì³££¬·ñÔò²»Á¬½ÓÈκÎÄÚÈÝ¡£ + +Çë×¢Ò⣬Èç¹ûÎÒÃÇʹÓù¹Ô캯Êý×¢È룬ÔòËùÓй¹Ô캯Êý²ÎÊý¶¼ÊÇÇ¿ÖÆÐԵġ£ + +´Ó 4.3 °æ±¾¿ªÊ¼£¬ÎÒÃDz»ÐèÒªÏÔʽµØÊ¹Óà @Autowired ×¢½â¹¹Ô캯Êý£¬³ý·ÇÎÒÃÇÉùÃ÷ÖÁÉÙÁ½¸ö¹¹Ô캯Êý¡£ + +### 2.2 @Bean + +@Bean ±ê¼ÇʵÀý»¯ Spring bean µÄ¹¤³§·½·¨£º + +``` +@Bean +Engine engine() { + return new Engine(); +} +```` + +µ±ÐèÒª·µ»ØÀàÐ͵ÄÐÂʵÀýʱ£¬Spring »áµ÷ÓÃÕâЩ·½·¨¡£ + +Éú³ÉµÄ bean Ó빤³§·½·¨Í¬Ãû¡£ Èç¹ûÎÒÃÇÏëÒÔ²»Í¬µÄ·½Ê½ÃüÃû£¬ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵ÄÃû³Æ»òÖµ²ÎÊý£¨²ÎÊýÖµÊDzÎÊýÃû³ÆµÄ±ðÃû£©£º + +```` +@Bean("engine") +Engine getEngine() { + return new Engine(); +} +```` +ÕâÊÇÒ»Öַdz£³£¼ûµÄbeanÉùÃ÷·½Ê½£¬ÒòΪºÜ¶àµÄbean²¢²»ÊÇÎÒÃÇÒ»¿ªÊ¼¾ÍÔÚ´úÂëÀﶨÒåºÃµÄ£¬Ëü¿ÉÄÜÐèÒª»ùÓÚÔËÐÐʱ»·¾³À´½øÐа´Ðè¹¹½¨¡£ + +ÎÒÃÇ¿ÉÒÔ×ÔÓɵØÉùÃ÷ºÍ¶¨ÒåBean£¬²¢ÇÒÒ²¿ÉÒÔ¸³ÓèËü×Ô¶¨ÒåµÄbeanÃû³Æ¡£ + +×¢Ò⣬ËùÓÐÓÃ@Bean ×¢Ê͵ķ½·¨¶¼±ØÐëÔÚ@Configuration ÀàÖС£ + +### 2.3 @Qualifier + +ÎÒÃÇʹÓÃ@Qualifier ºÍ@Autowired À´ÌṩÎÒÃÇÏëÒªÔÚ²»Ã÷È·Çé¿öÏÂʹÓõÄbean id »òbean Ãû³Æ¡£ + +ÀýÈ磬ÒÔÏÂÁ½¸ö bean ʵÏÖÏàͬµÄ½Ó¿Ú£º +```` +class Bike implements Vehicle {} + +class Car implements Vehicle {} + +```` + +Èç¹û Spring ÐèҪעÈëÒ»¸ö Vehicle bean£¬Ëü»áÒÔ¶à¸öÆ¥Å䶨Òå½áÊø¡£ ÔÚÕâÖÖÇé¿öÏ£¬ÎÒÃÇ¿ÉÒÔʹÓà @Qualifier ×¢ÊÍÏÔʽÌṩ bean µÄÃû³Æ¡£ + +**¹¹ÔìÆ÷×¢Èë** +```` +@Autowired +Biker(@Qualifier("bike") Vehicle vehicle) { +this.vehicle = vehicle; +} +```` + +**Setter×¢Èë** + +```` +@Autowired +void setVehicle(@Qualifier("bike") Vehicle vehicle) { +this.vehicle = vehicle; +} +```` +»òÕß: + +```` +@Autowired +@Qualifier("bike") +void setVehicle(Vehicle vehicle) { +this.vehicle = vehicle; +```` +**±äÁ¿×¢Èë** + +```` +@Autowired +@Qualifier("bike") +Vehicle vehicle; +```` +Õâ¸ö×¢½âÎÒÃÇ¿ÉÄÜÆ½³£ÓõIJ»¶à£¬µ«Êǵ±Ò»¸ö½Ó¿ÚÓжà¸öʵÏÖÀàʱ£¬Ëü¾Í»á¾­³£ÅÉÉÏÓ󡣡 + +### 2.4 @Required + +@Required ÔÚ setter ·½·¨Éϱê¼ÇÎÒÃÇÏëҪͨ¹ý XML Ìî³äµÄÒÀÀµÏ +```` +@Required +void setColor(String color) { +this.color = color; +} +```` +xml +```` + + + +```` +·ñÔò£¬½«Å׳ö BeanInitializationException¡£ +·Ç³£ÉÙ¼ûµÄÓ÷¨£¬ÖªµÀһϾÍÐÐÁË + +### 2.5 @Value +ÎÒÃÇ¿ÉÒÔʹÓà @Value ½«ÊôÐÔÖµ×¢Èë bean¡£ ËüÓë¹¹Ô캯Êý¡¢setter ºÍ×Ö¶Î×¢Èë¼æÈÝ¡£ + +ÕâÒ²ÊǷdz£³£ÓõÄÒ»¸ö×¢½â£¬ÒòΪÎÒÃǺܶàʱºò¶¼ÐèÒª´Óapplication.properties»òÕ߯äËûÅäÖÃÎļþÖÐÀ´¶ÁÈ¡ÅäÖÃÊôÐÔÖµ¡£ + +**¹¹ÔìÆ÷×¢Èë** +```` +Engine(@Value("8") int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +**setter×¢Èë** + +```` +@Autowired +void setCylinderCount(@Value("8") int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +»òÕß: +```` + +@Value("8") +void setCylinderCount(int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +**±äÁ¿×¢Èë** +```` +@Value("8") +int cylinderCount; +```` + +µ±È»£¬×¢È뾲ֵ̬ÊÇûÓÐÓõġ£ Òò´Ë£¬ÎÒÃÇ¿ÉÒÔÔÚ @Value ÖÐʹÓÃռλ·û×Ö·û´®À´Á¬½ÓÔÚÍⲿԴÖж¨ÒåµÄÖµ£¬ÀýÈçÔÚ .properties »ò .yaml ÎļþÖС£ +```` + +engine.fuelType=petrol +```` + +ÎÒÃÇ¿ÉÒÔͨ¹ýÒÔÏ·½Ê½×¢Èë engine.fuelType µÄÖµ£º + +```` +@Value("${engine.fuelType}") +String fuelType; +```` + +ÎÒÃÇÉõÖÁ¿ÉÒÔʹÓà SpEL À´Ê¹ÓÃ@Value¡£ ¸ü¶à¸ß¼¶Ê¾Àý¿ÉÒÔÔÚÎÒÃǹØÓÚ@Value µÄÎÄÕÂÖÐÕÒµ½¡£ + +### 2.6 @DependsOn +ÎÒÃÇ¿ÉÒÔʹÓôË×¢½âÈà Spring ÔÚ×¢½âµÄ bean ֮ǰ³õʼ»¯ÆäËû bean¡£ ͨ³££¬´ËÐÐΪÊÇ×Ô¶¯µÄ£¬»ùÓÚ bean Ö®¼äµÄÏÔʽÒÀÀµ¹ØÏµ¡£ + +ÎÒÃÇÖ»ÓÐÔÚÒÀÀµÊÇÒþʽµÄʱºò²ÅÐèÒªÕâ¸ö×¢½â£¬±ÈÈçJDBCÇý¶¯¼ÓÔØ»òÕß¾²Ì¬±äÁ¿³õʼ»¯¡£ + +ÎÒÃÇ¿ÉÒÔÔÚÖ¸¶¨ÒÀÀµ bean Ãû³ÆµÄÒÀÀµÀàÉÏʹÓà @DependsOn¡£ ×¢Ê͵ÄÖµ²ÎÊýÐèÒªÒ»¸ö°üº¬ÒÀÀµ bean Ãû³ÆµÄÊý×飺 + +```` +@DependsOn("engine") +class Car implements Vehicle {} +```` +Alternatively, if we define a bean with the @Bean annotation, the factory method should be annotated with @DependsOn: +```` +@Bean +@DependsOn("fuel") +Engine engine() { +return new Engine(); +} +```` +### 2.7 @Lazy +µ±ÎÒÃÇÏëÀÁ¶èµØ³õʼ»¯ÎÒÃÇµÄ bean ʱ£¬ÎÒÃÇʹÓà @Lazy¡£ ĬÈÏÇé¿öÏ£¬Spring ÔÚÓ¦ÓóÌÐòÉÏÏÂÎĵįô¶¯/Òýµ¼Ê±¼±Çеش´½¨ËùÓе¥Àý bean¡£ + +µ«ÊÇ£¬ÓÐЩÇé¿öÏÂÎÒÃÇÐèÒªÔÚÇëÇóʱ´´½¨ bean£¬¶ø²»ÊÇÔÚÓ¦ÓóÌÐòÆô¶¯Ê±´´½¨¡£ + +Õâ¸ö×¢½âµÄÐÐΪ»á¸ù¾ÝÎÒÃÇ·ÅÖÃËüµÄÈ·ÇÐλÖöøÓÐËù²»Í¬¡£ ÎÒÃÇ¿ÉÒÔ°ÑËü·ÅÔÚ£º + +Ò»¸ö @Bean ×¢Ê굀 bean ¹¤³§·½·¨£¬ÒÔÑÓ³Ù·½·¨µ÷Óã¨Òò´Ë´´½¨ bean£© +@Configuration ÀàºÍËùÓаüº¬µÄ@Bean ·½·¨¶¼»áÊܵ½Ó°Ïì + +Ò»¸ö @Component À࣬Ëü²»ÊÇ @Configuration À࣬Õâ¸ö bean ½«±»ÑÓ³Ù³õʼ»¯ + +@Autowired ¹¹Ô캯Êý¡¢setter »ò×ֶΣ¬ÓÃÓÚÑÓ³Ù¼ÓÔØÒÀÀµÏî±¾Éí£¨Í¨¹ý´úÀí£© + +```` +@Configuration +@Lazy +class VehicleFactoryConfig { + + @Bean + @Lazy(false) + Engine engine() { + return new Engine(); + } +} +```` +ÕâͬÑù²»ÊÇÒ»¸ö³£ÓõÄ×¢½â¡£ + +µ±ÎÒÃÇά»¤Ò»¸öÓдóÁ¿beanµÄÏîĿʱ£¬»á·¢ÏÖÓкܶàbean¿ÉÄܶ¼Êǰ´ÐèʹÓõ쬲»Ò»¶¨ÐèÒªÔÚÈÝÆ÷³õʼ»¯Ê±¾Í½øÐгõʼ»¯£¬Õâ¿ÉÒÔ°ïÎÒÃǽÚÊ¡ºÜ¶àʱ¼äºÍÐÔÄÜ¡£ + + +### 2.8 @Lookup +ͬÑùÊÇÒ»¸ö±È½ÏÉÙÓõ½µÄ×¢½â + +@Lookup »á¸æËß Spring ÔÚÎÒÃǵ÷ÓÃËüʱ·µ»Ø·½·¨·µ»ØÀàÐ͵ÄʵÀý¡£ + +±¾ÖÊÉÏ£¬Spring ½«¸²¸ÇÎÒÃÇ´ø×¢Ê͵ķ½·¨²¢Ê¹ÓÃÎÒÃÇ·½·¨µÄ·µ»ØÀàÐͺͲÎÊý×÷Ϊ BeanFactory#getBean µÄ²ÎÊý¡£ + +@Lookup ÊÊÓÃÓÚ£º + +½«Ô­ÐÍ bean ×¢ÈëÎÒÃÇ×Ô¼ºµÄbean£¨ÀàËÆÓÚ Provider£© + +Èç¹ûÎÒÃÇÅöÇɾö¶¨ÓµÓÐÒ»¸öÔ­ÐÍ Spring bean£¬ÄÇôÎÒÃǼ¸ºõ»áÁ¢¼´ÃæÁÙÕâÑùµÄÎÊÌ⣺ + +ÎÒÃǵĵ¥Àý Spring bean ½«ÈçºÎ·ÃÎÊÕâЩԭÐÍ Spring bean£¿ + +ÏÖÔÚ£¬Provider ¿Ï¶¨ÊÇÒ»ÖÖ·½Ê½£¬¾¡¹Ü @Lookup ÔÚijЩ·½Ãæ¸ü¼ÓͨÓᣠ+ +ҪעÒâµÄÊÇ£¬springĬÈÏʹÓõĵ¥Àýbean£¬ËùÒÔÈç¹ûÎÒÃÇҪעÈëÔ­ÐÍbean£¬ÎÒÃDzÅÐèÒª×öÕâÑùµÄ¶îÍ⹤×÷ + +Ê×ÏÈ£¬ÈÃÎÒÃÇ´´½¨Ò»¸öÔ­ÐÍ bean£¬ÉÔºóÎÒÃǽ«Æä×¢Èëµ½µ¥Àý bean ÖУº +```` +@Component +@Scope("prototype") +public class SchoolNotification { +// ... prototype-scoped state +} +```` +ʹÓÃ@Lookup£¬ÎÒÃÇ¿ÉÒÔͨ¹ýµ¥Àý bean »ñÈ¡ SchoolNotification µÄʵÀý£º + +```` +@Component +public class StudentServices { + + // ... member variables, etc. + + @Lookup + public SchoolNotification getNotification() { + return null; + } + + // ... getters and setters +} +```` +Using @Lookup, we can get an instance of SchoolNotification through our singleton bean: +```` +@Test +public void whenLookupMethodCalled_thenNewInstanceReturned() { +// ... initialize context +StudentServices first = this.context.getBean(StudentServices.class); +StudentServices second = this.context.getBean(StudentServices.class); + + assertEquals(first, second); + assertNotEquals(first.getNotification(), second.getNotification()); +} +```` +Çë×¢Ò⣬ÔÚ StudentServices ÖУ¬ÎÒÃǽ« getNotification ·½·¨±£ÁôΪ´æ¸ù¡£ + +ÕâÊÇÒòΪ Spring ͨ¹ýµ÷Óà beanFactory.getBean(StudentNotification.class) ¸²¸ÇÁ˸÷½·¨£¬Òò´ËÎÒÃÇ¿ÉÒÔ½«ÆäÁô¿Õ¡£ + + +### 2.9 @Primary +ÓÐʱÎÒÃÇÐèÒª¶¨Òå¶à¸öÏàͬÀàÐ͵Äbean¡£ ÔÚÕâЩÇé¿öÏ£¬×¢È뽫²»³É¹¦£¬ÒòΪ Spring ²»ÖªµÀÎÒÃÇÐèÒªÄĸö bean¡£ + +ÎÒÃÇÒѾ­¿´µ½ÁË´¦ÀíÕâÖÖÇé¿öµÄÑ¡ÏÓÃ@Qualifier ±ê¼ÇËùÓнÓÏߵ㲢ָ¶¨ËùÐè bean µÄÃû³Æ¡£ + +È»¶ø£¬´ó¶àÊýʱºòÎÒÃÇÐèÒªÒ»¸öÌØ¶¨µÄ bean£¬ºÜÉÙÐèÒªÆäËû bean¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃ@Primary À´¼ò»¯ÕâÖÖÇé¿ö£ºÈç¹ûÎÒÃÇÓÃ@Primary ±ê¼Ç×î³£ÓõÄbean£¬Ëü½«ÔÚunqualifiedµÄ×¢ÈëµãÉϱ»Ñ¡Ôñ£º + +```` +@Component +@Primary +class Car implements Vehicle {} + +@Component +class Bike implements Vehicle {} + +@Component +class Driver { +@Autowired +Vehicle vehicle; +} + +@Component +class Biker { +@Autowired +@Qualifier("bike") +Vehicle vehicle; +} +```` +ÔÚÇ°ÃæµÄʾÀýÖУ¬Æû³µÊÇÖ÷Òª³µÁ¾¡£ Òò´Ë£¬ÔÚ Driver ÀàÖУ¬Spring ×¢ÈëÁËÒ»¸ö Car bean¡£ µ±È»£¬ÔÚ Biker bean ÖУ¬×Ö¶Î vehicle µÄÖµ½«ÊÇÒ»¸ö Bike ¶ÔÏó£¬ÒòΪËüÊÇqualifiedµÄ¡£ + +### 2.10 @Scope + +¶ÔÓÚͨ³£µÄÓ¦ÓÃÀ´Ëµ£¬ÎÒÃÇbeanµÄscopeĬÈ϶¼Êǵ¥ÀýµÄ£¬µ«Êµ¼ÊÉÏspring bean¿ÉÒÔÖ§³Ö¶àÖÖ¶àÑùµÄ×÷Ó÷¶Î§£¬ËüÃÇÓÐ×Ų»Í¬µÄÉúÃüÖÜÆÚ¡£ + +ÎÒÃÇʹÓÃ@Scope À´¶¨Òå@Component Àà»ò@Bean ¶¨ÒåµÄ·¶Î§¡£ Ëü¿ÉÒÔÊǵ¥Àý¡¢Ô­ÐÍ¡¢ÇëÇ󡢻Ự¡¢globalSession »òһЩ×Ô¶¨Ò巶Χ¡£ + +¶ÔÓ¦µÄö¾ÙֵΪ +```` +singleton +prototype +request +session +application +websocket +```` + +Àý×Ó +```` +@Component +@Scope("prototype") +class Engine {} +```` +ÎÒÃÇ¿ÉÒÔÏëÏóһЩ³¡¾°£¬±ÈÈçrequest¡¢session¡¢websocketÕâÀà×÷ÓÃÓòµÄbean£¬Í¨³£Ó¦¸ÃÊǺÍÍøÂçÇëÇóÏà¹ØµÄ£¬±ÈÈçÓô洢Óû§ÐÅÏ¢¡¢sessionÖ®ÀàµÄbean¡£ + +¾ßÌåÔõôʹÓ㬾ÍÒª¸ù¾ÝÄãµÄ¾ßÌ峡¾°À´Ñ¡ÔñÁË£¬Õâ»áÊÇÒ»¸öºÜ´óµÄ»°Ì⣬ÕâÀï¾ÍÏȲ»Õ¹¿ªÁË£¬ÒÔºó¿ÉÒÔÔÚµ¥¶ÀµÄÎÄÕÂÀï¼ÌÐø½éÉÜ¡£ + +## 3 ÉÏÏÂÎÄÅäÖÃ×¢½â +ÎÒÃÇ¿ÉÒÔʹÓñ¾½ÚÖÐÃèÊöµÄ×¢ÊÍÀ´ÅäÖÃÓ¦ÓóÌÐòÉÏÏÂÎÄ¡£ + + +### 3.1 @Profile + +Èç¹ûÎÒÃÇÏ£Íû Spring ½öÔÚÌØ¶¨ÅäÖÃÎļþ´¦Óڻ״̬ʱʹÓÃ@Component Àà»ò@Bean ·½·¨£¬ÎÒÃÇ¿ÉÒÔʹÓÃ@Profile ¶ÔÆä½øÐбê¼Ç¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃ×¢Ê͵ÄÖµ²ÎÊýÅäÖÃÅäÖÃÎļþµÄÃû³Æ£º + +ÎÒÃÇͨ³£Õâ¸ö×¢½âÀ´ÅäÖò»Í¬»·¾³µÄÅäÖᣠ+±ÈÈçÏÂÃæÕâ¸öÀý×Ó + +```` +public interface DatasourceConfig { +public void setup(); +} +```` + +ÏÂÃæÊÇ¿ª·¢»·¾³µÄÅäÖ㺠+ +```` +@Component +@Profile("dev") +public class DevDatasourceConfig implements DatasourceConfig { +@Override +public void setup() { +System.out.println("Setting up datasource for DEV environment. "); +} +} +```` +ÏÂÃæÊÇÉú²ú»·¾³µÄÅäÖ㺠+ +```` +@Component +@Profile("production") +public class ProductionDatasourceConfig implements DatasourceConfig { +@Override +public void setup() { +System.out.println("Setting up datasource for PRODUCTION environment. "); +} +} +```` +µ±È»ÎÒÃÇÒ²¿ÉÒÔʹÓÃxml»òÕ߯äËûÀàÐ͵ÄÅäÖÃÎļþÀ´ÃèÊöÕâ¸öÅäÖÃbean + +xml +```` + + + +```` +### 3.2 @Import + +ÎÒÃÇ¿ÉÒÔʹÓÃÌØ¶¨µÄ @Configuration À࣬¶øÎÞÐèʹÓôË×¢½â½øÐÐ×é¼þɨÃè¡£ ÎÒÃÇ¿ÉÒÔΪÕâЩÀàÌṩ@Import µÄÖµ²ÎÊý + +Õý³£À´Ëµ£¬Èç¹ûÒªÓõ½@Configuration×¢½âµÄbean£¬ÄÇôspringÓ¦ÓñØÐëҪɨÃèµ½¸ÃĿ¼£¬Èç¹ûÇ¡ºÃûÓжԸ÷¾¶½øÐÐɨÃ裬»òÕßÎÒÃÇÖ»ÊÇÏëʹÓ÷¾¶Ïµĵ¥¸öÅäÖÃÀ࣬ÄÇôÎÒÃǾͿÉÒÔʹÓÃ@ImportÀ´µ¼ÈëÕâ¸öÅäÖÃÀàÁË¡£ + +Õâ¸ö×¢½â»¹ÊǷdz£³£Óõģ¬¿´Ò»ÏÂÏÂÃæÕâ¸öÀý×Ó + +```` +@Import(VehiclePartSupplier.class) +class VehicleFactoryConfig {} + +@Configuration +class VehiclePartSupplier{ +} +```` + +### 3.3 @ImportResource + +±ÈÈç˵£ºÏÖÔÚÓÐÒ»¸ö bean.xml µÄÅäÖÃÎļþ£¬ÐèÒª½«¸Ã beans.xml Öж¨ÒåµÄ bean¶ÔÏó ¶¼µ¼Èëµ½ Spring Boot »·¾³µÄÈÝÆ÷ÖУ¬¸ÃÈçºÎ²Ù×÷ÄØ£¿ + +1.Spring ·½Ê½µÄÅäÖÃÎļþ bean.xml ´Ë´¦Ëæ±ã¾Ù¸öʾÀý£¬±ÈÈç˵ xml ÖÐÅäÖÃÁËÒ»¸ö helloService£¬ÈçÏÂËùʾ +```` + + + + + + +```` +2.ʹÓÃ@ImportResource×¢½â£¬ÒýÈë xml ÅäÖà +```` +/** + * Spring BootÀïÃæÃ»ÓÐSpringµÄÅäÖÃÎļþ£¬ÎÒÃÇ×Ô¼º±àдµÄÅäÖÃÎļþ£¬Ò²²»ÄÜ×Ô¶¯Ê¶±ð£» + * Èç¹ûÏëÈÃSpringµÄÅäÖÃÎļþÉúЧ£¬¼ÓÔØµ½Spring ÈÝÆ÷ÖÐÀ´£» + * ʹÓÃ@ImportResource×¢½â£¬½«Æä±ê×¢ÔÚÒ»¸öÅäÖÃÀàÉÏ(´Ë´¦ÅäÖÃÔÚÆô¶¯Àà) + */ +@SpringBootApplication +@ImportResource(locations = {"classpath:beans.xml"}) +public class BootApplication { + + public static void main(String[] args) { + // SpringÓ¦ÓÃÆô¶¯ÆðÀ´ + SpringApplication.run(BootApplication.class,args); + + } +} + +```` +### 3.4 @PropertySource +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔΪӦÓóÌÐòÉèÖö¨ÒåÊôÐÔÎļþ£º + +@PropertySource ×¢½âÌṩÁËÒ»ÖÖ·½±ãµÄÉùÃ÷ÐÔ»úÖÆ£¬ÓÃÓÚ½« PropertySource Ìí¼Óµ½ Spring µÄ Environment ÖУ¬Óë @Configuration ÀàÒ»ÆðʹÓᣠ+ +Äã¿ÉÒÔʹÓà @Value È¥ÒýÓö¨ÒåµÄÊôÐÔ£¬ÀýÈ磺@Value("testbean.name")£»Ò²¿ÉÒÔÖ¸¶¨Ä¬ÈÏÖµ£¬È磺@Value("testbean.name:defaultValue")¡£ + +Ó÷¨Ê¾Àý + +¸ø¶¨Ò»¸öÅäÖÃÎļþapp.properties +```` +testbean.name=myTestBean +```` +ÒÔÏ @Configuration ÀàʹÓà @PropertySource ½« app.properties ÉèÖøø Environment µÄ PropertySources ¼¯ºÏ¡£ +```` +@Configuration +@PropertySource("classpath:/com/myco/app.properties") +public class AppConfig { + + @Autowired + Environment env; + + @Bean + public TestBean testBean() { + TestBean testBean = new TestBean(); + testBean.setName(env.getProperty("testbean.name")); + return testBean; + } +} +```` + +×¢Ò⣺ʹÓà @Autowired ½« Environment ¶ÔÏó×¢Èëµ½ÅäÖÃÀàÖУ¬È»ºóÔÚ testBean() ·½·¨ÖÐʹÓᣠ+ÒÔÉÏÅäÖÃÖУ¬µ÷Óà testBean.getName() ·½·¨½«·µ»Ø¡°myTestBean¡±×Ö·û´®¡£ + +@PropertySource ÀûÓÃÁË Java 8 µÄÖØ¸´×¢½âÌØÐÔ£¬ÕâÒâζ×ÅÎÒÃÇ¿ÉÒÔÓÃËü¶à´Î±ê¼ÇÒ»¸öÀࣺ + +```` +@Configuration +@PropertySource("classpath:/annotations.properties") +@PropertySource("classpath:/vehicle-factory.properties") +class VehicleFactoryConfig {} +```` + +### 3.5 @PropertySources +Ó÷¨Í¬ÉÏ£¬Ö»²»¹ý£¬ÕâÒ»´ÎÎÒÃÇ¿ÉÒÔʹÓÃÕâ¸ö×¢½âÖ¸¶¨¶à¸ö@PropertySource ÅäÖ㺠+```` +@Configuration +@PropertySources({ +@PropertySource("classpath:/annotations.properties"), +@PropertySource("classpath:/vehicle-factory.properties") +}) +class VehicleFactoryConfig {} +```` +Çë×¢Ò⣬×Ô Java 8 ÒÔÀ´£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÉÏÊöÖØ¸´×¢Ê͹¦ÄÜʵÏÖÏàͬµÄ¹¦ÄÜ¡£ + +## 4.½áÂÛ + +ÔÚ±¾ÎÄÖУ¬ÎÒÃÇ¿´µ½ÁË×î³£¼ûµÄ Spring ºËÐÄ×¢Ê͵ĸÅÊö¡£ ÎÒÃÇ¿´µ½ÁËÈçºÎÅäÖà bean Á¬½ÓºÍÓ¦ÓÃÉÏÏÂÎÄ£¬ÒÔ¼°ÈçºÎΪ×é¼þɨÃè±ê¼ÇÀà¡£ + +springÌåϵÖеij£¼û×¢½â»¹Óкܶ࣬һƪÎÄÕ²»¿ÉÄÜÈ«²¿¸²¸Ç£¬ÈçÓÐÒÅ©£¬»¶Ó­²¹³ä¡£ + +# Spring Bean×¢½â + +## 1 ¸ÅÊö +ÔÚ±¾½Ì³ÌÖУ¬ÎÒÃǽ«ÌÖÂÛÓÃÓÚ¶¨Ò岻ͬÀàÐÍ bean µÄ×î³£¼ûµÄ Spring bean ×¢ÊÍ¡£ + +Óм¸ÖÖ·½·¨¿ÉÒÔÔÚ Spring ÈÝÆ÷ÖÐÅäÖà bean¡£ Ê×ÏÈ£¬ÎÒÃÇ¿ÉÒÔʹÓà XML ÅäÖÃÉùÃ÷ËüÃÇ¡£ ÎÒÃÇ»¹¿ÉÒÔÔÚÅäÖÃÀàÖÐʹÓÃ@Bean ×¢½âÀ´ÉùÃ÷ bean¡£ + +×îºó£¬ÎÒÃÇ¿ÉÒÔʹÓà org.springframework.stereotype °üÖеÄ×¢ÊÍÖ®Ò»À´±ê¼Ç¸ÃÀ࣬²¢½«ÆäÓಿ·ÖÁô¸ø×é¼þɨÃè¡£ + +## 2 @ComponentScan +ÕâÊÇÎÒÃǾ­³£»áʹÓõÄÒ»¸ö×¢½â£¬ÔÚÎÒÃǵÄÓ¦ÓÃÖУ¬ÓÐʱºò²»Ò»¶¨»áɨÃèËùÓеİü£¬ÌرðÊǵ±ÎÒÃÇҪɨÃèÍⲿjar°üÖеÄbeanʱ£¬Ëü·Ç³£ÓÐÓᣠ+ +Ëü¿ÉÒÔ¼ÓÔÚSpringBootApplicationÉÏ£¬Ò²¿ÉÒÔ¼ÓÔÚ@configuration×¢½âÉϵÄÅäÖÃÀàÉÏ + +Èç¹ûÆôÓÃÁË×é¼þɨÃ裬Spring ¿ÉÒÔ×Ô¶¯É¨Ãè°üÖÐµÄ bean¡£ + +@ComponentScan ÅäÖÃʹÓÃ×¢½âÅäÖÃɨÃèÄÄЩ°üµÄÀà¡£ + +ÎÒÃÇ¿ÉÒÔÖ±½ÓʹÓà basePackages »ò value ²ÎÊýÖ®Ò»Ö¸¶¨»ù±¾°üÃû³Æ£¨value ÊÇ basePackages µÄ±ðÃû£© + +```` +@Configuration +@ComponentScan(basePackages = "com.baeldung.annotations") +class VehicleFactoryConfig {} +```` +´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓà basePackageClasses ²ÎÊýÖ¸Ïò»ù´¡°üÖеÄÀࣺ + +```` +@Configuration +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +class VehicleFactoryConfig {} +```` + +ÕâÁ½¸ö²ÎÊý¶¼ÊÇÊý×飬Òò´ËÎÒÃÇ¿ÉÒÔΪÿ¸ö²ÎÊýÌṩ¶à¸ö°ü¡£ + +Èç¹ûδָ¶¨²ÎÊý£¬ÔòɨÃè·¢ÉúÔÚ´æÔÚ @ComponentScan ×¢ÊÍÀàµÄͬһ°üÖС£ + +@ComponentScan ÀûÓÃÁË Java 8 µÄÖØ¸´×¢½âÌØÐÔ£¬ÕâÒâζ×ÅÎÒÃÇ¿ÉÒÔÓÃËü¶à´Î±ê¼ÇÒ»¸öÀࣺ + +```` +@Configuration +@ComponentScan(basePackages = "com.baeldung.annotations") +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +class VehicleFactoryConfig {} +```` + +»òÕߣ¬ÎÒÃÇ¿ÉÒÔʹÓà @ComponentScans Ö¸¶¨¶à¸ö @ComponentScan ÅäÖ㺠+ +```` +@Configuration +@ComponentScans({ +@ComponentScan(basePackages = "com.baeldung.annotations"), +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +}) +```` +```` +class VehicleFactoryConfig { +} +```` +ʹÓà XML ÅäÖÃʱ£¬ÅäÖÃ×é¼þɨÃèͬÑù¼òµ¥£º + +```` + +```` + +### 3 @Component + +@Component ÊÇÀ༶±ðµÄ×¢½â¡£ ÔÚ×é¼þɨÃèÆÚ¼ä£¬Spring Framework »á×Ô¶¯¼ì²âʹÓÃ@Component ×¢½âµÄÀࣺ +```` +@Component +class CarUtility { +// ... +} +```` + +ĬÈÏÇé¿öÏ£¬´ËÀàµÄ bean ʵÀýÓëÀàÃûͬÃû£¬Ê××ÖĸСд¡£ ´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵ĿÉѡֵ²ÎÊýÖ¸¶¨²»Í¬µÄÃû³Æ¡£ + +ÓÉÓÚ@Repository¡¢@Service¡¢@Configuration ºÍ@Controller ¶¼ÊÇ´øÓÐ@Component µÄ×¢½â£¬ËüÃǹ²ÏíÏàͬµÄbean ÃüÃûÐÐΪ¡£ + +Spring »¹»áÔÚ×é¼þɨÃè¹ý³ÌÖÐ×Ô¶¯¼ì²âËüÃÇ¡£ + +ͨ³£À´Ëµ£¬ÎÒÃÇ»áÔÚmvcÓ¦ÓÃÖÐÎÒÃÇ»áÓõ½ÉÏÊö¼¸ÖÖ×¢½â£¬¶øÎÒÃÇÔÚ·ÇwebÓ¦ÓÃÖиü¶àµØ¿ÉÒÔʹÓÃ@componentÀ´×¢½âbean + +### 4 @Repository + +DAO or Repository classes usually represent the database access layer in an application, and should be annotated with @Repository: +```` +@Repository +class VehicleRepository { +// ... +} +```` +ʹÓôË×¢Ê͵ÄÒ»¸öÓŵãÊÇËüÆôÓÃÁË×Ô¶¯³Ö¾ÃÐÔÒ쳣ת»»¡£ µ±Ê¹Óó־ÃÐÔ¿ò¼Ü£¨Èç Hibernate£©Ê±£¬ÔÚʹÓà @Repository ×¢Ê͵ÄÀàÖÐÅ׳öµÄ±¾»úÒì³£½«×Ô¶¯×ª»»Îª Spring µÄ DataAccessExeption µÄ×ÓÀà¡£ + +ÒªÆôÓÃÒ쳣ת»»£¬ÎÒÃÇÐèÒªÉùÃ÷ÎÒÃÇ×Ô¼ºµÄ PersistenceExceptionTranslationPostProcessor bean£º +```` +@Bean +public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { +return new PersistenceExceptionTranslationPostProcessor(); +} +```` +Çë×¢Ò⣬ÔÚ´ó¶àÊýÇé¿öÏ£¬Spring »á×Ô¶¯Ö´ÐÐÉÏÊö²½Öè¡£ + +»òÕßͨ¹ý XML ÅäÖ㺠+```` + +```` + +### 5 @Service +Ó¦ÓóÌÐòµÄÒµÎñÂß¼­Í¨³£×¤ÁôÔÚ·þÎñ²ãÖУ¬Òò´ËÎÒÃǽ«Ê¹ÓÃ@Service ×¢ÊÍÀ´Ö¸Ê¾Ò»¸öÀàÊôÓڸò㣺 + +```` +@Service +public class VehicleService { +// ... +} +```` +### 6 @Controller +@Controller ÊÇÒ»¸öÀ༶±ðµÄ×¢½â£¬Ëü¸æËß Spring Framework Õâ¸öÀà×÷Ϊ Spring MVC ÖеĿØÖÆÆ÷£º + +spring»á¶Ô@Controller ×¢½âµÄbean×öºÜ¶àÊÂÇ飬¾ßÌåÄÚÈÝÎÒÃÇ»áÔÚSpringMVCÏà¹ØµÄÄÚÈÝÀ´½²Êö + +```` +@Controller +public class VehicleController { +// ... +} + +```` +## 7 @Configuration + +ÅäÖÃÀà¿ÉÒÔ°üº¬ÓÃ@Bean ×¢Ê굀 bean ¶¨Òå·½·¨£º +```` +@Configuration +class VehicleFactoryConfig { + + @Bean + Engine engine() { + return new Engine(); + } + +} +```` +## 8 AOP×¢½â +µ±ÎÒÃÇʹÓà Spring ¹¹ÔìÐÍ×¢ÊÍʱ£¬ºÜÈÝÒ×´´½¨Ò»¸öÇÐÈëµã£¬¸ÃÇÐÈëµãÒÔËùÓоßÓÐÌØ¶¨¹¹ÔìÐ͵ÄÀàΪĿ±ê¡£ + +ÀýÈ磬¼ÙÉèÎÒÃÇÏë²âÁ¿ DAO ²ã·½·¨µÄÖ´ÐÐʱ¼ä¡£ ÎÒÃǽ«´´½¨ÒÔÏ·½Ã棨ʹÓà AspectJ ×¢ÊÍ£©£¬ÀûÓà @Repository ¹¹ÔìÐÍ£º + +``` +@Aspect +@Component +public class PerformanceAspect { +@Pointcut("within(@org.springframework.stereotype.Repository *)") +public void repositoryClassMethods() {}; + + @Around("repositoryClassMethods()") + public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) + throws Throwable { + long start = System.nanoTime(); + Object returnValue = joinPoint.proceed(); + long end = System.nanoTime(); + String methodName = joinPoint.getSignature().getName(); + System.out.println( + "Execution of " + methodName + " took " + + TimeUnit.NANOSECONDS.toMillis(end - start) + " ms"); + return returnValue; + } +} +```` + +ÔÚ´ËʾÀýÖУ¬ÎÒÃÇ´´½¨ÁËÒ»¸öÇÐÈëµã£¬¸ÃÇÐÈëµãÆ¥ÅäʹÓÃ@Repository ×¢Ê͵ÄÀàÖеÄËùÓз½·¨¡£ È»ºóÎÒÃÇʹÓÃ@Around ֪ͨÀ´¶¨Î»ÄǸöÇÐÈëµã£¬²¢È·¶¨À¹½Ø·½·¨µ÷ÓõÄÖ´ÐÐʱ¼ä¡£ + +´ËÍ⣬ʹÓÃÕâÖÖ·½·¨£¬ÎÒÃÇ¿ÉÒÔΪÿ¸öÓ¦ÓóÌÐò²ãÌí¼ÓÈÕÖ¾¼Ç¼¡¢ÐÔÄܹÜÀí¡¢Éó¼ÆºÍÆäËûÐÐΪ¡£ + +µ±È»ÁË£¬aspectJµÄÏà¹Ø×¢½â»¹ºÜ¶à£¬¿ÉÒÔÓÃÀ´ÉùÃ÷ÇÐÃæ£¬Î´À´ÎÒÃÇÒ²»áµ¥¶ÀдÎĽéÉÜ¡£ + +## 9 ½áÂÛ + +ÔÚ±¾ÎÄÖУ¬ÎÒÃǼì²éÁË Spring ¹¹ÔìÐÍ×¢ÊͲ¢ÌÖÂÛÁËËüÃǸ÷×Ô´ú±íµÄÓïÒåÀàÐÍ¡£ + +ÎÒÃÇ»¹Ñ§Ï°ÁËÈçºÎʹÓÃ×é¼þɨÃèÀ´¸æËßÈÝÆ÷ÔÚÄÄÀï¿ÉÒÔÕÒµ½´ø×¢Ê͵ÄÀà¡£ + +×îºó£¬ÎÒÃÇÁ˽âÁËÕâЩעÊÍÈçºÎµ¼Ö¸ɾ»¡¢·Ö²ãµÄÉè¼ÆÒÔ¼°Ó¦ÓóÌÐò¹Ø×¢µãÖ®¼äµÄ·ÖÀë¡£ ËüÃÇ»¹Ê¹ÅäÖøüС£¬ÒòΪÎÒÃDz»ÔÙÐèÒªÊÖ¶¯ÏÔʽ¶¨Òå bean¡£ + +# ²Î¿¼ÎÄÕ +https://www.baeldung.com/spring-annotations + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\346\246\202\350\277\260.md" new file mode 100644 index 0000000..e9a041f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/Spring\346\246\202\350\277\260.md" @@ -0,0 +1,145 @@ +Spring ÊÇ Java EE ±à³ÌÁìÓòµÄÒ»¿îÇáÁ¿¼¶µÄ¿ªÔ´¿ò¼Ü£¬Óɱ»³ÆÎª¡°Spring Ö®¸¸¡±µÄ Rod Johnson ÓÚ 2002 ÄêÌá³ö²¢´´Á¢£¬ËüµÄÄ¿±ê¾ÍÊÇÒª¼ò»¯ Java ÆóÒµ¼¶Ó¦ÓóÌÐòµÄ¿ª·¢ÄѶȺÍÖÜÆÚ¡£ + +Spring ×Ôµ®ÉúÒÔÀ´±¸ÊÜÇàíù£¬Ò»Ö±±»¹ã´ó¿ª·¢ÈËÔ±×÷Ϊ Java ÆóÒµ¼¶Ó¦ÓóÌÐò¿ª·¢µÄÊ×Ñ¡¡£Ê±ÖÁ½ñÈÕ£¬Spring ٲȻ³ÉΪÁË Java EE ´úÃû´Ê£¬³ÉΪÁ˹¹½¨ Java EE Ó¦ÓõÄÊÂʵ±ê×¼¡£ + +## Spring µÄµ®ÉúÓë·¢Õ¹ + +ÔçÆÚµÄ J2EE£¨Java EE ƽ̨£©ÍƳçÒÔ EJB ΪºËÐĵĿª·¢·½Ê½£¬µ«ÕâÖÖ¿ª·¢·½Ê½ÔÚʵ¼ÊµÄ¿ª·¢¹ý³ÌÖдæÔÚÖÖÖÖ±×¶Ë£¬ÀýÈçʹÓø´ÔÓ¡¢´úÂëÓ·Öס¢´úÂëÇÖÈëÐÔÇ¿¡¢¿ª·¢ÖÜÆÚ³¤¡¢ÒÆÖ²ÄѶȴóµÈ¡£ + +Rod Johnson ÔÚÆä 2004 Äê±àÖøµÄ³©ÏúÊé¡¶Expert One-on-One J2EE Development without EJB¡·ÖУ¬Õë¶Ô EJB ¸÷ÖÖÓ·Ö׵Ľṹ½øÐÐÁËÖðÒ»µÄ·ÖÎöºÍ·ñ¶¨£¬²¢·Ö±ðÒÔ¸ü¼Ó¼ò½àµÄ·½Ê½½øÐÐÁËÌæ»»¡£ + +ÔÚÕâ±¾ÊéÖУ¬Rod Johnson ͨ¹ýÒ»¸ö°üº¬ 3 ÍòÐдúÂëµÄ¸½¼þ£¬Õ¹Ê¾ÁËÈçºÎÔÚ²»Ê¹Óà EJB µÄÇé¿öϹ¹½¨Ò»¸ö¸ßÖÊÁ¿¡¢¿ÉÀ©Õ¹µÄ Java Ó¦ÓóÌÐò¡£ÔÚÕâ¸ö¸½¼þÖУ¬Rod Johnson ±àдÁËÉÏÍòÐлù´¡½á¹¹´úÂ룬ÆäÖаüº¬ÁËÐí¶à¿ÉÖØÓÃµÄ Java ½Ó¿ÚºÍÀ࣬ÀýÈç ApplicationContext¡¢BeanFactory µÈ¡£ÕâЩÀàµÄ¸ù°ü±»ÃüÃûΪ com.interface21£¬º¬ÒåΪ£ºÕâÊÇÌṩ¸ø 21 ÊÀ¼ÍµÄÒ»¸ö²Î¿¼¡£ + +Õâ±¾ÊéÓ°ÏìÉõÔ¶£¬ºóÀ´ Rod Johnson ½« com.interface21 µÄ´úÂ뿪Դ£¬²¢°ÑÕâ¸öпò¼Ü²¢ÃüÃûΪ¡°Spring¡±£¬º¬ÒåΪ£ºSpring ÏñÒ»ÂÆ´º·çÒ»Ñù£¬É¨Æ½´«Í³ J2EE µÄº®¶¬¡£ + +2003 Äê 2 Ô£¬Spring 0.9 °æ±¾·¢²¼£¬Ëü²ÉÓÃÁË Apache 2.0 ¿ªÔ´Ð­Ò飻2004 Äê 4 Ô£¬Spring 1.0 °æ±¾Õýʽ·¢²¼¡£µ½Ä¿Ç°ÎªÖ¹£¬Spring ÒѾ­²½Èëµ½ÁËµÚ 5 ¸ö´ó°æ±¾£¬Ò²¾ÍÊÇÎÒÃdz£ËµµÄ Spring 5¡£ + +## Spring µÄÏÁÒåºÍ¹ãÒå + +ÔÚ²»Í¬µÄÓï¾³ÖУ¬Spring Ëù´ú±íµÄº¬ÒåÊDz»Í¬µÄ¡£ÏÂÃæÎÒÃǾͷֱð´Ó¡°¹ãÒ塱ºÍ¡°ÏÁÒ塱Á½¸ö½Ç¶È£¬¶Ô Spring ½øÐнéÉÜ¡£ + +### ¹ãÒåµÄ Spring£ºSpring ¼¼ÊõÕ» + +¹ãÒåÉ쵀 Spring ·ºÖ¸ÒÔ Spring Framework ΪºËÐÄµÄ Spring ¼¼ÊõÕ»¡£ + +¾­¹ýÊ®¶àÄêµÄ·¢Õ¹£¬Spring ÒѾ­²»ÔÙÊÇÒ»¸öµ¥´¿µÄÓ¦Óÿò¼Ü£¬¶øÊÇÖð½¥·¢Õ¹³ÉΪһ¸öÓɶà¸ö²»Í¬×ÓÏîÄ¿£¨Ä£¿é£©×é³ÉµÄ³ÉÊì¼¼Êõ£¬ÀýÈç Spring Framework¡¢Spring MVC¡¢SpringBoot¡¢Spring Cloud¡¢Spring Data¡¢Spring Security µÈ£¬ÆäÖÐ Spring Framework ÊÇÆäËû×ÓÏîÄ¿µÄ»ù´¡¡£ + +ÕâЩ×ÓÏîÄ¿º­¸ÇÁË´ÓÆóÒµ¼¶Ó¦Óÿª·¢µ½ÔƼÆËãµÈ¸÷·½ÃæµÄÄÚÈÝ£¬Äܹ»°ïÖú¿ª·¢ÈËÔ±½â¾öÈí¼þ·¢Õ¹¹ý³ÌÖв»¶Ï²úÉúµÄ¸÷ÖÖʵ¼ÊÎÊÌ⣬¸ø¿ª·¢ÈËÔ±´øÀ´Á˸üºÃµÄ¿ª·¢ÌåÑé¡£ + +| ÏîÄ¿Ãû³Æ | ÃèÊö | +| --- | --- | +| Spring Data | Spring ÌṩµÄÊý¾Ý·ÃÎÊÄ£¿é£¬¶Ô JDBC ºÍ ORM ÌṩÁ˺ܺõÄÖ§³Ö¡£Í¨¹ýËü£¬¿ª·¢ÈËÔ±¿ÉÒÔʹÓÃÒ»ÖÖÏà¶ÔͳһµÄ·½Ê½£¬À´·ÃÎÊλÓÚ²»Í¬ÀàÐÍÊý¾Ý¿âÖеÄÊý¾Ý¡£ | +| Spring Batch | Ò»¿îרÃÅÕë¶ÔÆóÒµ¼¶ÏµÍ³ÖеÄÈÕ³£Åú´¦ÀíÈÎÎñµÄÇáÁ¿¼¶¿ò¼Ü£¬Äܹ»°ïÖú¿ª·¢ÈËÔ±·½±ãµÄ¿ª·¢³ö½¡×³¡¢¸ßЧµÄÅú´¦ÀíÓ¦ÓóÌÐò¡£ | +| Spring Security | ǰÉíΪ Acegi£¬ÊÇ Spring ÖнϳÉÊìµÄ×ÓÄ£¿éÖ®Ò»¡£ËüÊÇÒ»¿î¿ÉÒÔ¶¨ÖÆ»¯µÄÉí·ÝÑéÖ¤ºÍ·ÃÎÊ¿ØÖÆ¿ò¼Ü¡£ | +| Spring Mobile | ÊÇ¶Ô Spring MVC µÄÀ©Õ¹£¬ÓÃÀ´¼ò»¯Òƶ¯¶Ë Web Ó¦ÓõĿª·¢¡£ | +| Spring Boot | ÊÇ Spring ÍŶÓÌṩµÄȫпò¼Ü£¬ËüΪ Spring ÒÔ¼°µÚÈý·½¿âһЩ¿ªÏä¼´ÓõÄÅäÖ㬿ÉÒÔ¼ò»¯ Spring Ó¦ÓõĴ¼°¿ª·¢¹ý³Ì¡£ | +| Spring Cloud | Ò»¿î»ùÓÚ Spring Boot ʵÏÖµÄ΢·þÎñ¿ò¼Ü¡£Ëü²¢²»ÊÇijһÃż¼Êõ£¬¶øÊÇһϵÁÐ΢·þÎñ½â¾ö·½°¸»ò¿ò¼ÜµÄÓÐÐò¼¯ºÏ¡£Ëü½«ÊÐÃæÉϳÉÊìµÄ¡¢¾­¹ýÑéÖ¤µÄ΢·þÎñ¿ò¼ÜÕûºÏÆðÀ´£¬²¢Í¨¹ý Spring Boot µÄ˼Ïë½øÐÐÔÙ·â×°£¬ÆÁ±Îµ÷ÆäÖи´ÔÓµÄÅäÖúÍʵÏÖÔ­Àí£¬×îÖÕΪ¿ª·¢ÈËÔ±ÌṩÁËÒ»Ì×¼òµ¥Ò×¶®¡¢Òײ¿ÊðºÍÒ×ά»¤µÄ·Ö²¼Ê½ÏµÍ³¿ª·¢¹¤¾ß°ü¡£ | + +### ÏÁÒåµÄ Spring£ºSpring Framework + +ÏÁÒåµÄ Spring ÌØÖ¸ Spring Framework£¬Í¨³£ÎÒÃǽ«Ëü³ÆÎª Spring ¿ò¼Ü¡£ + +Spring ¿ò¼ÜÊÇÒ»¸ö·Ö²ãµÄ¡¢ÃæÏòÇÐÃæµÄ Java Ó¦ÓóÌÐòµÄһվʽÇáÁ¿¼¶½â¾ö·½°¸£¬ËüÊÇ Spring ¼¼ÊõÕ»µÄºËÐĺͻù´¡£¬ÊÇΪÁ˽â¾öÆóÒµ¼¶Ó¦Óÿª·¢µÄ¸´ÔÓÐÔ¶ø´´½¨µÄ¡£ + +Spring ÓÐÁ½¸öºËÐIJ¿·Ö£º IoC ºÍ AOP¡£ + +| ºËÐÄ | ÃèÊö | +| --- | --- | +| IOC | Inverse of Control µÄ¼òд£¬ÒëΪ¡°¿ØÖÆ·´×ª¡±£¬Ö¸°Ñ´´½¨¶ÔÏó¹ý³Ì½»¸ø Spring ½øÐйÜÀí¡£ | +| AOP | Aspect Oriented Programming µÄ¼òд£¬ÒëΪ¡°ÃæÏòÇÐÃæ±à³Ì¡±¡£AOP ÓÃÀ´·â×°¶à¸öÀàµÄ¹«¹²ÐÐΪ£¬½«ÄÇЩÓëÒµÎñÎ޹أ¬È´ÎªÒµÎñÄ£¿éËù¹²Í¬µ÷ÓõÄÂß¼­·â×°ÆðÀ´£¬¼õÉÙϵͳµÄÖØ¸´´úÂ룬½µµÍÄ£¿é¼äµÄñîºÏ¶È¡£ÁíÍ⣬AOP »¹½â¾öһЩϵͳ²ãÃæÉϵÄÎÊÌ⣬±ÈÈçÈÕÖ¾¡¢ÊÂÎñ¡¢È¨Ï޵ȡ£ | + +Spring ÊÇÒ»ÖÖ»ùÓÚ Bean µÄ±à³Ì¼¼Êõ£¬ËüÉî¿ÌµØ¸Ä±ä×Å Java ¿ª·¢ÊÀ½ç¡£Spring ʹÓüòµ¥¡¢»ù±¾µÄ Java Bean À´Íê³ÉÒÔǰֻÓÐ EJB ²ÅÄÜÍê³ÉµÄ¹¤×÷£¬Ê¹µÃºÜ¶à¸´ÔӵĴúÂë±äµÃÓÅÑźͼò½à£¬±ÜÃâÁË EJB Ó·Öס¢µÍЧµÄ¿ª·¢Ä£Ê½£¬¼«´óµÄ·½±ãÏîÄ¿µÄºóÆÚά»¤¡¢Éý¼¶ºÍÀ©Õ¹¡£ + +ÔÚʵ¼Ê¿ª·¢ÖУ¬·þÎñÆ÷¶ËÓ¦ÓóÌÐòͨ³£²ÉÓÃÈý²ãÌåϵ¼Ü¹¹£¬·Ö±ðΪ±íÏֲ㣨web£©¡¢ÒµÎñÂß¼­²ã£¨service£©¡¢³Ö¾Ã²ã£¨dao£©¡£ + +Spring ÖÂÁ¦ÓÚ Java EE Ó¦Óø÷²ãµÄ½â¾ö·½°¸£¬¶Ôÿһ²ã¶¼ÌṩÁ˼¼ÊõÖ§³Ö¡£ + +* ÔÚ±íÏÖ²ãÌṩÁË¶Ô Spring MVC¡¢Struts2 µÈ¿ò¼ÜµÄÕûºÏ£» +* ÔÚÒµÎñÂß¼­²ãÌṩÁ˹ÜÀíÊÂÎñºÍ¼Ç¼ÈÕÖ¾µÄ¹¦ÄÜ£» +* Ôڳ־ò㻹¿ÉÒÔÕûºÏ MyBatis¡¢Hibernate ºÍ JdbcTemplate µÈ¼¼Êõ£¬¶ÔÊý¾Ý¿â½øÐзÃÎÊ¡£ + +Õâ³ä·ÖµØÌåÏÖÁË Spring ÊÇÒ»¸öÈ«ÃæµÄ½â¾ö·½°¸£¬¶ÔÓÚÄÇЩÒѾ­ÓнϺýâ¾ö·½°¸µÄÁìÓò£¬Spring ¾ø²»×öÖØ¸´µÄÊÂÇé¡£ + +´ÓÉè¼ÆÉÏ¿´£¬Spring ¿ò¼Ü¸øÓèÁË Java ³ÌÐòÔ±¸ü¸ßµÄ×ÔÓɶȣ¬¶ÔÒµ½çµÄ³£¼ûÎÊÌâÒ²ÌṩÁËÁ¼ºÃµÄ½â¾ö·½°¸£¬Òò´ËÔÚ¿ªÔ´ÉçÇøÊܵ½Á˹㷺µÄ»¶Ó­£¬²¢ÇÒ±»´ó²¿·Ö¹«Ë¾×÷Ϊ Java ÏîÄ¿¿ª·¢µÄÊ×Ñ¡¿ò¼Ü¡£ + +## Spring Framework µÄÌØµã + +Spring ¿ò¼Ü¾ßÓÐÒÔϼ¸¸öÌØµã¡£ + +### **·½±ã½âñ¼ò»¯¿ª·¢** + +Spring ¾ÍÊÇÒ»¸ö´ó¹¤³§£¬¿ÉÒÔ½«ËùÓжÔÏóµÄ´´½¨ºÍÒÀÀµ¹ØÏµµÄά»¤½»¸ø Spring ¹ÜÀí¡£ + +### **·½±ã¼¯³É¸÷ÖÖÓÅÐã¿ò¼Ü** + +Spring ²»Åųâ¸÷ÖÖÓÅÐãµÄ¿ªÔ´¿ò¼Ü£¬ÆäÄÚ²¿ÌṩÁ˶Ը÷ÖÖÓÅÐã¿ò¼Ü£¨Èç Struts2¡¢Hibernate¡¢MyBatis µÈ£©µÄÖ±½ÓÖ§³Ö¡£ + +### **½µµÍ Java EE API µÄʹÓÃÄѶÈ** + +Spring ¶Ô Java EE ¿ª·¢Öзdz£ÄÑÓõÄһЩ API£¨JDBC¡¢JavaMail¡¢Ô¶³Ìµ÷Óõȣ©¶¼ÌṩÁË·â×°£¬Ê¹ÕâЩ API Ó¦ÓõÄÄѶȴó´ó½µµÍ¡£ + +### **·½±ã³ÌÐòµÄ²âÊÔ** + +Spring Ö§³Ö JUnit4£¬¿ÉÒÔͨ¹ý×¢½â·½±ãµØ²âÊÔ Spring ³ÌÐò¡£ + +### **AOP ±à³ÌµÄÖ§³Ö** + +Spring Ìá¹©ÃæÏòÇÐÃæ±à³Ì£¬¿ÉÒÔ·½±ãµØÊµÏÖ¶Ô³ÌÐò½øÐÐȨÏÞÀ¹½ØºÍÔËÐÐ¼à¿ØµÈ¹¦ÄÜ¡£ + +### **ÉùÃ÷ʽÊÂÎñµÄÖ§³Ö** + +Ö»ÐèҪͨ¹ýÅäÖþͿÉÒÔÍê³É¶ÔÊÂÎñµÄ¹ÜÀí£¬¶øÎÞÐëÊÖ¶¯±à³Ì¡£ + +Spring ¿ò¼Ü»ù±¾º­¸ÇÁËÆóÒµ¼¶Ó¦Óÿª·¢µÄ¸÷¸ö·½Ã棬Ëü°üº¬ÁË 20 ¶à¸ö²»Í¬µÄÄ£¿é¡£ + + spring-aop spring-context-indexer spring-instrument spring-orm spring-web +spring-aspects spring-context-support spring-jcl spring-oxm spring-webflux +spring-beans spring-core spring-jdbc spring-r2dbc spring-webmvc +spring-context spring-expression spring-jms spring-test spring-websocket +spring-messaging spring-tx + +![SpringÌåϵ½á¹¹Í¼](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/163550G63-0.png) +ͼ1£ºSpring¼Ü¹¹Í¼ + +ÉÏͼÖаüº¬ÁË Spring ¿ò¼ÜµÄËùÓÐÄ£¿é£¬ÕâЩģ¿é¿ÉÒÔÂú×ãÒ»ÇÐÆóÒµ¼¶Ó¦Óÿª·¢µÄÐèÇó£¬ÔÚ¿ª·¢¹ý³ÌÖпÉÒÔ¸ù¾ÝÐèÇóÓÐÑ¡ÔñÐÔµØÊ¹ÓÃËùÐèÒªµÄÄ£¿é¡£ÏÂÃæ·Ö±ð¶ÔÕâЩģ¿éµÄ×÷ÓýøÐмòµ¥½éÉÜ¡£ + +## 1\. Data Access/Integration£¨Êý¾Ý·ÃÎÊ£¯¼¯³É£© + +Êý¾Ý·ÃÎÊ£¯¼¯³É²ã°üÀ¨ JDBC¡¢ORM¡¢OXM¡¢JMS ºÍ Transactions Ä£¿é£¬¾ßÌå½éÉÜÈçÏ¡£ + +* JDBC Ä£¿é£ºÌṩÁËÒ»¸ö JBDC µÄÑùÀýÄ£°å£¬Ê¹ÓÃÕâЩģ°åÄÜÏû³ý´«Í³Èß³¤µÄ JDBC ±àÂ뻹ÓбØÐëµÄÊÂÎñ¿ØÖÆ£¬¶øÇÒÄÜÏíÊܵ½ Spring ¹ÜÀíÊÂÎñµÄºÃ´¦¡£ +* ORM Ä£¿é£ºÌṩÓëÁ÷Ðеġ°¶ÔÏó-¹ØÏµ¡±Ó³Éä¿ò¼ÜÎ޷켯³ÉµÄ API£¬°üÀ¨ JPA¡¢JDO¡¢Hibernate ºÍ MyBatis µÈ¡£¶øÇÒ»¹¿ÉÒÔʹÓà Spring ÊÂÎñ¹ÜÀí£¬ÎÞÐè¶îÍâ¿ØÖÆÊÂÎñ¡£ +* OXM Ä£¿é£ºÌṩÁËÒ»¸öÖ§³Ö Object /XML Ó³ÉäµÄ³éÏó²ãʵÏÖ£¬Èç JAXB¡¢Castor¡¢XMLBeans¡¢JiBX ºÍ XStream¡£½« Java ¶ÔÏóÓ³Éä³É XML Êý¾Ý£¬»òÕß½«XML Êý¾ÝÓ³Éä³É Java ¶ÔÏó¡£ +* JMS Ä£¿é£ºÖ¸ Java ÏûÏ¢·þÎñ£¬ÌṩһÌ× ¡°ÏûÏ¢Éú²úÕß¡¢ÏûÏ¢Ïû·ÑÕß¡±Ä£°åÓÃÓÚ¸ü¼Ó¼òµ¥µÄʹÓà JMS£¬JMS ÓÃÓÚÓÃÓÚÔÚÁ½¸öÓ¦ÓóÌÐòÖ®¼ä£¬»ò·Ö²¼Ê½ÏµÍ³Öз¢ËÍÏûÏ¢£¬½øÐÐÒ첽ͨÐÅ¡£ +* Transactions ÊÂÎñÄ£¿é£ºÖ§³Ö±à³ÌºÍÉùÃ÷ʽÊÂÎñ¹ÜÀí¡£ + +## 2\. Web Ä£¿é + +Spring µÄ Web ²ã°üÀ¨ Web¡¢Servlet¡¢WebSocket ºÍ Portlet ×é¼þ£¬¾ßÌå½éÉÜÈçÏ¡£ + +* Web Ä£¿é£ºÌṩÁË»ù±¾µÄ Web ¿ª·¢¼¯³ÉÌØÐÔ£¬ÀýÈç¶àÎļþÉÏ´«¹¦ÄÜ¡¢Ê¹ÓÃµÄ Servlet ¼àÌýÆ÷µÄ IOC ÈÝÆ÷³õʼ»¯ÒÔ¼° Web Ó¦ÓÃÉÏÏÂÎÄ¡£ +* Servlet Ä£¿é£ºÌṩÁËÒ»¸ö Spring MVC Web ¿ò¼ÜʵÏÖ¡£Spring MVC ¿ò¼ÜÌṩÁË»ùÓÚ×¢½âµÄÇëÇó×ÊÔ´×¢Èë¡¢¸ü¼òµ¥µÄÊý¾Ý°ó¶¨¡¢Êý¾ÝÑéÖ¤µÈ¼°Ò»Ì׷dz£Ò×ÓÃµÄ JSP ±êÇ©£¬ÍêÈ«ÎÞ·ìÓë Spring ÆäËû¼¼ÊõЭ×÷¡£ +* WebSocket Ä£¿é£ºÌṩÁ˼òµ¥µÄ½Ó¿Ú£¬Óû§Ö»ÒªÊµÏÖÏìÓ¦µÄ½Ó¿Ú¾Í¿ÉÒÔ¿ìËٵĴ WebSocket Server£¬´Ó¶øÊµÏÖË«ÏòͨѶ¡£ +* Portlet Ä£¿é£ºÌṩÁËÔÚ Portlet »·¾³ÖÐʹÓà MVC ʵÏÖ£¬ÀàËÆ Web-Servlet Ä£¿éµÄ¹¦ÄÜ¡£ + +## 3\. Core Container£¨Spring µÄºËÐÄÈÝÆ÷£© + +Spring µÄºËÐÄÈÝÆ÷ÊÇÆäËûÄ£¿é½¨Á¢µÄ»ù´¡£¬ÓÉ Beans Ä£¿é¡¢Core ºËÐÄÄ£¿é¡¢Context ÉÏÏÂÎÄÄ£¿éºÍ SpEL ±í´ïʽÓïÑÔÄ£¿é×é³É£¬Ã»ÓÐÕâЩºËÐÄÈÝÆ÷£¬Ò²²»¿ÉÄÜÓÐ AOP¡¢Web µÈÉϲãµÄ¹¦ÄÜ¡£¾ßÌå½éÉÜÈçÏ¡£ + +* Beans Ä£¿é£ºÌṩÁË¿ò¼ÜµÄ»ù´¡²¿·Ö£¬°üÀ¨¿ØÖÆ·´×ªºÍÒÀÀµ×¢Èë¡£ +* Core ºËÐÄÄ£¿é£º·â×°ÁË Spring ¿ò¼ÜµÄµ×²ã²¿·Ö£¬°üÀ¨×ÊÔ´·ÃÎÊ¡¢ÀàÐÍת»»¼°Ò»Ð©³£Óù¤¾ßÀà¡£ +* Context ÉÏÏÂÎÄÄ£¿é£º½¨Á¢ÔÚ Core ºÍ Beans Ä£¿éµÄ»ù´¡Ö®ÉÏ£¬¼¯³É Beans Ä£¿é¹¦Äܲ¢Ìí¼Ó×ÊÔ´°ó¶¨¡¢Êý¾ÝÑéÖ¤¡¢¹ú¼Ê»¯¡¢Java EE Ö§³Ö¡¢ÈÝÆ÷ÉúÃüÖÜÆÚ¡¢Ê¼þ´«²¥µÈ¡£ApplicationContext ½Ó¿ÚÊÇÉÏÏÂÎÄÄ£¿éµÄ½¹µã¡£ +* SpEL Ä£¿é£ºÌṩÁËÇ¿´óµÄ±í´ïʽÓïÑÔÖ§³Ö£¬Ö§³Ö·ÃÎʺÍÐÞ¸ÄÊôÐÔÖµ£¬·½·¨µ÷Óã¬Ö§³Ö·ÃÎʼ°ÐÞ¸ÄÊý×é¡¢ÈÝÆ÷ºÍË÷ÒýÆ÷£¬ÃüÃû±äÁ¿£¬Ö§³ÖËãÊýºÍÂß¼­ÔËË㣬֧³Ö´Ó Spring ÈÝÆ÷»ñÈ¡ Bean£¬ËüÒ²Ö§³ÖÁбíͶӰ¡¢Ñ¡ÔñºÍÒ»°ãµÄÁбí¾ÛºÏµÈ¡£ + +## 4\. AOP¡¢Aspects¡¢Instrumentation ºÍ Messaging + +ÔÚ Core Container Ö®ÉÏÊÇ AOP¡¢Aspects µÈÄ£¿é£¬¾ßÌå½éÉÜÈçÏ£º + +* AOP Ä£¿é£ºÌṩÁËÃæÏòÇÐÃæ±à³ÌʵÏÖ£¬Ìṩ±ÈÈçÈÕÖ¾¼Ç¼¡¢È¨ÏÞ¿ØÖÆ¡¢ÐÔÄÜͳ¼ÆµÈͨÓù¦ÄܺÍÒµÎñÂß¼­·ÖÀëµÄ¼¼Êõ£¬²¢ÇÒÄܶ¯Ì¬µÄ°ÑÕâЩ¹¦ÄÜÌí¼Óµ½ÐèÒªµÄ´úÂëÖУ¬ÕâÑù¸÷˾ÆäÖ°£¬½µµÍÒµÎñÂß¼­ºÍͨÓù¦ÄܵÄñîºÏ¡£ +* Aspects Ä£¿é£ºÌṩÓë AspectJ µÄ¼¯³É£¬ÊÇÒ»¸ö¹¦ÄÜÇ¿´óÇÒ³ÉÊìµÄÃæÏòÇÐÃæ±à³Ì£¨AOP£©¿ò¼Ü¡£ +* Instrumentation Ä£¿é£ºÌṩÁËÀ๤¾ßµÄÖ§³ÖºÍÀà¼ÓÔØÆ÷µÄʵÏÖ£¬¿ÉÒÔÔÚÌØ¶¨µÄÓ¦Ó÷þÎñÆ÷ÖÐʹÓᣠ+* messaging Ä£¿é£ºSpring 4.0 ÒÔºóÐÂÔöÁËÏûÏ¢£¨Spring-messaging£©Ä£¿é£¬¸ÃÄ£¿éÌṩÁ˶ÔÏûÏ¢´«µÝÌåϵ½á¹¹ºÍЭÒéµÄÖ§³Ö¡£ + +## 5\. Test Ä£¿é + +Test Ä£¿é£ºSpring Ö§³Ö Junit ºÍ TestNG ²âÊÔ¿ò¼Ü£¬¶øÇÒ»¹¶îÍâÌṩÁËһЩ»ùÓÚ Spring µÄ²âÊÔ¹¦ÄÜ£¬±ÈÈçÔÚ²âÊÔ Web ¿ò¼Üʱ£¬Ä£Äâ Http ÇëÇóµÄ¹¦ÄÜ¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/\347\254\254\344\270\200\344\270\252Spring\345\272\224\347\224\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/\347\254\254\344\270\200\344\270\252Spring\345\272\224\347\224\250.md" new file mode 100644 index 0000000..b40a369 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring/\347\254\254\344\270\200\344\270\252Spring\345\272\224\347\224\250.md" @@ -0,0 +1,70 @@ +## Ìí¼ÓÒÀÀµ +ÕâÀïÎÒÃÇ¿ÉÒÔÏ顄 Spring »ù´¡ Jar °üÒÔ¼° Commons-loggin µ¼Èëµ½ÏîÄ¿ÖУ¬ÈôºóÐø¹¦ÄÜÔö¼Ó£¬Ôò¸ù¾ÝÐèÒªÔÙµ¼Èë Spring µÄÆäËû Jar¡£ + +```` +org.springframework.core-5.3.13.jar +org.springframework.beans-5.3.13.jar +spring-context-5.3.13.jar +spring-expression-5.3.13.jar +commons.logging-1.2.jar +```` +µ±È»ÕâÀïÎÒÃÇÍÆ¼öʹÓÃmaven¹ÜÀíÒÀÀµ + +## ´´½¨ Java Àà + ÔÚ HelloSpring Öд´½¨ net.biancheng.c °ü£¬È»ºóÔÚÕâ¸ö°üÏ´´½¨ HelloWorld.java ºÍ MainApp.java Àà¡£ + +HelloWorld.java ÀàµÄ´úÂëÈçÏ +```` +package net.biancheng.c; +public class HelloWorld { + private String message; + public void setMessage(String message) { + this.message = message; + } + public void getMessage() { + System.out.println("message : " + message); + } +} +```` +MainApp.java ÀàµÄ´úÂëÈçÏ +```` +package net.biancheng.c; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + public class MainApp { + public static void main(String[] args) { + ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); + HelloWorld obj = context.getBean("helloWorld",HelloWorld.class); + obj.getMessage(); + } + } +```` +¹ØÓÚÒÔÉÏ´úÂ룬ÐèҪעÒâÒÔÏÂÁ½µã£º + +´´½¨ ApplicationContext ¶ÔÏóʱʹÓÃÁË ClassPathXmlApplicationContext À࣬Õâ¸öÀàÓÃÓÚ¼ÓÔØ Spring ÅäÖÃÎļþ¡¢´´½¨ºÍ³õʼ»¯ËùÓжÔÏó£¨Bean£©¡£ +ApplicationContext.getBean() ·½·¨ÓÃÀ´»ñÈ¡ Bean£¬¸Ã·½·¨·µ»ØÖµÀàÐÍΪ Object£¬Í¨¹ýÇ¿ÖÆÀàÐÍת»»Îª HelloWorld µÄʵÀý¶ÔÏ󣬵÷ÓÃÆäÖÐµÄ getMessage() ·½·¨¡£ + +## ´´½¨ÅäÖÃÎļþ + +ÔÚ src Ŀ¼Ï£¬´´½¨Ò»¸ö Spring ÅäÖÃÎļþ Beans.xml£¬ÄÚÈÝÈçÏ¡£ +```` + + + + + + +```` +ÎÒÃÇÒ²¿ÉÒÔ½«¸ÃÅäÖÃÎļþÃüÃûΪÆäËüÓÐЧµÄÃû³Æ£¬µ«ÐèҪעÒâµÄÊÇ£¬¸ÃÎļþÃû±ØÐëÓë MainApp.java ÖжÁÈ¡µÄÅäÖÃÎļþÃû³ÆÒ»Ö¡£ + +Beans.xml ÓÃÓÚ¸ø²»Í¬µÄ Bean ·ÖÅäΨһµÄ ID£¬²¢¸øÏàÓ¦µÄ Bean ÊôÐÔ¸³Öµ¡£ÀýÈ磬ÔÚÒÔÉÏ´úÂëÖУ¬ÎÒÃÇ¿ÉÒÔÔÚ²»Ó°ÏìÆäËüÀàµÄÇé¿öÏ£¬¸ø message ±äÁ¿¸³Öµ¡£ +## ÔËÐгÌÐò + + ÔËÐÐ MainApp.java£¬Eclipse IDE ¿ØÖÆÌ¨ÖÐÏÔʾÐÅÏ¢ÈçÏ¡£ + message : Hello World! + +ÖÁ´Ë£¬ÎÒÃǾͳɹ¦´´½¨Á˵ÚÒ»¸ö Spring Ó¦ÓóÌÐò¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\344\273\273\345\212\241\350\260\203\345\272\246\344\270\216@Async.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\344\273\273\345\212\241\350\260\203\345\272\246\344\270\216@Async.md" new file mode 100644 index 0000000..c3bce06 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\344\273\273\345\212\241\350\260\203\345\272\246\344\270\216@Async.md" @@ -0,0 +1,240 @@ + + +
+ +# Spring Bootµ÷¶È + +ÒÔÏÂÄÚÈݽöÊÇÕ¾³¤»òÍøÓѸöÈËѧϰ±Ê¼Ç¡¢×ܽáºÍÑо¿Êղء£²»±£Ö¤ÕýÈ·ÐÔ£¬ÒòʹÓöø´øÀ´µÄ·çÏÕÓë±¾Õ¾Î޹أ¡ + +
+ + + + + + + +µ÷¶ÈÊÇÖ´ÐÐÌØ¶¨Ê±¼ä¶ÎµÄÈÎÎñµÄ¹ý³Ì¡£Spring BootΪÔÚSpringÓ¦ÓóÌÐòÉϱàдµ÷¶È³ÌÐòÌṩÁ˺ܺõÄÖ§³Ö¡£ + +## Java Cron±í´ïʽ + +Java Cron±í´ïʽÓÃÓÚÅäÖÃCronTriggerµÄʵÀý£¬ËüÊÇ`org.quartz.Trigger`µÄ×ÓÀà¡£ ÓйØJava cron±í´ïʽµÄ¸ü¶àÐÅÏ¢£¬Çë²ÎÔÄ´ËÁ´½Ó - + +* [https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.html](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.html) + +`[@EnableScheduling](https://github.com/EnableScheduling "@EnableScheduling")`×¢½âÓÃÓÚΪӦÓóÌÐòÆôÓõ÷¶È³ÌÐò¡£½«´ËÅú×¢Ìí¼Óµ½Ö÷Spring BootÓ¦ÓóÌÐòÀàÎļþÖС£ + +``` +@SpringBootApplication +@EnableScheduling + +public class DemoApplication { + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } +} + +``` + +`[@Scheduled](https://github.com/Scheduled "@Scheduled")`×¢½âÓÃÓÚÔÚÌØ¶¨Ê±¼ä¶ÎÄÚ´¥·¢µ÷¶È³ÌÐò¡£ + +``` +@Scheduled(cron = "0 * 9 * * ?") +public void cronJobSch() throws Exception { +} + +``` + +ÒÔÏÂÊÇÒ»¸öʾÀý´úÂ룬ÑÝʾÈçºÎÔÚÿÌìÉÏÎç9:00¿ªÊ¼µ½Ã¿ÌìÉÏÎç9:59½áÊøÖ´ÐÐÈÎÎñ¡£ + +``` +package com.yiibai.demo.scheduler; + +import java.text.SimpleDateFormat; +import java.util.Date; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Scheduler { + @Scheduled(cron = "0 * 9 * * ?") + public void cronJobSch() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + Date now = new Date(); + String strDate = sdf.format(now); + System.out.println("Java cron job expression:: " + strDate); + } +} + +``` + +ÒÔÏÂÆÁÄ»½ØÍ¼ÏÔʾÁËÓ¦ÓóÌÐòÈçºÎÔÚ`09:03:23`Æô¶¯£¬²¢ÇÒ´ÓÄÇʱÆðÿ¸ôÒ»·ÖÖÓÖ´ÐÐÒ»´Îcron×÷Òµµ÷¶È³ÌÐòÈÎÎñ¡£ + +![](/uploads/images/2018/10/05/103218_77311.jpg) + +## ¹Ì¶¨ËÙÂÊ + +¹Ì¶¨ËÙÂʵ÷¶È³ÌÐòÓÃÓÚÔÚÌØ¶¨Ê±¼äÖ´ÐÐÈÎÎñ¡£Ëü²»µÈ´ýǰһ¸öÈÎÎñµÄÍê³É¡£ ÖµÊÇÒÔºÁÃëΪµ¥Î»¡£ ʾÀý´úÂëÏÔʾÔÚ´Ë´¦ - + +``` +@Scheduled(fixedRate = 1000) +public void fixedRateSch() { +} + +``` + +´Ë´¦ÏÔʾÁËÓ¦ÓóÌÐòÆô¶¯Ê±Ã¿ÃëÖ´ÐÐÈÎÎñµÄʾÀý´úÂë - + +``` +package com.yiibai.demo.scheduler; + +import java.text.SimpleDateFormat; +import java.util.Date; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Scheduler { + @Scheduled(fixedRate = 1000) + public void fixedRateSch() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + Date now = new Date(); + String strDate = sdf.format(now); + System.out.println("Fixed Rate scheduler:: " + strDate); + } +} + +``` + +Çë×¢ÒâÒÔÏÂÆÁÄ»½ØÍ¼£¬ÆäÖÐÏÔʾÁËÔÚ`09:12:00`Æô¶¯µÄÓ¦ÓóÌÐò£¬Ö®ºóÿ¸ôÒ»¸ö¹Ì¶¨ËÙÂʵ÷¶È³ÌÐòÖ´ÐÐÈÎÎñ¡£ + +![](/uploads/images/2018/10/05/103355_72877.jpg) + +## ¹Ì¶¨ÑÓ³Ù + +¹Ì¶¨ÑÓ³Ùµ÷¶È³ÌÐòÓÃÓÚÔÚÌØ¶¨Ê±¼äÖ´ÐÐÈÎÎñ¡£ ËüÓ¦¸ÃµÈ´ýÉÏÒ»¸öÈÎÎñÍê³É¡£ ÖµÓ¦ÒÔºÁÃëΪµ¥Î»¡£ ´Ë´¦ÏÔʾʾÀý´úÂë - + +``` +@Scheduled(fixedDelay = 1000, initialDelay = 1000) +public void fixedDelaySch() { +} + +``` + +ÕâÀ`initialDelay`ÊÇÔÚ³õʼÑÓ³ÙÖµÖ®ºóµÚÒ»´ÎÖ´ÐÐÈÎÎñµÄʱ¼ä¡£ + +´ÓÓ¦ÓóÌÐòÆô¶¯Íê³É`3`ÃëºóÿÃëÖ´ÐÐÒ»´ÎÈÎÎñµÄʾÀýÈçÏÂËùʾ - + +``` +package com.yiibai.demo.scheduler; + +import java.text.SimpleDateFormat; +import java.util.Date; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Scheduler { + @Scheduled(fixedDelay = 1000, initialDelay = 3000) + public void fixedDelaySch() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + Date now = new Date(); + String strDate = sdf.format(now); + System.out.println("Fixed Delay scheduler:: " + strDate); + } +} + +``` + +Ö´ÐÐÉÏÃæ´úÂëºó£¬Ëü»áÏÔʾÔÚ`09:18:39`¿ªÊ¼µÄÓ¦ÓóÌÐò£¬Ã¿`3`Ãëºó£¬¹Ì¶¨Ñӳټƻ®³ÌÐòÈÎÎñ(ÿÃëÖ´ÐÐÒ»´Î)¡£ + + + + + +//¸ü¶àÇëÔĶÁ£ºhttps://www.yiibai.com/spring-boot/spring_boot_scheduling.html + +@EnableAsync ×¢½â +ҪʹÓà @Async£¬Ê×ÏÈÐèҪʹÓà @EnableAsync ×¢½â¿ªÆô Spring Boot ÖеÄÒì²½ÌØÐÔ¡£ + +@Configuration +@EnableAsync +public class AppConfig { +} +¸üÏêϸµÄÅäÖÃ˵Ã÷£¬¿ÉÒԲο¼£ºAsyncConfigurer(opens new window) + +#@Async ×¢½â +#Ö§³ÖµÄÓ÷¨ +£¨1£©ÎÞÈë²ÎÎÞ·µ»ØÖµ·½·¨ + +Äú¿ÉÒÔÓà @Async ×¢½âÐÞÊη½·¨£¬Õâ±íÃ÷Õâ¸ö·½·¨ÊÇÒì²½·½Ê½µ÷Ó᣻»¾ä»°Ëµ£¬³ÌÐòÔÚµ÷Óô˷½·¨Ê±»áÁ¢¼´·µ»Ø£¬¶ø·½·¨µÄʵ¼ÊÖ´Ðз¢ÉúÔÚÒÑÌá½»¸ø Spring TaskExecutor µÄÈÎÎñÖС£ÔÚ×î¼òµ¥µÄÇé¿öÏ£¬Äú¿ÉÒÔ½«×¢½âÓ¦ÓÃÓÚ·µ»Ø void µÄ·½·¨£¬ÈçÒÔÏÂʾÀýËùʾ£º + +@Async +void doSomething() { +// this will be executed asynchronously +} +£¨2£©ÓÐÈë²ÎÎÞ·µ»ØÖµ·½·¨ + +ÓëʹÓà @Scheduled ×¢ÊÍ×¢Ê͵ķ½·¨²»Í¬£¬ÕâЩ·½·¨¿ÉÒÔÖ¸¶¨²ÎÊý£¬ÒòΪËüÃÇÔÚÔËÐÐʱÓɵ÷ÓÃÕßÒÔ¡°Õý³£¡±·½Ê½µ÷Ó㬶ø²»ÊÇÓÉÈÝÆ÷¹ÜÀíµÄµ÷¶ÈÈÎÎñµ÷Óá£ÀýÈ磬ÒÔÏ´úÂëÊÇ @Async ×¢½âµÄºÏ·¨Ó¦Ó㺠+ +@Async +void doSomething(String s) { +// this will be executed asynchronously +} +£¨3£©ÓÐÈë²ÎÓзµ»ØÖµ·½·¨ + +ÉõÖÁ¿ÉÒÔÒì²½µ÷Ó÷µ»ØÖµµÄ·½·¨¡£µ«ÊÇ£¬ÕâЩ·½·¨ÐèÒª¾ßÓÐ Future ÀàÐ͵ķµ»ØÖµ¡£ÕâÈÔÈ»ÌṩÁËÒì²½Ö´Ðеĺô¦£¬ÒÔ±ãµ÷ÓÃÕß¿ÉÒÔÔÚµ÷Óà Future É쵀 get() ֮ǰִÐÐÆäËûÈÎÎñ¡£ÒÔÏÂʾÀýÏÔʾÈçºÎÔÚ·µ»ØÖµµÄ·½·¨ÉÏʹÓÃ@Async£º + +@Async +Future returnSomething(int i) { +// this will be executed asynchronously +} +#²»Ö§³ÖµÄÓ÷¨ +@Async ²»ÄÜÓëÉúÃüÖÜÆÚ»Øµ÷Ò»ÆðʹÓã¬ÀýÈç @PostConstruct¡£ + +ÒªÒì²½³õʼ»¯ Spring bean£¬±ØÐëʹÓõ¥¶ÀµÄ³õʼ»¯ Spring bean£¬È»ºóÔÚÄ¿±êÉϵ÷Óà @Async ´ø×¢Ê͵ķ½·¨£¬ÈçÒÔÏÂʾÀýËùʾ£º + +public class SampleBeanImpl implements SampleBean { + + @Async + void doSomething() { + // ... + } + +} + +public class SampleBeanInitializer { + + private final SampleBean bean; + + public SampleBeanInitializer(SampleBean bean) { + this.bean = bean; + } + + @PostConstruct + public void initialize() { + bean.doSomething(); + } + +} +#Ã÷È·Ö¸¶¨Ö´ÐÐÆ÷ +ĬÈÏÇé¿öÏ£¬ÔÚ·½·¨ÉÏÖ¸¶¨ @Async ʱ£¬Ê¹ÓõÄÖ´ÐÐÆ÷ÊÇÔÚÆôÓÃÒì²½Ö§³ÖʱÅäÖõÄÖ´ÐÐÆ÷£¬¼´Èç¹ûʹÓà XML »ò AsyncConfigurer ʵÏÖ£¨Èç¹ûÓУ©£¬ÔòΪ annotation-driven ÔªËØ¡£µ«ÊÇ£¬Èç¹ûÐèҪָʾÔÚÖ´Ðиø¶¨·½·¨Ê±Ó¦Ê¹ÓÃĬÈÏÖµÒÔÍâµÄÖ´ÐÐÆ÷£¬Ôò¿ÉÒÔʹÓà @Async ×¢½âµÄ value ÊôÐÔ¡£ÒÔÏÂʾÀýÏÔʾÁËÈçºÎÖ´Ðд˲Ù×÷£º + +@Async("otherExecutor") +void doSomething(String s) { +// this will be executed asynchronously by "otherExecutor" +} +ÔÚÕâÖÖÇé¿öÏ£¬¡°otherExecutor¡±¿ÉÒÔÊÇ Spring ÈÝÆ÷ÖÐÈκΠExecutor bean µÄÃû³Æ£¬Ò²¿ÉÒÔÊÇÓëÈκΠExecutor ¹ØÁªµÄÏÞ¶¨·ûµÄÃû³Æ£¨ÀýÈ磬ʹÓÃ ÔªËØ»ò Spring µÄ @Qualifier ×¢ÊÍÖ¸¶¨£© £©¡£ + +#¹ÜÀí @Async µÄÒì³£ +µ± @Async ·½·¨µÄ·µ»ØÖµÀàÐÍΪ Future ÐÍʱ£¬ºÜÈÝÒ×¹ÜÀíÔÚ·½·¨Ö´ÐÐÆÚ¼äÅ׳öµÄÒì³££¬ÒòΪÔÚµ÷Óà get ½á¹ûʱ»áÅ׳ö´ËÒì³£¡£µ«ÊÇ£¬¶ÔÓÚ·µ»ØÖµÀàÐÍΪ void Ð͵ķ½·¨£¬Òì³£²»»á±»²¶»ñÇÒÎÞ·¨´«Êä¡£Äú¿ÉÒÔÌṩ AsyncUncaughtExceptionHandler À´´¦Àí´ËÀàÒì³£¡£ÒÔÏÂʾÀýÏÔʾÁËÈçºÎÖ´Ðд˲Ù×÷£º + +public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { + + @Override + public void handleUncaughtException(Throwable ex, Method method, Object... params) { + // handle exception + } +} +ĬÈÏÇé¿öÏ£¬½ö¼Ç¼Òì³£¡£Äú¿ÉÒÔʹÓà AsyncConfigurer »ò XML ÔªËØ¶¨Òå×Ô¶¨Òå AsyncUncaughtExceptionHandler¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\346\227\245\345\277\227\347\256\241\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\346\227\245\345\277\227\347\256\241\347\220\206.md" new file mode 100644 index 0000000..009576d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\344\270\255\347\232\204\346\227\245\345\277\227\347\256\241\347\220\206.md" @@ -0,0 +1,806 @@ +## 4\. ÈÕÖ¾ + + + + + +Spring BootÔÚËùÓÐÄÚ²¿ÈÕÖ¾ÖÐʹÓà [Commons Logging](https://commons.apache.org/logging) £¬µ«¶Ôµ×²ãÈÕÖ¾µÄʵÏÖ±£³Ö¿ª·Å¡£ Ϊ [Java Util Logging](https://docs.oracle.com/javase/17/docs/api/java/util/logging/package-summary.html) ¡¢ [Log4j2](https://logging.apache.org/log4j/2.x/) ¡¢ [Logback](https://logback.qos.ch/) ÌṩÁËĬÈÏÅäÖᣠÔÚÿһÖÖÇé¿öÏ£¬¼Ç¼Æ÷£¨logger£©¶¼±»Ô¤ÉèΪʹÓÿØÖÆÌ¨Êä³ö£¬Ò²¿ÉÒÔÑ¡ÔñÊä³öµ½Îļþ¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Èç¹ûÄãʹÓà ¡°Starter¡±£¬ÔòĬÈÏʹÓÃLogback¡£ Êʵ±µÄLogback·ÓÉÒ²°üÀ¨ÔÚÄÚ£¬ÒÔÈ·±£Ê¹ÓÃJava Util Logging¡¢Commons Logging¡¢Log4J»òSLF4JµÄÒÀÀµ¿â¶¼ÄÜÕýÈ·¹¤×÷¡£ + + + + + +| | ÓкܶàÊÊÓÃÓÚJavaµÄÈÕÖ¾¿ò¼Ü¡£ Èç¹ûÉÏÃæµÄÁÐ±í¿´ÆðÀ´ºÜ»ìÂÒ£¬Çë²»Òªµ£ÐÄ¡£ Ò»°ãÀ´Ëµ£¬Äã²»ÐèÒª¸Ä±äÄãµÄÈÕÖ¾ÒÀÀµ£¬Spring BootµÄĬÈÏÖµ¾ÍºÜºÃÓᣠ| +| --- | --- | + + + + + +| | µ±Äã°ÑÄãµÄÓ¦ÓóÌÐò²¿Êðµ½Ò»¸öservletÈÝÆ÷»òÓ¦Ó÷þÎñÆ÷ʱ£¬ÓÃJava Util Logging APIÖ´ÐеÄÈÕÖ¾²»»á±»´«Ë͵½ÄãµÄÓ¦ÓóÌÐòµÄÈÕÖ¾ÖС£ Õâ¿ÉÒÔ·ÀÖ¹ÓÉÈÝÆ÷»òÆäËûÒѾ­²¿Êðµ½ËüµÄÓ¦ÓóÌÐòÖ´ÐеÄÈÕÖ¾³öÏÖÔÚÄãµÄÓ¦ÓóÌÐòµÄÈÕÖ¾ÖС£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.log-format)4.1\. ÈÕÖ¾¸ñʽ + + + +Spring BootµÄĬÈϵÄÈÕÖ¾Êä³ö¸ñʽÀàËÆÓÚÏÂÃæµÄÀý×Ó¡£ + + + + + + + + 2023-03-03T21:18:18.827+08:00 INFO 19388 --- [ main] o.s.b.d.f.s.MyApplication : Starting MyApplication using Java 17 with PID 19388 (/opt/apps/myapp.jar started by myuser in /opt/apps/) +2023-03-03T21:18:18.834+08:00 INFO 19388 --- [ main] o.s.b.d.f.s.MyApplication : No active profile set, falling back to 1 default profile: "default" +2023-03-03T21:18:20.439+08:00 INFO 19388 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2023-03-03T21:18:20.461+08:00 INFO 19388 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2023-03-03T21:18:20.461+08:00 INFO 19388 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5] +2023-03-03T21:18:20.600+08:00 INFO 19388 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2023-03-03T21:18:20.602+08:00 INFO 19388 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1685 ms +2023-03-03T21:18:21.078+08:00 INFO 19388 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2023-03-03T21:18:21.093+08:00 INFO 19388 --- [ main] o.s.b.d.f.s.MyApplication : Started MyApplication in 2.998 seconds (process running for 3.601) + + + + + + + +Êä³öµÄÏîÄ¿ÈçÏ¡£ + + + + + +* DateºÍʱTime£º¾«È·µ½ºÁÃ룬Ò×ÓÚÅÅÐò¡£ + +* ÈÕÖ¾¼¶±ð: `ERROR`, `WARN`, `INFO`, `DEBUG`, »ò `TRACE`. + +* ½ø³ÌID¡£ + +* Ò»¸ö `---` ·Ö¸ô·û£¬ÒÔÇø·Öʵ¼ÊÈÕÖ¾ÐÅÏ¢µÄ¿ªÊ¼¡£ + +* Ïß³ÌÃû³Æ£º°üº¬ÔÚ·½À¨ºÅÖУ¨¶ÔÓÚ¿ØÖÆÌ¨Êä³ö¿ÉÄܻᱻ½Ø¶Ï£©¡£ + +* ¼Ç¼Æ÷Ãû³Æ£ºÕâͨ³£ÊÇÔ´ÀàµÄÃû³Æ£¨Í¨³£ÊÇËõд£©¡£ + +* ÈÕÖ¾ÏûÏ¢¡£ + + + + + +| | LogbackûÓÐ `FATAL` ¼¶±ð¡£ Ëü±»Ó³Éäµ½ `ERROR`¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.console-output)4.2\. ¿ØÖÆÌ¨Êä³ö + + + +ĬÈÏÇé¿öÏ£¬ÈÕÖ¾»áÊä³ö `ERROR`¡¢`WARN` ºÍ `INFO` ¼¶±ðµÄÏûÏ¢µ½¿ØÖÆÌ¨¡£ ÄãÒ²¿ÉÒÔͨ¹ýÓà `--debug` ±êÖ¾Æô¶¯ÄãµÄÓ¦ÓóÌÐò£¬À´ÆôÓà `debug` ģʽ¡£ + + + + + + + +``` +$ java -jar myapp.jar --debug +``` + + + + + + + +| | ÄãÒ²¿ÉÒÔÔÚÄãµÄ `application.properties` ÖÐÖ¸¶¨ `debug=true`¡£ | +| --- | --- | + + + + + +µ±debugģʽ±»ÆôÓÃʱ£¬Ò»Ð©ºËÐļǼÆ÷£¨Ç¶ÈëʽÈÝÆ÷¡¢HibernateºÍSpring Boot£©±»ÅäÖÃΪÊä³ö¸ü¶àÐÅÏ¢¡£ ÆôÓÃdebugģʽ²¢²»Òâζ׎«ÄãµÄÓ¦ÓóÌÐòÅäÖÃΪÒÔ `DEBUG` ¼¶±ð¼Ç¼ËùÓÐÐÅÏ¢¡£ + + + + + +ÁíÍ⣬Äã¿ÉÒÔͨ¹ýÔÚÆô¶¯Ó¦ÓóÌÐòʱʹÓà `--trace` ±êÖ¾£¨»òÔÚ `application.properties` ÖÐʹÓà `trace=true` £©À´ÆôÓà ¡°trace¡± ģʽ¡£ ÕâÑù×ö¿ÉÒÔ¶ÔһЩºËÐļǼÆ÷£¨Ç¶ÈëʽÈÝÆ÷¡¢Hibernate schemaÉú³ÉºÍÕû¸öSpring×éºÏ£©½øÐиú×ټǼ¡£ + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.console-output.color-coded)4.2.1\. ²ÊÉ«±àÂëµÄÊä³ö + + + +Èç¹ûÄãµÄÖÕ¶ËÖ§³ÖANSI£¬¾Í»áʹÓòÊÉ«Êä³öÀ´°ïÖúÔĶÁ¡£ Äã¿ÉÒÔ½« `spring.output.ansi.enabled` ÉèÖÃΪ [Ö§³ÖµÄÖµ](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/api/org/springframework/boot/ansi/AnsiOutput.Enabled.html)£¬ÒÔ¸²¸Ç×Ô¶¯¼ì²â¡£ + + + + + +ÑÕÉ«±àÂëÊÇͨ¹ýʹÓà `%clr` ת»»¹Ø¼ü×ÖÀ´ÅäÖõġ£ ÔÚÆä×î¼òµ¥µÄÐÎʽÖУ¬×ª»»Æ÷¸ù¾ÝÈÕÖ¾¼¶±ð¶ÔÊä³ö½øÐÐ×ÅÉ«£¬ÈçÏÂÃæµÄÀý×ÓÖÐËùʾ¡£ + + + + + + + +``` +%clr(%5p) +``` + + + + + + + +ϱíÃèÊöÁËÈÕÖ¾¼¶±ðÓëÑÕÉ«µÄÓ³Éä¹ØÏµ¡£ + + + + +| ÈÕÖ¾¼¶±ð | ÑÕÉ« | +| --- | --- | +| `FATAL` | ºì | +| `ERROR` | ºì | +| `WARN` | »Æ | +| `INFO` | ÂÌ | +| `DEBUG` | ÂÌ | +| `TRACE` | ÂÌ | + + + +ÁíÍ⣬ÄãÒ²¿ÉÒÔͨ¹ýΪת»»Ìṩһ¸öÑ¡ÏîÀ´Ö¸¶¨Ó¦¸ÃʹÓõÄÑÕÉ«»òÑùʽ¡£ ÀýÈ磬ҪʹÎı¾Îª»ÆÉ«£¬ÇëʹÓÃÒÔÏÂÉèÖᣠ+ + + + + + + +``` +%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){yellow} +``` + + + + + + + +Ö§³ÖÒÔÏÂÑÕÉ«ºÍÑùʽ¡£ + + + + + +* `blue` + +* `cyan` + +* `faint` + +* `green` + +* `magenta` + +* `red` + +* `yellow` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.file-output)4.3\. Êä³öµ½Îļþ + + + +ĬÈÏÇé¿öÏ£¬Spring BootÖ»Ïò¿ØÖÆÌ¨¼Ç¼ÈÕÖ¾£¬²»Ð´ÈÕÖ¾Îļþ¡£ Èç¹ûÄãÏëÔÚ¿ØÖÆÌ¨Êä³öÖ®ÍâдÈÕÖ¾Îļþ£¬ÄãÐèÒªÉèÖà `logging.file.name` »ò `logging.file.path` ÊôÐÔ£¨ÀýÈ磬ÔÚÄãµÄ `application.properties` ÖУ©¡£ + + + + + +ϱíÏÔʾÁË `logging.*` ÊôÐÔÈçºÎ±»Ò»ÆðʹÓᣠ+ + + +Table 5\. Logging properties +| `logging.file.name` | `logging.file.path` | Example | Description | +| --- | --- | --- | --- | +| _(none)_ | _(none)_ | | Ö»ÔÚ¿ØÖÆÌ¨½øÐмǼ¡£ | +| Ö¸¶¨Îļþ | _(none)_ | `my.log` | дÈëÖ¸¶¨µÄÈÕÖ¾Îļþ¡£ Ãû³Æ¿ÉÒÔÊÇÒ»¸öÈ·ÇеÄλÖã¬Ò²¿ÉÒÔÊÇÓ뵱ǰĿ¼µÄÏà¶ÔλÖᣠ| +| _(none)_ | Ö¸¶¨Ä¿Â¼ | `/var/log` | ½« `spring.log` дµ½Ö¸¶¨Ä¿Â¼¡£ Ãû³Æ¿ÉÒÔÊÇÒ»¸öÈ·ÇеÄλÖã¬Ò²¿ÉÒÔÊÇÓ뵱ǰĿ¼µÄÏà¶ÔλÖᣠ| + + + +ÈÕÖ¾ÎļþÔÚ´ïµ½10MBʱ¾Í»áÂÖ»»£¬Óë¿ØÖÆÌ¨Êä³öÒ»Ñù£¬Ä¬ÈÏÇé¿öÏ»á¼Ç¼ `ERROR` ¡¢`WARN` ¼¶ºÍ `INFO` ¼¶±ðµÄÐÅÏ¢¡£ + + + + + +| | ÈÕÖ¾ÊôÐÔ¶ÀÁ¢ÓÚʵ¼ÊµÄÈÕÖ¾»ù´¡ÉèÊ©¡£ Òò´Ë£¬Ìض¨µÄÅäÖÃÊôÐÔ£¨ÈçLogbackµÄ `logback.configurationFile` £©²»ÓÉspring Boot¹ÜÀí¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.file-rotation)4.4\. ÎļþÂÖ»»£¨¹ö¶¯ÈÕÖ¾£© + + + +Èç¹ûÄãʹÓÃLogback£¬¿ÉÒÔʹÓÃÄãµÄ `application.properties` »ò `application.yaml` ÎļþÀ´Î¢µ÷ÈÕÖ¾ÂÖ»»ÉèÖᣠ¶ÔÓÚËùÓÐÆäËûµÄÈÕ־ϵͳ£¬Ä㽫ÐèÒª×Ô¼ºÖ±½ÓÅäÖÃÂÖ»»ÉèÖã¨ÀýÈ磬Èç¹ûÄãʹÓÃLog4J2£¬ÄÇôÄã¿ÉÒÔÌí¼ÓÒ»¸ö `log4j2.xml` »ò `log4j2-spring.xml` Îļþ£©¡£ + + + + + +Ö§³ÖÒÔÏÂÂÖ»»²ßÂÔÊôÐÔ¡£ + + + + +| ÊôÐÔ | ˵Ã÷ | +| --- | --- | +| `logging.logback.rollingpolicy.file-name-pattern` | ÓÃÓÚ´´½¨ÈÕÖ¾¹éµµµÄÎļþÃûģʽ¡£ | +| `logging.logback.rollingpolicy.clean-history-on-start` | Ó¦ÓóÌÐòÆô¶¯Ê±£¬ÊÇ·ñÐÐÈÕÖ¾¹éµµÇåÀí¡£ | +| `logging.logback.rollingpolicy.max-file-size` | ÈÕÖ¾Îļþ¹éµµÇ°µÄ×î´ó³ß´ç£¨Îļþ×î´óÌå»ý£¬´ïµ½Õâ¸öÌå»ý¾Í»á¹éµµ£©¡£ | +| `logging.logback.rollingpolicy.total-size-cap` | ÈÕÖ¾µµ°¸ÔÚ±»É¾³ýǰµÄ×î´ó³ß´ç£¨¹éµµÎļþ×î´óÕ¼ÓôóС£¬³¬¹ýÕâ¸ö´óСºó»á±»É¾³ý£©¡£ | +| `logging.logback.rollingpolicy.max-history` | Òª±£ÁôµÄ¹éµµÈÕÖ¾ÎļþµÄ×î´óÊýÁ¿£¨Ä¬ÈÏΪ7£©¡£ | + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.log-levels)4.5\. ÈÕÖ¾¼¶±ð + + + +ËùÓÐÖ§³ÖµÄÈÕ־ϵͳ¶¼¿ÉÒÔͨ¹ýʹÓà `logging.level.=` ÔÚSpringµÄ `Environment`£¨ÀýÈ磬ÔÚ `application.properties`£©ÖÐÉèÖÃÈÕÖ¾¼¶±ð£¬ÆäÖÐ `level` ÊÇ `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`, »ò `OFF` Ö®Ò»¡£ `root` ¼Ç¼Æ÷£¨logger£©µÄ¼¶±ð¿ÉÒÔͨ¹ý `logging.level.root` À´ÅäÖᣠ+ + + + + +ÏÂÃæµÄÀý×ÓÏÔʾÁË `application.properties` ÖÐDZÔÚµÄÈÕÖ¾ÉèÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +logging.level.root=warn +logging.level.org.springframework.web=debug +logging.level.org.hibernate=error +``` + + + + + + + +Ò²¿ÉÒÔʹÓû·¾³±äÁ¿À´ÉèÖÃÈÕÖ¾¼¶±ð¡£ ÀýÈ磬`LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG` ½«ÉèÖà `org.springframework.web` Ϊ `DEBUG` ¡£ + + + + + +| | ÉÏÊö·½·¨Ö»ÊÊÓÃÓÚ°ü¼¶ÈÕÖ¾¡£ ÓÉÓÚ¿íËɰó¶¨×ÜÊǽ«»·¾³±äÁ¿×ª»»ÎªÐ¡Ð´×Öĸ£¬ËùÒÔ²»¿ÉÄÜÓÃÕâÖÖ·½Ê½Îªµ¥¸öÀàÅäÖÃÈÕÖ¾¡£ Èç¹ûÄãÐèҪΪһ¸öÀàÅäÖÃÈÕÖ¾£¬Äã¿ÉÒÔʹÓÃ[`SPRING_APPLICATION_JSON`](https://springdoc.cn/spring-boot/features.html#features.external-config.application-json)±äÁ¿¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.log-groups)4.6\. ÈÕÖ¾×飨Log Groups£© + + + +Äܹ»½«Ïà¹ØµÄÈÕÖ¾¼Ç¼Æ÷·Ö×飬ÒÔ±ãͬʱ¶ÔËüÃǽøÐÐÅäÖã¬Õâͨ³£ºÜÓÐÓᣠÀýÈ磬Äã¿ÉÄܾ­³£¸Ä±ä _ËùÓÐ_ ÓëTomcatÏà¹ØµÄ¼Ç¼Æ÷µÄ¼Ç¼¼¶±ð£¬µ«Äã²»ÈÝÒ×¼Çס×î¸ß¼¶±ðµÄ°ü¡£ + + + + + +ΪÁ˰ïÖú½â¾öÕâ¸öÎÊÌ⣬Spring BootÔÊÐíÄãÔÚSpring `Environment` Öж¨ÒåÈÕÖ¾×é¡£ ÀýÈ磬Äã¿ÉÒÔͨ¹ýÔÚ `application.properties` ÖмÓÈë ¡°tomcat¡± group À´¶¨ÒåËü¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat + +``` + + + + + + + +Ò»µ©¶¨ÒåºÃºó£¬¾Í¿ÉÒÔÓÃÒ»ÐдúÂëÀ´¸Ä±ä×éÖÐËùÓÐloggerµÄ¼¶±ð¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +logging.level.tomcat=trace + +``` + + + + + + + +Spring Boot°üÀ¨ÒÔÏÂÔ¤¶¨ÒåµÄÈÕÖ¾×飬¿ÉÒÔ¿ªÏä¼´Óᣠ+ + + + +| ×éÃû | ×éÖеÄlogger | +| --- | --- | +| web | `org.springframework.core.codec`, `org.springframework.http`, `org.springframework.web`, `org.springframework.boot.actuate.endpoint.web`, `org.springframework.boot.web.servlet.ServletContextInitializerBeans` | +| sql | `org.springframework.jdbc.core`, `org.hibernate.SQL`, `org.jooq.tools.LoggerListener` | + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.shutdown-hook)4.7\. ʹÓÃÈÕÖ¾ Shutdown Hook + + + +ΪÁËÔÚÄãµÄÓ¦ÓóÌÐòÖÕֹʱÊÍ·ÅÈÕÖ¾×ÊÔ´£¬ÎÒÃÇÌṩÁËÒ»¸öShutdown Hook£¬Ëü½«ÔÚJVMÍ˳öʱ´¥·¢ÈÕ־ϵͳÇåÀí¡£ ³ý·ÇÄãµÄÓ¦ÓóÌÐòÊÇÒÔwarÎļþµÄÐÎʽ²¿ÊðµÄ£¬·ñÔòÕâ¸öShutdown Hook»á×Ô¶¯×¢²á¡£ Èç¹ûÄãµÄÓ¦ÓóÌÐòÓи´ÔÓµÄÉÏÏÂÎIJã´Î½á¹¹£¬Shutdown Hook¿ÉÄÜÎÞ·¨Âú×ãÄãµÄÐèÇó¡£ Èç¹û²»ÄÜ£¬Çë½ûÓùػú¹³×Ó£¬²¢Ñо¿µ×²ãÈÕ־ϵͳֱ½ÓÌṩµÄÑ¡Ïî¡£ ÀýÈ磬LogbackÌṩÁË [context selectors](https://logback.qos.ch/manual/loggingSeparation.html)£¬ÔÊÐíÿ¸ö¼Ç¼Æ÷ÔÚËü×Ô¼ºµÄÉÏÏÂÎÄÖб»´´½¨¡£ Äã¿ÉÒÔʹÓà `logging.register-shutdown-hook` ÊôÐÔÀ´½ûÓÃShutdown Hook¡£ ½«ÆäÉèÖÃΪ `false` ½«½ûÓÃ×¢²á¡£ Äã¿ÉÒÔÔÚÄãµÄ `application.properties` »ò `application.yaml` ÎļþÖÐÉèÖøÃÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +logging.register-shutdown-hook=false + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.custom-log-configuration)4.8\. ×Ô¶¨ÒåÈÕÖ¾ÅäÖà + + + +¸÷ÖÖÈÕ־ϵͳ¿ÉÒÔͨ¹ýÔÚclasspathÉϰüº¬Êʵ±µÄ¿âÀ´¼¤»î£¬²¢ÇÒ¿ÉÒÔͨ¹ýÔÚclasspathµÄ¸ùĿ¼Ï»òÔÚ Spring `Environment` ÊôÐÔÖ¸¶¨µÄλÖÃÌṩһ¸öºÏÊʵÄÅäÖÃÎļþÀ´½øÒ»²½¶¨ÖÆ£º `logging.config`¡£ + + + + + +Äú¿ÉÒÔͨ¹ýʹÓà `org.springframework.boot.logging.LoggingSystem` ϵͳÊôÐÔ£¬Ç¿ÖÆSpring BootʹÓÃÌØ¶¨µÄÈÕ־ϵͳ¡£ ¸ÃÖµÓ¦¸ÃÊÇ `LoggingSystem` ʵÏÖµÄÈ«ÀàÃû¡£ ÄãÒ²¿ÉÒÔͨ¹ýʹÓà `none` µÄÖµÀ´ÍêÈ«½ûÓÃSpring BootµÄÈÕÖ¾ÅäÖᣠ+ + + + + +| | SÓÉÓÚÈÕÖ¾ÊÇÔÚ´´½¨ `ApplicationContext` ֮ǰ³õʼ»¯µÄ£¬ËùÒÔ²»¿ÉÄÜ´ÓSpring `@Configuration` ÎļþÖÐµÄ `@PropertySources` ¿ØÖÆÈÕÖ¾¡£ ¸Ä±äÈÕ־ϵͳ»òÍêȫͣÓÃËüµÄΨһ·½·¨ÊÇͨ¹ýSystem properties¡£ | +| --- | --- | + + + + + +¸ù¾ÝÄãµÄÈÕ־ϵͳ£¬»á¼ÓÔØÒÔÏÂÎļþ¡£ + + + + +| ÈÕ־ϵͳ | ÅäÖÃÎļþ | +| --- | --- | +| Logback | `logback-spring.xml`, `logback-spring.groovy`, `logback.xml` »òÕß `logback.groovy` | +| Log4j2 | `log4j2-spring.xml` »òÕß `log4j2.xml` | +| JDK (Java Util Logging) | `logging.properties` | + + + +| | ÔÚ¿ÉÄܵÄÇé¿öÏ£¬ÎÒÃǽ¨ÒéÄãʹÓà `-spring` ±äÌåÀ´½øÐÐÈÕÖ¾ÅäÖã¨ÀýÈ磬 `logback-spring.xml` ¶ø²»ÊÇ `logback.xml` £©¡£ Èç¹ûÄãʹÓñê×¼ÅäÖÃλÖã¬Spring²»ÄÜÍêÈ«¿ØÖÆÈÕÖ¾³õʼ»¯¡£ | +| --- | --- | + + + + + +| | µ±´Ó "¿ÉÖ´ÐеÄjar "ÖÐÔËÐÐʱ£¬Java Util LoggingÓÐһЩÒÑÖªµÄÀà¼ÓÔØÎÊÌ⣬»áµ¼ÖÂÎÊÌâ¡£ Èç¹û¿ÉÄܵϰ£¬ÎÒÃǽ¨ÒéÄãÔÚ´Ó "¿ÉÖ´ÐеÄjar" ÖÐÔËÐÐʱ±ÜÃâʹÓÃËü¡£ | +| --- | --- | + + + + + +ΪÁ˰ïÖú¶¨ÖÆ£¬ÆäËûһЩÊôÐÔ´ÓSpringµÄ `Environment` ×ªÒÆµ½System properties£¬ÈçϱíËùʾ¡£ + + + +| Spring Environment | System Property | ±¸×¢ | +| --- | --- | --- | +| `logging.exception-conversion-word` | `LOG_EXCEPTION_CONVERSION_WORD` | ¼Ç¼Ò쳣ʱʹÓõÄת»»´Ê¡£ | +| `logging.file.name` | `LOG_FILE` | Èç¹û¶¨ÒåÁË£¬Ëü½«ÓÃÓÚĬÈϵÄÈÕÖ¾ÅäÖÃÖС£ | +| `logging.file.path` | `LOG_PATH` | Èç¹û¶¨ÒåÁË£¬Ëü½«ÓÃÓÚĬÈϵÄÈÕÖ¾ÅäÖÃÖС£ | +| `logging.pattern.console` | `CONSOLE_LOG_PATTERN` | ÔÚ¿ØÖÆÌ¨£¨stdout£©Ê¹ÓõÄÈÕÖ¾Êä³öģʽ¡£ | +| `logging.pattern.dateformat` | `LOG_DATEFORMAT_PATTERN` | date ¸ñʽ»¯. | +| `logging.charset.console` | `CONSOLE_LOG_CHARSET` | ¿ØÖÆÌ¨Êä³öÈÕÖ¾µÄ×Ö·û±àÂë¡£ | +| `logging.threshold.console` | `CONSOLE_LOG_THRESHOLD` | ÓÃÓÚ¿ØÖÆÌ¨ÈÕÖ¾¼Ç¼µÄÈÕÖ¾¼¶±ð¡£ | +| `logging.pattern.file` | `FILE_LOG_PATTERN` | ÒªÔÚÎļþÖÐʹÓõÄÈÕ־ģʽ£¨Èç¹û `LOG_FILE` ±»ÆôÓã©¡£ | +| `logging.charset.file` | `FILE_LOG_CHARSET` | ÎļþÈÕÖ¾µÄ×Ö·û±àÂ루Èç¹û `LOG_FILE` ±»ÆôÓã©¡£ | +| `logging.threshold.file` | `FILE_LOG_THRESHOLD` | ÓÃÓÚÎļþÈÕÖ¾¼Ç¼µÄÈÕÖ¾¼¶±ð¡£ | +| `logging.pattern.level` | `LOG_LEVEL_PATTERN` | äÖȾÈÕÖ¾¼¶±ðʱʹÓõĸñʽ£¨Ä¬ÈÏΪ `%5p` £©¡£ | +| `PID` | `PID` | µ±Ç°µÄ½ø³ÌID | + + + +Èç¹ûÄãʹÓÃLogback£¬ÒÔÏÂÊôÐÔÒ²»á±»×ªÒÆ¡£ + + + +| Spring Environment | System Property | ±¸×¢ | +| --- | --- | --- | +| `logging.logback.rollingpolicy.file-name-pattern` | `LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN` | ¹ö¶¯ÈÕÖ¾ÎļþÃûµÄģʽ£¨Ä¬ÈÏΪ `${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz` £©¡£ | +| `logging.logback.rollingpolicy.clean-history-on-start` | `LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START` | ÊÇ·ñÔÚÆô¶¯Ê±ÇåÀí¹éµµÈÕÖ¾Îļþ¡£ | +| `logging.logback.rollingpolicy.max-file-size` | `LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE` | ×î´óÈÕÖ¾Îļþ´óС¡£ | +| `logging.logback.rollingpolicy.total-size-cap` | `LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP` | Òª±£ÁôµÄÈÕÖ¾±¸·ÝµÄ×Ü´óС¡£ | +| `logging.logback.rollingpolicy.max-history` | `LOGBACK_ROLLINGPOLICY_MAX_HISTORY` | Òª±£ÁôµÄ×î´ó¹éµµÈÕÖ¾ÎļþÊýÁ¿¡£ | + + + +ËùÓÐÖ§³ÖµÄÈÕ־ϵͳÔÚ½âÎöÆäÅäÖÃÎļþʱ¶¼¿ÉÒÔ´Ó System properties ÖлñÈ¡ÊôÐÔ¡£ Àý×Ó¼û `spring-boot.jar` ÖеÄĬÈÏÅäÖᣠ+ + + + + +* [Logback](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml) + +* [Log4j 2](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml) + +* [Java Util logging](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging-file.properties) + + + + + +| | Èç¹ûÄãÏëÔÚÈÕÖ¾ÊôÐÔÖÐʹÓÃռλ·û£¬ÄãÓ¦¸ÃʹÓÃ[Spring BootµÄÓï·¨](https://springdoc.cn/spring-boot/features.html#features.external-config.files.property-placeholders)¶ø²»Êǵײã¿ò¼ÜµÄÓï·¨¡£ ÖµµÃ×¢ÒâµÄÊÇ£¬Èç¹ûÄãʹÓÃLogback£¬ÄãÓ¦¸ÃʹÓà `:` ×÷ΪÊôÐÔÃûºÍÆäĬÈÏÖµÖ®¼äµÄ·Ö¸ô·û£¬¶ø²»ÊÇʹÓà `:-` ¡£ | +| --- | --- | + + + + + +| | Äã¿ÉÒÔͨ¹ýÖ»¸²¸Ç `LOG_LEVEL_PATTERN` £¨»òʹÓÃLogbackµÄ `logging.pattern.level` £©À´ÏòÈÕÖ¾ÐÐÌí¼ÓMDCºÍÆäËûÁÙʱÄÚÈÝ¡£ ÀýÈ磬Èç¹ûÄãʹÓà `logging.pattern.level=user:%X{user} %5p` £¬ÄÇôĬÈϵÄÈÕÖ¾¸ñʽ°üº¬Ò»¸ö "user" µÄMDCÌõÄ¿£¬Èç¹ûËü´æÔڵϰ£¬ÈçÏÂÀýËùʾ¡£ 2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.ControllerHandling authenticated request | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.logback-extensions)4.9\. Logback À©Õ¹ + + + +Spring Boot°üÀ¨Ò»Ð©¶ÔLogbackµÄÀ©Õ¹£¬¿ÉÒÔ°ïÖú½øÐи߼¶ÅäÖᣠÄã¿ÉÒÔÔÚÄãµÄ `logback-spring.xml` ÅäÖÃÎļþÖÐʹÓÃÕâЩÀ©Õ¹¡£ + + + + + +| | ÒòΪ±ê×¼µÄ `logback.xml` ÅäÖÃÎļþ±»¹ýÔç¼ÓÔØ£¬Äã²»ÄÜÔÚÆäÖÐʹÓÃÀ©Õ¹¡£ ÄãÐèҪʹÓà `logback-spring.xml` »òÕß¶¨ÒåÒ»¸ö `logging.config` ÊôÐÔ¡£ | +| --- | --- | + + + + + +| | À©Õ¹³ÌÐò²»ÄÜÓë [LogbackµÄÅäÖÃɨÃè](https://logback.qos.ch/manual/configuration.html#autoScan) Ò»ÆðʹÓᣠÈç¹ûÄãÊÔͼÕâÑù×ö£¬¶ÔÅäÖÃÎļþ½øÐÐÐ޸ĻᵼÖÂÀàËÆÓÚÒÔϵĴíÎ󱻼ǼÏÂÀ´¡£ | +| --- | --- | + + + + + + + + ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] +ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]] + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.logback-extensions.profile-specific)4.9.1\. ÌØ¶¨µÄÅäÖÃÎļþ + + + +`` ±êÇ©ÈÃÄã¿ÉÒÔ¸ù¾Ý»î¶¯µÄSpringÅäÖÃÎļþÑ¡ÔñÐԵذüÀ¨»òÅųýÅäÖõIJ¿·Ö£¬ Ö§³ÖÔÚ `` ÔªËØµÄÈκεط½¶¨ÒåËü¡£ ʹÓà `name` ÊôÐÔÀ´Ö¸¶¨½ÓÊÜÅäÖõÄÅäÖÃÎļþ¡£ `` ±êÇ©¿ÉÒÔ°üº¬Ò»¸öÅäÖÃÎļþÃû³Æ£¨ÀýÈç `staging` £©»òÒ»¸öÅäÖÃÎļþ±í´ïʽ¡£ ÅäÖÃÎļþ±í´ïʽÔÊÐí±í´ï¸ü¸´ÔÓµÄÅäÖÃÎļþÂß¼­£¬ÀýÈç `production & (eu-central | eu-west)` ¡£ ²é¿´ [Spring ¿ò¼Ü²Î¿¼Ö¸ÄÏ](https://docs.spring.io/spring-framework/docs/6.0.5/reference/html/core.html#beans-definition-profiles-java) ÒÔÁ˽â¸ü¶àϸ½Ú¡£ ÏÂÃæµÄÁбíÏÔʾÁËÈý¸öÑù±¾ÅäÖÃÎļþ¡£ + + + + + + + +``` + + + + + + + + + + + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.logback-extensions.environment-properties)4.9.2\. £¨»·¾³ÊôÐÔ£©Environment Properties + + + +`` ±êÇ©¿ÉÒÔ·ÃÎÊ Spring `Environment` ÖеÄÊôÐÔ£¬ÒÔ±ãÔÚLogbackÖÐʹÓᣠÈç¹ûÄãÏëÔÚLogbackÅäÖÃÖзÃÎÊ `application.properties` ÎļþÖеÄÖµ£¬ÕâÑù×ö»áºÜÓÐÓᣠ¸Ã±êÇ©µÄ¹¤×÷·½Ê½ÓëLogbackµÄ±ê×¼ `` ±êÇ©ÀàËÆ¡£ È»¶ø£¬Äã²»ÊÇÖ±½ÓÖ¸¶¨Ò»¸ö `value` £¬¶øÊÇÖ¸¶¨ÊôÐ﵀ `source` £¨À´×Ô `Environment` £©¡£ Èç¹ûÄãÐèÒªÔÚ `local` ·¶Î§ÒÔÍâµÄµØ·½´æ´¢¸ÃÊôÐÔ£¬Äã¿ÉÒÔʹÓà `scope` ÊôÐÔ¡£ Èç¹ûÄãÐèÒªÒ»¸öºó±¸Öµ£¨Ä¬ÈÏÖµ£©£¨ÍòÒ»¸ÃÊôÐÔûÓÐÔÚ `Environment` ÖÐÉèÖã©£¬Äã¿ÉÒÔʹÓà `defaultValue` ÊôÐÔ¡£ ÏÂÃæµÄÀý×ÓÏÔʾÁËÈçºÎ¹«¿ªÊôÐÔÒÔ±ãÔÚLogbackÖÐʹÓᣠ+ + + + + + + +``` + + + ${fluentHost} + ... + +``` + + + + + + + +| | `source` ±ØÐëÒÔkebab·ç¸ñÖ¸¶¨£¨Èç `my.property-name` £©¡£ È»¶ø£¬ÊôÐÔ¿ÉÒÔͨ¹ýʹÓÿíËɵĹæÔòÌí¼Óµ½ `Environment` ÖС£ | +| --- | --- | + + + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.logging.log4j2-extensions)4.10\. Log4j2 À©Õ¹ + + + +Spring Boot°üÀ¨Ò»Ð©¶ÔLog4j2µÄÀ©Õ¹£¬¿ÉÒÔ°ïÖú½øÐи߼¶ÅäÖá£Äã¿ÉÒÔÔÚÈκΠ`log4j2-spring.xml` ÅäÖÃÎļþÖÐʹÓÃÕâЩÀ©Õ¹¡£ + + + + + +| | ÒòΪ±ê×¼µÄ `log4j2.xml` ÅäÖÃÎļþ±»¹ýÔç¼ÓÔØ£¬Äã²»ÄÜÔÚÆäÖÐʹÓÃÀ©Õ¹¡£ÄãÐèҪʹÓà `log4j2-spring.xml` »òÕß¶¨ÒåÒ»¸ö ``logging.config`` ÊôÐÔ¡£ | +| --- | --- | + + + + + +| | ÕâЩÀ©Õ¹È¡´úÁËLog4JÌṩµÄ [Spring BootÖ§³Ö](https://logging.apache.org/log4j/2.x/log4j-spring-boot/index.html)¡£ ÄãÓ¦¸ÃÈ·±£ÔÚÄãµÄ¹¹½¨Öв»°üÀ¨ `org.apache.logging.log4j:log4j-spring-boot` Ä£¿é¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.log4j2-extensions.profile-specific)4.10.1\. ÌØ¶¨ÅäÖÃÎļþÅäÖà + + + +`` ±êÇ©ÈÃÄã¿ÉÒÔ¸ù¾Ý»î¶¯µÄSpringÅäÖÃÎļþÑ¡ÔñÐԵذüÀ¨»òÅųýÅäÖõIJ¿·Ö¡£ÅäÖÃÎļþ²¿·Ö±»Ö§³ÖÔÚ `` ÔªËØµÄÈκεط½¡£Ê¹Óà `name` ÊôÐÔÀ´Ö¸¶¨ÄĸöÅäÖÃÎļþ½ÓÊÜÅäÖᣠ`` ±êÇ©¿ÉÒÔ°üº¬Ò»¸öÅäÖÃÎļþÃû³Æ£¨ÀýÈç `staging`£©»òÒ»¸öÅäÖÃÎļþ±í´ïʽ¡£ ÅäÖÃÎļþ±í´ïʽÔÊÐí±í´ï¸ü¸´ÔÓµÄÅäÖÃÎļþÂß¼­£¬ÀýÈç `production & (eu-central | eu-west)`¡£²é¿´ [Spring¿ò¼Ü²Î¿¼Ö¸ÄÏ](https://docs.spring.io/spring-framework/docs/6.0.5/reference/html/core.html#beans-definition-profiles-java) ÒÔÁ˽â¸ü¶àϸ½Ú¡£ ÏÂÃæµÄÁбíÏÔʾÁËÈý¸öÑù±¾ÅäÖÃÎļþ¡£ + + + + + + + +``` + + + + + + + + + + + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.log4j2-extensions.environment-properties-lookup)4.10.2\. »·¾³£¨Environment£©ÊôÐÔ²éÕÒ + + + +Èç¹ûÄãÏëÔÚLog4j2ÅäÖÃÖÐÒýÓÃSpring `Environment` ÖеÄÊôÐÔ£¬Äã¿ÉÒÔʹÓà `spring:` ǰ׺ [²éÕÒ](https://logging.apache.org/log4j/2.x/manual/lookups.html)¡£Èç¹ûÄãÏëÔÚLog4j2ÅäÖÃÖзÃÎÊ `application.properties` ÎļþÖеÄÖµ£¬ÕâÑù×ö»áºÜÓÐÓᣠ+ + + + + +ÏÂÃæµÄÀý×ÓÏÔʾÁËÈçºÎÉèÖÃÒ»¸öÃûΪ `applicationName` µÄLog4j2ÊôÐÔ£¬Ëü´ÓSpring `Environment` ÖжÁÈ¡ `spring.application.name`¡£ + + + + + + + +``` + + ${spring:spring.application.name} + +``` + + + + + + + +| | ²éѯkeyÓ¦ÒÔkebabf·ç¸ñÖ¸¶¨£¨Èç `my.property-name`£©¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.logging.log4j2-extensions.environment-property-source)4.10.3\. Log4j2 ϵͳÊôÐÔ£¨System Properties£© + + + +Log4j2Ö§³ÖһЩ [System Properties](https://logging.apache.org/log4j/2.x/manual/configuration.html#SystemProperties)£¬¿ÉÒÔÓÃÀ´ÅäÖø÷ÖÖÏîÄ¿¡£ÀýÈ磬`log4j2.skipJansi` ϵͳÊôÐÔ¿ÉÒÔÓÃÀ´ÅäÖà `ConsoleAppender` ÊÇ·ñ»áÔÚWindowsÉϳ¢ÊÔʹÓà [Jansi](https://github.com/fusesource/jansi) Êä³öÁ÷¡£ + + + + + +Log4j2 ³õʼ»¯ºó¼ÓÔØµÄËùÓÐϵͳÊôÐÔ¶¼¿ÉÒÔ´ÓSpring `Environment` ÖлñµÃ¡£ÀýÈ磬Äã¿ÉÒÔÔÚ `application.properties` ÎļþÖÐÌí¼Ó `log4j2.skipJansi=false`£¬Èà `ConsoleAppender` ÔÚWindowsÉÏʹÓÃJansi¡£ + + + + + +| | Ö»Óе±ÏµÍ³ÊôÐÔ£¨system properties£©ºÍ²Ù×÷ϵͳ»·¾³±äÁ¿²»°üº¬ÕýÔÚ¼ÓÔØµÄֵʱ£¬²Å»á¿¼ÂÇSpring `Environment`¡£ | +| --- | --- | + + + + + +| | ÔÚÔçÆÚLog4j2³õʼ»¯ÆÚ¼ä¼ÓÔØµÄϵͳÊôÐÔ²»ÄÜÒýÓÃSpring `Environment`¡£ÀýÈ磬Log4j2ÓÃÓÚÔÊÐíÑ¡ÔñĬÈÏLog4j2ʵÏÖµÄÊôÐÔÊÇÔÚ Spring Environment ¿ÉÓÃ֮ǰʹÓõġ£ | +| --- | --- | + + + + + + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\270\270\350\247\201\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\270\270\350\247\201\346\263\250\350\247\243.md" new file mode 100644 index 0000000..c699974 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\270\270\350\247\201\346\263\250\350\247\243.md" @@ -0,0 +1,158 @@ +## 1 ¸ÅÊö +Spring Boot ͨ¹ýÆä×Ô¶¯ÅäÖù¦ÄÜʹÅäÖà Spring ±äµÃ¸ü¼ÓÈÝÒס£ + +ÔÚ±¾¿ìËٽ̳ÌÖУ¬ÎÒÃǽ«Ì½Ë÷ org.springframework.boot.autoconfigure ºÍ org.springframework.boot.autoconfigure.condition °üÖеÄ×¢½â¡£ + +## 2 @SpringBootApplication +ÎÒÃÇʹÓÃÕâ¸ö×¢½âÀ´±ê¼Ç Spring Boot Ó¦ÓóÌÐòµÄÖ÷Àࣺ + +@SpringBootApplication +ÎÒÃÇʹÓÃÕâ¸ö×¢½âÀ´±ê¼Ç Spring Boot Ó¦ÓóÌÐòµÄÖ÷Àࣺ +```` +@SpringBootApplication +class VehicleFactoryApplication { + + public static void main(String[] args) { + SpringApplication.run(VehicleFactoryApplication.class, args); + } +} +```` +@SpringBootApplication ·â×°ÁË@Configuration¡¢@EnableAutoConfiguration ºÍ@ComponentScan ×¢½â¼°ÆäĬÈÏÊôÐÔ¡£ + +## 3 @EnableAutoConfiguration + +@EnableAutoConfiguration£¬¹ËÃû˼Ò壬ÆôÓÃ×Ô¶¯ÅäÖᣠÕâÒâζ×Å Spring Boot ÔÚÆäÀà·¾¶ÖвéÕÒ×Ô¶¯ÅäÖà bean ²¢×Ô¶¯Ó¦ÓÃËüÃÇ¡£ + +Çë×¢Ò⣬ÎÒÃDZØÐ뽫´Ë×¢ÊÍÓë@Configuration Ò»ÆðʹÓ㺠+ +```` +@Configuration +@EnableAutoConfiguration +class VehicleFactoryConfig {} +```` + +## 4 @ConfigurationÒÔ¼°Ïà¹ØÀà + +@ConfigurationµÄ×÷Ó㺱ê×¢ÔÚÀàÉÏ£¬ÅäÖÃspringÈÝÆ÷(Ó¦ÓÃÉÏÏÂÎÄ)¡£ + +ÒÔÍùÎÒÃÇÔÚspringÖÐÅäÖÃbean»áʹÓõ½xmlÅäÖÃÎļþ£¬¶¨ÒåÒ»¸ö¸öbean£¬ÔÚspringbootÖУ¬ÎªÁË×öµ½ÁãÅäÖã¬spring¿ò¼ÜÌṩÁË@ConfigurationÕâÒ»×¢½â + +Ï൱ÓڰѸÃÀà×÷ΪspringµÄxmlÅäÖÃÎļþÖÐµÄ + +@Configuration×¢½âµÄÀàÖУ¬Ê¹ÓÃ@Bean×¢½â±ê×¢µÄ·½·¨£¬·µ»ØµÄÀàÐͶ¼»áÖ±½Ó×¢²áΪbean¡£ + +@Configure×¢½âµÄ¶¨ÒåÈçÏ£º +```` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Configuration { +String value() default ""; +} +```` +´Ó¶¨ÒåÀ´¿´£¬µ×²ãÊǺ¬ÓÐ@Component £¬ËùÒÔ@Configuration ¾ßÓÐºÍ @Component µÄ×÷Óá£Òò´Ëcontext:component-scan/»òÕß@ComponentScan¶¼ÄÜ´¦Àí@Configuration×¢½âµÄÀà¡£ + +ͨ³££¬µ±ÎÒÃDZàд×Ô¶¨Òå×Ô¶¯ÅäÖÃʱ£¬ÎÒÃÇÏ£Íû Spring ÓÐÌõ¼þµØÊ¹ÓÃËüÃÇ¡£ ÎÒÃÇ¿ÉÒÔͨ¹ý±¾½ÚÖеÄ×¢ÊÍÀ´ÊµÏÖÕâÒ»µã¡£ + +ÎÒÃÇ¿ÉÒÔ½«´Ë²¿·ÖÖеÄ×¢ÊÍ·ÅÔÚ@Configuration Àà»ò@Bean ·½·¨ÉÏ¡£ + +ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«Ö»½éÉÜÿ¸öÌõ¼þ±³ºóµÄ»ù±¾¸ÅÄî¡£ ÓûÁ˽â¸ü¶àÐÅÏ¢£¬Çë·ÃÎÊÕâÆªÎÄÕ¡£ + +### 4.1 @ConditionalOnClass and @ConditionalOnMissingClass +ºÜÃ÷ÏÔÕâÊÇÒ»¸ö´øÓÐÌõ¼þÅжϵÄÅäÖÃ×¢½â£¬ÒªÖªµÀ£¬ºÜ¶àʱºòÎÒÃǰ´Ðè¼ÓÔØbeanµÄ£¬ÉõÖÁÊǸù¾ÝÍⲿjar°üÊÇ·ñ´æÔÚÀ´½øÐмÓÔØºÍÅжϵġ£ + +ÕâÖÖʱºò£¬¾ÍÒª¸ù¾ÝÍⲿÀàÊÇ·ñ´æÔÚÀ´¾ö¶¨ÊÇ·ñÊÇ·ñ¼ÓÔØ¸Ãbean + +ʹÓÃÕâЩÌõ¼þ£¬Èç¹û×¢ÊͲÎÊýÖеÄÀà´æÔÚ/²»´æÔÚ£¬Spring ½«½öʹÓñê¼ÇµÄ×Ô¶¯ÅäÖà bean£º + +```` +@Configuration +@ConditionalOnClass(DataSource.class) +class MySQLAutoconfiguration { +//... +} +```` + +### 4.2 @ConditionalOnBean and @ConditionalOnMissingBean + +µ±ÎÒÃÇÏëÒª¸ù¾ÝÌØ¶¨ bean µÄ´æÔÚ»ò²»´æÔÚÀ´¶¨ÒåÌõ¼þʱ£¬ÎÒÃÇ¿ÉÒÔʹÓÃÕâЩעÊÍ£º + +ºÍÉÏÃæÒ»¸ö×¢½âÉÔÓÐЩ²»Í¬£¬ÒòΪÎÒÃǵÄÅжÏÌõ¼þ±ä³ÉÁËbean + +```` +@Bean +@ConditionalOnBean(name = "dataSource") +LocalContainerEntityManagerFactoryBean entityManagerFactory() { +// ... +} +```` +### 4.3 @ConditionalOnProperty +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔ¶ÔÊôÐÔµÄÖµÉèÖÃÌõ¼þ + +ҪעÒ⣬ÕâÀïµÄÊôÐÔÖµÀ´Ô´ÓÚapplication.propertiesÎļþÖеÄÅäÖà + +```` +@Bean +@ConditionalOnProperty( +name = "usemysql", +havingValue = "local" +) +DataSource dataSource() { +// ... +} +```` + +### 4.4 @ConditionalOnResource + +ÎÒÃÇ¿ÉÒÔÈà Spring ½öÔÚ´æÔÚÌØ¶¨×ÊԴʱʹÓö¨Ò壺 +¹ËÃû˼Ò壬ҪÇóclasspath´æÔÚÕâ¸ö×ÊÔ´Îļþʱ²Å½øÐмÓÔØ£¬ÕâÒ²ÊǺܳ£ÓõÄÒ»¸ö×¢½â¡£ + +```` + +@ConditionalOnResource(resources = "classpath:mysql.properties") +Properties ditionalProperties() { +// ... +} +```` + +### 4.5 @ConditionalOnWebApplication and @ConditionalOnNotWebApplication +Õâ¸ö×¢½âͨ³£ÓÃÓÚºÍwebÓÐÇ¿¹ØÁª£¬»òÕßÍêÈ«ÎÞ¹ØÁªµÄÅäÖÃÉÏ + +ʹÓÃÕâЩעÊÍ£¬ÎÒÃÇ¿ÉÒÔ¸ù¾Ýµ±Ç°Ó¦ÓóÌÐòÊÇ·ñÊÇ Web Ó¦ÓóÌÐòÀ´´´½¨Ìõ¼þ£º +```` + +@ConditionalOnWebApplication +HealthCheckController healthCheckController() { +// ... +} +```` + +### 4.6 @ConditionalExpression +springbootÕæµÄΪÎÒÃÇÏëµ½ÁËËùÓÐÇé¿ö£¬Èç¹ûÉÏÃæµÄ×¢½â»¹²»ÄÜÂú×ãÄãµÄÒªÇó£¬ÄÇô¸É´à¾ÍÈÃÄã×Ô¼ºÐ´£¬ÕâÏÂÓ¦¸ÃûÓÐÎÊÌâ°É£¿ + +ÎÒÃÇ¿ÉÒÔÔÚ¸ü¸´ÔÓµÄÇé¿öÏÂʹÓÃÕâ¸ö×¢½â¡£ µ± SpEL ±í´ïʽ±»ÆÀ¹ÀÎªÕæÊ±£¬Spring ½«Ê¹Óñê¼ÇµÄ¶¨Ò壺 + +```` +@Bean +@ConditionalOnExpression("${usemysql} && ${mysqlserver == 'local'}") +DataSource dataSource() { +// ... +} +```` + +### 4.7 @Conditional +ʲô£¬»¹ÓÐÎÊÌ⣿ +ÄÇspringbootÒ²²»ÔÙÌṩʲô±í´ïʽ¸øÄãÁË£¬Ö±½ÓÈÃÓû§Ð´Ò»¸öÅжÏÌõ¼þ£¬¸æËßÎÒtrue»òÕßfalse¾ÍÐÐÁË + +¶ÔÓÚ¸ü¸´ÔÓµÄÌõ¼þ£¬ÎÒÃÇ¿ÉÒÔ´´½¨Ò»¸öÆÀ¹À×Ô¶¨ÒåÌõ¼þµÄÀà¡£ ÎÒÃǸæËß Spring ½«Õâ¸ö×Ô¶¨ÒåÌõ¼þÓë @Conditional Ò»ÆðʹÓ㺠+ +```` +@Conditional(HibernateCondition.class) +Properties ditionalProperties() { +//... +} +```` + +## 5 ×ܽá +ÔÚ±¾ÎÄÖУ¬ÎÒÃǸÅÊöÁËÈçºÎ΢µ÷×Ô¶¯ÅäÖùý³Ì²¢Îª×Ô¶¨Òå×Ô¶¯ÅäÖà bean ÌṩÌõ¼þ¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\272\224\347\224\250\344\271\237\345\217\257\344\273\245\351\203\250\347\275\262\345\210\260\345\244\226\351\203\250Tomcat.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\272\224\347\224\250\344\271\237\345\217\257\344\273\245\351\203\250\347\275\262\345\210\260\345\244\226\351\203\250Tomcat.md" new file mode 100644 index 0000000..e22fa00 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\345\272\224\347\224\250\344\271\237\345\217\257\344\273\245\351\203\250\347\275\262\345\210\260\345\244\226\351\203\250Tomcat.md" @@ -0,0 +1,302 @@ + + +
+ +# Spring Boot Tomcat²¿Êð + +ÒÔÏÂÄÚÈݽöÊÇÕ¾³¤»òÍøÓѸöÈËѧϰ±Ê¼Ç¡¢×ܽáºÍÑо¿Êղء£²»±£Ö¤ÕýÈ·ÐÔ£¬ÒòʹÓöø´øÀ´µÄ·çÏÕÓë±¾Õ¾Î޹أ¡ + +
+ + + + + + + +ͨ¹ýʹÓÃSpring BootÓ¦ÓóÌÐò£¬¿ÉÒÔ´´½¨Ò»¸öwarÎļþÒÔ²¿Êðµ½Web·þÎñÆ÷ÖС£ÔÚ±¾ÕÂÖУ¬½«Ñ§Ï°ÈçºÎ´´½¨WARÎļþ²¢ÔÚTomcat Web·þÎñÆ÷Öв¿ÊðSpring BootÓ¦ÓóÌÐò¡£ + +## Spring Boot Servlet³õʼ»¯³ÌÐò + +´«Í³µÄ²¿Êð·½Ê½ÊÇʹSpring BootÓ¦ÓóÌÐò`[@SpringBootApplication](https://github.com/SpringBootApplication "@SpringBootApplication")`ÀàÀ©Õ¹`SpringBootServletInitializer`Àà¡£ `SpringBootServletInitializer`ÀàÎļþÔÊÐíÔÚʹÓÃServletÈÝÆ÷Æô¶¯Ê±ÅäÖÃÓ¦ÓóÌÐò¡£ + +ÏÂÃæ¸ø³öÁËÓÃÓÚJARÎļþ²¿ÊðµÄSpring BootÓ¦ÓóÌÐòÀàÎļþµÄ´úÂë - + +``` +package com.yiibai.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } +} + +``` + +ÐèÒªÀ©Õ¹Àà`SpringBootServletInitializer`ÒÔÖ§³ÖWARÎļþ²¿Êð¡£ Spring BootÓ¦ÓóÌÐòÀàÎļþµÄ´úÂëÈçÏ - + +``` +package com.yiibai.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; + +@SpringBootApplication +public class DemoApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(DemoApplication.class); + } + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } +} + +``` + +## ÉèÖÃMainÀà + +ÔÚSpring BootÖУ¬ÐèÒªÔÚ¹¹½¨ÎļþÖÐÖ¸¶¨Æô¶¯µÄÖ÷Àà¡£ +¶ÔÓÚMaven£¬ÔÚ`pom.xml`ÊôÐÔÖÐÌí¼Ó`start`À࣬ÈçÏÂËùʾ - + +``` +com.yiibai.demo.DemoApplication + +``` + +¶ÔÓÚGradle£¬ÔÚ`build.gradle`ÖÐÌí¼ÓÖ÷ÀàÃû£¬ÈçÏÂËùʾ - + +``` +mainClassName="com.yiibai.demo.DemoApplication" + +``` + +## ½«´ò°üJAR¸üÐÂΪWAR + +ʹÓÃÒÔÏ´úÂ뽫°ü×°JAR¸üÐÂΪWAR¡£ + +¶ÔÓÚMaven£¬ÔÚ_pom.xml_ Öн«°ü×°Ìí¼ÓΪWAR£¬ÈçÏÂËùʾ - + +``` +war + +``` + +¶ÔÓÚGradle£¬ÔÚ_build.gradle_ ÖÐÌí¼ÓÓ¦ÓóÌÐò²å¼þºÍwar²å¼þ£¬ÈçÏÂËùʾ - + +``` +apply plugin: 'war' +apply plugin: 'application' + +``` + +¶ÔÓÚGradlNow£¬±àдһ¸ö¼òµ¥µÄRest¶ËµãÀ´·µ»Ø×Ö·û´®:`"Hello World from Tomcat"`¡£ Òª±àдRest¶Ëµã£¬ÐèÒª½«Spring Boot Web starterÒÀÀµÏîÌí¼Óµ½¹¹½¨ÎļþÖС£ + +¶ÔÓÚMaven£¬Ê¹ÓÃÈçÏÂËùʾµÄ´úÂëÔÚ_pom.xml_ ÖÐÌí¼ÓSpring BootÆô¶¯³ÌÐòÒÀÀµÏî - + +``` + + org.springframework.boot + spring-boot-starter-web + + +``` + +¶ÔÓÚGradle£¬Ê¹ÓÃÈçÏÂËùʾµÄ´úÂëÔÚ_build.gradle_ ÖÐÌí¼ÓSpring Boot starterÒÀÀµÏî - + +``` +dependencies { + compile('org.springframework.boot:spring-boot-starter-web') +} + +``` + +ÏÖÔÚ£¬Ê¹ÓÃÈçÏÂËùʾµÄ´úÂëÔÚSpring Boot ApplicationÀàÎļþÖбàдһ¸ö¼òµ¥µÄRest¶Ëµã - + +``` +package com.yiibai.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class DemoApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(DemoApplication.class); + } + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @RequestMapping(value = "/") + public String hello() { + return "Hello World from Tomcat"; + } +} + +``` + +## ´ò°üÓ¦ÓóÌÐò + +ÏÖÔÚ£¬Ê¹ÓÃMavenºÍGradleÃüÁî´´½¨Ò»¸öWARÎļþÒÔ²¿Êðµ½Tomcat·þÎñÆ÷ÖУ¬ÒÔ´ò°üÓ¦ÓóÌÐò£¬ÈçÏÂËùʾ¡£ + +¶ÔÓÚMaven£¬Ê¹ÓÃÃüÁî`mvn package`´ò°üÓ¦ÓóÌÐò¡£ È»ºó´´½¨WARÎļþ£¬¿ÉÒÔÔÚÄ¿±êĿ¼ÖÐÕÒµ½Ëü£¬ÈçÏÂÃæ¸ø³öµÄÆÁÄ»½ØÍ¼Ëùʾ - + +![](/uploads/images/2018/09/27/084613_17931.jpg) + +¶ÔÓÚGradle£¬Ê¹ÓÃÃüÁî`gradle clean build`´ò°üÓ¦ÓóÌÐò¡£ È»ºó£¬½«´´½¨WARÎļþ£¬¿ÉÒÔÔÚ`build/libs`Ŀ¼ÏÂÕÒµ½Ëü¡£¹Û²ì´Ë´¦¸ø³öµÄÆÁÄ»½ØÍ¼ÒÔ±ã¸üºÃµØÀí½â - + +![](/uploads/images/2018/09/27/084717_10144.jpg) + +## ²¿Êðµ½Tomcat + +ÏÖÔÚ£¬ÔËÐÐTomcat·þÎñÆ÷£¬²¢ÔÚwebappsĿ¼Ï²¿ÊðWARÎļþ¡£¹Û²ì´Ë´¦ÏÔʾµÄÆÁÄ»½ØÍ¼ÒÔ±ã¸üºÃµØÀí½â - + +![](/uploads/images/2018/09/27/084759_50620.jpg) + +³É¹¦²¿Êðºó£¬µã»÷ÍøÒ³ä¯ÀÀÆ÷ÖеÄURL => `http://localhost:8080/demo-0.0.1-SNAPSHOT/`£¬¹Û²ìÊä³ö½á¹ûÈçÏÂͼËùʾ - + +![](/uploads/images/2018/09/27/084848_70593.jpg) + +ÍêÕû´úÂëÈçÏ£º + +Îļþ£º_pom.xml_ - + +``` + + +4.0.0 + + com.yiibai + demo + 0.0.1-SNAPSHOT + war + demo + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + com.yiibai.demo.DemoApplication + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + +``` + +Îļþ£º_build.gradle_ + +``` +buildscript { + ext { + springBootVersion = '1.5.8.RELEASE' + } + repositories { + mavenCentral() + } +dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'org.springframework.boot' +apply plugin: 'war' +apply plugin: 'application' + +group = 'com.yiibai' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = 1.8 +mainClassName = "com.yiibai.demo.DemoApplication" + +repositories { + mavenCentral() +} +dependencies { + compile('org.springframework.boot:spring-boot-starter-web') + testCompile('org.springframework.boot:spring-boot-starter-test') +} + +``` + +Spring BootÓ¦ÓóÌÐòÀàÎļþµÄ´úÂëÈçÏ - + +``` +package com.yiibai.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class DemoApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(DemoApplication.class); + } + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @RequestMapping(value = "/") + public String hello() { + return "Hello World from Tomcat"; + } +} +``` + + + + + +//¸ü¶àÇëÔĶÁ£ºhttps://www.yiibai.com/spring-boot/spring_boot_tomcat_deployment.html + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" new file mode 100644 index 0000000..9f13c13 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\346\211\223\345\214\205\344\270\216\345\220\257\345\212\250.md" @@ -0,0 +1,1388 @@ +在使用`maven`构建`springboot`项目时,`springboot`相关 jar 包å�¯ä»¥ä½¿ç”¨`parentæ–¹å¼�`引入(å�³åœ¨`pom.xml`çš„`parent`节点引入`springboot`çš„`GAV`:`org.springframework.boot:spring-boot-starter-parent:2.1.1.RELEASE`),也å�¯ä»¥ä½¿ç”¨`é�žparentæ–¹å¼�`引入(å�³åœ¨ pom çš„ dependencyManagement 节点引入`springboot`çš„`GAV`:`org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE`)。å�Œæ—¶ï¼Œåœ¨æ‰“包时,我们å�¯ä»¥æ‰“æˆ� jar 包,也å�¯ä»¥æ‰“æˆ� war 包,本文旨在梳ç�†å�„引入ã€�打包方å¼�的异å�Œã€‚ + +### 1\. parent æ–¹å¼�引入,打æˆ� jar 包 + +parent æ–¹å¼�,å�³åœ¨ pom 文件中,将 springboot çš„ä¾�赖当æˆ�项目的 parent 引入,pom 文件示例如下: + +``` + + + 4.0.0 + + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.gitee.funcy + springboot-parent-jar + 1.0.0 + jar + springboot parent jar打包方å¼� + + + UTF-8 + UTF-8 + 1.8 + 3.8.1 + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + +``` + +添加一个 controller: + +``` +package com.gitee.funcy.mavenparent.jar.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * {这里添加æ��è¿°ï½� + * + * @author funcy + * @date 2019-12-13 10:43 下å�ˆ + */ +@RestController +public class IndexController { + + @RequestMapping("/") + public String helloWorld() { + return "hello world"; + } + +} + +``` + +å†�引入å�¯åŠ¨ç±»ï¼š + +``` +package com.gitee.funcy.mavenparent.jar; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * {这里添加æ��è¿°ï½� + * + * @author funcy + * @date 2019-12-13 10:36 下å�ˆ + */ +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } + +} + +``` + +è¿�行 Main 方法,请求`http://localhost:8080/`,结果如下: + +``` + $ curl http://localhost:8080/ +hello world + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®è¿�行æˆ�功。 + +接ç�€ï¼Œå°�试使用 jar 包å�¯åŠ¨ï¼š + +``` +# 打包 + mvn clean install -Dmaven.test.skip=true + # å�¯åЍjar包 + java -jar target/springboot-parent-jar-1.0.0.jar + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�¯åЍæˆ�功,请求请求`http://localhost:8080/`,也能显示正确结果。 + +### 2\. é�ž parent æ–¹å¼�引入,打æˆ� jar 包 + +在实际项目中,项目的 parent ä¾�èµ–å�¯èƒ½ç»™äº†å…¶ä»–项目,此时 parent 引用就无法进行了,这时我们需è¦�é�ž parent 引入。é�ž parent 引入的 pom 如下: + +``` + + + 4.0.0 + + com.gitee.funcy + springboot-jar + 1.0.0 + jar + springbooté�žparent jar打包方å¼� + + + UTF-8 + UTF-8 + 1.8 + 2.1.1.RELEASE + 3.8.1 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + + repackage + + + + + + + + + +``` + +å†�添加一个`ControllerIndexController.java`与å�¯åŠ¨ç±»`Main.java`,这两个文件与上述示例相å�Œï¼Œè¿™é‡Œå°±ä¸�作展示了。 + +è¿�行 Main 方法,请求`http://localhost:8080/`,结果如下: + +``` + $ curl http://localhost:8080/ +hello world + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®è¿�行æˆ�功。 + +接ç�€ï¼Œå°�试使用 jar 包å�¯åŠ¨ï¼š + +``` +# 打包 + mvn clean install -Dmaven.test.skip=true + # å�¯åЍjar包 + java -jar target/springboot-jar-1.0.0.jar + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�¯åЍæˆ�功,请求请求`http://localhost:8080/`,也能显示正确结果。 + +### 3\. parent æ–¹å¼�引入,打æˆ� war 包 + +以上两ç§�æ–¹å¼�都是打æˆ� jar,为了兼容传统的 servlet 应用,springboot 也支æŒ�打包 war 包,parent 引入打包 war 包的 pom 文件如下: + +``` + + + 4.0.0 + + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.gitee.funcy + springboot-parent-war + 1.0.0 + + war + springboot parent war打包方å¼� + + + UTF-8 + UTF-8 + 1.8 + 3.8.1 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-test + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-war-plugin + + 3.2.2 + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + +``` + +å†�添加一个`ControllerIndexController.java`与å�¯åŠ¨ç±»`Main.java`,这两个文件与上述示例相å�Œï¼Œè¿™é‡Œå°±ä¸�作展示了。 + +除此之外,war 包方å¼�还需è¦�添加一个类,用以实现`SpringBootServletInitializer`,该类与å�¯åŠ¨ç±»`Main.java`ä½�于å�Œä¸€ä¸ªåŒ…下,主è¦�是用æ�¥å¼•导 tomcat ç­‰ servlet 容器加载 servlet,内容如下: + +``` +/** + * {这里添加æ��è¿°ï½� + * + * @author funcy + * @date 2019-12-20 1:22 下å�ˆ + */ +public class StartApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { + // 注æ„�这里è¦�指å�‘原先用main方法执行的Applicationå�¯åŠ¨ç±» + return builder.sources(Main.class); + } +} + +``` + +è¿�行 Main 方法,请求`http://localhost:8080/`,结果如下: + +``` + $ curl http://localhost:8080/ +hello world + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®è¿�行æˆ�功。 + +接ç�€ï¼Œå°�试使用 jar 包å�¯åŠ¨ï¼š + +``` +# 打包 + mvn clean install -Dmaven.test.skip=true + # å�¯åЍjar包 + java -jar target/springboot-parent-war-1.0.0.jar + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�¯åЍæˆ�功,请求请求`http://localhost:8080/`,也能显示正确结果。 + +### 4\. é�ž parent æ–¹å¼�引入,打æˆ� war 包 + +å�Œæ ·åœ°ï¼Œæ‰“æˆ� war 包时,也å�¯ä½¿ç”¨é�ž parent 引入方å¼�: + +``` + + + 4.0.0 + + com.gitee.funcy + springboot-war + 1.0.0 + + war + springbooté�žparent war打包方å¼� + + + UTF-8 + UTF-8 + 1.8 + 2.1.1.RELEASE + 3.8.1 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-test + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-war-plugin + + 3.2.2 + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + +``` + +å†�添加一个`ControllerIndexController.java`ã€�`StartApplication.java`与å�¯åŠ¨ç±»`Main.java`,这三个文件与上述示例相å�Œï¼Œè¿™é‡Œå°±ä¸�作展示了。 + +è¿�行 Main 方法,请求`http://localhost:8080/`,结果如下: + +``` + $ curl http://localhost:8080/ +hello world + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®è¿�行æˆ�功。 + +接ç�€ï¼Œå°�试使用 jar 包å�¯åŠ¨ï¼š + +``` +# 打包 + mvn clean install -Dmaven.test.skip=true + # å�¯åЍjar包 + java -jar target/springboot-war-1.0.0.jar + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�¯åЍæˆ�功,请求请求`http://localhost:8080/`,也能显示正确结果。 + +### 5\. 总结 + +springboot 引入å�Šæ‰“包方å¼�组å�ˆä¸‹æ�¥æœ‰å¦‚下四ç§�: + +| 打包 / 引入 | parent æ–¹å¼� | é�ž parent æ–¹å¼� | +| --- | --- | --- | +| jar | parent-jar æ–¹å¼� | é�ž parent-jar æ–¹å¼� | +| war | parent-war æ–¹å¼� | é�ž parent-war æ–¹å¼� | + +### 1\. å¼€å�‘æ—¶å�¯åЍ + +在开å�‘æ—¶å�¯åЍ springboot 应用,指的是直接è¿�行æº�ç �,如在开å�‘时在 ide 中è¿�行å�¯åŠ¨ç±»çš„ main () 方法。 + +#### 1.1 在 ide 中执行å�¯åŠ¨ç±»çš„`main()`方法 + +自从有了 springboot å�Žï¼Œweb 项目就ä¸�å¿…å†�放到 web 容器中è¿�行了,直接è¿�行项目的`main()`方法就行了: + +![](https://oscimg.oschina.net/oscnet/up-040888025c22694b18d7be748b8cbb89f06.png) + +å�¯åŠ¨æ—¥å¿—å¦‚ä¸‹ï¼š + +``` + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 21:11:16.365 INFO 84046 --- [ main] com.gitee.funcy.maven.jar.Main : Starting Main on l with PID 84046 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-jar/target/classes started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo) +2020-01-07 21:11:16.368 INFO 84046 --- [ main] com.gitee.funcy.maven.jar.Main : No active profile set, falling back to default profiles: default +2020-01-07 21:11:17.468 INFO 84046 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 21:11:17.497 INFO 84046 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 21:11:17.497 INFO 84046 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 21:11:17.513 INFO 84046 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 21:11:17.605 INFO 84046 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 21:11:17.605 INFO 84046 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1206 ms +2020-01-07 21:11:17.861 INFO 84046 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 21:11:18.096 INFO 84046 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 21:11:18.100 INFO 84046 --- [ main] com.gitee.funcy.maven.jar.Main : Started Main in 1.988 seconds (JVM running for 2.34) +2020-01-07 21:11:32.155 INFO 84046 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' +2020-01-07 21:11:32.155 INFO 84046 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' +2020-01-07 21:11:32.223 INFO 84046 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 68 ms + +``` + +访问`http://localhost:8080/`,结果如下: + +``` +$ curl http://localhost:8080 +hello world + +``` + +以上å�¯åŠ¨æ–¹å¼�**war 与 jar 打包方å¼�**都支æŒ�。 + +#### 1.2`mvn spring-boot:run`å�¯åЍ + +è¿™ç§�æ–¹å¼�也是æº�ç �å�¯åŠ¨ï¼Œåœ¨å‘½ä»¤è¡Œç•Œé�¢è¿›å…¥é¡¹ç›®å¯¹åº”çš„æº�ç �目录下,然å�Žæ‰§è¡Œ`mvn spring-boot:run`命令: + +``` +springboot-parent-war $ mvn spring-boot:run +[INFO] Scanning for projects... +[INFO] +[INFO] ---------------< com.gitee.funcy:springboot-parent-war >---------------- +[INFO] Building springboot parent war打包方å¼� 1.0.0 +[INFO] --------------------------------[ war ]--------------------------------- +[INFO] +[INFO] >>> spring-boot-maven-plugin:2.1.1.RELEASE:run (default-cli) > test-compile @ springboot-parent-war >>> +[INFO] +[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ springboot-parent-war --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] Copying 0 resource +[INFO] Copying 0 resource +[INFO] +[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ springboot-parent-war --- +[INFO] Changes detected - recompiling the module! +[INFO] Compiling 3 source files to /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-parent-war/target/classes +[INFO] +[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ springboot-parent-war --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] skip non existing resourceDirectory /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-parent-war/src/test/resources +[INFO] +[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ springboot-parent-war --- +[INFO] No sources to compile +[INFO] +[INFO] <<< spring-boot-maven-plugin:2.1.1.RELEASE:run (default-cli) < test-compile @ springboot-parent-war <<< +[INFO] +[INFO] +[INFO] --- spring-boot-maven-plugin:2.1.1.RELEASE:run (default-cli) @ springboot-parent-war --- + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 21:40:50.577 INFO 84448 --- [ main] com.gitee.funcy.mavenparent.war.Main : Starting Main on funcydeMacBook-Pro.local with PID 84448 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-parent-war/target/classes started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-parent-war) +2020-01-07 21:40:50.579 INFO 84448 --- [ main] com.gitee.funcy.mavenparent.war.Main : No active profile set, falling back to default profiles: default +2020-01-07 21:40:51.311 INFO 84448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 21:40:51.336 INFO 84448 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 21:40:51.337 INFO 84448 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 21:40:51.347 INFO 84448 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 21:40:51.406 INFO 84448 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 21:40:51.406 INFO 84448 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 800 ms +2020-01-07 21:40:51.582 INFO 84448 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 21:40:51.736 INFO 84448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 21:40:51.739 INFO 84448 --- [ main] com.gitee.funcy.mavenparent.war.Main : Started Main in 1.39 seconds (JVM running for 3.943) +2020-01-07 21:41:04.068 INFO 84448 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' +2020-01-07 21:41:04.069 INFO 84448 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' +2020-01-07 21:41:04.076 INFO 84448 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�¯åЍæˆ�功,请求`http://localhost:8080`,也能获得结果: + +``` + $ curl http://localhost:8080 +hello world + +``` + +以上å�¯åŠ¨æ–¹å¼�**war 与 jar 打包方å¼�**都支æŒ�。 + +### 2\. jar 包å�¯åЍ + +#### 2.1`java -jar`æ–¹å¼�å�¯åЍ + +对于打æˆ�`jar包`çš„`springboot`项目,使用`java -jar xxx.jar`命令å�³å�¯å�¯åŠ¨ï¼š + +``` +:target $ java -jar springboot-jar-1.0.0.jar + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 21:47:47.075 INFO 85080 --- [ main] com.gitee.funcy.maven.jar.Main : Starting Main on funcydeMacBook-Pro.local with PID 85080 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-jar/target/springboot-jar-1.0.0.jar started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-jar/target) +2020-01-07 21:47:47.077 INFO 85080 --- [ main] com.gitee.funcy.maven.jar.Main : No active profile set, falling back to default profiles: default +2020-01-07 21:47:48.152 INFO 85080 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 21:47:48.186 INFO 85080 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 21:47:48.186 INFO 85080 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 21:47:48.202 INFO 85080 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 21:47:48.303 INFO 85080 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 21:47:48.303 INFO 85080 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1177 ms +2020-01-07 21:47:48.502 INFO 85080 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 21:47:48.677 INFO 85080 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 21:47:48.680 INFO 85080 --- [ main] com.gitee.funcy.maven.jar.Main : Started Main in 1.977 seconds (JVM running for 2.398) + +``` + +访问`http://localhost:8080`,å�Œæ ·ä¹Ÿèƒ½èŽ·å¾—ç»“æžœ. + +#### 2.2`java org.springframework.boot.loader.JarLauncher`æ–¹å¼�å�¯åЍ + +è¿™ç§�å�¯åŠ¨æ–¹å¼�就魔幻了:好好的一个 jar,è¦�先解压,然å�Žç›´æŽ¥è¿�行里é�¢çš„类,æ“�作如下: + +``` +target $ unzip -d ./tmp springboot-jar-1.0.0.jar +Archive: springboot-jar-1.0.0.jar + creating: ./tmp/META-INF/ + inflating: ./tmp/META-INF/MANIFEST.MF + creating: ./tmp/org/ + creating: ./tmp/org/springframework/ + creating: ./tmp/org/springframework/boot/ +··· çœ�略其他内容 +target $ cd tmp/ +tmp $ java org.springframework.boot.loader.JarLauncher + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 21:56:00.472 INFO 85431 --- [ main] com.gitee.funcy.maven.jar.Main : Starting Main on funcydeMacBook-Pro.local with PID 85431 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-jar/target/tmp/BOOT-INF/classes started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-jar/target/tmp) +2020-01-07 21:56:00.475 INFO 85431 --- [ main] com.gitee.funcy.maven.jar.Main : No active profile set, falling back to default profiles: default +2020-01-07 21:56:01.589 INFO 85431 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 21:56:01.619 INFO 85431 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 21:56:01.619 INFO 85431 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 21:56:01.634 INFO 85431 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 21:56:01.722 INFO 85431 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 21:56:01.722 INFO 85431 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1203 ms +2020-01-07 21:56:01.931 INFO 85431 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 21:56:02.154 INFO 85431 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 21:56:02.157 INFO 85431 --- [ main] com.gitee.funcy.maven.jar.Main : Started Main in 2.025 seconds (JVM running for 2.472) + +``` + +总结下,步骤如下: + +1. 进入项目`target/`目录 +2. 解压`jar包`到`tmp`目录:`unzip -d ./tmp springboot-jar-1.0.0.jar` +3. 进入`tmp目录`:`cd tmp/` +4. è¿�行:`java org.springframework.boot.loader.JarLauncher` + +访问`http://localhost:8080`,也能得到正确结果。 + +> 注:这ç§�神奇的å�¯åŠ¨æ–¹å¼�在什么情况下会使用呢?我曾ç»�è§�过一些项目组,为了安全会把生产的é…�置文件放在æœ�务器上,在部署项目的时候,先解压 jar 包,然å�Žæ›¿æ�¢ç›¸åº”çš„é…�置文件,å†�è¿�行。这ç§�解压 jar 包ã€�替æ�¢é…�置文件的方å¼�å°±å�¯ä»¥ç”¨æ­¤å�¯åŠ¨æ–¹å¼�了。当然,这些解压ã€�替æ�¢ã€�å�¯åŠ¨ç­‰æ“�作都会写进 shell 脚本里,自动化è¿�行。 + +### 3\. war 包å�¯åЍ + +#### 3.1`java -jar`æ–¹å¼�å�¯åЍ + +项目都打æˆ�`war包`了,还能使用`java -jar`å�¯åŠ¨ï¼Ÿæ˜¯çš„ï¼Œ`springboot`就是这么方便: + +``` +target $ java -jar springboot-war-1.0.0.war + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 22:11:54.284 INFO 85638 --- [ main] com.gitee.funcy.maven.war.Main : Starting Main on funcydeMacBook-Pro.local with PID 85638 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-war/target/springboot-war-1.0.0.war started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-war/target) +2020-01-07 22:11:54.287 INFO 85638 --- [ main] com.gitee.funcy.maven.war.Main : No active profile set, falling back to default profiles: default +2020-01-07 22:11:55.257 INFO 85638 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 22:11:55.286 INFO 85638 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 22:11:55.287 INFO 85638 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 22:11:55.299 INFO 85638 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 22:11:55.711 INFO 85638 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 22:11:55.711 INFO 85638 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1379 ms +2020-01-07 22:11:55.873 INFO 85638 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 22:11:56.031 INFO 85638 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 22:11:56.034 INFO 85638 --- [ main] com.gitee.funcy.maven.war.Main : Started Main in 2.066 seconds (JVM running for 2.469) +2020-01-07 22:12:01.189 INFO 85638 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' +2020-01-07 22:12:01.190 INFO 85638 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' +2020-01-07 22:12:01.195 INFO 85638 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms + +``` + +看,项目真的跑起æ�¥äº†ï¼� + +#### 3.2`java org.springframework.boot.loader.WarLauncher`æ–¹å¼�å�¯åЍ + +`springboot`çš„`jar包`å�¯ä»¥è§£åŽ‹ï¼Œç„¶å�Žè¿�行æŸ�个类æ�¥å�¯åŠ¨ï¼Œ`war包`竟然也有这ç§�方法ï¼�`jar包`çš„å�¯åŠ¨ç±»æ˜¯`org.springframework.boot.loader.JarLauncher`,相应的`war包`å�¯åŠ¨ç±»æ˜¯`org.springframework.boot.loader.WarLauncher`,步骤如下: + +1. 进入项目`target/`目录 +2. 解压`war包`到`tmp`目录:`unzip -d ./tmp springboot-war-1.0.0.war` +3. 进入`tmp目录`:`cd tmp/` +4. è¿�行:`java org.springframework.boot.loader.WarLauncher` + +过程如下: + +``` +target $ unzip -d ./tmp springboot-war-1.0.0.war +Archive: springboot-war-1.0.0.war + creating: ./tmp/META-INF/ + inflating: ./tmp/META-INF/MANIFEST.MF + creating: ./tmp/org/ + creating: ./tmp/org/springframework/ + creating: ./tmp/org/springframework/boot/ +··· çœ�略其他 +target $ cd tmp/ +tmp $ java org.springframework.boot.loader.WarLauncher + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 22:17:09.637 INFO 85782 --- [ main] com.gitee.funcy.maven.war.Main : Starting Main on funcydeMacBook-Pro.local with PID 85782 (/Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-war/target/tmp/WEB-INF/classes started by funcy in /Users/funcy/IdeaProjects/myproject/springboot-demo/springboot-maven/springboot-war/target/tmp) +2020-01-07 22:17:09.640 INFO 85782 --- [ main] com.gitee.funcy.maven.war.Main : No active profile set, falling back to default profiles: default +2020-01-07 22:17:10.576 INFO 85782 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2020-01-07 22:17:10.603 INFO 85782 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2020-01-07 22:17:10.604 INFO 85782 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.13 +2020-01-07 22:17:10.616 INFO 85782 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/funcy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] +2020-01-07 22:17:10.725 INFO 85782 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2020-01-07 22:17:10.725 INFO 85782 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1046 ms +2020-01-07 22:17:10.942 INFO 85782 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 22:17:11.137 INFO 85782 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2020-01-07 22:17:11.140 INFO 85782 --- [ main] com.gitee.funcy.maven.war.Main : Started Main in 1.817 seconds (JVM running for 2.183) +2020-01-07 22:17:15.024 INFO 85782 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' +2020-01-07 22:17:15.024 INFO 85782 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' +2020-01-07 22:17:15.029 INFO 85782 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®ä¹Ÿå�¯åЍæˆ�功了ï¼� + +#### 3.3 传统方å¼�å�¯åŠ¨ï¼šä½¿ç”¨ tomcat 容器 + +最åˆ�çš„`war包`就是放在 tomcat 等容器中è¿�行的,我们也æ�¥è¯•试`war包`在 tomcat 容器中è¿�行情况如何。这里说的 tomcat 容器是指在[tomcat 官网](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Ftomcat.apache.org%2F "tomcat官网")下载的容器,é�ž`springboot`内置容器。这里我下载的是`apache-tomcat-8.5.47`,过程如下: + +``` +... çœ�ç•¥tomcat日志输出 + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.1.1.RELEASE) + +2020-01-07 22:28:23.519 INFO 85904 --- [ost-startStop-1] c.g.funcy.maven.war.StartApplication : Starting StartApplication on funcydeMacBook-Pro.local with PID 85904 (/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/springboot-war-1.0.0/WEB-INF/classes started by funcy in /Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47) +2020-01-07 22:28:23.523 INFO 85904 --- [ost-startStop-1] c.g.funcy.maven.war.StartApplication : No active profile set, falling back to default profiles: default +2020-01-07 22:28:24.256 INFO 85904 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 676 ms +2020-01-07 22:28:24.655 INFO 85904 --- [ost-startStop-1] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' +2020-01-07 22:28:24.920 INFO 85904 --- [ost-startStop-1] c.g.funcy.maven.war.StartApplication : Started StartApplication in 1.86 seconds (JVM running for 3.98) +07-Jan-2020 22:28:24.974 ä¿¡æ�¯ [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫æ��用于TLD但尚未包å�«TLD。 为此记录器å�¯ç”¨è°ƒè¯•日志记录,以获å�–已扫æ��但未在其中找到TLD的完整JAR列表。 在扫æ��期间跳过ä¸�需è¦�çš„JARå�¯ä»¥ç¼©çŸ­å�¯åŠ¨æ—¶é—´å’ŒJSP编译时间。 +07-Jan-2020 22:28:24.999 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/springboot-war-1.0.0.war] has finished in [3,468] ms +07-Jan-2020 22:28:25.000 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程åº�部署到目录 [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/docs] +07-Jan-2020 22:28:25.010 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/docs] has finished in [10] ms +07-Jan-2020 22:28:25.010 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程åº�部署到目录 [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/manager] +07-Jan-2020 22:28:25.027 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/manager] has finished in [17] ms +07-Jan-2020 22:28:25.027 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程åº�部署到目录 [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/examples] +07-Jan-2020 22:28:25.181 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/examples] has finished in [154] ms +07-Jan-2020 22:28:25.181 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程åº�部署到目录 [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/ROOT] +07-Jan-2020 22:28:25.191 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/ROOT] has finished in [10] ms +07-Jan-2020 22:28:25.191 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程åº�部署到目录 [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/host-manager] +07-Jan-2020 22:28:25.202 ä¿¡æ�¯ [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/funcy/Applications/Tomcat/apache-tomcat-8.5.47/webapps/host-manager] has finished in [11] ms +07-Jan-2020 22:28:25.206 ä¿¡æ�¯ [main] org.apache.coyote.AbstractProtocol.start 开始å��议处ç�†å�¥æŸ„["http-nio-8080"] +07-Jan-2020 22:28:25.212 ä¿¡æ�¯ [main] org.apache.coyote.AbstractProtocol.start 开始å��议处ç�†å�¥æŸ„["ajp-nio-8009"] +07-Jan-2020 22:28:25.213 ä¿¡æ�¯ [main] org.apache.catalina.startup.Catalina.start Server startup in 3717 ms +2020-01-07 22:29:30.754 INFO 85904 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' +2020-01-07 22:29:30.767 INFO 85904 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms + +``` + +请求`http://localhost:8080/springboot-war-1.0.0/`,结果如下: + +``` +$ curl 'http://localhost:8080/springboot-war-1.0.0/' +hello world + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œå·²ç»�部署æˆ�功了。 + +### 4\. 总结 + +| | main () 方法 | mvn 命令 | java -jar | java xxx.WarLauncher | java xxx.JarLauncher | 外置容器 | +| --- | --- | --- | --- | --- | --- | --- | +| war | 支æŒ� | 支æŒ� | 支æŒ� | 支æŒ� | ä¸�支æŒ� | 支æŒ� | +| jar | 支æŒ� | 支æŒ� | 支æŒ� | ä¸�支æŒ� | 支æŒ� | ä¸�支æŒ� + +### 1\. maven 打包å�Žçš„æ–‡ä»¶ + +进入`springboot-jar/target`目录,使用`tree`命令,目录结构如下: + +``` + $ tree +. +├── classes +│ └── com +│ └── gitee +│ └── funcy +│ └── maven +│ └── jar +│ ├── Main.class +│ └── controller +│ └── IndexController.class +├── generated-sources +│ └── annotations +├── maven-archiver +│ └── pom.properties +├── maven-status +│ └── maven-compiler-plugin +│ └── compile +│ └── default-compile +│ ├── createdFiles.lst +│ └── inputFiles.lst +├── springboot-jar-1.0.0.jar +└── springboot-jar-1.0.0.jar.original + +14 directories, 7 files + +``` + +注æ„�`springboot-jar-1.0.0.jar`与`springboot-jar-1.0.0.jar.original`的区别:`springboot-jar-1.0.0.jar.original`属于原始 Maven 打包 jar 文件,该文件仅包å�«åº”用本地资æº�,如编译å�Žçš„ classes 目录下的资æº�文件等,未引入第三方ä¾�赖资æº�;而`springboot-jar-1.0.0.jar`引入了第三方ä¾�赖资æº�(主è¦�为 jar 包)。 + +使用`unzip springboot-jar-1.0.0.jar -d tmp`解压 jar 包,内容如下: + +``` + $ tree tmp/ +tmp/ +├── BOOT-INF +│ ├── classes +│ │ └── com +│ │ └── gitee +│ │ └── funcy +│ │ └── maven +│ │ └── jar +│ │ ├── Main.class +│ │ └── controller +│ │ └── IndexController.class +│ └── lib +│ ├── classmate-1.4.0.jar +│ ├── hibernate-validator-6.0.13.Final.jar +│ ├── jackson-annotations-2.9.0.jar +│ ├── jackson-core-2.9.7.jar +│ ├── jackson-databind-2.9.7.jar +│ ├── jackson-datatype-jdk8-2.9.7.jar +│ ├── jackson-datatype-jsr310-2.9.7.jar +│ ├── jackson-module-parameter-names-2.9.7.jar +│ ├── javax.annotation-api-1.3.2.jar +│ ├── jboss-logging-3.3.2.Final.jar +│ ├── jul-to-slf4j-1.7.25.jar +│ ├── log4j-api-2.11.1.jar +│ ├── log4j-to-slf4j-2.11.1.jar +│ ├── logback-classic-1.2.3.jar +│ ├── logback-core-1.2.3.jar +│ ├── slf4j-api-1.7.25.jar +│ ├── snakeyaml-1.23.jar +│ ├── spring-aop-5.1.3.RELEASE.jar +│ ├── spring-beans-5.1.3.RELEASE.jar +│ ├── spring-boot-2.1.1.RELEASE.jar +│ ├── spring-boot-autoconfigure-2.1.1.RELEASE.jar +│ ├── spring-boot-starter-2.1.1.RELEASE.jar +│ ├── spring-boot-starter-json-2.1.1.RELEASE.jar +│ ├── spring-boot-starter-logging-2.1.1.RELEASE.jar +│ ├── spring-boot-starter-tomcat-2.1.1.RELEASE.jar +│ ├── spring-boot-starter-web-2.1.1.RELEASE.jar +│ ├── spring-context-5.1.3.RELEASE.jar +│ ├── spring-core-5.1.3.RELEASE.jar +│ ├── spring-expression-5.1.3.RELEASE.jar +│ ├── spring-jcl-5.1.3.RELEASE.jar +│ ├── spring-web-5.1.3.RELEASE.jar +│ ├── spring-webmvc-5.1.3.RELEASE.jar +│ ├── tomcat-embed-core-9.0.13.jar +│ ├── tomcat-embed-el-9.0.13.jar +│ ├── tomcat-embed-websocket-9.0.13.jar +│ └── validation-api-2.0.1.Final.jar +├── META-INF +│ ├── MANIFEST.MF +│ └── maven +│ └── com.gitee.funcy +│ └── springboot-jar +│ ├── pom.properties +│ └── pom.xml +└── org + └── springframework + └── boot + └── loader + ├── ExecutableArchiveLauncher.class + ├── JarLauncher.class + ├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class + ├── LaunchedURLClassLoader.class + ├── Launcher.class + ├── MainMethodRunner.class + ├── PropertiesLauncher$1.class + ├── PropertiesLauncher$ArchiveEntryFilter.class + ├── PropertiesLauncher$PrefixMatchingArchiveFilter.class + ├── PropertiesLauncher.class + ├── WarLauncher.class + ├── archive + │ ├── Archive$Entry.class + │ ├── Archive$EntryFilter.class + │ ├── Archive.class + │ ├── ExplodedArchive$1.class + │ ├── ExplodedArchive$FileEntry.class + │ ├── ExplodedArchive$FileEntryIterator$EntryComparator.class + │ ├── ExplodedArchive$FileEntryIterator.class + │ ├── ExplodedArchive.class + │ ├── JarFileArchive$EntryIterator.class + │ ├── JarFileArchive$JarFileEntry.class + │ └── JarFileArchive.class + ├── data + │ ├── RandomAccessData.class + │ ├── RandomAccessDataFile$1.class + │ ├── RandomAccessDataFile$DataInputStream.class + │ ├── RandomAccessDataFile$FileAccess.class + │ └── RandomAccessDataFile.class + ├── jar + │ ├── AsciiBytes.class + │ ├── Bytes.class + │ ├── CentralDirectoryEndRecord.class + │ ├── CentralDirectoryFileHeader.class + │ ├── CentralDirectoryParser.class + │ ├── CentralDirectoryVisitor.class + │ ├── FileHeader.class + │ ├── Handler.class + │ ├── JarEntry.class + │ ├── JarEntryFilter.class + │ ├── JarFile$1.class + │ ├── JarFile$2.class + │ ├── JarFile$JarFileType.class + │ ├── JarFile.class + │ ├── JarFileEntries$1.class + │ ├── JarFileEntries$EntryIterator.class + │ ├── JarFileEntries.class + │ ├── JarURLConnection$1.class + │ ├── JarURLConnection$JarEntryName.class + │ ├── JarURLConnection.class + │ ├── StringSequence.class + │ └── ZipInflaterInputStream.class + └── util + └── SystemPropertyUtils.class + +21 directories, 91 files + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œæ–‡ä»¶ä¸­ä¸»è¦�分为如下几个目录: + +* `BOOT-INF/classes`目录存放应用编译å�Žçš„ class 文件; +* `BOOT-INF/lib`目录存放应用ä¾�èµ–çš„ jar 包; +* `META-INF/`目录存放应用ä¾�èµ–çš„ jar 包; +* `org/`目录存放 spring boot 相关的 class 文件。 + +### 2.`java -jar`å�¯åЍ springboot jar 包 + +java 官方规定,`java -jar`命令引导的具体å�¯åŠ¨ç±»å¿…é¡»é…�置在`MANIFEST.MF`文件中,而根æ�®`jar文件规范`,`MANIFEST.MF`文件必须存放在`/META-INF/`目录下。因此,å�¯åŠ¨ç±»é…�置在 jar 包的`/META-INF/MANIFEST.MF`文件中,查看该文件,内容如下: + +``` +$ cat MANIFEST.MF +Manifest-Version: 1.0 +Archiver-Version: Plexus Archiver +Built-By: fangchengyan +Start-Class: com.gitee.funcy.maven.jar.Main +Spring-Boot-Classes: BOOT-INF/classes/ +Spring-Boot-Lib: BOOT-INF/lib/ +Spring-Boot-Version: 2.1.1.RELEASE +Created-By: Apache Maven 3.6.0 +Build-Jdk: 1.8.0_222 +Main-Class: org.springframework.boot.loader.JarLauncher + +``` + +å�‘现`Main-Class`属性指å�‘çš„`Class`为`org.springframework.boot.loader.JarLauncher`,而该类存放在 jar 包的`org/springframework/boot/loader/`目录下,并且项目的引导类定义在`Start-Class`属性性中,该属性并é�ž java å¹³å�°æ ‡å‡†`META-INF/MANIFEST.MF`属性。 + +> 注: +> +> 1. `org.springframework.boot.loader.JarLauncher`是å�¯æ‰§è¡Œ jar çš„å�¯åŠ¨å™¨ï¼Œ`org.springframework.boot.loader.WarLauncher`是å�¯æ‰§è¡Œ war çš„å�¯åŠ¨å™¨ã€‚ +> +> +> 2. `org.springframework.boot.loader.JarLauncher`所在的 jar 文件的 Maven GAV ä¿¡æ�¯ä¸º`org.springframework.boot:spring-boot-loader:${springboot-version}`,通常情况下,这个ä¾�赖没有必è¦�引入 springboot 项目的 pom.xml 文件。 + +查看`JarLauncher`æº�ç �,如下: + +``` +public class JarLauncher extends ExecutableArchiveLauncher { + + static final String BOOT_INF_CLASSES = "BOOT-INF/classes/"; + + static final String BOOT_INF_LIB = "BOOT-INF/lib/"; + + public JarLauncher() { + } + + protected JarLauncher(Archive archive) { + super(archive); + } + + @Override + protected boolean isNestedArchive(Archive.Entry entry) { + if (entry.isDirectory()) { + return entry.getName().equals(BOOT_INF_CLASSES); + } + return entry.getName().startsWith(BOOT_INF_LIB); + } + + public static void main(String[] args) throws Exception { + new JarLauncher().launch(args); + } + +} + +``` + +å�¯ä»¥å�‘现,`BOOT-INF/classes/`与`BOOT-INF/lib/`分别使用常é‡�`BOOT_INF_CLASSES`å’Œ`BOOT_INF_LIB`表示,并且用于`isNestedArchive(Archive.Entry)`方法判断,从该方法的实现分æž�,方法å�‚æ•°`Archive.Entry`看似为 jar 文件中的资æº�,比如`application.properties`。 + +`Archive.Entry`有两ç§�实现,其中一ç§�为`org.springframework.boot.loader.archive.JarFileArchive.JarFileEntry`,基于`java.util.jar.JarEntry`,表示`FAT JAR`嵌入资æº�,å�¦ä¸€ç§�为`org.springframework.boot.loader.archive.ExplodedArchive.FileEntry`,基于文件系统实现。这也说明了`JarLauncher`支æŒ�`JAR`å’Œ`文件系统`两ç§�å�¯åŠ¨æ–¹å¼�。 + +> 文件系统å�¯åŠ¨æ–¹å¼�如下: +> +> 1. 解压 jar 包到`temp`目录:`unzip springboot-jar-1.0.0.jar -d tmp` +> 2. 进入`temp`目录,è¿�行命令:`java org.springframework.boot.loader.JarLauncher`å�¯ä»¥çœ‹åˆ°ï¼Œé¡¹ç›®å�Œæ ·èƒ½æ­£å¸¸å�¯åŠ¨ã€‚ + +在`JarLauncher`作为引导类时,当执行`java -jar`命令时,`/META-INF`资æº�çš„`Main-Class`属性将调用其`main(String[])`方法,实际上调用的是`JarLauncher#launch(args)`方法,而该方法继承于基类`org.springframework.boot.loader.Launcher`,它们之间的继承关系如下: + +* `org.springframework.boot.loader.Launcher` + * `org.springframework.boot.loader.ExecutableArchiveLauncher` + * `org.springframework.boot.loader.JarLauncher` + * `org.springframework.boot.loader.WarLauncher` + +简å�•æ�¥è¯´ï¼Œspringboot jar å�¯åŠ¨è¿‡ç¨‹å¦‚ä¸‹ï¼š + +1. `java -jar xxx.jar`è¿�行的是`JarLauncher` +2. `JarLauncher#main(String[])`方法会调用`Launcher#launch(String[])`方法,创建 ClassLoader () å�Šè°ƒç”¨é¡¹ç›®çš„`main`方法 + * 项目主类的获å�–实现ä½�于`ExecutableArchiveLauncher#getMainClass()`,主è¦�是从`/META-INF/MANIFEST.MF`获å�–`Start-Class`属性 + * 项目主类的 main () 方法调用ä½�于`MainMethodRunner#run()`,使用å��å°„æ–¹å¼�进行调用 + +### 3.`java -jar`å�¯åЍ springboot war 包 + +从上é�¢çš„分æž�,我们得到了å�¯åЍ jar 包的`org.springframework.boot.loader.JarLauncher`以å�Šå�¯åЍ war 包的`org.springframework.boot.loader.WarLauncher`,这里我们æ�¥åˆ†æž�下`WarLauncher`上如何工作的。 + +`WarLauncher`代ç �如下: + +``` +public class WarLauncher extends ExecutableArchiveLauncher { + + private static final String WEB_INF = "WEB-INF/"; + + private static final String WEB_INF_CLASSES = WEB_INF + "classes/"; + + private static final String WEB_INF_LIB = WEB_INF + "lib/"; + + private static final String WEB_INF_LIB_PROVIDED = WEB_INF + "lib-provided/"; + + public WarLauncher() { + } + + protected WarLauncher(Archive archive) { + super(archive); + } + + @Override + public boolean isNestedArchive(Archive.Entry entry) { + if (entry.isDirectory()) { + return entry.getName().equals(WEB_INF_CLASSES); + } + else { + return entry.getName().startsWith(WEB_INF_LIB) + || entry.getName().startsWith(WEB_INF_LIB_PROVIDED); + } + } + + public static void main(String[] args) throws Exception { + new WarLauncher().launch(args); + } + +} + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œ`WEB-INF/classes/`ã€�`WEB-INF/lib/`ã€�`WEB-INF/lib-provided/`å�‡ä¸º`WarLauncher`çš„`Class Path`,其中`WEB-INF/classes/`ã€�`WEB-INF/lib/`是传统的 Servlet 应用的 ClassPath 路径,而`WEB-INF/lib-provided/`属性 springboot`WarLauncher`定制实现。那么`WEB-INF/lib-provided/`究竟是干嘛的呢?看到`provided`,我们å�¯ä»¥å¤§èƒ†çŒœæƒ³`WEB-INF/lib-provided/`存放的是`pom.xml`文件中,`scope`为`provided`çš„ jar。 + +为了验è¯�以上猜想,修改的 pom.xml 文件如下: + +``` + + + 4.0.0 + + com.gitee.funcy + springboot-war + 1.0.0 + + war + springbooté�žparent war打包方å¼� + + + UTF-8 + UTF-8 + 1.8 + 2.1.1.RELEASE + 3.8.1 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-test + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-war-plugin + + 3.2.2 + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + +``` + +这里我们添加了 springboot 的测试 jar`org.springframework.boot:spring-boot-test`,并将其`scope`设置为`provided`. è¿�行 maven 打包命令`mvn clean install -Dmaven.test.skip=true`,å�¯ä»¥çœ‹åˆ°é¡¹ç›®èƒ½æ­£å¸¸æ‰“包。 + +打包完æˆ�å�Žï¼Œè¿›å…¥`target`目录,è¿�行`java -jar springboot-war-1.0.0.war`,项目能正常å�¯åŠ¨ã€‚ + +接下æ�¥ï¼Œæˆ‘们æ�¥çœ‹çœ‹`springboot-war-1.0.0.war`有些啥。首先使用`unzip springboot-war-1.0.0.war -d tmp`命令解压,å†�使用`tree -h`命令查看文件结构,结果如下 + +``` + $ tree -h +. +├── [ 128] META-INF +│ ├── [ 311] MANIFEST.MF +│ └── [ 96] maven +│ └── [ 96] com.gitee.funcy +│ └── [ 128] springboot-war +│ ├── [ 95] pom.properties +│ └── [3.3K] pom.xml +├── [ 160] WEB-INF +│ ├── [ 96] classes +│ │ └── [ 96] com +│ │ └── [ 96] gitee +│ │ └── [ 96] funcy +│ │ └── [ 96] maven +│ │ └── [ 160] war +│ │ ├── [ 688] Main.class +│ │ ├── [ 891] StartApplication.class +│ │ └── [ 96] controller +│ │ └── [ 646] IndexController.class +│ ├── [1.2K] lib +│ │ ├── [ 65K] classmate-1.4.0.jar +│ │ ├── [1.1M] hibernate-validator-6.0.13.Final.jar +│ │ ├── [ 65K] jackson-annotations-2.9.0.jar +│ │ ├── [316K] jackson-core-2.9.7.jar +│ │ ├── [1.3M] jackson-databind-2.9.7.jar +│ │ ├── [ 33K] jackson-datatype-jdk8-2.9.7.jar +│ │ ├── [ 98K] jackson-datatype-jsr310-2.9.7.jar +│ │ ├── [8.4K] jackson-module-parameter-names-2.9.7.jar +│ │ ├── [ 26K] javax.annotation-api-1.3.2.jar +│ │ ├── [ 65K] jboss-logging-3.3.2.Final.jar +│ │ ├── [4.5K] jul-to-slf4j-1.7.25.jar +│ │ ├── [258K] log4j-api-2.11.1.jar +│ │ ├── [ 17K] log4j-to-slf4j-2.11.1.jar +│ │ ├── [284K] logback-classic-1.2.3.jar +│ │ ├── [461K] logback-core-1.2.3.jar +│ │ ├── [ 40K] slf4j-api-1.7.25.jar +│ │ ├── [294K] snakeyaml-1.23.jar +│ │ ├── [360K] spring-aop-5.1.3.RELEASE.jar +│ │ ├── [656K] spring-beans-5.1.3.RELEASE.jar +│ │ ├── [935K] spring-boot-2.1.1.RELEASE.jar +│ │ ├── [1.2M] spring-boot-autoconfigure-2.1.1.RELEASE.jar +│ │ ├── [ 413] spring-boot-starter-2.1.1.RELEASE.jar +│ │ ├── [ 421] spring-boot-starter-json-2.1.1.RELEASE.jar +│ │ ├── [ 423] spring-boot-starter-logging-2.1.1.RELEASE.jar +│ │ ├── [ 422] spring-boot-starter-tomcat-2.1.1.RELEASE.jar +│ │ ├── [ 421] spring-boot-starter-web-2.1.1.RELEASE.jar +│ │ ├── [1.0M] spring-context-5.1.3.RELEASE.jar +│ │ ├── [1.2M] spring-core-5.1.3.RELEASE.jar +│ │ ├── [274K] spring-expression-5.1.3.RELEASE.jar +│ │ ├── [ 23K] spring-jcl-5.1.3.RELEASE.jar +│ │ ├── [1.3M] spring-web-5.1.3.RELEASE.jar +│ │ ├── [782K] spring-webmvc-5.1.3.RELEASE.jar +│ │ ├── [3.1M] tomcat-embed-core-9.0.13.jar +│ │ ├── [244K] tomcat-embed-el-9.0.13.jar +│ │ ├── [257K] tomcat-embed-websocket-9.0.13.jar +│ │ └── [ 91K] validation-api-2.0.1.Final.jar +│ └── [ 96] lib-provided +│ └── [194K] spring-boot-test-2.1.1.RELEASE.jar +└── [ 96] org + └── [ 96] springframework + └── [ 96] boot + └── [ 544] loader + ├── [3.5K] ExecutableArchiveLauncher.class + ├── [1.5K] JarLauncher.class + ├── [1.5K] LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class + ├── [5.6K] LaunchedURLClassLoader.class + ├── [4.6K] Launcher.class + ├── [1.5K] MainMethodRunner.class + ├── [ 266] PropertiesLauncher$1.class + ├── [1.4K] PropertiesLauncher$ArchiveEntryFilter.class + ├── [1.9K] PropertiesLauncher$PrefixMatchingArchiveFilter.class + ├── [ 19K] PropertiesLauncher.class + ├── [1.7K] WarLauncher.class + ├── [ 416] archive + │ ├── [ 302] Archive$Entry.class + │ ├── [ 437] Archive$EntryFilter.class + │ ├── [ 945] Archive.class + │ ├── [ 273] ExplodedArchive$1.class + │ ├── [1.1K] ExplodedArchive$FileEntry.class + │ ├── [1.5K] ExplodedArchive$FileEntryIterator$EntryComparator.class + │ ├── [3.7K] ExplodedArchive$FileEntryIterator.class + │ ├── [5.1K] ExplodedArchive.class + │ ├── [1.7K] JarFileArchive$EntryIterator.class + │ ├── [1.1K] JarFileArchive$JarFileEntry.class + │ └── [7.2K] JarFileArchive.class + ├── [ 224] data + │ ├── [ 485] RandomAccessData.class + │ ├── [ 282] RandomAccessDataFile$1.class + │ ├── [2.6K] RandomAccessDataFile$DataInputStream.class + │ ├── [3.2K] RandomAccessDataFile$FileAccess.class + │ └── [3.9K] RandomAccessDataFile.class + ├── [ 768] jar + │ ├── [4.9K] AsciiBytes.class + │ ├── [ 616] Bytes.class + │ ├── [3.0K] CentralDirectoryEndRecord.class + │ ├── [5.1K] CentralDirectoryFileHeader.class + │ ├── [4.5K] CentralDirectoryParser.class + │ ├── [ 540] CentralDirectoryVisitor.class + │ ├── [ 345] FileHeader.class + │ ├── [ 12K] Handler.class + │ ├── [3.5K] JarEntry.class + │ ├── [ 299] JarEntryFilter.class + │ ├── [2.0K] JarFile$1.class + │ ├── [1.2K] JarFile$2.class + │ ├── [1.3K] JarFile$JarFileType.class + │ ├── [ 15K] JarFile.class + │ ├── [1.6K] JarFileEntries$1.class + │ ├── [2.0K] JarFileEntries$EntryIterator.class + │ ├── [ 14K] JarFileEntries.class + │ ├── [ 702] JarURLConnection$1.class + │ ├── [4.2K] JarURLConnection$JarEntryName.class + │ ├── [9.6K] JarURLConnection.class + │ ├── [3.5K] StringSequence.class + │ └── [1.8K] ZipInflaterInputStream.class + └── [ 96] util + └── [5.1K] SystemPropertyUtils.class + +22 directories, 93 files + +``` + +相比于`FAT JAR`的解压目录,`War`增加了`WEB-INF/lib-provided`,并且该目录仅有一个 jar 文件,å�³`spring-boot-test-2.1.1.RELEASE.jar`,这正是我们在 pom.xml 文件中设置的`scope`为`provided`çš„ jar 包。 + +由此å�¯ä»¥å¾—出结论:**`WEB-INF/lib-provided`存放的是`scope`为`provided`çš„ jar 包**。 + +我们现æ�¥çœ‹ä¸‹`META-INF/MANIFEST.MF`的内容: + +``` +$ cat META-INF/MANIFEST.MF +Manifest-Version: 1.0 +Built-By: fangchengyan +Start-Class: com.gitee.funcy.maven.war.Main +Spring-Boot-Classes: WEB-INF/classes/ +Spring-Boot-Lib: WEB-INF/lib/ +Spring-Boot-Version: 2.1.1.RELEASE +Created-By: Apache Maven 3.6.0 +Build-Jdk: 1.8.0_222 +Main-Class: org.springframework.boot.loader.WarLauncher + +``` + +å�¯ä»¥çœ‹åˆ°ï¼Œè¯¥æ–‡ä»¶ä¸Ž jar 包中的`META-INF/MANIFEST.MF`很相似,在文件中å�Œæ ·å®šä¹‰äº†`Main-Class`与`Start-Class`,这也说明了该 war å�¯ä»¥ä½¿ç”¨`java -jar xxx.jar`å’Œ`java org.springframework.boot.loader.WarLauncher`å�¯åŠ¨ï¼Œè¿™ä¹Ÿä¸Žæˆ‘ä»¬çš„éªŒè¯�结果一致。 + +### 4\. tomcat 等外部容器å�¯åЍ war 包 + +在 springboo 刚开始推广的时候,我们还是习惯于将项目打æˆ� war 包,然å�Žéƒ¨ç½²åˆ° tomcat ç­‰ web 容器中è¿�行。那 springboot çš„ war 包是如何å�šåˆ°æ—¢èƒ½ç”¨ java 命令å�¯åŠ¨ï¼Œå�ˆèƒ½æ”¾åœ¨ tomcat 容器中å�¯åŠ¨å‘¢ï¼Ÿè¿™å°±æ˜¯ä¹‹å‰�æ��到的`WEB-INF/lib-provided`目录的功能了。 + +传统的`servlet`应用的`class path`路径仅关注`WEB-INF/classes/`å’Œ`WEB-INF/lib/`,`WEB-INF/lib-provided/`目录下的 jar 包将被`servlet`容器忽略,如`servlet api`,该 api ç”±`servlet`容器æ��供。我们在打包时,å�¯ä»¥æŠŠ`servlet`相关 jar 包的`scope`设置æˆ�`provided`,这样就完美实现了`servlet`容器å�¯åŠ¨ä¸Ž`java`命令å�¯åŠ¨çš„å…¼å®¹ï¼š + +* 当部署到`servlet`容器中时,`WEB-INF/lib-provided/`目录下的 jar 包就被容器忽略了(由于`servlet`容器本身就æ��供了`servlet`的相关 jar 包,如果ä¸�忽略,就会出现 jar 包é‡�å¤�引入问题); +* 当使用`java`命令执行时,此时无`servlet`容器æ��ä¾›`servlet`的相关 jar 包,而`WarLauncher`在è¿�行过程中会加载`WEB-INF/lib-provided/`目录下的 jar 包。 \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\224\237\344\272\247\347\216\257\345\242\203\345\267\245\345\205\267Actuator.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\224\237\344\272\247\347\216\257\345\242\203\345\267\245\345\205\267Actuator.md" new file mode 100644 index 0000000..e0eeb3e --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\224\237\344\272\247\347\216\257\345\242\203\345\267\245\345\205\267Actuator.md" @@ -0,0 +1,5832 @@ + + +# Éú²ú¾ÍÐ÷¹¦ÄÜ + +[Back to index](https://springdoc.cn/spring-boot/index.html) + +* [1\. ÆôÓÃÉú²ú¾ÍÐ÷µÄ¹¦ÄÜ](https://springdoc.cn/spring-boot/actuator.html#actuator.enabling) +* [2\. ¶Ëµã£¨Endpoint£©](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints) +* [3\. ͨ¹ýHTTP½øÐÐ¼à¿ØºÍ¹ÜÀí](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring) +* [4\. ͨ¹ýJMX½øÐÐ¼à¿ØºÍ¹ÜÀí](https://springdoc.cn/spring-boot/actuator.html#actuator.jmx) +* [5\. ¿É¹Û²âÐÔ£¨Observability£©](https://springdoc.cn/spring-boot/actuator.html#actuator.observability) +* [6\. ÈÕÖ¾¼Ç¼Æ÷£¨Logger£©](https://springdoc.cn/spring-boot/actuator.html#actuator.loggers) +* [7\. Ö¸±ê£¨Metrics£©](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics) +* [8\. ×·×Ù£¨Tracing£©](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing) +* [9\. Éó¼Æ](https://springdoc.cn/spring-boot/actuator.html#actuator.auditing) +* [10\. ¼Ç¼ HTTP Exchange](https://springdoc.cn/spring-boot/actuator.html#actuator.http-exchanges) +* [11\. ½ø³Ì¼à¿Ø](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring) +* [12\. Cloud Foundry µÄÖ§³Ö](https://springdoc.cn/spring-boot/actuator.html#actuator.cloud-foundry) +* [13\. ½ÓÏÂÀ´¶Áʲô](https://springdoc.cn/spring-boot/actuator.html#actuator.whats-next) + + + + + + + + + + + + + +| | ±¾Õ¾([springdoc.cn](https://springdoc.cn/))ÖеÄÄÚÈÝÀ´Ô´ÓÚ [spring.io](https://spring.io/) £¬Ô­Ê¼°æÈ¨¹éÊôÓÚ [spring.io](https://spring.io/)¡£ÓÉ [springboot.io - Spring BootÖÐÎÄÉçÇø](https://springboot.io/) ½øÐз­Ò룬ÕûÀí¡£¿É¹©¸öÈËѧϰ¡¢Ñо¿£¬Î´¾­Ðí¿É£¬²»µÃ½øÐÐÈκÎ×ªÔØ¡¢ÉÌÓûòÓëÖ®Ïà¹ØµÄÐÐΪ¡£ É̱êÉùÃ÷£ºSpring ÊÇ Pivotal Software, Inc. ÔÚÃÀ¹úÒÔ¼°ÆäËû¹ú¼ÒµÄÉ̱ꡣ | +| --- | --- | + + + + + +Spring Boot°üÀ¨Ò»Ð©¶îÍâµÄ¹¦ÄÜ£¬ÒÔ°ïÖúÄãÔÚ½«Ó¦ÓóÌÐò·¢²¼µ½Éú²úʱ¼à¿ØºÍ¹ÜÀíÄãµÄÓ¦ÓóÌÐò¡£ Äã¿ÉÒÔÑ¡Ôñͨ¹ýʹÓÃHTTP¶Ëµã»òʹÓÃJMXÀ´¹ÜÀíºÍ¼à¿ØÄãµÄÓ¦ÓóÌÐò¡£ É󼯡¢½¡¿µºÍÖ¸±êÊÕ¼¯Ò²¿ÉÒÔ×Ô¶¯Ó¦ÓÃÓÚÄãµÄÓ¦ÓóÌÐò¡£ + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.enabling)1\. ÆôÓÃÉú²ú¾ÍÐ÷µÄ¹¦ÄÜ + + + + + +[`spring-boot-actuator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator) Ä£¿éÌṩÁËËùÓÐSpring BootµÄÉú²ú¾ÍÐ÷¹¦ÄÜ¡£ ÆôÓÃÕâЩ¹¦ÄܵÄÍÆ¼ö·½·¨ÊÇÌí¼Ó¶Ô `spring-boot-starter-actuator` ¡°Starter¡± µÄÒÀÀµ¡£ + + + + + + + +ActuatorµÄ¶¨Òå + + + +actuator£¨Ö´ÐÐÆ÷£© ÊÇÒ»¸öÖÆÔìÊõÓָµÄÊÇÓÃÓÚÒÆ¶¯»ò¿ØÖÆÄ³ÎïµÄ»úеװÖá£actuator ¿ÉÒÔ´ÓÒ»¸öСµÄ±ä»¯ÖвúÉú´óÁ¿µÄÔ˶¯¡£ + + + + + + + + + +ÒªÔÚ»ùÓÚMavenµÄÏîÄ¿ÖÐÌí¼Óactuator£¬ÇëÌí¼ÓÒÔÏ ¡°Starter¡± ÒÀÀµ¡£ + + + + + + + +``` + + + org.springframework.boot + spring-boot-starter-actuator + + +``` + + + + + + + +¶ÔÓÚGradle£¬Ê¹ÓÃÒÔÏÂÉùÃ÷¡£ + + + + + + + +``` +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' +} +``` + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints)2\. ¶Ëµã£¨Endpoint£© + + + + + +Actuator ¶Ëµã£¨endpoint£©ÈÃÄã¿ÉÒÔ¼à¿Ø²¢ÓëÄãµÄÓ¦ÓóÌÐò»¥¶¯¡£ Spring Boot°üÀ¨Ò»Ð©ÄÚÖõĶ˵㣬²¢ÔÊÐíÄãÌí¼Ó×Ô¼ºµÄ¶Ëµã¡£ ÀýÈ磬`health` ¶ËµãÌṩ»ù±¾µÄÓ¦ÓóÌÐò½¡¿µÐÅÏ¢¡£ + + + + + +Äã¿ÉÒÔ[ÆôÓûò½ûÓÃ](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.enabling)ÿ¸öµ¥¶ÀµÄ¶Ëµã£¬²¢[ͨ¹ýHTTP»òJMX¹«¿ªËüÃÇ£¨Ê¹ËüÃÇ¿ÉÒÔÔ¶³Ì·ÃÎÊ£©](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.exposing)¡£µ±Ò»¸ö¶Ëµã±»ÆôÓúͱ©Â¶Ê±£¬Ëü±»ÈÏΪÊÇ¿ÉÓõġ£ÄÚÖõĶ˵ãÖ»ÓÐÔÚËüÃÇ¿ÉÓÃʱ²Å»á±»×Ô¶¯ÅäÖᣴó¶àÊýÓ¦ÓóÌÐòÑ¡Ôñͨ¹ýHTTP±©Â¶£¬ÆäÖж˵ãµÄIDºÍ `/actuator` µÄǰ׺±»Ó³Éäµ½Ò»¸öURL¡£ÀýÈ磬ĬÈÏÇé¿öÏ£¬`health` ¶Ëµã±»Ó³Éäµ½ `/actuator/health`¡£ + + + + + +| | ÒªÁ˽â¸ü¶à¹ØÓÚactuatorµÄ¶ËµãÒÔ¼°ËüÃǵÄÇëÇóºÍÏìÓ¦¸ñʽ£¬Çë¿´µ¥¶ÀµÄAPIÎĵµ£¨ [HTML](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/htmlsingle) »ò [PDF](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/pdf/spring-boot-actuator-web-api.pdf)£©¡£ | +| --- | --- | + + + + + +ÒÔÏÂÊǼ¼ÊõÎ޹صÄÖÕ¶Ë¡£ + + + + +| ID | ˵Ã÷ | +| --- | --- | +| `auditevents` | ¹«¿ªµ±Ç°Ó¦ÓóÌÐòµÄÉó¼ÆÊ¼þÐÅÏ¢¡£ ÐèÒªÒ»¸ö `AuditEventRepository` bean¡£ | +| `beans` | ÏÔʾÄãµÄÓ¦ÓóÌÐòÖÐËùÓÐSpring BeanµÄÍêÕûÁÐ±í¡£ | +| `caches` | ÏÔʾ¿ÉÓõĻº´æ¡£ | +| `conditions` | ÏÔʾ¶ÔÅäÖúÍ×Ô¶¯ÅäÖÃÀà½øÐÐÆÀ¹ÀµÄÌõ¼þ£¬ÒÔ¼°ËüÃÇ·ûºÏ»ò²»·ûºÏµÄÔ­Òò¡£ | +| `configprops` | ÏÔʾËùÓÐ `@ConfigurationProperties` µÄÕûÀíÁÐ±í¡£ | +| `env` | ±©Â¶SpringµÄ `ConfigurableEnvironment` ÖеÄÊôÐÔ¡£ | +| `flyway` | ÏÔʾÈκÎÒѾ­Ó¦ÓõÄFlywayÊý¾Ý¿âÇ¨ÒÆ¡£ ÐèÒªÒ»¸ö»ò¶à¸ö `Flyway` bean¡£ | +| `health` | ÏÔʾӦÓóÌÐòµÄ½¡¿µÐÅÏ¢¡£ | +| `httpexchanges` | ÏÔʾ HTTP exchange ÐÅÏ¢£¨Ä¬ÈÏÇé¿öÏ£¬×îºó 100 ¸ö HTTP request/response exchange£©¡£ ÐèÒªÒ»¸ö `HttpExchangeRepository` bean¡£ | +| `info` | ÏÔʾÈÎÒâµÄÓ¦ÓóÌÐòÐÅÏ¢¡£ | +| `integrationgraph` | ÏÔʾSpring¼¯³Éͼ¡£ ÐèÒªÒÀÀµ `spring-integration-core`¡£ | +| `loggers` | ÏÔʾºÍÐÞ¸ÄÓ¦ÓóÌÐòÖÐloggerµÄÅäÖᣠ| +| `liquibase` | ÏÔʾÈκÎÒѾ­Ó¦ÓõÄLiquibaseÊý¾Ý¿âÇ¨ÒÆ¡£ ÐèÒªÒ»¸ö»ò¶à¸ö `Liquibase` Bean¡£ | +| `metrics` | ÏÔʾµ±Ç°Ó¦ÓóÌÐòµÄ ¡°metrics¡± ÐÅÏ¢¡£ | +| `mappings` | ÏÔʾËùÓÐ `@RequestMapping` ·¾¶µÄÕûÀíÁÐ±í¡£ | +| `quartz` | ÏÔʾÓйØQuartz Scheduler JobµÄÐÅÏ¢¡£ | +| `scheduledtasks` | ÏÔʾÄãµÄÓ¦ÓóÌÐòÖеļƻ®ÈÎÎñ¡£ | +| `sessions` | ÔÊÐí´ÓSpring SessionÖ§³ÖµÄ»á»°´æ´¢ÖмìË÷ºÍɾ³ýÓû§»á»°¡£ ÐèÒªÒ»¸öʹÓÃSpring SessionµÄ»ùÓÚServletµÄWebÓ¦ÓóÌÐò¡£ | +| `shutdown` | ÈÃÓ¦ÓóÌÐòÓÅÑŵعرա£Ö»ÔÚʹÓÃjar´ò°üʱÓÐЧ¡£Ä¬ÈÏÇé¿öÏÂÊǽûÓõġ£ | +| `startup` | ÏÔʾÓÉ `ApplicationStartup` ÊÕ¼¯µÄ[Æô¶¯²½ÖèÊý¾Ý](https://springdoc.cn/spring-boot/features.html#features.spring-application.startup-tracking)¡£ÒªÇó `SpringApplication` ±»ÅäÖÃΪ `BufferingApplicationStartup`¡£ | +| `threaddump` | Performs a thread dump. | + + + +Èç¹ûÄãµÄÓ¦ÓóÌÐòÊÇÒ»¸öWebÓ¦ÓóÌÐò£¨Spring MVC¡¢Spring WebFlux»òJersey£©£¬Äã¿ÉÒÔʹÓÃÒÔ϶îÍâµÄ¶Ëµã¡£ + + + + +| ID | ˵Ã÷ | +| --- | --- | +| `heapdump` | ·µ»ØÒ»¸ö¶ÑdumpÎļþ¡£ ÔÚHotSpot JVMÉÏ£¬·µ»ØÒ»¸ö `HPROF` ¸ñʽµÄÎļþ¡£ ÔÚOpenJ9 JVMÉÏ£¬·µ»ØÒ»¸ö `PHD` ¸ñʽµÄÎļþ¡£ | +| `logfile` | ·µ»ØÈÕÖ¾ÎļþµÄÄÚÈÝ£¨Èç¹û `logging.file.name` »ò `logging.file.path` ÊôÐÔÒѱ»ÉèÖã©¡£ Ö§³ÖʹÓÃHTTP `Range` Í·À´¼ìË÷ÈÕÖ¾ÎļþµÄ²¿·ÖÄÚÈÝ¡£ | +| `prometheus` | ÒԿɱ» Prometheus ·þÎñÆ÷ץȡµÄ¸ñʽչʾ¶ÈÁ¿£¨metric£©¡£ ÒÀÀµÓÚ `micrometer-registry-prometheus`¡£ | + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.enabling)2.1\. ÆôÓö˵ã + + + +ĬÈÏÇé¿öÏ£¬³ý `shutdown` ÍâµÄËùÓж˵㶼±»ÆôÓᣠҪÅäÖÃÒ»¸ö¶ËµãµÄÆôÓã¬ÇëʹÓÃÆä `management.endpoint..enabled` ÊôÐÔ¡£ ÏÂÃæµÄÀý×ÓÆôÓÃÁË `shutdown` ¶Ëµã¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.shutdown.enabled=true + +``` + + + + + + + +Èç¹ûÄãÏ£Íû¶ËµãÆôÓÃÊÇ¡°Ñ¡ÔñÆôÓá±¶ø²»ÊÇ¡°Ñ¡Ôñ½ûÓá±£¬Ç뽫 `management.endpoints.enabled-by-default` ÊôÐÔÉèÖÃΪ `false`£¬²¢Ê¹Óõ¥¸ö¶ËµãµÄ `enabled` ÊôÐÔÀ´Ñ¡ÔñÖØÐÂÆôÓᣠÏÂÃæµÄÀý×ÓÆôÓÃÁË `info` ¶Ëµã£¬²¢½ûÓÃÁËËùÓÐÆäËû¶Ëµã¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.enabled-by-default=false +management.endpoint.info.enabled=true + +``` + + + + + + + +| | ±»½ûÓõĶ˵ã»á´ÓÓ¦ÓóÌÐòÉÏÏÂÎÄÖÐÍêȫɾ³ý¡£Èç¹ûÄãÏëÖ»¸Ä±ä±©Â¶¶ËµãµÄ¼¼Êõ£¬ÇëʹÓà [`include` ºÍ `exclude` ÊôÐÔ](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.exposing)À´´úÌæ¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.exposing)2.2\. ±©Â¶¶Ëµã + + + +ĬÈÏÇé¿öÏ£¬Ö»ÓÐhealth¶ËµãÊÇͨ¹ýHTTPºÍJMX±©Â¶µÄ¡£ ÓÉÓڶ˵ã¿ÉÄܰüº¬Ãô¸ÐÐÅÏ¢£¬ÄãÓ¦¸Ã×Ðϸ¿¼ÂǺÎʱ±©Â¶ËüÃÇ¡£ + + + + + +Òª¸Ä±äÄÄЩ¶Ëµã±»±©Â¶£¬ÇëʹÓÃÒÔÏÂÌØ¶¨¼¼ÊõµÄ `include` ºÍ `exclude` ÊôÐÔ¡£ + + + + +| ÊôÐÔ | ĬÈÏ | +| --- | --- | +| `management.endpoints.jmx.exposure.exclude` | | +| `management.endpoints.jmx.exposure.include` | `health` | +| `management.endpoints.web.exposure.exclude` | | +| `management.endpoints.web.exposure.include` | `health` | + + + +`include` ÊôÐÔÁгöÁ˱»±©Â¶µÄ¶ËµãµÄID¡£ `exclude` ÊôÐÔÁгöÁ˲»Ó¦¸Ã±»¹«¿ªµÄ¶ËµãµÄID¡£ `exclude` ÊôÐÔÓÅÏÈÓÚ `include` ÊôÐÔ¡£ Äã¿ÉÒÔÓÃÒ»¸ö¶ËµãIDÁбíÀ´ÅäÖà `include` ºÍ `exclude` ÊôÐÔ¡£ + + + + + +ÀýÈ磬Ҫͨ¹ýJMXÖ»¹«¿ª `health` ºÍ `info` ¶Ëµã£¬ÇëʹÓÃÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.jmx.exposure.include=health,info + +``` + + + + + + + +`*` ¿ÉÒÔÓÃÀ´Ñ¡ÔñËùÓж˵㡣 ÀýÈ磬Ҫͨ¹ýHTTP¹«¿ªËùÓеĶ«Î÷£¬³ýÁË `env` ºÍ `beans` ¶Ëµã£¬Ê¹ÓÃÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.exposure.include=* +management.endpoints.web.exposure.exclude=env,beans + +``` + + + + + + + +| | `*` ÔÚYAMLÖоßÓÐÌØÊ⺬Ò壬ËùÒÔÈç¹ûÄãÏë°üÀ¨£¨»òÅųý£©ËùÓеĶ˵㣬һ¶¨Òª¼ÓÒýºÅ¡£ | +| --- | --- | + + + + + +| | Èç¹ûÄãµÄÓ¦ÓóÌÐòÊǹ«¿ª±©Â¶µÄ£¬ÎÒÃÇÇ¿ÁÒ½¨ÒéÄãÒ²[±£»¤ÄãµÄ¶Ëµã](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.security)¡£ | +| --- | --- | + + + + + +| | Èç¹ûÄãÏëÔڶ˵㱩¶ʱʵʩ×Ô¼ºµÄ²ßÂÔ£¬Äã¿ÉÒÔ×¢²áÒ»¸ö `EndpointFilter` bean¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.security)2.3\. °²È«£¨Security£© + + + +ΪÁ˰²È«Æð¼û£¬Ä¬ÈÏÇé¿öÏÂÖ»ÓÐ `/health` ¶Ëµãͨ¹ýHTTP¹«¿ª¡£ Äã¿ÉÒÔʹÓà `management.endpoints.web.exposure.include` ÊôÐÔÀ´ÅäÖñ»±©Â¶µÄ¶Ëµã¡£ + + + + + +| | ÔÚÉèÖà `management.endpoints.web.exposure.include` ֮ǰ£¬ÇëÈ·±£±©Â¶µÄÖ´ÐÐÆ÷²»°üº¬Ãô¸ÐÐÅÏ¢£¬½«ÆäÖÃÓÚ·À»ðǽ֮ºó£¬»òÓÉSpring SecurityÖ®ÀàµÄ¶«Î÷À´±£Ö¤°²È«¡£ | +| --- | --- | + + + + + +Èç¹ûSpring SecurityÔÚclasspathÉÏ£¬²¢ÇÒûÓÐÆäËû `SecurityFilterChain` bean£¬ÄÇô³ýÁË `/health` Ö®ÍâµÄËùÓÐÖ´ÐÐÆ÷£¨actuator£©¶¼ÓÉSpring Boot×Ô¶¯ÅäÖÃÀ´±£Ö¤°²È«¡£ Èç¹ûÄ㶨ÒåÁËÒ»¸ö×Ô¶¨ÒåµÄ `SecurityFilterChain` bean£¬Spring Boot×Ô¶¯ÅäÖþͻáÍËËõ£¬ÈÃÄãÍêÈ«¿ØÖÆÖ´ÐÐÆ÷µÄ·ÃÎʹæÔò¡£ + + + + + +Èç¹ûÄãÏëΪHTTP¶ËµãÅäÖÃ×Ô¶¨Ò尲ȫ£¨ÀýÈ磬ֻÔÊÐí¾ßÓÐijÖÖ½ÇÉ«µÄÓû§·ÃÎÊ£©£¬Spring BootÌṩÁËһЩ·½±ãµÄ `RequestMatcher` ¶ÔÏó£¬Äã¿ÉÒÔÓëSpring Security½áºÏʹÓᣠ+ + + + + +Ò»¸öµäÐ͵ÄSpring SecurityÅäÖÿÉÄÜ¿´ÆðÀ´ÏñÏÂÃæµÄÀý×Ó¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MySecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.securityMatcher(EndpointRequest.toAnyEndpoint()); + http.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN")); + http.httpBasic(withDefaults()); + return http.build(); + } + +} + +``` + + + + + + + +Ç°ÃæµÄÀý×ÓʹÓà `EndpointRequest.toAnyEndpoint()` À´Æ¥ÅäÒ»¸öÇëÇóµ½Èκζ˵㣬ȻºóÈ·±£ËùÓеĶ˵㶼ÓÐ `ENDPOINT_ADMIN` µÄ½ÇÉ«¡£ `EndpointRequest` ÉÏ»¹ÓÐÆäËû¼¸¸öÆ¥ÅäÆ÷·½·¨¡£ ÏêÇé¼ûAPIÎĵµ£¨ [HTML](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/htmlsingle) »ò [PDF](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/pdf/spring-boot-actuator-web-api.pdf)£©¡£ + + + + + +Èç¹ûÄãÔÚ·À»ðǽºóÃæ²¿ÊðÓ¦ÓóÌÐò£¬Äã¿ÉÄÜÏ£ÍûÄãµÄËùÓÐÖ´ÐÐÆ÷¶Ëµã¶¼Äܱ»·ÃÎÊ£¬¶ø²»ÐèÒªÈÏÖ¤¡£ Äã¿ÉÒÔͨ¹ý¸Ä±ä `management.endpoints.web.exposure.include` ÊôÐÔÀ´×öµ½ÕâÒ»µã£¬ÈçÏÂËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.exposure.include=* + +``` + + + + + + + +´ËÍ⣬Èç¹û´æÔÚSpring Security£¬ÄãÐèÒªÌí¼Ó×Ô¶¨Ò尲ȫÅäÖã¬ÔÊÐíδ¾­ÈÏÖ¤µÄ·ÃÎʶ˵㣬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MySecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.securityMatcher(EndpointRequest.toAnyEndpoint()); + http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); + return http.build(); + } + +} + +``` + + + + + + + +| | ÔÚÇ°ÃæµÄÁ½¸öÀý×ÓÖУ¬ÅäÖÃÖ»ÊÊÓÃÓÚactuator¶Ëµã¡£ ÓÉÓÚSpring BootµÄ°²È«ÅäÖÃÔÚÓÐÈκΠ`SecurityFilterChain` beanµÄÇé¿ö϶¼»áÍêÈ«Í˳ö£¬ËùÒÔÄãÐèÒªÅäÖÃÒ»¸ö¶îÍâµÄ `SecurityFilterChain` bean£¬Æä¹æÔòÊÊÓÃÓÚÓ¦ÓóÌÐòµÄÆäËû²¿·Ö¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.security.csrf)2.3.1\. ¿çÍøÕ¾ÇëÇóαÔì±£»¤£¨CSRF£© + + + +ÓÉÓÚSpring BootÒÀÀµSpring SecurityµÄĬÈÏÖµ£¬CSRF±£»¤ÔÚĬÈÏÇé¿öϱ»´ò¿ª¡£ ÕâÒâζ×ÅÔÚʹÓÃĬÈϰ²È«ÅäÖÃʱ£¬ÐèÒª `POST`£¨shutdownºÍloggers¶Ëµã£©¡¢`PUT` »ò `DELETE` µÄactuator¶Ëµã»á³öÏÖ403£¨½ûÖ¹£©µÄ´íÎó¡£ + + + + + +| | ÎÒÃǽ¨ÒéÖ»ÓÐÔÚÄã´´½¨µÄ·þÎñ±»·Çä¯ÀÀÆ÷¿Í»§¶ËʹÓÃʱ²ÅÍêÈ«½ûÓÃCSRF±£»¤¡£ | +| --- | --- | + + + + + +Äã¿ÉÒÔÔÚ [Spring°²È«²Î¿¼Ö¸ÄÏ](https://docs.spring.io/spring-security/reference/6.1.0-M1/features/exploits/csrf.html) ÖÐÕÒµ½¹ØÓÚCSRF±£»¤µÄÆäËûÐÅÏ¢¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.caching)2.4\. ÅäÖö˵ã + + + +¶Ëµã»á×Ô¶¯»º´æ²»ÐèÒªÈκβÎÊýµÄ¶ÁÈ¡²Ù×÷µÄÏìÓ¦¡£ ÒªÅäÖö˵㻺´æÏìÓ¦µÄʱ¼ä£¬ÇëʹÓÃÆä `cache.time-to-live` ÊôÐÔ¡£ ÏÂÃæµÄÀý×Ó½« `beans` ¶ËµãµÄ»º´æÉú´æÊ±¼äÉèÖÃΪ10Ãë¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.beans.cache.time-to-live=10s + +``` + + + + + + + +| | `management.endpoint.` ǰ׺ΨһµØ±êʶÁËÕýÔÚÅäÖõĶ˵㡣 | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.hypermedia)2.5\. ÓÃÓÚ Actuator Web ¶ËµãµÄ³¬Ã½Ì壨Hypermedia£© + + + +Ò»¸ö ¡°discovery page¡± ±»Ìí¼Óµ½ËùÓж˵ãµÄÁ´½ÓÖС£ ĬÈÏÇé¿öÏ£¬¡°discovery page¡± ÔÚ `/actuator` ÉÏÊÇ¿ÉÓõġ£ + + + + + +Òª½ûÓà ¡°discovery page¡±£¬ÇëÔÚÄãµÄÓ¦ÓóÌÐòÊôÐÔÖÐÌí¼ÓÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.discovery.enabled=false + +``` + + + + + + + +µ±ÅäÖÃÁËÒ»¸ö×Ô¶¨ÒåµÄ¹ÜÀíÉÏÏÂÎÄ·¾¶Ê±£¬¡°discovery page¡± »á×Ô¶¯´Ó `/actuator` ÒÆµ½¹ÜÀíÉÏÏÂÎĵĸù²¿¡£ ÀýÈ磬Èç¹û¹ÜÀíÉÏÏÂÎÄ·¾¶ÊÇ `/management`£¬discovery page¿ÉÒÔ´Ó `/management` »ñµÃ¡£ µ±¹ÜÀíÉÏÏÂÎÄ·¾¶±»ÉèÖÃΪ `/` ʱ£¬·¢ÏÖÒ³±»½ûÓã¬ÒÔ·ÀÖ¹ÓëÆäËûmapping·¢Éú³åÍ»µÄ¿ÉÄÜÐÔ¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.cors)2.6\. CORSµÄÖ§³Ö + + + +[¿çÔ´×ÊÔ´¹²Ïí](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)£¨CORS£©ÊÇ [W3CµÄÒ»¸ö¹æ·¶](https://www.w3.org/TR/cors/)£¬¿ÉÒÔÈÃÄãÒÔÁé»îµÄ·½Ê½Ö¸¶¨ÄÄÖÖ¿çÓòÇëÇó±»ÊÚȨ¡£Èç¹ûÄãʹÓÃSpring MVC»òSpring WebFlux£¬Äã¿ÉÒÔÅäÖÃActuatorµÄWeb¶ËµãÀ´Ö§³ÖÕâÖÖÇé¿ö¡£ + + + + + +CORSÖ§³ÖÔÚĬÈÏÇé¿öÏÂÊǽûÓõģ¬Ö»ÓÐÔÚÄãÉèÖÃÁË `management.endpoints.web.cors.allowed-origins` ÊôÐÔºó²Å»áÆôÓᣠÏÂÃæµÄÅäÖÃÔÊÐí´Ó `example.com` ÓòÃûÖе÷Óà `GET` ºÍ `POST`¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.cors.allowed-origins=https://example.com +management.endpoints.web.cors.allowed-methods=GET,POST + +``` + + + + + + + +| | ²Î¼û [`CorsEndpointProperties`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/CorsEndpointProperties.java) ÒÔ»ñµÃÍêÕûµÄÑ¡ÏîÁÐ±í¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom)2.7\. ʵÏÖ×Ô¶¨Òå¶Ëµã + + + +Èç¹ûÄãÌí¼ÓÁËÒ»¸ö´øÓÐ `@Endpoint` ×¢½âµÄ `@Bean`£¬ÈκδøÓÐ `@ReadOperation`¡¢`@WriteOperation` »ò `@DeleteOperation` ×¢Ê͵ķ½·¨¶¼»á×Ô¶¯Í¨¹ýJMX¹«¿ª£¬ÔÚWebÓ¦ÓóÌÐòÖÐÒ²»áͨ¹ýHTTP¹«¿ª¡£ ͨ¹ýʹÓÃJersey¡¢Spring MVC»òSpring WebFlux£¬¶Ëµã¿ÉÒÔͨ¹ýHTTP±©Â¶¡£ Èç¹ûJerseyºÍSpring MVC¶¼¿ÉÓã¬ÔòʹÓÃSpring MVC¡£ + + + + + +ÏÂÃæµÄÀý×Ó±©Â¶ÁËÒ»¸ö¶Á²Ù×÷£¬Ëü·µ»ØÒ»¸ö×Ô¶¨Òå¶ÔÏó¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ReadOperation +public CustomData getData() { + return new CustomData("test", 5); +} + +``` + + + + + + + +ÄãÒ²¿ÉÒÔͨ¹ýʹÓà `@JmxEndpoint` »ò `@WebEndpoint` À´±àÐ´ÌØ¶¨¼¼ÊõµÄ¶Ëµã¡£ ÕâЩ¶Ëµã±»ÏÞÖÆÔÚËüÃǸ÷×Եļ¼ÊõÉÏ¡£ ÀýÈ磬`@WebEndpoint` ֻͨ¹ýHTTP±©Â¶£¬¶ø²»ÊÇͨ¹ýJMX¡£ + + + + + +Äã¿ÉÒÔͨ¹ýʹÓà `@EndpointWebExtension` ºÍ `@EndpointJmxExtension` À´±àÐ´ÌØ¶¨µÄ¼¼ÊõÀ©Õ¹¡£ ÕâЩע½âÈÃÄãÌá¹©ÌØ¶¨¼¼ÊõµÄ²Ù×÷£¬ÒÔÔöÇ¿ÏÖÓеĶ˵㡣 + + + + + +×îºó£¬Èç¹ûÄãÐèÒª·ÃÎÊWeb¿ò¼ÜµÄÌØ¶¨¹¦ÄÜ£¬Äã¿ÉÒÔʵÏÖservlet»òSpringµÄ `@Controller` ºÍ `@RestController` ¶Ëµã£¬´ú¼ÛÊÇËüÃDz»ÄÜͨ¹ýJMX»òʹÓò»Í¬µÄWeb¿ò¼Üʱ¿ÉÓᣠ+ + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.input)2.7.1\. ½ÓÊÕÊäÈë + + + +¶ËµãÉϵIJÙ×÷ͨ¹ýÆä²ÎÊý½ÓÊÕÊäÈë¡£ µ±Í¨¹ýweb¹«¿ªÊ±£¬ÕâЩ²ÎÊýµÄÖµÀ´×ÔURLµÄ²éѯ²ÎÊýºÍJSONÇëÇóÌå¡£ µ±Í¨¹ýJMX¹«¿ªÊ±£¬²ÎÊý±»Ó³Éäµ½MBean²Ù×÷µÄ²ÎÊýÖС£ ĬÈÏÇé¿öÏ£¬²ÎÊýÊDZØÐèµÄ¡£ ËüÃÇ¿ÉÒÔͨ¹ýʹÓà `@javax.annotation.Nullable` »ò `@org.springframework.lang.Nullable` ½øÐÐ×¢½â¶ø³ÉΪ¿ÉÑ¡Ïî¡£ + + + + + +Äã¿ÉÒÔ½«JSONÇëÇóÌåÖеÄÿ¸ö¸ùÊôÐÔÓ³Éäµ½¶ËµãµÄÒ»¸ö²ÎÊý¡£ ¿¼ÂÇÒ»ÏÂÏÂÃæµÄJSONÇëÇóÌå¡£ + + + + + + + +``` +{ + "name": "test", + "counter": 42 +} +``` + + + + + + + +Äã¿ÉÒÔÓÃËüÀ´µ÷ÓÃÒ»¸öд²Ù×÷£¬¸Ã²Ù×÷ÐèÒª `String name` ºÍ `int counter` ²ÎÊý£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@WriteOperation +public void updateData(String name, int counter) { + // injects "test" and 42 +} + +``` + + + + + + + +| | ÒòΪ¶ËµãÊǼ¼Êõ²»¿ÉÖªµÄ£¬ÔÚ·½·¨Ç©ÃûÖÐÖ»ÄÜÖ¸¶¨¼òµ¥µÄÀàÐÍ¡£ ÌØ±ðÊÇ£¬²»Ö§³ÖÓà `CustomData` ÀàÐÍÉùÃ÷Ò»¸ö¶¨ÒåÁË `name` ºÍ `counter` ÊôÐԵĵ¥Ò»²ÎÊý¡£ | +| --- | --- | + + + + + +| | ΪÁËÈÃÊäÈëÓ³Éäµ½²Ù×÷·½·¨µÄ²ÎÊý£¬ÊµÏֶ˵ãµÄJava´úÂëÓ¦¸ÃÓà `-parameters` ±àÒ룬¶øÊµÏֶ˵ãµÄKotlin´úÂëÓ¦¸ÃÓà `-java-parameters` ±àÒë¡£ Èç¹ûÄãʹÓÃSpring BootµÄGradle²å¼þ»òʹÓÃMavenºÍ `spring-boot-starter-parent`£¬Õ⽫×Ô¶¯·¢Éú¡£ | +| --- | --- | + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.input.conversion)ÊäÈëÀàÐÍת»» + + + +Èç¹ûÓбØÒª£¬´«µÝ¸ø¶Ëµã²Ù×÷·½·¨µÄ²ÎÊý»á×Ô¶¯×ª»»ÎªËùÐèÀàÐÍ¡£ ÔÚµ÷ÓòÙ×÷·½·¨Ö®Ç°£¬Í¨¹ýJMX»òHTTPÊÕµ½µÄÊäÈ뱻ת»»ÎªËùÐèµÄÀàÐÍ£¬·½·¨ÊÇʹÓà `ApplicationConversionService` µÄʵÀýÒÔ¼°ÈκΠ`Converter` »ò `GenericConverter` Bean£¬²¢ÒÔ `@EndpointConverter` ÏÞ¶¨¡£ + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web)2.7.2\. ×Ô¶¨ÒåWEB¶Ëµã + + + +¶Ô `@Endpoint`¡¢`@WebEndpoint` »ò `@EndpointWebExtension` ²Ù×÷»á×Ô¶¯Ê¹ÓÃJersey¡¢Spring MVC»òSpring WebFluxͨ¹ýHTTP¹«¿ª¡£ Èç¹ûJerseyºÍSpring MVC¶¼¿ÉÓã¬ÔòʹÓÃSpring MVC¡£ + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.request-predicates)WEB¶ËµãÇëÇóν´Ê£¨Predicates£© + + + +Ò»¸öÇëÇóν´Ê»áΪweb±©Â¶µÄ¶ËµãÉϵÄÿ¸ö²Ù×÷£¨operation£©×Ô¶¯Éú³É¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.path-predicates)Path + + + +pathν´ÊÓɶ˵ãµÄIDºÍÍøÂ籩¶µÄ¶ËµãµÄ»ù±¾Â·¾¶¾ö¶¨¡£ ĬÈϵĻù±¾Â·¾¶ÊÇ `/actuator`¡£ ÀýÈ磬һ¸öIDΪ `sessions` µÄ¶ËµãÔÚν´ÊÖÐʹÓà `/actuator/sessions` ×÷ΪÆä·¾¶¡£ + + + + + +Äã¿ÉÒÔͨ¹ýÓà `@Selector` ×¢½â²Ù×÷·½·¨µÄÒ»¸ö»ò¶à¸ö²ÎÊýÀ´½øÒ»²½¶¨ÖÆÂ·¾¶¡£ ÕâÑùµÄ²ÎÊý»á×÷Ϊһ¸ö·¾¶±äÁ¿Ìí¼Óµ½Â·¾¶Î½´ÊÖС£ ÔÚµ÷Óö˵ã²Ù×÷ʱ£¬¸Ã±äÁ¿µÄÖµ»á±»´«Èë²Ù×÷·½·¨¡£ Èç¹ûÄãÏë²¶»ñËùÓÐÊ£ÓàµÄ·¾¶ÔªËØ£¬Äã¿ÉÒÔÔÚ×îºóÒ»¸ö²ÎÊýÉÏÌí¼Ó `@Selector(Match=ALL_REMAINING)`£¬²¢Ê¹Æä³ÉΪһ¸öÓë `String[]` ת»»¼æÈݵÄÀàÐÍ¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.method-predicates)HTTP method£¨·½·¨£© + + + +HTTP methodν´ÊÊÇÓɲÙ×÷ÀàÐ;ö¶¨µÄ£¬ÈçϱíËùʾ¡£ + + + + +| Operation | HTTP method | +| --- | --- | +| `@ReadOperation` | `GET` | +| `@WriteOperation` | `POST` | +| `@DeleteOperation` | `DELETE` | + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.consumes-predicates)Consumes + + + +¶ÔÓÚʹÓÃrequest bodyµÄ `@WriteOperation`£¨HTTP `POST`£©£¬Î½´ÊµÄ `consumes` ×Ó¾äÊÇ `application/vnd.spring-boot.actuator.v2+json, application/json`¡£ ¶ÔÓÚËùÓÐÆäËû²Ù×÷£¬`consumes` ×Ó¾äÊǿյġ£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.produces-predicates)Produces + + + +ν´ÊµÄ `produces` ×Ó¾ä¿ÉÒÔÓÉ `@DeleteOperation`¡¢`@ReadOperation` ºÍ `@WriteOperation` ×¢Ê굀 `produces` ÊôÐÔ¾ö¶¨¡£ ¸ÃÊôÐÔÊÇ¿ÉÑ¡µÄ¡£ Èç¹û²»Ê¹ÓÃËü£¬`produces` ×Ó¾ä»á×Ô¶¯È·¶¨¡£ + + + + + +Èç¹û²Ù×÷·½·¨·µ»Ø `void` »ò `Void`£¬Ôò `produces` ×Ó¾äΪ¿Õ¡£ Èç¹û²Ù×÷·½·¨·µ»Ø `org.springframework.core.io.Resource`£¬`produces` ×Ó¾äÊÇ `application/octet-stream`¡£ ¶ÔÓÚËùÓÐÆäËû²Ù×÷£¬`produces` ×Ó¾äÊÇ `application/vnd.spring-boot.actuator.v2+json, application/json`¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.response-status)WEB¶ËµãÏìӦ״̬ + + + +¶Ëµã²Ù×÷µÄĬÈÏÏìӦ״̬ȡ¾öÓÚ²Ù×÷ÀàÐÍ£¨¶Á¡¢Ð´»òɾ³ý£©ºÍ²Ù×÷·µ»ØµÄÄÚÈÝ£¨Èç¹ûÓеϰ£©¡£ + + + + + +Èç¹û `@ReadOperation` ·µ»ØÒ»¸öÖµ£¬ÏìӦ״̬½«ÊÇ200(Ok)¡£ Èç¹ûËüûÓзµ»ØÒ»¸öÖµ£¬ÏìӦ״̬½«ÊÇ404(Not Found)¡£ + + + + + +Èç¹û `@WriteOperation` »ò `@DeleteOperation` ·µ»ØÒ»¸öÖµ£¬ÏìӦ״̬½«ÊÇ200£¨OK£©¡£ Èç¹ûËüûÓзµ»ØÒ»¸öÖµ£¬ÏìӦ״̬½«ÊÇ204£¨No Content£©¡£ + + + + + +Èç¹ûÒ»¸ö²Ù×÷ÔÚµ÷ÓÃʱûÓÐËùÐèµÄ²ÎÊý£¬»òÕß²ÎÊý²»Äܱ»×ª»»ÎªËùÐèµÄÀàÐÍ£¬²Ù×÷·½·¨¾Í²»»á±»µ÷Óã¬ÏìӦ״̬½«ÊÇ400£¨Bad Request£©¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.range-requests)WEB¶Ëµã Range ÇëÇó + + + +Äã¿ÉÒÔʹÓÃHTTP rangeÇëÇóÀ´ÇëÇóÒ»¸öHTTP×ÊÔ´µÄÒ»²¿·Ö¡£ µ±Ê¹ÓÃSpring MVC»òSpring Web Fluxʱ£¬·µ»Ø `org.springframework.core.io.Resource` µÄ²Ù×÷×Ô¶¯Ö§³Ö·¶Î§ÇëÇó¡£ + + + + + +| | ʹÓÃJerseyʱ²»Ö§³Ö Range ÇëÇó¡£ | +| --- | --- | + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.web.security)Web¶ËµãµÄ°²È« + + + +ÔÚweb¶Ëµã»òwebÌØ¶¨¶ËµãÀ©Õ¹ÉϵIJÙ×÷¿ÉÒÔ½ÓÊÕµ±Ç°µÄ `java.security.Principal` »ò `org.springframework.boot.actuate.endpoint.SecurityContext` ×÷Ϊ·½·¨²ÎÊý¡£ ǰÕßͨ³£Óë `@Nullable` Ò»ÆðʹÓã¬ÎªÒÑÈÏÖ¤ºÍδÈÏÖ¤µÄÓû§Ìṩ²»Í¬µÄÐÐΪ¡£ ºóÕßͨ³£ÓÃÓÚͨ¹ýʹÓÃÆä `isUserInRole(String)` ·½·¨À´Ö´ÐÐÊÚȨ¼ì²é¡£ + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.servlet)2.7.3\. Servlet ¶Ëµã + + + +Ò»¸öServlet¿ÉÒÔ×÷Ϊһ¸ö¶Ëµã±©Â¶³öÀ´£¬·½·¨ÊÇʵÏÖÒ»¸öÓà `@ServletEndpoint` ×¢½âµÄÀ࣬ͬʱʵÏÖ `Supplier`¡£ Servlet¶ËµãÌṩÁËÓëservletÈÝÆ÷µÄ¸üÉî²ã´ÎµÄÕûºÏ£¬µ«È´ÎþÉüÁË¿ÉÒÆÖ²ÐÔ¡£ ËüÃǵÄÄ¿µÄÊÇÓÃÀ´½«ÏÖÓеÄServlet×÷Ϊһ¸ö¶ËµãÀ´¹«¿ª¡£ ¶ÔÓÚеĶ˵㣬Ӧ¾¡¿ÉÄÜÑ¡Ôñ `@Endpoint` ºÍ `@WebEndpoint` ×¢½â¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.implementing-custom.controller)2.7.4\. Controller ¶Ëµã + + + +Äã¿ÉÒÔʹÓà `@ControllerEndpoint` ºÍ `@RestControllerEndpoint` À´ÊµÏÖÒ»¸ö½öÓÉSpring MVC»òSpring WebFlux¹«¿ªµÄ¶Ëµã¡£ ·½·¨Í¨¹ýʹÓÃSpring MVCºÍSpring WebFluxµÄ±ê×¼×¢½â½øÐÐÓ³É䣬Èç `@RequestMapping` ºÍ `@GetMapping`£¬¶ËµãµÄID±»ÓÃ×÷·¾¶µÄǰ׺¡£ ¿ØÖÆÆ÷¶ËµãÌṩÁËÓëSpringµÄWeb¿ò¼Ü¸üÉîÈëµÄ¼¯³É£¬µ«È´ÎþÉüÁË¿ÉÒÆÖ²ÐÔ¡£ Ó¦¾¡¿ÉÄÜÑ¡Ôñ `@Endpoint` ºÍ `@WebEndpoint` ×¢½â¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health)2.8\. ½¡¿µÐÅÏ¢ + + + +Äã¿ÉÒÔʹÓý¡¿µÐÅÏ¢À´¼ì²éÄãÔËÐеÄÓ¦ÓóÌÐòµÄ״̬¡£ Ëü¾­³£±»¼à¿ØÈí¼þÓÃÀ´ÔÚÉú²úϵͳ·¢Éú¹ÊÕÏʱÌáÐѱðÈË¡£ `health` ¶Ëµã±©Â¶µÄÐÅϢȡ¾öÓÚ `management.endpoint.health.show-details` ºÍ `management.endpoint.health.show-components` ÊôÐÔ£¬ËüÃÇ¿ÉÒÔÅäÖÃΪÒÔÏÂÖµÖ®Ò»¡£ + + + + +| Öµ | ˵Ã÷ | +| --- | --- | +| `never` | ϸ½Ú´Ó²»ÏÔʾ¡£ | +| `when-authorized` | ϸ½ÚÖ»ÏÔʾ¸øÊÚȨÓû§¡£ ÊÚȨµÄ½ÇÉ«¿ÉÒÔͨ¹ýʹÓà `management.endpoint.health.roles` ½øÐÐÅäÖᣠ| +| `always` | ÏêÇéÏÔʾ¸øËùÓÐÓû§¡£ | + + + +ĬÈÏÖµÊÇ `never`¡£ µ±Óû§´¦Óڶ˵ãµÄÒ»¸ö»ò¶à¸ö½ÇÉ«ÖÐʱ£¬ËûÃDZ»ÈÏΪÊDZ»ÊÚȨµÄ¡£ Èç¹û¶ËµãûÓÐÅäÖýÇÉ«£¨Ä¬ÈÏÖµ£©£¬ËùÓÐÈÏÖ¤µÄÓû§¶¼±»ÈÏΪÊÇÊÚȨµÄ¡£ Äã¿ÉÒÔͨ¹ýʹÓà `management.endpoint.health.roles` ÊôÐÔÀ´ÅäÖýÇÉ«¡£ + + + + + +| | Èç¹ûÄãÒѾ­±£»¤ÁËÄãµÄÓ¦ÓóÌÐò²¢Ï£ÍûʹÓà `always`£¬ÄãµÄ°²È«ÅäÖã¨security configuration£©±ØÐëÔÊÐíÈÏÖ¤ºÍ·ÇÈÏÖ¤Óû§·ÃÎÊhealth¶Ëµã¡£ | +| --- | --- | + + + + + +½¡¿µÐÅÏ¢ÊÇ´Ó [`HealthContributorRegistry`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributorRegistry.java) µÄÄÚÈÝÖÐÊÕ¼¯µÄ£¨Ä¬ÈÏÇé¿öÏ£¬ËùÓÐ [`HealthContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributor.java) ʵÀý¶¼¶¨ÒåÔÚÄãµÄ `ApplicationContext` ÖУ©¡£ Spring Boot°üÀ¨Ò»Ð©×Ô¶¯ÅäÖÃµÄ `HealthContributor`£¬ÄãÒ²¿ÉÒÔ±àд×Ô¼ºµÄ¡£ + + + + + +Ò»¸ö `HealthContributor` ¿ÉÒÔÊÇÒ»¸ö `HealthIndicator` »òÒ»¸ö `CompositeHealthContributor`¡£ Ò»¸ö `HealthIndicator` Ìṩʵ¼ÊµÄ½¡¿µÐÅÏ¢£¬°üÀ¨ `Status`¡£ Ò»¸ö `CompositeHealthContributor` ÌṩÆäËû `HealthContributors` µÄ×éºÏ¡£ ×ÛºÏÆðÀ´£¬contributorÐγÉÒ»¸öÊ÷×´½á¹¹À´±íʾÕû¸öϵͳµÄ½¡¿µ×´¿ö¡£ + + + + + +ĬÈÏÇé¿öÏ£¬×îÖÕµÄϵͳ½¡¿µ×´¿öÊÇÓÉÒ»¸ö `StatusAggregator` µÃ³öµÄ£¬Ëü¸ù¾ÝÒ»¸öÓÐÐòµÄ״̬Áбí¶Ôÿ¸ö `HealthIndicator` µÄ״̬½øÐÐÅÅÐò¡£ ÅÅÐòÁбíÖеĵÚÒ»¸ö״̬±»ÓÃ×÷ÕûÌ彡¿µ×´Ì¬¡£ Èç¹ûûÓÐ `HealthIndicator` ·µ»ØµÄ״̬ÊÇ `StatusAggregator` ËùÖªµÀµÄ£¬¾Í»áʹÓà `UNKNOWN` ״̬¡£ + + + + + +| | Äã¿ÉÒÔʹÓà `HealthContributorRegistry` À´ÔÚÔËÐÐʱע²áºÍÈ¡Ïû×¢²á½¡¿µÖ¸±ê¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.auto-configured-health-indicators)2.8.1\. ×Ô¶¯ÅäÖõÄHealthIndicators + + + +ÔÚÊʵ±µÄʱºò£¬Spring Boot»á×Ô¶¯ÅäÖÃϱíÖÐÁгöµÄ `HealthIndicators`¡£ ÄãÒ²¿ÉÒÔͨ¹ýÅäÖà `management.health.key.enabled` À´ÆôÓûòÍ£ÓÃËùѡָ±ê¡£ ºÍϱíÖÐÁгöµÄ `key`¡£ + + + + +| Key | Name | ˵Ã÷ | +| --- | --- | --- | +| `cassandra` | [`CassandraDriverHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java) | ¼ì²éCassandraÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `couchbase` | [`CouchbaseHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java) | ¼ì²éCouchbase¼¯ÈºÊÇ·ñÒѾ­Æô¶¯¡£ | +| `db` | [`DataSourceHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java) | ¼ì²éÊÇ·ñ¿ÉÒÔ»ñµÃÓë`DataSource`µÄÁ¬½Ó¡£ | +| `diskspace` | [`DiskSpaceHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java) | ¼ì²é´ÅÅ̿ռäÊÇ·ñ²»×ã¡£ | +| `elasticsearch` | [`ElasticsearchRestHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java) | ¼ì²éElasticsearch¼¯ÈºÊÇ·ñÒѾ­Æô¶¯¡£ | +| `hazelcast` | [`HazelcastHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicator.java) | ¼ì²éHazelcast·þÎñÆ÷ÊÇ·ñÒѾ­Æô¶¯¡£ | +| `influxdb` | [`InfluxDbHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/influx/InfluxDbHealthIndicator.java) | ¼ì²éInfluxDB·þÎñÆ÷ÊÇ·ñÒѾ­Æô¶¯¡£ | +| `jms` | [`JmsHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jms/JmsHealthIndicator.java) | ¼ì²éÒ»¸öJMS´úÀíÊÇ·ñÒѾ­Æô¶¯¡£ | +| `ldap` | [`LdapHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ldap/LdapHealthIndicator.java) | ¼ì²éÒ»¸öLDAP·þÎñÆ÷ÊÇ·ñÕý³£¡£ | +| `mail` | [`MailHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java) | ¼ì²éÒ»¸öÓʼþ·þÎñÆ÷ÊÇ·ñÕý³£¡£ | +| `mongo` | [`MongoHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/mongo/MongoHealthIndicator.java) | ¼ì²éMongoÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `neo4j` | [`Neo4jHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java) | ¼ì²éNeo4jÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `ping` | [`PingHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/PingHealthIndicator.java) | ×ÜÊÇÏìÓ¦ `UP` ¡£ | +| `rabbit` | [`RabbitHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/amqp/RabbitHealthIndicator.java) | ¼ì²éÒ»¸öRabbit·þÎñÆ÷ÊÇ·ñÒѾ­Æô¶¯¡£ | +| `redis` | [`RedisHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/redis/RedisHealthIndicator.java) | ¼ì²éRedis·þÎñÆ÷ÊÇ·ñÒѾ­Æô¶¯¡£ | + + + +| | Äã¿ÉÒÔͨ¹ýÉèÖà `management.health.defaults.enabled` ÊôÐÔÀ´½ûÓÃËüÃÇ¡£ | +| --- | --- | + + + + + +¶îÍâµÄ `HealthIndicators` ÊÇ¿ÉÓõ쬵«ÔÚĬÈÏÇé¿öϲ»ÆôÓᣠ+ + + + +| Key | Name | ˵Ã÷ | +| --- | --- | --- | +| `livenessstate` | [`LivenessStateHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessStateHealthIndicator.java) | ÏÔʾ ¡°Liveness¡± Ó¦ÓóÌÐòµÄ¿ÉÓÃÐÔ״̬¡£ | +| `readinessstate` | [`ReadinessStateHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessStateHealthIndicator.java) | ±©Â¶ ¡°Readiness¡± Ó¦ÓóÌÐòµÄ¿ÉÓÃÐÔ״̬¡£ | + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.writing-custom-health-indicators)2.8.2\. ±àд×Ô¶¨ÒåHealthIndicators + + + +ΪÁËÌṩ×Ô¶¨ÒåµÄ½¡¿µÐÅÏ¢£¬Äã¿ÉÒÔ×¢²áʵÏÖ [`HealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java) ½Ó¿ÚµÄSpring Bean¡£ ÄãÐèÒªÌṩһ¸ö `health()` ·½·¨µÄʵÏÖ£¬²¢·µ»ØÒ»¸ö `Health` ÏìÓ¦¡£ `Health` ÏìÓ¦Ó¦¸Ã°üÀ¨Ò»¸östatus£¬²¢¿ÉÒÔÑ¡Ôñ°üÀ¨ÒªÏÔʾµÄÆäËûϸ½Ú¡£ ÏÂÃæµÄ´úÂëÏÔʾÁËÒ»¸ö `HealthIndicator` µÄʵÏÖÑù±¾¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Component +public class MyHealthIndicator implements HealthIndicator { + + @Override + public Health health() { + int errorCode = check(); + if (errorCode != 0) { + return Health.down().withDetail("Error Code", errorCode).build(); + } + return Health.up().build(); + } + + private int check() { + // perform some specific health check + return ... + } + +} + +``` + + + + + + + +| | Ò»¸ö¸ø¶¨µÄ `HealthIndicator` µÄ±êʶ·û£¨ID£©ÊÇûÓÐ `HealthIndicator` ºó׺µÄBeanµÄÃû×Ö£¬Èç¹ûËü´æÔڵϰ¡£ ÔÚÇ°ÃæµÄÀý×ÓÖУ¬½¡¿µÐÅÏ¢¿ÉÒÔÔÚÒ»¸öÃûΪ `my` µÄÌõÄ¿ÖÐÕÒµ½¡£ | +| --- | --- | + + + + + +| | ½¡¿µÖ¸±êͨ³£ÊÇͨ¹ýHTTPµ÷Óõģ¬ÐèÒªÔÚÈκÎÁ¬½Ó³¬Ê±Ö®Ç°×ö³öÏìÓ¦¡£ Èç¹ûÈκν¡¿µÖ¸±êµÄÏìӦʱ¼ä³¬¹ý10Ã룬Spring Boot½«¼Ç¼һÌõ¾¯¸æÐÅÏ¢¡£ Èç¹ûÄãÏëÅäÖÃÕâ¸öãÐÖµ£¬Äã¿ÉÒÔʹÓà `management.endpoint.health.logging.slow-indicator-threshold` ÊôÐÔ¡£ | +| --- | --- | + + + + + +³ýÁËSpring BootÔ¤¶¨ÒåµÄ [`Status`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java) ÀàÐÍÍ⣬`Health` ¿ÉÒÔ·µ»Ø´ú±íÐÂϵͳ״̬µÄ×Ô¶¨Òå `Status`¡£ ÔÚÕâÖÖÇé¿öÏ£¬Ä㻹ÐèÒªÌṩ `StatusAggregator` ½Ó¿ÚµÄ×Ô¶¨ÒåʵÏÖ£¬»òÕßÄã±ØÐëͨ¹ýʹÓà `management.endpoint.health.status.order` ÅäÖÃÊôÐÔÀ´ÅäÖÃĬÈÏʵÏÖ¡£ + + + + + +ÀýÈ磬¼ÙÉèÔÚÄãµÄÒ»¸ö `HealthIndicator` ʵÏÖÖÐʹÓÃÁËÒ»¸ö´úÂëΪ `FATAL` µÄР`Status`¡£ ΪÁËÅäÖÃÑÏÖØÐÔ˳Ðò£¬ÔÚÄãµÄÓ¦ÓóÌÐòÊôÐÔÖÐÌí¼ÓÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.status.order=fatal,down,out-of-service,unknown,up + +``` + + + + + + + +ÏìÓ¦ÖеÄHTTP״̬´úÂë·´Ó³ÁËÕûÌ彡¿µ×´Ì¬¡£ ĬÈÏÇé¿öÏ£¬`OUT_OF_SERVICE` ºÍ `DOWN` Ó³Éäµ½503¡£ ÈκÎδӳÉäµÄ½¡¿µ×´Ì¬£¬°üÀ¨ `UP`£¬¶¼Ó³ÉäΪ200¡£ Èç¹ûÄãͨ¹ýHTTP·ÃÎʽ¡¿µ¶Ëµã£¬Äã¿ÉÄÜ»¹Ïë×¢²á×Ô¶¨Òå״̬ӳÉä¡£ ÅäÖÃ×Ô¶¨ÒåÓ³Éä»á½ûÓà `DOWN` ºÍ `OUT_OF_SERVICE` µÄĬÈÏÓ³Éä¡£ Èç¹ûÄãÏë±£ÁôĬÈÏÓ³É䣬Äã±ØÐëÃ÷È·µØÅäÖÃËüÃÇ£¬ÒÔ¼°ÈκÎ×Ô¶¨ÒåÓ³Éä¡£ ÀýÈ磬ÏÂÃæµÄÊôÐÔ½« `FATAL` Ó³ÉäΪ503£¨·þÎñ²»¿ÉÓã©£¬²¢±£ÁôÁË `DOWN` ºÍ `OUT_OF_SERVICE` µÄĬÈÏÓ³Éä¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.status.http-mapping.down=503 +management.endpoint.health.status.http-mapping.fatal=503 +management.endpoint.health.status.http-mapping.out-of-service=503 + +``` + + + + + + + +| | Èç¹ûÄãÐèÒª¸ü¶àµÄ¿ØÖÆ£¬Äã¿ÉÒÔ¶¨ÒåÄã×Ô¼ºµÄ `HttpCodeStatusMapper` bean¡£ | +| --- | --- | + + + + + +ϱíÏÔʾÁËÄÚÖÃ״̬µÄĬÈÏ״̬ӳÉä¡£ + + + + +| Status | Mapping | +| --- | --- | +| `DOWN` | `SERVICE_UNAVAILABLE` (`503`) | +| `OUT_OF_SERVICE` | `SERVICE_UNAVAILABLE` (`503`) | +| `UP` | ĬÈÏÇé¿öÏÂûÓÐÓ³É䣬ËùÒÔHTTP״̬Ϊ `200`¡£ | +| `UNKNOWN` | ĬÈÏÇé¿öÏÂûÓÐÓ³É䣬ËùÒÔHTTP״̬Ϊ `200`¡£ | + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.reactive-health-indicators)2.8.3\. ÏìӦʽ½¡¿µÖ¸±ê + + + +¶ÔÓÚÏìӦʽӦÓóÌÐò£¬ÀýÈçÄÇЩʹÓÃSpring WebFluxµÄÓ¦ÓóÌÐò£¬`ReactiveHealthContributor` ÌṩÁËÒ»¸ö·Ç×èÈûµÄÆõÔ¼À´»ñȡӦÓóÌÐòµÄ½¡¿µ×´¿ö¡£ Ó봫ͳµÄ `HealthContributor` ÀàËÆ£¬½¡¿µÐÅÏ¢´Ó [`ReactiveHealthContributorRegistry`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthContributorRegistry.java) µÄÄÚÈÝÖÐÊÕ¼¯£¨Ä¬ÈÏÇé¿öÏ£¬ËùÓÐ [`HealthContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributor.java) ºÍ [`ReactiveHealthContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthContributor.java) µÄʵÀý¶¨ÒåÔÚÄãµÄ `ApplicationContext` À¡£ + + + + + +²»¶ÔÏìӦʽAPI½øÐмì²éµÄ³£¹æ `HealthContributors` ÔÚµ¯ÐÔµ÷¶ÈÆ÷ÉÏÖ´ÐС£ + + + + + +| | ÔÚÒ»¸öÏìӦʽӦÓóÌÐòÖУ¬ÄãÓ¦¸ÃʹÓà `ReactiveHealthContributorRegistry` À´ÔÚÔËÐÐʱע²áºÍÈ¡Ïû×¢²á½¡¿µÖ¸±ê¡£ Èç¹ûÄãÐèҪע²áÒ»¸öÆÕͨµÄ `HealthContributor`£¬ÄãÓ¦¸ÃÓà `ReactiveHealthContributor#adapt` À´°ü×°Ëü¡£ | +| --- | --- | + + + + + +ΪÁË´ÓÏìӦʽAPIÖÐÌṩ×Ô¶¨ÒåµÄ½¡¿µÐÅÏ¢£¬Äã¿ÉÒÔ×¢²áʵÏÖ [`ReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthIndicator.java) ½Ó¿ÚµÄSpring Bean¡£ ÏÂÃæµÄ´úÂëÏÔʾÁËÒ»¸ö `ReactiveHealthIndicator` µÄʾÀýʵÏÖ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Component +public class MyReactiveHealthIndicator implements ReactiveHealthIndicator { + + @Override + public Mono health() { + return doHealthCheck().onErrorResume((exception) -> + Mono.just(new Health.Builder().down(exception).build())); + } + + private Mono doHealthCheck() { + // perform some specific health check + return ... + } + +} + +``` + + + + + + + +| | ΪÁË×Ô¶¯´¦Àí´íÎ󣬿ÉÒÔ¿¼ÂÇ´Ó `AbstractReactiveHealthIndicator` ÖÐÀ©Õ¹¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.auto-configured-reactive-health-indicators)2.8.4\. ×Ô¶¯ÅäÖÃµÄ ReactiveHealthIndicators + + + +ÔÚÊʵ±µÄʱºò£¬Spring Boot»á×Ô¶¯ÅäÖÃÒÔÏ嵀 `ReactiveHealthIndicators`¡£ + + + + +| Key | Name | ˵Ã÷ | +| --- | --- | --- | +| `cassandra` | [`CassandraDriverReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicator.java) | ¼ì²éCassandraÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `couchbase` | [`CouchbaseReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java) | ¼ì²éCouchbase¼¯ÈºÊÇ·ñÒѾ­Æô¶¯¡£ | +| `elasticsearch` | [`ElasticsearchReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/elasticsearch/ElasticsearchReactiveHealthIndicator.java) | ¼ì²éElasticsearch¼¯ÈºÊÇ·ñÒѾ­Æô¶¯¡£ | +| `mongo` | [`MongoReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/mongo/MongoReactiveHealthIndicator.java) | ¼ì²éMongoÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `neo4j` | [`Neo4jReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java) | ¼ì²éNeo4jÊý¾Ý¿âÊÇ·ñÒѾ­Æô¶¯¡£ | +| `redis` | [`RedisReactiveHealthIndicator`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/redis/RedisReactiveHealthIndicator.java) | ¼ì²éRedis·þÎñÆ÷ÊÇ·ñÒѾ­Æô¶¯¡£ | + + + +| | Èç¹ûÓбØÒª£¬ÏìӦʽָ±ê»áÈ¡´ú³£¹æÖ¸±ê¡£ ÁíÍ⣬ÈκÎûÓб»Ã÷È·´¦ÀíµÄ `HealthIndicator` ¶¼»á±»×Ô¶¯°ü×°ÆðÀ´¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.groups)2.8.5\. Health·Ö×飨Health Groups£© + + + +ÓÐʱ£¬½«½¡¿µÖ¸±ê×éÖ¯³É¿ÉÒÔÓÃÓÚ²»Í¬Ä¿µÄµÄ×éÊǺÜÓÐÓõġ£ + + + + + +Òª´´½¨Ò»¸ö½¡¿µÖ¸±ê×飬Äã¿ÉÒÔʹÓà `management.endpoint.health.group.` ÊôÐÔ£¬²¢Ö¸¶¨Ò»¸ö½¡¿µÖ¸±êIDÁбíÀ´ `include` »ò `exclude`¡£ ÀýÈ磬Ҫ´´½¨Ò»¸öÖ»°üÀ¨Êý¾Ý¿âÖ¸±êµÄ×飬Äã¿ÉÒÔ¶¨ÒåÈçÏ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.group.custom.include=db + +``` + + + + + + + +È»ºóÄã¿ÉÒÔͨ¹ýµã»÷ `[localhost:8080/actuator/health/custom](http://localhost:8080/actuator/health/custom)` À´¼ì²é½á¹û¡£ + + + + + +ͬÑù£¬Òª´´½¨Ò»¸ö×飬½«Êý¾Ý¿âÖ¸±êÅųýÔÚ¸Ã×éÖ®Í⣬²¢°üÀ¨ËùÓÐÆäËûÖ¸±ê£¬Äã¿ÉÒÔ¶¨ÒåÈçÏ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.group.custom.exclude=db + +``` + + + + + + + +ĬÈÏÇé¿öÏ£¬×é¼Ì³ÐÁËÓëϵͳ½¡¿µÏàͬµÄ `StatusAggregator` ºÍ `HttpCodeStatusMapper` ÉèÖᣠȻ¶ø£¬ÄãÒ²¿ÉÒÔÔÚÿ¸ö×éµÄ»ù´¡É϶¨ÒåÕâЩ¡£ Èç¹ûÐèÒª£¬ÄãÒ²¿ÉÒÔ¸²¸Ç `show-details` ºÍ `roles` ÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.group.custom.show-details=when-authorized +management.endpoint.health.group.custom.roles=admin +management.endpoint.health.group.custom.status.order=fatal,up +management.endpoint.health.group.custom.status.http-mapping.fatal=500 +management.endpoint.health.group.custom.status.http-mapping.out-of-service=500 + +``` + + + + + + + +| | Èç¹ûÄãÐèҪע²á×Ô¶¨ÒåµÄ `StatusAggregator` »ò `HttpCodeStatusMapper` BeanÒÔÓÃÓÚ×飬Äã¿ÉÒÔʹÓà `@Qualifier("groupname")`¡£ | +| --- | --- | + + + + + +Ò»¸ö½¡¿µ×éÒ²¿ÉÒÔ°üÀ¨/ÅųýÒ»¸ö `CompositeHealthContributor`¡£ ÄúÒ²¿ÉÒÔÖ»°üÀ¨/ÅųýÒ»¸ö `CompositeHealthContributor` µÄij¸ö×é¼þ¡£ Õâ¿ÉÒÔʹÓÃ×é¼þµÄÍêÈ«Ãû³ÆÀ´Íê³É£¬ÈçÏÂËùʾ¡£ + + + + + + + +``` +management.endpoint.health.group.custom.include="test/primary" +management.endpoint.health.group.custom.exclude="test/primary/b" +``` + + + + + + + +ÔÚÉÏÃæµÄÀý×ÓÖУ¬`custom` ×齫°üÀ¨Ãû³ÆÎª `primary` µÄ `HealthContributor`£¬ËüÊǸ´ºÏ `test` µÄÒ»¸ö×é³É²¿·Ö¡£ ÔÚÕâÀ`primary` ±¾Éí¾ÍÊÇÒ»¸ö¸´ºÏÌ壬Ãû×ÖΪ `b` µÄ `HealthContributor` ½«±»ÅųýÔÚ `custom` ×éÖ®Íâ¡£ + + + + + +½¡¿µ×é¿ÉÒÔÔÚÖ÷¶Ë¿Ú»ò¹ÜÀí¶Ë¿ÚµÄ¶îÍâ·¾¶ÉÏÌṩ¡£ ÕâÔÚKubernetesµÈÔÆ»·¾³ÖкÜÓÐÓã¬ÔÚÕâЩ»·¾³ÖУ¬³öÓÚ°²È«¿¼ÂÇ£¬ÎªÖ´ÐÐÆ÷¶ËµãʹÓÃÒ»¸öµ¥¶ÀµÄ¹ÜÀí¶Ë¿ÚÊǺܳ£¼ûµÄ¡£ ÓÐÒ»¸öµ¥¶ÀµÄ¶Ë¿Ú¿ÉÄܵ¼Ö²»¿É¿¿µÄ½¡¿µ¼ì²é£¬ÒòΪ¼´Ê¹½¡¿µ¼ì²é³É¹¦£¬Ö÷Ó¦ÓóÌÐòÒ²¿ÉÄÜÎÞ·¨Õý³£¹¤×÷¡£ ½¡¿µ×é¿ÉÒÔÓÃÒ»¸ö¶îÍâµÄ·¾¶½øÐÐÅäÖã¬ÈçÏÂËùʾ¡£ + + + + + + + +``` +management.endpoint.health.group.live.additional-path="server:/healthz" +``` + + + + + + + +Õ⽫ʹ `live` ½¡¿µ×éÔÚÖ÷·þÎñÆ÷¶Ë¿Ú `/healthz` ÉÏ¿ÉÓᣠǰ׺ÊÇÇ¿ÖÆÐԵ쬱ØÐëÊÇ `server:`£¨´ú±íÖ÷·þÎñÆ÷¶Ë¿Ú£©»ò `management:`£¨´ú±í¹ÜÀí¶Ë¿Ú£¬Èç¹ûÒÑÅäÖã©¡£ ·¾¶±ØÐëÊÇÒ»¸öµ¥Ò»µÄ·¾¶¶Î¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.datasource)2.8.6\. Êý¾ÝÔ´½¡¿µ + + + +`DataSource` ½¡¿µÖ¸±êÏÔʾ±ê×¼Êý¾ÝÔ´ºÍ·ÓÉÊý¾ÝÔ´BeanµÄ½¡¿µ×´¿ö¡£ ·ÓÉÊý¾ÝÔ´µÄ½¡¿µ×´¿ö°üÀ¨Æäÿ¸öÄ¿±êÊý¾ÝÔ´µÄ½¡¿µ×´¿ö¡£ ÔÚ½¡¿µ¶ËµãµÄÏìÓ¦ÖУ¬Â·ÓÉÊý¾ÝÔ´µÄÿ¸öÄ¿±ê¶¼ÊÇͨ¹ýʹÓÃÆä·ÓɼüÀ´ÃüÃûµÄ¡£ Èç¹ûÄ㲻ϣÍûÔÚÖ¸±êµÄÊä³öÖаüÀ¨Â·ÓÉÊý¾ÝÔ´£¬Ç뽫 `management.health.db.ignore-routing-data-sources` ÉèÖÃΪ `true`¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.kubernetes-probes)2.9\. Kubernetes ̽Õë + + + +²¿ÊðÔÚKubernetesÉϵÄÓ¦ÓóÌÐò¿ÉÒÔͨ¹ý [ÈÝÆ÷̽Õë](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) ÌṩÓÐ¹ØÆäÄÚ²¿×´Ì¬µÄÐÅÏ¢¡£¸ù¾Ý [ÄãµÄKubernetesÅäÖÃ](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)£¬kubelet»áµ÷ÓÃÕâЩ̽Õë²¢¶Ô½á¹û×ö³ö·´Ó¦¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Spring Boot»á¹ÜÀíÄãµÄ[Ó¦ÓÿÉÓÃÐÔ״̬](https://springdoc.cn/spring-boot/features.html#features.spring-application.application-availability)¡£ Èç¹û²¿ÊðÔÚKubernetes»·¾³ÖУ¬actuator´Ó `ApplicationAvailability` ½Ó¿ÚÖÐÊÕ¼¯ ¡°Liveness¡± ºÍ ¡°Readiness¡± ÐÅÏ¢£¬²¢ÔÚרÓÃ[½¡¿µÖ¸±ê](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.auto-configured-health-indicators)ÖÐʹÓÃÕâЩÐÅÏ¢¡£`LivenessStateHealthIndicator` ºÍ `ReadinessStateHealthIndicator`¡£ ÕâЩָ±êÏÔʾÔÚÈ«¾Ö½¡¿µ¶Ëµã£¨`"/actuator/health"`£©¡£ ËüÃÇÒ²¿ÉÒÔͨ¹ýʹÓÃ[½¡¿µ×é](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.groups)×÷Ϊµ¥¶ÀµÄHTTP̽Õ룺`"/actuator/health/liveness"` ºÍ `"/actuator/health/readiness"`¡£ + + + + + +È»ºó£¬Äã¿ÉÒÔÓÃÒÔ϶˵ãÐÅÏ¢ÅäÖÃÄãµÄKubernetes»ù´¡ÉèÊ©¡£ + + + + + + + +``` +livenessProbe: + httpGet: + path: "/actuator/health/liveness" + port: + failureThreshold: ... + periodSeconds: ... + +readinessProbe: + httpGet: + path: "/actuator/health/readiness" + port: + failureThreshold: ... + periodSeconds: ... +``` + + + + + + + +| | `` Ó¦¸Ã±»ÉèÖÃΪִÐÐÆ÷¶Ëµã¿ÉÓõĶ˿ڡ£ Ëü¿ÉÒÔÊÇÖ÷Web·þÎñÆ÷µÄ¶Ë¿Ú£¬Ò²¿ÉÒÔÊÇÒ»¸öµ¥¶ÀµÄ¹ÜÀí¶Ë¿Ú£¬Èç¹û `"management.server.port"` ÊôÐÔÒѾ­±»ÉèÖᣠ| +| --- | --- | + + + + + +Ö»Óе±Ó¦ÓóÌÐò[ÔÚKubernetes»·¾³ÖÐÔËÐÐʱ](https://springdoc.cn/spring-boot/deployment.html#deployment.cloud.kubernetes)£¬ÕâЩ½¡¿µ×é²Å»á×Ô¶¯ÆôÓᣠÄã¿ÉÒÔͨ¹ýʹÓà `management.endpoint.health.probes.enabled` ÅäÖÃÊôÐÔÔÚÈκλ·¾³ÖÐÆôÓÃËüÃÇ¡£ + + + + + +| | Èç¹ûÒ»¸öÓ¦ÓóÌÐòµÄÆô¶¯Ê±¼ä³¬¹ýÁËÅäÖõÄÓÐЧÆÚ£¬Kubernetes »áÌáµ½ `"startupProbe"` ×÷Ϊһ¸ö¿ÉÄܵĽâ¾ö·½°¸¡£Ò»°ãÀ´Ëµ£¬ÕâÀï²»Ò»¶¨ÐèÒª `"startupProbe"`£¬ÒòΪ `"readinessProbe"` »áÔÚËùÓÐÆô¶¯ÈÎÎñÍê³É֮ǰʧЧ¡£ÕâÒâζ×ÅÄãµÄÓ¦ÓóÌÐòÔÚ×¼±¸ºÃ֮ǰ²»»áÊÕµ½Á÷Á¿¡£È»¶ø£¬Èç¹ûÄãµÄÓ¦ÓóÌÐòÐèÒªºÜ³¤Ê±¼ä²ÅÄÜÆô¶¯£¬¿ÉÒÔ¿¼ÂÇʹÓà `"startupProbe"` À´È·±£Kubernetes²»»áÔÚÄãµÄÓ¦ÓóÌÐòÆô¶¯¹ý³ÌÖÐɱËÀËü¡£Çë²ÎÔÄÃèÊö[̽ÕëÔÚÓ¦ÓóÌÐòÉúÃüÖÜÆÚÖеÄÐÐΪ](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.kubernetes-probes.lifecycle)µÄ²¿·Ö¡£ | +| --- | --- | + + + + + +Èç¹ûÄãµÄActuator¶Ëµã±»²¿ÊðÔÚÒ»¸öµ¥¶ÀµÄ¹ÜÀíÉÏÏÂÎÄÖУ¬ÄÇôÕâЩ¶Ëµã¾Í²»»áÏñÖ÷³ÌÐòÄÇÑùʹÓÃÏàͬµÄÍøÂç»ù´¡ÉèÊ©£¨¶Ë¿Ú¡¢Á¬½Ó³Ø¡¢¿ò¼Ü×é¼þ£©¡£ ÔÚÕâÖÖÇé¿öÏ£¬¼´Ê¹Ö÷³ÌÐò²»ÄÜÕý³£¹¤×÷£¨ÀýÈ磬Ëü²»ÄܽÓÊÜеÄÁ¬½Ó£©£¬Ì½²â¼ì²éÒ²¿ÉÄܳɹ¦¡£ ÓÉÓÚÕâ¸öÔ­Òò£¬ÔÚÖ÷·þÎñÆ÷¶Ë¿ÚÉÏÉèÖà `liveness` ºÍ `readiness` ½¡¿µ×éÊǸöºÃÖ÷Òâ¡£ Õâ¿ÉÒÔͨ¹ýÉèÖÃÒÔÏÂÊôÐÔÀ´ÊµÏÖ¡£ + + + + + + + +``` +management.endpoint.health.probes.add-additional-paths=true +``` + + + + + + + +Õ⽫ʹ `liveness` ÔÚ `/livez` ¿ÉÓã¬`readiness` ÔÚ `readyz` µÄÖ÷·þÎñÆ÷¶Ë¿Ú¿ÉÓᣠ+ + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.kubernetes-probes.external-state)2.9.1\. ÓÃKubernetes̽Õë¼ì²éÍⲿ״̬ + + + +Ö´ÐÐÆ÷½« ¡°liveness¡± ºÍ ¡°readiness¡± ̽ÕëÅäÖÃΪ½¡¿µ×é¡£ÕâÒâζ×ÅËùÓеÄ[½¡¿µ×éµÄ¹¦ÄÜ](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.health.groups)¶ÔËüÃǶ¼ÊÇ¿ÉÓõġ£ÀýÈ磬Äã¿ÉÒÔÅäÖöîÍâµÄ½¡¿µÖ¸±ê¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoint.health.group.readiness.include=readinessState,customCheck + +``` + + + + + + + +ĬÈÏÇé¿öÏ£¬Spring Boot²»»áÏòÕâЩ×éÌí¼ÓÆäËû½¡¿µÖ¸±ê¡£ + + + + + +¡°liveness¡± ̽Õë²»Ó¦¸ÃÒÀÀµÓÚÍⲿϵͳµÄ½¡¿µ¼ì²é¡£Èç¹û[Ó¦ÓóÌÐòµÄÓÐЧÐÔ״̬](https://springdoc.cn/spring-boot/features.html#features.spring-application.application-availability.liveness)±»ÆÆ»µ£¬Kubernetes»á³¢ÊÔͨ¹ýÖØÐÂÆô¶¯Ó¦ÓóÌÐòʵÀýÀ´½â¾öÕâ¸öÎÊÌâ¡£ÕâÒâζ×Å£¬Èç¹ûÒ»¸öÍⲿϵͳ£¨ÈçÊý¾Ý¿â¡¢Web API»òÍⲿ»º´æ£©³öÏÖ¹ÊÕÏ£¬Kubernetes¿ÉÄÜ»áÖØÐÂÆô¶¯ËùÓÐÓ¦ÓóÌÐòʵÀý£¬²¢²úÉú¼¶Áª¹ÊÕÏ¡£ + + + + + +ÖÁÓÚ ¡°readiness¡± ̽²â£¬¼ì²éÍⲿϵͳµÄÑ¡Ôñ±ØÐëÓÉÓ¦ÓóÌÐò¿ª·¢ÈËÔ±½÷É÷×÷³ö¡£³öÓÚÕâ¸öÔ­Òò£¬Spring BootÔÚ×¼±¸×´Ì¬Ì½²âÖв»°üÀ¨ÈκζîÍâµÄ½¡¿µ¼ì²é¡£Èç¹û[Ó¦ÓóÌÐòʵÀýµÄreadiness stateÊÇunready](https://springdoc.cn/spring-boot/features.html#features.spring-application.application-availability.readiness)£¬Kubernetes¾Í²»»á½«Á÷Á¿Â·Óɵ½¸ÃʵÀý¡£Ò»Ð©Íⲿϵͳ¿ÉÄܲ»±»Ó¦ÓÃʵÀýËù¹²Ïí£¬ÔÚÕâÖÖÇé¿öÏ£¬ËüÃÇ¿ÉÒÔ±»°üÀ¨ÔÚ×¼±¸×´Ì¬Ì½²âÖС£ÆäËûÍⲿϵͳ¿ÉÄܲ»ÊÇÓ¦ÓóÌÐòµÄ¹Ø¼ü£¨Ó¦ÓóÌÐò¿ÉÄÜÓжÏ·Æ÷ºÍ»ØÍË£©£¬ÔÚÕâÖÖÇé¿öÏ£¬ËüÃǾø¶Ô²»Ó¦¸Ã±»°üÀ¨ÔÚÄÚ¡£²»ÐÒµÄÊÇ£¬Ò»¸ö±»ËùÓÐÓ¦ÓÃʵÀý¹²ÏíµÄÍⲿϵͳºÜ³£¼û£¬Äã±ØÐë×ö³öÅжϡ£°ÑËü°üÀ¨ÔÚ×¼±¸¾ÍÐ÷̽ÕëÖУ¬²¢ÆÚÍûµ±Íⲿ·þÎñ·¢Éú¹ÊÕÏʱ£¬Ó¦ÓóÌÐò»á±»Í£Ö¹·þÎñ£¬»òÕß²»°üÀ¨Ëü£¬²¢´¦Àí¶ÑÕ»Öиü¸ß²ã´ÎµÄ¹ÊÕÏ£¬Ò²Ðíͨ¹ýÔÚµ÷ÓÃÕßÖÐʹÓöÏ·Æ÷¡£ + + + + + +| | Èç¹ûÒ»¸öÓ¦ÓóÌÐòµÄËùÓÐʵÀý¶¼Ã»ÓÐ×¼±¸ºÃ£¬`type=ClusterIP` »ò `NodePort` µÄKubernetes·þÎñ²»½ÓÊÜÈκδ«ÈëÁ¬½Ó¡£Ã»ÓÐHTTP´íÎóÏìÓ¦£¨503µÈ£©£¬ÒòΪûÓÐÁ¬½Ó¡£`type=LoadBalancer` µÄ·þÎñ¿ÉÄܽÓÊÜÒ²¿ÉÄܲ»½ÓÊÜÁ¬½Ó£¬ÕâÈ¡¾öÓÚÌṩÕß¡£Ò»¸öÓÐÃ÷È· [ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) µÄ·þÎñÒ²»áÒÔÒ»ÖÖÈ¡¾öÓÚʵÏֵķ½Ê½½øÐÐÏìÓ¦¡ª?Èë¿Ú·þÎñ±¾Éí±ØÐë¾ö¶¨ÈçºÎ´¦ÀíÀ´×ÔÏÂÓ뵀 ¡°connection refused¡±¡£ÔÚ¸ºÔؾùºâÆ÷ºÍÈë¿ÚµÄÇé¿öÏ£¬HTTP 503ÊǺÜÓпÉÄܵġ£ | +| --- | --- | + + + + + +ÁíÍ⣬Èç¹ûÒ»¸öÓ¦ÓóÌÐòʹÓà Kubernetes [autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)£¬Ëü¿ÉÄÜ»á¶ÔÓ¦ÓóÌÐò±»´Ó¸ºÔØÆ½ºâÆ÷ÖÐÈ¡³ö×ö³ö²»Í¬µÄ·´Ó¦£¬ÕâÈ¡¾öÓÚÆäautoscalerµÄÅäÖᣠ+ + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.kubernetes-probes.lifecycle)2.9.2\. Ó¦ÓóÌÐòÉúÃüÖÜÆÚºÍ̽Õë״̬ + + + +Kubernetes ProbesÖ§³ÖµÄÒ»¸öÖØÒª·½ÃæÊÇËüÓëÓ¦ÓóÌÐòÉúÃüÖÜÆÚµÄÒ»ÖÂÐÔ¡£ `AvailabilityState`£¨¼´Ó¦ÓóÌÐòµÄÄÚ´æÄÚ²¿×´Ì¬£©ºÍʵ¼ÊµÄ̽Õ루±©Â¶¸Ã״̬£©Ö®¼ä´æÔÚ×ÅÏÔÖøµÄÇø±ð¡£ ºÍʵ¼ÊµÄ̽Õ루±©Â¶Á˸Ã״̬£©Ö®¼äÓкܴóµÄÇø±ð¡£ ¸ù¾ÝÓ¦ÓóÌÐòÉúÃüÖÜÆÚµÄ²»Í¬½×¶Î£¬Ì½Õë¿ÉÄÜÎÞ·¨Ê¹Óᣠ+ + + + + +Spring BootÔÚ[Æô¶¯ºÍ¹Ø±ÕÆÚ¼ä·¢²¼application event](https://springdoc.cn/spring-boot/features.html#features.spring-application.application-events-and-listeners)£¬Ì½Õë¿ÉÒÔ¼àÌýÕâЩʼþ²¢±©Â¶³ö `AvailabilityState` ÐÅÏ¢¡£ + + + + + +ϱíÏÔʾÁ˲»Í¬½×¶ÎµÄ `AvailabilityState` ºÍHTTP connectorµÄ״̬¡£ + + + + + +µ±Ò»¸öSpring BootÓ¦ÓóÌÐòÆô¶¯Ê±¡£ + + + + +| Æô¶¯½×¶Î | LivenessState | ReadinessState | HTTP server | ±¸×¢ | +| --- | --- | --- | --- | --- | +| Starting | `BROKEN` | `REFUSING_TRAFFIC` | δÆô¶¯ | Kubernetes¼ì²é "liveness" ̽Õ룬Èç¹ûʱ¼ä¹ý³¤£¬¾ÍÖØÐÂÆô¶¯Ó¦ÓóÌÐò¡£ | +| Started | `CORRECT` | `REFUSING_TRAFFIC` | ¾Ü¾øÇëÇó | Ó¦ÓóÌÐòÉÏÏÂÎı»Ë¢Ð¡£Ó¦ÓóÌÐòÖ´ÐÐÆô¶¯ÈÎÎñ£¬»¹Ã»ÓÐÊÕµ½Á÷Á¿¡£ | +| Ready | `CORRECT` | `ACCEPTING_TRAFFIC` | ½ÓÊÜÇëÇó | Æô¶¯ÈÎÎñÒѾ­Íê³É¡£¸ÃÓ¦ÓóÌÐòÕýÔÚ½ÓÊÕÁ÷Á¿¡£ | + + + +µ±Ò»¸öSpring BootÓ¦ÓóÌÐò¹Ø±Õʱ¡£ + + + + +| Í£»ú½×¶Î | Liveness State | Readiness State | HTTP server | ±¸×¢ | +| --- | --- | --- | --- | --- | +| Running | `CORRECT` | `ACCEPTING_TRAFFIC` | ½ÓÊÜÇëÇó | ÒÑÒªÇ󹨱ա£ | +| Graceful shutdown | `CORRECT` | `REFUSING_TRAFFIC` | еÄÇëÇ󱻾ܾø | Èç¹ûÆôÓ㬠[ÓÅÑŹػú»á´¦Àí¡°´¦ÀíÖС±µÄÇëÇó](https://springdoc.cn/spring-boot/web.html#web.graceful-shutdown)¡£ | +| Shutdown complete | N/A | N/A | ·þÎñÆ÷±»¹Ø±Õ | Ó¦ÓóÌÐòÉÏÏÂÎı»¹Ø±Õ£¬Ó¦ÓóÌÐò±»¹Ø±Õ¡£ | + + + +| | ¹ØÓÚKubernetes²¿ÊðµÄ¸ü¶àÐÅÏ¢£¬Çë²Î¼û[KubernetesÈÝÆ÷ÉúÃüÖÜÆÚ](https://springdoc.cn/spring-boot/deployment.html#deployment.cloud.kubernetes.container-lifecycle)²¿·Ö¡£ | +| --- | --- | + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info)2.10\. Ó¦ÓÃÐÅÏ¢ + + + +Ó¦ÓóÌÐòÐÅÏ¢¹«¿ªÁË´ÓÄãµÄ `ApplicationContext` Öж¨ÒåµÄËùÓÐ [`InfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoContributor.java) BeanÊÕ¼¯µÄ¸÷ÖÖÐÅÏ¢¡£ Spring Boot°üÀ¨Ò»Ð©×Ô¶¯ÅäÖÃµÄ `InfoContributor` Bean£¬ÄãÒ²¿ÉÒÔ±àд×Ô¼ºµÄ¡£ + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.auto-configured-info-contributors)2.10.1\. ×Ô¶¯ÅäÖÃµÄ InfoContributor + + + +ÔÚÊʵ±µÄʱºò£¬Spring»á×Ô¶¯ÅäÖÃÒÔÏ `InfoContributor` Bean¡£ + + + + +| ID | Name | ˵Ã÷ | ǰÌáÌõ¼þ | +| --- | --- | --- | --- | +| `build` | [`BuildInfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/BuildInfoContributor.java) | ±©Â¶Á˹¹½¨ÐÅÏ¢¡£ | Ò»¸ö `META-INF/build-info.properties` ×ÊÔ´¡£ | +| `env` | [`EnvironmentInfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/EnvironmentInfoContributor.java) | ±©Â¶ `Environment` ÖÐÃû³ÆÒÔ `info.` ¿ªÍ·µÄÈκÎÊôÐÔ¡£ | None. | +| `git` | [`GitInfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/GitInfoContributor.java) | ±©Â¶ÁËgitÐÅÏ¢¡£ | Ò»¸ö `git.properties` ×ÊÔ´¡£ | +| `java` | [`JavaInfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/JavaInfoContributor.java) | ±©Â¶JavaÔËÐÐʱ£¨Runtime£©ÐÅÏ¢¡£ | None. | +| `os` | [`OsInfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/OsInfoContributor.java) | ±©Â¶²Ù×÷ϵͳÐÅÏ¢¡£ | None. | + + + +¸öÈ˹±Ï×Õߣ¨contributor£©ÊÇ·ñ±»ÆôÓÃÊÇÓÉÆä `management.info..enabled` ÊôÐÔ¿ØÖÆ¡£ ²»Í¬µÄcontributor¶ÔÕâ¸öÊôÐÔÓв»Í¬µÄĬÈÏÖµ£¬ÕâÈ¡¾öÓÚËûÃǵÄÏȾöÌõ¼þºÍËûÃÇËù±©Â¶µÄÐÅÏ¢µÄÐÔÖÊ¡£ + + + + + +ÓÉÓÚûÓÐÏȾöÌõ¼þ±íÃ÷ËüÃÇÓ¦¸Ã±»ÆôÓã¬`env`¡¢`java` ºÍ `os` contributor ĬÈÏÊǽûÓõġ£ ¿ÉÒÔͨ¹ýÉèÖà `management.info..enabled` ÊôÐÔΪ `true` À´ÆôÓÃËüÃÇ¡£ + + + + + +`build` ºÍ `git` ÐÅÏ¢contributorĬÈÏÊÇÆôÓõġ£ ¿ÉÒÔͨ¹ý½«Æä `management.info..enabled` ÊôÐÔÉèÖÃΪ `false` À´½ûÓᣠÁíÍ⣬Ҫ½ûÓÃÿһ¸öĬÈÏÆôÓõÄcontributor£¬Ç뽫 `management.info.defaults.enabled` ÊôÐÔÉèΪ `false`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.custom-application-information)2.10.2\. ×Ô¶¨ÒåÓ¦ÓÃÐÅÏ¢£¨Application Information£© + + + +µ± `env` contributor ±»ÆôÓÃʱ£¬Äã¿ÉÒÔͨ¹ýÉèÖà `info.*` SpringÊôÐÔÀ´¶¨ÖÆ `info` ¶ËµãËù±©Â¶µÄÊý¾Ý¡£ `info` keyϵÄËùÓÐ `Environment` ÊôÐÔ¶¼»á×Ô¶¯±©Â¶¡£ ÀýÈ磬Äã¿ÉÒÔÔÚÄãµÄ `application.properties` ÎļþÖÐÌí¼ÓÒÔÏÂÉèÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +info.app.encoding=UTF-8 +info.app.java.source=17 +info.app.java.target=17 + +``` + + + + + + + +| | ÓëÆäÓ²±àÂëÕâЩֵ£¬Ä㻹¿ÉÒÔ [ÔÚ¹¹½¨Ê±À©Õ¹ÐÅÏ¢ÊôÐÔ](https://springdoc.cn/spring-boot/howto.html#howto.properties-and-configuration.expand-properties)¡£¼ÙÉèÄãʹÓÃMaven£¬Äã¿ÉÒÔ½«Ç°ÃæµÄÀý×Ó¸ÄдÈçÏ¡£PropertiesYaml```info.app.encoding=@project.build.sourceEncoding@info.app.java.source=@java.version@info.app.java.target=@java.version@``` | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.git-commit-information)2.10.3\. Git Commit ÐÅÏ¢ + + + +`info` ¶ËµãµÄÁíÒ»¸öÓÐÓõŦÄÜÊÇËüÄܹ»·¢²¼¹ØÓÚÄãµÄ `git` Ô´´úÂë¿âÔÚÏîÄ¿½¨Á¢Ê±µÄ״̬µÄÐÅÏ¢¡£ Èç¹ûÓÐÒ»¸ö `GitProperties` bean£¬Äã¿ÉÒÔʹÓà `info` ¶ËµãÀ´¹«²¼ÕâЩÊôÐÔ¡£ + + + + + +| | Èç¹ûclasspathµÄ¸ù²¿ÓÐ `git.properties` Îļþ£¬`GitProperties` Bean¾Í»á±»×Ô¶¯ÅäÖ᣸ü¶àϸ½Ú¼û "[ÈçºÎÉú³ÉgitÐÅÏ¢](https://springdoc.cn/spring-boot/howto.html#howto.build.generate-git-info)"¡£ | +| --- | --- | + + + + + +ĬÈÏÇé¿öÏ£¬¶Ëµã»á±©Â¶ `git.branch`¡¢`git.commit.id` ºÍ `git.commit.time` ÊôÐÔ£¨Èç¹û´æÔÚ£©¡£ Èç¹ûÄã²»ÏëÈÃÕâЩÊôÐÔ³öÏÖÔڶ˵ãÏìÓ¦ÖУ¬ÐèÒª´Ó `git.properties` ÎļþÖÐÅųýËüÃÇ¡£ Èç¹ûÄãÏëÏÔʾÍêÕûµÄgitÐÅÏ¢£¨¼´ `git.properties` µÄÈ«²¿ÄÚÈÝ£©£¬Ê¹Óà `management.info.git.mode` ÊôÐÔ£¬ÈçÏÂËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.info.git.mode=full + +``` + + + + + + + +Òª´Ó `info` ¶ËµãÍêÈ«½ûÓÃgitÌá½»ÐÅÏ¢£¬Çë `management.info.git.enabled` ÊôÐÔÉèΪ `false`£¬ÈçÏÂËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.info.git.enabled=false + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.build-information)2.10.4\. ¹¹½¨ÐÅÏ¢ + + + +Èç¹û `BuildProperties` BeanÊÇ¿ÉÓõģ¬`info` ¶ËµãÒ²¿ÉÒÔ·¢²¼¹ØÓÚÄãµÄ¹¹½¨ÐÅÏ¢¡£Èç¹ûclasspathÖÐµÄ `META-INF/build-info.properties` Îļþ¿ÉÓ㬾ͻᷢÉúÕâÖÖÇé¿ö¡£ + + + + + +| | MavenºÍGradle²å¼þ¶¼¿ÉÒÔÉú³É¸ÃÎļþ¡£Ïê¼û "[ÈçºÎÉú³É¹¹½¨ÐÅÏ¢](https://springdoc.cn/spring-boot/howto.html#howto.build.generate-info)"¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.java-information)2.10.5\. JavaÐÅÏ¢ + + + +`info` ¶Ëµã·¢²¼Á˹ØÓÚÄãµÄJavaÔËÐл·¾³µÄÐÅÏ¢£¬¸ü¶àϸ½Ú¼û [`JavaInfo`](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/api/org/springframework/boot/info/JavaInfo.html)¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.os-information)2.10.6\. ²Ù×÷ϵͳ£¨OS£©ÐÅÏ¢ + + + +`info` ¶Ëµã·¢²¼¹ØÓÚÄãµÄ²Ù×÷ϵͳµÄÐÅÏ¢£¬¸ü¶àϸ½Ú¼û [`OsInfo`](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/api/org/springframework/boot/info/OsInfo.html)`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.info.writing-custom-info-contributors)2.10.7\. ±àд×Ô¶¨Òå InfoContributor + + + +ΪÁËÌṩ×Ô¶¨ÒåµÄÓ¦ÓóÌÐòÐÅÏ¢£¬Äã¿ÉÒÔ×¢²áʵÏÖ [`InfoContributor`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoContributor.java) ½Ó¿ÚµÄSpring Bean¡£ + + + + + +ÏÂÃæµÄÀý×Ó¹±Ï×ÁËÒ»¸öÖ»ÓÐÒ»¸öÖµµÄ `example` ÌõÄ¿¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Component +public class MyInfoContributor implements InfoContributor { + + @Override + public void contribute(Info.Builder builder) { + builder.withDetail("example", Collections.singletonMap("key", "value")); + } + +} + +``` + + + + + + + +Èç¹ûÄãÇëÇó `info` ¶Ëµã£¬ÄãÓ¦¸Ã¿´µ½Ò»¸ö°üº¬ÒÔ϶îÍâÌõÄ¿µÄÏìÓ¦¡£ + + + + + + + +``` +{ + "example": { + "key" : "value" + } +} +``` + + + + + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring)3\. ͨ¹ýHTTP½øÐÐ¼à¿ØºÍ¹ÜÀí + + + + + +Èç¹ûÄãÕýÔÚ¿ª·¢Ò»¸öWebÓ¦ÓóÌÐò£¬Spring Boot Actuator»á×Ô¶¯ÅäÖÃËùÓÐÆôÓõĶ˵㣬ʹÆäͨ¹ýHTTP¹«¿ª¡£ ĬÈϵĹßÀýÊÇʹÓö˵ãµÄ `id` ºÍ `/actuator` µÄǰ׺×÷ΪURL·¾¶¡£ ÀýÈ磬`health` ÒÔ `/actuator/health` µÄÐÎʽ¹«¿ª¡£ + + + + + +| | Actuator Ö§³Ö Spring MVC¡¢Spring WebFluxºÍJersey¡£ Èç¹ûJerseyºÍSpring MVC¶¼¿ÉÓã¬Ôò»áʹÓÃSpring MVC¡£ | +| --- | --- | + + + + + +| | ΪÁË»ñµÃAPIÎĵµ£¨ [HTML](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/htmlsingle) »ò [PDF](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/actuator-api/pdf/spring-boot-actuator-web-api.pdf) £©ÖмÇÔØµÄÕýÈ·µÄJSONÏìÓ¦£¬JacksonÊÇÒ»¸ö±ØÒªµÄÒÀÀµ¡£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.customizing-management-server-context-path)3.1\. ¶¨ÖƹÜÀí¶Ëµã·¾¶ + + + +ÓÐʱ£¬Îª¹ÜÀí¶Ëµã¶¨ÖÆÇ°×ºÊǺÜÓÐÓõġ£ ÀýÈ磬ÄãµÄÓ¦ÓóÌÐò¿ÉÄÜÒѾ­½« `/actuator` ÓÃÓÚÆäËûÄ¿µÄ¡£ Äã¿ÉÒÔʹÓà `management.endpoints.web.base-path` ÊôÐÔÀ´¸Ä±ä¹ÜÀí¶ËµãµÄǰ׺£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.base-path=/manage + +``` + + + + + + + +Ç°ÃæµÄ `application.properties` Àý×Ó½«¶Ëµã´Ó `/actuator/{id}` ¸ÄΪ `/manage/{id}` £¨ÀýÈ磬`/manage/info`£©¡£ + + + + + +| | ³ý·Ç¹ÜÀí¶Ë¿Ú±»ÅäÖÃΪ[ʹÓò»Í¬µÄHTTP¶Ë¿Ú](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.customizing-management-server-port)À´±©Â¶¶Ëµã£¬·ñÔò `management.endpoints.web.base-path` ÊÇÏà¶ÔÓÚ `server.servlet.context-path` £¨ÓÃÓÚServlet WebÓ¦Óã©»ò `spring.webflux.base-path` £¨ÓÃÓÚreactive WebÓ¦Óã©¡£Èç¹ûÅäÖÃÁË `management.server.port`£¬ `management.endpoints.web.base-path` ÊÇÏà¶ÔÓÚ `management.server.base-path` µÄ¡£ | +| --- | --- | + + + + + +Èç¹ûÄãÏë°Ñ¶ËµãÓ³Éäµ½²»Í¬µÄ·¾¶£¬Äã¿ÉÒÔʹÓà `management.endpoints.web.path-mapping` ÊôÐÔ¡£ + + + + + +ÏÂÃæµÄÀý×Ó½« `/actuator/health` ÖØÐÂÓ³ÉäΪ `/healthcheck`¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.base-path=/ +management.endpoints.web.path-mapping.health=healthcheck + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.customizing-management-server-port)3.2\. ¶¨ÖƹÜÀí·þÎñÆ÷¶Ë¿Ú + + + +¶ÔÓÚ»ùÓÚÔÆµÄ²¿ÊðÀ´Ëµ£¬Í¨¹ýʹÓÃĬÈϵÄHTTP¶Ë¿ÚÀ´±©Â¶¹ÜÀí¶ËµãÊÇÒ»¸öÃ÷ÖǵÄÑ¡Ôñ¡£ È»¶ø£¬Èç¹ûÄãµÄÓ¦ÓóÌÐòÔÚÄã×Ô¼ºµÄÊý¾ÝÖÐÐÄÄÚÔËÐУ¬Äã¿ÉÄܸüϲ»¶Ê¹Óò»Í¬µÄHTTP¶Ë¿ÚÀ´±©Â¶¶Ëµã¡£ + + + + + +Äã¿ÉÒÔÉèÖà `management.server.port` ÊôÐÔÀ´¸Ä±äHTTP¶Ë¿Ú£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.server.port=8081 + +``` + + + + + + + +| | ÔÚ Cloud Foundry ÉÏ£¬Ä¬ÈÏÇé¿öÏ£¬Ó¦ÓóÌÐò½öÔÚ¶Ë¿Ú 8080 ÉϽÓÊÕ HTTP ºÍ TCP ·ÓɵÄÇëÇó¡£ Èç¹ûÄúÏëÔÚ Cloud Foundry ÉÏʹÓÃ×Ô¶¨Òå¹ÜÀí¶Ë¿Ú£¬ÄúÐèÒªÃ÷È·ÉèÖÃÓ¦ÓóÌÐòµÄ·ÓÉÒÔ½«Á÷Á¿×ª·¢µ½×Ô¶¨Òå¶Ë¿Ú¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.management-specific-ssl)3.3\. ÅäÖÃÕë¶ÔManagement£¨¹ÜÀí£©²ãµÄSSL + + + +µ±ÅäÖÃΪʹÓÃ×Ô¶¨Òå¶Ë¿Úʱ£¬ÄãÒ²¿ÉÒÔͨ¹ýʹÓø÷ÖÖ `management.server.ssl.*` ÊôÐÔÀ´ÅäÖùÜÀí·þÎñÆ÷µÄSSL¡£ ÀýÈ磬ÕâÑù×ö¿ÉÒÔÈùÜÀí·þÎñÆ÷ͨ¹ýHTTPÌṩ·þÎñ£¬¶øÖ÷Ó¦ÓóÌÐòʹÓÃHTTPS£¬ÈçÒÔÏÂÊôÐÔÉèÖÃËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +server.port=8443 +server.ssl.enabled=true +server.ssl.key-store=classpath:store.jks +server.ssl.key-password=secret +management.server.port=8080 +management.server.ssl.enabled=false + +``` + + + + + + + +»òÕߣ¬Ö÷·þÎñÆ÷ºÍ¹ÜÀí·þÎñÆ÷¶¼¿ÉÒÔʹÓÃSSL£¬µ«Ê¹Óò»Í¬µÄÃÜÔ¿´æ´¢£¬ÈçÏÂËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +server.port=8443 +server.ssl.enabled=true +server.ssl.key-store=classpath:main.jks +server.ssl.key-password=secret +management.server.port=8080 +management.server.ssl.enabled=true +management.server.ssl.key-store=classpath:management.jks +management.server.ssl.key-password=secret + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.customizing-management-server-address)3.4\. ¶¨ÖÆManagement£¨¹ÜÀí£©·þÎñÆ÷µØÖ· + + + +Äã¿ÉÒÔͨ¹ýÉèÖà `management.server.address` ÐÔÀ´¶¨ÖƹÜÀí¶ËµãµÄ¿ÉÓõØÖ·¡£ Èç¹ûÄãÏëÖ»ÔÚÄÚ²¿»òÃæÏòÔËάµÄÍøÂçÉϼàÌý£¬»òÕßÖ»¼àÌýÀ´×Ô `localhost` µÄÁ¬½Ó£¬ÕâÑù×ö»áºÜÓÐÓᣠ+ + + + + +| | Ö»Óе±¶Ë¿ÚÓëÖ÷·þÎñÆ÷¶Ë¿Ú²»Í¬Ê±£¬Äã²ÅÄÜÔÚ²»Í¬µÄµØÖ·ÉϽøÐмàÌý¡£ | +| --- | --- | + + + + + +ÏÂÃæµÄÀý×Ó `application.properties` ²»ÔÊÐíÔ¶³Ì¹ÜÀíÁ¬½Ó¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.server.port=8081 +management.server.address=127.0.0.1 + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.monitoring.disabling-http-endpoints)3.5\. ½ûÓÃHTTP¶Ëµã + + + +Èç¹ûÄã²»Ïëͨ¹ýHTTP±©Â¶¶Ëµã£¬Äã¿ÉÒ԰ѹÜÀí¶Ë¿ÚÉèÖÃΪ `-1`£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.server.port=-1 + +``` + + + + + + + +ÄãÒ²¿ÉÒÔͨ¹ýʹÓà `management.endpoints.web.exposure.exclude` ÊôÐÔÀ´ÊµÏÖ£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.web.exposure.exclude=* + +``` + + + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.jmx)4\. ͨ¹ýJMX½øÐÐ¼à¿ØºÍ¹ÜÀí + + + + + +Java¹ÜÀíÀ©Õ¹£¨JMX£©ÌṩÁËÒ»¸ö±ê×¼µÄ»úÖÆÀ´¼à¿ØºÍ¹ÜÀíÓ¦ÓóÌÐò¡£ ĬÈÏÇé¿öÏ£¬¸Ã¹¦ÄÜδ±»ÆôÓᣠÄã¿ÉÒÔͨ¹ýÉèÖà `spring.jmx.enabled` ÅäÖÃÊôÐÔΪ `true` À´´ò¿ªËü¡£ Spring Boot½«×îºÏÊ浀 `MBeanServer` ×÷ΪIDΪ `mbeanServer` µÄBean¹«¿ª¡£ ÄãµÄÈκδøÓÐSpring JMX×¢½âµÄBean£¨`@ManagedResource`¡¢`@ManagedAttribute` »ò `@ManagedOperation`£©¶¼»á±©Â¶¸øËü¡£ + + + + + +Èç¹ûÄãµÄƽ̨ÌṩÁËÒ»¸ö±ê×¼µÄ `MBeanServer` £¬Spring Boot»áʹÓÃËü£¬²¢ÔÚ±ØÒªÊ±Ä¬ÈÏΪVM `MBeanServer`¡£ Èç¹ûËùÓÐÕâЩ¶¼Ê§°ÜÁË£¬¾Í»á´´½¨Ò»¸öÐ嵀 `MBeanServer`¡£ + + + + + +¸ü¶àϸ½Ú¼û [`JmxAutoConfiguration`](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java) Àà¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Spring BootÒ²½«¹ÜÀí¶Ëµã×÷ΪJMX MBeansÔÚ `org.springframework.boot` ÓòϹ«¿ª¡£ ÒªÍêÈ«¿ØÖÆJMXÓòÖеĶ˵ã×¢²á£¬¿ÉÒÔ¿¼ÂÇ×¢²áÄã×Ô¼ºµÄ `EndpointObjectNameFactory` ʵÏÖ¡£ + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.jmx.custom-mbean-names)4.1\. ×Ô¶¨ÒåMBeanÃû³Æ + + + +MBeanµÄÃû×Öͨ³£Óɶ˵ãµÄ `id` Éú³É¡£ ÀýÈ磬 `health` ¶Ëµã±»±©Â¶Îª `org.springframework.boot:type=Endpoint,name=Health`¡£ + + + + + +Èç¹ûÄãµÄÓ¦ÓóÌÐò°üº¬Ò»¸öÒÔÉϵÄSpring `ApplicationContext`£¬Äã¿ÉÄܻᷢÏÖÃû×Ö·¢Éú³åÍ»¡£ ΪÁ˽â¾öÕâ¸öÎÊÌ⣬Äã¿ÉÒÔ½« `spring.jmx.unique-names` ÊôÐÔÉèÖÃΪ `true`£¬ÕâÑùMBeanµÄÃû×Ö¾Í×ÜÊÇΨһµÄ¡£ + + + + + +Ä㻹¿ÉÒÔ×Ô¶¨Ò屩¶¶ËµãµÄJMXÓò¡£ ÏÂÃæµÄÉèÖÃÏÔʾÁËÔÚ `application.properties` ÖÐÕâÑù×öµÄÒ»¸öÀý×Ó¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +spring.jmx.unique-names=true +management.endpoints.jmx.domain=com.example.myapp + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.jmx.disable-jmx-endpoints)4.2\. ½ûÓÃJMX¶Ëµã + + + +Èç¹ûÄã²»Ïëͨ¹ýJMX±©Â¶¶Ëµã£¬Äã¿ÉÒÔ°Ñ `management.endpoints.jmx.exposure.exclude` ÊôÐÔÉèÖÃΪ `*`£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.endpoints.jmx.exposure.exclude=* + +``` + + + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.observability)5\. ¿É¹Û²âÐÔ£¨Observability£© + + + + + +¿É¹Û²ìÐÔÊÇÖ¸´ÓÍⲿ¹Û²ìÒ»¸öÔËÐÐÖеÄϵͳµÄÄÚ²¿×´Ì¬µÄÄÜÁ¦¡£ËüÓÉÈý¸öÖ§Öù×é³É£ºÈÕÖ¾¡¢¶ÈÁ¿ºÍ¸ú×Ù¡£ + + + + + +¶ÔÓÚ¶ÈÁ¿ºÍ¸ú×Ù£¬Spring BootʹÓà [Micrometer Observation](https://micrometer.io/docs/observation)¡£Òª´´½¨Äã×Ô¼ºµÄ¹Û²ì£¨Õ⽫µ¼Ö¶ÈÁ¿ºÍ¸ú×Ù£©£¬Äã¿ÉÒÔ×¢ÈëÒ»¸ö `ObservationRegistry`¡£ + + + + + + + +``` +@Component +public class MyCustomObservation { + + private final ObservationRegistry observationRegistry; + + public MyCustomObservation(ObservationRegistry observationRegistry) { + this.observationRegistry = observationRegistry; + } + + public void doSomething() { + Observation.createNotStarted("doSomething", this.observationRegistry) + .lowCardinalityKeyValue("locale", "en-US") + .highCardinalityKeyValue("userId", "42") + .observe(() -> { + // Execute business logic here + }); + } + +} + +``` + + + + + + + +| | µÍ¿¨¶ÈµÄ±êÇ©½«±»Ìí¼Óµ½Ö¸±êºÍ×·×ÙÖУ¬¶ø¸ß¿¨¶ÈµÄ±êÇ©½«Ö»±»Ìí¼Óµ½×·×ÙÖС£ | +| --- | --- | + + + + + +`ObservationPredicate`¡¢`GlobalObservationConvention` ºÍ `ObservationHandler` ÀàÐ굀 Bean ½«±»×Ô¶¯×¢²áµ½ `ObservationRegistry` ÉÏ¡£Äã¿ÉÒÔÁíÍâ×¢²áÈÎÒâÊýÁ¿µÄ `ObservationRegistryCustomizer` BeanÀ´½øÒ»²½ÅäÖÃ×¢²á±í¡£ + + + + + +¸ü¶àϸ½ÚÇë¼û [Micrometer Observation Îĵµ](https://micrometer.io/docs/observation)¡£ + + + + + +| | JDBCºÍR2DBCµÄ¿É¹Û²ìÐÔ£¨Observability£©¿ÉÒÔʹÓõ¥¶ÀµÄÏîÄ¿½øÐÐÅäÖ᣶ÔÓÚ JDBC£¬ [Datasource Micrometer ÏîÄ¿](https://github.com/jdbc-observations/datasource-micrometer) ÌṩÁËÒ»¸ö Spring Boot Starter£¬ÔÚµ÷ÓÃJDBC²Ù×÷ʱ×Ô¶¯´´½¨¹Û²ì¡£ÔÚ [²Î¿¼Îĵµ](https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/)ÖÐÔĶÁ¸ü¶à¹ØÓÚËüµÄÐÅÏ¢¡£¶ÔÓÚR2DBC£¬ [R2DBC¹Û²ìµÄSpring Boot×Ô¶¯ÅäÖÃ](https://github.com/spring-projects-experimental/r2dbc-micrometer-spring-boot) ¿ÉÒÔΪR2DBC²éѯµ÷Óô´½¨¹Û²ì¡£ | +| --- | --- | + + + + + +½ÓÏÂÀ´µÄÕ½ڽ«Ìṩ¹ØÓÚÈÕÖ¾¡¢Ö¸±êºÍ×·×ٵĸü¶àϸ½Ú¡£ + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.loggers)6\. ÈÕÖ¾¼Ç¼Æ÷£¨Logger£© + + + + + +Spring Boot Actuator°üÀ¨ÔÚÔËÐÐʱ²é¿´ºÍÅäÖÃÓ¦ÓóÌÐòµÄÈÕÖ¾¼¶±ðµÄ¹¦ÄÜ¡£ Äã¿ÉÒԲ鿴Õû¸öÁбí»òµ¥¸öÈÕÖ¾¼Ç¼Æ÷µÄÅäÖã¬ËüÓÉÃ÷È·ÅäÖõÄÈÕÖ¾¼¶±ðÒÔ¼°ÈÕÖ¾¿ò¼Ü¸³ÓèËüµÄÓÐЧÈÕÖ¾¼¶±ð×é³É¡£ ÕâЩ¼¶±ð¿ÉÒÔÊÇÒÔÏÂÖ®Ò»¡£ + + + + + +* `TRACE` + +* `DEBUG` + +* `INFO` + +* `WARN` + +* `ERROR` + +* `FATAL` + +* `OFF` + +* `null` + + + + + +`null` ±íʾûÓÐÃ÷È·µÄÅäÖᣠ+ + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.loggers.configure)6.1\. ÅäÖÃÒ»¸ö Logger + + + +ÒªÅäÖÃÒ»¸ö¸ø¶¨µÄ¼Ç¼Æ÷£¬`POST` Ò»¸ö²¿·ÖʵÌåµ½×ÊÔ´µÄURI£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +``` +{ + "configuredLevel": "DEBUG" +} +``` + + + + + + + +| | Òª ¡°reset¡± £¨ÖØÖ㩼ǼÆ÷µÄÌØ¶¨¼¶±ð£¨²¢Ê¹ÓÃĬÈÏÅäÖã©£¬Äã¿ÉÒÔ´«µÝÒ»¸ö `null` µÄÖµ×÷Ϊ `configuredLevel`¡£ | +| --- | --- | + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics)7\. Ö¸±ê£¨Metrics£© + + + + + +Spring Boot ActuatorΪ [Micrometer](https://micrometer.io/) ÌṩÁËÒÀÀµ¹ÜÀíºÍ×Ô¶¯ÅäÖã¬MicrometerÊÇÒ»¸öÖ§³Ö [ÖÚ¶à¼à¿ØÏµÍ³](https://micrometer.io/docs) µÄÓ¦ÓóÌÐòÖ¸±ê½Ó¿Ú£¬°üÀ¨¡£ + + + + + +* [AppOptics](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.appoptics) + +* [Atlas](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.atlas) + +* [Datadog](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.datadog) + +* [Dynatrace](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace) + +* [Elastic](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.elastic) + +* [Ganglia](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.ganglia) + +* [Graphite](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.graphite) + +* [Humio](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.humio) + +* [Influx](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.influx) + +* [JMX](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.jmx) + +* [KairosDB](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.kairos) + +* [New Relic](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.newrelic) + +* [OpenTelemetry](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.otlp) + +* [Prometheus](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.prometheus) + +* [SignalFx](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.signalfx) + +* [Simple (in-memory)](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.simple) + +* [Stackdriver](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.stackdriver) + +* [StatsD](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.statsd) + +* [Wavefront](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.wavefront) + + + + + +| | ÒªÁ˽â¸ü¶à¹ØÓÚMicrometerµÄ¹¦ÄÜ£¬Çë²Î¼ûÆä [²Î¿¼Îĵµ](https://micrometer.io/docs)£¬ÌرðÊÇ [¸ÅÄ·Ö](https://micrometer.io/docs/concepts)¡£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.getting-started)7.1\. ÈëÃÅ + + + +Spring Boot»á×Ô¶¯ÅäÖÃÒ»¸ö¸´ºÏµÄ `MeterRegistry`£¬²¢ÎªËüÔÚclasspathÉÏ·¢ÏÖµÄÿ¸öÖ§³ÖµÄʵÏÖÌí¼ÓÒ»¸ö×¢²á±í¡£ ÔÚÄãµÄÔËÐÐʱclasspathÖÐÓÐ¶Ô `micrometer-registry-{system}` µÄÒÀÀµ¾Í×ãÒÔÈÃSpring BootÅäÖÃ×¢²á±íÁË¡£ + + + + + +´ó¶àÊý×¢²á±íÓй²Í¬µÄÌØµã¡£ ÀýÈ磬¼´Ê¹ Micrometer ×¢²á±íµÄʵÏÖÔÚclasspathÉÏ£¬ÄãÒ²¿ÉÒÔ½ûÓÃÒ»¸öÌØ¶¨µÄ×¢²á±í¡£ ÏÂÃæµÄÀý×Ó½ûÓÃÁËDatadog¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.datadog.metrics.export.enabled=false + +``` + + + + + + + +ÄãÒ²¿ÉÒÔ½ûÓÃËùÓеÄ×¢²á±í£¬³ý·Ç×¢²á±íÌØ¶¨ÊôÐÔÁíÓÐ˵Ã÷£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.defaults.metrics.export.enabled=false + +``` + + + + + + + +Spring Boot»¹½«ÈκÎ×Ô¶¯ÅäÖõÄ×¢²á±íÌí¼Óµ½ `Metrics` ÀàÉϵÄÈ«¾Ö¾²Ì¬¸´ºÏ×¢²á±í£¬³ý·ÇÄãÃ÷È·¸æËßËü²»ÒªÕâÑù×ö¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.metrics.use-global-registry=false + +``` + + + + + + + +Äã¿ÉÒÔ×¢²áÈÎÒâÊýÁ¿µÄ `MeterRegistryCustomizer` BeanÀ´½øÒ»²½ÅäÖÃ×¢²á±í£¬±ÈÈçÔÚÈÎºÎ±í±»×¢²á֮ǰӦÓÃÆÕͨ±êÇ©¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyMeterRegistryConfiguration { + + @Bean + public MeterRegistryCustomizer metricsCommonTags() { + return (registry) -> registry.config().commonTags("region", "us-east-1"); + } + +} + +``` + + + + + + + +Äã¿ÉÒÔͨ¹ý¸ü¾ßÌåµÄ·ºÐͽ«¶¨ÖÆÓ¦ÓÃÓÚÌØ¶¨µÄ×¢²á±íʵÏÖ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyMeterRegistryConfiguration { + + @Bean + public MeterRegistryCustomizer graphiteMetricsNamingConvention() { + return (registry) -> registry.config().namingConvention(this::name); + } + + private String name(String name, Meter.Type type, String baseUnit) { + return ... + } + +} + +``` + + + + + + + +Spring Boot»¹ [ÅäÖÃÁËÄÚÖà instrumentation](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported)£¬Äã¿ÉÒÔͨ¹ýÅäÖûòרÓÃ×¢½â±ê¼ÇÀ´¿ØÖÆ¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export)7.2\. Ö§³ÖµÄ¼à¿ØÏµÍ³ + + + +±¾½Ú¼òÒª½éÉÜÿ¸öÖ§³ÖµÄ¼à¿ØÏµÍ³¡£ + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.appoptics)7.2.1\. AppOptics + + + +ĬÈÏÇé¿öÏ£¬AppOptics×¢²áÖÐÐĻᶨÆÚ½«Ö¸±êÍÆË͵½ `[api.appoptics.com/v1/measurements](https://api.appoptics.com/v1/measurements)`¡£Òª½«Ö¸±êµ¼³öµ½ SaaS [AppOptics](https://micrometer.io/docs/registry/appOptics)£¬±ØÐëÌṩÄãµÄAPIÁîÅÆ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.appoptics.metrics.export.api-token=YOUR_TOKEN + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.atlas)7.2.2\. Atlas + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê»á±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [Atlas](https://micrometer.io/docs/registry/atlas)¡£Äã¿ÉÒÔÌṩ [Atlas server](https://github.com/Netflix/atlas) µÄλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.atlas.metrics.export.uri=https://atlas.example.com:7101/api/v1/publish + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.datadog)7.2.3\. Datadog + + + +Ò»¸öDatadog×¢²áÖÐÐĻᶨÆÚ½«Ö¸±êÍÆË͵½ [datadoghq](https://www.datadoghq.com/)¡£ Òªµ¼³öÖ¸±êµ½ [Datadog](https://micrometer.io/docs/registry/datadog)£¬Äã±ØÐëÌṩÄãµÄAPIÃÜÔ¿¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.datadog.metrics.export.api-key=YOUR_KEY + +``` + + + + + + + +Èç¹ûÄãÁíÍâÌṩһ¸öÓ¦ÓÃÃÜÔ¿£¨¿ÉÑ¡£©£¬ÄÇôԪÊý¾Ý£¬ÈçÒDZíÃèÊö¡¢ÀàÐͺͻù±¾µ¥Î»Ò²½«±»µ¼³ö¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.datadog.metrics.export.api-key=YOUR_API_KEY +management.datadog.metrics.export.application-key=YOUR_APPLICATION_KEY + +``` + + + + + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê±»·¢Ë͵½DatadogÃÀ¹ú [site](https://docs.datadoghq.com/getting_started/site) £¨`[api.datadoghq.com](https://api.datadoghq.com/)`£©¡£ Èç¹ûÄãµÄDatadogÏîÄ¿ÍйÜÔÚÆäËûÍøÕ¾ÉÏ£¬»òÕßÄãÐèҪͨ¹ý´úÀí·¢ËÍÖ¸±ê£¬ÇëÏàÓ¦ÅäÖÃURI¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.datadog.metrics.export.uri=https://api.datadoghq.eu + +``` + + + + + + + +Ä㻹¿ÉÒԸıäÏòDatadog·¢ËÍÖ¸±êµÄʱ¼ä¼ä¸ô¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.datadog.metrics.export.step=30s + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace)7.2.4\. Dynatrace + + + +DynatraceÌṩÁËÁ½¸öÖ¸±êÉãÈ¡API£¬¶¼ÊÇΪ [Micrometer](https://micrometer.io/docs/registry/dynatrace) ʵÏֵġ£Äã¿ÉÒÔÔÚ [ÕâÀï](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/micrometer) ÕÒµ½Dynatrace¹ØÓÚMicrometerÖ¸±êÉãÈëµÄÎĵµ¡£`v1` ÃüÃû¿Õ¼äÖеÄÅäÖÃÊôÐÔÖ»ÊÊÓÃÓÚµ¼³öµ½ [Timeseries v1 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/) ʱ¡£`v2` ÃüÃû¿Õ¼äÖеÄÅäÖÃÊôÐÔÖ»ÊÊÓÃÓÚµ¼³öµ½ [Metrics v2 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/) ʱ¡£Çë×¢Ò⣬¸Ã¼¯³Éÿ´ÎÖ»Äܵ¼³öµ½APIµÄ `v1` »ò `v2` °æ±¾£¬`v2` °æ±¾ÊÇÊ×Ñ¡¡£Èç¹û `device-id`£¨v1°æÐèÒª£¬µ«ÔÚv2°æÖв»Ê¹Óã©ÔÚ `v1` °æÃüÃû¿Õ¼äÖб»ÉèÖã¬ÄÇômetric½«±»µ¼³öµ½ `v1` °æ¶Ëµã¡£·ñÔò£¬¾Í¼Ù¶¨ÊÇ `v2` °æ±¾¡£ + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace.v2-api)v2 API + + + +Äã¿ÉÒÔͨ¹ýÁ½ÖÖ·½Ê½Ê¹ÓÃv2 API¡£ + + + + + +###### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace.v2-api.auto-config)×Ô¶¯ÅäÖà + + + +Dynatrace×Ô¶¯ÅäÖÃÊÊÓÃÓÚÓÉOneAgent»òDynatrace Operator for Kubernetes¼à¿ØµÄÖ÷»ú¡£ + + + + + +**±¾µØOneAgent£º**Èç¹ûÖ÷»úÉÏÔËÐÐOneAgent£¬Ö¸±ê»á×Ô¶¯Êä³öµ½ [local OneAgent ingest endpoint](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/) ¡£ ÉãÈ¡¶Ëµã½«Ö¸±êת·¢µ½Dynatraceºó¶Ë¡£ + + + + + +**Dynatrace Kubernetes Operator£º**ÔÚ°²×°ÁËDynatrace OperatorµÄKubernetesÖÐÔËÐÐʱ£¬×¢²á±í½«×Ô¶¯´Ó²Ù×÷Ô±ÄÇÀï»ñÈ¡ÄãµÄ¶ËµãURIºÍAPIÁîÅÆ¡£ + + + + + +ÕâÊÇĬÈÏÐÐΪ£¬³ýÁËÒÀÀµ `io.micrometer:micrometer-registry-dynatrace` Ö®Í⣬²»ÐèÒªÌØ±ðµÄÉèÖᣠ+ + + + + + + +###### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace.v2-api.manual-config)ÊÖ¶¯ÅäÖà + + + +Èç¹ûûÓÐ×Ô¶¯ÅäÖã¬ÔòÐèÒª [Metrics v2 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/) µÄ¶ËµãºÍÒ»¸ö API ÁîÅÆ¡£APIÁîÅÆ±ØÐëÓÐ ¡°Ingest metrics¡± £¨`metrics.ingest`£©µÄȨÏÞÉèÖá£ÎÒÃǽ¨Ò齫ÁîÅÆµÄ·¶Î§ÏÞÖÆÔÚÕâÒ»¸öȨÏÞÉÏ¡£Äã±ØÐëÈ·±£¶ËµãURI°üº¬Â·¾¶£¨ÀýÈ磬`/api/v2/metrics/ingest`£©¡£ + + + + + +Metrics API v2ÉãÈ¡¶ËµãµÄURL¸ù¾ÝÄãµÄ²¿ÊðÑ¡Ïî¶ø²»Í¬¡£ + + + + + +* SaaS: `https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest` + +* Managed deployments: `https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest` + + + + + +ÏÂÃæµÄÀý×ÓÊÇÓà `example` environment id ÅäÖöÈÁ¿Öµµ¼³ö¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.dynatrace.metrics.export.uri=https://example.live.dynatrace.com/api/v2/metrics/ingest +management.dynatrace.metrics.export.api-token=YOUR_TOKEN + +``` + + + + + + + +ÔÚʹÓÃDynatrace v2 APIʱ£¬¿ÉÒÔʹÓÃÒÔÏ¿ÉÑ¡¹¦ÄÜ£¨¸ü¶àϸ½Ú¿ÉÔÚ [DynatraceÎĵµ](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/micrometer#dt-configuration-properties) ÖÐÕÒµ½£©¡£ + + + + + +* Metric key µÄǰ׺¡£ÉèÖÃÒ»¸öǰ׺£¬¸Ãǰ׺½«±»Ìí¼Óµ½ËùÓе¼³öµÄmetric keyÖС£ + +* ÓÃDynatraceÔªÊý¾ÝÀ´³äʵ¡£Èç¹ûOneAgent»òDynatrace²Ù×÷Ô±ÕýÔÚÔËÐУ¬ÓöîÍâµÄÔªÊý¾Ý£¨ÀýÈ磬¹ØÓÚÖ÷»ú¡¢½ø³Ì»òPod£©À´·á¸»Ö¸±ê¡£ + +* ĬÈÏά¶È¡£Ö¸¶¨Ìí¼Óµ½ËùÓе¼³ö¶ÈÁ¿µÄ¼üÖµ¶Ô¡£ Èç¹ûÓÃMicrometerÖ¸¶¨Á˾ßÓÐÏàͬ¼üµÄ±êÇ©£¬ËüÃǽ«¸²¸ÇĬÈÏdimension¡£ + +* ʹÓÃDynatrace Summary instrument¡£ÔÚijЩÇé¿öÏ£¬Micrometer Dynatrace×¢²á±í´´½¨µÄÖ¸±ê±»¾Ü¾ø¡£ ÔÚMicrometer 1.9.xÖУ¬Í¨¹ýÒýÈëDynatraceÌØ¶¨µÄÕªÒª¹¤¾ßÀ´½â¾öÕâ¸öÎÊÌâ¡£ °ÑÕâ¸ö¿ª¹ØÉèÖÃΪ `false` »áÆÈʹMicrometer»Øµ½1.9.x֮ǰµÄĬÈÏÐÐΪ¡£ Ö»ÓÐÔÚ´ÓMicrometer 1.8.xÇ¨ÒÆµ½1.9.xʱÓöµ½ÎÊÌâʱ²Å¿ÉÒÔʹÓᣠ+ + + + + +¿ÉÒÔ²»Ö¸¶¨URIºÍAPIÁîÅÆ£¬ÈçÒÔÏÂÀý×ÓËùʾ¡£ ÔÚÕâÖÖÇé¿öÏ£¬»áʹÓÃ×Ô¶¯ÅäÖõĶ˵㡣 + + + + + + + +Properties + +Yaml + + + + + +``` +management.dynatrace.metrics.export.v2.metric-key-prefix=your.key.prefix +management.dynatrace.metrics.export.v2.enrich-with-dynatrace-metadata=true +management.dynatrace.metrics.export.v2.default-dimensions.key1=value1 +management.dynatrace.metrics.export.v2.default-dimensions.key2=value2 +management.dynatrace.metrics.export.v2.use-dynatrace-summary-instruments=true + +``` + + + + + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace.v1-api)v1 API (Legacy) + + + +Dynatrace v1 APIÖ¸±ê×¢²á±íͨ¹ýʹÓà [Timeseries v1 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/) ¶¨ÆÚ½«Ö¸±êÍÆË͵½ÅäÖõÄURI¡£ ΪÁËÏòºó¼æÈÝÏÖÓеÄÉèÖ㬵± `device-id` ±»ÉèÖÃʱ£¨v1ÐèÒª£¬µ«ÔÚv2Öв»Ê¹Óã©£¬Ö¸±ê±»µ¼³öµ½Timeseries v1¶Ëµã¡£ ÒªÏò [Dynatrace](https://micrometer.io/docs/registry/dynatrace) µ¼³öÖ¸±ê£¬±ØÐëÌṩÄãµÄAPIÁîÅÆ¡¢É豸IDºÍURI¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.dynatrace.metrics.export.uri=https://{your-environment-id}.live.dynatrace.com +management.dynatrace.metrics.export.api-token=YOUR_TOKEN +management.dynatrace.metrics.export.v1.device-id=YOUR_DEVICE_ID + +``` + + + + + + + +¶ÔÓÚv1°æAPI£¬Äã±ØÐëÖ¸¶¨»ù±¾»·¾³URI£¬¶ø²»Ö¸¶¨Â·¾¶£¬ÒòΪv1°æµÄ¶Ëµã·¾¶»á×Ô¶¯Ìí¼Ó¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.dynatrace.version-independent-settings)Óë°æ±¾Î޹صÄÉèÖà + + + +³ýÁËAPI¶ËµãºÍÁîÅÆÍ⣬Ä㻹¿ÉÒԸıäÏòDynatrace·¢ËÍÖ¸±êµÄ¼ä¸ôʱ¼ä¡£ ĬÈϵĵ¼³öʱ¼ä¼ä¸ôÊÇ `60s`¡£ ÏÂÃæµÄÀý×Ó½«µ¼³öʱ¼ä¼ä¸ôÉèÖÃΪ30Ãë¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.dynatrace.metrics.export.step=30s + +``` + + + + + + + +Äã¿ÉÒÔÔÚ [MicrometerÎĵµ](https://micrometer.io/docs/registry/dynatrace) ºÍ [DynatraceÎĵµ](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/micrometer) ÖÐÕÒµ½¹ØÓÚÈçºÎΪMicrometerÉèÖÃDynatrace exporter£¨µ¼³öÆ÷£©µÄ¸ü¶àÐÅÏ¢¡£ + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.elastic)7.2.5\. Elastic + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [Elastic](https://micrometer.io/docs/registry/elastic) ¡£ Äã¿ÉÒÔͨ¹ýʹÓÃÒÔÏÂÊôÐÔÌṩҪʹÓõÄElastic·þÎñÆ÷µÄλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.elastic.metrics.export.host=https://elastic.example.com:8086 + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.ganglia)7.2.6\. Ganglia + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [Ganglia](https://micrometer.io/docs/registry/ganglia) ¡£Äã¿ÉÒÔÌṩ [Ganglia server](http://ganglia.sourceforge.net/) µÄÖ÷»úºÍ¶Ë¿Ú£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.ganglia.metrics.export.host=ganglia.example.com +management.ganglia.metrics.export.port=9649 + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.graphite)7.2.7\. Graphite + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê»á±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [Graphite](https://micrometer.io/docs/registry/graphite) ¡£Äã¿ÉÒÔÌṩ [Graphite server](https://graphiteapp.org/) µÄÖ÷»úºÍ¶Ë¿Ú£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.graphite.metrics.export.host=graphite.example.com +management.graphite.metrics.export.port=9004 + +``` + + + + + + + +MicrometerÌṩÁËÒ»¸öĬÈ쵀 `HierarchicalNameMapper`£¬¹ÜÀídimensional meter IDÈçºÎhttps://micrometer.io/docs/registry/graphite#_hierarchical_name_mapping[Ó³Éäµ½ flat hierarchical name]¡£ + + + + + +| | Òª¿ØÖÆÕâÖÖÐÐΪ£¬Ç붨ÒåÄãµÄ `GraphiteMeterRegistry` ²¢ÌṩÄã×Ô¼ºµÄ `HierarchicalNameMapper`¡£ ³ý·ÇÄã×Ô¼º¶¨Ò壬·ñÔò»áÌṩһ¸ö×Ô¶¯ÅäÖÃµÄ `GraphiteConfig` ºÍ `Clock` Bean¡£JavaKotlin```@Configuration(proxyBeanMethods = false)public class MyGraphiteConfiguration { @Bean public GraphiteMeterRegistry graphiteMeterRegistry(GraphiteConfig config, Clock clock) { return new GraphiteMeterRegistry(config, clock, this::toHierarchicalName); } private String toHierarchicalName(Meter.Id id, NamingConvention convention) { return ... }}``` | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.humio)7.2.8\. Humio + + + +ĬÈÏÇé¿öÏ£¬Humio×¢²áÖÐÐĻᶨÆÚ½«Ö¸±êÍÆË͵½ [cloud.humio.com](https://cloud.humio.com/) ¡£ Òª½«Ö¸±êµ¼³öµ½SaaS [Humio](https://micrometer.io/docs/registry/humio)£¬Äã±ØÐëÌṩÄãµÄAPIÁîÅÆ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.humio.metrics.export.api-token=YOUR_TOKEN + +``` + + + + + + + +Ä㻹Ӧ¸ÃÅäÖÃÒ»¸ö»ò¶à¸ö±êÇ©£¬ÒÔÈ·¶¨ÍÆËÍÖ¸±êµÄÊý¾ÝÔ´¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.humio.metrics.export.tags.alpha=a +management.humio.metrics.export.tags.bravo=b + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.influx)7.2.9\. Influx + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê»á±»µ¼³öµ½ÔËÐÐÔÚ±¾µØ»úÆ÷É쵀 [Influx](https://micrometer.io/docs/registry/influx) v1ʵÀý£¬²¢²ÉÓÃĬÈÏÅäÖá£Òªµ¼³öÖ¸±êµ½InfluxDB v2£¬ÇëÅäÖà `org`¡¢`bucket` ºÍÓÃÓÚдÈëÖ¸±êµÄauthentication `token`¡£Äã¿ÉÒÔͨ¹ýÒÔÏ·½Ê½ÌṩҪʹÓÃµÄ [Influx server](https://www.influxdata.com/) µÄλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.influx.metrics.export.uri=https://influx.example.com:8086 + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.jmx)7.2.10\. JMX + + + +MicrometerÌṩÁË¶Ô [JMX](https://micrometer.io/docs/registry/jmx) µÄ·Ö²ãÓ³É䣬Ö÷ÒªÊÇ×÷ΪһÖÖÁ®¼ÛºÍ¿ÉÒÆÖ²µÄ·½Ê½À´²é¿´±¾µØµÄ¶ÈÁ¿¡£ ĬÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ `metrics` JMXÓò¡£ Äã¿ÉÒÔͨ¹ýÒÔÏ·½Ê½ÌṩҪʹÓõÄÓò¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.jmx.metrics.export.domain=com.example.app.metrics + +``` + + + + + + + +MicrometerÌṩÁËÒ»¸öĬÈ쵀 `HierarchicalNameMapper`£¬¹ÜÀídimensional meter ID ÈçºÎ [Ó³Éäµ½ flat hierarchical name](https://micrometer.io/docs/registry/jmx#_hierarchical_name_mapping)¡£ + + + + + +| | Òª¿ØÖÆÕâÖÖÐÐΪ£¬Ç붨ÒåÄãµÄ `JmxMeterRegistry` ²¢ÌṩÄã×Ô¼ºµÄ `HierarchicalNameMapper`¡£ ³ý·ÇÄã×Ô¼º¶¨Ò壬·ñÔò»áÌṩһ¸ö×Ô¶¯ÅäÖÃµÄ `JmxConfig` ºÍ `Clock` Bean¡£JavaKotlin```@Configuration(proxyBeanMethods = false)public class MyJmxConfiguration { @Bean public JmxMeterRegistry jmxMeterRegistry(JmxConfig config, Clock clock) { return new JmxMeterRegistry(config, clock, this::toHierarchicalName); } private String toHierarchicalName(Meter.Id id, NamingConvention convention) { return ... }}``` | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.kairos)7.2.11\. KairosDB + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [KairosDB](https://micrometer.io/docs/registry/kairos) ¡£Äã¿ÉÒÔͨ¹ýÒÔÏ·½Ê½ÌṩҪʹÓÃµÄ [KairosDB server](https://kairosdb.github.io/) µÄλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.kairos.metrics.export.uri=https://kairosdb.example.com:8080/api/v1/datapoints + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.newrelic)7.2.12\. New Relic + + + +New Relic×¢²áÖÐÐĻᶨÆÚ½«Ö¸±êÍÆË͵½ [New Relic](https://micrometer.io/docs/registry/new-relic)¡£Òªµ¼³öÖ¸±êµ½ [New Relic](https://newrelic.com/)£¬Äã±ØÐëÌṩÄãµÄAPIÃÜÔ¿ºÍÕË»§ID¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.newrelic.metrics.export.api-key=YOUR_KEY +management.newrelic.metrics.export.account-id=YOUR_ACCOUNT_ID + +``` + + + + + + + +Ä㻹¿ÉÒԸıäÏòNew Relic·¢ËÍÖ¸±êµÄʱ¼ä¼ä¸ô¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.newrelic.metrics.export.step=30s + +``` + + + + + + + +ĬÈÏÇé¿öÏ£¬Ö¸±êÊÇͨ¹ýRESTµ÷Ó÷¢²¼µÄ£¬µ«Èç¹ûÄãÔÚclasspathÉÏÓÐJava Agent API£¬ÄãÒ²¿ÉÒÔʹÓÃËü¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.newrelic.metrics.export.client-provider-type=insights-agent + +``` + + + + + + + +×îºó£¬Äã¿ÉÒÔͨ¹ý¶¨ÒåÄã×Ô¼ºµÄ `NewRelicClientProvider` ¶¹À´ÍêÈ«¿ØÖÆ¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.otlp)7.2.13\. OpenTelemetry + + + +ĬÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [OpenTelemetry](https://micrometer.io/docs/registry/otlp) ¡£Äã¿ÉÒÔͨ¹ýÒÔÏ·½Ê½ÌṩҪʹÓÃµÄ [OpenTelemtry metric endpoint](https://opentelemetry.io/) µÄλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.otlp.metrics.export.url=https://otlp.example.com:4318/v1/metrics + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.prometheus)7.2.14\. Prometheus + + + +[Prometheus](https://micrometer.io/docs/registry/prometheus) Ï£Íûscrape»òÂÖѯµ¥¸öÓ¦ÓóÌÐòʵÀýµÄÖ¸±ê¡£Spring BootÔÚ `/actuator/prometheus` ÌṩÁËÒ»¸öactuator¶Ëµã£¬ÒÔ±ãÒÔÊʵ±µÄ¸ñʽ³ÊÏÖ [Prometheus scrape](https://prometheus.io/)¡£ + + + + + +| | ĬÈÏÇé¿öÏ£¬¸Ã¶ËµãÊDz»¿ÉÓõ쬱ØÐë±»±©Â¶¡£¸ü¶àϸ½ÚÇë²Î¼û[±©Â¶¶Ëµã](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.exposing)¡£ | +| --- | --- | + + + + + +ÏÂÃæµÄÀý×Ó `scrape_config` Ìí¼Óµ½ `prometheus.yml`¡£ + + + + + + + +``` +scrape_configs: + - job_name: "spring" + metrics_path: "/actuator/prometheus" + static_configs: + - targets: ["HOST:PORT"] +``` + + + + + + + +Ò²Ö§³Ö [Prometheus Exemplars](https://prometheus.io/docs/prometheus/latest/feature_flags/#exemplars-storage)¡£ÒªÆôÓÃÕâ¸ö¹¦ÄÜ£¬Ó¦¸ÃÓÐÒ»¸ö `SpanContextSupplier` Bean¡£Èç¹ûÄãʹÓà [Micrometer Tracing](https://micrometer.io/docs/tracing)£¬Õ⽫ΪÄã×Ô¶¯ÅäÖ㬵«Èç¹ûÄãÏ룬Äã×ÜÊÇ¿ÉÒÔ´´½¨×Ô¼ºµÄ¡£Çë²é¿´ [Prometheus Îĵµ](https://prometheus.io/docs/prometheus/latest/feature_flags/#exemplars-storage) £¬ÒòΪÕâ¸ö¹¦ÄÜÐèÒªÔÚPrometheusÕâ±ßÃ÷È·ÆôÓ㬶øÇÒÖ»Ö§³ÖʹÓà [OpenMetrics](https://github.com/OpenObservability/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#exemplars) ¸ñʽ¡£ + + + + + +¶ÔÓÚ¶ÌÔݵĻòÅú´¦ÀíµÄ×÷Òµ£¬¿ÉÄÜ´æÔÚµÄʱ¼ä²»¹»³¤£¬ÎÞ·¨±»¹ÎÈ¡£¬Äã¿ÉÒÔʹÓà [Prometheus Pushgateway](https://github.com/prometheus/pushgateway) Ö§³Ö£¬½«Ö¸±ê±©Â¶¸øPrometheus¡£ÒªÆôÓÃPrometheus PushgatewayÖ§³Ö£¬ÇëÔÚÄãµÄÏîÄ¿ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + + + + + +``` + + io.prometheus + simpleclient_pushgateway + +``` + + + + + + + +µ±Prometheus PushgatewayµÄÒÀÀµ³öÏÖÔÚclasspathÉÏ£¬²¢ÇÒ `management.prometheus.metrics.export.pushgateway.enabled` ÊôÐÔ±»ÉèÖÃΪ `true` ʱ£¬Ò»¸ö `PrometheusPushGatewayManager` bean¾Í±»×Ô¶¯ÅäÖÃÁË¡£ Ëü¸ºÔð¹ÜÀíÏòPrometheus PushgatewayÍÆËÍÖ¸±êµÄ¹¤×÷¡£ + + + + + +Äã¿ÉÒÔͨ¹ýʹÓà `management.prometheus.metrics.export.pushgateway` ϵÄÊôÐÔÀ´µ÷Õû `PrometheusPushGatewayManager`¡£ ¶ÔÓڸ߼¶ÅäÖã¬ÄãÒ²¿ÉÒÔÌṩÄã×Ô¼ºµÄ `PrometheusPushGatewayManager` bean¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.signalfx)7.2.15\. SignalFx + + + +SignalFx×¢²áÖÐÐĻᶨÆÚ½«Ö¸±êÍÆË͵½ [SignalFx](https://micrometer.io/docs/registry/signalFx)¡£Òªµ¼³öÖ¸±êµ½ [SignalFx](https://www.signalfx.com/)£¬Äã±ØÐëÌṩÄãµÄaccess token¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.signalfx.metrics.export.access-token=YOUR_ACCESS_TOKEN + +``` + + + + + + + +ÄãÒ²¿ÉÒԸıäÏòSignalFx·¢ËÍÖ¸±êµÄʱ¼ä¼ä¸ô¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.signalfx.metrics.export.step=30s + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.simple)7.2.16\. Simple + + + +MicrometerÌṩÁËÒ»¸ö¼òµ¥µÄ¡¢ÄÚ´æÖеĺó¶Ë£¬Èç¹ûûÓÐÅäÖÃÆäËû×¢²á±í£¬¸Ãºó¶Ë»á×Ô¶¯×÷Ϊ±¸Óá£Õâ¿ÉÒÔÈÃÄã¿´µ½ÔÚ [metrics endpoint](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.endpoint) ÖÐÊÕ¼¯ÁËÄÄЩ¶ÈÁ¿¡£ + + + + + +Ò»µ©ÄãʹÓÃÈÎºÎÆäËû¿ÉÓõĺó¶Ë£¬ÄÚ´æÖеĺó¶Ë¾Í»á×Ô¶¯¹Ø±Õ¡£ÄãÒ²¿ÉÒÔÃ÷È·µØ½ûÓÃËü¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.simple.metrics.export.enabled=false + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.stackdriver)7.2.17\. Stackdriver + + + +Stackdriver×¢²áÖÐÐĻᶨÆÚÏò [Stackdriver](https://cloud.google.com/stackdriver/) ÍÆËÍÖ¸±ê¡£Òªµ¼³öÖ¸±êµ½SaaS [Stackdriver](https://micrometer.io/docs/registry/stackdriver)£¬Äã±ØÐëÌṩÄãµÄGoogle Cloud project ID¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.stackdriver.metrics.export.project-id=my-project + +``` + + + + + + + +Ä㻹¿ÉÒԸıäÏòStackdriver·¢ËÍÖ¸±êµÄʱ¼ä¼ä¸ô¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.stackdriver.metrics.export.step=30s + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.statsd)7.2.18\. StatsD + + + +StatsD×¢²á±í¼±Çеؽ«Ö¸±êͨ¹ýUDPÍÆË͸øStatsD agent¡£Ä¬ÈÏÇé¿öÏ£¬Ö¸±ê±»µ¼³öµ½ÔËÐÐÔÚÄã±¾µØ»úÆ÷É쵀 [StatsD](https://micrometer.io/docs/registry/statsD) agent¡£Äã¿ÉÒÔͨ¹ýÒÔÏ·½Ê½ÌṩStatsD´úÀíµÄÖ÷»ú¡¢¶Ë¿ÚºÍЭÒ飬ÒÔ±ãʹÓᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.statsd.metrics.export.host=statsd.example.com +management.statsd.metrics.export.port=9125 +management.statsd.metrics.export.protocol=udp + +``` + + + + + + + +Ä㻹¿ÉÒԸıäҪʹÓõÄStatsDÏß·ЭÒ飨ĬÈÏΪDatadog£©¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.statsd.metrics.export.flavor=etsy + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.export.wavefront)7.2.19\. Wavefront + + + +Wavefront×¢²á±í¶¨ÆÚ½«Ö¸±êÍÆË͵½ [Wavefront](https://micrometer.io/docs/registry/wavefront)¡£Èç¹ûÄãÖ±½Ó½«Ö¸±êµ¼³öµ½ [Wavefront](https://www.wavefront.com/)£¬Äã±ØÐëÌṩÄãµÄAPI token¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.wavefront.api-token=YOUR_API_TOKEN + +``` + + + + + + + +ÁíÍ⣬Äú¿ÉÒÔÔÚÄúµÄ»·¾³ÖÐʹÓÃWavefront sidecar»òÄÚ²¿´úÀíÀ´×ª·¢Ö¸±êÊý¾Ýµ½Wavefront APIÖ÷»ú¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.wavefront.uri=proxy://localhost:2878 + +``` + + + + + + + +Èç¹ûÄú½«Ö¸±ê·¢²¼µ½Wavefront´úÀí£¨Èç [WavefrontÎĵµ](https://docs.wavefront.com/proxies_installing.html) ÖÐËùÊö£©£¬Ö÷»ú±ØÐëÊÇ `proxy://HOST:PORT` ¸ñʽ¡£ + + + + + +ÄãÒ²¿ÉÒԸıäÏòWavefront·¢ËÍÖ¸±êµÄʱ¼ä¼ä¸ô¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.wavefront.metrics.export.step=30s + +``` + + + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported)7.3\. Ö§³ÖµÄÖ¸±ê £¨Metric£©ºÍ¶ÈÁ¿£¨Meter£© + + + +Spring BootΪ¸÷ÖÖ¸÷ÑùµÄ¼¼ÊõÌṩÁË×Ô¶¯¼ÆÁ¿Æ÷×¢²á¡£ ÔÚ´ó¶àÊýÇé¿öÏ£¬Ä¬ÈÏÖµÌṩÁ˺ÏÀíµÄÖ¸±ê£¬¿ÉÒÔ·¢²¼µ½ÈκÎÖ§³ÖµÄ¼à¿ØÏµÍ³ÖС£ + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.jvm)7.3.1\. JVMÖ¸±ê + + + +×Ô¶¯ÅäÖÃͨ¹ýʹÓúËÐÄ Micrometer ÀàÆôÓÃJVM¶ÈÁ¿¡£ JVMÖ¸±êÔÚ `jvm.` meter name Ï·¢²¼¡£ + + + + + +ÌṩÒÔÏÂJVMÖ¸±ê¡£ + + + + + +* ¸÷ÖÖÄÚ´æºÍ»º³å³ØÏ¸½Ú + +* ÓëÀ¬»øÊÕ¼¯ÓйصÄͳ¼ÆÊý¾Ý + +* Ïß³ÌÀûÓÃÂÊ + +* ¼ÓÔØºÍÐ¶ÔØµÄÀàµÄÊýÁ¿ + +* JVMµÄ°æ±¾ÐÅÏ¢ + +* JIT ±àÒëʱ¼ä + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.system)7.3.2\. ϵͳָ±ê + + + +×Ô¶¯ÅäÖÃͨ¹ýʹÓúËÐÄMicrometerÀàÀ´ÊµÏÖϵͳ¶ÈÁ¿¡£ ϵͳָ±êÔÚ `system.`¡¢`process.` ºÍ `disk.` meter ÃûÏ·¢²¼¡£ + + + + + +ÌṩÒÔÏÂϵͳָ±ê¡£ + + + + + +* CPUÖ¸±ê + +* ÎļþÃèÊö·ûÖ¸±ê + +* Õý³£ÔËÐÐʱ¼äÖ¸±ê£¨°üÀ¨Ó¦ÓóÌÐòÒѾ­ÔËÐеÄʱ¼äºÍ¾ø¶ÔÆô¶¯Ê±¼äµÄ¹Ì¶¨²âÁ¿£©¡£ + +* ¿ÉÓõĴÅÅ̿ռä + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.application-startup)7.3.3\. Ó¦ÓóÌÐòÆô¶¯Ö¸±ê + + + +×Ô¶¯ÅäÖñ©Â¶ÁËÓ¦ÓóÌÐòµÄÆô¶¯Ê±¼äÖ¸±ê¡£ + + + + + +* `application.started.time`: Æô¶¯Ó¦ÓóÌÐòµÄʱ¼ä¡£ + +* `application.ready.time`£ºÓ¦ÓóÌÐò×¼±¸ºÃΪÇëÇóÌṩ·þÎñËùÐèµÄʱ¼ä¡£ + + + + + +Ö¸±êÊÇÓÉÓ¦ÓÃÀàµÄÍêÈ«Ãû³ÆÀ´±ê¼ÇµÄ¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.logger)7.3.4\. ÈÕÖ¾¼Ç¼Æ÷Ö¸±ê + + + +×Ô¶¯ÅäÖÃÆôÓÃÁËLogbackºÍLog4J2µÄʼþ¶ÈÁ¿¡£ ϸ½ÚÔÚ `log4j2.events.` »ò `logback.events.` meterÃûϹ«²¼¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.tasks)7.3.5\. ÈÎÎñÖ´Ðк͵÷¶ÈÖ¸±ê + + + +×Ô¶¯ÅäÖÃʹËùÓпÉÓÃµÄ `ThreadPoolTaskExecutor` ºÍ `ThreadPoolTaskScheduler` Bean¶¼Äܱ»²âÁ¿£¬Ö»Òªµ×²ãµÄ `ThreadPoolExecutor` ¿ÉÓᣠָ±êÓÉexecutorµÄÃû³ÆÀ´±ê¼Ç£¬executorµÄÃû³ÆÀ´×ÔÓÚBeanµÄÃû³Æ¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.spring-mvc)7.3.6\. Spring MVC Ö¸±ê + + + +×Ô¶¯ÅäÖÃÄܹ»¶Ô Spring MVC ControllerºÍ±à³Ìʽhandler´¦ÀíµÄËùÓÐÇëÇó½øÐжÈÁ¿¡£ ĬÈÏÇé¿öÏ£¬Ö¸±êÊÇÒÔ `http.server.requests` ΪÃû³ÆÉú³ÉµÄ¡£ Äã¿ÉÒÔͨ¹ýÉèÖà `management.observations.http.server.requests.name` ÊôÐÔÀ´¶¨ÖƸÃÃû³Æ¡£ + + + + + +¹ØÓÚ²úÉúµÄ¹Û²ì½á¹û£¨observation£©µÄ¸ü¶àÐÅÏ¢£¬Çë²Î¼û [Spring Framework ²Î¿¼Îĵµ](https://docs.spring.io/spring-framework/docs/6.0.5/reference/html/integration.html#integration.observability.http-server.servlet)¡£ + + + + + +ÒªÌí¼Óµ½Ä¬ÈϱêÇ©ÖУ¬ÇëÌṩһ¸ö¼Ì³ÐÁË `org.springframework.http.server.observation` °üÖÐµÄ `DefaultServerRequestObservationConvention` µÄ `@Bean`¡£ÒªÌ滻ĬÈϱêÇ©£¬ÇëÌṩһ¸öʵÏÖ `ServerRequestObservationConvention` µÄ `@Bean`¡£ + + + + + +| | ÔÚijЩÇé¿öÏ£¬Web¿ØÖÆÆ÷Öд¦ÀíµÄÒì³£²»»á±»¼Ç¼ΪÇëÇó¶ÈÁ¿±êÇ©¡£Ó¦ÓóÌÐò¿ÉÒÔÑ¡Ôñ¼ÓÈ벢ͨ¹ý½«[handled exception ÉèÖÃΪ request attribute](https://springdoc.cn/spring-boot/web.html#web.servlet.spring-mvc.error-handling)À´¼Ç¼Òì³£¡£ | +| --- | --- | + + + + + +ĬÈÏÇé¿öÏ£¬ËùÓÐÇëÇó¶¼±»´¦Àí¡£ Òª×Ô¶¨Òå¹ýÂËÆ÷£¬ÇëÌṩһ¸öʵÏÖ `FilterRegistrationBean` µÄ `@Bean`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.spring-webflux)7.3.7\. Spring WebFlux Ö¸±ê + + + +×Ô¶¯ÅäÖÃÄܹ»¶ÔSpring WebFlux controllerºÍ±à³ÌʽhandlerµÄËùÓÐÇëÇó½øÐжÈÁ¿¡£ ĬÈÏÇé¿öÏ£¬Ö¸±êÊÇÒÔ `http.server.requests` ΪÃûÉú³ÉµÄ¡£ Äã¿ÉÒÔͨ¹ýÉèÖà `management.observations.http.server.requests.name` ÊôÐÔÀ´¶¨ÖƸÃÃû³Æ¡£ + + + + + +¹ØÓÚ²úÉúµÄ¹Û²ì½á¹û£¨observation£©µÄ¸ü¶àÐÅÏ¢£¬Çë²Î¼û [Spring Framework ²Î¿¼Îĵµ](https://docs.spring.io/spring-framework/docs/6.0.5/reference/html/integration.html#integration.observability.http-server.reactive)¡£ + + + + + +ÒªÌí¼Óµ½Ä¬ÈϱêÇ©ÖУ¬ÇëÌṩ¼Ì³ÐÁË `org.springframework.http.server.reactive.observation` °üÖÐµÄ `DefaultServerRequestObservationConvention` µÄ `@Bean`¡£ÒªÌ滻ĬÈϱêÇ©£¬ÇëÌṩһ¸öʵÏÖ `ServerRequestObservationConvention` µÄ `@Bean`¡£ + + + + + +| | ÔÚijЩÇé¿öÏ£¬¿ØÖÆÆ÷ºÍ´¦Àíº¯ÊýÖд¦ÀíµÄÒì³£²»»á±»¼Ç¼ΪÇëÇó¶ÈÁ¿±êÇ©¡£Ó¦ÓóÌÐò¿ÉÒÔÑ¡Ôñ¼ÓÈ벢ͨ¹ý[½«handled exceptionÉèÖÃΪrequest attribute](https://springdoc.cn/spring-boot/web.html#web.reactive.webflux.error-handling)À´¼Ç¼Òì³£¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.jersey)7.3.8\. Jersey Server Ö¸±ê + + + +×Ô¶¯ÅäÖÃʹJersey JAX-RSʵÏÖËù´¦ÀíµÄËùÓÐÇëÇó¶¼Äܱ»²âÁ¿¡£ ĬÈÏÇé¿öÏ£¬Ö¸±êÊÇÒÔ `http.server.requests` ΪÃû³ÆÉú³ÉµÄ¡£ Äã¿ÉÒÔͨ¹ýÉèÖà `management.observations.http.server.requests.name` ÊôÐÔÀ´¶¨ÖÆÃû³Æ¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Jersey·þÎñÆ÷Ö¸±ê±»±ê¼ÇΪÒÔÏÂÐÅÏ¢¡£ + + + + +| Tag | ˵Ã÷ | +| --- | --- | +| `exception` | ´¦ÀíÇëÇóʱÅ׳öµÄÈκÎÒì³£µÄ¼òµ¥ÀàÃû¡£ | +| `method` | ÇëÇóµÄ·½·¨£¨ÀýÈ磬`GET` »ò `POST`£©¡£ | +| `outcome` | ÇëÇóµÄ½á¹û£¬»ùÓÚÏìÓ¦µÄ״̬´úÂë¡£ 1xxÊÇ `INFORMATIONAL`£¬2xxÊÇ `SUCCESS`£¬3xxÊÇ `REDIRECTION`£¬4xxÊÇ `CLIENT_ERROR`£¬5xxÊÇ `SERVER_ERROR`¡£ | +| `status` | ÏìÓ¦µÄHTTP״̬´úÂ루ÀýÈ磬`200` »ò `500`£©¡£ | +| `uri` | Èç¹û¿ÉÄܵϰ£¬ÔÚ½øÐбäÁ¿Ì滻֮ǰ£¬ÇëÇóµÄURIÄ£°å£¨ÀýÈ磺`/api/person/{id}`£©¡£ | + + + +Òª¶¨ÖƱêÇ©£¬ÇëÌṩһ¸öʵÏÖ `JerseyTagsProvider` µÄ `@Bean`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.http-clients)7.3.9\. HTTP Client Ö¸±ê + + + +Spring Boot Actuator¸ºÔð¹ÜÀí `RestTemplate` ºÍ `WebClient` µÄ¹¤¾ß¡£Îª´Ë£¬Äã±ØÐë×¢Èë×Ô¶¯ÅäÖõĹ¹½¨Æ÷²¢Ê¹ÓÃËüÀ´´´½¨ÊµÀý¡£ + + + + + +* `RestTemplateBuilder` ÓÃÓÚ `RestTemplate` + +* `WebClient.Builder` ÓÃÓÚ `WebClient` + + + + + +ÄãÒ²¿ÉÒÔÊÖ¶¯Ó¦ÓøºÔðÕâ¸ö¹¤¾ßµÄcustomizer£¬¼´ `ObservationRestTemplateCustomizer` ºÍ `ObservationWebClientCustomizer`¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Ö¸±êÊÇÒÔ `http.client.requests` Õâ¸öÃû×ÖÉú³ÉµÄ¡£ + + + + + +Äã¿ÉÒÔͨ¹ýÉèÖà `management.observations.http.client.requests.name` ÊôÐÔÀ´¶¨ÖÆÕâ¸öÃû×Ö¡£ + + + + + +¹ØÓÚ²úÉúµÄ¹Û²ì½á¹û£¨observation£©µÄ¸ü¶àÐÅÏ¢£¬Çë²Î¼û [Spring Framework ²Î¿¼Îĵµ](https://docs.spring.io/spring-framework/docs/6.0.5/reference/html/integration.html#integration.observability.http-client)¡£ + + + + + +ÒªÔÚʹÓà `RestTemplate` ʱ¶¨ÖƱêÇ©£¬ÇëÌṩһ¸öʵÏÖÁË `org.springframework.http.client.observation` °üÖÐ `ClientRequestObservationConvention` µÄ `@Bean`¡£ÒªÔÚʹÓà `WebClient` ʱ×Ô¶¨Òå±êÇ©£¬ÇëÌṩһ¸öʵÏÖÁË `org.springframework.web.reactive.function.client` °üÖÐ `ClientRequestObservationConvention` µÄ `@Bean`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.tomcat)7.3.10\. Tomcat Ö¸±ê + + + +×Ô¶¯ÅäÖýöÔÚ `MBeanRegistry` ±»ÆôÓÃʱ²Å»áÆôÓÃTomcatµÄÒÇÆ÷¡£ ĬÈÏÇé¿öÏ£¬`MBeanRegistry` ÊǽûÓõ쬵«Äã¿ÉÒÔͨ¹ýÉèÖà `server.tomcat.mbeanregistry.enabled` Ϊ `true` À´ÆôÓÃËü¡£ + + + + + +TomcatµÄÖ¸±êÔÚ `tomcat.` meter ÃûÏ·¢²¼¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.cache)7.3.11\. Cache Ö¸±ê + + + +×Ô¶¯ÅäÖÿÉÒÔÔÚÆô¶¯Ê±¶ÔËùÓпÉÓÃµÄ `Cache` ʵÀý½øÐмì²â£¬ÆäÖ¸±êÒÔ `cache` Ϊǰ׺¡£ + + + + + +»º´æÒDZíÊDZê×¼»¯µÄ»ù±¾Ö¸±ê¼¯¡£ + + + + + +Ò²¿ÉÒÔʹÓöîÍâµÄ¡¢Õë¶Ô»º´æµÄÖ¸±ê¡£ + + + + + +Ö§³ÖÒÔÏ»º´æ¿â¡£ + + + + + +* Cache2k + +* Caffeine + +* Hazelcast + +* ÈκμæÈݵÄJCache£¨JSR-107£©ÊµÏÖ + +* Redis + + + + + +Ö¸±êÓÉ»º´æµÄÃû³ÆºÍ `CacheManager` µÄÃû³ÆÀ´±ê¼Ç£¬`CacheManager` µÄÃû³ÆÊÇÓÉBeanÃû³ÆÅÉÉúµÄ¡£ + + + + + +| | Ö»ÓÐÔÚÆô¶¯Ê±ÅäÖõĻº´æ±»°ó¶¨µ½×¢²á±í¡£ ¶ÔÓÚûÓÐÔÚ»º´æÅäÖÃÖж¨ÒåµÄ»º´æ£¬ÀýÈçÔÚÆô¶¯½×¶ÎºóÁÙʱ´´½¨µÄ»º´æ»òÒÔ±à³Ì·½Ê½´´½¨µÄ»º´æ£¬ÐèÒªÃ÷È·×¢²á¡£ Ò»¸ö `CacheMetricsRegistrar` Bean¿ÉÒÔʹÕâ¸ö¹ý³Ì¸üÈÝÒס£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.spring-graphql)7.3.12\. Spring GraphQL Ö¸±ê + + + +²Î¼û [Spring GraphQL ²Î¿¼Îĵµ](https://docs.spring.io/spring-graphql/docs/1.1.2/reference/html/)¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.jdbc)7.3.13\. DataSource Ö¸±ê + + + +×Ô¶¯ÅäÖÃʹËùÓпÉÓÃµÄ `DataSource` ¶ÔÏóµÄÒÇÆ÷»¯£¬Ö¸±êǰ׺Ϊ `jdbc.connections`¡£ Êý¾ÝÔ´¼ì²âµÄ½á¹ûÊDZíʾ³ØÖе±Ç°»î¶¯µÄ¡¢¿ÕÏеġ¢×î´óÔÊÐíµÄºÍ×îСÔÊÐíµÄÁ¬½ÓÊýµÄÒÇ±í¡£ + + + + + +ºâÁ¿±ê×¼Ò²ÓÉ»ùÓÚbeanÃû³Æ¼ÆËãµÄ `DataSource` µÄÃû³ÆÀ´±ê¼Ç¡£ + + + + + +| | ĬÈÏÇé¿öÏ£¬Spring BootΪËùÓÐÖ§³ÖµÄÊý¾ÝÔ´ÌṩԪÊý¾Ý¡£ Èç¹ûÄãϲ»¶µÄÊý¾ÝÔ´²»±»Ö§³Ö£¬Äã¿ÉÒÔÌí¼Ó¶îÍâµÄ `DataSourcePoolMetadataProvider` Bean¡£ Çë²ÎÔÄ `DataSourcePoolMetadataProvidersConfiguration` ÒÔÁ˽âʵÀý¡£ | +| --- | --- | + + + + + +´ËÍ⣬HikariÌØ¶¨µÄÖ¸±êÒÔ `hikaricp` ǰ׺±©Â¶¡£ ÿ¸öÖ¸±ê¶¼±»poolµÄÃû×ÖËù±ê¼Ç£¨Äã¿ÉÒÔÓà `spring.datasource.name` À´¿ØÖÆ£©¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.hibernate)7.3.14\. Hibernate Ö¸±ê + + + +Èç¹û `org.hibernate.orm:hibernate-micrometer` ÔÚclasspathÉÏ£¬ËùÓÐÆôÓÃÁËͳ¼Æ¹¦ÄܵÄHibernate `EntityManagerFactory` ʵÀý¶¼»á±»Ò»¸öÃûΪ `hibernate` µÄÖ¸±êËù¼ì²â¡£ + + + + + +ºâÁ¿±ê×¼Ò²ÓÉ `EntityManagerFactory` µÄÃû³ÆÀ´±ê¼Ç£¬¸ÃÃû³ÆÊÇÓÉBeanÃû³ÆÅÉÉúµÄ¡£ + + + + + +ÒªÆôÓÃͳ¼Æ£¬±ê×¼JPAÊôÐÔ `hibernate.generate_statistics` ±ØÐëÉèÖÃΪ `true`¡£ Äã¿ÉÒÔÔÚ×Ô¶¯ÅäÖÃµÄ `EntityManagerFactory` ÉÏÆôÓᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +spring.jpa.properties[hibernate.generate_statistics]=true + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.spring-data-repository)7.3.15\. Spring Data Repository Ö¸±ê + + + +×Ô¶¯ÅäÖÃÄܹ»¶ÔËùÓÐSpring Data `Repository` ·½·¨µÄµ÷ÓýøÐжÈÁ¿¡£ ĬÈÏÇé¿öÏ£¬Ö¸±êÒÔ `spring.data.repository.invocations` ΪÃûÉú³É¡£ Äã¿ÉÒÔͨ¹ýÉèÖà `management.metrics.data.repository.metric-name` ÊôÐÔÀ´×Ô¶¨ÒåÃû³Æ¡£ + + + + + +`io.micrometer.core.annotation` °üÖÐµÄ `@Timed` ×¢½âÖ§³Ö `Repository` ½Ó¿ÚºÍ·½·¨¡£Èç¹ûÄã²»Ïë¼Ç¼ËùÓÐ `Repository` µ÷ÓõĶÈÁ¿£¨metric£©£¬Äã¿ÉÒÔ½« `management.metrics.data.repository.autotime.enabled` ÉèÖÃΪ `false`£¬¶ø×¨ÃÅʹÓà `@Timed` ×¢½â¡£ + + + + + +| | Ò»¸ö´øÓÐ `longTask = true` µÄ `@Timed` ×¢½â¿ÉÒÔΪ¸Ã·½·¨ÆôÓÃÒ»¸ö³¤ÈÎÎñ¼ÆÊ±Æ÷¡£³¤ÈÎÎñ¶¨Ê±Æ÷ÐèÒªÒ»¸öµ¥¶ÀµÄ metric name£¬²¢ÇÒ¿ÉÒÔÓë¶ÌÈÎÎñ¶¨Ê±Æ÷£¨task timer£©µþ¼Ó¡£ | +| --- | --- | + + + + + +ĬÈÏÇé¿öÏ£¬repositoryµ÷ÓÃÏà¹ØµÄ¶ÈÁ¿±ê×¼±»±ê¼ÇΪÒÔÏÂÐÅÏ¢¡£ + + + + +| Tag | ˵Ã÷ | +| --- | --- | +| `repository` | Ô´ `Repository` µÄ¼òµ¥ÀàÃû¡£ | +| `method` | ±»µ÷ÓÃµÄ `Repository` ·½·¨µÄÃû³Æ¡£ | +| `state` | ½á¹û״̬£¨`SUCCESS`, `ERROR`, `CANCELED`, `RUNNING`£©¡£ | +| `exception` | µ÷ÓÃÖÐÅ׳öµÄÈκÎÒì³£µÄ¼òµ¥ÀàÃû¡£ | + + + +ÒªÌæ»»Ä¬ÈϱêÇ©£¬ÐèÒªÌṩһ¸öʵÏÖÁË `RepositoryTagsProvider` µÄ `@Bean`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.rabbitmq)7.3.16\. RabbitMQ Ö¸±ê + + + +×Ô¶¯ÅäÖÃʹËùÓпÉÓÃµÄ RabbitMQ Á¬½Ó¹¤³§µÄÒÇÆ÷»¯£¬ÆäÖ¸±êÃûΪ `rabbitmq`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.spring-integration)7.3.17\. Spring Integration Ö¸±ê + + + +Ö»ÒªÓÐ `MeterRegistry` bean£¬Spring Integration¾Í»á×Ô¶¯Ìṩ [Micrometer support](https://docs.spring.io/spring-integration/docs/6.1.0-M1/reference/html/system-management.html#micrometer-integration)¡£ ¶ÈÁ¿±ê×¼ÔÚ `spring.integration.` meter Ãû³ÆÏ·¢²¼¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.kafka)7.3.18\. Kafka Ö¸±ê + + + +×Ô¶¯ÅäÖÃΪ×Ô¶¯ÅäÖõÄÏû·ÑÕß¹¤³§ºÍÉú²úÕß¹¤³§·Ö±ð×¢²áÁËÒ»¸ö `MicrometerConsumerListener` ºÍ `MicrometerProducerListener`¡£ Ëü»¹Îª `StreamsBuilderFactoryBean` ×¢²áÁËÒ»¸ö `KafkaStreamsMicrometerListener`¡£ ¸ü¶àϸ½Ú£¬Çë²ÎÔÄSpring KafkaÎĵµÖÐµÄ [Micrometer Native Metrics](https://docs.spring.io/spring-kafka/docs/3.0.3/reference/html/#micrometer-native) ²¿·Ö¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.mongodb)7.3.19\. MongoDB Ö¸±ê + + + +±¾½Ú¼òÒª½éÉÜMongoDBµÄ¿ÉÓöÈÁ¿¡£ + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.mongodb.command)MongoDBÃüÁîÖ¸±ê + + + +×Ô¶¯ÅäÖý« `MongoMetricsCommandListener` Óë×Ô¶¯ÅäÖÃµÄ `MongoClient` ×¢²á¡£ + + + + + +Ò»¸öÃûΪ `mongodb.driver.commands` µÄtimerÖ¸±ê±»´´½¨£¬ÓÃÓÚÏòµ×²ãMongoDB driver·¢³öµÄÿÌõÃüÁî¡£ ĬÈÏÇé¿öÏ£¬Ã¿¸öÖ¸±ê¶¼±»±ê¼ÇΪÒÔÏÂÐÅÏ¢¡£ + + + + +| Tag | ˵Ã÷ | +| --- | --- | +| `command` | ·¢³öµÄÃüÁîµÄÃû³Æ¡£ | +| `cluster.id` | ·¢Ë͸ÃÃüÁîµÄ¼¯ÈºµÄ±êʶ·û¡£ | +| `server.address` | ·¢ËÍÃüÁîµÄ·þÎñÆ÷µÄµØÖ·¡£ | +| `status` | ÃüÁîµÄ½á¹û£¨`SUCCESS` »ò `FAILED£©¡£ | + + + +ΪÁËÌæ»»Ä¬ÈϵĶÈÁ¿ºâ±êÇ©£¬¶¨ÒåÒ»¸ö `MongoCommandTagsProvider` bean£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyCommandTagsProviderConfiguration { + + @Bean + public MongoCommandTagsProvider customCommandTagsProvider() { + return new CustomCommandTagsProvider(); + } + +} + +``` + + + + + + + +Òª½ûÓÃ×Ô¶¯ÅäÖõÄÃüÁî¶ÈÁ¿£¬ÇëÉèÖÃÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.metrics.mongo.command.enabled=false + +``` + + + + + + + + + +##### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.mongodb.connection-pool)MongoDB Á¬½Ó³ØÖ¸±ê + + + +×Ô¶¯ÅäÖý« `MongoMetricsConnectionPoolListener` Óë×Ô¶¯ÅäÖÃµÄ `MongoClient` ×¢²á¡£ + + + + + +ÒÔÏÂÊÇΪÁ¬½Ó³Ø´´½¨µÄ²âÁ¿Ö¸±ê¡£ + + + + + +* `mongodb.driver.pool.size` ±¨¸æÁ¬½Ó³ØµÄµ±Ç°´óС£¬°üÀ¨¿ÕÏкÍÕýÔÚʹÓõijÉÔ±¡£ + +* `mongodb.driver.pool.checkedout` ±¨¸æµ±Ç°Ê¹ÓÃÖеÄÁ¬½ÓÊý¡£ + +* `mongodb.driver.pool.waitqueuesize` ±¨¸æ³ØÖÐÁ¬½ÓµÄµÈ´ý¶ÓÁеĵ±Ç°´óС¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Ã¿¸öÖ¸±ê¶¼±»±ê¼ÇΪÒÔÏÂÐÅÏ¢¡£ + + + + +| Tag | ˵Ã÷ | +| --- | --- | +| `cluster.id` | Á¬½Ó³ØËù¶ÔÓ¦µÄ¼¯ÈºµÄ±êʶ·û¡£ | +| `server.address` | Á¬½Ó³ØËù¶ÔÓ¦µÄ·þÎñÆ÷µÄµØÖ·¡£ | + + + +Ҫȡ´úĬÈϵĶÈÁ¿ºâ±êÇ©£¬Ç붨ÒåÒ»¸ö `MongoConnectionPoolTagsProvider` bean¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyConnectionPoolTagsProviderConfiguration { + + @Bean + public MongoConnectionPoolTagsProvider customConnectionPoolTagsProvider() { + return new CustomConnectionPoolTagsProvider(); + } + +} + +``` + + + + + + + +Òª½ûÓÃ×Ô¶¯ÅäÖõÄÁ¬½Ó³Ø¶ÈÁ¿£¬ÇëÉèÖÃÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.metrics.mongo.connectionpool.enabled=false + +``` + + + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.jetty)7.3.20\. Jetty Ö¸±ê + + + +×Ô¶¯ÅäÖÃͨ¹ýʹÓÃMicrometerµÄ `JettyServerThreadPoolMetrics` ΪJettyµÄ `ThreadPool` °ó¶¨Ö¸±ê¡£ JettyµÄ `Connector` ʵÀýµÄÖ¸±êͨ¹ýʹÓÃMicrometerµÄ `JettyConnectionMetrics` À´°ó¶¨£¬µ± `server.ssl.enabled` ±»ÉèÖÃΪ `true` ʱ£¬MicrometerµÄ `JettySslHandshakeMetrics`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.timed-annotation)7.3.21\. @Timed ×¢½âµÄÖ§³Ö + + + +ÒªÔÚSpring Boot²»Ö±½ÓÖ§³ÖµÄµØ·½Ê¹Óà `@Timed`£¬Çë²Î¿¼ [Micrometer Îĵµ](https://micrometer.io/docs/concepts#_the_timed_annotation)¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.supported.redis)7.3.22\. Redis Ö¸±ê + + + +×Ô¶¯ÅäÖÃΪ×Ô¶¯ÅäÖÃµÄ `LettuceConnectionFactory` ×¢²áÁËÒ»¸ö `MicrometerCommandLatencyRecorder`¡£ ¸ü¶àϸ½Ú£¬Çë²ÎÔÄLettuceÎĵµµÄ [Micrometer Metrics²¿·Ö](https://lettuce.io/core/6.2.3.RELEASE/reference/index.html#command.latency.metrics.micrometer)¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.registering-custom)7.4\. ×¢²á×Ô¶¨ÒåÖ¸±ê + + + +Ҫע²á×Ô¶¨Òå¶ÈÁ¿£¬Ç뽫 `MeterRegistry` ×¢ÈëÄãµÄ×é¼þÖС£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Component +public class MyBean { + + private final Dictionary dictionary; + + public MyBean(MeterRegistry registry) { + this.dictionary = Dictionary.load(); + registry.gauge("dictionary.size", Tags.empty(), this.dictionary.getWords().size()); + } + +} + +``` + + + + + + + +Èç¹ûÄãµÄ¶ÈÁ¿±ê×¼ÒÀÀµÓÚÆäËûBean£¬ÎÒÃǽ¨ÒéÄãʹÓà `MeterBinder` À´×¢²áËüÃÇ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +public class MyMeterBinderConfiguration { + + @Bean + public MeterBinder queueSize(Queue queue) { + return (registry) -> Gauge.builder("queueSize", queue::size).register(registry); + } + +} + +``` + + + + + + + +ʹÓà `MeterBinder` ¿ÉÒÔÈ·±£½¨Á¢ÕýÈ·µÄÒÀÀµ¹ØÏµ£¬²¢ÇÒÔÚ¼ìË÷¶ÈÁ¿ÖµµÄʱºò£¬BeanÊÇ¿ÉÓõġ£ Èç¹ûÄã·¢ÏÖÄãÔÚ×é¼þ»òÓ¦ÓóÌÐòÖÐÖØ¸´²âÁ¿Ò»Ì×Ö¸±ê£¬ÄÇô `MeterBinder` µÄʵÏÖÒ²»áºÜÓÐÓᣠ+ + + + + +| | ĬÈÏÇé¿öÏ£¬ËùÓÐ `MeterBinder` BeanµÄÖ¸±ê¶¼»á×Ô¶¯°ó¶¨µ½Spring¹ÜÀíµÄ `MeterRegistry`¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.customizing)7.5\. ¶¨ÖƸö±ðÖ¸±ê + + + +Èç¹ûÄãÐèÒª¶ÔÌØ¶¨µÄ `Meter` ʵÀý½øÐÐ×Ô¶¨Ò壬Äã¿ÉÒÔʹÓà `io.micrometer.core.instrument.config.MeterFilter` ½Ó¿Ú¡£ + + + + + +ÀýÈ磬Èç¹ûÄãÏë°ÑËùÓÐÒÔ `com.example` ¿ªÍ·µÄÒDZíIDµÄ `mytag.region` ±êÇ©ÖØÃüÃûΪ `mytag.area`£¬Äã¿ÉÒÔ×öÒÔϹ¤×÷¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyMetricsFilterConfiguration { + + @Bean + public MeterFilter renameRegionTagMeterFilter() { + return MeterFilter.renameTag("com.example", "mytag.region", "mytag.area"); + } + +} + +``` + + + + + + + +| | ĬÈÏÇé¿öÏ£¬ËùÓÐµÄ `MeterFilter` Bean¶¼×Ô¶¯°ó¶¨µ½Spring¹ÜÀíµÄ `MeterRegistry`¡£ ÇëÈ·±£Ê¹ÓÃSpring¹ÜÀíµÄ `MeterRegistry` À´×¢²áÄãµÄÖ¸±ê£¬¶ø²»ÊÇʹÓà `Metrics` µÄÈκξ²Ì¬·½·¨¡£ ÕâЩ·½·¨Ê¹ÓõÄÊDz»±»Spring¹ÜÀíµÄÈ«¾Ö×¢²á±í¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.customizing.common-tags)7.5.1\. ³£¼û±êÇ©£¨Tag£© + + + +ͨÓñêǩһ°ãÓÃÓÚ¶ÔÔËÐл·¾³½øÐÐά¶ÈÏÂ×꣬ÈçÖ÷»ú¡¢ÊµÀý¡¢ÇøÓò¡¢¶ÑÕ»µÈ¡£ ¹²ÓñêÇ©ÊÊÓÃÓÚËùÓÐÒÇ±í£¬¿ÉÒÔ½øÐÐÅäÖã¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.metrics.tags.region=us-east-1 +management.metrics.tags.stack=prod + +``` + + + + + + + +Ç°ÃæµÄÀý×ÓΪËùÓÐֵΪ `us-east-1` ºÍ `prod` µÄÒDZíÌí¼ÓÁË `region` ºÍ `stack` ±êÇ©¡£ + + + + + +| | Èç¹ûÄãʹÓÃGraphite£¬ÆÕͨ±êÇ©µÄ˳ÐòÊǺÜÖØÒªµÄ¡£ ÓÉÓÚʹÓÃÕâÖÖ·½·¨²»Äܱ£Ö¤³£¼û±êÇ©µÄ˳Ðò£¬½¨ÒéGraphiteÓû§¶¨ÒåÒ»¸ö×Ô¶¨ÒåµÄ `MeterFilter` À´´úÌæ¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.customizing.per-meter-properties)7.5.2\. Per-meter Properties + + + +³ýÁË `MeterFilter` Bean£¬Ä㻹¿ÉÒÔʹÓÃÊôÐÔÔÚÿ¸ö±íµÄ»ù´¡ÉÏÓ¦ÓÃÒ»×éÓÐÏÞµÄ×Ô¶¨Ò幦ÄÜ¡£ ʹÓÃSpring BootµÄ `PropertiesMeterFilter`£¬Ã¿¸ö±íµÄ¶¨ÖƱ»Ó¦ÓÃÓÚÒÔ¸ø¶¨Ãû³Æ¿ªÍ·µÄÈκαíµÄID¡£ ÏÂÃæµÄÀý×Ó¹ýÂ˵ôÈκÎIDÒÔ `example.remote` ¿ªÍ·µÄÒÇ±í¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.metrics.enable.example.remote=false + +``` + + + + + + + +ÏÂÃæµÄÊôÐÔÔÊÐíper-meterµÄ¶¨ÖÆ¡£ + + + +Table 1\. Per-meter customizations +| Property | ˵Ã÷ | +| --- | --- | +| `management.metrics.enable` | ÊÇ·ñ½ÓÊܾßÓÐÌØ¶¨IDµÄMeter¡£ ²»½ÓÊܵÄMeter½«´Ó `MeterRegistry` ÖйýÂ˵ô¡£ | +| `management.metrics.distribution.percentiles-histogram` | ÊÇ·ñ·¢²¼ÊʺϼÆËã¿É¾Û¼¯£¨¿çά¶È£©µÄ°Ù·ÖλÊý½üËÆÖµµÄÖ±·½Í¼¡£ | +| `management.metrics.distribution.minimum-expected-value`, `management.metrics.distribution.maximum-expected-value` | ͨ¹ýÇ¯ÖÆÔ¤ÆÚÖµµÄ·¶Î§£¬·¢²¼¸üÉÙµÄÖ±·½Í¼Í°¡£ | +| `management.metrics.distribution.percentiles` | ·¢²¼ÔÚÄãµÄÓ¦ÓóÌÐòÖмÆËãµÄ°Ù·Öλֵ | +| `management.metrics.distribution.expiry`, `management.metrics.distribution.buffer-length` | ͨ¹ýÔÚ»·Ðλº³åÇøÖлýÀÛ×î½üµÄÑù±¾£¬¸øËüÃǸü´óµÄÈ¨ÖØ£¬»·Ðλº³åÇøÔÚ¿ÉÅäÖõĹýÆÚºóÐýת£¬»º³åÇø³¤¶ÈΪ ¿ÉÅäÖõĻº³åÇø³¤¶È¡£ | +| `management.metrics.distribution.slo` | ·¢²¼Ò»¸öÀÛ»ýÖ±·½Í¼£¬ÆäÖеÄͰÓÉÄãµÄ·þÎñˮƽĿ±ê¶¨Òå¡£ | + + + +¹ØÓÚ `percentiles-histogram` £¨°Ù·ÖÊý-Ö±·½Í¼£©¡¢`percentiles`£¨°Ù·ÖÊý£©ºÍ `slo` ±³ºóµÄ¸ÅÄîµÄ¸ü¶àϸ½Ú£¬Çë²Î¼ûMicrometerÎĵµÖÐµÄ [¡°Histograms and percentiles¡± £¨Ö±·½Í¼ºÍ°Ù·ÖÊý£©²¿·Ö](https://micrometer.io/docs/concepts#_histograms_and_percentiles)¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.endpoint)7.6\. Ö¸±ê¶Ëµã + + + +Spring BootÌṩÁËÒ»¸ö `metrics` ¶Ëµã£¬Äã¿ÉÒÔÕï¶ÏÐÔµØÊ¹ÓÃËüÀ´¼ì²éÓ¦ÓóÌÐòÊÕ¼¯µÄÖ¸±ê¡£¸Ã¶ËµãĬÈÏÇé¿öÏÂÊDz»¿ÉÓõ쬱ØÐ빫¿ª¡£¸ü¶àϸ½ÚÇë²Î¼û [±©Â¶¶Ëµã](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints.exposing)¡£ + + + + + +µ¼º½µ½ `/actuator/metrics` »áÏÔʾһ¸ö¿ÉÓõÄÒDZíÃû³ÆÁÐ±í¡£ Äã¿ÉÒÔͨ¹ýÌṩÃû³Æ×÷ΪѡÔñÆ÷À´ÉîÈë²é¿´Ä³¸öÌØ¶¨ÒDZíµÄÐÅÏ¢£¬ÀýÈ磬`/actuator/metrics/jvm.memory.max`¡£ + + + + + +| | ÄãÔÚÕâÀïʹÓõÄÃû×ÖÓ¦¸ÃÓë´úÂëÖÐʹÓõÄÃû×ÖÒ»Ö£¬¶ø²»ÊÇÔÚËü±»ÔËÍùµÄ¼à¿ØÏµÍ³Öо­¹ýÃüÃû¹ßÀý¹æ·¶»¯ºóµÄÃû×Ö¡£ »»¾ä»°Ëµ£¬Èç¹û `jvm.memory.max` ÔÚPrometheusÖÐÏÔʾΪ `jvm_memory_max`£¬ÒòΪËüµÄÉßÐÎÃüÃû¹ßÀý£¬ÄãÈÔȻӦ¸ÃʹÓà `jvm.memory.max` ×÷ΪѡÔñÆ÷£¬ÔÚ `metrics` ¶ËµãÖмì²éÒÇ±í¡£ | +| --- | --- | + + + + + +ÄãÒ²¿ÉÒÔÔÚURLµÄĩβÌí¼ÓÈÎÒâÊýÁ¿µÄ `tag=KEY:VALUE` ²éѯ²ÎÊý£¬ÒÔ¶ÔÒÇ±í½øÐÐά¶ÈÏÂ×ê?¡ª?ÀýÈ磬`/actuator/metrics/jvm.memory.max?tag=area:nonheap`¡£ + + + + + +| | ±¨¸æµÄ²âÁ¿ÖµÊÇËùÓÐÓëÒDZíÃû³ÆÏàÆ¥ÅäµÄÒDZíºÍÈκÎÒÑÓ¦ÓõıêÇ©µÄͳ¼ÆÁ¿µÄ _×ܺÍ_¡£ ÔÚÇ°ÃæµÄÀý×ÓÖУ¬·µ»ØµÄ `Value` ͳ¼ÆÊÇ¶ÑµÄ ¡°Code Cache¡±¡¢¡°Compressed Class Space¡± ºÍ ¡°Metaspace¡± ÇøÓòµÄ×î´óÄÚ´æ¡°×ã¼£¡±Ö®ºÍ¡£ Èç¹ûÄãÏëÖ»¿´µ½ ¡°Metaspace¡± µÄ×î´ó³ß´ç£¬Äã¿ÉÒÔÌí¼ÓÒ»¸ö¶îÍâµÄ `tag=id:Metaspace` --¼´ `/actuator/metrics/jvm.memory.max?tag=area:nonheap&tag=id:Metaspace`¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.metrics.micrometer-observation)7.7\. ÕûºÏ Micrometer Observation + + + +Ò»¸ö `DefaultMeterObservationHandler` ±»×Ô¶¯×¢²áÔÚ `ObservationRegistry` ÉÏ£¬ËüΪÿ¸öÍê³ÉµÄ¹Û²ì£¨completed observation£©´´½¨¶ÈÁ¿£¨metric£©¡£ + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing)8\. ×·×Ù£¨Tracing£© + + + + + +Spring Boot Actuator Ϊ Micrometer Tracing ÌṩÁËÒÀÀµÐÔ¹ÜÀíºÍ×Ô¶¯ÅäÖ㬠[Micrometer Tracing](https://micrometer.io/docs/tracing) ÊÇÁ÷ÐеÄ×·×ÙÆ÷£¨tracer£©¿âµÄÒ»¸ö½Ó¿Ú£¨facade£©¡£ + + + + + +| | ÒªÁ˽â¸ü¶à¹ØÓÚ Micrometer Tracing ¹¦ÄܵÄÐÅÏ¢£¬Çë²ÎÔÄÆä [²Î¿¼Îĵµ](https://micrometer.io/docs/tracing)¡£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracers)8.1\. Ö§³ÖµÄ×·×ÙÆ÷ + + + +Spring BootΪÒÔÏÂ×·×ÙÆ÷ÌṩÁË×Ô¶¯ÅäÖᣠ+ + + + + +* ʹÓà [Zipkin](https://zipkin.io/) »ò [Wavefront](https://docs.wavefront.com/) µÄ [OpenTelemetry](https://opentelemetry.io/) + +* ʹÓà [Zipkin](https://zipkin.io/) »ò [Wavefront](https://docs.wavefront.com/) µÄ [OpenZipkin Brave](https://github.com/openzipkin/brave) + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.getting-started)8.2\. ÈëÃÅ + + + +ÎÒÃÇÐèÒªÒ»¸ö¿ÉÒÔÓÃÀ´¿ªÊ¼×·×ÙµÄʾÀýÓ¦ÓóÌÐò¡£¾ÍÎÒÃǵÄÄ¿µÄ¶øÑÔ£¬¡°[getting-started.html](https://springdoc.cn/spring-boot/getting-started.html#getting-started.first-application)¡± ²¿·ÖËùÉæ¼°µÄ¼òµ¥µÄ ¡°Hello World!¡± web³ÌÐò¾Í×ã¹»ÁË¡£ÎÒÃǽ«Ê¹Óà `OpenTelemetry` ×·×ÙÆ÷ºÍ `Zipkin` ×÷Ϊ׷×Ùºó¶Ë¡£ + + + + + +»Ø¹Ëһϣ¬ÎÒÃǵÄÖ÷ÒªÓ¦ÓôúÂë¿´ÆðÀ´ÊÇÕâÑùµÄ¡£ + + + + + + + +``` +@RestController +@SpringBootApplication +public class MyApplication { + + private static final Log logger = LogFactory.getLog(MyApplication.class); + + @RequestMapping("/") + String home() { + logger.info("home() has been called"); + return "Hello World!"; + } + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + + + + + + + +| | ÔÚ `home()` ·½·¨ÖУ¬ÓÐÒ»¸ö¸½¼ÓµÄloggerÓï¾ä£¬ÕâÔÚºóÃæ»áºÜÖØÒª¡£ | +| --- | --- | + + + + + +ÏÖÔÚ£¬ÎÒÃDZØÐëÌí¼ÓÒÔÏÂÒÀÀµÏî¡£ + + + + + +* `org.springframework.boot:spring-boot-starter-actuator` + +* `io.micrometer:micrometer-tracing-bridge-otel` - ÕâÊÇÁ¬½Ó Micrometer Observation API ºÍ OpenTelemetry µÄ±ØÒªÌõ¼þ¡£ + +* `io.opentelemetry:opentelemetry-exporter-zipkin` - ÕâÊÇÏòZipkin±¨¸æ [traces](https://micrometer.io/docs/tracing#_glossary) ËùÐèÒªµÄ¡£ + + + + + +Ìí¼ÓÈçÏ嵀 application properties: + + + + + + + +Properties + +Yaml + + + + + +``` +management.tracing.sampling.probability=1.0 + +``` + + + + + + + +ĬÈÏÇé¿öÏ£¬Spring BootÖ»¶Ô10%µÄÇëÇó½øÐвÉÑù£¬ÒÔ·ÀÖ¹×·×Ùºó¶Ë²»¿°Öظº¡£´ËÊôÐÔ½«ÆäÇл»Îª100%£¬ÕâÑùÿ¸öÇëÇ󶼻ᱻ·¢Ë͵½¸ú×Ùºó¶Ë¡£ + + + + + +ΪÁËÊÕ¼¯ºÍ¿ÉÊÓ»¯¸ú×Ù£¬ÎÒÃÇÐèÒªÒ»¸öÔËÐиú×ٵĺó¶Ë¡£ÎÒÃÇÔÚÕâÀïʹÓÃZipkin×÷ΪÎÒÃǵĸú×Ùºó¶Ë¡£ [Zipkin¿ìËÙÈëÃÅÖ¸ÄÏ](https://zipkin.io/pages/quickstart) ÌṩÁËÈçºÎÔÚ±¾µØÆô¶¯ZipkinµÄ˵Ã÷¡£ + + + + + +ZipkinÔËÐкó£¬Äã¿ÉÒÔÆô¶¯ÄãµÄÓ¦ÓóÌÐò¡£ + + + + + +Èç¹ûÄã´ò¿ªwebä¯ÀÀÆ÷·ÃÎÊ `[localhost:8080](http://localhost:8080/)`£¬ÄãÓ¦¸Ã¿´µ½ÒÔÏÂÊä³ö¡£ + + + + + + + + Hello World! + + + + + + + +ÔÚÄ»ºó£¬ÒѾ­ÎªHTTPÇëÇó´´½¨ÁËÒ»¸ö observation£¬Ëü·´¹ýÀ´±»ÇŽӵ½ `OpenTelemetry`£¬ºóÕßÏòZipkin±¨¸æÒ»¸öеĸú×Ù£¨trace£©¡£ + + + + + +ÏÖÔÚ£¬ÔÚ `[localhost:9411](http://localhost:9411/)` ´ò¿ªZipkinÓû§½çÃæ£¬µã»÷ "Run Query" °´Å¥£¬ÁгöËùÓÐÊÕ¼¯µ½µÄ¸ú×ÙÐÅÏ¢¡£ÄãÓ¦¸Ã¿´µ½Ò»¸ö×·×Ù¡£µã»÷ "Show" °´Å¥£¬²é¿´¸Ã×·×ÙµÄϸ½Ú¡£ + + + + + +| | Äã¿ÉÒÔͨ¹ý½« `logging.pattern.level` ÊôÐÔÉèÖÃΪ `%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]`£¬ÔÚÈÕÖ¾Öаüº¬µ±Ç°µÄ¸ú×Ù£¨trace£©ºÍ span id¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracer-implementations)8.3\. ¸ú×ÙÆ÷£¨Tracer£©µÄʵÏÖ + + + +ÓÉÓÚMicrometer TracerÖ§³Ö¶àÖÖʾ×ÙÆ÷µÄʵÏÖ£¬Òò´ËSpring Boot¿ÉÄÜÓжàÖÖÒÀÀµ×éºÏ¡£ + + + + + +ËùÓÐ×·×ÙÆ÷µÄʵÏÖ¶¼ÐèÒª `org.springframework.boot:spring-boot-starter-actuator` ÒÀÀµ¡£ + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracer-implementations.otel-zipkin)8.3.1\. ʹÓà Zipkin µÄ OpenTelemetry + + + +* `io.micrometer:micrometer-tracing-bridge-otel` - ÕâÊÇÁ¬½Ó Micrometer Observation API ºÍ OpenTelemetry µÄ±ØÒªÌõ¼þ¡£ + +* `io.opentelemetry:opentelemetry-exporter-zipkin` - ÕâÊÇÏòZipkin±¨¸ætraceËùÐèÒªµÄ¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracer-implementations.otel-wavefront)8.3.2\. ʹÓà Wavefront µÄ OpenTelemetry + + + +* `io.micrometer:micrometer-tracing-bridge-otel` - ÕâÊÇÁ¬½Ó Micrometer Observation API ºÍ OpenTelemetry µÄ±ØÒªÌõ¼þ¡£ + +* `io.micrometer:micrometer-tracing-reporter-wavefront` - ÕâÊÇÏòWavefront±¨¸ætraceËùÐèÒªµÄ¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracer-implementations.brave-zipkin)8.3.3\. ʹÓà Zipkin µÄ OpenZipkin Brave + + + +* `io.micrometer:micrometer-tracing-bridge-brave` - ÕâÊÇÁ¬½Ó Micrometer Observation API ºÍ Brave µÄ±ØÒªÌõ¼þ¡£ + +* `io.zipkin.reporter2:zipkin-reporter-brave` - ÕâÊÇÏòZipkin±¨¸æ trace ËùÐèÒªµÄ¡£ + + + + + +| | Èç¹ûÄãµÄÏîĿûÓÐʹÓÃSpring MVC»òSpring WebFlux£¬Ò²ÐèҪʹÓà `io.zipkin.reporter2:zipkin-sender-urlconnection` ÒÀÀµÏî¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.tracer-implementations.brave-wavefront)8.3.4\. ʹÓÃWavefrontµÄOpenZipkin Brave + + + +* `io.micrometer:micrometer-tracing-bridge-brave` - ÕâÊÇÁ¬½Ó²âMicrometer Observation APIºÍBraveµÄ±ØÒªÌõ¼þ¡£ + +* `io.micrometer:micrometer-tracing-reporter-wavefront` - ÕâÊÇÏòWavefront±¨¸ætraceËùÐèÒªµÄ¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.micrometer-tracing.creating-spans)8.4\. ´´½¨×Ô¶¨Òå¿ç¶È£¨span£© + + + +Äã¿ÉÒÔͨ¹ýÆô¶¯Ò»¸ö observation À´´´½¨Äã×Ô¼ºµÄspan¡£Îª´Ë£¬½« `ObservationRegistry` ×¢Èëµ½ÄãµÄ×é¼þÖС£ + + + + + + + +``` +@Component +class CustomObservation { + + private final ObservationRegistry observationRegistry; + + CustomObservation(ObservationRegistry observationRegistry) { + this.observationRegistry = observationRegistry; + } + + void someOperation() { + Observation observation = Observation.createNotStarted("some-operation", this.observationRegistry); + observation.lowCardinalityKeyValue("some-tag", "some-value"); + observation.observe(() -> { + // Business logic ... + }); + } + +} + +``` + + + + + + + +Õ⽫´´½¨Ò»¸öÃûΪ "some-operation" µÄ observation£¬±êǩΪ£¬±êǩΪ "some-tag=some-value"¡£ + + + + + +| | Èç¹ûÄãÏëÔÚ²»´´½¨metricµÄÇé¿öÏ´´½¨Ò»¸öspan£¬ÄãÐèҪʹÓà Micrometer µÄ [µÍ¼¶Tracer API](https://micrometer.io/docs/tracing#_using_micrometer_tracing_directly)¡£ | +| --- | --- | + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.auditing)9\. Éó¼Æ + + + + + +Ò»µ©Spring Security·¢»Ó×÷Óã¬Spring Boot Actuator¾ÍÓÐÒ»¸öÁé»îµÄÉó¼Æ¿ò¼Ü£¬¿ÉÒÔ·¢²¼Ê¼þ£¨Ä¬ÈÏΪ ¡°authentication success¡±, ¡°failure¡± ºÍ ¡°access denied¡± µÄÒì³££©¡£ ÕâÒ»¹¦ÄܶÔÓÚ±¨¸æºÍʵʩ»ùÓÚÈÏ֤ʧ°ÜµÄËø¶¨²ßÂԷdz£ÓÐÓᣠ+ + + + + +Äã¿ÉÒÔͨ¹ýÔÚÓ¦ÓóÌÐòµÄÅäÖÃÖÐÌṩһ¸ö `AuditEventRepository` ÀàÐ͵ÄbeanÀ´ÆôÓÃÉ󼯡£ ΪÁË·½±ã£¬Spring BootÌṩÁËÒ»¸ö `InMemoryAuditEventRepository`¡£ `InMemoryAuditEventRepository` µÄ¹¦ÄÜÓÐÏÞ£¬ÎÒÃǽ¨ÒéÖ»ÔÚ¿ª·¢»·¾³ÖÐʹÓÃËü¡£ ¶ÔÓÚÉú²ú»·¾³£¬Ç뿼ÂÇ´´½¨Äã×Ô¼ºµÄÌæ´ú `AuditEventRepository` ʵÏÖ¡£ + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.auditing.custom)9.1\. ¶¨ÖÆÉó¼Æ + + + +ΪÁ˶¨ÖÆ·¢²¼µÄ°²È«Ê¼þ£¬Äã¿ÉÒÔÌṩÄã×Ô¼ºµÄ `AbstractAuthenticationAuditListener` ºÍ `AbstractAuthorizationAuditListener` µÄʵÏÖ¡£ + + + + + +ÄãÒ²¿ÉÒÔΪÄã×Ô¼ºµÄÒµÎñʼþʹÓÃÉ󼯷þÎñ¡£ Òª×öµ½ÕâÒ»µã£¬ÒªÃ´½« `AuditEventRepository` bean×¢ÈëÄã×Ô¼ºµÄ×é¼þ²¢Ö±½ÓʹÓÃËü£¬ÒªÃ´ÓÃSpringµÄ `ApplicationEventPublisher` ·¢²¼ `AuditApplicationEvent`£¨Í¨¹ýʵÏÖ `ApplicationEventPublisherAware`£©¡£ + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.http-exchanges)10\. ¼Ç¼ HTTP Exchange + + + + + +Äã¿ÉÒÔͨ¹ýÔÚÓ¦ÓóÌÐòµÄÅäÖÃÖÐÌṩһ¸ö `HttpExchangeRepository` ÀàÐ굀 bean À´ÆôÓà HTTP exchange µÄ¼Ç¼¡£ÎªÁË·½±ãÆð¼û£¬Spring Boot ÌṩÁË `InMemoryHttpExchangeRepository`£¬Ä¬ÈÏÇé¿öÏ£¬Ëü´æ´¢ÁË×îºó100¸ö request/response exchange¡£Óë×·×Ù½â¾ö·½°¸£¨tracing solutions£©Ïà±È£¬`InMemoryHttpExchangeRepository` ÊÇÓÐÏ޵ģ¬ÎÒÃǽ¨ÒéÖ»ÔÚ¿ª·¢»·¾³ÖÐʹÓÃËü¡£¶ÔÓÚÉú²ú»·¾³£¬ÎÒÃǽ¨ÒéʹÓÃÒ»¸öÉú²ú¾ÍÐ÷µÄ¸ú×Ù»ò¹Û²ì½â¾ö·½°¸£¬Èç `Zipkin` »ò `OpenTelemetry`¡£ÁíÍ⣬ÄãÒ²¿ÉÒÔ´´½¨Äã×Ô¼ºµÄ `HttpExchangeRepository`¡£ + + + + + +Äã¿ÉÒÔʹÓà `httpexchanges` ¶ËµãÀ´»ñÈ¡´æ´¢ÔÚ `HttpExchangeRepository` ÖÐµÄ request/response exchange µÄÐÅÏ¢¡£ + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.http-exchanges.custom)10.1\. ×Ô¶¨Òå HTTP Exchange ¼Ç¼ + + + +Òª×Ô¶¨Òå°üÀ¨ÔÚÿ¸ö¼Ç¼µÄ exchange ÏîÄ¿£¬ÇëʹÓà `management.httpexchanges.recording.include` ÅäÖÃÊôÐÔ¡£ + + + + + +ÒªÍêÈ«½ûÖ¹ÖØÐ±àÂ룬Ç뽫 `management.httpexchanges.recording.enabled` ÉèÖÃΪ `false`¡£ + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring)11\. ½ø³Ì¼à¿Ø + + + + + +ÔÚ `spring-boot` Ä£¿éÖУ¬Äã¿ÉÒÔÕÒµ½Á½¸ö´´½¨ÎļþµÄÀ࣬ÕâЩÎļþÔÚ½ø³Ì¼à¿ØÖÐͨ³£ºÜÓÐÓᣠ+ + + + + +* `ApplicationPidFileWriter` ´´½¨Ò»¸ö°üº¬Ó¦ÓóÌÐòPIDµÄÎļþ£¨Ä¬ÈÏÇé¿öÏ£¬ÔÚÓ¦ÓóÌÐòĿ¼Ï£¬ÎļþÃûΪ `application.pid`£©¡£ + +* `WebServerPortFileWriter` ´´½¨Ò»¸ö£¨»ò¶à¸ö£©Îļþ£¬°üº¬ÔËÐÐÖеÄWeb·þÎñÆ÷µÄ¶Ë¿Ú£¨Ä¬ÈÏÇé¿öÏ£¬ÔÚÓ¦ÓóÌÐòĿ¼Ï£¬ÎļþÃûΪ `application.port`£©¡£ + + + + + +ĬÈÏÇé¿öÏ£¬ÕâЩдÊÖûÓб»¼¤»î£¬µ«Äã¿ÉÒÔÆôÓÃËüÃÇ¡£ + + + + + +* [ͨ¹ýÀ©Õ¹ÅäÖÃ](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring.configuration) + +* [ÒÔ±à³Ì·½Ê½ÊµÏÖ½ø³Ì¼à¿Ø](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring.programmatically) + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring.configuration)11.1\. À©Õ¹ÅäÖà + + + +ÔÚ `META-INF/spring.factories` ÎļþÖУ¬Äã¿ÉÒÔ¼¤»îдÈëPIDÎļþµÄlistener£¨Ò»¸ö»òÕß¶à¸ö£©¡£ + + + + + + + + org.springframework.context.ApplicationListener=\ +org.springframework.boot.context.ApplicationPidFileWriter,\ +org.springframework.boot.web.context.WebServerPortFileWriter + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.process-monitoring.programmatically)11.2\. ÒÔ±à³Ì·½Ê½ÊµÏÖ½ø³Ì¼à¿Ø + + + +ÄãÒ²¿ÉÒÔͨ¹ýµ÷Óà `SpringApplication.addListeners(¡­?)` ·½·¨²¢´«µÝÊʵ±µÄ `Writer` ¶ÔÏóÀ´¼¤»îÒ»¸ö¼àÌýÆ÷¡£ Õâ¸ö·½·¨»¹¿ÉÒÔÈÃÄãÔÚ `Writer` ¹¹Ô캯ÊýÖÐ×Ô¶¨ÒåÎļþÃûºÍ·¾¶¡£ + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.cloud-foundry)12\. Cloud Foundry µÄÖ§³Ö + + + + + +Spring Boot µÄactuatorÄ£¿é°üÀ¨¶îÍâµÄÖ§³Ö£¬µ±Äú²¿Êðµ½¼æÈÝµÄ Cloud Foundry ʵÀýʱ£¬¸ÃÖ§³Ö½«±»¼¤»î¡£ `/cloudfoundryapplication` ·¾¶ÎªËùÓÐ `@Endpoint` BeanÌṩÁËÁíÒ»Ìõ°²È«Â·Ïß¡£ + + + + + +À©Õ¹Ö§³Öʹ Cloud Foundry ¹ÜÀí UI£¨ÀýÈçÄú¿ÉÒÔÓÃÀ´²é¿´ÒѲ¿ÊðµÄÓ¦ÓóÌÐòµÄ Web Ó¦ÓóÌÐò£©µÃµ½ Spring Boot Ö´ÐÐÆ÷ÐÅÏ¢µÄÔöÇ¿¡£ ÀýÈ磬ӦÓóÌÐò×´Ì¬Ò³Ãæ¿ÉÒÔ°üÀ¨ÍêÕûµÄ½¡¿µÐÅÏ¢£¬¶ø²»ÊǵäÐ굀 ¡°running¡± »ò ¡°stopped¡± ״̬¡£ + + + + + +| | ÆÕͨÓû§ÎÞ·¨Ö±½Ó·ÃÎÊ `/cloudfoundryapplication` ·¾¶¡£ ҪʹÓøö˵㣬Äú±ØÐëÔÚÇëÇóÖд«µÝÒ»¸öÓÐЧµÄ UAA ÁîÅÆ¡£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.cloud-foundry.disable)12.1\. ½ûÓÃÀ©Õ¹µÄ Cloud Foundry Actuator Ö§³Ö + + + +Èç¹ûÄúÏëÍêÈ«½ûÓà `/cloudfoundryapplication` ¶Ëµã£¬Äú¿ÉÒÔÔÚÄúµÄ `application.properties` ÎļþÖÐÌí¼ÓÒÔÏÂÉèÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +management.cloudfoundry.enabled=false + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.cloud-foundry.ssl)12.2\. Cloud Foundry×ÔÇ©ÃûÖ¤Êé + + + +ĬÈÏÇé¿öÏ£¬`/cloudfoundryapplication` ¶ËµãµÄ°²È«ÑéÖ¤»á¶Ô¸÷ÖÖ Cloud Foundry ·þÎñ½øÐÐ SSL µ÷ÓᣠÈç¹ûÄúµÄ Cloud Foundry UAA »ò Cloud Controller ·þÎñʹÓÃ×ÔÇ©ÃûÖ¤Ê飬ÄúÐèÒªÉèÖÃÒÔÏÂÊôÐÔ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +management.cloudfoundry.skip-ssl-validation=true + +``` + + + + + + + + + +### [](https://springdoc.cn/spring-boot/actuator.html#actuator.cloud-foundry.custom-context-path)12.3\. ×Ô¶¨Òå Context Path + + + +Èç¹û·þÎñÆ÷µÄ context-path ±»ÅäÖÃΪ `/` ÒÔÍâµÄÈκÎÄÚÈÝ£¬Ôò Cloud Foundry ¶ËµãÔÚÓ¦ÓóÌÐòµÄ¸ù²¿²»¿ÉÓᣠÀýÈ磬Èç¹û `server.servlet.context-path=/app`£¬Ôò Cloud Foundry ¶ËµãÔÚ `/app/cloudfoundryapplication/*` ´¦¿ÉÓᣠ+ + + + + +Èç¹ûÄúÏ£Íû Cloud Foundry ¶ËµãʼÖÕÔÚ `/cloudfoundryapplication/*` ´¦¿ÉÓã¬ÎÞÂÛ·þÎñÆ÷µÄÉÏÏÂÎÄ·¾¶ÈçºÎ£¬ÄúÐèÒªÔÚÄúµÄÓ¦ÓóÌÐòÖÐÃ÷È·ÅäÖᣠ¸ÃÅäÖÃÒòʹÓÃµÄ Web ·þÎñÆ÷²»Í¬¶ø²»Í¬¡£ ¶ÔÓÚ Tomcat£¬Äú¿ÉÒÔÌí¼ÓÒÔÏÂÅäÖᣠ+ + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class MyCloudFoundryConfiguration { + + @Bean + public TomcatServletWebServerFactory servletWebServerFactory() { + return new TomcatServletWebServerFactory() { + + @Override + protected void prepareContext(Host host, ServletContextInitializer[] initializers) { + super.prepareContext(host, initializers); + StandardContext child = new StandardContext(); + child.addLifecycleListener(new Tomcat.FixContextListener()); + child.setPath("/cloudfoundryapplication"); + ServletContainerInitializer initializer = getServletContextInitializer(getContextPath()); + child.addServletContainerInitializer(initializer, Collections.emptySet()); + child.setCrossContext(true); + host.addChild(child); + } + + }; + } + + private ServletContainerInitializer getServletContextInitializer(String contextPath) { + return (classes, context) -> { + Servlet servlet = new GenericServlet() { + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + ServletContext context = req.getServletContext().getContext(contextPath); + context.getRequestDispatcher("/cloudfoundryapplication").forward(req, res); + } + + }; + context.addServlet("cloudfoundry", servlet).addMapping("/*"); + }; + } + +} + +``` + + + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/actuator.html#actuator.whats-next)13\. ½ÓÏÂÀ´¶Áʲô + + + + + +Äã¿ÉÄÜÏë¶ÁһϠ[Graphite](https://graphiteapp.org/) µÈͼÐι¤¾ß¡£ + + + + + +·ñÔò£¬Äã¿ÉÒÔ¼ÌÐøÔĶÁ [¡°²¿ÊðÑ¡Ï](https://springdoc.cn/spring-boot/deployment.html#deployment) £¬»òÕßÌøµ½Ç°ÃæÈ¥Á˽âÓйØSpring Boot [¹¹½¨¹¤¾ß²å](https://springdoc.cn/spring-boot/build-tool-plugins.html#build-tool-plugins)¼þµÄһЩÉîÈëÐÅÏ¢¡£ + + + + + + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204Starter\346\234\272\345\210\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204Starter\346\234\272\345\210\266.md" new file mode 100644 index 0000000..0318193 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204Starter\346\234\272\345\210\266.md" @@ -0,0 +1,262 @@ +starterÊÇSpringBootÖеÄÒ»¸öз¢Ã÷£¬ËüÓÐЧµÄ½µµÍÁËÏîÄ¿¿ª·¢¹ý³ÌµÄ¸´Ôӳ̶ȣ¬¶ÔÓÚ¼ò»¯¿ª·¢²Ù×÷ÓÐ×ŷdz£ºÃµÄЧ¹û¡£±¾ÎÄ×ªÔØÁËһƬÎÄÕ£¬Ïêϸ½éÉÜÁËspring boot staterÊÇʲô£¿ËüµÄ×÷ÓÃÊÇʲô£¿ + +Spring Boot StarterÊÇÔÚSpringBoot×é¼þÖб»Ìá³öÀ´µÄÒ»ÖÖ¸ÅÄstackoverflowÉÏÃæÒѾ­ÓÐÈ˸ÅÀ¨ÁËÕâ¸östarterÊÇʲô¶«Î÷£¬Ïë¿´ÍêÕûµÄ»Ø´ð´Á[ÕâÀï](https://stackoverflow.com/a/28273660)£¨https://stackoverflow.com/questions/28273543/what-are-spring-boot-starter-jars/28273660#28273660£© + +![](https://images2018.cnblogs.com/blog/697611/201804/697611-20180409110042391-1447358002.png) + +´ó¸ÅÒâ˼¾ÍÊÇ˵starterÊÇÒ»ÖÖ¶ÔÒÀÀµµÄsynthesize£¨ºÏ³É£©£¬ÕâÊÇʲôÒâË¼ÄØ£¿ÎÒ¿ÉÒÔ¾Ù¸öÀý×ÓÀ´ËµÃ÷¡£ + +### ¡¡ ? ´«Í³µÄ×ö·¨ + +ÔÚûÓÐstarter֮ǰ£¬¼ÙÈçÎÒÏëÒªÔÚSpringÖÐʹÓÃjpa£¬ÄÇÎÒ¿ÉÄÜÐèÒª×öÒÔϲÙ×÷£º + +1. ÔÚMavenÖÐÒýÈëʹÓõÄÊý¾Ý¿âµÄÒÀÀµ£¨¼´JDBCµÄjar£© +2. ÒýÈëjpaµÄÒÀÀµ +3. ÔÚxxx.xmlÖÐÅäÖÃһЩÊôÐÔÐÅÏ¢ +4. ·´¸´µÄµ÷ÊÔÖ±µ½¿ÉÒÔÕý³£ÔËÐÐ + +ÐèҪעÒâµÄÊÇ£¬ÕâÀï²Ù×÷ÔÚÎÒÃÇ**_ÿ´Îн¨Ò»¸öÐèÒªÓõ½jpaµÄÏîÄ¿µÄʱºò¶¼ÐèÒªÖØ¸´µÄ×öÒ»´Î_**¡£Ò²ÐíÄãÔÚµÚÒ»´Î×Ô¼º½¨Á¢ÏîÄ¿µÄʱºòÊÇÔÚGoogleÉÏ×Ô¼ºËÑË÷ÁËÒ»·¬£¬»¨Á˰ëÌìʱ¼ä½â¾öµôÁ˸÷ÖÖÆæ¹ÖµÄÎÊÌâÖ®ºó£¬jpaÖÕÓÚÄÜÕý³£ÔËÐÐÁË¡£ÓÐЩÓо­ÑéµÄÈË»áÔÚOneNoteÉÏÃæ°ÑÕâ´Î½¨Á¢ÏîÄ¿µÄ¹ý³Ì¸ø¼Ç¼ÏÂÀ´£¬°üÀ¨²Ù×÷µÄ²½ÖèÒÔ¼°ÐèÒªÓõ½µÄÅäÖÃÎļþµÄÄÚÈÝ£¬ÔÚÏÂÒ»´ÎÔÙ´´½¨jpaÏîÄ¿µÄʱºò£¬¾Í²»ÐèÒªÔÙ´ÎÈ¥GoogleÁË£¬Ö»ÐèÒªÕÕ×űʼÇÀ´£¬Ö®ºóÔÙ°ÑËùÓеÄÅäÖÃÎļþcopy&paste¾Í¿ÉÒÔÁË¡£ + +ÏñÉÏÃæÕâÑùµÄ²Ù×÷Ò²²»Ëã²»ÐУ¬ÊÂʵÉÏÎÒÃÇÔÚûÓÐstarter֮ǰ¶¼ÊÇÕâô¸ÉµÄ£¬µ«ÊÇÕâÑù×öÓм¸¸öÎÊÌ⣺ + +1. Èç¹û¹ý³Ì±È½Ï·±Ëö£¬ÕâÑùÒ»²½²½²Ù×÷»áÔö¼Ó³ö´íµÄ¿ÉÄÜÐÔ +2. ²»Í£µØcopy&paste²»·ûºÏ[Don¡¯t repeat yourself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)¾«Éñ +3. ÔÚµÚÒ»´ÎÅäÖõÄʱºò£¨ÓÈÆäÈç¹û¿ª·¢Õ߱ȽÏС°×£©£¬ÐèÒª»¨·Ñµô´óÁ¿µÄʱ¼ä + +### ¡¡¡¡Ê¹ÓÃSpring Boot StarterÌáÉýЧÂÊ + +starterµÄÖ÷ҪĿµÄ¾ÍÊÇΪÁ˽â¾öÉÏÃæµÄÕâЩÎÊÌâ¡£ + +starterµÄÀíÄstarter»á°ÑËùÓÐÓõ½µÄÒÀÀµ¶¼¸ø°üº¬½øÀ´£¬±ÜÃâÁË¿ª·¢Õß×Ô¼ºÈ¥ÒýÈëÒÀÀµËù´øÀ´µÄÂé·³¡£ÐèҪעÒâµÄÊDz»Í¬µÄstarterÊÇΪÁ˽â¾ö²»Í¬µÄÒÀÀµ£¬ËùÒÔËüÃÇÄÚ²¿µÄʵÏÖ¿ÉÄÜ»áÓкܴóµÄ²îÒ죬ÀýÈçjpaµÄstarterºÍRedisµÄstarter¿ÉÄÜʵÏ־Ͳ»Ò»Ñù£¬ÕâÊÇÒòΪstarterµÄ±¾ÖÊÔÚÓÚsynthesize£¬ÕâÊÇÒ»²ãÔÚÂß¼­²ãÃæµÄ³éÏó£¬Ò²ÐíÕâÖÖÀíÄîÓеãÀàËÆÓÚDocker£¬ÒòΪËüÃǶ¼ÊÇÔÚ×öÒ»¸ö¡°°ü×°¡±µÄ²Ù×÷£¬Èç¹ûÄãÖªµÀDockerÊÇΪÁ˽â¾öʲôÎÊÌâµÄ£¬Ò²ÐíÄã¿ÉÒÔÓÃDockerºÍstarter×öÒ»¸öÀà±È¡£ + +starterµÄʵÏÖ£ºËäÈ»²»Í¬µÄstarterʵÏÖÆðÀ´¸÷ÓвîÒ죬µ«ÊÇËûÃÇ»ù±¾É϶¼»áʹÓõ½Á½¸öÏàͬµÄÄÚÈÝ£ºConfigurationPropertiesºÍAutoConfiguration¡£ÒòΪSpring Boot¼áÐÅ¡°Ô¼¶¨´óÓÚÅäÖá±ÕâÒ»ÀíÄËùÒÔÎÒÃÇʹÓÃConfigurationPropertiesÀ´±£´æÎÒÃǵÄÅäÖ㬲¢ÇÒÕâЩÅäÖö¼¿ÉÒÔÓÐÒ»¸öĬÈÏÖµ£¬¼´ÔÚÎÒÃÇûÓÐÖ÷¶¯¸²Ð´Ô­Ê¼ÅäÖõÄÇé¿öÏ£¬Ä¬ÈÏÖµ¾Í»áÉúЧ£¬ÕâÔںܶàÇé¿öÏÂÊǷdz£ÓÐÓõġ£³ý´ËÖ®Í⣬starterµÄConfigurationProperties»¹Ê¹µÃËùÓеÄÅäÖÃÊôÐÔ±»¾Û¼¯µ½Ò»¸öÎļþÖУ¨Ò»°ãÔÚresourcesĿ¼ÏµÄapplication.properties£©£¬ÕâÑùÎÒÃǾ͸æ±ðÁËSpringÏîÄ¿ÖÐXMLµØÓü¡£ + +starterµÄÕûÌåÂß¼­£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/697611-20180409110236645-2097376440.png) + +ÉÏÃæµÄstarterÒÀÀµµÄjarºÍÎÒÃÇ×Ô¼ºÊÖ¶¯ÅäÖõÄʱºòÒÀÀµµÄjar²¢Ã»ÓÐʲô²»Í¬£¬ËùÒÔÎÒÃÇ¿ÉÒÔÈÏΪstarterÆäʵÊǰÑÕâһЩ·±ËöµÄÅäÖòÙ×÷½»¸øÁË×Ô¼º£¬¶ø°Ñ¼òµ¥½»¸øÁËÓû§¡£³ýÁ˰ïÖúÓû§È¥³ýÁË·±ËöµÄ¹¹½¨²Ù×÷£¬ÔÚ¡°Ô¼¶¨´óÓÚÅäÖá±µÄÀíÄîÏ£¬ConfigurationProperties»¹°ïÖúÓû§¼õÉÙÁËÎÞνµÄÅäÖòÙ×÷¡£²¢ÇÒÒòΪ?`application.properties`?ÎļþµÄ´æÔÚ£¬¼´Ê¹ÐèÒª×Ô¶¨ÒåÅäÖã¬ËùÓеÄÅäÖÃÒ²Ö»ÐèÒªÔÚÒ»¸öÎļþÖнøÐУ¬Ê¹ÓÃÆðÀ´·Ç³£·½±ã¡£ + +Á˽âÁËstarterÆäʵ¾ÍÊǰïÖúÓû§¼ò»¯ÁËÅäÖõIJÙ×÷Ö®ºó£¬ÒªÀí½âstarterºÍ±»ÅäÖÃÁËstarterµÄ×é¼þÖ®¼ä²¢²»ÊǾºÕù¹ØÏµ£¬¶øÊǸ¨Öú¹ØÏµ£¬¼´ÎÒÃÇ¿ÉÒÔ¸øÒ»¸ö×é¼þ´´½¨Ò»¸östarterÀ´ÈÃ×îÖÕÓû§ÔÚʹÓÃÕâ¸ö×é¼þµÄʱºò¸ü¼ÓµÄ¼òµ¥·½±ã¡£»ùÓÚÕâÖÖÀíÄÎÒÃÇ¿ÉÒÔ¸øÈÎÒâÒ»¸öÏÖÓеÄ×é¼þ´´½¨Ò»¸östarterÀ´ÈñðÈËÔÚʹÓÃÕâ¸ö×é¼þµÄʱºò¸ü¼ÓµÄ¼òµ¥·½±ã£¬ÊÂʵÉÏSpring BootÍŶÓÒѾ­°ïÖúÏÖÓд󲿷ֵÄÁ÷ÐеÄ×é¼þ´´½¨ºÃÁËËüÃǵÄstarter£¬Äã¿ÉÒÔÔÚ[ÕâÀï](https://github.com/spring-projects/spring-boot/tree/v1.5.7.RELEASE/spring-boot-starters)²é¿´ÕâЩstarterµÄÁÐ±í¡£ + +ÓÃÁËspringboot ÄÇô¾ÃÁ˾ÓÈ»¶¼»¹Ã»×Ô¶¨Òå¹ýstarter£¬ÏëÏë¶¼¾õµÃÐßÀ¢£¬ËùÒÔ½ñÌìÀ´ÍæÒ»Ï¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416200541.png) + +# SpringBoot starter + +SpringBootÖеÄstarterÊÇÒ»Öַdz£ÖØÒªµÄ»úÖÆ£¬Äܹ»ÅׯúÒÔǰ·±ÔÓµÄÅäÖ㬽«Æäͳһ¼¯³É½østarter£¬Ó¦ÓÃÕßÖ»ÐèÒªÔÚmavenÖÐÒýÈëstarterÒÀÀµ£¬SpringBoot¾ÍÄÜ×Ô¶¯É¨Ãèµ½Òª¼ÓÔØµÄÐÅÏ¢²¢Æô¶¯ÏàÓ¦µÄĬÈÏÅäÖá£starterÈÃÎÒÃǰÚÍÑÁ˸÷ÖÖÒÀÀµ¿âµÄ´¦Àí£¬ÐèÒªÅäÖø÷ÖÖÐÅÏ¢µÄÀ§ÈÅ¡£SpringBoot»á×Ô¶¯Í¨¹ýclasspath·¾¶ÏµÄÀà·¢ÏÖÐèÒªµÄBean£¬²¢×¢²á½øIOCÈÝÆ÷¡£SpringBootÌṩÁËÕë¶ÔÈÕ³£ÆóÒµÓ¦ÓÃÑз¢¸÷ÖÖ³¡¾°µÄspring-boot-starterÒÀÀµÄ£¿é¡£ËùÓÐÕâЩÒÀÀµÄ£¿é¶¼×ñÑ­×ÅÔ¼¶¨³ÉË×µÄĬÈÏÅäÖ㬲¢ÔÊÐíÎÒÃǵ÷ÕûÕâЩÅäÖ㬼´×ñÑ­¡°Ô¼¶¨´óÓÚÅäÖá±µÄÀíÄî¡£ + +# ×Ô¶¨Òåstarter + +ÈÕ³£¹¤×÷ÖÐÓÐʱÓÐһЩ¶ÀÁ¢ÓÚÒµÎñÖ®ÍâµÄ¹¦ÄÜ»òÄ£¿é£¬¿ÉÄÜÕâ¸öÏîÄ¿ÔÚÓã¬ÁíÒ»¸öÏîĿҲҪÓã¬Èç¹ûÿ´Î¶¼ÖØÐ¼¯³ÉµÄ»°¾Í»áºÜÂé·³£¬ÕâʱÎÒÃÇÖ»Òª°ÑÕâЩ¹¦ÄÜ»òÄ£¿é·â×°³ÉÒ»¸ö¸östarterµÄ»°£¬ÔÚʹÓõÄʱºòÒýÈë½øÈ¥¾ÍºÜ·½±ãÁË¡£ + +## ×Ô¶¨Òåstarter²½Öè + +Æäʵ×Ô¶¨ÒåstarterºÜ¼òµ¥£¬´óÖÂÐèÒªÒÔÏÂ5²½£º + +1. н¨Á½¸öÄ£¿é£¬ÃüÃû¹æ·¶£º springboot×Ô´øµÄstarterÃüÃû¹æ·¶Îªspring-boot-starter-xxx£¬ ×Ô¶¨ÒåµÄstarterÃüÃû¹æ·¶Îªxxx-spring-boot-starter + +¡ñ xxx-spring-boot-autoconfigure£º×Ô¶¯ÅäÖúËÐÄ´úÂë +¡ñ xxx-spring-boot-starter£º¹ÜÀíÒÀÀµ +Èç¹û²»ÐèÒª½«×Ô¶¯ÅäÖôúÂëºÍÒÀÀµÏî¹ÜÀí·ÖÀ뿪À´£¬Ôò¿ÉÒÔ½«ËüÃÇ×éºÏµ½Ò»¸öÄ£¿éÖС£Ö»²»¹ýspringboot¹Ù·½½¨Ò齫Á½¸öÄ£¿é·Ö¿ª¡£ +2\. ÒýÈëspring-boot-autoconfigureÒÀÀµ +3\. ´´½¨×Ô¶¨ÒåµÄXXXProperties Àà: Õâ¸öÀàµÄÊôÐÔ¸ù¾ÝÐèÒªÊÇÒª³öÏÖÔÚÅäÖÃÎļþÖеġ£ +4\. ´´½¨×Ô¶¨ÒåµÄXXXAutoConfigurationÀࣺÕâ¸öÀàÒªÅäÖÃ×Ô¶¯ÅäÖÃʱµÄһЩÂß¼­£¬Í¬Ê±Ò²ÒªÈÃXXXProperties ÀàÉúЧ¡£ +5\. ´´½¨×Ô¶¨ÒåµÄspring.factoriesÎļþ£ºÔÚresources/META-INF´´½¨Ò»¸öspring.factoriesÎļþºÍspring-configuration-metadata.json£¬spring-configuration-metadata.jsonÎļþÊÇÓÃÓÚÔÚÌîдÅäÖÃÎļþʱµÄÖÇÄÜÌáʾ£¬¿ÉÒª¿É²»Òª£¬ÓеϰÌáʾÆðÀ´¸üÓѺá£spring.factoriesÓÃÓÚµ¼Èë×Ô¶¯ÅäÖÃÀ࣬±ØÐëÒªÓÐ + +## ʵÏÖ + +ÎÒÕâÀïΪÁË·½±ã¾ÍÖ»´´½¨Ò»¸öÄ£¿éÁË£¬ + +1. ´´½¨Ò»¸öÄ£¿é£¬ÃüÃûΪspring-boot-starter-my-starter£¬¶ÔÓ¦pomÎļþ + +``` + com.example + spring-boot-starter-my-starter + 1.0 + my-starter +¸´ÖÆ´úÂë +``` + +1. ÒýÈëspring-boot-autoconfigureÒÀÀµ ÎÒÕâÀïʹÓõÄspring-boot-autoconfigure°æ±¾ÊÇ2.6.2 + +``` + + + org.springframework.boot + spring-boot-autoconfigure + 2.6.2 + + +¸´ÖÆ´úÂë +``` + +1. ´´½¨×Ô¶¨ÒåµÄXXXProperties Àà + +``` +@ConfigurationProperties(prefix = "com.arron") +public class MyStarterProperties { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +¸´ÖÆ´úÂë +``` + +ÔÙ´´½¨Ò»¸öMyStarterConfigÓÃÓÚ¶ÁÈ¡MyStarterProperties ÀïµÄÊôÐÔ + +``` +public class MyStarterConfig { + + private MyStarterProperties myStarterProperties; + + private String name; + + public MyStarterConfig(MyStarterProperties myStarterProperties) { + this.myStarterProperties = myStarterProperties; + } + + public String getName() { + return myStarterProperties.getName(); + } + + public void setName(String name) { + this.name = name; + } +} +¸´ÖÆ´úÂë +``` + +1. ´´½¨×Ô¶¨ÒåµÄXXXAutoConfigurationÀà + +``` +@Configuration +// EnableConfigurationProperties valueÊý×éÖеÄÅäÖÃÀàÆð×÷Óà +@EnableConfigurationProperties(value = {MyStarterProperties.class}) +public class MyStarterAutoConfiguration { + + @Autowired + private MyStarterProperties myStarterProperties; + + @Bean + @ConditionalOnMissingBean(MyStarterConfig.class) + public MyStarterConfig myStarterConfig(){ + return new MyStarterConfig(myStarterProperties); + } + +} +¸´ÖÆ´úÂë +``` + +1. ÔÚresources/META-INF´´½¨Ò»¸öspring.factoriesÎļþ + +spring.factories + +``` +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.myStarter.MyStarterAutoConfiguration +¸´ÖÆ´úÂë +``` + +spring-configuration-metadata.json + +``` +{ + "group": [ + { + "name": "com.arron", + "type": "com.example.myStarter.MyStarterProperties", + "sourceType": "com.example.myStarter.MyStarterProperties" + } + ], + "properties": [ + { + "name": "com.arron.name", + "type": "java.lang.String", + "description": "my start name", + "sourceType": "com.example.myStarter.MyStarterProperties", + "defaultValue": "MyStarterProperties name" + } + ] +} +¸´ÖÆ´úÂë +``` + +## ´ò°ü²âÊÔ + +ÕÒµ½Èçͼmaven£¬µã»÷install£¬°²×°µ½±¾µØ ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230416200527082.png) + +È»ºóн¨Ò»¸öÏîÄ¿µ¼°ü½øÐвâÊÔ£¬´´½¨ÏîÄ¿¹ý³Ì¾Í²»½éÉÜÁË¡£ + +1. ÒýÈëÒÀÀµ + +``` + + com.example + spring-boot-starter-my-starter + 1.0 + +¸´ÖÆ´úÂë +``` + +1. ÅäÖÃÎļþÌí¼ÓÊôÐÔ£º + +``` +com: + arron: + name: myname +¸´ÖÆ´úÂë +``` + +1. µ¥Ôª²âÊÔ£º + +``` +@RunWith(SpringRunner.class) +@SpringBootTest +class RabbitmqApplicationTests { + @Autowired + private MyStarterConfig myStarterConfig; + + @Test + public void testMyStarter(){ + String name = myStarterConfig.getName(); + System.out.println(name); + } +} +¸´ÖÆ´úÂë +``` + +¿ØÖÆÌ¨Êä³ö£º + +``` +myname +¸´ÖÆ´úÂë +``` + +ÖÁ´Ë£¬Ò»¸ö¼òµ¥×Ô¶¨ÒåµÄspringboot starter¾ÍÍê³ÉÁË¡£ + +# ×¢½â½âÊÍ + +ÏÂÃæÕâЩע½âÔÚ×Ô¶¨ÒåstarterÊÇ¿ÉÄÜ»áÓõ½¡£ + +* @Conditional£º°´ÕÕÒ»¶¨µÄÌõ¼þ½øÐÐÅжϣ¬Âú×ãÌõ¼þ¸øÈÝÆ÷×¢²ábean +* @ConditionalOnMissingBean£º¸ø¶¨µÄÔÚbean²»´æÔÚʱ,ÔòʵÀý»¯µ±Ç°Bean +* @ConditionalOnProperty£ºÅäÖÃÎļþÖÐÂú×㶨ÒåµÄÊôÐÔÔò´´½¨bean£¬·ñÔò²»´´½¨ +* @ConditionalOnBean£º¸ø¶¨µÄÔÚbean´æÔÚʱ,ÔòʵÀý»¯µ±Ç°Bean +* @ConditionalOnClass£º µ±¸ø¶¨µÄÀàÃûÔÚÀà·¾¶ÉÏ´æÔÚ£¬ÔòʵÀý»¯µ±Ç°Bean +* @ConditionalOnMissingClass £ºµ±¸ø¶¨µÄÀàÃûÔÚÀà·¾¶Éϲ»´æÔÚ£¬ÔòʵÀý»¯µ±Ç°Bean + + + +×÷ÕߣºË÷ÂëÀí +Á´½Ó£ºhttps://juejin.cn/post/7127468724046528525 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" new file mode 100644 index 0000000..c524bd5 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" @@ -0,0 +1,246 @@ +# SpringBootµÄǰÊÀ½ñÉú + +Spring Boot 2.0 µÄÍÆ³öÓÖ¼¤ÆðÁËÒ»Õóѧϰ Spring Boot ÈÈ£¬¾Íµ¥´ÓÎÒ¸öÈ˵IJ©¿ÍµÄ·ÃÎÊÁ¿´ó·ùÔö¼Ó¾Í¿ÉÒÔ¸ÐÊܵ½´ó¼Ò¶Ôѧϰ Spring Boot µÄÈÈÇ飬ÄÇôÔÚÕâô¶àÈËÈÈÖÔÓÚѧϰ Spring Boot ֮ʱ£¬ÎÒ×Ô¼ºÒ²ÔÚ˼¿¼£º Spring Boot µ®ÉúµÄ±³¾°ÊÇʲô£¿Spring ÆóÒµÓÖÊÇ»ùÓÚʲôÑùµÄ¿¼ÂÇ´´½¨ Spring Boot? ´«Í³ÆóҵʹÓà Spring Boot »á¸øÎÒÃÇ´øÀ´Ê²Ã´Ñù±ä¸ï? + +´ø×ÅÕâЩÎÊÌ⣬ÎÒÃÇÒ»ÆðÀ´Á˽âÏ Spring Boot µ½µ×ÊÇʲô? + +## Spring ÀúÊ· + +˵Æð Spring Boot ÎÒÃDz»µÃ²»ÏÈÁ˽âһϠSpring Õâ¸öÆóÒµ£¬²»½öÒòΪ Spring Boot À´Ô´ÓÚ Spirng ´ó¼Ò×壬¶øÇÒ Spring Boot µÄµ®ÉúºÍ Sping ¿ò¼ÜµÄ·¢Õ¹Ï¢Ï¢Ïà¹Ø¡£ + +ʱ¼ä»Øµ½2002Ä꣬µ±Ê±ÕýÊÇ Java EE ºÍ EJB ´óÐÐÆäµÀµÄʱºò£¬ºÜ¶àÖªÃû¹«Ë¾¶¼ÊDzÉÓô˼¼Êõ·½°¸½øÐÐÏîÄ¿¿ª·¢¡£ÕâʱºòÓÐÒ»¸öÃÀ¹úµÄС»ï×ÓÈÏΪ EJB Ì«¹ýÓ·Ö×£¬²¢²»ÊÇËùÓеÄÏîÄ¿¶¼ÐèҪʹÓà EJB ÕâÖÖ´óÐÍ¿ò¼Ü£¬Ó¦¸Ã»áÓÐÒ»ÖÖ¸üºÃµÄ·½°¸À´½â¾öÕâ¸öÎÊÌâ¡£ + +ΪÁËÖ¤Ã÷ËûµÄÏë·¨ÊÇÕýÈ·µÄ£¬ÓÚ2002Äê10ÔÂÉõÖÁдÁËÒ»±¾Êé¡¶ Expert One-on-One J2EE ¡·£¬½éÉÜÁ˵±Ê± Java ÆóÒµÓ¦ÓóÌÐò¿ª·¢µÄÇé¿ö£¬²¢Ö¸³öÁË Java EE ºÍ EJB ×é¼þ¿ò¼ÜÖдæÔÚµÄһЩÖ÷ҪȱÏÝ¡£ÔÚÕâ±¾ÊéÖУ¬ËûÌá³öÁËÒ»¸ö»ùÓÚÆÕͨ Java ÀàºÍÒÀÀµ×¢ÈëµÄ¸ü¼òµ¥µÄ½â¾ö·½°¸¡£ + +ÔÚÊéÖУ¬ËûչʾÁËÈçºÎÔÚ²»Ê¹Óà EJB µÄÇé¿öϹ¹½¨¸ßÖÊÁ¿£¬¿ÉÀ©Õ¹µÄÔÚÏß×ùλԤÁôϵͳ¡£ÎªÁ˹¹½¨Ó¦ÓóÌÐò£¬Ëû±àдÁ˳¬¹ý 30,000 ÐеĻù´¡½á¹¹´úÂ룬ÏîÄ¿Öеĸù°üÃüÃûΪ com.interface21£¬ËùÒÔÈËÃÇ×î³õ³ÆÕâÌ׿ªÔ´¿ò¼ÜΪ interface21£¬Ò²¾ÍÊÇ Spring µÄǰÉí¡£ + +ËûÊÇË­ÄØ£¬Ëû¾ÍÊÇ´óÃû¶¦¶¦µÄ Rod Johnson £¨ÏÂͼ£©, Rod Johnson ÔÚϤÄá´óѧ²»½ö»ñµÃÁ˼ÆËã»úѧλ£¬Í¬Ê±»¹»ñµÃÁËÒôÀÖѧ룬¸üÁîÈ˳ԾªµÄÊÇÔڻص½Èí¼þ¿ª·¢ÁìÓò֮ǰ£¬Ëû»¹»ñµÃÁËÒôÀÖѧµÄ²©Ê¿Ñ§Î»¡£ÏÖÔÚ Rod Johnson ÒѾ­À뿪ÁË Spring £¬³ÉΪÁËÒ»¸öÌìʹͶ×ÊÈË£¬Í¬Ê±Ò²ÊǶà¸ö¹«Ë¾µÄ¶­Ê£¬ÔçÒÑ×ßÉÏÈËÉúáÛ·å¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springboot-hot.png) + +ÔÚÕâ±¾Êé·¢²¼ºó£¬Ò»¶ÔÒ»µÄ J2EE Éè¼ÆºÍ¿ª·¢Ò»ÅÚ¶øºì¡£Õâ±¾ÊéÃâ·ÑÌṩµÄ´ó²¿·Ö»ù´¡¼Ü¹¹´úÂë¶¼ÊǸ߶ȿÉÖØÓõġ£ 2003 Äê Rod Johnson ºÍͬ°éÔÚ´Ë¿ò¼ÜµÄ»ù´¡ÉÏ¿ª·¢ÁËÒ»¸öȫеĿò¼ÜÃüÃûΪ Spring ,¾Ý Rod Johnson ½éÉÜ Spring ÊÇ´«Í³ J2EE еĿªÊ¼¡£Ëæºó Spring ·¢Õ¹½øÈë¿ì³µµÀ¡£ + +* 2004 Äê 03 Ô£¬1.0 °æ·¢²¼¡£ +* 2006 Äê 10 Ô£¬2.0 °æ·¢²¼¡£ +* 2007 Äê 11 Ô¸üÃûΪ SpringSource£¬Í¬Ê±·¢²¼ÁË Spring 2.5¡£ +* 2009 Äê 12 Ô£¬Spring 3.0 ·¢²¼¡£ +* 2013 Äê 12 Ô£¬Pivotal Ðû²¼·¢²¼ Spring ¿ò¼Ü 4.0¡£ +* 2017 Äê 09 Ô£¬Spring 5.0 ·¢²¼¡£ + +## Spring Boot µÄµ®Éú + +Ëæ×ÅʹÓà Spring ½øÐпª·¢µÄ¸öÈËºÍÆóÒµÔ½À´Ô½¶à£¬Spring Ò²ÂýÂý´ÓÒ»¸öµ¥Ò»¼ò½àµÄС¿ò¼Ü±ä³ÉÒ»¸ö´ó¶øÈ«µÄ¿ªÔ´Èí¼þ£¬Spring µÄ±ß½ç²»¶ÏµÄ½øÐÐÀ©³ä£¬µ½Á˺óÀ´ Spring ¼¸ºõ¿ÉÒÔ×öÈκÎÊÂÇéÁË£¬ÊÐÃæÉÏÖ÷Á÷µÄ¿ªÔ´Èí¼þ¡¢Öмä¼þ¶¼ÓÐ Spring ¶ÔÓ¦×é¼þÖ§³Ö£¬ÈËÃÇÔÚÏíÓà Spring µÄÕâÖÖ±ãÀûÖ®ºó£¬Ò²Óöµ½ÁËһЩÎÊÌâ¡£ + +Spring ÿ¼¯³ÉÒ»¸ö¿ªÔ´Èí¼þ£¬¾ÍÐèÒªÔö¼ÓһЩ»ù´¡ÅäÖã¬ÂýÂýµÄËæ×ÅÈËÃÇ¿ª·¢µÄÏîĿԽÀ´Ô½ÅÓ´ó£¬ÍùÍùÐèÒª¼¯³ÉºÜ¶à¿ªÔ´Èí¼þ£¬Òò´ËºóÆÚʹÓà Spirng ¿ª·¢´óÐÍÏîÄ¿ÐèÒªÒýÈëºÜ¶àÅäÖÃÎļþ£¬Ì«¶àµÄÅäÖ÷dz£ÄÑÒÔÀí½â£¬²¢ÈÝÒ×ÅäÖóö´í£¬µ½Á˺óÀ´ÈËÃÇÉõÖÁ³Æ Spring ΪÅäÖõØÓü¡£ + +Spring ËÆºõÒ²Òâʶµ½ÁËÕâЩÎÊÌ⣬¼±ÐèÓÐÕâôһÌ×Èí¼þ¿ÉÒÔ½â¾öÕâЩÎÊÌ⣬Õâ¸öʱºò΢·þÎñµÄ¸ÅÄîÒ²ÂýÂýÐËÆð£¬¿ìËÙ¿ª·¢Î¢Ð¡¶ÀÁ¢µÄÓ¦ÓñäµÃ¸üΪ¼±ÆÈ£¬Spring ¸ÕºÃ´¦ÔÚÕâôһ¸ö½»²æµãÉÏ£¬ÓÚ 2013 Äê³õ¿ªÊ¼µÄ Spring Boot ÏîÄ¿µÄÑз¢£¬2014Äê4Ô£¬Spring Boot 1.0.0 ·¢²¼¡£ + +Spring Boot µ®ÉúÖ®³õ£¬¾ÍÊܵ½¿ªÔ´ÉçÇøµÄ³ÖÐø¹Ø×¢£¬Â½ÐøÓÐһЩ¸öÈËºÍÆóÒµ³¢ÊÔ×ÅʹÓÃÁË Spring Boot£¬²¢Ñ¸ËÙϲ»¶ÉÏÁËÕâ¿î¿ªÔ´Èí¼þ¡£Ö±µ½2016Ä꣬ÔÚ¹úÄÚ Spring Boot ²Å±»ÕýÕæÊ¹ÓÃÁËÆðÀ´£¬ÆÚ¼äºÜ¶àÑо¿ Spring Boot µÄ¿ª·¢ÕßÔÚÍøÉÏдÁË´óÁ¿¹ØÓÚ Spring Boot µÄÎÄÕ£¬Í¬Ê±ÓÐһЩ¹«Ë¾ÔÚÆóÒµÄÚ²¿½øÐÐÁËС¹æÄ£µÄʹÓ㬲¢½«Ê¹Óþ­Ñé·ÖÏíÁ˳öÀ´¡£´Ó2016Äêµ½2018Ä꣬ʹÓà Spring Boot µÄÆóÒµºÍ¸öÈË¿ª·¢ÕßÔ½À´Ô½¶à£¬ÎÒÃÇ´Ó Spring Boot ¹Ø¼ü×ֵİٶÈÖ¸Êý¾Í¿ÉÒÔ¿´³ö¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springboot-hot.png) + +ÉÏͼΪ2014Äêµ½2018Äê Spring Boot µÄ°Ù¶ÈÖ¸Êý£¬¿ÉÒÔ¿´³ö Spring Boot 2.0 µÄÍÆ³öÒý·¢ÁËËÑË÷¸ß·å¡£ + +µ±È» Spring Boot ²»ÊÇΪÁËÈ¡´ú Spring ,Spring Boot »ùÓÚ Spring ¿ª·¢£¬ÊÇΪÁËÈÃÈËÃǸüÈÝÒ×µÄʹÓà Spring¡£¿´µ½ Spring Boot µÄÊг¡·´Ó¦£¬Spring ¹Ù·½Ò²·Ç³£ÖØÊÓ Spring Boot µÄºóÐø·¢Õ¹£¬ÒѾ­½« Spring Boot ×÷Ϊ¹«Ë¾×î¶¥¼¶µÄÏîÄ¿À´Íƹ㣬·Åµ½Á˹ÙÍøÉϵÚÒ»µÄλÖã¬Òò´ËºóÐø Spring Boot µÄ³ÖÐø·¢Õ¹Ò²±»¿´ºÃ¡£ + +# »ù±¾½éÉÜ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230415111020.png) + +ÕâÊÇspring¹ÙÍø¶ÔÓÚspringbootµÄ»ù±¾½éÉÜ + +Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". + +We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration. + +¼ò¶øÑÔÖ®£¬¾ÍÊÇÔÚ¸æËßÎÒÃÇ£¬springboot¿ÉÒÔ°ïÖúÎÒÃÇ¿ìËÙ¹¹½¨¶ÀÁ¢²¿ÊðµÄÉú²ú¼¶Ó¦Óà + +£¬Ö»ÐèÒªÒ»¸ö´øÓÐ@SpringBootApplication ×¢½âµÄÓ¦ÓÃÈë¿Ú£¬¼´¿É±»Ê¶±ðΪһ¸öspringbootÓ¦Óà + +²¢ÇÒ£¬springboot¼Ì³ÐÁË´óÁ¿µÄµÚÈý·½¿â£¬ÎÒÃǽöÐèºÜÉÙÉõÖÁÊÇÍêÈ«²»ÐèÒª¶îÍâµÄÅäÖ㬾ͿÉÒÔ½øÐÐÓ¦Óô£¬ÒòΪspringboot¿ò¼Ü±¾ÉíÒѾ­°ïÎÒÃÇÉèÖúÃÁË´óÁ¿µÄĬÈÏÅäÖᣠ+ +# »ù±¾ÌØÐÔ + +Features + +* Create stand-alone Spring applications +* Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files) +* Provide opinionated 'starter' dependencies to simplify your build configuration +* Automatically configure Spring and 3rd party libraries whenever possible +* Provide production-ready features such as metrics, health checks, and externalized configuration +* Absolutely no code generation and no requirement for XML configuration + +¹Ù·½¶ÔÓÚspringboot¼¸´óÌØÐÔµÄÃèÊöÈçÉÏ + +1¡¢´´½¨Ò»¸ö¶ÀÁ¢²¿ÊðµÄspringÓ¦Óã¬Ëü³£ÓÃÓÚ¶¨Òå΢·þÎñ£¬»òÕßÊÇÒ»¸öwebÓ¦Óà + +2¡¢ÄÚÖõÄtomcatÈÝÆ÷£¬ÎÒÃÇÔÙÒ²ÐèÒª°ÑspringÓ¦Óôò°ü³Éwar°ü£¬¶øÊÇÖ»ÐèÒª½«Æä´ò°ü³Éjar°ü½øÐв¿Êð¼´¿É¡£ + +3¡¢ÌṩstarterÒÀÀµ£¬¸ü¼Ó¼ò»¯ÁËspringbootÉú̬ÖеÄÒÀÀµºÍÅäÖã¬ÀýÈçµ±ÎÒÃÇʹÓÃspringwebÏà¹ØÒÀÀµÊ±£¬ÎÒÃDz»ÐèÒªµ¥¶ÀµØÌí¼Ó¶à¸öspringmvcÏà¹ØµÄmavenÒÀÀµ£¬¶øÊÇÖ±½ÓÒýÓÃspring-boot-starter-web¾Í¿ÉÒÔÁË£¬Õâ¸östarter»á×Ô¶¯ÒýÈëÏà¹ØµÄÒÀÀµ£¬²¢ÇÒ°ïÄã¹ÜÀí°æ±¾ºÅ£¬»á´óÁ¿µØ¼ò»¯ºÍÓÅ»¯ÄãµÄmavenÅäÖà + +4¡¢×Ô¶¯×°ÅäspringºÍµÚÈý·½¿â£¬ÕâÀïÖ÷ÒªÊÇָͨ¹ý×¢½âºÍÅäÖÃÎļþ½øÐÐ×Ô¶¯×°Å䣬²¢ÇÒ»¹ÓÐһЩÊÇ»ùÓÚÔ¼¶¨µÄ×Ô¶¯×°Åä»úÖÆ£¬Õâ¿ÉÒÔ°ïÎÒÃǽÚÊ¡´óÁ¿µÄ¶îÍâÅäÖã¬Ò²¿ÉÒÔ×Ô¶¯×°ÅäÍⲿjar°üÌṩµÄspringÏà¹ØbeanºÍÅäÖᣠ+ +5¡¢ÌṩÁËÉú²ú»·¾³Ïà¹ØµÄÌØÐÔ£¬±ÈÈç¼à¿Ø¡¢Ö¸±ê´òµã¡¢½¡¿µ¼ì²éµÈ¹¦ÄÜ£¬springbootÌṩÁËÇ¿´óµÄÉú̬×é¼þ£¬ÕâÀï²»½ö°üÀ¨springboot×Ô¼ºµÄ×é¼þ£¬Ò²°üÀ¨Ò»Ð©ÍⲿµÄÉú̬×é¼þ¡£ + +6¡¢²»²»´æÔÚ´úÂëÉú³É£¬Ò²²»ÐèÒªxmlÅäÖÃÎļþ£¬ÕâÖ¤Ã÷ÁËspringbootµÄ¿ò¼ÜÍêÕû¶È£¬ÒÔ¼°¶ÔÓÚÅäÖüò»¯µÄ¼«ÖÂ×·Çó£¬ÎÒÃǽöÐèÒªapplication.properties½øÐлù±¾ÅäÖòÎÊý¹ÜÀí£¬ÄãÒ²²»»áÔÚspringboot±àÒëÆ÷ÕÒµ½¶¯Ì¬Éú³ÉµÄ¶àÓàclassÎļþ¡£ + +# ºÍSpringµÄ¹ØÏµ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230415111139.png) + +## SpringFramework½â¾öÁËʲôÎÊÌ⣿ + +SpringÊÇJavaÆóÒµ°æ£¨Java Enterprise Edition£¬JEE£¬Ò²³ÆJ2EE£©µÄÇáÁ¿¼¶´úÌæÆ·¡£ÎÞÐ迪·¢ÖØÁ¿¼¶µÄEnterpriseJavaBean£¨EJB£©£¬SpringΪÆóÒµ¼¶Java¿ª·¢ÌṩÁËÒ»ÖÖÏà¶Ô¼òµ¥µÄ·½·¨£¬Í¨¹ýÒÀÀµ×¢ÈëºÍÃæÏòÇÐÃæ±à³Ì£¬Óüòµ¥µÄJava¶ÔÏó£¨Plain Old Java Object£¬POJO£©ÊµÏÖÁËEJBµÄ¹¦ÄÜ¡£ + +1.ʹÓÃSpringµÄIOCÈÝÆ÷,½«¶ÔÏóÖ®¼äµÄÒÀÀµ¹ØÏµ½»¸øSpring,½µµÍ×é¼þÖ®¼äµÄñîºÏÐÔ,ÈÃÎÒÃǸüרעÓÚÓ¦ÓÃÂß¼­ + +2.¿ÉÒÔÌṩÖÚ¶à·þÎñ,ÊÂÎñ¹ÜÀí,WSµÈ¡£ + +3.ºÜºÃµØÖ§³ÖAOP,·½±ãÃæÏòÇÐÃæ±à³Ì¡£ + +4.¶ÔÖ÷Á÷µÄ¿ò¼ÜÌṩÁ˺ܺõÉÖ§³Ö,ÈçHibernate,Struts2,JPAµÈ + +5.Spring DI»úÖÆ½µµÍÁËÒµÎñ¶ÔÏóÌæ»»µÄ¸´ÔÓÐÔ¡£ + +6.SpringÊôÓÚµÍÇÖÈë,´úÂëÎÛȾ¼«µÍ¡£ + +7.SpringµÄ¸ß¶È¿É¿ª·ÅÐÔ,²¢²»Ç¿ÖÆÒÀÀµÓÚSpring,¿ª·¢Õß¿ÉÒÔ×ÔÓÉÑ¡ÔñSpring²¿·Ö»òÈ«²¿ + +## SpringFrameworkûÓнâ¾öÁËʲôÎÊÌ⣿ + +ËäÈ»SpringµÄ×é¼þ´úÂëÊÇÇáÁ¿¼¶µÄ£¬µ«ËüµÄÅäÖÃÈ´ÊÇÖØÁ¿¼¶µÄ¡£Ò»¿ªÊ¼£¬SpringÓÃXMLÅäÖ㬶øÇÒÊǺܶàXMLÅäÖá£Spring 2.5ÒýÈëÁË»ùÓÚ×¢½âµÄ×é¼þɨÃ裬ÕâÏû³ýÁË´óÁ¿Õë¶ÔÓ¦ÓóÌÐò×ÔÉí×é¼þµÄÏÔʽXMLÅäÖá£Spring 3.0ÒýÈëÁË»ùÓÚJavaµÄÅäÖã¬ÕâÊÇÒ»ÖÖÀàÐͰ²È«µÄ¿ÉÖØ¹¹ÅäÖ÷½Ê½£¬¿ÉÒÔ´úÌæXML¡£ + +ËùÓÐÕâЩÅäÖö¼´ú±íÁË¿ª·¢Ê±µÄËðºÄ¡£ÒòΪÔÚ˼¿¼SpringÌØÐÔÅäÖúͽâ¾öÒµÎñÎÊÌâÖ®¼äÐèÒª½øÐÐ˼άÇл»£¬ËùÒÔ±àдÅäÖü·Õ¼Á˱àдӦÓóÌÐòÂß¼­µÄʱ¼ä¡£ºÍËùÓпò¼ÜÒ»Ñù£¬SpringʵÓ㬵«Óë´ËͬʱËüÒªÇóµÄ»Ø±¨Ò²²»ÉÙ¡£ + +³ý´ËÖ®Í⣬ÏîÄ¿µÄÒÀÀµ¹ÜÀíÒ²ÊÇÒ»¼þºÄʱºÄÁ¦µÄÊÂÇé¡£ÔÚ»·¾³´î½¨Ê±£¬ÐèÒª·ÖÎöÒªµ¼ÈëÄÄЩ¿âµÄ×ø±ê£¬¶øÇÒ»¹ÐèÒª·ÖÎöµ¼ÈëÓëÖ®ÓÐÒÀÀµ¹ØÏµµÄÆäËû¿âµÄ×ø±ê£¬Ò»µ©Ñ¡´íÁËÒÀÀµµÄ°æ±¾£¬ËæÖ®¶øÀ´µÄ²»¼æÈÝÎÊÌâ¾Í»áÑÏÖØ×è°­ÏîÄ¿µÄ¿ª·¢½ø¶È¡£ + +## SpringBoot½â¾öÉÏÊöSpringµÄȱµã + +SpringBoot¶ÔÉÏÊöSpringµÄȱµã½øÐеĸÄÉÆºÍÓÅ»¯£¬»ùÓÚÔ¼¶¨ÓÅÓÚÅäÖõÄ˼Ï룬¿ÉÒÔÈÿª·¢ÈËÔ±²»±ØÔÚÅäÖÃÓëÂß¼­ÒµÎñÖ®¼ä½øÐÐ˼άµÄÇл»£¬È«ÉíÐĵÄͶÈëµ½Âß¼­ÒµÎñµÄ´úÂë±àдÖУ¬´Ó¶ø´ó´óÌá¸ßÁË¿ª·¢µÄЧÂÊ£¬Ò»¶¨³Ì¶ÈÉÏËõ¶ÌÁËÏîÄ¿ÖÜÆÚ¡£ + +ÔÚʹÓÃSpring¿ò¼Ü½øÐпª·¢µÄ¹ý³ÌÖУ¬ÐèÒªÅäÖúܶàSpring¿ò¼Ü°üµÄÒÀÀµ£¬Èçspring-core¡¢spring-bean¡¢spring-contextµÈ£¬¶øÕâЩÅäÖÃͨ³£¶¼ÊÇÖØ¸´Ìí¼ÓµÄ£¬¶øÇÒÐèÒª×öºÜ¶à¿ò¼ÜʹÓü°»·¾³²ÎÊýµÄÖØ¸´ÅäÖã¬È翪Æô×¢½â¡¢ÅäÖÃÈÕÖ¾µÈ¡£Spring BootÖÂÁ¦ÓÚÈõ»¯ÕâЩ²»±ØÒªµÄ²Ù×÷£¬ÌṩĬÈÏÅäÖ㬵±È»ÕâЩĬÈÏÅäÖÃÊÇ¿ÉÒÔ°´ÐèÐ޸ĵģ¬¿ìËٴ¡¢¿ª·¢ºÍÔËÐÐSpringÓ¦Óà + +# ºÍSpringMVCµÄ¹ØÏµ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230415111153.png) + +Spring Boot£ºSpring Bootʹ¿ìËÙÒýµ¼ºÍ¿ªÊ¼¿ª·¢»ùÓÚSpringµÄÓ¦ÓóÌÐò±äµÃÈÝÒס£ Ëü±ÜÃâÁ˺ܶàÑù°å´úÂë¡£ ËüÔÚÄ»ºóÒþ²ØÁ˺ܶิÔÓÐÔ£¬Òò´Ë¿ª·¢ÈËÔ±¿ÉÒÔ¿ìËÙÉÏÊÖ²¢ÇáËÉ¿ª·¢»ùÓÚSpringµÄÓ¦ÓóÌÐò¡£ + +Spring MVC£ºSpring MVCÊÇÓÃÓÚ¹¹½¨WebÓ¦ÓóÌÐòµÄWeb MVC¿ò¼Ü¡£ Ëü°üº¬Ðí¶àÓÃÓÚ¸÷ÖÖ¹¦ÄܵÄÅäÖÃÎļþ¡£ ÕâÊÇÒ»¸öÃæÏòHTTPµÄWebÓ¦ÓóÌÐò¿ª·¢¿ò¼Ü¡£ + + + +Spring BootºÍSpring MVC³öÓÚ²»Í¬µÄÄ¿µÄ¶ø´æÔÚ¡£ ÏÂÃæÌÖÂÛÁËSpring BootºÍSpring MVCÖ®¼äµÄÖ÷񻂿±ð£º + + + +| Spring Boot | Spring MVC | +| --- | --- | +| Spring BootÓÃÓÚʹÓúÏÀíµÄĬÈÏÖµ´ò°ü»ùÓÚSpringµÄÓ¦ÓóÌÐò¡£ | Spring MVCÊÇSpring¿ò¼ÜÏ»ùÓÚÄ£ÐÍÊÓͼ¿ØÖÆÆ÷µÄWeb¿ò¼Ü¡£ | +| ËüÌṩÁËĬÈÏÅäÖÃÀ´¹¹½¨SpringÖ§³ÖµÄ¿ò¼Ü¡£ | ËüÌṩÁËÓÃÓÚ¹¹½¨WebÓ¦ÓóÌÐòµÄ¼´ÓÃÐ͹¦ÄÜ¡£ | +| ÎÞÐèÊÖ¶¯¹¹½¨ÅäÖᣠ| ËüÐèÒªÊÖ¶¯½øÐй¹½¨ÅäÖᣠ| +| ²»ÐèÒª²¿ÊðÃèÊö·û¡£ | ²¿ÊðÃèÊö·ûÊDZØÐèµÄ¡£ | +| Ëü±ÜÃâÁËÑù°å´úÂ룬²¢½«ÒÀÀµÏî°ü×°ÔÚÒ»¸öµ¥ÔªÖС£ | Ëü·Ö±ðÖ¸¶¨Ã¿¸öÒÀÀµÏî¡£ | +| Ëü¼õÉÙÁË¿ª·¢Ê±¼ä²¢Ìá¸ßÁËÉú²úÂÊ¡£ | ʵÏÖÏàͬĿµÄÐèÒª¸ü¶àʱ¼ä¡£ | + +# ºÍ΢·þÎñ¡¢SpringCloudµÄ¹ØÏµ + +Spring BootÊÇSpring¼Ò×åµÄ³ÉÔ±£¬ËüÊÇÒ»¸öȫеĿò¼Ü£¬ËüµÄÉè¼ÆÄ¿µÄÊǾ¡¿ÉÄܼòµ¥ºÍ¿ìËٵĿª·¢¡¢ÔËÐÐSpringÓ¦ÓóÌÐò£¬¼ò»¯ÅäÖá£ËüΪ¿ª·¢Õß¿ì½ÝµÄʹÓÃSpring¼°Ïà¹Ø¿ª·¢¿ò¼ÜÌṩÁ˱ãÀû£¬µ«ÊÇËü²¢²»ÊÇ΢·þÎñµÄ¿ò¼Ü£¬ËüÖ»ÊÇΪ΢·þÎñ¿ò¼ÜµÄʹÓÃÒ²ÌṩÁ˺ܺõĽÅÊּܡ£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230415111929.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230415111950.png) +ÉÏÃæÁ½ÕÅͼ±íÃ÷ÁËÁ½ÕߵĻù±¾Çø±ð£¬springbootÓÃÓÚ¹¹½¨µ¥¸ö·þÎñÓ¦Óã¬Ò²¾ÍÊÇÎÒÃdz£ËµµÄ΢·þÎñ + +¶øspringcloudÓÃÓÚ·þÎñÖÎÀí£¬Ò²¾ÍÊÇ´®Áª¡¢¹ÜÀíºÍЭµ÷¶à¸ö΢·þÎñ£¨springbootÓ¦ÓóÌÐò£© + +springcloudÖаüº¬ÁËÖÚ¶à·þÎñÖÎÀíÏà¹ØµÄ×é¼þ£º·þÎñAPIÍø¹ØSpringcloud Gateway£¬ÅäÖÃÖÐÐÄ×é¼þConfig Serever£¬¶Ï·Æ÷Circuit Breaker¡¢·þÎñ×¢²áÖÐÐÄService Registty£¬¼à¿Ø×é¼þSleuthµÈ + +# ʹÓÃSpringbootµÄ8¸öÔ­Òò + +## ¸ü¿ìµÄ¿ª·¢ + +Spring Boot ÔÚ Spring Éú̬ϵͳÉÏ×ö³öÁ˺ܶà¾ö¶¨ºÍ¹ÌÖ´¼º¼ûµÄĬÈÏÉèÖᣠÕâÖÖÐÔÖÊÓÐÖúÓÚ¿ª·¢ÈËÔ±¿ìËÙÉèÖò¢Í¶È뿪·¢¡£ + +ÀýÈ磬Spring MVC ¿ÉÒÔͨ¹ý´óÁ¿ XML bean ¶¨ÒåºÍ×Ô¶¨Òå servlet ÀàʵÏÖ¡£ µ«ÊÇʹÓà Spring Boot£¬Ëü¾ÍÏñÌí¼ÓÆô¶¯Æ÷ÒÀÀµÏîÒ»Ñù¼òµ¥¡£ ¾ø¶Ô²»ÐèÒª´úÂëÉú³É XML ÅäÖᣠ+ +## Ò»ÇнÔÓÐÏÈ»ú + +Spring Boot Starters Êǰüº¬¿âºÍËüÃǵÄһЩ×Ô¶¯ÅäÖÃµÄ Maven ÃèÊö·û¡£ ¶øÇÒ£¬ÕâЩÆô¶¯Æ÷ÓÐÖúÓÚΪ Spring Boot Ó¦ÓóÌÐòÌṩ¹¦ÄÜ¡£ ÏëÒª½¨Á¢Êý¾Ý¿âÁ¬½Ó£¿ ÓÐÒ»¸öÆô¶¯Æ÷ÒÀÀµÏî¡£ ÏëÓëÏûÏ¢¶ÓÁÐͨ»°»ò·¢Ë͵ç×ÓÓʼþ£¿ Spring Boot º­¸ÇÁËÕâÒ»ÇС£ + +¶ÔÓÚ¼¸ºõËùÓÐµÄ Spring Ä£¿é£¬¶¼ÓÐÒ»¸öÆô¶¯Æ÷ÒÀÀµÏî¿ÉÒÔΪÄúÅäÖÃËùÓÐÄÚÈÝ¡£ ÉõÖÁһЩµÚÈý·½¿âҲͨ¹ýËûÃÇµÄÆô¶¯Ä£¿éÌṩ¶Ô Spring µÄÖ§³Ö¡£ Èç¹ûûÓÐÕâЩÆô¶¯Æ÷£¬Äú×÷Ϊ¿ª·¢ÈËÔ±½«²»µÃ²»Î¬»¤ÒÀÀµÏîºÍ XML ÅäÖᣠÕâÊÇÄúÓ¦¸ÃʹÓà Spring Boot µÄÁíÒ»¸öÔ­Òò¡£ + +## ǶÈëʽ·þÎñÆ÷ + +Spring Boot ΪǶÈëʽ Tomcat¡¢Jetty ºÍ Undertow ·þÎñÆ÷Ìṩ¿ªÏä¼´ÓõÄÖ§³Ö¡£ ÕâÑù£¬¿ª·¢ÈËÔ±¾Í²»±Øµ£ÐÄÔÚ´«Í³Ó¦Ó÷þÎñÆ÷Öв¿Êð Web Ó¦ÓóÌÐò¡£ ͨ¹ýÊʵ±µÄÆô¶¯Æ÷ÒÀÀµÏÄúÉõÖÁ¿ÉÒÔ½«Ò»ÖÖ·þÎñÆ÷¼¼ÊõÓëÆäËû·þÎñÆ÷¼¼Êõ½»»»¡£ ËùÒÔÄãʵ¼ÊÉÏ×îÖյõ½ÁËÒ»¸ö¿ÉÒÔÏñÈκΠJAR Ò»ÑùÔËÐÐµÄ JAR Îļþ¡£ ÔÚÆô¶¯Ê±£¬JAR °üº¬×ã¹»µÄ¿âºÍÅäÖÃÒÔ×÷ΪӦÓóÌÐò·þÎñÆ÷Æô¶¯²¢ÕìÌýÇëÇó¡£ + +Èç¹ûÄú¶ÔǶÈëʽ·þÎñÆ÷²»¸ÐÐËȤ£¬Äú¿ÉÒÔËæÊ±½« Spring Boot Ó¦ÓóÌÐò´Ó JAR ת»»Îª WAR£¬²¢½«ËüÃDz¿Êðµ½´«Í³·þÎñÆ÷¡£ + +## IDE ¶Ô Spring Boot µÄÖ§³Ö + +ËùÓÐÖ÷ÒªµÄ IDE ¶¼Ìṩ¶Ô Spring Boot ´úÂë°ïÖúµÄÖ§³Ö¡£ ÀýÈ磬IntelliJ IDEA Ultimate Ϊ Spring Boot ÏîÄ¿ÌṩÁ˳öÉ«µÄ´úÂëÍê³ÉºÍµ¼º½¹¦ÄÜ¡£³ý´ËÖ®Í⣬VSCode ºÍ Eclipse Ò²¶ÔÆäÌṩÁ˷ḻµÄ¹¦ÄÜÖ§³Ö¡£ + +## Éú²ú»·¾³³£Óù¦ÄÜ + +Spring Boot ÌṩÁËÉú²ú¾ÍÐ÷µÄÌØÐÔ£¬ÀýÈç¼à¿Ø¡¢Ö¸±êºÍ¿ªÏä¼´ÓõÄ×¢Ïú¡£ ÓÐÁËÕâÐ©ÌØÐÔ£¬¿ª·¢ÈËÔ±¿ÉÒÔ±ÜÃâ¶îÍâµÄÅäÖᣠÀýÈ磬½¡¿µÖ´ÐÐÆ÷¶ËµãµÈ¹¦ÄÜʹӦÓóÌÐò״̬¼à¿Ø³ÉΪ¿ÉÄÜ¡£ ÀýÈ磬 + +Äú¿ÉÒÔÈÃÏñ Prometheus ÕâÑùµÄ¹¤¾ßÊÕ¼¯Ó¦ÓóÌÐòÖ¸±ê + +ÔÚÄúµÄ Kubernetes »ò Openshift »·¾³ÖÐʹÓþÍÐ÷ÐԺͻîÔ¾¶È½¡¿µ¶Ëµã¡£ + +Ö»ÐèÌí¼ÓÆäËûÊôÐÔ»òͨ¹ý /actuator/logging ¶Ëµã¼´¿É¸ü¸ÄÈÕÖ¾¼Ç¼¼¶±ð¡£ + +´ËÍ⣬¿ª·¢ÈËÔ±¿ÉÒÔʹÓÃ×Ô¼ºµÄ×Ô¶¨Ò彡¿µ¶ËµãÅäÖÃÕâЩִÐÐÆ÷¶Ëµã¡£ + +## ¿ªÏä¼´ÓÃµÄ JUnit Ö§³Ö + +ĬÈÏÇé¿öÏ£¬ËùÓÐ Spring Boot ÏîÄ¿¶¼´øÓÐ JUnit 5¡£ ´ËÍ⣬Spring Boot ÌṩÁË@SpringBootTest ×¢½âÀ´ÔÚÎÒÃÇÐèҪʱ³õʼ»¯²âÊÔÉÏÏÂÎÄ¡£ ËùÒÔ¿ª·¢ÈËÔ±Ö»ÐèÒª±àд²âÊÔÓÃÀý¡£ ËûÃDz»±ØÔÙµ£ÐIJâÊÔÓÃÀýµÄ¸´ÔÓ spring ÉÏÏÂÎÄ¡£ + +ÀýÈ磬ÏÂÃæ×Ô¶¯Éú³ÉµÄ²âÊÔ½«¼ì²éÉÏÏÂÎÄÊÇ·ñÕýÈ·¼ÓÔØ¡£ + +```` +@SpringBootTest +class SpringBootDerbyDatabaseApplicationTests { + + @²âÊÔ + void contextLoads() { + } + +} +```` + +## Spring Profiles + +Spring Profiles ÊÇ spring Boot µÄÒ»¸öÇ¿´óÌØÐÔ£¬ÓÐÖúÓÚ¸ôÀëÓ¦ÓóÌÐòÖеIJ»Í¬×é¼þ¡£ ʹÓÃÅäÖÃÎļþ£¬Äú¿ÉÒÔÔÚÌØ¶¨»·¾³ÖÐÆôÓûò½ûÓÃ×é¼þ¡£ µ±Äú±ØÐë¸ù¾ÝÌØ¶¨Ìõ¼þʹÓò»Í¬µÄ×é¼þʱ£¬Õâ¿ÉÄÜ»áÅÉÉÏÓó¡¡£ +```` +@Profile(value = {"prod","uat"}) +class RabbitMQConfig { + +// ÌýÖÚ + +} +```` + +ÔÚÉÏÃæµÄ´úÂëÖУ¬ÉÏÏÂÎĽ«ÏÞÖÆÍÃ×ÓÕìÌýÆ÷½öÔÚ¾ßÓÐ prod »ò uat ×÷Ϊ»î¶¯ÅäÖÃÎļþµÄ»·¾³ÖÐÔËÐС£ + +## ¶àÖÖ´ò°üºÍ²¿ÊðÑ¡Ïî + +¸Ã¿ò¼ÜÌṩÁ˶àÖÖ·½Ê½À´´ò°üÄúµÄÓ¦ÓóÌÐò¡£ ÕýÈçÎÒÃÇ֮ǰËù˵£¬Ó¦ÓóÌÐò¿ÉÒÔÊÇ JAR »ò WAR Îļþ¡£ ͨ¹ýһЩ¶îÍâµÄÅäÖúͲÎÊý£¬Äú»¹¿ÉÒÔ´´½¨¿ªÏä¼´ÓõĸßÐÔÄÜ docker ¾µÏñ¡£ + +Æô¶¯ºÍÍ£Ö¹ Spring Boot Ó¦ÓóÌÐò·Ç³£¼òµ¥¡£ ´ËÍ⣬Äú¿ÉÒÔͨ¹ý¼¸¸ö¶îÍâµÄ²½Ö轫ÕâЩ JAR Îļþ²¿ÊðΪ linux ·þÎñ¡£ JAR Îļþ³ÆÎª FAT jar£¬ËüÃǰüº¬ÓëÓ¦ÓóÌÐòÏà¹ØµÄËùÓÐÒÀÀµÏî¡£ ÕâʹµÃ²¿Êð¹ý³Ì²»ÄÇô¸´ÔÓ¡£ ʵ¼ÊÉÏ£¬ÕâЩ¹¹½¨¿ÉÒÔÔÚÈκÎ×°ÓÐ Java 8 »ò¸ü¸ß°æ±¾µÄ»úÆ÷ÉÏÔËÐС£ + +# ²Î¿¼ÎÄÕ + +[https://spring.io/](https://spring.io/) + +[https://pdai.tech/md/spring/springboot/springboot-x-overview.html](https://pdai.tech/md/spring/springboot/springboot-x-overview.html) + +[https://springhow.com/why-use-spring-boot/](https://springhow.com/why-use-spring-boot/) + +[https://dzone.com/articles/why-springboot](https://dzone.com/articles/why-springboot) + +[https://scand.com/company/blog/pros-and-cons-of-using-spring-boot/](https://scand.com/company/blog/pros-and-cons-of-using-spring-boot/) + +[https://cloud.tencent.com/developer/article/1620255](https://cloud.tencent.com/developer/article/1620255) + +[https://www.yiibai.com/spring-boot/spring-vs-spring-boot-vs-spring-mvc.html](https://www.yiibai.com/spring-boot/spring-vs-spring-boot-vs-spring-mvc.html) \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\237\272\346\234\254\344\275\277\347\224\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\237\272\346\234\254\344\275\277\347\224\250.md" new file mode 100644 index 0000000..247e419 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\345\237\272\346\234\254\344\275\277\347\224\250.md" @@ -0,0 +1,108 @@ +# ¿ìËÙ¹¹½¨SpringBootÓ¦Óà + +Õâ¸ö½Ì³ÌÀ´×ÔÓÚspringboot¹ÙÍø£¬×㹻ȨÍþ£¬Ò²×ã¹»¼òµ¥¡£ + +## µÚÒ»¸ö hello world ³ÌÐò + +Äú½«¹¹½¨Ò»¸ö¾­µäµÄ¡°Hello World£¡¡±ÈκÎä¯ÀÀÆ÷¶¼¿ÉÒÔÁ¬½Óµ½µÄ¶Ëµã¡£ÄãÉõÖÁ¿ÉÒÔ¸æËßËüÄãµÄÃû×Ö£¬Ëü»áÒÔ¸üÓѺõķ½Ê½»ØÓ¦¡£ + +## »ù±¾»·¾³ÒªÇó + +1¡¢Ê×ÏÈÄã×îºÃÓÐÒ»¸ö³ÃÊÖµÄIDE,ÈÈÃÅÑ¡Ôñ°üÀ¨ IntelliJ IDEA¡¢Spring Tools¡¢Visual Studio Code »ò Eclipse µÈµÈ¡£ + +2¡¢JDK£¬ÖÁÓÚ°æ±¾µÄ»°£¬8-17¶¼ÊDz»´íµÄÑ¡Ôñ + +3¡¢µ±È»ÕâÀﻹÐèÒªµ¼ÈëmavenµÄpomÒÀÀµ£¬ËùÒÔÎÒÃÇÒ²ÐèÒªmaven£¬maven²å¼þÔÚidea×Ô´øÁË¡£ÎÒÃÇ»áÔÚ½ÓÏÂÀ´µÄ²¿·Ö½øÐнéÉÜ£¬ÐèÒªÒýÈëÄÄЩÒÀÀµ + +## µÚÒ»²½£ºÆô¶¯Ò»¸öеÄSpring BootÏîÄ¿ + +ʹÓÃ[start.spring.io](http://start.spring.io/)´´½¨Ò»¸ö¡°web¡±ÏîÄ¿¡£ÔÚ¡°dependencies¡±¶Ô»°¿òÖÐËÑË÷²¢Ìí¼Ó¡°web¡±ÒÀÀµÏÈçÆÁÄ»½ØÍ¼Ëùʾ¡£ + +µã»÷¡°Éú³É¡±°´Å¥£¬ÏÂÔØ zip Îļþ£¬²¢½«Æä½âѹµ½Äú¼ÆËã»úÉϵÄÒ»¸öÎļþ¼ÐÖС£ + +![Start.spring.io ¿ìËÙÈëÃÅ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/quickstart-1.png) + +[start.spring.io](http://start.spring.io/)´´½¨µÄÏîÄ¿°üº¬[Spring Boot](https://spring.io/projects/spring-boot)£¬Ò»¸öÈÃSpring×¼±¸ºÃÔÚÄúµÄÓ¦ÓóÌÐòÄÚ²¿¹¤×÷£¬µ«²»ÐèҪ̫¶à´úÂë»òÅäÖᣠSpring Boot ÊÇÆô¶¯ Spring ÏîÄ¿µÄ×î¿ìºÍ×îÁ÷Ðеķ½Ê½¡£ + +ÕâÀïÎÒÃÇÑ¡ÔñʹÓÃmaven×÷Ϊ°ü¹ÜÀí¹¤¾ß£¬ÏÂÔØÏÂÀ´µÄÏîÄ¿Àï»á°üº¬pomÎļþ£¬pomÎļþÀï°üº¬ÁËÄãµÄÌí¼ÓµÄÒÀÀµ¡£ + +```` + + 4.0.0 org.springframework.boot spring-boot-starter-parent 3.0.5 com.example demo 0.0.1-SNAPSHOT demo Demo project for Spring Boot 17 org.springframework.boot spring-boot-starter-web + org.springframework.boot spring-boot-starter-test test + org.springframework.boot spring-boot-maven-plugin + +```` + + +## µÚ¶þ²½£ºÌí¼ÓÄãµÄ´úÂë + +ÔÚÄúµÄ IDE Öдò¿ªÏîÄ¿²¢ÔÚ `src/main/java/com/example/demo` Îļþ¼ÐÖÐÕÒµ½ `DemoApplication.java` Îļþ¡£ + +ÏÖÔÚͨ¹ýÌí¼ÓÏÂÃæ´úÂëÖÐÏÔʾµÄ¶îÍâ·½·¨ºÍ×¢ÊÍÀ´¸ü¸ÄÎļþµÄÄÚÈÝ¡£Äú¿ÉÒÔ¸´ÖƲ¢Õ³Ìù´úÂë»òÖ±½Ó¼üÈë´úÂë¡£ +``` +package com.example.demo; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class DemoApplication { + public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @GetMapping("/hello") public String hello(@RequestParam(value = "name", defaultValue = "World") String name) { return String.format("Hello %s!", name); }} + +``` + +ÕâÊÇÔÚ Spring Boot Öд´½¨Ò»¸ö¼òµ¥µÄ¡°Hello World¡± Web ·þÎñËùÐèµÄËùÓдúÂë¡£ + +ÎÒÃÇÌí¼ÓµÄ`hello()`·½·¨Ö¼ÔÚ»ñȡһ¸öÃûΪnameµÄString²ÎÊý£¬È»ºó½«´Ë²ÎÊýÓë´úÂëÖеĵ¥´Ê`"Hello"`½áºÏÆðÀ´¡£ + +ÕâÒâζ×ÅÈç¹ûÄúÔÚÇëÇóÖн«ÄúµÄÃû×ÖÉèÖÃΪ¡°Amy¡±£¬ÔòÏìÓ¦½«ÊÇ¡°Hello Amy¡±¡£ + +`@RestController` ×¢Ê͸æËß Spring£¬´Ë´úÂëÃèÊöÁËÒ»¸ö¶Ëµã£¬¸Ã¶ËµãÓ¦ÔÚ Web ÉÏ¿ÉÓᣠ+@GetMapping(¡°/hello¡±) ¸æËß Spring ʹÓÃÎÒÃÇµÄ hello() ·½·¨À´ÏìÓ¦·¢Ë͵½ http://localhost:8080/hello µØÖ·µÄÇëÇó¡£ + +×îºó£¬@RequestParam ¸æËß Spring ÔÚÇëÇóÖÐÆÚÍûÒ»¸öÃû³ÆÖµ£¬µ«Èç¹û²»´æÔÚ£¬Ëü½«Ä¬ÈÏʹÓõ¥´Ê¡°World¡±¡£ + +## µÚÈý²½£ºÆô¶¯ + +ÈÃÎÒÃǹ¹½¨²¢ÔËÐгÌÐò¡£´ò¿ªÃüÁîÐУ¨»òÖÕ¶Ë£©²¢µ¼º½µ½ÄúÓµÓÐÏîÄ¿ÎļþµÄÎļþ¼Ð¡£ + +ÎÒÃÇ¿ÉÒÔͨ¹ý·¢³öÒÔÏÂÃüÁîÀ´¹¹½¨ºÍÔËÐÐÓ¦ÓóÌÐò£º + +**MacOS/Linux:** + +``` +COPY./gradlew bootRun + +``` + +**Windows:** + +``` +COPY.\gradlew.bat bootRun + +``` + +ÄúÓ¦¸Ã»á¿´µ½Ò»Ð©Óë´Ë·Ç³£ÏàËÆµÄÊä³ö£º +![Quick Start On Start.spring.io](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/quickstart-2.png) + +ÕâÀïµÄ×îºó¼¸ÐиæËßÎÒÃÇ£¬SpringÓ¦ÓÃÒѾ­¿ªÊ¼ÔËÐÐÁË¡£ Spring Boot µÄǶÈëʽ Apache Tomcat ·þÎñÆ÷³äµ±ÍøÂç·þÎñÆ÷£¬²¢ÕýÔÚ¼àÌý¡°localhost¡±¶Ë¿Ú¡°8080¡±ÉϵÄÇëÇó¡£ + +´ò¿ªä¯ÀÀÆ÷£¬ÔÚ¶¥²¿µÄµØÖ·À¸ÖÐÊäÈë`http://localhost:8080/hello`¡£ + +ÄãÓ¦¸ÃµÃµ½ÕâÑùÒ»¸öÓѺõĻØÓ¦£º +![Quick Start On Start.spring.io](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/quickstart-3.png) + +# ×ܽá +¾ÍÊÇÈç´Ë¼òµ¥£¬µÚÒ»¸öSpringBootÓ¦ÓþÍÕâô¹¹½¨Íê³ÉÁË£¬Äã²»ÐèÒª¶îÍâµÄÅäÖÃÎļþ£¬¶îÍâµÄÒÀÀµ£¬ÉõÖÁÊǶîÍâµÄ·þÎñÆ÷¡£ +Ö»ÐèÒªÒ»¸öÆô¶¯À࣬¾Í¿ÉÒÔʵÏÖÒ»¸ö×î»ù±¾µÄSpringBootµÄÓ¦Óᣠ+ +ÕâÒ²ÊÇΪʲôspringboot¿ÉÒÔÓÃÀ´¿ìËÙ¹¹½¨Ò»¸ö΢·þÎñ£¬ÒòΪËüʵÔÚÊÇÌ«·½±ãÁË¡£ +µ±È»£¬Êµ¼Ê¿ª·¢ÖÐÎÒÃÇÐèÒªÓõ½¸ü¶àspringbootµÄ¹¦ÄܺÍÌØÐÔ£¬ÎÒÃǽ«»áÔÚ½ÓÏÂÀ´µÄÕ½ÚÖÐÖð½¥Õ¹¿ª½éÉÜ¡£ + +# ²Î¿¼ÎÄÕ +https://spring.io/quickstart \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\351\205\215\347\275\256\346\226\207\344\273\266\347\256\241\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\351\205\215\347\275\256\346\226\207\344\273\266\347\256\241\347\220\206.md" new file mode 100644 index 0000000..46c6771 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\347\232\204\351\205\215\347\275\256\346\226\207\344\273\266\347\256\241\347\220\206.md" @@ -0,0 +1,2989 @@ +## 1.SpringBootÅäÖùÜÀíµÄ±³¾°Óë»ù´¡ + +ΪʵÏÖ¿ìËٴºÍ¿ª·¢£¬ÏîÄ¿ÒÔSpringboot¿ò¼Ü´î½¨£¬springboot´î½¨µÄÏîÄ¿¿ÉÒÔ½«ÏîĿֱ½Ó´ò³Éjar°ü²¢ÔËÐУ¬ÎÞÐè×Ô¼º°²×°ÅäÖÃTomcat»òÕ߯äËû·þÎñÆ÷£¬ÊÇÒ»ÖÖ·½±ã¿ì½ÝµÄ²¿Êð·½Ê½¡£ + +¼ÙÉèÏîÄ¿ÒÔ×î³£¹æµÄ·½Ê½´ò°ü³ÉÒ»¸öÕûÌåµÄjar°ü²¿Ê𣬼´ÅäÖÃÎļþºÍµÚÈý·½ÒÀÀµ°ü¶¼°üº¬ÔÚjar°üÀ¾Í»áÓÐÈçÏÂÁ½¸öÎÊÌâ + +ÎÊÌâÒ»£ºÏîÄ¿ÔËÐйý³ÌÖУ¬Òª¸Ä¶¯ÅäÖÃÎļþµÄ»°ÐèÒªÖØÐ´ò°ü²¢²¿Êð¡£ + +ÎÊÌâ¶þ£º¶à¸öµÚÈý·½ÒÀÀµ°ü¶¼Ïà½üµÄÏîĿҪ²¿ÊðÔÚͬһ̨·þÎñÆ÷ʱ£¬¸÷×ÔµÄjar°ü¶¼°üº¬ÁËÏàͬµÄµÚÈý·½ÒÀÀµ°ü£¨¼ÙÉèÏîÄ¿jar°üÓÐ100M£¬µÚÈý·½ÒÀÀµ°ü¿ÉÄܾÍÕ¼ÓÃÁË99M£©£¬ÕâÑùµÚÈý·½ÒÀÀµ°üÈßÓàÔì³ÉÁË·þÎñÆ÷×ÊÔ´µÄÀË·ÑÒÔ¼°½µµÍÁËÏîÄ¿²¿ÊðµÄЧÂÊ¡£ + +Èç¹û½«¸÷ÏîÄ¿µÄÅäÖÃÎļþ¡¢µÚÈý·½ÒÀÀµ°ü¶¼ÌáÈ¡µ½jar°üÍâͳһ¹ÜÀí£¬ÕâÑù¼´ÌáÉýÁËÏîÄ¿´ò°üЧÂÊÓÖ½ÚÔ¼ÁË·þÎñÆ÷µÄ´ÅÅÌÏûºÄ£¬Í¬Ê±ÏîÄ¿µÄÔËάҲÊǷdz£·½±ãµÄ£¬¸Ä¶¯ÁËÅäÖÃÎļþÖØÆôÏ·þÎñ¾Í¿ÉÒÔÁË£¬ÎÞÐèÖØÐ¹¹½¨²¿Êð¡£ + +ÏÂÃæÊǾßÌåµÄʵÏÖ·½°¸ + +### 1.1 **ÅäÖÃÎļþͳһ¹ÜÀí** + +``` + - springbootºËÐÄÅäÖÃÎļþ + - Springboot¶ÁÈ¡ºËÐÄÅäÖÃÎļþ£¨application.properties£©µÄÓÅÏȼ¶Îª + - Jar°üͬ¼¶Ä¿Â¼µÄconfigĿ¼ + - Jar°üͬ¼¶Ä¿Â¼ + - classPath(¼´resourcesĿ¼)µÄconfigĿ¼ + - classpathĿ¼ + +``` + +ÉÏÃæÊÇspringbootĬÈÏÈ¥ÄÃ×Ô¼ºµÄºËÐÄÅäÖÃÎļþµÄÓÅÏȼ¶£¬»¹ÓÐÒ»ÖÖ×î¸ßÓÅÏȼ¶µÄ·½Ê½ÊÇÏîÄ¿Æô¶¯Ê±Í¨¹ýÃüÁîµÄ·½Ê½Ö¸¶¨ÏîÄ¿¼ÓÔØºËÐÄÅäÖÃÎļþ£¬ÃüÁîÈçÏ +java ¨Cjar -Dspring.config.location=xxx/xxx/xxxx.properties xxxx.jar + +Èç¹ûSpring BootÔÚÓÅÏȼ¶¸ü¸ßµÄλÖÃÕÒµ½ÁËÅäÖã¬ÄÇôËü»áÎÞÊÓÓÅÏȼ¶¸üµÍµÄÅäÖà + +### 1.2 **ÆäËû×ÊÔ´ÅäÖÃÎļþ** + +ÉÏÃæÃèÊöµÄSpringbootºËÐÄÎļþÒѾ­Äܹ»ÌáÈ¡³öjar°üÍâ½øÐйÜÀíÁË£¬µ«ÊÇ»¹ÓÐÆäËûһЩҵÎñÉϵÄÅäÖÃÎļþ£¬ÈçÊý¾ÝÔ´ÅäÖÃÎļþ£¬¹«¹²×ÊÔ´¶¨ÒåÅäÖÃÎļþ£¨³£Á¿£¬FTPÐÅÏ¢µÈ£©£¬quartz¶¨Ê±Æ÷£¬ÈÕÖ¾µÈÅäÖÃÎļþÎÒÃÇÈçºÎÈ¥ÌáÈ¡³öÀ´²¢È·±£ÄÜÔÚ´úÂëÖÐÒýÓõ½ÄØ + +ÎÒÃÇÖªµÀSpringbootÏîÄ¿¿ÉÒÔͨ¹ý×¢½â·½Ê½À´»ñÈ¡Ïà¹ØÅäÖÃÎļþ£¬ËùÒÔÎÒÃÇÒ²ÊÇͨ¹ý×¢½â·½Ê½ÈÃÏîÄ¿Äܹ»ÒýÓõ½jar°üÍⲿµÄÅäÖÃÎļþµÄ£¬ÈçÏÂͼ£º +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://img-blog.csdnimg.cn/20190121170400864.png) + +@PropertySourceÀïÃæµÄvalueÓÐÁ½¸öÖµ£¬µÚÒ»¸öÊÇclasspathÏÂconfigĿ¼ÏµÄÊý¾ÝÔ´ÅäÖÃÎļþ£¬µÚ¶þ¸öÔòÊǸù¾Ýspring.profiles.path¶¯Ì¬»ñÈ¡µÄĿ¼£¬spring.profiles.pathÊÇÎÒÃÇÔÚºËÐÄÎļþ×Ô¶¨ÒåµÄÒ»¸öÅäÖÃÏËüµÄÖµÊÇÎÒÃÇÅäÖÃÎļþͳһ¹ÜÀíµÄÎļþ¼Ð·¾¶£¬ºóÃæµÄignoreResourceNotFound=trueÔòÊÇÉ趨¼ÙÈç¸ù¾ÝÇ°ÃæÒ»¸ö·¾¶Ã»ÓÐÕÒµ½Ïà¹ØÅäÖÃÎļþ£¬Ôò¸ù¾ÝµÚ¶þ¸ö·¾¶È¥ÕÒ¡£ + +ÎÒÃÇ»¹¿ÉÒÔÖ±½Ó¸ù¾Ý·¾¶£¬ÓÃFileSystemResourceÀàÈ¥¼ÓÔØÒ»¸öÅäÖÃÎļþʵÀý³öÀ´£¬ÈçÏÂͼ +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://img-blog.csdnimg.cn/20190121170338934.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dlZUxvb25n,size_16,color_FFFFFF,t_70) + +Ô­ÀíÀàËÆ£¬¸ù¾ÝÔÚºËÐÄÎļþ×Ô¶¨ÒåµÄͳһÅäÖÃĿ¼µÄ·¾¶À´¼ÓÔØÅäÖÃÎļþ + +ÁíÍâlogbackÈÕÖ¾ÅäÖÃÎļþ¼ÓÔØ·½Ê½ÈçÏ£º +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://img-blog.csdnimg.cn/20190121170306979.png) + +×ÛÉÏËùÊö£¬ÎÒÃÇÊáÀíÒ»ÏÂʵÏÖ·½°¸µÄ˼· + +``` + - ÔÚspringbootºËÐÄÎļþÀﶨÒåÒ»¸öspring.profiles.pathÅäÖÃÏËüµÄÖµÖ¸ÏòÎÒÃÇËùÓÐÅäÖÃÎļþͳһ·ÅÖõÄĿ¼£¬°üº¬ºËÐÄÎļþ×ÔÉíÒ²ÊÇ·ÅÖÃÔÚÀïÃæµÄ + + - ´úÂë»òÕßÅäÖÃÎļþÀï¼ÓÔØÅäÖÃÎļþµÄµØ·½Ò²Ó¦¸Ã»ñÈ¡spring.profiles.pathÅäÖÃÏîÀ´¶¯Ì¬¼ÓÔØ¸Ã·¾¶ÏµÄÅäÖÃÎļþ + + - Pom.xmlÎļþÐ޸Ĵò°üÏà¹ØÄ£¿é£¬½«ÅäÖÃÎļþÅųý£¬ÕâÑùÎÒÃÇ´ò³öµÄjar°üÊDz»º¬ÅäÖÃÎļþµÄ£¬´ò°üÅäÖÃÇë²Î¿¼Îĵµ½Úµã3 + + - Æô¶¯jar°üʱ£¬Í¨¹ýÃüÁîÖ¸¶¨¼ÓÔØµÄºËÐÄÎļþΪspring.profiles.pathϵĺËÐÄÎļþ + +``` +**µÚÈý·½ÒÀÀµ°üͳһ¹ÜÀí** + +ͨ³£µÚÈý·½jar°ü¿ÉÒÔ´ò½øjar°üÀҲ¿ÉÒÔ·ÅÔÚÏîÄ¿jar°üͬ¼¶Ä¿Â¼ÏµÄlibĿ¼£¬ÎÒÃÇ¿ÉÒÔ¸ù¾ÝÐÞ¸Äpom.xml´ò°üÅäÖÃÀ´ÊµÏÖ£¬Çë²Î¿¼Îĵµ½Úµã3´ò°üÅäÖà + +**´ò°üÅäÖÃ** + +``` + + + + src/main/java + + **/*.properties + **/*.xml + + true + + + src/main/resources + + + **/*.properties + **/*.xml + **/*.yml + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + true + true + + C:/Program Files/Java/jdk1.8.0_161/bin/javac.exe + + + + + maven-jar-plugin + + + + true + lib/ + false + com.xrq.demo.Application + + + ./ + + + + *.properties + *.yml + *.xml + config/** + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + package + + copy-dependencies + + + + ${project.build.directory}/lib + + + + + + + + +``` + +¸ÄºÃpom.xmlµÄbuildÄ£¿éºó£¬¾Í¿ÉÒÔͨ¹ýmvn package »òÕßmvn install´ò³öÎÒÃǵÄjar°üÁË + +1. **ÏîÄ¿¹ÜÀíshell½Å±¾±àд** + ×Ô¶¨Òåshell½Å±¾£¬ÊµÏÖÏîÄ¿µÄÆô¶¯£¬Í£Ö¹£¬×´Ì¬£¬ÖØÆô²Ù×÷£º + +``` +#!/bin/bash +#ÕâÀï¿ÉÌæ»»ÎªÄã×Ô¼ºµÄÖ´ÐгÌÐò,ÆäËû´úÂëÎÞÐè¸ü¸Ä +APP_NAME=demo1-0.0.1-SNAPSHOT.jar +JVM="-server -Xms512m -Xmx512m -XX:PermSize=64M -XX:MaxNewSize=128m -XX:MaxPermSize=128m -Djava.awt.headless=true -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled" +APPFILE_PATH="-Dspring.config.location=/usr/local/demo/config/application-demo1.properties" +#ʹÓÃ˵Ã÷,ÓÃÀ´ÌáʾÊäÈë²ÎÊý +usage() { +echo "Usage: sh Ö´Ðнű¾.sh [start|stop|restart|status]" +exit 1 +} +#¼ì²é³ÌÐòÊÇ·ñÔÚÔËÐÐ +is_exist(){ +pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}' ` +#Èç¹û²»´æÔÚ·µ»Ø1,´æÔÚ·µ»Ø0 +if [ -z "${pid}" ]; then +return 1 +else +return 0 +fi +} +#Æô¶¯·½·¨ +start(){ +is_exist +if [ $? -eq "0" ]; then +echo "${APP_NAME} is already running. pid=${pid} ." +else +nohup java $JVM -jar $APPFILE_PATH $APP_NAME > /dev/null 2>&1 +fi +} +#Í£Ö¹·½·¨ +stop(){ +is_exist +if [ $? -eq "0" ]; then +kill -9 $pid +else +echo "${APP_NAME} is not running" +fi +} +#Êä³öÔËÐÐ״̬ +status(){ +is_exist +if [ $? -eq "0" ]; then +echo "${APP_NAME} is running. Pid is ${pid}" +else +echo "${APP_NAME} is NOT running." +fi +} +#ÖØÆô +restart(){ +stop +start +} +#¸ù¾ÝÊäÈë²ÎÊý,Ñ¡ÔñÖ´ÐжÔÓ¦·½·¨,²»ÊäÈëÔòÖ´ÐÐʹÓÃ˵Ã÷ +case "$1" in +"start") +start +;; +"stop") +stop +;; +"status") +status +;; +"restart") +restart +;; +*) +usage +;; +esac + +``` + +**²¿Êð** + +linux·þÎñÆ÷ÉÏн¨¸öÎļþ¼Ð£¬½«ÎÒÃÇ´òºÃµÄÏîÄ¿jar°ü¶¼¶ª½øÈ¥£¬ÔÚjar°üµÄͬ¼¶Ä¿Â¼Ð½¨configºÍlibÎļþ¼Ð£¬·Ö±ð½«ÅäÖÃÎļþºÍµÚÈý·½ÒÀÀµ°ü¶ª½øÈ¥£¬Æä½á¹¹ÈçÏÂͼ£¬*.shΪ×Ô¼ºÐ´µÄÏîÄ¿Æô¶¯shell½Å±¾ + +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://img-blog.csdnimg.cn/20190121170223384.png) + +´ò¿ªconfigÄÚµÄspringbootºËÐÄÎļþ£¨Èçapplication-demo1.propertiesÎļþ£©£¬ + +spring.profiles.pathÅäÖÃÏî¸Ä³Éµ±Ç°ÅäÖÃÎļþËùÔÚµÄĿ¼£¬¼ÙÉèΪ/usr/local/demo/config + +´ò¿ª*.sh½Å±¾£¬ÐÞ¸ÄAPPFILE_PATHµÄÖµ£¬ÈçÏ + +APPFILE_PATH="-Dspring.config.location=/usr/local/demo/config/application-demo1.properties" + +**ÏîÄ¿¹ÜÀí** + +½øÈëjar°üËùÔÚĿ¼ִÐÐÏÂÃæÃüÁî + +sh [demo1.sh](http://demo1.sh/) start Æô¶¯ÏîÄ¿ + +sh [demo1.sh](http://demo1.sh/) stop Í£Ö¹ÏîÄ¿ + +sh [demo1.sh](http://demo1.sh/) restartÖØÆôÏîÄ¿ + +sh [demo1.sh](http://demo1.sh/) statusÏîĿ״̬ + +## 2\. Íⲿ»¯µÄÅäÖà + + + + + +Spring Boot¿ÉÒÔÈÃÄ㽫ÅäÖÃÍⲿ»¯£¬ÕâÑùÄã¾Í¿ÉÒÔÔÚ²»Í¬µÄ»·¾³ÖÐʹÓÃÏàͬµÄÓ¦ÓóÌÐò´úÂë¡£ Äã¿ÉÒÔʹÓø÷ÖÖÍⲿÅäÖÃÔ´£¬°üÀ¨Java properties Îļþ¡¢YAMLÎļþ¡¢»·¾³±äÁ¿ºÍÃüÁîÐвÎÊý¡£ + + + + + +ÊôÐÔÖµ¿ÉÒÔͨ¹ýʹÓà `@Value` ×¢½âÖ±½Ó×¢ÈëÄãµÄBean£¬Ò²¿ÉÒÔͨ¹ýSpring µÄ `Environment` ·ÃÎÊ£¬»òÕßͨ¹ý `@ConfigurationProperties` [°ó¶¨µ½¶ÔÏó](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties)¡£ + + + + + +Spring Boot ʹÓÃÒ»¸ö·Ç³£ÌرðµÄ `PropertySource` ˳Ðò£¬Ö¼ÔÚÔÊÐíºÏÀíµØÖØÐ´Öµ¡£ ºóÃæµÄ property source ¿ÉÒÔ¸²¸ÇÇ°ÃæÊôÐÔÔ´Öж¨ÒåµÄÖµ¡£ °´ÒÔÏÂ˳Ðò¿¼ÂÇ¡£ + + + + + +1. ĬÈÏÊôÐÔ£¨Í¨¹ý `SpringApplication.setDefaultProperties` Ö¸¶¨£©¡£ + +2. @Configuration ÀàÉ쵀 [`@PropertySource`](https://docs.spring.io/spring-framework/docs/6.0.5/javadoc-api/org/springframework/context/annotation/PropertySource.html) ×¢½â¡£Çë×¢Ò⣬ÕâÑùµÄÊôÐÔÔ´Ö±µ½application context±»Ë¢ÐÂʱ²Å»á±»Ìí¼Óµ½»·¾³ÖС£Õâ¶ÔÓÚÅäÖÃijЩÊôÐÔÀ´ËµÒѾ­Ì«ÍíÁË£¬±ÈÈç `logging.*` ºÍ `spring.main.*` £¬ËüÃÇÔÚˢпªÊ¼Ç°¾ÍÒѾ­±»¶ÁÈ¡ÁË¡£ + +3. ÅäÖÃÊý¾Ý£¨Èç `application.properties` Îļþ£©¡£ + +4. `RandomValuePropertySource`£¬ËüÖ»ÓÐ `random.*` ÊôÐÔ¡£ + +5. ²Ù×÷ϵͳ»·¾³±äÁ¿ + +6. Java System properties (`System.getProperties()`). + +7. `java:comp/env` ÖÐµÄ JNDI ÊôÐÔ¡£ + +8. `ServletContext` init parameters. + +9. `ServletConfig` init parameters. + +10. À´×Ô `SPRING_APPLICATION_JSON` µÄÊôÐÔ£¨Ç¶Èë»·¾³±äÁ¿»òϵͳÊôÐÔÖеÄÄÚÁªJSON£©¡£ + +11. ÃüÁîÐвÎÊý + +12. ÄãÔÚ²âÊÔÖÐµÄ `properties` ÊôÐÔ¡£ÔÚ [`@SpringBootTest`](https://docs.spring.io/spring-boot/docs/3.1.0-SNAPSHOT/api/org/springframework/boot/test/context/SpringBootTest.html) ºÍ²âÊÔ×¢½âÖпÉÓã¬[ÓÃÓÚ²âÊÔÄãµÄÓ¦ÓóÌÐòµÄÒ»¸öÌØ¶¨Æ¬¶Ï](https://springdoc.cn/spring-boot/features.html#features.testing.spring-boot-applications.autoconfigured-tests)¡£ + +13. Äã²âÊÔÖеÄhttps://docs.spring.io/spring-framework/docs/6.0.5/javadoc-api/org/springframework/test/context/TestPropertySource.html[`@TestPropertySource`] ×¢½â. + +14. µ±devtools´¦Óڻ״̬ʱ£¬`$HOME/.config/spring-boot` Ŀ¼ÏµÄ[DevtoolsÈ«¾ÖÉèÖÃÊôÐÔ](https://springdoc.cn/spring-boot/using.html#using.devtools.globalsettings)¡£ + + + + + +ÅäÖÃÊý¾ÝÎļþ°´ÒÔÏÂ˳Ðò¿¼ÂÇ¡£ + + + + + +1. ÔÚÄãµÄjarÖдò°üµÄ[Application properties](https://springdoc.cn/spring-boot/features.html#features.external-config.files)£¨application.properties ºÍ YAML£©¡£ + +2. ÔÚÄãµÄjarÖдò°üµÄ [ÌØ¶¨µÄ Profile application properties](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)£¨`application-{profile}.properties` ºÍ YAML£©¡£ + +3. ÔÚÄã´ò°üµÄjarÖ®ÍâµÄ[Application properties](https://springdoc.cn/spring-boot/features.html#features.external-config.files)ÐÔ£¨application.propertiesºÍYAML£©¡£ + +4. ÔÚÄã´ò°üµÄjarÖ®ÍâµÄ[ÌØ¶¨µÄ Profile application properties](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)£¨ `application-{profile}.properties` ºÍYAML£©¡£ + + + + + +| | ½¨ÒéÄãÔÚÕû¸öÓ¦ÓóÌÐòÖмá³ÖʹÓÃÒ»ÖÖ¸ñʽ¡£ Èç¹ûÄãÔÚͬһ¸öµØ·½ÓÐ `.properties` ºÍ `.yml` ¸ñʽµÄÅäÖÃÎļþ£¬`.properties` ÓÅÏÈ¡£ | +| --- | --- | + + + + + +ΪÁËÌṩһ¸ö¾ßÌåµÄÀý×Ó£¬¼ÙÉèÄ㿪·¢ÁËÒ»¸ö `@Component`£¬Ê¹ÓÃÁËÒ»¸ö `name` ÊôÐÔ£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Component +public class MyBean { + + @Value("${name}") + private String name; + + // ... + +} + +``` + + + + + + + +ÔÚÄãµÄÓ¦ÓóÌÐòµÄclasspath£¨ÀýÈ磬ÔÚÄãµÄjarÖУ©£¬Äã¿ÉÒÔÓÐÒ»¸ö `application.properties` Îļþ£¬Îª `name` Ìṩһ¸öºÏÀíµÄĬÈÏÊôÐÔÖµ¡£µ±ÔÚÒ»¸öÐµĻ·¾³ÖÐÔËÐÐʱ£¬¿ÉÒÔÔÚÄãµÄjarÖ®ÍâÌṩһ¸ö `application.properties` ÎļþÀ´¸²¸Ç `name` ¡£¶ÔÓÚÒ»´ÎÐԵIJâÊÔ£¬Äã¿ÉÒÔÓÃÒ»¸öÌØ¶¨µÄÃüÁîÐвÎÊýÀ´Æô¶¯£¨ÀýÈ磬`java -jar app.jar --name="Spring"`£©¡£ + + + + + +| | `env` ºÍ `configprops` ¶ËµãÔÚÈ·¶¨Ò»¸öÊôÐÔΪʲôÓÐÒ»¸öÌØ¶¨µÄֵʱ·Ç³£ÓÐÓá£Äã¿ÉÒÔʹÓÃÕâÁ½¸ö¶ËµãÀ´Õï¶ÏÒâÍâµÄÊôÐÔÖµ¡£Ïê¼û "[Éú²ú¾ÍÐ÷¹¦ÄÜ](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints)" ²¿·Ö¡£ | +| --- | --- | + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.command-line-args)2.1\. ·ÃÎÊÃüÁîÐÐÊôÐÔ + + + +ĬÈÏÇé¿öÏ£¬`SpringApplication` »á½«ÈκÎÃüÁîÐÐÑ¡Ïî²ÎÊý£¨¼´ÒÔ `--` ¿ªÍ·µÄ²ÎÊý£¬Èç `--server.port=9000` £©×ª»»Îª `property` ²¢½«ÆäÌí¼Óµ½Spring `Environment` ÖС£ ÈçǰËùÊö£¬ÃüÁîÐÐÊôÐÔ×ÜÊÇÓÅÏÈÓÚ»ùÓÚÎļþµÄÊôÐÔÔ´¡£ + + + + + +Èç¹ûÄ㲻ϣÍûÃüÁîÐÐÊôÐÔ±»Ìí¼Óµ½ `Environment` ÖУ¬Äã¿ÉÒÔͨ¹ý `SpringApplication.setAddCommandLineProperties(false)` ½ûÓÃËüÃÇ¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.application-json)2.2\. JSON Application Properties + + + +»·¾³±äÁ¿ºÍϵͳÊôÐÔÍùÍùÓÐÏÞÖÆ£¬ÕâÒâζ×ÅÓÐЩÊôÐÔÃû³Æ²»ÄÜʹÓᣠΪÁ˰ïÖú½â¾öÕâ¸öÎÊÌ⣬Spring BootÔÊÐíÄ㽫һ¸öÊôÐÔ¿é±àÂëΪһ¸öµ¥Ò»µÄJSON½á¹¹¡£ + + + + + +µ±ÄãµÄÓ¦ÓóÌÐòÆô¶¯Ê±£¬ÈκΠ`spring.application.json` »ò `SPRING_APPLICATION_JSON` ÊôÐÔ½«±»½âÎö²¢Ìí¼Óµ½ `Environment` ÖС£ + + + + + +ÀýÈ磬`SPRING_APPLICATION_JSON` ÊôÐÔ¿ÉÒÔÔÚ UN*X shell µÄÃüÁîÐÐÖÐ×÷Ϊ»·¾³±äÁ¿Ìṩ¡£ + + + + + + + +``` +$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar +``` + + + + + + + +ÔÚÇ°ÃæµÄÀý×ÓÖУ¬ÄãÔÚSpringµÄ `Environment` ÖÐ×îÖյõ½ÁË `my.name=test`¡£ + + + + + +ͬÑùµÄJSONÒ²¿ÉÒÔ×÷Ϊһ¸öϵͳÊôÐÔÌṩ¡£ + + + + + + + +``` +$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar +``` + + + + + + + +»òÕßÄã¿ÉÒÔͨ¹ýʹÓÃÒ»¸öÃüÁîÐвÎÊýÀ´ÌṩJSON¡£ + + + + + + + +``` +$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}' +``` + + + + + + + +Èç¹ûÄãÒª²¿Êðµ½Ò»¸ö¾­µäµÄÓ¦Ó÷þÎñÆ÷ÖУ¬ÄãÒ²¿ÉÒÔʹÓÃÒ»¸öÃûΪ `java:comp/env/spring.application.json` µÄJNDI±äÁ¿¡£ + + + + + +| | ¾¡¹ÜJSONÖÐµÄ `null` Öµ½«±»Ìí¼Óµ½Éú³ÉµÄÊôÐÔÔ´ÖУ¬µ« `PropertySourcesPropertyResolver` ½« `null` ÊôÐÔÊÓΪȱʧֵ¡£ ÕâÒâζ×ÅJSON²»ÄÜÓà `null` Öµ¸²¸ÇÀ´×Եͽ×ÊôÐÔÔ´µÄÊôÐÔ¡£ | +| --- | --- | + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files)2.3\. ÍⲿµÄ Application Properties + + + +µ±ÄãµÄÓ¦ÓóÌÐòÆô¶¯Ê±£¬Spring Boot»á×Ô¶¯´ÓÒÔÏÂλÖÃÕÒµ½²¢¼ÓÔØ `application.properties` ºÍ `application.yaml` Îļþ¡£ + + + + + +1. classpath + + + + 1. classpath ¸ù·¾¶ + + 2. classpath Ï嵀 `/config` °ü + + + +2. µ±Ç°Ä¿Â¼ + + + + 1. µ±Ç°Ä¿Â¼Ï + + 2. µ±Ç°Ä¿Â¼Ï嵀 `config/` ×ÓĿ¼ + + 3. `config/` ×ÓĿ¼µÄÖ±½Ó×ÓĿ¼ + + + + + + + +ÁÐ±í°´ÓÅÏȼ¶ÅÅÐò£¨½ÏµÍÏîÄ¿µÄÖµ¸²¸Ç½ÏÔçÏîÄ¿µÄÖµ£©¡£ ¼ÓÔØµÄÎļþ±»×÷Ϊ `PropertySources` Ìí¼Óµ½SpringµÄ `Environment` ÖС£ + + + + + +Èç¹ûÄ㲻ϲ»¶ `application` ×÷ΪÅäÖÃÎļþÃû³Æ£¬Äã¿ÉÒÔͨ¹ýÖ¸¶¨ `spring.config.name` »·¾³ÊôÐÔÇл»µ½ÁíÒ»¸öÎļþÃû³Æ¡£ ÀýÈ磬ΪÁËѰÕÒ `myproject.properties` ºÍ `myproject.yaml` Îļþ£¬Äã¿ÉÒÔ°´ÒÔÏ·½Ê½ÔËÐÐÄãµÄÓ¦ÓóÌÐò¡£ + + + + + + + +``` +$ java -jar myproject.jar --spring.config.name=myproject +``` + + + + + + + +ÄãÒ²¿ÉÒÔͨ¹ýʹÓà `spring.config.location` »·¾³ÊôÐÔÀ´ÒýÓÃÒ»¸öÃ÷È·µÄλÖᣠ¸ÃÊôÐÔ½ÓÊÜÒ»¸ö¶ººÅ·Ö¸ôµÄÁÐ±í£¬ÆäÖаüº¬Ò»¸ö»ò¶à¸öÒª¼ì²éµÄλÖᣠ+ + + + + +ÏÂÃæµÄÀý×ÓÏÔʾÁËÈçºÎÖ¸¶¨Á½¸ö²»Í¬µÄÎļþ¡£ + + + + + + + +``` +$ java -jar myproject.jar --spring.config.location=\ + optional:classpath:/default.properties,\ + optional:classpath:/override.properties +``` + + + + + + + +| | Èç¹û [ÅäÖÃÎļþÊÇ¿ÉÑ¡µÄ](https://springdoc.cn/spring-boot/features.html#features.external-config.files.optional-prefix)£¬²¢ÇÒ¿ÉÒÔÊDz»´æÔڵģ¬ÄÇôÇëʹÓà `optional:` ǰ׺¡£ | +| --- | --- | + + + + + +| | `spring.config.name`, `spring.config.location`, ºÍ `spring.config.extra-location` ºÜÔç¾ÍÓÃÀ´È·¶¨ÄÄЩÎļþ±ØÐë±»¼ÓÔØ¡£ ËüÃDZØÐë±»¶¨ÒåΪ»·¾³ÊôÐÔ£¨Í¨³£ÊDzÙ×÷ϵͳ»·¾³±äÁ¿£¬ÏµÍ³ÊôÐÔ£¬»òÃüÁîÐвÎÊý£©¡£ | +| --- | --- | + + + + + +Èç¹û `spring.config.location` °üº¬Ä¿Â¼£¨¶ø²»ÊÇÎļþ£©£¬ËüÃÇÓ¦¸ÃÒÔ `/` ½áβ¡£ ÔÚÔËÐÐʱ£¬ËüÃǽ«±»¸½¼ÓÉÏÓÉ `spring.config.name` Éú³ÉµÄÃû³Æ£¬È»ºó±»¼ÓÔØ¡£ ÔÚ `spring.config.location` ÖÐÖ¸¶¨µÄÎļþ±»Ö±½Óµ¼Èë¡£ + + + + + +| | Ŀ¼ºÍÎļþλÖÃÖµÒ²±»À©Õ¹£¬ÒÔ¼ì²é[ÌØ¶¨µÄÅäÖÃÎļþ](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)¡£ÀýÈ磬Èç¹ûÄãµÄ `spring.config.location` ÊÇ `classpath:myconfig.properties`£¬ÄãÒ²»á·¢ÏÖÊʵ±µÄ `classpath:myconfig-.properties` Îļþ±»¼ÓÔØ¡£ | +| --- | --- | + + + + + +ÔÚ´ó¶àÊýÇé¿öÏ£¬ÄãÌí¼ÓµÄÿ¸ö `spring.config.location` ÏÒýÓÃÒ»¸öÎļþ»òĿ¼¡£ λÖÃÊǰ´ÕÕËüÃDZ»¶¨ÒåµÄ˳ÐòÀ´´¦ÀíµÄ£¬ºóÃæµÄλÖÿÉÒÔ¸²¸ÇÇ°ÃæµÄλÖõÄÖµ¡£ + + + + + +Èç¹ûÄãÓÐÒ»¸ö¸´ÔÓµÄλÖÃÉèÖ㬶øÇÒÄãʹÓÃÌØ¶¨µÄÅäÖÃÎļþ£¬Äã¿ÉÄÜÐèÒªÌṩ½øÒ»²½µÄÌáʾ£¬ÒÔ±ãSpring BootÖªµÀËüÃÇÓ¦¸ÃÈçºÎ·Ö×é¡£Ò»¸öλÖÃ×éÊÇÒ»¸öλÖõļ¯ºÏ£¬ÕâЩλÖö¼±»ÈÏΪÊÇÔÚͬһ¼¶±ð¡£ÀýÈ磬Äã¿ÉÄÜÏë°ÑËùÓÐclasspathλÖ÷Ö×飬ȻºóÊÇËùÓÐÍⲿλÖá£Ò»¸öλÖÃ×éÄÚµÄÏîĿӦ¸ÃÓà `;` ·Ö¸ô¡£¸ü¶àϸ½Ú¼û ¡°[Ö¸¶¨ profile](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)¡± ²¿·ÖµÄÀý×Ó¡£ + + + + + +ͨ¹ýʹÓà `spring.config.location` ÅäÖõÄλÖÃÈ¡´úĬÈÏλÖᣠÀýÈ磬Èç¹û `spring.config.location` ±»ÅäÖÃΪ `optional:classpath:/custom-config/,optional:file:./custom-config/` £¬¿¼ÂǵÄÍêÕûλÖü¯ÈçÏ¡£ + + + + + +1. `optional:classpath:custom-config/` + +2. `optional:file:./custom-config/` + + + + + +Èç¹ûÄãϲ»¶Ìí¼Ó¶îÍâµÄλÖ㬶ø²»ÊÇÌæ»»ËüÃÇ£¬Äã¿ÉÒÔʹÓà `spring.config.extra-location` ¡£ ´Ó¸½¼ÓλÖüÓÔØµÄÊôÐÔ¿ÉÒÔ¸²¸ÇĬÈÏλÖõÄÊôÐÔ¡£ ÀýÈ磬Èç¹û `spring.config.extra-location` ±»ÅäÖÃΪ `optional:classpath:/custom-config/,optional:file:./custom-config/` £¬¿¼ÂǵÄÍêÕûλÖü¯ÈçÏ¡£ + + + + + +1. `optional:classpath:/;optional:classpath:/config/` + +2. `optional:file:./;optional:file:./config/;optional:file:./config/*/` + +3. `optional:classpath:custom-config/` + +4. `optional:file:./custom-config/` + + + + + +ÕâÖÖËÑË÷ÅÅÐòÈÃÄãÔÚÒ»¸öÅäÖÃÎļþÖÐÖ¸¶¨Ä¬ÈÏÖµ£¬È»ºóÔÚÁíÒ»¸öÎļþÖÐÑ¡ÔñÐԵظ²¸ÇÕâЩֵ¡£ Äã¿ÉÒÔÔÚÆäÖÐÒ»¸öĬÈÏλÖÃµÄ `application.properties` £¨»òÄãÓà `spring.config.name` Ñ¡ÔñµÄÆäËûbasename£©ÖÐΪÄãµÄÓ¦ÓóÌÐòÌṩĬÈÏÖµ¡£ È»ºó£¬ÕâЩĬÈÏÖµ¿ÉÒÔÔÚÔËÐÐʱ±»Î»ÓÚÆäÖÐÒ»¸ö×Ô¶¨ÒåλÖõIJ»Í¬Îļþ¸²¸Ç¡£ + + + + + +| | Èç¹ûÄãʹÓû·¾³±äÁ¿¶ø²»ÊÇϵͳÊôÐÔ£¬´ó¶àÊý²Ù×÷ϵͳ²»ÔÊÐíʹÓþäµã·Ö¸ôµÄ¼üÃû£¬µ«Äã¿ÉÒÔʹÓÃÏ»®Ïß´úÌæ£¨ÀýÈ磬 `SPRING_CONFIG_NAME` ¶ø²»ÊÇ `spring.config.name` £©¡£ ²Î¼û[´Ó»·¾³±äÁ¿°ó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables) Á˽âÏêÇé¡£ | +| --- | --- | + + + + + +| | Èç¹ûÄãµÄÓ¦ÓóÌÐòÔÚservletÈÝÆ÷»òÓ¦Ó÷þÎñÆ÷ÖÐÔËÐУ¬ÄÇôJNDIÊôÐÔ£¨ÔÚ `java:comp/env` ÖУ©»òservletÉÏÏÂÎijõʼ»¯²ÎÊý¿ÉÒÔ´úÌæ»·¾³±äÁ¿»òϵͳÊôÐÔ£¬»òÕßÓëÖ®Ò»Ñù¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.optional-prefix)2.3.1\. ¿ÉÑ¡µÄλÖÃ(Optional Locations) + + + +ĬÈÏÇé¿öÏ£¬µ±Ö¸¶¨µÄÅäÖÃÊý¾ÝλÖò»´æÔÚʱ£¬Spring Boot½«Å׳öÒ»¸ö `ConfigDataLocationNotFoundException` £¬ÄãµÄÓ¦ÓóÌÐò½«ÎÞ·¨Æô¶¯¡£ + + + + + +Èç¹ûÄãÏëÖ¸¶¨Ò»¸öλÖ㬵«Äã²»½éÒâËü²¢²»×ÜÊÇ´æÔÚ£¬Äã¿ÉÒÔʹÓà `optional:` ǰ׺¡£Äã¿ÉÒÔÔÚ `spring.config.locationºÍspring.config.extra-location` ÊôÐÔÖÐʹÓÃÕâ¸öǰ׺£¬Ò²¿ÉÒÔÔÚ [`spring.config.import`](https://springdoc.cn/spring-boot/features.html#features.external-config.files.importing) ÉùÃ÷ÖÐʹÓᣠ+ + + + + +ÀýÈ磬`spring.config.import` ֵΪ `optional:file:./myconfig.properties` ÔÊÐíÄãµÄÓ¦ÓóÌÐòÆô¶¯£¬¼´Ê¹ `myconfig.properties` Îļþ¶ªÊ§¡£ + + + + + +Èç¹ûÄãÏëºöÂÔËùÓÐµÄ `ConfigDataLocationNotFoundExceptions` ²¢Ê¼ÖÕ¼ÌÐøÆô¶¯ÄãµÄÓ¦ÓóÌÐò£¬Äã¿ÉÒÔʹÓà `spring.config.on-not-found` ÊôÐÔ¡£ ʹÓà `SpringApplication.setDefaultProperties(..)` »òʹÓÃϵͳ/»·¾³±äÁ¿½«ÆäÖµÉèÖÃΪ `ignore`¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.wildcard-locations)2.3.2\. ͨÅä·ûµØÖ· + + + +Èç¹ûÒ»¸öÅäÖÃÎļþµÄλÖÃÔÚ×îºóÒ»¸ö·¾¶¶ÎÖаüº¬ `*` ×Ö·û£¬Ëü¾Í±»ÈÏΪÊÇÒ»¸öͨÅä·ûλÖᣠͨÅä·ûÔÚ¼ÓÔØÅäÖÃʱ±»À©Õ¹£¬Òò´Ë£¬Ö±½ÓµÄ×ÓĿ¼Ҳ±»¼ì²é¡£ ͨÅä·ûλÖÃÔÚKubernetesÕâÖÖÓжà¸öÅäÖÃÊôÐÔµÄÀ´Ô´µÄ»·¾³ÖÐÌØ±ðÓÐÓᣠ+ + + + + +ÀýÈ磬Èç¹ûÄãÓÐһЩRedisÅäÖúÍһЩMySQLÅäÖã¬Äã¿ÉÄÜÏë°ÑÕâÁ½²¿·ÖÅäÖ÷ֿª£¬Í¬Ê±ÒªÇóÕâÁ½²¿·Ö¶¼´æÔÚÓÚÒ»¸ö `application.properties` ÎļþÖС£ + + + + + +Õâ¿ÉÄܻᵼÖÂÁ½¸ö¶ÀÁ¢µÄ `application.properties` Îļþ¹ÒÔØÔÚ²»Í¬µÄλÖã¬Èç `/config/redis/application.properties` ºÍ `/config/mysql/application.properties` ¡£ ÔÚÕâÖÖÇé¿öÏ£¬ÓÐÒ»¸öͨÅä·ûλÖà `config/*/` £¬½«µ¼ÖÂÁ½¸öÎļþ±»´¦Àí¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Spring Boot½« `config/*/` ÁÐÈëĬÈÏËÑË÷λÖᣠÕâÒâζ×ÅÄãµÄjarÖ®ÍâµÄ `/config` Ŀ¼µÄËùÓÐ×ÓĿ¼¶¼»á±»ËÑË÷µ½¡£ + + + + + +Äã¿ÉÒÔÔÚ `spring.config.location` ºÍ `spring.config.extra-location` ÊôÐÔÖÐʹÓÃͨÅä·ûλÖᣠ+ + + + + +| | ͨÅä·ûλÖñØÐëÖ»°üº¬Ò»¸ö `*` ²¢ÒÔ `*/` ½á⣬ÓÃÓÚËÑË÷ÊôÓÚĿ¼µÄλÖ㬻ò `*/` ÓÃÓÚËÑË÷ÊôÓÚÎļþµÄλÖᣠ´øÓÐͨÅä·ûµÄλÖý«¸ù¾ÝÎļþÃûµÄ¾ø¶Ô·¾¶°´×Öĸ˳ÐòÅÅÐò¡£ | +| --- | --- | + + + + + +| | ͨÅä·ûλÖÃÖ»¶ÔÍⲿĿ¼Æð×÷ÓᣠÄã²»ÄÜÔÚ `classpath:` λÖÃÖÐʹÓÃͨÅä·û¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)2.3.3\. ÌØ¶¨Îļþ£¨Profile Specific Files£© + + + +³ýÁË `application` ÊôÐÔÎļþ£¬Spring Boot»¹½«³¢ÊÔʹÓà `application-{profile}` µÄÃüÃû¹ßÀý¼ÓÔØprofileÌØ¶¨µÄÎļþ¡£ ÀýÈ磬Èç¹ûÄãµÄÓ¦ÓóÌÐò¼¤»îÁËÃûΪ `prod` µÄÅäÖÃÎļþ£¨`spring.profiles.active=prod`£©²¢Ê¹ÓÃYAMLÎļþ£¬ÄÇô `application.yml` ºÍ `application-prod.yml` ¶¼½«±»¿¼ÂÇ¡£ + + + + + +ÌØ¶¨Îļþ(`profiles`)µÄÊôÐÔÓë±ê×¼µÄ `application.properties` µÄλÖÃÏàͬ£¬Ìض¨Îļþ×ÜÊÇÓÅÏÈÓÚ·ÇÌØ¶¨Îļþ¡£ Èç¹ûÖ¸¶¨Á˼¸¸öÅäÖÃÎļþ£¬Ôò²ÉÓÃ×îºóʤ³öµÄ²ßÂÔ¡£ ÀýÈ磬Èç¹ûÅäÖÃÎļþ `prod,live` ÊÇÓÉ `spring.profiles.active` ÊôÐÔÖ¸¶¨µÄ£¬`application-prod.properties` ÖеÄÖµ¿ÉÒÔ±» `application-live.properties` ÖеÄÖµËù¸²¸Ç¡£ + + + + + +| | ×îºóʤ³öµÄ²ßÂÔÊÊÓÃÓÚ[location group](https://springdoc.cn/spring-boot/features.html#features.external-config.files.location-groups)¼¶±ð¡£ `spring.config.location` µÄ `classpath:/cfg/,classpath:/ext/` ½«²»»áÓÐÓë `classpath:/cfg/;classpath:/ext/` ÏàͬµÄ¸²¸Ç¹æÔò¡£ÀýÈ磬ÄÃÎÒÃÇÉÏÃæµÄ `prod,live` Àý×ÓÀ´Ëµ£¬ÎÒÃÇ¿ÉÄÜÓÐÒÔÏÂÎļþ¡£ /cfg application-live.properties/ext application-live.properties application-prod.properties µ±ÎÒÃÇÓÐÒ»¸ö `spring.config.location` Ϊ `classpath:/cfg/,classpath:/ext/` ʱ£¬ÎÒÃÇ»áÔÚËùÓÐ `/ext` Îļþ֮ǰ´¦ÀíËùÓÐ `/cfg` Îļþ¡£1. `/cfg/application-live.properties` 2. `/ext/application-prod.properties` 3. `/ext/application-live.properties` µ±ÎÒÃÇÓà `classpath:/cfg/;classpath:/ext/` ´úÌæÊ±£¨Óà `;` ·Ö¸ô·û£©£¬ÎÒÃÇÔÚͬһ¼¶±ð´¦Àí `/cfg` ºÍ `/ext` ¡£1. `/ext/application-prod.properties` 2. `/cfg/application-live.properties` 3. `/ext/application-live.properties` | +| --- | --- | + + + + + +`Environment` ÓÐÒ»×éĬÈϵÄÅäÖÃÎļþ£¨Ä¬ÈÏΪ `[default]` £©£¬Èç¹ûûÓÐÉèÖûµÄÅäÖÃÎļþ£¬¾Í»áʹÓÃÕâЩÅäÖÃÎļþ¡£ »»¾ä»°Ëµ£¬Èç¹ûûÓÐÃ÷È·¼¤»îµÄÅäÖÃÎļþ£¬ÄÇô¾Í»á¿¼ÂÇÀ´×Ô `application-default` µÄÊôÐÔ¡£ + + + + + +| | ÊôÐÔÎļþÖ»±»¼ÓÔØÒ»´Î¡£ Èç¹ûÄãÒѾ­Ö±½Ó[µ¼ÈëÁË](https://springdoc.cn/spring-boot/features.html#features.external-config.files.importing)Ò»¸öÅäÖÃÎļþµÄÌØ¶¨ÊôÐÔÎļþ£¬ÄÇôËü½«²»»á±»µÚ¶þ´Îµ¼Èë¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.importing)2.3.4\. µ¼Èë¶îÍâµÄÊý¾Ý + + + +application properties ÖпÉÒÔʹÓà `spring.config.import` ÊôÐÔ´ÓÆäËûµØ·½µ¼Èë¸ü¶àµÄÅäÖÃÊý¾Ý¡£ µ¼ÈëÔÚ±»·¢ÏÖʱ±»´¦Àí£¬²¢±»ÊÓΪ½ô½Ó×ÅÉùÃ÷µ¼ÈëµÄÎļþÏÂÃæ²åÈëµÄ¶îÍâÎļþ¡£ + + + + + +ÀýÈ磬Äã¿ÉÄÜÔÚÄãµÄ classpath `application.properties` ÎļþÖÐÓÐÒÔÏÂÄÚÈÝ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +spring.application.name=myapp +spring.config.import=optional:file:./dev.properties + +``` + + + + + + + +Õ⽫´¥·¢µ¼È뵱ǰĿ¼Ï嵀 `dev.properties` Îļþ£¨Èç¹û´æÔÚÕâÑùµÄÎļþ£©¡£ µ¼ÈëµÄ `dev.properties` ÖеÄÖµ½«ÓÅÏÈÓÚ´¥·¢µ¼ÈëµÄÎļþ¡£ ÔÚÉÏÃæµÄÀý×ÓÖУ¬`dev.properties` ¿ÉÒÔ½« `spring.application.name` ÖØÐ¶¨ÒåΪһ¸ö²»Í¬µÄÖµ¡£ + + + + + +Ò»¸öµ¼ÈëÖ»»á±»µ¼ÈëÒ»´Î£¬ÎÞÂÛËü±»ÉùÃ÷¶àÉٴΡ£ Ò»¸öµ¼ÈëÔÚproperties/yamlÎļþÄڵĵ¥¸öÎļþÖб»¶¨ÒåµÄ˳Ðò²¢²»ÖØÒª¡£ ÀýÈ磬ÏÂÃæµÄÁ½¸öÀý×Ó²úÉúÏàͬµÄ½á¹û¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +spring.config.import=my.properties +my.property=value + +``` + + + + + + + + + +Properties + +Yaml + + + + + +``` +my.property=value +spring.config.import=my.properties + +``` + + + + + + + +ÔÚÉÏÊöÁ½¸öÀý×ÓÖУ¬`my.properties` ÎļþµÄÖµ½«ÓÅÏÈÓÚ´¥·¢Æäµ¼ÈëµÄÎļþ¡£ + + + + + +ÔÚÒ»¸öµ¥Ò»µÄ `spring.config.import` ÊôÐÔÏ¿ÉÒÔÖ¸¶¨¶à¸öλÖᣠλÖý«°´ÕÕËüÃDZ»¶¨ÒåµÄ˳Ðò±»´¦Àí£¬ºóÀ´µÄµ¼È뽫±»ÓÅÏÈ´¦Àí¡£ + + + + + +| | ÔÚÊʵ±µÄʱºò£¬[ÌØ¶¨ÅäÖÃÎļþµÄ±äÌå](https://springdoc.cn/spring-boot/features.html#features.external-config.files.profile-specific)Ò²±»¿¼Âǵ¼Èë¡£ ÉÏÃæµÄÀý×Ó½«µ¼Èë `my.properties` ÒÔ¼°ÈκΠ`my-.properties` ±äÌå¡£ | +| --- | --- | + + + + + +| | Spring Boot ÌṩÁ˿ɲå°ÎµÄAPI£¨²å¼þ£©£¬ÔÊÐíÖ§³Ö¸÷ÖÖ²»Í¬µÄλÖõØÖ·¡£ ĬÈÏÇé¿öÏ£¬Äã¿ÉÒÔµ¼ÈëJava Properties¡¢YAMLºÍ ¡°[ÅäÖÃÊ÷](https://springdoc.cn/spring-boot/features.html#features.external-config.files.configtree)¡± ¡£µÚÈý·½jar¿ÉÒÔÌṩ¶ÔÆäËû¼¼ÊõµÄÖ§³Ö£¨²»ÒªÇó±ØÐëÊDZ¾µØÎļþ£©¡£ ÀýÈ磬Äã¿ÉÒÔÏëÏóÅäÖÃÊý¾ÝÀ´×ÔÍⲿ´æ´¢£¬ÈçConsul¡¢Apache ZooKeeper»òNetflix Archaius£¨°üÀ¨Nacos£©¡£Èç¹ûÄãÏëÖ§³ÖÄã×Ô¼ºµÄλÖã¨ÊµÏÖ×Ô¼º¶¨ÒåµÄÅäÖüÓÔØ£©£¬Çë²ÎÔÄ `org.springframework.boot.context.config` °üÖÐµÄ `ConfigDataLocationResolver` ºÍ `ConfigDataLoader` Àà¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.importing-extensionless)2.3.5\. µ¼ÈëÎÞÀ©Õ¹ÃûµÄÎļþ + + + +ÓÐÐ©ÔÆÆ½Ì¨²»ÄÜΪ¾í×°Îļþ£¨volume mounted files£©Ìí¼ÓÎļþÀ©Õ¹Ãû¡£ Òªµ¼ÈëÕâЩÎÞÀ©Õ¹ÃûµÄÎļþ£¬ÄãÐèÒª¸øSpring BootÒ»¸öÌáʾ£¬ÒÔ±ãËüÖªµÀÈçºÎ¼ÓÔØËüÃÇ¡£ Äã¿ÉÒÔͨ¹ý°ÑÀ©Õ¹ÃûÌáʾ·ÅÔÚ·½À¨ºÅÀïÀ´×öµ½ÕâÒ»µã¡£ + + + + + +ÀýÈ磬¼ÙÉèÄãÓÐÒ»¸ö `/etc/config/myconfig` Îļþ£¬ÄãÏ£ÍûÒÔyamlÐÎʽµ¼Èë¡£ Äã¿ÉÒÔÓÃÏÂÃæµÄ·½·¨´ÓÄãµÄ `application.properties` Öе¼ÈëËü¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +spring.config.import=file:/etc/config/myconfig[.yaml] + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.configtree)2.3.6\. ʹÓÃÅäÖÃÊ÷£¨Configuration Trees£© + + + +µ±ÔÚÔÆÆ½Ì¨£¨ÈçKubernetes£©ÉÏÔËÐÐÓ¦ÓóÌÐòʱ£¬Äã¾­³£ÐèÒª¶Áȡƽ̨ÌṩµÄÅäÖÃÖµ¡£ ½«»·¾³±äÁ¿ÓÃÓÚ´ËÀàÄ¿µÄ²¢²»ÉÙ¼û£¬µ«Õâ¿ÉÄÜÓÐȱµã£¬ÌرðÊÇÈç¹û¸ÃÖµÊÇ secret µÄ¡£ + + + + + +×÷Ϊ»·¾³±äÁ¿µÄÌæ´ú·½°¸£¬Ðí¶àÔÆÆ½Ì¨ÏÖÔÚÔÊÐíÄ㽫ÅäÖÃÓ³Éäµ½¹ÒÔØµÄÊý¾Ý¾í¡£ ÀýÈ磬Kubernetes ¿ÉÒÔ¾í¹ÒÔØ [`ConfigMaps`](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap) ºÍ [`Secrets`](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-files-from-a-pod)¡£ + + + + + +¿ÉÒÔʹÓÃÁ½ÖÖ³£¼ûµÄ volume ¹ÒÔØÄ£Ê½£º + + + + + +1. Ò»¸öÎļþ°üº¬Ò»Ì×ÍêÕûµÄÊôÐÔ£¨Í¨³£Ð´³ÉYAML£©¡£ + +2. ¶à¸öÎļþ±»Ð´ÈëÒ»¸öĿ¼Ê÷ÖУ¬ÎļþÃû³ÉΪ ¡®key¡¯£¬ÄÚÈݳÉΪ ¡®value¡¯¡£ + + + + + +¶ÔÓÚµÚÒ»ÖÖÇé¿ö£¬Äã¿ÉÒÔʹÓà `spring.config.import` Ö±½Óµ¼ÈëYAML»òÊôÐÔÎļþ£¬[ÈçÉÏËùÊö](https://springdoc.cn/spring-boot/features.html#features.external-config.files.importing)¡£ ¶ÔÓÚµÚ¶þÖÖÇé¿ö£¬ÄãÐèҪʹÓà `configtree:` ǰ׺£¬ÒÔ±ãSpring BootÖªµÀËüÐèÒª½«ËùÓÐÎļþ×÷ΪÊôÐÔ¹«¿ª¡£ + + + + + +¾Ù¸öÀý×Ó£¬ÈÃÎÒÃÇÏëÏóһϣ¬KubernetesÒѾ­¹ÒÔØÁËÒÔÏÂvolume¡£ + + + + + + + + etc/ + config/ + myapp/ + username + password + + + + + + + +`username` ÎļþµÄÄÚÈݽ«ÊÇÒ»¸öÅäÖÃÖµ£¬¶ø `password` µÄÄÚÈݽ«ÊÇÒ»¸ö secret¡£ + + + + + +Òªµ¼ÈëÕâЩÊôÐÔ£¬Äã¿ÉÒÔÔÚÄãµÄ `application.properties` »ò `application.yaml` ÎļþÖÐÌí¼ÓÒÔÏÂÄÚÈÝ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +spring.config.import=optional:configtree:/etc/config/ + +``` + + + + + + + +È»ºóÄã¿ÉÒÔ´Ó `Environment` ÖÐÒÔ³£¹æ·½Ê½·ÃÎÊ»ò×¢Èë `myapp.username` ºÍ `myapp.password` ÊôÐÔ¡£ + + + + + +| | ÅäÖÃÊ÷ϵÄÎļþ¼Ð¹¹³ÉÁËÊôÐÔÃû³Æ¡£ ÔÚÉÏÃæµÄÀý×ÓÖУ¬ÎªÁË·ÃÎÊÊôÐÔΪ `username` ºÍ `password`£¬Äã¿ÉÒÔ½« `spring.config.import` ÉèÖÃΪ `optional:configtree:/etc/config/myapp` ¡£ | +| --- | --- | + + + + + +| | ´øÓеã·ûºÅµÄÎļþÃûÒ²»á±»ÕýÈ·Ó³Éä¡£ ÀýÈ磬ÔÚÉÏÃæµÄÀý×ÓÖУ¬`/etc/config` ÖÐÃûΪ `myapp.username` µÄÎļþÔÚ `Environment` ÖеÄÊôÐÔÃû½«»áÊÇ `myapp.username` ¡£ | +| --- | --- | + + + + + +| | ÅäÖÃÊ÷µÄÖµ¿ÉÒÔ±»°ó¶¨µ½×Ö·û´® `String` ºÍ `byte[]` ÀàÐÍ£¬ÕâÈ¡¾öÓÚÔ¤ÆÚµÄÄÚÈÝ¡£ | +| --- | --- | + + + + + +Èç¹ûÄãÓжà¸öÅäÖÃÊ÷Òª´Óͬһ¸ö¸¸Îļþ¼Ðµ¼È룬Äã¿ÉÒÔʹÓÃͨÅä·û¿ì½Ý·½Ê½¡£ ÈκÎÒÔ `/*/` ½áβµÄ `configtree:` λÖý«µ¼ÈëËùÓÐÖ±½ÓµÄ×ÓÎļþ¼Ð×÷ΪÅäÖÃÊ÷¡£ + + + + + +ÀýÈ磬¸ø¶¨ÒÔÏÂvolume£º + + + + + + + + etc/ + config/ + dbconfig/ + db/ + username + password + mqconfig/ + mq/ + username + password + + + + + + + +Äã¿ÉÒÔʹÓà `configtree:/etc/config/*/` ×÷Ϊµ¼ÈëλÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +spring.config.import=optional:configtree:/etc/config/*/ + +``` + + + + + + + +Õ⽫Ìí¼Ó `db.username`¡¢`db.password`¡¢`mq.username` ºÍ `mq.password` ÊôÐÔ¡£ + + + + + +| | ʹÓÃͨÅä·û¼ÓÔØµÄĿ¼Êǰ´×Öĸ˳ÐòÅÅÁеġ£ Èç¹ûÄãÐèÒªÒ»¸ö²»Í¬µÄ˳Ðò£¬ÄÇôÄãÓ¦¸Ã°Ñÿ¸öλÖÃ×÷Ϊһ¸öµ¥¶ÀµÄµ¼ÈëÁгö¡£ | +| --- | --- | + + + + + +ÅäÖÃÊ÷Ò²¿ÉÓÃÓÚDocker secret¡£ µ±Docker swarm·þÎñ±»ÊÚÓè¶ÔsecretµÄ·ÃÎÊȨʱ£¬¸Ãsecret»á±»×°Ôص½ÈÝÆ÷ÖС£ ÀýÈ磬Èç¹ûÒ»¸öÃûΪ `db.password` µÄsecret¡£±»¹ÒÔØÔÚ `/run/secrets/` µÄλÖã¬Äã¿ÉÒÔÓÃÒÔÏ·½·¨Èà `db.password` ¶ÔSpring»·¾³¿ÉÓᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +spring.config.import=optional:configtree:/run/secrets/ + +``` + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.property-placeholders)2.3.7\. ÊôÐÔռλ·û + + + +`application.properties` ºÍ `application.yml` ÖеÄÖµÔÚʹÓÃʱͨ¹ýÏÖÓÐµÄ `Environment` ¹ýÂË£¬ËùÒÔÄã¿ÉÒԲο¼ÒÔǰ¶¨ÒåµÄÖµ£¨ÀýÈ磬À´×ÔϵͳÊôÐÔ»ò»·¾³±äÁ¿£©¡£ ±ê×¼µÄ `${name}` ÊôÐÔռλ·ûÓï·¨¿ÉÒÔÓÃÔÚÒ»¸öÖµµÄÈκεط½¡£ ÊôÐÔռλ·ûÒ²¿ÉÒÔÖ¸¶¨Ò»¸öĬÈÏÖµ£¬Ê¹Óà `:` À´·Ö¸ôĬÈÏÖµºÍÊôÐÔÃû³Æ£¬ÀýÈç `${name:default}` ¡£ + + + + + +ÏÂÃæµÄÀý×ÓÏÔʾÁË´øÄ¬ÈÏÖµºÍ²»´øÄ¬ÈÏÖµµÄռλ·ûµÄʹÓÃÇé¿ö¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +app.name=MyApp +app.description=${app.name} is a Spring Boot application written by ${username:Unknown} + +``` + + + + + + + +¼ÙÉè `username` ÊôÐÔûÓÐÔÚÆäËûµØ·½ÉèÖã¬`app.description` µÄÖµ½«ÊÇ `MyApp is a Spring Boot application written by Unknown`¡£ + + + + + +| | ÄãÓ¦¸ÃʼÖÕʹÓÃռλ·ûÖеÄÊôÐÔÃû³ÆµÄ¹æ·¶ÐÎʽ£¨½öʹÓÃСд×ÖĸµÄkebab-case£©À´ÒýÓÃËüÃÇ¡£ Õ⽫ÔÊÐíSpring BootʹÓÃÓë[¿íËɰó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding) `@ConfigurationProperties` ʱÏàͬµÄÂß¼­¡£ÀýÈ磬`${demo.item-price}` ½«´Ó `application.properties` ÎļþÖлñÈ¡ `demo.item-price` ºÍ `demo.itemPrice` ÐÎʽµÄÊôÐÔ£¬ÒÔ¼°´Óϵͳ»·¾³ÖлñÈ¡ `DEMO_ITEMPRICE` ¡£ Èç¹ûÄãÓà `${demo.itemPrice}` µÄ»°£¬ `demo.item-price` ºÍ `DEMO_ITEMPRICE` ¾Í²»»á±»¿¼ÂÇ¡£ | +| --- | --- | + + + + + +| | ÄãÒ²¿ÉÒÔʹÓÃÕâÖÖ¼¼ÊõÀ´´´½¨ÏÖÓÐSpring BootÊôÐ﵀ ¡°short¡± ±äÌå¡£ ÏêÇéÇë²Î¼û_[howto.html](https://springdoc.cn/spring-boot/howto.html#howto.properties-and-configuration.short-command-line-arguments)_µÄ·½·¨¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.multi-document)2.3.8\. ʹÓöàÎĵµÎļþ£¨Working with Multi-Document Files£© + + + +Spring BootÔÊÐíÄ㽫һ¸öÎïÀíÎļþ·Ö³É¶à¸öÂß¼­Îļþ£¬Ã¿¸öÎļþ¶¼ÊǶÀÁ¢Ìí¼ÓµÄ¡£ ÎļþÊǰ´Ë³Ðò´¦ÀíµÄ£¬´ÓÉϵ½Ï¡£ ºóÃæµÄÎļþ¿ÉÒÔ¸²¸ÇÇ°ÃæÎļþÖж¨ÒåµÄÊôÐÔ¡£ + + + + + +¶ÔÓÚ `application.yml` Îļþ£¬Ê¹Óñê×¼µÄYAML¶àÎĵµÓï·¨¡£ Èý¸öÁ¬ÐøµÄÁ¬×Ö·û£¨`---`£©´ú±íÒ»¸öÎļþµÄ½áÊø£¬ºÍÏÂÒ»¸öÎļþµÄ¿ªÊ¼¡£ + + + + + +ÀýÈ磬ÏÂÃæµÄÎļþÓÐÁ½¸öÂß¼­Îĵµ¡£ + + + + + + + +``` +spring: + application: + name: "MyApp" +--- +spring: + application: + name: "MyCloudApp" + config: + activate: + on-cloud-platform: "kubernetes" +``` + + + + + + + +¶ÔÓÚ `application.properties` Îļþ£¬Ò»¸öÌØÊâµÄ `#---` »ò `!---` ×¢Êͱ»ÓÃÀ´±ê¼ÇÎļþµÄ·Ö¸î¡£ + + + + + + + +``` +spring.application.name=MyApp +#--- +spring.application.name=MyCloudApp +spring.config.activate.on-cloud-platform=kubernetes +``` + + + + + + + +| | properties ÎļþµÄ·Ö¸ô·û²»ÄÜÓÐÈκÎǰµ¼¿Õ°×£¬²¢ÇÒ±ØÐëÕýºÃÓÐÈý¸öÁ¬×Ö·û¡£ ·Ö¸ô·ûµÄǰºóÁ½Ðв»ÄÜÊÇÏàͬµÄ×¢ÊÍǰ׺¡£ | +| --- | --- | + + + + + +| | ¶àÎĵµÊôÐÔÎļþͨ³£Ó뼤»îÊôÐÔÒ»ÆðʹÓã¬Èç `spring.config.activated.on-profile`¡£ Ïê¼û[ÏÂÒ»½Ú](https://springdoc.cn/spring-boot/features.html#features.external-config.files.activation-properties)¡£ | +| --- | --- | + + + + + +| | ¶àÎĵµÊôÐÔÎļþ²»ÄÜͨ¹ýʹÓà `@PropertySource` »ò `@TestPropertySource` ×¢½â¼ÓÔØ¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.files.activation-properties)2.3.9\. ¼¤»îÊôÐÔ£¨Activation Properties£© + + + +ÓÐʱ£¬Ö»ÔÚÂú×ãijЩÌõ¼þʱ¼¤»îÒ»×éÌØ¶¨µÄÊôÐÔÊǺÜÓÐÓõġ£ ÀýÈ磬Äã¿ÉÄÜÓÐһЩÊôÐÔÖ»ÓÐÔÚÌØ¶¨µÄÅäÖÃÎļþ±»¼¤»îʱ²ÅÏà¹Ø¡£ + + + + + +Äã¿ÉÒÔʹÓà `spring.config.activation.*` ÓÐÌõ¼þµØ¼¤»îÒ»¸öÊôÐÔÎļþ¡£ + + + + + +¼¤»îÊôÐÔÓÐÈçÏ¡£ + + + +Table 2\. activation properties +| ÊôÐÔ | ˵Ã÷ | +| --- | --- | +| `on-profile` | Ò»¸ö±ØÐëÓë֮ƥÅäµÄÅäÖÃÎļþ±í´ïʽ£¬ÒÔʹÎļþ´¦Óڻ״̬£¨¼¤»îÖ¸¶¨µÄÅäÖÃÎļþʱÓÐЧ£©¡£ | +| `on-cloud-platform` | ±ØÐë¼ì²âµ½µÄ `CloudPlatform`£¬ÒÔʹÎļþ´¦Óڻ״̬¡££¨ÔÆÆ½Ì¨×´Ì¬ÏÂÓÐЧ£© | + + + +ÀýÈ磬ÏÂÃæÖ¸¶¨µÚ¶þ¸öÎļþÖ»ÓÐÔÚKubernetesÉÏÔËÐÐʱ²ÅÓÐЧ£¬²¢ÇÒÖ»ÓÐÔÚ ¡°prod¡± »ò ¡°staging¡± ÅäÖÃÎļþ´¦Óڻ״̬ʱ²ÅÓÐЧ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +myprop=always-set +#--- +spring.config.activate.on-cloud-platform=kubernetes +spring.config.activate.on-profile=prod | staging +myotherprop=sometimes-set + +``` + + + + + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.encrypting)2.4\. ¼ÓÃÜÅäÖÃÊôÐÔ£¨Encrypting Properties£© + + + +Spring BootûÓÐΪ¼ÓÃÜÊôÐÔÖµÌṩÈκÎÄÚÖÃÖ§³Ö£¬µ«ËüÌṩÁËHookm£¬¿ÉÒÔÓÃÀ´ÐÞ¸ÄSpring `Environment` Öаüº¬µÄÖµ¡£ `EnvironmentPostProcessor` ½Ó¿ÚÔÊÐíÄãÔÚÓ¦ÓóÌÐòÆô¶¯Ç°²Ù×÷ `Environment`¡£ ²Î¼û[howto.html](https://springdoc.cn/spring-boot/howto.html#howto.application.customize-the-environment-or-application-context)ÒÔÁ˽âÏêÇé¡£ + + + + + +Èç¹ûÄãÐèÒªÒ»ÖÖ°²È«µÄ·½Ê½À´´æ´¢Æ¾Ö¤ºÍÃÜÂ룬 [Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/) ÏîÄ¿ÌṩÁ˶ÔÔÚ [HashiCorp Vault](https://www.vaultproject.io/)Öд洢Íⲿ»¯ÅäÖõÄÖ§³Ö¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.yaml)2.5\. ʹÓà YAML + + + +[YAML](https://yaml.org/) ÊÇJSONµÄ³¬¼¯£¬Òò´ËÊÇÖ¸¶¨·Ö²ãÅäÖÃÊý¾ÝµÄ·½±ã¸ñʽ¡£ Ö»ÒªÄãµÄclasspathÉÏÓÐ [SnakeYAML](https://github.com/snakeyaml/snakeyaml) ¿â£¬`SpringApplication` Àà¾Í»á×Ô¶¯Ö§³ÖYAML×÷ΪpropertiesµÄÌæ´úÆ·¡£ + + + + + +| | Èç¹ûÄãʹÓà ¡°Starter¡±£¬SnakeYAML½«ÓÉ `spring-boot-starter` ×Ô¶¯Ìṩ¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.yaml.mapping-to-properties)2.5.1\. ½«YAMLÓ³Éäµ½Properties + + + +YAML ÎĵµÐèÒª´ÓÆä·Ö²ã¸ñʽת»»Îª¿ÉÓë Spring `Environment` Ò»ÆðʹÓÃµÄ±âÆ½½á¹¹¡£ ÀýÈ磬¿¼ÂÇÏÂÃæÕâ¸öYAMLÎĵµ¡£ + + + + + + + +``` +environments: + dev: + url: "https://dev.example.com" + name: "Developer Setup" + prod: + url: "https://another.example.com" + name: "My Cool App" +``` + + + + + + + +ΪÁË´Ó `Environment` ÖзÃÎÊÕâЩÊôÐÔ£¬ËüÃǽ«±»±âƽ»¯£¬ÈçÏÂËùʾ¡£ + + + + + + + +``` +environments.dev.url=https://dev.example.com +environments.dev.name=Developer Setup +environments.prod.url=https://another.example.com +environments.prod.name=My Cool App +``` + + + + + + + +ͬÑùµØ£¬YAMLÖеÄÁбíÒ²ÐèÒª½øÐÐ±âÆ½»¯´¦Àí¡£ ËüÃDZ»±íʾΪ´øÓÐ `[index]` Ë÷ÒýµÄkey¡£ ÀýÈ磬¿¼ÂÇÏÂÃæµÄYAML¡£ + + + + + + + +``` +my: + servers: + - "dev.example.com" + - "another.example.com" +``` + + + + + + + +Ç°ÃæµÄÀý×Ó½«±»×ª»¯ÎªÈçÏÂÊôÐÔ¡£ + + + + + + + +``` +my.servers[0]=dev.example.com +my.servers[1]=another.example.com +``` + + + + + + + +| | ʹÓà `[index]` ·ûºÅµÄÊôÐÔ¿ÉÒÔʹÓÃSpring BootµÄ `Binder` Àà°ó¶¨µ½Java `List` »ò `Set` ¶ÔÏó¡£ ¸ü¶àϸ½Ú¼ûÏÂÃæµÄ ¡°[ÀàÐͰ²È«µÄÅäÖÃÊôÐÔ](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties)¡± ²¿·Ö¡£ | +| --- | --- | + + + + + +| | YAMLÎļþ²»ÄÜͨ¹ýʹÓà `@PropertySource` »ò `@TestPropertySource` ×¢½âÀ´¼ÓÔØ¡£ ËùÒÔ£¬ÔÚÄãÐèÒªÒÔÕâÖÖ·½Ê½¼ÓÔØÖµµÄÇé¿öÏ£¬ÄãÐèҪʹÓÃÒ»¸ö properties Îļþ¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.yaml.directly-loading)2.5.2\. Ö±½Ó¼ÓÔØYAML + + + +Spring FrameworkÌṩÁËÁ½¸ö·½±ãµÄÀ࣬¿ÉÒÔÓÃÀ´¼ÓÔØYAMLÎĵµ¡£ `YamlPropertiesFactoryBean` ½«YAML×÷Ϊ `Properties` ¼ÓÔØ£¬`YamlMapFactoryBean` ½«YAML×÷Ϊ `Map` ¼ÓÔØ¡£ + + + + + +Èç¹ûÄãÏë°ÑYAML¼ÓÔØÎªSpringµÄ `PropertySource` £¬ÄãÒ²¿ÉÒÔʹÓà `YamlPropertySourceLoader` Àà¡£ + + + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.random-values)2.6\. ÅäÖÃËæ»úÖµ + + + +The `RandomValuePropertySource` is useful for injecting random values (for example, into secrets or test cases). It can produce integers, longs, uuids, or strings, as shown in the following example: + + + + + +`RandomValuePropertySource` ¶ÔÓÚ×¢ÈëËæ»úÖµºÜÓÐÓã¨ÀýÈ磬עÈëÃÜÂë»ò²âÊÔ°¸Àý£©¡£ Ëü¿ÉÒÔ²úÉúInteger¡¢Long¡¢UUID£¬»òString£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +my.secret=${random.value} +my.number=${random.int} +my.bignumber=${random.long} +my.uuid=${random.uuid} +my.number-less-than-ten=${random.int(10)} +my.number-in-range=${random.int[1024,65536]} + +``` + + + + + + + +`random.int*` µÄÓï·¨ÊÇ `OPEN value (,max) CLOSE`£¬ÆäÖÐ `OPEN,CLOSE` ÊÇÈκÎ×Ö·û£¬ `value,max` ÊÇÕûÊý¡£ Èç¹ûÌṩÁË `max`£¬ÄÇô `value` ÊÇ×îСֵ£¬ `max` ÊÇ×î´óÖµ£¨¶ÀÕ¼£©¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.system-environment)2.7\. ÅäÖÃϵͳ»·¾³ÊôÐÔ + + + +Spring BootÖ§³ÖΪ»·¾³ÊôÐÔÉèÖÃÒ»¸öǰ׺¡£ Èç¹ûϵͳ»·¾³±»¶à¸ö¾ßÓв»Í¬ÅäÖÃÒªÇóµÄSpring BootÓ¦ÓóÌÐò¹²Ïí£¬Õâ¾ÍºÜÓÐÓᣠϵͳ»·¾³ÊôÐÔµÄǰ׺¿ÉÒÔÖ±½ÓÔÚ `SpringApplication` ÉÏÉèÖᣠ+ + + + + +ÀýÈ磬Èç¹ûÄ㽫ǰ׺ÉèÖÃΪ `input` £¬ÖîÈç `remote.timeout` ÕâÑùµÄÊôÐÔÔÚϵͳ»·¾³ÖÐÒ²½«±»½âÎöΪ `input.remote.timeout`¡£ + + + + + + + +### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties)2.8\. ÀàÐͰ²È«µÄÅäÖÃÊôÐÔ + + + +ʹÓà `@Value("${property}")` ×¢½âÀ´×¢ÈëÅäÖÃÊôÐÔÓÐʱ»áºÜÂé·³£¬ÌرðÊǵ±ÄãÒª´¦Àí¶à¸öÊôÐÔ»òÄãµÄÊý¾ÝÊÇ·Ö²ãµÄ¡£ Spring BootÌṩÁËÒ»ÖÖ´¦ÀíÊôÐÔµÄÌæ´ú·½·¨£¬ÈÃÇ¿ÀàÐ͵ÄBean¹ÜÀíºÍÑéÖ¤ÄãµÄÓ¦ÓóÌÐòµÄÅäÖᣠ+ + + + + +| | ÁíÇë²Î¼û[`@Value` ºÍÀàÐͰ²È«ÅäÖÃÊôÐÔÖ®¼äµÄÇø±ð](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.vs-value-annotation)¡£ | +| --- | --- | + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.java-bean-binding)2.8.1\. JavaBean ÊôÐÔ°ó¶¨ + + + +ÈçÏÂÃæµÄÀý×ÓËùʾ£¬¿ÉÒÔ°ó¶¨Ò»¸öÉùÃ÷Á˱ê×¼JavaBeanÊôÐÔµÄbean¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my.service") +public class MyProperties { + + private boolean enabled; + + private InetAddress remoteAddress; + + private final Security security = new Security(); + + // getters / setters... + + public static class Security { + + private String username; + + private String password; + + private List roles = new ArrayList<>(Collections.singleton("USER")); + + // getters / setters... + + } + +} + +``` + + + + + + + +Ç°ÃæµÄPOJO¶¨ÒåÁËÒÔÏÂÊôÐÔ¡£ + + + + + +* `my.service.enabled`£¬Ä¬ÈÏֵΪ`false`¡£ + +* `my.service.remote-address`£¬ÆäÀàÐÍ¿ÉÓÉ`String`Ç¿ÖÆÌṩ¡£ + +* `my.service.security.username`£¬ÓÐÒ»¸öǶÌ×µÄ `security` ¶ÔÏ󣬯äÃû³ÆÓɸÃÊôÐÔµÄÃû³Æ¾ö¶¨¡£ ÌØ±ðÊÇ£¬ÄÇÀïÍêȫûÓÐʹÓÃÀàÐÍ£¬¿ÉÒÔÊÇ `SecurityProperties`¡£ + +* `my.service.security.password`. + +* `my.service.security.role`£¬ÓÐÒ»¸ö `String` µÄ¼¯ºÏ£¬Ä¬ÈÏΪ `USER`¡£ + + + + + +| | Ó³Éäµ½Spring BootÖпÉÓÃµÄ `@ConfigurationProperties` ÀàµÄÊôÐÔ£¬Í¨¹ýpropertiesÎļþ¡¢YAMLÎļþ¡¢»·¾³±äÁ¿ºÍÆäËû»úÖÆ½øÐÐÅäÖã¬ÕâЩÊôÐÔÊǹ«¹²API£¬µ«Àà±¾ÉíµÄ getters/setters ²¢²»Òâζ×Å¿ÉÒÔÖ±½ÓʹÓã¨Ò»¾ä»°£¬SpringÒ²ÊÇͨ¹ýgetter/setterÕâЩpublic·½·¨½øÐÐÉèÖÃÖµµÄ£¬Äã±ðÓã©¡£ | +| --- | --- | + + + + + +| | ÕâÑùµÄÉè¼ÆÒÀÀµÓÚÒ»¸öĬÈϵÄÎ޲ι¹Ô캯Êý£¬getterºÍsetterͨ³£ÊDZØÐëµÄ£¬ÒòΪ°ó¶¨ÊÇͨ¹ý±ê×¼µÄJava Beans property descriptor£¨JavaÄÚÊ¡£©ÊµÏֵ쬾ÍÏñÔÚSpring MVCÖÐÒ»Ñù¡£ ÔÚÒÔÏÂÇé¿öÏ£¬¿ÉÒÔÊ¡ÂÔsetter¡£* Map, Ö»ÒªËüÃDZ»³õʼ»¯£¬¾ÍÐèÒªÒ»¸ögetter£¬µ«²»Ò»¶¨ÐèÒªÒ»¸ösetter£¬ÒòΪËüÃÇ¿ÉÒÔ±»°ó¶¨Æ÷Í»±ä¡£ * CollectionºÍarray ¿ÉÒÔͨ¹ýË÷Òý£¨Í¨³£ÓÃYAML£©»òʹÓõ¥¸ö¶ººÅ·Ö¸ôµÄÖµ£¨ÊôÐÔ£©À´·ÃÎÊ¡£ ÔÚºóÒ»ÖÖÇé¿öÏ£¬Ò»¸ösetterÊDZØÐëµÄ¡£ ÎÒÃǽ¨Òé×ÜÊÇΪÕâÀàÀàÐÍÌí¼ÓÒ»¸ösetter¡£ Èç¹ûÄã³õʼ»¯Ò»¸ö¼¯ºÏ£¬È·±£Ëü²»ÊDz»¿É±äµÄ£¨ÈçÇ°ÃæµÄÀý×Ó£©¡£ * Èç¹ûǶÌ×µÄPOJOÊôÐÔ±»³õʼ»¯£¨¾ÍÏñÇ°ÃæÀý×ÓÖÐµÄ `Security` ×ֶΣ©£¬¾Í²»ÐèÒªsetter¡£ Èç¹ûÄãÏëÈÃ°ó¶¨Æ÷ͨ¹ýʹÓÃËüµÄĬÈϹ¹Ô캯ÊýÀ´¼´Ê±´´½¨ÊµÀý£¬ÄãÐèÒªÒ»¸ösetter¡£ ÓÐЩÈËʹÓÃProject LombokÀ´×Ô¶¯Ìí¼ÓgetterºÍsetter¡£ ÇëÈ·±£Lombok²»»áΪÕâÑùµÄÀàÐÍÉú³ÉÈκÎÌØ¶¨µÄ¹¹Ô캯Êý£¬ÒòΪËü±»ÈÝÆ÷×Ô¶¯ÓÃÀ´ÊµÀý»¯¶ÔÏó¡£×îºó£¬Ö»¿¼ÂDZê×¼µÄJava BeanÊôÐÔ£¬²»Ö§³Ö¶Ô¾²Ì¬ÊôÐԵİ󶨡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.constructor-binding)2.8.2\. ¹¹Ô캯Êý°ó¶¨ + + + +ÉÏÒ»½ÚµÄÀý×Ó¿ÉÒÔÓò»¿É±äµÄ·½Ê½ÖØÐ´£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my.service") +public class MyProperties { + + // fields... + + public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) { + this.enabled = enabled; + this.remoteAddress = remoteAddress; + this.security = security; + } + + // getters... + + public static class Security { + + // fields... + + public Security(String username, String password, @DefaultValue("USER") List roles) { + this.username = username; + this.password = password; + this.roles = roles; + } + + // getters... + + } + +} + +``` + + + + + + + +ÔÚÕâÖÖÉèÖÃÖУ¬Î¨Ò»µÄ¡°´ø²ÎÊý¹¹Ô캯Êý¡±µÄ´æÔÚÒâζ×ÅÓ¦¸ÃʹÓøù¹Ô캯Êý½øÐа󶨡£ ÕâÒâζ×Ű󶍯÷»áÕÒµ½Ò»¸ö´øÓÐÄãÏ£Íû°ó¶¨µÄ²ÎÊýµÄ¹¹Ô캯Êý¡£ Èç¹ûÄãµÄÀàÓжà¸ö¹¹Ô캯Êý£¬¿ÉÒÔʹÓà `@ConstructorBinding` ×¢½âÀ´Ö¸¶¨Ê¹ÓÃÄĸö¹¹Ô캯Êý½øÐй¹Ô캯Êý°ó¶¨¡£ Èç¹ûҪΪһ¸öÖ»ÓÐÒ»¸ö¡°´ø²ÎÊý¹¹Ô캯Êý¡±µÄÀàÑ¡Ôñ²»°ó¶¨¹¹Ô캯Êý£¬¸Ã¹¹Ô캯Êý±ØÐëÓà `@Autowired` À´×¢½â¡£ ¹¹Ô캯Êý°ó¶¨¿ÉÒÔÓë `Record` Ò»ÆðʹÓᣠ³ý·ÇÄãµÄ¼Ç¼Óжà¸ö¹¹Ô캯Êý£¬·ñÔòûÓбØÒªÊ¹Óà `@ConstructorBinding`¡£ + + + + + +¹¹Ô캯Êý°ó¶¨ÀàµÄǶÌ׳ÉÔ±£¨ÈçÉÏÃæÀý×ÓÖÐµÄ `Security`£©Ò²½«Í¨¹ýÆä¹¹Ô캯Êý±»°ó¶¨¡£ + + + + + +ĬÈÏÖµ¿ÉÒÔÔÚ¹¹Ô캯Êý²ÎÊýºÍRecord×é¼þÉÏʹÓà `@DefaultValue` À´Ö¸¶¨¡£ ת»»·þÎñ½«±»Ó¦ÓÃÓÚ½«×¢½âµÄ `String` ÖµÇ¿ÖÆ×ª»»ÎªÈ±Ê§ÊôÐÔµÄÄ¿±êÀàÐÍ¡£ + + + + + +²Î¿¼Ç°ÃæµÄÀý×Ó£¬Èç¹ûûÓÐÊôÐ԰󶨵½ `Security` £¬ `MyProperties` ʵÀý½«°üº¬Ò»¸ö `security` ÀàÐ굀 `null` Öµ¡£ ΪÁËʹËü°üº¬Ò»¸ö·Ç null µÄ `Security` ʵÀý£¬¼´Ê¹Ã»ÓÐÊôÐÔÓëÖ®°ó¶¨£¨µ±Ê¹ÓÃKotlinʱ£¬Õ⽫ҪÇó `Security` µÄ `username` ºÍ `password` ²ÎÊý±»ÉùÃ÷Ϊ nullable£¬ÒòΪËüÃÇûÓÐĬÈÏÖµ£©£¬Ê¹ÓÃÒ»¸ö¿ÕµÄ `@DefaultValue` ×¢½â¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) { + this.enabled = enabled; + this.remoteAddress = remoteAddress; + this.security = security; +} + +``` + + + + + + + +| | ҪʹÓù¹Ô캯Êý°ó¶¨£¬¸ÃÀà±ØÐëʹÓà `@EnableConfigurationProperties` »òÅäÖÃÊôÐÔɨÃèÀ´ÆôÓᣠÄã²»ÄܶÔͨ¹ý³£¹æSpring»úÖÆ´´½¨µÄBeanʹÓù¹Ô캯Êý°ó¶¨£¨ÀýÈç `@Component` Bean£¬Í¨¹ýʹÓà `@Bean` ·½·¨´´½¨µÄBean»òͨ¹ýʹÓà `@Import` ¼ÓÔØµÄBean£©¡£ | +| --- | --- | + + + + + +| | ÒªÔÚÔ­Éú¾µÏñÖÐʹÓù¹Ô캯Êý°ó¶¨£¬±ØÐëÓà `-parameters` ²ÎÊý±àÒë¸ÃÀà¡£Èç¹ûÄãʹÓà Spring Boot µÄ Gradle ²å¼þ»òʹÓà Maven ºÍ `spring-boot-starter-parent`£¬Õ⽫×Ô¶¯ÅäÖᣠ| +| --- | --- | + + + + + +| | ²»½¨Ò齫 `java.util.Optional` Óë `@ConfigurationProperties` Ò»ÆðʹÓã¬ÒòΪËüÖ÷ÒªÊÇ×÷Ϊһ¸ö·µ»ØÀàÐÍʹÓᣠÒò´Ë£¬Ëü²¢²»ÊʺÏÅäÖÃÊôÐÔ×¢Èë¡£ ΪÁËÓëÆäËûÀàÐ͵ÄÊôÐÔ±£³ÖÒ»Ö£¬Èç¹ûÄãȷʵÉùÃ÷ÁËÒ»¸ö `Optional` ÊôÐÔ£¬µ«ËüûÓÐÖµ£¬`null` ¶ø²»ÊÇÒ»¸ö¿ÕµÄ `Optional` ½«±»°ó¶¨¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.enabling-annotated-types)2.8.3\. ÆôÓà @ConfigurationProperties Àà + + + +Spring BootÌṩÁË°ó¶¨ `@ConfigurationProperties` ÀàÐͲ¢½«Æä×¢²áΪBeanµÄ»ù´¡ÉèÊ©¡£ Äã¿ÉÒÔÔÚÖð¸öÀàµÄ»ù´¡ÉÏÆôÓÃÅäÖÃÊôÐÔ£¬»òÕ߯ôÓÃÅäÖÃÊôÐÔɨÃ裬Æä¹¤×÷·½Ê½Óë×é¼þɨÃèÀàËÆ¡£ + + + + + +ÓÐʱ£¬Óà `@ConfigurationProperties` ×¢½âµÄÀà¿ÉÄܲ»ÊʺÏɨÃ裬ÀýÈ磬Èç¹ûÄãÕýÔÚ¿ª·¢Äã×Ô¼ºµÄ×Ô¶¯ÅäÖûòÕßÄãÏëÓÐÌõ¼þµØÆôÓÃËüÃÇ¡£ ÔÚÕâЩÇé¿öÏ£¬Ê¹Óà `@EnableConfigurationProperties` ×¢½âÖ¸¶¨Òª´¦ÀíµÄÀàÐÍÁÐ±í£¬ Ëü¿ÉÒÔ×¢½âÔÚÈκΠ`@Configuration` ÀàÉÏ£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SomeProperties.class) +public class MyConfiguration { + +} + +``` + + + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("some.properties") +public class SomeProperties { + +} + +``` + + + + + + + +ҪʹÓÃÅäÖÃÊôÐÔɨÃ裬ÇëÏòÄãµÄapplicationÌí¼Ó `@ConfigurationPropertiesScan` ×¢½â¡£ ͨ³££¬Ëü±»Ìí¼Óµ½Óà `@SpringBootApplication` ×¢½âµÄmainÀàÖУ¬µ«ËüÒ²¿ÉÒÔ±»Ìí¼Óµ½ÈκΠ`@Configuration` ÀàÉÏ¡£ ĬÈÏÇé¿öÏ£¬É¨Ãè»á´Ó×¢½âËùÔڵİü¿ªÊ¼£¬ÄãÈç¹ûÏë×Ô¶¨ÒåɨÃèÆäËû°ü£¬¿ÉÒԲο¼ÈçÏ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@SpringBootApplication +@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" }) +public class MyApplication { + +} + +``` + + + + + + + +| | µ± `@ConfigurationProperties` BeanʹÓÃÅäÖÃÊôÐÔɨÃè»òͨ¹ý `@EnableConfigurationProperties` ×¢²áʱ£¬¸ÃBeanÓÐÒ»¸ö³£¹æÃû³Æ£º`-`£¬ÆäÖÐ `` ÊÇ `@ConfigurationProperties` ×¢½âÖÐÖ¸¶¨µÄ»·¾³¼üǰ׺£¬ `` ÊÇBeanµÄÍêÈ«ÏÞ¶¨Ãû³Æ¡£ Èç¹û×¢½âûÓÐÌṩÈκÎǰ׺£¬ÔòֻʹÓÃBeanµÄÍêÈ«ÏÞ¶¨Ãû³Æ¡£¼ÙÉèËüÔÚ `com.example.app` °üÖУ¬ÉÏÃæµÄ `SomeProperties` Àý× bean Ãû³ÆÊÇ `some.properties-com.example.app.SomeProperties`¡£ | +| --- | --- | + + + + + +ÎÒÃǽ¨Òé `@ConfigurationProperties` Ö»´¦Àí environment£¬ÌرðÊDz»´ÓÉÏÏÂÎÄ×¢ÈëÆäËûBean¡£ ¶ÔÓڱ߽ǰ¸Àý£¨ÌØÊâÇé¿ö£©£¬¿ÉÒÔʹÓà setter ×¢Èë»ò¿ò¼ÜÌṩµÄÈκΠ`*Aware` ½Ó¿Ú£¨Èç `EnvironmentAware` £¬Èç¹ûÄãÐèÒª·ÃÎÊ `Environment`£©¡£ Èç¹ûÄãÈÔÈ»ÏëʹÓù¹ÔìÆ÷×¢ÈëÆäËûBean£¬ÅäÖÃÊôÐÔBean±ØÐëÓà `@Component` À´×¢½â£¬²¢Ê¹ÓûùÓÚJavaBeanµÄÊôÐ԰󶨡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.using-annotated-types)2.8.4\. ʹÓà @ConfigurationProperties Àà + + + +ÕâÖÖÅäÖ÷½Ê½Óë `SpringApplication` ÍⲿYAMLÅäÖÃÅäºÏµÃÌØ±ðºÃ£¬ÈçÒÔÏÂÀý×ÓËùʾ¡£ + + + + + + + +``` +my: + service: + remote-address: 192.168.1.1 + security: + username: "admin" + roles: + - "USER" + - "ADMIN" +``` + + + + + + + +ҪʹÓà `@ConfigurationProperties` Bean£¬Äã¿ÉÒÔÓÃÓëÆäËûBeanÏàͬµÄ·½Ê½×¢ÈëËüÃÇ£¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Service +public class MyService { + + private final MyProperties properties; + + public MyService(MyProperties properties) { + this.properties = properties; + } + + public void openConnection() { + Server server = new Server(this.properties.getRemoteAddress()); + server.start(); + // ... + } + + // ... + +} + +``` + + + + + + + +| | ʹÓà `@ConfigurationProperties` »¹¿ÉÒÔÈÃÄãÉú³ÉÔªÊý¾ÝÎļþ£¬ÕâЩÎļþ¿ÉÒÔ±»IDEÓÃÀ´ÅäÖÃÊôÐԵġ°×Ô¶¯²¹È«¡±¹¦ÄÜ¡£ ÏêÇé¼û[¸½Â¼](https://springdoc.cn/spring-boot/configuration-metadata.html#appendix.configuration-metadata)¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.third-party-configuration)2.8.5\. µÚÈý·½ÅäÖà + + + +³ýÁËʹÓà `@ConfigurationProperties` À´×¢½âÒ»¸öÀàÖ®Í⣬Ä㻹¿ÉÒÔÔÚ¹«¹²µÄ `@Bean` ·½·¨ÉÏʹÓÃËü¡£ µ±ÄãÏë°ÑÊôÐ԰󶨵½Äã¿ØÖÆÖ®ÍâµÄµÚÈý·½×é¼þʱ£¬ÕâÑù×öÌØ±ðÓÐÓᣠ+ + + + + +Òª´Ó `Environment` ÊôÐÔÖÐÅäÖÃÒ»¸öBean£¬ÇëÔÚÆäBean×¢²áÖÐÌí¼Ó `@ConfigurationProperties` £¬ÈçÏÂÀýËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@Configuration(proxyBeanMethods = false) +public class ThirdPartyConfiguration { + + @Bean + @ConfigurationProperties(prefix = "another") + public AnotherComponent anotherComponent() { + return new AnotherComponent(); + } + +} + +``` + + + + + + + +ÈκÎÓà `another` ǰ׺¶¨ÒåµÄJavaBeanÊôÐÔ¶¼»á±»Ó³Éäµ½ `AnotherComponent` BeanÉÏ£¬Æä·½Ê½ÀàËÆÓÚÇ°ÃæµÄ `SomeProperties` Àý×Ó¡£ + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding)2.8.6\. ¿íËÉµÄ°ó¶¨ + + + +Spring BootÔÚ½« `Environment` ÊôÐ԰󶨵½ `@ConfigurationProperties` beanʱʹÓÃÁËһЩ¿íËɵĹæÔò£¬Òò´Ë `Environment` ÊôÐÔÃû³ÆºÍbeanÊôÐÔÃû³ÆÖ®¼ä²»ÐèÒªÍêȫƥÅä¡£ ÕâºÜÓÐÓ㬳£¼ûµÄÀý×Ó°üÀ¨ÆÆÕۺŷָôµÄÊôÐÔÃû³Æ£¨ÀýÈ磬 `context-path` °ó¶¨µ½ `contextPath` £©£¬ºÍ´óдµÄÊôÐÔÃû³Æ£¨ÀýÈ磬`PORT` °ó¶¨µ½ `port` £©¡£ + + + + + +ÑÝʾһ¸öÀý×Ó£¬¿¼ÂÇÒÔÏ `@ConfigurationProperties` Àà¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties(prefix = "my.main-project.person") +public class MyPersonProperties { + + private String firstName; + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + +} + +``` + + + + + + + +¶ÔÒÔÉϵĴúÂëÀ´Ëµ£¬ÒÔϵÄÊôÐÔÃû³Æ¶¼¿ÉÒÔʹÓᣠ+ + + +Table 3\. relaxed binding +| Property | Note | +| --- | --- | +| `my.main-project.person.first-name` | Kebab ·ç¸ñ£¨¶ÌºáÏ߸ô¿ª£©£¬½¨ÒéÔÚ `.properties` ºÍ `.yml` ÎļþÖÐʹÓᣠ| +| `my.main-project.person.firstName` | ±ê×¼µÄÍÕ·åÓï·¨¡£ | +| `my.main-project.person.first_name` | Ï»®Ïߣ¬ÕâÊÇÒ»ÖÖÓÃÓÚ `.properties` ºÍ `.yml` ÎļþµÄÌæ´ú¸ñʽ¡£ | +| `MY_MAINPROJECT_PERSON_FIRSTNAME` | ´óд¸ñʽ£¬ÔÚʹÓÃϵͳ»·¾³±äÁ¿Ê±½¨ÒéʹÓôóд¸ñʽ¡£ | + + + +| | ×¢½âµÄ `prefix` Öµ _±ØÐë_ ÊÇkebab·ç¸ñ£¨Ð¡Ð´²¢ÒÔ `-` ·Ö¸ô£¬Èç `my.main-project.person` £©¡£ | +| --- | --- | + + + +Table 4\. ÿÖÖÊôÐÔÔ´µÄ¿íËɰ󶨹æÔò +| ÊôÐÔÔ´ | ¼òµ¥µÄ | Áбí | +| --- | --- | --- | +| Properties Îļþ | ÍÕ·å, kebab , Ï»®Ïß | ʹÓà `[ ]` »ò¶ººÅ·Ö¸ôÖµµÄ±ê×¼ÁбíÓï·¨ | +| YAML Îļþ | ÍÕ·å, kebab , Ï»®Ïß | ±ê×¼YAMLÁбíÓï·¨»ò¶ººÅ·Ö¸ôµÄÖµ | +| »·¾³±äÁ¿ | ´óд£¬Ï»®ÏßΪ·Ö¸ô·û(¼û [´Ó»·¾³±äÁ¿°ó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables)). | Numeric values surrounded by underscores (see [´Ó»·¾³±äÁ¿°ó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables)) | +| ϵͳÊôÐÔ£¨System properties£© | ÍÕ·å, kebab , Ï»®Ïß | ʹÓà `[ ]` »ò¶ººÅ·Ö¸ôÖµµÄ±ê×¼ÁбíÓï·¨ | + + + +| | ÎÒÃǽ¨Ò飬ÔÚ¿ÉÄܵÄÇé¿öÏ£¬ÊôÐÔÓ¦ÒÔСдµÄkebab¸ñʽ´æ´¢£¬ÀýÈç `my.person.first-name=Rod` ¡£ | +| --- | --- | + + + + + +##### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding.maps)°ó¶¨Map + + + +µ±°ó¶¨µ½ `Map` ÊôÐÔʱ£¬Äã¿ÉÄÜÐèҪʹÓÃÒ»¸öÌØÊâµÄÀ¨ºÅ·ûºÅ£¬ÒԱ㱣ÁôԭʼµÄ `key` Öµ¡£ Èç¹ûkeyûÓб» `[ ]` °ü¹ü£¬ÈκηÇ×ÖĸÊý×Ö¡¢`-` »ò `.` µÄ×Ö·û½«±»É¾³ý¡£ + + + + + +ÀýÈ磬¿¼Âǽ«ÒÔÏÂÊôÐ԰󶨵½Ò»¸ö `Map`¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +my.map.[/key1]=value1 +my.map.[/key2]=value2 +my.map./key3=value3 +``` + + + + + + + +| | ¶ÔÓÚYAMLÎļþ£¬À¨ºÅÐèÒªÓÃÒýºÅ°ü¹ü£¬ÒÔʹkey±»ÕýÈ·½âÎö¡£ | +| --- | --- | + + + + + +ÉÏÃæµÄÊôÐÔ½«°ó¶¨µ½Ò»¸ö `Map` £¬`/key1`£¬`/key2` ºÍ `key3` ×÷ΪmapµÄkey¡£ бÏßÒѾ­´Ó `key3` ÖÐɾ³ý£¬ÒòΪËüûÓб»·½À¨ºÅ°ü¹ü¡£ + + + + + +µ±°ó¶¨µ½±êÁ¿ÖµÊ±£¬´øÓÐ `.` µÄ¼ü²»ÐèÒªÓà `[]` °ü¹ü¡£ ±êÁ¿Öµ°üÀ¨Ã¶¾ÙºÍËùÓÐ `java.lang` °üÖеÄÀàÐÍ£¬³ýÁË `Object` ¡£ ½« `a.b=c` °ó¶¨µ½ `Map` ½«±£Áô¼üÖÐµÄ `.` £¬²¢·µ»ØÒ»¸ö´øÓÐ `{"a.b"="c"}` EntryµÄMap¡£ ¶ÔÓÚÈÎºÎÆäËûÀàÐÍ£¬Èç¹ûÄãµÄ `key` °üº¬ `.` £¬ÄãÐèҪʹÓÃÀ¨ºÅ·ûºÅ¡£ ÀýÈ磬½« `a.b=c` °ó¶¨µ½ `Map` ½«·µ»ØÒ»¸ö´øÓÐ `{"a"={"b"="c"}` entryµÄMap£¬¶ø `[a.b]=c` ½«·µ»ØÒ»¸ö´øÓÐ `{"a.b"="c"}` entry µÄMap¡£ + + + + + + + +##### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables)´Ó»·¾³±äÁ¿°ó¶¨ + + + +ÀýÈ磬Linux shell±äÁ¿Ö»Äܰüº¬×Öĸ£¨`a` µ½ `z` »ò `A` µ½ `Z` £©¡¢Êý×Ö£¨ `0` µ½ `9` £©»òÏ»®Ïß×Ö·û£¨ `_` £©¡£ °´ÕÕ¹ßÀý£¬Unix shell±äÁ¿µÄÃû³ÆÒ²½«²ÉÓôóд×Öĸ¡£ + + + + + +Spring Boot¿íËɵİ󶨹æÔò±»Éè¼ÆÎª¾¡¿ÉÄܵØÓëÕâЩÃüÃûÏÞÖÆ¼æÈÝ¡£ + + + + + +Òª½«¹æ·¶ÐÎʽµÄÊôÐÔÃû³Æ×ª»»Îª»·¾³±äÁ¿Ãû³Æ£¬Äã¿ÉÒÔ×ñÑ­ÕâЩ¹æÔò¡£ + + + + + +* ÓÃÏ»®Ïߣ¨`_`£©Ìæ»»µã£¨`.`£©¡£ + +* ɾ³ýÈÎºÎÆÆÕۺţ¨`-`£©¡£ + +* ת»»Îª´óд×Öĸ¡£ + + + + + +ÀýÈ磬ÅäÖÃÊôÐÔ `spring.main.log-startup-info` ½«ÊÇÒ»¸öÃûΪ `SPRING_MAIN_LOGSTARTUPINFO` µÄ»·¾³±äÁ¿¡£ + + + + + +»·¾³±äÁ¿Ò²¿ÉÒÔÔڰ󶨵½¶ÔÏóÁÐ±í£¨List£©Ê±Ê¹ÓᣠҪ°ó¶¨µ½Ò»¸ö `List`£¬ÔÚ±äÁ¿Ãû³ÆÖУ¬ÔªËرàºÅ£¨Ë÷Òý£©Ó¦¸ÃÓÃÏ»®Ïß°ü¹ü¡£ + + + + + +ÀýÈ磬ÅäÖÃÊôÐÔ `my.service[0].other` ½«Ê¹ÓÃÒ»¸öÃûΪ `MY_SERVICE_0_OTHER` µÄ»·¾³±äÁ¿¡£ + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.merging-complex-types)2.8.7\. ºÏ²¢¸´ÔÓµÄÀàÐÍ + + + +µ±List±»ÅäÖÃÔÚ¶à¸öµØ·½Ê±£¬¸²¸ÇµÄ×÷ÓÃÊÇÌæ»»Õû¸ölist¡£ + + + + + +ÀýÈ磬¼ÙÉèÒ»¸ö `MyPojo` ¶ÔÏóµÄ `name` ºÍ `description` ÊôÐÔĬÈÏΪ `null`¡£ ÏÂÃæµÄÀý×Ó´Ó `MyProperties` Öб©Â¶ÁËÒ»¸ö `MyPojo` ¶ÔÏóµÄÁÐ±í¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + private final List list = new ArrayList<>(); + + public List getList() { + return this.list; + } + +} + +``` + + + + + + + +¿¼ÂÇÒÔÏÂÅäÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +my.list[0].name=my name +my.list[0].description=my description +#--- +spring.config.activate.on-profile=dev +my.list[0].name=my another name + +``` + + + + + + + +Èç¹û `dev` ÅäÖÃÎļþδ±»¼¤»î£¬`MyProperties.list` °üº¬Ò»¸ö `MyPojo` ÌõÄ¿£¬Èç֮ǰ¶¨ÒåµÄÄÇÑù¡£ È»¶ø£¬Èç¹û `dev` ÅäÖÃÎļþ±»¼¤»î£¬`list` ÈÔȻֻ°üº¬Ò»¸öÌõÄ¿£¨name Ϊ `my another name`£¬descriptionΪ `null`£©¡£ ÕâÖÖÅäÖò»»áÔÚÁбíÖÐÌí¼ÓµÚ¶þ¸ö `MyPojo` ʵÀý£¬Ò²²»»áºÏ²¢ÏîÄ¿¡£ + + + + + +µ±Ò»¸ö `List` ÔÚ¶à¸öÅäÖÃÎļþÖб»Ö¸¶¨Ê±£¬½«Ê¹ÓþßÓÐ×î¸ßÓÅÏȼ¶µÄÄǸö£¨²¢ÇÒÖ»ÓÐÄǸö£©¡£ ¿¼ÂÇÏÂÃæµÄÀý×Ó¡£ + + + + + + + +Properties + +Yaml + + + + + +``` +my.list[0].name=my name +my.list[0].description=my description +my.list[1].name=another name +my.list[1].description=another description +#--- +spring.config.activate.on-profile=dev +my.list[0].name=my another name + +``` + + + + + + + +ÔÚÇ°ÃæµÄÀý×ÓÖУ¬Èç¹û `dev` ÅäÖÃÎļþÊǼ¤»îµÄ£¬`MyProperties.list` °üº¬ _Ò»¸ö_ `MyPojo` ÌõÄ¿£¨name ÊÇ `my another name`£¬descriptionÊÇ `null`£©¡£ ¶ÔÓÚYAML£¬¶ººÅ·Ö¸ôµÄÁбíºÍYAMLÁÐ±í¶¼¿ÉÒÔÓÃÀ´ÍêÈ«¸²¸ÇÁбíµÄÄÚÈÝ¡£ + + + + + +¶ÔÓÚ `Map` ÊôÐÔ£¬Äã¿ÉÒÔÓôӶà¸öÀ´Ô´»ñÈ¡µÄÊôÐÔÖµ½øÐа󶨡£ È»¶ø£¬¶ÔÓÚ¶à¸öÀ´Ô´ÖеÄͬһÊôÐÔ£¬Ê¹ÓþßÓÐ×î¸ßÓÅÏȼ¶µÄÄǸö¡£ ÏÂÃæµÄÀý×Ó´Ó `MyProperties` ±©Â¶ÁËÒ»¸ö `Map`¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + private final Map map = new LinkedHashMap<>(); + + public Map getMap() { + return this.map; + } + +} + +``` + + + + + + + +¿¼ÂÇÒÔÏÂÅäÖᣠ+ + + + + + + +Properties + +Yaml + + + + + +``` +my.map.key1.name=my name 1 +my.map.key1.description=my description 1 +#--- +spring.config.activate.on-profile=dev +my.map.key1.name=dev name 1 +my.map.key2.name=dev name 2 +my.map.key2.description=dev description 2 + +``` + + + + + + + +Èç¹û `dev` ÅäÖÃÎļþûÓм¤»î£¬`MyProperties.map` °üº¬Ò»¸ökeyΪ `key1` µÄÌõÄ¿£¨nameΪ `my name 1` £¬descriptionΪ `my description 1` £©¡£ È»¶ø£¬Èç¹û `dev` ÅäÖÃÎļþ±»¼¤»î£¬`map` °üº¬Á½¸öÌõÄ¿£¬keyΪ `key1` £¨nameΪ `dev name 1`£¬descriptionΪ `my description 1` £©ºÍ `key2`£¨nameΪ `dev name 2`£¬descriptionΪ `dev description 2`£©¡£ + + + + + +| | Ç°ÃæµÄºÏ²¢¹æÔòÊÊÓÃÓÚËùÓÐÊôÐÔÔ´µÄÊôÐÔ£¬¶ø²»½ö½öÊÇÎļþ¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.conversion)2.8.8\. ÊôÐÔ£¨Properties£©×ª»» + + + +µ±Spring BootÓë `@ConfigurationProperties` Bean°ó¶¨Ê±£¬ËüÊÔͼ½«Íⲿapplication propertiesÇ¿ÖÆ¸ÄΪÕýÈ·µÄÀàÐÍ¡£ Èç¹ûÄãÐèÒª×Ô¶¨ÒåÀàÐÍת»»£¬Äã¿ÉÒÔÌṩһ¸ö `ConversionService` bean£¨BeanµÄÃû³ÆÎª `conversionService` £©»ò×Ô¶¨ÒåÊôÐԱ༭Æ÷£¨Í¨¹ý `CustomEditorConfigurer` bean£©»ò×Ô¶¨Òå `Converters` Bean£¨Ê¹Óà `@ConfigurationPropertiesBinding` ×¢½â£©¡£ + + + + + +| | ÓÉÓÚÕâ¸öBeanÊÇÔÚÓ¦ÓóÌÐòÉúÃüÖÜÆÚµÄÔçÆÚ±»ÇëÇóµÄ£¬ÇëÈ·±£ÏÞÖÆÄãµÄ `ConversionService` ËùʹÓõÄÒÀÀµ¹ØÏµ¡£ ͨ³£Çé¿öÏ£¬ÄãËùÐèÒªµÄÈκÎÒÀÀµ¹ØÏµÔÚ´´½¨Ê±¿ÉÄÜûÓÐÍêÈ«³õʼ»¯¡£ Èç¹ûÄãµÄ×Ô¶¨Òå `ConversionService` ²»ÐèÒªÅäÖÃkeys coercion£¬Äã¿ÉÄÜÏëÖØÃüÃûËü£¬²¢ÇÒÖ»ÒÀÀµÓà `@ConfigurationPropertiesBinding` ÏÞ¶¨µÄ×Ô¶¨Òåת»»Æ÷¡£ | +| --- | --- | + + + + + +##### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.conversion.durations)ת»»Îª Duration + + + +Spring Boot¶Ô±í´ï³ÖÐøÊ±¼äÓÐרÃŵÄÖ§³Ö¡£ Èç¹ûÄ㹫¿ªÁËÒ»¸ö `java.time.Duration` ÊôÐÔ£¬application propertiesÖеÄÒÔϸñʽ¾Í¿ÉÓᣠ+ + + + + +* ÆÕͨµÄ `long` £¨Ê¹ÓúÁÃë×÷ΪĬÈϵ¥Î»£¬³ý·ÇÖ¸¶¨ÁË `@DurationUnit` £©¡£ + +* ±ê×¼µÄISO-8601¸ñʽ [ÓÉ `java.time.Duration` ʹÓÃ](https://docs.oracle.com/javase/17/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-)¡£ + +* Ò»¸ö¸üÒ×¶ÁµÄ¸ñʽ£¬ÆäÖÐÖµºÍµ¥Î»ÊÇñîºÏµÄ£¨`10s` ±íʾ10Ã룩¡£ + + + + + +Ç뿼ÂÇÒÔÏÂÀý×Ó¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + @DurationUnit(ChronoUnit.SECONDS) + private Duration sessionTimeout = Duration.ofSeconds(30); + + private Duration readTimeout = Duration.ofMillis(1000); + + // getters / setters... + +} + +``` + + + + + + + +ÒªÖ¸¶¨Ò»¸ö30ÃëµÄ»á»°³¬Ê±£¬ `30` ¡¢ `PT30S` ºÍ `30s` ¶¼Êǵȼ۵ġ£ ¶ÁÈ¡³¬Ê±Îª500ms£¬¿ÉÒÔÓÃÒÔÏÂÈκÎÒ»ÖÖÐÎʽָ¶¨¡£ `500`, `PT0.5S` ºÍ `500ms`. + + + + + +ÄãÒ²¿ÉÒÔʹÓÃÈçÏÂÖ§³ÖµÄʱ¼äµ¥Î»¡£ + + + + + +* `ns` ÄÉÃë + +* `us` ΢Ãë + +* `ms` ºÁÃë + +* `s` Ãë + +* `m` ·Ö + +* `h` Сʱ + +* `d` Ìì + + + + + +ĬÈϵ¥Î»ÊǺÁÃ룬¿ÉÒÔʹÓà `@DurationUnit` À´ÖØÐ´£¬ÈçÉÏÃæµÄÀý×ÓËùʾ¡£ + + + + + +Èç¹ûÄãϲ»¶Ê¹Óù¹Ô캯Êý°ó¶¨£¬Í¬ÑùµÄÊôÐÔ¿ÉÒÔ±»±©Â¶³öÀ´£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + // fields... + + public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout, + @DefaultValue("1000ms") Duration readTimeout) { + this.sessionTimeout = sessionTimeout; + this.readTimeout = readTimeout; + } + + // getters... + +} + +``` + + + + + + + +| | Èç¹ûÄãÒªÉý¼¶Ò»¸ö `Long` µÄÊôÐÔ£¬Èç¹ûËü²»ÊǺÁÃ룬ÇëÈ·±£¶¨Ò嵥λ£¨Ê¹Óà `@DurationUnit` £©¡£ ÕâÑù×öÌṩÁËÒ»¸ö͸Ã÷µÄÉý¼¶Â·¾¶£¬Í¬Ê±Ö§³Ö¸ü·á¸»µÄ¸ñʽ | +| --- | --- | + + + + + + + +##### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.conversion.periods)ת»»ÎªÆÚ¼ä£¨Period£© + + + +³ýÁËduration£¬Spring Boot»¹¿ÉÒÔʹÓà `java.time.Period` ÀàÐÍ¡£ ÒÔϸñʽ¿ÉÒÔÔÚapplication propertiesÖÐʹÓᣠ+ + + + + +* Ò»¸ö³£¹æµÄ `int` ±íʾ·¨£¨Ê¹ÓÃÌì×÷ΪĬÈϵ¥Î»£¬³ý·ÇÖ¸¶¨ÁË `@PeriodUnit` £©¡£ + +* ±ê×¼µÄISO-8601¸ñʽ [ÓÉ `java.time.Period` ʹÓÃ](https://docs.oracle.com/javase/17/docs/api/java/time/Period.html#parse-java.lang.CharSequence-)¡£ + +* Ò»¸ö¸ü¼òµ¥µÄ¸ñʽ£¬ÆäÖÐÖµºÍµ¥Î»¶ÔÊÇñîºÏµÄ£¨ `1y3d` ±íʾ1Äê3Ì죩¡£ + + + + + +Ö§³ÖÏÂÁмòµ¥µÄµ¥Î»¸ñʽ¡£ + + + + + +* `y` Äê + +* `m` Ô + +* `w` ÖÜ + +* `d` ÈÕ + + + + + +| | `java.time.Period` ÀàÐÍʵ¼ÊÉÏ´Óδ´æ´¢¹ýÖÜÊý£¬ËüÊÇÒ»¸ö¿ì½Ý·½Ê½£¬Òâζ×Å ¡°7Ì족¡£ | +| --- | --- | + + + + + + + +##### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.conversion.data-sizes)ת»»ÎªÊý¾Ý´óС£¨Data Sizes£© + + + +Spring FrameworkÓÐÒ»¸ö `DataSize` ÖµÀàÐÍ£¬ÒÔ×Ö½ÚΪµ¥Î»±í´ï´óС¡£ Èç¹ûÄ㹫¿ªÁËÒ»¸ö `DataSize` ÊôÐÔ£¬application propertiesÖеÄÒÔϸñʽ¾Í¿ÉÓᣠ+ + + + + +* Ò»¸ö³£¹æµÄ `long` ±íʾ£¨Ê¹ÓÃ×Ö½Ú×÷ΪĬÈϵ¥Î»£¬³ý·ÇÖ¸¶¨ÁË `@DataSizeUnit`£©¡£ + +* Ò»¸ö¸üÒ×¶ÁµÄ¸ñʽ£¬ÆäÖÐÖµºÍµ¥Î»ÊÇñîºÏµÄ£¨`10MB` Òâζ×Å10Õ××Ö½Ú£©¡£ + + + + + +¿¼ÂÇÒÔÏÂÀý×Ó¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + @DataSizeUnit(DataUnit.MEGABYTES) + private DataSize bufferSize = DataSize.ofMegabytes(2); + + private DataSize sizeThreshold = DataSize.ofBytes(512); + + // getters/setters... + +} + +``` + + + + + + + +ÒªÖ¸¶¨Ò»¸ö10Õ××Ö½Ú£¨Mb£©µÄ»º³åÇø´óС£¬ `10` ºÍ `10MB` Êǵȼ۵ġ£ 256×ֽڵĴóСãÐÖµ¿ÉÒÔÖ¸¶¨Îª `256` »ò `256B`¡£ + + + + + +ÄãÒ²¿ÉÒÔʹÓÃÈçÏÂÕâЩ֧³ÖµÄµ¥Î»¡£ + + + + + +* `B` ×Ö½Ú + +* `KB` KB + +* `MB` MB + +* `GB` GB + +* `TB` TB + + + + + +ĬÈϵ¥Î»ÊÇ×Ö½Ú£¬¿ÉÒÔʹÓà `@DataSizeUnit` À´ÖØÐ´£¬ÈçÉÏÃæµÄÀý×ÓËùʾ¡£ + + + + + +Èç¹ûÄãϲ»¶Ê¹Óù¹Ô캯Êý°ó¶¨£¬Í¬ÑùµÄÊôÐÔ¿ÉÒÔ±»±©Â¶³öÀ´£¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my") +public class MyProperties { + + // fields... + + public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize, + @DefaultValue("512B") DataSize sizeThreshold) { + this.bufferSize = bufferSize; + this.sizeThreshold = sizeThreshold; + } + + // getters... + +} + +``` + + + + + + + +| | Èç¹ûÄãÕýÔÚÉý¼¶Ò»¸ö `Long` ÊôÐÔ£¬È·±£¶¨Ò嵥λ£¨Ê¹Óà `@DataSizeUnit`£©£¬Èç¹ûËü²»ÊÇ×Ö½Ú¡£ ÕâÑù×öÌṩÁËÒ»¸ö͸Ã÷µÄÉý¼¶Â·¾¶£¬Í¬Ê±Ö§³Ö¸ü·á¸»µÄ¸ñʽ¡£ | +| --- | --- | + + + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.validation)2.8.9\. @ConfigurationProperties УÑé + + + +ֻҪʹÓÃSpringµÄ `@Validated` ×¢½â£¬Spring Boot¾Í»á³¢ÊÔÑéÖ¤ `@ConfigurationProperties` Àà¡£ Äã¿ÉÒÔÖ±½ÓÔÚÄãµÄÅäÖÃÀàÉÏʹÓÃJSR-303µÄ `jakarta.validation` Ô¼Êø×¢½â¡£ Òª×öµ½ÕâÒ»µã£¬ÇëÈ·±£ÄãµÄclasspathÉÏÓÐÒ»¸ö¼æÈݵÄJSR-303ʵÏÖ£¬È»ºó½«Ô¼Êø×¢½âÌí¼Óµ½ÄãµÄ×Ö¶ÎÖУ¬ÈçÏÂÃæµÄÀý×ÓËùʾ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my.service") +@Validated +public class MyProperties { + + @NotNull + private InetAddress remoteAddress; + + // getters/setters... + +} + +``` + + + + + + + +| | ÄãÒ²¿ÉÒÔͨ¹ýÔÚ configuration properties µÄ `@Bean` ·½·¨ÉÏ×¢½â `@Validated` À´´¥·¢ÑéÖ¤¡£ | +| --- | --- | + + + + + +ΪÁËÈ·±£×ÜÊÇΪǶÌ×µÄÊôÐÔ´¥·¢ÑéÖ¤£¬¼´Ê¹Ã»ÓÐÕÒµ½ÊôÐÔ£¬Ïà¹ØµÄ×ֶαØÐëÓà `@Valid` À´×¢ÊÍ¡£ ÏÂÃæµÄÀý×Ó½¨Á¢ÔÚÇ°ÃæµÄ `MyProperties` µÄ»ù´¡ÉÏ¡£ + + + + + + + +Java + +Kotlin + + + + + +``` +@ConfigurationProperties("my.service") +@Validated +public class MyProperties { + + @NotNull + private InetAddress remoteAddress; + + @Valid + private final Security security = new Security(); + + // getters/setters... + + public static class Security { + + @NotEmpty + private String username; + + // getters/setters... + + } + +} + +``` + + + + + + + +ÄãÒ²¿ÉÒÔͨ¹ý´´½¨Ò»¸öÃûΪ `configurationPropertiesValidator` µÄbean¶¨ÒåÀ´Ìí¼ÓÒ»¸ö×Ô¶¨ÒåµÄSpring `Validator`¡£ `@Bean` ·½·¨Ó¦¸Ã±»ÉùÃ÷Ϊ `static`¡£ ÅäÖÃÊôÐÔÑéÖ¤Æ÷ÊÇÔÚÓ¦ÓóÌÐòÉúÃüÖÜÆÚµÄÔçÆÚ´´½¨µÄ£¬½« `@Bean` ·½·¨ÉùÃ÷Ϊ¾²Ì¬£¬¿ÉÒÔÈÃBeanµÄ´´½¨²»ÐèҪʵÀý»¯ `@Configuration` Àà¡£ ÕâÑù×ö¿ÉÒÔ±ÜÃâ¹ýÔçʵÀý»¯¿ÉÄÜÒýÆðµÄÈκÎÎÊÌâ¡£ + + + + + +| | `spring-boot-actuator` Ä£¿é°üÀ¨Ò»¸ö±©Â¶ËùÓÐ `@ConfigurationProperties` Bean µÄ¶Ëµã¡£ Äã¿ÉÒÔͨ¹ýä¯ÀÀÆ÷·ÃÎÊ `/actuator/configprops` »òʹÓÃÏàÓ¦µÄJMX¶Ëµã¡£ ÏêÇé¼û"[Éú²ú¾ÍÐ÷](https://springdoc.cn/spring-boot/actuator.html#actuator.endpoints)"²¿·Ö¡£ | +| --- | --- | + + + + + + + +#### [](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.vs-value-annotation)2.8.10\. @ConfigurationProperties vs. @Value + + + +`@Value` ×¢½âÊÇÒ»¸öºËÐĵÄÈÝÆ÷¹¦ÄÜ£¬Ëü²»ÌṩÓëÀàÐͰ²È«µÄÅäÖÃÊôÐÔÏàͬµÄ¹¦ÄÜ¡£ ϱí×ܽáÁË `@ConfigurationProperties` ºÍ `@Value` ËùÖ§³ÖµÄ¹¦ÄÜ¡£ + + + + +| ¹¦ÄÜ | `@ConfigurationProperties` | `@Value` | +| --- | --- | --- | +| [¿íËɰó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding) | Yes | ÓÐÏÞÖÆ (¼û [ÏÂÎÄ×¢ÊÍ](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.vs-value-annotation.note)) | +| [Ö§³Ö Meta-data](https://springdoc.cn/spring-boot/configuration-metadata.html#appendix.configuration-metadata) | Yes | No | +| `SpEL` ±í´ïʽ | No | Yes | + + + +| | Èç¹ûÄãȷʵÏëʹÓà `@Value`£¬ÎÒÃǽ¨ÒéÄãʹÓÃÊôÐÔÃû³ÆµÄ¹æ·¶ÐÎʽ£¨½öʹÓÃСд×ÖĸµÄkebab-case£©À´ÒýÓÃÊôÐÔÃû³Æ¡£ Õ⽫ÔÊÐíSpring BootʹÓÃÓë [¿íËɰó¶¨](https://springdoc.cn/spring-boot/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding) `@ConfigurationProperties` ʱÏàͬµÄÂß¼­¡£ÀýÈ磬`@Value("${demo.item-price}")` ½«´Ó `application.properties` ÎļþÖлñÈ¡ `demo.item-price` ºÍ `demo.itemPrice` ÐÎʽ£¬ÒÔ¼°´Óϵͳ»·¾³ÖлñÈ¡ `DEMO_ITEMPRICE`¡£ Èç¹ûÄãÓà `@Value("${demo.itemPrice}")` ´úÌæ£¬`demo.item-price` ºÍ `DEMO_ITEMPRICE` ½«²»»á±»¿¼ÂÇ¡£ | +| --- | --- | + + + + + +Èç¹ûÄãΪÄã×Ô¼ºµÄ×é¼þ¶¨ÒåÁËÒ»×éÅäÖüü£¬ÎÒÃǽ¨ÒéÄ㽫ËüÃÇ·Ö×éÔÚÒ»¸öÓà `@ConfigurationProperties` ×¢½âµÄPOJOÖС£ ÕâÑù×ö½«ÎªÄãÌṩ½á¹¹»¯µÄ¡¢ÀàÐͰ²È«µÄ¶ÔÏó£¬Äã¿ÉÒÔ½«Æä×¢Èëµ½Äã×Ô¼ºµÄbeanÖС£ + + + + + +À´×ÔÓ¦ÓÃ[application property](https://springdoc.cn/spring-boot/features.html#features.external-config.files) ÎļþµÄ `SpEL` ±í´ïʽÔÚ½âÎöÕâЩÎļþºÍÌî³äenvironmentʱ²»»á±»´¦Àí¡£ È»¶ø£¬¿ÉÒÔÔÚ `@Value` ÖÐдһ¸ö `SpEL` ±í´ïʽ¡£ Èç¹ûÀ´×ÔÓ¦ÓóÌÐòÊôÐÔÎļþµÄÊôÐÔÖµÊÇÒ»¸ö `SpEL` ±í´ïʽ£¬Ëü½«ÔÚ±» `@Value` Ïû·Ñʱ±»½âÎö¡£ + + + + + + + + + + + + + +## [](https://springdoc.cn/spring-boot/features.html#features.profiles) + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\350\207\252\345\270\246\347\232\204\347\203\255\351\203\250\347\275\262\345\267\245\345\205\267.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\350\207\252\345\270\246\347\232\204\347\203\255\351\203\250\347\275\262\345\267\245\345\205\267.md" new file mode 100644 index 0000000..477370a --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\350\207\252\345\270\246\347\232\204\347\203\255\351\203\250\347\275\262\345\267\245\345\205\267.md" @@ -0,0 +1,345 @@ +# SpringBootÈëÃÅ - ÅäÖÃÈȲ¿Êðdevtools¹¤¾ß + +> ÔÚSpringBoot¿ª·¢µ÷ÊÔÖУ¬Èç¹ûÎÒÿÐдúÂëµÄÐ޸ͼÐèÒªÖØÆôÆô¶¯ÔÙµ÷ÊÔ£¬¿ÉÄܱȽϷÑʱ¼ä£»SpringBootÍŶÓÕë¶Ô´ËÎÊÌâÌṩÁËspring-boot-devtools£¨¼ò³Ædevtools£©²å¼þ£¬ËüÊÔͼÌáÉý¿ª·¢µ÷ÊÔµÄЧÂÊ¡£@pdai + +* [SpringBootÈëÃÅ - ÅäÖÃÈȲ¿Êðdevtools¹¤¾ß](#springboot%E5%85%A5%E9%97%A8---%E9%85%8D%E7%BD%AE%E7%83%AD%E9%83%A8%E7%BD%B2devtools%E5%B7%A5%E5%85%B7) + * [×¼±¸ÖªÊ¶µã](#%E5%87%86%E5%A4%87%E7%9F%A5%E8%AF%86%E7%82%B9) + * [ʲôÊÇÈȲ¿ÊðºÍÈȼÓÔØ£¿](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%83%AD%E9%83%A8%E7%BD%B2%E5%92%8C%E7%83%AD%E5%8A%A0%E8%BD%BD) + * [ʲôÊÇLiveLoad£¿](#%E4%BB%80%E4%B9%88%E6%98%AFliveload) + * [ÅäÖÃdevtoolsʵÏÖÈȲ¿Êð](#%E9%85%8D%E7%BD%AEdevtools%E5%AE%9E%E7%8E%B0%E7%83%AD%E9%83%A8%E7%BD%B2) + * [POMÅäÖÃ](#pom%E9%85%8D%E7%BD%AE) + * [IDEAÅäÖÃ](#idea%E9%85%8D%E7%BD%AE) + * [application.ymlÅäÖÃ](#applicationyml%E9%85%8D%E7%BD%AE) + * [ʹÓÃLiveLoad](#%E4%BD%BF%E7%94%A8liveload) + * [½øÒ»²½Àí½â](#%E8%BF%9B%E4%B8%80%E6%AD%A5%E7%90%86%E8%A7%A3) + * [devtoolµÄÔ­Àí£¿ÎªºÎ»á×Ô¶¯ÖØÆô£¿](#devtool%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%BA%E4%BD%95%E4%BC%9A%E8%87%AA%E5%8A%A8%E9%87%8D%E5%90%AF) + * [devtoolÊÇ·ñ»á±»´ò°ü½øJar£¿](#devtool%E6%98%AF%E5%90%A6%E4%BC%9A%E8%A2%AB%E6%89%93%E5%8C%85%E8%BF%9Bjar) + * [devtoolΪºÎ»áĬÈϽûÓûº´æÑ¡Ï](#devtool%E4%B8%BA%E4%BD%95%E4%BC%9A%E9%BB%98%E8%AE%A4%E7%A6%81%E7%94%A8%E7%BC%93%E5%AD%98%E9%80%89%E9%A1%B9) + * [devtoolÊÇ·ñ¿ÉÒÔ¸øËùÓÐSpringbootÓ¦ÓÃ×öÈ«¾ÖµÄÅäÖã¿](#devtool%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E7%BB%99%E6%89%80%E6%9C%89springboot%E5%BA%94%E7%94%A8%E5%81%9A%E5%85%A8%E5%B1%80%E7%9A%84%E9%85%8D%E7%BD%AE) + * [Èç¹ûÎÒ²»ÓÃdevtool£¬»¹ÓÐʲôѡÔñ£¿](#%E5%A6%82%E6%9E%9C%E6%88%91%E4%B8%8D%E7%94%A8devtool%E8%BF%98%E6%9C%89%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9) + * [ʾÀýÔ´Âë](#%E7%A4%BA%E4%BE%8B%E6%BA%90%E7%A0%81) + * [²Î¿¼ÎÄÕÂ](#%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0) + +## [#](#×¼±¸ÖªÊ¶µã) ×¼±¸ÖªÊ¶µã + +### [#](#ʲôÊÇÈȲ¿ÊðºÍÈȼÓÔØ) ʲôÊÇÈȲ¿ÊðºÍÈȼÓÔØ£¿ + +> ÈȲ¿ÊðºÍÈȼÓÔØÊÇÔÚÓ¦ÓÃÕýÔÚÔËÐеÄʱºò£¬×Ô¶¯¸üУ¨ÖØÐ¼ÓÔØ»òÕßÌæ»»classµÈ£©Ó¦ÓõÄÒ»ÖÖÄÜÁ¦¡££¨PS£ºspring-boot-devtoolsÌṩµÄ·½°¸Ò²ÊÇÒªÖØÆôµÄ£¬Ö»ÊÇÎÞÐèÊÖ¶¯ÖØÆôÄÜʵÏÖ×Ô¶¯¼ÓÔØ¶øÒÑ¡££© + +ÑϸñÒâÒåÉÏ£¬ÎÒÃÇÐè񻂿·ÖÏÂÈȲ¿ÊðºÍÈȼÓÔØ, ¶ÔÓÚJavaÏîÄ¿¶øÑÔ£º + +* **ÈȲ¿Êð** + + * ÔÚ·þÎñÆ÷ÔËÐÐÊ±ÖØÐ²¿ÊðÏîÄ¿ + * ËüÊÇÖ±½ÓÖØÐ¼ÓÔØÕû¸öÓ¦Óã¬ÕâÖÖ·½Ê½»áÊÍ·ÅÄڴ棬±ÈÈȼÓÔØ¸ü¼Ó¸É¾»³¹µ×£¬µ«Í¬Ê±Ò²¸ü·Ñʱ¼ä¡£ +* **ÈȼÓÔØ** + + * ÔÚÔÚÔËÐÐÊ±ÖØÐ¼ÓÔØclass£¬´Ó¶øÉý¼¶Ó¦Óᣠ+ * ÈȼÓÔØµÄʵÏÖÔ­ÀíÖ÷ÒªÒÀÀµ[javaµÄÀà¼ÓÔØ»úÖÆ](/md/java/jvm/java-jvm-classload.html)£¬ÔÚʵÏÖ·½Ê½¿ÉÒÔ¸ÅÀ¨ÎªÔÚÈÝÆ÷Æô¶¯µÄʱºòÆðÒ»Ìõºǫ́Ị̈߳¬¶¨Ê±µÄ¼ì²âÀàÎļþµÄʱ¼ä´Á±ä»¯£¬Èç¹ûÀàµÄʱ¼ä´Á±äµôÁË£¬Ôò½«ÀàÖØÐÂÔØÈë¡£ + * ¶Ô±È·´Éä»úÖÆ£¬·´ÉäÊÇÔÚÔËÐÐʱ»ñÈ¡ÀàÐÅÏ¢£¬Í¨¹ý¶¯Ì¬µÄµ÷ÓÃÀ´¸Ä±ä³ÌÐòÐÐΪ£» ÈȼÓÔØÔòÊÇÔÚÔËÐÐʱͨ¹ýÖØÐ¼ÓÔØ¸Ä±äÀàÐÅÏ¢£¬Ö±½Ó¸Ä±ä³ÌÐòÐÐΪ¡£ + +### [#](#ʲôÊÇliveload) ʲôÊÇLiveLoad£¿ + +LiveLoadÊÇÌṩä¯ÀÀÆ÷¿Í»§¶Ë×Ô¶¯¼ÓÔØ¸üÐµĹ¤¾ß£¬·ÖΪLiveLoad·þÎñÆ÷ºÍLiveloadä¯ÀÀÆ÷²å¼þÁ½²¿·Ö£» devtoolsÖÐÒѾ­¼¯³ÉÁËLiveLoad·þÎñÆ÷£¬ËùÒÔÈç¹ûÎÒÃÇ¿ª·¢µÄÊÇwebÓ¦Ó㬲¢ÇÒÆÚÍûä¯ÀÀÆ÷×Ô¶¯Ë¢Ð£¬ Õâʱºò¿ÉÒÔ¿¼ÂÇLiveLoad. + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205152.png) + +ͬһʱ¼äÖ»ÄÜÔËÐÐÒ»¸öLiveReload·þÎñÆ÷¡£ ¿ªÊ¼Ó¦ÓóÌÐò֮ǰ£¬ÇëÈ·±£Ã»ÓÐÆäËûLiveReload·þÎñÆ÷ÕýÔÚÔËÐС£Èç¹û´ÓIDEÆô¶¯¶à¸öÓ¦ÓóÌÐò£¬ÔòÖ»ÓеÚÒ»¸öÓ¦ÓóÌÐò½«Ö§³ÖLiveReload¡£ + +## [#](#ÅäÖÃdevtoolsʵÏÖÈȲ¿Êð) ÅäÖÃdevtoolsʵÏÖÈȲ¿Êð + +> ÎÒÃÇͨ¹ýÈçÏÂÅäÖÃÀ´ÊµÏÖ×Ô¶¯ÖØÆô·½Ê½µÄÈȲ¿Êð¡£ + +### [#](#pomÅäÖÃ) POMÅäÖà + +Ìí¼Óspring-boot-devtoolsµÄÒÀÀµ + + + +``` + + + org.springframework.boot + spring-boot-devtools + true + + + +``` + + + +### [#](#ideaÅäÖÃ) IDEAÅäÖà + +> Èç¹ûÄãʹÓÃIDEA¿ª·¢¹¤¾ß£¬Í¨³£ÓÐÈçÏÂÁ½ÖÖ·½Ê½£º + +* ·½Ê½Ò»£º **ÎÞÈκÎÅäÖÃʱ£¬ÊÖ¶¯´¥·¢ÖØÆô¸üУ¨Ctrl+F9£©** + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205320.png) +£¨Ò²¿ÉÒÔÓÃ`mvn compile`±àÒë´¥·¢ÖØÆô¸üУ© + +* ·½Ê½¶þ£º **IDEAÐ迪ÆôÔËÐÐʱ±àÒ룬×Ô¶¯ÖØÆô¸üÐÂ** + +**ÉèÖÃ1**£º + +File->Setting->Build,Execution,Deployment->Compile + +¹´Ñ¡£ºMake project automatically + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205335.png) + +**ÉèÖÃ2**£º + +¿ì½Ý¼ü£ºctrl+alt+shift+/ + +Ñ¡Ôñ£ºRegistry + +¹´Ñ¡£ºcompiler.automake.allow.when.app.running + +а汾µÄIDEA¿ÉÒÔÔÚFile->setting->Advanced SetttingsÀïÃæµÄµÚÒ»¸öÉèÖ㺠+ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205405.png) +### [#](#application-ymlÅäÖÃ) application.ymlÅäÖà + + + +``` +spring: + devtools: + restart: + enabled: true #ÉèÖÿªÆôÈȲ¿Êð + additional-paths: src/main/java #ÖØÆôĿ¼ + exclude: WEB-INF/** + thymeleaf: + cache: false #ʹÓÃThymeleafÄ£°åÒýÇæ£¬¹Ø±Õ»º´æ + +``` + + + +### [#](#ʹÓÃliveload) ʹÓÃLiveLoad + +spring-boot-devtoolsÄ£¿é°üº¬**ǶÈëʽLiveReload·þÎñÆ÷**£¬¿ÉÒÔÔÚ×ÊÔ´¸ü¸ÄʱÓÃÓÚ´¥·¢ä¯ÀÀÆ÷ˢС£ LiveReloadä¯ÀÀÆ÷À©Õ¹³ÌÐòÖ§³ÖChrome£¬FirefoxºÍSafari£¬Äã¿ÉÒÔ´Ólivereload.comÃâ·ÑÏÂÔØ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205417.png) +»òÕß´Óä¯ÀÀÆ÷²å¼þÖÐÐÄÏÂÔØ£¬±ÈÈçfirefox: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205426.png) + +°²×°ÍêÖ®ºó£¬¿ÉÒÔͨ¹ýÈçÏÂͼ±ê¹ÜÀí + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416205442.png) + +Èç¹ûÄã²»ÏëÔÚÓ¦ÓóÌÐòÔËÐÐʱÆô¶¯LiveReload·þÎñÆ÷£¬Ôò¿ÉÒÔ½«spring.devtools.livereload.enabledÊôÐÔÉèÖÃΪfalse ¡£ + +ͬһʱ¼äÖ»ÄÜÔËÐÐÒ»¸öLiveReload·þÎñÆ÷¡£ ¿ªÊ¼Ó¦ÓóÌÐò֮ǰ£¬ÇëÈ·±£Ã»ÓÐÆäËûLiveReload·þÎñÆ÷ÕýÔÚÔËÐС£Èç¹û´ÓIDEÆô¶¯¶à¸öÓ¦ÓóÌÐò£¬ÔòÖ»ÓеÚÒ»¸öÓ¦ÓóÌÐò½«Ö§³ÖLiveReload¡£ + +## [#](#½øÒ»²½Àí½â) ½øÒ»²½Àí½â + +> ËäȻһЩ¿ª·¢Õß»áʹÓÃdevtool¹¤¾ß£¬µ«ÊǺÜÉÙÓÐÄܹ»ÉîÈëÀí½âµÄ£»ÈÃÎÒÃÇÀí½âÈçϼ¸¸öÎÊÌ⣬°ïÖúÄã½øÒ»²½Àí½â¡£@pdai + +### [#](#devtoolµÄÔ­Àí-ΪºÎ»á×Ô¶¯ÖØÆô) devtoolµÄÔ­Àí£¿ÎªºÎ»á×Ô¶¯ÖØÆô£¿ + +> ΪʲôͬÑùÊÇÖØÆôÓ¦Óã¬ÎªÊ²Ã´²»ÊÖ¶¯ÖØÆô£¬¶øÊǽ¨ÒéʹÓÃspring-boot-devtools½øÐÐÈȲ¿ÊðÖØÆô£¿ + +spring-boot-devtoolsʹÓÃÁËÁ½¸öÀà¼ÓÔØÆ÷ClassLoader£¬Ò»¸öClassLoader¼ÓÔØ²»»á·¢Éú¸ü¸ÄµÄÀࣨµÚÈý·½jar°ü£©£¬ÁíÒ»¸öClassLoader£¨restart ClassLoader£©¼ÓÔØ»á¸ü¸ÄµÄÀࣨ×Ô¶¨ÒåµÄÀࣩ¡£ + +ºǫ́Æô¶¯Ò»¸ö**Îļþ¼àÌýỊ̈߳¨File Watcher£©**£¬**¼à²âµÄĿ¼ÖеÄÎļþ·¢Éú±ä¶¯Ê±£¬ Ô­À´µÄrestart ClassLoader±»¶ªÆú£¬½«»áÖØÐ¼ÓÔØÐµÄrestart ClassLoader**¡£ + +ÒòΪÎļþ±ä¶¯ºó£¬µÚÈý·½jar°ü²»ÔÙÖØÐ¼ÓÔØ£¬Ö»¼ÓÔØ×Ô¶¨ÒåµÄÀ࣬¼ÓÔØµÄÀà±È½ÏÉÙ£¬ËùÒÔÖØÆô±È½Ï¿ì¡£ + +ÕâÒ²ÊÇΪʲô£¬Í¬ÑùÊÇÖØÆôÓ¦Óã¬ÎªÊ²Ã´²»ÊÖ¶¯ÖØÆô£¬½¨ÒéʹÓÃspring-boot-devtools½øÐÐÈȲ¿ÊðÖØÆô¡£ + +ÔÚ×Ô¶¯ÖØÆôÖÐÓм¸µãÐèҪעÒâ: + +* **×Ô¶¯ÖØÆô»á¼Ç¼ÈÕÖ¾µÄ** + +£¨¼Ç¼ÔÚʲôÇé¿öÏÂÖØÆôµÄÈÕÖ¾£© + +¿ÉÒÔͨ¹ýÈçÏÂ¹Ø±Õ + + + +``` +spring: + devtools: + restart: + log-condition-evaluation-delta: false + +``` + + + +* **ÅųýһЩ²»ÐèÒª×Ô¶¯ÖØÆôµÄ×ÊÔ´** + +ijЩ×ÊÔ´ÔÚ¸ü¸Äʱ²»Ò»¶¨ÐèÒª´¥·¢ÖØÐÂÆô¶¯¡£Ä¬ÈÏÇé¿öÏ£¬¸Ä±ä×ÊÔ´/META-INF/maven£¬/META-INF/resources£¬/resources£¬/static£¬/public£¬»ò/templates²»´¥·¢ÖØÐÂÆô¶¯£¬µ«È·»á´¥·¢ÏÖ³¡ÖØ×°¡£Èç¹ûÒª×Ô¶¨ÒåÕâЩÅųýÏ¿ÉÒÔʹÓøÃspring.devtools.restart.excludeÊôÐÔ¡£ÀýÈ磬Ҫ½öÅųý/static£¬/publicÄ㽫ÉèÖÃÒÔÏÂÊôÐÔ£º + + + +``` +spring: + devtools: + restart: + exclude: "static/**,public/**" + +``` + + + +Èç¹ûÒª±£ÁôÕâЩĬÈÏÖµ²¢Ìí¼ÓÆäËûÅųýÏÇë¸ÄÓøÃspring.devtools.restart.additional-excludeÊôÐÔ¡£ + +* **×Ô¶¨ÒåÖØÆôÀà¼ÓÔØÆ÷** + +ÖØÆô¹¦ÄÜÊÇͨ¹ýʹÓÃÁ½¸öÀà¼ÓÔØÆ÷À´ÊµÏֵġ£¶ÔÓÚ´ó¶àÊýÓ¦ÓóÌÐò£¬ÕâÖÖ·½·¨Ð§¹ûºÜºÃ¡£µ«ÊÇ£¬ËüÓÐʱ»áµ¼ÖÂÀà¼ÓÔØÎÊÌâ¡£ + +ĬÈÏÇé¿öÏ£¬IDE ÖеÄÈκδò¿ªÏîÄ¿¶¼Ê¹Óá°ÖØÆô¡±Àà¼ÓÔØÆ÷¼ÓÔØ£¬Èκγ£¹æ.jarÎļþ¶¼Ê¹Óá°»ù±¾¡±Àà¼ÓÔØÆ÷¼ÓÔØ¡£Èç¹ûÄã´¦ÀíÒ»¸ö¶àÄ£¿éÏîÄ¿£¬²¢ÇÒ²»ÊÇÿ¸öÄ£¿é¶¼µ¼Èëµ½ÄãµÄ IDE ÖУ¬Äã¿ÉÄÜÐèÒª×Ô¶¨ÒåһЩ¶«Î÷¡£Îª´Ë£¬Äã¿ÉÒÔ´´½¨Ò»¸öMETA-INF/spring-devtools.propertiesÎļþ¡£ + +¸Ãspring-devtools.propertiesÎļþ¿ÉÒÔ°üº¬ÒÔrestart.excludeºÍΪǰ׺µÄÊôÐÔrestart.include¡£¸ÃincludeÔªËØÊÇÓ¦¸Ã±»À­¸ßµ½¡°ÖØÆô¡±µÄÀà¼ÓÔØÆ÷µÄÏîÄ¿£¬ÒÔ¼°excludeÒªËØÊÇÓ¦¸ÃÏòÏÂÍÆÈë¡°Base¡±Àà¼ÓÔØÆ÷µÄÏîÄ¿¡£¸ÃÊôÐÔµÄÖµÊÇÓ¦ÓÃÓÚÀà·¾¶µÄÕýÔò±í´ïʽģʽ£¬ÈçÒÔÏÂʾÀýËùʾ£º + + + +``` +restart: + exclude: + companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar" + include: + projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar" + +``` + + + +¸ü¶àÏà¹ØµÄÐÅÏ¢¿ÉÒÔÔÚ[ÕâÀïÔÚд°¿Ú´ò¿ª](https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.devtools)²é¿´¡£ + +### [#](#devtoolÊÇ·ñ»á±»´ò°ü½øjar) devtoolÊÇ·ñ»á±»´ò°ü½øJar£¿ + +> devtoolÔ­ÔòÉÏÀ´ËµÓ¦¸ÃÊÇÖ»ÔÚ¿ª·¢µ÷ÊÔµÄʱºòʹÓ㬶øÔÚÉú²ú»·¾³ÔËÐÐjar°üʱÊDz»ÐèÒªµÄ£¬ËùÒÔSpring´ò°ü»á²»»á°ÑËü´ò½øJARÂ𣿠+ +* **ĬÈÏÇé¿öÏ£¬²»»á±»´ò°ü½øJAR** + +ÔËÐдò°üµÄÓ¦ÓóÌÐòʱ£¬¿ª·¢ÈËÔ±¹¤¾ß**»á×Ô¶¯½ûÓÃ**¡£Èç¹ûÄãͨ¹ý java -jar»òÕ߯äËûÌØÊâµÄÀà¼ÓÔØÆ÷½øÐÐÆô¶¯Ê±£¬¶¼»á±»ÈÏΪÊÇ¡°Éú²ú»·¾³µÄÓ¦Óᱡ£ + +* **Èç¹ûÎÒÃÇÆÚÍûÔ¶³Ìµ÷ÊÔÓ¦ÓÃ** + +£¨_Éú²ú»·¾³ÎðÓã¬Ö»ÓÐÔÚÊÜÐÅÈεÄÍøÂçÉÏÔËÐлòʹÓà SSL ½øÐб£»¤Ê±£¬²ÅÓ¦ÆôÓÃËü_£© + +ÔÚÕâÖÖÇé¿öÏ£¬devtoolÒ²¾ß±¸Ô¶³Ìµ÷ÊÔµÄÄÜÁ¦£ºÔ¶³Ì¿Í»§¶ËÓ¦ÓóÌÐòÖ¼ÔÚ´ÓÄãµÄ IDE ÖÐÔËÐС£ÄãÐèÒªorg.springframework.boot.devtools.RemoteSpringApplicationʹÓÃÓëÄãÁ¬½ÓµÄÔ¶³ÌÏîÄ¿ÏàͬµÄÀà·¾¶ÔËÐС£Ó¦ÓóÌÐòµÄΨһ±ØÐè²ÎÊýÊÇËüÁ¬½Óµ½µÄÔ¶³Ì URL¡£ + +ÀýÈ磬Èç¹ûʹÓà Eclipse »ò Spring Tools£¬²¢ÇÒÄãÓÐÒ»¸ömy-appÒѲ¿Êðµ½ Cloud Foundry µÄÃûΪµÄÏîÄ¿£¬Ö´ÐÐÒÔϲÙ×÷£º + +1. Ñ¡ÔñRun Configurations¡­?´ÓRun²Ëµ¥¡£ +2. ´´½¨Ò»¸öеÄJava Application¡°Æô¶¯ÅäÖᱡ£ +3. ä¯ÀÀmy-appÏîÄ¿¡£ +4. ʹÓÃorg.springframework.boot.devtools.RemoteSpringApplication×÷ΪÖ÷Àà¡£ +5. Ìí¼Óhttps://myapp.cfapps.ioµ½Program arguments£¨»òÈκÎÄãµÄÔ¶³Ì URL£©¡£ + +ÕýÔÚÔËÐеÄÔ¶³Ì¿Í»§¶Ë¿ÉÄÜÀàËÆÓÚÒÔÏÂÁÐ±í£º + + + +``` + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \ + \\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / / + =========|_|==============|___/===================================/_/_/_/ + :: Spring Boot Remote :: 2.5.4 + +2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-project/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code) +2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy +2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'. +2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729 +2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105) + +``` + + + +### [#](#devtoolΪºÎ»áĬÈϽûÓûº´æÑ¡Ïî) devtoolΪºÎ»áĬÈϽûÓûº´æÑ¡Ï + +> Spring Boot Ö§³ÖµÄһЩ¿â**ʹÓûº´æÀ´Ìá¸ßÐÔÄÜ**¡£ÀýÈ磬ģ°åÒýÇæ»º´æÒѱàÒëµÄÄ£°åÒÔ±ÜÃâÖØ¸´½âÎöÄ£°åÎļþ¡£´ËÍ⣬Spring MVC ¿ÉÒÔÔÚÌṩ¾²Ì¬×ÊԴʱÏòÏìÓ¦Ìí¼Ó HTTP »º´æ±êÍ·¡£ + +ËäÈ»»º´æ**ÔÚÉú²úÖзdz£ÓÐÒæ£¬µ«ÔÚ¿ª·¢¹ý³ÌÖпÉÄÜ»áÊÊµÃÆä·´**£¬Ê¹ÄãÎÞ·¨¿´µ½¸Õ¸ÕÔÚÓ¦ÓóÌÐòÖÐËù×öµÄ¸ü¸Ä¡£³öÓÚÕâ¸öÔ­Òò£¬ spring-boot-devtools ĬÈϽûÓûº´æÑ¡Ïî¡£ + +±ÈÈçThymeleaf ÌṩÁËspring.thymeleaf.cacheÀ´ÉèÖÃÄ£°åÒýÇæµÄ»º´æ£¬Ê¹ÓÃspring-boot-devtoolsÄ£¿éʱÊDz»ÐèÒªÊÖ¶¯ÉèÖÃÕâЩÊôÐԵģ¬ÒòΪspring-boot-devtools»á×Ô¶¯½øÐÐÉèÖᣠ+ +ÄÇô»á×Ô¶¯ÉèÖÃÄÄЩÅäÖÃÄØ£¿Äã¿ÉÒÔÔÚDevToolsPropertyDefaultsPostProcessorÀàÕÒµ½¶ÔÓ¦µÄĬÈÏÅäÖᣠ+ + + +``` +public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor { + + static { + Map properties = new HashMap<>(); + properties.put("spring.thymeleaf.cache", "false"); + properties.put("spring.freemarker.cache", "false"); + properties.put("spring.groovy.template.cache", "false"); + properties.put("spring.mustache.cache", "false"); + properties.put("server.servlet.session.persistent", "true"); + properties.put("spring.h2.console.enabled", "true"); + properties.put("spring.web.resources.cache.period", "0"); + properties.put("spring.web.resources.chain.cache", "false"); + properties.put("spring.template.provider.cache", "false"); + properties.put("spring.mvc.log-resolved-exception", "true"); + properties.put("server.error.include-binding-errors", "ALWAYS"); + properties.put("server.error.include-message", "ALWAYS"); + properties.put("server.error.include-stacktrace", "ALWAYS"); + properties.put("server.servlet.jsp.init-parameters.development", "true"); + properties.put("spring.reactor.debug", "true"); + PROPERTIES = Collections.unmodifiableMap(properties); + } + +``` + + + +µ±È»Èç¹ûÄã²»Ïë±»Ó¦ÓÃÊôÐÔ±»spring-boot-devtoolsĬÈÏÉèÖ㬠¿ÉÒÔͨ¹ýspring.devtools.add-propertiesµ½falseÄãapplication.ymlÖС£ + +### [#](#devtoolÊÇ·ñ¿ÉÒÔ¸øËùÓÐspringbootÓ¦ÓÃ×öÈ«¾ÖµÄÅäÖÃ) devtoolÊÇ·ñ¿ÉÒÔ¸øËùÓÐSpringbootÓ¦ÓÃ×öÈ«¾ÖµÄÅäÖã¿ + +> ¿ÉÒÔͨ¹ý½«spring-boot-devtools.ymlÎļþÌí¼Óµ½$HOME/.config/spring-bootĿ¼À´**ÅäÖÃÈ«¾Ö devtools ÉèÖÃ**¡£ + +Ìí¼Óµ½ÕâЩÎļþµÄÈκÎÊôÐÔ¶¼ÊÊÓÃÓÚÄã»úÆ÷ÉÏʹÓà devtools µÄËùÓÐSpring Boot Ó¦ÓóÌÐò¡£ÀýÈ磬Ҫ½«ÖØÐÂÆô¶¯ÅäÖÃΪʼÖÕʹÓô¥·¢Æ÷Îļþ£¬ÄãÐèÒª½«ÒÔÏÂÊôÐÔÌí¼Óµ½ÄãµÄspring-boot-devtoolsÎļþÖУº + + + +``` +spring: + devtools: + restart: + trigger-file: ".reloadtrigger" + +``` + + + +### [#](#Èç¹ûÎÒ²»ÓÃdevtool-»¹ÓÐʲôѡÔñ) Èç¹ûÎÒ²»ÓÃdevtool£¬»¹ÓÐʲôѡÔñ£¿ + +> Èç¹ûÎÒ²»ÓÃdevtool£¬»¹ÓÐʲôѡÔñ£¿ + +**ÔÚʵ¼ÊµÄ¿ª·¢¹ý³ÌÖУ¬ÎÒÒ²²»»áȥʹÓÃdevtool¹¤¾ß**, ÒòΪ£º + +* devtool±¾Éí»ùÓÚÖØÆô·½Ê½£¬ÕâÖÖÈÔÈ»²»ÊÇÕæÕýµÄÈÈÌæ»»·½°¸£¬JRebel²ÅÊÇ£¨ËüÊÇÊշѵģ© +* ¿ª·¢µ÷ÊÔ×îÖØÒªµÄ»¹ÊÇÒ»ÖÖȨºâ + * ×Ô¶¯ÖØÆôµÄ¿ªÏúÈç¹ûºÍÊÖ¶¯ÖØÆôûÓÐʲô̫´ó²î±ð£¬ÄÇô»¹²»ÈçÊÖ¶¯ÖØÆô£¨°´ÐèÖØÆô£© + * ¶àÊýÇé¿öÏ£¬Èç¹ûÊÇ**·½·¨ÄÚ²¿µÄÐ޸ĻòÕß¾²Ì¬×ÊÔ´µÄÐÞ¸Ä**£¬ÔÚIDEAÖÐÊÇ¿ÉÒÔͨ¹ýRebuild£¨Ctrl + Shift + F9£©½øÐÐÈȸüµÄ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416211745.png) + +* ´ËÍ⻹ÓÐÒ»¸ö¹¤¾ßspring loaded£¬ ¿ÉʵÏÖÐÞ¸ÄÀàÎļþµÄÈȲ¿Ê𣬾ßÌå¿É¿´Æä[githubµØÖ·ÔÚд°¿Ú´ò¿ª](https://github.com/spring-projects/spring-loaded)ÉϵÄ˵Ã÷¡£ + +## [#](#ʾÀýÔ´Âë) ʾÀýÔ´Âë + +https://github.com/realpdai/tech-pdai-spring-demos + +## [#](#²Î¿¼ÎÄÕÂ) ²Î¿¼ÎÄÕ + +https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.devtools + +https://liayun.blog.csdn.net/article/details/116541775 + +* * * + +Öø×÷Ȩ¹é@pdaiËùÓÐ Ô­ÎÄÁ´½Ó£ºhttps://pdai.tech/md/spring/springboot/springboot-x-hello-devtool.html \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\351\233\206\346\210\220Swagger\345\256\236\347\216\260API\346\226\207\346\241\243\350\207\252\345\212\250\347\224\237\346\210\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\351\233\206\346\210\220Swagger\345\256\236\347\216\260API\346\226\207\346\241\243\350\207\252\345\212\250\347\224\237\346\210\220.md" new file mode 100644 index 0000000..e225d74 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/SpringBoot\351\233\206\346\210\220Swagger\345\256\236\347\216\260API\346\226\207\346\241\243\350\207\252\345\212\250\347\224\237\346\210\220.md" @@ -0,0 +1,625 @@ + + +Ŀ¼ + +* [SwaggerµÄ½éÉÜ](https://www.cnblogs.com/progor/p/13297904.html#swagger%E7%9A%84%E4%BB%8B%E7%BB%8D) + * [ÓŵãÓëȱµã](https://www.cnblogs.com/progor/p/13297904.html#%E4%BC%98%E7%82%B9%E4%B8%8E%E7%BC%BA%E7%82%B9) +* [Ìí¼Óswagger](https://www.cnblogs.com/progor/p/13297904.html#%E6%B7%BB%E5%8A%A0swagger) + * [1.Ìí¼ÓÒÀÀµ°ü£º](https://www.cnblogs.com/progor/p/13297904.html#1%E6%B7%BB%E5%8A%A0%E4%BE%9D%E8%B5%96%E5%8C%85) + * [2.ÅäÖÃSwagger:](https://www.cnblogs.com/progor/p/13297904.html#2%E9%85%8D%E7%BD%AEswagger) + * [3.²âÊÔ](https://www.cnblogs.com/progor/p/13297904.html#3%E6%B5%8B%E8%AF%95) +* [³¡¾°£º](https://www.cnblogs.com/progor/p/13297904.html#%E5%9C%BA%E6%99%AF) + * [¶¨Òå½Ó¿Ú×é](https://www.cnblogs.com/progor/p/13297904.html#%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E7%BB%84) + * [¶¨Òå½Ó¿Ú](https://www.cnblogs.com/progor/p/13297904.html#%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3) + * [¶¨Òå½Ó¿ÚÇëÇó²ÎÊý](https://www.cnblogs.com/progor/p/13297904.html#%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) + * [³¡¾°Ò»£ºÇëÇó²ÎÊýÊÇʵÌåÀà¡£](https://www.cnblogs.com/progor/p/13297904.html#%E5%9C%BA%E6%99%AF%E4%B8%80%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E6%98%AF%E5%AE%9E%E4%BD%93%E7%B1%BB) + * [³¡¾°¶þ£ºÇëÇó²ÎÊýÊÇ·ÇʵÌåÀà¡£](https://www.cnblogs.com/progor/p/13297904.html#%E5%9C%BA%E6%99%AF%E4%BA%8C%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E6%98%AF%E9%9D%9E%E5%AE%9E%E4%BD%93%E7%B1%BB) + * [¶¨Òå½Ó¿ÚÏìÓ¦](https://www.cnblogs.com/progor/p/13297904.html#%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E5%93%8D%E5%BA%94) + * [ÏìÓ¦ÊÇʵÌåÀࣺ](https://www.cnblogs.com/progor/p/13297904.html#%E5%93%8D%E5%BA%94%E6%98%AF%E5%AE%9E%E4%BD%93%E7%B1%BB) + * [ÏìÓ¦ÊÇ·ÇʵÌåÀࣺ](https://www.cnblogs.com/progor/p/13297904.html#%E5%93%8D%E5%BA%94%E6%98%AF%E9%9D%9E%E5%AE%9E%E4%BD%93%E7%B1%BB) +* [Swagger UIÔöÇ¿](https://www.cnblogs.com/progor/p/13297904.html#swagger-ui%E5%A2%9E%E5%BC%BA) + * [UI¶Ô±È£º](https://www.cnblogs.com/progor/p/13297904.html#ui%E5%AF%B9%E6%AF%94) + * [ʹÓÃ](https://www.cnblogs.com/progor/p/13297904.html#%E4%BD%BF%E7%94%A8) + * [Óŵã](https://www.cnblogs.com/progor/p/13297904.html#%E4%BC%98%E7%82%B9) +* [ÕûºÏSpring Security×¢Òâ](https://www.cnblogs.com/progor/p/13297904.html#%E6%95%B4%E5%90%88spring-security%E6%B3%A8%E6%84%8F) +* [¶ÔÓÚtokenµÄ´¦Àí](https://www.cnblogs.com/progor/p/13297904.html#%E5%AF%B9%E4%BA%8Etoken%E7%9A%84%E5%A4%84%E7%90%86) +* [SwaggerµÄ°²È«¹ÜÀí](https://www.cnblogs.com/progor/p/13297904.html#swagger%E7%9A%84%E5%AE%89%E5%85%A8%E7%AE%A1%E7%90%86) + + + +* * * + +# SwaggerµÄ½éÉÜ + +?Äã¿ÉÄܳ¢ÊÔ¹ýдÍêÒ»¸ö½Ó¿Úºó£¬×Ô¼ºÈ¥´´½¨½Ó¿ÚÎĵµ£¬»òÕßÐ޸ĽӿںóÐ޸ĽӿÚÎĵµ¡£¶àÁËÖ®ºó£¬Äã¿Ï¶¨»á·¢ÉúÒ»¸ö²Ù×÷£¬ÄǾÍÊÇÍü¼ÇÁËÐÞ¸ÄÎĵµ»òÕß´´½¨Îĵµ£¨³ý·ÇÄãÃǹ«Ë¾°Ñ½Ó¿ÚÎĵµºÍд½Ó¿ÚÒªÇóµÃºÜ½ôÃÜ?Íü¼ÇдÎĵµ¾Í¿Û¹¤×Ê£¿£¬·ñÔòÁ½¸ö·ÖÀëµÄ¹¤×÷×ÜÊÇÓпÉÄÜÒÅ©µÄ£©¡£¶øswagger¾ÍÊÇÒ»¸öÔÚÄãд½Ó¿ÚµÄʱºò×Ô¶¯°ïÄãÉú³É½Ó¿ÚÎĵµµÄ¶«Î÷£¬Ö»ÒªÄã×ñÑ­ËüµÄ¹æ·¶²¢Ð´Ò»Ð©½Ó¿ÚµÄ˵Ã÷×¢½â¼´¿É¡£ + +## ÓŵãÓëȱµã + +?Óŵ㣺 + +* ×Ô¶¯Éú³ÉÎĵµ£¬Ö»ÐèÒªÔÚ½Ó¿ÚÖÐʹÓÃ×¢½â½øÐбê×¢£¬¾ÍÄÜÉú³É¶ÔÓ¦µÄ½Ó¿ÚÎĵµ¡£ +* ×Ô¶¯¸üÐÂÎĵµ£¬ÓÉÓÚÊǶ¯Ì¬Éú³ÉµÄ£¬ËùÒÔÈç¹ûÄãÐÞ¸ÄÁ˽ӿڣ¬ÎĵµÒ²»á×Ô¶¯¶ÔÓ¦Ð޸ģ¨Èç¹ûÄãÒ²¸üÐÂÁË×¢½âµÄ»°£©¡£ÕâÑù¾Í²»»á·¢ËÍÎÒÐÞ¸ÄÁ˽ӿڣ¬È´Íü¼Ç¸üнӿÚÎĵµµÄÇé¿ö¡£ +* Ö§³ÖÔÚÏßµ÷ÊÔ£¬swaggerÌṩÁËÔÚÏßµ÷ÓýӿڵŦÄÜ¡£ + +?ȱµã£º + +* ²»ÄÜ´´½¨²âÊÔÓÃÀý£¬ËùÒÔËûÔÝʱ²»ÄܰïÄã´¦ÀíÍêËùÓеÄÊÂÇé¡£ËûÖ»ÄÜÌṩһ¸ö¼òµ¥µÄÔÚÏßµ÷ÊÔ£¬Èç¹ûÄãÏë´æ´¢ÄãµÄ²âÊÔÓÃÀý£¬¿ÉÒÔʹÓÃPostman»òÕßYAPIÕâÑùÖ§³Ö´´½¨²âÊÔÓû§µÄ¹¦ÄÜ¡£ +* Òª×ñѭһЩ¹æ·¶£¬Ëü²»ÊÇÈÎÒâ¹æ·¶µÄ¡£±ÈÈç˵£¬Äã¿ÉÄܻ᷵»ØÒ»¸öjsonÊý¾Ý£¬¶øÕâ¸öÊý¾Ý¿ÉÄÜÊÇÒ»¸öMap¸ñʽµÄ£¬ÄÇôÎÒÃÇ´Ëʱ²»Äܱê×¢Õâ¸öMap¸ñʽµÄ·µ»ØÊý¾ÝµÄÿ¸ö×ֶεÄ˵Ã÷£¬¶øÈç¹ûËüÊÇÒ»¸öʵÌåÀàµÄ»°£¬ÎÒÃÇ¿ÉÒÔͨ¹ý±ê×¢ÀàµÄÊôÐÔÀ´¸ø·µ»Ø×ֶμÓ˵Ã÷¡£Ò²±ÈÈç˵£¬¶ÔÓÚswagger£¬²»ÍƼöÔÚʹÓÃGET·½Ê½Ìá½»Êý¾ÝµÄʱºò»¹Ê¹ÓÃBody£¬½öÍÆ¼öʹÓÃquery²ÎÊý¡¢header²ÎÊý»òÕß·¾¶²ÎÊý£¬µ±È»ÁËÕâ¸öÏÞÖÆÖ»ÊÊÓÃÓÚÔÚÏßµ÷ÊÔ¡£ +* ûÓнӿÚÎĵµ¸üйÜÀí£¬ËäȻһ¸ö½Ó¿Ú¸üÐÂÖ®ºó£¬¿ÉÄܲ»»á¹ØÐľɰæµÄ½Ó¿ÚÐÅÏ¢£¬µ«Äã¡°¿ÉÄÜ¡±Ïë¿´¿´¾É°æµÄ½Ó¿ÚÐÅÏ¢£¬ÀýÈçÓÐЩ»Ò¶È¸üз¢²¼µÄʱºò¿ÉÄÜ»¹»á¹ØÐľɰæµÄ½Ó¿Ú¡£ÄÇô´ËʱֻÄÜÓɺó¶ËÈ¥¿´¿´ÓÐûÓÐ×¢ÊÍÁôÏÂÁË£¬ËùÒÔ¿ÉÒÔ¿¼ÂǽӿÚÎĵµ´ó¸üеÄʱºò×¢Ê;ɰæµÄ£¬È»ºóдÏÂаæµÄ¡£¡¾µ±È»Õâ¸öÎÊÌâ¿ÉÒÔͨ¹ýµ¼³ö½Ó¿ÚÎĵµÀ´¶Ô±È¡£¡¿ +* ËäÈ»ÏÖÔÚJavaµÄʵÌåÀàÖÐÓв»ÉÙÄ£ÐÍ£¬po,dto,voµÈ£¬Ä£Ð͵ÄÇø·ÖÊÇΪÁËÆÁ±ÎһЩ¶àÓà²ÎÊý£¬±ÈÈçÒ»¸öÓû§µÇ¼µÄʱºòÖ»ÐèÒªusername,password£¬µ«²éȨÏÞµÄʱºòÐèÒªÁ¬½ÓÉÏȨÏÞ±íµÄÐÅÏ¢£¬¶øÈç¹ûÉÏÊöÁ½¸ö²Ù×÷¶¼ÊÇʹÓÃÁËUserÕâ¸öʵÌåµÄ»°£¬ÔÚÎĵµÖоͻá×Ô¶¯Éú³ÉÁ˶àÓàµÄÐÅÏ¢£¬Õâ¾ÍÒªÇóÁËÄã»ùÓÚÄ£ÐÍÀ´´´½¨¶à¸öʵÌåÀ࣬±ÈÈçµÇ¼µÄʱºòÒ»¸öLoginForm£¬ÐèÒªÓû§-ȨÏÞµÈÐÅÏ¢µÄʱºò²ÅʹÓÃUserÀà¡££¨µ±È»ÁË£¬Õâ¸öÎÊÌâµÈÄã»áswaggerÖ®ºóÄã¾Í´ó¸Å¾Í»áÔõô¹æ±ÜÕâ¸öÎÊÌâÁË¡££© + +?ÉÏÃæµÄȱµãºÃÏñдµÄÓеã¶à£¬Äã¿ÉÄÜ»á¾õµÃswaggerÕâ¸ö¿ÓÓеã´ó¡£µ«ÆäʵÖ÷ÒªÊǹ淶ÎÊÌ⣬¶ø¹æ·¶ÎÊÌâÓÐʱºòÓÖ»áÌá¸ßÄãµÄ´úÂë¹æ·¶ÐÔ£¬Õâ¸ö¾Í¼ûÈʼûÖÇÁË£¬ÄãÒÔǰ¿ÉÄÜʲô½Ó¿ÚµÄ²ÎÊý¶¼Ê¹ÓÃÒ»¸öÀ࣬¶øÏÖÔÚswaggerÒªÇóÄã·Ö¿ªºó£¬Ä³ÖÖ²ã´ÎÉÏÌá¸ßÁËÄãµÄ´úÂë¹æ·¶ÐÔ¡£ + +?×¢£ºÒÔÏ´úÂëʾÀý»ùÓÚSpring Boot¡£ÍêÕû´úÂë¿ÉÒԲο¼£º[swagger-demo](https://github.com/alprogor/swagger-demo) + +* * * + +# Ìí¼Óswagger + +?ÕâÀïÏȽ²Ìí¼Óswagger£¬Ò²¾ÍÊÇÏÈÕûºÏ½øÀ´£¬ÖÁÓÚÔõôʹÓã¬ÏÂÃæµÄ¡°³¡¾°¡±ÖÐÔÙ½²½â¡£ + +## 1.Ìí¼ÓÒÀÀµ°ü£º + +?×¢Ò⣬ÕâÀïµÄǰÌáÊÇÒѾ­µ¼ÈëÁËspring bootµÄweb°ü¡£ + +¸´ÖÆ´úÂë + +``` + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + +``` + +## 2.ÅäÖÃSwagger: + +ҪʹÓÃswagger£¬ÎÒÃDZØÐë¶Ôswagger½øÐÐÅäÖã¬ÎÒÃÇÐèÒª´´½¨Ò»¸öswaggerµÄÅäÖÃÀ࣬±ÈÈç¿ÉÒÔÃüÃûΪSwaggerConfig.java + +¸´ÖÆ´úÂë + +``` +package com.example.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration // ±êÃ÷ÊÇÅäÖÃÀà +@EnableSwagger2 //¿ªÆôswagger¹¦ÄÜ +public class SwaggerConfig { + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) // DocumentationType.SWAGGER_2 ¹Ì¶¨µÄ£¬´ú±íswagger2 +// .groupName("·Ö²¼Ê½ÈÎÎñϵͳ") // Èç¹ûÅäÖöà¸öÎĵµµÄʱºò£¬ÄÇôÐèÒªÅäÖÃgroupNameÀ´·Ö×é±êʶ + .apiInfo(apiInfo()) // ÓÃÓÚÉú³ÉAPIÐÅÏ¢ + .select() // select()º¯Êý·µ»ØÒ»¸öApiSelectorBuilderʵÀý,ÓÃÀ´¿ØÖƽӿڱ»swagger×ö³ÉÎĵµ + .apis(RequestHandlerSelectors.basePackage("com.example.controller")) // ÓÃÓÚÖ¸¶¨É¨ÃèÄĸö°üÏÂµÄ½Ó¿Ú + .paths(PathSelectors.any())// Ñ¡ÔñËùÓеÄAPI,Èç¹ûÄãÏëֻΪ²¿·ÖAPIÉú³ÉÎĵµ£¬¿ÉÒÔÅäÖÃÕâÀï + .build(); + } + + /** + * ÓÃÓÚ¶¨ÒåAPIÖ÷½çÃæµÄÐÅÏ¢£¬±ÈÈç¿ÉÒÔÉùÃ÷ËùÓеÄAPIµÄ×ܱêÌâ¡¢ÃèÊö¡¢°æ±¾ + * @return + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("XXÏîÄ¿API") // ¿ÉÒÔÓÃÀ´×Ô¶¨ÒåAPIµÄÖ÷±êÌâ + .description("XXÏîÄ¿SwaggerAPI¹ÜÀí") // ¿ÉÒÔÓÃÀ´ÃèÊöÕûÌåµÄAPI + .termsOfServiceUrl("") // ÓÃÓÚ¶¨Òå·þÎñµÄÓòÃû + .version("1.0") // ¿ÉÒÔÓÃÀ´¶¨Òå°æ±¾¡£ + .build(); // + } +} + +``` + +## 3.²âÊÔ + +ÔËÐÐÎÒÃǵÄSpring BootÏîÄ¿£¬£¨ÎÒĬÈÏÊÇ8080¶Ë¿Ú£¬Èç¹ûÄã²»Ò»Ñù£¬Çë×¢ÒâÐ޸ĺóÐøµÄurl£©£¬·ÃÎÊ`http://localhost:8080/swagger-ui.html` +È»ºóÄã¾Í¿ÉÒÔ¿´µ½Ò»¸öÈçϵĽçÃæ£¬ÓÉÓÚÎÒÃÇÔÝʱûÓÐÅäÖýӿÚÊý¾Ý£¬ËùÒÔÏÂÃæÏÔʾ`No operations defined in spec!` + +![20200711013419](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200711013419.png) + +?ÏÂÃæÎÒÃǽ«½éÉÜÈçºÎ¶¨Òå½Ó¿Ú£¬ÒÔ¼°ÔÚswagger UI½çÃæÖеÄÄÚÈÝ¡£ + +* * * + +# ³¡¾°£º + +## ¶¨Òå½Ó¿Ú×é + +½Ó¿ÚÓÐʱºòÓ¦¸ÃÊÇ·Ö×éµÄ£¬¶øÇҴ󲿷ֶ¼ÊÇÔÚÒ»¸öcontrollerÖе쬱ÈÈçÓû§¹ÜÀíÏà¹ØµÄ½Ó¿ÚÓ¦¸Ã¶¼ÔÚUserControllerÖУ¬ÄÇô²»Í¬µÄÒµÎñµÄʱºò£¬Ó¦¸Ã¶¨Òå/»®·Ö²»Í¬µÄ½Ó¿Ú×é¡£½Ó¿Ú×é¿ÉÒÔʹÓÃ`@Api`À´»®·Ö¡£ +±ÈÈ磺 + +¸´ÖÆ´úÂë + +``` +@Api(tags = "½ÇÉ«¹ÜÀí") // tags£ºÄã¿ÉÒÔµ±×÷ÊÇÕâ¸ö×éµÄÃû×Ö¡£ +@RestController +public class RoleController { +} + +``` + +ºÍ + +¸´ÖÆ´úÂë + +``` +@Api(tags = "Óû§¹ÜÀí") // tags£ºÄã¿ÉÒÔµ±×÷ÊÇÕâ¸ö×éµÄÃû×Ö¡£ +@RestController +public class UserController { +} + +``` + +?ÄãÒ²¿ÉÒÔÀí½â³É»ùÓÚtagsÀ´·Ö×飬¾ÍºÃÏñһЩÎÄÕÂÀïÃæµÄ±êǩһÑù£¬Ê¹ÓñêÇ©À´·ÖÀà¡£ +?Èç¹ûÕâ¸öControllerÏ£¨½Ó¿Ú×飩ÏÂÃæÃ»Óнӿڣ¬ÄÇôÔÚswagger uiÖÐÊDz»»áÏÔʾµÄ£¬Èç¹ûÓеϰ¾Í»áÕâÑùÏÔʾ£º +![20200712022545](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712022545.png) + +## ¶¨Òå½Ó¿Ú + +ʹÓÃÁË`@Api`À´±ê×¢Ò»¸öControllerÖ®ºó£¬Èç¹ûÏÂÃæÓнӿڣ¬ÄÇô¾Í»áĬÈÏÉú³ÉÎĵµ£¬µ«Ã»ÓÐÎÒÃÇ×Ô¶¨ÒåµÄ˵Ã÷£º + +¸´ÖÆ´úÂë + +``` +@Api(tags = "Óû§¹ÜÀí") +@RestController +public class UserController { + // ×¢Ò⣬¶ÔÓÚswagger£¬²»ÒªÊ¹ÓÃ@RequestMapping£¬ + // ÒòΪ@RequestMappingÖ§³ÖÈÎÒâÇëÇó·½Ê½£¬swagger»áΪÕâ¸ö½Ó¿ÚÉú³É7ÖÖÇëÇó·½Ê½µÄ½Ó¿ÚÎĵµ + @GetMapping("/info") + public String info(String id){ + return "aaa"; + } +} + +``` + +![20200711015840](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200711015840.png) + +![20200711020149](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200711020149.png) + +ÎÒÃÇ¿ÉÒÔʹÓÃ`@ApiOperation`À´ÃèÊö½Ó¿Ú£¬±ÈÈ磺 + +¸´ÖÆ´úÂë + +``` + @ApiOperation(value = "Óû§²âÊÔ",notes = "Óû§²âÊÔnotes") + @GetMapping("/test") + public String test(String id){ + return "test"; + } + +``` + +![20200711021112](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200711021112.png) +³£ÓÃÅäÖÃÏ + +* value£º¿ÉÒÔµ±×÷Êǽӿڵļò³Æ +* notes£º½Ó¿ÚµÄÃèÊö +* tags£º¿ÉÒÔ¶îÍⶨÒå½Ó¿Ú×飬±ÈÈçÕâ¸ö½Ó¿ÚÍâ²ãÒѾ­ÓÐ`@Api(tags = "Óû§¹ÜÀí")`£¬½«½Ó¿Ú»®·Öµ½ÁË¡°Óû§¹ÜÀí¡±ÖУ¬µ«Äã¿ÉÒÔ¶îÍâµÄʹÓÃtags£¬ÀýÈç`tags = "½ÇÉ«¹ÜÀí"`ÈýÇÉ«¹ÜÀíÖÐÒ²ÓÐÕâ¸ö½Ó¿ÚÎĵµ¡£ + +## ¶¨Òå½Ó¿ÚÇëÇó²ÎÊý + +ÉÏÃæÊ¹ÓÃÁË`@ApiOperation`À´ÁËÃèÊö½Ó¿Ú£¬µ«Æäʵ»¹È±ÉÙ½Ó¿ÚÇëÇó²ÎÊýµÄ˵Ã÷£¬ÏÂÃæÎÒÃÇ·Ö³¡¾°À´½²¡£ +?×¢Òâһϣ¬**¶ÔÓÚGET·½Ê½£¬swagger²»ÍƼöʹÓÃbody·½Ê½À´´«µÝÊý¾Ý**£¬Ò²¾ÍÊDz»Ï£ÍûÔÚGET·½Ê½Ê±Ê¹ÓÃjson¡¢form-dataµÈ·½Ê½À´´«µÝ£¬Õâʱºò×îºÃʹÓ÷¾¶²ÎÊý»òÕßurl²ÎÊý¡£(?ËäÈ»POSTMANµÈÊÇÖ§³ÖµÄ)£¬ËùÒÔÈç¹û½Ó¿Ú´«µÝµÄÊý¾ÝÊÇjson»òÕßform-data·½Ê½µÄ£¬»¹ÊÇʹÓÃPOST·½Ê½ºÃ¡£ + +### ³¡¾°Ò»£ºÇëÇó²ÎÊýÊÇʵÌåÀà¡£ + +´ËʱÎÒÃÇÐèҪʹÓÃ`@ApiModel`À´±êעʵÌåÀ࣬ȻºóÔÚ½Ó¿ÚÖж¨ÒåÈë²ÎΪʵÌåÀ༴¿É£º + +* @ApiModel£ºÓÃÀ´±êÀà + * ³£ÓÃÅäÖÃÏ + * value£ºÊµÌåÀà¼ò³Æ + * description£ºÊµÌåÀà˵Ã÷ +* @ApiModelProperty£ºÓÃÀ´ÃèÊöÀàµÄ×ֶεÄÒâÒå¡£ + * ³£ÓÃÅäÖÃÏ + * value£º×Ö¶Î˵Ã÷ + * example£ºÉèÖÃÇëÇóʾÀý£¨Example Value£©µÄĬÈÏÖµ£¬Èç¹û²»ÅäÖ㬵±×Ö¶ÎΪstringµÄʱºò£¬´ËʱÇëÇóʾÀýÖÐĬÈÏֵΪ"". + * name£ºÓÃеÄ×Ö¶ÎÃûÀ´Ìæ´ú¾ÉµÄ×Ö¶ÎÃû¡£ + * allowableValues£ºÏÞÖÆÖµµÃ·¶Î§£¬ÀýÈç`{1,2,3}`´ú±íÖ»ÄÜÈ¡ÕâÈý¸öÖµ£»`[1,5]`´ú±íÈ¡1µ½5µÄÖµ£»`(1,5)`´ú±í1µ½5µÄÖµ£¬²»°üÀ¨1ºÍ5£»»¹¿ÉÒÔʹÓÃinfinity»ò-infinityÀ´ÎÞÏÞÖµ£¬±ÈÈç`[1, infinity]`´ú±í×îСֵΪ1£¬×î´óÖµÎÞÇî´ó¡£ + * required£º±ê¼Ç×Ö¶ÎÊÇ·ñ±ØÌĬÈÏÊÇfalse, + * hidden£ºÓÃÀ´Òþ²Ø×ֶΣ¬Ä¬ÈÏÊÇfalse£¬Èç¹ûÒªÒþ²ØÐèҪʹÓÃtrue£¬ÒòΪ×Ö¶ÎĬÈ϶¼»áÏÔʾ£¬¾ÍËãûÓÐ`@ApiModelProperty`¡£ + +¸´ÖÆ´úÂë + +``` +// ÏÈʹÓÃ@ApiModelÀ´±ê×¢Àà +@ApiModel(value="Óû§µÇ¼±íµ¥¶ÔÏó",description="Óû§µÇ¼±íµ¥¶ÔÏó") +public class LoginForm { + // ʹÓÃApiModelPropertyÀ´±ê×¢×Ö¶ÎÊôÐÔ¡£ + @ApiModelProperty(value = "Óû§Ãû",required = true,example = "root") + private String username; + @ApiModelProperty(value = "ÃÜÂë",required = true,example = "123456") + private String password; + + // ´Ë´¦Ê¡ÂÔÈë²Î¸³ÖµÊ±ÐèÒªµÄgetter,setter,swaggerÒ²ÐèÒªÕâ¸ö +} + +``` + +¶¨Òå³ÉÈë²Î£º + +¸´ÖÆ´úÂë + +``` + @ApiOperation(value = "µÇ¼½Ó¿Ú",notes = "µÇ¼½Ó¿ÚµÄ˵Ã÷") + @PostMapping("/login") + public LoginForm login(@RequestBody LoginForm loginForm){ + return loginForm; + } + +``` + +Ч¹û£º + +![20200711181038](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200711181038.png) + +### ³¡¾°¶þ£ºÇëÇó²ÎÊýÊÇ·ÇʵÌåÀà¡£ + +£¨**ÔÙ˵һ´Î£º¶ÔÓÚGET·½Ê½£¬swagger²»ÍƼöʹÓÃbody·½Ê½À´´«µÝÊý¾Ý£¬ËùÒÔËäÈ»Spring MVC¿ÉÒÔ×Ô¶¯·â×°²ÎÊý£¬µ«¶ÔÓÚGETÇëÇó»¹ÊDz»ÒªÊ¹ÓÃform-data£¬jsonµÈ·½Ê½´«µÝ²ÎÊý£¬³ý·ÇÄãʹÓÃPostmanÀ´²âÊÔ½Ó¿Ú£¬swaggerÔÚÏß²âÊÔÊDz»Ö§³ÖÕâ¸ö²Ù×÷µÄ**£© +¶ÔÓÚ·ÇʵÌåÀà²ÎÊý£¬¿ÉÒÔʹÓÃ`@ApiImplicitParams`ºÍ`@ApiImplicitParam`À´ÉùÃ÷ÇëÇó²ÎÊý¡£ +`@ApiImplicitParams`ÓÃÔÚ·½·¨Í·ÉÏ£¬`@ApiImplicitParam`¶¨ÒåÔÚ`@ApiImplicitParams`ÀïÃæ£¬Ò»¸ö`@ApiImplicitParam`¶ÔÓ¦Ò»¸ö²ÎÊý¡£ +`@ApiImplicitParam`³£ÓÃÅäÖÃÏ + +* name£ºÓÃÀ´¶¨Òå²ÎÊýµÄÃû×Ö£¬Ò²¾ÍÊÇ×ֶεÄÃû×Ö,¿ÉÒÔÓë½Ó¿ÚµÄÈë²ÎÃû¶ÔÓ¦¡£**Èç¹û²»¶ÔÓ¦£¬Ò²»áÉú³É£¬ËùÒÔ¿ÉÒÔÓÃÀ´¶¨Òå¶îÍâ²ÎÊý£¡** +* value£ºÓÃÀ´ÃèÊö²ÎÊý +* required£ºÓÃÀ´±ê×¢²ÎÊýÊÇ·ñ±ØÌî +* paramTypeÓÐpath,query,body,form,headerµÈ·½Ê½£¬µ«¶ÔÓÚ¶ÔÓÚ·ÇʵÌåÀà²ÎÊýµÄʱºò£¬³£ÓõÄÖ»ÓÐpath,query,header£»bodyºÍformÊDz»³£Óõġ£body²»ÊÊÓÃÓÚ¶à¸öÁãÉ¢²ÎÊýµÄÇé¿ö£¬Ö»ÊÊÓÃÓÚjson¶ÔÏóµÈÇé¿ö¡£¡¾Èç¹ûÄãµÄ½Ó¿ÚÊÇ`form-data`,`x-www-form-urlencoded`µÄʱºò¿ÉÄܲ»ÄÜʹÓÃswaggerÒ³ÃæAPIµ÷ÊÔ£¬µ«¿ÉÒÔÔÚºóÃæ½²µ½»ùÓÚBootstrapUIµÄswaggerÔöÇ¿Öе÷ÊÔ£¬»ùÓÚBootstrapUIµÄswaggerÖ§³ÖÖ¸¶¨`form-data`»ò`x-www-form-urlencoded`¡¿ + +ʾÀýÒ»£ºÉùÃ÷Èë²ÎÊÇURL²ÎÊý + +¸´ÖÆ´úÂë + +``` + // ʹÓÃURL query²ÎÊý + @ApiOperation(value = "µÇ¼½Ó¿Ú2",notes = "µÇ¼½Ó¿ÚµÄ˵Ã÷2") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username",//²ÎÊýÃû×Ö + value = "Óû§Ãû",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + //paramType¶¨Òå²ÎÊý´«µÝÀàÐÍ£ºÓÐpath,query,body,form,header + paramType = "query" + ) + , + @ApiImplicitParam(name = "password",//²ÎÊýÃû×Ö + value = "ÃÜÂë",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + paramType = "query" + ) + }) + @PostMapping(value = "/login2") + public LoginForm login2(String username,String password){ + System.out.println(username+":"+password); + LoginForm loginForm = new LoginForm(); + loginForm.setUsername(username); + loginForm.setPassword(password); + return loginForm; + } + +``` + +ʾÀý¶þ£ºÉùÃ÷Èë²ÎÊÇURL·¾¶²ÎÊý + +¸´ÖÆ´úÂë + +``` + // ʹÓ÷¾¶²ÎÊý + @PostMapping("/login3/{id1}/{id2}") + @ApiOperation(value = "µÇ¼½Ó¿Ú3",notes = "µÇ¼½Ó¿ÚµÄ˵Ã÷3") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id1",//²ÎÊýÃû×Ö + value = "Óû§Ãû",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + //paramType¶¨Òå²ÎÊý´«µÝÀàÐÍ£ºÓÐpath,query,body,form,header + paramType = "path" + ) + , + @ApiImplicitParam(name = "id2",//²ÎÊýÃû×Ö + value = "ÃÜÂë",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + paramType = "path" + ) + }) + public String login3(@PathVariable Integer id1,@PathVariable Integer id2){ + return id1+":"+id2; + } + +``` + +ʾÀýÈý£ºÉùÃ÷Èë²ÎÊÇheader²ÎÊý + +¸´ÖÆ´úÂë + +``` + // ÓÃheader´«µÝ²ÎÊý + @PostMapping("/login4") + @ApiOperation(value = "µÇ¼½Ó¿Ú4",notes = "µÇ¼½Ó¿ÚµÄ˵Ã÷4") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username",//²ÎÊýÃû×Ö + value = "Óû§Ãû",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + //paramType¶¨Òå²ÎÊý´«µÝÀàÐÍ£ºÓÐpath,query,body,form,header + paramType = "header" + ) + , + @ApiImplicitParam(name = "password",//²ÎÊýÃû×Ö + value = "ÃÜÂë",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + paramType = "header" + ) + }) + public String login4( @RequestHeader String username, + @RequestHeader String password){ + return username+":"+password; + } + +``` + +ʾÀýËÄ£ºÉùÃ÷ÎļþÉÏ´«²ÎÊý + +¸´ÖÆ´úÂë + +``` + // ÓÐÎļþÉÏ´«Ê±ÒªÓÃ@ApiParam£¬Ó÷¨»ù±¾Óë@ApiImplicitParamÒ»Ñù£¬²»¹ý@ApiParamÓÃÔÚ²ÎÊýÉÏ + // »òÕßÄãÒ²¿ÉÒÔ²»×¢½â£¬swagger»á×Ô¶¯Éú³É˵Ã÷ + @ApiOperation(value = "ÉÏ´«Îļþ",notes = "ÉÏ´«Îļþ") + @PostMapping(value = "/upload") + public String upload(@ApiParam(value = "ͼƬÎļþ", required = true)MultipartFile uploadFile){ + String originalFilename = uploadFile.getOriginalFilename(); + + return originalFilename; + } + + // ¶à¸öÎļþÉÏ´«Ê±£¬**swaggerÖ»ÄܲâÊÔµ¥ÎļþÉÏ´«** + @ApiOperation(value = "ÉÏ´«¶à¸öÎļþ",notes = "ÉÏ´«¶à¸öÎļþ") + @PostMapping(value = "/upload2",consumes = "multipart/*", headers = "content-type=multipart/form-data") + public String upload2(@ApiParam(value = "ͼƬÎļþ", required = true,allowMultiple = true)MultipartFile[] uploadFile){ + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < uploadFile.length; i++) { + System.out.println(uploadFile[i].getOriginalFilename()); + sb.append(uploadFile[i].getOriginalFilename()); + sb.append(","); + } + return sb.toString(); + } + + // ¼ÈÓÐÎļþ£¬ÓÖÓвÎÊý + @ApiOperation(value = "¼ÈÓÐÎļþ£¬ÓÖÓвÎÊý",notes = "¼ÈÓÐÎļþ£¬ÓÖÓвÎÊý") + @PostMapping(value = "/upload3") + @ApiImplicitParams({ + @ApiImplicitParam(name = "name", + value = "ͼƬÐÂÃû×Ö", + required = true + ) + }) + public String upload3(@ApiParam(value = "ͼƬÎļþ", required = true)MultipartFile uploadFile, + String name){ + String originalFilename = uploadFile.getOriginalFilename(); + + return originalFilename+":"+name; + } + +``` + +## ¶¨Òå½Ó¿ÚÏìÓ¦ + +¶¨Òå½Ó¿ÚÏìÓ¦£¬ÊÇ·½±ã²é¿´½Ó¿ÚÎĵµµÄÈËÄܹ»ÖªµÀ½Ó¿Ú·µ»ØµÄÊý¾ÝµÄÒâÒå¡£ + +### ÏìÓ¦ÊÇʵÌåÀࣺ + +Ç°ÃæÔÚ¶¨Òå½Ó¿ÚÇëÇó²ÎÊýµÄʱºòÓÐÌᵽʹÓÃ`@ApiModel`À´±ê×¢À࣬Èç¹û½Ó¿Ú·µ»ØÁËÕâ¸öÀ࣬ÄÇôÕâ¸öÀàÉϵÄ˵Ã÷Ò²»á×÷ΪÏìÓ¦µÄ˵Ã÷£º + +¸´ÖÆ´úÂë + +``` + // ·µ»Ø±»@ApiModel±ê×¢µÄÀà¶ÔÏó + @ApiOperation(value = "ʵÌåÀàÏìÓ¦",notes = "·µ»ØÊý¾ÝΪʵÌåÀàµÄ½Ó¿Ú") + @PostMapping("/role1") + public LoginForm role1(@RequestBody LoginForm loginForm){ + return loginForm; + } + +``` + +![20200712000406](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712000406.png) + +### ÏìÓ¦ÊÇ·ÇʵÌåÀࣺ + +swaggerÎÞ·¨¶Ô·ÇʵÌåÀàµÄÏìÓ¦½øÐÐÏêϸ˵Ã÷£¬Ö»Äܱê×¢ÏìÓ¦ÂëµÈÐÅÏ¢¡£ÊÇͨ¹ý`@ApiResponses`ºÍ`@ApiResponse`À´ÊµÏֵġ£ +`@ApiResponses`ºÍ`@ApiResponse`¿ÉÒÔÓë`@ApiModel`Ò»ÆðʹÓᣠ+ +¸´ÖÆ´úÂë + +``` + // ÆäËûÀàÐ͵Ä,´Ëʱ²»ÄÜÔö¼Ó×Ö¶Î×¢ÊÍ£¬ËùÒÔÆäʵswaggerÍÆ¼öʹÓÃʵÌåÀà + @ApiOperation(value = "·ÇʵÌåÀà",notes = "·ÇʵÌåÀà") + @ApiResponses({ + @ApiResponse(code=200,message = "µ÷Óóɹ¦"), + @ApiResponse(code=401,message = "ÎÞȨÏÞ" ) + } + ) + @PostMapping("/role2") + public String role2(){ + return " {\n" + + " name:\"¹ã¶«\",\n" + + " citys:{\n" + + " city:[\"¹ãÖÝ\",\"ÉîÛÚ\",\"Ö麣\"]\n" + + " }\n" + + " }"; + } + +``` + +![20200712013503](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712013503.png) + +* * * + +# Swagger UIÔöÇ¿ + +Äã¿ÉÄÜ»á¾õµÃÏÖÔÚÕâ¸öUI²»ÊǺܺÿ´£¬ÏÖÔÚÓÐһЩµÚÈý·½ÌṩÁËһЩSwagger UIÔöÇ¿£¬±È½ÏÁ÷ÐеÄÊÇ`swagger-bootstrap-ui`£¬ÎÒÃÇÕâÀïÒÔ`swagger-bootstrap-ui`ΪÀý¡£ + +## UI¶Ô±È£º + +![20200712013653](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712013653.png) + +![20200712013723](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712013723.png) + +## ʹÓà + +1.Ìí¼ÓÒÀÀµ°ü£º + +¸´ÖÆ´úÂë + +``` + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + com.github.xiaoymin + swagger-bootstrap-ui + 1.8.7 + + +``` + +2.ÔÚswaggerÅäÖÃÀàÖÐÔö¼Ó×¢½â`@EnableSwaggerBootstrapUI`: + +¸´ÖÆ´úÂë + +``` +@Configuration // ±êÃ÷ÊÇÅäÖÃÀà +@EnableSwagger2 //¿ªÆôswagger¹¦ÄÜ +@EnableSwaggerBootstrapUI // ¿ªÆôSwaggerBootstrapUI +public class SwaggerConfig { + // Ê¡ÂÔÅäÖÃÄÚÈÝ +} + +``` + +3.·ÃÎÊAPI£º`http://localhost:8080/doc.html`£¬¼´¿ÉÔ¤ÀÀµ½»ùÓÚbootstarpµÄSwagger UI½çÃæ¡£ + +## Óŵã + +1.?½çÃæºÃ¿´ÁËÒ»µã + +2.ÉÏÃæËµ¹ýÁË£¬»ùÓÚBootstrapUIµÄswaggerÖ§³ÖÖ¸¶¨`form-data`»ò`x-www-form-urlencoded`£º +![20200712024858](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712024858.png) + +3.Ö§³Ö¸´ÖƵ¥¸öAPIÎĵµºÍµ¼³öÈ«²¿APIÎĵµ£º +![20200712025020](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712025020.png) + +![20200712025044](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20200712025044.png) + +* * * + +# ÕûºÏSpring Security×¢Òâ + +ÔÚSpring BootÕûºÏSpring SecurityºÍSwaggerµÄʱºò£¬ÐèÒªÅäÖÃÀ¹½ØµÄ·¾¶ºÍ·ÅÐеÄ·¾¶£¬×¢ÒâÊÇ·ÅÐÐÒÔϼ¸¸ö·¾¶¡£ + +¸´ÖÆ´úÂë + +``` +.antMatchers("/swagger**/**").permitAll() +.antMatchers("/webjars/**").permitAll() +.antMatchers("/v2/**").permitAll() +.antMatchers("/doc.html").permitAll() // Èç¹ûÄãÓÃÁËbootstarpµÄSwagger UI½çÃæ£¬¼ÓÒ»¸öÕâ¸ö¡£ + +``` + +* * * + +# ¶ÔÓÚtokenµÄ´¦Àí + +ÔÚswaggerÖÐÖ»Ö§³ÖÁ˼òµ¥µÄµ÷ÊÔ£¬µ«¶ÔÓÚһЩ½Ó¿Ú£¬ÎÒÃDzâÊÔµÄʱºò¿ÉÄÜÐèÒª°ÑtokenÐÅϢдµ½headerÖУ¬Ä¿Ç°ºÃÏñû¿´µ½¿ÉÒÔ×Ô¶¨Òå¼ÓÇëÇóÍ·µÄµØ·½£¿ +?·½·¨Ò»£º +¡¡¡¡Èç¹ûÄãʹÓÃÁËSwagger BootstrapUI£¬ÄÇôÄã¿ÉÒÔÔÚ¡°Îĵµ¹ÜÀí¡±ÖÐÔö¼ÓÈ«¾Ö²ÎÊý£¬Õâ°üÀ¨ÁËÌí¼Óheader²ÎÊý¡£ + +?·½·¨¶þ£ºÔÚswaggerÅäÖÃÀàÖÐÔö¼ÓÈ«¾Ö²ÎÊýÅäÖ㺠+ +¸´ÖÆ´úÂë + +``` + //Èç¹ûÓжîÍâµÄÈ«¾Ö²ÎÊý£¬±ÈÈç˵ÇëÇóÍ·²ÎÊý£¬¿ÉÒÔÕâÑùÌí¼Ó + ParameterBuilder parameterBuilder = new ParameterBuilder(); + List parameters = new ArrayList(); + parameterBuilder.name("authorization").description("ÁîÅÆ") + .modelRef(new ModelRef("string")).parameterType("header").required(false).build(); + parameters.add(parameterBuilder.build()); + return new Docket(DocumentationType.SWAGGER_2) // DocumentationType.SWAGGER_2 ¹Ì¶¨µÄ£¬´ú±íswagger2 + .apiInfo(apiInfo()) // ÓÃÓÚÉú³ÉAPIÐÅÏ¢ + .select() // select()º¯Êý·µ»ØÒ»¸öApiSelectorBuilderʵÀý,ÓÃÀ´¿ØÖƽӿڱ»swagger×ö³ÉÎĵµ + .apis(RequestHandlerSelectors.basePackage("com.example.controller")) // ÓÃÓÚÖ¸¶¨É¨ÃèÄĸö°üÏÂµÄ½Ó¿Ú + .paths(PathSelectors.any())// Ñ¡ÔñËùÓеÄAPI,Èç¹ûÄãÏëֻΪ²¿·ÖAPIÉú³ÉÎĵµ£¬¿ÉÒÔÅäÖÃÕâÀï + .build().globalOperationParameters(parameters); + +``` + +?·½·¨Èý£ºÊ¹ÓÃ`@ApiImplicitParams`À´¶îÍâ±ê×¢Ò»¸öÇëÇóÍ·²ÎÊý£¬ÀýÈ磺 + +¸´ÖÆ´úÂë + +``` + // Èç¹ûÐèÒª¶îÍâµÄ²ÎÊý£¬·Ç±¾·½·¨Óõ½£¬µ«¹ýÂËÆ÷ÒªÓÃ,ÀàËÆÓÚȨÏÞtoken + @PostMapping("/login6") + @ApiOperation(value = "´øtokenµÄ½Ó¿Ú",notes = "´øtokenµÄ½Ó¿Ú") + @ApiImplicitParams({ + @ApiImplicitParam(name = "authorization",//²ÎÊýÃû×Ö + value = "ÊÚȨtoken",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + paramType = "header" + ) + , + @ApiImplicitParam(name = "username",//²ÎÊýÃû×Ö + value = "Óû§Ãû",//²ÎÊýµÄÃèÊö + required = true,//ÊÇ·ñ±ØÐë´«Èë + paramType = "query" + ) + }) + public String login6(String username){ + return username; + } + +``` + +* * * + +# SwaggerµÄ°²È«¹ÜÀí + +1.Èç¹ûÄãÕûºÏÁËȨÏÞ¹ÜÀí£¬¿ÉÒÔ¸øswagger¼ÓÉÏȨÏÞ¹ÜÀí£¬ÒªÇó·ÃÎÊswaggerÒ³ÃæÊäÈëÓû§ÃûºÍÃÜÂ룬ÕâЩÊÇspring securityºÍshiroµÄÊÂÁË£¬ÕâÀï²»½²¡£ + +2.Èç¹ûÄã½ö½öÊDz»ÏëÔÚÕýʽ»·¾³ÖпÉÒÔ·ÃÎÊ£¬¿ÉÒÔÔÚÕýʽ»·¾³ÖйرÕSwagger×Ô¶¯ÅäÖã¬Õâ¾Í²»»áÓÐswaggerÒ³ÃæÁË¡£Ê¹ÓÃ`@Profile({"dev","test"})`×¢½âÀ´ÏÞÖÆÖ»ÔÚdev»òÕßtestÏÂÆôÓÃSwagger×Ô¶¯ÅäÖᣠ+È»ºóÔÚSpring BootÅäÖÃÎļþÖÐÐ޸ĵ±Ç°profile`spring.profiles.active=release`£¬ÖØÆôÖ®ºó£¬´ËʱÎÞ·¨·ÃÎÊ`http://localhost:8080/swagger-ui.html` + +* * * + + + +×÷Õߣº[progor](https://www.cnblogs.com/progor/) +±¾ÎÄΪ×÷ÕßÔ­´´£¬×ªÔØÇë×¢Ã÷³ö´¦ + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/Spring\345\270\270\350\247\201\346\263\250\350\247\243\344\275\277\347\224\250\346\214\207\345\215\227(\345\214\205\345\220\253Spring+SpringMVC+SpringBoot).md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/Spring\345\270\270\350\247\201\346\263\250\350\247\243\344\275\277\347\224\250\346\214\207\345\215\227(\345\214\205\345\220\253Spring+SpringMVC+SpringBoot).md" new file mode 100644 index 0000000..813fabe --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/Spring\345\270\270\350\247\201\346\263\250\350\247\243\344\275\277\347\224\250\346\214\207\345\215\227(\345\214\205\345\220\253Spring+SpringMVC+SpringBoot).md" @@ -0,0 +1,1112 @@ +# Spring×¢½â +## 1 ¸ÅÊö + +ÎÒÃǶ¼ÖªµÀSpring×îºËÐĵÄÌØÐÔ¾ÍÊÇIOC+AOP£¬IOCµÄÔ­Àí¾ÍÊÇʵÏÖÁËÒ»¸öSpringÈÝÆ÷£¬ÓÃÀ´¹ÜÀíËùÓÐSpring BeanʵÀý +DI£¬Ò²¾ÍÊÇÒÀÀµ×¢È룬ÊÇÎÒÃÇ×÷Ϊ¿ª·¢ÕßÐèÒª¹ØÐĵĺËÐÄ»°Ì⣬ÈçºÎ×¢ÈëÒÀÀµ£¬×¢ÈëÄĸöÒÀÀµ£¬ÊÇÎÒÃÇÐèÒªÃ÷È·ÖªµÀµÄ¡£ +ÔÚÒÔǰ£¬ÎÒÃÇϰ¹ßÓÚÓÃxmlÅäÖÃÎļþÀ´ÉùÃ÷Ò»¸öbean£¬µ«ÏÖÔÚ£¬ÎÒÃǸü¶àµØÊ¹ÓÃ×¢½âºÍ´úÂëÀ´Íê³ÉDIµÄ¹ý³Ì + + +ÎÒÃÇ¿ÉÒÔʹÓà org.springframework.beans.factory.annotation ºÍ org.springframework.context.annotation °üÖеÄ×¢ÊÍÀ´ÆôÓà Spring DI ÒýÇæµÄ¹¦ÄÜ¡£ + +ÎÒÃÇͨ³£½«ÕâЩ³ÆÎª¡°Spring ºËÐÄ×¢ÊÍ¡±£¬ÎÒÃǽ«ÔÚ±¾½Ì³ÌÖÐ¶ÔÆä½øÐлعˡ£ + + +## 2 DIÏà¹Ø×¢½â + +### 2.1 @Autowired + +ÎÒÃÇ¿ÉÒÔʹÓà @Autowired À´±ê¼Ç Spring ½«Òª½âÎöºÍ×¢ÈëµÄÒÀÀµÏî¡£ +ÎÒÃÇ¿ÉÒÔ½«´Ë×¢ÊÍÓë¹¹Ô캯Êý¡¢setter »ò×Ö¶Î×¢ÈëÒ»ÆðʹÓᣠ+Constructor injection: + +**¹¹ÔìÆ÷×¢Èë** + +```` +class Car { + Engine engine; + + @Autowired + Car(Engine engine) { + this.engine = engine; + } +} +```` + +**Setter×¢Èë** +```` +class Car { + Engine engine; + + @Autowired + void setEngine(Engine engine) { + this.engine = engine; + } +} +```` +**±äÁ¿×¢Èë** +```` +class Car { + @Autowired + Engine engine; +} +```` + +@Autowired ÓÐÒ»¸öÃûΪ required µÄ²¼¶û²ÎÊý£¬Ä¬ÈÏֵΪ true¡£ + +µ±ËüÕÒ²»µ½ºÏÊ浀 bean À´Á¬½Óʱ£¬Ëü»áµ÷Õû Spring µÄÐÐΪ¡£ µ±Îª true ʱ£¬½«Å׳öÒì³££¬·ñÔò²»Á¬½ÓÈκÎÄÚÈÝ¡£ + +Çë×¢Ò⣬Èç¹ûÎÒÃÇʹÓù¹Ô캯Êý×¢È룬ÔòËùÓй¹Ô캯Êý²ÎÊý¶¼ÊÇÇ¿ÖÆÐԵġ£ + +´Ó 4.3 °æ±¾¿ªÊ¼£¬ÎÒÃDz»ÐèÒªÏÔʽµØÊ¹Óà @Autowired ×¢½â¹¹Ô캯Êý£¬³ý·ÇÎÒÃÇÉùÃ÷ÖÁÉÙÁ½¸ö¹¹Ô캯Êý¡£ + +### 2.2 @Bean + +@Bean ±ê¼ÇʵÀý»¯ Spring bean µÄ¹¤³§·½·¨£º + +``` +@Bean +Engine engine() { + return new Engine(); +} +```` + +µ±ÐèÒª·µ»ØÀàÐ͵ÄÐÂʵÀýʱ£¬Spring »áµ÷ÓÃÕâЩ·½·¨¡£ + +Éú³ÉµÄ bean Ó빤³§·½·¨Í¬Ãû¡£ Èç¹ûÎÒÃÇÏëÒÔ²»Í¬µÄ·½Ê½ÃüÃû£¬ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵ÄÃû³Æ»òÖµ²ÎÊý£¨²ÎÊýÖµÊDzÎÊýÃû³ÆµÄ±ðÃû£©£º + +```` +@Bean("engine") +Engine getEngine() { + return new Engine(); +} +```` +ÕâÊÇÒ»Öַdz£³£¼ûµÄbeanÉùÃ÷·½Ê½£¬ÒòΪºÜ¶àµÄbean²¢²»ÊÇÎÒÃÇÒ»¿ªÊ¼¾ÍÔÚ´úÂëÀﶨÒåºÃµÄ£¬Ëü¿ÉÄÜÐèÒª»ùÓÚÔËÐÐʱ»·¾³À´½øÐа´Ðè¹¹½¨¡£ + +ÎÒÃÇ¿ÉÒÔ×ÔÓɵØÉùÃ÷ºÍ¶¨ÒåBean£¬²¢ÇÒÒ²¿ÉÒÔ¸³ÓèËü×Ô¶¨ÒåµÄbeanÃû³Æ¡£ + +×¢Ò⣬ËùÓÐÓÃ@Bean ×¢Ê͵ķ½·¨¶¼±ØÐëÔÚ@Configuration ÀàÖС£ + +### 2.3 @Qualifier + +ÎÒÃÇʹÓÃ@Qualifier ºÍ@Autowired À´ÌṩÎÒÃÇÏëÒªÔÚ²»Ã÷È·Çé¿öÏÂʹÓõÄbean id »òbean Ãû³Æ¡£ + +ÀýÈ磬ÒÔÏÂÁ½¸ö bean ʵÏÖÏàͬµÄ½Ó¿Ú£º +```` +class Bike implements Vehicle {} + +class Car implements Vehicle {} + +```` + +Èç¹û Spring ÐèҪעÈëÒ»¸ö Vehicle bean£¬Ëü»áÒÔ¶à¸öÆ¥Å䶨Òå½áÊø¡£ ÔÚÕâÖÖÇé¿öÏ£¬ÎÒÃÇ¿ÉÒÔʹÓà @Qualifier ×¢ÊÍÏÔʽÌṩ bean µÄÃû³Æ¡£ + +**¹¹ÔìÆ÷×¢Èë** +```` +@Autowired +Biker(@Qualifier("bike") Vehicle vehicle) { +this.vehicle = vehicle; +} +```` + +**Setter×¢Èë** + +```` +@Autowired +void setVehicle(@Qualifier("bike") Vehicle vehicle) { +this.vehicle = vehicle; +} +```` +»òÕß: + +```` +@Autowired +@Qualifier("bike") +void setVehicle(Vehicle vehicle) { +this.vehicle = vehicle; +```` +**±äÁ¿×¢Èë** + +```` +@Autowired +@Qualifier("bike") +Vehicle vehicle; +```` +Õâ¸ö×¢½âÎÒÃÇ¿ÉÄÜÆ½³£ÓõIJ»¶à£¬µ«Êǵ±Ò»¸ö½Ó¿ÚÓжà¸öʵÏÖÀàʱ£¬Ëü¾Í»á¾­³£ÅÉÉÏÓ󡣡 + +### 2.4 @Required + +@Required ÔÚ setter ·½·¨Éϱê¼ÇÎÒÃÇÏëҪͨ¹ý XML Ìî³äµÄÒÀÀµÏ +```` +@Required +void setColor(String color) { +this.color = color; +} +```` +xml +```` + + + +```` +·ñÔò£¬½«Å׳ö BeanInitializationException¡£ +·Ç³£ÉÙ¼ûµÄÓ÷¨£¬ÖªµÀһϾÍÐÐÁË + +### 2.5 @Value +ÎÒÃÇ¿ÉÒÔʹÓà @Value ½«ÊôÐÔÖµ×¢Èë bean¡£ ËüÓë¹¹Ô캯Êý¡¢setter ºÍ×Ö¶Î×¢Èë¼æÈÝ¡£ + +ÕâÒ²ÊǷdz£³£ÓõÄÒ»¸ö×¢½â£¬ÒòΪÎÒÃǺܶàʱºò¶¼ÐèÒª´Óapplication.properties»òÕ߯äËûÅäÖÃÎļþÖÐÀ´¶ÁÈ¡ÅäÖÃÊôÐÔÖµ¡£ + +**¹¹ÔìÆ÷×¢Èë** +```` +Engine(@Value("8") int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +**setter×¢Èë** + +```` +@Autowired +void setCylinderCount(@Value("8") int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +»òÕß: +```` + +@Value("8") +void setCylinderCount(int cylinderCount) { +this.cylinderCount = cylinderCount; +} +```` + +**±äÁ¿×¢Èë** +```` +@Value("8") +int cylinderCount; +```` + +µ±È»£¬×¢È뾲ֵ̬ÊÇûÓÐÓõġ£ Òò´Ë£¬ÎÒÃÇ¿ÉÒÔÔÚ @Value ÖÐʹÓÃռλ·û×Ö·û´®À´Á¬½ÓÔÚÍⲿԴÖж¨ÒåµÄÖµ£¬ÀýÈçÔÚ .properties »ò .yaml ÎļþÖС£ +```` + +engine.fuelType=petrol +```` + +ÎÒÃÇ¿ÉÒÔͨ¹ýÒÔÏ·½Ê½×¢Èë engine.fuelType µÄÖµ£º + +```` +@Value("${engine.fuelType}") +String fuelType; +```` + +ÎÒÃÇÉõÖÁ¿ÉÒÔʹÓà SpEL À´Ê¹ÓÃ@Value¡£ ¸ü¶à¸ß¼¶Ê¾Àý¿ÉÒÔÔÚÎÒÃǹØÓÚ@Value µÄÎÄÕÂÖÐÕÒµ½¡£ + +### 2.6 @DependsOn +ÎÒÃÇ¿ÉÒÔʹÓôË×¢½âÈà Spring ÔÚ×¢½âµÄ bean ֮ǰ³õʼ»¯ÆäËû bean¡£ ͨ³££¬´ËÐÐΪÊÇ×Ô¶¯µÄ£¬»ùÓÚ bean Ö®¼äµÄÏÔʽÒÀÀµ¹ØÏµ¡£ + +ÎÒÃÇÖ»ÓÐÔÚÒÀÀµÊÇÒþʽµÄʱºò²ÅÐèÒªÕâ¸ö×¢½â£¬±ÈÈçJDBCÇý¶¯¼ÓÔØ»òÕß¾²Ì¬±äÁ¿³õʼ»¯¡£ + +ÎÒÃÇ¿ÉÒÔÔÚÖ¸¶¨ÒÀÀµ bean Ãû³ÆµÄÒÀÀµÀàÉÏʹÓà @DependsOn¡£ ×¢Ê͵ÄÖµ²ÎÊýÐèÒªÒ»¸ö°üº¬ÒÀÀµ bean Ãû³ÆµÄÊý×飺 + +```` +@DependsOn("engine") +class Car implements Vehicle {} +```` +Alternatively, if we define a bean with the @Bean annotation, the factory method should be annotated with @DependsOn: +```` +@Bean +@DependsOn("fuel") +Engine engine() { +return new Engine(); +} +```` +### 2.7 @Lazy +µ±ÎÒÃÇÏëÀÁ¶èµØ³õʼ»¯ÎÒÃÇµÄ bean ʱ£¬ÎÒÃÇʹÓà @Lazy¡£ ĬÈÏÇé¿öÏ£¬Spring ÔÚÓ¦ÓóÌÐòÉÏÏÂÎĵįô¶¯/Òýµ¼Ê±¼±Çеش´½¨ËùÓе¥Àý bean¡£ + +µ«ÊÇ£¬ÓÐЩÇé¿öÏÂÎÒÃÇÐèÒªÔÚÇëÇóʱ´´½¨ bean£¬¶ø²»ÊÇÔÚÓ¦ÓóÌÐòÆô¶¯Ê±´´½¨¡£ + +Õâ¸ö×¢½âµÄÐÐΪ»á¸ù¾ÝÎÒÃÇ·ÅÖÃËüµÄÈ·ÇÐλÖöøÓÐËù²»Í¬¡£ ÎÒÃÇ¿ÉÒÔ°ÑËü·ÅÔÚ£º + +Ò»¸ö @Bean ×¢Ê굀 bean ¹¤³§·½·¨£¬ÒÔÑÓ³Ù·½·¨µ÷Óã¨Òò´Ë´´½¨ bean£© +@Configuration ÀàºÍËùÓаüº¬µÄ@Bean ·½·¨¶¼»áÊܵ½Ó°Ïì + +Ò»¸ö @Component À࣬Ëü²»ÊÇ @Configuration À࣬Õâ¸ö bean ½«±»ÑÓ³Ù³õʼ»¯ + +@Autowired ¹¹Ô캯Êý¡¢setter »ò×ֶΣ¬ÓÃÓÚÑÓ³Ù¼ÓÔØÒÀÀµÏî±¾Éí£¨Í¨¹ý´úÀí£© + +```` +@Configuration +@Lazy +class VehicleFactoryConfig { + + @Bean + @Lazy(false) + Engine engine() { + return new Engine(); + } +} +```` +ÕâͬÑù²»ÊÇÒ»¸ö³£ÓõÄ×¢½â¡£ + +µ±ÎÒÃÇά»¤Ò»¸öÓдóÁ¿beanµÄÏîĿʱ£¬»á·¢ÏÖÓкܶàbean¿ÉÄܶ¼Êǰ´ÐèʹÓõ쬲»Ò»¶¨ÐèÒªÔÚÈÝÆ÷³õʼ»¯Ê±¾Í½øÐгõʼ»¯£¬Õâ¿ÉÒÔ°ïÎÒÃǽÚÊ¡ºÜ¶àʱ¼äºÍÐÔÄÜ¡£ + + +### 2.8 @Lookup +ͬÑùÊÇÒ»¸ö±È½ÏÉÙÓõ½µÄ×¢½â + +@Lookup »á¸æËß Spring ÔÚÎÒÃǵ÷ÓÃËüʱ·µ»Ø·½·¨·µ»ØÀàÐ͵ÄʵÀý¡£ + +±¾ÖÊÉÏ£¬Spring ½«¸²¸ÇÎÒÃÇ´ø×¢Ê͵ķ½·¨²¢Ê¹ÓÃÎÒÃÇ·½·¨µÄ·µ»ØÀàÐͺͲÎÊý×÷Ϊ BeanFactory#getBean µÄ²ÎÊý¡£ + +@Lookup ÊÊÓÃÓÚ£º + +½«Ô­ÐÍ bean ×¢ÈëÎÒÃÇ×Ô¼ºµÄbean£¨ÀàËÆÓÚ Provider£© + +Èç¹ûÎÒÃÇÅöÇɾö¶¨ÓµÓÐÒ»¸öÔ­ÐÍ Spring bean£¬ÄÇôÎÒÃǼ¸ºõ»áÁ¢¼´ÃæÁÙÕâÑùµÄÎÊÌ⣺ + +ÎÒÃǵĵ¥Àý Spring bean ½«ÈçºÎ·ÃÎÊÕâЩԭÐÍ Spring bean£¿ + +ÏÖÔÚ£¬Provider ¿Ï¶¨ÊÇÒ»ÖÖ·½Ê½£¬¾¡¹Ü @Lookup ÔÚijЩ·½Ãæ¸ü¼ÓͨÓᣠ+ +ҪעÒâµÄÊÇ£¬springĬÈÏʹÓõĵ¥Àýbean£¬ËùÒÔÈç¹ûÎÒÃÇҪעÈëÔ­ÐÍbean£¬ÎÒÃDzÅÐèÒª×öÕâÑùµÄ¶îÍ⹤×÷ + +Ê×ÏÈ£¬ÈÃÎÒÃÇ´´½¨Ò»¸öÔ­ÐÍ bean£¬ÉÔºóÎÒÃǽ«Æä×¢Èëµ½µ¥Àý bean ÖУº +```` +@Component +@Scope("prototype") +public class SchoolNotification { +// ... prototype-scoped state +} +```` +ʹÓÃ@Lookup£¬ÎÒÃÇ¿ÉÒÔͨ¹ýµ¥Àý bean »ñÈ¡ SchoolNotification µÄʵÀý£º + +```` +@Component +public class StudentServices { + + // ... member variables, etc. + + @Lookup + public SchoolNotification getNotification() { + return null; + } + + // ... getters and setters +} +```` +Using @Lookup, we can get an instance of SchoolNotification through our singleton bean: +```` +@Test +public void whenLookupMethodCalled_thenNewInstanceReturned() { +// ... initialize context +StudentServices first = this.context.getBean(StudentServices.class); +StudentServices second = this.context.getBean(StudentServices.class); + + assertEquals(first, second); + assertNotEquals(first.getNotification(), second.getNotification()); +} +```` +Çë×¢Ò⣬ÔÚ StudentServices ÖУ¬ÎÒÃǽ« getNotification ·½·¨±£ÁôΪ´æ¸ù¡£ + +ÕâÊÇÒòΪ Spring ͨ¹ýµ÷Óà beanFactory.getBean(StudentNotification.class) ¸²¸ÇÁ˸÷½·¨£¬Òò´ËÎÒÃÇ¿ÉÒÔ½«ÆäÁô¿Õ¡£ + + +### 2.9 @Primary +ÓÐʱÎÒÃÇÐèÒª¶¨Òå¶à¸öÏàͬÀàÐ͵Äbean¡£ ÔÚÕâЩÇé¿öÏ£¬×¢È뽫²»³É¹¦£¬ÒòΪ Spring ²»ÖªµÀÎÒÃÇÐèÒªÄĸö bean¡£ + +ÎÒÃÇÒѾ­¿´µ½ÁË´¦ÀíÕâÖÖÇé¿öµÄÑ¡ÏÓÃ@Qualifier ±ê¼ÇËùÓнÓÏߵ㲢ָ¶¨ËùÐè bean µÄÃû³Æ¡£ + +È»¶ø£¬´ó¶àÊýʱºòÎÒÃÇÐèÒªÒ»¸öÌØ¶¨µÄ bean£¬ºÜÉÙÐèÒªÆäËû bean¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃ@Primary À´¼ò»¯ÕâÖÖÇé¿ö£ºÈç¹ûÎÒÃÇÓÃ@Primary ±ê¼Ç×î³£ÓõÄbean£¬Ëü½«ÔÚunqualifiedµÄ×¢ÈëµãÉϱ»Ñ¡Ôñ£º + +```` +@Component +@Primary +class Car implements Vehicle {} + +@Component +class Bike implements Vehicle {} + +@Component +class Driver { +@Autowired +Vehicle vehicle; +} + +@Component +class Biker { +@Autowired +@Qualifier("bike") +Vehicle vehicle; +} +```` +ÔÚÇ°ÃæµÄʾÀýÖУ¬Æû³µÊÇÖ÷Òª³µÁ¾¡£ Òò´Ë£¬ÔÚ Driver ÀàÖУ¬Spring ×¢ÈëÁËÒ»¸ö Car bean¡£ µ±È»£¬ÔÚ Biker bean ÖУ¬×Ö¶Î vehicle µÄÖµ½«ÊÇÒ»¸ö Bike ¶ÔÏó£¬ÒòΪËüÊÇqualifiedµÄ¡£ + +### 2.10 @Scope + +¶ÔÓÚͨ³£µÄÓ¦ÓÃÀ´Ëµ£¬ÎÒÃÇbeanµÄscopeĬÈ϶¼Êǵ¥ÀýµÄ£¬µ«Êµ¼ÊÉÏspring bean¿ÉÒÔÖ§³Ö¶àÖÖ¶àÑùµÄ×÷Ó÷¶Î§£¬ËüÃÇÓÐ×Ų»Í¬µÄÉúÃüÖÜÆÚ¡£ + +ÎÒÃÇʹÓÃ@Scope À´¶¨Òå@Component Àà»ò@Bean ¶¨ÒåµÄ·¶Î§¡£ Ëü¿ÉÒÔÊǵ¥Àý¡¢Ô­ÐÍ¡¢ÇëÇ󡢻Ự¡¢globalSession »òһЩ×Ô¶¨Ò巶Χ¡£ + +¶ÔÓ¦µÄö¾ÙֵΪ +```` +singleton +prototype +request +session +application +websocket +```` + +Àý×Ó +```` +@Component +@Scope("prototype") +class Engine {} +```` +ÎÒÃÇ¿ÉÒÔÏëÏóһЩ³¡¾°£¬±ÈÈçrequest¡¢session¡¢websocketÕâÀà×÷ÓÃÓòµÄbean£¬Í¨³£Ó¦¸ÃÊǺÍÍøÂçÇëÇóÏà¹ØµÄ£¬±ÈÈçÓô洢Óû§ÐÅÏ¢¡¢sessionÖ®ÀàµÄbean¡£ + +¾ßÌåÔõôʹÓ㬾ÍÒª¸ù¾ÝÄãµÄ¾ßÌ峡¾°À´Ñ¡ÔñÁË£¬Õâ»áÊÇÒ»¸öºÜ´óµÄ»°Ì⣬ÕâÀï¾ÍÏȲ»Õ¹¿ªÁË£¬ÒÔºó¿ÉÒÔÔÚµ¥¶ÀµÄÎÄÕÂÀï¼ÌÐø½éÉÜ¡£ + +## 3 ÉÏÏÂÎÄÅäÖÃ×¢½â +ÎÒÃÇ¿ÉÒÔʹÓñ¾½ÚÖÐÃèÊöµÄ×¢ÊÍÀ´ÅäÖÃÓ¦ÓóÌÐòÉÏÏÂÎÄ¡£ + + +### 3.1 @Profile + +Èç¹ûÎÒÃÇÏ£Íû Spring ½öÔÚÌØ¶¨ÅäÖÃÎļþ´¦Óڻ״̬ʱʹÓÃ@Component Àà»ò@Bean ·½·¨£¬ÎÒÃÇ¿ÉÒÔʹÓÃ@Profile ¶ÔÆä½øÐбê¼Ç¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃ×¢Ê͵ÄÖµ²ÎÊýÅäÖÃÅäÖÃÎļþµÄÃû³Æ£º + +ÎÒÃÇͨ³£Õâ¸ö×¢½âÀ´ÅäÖò»Í¬»·¾³µÄÅäÖᣠ+±ÈÈçÏÂÃæÕâ¸öÀý×Ó + +```` +public interface DatasourceConfig { +public void setup(); +} +```` + +ÏÂÃæÊÇ¿ª·¢»·¾³µÄÅäÖ㺠+ +```` +@Component +@Profile("dev") +public class DevDatasourceConfig implements DatasourceConfig { +@Override +public void setup() { +System.out.println("Setting up datasource for DEV environment. "); +} +} +```` +ÏÂÃæÊÇÉú²ú»·¾³µÄÅäÖ㺠+ +```` +@Component +@Profile("production") +public class ProductionDatasourceConfig implements DatasourceConfig { +@Override +public void setup() { +System.out.println("Setting up datasource for PRODUCTION environment. "); +} +} +```` +µ±È»ÎÒÃÇÒ²¿ÉÒÔʹÓÃxml»òÕ߯äËûÀàÐ͵ÄÅäÖÃÎļþÀ´ÃèÊöÕâ¸öÅäÖÃbean + +xml +```` + + + +```` +### 3.2 @Import + +ÎÒÃÇ¿ÉÒÔʹÓÃÌØ¶¨µÄ @Configuration À࣬¶øÎÞÐèʹÓôË×¢½â½øÐÐ×é¼þɨÃè¡£ ÎÒÃÇ¿ÉÒÔΪÕâЩÀàÌṩ@Import µÄÖµ²ÎÊý + +Õý³£À´Ëµ£¬Èç¹ûÒªÓõ½@Configuration×¢½âµÄbean£¬ÄÇôspringÓ¦ÓñØÐëҪɨÃèµ½¸ÃĿ¼£¬Èç¹ûÇ¡ºÃûÓжԸ÷¾¶½øÐÐɨÃ裬»òÕßÎÒÃÇÖ»ÊÇÏëʹÓ÷¾¶Ïµĵ¥¸öÅäÖÃÀ࣬ÄÇôÎÒÃǾͿÉÒÔʹÓÃ@ImportÀ´µ¼ÈëÕâ¸öÅäÖÃÀàÁË¡£ + +Õâ¸ö×¢½â»¹ÊǷdz£³£Óõģ¬¿´Ò»ÏÂÏÂÃæÕâ¸öÀý×Ó + +```` +@Import(VehiclePartSupplier.class) +class VehicleFactoryConfig {} + +@Configuration +class VehiclePartSupplier{ +} +```` + +### 3.3 @ImportResource + +±ÈÈç˵£ºÏÖÔÚÓÐÒ»¸ö bean.xml µÄÅäÖÃÎļþ£¬ÐèÒª½«¸Ã beans.xml Öж¨ÒåµÄ bean¶ÔÏó ¶¼µ¼Èëµ½ Spring Boot »·¾³µÄÈÝÆ÷ÖУ¬¸ÃÈçºÎ²Ù×÷ÄØ£¿ + +1.Spring ·½Ê½µÄÅäÖÃÎļþ bean.xml ´Ë´¦Ëæ±ã¾Ù¸öʾÀý£¬±ÈÈç˵ xml ÖÐÅäÖÃÁËÒ»¸ö helloService£¬ÈçÏÂËùʾ +```` + + + + + + +```` +2.ʹÓÃ@ImportResource×¢½â£¬ÒýÈë xml ÅäÖà +```` +/** + * Spring BootÀïÃæÃ»ÓÐSpringµÄÅäÖÃÎļþ£¬ÎÒÃÇ×Ô¼º±àдµÄÅäÖÃÎļþ£¬Ò²²»ÄÜ×Ô¶¯Ê¶±ð£» + * Èç¹ûÏëÈÃSpringµÄÅäÖÃÎļþÉúЧ£¬¼ÓÔØµ½Spring ÈÝÆ÷ÖÐÀ´£» + * ʹÓÃ@ImportResource×¢½â£¬½«Æä±ê×¢ÔÚÒ»¸öÅäÖÃÀàÉÏ(´Ë´¦ÅäÖÃÔÚÆô¶¯Àà) + */ +@SpringBootApplication +@ImportResource(locations = {"classpath:beans.xml"}) +public class BootApplication { + + public static void main(String[] args) { + // SpringÓ¦ÓÃÆô¶¯ÆðÀ´ + SpringApplication.run(BootApplication.class,args); + + } +} + +```` +### 3.4 @PropertySource +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔΪӦÓóÌÐòÉèÖö¨ÒåÊôÐÔÎļþ£º + +@PropertySource ×¢½âÌṩÁËÒ»ÖÖ·½±ãµÄÉùÃ÷ÐÔ»úÖÆ£¬ÓÃÓÚ½« PropertySource Ìí¼Óµ½ Spring µÄ Environment ÖУ¬Óë @Configuration ÀàÒ»ÆðʹÓᣠ+ +Äã¿ÉÒÔʹÓà @Value È¥ÒýÓö¨ÒåµÄÊôÐÔ£¬ÀýÈ磺@Value("testbean.name")£»Ò²¿ÉÒÔÖ¸¶¨Ä¬ÈÏÖµ£¬È磺@Value("testbean.name:defaultValue")¡£ + +Ó÷¨Ê¾Àý + +¸ø¶¨Ò»¸öÅäÖÃÎļþapp.properties +```` +testbean.name=myTestBean +```` +ÒÔÏ @Configuration ÀàʹÓà @PropertySource ½« app.properties ÉèÖøø Environment µÄ PropertySources ¼¯ºÏ¡£ +```` +@Configuration +@PropertySource("classpath:/com/myco/app.properties") +public class AppConfig { + + @Autowired + Environment env; + + @Bean + public TestBean testBean() { + TestBean testBean = new TestBean(); + testBean.setName(env.getProperty("testbean.name")); + return testBean; + } +} +```` + +×¢Ò⣺ʹÓà @Autowired ½« Environment ¶ÔÏó×¢Èëµ½ÅäÖÃÀàÖУ¬È»ºóÔÚ testBean() ·½·¨ÖÐʹÓᣠ+ÒÔÉÏÅäÖÃÖУ¬µ÷Óà testBean.getName() ·½·¨½«·µ»Ø¡°myTestBean¡±×Ö·û´®¡£ + +@PropertySource ÀûÓÃÁË Java 8 µÄÖØ¸´×¢½âÌØÐÔ£¬ÕâÒâζ×ÅÎÒÃÇ¿ÉÒÔÓÃËü¶à´Î±ê¼ÇÒ»¸öÀࣺ + +```` +@Configuration +@PropertySource("classpath:/annotations.properties") +@PropertySource("classpath:/vehicle-factory.properties") +class VehicleFactoryConfig {} +```` + +### 3.5 @PropertySources +Ó÷¨Í¬ÉÏ£¬Ö»²»¹ý£¬ÕâÒ»´ÎÎÒÃÇ¿ÉÒÔʹÓÃÕâ¸ö×¢½âÖ¸¶¨¶à¸ö@PropertySource ÅäÖ㺠+```` +@Configuration +@PropertySources({ +@PropertySource("classpath:/annotations.properties"), +@PropertySource("classpath:/vehicle-factory.properties") +}) +class VehicleFactoryConfig {} +```` +Çë×¢Ò⣬×Ô Java 8 ÒÔÀ´£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÉÏÊöÖØ¸´×¢Ê͹¦ÄÜʵÏÖÏàͬµÄ¹¦ÄÜ¡£ + +## 4.½áÂÛ + +ÔÚ±¾ÎÄÖУ¬ÎÒÃÇ¿´µ½ÁË×î³£¼ûµÄ Spring ºËÐÄ×¢Ê͵ĸÅÊö¡£ ÎÒÃÇ¿´µ½ÁËÈçºÎÅäÖà bean Á¬½ÓºÍÓ¦ÓÃÉÏÏÂÎÄ£¬ÒÔ¼°ÈçºÎΪ×é¼þɨÃè±ê¼ÇÀà¡£ + +springÌåϵÖеij£¼û×¢½â»¹Óкܶ࣬һƪÎÄÕ²»¿ÉÄÜÈ«²¿¸²¸Ç£¬ÈçÓÐÒÅ©£¬»¶Ó­²¹³ä¡£ + +# Spring Bean×¢½â + +## 1 ¸ÅÊö +ÔÚ±¾½Ì³ÌÖУ¬ÎÒÃǽ«ÌÖÂÛÓÃÓÚ¶¨Ò岻ͬÀàÐÍ bean µÄ×î³£¼ûµÄ Spring bean ×¢ÊÍ¡£ + +Óм¸ÖÖ·½·¨¿ÉÒÔÔÚ Spring ÈÝÆ÷ÖÐÅäÖà bean¡£ Ê×ÏÈ£¬ÎÒÃÇ¿ÉÒÔʹÓà XML ÅäÖÃÉùÃ÷ËüÃÇ¡£ ÎÒÃÇ»¹¿ÉÒÔÔÚÅäÖÃÀàÖÐʹÓÃ@Bean ×¢½âÀ´ÉùÃ÷ bean¡£ + +×îºó£¬ÎÒÃÇ¿ÉÒÔʹÓà org.springframework.stereotype °üÖеÄ×¢ÊÍÖ®Ò»À´±ê¼Ç¸ÃÀ࣬²¢½«ÆäÓಿ·ÖÁô¸ø×é¼þɨÃè¡£ + +## 2 @ComponentScan +ÕâÊÇÎÒÃǾ­³£»áʹÓõÄÒ»¸ö×¢½â£¬ÔÚÎÒÃǵÄÓ¦ÓÃÖУ¬ÓÐʱºò²»Ò»¶¨»áɨÃèËùÓеİü£¬ÌرðÊǵ±ÎÒÃÇҪɨÃèÍⲿjar°üÖеÄbeanʱ£¬Ëü·Ç³£ÓÐÓᣠ+ +Ëü¿ÉÒÔ¼ÓÔÚSpringBootApplicationÉÏ£¬Ò²¿ÉÒÔ¼ÓÔÚ@configuration×¢½âÉϵÄÅäÖÃÀàÉÏ + +Èç¹ûÆôÓÃÁË×é¼þɨÃ裬Spring ¿ÉÒÔ×Ô¶¯É¨Ãè°üÖÐµÄ bean¡£ + +@ComponentScan ÅäÖÃʹÓÃ×¢½âÅäÖÃɨÃèÄÄЩ°üµÄÀà¡£ + +ÎÒÃÇ¿ÉÒÔÖ±½ÓʹÓà basePackages »ò value ²ÎÊýÖ®Ò»Ö¸¶¨»ù±¾°üÃû³Æ£¨value ÊÇ basePackages µÄ±ðÃû£© + +```` +@Configuration +@ComponentScan(basePackages = "com.baeldung.annotations") +class VehicleFactoryConfig {} +```` +´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓà basePackageClasses ²ÎÊýÖ¸Ïò»ù´¡°üÖеÄÀࣺ + +```` +@Configuration +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +class VehicleFactoryConfig {} +```` + +ÕâÁ½¸ö²ÎÊý¶¼ÊÇÊý×飬Òò´ËÎÒÃÇ¿ÉÒÔΪÿ¸ö²ÎÊýÌṩ¶à¸ö°ü¡£ + +Èç¹ûδָ¶¨²ÎÊý£¬ÔòɨÃè·¢ÉúÔÚ´æÔÚ @ComponentScan ×¢ÊÍÀàµÄͬһ°üÖС£ + +@ComponentScan ÀûÓÃÁË Java 8 µÄÖØ¸´×¢½âÌØÐÔ£¬ÕâÒâζ×ÅÎÒÃÇ¿ÉÒÔÓÃËü¶à´Î±ê¼ÇÒ»¸öÀࣺ + +```` +@Configuration +@ComponentScan(basePackages = "com.baeldung.annotations") +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +class VehicleFactoryConfig {} +```` + +»òÕߣ¬ÎÒÃÇ¿ÉÒÔʹÓà @ComponentScans Ö¸¶¨¶à¸ö @ComponentScan ÅäÖ㺠+ +```` +@Configuration +@ComponentScans({ +@ComponentScan(basePackages = "com.baeldung.annotations"), +@ComponentScan(basePackageClasses = VehicleFactoryConfig.class) +}) +```` +```` +class VehicleFactoryConfig { +} +```` +ʹÓà XML ÅäÖÃʱ£¬ÅäÖÃ×é¼þɨÃèͬÑù¼òµ¥£º + +```` + +```` + +### 3 @Component + +@Component ÊÇÀ༶±ðµÄ×¢½â¡£ ÔÚ×é¼þɨÃèÆÚ¼ä£¬Spring Framework »á×Ô¶¯¼ì²âʹÓÃ@Component ×¢½âµÄÀࣺ +```` +@Component +class CarUtility { +// ... +} +```` + +ĬÈÏÇé¿öÏ£¬´ËÀàµÄ bean ʵÀýÓëÀàÃûͬÃû£¬Ê××ÖĸСд¡£ ´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵ĿÉѡֵ²ÎÊýÖ¸¶¨²»Í¬µÄÃû³Æ¡£ + +ÓÉÓÚ@Repository¡¢@Service¡¢@Configuration ºÍ@Controller ¶¼ÊÇ´øÓÐ@Component µÄ×¢½â£¬ËüÃǹ²ÏíÏàͬµÄbean ÃüÃûÐÐΪ¡£ + +Spring »¹»áÔÚ×é¼þɨÃè¹ý³ÌÖÐ×Ô¶¯¼ì²âËüÃÇ¡£ + +ͨ³£À´Ëµ£¬ÎÒÃÇ»áÔÚmvcÓ¦ÓÃÖÐÎÒÃÇ»áÓõ½ÉÏÊö¼¸ÖÖ×¢½â£¬¶øÎÒÃÇÔÚ·ÇwebÓ¦ÓÃÖиü¶àµØ¿ÉÒÔʹÓÃ@componentÀ´×¢½âbean + +### 4 @Repository + +DAO or Repository classes usually represent the database access layer in an application, and should be annotated with @Repository: +```` +@Repository +class VehicleRepository { +// ... +} +```` +ʹÓôË×¢Ê͵ÄÒ»¸öÓŵãÊÇËüÆôÓÃÁË×Ô¶¯³Ö¾ÃÐÔÒ쳣ת»»¡£ µ±Ê¹Óó־ÃÐÔ¿ò¼Ü£¨Èç Hibernate£©Ê±£¬ÔÚʹÓà @Repository ×¢Ê͵ÄÀàÖÐÅ׳öµÄ±¾»úÒì³£½«×Ô¶¯×ª»»Îª Spring µÄ DataAccessExeption µÄ×ÓÀà¡£ + +ÒªÆôÓÃÒ쳣ת»»£¬ÎÒÃÇÐèÒªÉùÃ÷ÎÒÃÇ×Ô¼ºµÄ PersistenceExceptionTranslationPostProcessor bean£º +```` +@Bean +public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { +return new PersistenceExceptionTranslationPostProcessor(); +} +```` +Çë×¢Ò⣬ÔÚ´ó¶àÊýÇé¿öÏ£¬Spring »á×Ô¶¯Ö´ÐÐÉÏÊö²½Öè¡£ + +»òÕßͨ¹ý XML ÅäÖ㺠+```` + +```` + +### 5 @Service +Ó¦ÓóÌÐòµÄÒµÎñÂß¼­Í¨³£×¤ÁôÔÚ·þÎñ²ãÖУ¬Òò´ËÎÒÃǽ«Ê¹ÓÃ@Service ×¢ÊÍÀ´Ö¸Ê¾Ò»¸öÀàÊôÓڸò㣺 + +```` +@Service +public class VehicleService { +// ... +} +```` +### 6 @Controller +@Controller ÊÇÒ»¸öÀ༶±ðµÄ×¢½â£¬Ëü¸æËß Spring Framework Õâ¸öÀà×÷Ϊ Spring MVC ÖеĿØÖÆÆ÷£º + +spring»á¶Ô@Controller ×¢½âµÄbean×öºÜ¶àÊÂÇ飬¾ßÌåÄÚÈÝÎÒÃÇ»áÔÚSpringMVCÏà¹ØµÄÄÚÈÝÀ´½²Êö + +```` +@Controller +public class VehicleController { +// ... +} + +```` +## 7 @Configuration + +ÅäÖÃÀà¿ÉÒÔ°üº¬ÓÃ@Bean ×¢Ê굀 bean ¶¨Òå·½·¨£º +```` +@Configuration +class VehicleFactoryConfig { + + @Bean + Engine engine() { + return new Engine(); + } + +} +```` +## 8 AOP×¢½â +µ±ÎÒÃÇʹÓà Spring ¹¹ÔìÐÍ×¢ÊÍʱ£¬ºÜÈÝÒ×´´½¨Ò»¸öÇÐÈëµã£¬¸ÃÇÐÈëµãÒÔËùÓоßÓÐÌØ¶¨¹¹ÔìÐ͵ÄÀàΪĿ±ê¡£ + +ÀýÈ磬¼ÙÉèÎÒÃÇÏë²âÁ¿ DAO ²ã·½·¨µÄÖ´ÐÐʱ¼ä¡£ ÎÒÃǽ«´´½¨ÒÔÏ·½Ã棨ʹÓà AspectJ ×¢ÊÍ£©£¬ÀûÓà @Repository ¹¹ÔìÐÍ£º + +``` +@Aspect +@Component +public class PerformanceAspect { +@Pointcut("within(@org.springframework.stereotype.Repository *)") +public void repositoryClassMethods() {}; + + @Around("repositoryClassMethods()") + public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) + throws Throwable { + long start = System.nanoTime(); + Object returnValue = joinPoint.proceed(); + long end = System.nanoTime(); + String methodName = joinPoint.getSignature().getName(); + System.out.println( + "Execution of " + methodName + " took " + + TimeUnit.NANOSECONDS.toMillis(end - start) + " ms"); + return returnValue; + } +} +```` + +ÔÚ´ËʾÀýÖУ¬ÎÒÃÇ´´½¨ÁËÒ»¸öÇÐÈëµã£¬¸ÃÇÐÈëµãÆ¥ÅäʹÓÃ@Repository ×¢Ê͵ÄÀàÖеÄËùÓз½·¨¡£ È»ºóÎÒÃÇʹÓÃ@Around ֪ͨÀ´¶¨Î»ÄǸöÇÐÈëµã£¬²¢È·¶¨À¹½Ø·½·¨µ÷ÓõÄÖ´ÐÐʱ¼ä¡£ + +´ËÍ⣬ʹÓÃÕâÖÖ·½·¨£¬ÎÒÃÇ¿ÉÒÔΪÿ¸öÓ¦ÓóÌÐò²ãÌí¼ÓÈÕÖ¾¼Ç¼¡¢ÐÔÄܹÜÀí¡¢Éó¼ÆºÍÆäËûÐÐΪ¡£ + +µ±È»ÁË£¬aspectJµÄÏà¹Ø×¢½â»¹ºÜ¶à£¬¿ÉÒÔÓÃÀ´ÉùÃ÷ÇÐÃæ£¬Î´À´ÎÒÃÇÒ²»áµ¥¶ÀдÎĽéÉÜ¡£ + +## 9 ½áÂÛ + +ÔÚ±¾ÎÄÖУ¬ÎÒÃǼì²éÁË Spring ¹¹ÔìÐÍ×¢ÊͲ¢ÌÖÂÛÁËËüÃǸ÷×Ô´ú±íµÄÓïÒåÀàÐÍ¡£ + +ÎÒÃÇ»¹Ñ§Ï°ÁËÈçºÎʹÓÃ×é¼þɨÃèÀ´¸æËßÈÝÆ÷ÔÚÄÄÀï¿ÉÒÔÕÒµ½´ø×¢Ê͵ÄÀà¡£ + +×îºó£¬ÎÒÃÇÁ˽âÁËÕâЩעÊÍÈçºÎµ¼Ö¸ɾ»¡¢·Ö²ãµÄÉè¼ÆÒÔ¼°Ó¦ÓóÌÐò¹Ø×¢µãÖ®¼äµÄ·ÖÀë¡£ ËüÃÇ»¹Ê¹ÅäÖøüС£¬ÒòΪÎÒÃDz»ÔÙÐèÒªÊÖ¶¯ÏÔʽ¶¨Òå bean¡£ +# SpringMVC×¢½â +## 1. ¸ÅÊö +ÔÚ±¾½Ì³ÌÖУ¬ÎÒÃǽ«Ì½Ë÷ org.springframework.web.bind.annotation °üÖÐµÄ Spring Web ×¢ÊÍ¡£ + +## 2. @RequestMapping + +¼òµ¥µØËµ£¬@RequestMapping ±ê¼Ç@Controller ÀàÄÚ²¿µÄÇëÇó´¦Àí³ÌÐò·½·¨£» Ëü¿ÉÒÔʹÓÃÅäÖ㺠+ +·¾¶£¬»òÆä±ðÃû¡¢Ãû³ÆºÍÖµ£º·½·¨Ó³Éäµ½Äĸö URL +·½·¨£º¼æÈÝµÄ HTTP ·½·¨ +params£º¸ù¾Ý HTTP ²ÎÊýµÄ´æÔÚ¡¢²»´æÔÚ»òÖµ¹ýÂËÇëÇó +headers£º¸ù¾Ý HTTP ±êÍ·µÄ´æÔÚ¡¢²»´æÔÚ»òÖµ¹ýÂËÇëÇó +ÏûºÄ£º¸Ã·½·¨¿ÉÒÔÔÚ HTTP ÇëÇóÕýÎÄÖÐÏûºÄÄÄЩýÌåÀàÐÍ +produces£º¸Ã·½·¨¿ÉÒÔÔÚ HTTP ÏìÓ¦ÕýÎÄÖÐÉú³ÉÄÄЩýÌåÀàÐÍ +ÕâÊÇÒ»¸ö¼òµ¥µÄʾÀý£º + +```` +@Controller +class VehicleController { + + @RequestMapping(value = "/vehicles/home", method = RequestMethod.GET) + String home() { + return "home"; + } +} +```` +Èç¹ûÎÒÃÇÔÚÀ༶±ðÓ¦ÓôË×¢ÊÍ£¬ÎÒÃÇ¿ÉÒÔΪ @Controller ÀàÖеÄËùÓд¦Àí³ÌÐò·½·¨ÌṩĬÈÏÉèÖᣠΨһµÄÀýÍâÊÇ Spring ²»»áʹÓ÷½·¨¼¶±ðÉèÖø²¸Çµ«»á¸½¼ÓÁ½¸ö·¾¶²¿·ÖµÄ URL¡£ + +ÀýÈ磬ÏÂÃæµÄÅäÖúÍÉÏÃæµÄЧ¹ûÊÇÒ»ÑùµÄ£º + +```` +@Controller +@RequestMapping(value = "/vehicles", method = RequestMethod.GET) +class VehicleController { + + @RequestMapping("/home") + String home() { + return "home"; + } +} +```` + +´ËÍ⣬@GetMapping¡¢@PostMapping¡¢@PutMapping¡¢@DeleteMapping ºÍ@PatchMapping ÊÇ@RequestMapping µÄ²»Í¬±äÌ壬ÆäHTTP ·½·¨ÒÑ·Ö±ðÉèÖÃΪGET¡¢POST¡¢PUT¡¢DELETE ºÍPATCH¡£ + +ÕâЩ´Ó Spring 4.3 °æ±¾¿ªÊ¼¿ÉÓᣠ+ +## 3 @RequestBody + +ÈÃÎÒÃǼÌÐø@RequestBody¡ª¡ªËü½« HTTP ÇëÇóµÄÖ÷ÌåÓ³Éäµ½Ò»¸ö¶ÔÏó£º + +```` +@PostMapping("/save") +void saveVehicle(@RequestBody Vehicle vehicle) { +// ... +} +```` +·´ÐòÁл¯ÊÇ×Ô¶¯µÄ£¬È¡¾öÓÚÇëÇóµÄÄÚÈÝÀàÐÍ¡£ + +## 4 @PathVariable +½ÓÏÂÀ´ËµËµ@PathVariable¡£ + +´Ë×¢ÊÍָʾ·½·¨²ÎÊý°ó¶¨µ½ URI Ä£°å±äÁ¿¡£ ÎÒÃÇ¿ÉÒÔʹÓà @RequestMapping ×¢ÊÍÖ¸¶¨ URI Ä£°å£¬²¢Ê¹Óà @PathVariable ½«·½·¨²ÎÊý°ó¶¨µ½Ä£°å²¿·ÖÖ®Ò»¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃÃû³Æ»òÆä±ðÃû£¬¼´Öµ²ÎÊýÀ´ÊµÏÖÕâÒ»µã£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable("id") long id) { +// ... +} +```` +Èç¹ûÄ£°åÖв¿·ÖµÄÃû³ÆÓë·½·¨²ÎÊýµÄÃû³ÆÏàÆ¥Å䣬ÎÒÃǾͲ»±ØÔÚ×¢ÊÍÖÐÖ¸¶¨Ëü£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable long id) { +// ... +} +```` +´ËÍ⣬ÎÒÃÇ¿ÉÒÔͨ¹ý½«ËùÐèµÄ²ÎÊýÉèÖÃΪ false À´½«Â·¾¶±äÁ¿±ê¼ÇΪ¿ÉÑ¡£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable(required = false) long id) { +// ... +} +```` +## 5. @RequestParam +We use @RequestParam for accessing HTTP request parameters: +```` +@RequestMapping +Vehicle getVehicleByParam(@RequestParam("id") long id) { +// ... +} +```` +Ëü¾ßÓÐÓë @PathVariable ×¢ÊÍÏàͬµÄÅäÖÃÑ¡Ïî¡£ + +³ýÁËÕâЩÉèÖÃÖ®Í⣬µ± Spring ÔÚÇëÇóÖз¢ÏÖûÓÐÖµ»òΪ¿Õֵʱ£¬ÎÒÃÇ¿ÉÒÔʹÓà @RequestParam Ö¸¶¨×¢ÈëÖµ¡£ Ϊ´Ë£¬ÎÒÃDZØÐëÉèÖà defaultValue ²ÎÊý¡£ + +ÌṩĬÈÏÖµÒþʽÉèÖà required Ϊ false£º +```` +@RequestMapping("/buy") +Car buyCar(@RequestParam(defaultValue = "5") int seatCount) { +// ... +} +```` +³ýÁ˲ÎÊýÖ®Í⣬ÎÒÃÇ»¹¿ÉÒÔ·ÃÎÊÆäËû HTTP ÇëÇ󲿷֣ºcookie ºÍ±êÍ·¡£ + +ÎÒÃÇ¿ÉÒÔ·Ö±ðʹÓÃ×¢½â@CookieValue ºÍ@RequestHeader À´·ÃÎÊËüÃÇ¡£ + + +## 6. Response Handling Annotations +ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«¿´µ½ÔÚ Spring MVC ÖвÙ×÷ HTTP ÏìÓ¦µÄ×î³£¼û×¢ÊÍ¡£ + +### 6.1 @ResponseBody +Èç¹ûÎÒÃÇÓÃ@ResponseBody ±ê¼ÇÇëÇó´¦Àí³ÌÐò·½·¨£¬Spring »á½«·½·¨µÄ½á¹ûÊÓΪÏìÓ¦±¾Éí£º + +```` +@ResponseBody +@RequestMapping("/hello") +String hello() { +return "Hello World!"; +} +```` +Èç¹ûÎÒÃÇÓÃÕâ¸ö×¢½âÀ´×¢½â @Controller À࣬ËùÓÐÇëÇó´¦Àí³ÌÐò·½·¨¶¼½«Ê¹ÓÃËü¡£ + +### 6.2 @ExceptionHandler + +ʹÓôË×¢ÊÍ£¬ÎÒÃÇ¿ÉÒÔÉùÃ÷Ò»¸ö×Ô¶¨Òå´íÎó´¦Àí³ÌÐò·½·¨¡£ µ±ÇëÇó´¦Àí³ÌÐò·½·¨Å׳öÈκÎÖ¸¶¨µÄÒ쳣ʱ£¬Spring µ÷Óô˷½·¨¡£ + +²¶»ñµÄÒì³£¿ÉÒÔ×÷Ϊ²ÎÊý´«µÝ¸ø·½·¨£º +```` +@ExceptionHandler(IllegalArgumentException.class) +void onIllegalArgumentException(IllegalArgumentException exception) { +// ... +} +```` + +### 6.3 @ResponseStatus +Èç¹ûÎÒÃÇʹÓôË×¢ÊͶÔÇëÇó´¦Àí³ÌÐò·½·¨½øÐÐ×¢ÊÍ£¬Ôò¿ÉÒÔÖ¸¶¨ÏìÓ¦µÄËùÐè HTTP ״̬¡£ ÎÒÃÇ¿ÉÒÔʹÓà code ²ÎÊý»òÆä±ðÃû value ²ÎÊýÀ´ÉùÃ÷״̬´úÂë¡£ + +´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓà reason ²ÎÊýÌṩԭÒò¡£ + +ÎÒÃÇÒ²¿ÉÒÔ½«ËüÓë@ExceptionHandler Ò»ÆðʹÓ㺠+ +@ExceptionHandler(IllegalArgumentException.class) +@ResponseStatus(HttpStatus.BAD_REQUEST) +void onIllegalArgumentException(IllegalArgumentException exception) { +// ... +} + +ÓÐ¹Ø HTTP ÏìӦ״̬µÄ¸ü¶àÐÅÏ¢£¬Çë·ÃÎʱ¾ÎÄ¡£ + +## 7 ÆäËû Web×¢½â +һЩעÊͲ»Ö±½Ó¹ÜÀí HTTP ÇëÇó»òÏìÓ¦¡£ ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«½éÉÜ×î³£¼ûµÄ¡£ + +### 7.1 @Controller +ÎÒÃÇ¿ÉÒÔʹÓÃ@Controller ¶¨ÒåÒ»¸öSpring MVC ¿ØÖÆÆ÷¡£ Óйظü¶àÐÅÏ¢£¬Çë·ÃÎÊÎÒÃǹØÓÚ Spring Bean Annotations µÄÎÄÕ¡£ + +### 7.2 @RestController +@RestController ½áºÏÁË@Controller ºÍ@ResponseBody¡£ + +Òò´Ë£¬ÒÔÏÂÉùÃ÷ÊǵÈЧµÄ£º + +```` +@Controller +@ResponseBody +class VehicleRestController { +// ... +} +```` + +```` +@RestController +class VehicleRestController { +// ... +} +```` +### 7.3 @ModelAttribute +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÌṩģÐͼüÀ´·ÃÎÊÒѾ­´æÔÚÓÚ MVC @Controller Ä£ÐÍÖеÄÔªËØ£º + +```` +@PostMapping("/assemble") +void assembleVehicle(@ModelAttribute("vehicle") Vehicle vehicleInModel) { +// ... +} +```` +Óë@PathVariable ºÍ@RequestParam Ò»Ñù£¬Èç¹û²ÎÊý¾ßÓÐÏàͬµÄÃû³Æ£¬ÎÒÃDz»±ØÖ¸¶¨Ä£Ðͼü£º + +```` +@PostMapping("/assemble") +void assembleVehicle(@ModelAttribute Vehicle vehicle) { +// ... +} +```` +´ËÍ⣬@ModelAttribute»¹ÓÐÒ»¸öÓÃ;£ºÈç¹ûÎÒÃÇÓÃËü×¢½âÒ»¸ö·½·¨£¬Spring»á×Ô¶¯½«·½·¨µÄ·µ»ØÖµÌí¼Óµ½Ä£ÐÍÖУº + +```` +@ModelAttribute("vehicle") +Vehicle getVehicle() { +// ... +} +```` +ºÍÒÔǰһÑù£¬ÎÒÃDz»±ØÖ¸¶¨Ä£Ðͼü£¬Spring ĬÈÏʹÓ÷½·¨µÄÃû³Æ£º +```` +@ModelAttribute +Vehicle vehicle() { +// ... +} +```` +ÔÚ Spring µ÷ÓÃÇëÇó´¦Àí³ÌÐò·½·¨Ö®Ç°£¬Ëü»áµ÷ÓÃÀàÖÐËùÓÐ @ModelAttribute ×¢Ê͵ķ½·¨¡£ + +ÓÐ¹Ø @ModelAttribute µÄ¸ü¶àÐÅÏ¢£¬Çë²ÎÔı¾ÎÄ¡£ + +### 7.4 @CrossOrigin +@CrossOrigin Ϊ´ø×¢Ê͵ÄÇëÇó´¦Àí³ÌÐò·½·¨ÆôÓÿçÓòͨÐÅ£º + +```` +@CrossOrigin +@RequestMapping("/hello") +String hello() { +return "Hello World!"; +} +```` +Èç¹ûÎÒÃÇÓÃËü±ê¼ÇÒ»¸öÀ࣬ËüÊÊÓÃÓÚÆäÖеÄËùÓÐÇëÇó´¦Àí³ÌÐò·½·¨¡£ + +ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵IJÎÊý΢µ÷ CORS ÐÐΪ¡£ + +ÓйØÏêϸÐÅÏ¢£¬Çë·ÃÎʱ¾ÎÄ¡£ + +# SpringBoot×¢½â +## 1 ¸ÅÊö +Spring Boot ͨ¹ýÆä×Ô¶¯ÅäÖù¦ÄÜʹÅäÖà Spring ±äµÃ¸ü¼ÓÈÝÒס£ + +ÔÚ±¾¿ìËٽ̳ÌÖУ¬ÎÒÃǽ«Ì½Ë÷ org.springframework.boot.autoconfigure ºÍ org.springframework.boot.autoconfigure.condition °üÖеÄ×¢½â¡£ + +## 2 @SpringBootApplication +ÎÒÃÇʹÓÃÕâ¸ö×¢½âÀ´±ê¼Ç Spring Boot Ó¦ÓóÌÐòµÄÖ÷Àࣺ + +@SpringBootApplication +ÎÒÃÇʹÓÃÕâ¸ö×¢½âÀ´±ê¼Ç Spring Boot Ó¦ÓóÌÐòµÄÖ÷Àࣺ +```` +@SpringBootApplication +class VehicleFactoryApplication { + + public static void main(String[] args) { + SpringApplication.run(VehicleFactoryApplication.class, args); + } +} +```` +@SpringBootApplication ·â×°ÁË@Configuration¡¢@EnableAutoConfiguration ºÍ@ComponentScan ×¢½â¼°ÆäĬÈÏÊôÐÔ¡£ + +## 3 @EnableAutoConfiguration + +@EnableAutoConfiguration£¬¹ËÃû˼Ò壬ÆôÓÃ×Ô¶¯ÅäÖᣠÕâÒâζ×Å Spring Boot ÔÚÆäÀà·¾¶ÖвéÕÒ×Ô¶¯ÅäÖà bean ²¢×Ô¶¯Ó¦ÓÃËüÃÇ¡£ + +Çë×¢Ò⣬ÎÒÃDZØÐ뽫´Ë×¢ÊÍÓë@Configuration Ò»ÆðʹÓ㺠+ +```` +@Configuration +@EnableAutoConfiguration +class VehicleFactoryConfig {} +```` + +## 4 @ConfigurationÒÔ¼°Ïà¹ØÀà + +@ConfigurationµÄ×÷Ó㺱ê×¢ÔÚÀàÉÏ£¬ÅäÖÃspringÈÝÆ÷(Ó¦ÓÃÉÏÏÂÎÄ)¡£ + +ÒÔÍùÎÒÃÇÔÚspringÖÐÅäÖÃbean»áʹÓõ½xmlÅäÖÃÎļþ£¬¶¨ÒåÒ»¸ö¸öbean£¬ÔÚspringbootÖУ¬ÎªÁË×öµ½ÁãÅäÖã¬spring¿ò¼ÜÌṩÁË@ConfigurationÕâÒ»×¢½â + +Ï൱ÓڰѸÃÀà×÷ΪspringµÄxmlÅäÖÃÎļþÖÐµÄ + +@Configuration×¢½âµÄÀàÖУ¬Ê¹ÓÃ@Bean×¢½â±ê×¢µÄ·½·¨£¬·µ»ØµÄÀàÐͶ¼»áÖ±½Ó×¢²áΪbean¡£ + +@Configure×¢½âµÄ¶¨ÒåÈçÏ£º +```` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Configuration { +String value() default ""; +} +```` +´Ó¶¨ÒåÀ´¿´£¬µ×²ãÊǺ¬ÓÐ@Component £¬ËùÒÔ@Configuration ¾ßÓÐºÍ @Component µÄ×÷Óá£Òò´Ëcontext:component-scan/»òÕß@ComponentScan¶¼ÄÜ´¦Àí@Configuration×¢½âµÄÀà¡£ + +ͨ³££¬µ±ÎÒÃDZàд×Ô¶¨Òå×Ô¶¯ÅäÖÃʱ£¬ÎÒÃÇÏ£Íû Spring ÓÐÌõ¼þµØÊ¹ÓÃËüÃÇ¡£ ÎÒÃÇ¿ÉÒÔͨ¹ý±¾½ÚÖеÄ×¢ÊÍÀ´ÊµÏÖÕâÒ»µã¡£ + +ÎÒÃÇ¿ÉÒÔ½«´Ë²¿·ÖÖеÄ×¢ÊÍ·ÅÔÚ@Configuration Àà»ò@Bean ·½·¨ÉÏ¡£ + +ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«Ö»½éÉÜÿ¸öÌõ¼þ±³ºóµÄ»ù±¾¸ÅÄî¡£ ÓûÁ˽â¸ü¶àÐÅÏ¢£¬Çë·ÃÎÊÕâÆªÎÄÕ¡£ + +### 4.1 @ConditionalOnClass and @ConditionalOnMissingClass +ºÜÃ÷ÏÔÕâÊÇÒ»¸ö´øÓÐÌõ¼þÅжϵÄÅäÖÃ×¢½â£¬ÒªÖªµÀ£¬ºÜ¶àʱºòÎÒÃǰ´Ðè¼ÓÔØbeanµÄ£¬ÉõÖÁÊǸù¾ÝÍⲿjar°üÊÇ·ñ´æÔÚÀ´½øÐмÓÔØºÍÅжϵġ£ + +ÕâÖÖʱºò£¬¾ÍÒª¸ù¾ÝÍⲿÀàÊÇ·ñ´æÔÚÀ´¾ö¶¨ÊÇ·ñÊÇ·ñ¼ÓÔØ¸Ãbean + +ʹÓÃÕâЩÌõ¼þ£¬Èç¹û×¢ÊͲÎÊýÖеÄÀà´æÔÚ/²»´æÔÚ£¬Spring ½«½öʹÓñê¼ÇµÄ×Ô¶¯ÅäÖà bean£º + +```` +@Configuration +@ConditionalOnClass(DataSource.class) +class MySQLAutoconfiguration { +//... +} +```` + +### 4.2 @ConditionalOnBean and @ConditionalOnMissingBean + +µ±ÎÒÃÇÏëÒª¸ù¾ÝÌØ¶¨ bean µÄ´æÔÚ»ò²»´æÔÚÀ´¶¨ÒåÌõ¼þʱ£¬ÎÒÃÇ¿ÉÒÔʹÓÃÕâЩעÊÍ£º + +ºÍÉÏÃæÒ»¸ö×¢½âÉÔÓÐЩ²»Í¬£¬ÒòΪÎÒÃǵÄÅжÏÌõ¼þ±ä³ÉÁËbean + +```` +@Bean +@ConditionalOnBean(name = "dataSource") +LocalContainerEntityManagerFactoryBean entityManagerFactory() { +// ... +} +```` +### 4.3 @ConditionalOnProperty +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔ¶ÔÊôÐÔµÄÖµÉèÖÃÌõ¼þ + +ҪעÒ⣬ÕâÀïµÄÊôÐÔÖµÀ´Ô´ÓÚapplication.propertiesÎļþÖеÄÅäÖà + +```` +@Bean +@ConditionalOnProperty( +name = "usemysql", +havingValue = "local" +) +DataSource dataSource() { +// ... +} +```` + +### 4.4 @ConditionalOnResource + +ÎÒÃÇ¿ÉÒÔÈà Spring ½öÔÚ´æÔÚÌØ¶¨×ÊԴʱʹÓö¨Ò壺 +¹ËÃû˼Ò壬ҪÇóclasspath´æÔÚÕâ¸ö×ÊÔ´Îļþʱ²Å½øÐмÓÔØ£¬ÕâÒ²ÊǺܳ£ÓõÄÒ»¸ö×¢½â¡£ + +```` + +@ConditionalOnResource(resources = "classpath:mysql.properties") +Properties ditionalProperties() { +// ... +} +```` + +### 4.5 @ConditionalOnWebApplication and @ConditionalOnNotWebApplication +Õâ¸ö×¢½âͨ³£ÓÃÓÚºÍwebÓÐÇ¿¹ØÁª£¬»òÕßÍêÈ«ÎÞ¹ØÁªµÄÅäÖÃÉÏ + +ʹÓÃÕâЩעÊÍ£¬ÎÒÃÇ¿ÉÒÔ¸ù¾Ýµ±Ç°Ó¦ÓóÌÐòÊÇ·ñÊÇ Web Ó¦ÓóÌÐòÀ´´´½¨Ìõ¼þ£º +```` + +@ConditionalOnWebApplication +HealthCheckController healthCheckController() { +// ... +} +```` + +### 4.6 @ConditionalExpression +springbootÕæµÄΪÎÒÃÇÏëµ½ÁËËùÓÐÇé¿ö£¬Èç¹ûÉÏÃæµÄ×¢½â»¹²»ÄÜÂú×ãÄãµÄÒªÇó£¬ÄÇô¸É´à¾ÍÈÃÄã×Ô¼ºÐ´£¬ÕâÏÂÓ¦¸ÃûÓÐÎÊÌâ°É£¿ + +ÎÒÃÇ¿ÉÒÔÔÚ¸ü¸´ÔÓµÄÇé¿öÏÂʹÓÃÕâ¸ö×¢½â¡£ µ± SpEL ±í´ïʽ±»ÆÀ¹ÀÎªÕæÊ±£¬Spring ½«Ê¹Óñê¼ÇµÄ¶¨Ò壺 + +```` +@Bean +@ConditionalOnExpression("${usemysql} && ${mysqlserver == 'local'}") +DataSource dataSource() { +// ... +} +```` + +### 4.7 @Conditional +ʲô£¬»¹ÓÐÎÊÌ⣿ +ÄÇspringbootÒ²²»ÔÙÌṩʲô±í´ïʽ¸øÄãÁË£¬Ö±½ÓÈÃÓû§Ð´Ò»¸öÅжÏÌõ¼þ£¬¸æËßÎÒtrue»òÕßfalse¾ÍÐÐÁË + +¶ÔÓÚ¸ü¸´ÔÓµÄÌõ¼þ£¬ÎÒÃÇ¿ÉÒÔ´´½¨Ò»¸öÆÀ¹À×Ô¶¨ÒåÌõ¼þµÄÀà¡£ ÎÒÃǸæËß Spring ½«Õâ¸ö×Ô¶¨ÒåÌõ¼þÓë @Conditional Ò»ÆðʹÓ㺠+ +```` +@Conditional(HibernateCondition.class) +Properties ditionalProperties() { +//... +} +```` + +## 5 ×ܽá +ÔÚ±¾ÎÄÖУ¬ÎÒÃǸÅÊöÁËÈçºÎ΢µ÷×Ô¶¯ÅäÖùý³Ì²¢Îª×Ô¶¨Òå×Ô¶¯ÅäÖà bean ÌṩÌõ¼þ¡£ + +# ²Î¿¼ÎÄÕ +https://www.baeldung.com/spring-annotations diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\345\237\272\344\272\216SpringBoot\344\270\255\347\232\204\345\274\200\346\272\220\347\233\221\346\216\247\345\267\245\345\205\267SpringBootAdmin.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\345\237\272\344\272\216SpringBoot\344\270\255\347\232\204\345\274\200\346\272\220\347\233\221\346\216\247\345\267\245\345\205\267SpringBootAdmin.md" new file mode 100644 index 0000000..fc25a2c --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\345\237\272\344\272\216SpringBoot\344\270\255\347\232\204\345\274\200\346\272\220\347\233\221\346\216\247\345\267\245\345\205\267SpringBootAdmin.md" @@ -0,0 +1,294 @@ +Spring Boot Admin(SBA)ÊÇÒ»¸ö¿ªÔ´µÄÉçÇøÏîÄ¿£¬ÓÃÓÚ¹ÜÀíºÍ¼à¿Ø Spring Boot Ó¦ÓóÌÐò¡£Ó¦ÓóÌÐò¿ÉÒÔͨ¹ý http µÄ·½Ê½£¬»ò Spring Cloud ·þÎñ·¢ÏÖ»ú֯ע²áµ½ SBA ÖУ¬È»ºó¾Í¿ÉÒÔʵÏÖ¶Ô Spring Boot ÏîÄ¿µÄ¿ÉÊÓ»¯¹ÜÀíºÍ²é¿´ÁË¡£ + +Spring Boot Admin ¿ÉÒÔ¼à¿Ø Spring Boot µ¥»ú»ò¼¯ÈºÏîÄ¿£¬ËüÌṩÏêϸµÄ½¡¿µ (Health)ÐÅÏ¢¡¢ÄÚ´æÐÅÏ¢¡¢JVM ϵͳºÍ»·¾³ÊôÐÔ¡¢À¬»ø»ØÊÕÐÅÏ¢¡¢ÈÕÖ¾ÉèÖúͲ鿴¡¢¶¨Ê±ÈÎÎñ²é¿´¡¢Spring Boot »º´æ²é¿´ºÍ¹ÜÀíµÈ¹¦ÄÜ¡£½ÓÏÂÀ´ÎÒÃÇÒ»ÆðÀ´´î½¨ºÍʹÓðɡ£ + +Ëü×îÖÕµÄչʾЧ¹ûÈçÏ£º + +[![](https://s5.51cto.com/oss/202201/14/5d142e8c6b544f7b981b3eff8099b3d8.png)](https://s5.51cto.com/oss/202201/14/5d142e8c6b544f7b981b3eff8099b3d8.png) + +## 1.´î½¨SBA¼à¿Ø¶Ë + +ÎÒÃÇÐèÒª´´½¨Ò»¸ö Spring Boot Admin ÏîÄ¿£¬ÓÃÀ´¼à¿ØºÍ¹ÜÀíÎÒÃÇµÄ Spring Boot ÏîÄ¿£¬´î½¨µÄ·½Ê½ºÍ´´½¨ÆÕͨµÄ Spring Boot ÏîÄ¿ÀàËÆ£¬¾ßÌå²½ÖèÈçÏ¡£Ê¹Óà Idea ´´½¨Ò»¸ö Spring Boot ÏîÄ¿£º + +[![](https://s5.51cto.com/oss/202201/14/d97c492785db6ff2ded49175184ceda9.png)](https://s5.51cto.com/oss/202201/14/d97c492785db6ff2ded49175184ceda9.png) + +[![](https://s3.51cto.com/oss/202201/14/8bb1f0389b95e174b56ac01ba313ec7b.png)](https://s3.51cto.com/oss/202201/14/8bb1f0389b95e174b56ac01ba313ec7b.png) + +ÕâÀïÐèҪעÒ⣬ÐèÒªÌí¼Ó Spring Boot Admin(Server)·þÎñ¶Ë¿ò¼ÜµÄÖ§³Ö£¬ÈçÏÂͼËùʾ£º + +[![](https://s4.51cto.com/oss/202201/14/122e9f0726fde8ac0c8936c79ef12f5f.png)](https://s4.51cto.com/oss/202201/14/122e9f0726fde8ac0c8936c79ef12f5f.png) + +Ò²¾ÍÊÇ´´½¨µÄ Spring Boot ÏîÄ¿ÐèÒªÌí¼ÓÒÔÏÂÁ½¸öÖØÒªµÄ¿ò¼ÜÖ§³Ö£º + + + + + +``` + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-server + + +``` + + + + + + + +### 1.1 ¿ªÆôSBA·þÎñ + +´´½¨ÍêÏîĿ֮ºó£¬ÐèÒªÔÚÆô¶¯ÀàÉÏ¿ªÆô SBA ·þÎñ£º + + + + + +``` +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@EnableAdminServer // Ìí¼Ó´ËÐдúÂë +@SpringBootApplication +public class SbaserverApplication { + public static void main(String[] args) { + SpringApplication.run(SbaserverApplication.class, args); + } +} + +``` + + + + + +### 1.2 ÅäÖÃSBA¶Ë¿ÚºÅ + +ÔÚ application.properties ÖÐÅäÖÃÒ»¸öÏîÄ¿µÄ¶Ë¿ÚºÅ¾Í¿ÉÒÔÖ±½ÓÆô¶¯ÁË£¬ÎÒÅäÖõĶ˿ںÅÊÇ 9001£º + + + + + +``` +server.port=9001 + +``` + + + + + +PS£ºÅäÖö˿ںŵÄÖ÷ҪĿµÄÊÇΪÁ˲»ºÍÆäËû Spring Boot ÏîÄ¿³åÍ»£¬Èç¹û SBA Êǵ¥¶À²¿Êð´Ë²½Öè¿ÉÒÔºöÂÔ¡£ + +Æô¶¯ÏîĿ֮ºó£¬¾Í¿ÉÒÔ¿´µ½ SBA µÄÖ÷Ò³ÁË£¬ÈçÏÂͼËùʾ£º + +[![](https://s5.51cto.com/oss/202201/14/20418ce88230b20b234f9e9c15e07f47.png)](https://s5.51cto.com/oss/202201/14/20418ce88230b20b234f9e9c15e07f47.png) + +´Ëʱ SBA Öл¹Ã»ÓÐÌí¼ÓÈκÎÐèÒª¼à¿ØµÄÏîÄ¿£¬½ÓÏÂÀ´ÎÒÃÇÔÙ´´½¨Ò»¸ö Spring Boot ÏîÄ¿£¬¼ÓÈëµ½ SBA ÖÐÀ´½øÐÐ¼à¿ØºÍ¹ÜÀí°É¡£ + +## 2.´´½¨Ò»¸öÆÕͨSpringBootÏîÄ¿ + +Ê×ÏÈ£¬ÎÒÃÇÐèÒª´´½¨Ò»¸öÆÕͨµÄ Spring Boot ÏîÄ¿£¬¾ßÌåµÄ´´½¨²½ÖèÕâÀï¾Í²»ÑÝʾÁË¡£µ±´´½¨ºÃ Spring Boot ÏîĿ֮ºó£¬ÐèÒªÔÚÕâ¸ö Spring Boot ÏîÄ¿ÖÐÐèÒªÌí¼Ó SBA ¿Í»§¶Ë¿ò¼ÜµÄÖ§³Ö£¬Ò²¾ÍÊÇÔÚ pom.xml ÖÐÅäÖÃÈçÏÂÄÚÈÝ£º + + + + + + + +``` + + de.codecentric + spring-boot-admin-starter-client + + +``` + + + + + +È»ºóÔÚ application.properties ÎļþÖÐÅäÖà SBA ·þÎñÆ÷¶ËµØÖ·£¬Ò²¾ÍÊÇÔÛÃǵÚÒ»²½´´½¨ SBA ÏîÄ¿µÄµØÖ·£¬ÅäÖÃÄÚÈÝÈçÏ£º + + + + + +``` +# µ±Ç°ÏîÄ¿¶Ë¿ÚºÅ +server.port=8080 +# Spring Boot Admin ¼à¿Ø·þÎñÆ÷¶ËµØÖ· +spring.boot.admin.client.url=http://localhost:9001 + +``` + + + + + +ÆäÖС°spring.boot.admin.client.url¡±Îª SBA ¼à¿ØµØÖ·¡£ + +## 3.SpringBootAdmin¼à¿Ø×ÜÀÀ + +ÅäÖÃÍêÒÔÉÏÐÅÏ¢Ö®ºó£¬´Ëʱ²é¿´ Spring Boot Admin Ò³ÃæÖоÍÓб»¼à¿ØµÄ Spring Boot ÏîÄ¿ÁË£¬ÈçÏÂͼËùʾ£º + +[![](https://s5.51cto.com/oss/202201/14/c010770a5cdfe5fad0ad1e8f0c3b07dc.png)](https://s5.51cto.com/oss/202201/14/c010770a5cdfe5fad0ad1e8f0c3b07dc.png) + +Ò²¿ÉÒÔµã»÷¡°Ó¦ÓÃǽ¡±²é¿´ Spring Boot Admin ÖÐËùÓб»¼à¿ØµÄ Spring Boot ÏîÄ¿£¬ÈçÏÂͼËùʾ£º + +[![](https://s4.51cto.com/oss/202201/14/d88e33f87e116000f9717e8c19c43cc4.png)](https://s4.51cto.com/oss/202201/14/d88e33f87e116000f9717e8c19c43cc4.png) + +µã»÷Ó¦ÓýøÈëÏêÇéÒ³Ãæ£¬ÈçÏÂͼËùʾ£º + +[![](https://s2.51cto.com/oss/202201/14/4c3609840ea5cb45779eafbc2b260324.png)](https://s2.51cto.com/oss/202201/14/4c3609840ea5cb45779eafbc2b260324.png) + +[![](https://s4.51cto.com/oss/202201/14/2912ed434d97f8dd49c27ce73252d34c.png)](https://s4.51cto.com/oss/202201/14/2912ed434d97f8dd49c27ce73252d34c.png) + +ʼþÈÕÖ¾Öаüº¬ Spring Boot ¸÷ÖÖ״̬µÄչʾ(UP ΪÕý³£¡¢OFFLINE ΪÒì³£)ºÍ·¢ÉúµÄʱ¼ä£¬ÈçÏÂͼËùʾ£º + +[![](https://s5.51cto.com/oss/202201/14/5792a62fbcafe6978bfe3bd26cf1e3ab.png)](https://s5.51cto.com/oss/202201/14/5792a62fbcafe6978bfe3bd26cf1e3ab.png) + +## 4.SpringBootÒì³£¼à¿Ø + +µ±ÎÒÃÇÊÖ¶¯°Ñ±»¼à¿ØµÄ Spring Boot ÏîĿֹ֮ͣºó£¬ÔÚ Spring Boot Admin ÖоͿÉÒԲ鿴µ½Ò»¸öÓ¦ÓÃÒѾ­±»Í£µôÁË£¬ÈçÏÂͼËùʾ£º + +[![](https://s5.51cto.com/oss/202201/14/47569a3fe09e62b2364c26bdbd7da4bc.png)](https://s5.51cto.com/oss/202201/14/47569a3fe09e62b2364c26bdbd7da4bc.png) + +ÎÒÃÇÒ²¿ÉÒÔͨ¹ýʼþÈÕÖ¾²é¿´ Spring Boot å´»úµÄ¾ßÌåʱ¼ä£¬ÈçÏÂͼËùʾ£º + +[![](https://s2.51cto.com/oss/202201/14/b63f631561fa646f85ccf3e1e4321939.png)](https://s2.51cto.com/oss/202201/14/b63f631561fa646f85ccf3e1e4321939.png) + +## 5.ÅäÖò鿴¸ü¶à¼à¿ØÏî + +ͨ¹ýÉÏÃæµÄÄÚÈÝÎÒÃÇ¿ÉÒÔ¿´³ö£¬¼à¿ØµÄ Spring Boot Ñ¡ÏÊDZȽÏÉٵģ¬Ôõô²ÅÄܲ鿴¸ü¶àµÄ¼à¿ØÏîÄØ?Òª½â¾öÕâ¸öÎÊÌ⣬ÎÒÃÇÐèÒªÔÚ±»¼à¿ØµÄ Spring Boot ÏîÄ¿ÖÐÌí¼Ó spring-boot-starter-actuator ¿ò¼ÜµÄÖ§³Ö£¬²¢¿ªÆô²é¿´ËùÓÐ¼à¿ØÏîµÄÅäÖòÅÐУ¬×îÖÕչʾЧ¹ûÈçÏ£º + +[![](https://s4.51cto.com/oss/202201/14/03938ac0bded4487b6720fc4657f9e99.png)](https://s4.51cto.com/oss/202201/14/03938ac0bded4487b6720fc4657f9e99.png) + +½ÓÏÂÀ´ÎÒÃÇÀ´ÅäÖÃÒ»ÏÂÕâЩ¼à¿ØÏî¡£ + +### 5.1 Ìí¼Óactuator¿ò¼ÜÖ§³Ö + +ÔÚ±»¼à¿ØµÄ Spring Boot ÏîÄ¿ÖÐÌí¼Ó actuator ¿ò¼ÜÖ§³Ö£¬Ò²¾ÍÊÇÔÚ pom.xml ÖÐÌí¼ÓÒÔÏÂÅäÖ㺠+ + + + + + + +``` + + org.springframework.boot + spring-boot-starter-actuator + + +``` + + + + + +ÊÖ¶¯µã»÷ Maven µ¼ÈëÒÀÀµ°ü(Èç¹û¿ªÆôÁË×Ô¶¯µ¼È룬´Ë²½Öè¿ÉºöÂÔ)¡£ + +### 5.2 ÅäÖÿª·ÅËùÓÐ¼à¿ØÏî + +ÔÚ±»¼à¿ØµÄ Spring Boot ÏîÄ¿ÖÐÌí¼ÓÒÔÏÂÅäÖ㺠+ + + + + +``` +# ¿ªÆô¼à¿ØËùÓÐÏî +management.endpoints.web.exposure.include=* + +``` + + + + + +ÒÔÉϵÄÅäÖÃÊÇ¿ª·Å¼à¿ØËùÓÐÑ¡ÏÅäÖÃÍêÖ®ºó£¬ÖØÆô´Ë Spring Boot ÏîÄ¿£¬È»ºóÔÙˢРSpring Boot Admin ¸ü¶àµÄ¼à¿ØÏî¾Íչʾ³öÀ´ÁË£¬ÈçÏÂͼËùʾ£º + +[![](https://s6.51cto.com/oss/202201/14/2cc24e05bc6185ba1869872db5a864a5.png)](https://s6.51cto.com/oss/202201/14/2cc24e05bc6185ba1869872db5a864a5.png) + +### 5.3 ¼à¿ØÏîĿԤÀÀ + +½« Spring Boot µÄËùÓÐ¼à¿ØÏî¶¼¿ªÆôÖ®ºó£¬Í¨¹ý SBA ¾Í¿ÉÒԲ鿴ÒÔÏÂÄÚÈÝÁË£º + +* Æô¶¯Ê±¼ä¡¢ÀÛ¼ÆÔËÐÐʱ¼ä; +* ½ø³ÌºÍÏß³ÌÊýÁ¿ºÍÕ¼ÓÃµÄ CPU ×ÊÔ´; +* À¬»ø»ØÊÕÏêÇéÐÅÏ¢£¬»ØÊÕ´ÎÊýºÍ»¨·Ñʱ¼ä; +* JVM Ïß³Ìת´¢¡¢ÄÚ´æ×ª´¢ÏêÇéºÍ¶ÔÓ¦µÄÎļþÏÂÔØ; +* ¿ÉÒԲ鿴ºÍÅäÖà Spring Boot ÏîÄ¿ÖеÄÈÕÖ¾¼¶±ð; +* ²é¿´ Spring Boot ÏîÄ¿ÐÔÄÜ¼à¿Ø; +* ²é¿´ Spring Boot ÔËÐл·¾³ÐÅÏ¢; +* ²é¿´ Spring Boot ËùÓÐÀàÐÅÏ¢; +* ²é¿´ Spring Boot ÖеĶ¨Ê±ÈÎÎñ; +* ²é¿´ºÍ¹ÜÀí Spring Boot ÏîÄ¿ÖеÄËùÓлº´æ¡£ + +ÒÔÏÂÊǼ¸¸öÖØÒªÒ³ÃæµÄ½ØÍ¼£¬ÎÒÃÇÒ»ÆðÀ´¿´¡£ + +### 5.3.1 ²é¿´ÔËÐл·¾³ + +[![](https://s5.51cto.com/oss/202201/14/a9db77b1b0e378450086edd1ab438df5.png)](https://s5.51cto.com/oss/202201/14/a9db77b1b0e378450086edd1ab438df5.png) + +[![](https://s2.51cto.com/oss/202201/14/17604f4f5eb388a2a6c08f55e1e050ac.png)](https://s2.51cto.com/oss/202201/14/17604f4f5eb388a2a6c08f55e1e050ac.png) + +### 5.3.2 ²é¿´¶¨Ê±ÈÎÎñ + +[![](https://s5.51cto.com/oss/202201/14/113d94b9bd488e239967915aededc89c.png)](https://s5.51cto.com/oss/202201/14/113d94b9bd488e239967915aededc89c.png) + +### 5.3.3 ÏîÄ¿ÈÕÖ¾¼¶±ðÅäÖà + +[![](https://s3.51cto.com/oss/202201/14/8da60846eecbdbd9472ae6dbf17d951f.png)](https://s3.51cto.com/oss/202201/14/8da60846eecbdbd9472ae6dbf17d951f.png) + +ÎÒÃÇ¿ÉÒÔͨ¹ý Spring Boot Admin À´¶¯Ì¬µÄÅäÖÃÏîÄ¿ÖеÄÈÕÖ¾¼¶±ð¡£ + +### 5.3.4 JVMÏ̺߳ÍÄÚ´æ²é¿´ + +[![](https://s2.51cto.com/oss/202201/14/d3f98228a8b19675475c863457821034.png)](https://s2.51cto.com/oss/202201/14/d3f98228a8b19675475c863457821034.png) + +### 5.3.5 ²é¿´SpringBootËùÓлº´æ + +[![](https://s5.51cto.com/oss/202201/14/ae811102080c26b11135be50cd889710.png)](https://s5.51cto.com/oss/202201/14/ae811102080c26b11135be50cd889710.png) + +µ±È»ÎÒÃÇ»¹¿ÉÒÔ¶ÔÕâЩ»º´æ½øÐÐɾ³ý²Ù×÷¡£ + +## 6.²é¿´ÏîĿʵʱÈÕÖ¾ + +ÏëÒª²é¿´¼à¿ØÏîÄ¿ÖеÄÈÕÖ¾ÐÅÏ¢£¬ÓÐÒ»¸öǰÌáÌõ¼þ£¬Ç°ÌáÌõ¼þÊÇÄã±»¼à¿ØµÄ Spring Boot ÏîÄ¿£¬±ØÐëÅäÖÃÁËÈÕÖ¾µÄ±£´æÂ·¾¶»òÕßÈÕÖ¾±£´æÎļþÃû£¬Ö»ÓÐÅäÖÃÕâÁ½ÏîÖеÄÈÎÒâÒ»ÏÄãµÄ Spring Boot ÏîÄ¿²Å»á½«ÈÕÖ¾±£´æµ½´ÅÅÌÉÏ£¬ÕâÑù²ÅÄÜͨ¹ý SBA ²é¿´µ½£¬ÎÒÅäÖõÄÊÇÈÕ־·¾¶£¬ÔÚ Spring Boot µÄ application.properties ÅäÖÃÎļþÖÐÌí¼ÓÒÔÏÂÅäÖ㺠+ + + + + +``` +# ÉèÖÃÈÕÖ¾±£´æÂ·¾¶ +logging.file.path=C:\\work\\log + +``` + + + + + +ÉèÖÃÍê³ÉÖ®ºó£¬ÖØÆôÄãµÄ Spring Boot ÏîÄ¿£¬È»ºóˢРSBA Ò³Ãæ£¬×îÖÕչʾЧ¹ûÈçÏ£º + +[![](https://s6.51cto.com/oss/202201/14/3f03c6402cc8a2532ed45ab43be156ac.png)](https://s6.51cto.com/oss/202201/14/3f03c6402cc8a2532ed45ab43be156ac.png) + +´ËʱÎÒÃǾͿÉÒԲ鿴ʵʱµÄÈÕÖ¾ÐÅÏ¢ÁË£¬µ±È»ÄãÒ²¿ÉÒÔËæÊ±ÏÂÔØÈÕÖ¾£¬Èç¹ûÐèÒªµÄ»°¡£ + +## ×ܽá + +Spring Boot Admin(SBA)ÊÇÒ»¸öÉçÇø¿ªÔ´ÏîÄ¿£¬ÓÃÓÚ¹ÜÀíºÍ¼àÊÓ Spring Boot Ó¦ÓóÌÐò£¬ËüÌṩÏêϸµÄ½¡¿µ (Health)ÐÅÏ¢¡¢ÄÚ´æÐÅÏ¢¡¢JVM ϵͳºÍ»·¾³ÊôÐÔ¡¢À¬»ø»ØÊÕÐÅÏ¢¡¢ÈÕÖ¾ÉèÖúͲ鿴¡¢¶¨Ê±ÈÎÎñ²é¿´¡¢Spring Boot »º´æ²é¿´ºÍ¹ÜÀíµÈ¹¦ÄÜ¡£ + +ÎÒÃÇÐèÒª´´½¨Ò»¸ö SBA ·þÎñÆ÷¶ËÓÃÀ´¼à¿ØÒ»¸ö»ò¶à¸ö Spring Boot ÏîÄ¿£¬±»¼à¿ØµÄ Spring Boot ÏîĿҪÌí¼Ó SBA Client ¿ò¼ÜµÄÖ§³Ö£¬ÇÒÌí¼Ó actuator ¿ò¼ÜºÍÏàÓ¦µÄÅäÖ㬾ͿÉÒÔʵÏÖ¶Ô Spring Boot ÏîÄ¿µÄÍêÃÀ¼à¿ØÁË¡£ \ No newline at end of file diff --git "a/docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" similarity index 96% rename from "docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" index 8564da9..96260af 100644 --- "a/docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" @@ -8,7 +8,6 @@ * [2.3ã€�@Import](#23ã€�import) * [2.4ã€�@Conditional](#24ã€�conditional) * [2.5ã€�@ConfigurationProperties与@EnableConfigurationProperties](#25ã€�configurationproperties与enableconfigurationproperties) - * [](#) * [三ã€�削é“�如泥:SpringFactoriesLoader详解](#三ã€�削é“�如泥:springfactoriesloader详解) * [å››ã€�å�¦ä¸€ä»¶æ­¦å™¨ï¼šSpring容器的事件监å�¬æœºåˆ¶](#å››ã€�å�¦ä¸€ä»¶æ­¦å™¨ï¼šspring容器的事件监å�¬æœºåˆ¶) * [Spring容器内的事件监å�¬æœºåˆ¶](#spring容器内的事件监å�¬æœºåˆ¶) @@ -17,9 +16,6 @@ * [6.1 SpringApplicationåˆ�始化](#61-springapplicationåˆ�始化) * [6.2 Spring Bootå�¯åЍæµ�程](#62-spring-bootå�¯åЍæµ�程) * [å�‚考文章](#å�‚考文章) -* [微信公众å�·](#微信公众å�·) - * [个人公众å�·ï¼šé»„å°�æ–œ](#个人公众å�·ï¼šé»„å°�æ–œ) - * [技术公众å�·ï¼šJava技术江湖](#技术公众å�·ï¼šjava技术江湖) 本文转自互è�”网,侵删 @@ -806,30 +802,3 @@ public void initialize(ConfigurableApplicationContext context) { [5][spring boot实战:自动é…�置原ç�†åˆ†æž�](https://link.jianshu.com/?t=http%3A%2F%2Fblog.csdn.net%2Fliaokailin%2Farticle%2Fdetails%2F49559951):[http://blog.csdn.net/liaokailin/article/details/49559951](https://link.jianshu.com/?t=http%3A%2F%2Fblog.csdn.net%2Fliaokailin%2Farticle%2Fdetails%2F49559951) [6][spring boot实战:Spring boot Bean加载æº�ç �分æž�](https://link.jianshu.com/?t=http%3A%2F%2Fblog.csdn.net%2Fliaokailin%2Farticle%2Fdetails%2F49107209):[http://blog.csdn.net/liaokailin/article/details/49107209](https://link.jianshu.com/?t=http%3A%2F%2Fblog.csdn.net%2Fliaokailin%2Farticle%2Fdetails%2F49107209) - - -## 微信公众å�· - -### 个人公众å�·ï¼šé»„å°�æ–œ - -黄å°�斜是跨考软件工程的 985 硕士,自学 Java 两年,拿到了 BAT 等近å��家大厂 offer,从技术å°�白æˆ�长为阿里工程师。 - -作者专注于 JAVA å�Žç«¯æŠ€æœ¯æ ˆï¼Œçƒ­è¡·äºŽåˆ†äº«ç¨‹åº�员干货ã€�学习ç»�验ã€�求è�Œå¿ƒå¾—和程åº�人生,目å‰�黄å°�斜的CSDNå�šå®¢æœ‰ç™¾ä¸‡+访问é‡�,知乎粉ä¸�2W+,全网已有10W+读者。 - -黄å°�斜是一个斜æ� é�’年,å�šæŒ�学习和写作,相信终身学习的力é‡�,希望和更多的程åº�员交朋å�‹ï¼Œä¸€èµ·è¿›æ­¥å’Œæˆ�é•¿ï¼� - -**原创电å­�书:** -关注公众å�·ã€�黄å°�斜】å�Žå›žå¤�ã€�原创电å­�书】å�³å�¯é¢†å�–我原创的电å­�书《è�œé¸Ÿç¨‹åº�员修炼手册:从技术å°�白到阿里巴巴Java工程师》 - -**程åº�员3T技术学习资æº�:** 一些程åº�员学习技术的资æº�大礼包,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“资料â€�** å�³å�¯å…�费无套路获å�–。 -**考研å¤�习资料:** 计算机考研大礼包,都是我自己考研å¤�习时用的一些å¤�习资料,包括公共课和专业的å¤�习视频,这里也推è��给大家,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“考研â€�** å�³å�¯å…�费获å�–。 -![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20190829222750556.jpg) - - -### 技术公众å�·ï¼šJava技术江湖 - -如果大家想è¦�实时关注我更新的文章以å�Šåˆ†äº«çš„干货的è¯�,å�¯ä»¥å…³æ³¨æˆ‘的公众å�·ã€�Java技术江湖】一ä½�阿里 Java 工程师的技术å°�站,作者黄å°�斜,专注 Java 相关技术:SSMã€�SpringBootã€�MySQLã€�分布å¼�ã€�中间件ã€�集群ã€�Linuxã€�网络ã€�多线程,å�¶å°”讲点Dockerã€�ELK,å�Œæ—¶ä¹Ÿåˆ†äº«æŠ€æœ¯å¹²è´§å’Œå­¦ä¹ ç»�验,致力于Java全栈开å�‘ï¼� - -**Java工程师必备学习资æº�:** 一些Java工程师常用学习资æº�,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“Javaâ€�** å�³å�¯å…�费无套路获å�–。 - -![我的公众å�·](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20190805090108984.jpg) \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" new file mode 100644 index 0000000..dd72d1f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/@SpringBootApplication\346\263\250\350\247\243.md" @@ -0,0 +1,382 @@ +springboot ÔÚÆô¶¯ÀàÉÏ»á±ê×¢Ò»¸ö×¢½â£º`@SpringBootApplication`£¬±¾È˽«´ÓÔ´Âë½âÎö·ÖÎöÕâ ¸ö×¢½âµÄ×÷Óᣠ+ +`@SpringBootApplication` ´úÂëÈçÏ£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan(excludeFilters = { + @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) +public @interface SpringBootApplication { + + /** + * ×Ô¶¯×°ÅäÒªÅųýµÄÀ࣬¹¦ÄÜÀ´×ÔÓÚ @EnableAutoConfiguration + */ + @AliasFor(annotation = EnableAutoConfiguration.class) + Class[] exclude() default {}; + + /** + * ×Ô¶¯×°ÅäÒªÅųýµÄÀàÃû£¬¹¦ÄÜÀ´×ÔÓÚ @EnableAutoConfiguration + */ + @AliasFor(annotation = EnableAutoConfiguration.class) + String[] excludeName() default {}; + + /** + * ÅäÖÃɨÃèµÄ°ü£¬¹¦ÄÜÀ´×ÔÓÚ @ComponentScan + */ + @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") + String[] scanBasePackages() default {}; + + /** + * ÅäÖÃɨÃèµÄclass£¬¸ÃclassËùÔڵİü¶¼»á±»É¨Ã裬¹¦ÄÜÀ´×ÔÓÚ @ComponentScan + */ + @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") + Class[] scanBasePackageClasses() default {}; + + /** + * ÊÇ·ñÆôÓà @Bean ·½·¨´úÀí£¬¹¦ÄÜÀ´×ÔÓÚ @Configuration + */ + @AliasFor(annotation = Configuration.class) + boolean proxyBeanMethods() default true; + +} + +``` + +1. `@SpringBootApplication` ÊÇÒ»¸ö×éºÏ×¢½â£¬°üº¬ÁË `@SpringBootConfiguration`¡¢`@EnableAutoConfiguration`¡¢`@ComponentScan` Èý¸ö×¢½âµÄ¹¦ÄÜ£» +2. `@SpringBootApplication` ÖÐÒ²ÌṩÁËһЩÅäÖÃÊôÐÔ£¬¶øÕâЩÊôÐÔÀ´×ÔÓÚÒÔÉÏÈý¸ö×¢½â¡£ + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ÕâÈý¸ö×¢½âµÄ×÷Ó÷ֱðÊÇʲô¡£ + +### 1. `@SpringBootConfiguration` + +½øÈë `@SpringBootConfiguration`£¬´úÂëÈçÏ£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Configuration +public @interface SpringBootConfiguration { + + @AliasFor(annotation = Configuration.class) + boolean proxyBeanMethods() default true; + +} + +``` + +Õâ¸ö×¢½â±È½Ï¼òµ¥£¬ÉÏÃæ±ê¼ÇÁË `@Configuration`£¬È»ºóÊÇÒ»¸öÊôÐÔ `proxyBeanMethods()`£¬ËüÀ´×ÔÓÚ `@Configuration`¡£Òò´Ë£¬`@SpringBootConfiguration` ²¢Ã»ÓÐ×öʲô£¬½ö½öÖ»Êǽ« `@Configuration` ʹÓÃÁË `@Configuration` µÄ¹¦ÄÜ¡£ + +¹ØÓÚ `@Configuration`£¬ËüÀ´×ÔÓÚ spring£¬Äܱ» spring ʶ±ðΪ `Component`£¬ÇÒ `proxyBeanMethods != false` ʱ£¬»á±» spring ±ê¼ÇΪ `Full` ÅäÖÃÀ࣬ÔÚºóÐø¶ÔÆäÖÐµÄ `@Bean` ·½·¨´¦Àíʱ£¬»á½øÐÐ cglib ´úÀí£¬¹ØÓÚÕâ·½ÃæµÄÄÚÈÝ£¬¿É²Î¿¼ [ConfigurationClassPostProcessor£¨¶þ£©£º´¦Àí @Bean ×¢½â](https://my.oschina.net/funcy/blog/4492878). + +### 2. `@EnableAutoConfiguration` + +`@EnableAutoConfiguration` Ö÷Òª ÓÃÀ´¿ªÆô×Ô¶¯×°Å书ÄÜ£¬´úÂëÈçÏ£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +// ×Ô¶¯×°ÅäµÄ°ü +@AutoConfigurationPackage +// ÒýÈëµÄ×Ô¶¯×°ÅäÀà +@Import(AutoConfigurationImportSelector.class) +public @interface EnableAutoConfiguration { + + String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; + + /** + * ¿É×ÔÐж¨ÒåÅųý×Ô¶¯×°ÅäµÄÀà + */ + Class[] exclude() default {}; + + /** + * ¿É×ÔÐж¨ÒåÅųý×Ô¶¯×°ÅäµÄÀàÃû + */ + String[] excludeName() default {}; + +} + +``` + +´Ó´úÂëÖпÉÒÔ¿´µ½£¬ + +1. ¸Ã×¢½â×éºÏÁË `@AutoConfigurationPackage` ×¢½âµÄ¹¦ÄÜ£¬¸Ã×¢½âÓÃÀ´Ö¸¶¨×Ô¶¯×°ÅäµÄ°ü£» +2. ¸Ã×¢½âͨ¹ý `@Import` ×¢½âÒýÈëÁËÒ»¸öÀà `AutoConfigurationImportSelector`£¬Õâ¸öÀàÊÇ×Ô¶¯×°ÅäµÄ¹Ø¼ü£» +3. ¸Ã×¢½âÌṩÁËÁ½¸öÅäÖã¬ÓÃÀ´ÅųýÖ¸¶¨µÄ×Ô¶¯×°ÅäÀ࣬¿ÉÒÔ¸ù¾ÝÀàÀ´Åųý (`Class` ¶ÔÏó)£¬Ò²¿ÉÒÔ¸ù¾ÝÀàÃû (`°üÃû.ÀàÃû`) Åųý¡£ + +½ÓÏÂÀ´ÎÒÃÇÀ´¹Ø×¢ `@AutoConfigurationPackage` ¼°ÒýÈëµÄ `AutoConfigurationImportSelector`¡£ + +#### 2.1 `@AutoConfigurationPackage` + +`@AutoConfigurationPackage` Ö¸¶¨ÁË×Ô¶¯×°ÅäµÄ°ü£¬´úÂëÈçÏ£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(AutoConfigurationPackages.Registrar.class) +public @interface AutoConfigurationPackage { + +} + +``` + +Õâ¸ö×¢½âµÄÄÚÈݷdz£¼òµ¥£¬½öʹÓà `@Import` ×¢½âÒýÈëÁË `AutoConfigurationPackages.Registrar`£¬ÎÒÃÇÀ´¿´ÏÂËüµÄÄÚÈÝ£º + +``` +public abstract class AutoConfigurationPackages { + + private static final String BEAN = AutoConfigurationPackages.class.getName(); + + static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { + + /** + * ¸ù¾Ý ImportBeanDefinitionRegistrar µÄ´¦Àí£¬spring½«µ÷Óà registerBeanDefinitions() ×¢²áÄÚÈÝ + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, + BeanDefinitionRegistry registry) { + register(registry, new PackageImport(metadata).getPackageName()); + } + + @Override + public Set determineImports(AnnotationMetadata metadata) { + return Collections.singleton(new PackageImport(metadata)); + } + + } + + /** + * ´¦Àí¾ßÌåµÄ×¢²á²Ù×÷ + * 1\. Èç¹û beanFacotry Öаüº¬ BEAN£¬Ôò½«´«ÈëµÄ°üÃûÌí¼Óµ½ BEAN ¶ÔÓ¦µÄ BeanDefinition µÄ¹¹Ôì·½·¨²ÎÊýÖµÉÏ£» + * 2\. Èç¹û beanFacotry Öв»°üº¬ BEAN£¬Ôò´´½¨ beanDefinition£¬ÉèÖòÎÊýÖµ£¬È»ºó½«Æä×¢²áµ½ beanFacotry¡£ + * ×¢²áµ½beanFacotryÖеÄbeanΪBasePackages + */ + public static void register(BeanDefinitionRegistry registry, String... packageNames) { + if (registry.containsBeanDefinition(BEAN)) { + BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); + // bean ÊÇ BasePackages£¬¹¹Ôì·½·¨ÊÇ BasePackages(String... names)£¬ÕâÀï»ñȡԭ±¾µÄ¹¹Ôì²ÎÊýµÄÖµ + ConstructorArgumentValues constructorArguments + = beanDefinition.getConstructorArgumentValues(); + // ½«Ô­±¾µÄ¹¹Ôì²ÎÊýÖµ£¬ÒÔ¼°´«ÈëµÄ packageNames ͳһÌí¼Óµ½¹¹Ôì·½·¨µÄµÚ0¸ö²ÎÊýÖµÉÏ + constructorArguments.addIndexedArgumentValue(0, + addBasePackages(constructorArguments, packageNames)); + } + else { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + // ÉèÖÃBeanClassΪBasePackages.class + beanDefinition.setBeanClass(BasePackages.class); + // ÉèÖù¹Ôì·½·¨µÄ²ÎÊýÖµ + beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN, beanDefinition); + } + } + + /** + * packageName µÄ°ü×°Àà + * packageName ÊÇ´«ÈëÀàËùÔڵİüÃû£¬ÔÚPackageImportµÄ¹¹Ôì·½·¨ÖлñÈ¡ + */ + private static final class PackageImport { + + private final String packageName; + + PackageImport(AnnotationMetadata metadata) { + // »ñÈ¡´«ÈëÀàËùÔÚ°üÃû + this.packageName = ClassUtils.getPackageName(metadata.getClassName()); + } + + String getPackageName() { + return this.packageName; + } + + // Ê¡ÂÔ equals/toString/hashCode ·½·¨ + ... + + } + + /** + * ×¢½âµ½ beanFactory ÖеÄÀà + * ¸ÃÀàÖÐÓÐÒ»¸öList½á¹¹£¬ÓÃÀ´±£´æ°üɨÃè·¾¶ + */ + static final class BasePackages { + // °üɨÃè·¾¶ÔÚÕâÀï±£´æ + private final List packages; + + private boolean loggedBasePackageInfo; + + BasePackages(String... names) { + List packages = new ArrayList<>(); + for (String name : names) { + if (StringUtils.hasText(name)) { + packages.add(name); + } + } + this.packages = packages; + } + + // Ê¡ÂÔÁËһЩ´úÂë + ... + } + +} + +``` + +´úÂëÓе㳤£¬µ«Âß¼­²¢²»¸´ÔÓ£¬Á÷³ÌÈçÏ£º + +1. `AutoConfigurationPackages.Registrar` ʵÏÖÁË `ImportBeanDefinitionRegistrar`£¬`registerBeanDefinitions(...)` ·½·¨Ïò spring ÖÐ×¢²áÁË `BasePackages`£¬×¢²áÂß¼­ÔÚ `AutoConfigurationPackages#register` ·½·¨ÖУ» +2. `AutoConfigurationPackages#register` ·½·¨µÄ×¢²áÂß¼­Îª£¬ÏÈÅжÏÊÇ·ñÒÑ×¢²áÁË `BasePackages`£¬Èç¹û×¢²áÁË£¬¾Í½«µ±Ç°ÀàËùÔڵİüÌí¼Óµ½ `BasePackages` µÄ¹¹Ôì·½·¨²ÎÊýÖµÖУ¬·ñÔò¾Í´´½¨ `BeanDefinition`£¬ÉèÖù¹Ôì·½·¨µÄ²ÎÊýÖµ£¬È»ºó×¢²áµ½ spring ÖУ» + +#### 2.2 `AutoConfigurationImportSelector` + +`AutoConfigurationImportSelector` ÊÇ´¦Àí×Ô¶¯ÅäÖõĹؼü£¬´úÂëÈçÏ£º + +``` +public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, + + ... + +} + +``` + +`AutoConfigurationImportSelector` ʵÏÖÁË `DeferredImportSelector`£¬ÕâÊÇÒ»¸ö `ImportSelector` À࣬µ«´¦ÀíµÄÓÅÏȼ¶×îµÍ (ÔÚ `@ComponentScan`¡¢`@Component`¡¢`@Bean`¡¢`@Configuration` ¼°ÆäËû `@Import` ×¢½â´¦ÀíÍêÖ®ºóÔÙ´¦Àí)£¬ÔÚ `AutoConfigurationImportSelector` ÀàÖлᴦÀí×Ô¶¯ÅäÖÃÀàµÄ¼ÓÔØÁ÷³Ì£¬ÕýÊÇͨ¹ýÕâÖÖ·½Ê½£¬½«×Ô¶¯ÅäÖÃÀàÒýÈëÁË spring ÈÝÆ÷ÖС£ + +¹ØÓÚ spring ¶Ô `@Import` µÄ´¦Àí£¬¿ÉÒԲο¼ [ConfigurationClassPostProcessor Ö®´¦Àí @Import ×¢½â](https://my.oschina.net/funcy/blog/4678152). + +¹ØÓÚ `AutoConfigurationImportSelector` »ñÈ¡×Ô¶¯ÅäÖÃÀàµÄÁ÷³Ì£¬½«ÔÚºóÃæµÄÎÄÕÂÖоßÌå·ÖÎö£¬±¾ÎľͲ»Õ¹¿ªÁË¡£ + +### 3. `@ComponentScan` + +Õâ¸ö×¢½âÏë±Ø´ó¼ÒÒѾ­ºÜÊìϤÁË£¬ËüÖ¸¶¨Á˰üɨÃè·¾¶£¬Èç¹û²»Ö¸¶¨£¬¾ÍɨÃèËùÔÚÀàµÄ°ü£¬¹ØÓÚÕâЩ£¬ÔÚ [ConfigurationClassPostProcessor Ö®´¦Àí @ComponentScan ×¢½â](https://my.oschina.net/funcy/blog/4836178)Ò»ÎÄÖÐÒѾ­Ïêϸ·ÖÎö¹ýÁË£¬¾Í²»ÔÙ·ÖÎöÁË¡£ + +±¾ÎÄÎÒÃÇÀ´·ÖÎöÕâ¸ö×¢½âÊôÐÔÖÐʹÓÃµÄ 2 ¸öÀࣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-19d14d3d8262eead434d5ca09369e1789d5.png) + +#### 3.1 `TypeExcludeFilter` + +Õâ¸öÀà±íʾÔÚ½øÐаüɨÃèʱ£¬¿ÉÒÔÅųýһЩÀ࣬´úÂëÈçÏ£º + +``` +public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware { + + private BeanFactory beanFactory; + + private Collection delegates; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public boolean match(MetadataReader metadataReader, + MetadataReaderFactory metadataReaderFactory) throws IOException { + if (this.beanFactory instanceof ListableBeanFactory + && getClass() == TypeExcludeFilter.class) { + // getDelegates() »ñÈ¡µ±Ç°ÈÝÆ÷ÖÐËùÓÐµÄ TypeExcludeFilter ʵÀý + // ¿ÉÒÔ×ÔÖ÷¼Ì³Ð TypeExcludeFilter£¬×Ô¶¨Ò寥Å乿Ôò + for (TypeExcludeFilter delegate : getDelegates()) { + if (delegate.match(metadataReader, metadataReaderFactory)) { + return true; + } + } + } + return false; + } + + private Collection getDelegates() { + Collection delegates = this.delegates; + if (delegates == null) { + delegates = ((ListableBeanFactory) this.beanFactory) + .getBeansOfType(TypeExcludeFilter.class).values(); + this.delegates = delegates; + } + return delegates; + } + + .... + +``` + +´Ó´úÂëÉÏÀ´¿´£¬Èç¹ûÒªÅųýһЩ À࣬ÎÒÃÇ¿ÉÒÔ×ÔÖ÷¼Ì³Ð `TypeExcludeFilter` À࣬ȻºóÖØÐ´ `match(...)` ·½·¨£¬ÔÚÆäÖж¨Ò寥ÅäÂß¼­¡£ + +#### 3.1 `AutoConfigurationExcludeFilter` + +`AutoConfigurationExcludeFilter` ÓÃÀ´Åųý×Ô¶¯ÅäÖÃÀ࣬Ҳ¾ÍÊÇ˵£¬spring ÔÚ½øÐаüɨÃèʱ£¬²»»áɨÃè×Ô¶¯ÅäÖÃÀ࣬´úÂëÈçÏ£º + +``` +public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware { + + private ClassLoader beanClassLoader; + + private volatile List autoConfigurations; + + @Override + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } + + @Override + public boolean match(MetadataReader metadataReader, + MetadataReaderFactory metadataReaderFactory) throws IOException { + // isConfiguration(...)£ºµ±Ç°ÀàÊÇ·ñ±» @Configuration ±ê¼Ç + // isAutoConfiguration(...)£ºµ±Ç°ÀàÊÇ·ñΪ×Ô¶¯ÅäÖÃÀà + return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader); + } + + private boolean isConfiguration(MetadataReader metadataReader) { + return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName()); + } + + private boolean isAutoConfiguration(MetadataReader metadataReader) { + // »ñÈ¡ËùÓеÄ×Ô¶¯ÅäÖÃÀ࣬ȻºóÅжϵ±Ç°ÀàÊÇ·ñ´æÔÚÓÚÆäÖÐ + return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName()); + } + + protected List getAutoConfigurations() { + if (this.autoConfigurations == null) { + this.autoConfigurations = SpringFactoriesLoader + .loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader); + } + return this.autoConfigurations; + } + +} + +``` + +ÎÒÃÇÖ÷Òª¿´ `match(...)` ·½·¨£¬ËüµÄÆ¥ÅäµÄÀàΪ£º + +1. ±» `@Configuration` ±ê¼Ç£» +2. ÊÇ×Ô¶¯ÅäÖÃÀà¡£ + +Âú×ãÒÔÉÏÁ½¸öÌõ¼þ£¬spring ¾Í²»»á¶ÔÆä½øÐÐɨÃè´¦Àí¡£ + +ÄÇʲôÊÇ×Ô¶¯ÅäÖÃÀàÄØ£¿´Ó `isAutoConfiguration(...)` ¿ÉÒÔ¿´µ½£¬ÔÚÅжÏÊÇ·ñΪ×Ô¶¯ÅäÖÃÀàÉÏ£¬springboot ÏÈʹÓà `SpringFactoriesLoader` ¼ÓÔØËùÓÐÅäÖÃÀ࣬ȻºóÔÙÅжϴ«ÈëµÄÀàÊÇ·ñΪÆäÖÐÖ®Ò»¡£´ÓÕâÀï¿ÉÒÔ¿´³ö£¬×Ô¶¯ÅäÖÃÀಢ²»½øÐаüɨÃè²Ù×÷¡£ + +¹ØÓÚ `SpringFactoriesLoader` ÈçºÎ¼ÓÔØÅäÖÃÀ࣬ºóÃæµÄÎÄÕ»áÏêϸ·ÖÎö¡£ + +### 4\. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎö `@SpringBootApplication` µÄ¹¦ÄÜ£¬×ܽáÈçÏ£º + +1. `@SpringBootApplication` ÊÇÒ»¸ö×éºÏ×¢½â£¬°üº¬ÁË `@SpringBootConfiguration`¡¢`@EnableAutoConfiguration`¡¢`@ComponentScan` Èý¸ö×¢½âµÄ¹¦ÄÜ£¬Í¬Ê±ÌṩÁËһЩÊôÐÔÅäÖã¬Ò²ÊÇÀ´×ÔÓÚÒÔÉÏ 3 ¸ö×¢½â£» +2. `@SpringBootConfiguration` °üº¬ÁË `Configuration` ×¢½âµÄ¹¦ÄÜ£» +3. `@EnableAutoConfiguration` ÊÇ¿ªÆô×Ô¶¯×°ÅäµÄ¹Ø¼ü×¢½â£¬ÆäÖбê¼ÇÁË `@AutoConfigurationPackage`£¬»á½«±» `@SpringBootApplication` ±ê¼ÇµÄÀàËùÔڵİü£¬°ü×°³É `BasePackages`£¬È»ºó×¢²áµ½ spring ÈÝÆ÷ÖУ»`@EnableAutoConfiguration` »¹Í¨¹ý `@Import` ×¢½âÏòÈÝÆ÷ÖÐÒýÈëÁË `AutoConfigurationImportSelector`£¬¸ÃÀà»á½«µ±Ç°ÏîĿ֧³ÖµÄ×Ô¶¯ÅäÖÃÀàÌí¼Óµ½ spring ÈÝÆ÷ÖУ» +4. `@ComponentScan` ¶¨ÒåÁ˰üɨÃè·¾¶£¬Æä `excludeFilters` Öµ¿ÉÒÔÓÃÀ´ÅųýÀàµÄɨÃ裬springboot Ö¸¶¨ÁË `TypeExcludeFilter`£¬±íÃ÷ÎÒÃÇ¿ÉÒԼ̳иÃÀàÀ´×ÔÖ÷¶¨ÒåÅųýµÄÀà £»Í¬Ê±Ò²Ö¸¶¨ÁË `AutoConfigurationExcludeFilter` £¬¸Ã `Filter` ¿ÉÒÔÓÃÀ´Åųý×Ô¶¯ÅäÖÃÀ࣬Ҳ¾ÍÊÇ˵£¬×Ô¶¯ÅäÖÃÀ಻»á½øÐаüÃèÊö²Ù×÷¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4870882](https://my.oschina.net/funcy/blog/4870882) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" new file mode 100644 index 0000000..052d637 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\270\200\357\274\211\357\274\232servlet\347\273\204\344\273\266\347\232\204\346\263\250\345\206\214\346\265\201\347\250\213.md" @@ -0,0 +1,655 @@ +ÔÚ springboot ÖУ¬Èç¹ûÎÒÃÇÐèҪע²á servlet Èý´ó×é¼þ£º`servlet`¡¢`filter`¡¢`listener`£¬¸ÃÔõô×öÄØ£¬springboot ÌùÐĵØÎªÎÒÃÇÌṩÁË 3 ÖÖ·½·¨£¬±¾ÎľÍÀ´·ÖÎöÕâ 3 ÖÖ·½·¨µÄÔ´ÂëʵÏÖ¡£ + +### 1\. ×¢²á·½Ê½ + +#### 1.1 ʹÓà `XxxRegistrationBean` ×¢²á + +springboot ÌṩÁËÈý¸öÀàÐ굀 `RegistrationBean` À´´¦Àí servlet Èý´ó×é¼þµÄ×¢²á£¬·Ö±ðÊÇ `ServletRegistrationBean`¡¢`FilterRegistrationBean`¡¢`ServletListenerRegistrationBean`£¬ÕâÀïÎÒÃǼòµ¥Ê¾ÒâÏÂËüÃǵÄÓ÷¨£º + +``` +/** + * ×¼±¸ÁËÒ»¸öservlet + */ +public class MyServlet extends HttpServlet { + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + // ´¦ÀíһЩ²Ù×÷ + ... + } +} + +/** + * ½øÐÐ×¢²á²Ù×÷ + */ +@Bean +public ServletRegistrationBean registerServlet() { + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean( + new MyServlet(), "/myServlet"); + // ´¦ÀíһЩÅäÖòÙ×÷ + servletRegistrationBean.setXxx(); + ... + return servletRegistrationBean; +} + +``` + +ÒÔÉÏÌṩÁË `servlet` µÄ×¢²á·½Ê½£¬Òª×¢²á `filter`¡¢`listener`£¬Ö»ÐèʹÓöÔÓ¦µÄ `RegistrationBean` ¼´¿É£¬ÕâÀï¾Í²»Õ¹Ê¾ÁË¡£ + +#### 1.2 ʹÓà servlet ×¢½â×¢²á + +ÔÚ `Servlet 3.0`£¬servlet ÈÝÆ÷ÌṩÁË 3 ¸ö×¢½âÀ´´¦Àí `servlet` Èý´ó×é¼þµÄ×¢²á£º + +* `@WebServlet`: ´¦Àí `servlet` ×¢²á +* `@WebFilter`: ´¦Àí `filter` ×¢²á +* `@WebListener`: ´¦Àí `listener` ×¢²á + +»¹ÊÇÒÔ `servlet` ×¢²áΪÀý£¬ÏÈÀ´¿´¿´ `@WebServlet`: + +``` +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface WebServlet { + String name() default ""; + + String[] value() default {}; + + String[] urlPatterns() default {}; + + int loadOnStartup() default -1; + + WebInitParam[] initParams() default {}; + + boolean asyncSupported() default false; + + String smallIcon() default ""; + + String largeIcon() default ""; + + String description() default ""; + + String displayName() default ""; +} + +``` + +¿ÉÒÔ¿´µ½£¬`@WebServlet` Ö§³Ö¶à¸öÊôÐÔÅäÖã¬ÏñÖ¸¶¨ servlet µÄÃû³Æ¡¢Ó³ÉäµÄ url ¶¼¿ÉÒÔÔÚÕâÀïÖ¸¶¨£¬ÎÒÃÇÒ²Ìṩһ¸öʾÀý£º + +``` +@WebServlet(name = "myServlet", urlPatterns = "/myServlet") +public class JavaServlet extends HttpServlet { + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + // ´¦ÀíһЩ²Ù×÷ + ... + } + +} + +``` + +ÕâÑù´¦Àíºó£¬»¹Òª×öÒ»¸öÖØÒªµÄ²Ù×÷£¬ÄǾÍÊÇʹÓà `@ServletComponentScan` À´¿ªÆôɨÃ蹦ÄÜ£º + +``` +// ʹÓà @ServletComponentScan À´¿ªÆô servlet ×é¼þµÄɨÃ蹦ÄÜ +@ServletComponentScan +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + ... + } +} + +``` + +#### 1.3 `ServletContextInitializer` ×¢²á + +ʹÓÃÕâÖÖ·½Ê½×¢²á£¬ÐèҪʵÏÖ `ServletContextInitializer` ½Ó¿Ú£º + +``` +/** + * ×¼±¸Ò»¸öservlet + */ +public class MyServlet extends HttpServlet { + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + // ´¦ÀíһЩ²Ù×÷ + ... + } +} + +/** + * ʵÏÖ ServletContextInitializer + */ +@Component +public class ServletConfig implements ServletContextInitializer { + + @Override + public void onStartup(ServletContext servletContext) { + // ʹÓà servletContext ½øÐÐ×¢²á + ServletRegistration initServlet = servletContext.addServlet("myServlet", MyServlet.class); + // ¿ÉÒÔ½øÐÐһЩÅäÖà + initServlet.addMapping("/myServlet"); + } + +} + +``` + +ʹÓÃÕâÖÖ·½Ê½×¢²á£¬ÏÈҪʵÏÖ `ServletContextInitializer`£¬È»ºóÖØÐ´ `ServletContextInitializer#onStartup` ·½·¨£¬ÔÚ `ServletContextInitializer#onStartup` ʹÓà `ServletContext` ¶ÔÏó½øÐÐ×¢²á¡£`ServletContext` ¶ÔÏóÓÉ servlet ÈÝÆ÷Ìṩ£¬Õâ¸ö¶ÔÏó¾ÍÊÇ×¢²áµÄÖÕ¼«À࣬²»¹ÜÊÇʹÓà `RegistrationBean` ×¢²á£¬»¹ÊÇʹÓà `@ServletComponentScan` ɨÃè×¢²á£¬×îÖÕ¶¼ÊÇͨ¹ý `ServletContext` ×¢²áµ½ servlet ÈÝÆ÷ÖС£ + +### 2\. Ô´ÂëʵÏÖ + +Á˽âÍêÈçºÎʹÓú󣬽ÓÏÂÀ´ÎÒÃÇ¾Í ½øÈëÔ´Âë¿´¿´ÕâЩÁ÷³ÌÊÇÈçºÎʵÏÖ¡£ + +#### 2.1 `@ServletComponentScan` ɨÃè + +ÎÒÃÇÖ±½Ó½øÈë `@ServletComponentScan`£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(ServletComponentScanRegistrar.class) +public @interface ServletComponentScan { + ... +} + +``` + +Õâ¸ö×¢²áÉÏÃæ±ê¼ÇÁË `@Import` ×¢½â£¬ÒýÈëÁËÒ»¸öÀࣺ`ServletComponentScanRegistrar`£¬ÎÒÃÇ¿´¿´Õâ¸öÀ྿¾¹×öÁËɶ£º + +``` +/** + * ʵÏÖÁËImportBeanDefinitionRegistrar + * ÏòÈÝÆ÷ÖÐ×¢²áÁË ServletComponentRegisteringPostProcessor + */ +class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar { + + private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor"; + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + Set packagesToScan = getPackagesToScan(importingClassMetadata); + if (registry.containsBeanDefinition(BEAN_NAME)) { + updatePostProcessor(registry, packagesToScan); + } + else { + // ×¢²á BeanFactoryPostProcessor + addPostProcessor(registry, packagesToScan); + } + } + + /** + * ×¢²á BeanFactoryPostProcessor + * ×¢²áÁË ServletComponentRegisteringPostProcessor + */ + private void addPostProcessor(BeanDefinitionRegistry registry, Set packagesToScan) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + // ServletComponentRegisteringPostProcessor: ´¦ÀíɨÃèµÄ BeanFactoryPostProcessor + beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class); + beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + // ServletComponentScanRegistrar ¾ÍÊÇΪÁË×¢²á ServletComponentRegisteringPostProcessor + registry.registerBeanDefinition(BEAN_NAME, beanDefinition); + } + + ... + +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸öÀàʵÏÖÁË `ImportBeanDefinitionRegistrar`£¬Ö÷ÒªÊÇÏò spring ÈÝÆ÷ÖÐ×¢²áÁË `ServletComponentRegisteringPostProcessor`¡£ÎÒÃǼÌÐø¿´ÏÂÈ¥£¬½øÈë `ServletComponentRegisteringPostProcessor`£º + +``` +class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, + ApplicationContextAware { + + /** + * ÐèҪɨÃèµÄ°ü. + */ + private final Set packagesToScan; + + /** + * ҪɨÃèµÄ°üÓɹ¹Ôì·½·¨´«Èë + */ + ServletComponentRegisteringPostProcessor(Set packagesToScan) { + this.packagesToScan = packagesToScan; + } + + /** + * ÖØÐ´ÁËBeanFactoryPostProcessorµÄ·½·¨ + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { + // ÅжÏÊÇ·ñÔËÐÐÔÚÄÚǶµÄ web ÈÝÆ÷ÖÐ + if (isRunningInEmbeddedWebServer()) { + // ɨÃèÆ÷£¬ÅäÖÃÁËɨÃè¹æÔò + ClassPathScanningCandidateComponentProvider componentProvider + = createComponentProvider(); + for (String packageToScan : this.packagesToScan) { + // ½øÐаüɨÃè²Ù×÷ + scanPackage(componentProvider, packageToScan); + } + } + } + + ... + +} + +``` + +¿ÉÒÔ¿´µ½£¬`ServletComponentRegisteringPostProcessor` ʵÏÖÁË `BeanFactoryPostProcessor`£¬ÔÚÖØÐ´µÄ `BeanFactoryPostProcessor#postProcessBeanFactory` ·½·¨Öд¦ÀíɨÃè²Ù×÷£¬É¨Ãèǰ£¬ÏÈÊÇ´´½¨ÁËɨÃèÆ÷ `ClassPathScanningCandidateComponentProvider`£¬È»ºóÔÙ½øÐÐɨÃè¡£ + +ÎÒÃÇÏÈÀ´¿´É¨ÃèÆ÷µÄ´´½¨·½·¨ `createComponentProvider()`£º + +``` +// ´¦Àí¸÷ÖÖ handler +private static final List HANDLERS; + +static { + List servletComponentHandlers = new ArrayList<>(); + servletComponentHandlers.add(new WebServletHandler()); + servletComponentHandlers.add(new WebFilterHandler()); + servletComponentHandlers.add(new WebListenerHandler()); + HANDLERS = Collections.unmodifiableList(servletComponentHandlers); +} + +/** + * ´´½¨É¨ÃèÆ÷ + */ +private ClassPathScanningCandidateComponentProvider createComponentProvider() { + // ´´½¨¶ÔÏó + ClassPathScanningCandidateComponentProvider componentProvider + = new ClassPathScanningCandidateComponentProvider(false); + componentProvider.setEnvironment(this.applicationContext.getEnvironment()); + componentProvider.setResourceLoader(this.applicationContext); + for (ServletComponentHandler handler : HANDLERS) { + // ÅäÖùýÂ˹æÔò + componentProvider.addIncludeFilter(handler.getTypeFilter()); + } + return componentProvider; +} + +``` + +`createComponentProvider()` ·½·¨ÖУ¬ÏÈÊÇ´´½¨ÁËɨÃèÆ÷¶ÔÏó£¬È»ºóÉèÖÃÁËһЩÊôÐÔ£¬½ÓמÍÊÇÅäÖùýÂ˹æÔò£¬ÎÒÃÇÕâÀïÖØµãÀ´¿´Ï¹ýÂ˹æÔòµÄÅäÖã¬ÕâЩ¹æÔòÓÉ `WebServletHandler`/`WebFilterHandler`/`WebListenerHandler` µÄ `getTypeFilter()` ·½·¨Ìṩ£º + +`getTypeFilter()` ·½·¨Î»ÓÚÒ»¸ö³éÏó·½·¨ÖУº + +``` +abstract class ServletComponentHandler { + + private final TypeFilter typeFilter; + + /** + * ´«Èë×¢½â£¬×ª»»Îª AnnotationTypeFilter ¶ÔÏó + */ + protected ServletComponentHandler(Class annotationType) { + this.typeFilter = new AnnotationTypeFilter(annotationType); + ... + } + + /** + * ·µ»Ø TypeFilter + */ + TypeFilter getTypeFilter() { + return this.typeFilter; + } + ... +} + +``` + +ÔÚ `ServletComponentHandler` ÖУ¬ÓÐÒ»¸ö³ÉÔ±±äÁ¿ `typeFilter`£¬ÔÚ¹¹Ôì·½·¨Öд«Èë×¢²áÖµºó»áת»»»á `AnnotationTypeFilter`£¬È»ºó¸³Öµ¸ø `typeFilter`£¬¶ø `getTypeFilter()` ·½·¨·µ»ØµÄ¾ÍÊÇÕâ¸ö `typeFilter`¡£ + +Á˽âÍêÕâ¸ö `typeFilter` µÄÀ´Ô´ºó£¬ÎÒÃÇÀ´¿´¿´ËüµÄ¼¸¸öʵÏÖÀࣺ + +``` +/** + * WebFilterHandler ¹¹Ôì·½·¨´«ÈëµÄ²ÎÊýÊÇ WebFilter + */ +class WebFilterHandler extends ServletComponentHandler { + WebFilterHandler() { + super(WebFilter.class); + } + ... +} + +/** + * WebListenerHandler ¹¹Ôì·½·¨´«ÈëµÄ²ÎÊýÊÇ WebListener + */ +class WebListenerHandler extends ServletComponentHandler { + WebListenerHandler() { + super(WebListener.class); + } + ... +} + +/** + * WebServletHandler ¹¹Ôì·½·¨´«ÈëµÄ²ÎÊýÊÇ WebServlet + */ +class WebServletHandler extends ServletComponentHandler { + WebServletHandler() { + super(WebServlet.class); + } + ... +} + +``` + +Óɴ˾ÍÃ÷°×ÁË£¬`createComponentProvider()` µÃµ½µÄ `ClassPathScanningCandidateComponentProvider` Ö»´¦Àí°üº¬ 3 ¸ö×¢½âµÄÀࣺ + +* `@WebFilter` +* `@WebListener` +* `@WebServlet` + +ÎÒÃǼÌÐø£¬½ÓÏÂÀ´¿´¿´É¨ÃèÁ÷³Ì£¬·½·¨Îª `ServletComponentRegisteringPostProcessor#scanPackage`: + +``` +private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, + String packageToScan) { + for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) { + if (candidate instanceof AnnotatedBeanDefinition) { + // ´¦ÀíµÃµ½µÄ BeanDefinition + for (ServletComponentHandler handler : HANDLERS) { + handler.handle(((AnnotatedBeanDefinition) candidate), + (BeanDefinitionRegistry) this.applicationContext); + } + } + } +} + +``` + +¹ØÓÚ¾ßÌåµÄɨÃèÁ÷³Ì£¨`ClassPathScanningCandidateComponentProvider#findCandidateComponents` ·½·¨£©£¬Í¬ spring µÄ°üɨÃèÁ÷³Ì»ù±¾Ò»Ö£¬ÕâÀï¾Í²»Õ¹¿ªÏ¸½²ÁË£¬ÎÒÃǰÑÖØµã·ÅÔÚ `BeanDefinition` µÄ´¦ÀíÉÏ£¬Ò²¾ÍÊÇ `ServletComponentHandler#handle` ·½·¨£º + +``` +void handle(AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) { + // ÕâÀïµÄ annotationType ¾ÍÊǹ¹Ôì·½·¨Öд«ÈëµÄ×¢½â£¬Èç@WebFilter£¬@WebListener µÈ + Map attributes = beanDefinition.getMetadata() + .getAnnotationAttributes(this.annotationType.getName()); + // Åж϶ÔÓ¦µÄ×¢½âÊÇ·ñ´æÔÚ£¬´æÔÚÔò´¦Àí + if (attributes != null) { + doHandle(attributes, beanDefinition, registry); + } +} + +``` + +ÔÚ´¦ÀíɨÃèµÃµ½µÄ `BeanDefinition` ʱ£¬ÏȱéÀúËùÓÐµÄ `handler`(`WebServletHandler`/`WebFilterHandler`/`WebListenerHandler`)£¬È»ºóµ÷Óà `ServletComponentHandler#handle` ·½·¨½øÐд¦Àí¡£ÔÚ `ServletComponentHandler#handle` ÖУ¬ÓÖ»á¸ù¾ÝÊÇ·ñ´æÔÚ¶ÔÓ¦µÄ×¢½âÊÇ·ñ´æÔÚ£¨Ê¹Óà `AnnotatedBeanDefinition#getMetadata` »ñÈ¡¶ÔӦע²áµÄÐÅÏ¢£©À´¾ö¶¨ÊÇ·ñµ÷ÓÃ×ÓÀàµÄ `doHandler()` ·½·¨¡£ + +ÄÇôÕâ¸ö `doHandler()` ·½·¨¸ÉÁËÊ²Ã´ÄØ£¿ÎÒÃǽøÈë `WebServletHandler#doHandle`£º + +``` +public void doHandle(Map attributes, AnnotatedBeanDefinition beanDefinition, + BeanDefinitionRegistry registry) { + // ×¢²áµÄÊÇ ServletRegistrationBean ¶ÔÓ¦µÄ BeanDefinition + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .rootBeanDefinition(ServletRegistrationBean.class); + builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported")); + builder.addPropertyValue("initParameters", extractInitParameters(attributes)); + builder.addPropertyValue("loadOnStartup", attributes.get("loadOnStartup")); + // »ñÈ¡ servlet Ãû³Æ£¬Èç¹ûÖ¸¶¨ÁËÃû³Æ£¬¾ÍʹÓÃÖ¸¶¨Ãû³Æ£¬Èç¹ûûÓÐÖ¸¶¨£¬¾ÍʹÓÃÊÇbeanµÄÃû³Æ + String name = determineName(attributes, beanDefinition); + builder.addPropertyValue("name", name); + builder.addPropertyValue("servlet", beanDefinition); + builder.addPropertyValue("urlMappings", extractUrlPatterns(attributes)); + builder.addPropertyValue("multipartConfig", determineMultipartConfig(beanDefinition)); + registry.registerBeanDefinition(name, builder.getBeanDefinition()); +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨Ö÷ÒªÊÇ´¦Àí `Servlet` µÄÅäÖã¬×îÖÕÏò spring ÈÝÆ÷ÖÐ×¢²áµÄÊÇ `ServletRegistrationBean` ¶ÔÓ¦µÄ `beanDefinition`¡£ + +ÆäËûÁ½¸ö `Handler` ÀàµÄ `doHandle()` ·½·¨´¦ÀíÁ÷³ÌÒ²²î²»¶à£¬²»¹ý×îÖÕÏò spring ÈÝÆ÷ÖÐ×¢²áµÄ `beanDefinition` ÓÐËù²»Í¬£¬ÕâÀï¾Í²»Ï¸¶ÁÁË¡£ + +ÕâÀï×ܽáÏÂÕ⼸¸ö×¢½â×îÖÕÏò spring ÈÝÆ÷ÖÐ×¢²áµÄ `beanDefinition`£º + +* `@WebServlet`: ×¢²áÁË `ServletRegistrationBean` ¶ÔÓ¦µÄ `beanDefinition` +* `@WebFilter`: ×¢²áÁË `FilterRegistrationBean` ¶ÔÓ¦µÄ `beanDefinition` +* `@WebListener`: ×¢²áÁË `ServletListenerRegistrationBean` ¶ÔÓ¦µÄ `beanDefinition` + +ÔÚʹÓà `XxxRegistrationBean` ×¢²áʱ£¬ÎÒÃÇÊÇÊÖ¶¯´´½¨ÁË `XxxRegistrationBean`£¬È»ºóͨ¹ý `@Bean` ×¢½â×¢²áµ½ spring ÈÝÆ÷ÖУ¬¶øÊ¹Óà `@WebServlet`/`@WebFilter`/`@WebListener` ÄÖÁËһȦ£¬×îÖÕÒ²Êǻص½ÁË `XxxRegistrationBean`£¡ + +#### 2.2 `XxxRegistrationBean` µÄ×¢²á + +ÎÞÂÛÊÇʹÓà `XxxRegistrationBean` ×¢²á£¬»¹ÊÇʹÓà `@ServletComponentScan` ɨÃè×¢²á£¬×îÖÕ¶¼»áµÃµ½ `XxxRegistrationBean` ¶ÔÓ¦µÄ bean£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´Ì½¾¿ÏÂÕâЩ bean ÊÇÈçºÎ×¢²áµ½ servlet ÈÝÆ÷Öеġ£ + +´Ó´úÂëÉÏ¿´£¬`ServletRegistrationBean`¡¢`FilterRegistrationBean` Óë `ServletListenerRegistrationBean` ¶¼ÊÇ `ServletContextInitializer` ½Ó¿ÚµÄʵÏÖÀ࣬`ServletRegistrationBean` µÄ¼Ì³Ð½á¹¹ÈçÏ£º + +![ͼƬÀ´×ÔÍøÂç](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0397d56cfb328683ae349d0822db2320231.png) + +`ServletContextInitializer` ÖÐÖ»ÓÐÒ»¸ö·½·¨ `onStartup(...)`£º + +``` +@FunctionalInterface +public interface ServletContextInitializer { + + /** + * ¾ÍÊÇÕâ¸ö servletContext£¬ÓÐÁËËü£¬¾ÍÊǽøÐÐ Servlet£¬filter£¬listener µÄ×¢²áÁË + */ + void onStartup(ServletContext servletContext) throws ServletException; + +} + +``` + +ÎÒÃÇÔÚ½éÉÜ `servlet` Èý´ó×é¼þµÄ×¢²á·½Ê½Ê±£¬ÎÒÃÇÌáµ½¿ÉÒÔͨ¹ýʵÏÖ `ServletContextInitializer`£¬ÖØÐ´Æä `onStartup()` À´ÊµÏÖ `servlet` ×é¼þµÄ×¢²á£¬¶ø `XxxRegistrationBean` µÄµ×²ãʵÏÖÒ²ÊÇÕâô×öµÄ¡£ + +ÒÔ `ServletRegistrationBean`£¬ÎÒÃÇÀ´¿´¿´ËüµÄ `onStartup(...)` ·½·¨×öÁËɶ£º + +`ServletRegistrationBean` ûÓÐÖØÐ´ `onStartup(...)` ·½·¨£¬Ö±½Ó¼Ì³Ð×Ô `RegistrationBean`: + +``` +public final void onStartup(ServletContext servletContext) throws ServletException { + // »ñÈ¡ÃèÊöÐÅÏ¢ + String description = getDescription(); + // ÊÇ·ñ¿ªÆô×¢²á£¬Ä¬ÈÏΪtrue + if (!isEnabled()) { + logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); + return; + } + // ½øÐÐ×¢²á²Ù×÷ + register(description, servletContext); +} + +``` + +Õâ¸ö·½·¨²¢²»¸´ÔÓ£¬ÏÈ»ñÈ¡ÁËÒ»ÏÂÃèÊöÐÅÏ¢£¬È»ºóÅжÏÊÇ·ñ¿ªÆôÁË×¢²á£¬½ÓמÍÊǽøÐÐ×¢²á²Ù×÷ÁË¡£ÎÒÃÇÖ±½Ó²é¿´×¢²á²Ù×÷£¬½øÈë `DynamicRegistrationBean#register`: + +``` +protected final void register(String description, ServletContext servletContext) { + D registration = addRegistration(description, servletContext); + if (registration == null) { + logger.info(...); + return; + } + // ´¦ÀíÅäÖà + configure(registration); +} + +``` + +Õâ¸ö·½·¨Ö÷Òª×öÁËÁ½¸ö¼þÊ£º×¢²á `servlet` Óë´¦ÀíÅäÖã¬ÎÒÃÇÏÈÀ´¿´¿´×¢²á²Ù×÷£¬½øÈë `ServletRegistrationBean#addRegistration` ·½·¨: + +``` +protected ServletRegistration.Dynamic addRegistration(String description, + ServletContext servletContext) { + String name = getServletName(); + // ×¢²á + return servletContext.addServlet(name, this.servlet); +} + +``` + +×¢²á²Ù×÷»¹ÊDZȽϼòµ¥µÄ£¬Ö±½Óµ÷Óà `ServletContext#addServlet` ½øÐС£ + +¼ÌÐø²é¿´ÅäÖô¦Àí£¬½øÈë `ServletRegistrationBean#configure` ·½·¨£º + +``` +protected void configure(ServletRegistration.Dynamic registration) { + // µ÷Óø¸Àà + super.configure(registration); + // ÅäÖÃurlMapping + String[] urlMapping = StringUtils.toStringArray(this.urlMappings); + if (urlMapping.length == 0 && this.alwaysMapUrl) { + urlMapping = DEFAULT_MAPPINGS; + } + if (!ObjectUtils.isEmpty(urlMapping)) { + registration.addMapping(urlMapping); + } + // ÅäÖÃloadOnStartup + registration.setLoadOnStartup(this.loadOnStartup); + // »¹´¦ÀíÁËһЩÆäËûÅäÖà + if (this.multipartConfig != null) { + registration.setMultipartConfig(this.multipartConfig); + } +} + +``` + +Õâ¸ö·½·¨ÏÈÊǵ÷ÓÃÁ˸¸ÀàµÄ·½·¨£¬È»ºó¾ÍÊÇÅäÖô¦ÀíÁË£¬ÔÚÕâ¸ö·½·¨ÀïÖ÷ÒªÊÇ´¦ÀíÁË `urlMapping` Óë `loadOnStartup`£¬¾Í²»¶à×ö·ÖÎöÁË¡£ + +ÎÒÃÇÔÙÀ´¿´¿´ `super.configure(...)` ÅäÖÃÁËɶ£¬½øÈë `DynamicRegistrationBean#configure`: + +``` +/** + * Ò²ÊÇ´¦ÀíһЩÅäÖà + */ +protected void configure(D registration) { + registration.setAsyncSupported(this.asyncSupported); + // ÅäÖóõʼ»¯²ÎÊý + if (!this.initParameters.isEmpty()) { + registration.setInitParameters(this.initParameters); + } +} + +``` + +Õâ¸ö·½·¨Ö÷Òª´¦ÀíÁ˳õʼ²ÎÊýµÄÅäÖᣠ+ +´ÓÉÏÃæµÄ·ÖÎöÀ´¿´£¬`ServletRegistrationBean` µÄ `onStartup(...)` ·½·¨Ö÷Òª´¦ÀíÁËÁ½¸ö²Ù×÷£º + +1. ½« `servlet` Ìí¼Óµ½ÈÝÆ÷ÖÐ +2. ´¦Àí `servlet` ²ÎÊýÅäÖà + +`FilterRegistrationBean` Óë `ServletListenerRegistrationBean` µÄ×¢²á²Ù×÷ÀàËÆ£¬ÕâÀï¾Í²»¶à˵ÁË¡£ + +#### 2.3 `ServletContextInitializer#onStartup` µÄÖ´ÐÐ + +×¢²áÁ÷³ÌÒѾ­ÞÛÍêÁË£¬ÎÒÃÇÔÙÀ´¿´¿´ `ServletContextInitializer#onStartup` ÊÇÔÚÄÄÀïÖ´Ðеġ£×¢£ºÕâÒ»²½µÄÁ÷³Ì±È½Ï¸´ÔÓ£¬»áÉæ¼°µ½ tomcat µÄÆô¶¯Á÷³Ì£¬Òò´ËÕⲿ·ÖÖ»¹Ø×¢Öصã´úÂ룬²»¾ßÌå·ÖÎöÒ»²½²½Á÷³Ì¡£ + +ÒÔ tomcat ÈÝÆ÷ΪÀý£¬¾­¹ýһϵÁеĵ÷ÊÔÓë´úÂë×·×Ù£¬·¢ÏÖËüÊÇÔÚ `TomcatStarter` ÖÐÔËÐе쬴úÂëÈçÏ£º + +``` +class TomcatStarter implements ServletContainerInitializer { + + private final ServletContextInitializer[] initializers; + + TomcatStarter(ServletContextInitializer[] initializers) { + this.initializers = initializers; + } + + @Override + public void onStartup(Set> classes, ServletContext servletContext) + throws ServletException { + try { + for (ServletContextInitializer initializer : this.initializers) { + // ÕâÀïÖ´ÐÐ ServletContextInitializer#onStartup·½·¨ + initializer.onStartup(servletContext); + } + } + catch (Exception ex) { + this.startUpException = ex; + ... + } + } + + ... + +} + +``` + +`TomcatStarter` ÊÇ springboot ÌṩµÄÀ࣬ËüʵÏÖÁË `ServletContainerInitializer`£¬Çø±ðÓÚ `ServletContextInitializer`£¬`ServletContainerInitializer` ÊÇÓÉ tomcat ÌṩµÄ£¬ÔÚ tomcat Æô¶¯Ê±£¬»áÖ´ÐÐ `ServletContainerInitializer#onStartup` ·½·¨£¨`servlt 3.0` ¹æ·¶£©¡£ + +ÄÇô `TomcatStarter` ÊÇÈçºÎÌí¼Óµ½ tomcat ÈÝÆ÷ÖеÄÄØ£¿ËäÈ» `servlt 3.0` ¹æ·¶¿ÉÒÔͨ¹ý `spi` ¼¼ÊõɨÃèµ½ `ServletContainerInitializer` µÄʵÏÖ£¬µ«ÊÇÕâÀïÃ÷ÏÔ²»ÊÇÕâÑù×öµÄ£¬ÒòΪÈç¹ûÓÉ tomcat ͨ¹ý `spi` ɨÃèµÃµ½ `TomcatStarter` µÄʵÀý£¬ÄÇËüµÄ³ÉÔ±±äÁ¿ `initializers` ¾ÍÎÞ·¨¸³ÖµÁË£¬ËùÒÔÔÚÌí¼Óµ½ tomcat ǰ£¬`TomcatStarter` ¾ÍҪʵÀý»¯²¢ÇÒ `initializers` ¾ÍÒª±»¸³Öµ¡£ + +¾­¹ý¶à´Îµ÷ÊÔ£¬·¢ÏÖ `TomcatStarter` ÊÇÔÚ `TomcatServletWebServerFactory#configureContext` ÖÐÌí¼Óµ½ tomcat ÈÝÆ÷µÄ£¬¹Ø¼ü´úÂëÈçÏÂ: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6625407ce4d1dad8de736db00db124a2a99.png) + +¿ÉÒÔ¿´µ½£¬`initializers` ±»µ±×÷¹¹Ôì²ÎÊý´«Èëµ½ `TomcatStarter` µÄ¹¹Ôì·½·¨ÖУ¬µÃµ½ `TomcatStarter` µÄʵÀý£¬ÔÙÊÖ¶¯Ìí¼Óµ½ tomcat ÈÝÆ÷ÖÐÁË¡£ + +ÄÇô£¬Õâ¸ö `initializers` ÊÇÔÚÄÄÀï»ñÈ¡µ½µÄÄØ£¿ÊÂʵÉÏ£¬ÎÒÃÇµÄ `XxxRegistrationBean` ¶¼Òª spring ÈÝÆ÷ÖУ¬Òª»ñÈ¡µÄ»°£¬Ö»Òªµ÷Óà `beanFactory.getBeansOfType(...)` ¾Í¿ÉÒÔÁË£¬`ServletContextInitializerBeans#addServletContextInitializerBean(String, ServletContextInitializer, ListableBeanFactory)` ¾ÍÊǸÉÕâ¼þÊµģº + +``` +private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { + for (Class initializerType : this.initializerTypes) { + // »ñÈ¡ ServletContextInitializer: getOrderedBeansOfType(beanFactory, initializerType) + for (Entry initializerBean + : getOrderedBeansOfType(beanFactory, initializerType)) { + addServletContextInitializerBean(initializerBean.getKey(), + initializerBean.getValue(), beanFactory); + } + } +} + +/** + * ´¦ÀíÌí¼Ó²Ù×÷ + */ +private void addServletContextInitializerBean(String beanName, + ServletContextInitializer initializer, ListableBeanFactory beanFactory) { + // Ìí¼Ó ServletRegistrationBean + if (initializer instanceof ServletRegistrationBean) { + Servlet source = ((ServletRegistrationBean) initializer).getServlet(); + addServletContextInitializerBean(Servlet.class, beanName, initializer, + beanFactory, source); + } + // Ìí¼Ó FilterRegistrationBean + else if (initializer instanceof FilterRegistrationBean) { + Filter source = ((FilterRegistrationBean) initializer).getFilter(); + addServletContextInitializerBean(Filter.class, beanName, initializer, + beanFactory, source); + } + // Ìí¼Ó DelegatingFilterProxyRegistrationBean + else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { + String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName(); + addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); + } + // Ìí¼Ó ServletListenerRegistrationBean + else if (initializer instanceof ServletListenerRegistrationBean) { + EventListener source = ((ServletListenerRegistrationBean) initializer).getListener(); + addServletContextInitializerBean(EventListener.class, beanName, initializer, + beanFactory, source); + } + else { + // ÆäËûµÄ ServletContextInitializer Bean + addServletContextInitializerBean(ServletContextInitializer.class, beanName, + initializer, beanFactory, initializer); + } +} + +``` + +### 3\. ×ܽá + +±¾ÎÄ·ÖÎöÁË springboot ×¢²á servlet Èý´ó×é¼þµÄÁ÷³Ì£º + +1. ÒÔ `Servlet` ΪÀý£¬½éÉÜÁË 3 ÖÖ×¢²á·½Ê½£ºÊ¹Óà `XxxRegistrationBean` ×¢²á¡¢Ê¹Óà `servlet` ×¢½â (`@WebServlet`/`@WebFilter`/`@WebListener`) ×¢²á£¬ÒÔ¼°ÊµÏÖ `ServletContextInitializer` ½Ó¿ÚÊÖ¶¯×¢²á£» +2. ·ÖÎöÁË `@ServletComponentScan` ×¢²áµÄɨÃèÁ÷³Ì +3. ÒÔ `ServletRegistrationBean` ΪÀý£¬·ÖÎöÁ˽« `ServletRegistrationBean` ×¢²áµ½ servlet µÄÁ÷³Ì +4. ·ÖÎöÁË `ServletContainerInitializer#onStartup` µÄÖ´ÐÐ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4951050](https://my.oschina.net/funcy/blog/4951050) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" new file mode 100644 index 0000000..46adf6d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBootWeb\345\272\224\347\224\250\357\274\210\344\272\214\357\274\211\357\274\232WebMvc\350\243\205\351\205\215\350\277\207\347\250\213.md" @@ -0,0 +1,815 @@ +ƽʱ¹¤×÷ÖУ¬springboot ¾ø´ó¶àÊýÇé¿öÏÂÔËÐеÄÊÇ web ÏîÄ¿£¬±¾ÎĽ«·ÖÎö springboot ×Ô¶¯ springMvc ÏîÄ¿µÄÁ÷³Ì¡£ + +### 1\. springMvc µÄ×Ô¶¯×°ÅäÀà + +springMvc µÄ×Ô¶¯×°ÅäÀàΪ + +``` +@Configuration(proxyBeanMethods = false) +// ¼¸¸ö×°ÅäÌõ¼þ +@ConditionalOnWebApplication(type = Type.SERVLET) +@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) +// Èç¹ûûÓÐ×Ô¶¨ÒåWebMvcµÄÅäÖÃÀ࣬ÔòʹÓñ¾ÅäÖà +@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) +// ×°Åä˳Ðò +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) +@AutoConfigureAfter({ + // DispatcherServlet µÄ×Ô¶¯×°Åä + DispatcherServletAutoConfiguration.class, + // Ï̳߳صÄ×Ô¶¯×°Åä + TaskExecutionAutoConfiguration.class, + // jsr 303 ÑéÖ¤¿ò¼ÜµÄ×Ô¶¯×°Åä + ValidationAutoConfiguration.class }) +public class WebMvcAutoConfiguration { + ... +} + +``` + +´ÓÉÏÃæµÄ `@AutoConfigureAfter` ×¢½âÖУ¬`WebMvcAutoConfiguration` ÐèÒªÔÚ `DispatcherServletAutoConfiguration`¡¢`TaskExecutionAutoConfiguration`¡¢`ValidationAutoConfiguration` µÈÀà×°ÅäÍê³ÉÖ®ºóÔÙ×°Å䣬¶ÔÕâЩÀàµÄ×÷ÓÃÁоÙÈçÏ£º + +* `DispatcherServletAutoConfiguration`£º`DispatcherServlet` ×Ô¶¯×°Åä +* `TaskExecutionAutoConfiguration`£ºÈÎÎñÖ´ÐÐÆ÷£¬Æäʵ¾ÍÊÇ´´½¨ÁËÒ»¸öÏß³Ì³Ø +* `ValidationAutoConfiguration`£ºjsr 303 ÑéÖ¤Æ÷µÄ×Ô¶¯×°Å䣬ÑéÖ¤Æ÷ÓÃÀ´´¦Àí `@NotNull`¡¢`@NotEmpty` µÈ×¢½âµÄÑéÖ¤¹¦ÄÜ + +Õâ 3 ¸öÀàÖУ¬Óë springMvc ÓйصÄÖ»ÓÐ `DispatcherServletAutoConfiguration`£¬ÎÒÃÇÀ´ÈÏʶһÏÂËü£º + +``` +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) +@Configuration(proxyBeanMethods = false) +@ConditionalOnWebApplication(type = Type.SERVLET) +@ConditionalOnClass(DispatcherServlet.class) +// ÐèÒªÔÚ ServletWebServerFactoryAutoConfiguration ×Ô¶¯×°ÅäÍê³Éºó´¦Àí +@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) +public class DispatcherServletAutoConfiguration { + ... +} + +``` + +`DispatcherServletAutoConfiguration` ÐèÒªµÈ `ServletWebServerFactoryAutoConfiguration` ×Ô¶¯×°ÅäÍê³É²Å½øÐÐ×°Å䣬Õâ¸öÀàÊÇ×öʲôµÄÄØ£¿¾ç͸Ï£¬ËüÊÇ´¦Àí servlet ÈÝÆ÷£¨`tomcat`, `jetty`, `undertow` µÈ£©µÄÉú³ÉµÄ£¬ÎÒÃÇÔÙÀ´¿´¿´ `ServletWebServerFactoryAutoConfiguration`£º + +``` +@Configuration(proxyBeanMethods = false) +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) +@ConditionalOnClass(ServletRequest.class) +@ConditionalOnWebApplication(type = Type.SERVLET) +@EnableConfigurationProperties(ServerProperties.class) +// ÒýÈëÁËһЩÀà +@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, + // 3¸ö web ÈÝÆ÷ + ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, + ServletWebServerFactoryConfiguration.EmbeddedJetty.class, + ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) +public class ServletWebServerFactoryAutoConfiguration { + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸öÀàÖÐ×ÜËãÊÇûÓÐ `@AutoConfigureAfter` ×¢½âÁË£¬Òò´ËÕâ¸öÀà springMvc ¾ÍÊÇ×î³õ×Ô¶¯×°ÅäµÄÀ࣬ÎÒÃǵķÖÎö¾Í´ÓÕâ¸öÀ࿪ʼ¡£ + +×ܽáÏÂÒÔÉϼ¸¸öÀàµÄ×°Åä˳Ðò£º + +1. `ServletWebServerFactoryAutoConfiguration` +2. `DispatcherServletAutoConfiguration` +3. `WebMvcAutoConfiguration` + +½ÓÏÂÀ´ÎÒÃǵķÖÎöÒ²°´ÕâÑùµÄ˳Ðò£¬ÖðÒ»·ÖÎöÕâЩ×Ô¶¯×°ÅäÀà¡£ + +### 2. `ServletWebServerFactoryAutoConfiguration` µÄ×Ô¶¯×°Åä + +`ServletWebServerFactoryAutoConfiguration` ÀàÈçÏ£º + +``` +@Configuration(proxyBeanMethods = false) +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) +@ConditionalOnClass(ServletRequest.class) +@ConditionalOnWebApplication(type = Type.SERVLET) +@EnableConfigurationProperties(ServerProperties.class) +// ÒýÈëÁËһЩÀà +@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, + // 3¸ö web ÈÝÆ÷ + ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, + ServletWebServerFactoryConfiguration.EmbeddedJetty.class, + ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) +public class ServletWebServerFactoryAutoConfiguration { + ... +} + +``` + +Õâ¸öÀàÒýÈëÁË `BeanPostProcessorsRegistrar`¡¢`EmbeddedTomcat`¡¢`EmbeddedJetty`¡¢`EmbeddedUndertow`£¬ÎÒÃÇÀ´ÖðÒ»·ÖÎö°É£¡ + +#### 2.1 `BeanPostProcessorsRegistrar` + +`BeanPostProcessorsRegistrar` ÊÇ `ServletWebServerFactoryAutoConfiguration` µÄÄÚ²¿À࣬´úÂëÈçÏ£º + +``` +public static class BeanPostProcessorsRegistrar + implements ImportBeanDefinitionRegistrar, BeanFactoryAware { + + ... + + /** + * À´×Ô ImportBeanDefinitionRegistrar µÄ·½·¨ + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + if (this.beanFactory == null) { + return; + } + // ×¢²á×é¼þ + registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", + WebServerFactoryCustomizerBeanPostProcessor.class); + registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", + ErrorPageRegistrarBeanPostProcessor.class); + } + + /** + * ¾ßÌåµÄ×¢²á²Ù×÷ + */ + private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, + String name, Class beanClass) { + if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); + beanDefinition.setSynthetic(true); + registry.registerBeanDefinition(name, beanDefinition); + } + } +} + +``` + +Õâ¸öÀàÖ÷ÒªÊÇÏò spring ÈÝÆ÷ÖÐ×¢²áÁËÁ½¸öÀࣺ`WebServerFactoryCustomizerBeanPostProcessor`¡¢`ErrorPageRegistrarBeanPostProcessor`£¬ÎÒÃÇÀ´¿´ÏÂËüÃÇ·Ö±ðÊǸöɶ¡£ + +##### 1. `WebServerFactoryCustomizerBeanPostProcessor` + +`WebServerFactoryCustomizerBeanPostProcessor` µÄ´úÂëÈçÏ£º + +``` +public class WebServerFactoryCustomizerBeanPostProcessor + implements BeanPostProcessor, BeanFactoryAware { + + ... + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + // bean µÄÀàÐÍÊÇ WebServerFactory ²Å´¦Àí + if (bean instanceof WebServerFactory) { + postProcessBeforeInitialization((WebServerFactory) bean); + } + return bean; + } + + @SuppressWarnings("unchecked") + private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { + LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory) + .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class) + // ´¦ÀíÅäÖà + .invoke((customizer) -> customizer.customize(webServerFactory)); + } + + /** + * »ñÈ¡ WebServerFactoryCustomizer£¬µÃµ½µÄÊÇÒ»¸ö²»¿É±äµÄ List + */ + private Collection> getCustomizers() { + if (this.customizers == null) { + this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans()); + this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE); + this.customizers = Collections.unmodifiableList(this.customizers); + } + return this.customizers; + } + + /** + * »ñÈ¡ beanFactory ÖÐËùÓÐµÄ WebServerFactoryCustomizer + */ + private Collection> getWebServerFactoryCustomizerBeans() { + return (Collection) this.beanFactory.getBeansOfType( + WebServerFactoryCustomizer.class, false, false).values(); + } + +} + +``` + +Õâ¸öÀàÊÇÓÃÀ´´¦Àí `WebServerFactory` µÄ×Ô¶¨ÒåÅäÖõģ¬ËüʵÏÖÁË `BeanPostProcessor`£¬ ÎÒÃÇÖ÷Òª¹Ø×¢ËüµÄ `postProcessBeforeInitialization(...)` ·½·¨¡£ + +ÔÚ `WebServerFactoryCustomizerBeanPostProcessor` µÄ `postProcessBeforeInitialization(...)` ·½·¨ÖУ¬Èç¹ûµ±Ç° bean µÄÀàÐÍÊÇ `WebServerFactory`£¬»áÏÈ»ñÈ¡ `beanFactory` ÖÐËùÓÐÀàÐÍΪ `WebServerFactoryCustomizer` µÄ bean£¬È»ºó½«ÕâЩ `WebServerFactoryCustomizer` ÅäÖõ½ `WebServerFactory`bean ÖС£ + +Èç¹ûÎÒÃÇÒª×Ô¶¨Òå `Tomcat` µÄÅäÖ㬿ÉÒÔÕâÑù´¦Àí£º + +``` +@Component +public class MyCustomizer implements WebServerFactoryCustomizer { + + @Override + public void customize(TomcatServletWebServerFactory factory) { + factory.setPort(8091); + factory.setContextPath("/"); + // ÉèÖÃÆäËûÅäÖà + ... + } + +} + +``` + +ÔÚ `ServletWebServerFactoryAutoConfiguration` ÖÐÌṩÁËÁ½¸ö `WebServerFactoryCustomizer`: + +``` +public class ServletWebServerFactoryAutoConfiguration { + + @Bean + public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer( + ServerProperties serverProperties) { + return new ServletWebServerFactoryCustomizer(serverProperties); + } + + @Bean + @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") + public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( + ServerProperties serverProperties) { + return new TomcatServletWebServerFactoryCustomizer(serverProperties); + } + + ... + +} + +``` + +´Ó·½·¨²ÎÊýÀ´¿´£¬ÕâÁ½¸öÀàµÄÅäÖö¼À´×ÔÓÚ `ServerProperties`£º + +``` +@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) +public class ServerProperties { + ... +} + +``` + +ËüÉÏÃæÁË `ConfigurationProperties` ×¢½â£¬`prefix` Ϊ "server"£¬Õâ±íÃ÷ËüµÄÅäÖö¼ÊÇÒÔ `server` ¿ªÍ·£¬Ö§³ÖµÄÅäÖÃÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-586d95875e2eeefd295643c3e6acf0091e1.png) + +ÀýÈ磬ÎÒÃÇÒªÅäÖÃËüµÄ¶Ë¿Ú£¬Ö»ÐèÔÚ `application.properties` ÖÐÕâÑùÅäÖü´¿É£º + +``` +server.port=8080 + +``` + +ÖÁÓÚÆäËûÅäÖã¬ÕâÀï¾Í²»¶à×÷·ÖÎöÁË¡£ + +##### 2. `ErrorPageRegistrarBeanPostProcessor` + +`ErrorPageRegistrarBeanPostProcessor` µÄ´úÂëÈçÏ£º + +``` +public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + + /** + * À´×ÔBeanPostProcessorµÄ·½·¨£¬ÔÚbean³õʼ»¯Ç°Ö´ÐÐ + */ + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof ErrorPageRegistry) { + // Èç¹û bean ÊÇ ErrorPageRegistry + postProcessBeforeInitialization((ErrorPageRegistry) bean); + } + return bean; + } + + private void postProcessBeforeInitialization(ErrorPageRegistry registry) { + for (ErrorPageRegistrar registrar : getRegistrars()) { + // ×¢²á´íÎóÒ³ + registrar.registerErrorPages(registry); + } + } + + private Collection getRegistrars() { + if (this.registrars == null) { + // »ñÈ¡ËùÓеĴíÎóÒ³ + this.registrars = new ArrayList<>(this.beanFactory.getBeansOfType( + ErrorPageRegistrar.class, false, false).values()); + this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE); + this.registrars = Collections.unmodifiableList(this.registrars); + } + return this.registrars; + } + + ... +} + +``` + +ÎÒÃÇÖ÷Òª¹Ø×¢ `postProcessBeforeInitialization` µÄÁ÷³Ì£º + +1. Èç¹û bean µÄÀàÐÍÊÇ `ErrorPageRegistry`£¬½øÐÐµÚ 2 ²½ +2. »ñÈ¡ beanFactory ÖÐËùÓÐ `ErrorPageRegistrar` ÀàÐ굀 bean£¬±éÀú½øÐÐµÚ 3 ²½ +3. µ÷Óà `registrar.registerErrorPages(registry)` ½øÐдíÎóÒ³²Ù×÷ + +ÕâÀïÓÐÁ½¸öÀàÐèҪ˵Ã÷£º + +* `ErrorPageRegistrar`£º´íÎóÒ³×¢²áÆ÷£¨Èà `ErrorPageRegistry` ¸É»îµÄÀࣩ +* `ErrorPageRegistry`£º´íÎóµÄ×¢²áÆ÷Àࣨʵ¼Ê¸É»îµÄÀࣩ + +Èç¹ûÎÒÃÇÏëÒª×Ô¶¨Òå´íÎóÒ³£¬¿ÉÒÔʵÏÖ `ErrorPageRegistry` ½Ó¿Ú£º + +``` +@Component +public class MyErrorPage implements ErrorPageRegistrar { + + /** + * ×¢²á´íÎóÒ³ + */ + @Override + public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { + // ×îÖÕµ÷ÓõÄÊÇ ErrorPageRegistry#addErrorPages ½øÐÐ×¢²á + errorPageRegistry.addErrorPages(new ErrorPage("/error/page")); + } +} + +``` + +#### 2.2 `EmbeddedTomcat` + +½øÈë `ServletWebServerFactoryConfiguration.EmbeddedTomcat` Àࣺ + +``` +@Configuration(proxyBeanMethods = false) +class ServletWebServerFactoryConfiguration { + + @Configuration(proxyBeanMethods = false) + // Ìõ¼þ×¢½â£¬Ìõ¼þ´æÔÚʱ²ÅÒýÈë + @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) + // ×¢Òâ ServletWebServerFactory£¬ÎÒÃÇ¿ÉÒÔ×ÔÖ÷ʵÏÖ tomcat ÈÝÆ÷µÄ×°Åä + @ConditionalOnMissingBean(value = ServletWebServerFactory.class, + search = SearchStrategy.CURRENT) + public static class EmbeddedTomcat { + + @Bean + public TomcatServletWebServerFactory tomcatServletWebServerFactory( + ObjectProvider connectorCustomizers, + ObjectProvider contextCustomizers, + ObjectProvider> protocolHandlerCustomizers) { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); + // ÅäÖÃһЩ²ÎÊý + factory.getTomcatConnectorCustomizers() + .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); + factory.getTomcatContextCustomizers() + .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); + factory.getTomcatProtocolHandlerCustomizers() + .addAll(protocolHandlerCustomizers.orderedStream() + .collect(Collectors.toList())); + return factory; + } + + } + + ... +} + +``` + +Õâ¸öÀàÖ÷Òª¾ÍÊÇ·µ»Ø `TomcatServletWebServerFactory` bean£¬¿ÉÒÔ×¢ÈëһЩ `connectorCustomizers`¡¢`contextCustomizers`¡¢`protocolHandlerCustomizers` µÈ²ÎÊý½øÐÐ×Ô¶¨ÒåÅäÖã¬ÕâЩ²ÎÊý¾ÍÊÇ´Ó `BeanPostProcessorsRegistrar` ÖÐÀ´µÄ¡£ + +ÕâÀïÓиöµØ·½ÐèÒªÌáһϣ¬Èç¹û²»ÏëʹÓà springboot ÌṩµÄ `TomcatServletWebServerFactory`£¬ÎÒÃÇ¿ÉÒÔ×Ô¼ºÊµÏÖ `TomcatServletWebServerFactory`£¬ÏñÕâÑù£º + +``` +@Bean +public ServletWebServerFactory servletWebServerFactory() { + TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); + // ´¦Àí×Ô¶¨ÒåµÄ¸÷ÖÖÅäÖà + ... + return tomcat; +} + +``` + +ÕâÑù´¦ÀíÖ®ºó£¬springboot ÌṩµÄ `tomcatServletWebServerFactory` ¾Í²»»á´¦ÀíÁË¡£ + +ÆäËûÁ½¸öÀà `EmbeddedJetty`¡¢`EmbeddedUndertow`£¬¸ú `EmbeddedTomcat` µÄ´¦Àí»ù±¾ÏàËÆ£¬¾Í²»¶à˵ÁË¡£ + +### 3. `DispatcherServletAutoConfiguration` + +ÎÒÃÇÔÙÀ´¿´¿´ `DispatcherServletAutoConfiguration`£¬¹Ø¼ü´úÂëÈçÏ£º + +``` +public class DispatcherServletAutoConfiguration { + + public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; + + public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME + = "dispatcherServletRegistration"; + + @Configuration(proxyBeanMethods = false) + @Conditional(DefaultDispatcherServletCondition.class) + @ConditionalOnClass(ServletRegistration.class) + @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class }) + protected static class DispatcherServletConfiguration { + + /** + * ´óÃû¶¦¶¦µÄ DispatcherServlet. + * @param httpProperties httpÊôÐÔ. + * @param webMvcProperties webMvc ÊôÐÔ. + * @return ·µ»Ø¶ÔÏó + */ + @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) + public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, + WebMvcProperties webMvcProperties) { + DispatcherServlet dispatcherServlet = new DispatcherServlet(); + dispatcherServlet.setDispatchOptionsRequest( + webMvcProperties.isDispatchOptionsRequest()); + dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); + dispatcherServlet.setThrowExceptionIfNoHandlerFound( + webMvcProperties.isThrowExceptionIfNoHandlerFound()); + dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); + dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails()); + return dispatcherServlet; + } + + /** + * ÎļþÉÏ´«×é¼þ. + * @param resolver ²ÎÊý. + * @return ·µ»ØÖµ. + */ + @Bean + @ConditionalOnBean(MultipartResolver.class) + @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) + public MultipartResolver multipartResolver(MultipartResolver resolver) { + return resolver; + } + + } + + /** + * Éú³É DispatcherServletRegistrationBean + * Ëü»á½«dispatcherServlet×¢²áµ½servletÈÝÆ÷ + */ + @Configuration(proxyBeanMethods = false) + @Conditional(DispatcherServletRegistrationCondition.class) + @ConditionalOnClass(ServletRegistration.class) + @EnableConfigurationProperties(WebMvcProperties.class) + @Import(DispatcherServletConfiguration.class) + protected static class DispatcherServletRegistrationConfiguration { + + @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) + @ConditionalOnBean(value = DispatcherServlet.class, + name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) + public DispatcherServletRegistrationBean dispatcherServletRegistration( + DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, + ObjectProvider multipartConfig) { + // Éú³É DispatcherServletRegistrationBean£¨Ëü»á½«dispatcherServlet×¢²áµ½servletÈÝÆ÷£© + DispatcherServletRegistrationBean registration = + new DispatcherServletRegistrationBean(dispatcherServlet, + webMvcProperties.getServlet().getPath()); + registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); + registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); + multipartConfig.ifAvailable(registration::setMultipartConfig); + return registration; + } + + } + + ... + +} + +``` + +ËüÖ÷ÒªÊÇ×¢²áÁË 3 ¸ö bean£º + +* `dispatcherServlet`£ºspringMvc µÄÇëÇóÈë¿Ú£¬url ÇëÇóÓɴ˽øÈ룬Ȼºóת»¯µ½ `requestMapping` +* `multipartResolver`£º´¦ÀíÎļþÉÏ´« +* `dispatcherServletRegistration`£º´¦Àí `dispatcherServlet` µÄ×¢²á£¬Ëü»á½« `dispatcherServlet` ×¢²áµ½ servlet ÈÝÆ÷ÖУ¨¹ØÓÚ springboot ×¢²á servlet ×é¼þµÄÄÚÈÝ£¬¿ÉÒԲο¼ [springboot web Ó¦ÓÃÖ® servlet ×é¼þµÄ×¢²áÁ÷³Ì](https://my.oschina.net/funcy/blog/4951050)£© + +### 4. `WebMvcAutoConfiguration` + +¼ÌÐø¿´ `WebMvcAutoConfiguration`: + +``` +@Configuration(proxyBeanMethods = false) +... +// Èç¹ûûÓÐ×Ô¶¨ÒåWebMvcµÄÅäÖÃÀ࣬ÔòʹÓñ¾ÅäÖà +@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) +... +public class WebMvcAutoConfiguration { + ... +} + +``` + +`WebMvcAutoConfiguration` ÉÏÓиö×¢½âÐèҪעÒâÏ£º + +``` +@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) + +``` + +ÕâÐдúÂë±íÃ÷ÁË `WebMvcAutoConfiguration` Ö»ÓÐÔÚ `WebMvcConfigurationSupport` ÀàÐ굀 bean ²»´æÔÚʱ£¬Ëü²Å»á½øÐÐ×Ô¶¯×°Åä¡£ + +`WebMvcConfigurationSupport` ÊǸöɶĨ£¿ÔÚ [springmvc demo Óë @EnableWebMvc ×¢½â](https://my.oschina.net/funcy/blog/4696657)Ò»ÎÄÖУ¬ÎÒÃÇÌáµ½ÒýÈë springMvc ×é¼þµÄÁ½ÖÖ·½Ê½£º + +* ʹÓà `@EnableWebMvc` ×¢½â +* ¼Ì³Ð `WebMvcConfigurationSupport` Àà + +¶øÕâÖÖ·½·¨×îÖÕ¶¼»áÏò spring ÈÝÆ÷ÖÐÒýÈëÀàÐÍΪ `WebMvcConfigurationSupport` µÄ bean£¬Òò´ËÔÚ `springboot` ÏîÄ¿ÖУ¬Èç¹ûÎÒÃǽøÐÐÁËÒÔÉÏÁ½ÖÖ²Ù×÷Ö®Ò»£¬ÄÇô¾ÍÒýÈëÁË `WebMvcConfigurationSupport`£¬`WebMvcAutoConfiguration` µÄ×Ô¶¯×°Åä¾Í²»Ö´ÐÐÁË¡£ + +ÎÒÃÇÔÙÀ´¿´¿´ `WebMvcAutoConfiguration` ×°ÅäµÄ bean¡£ + +#### 4.1 `WebMvcAutoConfigurationAdapter` + +`WebMvcAutoConfigurationAdapter` ÊÇ `WebMvcAutoConfiguration` µÄÄÚ²¿À࣬¶¨ÒåÈçÏ£º + +``` +@Configuration(proxyBeanMethods = false) +// ÒýÈëÁË EnableWebMvcConfiguration +@Import(EnableWebMvcConfiguration.class) +@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) +@Order(0) +public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer { + ... +} + +``` + +ËüʵÏÖÁË `WebMvcConfigurer`£¬ÇÒÒýÈëÁË `EnableWebMvcConfiguration`¡£`WebMvcConfigurer` ¿ÉÒÔÓÃÀ´´¦Àí springMvc µÄÅäÖã¬Ö»ÐèÒªÖØÐ´ÆäÖжÔÓ¦µÄ·½·¨¼´¿É£¬`EnableWebMvcConfiguration` ´ÓÃû³ÆÀ´¿´£¬ÊÇ ¡°ÆôÓà webMvc ÅäÖá±£¬ËüÒ²ÊÇ `WebMvcAutoConfiguration` µÄÄÚ²¿À࣬ÎÒÃÇÀ´¿´¿´Ëü×öÁËɶ£º + +``` +@Configuration(proxyBeanMethods = false) +public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration + implements ResourceLoaderAware { + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬ËüÊÇ `DelegatingWebMvcConfiguration` µÄ×ÓÀ࣬¶ø `DelegatingWebMvcConfiguration` ÓÖÊǸöɶĨ£¿ÎÒÃÇÒ²À´¿´¿´ËüµÄ¶¨Ò壺 + +``` +@Configuration(proxyBeanMethods = false) +public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { + ... +} + +``` + +`DelegatingWebMvcConfiguration` ÊÇÓÉ springMvc ÌṩµÄ£¬¿ÉÒÔ¿´µ½£¬ËüʵÏÖÁË `WebMvcConfigurationSupport`£¬Òò´Ë£¬springboot ͨ¹ý `@Import(EnableWebMvcConfiguration.class)` µÄ·½Ê½Ïò spring ÈÝÆ÷ÖÐÒýÈëÁË `WebMvcConfigurationSupport` ÀàÐ굀 bean¡£ + +ÊÂʵÉÏ£¬`DelegatingWebMvcConfiguration` ÕýÊÇ `@EnableWebMvc` ÒýÈëµÄ bean£º + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +// ÒýÈëÁË DelegatingWebMvcConfiguration +@Import(DelegatingWebMvcConfiguration.class) +public @interface EnableWebMvc { + ... +} + +``` + +springbot ²»Ö±½ÓÒýÈë `DelegatingWebMvcConfiguration` ¶øÊÇÒýÈëËüµÄ×ÓÀà `EnableWebMvcConfiguration`£¬ËäÈ»ËüÊÇ×öÁËһЩ×Ô¶¨ÒåÅäÖõġ£ + +¹ØÓÚ `EnableWebMvcConfiguration` ÅäÖÃÁËЩɶ£¬ÎÒÃÇÒ»»áÔÙ·ÖÎö£¬¼ÌÐø·ÖÎö `WebMvcAutoConfigurationAdapter` µÄÒýÈë bean£º + +``` +/** + * http ÏûϢת»»Æ÷. + */ +@Override +public void configureMessageConverters(List> converters) { + this.messageConvertersProvider.ifAvailable( + (customConverters) -> converters.addAll(customConverters.getConverters())); +} + +/** + * ÊÓͼ½âÎöÆ÷. + */ +@Bean +@ConditionalOnMissingBean +public InternalResourceViewResolver defaultViewResolver() { + InternalResourceViewResolver resolver = new InternalResourceViewResolver(); + resolver.setPrefix(this.mvcProperties.getView().getPrefix()); + resolver.setSuffix(this.mvcProperties.getView().getSuffix()); + return resolver; +} + +@Bean +@ConditionalOnBean(View.class) +@ConditionalOnMissingBean +public BeanNameViewResolver beanNameViewResolver() { + BeanNameViewResolver resolver = new BeanNameViewResolver(); + resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10); + return resolver; +} + +@Bean +@ConditionalOnBean(ViewResolver.class) +@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class) +public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) { + ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver(); + resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class)); + resolver.setOrder(Ordered.HIGHEST_PRECEDENCE); + return resolver; +} + +/** + * ÒýÈë¹ú¼Ê»¯ÅäÖÃ. + */ +@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; +} + +/** + * ¾²Ì¬×ÊÔ´Ó³Éä + */ +@Override +public void addResourceHandlers(ResourceHandlerRegistry registry) { + if (!this.resourceProperties.isAddMappings()) { + logger.debug("Default resource handling disabled"); + return; + } + // Ó³Éäwebjars + Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); + CacheControl cacheControl = this.resourceProperties.getCache() + .getCachecontrol().toHttpCacheControl(); + if (!registry.hasMappingForPattern("/webjars/**")) { + customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/") + .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); + } + // Ó³É侲̬×ÊԴ·¾¶ + String staticPathPattern = this.mvcProperties.getStaticPathPattern(); + if (!registry.hasMappingForPattern(staticPathPattern)) { + customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) + // staticLocations ĬÈÏΪ classpath:[/META-INF/resources/, + // /resources/, /static/, /public/] + .addResourceLocations(getResourceLocations( + this.resourceProperties.getStaticLocations())) + .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); + } +} + +``` + +¿ÉÒÔ¿´µ½£¬`WebMvcAutoConfigurationAdapter` ÒýÈëÁËÈýÀà bean: + +1. http ÏûϢת»»Æ÷ +2. ÊÓͼ½âÎöÆ÷ +3. ¹ú¼Ê»¯ÅäÖà + +ÕâÀïÎÒÃÇÖØµãÀ´¿´¿´ `httpÏûϢת»»Æ÷`¡£ÔÚʹÓà springMvc ʱ£¬Í¨¹ýÔÚ `Controller` Àà»òÆäÖеķ½·¨Éϱê¼Ç `@ResponseBody`£¬·µ»Ø²ÎÊý¾Í»áת»»Îª json£¬Õâ¾ÍÊÇ `httpÏûϢת»»Æ÷`Ëù×öµÄ¹¤×÷ÁË¡£springboot ĬÈ쵀 jaon ´¦ÀíÊÇ `jackson`£¬ËüµÄʵÀý»¯ÔÚ `JacksonAutoConfiguration` ÖУ¬×°ÅäΪ `HttpMessageConverter` µÄ´¦ÀíÔÚ `JacksonHttpMessageConvertersConfiguration`£¬ÕâÀï¾Í²»Õ¹¿ªÁË¡£ + +ÔÚÉÏÃæ µÄ·ÖÎöÖУ¬¶à´Î³öÏÖÁË `WebMvcProperties` ÅäÖã¬ÎÒÃÇÀ´¿´¿´ËüÊǸöɶ£º + +``` +@ConfigurationProperties(prefix = "spring.mvc") +public class WebMvcProperties { + ... +} + +``` + +ºÜÃ÷ÏÔ£¬ËüÊǸöÅäÖÃÀà £¬¿ÉÒÔ¿´µ½£¬ËüµÄÅäÖÃǰ׺Ϊ `spring.mvc`£¬Ö§³ÖµÄÅäÖÃÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-062f490afbb74e496f50f4cad4bd13ede82.png) + +#### 4.2 `EnableWebMvcConfiguration` + +ÎÒÃÇÀ´¿´¿´ `EnableWebMvcConfiguration` ×Ô¶¨ÒåµÄÅäÖ㺠+ +``` +public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration + implements ResourceLoaderAware { + + /** + * ÅäÖÃÊÊÅäÆ÷ + */ + @Bean + @Override + public RequestMappingHandlerAdapter requestMappingHandlerAdapter( + @Qualifier("mvcContentNegotiationManager") + ContentNegotiationManager contentNegotiationManager, + @Qualifier("mvcConversionService") FormattingConversionService conversionService, + @Qualifier("mvcValidator") Validator validator) { + RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter( + contentNegotiationManager, conversionService, validator); + adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null + || this.mvcProperties.isIgnoreDefaultModelOnRedirect()); + return adapter; + } + + /** + * ·¾¶Ó³Éä + */ + @Bean + @Primary + @Override + public RequestMappingHandlerMapping requestMappingHandlerMapping( + @Qualifier("mvcContentNegotiationManager") + ContentNegotiationManager contentNegotiationManager, + @Qualifier("mvcConversionService") FormattingConversionService conversionService, + @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { + // Must be @Primary for MvcUriComponentsBuilder to work + return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, + resourceUrlProvider); + } + + /** + * »¶Ó­Ò³ + */ + @Bean + public WelcomePageHandlerMapping welcomePageHandlerMapping( + ApplicationContext applicationContext, + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { + WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping( + new TemplateAvailabilityProviders(applicationContext), applicationContext, + getWelcomePage(), this.mvcProperties.getStaticPathPattern()); + welcomePageHandlerMapping.setInterceptors(getInterceptors( + mvcConversionService, mvcResourceUrlProvider)); + return welcomePageHandlerMapping; + } + + /** + * mvcÅäÖã¬Ìí¼ÓÁËÈÕÆÚ¸ñʽµÄ´¦Àí + */ + @Bean + @Override + public FormattingConversionService mvcConversionService() { + WebConversionService conversionService + = new WebConversionService(this.mvcProperties.getDateFormat()); + addFormatters(conversionService); + return conversionService; + } + + /** + * ²ÎÊýУÑéÆ÷ + */ + @Bean + @Override + public Validator mvcValidator() { + if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) { + return super.mvcValidator(); + } + return ValidatorAdapter.get(getApplicationContext(), getValidator()); + } + + /** + * Òì³£´¦Àí + */ + @Override + protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() { + if (this.mvcRegistrations != null + && this.mvcRegistrations.getExceptionHandlerExceptionResolver() != null) { + return this.mvcRegistrations.getExceptionHandlerExceptionResolver(); + } + return super.createExceptionHandlerExceptionResolver(); + } + + @Override + protected void extendHandlerExceptionResolvers( + List exceptionResolvers) { + super.extendHandlerExceptionResolvers(exceptionResolvers); + if (this.mvcProperties.isLogResolvedException()) { + for (HandlerExceptionResolver resolver : exceptionResolvers) { + if (resolver instanceof AbstractHandlerExceptionResolver) { + ((AbstractHandlerExceptionResolver) resolver).setWarnLogCategory( + resolver.getClass().getName()); + } + } + } + } + ... +} + +``` + +Ïà±ÈÓÚÔ­ÉúµÄ springMvc£¬Ëü¶îÍâÅäÖÃÁËÊÊÅäÆ÷¡¢Â·¾¶Ó³Éä¡¢²ÎÊýУÑé¡¢Òì³£´¦ÀíµÈ¡£ + +### 5\. ×ܽá + +1. `webMvc` ÔÚ×°Åäʱ£¬ÏÈ×°Åä `ServletWebServerFactoryAutoConfiguration`£¬ÔÙ×°Åä `DispatcherServletAutoConfiguration`£¬×îºó×°Åä `WebMvcAutoConfiguration` +2. `ServletWebServerFactoryAutoConfiguration` ´¦Àí servlet ÈÝÆ÷µÄ×°Å䣬`DispatcherServletAutoConfiguration` ´¦Àí `DispatcherServlet` µÄ×°Å䣬`WebMvcAutoConfiguration` ´¦Àí `webMvc` ×é¼þ£¨ÏûϢת»»Æ÷¡¢ÊÓͼ½âÎöÆ÷¡¢¾²Ì¬×ÊÔ´Ó³ÉäµÈ£©µÄ×°Åä +3. Èç¹ûÏò spring ÈÝÆ÷ÖÐÒýÈëÁË `WebMvcConfigurationSupport`£¬ `WebMvcAutoConfiguration` µÄ×°Åä²Ù×÷½«²»»áÖ´ÐÐ +4. `servlet` µÄÏà¹ØÅäÖÃÒÔ `servlet` Ϊǰ׺£¬`webMvc` µÄÅäÖÃÒÔ `spring.mvc` Ϊǰ׺£¬ÎÒÃÇ¿ÉÒÔÔÚÅäÖÃÎļþ£¨Ò»°ãΪ `application.properties/application.yml`£©ÖнøÐÐÅäÖà + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4921595](https://my.oschina.net/funcy/blog/4921595) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" new file mode 100644 index 0000000..ece7a22 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\207\206\345\244\207SpringApplication.md" @@ -0,0 +1,318 @@ +±¾ÎÄÀ´Ì½¾¿ springboot ÊÇÈçºÎÆô¶¯µÄ£¬±¾ÎÄʹÓõ½µÄ demo λÓÚ?[gitee/funcy](https://gitee.com/funcy/spring-boot/tree/v2.2.2.RELEASE_learn/spring-boot-project/spring-boot-learn/src/main/java/org/springframework/boot/learn/autoconfigure/demo01). + +## 1\. ´Ó?`Demo01Application#main(...)`?¿ªÊ¼ + +springboot µÄÆô¶¯·½Ê½·Ç³£¼òµ¥£¬¾ÍÒ»ÐУº + +``` +@SpringBootApplication +public class Demo01Application { + + public static void main(String[] args) { + // ÕâÒ»ÐоÍÊÇÓÃÀ´Æô¶¯springbootµÄ + SpringApplication.run(Demo01Application.class, args); + } +} + +``` + +È»ºóÎÒÃǾͽøÈëÕâ¸ö·½·¨£¬¿´¿´Ëü¸ÉÁËʲô£º + +``` +public class SpringApplication { + ... + // primarySource ¾ÍÊÇÎÒÃÇ´«ÈëµÄ Demo01Application.class£¬ + // args ¾ÍÊÇ main() ·½·¨µÄ²ÎÊý + public static ConfigurableApplicationContext run( + Class primarySource, String... args) { + // ½« primarySource °ü×°³ÉÊý×飬¼ÌÐøµ÷Óà run(...) ·½·¨ + return run(new Class[] { primarySource }, args); + } + + // primarySources ¾ÍÊÇÎÒÃÇ´«ÈëµÄ Demo01Application.class °ü×°³ÉµÄÊý×飬 + // args ¾ÍÊÇ main() ·½·¨µÄ²ÎÊý + public static ConfigurableApplicationContext run( + Class[] primarySources, String[] args) { + // ÕâÀ↑ʼ¸ÉÕýÊÂÁË + return new SpringApplication(primarySources).run(args); + } + ... +} + +``` + +ͨ¹ý·½·¨Ò»²½²½×·²éÏÂÈ¥ºó£¬×îºóµ½ÁË?`SpringApplication#run(Class[], String[])`?·½·¨£¬¹Ø¼ü´úÂëÈçÏ£º + +``` +return new SpringApplication(primarySources).run(args); + +``` + +Õâ¿é´úÂëÐèÒª²ð¿ªÀ´¿´£¬¿ÉÒÔ²ð³ÉÒÔÏÂÁ½²¿·Ö£º + +* ¹¹Ôì·½·¨£º`SpringApplication#SpringApplication(Class...)` +* ʵÀý·½·¨£º`SpringApplication#run(String...)` + +¿´À´£¬ÕâÁ½¸ö·½·¨¾ÍÊÇ springboot µÄÆô¶¯ËùÓÐÁ÷³ÌÁË£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÕâÁ½¸ö·½·¨¡£ + +## 2\. ´´½¨?`SpringApplication`£º`SpringApplication#SpringApplication(Class...)` + +``` +public class SpringApplication { + public SpringApplication(Class... primarySources) { + // ¼ÌÐøµ÷Óà + this(null, primarySources); + } + + /** + * ÕâÀï¾Í×îÖÕµ÷ÓõĹ¹Ôì·½·¨ÁË + * resourceLoader Ϊ null + * primarySources Ϊ Demo01Application.class + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { + // 1\. ½«´«ÈëµÄresourceLoaderÉèÖõ½³ÉÔ±±äÁ¿£¬ÕâÀïµÄֵΪnull + this.resourceLoader = resourceLoader; + Assert.notNull(primarySources, "PrimarySources must not be null"); + // 2\. ½«´«ÈëµÄprimarySourcesÉèÖõ½³ÉÔ±±äÁ¿£¬ÕâÀïµÄֵΪ Demo01Application.class + this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); + // 3\. µ±Ç°µÄ web Ó¦ÓÃÀàÐÍ£¬REACTIVE£¬NONE£¬SERVLET + this.webApplicationType = WebApplicationType.deduceFromClasspath(); + // 4\. ÉèÖóõʼ»¯Æ÷£¬getSpringFactoriesInstances£º´Ó META-INF/spring.factories ÖлñÈ¡ÅäÖà + setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); + // 5\. ÉèÖüàÌýÆ÷£¬getSpringFactoriesInstances£º´Ó META-INF/spring.factories ÖлñÈ¡ÅäÖà + setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); + // 6\. ·µ»Ø°üº¬main()·½·¨µÄclass + this.mainApplicationClass = deduceMainApplicationClass(); + } +} + +``` + +Õâ¸ö·½·¨Ëù×öµÄÊ»¹ÊDZȽÏÇåÎúµÄ£¬Ïà¹ØÄÚÈݶ¼ÒÑÔÚ´úÂëÖÐ×¢ÊÍÁË£¬²»¹ýÓÐЩ·½·¨»¹ÐèÒªÕ¹¿ª£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´¿´¿´¡£ + +### 2.1 »ñÈ¡µ±Ç° web Ó¦ÓÃÀàÐÍ£º`WebApplicationType.deduceFromClasspath()` + +`WebApplicationType.deduceFromClasspath()`?·½·¨ÊÇÓÃÀ´Íƶϵ±Ç°ÏîÄ¿ÊÇʲôÀàÐ͵쬴úÂëÈçÏ£º + +``` +public enum WebApplicationType { + // ²»ÊÇ web Ó¦Óà + NONE, + + // servlet ÀàÐ굀 web Ó¦Óà + SERVLET, + + // reactive ÀàÐ굀 web Ó¦Óà + REACTIVE; + + ... + + private static final String[] SERVLET_INDICATOR_CLASSES = { + "javax.servlet.Servlet", + "org.springframework.web.context.ConfigurableWebApplicationContext" }; + + private static final String WEBMVC_INDICATOR_CLASS + = "org.springframework.web.servlet.DispatcherServlet"; + + private static final String WEBFLUX_INDICATOR_CLASS + = "org.springframework.web.reactive.DispatcherHandler"; + + private static final String JERSEY_INDICATOR_CLASS + = "org.glassfish.jersey.servlet.ServletContainer"; + + static WebApplicationType deduceFromClasspath() { + // classpath Öнö´æÔÚ WEBFLUX Ïà¹ØÀà + if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) + && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) + && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { + return WebApplicationType.REACTIVE; + } + // classpath ²»´æÔÚ SERVLET Ïà¹ØÀà + for (String className : SERVLET_INDICATOR_CLASSES) { + if (!ClassUtils.isPresent(className, null)) { + return WebApplicationType.NONE; + } + } + // ĬÈÏ web ÀàÐÍΪ SERVLET + // Ò²¾ÍÊÇ˵£¬Í¬Ê±´æÔÚ WEBFLUX Óë SERVLET Ïà¹ØÀ࣬×îÖÕ·µ»ØµÄÊÇ SERVLET + return WebApplicationType.SERVLET; + } + + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬springboot ¶¨ÒåÁËÈýÖÖÏîÄ¿ÀàÐÍ£º`NONE`(²»ÊÇ web Ó¦ÓÃ)¡¢`SERVLET`(`servlet`?ÀàÐ굀 web Ó¦ÓÃ)¡¢`REACTIVE`(`reactive`?ÀàÐ굀 web Ó¦ÓÃ)£¬`WebApplicationType.deduceFromClasspath()`?µÄÖ´ÐÐÁ÷³ÌÈçÏ£º + +1. Èç¹û?`classpath`?Öнö´æÔÚ?`WEBFLUX`?Ïà¹ØÀ࣬Ôò±íÃ÷µ±Ç°ÏîÄ¿ÊÇ?`reactive`?ÀàÐ굀 web Ó¦Ó㬷µ»Ø£» +2. Èç¹û?`classpath`?Öв»´æÔÚ?`SERVLET`?Ïà¹ØÀ࣬Ôò±íÃ÷µ±Ç°ÏîÄ¿²»ÊÇ web Ó¦Ó㬷µ»Ø£» +3. Èç¹ûÒÔÉÏÌõ¼þ¶¼²»Âú×㣬Ôò±íÃ÷µ±Ç°ÏîÄ¿ÊÇ?`servlet`?ÀàÐ굀 web Ó¦Óᣠ+ +ÓÉÓÚ demo ÒýÓÃÁË?`spring-boot-starter-web`?Ïà¹ØÒÀÀµ£¬Òò´Ëµ±Ç°ÏîÄ¿ÊÇ?`servlet`?ÀàÐ굀 web Ó¦Óᣠ+ +### 2.2 ÉèÖóõʼ»¯Æ÷£º`setInitializers(...)` + +Õâ¿é´úÂëÈçÏ£º + +``` +setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); + +``` + +ÕâÐдúÂë·ÖΪÁ½²¿·Ö: + +* »ñÈ¡?`ApplicationContextInitializer`£º`getSpringFactoriesInstances(ApplicationContextInitializer.class)` +* ÉèÖóõʼ»¯Æ÷£º`setInitializers(...)` + +ÎÒÃÇÏÈÀ´¿´¿´»ñÈ¡?`ApplicationContextInitializer`?µÄÁ÷³Ì£¬´úÂëÈçÏ£º + +``` +public class SpringApplication { + ... + + // type Ϊ ApplicationContextInitializer.class + private Collection getSpringFactoriesInstances(Class type) { + return getSpringFactoriesInstances(type, new Class[] {}); + } + + /** + * type Ϊ ApplicationContextInitializer.class + * parameterTypes Ϊ ew Class[] {} + * args Ϊ null + */ + private Collection getSpringFactoriesInstances(Class type, + Class[] parameterTypes, Object... args) { + ClassLoader classLoader = getClassLoader(); + // ´Ó META-INF/spring.factories ¼ÓÔØÄÚÈÝ + Set names = new LinkedHashSet<>( + SpringFactoriesLoader.loadFactoryNames(type, classLoader)); + // ʵÀý»¯£¬Ê¹Óõķ´Éä²Ù×÷ + List instances = createSpringFactoriesInstances( + type, parameterTypes, classLoader, args, names); + // ÅÅÐò£¬±È½ÏµÄÊÇ @Order ×¢½â£¬»òʵÏÖµÄ Orderd ½Ó¿Ú + AnnotationAwareOrderComparator.sort(instances); + return instances; + } + ... +} + +``` + +ÒÔÉÏ´úÂë±È½Ï¼òµ¥£¬ÏÈ´Ó?`META-INF/spring.factories`?»ñÈ¡ÄÚÈÝ£¬È»ºóʹÓ÷´Éä½øÐÐʵÀý»¯£¬½øÐÐÅÅÐòºóÔÙ·µ»Ø¡£ + +¹ØÓÚ?`SpringFactoriesLoader.loadFactoryNames(...)`£¬Ëü×îÖÕ»á´Ó?`META-INF/spring.factories`?¼ÓÔØÄÚÈÝ£¬`META-INF/spring.factories`?¿ÉÒÔÀí½âΪһ¸öÅäÖÃÎļþ£¬key Ϊ´«ÈëµÄ type£¬¸Ã·½·¨µÄÏêϸ·ÖÎö£¬¿ÉÒԲο¼?[springboot ×Ô¶¯×°ÅäÖ®¼ÓÔØ×Ô¶¯×°ÅäÀà](https://my.oschina.net/funcy/blog/4870868)¡£ + +×îÖÕ»áÓжàÉÙ¸ö?`ApplicationContextInitializer`?¼ÓÔØ½øÀ´ÄØ£¿Í¨¹ýµ÷ÊÔ£¬·¢ÏÖÒ»¹²ÓÐ 7 ¸ö£º + +![](https://oscimg.oschina.net/oscnet/up-53f764fefeb0c55fcfef6e34634805162f5.png) + +¶ÔÕâ 7 ¸ö?`ApplicationContextInitializer`£¬ËµÃ÷ÈçÏ£º + +* `ConfigurationWarningsApplicationContextInitializer`£º±¨¸æ IOC ÈÝÆ÷µÄһЩ³£¼ûµÄ´íÎóÅäÖà +* `ContextIdApplicationContextInitializer`£ºÉèÖà Spring Ó¦ÓÃÉÏÏÂÎÄµÄ ID +* `DelegatingApplicationContextInitializer`£º¼ÓÔØ?`application.properties`?ÖÐ?`context.initializer.classes`?ÅäÖõÄÀà +* `RSocketPortInfoApplicationContextInitializer`£º½«?`RSocketServer`?ʵ¼ÊʹÓõļàÌý¶Ë¿ÚдÈëµ½?`Environment`?»·¾³ÊôÐÔÖÐ +* `ServerPortInfoApplicationContextInitializer`£º½«ÄÚÖà servlet ÈÝÆ÷ʵ¼ÊʹÓõļàÌý¶Ë¿ÚдÈëµ½?`Environment`?»·¾³ÊôÐÔÖÐ +* `SharedMetadataReaderFactoryContextInitializer`£º´´½¨Ò»¸ö?`SpringBoot`?ºÍ?`ConfigurationClassPostProcessor`?¹²ÓõÄ?`CachingMetadataReaderFactory`?¶ÔÏó +* `ConditionEvaluationReportLoggingListener`£º½«?`ConditionEvaluationReport`?дÈëÈÕÖ¾ + +»ñÈ¡µ½?`ApplicationContextInitializer`£¬ÎÒÃÇÔÙÀ´¿´¿´?`setInitializers(...)`?·½·¨£º + +``` +public class SpringApplication { + ... + public void setInitializers( + Collection> initializers) { + this.initializers = new ArrayList<>(initializers); + } + ... +} + +``` + +ÕâÊÇÒ»¸ö±ê×¼µÄ?`setter`?·½·¨£¬Ëù×öµÄ¾ÍÖ»ÊÇÉèÖóÉÔ±±äÁ¿¡£ + +### 2.3 ÉèÖüàÌýÆ÷£º`setListeners(...)` + +ÉèÖüàÌýÆ÷µÄ´úÂëÈçÏ£º + +``` +setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); + +``` + +´ÓÐÎʽÉÏ¿´£¬Í¬?`Initializer`?Ò»Ñù£¬Ò²ÊÇÏÈ´Ó?`META-INF/spring.factories`?ÖмÓÔØ?`ApplicationListener`£¬È»ºóÌí¼Óµ½³ÉÔ±±äÁ¿ÖУ¬ÕâÀïÎÒÃÇÖ±½Ó¿´ÄÜ»ñÈ¡µ½ÄÄЩ?`listener`£º + +![](https://oscimg.oschina.net/oscnet/up-0440eb21c69a75686850a1b44eb9f1287c8.png) + +¿ÉÒÔ¿´µ½£¬Ò»¹²¿ÉÒÔ»ñÈ¡µ½ 11 ¸ö?`listener`£¬ÕâЩ?`listener`?µÄ×÷ÓÃÈçÏ£º + +* `ClearCachesApplicationListener`£ºÓ¦ÓÃÉÏÏÂÎļÓÔØÍê³Éºó¶Ô»º´æ×öÇå³ý¹¤×÷ +* `ParentContextCloserApplicationListener`£º¼àÌýË«Ç×Ó¦ÓÃÉÏÏÂÎĵĹرÕʼþ²¢Íù×Ô¼ºµÄ×ÓÓ¦ÓÃÉÏÏÂÎÄÖд«²¥ +* `CloudFoundryVcapEnvironmentPostProcessor`£º¶Ô?`CloudFoundry`?Ìṩ֧³Ö +* `FileEncodingApplicationListener`£º¼ì²âϵͳÎļþ±àÂëÓëÓ¦Óû·¾³±àÂëÊÇ·ñÒ»Ö£¬Èç¹ûϵͳÎļþ±àÂëºÍÓ¦Óû·¾³µÄ±àÂ벻ͬÔòÖÕÖ¹Ó¦ÓÃÆô¶¯ +* `AnsiOutputApplicationListener`£º¸ù¾Ý?`spring.output.ansi.enabled`?²ÎÊýÅäÖÃ?`AnsiOutput` +* `ConfigFileApplicationListener`£º´Ó³£¼ûµÄÄÇЩԼ¶¨µÄλÖöÁÈ¡ÅäÖÃÎļþ +* `DelegatingApplicationListener`£º¼àÌýµ½Ê¼þºóת·¢¸ø?`application.properties`?ÖÐÅäÖõÄ?`context.listener.classes`?µÄ¼àÌýÆ÷ +* `ClasspathLoggingApplicationListener`£º¶Ô»·¾³¾ÍÐ÷ʼþ?`ApplicationEnvironmentPreparedEvent`?ºÍÓ¦ÓÃʧ°Üʼþ?`ApplicationFailedEvent`?×ö³öÏìÓ¦ +* `LoggingApplicationListener`£ºÅäÖÃ?`LoggingSystem`£¬Ê¹ÓÃ?`logging.config`?»·¾³±äÁ¿Ö¸¶¨µÄÅäÖûòÕßȱʡÅäÖà +* `LiquibaseServiceLocatorApplicationListener`£ºÊ¹ÓÃÒ»¸ö¿ÉÒÔºÍ?`SpringBoot`?¿ÉÖ´ÐÐ jar °üÅäºÏ¹¤×÷µÄ°æ±¾Ìæ»»?`LiquibaseServiceLocator` +* `BackgroundPreinitializer`£ºÊ¹ÓÃÒ»¸öºǫ́Ï߳̾¡Ôç´¥·¢Ò»Ð©ºÄʱµÄ³õʼ»¯ÈÎÎñ + +ÔÙÀ´¿´¿´?`SpringApplication#setListeners`£º + +``` +public void setListeners(Collection> listeners) { + this.listeners = new ArrayList<>(listeners); +} + +``` + +ÕâÒ²ÊÇÒ»¸ö±ê×¼µÄ?`setter`?·½·¨¡£ + +### 2.4 ÍÆ¶ÏÖ÷Àࣺ`deduceMainApplicationClass()` + +ËùνÖ÷À࣬¾ÍÊǰüº¬?`main(String[])`£¬Ò²¾ÍÊǵ±Ç° spring Ó¦ÓÃµÄÆô¶¯À࣬`SpringApplication#deduceMainApplicationClass`?´úÂëÈçÏ£º + +``` +private Class deduceMainApplicationClass() { + try { + // »ñÈ¡µ÷ÓÃÕ» + StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); + // ±éÀúµ÷ÓÃÕ»£¬ÕÒ µ½main·½·¨ + for (StackTraceElement stackTraceElement : stackTrace) { + if ("main".equals(stackTraceElement.getMethodName())) { + return Class.forName(stackTraceElement.getClassName()); + } + } + } + catch (ClassNotFoundException ex) { + // Swallow and continue + } + return null; +} + +``` + +ÕâÀïÖ÷ÒªÊÇͨ¹ý?`new RuntimeException().getStackTrace()`?»ñÈ¡µ÷ÓÃÕ»£¬È»ºó±éÀú£¬µÃµ½°üº¬?`main`?·½·¨µÄÀ࣬µÃµ½µÄµ÷ÓÃÕ»ÈçÏ£º + +![](https://oscimg.oschina.net/oscnet/up-8c04487e6b05f583e7d45b83c293634f42a.png) + +¿ÉÒÔ¿´µ½£¬`main()`?¾Í°üº¬ÔÚµ÷ÓÃÕ»ÖÐÁË¡£ + +### 2.5 ×ܽá + +±¾ÎÄÖ÷ÒªÊǽéÉÜ?`SpringApplication`?µÄ´´½¨¹ý³Ì£¬ÎÒÃÇÖØµã·ÖÎöÁËÒÔϼ¸µã£º + +1. ÍÆ¶Ïµ±Ç° web Ó¦ÓÃÀàÐÍ£ºNONE, SERVLET,REACTIVE£» +2. ÉèÖóõʼ»¯Æ÷£º`ApplicationContextInitializer`£» +3. ÉèÖüàÌýÆ÷£º`ApplicationListener`£» +4. ÍÆ¶ÏÖ÷Àà¡£ + +![](https://oscimg.oschina.net/oscnet/up-e9a43f1c523c0f19d37e4741580ed32ca08.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4877610](https://my.oschina.net/funcy/blog/4877610)?£¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" new file mode 100644 index 0000000..f916074 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\207\206\345\244\207IOC\345\256\271\345\231\250.md" @@ -0,0 +1,535 @@ +ÉÏһƪÎÄÕÂ×ܽá springboot Æô¶¯Á÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-07a6b491fbe69b8dcbd41e59a8543f06671.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö½ÓÏÂÀ´µÄ²½Öè¡£ + +### 3.8 ´´½¨ ioc ÈÝÆ÷ + +´´½¨ ioc ÈÝÆ÷µÄ´úÂëÈçÏ£º + +``` +ConfigurableApplicationContext context = null; +.... +// ´´½¨applicationContext +context = createApplicationContext(); + +``` + +ÎÒÃǽøÈë `SpringApplication#createApplicationContext` ·½·¨£º + +``` +/** ĬÈ쵀 ApplicationContext */ +public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + + "annotation.AnnotationConfigApplicationContext"; + +/** servlet Ó¦ÓÃµÄµÄ ApplicationContext */ +public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; + +/** Reactive Ó¦ÓÃµÄ ApplicationContext */ +public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; + +protected ConfigurableApplicationContext createApplicationContext() { + Class contextClass = this.applicationContextClass; + if (contextClass == null) { + try { + // ¸ù¾ÝÓ¦ÓÃÀàÐÍÀ´´´½¨²»Í¬µÄÈÝÆ÷ + switch (this.webApplicationType) { + case SERVLET: + // ʹÓõÄÊÇ AnnotationConfigServletWebServerApplicationContext + contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); + break; + case REACTIVE: + contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); + break; + default: + // ĬÈÏʹÓõÄÊÇ AnnotationConfigApplicationContext + contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); + } + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException(...); + } + } + // ʹÓ÷´Éä½øÐÐʵÀý»¯ + return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); +} + +``` + +Õâ¸ö·½·¨Ö÷Òª¾ÍÊǸù¾ÝÓ¦ÓÃÀàÐÍÀ´´´½¨²»Í¬ `ApplicationContext`£¬Ê¹Ó÷´ÉäµÄ·½·¨½øÐÐʵÀý»¯£¬¸÷Ó¦ÓÃÀàÐͶÔÓ¦µÄ `ApplicationContext` ÈçÏ£º + +1. `servlet` Ó¦Óãº`AnnotationConfigServletWebServerApplicationContext` +2. `reactive` Ó¦Óãº`AnnotationConfigReactiveWebServerApplicationContext` +3. ÒÔÉ϶¼²»ÊÇ£º`AnnotationConfigApplicationContext` + +µ±Ç°Ó¦ÓõÄÀàÐÍÊÇ `servlet`£¬Òò´Ë´´½¨µÄ `ApplicationContext` ÊÇ `AnnotationConfigReactiveWebServerApplicationContext`£¬À´¿´¿´ËüµÄ¹¹Ôì·½·¨£º + +``` +public class AnnotationConfigServletWebServerApplicationContext + extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { + + // ÓÃÀ´´¦Àí BeanDefinition µÄ×¢²á + private final AnnotatedBeanDefinitionReader reader; + + // ÓÃÀ´´¦Àí°üµÄɨÃè + private final ClassPathBeanDefinitionScanner scanner; + + ... + + public AnnotationConfigServletWebServerApplicationContext() { + this.reader = new AnnotatedBeanDefinitionReader(this); + this.scanner = new ClassPathBeanDefinitionScanner(this); + } + + ... +} + +``` + +`AnnotationConfigServletWebServerApplicationContext` µÄ¹¹Ôì·½·¨»¹ÊDZȽϼòµ¥µÄ£¬Ö»ÊÇÉèÖÃÁËÁ½¸öÊôÐÔ£¬¾Í²»¶à˵ÁË¡£²»¹ý£¬ÎÒÃÇÒ²Òª°ÑÄ¿¹â·ÅÔ¶Ò»µã£¬¿´¿´Æä¸¸ÀàµÄ¹¹Ôì·½·¨£¬×îÖÕÔÚ `GenericApplicationContext` µÄ¹¹Ôì·½·¨ÖÐÕÒµ½Õâôһ¾ä£º + +``` +public GenericApplicationContext() { + this.beanFactory = new DefaultListableBeanFactory(); +} + +``` + +ÕâÐдúÂë´´½¨ÁË `DefaultListableBeanFactory` ²¢½«Æä¸³Öµ¸øÁË `beanFactory`£¬Õâ±íÃ÷ÎÒÃÇÔÚ `ApplicationContext` ʹÓÃµÄ `beanFactory` ¾ÍÊÇ `DefaultListableBeanFactory`¡£ + +### 3.9 ×¼±¸ ioc ÈÝÆ÷ + +´´½¨Íê ioc ÈÝÆ÷ºó£¬½ÓמÍÊǶÔÈÝÆ÷½øÐÐһЩ׼±¸²Ù×÷£¬´úÂëÈçÏ£º + +``` +public class SpringApplication { + + ... + + private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + // ½«´´½¨ºÃµÄÓ¦Óû·¾³ÉèÖõ½IOCÈÝÆ÷ÖÐ + context.setEnvironment(environment); + // ÉèÖÃÈÝÆ÷µÄһЩ²ÎÊý + postProcessApplicationContext(context); + // Ó¦ÓÃInitializer½øÐгõʼ»¯²Ù×÷ + applyInitializers(context); + // ¼àÌýÆ÷£ºSpringApplicationRunListenersµÄcontextPrepared·½·¨ + // £¨ÔÚ´´½¨ºÍ×¼±¸ApplicationContextÖ®ºó£¬µ«ÔÚ¼ÓÔØÖ®Ç°£© + listeners.contextPrepared(context); + // ´òÓ¡¸öÈÕÖ¾ + if (this.logStartupInfo) { + logStartupInfo(context.getParent() == null); + logStartupProfileInfo(context); + } + // »ñÈ¡beanFactory + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + // ½«ÔËÐвÎÊý×÷Ϊbean×¢²áµ½beanFactoryÖÐ + beanFactory.registerSingleton("springApplicationArguments", applicationArguments); + // ½« banner ×÷Ϊbean×¢²áµ½beanFactoryÖÐ + if (printedBanner != null) { + beanFactory.registerSingleton("springBootBanner", printedBanner); + } + if (beanFactory instanceof DefaultListableBeanFactory) { + // ÊÇ·ñÔÊÐíbeanµÄÐÅÏ¢±»¸²¸Ç + ((DefaultListableBeanFactory) beanFactory) + .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); + } + // ´¦ÀíÀÁ¼ÓÔØ + if (this.lazyInitialization) { + context.addBeanFactoryPostProcessor( + new LazyInitializationBeanFactoryPostProcessor()); + } + // »ñÈ¡ËùÓÐ×ÊÔ´ + Set sources = getAllSources(); + Assert.notEmpty(sources, "Sources must not be empty"); + // ¼ÓÔØ class + load(context, sources.toArray(new Object[0])); + // ·¢²¼Ê¼þ + listeners.contextLoaded(context); + } +} + +``` + +×¼±¸¹¤×÷µÄ²½Ö軹ÊǺÜÇåÎúµÄ£¬ÖØÒªÄÚÈݶ¼ÒÑÔÚ´úÂëÖнøÐÐÁË×¢ÊÍ£¬ÕâÀï¶ÔһЩ²Ù×÷ÎÒÃÇÉÔ΢չ¿ªÏ¡£ + +#### 1\. ÉèÖà `Environment` + +¸Ã²Ù×÷µÄ´úÂëΪ + +``` +private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + // ½«´´½¨ºÃµÄÓ¦Óû·¾³ÉèÖõ½IOCÈÝÆ÷ÖÐ + context.setEnvironment(environment); + ... +} + +``` + +Õâ¸ö `environment` ¾ÍÊÇÇ°Ãæ´´½¨µÄ `environment`£¬ioc ÈÝÆ÷Ò²ÊÇʹÓÃÕâ¸ö£¬ÎÒÃÇÀ´¿´¿´ÉèÖòÙ×÷£º + +``` +public class AnnotationConfigServletWebServerApplicationContext extends ... { + + ... + + @Override + public void setEnvironment(ConfigurableEnvironment environment) { + // µ÷Óø¸ÀàµÄ·½·¨½øÐÐÉèÖà + super.setEnvironment(environment); + // Ò²½«environmentÉèÖõ½ÕâÁ½¸öÊôÐÔÖÐ + this.reader.setEnvironment(environment); + this.scanner.setEnvironment(environment); + } + + .... +} + +``` + +#### 2\. ´¦Àí ioc µÄ²¿·ÖÊôÐÔ + +ÎÒÃÇÀ´¿´¿´ `postProcessApplicationContext(context);` Ëù×öµÄ¹¤×÷£º + +``` +public class SpringApplication { + + ... + + protected void postProcessApplicationContext(ConfigurableApplicationContext context) { + // ÉèÖà beanNameGenerator£¬ÓÃÀ´Éú³É bean µÄÃû³Æ£¬ÕâÀï´«ÈëµÄÊÇnull + if (this.beanNameGenerator != null) { + context.getBeanFactory().registerSingleton( + AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, + this.beanNameGenerator); + } + // ÉèÖà resourceLoaderµÄÏà¹Ø²ÎÊý£¬ÓÉÓÚΪnull£¬ifÀïµÄ´úÂë²»»áÖ´ÐÐ + if (this.resourceLoader != null) { + if (context instanceof GenericApplicationContext) { + ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); + } + if (context instanceof DefaultResourceLoader) { + ((DefaultResourceLoader) context).setClassLoader( + this.resourceLoader.getClassLoader()); + } + } + // ÕâÀï»áÖ´ÐÐ + if (this.addConversionService) { + // ÉèÖÃÀàÐÍת»»Æ÷£¬Èç°ÑStringתNumberµÈ + context.getBeanFactory().setConversionService( + ApplicationConversionService.getSharedInstance()); + } + } + + ... + +} + +``` + +Õⲿ·Ö¾ÍÊÇÉèÖà `ApplicationContext` µÄ¼¸¸öÊôÐÔ£¬ÔÚÎÒÃÇµÄ demo ÖУ¬`beanNameGenerator` Óë `resourceLoader` ¶¼ÊÇ `null`£¬Òò´ËÕâÁ½¿é¶¼²»»áÔËÐУ¬ÄÜÔËÐеľÍÖ»ÓÐÕâ¿é´úÂ룺 + +``` + context.getBeanFactory().setConversionService( + ApplicationConversionService.getSharedInstance()); + +``` + +`ConversionService` ÔÚÇ°ÃæÒ²Ìáµ½¹ý£¬ËüÖ÷ÒªÊÇÓÃÀ´½øÐвÎÊýÀàÐÍת»»µÄ¡£ + +#### 3\. Ó¦Óóõʼ»¯Æ÷£º`applyInitializers(context)` + +`SpringApplication#applyInitializers` ·½·¨ÈçÏ£º + +``` +public class SpringApplication { + + ... + + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected void applyInitializers(ConfigurableApplicationContext context) { + // getInitializers()£º»ñÈ¡ËùÓеijõʼ»¯Æ÷ + for (ApplicationContextInitializer initializer : getInitializers()) { + Class requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), + ApplicationContextInitializer.class); + Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); + // ÖðÒ»µ÷ÓÃinitializerµÄinitialize(...)·½·¨ + initializer.initialize(context); + } + } + +} + +``` + +Õâ¿é²Ù×÷»¹ÊǺÜÇåÎúµÄ£¬¾ÍÊÇ»ñÈ¡ËùÓÐµÄ `Initializer`£¬È»ºó±éÀú£¬Öð¸öµ÷ÓÃÆä `initialize(...)` ·½·¨¡£ + +ÕâÀﻹÐèÒªÌáÏ£¬`getInitializers()` ÊÇÔõô»ñÈ¡ `Initializer` µÄÄØ£¿Ïà¹Ø´úÂëÈçÏ£º + +``` +public class SpringApplication { + + private List> initializers; + + ... + + // ÔÚ¹¹Ôì·½·¨ÖÐÉèÖÃµÄ initializers + public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { + ... + + // ÉèÖóõʼ»¯Æ÷£¬getSpringFactoriesInstances£º´Ó META-INF/spring.factories ÖлñÈ¡ÅäÖà + setInitializers((Collection) getSpringFactoriesInstances( + ApplicationContextInitializer.class)); + + ... + } + + // »ñÈ¡ Initializer µÄ²Ù×÷£¬´ÓÊôÐÔÖлñÈ¡ + public Set> getInitializers() { + return asUnmodifiableOrderedSet(this.initializers); + } + + ... + +``` + +¿´ÁË´úÂë¾ÍÃ÷°×ÁË£¬ÔÚÇ°Ãæ·ÖÎö `SpringApplication` µÄ¹¹Ôì·½·¨Ê±£¬ÎÒÃÇÌáµ½ springboot »á´Ó `META-INF/spring.factories` »ñÈ¡ÅäÖÃµÄ `Initializer`£¬½«ÆäÉèÖõ½ `initializers` ÊôÐÔ£¬¶øÕâÀï¾ÍÊÇʹÓà `Initializer` µÄµØ·½ÁË¡£ + +#### 4\. »ñÈ¡ËùÓÐ×ÊÔ´ + +´úÂëÈçÏ£º + +``` +private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + ... + // »ñÈ¡ËùÓÐ×ÊÔ´ + Set sources = getAllSources(); + ... +} + +``` + +½øÈë `getAllSources()` ·½·¨£º + +``` +// ½« primarySources ·ÅÈësetÖУ¬È»ºó½«setת»¯Îª²»¿É±äµÄset£¬·µ»Ø +public Set getAllSources() { + Set allSources = new LinkedHashSet<>(); + if (!CollectionUtils.isEmpty(this.primarySources)) { + allSources.addAll(this.primarySources); + } + // sources Ϊ¿Õ£¬if²»Ö´ÐÐ + if (!CollectionUtils.isEmpty(this.sources)) { + allSources.addAll(this.sources); + } + return Collections.unmodifiableSet(allSources); +} + +``` + +Õâ¸ö·½·¨ºÜ¼òµ¥£¬¾ÍÒ»¸ö»°£º½« `primarySources` ·ÅÈë `set` ÖУ¬È»ºó½« `set` ת»¯Îª²»¿É±äµÄ `set`£¬·µ»Ø¡£ + +ÄÇÕâ¸ö `primarySources` ÊÇɶĨ£¿Õâ¾ÍÓÖÒª»Øµ½ `SpringApplication` µÄ¹¹Ôì·½·¨ÁË£º + +``` +public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { + ... + // ÕâÀïÉèÖÃÁË primarySources + Assert.notNull(primarySources, "PrimarySources must not be null"); + this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); + ... +} + +``` + +È»ºó¼ÌÐøÍùÉÏ£¬×îºó·¢ÏÖ `primarySources` ÊÇÎÒÃÇÔÚ `main` ·½·¨Öд«ÈëµÄ£º + +``` +@SpringBootApplication +public class Demo01Application { + + public static void main(String[] args) { + // Demo01Application.class ¾ÍÊÇ primarySources + SpringApplication.run(Demo01Application.class, args); + } + +} + +``` + +ÎÒÃÇ´«ÈëµÄ `Demo01Application.class` ¾ÍÊÇ `primarySources`£¡ + +Òò´Ë£¬`getAllSources()` ·µ»ØÁËÒ»¸ö set£¬set ÖÐÖ»ÓÐÒ»¸öÔªËØ£º`Demo01Application.class`£¬ÎÒÃÇÒ²¿ÉÒÔͨ¹ýµ÷ÊÔÀ´ÑéÖ¤£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-29dd8056dc457c56e6fe41020516d02fe80.png) + +#### 5\. ¼ÓÔØ×ÊÔ´ + +Õâ¸ö²Ù×÷µÄ´úÂëÈçÏ£º + +``` +private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + ... + // ¼ÓÔØ class + load(context, sources.toArray(new Object[0])); + ... +} + +``` + +½øÈë `SpringApplication#load` ·½·¨£º + +``` +public class SpringApplication { + ... + + protected void load(ApplicationContext context, Object[] sources) { + // ´´½¨Ò»¸öBeanDefinitionµÄ¼ÓÔØÆ÷ + BeanDefinitionLoader loader = createBeanDefinitionLoader( + getBeanDefinitionRegistry(context), sources); + // µ±Ç° beanNameGenerator Ϊ null£¬²»µ÷Óà + if (this.beanNameGenerator != null) { + loader.setBeanNameGenerator(this.beanNameGenerator); + } + // µ±Ç° resourceLoader Ϊ null£¬²»µ÷Óà + if (this.resourceLoader != null) { + loader.setResourceLoader(this.resourceLoader); + } + // µ±Ç° environment Ϊ null£¬²»µ÷Óà + // Ç°Ãæ´´½¨µÄenvironment²¢Ã»Óи³Öµµ½³ÉÔ±±äÁ¿ + if (this.environment != null) { + loader.setEnvironment(this.environment); + } + loader.load(); + } + ... +} + +``` + +Õâ¸ö·½·¨ÏÈÊÇ´´½¨ÁËÒ»¸ö `BeanDefinitionLoader` ʵÀý£¬ÉÏÃæ»ñÈ¡µÄ `source` »áÔÚ `BeanDefinitionLoader` µÄ¹¹Ôì·½·¨Öд«È뵽ʵÀýÖУ¬È»ºó¶Ô¸Ã `loader` ½øÐÐһϵÁеÄÊôÐÔÉèÖã¬×îºóÔÙµ÷ÓÃÆä `load()` ·½·¨¡£ÕâÀïÐèҪ˵Ã÷µÄÊÇ£¬ËäÈ»Ç°Ãæ´´½¨ÁË `environment`£¬µ« `this.environment` »¹ÊÇΪ `null`£¬Ô­ÒòÊÇÇ°Ãæ´´½¨µÄ `environment` ²¢Ã»Óи³Öµ¸ø `this.environment`¡£ + +ÎÒÃǼÌÐø£¬½øÈë `BeanDefinitionLoader#load()`£º + +``` +class BeanDefinitionLoader { + + ... + + int load() { + int count = 0; + // ÕâÀïµÄ sources ÖоÍÖ»ÓÐÒ»¸öÔªËØ£ºDemo01Application.class + for (Object source : this.sources) { + count += load(source); + } + return count; + } + + // ´¦Àí¼ÓÔØ²Ù×÷ + private int load(Object source) { + Assert.notNull(source, "Source must not be null"); + // Class ÀàÐÍÔÚÕâÀï¼ÓÔØ + if (source instanceof Class) { + // source Ϊ Demo01Application.class£¬ÎÒÃÇÖ÷Òª¹Ø×¢Õâ¸ö·½·¨ + return load((Class) source); + } + if (source instanceof Resource) { + return load((Resource) source); + } + if (source instanceof Package) { + return load((Package) source); + } + if (source instanceof CharSequence) { + return load((CharSequence) source); + } + throw new IllegalArgumentException("Invalid source type " + source.getClass()); + } + + // Class ÀàÐ͵ļÓÔØ²Ù×÷ + private int load(Class source) { + // ´¦Àí grouovy ÓïÑԵ쬲»ÓÃ¹Ü + if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { + GroovyBeanDefinitionSource loader + = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); + load(loader); + } + // ÊÇ·ñÓÐ @Component ×¢½â + if (isComponent(source)) { + // ÔÚÕâÀïÉú³É BeanDefinition ¶ÔÏ󣬲¢×¢²áµ½springÖÐ + this.annotatedReader.register(source); + return 1; + } + return 0; + } + ... + +``` + +ÔÚ´´½¨ `BeanDefinitionLoader` ʵÀýʱ£¬Í¨¹ýÆä¹¹Ôì·½·¨½« `sources` ´«ÈëÁË `BeanDefinitionLoader` µÄʵÀýÖУ¬`sources` ÖоÍÖ»ÓÐÒ»¸öÔªËØ£º`Demo01Application.class`£¬Òò´ËÎÒÃÇÖ»Ðè¹Ø×¢ `Class` ÀàÐÍ×ÊÔ´µÄ¼ÓÔØ¾Í¿ÉÒÔÁË£¬×îÖÕµ½ÁË `BeanDefinitionLoader#load(java.lang.Class)` ·½·¨£¬¸Ã·½·¨Ëù×öµÄ²Ù×÷Ϊ£ºÅжϴ«ÈëµÄ `Class` ÊÇ·ñÓÐ `@Component`£¬Èç¹ûÓоͽ«Æä×¢²áµ½ ioc ÈÝÆ÷ÖС£ + +ÄÇô `Demo01Application.class` ÊÇ·ñÓÐ `@Component` ×¢½âÄØ£¿Óе쬲»¹ýÒþ²ØµÃ±È½ÏÉÆä×¢½â²ã¼¶ÈçÏ£º + +``` +@SpringBootApplication +public class Demo01Application { + ... +} + +``` + +½øÈë `@SpringBootApplication`£º + +``` +... +@SpringBootConfiguration +... +public @interface SpringBootApplication { + ... +} + +``` + +¼ÌÐø½øÈë `@SpringBootApplication`£º + +``` +... +@Configuration +public @interface SpringBootConfiguration { + ... +} + +``` + +¼ÌÐø½øÈë `@Configuration`£º + +``` +... +@Component +public @interface Configuration { + ... +} + +``` + +¾­¹ýÖØÖØ²¨ÕÛ£¬×îÖÕÔÚ `@Configuration` ×¢½âÉÏÕÒµ½ÁË `@Component`. + +¹ØÓÚ `this.annotatedReader.register(source)` ×¢²áµÄ¾ßÌå²Ù×÷£¬Õâ¿éÔÚ [spring ·ÖÎö](https://my.oschina.net/funcy/blog/4527454)ÖУ¬ÒѾ­·ÖÎö¹ýÁË£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£ + +ÏÞÓÚÆª·ù£¬±¾Îľ͵½ÕâÀïÁË£¬ÏÂÆª¼ÌÐø·ÖÎöʣϵÄÁ÷³Ì¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fba95ddd84c68cdd1757f060ab131478a3c.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4884127](https://my.oschina.net/funcy/blog/4884127) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" new file mode 100644 index 0000000..4fceb03 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232\345\207\206\345\244\207\350\277\220\350\241\214\347\216\257\345\242\203.md" @@ -0,0 +1,508 @@ +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö£¬±¾ÎĽ«·ÖÎö`SpringApplication#run(String...)`·½·¨¡£ + +## 3.`springboot`µÄÔËÐУº`SpringApplication#run(String...)` + +·½·¨ÄÚÈÝÈçÏ£º + +``` +public ConfigurableApplicationContext run(String... args) { + // 1\. ´´½¨ StopWatch ʵÀý£¬Æäʵ¾ÍÊǸö¼ÆÊ±Æ÷£¬ÓÃÀ´Í³¼ÆspringbootÆô¶¯ºÄʱ + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + // ×¼±¸¿ÕµÄApplicationContext£¬ÒÔ¼°Ò»×éÒì³£±¨¸æÆ÷ + ConfigurableApplicationContext context = null; + Collection exceptionReporters = new ArrayList<>(); + // 2\. ÅäÖÃÒ»¸öϵͳÊôÐÔ£ºjava.awt.headless£¬java.awt.headlessģʽÊÇϵͳµÄÒ»ÖÖÅäÖÃģʽ£¬ + // ÔÚϵͳ¿ÉÄÜȱÉÙÏÔʾÉ豸¡¢¼üÅÌ»òÊó±êÕâЩÍâÉèµÄÇé¿öÏ¿ÉÒÔʹÓøÃģʽ + configureHeadlessProperty(); + // 3\. »ñÈ¡¼àÌýÆ÷£¬Ò²ÊÇ´Ó META-INF/spring.factories ÖлñÈ¡ + SpringApplicationRunListeners listeners = getRunListeners(args); + // starting()£ºÊ×´ÎÆô¶¯run·½·¨Ê±Á¢¼´µ÷Ó᣿ÉÓÃÓڷdz£ÔçÆÚµÄ³õʼ»¯£¨×¼±¸ÔËÐÐʱ»·¾³Ö®Ç°£©¡£ + // 4\. ·¢²¼Ê¼þ + listeners.starting(); + try { + // ·â×°´«ÈëµÄ²ÎÊý + ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); + // 5\. ´¦Àí»·¾³²ÎÊý + ConfigurableEnvironment environment + = prepareEnvironment(listeners, applicationArguments); + // 6\. Èç¹ûÓÐÅäÖà spring.beaninfo.ignore£¬Ôò½«¸ÃÅäÖÃÉèÖýøÏµÍ³²ÎÊý + configureIgnoreBeanInfo(environment); + // 7\. Êä³öbanner + Banner printedBanner = printBanner(environment); + // 8\. ´´½¨applicationContext + context = createApplicationContext(); + // Æô¶¯´íÎ󱨸æµÄ×Ô¶¨Ò廨µ÷½Ó¿Ú + exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, + new Class[] { ConfigurableApplicationContext.class }, context); + // 9\. ×¼±¸ÉÏÏÂÎÄ£¬ÉèÖÃÁËһϵÁеÄÊôÐÔÖµ + prepareContext(context, environment, listeners, applicationArguments, printedBanner); + // 10\. µ÷Óà AbstractApplicationContext.refresh£¬Æô¶¯springÈÝÆ÷ + refreshContext(context); + // 11\. ˢкóµÄ´¦Àí + afterRefresh(context, applicationArguments); + stopWatch.stop(); + if (this.logStartupInfo) { + new StartupInfoLogger(this.mainApplicationClass) + .logStarted(getApplicationLog(), stopWatch); + } + // 12\. ·¢²¼Ê¼þ + listeners.started(context); + // 13\. µ÷Óà runner£¬ÊµÏÖÁË ApplicationRunner»òCommandLineRunner µÄ½Ó¿Ú + callRunners(context, applicationArguments); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, listeners); + throw new IllegalStateException(ex); + } + try { + // 14\. ·¢²¼Ê¼þ + listeners.running(context); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, null); + throw new IllegalStateException(ex); + } + return context; +} + +``` + +Õû¸öÆô¶¯Á÷³ÌÈçÏ£º + +![](https://oscimg.oschina.net/oscnet/up-07a6b491fbe69b8dcbd41e59a8543f06671.png) + +½ÓÏÂÀ´ÎÒÃÇÀ´Öصã·ÖÎöÕâ 13 ¸öÆô¶¯Á÷³Ì¡£ + +### 3.1`stopWatch`£º´´½¨¼ÆÊ±Æ÷²¢Æô¶¯ + +Ò»¿ªÊ¼£¬springboot ¾Í´´½¨ÁË`stopWatch`ʵÀý£¬È»ºóµ÷ÓÃ`StopWatch#start()`Æô¶¯¼ÆÊ±¹¦ÄÜ£¬¹ØÓÚÕâ¸öÀàûɶºÃ˵µÄ£¬Ëü¾ÍÊǸö¼ÆÊ±Æ÷£¬ÕâÀïÊÇÓÃÀ´¼ÆËã springboot Æô¶¯ºÄʱ£¬ÒÔÏÂÈÕÖ¾ÖеÄʱ¼ä¾ÍÊÇÓÉÕâ¸ö¼ÆÊ±Æ÷µÃµ½µÄ£º + +![](https://oscimg.oschina.net/oscnet/up-70a9e95e6c1208288334341bdb54bd59c17.png) + +### 3.2 ÉèÖÃ`java.awt.headless`ÊôÐÔÖµ + +`SpringApplication#configureHeadlessProperty`·½·¨µÄÏà¹Ø´úÂëÈçÏ£º + +``` +public class SpringApplication { + + private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; + + ... + + private boolean headless = true; + + public void setHeadless(boolean headless) { + this.headless = headless; + } + ... + + private void configureHeadlessProperty() { + // ½« java.awt.headless µÄÖµÅäÖõ½ÏµÍ³±äÁ¿ + System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, + System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, + Boolean.toString(this.headless))); + } + ... +} + +``` + +Õâ¸ö·½·¨½ö½öÊǽ«`java.awt.headless`µÄÖµÉèÖõ½ÁËϵͳ±äÁ¿£¬ÉèÖóÉ`true`£¬±íʾÆô¶¯`java.awt.headless`ģʽ¡£ÄÇôËüÊǸöÉ¶Ä£Ê½ÄØ£¿¼òµ¥À´Ëµ£¬¸Ãģʽ¾ÍÊÇϵͳȱÉÙÁËÏÔʾÉ豸¡¢¼üÅÌ»òÊó±êµÈÇé¿öµÄģʽ£¬·þÎñÆ÷Ò»°ã¶¼ÊÇÔÚÕâÖÖÇé¿öϹ¤×÷µÄ¡£ + +### 3.3 »ñÈ¡ÔËÐмàÌýÆ÷ + +¼ÌÐø£¬ÕâÒ»²½ÊÇ»ñÈ¡ÔËÐмàÌýÆ÷£¬¿ÉÒÔ¼àÌýÔËÐÐÆÚ¼äµÄһЩ״̬£¬¿´´úÂ룺 + +``` +// »ñÈ¡¼àÌýÆ÷£¬Ò²ÊÇ´Ó META-INF/spring.factories ÖлñÈ¡ +SpringApplicationRunListeners listeners = getRunListeners(args); + +``` + +½øÈë`SpringApplication#getRunListeners`£º + +``` +public class SpringApplication { + ... + + private SpringApplicationRunListeners getRunListeners(String[] args) { + Class[] types = new Class[] { SpringApplication.class, String[].class }; + return new SpringApplicationRunListeners(logger, + // ÒÀÈ»ÊÇ´ÓMETA-INF/spring.factories ÖлñÈ¡£¬key ÊÇ SpringApplicationRunListener + getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); + } + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬`SpringApplicationRunListener`ÒÀÈ»ÊÇ´Ó`META-INF/spring.factories`ÖлñÈ¡£¬ÄÇ`SpringApplicationRunListener`ÊǸöɶĨ£¿ÎÒÃÇÀ´¿´´úÂ룺 + +``` +public interface SpringApplicationRunListener { + + /** + * Ê×´ÎÆô¶¯run·½·¨Ê±Á¢¼´µ÷Ó᣿ÉÓÃÓڷdz£ÔçÆÚµÄ³õʼ»¯¡£ + */ + default void starting() { + } + + /** + * ×¼±¸ºÃ»·¾³£¨Environment¹¹½¨Íê³É£©£¬µ«ÔÚ´´½¨ApplicationContext֮ǰµ÷Óᣠ+ */ + default void environmentPrepared(ConfigurableEnvironment environment) { + } + + /** + * ÔÚ´´½¨ºÍ¹¹½¨ApplicationContextÖ®ºó£¬µ«ÔÚ¼ÓÔØÖ®Ç°µ÷Óᣠ+ */ + default void contextPrepared(ConfigurableApplicationContext context) { + } + + /** + * ApplicationContextÒѼÓÔØµ«ÔÚË¢ÐÂ֮ǰµ÷Óᣠ+ */ + default void contextLoaded(ConfigurableApplicationContext context) { + } + + /** + * ApplicationContextÒÑˢУ¬Ó¦ÓóÌÐòÒÑÆô¶¯£¬ + * µ«ÉÐδµ÷ÓÃCommandLineRunnersºÍApplicationRunners¡£ + */ + default void started(ConfigurableApplicationContext context) { + } + + /** + * ÔÚÔËÐз½·¨³¹µ×Íê³É֮ǰÁ¢¼´µ÷Ó㬠+ * Ë¢ÐÂApplicationContext²¢µ÷ÓÃËùÓÐCommandLineRunnersºÍApplicationRunner¡£ + */ + default void running(ConfigurableApplicationContext context) { + } + + /** + * ÔÚÔËÐÐÓ¦ÓóÌÐòʱʧ°Üʱµ÷Óᣠ+ */ + default void failed(ConfigurableApplicationContext context, Throwable exception) { + } +} + +``` + +`SpringApplicationRunListener`ÊÇÒ»¸ö½Ó¿Ú£¬¶¨ÒåÁËһϵÁеķ½·¨À´¼àÌý springboot µÄÆô¶¯¹ý³Ì£¬·½·¨µÄ˵Ã÷ÒѾ­ÔÚÎĵµÖÐÓÐÏêϸ¶¨Ò壬Èç¹ûÎÒÃÇÐèÒªÔÚ springboot µÄÆô¶¯ÖеÄij¸ö¹ý³Ì×öһЩÊÂÇ飬¾Í¿ÉÒÔʵÏÖ`SpringApplicationRunListener`È»ºóÖØÐ´¶ÔÓ¦µÄ·½·¨¡£ + +ͨ¹ýµ÷ÊÔ£¬·¢ÏÖ springboot »ñµÃµÄÔËÐмàÌýÆ÷ÈçÏ£º + +![](https://oscimg.oschina.net/oscnet/up-3ed62d827b3bf1989af74f9c4db1fc0b9ce.png) + +### 3.4 ÔËÐмàÌýÆ÷·½·¨£º`listeners.starting()` + +»Øµ½`SpringApplication#run(java.lang.String...)`£¬»ñÈ¡µ½ÔËÐмàÌýÆ÷ºó£¬»áÁ¢¼´µ÷ÓÃ`starting()`·½·¨À´·¢²¼Æô¶¯Ê¼þ£º + +``` +// »ñÈ¡¼àÌýÆ÷ +SpringApplicationRunListeners listeners = getRunListeners(args); +// starting()£ºÊ×´ÎÆô¶¯run·½·¨Ê±Á¢¼´µ÷Ó᣿ÉÓÃÓڷdz£ÔçÆÚµÄ³õʼ»¯£¨×¼±¸ÔËÐÐʱ»·¾³Ö®Ç°£©¡£ +listeners.starting(); + +``` + +½øÈë`SpringApplicationRunListeners#starting`·½·¨£º + +``` +void starting() { + for (SpringApplicationRunListener listener : this.listeners) { + listener.starting(); + } +} + +``` + +¿ÉÒÔ¿´µ½£¬ËùνµÄ·¢²¼Ê¼þ£¬¾ÍÊDZéÀúËùÓеļàÌýÆ÷£¬ÖðÒ»µ÷ÓÃÆä`starting()`°ÕÁË£¬`this.listeners`¾ÍÊÇÉÏÃæ»ñÈ¡µ½µÄËùÓеÄÔËÐмàÌýÆ÷¡£²»½öÈç´Ë£¬`SpringApplicationRunListener`µÄÆäËû·½·¨Èç`environmentPrepared(...)`¡¢`contextPrepared(...)`µÈ¶¼ÊÇÕâÑùµÄµ÷ÓÃÌ×·£¬ºóÃæ¿´µ½Á˾Ͳ»Öظ´·ÖÎöÁË¡£ + +### 3.5 ×¼±¸ÔËÐÐʱ»·¾³ + +½ÓÏÂÎÒÃÇÀ´¿´¿´»·¾³²ÎÊýµÄ´¦Àí£¬´úÂëÈçÏ£º + +``` +// ·â×°´«ÈëµÄ²ÎÊý +ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); +// ´¦Àí»·¾³²ÎÊý +ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); + +``` + +½øÈë`SpringApplication#prepareEnvironment`£º + +``` +private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments) { + // »ñÈ¡»·¾³£¬Èç¹û²»´æÔÚÔò´´½¨ + ConfigurableEnvironment environment = getOrCreateEnvironment(); + // ÅäÖÃÔËÐÐʱ»·¾³ + configureEnvironment(environment, applicationArguments.getSourceArgs()); + ConfigurationPropertySources.attach(environment); + // SpringApplicationRunListener µÄ environmentPrepared ·½·¨ + // £¨Environment¹¹½¨Íê³É£¬µ«ÔÚ´´½¨ApplicationContext֮ǰ£© + listeners.environmentPrepared(environment); + // »·¾³ÓëÓ¦ÓÃ°ó¶¨ + bindToSpringApplication(environment); + if (!this.isCustomEnvironment) { + environment = new EnvironmentConverter(getClassLoader()) + .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); + } + ConfigurationPropertySources.attach(environment); + return environment; +} + +``` + +¿ÉÒÔ¿´µ½¸÷ÖÖ»·¾³×¼±¸£¬ÔÛÃÇÌôÖØµã½²¡£ + +#### 1\. »ñÈ¡»ò´´½¨`Environment` + +Ö±½Ó½øÈë`SpringApplication#getOrCreateEnvironment`£º + +``` +private ConfigurableEnvironment getOrCreateEnvironment() { + if (this.environment != null) { + return this.environment; + } + switch (this.webApplicationType) { + case SERVLET: + return new StandardServletEnvironment(); + case REACTIVE: + return new StandardReactiveWebEnvironment(); + default: + return new StandardEnvironment(); + } +} + +``` + +´Ó´úÂëÀ´¿´£¬Õâ¸ö·½·¨ÊǸù¾ÝÓ¦ÓÃÀàÐÍÀ´´´½¨¶ÔÓ¦µÄ`Environment`ʵÀý£¬µ±Ç°Ó¦ÓÃÀàÐÍÊÇ`SERVLET`£¬ÔÛÃÇÖ±½Ó¿´`StandardServletEnvironment`ÊÇÈçºÎ´´½¨µÄ¡£ + +ÎÒÃÇÖªµÀ£¬ÔÚ java ÖУ¬´´½¨Ààʱ£¬»áÏȵ÷Óø¸ÀàµÄ¹¹Ôì·½·¨£¬ÕâÀïÎÒÃÇÖ±½Ó½øÈë`AbstractEnvironment`¹¹Ôì·½·¨£º + +``` + +public abstract class AbstractEnvironment implements ConfigurableEnvironment { + + ... + + public AbstractEnvironment() { + customizePropertySources(this.propertySources); + } + + ... +} + +``` + +·¢ÏÖÔÚ`AbstractEnvironment`µÄ¹¹Ôì·½·¨ÖУ¬µ÷ÓÃÁË`customizePropertySources()`£¬¶øÕâ¸ö·½·¨ÔÚ`StandardServletEnvironment`ʵÏÖ£º + +``` +public class StandardServletEnvironment extends StandardEnvironment + implements ConfigurableWebEnvironment { + public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"; + public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"; + public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties"; + + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + // Ìí¼Ó servletConfigInitParams + propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); + // Ìí¼Ó servletContextInitParams + propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { + propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); + } + // µ÷Óø¸ÀàµÄ·½·¨ + super.customizePropertySources(propertySources); + } + + @Override + public void initPropertySources(@Nullable ServletContext servletContext, + @Nullable ServletConfig servletConfig) { + // Ìæ»»ÉÏÃæÉèÖÃµÄ servletContextInitParams Ϊ servletContext + // Ìæ»»ÉÏÃæÉèÖÃµÄ servletConfigInitParams Ϊ servletConfig + WebApplicationContextUtils.initServletPropertySources( + getPropertySources(), servletContext, servletConfig); + } + +} + +``` + +¿ÉÒÔ¿´µ½£¬`StandardServletEnvironment`µÄ`customizePropertySources()`·½·¨Ö»ÊÇÌí¼ÓÁ˼¸¸ö servlet Ïà¹ØµÄ²ÎÊý£¬È»ºó¾ÍÈ¥µ÷Óø¸ÀàµÄ¹¹Ôì·½·¨ÁË£¬ÎÒÃǼÌÐø½øÈë`StandardEnvironment`£º + +Õâ¸öÀàËÆºõ²¢Ã»ÓÐ×öʲô£¬ÎÒÃǼÌÐø×·×Ù£¬¿´Ëü¸¸ÀàµÄ¹¹Ôì·½·¨£º + +``` +public class StandardEnvironment extends AbstractEnvironment { + + /** ϵͳ»·¾³ */ + public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"; + + /** ϵͳÊôÐÔ */ + public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"; + + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + // »ñȡϵͳÊôÐÔ£¬µ÷ÓõÄÊÇ System.getenv() + propertySources.addLast(new PropertiesPropertySource( + SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); + // »ñȡϵͳ»·¾³£¬µ÷ÓõÄÊÇ System.getProperties() + propertySources.addLast(new SystemEnvironmentPropertySource( + SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); + } + +} + +``` + +¿ÉÒÔ¿´µ½£¬`StandardEnvironment`µÄ`customizePropertySources()`·½·¨Ö÷ÒªÊǽ«ÏµÍ³ÊôÐÔÓëϵͳ»·¾³Ìí¼Óµ½`Environment`ÖС£¶øÊµ¼ÊÉÏ£¬`Environment`Öаüº¬ÁËÐí¶àµÄϵͳÓë»·¾³Ïà¹ØµÄ²ÎÊý£¬±¾ÉíÒ²ÌṩÁËһЩ`getter`·½·¨¿ÉÒԺܷ½±ãµØ»ñÈ¡ÕâЩ²ÎÊý£º + +![](https://oscimg.oschina.net/oscnet/up-d2d69692db15146f2981db94633e7c575d5.png) + +µ½ÕâÀÎÒÃǾÍÃ÷°×ÁË£¬`StandardServletEnvironment`Öаüº¬ÁËÈçÏÂÄÚÈÝ£º + +* ϵͳÊôÐÔ£¬ÎÒÃÇÆ½Ê±µ÷ÓÃ`System.getenv()`µÃµ½µÄ²ÎÊý£» +* ϵͳ»·¾³£¬ÎÒÃÇÆ½Ê±µ÷ÓÃ`System.getProperties()`µÃµ½µÄ²ÎÊý£» +* `servlet`µÄ`servletContext`Óë`servletConfig`. + +#### 2\. ÅäÖû·¾³ + +ÎÒÃǼÌÐø·ÖÎö£¬½Ó×Å¿´¿´ÅäÖû·¾³µÄÁ÷³Ì£¬Ò²¾ÍÊÇ`SpringApplication#configureEnvironment`·½·¨£º + +``` +protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + if (this.addConversionService) { + // Ìí¼Óת»»Æ÷£¬´¦Àí²ÎÊýÀàÐÍת»»£¬Èç StringתNumber£¬IntegerתEnumµÈ + ConversionService conversionService = ApplicationConversionService.getSharedInstance(); + environment.setConversionService((ConfigurableConversionService) conversionService); + } + // ½«Æô¶¯²ÎÊýÌí¼Óµ½ environment ÖÐ + configurePropertySources(environment, args); + // ´¦Àí ActiveProfiles Öµ + configureProfiles(environment, args); +} + +``` + +Õâ¸ö·½·¨´úÂë²»¶à£¬¹Ø¼üµã¶¼ÒÑÔÚ´úÂëÖÐ×¢ÊÍÇå³þÁË£¬ÕâÀïÐèÒªÉÔ΢ÌáÏÂ`SpringApplication#configurePropertySources`£º + +``` +protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { + MutablePropertySources sources = environment.getPropertySources(); + // ÉèÖÃĬÈÏÊôÐÔ£¬Èç¹ûÖ¸¶¨ÁËĬÈÏÊôÐÔ£¬¾ÍÔÚÕâÀïÅäÖà + if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { + sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties)); + } + if (this.addCommandLineProperties && args.length > 0) { + String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; + if (sources.contains(name)) { + PropertySource source = sources.get(name); + CompositePropertySource composite = new CompositePropertySource(name); + composite.addPropertySource( + // ½âÎöÆô¶¯Ê±´«ÈëµÄ²ÎÊý + new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); + composite.addPropertySource(source); + sources.replace(name, composite); + } + else { + sources.addFirst(new SimpleCommandLinePropertySource(args)); + } + } +} + +``` + +ÔÚÕâ¸ö·½·¨Àï»á¶Ô´«ÈëµÄ²ÎÊý½øÐнâÎö£¬½øÈë`SimpleCommandLinePropertySource`£º + +``` +public class SimpleCommandLinePropertySource + extends CommandLinePropertySource { + + public SimpleCommandLinePropertySource(String... args) { + super(new SimpleCommandLineArgsParser().parse(args)); + } + ... +} + +``` + +×îÖÕ½âÎöµÄ·½·¨ÊÇ`SimpleCommandLineArgsParser#parse`£º + +``` +public class SimpleCommandLineArgsParser { + public CommandLineArgs parse(String... args) { + CommandLineArgs commandLineArgs = new CommandLineArgs(); + for (String arg : args) { + if (arg.startsWith("--")) { + String optionText = arg.substring(2, arg.length()); + String optionName; + String optionValue = null; + if (optionText.contains("=")) { + // -- ¿ªÍ·ÇÒ°üº¬ = µÄ²ÎÊý£¬»á±»½âÎö³É key/value + optionName = optionText.substring(0, optionText.indexOf('=')); + optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length()); + } + else { + optionName = optionText; + } + if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) { + throw new IllegalArgumentException("Invalid argument syntax: " + arg); + } + commandLineArgs.addOptionArg(optionName, optionValue); + } + else { + commandLineArgs.addNonOptionArg(arg); + } + } + return commandLineArgs; + } + + ... +} + +``` + +½âÎö·½·¨»¹ÊDZȽϼòµ¥µÄ£¬¾ÍÊÇ×Ö·û´®µÄ´¦Àí¶øÒÑ¡£ + +springboot µÄÆô¶¯²ÎÊýÓÐɶºÃ½âÎöµÄÄØ£¿ÔÚÆô¶¯ spring ÏîĿʱ£¬ÎÒÃÇ¿ÉÒÔÕâÑùÖ¸¶¨²ÎÊý£º + +``` +java -jar xxx.jar --a1=aaa --b1=bbb + +``` + +È»ºóÎÒÃǾÍÄÜͨ¹ý`@Value("${a1}")`»ñÈ¡µ½£¬¹Ø¼ü¾ÍÊÇÔÚÕâÀ¿ÉÒÔ¿´µ½£¬springboot »á°Ñ´«ÈëµÄ`--a1=aaa`¡¢`--b1=bbb`½âÎö³É`a1/aaa`¡¢`b1/bbb`¼üÖµ¶ÔµÄÐÎʽ£¬±£´æµ½`Environment`£¬ºóÐøÒªÓõ½Ê±£¬¾Í¿ÉºÜ·½±ãµØ´Ó`Environment`ÖлñÈ¡ÁË¡£ + +ºÃÁË£¬×¼±¸»·¾³µÄ·ÖÎö¾Íµ½ÕâÀïÁË¡£ + +### 3.6 ÅäÖÃϵͳ²ÎÊý + +Õâ¿éÖ÷ÒªÊÇÅäÖÃÁËÒ»¸ö²ÎÊý£º`spring.beaninfo.ignore`£¬ÓÃÀ´¿ØÖÆÊÇ·ñÌø¹ý`BeanInfo`ÀàµÄËÑË÷£¬`²¢ÇÒÓÉÔ´Âë¿É֪ĬÈÏÖµÊÇtrue`£¬¸Ð¾õÓõIJ»¶à£¬¾Í²»·ÖÎöÁË¡£ + +### 3.7 ´òÓ¡`banner` + +`banner`ÊÇÔÚÕâÀï´òÓ¡³öµÄ£º + +``` +Banner printedBanner = printBanner(environment); + +``` + +ÎÒÃÇÒ²¿ÉÒÔ×Ô¼º¶¨Òå banner£¬Õâ¸öÍøÉϽ̳ÌÒ»´ó¶Ñ£¬demo ¾Í²»ÌṩÁË¡£ + +¹ØÓÚ`banner`£¬Óë springboot µÄÆô¶¯Á÷³Ì¹ØÏµ²»´ó£¬¾Í²»·ÖÎöÁË£¬Ð¡»ï°éÃÇÖ»ÐèÁ˽âÔõôÑùÓü´¿É¡£ + +ºÃÁË£¬ÏÞÓÚÆª·ù£¬±¾Îľ͵½ÕâÀïÁË£¬ÏÂÆªÎÒÃǼÌÐø¡£ + +![](https://oscimg.oschina.net/oscnet/up-38d3824690292937a6b0cba5b081c8f8fec.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4882417](https://my.oschina.net/funcy/blog/4882417)£¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" new file mode 100644 index 0000000..8e6b041 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\345\256\214\346\210\220\345\220\257\345\212\250.md" @@ -0,0 +1,108 @@ +ÉÏһƪÎÄÕÂ×ܽá springboot Æô¶¯Á÷³ÌÈçÏ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-07a6b491fbe69b8dcbd41e59a8543f06671.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö½ÓÏÂÀ´µÄ²½Öè¡£ + +### 3.11 ˢкóµÄ´¦Àí + +ˢкóµÄ´¦Àí·½·¨Îª `SpringApplication#afterRefresh`£¬ÄÚÈÝÈçÏ£º + +``` +protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { +} +``` + +¿ÉÒÔ¿´µ½£¬ÕâÊÇÒ»¸ö¿Õ·½·¨£¬springboot ÌṩµÄÀ©Õ¹¡£ + +### 3.12 ·¢²¼ `started` ʼþ + +¸Ã²Ù×÷µÄ·½·¨Îª `listeners.started(context)`£¬·¢²¼Âß¼­Ç°ÃæÒѾ­·ÖÎö¹ý£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£ + +### 3.13 ÔËÐÐÆ÷»Øµ÷ + +´¦ÀíÔËÐÐÆ÷µÄ·½·¨ÊÇ `SpringApplication#callRunners`£¬´úÂëÈçÏ£º + +``` +private void callRunners(ApplicationContext context, ApplicationArguments args) { + List runners = new ArrayList<>(); + // »ñÈ¡ËùÓÐµÄ ApplicationRunner Óë CommandLineRunner + runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); + runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); + // ÅÅÐò + AnnotationAwareOrderComparator.sort(runners); + // ±éÀúµ÷Óà + for (Object runner : new LinkedHashSet<>(runners)) { + // µ÷Óà ApplicationRunner#run ·½·¨ + if (runner instanceof ApplicationRunner) { + callRunner((ApplicationRunner) runner, args); + } + // µ÷Óà CommandLineRunner#run ·½·¨ + if (runner instanceof CommandLineRunner) { + callRunner((CommandLineRunner) runner, args); + } + } +} + +/** + * µ÷Óà ApplicationRunner#run ·½·¨ + */ +private void callRunner(ApplicationRunner runner, ApplicationArguments args) { + try { + (runner).run(args); + } + catch (Exception ex) { + throw new IllegalStateException("Failed to execute ApplicationRunner", ex); + } +} + +/** + * µ÷Óà CommandLineRunner#run ·½·¨ + */ +private void callRunner(CommandLineRunner runner, ApplicationArguments args) { + try { + (runner).run(args.getSourceArgs()); + } + catch (Exception ex) { + throw new IllegalStateException("Failed to execute CommandLineRunner", ex); + } +} +``` + +ÕâÁ½¸ö·½·¨±íʾ£¬springboot ΪÎÒÃÇÌṩÁËÁ½¸ö½Ó¿Ú£º`ApplicationRunner` Óë `CommandLineRunner`£¬ÎÒÃÇ¿ÉÒÔʵÏÖËüÀ´Íê³ÉһЩ²Ù×÷£¬Ó¦ÓÃʾÀýÈçÏ£º + +``` +/** + * ApplicationRunner ʾÀý + */ +@Component +public class MyApplicationRunner implements ApplicationRunner { + @Override + public void run(ApplicationArguments args) throws Exception { + System.out.println("MyApplicationRunner: hello world"); + } +} + +/** + * CommandLineRunner ʾÀý + */ +@Component +public class MyCommandLineRunner implements CommandLineRunner { + @Override + public void run(String... args) throws Exception { + System.out.println("MyCommandLineRunner: hello world!"); + } +} +``` + +### 3.14 ÔËÐмàÌýÆ÷·½·¨£º `listeners.running(...)` + +Õâ¸ö·½·¨µÄÔËÐÐÍ¬Ç°Ãæ·ÖÎöµÄ `listeners.starting()` Ì×·һÑù£¬ÕâÀï¾Í²»·ÖÎöÁË¡£ + +ºÃÁË£¬±¾ÎĵķÖÎö¾Íµ½ÕâÀïÁË£¬¹ØÓÚ springboot µÄÆô¶¯Á÷³ÌµÄ·ÖÎöÒ²µ½ÕâÀïÁË¡£ + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9b539b547c6004c40d2b6f8bd59481b8e34.png) + +------ + +*±¾ÎÄÔ­ÎÄÁ´½Ó£ºhttps://my.oschina.net/funcy/blog/4906553 £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£* \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" new file mode 100644 index 0000000..8b2d0ee --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" @@ -0,0 +1,41 @@ +Ç°Ãæ¼¸ÆªÎÄÕ·ÖÎö springboot µÄÆô¶¯Á÷³Ì£¬±¾ÎÄÀ´×ܽáÏ¡£ + +ÎÄÕÂÒ»¿ªÊ¼£¬´Ó `SpringApplication.run(Demo01Application.class, args);` ´úÂëÈëÊÖ£¬×ÅÖØ·ÖÎöÁËÁ½¸ö·½·¨£º + +* `SpringApplication#SpringApplication(...)` +* `SpringApplication#run(...)` + +ÕâÁ½¸ö·½·¨º­¸ÇÁË springboot Æô¶¯µÄÕû¸öÁ÷³Ì£¬ÕâÀïÎÒÃÇÖðÒ»×ܽáÏ ¡£ + +### `SpringApplication#SpringApplication(...)` + +Õâ¸ö·½·¨µÄÁ÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e9a43f1c523c0f19d37e4741580ed32ca08.png) + +ÆäÖУ¬ + +* `webApplicationType` »áÔÚºóÃæ¾ö¶¨´´½¨Ê²Ã´ÀàÐ굀 `applicationContext`£» +* `Initialzers` À´×ÔÓÚ `META-INF/spring.factories`£¬»áÔÚ springboot Æô¶¯Ê±×öһЩ³õʼ»¯²Ù×÷£» +* `Listteners` ͬÑùÀ´×ÔÓÚ `META-INF/spring.factories`£¬ÌṩÁ˶à¸ö·½·¨£¬¿ÉÒÔ·½±ãµØ¼àÌý springboot µÄÖ´Ðйý³Ì¡£ + +### `SpringApplication#run(...)` + +Õⲿ·ÖµÄÁ÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-07a6b491fbe69b8dcbd41e59a8543f06671.png) + +ÆäÖУ¬ + +* `getRunListener()` »á»ñÈ¡ËùÓÐµÄ `Listeners`£¬Ò²¾ÍÊÇÔÚ `SpringApplication#SpringApplication(...)` ÖÐ »ñÈ¡µÄ `Listeners`£¬`Listeners` ÖÐÌṩÁËÖÚ¶à·½·¨£¬¿É¼àÌý springboot µÄÆô¶¯Á÷³Ì£» +* ×¼±¸ÔËÐл·¾³Ê±£¬»á¸ù¾Ý `webApplicationType` µÄÀàÐÍÀ´´´½¨¡¢ÅäÖ㬵õ½ÏàÓ¦ÀàÐ굀 `Environment` ¶ÔÏó£¬Õâ ¸ö¶ÔÏóºóÃæ»áÉèÖõ½ spring ÈÝÆ÷ÖУ¬spring ÈÝÆ÷ÖÐʹÓÃµÄ `Environment` ¾ÍÊÇÔÚÕâÀï´´½¨¼°ÅäÖõģ» +* ´´½¨ ioc ÈÝÆ÷ʱ£¬Ò²ÊǸù¾Ý `webApplicationType` µÄÀàÐÍÀ´´´½¨¶ÔÓ¦µÄ `ApplicationContext`£» +* ÔÚ×¼±¸ ioc ÈÝÆ÷µÄ·½·¨ÖУ¬»á¶Ô `ApplicationContext` ×öÒ»¸öÅäÖà £¬`Initializers` Ò²»áÔÚÕâÀïÔËÐУ» +* Æô¶¯ ioc ÈÝÆ÷ʱ£¬springboot »á×¢²áÒ»¸ö shutdownhook£¬ÓÃÒÔÔÚÏîÄ¿¹Ø±Õʱ´¦Àí¹Ø±Õ²Ù×÷£¬ÁíÍ⣬¶ÔÓÚ ioc µÄÆô¶¯Á÷³Ì£¬springboot ÔÚÆäÀ©Õ¹Öлᴴ½¨¼°Æô¶¯ web ÈÝÆ÷£» +* springboot ÌṩÁËÁ½ÖÖÀàÐ͵ÄÔËÐÐÆ÷£º`ApplicationRunner`¡¢`CommandLineRunner`£¬ÔÚÆô¶¯ºó»áµ÷ÓÃÁ½Õߵķ½·¨¡£ + +ÒÔÉÏÄÚÈݽ²µÄ±È½Ï¼òÂÔ£¬Èç¹ûÒªÏêϸÁ˽⣬¿É×ÔÐÐÔĶÁÇ°ÃæµÄÎÄÕ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4906588](https://my.oschina.net/funcy/blog/4906588) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" new file mode 100644 index 0000000..507a1a5 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250IOC\345\256\271\345\231\250.md" @@ -0,0 +1,389 @@ +ÉÏһƪÎÄÕÂ×ܽá springboot Æô¶¯Á÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-07a6b491fbe69b8dcbd41e59a8543f06671.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö½ÓÏÂÀ´µÄ²½Öè¡£ + +### 3.10 ˢРioc ÈÝÆ÷ + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `SpringApplication#refreshContext` ·½·¨£º + +``` +private void refreshContext(ConfigurableApplicationContext context) { + // Æô¶¯springÈÝÆ÷ + refresh(context); + if (this.registerShutdownHook) { + try { + // ×¢²á ShutdownHook + context.registerShutdownHook(); + } + catch (AccessControlException ex) { + // Not allowed in some environments. + } + } +} + +``` + +Õâ¸ö·½·¨²Ù×÷¾ÍÁ½¸ö£º + +1. `refresh(context)`£ºÆô¶¯ spring ÈÝÆ÷£¬Ò²²»Êǵ÷Óà `AbstractApplicationContext#refresh` ·½·¨£» +2. `context.registerShutdownHook()`£º×¢²á `ShutdownHook`£¬¿ÉÒÔÔÚ jvm ½ø³Ì¹Ø±Õʱ´¦ÀíÒ»Ð©ÌØ¶¨µÄ²Ù×÷¡£ + +#### 3.10.1 Æô¶¯ spring ÈÝÆ÷ + +½øÈë `SpringApplication#refresh`£º + +``` +protected void refresh(ApplicationContext applicationContext) { + Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); + // spring ÈÝÆ÷µÄÆô¶¯²Ù×÷ÁË + ((AbstractApplicationContext) applicationContext).refresh(); +} + +``` + +Õâ¸ö·½·¨ºÜ¼òµ¥£¬ÏÈÅÐ¶Ï `applicationContext` µÄÀàÐÍÊÇ·ñΪ `AbstractApplicationContext`£¬È»ºóÔÙµ÷Óà `AbstractApplicationContext#refresh()`¡£ + +¹ØÓÚ `AbstractApplicationContext#refresh()`£¬ÄÇ¿ÉÊÇ´óÃû¶¦¶¦°¡£¬¸Ã·½·¨º­¸ÇÁË spring ÈÝÆ÷µÄÆô¶¯Á÷³Ì¡£ÓÉÓÚ±¾ÎIJ»ÊÇ·ÖÎö spring µÄÎÄÕ£¬Òò´ËÕâ¿é¾Í²»Õ¹¿ª·ÖÎöÁË£¬ÏëÒªÁ˽âµÄÆô¶¯Á÷³ÌµÄС»ï°é¿ÉÒԲο¼ÒÔÏÂÎÄÕ£º + +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨ËÄ£©£ºÆô¶¯Ç°µÄ×¼±¸¹¤×÷](https://my.oschina.net/funcy/blog/4633169) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨Î壩£ºÖ´ÐÐ BeanFactoryPostProcessor](https://my.oschina.net/funcy/blog/4641114) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨Áù£©£º×¢²á BeanPostProcessor](https://my.oschina.net/funcy/blog/4657181) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨Æß£©£º¹ú¼Ê»¯Óëʼþ´¦Àí](https://my.oschina.net/funcy/blog/4892120) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨°Ë£©£ºÍê³É BeanFactory µÄ³õʼ»¯](https://my.oschina.net/funcy/blog/4658230) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨¾Å£©£ºµ¥Àý bean µÄ´´½¨](https://my.oschina.net/funcy/blog/4659524) +* [¡¾spring Ô´Âë·ÖÎö¡¿spring Æô¶¯Á÷³Ì£¨Ê®£©£ºÆô¶¯Íê³ÉµÄ´¦Àí](https://my.oschina.net/funcy/blog/4892555) + +ÔÚ `AbstractApplicationContext#refresh()` ÖУ¬spring ÌṩÁ˼¸¸öÀ©Õ¹µã£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b86d0fb2e3790f63b5c6590884be9401354.png) + +ÎÒÃǵ±Ç°Ê¹ÓÃµÄ `applicationContext` Ϊ `AnnotationConfigServletWebServerApplicationContext`£¬ÆäÖÐҲʹÓÃÁËÕâЩÀ©Õ¹µã£¬ÎÒÃÇÖ÷Òª¹Ø×¢ÕâЩÀ©Õ¹µãµÄÓ¦Óᣠ+ +##### 1\. Æô¶¯Ç°×¼±¸£º`prepareRefresh()` + +¾­¹ýµ÷ÊÔ·¢ÏÖ£¬`initPropertySources()` ·½·¨»áÔËÐе½£¬µ÷ÓÃÁ´ÈçÏ£º + +``` +AbstractApplicationContext#refresh + |- AnnotationConfigServletWebServerApplicationContext#prepareRefresh + |- AbstractApplicationContext#prepareRefresh + |- GenericWebApplicationContext#initPropertySources + +``` + +×îÖÕµ÷ÓõÄÊÇ `GenericWebApplicationContext#initPropertySources`£º + +``` +protected void initPropertySources() { + ConfigurableEnvironment env = getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null); + } +} + +``` + +Õâ¸ö·½·¨ÀïÏÈ»ñÈ¡ `Environment`£¬È»ºóÅжÏÊÇ·ñΪ `ConfigurableWebEnvironment` µÄʵÀý£¬ÔÚÇ°Ãæ·ÖÎö**×¼±¸ÔËÐÐʱ»·¾³**ʱ£¬ÎÒÃǵõ½µÄ `Environment` Ϊ `StandardServletEnvironment`£¬ÊÇ `ConfigurableWebEnvironment` µÄ·ûºÏ£¬È»ºóµ÷Óà `ConfigurableWebEnvironment#initPropertySources` ·½·¨£¬½á¹ûµ½ÁË `StandardServletEnvironment#initPropertySources`£º + +``` +public void initPropertySources(@Nullable ServletContext servletContext, + @Nullable ServletConfigservletConfig) { + // Ìæ»»ÉÏÃæÉèÖÃµÄ servletContextInitParams Ϊ servletContext + // Ìæ»»ÉÏÃæÉèÖÃµÄ servletConfigInitParams Ϊ servletConfig + WebApplicationContextUtils.initServletPropertySources(getPropertySources(), + servletContext, servletConfig); +} + +``` + +Õâ¸ö·½·¨»¹ÊǺܼòµ¥£¬Ö»Êǽ« `servletContext` Óë `servletConfig` ÉèÖõ½ÁË `Environment` ÖС£ + +##### 2\. »ñÈ¡ `beanFactory`: `obtainFreshBeanFactory()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 3\. ×¼±¸ `beanFactory`: `prepareBeanFactory(beanFactory)` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 4\. À©Õ¹µã£º`postProcessBeanFactory(beanFactory)` + +`AnnotationConfigServletWebServerApplicationContext` ÖØÐ´ÁËÕâ¸ö·½·¨£º + +``` +@Override +protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // µ÷Óø¸ÀàµÄ·½·¨ + super.postProcessBeanFactory(beanFactory); + // ½øÐаüɨÃ裬ÕâÀïµÄ°ü²¢²»´æÔÚ + if (this.basePackages != null && this.basePackages.length > 0) { + this.scanner.scan(this.basePackages); + } + // ×¢²ábean£¬Îª¿Õ + if (!this.annotatedClasses.isEmpty()) { + this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); + } +} + +``` + +Õâ¸ö·½·¨µÄÖ´Ðйý³ÌÈçÏ£º + +1. µ÷ÓÃÁ˸¸ÀàµÄ·½·¨ `super.postProcessBeanFactory(beanFactory)` +2. ½øÐаüɨÃ裬ͨ¹ýµ÷ÊÔ·¢ÏÖ£¬ÕâÀïµÄ `basePackages` Ϊ nul +3. ×¢²á `annotatedClasses`£¬ÕâÀïµÄ `annotatedClasses` Ϊ¿Õ + +ÎÒÃÇÖ÷ÒªÀ´¿´¿´ `super.postProcessBeanFactory(beanFactory)`£¬¸Ã·½·¨ÔÚ `ServletWebServerApplicationContext` ÖУº + +``` +@Override +protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // Ìí¼ÓÒ»¸ö BeanPostProcessor + beanFactory.addBeanPostProcessor( + new WebApplicationContextServletContextAwareProcessor(this)); + // ºöÂÔ ServletContextAware µÄ×Ô¶¯×¢Èë + beanFactory.ignoreDependencyInterface(ServletContextAware.class); + // ×¢²á web bean µÄ·¶Î§£¬ÕâÀï»á×¢²árequest¡¢session¡¢globalSessionµÄ×÷ÓÃÓò + registerWebApplicationScopes(); +} + +``` + +Õâ¸ö·½·¨ÄÚÈݱȽϼòµ¥£¬Ö÷ÒªÊÇ×¢²á `BeanPostProcessor` ÒÔ¼°×¢²á `web bean` µÄ×÷Ó÷¶Î§¡£ÕâÀïÎÒÃÇÖ÷Òª¿´Ï `WebApplicationContextServletContextAwareProcessor` µÄ×÷Ó㬴úÂëÈçÏ£º + +``` +public class WebApplicationContextServletContextAwareProcessor + extends ServletContextAwareProcessor { + + private final ConfigurableWebApplicationContext webApplicationContext; + + public WebApplicationContextServletContextAwareProcessor( + ConfigurableWebApplicationContext webApplicationContext) { + Assert.notNull(webApplicationContext, "WebApplicationContext must not be null"); + this.webApplicationContext = webApplicationContext; + } + + /** + * »ñÈ¡ ServletContext + */ + @Override + protected ServletContext getServletContext() { + ServletContext servletContext = this.webApplicationContext.getServletContext(); + return (servletContext != null) ? servletContext : super.getServletContext(); + } + + /** + * »ñÈ¡ ServletConfig + */ + @Override + protected ServletConfig getServletConfig() { + ServletConfig servletConfig = this.webApplicationContext.getServletConfig(); + return (servletConfig != null) ? servletConfig : super.getServletConfig(); + } + +} + +``` + +Õâ¸öÀàËÆºõ²¢Ã»ÓÐ×öʲô£¬ÎÒÃÇÔÙ¸ú½ø¸¸À࣬ÓÉÓÚËüÊǸö `BeanPostProcessor`£¬ÎÒÃÇÖ÷Òª¹Ø×¢ËüµÄ `postProcessBeforeInitialization()` Óë `postProcessAfterInitialization()` Á½¸ö·½·¨£º + +``` +public class ServletContextAwareProcessor implements BeanPostProcessor { + + ... + + public Object postProcessBeforeInitialization(Object bean, + String beanName) throws BeansException { + // ÉèÖà ServletContext + if (getServletContext() != null && bean instanceof ServletContextAware) { + ((ServletContextAware) bean).setServletContext(getServletContext()); + } + // ÉèÖà ServletConfig + if (getServletConfig() != null && bean instanceof ServletConfigAware) { + ((ServletConfigAware) bean).setServletConfig(getServletConfig()); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) { + return bean; + } + +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö `BeanPostProcessor` ÊÇÓÃÀ´´¦Àí `ServletContextAware` Óë `ServletConfigAware` Á½¸ö `Aware` ½Ó¿ÚµÄ£¬Ì×·ͬ´¦Àí `ApplicationAware`¡¢`BeanFactoryAware` µÈÒ»Ñù¡£ + +##### 5\. Ö´ÐÐ `BeanFactoryPostProcessors`: `invokeBeanFactoryPostProcessors(beanFactory)` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +ÖµµÃÒ»ÌáµÄÊÇ£¬ÔÚÕâ¸ö·½·¨ÖУ¬ÓиöÖØµÄ `BeanFactoryPostProcessor` »á±»Ö´ÐУº`ConfigurationClassPostProcessor`£¬springboot µÄ×Ô¶¯×°ÅäµÄÆôÓÃ×¢½â `@EnableAutoConfiguration` »áÔÚÕâÀï´¦Àí£¬×Ô¶¯×°ÅäÀàµÄ¼ÓÔØ¡¢Ìõ¼þ×¢½âÒ²ÊÇÔÚ `ConfigurationClassPostProcessor` ÖС£ + +##### 6\. ×¢²á `BeanPostProcessor`: `registerBeanPostProcessors(beanFactory)` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 7\. ³õʼ»¯` MessageSource`(ÓÃÓÚ¹ú¼Ê»¯²Ù×÷): `initMessageSource()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 8\. ³õʼ»¯Ê¼þ¹ã²¥Æ÷£º`initApplicationEventMulticaster()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 9\. À©Õ¹µã£º`onRefresh()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨µÄÀ©Õ¹Îª `ServletWebServerApplicationContext#onRefresh` ·½·¨£¬´úÂëÈçÏ£º + +``` +@Override +protected void onRefresh() { + // µ÷Óø¸Àà·½·¨ + super.onRefresh(); + try { + // ´´½¨web·þÎñÆ÷£¬Èçtomcat,jettyµÈ + createWebServer(); + } + catch (Throwable ex) { + throw new ApplicationContextException(...); + } +} + +``` + +¿ÉÒÔ ¿´µ½£¬web ·þÎñÆ÷ÊÇÔÚÕâ¸ö·½·¨Öд´½¨µÄ¡£²»¹ý web ·þÎñÆ÷µÄ´´½¨²¢²»¼òµ¥£¬ÐèÒª¾­¹ý¶àÖÖÌõ¼þÅжϣ¬¹ØÓÚÕâµãÎÒÃǺóÃæÔÙÏêϸ˵Ã÷¡£ + +##### 10\. ×¢²áʼþ¼àÌýÆ÷£º`registerListeners()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 11\. ³õʼ»¯µ¥Àý `bean`: `finishBeanFactoryInitialization(beanFactory)` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +##### 12\. Íê³ÉÆô¶¯²Ù×÷: `finishRefresh()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨µÄÀ©Õ¹Îª `ServletWebServerApplicationContext#finishRefresh` ·½·¨£¬´úÂëÈçÏ£º + +``` +@Override +protected void finishRefresh() { + super.finishRefresh(); + // Æô¶¯webÈÝÆ÷ + WebServer webServer = startWebServer(); + if (webServer != null) { + // ·¢²¼ ServletWebServerInitializedEvent ʼþ + publishEvent(new ServletWebServerInitializedEvent(webServer, this)); + } +} + +/** + * Æô¶¯webÈÝÆ÷ + */ +private WebServer startWebServer() { + WebServer webServer = this.webServer; + if (webServer != null) { + webServer.start(); + } + return webServer; +} + +``` + +¿ÉÒÔ¿´µ½£¬ÕâÀï²ÅÊÇÕæÕýÆô¶¯ web ÈÝÆ÷¡£ + +##### 13\. Çå³ý»º´æ: `resetCommonCaches()` + +µ±Ç° `applicationContext` ¶Ô¸Ã·½·¨ÎÞÀ©Õ¹£¬²»·ÖÎö¡£ + +#### 3.10.2 ×¢²á `ShutdownHook` + +ÎÒÃÇÔÙÀ´¿´ `context.registerShutdownHook()`£¬¸Ã·½·¨ÓÉ `AbstractApplicationContext#registerShutdownHook` Ìṩ£º + +``` +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + ... + @Override + public void registerShutdownHook() { + if (this.shutdownHook == null) { + // Ö¸¶¨Ï̵߳ÄÃû×Ö + this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) { + @Override + public void run() { + synchronized (startupShutdownMonitor) { + // ÕâÀï¾ÍÊÇ ShutdownHook µÄÄÚÈÝ + doClose(); + } + } + }; + Runtime.getRuntime().addShutdownHook(this.shutdownHook); + } + } + + /** + * ´¦ÀíÈÝÆ÷µÄ¹Ø±Õ²Ù×÷ + */ + protected void doClose() { + // Check whether an actual close attempt is necessary... + if (this.active.get() && this.closed.compareAndSet(false, true)) { + LiveBeansView.unregisterApplicationContext(this); + + try { + // ·¢²¼¹Ø±Õʼþ + publishEvent(new ContextClosedEvent(this)); + } + catch (Throwable ex) { + logger.warn(...); + } + + // µ÷Óà lifecycle µÄ onClose() ·½·¨ + if (this.lifecycleProcessor != null) { + try { + this.lifecycleProcessor.onClose(); + } + catch (Throwable ex) { + logger.warn(...); + } + } + + // Ïú»Ù bean + destroyBeans(); + + // ¹Ø±ÕÈÝÆ÷ + closeBeanFactory(); + + // À©Õ¹µã£¬´ý×ÓÀàʵÏÖ + onClose(); + + // Çå³ý¼àÌýÆ÷ + if (this.earlyApplicationListeners != null) { + this.applicationListeners.clear(); + this.applicationListeners.addAll(this.earlyApplicationListeners); + } + + // ÉèÖà active ±êʶ + this.active.set(false); + } + } + + ... + +} + +``` + +¿ÉÒÔ¿´µ½£¬`context.registerShutdownHook()` ʵ¼ÊÉÏÊÇÔËÐÐÁË `doClose()` ·½·¨£¬ÓÃÀ´´¦ÀíÈÝÆ÷µÄ¹Ø±Õ²Ù×÷¡£¹Ø±Õ spring ÈÝÆ÷µÄ¹Ø±Õ£¬×¢ÊÍÒѾ­Ï൱Çå³þÁË£¬ÕâÀï¾Í²»ÉîÈëÁË¡£ + +ºÃÁË£¬ÈÝÆ÷µÄÆô¶¯¾Í·ÖÎöµ½ÕâÀïÁË£¬´ÓÁ÷³ÌÉÏÀ´½²£¬Óë spring ÈÝÆ÷Æô¶¯µÄ×î´óÀ©Õ¹ÔÚÓÚ `onRefresh()` Óë `finishRefresh()`£¬Ç°Õß´´½¨ÁË `webServer` ÈÝÆ÷£¬ºóÕ߯ô¶¯ÁË `webServer` ÈÝÆ÷¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-81033ec78641ad875623cf452ef9cd62eb6.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4888129](https://my.oschina.net/funcy/blog/4888129) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" new file mode 100644 index 0000000..2dbdfc7 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\200\357\274\211\357\274\232\345\212\240\350\275\275\350\207\252\345\212\250\350\243\205\351\205\215\347\261\273.md" @@ -0,0 +1,456 @@ +×Ô¶¯×°ÅäÊÇ springboot µÄºËÐÄÖ®Ò»£¬±¾ÎĽ«À´Ì½¾¿ springboot ÊÇÈçºÎ¼ÓÔØ×Ô¶¯×°ÅäÀàµÄ¡£ + +ÔÚ [@SpringBootApplication ×¢½â](https://my.oschina.net/funcy/blog/4870882)Ò»ÎÄÖУ¬ÎÒÃÇÌáµ½ springboot ´¦Àí×Ô¶¯×°ÅäµÄ×¢½âÊÇ `@EnableAutoConfiguration`£¬´úÂëÈçÏ£º + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +// ×Ô¶¯×°ÅäµÄ°ü +@AutoConfigurationPackage +// ÒýÈëµÄ×Ô¶¯×°ÅäÀà +@Import(AutoConfigurationImportSelector.class) +public @interface EnableAutoConfiguration { + + String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; + + /** + * ¿É×ÔÐж¨ÒåÅųý×Ô¶¯×°ÅäµÄÀà + */ + Class[] exclude() default {}; + + /** + * ¿É×ÔÐж¨ÒåÅųý×Ô¶¯×°ÅäµÄÀàÃû + */ + String[] excludeName() default {}; + +} + +``` + +ÒÔÉÏ´úÂë°üº¬Èý¸ö²¿·Ö£º + +1. `@AutoConfigurationPackage`£ºÖ¸¶¨×Ô¶¯×°ÅäµÄ°ü£» +2. `@Import(AutoConfigurationImportSelector.class)`£ºÒýÈë×Ô¶¯×°ÅäµÄ´¦ÀíÀà `AutoConfigurationImportSelector`£¬Õâ¸öÀàÊÇ×Ô¶¯×°ÅäµÄ¹Ø¼üËùÔÚ£» +3. `@EnableAutoConfiguration` µÄÊôÐÔ£º`@EnableAutoConfiguration` ÌṩÁËÁ½¸öÊôÐÔ£º`exclude` Óë `excludeName`£¬¿ÉÒÔÓÃÀ´Åųý²»ÐèÒª×Ô¶¯×°ÅäµÄÀà¡£ + +±¾ÎÄÖØµãÀ´·ÖÎö `AutoConfigurationImportSelector` Àà¡£ + +### 1. `AutoConfigurationImportSelector.AutoConfigurationGroup` + +`AutoConfigurationImportSelector` ʵÏÖÁË `DeferredImportSelector`£¬¹ØÓÚ `DeferredImportSelector` µÄ·ÖÎö£¬¿ÉÒԲο¼ [ConfigurationClassPostProcessor Ö®´¦Àí @Import ×¢½â](https://my.oschina.net/funcy/blog/4678152)£¬ÕâÀïÎÒÃÇÖ±½Ó¸ø³ö½áÂÛ£º + +* `DeferredImportSelector` ÊÇ `ImportSelector` µÄ×Ó½Ó¿Ú£¬ÆäÄÚ²¿ÓÐÒ»¸ö½Ó¿Ú `Group`£¬¸Ã½Ó¿Ú¶¨ÒåÁËÁ½¸ö·½·¨£º + + ``` + public interface DeferredImportSelector extends ImportSelector { + ... + + interface Group { + + /** + * ´¦Àíµ¼Èë²Ù×÷ + */ + void process(AnnotationMetadata metadata, DeferredImportSelector selector); + + /** + * ·µ»Øµ¼ÈëÀà + */ + Iterable selectImports() + } + } + + ``` + + ÔÚ´¦Àí `DeferredImportSelector` µÄµ¼ÈëÀàʱ£¬`DeferredImportSelector.Group#process` ·½·¨»áÏȵ÷Óã¬È»ºóÔÙµ÷Óà `DeferredImportSelector.Group#selectImports` ·µ»Øµ¼ÈëÀࣻ + +* `DeferredImportSelector` ¿ÉÒÔÖ¸¶¨µ¼ÈëÀàµÄ·Ö×飬ÔÚ´¦Àíʱ£¬¿ÉÒÔ°´·Ö×é´¦Àíµ¼ÈëÀࣻ + +* `DeferredImportSelector` ÔÚ´¦Àíµ¼ÈëÀàʱ£¬ÏȽ«µ¼ÈëÀà°´·Ö×é·ÅÈëÒ»¸ö `map` ÖУ¬ÔÚ´¦ÀíÍêÆäËûÅäÖÃÀࣨspring µÄÅäÖÃÀàΪ `@Component`¡¢`@ComponentScan`¡¢`@Import`¡¢`@Configuration`¡¢`@Bean` ±ê¼ÇµÄÀࣩºóÔÙÀ´´¦Àí·Ö×éÖеĵ¼ÈëÀ࣬Ҳ¾ÍÊÇ˵£¬`DeferredImportSelector` µ¼ÈëµÄÀ࣬»áÔÚÆäËûÀà×¢²áµ½ `beanFactory` Öкó£¬ÔÙ½øÐÐ×¢²á£¨×¢²áǰ»¹ÐèÅжÏÄÜ·ñ×¢²áµ½ `beanFactory`£¬ÈôÄܲÅ×¢²á£©¡£ + +ÎÒÃÇÀ´¿´¿´ `AutoConfigurationImportSelector` µÄ´úÂ룺 + +``` +// ʵÏÖÁË DeferredImportSelector +public class AutoConfigurationImportSelector implements DeferredImportSelector, + BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { + + ... + + /** + * ÕâÀïʵÏÖÁË DeferredImportSelector.Group + */ + private static class AutoConfigurationGroup implements DeferredImportSelector.Group, + BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { + + /** + * ±£´æµ¼ÈëµÄÀà + */ + private final List autoConfigurationEntries = new ArrayList<>(); + + /** + * ´¦Àíµ¼ÈëÀà + */ + @Override + public void process(AnnotationMetadata annotationMetadata, + DeferredImportSelector deferredImportSelector) { + Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, + () -> String.format("Only %s implementations are supported, got %s", + AutoConfigurationImportSelector.class.getSimpleName(), + deferredImportSelector.getClass().getName())); + // 1\. µ÷Óà AutoConfigurationImportSelector#getAutoConfigurationEntry(...) ·½·¨£¬ + // ÔÚÕâ¸ö·½·¨Àï»á¼ÓÔØ×Ô¶¯×°ÅäÀà + AutoConfigurationEntry autoConfigurationEntry = + ((AutoConfigurationImportSelector) deferredImportSelector) + .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); + // 2\. ½«»ñÈ¡µ½µÄ autoConfigurationEntry ±£´æÆðÀ´ + this.autoConfigurationEntries.add(autoConfigurationEntry); + for (String importClassName : autoConfigurationEntry.getConfigurations()) { + this.entries.putIfAbsent(importClassName, annotationMetadata); + } + } + + /** + * ·µ»Øµ¼ÈëÀà + */ + @Override + public Iterable selectImports() { + if (this.autoConfigurationEntries.isEmpty()) { + return Collections.emptyList(); + } + // 3\. µÃµ½¹ýÂËÀà + Set allExclusions = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream) + .collect(Collectors.toSet()); + // 4\. ½« autoConfigurationEntries ת»»Îª LinkedHashSet + Set processedConfigurations = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + // 5\. È¥³ýÐèÒª¹ýÂ˵ÄÀà + processedConfigurations.removeAll(allExclusions); + // 6\. ½øÐÐÅÅÐò + return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()) + .stream().map((importClassName) -> new Entry( + this.entries.get(importClassName), importClassName)) + .collect(Collectors.toList()); + } + + ... + } + +} + +``` + +ÕâÀïÎÒÃǽ« `DeferredImportSelector.Group#process` Óë `DeferredImportSelector.Group#selectImports` Á½¸ö·½·¨½áºÏÆðÀ´¿´£¬´¦Àí²½Öè×ܽáÈçÏÂ: + +1. µ÷Óà `AutoConfigurationImportSelector#getAutoConfigurationEntry(...)` ·½·¨¼ÓÔØ×Ô¶¯×°ÅäÀࣻ +2. ½«µÃµ½µÄ×Ô¶¯×°ÅäÀà±£´æµ½ `autoConfigurationEntries` ÖУ» +3. µÃµ½¹ýÂËÀ࣬ÕâЩ¹ýÂËÀà¾ÍÊÇÓÉ `@EnableAutoConfiguration` µÄ `exclude` »ò `excludeName` Ö¸¶¨µÄ£» +4. ½« `autoConfigurationEntries` ת»»Îª `LinkedHashSet`£¬½á¹ûΪ `processedConfigurations`£» +5. È¥³ý `processedConfigurations` ÐèÒª¹ýÂ˵ÄÀࣻ +6. ½«µÚ 5 ²½µÃµ½µÄÀàÅÅÐòºó£¬·µ»Ø¡£ + +½ÓÏÂÀ´ÎÒÃǶÔÕâЩ¹Ø¼ü²½Öè½øÐзÖÎö¡£ + +> ÌØ±ð˵Ã÷£º`DeferredImportSelector` ÊÇ `ImportSelector` µÄ×Ó½Ó¿Ú£¬`ImportSelector` ´¦Àíµ¼ÈëÀàµÄ·½·¨ÊÇ `selectImports(...)`£¬ÔÚ `DeferredImportSelector` ÖÐÒ²ÖØÐ´Á˸÷½·¨£º +> +> ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-75a2839c622af2d0f374189b2e2765a64d7.png) +> +> Õâ¸ö·½·¨Ëù×öµÄÒ²ÊǼÓÔØ×Ô¶¯×°ÅäÀ࣬·µ»Ø×îÖÕµ¼ÈëµÄÀ࣬µ«ÐèҪעÒâµÄÊÇ£¬springboot µÄ×Ô¶¯µ¼ÈëÀà**²»ÊÇ**ÔÚÕâÀï´¦ÀíµÄ£¬¹ØÓÚÕâµã£¬¿ÉÒÔÔÚ·½·¨ÄÚ´ò¸ö¶Ïµã£¬È»ºó¾Í»á·¢ÏÖÕâ¸ö·½·¨²¢Ã»ÓÐÔËÐе½£¡ +> +> ×îºóÔÙÉùÃ÷Ï£ºspringboot µÄ×Ô¶¯µ¼ÈëÀà**²»ÊÇ**ÔÚ `AutoConfigurationImportSelector#selectImports` ·½·¨Öд¦ÀíµÄ£¬¶øÊÇÔÚ `AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports` ·½·¨Öд¦ÀíµÄ¡£ + +### 2\. »ñȡװÅäÀࣺ`AutoConfigurationImportSelector#getAutoConfigurationEntry` + +×Ô¶¯ÅäÖà ÀàµÄ¼ÓÔØ´úÂëΪ£º + +``` +AutoConfigurationEntry autoConfigurationEntry = + ((AutoConfigurationImportSelector) deferredImportSelector) + .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); + +``` + +¸Ã´úÂë¾ÍÊÇÓÃÀ´¼ÓÔØ×Ô¶¯×°ÅäÀàµÄ£¬ÎÒÃÇÖ±½Ó½øÈë `AutoConfigurationImportSelector#getAutoConfigurationEntry` ·½·¨£º + +``` +protected AutoConfigurationEntry getAutoConfigurationEntry( + AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { + // ÓÖÒ»´ÎÅжÏÊÇ·ñ¿ªÆô×Ô¶¯×°Åä + if (!isEnabled(annotationMetadata)) { + return EMPTY_ENTRY; + } + // »ñȡע½âµÄÊôÐÔ + AnnotationAttributes attributes = getAttributes(annotationMetadata); + // 1\. ¼ÓÔØºòÑ¡µÄ×Ô¶¯ÅäÖÃÀà + List configurations = getCandidateConfigurations(annotationMetadata, attributes); + // 2\. È¥ÖØ£¬×ª»»³Éset£¬ÔÙת»»³Élist + configurations = removeDuplicates(configurations); + // 3\. È¥³ýÐèÒªÅųýµÄÀ࣬Æäʵ¾ÍÊÇ´¦Àí@EnableAutoConfigurationµÄexcludeÓëexcludeName + Set exclusions = getExclusions(annotationMetadata, attributes); + checkExcludedClasses(configurations, exclusions); + configurations.removeAll(exclusions); + // 4\. ¹ýÂ˲»ÐèÒª×Ô¶¯×°ÅäµÄÀà + configurations = filter(configurations, autoConfigurationMetadata); + // 5\. ´¥·¢ AutoConfigurationImportEvent ʼþ + fireAutoConfigurationImportEvents(configurations, exclusions); + // 6\. ×îÖÕ·µ»ØµÄÖµ + return new AutoConfigurationEntry(configurations, exclusions); +} + +``` + +Õâ¸ö·½·¨·Ç³£ÖØÒª£¬°üº¬ÁË»ñÈ¡×Ô¶¯×°ÅäÀàµÄÈ«²¿²Ù×÷£¬¸Ã²Ù×÷Á÷³ÌÈçÏ£º + +1. ¼ÓÔØºòÑ¡µÄ×Ô¶¯×°ÅäÀ࣬springboot ×Ô¶¯×°ÅäµÄÀàλÓÚ `classpath` Ï嵀 `META-INF/spring.factories` ÎļþÖУ¬key Ϊ `org.springframework.boot.autoconfigure.EnableAutoConfiguration`£¬Õâ¸öÎÒÃǺóÃæÔÙÏêϸ·ÖÎö£» + +2. È¥³ýÖØ¸´µÄ×Ô¶¯×°ÅäÀ࣬ÉÏÒ»²½¼ÓÔØµÃµ½µÄ×Ô¶¯×°ÅäÀà¿ÉÄÜ»áÓÐÖØ¸´£¬ÔÚÕâÀï»áÈ¥³ýÖØ¸´µÄÀ࣬ȥ³ý·½Ê½Ò²·Ç³£¼òµ¥£¬springboot ¾ÍÖ»ÊÇÏÈת»»³É `Set`£¬ÔÙת»»³É `List`£» + +3. È¥³ýÅųýµÄÀà£¬Ç°ÃæÌáµ½ `@EnableAutoConfiguration` ¿ÉÒÔͨ¹ý `exclude` Óë `excludeName` Ö¸¶¨ÐèÒªÅųýµÄÀ࣬ÕâÒ»²½¾ÍÊÇÀ´´¦ÀíÕâÁ½¸öÊôÐԵģ» + +4. ¹ýÂ˲»ÐèÒª×Ô¶¯×°ÅäµÄÀ࣬¸ù¾Ý±¾È˵÷ÊÔ£¬·¢ÏÖ²¢Ã»ÓÐÍê³É¹ýÂË£º + + ¹ýÂËǰÊÇ 124 ¸ö£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-773695c0161c8126f239c2e66529fc8a394.png) + + ¹ýÂ˺ó»¹ÊÇ 124 ¸ö£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-7144d971f729b5dfbddfeff2d3319c7705c.png) + +5. ´¥·¢ `AutoConfigurationImportEvent` ʼþ£» + +6. ½«µÚ 3 ²½µÃµ½µÄÅųýÀàÓëµÚ 4 ²½µÃµ½µÄ×Ô¶¯×°ÅäÀà°ü×°³É `AutoConfigurationEntry` ·µ»Ø¡£ + +×¢Òâ×îºóÒ»ÐдúÂ룺 + +``` +// 6\. ×îÖÕ·µ»ØµÄÖµ +return new AutoConfigurationEntry(configurations, exclusions); + +``` + +ÕâÀï°Ñ `configurations` Óë `exclusions` ¶¼´«ÈëÁË `AutoConfigurationEntry` µÄ¹¹Ôì·½·¨£¬ÎÒÃÇÀ´¿´¿´ `AutoConfigurationEntry`£º + +``` +protected static class AutoConfigurationEntry { + // ×Ô¶¯×°ÅäÀà + private final List configurations; + + // ÐèÒªÅųýµÄ×Ô¶¯×°ÅäÀà + private final Set exclusions; + + /** + * ¹¹Ôì·½·¨£¬¶ÔÔÙÕß½øÐи³Öµ + */ + AutoConfigurationEntry(Collection configurations, Collection exclusions) { + this.configurations = new ArrayList<>(configurations); + this.exclusions = new HashSet<>(exclusions); + } + + ... +} + +``` + +ÓÉЩ¿É¼û£¬×îÖÕ·µ»ØµÄ `AutoConfigurationEntry` °üº¬Á½´óÄÚÈÝ£º + +* `configurations`£º×Ô¶¯×°ÅäÀ࣬ÒѾ­È¥³ýÁËÐèÒªÅųýµÄÀà +* `exclusions`£ºÍ¨¹ý `@EnableAutoConfiguration` Ö¸¶¨µÄÐèÒªÅųýµÄÀà + +Õû¸ö×Ô¶¯×°ÅäÀàµÄ»ñÈ¡¾ÍÊÇÕâÑùÁË£¬ÏÂÃæÎÒÃÇÀ´¿´¿´¼ÓÔØºòÑ¡µÄ×Ô¶¯×°ÅäÀàµÄÁ÷³Ì¡£ + +### 3\. ¼ÓÔØºòÑ¡µÄ×Ô¶¯×°ÅäÀà + +×Ô¶¯×°ÅäÀàµÄ¼ÓÔØÎ»ÓÚ `AutoConfigurationImportSelector#getCandidateConfigurations`£¬´úÂëÈçÏ£º + +``` +protected List getCandidateConfigurations(AnnotationMetadata metadata, + AnnotationAttributes attributes) { + // µ÷ÓõÄÊÇ spring ÌṩµÄ·½·¨£ºSpringFactoriesLoader.loadFactoryNames(...) + // getSpringFactoriesLoaderFactoryClass() ·µ»ØµÄÊÇEnableAutoConfiguration + List configurations = SpringFactoriesLoader + .loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); + Assert.notEmpty(configurations, "..."); + return configurations; +} + +protected Class getSpringFactoriesLoaderFactoryClass() { + return EnableAutoConfiguration.class; +} + +``` + +¼ÌÐø½øÈë `SpringFactoriesLoader#loadFactoryNames`£º + +``` +public final class SpringFactoriesLoader { + + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + + public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) { + // µÃµ½µÄ factoryTypeName ÊÇ org.springframework.boot.autoconfigure.EnableAutoConfiguration + String factoryTypeName = factoryType.getName(); + return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); + } + + /** + * ÔÚÕâÀï½øÐмÓÔØ£¬¼ÓÔØµÄÊÇ META-INF/spring.factories ÖеÄÊôÐÔ + */ + private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) { + MultiValueMap result = cache.get(classLoader); + if (result != null) { + return result; + } + + try { + // ¼ÓÔØ META-INF/spring.factories µÄÄÚÈÝ + Enumeration urls = (classLoader != null ? + classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : + ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); + result = new LinkedMultiValueMap<>(); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + UrlResource resource = new UrlResource(url); + // ½« META-INF/spring.factories µÄÄÚÈÝת»»Îª Properties ¶ÔÏó + Properties properties = PropertiesLoaderUtils.loadProperties(resource); + for (Map.Entry entry : properties.entrySet()) { + String factoryTypeName = ((String) entry.getKey()).trim(); + // StringUtils.commaDelimitedListToStringArray(...) ¶ººÅ·Ö¸îΪÊý×é + for (String factoryImplementationName : + StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { + result.add(factoryTypeName, factoryImplementationName.trim()); + } + } + } + cache.put(classLoader, result); + return result; + } + catch (IOException ex) { + throw new IllegalArgumentException("Unable to load factories from location [" + + FACTORIES_RESOURCE_LOCATION + "]", ex); + } + } + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬ÕâÀï¼ÓÔØµÄÊÇ `classpath` Ï嵀 `META-INF/spring.factories` Îļþ£¬×¢Ò⣺Õâ¸öÎļþ¿ÉÄÜ»áÓжà¸ö£¬Î»ÓÚ²»Í¬µÄ jar °üÖС£ + +springboot ×Ô´øµÄ `META-INF/spring.factories` λÓÚ `spring-boot-autoconfigure` Ä£¿éÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ace5e83645626966eae1e62a50752f2417d.png) + +ÎÒÃÇÀ´¿´Ò»ÑÛ `spring.factories`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d7de9ecd19345f0dc77cc304843c588fe4d.png) + +Õâ¸öÎļþ¶¨ÒåÁËÐí¶àµÄÅäÖÃÀ࣬ÒÔ `key-value` µÄÐÎʽ±£´æ£¬¶à¸öÖµÖ®¼äʹÓà ¡°,¡± ·Ö¿ª£¬ÉÏÃæÌáµ½µÄ×Ô¶¯×°ÅäÀàµÄ key ÊÇ `org.springframework.boot.autoconfigure.EnableAutoConfiguration`£¬¶ÔÓ¦µÄ `value` ·Ç³£¶à£¬ÕâÀï¾Í²»Õ¹Ê¾ÁË¡£ + +ÕâÒ»²½Ö®ºó£¬×Ô¶¯×°ÅäÀà¾Í±»×¢²áµ½ spring ÈÝÆ÷ÖÐÁË¡£×¢£º´Ëʱ¼ÓÔØµ½ spring ÈÝÆ÷ÖеϹÊÇ `BeanDefinition`£¬ÒªÏë³ÉΪ spring bean£¬»¹µÃ¾­¹ý `ConditionalOnBean`¡¢`ConditionalOnClass` µÈ×¢½âµÄ¿¼Ñ飬ÕâЩÎÒÃǺóÃæÔÙ·ÖÎö¡£ + +### 4\. »ñÈ¡×Ô¶¯×°ÅäÀàºóµÄ´¦Àí + +ÈÃÎÒÃÇÔٻص½ `AutoConfigurationImportSelector.AutoConfigurationGroup`£¬ÔÚµÚ 1 ½ÚÎÒÃÇ×ܽáµÄÁ÷³ÌÈçÏ£º + +1. µ÷Óà `AutoConfigurationImportSelector#getAutoConfigurationEntry(...)` ·½·¨¼ÓÔØ×Ô¶¯×°ÅäÀࣻ +2. ½«µÃµ½µÄ×Ô¶¯×°ÅäÀà±£´æµ½ `autoConfigurationEntries` ÖУ» +3. µÃµ½¹ýÂËÀ࣬ÕâЩ¹ýÂËÀà¾ÍÊÇÓÉ `@EnableAutoConfiguration` µÄ `exclude` »ò `excludeName` Ö¸¶¨µÄ£» +4. ½« `autoConfigurationEntries` ת»»Îª `LinkedHashSet`£¬½á¹ûΪ `processedConfigurations`£» +5. È¥³ý `processedConfigurations` ÐèÒª¹ýÂ˵ÄÀࣻ +6. ½«µÚ 5 ²½µÃµ½µÄÀàÅÅÐòºó£¬·µ»Ø¡£ + +ÒÔÉÏµÚ 2 ½ÚÓëµÚ 3 ½Ú£¬·ÖÎöµÄÊÇ×Ô¶¯¼ÓÔØÀàµÄ¼ÓÔØ¹ý³Ì£¬ÎÒÃÇÔÙÀ´¿´¿´½ÓÏÂÀ´µÄ²½Öè¡£ + +¶ÔÕÕ×Å´úÂ룬ÎÒÃǻᷢÏÖ½ÓÏÂÀ´µÄ²½Öè¶¼±È½Ï¼òµ¥£¬ÕâÀïÒ²Öðһ˵Ã÷ϰɡ£ + +* µÚ 2 ²½£¬±£´æµÃµ½µÄ×Ô¶¯×°ÅäÀ࣬Õâ¸ö²Ù×÷½ö½öÖ»Êǵ÷ÓÃÁË `List#add(...)` ·½·¨£¬½«µÃµ½µÄ `autoConfigurationEntry` ±£´æµ½ `autoConfigurationEntries`£¬Õâ¸ö½á¹¹ÊÇ `AutoConfigurationGroup` µÄ³ÉÔ±±äÁ¿£¬ÔÚ `AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports` ·½·¨ÖлáÓõ½£» + +* µÚ 3 ²½£¬µÃµ½µÄÊÇËùÓеĹýÂËÀ࣬¸Ã¹ýÂËÀàÊDZéÀú `autoConfigurationEntries`£¬È»ºóͨ¹ý `autoConfigurationEntry#getExclusions` ·½·¨µÃµ½µÄ £¬Ç°ÃæÎÒÃÇÒ²Ìáµ½¹ý£¬`autoConfigurationEntry` Ö»°üº¬Á½¸ö³ÉÔ±±äÁ¿£º`configurations`(È¥³ýÅųýÀàºóµÄ×Ô¶¯×°ÅäÀà) Óë `exclusions`(ͨ¹ý `@EnableAutoConfiguration` Ö¸¶¨µÄÅųýÀà)£» + +* µÚ 4 ²½£¬½« `List` ת»»Îª `LinkedHashSet`£¬²»·ÖÎö£» + +* µÚ 5 ²½£¬¶ÔËùÓеÄ×Ô¶¯×°ÅäÀàÔÙ½øÐÐÒ»´ÎÈ¥³ýÅųýÀàµÄ²Ù×÷£¬ÅųýµÄ¶ÔÏóÊÇËùÓеÄÅųýÀ࣬ÕâÖÖÇé¿öÓ¦¸ÃÊÇ»á¶ÔͬһÏîÄ¿ÖÐÓжà¸ö `@EnableAutoConfiguration` µÄÇé¿ö£¬±ÈÈçµÚÒ»¸ö `@EnableAutoConfiguration` ×¢½âÅųý `A`¡¢`B` Á½¸öÀ࣬µÚ¶þ¸ö `@EnableAutoConfiguration` ×¢½âÅųý `C`£¬`D` Á½¸öÀ࣬ÄÇ×îÖÕÅųýµÄÊÇ `A`¡¢`B`¡¢`C`£¬`D` ËĸöÀࣻ + +* µÚ 6 ²½£¬ÕâÒ»²½µÄÖ÷Òª²Ù×÷ÊÇÅÅÐò£¬Õâ¸ö˳Ðò¾ö¶¨ÁË×Ô¶¯×°ÅäÀà×¢²áµ½ `beanFactory` ÖеÄ˳Ðò£¬`AutoConfigureOrder`¡¢`@AutoConfigureAfter` Óë `@AutoConfigureBefore` ¾ÍÊÇÔÚÕâÀï´¦ÀíµÄ£¬¹ØÓÚÕâ¿éÄÚÈÝ£¬¿ÉÒԲο¼ [springboot ×Ô¶¯×°ÅäÖ®×Ô¶¯×°Åä˳Ðò](https://my.oschina.net/funcy/blog/4921594). + +¾­¹ýÕâЩ²½Öèºó£¬×Ô¶¯×°ÅäµÄ»ñÈ¡¾ÍÍê³ÉÁË¡£ + +### 5\. ×Ô¶¨Òå×Ô¶¯×°ÅäÀà + +Á˽âÍê×Ô¶¯×°ÅäÀàµÄ¼ÓÔØ¹ý³Ìºó£¬ÎÒÃÇÒ²¿ÉÒÔ×Ô¶¨ÒåÒ»¸ö×Ô¶¯×°ÅäÀà¡£ + +1. ×¼±¸Ò»¸ö×Ô¶¯×°ÅäÀà + +``` +@Configuration +public class MyAutoConfiguration { + + @Bean + public Object object() { + System.out.println("create object"); + return new Object(); + } +} + +``` + +Õâ¸öÀàºÜ¼òµ¥£¬¾ÍÊÇÒ»¸ö±ê¼ÇÁË `@Configuration` µÄÀ࣬ÀàÖÐʹÓà `@Bean` ×¢½â´´½¨ÁËÒ»¸ö bean£¬ÔÚ´´½¨ bean µÄ¹ý³ÌÖÐ»á ´òÓ¡ "create object"¡£ + +1. ×¼±¸ `META-INF/spring.factories` ÄÚÈÝÈçÏ£º + +``` +# Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.boot.learn.autoconfigure.demo01.configure.MyAutoConfiguration + +``` + +1. Ö÷Àà + +``` +@SpringBootApplication +public class AutoconfigureDemo01Application { + + public static void main(String[] args) { + SpringApplication.run(AutoconfigureDemo01Application.class, args); + } + +} + +``` + +ÔËÐнá¹ûÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9337a7ac4ce4ff7d71e69bc952bfac30b12.png) + +¿ÉÒÔ¿´µ½£¬`create object` ³É¹¦´òÓ¡ÁË¡£ + +ÄÇÕâ¸ö `bean` ÊÇͨ¹ý°üɨÃè´´½¨µÄ£¬»¹ÊÇ×Ô¶¯×°Åäµ¼ÈëµÄÄØ£¿ÎÒÃÇͨ¹ýµ÷ÊԵķ½Ê½À´¿´ÏÂ×Ô¶¯×°ÅäµÃµ½µÄÀࣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-27bea49ddeae2f0f720ea338914cc443aec.png) + +¿ÉÒÔ¿´µ½£¬`MyAutoConfiguration` ¾ÍÔÚ×Ô¶¯×°ÅäÀàµÄÁбíÖÐÁË¡£ + +×¢Òâµ½£¬`MyAutoConfiguration` ¼ÓÁË `@Configuration` ×¢½â£¬ ÄÇôËü¾¿¾¹ÊÇÓÉ sping ÈÝÆ÷ɨÃèµ½µÄ£¬»¹ÊÇÓÉ×Ô¶¯×°ÅäµÃµ½µÄÄØ£¿ + +ÔÚ[¡¾springboot Ô´Âë·ÖÎö¡¿@SpringBootApplication ×¢½â](https://my.oschina.net/funcy/blog/4870882)Ò»ÎÄÖУ¬ÎÒÃÇÌáµ½ `SpringBootApplication` ×¢½âÖÐµÄ `@ComponentScan` »áÖ¸¶¨Ò»¸ö¹ýÂËÆ÷£º`AutoConfigurationExcludeFilter`£¬Õâ¸ö¹ýÂËÆ÷»á¹ýÂË×Ô¶¯×°ÅäÀ࣬ÕâÀïÎÒÃÇ¿´ÏÂĿǰΪֹ `beanFactory` ¶¼ÓÐÄÄЩ `beanName`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1725008451f5516ab540bcc3ae13d1f37ee.png) + +¿ÉÒÔ¿´µ½£¬²¢Ã»ÓÐ `MyAutoConfiguration`£¬Òò´Ë´ËʱËü»¹Ã»±»É¨Ãè½ø `beanFactory` ÖС£ + +µ±È»£¬ÎÒÃÇÒ²¿ÉÒÔ°Ñ `MyAutoConfiguration` ÉÏÃæµÄ `@Configuration` ×¢½âÈ¥µô£¬ÕâÑù¾Í²»»áÓÐÕâ¸öÀ§»óÁË¡£ + +### 6\. ×ܽá + +±¾ÎÄ´Ó `@EnableAutoConfiguration` ×¢½â³ö·¢£¬·ÖÎöÁË×Ô¶¯×°ÅäÀàµÄ¼ÓÔØÁ÷³Ì£¬¼ÓÔØÁ÷³ÌÔÚ `AutoConfigurationImportSelector#getAutoConfigurationEntry` ·½·¨ÖУ¬×îÖÕ¼ÓÔØµÄÊÇ `META-INF/spring.factories` ÎļþÖÐ key ÊÇ `org.springframework.boot.autoconfigure.EnableAutoConfiguration` µÄÀà¡£ + +µÃµ½×Ô¶¯×°ÅäÀàºó£¬spring »á½«Æä×¢²áµ½ÈÝÆ÷ÖУ¬´ËʱËüÃÇ»¹ÊÇÒ»¸öµÄ `BeanDefinition`£¬ÒªÏë³ÉΪ spring bean£¬»¹µÃ¾­¹ý `ConditionalOnBean`¡¢`ConditionalOnClass` µÈ×¢½âµÄ¿¼Ñ飬ÕâЩÎÒÃǺóÃæÔÙ·ÖÎö¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4870868](https://my.oschina.net/funcy/blog/4870868) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" new file mode 100644 index 0000000..03744e2 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\270\211\357\274\211\357\274\232\350\207\252\345\212\250\350\243\205\351\205\215\351\241\272\345\272\217.md" @@ -0,0 +1,568 @@ +ÔÚ [springboot ×Ô¶¯×°ÅäÖ®Ìõ¼þ×¢½â£¨Ò»£©](https://my.oschina.net/funcy/blog/4918863)Ò»ÎÄÖУ¬ÔÚ·ÖÎö `@ConditionalOnBean/@ConditionalOnMissingBean` ×¢½âµÄÌõ¼þÅжÏʱ£¬¹Ù·½Ç¿ÁÒ½¨ÒéÎÒÃÇÔÚ×Ô¶¯×°ÅäÀàÖÐʹÓÃÕâÁ½¸ö×¢½â£¬²¢ÇÒ `@ConditionalOnBean/@ConditionalOnMissingBean` ±ê¼ÇµÄÀàÒªÔÚÖ¸¶¨µÄÀàÖ®ºó³õʼ»¯£¬ÄÇ springboot ÈçºÎÀ´¿ØÖÆ×Ô¶¯×°Åä˳ÐòÄØ£¿±¾ÎĽ«À´Ñо¿Ï¡£ + +### 1\. springboot ´¦Àí×Ô¶¯×°ÅäÀàµÄ¹ý³Ì + +ÐèÒªÃ÷È·µÄÊÇ£¬±¾ÎÄ̽ÌÖµÄ`×Ô¶¯×°Åä˳Ðò`ÊÇÖ¸½« `class` ×¢²áµ½ `beanFactory` µÄ˳Ðò£¬springboot ´¦Àí×Ô¶¯×°ÅäÀàµÄ´óÖ¹ý³ÌÈçÏ£º + +1. ¼ÓÔØ×Ô¶¯×°ÅäÀ࣬ÔÚ [springboot ×Ô¶¯×°ÅäÖ®¼ÓÔØ×Ô¶¯×°ÅäÀà](https://my.oschina.net/funcy/blog/4870868) Ò»ÎÄÖÐÒÑ·ÖÎö¹ý£» +2. ¶Ô×Ô¶¯×°ÅäÀà½øÐÐÅÅÐò£¬ÕâÊDZ¾ÎĽ«Òª·ÖÎöµÄÄÚÈÝ£» +3. ±éÀú×Ô¶¯×°ÅäÀ࣬¶Ôÿ¸ö×Ô¶¯×°ÅäÀàÖðÒ»½øÐÐÒÔϲÙ×÷£º + 1. ¸ù¾ÝÌõ¼þ×¢½âÅжϵ±Ç°×Ô¶¯×°ÅäÀàÊÇ·ñÂú×ã×°ÅäÌõ¼þ£» + 2. Èç¹ûµ±Ç°×Ô¶¯×°ÅäÀàÂú×ì×°ÅäÌõ¼þ£¬×¢²áµ½ `beanFactory` ÖС£ + +Ôٻص½ `@ConditionalOnBean/@ConditionalOnMissingBean`£¬¶ÔÈçÏÂÁ½¸ö×Ô¶¯×°ÅäÀࣺ + +``` +// AÊÇ×Ô¶¯×°ÅäÀà +@Configuration +public class A { + @Bean + @ConditionalOnMissingBean("b1") + public A1 a1() { + return new A1(); + } +} + +// BÊÇ×Ô¶¯×°ÅäÀà +@Configuration +public class B { + @Bean + public B1 b1() { + return new b1(); + } +} + +``` + +`a1` Óë `b1` ÔÚÁ½¸ö²»Í¬µÄ×Ô¶¯×°ÅäÀàÖгõʼ»¯£¬ÇÒ `a1` Ö»ÓÐÔÚ `b1` ²»´æÔÚʱ£¬²Å»á³õʼ»¯£¬¸ù¾ÝÉÏÃæ×ܽáµÄ springboot ´¦Àí×Ô¶¯×°ÅäÀàµÄ²½Ö裬ÎÒÃÇÖ»ÒªÖ¸¶¨ `b1` ÔÚ `a1` ֮ǰ³õʼ»¯¾Í²»»á²úÉúÒì³£ÁË¡£ + +ÄÇô£¬×Ô¶¯×°ÅäÀàµÄ˳ÐòÈçºÎÖ¸¶¨ÄØ£¿ + +### 2\. ×Ô¶¯×°ÅäÀàµÄ˳Ðò¿ØÖÆ×¢½â + +springboot ΪÎÒÃÇÌṩÁËÁ½ÖÖ×Ô¶¯×°ÅäÀàµÄÅÅÐòÊֶΣº + +* ¾ø¶Ô×Ô¶¯×°Åä˳Ðò ¡ª¡ª`@AutoConfigOrder` +* Ïà¶Ô×Ô¶¯×°Åä˳Ðò ¡ª¡ª`@AutoConfigureBefore` Óë `@AutoConfigureAfter` + +ÕâÈý¸ö×¢½â¾ÍÊÇÓÃÀ´´¦Àí×Ô¶¯×°ÅäÀàµÄÅÅÐòµÄÁË£¬`@AutoConfigOrder` Ö¸¶¨ÁË×°Åä˳Ðò£¬Í¬ spring ÌṩµÄ `@Order` ÀàËÆ£¬`@AutoConfigureBefore` Óë `@AutoConfigureAfter` ¿ÉÒÔÖ¸¶¨ `class`£¬±íʾÔÚÄĸö `class` ֮ǰ»òÖ®ºó×°Åä¡£ + +»Øµ½Ê¾Àý£¬ÎÒÃÇ¿ÉÒÔÕâÑùÖ¸¶¨×°Åä˳Ðò£º + +``` +// AÊÇ×Ô¶¯×°ÅäÀà +@Configuration +// ÔÚB.classÖ®ºó×Ô¶¯×°Åä +@AutoConfigureAfter(B.class) +public class A { + @Bean + @ConditionalOnMissingBean("b1") + public A1 a1() { + ... + } +} + +// BÊÇ×Ô¶¯×°ÅäÀà +@Configuration +public class B { + ... +} + +``` + +### 3\. ×Ô¶¯×°ÅäÀàµÄÅÅÐò + +Ç°ÃæÎÒÃÇÌáµ½£¬`@AutoConfigOrder`¡¢`@AutoConfigureBefore` Óë `@AutoConfigureAfter` ¿ÉÒÔ¿ØÖÆ×Ô¶¯×°ÅäÀàµÄ×°Åä˳Ðò£¬ÄÇôËüÃÇÊÇÔÚÄÄÀï½øÐÐÅÅÐòµÄÄØ£¿ÔÚ [springboot ×Ô¶¯×°ÅäÖ®¼ÓÔØ×Ô¶¯×°ÅäÀà](https://my.oschina.net/funcy/blog/4870868) Ò»ÎÄÖУ¬ÎÒÃÇÌá×ܽáÁË»ñÈ¡×Ô¶¯×°ÅäÀàµÄ²½ÖèÓÐ 6 ²½£º + +1. µ÷Óà `AutoConfigurationImportSelector#getAutoConfigurationEntry(...)` ·½·¨¼ÓÔØ×Ô¶¯×°ÅäÀࣻ +2. ½«µÃµ½µÄ×Ô¶¯×°ÅäÀà±£´æµ½ `autoConfigurationEntries` ÖУ» +3. µÃµ½¹ýÂËÀ࣬ÕâЩ¹ýÂËÀà¾ÍÊÇÓÉ `@EnableAutoConfiguration` µÄ `exclude` »ò `excludeName` Ö¸¶¨µÄ£» +4. ½« `autoConfigurationEntries` ת»»Îª `LinkedHashSet`£¬½á¹ûΪ `processedConfigurations`£» +5. È¥³ý `processedConfigurations` ÐèÒª¹ýÂ˵ÄÀࣻ +6. ½«µÚ 5 ²½µÃµ½µÄÀàÅÅÐòºó£¬·µ»Ø¡£ + +¶ø¶Ô×Ô¶¯×°ÅäÀàµÄÅÅÐòÕýÊÇÔÚµÚ 6 ²½£¬¶ÔÓ¦µÄ·½·¨ÊÇ `AutoConfigurationImportSelector.AutoConfigurationGroup#sortAutoConfigurations`£¬´úÂëÈçÏ£º + +``` +private List sortAutoConfigurations(Set configurations, + AutoConfigurationMetadata autoConfigurationMetadata) { + // ÏÈ´´½¨ÁË AutoConfigurationSorter ¶ÔÏó£¬ + // È»ºóµ÷Óà AutoConfigurationSorter.getInPriorityOrder ½øÐÐÅÅÐò + return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata) + .getInPriorityOrder(configurations); +} + +``` + +Õâ¸ö·½·¨µÄ´¦Àí·ÖΪÁ½²½£º + +1. ´´½¨ÁË `AutoConfigurationSorter` ¶ÔÏó +2. µ÷Óà `AutoConfigurationSorter.getInPriorityOrder` ½øÐÐÅÅÐò + +ÎÒÃÇÏÈÀ´¿´¿´ `AutoConfigurationSorter` µÄ´´½¨²Ù×÷£º + +``` +class AutoConfigurationSorter { + + private final MetadataReaderFactory metadataReaderFactory; + + private final AutoConfigurationMetadata autoConfigurationMetadata; + + /** + * ¹¹Ôì·½·¨ + * ½ö½öÖ»ÊǶԴ«ÈëµÄ²ÎÊý½øÐи³Öµ£¬½«ËûÃǸ³ÖµÎª³ÉÔ±±äÁ¿ + */ + AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory, + AutoConfigurationMetadata autoConfigurationMetadata) { + Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null"); + this.metadataReaderFactory = metadataReaderFactory; + this.autoConfigurationMetadata = autoConfigurationMetadata; + } + + ... + +} + +``` + +¿ÉÒÔ¿´µ½£¬`AutoConfigurationSorter` µÄ¹¹Ôì·½·¨²¢Ã»ÓÐ×öʲôʵÖÊÐԵIJÙ×÷£¬¿´À´ÅÅÐòµÄ¹Ø¼ü»¹µÃ¿´ `AutoConfigurationSorter.getInPriorityOrder` ·½·¨£¬¸Ã·½·¨µÄ´úÂëÈçÏ£º + +``` +List getInPriorityOrder(Collection classNames) { + // 1\. ½« classNames °ü×°³É AutoConfigurationClasses + AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, + this.autoConfigurationMetadata, classNames); + List orderedClassNames = new ArrayList<>(classNames); + // 2\. °´ÀàÃûÅÅÐò + Collections.sort(orderedClassNames); + // 3\. ʹÓà @AutoConfigureOrder ÅÅÐò + orderedClassNames.sort((o1, o2) -> { + int i1 = classes.get(o1).getOrder(); + int i2 = classes.get(o2).getOrder(); + return Integer.compare(i1, i2); + }); + // 4\. ʹÓà @AutoConfigureBefore£¬@AutoConfigureAfter ÅÅÐò + orderedClassNames = sortByAnnotation(classes, orderedClassNames); + return orderedClassNames; +} + +``` + +´Ó´úÂëÀ´ ¿´£¬Õâ¸ö·½·¨µÄÖ´Ðв½ÖèÈçÏ£º + +1. ½« `classNames` °ü×°³É `AutoConfigurationClasses` +2. °´ÀàÃûÅÅÐò +3. ʹÓà `@AutoConfigureOrder` ÅÅÐò +4. ʹÓà `@AutoConfigureBefore`£¬`@AutoConfigureAfter` ÅÅÐò + +Õâ¸ö·½·¨ÅÅÐò¹²½øÐÐÁË 3 ´Î£¬¶¼ÊÇ¶Ô `orderedClassNames` ½øÐÐÅÅÐò£¬ÕâÑùÒ»À´£¬ºóÃæµÄÅÅÐò»á´òÂÒÇ°ÃæµÄÅÅÐò£¬×îÏȵÄÅÅÐòÊǰ´ÀàÃûÅÅÐò£¬Ò²¾ÍÊÇ˵£¬Èç¹ûûÓÐÖ¸¶¨ `@AutoConfigureOrder`¡¢`@AutoConfigureBefore` µÈ×¢½â£¬¾Í»áʹÓÃÀàÃû½øÐÐÅÅÐò¡£ + +½ÓÏÂÀ´ÎÒÃǾßÌå·ÖÎöÕ⼸¸ö²Ù×÷°É¡£ + +### 4\. ½« `classNames` °ü×°³É `AutoConfigurationClasses` + +¸Ã²Ù×÷λÓÚ `AutoConfigurationSorter.AutoConfigurationClasses#AutoConfigurationClasses` ·½·¨£¬´úÂëÈçÏ£º + +``` +private static class AutoConfigurationClasses { + + // ±£´æ½á¹û + private final Map classes = new HashMap<>(); + + /** + * ¹¹Ôì·½·¨ + */ + AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, + AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames) { + // ½øÐз½·¨µ÷Óà + addToClasses(metadataReaderFactory, autoConfigurationMetadata, classNames, true); + } + + /** + * Ìí¼ÓÀ࣬¾ÍÊǽ«Àà°ü×°³É AutoConfigurationClass£¬Ìí¼Óµ½ÃûΪ classes µÄ Map ÖÐ + * classNames ¾ÍÊÇÈ¥³ýÁËÅųýÀàµÄËùÓÐ×Ô¶¯×°ÅäÀà + */ + private void addToClasses(MetadataReaderFactory metadataReaderFactory, + AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames, + boolean required) { + for (String className : classNames) { + if (!this.classes.containsKey(className)) { + // ½« className °ü×°³É AutoConfigurationClass + AutoConfigurationClass autoConfigurationClass = new AutoConfigurationClass( + className, metadataReaderFactory, autoConfigurationMetadata); + boolean available = autoConfigurationClass.isAvailable(); + // @AutoConfigureBefore Óë @AutoConfigureAfter ±ê¼ÇµÄÀàµÄ required Ϊ false + if (required || available) { + this.classes.put(className, autoConfigurationClass); + } + if (available) { + // µÝ¹éµ÷Óà + addToClasses(metadataReaderFactory, autoConfigurationMetadata, + autoConfigurationClass.getBefore(), false); + addToClasses(metadataReaderFactory, autoConfigurationMetadata, + autoConfigurationClass.getAfter(), false); + } + } + } + } + ... +} + +``` + +´ÓÒÔÉÏ´úÂëÀ´¿´£¬ + +* `AutoConfigurationClasses` °üº¬Ò»¸ö³ÉÔ±±äÁ¿£º`classes`£¬ÀàÐÍÊÇ `Map`£¬`key` ÊÇ `String`£¨Ò²¾ÍÊÇ `className`£©£¬`value` ÊÇ `AutoConfigurationClass`£¨Ò²¾ÍÊÇ `className` µÄ°üº¬Àࣩ; +* `AutoConfigurationClasses` µÄ¹¹Ôì·½·¨»áµ÷Óà `addToClasses(...)` ¸Ã·½·¨»á±éÀú´«ÈëµÄ `classNames`£¬½«Æä°ü×°³É `AutoConfigurationClass` ºó£¬ÔÙ±£´æµ½ `classes` ÖС£ + +ÔÚ·ÖÎö `addToClasses(...)` µÄ¾ßÌåÂß¼­Ç°£¬ÎÒÃÇÏÈÀ´¿´¿´ `AutoConfigurationClass` ÊǸöɶ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fd9c1a4c335951391a33f86a86fe89ff496.png) + +¿ÉÒÔ¿´µ½ £¬`AutoConfigurationClass` ÊÇÀàÃûµÄ°ü×°£¬²¢ÇÒ»¹±£´æÁË `@AutoConfigureBefore` Óë `@AutoConfigureAfter` Ö¸¶¨µÄÀ࣬ÒÔ¼°ÌṩÁ˸ú `@AutoConfigureOrder`¡¢ `@AutoConfigureBefore`¡¢`@AutoConfigureAfter` Ïà¹ØµÄһЩ·½·¨¡£ + +ÎÒÃÇÔٻعýÍ·¿´ `addToClasses(...)` µÄÖ´ÐÐÁ÷³Ì£¬¸Ã·½·¨µÄÖ´ÐÐÁ÷³ÌÈçÏ£º + +1. ±éÀú´«ÈëµÄ `classNames`£¬¶ÔÆäÖÐÿһ¸ö `className`£¬½øÐÐÏÂÃæµÄ²Ù×÷£» +2. ´´½¨ `AutoConfigurationClass`£¬´«Èë `className`£» +3. µ÷Óà `AutoConfigurationSorter.AutoConfigurationClass#isAvailable` ·½·¨£¬µÃµ½ `available`£» +4. ÅÐ¶Ï `available` Óë `required` µÄÖµ£¬Èç¹ûÆäһΪ ture£¬¾Í½«ÆäÌí¼Óµ½ `classes`£» +5. Èç¹û `available` Ϊ `true`£¬µÝ¹é´¦Àí `className` ÓÉ `@AutoConfigureBefore` Óë `@AutoConfigureAfter` Ö¸¶¨µÄÀà¡£ + +Á÷³Ì¿´×Ų»¸´ÔÓ£¬²»¹ýÓм¸¸ö¾ÍÈ¥ÐèÒª·ÖÎöÏ £º + +1. `AutoConfigurationSorter.AutoConfigurationClass#isAvailable`£ºÅжϵ±Ç° `class` ÊÇ·ñ´æÔÚ +2. `AutoConfigurationSorter.AutoConfigurationClass#getBefore`£º»ñÈ¡ `class`£ºµ±Ç° `class` ÐèÒªÔÚÕâЩ `class` ֮ǰ´¦Àí +3. `AutoConfigurationSorter.AutoConfigurationClass#getAfter`£º»ñÈ¡ `class`£ºµ±Ç° `class` ÐèÒªÔÚÕâЩ `class` Ö®ºó´¦Àí + +½ÓÏÂÀ´ÎÒÃÇÒ»Ò»À´·ÖÎöÏÂÕ⼸¸ö·½·¨¡£ + +#### 4.1 `AutoConfigurationSorter.AutoConfigurationClass#isAvailable` + +Õâ¸ö·½·¨ÊÇÓÃÀ´Åжϵ±Ç° `class` ÊÇ·ñÔÚµ±Ç°ÏîÄ¿µÄ `classpath` ÖУ¬¿´´úÂ룺 + +``` +boolean isAvailable() { + try { + if (!wasProcessed()) { + getAnnotationMetadata(); + } + return true; + } + catch (Exception ex) { + return false; + } +} + +``` + +Õâ¸ö·½·¨´úÂë²»¶à£¬ÏÈÊǵ÷Óà `wasProcessed()` ·½·¨£¬ÔÙµ÷Óà `getAnnotationMetadata()`£¬ÐèҪעÒâµÄÊÇ£¬`getAnnotationMetadata()` ¿ÉÄÜ»áÅ׳öÒì³££¬ÕÐÆ¸Òì³£Ò²»á·µ»Ø `false`. + +ÎÒÃǼÌÐø¸ú½ø `AutoConfigurationSorter.AutoConfigurationClass#wasProcessed` ·½·¨£º + +``` +private boolean wasProcessed() { + return (this.autoConfigurationMetadata != null + // ÅÐ¶Ï META-INF/spring-autoconfigure-metadata.properties ÎļþÖÐÊÇ·ñ´æÔÚ¸ÃÅäÖà + && this.autoConfigurationMetadata.wasProcessed(this.className)); +} + +``` + +Õâ¸ö·½·¨ÀïÖ÷Òªµ÷ÓÃÁË `AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata#wasProcessed` ·½·¨À´Åжϣº + +``` +@Override +public boolean wasProcessed(String className) { + // ÅÐ¶Ï properties ÊÇ·ñ´æÔÚ¶ÔÓ¦µÄ className + return this.properties.containsKey(className); +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨½öÊÇÅÐ¶Ï `properties` ÀïÊÇ·ñ°üº¬´«ÈëµÄ `className`£¬`properties` µÄÄÚÈÝÀ´×ÔÓÚ `META-INF/spring-autoconfigure-metadata.properties`£¬ÄÚÈÝʾÀýÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-069444fdaa952c6fa8c39576e776275982b.png) + +ÐèҪעÒâµÄÊÇ£¬¸ÃÎļþÔÚÔ´ÂëÖÐÊDz»´æÔڵģ¬ËüÊÇÔÚ±àÒëʱдÈëµÄ£¬¹ØÓÚ¸ÃÎļþµÄдÈë¡¢¼ÓÔØµ½ `properties` µÄÁ÷³Ì£¬±¾ÎľͲ»Õ¹¿ª·ÖÎöÁË£¬ÕâÀïÌṩ¸ö´ó¸Å˼·£º + +* ÎļþµÄдÈ룺ÔÚ´úÂë±àÒëʱ£¬springboot »á½«×Ô¶¯×°ÅäÀàµÄһЩÐÅÏ¢ (ÀýÈ磬`@ConditionalOnClass` Ö¸¶¨µÄ `class`£¬`@ConditionalOnBean` Ö¸¶¨µÄ `bean`£¬`@AutoConfigureBefore` Óë `@AutoConfigureAfter` µÄÖ¸¶¨µÄ `class` µÈ) дÈëµ½ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖУ¬´¦ÀíÀàΪ `AutoConfigureAnnotationProcessor`£¬Õâ¸öÀàÊÇ `javax.annotation.processing.AbstractProcessor` µÄ×ÓÀ࣬¶ø `AbstractProcessor` ÓÉ jdk Ìṩ£¬¿ÉÒÔÔÚ±àÒëÆÚ¶Ô×¢½â½øÐд¦Àí£» + +* ÎļþµÄ¼ÓÔØ£ºÔÚ `AutoConfigurationImportSelector.AutoConfigurationGroup#process` ·½·¨Öе÷Óà `AutoConfigurationImportSelector#getAutoConfigurationEntry` ʱ£¬»á´«Èë `AutoConfigurationMetadata`£¬Îļþ `META-INF/spring-autoconfigure-metadata.properties` ÖеÄÄÚÈݾÍÊÇ´ÓÕâÀï¼ÓÔØµ½ `AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata#properties` Öеģ» + +ÓÉЩ¿É¼û£¬`AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata#wasProcessed` ·½·¨Êµ¼ÊÉϾÍÊÇÅÐ¶Ï `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖÐÊÇ·ñÓÐ `className` µÄÅäÖᣠ+ +ÈÃÎÒÃǻص½ `AutoConfigurationSorter.AutoConfigurationClass#isAvailable`£¬ÔÙÀ´¿´¿´ÁíÒ»¸ö·½·¨£º`getAnnotationMetadata()`£¬¸Ã·½·¨Î»ÓÚ `AutoConfigurationSorter.AutoConfigurationClass` ÖУ¬´úÂëÈçÏ£º + +``` +private AnnotationMetadata getAnnotationMetadata() { + if (this.annotationMetadata == null) { + try { + // ¼ÓÔØ`className`¶ÔÓ¦µÄ×ÊÔ´£¬µ± className ¶ÔÓ¦µÄ×ÊÔ´²»´æÔÚʱ£¬»áÅ׳öÒì³£ + MetadataReader metadataReader = this.metadataReaderFactory + .getMetadataReader(this.className); + this.annotationMetadata = metadataReader.getAnnotationMetadata(); + } + catch (IOException ex) { + throw new IllegalStateException(...); + } + } + return this.annotationMetadata; +} + +``` + +¼ÌÐø½øÈë `SimpleMetadataReaderFactory#getMetadataReader(String)`£º + +``` +@Override +/** + * Õâ¸ö·½·¨»á»ñÈ¡ className ¶ÔÓ¦µÄ .class Îļþ + * Èç¹û .class Îļþ²»´æÔÚ£¬¾Í±¨Òì³£ÁË£ºIOException + */ +public MetadataReader getMetadataReader(String className) throws IOException { + try { + // ת»»Ãû³Æ£º"classpath:xxx/xxx/Xxx.class" + String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + + lassUtils.convertClassNameToResourcePath(className) + + ClassUtils.CLASS_FILE_SUFFIX; + // »ñÈ¡×ÊÔ´£¬Ä¬È쵀 resourceLoader Ϊ classLoader + Resource resource = this.resourceLoader.getResource(resourcePath); + // ½« resource ת»»³É MetadataReader ¶ÔÏ󣬲»´æÔھͻáÅ׳öÒì³££ºIOException + return getMetadataReader(resource); + } + catch (FileNotFoundException ex) { + // ÓпÉÄÜÊÇÄÚ²¿À࣬ÔÙ°´ÄÚ²¿ÀàµÄÃüÃû·½Ê½´¦ÀíÒ»´Î + int lastDotIndex = className.lastIndexOf('.'); + if (lastDotIndex != -1) { + String innerClassName = className.substring(0, lastDotIndex) + '$' + + className.substring(lastDotIndex + 1); + // ת»»Ãû³Æ£º"classpath:xxx/Xxx$Xxx.class" + String innerClassResourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(innerClassName) + + ClassUtils.CLASS_FILE_SUFFIX; + Resource innerClassResource = this.resourceLoader.getResource(innerClassResourcePath); + // ÅжÏÊÇ·ñ´æÔÚ£¬²»´æÔÚ»¹ÊǻᱨÒì³£µÄ£ºIOException + if (innerClassResource.exists()) { + return getMetadataReader(innerClassResource); + } + } + throw ex; + } +} + +``` + +Õâ¸ö·½·¨µÄ´¦ÀíÁ÷³ÌÈçÏ£º + +1. ½«´«ÈëµÄ `className` ת»»Îª `classpath:xxx/xxx/Xxx.class` µÄÐÎʽ£¬È»ºóÈ¥¼ÓÔØ¶ÔÓ¦µÄ×ÊÔ´£¬Èç¹û×ÊÔ´²»´æÔÚ¼´ `className` ¶ÔÓ¦µÄ`.class` Îļþ²»´æÔÚ£¬ÔòÅ׳öÒì³££» +2. ÔÚÒì³£µÄ `catch` ¿éÖУ¬ÎªÁË·ÀÖ¹ `className` ÊÇÄÚ²¿À࣬»á½« `className` ת»»Îª `classpath:xxx/Xxx$Xxx.class` µÄÐÎʽ£¬È»ºóÔÙ¼ÓÔØÒ»´Î×ÊÔ´£¬Èç¹û×ÊÔ´´æÔÚ£¬Ö±½Ó·µ»Ø£¬·ñÔò½«Òì³£ÍùÍâÅ×£» + +µ½ÁËÕâÀÎÒÃǾÍÃ÷°×ÁË£¬`getAnnotationMetadata()` ¾ÍÊÇÓÃÀ´Åжϵ±Ç° `className` ¶ÔÓ¦µÄ`.class` ÔÚÏîÄ¿µÄ `classpath` ·¾¶ÖÐÊÇ·ñ´æÔÚ¡£ + +¶ÔÕâÁ½¸ö·½·¨£¬ÎÒÃÇ×ܽáÈçÏ£º + +* `AutoConfigurationSorter.AutoConfigurationClass#wasProcessed`£ºµ±Ç° `className` ÊÇ·ñÔÚ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖÐ +* `AutoConfigurationSorter.AutoConfigurationClass#isAvailable`£ºµ±Ç° `className` ¶ÔÓ¦µÄ`.class` ÎļþÊÇ·ñ´æÔÚ + +×îÖյĽáÂÛ£º`AutoConfigurationSorter.AutoConfigurationClass#isAvailable` ¾ÍÊÇÓÃÀ´Åжϵ±Ç° `className` ¶ÔÓ¦µÄ`.class` ÎļþÔÚÏîÄ¿µÄ `classpath` ·¾¶ÖÐ. + +#### 4.2 `AutoConfigurationSorter.AutoConfigurationClass#getBefore/getAfter` + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `AutoConfigurationSorter.AutoConfigurationClass` ÀàµÄÁ½¸ö·½·¨£º`getAfter()` Óë `getBefore()`£º + +``` +Set getBefore() { + if (this.before == null) { + this.before = (wasProcessed() + // Èç¹û´æÔÚÓÚ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖУ¬Ö±½Ó»ñȡֵ + ? this.autoConfigurationMetadata.getSet(this.className, "AutoConfigureBefore", + Collections.emptySet()) + // ·ñÔò´Ó @AutoConfigureBefore ×¢½âÉÏ»ñÈ¡ + : getAnnotationValue(AutoConfigureBefore.class)); + } + return this.before; +} + +Set getAfter() { + if (this.after == null) { + this.after = (wasProcessed() + // Èç¹û´æÔÚÓÚ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖУ¬Ö±½Ó»ñȡֵ + ? this.autoConfigurationMetadata.getSet(this.className, "AutoConfigureAfter", + Collections.emptySet()) + // ·ñÔò´Ó @AutoConfigureAfter ×¢½âÉÏ»ñÈ¡ + : getAnnotationValue(AutoConfigureAfter.class)); + } + return this.after; +} + +/** + * ´Ó @AutoConfigureBefore/@AutoConfigureAfter ×¢½âÖлñȡֵ£ºvalue Óë name Ö¸¶¨µÄÖµ + */ +private Set getAnnotationValue(Class annotation) { + Map attributes = getAnnotationMetadata() + .getAnnotationAttributes(annotation.getName(), true); + if (attributes == null) { + return Collections.emptySet(); + } + Set value = new LinkedHashSet<>(); + Collections.addAll(value, (String[]) attributes.get("value")); + Collections.addAll(value, (String[]) attributes.get("name")); + return value; +} + +``` + +ÕâÁ½¸ö·½·¨ÔÚ´úÂëÐÎʽ»ù±¾Ò»Ö£¬ÏÈ¿´ `getBefore()` µÄÁ÷³Ì£º + +1. Èç¹ûµ±Ç° `className` ´æÔÚÓÚ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖУ¬Ö±½Óȡֵ£¬Ç°Ãæ·ÖÎöÒ²Ìáµ½£¬springboot ÔÚ±àÒëʱ£¬»á°ÑһЩע½âµÄÐÅϢдÈëµ½ `META-INF/spring-autoconfigure-metadata.properties` ÎļþÖУ» + +2. Èç¹ûµÚ 1 ²½²»³É¹¦£¬Ôò´Óµ±Ç° `class` µÄ `@AutoConfigureBefore` ȡֵ£» + +`getAfter()` ·½·¨µÄÁ÷³ÌÓë `getBefore()` µÄÁ÷³Ì»ù±¾Ò»Ö£¬¾Í²»·ÖÎöÁË¡£ + +### 5\. ʹÓà `@AutoConfigureOrder` ÅÅÐò + +ÈÃÎÒÃǻص½ `AutoConfigurationSorter#getInPriorityOrder` ·½·¨£¬ÎÒÃÇÀ´¿´¿´ `@AutoConfigureOrder` µÄÅÅÐò¹ý³Ì£º + +``` +List getInPriorityOrder(Collection classNames) { + ... + orderedClassNames.sort((o1, o2) -> { + int i1 = classes.get(o1).getOrder(); + int i2 = classes.get(o2).getOrder(); + return Integer.compare(i1, i2); + }); + ... +} + +``` + +Õâ¸öÅÅÐò²Ù×÷ʹÓõÄÊÇ `List#sort`£¬`sort(...)` ÀïµÄ²ÎÊýΪ `Comparator`£¬Ö¸¶¨ÁËÅÅÐò¹æÔò¡£´Ó´úÂëÀ´¿´£¬Í¨¹ý `getOrder()` »ñÈ¡µ½µ±Ç°ÀàµÄ˳Ðòºó£¬ÔÙʹÓõÄÊÇ `Integer` µÄ±È½Ï¹æÔò½øÐÐÅÅÐò£¬Òò´Ë `getOrder()` ÊÇÅÅÐòµÄ¹Ø¼ü£¬ËüËù¶Ô¾ÍµÄ·½·¨ÊÇ `AutoConfigurationSorter.AutoConfigurationClass#getOrder`£¬´úÂëÈçÏ£º + +``` +private int getOrder() { + // ÅÐ¶Ï META-INF/spring-autoconfigure-metadata.properties ÎļþÖÐÊÇ·ñ´æÔÚµ±Ç° className + if (wasProcessed()) { + // Èç¹û´æÔÚ£¬¾ÍʹÓÃÎļþÖÐÖ¸¶¨µÄ˳Ðò£¬·ñÔò¾ÍʹÓÃĬÈÏ˳Ðò + return this.autoConfigurationMetadata.getInteger(this.className, + "AutoConfigureOrder", AutoConfigureOrder.DEFAULT_ORDER); + } + // ´¦Àí²»´æÔÚµÄÇé¿ö£º»ñÈ¡ @AutoConfigureOrder ×¢½âÖ¸¶¨µÄ˳Ðò + Map attributes = getAnnotationMetadata() + .getAnnotationAttributes(AutoConfigureOrder.class.getName()); + // Èç¹û @AutoConfigureOrder δÅäÖ㬾ÍʹÓÃĬÈÏ˳Ðò + return (attributes != null) ? (Integer) attributes.get("value") + : AutoConfigureOrder.DEFAULT_ORDER; +} + +``` + +Õâ¸ö·½·¨»¹ÊDZȽϼòµ¥µÄ£¬¾ÍÊÇ»ñÈ¡ `@AutoConfigureOrder` ×¢½âÖ¸¶¨µÄ˳Ðò£¬Èç¹ûûÓÐ `@AutoConfigureOrder` ×¢½â£¬¾ÍʹÓÃĬÈÏ˳Ðò£¬Ä¬ÈÏ˳Ðò `AutoConfigureOrder.DEFAULT_ORDER` µÄֵΪ 0¡£ + +### 6\. ʹÓà `@AutoConfigureBefore`£¬`@AutoConfigureAfter` ÅÅÐò + +½ÓÏÂÀ´¾ÍÊÇ×¶¯ÈËÐÄµÄ `@AutoConfigureBefore` Óë `@AutoConfigureAfter` ×¢½âµÄÅÅÐòÁË£¬¶ÔÓ¦µÄ·½·¨Îª `AutoConfigurationSorter#sortByAnnotation`£¬´úÂëÈçÏ£º + +``` +/** + * ½øÐÐÅÅÐò£¬ + * ʵ¼ÊÉÏÕâ¸ö·½·¨ÀïÖ»ÊÇ×¼±¸ÁËһЩÊý¾Ý£¬ÕæÕý¸É»îµÄÊÇ doSortByAfterAnnotation(...) + */ +private List sortByAnnotation(AutoConfigurationClasses classes, List classNames) { + // ÐèÒªÅÅÐòµÄ className + List toSort = new ArrayList<>(classNames); + toSort.addAll(classes.getAllNames()); + // ÅÅÐòºÃµÄ className + Set sorted = new LinkedHashSet<>(); + // ÕýÔÚÅÅÐòÖÐµÄ className + Set processing = new LinkedHashSet<>(); + while (!toSort.isEmpty()) { + // ÕæÕý´¦ÀíÅÅÐòµÄ·½·¨ + doSortByAfterAnnotation(classes, toSort, sorted, processing, null); + } + // ´æÔÚÓÚ¼¯ºÏ sorted ÖУ¬µ«²»´æÔÚÓÚ classNames ÖеÄÔªËØ½«»á±»ÒƳý + sorted.retainAll(classNames); + return new ArrayList<>(sorted); +} + +/** + * ¾ßÌå½øÐÐÅÅÐòµÄ·½·¨ + */ +private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List toSort, + Set sorted, Set processing, String current) { + if (current == null) { + current = toSort.remove(0); + } + // ʹÓà processing À´ÅжÏÊÇ·ñ´æÔÚÑ­»·±È½Ï£¬±ÈÈ磬ÀàA after ÀàB£¬¶ø ÀàB ÓÖ after ÀàA + processing.add(current); + // classes.getClassesRequestedAfter£ºµ±Ç° className ÐèÒªÔÚÄÄЩ className Ö®ºóÖ´ÐÐ + for (String after : classes.getClassesRequestedAfter(current)) { + Assert.state(!processing.contains(after), + "AutoConfigure cycle detected between " + current + " and " + after); + if (!sorted.contains(after) && toSort.contains(after)) { + // µÝ¹éµ÷Óà + doSortByAfterAnnotation(classes, toSort, sorted, processing, after); + } + } + processing.remove(current); + // Ìí¼Óµ½ÒÑÅÅÐò½á¹ûÖÐ + sorted.add(current); +} + +``` + +`AutoConfigurationSorter#sortByAnnotation` ÌṩÁ˱£´æÊý¾ÝµÄ½á¹¹£¬¶ø `AutoConfigurationSorter#doSortByAfterAnnotation` ²ÅÊÇÕæÕý´¦ÀíÅÅÐòµÄ·½·¨£¬ÅÅÐò²Ù×÷²»Ì«ºÃ¶®£¬´óÖÂÁ÷³ÌÈçÏ£º + +1. ²éÕÒµ±Ç° `className` ÐèÒªÔÚÄÄЩ `className` Ö®ºó×°Å䣬½«Æä±£´æÎª `afterClasses`£¬Ò²¾ÍÊÇ˵£¬`afterClasses` ÖеÄÿһ¸ö `className` ¶¼ÒªÔÚµ±Ç° `className` ֮ǰװÅ䣻 + +2. ±éÀú `afterClasses`£¬¶ÔÆäÖÐÿһ¸ö `className`£¬¼ÌÐø²éÕÒÆä `afterClasses`£¬ÕâÑùµÝ¹éÏÂÈ¥£¬²»¿¼ÂÇÑ­»·±È½ÏµÄÇé¿öÏ£¬×îÖÕ±ØÈ»»á´æÔÚÒ»¸ö `className`£¬ËüµÄ `afterClasses` Ϊ¿Õ£¬ÕâÀï¾Í°Ñ `className` ¼ÓÈëµ½ÒÑÍê³ÉÅÅÐòµÄ½á¹¹ÖС£ + +ÎÒÃÇÔÙÀ´¿´¿´»ñÈ¡ `afterClasses` µÄ²Ù×÷£¬·½·¨Îª `AutoConfigurationSorter.AutoConfigurationClasses#getClassesRequestedAfter`£¬´úÂëÈçÏ£º + +``` +Set getClassesRequestedAfter(String className) { + // µ±Ç°Àࣺ»ñÈ¡ÔÚÄÄЩÀàÖ®ºóÖ´ÐУ¬¾ÍÊÇ»ñÈ¡ @AutoConfigureAfter ×¢½âÖ¸¶¨µÄÀà + Set classesRequestedAfter = new LinkedHashSet<>(get(className).getAfter()); + // ÆäËûÀࣺÐèҪǰÖÃÖ´ÐеÄÀàÖÐ + this.classes.forEach((name, autoConfigurationClass) -> { + if (autoConfigurationClass.getBefore().contains(className)) { + classesRequestedAfter.add(name); + } + }); + return classesRequestedAfter; +} + +``` + +´Ó´úÂëÀ´µÄÀ´¿´£¬Õâ¸ö `afterClasses` °üº¬Á½¸öÄÚÈÝ£º + +* »ñÈ¡ÔÚÄÄЩÀà×°ÅäÍê³ÉÖ®ºó×°Å䣬¾ÍÊÇ»ñÈ¡ `@AutoConfigureAfter` ×¢½âÖ¸¶¨µÄÀà +* »ñÈ¡ÄÄЩÀàÐèÒªÔÚµ±Ç°Àà×°Åä֮ǰ½øÐÐ×°Åä + +### 7\. ÔÙÀ´¿´£º`@ConditionalOnBean/@ConditionalOnMissingBean` + +Ç°ÃæÌáµ½ÁË `@ConditionalOnBean/@ConditionalOnMissingBean` µÄ¿Ó£¬Á˽âÍê×Ô¶¯×°ÅäµÄ˳Ðòºó£¬¾ÍÄܺܺùæ±ÜÕâЩ¿ÓÁË£º + +1. Á½¸ö `bean` ¶¼ÊÇ×Ô¶¯×°ÅäÀࣺ±Ü¿Ó·½Ê½ÊÇ£¬Ê¹Óà `@AutoConfigureBefore` / `@AutoConfigureAfter` »ò `@AutoConfigureOrder` Ö¸¶¨Ìõ¼þ˳Ðò£¬±£Ö¤Ìõ¼þ×¢½âÖÐµÄ `bean` ÏÈ×°Åä¼´¿É£» +2. Ò»¸öÊÇÆÕͨ `spring bean`£¬Ò»¸öÊÇ×Ô¶¯×°ÅäÀࣺÈç¹ûÌõ¼þ×¢½âÖÐµÄ `bean` ÊÇÆÕͨ spring bean£¬ÁíÒ»¸öÊÇ×Ô¶¯×°ÅäÀ࣬ÕâÖÖÇé¿öϲ»Óô¦Àí£¬×Ô¶¯×°ÅäµÄ´¦ÀíÀàÊÇ `DeferredImportSelector` µÄ×ÓÀ࣬ÏÈÌì¾ö¶¨×Ô¶¯×°ÅäÀàÔÚÆÕͨ `spring bean` Ö®ºó´¦Àí£»·´Ö® £¬Ìõ¼þ×¢½âÖÐµÄ `bean` ÊÇ×Ô¶¯×°ÅäÀ࣬ÁíÒ»¸öÊÇÆÕͨ `spring bean`£¬ÕâÖÖÒ»¶¨»á³ö´í£¬²»ÒªÊ¹Óã» +3. Á½¸ö¶¼ÊÇÆÕͨ `spring bean`£ºÎޱܿӷ½·¨£¬`spring bean` ×¢²áµ½ `beanFactory` µÄ˳Ðò²»¿É¿Ø£¬²»½¨ÒéÔÚÕâÖÖÇé¿öÏÂʹÓã» + +### 8\. ×ܽá + +±¾ÎÄ×ܽáÁË×Ô¶¯×°ÅäÀàµÄ×°Åä˳Ðò£¬Ö÷Òª½éÉÜÁËÈçÏÂÄÚÈÝ£º + +1. ¶Ô×Ô¶¯×°ÅäÀàÅÅÐò£º`AutoConfigurationImportSelector.AutoConfigurationGroup#sortAutoConfigurations` +2. Ö¸¶¨×Ô¶¯×°ÅäÀàµÄ×°Åä˳Ðò£ºÊ¹Óà `@AutoConfigureBefore` / `@AutoConfigureAfter` »ò `@AutoConfigureOrder` +3. ÅÅÐò·½Ê½ÓÐÈýÖÖ£¬ÒÀ´ÎÊÇ£º + 1. °´ className ÅÅÐò£¬ÓÉ `String` ÌṩÅÅÐò¹æÔò + 2. ¸ù¾Ý `@AutoConfigureOrder` Ö¸¶¨µÄÖµ½øÐÐÅÅÐò£¬ÓÉ `Integer` ÌṩÅÅÐò¹æÔò + 3. ¸ù¾Ý `@AutoConfigureBefore` / `@AutoConfigureAfter` ½øÐÐÅÅÐò ÐèҪעÒâµÄÊÇ£¬ÒÔÉÏÈýÖÖÅÅÐò·½Ê½ÏȺó½øÐУ¬ÒÔ×îºóÅÅÐòÍêµÄ½á¹ûΪ×îÖÕ˳Ðò +4. ¹ØÓÚ `@ConditionalOnBean/@ConditionalOnMissingBean` ±Ü¿ÓÖ¸ÄÏ£º + 1. Á½¸ö `bean` ¶¼ÊÇ×Ô¶¯×°ÅäÀࣺ±Ü¿Ó·½Ê½ÊÇ£¬Ê¹Óà `@AutoConfigureBefore` / `@AutoConfigureAfter` »ò `@AutoConfigureOrder` Ö¸¶¨Ìõ¼þ˳Ðò£¬±£Ö¤Ìõ¼þ×¢½âÖÐµÄ `bean` ÏÈ×°Åä¼´¿É£» + 2. Ò»¸öÊÇÆÕͨ `spring bean`£¬Ò»¸öÊÇ×Ô¶¯×°ÅäÀࣺÌõ¼þ×¢½âÖÐµÄ `bean` ±ØÐëΪÆÕͨµÄ `spring bean`£» + 3. ÆäËûÇé¿ö²»¿É¿Ø£¬²»½¨ÒéʹÓᣠ+ +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4921594](https://my.oschina.net/funcy/blog/4921594) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" new file mode 100644 index 0000000..31d27a9 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringBoot\346\272\220\347\240\201\350\247\243\346\236\220/SpringBoot\350\207\252\345\212\250\350\243\205\351\205\215\357\274\210\344\272\214\357\274\211\357\274\232\346\235\241\344\273\266\346\263\250\350\247\243.md" @@ -0,0 +1,961 @@ +### 1\. Ìõ¼þ×¢½â¼°ÆäÅжÏÀà + +ÔÚ [springboot ×Ô¶¯×°ÅäÖ®¼ÓÔØ×Ô¶¯×°ÅäÀà](https://my.oschina.net/funcy/blog/4870868)Ò»ÎÄÖУ¬ÎÒÃÇ·ÖÎöµ½ springboot »á¼ÓÔØ `META-INF/spring.factories` ÎļþÖж¨ÒåµÄ×Ô¶¯×°ÅäÀ࣬¼ÓÔØµ½ÕâЩ×Ô¶¯×°ÅäÀàºó£¬ÕâЩÀàÖÐµÄ bean ¾ÍÒ»¶¨»á³õʼ»¯Â𣿲¢²»ÊÇ£¬ÎÒÃÇ¿ÉÒÔÔÚ¶ÔÓ¦µÄ Bean Éú³É·½·¨ÉÏʹÓÃ**Ìõ¼þ×¢½â**À´¿ØÖÆÀàÊÇ·ñ½øÐгõʼ»¯£¡ + +springboot ÌṩµÄÌõ¼þ×¢½âÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0e8d27c887fb6ca142672cad1c60e9de207.png) + +ÕâÀïÁоٲ¿·ÖÈçÏ£º + +| ×¢½âÀàÐÍ | ×¢½âÀàÐÍ | ¹¦ÄÜ˵Ã÷ | +| --------------------- | ------------------------------------------------------------ | --------------------------------------------- | +| class Ìõ¼þ×¢½â | `@ConditionalOnClass`/`@ConditionalOnMissingClass` | µ±Ö¸¶¨µÄÀà**´æÔÚ / ȱʧ**ʱ³õʼ»¯¸Ã bean | +| bean Ìõ¼þ×¢½â | `@ConditionalOnBean`/`@ConditionalOnMissingBean` | µ±Ö¸¶¨µÄ bean **´æÔÚ / ȱʧ**ʱ³õʼ»¯¸Ã bean | +| ÊôÐÔÌõ¼þ×¢½â | `@ConditionalOnProperty` | µ±Ö¸¶¨µÄÊôÐÔ´æÔÚ³õʼ»¯¸Ã bean | +| Resource Ìõ¼þ×¢½â | `@ConditionalOnResource` | µ±Ö¸¶¨µÄ×ÊÔ´´æÔÚ³õʼ»¯¸Ã bean | +| Web Ó¦ÓÃÌõ¼þ×¢½â | `@ConditionalOnWebApplication` / `@ConditionalOnNotWebApplication` | µ±Ç°Ó¦ÓÃ**Ϊ / ²»Îª** web Ó¦ÓÃʱ³õʼ»¯¸Ã bean | +| spring ±í´ïʽÌõ¼þ×¢½â | `@ConditionalOnExpression` | µ±±í´ïʽ½á¹ûΪ true ʱ³õʼ»¯¸Ã bean | + +ÎÒÃǽøÈë `@ConditionalOnClass` ¿´¿´¸Ã×¢½âµÄÄÚÈÝ£º + +``` +... +@Conditional(OnClassCondition.class) +public @interface ConditionalOnClass { + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬`ConditionalOnClass` ×éºÏÁË `@Conditional` ×¢½âµÄ¹¦ÄÜ£¬´¦ÀíÀàÊÇ `OnClassCondition.class`¡£ + +¹ØÓÚ `@Conditional` ×¢½â¿ÉÒԲο¼ [ConfigurationClassPostProcessor Ö®´¦Àí @Conditional ×¢½â](https://my.oschina.net/funcy/blog/4873444)£¬ÕâÀïÎÒÃÇÖ±½Ó˵ `@Conditional` µÄʹÓ÷½Ê½£º + +1. `@Conditional` ÊÇ spring ´¦ÀíµÄÌõ¼þ×¢½â£» + +2. `@Conditional` ÌṩÁËÒ»¸öÊôÐÔ `value`£¬ÀàÐÍΪ `Class`£¬Æä±ØÐëÊÇ `Condition` µÄ×ÓÀࣺ + + ``` + Class[] value(); + + ``` + +3. `Condition` ÊÇÒ»¸ö½Ó¿Ú£¬ÆäÖÐÓÐÒ»¸ö `matches(...)` ·½·¨£º + + ``` + public interface Condition { + + boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); + + } + + ``` + + Ö»ÓÐÔÚ `matches(...)` ·½·¨·µ»Ø `true` ʱ£¬`ConfigurationClassPostProcessor` ²Å»á½«Æä¶ÔÓ¦µÄ bean ×¢²áµ½ `beanFactory` µÄ `beanDefinitionMap` ÖÐ. + +×ܽáÍê `@Conditional` µÄʹÓ÷½Ê½ºó£¬ÎÒÃǾÍÃ÷°×ÁË£º`OnClassCondition.class` ÊÇ `Condition` µÄ×ÓÀ࣬Æä `matches(...)` ·½·¨ÓÃÀ´´¦ÀíÖ÷ÒªÌõ¼þ¹æÔò¡£Í¬Àí£¬ÆäËûÌõ¼þ×¢½âµÄ´¦Àí·½Ê½Ò²ÀàËÆ£¬ÕâÀï×ܽáÏÂÌõ¼þ×¢½âµÄÅжÏÀࣺ + +| ×¢½âÀàÐÍ | ×¢½âÀàÐÍ | Ìõ¼þÅжÏÀà | +| --------------------- | ------------------------------------------------------------ | --------------------------- | +| class Ìõ¼þ×¢½â | `@ConditionalOnClass`/`@ConditionalOnMissingClass` | `OnClassCondition` | +| bean Ìõ¼þ×¢½â | `@ConditionalOnBean`/`@ConditionalOnMissingBean` | `OnBeanCondition` | +| ÊôÐÔÌõ¼þ×¢½â | `@ConditionalOnProperty` | `OnPropertyCondition` | +| Resource Ìõ¼þ×¢½â | `@ConditionalOnResource` | `OnResourceCondition` | +| Web Ó¦ÓÃÌõ¼þ×¢½â | `@ConditionalOnWebApplication` / `@ConditionalOnNotWebApplication` | `OnWebApplicationCondition` | +| spring ±í´ïʽÌõ¼þ×¢½â | `@ConditionalOnExpression` | `OnExpressionCondition` | + +½ÓÏÂÀ´£¬·ÖÎöÄ¿µÄ¾ÍºÜÃ÷È·ÁË£ºÒª·ÖÎöÕâЩÌõ¼þ×¢½âµÄÅжÏÂß¼­£¬Ö»ÐèÒª·ÖÎö¶ÔÓ¦Ìõ¼þÅжÏÀàµÄ `matches(...)` ·½·¨¾Í¿ÉÒÔÁË¡£ + +### 2. `SpringBootCondition#matches` + +½øÈë `OnClassCondition#matches` ·½·¨£¬·¢ÏÖÀ´µ½µÄÊÇ `SpringBootCondition`£¬Ïà¹Ø·½·¨ÈçÏ£º + +``` +public abstract class SpringBootCondition implements Condition { + + private final Log logger = LogFactory.getLog(getClass()); + + @Override + public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String classOrMethodName = getClassOrMethodName(metadata); + try { + // »ñÈ¡Ìõ¼þÆ¥Åä½á¹û + ConditionOutcome outcome = getMatchOutcome(context, metadata); + // ´òÓ¡Ò»ÌõÈÕÖ¾ + logOutcome(classOrMethodName, outcome); + // ¼Ç¼Ìõ¼þÆÀ¹ÀµÄ·¢Éú£¬¼òµ¥Àí½âΪ¼Ç¼һÌõÌõ¼þÅжϼǼ°É + recordEvaluation(context, classOrMethodName, outcome); + // ÕâÀï·µ»Ø×îÖÕ½á¹û£ºtrue »ò false + return outcome.isMatch(); + } + catch (NoClassDefFoundError ex) { + throw new IllegalStateException(...); + } + catch (RuntimeException ex) { + throw new IllegalStateException(...); + } + } + + /** + * ÕâÊǸö³éÏó·½Ê½£¬¾ßÌåÄÚÈÝÓÉ×ÓÀàʵÏÖ + */ + public abstract ConditionOutcome getMatchOutcome( + ConditionContext context, AnnotatedTypeMetadata metadata); + + ... + +} + +``` + +`SpringBootCondition` µÄ `matches(...)` ¹Ø¼ü¾ÍÁ½ÐУº + +``` +... +ConditionOutcome outcome = getMatchOutcome(context, metadata); +... +return outcome.isMatch(); + +``` + +¶ø `SpringBootCondition` µÄ `getMatchOutcome(...)` ÓÖÊǸö³éÏó·½·¨£¬¾ßÌåµÄÂß¼­ÓÉ×ÓÀàÌṩ£¬`OnClassCondition` ËüµÄʵÏÖÖ®Ò»¡£Êµ¼ÊÉÏ£¬ÉÏÊöÌõ¼þÅжÏÀà¶¼ÊÇ `SpringBootCondition` µÄ×ÓÀ࣬ºóÃæÎÒÃǾÍÖ±½Ó½øÈë¾ßÌåÀàµÄ `getMatchOutcome(...)` ·½·¨·ÖÎöÁË¡£ + +`getMatchOutcome(...)` ·½·¨·µ»ØµÄ½á¹ûÊÇ `ConditionOutcome`£¬½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `ConditionOutcome` ÊǸöɶ£º + +``` +public class ConditionOutcome { + + private final boolean match; + + private final ConditionMessage message; + + /** + * ¹¹Ôì·½·¨ + */ + public ConditionOutcome(boolean match, String message) { + this(match, ConditionMessage.of(message)); + } + + /** + * ¹¹Ôì·½·¨ + */ + public ConditionOutcome(boolean match, ConditionMessage message) { + Assert.notNull(message, "ConditionMessage must not be null"); + this.match = match; + this.message = message; + } + + /** + * ·µ»ØÆ¥ÅäµÄ½á¹û + */ + public boolean isMatch() { + return this.match; + } + + ... +} + +``` + +´Ó´úÂëÀ´¿´£¬Õâ¸öÀà¾ÍÊÇÓÃÀ´·â×°±È½Ï½á¹ûµÄ£¬ÄÚ²¿ÓÐÁ½¸öÊôÐÔ£º`match` Óë `message`: + +* `match` µÄÀàÐÍÊÇ `boolean`£¬Õâ¸ö¾ÍÊÇ×îÖÕÆ¥Åä³É¹¦»¹ÊÇʧ°ÜµÄ±êʶ +* `message` µÄÀàÐÍÊÇ `ConditionMessage`£¬Ëü±íʾƥÅä½á¹ûµÄ˵Ã÷ + +ÎÒÃÇÔÙÀ´¿´¿´ `ConditionMessage`: + +``` +public final class ConditionMessage { + + private String message; + + private ConditionMessage() { + this(null); + } + + private ConditionMessage(String message) { + this.message = message; + } + + ... +} + +``` + +Ëü½öÓÐÒ»¸öÊôÐÔ£º`message`£¬Õâ±íÃ÷Ëü¾ÍÊǶÔ˵Ã÷ÐÅÏ¢µÄ°ü×°¡£ + +### 3. `@ConditionalOnClass`: `OnClassCondition#getMatchOutcome` + +½ÓÏÂÀ´ÎÒÃÇÀ´·ÖÎö `OnClassCondition` µÄÆ¥ÅäÂß¼­£¬Ö±½Ó½øÈë `getMatchOutcome` ·½·¨£º + +``` +public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ClassLoader classLoader = context.getClassLoader(); + ConditionMessage matchMessage = ConditionMessage.empty(); + // 1\. ´¦Àí @ConditionalOnClass ×¢½â + List onClasses = getCandidates(metadata, ConditionalOnClass.class); + if (onClasses != null) { + // 1.1 ´¦ÀíÌõ¼þÅÐ¶Ï + List missing = filter(onClasses, ClassNameFilter.MISSING, classLoader); + if (!missing.isEmpty()) { + // 1.2 ¹¹½¨·µ»Ø½á¹û£º²»Æ¥ÅäµÄÇé¿ö + return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) + .didNotFind("required class", "required classes").items(Style.QUOTE, missing)); + } + matchMessage = matchMessage.andCondition(ConditionalOnClass.class) + .found("required class", "required classes") + .items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader)); + } + + // 2\. ´¦Àí @ConditionalOnMissingClass ×¢½â + List onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class); + if (onMissingClasses != null) { + // 2.1 ´¦ÀíÌõ¼þÅÐ¶Ï + List present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader); + if (!present.isEmpty()) { + // 2.2 ¹¹½¨·µ»Ø½á¹û£º²»Æ¥ÅäµÄÇé¿ö + return ConditionOutcome.noMatch(ConditionMessage + .forCondition(ConditionalOnMissingClass.class) + .found("unwanted class", "unwanted classes").items(Style.QUOTE, present)); + } + matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class) + .didNotFind("unwanted class", "unwanted classes") + .items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader)); + } + // ×îºó·µ»ØÆ¥ÅäµÄ½á¹û + return ConditionOutcome.match(matchMessage); +} + +``` + +Õâ¸ö·½·¨Í¬Ê±´¦ÀíÁË `@ConditionalOnClass` Óë `@ConditionalOnMissingClass` Á½¸ö×¢½â£¬´¦ÀíÁ÷³Ì¼«ÆäÏàËÆ£¬Á½¸ö×¢½âµÄÌõ¼þÅж϶¼ÊÇͨ¹ý `FilteringSpringBootCondition#filter` ÄÚÈÝÈçÏ£º + +``` +protected final List filter(Collection classNames, ClassNameFilter classNameFilter, + ClassLoader classLoader) { + if (CollectionUtils.isEmpty(classNames)) { + return Collections.emptyList(); + } + List matches = new ArrayList<>(classNames.size()); + for (String candidate : classNames) { + // ½øÐÐÌõ¼þÆ¥Åä + if (classNameFilter.matches(candidate, classLoader)) { + matches.add(candidate); + } + } + return matches; +} + +``` + +Óɴ˿ɼû£¬´«ÈëµÄ `classNameFilter` ³ÉÁ˹ؼü£º + +* ´¦Àí `@ConditionalOnClass` ʱ£¬`classNameFilter` Ϊ `ClassNameFilter.MISSING` +* ´¦Àí `@ConditionalOnMissingClass` ʱ£¬`classNameFilter` Ϊ `ClassNameFilter.PRESENT` + +ÈÃÎÒÃǽøÈë `ClassNameFilter` һ̽¾¿¾¹£¬ËüÊÇ `FilteringSpringBootCondition` µÄ×ÓÀ࣬ÄÚÈÝÈçÏ£º + +``` +abstract class FilteringSpringBootCondition extends SpringBootCondition + implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware { + ... + /** + * Èç¹û classLoader ´æÔÚ£¬Ôòµ÷Óà ClassLoader#loadClass ·½·¨ + * ·ñÔòµ÷Óà Class#forName ·½·¨ + */ + protected static Class resolve(String className, ClassLoader classLoader) + throws ClassNotFoundException { + if (classLoader != null) { + return classLoader.loadClass(className); + } + return Class.forName(className); + } + + /** + * ´¦ÀíÌõ¼þÆ¥Åä + */ + protected enum ClassNameFilter { + + PRESENT { + + @Override + public boolean matches(String className, ClassLoader classLoader) { + return isPresent(className, classLoader); + } + + }, + + MISSING { + + @Override + public boolean matches(String className, ClassLoader classLoader) { + return !isPresent(className, classLoader); + } + + }; + + abstract boolean matches(String className, ClassLoader classLoader); + + /** + * Class ÊÇ·ñ´æÔÚ + * ͨ¹ý²¶»ñÀà¼ÓÔØÊ±µÄÒì³£À´ÅжÏÀàÊÇ·ñ´æÔÚ£¬Î´Å׳öÒì³£Ôò±íʾÀà´æÔÚ + */ + static boolean isPresent(String className, ClassLoader classLoader) { + if (classLoader == null) { + classLoader = ClassUtils.getDefaultClassLoader(); + } + try { + // ͨ¹ýÒì³£²¶»ñÀ´ÅжÏÊÇ·ñ´æÔÚ¸Ãclass + resolve(className, classLoader); + return true; + } + catch (Throwable ex) { + return false; + } + } + } + ... +} + +``` + +¿´µ½ÕâÀïÎÒÃǾÍÃ÷°×ÁË£ºÅÐ¶Ï `Class` ÊÇ·ñ´æÔÚ£¬spring ÊÇͨ¹ý²¶»ñ `ClassLoader.load(String)` »ò `Class.forName(String)` ·½·¨µÄÒì³£À´´¦ÀíµÄ£¬Èç¹ûÅ׳öÁËÒì³£¾Í±íÃ÷ `Class` ²»´æÔÚ¡£ + +ÕâÀï×ܽáÏ `@ConditionalOnClass`/`@ConditionalOnMissingClass` µÄ´¦Àí·½Ê½£º**Á½ÕߵĴ¦ÀíÀ඼Ϊ `OnClassCondition`£¬Í¨¹ý²¶»ñ `ClassLoader.load(String)` »ò `Class.forName(String)` ·½·¨µÄÒì³£À´ÅÐ¶Ï `Class` ÊÇ·ñ´æÔÚ£¬Èç¹ûÅ׳öÁËÒì³£¾Í±íÃ÷ `Class` ²»´æÔÚ**¡£ + +### 4. `@ConditionalOnBean`: `OnBeanCondition#getMatchOutcome` + +¼ÌÐø¿´¿´ `@ConditionalOnBean` µÄ ´¦Àí£¬Ö±½Ó½øÈë `OnBeanCondition#getMatchOutcome`£º + +``` +public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ConditionMessage matchMessage = ConditionMessage.empty(); + MergedAnnotations annotations = metadata.getAnnotations(); + // ´¦Àí @ConditionalOnBean + if (annotations.isPresent(ConditionalOnBean.class)) { + Spec spec = new Spec<>(context, metadata, + annotations, ConditionalOnBean.class); + // ´¦ÀíÆ¥Åä + MatchResult matchResult = getMatchingBeans(context, spec); + // ×¢ÒâÅжÏÌõ¼þ + if (!matchResult.isAllMatched()) { + String reason = createOnBeanNoMatchReason(matchResult); + return ConditionOutcome.noMatch(spec.message().because(reason)); + } + matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE, + matchResult.getNamesOfAllMatches()); + } + + // ´¦Àí @ConditionalOnSingleCandidate + if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) { + Spec spec + = new SingleCandidateSpec(context, metadata, annotations); + // ´¦ÀíÆ¥Åä + MatchResult matchResult = getMatchingBeans(context, spec); + // ×¢ÒâÅжÏÌõ¼þ + if (!matchResult.isAllMatched()) { + return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll()); + } + else if (!hasSingleAutowireCandidate(context.getBeanFactory(), + matchResult.getNamesOfAllMatches(), spec.getStrategy() == SearchStrategy.ALL)) { + return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans") + .items(Style.QUOTE, matchResult.getNamesOfAllMatches())); + } + matchMessage = spec.message(matchMessage).found("a primary bean from beans") + .items(Style.QUOTE, matchResult.getNamesOfAllMatches()); + } + + // ´¦Àí @ConditionalOnMissingBean + if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) { + Spec spec = new Spec<>(context, metadata, annotations, + ConditionalOnMissingBean.class); + // ´¦ÀíÆ¥Åä + MatchResult matchResult = getMatchingBeans(context, spec); + // ×¢ÒâÅжÏÌõ¼þ + if (matchResult.isAnyMatched()) { + String reason = createOnMissingBeanNoMatchReason(matchResult); + return ConditionOutcome.noMatch(spec.message().because(reason)); + } + matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll(); + } + return ConditionOutcome.match(matchMessage); +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨Ò»¹²´¦ÀíÁËÁ½¸ö×¢½âµÄÌõ¼þÆ¥Å䣺`@ConditionalOnBean`¡¢`@ConditionalOnSingleCandidate` Óë `@ConditionalOnMissingBean`£¬ÈýÕß¶¼µ÷ÓÃÁËͬһ¸ö·½·¨ `getMatchingBeans(...)` À´»ñȡƥÅä½á¹û£¬È»ºóʹÓà `matchResult.isAllMatched()` »ò `matchResult.isAnyMatched()` À´×ö×îÖյĽá¹ûÅжϡ£ + +#### `OnBeanCondition#getMatchingBeans` + +`getMatchingBeans(...)` µÄ´úÂëÈçÏ£º + +``` +protected final MatchResult getMatchingBeans(ConditionContext context, Spec spec) { + ClassLoader classLoader = context.getClassLoader(); + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT; + Set> parameterizedContainers = spec.getParameterizedContainers(); + if (spec.getStrategy() == SearchStrategy.ANCESTORS) { + BeanFactory parent = beanFactory.getParentBeanFactory(); + Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, + "Unable to use SearchStrategy.ANCESTORS"); + beanFactory = (ConfigurableListableBeanFactory) parent; + } + MatchResult result = new MatchResult(); + // 1\. »ñÈ¡ ignoreType£¬Ö»ÓÐ @ConditionalOnMissingBean ÓÐÕâ¸öÊôÐÔ + Set beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, + considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); + + // 2\. ´¦Àí types + for (String type : spec.getTypes()) { + Collection typeMatches = getBeanNamesForType(classLoader, considerHierarchy, + beanFactory, type, parameterizedContainers); + typeMatches.removeAll(beansIgnoredByType); + if (typeMatches.isEmpty()) { + result.recordUnmatchedType(type); + } + else { + result.recordMatchedType(type, typeMatches); + } + } + + // 3\. ´¦ÀíÀàÉϵÄ×¢½â @ConditionalOnMissingBean ÓÐÕâ¸öÊôÐÔ + for (String annotation : spec.getAnnotations()) { + Set annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, + annotation, considerHierarchy); + annotationMatches.removeAll(beansIgnoredByType); + if (annotationMatches.isEmpty()) { + result.recordUnmatchedAnnotation(annotation); + } + else { + result.recordMatchedAnnotation(annotation, annotationMatches); + } + } + + // 4\. ´¦Àí beanName + for (String beanName : spec.getNames()) { + if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, + considerHierarchy)) { + result.recordMatchedName(beanName); + } + else { + result.recordUnmatchedName(beanName); + } + } + return result; +} + +``` + +ÐèҪ˵Ã÷µÄÊÇ£¬Õâ¸ö·½·¨»á´¦Àí 3 ¸ö×¢½âµÄÆ¥Å乿Ôò£º`@ConditionalOnBean`¡¢`@ConditionalOnSingleCandidate` Óë `@ConditionalOnMissingBean`£¬´¦Àí²½ÖèÈçÏ£º + +1. »ñÈ¡ `ignoreType`£¬Ö»ÓÐ `@ConditionalOnMissingBean` ÓÐÕâ¸öÊôÐÔ +2. ´¦Àí `types` µÄÆ¥Å乿Ôò +3. ´¦Àí×¢½â£¨ÀàÉϵÄ×¢½â£©µÄÆ¥Å乿Ôò£¬ Ö»ÓÐ `@ConditionalOnMissingBean` ÓÐÕâ¸öÊôÐÔ +4. ´¦Àí `beanName` µÄÆ¥Å乿Ôò + +¹ØÓÚÒÔÉϲ½ÖèµÄ¾ßÌåϸ½Ú£¬±¾ÎľͲ»¾ßÌåÕ¹¿ªÁË£¬ÕâÀï½öÌṩÁ÷³Ì£º + +1. »ñÈ¡ `ignoreType`£º + 1. ʹÓà `ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)` ·½·¨»ñÈ¡ÈÝÆ÷ÖÐËùÓÐµÄ `ignoreType` µÄ `beanName` + 2. ½á¹ûΪ `beansIgnoredByType`(ÀàÐÍÊÇ `Set`) +2. ´¦Àí `types` µÄÆ¥Å乿Ôò + 1. ʹÓà `ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)` ·½·¨»ñÈ¡ÈÝÆ÷ÖÐËùÓÐµÄ `type` ¶ÔÓ¦µÄ `beanName`£¬½á¹ûΪ `typeMatches` + 2. ½« `typeMatches` ÖеÄֵȥ³ý `ignoreType` + 3. Åжϵڶþ²½µÃµ½µÄ `typeMatches`£¬Èç¹ûÄÚÈÝΪ¿Õ£¬½«µ±Ç° `Type` ±£´æµ½ `unmatchedTypes` ÖУ¬·ñÔò±£´æµ½ `matchedTypes` Óë `namesOfAllMatches` ÖÐ +3. ´¦Àí×¢½âµÄÆ¥Å乿Ôò + 1. ʹÓà `ListableBeanFactory#getBeanNamesForAnnotation` ·½·¨»ñÈ¡ÈÝÆ÷ÖÐËùÓÐµÄ `annotation` ¶ÔÓ¦µÄ `beanName`£¬½á¹ûΪ `annotationMatches` + 2. ½« `annotationMatches` ÖеÄֵȥ³ý `ignoreType` + 3. Åжϵڶþ²½µÃµ½µÄ `annotationMatches`£¬Èç¹ûÄÚÈÝΪ¿Õ£¬½«µ±Ç° `Annotation` ±£´æµ½ `unmatchedAnnotations` ÖУ¬·ñÔò±£´æµ½ `matchedAnnotations` Óë `namesOfAllMatches` ÖÐ +4. ´¦Àí `beanName` µÄÆ¥Å乿Ôò + 1. ÅÐ¶Ï `beansIgnoredByType` ÊÇ·ñ°üº¬ `beanName` + 2. ʹÓà `BeanFactory#containsBean` ·½·¨ÅжÏÈÝÆ÷ÖÐÓиà `beanName` + 3. Èç¹ûµÚ 2 ²½½á¹ûΪ `false`£¬µÚ¶þ²½½á¹ûΪ `true`£¬Ôò½«µ±Ç° `beanName` ¼ÓÈëµ½ `matchedNames` Óë `namesOfAllMatches`£¬·ñÔò±£´æµ½ `unmatchedNames` ÖÐ + +µÃµ½ `matchedTypes`¡¢`unmatchedNames` µÈÄÚÈݺó£¬`matchResult.isAllMatched()` »ò `matchResult.isAnyMatched()` ×îÖÕµÄÅжϽá¹û¾ÍÊÇÅжÏÕâЩ½á¹¹ÊÇ·ñ¿Õ£º + +``` +boolean isAllMatched() { + return this.unmatchedAnnotations.isEmpty() && this.unmatchedNames.isEmpty() + && this.unmatchedTypes.isEmpty(); +} + +boolean isAnyMatched() { + return (!this.matchedAnnotations.isEmpty()) || (!this.matchedNames.isEmpty()) + || (!this.matchedTypes.isEmpty()); +} + +``` + +¿´À´£¬`@ConditionalOnBean`/`@ConditionalOnMissingBean` µÄ¹Ø¼ü£¬¾ÍÊÇʹÓà `ListableBeanFactory#getBeanNamesForType` »ò `BeanFactory#containsBean` À´ÅÐ¶Ï `beanName`¡¢`beanType` ÊÇ·ñ´æÔÚÁË¡£ + +ÔÚʹÓà `@ConditionalOnBean`/`@ConditionalOnMissingBean` ʱ£¬ÓÐÒ»¸ö¿ÓÐèÒªÌØ±ð×¢Ò⣺Ìõ¼þ×¢½âµÄÖ´ÐÐʱ»úÊÇÔÚ spring µÄ `ConfigurationClassPostProcessor` Öеģ¬È·ÇеØËµ£¬ÊÇÔÚ½« `bean` ¼ÓÈëµ½ `beanFactory` µÄ `beanDefinitionMap` ֮ǰÅжϵģ¬Èç¹ûÂú×ãÌõ¼þÔòÌí¼Óµ½ `beanDefinitionMap` ÖУ¬·ñÔò¾Í²»Ìí¼Ó¡£ÕâÑù¾Íµ¼ÖÂÁËÒ»¸öÎÊÌ⣺Èç¹ûÔÚ `@ConditionalOnBean`/`@ConditionalOnMissingBean` µÄ `bean` Ôڸà `bean` Ö®ºó¼ÓÈëµ½ `beanDefinitionMap` ÖУ¬¾ÍÓпÉÄܳöÏÖÎóÅУ¬¾ÙÀý˵Ã÷£º + +ÏÖÔÚÓÐÁ½¸öÀࣺ + +``` +@Component +@ConditionalOnMissingBean("b") +public class A { + +} + +@Component +public class B { + +} + +``` + +ÆäÖÐ `A` Óë `B` ¶¼Ìí¼ÓÁË `@Component`£¬±íÃ÷ÕâÊÇ spring bean£¬È»ºóÔÚ `A` ÉÏÌí¼ÓÁË×¢½â `@ConditionalOnMissingBean("b")`£¬±íÃ÷ÔÚ `b` ²»´æÔÚʱ£¬`A` ²Å½øÐгõʼ»¯¡£ÓÐÁËÕâЩǰÌᣬÎÒÃÇÔÙÀ´¿´¿´Á½ÖÖÇé¿ö£º + +1. Èç¹û `b` ÏÈÌí¼Óµ½ `beanDefinitionMap` ÖУ¬ÔÚ½« `a` Ìí¼Óµ½ `beanDefinitionMap` ʱ£¬·¢ÏÖ `b` ÒѾ­´æÔÚÁË£¬ÓÚÊǾͲ»Ìí¼ÓÁË£¬·ûºÏÎÒÃǵÄÔ¤ÆÚ£» +2. Èç¹û `a` Ïȱ»´¦Àí£¬ÔÚÌí¼Óʱ£¬·¢ÏÖ `beanDefinitionMap` Öв¢Ã»ÓÐ `b`£¬ÓÚÊÇ `a` ±»Ìí¼Óµ½ `beanDefinitionMap` ÖУ¬ÔÙ´¦Àí `b`£¬`b` Ò²»á±»Ìí¼Óµ½ `beanDefinitionMap`£¬ÕâÑùÒ»À´£¬`a` Óë `b` ͬʱ´æÔÚÓÚ `beanDefinitionMap` ÖУ¬×îÖÕ¶¼»á±»³õʼ»¯³É spring bean£¬ÕâÓëÎÒÃǵÄÔ¤ÆÚ²»·û¡£ + +ÄÇô springboot ÈçºÎ½â¾öÒÔÉÏÎÊÌâÄØ£¿ÎÒÃÇÀ´¿´¿´ `@ConditionalOnBean`/`@ConditionalOnMissingBean` µÄ˵Ã÷£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-30df67cb01c73a6b201695298aad14fd0a5.png) + +ÉÔ΢·­ÒëÈçÏ£º + +¸ÃÌõ¼þÖ»ÄÜÆ¥Å䵽ĿǰΪֹµÄÓ¦ÓóÌÐòÉÏÏÂÎÄÖÐµÄ bean ´æÔÚÇé¿ö£¬Òò´Ë£¬Ç¿ÁÒ½¨Òé½öÓÚ×Ô¶¯ÅäÖÃÀàÖÐʹÓá£Èç¹ûºòÑ¡ bean ÒªÔÚÁíÒ»ÖÖ×Ô¶¯ÅäÖÃÏ´´½¨£¬ÇëÈ·±£Ê¹ÓôËÌõ¼þµÄÅäÖÃÔÚ´ËÖ®ºóÔËÐС£ + +¶ÔÒÔÉÏÄÚÈÝ£¬ÎҵĽâ¶ÁÈçÏ£º + +* ±» `@ConditionalOnBean`/`@ConditionalOnMissingBean` ±ê¼Ç `bean` ¼ÓÈëµ½ `beanDefinitionMap` ÄÇÒ»¿Ì£¬½öÆ¥ÅäĿǰΪֹ `beanDefinitionMap` ÖÐÒÑ´æÔÚµÄ bean£¬¶ÔÖ®ºó¼ÓÈëµÄ bean ²»¿¼ÂÇ£¬Õâ¾ÍÓпÉÄÜÔì³ÉÎóÅУ¬¿ÉÒԲο¼ÉÏÃæ¾ÙµÄ `a` Óë `b` µÄÀý×Ó +* Ç¿ÁÒ½¨Òé½öÔÚ×Ô¶¯ÅäÖÃÀàÖÐʹÓà `@ConditionalOnBean`/`@ConditionalOnMissingBean` ÕâÁ½¸ö×¢½â£¬Ò²¾ÍÊÇ˵ÔÚ×Ô¶¯ÅäÖÃÀàÖÐʹÓõϰ£¬ÄÜÕýÈ·´¦ÀíÆ¥Åä +* »¹ÊÇÄÃÉÏÃæµÄ `a` Óë `b` ¾ÙÀý£¬Èç¹û `a` Óë `b` ·Ö±ðλÓÚ²»Í¬µÄ×Ô¶¯ÅäÖÃÀàÖУ¬ÄÇô `a` ÐèÒªÔÚ `b` Ö®ºó¼ÓÔØµ½ `beanDefinitionMap` ÖУ¬Õâ¸ö¿ÉÒÔͨ¹ý `@AutoConfigureAfter`¡¢`@AutoConfigureBefore`¡¢`@AutoConfigureOrder` µÈ×¢½âÀ´Ö¸¶¨ + +¹ØÓÚ×Ô¶¯ÅäÖÃÀàµÄ¼ÓÔØË³Ðò£¬ºóÃæÔÙ×ö·ÖÎö°É¡£ + +ÏÞÓÚÆª·ù£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬ÏÂÆª¼ÌÐø·ÖÎöʣϵÄÌõ¼þ×¢½â¡£ + +* * * + +±¾ÎÄÊÇ springboot Ìõ¼þ×¢½â·ÖÎöµÄµÚ¶þƪ£¬ÉÏÎÄÎÒÃÇ×ܽáÁË springboot µÄ¼¸¸öÌõ¼þ×ܽ᣺ + +| ×¢½âÀàÐÍ | ×¢½âÀàÐÍ | Ìõ¼þÅжÏÀà | +| --------------------- | ------------------------------------------------------------ | --------------------------- | +| class Ìõ¼þ×¢½â | `@ConditionalOnClass`/`@ConditionalOnMissingClass` | `OnClassCondition` | +| bean Ìõ¼þ×¢½â | `@ConditionalOnBean`/`@ConditionalOnMissingBean` | `OnBeanCondition` | +| ÊôÐÔÌõ¼þ×¢½â | `@ConditionalOnProperty` | `OnPropertyCondition` | +| Resource Ìõ¼þ×¢½â | `@ConditionalOnResource` | `OnResourceCondition` | +| Web Ó¦ÓÃÌõ¼þ×¢½â | `@ConditionalOnWebApplication` / `@ConditionalOnNotWebApplication` | `OnWebApplicationCondition` | +| spring ±í´ïʽÌõ¼þ×¢½â | `@ConditionalOnExpression` | `OnExpressionCondition` | + +±¾ÎļÌÐø·ÖÎöÌõ¼þÅжϡ£ + +### 5. `@ConditionalOnProperty`£º`OnPropertyCondition#getMatchOutcome` + +ÎÒÃÇÔÙÀ´¿´¿´ `@ConditionalOnProperty` µÄ´¦Àí£¬½øÈë `OnPropertyCondition#getMatchOutcome` ·½·¨£º + +``` +class OnPropertyCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + // »ñÈ¡ @ConditionalOnProperty µÄÊôÐÔÖµ + List allAnnotationAttributes = annotationAttributesFromMultiValueMap( + metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName())); + List noMatch = new ArrayList<>(); + List match = new ArrayList<>(); + for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) { + // ÔÚ determineOutcome(...) ·½·¨ÖнøÐÐÅжϣ¬×¢Òâ²ÎÊý£ºcontext.getEnvironment() + ConditionOutcome outcome = determineOutcome(annotationAttributes, + context.getEnvironment()); + (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage()); + } + if (!noMatch.isEmpty()) { + return ConditionOutcome.noMatch(ConditionMessage.of(noMatch)); + } + return ConditionOutcome.match(ConditionMessage.of(match)); + } + + ... + +} + +``` + +Õâ¸ö·½·¨»¹ÊDZȽϼòµ¥µÄ£¬ÏÈÊÇ»ñÈ¡ `@ConditionalOnProperty` µÄÊôÐÔÖµ£¬ÔÙµ÷Óà `determineOutcome(...)` ·½·¨½øÐд¦Àí£¬ÈÃÎÒÃÇÔÙ½øÐÐ `OnPropertyCondition#determineOutcome` ·½·¨£º + +``` +/** + * ´¦Àí½á¹û + * ×¢Ò⣺resolver ´«ÈëµÄµÄÊÇ Environment£¬Õâ¾ÍÊÇ applicationContext ÖÐµÄ Environment + */ +private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, + PropertyResolver resolver) { + Spec spec = new Spec(annotationAttributes); + List missingProperties = new ArrayList<>(); + List nonMatchingProperties = new ArrayList<>(); + // ´¦Àí²Ù×÷ + spec.collectProperties(resolver, missingProperties, nonMatchingProperties); + // ÅжϽá¹û + if (!missingProperties.isEmpty()) { + return ConditionOutcome.noMatch(ConditionMessage + .forCondition(ConditionalOnProperty.class, spec) + .didNotFind("property", "properties").items(Style.QUOTE, missingProperties)); + } + // ÅжϽá¹û + if (!nonMatchingProperties.isEmpty()) { + return ConditionOutcome.noMatch(ConditionMessage + .forCondition(ConditionalOnProperty.class, spec) + .found("different value in property", "different value in properties") + .items(Style.QUOTE, nonMatchingProperties)); + } + // ÅжϽá¹û + return ConditionOutcome.match(ConditionMessage + .forCondition(ConditionalOnProperty.class, spec).because("matched")); +} + +/** + * ´¦ÀíÊôÐÔ + */ +private void collectProperties(PropertyResolver resolver, List missing, + List nonMatching) { + for (String name : this.names) { + String key = this.prefix + name; + // resolver ´«ÈëµÄ environment + // properties Ìõ¼þÅжϾÍÊÇÅÐ¶Ï environment ÀïÓÐûÓÐÏàÓ¦ÊôÐÔ + if (resolver.containsProperty(key)) { + if (!isMatch(resolver.getProperty(key), this.havingValue)) { + nonMatching.add(name); + } + } + else { + if (!this.matchIfMissing) { + missing.add(name); + } + } + } +} + +``` + +¿ÉÒÔ¿´µ½£¬`@ConditionalOnProperty` ×îÖÕÊÇͨ¹ýÅÐ¶Ï `environment` ÖÐÊÇ·ñÓиÃÊôÐÔÀ´´¦ÀíÌõ¼þÅжϵġ£ + +### 6. `@ConditionalOnResource`£º`OnResourceCondition#getMatchOutcome` + +ÎÒÃÇÔÙÀ´¿´¿´ `@ConditionalOnResource` µÄ´¦Àí£¬Ò»°ãÎÒÃÇÕâÑùʹÓ㺠+ +``` +@Bean +@ConditionalOnResource(resources = "classpath:config.properties") +public Config config() { + return config; +} + +``` + +±íʾµ± `classpath` ÖдæÔÚ `config.properties` ʱ£¬`config` ²Å»á±»³õʼ»¯ springbean¡£ + +ÔÙ½øÈë `OnResourceCondition#getOutcomes` ·½·¨£º + +``` +@Override +public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + MultiValueMap attributes = metadata + .getAllAnnotationAttributes(ConditionalOnResource.class.getName(), true); + // »ñÈ¡ ResourceLoader + ResourceLoader loader = context.getResourceLoader(); + List locations = new ArrayList<>(); + collectValues(locations, attributes.get("resources")); + Assert.isTrue(!locations.isEmpty(), + "@ConditionalOnResource annotations must specify at least one resource location"); + List missing = new ArrayList<>(); + // ±éÀúÅжÏ×ÊÔ´ÊÇ·ñ´æÔÚ + for (String location : locations) { + // location ÖпÉÄÜÓÐռλ·û£¬ÔÚÕâÀï´¦Àí + String resource = context.getEnvironment().resolvePlaceholders(location); + // ÅÐ¶Ï resource ÊÇ·ñ´æÔÚ + if (!loader.getResource(resource).exists()) { + missing.add(location); + } + } + // ´¦Àí½á¹û + if (!missing.isEmpty()) { + return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnResource.class) + .didNotFind("resource", "resources").items(Style.QUOTE, missing)); + } + return ConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnResource.class) + .found("location", "locations").items(locations)); +} + +``` + +ÏÈÊÇͨ¹ý `OnResourceCondition#getOutcomes` ·½·¨À´»ñÈ¡ `ResourceLoader`£¬Í¨¹ýµ÷ÊÔ·½Ê½·¢ÏÖµ±Ç°µÄ `ResourceLoader` Ϊ `AnnotationConfigServletWebServerApplicationContext`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8f1de99f757eca7aeb0307b06b28d1020d2.png) + +»ñÈ¡µ½ `ResourceLoader` ºó£¬µ÷Óà `ResourceLoader#getResource(String)` À´»ñÈ¡×ÊÔ´£¬È»ºóµ÷Óà `Resource#exists` À´ÅжÏ×ÊÔ´ÊÇ·ñ´æÔÚ£¬×îºó´¦ÀíÆ¥Åä½á¹û¡£ + +Õû¸öÁ÷³ÌµÄ¹Ø¼üÊÇÔÚ `ResourceLoader#getResource(String)`£¬ÎÒÃÇÀ´¿´¿´¸Ã·½·¨µÄ´¦Àí£¬½øÈëµ½ `GenericApplicationContext#getResource` ·½·¨£º + +``` +@Override +public Resource getResource(String location) { + if (this.resourceLoader != null) { + return this.resourceLoader.getResource(location); + } + return super.getResource(location); +} + +``` + +ÕâÀïµÄ `this.resourceLoader` Ϊ `null`£¬½øÈ븸ÀàµÄ·½·¨ `DefaultResourceLoader#getResource`£º + +``` +public Resource getResource(String location) { + Assert.notNull(location, "Location must not be null"); + for (ProtocolResolver protocolResolver : getProtocolResolvers()) { + Resource resource = protocolResolver.resolve(location, this); + if (resource != null) { + return resource; + } + } + // ´¦Àí/¿ªÍ·µÄ×ÊÔ´ + if (location.startsWith("/")) { + return getResourceByPath(location); + } + else if (location.startsWith(CLASSPATH_URL_PREFIX)) { + // ´¦Àíclasspath¿ªÍ·µÄ×ÊÔ´ + return new ClassPathResource( + location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); + } + else { + try { + // ÒÔÉ϶¼²»Âú×㣬ʹÓà url À´½âÎö + URL url = new URL(location); + return (ResourceUtils.isFileURL(url) + ? new FileUrlResource(url) : new UrlResource(url)); + } + catch (MalformedURLException ex) { + // url½âÎö³öÁËÎÊÌ⣬×îÖÕ»¹ÊÇÓà getResourceByPath(...) À´½âÎö + return getResourceByPath(location); + } + } +} + +/** + * ͨ¹ý·¾¶µÃµ½ Resource + */ +protected Resource getResourceByPath(String path) { + return new ClassPathContextResource(path, getClassLoader()); +} + +``` + +¿ÉÒÔ¿´µ½£¬`DefaultResourceLoader#getResource` ͨ¹ýÅÐ¶Ï `location` µÄǰ׺£¬µÃµ½ÁË 4 ÖÖ `Resource`£º + +* `ClassPathContextResource` +* `FileUrlResource` +* `UrlResource` + +µÃµ½ `Resource` ºó£¬½ÓמÍÊÇÅжϸà `Resource` ÊÇ·ñ´æÔÚÁË£¬ÎÒÃÇÏÈÀ´¿´¿´ `ClassPathContextResource#exist` ·½·¨£¬¸Ã·½·¨ÔÚ `ClassPathResource#exists`£º + +``` +/** + * ÅÐ¶Ï Resource ÊÇ·ñ´æÔÚ + */ +@Override +public boolean exists() { + return (resolveURL() != null); +} + +/** + * ×ÊÔ´ÄÜ»ñÈ¡µ½£¬Ôò·µ»Ø×ÊÔ´¶ÔÓ¦µÄurl£¬·ñÔò·µ»Ønull + */ +@Nullable +protected URL resolveURL() { + if (this.clazz != null) { + // ʹÓõ±Ç°µÄ class ¶ÔÓ¦µÄ classLoader À´»ñÈ¡ + return this.clazz.getResource(this.path); + } + else if (this.classLoader != null) { + // ʹÓÃÖ¸¶¨µÄ classLoader À´»ñÈ¡ + return this.classLoader.getResource(this.path); + } + else { + // »ñȡϵͳÀà¼ÓÔØÆ÷»ñÈ¡ + return ClassLoader.getSystemResource(this.path); + } +} + +``` + +´Ó´úÂë¿ÉÒÔ¿´µ½£¬×îÖÕÊÇͨ¹ý `classLoader` »ñÈ¡ÎļþµÄ `url`£¬Í¨¹ýÅжÏÎļþ `url` ÊÇ·ñΪ `null` À´ÅÐ¶Ï `resource` ÊÇ·ñ´æÔÚ¡£ + +ÔÙÀ´¿´¿´ `FileUrlResource` µÄÅжϣ¬Êµ¼ÊÉÏ `FileUrlResource` Óë `UrlResource` µÄ `exist()` ·½·¨¶¼ÊÇ `AbstractFileResolvingResource#exists`£¬ÕâÀïͳһ·ÖÎö¾Í¿ÉÒÔÁË£¬¸Ã·½·¨ÄÚÈÝÈçÏ£º + +``` +public boolean exists() { + try { + URL url = getURL(); + if (ResourceUtils.isFileURL(url)) { + // Èç¹ûÊÇÎļþ£¬Ö±½ÓÅжÏÎļþÊÇ·ñ´æÔÚ + return getFile().exists(); + } + else { + // ·ñÔòʹÓÃÍøÂçÎļþÀ´´¦Àí + URLConnection con = url.openConnection(); + customizeConnection(con); + HttpURLConnection httpCon = + (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); + // Èç¹ûÊÇhttp£¬ÔòÅжϿ´¿´Á´½Ó·µ»ØµÄ״̬Âë + if (httpCon != null) { + int code = httpCon.getResponseCode(); + if (code == HttpURLConnection.HTTP_OK) { + return true; + } + else if (code == HttpURLConnection.HTTP_NOT_FOUND) { + return false; + } + } + // Á¬½Ó contentLengthLong ´óÓÚ0£¬Ò²µ±³ÉÊÇtrue + if (con.getContentLengthLong() > 0) { + return true; + } + if (httpCon != null) { + httpCon.disconnect(); + return false; + } + else { + getInputStream().close(); + return true; + } + } + } + catch (IOException ex) { + return false; + } +} + +``` + +Èç¹ûÊDZ¾µØÎļþ£¬Ö±½ÓʹÓà `File#exists()` ·½·¨ÅжÏÎļþÊÇ·ñ´æÔÚ£¬·ñÔò¾ÍÅжÏÍøÂçÎļþÊÇ·ñ´æÔÚ£¬ÅжϷ½Ê½ÕâÀï¾Í²»Ï¸ËµÁË¡£ + +×ܵÄÀ´Ëµ£¬springboot ¶Ô `@ConditionalOnResource` µÄÅжϻ¹ÊÇÓÐЩ¸´Ôӵģ¬ÕâÀï×ܽáÈçÏ£º + +1. Èç¹ûÊÇ `classpath` Îļþ£¬Í¨¹ý `classloader` »ñÈ¡Îļþ¶ÔÓ¦µÄ `url` ÊÇ·ñΪ `null` À´ÅжÏÎļþÊÇ·ñ´æÔÚ£» +2. Èç¹ûÊÇÆÕͨÎļþ£¬ÔòÖ±½Ó `File#exists()` ·½·¨ÅжÏÎļþÊÇ·ñ´æÔÚ£» +3. Èç¹ûÊÇÍøÂçÎļþ£¬ÏÈ´ò¿ªÒ»¸öÍøÂçÁ¬½Ó£¬ÅжÏÎļþÊÇ·ñ´æÔÚ¡£ + +### 7. `@ConditionalOnWebApplication`£º`OnWebApplicationCondition#getMatchOutcome` + +ÎÒÃÇÔÙÀ´¿´¿´ `@ConditionalOnWebApplication` µÄ´¦Àí£¬½øÈë `OnWebApplicationCondition#getOutcomes` ·½·¨£º + +``` +@Override +protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + AutoConfigurationMetadata autoConfigurationMetadata) { + ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; + for (int i = 0; i < outcomes.length; i++) { + String autoConfigurationClass = autoConfigurationClasses[i]; + if (autoConfigurationClass != null) { + // ´¦Àí½á¹û + outcomes[i] = getOutcome(autoConfigurationMetadata.get(autoConfigurationClass, + "ConditionalOnWebApplication")); + } + } + return outcomes; +} + +/** + * ´¦Àí½á¹û + * springbootÖ§³ÖµÄwebÀàÐÍÓÐÁ½ÖÖ£ºSERVLET£¬REACTIVE + */ +private ConditionOutcome getOutcome(String type) { + if (type == null) { + return null; + } + ConditionMessage.Builder message = ConditionMessage + .forCondition(ConditionalOnWebApplication.class); + // Èç¹ûÖ¸¶¨µÄÀàÐÍÊÇ SERVLET + if (ConditionalOnWebApplication.Type.SERVLET.name().equals(type)) { + if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader())) { + return ConditionOutcome.noMatch( + message.didNotFind("servlet web application classes").atAll()); + } + } + // Èç¹ûÖ¸¶¨µÄÀàÐÍÊÇ REACTIVE + if (ConditionalOnWebApplication.Type.REACTIVE.name().equals(type)) { + if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) { + return ConditionOutcome.noMatch( + message.didNotFind("reactive web application classes").atAll()); + } + } + // Èç¹ûûÓÐÖ¸¶¨webÀàÐÍ + if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader()) + && !ClassUtils.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) { + return ConditionOutcome.noMatch( + message.didNotFind("reactive or servlet web application classes").atAll()); + } + return null; +} + +``` + +Õâ¸ö·½·¨ºÜ¼òµ¥£¬´¦ÀíÂß¼­Îª£º¸ù¾Ý `@ConditionalOnWebApplication` ÖÐÖ¸¶¨µÄÀàÐÍ£¬Åж϶ÔÓ¦µÄÀàÊÇ·ñ´æÔÚ£¬ÅжϷ½Ê½Óë `@ConditionalOnClass` ÅжÏÀàÊÇ·ñ´æÔÚÒ»Ö£¬¶øÁ½ÖÖÀàÐͶÔÓ¦µÄÀàÈçÏ£º + +* Servlet£º`org.springframework.web.context.support.GenericWebApplicationContext` +* Reactive£º`org.springframework.web.reactive.HandlerResult` + +### 8. `@ConditionalOnExpression`£º`OnExpressionCondition#getMatchOutcome` + +ÎÒÃÇÔÙÀ´¿´¿´ `@ConditionalOnExpression` µÄ´¦Àí£¬½øÈë `OnExpressionCondition#getOutcomes` ·½·¨£º + +``` +/** + * ´¦ÀíÆ¥Åä½á¹û + */ +@Override +public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + // »ñÈ¡±í´ïʽ + String expression = (String) metadata.getAnnotationAttributes( + ConditionalOnExpression.class.getName()).get("value"); + expression = wrapIfNecessary(expression); + ConditionMessage.Builder messageBuilder = ConditionMessage + .forCondition(ConditionalOnExpression.class, "(" + expression + ")"); + // ´¦Àíռλ·û + expression = context.getEnvironment().resolvePlaceholders(expression); + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + if (beanFactory != null) { + // ¼ÆËã±í´ïʽµÄÖµ + boolean result = evaluateExpression(beanFactory, expression); + return new ConditionOutcome(result, messageBuilder.resultedIn(result)); + } + return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available.")); +} + +/** + * ¼ÆËã±í´ïʽµÄÖµ + */ +private Boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory, + String expression) { + BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver(); + if (resolver == null) { + resolver = new StandardBeanExpressionResolver(); + } + // ÔÚÕâÀï½âÎö±í´ïʽµÄÖµ + BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null); + Object result = resolver.evaluate(expression, expressionContext); + return (result != null && (boolean) result); +} + +``` + +¿ÉÒÔ¿´µ½£¬springboot ×îÖÕÊÇͨ¹ý `BeanExpressionResolver#evaluate` ·½·¨À´¼ÆËã±í´ïʽ½á¹û£¬¹ØÓÚ spring ±í´ïʽ£¬±¾ÎľͲ»Õ¹¿ª·ÖÎöÁË¡£ + +ºÃÁË£¬spring Ìõ¼þ×¢½âµÄ·ÖÎö¾Íµ½ÕâÀïÁË£¬ÐèҪ˵Ã÷µÄÊÇ£¬springboot »¹ ÓÐÆäËûÌõ¼þ×¢½â£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0e8d27c887fb6ca142672cad1c60e9de207.png) + +ÕâЩע½âµÄÅжϷ½Ê½Óë±¾Îĵķ½Ê½ÏàÀàËÆ£¬¾Í²»Ò»Ò»½øÐзÖÎöÁË¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4921590](https://my.oschina.net/funcy/blog/4921590) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" new file mode 100644 index 0000000..cb6a94c --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConfig.md" @@ -0,0 +1,833 @@ +ÔÚ·Ö²¼Ê½Î¢·þÎñϵͳÖУ¬¼¸ºõËùÓзþÎñµÄÔËÐж¼Àë²»¿ªÅäÖÃÎļþµÄÖ§³Ö£¬ÕâЩÅäÖÃÎļþͨ³£Óɸ÷¸ö·þÎñ×ÔÐйÜÀí£¬ÒÔ properties »ò yml ¸ñʽ±£´æÔÚ¸÷¸ö΢·þÎñµÄÀà·¾¶Ï£¬ÀýÈç application.properties »ò application.yml µÈ¡£ + +ÕâÖÖ½«ÅäÖÃÎļþÉ¢ÂäÔÚ¸÷¸ö·þÎñÖеĹÜÀí·½Ê½£¬´æÔÚÒÔÏÂÎÊÌ⣺ + +* **¹ÜÀíÄѶȴó**£ºÅäÖÃÎļþÉ¢ÂäÔÚ¸÷¸ö΢·þÎñÖУ¬ÄÑÒÔ¹ÜÀí¡£ +* **°²È«ÐÔµÍ**£ºÅäÖøúËæÔ´´úÂë±£´æÔÚ´úÂë¿âÖУ¬ÈÝÒ×Ôì³ÉÅäÖÃй©¡£ +* **ʱЧÐÔ²î**£ºÎ¢·þÎñÖеÄÅäÖÃÐ޸ĺ󣬱ØÐëÖØÆô·þÎñ£¬·ñÔòÎÞ·¨ÉúЧ¡£ +* **¾ÖÏÞÐÔÃ÷ÏÔ**£ºÎÞ·¨Ö§³Ö¶¯Ì¬µ÷Õû£¬ÀýÈçÈÕÖ¾¿ª¹Ø¡¢¹¦ÄÜ¿ª¹Ø¡£ + +ΪÁ˽â¾öÕâЩÎÊÌ⣬ͨ³£ÎÒÃǶ¼»áʹÓÃÅäÖÃÖÐÐĶÔÅäÖýøÐÐͳһ¹ÜÀí¡£ÊÐÃæÉÏ¿ªÔ´µÄÅäÖÃÖÐÐÄÓкܶ࣬ÀýÈç°Ù¶ÈµÄ Disconf¡¢ÌÔ±¦µÄ diamond¡¢360 µÄ QConf¡¢Ð¯³ÌµÄ Apollo µÈ¶¼Êǽâ¾öÕâÀàÎÊÌâµÄ¡£Spring Cloud Ò²ÓÐ×Ô¼ºµÄ·Ö²¼Ê½ÅäÖÃÖÐÐÄ£¬ÄǾÍÊÇ Spring Cloud Config¡£ + +## Spring Cloud Config + +Spring Cloud Config ÊÇÓÉ Spring Cloud ÍŶӿª·¢µÄÏîÄ¿£¬Ëü¿ÉÒÔΪ΢·þÎñ¼Ü¹¹Öи÷¸ö΢·þÎñÌṩ¼¯Öл¯µÄÍⲿÅäÖÃÖ§³Ö¡£ + +¼òµ¥µã˵¾ÍÊÇ£¬Spring Cloud Config ¿ÉÒÔ½«¸÷¸ö΢·þÎñµÄÅäÖÃÎļþ¼¯Öд洢ÔÚÒ»¸öÍⲿµÄ´æ´¢²Ö¿â»òϵͳ£¨ÀýÈç Git ¡¢SVN µÈ£©ÖУ¬¶ÔÅäÖõÄͳһ¹ÜÀí£¬ÒÔÖ§³Ö¸÷¸ö΢·þÎñµÄÔËÐС£ +Spring Cloud Config °üº¬ÒÔÏÂÁ½¸ö²¿·Ö£º + +* Config Server£ºÒ²±»³ÆÎª·Ö²¼Ê½ÅäÖÃÖÐÐÄ£¬ËüÊÇÒ»¸ö¶ÀÁ¢ÔËÐеÄ΢·þÎñÓ¦Óã¬ÓÃÀ´Á¬½ÓÅäÖòֿⲢΪ¿Í»§¶ËÌṩ»ñÈ¡ÅäÖÃÐÅÏ¢¡¢¼ÓÃÜÐÅÏ¢ºÍ½âÃÜÐÅÏ¢µÄ·ÃÎʽӿڡ£ +* Config Client£ºÖ¸µÄÊÇ΢·þÎñ¼Ü¹¹Öеĸ÷¸ö΢·þÎñ£¬ËüÃÇͨ¹ý Config Server ¶ÔÅäÖýøÐйÜÀí£¬²¢´Ó Config Sever ÖлñÈ¡ºÍ¼ÓÔØÅäÖÃÐÅÏ¢¡£ + +Spring Cloud Config ĬÈÏʹÓà Git ´æ´¢ÅäÖÃÐÅÏ¢£¬Òò´ËʹÓà Spirng Cloud Config ¹¹½¨µÄÅäÖ÷þÎñÆ÷ÌìÈ»¾ÍÖ§³Ö¶Ô΢·þÎñÅäÖõİ汾¹ÜÀí¡£ÎÒÃÇ¿ÉÒÔʹÓà Git ¿Í»§¶Ë¹¤¾ß·½±ãµØ¶ÔÅäÖÃÄÚÈݽøÐйÜÀíºÍ·ÃÎÊ¡£³ýÁË Git Í⣬Spring Cloud Config »¹ÌṩÁË¶ÔÆäËû´æ´¢·½Ê½µÄÖ§³Ö£¬ÀýÈç SVN¡¢±¾µØ»¯ÎļþϵͳµÈ¡£ + +## Spring Cloud Config ¹¤×÷Ô­Àí + +Spring Cloud Config ¹¤×÷Ô­ÀíÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019425240-0.png) +ͼ1£ºSpring Cloud Config ¹¤×÷Ô­Àí + +Spring Cloud Config ¹¤×÷Á÷³ÌÈçÏ£º + +1. ¿ª·¢»òÔËάÈËÔ±Ìá½»ÅäÖÃÎļþµ½Ô¶³ÌµÄ Git ²Ö¿â¡£ +2. Config ·þÎñ¶Ë£¨·Ö²¼Ê½ÅäÖÃÖÐÐÄ£©¸ºÔðÁ¬½ÓÅäÖòֿâ Git£¬²¢¶Ô Config ¿Í»§¶Ë±©Â¶»ñÈ¡ÅäÖõĽӿڡ£ +3. Config ¿Í»§¶Ëͨ¹ý Config ·þÎñ¶Ë±©Â¶³öÀ´µÄ½Ó¿Ú£¬À­È¡ÅäÖòֿâÖеÄÅäÖᣠ+4. Config ¿Í»§¶Ë»ñÈ¡µ½ÅäÖÃÐÅÏ¢£¬ÒÔÖ§³Ö·þÎñµÄÔËÐС£ + +## Spring Cloud Config µÄÌØµã + +Spring Cloud Config ¾ßÓÐÒÔÏÂÌØµã£º + +* Spring Cloud Config ÓÉ Spring Cloud ÍŶӿª·¢£¬¿ÉÒÔ˵ÊÇ Spring µÄÇ×¶ù×Ó£¬Äܹ»Óë Spring µÄÉú̬ÌåϵÎ޷켯³É¡£ +* Spring Cloud Config ½«ËùÓÐ΢·þÎñµÄÅäÖÃÎļþ¼¯Öд洢ÔÚÒ»¸öÍⲿµÄ´æ´¢²Ö¿â»òϵͳ£¨ÀýÈç Git£©ÖУ¬Í³Ò»¹ÜÀí¡£ +* Spring Cloud Config ÅäÖÃÖÐÐĽ«ÅäÖÃÒÔ REST ½Ó¿ÚµÄÐÎʽ±©Â¶¸ø¸÷¸ö΢·þÎñ£¬ÒÔ·½±ã¸÷¸ö΢·þÎñ»ñÈ¡¡£ +* ΢·þÎñ¿ÉÒÔͨ¹ý Spring Cloud Config ÏòÅäÖÃÖÐÐÄͳһÀ­È¡ÊôÓÚËüÃÇ×Ô¼ºµÄÅäÖÃÐÅÏ¢¡£ +* µ±ÅäÖ÷¢Éú±ä»¯Ê±£¬Î¢·þÎñ²»ÐèÒªÖØÆô¼´¿É¸ÐÖªµ½ÅäÖõı仯£¬²¢×Ô¶¯»ñÈ¡ºÍÓ¦ÓÃ×îÐÂÅäÖᣠ+* Ò»¸öÓ¦ÓÿÉÄÜÓжà¸ö»·¾³£¬ÀýÈ翪·¢£¨dev£©»·¾³¡¢²âÊÔ£¨test£©»·¾³¡¢Éú²ú£¨prod£©»·¾³µÈµÈ£¬¿ª·¢ÈËÔ±¿ÉÒÔͨ¹ý Spring Cloud Config ¶Ô²»Í¬»·¾³µÄ¸÷ÅäÖýøÐйÜÀí£¬ÇÒÄܹ»È·±£Ó¦ÓÃÔÚ»·¾³Ç¨ÒƺóÈÔÈ»ÓÐÍêÕûµÄÅäÖÃÖ§³ÖÆäÕý³£ÔËÐС£ + +ÏÂÃæÎÒÃǾÍͨ¹ý¼¸¸öʵÀýÀ´ÑÝʾ Spring Cloud Config µÄʹÓᣠ+ +## ´î½¨ Config ·þÎñ¶Ë + +1\. ÔÚ Github ÉÏ´´½¨Ò»¸öÃûΪ springcloud-config µÄ²Ö¿â£¨Repository£©²¢»ñÈ¡¸Ã²Ö¿âµÄµØÖ·¡£ÓÉÓÚ Github Õ¾µã¶ÔÓÚ¹úÄÚÓû§À´Ëµ²»¹»Îȶ¨£¬ºÜ¿ÉÄÜ´æÔÚ¼ÓÔØ»ºÂýµÈÎÊÌ⣬Òò´ËÎÒÃÇ¿ÉÒÔÔÚ[ÂëÔÆ](https://gitee.com/)ÉÏÖ´ÐиòÙ×÷¡£ + +2\. ÔÚ¸¸¹¤³Ì spring-cloud-demo2 Ï£¬´´½¨Ò»¸öÃûΪ micro-service-cloud-config-center-3344 µÄ Spring Boot Ä£¿é£¬²¢ÔÚ pom.xml ÖÐÌí¼Ó Spring Cloud Config ·þÎñ¶ËÒÀÀµ£¬ÅäÖÃÈçÏ¡£ + + + + + +``` + + + 4.0.0 + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + net.biancheng.c + micro-service-cloud-config-center-3344 + 0.0.1-SNAPSHOT + micro-service-cloud-config-center-3344 + Demo project for Spring Boot + + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + + + + + +3\. ÔÚ micro-service-cloud-config-center-3344 Àà·¾¶£¨/resources Ŀ¼£©Ï£¬´´½¨Ò»¸öÃûΪ application.yml µÄÅäÖÃÎļþ£¬ÅäÖÃÈçÏ¡£ + + + + + +``` + +server: + port: 3344 #¶Ë¿ÚºÅ +spring: + application: + name: spring-cloud-config-center #·þÎñÃû + cloud: + config: + server: + git: + # Git µØÖ·£¬https://gitee.com/java-mohan/springcloud-config.git + # ÂëÔÆ£¨gitee£©µØÖ· uri: https://github.com/javmohan/springcloud-config.git (github Õ¾µã·ÃÎʽÏÂý£¬Òò´ËÕâÀïÎÒÃÇʹÓà gitee) + uri: https://gitee.com/java-mohan/springcloud-config.git + #²Ö¿âÃû + search-paths: + - springcloud-config + force-pull: true + # Èç¹ûGit²Ö¿âΪ¹«¿ª²Ö¿â£¬¿ÉÒÔ²»ÌîдÓû§ÃûºÍÃÜÂ룬Èç¹ûÊÇ˽ÓвֿâÐèÒªÌîд + # username: ******** + # password: ******** + #·ÖÖ§Ãû + label: master + +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº +``` + + + + + +4\. ÔÚ micro-service-cloud-config-center-3344 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableConfigServer ×¢½â¿ªÆô Spring Cloud Config ÅäÖÃÖÐÐŦÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient +@EnableConfigServer +public class MicroServiceCloudConfigCenter3344Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConfigCenter3344Application.class, args); + } + +} + +``` + + + + + +5\. н¨Ò»¸öÃûΪ config-dev.yml µÄÎļþ£¬²¢½«ÆäÉÏ´«µ½ springcloud-config ²Ö¿â master ·ÖÖ§Ï£¬config-dev.yml µÄÄÚÈÝÈçÏ¡£ + +``` +config: + info: c.biancheng.net + version: 1.0 +``` + + +6\. ÒÀ´ÎÆô¶¯·þÎñ×¢²áÖÐÐÄ£¨¼¯Èº£©ºÍ micro-service-cloud-config-center-3344£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3344/master/config-dev.yml¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Spring Cloud Config ·ÃÎÊÅäÖÃÎļþ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019423313-2.png) +ͼ2£º·ÃÎÊÅäÖÃÎļþ + +Spring Cloud Config ¹æ¶¨ÁËÒ»Ì×ÅäÖÃÎļþ·ÃÎʹæÔò£¬ÈçÏÂ±í¡£ + +| ·ÃÎʹæÔò | ʾÀý | +| ----------------------------------------- | ---------------------- | +| /{application}/{profile}[/{label}] | /config/dev/master | +| /{application}-{profile}.{suffix} | /config-dev.yml | +| /{label}/{application}-{profile}.{suffix} | /master/config-dev.yml | + +·ÃÎʹæÔòÄÚ¸÷²ÎÊý˵Ã÷ÈçÏ¡£ + +* {application}£ºÓ¦ÓÃÃû³Æ£¬¼´ÅäÖÃÎļþµÄÃû³Æ£¬ÀýÈç config-dev¡£ +* {profile}£º»·¾³Ãû£¬Ò»¸öÏîĿͨ³£¶¼Óпª·¢£¨dev£©°æ±¾¡¢²âÊÔ£¨test£©»·¾³°æ±¾¡¢Éú²ú£¨prod£©»·¾³°æ±¾£¬ÅäÖÃÎļþÔòÒÔ application-{profile}.yml µÄÐÎʽ½øÐÐÇø·Ö£¬ÀýÈç application-dev.yml¡¢application-test.yml¡¢application-prod.yml µÈ¡£ +* {label}£ºGit ·ÖÖ§Ãû£¬Ä¬ÈÏÊÇ master ·ÖÖ§£¬µ±·ÃÎÊĬÈÏ·Ö֧ϵÄÅäÖÃÎļþʱ£¬¸Ã²ÎÊý¿ÉÒÔÊ¡ÂÔ£¬¼´µÚ¶þÖÖ·ÃÎÊ·½Ê½¡£ +* {suffix}£ºÅäÖÃÎļþµÄºó׺£¬ÀýÈç config-dev.yml µÄºó׺Ϊ yml¡£ + +ͨ¹ýÕâÌ×¹æÔò£¬ÎÒÃÇÔÚä¯ÀÀÆ÷ÉϾÍÖ±½Ó¶ÔÅäÖÃÎļþ½øÐзÃÎÊ¡£ + +7\. ÔÚä¯ÀÀÆ÷ÉÏ·ÃÎÊ¡°http://localhost:3344/config-dev.yml¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Spring Cloud Config ·ÃÎÊÅäÖÃÎļþ2](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019422606-3.png) +ͼ3£ºSpring Cloud Config ·ÃÎÊÅäÖÃÎļþ + +8\. ÔÚä¯ÀÀÆ÷ÉÏ·ÃÎÊ¡°http://localhost:3344/config/dev/master¡±£¬½á¹ûÈçÏ¡£ + +``` +{"name":"config","profiles":["dev"],"label":"master","version":"9caafcc3498e04147463482f8b29e925e8afcc3a","state":null,"propertySources":[{"name":"https://gitee.com/java-mohan/springcloud-config.git/config-dev.yml","source":{"config.info":"c.biancheng.net","config.version":1.0}}]} +``` + +×Ô´ËÎÒÃǾÍÍê³ÉÁË¶Ô Spring Cloud Config ·þÎñ¶ËµÄ´î½¨ºÍ²âÊÔ¡£ + +## ´î½¨ Config ¿Í»§¶Ë + +1\. ÔÚ¸¸¹¤³Ì spring-cloud-demo2 Ï£¬´´½¨Ò»¸öÃûΪ micro-service-cloud-config-client-3355 µÄ Spring Boot Ä£¿é£¬²¢ÔÚÆä pom.xml ÖÐÌí¼Ó Spring Cloud Config ¿Í»§¶ËµÄÒÀÀµ£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` + + + + 4.0.0 + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + net.biancheng.c + micro-service-cloud-config-client-3355 + 0.0.1-SNAPSHOT + micro-service-cloud-config-client-3355 + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + +``` + + + + + +2\. ÔÚ micro-service-cloud-config-client-3355 ÖÐÀà·¾¶£¨/resources Ŀ¼£©Ï£¬´´½¨Ò»¸öÃûΪ bootstrap.yml µÄÅäÖÃÎļþ£¬ÅäÖÃÈçÏ¡£ + + + + + +``` +#bootstrap.yml ÊÇϵͳ¼¶±ðµÄ£¬¼ÓÔØÓÅÏȼ¶¸ßÓÚ application.yml £¬¸ºÔð´ÓÍⲿ¼ÓÔØÅäÖò¢½âÎö +server: + port: 3355 #¶Ë¿ÚºÅ +spring: + application: + name: spring-cloud-config-client #·þÎñÃû + cloud: + config: + label: master #·ÖÖ§Ãû³Æ + name: config #ÅäÖÃÎļþÃû³Æ£¬config-dev.yml ÖÐµÄ config + profile: dev #»·¾³Ãû config-dev.yml ÖÐµÄ dev + #ÕâÀï²»ÒªÍü¼ÇÌí¼Ó http:// ·ñÔòÎÞ·¨¶ÁÈ¡ + uri: http://localhost:3344 #Spring Cloud Config ·þÎñ¶Ë£¨ÅäÖÃÖÐÐÄ£©µØÖ· + +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº +``` + + + + + +3\. ÔÚ net.biancheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ ConfigClientController µÄÀ࣬ͨ¹ý¸ÃÀà»ñÈ¡ÅäÖÃÎļþÖеÄÅäÖ㬴úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.controller; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +//¶ÁÈ¡ÅäÖÃÖÐÐÄÖ¸¶¨ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢Õ¹Ê¾µ½Ò³Ãæ +@RestController +public class ConfigClientController { + @Value("${server.port}") + private String serverPort; + + @Value("${config.info}") + private String configInfo; + + @Value("${config.version}") + private String configVersion; + + @GetMapping(value = "/getConfig") + public String getConfig() { + return "info£º" + configInfo + "
version£º" + configVersion + "
port£º" + serverPort; + } +} +``` + + + + + +4\. ÔÚ micro-service-cloud-config-client-3355 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableEurekaClient ×¢½â¿ªÆô Eureka ¿Í»§¶Ë¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient +public class MicroServiceCloudConfigClient3355Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConfigClient3355Application.class, args); + } + +} + +``` + + + + + +5\. Æô¶¯ micro-service-cloud-config-client-3355£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3355/getConfig¡±,½á¹ûÈçÏÂͼ¡£ + +![Config ¿Í»§¶Ë»ñÈ¡ÅäÖÃÐÅÏ¢](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019422604-4.png) +ͼ4£ºSpring Cloud Config ¿Í»§¶Ë»ñÈ¡ÅäÖÃÐÅÏ¢ + +6\. ½«ÅäÖÃÎļþ config-dev.yml ÖÐ config.version µÄÖµÐÞ¸ÄΪ 2.0£¬ÅäÖÃÈçÏ¡£ + +``` +config: + info: c.biancheng.net + version: 2.0 +``` + + +7\. ÒÀ´ÎÆô¶¯ Eureka ·þÎñ×¢²áÖÐÐÄ£¨¼¯Èº£©ºÍ micro-service-cloud-config-center-3344£¬ ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3344/master/config-dev.yml¡±£¬½á¹ûÈçÏÂͼ¡£ + +![ÅäÖÃÖÐÐÄ»ñÈ¡Ð޸ĺóµÄÅäÖÃÎļþ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194255O-5.png) +ͼ5£ºÅäÖÃÖÐÐÄ»ñÈ¡Ð޸ĺóµÄÅäÖÃÎļþ + +´Óͼ 6 ¿ÉÒÔ¿´³ö£¬ÅäÖÃÖÐÐÄÒѾ­³É¹¦µØ»ñÈ¡µ½ÁËÐ޸ĺóµÄÅäÖᣠ+ +8\. ÔٴηÃÎÊ¡°http://localhost:3355/getConfig¡±£¬³¢ÊÔͨ¹ý Spring Cloud Config ¿Í»§¶Ë»ñÈ¡Ð޸ĺóµÄÅäÖÃÐÅÏ¢£¬½á¹ûÈçÏÂͼ¡£ + +![Config ¿Í»§¶Ë»ñÈ¡ÅäÖÃÐÅÏ¢](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019422604-4.png) +ͼ6£ºSpring Cloud Config ¿Í»§¶Ë»ñÈ¡Ð޸ĺóµÄÅäÖÃÐÅÏ¢ + +9\. ÖØÆô micro-service-cloud-config-client-3355£¬ÔÙ´ÎʹÓ÷ÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![ÖØÆôConfig ¿Í»§¶Ë](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019425023-7.png) +ͼ7£ºÖØÆô Spring Cloud Config ¿Í»§¶Ë»ñÈ¡ÅäÖà + +ͨ¹ý¸ÃʵÀý£¬ÎÒÃÇ¿ÉÒԵõ½ÒÔÏ 2 µã½áÂÛ£¬ + +* ÅäÖøüкó£¬Spring Cloud Config ·þÎñ¶Ë£¨Server£©¿ÉÒÔÖ±½Ó´Ó Git ²Ö¿âÖлñÈ¡×îеÄÅäÖᣠ+* ³ý·ÇÖØÆô Spring Cloud Config ¿Í»§¶Ë£¨Client£©£¬·ñÔòÎÞ·¨Í¨¹ý Spring Cloud Config ·þÎñ¶Ë»ñÈ¡×îеÄÅäÖÃÐÅÏ¢¡£ + +## ÊÖ¶¯Ë¢ÐÂÅäÖà + +ΪÁ˽â¾ö²»ÖØÆô Config ¿Í»§¶ËÎÞ·¨»ñÈ¡×îÐÂÅäÖõÄÎÊÌ⣬½ÓÏÂÀ´£¬ÎÒÃÇ¾Í¶Ô micro-service-cloud-config-client-3355 ½øÐиÄÔ죬¸ÄÔì²½ÖèÈçÏ¡£ + +1\. ÔÚ micro-service-cloud-config-client-3355 µÄ pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ£¬ÒýÈë Spring Boot actuator ¼à¿ØÄ£¿é¡£ + + + + + +``` + + + + org.springframework.boot + spring-boot-starter-actuator + + +``` + + + + + +2\. ÔÚÅäÖÃÎļþ bootstrap.yml ÖÐÌí¼ÓÒÔÏÂÅäÖ㬶ÔÍⱩ¶ Spring Boot actuator µÄ¼à¿Ø½Úµã¡£ + + + + + +``` + +# Spring Boot 2.50¶Ô actuator ¼à¿ØÆÁ±ÎÁË´ó¶àÊýµÄ½Úµã£¬Ö»±©Â¶ÁË health ½Úµã£¬±¾¶ÎÅäÖã¨*£©¾ÍÊÇΪÁË¿ªÆôËùÓеĽڵã +management: + endpoints: + web: + exposure: + include: "*" # * ÔÚyaml ÎļþÊôÓڹؼü×Ö£¬ËùÒÔÐèÒª¼ÓÒýºÅ + +``` + + + + + + +3\. ÔÚ ConfigClientController ÀàÉÏʹÓà @RefreshScope ×¢½â¿ªÆôÅäÖÃˢУ¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.controller; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +// ¶ÁÈ¡ÅäÖÃÖÐÐÄÖ¸¶¨ÅäÖÃÎļþµÄÄÚÈÝ£¬²¢Õ¹Ê¾µ½Ò³Ãæ +@RefreshScope //ΪÁËÈö¯Ì¬£¨ÊÖ¶¯£©µÄ»ñÈ¡×îеÄgit ÅäÖã¬ÔÚÌí¼Ó actuator ¼à¿Ø¼ÓÔØ RefreshScope£¬ +@RestController +public class ConfigClientController { + @Value("${server.port}") + private String serverPort; + + @Value("${config.info}") + private String configInfo; + + @Value("${config.version}") + private String configVersion; + + @GetMapping(value = "/getConfig") + public String getConfig() { + return "info£º" + configInfo + "
version£º" + configVersion + "
port£º" + serverPort; + } +} +``` + + + + + +4\. ÖØÆô micro-service-cloud-config-client-3355£¬È»ºó½«ÅäÖÃÎļþ config-dev.yml ÖÐµÄ config.version ÐÞ¸ÄΪ 3.0£¬ÅäÖÃÈçÏ¡£ + +``` +config: + info: c.biancheng.net + version: 3.0 +``` + + +5\. ʹÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![ÖØÆôConfig ¿Í»§¶Ë](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019425023-7.png) +ͼ8£ºÖØÆô Spring Cloud Config ¿Í»§¶Ëºó»ñÈ¡ÅäÖà + +´Óͼ 9 ¿ÉÒÔ¿´³ö£¬¼´Ê¹ÎÒÃÇ¶Ô Spring Cloud Config ¿Í»§¶Ë½øÐÐÁ˸ÄÔ죬ҲÒÀÈ»ÎÞ·¨Ö±½Ó»ñÈ¡µ½×îÐÂÅäÖᣠ+ +6\. ´ò¿ªÃüÁîÐд°¿Ú£¬Ê¹ÓÃÒÔÏÂÃüÁî·¢ËÍÒ»¸ö POST ÇëÇóˢРSpring Cloud Config 3355 ¿Í»§¶Ë£¬Í¨Öª¿Í»§¶ËÅäÖÃÎļþÒѾ­Ð޸ģ¬ÐèÒªÖØÐÂÀ­È¥ÅäÖᣠ+ +```curl -X POST "http://localhost:3355/actuator/refresh"``` + +7. ʹÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![ÊÖ¶¯Ë¢ÐÂ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101942C62-9.png) +ͼ9£ºÊÖ¶¯Ë¢Ð¿ͻ§¶Ë + +#### ÊÖ¶¯Ë¢ÐÂÅäÖõÄÎÊÌâ + +ÔÚÉÏÃæµÄʵÀýÖУ¬ÎÒÃÇͨ¹ýÔÚ Config ¿Í»§¶Ë£¨¶Ë¿ÚºÅ£º3355£©ÖÐÒýÈë Spring Boot actuator ¼à¿Ø×é¼þÀ´¼à¿ØÅäÖõı仯£¬Ê¹ÎÒÃÇ¿ÉÒÔÔÚ²»ÖØÆô Config ¿Í»§¶ËµÄÇé¿öÏ»ñÈ¡µ½ÁË×îÐÂÅäÖã¬Ô­ÀíÈçÏÂͼ¡£ + +![Spring Cloud Config ÊÖ¶¯Ë¢ÐÂ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019424S8-10.png) +ͼ10£ºSpring Cloud Congfig ÊÖ¶¯Ë¢Ð + +ÕâÖÖ·½Ê½ËäÈ»½â¾öÁËÖØÆô Config ¿Í»§¶Ë²ÅÄÜ»ñÈ¡×îÐÂÅäÖõÄÎÊÌ⣬µ«ÁíÒ»¸öÎÊÌâÈ´Ò²½Óõà¶øÖÁ£¬ÄǾÍÊÇÖ»ÒªÅäÖòֿâÖеÄÅäÖ÷¢Éú¸Ä±ä£¬¾ÍÐèÒªÎÒÃǰ¤¸öÏò Config ¿Í»§¶ËÊÖ¶¯·¢ËÍ POST ÇëÇó£¬Í¨ÖªËüÃÇÖØÐÂÀ­È¡ÅäÖᣠ+ +ÎÒÃÇÖªµÀ£¬ËùνµÄ Config ¿Í»§¶ËÆäʵ¾ÍÊÇÒ»¸öÒ»¸öµÄ·þÎñ¡£ÔÚ΢·þÎñ¼Ü¹¹ÖУ¬Ò»¸öϵͳÍùÍù°üº¬Ê®¼¸ÉõÖÁ¼¸Ê®¸ö·þÎñ£¬Èç¹ûÒòΪijһ¸öÅäÖÃÎļþµÄÐ޸ĶøÏò¼¸Ê®¸ö΢·þÎñ·¢ËÍ POST ÇëÇó£¬ÕâÏÔÈ»ÊDz»ºÏÀíµÄ¡£ + +ÄÇôÓÐûÓС°Ò»´Î֪ͨ£¬´¦´¦ÉúЧ¡±µÄ·½Ê½ÄØ£¿´ð°¸Êǿ϶¨µÄ¡£Spring Cloud Config ÅäºÏ Bus ¾Í¿ÉÒÔʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ + +## Config+Bus ʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð + +Spring Cloud Bus ÓÖ±»³ÆÎªÏûÏ¢×ÜÏߣ¬ËüÄܹ»Í¨¹ýÇáÁ¿¼¶µÄÏûÏ¢´úÀí£¨ÀýÈç RabbitMQ¡¢Kafka µÈ£©½«Î¢·þÎñ¼Ü¹¹Öеĸ÷¸ö·þÎñÁ¬½ÓÆðÀ´£¬ÊµÏֹ㲥״̬¸ü¸Ä¡¢Ê¼þÍÆË͵ȹ¦ÄÜ£¬»¹¿ÉÒÔʵÏÖ΢·þÎñÖ®¼äµÄͨÐŹ¦ÄÜ¡£ + +Ŀǰ Spring Cloud Bus Ö§³ÖÁ½ÖÖÏûÏ¢´úÀí£ºRabbitMQ ºÍ Kafka¡£ + +#### Spring Cloud Bus µÄ»ù±¾Ô­Àí + +Spring Cloud Bus »áʹÓÃÒ»¸öÇáÁ¿¼¶µÄÏûÏ¢´úÀíÀ´¹¹½¨Ò»¸ö¹«¹²µÄÏûÏ¢Ö÷Ìâ Topic£¨Ä¬ÈÏΪ¡°springCloudBus¡±£©£¬Õâ¸ö Topic ÖеÄÏûÏ¢»á±»ËùÓзþÎñʵÀý¼àÌýºÍÏû·Ñ¡£µ±ÆäÖеÄÒ»¸ö·þÎñË¢ÐÂÊý¾Ýʱ£¬Spring Cloud Bus »á°ÑÐÅÏ¢±£´æµ½ Topic ÖУ¬ÕâÑù¼àÌýÕâ¸ö Topic µÄ·þÎñ¾ÍÊÕµ½ÏûÏ¢²¢×Ô¶¯Ïû·Ñ¡£ + +#### Spring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖõÄÔ­Àí + +ÀûÓà Spring Cloud Bus µÄÌØÊâ»úÖÆ¿ÉÒÔʵÏֺܶ๦ÄÜ£¬ÆäÖÐÅäºÏ Spring Cloud Config ʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð¾ÍÊÇ×îµäÐ͵ÄÓ¦Óó¡¾°Ö®Ò»¡£ + +µ± Git ²Ö¿âÖеÄÅäÖ÷¢ÉúÁ˸ı䣬ÎÒÃÇÖ»ÐèÒªÏòijһ¸ö·þÎñ£¨¼È¿ÉÒÔÊÇ Config ·þÎñ¶Ë£¬Ò²¿ÉÒÔÊÇ Config ¿Í»§¶Ë£©·¢ËÍÒ»¸ö POST ÇëÇó£¬Spring Cloud Bus ¾Í¿ÉÒÔͨ¹ýÏûÏ¢´úÀí֪ͨÆäËû·þÎñÖØÐÂÀ­È¡×îÐÂÅäÖã¬ÒÔʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ + +Spring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖõŤ×÷Ô­Àí£¬ÈçÏÂͼËùʾ¡£ + +![bus+config ¶¯Ì¬Ë¢ÐÂÅäÖÃ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101942GY-11.png) +ͼ11£ºBus+Config ʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð + +¸ù¾Ýͼ 12£¬ÀûÓà Spring Cloud Bus ʵÏÖÅäÖõĶ¯Ì¬Ë¢ÐÂÐèÒªÒÔϲ½Öè: + +1. µ± Git ²Ö¿âÖеÄÅäÖ÷¢Éú¸Ä±äºó£¬ÔËάÈËÔ±Ïò Config ·þÎñ¶Ë·¢ËÍÒ»¸ö POST ÇëÇó£¬ÇëÇ󷾶Ϊ¡°/actuator/refresh¡±¡£ +2. Config ·þÎñ¶Ë½ÓÊÕµ½ÇëÇóºó£¬»á½«¸ÃÇëÇóת·¢¸ø·þÎñ×ÜÏß Spring Cloud Bus¡£ +3. Spring Cloud Bus ½Óµ½ÏûÏ¢ºó£¬»á֪ͨ¸øËùÓÐ Config ¿Í»§¶Ë¡£ +4. Config ¿Í»§¶Ë½ÓÊÕµ½Í¨Öª£¬ÇëÇó Config ·þÎñ¶ËÀ­È¡×îÐÂÅäÖᣠ+5. ËùÓÐ Config ¿Í»§¶Ë¶¼»ñÈ¡µ½×îеÄÅäÖᣠ+ +#### Spring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖã¨È«¾Ö¹ã²¥£© + +ÏÂÃæÎÒÃÇÒÔ RabbitMQ ΪÀý£¬À´ÑÝʾÈçºÎʹÓà Config+Bus ʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ + +1\. ÔÚ micro-service-cloud-config-center-3344 µÄ pom.xml ÖУ¬Ìí¼Ó Spring Boot actuator ¼à¿ØÄ£¿éºÍ Spring Cloud Bus µÄÒÀÀµ£¬´úÂëÈçÏ¡£ + + + + + +``` + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + org.springframework.boot + spring-boot-starter-actuator + + +``` + + + + + +2\. ÔÚ micro-service-cloud-config-center-3344 µÄÅäÖÃÎļþ application.yml ÖУ¬Ìí¼Ó RabbitMQ ºÍ Spring Boot actuator µÄÏà¹ØÅäÖã¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` +##### RabbitMQ Ïà¹ØÅäÖã¬15672 ÊÇweb ¹ÜÀí½çÃæµÄ¶Ë¿Ú£¬5672 ÊÇ MQ µÄ·ÃÎʶ˿Ú########### +spring: + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: guest + password: guest + +# Spring Boot 2.50¶Ô actuator ¼à¿ØÆÁ±ÎÁË´ó¶àÊýµÄ½Úµã£¬Ö»±©Â¶ÁË heath ½Úµã£¬±¾¶ÎÅäÖã¨*£©¾ÍÊÇΪÁË¿ªÆôËùÓеĽڵã +management: + endpoints: + web: + exposure: + include: 'bus-refresh' + +``` + + + + + +3\. ÔÚ micro-service-cloud-config-client-3355 µÄ pom.xml ÖУ¬Ìí¼Ó Spring Cloud Bus µÄÏà¹ØÒÀÀµ£¬´úÂëÈçÏ¡£ + + + + + +``` + + +1. +2. +3. org.springframework.cloud +4. spring-cloud-starter-bus-amqp +5. + +``` + + + + + +4. ÔÚ micro-service-cloud-config-client-3355 µÄÅäÖÃÎļþ bootstrap.yml ÖÐÌí¼ÓÒÔÏÂÅäÖᣠ+ + + + + +``` + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + +``` + + + + + +5\. ²ÎÕÕ micro-service-cloud-config-client-3355£¬Ð½¨Ò»¸öÃûΪ micro-service-cloud-config-client-bus-3366 µÄ Spring Boot Ä£¿é£¨¶Ë¿ÚºÅΪ 3366£©£¬²¢ÔÚÆäÅäÖÃÎļþ bootstrap.yml ÖÐÌí¼ÓÒÔÏÂÅäÖᣠ+ + + + + +``` + +#bootstrap.yml ÊÇϵͳ¼¶±ðµÄ£¬¼ÓÔØÓÅÏȼ¶¸ßÓÚ application.yml £¬¸ºÔð´ÓÍⲿ¼ÓÔØÅäÖò¢½âÎö +server: + port: 3366 #¶Ë¿ÚºÅΪ 3366 +spring: + application: + name: spring-cloud-config-client-bus + + cloud: + config: + label: master #·ÖÖ§Ãû³Æ + name: config #ÅäÖÃÎļþÃû³Æ£¬config-dev.yml ÖÐµÄ config + profile: dev #ÅäÖÃÎļþµÄºó׺Ãû config-dev.yml ÖÐµÄ dev + #ÕâÀï²»ÒªÍü¼ÇÌí¼Ó http:// ·ñÔòÎÞ·¨¶ÁÈ¡ + uri: http://localhost:3344 #spring cloud ÅäÖÃÖÐÐĵØÖ· + +##### RabbitMQ Ïà¹ØÅäÖã¬15672 ÊÇweb ¹ÜÀí½çÃæµÄ¶Ë¿Ú£¬5672 ÊÇ MQ µÄ·ÃÎʶ˿Ú########### + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: guest + password: guest +###################### eureka ÅäÖà #################### +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº + +# Spring Boot 2.50¶Ô actuator ¼à¿ØÆÁ±ÎÁË´ó¶àÊýµÄ½Úµã£¬Ö»±©Â¶ÁË heath ½Úµã£¬±¾¶ÎÅäÖã¨*£©¾ÍÊÇΪÁË¿ªÆôËùÓеĽڵã +management: + endpoints: + web: + exposure: + include: "*" # * ÔÚyaml ÎļþÊôÓڹؼü×Ö£¬ËùÒÔÐèÒª¼ÓÒýºÅ + +``` + + + + + +6\. ÒÀ´ÎÖØÆô micro-service-cloud-config-center-3344¡¢micro-service-cloud-config-client-3355£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Bus ¶¯Ì¬ÅäÖøüÐÂ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019422S4-12.png) +ͼ12£ºSpring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖà + +7\. Æô¶¯ micro-service-cloud-config-client-bus-3366£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3366/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Bus ¶¯Ì¬¸üÐÂ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194254S-13.png) +ͼ13£ºSpring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖà + +8. ½«ÅäÖÃÎļþ config-dev.yml ÖÐµÄ config.version ÐÞ¸ÄΪ 4.0£¬ÅäÖÃÈçÏ¡£ + +``` +config: + info: c.biancheng.net + version: 4.0 +``` + + +9\. ´ò¿ªÃüÁîÐд°¿Ú£¬Ê¹ÓÃÒÔÏÂÃüÁîÏò micro-service-cloud-config-center-3344£¨Config Server£©·¢ËÍÒ»¸ö POST ÇëÇó£¬Ë¢ÐÂÅäÖᣠ+ +```curl -X POST "http://localhost:3344/actuator/bus-refresh"``` + +10. ʹÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![bus ¶¯Ì¬Ë¢ÐÂÅäÖÃ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1019423453-14.png) +ͼ14£ºSpring Cloud Bus ¶¯Ì¬Ë¢Ð + +11. ʹÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3366/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194222Y-15.png) +ͼ15£ºSpring Cloud Bus ¶¯Ì¬Ë¢Ð + +> ×¢Ò⣺ÔÚʹÓà Spring Cloud Bus ʱ£¬±ØÐë±£Ö¤ Bus ËùÁ¬½ÓµÄÏûÏ¢´úÀíÈí¼þ£¨ÀýÈç RabbitMQ£©ÒѾ­ÕýÈ·°²×°ÇÒÔËÐÐÕý³£¡£ + +#### Spring Cloud Bus ¶¯Ì¬Ë¢ÐÂÅäÖ㨶¨µã֪ͨ£© + +Ëùν¶¨µã֪ͨ£¬¾ÍÊDz»ÔÙ֪ͨËùÓÐµÄ Config ¿Í»§¶Ë£¬¶øÊǸù¾ÝÐèÇóֻ֪ͨÆäÖÐijһ¸ö Config ¿Í»§¶Ë¡£ + +ʹÓà Spring Cloud Bus ʵÏÖ¶¨µã֪ͨµÄ·½·¨Ê®·Ö¼òµ¥£¬Ö»ÒªÎÒÃÇÔÚ·¢ËÍ POST ÇëÇóʱʹÓÃÒÔϸñʽ¼´¿É¡£ + +```http://{hostname}:{port}/actuator/bus-refresh/{destination}``` + +²ÎÊý˵Ã÷ÈçÏ£º + +* {hostname}£º ±íʾ Config ·þÎñ¶ËµÄÖ÷»úµØÖ·£¬¼È¿ÉÒÔÊÇÓòÃû£¬Ò²¿ÉÒÔÊÇ IP µØÖ·¡£ +* {port}£º±íʾ Config ·þÎñ¶ËµÄ¶Ë¿ÚºÅ. +* {destination}£º±íʾÐèÒª¶¨µã֪ͨµÄ Config ¿Í»§¶Ë£¨Î¢·þÎñ£©£¬ÓÉ Config ¿Í»§¶ËµÄ·þÎñÃû£¨spring.application.name£©+¶Ë¿ÚºÅ£¨server.port£©×é³É£¬ÀýÈçֻ֪ͨ micro-service-cloud-config-client-3355 Ë¢ÐÂÅäÖã¬ÔòȡֵΪ spring-cloud-config-client:3355¡£ + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸ö¼òµ¥µÄʵÀý£¬À´ÑÝʾÏ Spring Cloud Bus ¶¯Ì¬Ë¢ÐµĶ¨µã֪ͨ¡£ + +1\. ½«ÅäÖÃÎļþ config-dev.yml ÖÐµÄ config.version ÐÞ¸ÄΪ 5.0£¬ÅäÖÃÈçÏ¡£ + +``` +config: + info: c.biancheng.net + version: 5.0 +``` + + +2\. ´ò¿ªÃüÁîÐд°¿Ú£¬Ê¹ÓÃÒÔÏÂÃüÁîÏò micro-service-cloud-config-center-3344 ·¢ËÍÒ»¸ö POST ÇëÇó¡£ + +```curl -X POST "http://localhost:3344/actuator/bus-refresh/spring-cloud-config-client:3355"``` + +3\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3355/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Bus ¶¨µã֪ͨ 3355](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194233O-16.png) +ͼ16£ºSpring Cloud Bus ¶¨µã֪ͨ + +4\. ʹÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3366/getConfig¡±£¬½á¹ûÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10194222Y-15.png) +ͼ17£ºSpring Cloud Bus ¶¨µã֪ͨ + + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" new file mode 100644 index 0000000..85fcf19 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudConsul.md" @@ -0,0 +1,248 @@ +Spring Cloud Consul Ϊ SpringBoot Ó¦ÓÃÌṩÁË ConsulµÄÖ§³Ö£¬Consul¼È¿ÉÒÔ×÷Ϊע²áÖÐÐÄʹÓã¬Ò²¿ÉÒÔ×÷ΪÅäÖÃÖÐÐÄʹÓ㬱¾ÎĽ«¶ÔÆäÓ÷¨½øÐÐÏêϸ½éÉÜ¡£ + +# Consul ¼ò½é + +ConsulÊÇHashiCorp¹«Ë¾ÍƳöµÄ¿ªÔ´Èí¼þ£¬ÌṩÁË΢·þÎñϵͳÖеķþÎñÖÎÀí¡¢ÅäÖÃÖÐÐÄ¡¢¿ØÖÆ×ÜÏߵȹ¦ÄÜ¡£ÕâЩ¹¦ÄÜÖеÄÿһ¸ö¶¼¿ÉÒÔ¸ù¾ÝÐèÒªµ¥¶ÀʹÓã¬Ò²¿ÉÒÔÒ»ÆðʹÓÃÒÔ¹¹½¨È«·½Î»µÄ·þÎñÍø¸ñ£¬×ÜÖ®ConsulÌṩÁËÒ»ÖÖÍêÕûµÄ·þÎñÍø¸ñ½â¾ö·½°¸¡£ + +Spring Cloud Consul ¾ßÓÐÈçÏÂÌØÐÔ£º + +- Ö§³Ö·þÎñÖÎÀí£ºConsul×÷Ϊע²áÖÐÐÄʱ£¬Î¢·þÎñÖеÄÓ¦ÓÿÉÒÔÏòConsul×¢²á×Ô¼º£¬²¢ÇÒ¿ÉÒÔ´ÓConsul»ñÈ¡ÆäËûÓ¦ÓÃÐÅÏ¢£» +- Ö§³Ö¿Í»§¶Ë¸ºÔð¾ùºâ£º°üÀ¨RibbonºÍSpring Cloud LoadBalancer£» +- Ö§³ÖZuul£ºµ±Zuul×÷ÎªÍø¹ØÊ±£¬¿ÉÒÔ´ÓConsulÖÐ×¢²áºÍ·¢ÏÖÓ¦Óã» +- Ö§³Ö·Ö²¼Ê½ÅäÖùÜÀí£ºConsul×÷ΪÅäÖÃÖÐÐÄʱ£¬Ê¹ÓüüÖµ¶ÔÀ´´æ´¢ÅäÖÃÐÅÏ¢£» +- Ö§³Ö¿ØÖÆ×ÜÏߣº¿ÉÒÔÔÚÕû¸ö΢·þÎñϵͳÖÐͨ¹ý Control Bus ·Ö·¢Ê¼þÏûÏ¢¡£ + +# ʹÓÃConsul×÷Ϊע²áÖÐÐÄ + +# °²×°²¢ÔËÐÐConsul + +- Ê×ÏÈÎÒÃÇ´Ó¹ÙÍøÏÂÔØConsul£¬µØÖ·£ºhttps://www.consul.io/downloads.html + + + +- ÏÂÔØÍê³ÉºóÖ»ÓÐÒ»¸öexeÎļþ£¬Ë«»÷ÔËÐУ» +- ÔÚÃüÁîÐÐÖÐÊäÈëÒÔÏÂÃüÁî¿ÉÒԲ鿴°æ±¾ºÅ£º + + + + consul --version + + + +- ²é¿´°æ±¾ºÅÐÅÏ¢ÈçÏ£º + + + + Consul v1.6.1 + Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) + + +- ʹÓÿª·¢Ä£Ê½Æô¶¯£º + + + + consul agent -dev + + + + + + +- ͨ¹ýÒÔϵØÖ·¿ÉÒÔ·ÃÎÊConsulµÄÊ×Ò³£ºhttp://localhost:8500 + + + +# ´´½¨Ó¦ÓÃ×¢²áµ½Consul + +ÎÒÃÇͨ¹ý¸ÄÔìuser-serviceºÍribbon-serviceÀ´ÑÝʾÏ·þÎñ×¢²áÓë·¢ÏֵŦÄÜ£¬Ö÷ÒªÊǽ«Ó¦ÓÃÔ­À´µÄEureka×¢²áÖÐÐÄÖ§³Ö¸ÄΪConsul×¢²áÖÐÐÄÖ§³Ö¡£ + +- ´´½¨consul-user-serviceÄ£¿éºÍconsul-ribbon-serviceÄ£¿é£» +- ÐÞ¸ÄÏà¹ØÒÀÀµ£¬°ÑÔ­À´µÄEureka×¢²á·¢ÏÖµÄÒÀÀµ¸ÄΪConsulµÄ£¬²¢Ìí¼ÓSpringBoot ActuatorµÄÒÀÀµ£º + + + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + org.springframework.boot + spring-boot-starter-actuator + + + + + +- ÐÞ¸ÄÅäÖÃÎļþapplication.yml£¬½«EurekaµÄ×¢²á·¢ÏÖÅäÖøÄΪConsulµÄ£º + + + + server: + port: 8206 + spring: + application: + name: consul-user-service + cloud: + consul: #Consul·þÎñ×¢²á·¢ÏÖÅäÖà + host: localhost + port: 8500 + discovery: + service-name: ${spring.application.name} + + + + +- ÔËÐÐÁ½¸öconsul-user-serviceºÍÒ»¸öconsul-ribbon-service£¬ÔÚConsulÒ³ÃæÉÏ¿ÉÒÔ¿´µ½ÈçÏÂÐÅÏ¢£º + + + +# ¸ºÔؾùºâ¹¦ÄÜ + +ÓÉÓÚÎÒÃÇÔËÐÐÁËÁ½¸öconsul-user-service£¬¶øconsul-ribbon-serviceĬÈÏ»áÈ¥µ÷ÓÃËüµÄ½Ó¿Ú£¬ÎÒÃǵ÷ÓÃconsul-ribbon-serviceµÄ½Ó¿ÚÀ´ÑÝʾϸºÔؾùºâ¹¦ÄÜ¡£ + +¶à´Îµ÷Óýӿڣºhttp://localhost:8308/user/1 £¬¿ÉÒÔ·¢ÏÖÁ½¸öconsul-user-serviceµÄ¿ØÖÆÌ¨½»Ìæ´òÓ¡ÈçÏÂÐÅÏ¢¡£ + + + + 2019-10-20 10:39:32.580 INFO 12428 --- [io-8206-exec-10] c.macro.cloud.controller.UserController : ¸ù¾Ýid»ñÈ¡Óû§ÐÅÏ¢£¬Óû§Ãû³ÆÎª£ºmacro + + + + +# ʹÓÃConsul×÷ΪÅäÖÃÖÐÐÄ + +ÎÒÃÇͨ¹ý´´½¨consul-config-clientÄ£¿é£¬²¢ÔÚConsulÖÐÌí¼ÓÅäÖÃÐÅÏ¢À´ÑÝʾÏÂÅäÖùÜÀíµÄ¹¦ÄÜ¡£ + +# ´´½¨consul-config-clientÄ£¿é + +- ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + + + org.springframework.cloud + spring-cloud-starter-consul-config + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + + +- Ìí¼ÓÅäÖÃÎļþapplication.yml£¬ÆôÓõÄÊÇdev»·¾³µÄÅäÖ㺠+ + + + spring: + profiles: + active: dev + + + + +- Ìí¼ÓÅäÖÃÎļþbootstrap.yml£¬Ö÷ÒªÊǶÔConsulµÄÅäÖù¦ÄܽøÐÐÅäÖ㺠+ + + + server: + port: 9101 + spring: + application: + name: consul-config-client + cloud: + consul: + host: localhost + port: 8500 + discovery: + serviceName: consul-config-client + config: + enabled: true #ÊÇ·ñÆôÓÃÅäÖÃÖÐÐŦÄÜ + format: yaml #ÉèÖÃÅäÖÃÖµµÄ¸ñʽ + prefix: config #ÉèÖÃÅäÖÃËùÔÚĿ¼ + profile-separator: ':' #ÉèÖÃÅäÖõķָô·û + data-key: data #ÅäÖÃkeyµÄÃû×Ö£¬ÓÉÓÚConsulÊÇK/V´æ´¢£¬ÅäÖô洢ÔÚ¶ÔÓ¦KµÄVÖÐ + + +- ´´½¨ConfigClientController£¬´ÓConsulÅäÖÃÖÐÐÄÖлñÈ¡ÅäÖÃÐÅÏ¢£º + + + + /** + * Created by macro on 2019/9/11. + */ + @RestController + @RefreshScope + public class ConfigClientController { + + @Value("${config.info}") + private String configInfo; + + @GetMapping("/configInfo") + public String getConfigInfo() { + return configInfo; + } + } + + +# ÔÚConsulÖÐÌí¼ÓÅäÖà + +- ÔÚconsulÖÐÌí¼ÓÅäÖô洢µÄkeyΪ: + + + + config/consul-config-client:dev/data + + + + + +- ÔÚconsulÖÐÌí¼ÓÅäÖô洢µÄvalueΪ£º + + + + config: + info: "config info for dev" + + + + +- ´æ´¢ÐÅÏ¢½ØÍ¼ÈçÏ£º + + + +- Æô¶¯consul-config-client£¬µ÷Óýӿڲ鿴ÅäÖÃÐÅÏ¢£ºhttp://localhost:9101/configInfo + + + + config info for dev + + + + +# ConsulµÄ¶¯Ì¬Ë¢ÐÂÅäÖà + +ÎÒÃÇÖ»ÒªÐÞ¸ÄÏÂConsulÖеÄÅäÖÃÐÅÏ¢£¬Ôٴε÷Óò鿴ÅäÖõĽӿڣ¬¾Í»á·¢ÏÖÅäÖÃÒѾ­Ë¢Ð¡£»ØÏëÏÂÔÚʹÓÃSpring Cloud ConfigµÄʱºò£¬ÎÒÃÇÐèÒªµ÷Óýӿڣ¬Í¨¹ýSpring Cloud Bus²ÅÄÜË¢ÐÂÅäÖá£ConsulʹÓÃÆä×Ô´øµÄControl Bus ʵÏÖÁËÒ»ÖÖʼþ´«µÝ»úÖÆ£¬´Ó¶øÊµÏÖÁ˶¯Ì¬Ë¢Ð¹¦ÄÜ¡£ + +# ʹÓõ½µÄÄ£¿é + + + + springcloud-learning + ©À©¤©¤ consul-config-client -- ÓÃÓÚÑÝʾconsul×÷ΪÅäÖÃÖÐÐĵÄconsul¿Í»§¶Ë + ©À©¤©¤ consul-user-service -- ×¢²áµ½consulµÄÌṩUser¶ÔÏóCRUD½Ó¿ÚµÄ·þÎñ + ©¸©¤©¤ consul-service -- ×¢²áµ½consulµÄribbon·þÎñµ÷ÓòâÊÔ·þÎñ + + + + +# ÏîĿԴÂëµØÖ· + +https://github.com/macrozheng/springcloud-learning +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" new file mode 100644 index 0000000..960e5c7 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudEureka.md" @@ -0,0 +1,1052 @@ +Eureka Ò»´ÊÀ´Ô´ÓÚ¹ÅÏ£À°´Ê»ã£¬ÊÇ¡°·¢ÏÖÁË¡±µÄÒâ˼¡£ÔÚÈí¼þÁìÓò£¬Eureka ÊÇ Netflix ¹«Ë¾¿ª·¢µÄÒ»¿î¿ªÔ´µÄ·þÎñ×¢²áÓë·¢ÏÖ×é¼þ¡£ + +Spring Cloud ½« Eureka Óë Netflix ÖÐµÄÆäËû¿ªÔ´·þÎñ×é¼þ£¨ÀýÈç Ribbon¡¢Feign ÒÔ¼° Hystrix µÈ£©Ò»ÆðÕûºÏ½ø Spring Cloud Netflix Ä£¿éÖУ¬ÕûºÏºóµÄ×é¼þÈ«³ÆÎª Spring Cloud Netflix Eureka¡£ + +Eureka ÊÇ Spring Cloud Netflix Ä£¿éµÄ×ÓÄ£¿é£¬ËüÊÇ Spring Cloud ¶Ô Netflix Eureka µÄ¶þ´Î·â×°£¬Ö÷Òª¸ºÔð Spring Cloud µÄ·þÎñ×¢²áÓë·¢ÏÖ¹¦ÄÜ¡£ + +Spring Cloud ʹÓà Spring Boot ˼ÏëΪ Eureka Ôö¼ÓÁË×Ô¶¯»¯ÅäÖ㬿ª·¢ÈËÔ±Ö»ÐèÒªÒýÈëÏà¹ØÒÀÀµºÍ×¢½â£¬¾ÍÄܽ« Spring Boot ¹¹½¨µÄ΢·þÎñÇáËɵØÓë Eureka ½øÐÐÕûºÏ¡£ + +## Eureka Á½´ó×é¼þ + +Eureka ²ÉÓà CS£¨Client/Server£¬¿Í»§¶Ë/·þÎñÆ÷£© ¼Ü¹¹£¬Ëü°üÀ¨ÒÔÏÂÁ½´ó×é¼þ£º + +* **Eureka Server**£ºEureka ·þÎñ×¢²áÖÐÐÄ£¬Ö÷ÒªÓÃÓÚÌṩ·þÎñ×¢²á¹¦ÄÜ¡£µ±Î¢·þÎñÆô¶¯Ê±£¬»á½«×Ô¼ºµÄ·þÎñ×¢²áµ½ Eureka Server¡£Eureka Server ά»¤ÁËÒ»¸ö¿ÉÓ÷þÎñÁÐ±í£¬´æ´¢ÁËËùÓÐ×¢²áµ½ Eureka Server µÄ¿ÉÓ÷þÎñµÄÐÅÏ¢£¬ÕâЩ¿ÉÓ÷þÎñ¿ÉÒÔÔÚ Eureka Server µÄ¹ÜÀí½çÃæÖÐÖ±¹Û¿´µ½¡£ +* **Eureka Client**£ºEureka ¿Í»§¶Ë£¬Í¨³£Ö¸µÄÊÇ΢·þÎñϵͳÖи÷¸ö΢·þÎñ£¬Ö÷ÒªÓÃÓÚºÍ Eureka Server ½øÐн»»¥¡£ÔÚ΢·þÎñÓ¦ÓÃÆô¶¯ºó£¬Eureka Client »áÏò Eureka Server ·¢ËÍÐÄÌø£¨Ä¬ÈÏÖÜÆÚΪ 30 Ã룩¡£Èô Eureka Server ÔÚ¶à¸öÐÄÌøÖÜÆÚÄÚûÓнÓÊÕµ½Ä³¸ö Eureka Client µÄÐÄÌø£¬Eureka Server ½«Ëü´Ó¿ÉÓ÷þÎñÁбíÖÐÒÆ³ý£¨Ä¬ÈÏ 90 Ã룩¡£ + +> ×¢£º¡°ÐÄÌø¡±Ö¸µÄÊÇÒ»¶Î¶¨Ê±·¢Ë͵Ä×Ô¶¨ÒåÐÅÏ¢£¬ÈöԷ½ÖªµÀ×Ô¼º¡°´æ»î¡±£¬ÒÔÈ·±£Á¬½ÓµÄÓÐЧÐÔ¡£´ó²¿·Ö CS ¼Ü¹¹µÄÓ¦ÓóÌÐò¶¼²ÉÓÃÁËÐÄÌø»úÖÆ£¬·þÎñ¶ËºÍ¿Í»§¶Ë¶¼¿ÉÒÔ·¢ÐÄÌø¡£Í¨³£Çé¿öÏÂÊǿͻ§¶ËÏò·þÎñÆ÷¶Ë·¢ËÍÐÄÌø°ü£¬·þÎñ¶ËÓÃÓÚÅжϿͻ§¶ËÊÇ·ñÔÚÏß¡£ + +## Eureka ·þÎñ×¢²áÓë·¢ÏÖ + +Eureka ʵÏÖ·þÎñ×¢²áÓë·¢ÏÖµÄÔ­Àí£¬ÈçÏÂͼËùʾ¡£ + +![Eureka ·þÎñ×¢²áÓë·¢ÏÖ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010305209-0.png) +ͼ1£ºEureka Ô­Àíͼ + +ÉÏͼÖй²Éæ¼°µ½ÒÔÏ 3 ¸ö½ÇÉ«£º + +* **·þÎñ×¢²áÖÐÐÄ£¨Register Service£©**£ºËüÊÇÒ»¸ö Eureka Server£¬ÓÃÓÚÌṩ·þÎñ×¢²áºÍ·¢ÏÖ¹¦ÄÜ¡£ +* **·þÎñÌṩÕߣ¨Provider Service£©**£ºËüÊÇÒ»¸ö Eureka Client£¬ÓÃÓÚÌṩ·þÎñ¡£Ëü½«×Ô¼ºÌṩµÄ·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐÄ£¬ÒÔ¹©·þÎñÏû·ÑÕß·¢ÏÖ¡£ +* **·þÎñÏû·ÑÕߣ¨Consumer Service£©**£ºËüÊÇÒ»¸ö Eureka Client£¬ÓÃÓÚÏû·Ñ·þÎñ¡£Ëü¿ÉÒÔ´Ó·þÎñ×¢²áÖÐÐÄ»ñÈ¡·þÎñÁÐ±í£¬µ÷ÓÃËùÐèµÄ·þÎñ¡£ + +Eureka ʵÏÖ·þÎñ×¢²áÓë·¢ÏÖµÄÁ÷³ÌÈçÏ£º + +1. ´î½¨Ò»¸ö Eureka Server ×÷Ϊ·þÎñ×¢²áÖÐÐÄ£» +2. ·þÎñÌṩÕß Eureka Client Æô¶¯Ê±£¬»á°Ñµ±Ç°·þÎñÆ÷µÄÐÅÏ¢ÒÔ·þÎñÃû£¨spring.application.name£©µÄ·½Ê½×¢²áµ½·þÎñ×¢²áÖÐÐÄ£» +3. ·þÎñÏû·ÑÕß Eureka Client Æô¶¯Ê±£¬Ò²»áÏò·þÎñ×¢²áÖÐÐÄ×¢²á£» +4. ·þÎñÏû·ÑÕß»¹»á»ñȡһ·Ý¿ÉÓ÷þÎñÁÐ±í£¬¸ÃÁбíÖаüº¬ÁËËùÓÐ×¢²áµ½·þÎñ×¢²áÖÐÐĵķþÎñÐÅÏ¢£¨°üÀ¨·þÎñÌṩÕߺÍ×ÔÉíµÄÐÅÏ¢£©£» +5. ÔÚ»ñµÃÁË¿ÉÓ÷þÎñÁбíºó£¬·þÎñÏû·ÑÕßͨ¹ý HTTP »òÏûÏ¢Öмä¼þÔ¶³Ìµ÷Ó÷þÎñÌṩÕßÌṩµÄ·þÎñ¡£ + +·þÎñ×¢²áÖÐÐÄ£¨Eureka Server£©Ëù°çÑݵĽÇɫʮ·ÖÖØÒª£¬ËüÊÇ·þÎñÌṩÕߺͷþÎñÏû·ÑÕßÖ®¼äµÄÇÅÁº¡£·þÎñÌṩÕßÖ»Óн«×Ô¼ºµÄ·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐIJſÉÄܱ»·þÎñÏû·ÑÕßµ÷Ó㬶ø·þÎñÏû·ÑÕßÒ²Ö»ÓÐͨ¹ý·þÎñ×¢²áÖÐÐÄ»ñÈ¡¿ÉÓ÷þÎñÁбíºó£¬²ÅÄܵ÷ÓÃËùÐèµÄ·þÎñ¡£ + +## ʾÀý 1 + +ÏÂÃæ£¬ÎÒÃÇͨ¹ýÒ»¸ö°¸ÀýÀ´Õ¹Ê¾Ï Eureka ÊÇÈçºÎʵÏÖ·þÎñ×¢²áÓë·¢Ïֵġ£ + +#### 1\. ´´½¨Ö÷¹¤³Ì£¨Maven Project£© + +ÓÉÓÚ±¾°¸ÀýÖУ¬»áÉæ¼°µ½¶à¸öÓÉ Spring Boot ´´½¨µÄ΢·þÎñ£¬ÎªÁË·½±ã¹ÜÀí£¬ÕâÀïÎÒÃDzÉÓà Maven µÄ¶à Module ½á¹¹£¨¼´Ò»¸ö Project °üº¬¶à¸ö Module£©À´¹¹½¨¹¤³Ì¡£ + +´´½¨Ò»¸öÃûΪ spring-cloud-demo2 µÄ Maven Ö÷¹¤³Ì £¬È»ºóÔÚ¸ÃÖ÷¹¤³ÌµÄ pom.xml ÖÐʹÓà dependencyManagement À´¹ÜÀí Spring Cloud µÄ°æ±¾£¬ÄÚÈÝÈçÏ¡£ + + + + + +``` + + + + 4.0.0 + pom + + micro-service-cloud-api + + + org.springframework.boot + spring-boot-starter-parent + 2.3.6.RELEASE + + + net.biancheng.c + spring-cloud-demo2 + 0.0.1-SNAPSHOT + + 8 + 8 + UTF-8 + 1.8 + 1.8 + 4.12 + 1.2.17 + 1.16.18 + + + + + + org.springframework.cloud + spring-cloud-dependencies + Hoxton.SR12 + pom + import + + + + + microservicecloud + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-resources-plugin + + + $ + + + + + + + +``` + + + + + +#### 2\. ´´½¨¹«¹²×ÓÄ£¿é£¨Maven Module£© + +1) ÔÚÖ÷¹¤³ÌÏ£¬´´½¨Ò»¸öÃûΪ micro-service-cloud-api µÄ Maven Module£ºmicro-service-cloud-api£¬Æä pom.xml ÅäÖÃÈçÏ¡£ + + + + + +``` + + + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + 4.0.0 + micro-service-cloud-api + + 8 + 8 + + + + org.projectlombok + lombok + + + + +``` + + + + + +> ×¢£ºmicro-service-cloud-api ÊÇÕû¸ö¹¤³ÌµÄ¹«¹²×ÓÄ£¿é£¬Ëü°üº¬ÁËһЩÆäËû×ÓÄ£¿é¹²ÓеÄÄÚÈÝ£¬ÀýÈçʵÌåÀà¡¢¹«¹²¹¤¾ßÀà¡¢¹«¹²ÒÀÀµÏîµÈ¡£µ±ÆäËû×ÓÄ£¿éÐèҪʹÓù«¹²×ÓÄ£¿éÖеÄÄÚÈÝʱ£¬Ö»ÐèÒªÔÚÆä pom.xml ÒýÈ빫¹²×ÓÄ£¿éµÄÒÀÀµ¼´¿É¡£ + +2) ÔÚ micro-service-cloud-api µÄ net.biancheng.c.entity °üÏ£¬´´½¨Ò»¸öÃûΪ Dept µÄʵÌåÀ࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +@NoArgsConstructor //Î޲ι¹Ô캯Êý +@Data // ÌṩÀàµÄget¡¢set¡¢equals¡¢hashCode¡¢canEqual¡¢toString ·½·¨ +@Accessors(chain = true) +public class Dept implements Serializable { + private Integer deptNo; + private String deptName; + private String dbSource; +} + +``` + + + + + +#### 3\. ´î½¨·þÎñ×¢²áÖÐÐÄ + +1) ÔÚÖ÷¹¤³ÌÏ´´½¨Ò»¸öÃûΪ micro-service-cloud-eureka-7001 µÄ Spring Boot Module ×÷Ϊ·þÎñ×¢²áÖÐÐÄ£¬²¢ÔÚÆä pom.xml ÖÐÒýÈëÒÔÏÂÒÀÀµ¡£ + + + + + +``` + + + + 4.0.0 + + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + net.biancheng.c + micro-service-cloud-eureka-7001 + 0.0.1-SNAPSHOT + micro-service-cloud-eureka-7001 + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + +``` + + + + + +2) ÔÚ micro-service-cloud-eureka-7001 µÄÀà·¾¶£¨/resouces Ŀ¼£©Ï£¬Ìí¼ÓÒ»¸öÅäÖÃÎļþ application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` +server: + port: 7001 #¸Ã Module µÄ¶Ë¿ÚºÅ + +eureka: + instance: + hostname: localhost #eureka·þÎñ¶ËµÄʵÀýÃû³Æ£¬ + + client: + register-with-eureka: false #false±íʾ²»Ïò×¢²áÖÐÐÄ×¢²á×Ô¼º¡£ + fetch-registry: false #false±íʾ×Ô¼º¶Ë¾ÍÊÇ×¢²áÖÐÐÄ£¬ÎÒµÄÖ°Ôð¾ÍÊÇά»¤·þÎñʵÀý£¬²¢²»ÐèҪȥ¼ìË÷·þÎñ + service-url: + defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #µ¥»ú°æ·þÎñ×¢²áÖÐÐÄ +``` + + + + + +3) ÔÚ micro-service-cloud-eureka-7001 µÄÖ÷Æô¶¯ÀàÉÏʹÓà @EnableEurekaServer ×¢½â¿ªÆô·þÎñ×¢²áÖÐÐŦÄÜ£¬½ÓÊÜÆäËû·þÎñµÄ×¢²á£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; +@SpringBootApplication +@EnableEurekaServer //¿ªÆô Eureka server,½ÓÊÜÆäËû΢·þÎñµÄ×¢²á +public class MicroServiceCloudEureka7001Application { + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudEureka7001Application.class, args); + } +} + +``` + + + + + +4) Æô¶¯ micro-service-cloud-eureka-7001£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ Eureka ·þÎñ×¢²áÖÐÐÄÖ÷Ò³£¬µØÖ·Îª¡°http://localhost:7001/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka Server 7001 ·þÎñ×¢²áÖÐÐÄ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010306360-1.png) + +ͼ2£ºEureka 7001 ·þÎñ×¢²áÖÐÐÄ + +#### 4\. ´î½¨·þÎñÌṩÕß + +1) ÔÚÖ÷¹¤³ÌÏ´´½¨Ò»¸öÃûΪ micro-service-cloud-provider-dept-8001 µÄ Spring Boot Module£¬²¢ÔÚÆä pom.xml ÖÐÒýÈëÒÔÏÂÒÀÀµ¡£ + + + + + +``` + + + + 4.0.0 + + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + + net.biancheng.c + micro-service-cloud-provider-dept-8001 + 0.0.1-SNAPSHOT + micro-service-cloud-provider-dept-8001 + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + net.biancheng.c + micro-service-cloud-api + ${project.version} + + + + junit + junit + 4.12 + + + + mysql + mysql-connector-java + 5.1.49 + + + + ch.qos.logback + logback-core + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.2.0 + + + + org.springframework + springloaded + 1.2.8.RELEASE + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + + org.mybatis.generator + mybatis-generator-maven-plugin + 1.4.0 + + src/main/resources/mybatis-generator/generatorConfig.xml + true + + true + + + + + mysql + mysql-connector-java + 5.1.49 + + + org.mybatis.generator + mybatis-generator-core + 1.4.0 + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + + + + + +2) ÔÚ micro-service-cloud-provider-dept-8001 Àà·¾¶£¨/resources Ŀ¼£©Ï£¬Ìí¼ÓÅäÖÃÎļþ application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` + +server: + port: 8001 #·þÎñ¶Ë¿ÚºÅ +spring: + application: + name: microServiceCloudProviderDept #΢·þÎñÃû³Æ£¬¶ÔÍⱩ©µÄ΢·þÎñÃû³Æ£¬Ê®·ÖÖØÒª +################################################## JDBC ͨÓÃÅäÖà ########################################## + datasource: + username: root #Êý¾Ý¿âµÇ½Óû§Ãû + password: root #Êý¾Ý¿âµÇ½ÃÜÂë + url: jdbc:mysql://127.0.0.1:3306/bianchengbang_jdbc #Êý¾Ý¿âurl + driver-class-name: com.mysql.jdbc.Driver #Êý¾Ý¿âÇý¶¯ + +############################### ²»¼ì²é spring.config.import=configserver:################## +# cloud: +# config: +# enabled: false +###################################### MyBatis ÅäÖà ###################################### +mybatis: + # Ö¸¶¨ mapper.xml µÄλÖà + mapper-locations: classpath:mybatis/mapper/*.xml + #ɨÃèʵÌåÀàµÄλÖÃ,ÔÚ´Ë´¦Ö¸Ã÷ɨÃèʵÌåÀàµÄ°ü£¬ÔÚ mapper.xml ÖоͿÉÒÔ²»Ð´ÊµÌåÀàµÄȫ·¾¶Ãû + type-aliases-package: net.biancheng.c.entity + configuration: + #ĬÈÏ¿ªÆôÍÕ·åÃüÃû·¨£¬¿ÉÒÔ²»ÓÃÉèÖøÃÊôÐÔ + map-underscore-to-camel-case: true +########################################### Spring cloud ×Ô¶¨Òå·þÎñÃû³ÆºÍ ip µØÖ·############################################### +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + defaultZone: http://eureka7001.com:7001/eureka #Õâ¸öµØÖ·ÊÇ 7001×¢²áÖÐÐÄÔÚ application.yml Öб©Â¶³öÀ´¶î×¢²áµØÖ· £¨µ¥»ú°æ£© + + instance: + instance-id: spring-cloud-provider-8001 #×Ô¶¨Òå·þÎñÃû³ÆÐÅÏ¢ + prefer-ip-address: true #ÏÔʾ·ÃÎÊ·¾¶µÄ ip µØÖ· +########################################## spring cloud ʹÓà Spring Boot actuator ¼à¿ØÍêÉÆÐÅÏ¢################################### +# Spring Boot 2.50¶Ô actuator ¼à¿ØÆÁ±ÎÁË´ó¶àÊýµÄ½Úµã£¬Ö»±©Â¶ÁË heath ½Úµã£¬±¾¶ÎÅäÖã¨*£©¾ÍÊÇΪÁË¿ªÆôËùÓеĽڵã +management: + endpoints: + web: + exposure: + include: "*" # * ÔÚyaml ÎļþÊôÓڹؼü×Ö£¬ËùÒÔÐèÒª¼ÓÒýºÅ +info: + app.name: micro-service-cloud-provider-dept + company.name: c.biancheng.net + build.aetifactId: @project.artifactId@ + build.version: @project.version@ +``` + + + + + +3) ÔÚ net.biancheng.c.mapper °üÏ´´½¨Ò»¸öÃûΪ DeptMapper µÄ½Ó¿Ú£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.mapper; +import net.biancheng.c.entity.Dept; +import org.apache.ibatis.annotations.Mapper; +import java.util.List; +@Mapper +public interface DeptMapper { + //¸ù¾ÝÖ÷¼ü»ñÈ¡Êý¾Ý + Dept selectByPrimaryKey(Integer deptNo); + //»ñÈ¡±íÖеÄÈ«²¿Êý¾Ý + List GetAll(); +} + +``` + + + + + +4) ÔÚ resources/mybatis/mapper/ Ŀ¼Ï£¬´´½¨Ò»¸öÃûΪ DeptMapper.xml µÄ MyBatis Ó³ÉäÎļþ£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` + + + + + + + + + + + + dept_no + , dept_name, db_source + + + + + + +``` + + + + + +5) ÔÚ net.biancheng.c.service °üÏ´´½¨Ò»¸öÃûΪ DeptService µÄ½Ó¿Ú£¬´úÂëÈçÏ¡£ + + + + + +``` + + +package net.biancheng.c.service; +import net.biancheng.c.entity.Dept; +import java.util.List; +public interface DeptService { + Dept get(Integer deptNo); + List selectAll(); +} + +``` + + + + + +6) ÔÚ net.biancheng.c.service.impl °üÏ´´½¨ DeptService ½Ó¿ÚµÄʵÏÖÀà DeptServiceImpl£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.service.impl; + +import net.biancheng.c.entity.Dept; +import net.biancheng.c.mapper.DeptMapper; +import net.biancheng.c.service.DeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service("deptService") +public class DeptServiceImpl implements DeptService { + @Autowired + private DeptMapper deptMapper; + + @Override + public Dept get(Integer deptNo) { + return deptMapper.selectByPrimaryKey(deptNo); + } + + @Override + public List selectAll() { + return deptMapper.GetAll(); + } +} + +``` + + + + + +7) ÔÚ net.biancheng.c.controller °üÏ´´½¨Ò»¸öÃûΪ DeptController µÄ Controller À࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.controller; + +import lombok.extern.slf4j.Slf4j; +import net.biancheng.c.entity.Dept; +import net.biancheng.c.service.DeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** +* ·þÎñÌṩÕߵĿØÖƲã +* author:cÓïÑÔÖÐÎÄÍø c.biancheng.net +*/ +@RestController +@Slf4j +public class DeptController { + @Autowired + private DeptService deptService; + + @Value("${server.port}") + private String serverPort; + + @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) + public Dept get(@PathVariable("id") int id) { + return deptService.get(id); + } + + @RequestMapping(value = "/dept/list", method = RequestMethod.GET) + public List list() { + return deptService.selectAll(); + } +} +``` + + + + + +8) ÔÚ micro-service-cloud-provider-dept-8001 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableEurekaClient ×¢½â¿ªÆô Eureka ¿Í»§¶Ë¹¦ÄÜ£¬½«·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐÄ£¨Eureka Server£©£¬´úÂëÈçÏ¡£ + + + + + +``` + + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient // Spring cloud Eureka ¿Í»§¶Ë£¬×Ô¶¯½«±¾·þÎñ×¢²áµ½ Eureka Server ×¢²áÖÐÐÄÖÐ +public class MicroServiceCloudProviderDept8001Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudProviderDept8001Application.class, args); + } +} + +``` + + + + + +9) ÒÀ´ÎÆô¶¯ micro-service-cloud-eureka-7001 ºÍ micro-service-cloud-provider-dept-8001£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÔÙ´ÎÎÊ Eureka ·þÎñ×¢²áÖÐÐÄÖ÷Ò³£¨http://localhost:7001/£©£¬ÈçÏÂͼ¡£ + +![Eureka Client ×¢²áµ½·þÎñ×¢²áÖÐÐÄ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010304436-2.png) +ͼ3£º·þÎñÌṩÕß×¢²áµ½·þÎñ×¢²áÖÐÐÄ + +´Óͼ 3 ¿ÉÒÔ¿´µ½£¬ Instances currently registered with Eureka £¨×¢²áµ½ Eureka Server µÄʵÀý£©Ñ¡ÏîÖÐÒѾ­°üº¬ÁËÒ»Ìõ·þÎñÐÅÏ¢£¬¼´ÒѾ­ÓзþÎñ×¢²áµ½ Eureka Server ÉÏÁË¡£ + +Instances currently registered with Eureka Ñ¡ÏîÖаüº¬ÒÔÏÂÄÚÈÝ£º + +* Application£ºMICROSERVICECLOUDPROVIDERDEPT£¬¸ÃȡֵΪ micro-service-cloud-provider-dept-8001 ÅäÖÃÎļþ application.yml ÖÐ spring.application.name µÄȡֵ¡£ +* Status£º UP (1) - spring-cloud-provider-8001£¬UP ±íʾ·þÎñÔÚÏߣ¬ (1) ±íʾÓм¯ÈºÖзþÎñµÄÊýÁ¿£¬spring-cloud-provider-8001 ÔòÊÇ micro-service-cloud-provider-dept-8001 ÅäÖÃÎļþ application.yml ÖÐ eureka.instance.instance-id µÄȡֵ¡£ + +10) ÔÚ MySQL µÄ bianchengbang_jdbc Êý¾Ý¿âÖÐÖ´ÐÐÒÔÏ SQL£¬×¼±¸²âÊÔÊý¾Ý¡£ + +``` +DROP TABLE IF EXISTS `dept`; +CREATE TABLE `dept` ( + `dept_no` int NOT NULL AUTO_INCREMENT, + `dept_name` varchar(255) DEFAULT NULL, + `db_source` varchar(255) DEFAULT NULL, + PRIMARY KEY (`dept_no`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + + +INSERT INTO `dept` VALUES ('1', '¿ª·¢²¿', 'bianchengbang_jdbc'); +INSERT INTO `dept` VALUES ('2', 'ÈËʲ¿', 'bianchengbang_jdbc'); +INSERT INTO `dept` VALUES ('3', '²ÆÎñ²¿', 'bianchengbang_jdbc'); +INSERT INTO `dept` VALUES ('4', 'Êг¡²¿', 'bianchengbang_jdbc'); +INSERT INTO `dept` VALUES ('5', 'ÔËά²¿', 'bianchengbang_jdbc'); +``` + +11) ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:8001/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka Client 8001 ·ÃÎÊÊý¾Ý¿â](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010301b2-3.png) +ͼ4£º·þÎñÌṩÕßÌṩ·þÎñ·ÃÎÊÊý¾Ý¿â + +## Eureka Server ¼¯Èº + +ÔÚ΢·þÎñ¼Ü¹¹ÖУ¬Ò»¸öϵͳÍùÍùÓÉÊ®¼¸ÉõÖÁ¼¸Ê®¸ö·þÎñ×é³É£¬Èô½«ÕâЩ·þÎñÈ«²¿×¢²áµ½Í¬Ò»¸ö Eureka Server ÖУ¬¾Í¼«ÓпÉÄܵ¼Ö Eureka Server Òò²»¿°Öظº¶ø±ÀÀ££¬×îÖÕµ¼ÖÂÕû¸öϵͳ̱»¾¡£½â¾öÕâ¸öÎÊÌâ×îÖ±½ÓµÄ°ì·¨¾ÍÊDz¿Êð Eureka Server ¼¯Èº¡£ + +ÎÒÃÇÖªµÀ£¬ÔÚ Eureka ʵÏÖ·þÎñ×¢²áÓë·¢ÏÖʱһ¹²Éæ¼°ÁË 3 ¸ö½ÇÉ«£º·þÎñ×¢²áÖÐÐÄ¡¢·þÎñÌṩÕßÒÔ¼°·þÎñÏû·ÑÕߣ¬ÕâÈý¸ö½ÇÉ«·Ö¹¤Ã÷È·£¬¸÷˾ÆäÖ°¡£µ«ÊÇÆäʵÔÚ Eureka ÖУ¬ËùÓзþÎñ¶¼¼ÈÊÇ·þÎñÏû·ÑÕßÒ²ÊÇ·þÎñÌṩÕߣ¬·þÎñ×¢²áÖÐÐÄ Eureka Server Ò²²»ÀýÍâ¡£ + +ÎÒÃÇÔڴ·þÎñ×¢²áÖÐÐÄʱ£¬ÔÚ application.yml ÖÐÉæ¼°ÁËÕâÑùµÄÅäÖ㺠+ + + + + +``` + + +eureka: + client: + register-with-eureka: false #false ±íʾ²»Ïò×¢²áÖÐÐÄ×¢²á×Ô¼º¡£ + fetch-registry: false #false±íʾ×Ô¼º¶Ë¾ÍÊÇ×¢²áÖÐÐÄ£¬Ö°Ôð¾ÍÊÇά»¤·þÎñʵÀý£¬²¢²»ÐèҪȥ¼ìË÷·þÎñ +``` + + + + + +ÕâÑùÉèÖõÄÔ­ÒòÊÇ micro-service-cloud-eureka-7001 ±¾Éí×Ô¼º¾ÍÊÇ·þÎñ×¢²áÖÐÐÄ£¬·þÎñ×¢²áÖÐÐÄÊDz»Äܽ«×Ô¼º×¢²áµ½×Ô¼ºÉíÉϵ쬵«·þÎñ×¢²áÖÐÐÄÊÇ¿ÉÒÔ½«×Ô¼º×÷Ϊ·þÎñÏòÆäËûµÄ·þÎñ×¢²áÖÐÐÄ×¢²á×Ô¼ºµÄ¡£ + +¾Ù¸öÀý×Ó£¬ÓÐÁ½¸ö Eureka Server ·Ö±ðΪ A ºÍ B£¬ËäÈ» A ²»Äܽ«×Ô¼º×¢²áµ½ A ÉÏ£¬B Ò²²»Äܽ«×Ô¼º×¢²áµ½ B ÉÏ£¬µ« A ÊÇ¿ÉÒÔ×÷Ϊһ¸ö·þÎñ°Ñ×Ô¼º×¢²áµ½ B Éϵģ¬Í¬Àí B Ò²¿ÉÒÔ½«×Ô¼º×¢²áµ½ A ÉÏ¡£ + +ÕâÑù¾Í¿ÉÒÔÐγÉÒ»×黥Ïà×¢²áµÄ Eureka Server ¼¯Èº£¬µ±·þÎñÌṩÕß·¢ËÍ×¢²áÇëÇóµ½ Eureka Server ʱ£¬Eureka Server »á½«ÇëÇóת·¢¸ø¼¯ÈºÖÐËùÓÐÓëÖ®ÏàÁ¬µÄ Eureka Server ÉÏ£¬ÒÔʵÏÖ Eureka Server Ö®¼äµÄ·þÎñͬ²½¡£ + +ͨ¹ý·þÎñͬ²½£¬·þÎñÏû·ÑÕß¿ÉÒÔÔÚ¼¯ÈºÖеÄÈÎÒâһ̨ Eureka Server ÉÏ»ñÈ¡·þÎñÌṩÕßÌṩµÄ·þÎñ¡£ÕâÑù£¬¼´Ê¹¼¯ÈºÖеÄij¸ö·þÎñ×¢²áÖÐÐÄ·¢Éú¹ÊÕÏ£¬·þÎñÏû·ÑÕßÈÔÈ»¿ÉÒÔ´Ó¼¯ÈºÖÐµÄÆäËû Eureka Server ÖлñÈ¡·þÎñÐÅÏ¢²¢µ÷Ó㬶ø²»»áµ¼ÖÂϵͳµÄÕûÌå̱»¾£¬Õâ¾ÍÊÇ Eureka Server ¼¯ÈºµÄ¸ß¿ÉÓÃÐÔ¡£ + +## ʾÀý 2 + +ÏÂÃæÎÒÃÇÔÚʾÀý 1 µÄ»ù´¡ÉϽøÐÐÀ©Õ¹£¬¹¹½¨Ò»¸öÓµÓÐ 3 ¸ö Eureka Server ʵÀýµÄ¼¯Èº¡£ + +1\. ²ÎÕÕ micro-service-cloud-eureka-7001 µÄ´î½¨¹ý³Ì£¬ÔÚÖ÷¹¤³ÌÏÂÁíÍâÔÙ´´½¨Á½¸ö Eureka Server£ºmicro-service-cloud-eureka-7002 ºÍ micro-service-cloud-eureka-7003£¬´ËʱÕâ 3 ¸ö Eureka Server ÎÞÂÛÊÇ Maven ÒÀÀµ¡¢´úÂ뻹ÊÇÅäÖö¼ÊÇһģһÑùµÄ¡£ + +2\. ÐÞ¸Ä micro-service-cloud-eureka-7001¡¢micro-service-cloud-eureka-7002¡¢micro-service-cloud-eureka-7003 ÖÐ application.yml µÄÅäÖ㬾ßÌåÅäÖÃÈçÏ ¡£ + +micro-service-cloud-eureka-7001 ÖÐ application.yml µÄÅäÖÃÈçÏ¡£ + + + + + +``` +server: + port: 7001 #¶Ë¿ÚºÅ +eureka: + instance: + hostname: eureka7001.com #eureka·þÎñ¶ËµÄʵÀýÃû³Æ + client: + register-with-eureka: false #false ±íʾ²»Ïò×¢²áÖÐÐÄ×¢²á×Ô¼º¡£ + fetch-registry: false #false ±íʾ×Ô¼º¶Ë¾ÍÊÇ×¢²áÖÐÐÄ£¬ÎÒµÄÖ°Ôð¾ÍÊÇά»¤·þÎñʵÀý£¬²¢²»ÐèҪȥ¼ìË÷·þÎñ + service-url: + #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #µ¥»ú°æ + defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #¼¯Èº°æ ½«µ±Ç°µÄ Eureka Server ×¢²áµ½ 7003 ºÍ 7003 ÉÏ£¬ÐγÉÒ»×黥Ïà×¢²áµÄ Eureka Server ¼¯Èº +``` + + + + + +micro-service-cloud-eureka-7002 ÖÐ application.yml µÄÅäÖÃÈçÏ¡£ + + + + + +``` + +server: + port: 7002 #¶Ë¿ÚºÅ + +eureka: + instance: + hostname: eureka7002.com #Eureka Server ʵÀýÃû³Æ + + client: + register-with-eureka: false #false ±íʾ²»Ïò×¢²áÖÐÐÄ×¢²á×Ô¼º¡£ + fetch-registry: false #false ±íʾ×Ô¼º¶Ë¾ÍÊÇ×¢²áÖÐÐÄ£¬ÎÒµÄÖ°Ôð¾ÍÊÇά»¤·þÎñʵÀý£¬²¢²»ÐèҪȥ¼ìË÷·þÎñ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #½«Õâ¸ö Eureka Server ×¢²áµ½ 7001 ºÍ 7003 ÉÏ +``` + + + + + +micro-service-cloud-eureka-7003 ÖÐ application.yml µÄÅäÖÃÈçÏ¡£ + + + + + +``` + +server: + port: 7003 #¶Ë¿ÚºÅ + +eureka: + instance: + hostname: eureka7003.com #Eureka Server ʵÀýÃû³Æ + + client: + register-with-eureka: false #false ±íʾ²»Ïò×¢²áÖÐÐÄ×¢²á×Ô¼º¡£ + fetch-registry: false #false ±íʾ×Ô¼º¶Ë¾ÍÊÇ×¢²áÖÐÐÄ£¬ÎÒµÄÖ°Ôð¾ÍÊÇά»¤·þÎñʵÀý£¬²¢²»ÐèҪȥ¼ìË÷·þÎñ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #½«Õâ¸ö Eureka Server ×¢²áµ½ 7001 ºÍ 7002 ÉÏ +``` + + + + + +3\. ÓÉÓÚÎÒÃÇÊÇÔÚ±¾µØ´î½¨µÄ Eureka Server ¼¯Èº£¬Òò´ËÎÒÃÇÐèÒªÐ޸ı¾µØµÄ host Îļþ£¬Windows ²Ù×÷ϵͳµÄµçÄÔÔÚ C:/Windows/System/drivers/etc/hosts ÖÐÐ޸ģ¬Mac ϵͳµÄµçÄÔÔòÐèÒªÔÚ vim/etc/hosts ÖÐÐ޸ģ¬ÐÞ¸ÄÄÚÈÝÈçÏ¡£ + +``` +#Spring Cloud eureka ¼¯Èº +127.0.0.1 eureka7001.com +127.0.0.1 eureka7002.com +127.0.0.1 eureka7003.com +``` + + +4\. ÐÞ¸Ä micro-service-cloud-provider-dept-8001£¨·þÎñÌṩÕߣ©ÅäÖÃÎļþ application.yml ÖÐ eureka.client.service-url.defaultZone µÄȡֵ£¬½«·þÎñ×¢²áµ½ Eureka Server ¼¯ÈºÉÏ£¬¾ßÌåÅäÖÃÈçÏ¡£ + + + + + +``` + +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + #defaultZone: http://eureka7001.com:7001/eureka #Õâ¸öµØÖ·ÊÇ 7001 ×¢²áÖÐÐÄÔÚ application.yml Öб©Â¶³öÀ´µÄ×¢²áµØÖ· £¨µ¥»ú°æ£© + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka Server ¼¯Èº +``` + + + + + +5\. Æô¶¯ micro-service-cloud-eureka-7001£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:7001/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ¼¯Èº 7001](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010302b3-4.png) + +ͼ5£ºEureka Server ¼¯Èº -7001 + +´ÓÉÏͼ¿ÉÒÔ¿´µ½£¬ ·þÎñÌṩÕߣ¨micro-service-cloud-provider-dept-8001£©µÄ·þÎñÒѾ­×¢²áµ½ÁË Eureka Server 7001£¬²¢ÇÒÔÚ DS Replicas Ñ¡ÏîÖÐÒ²ÏÔʾÁ˼¯ÈºÖеÄÁíÍâÁ½¸ö Eureka Server£ºEureka Server 7002 ºÍ Eureka Server 7003¡£ + +6\. Æô¶¯ micro-service-cloud-eureka-7002£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7002.com:7002/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ¼¯Èº 7002](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101030E36-5.png) +ͼ6£ºEureka Server ¼¯Èº -7002 + + +´ÓÉÏͼ¿ÉÒÔ¿´µ½£¬ ·þÎñÌṩÕߣ¨micro-service-cloud-provider-dept-8001£©ËùÌṩµÄ·þÎñÒѾ­×¢²áµ½ÁË Eureka Server 7002£¬²¢ÇÒÔÚ DS Replicas Ñ¡ÏîÖÐÒ²ÏÔʾÁ˼¯ÈºÖеÄÁíÍâÁ½¸ö Eureka Server£ºEureka Server 7001 ºÍ Eureka Server 7003¡£ + +7. Æô¶¯ micro-service-cloud-eureka-7003£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7003.com:7003/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ¼¯Èº 7003](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010306034-6.png) +ͼ7£ºEureka Server ¼¯Èº -7003 + +´ÓÉÏͼ¿ÉÒÔ¿´µ½£¬ ·þÎñÌṩÕߣ¨micro-service-cloud-provider-dept-8001£©ËùÌṩµÄ·þÎñÒѾ­×¢²áµ½ÁË Eureka Server 7003£¬²¢ÇÒÔÚ DS Replicas Ñ¡ÏîÖÐÒ²ÏÔʾÁ˼¯ÈºÖеÄÁíÍâÁ½¸ö Eureka Server£ºEureka Server 7001 ºÍ Eureka Server 7002¡£ + +×Ô´ËÎÒÃǾÍÍê³ÉÁË Eureka Server ¼¯ÈºµÄ´î½¨ºÍʹÓᣠ+ +## Eureka ×ÔÎÒ±£»¤»úÖÆ + +µ±ÎÒÃÇÔÚ±¾µØµ÷ÊÔ»ùÓÚ Eureka µÄ³ÌÐòʱ£¬Eureka ·þÎñ×¢²áÖÐÐĺÜÓпÉÄÜ»á³öÏÖÈçÏÂͼËùʾµÄºìÉ«¾¯¸æ¡£ + +![Eureka ×ÔÎÒ±£»¤»úÖÆ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10103014a-7.png) +ͼ8£ºEureka ×ÔÎÒ±£»¤Ìáʾ + + +ʵ¼ÊÉÏ£¬Õâ¸ö¾¯¸æÊÇ´¥·¢ÁË Eureka µÄ×ÔÎÒ±£»¤»úÖÆ¶ø³öÏֵġ£Ä¬ÈÏÇé¿öÏ£¬Èç¹û Eureka Server ÔÚÒ»¶Îʱ¼äÄÚ£¨Ä¬ÈÏΪ 90 Ã룩ûÓнÓÊÕµ½Ä³¸ö·þÎñÌṩÕߣ¨Eureka Client£©µÄÐÄÌø£¬¾Í»á½«Õâ¸ö·þÎñÌṩÕßÌṩµÄ·þÎñ´Ó·þÎñ×¢²á±íÖÐÒÆ³ý¡£ ÕâÑù·þÎñÏû·ÑÕß¾ÍÔÙÒ²ÎÞ·¨´Ó·þÎñ×¢²áÖÐÐÄÖлñÈ¡µ½Õâ¸ö·þÎñÁË£¬¸üÎÞ·¨µ÷Óø÷þÎñ¡£ + +µ«ÔÚʵ¼ÊµÄ·Ö²¼Ê½Î¢·þÎñϵͳÖУ¬½¡¿µµÄ·þÎñ£¨Eureka Client£©Ò²ÓпÉÄÜ»áÓÉÓÚÍøÂç¹ÊÕÏ£¨ÀýÈçÍøÂçÑÓ³Ù¡¢¿¨¶Ù¡¢Óµ¼·µÈÔ­Òò£©¶øÎÞ·¨Óë Eureka Server Õý³£Í¨Ñ¶¡£Èô´Ëʱ Eureka Server ÒòΪûÓнÓÊÕÐÄÌø¶øÎ󽫽¡¿µµÄ·þÎñ´Ó·þÎñÁбíÖÐÒÆ³ý£¬ÕâÏÔÈ»ÊDz»ºÏÀíµÄ¡£¶ø Eureka µÄ×ÔÎÒ±£»¤»úÖÆ¾ÍÊÇÀ´½â¾ö´ËÎÊÌâµÄ¡£ + +Ëùν ¡°Eureka µÄ×ÔÎÒ±£»¤»úÖÆ¡±£¬ÆäÖÐÐÄ˼Ïë¾ÍÊÇ¡°ºÃËÀ²»ÈçÀµ»î×Å¡±¡£Èç¹û Eureka Server ÔÚÒ»¶Îʱ¼äÄÚûÓнÓÊÕµ½ Eureka Client µÄÐÄÌø£¬ÄÇô Eureka Server ¾Í»á¿ªÆô×ÔÎÒ±£»¤Ä£Ê½£¬½«ËùÓÐµÄ Eureka Client µÄ×¢²áÐÅÏ¢±£»¤ÆðÀ´£¬¶ø²»ÊÇÖ±½Ó´Ó·þÎñ×¢²á±íÖÐÒÆ³ý¡£Ò»µ©ÍøÂç»Ö¸´£¬ÕâЩ Eureka Client ÌṩµÄ·þÎñ»¹¿ÉÒÔ¼ÌÐø±»·þÎñÏû·ÑÕßÏû·Ñ¡£ + +×ÛÉÏ£¬Eureka µÄ×ÔÎÒ±£»¤»úÖÆÊÇÒ»ÖÖÓ¦¶ÔÍøÂçÒì³£µÄ°²È«±£»¤´ëÊ©¡£ËüµÄ¼Ü¹¹ÕÜѧÊÇ£ºÄþ¿Éͬʱ±£ÁôËùÓÐ΢·þÎñ£¨½¡¿µµÄ·þÎñºÍ²»½¡¿µµÄ·þÎñ¶¼»á±£Áô£©Ò²²»Ã¤Ä¿ÒƳýÈκν¡¿µµÄ·þÎñ¡£Í¨¹ý Eureka µÄ×ÔÎÒ±£»¤»úÖÆ£¬¿ÉÒÔÈà Eureka Server ¼¯Èº¸ü¼ÓµÄ½¡×³¡¢Îȶ¨¡£ + +> Eureka µÄ×ÔÎÒ±£»¤»úÖÆÒ²´æÔÚ±×¶Ë¡£Èç¹ûÔÚ Eureka ×ÔÎÒ±£»¤»úÖÆ´¥·¢ÆÚ¼ä£¬·þÎñÌṩÕßÌṩµÄ·þÎñ³öÏÖÎÊÌ⣬ÄÇô·þÎñÏû·ÑÕ߾ͺÜÈÝÒ×»ñÈ¡µ½ÒѾ­²»´æÔڵķþÎñ½ø¶ø³öÏÖµ÷ÓÃʧ°ÜµÄÇé¿ö£¬´Ëʱ£¬ÎÒÃÇ¿ÉÒÔͨ¹ý¿Í»§¶ËµÄÈÝ´í»úÖÆÀ´½â¾ö´ËÎÊÌ⣬ÏêÇéÇë²Î¿¼ [Spring Cloud Netflix Ribbon](http://c.biancheng.net/springcloud/ribbon.html) ºÍ [Spring Cloud Netflix Hystrix](http://c.biancheng.net/springcloud/hystrix.html)¡£ + +ĬÈÏÇé¿öÏ£¬Eureka µÄ×ÔÎÒ±£»¤»úÖÆÊÇ¿ªÆôµÄ£¬Èç¹ûÏëÒª¹Ø±Õ£¬ÔòÐèÒªÔÚÅäÖÃÎļþÖÐÌí¼ÓÒÔÏÂÅäÖᣠ+ + + + + +``` + + +eureka: +server: +enable-self-preservation: false # false ¹Ø±Õ Eureka µÄ×ÔÎÒ±£»¤»úÖÆ£¬Ä¬ÈÏÊÇ¿ªÆô,Ò»°ã²»½¨Òé´ó¼ÒÐÞ¸Ä + +``` + + + + + +## ʾÀý 3 + +ÏÂÃæÎÒÃÇͨ¹ýÒ»¸öʵÀý£¬À´Ñé֤ϠEureka µÄ×ÔÎÒ±£»¤»úÖÆ¡£ + +1\. ÔÚ micro-service-cloud-eureka-7001 µÄÅäÖÃÎļþ application.yml ÖÐÌí¼ÓÒÔÏÂÅäÖã¬¹Ø±Õ Eureka µÄ×ÔÎÒ±£»¤»úÖÆ¡£ + + + + + +``` + + +eureka: +server: +enable-self-preservation: false # false ¹Ø±Õ Eureka µÄ×ÔÎÒ±£»¤»úÖÆ£¬Ä¬ÈÏÊÇ¿ªÆô,Ò»°ã²»½¨Òé´ó¼ÒÐÞ¸Ä + +``` + + + + + +2\. ¼¯ÈºÖÐµÄ micro-service-cloud-eureka-7002 ºÍ micro-service-cloud-eureka-7002 ²»×÷ÈκÎÐ޸쬼´ËüÃǵÄ×ÔÎÒ±£»¤»úÖÆÊÇ¿ªÆôµÄ¡£ + +3\. ÖØÆô Eureka Server ¼¯ÈºÒÔ¼° micro-service-cloud-provider-dept-8001£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:7001/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ×ÔÎÒ±£»¤»úÖÆ 7001](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101030J48-8.png) +ͼ9£ºEureka ¹Ø±Õ×ÔÎÒ±£»¤»úÖÆ + +´Óͼ 8 £¬Äú¿ÉÒÔ¿´µ½ÒÔÏÂÄÚÈÝ£º + +* ÔÚ DS Replicas Ñ¡ÏîÉÏÃæ³öÏÖÁ˺ìÉ«¾¯¸æÐÅÏ¢¡°THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.¡±£¬³öÏÖ¸ÃÐÅÏ¢Ôò±íʾ¡° Eureka ×ÔÎÒ±£»¤Ä£Ê½Òѹرա£¡± +* micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒѾ­×¢²áµ½¸Ã Eureka Server ÖС£ + +4\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7002.com:7002/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ×ÔÎÒ±£»¤»úÖÆ 7002](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010301938-9.png) +ͼ10£ºEureka ×ÔÎÒ±£»¤»úÖÆ + +´Óͼ 9 ¿ÉÒÔ¿´³ö£¬micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒ²ÒѾ­×¢²áµ½µ±Ç° Eureka Server ÖУ¬µ« DS Replicas Ñ¡ÏîÉÏ·½Ã»ÓÐÈκξ¯¸æÌáʾ¡£ + +5\. ¹Ø±Õ micro-service-cloud-provider-dept-8001£¬µÈ´ý¼¸·ÖÖÓ£¬ÔٴηÃÎÊ¡°http://eureka7001.com:7001/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ×ÔÎÒ±£»¤»úÖÆ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10103014J-10.png) +ͼ11£ºEureka ¹Ø±Õ×ÔÎÒ±£»¤»úÖÆ-2 + +ÔÚͼ 10 ÖÐ £¬ÎÒÃÇ¿ÉÒÔ¿´µ½ÒÔÏÂÄÚÈÝ£º + +* ÔÚ DS Replicas Ñ¡ÏîÉÏÃæ³öÏÖÁ˺ìÉ«¾¯¸æÐÅÏ¢¡°RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.¡±£¬³öÏÖ¸ÃÐÅÏ¢Ôò±íʾ Eureka µÄ×ÔÎÒ±£»¤Ä£Ê½Òѹرգ¬ÇÒÒѾ­ÓзþÎñ±»ÒƳý¡£ +* micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒѾ­´Ó·þÎñÁбíÖÐÒÆ³ý¡£ + +6\. ÔٴηÃÎÊ¡°http://eureka7002.com:7002/¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Eureka ×ÔÎÒ±£»¤»úÖÆ¿ªÆô](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1010301210-11.png) +ͼ12£ºEureka ×ÔÎÒ±£»¤»úÖÆÉúЧ + +ÔÚͼ 11 ÖÐ £¬Äú¿ÉÒÔ¿´µ½ÒÔÏÂÄÚÈÝ£º + +* ÔÚ DS Replicas Ñ¡ÏîÉÏÃæ³öÏÖÁ˺ìÉ«¾¯¸æÐÅÏ¢¡°EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.¡±£¬³öÏÖ¸ÃÐÅÏ¢±íÃ÷ Eureka µÄ×ÔÎÒ±£»¤»úÖÆ´¦ÓÚ¿ªÆô״̬£¬ÇÒÒѾ­±»´¥·¢¡£ +* micro-service-cloud-provider-dept-8001 µÄ·þÎñÐÅÏ¢ÒÀÈ»±£´æ Eureka Server ·þÎñ×¢²á±íÖУ¬²¢Î´±»ÒƳý¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" new file mode 100644 index 0000000..e99968d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudGateway.md" @@ -0,0 +1,513 @@ +ÔÚ΢·þÎñ¼Ü¹¹ÖУ¬Ò»¸öϵͳÍùÍùÓɶà¸ö΢·þÎñ×é³É£¬¶øÕâЩ·þÎñ¿ÉÄܲ¿ÊðÔÚ²»Í¬»ú·¿¡¢²»Í¬µØÇø¡¢²»Í¬ÓòÃûÏ¡£ÕâÖÖÇé¿öÏ£¬¿Í»§¶Ë£¨ÀýÈçä¯ÀÀÆ÷¡¢ÊÖ»ú¡¢Èí¼þ¹¤¾ßµÈ£©ÏëÒªÖ±½ÓÇëÇóÕâЩ·þÎñ£¬¾ÍÐèÒªÖªµÀËüÃǾßÌåµÄµØÖ·ÐÅÏ¢£¬ÀýÈç IP µØÖ·¡¢¶Ë¿ÚºÅµÈ¡£ + +ÕâÖÖ¿Í»§¶ËÖ±½ÓÇëÇó·þÎñµÄ·½Ê½´æÔÚÒÔÏÂÎÊÌ⣺ + +* µ±·þÎñÊýÁ¿ÖÚ¶àʱ£¬¿Í»§¶ËÐèҪά»¤´óÁ¿µÄ·þÎñµØÖ·£¬Õâ¶ÔÓÚ¿Í»§¶ËÀ´Ëµ£¬ÊǷdz£·±Ëö¸´Ôӵġ£ +* ÔÚijЩ³¡¾°Ï¿ÉÄÜ»á´æÔÚ¿çÓòÇëÇóµÄÎÊÌâ¡£ +* Éí·ÝÈÏÖ¤µÄÄѶȴó£¬Ã¿¸ö΢·þÎñÐèÒª¶ÀÁ¢ÈÏÖ¤¡£ + +ÎÒÃÇ¿ÉÒÔͨ¹ý API Íø¹ØÀ´½â¾öÕâЩÎÊÌ⣬ÏÂÃæ¾ÍÈÃÎÒÃÇÀ´¿´¿´Ê²Ã´ÊÇ API Íø¹Ø¡£ + +## API Íø¹Ø + +API Íø¹ØÊÇÒ»¸ö´î½¨ÔÚ¿Í»§¶ËºÍ΢·þÎñÖ®¼äµÄ·þÎñ£¬ÎÒÃÇ¿ÉÒÔÔÚ API Íø¹ØÖд¦ÀíһЩ·ÇÒµÎñ¹¦ÄܵÄÂß¼­£¬ÀýÈçȨÏÞÑéÖ¤¡¢¼à¿Ø¡¢»º´æ¡¢ÇëÇó·Óɵȡ£ + +API Íø¹Ø¾ÍÏñÕû¸ö΢·þÎñϵͳµÄÃÅÃæÒ»Ñù£¬ÊÇϵͳ¶ÔÍâµÄΨһÈë¿Ú¡£ÓÐÁËËü£¬¿Í»§¶Ë»áÏȽ«ÇëÇó·¢Ë͵½ API Íø¹Ø£¬È»ºóÓÉ API Íø¹Ø¸ù¾ÝÇëÇóµÄ±êʶÐÅÏ¢½«ÇëÇóת·¢µ½Î¢·þÎñʵÀý¡£ + +![](http://c.biancheng.net/uploads/allimg/211210/101P46212-0.png) +ͼ1£ºÁ½ÖÖ·þÎñ·ÃÎÊ·½Ê½¶Ô±È + +¶ÔÓÚ·þÎñÊýÁ¿ÖÚ¶à¡¢¸´ÔӶȽϸߡ¢¹æÄ£±È½Ï´óµÄϵͳÀ´Ëµ£¬Ê¹Óà API Íø¹Ø¾ßÓÐÒÔϺô¦£º + +* ¿Í»§¶Ëͨ¹ý API Íø¹ØÓë΢·þÎñ½»»¥Ê±£¬¿Í»§¶ËÖ»ÐèÒªÖªµÀ API Íø¹ØµØÖ·¼´¿É£¬¶ø²»ÐèҪά»¤´óÁ¿µÄ·þÎñµØÖ·£¬¼ò»¯Á˿ͻ§¶ËµÄ¿ª·¢¡£ +* ¿Í»§¶ËÖ±½ÓÓë API Íø¹ØÍ¨ÐÅ£¬Äܹ»¼õÉÙ¿Í»§¶ËÓë¸÷¸ö·þÎñµÄ½»»¥´ÎÊý¡£ +* ¿Í»§¶ËÓëºó¶ËµÄ·þÎññîºÏ¶È½µµÍ¡£ +* ½ÚÊ¡Á÷Á¿£¬Ìá¸ßÐÔÄÜ£¬ÌáÉýÓû§ÌåÑé¡£ +* API Íø¹Ø»¹ÌṩÁ˰²È«¡¢Á÷¿Ø¡¢¹ýÂË¡¢»º´æ¡¢¼Æ·ÑÒÔ¼°¼à¿ØµÈ API ¹ÜÀí¹¦ÄÜ¡£ + +³£¼ûµÄ API Íø¹ØÊµÏÖ·½°¸Ö÷ÒªÓÐÒÔÏ 5 ÖÖ£º + +* Spring Cloud Gateway +* Spring Cloud Netflix Zuul +* Kong +* Nginx+Lua +* Traefik + +±¾½Ú£¬ÎÒÃÇ¾Í¶Ô Spring Cloud Gateway ½øÐÐÏêϸ½éÉÜ¡£ + +## Spring Cloud Gateway``` + +Spring Cloud Gateway ÊÇ Spring Cloud ÍŶӻùÓÚ Spring 5.0¡¢Spring Boot 2.0 ºÍ Project Reactor µÈ¼¼Êõ¿ª·¢µÄ¸ßÐÔÄÜ API Íø¹Ø×é¼þ¡£ + +Spring Cloud Gateway Ö¼ÔÚÌṩһÖÖ¼òµ¥¶øÓÐЧµÄ;¾¶À´·¢ËÍ API£¬²¢ÎªËüÃÇÌṩºáÇйØ×¢µã£¬ÀýÈ磺°²È«ÐÔ£¬¼à¿Ø/Ö¸±êºÍµ¯ÐÔ¡£``` + +> Spring Cloud Gateway ÊÇ»ùÓÚ WebFlux ¿ò¼ÜʵÏֵ쬶ø WebFlux ¿ò¼Üµ×²ãÔòʹÓÃÁ˸ßÐÔÄÜµÄ Reactor ģʽͨÐÅ¿ò¼Ü Netty¡£ + +#### Spring Cloud Gateway ºËÐĸÅÄî + +Spring Cloud GateWay ×îÖ÷ÒªµÄ¹¦ÄܾÍÊÇ·ÓÉת·¢£¬¶øÔÚ¶¨Òåת·¢¹æÔòʱÖ÷񻃾¼°ÁËÒÔÏÂÈý¸öºËÐĸÅÄÈçÏÂ±í¡£ + +| ºËÐĸÅÄî | ÃèÊö | +| --- | --- | +| Route£¨Â·ÓÉ£© | Íø¹Ø×î»ù±¾µÄÄ£¿é¡£ËüÓÉÒ»¸ö ID¡¢Ò»¸öÄ¿±ê URI¡¢Ò»×é¶ÏÑÔ£¨Predicate£©ºÍÒ»×é¹ýÂËÆ÷£¨Filter£©×é³É¡£ | +| Predicate£¨¶ÏÑÔ£© | ·ÓÉת·¢µÄÅжÏÌõ¼þ£¬ÎÒÃÇ¿ÉÒÔͨ¹ý Predicate ¶Ô```HTTP ÇëÇó½øÐÐÆ¥Å䣬ÀýÈçÇëÇó·½Ê½¡¢ÇëÇó·¾¶¡¢ÇëÇóÍ·¡¢²ÎÊýµÈ£¬Èç¹ûÇëÇóÓë¶ÏÑÔÆ¥Åä³É¹¦£¬Ôò½«ÇëÇóת·¢µ½ÏàÓ¦µÄ·þÎñ¡£ | +| Filter£¨¹ýÂËÆ÷£© | ¹ýÂËÆ÷£¬ÎÒÃÇ¿ÉÒÔʹÓÃËü¶ÔÇëÇó½øÐÐÀ¹½ØºÍÐ޸쬻¹¿ÉÒÔʹÓÃËü¶ÔÉÏÎĵÄÏìÓ¦½øÐÐÔÙ´¦Àí¡£ | + +> ×¢Ò⣺ÆäÖÐ Route ºÍ Predicate ±ØÐëͬʱÉùÃ÷¡£ + +#### Spring Cloud Gateway µÄÌØÕ÷ + +Spring Cloud Gateway ¾ßÓÐÒÔÏÂÌØÐÔ£º + +* »ùÓÚ Spring Framework 5¡¢Project Reactor ºÍ Spring Boot 2.0 ¹¹½¨¡£ +* Äܹ»ÔÚÈÎÒâÇëÇóÊôÐÔÉÏÆ¥Åä·ÓÉ¡£ +* predicates£¨¶ÏÑÔ£© ºÍ filters£¨¹ýÂËÆ÷£©ÊÇÌØ¶¨ÓÚ·Óɵġ£ +* ¼¯³ÉÁË Hystrix ÈÛ¶ÏÆ÷¡£ +* ¼¯³ÉÁË Spring Cloud DiscoveryClient£¨·þÎñ·¢ÏÖ¿Í»§¶Ë£©¡£ +* Ò×ÓÚ±àд¶ÏÑԺ͹ýÂËÆ÷¡£ +* Äܹ»ÏÞÖÆÇëÇóƵÂÊ¡£ +* Äܹ»ÖØÐ´ÇëÇó·¾¶¡£ + +## Gateway µÄ¹¤×÷Á÷³Ì + +Spring Cloud Gateway ¹¤×÷Á÷³ÌÈçÏÂͼ¡£ + +![Spring Cloud Gateway ¹¤×÷Á÷³Ì](http://c.biancheng.net/uploads/allimg/211210/101P45T2-1.png) +ͼ2£ºSpring Cloud Gateway ¹¤×÷Á÷³Ì + +Spring Cloud Gateway ¹¤×÷Á÷³Ì˵Ã÷ÈçÏ£º + +1. ¿Í»§¶Ë½«ÇëÇó·¢Ë͵½```Spring Cloud Gateway ÉÏ¡£ +2. Spring Cloud Gateway ͨ¹ý```Gateway Handler Mapping ÕÒµ½ÓëÇëÇóÏàÆ¥ÅäµÄ·ÓÉ£¬½«Æä·¢Ë͸ø Gateway Web Handler¡£ +3. Gateway Web Handler```ͨ¹ýÖ¸¶¨µÄ¹ýÂËÆ÷Á´£¨Filter Chain£©£¬½«ÇëÇóת·¢µ½Êµ¼ÊµÄ·þÎñ½ÚµãÖУ¬Ö´ÐÐÒµÎñÂß¼­·µ»ØÏìÓ¦½á¹û¡£ +4. ¹ýÂËÆ÷Ö®¼äÓÃÐéÏß·Ö¿ªÊÇÒòΪ¹ýÂËÆ÷¿ÉÄÜ»áÔÚת·¢ÇëÇó֮ǰ£¨pre£©»òÖ®ºó£¨post£©Ö´ÐÐÒµÎñÂß¼­¡£ +5. ¹ýÂËÆ÷£¨Filter£©¿ÉÒÔÔÚÇëÇó±»×ª·¢µ½·þÎñ¶Ëǰ£¬¶ÔÇëÇó½øÐÐÀ¹½ØºÍÐ޸ģ¬ÀýÈç²ÎÊýУÑ顢ȨÏÞУÑé¡¢Á÷Á¿¼à¿Ø¡¢ÈÕÖ¾Êä³öÒÔ¼°Ð­Òéת»»µÈ¡£ +6. ¹ýÂËÆ÷¿ÉÒÔÔÚÏìÓ¦·µ»Ø¿Í»§¶Ë֮ǰ£¬¶ÔÏìÓ¦½øÐÐÀ¹½ØºÍÔÙ´¦Àí£¬ÀýÈçÐÞ¸ÄÏìÓ¦ÄÚÈÝ»òÏìӦͷ¡¢ÈÕÖ¾Êä³ö¡¢Á÷Á¿¼à¿ØµÈ¡£ +7. ÏìӦԭ··µ»Ø¸ø¿Í»§¶Ë¡£ + +×ܶøÑÔÖ®£¬¿Í»§¶Ë·¢Ë͵½ Spring Cloud Gateway µÄÇëÇóÐèҪͨ¹ýÒ»¶¨µÄÆ¥ÅäÌõ¼þ£¬²ÅÄܶ¨Î»µ½ÕæÕýµÄ·þÎñ½Úµã¡£ÔÚ½«ÇëÇóת·¢µ½·þÎñ½øÐд¦ÀíµÄ¹ý³Ìǰºó£¨pre ºÍ post£©£¬ÎÒÃÇ»¹¿ÉÒÔ¶ÔÇëÇóºÍÏìÓ¦½øÐÐһЩ¾«Ï¸»¯¿ØÖÆ¡£ + +Predicate ¾ÍÊÇ·Óɵį¥ÅäÌõ¼þ£¬¶ø Filter ¾ÍÊǶÔÇëÇóºÍÏìÓ¦½øÐо«Ï¸»¯¿ØÖƵŤ¾ß¡£ÓÐÁËÕâÁ½¸öÔªËØ£¬ÔÙ¼ÓÉÏÄ¿±ê URI£¬¾Í¿ÉÒÔʵÏÖÒ»¸ö¾ßÌåµÄ·ÓÉÁË¡£ + +## Predicate ¶ÏÑÔ + +Spring Cloud Gateway ͨ¹ý```Predicate ¶ÏÑÔÀ´ÊµÏÖ Route ·Óɵį¥Å乿Ôò¡£¼òµ¥µã˵£¬Predicate ÊÇ·ÓÉת·¢µÄÅжÏÌõ¼þ£¬ÇëÇóÖ»ÓÐÂú×ãÁË Predicate µÄÌõ¼þ£¬²Å»á±»×ª·¢µ½Ö¸¶¨µÄ·þÎñÉϽøÐд¦Àí¡£ + +ʹÓà Predicate ¶ÏÑÔÐèҪעÒâÒÔÏ 3 µã£º + +* Route ·ÓÉÓë Predicate ¶ÏÑԵĶÔÓ¦¹ØÏµÎª¡°Ò»¶Ô¶à¡±£¬Ò»¸ö·ÓÉ¿ÉÒÔ°üº¬¶à¸ö²»Í¬¶ÏÑÔ¡£ +* Ò»¸öÇëÇóÏëҪת·¢µ½Ö¸¶¨µÄ·ÓÉÉÏ£¬¾Í±ØÐëͬʱƥÅä·ÓÉÉϵÄËùÓжÏÑÔ¡£ +* µ±Ò»¸öÇëÇóͬʱÂú×ã¶à¸ö·ÓɵĶÏÑÔÌõ¼þʱ£¬ÇëÇóÖ»»á±»Ê׸ö³É¹¦Æ¥ÅäµÄ·ÓÉת·¢¡£ + +![](http://c.biancheng.net/uploads/allimg/211210/101P42B6-2.png) +ͼ3£ºPredicate ¶ÏÑÔÆ¥Åä + +³£¼ûµÄ Predicate ¶ÏÑÔÈçÏÂ±í£¨¼ÙÉèת·¢µÄ URI Ϊ http://localhost:8001£©¡£ + +| ¶ÏÑÔ | ʾÀý | ˵Ã÷ | +| --- | --- | --- | +| Path | - Path=/dept/list/**``` | µ±ÇëÇó·¾¶Óë /dept/list/** Æ¥Åäʱ£¬¸ÃÇëÇó²ÅÄܱ»×ª·¢µ½```http://localhost:8001 ÉÏ¡£ | +| Before | - Before=2021-10-20T11:47:34.255+08:00[Asia/Shanghai] | ÔÚ```2021 Äê 10 Ô 20 ÈÕ 11 ʱ 47 ·Ö 34.255 Ãë֮ǰµÄÇëÇ󣬲Żᱻת·¢µ½```http://localhost:8001 ÉÏ¡£ | +| After | - After=2021-10-20T11:47:34.255+08:00[Asia/Shanghai] | ÔÚ 2021 Äê 10 Ô 20 ÈÕ 11 ʱ 47 ·Ö 34.255 ÃëÖ®ºóµÄÇëÇ󣬲Żᱻת·¢µ½```http://localhost:8001 ÉÏ¡£ | +| Between | - Between=2021-10-20T15:18:33.226+08:00[Asia/Shanghai],2021-10-20T15:23:33.226+08:00[Asia/Shanghai] | ÔÚ 2021 Äê 10 Ô 20 ÈÕ 15 ʱ 18 ·Ö 33.226 Ãë µ½ 2021 Äê 10 Ô 20 ÈÕ 15 ʱ 23 ·Ö 33.226 ÃëÖ®¼äµÄÇëÇ󣬲Żᱻת·¢µ½```http://localhost:8001 ·þÎñÆ÷ÉÏ¡£ | +| Cookie | - Cookie=name,c.biancheng.net | Я´ø```Cookie ÇÒ Cookie µÄÄÚÈÝΪ```name=c.biancheng.net µÄÇëÇ󣬲Żᱻת·¢µ½ http://localhost:8001 ÉÏ¡£ | +| Header | - Header=X-Request-Id,\d+ | ÇëÇóÍ·ÉÏЯ´øÊôÐÔ X-Request-Id ÇÒÊôÐÔֵΪÕûÊýµÄÇëÇ󣬲Żᱻת·¢µ½ http://localhost:8001 ÉÏ¡£ | +| Method | - Method=GET | Ö»ÓÐ GET ÇëÇó²Å»á±»×ª·¢µ½ http://localhost:8001 ÉÏ¡£ | + +#### ʾÀý + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´ÑÝʾÏ Predicate ÊÇÈçºÎʹÓõġ£ + +1\. ÔÚ¸¸¹¤³Ì```spring-cloud-demo2 Ï´´½¨Ò»¸öÃûΪ```micro-service-cloud-gateway-9527 µÄ Spring Boot Ä£¿é£¬²¢ÔÚÆä pom.xml ÖÐÒýÈëÏà¹ØÒÀÀµ£¬ÅäÖÃÈçÏ¡£ + + + + + +``` + + + 4.0.0 + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + + net.biancheng.c + micro-service-cloud-gateway-9527 + 0.0.1-SNAPSHOT + micro-service-cloud-gateway-9527 + Demo project for Spring Boot + + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.boot + spring-boot-devtools + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` + + + + + +2\. ÔÚ```micro-service-cloud-gateway-9527 µÄÀà·¾¶£¨/resources Ŀ¼£©Ï£¬Ð½¨Ò»¸öÅäÖÃÎļþ application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` +server: + port: 9527 #¶Ë¿ÚºÅ +spring: + application: + name: microServiceCloudGateway + cloud: + gateway: #Íø¹ØÂ·ÓÉÅäÖà + routes: + #½« micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒþ²ØÆðÀ´£¬²»±©Â¶¸ø¿Í»§¶Ë£¬Ö»¸ø¿Í»§¶Ë±©Â¶ API Íø¹ØµÄµØÖ· 9527 + - id: provider_dept_list_routh #·ÓÉ id,ûÓй̶¨¹æÔò£¬µ«Î¨Ò»£¬½¨ÒéÓë·þÎñÃû¶ÔÓ¦ + uri: http://localhost:8001 #Æ¥ÅäºóÌṩ·þÎñµÄ·ÓɵØÖ· + predicates: + #ÒÔÏÂÊǶÏÑÔÌõ¼þ£¬±ØÑ¡È«²¿·ûºÏÌõ¼þ + - Path=/dept/list/** #¶ÏÑÔ£¬Â·¾¶Æ¥Åä ×¢Ò⣺Path ÖÐ P Ϊ´óд + - Method=GET #Ö»ÄÜʱ GET ÇëÇóʱ£¬²ÅÄÜ·ÃÎÊ + +eureka: + instance: + instance-id: micro-service-cloud-gateway-9527 + hostname: micro-service-cloud-gateway + client: + fetch-registry: true + register-with-eureka: true + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ +``` + + + + + +ÒÔÉÏÅäÖÃÖУ¬ÎÒÃÇÔÚ spring.cloud.gateway.routes ÏÂʹÓà predicates ÊôÐÔ£¬¶¨ÒåÁËÒÔÏÂÁ½¸ö¶ÏÑÔÌõ¼þ£º + +``` +- Path=/dept/list/** +- Method=GET +``` + +Ö»Óе±Íⲿ£¨¿Í»§¶Ë£©·¢Ë͵½ micro-service-cloud-gateway-9527 µÄ HTTP ÇëÇóͬʱÂú×ãÒÔÉÏËùÓеĶÏÑÔʱ£¬¸ÃÇëÇó²Å»á±»×ª·¢µ½Ö¸¶¨µÄ·þÎñ¶ËÖУ¨¼´ http://localhost:8001£©¡£3\. ÔÚ```micro-service-cloud-gateway-9527 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableEurekaClient ×¢½â¿ªÆô Eureka ¿Í»§¶Ë¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient +public class MicroServiceCloudGateway9527Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudGateway9527Application.class, args); + } +} + +``` + + + + + +4\. ÒÀ´ÎÆô¶¯ Eureka ·þÎñ×¢²áÖÐÐÄ£¨¼¯Èº£©¡¢micro-service-cloud-provider-dept-8001 ÒÔ¼°```micro-service-cloud-gateway-9527£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:9527/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Spring Cloud ¶ÏÑÔÌõ¼þ](http://c.biancheng.net/uploads/allimg/211210/101P43419-3.png) +ͼ4£ºSpring Cloud Gateway ·ÓÉת·¢ + +## Spring Cloud Gateway ¶¯Ì¬Â·ÓÉ + +ĬÈÏÇé¿öÏ£¬Spring Cloud Gateway »á¸ù¾Ý·þÎñ×¢²áÖÐÐÄ£¨ÀýÈç Eureka Server£©ÖÐά»¤µÄ·þÎñÁÐ±í£¬ÒÔ·þÎñÃû£¨spring.application.name£©×÷Ϊ·¾¶´´½¨¶¯Ì¬Â·ÓɽøÐÐת·¢£¬´Ó¶øÊµÏÖ¶¯Ì¬Â·Óɹ¦ÄÜ¡£ + +ÎÒÃÇ¿ÉÒÔÔÚÅäÖÃÎļþÖУ¬½« Route µÄ uri µØÖ·ÐÞ¸ÄΪÒÔÏÂÐÎʽ¡£ + +```lb://service-name``` + +ÒÔÉÏÅäÖÃ˵Ã÷ÈçÏ£º + +* lb£ºuri µÄЭÒ飬±íʾ¿ªÆô Spring Cloud Gateway µÄ¸ºÔؾùºâ¹¦ÄÜ¡£ +* service-name£º·þÎñÃû£¬Spring Cloud Gateway »á¸ù¾ÝËü»ñÈ¡µ½¾ßÌåµÄ΢·þÎñµØÖ·¡£ + +#### ʾÀý + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´Õ¹Ê¾Ï Spring Cloud Gateway ÊÇÈçºÎʵÏÖ¶¯Ì¬Â·Óɵġ£ + +1\. ÐÞ¸Ä```micro-service-cloud-gateway-9527 ÖÐ application.yml µÄÅäÖã¬Ê¹ÓÃ×¢²áÖÐÐÄÖеÄ΢·þÎñÃû´´½¨¶¯Ì¬Â·ÓɽøÐÐת·¢£¬ÅäÖÃÈçÏ¡£ + + + + + +``` +server: + port: 9527 #¶Ë¿ÚºÅ + +spring: + application: + name: microServiceCloudGateway #·þÎñ×¢²áÖÐÐÄ×¢²áµÄ·þÎñÃû + + cloud: + gateway: #Íø¹ØÂ·ÓÉÅäÖà + discovery: + locator: + enabled: true #ĬÈÏֵΪ true£¬¼´Ä¬ÈÏ¿ªÆô´Ó×¢²áÖÐÐĶ¯Ì¬´´½¨Â·ÓɵŦÄÜ£¬ÀûÓÃ΢·þÎñÃû½øÐзÓÉ + + routes: + #½« micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒþ²ØÆðÀ´£¬²»±©Â¶¸ø¿Í»§¶Ë£¬Ö»¸ø¿Í»§¶Ë±©Â¶ API Íø¹ØµÄµØÖ· 9527 + - id: provider_dept_list_routh #·ÓÉ id,ûÓй̶¨¹æÔò£¬µ«Î¨Ò»£¬½¨ÒéÓë·þÎñÃû¶ÔÓ¦ + uri: lb://MICROSERVICECLOUDPROVIDERDEPT #¶¯Ì¬Â·ÓÉ£¬Ê¹Ó÷þÎñÃû´úÌæÉÏÃæµÄ¾ßÌå´ø¶Ë¿Ú http://eureka7001.com:9527/dept/list + + predicates: + #ÒÔÏÂÊǶÏÑÔÌõ¼þ£¬±ØÑ¡È«²¿·ûºÏÌõ¼þ + - Path=/dept/list/** #¶ÏÑÔ£¬Â·¾¶Æ¥Åä ×¢Ò⣺Path ÖÐ P Ϊ´óд + - Method=GET #Ö»ÄÜʱ GET ÇëÇóʱ£¬²ÅÄÜ·ÃÎÊ + +eureka: + instance: + instance-id: micro-service-cloud-gateway-9527 + hostname: micro-service-cloud-gateway + client: + fetch-registry: true + register-with-eureka: true + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ +``` + + + + + +2\. ÒÀ´ÎÆô¶¯ Eureka ·þÎñ×¢²áÖÐÐÄ£¨¼¯Èº£©¡¢·þÎñÌṩÕß¼¯Èº£¨micro-service-cloud-provider-dept-8001/8002/8003£©ÒÔ¼°```micro-service-cloud-gateway-9527¡£ + +3\. ÔÚä¯ÀÀÆ÷ÖзÃÎÊ¡°http://localhost:9527/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Gateway ¶¯Ì¬Â·ÓÉ](http://c.biancheng.net/uploads/allimg/211210/101P46240-4.gif) +ͼ5£ºSpring Cloud ʵÏÖ¶¯Ì¬Â·ÓÉ + +## Filter ¹ýÂËÆ÷ + +ͨ³£Çé¿öÏ£¬³öÓÚ°²È«·½ÃæµÄ¿¼ÂÇ£¬·þÎñ¶ËÌṩµÄ·þÎñÍùÍù¶¼»áÓÐÒ»¶¨µÄУÑéÂß¼­£¬ÀýÈçÓû§µÇ½״̬УÑ顢ǩÃûУÑéµÈ¡£ + +ÔÚ΢·þÎñ¼Ü¹¹ÖУ¬ÏµÍ³Óɶà¸ö΢·þÎñ×é³É£¬ËùÓÐÕâЩ·þÎñ¶¼ÐèÒªÕâЩУÑéÂß¼­£¬´ËʱÎÒÃǾͿÉÒÔ½«ÕâЩУÑéÂß¼­Ð´µ½ Spring Cloud Gateway µÄ Filter ¹ýÂËÆ÷ÖС£ + +#### Filter µÄ·ÖÀà + +Spring Cloud Gateway ÌṩÁËÒÔÏÂÁ½ÖÖÀàÐ͵ĹýÂËÆ÷£¬¿ÉÒÔ¶ÔÇëÇóºÍÏìÓ¦½øÐо«Ï¸»¯¿ØÖÆ¡£ + +| ¹ýÂËÆ÷ÀàÐÍ | ˵Ã÷ | +| --- | --- | +| Pre ÀàÐÍ | ÕâÖÖ¹ýÂËÆ÷ÔÚÇëÇó±»×ª·¢µ½Î¢·þÎñ֮ǰ¿ÉÒÔ¶ÔÇëÇó½øÐÐÀ¹½ØºÍÐ޸ģ¬ÀýÈç²ÎÊýУÑ顢ȨÏÞУÑé¡¢Á÷Á¿¼à¿Ø¡¢ÈÕÖ¾Êä³öÒÔ¼°Ð­Òéת»»µÈ²Ù×÷¡£ | +| Post ÀàÐÍ | ÕâÖÖ¹ýÂËÆ÷ÔÚ΢·þÎñ¶ÔÇëÇó×ö³öÏìÓ¦ºó¿ÉÒÔ¶ÔÏìÓ¦½øÐÐÀ¹½ØºÍÔÙ´¦Àí£¬ÀýÈçÐÞ¸ÄÏìÓ¦ÄÚÈÝ»òÏìӦͷ¡¢ÈÕÖ¾Êä³ö¡¢Á÷Á¿¼à¿ØµÈ¡£ | + +°´ÕÕ×÷Ó÷¶Î§»®·Ö£¬Spring Cloud gateway µÄ Filter ¿ÉÒÔ·ÖΪ 2 Àࣺ + +* GatewayFilter£ºÓ¦ÓÃÔÚµ¥¸ö·ÓÉ»òÕßÒ»×é·ÓÉÉϵĹýÂËÆ÷¡£ +* GlobalFilter£ºÓ¦ÓÃÔÚËùÓеÄ·ÓÉÉϵĹýÂËÆ÷¡£ + +#### GatewayFilter Íø¹Ø¹ýÂËÆ÷ + +GatewayFilter ÊÇ Spring Cloud Gateway Íø¹ØÖÐÌṩµÄÒ»ÖÖÓ¦ÓÃÔÚµ¥¸ö»òÒ»×é·ÓÉÉϵĹýÂËÆ÷¡£Ëü¿ÉÒÔ¶Ôµ¥¸ö·ÓÉ»òÕßÒ»×é·ÓÉÉÏ´«ÈëµÄÇëÇóºÍ´«³öÏìÓ¦½øÐÐÀ¹½Ø£¬²¢ÊµÏÖһЩÓëÒµÎñÎ޹صŦÄÜ£¬±ÈÈçµÇ½״̬УÑ顢ǩÃûУÑ顢ȨÏÞУÑé¡¢ÈÕÖ¾Êä³ö¡¢Á÷Á¿¼à¿ØµÈ¡£ + +GatewayFilter ÔÚÅäÖÃÎļþ£¨ÀýÈç application.yml£©ÖеÄд·¨Óë Predicate ÀàËÆ£¬¸ñʽÈçÏ¡£ + + + + + +``` + +spring: + cloud: + gateway: + routes: + - id: xxxx + uri: xxxx + predicates: + - Path=xxxx + filters: + - AddRequestParameter=X-Request-Id,1024 #¹ýÂËÆ÷¹¤³§»áÔÚÆ¥ÅäµÄÇëÇóÍ·¼ÓÉÏÒ»¶ÔÇëÇóÍ·£¬Ãû³ÆÎª X-Request-Id ֵΪ 1024 + - PrefixPath=/dept #ÔÚÇëÇó·¾¶Ç°Ãæ¼ÓÉÏ /dept + ¡­¡­ +``` + + + + + +Spring Cloud Gateway ÄÚÖÃÁ˶à´ï 31 ÖÖ GatewayFilter£¬Ï±íÖÐÁоÙÁ˼¸ÖÖ³£ÓõÄÍø¹Ø¹ýÂËÆ÷¼°ÆäʹÓÃʾÀý¡£ + +| ·ÓɹýÂËÆ÷ | ÃèÊö | ²ÎÊý | ʹÓÃʾÀý | +| --- | --- | --- | --- | +| AddRequestHeader | ```À¹½Ø´«ÈëµÄÇëÇ󣬲¢ÔÚÇëÇóÉÏÌí¼ÓÒ»¸öÖ¸¶¨µÄÇëÇóÍ·²ÎÊý¡£ | name£ºÐèÒªÌí¼ÓµÄÇëÇóÍ·²ÎÊýµÄ key£»value£ºÐèÒªÌí¼ÓµÄÇëÇóÍ·²ÎÊýµÄ value¡£ | - AddRequestHeader=my-request-header,1024 | +| AddRequestParameter | À¹½Ø´«ÈëµÄÇëÇ󣬲¢ÔÚÇëÇóÉÏÌí¼ÓÒ»¸öÖ¸¶¨µÄÇëÇó²ÎÊý¡£ | name£ºÐèÒªÌí¼ÓµÄÇëÇó²ÎÊýµÄ key£»value£ºÐèÒªÌí¼ÓµÄÇëÇó²ÎÊýµÄ value¡£ | - AddRequestParameter=my-request-param,c.biancheng.net | +| AddResponseHeader | À¹½ØÏìÓ¦£¬²¢ÔÚÏìÓ¦ÉÏÌí¼ÓÒ»¸öÖ¸¶¨µÄÏìӦͷ²ÎÊý¡£ | name£ºÐèÒªÌí¼ÓµÄÏìӦͷµÄ key£»value£ºÐèÒªÌí¼ÓµÄÏìӦͷµÄ value¡£ | - AddResponseHeader=my-response-header,c.biancheng.net | +| PrefixPath | À¹½Ø´«ÈëµÄÇëÇ󣬲¢ÔÚÇëÇó·¾¶Ôö¼ÓÒ»¸öÖ¸¶¨µÄǰ׺¡£ | ```prefix£ºÐèÒªÔö¼ÓµÄ·¾¶Ç°×º¡£ | - PrefixPath=/consumer | +| PreserveHostHeader | ת·¢ÇëÇóʱ£¬±£³Ö¿Í»§¶ËµÄ Host ÐÅÏ¢²»±ä£¬È»ºó½«Ëü´«µÝµ½Ìṩ¾ßÌå·þÎñµÄ΢·þÎñÖС£ | ÎÞ | - PreserveHostHeader | +| RemoveRequestHeader | ÒÆ³ýÇëÇóÍ·ÖÐÖ¸¶¨µÄ²ÎÊý¡£ | name£ºÐèÒªÒÆ³ýµÄÇëÇóÍ·µÄ key¡£ | - RemoveRequestHeader=my-request-header | +| RemoveResponseHeader | ÒÆ³ýÏìӦͷÖÐÖ¸¶¨µÄ²ÎÊý¡£ | name£ºÐèÒªÒÆ³ýµÄÏìӦͷ¡£ | - RemoveResponseHeader=my-response-header | +| RemoveRequestParameter | ÒÆ³ýÖ¸¶¨µÄÇëÇó²ÎÊý¡£ | name£ºÐèÒªÒÆ³ýµÄÇëÇó²ÎÊý¡£ | - RemoveRequestParameter=my-request-param | +| RequestSize | ÅäÖÃÇëÇóÌåµÄ´óС£¬µ±ÇëÇóÌå¹ý´óʱ£¬½«»á·µ»Ø 413 Payload Too Large¡£ | maxSize£ºÇëÇóÌåµÄ´óС¡£ | - name: RequestSize`````````args:```````````````maxSize: 5000000 | + +#### ʾÀý + +ÏÂÃæÎÒÃÇͨ¹ýÒ»¸öʵÀýÀ´ÑÝʾ GatewayFilter µÄÅäÖ㬲½ÖèÈçÏ¡£ + +1\. ÔÚ```micro-service-cloud-gateway-9527 µÄ application.yml ÖÐÔÚÌí¼ÓÒ»¸ö¶¯Ì¬Â·ÓÉ£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` +spring: + cloud: + gateway: + routes: + - id: xxxx + uri: xxxx + predicates: + - Path=xxxx + filters: + - AddRequestParameter=X-Request-Id,1024 #¹ýÂËÆ÷¹¤³§»áÔÚÆ¥ÅäµÄÇëÇóÍ·¼ÓÉÏÒ»¶ÔÇëÇóÍ·£¬Ãû³ÆÎª X-Request-Id ֵΪ 1024 + - PrefixPath=/dept #ÔÚÇëÇó·¾¶Ç°Ãæ¼ÓÉÏ /dept + ¡­¡­ + +``` + + + + + +2\. ÖØÆô```micro-service-cloud-gateway-9527£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:9527/get/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Gateway ·ÓɹýÂËÆ÷](http://c.biancheng.net/uploads/allimg/211210/101P4J58-5.png) +ͼ6£ºÂ·ÓɹýÂËÆ÷ʾÀý + +#### GlobalFilter È«¾Ö¹ýÂËÆ÷ + +GlobalFilter ÊÇÒ»ÖÖ×÷ÓÃÓÚËùÓеÄ·ÓÉÉϵÄÈ«¾Ö¹ýÂËÆ÷£¬Í¨¹ýËü£¬ÎÒÃÇ¿ÉÒÔʵÏÖһЩͳһ»¯µÄÒµÎñ¹¦ÄÜ£¬ÀýÈçȨÏÞÈÏÖ¤¡¢IP ·ÃÎÊÏÞÖÆµÈ¡£µ±Ä³¸öÇëÇó±»Â·ÓÉÆ¥Åäʱ£¬ÄÇôËùÓÐµÄ GlobalFilter »áºÍ¸Ã·ÓÉ×ÔÉíÅäÖÃµÄ GatewayFilter ×éºÏ³ÉÒ»¸ö¹ýÂËÆ÷Á´¡£ + +Spring Cloud Gateway ΪÎÒÃÇÌṩÁ˶àÖÖĬÈ쵀 GlobalFilter£¬ÀýÈçÓëת·¢¡¢Â·ÓÉ¡¢¸ºÔؾùºâµÈÏà¹ØµÄÈ«¾Ö¹ýÂËÆ÷¡£µ«ÔÚʵ¼ÊµÄÏîÄ¿¿ª·¢ÖУ¬Í¨³£ÎÒÃǶ¼»á×Ô¶¨ÒåһЩ×Ô¼ºµÄ GlobalFilter È«¾Ö¹ýÂËÆ÷ÒÔÂú×ãÎÒÃÇ×ÔÉíµÄÒµÎñÐèÇ󣬶øºÜÉÙÖ±½ÓʹÓà Spring Cloud``` Config ÌṩÕâЩĬÈ쵀 GlobalFilter¡£ + +> ¹ØÓÚĬÈϵÄÈ«¾Ö¹ýÂËÆ÷µÄÏêϸÄÚÈÝ£¬Çë²Î¿¼```[Spring Cloud ¹ÙÍø](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters)¡£ + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀýÀ´ÑÝʾÏ£¬ÈçºÎ×Ô¶¨Òå GlobalFilter È«¾Ö¹ýÂËÆ÷¡£ + +1\. ÔÚ```net.biancheng.c.filter °üÏ£¬Ð½¨Ò»¸öÃûΪ```MyGlobalFilter È«¾Ö¹ýÂËÆ÷ÅäÖÃÀ࣬´úÂëÈçÏ¡£ + + + + + +``` +server: + port: 9527 #¶Ë¿ÚºÅ + +spring: + application: + name: microServiceCloudGateway #·þÎñ×¢²áÖÐÐÄ×¢²áµÄ·þÎñÃû + + cloud: + gateway: #Íø¹ØÂ·ÓÉÅäÖà + discovery: + locator: + enabled: true #ĬÈÏֵΪ true£¬¼´Ä¬ÈÏ¿ªÆô´Ó×¢²áÖÐÐĶ¯Ì¬´´½¨Â·ÓɵŦÄÜ£¬ÀûÓÃ΢·þÎñÃû½øÐзÓÉ + + routes: + #½« micro-service-cloud-provider-dept-8001 ÌṩµÄ·þÎñÒþ²ØÆðÀ´£¬²»±©Â¶¸ø¿Í»§¶Ë£¬Ö»¸ø¿Í»§¶Ë±©Â¶ API Íø¹ØµÄµØÖ· 9527 + - id: provider_dept_list_routh #·ÓÉ id,ûÓй̶¨¹æÔò£¬µ«Î¨Ò»£¬½¨ÒéÓë·þÎñÃû¶ÔÓ¦ + uri: lb://MICROSERVICECLOUDPROVIDERDEPT #¶¯Ì¬Â·ÓÉ£¬Ê¹Ó÷þÎñÃû´úÌæÉÏÃæµÄ¾ßÌå´ø¶Ë¿Ú http://eureka7001.com:9527/dept/list + + predicates: + #ÒÔÏÂÊǶÏÑÔÌõ¼þ£¬±ØÑ¡È«²¿·ûºÏÌõ¼þ + - Path=/dept/list/** #¶ÏÑÔ£¬Â·¾¶Æ¥Åä ×¢Ò⣺Path ÖÐ P Ϊ´óд + - Method=GET #Ö»ÄÜʱ GET ÇëÇóʱ£¬²ÅÄÜ·ÃÎÊ + +eureka: + instance: + instance-id: micro-service-cloud-gateway-9527 + hostname: micro-service-cloud-gateway + client: + fetch-registry: true + register-with-eureka: true + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ + +``` + + + + + +2\. ÖØÆô```micro-service-cloud-gateway-9527£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:9527/dept/list¡±£¬ÎÒÃǻᷢÏÖ·ÃÎʱ¨ 406 ´íÎ󣬿ØÖÆÌ¨Êä³öÈçÏ¡£ + +```2021-10-21 16:25:39.450 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : Thu Oct 21 16:25:39 CST 2021½øÈë×Ô¶¨ÒåµÄÈ«¾Ö¹ýÂËÆ÷ MyGlobalFilter +2021-10-21 16:25:39.451 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : ²ÎÊý uname ²»ÄÜΪ null£¡ +``` + +3\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:9527/dept/list?uname=123¡±,½á¹ûÈçÏÂͼ¡£ + +![×Ô¶¨ÒåÈ«¾ÖÍø¹Ø¹ýÂËÆ÷](http://c.biancheng.net/uploads/allimg/211210/101P43096-6.png) +ͼ7£º×Ô¶¨ÒåÈ«¾ÖÍø¹Ø¹ýÂËÆ÷ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" new file mode 100644 index 0000000..1a26d68 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudHystrix.md" @@ -0,0 +1,1120 @@ +ÔÚ΢·þÎñ¼Ü¹¹ÖУ¬Ò»¸öÓ¦ÓÃÍùÍùÓɶà¸ö·þÎñ×é³É£¬ÕâЩ·þÎñÖ®¼äÏ໥ÒÀÀµ£¬ÒÀÀµ¹ØÏµ´í×Û¸´ÔÓ¡£ + +ÀýÈçÒ»¸ö΢·þÎñϵͳÖдæÔÚ A¡¢B¡¢C¡¢D¡¢E¡¢F µÈ¶à¸ö·þÎñ£¬ËüÃǵÄÒÀÀµ¹ØÏµÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101623H11-0.png) + +ͼ1£º·þÎñÒÀÀµ¹ØÏµ + +ͨ³£Çé¿öÏ£¬Ò»¸öÓû§ÇëÇóÍùÍùÐèÒª¶à¸ö·þÎñÅäºÏ²ÅÄÜÍê³É¡£Èçͼ 1 Ëùʾ£¬ÔÚËùÓзþÎñ¶¼´¦ÓÚ¿ÉÓÃ״̬ʱ£¬ÇëÇó 1 ÐèÒªµ÷Óà A¡¢D¡¢E¡¢F Ëĸö·þÎñ²ÅÄÜÍê³É£¬ÇëÇó 2 ÐèÒªµ÷Óà B¡¢E¡¢D Èý¸ö·þÎñ²ÅÄÜÍê³É£¬ÇëÇó 3 ÐèÒªµ÷Ó÷þÎñ C¡¢F¡¢E¡¢D Ëĸö·þÎñ²ÅÄÜÍê³É¡£ + +µ±·þÎñ E ·¢Éú¹ÊÕÏ»òÍøÂçÑÓ³Ùʱ£¬»á³öÏÖÒÔÏÂÇé¿ö£º + +1. ¼´Ê¹ÆäËûËùÓзþÎñ¶¼¿ÉÓã¬ÓÉÓÚ·þÎñ E µÄ²»¿ÉÓã¬ÄÇôÓû§ÇëÇó 1¡¢2¡¢3 ¶¼»á´¦ÓÚ×èÈû״̬£¬µÈ´ý·þÎñ E µÄÏìÓ¦¡£Ôڸ߲¢·¢µÄ³¡¾°Ï£¬»áµ¼ÖÂÕû¸ö·þÎñÆ÷µÄÏß³Ì×ÊÔ´ÔÚ¶Ìʱ¼äÄÚѸËÙÏûºÄ´ù¾¡¡£ +2. ËùÓÐÒÀÀµÓÚ·þÎñ E µÄÆäËû·þÎñ£¬ÀýÈç·þÎñ B¡¢D ÒÔ¼° F Ò²¶¼»á´¦ÓÚÏß³Ì×èÈû״̬£¬µÈ´ý·þÎñ E µÄÏìÓ¦£¬µ¼ÖÂÕâЩ·þÎñµÄ²»¿ÉÓᣠ+3. ËùÓÐÒÀÀµ·þÎñB¡¢D ºÍ F µÄ·þÎñ£¬ÀýÈç·þÎñ A ºÍ·þÎñ C Ò²»á´¦ÓÚÏß³Ì×èÈû״̬£¬ÒԵȴý·þÎñ D ºÍ·þÎñ F µÄÏìÓ¦£¬µ¼Ö·þÎñ A ºÍ·þÎñ C Ò²²»¿ÉÓᣠ+ +´ÓÒÔÉϹý³Ì¿ÉÒÔ¿´³ö£¬µ±Î¢·þÎñϵͳµÄÒ»¸ö·þÎñ³öÏÖ¹ÊÕÏʱ£¬¹ÊÕÏ»áÑØ×Å·þÎñµÄµ÷ÓÃÁ´Â·ÔÚϵͳÖзè¿ñÂûÑÓ£¬×îÖÕµ¼ÖÂÕû¸ö΢·þÎñϵͳµÄ̱»¾£¬Õâ¾ÍÊÇ¡°Ñ©±ÀЧӦ¡±¡£ÎªÁË·ÀÖ¹´ËÀàʼþµÄ·¢Éú£¬Î¢·þÎñ¼Ü¹¹ÒýÈëÁË¡°ÈÛ¶ÏÆ÷¡±µÄһϵÁзþÎñÈÝ´íºÍ±£»¤»úÖÆ¡£ + +## ÈÛ¶ÏÆ÷ + +ÈÛ¶ÏÆ÷£¨Circuit Breaker£©Ò»´ÊÀ´Ô´ÎïÀíѧÖеĵç·֪ʶ£¬ËüµÄ×÷ÓÃÊǵ±Ïß·³öÏÖ¹ÊÕÏʱ£¬Ñ¸ËÙÇжϵçÔ´ÒÔ±£»¤µç·µÄ°²È«¡£ + +ÔÚ΢·þÎñÁìÓò£¬ÈÛ¶ÏÆ÷×îÔçÊÇÓÉ Martin Fowler ÔÚËû·¢±íµÄ ¡¶[Circuit Breake](https://martinfowler.com/bliki/CircuitBreaker.html)r¡·Ò»ÎÄÖÐÌá³ö¡£ÓëÎïÀíѧÖеÄÈÛ¶ÏÆ÷×÷ÓÃÏàËÆ£¬Î¢·þÎñ¼Ü¹¹ÖеÄÈÛ¶ÏÆ÷Äܹ»ÔÚij¸ö·þÎñ·¢Éú¹ÊÕϺó£¬Ïò·þÎñµ÷Ó÷½·µ»ØÒ»¸ö·ûºÏÔ¤ÆÚµÄ¡¢¿É´¦ÀíµÄ½µ¼¶ÏìÓ¦£¨FallBack£©£¬¶ø²»Êdz¤Ê±¼äµÄµÈ´ý»òÕßÅ׳öµ÷Ó÷½ÎÞ·¨´¦ÀíµÄÒì³£¡£ÕâÑù¾Í±£Ö¤ÁË·þÎñµ÷Ó÷½µÄÏ̲߳»»á±»³¤Ê±¼ä¡¢²»±ØÒªµØÕ¼Ó㬱ÜÃâ¹ÊÕÏÔÚ΢·þÎñϵͳÖеÄÂûÑÓ£¬·Àֹϵͳѩ±ÀЧӦµÄ·¢Éú¡£ + +## Spring Cloud Hystrix + +Spring Cloud Hystrix ÊÇÒ»¿îÓÅÐãµÄ·þÎñÈÝ´íÓë±£»¤×é¼þ£¬Ò²ÊÇ Spring Cloud ÖÐ×îÖØÒªµÄ×é¼þÖ®Ò»¡£ + +Spring Cloud Hystrix ÊÇ»ùÓÚ Netflix ¹«Ë¾µÄ¿ªÔ´×é¼þ Hystrix ʵÏֵģ¬ËüÌṩÁËÈÛ¶ÏÆ÷¹¦ÄÜ£¬Äܹ»ÓÐЧµØ×èÖ¹·Ö²¼Ê½Î¢·þÎñϵͳÖгöÏÖÁª¶¯¹ÊÕÏ£¬ÒÔÌá¸ß΢·þÎñϵͳµÄµ¯ÐÔ¡£Spring Cloud Hystrix ¾ßÓзþÎñ½µ¼¶¡¢·þÎñÈÛ¶Ï¡¢Ï̸߳ôÀë¡¢ÇëÇ󻺴桢ÇëÇóºÏ²¢ÒÔ¼°ÊµÊ±¹ÊÕÏ¼à¿ØµÈÇ¿´ó¹¦ÄÜ¡£ + +> Hystrix [h?st'r?ks]£¬ÖÐÎĺ¬ÒåÊǺÀÖí£¬ºÀÖíµÄ±³Éϳ¤ÂúÁ˼¬´Ì£¬Ê¹ËüÓµÓÐÁËÇ¿´óµÄ×ÔÎÒ±£»¤ÄÜÁ¦¡£¶ø Spring Cloud Hystrix ×÷Ϊһ¸ö·þÎñÈÝ´íÓë±£»¤×é¼þ£¬Ò²¿ÉÒÔÈ÷þÎñÓµÓÐ×ÔÎÒ±£»¤µÄÄÜÁ¦£¬Òò´ËÒ²ÓÐÈ˽«ÆäÏ·³ÆÎª¡°ºÀÖí¸ç¡±¡£ + +ÔÚ΢·þÎñϵͳÖУ¬Hystrix Äܹ»°ïÖúÎÒÃÇʵÏÖÒÔÏÂÄ¿±ê£º + +* **±£»¤Ïß³Ì×ÊÔ´**£º·ÀÖ¹µ¥¸ö·þÎñµÄ¹ÊÕϺľ¡ÏµÍ³ÖеÄËùÓÐÏß³Ì×ÊÔ´¡£ +* **¿ìËÙʧ°Ü»úÖÆ**£ºµ±Ä³¸ö·þÎñ·¢ÉúÁ˹ÊÕÏ£¬²»È÷þÎñµ÷Ó÷½Ò»Ö±µÈ´ý£¬¶øÊÇÖ±½Ó·µ»ØÇëÇóʧ°Ü¡£ +* **Ìṩ½µ¼¶£¨FallBack£©·½°¸**£ºÔÚÇëÇóʧ°Üºó£¬Ìṩһ¸öÉè¼ÆºÃµÄ½µ¼¶·½°¸£¬Í¨³£ÊÇÒ»¸ö¶µµ×·½·¨£¬µ±ÇëÇóʧ°Üºó¼´µ÷Óø÷½·¨¡£ +* **·ÀÖ¹¹ÊÕÏÀ©É¢**£ºÊ¹ÓÃÈÛ¶Ï»úÖÆ£¬·ÀÖ¹¹ÊÕÏÀ©É¢µ½ÆäËû·þÎñ¡£ +* **¼à¿Ø¹¦ÄÜ**£ºÌṩÈÛ¶ÏÆ÷¹ÊÕÏ¼à¿Ø×é¼þ Hystrix Dashboard£¬ËæÊ±¼à¿ØÈÛ¶ÏÆ÷µÄ״̬¡£ + +## Hystrix ·þÎñ½µ¼¶ + +Hystrix ÌṩÁË·þÎñ½µ¼¶¹¦ÄÜ£¬Äܹ»±£Ö¤µ±Ç°·þÎñ²»ÊÜÆäËû·þÎñ¹ÊÕϵÄÓ°Ï죬Ìá¸ß·þÎñµÄ½¡×³ÐÔ¡£ + +·þÎñ½µ¼¶µÄʹÓó¡¾°ÓÐÒÔÏ 2 ÖÖ£º + +* ÔÚ·þÎñÆ÷ѹÁ¦¾çÔöʱ£¬¸ù¾Ýʵ¼ÊÒµÎñÇé¿ö¼°Á÷Á¿£¬¶ÔһЩ²»ÖØÒª¡¢²»½ô¼±µÄ·þÎñ½øÐÐÓвßÂԵز»´¦Àí»ò¼òµ¥´¦Àí£¬´Ó¶øÊÍ·Å·þÎñÆ÷×ÊÔ´ÒÔ±£Ö¤ºËÐÄ·þÎñÕý³£ÔË×÷¡£ +* µ±Ä³Ð©·þÎñ²»¿ÉÓÃʱ£¬ÎªÁ˱ÜÃⳤʱ¼äµÈ´ýÔì³É·þÎñ¿¨¶Ù»òÑ©±ÀЧӦ£¬¶øÖ÷¶¯Ö´Ðб¸ÓõĽµ¼¶Âß¼­Á¢¿Ì·µ»ØÒ»¸öÓѺõÄÌáʾ£¬ÒÔ±£ÕÏÖ÷ÌåÒµÎñ²»ÊÜÓ°Ïì¡£ + +ÎÒÃÇ¿ÉÒÔͨ¹ýÖØÐ´ HystrixCommand µÄ getFallBack() ·½·¨»ò HystrixObservableCommand µÄ resumeWithFallback() ·½·¨£¬Ê¹·þÎñÖ§³Ö·þÎñ½µ¼¶¡£ + +Hystrix ·þÎñ½µ¼¶ FallBack ¼È¿ÉÒÔ·ÅÔÚ·þÎñ¶Ë½øÐУ¬Ò²¿ÉÒÔ·ÅÔÚ¿Í»§¶Ë½øÐС£ + +Hystrix »áÔÚÒÔϳ¡¾°Ï½øÐзþÎñ½µ¼¶´¦Àí£º + +* ³ÌÐòÔËÐÐÒì³£ +* ·þÎñ³¬Ê± +* ÈÛ¶ÏÆ÷´¦ÓÚ´ò¿ª×´Ì¬ +* Ï̳߳Ø×ÊÔ´ºÄ¾¡ + +## ʾÀý1 + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸ö°¸Àý£¬·Ö±ðÑÝʾÏ Hystrix ·þÎñ¶Ë·þÎñ½µ¼¶ºÍ¿Í»§¶Ë·þÎñ½µ¼¶¡£ + +#### ·þÎñ¶Ë·þÎñ½µ¼¶ + +1\. ÔÚÖ÷¹¤³Ì spring-cloud-demo2 Ï´´½¨Ò»¸öÃûΪ micro-service-cloud-provider-dept-hystrix-8004 µÄ·þÎñÌṩÕߣ¬²¢ÔÚÆä pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + + + +``` + + + + 4.0.0 + + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + + net.biancheng.c + micro-service-cloud-provider-dept-hystrix-8004 + 0.0.1-SNAPSHOT + micro-service-cloud-provider-dept-hystrix-8004 + Demo project for Spring Boot + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + +``` + + + + + +2\. ÔÚÀà·¾¶£¨¼´ /resources Ŀ¼£©ÏÂÌí¼ÓÒ»¸öÅäÖÃÎļþ application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` + +spring: + application: + name: microServiceCloudProviderDeptHystrix #΢·þÎñÃû³Æ£¬¶ÔÍⱩ©µÄ΢·þÎñÃû³Æ£¬Ê®·ÖÖØÒª + +server: + port: 8004 +########################################### Spring cloud ×Ô¶¨Òå·þÎñÃû³ÆºÍ ip µØÖ·############################################### +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + #defaultZone: http://eureka7001:7001/eureka #Õâ¸öµØÖ·ÊÇ 7001×¢²áÖÐÐÄÔÚ application.yml Öб©Â¶³öÀ´¶î×¢²áµØÖ· £¨µ¥»ú°æ£© + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº + instance: + instance-id: spring-cloud-provider-8004 #×Ô¶¨Òå·þÎñÃû³ÆÐÅÏ¢ + prefer-ip-address: true #ÏÔʾ·ÃÎÊ·¾¶µÄ ip µØÖ· +#####################spring cloud ʹÓà Spring Boot actuator ¼à¿ØÍêÉÆÐÅÏ¢########################################### +# Spring Boot 2.50¶Ô actuator ¼à¿ØÆÁ±ÎÁË´ó¶àÊýµÄ½Úµã£¬Ö»±©Â¶ÁË heath ½Úµã£¬±¾¶ÎÅäÖã¨*£©¾ÍÊÇΪÁË¿ªÆôËùÓеĽڵã +management: + endpoints: + web: + exposure: + include: "*" # * ÔÚyaml ÎļþÊôÓڹؼü×Ö£¬ËùÒÔÐèÒª¼ÓÒýºÅ +info: + app.name: micro-service-cloud-provider-dept-hystrix + company.name: c.biancheng.net + build.aetifactId: @project.artifactId@ + build.version: @project.version@ + +``` + + + + + +3\. ÔÚ net.biancheng.c.service °üÏ´´½¨Ò»¸öÃûΪ DeptService µÄ½Ó¿Ú£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.service; + +public interface DeptService { + + // hystrix ÈÛ¶ÏÆ÷ʾÀý ok + public String deptInfo_Ok(Integer id); + + //hystrix ÈÛ¶ÏÆ÷³¬Ê±°¸Àý + public String deptInfo_Timeout(Integer id); +} + +``` + + + + + +4\. ÔÚ net.biancheng.c.service.impl °üÏ£¬´´½¨ DeptService ½Ó¿ÚµÄʵÏÖÀà DeptServiceImpl£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.service.impl; + +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; +import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; +import net.biancheng.c.service.DeptService; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service("deptService") +public class DeptServiceImpl implements DeptService { + + @Override + public String deptInfo_Ok(Integer id) { + return "Ï̳߳أº" + Thread.currentThread().getName() + " deptInfo_Ok,id: " + id; + } + + //Ò»µ©¸Ã·½·¨Ê§°Ü²¢Å׳öÁËÒì³£ÐÅÏ¢ºó£¬»á×Ô¶¯µ÷Óà @HystrixCommand ×¢½â±ê×¢µÄ fallbackMethod Ö¸¶¨µÄ·½·¨ + @HystrixCommand(fallbackMethod = "dept_TimeoutHandler", + commandProperties = + //¹æ¶¨ 5 ÃëÖÓÒÔÄھͲ»±¨´í£¬Õý³£ÔËÐУ¬³¬¹ý 5 Ãë¾Í±¨´í£¬µ÷ÓÃÖ¸¶¨µÄ·½·¨ + {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")}) + @Override + public String deptInfo_Timeout(Integer id) { + int outTime = 6; + try { + TimeUnit.SECONDS.sleep(outTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "Ï̳߳أº" + Thread.currentThread().getName() + " deptInfo_Timeout,id: " + id + " ºÄʱ: " + outTime; + } + + // µ±·þÎñ³öÏÖ¹ÊÕϺ󣬵÷Óø÷½·¨¸ø³öÓѺÃÌáʾ + public String dept_TimeoutHandler(Integer id) { + return "CÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬ÏµÍ³·±Ã¦ÇëÉÔºóÔÙÊÔ£¡"+"Ï̳߳أº" + Thread.currentThread().getName() + " deptInfo_Timeout,id: " + id; + } +} + +``` + + + + + +ÎÒÃÇ¿ÉÒÔ¿´µ½ deptInfo_Timeout() ·½·¨ÉÏʹÓà @HystrixCommand ×¢½â£¬¸Ã×¢½â˵Ã÷ÈçÏ£º + +* ²ÎÊý fallbackMethod ÊôÐÔÓÃÓÚÖ¸¶¨½µ¼¶·½·¨¡£ +* ²ÎÊý execution.isolation.thread.timeoutInMilliseconds ÓÃÓÚÉèÖÃ×ÔÉíµ÷Óó¬Ê±Ê±¼äµÄ·åÖµ£¬·åÖµÄÚ¿ÉÒÔÕý³£ÔËÐУ¬·ñÔòÖ´Ðнµ¼¶·½·¨ + +5. ÔÚ net.biancheng.c.controller °üÏ´´½¨Ò»¸öÃûΪ DeptController µÄ Controller À࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.controller; + +import lombok.extern.slf4j.Slf4j; +import net.biancheng.c.service.DeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +@RestController +@Slf4j +public class DeptController { + @Autowired + private DeptService deptService; + @Value("${server.port}") + private String serverPort; + + @RequestMapping(value = "/dept/hystrix/ok/{id}") + public String deptInfo_Ok(@PathVariable("id") Integer id) { + String result = deptService.deptInfo_Ok(id); + log.info("¶Ë¿ÚºÅ£º" + serverPort + " result:" + result); + return result + "£¬ ¶Ë¿ÚºÅ£º" + serverPort; + } + + // Hystrix ·þÎñ³¬Ê±½µ¼¶ + @RequestMapping(value = "/dept/hystrix/timeout/{id}") + public String deptInfo_Timeout(@PathVariable("id") Integer id) { + String result = deptService.deptInfo_Timeout(id); + log.info("¶Ë¿ÚºÅ£º" + serverPort + " result:" + result); + return result + "£¬ ¶Ë¿ÚºÅ£º" + serverPort; + } + +} + +``` + + + + + +6\. ÔÚ micro-service-cloud-provider-dept-hystrix-8004 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableCircuitBreaker ×¢½â¿ªÆôÈÛ¶ÏÆ÷¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + + +package net.biancheng.c; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient //¿ªÆô Eureka ¿Í»§¶Ë¹¦ÄÜ +@EnableCircuitBreaker //¼¤»îÈÛ¶ÏÆ÷¹¦ÄÜ +public class MicroServiceCloudProviderDeptHystrix8004Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudProviderDeptHystrix8004Application.class, args); + } + +} +``` + + + + + +7\. ÒÀ´ÎÆô¶¯·þÎñ×¢²áÖÐÐÄ£¨Eureka Server£©¼¯ÈººÍ micro-service-cloud-provider-dept-hystrix-8004£¬²¢Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/ok/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ok](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016234615-1.png) +ͼ2£ºHystrix Õý³£·þÎñ°¸Àý + +8\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/timeout/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ·þÎñ½µ¼¶](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101623FR-2.png) +ͼ3£ºHystrix ·þÎñ¶Ë·þÎñ½µ¼¶ + +#### ¿Í»§¶Ë·þÎñ½µ¼¶ + +ͨ³£Çé¿öÏ£¬ÎÒÃǶ¼»áÔÚ¿Í»§¶Ë½øÐзþÎñ½µ¼¶£¬µ±¿Í»§¶Ëµ÷ÓõķþÎñ¶ËµÄ·þÎñ²»¿ÉÓÃʱ£¬¿Í»§¶ËÖ±½Ó½øÐзþÎñ½µ¼¶´¦Àí£¬±ÜÃâÆäÏ̱߳»³¤Ê±¼ä¡¢²»±ØÒªµØÕ¼Óᣠ+ +¿Í»§¶Ë·þÎñ½µ¼¶²½ÖèÈçÏ¡£ + +1\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ pom.xml ÖÐÌí¼Ó Hystrix µÄÒÀÀµ£¬´úÂëÈçÏ¡£ + + + + + +``` + + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + +``` + + + + + +2. ÔÚ micro-service-cloud-consumer-dept-feign µÄ application.yml ÖÐÌí¼ÓÒÔÏÂÅäÖ㬿ªÆô¿Í»§¶ËµÄ Hystrix ¹¦ÄÜ¡£ + + + + + +``` + +feign: + hystrix: + enabled: true #¿ªÆô¿Í»§¶Ë hystrix + +``` + + + + + +3\. ÔÚ net.biancheng.c.service °üÏ£¬´´½¨Ò»¸öÃûΪ DeptHystrixService µÄ·þÎñ°ó¶¨½Ó¿Ú£¬Óë micro-service-cloud-provider-dept-hystrix-8004 ÖÐÌṩµÄ·þÎñ½Ó¿Ú½øÐа󶨣¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.service; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@Component +@FeignClient(value = "MICROSERVICECLOUDPROVIDERDEPTHYSTRIX") +public interface DeptHystrixService { + @RequestMapping(value = "/dept/hystrix/ok/{id}") + public String deptInfo_Ok(@PathVariable("id") Integer id); + + @RequestMapping(value = "/dept/hystrix/timeout/{id}") + public String deptInfo_Timeout(@PathVariable("id") Integer id); +} +``` + + + + + +4\. ÔÚ net.biancheng.c.controller °üÏ´´½¨Ò»¸öÃûΪ HystrixController_Consumer µÄ Controller £¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.controller; + +import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;; +import lombok.extern.slf4j.Slf4j; +import net.biancheng.c.service.DeptHystrixService; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Slf4j +@RestController +public class HystrixController_Consumer { + + @Resource + private DeptHystrixService deptHystrixService; + + @RequestMapping(value = "/consumer/dept/hystrix/ok/{id}") + public String deptInfo_Ok(@PathVariable("id") Integer id) { + return deptHystrixService.deptInfo_Ok(id); + } + + //ÔÚ¿Í»§¶Ë½øÐнµ¼¶ + @RequestMapping(value = "/consumer/dept/hystrix/timeout/{id}") + @HystrixCommand(fallbackMethod = "dept_TimeoutHandler") //Ϊ¸ÃÇëÇóÖ¸¶¨×¨ÊôµÄ»ØÍË·½·¨ + public String deptInfo_Timeout(@PathVariable("id") Integer id) { + String s = deptHystrixService.deptInfo_Timeout(id); + log.info(s); + return s; + } + + // deptInfo_Timeout·½·¨µÄ רÓà fallback ·½·¨ + public String dept_TimeoutHandler(@PathVariable("id") Integer id) { + log.info("deptInfo_Timeout ³ö´í£¬·þÎñÒѱ»½µ¼¶£¡"); + return "CÓïÑÔÖÐÎÄÍøÌáÐÑÄú£º·þÎñ¶Ëϵͳ·±Ã¦£¬ÇëÉÔºóÔÙÊÔ£¡£¨¿Í»§¶Ë deptInfo_Timeout רÊôµÄ»ØÍË·½·¨´¥·¢£©"; + } +} + +``` + + + + + +5\. ÔÚÅäÖÃÎļþ appliction.yml ÖÐÌí¼ÓÒÔÏÂÅäÖã¬ÔÚ¿Í»§¶ËÅäÖÃÇëÇó³¬Ê±µÄʱ¼ä¡£ + + + + + +``` +######################### Ribbon ¿Í»§¶Ë³¬Ê±¿ØÖÆ ################################### +ribbon: + ReadTimeout: 6000 #½¨Á¢Á¬½ÓËùÓõÄʱ¼ä£¬ÊÊÓÃÓÚÍøÂç×´¿öÕý³£µÄÇé¿öÏ£¬Á½¶ËÁ½¶ËÁ¬½ÓËùÓõÄʱ¼ä + ConnectionTimeout: 6000 #½¨Á¢Á¬½Óºó£¬·þÎñÆ÷¶ÁÈ¡µ½¿ÉÓÃ×ÊÔ´µÄʱ¼ä +######################ÅäÖÃÇëÇó³¬Ê±Ê±¼ä########################## +hystrix: + command: + default: + execution: + isolation: + thread: + timeoutInMilliseconds: 7000 +####################ÅäÖþßÌå·½·¨³¬Ê±Ê±¼ä Ϊ 3 Ãë######################## + DeptHystrixService#deptInfo_Timeout(Integer): + execution: + isolation: + thread: + timeoutInMilliseconds: 3000 + +``` + + + + + +ÔÚÅäÖÃÎļþÖÐÉè¼ÆÇëÇóµÄ³¬Ê±Ê±¼äʱ£¬ÐèҪעÒâÒÔÏ 2 µã£º + +1£©Hystrix ¿ÉÒÔÀ´ÎªËùÓÐÇëÇ󣨷½·¨£©ÉèÖó¬Ê±Ê±¼ä£¨µ¥Î»ÎªºÁÃ룩£¬ÈôÇëÇó³¬Ê±Ôò´¥·¢È«¾ÖµÄ»ØÍË·½·¨½øÐд¦Àí¡£ + +```hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=mmm``` + +2£©Hystrix »¹¿ÉÒÔΪij¸öÌØ¶¨µÄ·þÎñÇëÇ󣨷½·¨£©ÉèÖó¬Ê±Ê±¼ä£¬¸ñʽÈçÏ£º + +```hystrix.command.xxx#yyy(zzz).execution.isolation.thread.timeoutInMilliseconds=mmm``` + +¸ñʽ˵Ã÷ÈçÏ£º + +* xxx£ºÎª°üº¬¸Ã·þÎñ·½·¨µÄÀàµÄÃû³Æ£¨Í¨³£Îª·þÎñ°ó¶¨½Ó¿ÚµÄÃû³Æ£©£¬ÀýÈç DeptHystrixService ½Ó¿Ú¡£ +* yyy£º·þÎñ·½·¨Ãû£¬ÀýÈç deptInfo_Timeout() ·½·¨¡£ +* zzz£º·½·¨ÄڵIJÎÊýÀàÐÍ£¬ÀýÈç Integer¡¢String µÈµÈ +* mmm£ºÒªÉèÖõij¬Ê±Ê±¼ä£¬µ¥Î»ÎªºÁÃ루1 Ãë =1000 ºÁÃ룩 + +6\. ÔÚ micro-service-cloud-consumer-dept-feign µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableHystrix ×¢½â¿ªÆô¿Í»§¶Ë Hystrix ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.hystrix.EnableHystrix; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients //¿ªÆô OpenFeign ¹¦ÄÜ +@EnableHystrix //ÆôÓà Hystrix +public class MicroServiceCloudConsumerDeptFeignApplication { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConsumerDeptFeignApplication.class, args); + } +} + +``` + + + + + +7\. ÐÞ¸Ä micro-service-cloud-provider-dept-hystrix-8004 ÖÐ DeptServiceImpl µÄ´úÂ룬½« deptInfo_Timeout() ·½·¨µÄÔËÐÐʱ¼äÐÞ¸ÄΪ 4 Ã루СÓÚ³¬Ê±Ê±¼ä 5 Ã룩£¬ÒÔ±£Ö¤·þÎñ¶ËÇëÇóÕý³£²»±»½µ¼¶£¬´úÂëÈçÏ¡£ + + + + + +``` +//Ò»µ©¸Ã·½·¨Ê§°Ü²¢Å׳öÁËÒì³£ÐÅÏ¢ºó£¬»á×Ô¶¯µ÷Óà @HystrixCommand ×¢½â±ê×¢µÄ fallbackMethod Ö¸¶¨µÄ·½·¨ +@HystrixCommand(fallbackMethod = "dept_TimeoutHandler", + commandProperties = + //¹æ¶¨ 5 ÃëÖÓÒÔÄھͲ»±¨´í£¬Õý³£ÔËÐУ¬³¬¹ý 5 Ãë¾Í±¨´í£¬µ÷ÓÃÖ¸¶¨µÄ·½·¨ + {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")}) +@Override +public String deptInfo_Timeout(Integer id) { + int outTime = 4; + try { + TimeUnit.SECONDS.sleep(outTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "Ï̳߳أº" + Thread.currentThread().getName() + " deptInfo_Timeout,id: " + id + " ºÄʱ: " + outTime; +} + +``` + + + + + +8\. ÖØÆô micro-service-cloud-provider-dept-hystrix-8004 ºÍ micro-service-cloud-consumer-dept-feign£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/timeout/1¡±£¬Ö±½Óµ÷Ó÷þÎñ¶ËµÄ deptInfo_Timeout() ·½·¨£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ·þÎñ¶ËÇëÇóÕý³£](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016231059-3.png) +ͼ4£ºHystrix ·þÎñ¶ËÇëÇóÕý³£ + +9\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/hystrix/timeout/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ¿Í»§¶Ë·þÎñ½µ¼¶](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101623C94-4.png) +ͼ5£ºHystrix ¿Í»§¶Ë·þÎñ½µ¼¶ + +ÓÉͼ 5 ¿ÉÒÔ¿´³ö£¬ÓÉÓÚ·þÎñÇëÇóµÄºÄʱΪ 4 Ã룬³¬¹ýÁ˿ͻ§¶ËΪ¸ÃÇëÇóÖ¸¶¨µÄ³¬Ê±Ê±¼ä £¨3 Ãë £©£¬Òò´Ë¸Ã·þÎñ±»½µ¼¶´¦Àí£¬´¥·¢ÁËÆäÖ¸¶¨µÄ»ØÍË·½·¨¡£ + +## È«¾Ö½µ¼¶·½·¨ + +ͨ¹ýÉÏÃæµÄ·½Ê½ÊµÏÖ·þÎñ½µ¼¶Ê±£¬ÐèÒªÕë¶ÔËùÓÐÒµÎñ·½·¨¶¼ÅäÖýµ¼¶·½·¨£¬Õ⼫ÓпÉÄÜ»áÔì³É´úÂëµÄ¼±¾çÅòÕÍ¡£ÎªÁ˽â¾ö¸ÃÎÊÌ⣬ÎÒÃÇ»¹¿ÉÒÔΪËùÓÐÒµÎñ·½·¨Ö¸¶¨Ò»¸öÈ«¾ÖµÄ»ØÍË·½·¨£¬¾ßÌå²½ÖèÈçÏ¡£ + +1\. ÔÚ HystrixController_Consumer µÄÀàÃûÉϱê×¢ @DefaultProperties ×¢½â£¬²¢Í¨¹ýÆä defaultFallback ÊôÐÔÖ¸¶¨Ò»¸öÈ«¾ÖµÄ½µ¼¶·½·¨£¬´úÂëÈçÏ¡£ + + + + + +``` + + +@Slf4j +@RestController +@DefaultProperties(defaultFallback = "dept_Global_FallbackMethod") //È«¾ÖµÄ·þÎñ½µ¼¶·½·¨ +public class HystrixController_Consumer { +¡­¡­ +} + +``` + + + + + +2\. ÔÚ HystrixController_Consumer ÖУ¬´´½¨Ò»¸öÃûΪ dept_Global_FallbackMethod µÄÈ«¾Ö»Ø·½·¨£¬´úÂëÈçÏ¡£ + + + + + +``` + + +/** + * È«¾ÖµÄ fallback ·½·¨£¬ + * »ØÍË·½·¨±ØÐëºÍ hystrix µÄÖ´Ðз½·¨ÔÚÏàͬÀàÖÐ + * @DefaultProperties(defaultFallback = "dept_Global_FallbackMethod") ÀàÉÏ×¢½â£¬ÇëÇó·½·¨ÉÏʹÓà @HystrixCommand ×¢½â + */ +public String dept_Global_FallbackMethod() { + return "CÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬ÔËÐгö´í»ò·þÎñ¶Ëϵͳ·±Ã¦£¬ÇëÉÔºóÔÙÊÔ£¡£¨¿Í»§¶ËÈ«¾Ö»ØÍË·½·¨´¥·¢,£©"; +} + +``` + + + + + +> **×¢Òâ**£º½µ¼¶£¨FallBack£©·½·¨±ØÐëÓëÆä¶ÔÓ¦µÄÒµÎñ·½·¨ÔÚͬһ¸öÀàÖУ¬·ñÔòÎÞ·¨ÉúЧ¡£ + +3\. ÔÚËùÓеÄÒµÎñ·½·¨É϶¼±ê×¢ @HystrixCommand ×¢½â£¬ÕâÀïÎÒÃǽ« deptInfo_Timeout() ·½·¨É쵀 @HystrixCommand(fallbackMethod = "dept_TimeoutHandler") ÐÞ¸ÄΪ @HystrixCommand ¼´¿É£¬´úÂëÈçÏ¡£ + + + + + +``` + + +//ÔÚ¿Í»§¶Ë½øÐнµ¼¶ +@RequestMapping(value = "/consumer/dept/hystrix/timeout/{id}") +@HystrixCommand +public String deptInfo_Timeout(@PathVariable("id") Integer id) { + String s = deptHystrixService.deptInfo_Timeout(id); + log.info(s); + return s; +} +``` + + + + + +> **×¢Òâ**£ºÈ«¾Ö½µ¼¶·½·¨µÄÓÅÏȼ¶½ÏµÍ£¬Ö»ÓÐÒµÎñ·½·¨Ã»ÓÐÖ¸¶¨Æä½µ¼¶·½·¨Ê±£¬·þÎñ½µ¼¶Ê±²Å»á´¥·¢È«¾Ö»ØÍË·½·¨¡£ÈôÒµÎñ·½·¨Ö¸¶¨Ëü×Ô¼ºµÄ»ØÍË·½·¨£¬ÄÇôÔÚ·þÎñ½µ¼¶Ê±£¬¾ÍÖ»»áÖ±½Ó´¥·¢Ëü×Ô¼ºµÄ»ØÍË·½·¨£¬¶ø·ÇÈ«¾Ö»ØÍË·½·¨¡£ + +4\. ÖØÆô micro-service-cloud-consumer-dept-feign£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/hystrix/timeout/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix È«¾Ö»ØÍË·½·¨](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101623J11-5.png) +ͼ6£ºÈ«¾Ö»ØÍË·½·¨ + +## ½âñî½µ¼¶Âß¼­ + +²»¹ÜÊÇÒµÎñ·½·¨Ö¸¶¨µÄ½µ¼¶·½·¨»¹ÊÇÈ«¾Ö½µ¼¶·½·¨£¬ËüÃǶ¼±ØÐëºÍÒµÎñ·½·¨ÔÚͬһ¸öÀàÖвÅÄÜÉúЧ£¬ÒµÎñÂß¼­Óë½µ¼¶Âß¼­ñîºÏ¶È¼«¸ß¡£ + +ÏÂÃæÎÒÃǶÔÒµÎñÂß¼­Óë½µ¼¶Âß¼­½øÐнâñ²Ù×÷²½ÖèÈçÏ¡£ + +1\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ net.biancheng.c.service °üÏ£¬Ð½¨ DeptHystrixService ½Ó¿ÚµÄʵÏÖÀà DeptHystrixFallBackService£¬Í³Ò»Îª DeptHystrixService Öеķ½·¨Ìṩ·þÎñ½µ¼¶´¦Àí £¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.service; +import org.springframework.stereotype.Component; +/** +* Hystrix ·þÎñ½µ¼¶ +* ½âñî»ØÍËÂß¼­ +*/ +@Component +public class DeptHystrixFallBackService implements DeptHystrixService { + @Override + public String deptInfo_Ok(Integer id) { + return "--------------------CÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬ÏµÍ³·±Ã¦£¬ÇëÉÔºóÖØÊÔ£¡£¨½âñî»ØÍË·½·¨´¥·¢£©-----------------------"; + } + @Override + public String deptInfo_Timeout(Integer id) { + return "--------------------CÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬ÏµÍ³·±Ã¦£¬ÇëÉÔºóÖØÊÔ£¡£¨½âñî»ØÍË·½·¨´¥·¢£©-----------------------"; + } +} + +``` + + + + + +> **×¢Òâ**£º¸ÃÀà±ØÐëÒÔ×é¼þµÄÐÎʽÌí¼Ó Spring ÈÝÆ÷ÖвÅÄÜÉúЧ£¬×î³£Óõķ½Ê½¾ÍÊÇÔÚÀàÉϱê×¢ @Component ×¢½â¡£ + +2\. ÔÚ·þÎñ°ó¶¨½Ó¿Ú DeptHystrixService ±ê×¢µÄ @FeignClient ×¢½âÖÐÌí¼Ó fallback ÊôÐÔ£¬ÊôÐÔֵΪ DeptHystrixFallBackService.class£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.service; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +@Component +@FeignClient(value = "MICROSERVICECLOUDPROVIDERDEPTHYSTRIX", fallback = DeptHystrixFallBackService.class) +public interface DeptHystrixService { + @RequestMapping(value = "/dept/hystrix/ok/{id}") + public String deptInfo_Ok(@PathVariable("id") Integer id); + @RequestMapping(value = "/dept/hystrix/timeout/{id}") + public String deptInfo_Timeout(@PathVariable("id") Integer id); +} + +``` + + + + + +3\. ÖØÆô micro-service-cloud-consumer-dept-feign£¬È»ºó¹Ø±Õ·þÎñ¶Ë micro-service-cloud-provider-dept-hystrix-8004£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/hystrix/ok/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ½âñî»ØÍËÂß¼­](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016233063-6.png) +ͼ7£ºHystrix ½âñî»ØÍË·½·¨ + +## Hystrix ·þÎñÈÛ¶Ï + +ÈÛ¶Ï»úÖÆÊÇΪÁËÓ¦¶ÔÑ©±ÀЧӦ¶ø³öÏÖµÄÒ»ÖÖ΢·þÎñÁ´Â·±£»¤»úÖÆ¡£ + +µ±Î¢·þÎñϵͳÖеÄij¸ö΢·þÎñ²»¿ÉÓûòÏìӦʱ¼äÌ«³¤Ê±£¬ÎªÁ˱£»¤ÏµÍ³µÄÕûÌå¿ÉÓÃÐÔ£¬ÈÛ¶ÏÆ÷»áÔÝʱÇжÏÇëÇó¶Ô¸Ã·þÎñµÄµ÷Ó㬲¢¿ìËÙ·µ»ØÒ»¸öÓѺõĴíÎóÏìÓ¦¡£ÕâÖÖÈÛ¶Ï״̬²»ÊÇÓÀ¾ÃµÄ£¬ÔÚ¾­ÀúÁËÒ»¶¨µÄʱ¼äºó£¬ÈÛ¶ÏÆ÷»áÔٴμì²â¸Ã΢·þÎñÊÇ·ñ»Ö¸´Õý³££¬Èô·þÎñ»Ö¸´Õý³£Ôò»Ö¸´Æäµ÷ÓÃÁ´Â·¡£ + +#### ÈÛ¶Ï״̬ + +ÔÚÈÛ¶Ï»úÖÆÖÐÉæ¼°ÁËÈýÖÖÈÛ¶Ï״̬£º + +* È۶ϹرÕ״̬£¨Closed£©£ºµ±·þÎñ·ÃÎÊÕý³£Ê±£¬ÈÛ¶ÏÆ÷´¦ÓڹرÕ״̬£¬·þÎñµ÷Ó÷½¿ÉÒÔÕý³£µØ¶Ô·þÎñ½øÐе÷Óᣠ+* ÈÛ¶Ï¿ªÆô״̬£¨Open£©£ºÄ¬ÈÏÇé¿öÏ£¬Ôڹ̶¨Ê±¼äÄÚ½Ó¿Úµ÷Óóö´í±ÈÂÊ´ïµ½Ò»¸öãÐÖµ£¨ÀýÈç 50%£©£¬ÈÛ¶ÏÆ÷»á½øÈëÈÛ¶Ï¿ªÆô״̬¡£½øÈëÈÛ¶Ï״̬ºó£¬ºóÐø¶Ô¸Ã·þÎñµÄµ÷Óö¼»á±»Çжϣ¬ÈÛ¶ÏÆ÷»áÖ´Ðб¾µØµÄ½µ¼¶£¨FallBack£©·½·¨¡£ +* °ëÈÛ¶Ï״̬£¨Half-Open£©£º ÔÚÈÛ¶Ï¿ªÆôÒ»¶Îʱ¼äÖ®ºó£¬ÈÛ¶ÏÆ÷»á½øÈë°ëÈÛ¶Ï״̬¡£ÔÚ°ëÈÛ¶Ï״̬Ï£¬ÈÛ¶ÏÆ÷»á³¢ÊÔ»Ö¸´·þÎñµ÷Ó÷½¶Ô·þÎñµÄµ÷Óã¬ÔÊÐí²¿·ÖÇëÇóµ÷Óø÷þÎñ£¬²¢¼à¿ØÆäµ÷Óóɹ¦ÂÊ¡£Èç¹û³É¹¦ÂÊ´ïµ½Ô¤ÆÚ£¬Ôò˵Ã÷·þÎñÒѻָ´Õý³££¬ÈÛ¶ÏÆ÷½øÈë¹Ø±Õ״̬£»Èç¹û³É¹¦ÂÊÈԾɺܵͣ¬ÔòÖØÐ½øÈëÈÛ¶Ï¿ªÆô״̬¡£ + +ÈýÖÖÈÛ¶Ï״̬֮¼äµÄת»¯¹ØÏµÈçÏÂͼ£º + +![ÈÛ¶Ï״̬ת»»](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10162355X-7.png) +ͼ8£ºÈýÖÖÈÛ¶Ï״̬ת»» + +#### Hystrix ʵÏÖÈÛ¶Ï»úÖÆ + +ÔÚ Spring Cloud ÖУ¬ÈÛ¶Ï»úÖÆÊÇͨ¹ý Hystrix ʵÏֵġ£Hystrix »á¼à¿ØÎ¢·þÎñ¼äµ÷ÓõÄ×´¿ö£¬µ±Ê§°Üµ÷Óõ½Ò»¶¨±ÈÀýʱ£¨ÀýÈç 5 ÃëÄÚʧ°Ü 20 ´Î£©£¬¾Í»áÆô¶¯ÈÛ¶Ï»úÖÆ¡£ + +Hystrix ʵÏÖ·þÎñÈ۶ϵIJ½ÖèÈçÏ£º + +1. µ±·þÎñµÄµ÷Óóö´íÂÊ´ïµ½»ò³¬¹ý Hystix ¹æ¶¨µÄ±ÈÂÊ£¨Ä¬ÈÏΪ 50%£©ºó£¬ÈÛ¶ÏÆ÷½øÈëÈÛ¶Ï¿ªÆô״̬¡£ +2. ÈÛ¶ÏÆ÷½øÈëÈÛ¶Ï¿ªÆô״̬ºó£¬Hystrix »áÆô¶¯Ò»¸öÐÝÃßʱ¼ä´°£¬ÔÚÕâ¸öʱ¼ä´°ÄÚ£¬¸Ã·þÎñµÄ½µ¼¶Âß¼­»áÁÙʱ³äµ±ÒµÎñÖ÷Âß¼­£¬¶øÔ­À´µÄÒµÎñÖ÷Âß¼­²»¿ÉÓᣠ+3. µ±ÓÐÇëÇóÔٴε÷Óø÷þÎñʱ£¬»áÖ±½Óµ÷Óýµ¼¶Âß¼­¿ìËٵطµ»ØÊ§°ÜÏìÓ¦£¬ÒÔ±ÜÃâϵͳѩ±À¡£ +4. µ±ÐÝÃßʱ¼ä´°µ½ÆÚºó£¬Hystrix »á½øÈë°ëÈÛ¶Ïת̬£¬ÔÊÐí²¿·ÖÇëÇó¶Ô·þÎñÔ­À´µÄÖ÷ÒµÎñÂß¼­½øÐе÷Ó㬲¢¼à¿ØÆäµ÷Óóɹ¦ÂÊ¡£ +5. Èç¹ûµ÷Óóɹ¦ÂÊ´ïµ½Ô¤ÆÚ£¬Ôò˵Ã÷·þÎñÒѻָ´Õý³££¬Hystrix ½øÈëÈ۶ϹرÕ״̬£¬·þÎñÔ­À´µÄÖ÷ÒµÎñÂß¼­»Ö¸´£»·ñÔò Hystrix ÖØÐ½øÈëÈÛ¶Ï¿ªÆô״̬£¬ÐÝÃßʱ¼ä´°¿ÚÖØÐ¼ÆÊ±£¬¼ÌÐøÖØ¸´µÚ 2 µ½µÚ 5 ²½¡£ + +#### ʾÀý + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀýÀ´Ñé֤ϠHystrix ÊÇÈçºÎʵÏÖÈÛ¶Ï»úÖÆµÄ¡£ + +1\. ÔÚ micro-service-cloud-provider-dept-hystrix-8004 ÖÐµÄ DeptService ½Ó¿ÚÖÐÌí¼ÓÒ»¸ö deptCircuitBreaker() ·½·¨£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.service; +public interface DeptService { + // hystrix ÈÛ¶ÏÆ÷ʾÀý ok + public String deptInfo_Ok(Integer id); + + //hystrix ÈÛ¶ÏÆ÷³¬Ê±°¸Àý + public String deptInfo_Timeout(Integer id); + // Hystrix ÈÛ¶Ï»úÖÆ°¸Àý + public String deptCircuitBreaker(Integer id); +} + +``` + + + + + +2\. ÔÚ DeptService ½Ó¿ÚµÄʵÏÖÀà DeptServiceImpl Ìí¼Ó deptCircuitBreaker() µÄ·½·¨ÊµÏÖ¼°Æä»ØÍË·½·¨£¬´úÂëÈçÏ¡£ + + + + + +``` +//Hystrix È۶ϰ¸Àý +@Override +@HystrixCommand(fallbackMethod = "deptCircuitBreaker_fallback", commandProperties = { + //ÒÔϲÎÊýÔÚ HystrixCommandProperties ÀàÖÐÓÐĬÈÏÅäÖà + @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //ÊÇ·ñ¿ªÆôÈÛ¶ÏÆ÷ + @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "1000"), //ͳ¼ÆÊ±¼ä´° + @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), //ͳ¼ÆÊ±¼ä´°ÄÚÇëÇó´ÎÊý + @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //ÐÝÃßʱ¼ä´°¿ÚÆÚ + @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), //ÔÚͳ¼ÆÊ±¼ä´°¿ÚÆÚÒÔÄÚ£¬ÇëÇóʧ°ÜÂÊ´ïµ½ 60% ʱ½øÈëÈÛ¶Ï״̬ +}) +public String deptCircuitBreaker(Integer id) { + if (id < 0) { + //µ±´«ÈëµÄ id Ϊ¸ºÊýʱ£¬Å׳öÒì³££¬µ÷Óýµ¼¶·½·¨ + throw new RuntimeException("cÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬id ²»ÄÜÊǸºÊý£¡"); + } + String serialNum = IdUtil.simpleUUID(); + return Thread.currentThread().getName() + "\t" + "µ÷Óóɹ¦£¬Á÷Ë®ºÅΪ£º" + serialNum; +} + +//deptCircuitBreaker µÄ½µ¼¶·½·¨ +public String deptCircuitBreaker_fallback(Integer id) { + return "cÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬id ²»ÄÜÊǸºÊý,ÇëÉÔºóÖØÊÔ!\t id:" + id; +} + +``` + + + + + +ÔÚÒÔÉÏ´úÂëÖУ¬¹²Éæ¼°µ½ÁË 4 ¸öÓë Hystrix ÈÛ¶Ï»úÖÆÏà¹ØµÄÖØÒª²ÎÊý£¬Õâ 4 ¸ö²ÎÊýµÄº¬ÒåÈçÏÂ±í¡£ + +| ²ÎÊý | ÃèÊö | +| ---------------------------------------- | ------------------------------------------------------------ | +| metrics.rollingStats.timeInMilliseconds | ͳ¼ÆÊ±¼ä´°¡£ | +| circuitBreaker.sleepWindowInMilliseconds | ÐÝÃßʱ¼ä´°£¬ÈÛ¶Ï¿ªÆô״̬³ÖÐøÒ»¶Îʱ¼äºó£¬ÈÛ¶ÏÆ÷»á×Ô¶¯½øÈë°ëÈÛ¶Ï״̬£¬Õâ¶Îʱ¼ä¾Í±»³ÆÎªÐÝÃß´°¿ÚÆÚ¡£ | +| circuitBreaker.requestVolumeThreshold | ÇëÇó×ÜÊý·§Öµ¡£ÔÚͳ¼ÆÊ±¼ä´°ÄÚ£¬ÇëÇó×ÜÊý±ØÐëµ½´ïÒ»¶¨µÄÊýÁ¿¼¶£¬Hystrix ²Å¿ÉÄܻὫÈÛ¶ÏÆ÷´ò¿ª½øÈëÈÛ¶Ï¿ªÆôת̬£¬¶øÕâ¸öÇëÇóÊýÁ¿¼¶¾ÍÊÇ ÇëÇó×ÜÊý·§Öµ¡£Hystrix ÇëÇó×ÜÊýãÐֵĬÈÏΪ 20£¬Õâ¾ÍÒâζ×ÅÔÚͳ¼ÆÊ±¼ä´°ÄÚ£¬Èç¹û·þÎñµ÷ÓôÎÊý²»×ã 20 ´Î£¬¼´Ê¹ËùÓеÄÇëÇó¶¼µ÷Óóö´í£¬ÈÛ¶ÏÆ÷Ò²²»»á´ò¿ª¡£ | +| circuitBreaker.errorThresholdPercentage | ´íÎó°Ù·Ö±ÈãÐÖµ¡£µ±ÇëÇó×ÜÊýÔÚͳ¼ÆÊ±¼ä´°ÄÚ³¬¹ýÁËÇëÇó×ÜÊý·§Öµ£¬ÇÒÇëÇóµ÷Óóö´íÂʳ¬¹ýÒ»¶¨µÄ±ÈÀý£¬ÈÛ¶ÏÆ÷²Å»á´ò¿ª½øÈëÈÛ¶Ï¿ªÆôת̬£¬¶øÕâ¸ö±ÈÀý¾ÍÊÇ´íÎó°Ù·Ö±ÈãÐÖµ¡£´íÎó°Ù·Ö±ÈãÐÖµÉèÖÃΪ 50£¬¾Í±íʾ´íÎó°Ù·Ö±ÈΪ 50%£¬Èç¹û·þÎñ·¢ÉúÁË 30 ´Îµ÷Óã¬ÆäÖÐÓÐ 15 ´Î·¢ÉúÁË´íÎ󣬼´³¬¹ýÁË 50% µÄ´íÎó°Ù·Ö±È£¬Õâʱºò½«ÈÛ¶ÏÆ÷¾Í»á´ò¿ª¡£ | + +3\. ÔÚ DeptController ÖÐÌí¼ÓÒ»¸ö deptCircuitBreaker() ·½·¨¶ÔÍâÌṩ·þÎñ£¬´úÂëÈçÏ¡£ + + + + + +``` + +// Hystrix ·þÎñÈÛ¶Ï +@RequestMapping(value = "/dept/hystrix/circuit/{id}") +public String deptCircuitBreaker(@PathVariable("id") Integer id){ + String result = deptService.deptCircuitBreaker(id); + log.info("result:"+result); + return result; +} + +``` + + + + + +4\. ÖØÆô micro-service-cloud-provider-dept-hystrix-8004£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/circuit/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ʵÏÖÈÛ¶Ï»úÖÆ µ÷ÓÃÕýÈ·](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016233Q8-8.png) +ͼ9£ºHystrix ʵÏÖÈÛ¶Ï»úÖÆ µ÷ÓÃÕýȷʾÀý + +5\. ä¯ÀÀÆ÷¶à´Î£¨µ÷ÓôÎÊý´óÓÚÇëÇó×ÜÊý·§Öµ£©·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/circuit/-2¡±£¬Ê¹µ÷Óóö´íÂÊ´óÓÚ´íÎó°Ù·Ö±È·§Öµ£¬½á¹ûÏÂͼ¡£ + +![Hystrix ʵÏÖÈÛ¶Ï»úÖÆ ´íÎóµ÷ÓÃ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10162332D-9.png) +ͼ10£ºHystrix ʵÏÖÈÛ¶Ï»úÖÆ ´íÎóµ÷Óà + +6\. ÖØÐ½«²ÎÊýÐÞ¸ÄΪÕýÊý£¨ÀýÈç²ÎÊýΪ 3£©£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/circuit/3¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ÈÛ¶Ï¿ªÆôת̨](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016235592-10.png) +ͼ11£ºHystrix ÈÛ¶Ï¿ªÆô״̬´«ÈëÕýÊý + +ͨ¹ýͼ 11 ¿ÉÒÔ¿´µ½£¬ÔÚÈÛ¶Ï¿ªÆô״̬Ï£¬¼´Ê¹ÎÒÃÇ´«ÈëµÄ²ÎÊýÒѾ­ÊÇÕýÊý£¬µ÷ÓõÄÒÀÈ»½µ¼¶Âß¼­¡£ + +7\. ¼ÌÐøÁ¬Ðø·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/circuit/3¡±£¬½á¹ûÏÂͼ¡£ + +![Hystrix È۶ϹرÕ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016231R0-11.gif) +ͼ12£ºHystrix ½øÈëÈ۶ϹرÕ״̬ + +ͨ¹ýͼ 12 ¿ÉÒÔ¿´³ö£¬µ±·þÎñµ÷ÓÃÕýÈ·ÂÊÉÏÉýµ½Ò»¶¨µÄÀûÂʺó£¬Hystrix ½øÈëÈ۶ϹرÕ״̬¡£ + +## Hystrix ¹ÊÕÏ¼à¿Ø + +Hystrix »¹ÌṩÁË׼ʵʱµÄµ÷ÓÃ¼à¿Ø£¨Hystrix Dashboard£©¹¦ÄÜ£¬Hystrix »á³ÖÐøµØ¼Ç¼ËùÓÐͨ¹ý Hystrix ·¢ÆðµÄÇëÇóµÄÖ´ÐÐÐÅÏ¢£¬²¢ÒÔͳ¼Æ±¨±íµÄÐÎʽչʾ¸øÓû§£¬°üÀ¨Ã¿ÃëÖ´ÐÐÇëÇóµÄÊýÁ¿¡¢³É¹¦ÇëÇóµÄÊýÁ¿ºÍʧ°ÜÇëÇóµÄÊýÁ¿µÈ¡£ + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀýÀ´´î½¨ Hystrix Dashboard£¬¼à¿Ø micro-service-cloud-provider-dept-hystrix-8004 µÄÔËÐÐÇé¿ö¡£ + +1\. ÔÚ¸¸¹¤³ÌÏÂн¨Ò»¸öÃûΪ micro-service-cloud-consumer-dept-hystrix-dashboard-9002 µÄ×ÓÄ£¿é£¬²¢ÔÚÆä pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + + + +``` + + + + 4.0.0 + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + net.biancheng.c + micro-service-cloud-consumer-dept-hystrix-dashboard-9002 + 0.0.1-SNAPSHOT + micro-service-cloud-consumer-dept-hystrix-dashboard-9002 + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix-dashboard + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + + + + + +2\. ÔÚ micro-service-cloud-consumer-dept-hystrix-dashboard-9002 µÄ application.yml ÖÐÌí¼ÓÒÔÏÂÅäÖᣠ+ + + + + +``` + +server: + port: 9002 #¶Ë¿ÚºÅ + +#http://eureka7001.com:9002/hystrix ÈÛ¶ÏÆ÷¼à¿ØÒ³Ãæ +# localhost:8004//actuator/hystrix.stream ¼à¿ØµØÖ· +hystrix: + dashboard: + proxy-stream-allow-list: + - "localhost" + +``` + + + + + +3\. ÔÚ micro-service-cloud-consumer-dept-hystrix-dashboard-9002 µÄÖ÷Æô¶¯ÀàÉÏÌí¼Ó @EnableHystrixDashboard ×¢½â£¬¿ªÆô Hystrix ¼à¿Ø¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; + +@SpringBootApplication +@EnableHystrixDashboard +public class MicroServiceCloudConsumerDeptHystrixDashboard9002Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConsumerDeptHystrixDashboard9002Application.class, args); + } + +} + +``` + + + + + +4\. ÔÚ micro-service-cloud-provider-dept-hystrix-8004 µÄ net.biancheng.c.config °üÏ£¬´´½¨Ò»¸öÃûΪ HystrixDashboardConfig µÄÅäÖÃÀ࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.config; + +import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class HystrixDashboardConfig { + /** + * Hystrix dashboard ¼à¿Ø½çÃæ±ØÐëÅäÖà + * @return + */ + @Bean + public ServletRegistrationBean getServlet() { + HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); + ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); + registrationBean.setLoadOnStartup(1); + registrationBean.addUrlMappings("/actuator/hystrix.stream");//·ÃÎÊ·¾¶ + registrationBean.setName("hystrix.stream"); + return registrationBean; + } + +} + +``` + + + + + +5\. Æô¶¯ micro-service-cloud-consumer-dept-hystrix-dashboard-9002£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:9002/hystrix¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Hystrix ¼à¿Ø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016234440-12.png) +ͼ13£ºHystrix ¼à¿ØÒ³Ãæ + +6\. ÖØÆô micro-service-cloud-provider-dept-hystrix-8004£¬²¢½«ÒÔÏÂÐÅÏ¢Ìîµ½ Hystrix ¼à¿ØÒ³ÃæÖУ¬ÈçÏÂͼ¡£ + +![Hystrix ¼à¿ØÐÅÏ¢](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016232636-13.png) +ͼ14£ºHystrix ¼à¿ØÐÅÏ¢ + +7\. µã»÷Ï·½µÄ Monitor Stream °´Å¥£¬Ìø×ªµ½ Hystrix ¶Ô micro-service-cloud-provider-dept-hystrix-8004 µÄ¼à¿ØÒ³Ã棬ÈçÏÂͼ¡£ + +![Hystrix ¼à¿Ø 8004](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1016231229-14.png) +ͼ15£ºHystrix ¼à¿ØÎ¢·þÎñÔËÐÐÇé¿ö + +8\. ʹÓÃä¯ÀÀÆ÷¶à´Î·ÃÎÊ¡°http://eureka7001.com:8004/dept/hystrix/circuit/1¡±ºÍ ¡°http://eureka7001.com:8004/dept/hystrix/circuit/-1¡±£¬²é¿´ Hystrix ¼à¿ØÒ³Ã棬ÈçÏÂͼ¡£ + +![Hystrix ¼à¿Ø 8004 ÔËÐÐÇé¿ö](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10162345J-15.png) +ͼ16£ºHystrix ¼à¿Ø·þÎñÔËÐÐÇé¿ö + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" new file mode 100644 index 0000000..1a35579 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudLoadBalancer.md" @@ -0,0 +1,346 @@ +ǰ²»¾Ã£¬ÎÒ°ÑMall΢·þÎñ°æ±¾È«ÃæÉý¼¶ÁË £¬ÔÚͨ¹ýGatewayÍø¹Øµ÷ÓÃÆäËû·þÎñµÄʱºò£¬³öÏÖÁËService UnavailableµÄÎÊÌâ¡£ÅŲéÔ­Òòʱ·¢ÏÖ×÷Ϊ¸ºÔؾùºâ×é¼þµÄRibbon±»ÆúÓÃÁË£¬×÷ΪNetflix¿ªÔ´µÄÒ»¸ö×é¼þ£¬RibbonÔçÒѽøÈëά»¤×´Ì¬¡£ÏÖÔÚÍÆ¼öʹÓõÄÊÇLoadbalancer£¬½ñÌìÎÒÃǾÍÀ´ÁÄÁÄLoadbalancerµÄʹÓã¡ + +# LoadBalancer¼ò½é + +LoadBalancerÊÇSpring Cloud¹Ù·½ÌṩµÄ¸ºÔؾùºâ×é¼þ£¬¿ÉÓÃÓÚÌæ´úRibbon¡£ÆäʹÓ÷½Ê½ÓëRibbon»ù±¾¼æÈÝ£¬¿ÉÒÔ´ÓRibbon½øÐÐÆ½»¬¹ý¶É¡£ + +# ʹÓà + +ÏÂÃæ½éÉÜÏÂLoadBalancerµÄ»ù±¾Ê¹Óã¬ÎÒÃǽ«Ê¹ÓÃNacos×÷Ϊע²áÖÐÐÄ£¬Í¨¹ýnacos-loadbalancer-serviceºÍnacos-user-serviceÁ½¸ö·þÎñ¼äµÄÏ໥µ÷ÓÃÀ´½øÐÐÑÝʾ¡£ + +# ¸ºÔؾùºâ + +ÎÒÃǽ«Ê¹ÓÃRestTemplateÀ´ÑÝʾÏÂLoadBalancerµÄ¸ºÔؾùºâ¹¦ÄÜ¡£ + +- Ê×ÏÈÔÚnacos-loadbalancer-serviceÄ£¿éµÄpom.xmlÎļþÖÐÌí¼ÓLoadBalancerÏà¹ØÒÀÀµ£» + + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + + +- È»ºó´´½¨JavaÅäÖÃÀ࣬ÓÃÓÚÅäÖÃRestTemplate£¬Í¬Ê±Ê¹ÓÃ@LoadBalanced×¢½â¸³ÓèÆä¸ºÔؾùºâÄÜÁ¦£» + + + + /** + * RestTemplateÏà¹ØÅäÖà + * Created by macro on 2019/8/29. + */ + @Configuration + public class RestTemplateConfig { + + @Bean + @ConfigurationProperties(prefix = "rest.template.config") + public HttpComponentsClientHttpRequestFactory customHttpRequestFactory() { + return new HttpComponentsClientHttpRequestFactory(); + } + + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(customHttpRequestFactory()); + } + } + + + + + +- ÔÚapplication.ymlÖпÉÒÔʹÓÃ×Ô¶¨ÒåÅäÖöÔRestTemplateµÄµ÷Óó¬Ê±½øÐÐÅäÖã» + + + + rest: + template: + config: # RestTemplateµ÷Óó¬Ê±ÅäÖà + connectTimeout: 5000 + readTimeout: 5000 + + + + +- È»ºóÔÚControllerÖÐʹÓÃRestTemplate½øÐÐÔ¶³Ìµ÷Óã» + + + + /** + * Created by macro on 2019/8/29. + */ + @RestController + @RequestMapping("/user") + public class UserLoadBalancerController { + @Autowired + private RestTemplate restTemplate; + @Value("${service-url.nacos-user-service}") + private String userServiceUrl; + + @GetMapping("/{id}") + public CommonResult getUser(@PathVariable Long id) { + return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); + } + + @GetMapping("/getByUsername") + public CommonResult getByUsername(@RequestParam String username) { + return restTemplate.getForObject(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username); + } + + @GetMapping("/getEntityByUsername") + public CommonResult getEntityByUsername(@RequestParam String username) { + ResponseEntity entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username); + if (entity.getStatusCode().is2xxSuccessful()) { + return entity.getBody(); + } else { + return new CommonResult("²Ù×÷ʧ°Ü", 500); + } + } + + @PostMapping("/create") + public CommonResult create(@RequestBody User user) { + return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class); + } + + @PostMapping("/update") + public CommonResult update(@RequestBody User user) { + return restTemplate.postForObject(userServiceUrl + "/user/update", user, CommonResult.class); + } + + @PostMapping("/delete/{id}") + public CommonResult delete(@PathVariable Long id) { + return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id); + } + } + + + + +- ÔÚnacos-user-serviceÖÐÎÒÃÇÒѾ­ÊµÏÖÁËÕâЩ½Ó¿Ú£¬¿ÉÒÔÌṩ¸ønacos-loadbalancer-service·þÎñ½øÐÐÔ¶³Ìµ÷Óã» + + + +- È»ºóÆô¶¯Ò»¸önacos-loadbalancer-service£¬ºÍÁ½¸önacos-user-service£¬´ËʱNacosÖлáÏÔʾÈçÏ·þÎñ£» + + + +- ´Ëʱͨ¹ýnacos-loadbalancer-serviceµ÷ÓýӿڽøÐвâÊÔ£¬»á·¢ÏÖÁ½¸önacos-user-service½»Ìæ´òÓ¡ÈÕÖ¾ÐÅÏ¢£¬Ê¹ÓõÄÊÇÂÖѯ²ßÂÔ£¬·ÃÎʵØÖ·£ºhttp://localhost:8308/user/1 + + + +# ÉùÃ÷ʽ·þÎñµ÷Óà + +µ±È»LoadBalancer³ýÁËʹÓÃRestTemplateÀ´½øÐÐÔ¶³Ìµ÷Ó㬻¹¿ÉÒÔʹÓÃOpenFeignÀ´½øÐÐÉùÃ÷ʽ·þÎñµ÷Óã¬ÏÂÃæÎÒÃǾÍÀ´½éÉÜÏ¡£ + +- Ê×ÏÈnacos-loadbalancer-serviceÄ£¿éµÄpom.xmlÎļþÖÐÌí¼ÓOpenFeignµÄÏà¹ØÒÀÀµ£» + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + +- È»ºóÔÚOpenFeignµÄ¿Í»§¶Ë½Ó¿ÚÖÐÉùÃ÷ºÃÐèÒªµ÷ÓõķþÎñ½Ó¿ÚÒÔ¼°µ÷Ó÷½Ê½£» + + + + /** + * Created by macro on 2019/9/5. + */ + @FeignClient(value = "nacos-user-service") + public interface UserService { + @PostMapping("/user/create") + CommonResult create(@RequestBody User user); + + @GetMapping("/user/{id}") + CommonResult getUser(@PathVariable Long id); + + @GetMapping("/user/getByUsername") + CommonResult getByUsername(@RequestParam String username); + + @PostMapping("/user/update") + CommonResult update(@RequestBody User user); + + @PostMapping("/user/delete/{id}") + CommonResult delete(@PathVariable Long id); + } + + + +- ÔÙÔÚControllerÖÐʹÓÃOpenFeignµÄ¿Í»§¶Ë½Ó¿ÚÀ´µ÷ÓÃÔ¶³Ì·þÎñ£» + + + + /** + * Created by macro on 2019/8/29. + */ + @RestController + @RequestMapping("/userFeign") + public class UserFeignController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public CommonResult getUser(@PathVariable Long id) { + return userService.getUser(id); + } + + @GetMapping("/getByUsername") + public CommonResult getByUsername(@RequestParam String username) { + return userService.getByUsername(username); + } + + @PostMapping("/create") + public CommonResult create(@RequestBody User user) { + return userService.create(user); + } + + @PostMapping("/update") + public CommonResult update(@RequestBody User user) { + return userService.update(user); + } + + @PostMapping("/delete/{id}") + public CommonResult delete(@PathVariable Long id) { + return userService.delete(id); + } + } + + + + + +- Èç¹ûÄãÏëÉèÖÃÏÂOpenFeignµÄ³¬Ê±ÅäÖõϰ£¬¿ÉÒÔÔÚapplication.ymlÖÐÌí¼ÓÈçÏÂÄÚÈÝ£» + + + + feign: + client: + config: + default: # Feignµ÷Óó¬Ê±ÅäÖà + connectTimeout: 5000 + readTimeout: 5000 + + + +- ½ÓÏÂÀ´Í¨¹ý²âÊÔ½Ó¿Úµ÷ÓÃÔ¶³Ì·þÎñ£¬·¢ÏÖ¿ÉÒÔÕý³£µ÷Ó㬷ÃÎʵØÖ·£ºhttp://localhost:8308/userFeign/1 + + + +# ·þÎñʵÀý»º´æ + +LoadBalancerΪÁËÌá¸ßÐÔÄÜ£¬²»»áÔÚÿ´ÎÇëÇóʱȥ»ñȡʵÀýÁÐ±í£¬¶øÊǽ«·þÎñʵÀýÁÐ±í½øÐÐÁ˱¾µØ»º´æ¡£ + +ĬÈϵĻº´æÊ±¼äΪ35s£¬ÎªÁ˼õÉÙ·þÎñ²»¿ÉÓû¹»á±»Ñ¡ÔñµÄ¿ÉÄÜÐÔ£¬ÎÒÃÇ¿ÉÒÔ½øÐÐÈçÏÂÅäÖᣠ+ + + + spring: + cloud: + loadbalancer: + cache: # ¸ºÔؾùºâ»º´æÅäÖà + enabled: true # ¿ªÆô»º´æ + ttl: 5s # ÉèÖûº´æÊ±¼ä + capacity: 256 # ÉèÖûº´æ´óС + + + + +# HTTPÇëÇóת»» + +Èç¹ûÄãÏëÔÚÿ´ÎÔ¶³Ìµ÷ÓÃÖд«Èë×Ô¶¨ÒåµÄÇëÇóÍ·µÄ»°£¬¿ÉÒÔÊÔÊÔLoadBalancerRequestTransformer£¬Í¨¹ýËü¿ÉÒÔ¶ÔԭʼÇëÇó½øÐÐÒ»¶¨µÄת»»¡£ + +- Ê×ÏÈÎÒÃÇÐèÒªÅäÖúÃLoadBalancerRequestTransformerµÄBeanʵÀý£¬ÕâÀïÎÒÃǽ«ServiceInstanceµÄinstanceId·ÅÈëµ½ÇëÇóÍ·X-InstanceIdÖУ» + + + + /** + * LoadBalancerÏà¹ØÅäÖà + * Created by macro on 2022/7/26. + */ + @Configuration + public class LoadBalancerConfig { + @Bean + public LoadBalancerRequestTransformer transformer() { + return new LoadBalancerRequestTransformer() { + @Override + public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) { + return new HttpRequestWrapper(request) { + @Override + public HttpHeaders getHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(super.getHeaders()); + headers.add("X-InstanceId", instance.getInstanceId()); + return headers; + } + }; + } + }; + } + } + + + + + +- È»ºóÐÞ¸Änacos-user-serviceÖеĴúÂ룬´òÓ¡»ñÈ¡µ½µÄÇëÇóÍ·X-InstanceIdµÄÐÅÏ¢£» + + + + /** + * Created by macro on 2019/8/29. + */ + @RestController + @RequestMapping("/user") + public class UserController { + @GetMapping("/{id}") + public CommonResult getUser(@PathVariable Long id) { + User user = userService.getUser(id); + LOGGER.info("¸ù¾Ýid»ñÈ¡Óû§ÐÅÏ¢£¬Óû§Ãû³ÆÎª£º{}", user.getUsername()); + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = servletRequestAttributes.getRequest(); + String instanceId = request.getHeader("X-InstanceId"); + if (StrUtil.isNotEmpty(instanceId)) { + LOGGER.info("»ñÈ¡µ½×Ô¶¨ÒåÇëÇóÍ·:X-InstanceId={}", instanceId); + } + return new CommonResult<>(user); + } + } + +- ½ÓÏÂÀ´·ÃÎʽӿڽøÐвâÊÔ£¬nacos-user-service¿ØÖÆÌ¨½«´òÓ¡ÈçÏÂÈÕÖ¾£¬·¢ÏÖ×Ô¶¨ÒåÇëÇóÍ·ÒѾ­³É¹¦´«µÝÁË£¬·ÃÎʵØÖ·£ºhttp://localhost:8308/user/1 + + + + 2022-07-26 15:05:19.920 INFO 14344 --- [nio-8206-exec-5] c.macro.cloud.controller.UserController : ¸ù¾Ýid»ñÈ¡Óû§ÐÅÏ¢£¬Óû§Ãû³ÆÎª£ºmacro + 2022-07-26 15:05:19.921 INFO 14344 --- [nio-8206-exec-5] c.macro.cloud.controller.UserController : »ñÈ¡µ½×Ô¶¨ÒåÇëÇóÍ·:X-InstanceId=192.168.3.227#8206#DEFAULT#DEFAULT_GROUP@@nacos-user-service + + + +# ×ܽá + +½ñÌìͨ¹ý¶ÔLoadBalancerµÄÒ»²¨Êµ¼ùÎÒÃÇ¿ÉÒÔ·¢ÏÖ£¬Ê¹ÓÃLoadBalancerºÍRibbonµÄÇø±ðÆäʵ²¢²»´ó£¬Ö÷ÒªÊÇһЩÅäÖ÷½Ê½µÄÏàͬ¡£Èç¹ûÄã֮ǰʹÓùýRibbonµÄ»°£¬»ù±¾ÉÏ¿ÉÒÔÎÞ·ìÇл»µ½LoadBalancer¡£ + +# ²Î¿¼×ÊÁÏ + +¹Ù·½Îĵµ£ºhttps://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer + +# ÏîĿԴÂëµØÖ· + +https://github.com/macrozheng/springcloud-learning/tree/master/nacos-loadbalancer-service + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" new file mode 100644 index 0000000..50c14d5 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudOpenFeign.md" @@ -0,0 +1,489 @@ +Netflix Feign ÊÇ Netflix ¹«Ë¾·¢²¼µÄÒ»ÖÖʵÏÖ¸ºÔؾùºâºÍ·þÎñµ÷ÓõĿªÔ´×é¼þ¡£Spring Cloud ½«ÆäÓë Netflix ÖÐµÄÆäËû¿ªÔ´·þÎñ×é¼þ£¨ÀýÈç Eureka¡¢Ribbon ÒÔ¼° Hystrix µÈ£©Ò»ÆðÕûºÏ½ø Spring Cloud Netflix Ä£¿éÖУ¬ÕûºÏºóÈ«³ÆÎª Spring Cloud Netflix Feign¡£ + +Feign ¶Ô [Ribbon](http://c.biancheng.net/springcloud/ribbon.html) ½øÐÐÁ˼¯³É£¬ÀûÓà Ribbon ά»¤ÁËÒ»·Ý¿ÉÓ÷þÎñÇåµ¥£¬²¢Í¨¹ý Ribbon ʵÏÖÁ˿ͻ§¶ËµÄ¸ºÔؾùºâ¡£ + +Feign ÊÇÒ»ÖÖÉùÃ÷ʽ·þÎñµ÷ÓÃ×é¼þ£¬ËüÔÚ RestTemplate µÄ»ù´¡ÉÏ×öÁ˽øÒ»²½µÄ·â×°¡£Í¨¹ý Feign£¬ÎÒÃÇÖ»ÐèÒªÉùÃ÷Ò»¸ö½Ó¿Ú²¢Í¨¹ý×¢½â½øÐмòµ¥µÄÅäÖã¨ÀàËÆÓÚ Dao ½Ó¿ÚÉÏÃæµÄ Mapper ×¢½âÒ»Ñù£©¼´¿ÉʵÏÖ¶Ô HTTP ½Ó¿ÚµÄ°ó¶¨¡£ + +ͨ¹ý Feign£¬ÎÒÃÇ¿ÉÒÔÏñµ÷Óñ¾µØ·½·¨Ò»ÑùÀ´µ÷ÓÃÔ¶³Ì·þÎñ£¬¶øÍêÈ«¸Ð¾õ²»µ½ÕâÊÇÔÚ½øÐÐÔ¶³Ìµ÷Óᣠ+ +Feign Ö§³Ö¶àÖÖ×¢½â£¬ÀýÈç Feign ×Ô´øµÄ×¢½âÒÔ¼° JAX-RS ×¢½âµÈ£¬µ«Òź¶µÄÊÇ Feign ±¾Éí²¢²»Ö§³Ö Spring MVC ×¢½â£¬ÕâÎÞÒÉ»á¸ø¹ã´ó Spring Óû§´øÀ´²»±ã¡£ + +2019 Äê Netflix ¹«Ë¾Ðû²¼ Feign ×é¼þÕýʽ½øÈëÍ£¸üά»¤×´Ì¬£¬ÓÚÊÇ Spring ¹Ù·½±ãÍÆ³öÁËÒ»¸öÃûΪ OpenFeign µÄ×é¼þ×÷Ϊ Feign µÄÌæ´ú·½°¸¡£ + +## OpenFeign + +OpenFeign È«³Æ Spring Cloud OpenFeign£¬ËüÊÇ Spring ¹Ù·½ÍƳöµÄÒ»ÖÖÉùÃ÷ʽ·þÎñµ÷ÓÃÓë¸ºÔØ¾ùºâ×é¼þ£¬ËüµÄ³öÏÖ¾ÍÊÇΪÁËÌæ´ú½øÈëÍ£¸üά»¤×´Ì¬µÄ Feign¡£ + +OpenFeign ÊÇ Spring Cloud ¶Ô Feign µÄ¶þ´Î·â×°£¬Ëü¾ßÓÐ Feign µÄËùÓй¦ÄÜ£¬²¢ÔÚ Feign µÄ»ù´¡ÉÏÔö¼ÓÁË¶Ô Spring MVC ×¢½âµÄÖ§³Ö£¬ÀýÈç @RequestMapping¡¢@GetMapping ºÍ @PostMapping µÈ¡£ + +#### OpenFeign ³£ÓÃ×¢½â + +ʹÓà OpenFegin ½øÐÐÔ¶³Ì·þÎñµ÷ÓÃʱ£¬³£ÓÃ×¢½âÈçÏÂ±í¡£ + +| ×¢½â | ˵Ã÷ | +| ------------------- | ------------------------------------------------------------ | +| @FeignClient | ¸Ã×¢½âÓÃÓÚ֪ͨ OpenFeign ×é¼þ¶Ô @RequestMapping ×¢½âϵĽӿڽøÐнâÎö£¬²¢Í¨¹ý¶¯Ì¬´úÀíµÄ·½Ê½²úÉúʵÏÖÀ࣬ʵÏÖ¸ºÔؾùºâºÍ·þÎñµ÷Óᣠ| +| @EnableFeignClients | ¸Ã×¢½âÓÃÓÚ¿ªÆô OpenFeign ¹¦ÄÜ£¬µ± Spring Cloud Ó¦ÓÃÆô¶¯Ê±£¬OpenFeign »áɨÃè±êÓÐ @FeignClient ×¢½âµÄ½Ó¿Ú£¬Éú³É´úÀí²¢×¢²áµ½ Spring ÈÝÆ÷ÖС£ | +| @RequestMapping | Spring MVC ×¢½â£¬ÔÚ Spring MVC ÖÐʹÓøÃ×¢½âÓ³ÉäÇëÇó£¬Í¨¹ýËüÀ´Ö¸¶¨¿ØÖÆÆ÷£¨Controller£©¿ÉÒÔ´¦ÀíÄÄЩ URL ÇëÇó£¬Ï൱ÓÚ Servlet ÖÐ web.xml µÄÅäÖᣠ| +| @GetMapping | Spring MVC ×¢½â£¬ÓÃÀ´Ó³Éä GET ÇëÇó£¬ËüÊÇÒ»¸ö×éºÏ×¢½â£¬Ï൱ÓÚ @RequestMapping(method = RequestMethod.GET) ¡£ | +| @PostMapping | Spring MVC ×¢½â£¬ÓÃÀ´Ó³Éä POST ÇëÇó£¬ËüÊÇÒ»¸ö×éºÏ×¢½â£¬Ï൱ÓÚ @RequestMapping(method = RequestMethod.POST) ¡£ | + +> Spring Cloud Finchley ¼°ÒÔÉϰ汾һ°ãʹÓà OpenFeign ×÷ΪÆä·þÎñµ÷ÓÃ×é¼þ¡£ÓÉÓÚ OpenFeign ÊÇÔÚ 2019 Äê Feign Í£¸ü½øÈëά»¤ºóÍÆ³öµÄ£¬Òò´Ë´ó¶àÊý 2019 Äê¼°ÒÔºóµÄÐÂÏîĿʹÓõͼÊÇ OpenFeign£¬¶ø 2018 ÄêÒÔǰµÄÏîĿһ°ãʹÓà Feign¡£ + +## Feign VS OpenFeign + +ÏÂÃæÎÒÃǾÍÀ´¶Ô±ÈÏ Feign ºÍ OpenFeign µÄÒìͬ¡£ + +#### Ïàͬµã + +Feign ºÍ OpenFegin ¾ßÓÐÒÔÏÂÏàͬµã£º + +* Feign ºÍ OpenFeign ¶¼ÊÇ Spring Cloud ϵÄÔ¶³Ìµ÷Óú͸ºÔؾùºâ×é¼þ¡£ +* Feign ºÍ OpenFeign ×÷ÓÃÒ»Ñù£¬¶¼¿ÉÒÔʵÏÖ·þÎñµÄÔ¶³Ìµ÷Óú͸ºÔؾùºâ¡£ +* Feign ºÍ OpenFeign ¶¼¶Ô Ribbon ½øÐÐÁ˼¯³É£¬¶¼ÀûÓà Ribbon ά»¤ÁË¿ÉÓ÷þÎñÇåµ¥£¬²¢Í¨¹ý Ribbon ʵÏÖÁ˿ͻ§¶ËµÄ¸ºÔؾùºâ¡£ +* Feign ºÍ OpenFeign ¶¼ÊÇÔÚ·þÎñÏû·ÑÕߣ¨¿Í»§¶Ë£©¶¨Òå·þÎñ°ó¶¨½Ó¿Ú²¢Í¨¹ý×¢½âµÄ·½Ê½½øÐÐÅäÖã¬ÒÔʵÏÖÔ¶³Ì·þÎñµÄµ÷Óᣠ+ +#### ²»Í¬µã + +Feign ºÍ OpenFeign ¾ßÓÐÒÔϲ»Í¬£º + +* Feign ºÍ OpenFeign µÄÒÀÀµÏͬ£¬Feign µÄÒÀÀµÎª spring-cloud-starter-feign£¬¶ø OpenFeign µÄÒÀÀµÎª spring-cloud-starter-openfeign¡£ +* Feign ºÍ OpenFeign Ö§³ÖµÄ×¢½â²»Í¬£¬Feign Ö§³Ö Feign ×¢½âºÍ JAX-RS ×¢½â£¬µ«²»Ö§³Ö Spring MVC ×¢½â£»OpenFeign ³ýÁËÖ§³Ö Feign ×¢½âºÍ JAX-RS ×¢½âÍ⣬»¹Ö§³Ö Spring MVC ×¢½â¡£ + +## OpenFeign ʵÏÖÔ¶³Ì·þÎñµ÷Óà + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´ÑÝʾÏÂͨ¹ý OpenFeign ÊÇÈçºÎʵÏÖÔ¶³Ì·þÎñµ÷Óõġ£ + +1\. ÔÚ spring-cloud-demo2 Ï´´½¨Ò»¸öÃûΪ micro-service-cloud-consumer-dept-feign µÄ Spring Boot Ä£¿é£¬²¢ÔÚ pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + + + +``` + + + 4.0.0 + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + + net.biancheng.c + micro-service-cloud-consumer-dept-feign + 0.0.1-SNAPSHOT + micro-service-cloud-consumer-dept-feign + Demo project for Spring Boot + + 1.8 + + + + net.biancheng.c + micro-service-cloud-api + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + +``` + + + + + +2\. ÔÚ micro-service-cloud-consumer-dept-feign ϵÄÀà·¾¶£¨¼´ /resources Ŀ¼£©Ï£¬Ìí¼ÓÒ»¸ö application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` + +server: + port: 80 +eureka: + client: + register-with-eureka: false #·þÎñÏû·ÑÕß¿ÉÒÔ²»Ïò·þÎñ×¢²áÖÐÐÄ×¢²á·þÎñ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ + fetch-registry: true #·þÎñÏû·ÑÕ߿ͻ§¶ËÐèҪȥ¼ìË÷·þÎñ +``` + + + + + +3\. ÔÚ net.biancheng.c.service °üÏ´´½¨Ò»¸öÃûΪ DeptFeignService µÄ½Ó¿Ú£¬²¢ÔڸýӿÚÉÏʹÓà @FeignClient ×¢½âʵÏÖ¶Ô·þÎñ½Ó¿ÚµÄ°ó¶¨£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.service; + +import net.biancheng.c.entity.Dept; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.List; + +//Ìí¼ÓΪÈÝÆ÷ÄÚµÄÒ»¸ö×é¼þ +@Component +// ·þÎñÌṩÕßÌṩµÄ·þÎñÃû³Æ£¬¼´ application.name +@FeignClient(value = "MICROSERVICECLOUDPROVIDERDEPT") +public interface DeptFeignService { + //¶ÔÓ¦·þÎñÌṩÕߣ¨8001¡¢8002¡¢8003£©Controller Öж¨ÒåµÄ·½·¨ + @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) + public Dept get(@PathVariable("id") int id); + + @RequestMapping(value = "/dept/list", method = RequestMethod.GET) + public List list(); +} + +``` + + + + + +ÔÚ±àд·þÎñ°ó¶¨½Ó¿Úʱ£¬ÐèҪעÒâÒÔÏ 2 µã£º + +* ÔÚ @FeignClient ×¢½âÖУ¬value ÊôÐÔµÄȡֵΪ£º·þÎñÌṩÕߵķþÎñÃû£¬¼´·þÎñÌṩÕßÅäÖÃÎļþ£¨application.yml£©ÖÐ spring.application.name µÄȡֵ¡£ +* ½Ó¿ÚÖж¨ÒåµÄÿ¸ö·½·¨¶¼Óë·þÎñÌṩÕߣ¨¼´ micro-service-cloud-provider-dept-8001 µÈ£©ÖÐ Controller ¶¨ÒåµÄ·þÎñ·½·¨¶ÔÓ¦¡£ + +4\. ÔÚ net.biancheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ DeptController_Consumer µÄ Controller À࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.controller; + +import net.biancheng.c.entity.Dept; +import net.biancheng.c.service.DeptFeignService; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +public class DeptController_Consumer { + + @Resource + private DeptFeignService deptFeignService; + + @RequestMapping(value = "/consumer/dept/get/{id}") + public Dept get(@PathVariable("id") Integer id) { + return deptFeignService.get(id); + } + + @RequestMapping(value = "/consumer/dept/list") + public List list() { + return deptFeignService.list(); + } +} +``` + + + + + +5\. ÔÚÖ÷Æô¶¯ÀàÉÏÌí¼Ó @EnableFeignClients ×¢½â¿ªÆô OpenFeign ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients //¿ªÆô OpenFeign ¹¦ÄÜ +public class MicroServiceCloudConsumerDeptFeignApplication { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConsumerDeptFeignApplication.class, args); + } +} +``` + + + + + +Spring Cloud Ó¦ÓÃÔÚÆô¶¯Ê±£¬OpenFeign »áɨÃè±êÓÐ @FeignClient ×¢½âµÄ½Ó¿ÚÉú³É´úÀí£¬²¢×¢È˵½ Spring ÈÝÆ÷ÖС£ + +6\. ÒÀ´ÎÆô¶¯·þÎñ×¢²áÖÐÐļ¯Èº¡¢·þÎñÌṩÕßÒÔ¼° micro-service-cloud-consumer-dept-feign£¬Æô¶¯Íê³Éºó£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![OpenFeign ʵÏÖ·þÎñµ÷ÓÃ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1014296212-0.png) +ͼ1£ºOpenFeign ʵÏÖÔ¶³Ì·þÎñµ÷Óà + +7\. Á¬Ðø¶à´Î·ÃÎÊ¡°http://eureka7001.com/consumer/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![OpenFeign ĬÈϸºÔؾùºâ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1014294062-1.gif) +ͼ2£ºOpenFeign ¸ºÔؾùºâ + +´Óͼ 2 ¿ÉÒÔ¿´³ö£¬ÓÉÓÚ OpenFeign ¼¯³ÉÁË Ribbon£¬Òò´ËËüҲʵÏÖÁ˿ͻ§¶ËµÄ¸ºÔؾùºâ£¬ÆäĬÈϸºÔؾùºâ²ßÂÔΪÂÖѯ²ßÂÔ¡£ + +## OpenFeign ³¬Ê±¿ØÖÆ + +OpenFeign ¿Í»§¶ËµÄĬÈϳ¬Ê±Ê±¼äΪ 1 ÃëÖÓ£¬Èç¹û·þÎñ¶Ë´¦ÀíÇëÇóµÄʱ¼ä³¬¹ý 1 Ãë¾Í»á±¨´í¡£ÎªÁ˱ÜÃâÕâÑùµÄÇé¿ö£¬ÎÒÃÇÐèÒª¶Ô OpenFeign ¿Í»§¶ËµÄ³¬Ê±Ê±¼ä½øÐпØÖÆ¡£ + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´ÑÝʾ OpenFeign ÊÇÈçºÎ½øÐг¬Ê±¿ØÖƵġ£ + +1\. ÔÚËùÓеķþÎñÌṩÕߣ¨·þÎñ¶Ë£©µÄ DeptController ÖÐÌí¼ÓÒ»¸öÏìӦʱ¼äΪ 5 ÃëµÄ·þÎñ£¬´úÂëÈçÏ¡£ + + + + + +``` + +//³¬Ê±²âÊÔ,¸Ã·þÎñµÄÏìӦʱ¼äΪ 5 Ãë +@RequestMapping(value = "/dept/feign/timeout") +public String DeptFeignTimeout() { + //ÔÝÍ£ 5 Ãë + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return serverPort; +} + +``` + + + + + +2\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ DeptFeignService ½Ó¿ÚÖÐÌí¼ÓÒÔÏ´úÂ룬°ó¶¨·þÎñ¶Ë¸Õ¸ÕÌí¼ÓµÄ³¬Ê±·þÎñ¡£ + + + + + +``` + +@RequestMapping(value = "/dept/feign/timeout") +public String DeptFeignTimeout(); + +``` + + + + + +3\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ DeptController_Consumer Ìí¼ÓÒÔÏ´úÂë¡£ + + + + + +``` + +@RequestMapping(value = "/consumer/dept/feign/timeout") +public String DeptFeignTimeout() { + // openFeign-ribbon ¿Í»§¶ËÒ»°ãĬÈϵȴýÒ»ÃëÖÓ£¬³¬¹ý¸Ãʱ¼ä¾Í»á±¨´í + return deptFeignService.DeptFeignTimeout(); +} + +``` + + + + + +4\. ÖØÆôËùÓзþÎñÌṩÕߣ¬Ê¹ÓÃä¯ÀÀÆ÷ÒÀ´Î·ÃÎÊ¡°http://eureka7001.com:8001/dept/feign/timeout¡±¡¢¡°http://eureka7001.com:8002/dept/feign/timeout¡±ºÍ¡°http://eureka7001.com:8003/dept/feign/timeout¡±£¬È·±£ËùÓзþÎñÌṩÕßÌṩµÄ³¬Ê±·þÎñ¶¼ÄÜÕý³£Ê¹Óã¬ÈçÏÂͼ¡£ + +![·þÎñÌṩÕß³¬Ê±·þÎñ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10142a102-2.png) +ͼ3£º·þÎñÌṩÕߵij¬Ê±·þÎñ + +5\. ÖØÆô micro-service-cloud-consumer-dept-feign£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/feign/timeout¡±£¬½á¹ûÈçÏÂͼ¡£ + +![OpenFeign ³¬Ê±±¨´í](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1014293G1-3.png) +ͼ4£ºOpenFeign ³¬Ê±±¨´í + +6\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ application.yml ÖÐÌí¼ÓÒÔÏÂÅäÖ㬽«³¬Ê±Ê±¼äÉèÖÃΪ 6 Ãë¡£ + + + + + +``` + + +ribbon: + ReadTimeout: 6000 #½¨Á¢Á¬½ÓËùÓõÄʱ¼ä£¬ÊÊÓÃÓÚÍøÂç×´¿öÕý³£µÄÇé¿öÏ£¬Á½¶ËÁ½¶ËÁ¬½ÓËùÓõÄʱ¼ä + ConnectionTimeout: 6000 #½¨Á¢Á¬½Óºó£¬·þÎñÆ÷¶ÁÈ¡µ½¿ÉÓÃ×ÊÔ´µÄʱ¼ä + +``` + + + + + +> ×¢£ºÓÉÓÚ OpenFeign ¼¯³ÉÁË Ribbon £¬Æä·þÎñµ÷ÓÃÒÔ¼°¸ºÔؾùºâÔڵײ㶼ÊÇÒÀ¿¿ Ribbon ʵÏֵģ¬Òò´Ë OpenFeign ³¬Ê±¿ØÖÆÒ²ÊÇͨ¹ý Ribbon À´ÊµÏֵġ£ + +7\. ÔÙ´ÎÖØÆô micro-service-cloud-consumer-dept-feign£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/feign/timeout¡±£¬½á¹ûÈçÏÂͼ¡£ + +![OpenFeign ³¬Ê±¿ØÖÆ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10142942D-4.png) +ͼ5£ºOpenFeign ³¬Ê±¿ØÖÆ + +## OpenFeign ÈÕÖ¾ÔöÇ¿ + +OpenFeign ÌṩÁËÈÕÖ¾´òÓ¡¹¦ÄÜ£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÅäÖõ÷ÕûÈÕÖ¾¼¶±ð£¬À´Á˽âÇëÇóµÄϸ½Ú¡£ + +Feign Ϊÿһ¸ö FeignClient ¶¼ÌṩÁËÒ»¸ö feign.Logger ʵÀý£¬Í¨¹ýËü¿ÉÒÔ¶Ô OpenFeign ·þÎñ°ó¶¨½Ó¿ÚµÄµ÷ÓÃÇé¿ö½øÐÐ¼à¿Ø¡£ + +OpenFeign ÈÕÖ¾´òÓ¡¹¦ÄܵĿªÆô·½Ê½±È½Ï¼òµ¥£¬ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý½øÐÐÑÝʾ¡£ + +1\. ÔÚ micro-service-cloud-consumer-dept-feign µÄ application.yml ÖÐÅäÖÃÒÔÏÂÄÚÈÝ¡£ + + + + + +``` + + +logging: + level: + #feign ÈÕÖ¾ÒÔʲôÑùµÄ¼¶±ð¼à¿Ø¸Ã½Ó¿Ú + net.biancheng.c.service.DeptFeignService: debug +``` + + + + + +ÒÔÉÏÅäÖÃ˵Ã÷ÈçÏ£º + +* net.biancheng.c.service.DeptFeignService ÊÇ¿ªÆô @FeignClient ×¢½âµÄ½Ó¿Ú£¨¼´·þÎñ°ó¶¨½Ó¿Ú£©µÄÍêÕûÀàÃû¡£Ò²¿ÉÒÔÖ»ÅäÖò¿·Ö·¾¶£¬±íʾ¼à¿Ø¸Ã·¾¶ÏµÄËùÓзþÎñ°ó¶¨½Ó¿Ú +* debug£º±íʾ¼àÌý¸Ã½Ó¿ÚµÄÈÕÖ¾¼¶±ð¡£ + +ÒÔÉÏÅäÖõĺ¬Òå¾ÍÊÇ£¬OpenFeign ÒÔ debug ¼¶±ð¼à¿Ø net.biancheng.c.service.DeptFeignService ½Ó¿Ú¡£ + +2. ÔÚ net.biancheng.c. config °üÏ´´½¨Ò»¸öÃûΪ ConfigBean µÄÅäÖÃÀ࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c.config; +import feign.Logger; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +@Configuration +public class ConfigBean { + /** + * OpenFeign ÈÕÖ¾ÔöÇ¿ + * ÅäÖà OpenFeign ¼Ç¼ÄÄЩÄÚÈÝ + */ + @Bean + Logger.Level feginLoggerLevel() { + return Logger.Level.FULL; + } +} + +``` + + + + + +¸ÃÅäÖõÄ×÷ÓÃÊÇͨ¹ýÅäÖÃµÄ Logger.Level ¶ÔÏó¸æËß OpenFeign ¼Ç¼ÄÄЩÈÕÖ¾ÄÚÈÝ¡£ + +Logger.Level µÄ¾ßÌå¼¶±ðÈçÏ£º + +* NONE£º²»¼Ç¼ÈκÎÐÅÏ¢¡£ +* BASIC£º½ö¼Ç¼ÇëÇó·½·¨¡¢URL ÒÔ¼°ÏìӦ״̬ÂëºÍÖ´ÐÐʱ¼ä¡£ +* HEADERS£º³ýÁ˼Ǽ BASIC ¼¶±ðµÄÐÅÏ¢Í⣬»¹»á¼Ç¼ÇëÇóºÍÏìÓ¦µÄÍ·ÐÅÏ¢¡£ +* FULL£º¼Ç¼ËùÓÐÇëÇóÓëÏìÓ¦µÄÃ÷ϸ£¬°üÀ¨Í·ÐÅÏ¢¡¢ÇëÇóÌå¡¢ÔªÊý¾ÝµÈµÈ¡£ + +3\. ÖØÆô micro-service-cloud-consumer-dept-feign£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/list¡±£¬¿ØÖÆÌ¨Êä³öÈçÏ¡£ + +``` +2021-10-12 14:33:07.408 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] ---> GET http://MICROSERVICECLOUDPROVIDERDEPT/dept/list HTTP/1.1 +2021-10-12 14:33:07.408 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] ---> END HTTP (0-byte body) +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] <--- HTTP/1.1 200 (574ms) +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] connection: keep-alive +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] content-type: application/json +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] date: Tue, 12 Oct 2021 06:33:07 GMT +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] keep-alive: timeout=60 +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] transfer-encoding: chunked +2021-10-12 14:33:07.983 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] +2021-10-12 14:33:07.991 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] [{"deptNo":1,"deptName":"¿ª·¢²¿","dbSource":"bianchengbang_jdbc"},{"deptNo":2,"deptName":"ÈËʲ¿","dbSource":"bianchengbang_jdbc"},{"deptNo":3,"deptName":"²ÆÎñ²¿","dbSource":"bianchengbang_jdbc"},{"deptNo":4,"deptName":"Êг¡²¿","dbSource":"bianchengbang_jdbc"},{"deptNo":5,"deptName":"ÔËά²¿","dbSource":"bianchengbang_jdbc"}] +2021-10-12 14:33:07.991 DEBUG 13388 --- [p-nio-80-exec-2] n.biancheng.c.service.DeptFeignService : [DeptFeignService#list] <--- END HTTP (341-byte body)``` +``` +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" new file mode 100644 index 0000000..74eff0a --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudRibbon.md" @@ -0,0 +1,665 @@ +Spring Cloud Ribbon ÊÇÒ»Ì×»ùÓÚ Netflix Ribbon ʵÏֵĿͻ§¶Ë¸ºÔؾùºâºÍ·þÎñµ÷Óù¤¾ß¡£ + +Netflix Ribbon ÊÇ Netflix ¹«Ë¾·¢²¼µÄ¿ªÔ´×é¼þ£¬ÆäÖ÷Òª¹¦ÄÜÊÇÌṩ¿Í»§¶ËµÄ¸ºÔؾùºâËã·¨ºÍ·þÎñµ÷Óá£Spring Cloud ½«ÆäÓë Netflix ÖÐµÄÆäËû¿ªÔ´·þÎñ×é¼þ£¨ÀýÈç Eureka¡¢Feign ÒÔ¼° Hystrix µÈ£©Ò»ÆðÕûºÏ½ø Spring Cloud Netflix Ä£¿éÖУ¬ÕûºÏºóÈ«³ÆÎª Spring Cloud Netflix Ribbon¡£ + +Ribbon ÊÇ Spring Cloud Netflix Ä£¿éµÄ×ÓÄ£¿é£¬ËüÊÇ Spring Cloud ¶Ô Netflix Ribbon µÄ¶þ´Î·â×°¡£Í¨¹ýËü£¬ÎÒÃÇ¿ÉÒÔ½«ÃæÏò·þÎñµÄ REST Ä£°å£¨RestTemplate£©ÇëÇóת»»Îª¿Í»§¶Ë¸ºÔؾùºâµÄ·þÎñµ÷Óᣠ+ +Ribbon ÊÇ Spring Cloud ÌåϵÖÐ×îºËÐÄ¡¢×îÖØÒªµÄ×é¼þÖ®Ò»¡£ËüËäȻֻÊÇÒ»¸ö¹¤¾ßÀàÐ͵Ŀò¼Ü£¬²¢²»Ïñ Eureka Server£¨·þÎñ×¢²áÖÐÐÄ£©ÄÇÑùÐèÒª¶ÀÁ¢²¿Ê𣬵«Ëü¼¸ºõ´æÔÚÓÚÿһ¸öʹÓà Spring Cloud ¹¹½¨µÄ΢·þÎñÖС£ + +Spring Cloud ΢·þÎñÖ®¼äµÄµ÷Óã¬API Íø¹ØµÄÇëÇóת·¢µÈÄÚÈÝ£¬Êµ¼ÊÉ϶¼ÊÇͨ¹ý Spring Cloud Ribbon À´ÊµÏֵ쬰üÀ¨ºóÐøÎÒÃÇÒª½éÉÜµÄ [OpenFeign](http://c.biancheng.net/springcloud/open-feign.html) Ò²ÊÇ»ùÓÚËüʵÏֵġ£ + +## ¸ºÔؾùºâ + +ÔÚÈκÎÒ»¸öϵͳÖУ¬¸ºÔؾùºâ¶¼ÊÇÒ»¸öÊ®·ÖÖØÒªÇÒ²»µÃ²»È¥ÊµÊ©µÄÄÚÈÝ£¬ËüÊÇϵͳ´¦Àí¸ß²¢·¢¡¢»º½âÍøÂçѹÁ¦ºÍ·þÎñ¶ËÀ©ÈݵÄÖØÒªÊÖ¶ÎÖ®Ò»¡£ + +¸ºÔؾùºâ£¨Load Balance£© £¬¼òµ¥µã˵¾ÍÊǽ«Óû§µÄÇëÇóƽ̯·ÖÅäµ½¶à¸ö·þÎñÆ÷ÉÏÔËÐУ¬ÒÔ´ïµ½À©Õ¹·þÎñÆ÷´ø¿í¡¢ÔöÇ¿Êý¾Ý´¦ÀíÄÜÁ¦¡¢Ôö¼ÓÍÌÍÂÁ¿¡¢Ìá¸ßÍøÂçµÄ¿ÉÓÃÐÔºÍÁé»îÐÔµÄÄ¿µÄ¡£ + +³£¼ûµÄ¸ºÔؾùºâ·½Ê½ÓÐÁ½ÖÖ£º + +* ·þÎñ¶Ë¸ºÔؾùºâ +* ¿Í»§¶Ë¸ºÔؾùºâ + +#### ·þÎñ¶Ë¸ºÔؾùºâ + +·þÎñ¶Ë¸ºÔؾùºâÊÇ×î³£¼ûµÄ¸ºÔؾùºâ·½Ê½£¬Æä¹¤×÷Ô­ÀíÈçÏÂͼ¡£ + +![·þÎñ¶Ë¸ºÔؾùºâ¹¤×÷Ô­Àí](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10122164F-0.png) +ͼ1£º·þÎñ¶Ë¸ºÔؾùºâ¹¤×÷Ô­Àí + +·þÎñ¶Ë¸ºÔؾùºâÊÇÔÚ¿Í»§¶ËºÍ·þÎñ¶ËÖ®¼ä½¨Á¢Ò»¸ö¶ÀÁ¢µÄ¸ºÔؾùºâ·þÎñÆ÷£¬¸Ã·þÎñÆ÷¼È¿ÉÒÔÊÇÓ²¼þÉ豸£¨ÀýÈç F5£©£¬Ò²¿ÉÒÔÊÇÈí¼þ£¨ÀýÈç Nginx£©¡£Õâ¸ö¸ºÔؾùºâ·þÎñÆ÷ά»¤ÁËÒ»·Ý¿ÉÓ÷þÎñ¶ËÇåµ¥£¬È»ºóͨ¹ýÐÄÌø»úÖÆÀ´É¾³ý¹ÊÕϵķþÎñ¶Ë½Úµã£¬ÒÔ±£Ö¤Çåµ¥ÖеÄËùÓзþÎñ½Úµã¶¼ÊÇ¿ÉÒÔÕý³£·ÃÎʵġ£ + +µ±¿Í»§¶Ë·¢ËÍÇëÇóʱ£¬¸ÃÇëÇó²»»áÖ±½Ó·¢Ë͵½·þÎñ¶Ë½øÐд¦Àí£¬¶øÊÇÈ«²¿½»¸ø¸ºÔؾùºâ·þÎñÆ÷£¬ÓɸºÔؾùºâ·þÎñÆ÷°´ÕÕijÖÖËã·¨£¨ÀýÈçÂÖѯ¡¢Ëæ»úµÈ£©£¬´ÓÆäά»¤µÄ¿ÉÓ÷þÎñÇåµ¥ÖÐÑ¡ÔñÒ»¸ö·þÎñ¶Ë£¬È»ºó½øÐÐת·¢¡£ + +·þÎñ¶Ë¸ºÔؾùºâ¾ßÓÐÒÔÏÂÌØµã£º + +* ÐèÒª½¨Á¢Ò»¸ö¶ÀÁ¢µÄ¸ºÔؾùºâ·þÎñÆ÷¡£ +* ¸ºÔؾùºâÊÇÔÚ¿Í»§¶Ë·¢ËÍÇëÇóºó½øÐеģ¬Òò´Ë¿Í»§¶Ë²¢²»ÖªµÀµ½µ×ÊÇÄĸö·þÎñ¶ËÌṩµÄ·þÎñ¡£ +* ¿ÉÓ÷þÎñ¶ËÇåµ¥´æ´¢ÔÚ¸ºÔؾùºâ·þÎñÆ÷ÉÏ¡£ + +#### ¿Í»§¶Ë¸ºÔؾùºâ + +Ïà½ÏÓÚ·þÎñ¶Ë¸ºÔؾùºâ£¬¿Í»§¶Ë·þÎñÔÚ¾ùºâÔòÊÇÒ»¸ö±È½ÏСÖڵĸÅÄî¡£ + +¿Í»§¶Ë¸ºÔؾùºâµÄ¹¤×÷Ô­ÀíÈçÏÂͼ¡£ + +![¿Í»§¶Ë¸ºÔؾùºâÔ­Àí](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1012212Q4-1.png) + +ͼ2£º¿Í»§¶Ë¸ºÔؾùºâ¹¤×÷Ô­Àí + + +¿Í»§¶Ë¸ºÔؾùºâÊǽ«¸ºÔؾùºâÂß¼­ÒÔ´úÂëµÄÐÎʽ·â×°µ½¿Í»§¶ËÉÏ£¬¼´¸ºÔؾùºâÆ÷λÓÚ¿Í»§¶Ë¡£¿Í»§¶Ëͨ¹ý·þÎñ×¢²áÖÐÐÄ£¨ÀýÈç Eureka Server£©»ñÈ¡µ½Ò»·Ý·þÎñ¶ËÌṩµÄ¿ÉÓ÷þÎñÇåµ¥¡£ÓÐÁË·þÎñÇåµ¥ºó£¬¸ºÔؾùºâÆ÷»áÔÚ¿Í»§¶Ë·¢ËÍÇëÇóǰͨ¹ý¸ºÔؾùºâË㷨ѡÔñÒ»¸ö·þÎñ¶ËʵÀýÔÙ½øÐзÃÎÊ£¬ÒÔ´ïµ½¸ºÔؾùºâµÄÄ¿µÄ£» + +¿Í»§¶Ë¸ºÔؾùºâÒ²ÐèÒªÐÄÌø»úÖÆÈ¥Î¬»¤·þÎñ¶ËÇåµ¥µÄÓÐЧÐÔ£¬Õâ¸ö¹ý³ÌÐèÒªÅäºÏ·þÎñ×¢²áÖÐÐÄÒ»ÆðÍê³É¡£ + +¿Í»§¶Ë¸ºÔؾùºâ¾ßÓÐÒÔÏÂÌØµã£º + +* ¸ºÔؾùºâÆ÷λÓÚ¿Í»§¶Ë£¬²»ÐèÒªµ¥¶À´î½¨Ò»¸ö¸ºÔؾùºâ·þÎñÆ÷¡£ +* ¸ºÔؾùºâÊÇÔÚ¿Í»§¶Ë·¢ËÍÇëÇóǰ½øÐеģ¬Òò´Ë¿Í»§¶ËÇå³þµØÖªµÀÊÇÄĸö·þÎñ¶ËÌṩµÄ·þÎñ¡£ +* ¿Í»§¶Ë¶¼Î¬»¤ÁËÒ»·Ý¿ÉÓ÷þÎñÇåµ¥£¬¶øÕâ·ÝÇåµ¥¶¼ÊÇ´Ó·þÎñ×¢²áÖÐÐÄ»ñÈ¡µÄ¡£ + +Ribbon ¾ÍÊÇÒ»¸ö»ùÓÚ HTTP ºÍ TCP µÄ¿Í»§¶Ë¸ºÔؾùºâÆ÷£¬µ±ÎÒÃǽ« Ribbon ºÍ Eureka Ò»ÆðʹÓÃʱ£¬Ribbon »á´Ó Eureka Server£¨·þÎñ×¢²áÖÐÐÄ£©ÖлñÈ¡·þÎñ¶ËÁÐ±í£¬È»ºóͨ¹ý¸ºÔؾùºâ²ßÂÔ½«ÇëÇó·Ö̯¸ø¶à¸ö·þÎñÌṩÕߣ¬´Ó¶ø´ïµ½¸ºÔؾùºâµÄÄ¿µÄ¡£ + +#### ·þÎñ¶Ë¸ºÔؾùºâ VS ¿Í»§¶Ë¸ºÔؾùºâ + +ÏÂÃæÎÒÃǾÍÀ´¶Ô±ÈÏ£¬·þÎñ¶Ë¸ºÔؾùºâºÍ¿Í»§¶Ë¸ºÔؾùºâµ½µ×ÓÐÊ²Ã´Çø±ð£¬ÈçÏÂ±í¡£ + +| ²»Í¬µã | ·þÎñ¶Ë¸ºÔؾùºâ | ¿Í»§¶Ë¸ºÔؾùºâ | +| ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| ÊÇ·ñÐèÒª½¨Á¢¸ºÔؾùºâ·þÎñÆ÷ | ÐèÒªÔÚ¿Í»§¶ËºÍ·þÎñ¶ËÖ®¼ä½¨Á¢Ò»¸ö¶ÀÁ¢µÄ¸ºÔؾùºâ·þÎñÆ÷¡£ | ½«¸ºÔؾùºâµÄÂß¼­ÒÔ´úÂëµÄÐÎʽ·â×°µ½¿Í»§¶ËÉÏ£¬Òò´Ë²»ÐèÒªµ¥¶À½¨Á¢¸ºÔؾùºâ·þÎñÆ÷¡£ | +| ÊÇ·ñÐèÒª·þÎñ×¢²áÖÐÐÄ | ²»ÐèÒª·þÎñ×¢²áÖÐÐÄ¡£ | ÐèÒª·þÎñ×¢²áÖÐÐÄ¡£ÔÚ¿Í»§¶Ë¸ºÔؾùºâÖУ¬ËùÓеĿͻ§¶ËºÍ·þÎñ¶Ë¶¼ÐèÒª½«ÆäÌṩµÄ·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐÄÉÏ¡£ | +| ¿ÉÓ÷þÎñÇåµ¥´æ´¢µÄλÖà | ¿ÉÓ÷þÎñÇåµ¥´æ´¢ÔÚλÓÚ¿Í»§¶ËÓë·þÎñÆ÷Ö®¼äµÄ¸ºÔؾùºâ·þÎñÆ÷ÉÏ¡£ | ËùÓеĿͻ§¶Ë¶¼Î¬»¤ÁËÒ»·Ý¿ÉÓ÷þÎñÇåµ¥£¬ÕâЩÇåµ¥¶¼ÊÇ´Ó·þÎñ×¢²áÖÐÐÄ»ñÈ¡µÄ¡£ | +| ¸ºÔؾùºâµÄʱ»ú | ÏȽ«ÇëÇó·¢Ë͵½¸ºÔؾùºâ·þÎñÆ÷£¬È»ºóÓɸºÔؾùºâ·þÎñÆ÷ͨ¹ý¸ºÔؾùºâËã·¨£¬ÔÚ¶à¸ö·þÎñ¶ËÖ®¼äÑ¡ÔñÒ»¸ö½øÐзÃÎÊ£»¼´ÔÚ·þÎñÆ÷¶ËÔÙ½øÐиºÔؾùºâËã·¨·ÖÅä¡£¼òµ¥µã˵¾ÍÊÇ£¬ÏÈ·¢ËÍÇëÇó£¬ÔÙ½øÐиºÔؾùºâ¡£ | ÔÚ·¢ËÍÇëÇóǰ£¬ÓÉλÓÚ¿Í»§¶ËµÄ·þÎñ¸ºÔؾùºâÆ÷£¨ÀýÈç Ribbon£©Í¨¹ý¸ºÔؾùºâË㷨ѡÔñÒ»¸ö·þÎñÆ÷£¬È»ºó½øÐзÃÎÊ¡£¼òµ¥µã˵¾ÍÊÇ£¬ÏȽøÐиºÔؾùºâ£¬ÔÙ·¢ËÍÇëÇó¡£ | +| ¿Í»§¶ËÊÇ·ñÁ˽â·þÎñÌṩ·½ÐÅÏ¢ | ÓÉÓÚ¸ºÔؾùºâÊÇÔÚ¿Í»§¶Ë·¢ËÍÇëÇóºó½øÐеģ¬Òò´Ë¿Í»§¶Ë²¢²»ÖªµÀµ½µ×ÊÇÄĸö·þÎñ¶ËÌṩµÄ·þÎñ¡£ | ¸ºÔؾùºâÊÇÔÚ¿Í»§¶Ë·¢ËÍÇëÇóǰ½øÐеģ¬Òò´Ë¿Í»§¶ËÇå³þµÄÖªµÀÊÇÄĸö·þÎñ¶ËÌṩµÄ·þÎñ¡£ | + +## Ribbon ʵÏÖ·þÎñµ÷Óà + +Ribbon ¿ÉÒÔÓë RestTemplate£¨Rest Ä£°å£©ÅäºÏʹÓã¬ÒÔʵÏÖ΢·þÎñÖ®¼äµÄµ÷Óᣠ+ +RestTemplate ÊÇ Spring ¼Ò×åÖеÄÒ»¸öÓÃÓÚÏû·ÑµÚÈý·½ REST ·þÎñµÄÇëÇó¿ò¼Ü¡£RestTemplate ʵÏÖÁË¶Ô HTTP ÇëÇóµÄ·â×°£¬ÌṩÁËÒ»Ì×Ä£°å»¯µÄ·þÎñµ÷Ó÷½·¨¡£Í¨¹ýËü£¬Spring Ó¦ÓÿÉÒԺܷ½±ãµØ¶Ô¸÷ÖÖÀàÐ굀 HTTP ÇëÇó½øÐзÃÎÊ¡£ + +RestTemplate Õë¶Ô¸÷ÖÖÀàÐ굀 HTTP ÇëÇó¶¼ÌṩÁËÏàÓ¦µÄ·½·¨½øÐд¦Àí£¬ÀýÈç HEAD¡¢GET¡¢POST¡¢PUT¡¢DELETE µÈÀàÐ굀 HTTP ÇëÇ󣬷ֱð¶ÔÓ¦ RestTemplate ÖÐµÄ headForHeaders()¡¢getForObject()¡¢postForObject()¡¢put() ÒÔ¼° delete() ·½·¨¡£ + +ÏÂÃæÎÒÃÇͨ¹ýÒ»¸ö¼òµ¥µÄʵÀý£¬À´ÑÝʾ Ribbon ÊÇÈçºÎʵÏÖ·þÎñµ÷Óõġ£ + +1\. ÔÚÖ÷¹¤³Ì spring-cloud-demo2 Ï£¬´´½¨Ò»¸öÃûΪ micro-service-cloud-consumer-dept-80 µÄ΢·þÎñ£¬²¢ÔÚÆä pom.xml ÖÐÒýÈëËùÐèµÄÒÀÀµ£¬´úÂëÈçÏ¡£ + + + + + +``` + + + 4.0.0 + + + spring-cloud-demo2 + net.biancheng.c + 0.0.1-SNAPSHOT + + + net.biancheng.c + micro-service-cloud-consumer-dept-80 + 0.0.1-SNAPSHOT + micro-service-cloud-consumer-dept-80 + Demo project for Spring Boot + + 1.8 + + + + + + net.biancheng.c + micro-service-cloud-api + ${project.version} + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + +``` + + + + + +2. ÔÚÀà·¾¶£¨¼´ /resource Ŀ¼£©Ï£¬Ð½¨Ò»¸öÅäÖÃÎļþ application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + + +``` +server: + port: 80 #¶Ë¿ÚºÅ + +############################################# Spring Cloud Ribbon ¸ºÔؾùºâÅäÖÃ########################## +eureka: + client: + register-with-eureka: false #±¾Î¢·þÎñΪ·þÎñÏû·ÑÕߣ¬²»ÐèÒª½«×Ô¼º×¢²áµ½·þÎñ×¢²áÖÐÐÄ + fetch-registry: true #±¾Î¢·þÎñΪ·þÎñÏû·ÑÕߣ¬ÐèÒªµ½·þÎñ×¢²áÖÐÐÄËÑË÷·þÎñ + service-url: + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #·þÎñ×¢²áÖÐÐļ¯Èº +``` + + + + + +3\. ÔÚ net.biancheng.c.config °üÏ£¬´´½¨Ò»¸öÃûΪ ConfigBean µÄÅäÖÃÀ࣬½« RestTemplate ×¢Èëµ½ÈÝÆ÷ÖУ¬´úÂëÈçÏ¡£ + + + + + +``` + + +package net.biancheng.c.config; + +import com.netflix.loadbalancer.IRule; + +import com.netflix.loadbalancer.RetryRule; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; +// ÅäÖÃÀà×¢½â +@Configuration +public class ConfigBean { + + @Bean //½« RestTemplate ×¢Èëµ½ÈÝÆ÷ÖÐ + @LoadBalanced //ÔÚ¿Í»§¶ËʹÓà RestTemplate ÇëÇó·þÎñ¶Ëʱ£¬¿ªÆô¸ºÔؾùºâ£¨Ribbon£© + public RestTemplate getRestTemplate() { + return new RestTemplate(); + } +} + +``` + + + + + +4\. ÔÚ net.biancheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ DeptController_Consumer µÄ Controller£¬¸Ã Controller Öж¨ÒåµÄÇëÇóÓÃÓÚµ÷Ó÷þÎñ¶ËÌṩµÄ·þÎñ£¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.c.controller; + +import net.biancheng.c.entity.Dept; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import java.util.List; + +@RestController +public class DeptController_Consumer { + //private static final String REST_URL_PROVIDER_PREFIX = "http://localhost:8001/"; ÕâÖÖ·½Ê½ÊÇÖ±µ÷Ó÷þÎñ·½µÄ·½·¨£¬¸ù±¾Ã»ÓÐÓõ½ Spring Cloud + + //ÃæÏò΢·þÎñ±à³Ì£¬¼´Í¨¹ý΢·þÎñµÄÃû³ÆÀ´»ñÈ¡µ÷ÓõØÖ· + private static final String REST_URL_PROVIDER_PREFIX = "http://MICROSERVICECLOUDPROVIDERDEPT"; // ʹÓÃ×¢²áµ½ Spring Cloud Eureka ·þÎñ×¢²áÖÐÐÄÖеķþÎñ£¬¼´ application.name + + @Autowired + private RestTemplate restTemplate; //RestTemplate ÊÇÒ»ÖÖ¼òµ¥±ã½ÝµÄ·ÃÎÊ restful ·þÎñÄ£°åÀ࣬ÊÇ Spring ÌṩµÄÓÃÓÚ·ÃÎÊ Rest ·þÎñµÄ¿Í»§¶ËÄ£°å¹¤¾ß¼¯£¬ÌṩÁ˶àÖÖ±ã½Ý·ÃÎÊÔ¶³Ì HTTP ·þÎñµÄ·½·¨ + + //»ñȡָ¶¨²¿ÃÅÐÅÏ¢ + @RequestMapping(value = "/consumer/dept/get/{id}") + public Dept get(@PathVariable("id") Integer id) { + return restTemplate.getForObject(REST_URL_PROVIDER_PREFIX + "/dept/get/" + id, Dept.class); + } + //»ñÈ¡²¿ÃÅÁбí + @RequestMapping(value = "/consumer/dept/list") + public List list() { + return restTemplate.getForObject(REST_URL_PROVIDER_PREFIX + "/dept/list", List.class); + } +} +``` + + + + + +5\. ÔÚ micro-service-cloud-consumer-dept-80 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableEurekaClient ×¢½âÀ´¿ªÆô Eureka ¿Í»§¶Ë¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.c; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient +public class MicroServiceCloudConsumerDept80Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConsumerDept80Application.class, args); + } +} + +``` + + + + + +6\. ÒÀ´ÎÆô¶¯·þÎñ×¢²áÖÐÐÄ micro-service-cloud-eureka-7001¡¢·þÎñÌṩÕß micro-service-cloud-provider-dept-8001 ÒÔ¼°·þÎñÏû·ÑÕß micro-service-cloud-consumer-dept-80¡£ + +7\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com:80/consumer/dept/list¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Ribbon ʵÏÖ·þÎñµ÷ÓÃ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/101221CG-2.png) +ͼ3£ºRibbon ʵÏÖ·þÎñµ÷Óà + +## Ribbon ʵÏÖ¸ºÔؾùºâ + +Ribbon ÊÇÒ»¸ö¿Í»§¶ËµÄ¸ºÔؾùºâÆ÷£¬Ëü¿ÉÒÔÓë Eureka ÅäºÏʹÓÃÇáËɵØÊµÏÖ¿Í»§¶ËµÄ¸ºÔؾùºâ¡£Ribbon »áÏÈ´Ó Eureka Server£¨·þÎñ×¢²áÖÐÐÄ£©È¥»ñÈ¡·þÎñ¶ËÁÐ±í£¬È»ºóͨ¹ý¸ºÔؾùºâ²ßÂÔ½«ÇëÇó·Ö̯¸ø¶à¸ö·þÎñ¶Ë£¬´Ó¶ø´ïµ½¸ºÔؾùºâµÄÄ¿µÄ¡£ + +Spring Cloud Ribbon ÌṩÁËÒ»¸ö IRule ½Ó¿Ú£¬¸Ã½Ó¿ÚÖ÷ÒªÓÃÀ´¶¨Òå¸ºÔØ¾ùºâ²ßÂÔ£¬ËüÓÐ 7 ¸öĬÈÏʵÏÖÀ࣬ÿһ¸öʵÏÖÀà¶¼ÊÇÒ»ÖÖ¸ºÔؾùºâ²ßÂÔ¡£ + +| ÐòºÅ | ʵÏÖÀà | ¸ºÔؾùºâ²ßÂÔ | +| ---- | ------------------------- | ------------------------------------------------------------ | +| 1 | RoundRobinRule | °´ÕÕÏßÐÔÂÖѯ²ßÂÔ£¬¼´°´ÕÕÒ»¶¨µÄ˳ÐòÒÀ´Îѡȡ·þÎñʵÀý | +| 2 | RandomRule | Ëæ»úѡȡһ¸ö·þÎñʵÀý | +| 3 | RetryRule | °´ÕÕ RoundRobinRule£¨ÂÖѯ£©µÄ²ßÂÔÀ´»ñÈ¡·þÎñ£¬Èç¹û»ñÈ¡µÄ·þÎñʵÀýΪ null »òÒѾ­Ê§Ð§£¬ÔòÔÚÖ¸¶¨µÄʱ¼äÖ®ÄÚ²»¶ÏµØ½øÐÐÖØÊÔ£¨ÖØÊÔʱ»ñÈ¡·þÎñµÄ²ßÂÔ»¹ÊÇ RoundRobinRule Öж¨ÒåµÄ²ßÂÔ£©£¬Èç¹û³¬¹ýÖ¸¶¨Ê±¼äÒÀȻû»ñÈ¡µ½·þÎñʵÀýÔò·µ»Ø null ¡£ | +| 4 | WeightedResponseTimeRule | WeightedResponseTimeRule ÊÇ RoundRobinRule µÄÒ»¸ö×ÓÀ࣬Ëü¶Ô RoundRobinRule µÄ¹¦ÄܽøÐÐÁËÀ©Õ¹¡£¸ù¾Ýƽ¾ùÏìӦʱ¼ä£¬À´¼ÆËãËùÓзþÎñʵÀýµÄÈ¨ÖØ£¬ÏìӦʱ¼äÔ½¶ÌµÄ·þÎñʵÀýÈ¨ÖØÔ½¸ß£¬±»Ñ¡ÖеĸÅÂÊÔ½´ó¡£¸ÕÆô¶¯Ê±£¬Èç¹ûͳ¼ÆÐÅÏ¢²»×㣬ÔòʹÓÃÏßÐÔÂÖѯ²ßÂÔ£¬µÈÐÅÏ¢×㹻ʱ£¬ÔÙÇл»µ½ WeightedResponseTimeRule¡£ | +| 5 | BestAvailableRule | ¼Ì³Ð×Ô ClientConfigEnabledRoundRobinRule¡£ÏȹýÂ˵ã¹ÊÕÏ»òʧЧµÄ·þÎñʵÀý£¬È»ºóÔÙÑ¡Ôñ²¢·¢Á¿×îСµÄ·þÎñʵÀý¡£ | +| 6 | AvailabilityFilteringRule | ÏȹýÂ˵ô¹ÊÕÏ»òʧЧµÄ·þÎñʵÀý£¬È»ºóÔÙÑ¡Ôñ²¢·¢Á¿½ÏСµÄ·þÎñʵÀý¡£ | +| 7 | ZoneAvoidanceRule | ĬÈϵĸºÔؾùºâ²ßÂÔ£¬×ÛºÏÅжϷþÎñËùÔÚÇøÓò£¨zone£©µÄÐÔÄܺͷþÎñ£¨server£©µÄ¿ÉÓÃÐÔ£¬À´Ñ¡Ôñ·þÎñʵÀý¡£ÔÚûÓÐÇøÓòµÄ»·¾³Ï£¬¸Ã²ßÂÔÓëÂÖѯ£¨RandomRule£©²ßÂÔÀàËÆ¡£ | + +ÏÂÃæÎÒÃǾÍÀ´Í¨¹ýÒ»¸öʵÀýÀ´Ñé֤ϣ¬Ribbon ĬÈÏÊÇʹÓÃʲô²ßÂÔѡȡ·þÎñʵÀýµÄ¡£ + +1. ÔÚ MySQL Êý¾Ý¿âÖÐÖ´ÐÐÒÔÏ SQL Óï¾ä£¬×¼±¸²âÊÔÊý¾Ý¡£ + +``` +DROP DATABASE IF EXISTS spring_cloud_db2; +CREATE DATABASE spring_cloud_db2 CHARACTER SET UTF8; + +USE spring_cloud_db2; + +DROP TABLE IF EXISTS `dept`; +CREATE TABLE `dept` ( + `dept_no` int NOT NULL AUTO_INCREMENT, + `dept_name` varchar(255) DEFAULT NULL, + `db_source` varchar(255) DEFAULT NULL, + PRIMARY KEY (`dept_no`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +INSERT INTO `dept` VALUES ('1', '¿ª·¢²¿', DATABASE()); +INSERT INTO `dept` VALUES ('2', 'ÈËʲ¿', DATABASE()); +INSERT INTO `dept` VALUES ('3', '²ÆÎñ²¿', DATABASE()); +INSERT INTO `dept` VALUES ('4', 'Êг¡²¿', DATABASE()); +INSERT INTO `dept` VALUES ('5', 'ÔËά²¿', DATABASE()); + +############################################################################################# + +DROP DATABASE IF EXISTS spring_cloud_db3; + +CREATE DATABASE spring_cloud_db3 CHARACTER SET UTF8; + +USE spring_cloud_db3; + +DROP TABLE IF EXISTS `dept`; +CREATE TABLE `dept` ( + `dept_no` int NOT NULL AUTO_INCREMENT, + `dept_name` varchar(255) DEFAULT NULL, + `db_source` varchar(255) DEFAULT NULL, + PRIMARY KEY (`dept_no`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +INSERT INTO `dept` VALUES ('1', '¿ª·¢²¿', DATABASE()); +INSERT INTO `dept` VALUES ('2', 'ÈËʲ¿', DATABASE()); +INSERT INTO `dept` VALUES ('3', '²ÆÎñ²¿', DATABASE()); +INSERT INTO `dept` VALUES ('4', 'Êг¡²¿', DATABASE()); +INSERT INTO `dept` VALUES ('5', 'ÔËά²¿', DATABASE());``` + +2\. ²Î¿¼ micro-service-cloud-provider-dept-8001£¬ÔÙ´´½¨Á½¸ö΢·þÎñ Moudle £ºmicro-service-cloud-provider-dept-8002 ºÍ micro-service-cloud-provider-dept-8003¡£ + +3\. ÔÚ micro-service-cloud-provider-dept-8002 ÖÐ application.yml ÖУ¬Ð޸Ķ˿ںš¢Êý¾Ý¿âÁ¬½ÓÐÅÏ¢ÒÔ¼°×Ô¶¨Òå·þÎñÃû³ÆÐÅÏ¢£¨eureka.instance.instance-id£©£¬Ð޸ĵÄÅäÖÃÈçÏ¡£ +``` + + + + +``` + +server: + port: 8002 #¶Ë¿ÚºÅÐÞ¸ÄΪ 8002 + +spring: + application: + name: microServiceCloudProviderDept #΢·þÎñÃû³Æ£¬²»×öÐ޸ģ¬Óë micro-service-cloud-provider-dept-8001 µÄÅäÖñ£³ÖÒ»Ö + + datasource: + username: root #Êý¾Ý¿âµÇ½Óû§Ãû + password: root #Êý¾Ý¿âµÇ½ÃÜÂë + url: jdbc:mysql://127.0.0.1:3306/spring_cloud_db2 #Êý¾Ý¿âurl + driver-class-name: com.mysql.jdbc.Driver #Êý¾Ý¿âÇý¶¯ + +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + #defaultZone: http://eureka7001:7001/eureka #Õâ¸öµØÖ·ÊÇ 7001×¢²áÖÐÐÄÔÚ application.yml Öб©Â¶³öÀ´¶î×¢²áµØÖ· £¨µ¥»ú°æ£© + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº + instance: + instance-id: spring-cloud-provider-8002 #ÐÞ¸Ä×Ô¶¨ÒåµÄ·þÎñÃû³ÆÐÅÏ¢ + prefer-ip-address: true #ÏÔʾ·ÃÎÊ·¾¶µÄ ip µØÖ· + +``` + + + + + +4\. ÔÚ micro-service-cloud-provider-dept-8003 ÖÐ application.yml ÖУ¬Ð޸Ķ˿ںÅÒÔ¼°Êý¾Ý¿âÁ¬½ÓÐÅÏ¢£¬Ð޸ĵÄÅäÖÃÈçÏ¡£ + + + + + +``` + +server: + port: 8003 #¶Ë¿ÚºÅÐÞ¸ÄΪ 8003 + +spring: + application: + name: microServiceCloudProviderDept #΢·þÎñÃû³Æ£¬²»×öÐ޸ģ¬Óë micro-service-cloud-provider-dept-8001 µÄÅäÖñ£³ÖÒ»Ö + + datasource: + username: root #Êý¾Ý¿âµÇ½Óû§Ãû + password: root #Êý¾Ý¿âµÇ½ÃÜÂë + url: jdbc:mysql://127.0.0.1:3306/spring_cloud_db3 #Êý¾Ý¿âurl + driver-class-name: com.mysql.jdbc.Driver #Êý¾Ý¿âÇý¶¯ + +eureka: + client: #½«¿Í»§¶Ë×¢²áµ½ eureka ·þÎñÁбíÄÚ + service-url: + #defaultZone: http://eureka7001:7001/eureka #Õâ¸öµØÖ·ÊÇ 7001×¢²áÖÐÐÄÔÚ application.yml Öб©Â¶³öÀ´¶î×¢²áµØÖ· £¨µ¥»ú°æ£© + defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #½«·þÎñ×¢²áµ½ Eureka ¼¯Èº + instance: + instance-id: spring-cloud-provider-8003 #×Ô¶¨Òå·þÎñÃû³ÆÐÅÏ¢ + prefer-ip-address: true #ÏÔʾ·ÃÎÊ·¾¶µÄ ip µØÖ· + +``` + + + + + +5. ÒÀ´ÎÆô¶¯ micro-service-cloud-eureka-7001/7002/7003£¨·þÎñ×¢²áÖÐÐļ¯Èº£©¡¢micro-service-cloud-provider-dept-8001/8002/8003£¨·þÎñÌṩÕß¼¯Èº£©ÒÔ¼° micro-service-cloud-consumer-dept-80£¨·þÎñÏû·ÑÕߣ©¡£ + +6\. ʹÓÃä¯ÀÀÆ÷Á¬Ðø·ÃÎÊ¡°http://eureka7001.com/consumer/dept/get/1¡±,½á¹ûÈçÏÂͼ¡£ + +![Ribbon ĬÈϸºÔؾùºâ²ßÂÔ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1012215133-3.gif) +ͼ4£ºRibbon ĬÈϸºÔؾùºâ²ßÂÔ + +ͨ¹ýͼ 4 ÖÐ dbSource ×Ö¶ÎȡֵµÄ±ä»¯¿ÉÒÔ¿´³ö£¬Spring Cloud Ribbon ĬÈÏʹÓÃÂÖѯ²ßÂÔ½øÐиºÔؾùºâ¡£ + +## Çл»¸ºÔؾùºâ²ßÂÔ + +Spring Cloud Ribbon ĬÈÏʹÓÃÂÖѯ²ßÂÔѡȡ·þÎñʵÀý£¬ÎÒÃÇÒ²¿ÉÒÔ¸ù¾Ý×ÔÉíµÄÐèÇóÇл»¸ºÔؾùºâ²ßÂÔ¡£ + +Çл»¸ºÔؾùºâ²ßÂԵķ½·¨ºÜ¼òµ¥£¬ÎÒÃÇÖ»ÐèÒªÔÚ·þÎñÏû·ÑÕߣ¨¿Í»§¶Ë£©µÄÅäÖÃÀàÖУ¬½« IRule µÄÆäËûʵÏÖÀà×¢Èëµ½ÈÝÆ÷Öм´¿É¡£ + +ÏÂÃæÎÒÃǾÍͨ¹ýÒ»¸öʵÀý£¬À´ÑÝʾÏÂÈçºÎÇл»¸ºÔؾùºâµÄ²ßÂÔ¡£ + +1\. ÔÚ micro-service-cloud-consumer-dept-80 µÄÅäÖÃÀà ConfigBean ÖÐÌí¼ÓÒÔÏ´úÂ룬½«¸ºÔؾùºâ²ßÂÔÇл»Îª RandomRule£¨Ëæ»ú£©¡£ + + + + + +``` + + +@Bean +public IRule myRule() { + // RandomRule ÎªËæ»ú²ßÂÔ + return new RandomRule(); +} + +``` + + + + + +2\. ÖØÆô micro-service-cloud-consumer-dept-80£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/get/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Çл»¸ºÔؾùºâ²ßÂÔÎªËæ»ú](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1012214B8-4.gif) +ͼ5£ºÇл»¸ºÔؾùºâ²ßÂÔÎªËæ»ú + +ͨ¹ýͼ 5 ÖÐ dbSource ×Ö¶ÎȡֵµÄ±ä»¯¿ÉÒÔ¿´³ö£¬ÎÒÃÇÒѾ­½«¸ºÔؾùºâ²ßÂÔÇл»Îª RandomRule£¨Ëæ»ú£©¡£ + +## ¶¨ÖƸºÔؾùºâ²ßÂÔ + +ͨ³£Çé¿öÏ£¬Ribbon ÌṩµÄÕâЩĬÈϸºÔؾùºâ²ßÂÔÊÇ¿ÉÒÔÂú×ãÎÒÃǵÄÐèÇóµÄ£¬Èç¹ûÓÐÌØÊâµÄÒªÇó£¬ÎÒÃÇ»¹¿ÉÒÔ¸ù¾Ý×ÔÉíÐèÇó¶¨ÖƸºÔؾùºâ²ßÂÔ¡£ + +ÏÂÃæÎÒÃǾÍÀ´ÑÝʾÏÂÈçºÎ¶¨ÖƸºÔؾùºâ²ßÂÔ¡£ + +1\. ÔÚ micro-service-cloud-consumer-dept-80 ÖÐн¨Ò»¸ö net.biancheng.myrule °ü£¬²¢ÔڸðüÏ´´½¨Ò»¸öÃûΪ MyRandomRule µÄÀ࣬´úÂëÈçÏ¡£ + + + + + +``` + +package net.biancheng.myrule; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractLoadBalancerRule; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +import java.util.List; + +/** +* ¶¨ÖÆ Ribbon ¸ºÔؾùºâ²ßÂÔ +*/ +public class MyRandomRule extends AbstractLoadBalancerRule { + private int total = 0; // ×ܹ²±»µ÷ÓõĴÎÊý£¬Ä¿Ç°ÒªÇóÿ̨±»µ÷ÓÃ5´Î + private int currentIndex = 0; // µ±Ç°Ìṩ·þÎñµÄ»úÆ÷ºÅ + + public Server choose(ILoadBalancer lb, Object key) { + if (lb == null) { + return null; + } + Server server = null; + + while (server == null) { + if (Thread.interrupted()) { + return null; + } + //»ñÈ¡ËùÓÐÓÐЧµÄ·þÎñʵÀýÁбí + List upList = lb.getReachableServers(); + //»ñÈ¡ËùÓеķþÎñʵÀýµÄÁбí + List allList = lb.getAllServers(); + + //Èç¹ûûÓÐÈκεķþÎñʵÀýÔò·µ»Ø null + int serverCount = allList.size(); + if (serverCount == 0) { + return null; + } + //ÓëËæ»ú²ßÂÔÏàËÆ£¬µ«Ã¿¸ö·þÎñʵÀýÖ»ÓÐÔÚµ÷Óà 3 ´ÎÖ®ºó£¬²Å»áµ÷ÓÃÆäËûµÄ·þÎñʵÀý + if (total < 3) { + server = upList.get(currentIndex); + total++; + } else { + total = 0; + currentIndex++; + if (currentIndex >= upList.size()) { + currentIndex = 0; + } + } + if (server == null) { + Thread.yield(); + continue; + } + if (server.isAlive()) { + return (server); + } + server = null; + Thread.yield(); + } + return server; + } + + @Override + public Server choose(Object key) { + return choose(getLoadBalancer(), key); + } + + @Override + public void initWithNiwsConfig(IClientConfig clientConfig) { + // TODO Auto-generated method stub + } +} + +``` + + + + + +2\. ÔÚ net.biancheng.myrule °üÏ´´½¨Ò»¸öÃûΪ MySelfRibbonRuleConfig µÄÅäÖÃÀ࣬½«ÎÒÃǶ¨ÖƵĸºÔؾùºâ²ßÂÔʵÏÖÀà×¢Èëµ½ÈÝÆ÷ÖУ¬´úÂëÈçÏ¡£ + + + + + +``` +package net.biancheng.myrule; + +import com.netflix.loadbalancer.IRule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** +* ¶¨ÖÆ Ribbon ¸ºÔؾùºâ²ßÂÔµÄÅäÖÃÀà +* ¸Ã×Ô¶¨Òå Ribbon ¸ºÔؾùºâ²ßÂÔÅäÖÃÀà ²»ÄÜÔÚ net.biancheng.c °ü¼°Æä×Ó°üÏ +* ·ñÔòËùÓÐµÄ Ribbon ¿Í»§¶Ë¶¼»á²ÉÓøòßÂÔ£¬ÎÞ·¨´ïµ½ÌØÊ⻯¶¨ÖƵÄÄ¿µÄ +*/ +@Configuration +public class MySelfRibbonRuleConfig { + @Bean + public IRule myRule() { + //×Ô¶¨Òå Ribbon ¸ºÔؾùºâ²ßÂÔ + return new MyRandomRule(); //×Ô¶¨Òå£¬Ëæ»úÑ¡Ôñijһ¸ö΢·þÎñ£¬Ö´ÐÐÎå´Î + } +} + +``` + + + + + +3\. ÐÞ¸ÄλÓÚ net.biancheng.c °üϵÄÖ÷Æô¶¯À࣬ÔÚ¸ÃÀàÉÏʹÓà @RibbonClient ×¢½âÈÃÎÒÃǶ¨ÖƵĸºÔؾùºâ²ßÂÔÉúЧ£¬´úÂëÈçÏ¡£ + + + + + +``` + + +package net.biancheng.c; + +import net.biancheng.myrule.MySelfRibbonRuleConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; + +@SpringBootApplication +@EnableEurekaClient +//×Ô¶¨Òå Ribbon ¸ºÔؾùºâ²ßÂÔÔÚÖ÷Æô¶¯ÀàÉÏʹÓà RibbonClient ×¢½â£¬ÔÚ¸Ã΢·þÎñÆô¶¯Ê±£¬¾ÍÄÜ×Ô¶¯È¥¼ÓÔØÎÒÃÇ×Ô¶¨ÒåµÄ Ribbon ÅäÖÃÀ࣬´Ó¶øÊÇÅäÖÃÉúЧ +// name ΪÐèÒª¶¨ÖƸºÔؾùºâ²ßÂÔµÄ΢·þÎñÃû³Æ£¨application name£© +// configuration Ϊ¶¨ÖƵĸºÔؾùºâ²ßÂÔµÄÅäÖÃÀ࣬ +// ÇÒ¹Ù·½ÎĵµÖÐÃ÷È·Ìá³ö£¬¸ÃÅäÖÃÀ಻ÄÜÔÚ ComponentScan ×¢½â£¨SpringBootApplication ×¢½âÖаüº¬Á˸Ã×¢½â£©Ïµİü»òÆä×Ó°üÖУ¬¼´×Ô¶¨Òå¸ºÔØ¾ùºâÅäÖÃÀ಻ÄÜÔÚ net.biancheng.c °ü¼°Æä×Ó°üÏ +@RibbonClient(name = "MICROSERVICECLOUDPROVIDERDEPT", configuration = MySelfRibbonRuleConfig.class) +public class MicroServiceCloudConsumerDept80Application { + + public static void main(String[] args) { + SpringApplication.run(MicroServiceCloudConsumerDept80Application.class, args); + } +} + +``` + + + + + +4\. ÖØÆô micro-service-cloud-consumer-dept-80£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://eureka7001.com/consumer/dept/get/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![¶¨ÖƸºÔؾùºâ²ßÂÔ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10122152E-5.gif) +ͼ6£º¶¨ÖƸºÔؾùºâ²ßÂÔ + +ͨ¹ýͼ 6 ÖÐ dbSource ×Ö¶ÎȡֵµÄ±ä»¯¿ÉÒÔ¿´³ö£¬ÎÒÃǶ¨ÖƵĸºÔؾùºâ²ßÂÔÒѾ­ÉúЧ¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" new file mode 100644 index 0000000..45bc82f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudSleuth.md" @@ -0,0 +1,142 @@ +> Spring Cloud Sleuth ÊÇ·Ö²¼Ê½ÏµÍ³Öиú×Ù·þÎñ¼äµ÷ÓõŤ¾ß£¬Ëü¿ÉÒÔÖ±¹ÛµØÕ¹Ê¾³öÒ»´ÎÇëÇóµÄµ÷Óùý³Ì£¬±¾ÎĽ«¶ÔÆäÓ÷¨½øÐÐÏêϸ½éÉÜ¡£ + +## [#](https://www.macrozheng.com/cloud/sleuth.html#spring-cloud-sleuth-%E7%AE%80%E4%BB%8B)Spring Cloud Sleuth ¼ò½é + +Ëæ×ÅÎÒÃǵÄϵͳԽÀ´Ô½ÅӴ󣬸÷¸ö·þÎñ¼äµÄµ÷ÓùØÏµÒ²±äµÃÔ½À´Ô½¸´ÔÓ¡£µ±¿Í»§¶Ë·¢ÆðÒ»¸öÇëÇóʱ£¬Õâ¸öÇëÇó¾­¹ý¶à¸ö·þÎñºó£¬×îÖÕ·µ»ØÁ˽á¹û£¬¾­¹ýµÄÿһ¸ö·þÎñ¶¼ÓпÉÄÜ·¢ÉúÑÓ³Ù»ò´íÎ󣬴Ӷøµ¼ÖÂÇëÇóʧ°Ü¡£ÕâʱºòÎÒÃǾÍÐèÒªÇëÇóÁ´Â·¸ú×Ù¹¤¾ßÀ´°ïÖúÎÒÃÇ£¬ÀíÇåÇëÇóµ÷ÓõķþÎñÁ´Â·£¬½â¾öÎÊÌâ¡£ + +## [#](https://www.macrozheng.com/cloud/sleuth.html#%E7%BB%99%E6%9C%8D%E5%8A%A1%E6%B7%BB%E5%8A%A0%E8%AF%B7%E6%B1%82%E9%93%BE%E8%B7%AF%E8%B7%9F%E8%B8%AA)¸ø·þÎñÌí¼ÓÇëÇóÁ´Â·¸ú×Ù + +> ÎÒÃǽ«Í¨¹ýuser-serviceºÍribbon-serviceÖ®¼äµÄ·þÎñµ÷ÓÃÀ´ÑÝʾ¸Ã¹¦ÄÜ£¬ÕâÀïÎÒÃǵ÷ÓÃribbon-serviceµÄ½Ó¿Úʱ£¬ribbon-service»áͨ¹ýRestTemplateÀ´µ÷ÓÃuser-serviceÌṩµÄ½Ó¿Ú¡£ + +* Ê×Ïȸøuser-serviceºÍribbon-serviceÌí¼ÓÇëÇóÁ´Â·¸ú×Ù¹¦ÄܵÄÖ§³Ö£» + +* ÔÚuser-serviceºÍribbon-serviceÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + +``` + + org.springframework.cloud + spring-cloud-starter-zipkin + + +``` + + + + +* ÐÞ¸Äapplication.ymlÎļþ£¬ÅäÖÃÊÕ¼¯ÈÕÖ¾µÄzipkin-server·ÃÎʵØÖ·£º + + + +``` +spring: + zipkin: + base-url: http://localhost:9411 + sleuth: + sampler: + probability: 0.1 #ÉèÖÃSleuthµÄ³éÑùÊÕ¼¯¸ÅÂÊ + +``` + + + +## [#](https://www.macrozheng.com/cloud/sleuth.html#%E6%95%B4%E5%90%88zipkin%E8%8E%B7%E5%8F%96%E5%8F%8A%E5%88%86%E6%9E%90%E6%97%A5%E5%BF%97)ÕûºÏZipkin»ñÈ¡¼°·ÖÎöÈÕÖ¾ + +> ZipkinÊÇTwitterµÄÒ»¸ö¿ªÔ´ÏîÄ¿£¬¿ÉÒÔÓÃÀ´»ñÈ¡ºÍ·ÖÎöSpring Cloud Sleuth ÖвúÉúµÄÇëÇóÁ´Â·¸ú×ÙÈÕÖ¾£¬ËüÌṩÁËWeb½çÃæÀ´°ïÖúÎÒÃÇÖ±¹ÛµØ²é¿´ÇëÇóÁ´Â·¸ú×ÙÐÅÏ¢¡£ + +* SpringBoot 2.0ÒÔÉϰ汾ÒѾ­²»ÐèÒª×ÔÐдzipkin-server£¬ÎÒÃÇ¿ÉÒԴӸõØÖ·ÏÂÔØzipkin-server£ºhttps://repo1.maven.org/maven2/io/zipkin/java/zipkin-server/2.12.9/zipkin-server-2.12.9-exec.jar + +* ÏÂÔØÍê³ÉºóʹÓÃÒÔÏÂÃüÁîÔËÐÐzipkin-server£º + + + +``` +java -jar zipkin-server-2.12.9-exec.jar + +``` + +1 + + + + +* ZipkinÒ³Ãæ·ÃÎʵØÖ·£ºhttp://localhost:9411 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springcloud_sleuth_01.625f37c3.png) + +* Æô¶¯eureka-sever£¬ribbon-service£¬user-service£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springcloud_sleuth_02.baf7b77c.png) + +* ¶à´Îµ÷Óã¨SleuthΪ³éÑùÊÕ¼¯£©ribbon-serviceµÄ½Ó¿Ú[http://localhost:8301/user/1open in new window](http://localhost:8301/user/1) £¬µ÷ÓÃÍêºó²é¿´ZipkinÊ×Ò³·¢ÏÖÒѾ­ÓÐÇëÇóÁ´Â·¸ú×ÙÐÅÏ¢ÁË£» + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springcloud_sleuth_03.a71d1cf1.png) + +* µã»÷²é¿´ÏêÇé¿ÉÒÔÖ±¹ÛµØ¿´µ½ÇëÇóµ÷ÓÃÁ´Â·ºÍͨ¹ýÿ¸ö·þÎñµÄºÄʱ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springcloud_sleuth_04.d13e3d99.png) + +## [#](https://www.macrozheng.com/cloud/sleuth.html#%E4%BD%BF%E7%94%A8elasticsearch%E5%AD%98%E5%82%A8%E8%B7%9F%E8%B8%AA%E4%BF%A1%E6%81%AF)ʹÓÃElasticsearch´æ´¢¸ú×ÙÐÅÏ¢ + +> Èç¹ûÎÒÃǰÑzipkin-serverÖØÆôһϾͻᷢÏָոյĴ洢µÄ¸ú×ÙÐÅϢȫ²¿¶ªÊ§ÁË£¬¿É¼ûÆäÊÇ´æ´¢ÔÚÄÚ´æÖеģ¬ÓÐʱºòÎÒÃÇÐèÒª½«ËùÓÐÐÅÏ¢´æ´¢ÏÂÀ´£¬ÕâÀïÒÔ´æ´¢µ½ElasticsearchΪÀý£¬À´ÑÝʾϸù¦ÄÜ¡£ + +### [#](https://www.macrozheng.com/cloud/sleuth.html#%E5%AE%89%E8%A3%85elasticsearch)°²×°Elasticsearch + +* ÏÂÔØElasticsearch6.2.2µÄzip°ü£¬²¢½âѹµ½Ö¸¶¨Ä¿Â¼£¬ÏÂÔØµØÖ·£º[https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2open in new window](https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/arch_screen_25.48daf958.png) + +* ÔËÐÐbinĿ¼ÏµÄelasticsearch.batÆô¶¯Elasticsearch + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/arch_screen_27.ba3cb8e0.png) + +### [#](https://www.macrozheng.com/cloud/sleuth.html#%E4%BF%AE%E6%94%B9%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0%E5%B0%86%E4%BF%A1%E6%81%AF%E5%AD%98%E5%82%A8%E5%88%B0elasticsearch)ÐÞ¸ÄÆô¶¯²ÎÊý½«ÐÅÏ¢´æ´¢µ½Elasticsearch + +* ʹÓÃÒÔÏÂÃüÁîÔËÐУ¬¾Í¿ÉÒ԰Ѹú×ÙÐÅÏ¢´æ´¢µ½ElasticsearchÀïÃæÈ¥ÁË£¬ÖØÐÂÆô¶¯Ò²²»»á¶ªÊ§£» + + + +``` +# STORAGE_TYPE£º±íʾ´æ´¢ÀàÐÍ ES_HOSTS£º±íʾESµÄ·ÃÎʵØÖ· +java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=elasticsearch --ES_HOSTS=localhost:9200 + +``` + + +* Ö®ºóÐèÒªÖØÐÂÆô¶¯user-serviceºÍribbon-service²ÅÄÜÉúЧ£¬ÖØÆôºó¶à´Îµ÷ÓÃribbon-serviceµÄ½Ó¿Ú[http://localhost:8301/user/1open in new window](http://localhost:8301/user/1)£» + +* Èç¹û°²×°ÁËElasticsearchµÄ¿ÉÊÓ»¯¹¤¾ßKibanaµÄ»°£¬¿ÉÒÔ¿´µ½ÀïÃæÒѾ­´æ´¢Á˸ú×ÙÐÅÏ¢£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springcloud_sleuth_05.9929ce6a.png) + +### [#](https://www.macrozheng.com/cloud/sleuth.html#%E6%9B%B4%E5%A4%9A%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0%E5%8F%82%E8%80%83)¸ü¶àÆô¶¯²ÎÊý²Î¿¼ + +https://github.com/openzipkin/zipkin/tree/master/zipkin-server#elasticsearch-storage + +## [#](https://www.macrozheng.com/cloud/sleuth.html#%E4%BD%BF%E7%94%A8%E5%88%B0%E7%9A%84%E6%A8%A1%E5%9D%97)ʹÓõ½µÄÄ£¿é + + + +``` +springcloud-learning +©À©¤©¤ eureka-server -- eureka×¢²áÖÐÐÄ +©À©¤©¤ user-service -- ÌṩUser¶ÔÏóCRUD½Ó¿ÚµÄ·þÎñ +©¸©¤©¤ ribbon-service -- ribbon·þÎñµ÷ÓòâÊÔ·þÎñ + +``` + + + + +## [#](https://www.macrozheng.com/cloud/sleuth.html#%E9%A1%B9%E7%9B%AE%E6%BA%90%E7%A0%81%E5%9C%B0%E5%9D%80)ÏîĿԴÂëµØÖ· + +[https://github.com/macrozheng/springcloud-learning](https://github.com/macrozheng/springcloud-learning) + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" new file mode 100644 index 0000000..9040110 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloudZuul.md" @@ -0,0 +1,392 @@ +Spring Cloud Zuul ÊÇSpring Cloud Netflix ×ÓÏîÄ¿µÄºËÐÄ×é¼þÖ®Ò»£¬¿ÉÒÔ×÷Ϊ΢·þÎñ¼Ü¹¹ÖеÄAPIÍø¹ØÊ¹Óã¬Ö§³Ö¶¯Ì¬Â·ÓÉÓë¹ýÂ˹¦ÄÜ£¬±¾ÎĽ«¶ÔÆäÓ÷¨½øÐÐÏêϸ½éÉÜ¡£ + +# Zuul¼ò½é + +APIÍø¹ØÎªÎ¢·þÎñ¼Ü¹¹ÖеķþÎñÌṩÁËͳһµÄ·ÃÎÊÈë¿Ú£¬¿Í»§¶Ëͨ¹ýAPIÍø¹Ø·ÃÎÊÏà¹Ø·þÎñ¡£APIÍø¹ØµÄ¶¨ÒåÀàËÆÓÚÉè¼ÆÄ£Ê½ÖеÄÃÅÃæÄ£Ê½£¬ËüÏ൱ÓÚÕû¸ö΢·þÎñ¼Ü¹¹ÖеÄÃÅÃæ£¬ËùÓпͻ§¶ËµÄ·ÃÎʶ¼Í¨¹ýËüÀ´½øÐзÓɼ°¹ýÂË¡£ËüʵÏÖÁËÇëÇó·ÓÉ¡¢¸ºÔؾùºâ¡¢Ð£Ñé¹ýÂË¡¢·þÎñÈÝ´í¡¢·þÎñ¾ÛºÏµÈ¹¦ÄÜ¡£ + +#´´½¨Ò»¸özuul-proxyÄ£¿é + +ÕâÀïÎÒÃÇ´´½¨Ò»¸özuul-proxyÄ£¿éÀ´ÑÝʾzuulµÄ³£Óù¦ÄÜ¡£ + +#ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ + + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + + + +# ÔÚapplication.ymlÖнøÐÐÅäÖà + + + + server: + port: 8801 + spring: + application: + name: zuul-proxy + eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ + + +#ÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableZuulProxy×¢½âÀ´ÆôÓÃZuulµÄAPIÍø¹Ø¹¦ÄÜ + + + + @EnableZuulProxy + @EnableDiscoveryClient + @SpringBootApplication + public class ZuulProxyApplication { + + public static void main(String[] args) { + SpringApplication.run(ZuulProxyApplication.class, args); + } + + } + + + +# ³£Óù¦ÄÜ + +# Æô¶¯Ïà¹Ø·þÎñ + +ÕâÀïÎÒÃÇͨ¹ýÆô¶¯eureka-server£¬Á½¸öuser-service£¬feign-serviceºÍzuul-proxyÀ´ÑÝʾZuulµÄ³£Óù¦ÄÜ£¬Æô¶¯ºó×¢²áÖÐÐÄÏÔʾÈçÏ¡£ + + + +# ÅäÖ÷ÓɹæÔò + +- ÎÒÃÇ¿ÉÒÔͨ¹ýÐÞ¸Äapplication.ymlÖеÄÅäÖÃÀ´ÅäÖ÷ÓɹæÔò£¬ÕâÀïÎÒÃǽ«Æ¥Åä/userService/**µÄÇëÇó·Óɵ½user-service·þÎñÉÏÈ¥£¬Æ¥Åä/feignService/**µÄÇëÇó·Óɵ½feign-serviceÉÏÈ¥¡£ + + + + zuul: + routes: #¸ø·þÎñÅäÖ÷ÓÉ + user-service: + path: /userService/** + feign-service: + path: /feignService/** + + + +- ·ÃÎÊhttp://localhost:8801/userService/user/1open in new window¿ÉÒÔ·¢ÏÖÇëÇó·Óɵ½ÁËuser-serviceÉÏÁË£» +- ·ÃÎÊhttp://localhost:8801/feignService/user/1open in new window¿ÉÒÔ·¢ÏÖÇëÇó·Óɵ½ÁËfeign-serviceÉÏÁË¡£ + +# ĬÈÏ·ÓɹæÔò + +- ZuulºÍEureka½áºÏʹÓ㬿ÉÒÔʵÏÖ·ÓɵÄ×Ô¶¯ÅäÖã¬×Ô¶¯ÅäÖõÄ·ÓÉÒÔ·þÎñÃû³ÆÎªÆ¥Åä·¾¶£¬Ï൱ÓÚÈçÏÂÅäÖ㺠+ + + + zuul: + routes: #¸ø·þÎñÅäÖ÷ÓÉ + user-service: + path: /user-service/** + feign-service: + path: /feign-service/** + + + +- ·ÃÎÊhttp://localhost:8801/user-service/user/1open in new windowͬÑù¿ÉÒÔ·Óɵ½ÁËuser-serviceÉÏÁË£» +- ·ÃÎÊhttp://localhost:8801/feign-service/user/1open in new windowͬÑù¿ÉÒÔ·Óɵ½ÁËfeign-serviceÉÏÁË¡£ +- Èç¹û²»ÏëʹÓÃĬÈϵÄ·ÓɹæÔò£¬¿ÉÒÔÌí¼ÓÒÔÏÂÅäÖÃÀ´ºöÂÔĬÈÏ·ÓÉÅäÖ㺠+ + + + zuul: + ignored-services: user-service,feign-service #¹Ø±ÕĬÈÏ·ÓÉÅäÖà + + + + + +# ¸ºÔؾùºâ¹¦ÄÜ + +¶à´Îµ÷ÓÃhttp://localhost:8801/user-service/user/1open in new window½øÐвâÊÔ£¬¿ÉÒÔ·¢ÏÖÔËÐÐÔÚ8201ºÍ8202µÄuser-service·þÎñ½»Ìæ´òÓ¡ÈçÏÂÐÅÏ¢¡£ + + + + 2019-10-05 10:31:58.738 INFO 11520 --- [nio-8202-exec-5] c.macro.cloud.controller.UserController : ¸ù¾Ýid»ñÈ¡Óû§ÐÅÏ¢£¬Óû§Ãû³ÆÎª£ºmacro + 2019-10-05 10:32:00.356 INFO 11520 --- [nio-8202-exec-6] c.macro.cloud.controller.UserController : ¸ù¾Ýid»ñÈ¡Óû§ÐÅÏ¢£¬Óû§Ãû³ÆÎª£ºmacro + + + +# ÅäÖ÷ÃÎÊǰ׺ + +ÎÒÃÇ¿ÉÒÔͨ¹ýÒÔÏÂÅäÖÃÀ´¸øÍø¹ØÂ·¾¶Ìí¼Óǰ׺£¬´Ë´¦Ìí¼ÓÁË/proxyǰ׺£¬ÕâÑùÎÒÃÇÐèÒª·ÃÎÊhttp://localhost:8801/proxy/user-service/user/1open in new window²ÅÄÜ·ÃÎʵ½user-serviceÖеĽӿڡ£ + + + + zuul: + prefix: /proxy #¸øÍø¹ØÂ·ÓÉÌí¼Óǰ׺ + + + + +# Header¹ýÂ˼°Öض¨ÏòÌí¼ÓHost + +- ZuulÔÚÇëÇó·ÓÉʱ£¬Ä¬ÈÏ»á¹ýÂ˵ôһЩÃô¸ÐµÄÍ·ÐÅÏ¢£¬ÒÔÏÂÅäÖÿÉÒÔ·Àֹ·ÓÉʱµÄCookie¼°AuthorizationµÄ¶ªÊ§£º + + + + zuul: + sensitive-headers: Cookie,Set-Cookie,Authorization #ÅäÖùýÂËÃô¸ÐµÄÇëÇóÍ·ÐÅÏ¢£¬ÉèÖÃΪ¿Õ¾Í²»»á¹ýÂË + + + + +- ZuulÔÚÇëÇó·ÓÉʱ£¬²»»áÉèÖÃ×î³õµÄhostÍ·ÐÅÏ¢£¬ÒÔÏÂÅäÖÿÉÒÔ½â¾ö£º + + + + zuul: + add-host-header: true #ÉèÖÃΪtrueÖØ¶¨ÏòÊÇ»áÌí¼ÓhostÇëÇóÍ· + + + +# ²é¿´Â·ÓÉÐÅÏ¢ + +ÎÒÃÇ¿ÉÒÔͨ¹ýSpringBoot ActuatorÀ´²é¿´ZuulÖеÄ·ÓÉÐÅÏ¢¡£ + +- ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + +- ÐÞ¸Äapplication.yamlÅäÖÃÎļþ£¬¿ªÆô²é¿´Â·ÓɵĶ˵㣺 + + + + management: + endpoints: + web: + exposure: + include: 'routes' + + + + + +- ͨ¹ý·ÃÎÊhttp://localhost:8801/actuator/routesopen in new window²é¿´¼òµ¥Â·ÓÉÐÅÏ¢£º + + + +- ͨ¹ý·ÃÎÊhttp://localhost:8801/actuator/routes/detailsopen in new window²é¿´Ïêϸ·ÓÉÐÅÏ¢£º + + + +# ¹ýÂËÆ÷ + +·ÓÉÓë¹ýÂËÊÇZuulµÄÁ½´óºËÐŦÄÜ£¬Â·Óɹ¦ÄܸºÔð½«ÍⲿÇëÇóת·¢µ½¾ßÌåµÄ·þÎñʵÀýÉÏÈ¥£¬ÊÇʵÏÖͳһ·ÃÎÊÈë¿ÚµÄ»ù´¡£¬¹ýÂ˹¦ÄܸºÔð¶ÔÇëÇó¹ý³Ì½øÐжîÍâµÄ´¦Àí£¬ÊÇÇëÇóУÑé¹ýÂ˼°·þÎñ¾ÛºÏµÄ»ù´¡¡£ + +# ¹ýÂËÆ÷ÀàÐÍ + +ZuulÖÐÓÐÒÔϼ¸ÖÖµäÐ͵ĹýÂËÆ÷ÀàÐÍ¡£ + +- pre£ºÔÚÇëÇó±»Â·Óɵ½Ä¿±ê·þÎñǰִÐУ¬±ÈÈçȨÏÞУÑé¡¢´òÓ¡ÈÕÖ¾µÈ¹¦ÄÜ£» +- routing£ºÔÚÇëÇó±»Â·Óɵ½Ä¿±ê·þÎñʱִÐУ¬ÕâÊÇʹÓÃApache HttpClient»òNetflix Ribbon¹¹½¨ºÍ·¢ËÍԭʼHTTPÇëÇóµÄµØ·½£» +- post£ºÔÚÇëÇó±»Â·Óɵ½Ä¿±ê·þÎñºóÖ´ÐУ¬±ÈÈç¸øÄ¿±ê·þÎñµÄÏìÓ¦Ìí¼ÓÍ·ÐÅÏ¢£¬ÊÕ¼¯Í³¼ÆÊý¾ÝµÈ¹¦ÄÜ£» +- error£ºÇëÇóÔÚÆäËû½×¶Î·¢Éú´íÎóʱִÐС£ + +# ¹ýÂËÆ÷µÄÉúÃüÖÜÆÚ + +ÏÂͼÃèÊöÁËÒ»¸öHTTPÇëÇóµ½´ïAPIÍø¹Øºó£¬ÈçºÎÔÚ¸÷ÖÖ²»Í¬ÀàÐ͵ĹýÂËÆ÷ÖÐÁ÷תµÄ¹ý³Ì¡£ + + + +# ×Ô¶¨Òå¹ýÂËÆ÷ + +½ÓÏÂÀ´ÎÒÃÇ×Ô¶¨ÒåÒ»¸ö¹ýÂËÆ÷À´ÑÝʾϹýÂËÆ÷µÄ×÷Óᣠ+ +# Ìí¼ÓPreLogFilterÀà¼Ì³ÐZuulFilter + +ÕâÊÇÒ»¸öǰÖùýÂËÆ÷£¬ÓÃÓÚÔÚÇëÇó·Óɵ½Ä¿±ê·þÎñǰ´òÓ¡ÇëÇóÈÕÖ¾¡£ + + + + /** + * Created by macro on 2019/9/9. + */ + @Component + public class PreLogFilter extends ZuulFilter { + private Logger LOGGER = LoggerFactory.getLogger(this.getClass()); + + /** + * ¹ýÂËÆ÷ÀàÐÍ£¬ÓÐpre¡¢routing¡¢post¡¢errorËÄÖÖ¡£ + */ + @Override + public String filterType() { + return "pre"; + } + + /** + * ¹ýÂËÆ÷Ö´ÐÐ˳Ðò£¬ÊýֵԽСÓÅÏȼ¶Ô½¸ß¡£ + */ + @Override + public int filterOrder() { + return 1; + } + + /** + * ÊÇ·ñ½øÐйýÂË£¬·µ»Øtrue»áÖ´ÐйýÂË¡£ + */ + @Override + public boolean shouldFilter() { + return true; + } + + /** + * ×Ô¶¨ÒåµÄ¹ýÂËÆ÷Âß¼­£¬µ±shouldFilter()·µ»Øtrueʱ»áÖ´ÐС£ + */ + @Override + public Object run() throws ZuulException { + RequestContext requestContext = RequestContext.getCurrentContext(); + HttpServletRequest request = requestContext.getRequest(); + String host = request.getRemoteHost(); + String method = request.getMethod(); + String uri = request.getRequestURI(); + LOGGER.info("Remote host:{},method:{},uri:{}", host, method, uri); + return null; + } + } + + + +# ¹ýÂËÆ÷¹¦ÄÜÑÝʾ + +Ìí¼Ó¹ýÂËÆ÷ºó£¬ÎÒÃÇ·ÃÎÊhttp://localhost:8801/user-service/user/1open in new window²âÊÔÏ£¬»á´òÓ¡ÈçÏÂÈÕÖ¾¡£ + + + + 2019-10-05 15:13:10.232 INFO 11040 --- [nio-8801-exec-7] com.macro.cloud.filter.PreLogFilter : Remote host:0:0:0:0:0:0:0:1,method:GET,uri:/user-service/user/1 + + + + + + +# ºËÐĹýÂËÆ÷ + + ¹ýÂËÆ÷Ãû³Æ ¹ýÂËÀàÐÍ ÓÅÏȼ¶ ¹ýÂËÆ÷µÄ×÷Óà + ServletDetectionFilter pre -3 ¼ì²âµ±Ç°ÇëÇóÊÇͨ¹ýDispatcherServlet´¦ÀíÔËÐеϹÊÇZuulServletÔËÐд¦ÀíµÄ¡£ + Servlet30WrapperFilter pre -2 ¶ÔԭʼµÄHttpServletRequest½øÐаü×°¡£ + FormBodyWrapperFilter pre -1 ½«Content-TypeΪapplication/x-www-form-urlencoded»òmultipart/form-dataµÄÇëÇó°ü×°³ÉFormBodyRequestWrapper¶ÔÏó¡£ + DebugFilter route 1 ¸ù¾Ýzuul.debug.requestµÄÅäÖÃÀ´¾ö¶¨ÊÇ·ñ´òÓ¡debugÈÕÖ¾¡£ + PreDecorationFilter route 5 ¶Ôµ±Ç°ÇëÇó½øÐÐÔ¤´¦ÀíÒÔ±ãÖ´ÐкóÐø²Ù×÷¡£ + RibbonRoutingFilter route 10 ͨ¹ýRibbonºÍHystrixÀ´Ïò·þÎñʵÀý·¢ÆðÇëÇ󣬲¢½«ÇëÇó½á¹û½øÐзµ»Ø¡£ + SimpleHostRoutingFilter route 100 Ö»¶ÔÇëÇóÉÏÏÂÎÄÖÐÓÐrouteHost²ÎÊýµÄ½øÐд¦Àí£¬Ö±½ÓʹÓÃHttpClientÏòrouteHost¶ÔÓ¦µÄÎïÀíµØÖ·½øÐÐת·¢¡£ + SendForwardFilter route 500 Ö»¶ÔÇëÇóÉÏÏÂÎÄÖÐÓÐforward.to²ÎÊýµÄ½øÐд¦Àí£¬½øÐб¾µØÌø×ª¡£ + SendErrorFilter post 0 µ±ÆäËû¹ýÂËÆ÷ÄÚ²¿·¢ÉúÒ쳣ʱµÄ»áÓÉËüÀ´½øÐд¦Àí£¬²úÉú´íÎóÏìÓ¦¡£ + SendResponseFilter post 1000 ÀûÓÃÇëÇóÉÏÏÂÎĵÄÏìÓ¦ÐÅÏ¢À´×éÖ¯ÇëÇó³É¹¦µÄÏìÓ¦ÄÚÈÝ¡£ + +# ½ûÓùýÂËÆ÷ + +- ÎÒÃÇ¿ÉÒÔ¶Ô¹ýÂËÆ÷½øÐнûÓõÄÅäÖã¬ÅäÖøñʽÈçÏ£º + + + + zuul: + filterClassName: + filter: + disable: true + + + +- ÒÔÏÂÊǽûÓÃPreLogFilterµÄʾÀýÅäÖ㺠+ + + + zuul: + PreLogFilter: + pre: + disable: true + + + +#RibbonºÍHystrixµÄÖ§³Ö + +ÓÉÓÚZuul×Ô¶¯¼¯³ÉÁËRibbonºÍHystrix£¬ËùÒÔZuulÌìÉú¾ÍÓиºÔؾùºâºÍ·þÎñÈÝ´íÄÜÁ¦£¬ÎÒÃÇ¿ÉÒÔͨ¹ýRibbonºÍHystrixµÄÅäÖÃÀ´ÅäÖÃZuulÖеÄÏàÓ¦¹¦ÄÜ¡£ + +- ¿ÉÒÔʹÓÃHystrixµÄÅäÖÃÀ´ÉèÖ÷ÓÉת·¢Ê±HystrixCommandµÄÖ´Ðг¬Ê±Ê±¼ä£º + + + + hystrix: + command: #ÓÃÓÚ¿ØÖÆHystrixCommandµÄÐÐΪ + default: + execution: + isolation: + thread: + timeoutInMilliseconds: 1000 #ÅäÖÃHystrixCommandÖ´Ðеij¬Ê±Ê±¼ä£¬Ö´Ðг¬¹ý¸Ãʱ¼ä»á½øÐзþÎñ½µ¼¶´¦Àí + + +- ¿ÉÒÔʹÓÃRibbonµÄÅäÖÃÀ´ÉèÖ÷ÓÉת·¢Ê±ÇëÇóÁ¬½Ó¼°´¦ÀíµÄ³¬Ê±Ê±¼ä£º + + + + ribbon: #È«¾ÖÅäÖà + ConnectTimeout: 1000 #·þÎñÇëÇóÁ¬½Ó³¬Ê±Ê±¼ä£¨ºÁÃ룩 + ReadTimeout: 3000 #·þÎñÇëÇó´¦Àí³¬Ê±Ê±¼ä£¨ºÁÃ룩 + + +#³£ÓÃÅäÖà + + + + zuul: + routes: #¸ø·þÎñÅäÖ÷ÓÉ + user-service: + path: /userService/** + feign-service: + path: /feignService/** + ignored-services: user-service,feign-service #¹Ø±ÕĬÈÏ·ÓÉÅäÖà + prefix: /proxy #¸øÍø¹ØÂ·ÓÉÌí¼Óǰ׺ + sensitive-headers: Cookie,Set-Cookie,Authorization #ÅäÖùýÂËÃô¸ÐµÄÇëÇóÍ·ÐÅÏ¢£¬ÉèÖÃΪ¿Õ¾Í²»»á¹ýÂË + add-host-header: true #ÉèÖÃΪtrueÖØ¶¨ÏòÊÇ»áÌí¼ÓhostÇëÇóÍ· + retryable: true # ¹Ø±ÕÖØÊÔ»úÖÆ + PreLogFilter: + pre: + disable: false #¿ØÖÆÊÇ·ñÆôÓùýÂËÆ÷ + + +#ʹÓõ½µÄÄ£¿é + + + + springcloud-learning + ©À©¤©¤ eureka-server -- eureka×¢²áÖÐÐÄ + ©À©¤©¤ user-service -- ÌṩUser¶ÔÏóCRUD½Ó¿ÚµÄ·þÎñ + ©À©¤©¤ feign-service -- feign·þÎñµ÷ÓòâÊÔ·þÎñ + ©¸©¤©¤ zuul-proxy -- zuul×÷ÎªÍø¹ØµÄ²âÊÔ·þÎñ + + +#ÏîĿԴÂëµØÖ· + +https://github.com/macrozheng/springcloud-learning +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" new file mode 100644 index 0000000..a525595 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud/SpringCloud\346\246\202\350\277\260.md" @@ -0,0 +1,143 @@ +Spring Cloud ÊÇÒ»¿î»ùÓÚ Spring Boot ʵÏÖµÄ΢·þÎñ¿ò¼Ü¡£Spring Cloud Ô´×Ô Spring ÉçÇø£¬Ö÷ÒªÓÉ Pivotal ºÍ Netflix Á½´ó¹«Ë¾Ìṩ¼¼Êõµü´úºÍά»¤¡£ + +Ëæ×Å΢·þÎñµÄ»ð±¬Á÷ÐУ¬¹úÄÚÍâ¸÷´ó»¥ÁªÍø¹«Ë¾¶¼Ïà¼Ì·ÖÏíÁËËûÃÇÔÚ΢·þÎñ¼Ü¹¹ÖУ¬Õë¶Ô²»Í¬³¡¾°³öÏֵĸ÷ÖÖÎÊÌâµÄ½â¾ö·½°¸ºÍ¿ªÔ´¿ò¼Ü¡£ + +* **·þÎñÖÎÀí**£º°¢Àï°Í°Í¿ªÔ´µÄ Dubbo ºÍµ±µ±ÍøÔÚÆä»ù´¡ÉÏÀ©Õ¹³öÀ´µÄ DubboX¡¢Netflix µÄ Eureka ÒÔ¼° Apache µÄ Consul µÈ¡£ +* **·Ö²¼Ê½ÅäÖùÜÀí**£º°Ù¶ÈµÄ Disconf¡¢Netflix µÄ Archaius¡¢360 µÄ QConf¡¢Ð¯³ÌµÄ Apollo ÒÔ¼° Spring Cloud µÄ Config µÈ¡£ +* **ÅúÁ¿ÈÎÎñ**£ºµ±µ±ÍøµÄ Elastic-Job¡¢LinkedIn µÄ Azkaban ÒÔ¼° Spring Cloud µÄ Task µÈ¡£ +* **·þÎñ¸ú×Ù**£º¾©¶«µÄ Hydra¡¢Spring Cloud µÄ Sleuth ÒÔ¼° Twitter µÄ Zipkin µÈ¡£ +* **¡­¡­** + +ÒÔÉÏÕâЩ΢·þÎñ¿ò¼Ü»ò½â¾ö·½°¸¶¼¾ßÓÐÒÔÏ 2 ¸öÌØµã£º + +* ¶ÔÓÚͬһ¸ö΢·þÎñÎÊÌ⣬¸÷»¥ÁªÍø¹«Ë¾¸ø³öµÄ½â¾ö·½°¸¸÷²»Ïàͬ¡£ +* Ò»¸ö΢·þÎñ¿ò¼Ü»ò½â¾ö·½°¸¶¼Ö»Äܽâ¾ö΢·þÎñÖеÄijһ¸ö»òij¼¸¸öÎÊÌ⣬¶ÔÓÚÆäËûÎÊÌâÔòÎÞÄÜΪÁ¦¡£ + +ÕâÖÖÇé¿öÏ£¬´î½¨Ò»Ì×΢·Ö²¼Ê½Î¢·þÎñϵͳ£¬¾ÍÐèÒªÕë¶ÔÕâЩÎÊÌâ´ÓÖî¶àµÄ½â¾ö·½°¸ÖÐ×ö³öÑ¡Ôñ£¬ÕâʹµÃÎÒÃDz»µÃ²»½«´óÁ¿µÄ¾«Á¦»¨·ÑÔÚǰÆÚµÄµ÷ÑС¢·ÖÎöÒÔ¼°ÊµÑéÉÏ¡£ + +Spring Cloud ±»³ÆÎª¹¹½¨·Ö²¼Ê½Î¢·þÎñϵͳµÄ¡°È«¼ÒͰ¡±£¬Ëü²¢²»ÊÇijһÃż¼Êõ£¬¶øÊÇһϵÁÐ΢·þÎñ½â¾ö·½°¸»ò¿ò¼ÜµÄÓÐÐò¼¯ºÏ¡£Ëü½«ÊÐÃæÉϳÉÊìµÄ¡¢¾­¹ýÑéÖ¤µÄ΢·þÎñ¿ò¼ÜÕûºÏÆðÀ´£¬²¢Í¨¹ý Spring Boot µÄ˼Ïë½øÐÐÔÙ·â×°£¬ÆÁ±Îµ÷ÆäÖи´ÔÓµÄÅäÖúÍʵÏÖÔ­Àí£¬×îÖÕΪ¿ª·¢ÈËÔ±ÌṩÁËÒ»Ì×¼òµ¥Ò×¶®¡¢Òײ¿ÊðºÍÒ×ά»¤µÄ·Ö²¼Ê½ÏµÍ³¿ª·¢¹¤¾ß°ü¡£ + +Spring Cloud Öаüº¬ÁË spring-cloud-config¡¢spring-cloud-bus µÈ½ü 20 ¸ö×ÓÏîÄ¿£¬ÌṩÁË·þÎñÖÎÀí¡¢·þÎñÍø¹Ø¡¢ÖÇÄÜ·ÓÉ¡¢¸ºÔؾùºâ¡¢¶Ï·Æ÷¡¢¼à¿Ø¸ú×Ù¡¢·Ö²¼Ê½ÏûÏ¢¶ÓÁС¢ÅäÖùÜÀíµÈÁìÓòµÄ½â¾ö·½°¸¡£ + +Spring Cloud ²¢²»ÊÇÒ»¸öÄÃÀ´¼´¿ÉÓõĿò¼Ü£¬ËüÊÇÒ»ÖÖ΢·þÎñ¹æ·¶£¬¹²ÓÐÒÔÏ 2 ´úʵÏÖ£º + +* µÚÒ»´úʵÏÖ£ºSpring Cloud Netflix +* µÚ¶þ´úʵÏÖ£ºSpring Cloud Alibaba + +ÕâÀïÎÒÃǽéÉÜµÄ Spring Cloud ÌØÖ¸ Spring Cloud µÄµÚÒ»´úʵÏÖ¡£ + +## Spring Cloud ³£ÓÃ×é¼þ + +Spring Cloud °üÀ¨ Spring Cloud Gateway¡¢Spring Cloud Config¡¢Spring Cloud Bus µÈ½ü 20 ¸ö·þÎñ×é¼þ£¬ÕâЩ×é¼þÌṩÁË·þÎñÖÎÀí¡¢·þÎñÍø¹Ø¡¢ÖÇÄÜ·ÓÉ¡¢¸ºÔؾùºâ¡¢ÈÛ¶ÏÆ÷¡¢¼à¿Ø¸ú×Ù¡¢·Ö²¼Ê½ÏûÏ¢¶ÓÁС¢ÅäÖùÜÀíµÈÁìÓòµÄ½â¾ö·½°¸¡£ + +Spring Cloud µÄ³£ÓÃ×é¼þÈçϱíËùʾ¡£ + +| Spring Cloud ×é¼þ | ÃèÊö | +| ---------------------------- | ------------------------------------------------------------ | +| Spring Cloud Netflix Eureka | Spring Cloud Netflix ÖеķþÎñÖÎÀí×é¼þ£¬°üº¬·þÎñ×¢²áÖÐÐÄ¡¢·þÎñ×¢²áÓë·¢ÏÖ»úÖÆµÄʵÏÖ¡£ | +| Spring Cloud Netflix Ribbon | Spring Cloud Netflix ÖеķþÎñµ÷ÓúͿͻ§¶Ë¸ºÔؾùºâ×é¼þ¡£ | +| Spring Cloud Netflix Hystrix | È˳ơ°ºÀÖí¸ç¡±£¬Spring Cloud Netflix µÄÈÝ´í¹ÜÀí×é¼þ£¬Îª·þÎñÖгöÏÖµÄÑӳٺ͹ÊÕÏÌṩǿ´óµÄÈÝ´íÄÜÁ¦¡£ | +| Spring Cloud Netflix Feign | »ùÓÚ Ribbon ºÍ Hystrix µÄÉùÃ÷ʽ·þÎñµ÷ÓÃ×é¼þ¡£ | +| Spring Cloud Netflix Zuul | Spring Cloud Netflix ÖеÄÍø¹Ø×é¼þ£¬ÌṩÁËÖÇÄÜ·ÓÉ¡¢·ÃÎʹýÂ˵ȹ¦ÄÜ¡£ | +| Spring Cloud Gateway | Ò»¸ö»ùÓÚ Spring 5.0£¬Spring Boot 2.0 ºÍ Project Reactor µÈ¼¼Êõ¿ª·¢µÄÍø¹Ø¿ò¼Ü£¬ËüʹÓà Filter Á´µÄ·½Ê½ÌṩÁËÍø¹ØµÄ»ù±¾¹¦ÄÜ£¬ÀýÈ簲ȫ¡¢¼à¿Ø/Ö¸±êºÍÏÞÁ÷µÈ¡£ | +| Spring Cloud Config | Spring Cloud µÄÅäÖùÜÀí¹¤¾ß£¬Ö§³ÖʹÓà Git ´æ´¢ÅäÖÃÄÚÈÝ£¬ÊµÏÖÓ¦ÓÃÅäÖõÄÍⲿ»¯´æ´¢£¬²¢Ö§³ÖÔÚ¿Í»§¶Ë¶ÔÅäÖýøÐÐˢС¢¼ÓÃÜ¡¢½âÃܵȲÙ×÷¡£ | +| Spring Cloud Bus | Spring Cloud µÄʼþºÍÏûÏ¢×ÜÏߣ¬Ö÷ÒªÓÃÓÚÔÚ¼¯ÈºÖд«²¥Ê¼þ»ò״̬±ä»¯£¬ÒÔ´¥·¢ºóÐøµÄ´¦Àí£¬ÀýÈ綯̬ˢÐÂÅäÖᣠ| +| Spring Cloud Stream | Spring Cloud µÄÏûÏ¢Öмä¼þ×é¼þ£¬Ëü¼¯³ÉÁË Apache Kafka ºÍ RabbitMQ µÈÏûÏ¢Öмä¼þ£¬²¢Í¨¹ý¶¨Òå°ó¶¨Æ÷×÷ΪÖмä²ã£¬ÍêÃÀµØÊµÏÖÁËÓ¦ÓóÌÐòÓëÏûÏ¢Öмä¼þÖ®¼äµÄ¸ôÀ롣ͨ¹ýÏòÓ¦ÓóÌÐò±©Â¶Í³Ò»µÄ Channel ͨµÀ£¬Ê¹µÃÓ¦ÓóÌÐò²»ÐèÒªÔÙ¿¼ÂǸ÷ÖÖ²»Í¬µÄÏûÏ¢Öмä¼þʵÏÖ£¬¾ÍÄÜÇáËɵط¢ËͺͽÓÊÕÏûÏ¢¡£ | +| Spring Cloud Sleuth | Spring Cloud ·Ö²¼Ê½Á´Â·¸ú×Ù×é¼þ£¬Äܹ»ÍêÃÀµÄÕûºÏ Twitter µÄ Zipkin¡£ | + +> ×¢£ºNetflix ÊÇÃÀ¹úµÄÒ»¸öÔÚÏßÊÓÆµÍøÕ¾£¬ËüÊǹ«ÈϵĴó¹æÄ£Éú²ú¼¶Î¢·þÎñµÄ½Ü³öʵ¼ùÕߣ¬Î¢·þÎñ½çµÄÇ̳þ¡£Netflix µÄ¿ªÔ´×é¼þÒѾ­ÔÚÆä´ó¹æÄ£·Ö²¼Ê½Î¢·þÎñ»·¾³Öо­¹ýÁ˶àÄêµÄÉú²úʵսÑéÖ¤£¬³ÉÊìÇÒ¿É¿¿¡£ + +## Spring Boot ºÍ Spring Cloud µÄÇø±ðÓëÁªÏµ + +Spring Boot ºÍ Spring Cloud ¶¼ÊÇ Spring ´ó¼Ò×åµÄÒ»Ô±£¬ËüÃÇÔÚ΢·þÎñ¿ª·¢Öж¼°çÑÝ×ÅÊ®·ÖÖØÒªµÄ½ÇÉ«£¬Á½ÕßÖ®¼ä¼È´æÔÚÇø±ðÒ²´æÔÚÁªÏµ¡£ + +#### 1\. Spring Boot ºÍ Spring Cloud ·Ö¹¤²»Í¬ + +Spring Boot ÊÇÒ»¸ö»ùÓÚ Spring µÄ¿ìËÙ¿ª·¢¿ò¼Ü£¬ËüÄܹ»°ïÖú¿ª·¢ÕßѸËÙ´î Web ¹¤³Ì¡£ÔÚ΢·þÎñ¿ª·¢ÖУ¬Spring Boot רעÓÚ¿ìËÙ¡¢·½±ãµØ¿ª·¢µ¥¸ö΢·þÎñ¡£ + +Spring Cloud ÊÇ΢·þÎñ¼Ü¹¹ÏµÄһվʽ½â¾ö·½°¸¡£Spring Cloud רעÓÚÈ«¾Ö΢·þÎñµÄЭµ÷ºÍÖÎÀí¹¤×÷¡£»»¾ä»°Ëµ£¬Spring Cloud Ï൱ÓÚ΢·þÎñµÄ´ó¹Ü¼Ò£¬¸ºÔ𽫠Spring Boot ¿ª·¢µÄÒ»¸ö¸ö΢·þÎñ¹ÜÀíÆðÀ´£¬²¢ÎªËüÃÇÌṩÅäÖùÜÀí¡¢·þÎñ·¢ÏÖ¡¢¶Ï·Æ÷¡¢Â·ÓÉ¡¢Î¢´úÀí¡¢Ê¼þ×ÜÏß¡¢¾ö²ß¾ºÑ¡ÒÔ¼°·Ö²¼Ê½»á»°µÈ·þÎñ¡£ + +#### 2\. Spring Cloud ÊÇ»ùÓÚ Spring Boot ʵÏÖµÄ + +Spring Cloud ÊÇ»ùÓÚ Spring Boot ʵÏֵġ£Óë Spring Boot ÀàËÆ£¬Spring Cloud ҲΪÌṩÁËһϵÁÐ Starter£¬ÕâЩ Starter ÊÇ Spring Cloud ʹÓà Spring Boot ˼Ïë¶Ô¸÷¸ö΢·þÎñ¿ò¼Ü½øÐÐÔÙ·â×°µÄ²úÎï¡£ËüÃÇÆÁ±ÎÁËÕâЩ΢·þÎñ¿ò¼ÜÖи´ÔÓµÄÅäÖúÍʵÏÖÔ­Àí£¬Ê¹¿ª·¢ÈËÔ±Äܹ»¿ìËÙ¡¢·½±ãµØÊ¹Óà Spring Cloud ´î½¨Ò»Ì×·Ö²¼Ê½Î¢·þÎñϵͳ¡£ + +#### 3\. Spring Boot ºÍ Spring Cloud ÒÀÀµÏîÊýÁ¿²»Í¬ + +Spring Boot ÊôÓÚÒ»ÖÖÇáÁ¿¼¶µÄ¿ò¼Ü£¬¹¹½¨ Spring Boot ¹¤³ÌËùÐèµÄÒÀÀµ½ÏÉÙ¡£ + +Spring Cloud ÊÇһϵÁÐ΢·þÎñ¿ò¼Ü¼¼ÊõµÄ¼¯ºÏÌ壬ËüµÄÿ¸ö×é¼þ¶¼ÐèÒªÒ»¸ö¶ÀÁ¢µÄÒÀÀµÏStarter POM£©£¬Òò´ËÏëÒª¹¹½¨Ò»Ì×ÍêÕûµÄ Spring Cloud ¹¤³ÌÍùÍùÐèÒª´óÁ¿µÄÒÀÀµÏî¡£ + +#### 4\. Spring Cloud ²»ÄÜÍÑÀë Spring Boot µ¥¶ÀÔËÐÐ + +Spring Boot ²»ÐèÒª Spring Cloud£¬¾ÍÄÜÖ±½Ó´´½¨¿É¶ÀÁ¢ÔËÐеŤ³Ì»òÄ£¿é¡£ + +Spring Cloud ÊÇ»ùÓÚ Spring Boot ʵÏֵģ¬Ëü²»ÄܶÀÁ¢´´½¨¹¤³Ì»òÄ£¿é£¬¸ü²»ÄÜÍÑÀë Spring Boot ¶ÀÁ¢ÔËÐС£ + +> ×¢Ò⣺ËäÈ» Spring Boot Äܹ»ÓÃÓÚ¿ª·¢µ¥¸ö΢·þÎñ£¬µ«Ëü²¢²»¾ß±¸¹ÜÀíºÍЭµ÷΢·þÎñµÄÄÜÁ¦£¬Òò´ËËüÖ»ÄÜËãÊÇÒ»¸ö΢·þÎñ¿ìËÙ¿ª·¢¿ò¼Ü£¬¶ø·Ç΢·þÎñ¿ò¼Ü¡£ + +## Spring Cloud °æ±¾ + +Spring Cloud °üº¬ÁËÐí¶à×ÓÏîÄ¿£¨×é¼þ£©£¬ÕâЩ×ÓÏîÄ¿¶¼ÊǶÀÁ¢½øÐÐÄÚÈݸüк͵ü´úµÄ£¬¸÷×Ô¶¼Î¬»¤×Å×Ô¼ºµÄ·¢²¼°æ±¾ºÅ¡£ + +ΪÁ˱ÜÃâ Spring Cloud µÄ°æ±¾ºÅÓëÆä×ÓÏîÄ¿µÄ°æ±¾ºÅ»ìÏý£¬Spring Cloud ûÓвÉÓó£¼ûµÄÊý×Ö°æ±¾ºÅ£¬¶øÊÇͨ¹ýÒÔÏ·½Ê½¶¨Òå°æ±¾ÐÅÏ¢¡£ + +
{version.name} .{version.number}
+ +Spring Cloud °æ±¾ÐÅϢ˵Ã÷ÈçÏ£º + +* **version.name**£º°æ±¾Ãû£¬²ÉÓÃÓ¢¹úÂ׶صØÌúÕ¾µÄÕ¾ÃûÀ´ÃüÃû£¬²¢°´ÕÕ×Öĸ±íµÄ˳Ðò£¨¼´´Ó A µ½ Z£©À´¶ÔÓ¦ Spring Cloud µÄ°æ±¾·¢²¼Ë³Ðò£¬ÀýÈçµÚÒ»¸ö°æ±¾Îª Angel£¬µÚ¶þ¸ö°æ±¾Îª Brixton£¨Ó¢¹úµØÃû£©£¬È»ºóÒÀ´ÎÊÇ Camden¡¢Dalston¡¢Edgware¡¢Finchley¡¢Greenwich¡¢Hoxton µÈ¡£ +* **version.number**£º°æ±¾ºÅ£¬Ã¿Ò»¸ö°æ±¾µÄ Spring Cloud ÔÚ¸üÐÂÄÚÈÝ»ýÀÛµ½Ò»¶¨µÄÁ¿¼¶»òÓÐÖØ´ó BUG ÐÞ¸´Ê±£¬¾Í»á·¢²¼Ò»¸ö¡°service releases¡±°æ±¾£¬¼ò³Æ SRX °æ±¾£¬ÆäÖÐ X Ϊһ¸öµÝÔöµÄÊý×Ö£¬ÀýÈç Hoxton.SR8 ¾Í±íʾ Hoxton µÄµÚ 8 ¸ö Release °æ±¾¡£ + +## Spring Cloud °æ±¾Ñ¡Ôñ + +ÔÚʹÓà Spring Boot + Spring Cloud ½øÐÐ΢·þÎñ¿ª·¢Ê±£¬ÎÒÃÇÐèÒª¸ù¾ÝÏîÄ¿ÖÐ Spring Boot µÄ°æ±¾À´¾ö¶¨ Spring Cloud °æ±¾£¬·ñÔò»á³öÏÖÐí¶àÒâÏë²»µ½µÄ´íÎó¡£ + +Spring Boot Óë Spring Cloud µÄ°æ±¾¶ÔÓ¦¹ØÏµÈçÏÂ±í£¨²Î¿¼×Ô[ ](https://spring.io/projects/spring-cloud)[Spring Cloud ¹ÙÍø](http://spring.io/projects/spring-cloud)£©¡£ + +| Spring Cloud | Spring Boot | +| ------------------- | ---------------------------------------------- | +| 2020.0.x £¨Ilford£© | 2.4.x, 2.5.x £¨´Ó Spring Cloud 2020.0.3 ¿ªÊ¼£© | +| Hoxton | 2.2.x, 2.3.x £¨´Ó Spring Cloud SR5 ¿ªÊ¼£© | +| Greenwich | 2.1.x | +| Finchley | 2.0.x | +| Edgware | 1.5.x | +| Dalston | 1.5.x | + +> ×¢Ò⣺Spring Cloud ¹Ù·½ÒѾ­Í£Ö¹¶Ô Dalston¡¢Edgware¡¢Finchley ºÍ Greenwich µÄ°æ±¾¸üС£ + +³ýÁËÉϱíÖÐչʾµÄ°æ±¾¶ÔÓ¦¹ØÏµÖ®Í⣬ÎÒÃÇ»¹¿ÉÒÔʹÓÃä¯ÀÀÆ÷·ÃÎÊ [https://start.spring.io/actuator/info](https://start.spring.io/actuator/info)£¬»ñÈ¡ Spring Cloud Óë Spring Boot µÄ°æ±¾¶ÔÓ¦¹ØÏµ£¨JSON °æ£©¡£ + + + + + + + + + + +```` + +{ + ¡­¡­ + "bom-ranges":{ + ¡­¡­ + "spring-cloud":{ + "Hoxton.SR12":"Spring Boot >=2.2.0.RELEASE and <2.4.0.M1", + "2020.0.4":"Spring Boot >=2.4.0.M1 and <2.5.6-SNAPSHOT", + "2020.0.5-SNAPSHOT":"Spring Boot >=2.5.6-SNAPSHOT and <2.6.0-M1", + "2021.0.0-M1":"Spring Boot >=2.6.0.M1 and <2.6.0-SNAPSHOT", + "2021.0.0-SNAPSHOT":"Spring Boot >=2.6.0-SNAPSHOT" + }, + ¡­¡­ + }, + ¡­¡­ +} + +```` +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" new file mode 100644 index 0000000..4fc0f7b --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaNacos.md" @@ -0,0 +1,1212 @@ +Nacos Ó¢ÎÄÈ«³ÆÎª Dynamic Naming and Configuration Service£¬ÊÇÒ»¸öÓɰ¢Àï°Í°ÍÍŶÓʹÓà Java ÓïÑÔ¿ª·¢µÄ¿ªÔ´ÏîÄ¿¡£ + +Nacos ÊÇÒ»¸ö¸üÒ×ÓÚ°ïÖú¹¹½¨ÔÆÔ­ÉúÓ¦ÓõĶ¯Ì¬·þÎñ·¢ÏÖ¡¢ÅäÖúͷþÎñ¹ÜÀíÆ½Ì¨£¨²Î¿¼×Ô [Nacos ¹ÙÍø](https://nacos.io/zh-cn/index.html)£©¡£ + +Nacos µÄÃüÃûÊÇÓÉ 3 ²¿·Ö×é³É£º + +| ×é³É²¿·Ö | È«³Æ | ÃèÊö | +| -------- | ----------------- | ------------------------------------------------------------ | +| Na | naming/nameServer | ¼´·þÎñ×¢²áÖÐÐÄ£¬Óë Spring Cloud Eureka µÄ¹¦ÄÜÀàËÆ¡£ | +| co | configuration | ¼´ÅäÖÃÖÐÐÄ£¬Óë Spring Cloud Config+Spring Cloud Bus µÄ¹¦ÄÜÀàËÆ¡£ | +| s | service | ¼´·þÎñ£¬±íʾ Nacos ʵÏֵķþÎñ×¢²áÖÐÐĺÍÅäÖÃÖÐÐͼÊÇÒÔ·þÎñΪºËÐĵġ£ | + +ÎÒÃÇ¿ÉÒÔ½« Nacos Àí½â³É·þÎñ×¢²áÖÐÐĺÍÅäÖÃÖÐÐĵÄ×éºÏÌ壬Ëü¿ÉÒÔÌæ»» [Eureka](http://c.biancheng.net/springcloud/eureka.html) ×÷Ϊ·þÎñ×¢²áÖÐÐÄ£¬ÊµÏÖ·þÎñµÄ×¢²áÓë·¢ÏÖ£»»¹¿ÉÒÔÌæ»» [Spring Cloud Config](http://c.biancheng.net/springcloud/config.html) ×÷ΪÅäÖÃÖÐÐÄ£¬ÊµÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ + +Nacos ×÷Ϊ·þÎñ×¢²áÖÐÐľ­ÀúÁËÊ®Äꡰ˫ʮһ¡±µÄºé·å¿¼Ñ飬¾ßÓмòµ¥Ò×Óá¢Îȶ¨¿É¿¿¡¢ÐÔÄÜ׿ԽµÈÓŵ㣬¿ÉÒÔ°ïÖúÓû§¸üÃô½Ý¡¢ÈÝÒ׵ع¹½¨ºÍ¹ÜÀí΢·þÎñÓ¦Óᣠ+ +Nacos Ö§³Ö¼¸ºõËùÓÐÖ÷Á÷ÀàÐÍ¡°·þÎñ¡±µÄ·¢ÏÖ¡¢ÅäÖú͹ÜÀí£º + +* [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) +* [gRPC ](https://grpc.io/docs/what-is-grpc/core-concepts#service-definition)& [Dubbo RPC Service](https://dubbo.apache.org/zh/) +* Spring Cloud RESTful Service + +## Nacos µÄÌØÐÔ + +Nacos ÌṩÁËһϵÁмòµ¥Ò×ÓõÄÌØÐÔ£¬Äܹ»°ïÖúÎÒÃÇ¿ìËÙµØÊµÏÖ¶¯Ì¬·þÎñ·¢ÏÖ¡¢·þÎñÅäÖõȹ¦ÄÜ¡£ + +#### ·þÎñ·¢ÏÖ + +Nacos Ö§³Ö»ùÓÚ DNS ºÍ RPC µÄ·þÎñ·¢ÏÖ¡£µ±·þÎñÌṩÕßʹÓÃÔ­Éú SDK¡¢OpenAPI »òÒ»¸ö¶ÀÁ¢µÄ Agent TODO Ïò Nacos ×¢²á·þÎñºó£¬·þÎñÏû·ÑÕß¿ÉÒÔÔÚ Nacos ÉÏͨ¹ý DNS TODO »ò HTTP&API ²éÕÒ¡¢·¢ÏÖ·þÎñ¡£ + +#### ·þÎñ½¡¿µ¼à²â + +Nacos Ìṩ¶Ô·þÎñµÄʵʱ½¡¿µ¼ì²é£¬Äܹ»×èÖ¹ÇëÇó·¢Ë͵½²»½¡¿µÖ÷»ú»ò·þÎñʵÀýÉÏ¡£Nacos »¹ÌṩÁËÒ»¸ö½¡¿µ¼ì²éÒDZíÅÌ£¬Äܹ»°ïÖúÎÒÃǸù¾Ý½¡¿µ×´Ì¬¹ÜÀí·þÎñµÄ¿ÉÓÃÐÔ¼°Á÷Á¿¡£ + +#### ¶¯Ì¬ÅäÖ÷þÎñ + +¶¯Ì¬ÅäÖ÷þÎñ¿ÉÒÔÈÃÎÒÃÇÒÔÖÐÐÄ»¯¡¢Íⲿ»¯ºÍ¶¯Ì¬»¯µÄ·½Ê½£¬¹ÜÀíËùÓл·¾³µÄÓ¦ÓÃÅäÖúͷþÎñÅäÖᣠ+ +¶¯Ì¬ÅäÖÃÏû³ýÁËÅäÖñä¸üÊ±ÖØÐ²¿ÊðÓ¦ÓúͷþÎñµÄÐèÒª£¬ÈÃÅäÖùÜÀí±äµÃ¸ü¼Ó¸ßЧ¡¢Ãô½Ý¡£ + +ÅäÖÃÖÐÐÄ»¯¹ÜÀíÈÃʵÏÖÎÞ״̬·þÎñ±äµÃ¸ü¼òµ¥£¬È÷þÎñ°´Ð赯ÐÔÀ©Õ¹±äµÃ¸üÈÝÒס£ + +Nacos ÌṩÁËÒ»¸ö¼ò½àÒ×ÓÃµÄ UI °ïÖúÎÒÃǹÜÀíËùÓзþÎñºÍÓ¦ÓõÄÅäÖá£Nacos »¹Ìṩ°üÀ¨ÅäÖð汾¸ú×Ù¡¢½ð˿ȸ·¢²¼¡¢Ò»¼ü»Ø¹öÅäÖÃÒÔ¼°¿Í»§¶ËÅäÖøüÐÂ״̬¸ú×ÙÔÚÄÚµÄһϵÁпªÏä¼´ÓõÄÅäÖùÜÀíÌØÐÔ£¬°ïÖúÎÒÃǸü°²È«µØÔÚÉú²ú»·¾³ÖйÜÀíÅäÖñä¸üºÍ½µµÍÅäÖñä¸ü´øÀ´µÄ·çÏÕ¡£ + +#### ¶¯Ì¬ DNS ·þÎñ + +Nacos ÌṩÁ˶¯Ì¬ DNS ·þÎñ£¬Äܹ»ÈÃÎÒÃǸüÈÝÒ×µØÊµÏÖ¸ºÔؾùºâ¡¢Á÷Á¿¿ØÖÆÒÔ¼°Êý¾ÝÖÐÐÄÄÚÍøµÄ¼òµ¥ DNS ½âÎö·þÎñ¡£ + +Nacos ÌṩÁËһЩ¼òµ¥µÄ DNS APIs TODO£¬¿ÉÒÔ°ïÖúÎÒÃǹÜÀí·þÎñµÄ¹ØÁªÓòÃûºÍ¿ÉÓÃµÄ IP:PORT ÁÐ±í¡£ + +#### ·þÎñ¼°ÆäÔªÊý¾Ý¹ÜÀí + +Nacos ÄÜÈÃÎÒÃÇ´Ó΢·þÎñƽ̨½¨ÉèµÄÊӽǹÜÀíÊý¾ÝÖÐÐĵÄËùÓзþÎñ¼°ÔªÊý¾Ý£¬°üÀ¨¹ÜÀí·þÎñµÄÃèÊö¡¢ÉúÃüÖÜÆÚ¡¢·þÎñµÄ¾²Ì¬ÒÀÀµ·ÖÎö¡¢·þÎñµÄ½¡¿µ×´Ì¬¡¢·þÎñµÄÁ÷Á¿¹ÜÀí¡¢Â·Óɼ°°²È«²ßÂÔ¡¢·þÎñµÄ SLA ÒÔ¼° metrics ͳ¼ÆÊý¾Ý¡£ + +## Nacos Á½´ó×é¼þ + +Óë Eureka ÀàËÆ£¬Nacos Ò²²ÉÓà CS£¨Client/Server£¬¿Í»§¶Ë/·þÎñÆ÷£©¼Ü¹¹£¬Ëü°üº¬Á½´ó×é¼þ£¬ÈçÏÂ±í¡£ +¡¢ + +| ×é¼þ | ÃèÊö | ¹¦ÄÜ | +| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| Nacos Server | Nacos ·þÎñ¶Ë£¬Óë Eureka Server ²»Í¬£¬Nacos Server Óɰ¢Àï°Í°ÍÍŶÓʹÓà Java ÓïÑÔ±àд²¢½« Nacos Server µÄÏÂÔØµØÖ·¸øÓû§£¬Óû§Ö»ÐèÒªÖ±½ÓÏÂÔØ²¢ÔËÐм´¿É¡£ | Nacos Server ¿ÉÒÔ×÷Ϊ·þÎñ×¢²áÖÐÐÄ£¬°ïÖú Nacos Client ʵÏÖ·þÎñµÄ×¢²áÓë·¢ÏÖ¡£ | +| Nacos Server ¿ÉÒÔ×÷ΪÅäÖÃÖÐÐÄ£¬°ïÖú Nacos Client ÔÚ²»ÖØÆôµÄÇé¿öÏ£¬ÊµÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ | | | +| Nacos Client | Nacos ¿Í»§¶Ë£¬Í¨³£Ö¸µÄÊÇ΢·þÎñ¼Ü¹¹Öеĸ÷¸ö·þÎñ£¬ÓÉÓû§×Ô¼º´î½¨£¬¿ÉÒÔʹÓöàÖÖÓïÑÔ±àд¡£ | Nacos Client ͨ¹ýÌí¼ÓÒÀÀµ spring-cloud-starter-alibaba-nacos-discovery£¬ÔÚ·þÎñ×¢²áÖÐÐÄ£¨Nacos Server£©ÖÐʵÏÖ·þÎñµÄ×¢²áÓë·¢ÏÖ¡£ | +| Nacos Client ͨ¹ýÌí¼ÓÒÀÀµ spring-cloud-starter-alibaba-nacos-config£¬ÔÚÅäÖÃÖÐÐÄ£¨Nacos Server£©ÖÐʵÏÖÅäÖõĶ¯Ì¬Ë¢Ð¡£ | | | + +## Nacos ·þÎñ×¢²áÖÐÐÄ + +Nacos ×÷Ϊ·þÎñ×¢²áÖÐÐÄ¿ÉÒÔʵÏÖ·þÎñµÄ×¢²áÓë·¢ÏÖ£¬Á÷³ÌÈçÏÂͼ¡£ + +![Nacos ·þÎñ×¢²áÓë·¢ÏÖ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022563360-0.png) +ͼ1£ºNacos ·þÎñ×¢²áÓë·¢ÏÖ + +ÔÚͼ 1 Öй²Éæ¼°µ½ÒÔÏ 3 ¸ö½ÇÉ«£º + +* ·þÎñ×¢²áÖÐÐÄ£¨Register Service£©£ºËüÊÇÒ»¸ö Nacos Server£¬¿ÉÒÔΪ·þÎñÌṩÕߺͷþÎñÏû·ÑÕßÌṩ·þÎñ×¢²áºÍ·¢ÏÖ¹¦ÄÜ¡£ +* ·þÎñÌṩÕߣ¨Provider Service£©£ºËüÊÇÒ»¸ö Nacos Client£¬ÓÃÓÚ¶ÔÍâ·þÎñ¡£Ëü½«×Ô¼ºÌṩµÄ·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐÄ£¬ÒÔ¹©·þÎñÏû·ÑÕß·¢Ïֺ͵÷Óᣠ+* ·þÎñÏû·ÑÕߣ¨Consumer Service£©£ºËüÊÇÒ»¸ö Nacos Client£¬ÓÃÓÚÏû·Ñ·þÎñ¡£Ëü¿ÉÒÔ´Ó·þÎñ×¢²áÖÐÐÄ»ñÈ¡·þÎñÁÐ±í£¬µ÷ÓÃËùÐèµÄ·þÎñ¡£ + +Nacos ʵÏÖ·þÎñ×¢²áÓë·¢ÏÖµÄÁ÷³ÌÈçÏ£º + +1. ´Ó Nacos ¹Ù·½ÌṩµÄÏÂÔØÒ³ÃæÖУ¬ÏÂÔØ Nacos Server ²¢ÔËÐС£ +2. ·þÎñÌṩÕß Nacos Client Æô¶¯Ê±£¬»á°Ñ·þÎñÒÔ·þÎñÃû£¨spring.application.name£©µÄ·½Ê½×¢²áµ½·þÎñ×¢²áÖÐÐÄ£¨Nacos Server£©£» +3. ·þÎñÏû·ÑÕß Nacos Client Æô¶¯Ê±£¬Ò²»á½«×Ô¼ºµÄ·þÎñ×¢²áµ½·þÎñ×¢²áÖÐÐÄ£» +4. ·þÎñÏû·ÑÕßÔÚ×¢²á·þÎñµÄͬʱ£¬Ëü»¹»á´Ó·þÎñ×¢²áÖÐÐÄ»ñȡһ·Ý·þÎñ×¢²áÁбíÐÅÏ¢£¬¸ÃÁбíÖаüº¬ÁËËùÓÐ×¢²áµ½·þÎñ×¢²áÖÐÐÄÉϵķþÎñµÄÐÅÏ¢£¨°üÀ¨·þÎñÌṩÕߺÍ×ÔÉíµÄÐÅÏ¢£©£» +5. ÔÚ»ñÈ¡ÁË·þÎñÌṩÕßµÄÐÅÏ¢ºó£¬·þÎñÏû·ÑÕßͨ¹ý HTTP »òÏûÏ¢Öмä¼þÔ¶³Ìµ÷Ó÷þÎñÌṩÕßÌṩµÄ·þÎñ¡£ + +#### °²×°ºÍÔËÐÐ Nacos Server + +ÏÂÃæÎÒÃÇÒÔ Nacos 2.0.3 ΪÀý£¬ÑÝʾÏÂÈçºÎ°²×°ºÍÔËÐÐ Nacos Server£¬²½ÖèÈçÏ¡£ + +1\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ [Nacos Server ÏÂÔØÒ³Ãæ](https://github.com/alibaba/nacos/releases/tag/2.0.3)£¬²¢ÔÚÒ³Ãæ×îÏ·½µã»÷Á´½Ó nacos-server-2.0.3.zip£¬ÈçÏÂͼ¡£ + +![Nacos ÏÂÔØ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022562c6-1.png) +ͼ2£ºNacos Server ÏÂÔØ + +2\. ÏÂÔØÍê³Éºó£¬½âѹ nacos-server-2.0.3.zip£¬Ä¿Â¼½á¹¹ÈçÏ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022563922-2.png) +ͼ3£ºNacos Server Ŀ¼½á¹¹ + +Nacos Server ϸ÷Ŀ¼˵Ã÷ÈçÏ£º + +* bin£ºÓÃÓÚ´æ·Å Nacos µÄ¿ÉÖ´ÐÐÃüÁî¡£ +* conf£ºÓÃÓÚ´æ·Å Nacos ÅäÖÃÎļþ¡£ +* target£ºÓÃÓÚ´æ·Å Nacos Ó¦ÓÃµÄ jar °ü¡£ + +3\. ´ò¿ªÃüÁîÐд°¿Ú£¬Ìø×ªµ½ Nacos Server °²×°Ä¿Â¼µÄ bin Ï£¬Ö´ÐÐÒÔÏÂÃüÁÒÔµ¥»úģʽÆô¶¯ Nacos Server¡£ + +startup.cmd -m standalone + +4\. Nacos Server Æô¶¯ÈÕÖ¾ÈçÏ¡£ + +"nacos is starting with standalone" + + + ,--. + ,--.'| + + ,--,: : | Nacos 2.0.3 +,`--.'`| ' : ,---. Running in stand alone mode, All function modules +| : : | | ' ,'\ .--.--. Port: 8848 +: | \ | : ,--.--. ,---. / / | / / ' Pid: 27512 +| : ' '; | / \ / \. ; ,. :| : /`./ Console: http://192.168.3.138:8848/nacos/index.html +' ' ;. ;.--. .-. | / / '' | |: :| : ;_ +| | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io +' : | ; .' ," .--.; |' ; :__| : | `----. \ +| | '`--' / / ,. |' | '.'|\ \ / / /`--' / +' : | ; : .' \ : : `----' '--'. / +; |.' | , .-./\ \ / `--'---' +'---' `--`---' `----' +```` +2021-11-08 16:16:38,877 INFO Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@5ab9b447' of type [org.springframework.security.access.expression.method +.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) +2021-11-08 16:16:38,884 INFO Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by al +l BeanPostProcessors (for example: not eligible for auto-proxying) +2021-11-08 16:16:40,001 INFO Tomcat initialized with port(s): 8848 (http) +2021-11-08 16:16:40,713 INFO Root WebApplicationContext: initialization completed in 14868 ms +2021-11-08 16:16:52,351 INFO Initializing ExecutorService 'applicationTaskExecutor' +2021-11-08 16:16:52,560 INFO Adding welcome page: class path resource [static/index.html] +2021-11-08 16:16:54,239 INFO Creating filter chain: Ant [pattern='/**'], [] +2021-11-08 16:16:54,344 INFO Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7dd611c8, org.springframework.security.web.con +text.SecurityContextPersistenceFilter@5c7668ba, org.springframework.security.web.header.HeaderWriterFilter@fb713e7, org.springframework.security.web.csrf.CsrfFilter@6ec7bce0, org.springframework.secur +ity.web.authentication.logout.LogoutFilter@7d9ba6c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@158f4cfe, org.springframework.security.web.servletapi.SecurityContextHolderAwa +reRequestFilter@6c6333cd, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5d425813, org.springframework.security.web.session.SessionManagementFilter@13741d5a, org.springf +ramework.security.web.access.ExceptionTranslationFilter@3727f0ee] +2021-11-08 16:16:54,948 INFO Initializing ExecutorService 'taskScheduler' +2021-11-08 16:16:54,977 INFO Exposing 16 endpoint(s) beneath base path '/actuator' +2021-11-08 16:16:55,309 INFO Tomcat started on port(s): 8848 (http) with context path '/nacos' +2021-11-08 16:16:55,319 INFO Nacos started successfully in stand alone mode. use embedded storage +```` +5\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:8848/nacos¡±£¬Ìø×ªµ½ Nacos Server µÇÂ½Ò³Ãæ£¬ÈçÏÂͼ¡£ + +![Nacos µÇÂ½Ò³Ãæ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022562045-3.png) +ͼ4£ºNacos Server µÇÂ½Ò³Ãæ + +6\. ÔڵǽҳÊäÈëµÇ¼ÃûºÍÃÜÂ루ĬÈ϶¼ÊÇ nacos£©£¬µã»÷Ìá½»°´Å¥£¬Ìø×ªµ½ Nacos Server ¿ØÖÆÌ¨Ö÷Ò³£¬ÈçÏÂͼ¡£ + +![Nacos Server Ö÷Ò³](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225633C-4.png) +ͼ5£ºNacos Server ¿ØÖÆÌ¨ + +×Ô´Ë£¬ÎÒÃǾÍÍê³ÉÁË Nacos Server µÄÏÂÔØ¡¢°²×°ºÍÔËÐй¤×÷¡£ + +#### ´î½¨·þÎñÌṩÕß + +½ÓÏÂÀ´£¬ÎÒÃÇÀ´´î½¨Ò»¸ö·þÎñÌṩÕߣ¬²½ÖèÈçÏ¡£ + +1. ´´½¨Ò»¸öÃûΪ spring-cloud-alibaba-demo µÄ Maven ¹¤³Ì £¬¸Ã¹¤³ÌµÄ pom.xml ÄÚÈÝÈçÏ¡£ + + + + +```` + + + 4.0.0 + pom + + org.springframework.boot + spring-boot-starter-parent + 2.5.6 + + + net.biancheng.c + spring-cloud-alibaba-demo + 1.0-SNAPSHOT + + + + 8 + 8 + UTF-8 + 1.8 + 1.8 + 4.12 + 1.2.17 + 1.16.18 + 2020.0.4 + + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + 2021.1 + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + +```` + + + + +> Ôڸù¤³ÌµÄ pom.xml ÖУ¬ÎÒÃÇͨ¹ý dependencyManagement ¶Ô Spring Cloud Alibaba µÄ°æ±¾ÐÅÏ¢½øÐйÜÀí£¬¸Ã¹¤³Ìϵĸ÷¸ö×ÓÄ£¿éÔÚÒýÈë Spring Cloud Alibaba µÄ¸÷¸ö×é¼þʱ¾Í²»Òªµ¥¶ÀÖ¸¶¨°æ±¾ºÅÁË¡£ + +2\. ÔÚ spring-cloud-alibaba-demo Ï£¬´´½¨Ò»¸öÃûΪ spring-cloud-alibaba-provider-8001 µÄ Spring Boot Ä£¿é£¬²¢ÔÚÆä pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ£¬ÄÚÈÝÈçÏ¡£ + + + + +```` + + + 4.0.0 + + + net.biancheng.c + 1.0-SNAPSHOT + spring-cloud-alibaba-demo + + + + net.biancheng.c + spring-cloud-alibaba-provider-8001 + 0.0.1-SNAPSHOT + spring-cloud-alibaba-provider-8001 + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + +```` + + + +3\. ÔÚ spring-cloud-alibaba-provider-8001 µÄÅäÖÃÎļþ application.properties ÖÐÌí¼ÓÒÔÏÂÅäÖã¬ÄÚÈÝÈçÏ¡£ +```` +#¶Ë¿ÚºÅ +server.port=8001 +#·þÎñÃû +spring.application.name=spring-cloud-alibaba-provider +#Nacos Server µÄµØÖ· +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +management.endpoints.web.exposure.include=* +```` + +4\. ÔÚ net.biacheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ DeptController µÄ Controller À࣬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c.controller; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +public class DeptController { + @Value("${server.port}") + private String serverPort; + + @GetMapping(value = "/dept/nacos/{id}") + public String getPayment(@PathVariable("id") Integer id) { + return "

cÓïÑÔÖÐÎÄÍøÌáÐÑÄú£¬·þÎñ·ÃÎʳɹ¦£¡

·þÎñÃû£ºspring-cloud-alibaba-provider
¶Ë¿ÚºÅ£º " + serverPort + "
´«ÈëµÄ²ÎÊý£º" + id; + } + +} + +```` + + + +5\. ÔÚ spring-cloud-alibaba-provider-8001 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableDiscoveryClient ×¢½â¿ªÆô Nacos ·þÎñ·¢ÏÖ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + + +package net.biancheng.c; + +```` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient //¿ªÆô·þÎñ·¢ÏÖ¹¦ÄÜ +public class SpringCloudAlibabaProvider8001Application { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudAlibabaProvider8001Application.class, args); + } + +} +```` + + + + +6\. Æô¶¯ spring-cloud-alibaba-provider-8001£¬Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:8001/dept/nacos/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![·ÃÎÊ·þÎñÌṩÕߵķþÎñ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225C033-5.png) +ͼ6£º·þÎñÌṩÕß + +7\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:8848/nacos¡±£¬²é¿´¡°·þÎñ¹ÜÀí¡±Ïµġ°·þÎñÁÐ±í¡±£¬ÈçÏÂͼ¡£ + +[![Nacos ·þÎñÁбí](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225Ab5-6.png)](http://new-local.weixueyuan.net/uploads/allimg/211108/6-21110QJ12b02.png) +ͼ7£º·þÎñ×¢²áÁбí + +´Óͼ 7 ¿ÉÒÔ¿´µ½£¬ÎÒÃǴµÄ·þÎñÌṩÕß spring-cloud-alibaba-provider-8001 ËùÌṩµÄ·þÎñÒѾ­±»×¢²áµ½ÁË Nacos Server ÉÏÁË¡£ + +#### ´î½¨·þÎñÏû·ÑÕß + +ÏÂÃæ£¬ÎÒÃǾÍÀ´´î½¨Ò»¸ö·þÎñÏû·ÑÕßÀ´Ïû·Ñ spring-cloud-alibaba-provider-8001 ÌṩµÄ·þÎñ£¬²½ÖèÈçÏ¡£ + +1\. ÔÚÖ÷¹¤³Ì spring-cloud-alibaba-demo Ï£¬´´½¨Ò»¸öÃûΪ spring-cloud-alibaba-consumer-nacos-8801 µÄ Spring Boot Ä£¿é£¬²¢ÔÚÆä pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + +```` + + + + 4.0.0 + + net.biancheng.c + 1.0-SNAPSHOT + spring-cloud-alibaba-demo + + net.biancheng.c + spring-cloud-alibaba-consumer-nacos-8081 + 0.0.1-SNAPSHOT + spring-cloud-alibaba-consumer-nacos-8081 + Demo project for Spring Boot + + 1.8 + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-loadbalancer + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + +```` + + + + +> ×¢Ò⣺ÓÉÓÚ Netflix Ribbon ÒѾ­½øÈëÍ£¸üά»¤×´Ì¬£¬Nacos Discovery ÒѾ­Í£ÁË¶Ô Ribbon µÄÖ§³Ö£¬Òò´ËÎÒÃÇÐèÒªÔÚ·þÎñÏû·ÑÕßµÄ pom.xml ÖÐÒýÈë spring-cloud-loadbalancer ²ÅÄܵ÷Ó÷þÎñÌṩÕßÌṩµÄ·þÎñ¡£ + +2\. ÔÚ spring-cloud-alibaba-consumer-nacos-8801 µÄÅäÖÃÎļþ application.yml ÖУ¬Ìí¼ÓÒÔÏÂÅäÖᣠ+ + +```` + + +server: + port: 8801 #¶Ë¿ÚºÅ +spring: + application: + name: spring-cloud-alibaba-consumer #·þÎñÃû + cloud: + nacos: + discovery: + server-addr: localhost:8848 #Nacos server µÄµØÖ· +```` + +#ÒÔÏÂÅäÖÃÐÅÏ¢²¢²»ÊÇĬÈÏÅäÖ㬶øÊÇÎÒÃÇ×Ô¶¨ÒåµÄÅäÖã¬Ä¿µÄÊDz»ÔÚ Controller ÄÚÓ²±àÂë·þÎñÌṩÕߵķþÎñÃû +```` +service-url: + nacos-user-service: http://spring-cloud-alibaba-provider #·þÎñÌṩÕߵķþÎñÃû +```` + + + + +3\. ÔÚ spring-cloud-alibaba-consumer-nacos-8801 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableDiscoveryClient ×¢½â¿ªÆô·þÎñ·¢ÏÖ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient // ¿ªÆô·þÎñ×¢²áÓë·¢ÏÖ¹¦ÄÜ +public class SpringCloudAlibabaConsumerNacos8801Application { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudAlibabaConsumerNacos8081Application.class, args); + } + +} +```` + + + + +4\. ÔÚ net.biancheng.c.config °üÏ´´½¨Ò»¸öÃûΪ ApplicationContextBean µÄÅäÖÃÀ࣬²¢Ê¹Óà @LoadBalanced ×¢½âÓë Ribbon ½øÐм¯³É¿ªÆô¸ºÔؾùºâ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c.config; + + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class ApplicationContextBean { + + @Bean + @LoadBalanced //Óë Ribbon ¼¯³É£¬²¢¿ªÆô¸ºÔؾùºâ¹¦ÄÜ + public RestTemplate getRestTemplate() { + return new RestTemplate(); + } + +} + +```` + + + +5\. ÔÚ net.biancheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ DeptController_Consumer µÄ Controller À࣬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c.controller; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; + +@RestController +@Slf4j +public class DeptController_Consumer { + + @Resource + private RestTemplate restTemplate; + + @Value("${service-url.nacos-user-service}") + private String serverURL; //·þÎñÌṩÕߵķþÎñÃû + + @GetMapping("/consumer/dept/nacos/{id}") + public String paymentInfo(@PathVariable("id") Long id) { + return restTemplate.getForObject(serverURL + "/dept/nacos/" + id, String.class); + } + +} +```` + + + + +6\. Æô¶¯ spring-cloud-alibaba-consumer-nacos-8801£¬²é¿´ Nacos Server µÄ·þÎñÁÐ±í£¬ÈçÏÂͼ¡£ + +[![·þÎñÏû·ÑÕßÕâ Nacos Server ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225621N-7.png)](http://new-local.weixueyuan.net/uploads/allimg/211109/6-21110Z91435H1.png) +ͼ8£º·þÎñÏû·ÑÕß + +7\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:8801/consumer/dept/nacos/1¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Nacos ·þÎñÏû·ÑÕßÏû·Ñ·þÎñ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225B156-8.png) +ͼ9£º·þÎñÏû·ÑÕßµ÷Ó÷þÎñ + +## Nacos ÅäÖÃÖÐÐÄ + +Nacos Server »¹¿ÉÒÔ×÷ΪÅäÖÃÖÐÐÄ£¬¶Ô Spring Cloud Ó¦ÓõÄÍⲿÅäÖýøÐÐͳһµØ¼¯Öл¯¹ÜÀí¡£¶øÎÒÃÇÖ»ÐèÒªÔÚÓ¦ÓÃµÄ POM ÎļþÖÐÒýÈë spring-cloud-starter-alibaba-nacos-config ¼´¿ÉʵÏÖÅäÖõĻñÈ¡Ó붯̬ˢС£ + +´ÓÅäÖùÜÀíµÄ½Ç¶È¿´£¬Nacos ¿ÉÒÔ˵ÊÇ Spring Cloud Config µÄÌæ´ú·½°¸£¬µ«Ïà±ÈºóÕß Nacos µÄʹÓøü¼òµ¥£¬²Ù×÷²½ÖèÒ²¸üÉÙ¡£ + +½ÓÏÂÀ´£¬ÎÒÃÇͨ¹ýÒ»¸öʵÀýÀ´ÑÝʾÏ Nacos ÊÇÈçºÎʵÏÖÅäÖõÄͳһ¹ÜÀíºÍ¶¯Ì¬Ë¢Ðµġ£ + +1\. ÔÚÖ÷¹¤³Ì spring-cloud-alibaba-demo Ï£¬´´½¨Ò»¸öÃûΪ spring-cloud-alibaba-config-client-3377 µÄ Spring Boot Ä£¿é£¬²¢ÔÚÆä pom.xml ÖÐÌí¼ÓÒÔÏÂÒÀÀµ¡£ + + + +```` + + + + 4.0.0 + + net.biancheng.c + 1.0-SNAPSHOT + spring-cloud-alibaba-demo + + net.biancheng.c + spring-cloud-alibaba-config-client-3377 + 0.0.1-SNAPSHOT + spring-cloud-alibaba-nacos-config-client-3377 + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + +```` + + + +> ×¢Ò⣺ÎÒÃÇʹÓÃµÄ Spring Cloud 2020 °æ±¾Ä¬Èϲ»ÆôÓà bootstrap£¬ÈôÏëÒªÔÚÓ¦ÓÃÆô¶¯Ê±¼ÓÔØ bootstrap ÅäÖã¨ÀýÈç bootstrap.yml »ò bootstrap.properties£©£¬¾ÍÐèÒªÎÒÃÇÔÚ pom.xml ÖÐÏÔʽµÄÒýÈë spring-cloud-starter-bootstrap ÒÀÀµ¡£ + +2\. ÔÚ spring-cloud-alibaba-config-client-3377 µÄÀà·¾¶£¨ÀýÈç /resources Ŀ¼£©Ï£¬Ìí¼ÓÒ»¸ö bootstrap.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + +```` +server: + port: 3377 #¶Ë¿ÚºÅ +spring: + application: + name: config-client #·þÎñÃû + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 #Nacos·þÎñ×¢²áÖÐÐĵØÖ· + + + config: + server-addr: 127.0.0.1:8848 #Nacos×÷ΪÅäÖÃÖÐÐĵØÖ· + file-extension: yaml #Ö¸¶¨yaml¸ñʽµÄÅäÖà + +```` + + + +3\. ÔÚ spring-cloud-alibaba-config-client-3377 µÄÀà·¾¶£¨ÀýÈç /resources Ŀ¼£©Ï£¬Ìí¼ÓÒ»¸ö application.yml£¬ÅäÖÃÄÚÈÝÈçÏ¡£ + + + + +```` +spring: + profiles: + active: dev #¼¤»î dev µÄÅäÖà + +```` + + + + +4\. ÔÚ net.biancheng.c.controller °üÏ£¬´´½¨Ò»¸öÃûΪ ConfigClientController µÄ Controller À࣬²¢ÔÚ¸ÃÀàÉÏʹÓà @RefreshScope ×¢½âʵÏÖÅäÖõÄ×Ô¶¯¸üУ¬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c.controller; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RefreshScope +public class ConfigClientController { + + @Value("${config.info}") + private String ConfigInfo; + + @GetMapping("/config/info") + public String getConfigInfo(){ + return ConfigInfo; + } + +} +```` + + + + +5\. ÔÚ spring-cloud-alibaba-config-client-3377 µÄÖ÷Æô¶¯ÀàÉÏ£¬Ê¹Óà @EnableDiscoveryClient ×¢½â¿ªÆô·þÎñ·¢ÏÖ¹¦ÄÜ£¬´úÂëÈçÏ¡£ + + + + +```` +package net.biancheng.c; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class SpringCloudAlibabaNacosConfigClient3377Application { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudAlibabaNacosConfigClient3377Application.class, args); + } + +} +```` + + + + +6\. Æô¶¯ Nacos Server£¬²¢ÔÚ Nacos Server ¿ØÖÆÌ¨µÄ¡°ÅäÖùÜÀí¡±Ïµġ°ÅäÖÃÁÐ±í¡±ÖУ¬µã»÷¡°+¡±°´Å¥£¬Ð½¨ÈçÏÂÅäÖᣠ+```` +Data ID: config-client-dev.yaml + + +Group : DEFAULT_GROUP + +ÅäÖøñʽ: YAML + +ÅäÖÃÄÚÈÝ: config: + info: c.biancheng.net +```` +ÔÚ Nacos Server ÖУ¬ÅäÖÃµÄ dataId£¨¼´ Data ID£©µÄÍêÕû¸ñʽÈçÏ£º +```` +${prefix}-${spring.profiles.active}.${file-extension} +```` +dataId ¸ñʽÖи÷²ÎÊý˵Ã÷ÈçÏ£º + +* ${prefix}£ºÄ¬ÈÏȡֵΪ΢·þÎñµÄ·þÎñÃû£¬¼´ÅäÖÃÎļþÖÐ spring.application.name µÄÖµ£¬ÎÒÃÇ¿ÉÒÔÔÚÅäÖÃÎļþÖÐͨ¹ýÅäÖà spring.cloud.nacos.config.prefix À´Ö¸¶¨¡£ +* ${spring.profiles.active}£º±íʾµ±Ç°»·¾³¶ÔÓ¦µÄ Profile£¬ÀýÈç dev¡¢test¡¢prod µÈ¡£µ±Ã»ÓÐÖ¸¶¨»·¾³µÄ Profile ʱ£¬Æä¶ÔÓ¦µÄÁ¬½Ó·ûÒ²½«²»´æÔÚ£¬ dataId µÄ¸ñʽ±ä³É ${prefix}.${file-extension}¡£ +* ${file-extension}£º±íʾÅäÖÃÄÚÈݵÄÊý¾Ý¸ñʽ£¬ÎÒÃÇ¿ÉÒÔÔÚÅäÖÃÎļþÖÐͨ¹ýÅäÖÃÏî spring.cloud.nacos.config.file-extension À´ÅäÖã¬ÀýÈç properties ºÍ yaml¡£ + +7\. Æô¶¯ spring-cloud-alibaba-config-client-3377£¬²¢Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:3377/config/info¡±£¬½á¹ûÈçÏÂͼ¡£ + +![Nacos Config](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225B3C-9.png) +ͼ10£ºNacos Config + +8\. ÔÚ Nacos Server ÖУ¬½« config-client-dev.yaml ÖеÄÅäÖÃÐ޸ijÉÈçÏÂÄÚÈÝ¡£ +```` +config: + info: this is c.biancheng.net +```` + +9\. ÔÚ²»ÖØÆô spring-cloud-alibaba-config-client-3377 µÄÇé¿öÏ£¬Ê¹ÓÃä¯ÀÀÆ÷ÔٴηÃÎÊ¡°http://localhost:3377/config/info¡±£¬½á¹ûÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022563A7-10.png) +ͼ11£ºNacos Cofig + +## Nacos Server ¼¯Èº»¯²¿Êð + +ÔÚʵ¼ÊµÄÏîÄ¿¿ª·¢ÖУ¬Ò»¸ö΢·þÎñϵͳÍùÍùÓÉÊ®¼¸£¬¼¸Ê®¸öÉõÖÁ¼¸°Ù¸ö΢·þÎñ×é³É¡£ ÕâЩ·þÎñÈôÈ«²¿×¢²áµ½Í¬Ò»Ì¨ Nacos Server£¬¾Í¼«ÓпÉÄܵ¼Ö Nacos Server ÒòΪ²»¿°Öظº¶ø±ÀÀ££¬×îÖÕµ¼ÖÂÕû¸ö΢·þÎñϵͳ̱»¾¡£½â¾öÕâ¸öÎÊÌâ×îÖ±½ÓµÄ°ì·¨¾ÍÊÇʹÓà Nacos Server ¼¯Èº¡£ + +Nacos Server µÄ¼¯Èº»¯²¿ÊðÓÐÒ»¸öÊ®·ÖÃ÷ÏÔµÄÓŵ㣬ÄǾÍÊÇ¿ÉÒÔ±£ÕÏϵͳµÄ¸ß¿ÉÓÃÐÔ¡£ÔÚ¼¯Èº»¯²¿ÊðÖУ¬Ö»Òª²»ÊÇËùÓÐµÄ Nacos Server ¶¼Í£Ö¹¹¤×÷£¬Nacos Client ¾Í»¹¿ÉÒÔ´Ó¼¯ÈºÖÐÕý³£µÄ Nacos Server ÉÏ»ñÈ¡·þÎñÐÅÏ¢¼°ÅäÖ㬶ø²»»áµ¼ÖÂϵͳµÄÕûÌå̱»¾£¬Õâ¾ÍÊÇ Nacos Server ¼¯Èº»¯²¿ÊðµÄ¸ß¿ÉÓÃÐÔ¡£ + +ÏÂͼչʾÁË Nacos Server ¼¯Èº»¯²¿ÊðµÄ»ù±¾¼Ü¹¹¡£ + +![Nacos Server ¼¯Èº¼Ü¹¹](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1022563543-11.png) +ͼ12£ºNacos Server ¼¯Èº¼Ü¹¹ + +ÏÂÃæÎÒÃÇÒÔ Windows ϵͳΪÀý£¬ÑÝʾÈçºÎ²¿Êð Nacos Server ¼¯Èº¡£ + +1\. ÔÚ MySQL ÖУ¬Ð½¨Ò»¸öÃûΪ nacos_config µÄÊý¾Ý¿âʵÀý£¬²¢ÔÚ¸ÃÊý¾Ý¿âÏÂÖ´ÐÐÒÔÏ SQL Óï¾ä¡£ +```` +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = config_info */ +/******************************************/ +CREATE TABLE `config_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(255) DEFAULT NULL, + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '´´½¨Ê±¼ä', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'ÐÞ¸Äʱ¼ä', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '×â»§×Ö¶Î', + `c_desc` varchar(256) DEFAULT NULL, + `c_use` varchar(64) DEFAULT NULL, + `effect` varchar(64) DEFAULT NULL, + `type` varchar(64) DEFAULT NULL, + `c_schema` text, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; + + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = config_info_aggr */ +/******************************************/ +CREATE TABLE `config_info_aggr` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(255) NOT NULL COMMENT 'group_id', + `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', + `content` longtext NOT NULL COMMENT 'ÄÚÈÝ', + `gmt_modified` datetime NOT NULL COMMENT 'ÐÞ¸Äʱ¼ä', + `app_name` varchar(128) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '×â»§×Ö¶Î', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Ôö¼Ó×â»§×Ö¶Î'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = config_info_beta */ +/******************************************/ +CREATE TABLE `config_info_beta` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '´´½¨Ê±¼ä', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'ÐÞ¸Äʱ¼ä', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `tenant_id` varchar(128) DEFAULT '' COMMENT '×â»§×Ö¶Î', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = config_info_tag */ +/******************************************/ +CREATE TABLE `config_info_tag` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '´´½¨Ê±¼ä', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'ÐÞ¸Äʱ¼ä', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = config_tags_relation */ +/******************************************/ +CREATE TABLE `config_tags_relation` ( + `id` bigint(20) NOT NULL COMMENT 'id', + `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `nid` bigint(20) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`nid`), + UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = group_capacity */ +/******************************************/ +CREATE TABLE `group_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Ö÷¼üID', + `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID£¬¿Õ×Ö·û±íʾÕû¸ö¼¯Èº', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Åä¶î£¬0±íʾʹÓÃĬÈÏÖµ', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'ʹÓÃÁ¿', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'µ¥¸öÅäÖôóСÉÏÏÞ£¬µ¥Î»Îª×Ö½Ú£¬0±íʾʹÓÃĬÈÏÖµ', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '¾ÛºÏ×ÓÅäÖÃ×î´ó¸öÊý£¬£¬0±íʾʹÓÃĬÈÏÖµ', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'µ¥¸ö¾ÛºÏÊý¾ÝµÄ×ÓÅäÖôóСÉÏÏÞ£¬µ¥Î»Îª×Ö½Ú£¬0±íʾʹÓÃĬÈÏÖµ', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '×î´ó±ä¸üÀúÊ·ÊýÁ¿', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '´´½¨Ê±¼ä', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'ÐÞ¸Äʱ¼ä', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='¼¯Èº¡¢¸÷GroupÈÝÁ¿ÐÅÏ¢±í'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = his_config_info */ +/******************************************/ +CREATE TABLE `his_config_info` ( + `id` bigint(64) unsigned NOT NULL, + `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `data_id` varchar(255) NOT NULL, + `group_id` varchar(128) NOT NULL, + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL, + `md5` varchar(32) DEFAULT NULL, + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `src_user` text, + `src_ip` varchar(50) DEFAULT NULL, + `op_type` char(10) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '×â»§×Ö¶Î', + PRIMARY KEY (`nid`), + KEY `idx_gmt_create` (`gmt_create`), + KEY `idx_gmt_modified` (`gmt_modified`), + KEY `idx_did` (`data_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='¶à×â»§¸ÄÔì'; + +/******************************************/ +/* Êý¾Ý¿âÈ«Ãû = nacos_config */ +/* ±íÃû³Æ = tenant_capacity */ +/******************************************/ +CREATE TABLE `tenant_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Ö÷¼üID', + `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Åä¶î£¬0±íʾʹÓÃĬÈÏÖµ', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'ʹÓÃÁ¿', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'µ¥¸öÅäÖôóСÉÏÏÞ£¬µ¥Î»Îª×Ö½Ú£¬0±íʾʹÓÃĬÈÏÖµ', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '¾ÛºÏ×ÓÅäÖÃ×î´ó¸öÊý', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'µ¥¸ö¾ÛºÏÊý¾ÝµÄ×ÓÅäÖôóСÉÏÏÞ£¬µ¥Î»Îª×Ö½Ú£¬0±íʾʹÓÃĬÈÏÖµ', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '×î´ó±ä¸üÀúÊ·ÊýÁ¿', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '´´½¨Ê±¼ä', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'ÐÞ¸Äʱ¼ä', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='×â»§ÈÝÁ¿ÐÅÏ¢±í'; + +CREATE TABLE `tenant_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) default '' COMMENT 'tenant_id', + `tenant_name` varchar(128) default '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint(20) NOT NULL COMMENT '´´½¨Ê±¼ä', + `gmt_modified` bigint(20) NOT NULL COMMENT 'ÐÞ¸Äʱ¼ä', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; + +CREATE TABLE `users` ( +`username` varchar(50) NOT NULL PRIMARY KEY, +`password` varchar(500) NOT NULL, +`enabled` boolean NOT NULL +); + +CREATE TABLE `roles` ( +`username` varchar(50) NOT NULL, +`role` varchar(50) NOT NULL, +UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE +); + +CREATE TABLE `permissions` ( + `role` varchar(50) NOT NULL, + `resource` varchar(255) NOT NULL, + `action` varchar(8) NOT NULL, + UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE +); + +INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); + +INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN'); +```` +2\. ÔÚ Nacos Server °²×°Ä¿Â¼Ï嵀 conf Îļþ¼ÐÖУ¬½« cluster.conf.example ÖØÃüÃûΪ cluster.conf£¬È»ºóÔÚ¸ÃÎļþÖÐÌí¼ÓÒÔÏÂÄÚÈÝ¡£ + + 192.168.3.138:3333 + 192.168.3.138:4444 + 192.168.3.138:5555 + + +ÅäÖÃ˵Ã÷ÈçÏ£º + +* 192.168.138 Ϊ±¾µØµçÄÔÖ÷»úµÄ IP µØÖ·£¬ÕâÀï×îºÃ²»ÒªÐ´³É localhost »ò 127.0.0.1£¬·ñÔò Nacos Server ¼¯Èº¿ÉÄÜ»á´î½¨Ê§°Ü£¡ +* ±¾´Î´î½¨µÄ Nacos Server ¼¯ÈºµÄ¶Ë¿Ú·Ö±ðΪ£º3333¡¢4444¡¢5555¡£ + +3\. ÔÚ config Ŀ¼Ï嵀 application.properties ÖУ¬½« server.port£¨¶Ë¿ÚºÅ£©ÐÞ¸ÄΪ 3333£¬²¢ÔÚ¸ÃÎļþÖÐÌí¼Ó MySQL Êý¾Ý¿âÅäÖ㬾ßÌåÐÞ¸ÄÄÚÈÝÈçÏ¡£ +```` +server.port=3333 +################ MySQL Êý¾Ý¿âÅäÖÃ################## +spring.datasource.platform=mysql + + +db.num=1 +db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai +db.user=root +db.password=root +```` +4\. ½«¸Ã Nacos Server Ŀ¼¸´ÖƵ½ÁíÍâÁ½Ì¨»úÆ÷ÉÏ£¬²¢½«ËüÃǵĶ˿ںŷֱðÐÞ¸ÄΪ£º 4444 ºÍ 5555¡£ + +5\. ÏÂÔØ Nginx£¬²¢ÐÞ¸Ä Nginx ÖÐ conf Ŀ¼Ï嵀 nginx.conf µÄÅäÖã¬ÄÚÈÝÈçÏ¡£ +```` +#user nobody; +worker_processes 1; + + +#error_log logs/error.log; + +#error_log logs/error.log notice; + +#error_log logs/error.log info; + +#pid logs/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + upstream cluster{ + server 127.0.0.1:3333; + server 127.0.0.1:4444; + server 127.0.0.1:5555; + } + + server { + listen 1111; + server_name localhost; + #charset koi8-r; + #access_log logs/host.access.log main; + location / { + #root html; + #index index.html index.htm; + proxy_pass http://cluster; + } + } + +} +```` +6\. Æô¶¯¼¯ÈºÖÐËùÓÐµÄ Nacos Server£¬µ±³öÏÖÒÔÏÂÄÚÈÝʱ£¬±íʾ Nacos Server Æô¶¯³É¹¦¡£ +```` +"nacos is starting with cluster" + + + ,--. + ,--.'| + + ,--,: : | Nacos 2.0.3 +,`--.'`| ' : ,---. Running in cluster mode, All function modules +| : : | | ' ,'\ .--.--. Port: **** +: | \ | : ,--.--. ,---. / / | / / ' Pid: 21592 +| : ' '; | / \ / \. ; ,. :| : /`./ Console: http://192.168.3.138:3333/nacos/index.html +' ' ;. ;.--. .-. | / / '' | |: :| : ;_ +| | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io +' : | ; .' ," .--.; |' ; :__| : | `----. \ +| | '`--' / / ,. |' | '.'|\ \ / / /`--' / +' : | ; : .' \ : : `----' '--'. / +; |.' | , .-./\ \ / `--'---' +'---' `--`---' `----' + +2021-11-09 16:25:00,993 INFO The server IP list of Nacos is [192.168.3.138:3333, 192.168.3.138:4444, 192.168.3.138:5555] + +2021-11-09 16:27:07,318 INFO Nacos is starting... + +2021-11-09 16:27:08,325 INFO Nacos is starting... + +2021-11-09 16:27:09,340 INFO Nacos is starting... + +2021-11-09 16:27:10,343 INFO Nacos is starting... + +2021-11-09 16:27:10,742 INFO Nacos started successfully in cluster mode. use external storage +```` +7\. µ±¼¯ÈºÖеÄËùÓÐ Nacos Server ¶¼Æô¶¯³É¹¦ºó£¬Ë«»÷ Nignx °²×°Ä¿Â¼Ï嵀 nginx.exe£¬Æô¶¯ Nginx¡£ + +![Nginx ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225BO0-12.png) +ͼ13£ºNginx Æô¶¯½Å±¾ + +8\. ʹÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:1111/nacos/¡±£¬Èô³É¹¦·ÃÎÊ Nacos Server µÄ¿ØÖÆÌ¨£¬Ôò˵Ã÷ Nacos ¼¯Èº²¿Êð³É¹¦£¬ÈçÏÂͼ¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225C554-13.png) +ͼ14£ºNacos ¼¯Èº + +9\. ½«Ö÷¹¤³Ì spring-cloud-alibaba-demo ÏÂËùÓÐ×ÓÄ£¿éÅäÖÃÎļþÖÐµÄ Nacos Server µØÖ·Í³Ò»ÐÞ¸ÄΪ£ºlocalhost:1111¡£ÎÒÃÇÒÔ spring-cloud-alibaba-consumer-nacos-8801 ΪÀý£¬ÅäÖÃÎļþ application.yml µÄÅäÖÃÄÚÈÝÈçÏ¡£ + + + + +```` +server: + port: 8801 #¶Ë¿ÚºÅ +spring: + application: + name: spring-cloud-alibaba-consumer #·þÎñÃû + cloud: + nacos: + discovery: + #server-addr: localhost:8848 #µ¥»ú°æ Nacos Server µÄµØÖ· + server-addr: localhost:1111 #¼¯Èº°æ Nacos Server µÄµØÖ· +```` + +#ÒÔÏÂÅäÖÃÐÅÏ¢²¢²»ÊÇĬÈÏÅäÖ㬶øÊÇÎÒÃÇ×Ô¶¨ÒåµÄÅäÖã¬Ä¿µÄÊDz»ÔÚ Controller ÄÚÓ²±àÂë·þÎñÌṩÕߵķþÎñÃû +```` +service-url: + nacos-user-service: http://spring-cloud-alibaba-provider #·þÎñÌṩÕߵķþÎñÃû +```` + + + + +10\. ÖØÆô spring-cloud-alibaba-consumer-nacos-8801£¬²¢Ê¹ÓÃä¯ÀÀÆ÷·ÃÎÊ¡°http://localhost:1111/nacos¡±£¬²é¿´¡°·þÎñ¹ÜÀí¡±Ïµġ°·þÎñÁÐ±í¡±£¬½á¹ûÈçÏÂͼ¡£ + +[![Nacos ¼¯Èº 2](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/10225A913-14.png)](http://new-local.weixueyuan.net/uploads/allimg/211109/6-2111091H412N8.png) +ͼ15£º·þÎñ×¢²áµ½ Nacos Server ¼¯ÈºÉÏ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" new file mode 100644 index 0000000..6cb2d45 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md" @@ -0,0 +1,243 @@ +1. RocketMQ ¼ò½é + +RocketMQ Êǰ¢Àï°Í°Í¿ªÔ´µÄ·Ö²¼Ê½ÏûÏ¢Öмä¼þ¡£Ö§³ÖÊÂÎñÏûÏ¢¡¢Ë³ÐòÏûÏ¢¡¢ÅúÁ¿ÏûÏ¢¡¢¶¨Ê±ÏûÏ¢¡¢ÏûÏ¢»ØËݵȡ£ËüÀïÃæÓм¸¸öÇø±ðÓÚ±ê×¼ÏûÏ¢Öмþ¼äµÄ¸ÅÄÈçGroup¡¢Topic¡¢QueueµÈ¡£ÏµÍ³×é³ÉÔòÓÉProducer¡¢Consumer¡¢Broker¡¢NameServerµÈ¡£ + +RocketMQ ÌØµã + +- Ö§³Ö·¢²¼/¶©ÔÄ£¨Pub/Sub£©ºÍµã¶Ôµã£¨P2P£©ÏûϢģÐÍ£» +- ÔÚÒ»¸ö¶ÓÁÐÖпɿ¿µÄÏȽøÏȳö£¨FIFO£©ºÍÑϸñµÄ˳Ðò´«µÝ£¬RocketMQ ¿ÉÒÔ±£Ö¤ÑϸñµÄÏûϢ˳Ðò£¬¶øActiveMQ ÎÞ·¨±£Ö¤£» +- Ö§³ÖÀ­£¨Pull£©ºÍÍÆ£¨Push£©Á½ÖÖÏûϢģʽ£»Push ºÃÀí½â£¬±ÈÈçÔÚÏû·ÑÕß¶ËÉèÖà Listener »Øµ÷£»¶ø Pull£¬¿ØÖÆÈ¨ÔÚÓÚÓ¦Ó㬼´Ó¦ÓÃÐèÒªÖ÷¶¯µÄµ÷ÓÃÀ­ÏûÏ¢·½·¨´Ó Broker »ñÈ¡ÏûÏ¢£¬ÕâÀïÃæ´æÔÚÒ»¸öÏû·ÑλÖüǼµÄÎÊÌ⣨Èç¹û²»¼Ç¼£¬»áµ¼ÖÂÏûÏ¢ÖØ¸´Ïû·Ñ£©£» +- µ¥Ò»¶ÓÁаÙÍòÏûÏ¢µÄ¶Ñ»ýÄÜÁ¦£»RocketMQ ÌṩÒÚ¼¶ÏûÏ¢µÄ¶Ñ»ýÄÜÁ¦£¬Õâ²»ÊÇÖØµã£¬ÖصãÊǶѻýÁËÒÚ¼¶µÄÏûÏ¢ºó£¬ÒÀÈ»±£³ÖдÈëµÍÑÓ³Ù£» +- Ö§³Ö¶àÖÖÏûϢЭÒ飬Èç JMS¡¢MQTT µÈ£» +- ·Ö²¼Ê½¸ß¿ÉÓõIJ¿Êð¼Ü¹¹£¬Âú×ãÖÁÉÙÒ»´ÎÏûÏ¢´«µÝÓïÒ壻RocketMQ Ô­Éú¾ÍÊÇÖ§³Ö·Ö²¼Ê½µÄ£¬¶øActiveMQ Ô­Éú´æÔÚµ¥µãÐÔ£» +- Ìṩ docker ¾µÏñÓÃÓÚ¸ôÀë²âÊÔºÍÔÆ¼¯Èº²¿Ê𣻠+- ÌṩÅäÖá¢Ö¸±êºÍ¼à¿ØµÈ¹¦ÄܷḻµÄ Dashboard¡£ + +Broker + +Broker Æäʵ¾ÍÊÇ RocketMQ ·þÎñÆ÷£¬¸ºÔð´æ´¢ÏûÏ¢¡¢×ª·¢ÏûÏ¢¡£Broker ÔÚ RocketMQ ϵͳÖиºÔð½ÓÊÕ´ÓÉú²úÕß·¢ËÍÀ´µÄÏûÏ¢²¢´æ´¢¡¢Í¬Ê±ÎªÏû·ÑÕßµÄÀ­È¡ÇëÇó×÷×¼±¸¡£Broker Ò²´æ´¢ÏûÏ¢Ïà¹ØµÄÔªÊý¾Ý£¬°üÀ¨Ïû·ÑÕß×é¡¢Ïû·Ñ½ø¶ÈÆ«ÒÆºÍÖ÷ÌâºÍ¶ÓÁÐÏûÏ¢µÈ¡£ + +Broker Server ÊÇ RocketMQ ÕæÕýµÄÒµÎñºËÐÄ£¬°üº¬Á˶à¸öÖØÒªµÄ×ÓÄ£¿é£º + +- ·ÓÉÄ£¿é£ºÕû¸ö Broker µÄʵÌ壬¸ºÔð´¦ÀíÀ´×Ô clients ¶ËµÄÇëÇó¡£ +- ¿Í»§¶Ë¹ÜÀí£º¸ºÔð¹ÜÀí¿Í»§¶Ë(Producer/Consumer)ºÍά»¤ Consumer µÄ Topic ¶©ÔÄÐÅÏ¢ +- ´æ´¢·þÎñ£ºÌṩ·½±ã¼òµ¥µÄ API ½Ó¿Ú´¦ÀíÏûÏ¢´æ´¢µ½ÎïÀíÓ²Å̺Ͳéѯ¹¦ÄÜ¡£ +- ¸ß¿ÉÓ÷þÎñ£º¸ß¿ÉÓ÷þÎñ£¬Ìṩ Master Broker ºÍ Slave Broker Ö®¼äµÄÊý¾Ýͬ²½¹¦ÄÜ¡£ +- ÏûÏ¢Ë÷Òý·þÎñ£º¸ù¾ÝÌØ¶¨µÄ Message key ¶ÔͶµÝµ½ Broker µÄÏûÏ¢½øÐÐË÷Òý·þÎñ£¬ÒÔÌṩÏûÏ¢µÄ¿ìËÙ²éѯ¡£ + +NameServer + +NameServer ÊÇÒ»¸ö·Ç³£¼òµ¥µÄ Topic ·ÓÉ×¢²áÖÐÐÄ£¬Æä½ÇÉ«ÀàËÆ Dubbo ÖÐµÄ zookeeper£¬Ö§³Ö Broker µÄ¶¯Ì¬×¢²áÓë·¢ÏÖ¡£ + +Ö÷Òª°üÀ¨Á½¸ö¹¦ÄÜ£º + +- Broker ¹ÜÀí£ºNameServer ½ÓÊÜ Broker ¼¯ÈºµÄ×¢²áÐÅÏ¢²¢ÇÒ±£´æÏÂÀ´×÷Ϊ·ÓÉÐÅÏ¢µÄ»ù±¾Êý¾Ý¡£È»ºóÌṩÐÄÌø¼ì²â»úÖÆ£¬¼ì²é Broker ÊÇ·ñ»¹´æ»î£» +- ·ÓÉÐÅÏ¢¹ÜÀí£º¸ø Producer ºÍ Consumer Ìṩ·þÎñ»ñÈ¡ Broker ÁÐ±í¡£Ã¿¸ö NameServer ½«±£´æ¹ØÓÚ Broker ¼¯ÈºµÄÕû¸ö·ÓÉÐÅÏ¢ºÍÓÃÓÚ¿Í»§¶Ë²éѯµÄ¶ÓÁÐÐÅÏ¢¡£È»ºó Producer ºÍ Conumser ͨ¹ý NameServer ¾Í¿ÉÒÔÖªµÀÕû¸ö Broker ¼¯ÈºµÄ·ÓÉÐÅÏ¢£¬´Ó¶ø½øÐÐÏûÏ¢µÄͶµÝºÍÏû·Ñ¡£ + +2. ʹÓà Docker ¿ìËٴ RocketMQ 4.4 + +rocketmq ÐèÒª²¿Êð broker Óë nameserver £¬¿¼Âǵ½·Ö¿ª²¿Êð±È½ÏÂé·³£¬ÕâÀォ»áʹÓà docker-compose¡£ÁíÍ⣬»¹ÐèÒª´î½¨Ò»¸ö web ¿ÉÊÓ»¯¿ØÖÆÌ¨£¬¿ÉÒÔ¼à¿Ø mq ·þÎñ״̬£¬ÒÔ¼°ÏûÏ¢Ïû·ÑÇé¿ö£¬ÕâÀïʹÓà rocketmq-console£¬Í¬Ñù¸Ã³ÌÐòÒ²½«Ê¹Óà docker °²×°¡£ + +1. ÔÚ linux ·þÎñÆ÷ÉÏÑ¡Ôñ²¢½¨Á¢Ä¿Â¼£» + + mkdir rocketmq-docker + ¸´ÖÆ´úÂë + +1. ½øÈë rocketmq-docker Ŀ¼£¬½¨Á¢Ò»¸öÃûΪ broker.conf µÄÅäÖÃÎļþ£¬ÄÚÈÝÈçÏ£º + + # ËùÊô¼¯ÈºÃû³Æ£¬Èç¹û½Úµã½Ï¶à¿ÉÒÔÅäÖöà¸ö + brokerClusterName = DefaultCluster + # brokerÃû³Æ£¬masterºÍslaveʹÓÃÏàͬµÄÃû³Æ£¬±íÃ÷ËûÃǵÄÖ÷´Ó¹ØÏµ + brokerName = broker-a + # 0±íʾMaster£¬´óÓÚ0±íʾ²»Í¬µÄslave + brokerId = 0 + # ±íʾ¼¸µã×öÏûϢɾ³ý¶¯×÷£¬Ä¬ÈÏÊÇÁ賿4µã + deleteWhen = 04 + # ÔÚ´ÅÅÌÉϱ£ÁôÏûÏ¢µÄʱ³¤£¬µ¥Î»ÊÇСʱ + fileReservedTime = 48 + # ÓÐÈý¸öÖµ£ºSYNC_MASTER£¬ASYNC_MASTER£¬SLAVE£»Í¬²½ºÍÒì²½±íʾMasterºÍSlaveÖ®¼äͬ²½Êý¾ÝµÄ»úÖÆ£» + brokerRole = ASYNC_MASTER + # Ë¢Å̲ßÂÔ£¬È¡ÖµÎª£ºASYNC_FLUSH£¬SYNC_FLUSH±íʾͬ²½Ë¢Å̺ÍÒ첽ˢÅÌ£»SYNC_FLUSHÏûϢдÈë´ÅÅ̺ó²Å·µ»Ø³É¹¦×´Ì¬£¬ASYNC_FLUSH²»ÐèÒª£» + flushDiskType = ASYNC_FLUSH + # ÉèÖÃbroker½ÚµãËùÔÚ·þÎñÆ÷µÄipµØÖ· + # brokerIP1 = 192.168.138.131 + ¸´ÖÆ´úÂë + +×¢Ò⣺ÕâÀïµÄ brokerIP1 ÇëÎñ±ØÉèÖ㬷ñÔòĬÈÏ»á³ÉΪ docker ÈÝÆ÷ÄÚ²¿IPµ¼ÖÂÍâÍøÁ´½Ó²»ÉÏ¡£ + +1. »¹ÊÇÔÚ rocketmq-docker Ŀ¼£¬½¨Á¢Ò»¸öÃûΪ rocketmq.yaml µÄ½Å±¾Îļþ£» + + + +rocketmq.yaml ÄÚÈÝÈçÏÂ: + + version: '2' + services: + namesrv: + image: rocketmqinc/rocketmq + container_name: rmqnamesrv + ports: + - 9876:9876 + volumes: + - /docker/rocketmq/data/namesrv/logs:/home/rocketmq/logs + - /docker/rocketmq/data/namesrv/store:/home/rocketmq/store + command: sh mqnamesrv + broker: + image: rocketmqinc/rocketmq + container_name: rmqbroker + ports: + - 10909:10909 + - 10911:10911 + - 10912:10912 + volumes: + - /docker/rocketmq/data/broker/logs:/home/rocketmq/logs + - /docker/rocketmq/data/broker/store:/home/rocketmq/store + - /docker/rocketmq/conf/broker.conf:/opt/rocketmq-4.4.0/conf/broker.conf + command: sh mqbroker -n namesrv:9876 -c /opt/rocketmq-4.4.0/conf/broker.conf + depends_on: + - namesrv + environment: + - JAVA_HOME=/usr/lib/jvm/jre + console: + image: styletang/rocketmq-console-ng + container_name: rocketmq-console-ng + ports: + - 8087:8080 + depends_on: + - namesrv + environment: + - JAVA_OPTS= -Dlogging.level.root=info -Drocketmq.namesrv.addr=rmqnamesrv:9876 + - Dcom.rocketmq.sendMessageWithVIPChannel=false + ¸´ÖÆ´úÂë + +1. ¿ªÆô broker Óõ½µÄ·À»ðǽ¶Ë¿Ú£¬·½±ãºóÐøÊ¹Ó㺠+ + firewall-cmd --zone=public --add-port=10909-10912/tcp --permanent + ¸´ÖÆ´úÂë + +1. Ö´ÐÐ sentinel-dashboard.yaml ½Å±¾Æô¶¯ÈÝÆ÷£º + + docker-compose -f rocketmq.yaml up + ¸´ÖÆ´úÂë + +1. ½øÈë rocketmq ¿ØÖÆÌ¨£¬ÎÒÃǻᷢÏÖÀàËÆµÄͼ±í£¨µ±È»¿ªÊ¼¿Ï¶¨Êǿյģ©£º + + http://(°²×°RocketMQ»úÆ÷µÄIP):8087 + ¸´ÖÆ´úÂë + + + +1. ÎÒÃÇÑ¡Ôñ ¡°¼¯Èº¡± ÕâÒ»À¸£¬½øÈë¾ÍÄÜ¿´¼ûÎÒÃÇÉèÖÃµÄ broker µÄÍâÍøIPÁË£» + + + +µ½ÕâÀïÎÒÃÇÀíÂÛÉÏÒѾ­Íê³É RocketMQ ·þÎñ¶ËµÄ²¿Êð£¬ÏÖÔھͿÉÒÔÈ¥ Spring ÏîÄ¿ÖÐʹÓÿͻ§¶ËÁË¡£ + +3. ÔÚ Spring ÏîÄ¿ÖÐÒýÈë RocketMQ ¿Í»§¶Ë + +1. Ìí¼Ó pom ÎļþÒÀÀµ£º + + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.0.4 + + ¸´ÖÆ´úÂë + +1. ÔÚ application.yml Ìí¼ÓÅäÖ㺠+ + server: + port: 10801 + + spring: + application: + name: (ÏîÄ¿Ãû³Æ)-service + + rocketmq: + name-server: (°²×°RocketMQ»úÆ÷µÄIP):9876 + producer: + group: (ÏîÄ¿Ãû³Æ)-group + ¸´ÖÆ´úÂë + +1. н¨Ò»¸öÏûÏ¢·¢ËÍÀà MessageProducer ×÷ΪÏûÏ¢µÄ Éú²úÕߣº + + @Service + public class MessageProducer implements CommandLineRunner { + + @Resource + private RocketMQTemplate rocketMQTemplate; + + @Override + public void run(String... args) throws Exception { + rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); + } + + } + ¸´ÖÆ´úÂë + +1. н¨Ò»¸öÏûÏ¢½ÓÊÕÀà MessageListener ×÷ΪÏûÏ¢µÄ Ïû·ÑÕߣº + + @Slf4j + @Service + @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") + public class MessageListener implements RocketMQListener { + + @Override + public void onMessage(String message) { + log.info("received message: {}", message); + } + + } + ¸´ÖÆ´úÂë + +1. н¨Ò»¸ö MessageProducer µÄµ÷ÓÿØÖÆÆ÷Àࣺ + + @RestController + @RequestMapping + public class HelloController { + + @Resource + private MessageProducer messageProducer; + + @RequestMapping("/message") + public void message() throws Exception { + messageProducer.run(""); + } + ¸´ÖÆ´úÂë + +1. Æô¶¯ Spring ÏîÄ¿£¬ÎÒÃÇÀ´²âÊÔÒ»ÏÂ×î¼òµ¥µÄÏûÏ¢·¢ËÍ: + + GET http://localhost:10801/message + Accept: */* + Cache-Control: no-cache + ¸´ÖÆ´úÂë + + + +ÎÒÃÇÒ²ÄÜÔÚ RocketMQ µÄ¹ÜÀí¿ØÖÆÌ¨£¬´óÖ¿´µ½ÏûÏ¢µÄÏû·ÑÇé¿ö£º + + + +ʵսµ½ÁËÕâÀﲢûÓнâÊÍһЩ¸ÅÄ¿ÉÄÜ´ó¼Ò»áÓе㷸ÃÔºý¡£ + +Topic£¨Ö÷Ì⣩Ï൱ÓÚÒ»ÖÖÀàÐ͵ÄÏûÏ¢£¬±ÈÈç¿ÉÒÔÉèÖà Topic1 רÃÅÊÇ·ÖÏúµÄÒµÎñ£¬ Topic2 רÃÅÊÇÓÅ»ÝȯµÄÒµÎñ£»¶ø Group£¨·Ö×飩Ï൱ÓÚ¶ÔÉú²úÕߺÍÏû·ÑÕߵķÖ×飬ÄÇÎÒÃǵÄ΢·þÎñ¼ÈÄÜ×öÉú²úÕßÒ²ÄÜ×öÏû·ÑÕߣ¬±ÈÈç¿ÉÒÔÉèÖà Group1 ÊÇÉÌÆ·µÄ΢·þÎñ£¬Group2 ÊǶ©µ¥µÄ΢·þÎñ£¬µ±È»»¹ÒªÇø·ÖÏÂÊÇÉú²úÕß·Ö×黹ÊÇÏû·ÑÕß·Ö×é¡£ + +- Group£º·ÖΪProducerGroup ºÍ ConsumerGroup, ´ú±íijһÀàµÄÉú²úÕߺÍÏû·ÑÕߣ¬Ò»°ãÀ´ËµÍ¬Ò»¸ö·þÎñ¿ÉÒÔ×÷Ϊ Group£¬Í¬Ò»¸ö Group Ò»°ãÀ´Ëµ·¢ËͺÍÏû·ÑµÄÏûÏ¢¶¼ÊÇÒ»ÑùµÄ¡£ +- Topic£ºÏûÏ¢Ö÷Ì⣬һ¼¶ÏûÏ¢ÀàÐÍ£¬Éú²úÕßÏòÆä·¢ËÍÏûÏ¢£¬Ïû·ÑÕß¶ÁÈ¡ÆäÏûÏ¢¡£ +- Queue: ·ÖΪ¶ÁºÍдÁ½ÖÖ¶ÓÁУ¬Ò»°ãÀ´Ëµ¶Áд¶ÓÁÐÊýÁ¿Ò»Ö£¬Èç¹û²»Ò»Ö¾ͻá³öÏֺܶàÎÊÌâ¡£ + +Topic ÖÐÓзÖΪÁ˶à¸ö Queue£¬ÕâÆäʵÊÇÎÒÃÇ·¢ËÍ/¶ÁÈ¡ÏûϢͨµÀµÄ×îСµ¥Î»¡£ÎÒÃÇ·¢ËÍÏûÏ¢¶¼ÐèÒªÖ¸¶¨Ä³¸öдÈëij¸ö Queue£¬À­È¡ÏûÏ¢µÄʱºòÒ²ÐèÒªÖ¸¶¨À­È¡Ä³¸ö Queue£¬ËùÒÔÎÒÃǵÄ˳ÐòÏûÏ¢¿ÉÒÔ»ùÓÚÎÒÃÇµÄ Queue ά¶È±£³Ö¶ÓÁÐÓÐÐò£¬Èç¹ûÏë×öµ½È«¾ÖÓÐÐòÄÇôÐèÒª½« Queue ´óСÉèÖÃΪ1£¬ÕâÑùËùÓеÄÊý¾Ý¶¼»áÔÚ Queue ÖÐÓÐÐò¡£ + + + +×÷Õߣº°×²Ë˵¼¼Êõ +Á´½Ó£ºhttps://juejin.cn/post/6930869079217717256 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" new file mode 100644 index 0000000..1264b0e --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSeata.md" @@ -0,0 +1,1075 @@ +## ¼ò½é + +ÎÒÃǶ¼ÖªµÀ Seata ÊÇÒ»¸ö·Ö²¼Ê½ÊÂÎñµÄ½â¾ö·½°¸£¬½ñÌìÎÒÃǾÍÀ´´ø´ó¼ÒÁ˽âÒ»ÏÂʲôÊÇ·Ö²¼Ê½ÊÂÎñ£¬Ê×ÏÈÎÒÃÇÏÈÀ´Á˽âһϻù´¡µÄ֪ʶ¡ª¡ªÊÂÎñ£¬ÎÒÃÇÏÈÀ´Á˽âÒ»ÏÂÊÂÎñµÄ¸ÅÄîÊÇʲô¡£ + +### »ù±¾¸ÅÄî + +ÊÂÎñËIJ¿·Ö¹¹³É¡ª ACID£º + +* A(Atomic)£ºÔ­×ÓÐÔ£¬¹¹³ÉÊÂÎñµÄËùÓвÙ×÷£¬ÒªÃ´È«²¿Ö´Ðгɹ¦£¬ÒªÃ´È«²¿Ö´ÐÐʧ°Ü£¬²»»á³öÏÖ²¿·Ö³É¹¦»òÕß²¿·Öʧ°ÜµÄÇé¿ö¡£ +* C(Consistency)£ºÒ»ÖÂÐÔ£¬ÔÚÊÂÎñÖ´ÐÐǰºó£¬Êý¾Ý¿âµÄÒ»ÖÂÐÔÔ¼ÊøÃ»Óб»ÆÆ»µ£¬±ÈÈ磬СÓÂÈ¥ÒøÐÐÈ¡100¿éÇ®£¬È¡Ö®Ç°ÊÇ600£¬È¡Ö®ºóÓ¦¸ÃÊÇ400£¬È¡Ö®Ç°ºÍȡ֮ºóµÄÊý¾ÝΪÕýÈ·ÊýֵΪһÖÂÐÔ£¬Èç¹ûÈ¡³ö100£¬¶øÒøÐÐÀïÃæµÄǮûÓмõÉÙ£¬ÒªÃ´Ð¡ÓÂҪЦÐÑÁË£¬Õâ¸ö¾ÍûÓдﵽһÖÂÐÔµÄÒªÇó¡£ +* I(Isolation):¸ôÀëÐÔ£¬Êý¾Ý¿âÖеÄÊÂÎñÒ»°ã¶¼ÊDz¢·¢µÄ£¬¸ôÀëÐÔÊÇÖ»ÔÚ²¢·¢µÄÁ½¸öÊÂÎñÖ´Ðйý³Ì»¥²»¸ÉÈÅ£¬Ò»¸öÊÂÎñÔÚÖ´Ðйý³ÌÖв»ÄÜ¿´µ½ÆäËûÊÂÎñÔËÐйý³ÌµÄÖмä״̬£¬Í¨¹ýÅäÖÃÊÂÎñ¸ôÀë¼¶±ð¿ÉÒÔ±ÜÃâÔà¶Á£¬Öظ´¶ÁµÈÎÊÌâ¡£ +* D(Durability)£º³Ö¾ÃÐÔ£¬µ±ÊÂÎñÍê³ÉÖ®ºó£¬ÊÂÎñ¶ÔÊý¾ÝµÄ¸ü¸Ä»á±»³Ö¾Ã»¯µ½Êý¾Ý¿â£¬ÇÒ²»»á»Ø¹ö¡£ + +ÊÂÎñ·ÖΪÁ½²¿·Ö£º±¾µØÊÂÎñºÍ·Ö²¼Ê½ÊÂÎñ¡£ + +#### ±¾µØÊÂÎñ£º + +ÔÚ¼ÆËã»úϵͳÖУ¬±È½Ï¶àµÄÊÇͨ¹ý¹ØÏµÐÍÊý¾Ý¿âÀ´¿ØÖÆÊÂÎñ£¬ÕâÊÇÀûÓÃÊý¾Ý¿â±¾ÉíµÄÊÂÎñÌØÐÔ½øÐÐʵÏֵģ¬ÒòΪӦÓÃÖ÷Òª¿¿¹ØÏµÐÍÊý¾Ý¿âÀ´Î¬³ÖÊÂÎñ£¬¼ÓÉÏÊý¾Ý¿âºÍÓ¦Óö¼ÔÚͬһ¸ö·þÎñÆ÷£¬ËùÒÔ»ùÓÚ¹ØÏµÐÍÊý¾ÝµÄÊÂÎñÓÖ±»³ÆÎª±¾µØÊÂÎñ¡£ + +#### ·Ö²¼Ê½ÊÂÎñ£º + +·Ö²¼Ê½ÊÂÎñÊÇÖ¸ÊÂÎñµÄ²ÎÓëÕß¡¢Ö§³ÖÊÂÎñµÄ·þÎñÆ÷¡¢×ÊÔ´·þÎñÆ÷ÒÔ¼°ÊÂÎñ¹ÜÀíÕß·Ö±ðλÓÚ²»Í¬µÄ·Ö²¼Ê½ÏµÍ³µÄ²»Í¬½ÚµãÖ®ÉÏ£¬ÇÒÊôÓÚ²»Í¬µÄÓ¦Ó㬷ֲ¼Ê½ÊÂÎñÐèÒª±£Ö¤ÕâЩ²Ù×÷Ҫôȫ²¿³É¹¦£¬ÒªÃ´È«²¿Ê§°Ü£¬·Ö²¼Ê½ÊÂÎñ¾ÍÊÇΪÁ˱£Ö¤ÔÚ²»Í¬·þÎñÆ÷ÉÏÊý¾Ý¿âÊý¾ÝµÄÒ»ÖÂÐÔ¡£ + +Seata µÄÉè¼ÆË¼Â·Êǽ«¶à¸ö·þÎñÆ÷µÄ±¾µØÊÂÎñ×é³ÉÒ»¸öÈ«¾ÖÊÂÎñ£¬ÏÂÃæÈô¸É¸ö±¾µØÊÂÎñ£¬¶¼ÄÜÂú×ãACID£¬×îºÃÐγÉÒ»¸öÕûµÄ·Ö²¼Ê½ÊÂÎñ£¬²Ù×÷·Ö²¼Ê½ÊÂÎñ¾ÍÏñÊDzÙ×÷±¾µØÊÂÎñÒ»Ñù¡£ + +·Ö²¼Ê½ÏµÍ³»á°ÑÒ»¸öÓ¦Óòð·ÖΪ¶à¸ö¿É¶ÀÁ¢²¿ÊðµÄ·þÎñ£¬·þÎñÓÚ·þÎñÖ®¼äͨ³£ÐèÒªÔ¶³ÌЭ×÷²ÅÄÜÍê³ÉÊÂÎñµÄ²Ù×÷£¬ÕâÖÖ·Ö²¼Ê½ÏµÍ³»·¾³ÏÂÓÉÓÚ²»Í¬µÄ·þÎñÖ®¼äͨ¹ýÍøÂçÔ¶³ÌЭ×÷Íê³ÉµÄÊÂÎñ±»³ÆÎª·Ö²¼Ê½ÊÂÎñ£¬ÀýÈ繩ӦÁ´ÏµÍ³ÖУ¬¶©µ¥´´½¨£¨Éú³É¶©µ¥¡¢¿Û¼õ¿â´æ¡¢ÂÄԼ֪ͨ·¢»õ£©µÈ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/6471b8f38c5920f6ace85580abd1567bc0f021.png "ͼƬ") + +ÔÚÉÏͼÖÐÎÒÃÇ¿ÉÒÔ¿´³ö£¬Ö»ÒªÉæ¼°µ½²Ù×÷¶à¸öÊý¾ÝÔ´£¬¾Í»á²úÉúÊÂÎñµÄÎÊÌ⣬ÎÒÃÇÔÚʵ¼Ê¿ª·¢ÖÐÓ¦¸ÃÒª±ÜÃâÕâÕâ¸öÄãÎÊÌâµÄ³öÏÖ£¬µ«ÊÇËäȻϵͳµÄÍØÕ¹£¬Ó¦ÓúÍÓ¦ÓÃÖ®¼ä±ØÈ»»á²úÉúÓ¦ÓÃÖ®¼äÊÂÎñµÄ·ÖÀ룬µ±Î¢·þÎñ¼Ü¹¹ÖУ¬Ö÷ÒªÓÐMQºÍSeata£¬ÔÚÁ˽âËûÃÇ֮ǰ£¬ÎÒÃÇÏÈÀ´Á˽âһϷֲ¼Ê½ÊÂÎñÊÇÔõÑù×é³É£¬ÒÔ¼°ÈçºÎʵÏֵġ£ + +## ·Ö²¼Ê½ÊÂÎñ + +#### ·Ö²¼Ê½ÊÂÎñÊÇʲô£¿ + +·Ö²¼Ê½ÊÂÎñÖ¸µÄÊÇÊÂÎñµÄ²ÎÓëÕߣ¬Ö§³ÖÊÂÎñµÄ·þÎñÆ÷£¬×ÊÔ´·þÎñÆ÷·Ö±ðλÓÚ·Ö²¼Ê½ÏµÍ³µÄ²»Í¬½ÚµãÖ®ÉÏ£¬Í¨³£Ò»¸ö·Ö²¼Ê½ÊÂÎïÖлáÉæ¼°µ½¶Ô¶à¸öÊý¾ÝÔ´»òÒµÎñϵͳµÄ²Ù×÷¡£ + +Ëæ×Å»¥ÁªÍøµÄ·¢Õ¹£¬´Ó֮ǰµÄµ¥Ò»ÏîÄ¿Öð½¥Ïò·Ö²¼Ê½·þÎñ×öת»»£¬ÏÖÈç½ñ΢·þÎñÔÚ¸÷¸ö¹«Ë¾ÒѾ­ÆÕ±é´æÔÚ£¬¶øµ±Ê±µÄ±¾µØÊÂÎñÒѾ­ÎÞ·¨Âú×ã·Ö²¼Ê½Ó¦ÓõÄÒªÇó£¬Òò´Ë·Ö²¼Ê½·þÎñÖ®¼äÊÂÎñµÄЭµ÷¾Í²úÉúÁËÎÊÌ⣬Èç¹û×öµ½¶à¸ö·þÎñÖ®¼äÊÂÎñµÄ²Ù×÷£¬Äܹ»Ïñ±¾µØÊÂÎñÒ»Ñù×ñÑ­ACIDÔ­Ôò£¬³ÉΪһ¸öÄÑÌ⣬µ«ÊÇÔÚ´óÅ£ÃDz»¶ÏµÄ̽Ë÷Ï£¬ÖÕÓÚÕÒµ½ÁË·Ö²¼Ê½ÊÂÎñ´æÔÚÁ½´óÀíÂÛÒÀ¾Ý£ºCAP¶¨ÂɺÍBASEÀíÂÛ¡£ + +### CAP¶¨ÂÉ + +CAP¶¨ÂÉÓÉÒ»ÖÂÐÔ(C)¡¢¿ÉÓÃÐÔ(A)¡¢·ÖÇøÈÝ´íÐÔ(P)×é³É£¬ÔÚ·Ö²¼Ê½ÏµÍ³ÖУ¬²»¿ÉÄÜͬʱÂú×ãConsistency(Ò»ÖÂÐÔ)/Availability(¿ÉÓÃÐÔ)/Partition tolerance(·ÖÇøÈÝ´íÐÔ) Èý¸öÌØÐÔ£¬×î¶àÖ»ÄÜͬʱÂú×ãÆäÖÐÁ½Ïî¡£ + +* Ò»ÖÂÐÔ(C)£ºÔÚ·Ö²¼Ê½ÏµÍ³ÖÐËùÓеÄÊý¾Ý±¸·Ý£¬ÔÚͬһʱ¿Ì±£³ÖÒ»ÖµÄÌØÐÔ£¬ËùÓеÄÓ¦Óýڵã·ÃÎʵͼÊÇͬһ·Ý×îеÄÊý¾Ý¸±±¾¡£ +* ¿ÉÓÃÐÔ(A): µ±¼¯ÈºÖÐÒ»²¿·Ö½Úµã¹ÊÕÏÒԺ󣬼¯ÈºÕûÌåÄܹ»ÏìÓ¦¿Í»§¶ËµÄ¶ÁдÇëÇ󣬶ÔÊý¾Ý¸üо߱¸¸ß¿ÉÓÃÐÔ¡£ +* ·ÖÇøÈÝ´íÐÔ(P): Èç¹ûϵͳÔڹ涨ʱ¼äÏÞÖÆÄÚ²»ÄÜ´ï³ÉÊý¾ÝµÄÒ»ÖÂÐÔ£¬¾Í±íʾҪ·¢Éú·ÖÇøµÄÇé¿ö£¬µ±Ç°²Ù×÷ÐèÒªÔÚCºÍAÖ®¼ä×ö³öÑ¡Ôñ£¬ÈÃϵͳÄܹ»ÔÚÓöµ½ÍøÂç¹ÊÕϵÈÇé¿öµÄʱºò£¬ÈÎÈ»Äܹ»±£Ö¤¶ÔÍâÌṩÂú×ãÒ»ÖÂÐÔ»òÕß¿ÉÓÃÐԵķþÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a5ec7fb950052bd1116452eb0ca9f52fd4e5a5.png "ͼƬ") + +ÔÚÉÏͼÖÐÎÒÃÇ¿ÉÒÔ¿´µ½£¬µ±ÎÒÃÇÓû§È¥¹ºÎï³µÀïÃæµã»÷ϵ¥½áËãµÄʱºò£¬Ê×ÏȻᾭ¹ýÎÒÃÇ¿â´æ·þÎñ£¬ÅÐ¶Ï¿â´æÊÇ·ñ×ã¹»£¬µ±¿â´æÂú×㣬¿Û¼õ¿â´æÒÔºó£¬ÎÒÃÇÐèÒª½«Êý¾Ýͬ²½µ½ÆäËû·þÎñÆ÷ÉÏ£¬ÕâÒ»²½ÊÇΪÁ˱£Ö¤Êý¾ÝµÄ½á¹ûµÄÒ»ÖÂÐÔ£¬Õâ¸öʱºòÈç¹ûÍøÂç²úÉú²¨¶¯ÁË£¬ÎÒÃǵÄϵͳÐèÒª±£Ö¤·ÖÇøÈÝ´íÐÔ£¬Ò²¾ÍÊÇÎÒÃDZØÐëÈÝÈÌÍøÂçËù´øÀ´µÄһЩÎÊÌ⣬´ËʱÏë±£Ö¤Ò»ÖÂÐÔ£¬¾ÍÐèÒªÉáÆú¿ÉÓÃÐÔ¡£ + +µ«ÊÇÈç¹ûΪÁ˱£Ö¤¸ß¿ÉÓÃÐÔ£¬ÄÇôÔڸ߲¢·¢µÄÇé¿öÏ£¬ÊÇÎÞ·¨±£Ö¤ÔÚÏÞ¶¨Ê±¼äÄÚ¸ø³öÏìÓ¦£¬ÓÉÓÚÍøÂçµÄ²»¿É¿¿£¬ÎÒÃǵĶ©µ¥·þÎñ¿ÉÄÜÎÞ·¨Äõ½×îеÄÊý¾Ý£¬µ«ÊÇÎÒÃÇÒª¸øÓû§×ö³öÏìÓ¦£¬ÄÇôҲÎÞ·¨±£Ö¤Ò»ÖÂÐÔ£¬ËùÒÔAPÊÇÎÞ·¨±£Ö¤Ç¿Ò»ÖÂÐԵġ£ + +Èç¹û¼ÈÏëÒª±£Ö¤¸ß¿ÉÓÃÓÖÏëÒª±£Ö¤Ò»ÖÂÐÔ£¬±ØÐëÔÚÍøÂçÁ¼ºÃµÄÇé¿öϲÅÄÜʵÏÖ£¬ÄÇô½â¾ö·½·¨Ö»ÓÐÒ»¸ö£¬ÄǾÍÊÇÐèÒª½«¿â´æ¡¢¶©µ¥¡¢ÂÄÔ¼·Åµ½Ò»Æð£¬µ«ÊÇÕâ¸ö¾ÍÉÏÈ¥ÁËÎÒÃÇ΢·þÎñµÄ×÷Óã¬Ò²¾Í²»ÔÙÊÇ·Ö²¼Ê½ÏµÍ³ÁË¡£ + +ÔÚ·Ö²¼Ê½ÏµÍ³ÖУ¬·ÖÇøÈÝ´íÐÔÊDZØÐë´æÔڵģ¬ÎÒÃÇÖ»ÄÜÔÚÒ»ÖÂÐԺͿÉÓÃÐÔÉÏÈ¡ÉᣬÔÚÕâÖÖÌõ¼þϾ͵®ÉúÁËBASEÀíÂÛ¡£ + +### BASEÀíÂÛ + +BASEÓÉ »ù±¾¿ÉÓà (Basically Available)¡¢Èí״̬ (Soft state)ºÍ ×îÖÕÒ»ÖÂÐÔ (Eventually consistent) Èý¸ö¹¹½¨¶ø³É£¬ÊǶÔCAPÖÐÒ»ÖÂÐԺͿÉÓÃÐÔȨºâµÄ½á¹û£¬À´Ô´ÓÚ¶Ô»¥ÁªÍøÏµÍ³·Ö²¼Ê½Êµ¼ùµÄ×ܽᣬÊÇ»ùÓÚCAP¶¨ÀíÖð²½ÑÝ»¯¶øÀ´µÄ£¬ºËÐÄËÄϵÄǸöÊǼ°Ê±ÎÞ·¨×öµ½Ç¿Ò»ÖÂÐÔ£¬µ«ÊÇÿ¸öÓ¦Óö¼¿ÉÒÔ¸ù¾Ý×ÔÉíµÄÒµÎñÌØµã£¬²ÉÓÃÊʵ±µÄ·½Ê½À´Ê¹ÏµÍ³´ïµ½×îÖÕÒ»ÖÂÐÔ¡£ + +1. »ù±¾¿ÉÓ㺻ù±¾¿ÉÓÃÊÇÖ¸µ±·Ö²¼Ê½ÏµÍ³³öÏÖ²»¿ÉÔ¤Öª¹ÊÕϵÄʱºò£¬ÔÊÐíËðʧ²¿·Ö¿ÉÓÃÐÔ£¬µ«ÊÇÕâÀï²¢²»ÊÇ˵±íʾϵͳ²»¿ÉÒÔÓã¬Ö÷ÒªÌåÏÖΪÒÔϼ¸µã: + +* ÏìӦʱ¼äÉϵÄËðʧ£¬ÔÚÕý³£Çé¿öÏ£¬Ò»¸öÔÚÏßËÑË÷ÒýÇæÐèÒªÔÚ0.5ÃëÖ®ÄÚ·µ»Ø¸øÓû§ÏìÓ¦µÄ²éѯ½á¹û£¬µ«ÊÇÓÉÓÚ³öÏÖ¹ÊÕÏ£¬²éѯ½á¹ûµÄÏìӦʱ¼äÔö¼ÓÁË1-2Ãë¡£ +* ϵͳ¹¦ÄÜÉϵÄËðʧ£¬ÔÚÕý³£Çé¿öÏ£¬Ò»¸öµç×ÓÉÌÎñÍøÕ¾ÉϽøÐйºÎÏû·ÑÕß¼¸ºõÄܹ»Ë³ÀûµÄÍê³Éÿһµ¥²Ù×÷£¬µ«ÊÇÔÚһЩ½ÚÈÕ´ó´ÙÏú¹ºÎï¸ß·åÆÚµÄʱºò£¬ÓÉÓÚÍøÕ¾ÉϹºÂòÁ¿µÄÃÍÔö£¬ÎªÁ˱£Ö¤ÏµÍ³µÄÎȶ¨ÐÔ£¬²¿·ÖÏû·ÑÕß¿ÉÄÜ»áÒýµ¼µ½Ò»¸öÁÙʱ½µ¼¶´¦ÀíµÄÒ³Ãæ»òÕßÌáʾ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a7dac0d93bc82c6c6c7076b77061ed1ebed415.png "ͼƬ") + +»ù±¾¿ÉÓõÄÒâ˼ÊÇ£¬¶ÔÓÚÎÒÃǵĺËÐÄ·þÎñÊÇ¿ÉÒÔʹÓõ쬯äËûµÄ·þÎñ¿ÉÒÔÊʵ±µÄ½µµÍÏìӦʱ¼ä£¬ÉõÖÁÊǽøÐзþÎñ½µ¼¶´¦Àí£¬ÔÚµ±Ç°ÖУ¬¿â´æºÍ¶©µ¥¿Ï¶¨ÊǺËÐÄ·þÎñ£¬ÖÁÓÚÎÒÃǵķ¢»õϵͳÔÚµ±Ê±Ö»Òª±£Ö¤»ù±¾¿ÉÓþÍÐУ¬ËüµÄͬ²½¿ÉÒÔÂýÒ»µã»òÕßÑÓ³Ù¸ü¸ß£¬µÈ´ýÁ÷Á¿¸ß·å¹ýÈ¥ÒÔºó£¬ÔÚ½øÐлָ´¡£ + +1. Èí״̬£ºÈí״̬ÊÇÖ¸ÔÊÐíϵͳÖеÄÊý¾Ý´æÔÚÖмä״̬£¬²¢ÈÏΪ¸ÃÖмä״̬µÄ´æÔÚ²»»áÓ°ÏìϵͳµÄÕûÌå¿ÉÓÃÐÔ£¬¼´ÔÊÐíϵͳ²»ÓýڵãµÄÊý¾Ý¸±±¾Ö®¼ä½øÐÐÊý¾Ýͬ²½µÄ¹ý³Ì´æÔÚÑÓʱ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/610c63492ee5e0f1d30493958b54430c85a9f3.png "ͼƬ") + +Èí״̬µÄÒâ˼ÊÇ˵£¬µ±ÎÒÃÇ´óÁ¿Ïµ¥µÄʱºò£¬¿Û¼õ¿â´æÊ±£¬Á÷Á¿¼¤Ôö£¬Õâ¸öʱºòÈç¹û´óÁ¿·ÃÎʵ½¿â´æ»òÕß¶©µ¥ÖУ¬¿ÉÄܻὫϵͳŪ¿å£¬Õâ¸ö¹ý³ÌÖÐÎÒÃÇ¿ÉÒÔÔÊÐíÊý¾ÝµÄͬ²½´æÔÚÑÓ³Ù£¬²»Ó°ÏìÕûÌåϵͳµÄʹÓᣠ+ +1. ×îÖÕÒ»ÖÂÐÔ£º×îÖÕÒ»ÖÂÐÔÇ¿µ÷µÄÊÇËùÓÐÊý¾Ý¸±±¾£¬ÔÚ¾­¹ýÒ»¶Îʱ¼äµÄͬ²½Ö®ºó£¬×îÖÕ¶¼Äܹ»´ïµ½Ò»¸öÒ»ÖµÄ״̬£¬Òò´Ë£¬×îÖÕÒ»ÖÂÐԵı¾ÖÊÊÇÐèҪϵͳ±£Ö¤×îÖÕÊý¾ÝÄܹ»´ïµ½Ò»Ö£¬¶ø²»ÊÇÐèҪʵʱ±£Ö¤ÏµÍ³µÄǿһÖÂÐÔ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a23602e9776d2c7bd3d507582538c32a471c8c.png "ͼƬ") + +¾­¹ýÁ÷Á¿¸ß·åÆÚÒԺ󣬾­¹ýÒ»¶Îʱ¼äµÄͬ²½£¬´ÓÖмä״̬×îºó±ä³ÉÊý¾Ý×îÖÕÒ»ÖÂÐÔ£¬±£Ö¤¸÷¸ö·þÎñÊý¾ÝµÄÒ»ÖÂÐÔ¡£ + +### ¶þ½×¶ÎÌá½»(2PC) + +2PC¼´Á½½×¶ÎÌύЭÒ飬Êǽ«Õû¸öÊÂÎñÁ÷³Ì·ÖΪÁ½¸ö½×¶Î£¬PÊÇָ׼±¸½×¶Î£¬CÊÇÖ¸Ìá½»½×¶Î¡£ + +¾ÍºÃ±ÈÎÒÃÇÈ¥KCCÂò±ùä¿Áܳԣ¬ÄǸպÃÓл£¬µÚ¶þ±­°ë¼Û£¬µ«ÊÇÄãÊÇÒ»¸öÈË£¬Õâ¸öʱºò¸ÕºÃÓиöС½ã½ã¹ýÀ´£¬ÕýÔÚ¿¼ÂÇÂò²»Âò±ùä¿Áܳԣ¬Õâ¸öʱºòÄãºÍËýÌá³öÁËAA£¬Ò²¾Í»á˵ֻÓе±ÄãºÍËý¶¼Í¬ÒâÂòÕâ¸öµÄʱºò£¬²ÅÄܹºÂòµ½£¬Èç¹ûÁ½¸öÈËÖÐÓÐÒ»¸ö²»Í¬ÒâÄÇô¾Í²»ÄÜÂòÕâ¸ö±ùä¿Áܳԡ£ + +**½×¶ÎÒ»£º**×¼±¸½×¶Î ÀϰåÒªÇóÄãÏȽøÐи¶¿î£¬ÄãͬÒ⸶¿îÍê³Éºó£¬ÔÙÒªÇóÅ®·½¸¶¿î£¬Å®·½Í¬Ò⸶¿îÍê³É¡£ + +**½×¶Î¶þ£º**Ìá½»½×¶Î ¶¼¸¶¿îÍê³É£¬Àϰå³ö²Í£¬Á½¸öÈ˶¼³Ôµ½±ùä¿ÁÜ¡£ + +Õâ¸öÀý×Ó¾Í×é³ÉÁËÒ»¸öÊÂÎñ¡£Èç¹ûÄÐŮ˫·½ÓÐÒ»¸öÈ˾ܾø¸¶¿î£¬ÄÇôÀϰå¾Í²»»á³ö²Í£¬²¢ÇÒ»á°ÑÒÑÊÕÈ¡µÄǮԭ·Í˻ء£ + +Õû¸öÊÂÎñ¹ý³ÌÊÇÓÉÊÂÎñ¹ÜÀíÆ÷ºÍ²ÎÓëÕß×é³ÉµÄ£¬µêÀϰå¾ÍÊÇÊÂÎñ¹ÜÀíÆ÷£¬ÄãºÍÄǸöÅ®º¢¾ÍÊDzÎÓëÕߣ¬ÊÂÎñ¹ÜÀíÆ÷¾ö²ßÕû¸ö·Ö²¼Ê½ÊÂÎñÔÚ¼ÆËã»úÖйØÏµÊý¾ÝÖ§³ÖÁ½½×¶ÎÌύЭÒ飺 + +* ×¼±¸½×¶Î(Prepare phase)£ºÊÂÎñ¹ÜÀíÆ÷¸øÃ¿¸ö²ÎÓëÕß·¢ËÍPrepare ÏûÏ¢£¬Ã¿¸öÊý¾Ý¿â²ÎÓëÕßÔÚ±¾µØÖ´ÐÐÊÂÎñ£¬²¢Ð´±¾µØµÄUndo/Redo ÈÕÖ¾£¬´ËʱÊÂÎñûÓÐÌá½»¡£ + +> undoÈÕÖ¾ÊǼǼÐÞ¸ÄǰµÄÊý¾Ý£¬ÓÃÓÚÊý¾Ý¿â»Ø¹ö¡£ +> +> Redo ÈÕÖ¾ÊǼǼÐ޸ĺóµÄÊý¾Ý£¬ÓÃÓÚÌá½»ÊÂÎñдÈëÊý¾ÝÎļþ¡£ + +* Ìá½»½×¶Î(commit phase)£ºÈç¹ûÊÂÎñ¹ÜÀíÆ÷ÊÕµ½Á˲ÎÓëÕßµÄÖ´ÐÐʧ°Ü»òÕß³¬Ê±ÏûϢʱ£¬Ö±½Ó¸øÃ¿¸ö²ÎÓëÕß·¢ËÍ(Rollback) ÏûÏ¢£¬Èç¹ûÊÕµ½²ÎÓëÕß¶¼³É¹¦£¬·¢ËÍ(Commit) ²ÎÓëÕ߸ù¾ÝÊÂÎñ¹ÜÀíÆ÷µÄÖ¸ÁîÖ´ÐÐÌá½»»òÕ߻عö²Ù×÷£¬²¢ÊÍ·ÅÊÂÎñ´¦Àí¹ý³ÌÖÐʹÓõÄ×ÊÔ´¡£ + +#### ³É¹¦Ìá½»£º + +ÊÂÎñ¹ÜÀíÆ÷ÏòËùÓвÎÓëÕß·¢ËÍÊÂÎñÄÚÈÝ£¬Ñ¯ÎÊÊÇ·ñ×¼±¸ºÃÁË£¬µÈ´ý²ÎÓëÕßµÄÏìÓ¦£¬¸÷¸ö²ÎÓëÕßÊÂÎñ½ÚµãÖ´ÐÐÊÂÎñ²Ù×÷£¬²¢½« UndoºÍRedo ÐÅÏ¢¼ÇÈëÊÂÎñÈÕÖ¾ÖС£Èç¹û²ÎÓëÕ߳ɹ¦Ö´ÐÐÊÂÎñ²Ù×÷£¬·´À¡ÊÂÎñ¹ÜÀíÆ÷YES²Ù×÷£¬±íʾÊÂÎñ¿ÉÒÔÖ´ÐУ¬¼ÙÈçЭµ÷Õß´ÓËùÓеIJÎÓëÕß»òµÃ·´À¡¶¼ÊÇYesÏìÓ¦£¬ÄÇô¾Í»áÖ´ÐÐÊÂÎñÌá½»¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/48e1a2907174a1ed035541e548755d39490441.png "ͼƬ") + +#### ʧ°Ü£º + +¼ÙÈçÈκÎÒ»¸ö²ÎÓëÕßÏòÊÂÎñ¹ÜÀíÆ÷·´À¡ÁËNoÖ¸Á»òÕߵȴý³¬Ê±Ö®ºó£¬ÊÂÎñ¹ÜÀíÆ÷ÎÞ·¨½ÓÊÕµ½ËùÓвÎÓëÕߵķ´À¡ÏìÓ¦£¬ÄÇôÖжÏÊÂÎñ£¬·¢ËͻعöÇëÇó£¬ÊÂÎñ¹ÜÀíÆ÷ÏòËùÓвÎÓëÕ߽ڵ㷢ËÍ RollBack ÇëÇ󣬲ÎÓëÕß½ÓÊÕµ½ RollBack ÇëÇóºó£¬»áÀûÓÃÔÚ½×¶ÎÒ»¼Ç¼µÄUndoÐÅÏ¢Ö´ÐÐÊÂÎñµÄ»Ø¹ö²Ù×÷£¬ÔÚÍê³É»Ø¹öÖ®ºóÊÍ·ÅÊÂÎñÖ´ÐÐÆÚ¼äÕ¼ÓõÄ×ÊÔ´£¬²ÎÓëÕßÔÚÍê³ÉÊÂÎñ»Ø¹öÖ®ºó£¬ÏòЭµ÷Õß·¢ËÍACKÏûÏ¢£¬ÊÂÎñ¹ÜÀíÆ÷ÔÚ½ÓÊܵ½ËùÓвÎÓëÕß·´À¡µÄACKÏûÏ¢Ö®ºó£¬Íê³ÉÊÂÎñÖжϡ£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0928e8933a69a75eff4968f6989ca78ceaeb47.png "ͼƬ") + +### Èý½×¶ÎÌá½»(3PC) + +3PC Ö÷ÒªÊÇΪÁ˽â¾öÁ½½×¶ÎÌύЭÒéµÄµ¥µã¹ÊÕÏÎÊÌâºÍËõС²ÎÓëÕß×èÈû·¶Î§¡£ÊǶþ½×¶ÎÌá½»£¨2PC£©µÄ¸Ä½ø°æ±¾£¬ÒýÈë²ÎÓë½ÚµãµÄ³¬Ê±»úÖÆÖ®Í⣬3PC°Ñ2PCµÄ×¼±¸½×¶Î·Ö³ÉÊÂÎñѯÎÊ£¨¸Ã½×¶Î²»»á×èÈû£©ºÍÊÂÎñÔ¤Ìá½»,ÔòÈý¸ö½×¶Î·Ö±ðΪ CanCommit¡¢PreCommit¡¢DoCommit¡£ + +#### CanCommit ѯÎÊ״̬ + +CanCommit½×¶Î Эµ÷Õß(Coordinator)»áÏò²ÎÓëÕß(Participant) ·¢ËÍCanCommitÏûÏ¢£¬Ñ¯ÎÊÊÇ·ñ¿ÉÒÔÖ´ÐвÙ×÷£¬²ÎÓëÕßÊÕµ½ÏûÏ¢ºó£¬±íʾÄܹ»Ö´ÐУ¬»á·µ»Ø¸øÐ­µ÷ÕßÄܹ»Ö´ÐеÄ(yes)ÃüÁî¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c9cc9e8959b9a5393cb59672bc05a9121825ff.png "ͼƬ") + +Èç¹û²ÎÓëÕß²»ÄÜÖ´ÐУ¬»á·µ»ØNoÃüÁî,ÊÍ·Å×ÊÔ´£¬½áÊøÊÂÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/61bf61d800a42fb1583689ccbc1b1e862fec29.png "ͼƬ") + +#### PreCommit Ô¤Ìá½» + +PreCommit ½×¶ÎÈç¹ûЭµ÷ÕßÊÕµ½²ÎÓëÕß·µ»ØµÄ״ֵ̬ΪYES£¬ÄÇô¾ÍÖ¤Ã÷ËüÃǶ¼ÓÐÄÜÁ¦È¥Ö´ÐÐÕâ¸ö²Ù×÷£¬ÄÇôЭµ÷Õ߾ͻáÏòËùÓвÎÓëÕß ·¢ËÍ PreCommit ÏûÏ¢£¬Ð­µ÷ÕßÊÕµ½ PreCommitÏûÏ¢ºó£¬»ØÈ¥Ö´Ðб¾µØÊÂÎñ£¬Èç¹ûÖ´Ðгɹ¦»á½«±¾µØÊÂÎñ±£´æµ½ undoºÍredo ºó£¬ÔÙ·µ»Ø¸øÐ­µ÷ÕßYESÖ¸ÁÈç¹ûÖ´Ðб¾µØÊÂÎñʧ°Ü£¬·µ»ØÐ­µ÷ÕßNo,ֻҪЭµ÷ÕßÊÕµ½Ò»¸öÖ´ÐÐʧ°Ü£¬¸øËùÓвÎÓëÕß·¢ËÍÖжÏÊÂÎñÏûÏ¢£¬²ÎÓëÕßÊÕµ½ÏûÏ¢ºó£¬¶ÔÊÂÎñ½øÐлعö²Ù×÷¡£ + +ÔÚÕâ¸ö½×¶Î²ÎÓëÕߺÍЭµ÷Õß¶¼ÒýÈëÁ˳¬Ê±»úÖÆ£¬Èç¹û²ÎÓëÕßûÓÐÊÕµ½£¬Ð­µ÷ÕßµÄÏûÏ¢£¬»òÕßЭµ÷ÕßûÓÐÊÕµ½²ÎÓëÕß·µ»ØµÄÔ¤Ö´Ðнá¹û״̬£¬Ôڵȴý³¬Ê±Ö®ºó£¬ÊÂÎñ»áÖжϣ¬±ÜÃâÁËÊÂÎñµÄ×èÈû¡£ + +Эµ÷ÕßÏò²ÎÓëÕß·¢ËÍPreCommit£¬Èç¹û²ÎÓëÕßÖ´Ðгɹ¦£¬·µ»Øyes![ͼƬ](https://s2.51cto.com/oss/202206/18/91b004f982c531b8a641821cf1a49be547f9b3.gif "ͼƬ")¡£ + +Èç¹û²ÎÓëÕßÖ´ÐÐʧ°Ü£¬Ö»ÓÐÓÐÒ»¸ö·µ»ØNoµ½Ð­µ÷Õߣ¬Ð­µ÷Õß»áÏò²ÎÓëÕß·¢ËÍÖжÏÊÂÎñµÄÏûÏ¢£¬²ÎÓëÕ߻عöÊÂÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/996ca048361577f6ba066998a6b3ed9ac0450b.png "ͼƬ") + +#### DoCommit Ìá½» + +Эµ÷ÕßÊÕµ½ËùÓвÎÓëÕß·µ»ØµÄ״̬¶¼ÊÇYES£¬ÕâʱЭµ÷Õß»áÏòËùÓеIJÎÓëÕß¶¼·¢ËÍ DoCommit £¬²ÎÓëÕßÊÕµ½ DoCommit ºó£¬»áÕæÕýµÄÌá½»ÊÂÎñ£¬µ±ÊÂÎñÌá½»³É¹¦ºó£¬·µ»ØÐ­µ÷ÕßYES״̬£¬±íʾÎÒÒѾ­Íê³ÉÊÂÎñµÄÌá½»ÁË£¬Ð­µ÷ÕßÊÕµ½ËùÓвÎÓëÕß¶¼·µ»ØYES״̬ºó£¬ÄÇô¾ÍÍê³ÉÁ˱¾´ÎÊÂÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f4ab1eb14668202d8be313dc1b754833689c37.png "ͼƬ") + +Èç¹ûij¸ö²ÎÓëÕß·µ»ØNoÏûÏ¢£¬Ð­µ÷Õß·¢ËÍÖжÏÊÂÎñÏûÏ¢(abort)£¬¸ø²ÎÓëÕßÃÇ£¬²ÎÓëÕ߻عöÊÂÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/42ab62f59f9e71c66f7213a6833c2c7e436193.png "ͼƬ") + +3PCÊÇ2PCµÄÉý¼¶°æ£¬ÒýÈëÁ˳¬Ê±»úÖÆ£¬½â¾öÁ˵¥µã¹ÊÕÏÒýÆðµÄÊÂÎñ×èÈûÎÊÌ⣬µ«ÊÇ3PCÒÀÈ»²»Äܽâ¾öÊÂÎñÒ»ÖÂÐÔµÄÎÊÌ⣬ÒòΪÔÚDoCommit½×¶Î£¬Èç¹ûÓÉÓÚÍøÂç»òÕß³¬Ê±µÈÔ­Ôòµ¼Ö²ÎÓëÕßÊÕ²»µ½Ð­µ÷Õß·¢Ë͹ýÀ´µÄ ÖжÏÊÂÎñÏûÏ¢(abort) £¬¹ýÁËÕâ¸öʱ¼äºó£¬²ÎÓëÕß»áÌá½»ÊÂÎñ£¬±¾À´ÊÇÓ¦¸Ã½øÐлعö£¬Ìá½»ÊÂÎñºó£¬»áµ¼ÖÂÊý¾Ý²»Ò»ÖµÄÎÊÌâ³öÏÖ£¬2PCËäÈ»ÔÚÍøÂç¹ÊÕÏÇé¿öÏ´æÔÚǿһÖÂÐÔ±»ÆÆ»µµÄÎÊÌ⣬µ«ÊǹÊÕϻָ´ÒÔºóÄܱ£Ö¤×îÖÕÒ»ÖÂÐÔ£¬3PCËäÈ»Óг¬Ê±Ê±¼ä£¬½â¾öÁË×èÈû£¬Ìá¸ßÁË¿ÉÓÃÐÔ£¬µ«ÊÇÎþÉüÁËÒ»ÖÂÐÔ£¬Èç¹ûÕë¶ÔÍøÂ粨¶¯ÎÊÌâµ¼ÖÂÊý¾ÝÎÊÌâÕâÒ»µãÉÏ£¬2PCÊÇÓÅÓÚ3PCµÄ¡£ + +## Seata + +¹ÙÍø£ºhttps://seata.io/zh-cn/docs/overview/what-is-seata.html¡£ + +Seata ÊÇÒ»¿î¿ªÔ´µÄ·Ö²¼Ê½ÊÂÎñ½â¾ö·½°¸£¬ÖÂÁ¦ÓÚÌṩ¸ßÐÔÄܺͼòµ¥Ò×Óõķֲ¼Ê½ÊÂÎñ·þÎñ¡£Seata ½«ÎªÓû§ÌṩÁË AT¡¢TCC¡¢SAGA ºÍ XA ÊÂÎñģʽ£¬ÎªÓû§´òÔìһվʽµÄ·Ö²¼Ê½½â¾ö·½°¸¡£ + +![ͼƬ](https://s6.51cto.com/oss/202206/18/22906d30493df9861728958921200a254ef5d5.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e335a42151fa17eb25e848defd449317e036d3.png "ͼƬ") + +ÔÚ΢·þÎñϵͳÖУ¬Ò»°ãÒµÎñ»á±»²ð·Ö³É¶ÀÁ¢µÄÄ£¿é£¬ÔÚ¹Ù·½ÌṩµÄ½á¹¹Í¼ÖУ¬ÎÒÃÇ¿ÉÒÔ¿´µ½µ±Ç°Ö÷Òª·ÖΪÈý¸öÄ£¿é¡£ + +* ¿â´æ·þÎñ£º¶ÔÓÚÉÌÆ·¿â´æÐÅÏ¢½øÐÐÔö¼Ó»òÕß¼õÉÙ²Ù×÷¡£ +* ¶©µ¥·þÎñ£º¸ù¾ÝÓû§Ö¸¶¨ÉÌÆ·Éú³É¶©µ¥¡£ +* ÕË»§·þÎñ£º´ÓÓû§ÕË»§Öп۳ýÓà¶î£¬Ôö¼Ó»ý·Ö£¬Î¬»¤µØÖ·ÐÅÏ¢µÈµÈ¡£ + +ÔÚµ±Ç°¼Ü¹¹ÖУ¬Óû§ÌôÑ¡ÐÄÒǵÄÉÌÆ·Ïµ¥ºó£¬ÐèÒªÈý¸ö·þÎñÀ´Íê³É²Ù×÷£¬Ã¿Ò»¸ö·þÎñµÄÄÚ²¿¶¼ÓµÓÐÒ»¸ö¶ÀÁ¢µÄ±¾µØÊÂÎñÀ´±£Ö¤µ±Ç°·þÎñÊý¾ÝµÄǿһÖÂÐÔ£¬µ«ÊÇÈý¸ö·þÎñ×é³ÉµÄÈ«¾ÖÊÂÎñÒ»ÖÂÐÔ¾Íû°ì·¨½øÐб£Ö¤£¬ÄÇôSeata¾ÍÊÇÀ´½â¾öÕâ¸öÎÊÌâµÄ¡£ + +### SeataÊõÓï + +¹ÙÍøµØÖ·£ºhttps://seata.io/zh-cn/docs/overview/terminology.html¡£ + +ÔÚÁ˽âSeata֮ǰ£¬ÎÒÃÇÏÈÀ´Á˽âһϠSeata ¼¸¸ö¹Ø¼üµÄ¸ÅÄ + +1. TC(Transaction Coordinator)ÊÂÎñЭµ÷ÕߣºÎ¬»¤È«¾ÖºÍ·ÖÖ§ÊÂÎñµÄ״̬£¬Çý¶¯È«¾ÖÊÂÎñÌá½»»òÕ߻عö¡£ +2. TM(Transaction Manager) ÊÂÎñ¹ÜÀíÕߣº ·¢ÆðÕߣ¬Í¬Ê±Ò»¸öRMµÄÒ»ÖÖ£¬¶¨ÒåÈ«¾ÖÊÂÎñµÄ·¶Î§£¬¿ªÊ¼È«¾ÖÊÂÎñ£¬Ìá½»»ò»Ø¹öÈ«¾ÖÊÂÎñ¡£ +3. RM(Resource Manager) ×ÊÔ´¹ÜÀíÆ÷£º ²ÎÓëÊÂÎñµÄ΢·þÎñ£¬¹ÜÀí·ÖÖ§ÊÂÎñ´¦ÀíµÄ×ÊÔ´£¬ÓëTC½»Ì¸ÒÔ×¢²á·ÖÖ§ÊÂÎñºÍ±¨¸æ·ÖÖ§ÊÂÎñµÄ״̬£¬²¢Çý¶¯·ÖÖ§ÊÂÎñÌá½»»ò»Ø¹ö¡£ + +### Seata 2PC + +**Ò»½×¶Î£º** ÒµÎñÊý¾ÝºÍ»Ø¹öÈÕÖ¾¼Ç¼ÔÚͬһ¸ö±¾µØÊÂÎñÖÐÌá½»£¬Êͷű¾µØËøºÍÁ¬½Ó×ÊÔ´¡£ + +**¶þ½×¶Î£º** Ìá½»Òì²½»¯£¬·Ç³£¿ìËÙµØÍê³É¡£»Ø¹öͨ¹ýÒ»½×¶ÎµÄ»Ø¹öÈÕÖ¾½øÐз´Ïò²¹³¥¡£ + +Ò»½×¶Î±¾µØÊÂÎñÌύǰ£¬ÐèҪȷ±£ÏÈÄõ½ È«¾ÖËø ¡£Äò»µ½È«¾ÖËø £¬²»ÄÜÌá½»±¾µØÊÂÎñ¡£ÄÃÈ«¾ÖËøµÄ³¢ÊÔ±»ÏÞÖÆÔÚÒ»¶¨·¶Î§ÄÚ£¬³¬³ö·¶Î§½«·ÅÆú£¬²¢»Ø¹ö±¾µØÊÂÎñ£¬Êͷű¾µØËø¡£ + +ÔÚÊý¾Ý¿â±¾µØÊÂÎñ¸ôÀë¼¶±ð¶ÁÒÑÌá½»»òÒÔÉϵĻù´¡ÉÏ£¬Seata£¨AT ģʽ£©µÄĬÈÏÈ«¾Ö¸ôÀë¼¶±ðÊÇ ¶ÁδÌá½»¡£ + +Èç¹ûÓ¦ÓÃÔÚÌØ¶¨³¡¾°Ï£¬±ØÐèÒªÇóÈ«¾ÖµÄ ¶ÁÒÑÌá½» £¬Ä¿Ç° Seata µÄ·½Ê½ÊÇͨ¹ý SELECT FOR UPDATE Óï¾äµÄ´úÀí¡£ + +SeataÖ´ÐÐÁ÷³Ì·ÖÎö£º + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c7e10e315ebb9d8effa798730cebde46ec741a.png "ͼƬ") + +ÿ¸öRM ʹÓà DataSourceProxy Á´½ÓÊý¾Ý·£¬Ä¿µÄÊÇʹÓà ConnectionProxy £¬Ê¹ÓÃÊý¾ÝÔ´ºÍÊý¾Ý´úÀíµÄÄ¿µÄÊÇÔÚµÚÒ»½×¶Î½« undoºÍÒµÎñÊý¾Ý·ÅÔÚÒ»¸ö±¾µØÊÂÎñÖÐÌá½»£¬ÕâÑù¾Í±£´æÁËÖ»ÒªÓÐÒµÎñ²Ù×÷¾ÍÒ»¶¨»áÓÐdudoÈÕÖ¾¡£ + +ÔÚµÚÒ»½×¶ÎÖУ¬undo´æ·ÅÁËÊý¾ÝÐÞ¸ÄǰºóÐ޸ĵÄÖµ£¬ÊÇΪÁËÊÂÎñ»Ø¹ö×öºÃ×¼±¸£¬ÔÚµÚÒ»½×¶ÎÍê³É¾ÍÒѾ­½«·ÖÖ§ÊÂÎñÌá½»ÁË£¬Ò²¾ÍÊÍ·ÅÁËËø×ÊÔ´¡£ + +TM¿ªÆôÈ«¾ÖÊÂÎñ¿ªÊ¼£¬½«XIDÈ«¾ÖÊÂÎñID·ÅÔÚÊÂÎñÉÏÏÂÎÄÖУ¬Í¨¹ýfeignµ÷Óý«XID´«ÈëÏÂÓηþÎñÆ÷ÖУ¬Ã¿¸ö·ÖÖ§ÊÂÎñ½«×Ô¼ºµÄ Branch ID·ÖÖ§ÊÂÎñIDºÍXID½øÐйØÁª¡£ + +ÔÚµÚ¶þ½×¶ÎÈ«¾ÖÊÂÎñÌá½»£¬TC»á֪ͨ¸÷¸ö·ÖÖ§²ÎÓëÕßÌá½»·ÖÖ§ÊÂÎñ£¬ÔÚµÚÒ»½×¶ÎÒѾ­Ìá½»ÁË·ÖÖ§ÊÂÎñ£¬ÔÚÕâÀï¸÷²ÎÓëÕßÖ»ÐèҪɾ³ýundo¼´¿É£¬²¢ÇÒ¿ÉÒÔÒì²½Ö´ÐС£ + +Èç¹ûijһ¸ö·ÖÖ§ÊÂÎñÒì³£ÁË£¬µÚ¶þ½×¶ÎÈ«¾ÖÊÂÎñ»Ø¹ö²Ù×÷£¬TC»á֪ͨ¸÷¸ö·ÖÖ§²ÎÓëÕ߻عö·ÖÖ§ÊÂÎñ£¬Í¨¹ýXIDºÍBranch-IDÕÒµ½¶ÔÓ¦µÄ»Ø¹öÈÕÖ¾£¬Í¨¹ý»Ø¹öÈÕÖ¾Éú³ÉµÄ·´ÏòSQLÖ´ÐУ¬Íê³É·ÖÖ§ÊÂÎñ»Ø¹öµ½Ö®Ç°µÄ״̬¡£ + +### Seata ÏÂÔØ°²×° + +ÏÂÔØµØÖ·£ºhttps://github.com/seata/seata/releases¡£ + +![ͼƬ](https://s7.51cto.com/oss/202206/18/97b6c0b520c17b43b883003d10ef8e22d933a2.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/019d53972f031fdf1767425abf92f907f0f1c8.png "ͼƬ") + +½âѹºóÕÒµ½confĿ¼¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/88a3fa94605fb2567a64954540711876f4a6df.png "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/6998821822b0d8ed7a44318b8b3390bc421303.png "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d157544494550bb1c865815e31ae990fbac40b.png "ͼƬ") + +ÎÒÃÇÔÚÆô¶¯seata֮ǰ£¬Ê×ÏÈÒªÆô¶¯nacos£¬ÆäʵҲºÜ¼òµ¥£¬Ö»ÐèÒªÏÂÔØnacosºóÆô¶¯¾ÍÐУ¬²»ÖªµÀnacosÔõô²Ù×÷µÄ¿´ÕâÀïµÄ½éÉÜnacos»ù´¡½éÉÜ£¬Æô¶¯ºÃÖ®ºó£¬ÎÒÃÇÔÙÀ´Æô¶¯seata£¬binĿ¼ÏÂseata-server.bat![ͼƬ](https://s5.51cto.com/oss/202206/18/b8605b968056145dc6c740b5c95f346a98eaae.gif "ͼƬ")¡£ + +Èç¹ûÎÒÃÇ¿´µ½8091¶Ë¿ÚÔÚ¼àÌý£¬²¢ÇÒÔÚnacos¿´µ½·þÎñ×¢²áÉÏÈ¥ÁË£¬¾Í±íʾÎÒÃÇseataÆô¶¯³É¹¦ÁË¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/96829da176e4fcca80a0230d5c14b601ac549b.png "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c253ebd69820f7d2daa259c97f27cdc70fd160.png "ͼƬ") + +## ×ܽá + +µ½ÕâÀïÎÒÃǹØÓÚ·Ö²¼Ê½ÊÂÎñµÄºÍseataµÄ½éÉܾͽ²ÍêÁË£¬Æäʵ¹ØÓÚ·Ö²¼Ê½»¹ÓÐMQʵÏÖ¿É¿¿ÏûÏ¢×îÖÕÒ»ÖÂÐÔ£¬MQÖ÷Òª½â¾öÁËÁ½¸ö¹¦ÄÜ£º±¾µØÊÂÎñÓëÏûÏ¢·¢Ë͵ÄÔ­×ÓÐÔÎÊÌâ¡£ÊÂÎñ²ÎÓë·½½ÓÊÕÏûÏ¢µÄ¿É¿¿ÐÔ¡£ + +## ǰÑÔ + +ÔÚÉÏÒ»½ÚÖÐÎÒÃǽ²½âÁË£¬¹ØÓÚ·Ö²¼Ê½ÊÂÎñºÍseataµÄ»ù±¾½éÉܺÍʹÓ㬸ÐÐËȤµÄС»ï°é¿ÉÒԻعËÒ»ÏÂ[¡¶±ðÔÙ˵Äã²»ÖªµÀ·Ö²¼Ê½ÊÂÎñÁË!¡·](https://developer.51cto.com/article/711909.html) ×îºóСũҲ˵ÁË£¬ÏÂÆÚ»á´ø¸ø´ó¼Ò¹ØÓÚSeataÖйØÓÚseataÖÐAT¡¢TCC¡¢SAGA ºÍ XA ģʽµÄ½éÉܺÍʹÓ㬽ñÌì¾ÍÀ´½²½â¹ØÓÚSeataÖзֲ¼Ê½ËÄÖÖÄ£Ð͵ĽéÉÜ¡£ + +Seata·ÖΪÈý´óÄ£¿é£¬·Ö±ðÊÇ TM¡¢RM ºÍ TC¡£ + +**TC (Transaction Coordinator) - ÊÂÎñЭµ÷Õߣº**ά»¤È«¾ÖºÍ·ÖÖ§ÊÂÎñµÄ״̬£¬Çý¶¯È«¾ÖÊÂÎñÌá½»»ò»Ø¹ö¡£ + +**TM (Transaction Manager) - ÊÂÎñ¹ÜÀíÆ÷£º**¶¨ÒåÈ«¾ÖÊÂÎñµÄ·¶Î§£º¿ªÊ¼È«¾ÖÊÂÎñ¡¢Ìá½»»ò»Ø¹öÈ«¾ÖÊÂÎñ¡£ + +**RM (Resource Manager) - ×ÊÔ´¹ÜÀíÆ÷£º**¹ÜÀí·ÖÖ§ÊÂÎñ´¦ÀíµÄ×ÊÔ´£¬ÓëTC½»Ì¸ÒÔ×¢²á·ÖÖ§ÊÂÎñºÍ±¨¸æ·ÖÖ§ÊÂÎñµÄ״̬£¬²¢Çý¶¯·ÖÖ§ÊÂÎñÌá½»»ò»Ø¹ö¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c1ba09f1965be00dd7b817c1942fccfa2d7d25.png "ͼƬ") + +ÔÚ Seata ÖУ¬·Ö²¼Ê½ÊÂÎñµÄÖ´ÐÐÁ÷³Ì£º + +* TM ¿ªÆô·Ö²¼Ê½ÊÂÎñ£¨TM Ïò TC ×¢²áÈ«¾ÖÊÂÎñ¼Ç¼£©¡£ +* °´ÒµÎñ³¡¾°£¬±àÅÅÊý¾Ý¿â¡¢·þÎñµÈÊÂÎñÄÚ×ÊÔ´£¨RM Ïò TC »ã±¨×ÊÔ´×¼±¸×´Ì¬ £©¡£ +* TM ½áÊø·Ö²¼Ê½ÊÂÎñ£¬ÊÂÎñÒ»½×¶Î½áÊø£¨TM ֪ͨ TC Ìá½»/»Ø¹ö·Ö²¼Ê½ÊÂÎñ£©¡£ +* TC »ã×ÜÊÂÎñÐÅÏ¢£¬¾ö¶¨·Ö²¼Ê½ÊÂÎñÊÇÌá½»»¹Êǻعö¡£ +* TC ֪ͨËùÓÐ RM Ìá½»/»Ø¹ö ×ÊÔ´£¬ÊÂÎñ¶þ½×¶Î½áÊø¡£ + +TM ºÍ RM ÊÇ×÷Ϊ Seata µÄ¿Í»§¶ËÓëÒµÎñϵͳ¼¯³ÉÔÚÒ»Æð£¬TC ×÷Ϊ Seata µÄ·þÎñ¶Ë¶ÀÁ¢²¿Êð¡£ + +·þÎñ¶Ë´æ´¢Ä£Ê½Ö§³ÖÈýÖÖ£º + +**file£º** µ¥»úģʽ£¬È«¾ÖÊÂÎñ»á»°ÐÅÏ¢ÄÚ´æÖжÁд²¢³Ö¾Ã»¯±¾µØÎļþroot.data£¬ÐÔÄܽϸߣ¨Ä¬ÈÏ£©¡£ + +**DB:** ¸ß¿ÉÓÃģʽ£¬È«¾ÖÊÂÎñ»á»°ÐÅϢͨ¹ýDB¹²Ïí£¬Ïà¶ÔÐÔÄܲîһЩ¡£ + +**redis£º** Seata-Server1.3¼°ÒÔÉϰ汾֧³Ö£¬ÐÔÄܽϸߣ¬´æÔÚÊÂÎñÐÅÏ¢¶ªÊ§·çÏÕ£¬ÐèÒªÅäºÏʵ¼Ê³¡¾°Ê¹Óᣠ+ +### TC»·¾³´î½¨Ïê½â + +ÕâÀïÎÒÃÇʹÓÃDB¸ß¿ÉÓÃģʽ£¬ÕÒµ½conf/file.confÎļþ¡£ + +![ͼƬ](https://s9.51cto.com/oss/202207/03/84e02c1536f8c1431cc833b98958c1168f8658.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d9e1e2d06e2f4e53344238a2806fbc9bdbaf7b.png "ͼƬ") + +ÐÞ¸ÄÒÔÉÏÖеÄÐÅÏ¢£¬ÕÒµ½¶ÔÓ¦µÄdbÅäÖã¬ÐÞ¸ÄÆäÖеÄjdbcÁ¬½Ó£¬Òª×¢ÒâÆäÖÐÉæ¼°µ½Èý¸ö±í£¨global_table£¬branch_table£¬lock_table£©£¬Í¬Ê± mysql5ºÍmysql8µÄÇý¶¯ÊDz»Ò»ÑùµÄ¡£ + +> mysql5£ºcom.mysql.jdbc.Driver¡£ +> +> mysql8£ºcom.mysql.cj.jdbc.Driver¡£ + +½¨±íÓï¾äµØÖ·£ºhttps://github.com/seata/seata/blob/develop/script/server/db/mysql.sql¡£ + +**global_table£º** È«¾ÖÊÂÎñ±í£¬Ã¿µ±ÓÐÒ»¸öÈ«¾ÖÊÂÎñ·¢Æðºó£¬¾Í»áÔڸñíÖмǼȫ¾ÖÊÂÎñµÄID¡£ + +**branch_table£º** ·ÖÖ§ÊÂÎñ±í£¬¼Ç¼ÿһ¸ö·ÖÖ§ÊÂÎñµÄ ID£¬·ÖÖ§ÊÂÎñ²Ù×÷µÄÄĸöÊý¾Ý¿âµÈÐÅÏ¢¡£ + +**lock_table£º** È«¾ÖËø¡£ + +µ±ÉÏÊöÅäÖúÃÒÔºó£¬ÖØÆôSeata¼´¿ÉÉúЧ¡£ + +### Seata ÅäÖà Nacos + +SeataÖ§³Ö×¢²á·þÎñµ½Nacos£¬ÒÔ¼°Ö§³ÖSeataËùÓÐÅäÖ÷ŵ½NacosÅäÖÃÖÐÐÄ£¬ÔÚNacosÖÐͳһά»¤£»ÔÚ ¸ß¿ÉÓÃģʽϾÍÐèÒªÅäºÏNacosÀ´Íê³É + +Ê×ÏÈÕÒµ½ conf/registry.conf£¬ÐÞ¸ÄregistryÐÅÏ¢¡£ + + + + + + + + + +``` +registry { + # file ¡¢nacos ¡¢eureka¡¢redis¡¢zk¡¢consul¡¢etcd3¡¢sofa + type = "nacos" + nacos { + application = "seata-server" # ÕâÀïµÄÅäÖÃÒªºÍ¿Í»§¶Ë±£³ÖÒ»Ö + serverAddr = "127.0.0.1:8848" + group = "SEATA_GROUP" # ÕâÀïµÄÅäÖÃÒªºÍ¿Í»§¶Ë±£³ÖÒ»Ö + namespace = "" + cluster = "default" + username = "nacos" + password = "nacos" + } + config { + # file¡¢nacos ¡¢apollo¡¢zk¡¢consul¡¢etcd3 + type = "nacos" + nacos { + serverAddr = "127.0.0.1:8848" + namespace = "" + group = "SEATA_GROUP" + username = "nacos" + password = "nacos" + dataId = "seataServer.properties" + } + ...... +} +``` + + + + + + + + + + + +Ð޸ĺú󣬽«seataÖеÄһЩÅäÖÃÉÏ´«µ½NacosÖУ¬ÒòΪÅäÖÃÏî±È½Ï¶à£¬ËùÒÔ¹Ù·½ÌṩÁËÒ»¸öconfig.txt£¬Ö»ÏÂÔØ²¢ÇÒÐÞ¸ÄÆäÖÐijЩ²ÎÊýºó£¬ÉÏ´«µ½NacosÖм´¿É¡£ + +**ÏÂÔØµØÖ·£º**https://github.com/seata/seata/tree/develop/script/config-center¡£ + +ÐÞ¸ÄÏîÈçÏ£º + + + + + + + + + + + +``` +service.vgroupMapping.mygroup=default # ÊÂÎñ·Ö×é +store.mode=db +store.db.driverClassName=com.mysql.cj.jdbc.Driver +store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai +store.db.user=root +store.db.password=123456 +``` + + + + + + + + + + + +Ð޸ĺÃÕâ¸öÎļþÒԺ󣬰ÑÕâ¸öÎļþ·Åµ½seataĿ¼Ï£º + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b42aeca797a2bcb8fb5791e836d0f6011e9848.png "ͼƬ") + +°ÑÕâЩÅäÖö¼¼ÓÈëµ½NacosÅäÖÃÖУ¬Òª½èÖúÒ»¸ö½Å±¾À´½øÐÐÖ´ÐУ¬¹Ù·½ÒѾ­ÌṩºÃ¡£ + +**µØÖ·Îª£º**https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh¡£ + +н¨Ò»¸önacos-config.shÎļþ£¬½«½Å±¾ÄÚÈݸ´ÖƽøÈ¥£¬ÐÞ¸Äcongfig.txtµÄ·¾¶¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b876340233c7ac1623731359ea33164becafc1.png "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f563db6014ec0cb7f5a13952c63552c916da05.png "ͼƬ") + +ÉÏÊöÎļþÐ޸ĺú󣬴ò¿ªgit¹¤¾ß£¬½«nacos-config.sh¹¤¾ßÍÏ×§µ½´°ÌåÖм´¿É»òÕßʹÓÃÃüÁî¡£ + +> sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 88b8f583-43f9-4272-bd46-78a9f89c56e8 -u nacos -w nacos¡£ +> +> -h£ºnacosµØÖ·¡£ +> +> -p£º¶Ë¿Ú£¬Ä¬ÈÏ8848¡£ +> +> -g£ºseataµÄ·þÎñÁбí·Ö×éÃû³Æ¡£ +> +> -t£ºnacosÃüÃû¿Õ¼äid¡£ +> +> -uºÍ-w£ºnacosµÄÓû§ÃûºÍÃÜÂë¡£ + +![ͼƬ](https://s3.51cto.com/oss/202207/03/b685868586971f9c3b1490e3fcf50041fc2f6b.gif "ͼƬ")![ͼƬ](https://s4.51cto.com/oss/202207/03/965180d92863b00defe5550f051ade40ac60e8.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/764bfaf385a67b25f732035cde02cc76bbcd20.png "ͼƬ") + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/110a73f54f3f0e565cd21630cf0e0cf4b80ee2.png "ͼƬ") + +×îºó»áÓÐËĸöÖ´ÐÐʧ°Ü£¬ÊÇÒòΪredis±¨´íµÄ¹ØÏµ£¬Õâ¸ö¿ÉÒÔºöÂÔ£¬²»Ó°ÏìÕý³£Ê¹Óá£×îºó¿ÉÒÔ¿´µ½ÔÚNacosÖÐÓкܶàµÄÅäÖÃÏ˵Ã÷µ¼Èë³É¹¦¡£ÔÙÖØÐÂÆäÖÐseata£¬³É¹¦¼àÌýµ½8091¶Ë¿Ú£¬±íʾǰÖù¤×÷¶¼ÒѾ­×¼±¸Íê³É¡£ + +![ͼƬ](https://s7.51cto.com/oss/202207/03/573560212aa718ea82c317737eefc492908086.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f71535950882c017bbd05116c54c16ccb20f0e.png "ͼƬ") + +### SeataµÄÊÂÎñģʽ + +Seata ¶¨ÒåÁËÈ«¾ÖÊÂÎñµÄ¿ò¼Ü£¬Ö÷Òª·ÖΪÒÔϼ¸²½£º + +1. TM Ïò TCÇëÇó ·¢Æð(Begin)¡¢Ìá½»(Commit)¡¢»Ø¹ö(Rollback)µÈÈ«¾ÖÊÂÎñ¡£ +2. TM°Ñ´ú±íÈ«¾ÖÊÂÎñµÄXID°ó¶¨µ½·ÖÖ§ÊÂÎñÉÏ¡£ +3. RMÏòTC×¢²á£¬°Ñ·ÖÖ§ÊÂÎñ¹ØÁªµ½XID´ú±íµÄÈ«¾ÖÊÂÎñÖС£ +4. RM°Ñ·ÖÖ§ÊÂÎñµÄÖ´Ðнá¹ûÉϱ¨¸øTC¡£ +5. TC·¢ËÍ·ÖÖ§Ìá½»£¨Branch Commit£©»ò·ÖÖ§»Ø¹ö£¨Branch Rollback£©ÃüÁî¸øRM¡£ + +![ͼƬ](https://s9.51cto.com/oss/202207/03/410c3b93872ba8a55a9532876f3afbe1853d88.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a30dd41045c8db4e0c5757095c6d1ab52fd57e.png "ͼƬ") + +Seata µÄ È«¾ÖÊÂÎñ ´¦Àí¹ý³Ì£¬·ÖΪÁ½¸ö½×¶Î£º + +* Ö´Ðн׶Π£ºÖ´ÐзÖÖ§ÊÂÎñ£¬²¢±£Ö¤Ö´Ðнá¹ûÂú×ãÊÇ_¿É»Ø¹öµÄ£¨Rollbackable£©_ºÍ_³Ö¾Ã»¯µÄ£¨Durable£©_¡£ +* Íê³É½×¶Î£º¸ù¾Ý Ö´Ðн׶Π½á¹ûÐγɵľöÒ飬ӦÓÃͨ¹ý TM ·¢³öµÄÈ«¾ÖÌá½»»ò»Ø¹öµÄÇëÇó¸ø TC£¬TC ÃüÁî RM Çý¶¯ ·ÖÖ§ÊÂÎñ ½øÐÐ Commit »ò Rollback¡£ + +Seata µÄËùνÊÂÎñģʽÊÇÖ¸£ºÔËÐÐÔÚ Seata È«¾ÖÊÂÎñ¿ò¼ÜÏ嵀 ·ÖÖ§ÊÂÎñ µÄÐÐΪģʽ¡£×¼È·µØ½²£¬Ó¦¸Ã½Ð×÷ ·ÖÖ§ÊÂÎñģʽ¡£ + +²»Í¬µÄ ÊÂÎñģʽ Çø±ðÔÚÓÚ ·ÖÖ§ÊÂÎñ ʹÓò»Í¬µÄ·½Ê½´ïµ½È«¾ÖÊÂÎñÁ½¸ö½×¶ÎµÄÄ¿±ê¡£¼´£¬»Ø´ðÒÔÏÂÁ½¸öÎÊÌ⣺ + +* Ö´Ðн׶Π£ºÈçºÎÖ´Ðв¢ ±£Ö¤ Ö´Ðнá¹ûÂú×ãÊÇ_¿É»Ø¹öµÄ£¨Rollbackable£©_ºÍ_³Ö¾Ã»¯µÄ£¨Durable£©_¡£ +* Íê³É½×¶Î£ºÊÕµ½ TC µÄÃüÁîºó£¬ÈçºÎ×öµ½·ÖÖ§µÄÌá½»»ò»Ø¹ö£¿ + +ÎÒÃÇÒÔATģʽΪÀý£º + +* Ö´Ðн׶Σº + +* ¿É»Ø¹ö£º¸ù¾Ý SQL ½âÎö½á¹û£¬¼Ç¼»Ø¹öÈÕÖ¾ +* ³Ö¾Ã»¯£º»Ø¹öÈÕÖ¾ºÍÒµÎñ SQL ÔÚͬһ¸ö±¾µØÊÂÎñÖÐÌá½»µ½Êý¾Ý¿â + +* Íê³É½×¶Î£º +* ·ÖÖ§Ìá½»£ºÒ첽ɾ³ý»Ø¹öÈÕÖ¾¼Ç¼ +* ·ÖÖ§»Ø¹ö£ºÒÀ¾Ý»Ø¹öÈÕÖ¾½øÐз´Ïò²¹³¥¸üР+ +½ÓÏÂÀ´¾Í½øÈëÖØÍ·Ï·£¬SeataËÄ´óģʽµÄ½éÉÜ¡£ + +## Seata-XAģʽ + +Seata 1.2.0 °æ±¾·¢²¼ÁËеÄÊÂÎñÄ£ÐÍ£ºXAģʽ£¬ÊµÏÖÁ˶ÔXAЭÒéµÄÖ§³Ö¡£¶ÔÓÚXAģʽÎÒÃÇÐèÒª´ÓÈý¸öµãÈ¥½âÎöËü¡£ + +* XAģʽÊÇʲô¡£ +* Ϊʲô֧³ÖXA¡£ +* XAģʽÈçºÎʵÏÖºÍʹÓᣠ+ +### XAģʽ¼ò½é + +Ê×ÏÈÐèÒªÖªµÀXAÄ£ÐÍÊÇʲô£¬XA ¹æ·¶ÔçÔÚÉÏÊÀ¼Í 90 Äê´ú³õ¾Í±»Ìá³ö£¬ÓÃÓÚ½â¾ö·Ö²¼Ê½ÊÂÎñÁìÓòµÄÎÊÌ⣬ËûÒ²ÊÇ×îÔçµÄ·Ö²¼Ê½ÊÂÎñ´¦Àí·½°¸£¬ÒòΪÐèÒªÊý¾Ý¿âÄÚ²¿Ò²ÊÇÖ§³ÖXAģʽµÄ£¬±ÈÈçMYSQL£¬XAģʽ¾ßÓÐǿһÖÂÐÔµÄÌØµã£¬Òò´ËËû¶ÔÊý¾Ý¿âÕ¼ÓÃʱ¼ä±È½Ï³¤£¬ËùÒÔÐÔÄܱȽϵ͡£ + +XAģʽÊôÓÚÁ½½×¶ÎÌá½»¡£ + +1. µÚÒ»½×¶Î½øÐÐÊÂÎñ×¢²á£¬½«ÊÂÎñ×¢²áµ½TCÖУ¬Ö´ÐÐSQLÓï¾ä¡£ +2. µÚ¶þ½×¶ÎTCÅжÏÎÞÊÂÎñ³ö´í£¬Í¨ÖªËùÓÐÊÂÎñÌá½»£¬·ñÔò»Ø¹ö¡£ +3. ÔÚµÚÒ»µ½µÚ¶þ½×¶Î¹ý³ÌÖУ¬ÊÂÎñÒ»Ö±Õ¼ÓÐÊý¾Ý¿âËø£¬Òò´ËÐÔÄܱȽϵͣ¬µ«ÊÇËùÓÐÊÂÎñҪôһÆðÌá½»£¬ÒªÃ´Ò»Æð»Ø¹ö£¬ËùÒÔÄÜʵÏÖǿһÖÂÐÔ¡£ + +ÎÞÂÛÊÇATģʽ¡¢TCC»¹ÊÇSAGA£¬ÕâЩģʽµÄÌá³ö£¬¶¼ÊÇÔ´ÓÚXA¹æ·¶¶ÔijЩҵÎñ³¡¾°ÎÞ·¨Âú×ã¡£ + +### ʲôÊÇXAЭÒé + +XA¹æ·¶ÊÇX/OPEN×éÖ¯¶¨ÒåµÄ·Ö²¼Ê½ÊÂÎñ´¦Àí£¨DTP£¬Distributed Transaction Processing£©±ê×¼£¬XA¹æ·¶ÃèÊöÁËÈ«¾ÖÊÂÎñ¹ÜÀíÆ÷ºÍ¾Ö²¿×ÊÔ´¹ÜÀíÆ÷Ö®¼äµÄ½Ó¿Ú£¬XA¹æ·¶µÄÄ¿µÄÊÇÔÊÐí¶à¸ö×ÊÔ´(ÈçÊý¾Ý¿â£¬Ó¦Ó÷þÎñÆ÷£¬ÏûÏ¢¶ÓÁеȣ©ÔÚͬһÊÂÎñÖзÃÎÊ£¬ÕâÑù¿ÉÒÔʹ ACID ÊôÐÔ¿çÔ½Ó¦ÓóÌÐò¶ø±£³ÖÓÐЧ¡£ + +XA ¹æ·¶ ʹÓÃÁ½½×¶ÎÌá½»£¨2PC£¬Two-Phase Commit£©À´±£Ö¤ËùÓÐ×ÊԴͬʱÌá½»»ò»Ø¹öÈκÎÌØ¶¨µÄÊÂÎñ¡£ÒòΪXA¹æ·¶×îÔç±»Ìá³ö£¬ËùÒÔ¼¸ºõËùÓеÄÖ÷Á÷Êý¾Ý¿â¶¼±£ÓжÔXA¹æ·¶µÄÖ§³Ö¡£ + +·Ö²¼Ê½ÊÂÎñDTPÄ£ÐͶ¨ÒåµÄ½ÇÉ«ÈçÏ£º + +* AP£º¼´Ó¦ÓóÌÐò£¬¿ÉÒÔÀí½âΪʹÓÃDTP·Ö²¼Ê½ÊÂÎñµÄ³ÌÐò£¬ÀýÈç¶©µ¥·þÎñ¡¢¿â´æ·þÎñ¡£ +* RM£º×ÊÔ´¹ÜÀíÆ÷£¬¿ÉÒÔÀí½âΪÊÂÎñµÄ²ÎÓëÕߣ¬Ò»°ãÇé¿öÏÂÊÇÖ¸Ò»¸öÊý¾Ý¿âµÄʵÀý£¨MySql£©£¬Í¨¹ý×ÊÔ´¹ÜÀíÆ÷¶Ô¸ÃÊý¾Ý¿â½øÐпØÖÆ£¬×ÊÔ´¹ÜÀíÆ÷¿ØÖÆ×Å·ÖÖ§ÊÂÎñ¡£ +* TM£ºÊÂÎñ¹ÜÀíÆ÷£¬¸ºÔðЭµ÷ºÍ¹ÜÀíÊÂÎñ£¬ÊÂÎñ¹ÜÀíÆ÷¿ØÖÆ×ÅÈ«¾ÖÊÂÎñ£¬¹ÜÀíʵÎñÉúÃüÖÜÆÚ£¬²¢Ð­µ÷¸÷¸öRM¡£È«¾ÖÊÂÎñÊÇÖ¸·Ö²¼Ê½ÊÂÎñ´¦Àí»·¾³ÖУ¬ÐèÒª²Ù×÷¶à¸öÊý¾Ý¿â¹²Í¬Íê³ÉÒ»¸ö¹¤×÷£¬Õâ¸ö¹¤×÷¼´ÊÇÒ»¸öÈ«¾ÖÊÂÎñ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/85cb437444a4debfdbb835519a1b2fdd16ee7b.png "ͼƬ") + +DTPģʽ¶¨ÒåTMºÍRMÖ®¼äͨѶµÄ½Ó¿Ú¹æ·¶½ÐXA£¬¼òµ¥Àí½âΪÊý¾Ý¿âÌṩµÄ2PC½Ó¿ÚЭÒ飬»ùÓÚÊý¾Ý¿âµÄXAЭÒéÀ´ÊµÏÖµÄ2PCÓÖ³ÆÎªXA·½°¸¡£ + +ÏÖÔÚÓÐÓ¦ÓóÌÐò(AP)³ÖÓж©µ¥¿âºÍ¿â´æ¿â£¬Ó¦ÓóÌÐò(AP)ͨ¹ýTM֪ͨ¶©µ¥¿â(RM)ºÍ¿â´æ¿â(RM)£¬½øÐпۼõ¿â´æºÍÉú³É¶©µ¥£¬Õâ¸öʱºòRM²¢Ã»ÓÐÌá½»ÊÂÎñ£¬¶øÇÒËø¶¨×ÊÔ´¡£ + +µ±TMÊÕµ½Ö´ÐÐÏûÏ¢£¬Èç¹ûÓÐÒ»·½RMÖ´ÐÐʧ°Ü£¬·Ö±ðÏòÆäËûRMÒ²·¢ËͻعöÊÂÎñ£¬»Ø¹öÍê±Ï£¬ÊÍ·ÅËø×ÊÔ´¡£ + +µ±TMÊÕµ½Ö´ÐÐÏûÏ¢£¬RMÈ«²¿³É¹¦£¬ÏòËùÓÐRM·¢ÆðÌá½»ÊÂÎñ£¬Ìá½»Íê±Ï£¬ÊÍ·ÅËø×ÊÔ´¡£ + +![ͼƬ](https://s7.51cto.com/oss/202207/03/d264d0e03f64cdd85f1600518e2db31ee0b8a5.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/8298749403719fbb05d917a501c3dd3b1cc449.png "ͼƬ") + +·Ö²¼Ê½Í¨ÐÅЭÒéXA¹æ·¶£¬¾ßÌåÖ´ÐÐÁ÷³ÌÈçÏÂËùʾ£º + +![ͼƬ](https://s7.51cto.com/oss/202207/03/3609cd043edf05941832252b7e7ce838b95837.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/9235cb212a65d21b72549719fdc8b7e4cdd340.png "ͼƬ") + +µÚÒ»²½£ºAP´´½¨ÁËRM1,RM2µÄJDBCÁ¬½Ó¡£ + +µÚ¶þ²½£ºAP֪ͨÉú³ÉÈ«¾ÖÊÂÎïID£¬²¢°ÑRM1£¬RM2×¢²áµ½È«¾ÖÊÂÎñID¡£ + +µÚÈý²½£ºÖ´Ðжþ½×¶ÎЭÒéÖеĵÚÒ»½×¶Îprepare¡£ + +µÚËIJ½£º¸ù¾ÝprepareÇëÇ󣬾ö¶¨ÕûÌåÌá½»»ò»Ø¹ö¡£ + +µ«ÊǶÔÓÚXA¶øÑÔ£¬Èç¹ûÒ»¸ö²ÎÓëÈ«¾ÖÊÂÎñµÄ×ÊÔ´¡°Ê§Áª¡±ÁË£¬ÄÇô¾ÍÒâζ×ÅTMÊÕ²»µ½·ÖÖ§ÊÂÎñ½áÊøµÄÃüÁÄÇôËüËø¶¨µÄÊý¾Ý£¬½«»áÒ»Ö±±»Ëø¶¨£¬´Ó¶ø²úÉúËÀËø£¬Õâ¸öÒ²ÊÇSeataÐèÒªÖØµã½â¾öµÄÎÊÌâ¡£ + +ÔÚSeata¶¨ÒåµÄ·Ö²¼Ê½ÊÂÎñ¼Ü¹¹ÖУ¬ÀûÓÃÊÂÎñ×ÊÔ´(Êý¾Ý¾Ö¡¢ÏûÏ¢)µÈ¶ÔXAЭÒé½øÐÐÖ§³Ö£¬ÓÃXAЭÒéµÄ»úÖÆÀ´¹ÜÀí·ÖÖ§ÊÂÎñ¡£ + +![ͼƬ](https://s2.51cto.com/oss/202207/03/a93391c180ce2293be8525a483594c701ebcbd.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/52b22c57810685deea7412204aa069e494f863.png "ͼƬ") + +* Ö´Ðн׶Σº + +* ¿É»Ø¹ö£ºÒµÎñSQL²Ù×÷ÔÚXA·ÖÖ§ÖнøÐУ¬ÓÐ×ÊÔ´¹ÜÀíÆ÷¶ÔXAЭÒéµÄÖ§³ÖÀ´±£Ö¤¿É»Ø¹ö¡£ +* ³Ö¾Ã»¯£ºZA·ÖÖ§Íê³ÉÒÔºó£¬Ö´ÐÐ XA prepare£¬Í¬Ñù£¬ÓÉ×ÊÔ´¶ÔXAЭÒéµÄÖ§³ÖÀ´±£Ö¤³Ö¾Ã»¯¡£ + +* Íê³É½×¶Î£º + +### XA´æÔÚµÄÒâÒå + +* ·ÖÖ§Ìá½»£ºÖ´ÐÐXA·ÖÖ§µÄcommit¡£ +* ·ÖÖ§»Ø¹ö£ºÖ´ÐÐXA·ÖÖ§µÄrollback¡£ + +Seata ÒѾ­Ö§³ÖÁËÈý´óÊÂÎñģʽ£ºAT\TCC\SAGA£¬ÕâÈý¸ö¶¼ÊDz¹³¥ÐÍÊÂÎñ£¬²¹³¥ÐÍÊÂÎñ´¦ÀíÄã»úÖÆ¹¹½¨ÔÚ ÊÂÎñ×ÊÔ´ Ö®ÉÏ(ҪôÖмä¼þ²ãÃæ£¬ÒªÃ´Ó¦Óòã)£¬ÊÂÎñ×ÊÔ´±¾Éí¶ÔÓÚ·Ö²¼Ê½µÄÊÂÎñÊÇÎÞ¸ÐÖªµÄ£¬ÕâÖÖ¶ÔÓÚ·Ö²¼Ê½ÊÂÎñµÄÎÞ¸ÐÖª´æÔÚÓÐÒ»¸ö¸ù±¾ÐÔµÄÎÊÌ⣬ÎÞ·¨×öµ½ÕæÕýµÄÈ«¾ÖÒ»ÖÂÐÔ¡£ + +ÀýÈçÒ»¸ö¿â´æ¼Ç¼£¬ÔÚ²¹³¥ÐÍÊÂÎñ´¦Àí¹ý³ÌÖУ¬ÓÃ80¿Û¼õΪ60£¬Õâ¸öʱºò²Ö¿â¹ÜÀíÔ±²éѯÊý¾Ý½á¹û£¬¿´µ½µÄÊÇ60£¬Ö®ºóÒòΪÒì³£»Ø¹ö£¬¿â´æ»Ø¹öµ½Ô­À´µÄ80£¬ÄÇôÕâ¸öʱºò¿â´æ¹ÜÀíÔ±¿´µ½µÄ60£¬Æäʵ¾ÍÊÇÔàÊý¾Ý£¬¶øÕâ¸öÖмä״̬¾ÍÊDz¹³¥ÐÍÊÂÎñ´æÔÚµÄÔàÊý¾Ý¡£ + +ºÍ²¹³¥ÐÍÊÂÎñ²»Í¬£¬XAЭÒéÒªÇóÊÂÎñ×ÊÔ´ ±¾ÉíÌṩ¶Ô¹æ·¶ºÍЭÒéµÄÖ§³Ö£¬ÒòΪÊÂÎñ×ÊÔ´¸ÐÖª²¢²ÎÓë·Ö²¼Ê½ÊÂÎñ´¦Àí¹ý³ÌÖУ¬ËùÒÔÊÂÎñ×ÊÔ´¿ÉÒÔ±£Ö¤´ÓÈÎÒâÊӽǶÔÊý¾ÝµÄ·ÃÎÊÓÐЧ¸ôÀëÐÔ£¬Âú×ãÈ«¾ÖÊý¾ÝµÄÒ»ÖÂÐÔ¡£ + +### XAģʽµÄʹÓà + +**¹Ù·½°¸Àý£º**https://github.com/seata/seata-samples¡£ + +**ÏîÄ¿Ãû£º**seata-samples¡£ + +**ÒµÎñ¿ªÊ¼£º**business-xa¿â´æ·þÎñ£ºstock-xa¶©µ¥·þÎñ£ºorder-xaÕ˺ŷþÎñ£ºaccount-xa¡£ + +°ÑÕâ¸öÏîÄ¿°¸ÀýÏÂÔØÏÂÀ´ÒÔºó£¬ÕÒµ½ÏîÄ¿ÃûΪseata-xaµÄĿ¼£¬ÀïÃæÓвâÊÔÊý¾Ý¿âµÄÁ´½Ó£¬Èç¹û²»ÏëÓòâÊÔÊý¾Ý¿â£¬Ö»ÐèÒªÐ޸Ĺٷ½ÎĵµÖÐÊý¾Ý¿âÅäÖÃÐÅÏ¢¼´¿É¡£ + +Ê×ÏȹØ×¢µÄÊÇ business-xaÏîÄ¿£¬¸ü¶àµÄ¹Ø×¢BusinessService.purchase()·½·¨¡£ + + + + + + + + + + +``` +@GlobalTransactional + public void purchase(String userId, String commodityCode, int orderCount, boolean rollback) { + String xid = RootContext.getXID(); + LOGGER.info("New Transaction Begins: " + xid); + //µ÷ÓÃ¿â´æ¼õ¿â´æ + String result = stockFeignClient.deduct(commodityCode, orderCount); + if (!SUCCESS.equals(result)) { + throw new RuntimeException("¿â´æ·þÎñµ÷ÓÃʧ°Ü,ÊÂÎñ»Ø¹ö!"); + } + //Éú³É¶©µ¥ + result = orderFeignClient.create(userId, commodityCode, orderCount); + if (!SUCCESS.equals(result)) { + throw new RuntimeException("¶©µ¥·þÎñµ÷ÓÃʧ°Ü,ÊÂÎñ»Ø¹ö!"); + } + if (rollback) { + throw new RuntimeException("Force rollback ... "); + } + } +``` + + + + + + + + + + + +ÆäʵÏÖ·½·¨½Ï֮ǰ²î²»¶à£¬ÎÒÃÇÖ»ÐèÒªÔÚorder-xaÀïÃæ(OrderService.create)£¬Ìí¼ÓÈËΪ´íÎó(int i = 1/0;)¡£ + + + + + + + + + + + +``` +public void create(String userId, String commodityCode, Integer count) { + String xid = RootContext.getXID(); + LOGGER.info("create order in transaction: " + xid); + int i = 1/0; + // ¶¨µ¥×Ü¼Û = ¶©¹ºÊýÁ¿(count) * ÉÌÆ·µ¥¼Û(100) + int orderMoney = count * 100; + // Éú³É¶©µ¥ + jdbcTemplate.update("insert order_tbl(user_id,commodity_code,count,money) values(?,?,?,?)", + new Object[] {userId, commodityCode, count, orderMoney}); + // µ÷ÓÃÕË»§Óà¶î¿Û¼õ + String result = accountFeignClient.reduce(userId, orderMoney); + if (!SUCCESS.equals(result)) { + throw new RuntimeException("Failed to call Account Service. "); + } + + } +``` + + + + + + + + + + + +ÀïÃæÓÐÒ»¸ö·½·¨¿ÉÒÔ½øÐÐXAģʽºÍATģʽµÄת»»OrderXADataSourceConfiguration.dataSource¡£ + + + + + + + + + + +``` +@Bean("dataSourceProxy") + public DataSource dataSource(DruidDataSource druidDataSource) { + // DataSourceProxy for AT mode + // return new DataSourceProxy(druidDataSource); + // DataSourceProxyXA for XA mode + return new DataSourceProxyXA(druidDataSource); + } +``` + + + + + + + + + + + +ÎÒÃÇÆô¶¯ÕâËĸö·þÎñ£¬·ÃÎʵØÖ· http://localhost:8084/purchase¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d82378852259a962ae041377e8433a31eb8602.png "ͼƬ") + +ÎÒÃÇ¿ÉÒÔÆäÖб¨´í£¬È»ºóÔÙÈ¥¿´¶ÔÓ¦Êý¾Ý¿âµÄÊý¾Ý£¬Ã»Óз¢Éú¸ü¸Ä£¬ËµÃ÷ÎÒÃǵÄXAģʽÉúЧÁË£¬µ±ÄãdubugÈ¥¿´ÀïÃæµÄ¿â´æ·þÎñµÄʱºò£¬µ±²Ù×÷Êý¾Ý¸ü¸ÄµÄʱºò£¬Êý¾Ý¿âÀïÃæÆäʵҲÊÇûÓмǼµÄ£¬ÒòΪXAÊÇǿһÖÂÐÔ£¬Ö»Óе±ÊÂÎñ½áÊøÍê³ÉÒԺ󣬲Żá¸ü¸ÄÆäÖеÄÊý¾Ý¡£ + +XAģʽµÄ¼ÓÈ룬²¹ÆëÁËSeataÔÚÈ«¾ÖÒ»ÖÂÐÔ³¡¾°ÏµÄȱ¿Ú£¬ÐγÉÁËAT¡¢TCC¡¢Saga¡¢XA ËÄ´óÊÂÎñģʽµÄ°æÍ¼£¬»ù±¾Âú×ãÁËËùÓг¡¾°·Ö²¼Ê½ÊÂÎñ´¦ÀíµÄÐèÇó¡£ + +ÆäÖÐXAºÍATÊÇÎÞÒµÎñÇÖÈëµÄ£¬¶øTCCºÍSagaÊÇÓÐÒ»¶¨ÒµÎñÇÖÈëµÄ¡£ + +## Seata-ATģʽ + +ÏÈÀ´½éÉÜÒ»ÏÂATģʽ£¬ATģʽÊÇÒ»ÖÖûÓÐÇÖÈëµÄ·Ö²¼Ê½ÊÂÎñµÄ½â¾ö·½°¸£¬ÔÚATģʽÏ£¬Óû§Ö»Ðè¹Ø×¢×Ô¼ºµÄÒµÎñSQL£¬Óû§µÄÒµÎñSQL×÷Ϊһ½×¶Î£¬Seata¿ò¼Ü»á×Ô¶¯Éú³ÉÊÂÎñ½øÐжþ½×¶ÎÌá½»ºÍ»Ø¹ö²Ù×÷¡£ + +**Á½½×¶ÎÌύЭÒéµÄÑݱ䣺** + +* Ò»½×¶Î£ºÒµÎñÊý¾ÝºÍ»Ø¹öÈÕÖ¾¼Ç¼ÔÚͬһ¸ö±¾µØÊÂÎñÖÐÌá½»£¬Êͷű¾µØËøºÍÁ¬½Ó×ÊÔ´¡£ +* ¶þ½×¶Î£º + Ìá½»Òì²½»¯£¬·Ç³£¿ìËÙµØÍê³É¡£ + »Ø¹öͨ¹ýÒ»½×¶ÎµÄ»Ø¹öÈÕÖ¾½øÐз´Ïò²¹³¥¡£ + +#### ATģʽÖ÷ÒªÌØµã + +1. ×îÖÕÒ»ÖÂÐÔ¡£ +2. ÐÔÄܽÏXA¸ß¡£ +3. Ö»ÔÚµÚÒ»½×¶Î»ñÈ¡Ëø£¬ÔÚµÚÒ»½×¶Î½øÐÐÌá½»ºóÊÍ·ÅËø¡£ + +ÔÚÒ»½×¶ÎÖУ¬Seata»áÀ¹½Ø ÒµÎñSQL £¬Ê×ÏȽâÎöSQLÓïÒ壬ÕÒµ½Òª²Ù×÷µÄÒµÎñÊý¾Ý£¬ÔÚÊý¾Ý±»²Ù×÷ǰ£¬±£´æÏÂÀ´¼Ç¼ undo log£¬È»ºóÖ´ÐÐ ÒµÎñSQL ¸üÐÂÊý¾Ý£¬¸üÐÂÖ®ºóÔٴα£´æÊý¾Ý redo log£¬×îºóÉú³ÉÐÐËø£¬ÕâЩ²Ù×÷¶¼ÔÚ±¾µØÊý¾Ý¿âÊÂÎñÄÚÍê³É£¬ÕâÑù±£Ö¤ÁËÒ»½×¶ÎµÄÔ­×ÓÐÔ¡£ + +Ïà¶ÔÒ»½×¶Î£¬¶þ½×¶Î±È½Ï¼òµ¥£¬¸ºÔðÕûÌåµÄ»Ø¹öºÍÌá½»£¬Èç¹û֮ǰµÄÒ»½×¶ÎÖÐÓб¾µØÊÂÎñûÓÐͨ¹ý£¬ÄÇô¾ÍÖ´ÐÐÈ«¾Ö»Ø¹ö£¬·ñÔÚÖ´ÐÐÈ«¾ÖÌá½»£¬»Ø¹öÓõ½µÄ¾ÍÊÇÒ»½×¶Î¼Ç¼µÄ undo Log £¬Í¨¹ý»Ø¹ö¼Ç¼Éú³É·´Ïò¸üÐÂSQL²¢Ö´ÐУ¬ÒÔÍê³É·ÖÖ§µÄ»Ø¹ö¡£µ±È»ÊÂÎñÍê³Éºó»áÊÍ·ÅËùÓÐ×ÊÔ´ºÍɾ³ýËùÓÐÈÕÖ¾¡£ + +ATÁ÷³Ì·ÖΪÁ½½×¶Î£¬Ö÷ÒªÂß¼­È«²¿ÔÚµÚÒ»½×¶Î£¬µÚ¶þ½×¶ÎÖ÷Òª×ö»Ø¹ö»òÈÕÖ¾ÇåÀíµÄ¹¤×÷¡£Á÷³ÌÈçÏ£º + +![ͼƬ](https://s9.51cto.com/oss/202207/03/c7d9f9e7001e9fca3d8309b146f3014ecfbf06.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/02880c46589d54a3f12449afa76a44e9b8e73a.png "ͼƬ") + +´ÓÉÏͼÖÐÎÒÃÇ¿ÉÒÔ¿´µ½£¬¶©µ¥·þÎñÖÐTMÏòTCÉêÇ뿪ÆôÒ»¸öÈ«¾ÖÊÂÎñ£¬Ò»°ãͨ¹ý@GlobalTransactional±ê×¢¿ªÆô£¬TC»á·µ»ØÒ»¸öÈ«¾ÖÊÂÎñID(XID)£¬¶©µ¥·þÎñÔÚÖ´Ðб¾µØÊÂÎñ֮ǰ£¬RM»áÏÈÏòTC×¢²áÒ»¸ö·ÖÖ§ÊÂÎñ£¬ ¶©µ¥·þÎñÒÀ´ÎÉú³Éundo log Ö´Ðб¾µØÊÂÎñ£¬Éú³Éredo log Ìá½»±¾µØÊÂÎñ£¬ÏòTC»ã±¨£¬ÊÂÎñÖ´ÐÐOK¡£ + +¶©µ¥·þÎñ·¢ÆðÔ¶³Ìµ÷Ó㬽«ÊÂÎñID´«µÝ¸ø¿â´æ·þÎñ£¬¿â´æ·þÎñÔÚÖ´Ðб¾µØÊÂÎñ֮ǰ£¬ÏÈÏòTC×¢²á·ÖÖ§ÊÂÎñ£¬¿â´æ·þÎñͬÑùÉú³Éundo LogºÍredo Log£¬ÏòTC»ã±¨£¬ÊÂÎñ״̬³É¹¦¡£ + +Èç¹ûÕý³£È«¾ÖÌá½»£¬TC֪ͨRMÒ»²½ÇåÀíµô±¾µØundoºÍredoÈÕÖ¾£¬Èç¹û´æÔÚÒ»¸ö·þÎñÖ´ÐÐʧ°Ü£¬ÄÇô·¢Æð»Ø¹öÇëÇó¡£Í¨¹ýundo log½øÐлعö¡£ + +ÔÚÕâÀﻹ»á´æÔÚÒ»¸öÎÊÌ⣬ÒòΪÿ¸öÊÂÎñ´Ó±¾µØÌá½»µ½Í¨Öª»Ø¹öÕâ¶Îʱ¼äÀïÃæ£¬¿ÉÄÜÕâÌõÊý¾ÝÒѾ­±»ÆäËûÊÂÎñ½øÐÐÐ޸ģ¬Èç¹ûÖ±½ÓÓÃundo log½øÐлعö£¬¿ÉÄܻᵼÖÂÊý¾Ý²»Ò»ÖµÄÇé¿ö£¬ + +Õâ¸öʱºò RM»áÓà redo log½øÐÐÑéÖ¤£¬¶Ô±ÈÊý¾ÝÊÇ·ñÒ»Ñù£¬´Ó¶øµÃÖªÊý¾ÝÊÇ·ñÓбðµÄÊÂÎñ½øÐÐÐ޸Ĺý£¬undo logÊÇÓÃÓÚ±»ÐÞ¸ÄǰµÄÊý¾Ý£¬¿ÉÒÔÓÃÀ´»Ø¹ö£¬redologÊÇÓÃÓÚ±»Ð޸ĺóµÄÊý¾Ý£¬ÓÃÓڻعöУÑé¡£ + +Èç¹ûÊý¾ÝûÓб»ÆäËûÊÂÎñÐ޸Ĺý£¬¿ÉÒÔÖ±½Ó½øÐлعö£¬Èç¹ûÊÇÔàÊý¾Ý£¬redologУÑéºó½øÐд¦Àí¡£ + +### ʵս + +Á˽âÁËATÄ£Ð͵Ļù±¾²Ù×÷£¬½ÓÏÂÀ´¾ÍÀ´ÊµÕ½²Ù×÷һϣ¬¹ØÓÚATÄ£Ð;ßÌåÊÇÈçºÎʵÏֵġ£Ê×ÏÈÉè¼ÆÁ½¸ö·þÎñ cloud-alibaba-seata-order ºÍ cloud-alibaba-seata-stock¡£ + +±í½á¹¹t_order¡¢t_stockºÍundo_logÈýÕÅ±í£¬ÏîĿԴÂëºÍ±í½á¹¹£¬¼ÓÉÏundo_log±í£¬´Ë±íÓÃÓÚÊý¾ÝµÄ»Ø¹ö£¬ÎÄÄ©ÓÐÁ´½Ó¡£ + +cloud-alibaba-seata-orderºËÐÄ´úÂëÈçÏ£º + +controller£º + + + + + + + + + + +``` +@RestController +public class OrderController { + @Autowired + private OrderService orderService; + @GetMapping("order/create") + @GlobalTransactional //¿ªÆô·Ö²¼Ê½ÊÂÎñ + public String create(){ + orderService.create(); + return "¶©µ¥´´½¨³É¹¦£¡"; + } +} +``` + + + + + + + + + + + +OrderService£º + + + + + + + + + + +``` +public interface OrderService { + void create(); +} +``` + + + + + + + + + + + +StockClient£º + + + + + + + + + + +``` +@FeignClient(value = "seata-stock") +public interface StockClient { + @GetMapping("/stock/reduce") + String reduce(); + +} +``` + + + + + + + + + + + +OrderServiceImpl£º + + + + + + + + + + +``` +@Service +public class OrderServiceImpl implements OrderService{ + @Autowired + private OrderMapper orderMapper; + @Autowired + private StockClient stockClient; + @Override + public void create() { + //¿Û¼õ¿â´æ + stockClient.reduce(); + System.out.println("¿Û¼õ¿â´æ³É¹¦£¡"); + //ÊÖ¹¤Òì³£ ÓÃÓڻعö¿â´æÐÅÏ¢ + int i = 1/0; + System.err.println("Òì³££¡"); + //´´½¨¶©µ¥ + orderMapper.createOrder(); + System.out.println("´´½¨¶©µ¥³É¹¦£¡"); + } +} +``` + + + + + + + + + + + +OrderMapper£º + + + + + + + + + + +``` +@Mapper +public interface OrderMapper { + @Insert("insert into t_order (order_no,order_num) value (order_no+1,1)") + void createOrder(); +} +``` + + + + + + + + + + + +cloud-alibaba-seata-stockºËÐÄ´úÂëÈçÏ£º + + + + + + + + + + +``` +@RestController +public class StockController { + @Autowired + private StockService stockService; + @GetMapping("stock/reduce") + public String reduce(){ + stockService.reduce(); + return "¿â´æÊýÁ¿Òѿۼõ£º"+ new Date(); + } +} +``` + + + + + + + + + + + + + + + + + + + + +``` +public interface StockService { + void reduce(); +} +``` + + + + + + + + + + + + + + + + + + + + +``` +@Service +public class StockServiceImpl implements StockService{ + @Autowired + StockMapper stockMapper; + @Override + public void reduce() { + stockMapper.reduce(); + } +} +``` + + + + + + + + + + + + + + + + + + + + +``` +@Mapper +@Repository +public interface StockMapper { + @Update("update t_stock set order_num = order_num - 1 where order_no = 1 ") + void reduce(); + +} +``` + + + + + + + + + + + +´úÂë¶¼±È½Ï¼òµ¥£¬ÎÒÃǾͲ»×ö¹ý¶àµÄÃèÊö£¬»ù±¾×¢ÊÍÒ²¶¼ÓУ¬£¬Ê×ÏÈÎÒÃÇÐèÒª½«orderºÍstock·þÎñ¶¼ÅÜÆðÀ´£¬ÔÚ֮ǰÎÒÃǵÄNacosºÍSeata¶¼ÒªÆô¶¯ÆðÀ´£¬Õâ¸öʱºòÎÒÃÇ·ÃÎÊorderµÄRest½Ó¿Ú£¬http://localhost:8087/order/create,ΪÁËÑéÖ¤undo_logµÄ±íÊÇÓÃÓÚ´æ´¢»Ø¹öÊý¾Ý£¬ÎÒÃÇÔÚOrderServiceImpl.create()ÖÐÌí¼Ó¶Ïµã£¬ÓÃdebugµÄ·½Ê½Æô¶¯¡£ + +![ͼƬ](https://s5.51cto.com/oss/202207/03/e9df0649789b2ad3b0a9120c1992f9a7e55cb9.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/64e370d42b8375a8f7a1285cc1bc47b1437bff.png "ͼƬ") + +È»ºó·ÃÎÊhttp://localhost:8087/order/create£¬µ±³ÌÐò¿¨ÔÚÕâ¸ö½ÚµãµÄʱ¼ä£¬ÎÒÃÇÈ¥¿´undo_logºÍ¿â´æ±í£¬»á·¢ÏÖ£¬¿â´æÈ·Êµ¼õÉÙÁË£¬¶øÇÒundo_logÒ²³öÏÖÁ˶ÔÓ¦µÄ¿ìÕռǼÐ޸ĵ±Ç°µÄÊý¾ÝÐÅÏ¢£¬Õâ¸öÊý¾Ý¾ÍÊÇÓÃÀ´»Ø¹öµÄÊý¾Ý¡£ + +![ͼƬ](https://s8.51cto.com/oss/202207/03/9361fc3173dcb931a615608893af1f48fbeed0.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/567b564906d2c473d3d770087b745a5218587f.png "ͼƬ") + +µ«Êǵ±ÎÒÃÇF9ͨ¹ýÒÔºó£¬¿â´æÊýÁ¿»Ö¸´£¬²¢ÇÒundo_log±íµÄÊý¾ÝÐÐҲûÓÐÁË£¬Õâ¸öʱºòÖ¤Ã÷ÎÒÃǵÄSeataÊÂÎñÉúЧ£¬»Ø¹ö³É¹¦¡£ + +![ͼƬ](https://s8.51cto.com/oss/202207/03/66c11ca96212ce62540349442f14ccff8b87a4.gif "ͼƬ")![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/75cd0d103bd57d300e5194f66aa826f8494b94.png "ͼƬ") + +µ½ÕâÀïÎÒÃǾÍÑéÖ¤ÁËATÊÂÎñµÄÖ´Ðйý³Ì£¬Ïà±ÈÓÚXAºÍTCCµÈÊÂÎñÄ£ÐÍ£¬SeataµÄATÄ£ÐÍ¿ÉÒÔÓ¦¶Ô´ó¶àÊýµÄÒµÎñ³¡¾°£¬²¢ÇÒ¿ÉÒÔ×öµ½ÎÞÒµÎñÇÖÈ룬¿ª·¢ÕßÎÞ¸ÐÖª£¬¶ÔÓÚÕû¸öÊÂÎñµÄЭµ÷¡¢Ìá½»»òÕ߻عö²Ù×÷£¬¶¼¿ÉÒÔͨ¹ýAOPÍê³É£¬¿ª·¢ÕßÖ»ÐèÒª¹Ø×¢ÒµÎñ¼´¿É¡£ + +ÓÉÓÚSeataÐèÒªÔÚ²»Í¬µÄ·þÎñÖ®¼ä´«µÝÈ«¾ÖΨһµÄÊÂÎñID£¬ºÍDubboµÈ¿ò¼Ü¼¯³É»á±È½ÏÓѺã¬ÀýÈçDubbo¿ÉÒÔÓùýÒþÊ¿´«²ÎÀ´½øÐÐÊÂÎñIDµÄ´«µÝ£¬Õû¸öÊÂÎñIDµÄ´«²¥¹ý³Ì¶Ô¿ª·¢ÕßÒ²¿ÉÒÔ×öµ½ÎÞ¸ÐÖª¡£ + +## Seata-TCCģʽ + +¾ßÌåʹÓð¸Àý£ºhttps://seata.io/zh-cn/blog/integrate-seata-tcc-mode-with-spring-cloud.html¡£ + +### ʲôÊÇTCC + +TCC ÊÇ·Ö²¼Ê½ÊÂÎñÖеĶþ½×¶ÎÌύЭÒ飬ËüµÄÈ«³ÆÎª Try-Confirm-Cancel£¬¼´×ÊÔ´Ô¤Áô£¨Try£©¡¢È·ÈϲÙ×÷£¨Confirm£©¡¢È¡Ïû²Ù×÷£¨Cancel£©£¬ËûÃǵľßÌ庬ÒåÈçÏ£º + +1. Try£º¶ÔÒµÎñ×ÊÔ´µÄ¼ì²é²¢Ô¤Áô¡£ +2. Confirm£º¶ÔÒµÎñ´¦Àí½øÐÐÌá½»£¬¼´ commit ²Ù×÷£¬Ö»Òª Try ³É¹¦£¬ÄÇô¸Ã²½ÖèÒ»¶¨³É¹¦¡£ +3. Cancel£º¶ÔÒµÎñ´¦Àí½øÐÐÈ¡Ïû£¬¼´»Ø¹ö²Ù×÷£¬¸Ã²½Öè»Ø¶Ô Try Ô¤ÁôµÄ×ÊÔ´½øÐÐÊÍ·Å¡£ + +TCC ÊÇÒ»ÖÖÇÖÈëʽµÄ·Ö²¼Ê½ÊÂÎñ½â¾ö·½°¸£¬ÒÔÉÏÈý¸ö²Ù×÷¶¼ÐèÒªÒµÎñϵͳ×ÔÐÐʵÏÖ£¬¶ÔÒµÎñϵͳÓÐ×ŷdz£´óµÄÈëÇÖÐÔ£¬Éè¼ÆÏà¶Ô¸´ÔÓ£¬µ«ÓŵãÊÇ TCC ÍêÈ«²»ÒÀÀµÊý¾Ý¿â£¬Äܹ»ÊµÏÖ¿çÊý¾Ý¿â¡¢¿çÓ¦ÓÃ×ÊÔ´¹ÜÀí£¬¶ÔÕâЩ²»Í¬Êý¾Ý·ÃÎÊͨ¹ýÇÖÈëʽµÄ±àÂ뷽ʽʵÏÖÒ»¸öÔ­×Ó²Ù×÷£¬¸üºÃµØ½â¾öÁËÔÚ¸÷ÖÖ¸´ÔÓÒµÎñ³¡¾°Ïµķֲ¼Ê½ÊÂÎñÎÊÌâ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1431eaa67529de68d4f9506ae25dc17c3882b8.png "ͼƬ") + +### TCCºÍATÇø±ð + +AT ģʽ»ùÓÚ Ö§³Ö±¾µØ ACID ÊÂÎñ µÄ ¹ØÏµÐÍÊý¾Ý¿â£º + +* Ò»½×¶Î prepare ÐÐΪ£ºÔÚ±¾µØÊÂÎñÖУ¬Ò»²¢Ìá½»ÒµÎñÊý¾Ý¸üкÍÏàÓ¦»Ø¹öÈÕÖ¾¼Ç¼¡£ +* ¶þ½×¶Î commit ÐÐΪ£ºÂíÉϳɹ¦½áÊø£¬**×Ô¶¯**Òì²½ÅúÁ¿ÇåÀí»Ø¹öÈÕÖ¾¡£ +* ¶þ½×¶Î rollback ÐÐΪ£ºÍ¨¹ý»Ø¹öÈÕÖ¾£¬**×Ô¶¯**Éú³É²¹³¥²Ù×÷£¬Íê³ÉÊý¾Ý»Ø¹ö¡£ + +ÏàÓ¦µÄ£¬TCC ģʽ£¬²»ÒÀÀµÓڵײãÊý¾Ý×ÊÔ´µÄÊÂÎñÖ§³Ö£º + +* Ò»½×¶Î prepare ÐÐΪ£ºµ÷ÓÃ**×Ô¶¨Òå**µÄ prepare Âß¼­¡£ +* ¶þ½×¶Î commit ÐÐΪ£ºµ÷ÓÃ**×Ô¶¨Òå**µÄ commit Âß¼­¡£ +* ¶þ½×¶Î rollback ÐÐΪ£ºµ÷ÓÃ**×Ô¶¨Òå**µÄ rollback Âß¼­¡£ + +Ëùν TCC ģʽ£¬ÊÇÖ¸Ö§³Ö°Ñ **×Ô¶¨Òå** µÄ·ÖÖ§ÊÂÎñÄÉÈ뵽ȫ¾ÖÊÂÎñµÄ¹ÜÀíÖС£ + +#### ÌØµã£º + +1. ÇÖÈëÐԱȽÏÇ¿£¬²¢ÇÒÐèÒª×Ô¼ºÊµÏÖÏà¹ØÊÂÎñ¿ØÖÆÂß¼­ +2. ÔÚÕû¸ö¹ý³Ì»ù±¾Ã»ÓÐËø£¬ÐÔÄܽÏÇ¿ + +## Seata-Sagaģʽ + +SagaģʽÊÇSEATAÌṩµÄ³¤ÊÂÎñ½â¾ö·½°¸£¬ÔÚSagaģʽÖУ¬ÒµÎñÁ÷³ÌÖÐÿ¸ö²ÎÓëÕß¶¼Ìá½»±¾µØÊÂÎñ£¬µ±³öÏÖijһ¸ö²ÎÓëÕßʧ°ÜÔò²¹³¥Ç°ÃæÒѾ­³É¹¦µÄ²ÎÓëÕߣ¬Ò»½×¶ÎÕýÏò·þÎñºÍ¶þ½×¶Î²¹³¥·þÎñ£¨Ö´Ðд¦Àíʱºò³ö´íÁË£¬¸øÒ»¸öÐÞ¸´µÄ»ú»á£©¶¼ÓÉÒµÎñ¿ª·¢ÊµÏÖ¡£ + +Saga ģʽÏ·ֲ¼Ê½ÊÂÎñͨ³£ÊÇÓÉʼþÇý¶¯µÄ£¬¸÷¸ö²ÎÓëÕßÖ®¼äÊÇÒì²½Ö´Ðеģ¬Saga ģʽÊÇÒ»ÖÖ³¤ÊÂÎñ½â¾ö·½°¸¡£ + +֮ǰÎÒÃÇѧϰµÄSeata·Ö²¼Ê½ÈýÖÖ²Ù×÷Ä£ÐÍÖÐËùʹÓõĵÄ΢·þÎñÈ«²¿¿ÉÒÔ¸ù¾Ý¿ª·¢ÕßµÄÐèÇó½øÐÐÐ޸쬵«ÊÇÔÚÒ»Ð©ÌØÊâ»·¾³Ï£¬±ÈÈçÀÏϵͳ£¬·â±ÕµÄϵͳ£¨ÎÞ·¨Ð޸ģ¬Í¬Ê±Ã»ÓÐÈκηֲ¼Ê½ÊÂÎñÒýÈ룩£¬ÄÇôAT¡¢XA¡¢TCCÄ£Ðͽ«È«²¿²»ÄÜʹÓã¬ÎªÁ˽â¾öÕâÑùµÄÎÊÌ⣬²ÅÒýÓÃÁËSagaÄ£ÐÍ¡£ + +±ÈÈ磺ÊÂÎñ²ÎÓëÕß¿ÉÄÜÊÇÆäËû¹«Ë¾µÄ·þÎñ»òÕßÊÇÒÅÁôϵͳ£¬ÎÞ·¨¸ÄÔ죬¿ÉÒÔʹÓÃSagaģʽ¡£ + +![ͼƬ](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e923ef663d8c0a9de06072d96c6debbac49795.png "ͼƬ") + +SagaģʽÊÇSeataÌṩµÄ³¤ÊÂÎñ½â¾ö·½°¸£¬ÌṩÁËÒ칹ϵͳµÄÊÂÎñͳһ´¦ÀíÄ£ÐÍ¡£ÔÚSagaģʽÖУ¬ËùÓеÄ×ÓÒµÎñ¶¼²»ÔÚÖ±½Ó²ÎÓëÕûÌåÊÂÎñµÄ´¦Àí£¨Ö»¸ºÔð±¾µØÊÂÎñµÄ´¦Àí£©£¬¶øÊÇÈ«²¿½»ÓÉÁË×îÖÕµ÷ÓöËÀ´¸ºÔðʵÏÖ£¬¶øÔÚ½øÐÐ×ÜÒµÎñÂß¼­´¦Àíʱ£¬ÔÚijһ¸ö×ÓÒµÎñ³öÏÖÎÊÌâʱ£¬Ôò×Ô¶¯²¹³¥È«ÃæÒѾ­³É¹¦µÄÆäËû²ÎÓëÕߣ¬ÕâÑùÒ»½×¶ÎµÄÕýÏò·þÎñµ÷ÓúͶþ½×¶ÎµÄ·þÎñ²¹³¥´¦ÀíÈ«²¿ÓÉ×ÜÒµÎñ¿ª·¢ÊµÏÖ¡£ + +### Saga״̬»ú + +ĿǰSeataÌṩµÄSagaģʽֻÄÜͨ¹ý״̬»úÒýÇæÀ´ÊµÏÖ£¬ÐèÒª¿ª·¢ÕßÊÖ¹¤µÄ½øÐÐSagaÒµÎñÁ÷³Ì»æÖÆ£¬²¢ÇÒ½«Æäת»»ÎªJsonÅäÖÃÎļþ£¬¶øºóÔÚ³ÌÐòÔËÐÐʱ£¬½«ÒÀ¾Ý×ÓÅäÖÃÎļþʵÏÖÒµÎñ´¦ÀíÒÔ¼°·þÎñ²¹³¥´¦Àí£¬¶øÒªÏë½øÐÐSaga״̬ͼµÄ»æÖÆ£¬Ò»°ãÐèҪͨ¹ýSaga״̬»úÀ´ÊµÏÖ¡£ + +#### »ù±¾Ô­Àí£º + +* ͨ¹ý״̬ͼÀ´¶¨Òå·þÎñµ÷ÓõÄÁ÷³Ì²¢Éú³Éjson¶¨ÒåÎļþ¡£ +* ״̬ͼÖÐÒ»¸ö½Úµã¿ÉÒÔµ÷ÓÃÒ»¸ö·þÎñ£¬½Úµã¿ÉÒÔÅäÖÃËüµÄ²¹³¥½Úµã¡£ +* ״̬ͼ json ÓÉ״̬»úÒýÇæÇý¶¯Ö´ÐУ¬µ±³öÏÖÒ쳣ʱ״̬ÒýÇæ·´ÏòÖ´ÐÐÒѳɹ¦½Úµã¶ÔÓ¦µÄ²¹³¥½Úµã½«ÊÂÎñ»Ø¹ö¡£ +* ¿ÉÒÔʵÏÖ·þÎñ±àÅÅÐèÇó£¬Ö§³Öµ¥ÏîÑ¡Ôñ¡¢²¢·¢¡¢×ÓÁ÷³Ì¡¢²ÎÊýת»»¡¢²ÎÊýÓ³Éä¡¢·þÎñÖ´ÐÐ״̬Åжϡ¢Òì³£²¶»ñµÈ¹¦ÄÜ¡£ + +#### Saga״̬»úµÄÓ¦Óà + +**¹Ù·½ÎĵµµØÖ·£º**https://seata.io/zh-cn/docs/user/saga.html¡£ + +Seata Safa״̬»ú¿ÉÊÓ»¯Í¼ÐÎÉè¼ÆÆ÷ʹÓõØÖ·£ºhttps://github.com/seata/seata/blob/develop/saga/seata-saga-statemachine-designer/README.zh-CN.md¡£ + +## ×ܽá + +×ܵÄÀ´ËµÔÚSeataµÄÖÐATģʽ»ù±¾¿ÉÒÔÂú×ã°Ù·ÖÖ®80µÄ·Ö²¼Ê½ÊÂÎñµÄÒµÎñÐèÇó£¬ATģʽʵÏÖµÄÊÇ×îÖÕÒ»ÖÂÐÔ£¬ËùÒÔ¿ÉÄÜ´æÔÚÖмä״̬£¬¶øXAģʽʵÏÖµÄǿһÖÂÐÔ£¬ËùÒÔЧÂʽϵÍÒ»µã£¬¶øSaga¿ÉÒÔÓÃÀ´´¦Àí²»Í¬¿ª·¢ÓïÑÔÖ®¼äµÄ·Ö²¼Ê½ÊÂÎñ£¬ËùÒÔ¹ØÓÚ·Ö²¼Ê½ÊÂÎñµÄËÄ´óÄ£ÐÍ£¬»ù±¾¿ÉÒÔÂú×ãËùÓеÄÒµÎñ³¡¾°£¬ÆäÖÐXAºÍATûÓÐÒµÎñÇÖÈëÐÔ£¬¶øSagaºÍTCC¾ßÓÐÒ»¶¨µÄÒµÎñÇÖÈë¡£ + +# ²Î¿¼ÎÄÕ +https://www.51cto.com/article/713007.html +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" new file mode 100644 index 0000000..e476038 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md" @@ -0,0 +1,594 @@ +## ÕªÒª + +Spring Cloud Alibaba ÖÂÁ¦ÓÚÌṩ΢·þÎñ¿ª·¢µÄһվʽ½â¾ö·½°¸£¬Sentinel ×÷ΪÆäºËÐÄ×é¼þÖ®Ò»£¬¾ßÓÐÈÛ¶ÏÓëÏÞÁ÷µÈһϵÁзþÎñ±£»¤¹¦ÄÜ£¬±¾ÎĽ«¶ÔÆäÓ÷¨½øÐÐÏêϸ½éÉÜ¡£ + +## Sentinel¼ò½é + +Ëæ×Å΢·þÎñµÄÁ÷ÐУ¬·þÎñºÍ·þÎñÖ®¼äµÄÎȶ¨ÐÔ±äµÃÔ½À´Ô½ÖØÒª¡£ Sentinel ÒÔÁ÷Á¿ÎªÇÐÈëµã£¬´ÓÁ÷Á¿¿ØÖÆ¡¢È۶Ͻµ¼¶¡¢ÏµÍ³¸ºÔر£»¤µÈ¶à¸öά¶È±£»¤·þÎñµÄÎȶ¨ÐÔ¡£ + +Sentinel¾ßÓÐÈçÏÂÌØÐÔ: + +* ·á¸»µÄÓ¦Óó¡¾°£º³Ð½ÓÁ˰¢Àï°Í°Í½ü 10 ÄêµÄ˫ʮһ´ó´ÙÁ÷Á¿µÄºËÐij¡¾°£¬ÀýÈçÃëɱ£¬¿ÉÒÔʵʱÈÛ¶ÏÏÂÓβ»¿ÉÓÃÓ¦Óã» +* Í걸µÄʵʱ¼à¿Ø£ºÍ¬Ê±ÌṩʵʱµÄ¼à¿Ø¹¦ÄÜ¡£¿ÉÒÔÔÚ¿ØÖÆÌ¨Öп´µ½½ÓÈëÓ¦Óõĵ¥Ì¨»úÆ÷Ãë¼¶Êý¾Ý£¬ÉõÖÁ 500 ̨ÒÔϹæÄ£µÄ¼¯ÈºµÄ»ã×ÜÔËÐÐÇé¿ö£» +* ¹ã·ºµÄ¿ªÔ´Éú̬£ºÌṩ¿ªÏä¼´ÓõÄÓëÆäËü¿ªÔ´¿ò¼Ü/¿âµÄÕûºÏÄ£¿é£¬ÀýÈçÓë Spring Cloud¡¢Dubbo¡¢gRPC µÄÕûºÏ£» +* ÍêÉÆµÄ SPI À©Õ¹µã£ºÌṩ¼òµ¥Ò×Óá¢ÍêÉÆµÄ SPI À©Õ¹µã¡£Äú¿ÉÒÔͨ¹ýʵÏÖÀ©Õ¹µã£¬¿ìËٵ͍֯Âß¼­¡£ + +## °²×°Sentinel¿ØÖÆÌ¨ + +> Sentinel¿ØÖÆÌ¨ÊÇÒ»¸öÇáÁ¿¼¶µÄ¿ØÖÆÌ¨Ó¦Óã¬Ëü¿ÉÓÃÓÚʵʱ²é¿´µ¥»ú×ÊÔ´¼à¿Ø¼°¼¯Èº×ÊÔ´»ã×Ü£¬²¢ÌṩÁËһϵÁеĹæÔò¹ÜÀí¹¦ÄÜ£¬ÈçÁ÷¿Ø¹æÔò¡¢½µ¼¶¹æÔò¡¢ÈÈµã¹æÔòµÈ¡£ + +* ÎÒÃÇÏÈ´Ó¹ÙÍøÏÂÔØSentinel£¬ÕâÀïÏÂÔØµÄÊÇ`sentinel-dashboard-1.6.3.jar`Îļþ£¬ÏÂÔØµØÖ·£º[github.com/alibaba/Sen¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2FSentinel%2Freleases "https://github.com/alibaba/Sentinel/releases") + +* ÏÂÔØÍê³ÉºóÔÚÃüÁîÐÐÊäÈëÈçÏÂÃüÁîÔËÐÐSentinel¿ØÖÆÌ¨£º + +``` +java -jar sentinel-dashboard-1.6.3.jar +¸´ÖÆ´úÂë +``` + +* Sentinel¿ØÖÆÌ¨Ä¬ÈÏÔËÐÐÔÚ8080¶Ë¿ÚÉÏ£¬µÇ¼Õ˺ÅÃÜÂë¾ùΪ`sentinel`£¬Í¨¹ýÈçϵØÖ·¿ÉÒÔ½øÐзÃÎÊ£º[http://localhost:8080](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080 "http://localhost:8080") + +![image-20230423173003900](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173003900.png) + + + + + + +* Sentinel¿ØÖÆÌ¨¿ÉÒԲ鿴µ¥Ì¨»úÆ÷µÄʵʱ¼à¿ØÊý¾Ý¡£ + +![image-20230423173019956](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173019956.png) + + + + + + +## ´´½¨sentinel-serviceÄ£¿é + +> ÕâÀïÎÒÃÇ´´½¨Ò»¸ösentinel-serviceÄ£¿é£¬ÓÃÓÚÑÝʾSentinelµÄÈÛ¶ÏÓëÏÞÁ÷¹¦ÄÜ¡£ + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£¬ÕâÀïÎÒÃÇʹÓÃNacos×÷Ϊע²áÖÐÐÄ£¬ËùÒÔÐèҪͬʱÌí¼ÓNacosµÄÒÀÀµ£º + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + +¸´ÖÆ´úÂë +``` + +* ÔÚapplication.ymlÖÐÌí¼ÓÏà¹ØÅäÖã¬Ö÷ÒªÊÇÅäÖÃÁËNacosºÍSentinel¿ØÖÆÌ¨µÄµØÖ·£º + +``` +server: + port: 8401 +spring: + application: + name: sentinel-service + cloud: + nacos: + discovery: + server-addr: localhost:8848 #ÅäÖÃNacosµØÖ· + sentinel: + transport: + dashboard: localhost:8080 #ÅäÖÃsentinel dashboardµØÖ· + port: 8719 +service-url: + user-service: http://nacos-user-service +management: + endpoints: + web: + exposure: + include: '*' +¸´ÖÆ´úÂë +``` + +## ÏÞÁ÷¹¦ÄÜ + +> Sentinel Starter ĬÈÏΪËùÓÐµÄ HTTP ·þÎñÌṩÁËÏÞÁ÷Âñµã£¬ÎÒÃÇÒ²¿ÉÒÔͨ¹ýʹÓÃ@SentinelResourceÀ´×Ô¶¨ÒåһЩÏÞÁ÷ÐÐΪ¡£ + +### ´´½¨RateLimitControllerÀà + +> ÓÃÓÚ²âÊÔÈ۶ϺÍÏÞÁ÷¹¦ÄÜ¡£ + +``` +/** + * ÏÞÁ÷¹¦ÄÜ + * Created by macro on 2019/11/7. + */ +@RestController +@RequestMapping("/rateLimit") +public class RateLimitController { + + /** + * °´×ÊÔ´Ãû³ÆÏÞÁ÷£¬ÐèÒªÖ¸¶¨ÏÞÁ÷´¦ÀíÂß¼­ + */ + @GetMapping("/byResource") + @SentinelResource(value = "byResource",blockHandler = "handleException") + public CommonResult byResource() { + return new CommonResult("°´×ÊÔ´Ãû³ÆÏÞÁ÷", 200); + } + + /** + * °´URLÏÞÁ÷£¬ÓÐĬÈϵÄÏÞÁ÷´¦ÀíÂß¼­ + */ + @GetMapping("/byUrl") + @SentinelResource(value = "byUrl",blockHandler = "handleException") + public CommonResult byUrl() { + return new CommonResult("°´urlÏÞÁ÷", 200); + } + + public CommonResult handleException(BlockException exception){ + return new CommonResult(exception.getClass().getCanonicalName(),200); + } + +} +¸´ÖÆ´úÂë +``` + +### ¸ù¾Ý×ÊÔ´Ãû³ÆÏÞÁ÷ + +> ÎÒÃÇ¿ÉÒÔ¸ù¾Ý@SentinelResource×¢½âÖж¨ÒåµÄvalue£¨×ÊÔ´Ãû³Æ£©À´½øÐÐÏÞÁ÷²Ù×÷£¬µ«ÊÇÐèÒªÖ¸¶¨ÏÞÁ÷´¦ÀíÂß¼­¡£ + +* Á÷¿Ø¹æÔò¿ÉÒÔÔÚSentinel¿ØÖÆÌ¨½øÐÐÅäÖã¬ÓÉÓÚÎÒÃÇʹÓÃÁËNacos×¢²áÖÐÐÄ£¬ÎÒÃÇÏÈÆô¶¯NacosºÍsentinel-service£» + +* ÓÉÓÚSentinel²ÉÓõÄÀÁ¼ÓÔØ¹æÔò£¬ÐèÒªÎÒÃÇÏÈ·ÃÎÊϽӿڣ¬Sentinel¿ØÖÆÌ¨ÖвŻáÓжÔÓ¦·þÎñÐÅÏ¢£¬ÎÒÃÇÏÈ·ÃÎÊϸýӿڣº[http://localhost:8401/rateLimit/byResource](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8401%2FrateLimit%2FbyResource "http://localhost:8401/rateLimit/byResource") + +* ÔÚSentinel¿ØÖÆÌ¨ÅäÖÃÁ÷¿Ø¹æÔò£¬¸ù¾Ý@SentinelResource×¢½âµÄvalueÖµ£º + +![image-20230423173034164](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173034164.png) + + + + + + +* ¿ìËÙ·ÃÎÊÉÏÃæµÄ½Ó¿Ú£¬¿ÉÒÔ·¢ÏÖ·µ»ØÁË×Ô¼º¶¨ÒåµÄÏÞÁ÷´¦ÀíÐÅÏ¢£º + +![image-20230423173044930](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173044930.png) + + + + + + +### ¸ù¾ÝURLÏÞÁ÷ + +> ÎÒÃÇ»¹¿ÉÒÔͨ¹ý·ÃÎʵÄURLÀ´ÏÞÁ÷£¬»á·µ»ØÄ¬ÈϵÄÏÞÁ÷´¦ÀíÐÅÏ¢¡£ + +* ÔÚSentinel¿ØÖÆÌ¨ÅäÖÃÁ÷¿Ø¹æÔò£¬Ê¹Ó÷ÃÎʵÄURL£º + +![image-20230423173055243](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173055243.png) + + + + + + +* ¶à´Î·ÃÎʸýӿڣ¬»á·µ»ØÄ¬ÈϵÄÏÞÁ÷´¦Àí½á¹û£º[http://localhost:8401/rateLimit/byUrl](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8401%2FrateLimit%2FbyUrl "http://localhost:8401/rateLimit/byUrl") + +![image-20230423173105426](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173105426.png) + + + + + + +### ×Ô¶¨ÒåÏÞÁ÷´¦ÀíÂß¼­ + +> ÎÒÃÇ¿ÉÒÔ×Ô¶¨ÒåͨÓõÄÏÞÁ÷´¦ÀíÂß¼­£¬È»ºóÔÚ@SentinelResourceÖÐÖ¸¶¨¡£ + +* ´´½¨CustomBlockHandlerÀàÓÃÓÚ×Ô¶¨ÒåÏÞÁ÷´¦ÀíÂß¼­£º + +``` +/** + * Created by macro on 2019/11/7. + */ +public class CustomBlockHandler { + + public CommonResult handleException(BlockException exception){ + return new CommonResult("×Ô¶¨ÒåÏÞÁ÷ÐÅÏ¢",200); + } +} +¸´ÖÆ´úÂë +``` + +* ÔÚRateLimitControllerÖÐʹÓÃ×Ô¶¨ÒåÏÞÁ÷´¦ÀíÂß¼­£º + +``` +/** + * ÏÞÁ÷¹¦ÄÜ + * Created by macro on 2019/11/7. + */ +@RestController +@RequestMapping("/rateLimit") +public class RateLimitController { + + /** + * ×Ô¶¨ÒåͨÓõÄÏÞÁ÷´¦ÀíÂß¼­ + */ + @GetMapping("/customBlockHandler") + @SentinelResource(value = "customBlockHandler", blockHandler = "handleException",blockHandlerClass = CustomBlockHandler.class) + public CommonResult blockHandler() { + return new CommonResult("ÏÞÁ÷³É¹¦", 200); + } + +} +¸´ÖÆ´úÂë +``` + +## È۶Ϲ¦ÄÜ + +> Sentinel Ö§³Ö¶Ô·þÎñ¼äµ÷ÓýøÐб£»¤£¬¶Ô¹ÊÕÏÓ¦ÓýøÐÐÈ۶ϲÙ×÷£¬ÕâÀïÎÒÃÇʹÓÃRestTemplateÀ´µ÷ÓÃnacos-user-service·þÎñËùÌṩµÄ½Ó¿ÚÀ´ÑÝʾϸù¦ÄÜ¡£ + +* Ê×ÏÈÎÒÃÇÐèҪʹÓÃ@SentinelRestTemplateÀ´°ü×°ÏÂRestTemplateʵÀý£º + +``` +/** + * Created by macro on 2019/8/29. + */ +@Configuration +public class RibbonConfig { + + @Bean + @SentinelRestTemplate + public RestTemplate restTemplate(){ + return new RestTemplate(); + } +} +¸´ÖÆ´úÂë +``` + +* Ìí¼ÓCircleBreakerControllerÀ࣬¶¨Òå¶Ônacos-user-serviceÌṩ½Ó¿ÚµÄµ÷Ó㺠+ +``` +/** + * È۶Ϲ¦ÄÜ + * Created by macro on 2019/11/7. + */ +@RestController +@RequestMapping("/breaker") +public class CircleBreakerController { + + private Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class); + @Autowired + private RestTemplate restTemplate; + @Value("${service-url.user-service}") + private String userServiceUrl; + + @RequestMapping("/fallback/{id}") + @SentinelResource(value = "fallback",fallback = "handleFallback") + public CommonResult fallback(@PathVariable Long id) { + return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); + } + + @RequestMapping("/fallbackException/{id}") + @SentinelResource(value = "fallbackException",fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class}) + public CommonResult fallbackException(@PathVariable Long id) { + if (id == 1) { + throw new IndexOutOfBoundsException(); + } else if (id == 2) { + throw new NullPointerException(); + } + return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id); + } + + public CommonResult handleFallback(Long id) { + User defaultUser = new User(-1L, "defaultUser", "123456"); + return new CommonResult<>(defaultUser,"·þÎñ½µ¼¶·µ»Ø",200); + } + + public CommonResult handleFallback2(@PathVariable Long id, Throwable e) { + LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass()); + User defaultUser = new User(-2L, "defaultUser2", "123456"); + return new CommonResult<>(defaultUser,"·þÎñ½µ¼¶·µ»Ø",200); + } +} +¸´ÖÆ´úÂë +``` + +* Æô¶¯nacos-user-serviceºÍsentinel-service·þÎñ£º + +* ÓÉÓÚÎÒÃDz¢Ã»ÓÐÔÚnacos-user-serviceÖж¨ÒåidΪ4µÄÓû§£¬ËùÓзÃÎÊÈçϽӿڻ᷵»Ø·þÎñ½µ¼¶½á¹û£º[http://localhost:8401/breaker/fallback/4](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8401%2Fbreaker%2Ffallback%2F4 "http://localhost:8401/breaker/fallback/4") + +``` +{ + "data": { + "id": -1, + "username": "defaultUser", + "password": "123456" + }, + "message": "·þÎñ½µ¼¶·µ»Ø", + "code": 200 +} +¸´ÖÆ´úÂë +``` + +* ÓÉÓÚÎÒÃÇʹÓÃÁËexceptionsToIgnore²ÎÊýºöÂÔÁËNullPointerException£¬ËùÒÔÎÒÃÇ·ÃÎʽӿڱ¨¿ÕÖ¸Õëʱ²»»á·¢Éú·þÎñ½µ¼¶£º[http://localhost:8401/breaker/fallbackException/2](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8401%2Fbreaker%2FfallbackException%2F2 "http://localhost:8401/breaker/fallbackException/2") + +![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/11/18/16e7eb0fb3df7c01~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.awebp) + + + + + + +## ÓëFeign½áºÏʹÓà + +> SentinelÒ²ÊÊÅäÁËFeign×é¼þ£¬ÎÒÃÇʹÓÃFeignÀ´½øÐзþÎñ¼äµ÷ÓÃʱ£¬Ò²¿ÉÒÔʹÓÃËüÀ´½øÐÐÈÛ¶Ï¡£ + +* Ê×ÏÈÎÒÃÇÐèÒªÔÚpom.xmlÖÐÌí¼ÓFeignÏà¹ØÒÀÀµ£º + +``` + + org.springframework.cloud + spring-cloud-starter-openfeign + +¸´ÖÆ´úÂë +``` + +* ÔÚapplication.ymlÖдò¿ªSentinel¶ÔFeignµÄÖ§³Ö£º + +``` +feign: + sentinel: + enabled: true #´ò¿ªsentinel¶ÔfeignµÄÖ§³Ö +¸´ÖÆ´úÂë +``` + +* ÔÚÓ¦ÓÃÆô¶¯ÀàÉÏÌí¼Ó@EnableFeignClientsÆô¶¯FeignµÄ¹¦ÄÜ£» + +* ´´½¨Ò»¸öUserService½Ó¿Ú£¬ÓÃÓÚ¶¨Òå¶Ônacos-user-service·þÎñµÄµ÷Ó㺠+ +``` +/** + * Created by macro on 2019/9/5. + */ +@FeignClient(value = "nacos-user-service",fallback = UserFallbackService.class) +public interface UserService { + @PostMapping("/user/create") + CommonResult create(@RequestBody User user); + + @GetMapping("/user/{id}") + CommonResult getUser(@PathVariable Long id); + + @GetMapping("/user/getByUsername") + CommonResult getByUsername(@RequestParam String username); + + @PostMapping("/user/update") + CommonResult update(@RequestBody User user); + + @PostMapping("/user/delete/{id}") + CommonResult delete(@PathVariable Long id); +} +¸´ÖÆ´úÂë +``` + +* ´´½¨UserFallbackServiceÀàʵÏÖUserService½Ó¿Ú£¬ÓÃÓÚ´¦Àí·þÎñ½µ¼¶Âß¼­£º + +``` +/** + * Created by macro on 2019/9/5. + */ +@Component +public class UserFallbackService implements UserService { + @Override + public CommonResult create(User user) { + User defaultUser = new User(-1L, "defaultUser", "123456"); + return new CommonResult<>(defaultUser,"·þÎñ½µ¼¶·µ»Ø",200); + } + + @Override + public CommonResult getUser(Long id) { + User defaultUser = new User(-1L, "defaultUser", "123456"); + return new CommonResult<>(defaultUser,"·þÎñ½µ¼¶·µ»Ø",200); + } + + @Override + public CommonResult getByUsername(String username) { + User defaultUser = new User(-1L, "defaultUser", "123456"); + return new CommonResult<>(defaultUser,"·þÎñ½µ¼¶·µ»Ø",200); + } + + @Override + public CommonResult update(User user) { + return new CommonResult("µ÷ÓÃʧ°Ü£¬·þÎñ±»½µ¼¶",500); + } + + @Override + public CommonResult delete(Long id) { + return new CommonResult("µ÷ÓÃʧ°Ü£¬·þÎñ±»½µ¼¶",500); + } +} +¸´ÖÆ´úÂë +``` + +* ÔÚUserFeignControllerÖÐʹÓÃUserServiceͨ¹ýFeignµ÷ÓÃnacos-user-service·þÎñÖеĽӿڣº + +``` +/** + * Created by macro on 2019/8/29. + */ +@RestController +@RequestMapping("/user") +public class UserFeignController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public CommonResult getUser(@PathVariable Long id) { + return userService.getUser(id); + } + + @GetMapping("/getByUsername") + public CommonResult getByUsername(@RequestParam String username) { + return userService.getByUsername(username); + } + + @PostMapping("/create") + public CommonResult create(@RequestBody User user) { + return userService.create(user); + } + + @PostMapping("/update") + public CommonResult update(@RequestBody User user) { + return userService.update(user); + } + + @PostMapping("/delete/{id}") + public CommonResult delete(@PathVariable Long id) { + return userService.delete(id); + } +} +¸´ÖÆ´úÂë +``` + +* µ÷ÓÃÈçϽӿڻᷢÉú·þÎñ½µ¼¶£¬·µ»Ø·þÎñ½µ¼¶´¦ÀíÐÅÏ¢£º[http://localhost:8401/user/4](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8401%2Fuser%2F4 "http://localhost:8401/user/4") + +``` +{ + "data": { + "id": -1, + "username": "defaultUser", + "password": "123456" + }, + "message": "·þÎñ½µ¼¶·µ»Ø", + "code": 200 +} +¸´ÖÆ´úÂë +``` + +## ʹÓÃNacos´æ´¢¹æÔò + +> ĬÈÏÇé¿öÏ£¬µ±ÎÒÃÇÔÚSentinel¿ØÖÆÌ¨ÖÐÅäÖùæÔòʱ£¬¿ØÖÆÌ¨ÍÆË͹æÔò·½Ê½ÊÇͨ¹ýAPI½«¹æÔòÍÆËÍÖÁ¿Í»§¶Ë²¢Ö±½Ó¸üе½ÄÚ´æÖС£Ò»µ©ÎÒÃÇÖØÆôÓ¦Ó㬹æÔò½«Ïûʧ¡£ÏÂÃæÎÒÃǽéÉÜÏÂÈçºÎ½«ÅäÖùæÔò½øÐг־û¯£¬ÒÔ´æ´¢µ½NacosΪÀý¡£ + +### Ô­ÀíʾÒâͼ + +![image-20230423173120010](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173120010.png) + + + + + + +* Ê×ÏÈÎÒÃÇÖ±½ÓÔÚÅäÖÃÖÐÐÄ´´½¨¹æÔò£¬ÅäÖÃÖÐÐĽ«¹æÔòÍÆË͵½¿Í»§¶Ë£» + +* Sentinel¿ØÖÆÌ¨Ò²´ÓÅäÖÃÖÐÐÄÈ¥»ñÈ¡ÅäÖÃÐÅÏ¢¡£ + +### ¹¦ÄÜÑÝʾ + +* ÏÈÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + +``` + + com.alibaba.csp + sentinel-datasource-nacos + +¸´ÖÆ´úÂë +``` + +* ÐÞ¸Äapplication.ymlÅäÖÃÎļþ£¬Ìí¼ÓNacosÊý¾ÝÔ´ÅäÖ㺠+ +``` +spring: + cloud: + sentinel: + datasource: + ds1: + nacos: + server-addr: localhost:8848 + dataId: ${spring.application.name}-sentinel + groupId: DEFAULT_GROUP + data-type: json + rule-type: flow +¸´ÖÆ´úÂë +``` + +* ÔÚNacosÖÐÌí¼ÓÅäÖ㺠+ +![image-20230423173137516](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173137516.png) + + + + + + +* Ìí¼ÓÅäÖÃÐÅÏ¢ÈçÏ£º + +``` +[ + { + "resource": "/rateLimit/byUrl", + "limitApp": "default", + "grade": 1, + "count": 1, + "strategy": 0, + "controlBehavior": 0, + "clusterMode": false + } +] +¸´ÖÆ´úÂë +``` + +* Ïà¹Ø²ÎÊý½âÊÍ£º + + * resource£º×ÊÔ´Ãû³Æ£» + * limitApp£ºÀ´Ô´Ó¦Óã» + * grade£ºãÐÖµÀàÐÍ£¬0±íʾÏß³ÌÊý£¬1±íʾQPS£» + * count£ºµ¥»úãÐÖµ£» + * strategy£ºÁ÷¿ØÄ£Ê½£¬0±íʾֱ½Ó£¬1±íʾ¹ØÁª£¬2±íʾÁ´Â·£» + * controlBehavior£ºÁ÷¿ØÐ§¹û£¬0±íʾ¿ìËÙʧ°Ü£¬1±íʾWarm Up£¬2±íʾÅŶӵȴý£» + * clusterMode£ºÊÇ·ñ¼¯Èº¡£ +* ·¢ÏÖSentinel¿ØÖÆÌ¨ÒѾ­ÓÐÁËÈçÏÂÏÞÁ÷¹æÔò£º + +![image-20230423173152487](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173152487.png) + + + + + + +* ¿ìËÙ·ÃÎʲâÊÔ½Ó¿Ú£¬¿ÉÒÔ·¢ÏÖ·µ»ØÁËÏÞÁ÷´¦ÀíÐÅÏ¢£º + +![image-20230423173203461](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423173203461.png) + + + + + + +## ²Î¿¼×ÊÁÏ + +Spring Cloud Alibaba ¹Ù·½Îĵµ£º[github.com/alibaba/spr¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2Fspring-cloud-alibaba%2Fwiki "https://github.com/alibaba/spring-cloud-alibaba/wiki") + +## ʹÓõ½µÄÄ£¿é + +``` +springcloud-learning +©À©¤©¤ nacos-user-service -- ×¢²áµ½nacosµÄÌṩUser¶ÔÏóCRUD½Ó¿ÚµÄ·þÎñ +©¸©¤©¤ sentinel-service -- sentinel¹¦ÄܲâÊÔ·þÎñ +¸´ÖÆ´úÂë +``` + +## ÏîĿԴÂëµØÖ· + +[github.com/macrozheng/¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fmacrozheng%2Fspringcloud-learning "https://github.com/macrozheng/springcloud-learning") + + + +×÷ÕߣºMacroZheng +Á´½Ó£ºhttps://juejin.cn/post/6844903999876022279 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" new file mode 100644 index 0000000..3bce54d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md" @@ -0,0 +1,413 @@ +# 1\. SkyWalking ¼ò½é + +> Skywalking ÊÇÓɹúÄÚ¿ªÔ´°®ºÃÕßÎâêÉ£¨Ô­ OneAPM ¹¤³Ìʦ£¬Ä¿Ç°ÔÚ»ªÎª£©¿ªÔ´²¢Ìá½»µ½ Apache ·õ»¯Æ÷µÄ²úÆ·£¬ËüͬʱÎüÊÕÁË Zipkin/Pinpoint/CAT µÄÉè¼ÆË¼Â·£¬Ö§³Ö·ÇÇÖÈëʽÂñµã¡£ÊÇÒ»¿î»ùÓÚ·Ö²¼Ê½¸ú×ÙµÄÓ¦ÓóÌÐòÐÔÄÜ¼à¿ØÏµÍ³¡£ÁíÍâÉçÇø»¹·¢Õ¹³öÁËÒ»¸ö½Ð OpenTracing µÄ×éÖ¯£¬Ö¼ÔÚÍÆ½øµ÷ÓÃÁ´¼à¿ØµÄһЩ¹æ·¶ºÍ±ê×¼¹¤×÷¡£ + +* SkyWalking ÊÇÒ»¸ö¿ªÔ´¼à¿ØÆ½Ì¨£¬ÓÃÓÚ´Ó·þÎñºÍÔÆÔ­Éú»ù´¡ÉèÊ©ÊÕ¼¯¡¢·ÖÎö¡¢¾ÛºÏºÍ¿ÉÊÓ»¯Êý¾Ý¡£ +* SkyWalking ÌṩÁËÒ»ÖÖ¼òµ¥µÄ·½·¨À´Î¬»¤·Ö²¼Ê½ÏµÍ³µÄÇåÎúÊÓͼ£¬ÉõÖÁ¿ÉÒÔ¿çÔÆ²é¿´¡£ËüÊÇÒ»ÖÖÏÖ´úAPM£¬×¨ÃÅÎªÔÆÔ­Éú¡¢»ùÓÚÈÝÆ÷µÄ·Ö²¼Ê½ÏµÍ³Éè¼Æ¡£ +* SkyWalking ´ÓÈý¸öά¶È¶ÔÓ¦ÓýøÐмàÊÓ£ºservice£¨·þÎñ£©, service instance£¨ÊµÀý£©, endpoint£¨¶Ëµã£©¡£·þÎñºÍʵÀý¾Í²»¶à˵ÁË£¬¶ËµãÊÇ·þÎñÖеÄij¸ö·¾¶»òÕß˵URI¡£ +* SkyWalking ÔÊÐíÓû§Á˽â·þÎñºÍ¶ËµãÖ®¼äµÄÍØÆË¹ØÏµ£¬²é¿´Ã¿¸ö·þÎñ/·þÎñʵÀý/¶ËµãµÄ¶ÈÁ¿£¬²¢ÉèÖþ¯±¨¹æÔò¡£ + +## SkyWalkingµÄ×é³É + +SkyWalkingÖ÷ÒªµÄ¼¸¸ö×é³ÉÄ£¿é: + +1. Agent Ö÷Òª¸ºÔð´ÓϵͳÖвɼ¯¸÷ÖÖÖ¸±ê£¬Á´Â·Êý¾Ý£¬·¢Ë͸ø oap ·þÎñ¡£ +2. oap ·þÎñ½ÓÊÕ Agent ·¢Ë͹ýÀ´µÄÊý¾Ý£¬´æ´¢£¬Ö´ÐзÖÎö£¬Ìṩ²éѯºÍ±¨¾¯¹¦ÄÜ¡£ +3. Storage ºÍ UI ¸ºÔð´æ´¢Êý¾ÝÒÔ¼°²é¿´Êý¾Ý¡£ + +# 2\. ʹÓà Docker ¿ìËٴ SkyWalking 8.0 + +1. **ÔÚ linux ·þÎñÆ÷ÉÏÑ¡Ôñ²¢½¨Á¢Ä¿Â¼**£» + +``` +mkdir skywalking-docker +¸´ÖÆ´úÂë +``` + +1. **½øÈë skywalking-docker Ŀ¼£¬½¨Á¢Ò»¸öÃûΪ skywalking.yaml µÄ½Å±¾Îļþ£¬ÄÚÈÝÈçÏÂ**£º + +``` +version: '3' +services: + elasticsearch7: + image: docker.elastic.co/elasticsearch/elasticsearch:7.5.0 + container_name: elasticsearch7 + restart: always + ports: + - 9023:9200 + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - TZ=Asia/Shanghai + ulimits: + memlock: + soft: -1 + hard: -1 + networks: + - skywalking + volumes: + - elasticsearch7:/usr/share/elasticsearch/data + oap: + image: apache/skywalking-oap-server:8.0.1-es7 + container_name: oap + depends_on: + - elasticsearch7 + links: + - elasticsearch7 + restart: always + ports: + - 9022:11800 + - 9021:12800 + networks: + - skywalking + volumes: + - ./ext-config:/skywalking/ext-config + ui: + image: apache/skywalking-ui:8.0.1 + container_name: ui + depends_on: + - oap + links: + - oap + restart: always + ports: + - 9020:8080 + environment: + SW_OAP_ADDRESS: oap:12800 + networks: + - skywalking + +networks: + skywalking: + driver: bridge + +volumes: + elasticsearch7: + driver: local +¸´ÖÆ´úÂë +``` + +**×¢Òâ**£ºÈç¹ûÎÒÃÇÏ븲¸Ç oap ¾µÏñÖÐµÄ /skywalking/config Ŀ¼ÏµÄÅäÖÃÎļþ£¬ÎÒÃÇ¿ÉÒÔÔÚ docker ÖйÒÔØÒ»¸ö /skywalking/ext-config Ŀ¼£¬½«ÅäÖÃÎļþ¶ªµ½´ËĿ¼Öм´¿É¡£ + +![image-20230423174422281](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174422281.png) + +1. **Ö´ÐÐ skywalking.yaml ½Å±¾Æô¶¯ÈÝÆ÷**£º + +``` +docker-compose -f skywalking.yaml up +¸´ÖÆ´úÂë +``` + +1. **½øÈë skywalking µÄ¿ØÖÆÌ¨£¬·¢ÏÖ¸÷ÖÖÒDZíÅÌ£¬¿ªÊ¼µ±È»ÊǿյÄ**£º + +``` +http://(°²×°SkyWalking»úÆ÷µÄIP):9020 +¸´ÖÆ´úÂë +``` + +![image-20230423174444272](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174444272.png) + +# 3\. ÔÚ Spring ÏîÄ¿ÖÐÒýÈë SkyWalking ¿Í»§¶Ë + +È«¾ÖÈÕÖ¾×·×Ù traceId µÄʹÓ㺠+ +1. **Ìí¼Ó pom ÎļþÒÀÀµ**£º + +``` + + org.apache.skywalking + apm-toolkit-logback-1.x + 8.0.1 + + + org.apache.skywalking + apm-toolkit-trace + 8.0.1 + +¸´ÖÆ´úÂë +``` + +1. **ÔÚ resources Ŀ¼Ï Ìí¼Ó logback-spring.xml Îļþ£¬ÄÚÈÝÈçÏÂ**: + +``` + + + + + + + + + + + + + + + + info + + + ${CONSOLE_LOG_PATTERN} + UTF-8 + + + + + + + + + + + + + + + + + + + + + ${logger.path}/log_info.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + ${logger.path}/info/log-info-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + info + ACCEPT + DENY + + + + + + + + + + ${logger.path}/log_error.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${logger.path}/error/log-error-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + ERROR + ACCEPT + DENY + + + + + + + + + + + + + + +¸´ÖÆ´úÂë +``` + +**×¢Òâ**£ºÆäËû¶¼ÊÇÈÕÖ¾³£¹æÅäÖã¬Ö÷ÒªÊÇÕⲿ·Ö `` µÄÅäÖᣠ+ +1. **½øÈë skywalking ¹ÙÍøÏÂÔØ SkyWalking APM£¬Ö÷ÒªÊÇÒªÓõ½ agent**£º + +skywalking ¹ÙÍøÏÂÔØµØÖ·£º[skywalking.apache.org/downloads/](https://link.juejin.cn?target=http%3A%2F%2Fskywalking.apache.org%2Fdownloads%2F "http://skywalking.apache.org/downloads/") + +![image-20230423174506711](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174506711.png) + +1. **½âѹÏÂÔØµÄ apache-skywalking-apm-es7-8.0.1.tar.gz °ü£¬Ä¿Â¼½á¹¹Èçͼ**£º + +![image-20230423174522153](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174522153.png) + +ÎÒÃÇÖ»ÒªÆäÖÐµÄ agent Ŀ¼¾ÍÐУ¬agent ÀïµÄ¶«Î÷´ó¸ÅÓÐÕâЩ£º + +![image-20230423174533023](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174533023.png) + +°Ñ agent Ŀ¼¸´ÖƵ½Ò»¸öÍ×ÉÆµÄĿ¼Ï£¬Ò»»á¶ùÐèÒªÅäÖà JVM Æô¶¯²ÎÊýĿ¼£¬µ±È»×÷ÕßÖ±½Ó·Åµ½ÁËÏîÄ¿À + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8deaee160419423e9a71e710d3b2c3dd~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp) + +1. **ÔÚ idea ³ÌÐòÆô¶¯ÃüÁîÔö¼ÓÈçÏ JVM Æô¶¯²ÎÊý**£º + +``` +-javaagent:(agentÎļþ¼ÐËùÔÚµÄĿ¼)\agent\skywalking-agent.jar -Dskywalking.agent.service_name=(·þÎñÃû)-service -Dskywalking.agent.instance_name=(·þÎñÃû)-instance -Dskywalking.collector.backend_service=(°²×°SkyWalking»úÆ÷µÄIP):9022 +¸´ÖÆ´úÂë +``` + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96e9d5a3aa5c44c3b0b948929609ae1f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp) + +ÒòΪ skywalking ÊÇ·ÇÇÖÈëʽÂñµãʵÏÖ·Ö²¼Ê½Á´Â·¸ú×ÙºÍÐÔÄÜ¼à¿Ø£¬ËùÒÔÒ»°ã²ÉÓà javaagent µÄ·½Ê½¡£ + +> **Javaagent ÊÇʲô**£¨JVM Æô¶¯Ç°¾²Ì¬ Instrument£©£¿ +> +> Javaagent ÊÇ java ÃüÁîµÄÒ»¸ö²ÎÊý¡£²ÎÊý javaagent ¿ÉÒÔÓÃÓÚÖ¸¶¨Ò»¸ö jar °ü£¬²¢ÇҶԸà java °üÓÐÁ½¸öÒªÇó£º +> +> 1. Õâ¸ö jar °üµÄ MANIFEST.MF Îļþ±ØÐëÖ¸¶¨ Premain-Class Ïî¡£ +> 2. Premain-Class Ö¸¶¨µÄÄǸöÀà±ØÐëʵÏÖ premain() ·½·¨¡£ +> +> premain() ·½·¨£¬´Ó×ÖÃæÉÏÀí½â£¬¾ÍÊÇÔËÐÐÔÚ main() º¯Êý֮ǰµÄµÄÀà¡£µ± Java ÐéÄâ»úÆô¶¯Ê±£¬ÔÚÖ´ÐÐ main() º¯Êý֮ǰ£¬jvm »áÏÈÔËÐÐ -javaagent ËùÖ¸¶¨ jar °üÄÚ Premain-Class Õâ¸öÀàµÄ premain() ·½·¨ ¡£ + +1. **ÎÒÃÇǰ¼¸ÆªÎÄÕÂÖдÁËÍêÕûµÄÏîÄ¿£¬ÔÚÕâЩÏîÄ¿ÖÐÈ«²¿°´ÕÕÉϱߵÄÅäÖÃÒ»±é£¬Æô¶¯¿´ÏÂЧ¹û**£º + +* Íø¹Ø·þÎñ£ºherring-gateway£¬zuul Í³Ò»Íø¹ØÎ¢·þÎñ¡£ +* ÈÏÖ¤·þÎñ£ºherring-oauth2£¬oauth2 ÈÏÖ¤ÖÐÐÄ΢·þÎñ¡£ +* »áÔ±·þÎñ£ºherring-member-service£¬Î¢·þÎñÖ®Ò»£¬½ÓÊÕµ½ÇëÇóºó»áµ½ÈÏÖ¤ÖÐÐÄÑéÖ¤¡£ +* ¶©µ¥·þÎñ£ºherring-orders-service£¬Î¢·þÎñÖ®¶þ£¬½ÓÊÕµ½ÇëÇóºó»áµ½ÈÏÖ¤ÖÐÐÄÑéÖ¤¡£ +* ÉÌÆ··þÎñ£ºherring-product-service£¬Î¢·þÎñÖ®Èý£¬½ÓÊÕµ½ÇëÇóºó»áµ½ÈÏÖ¤ÖÐÐÄÑéÖ¤¡£ + +![image-20230423174600279](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174600279.png) + +1. **ÎÒÃÇÀ´²âÊÔһϣ¬ÏÈÇëÇóÏ token£¬È»ºóÔÙÇëÇó /api/member/update**£º + +``` +#### + +POST http://localhost:8080/oauth2-service/oauth/token?grant_type=password&username=admin&password=123456&client_id=app-client&client_secret=client-secret-8888&scope=all +Accept: */* +Cache-Control: no-cache +¸´ÖÆ´úÂë +``` + +µÃµ½·µ»Ø½á¹û token£º + +``` +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZ2F0ZXdheS1zZXJ2aWNlIl0sInVzZXJfbmFtZSI6ImFkbWluIiwiand0LWV4dCI6IkpXVCDmianlsZXkv6Hmga8iLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNjEzOTcwMDk2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjU4MDY5ODlhLWUyNDQtNGQyMy04YTU5LTBjODRiYzE0Yjk5OSIsImNsaWVudF9pZCI6ImFwcC1jbGllbnQifQ.EP4acam0tkJQ9kSGRGk_mQsfi1y4M_hhiBL0H931v60", + "token_type": "bearer", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZ2F0ZXdheS1zZXJ2aWNlIl0sInVzZXJfbmFtZSI6ImFkbWluIiwiand0LWV4dCI6IkpXVCDmianlsZXkv6Hmga8iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiNTgwNjk4OWEtZTI0NC00ZDIzLThhNTktMGM4NGJjMTRiOTk5IiwiZXhwIjoxNjE0MDM0ODk2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjQxZGM1ZDc1LTZmZDgtNDU3My04YmRjLWI4ZTMwNWEzMThmMyIsImNsaWVudF9pZCI6ImFwcC1jbGllbnQifQ.CGmGx_msqJBHxa95bBROY2SAO14RyeRklVPYrRxZ7pQ", + "expires_in": 7199, + "scope": "all", + "jwt-ext": "JWT À©Õ¹ÐÅÏ¢", + "jti": "5806989a-e244-4d23-8a59-0c84bc14b999" +} +¸´ÖÆ´úÂë +``` + +ÇëÇóÖ´ÐÐ /api/member/update + +``` +#### + +GET http://localhost:8080/member-service/api/member/update +Accept: */* +Cache-Control: no-cache +Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZ2F0ZXdheS1zZXJ2aWNlIl0sInVzZXJfbmFtZSI6ImFkbWluIiwiand0LWV4dCI6IkpXVCDmianlsZXkv6Hmga8iLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNjEzOTcwMDk2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjU4MDY5ODlhLWUyNDQtNGQyMy04YTU5LTBjODRiYzE0Yjk5OSIsImNsaWVudF9pZCI6ImFwcC1jbGllbnQifQ.EP4acam0tkJQ9kSGRGk_mQsfi1y4M_hhiBL0H931v60 +¸´ÖÆ´úÂë +``` + +**ÒDZíÅ̽á¹ûչʾ**: + +![image-20230423174639471](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174639471.png) + +![image-20230423174721822](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174721822.png) + +![image-20230423174742703](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174742703.png) + +**ÍØÆËͼ½á¹ûչʾ**£º + +![image-20230423174809108](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174809108.png) + +![image-20230423174831290](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174831290.png) + +**Á´Â·×·×Ù½á¹ûչʾ**£º + +![image-20230423174845526](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423174845526.png) + + + +×÷Õߣº°×²Ë˵¼¼Êõ +Á´½Ó£ºhttps://juejin.cn/post/6931922457741770760 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" new file mode 100644 index 0000000..6fa981f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba/SpringCloudAlibaba\346\246\202\350\247\210.md" @@ -0,0 +1,202 @@ +Spring Cloud AlibabaÖÂÁ¦ÓÚÌṩ΢·þÎñ¿ª·¢µÄһվʽ½â¾ö·½°¸£¬ËüÊÇSpring Cloud×é¼þ±»Ö²ÈëAlibabaÔªËØÖ®ºóµÄ²úÎï¡£ÀûÓÃSpring Cloud Alibaba£¬¿ÉÒÔ¿ìËٴ΢·þÎñ¼Ü¹¹²¢Íê³É¼¼ÊõÉý¼¶¡£ÖÐСÆóÒµÈç¹ûÐèÒª¿ìËÙÂäµØÒµÎñÖÐ̨ºÍ¼¼ÊõÖÐ̨£¬²¢ÏòÊý×Ö»¯ÒµÎñתÐÍ£¬ÄÇSpring Cloud Alibaba¾ø¶ÔÊÇÒ»¸ö¡°ÉñÆ÷¡±¡£ + +# ʲôÊÇSpring Cloud Alibaba + +ºÃ°ÉÎÒÃÇÏÈ¿´¿´Spring Cloud AlibabaµÄ¹ÙÍø£¬ÈçÏÂͼËùʾ¡£ + +![image-20230423165959115](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423165959115.png) + +´ÓͼÖоͿÉÒÔ¿´³ö£¬Spring Cloud AlibabaÊÇSpring CloudµÄ×ÓÏîÄ¿£¬ºÃ°É£¬ÕâÁ½ÏîĿһ¿ªÊ¼µÄ¶¨Î»¾ÍÊǸ¸×Ó¹ØÏµ¡£ + +¿´À´¹Ù·½»¹ÊÇû´òËãµß¸²Spring CloudµÄ¼Ü¹¹Ë¼Ï룬ֻÊÇÏë×öÒ»´ÎÄÜÁ¦µÄÔöÇ¿ºÍÀ©Õ¹¡£ + + +# ΪʲôҪÓÐSpring Cloud Alibaba + + +ÎÒÃÇÏÈ¿´¿´Spring Cloud AlibabaÓÐÄÄЩÄÜÁ¦£¿ + + + +Spring Cloud Alibaba µÄ×é¼þ¹¦ÄܼÈÓÐÃâ·Ñ°æ±¾£¬Ò²ÓÐÊշѰ汾¡£ + +Sentinel£ºÒÔ¡°Á÷¡±ÎªÇÐÈëµã£¬ÔÚÁ÷Á¿¿ØÖÆ¡¢²¢·¢ÐÔ¡¢ÈÝ´í½µ¼¶ºÍ¸ºÔر£»¤µÈ·½ÃæÌṩ½â¾ö·½°¸£¬ÒÔ±£»¤·þÎñµÄÎȶ¨ÐÔ¡£ + + + +Nacos£ºÒ»¸ö¾ß±¸¶¯Ì¬·þÎñ·¢Ïֺͷֲ¼Ê½ÅäÖõȹ¦ÄܵĹÜÀíÆ½Ì¨£¬Ö÷ÒªÓÃÓÚ¹¹½¨ÔÆÔ­ÉúÓ¦ÓóÌÐò¡£ + + + +RocketMQ£ºÒ»¸ö¸ßÐÔÄÜ¡¢¸ß¿ÉÓᢸßÍÌÍÂÁ¿µÄ½ðÈÚ¼¶ÏûÏ¢Öмä¼þ¡£Spring Cloud Alibaba ½«RocketMQ ¶¨ÖÆ»¯·â×°£¬¿ª·¢ÈËÔ±¿É¡°¿ªÏä¼´Óᱡ£ + + + +Dubbo£ºÒ»¸ö»ùÓÚJavaµÄ¸ßÐÔÄÜ¿ªÔ´RPC¿ò¼Ü¡£ + + + +Seata£ºÒ»¸ö¸ßÐÔÄÜÇÒÒ×ÓÚʹÓõķֲ¼Ê½ÊÂÎñ½â¾ö·½°¸£¬¿ÉÓÃÓÚ΢·þÎñ¼Ü¹¹¡£ + +°¢ÀïÔÆOSS£¨°¢ÀïÔÆ¶ÔÏó´æ´¢·þÎñ£©£ºÒ»ÖÖ¼ÓÃܵİ²È«ÔÆ´æ´¢·þÎñ£¬¿ÉÒÔ´æ´¢¡¢´¦ÀíºÍ·ÃÎÊÀ´×ÔÊÀ½çÈκεط½µÄ´óÁ¿Êý¾Ý¡£ + + + +°¢ÀïÔÆSchedulerX£ºÒ»¿î·Ö²¼Ê½ÈÎÎñµ÷¶È²úÆ·£¬Ö§³Ö¶¨ÆÚÈÎÎñºÍÔÚÖ¸¶¨Ê±¼äµã´¥·¢ÈÎÎñ¡£ + + + +°¢ÀïÔÆSMS£ºÒ»ÖÖ¸²¸ÇÈ«ÇòµÄÏûÏ¢·þÎñ£¬Ìṩ±ã½Ý¡¢¸ßЧºÍÖÇÄܵÄͨÐŹ¦ÄÜ£¬¿É°ïÖúÆóÒµ¿ìËÙÁªÏµÆä¿Í»§¡£ + + + +ÎÒÃÇÔÙÀ´¿´¿´Spring Cloud AlibabaµÄ¸÷¸ö°æ±¾µÄ¶Ô±È£¬ÈçÏÂͼËùʾ¡£ + +![image-20230423170034102](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423170034102.png) + +ÎÒÃÇÔÙÀ´¿´¿´£¬Ã»ÓÐSpring Cloud Alibaba֮ǰ£¬ÎÒÃÇÈçºÎʹÓÃSpring Cloud½øÐÐ΢·þÎñ¿ª·¢¡£ + + + +Spring CloudÖ§³Ö¶àÖÖ×¢²áÖÐÐÄ£¬±ÈÈçEureka¡¢ZooKeeper¡¢ConsulµÈ¡£ + + + +Èç¹ûÈí¼þ¿ª·¢ÈËÔ±ÐèÒª²ÉÓÃEureka×÷Ϊע²áÖÐÐÄ£¬ÔòÐèÒª´î½¨Ò»¸öEureka Server¼¯Èº£¬ÓÃÓÚ¹ÜÀí×¢²áÖÐÐÄ·þÎñµÄÔªÊý¾Ý£¬È»ºóÓ¦Ó÷þÎñÐèÒª½ÓÈëEureka£¬¾ÍÐèҪʹÓöÔÓ¦µÄ×¢½â½«·þÎñÌṩÕßÒÔ¼°·þÎñ¶©ÔÄÕß×¢²áµ½Eureka×¢²áÖÐÐÄ¡£Í¬ÀíZooKeeperºÍConsulÒ²ÊDzÉÓÃͬÑùµÄ·½Ê½£¬Ê¹ÓöÔӦע²áÖÐÐĵÄ×¢½âÍê³É·þÎñµÄ×¢²áºÍ¶©ÔÄ¡£ + + + +Spring CloudΪÁË·½±ãÈí¼þ¿ª·¢ÈËÔ±¿ìËٵĽÓÈ벻ͬµÄ×¢²áÖÐÐÄ£¬Í³Ò»Ê¹ÓÃ×¢½â@EnableDiscoveryClient+¶ÔӦע²áÖÐÐĵÄStarter×é¼þ¡£µ±È»Eureka»¹ÊÇÑØÓÃÀϵÄʹÓ÷½Ê½@EnableEurekaClient+¶ÔӦע²áÖÐÐĵÄStarter×é¼þ£¬Ö÷ÒªÊÇÓÉÓÚSpring CloudÒѾ­Í£Ö¹Á˶ÔEurekaµÄά»¤¡£ + + + +ºÃ°ÉÎÊÌâÀ´ÁË£¬Spring CloudÒѾ­½«ZooKeeperºÍConsulµÄʹÓ÷½Ê½Í³Ò»ÆðÀ´£¬Èí¼þ¿ª·¢ÈËÔ±·Ç³£Óä¿ìµÄ½«Ó¦ÓýÓÈëSpring Cloud£¬µ«ÊÇĿǰÊÐÃæÉÏÓÖ³öÁËÒ»¸öеÄ×¢²áÖÐÐÄ£¬±ÈÈçNacos£¬ËüµÄÐÔÄܷdz£¸ß£¬²¢ÇÒÖ§³ÖCPºÍAPģʽ£¬µ«ÊÇSpring Cloud²»Ö§³Ö¡£ + + + +ÎÒÃÇÔÙÀ´¿´¿´ÔÚûÓÐSpring Cloud Alibaba֮ǰ£¬ÎÒÃÇÈçºÎʹÓÃNacos¡£ + + + +ºÃ°É£¬NacosÊÇÒ»¿î¼ÈÖ§³Ö·Ö²¼Ê½×¢²áÖÐÐĺͷֲ¼Ê½ÅäÖÃÖÐÐĵÄÉñÆ÷¡£Nacos¹Ù·½ÌṩÁ˺ܶà½ÓÈëģʽ£¬±ÈÈçSpring Framework¡¢Spring BootµÈ£¬µ«ÊÇÆäµ×²ã±¾ÖÊÉÏÊÇÒÀÀµNacosÌṩµÄSDK,±ÈÈçNacos Client¡£ + + + +Èç¹û²ÉÓÃSpring Framework+Nacos Client£¨±ÈÈçnacos-spring-context£©£¬ÔòÐèÒª¿ª·¢ÈËÔ±×Ô¼ºÎ¬»¤NacosNamingServceºÍNacosConfigServiceʵÀý¶ÔÏó£¬Ò²¾ÍÊÇ˵¿ª·¢ÈËÔ±ÐèÒª×Ô¼ºÒÀÀµNacos Client×ö¶þ´Î¿ª·¢£¬³É±¾·Ç³£´ó¡£ + + + +Èç¹û²ÉÓÃSpring Boot+Nacos Starter×é¼þ£¨±ÈÈçnacos-discovery-spring-boot-starter£©£¬Ôò¿ª·¢ÈËÔ±¿ÉÒÔ¸ßЧµÄ½ÓÈëNacosÅäÖÃÖÐÐÄ£¬²¢ÇÒ¿ÉÒÔʹÓÃSpring BootÌṩµÄ¸÷ÖÖStarter×é¼þ¡£ + + + +Èç¹û´¿Ëé²ÉÓÃSpring Cloud×÷Ϊ»ù´¡¿ò¼Ü£¬Ôò²»ÄÜʹÓÃNacos×÷Ϊע²áÖÐÐÄ£¬ºÃ¾À½á°¡¡£ + + + +ÄÇôÓÐûÓÐÒ»¸ö¿ò¼Ü¼È¿ÉÒÔʹÓÃSpring Cloud£¬ÓÖ¿ÉÒÔʹÓÃSpring Boot£¬»¹ÄܼæÈݸ÷ÖÖ×¢²áÖÐÐÄÄØ£¬ºÜ¸ßÐ˵ĸæËß´ó¼Ò£¬Spring Cloud Alibaba¾ÍÊÇÕâ¸öÉñÆ÷£¬ÍêÃÀµÄ½â¾öÁË¿ª·¢ÈËԱ΢·þÎñ¼Ü¹¹¿ò¼ÜÑ¡Ð͵ÄÎÊÌ⣬ÓÃËü¾ÍÊÇÁË¡£ + +# Spring Cloud AlibabaµÄºËÐļܹ¹Ë¼Ïë + + +Spring Cloud AlibabaÕûÌå¼Ü¹¹£¬ÈçÏÂͼ¡£ + +![image-20230423170108696](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230423170108696.png) + +Spring Cloud Alibaba¼Ü¹¹µÄľĿ±êÊǽ«¿ªÔ´²úÆ·ºÍÔÆ³§ÉÌÈںϣ¬×öµ½ÒµÎñÔÆÉϺÍÔÆÏµÄÎÞ·ì¼æÈÝ£¬ÒµÎñ¿ª·¢ÈËÔ±Ö»ÐèÒª¹ØÐÄÒµÎñÏîÄ¿¿ª·¢£¬µ×²ã¼¼Êõϸ½Ú¾Í½»¸øSpring Cloud Alibaba¡£ + + + +ÈçºÎ¿ìËÙÉÏÊÖ²¢Ê¹Óà + + + + +ºÃ°É£¬ËµÁËÄÇô¶àÎÒÃÇÈí¼þ¿ª·¢ÈËÔ±Ôõô¿ìËÙÉÏÊÖSpring Cloud AlibabaÄØ£¿ + +Æäʵ×î¼òµ¥µÄ·½Ê½ÊÇÓÃÎÊÌâÇý¶¯£¬È»ºó×Ô¼ºÇ××Ô¶¯ÊÖʹÓÃSpring Cloud AlibabaµÄ¹¦ÄÜ£¬ÔÙÈ¥·ÖÎöÔ´ÂëÕâÑù²ÅÄܸü¼ÓÉî¿ÌµÄÊìϤԭÀí¡£ + + + +Ò²Ðí»áÓÐһЩÈË»áÈÏΪ£¬ÎÒ¿´ÁËÔ´Âë¾ÍËãÊÇÎÒÏÖÔÚÊìϤÁË£¬¹ýÁËÒ»¸öÔÂÖ®ºó¾ÍÍü¼ÇÁË£¬ÓÐʲôÓÃÄØ¡£µ«ÊÇÄãÕâÑùÏ룬¾ÍËãÊDz»¿´Ô´Â룬Äã×öÆäËûµÄÊÂÇ飬һ¸öÔÂÖ®ºóÒ²»áÍü¼ÇµÄ¡£Ö»ÓÐͨ¹ýÀíÂÛºÍʵս½áºÏ£¬Äã²Å»á³¤ÆÚµÄ±£³Öϰ¹ßÈ¥Á·Ï°£¬×öµ½Î¹ʶøÖªÐ¡£ + + + +ÔÛÃÇÏÖÔÚÈí¼þ¿ª·¢ÈËÔ±£¬ÓÈÆäÊÇJava¿ª·¢ÈËÔ±£¬Ó¦¸Ã¶¼ÓÃIDEAÀ´×öÏîÄ¿¿ª·¢£¬ÎÒ¾õµÃ¾Í¿ÉÒÔÀûÓÃËüÀ´¿ìËÙµÄÉú³ÉÒ»¸öSpring Cloud AlibabaÏîÄ¿£¬»òÕßÀûÓÃSpring Framework¹Ù·½µÄ½ÅÊÖ¼ÜÏîÄ¿Spring InitializrµÈ¡£ + + + +»¹ÓоÍÊÇÒª¶à×ܽáһЩ×î¼Ñʵ¼ù£¬±ÈÈçÒª´ÓSpring Cloud AlibabaµÄÅäÖÃÎļþ¿ªÊ¼£¬·´ß£Ò»Ð©ÅäÖõÄÒµÎñ³¡¾°£¬ÕâÑù²ÅÄܸü¼ÓÉî¿ÌµÄÁ˽âSpring Cloud AlibabaµÄÉè¼ÆË¼Ïë¡£ + + +Copyright ? ¿¡ÒÝ Link: [https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html](https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html) + +Õâ¿éÏñ¹Ù·½ÎĵµÒ»Ñù£¬¼òµ¥µÄ½éÉÜSpring Cloud Alibaba°üº¬ÁËÄÇЩÖ÷Òª²¿·Ö£¬¼òµ¥Óиö¸ÅÄîºÍÓ¡Ï󣬺óÃæµ±È»»áÏêϸµÄ½éÉÜ£¬ÔÙÈ¥ÉîÈëÀí½â¡£ + +- **·þÎñÏÞÁ÷ºÍ½µ¼¶**£ºÖ§³Ö`WebServlet`¡¢`WebFlux`¡¢`OpenFeign`¡¢`RestTemplate`¡¢`Dubbo`·ÃÎÊÏÞÁ÷ºÍ½µ¼¶£¬¿ÉÒÔͨ¹ý`console`ʵʱÐÞ¸ÄÏÞÁ÷½µ¼¶²ßÂÔ£¬Ö§³Ö¼à¿ØÏÞÁ÷½µ¼¶Ö¸±ê +- **·þÎñ×¢²áºÍ·¢ÏÖ**£º¿ÉÒÔ×¢²á·þÎñ£¬clients¿ÉÒÔͨ¹ýSpring¹ÜÀíµÄbean·¢ÏÖʵÀý£¬²¢ÈÚºÏÁËRibbon +- **·Ö²¼Ê½ÅäÖÃ**£ºÖ§³Ö·Ö²¼Ê½ÏµÍ³µÄÅäÖÃÀ©Õ¹£¬ÅäÖøıäʱ×Ô¶¯Ë¢Ð +- **Rpc ·þÎñ**£ºÀ©Õ¹Spring CloudµÄRestTemplateºÍOpenFeignÖ§³Öµ÷ÓÃDubbo RPC·þÎñ +- **ʼþÇý¶¯**£ºÖ§³Ö¹¹½¨Í¨¹ý¹²ÏíÏûϢϵͳÁ¬½ÓµÄ¸ß¶È¿ÉÉìËõµÄʼþÇý¶¯µÄ΢·þÎñ +- **·Ö²¼Ê½ÊÂÎñ**£ºÖ§³Ö¸ßÐÔÄÜ¡¢ÈÝÒ×ʹÓõķֲ¼Ê½ÊÂÎñ½â¾ö·½°¸ +- °¢ÀïÔÆ¶ÔÏó´æ´¢¡¢°¢ÀïÔÆÈÎÎñµ÷¶È¡¢°¢ÀïÔÆ¶ÌÐÅ·þÎñ + +ÉÏÃæ»ù±¾ÉÏÊǰѹٷ½Îĵµ·­Òë¹ýÀ´µÄ£¬¿ÉÒÔ·¢ÏÖSpring Cloud Alibaba·Ç³£Ç¿´ó£¬»¹²»Ð͝µÄÍùÏ¿´~ + + + +# ÕûÌå´ó¸Ù + +## ![springCloud-Alibaba-1](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/springCloud-Alibaba-1.png)[#](#°æ±¾¹ØÏµ) °æ±¾¹ØÏµ + +[¹Ù·½°æ±¾ËµÃ÷(opens new window)](https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E) + +### 2021.x ·ÖÖ§ + +| Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version | +| ---------------------------- | --------------------- | ------------------- | +| 2021.0.1.0* | Spring Cloud 2021.0.1 | 2.6.3 | +| 2021.1 | Spring Cloud 2020.0.1 | 2.4.2 | + +### 2.2.x ·ÖÖ§ + +| Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version | +| --------------------------------- | --------------------------- | ------------------- | +| 2.2.8.RELEASE* | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE | +| 2.2.7.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE | +| 2.2.6.RELEASE | Spring Cloud Hoxton.SR9 | 2.3.2.RELEASE | +| 2.1.4.RELEASE | Spring Cloud Greenwich.SR6 | 2.1.13.RELEASE | +| 2.2.1.RELEASE | Spring Cloud Hoxton.SR3 | 2.2.5.RELEASE | +| 2.2.0.RELEASE | Spring Cloud Hoxton.RELEASE | 2.2.X.RELEASE | +| 2.1.2.RELEASE | Spring Cloud Greenwich | 2.1.X.RELEASE | +| 2.0.4.RELEASE(ֹͣά»¤£¬½¨ÒéÉý¼¶) | Spring Cloud Finchley | 2.0.X.RELEASE | +| 1.5.1.RELEASE(ֹͣά»¤£¬½¨ÒéÉý¼¶) | Spring Cloud Edgware | 1.5.X.RELEASE | + +### ×é¼þ°æ±¾¹ØÏµ + +| Spring Cloud Alibaba Version | Sentinel Version | Nacos Version | RocketMQ Version | Dubbo Version | Seata Version | +| --------------------------------------------------------- | ---------------- | ------------- | ---------------- | ------------- | ------------- | +| 2.2.8.RELEASE | 1.8.4 | 2.1.0 | 4.9.3 | ~ | 1.5.1 | +| 2021.0.1.0 | 1.8.3 | 1.4.2 | 4.9.2 | ~ | 1.4.2 | +| 2.2.7.RELEASE | 1.8.1 | 2.0.3 | 4.6.1 | 2.7.13 | 1.3.0 | +| 2.2.6.RELEASE | 1.8.1 | 1.4.2 | 4.4.0 | 2.7.8 | 1.3.0 | +| 2021.1 or 2.2.5.RELEASE or 2.1.4.RELEASE or 2.0.4.RELEASE | 1.8.0 | 1.4.1 | 4.4.0 | 2.7.8 | 1.3.0 | +| 2.2.3.RELEASE or 2.1.3.RELEASE or 2.0.3.RELEASE | 1.8.0 | 1.3.3 | 4.4.0 | 2.7.8 | 1.3.0 | +| 2.2.1.RELEASE or 2.1.2.RELEASE or 2.0.2.RELEASE | 1.7.1 | 1.2.1 | 4.4.0 | 2.7.6 | 1.2.0 | +| 2.2.0.RELEASE | 1.7.1 | 1.1.4 | 4.4.0 | 2.7.4.1 | 1.0.0 | +| 2.1.1.RELEASE or 2.0.1.RELEASE or 1.5.1.RELEASE | 1.7.0 | 1.1.4 | 4.4.0 | 2.7.3 | 0.9.0 | +| 2.1.0.RELEASE or 2.0.0.RELEASE or 1.5.0.RELEASE | 1.6.3 | 1.1.1 | 4.4.0 | 2.7.3 | 0.7.1 | + +# ×ܽá + + + +±¾ÎľÍÊÇ´ÓÈ«¾ÖÁÄÁËһЩSpring Cloud AlibabaµÄһЩ¼Ü¹¹Ë¼Ï룬µ«ÊÇûÓдÓϸ½ÚÁÄ£¬¾ßÌåϸ½Ú¿ÉÒԲο¼¡°Spring Cloud AlibabaϵÁС±¡£ + + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" new file mode 100644 index 0000000..af691d6 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\345\217\221\347\216\260.md" @@ -0,0 +1,469 @@ +# Ò»¡¢Nacos·þÎñ·¢ÏÖÁ÷³Ìͼ + +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b5b4ef0330dc4882b0fc2f73994face7.png "ÔÚÕâÀï²åÈëͼƬÃèÊö") + +½¨Òé´ó¼Ò×Ô¼ºÊáÀíÒ»ÏÂÁ÷³Ì£¬Ò²¿ÉÒԲο¼£º[Nacos·þÎñ×¢²áÔ´Âë·ÖÎöÁ÷³Ìͼ](https://blog.csdn.net/Saintmm/article/details/121981184) + +# ¶þ¡¢ÕÒÔ´ÂëÈë¿Ú + +spring-cloud-commons°üÖж¨ÒåÁËÒ»Ì×·þÎñ·¢ÏֵĹ淶£¬ºËÐÄÂß¼­ÔÚ`DiscoveryClient`½Ó¿ÚÖУ» +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/75ebfd400023456faaafd95e7d9cdbf7.png "ÔÚÕâÀï²åÈëͼƬÃèÊö") +¼¯³ÉSpring CloudʵÏÖ·þÎñ·¢ÏÖµÄ×é¼þ¶¼»áʵÏÖ`DiscoveryClient`½Ó¿Ú£»nacos-discovery°üϵÄ`NacosDiscoveryClient`ÀàʵÏÖ`DiscoveryClient`½Ó¿Ú¡£ +![ÔÚÕâÀï²åÈëͼƬÃèÊö](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0c330a7ce5c744c4a2b25dc024b2430f.png "ÔÚÕâÀï²åÈëͼƬÃèÊö") + +# Èý¡¢¿Í»§¶Ë·þÎñ·¢ÏÖ + +> 1¡¢µ±nacos¿Í»§¶ËÔË?ÆðÀ´Ö®ºó£¬ËüÖ»ÊÇÈ¥×ö·þÎñ×¢²á¡¢ÅäÖûñÈ¡µÈ²Ù×÷£»²¢²»»áÁ¢¼´È¥ÇëÇó·þÎñÐÅÏ¢£» +> 2¡¢µ±µÚÒ»´ÎÇëÇóʱºò£¬²Å»áÈ¥»ñÈ¡·þÎñ£¬¼´`ÀÁ¼ÓÔØ»úÖÆ`£» + +#### 1£©ÏÈ´Ó±¾µØ»º´æserviceInfoMapÖлñÈ¡·þÎñʵÀýÐÅÏ¢£¬»ñÈ¡²»µ½Ôòͨ¹ý`NamingProxy`µ÷ÓÃNacos ·þÎñ¶Ë»ñÈ¡·þÎñʵÀýÐÅÏ¢£»×îºó¿ªÆô¶¨Ê±ÈÎÎñÿÃëÇëÇó·þÎñ¶Ë »ñȡʵÀýÐÅÏ¢ÁÐ±í½ø¶ø¸üб¾µØ»º´æserviceInfoMap£» + +``` +// NacosDiscoveryClient#getInstances() +public List getInstances(String serviceId) { + try { + // ͨ¹ýNacosNamingService»ñÈ¡·þÎñ¶ÔÓ¦µÄʵÀýÐÅÏ¢£»µã½øÈ¥ + List instances = discoveryProperties.namingServiceInstance() + .selectInstances(serviceId, true); + return hostToServiceInstanceList(instances, serviceId); + } catch (Exception e) { + throw new RuntimeException( + "Can not get hosts from nacos server. serviceId: " + serviceId, e); + } +} + +// NacosNamingService#selectInstances() +public List selectInstances(String serviceName, boolean healthy) throws NacosException { + return selectInstances(serviceName, new ArrayList(), healthy); +} +public List selectInstances(String serviceName, List clusters, boolean healthy) + throws NacosException { + // ĬÈÏ×ß¶©ÔÄģʽ + return selectInstances(serviceName, clusters, healthy, true); +} +public List selectInstances(String serviceName, List clusters, boolean healthy, + boolean subscribe) throws NacosException { + // ĬÈϲéѯDEFAULT_GROUPϵķþÎñʵÀýÐÅÏ¢ + return selectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe); +} +public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException { + + ServiceInfo serviceInfo; + // ĬÈÏ×ß¶©ÔÄģʽ£¬¼´subscribeΪTRUE + if (subscribe) { + serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); + } else { + serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); + } + return selectInstances(serviceInfo, healthy); +} +``` + +`HostReactor#getServiceInfo()`·½·¨ÊÇÕæÕý»ñÈ¡·þÎñʵÀýÐÅÏ¢µÄµØ·½£º + +``` +public ServiceInfo getServiceInfo(final String serviceName, final String clusters) { + + NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); + String key = ServiceInfo.getKey(serviceName, clusters); + if (failoverReactor.isFailoverSwitch()) { + return failoverReactor.getService(key); + } + + // 1¡¢´Ó±¾µØ»º´æserviceInfoMapÖлñȡʵÀýÐÅÏ¢ + ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters); + + // 2¡¢Èç¹û±¾µØ»º´æÖÐûÓУ¬Ôò×ßHTTPµ÷ÓôÓNacos·þÎñ¶Ë»ñÈ¡ + if (null == serviceObj) { + serviceObj = new ServiceInfo(serviceName, clusters); + + serviceInfoMap.put(serviceObj.getKey(), serviceObj); + + updatingMap.put(serviceName, new Object()); + updateServiceNow(serviceName, clusters); + updatingMap.remove(serviceName); + + } else if (updatingMap.containsKey(serviceName)) { + + if (UPDATE_HOLD_INTERVAL > 0) { + // hold a moment waiting for update finish + synchronized (serviceObj) { + try { + serviceObj.wait(UPDATE_HOLD_INTERVAL); + } catch (InterruptedException e) { + NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e); + } + } + } + } + + // 3¡¢¿ªÆôÒ»¸ö¶¨Ê±ÈÎÎñ£¬Ã¿¸ôÒ»Ãë´ÓNacos·þÎñ¶Ë»ñÈ¡×îеķþÎñʵÀýÐÅÏ¢£¬¸üе½±¾µØ»º´æseriveInfoMapÖÐ + scheduleUpdateIfAbsent(serviceName, clusters); + + // 4¡¢ ´Ó±¾µØ»º´æserviceInfoMapÖлñÈ¡·þÎñʵÀýÐÅÏ¢ + return serviceInfoMap.get(serviceObj.getKey()); +} +``` + +1¡¢´Ó±¾µØ»º´æÖлñÈ¡·þÎñʵÀýÐÅÏ¢£º + +``` +private ServiceInfo getServiceInfo0(String serviceName, String clusters) { + + String key = ServiceInfo.getKey(serviceName, clusters); + + return serviceInfoMap.get(key); +} +``` + +2¡¢Ôò×ßHTTPµ÷ÓôÓNacos·þÎñ¶Ë»ñÈ¡·þÎñʵÀýÐÅÏ¢£º + +``` +public void updateServiceNow(String serviceName, String clusters) { + ServiceInfo oldService = getServiceInfo0(serviceName, clusters); + try { + + // ͨ¹ýNamingProxy×ßHTTP½Ó¿Úµ÷Ó㬻ñÈ¡·þÎñʵÀýÐÅÏ¢ + String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false); + if (StringUtils.isNotEmpty(result)) { + // ¸üб¾µØ»º´æserviceInfoMap + processServiceJSON(result); + } + } catch (Exception e) { + NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e); + } finally { + if (oldService != null) { + synchronized (oldService) { + oldService.notifyAll(); + } + } + } +} +``` + +3¡¢¿ªÆôÒ»¸ö¶¨Ê±ÈÎÎñ£¬Ã¿¸ôÒ»Ãë´ÓNacos·þÎñ¶Ë»ñÈ¡×îеķþÎñʵÀýÐÅÏ¢£¬¸üе½±¾µØ»º´æseriveInfoMapÖУº + +``` +public void scheduleUpdateIfAbsent(String serviceName, String clusters) { + if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) { + return; + } + + synchronized (futureMap) { + if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) { + return; + } + + // Æô¶¯¶¨Ê±ÈÎÎñ + ScheduledFuture future = addTask(new UpdateTask(serviceName, clusters)); + futureMap.put(ServiceInfo.getKey(serviceName, clusters), future); + } +} + +// ¶¨Ê±ÈÎÎñÖ´ÐÐÂß¼­£¬UpdateTask#run() +public void run() { + try { + ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters)); + + if (serviceObj == null) { + updateServiceNow(serviceName, clusters); + executor.schedule(this, DEFAULT_DELAY, TimeUnit.MILLISECONDS); + return; + } + + if (serviceObj.getLastRefTime() <= lastRefTime) { + updateServiceNow(serviceName, clusters); + serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters)); + } else { + // if serviceName already updated by push, we should not override it + // since the push data may be different from pull through force push + refreshOnly(serviceName, clusters); + } + + // ¿ªÆôÒ»¸ö¶¨Ê±ÈÎÎñ£¬1sÖ®ºóÖ´ÐÐ + executor.schedule(this, serviceObj.getCacheMillis(), TimeUnit.MILLISECONDS); + + lastRefTime = serviceObj.getLastRefTime(); + } catch (Throwable e) { + NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e); + } + +} +``` + +²éѯ·þÎñʵÀýÁÐ±í£º + +``` +public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly) + throws NacosException { + + final Map params = new HashMap(8); + params.put(CommonParams.NAMESPACE_ID, namespaceId); + params.put(CommonParams.SERVICE_NAME, serviceName); + params.put("clusters", clusters); + params.put("udpPort", String.valueOf(udpPort)); + params.put("clientIP", NetUtils.localIP()); + params.put("healthyOnly", String.valueOf(healthyOnly)); + + return reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/list", params, HttpMethod.GET); +} +``` + +#### 2£©ÔÚHostReactorʵÀý»¯µÄʱºò»áʵÀý»¯PushReceiver£¬½ø¶ø¿ªÆôÒ»¸öÏß³ÌËÀÑ­»·Í¨¹ý`DatagramSocket#receive()`¼àÌýNacos·þÎñ¶ËÖзþÎñʵÀýÐÅÏ¢·¢Éú±ä¸üºóµÄUDP֪ͨ¡£ + +``` +public class PushReceiver implements Runnable { + private DatagramSocket udpSocket; + + public PushReceiver(HostReactor hostReactor) { + try { + this.hostReactor = hostReactor; + udpSocket = new DatagramSocket(); + // Æô¶¯Ò»¸öÏß³Ì + executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("com.alibaba.nacos.naming.push.receiver"); + return thread; + } + }); + + executorService.execute(this); + } catch (Exception e) { + NAMING_LOGGER.error("[NA] init udp socket failed", e); + } + } + + public void run() { + while (true) { + try { + // byte[] is initialized with 0 full filled by default + byte[] buffer = new byte[UDP_MSS]; + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + + // ¼àÌýNacos·þÎñ¶Ë·þÎñʵÀýÐÅÏ¢±ä¸üºóµÄ֪ͨ + udpSocket.receive(packet); + + String json = new String(IoUtils.tryDecompress(packet.getData()), "UTF-8").trim(); + NAMING_LOGGER.info("received push data: " + json + " from " + packet.getAddress().toString()); + + PushPacket pushPacket = JSON.parseObject(json, PushPacket.class); + String ack; + if ("dom".equals(pushPacket.type) || "service".equals(pushPacket.type)) { + hostReactor.processServiceJSON(pushPacket.data); + + // send ack to server + ack = "{\"type\": \"push-ack\"" + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; + } else if ("dump".equals(pushPacket.type)) { + // dump data to server + ack = "{\"type\": \"dump-ack\"" + + ", \"lastRefTime\": \"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"" + + StringUtils.escapeJavaScript(JSON.toJSONString(hostReactor.getServiceInfoMap())) + + "\"}"; + } else { + // do nothing send ack only + ack = "{\"type\": \"unknown-ack\"" + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; + } + + udpSocket.send(new DatagramPacket(ack.getBytes(Charset.forName("UTF-8")), + ack.getBytes(Charset.forName("UTF-8")).length, packet.getSocketAddress())); + } catch (Exception e) { + NAMING_LOGGER.error("[NA] error while receiving push data", e); + } + } + } + +} +``` + +# ËÄ¡¢·þÎñ¶Ë·þÎñ·¢ÏÖ + +Nacos·þÎñ¶ËµÄ·þÎñ·¢ÏÖÖ÷Òª×öÁ½¼þÊ£º + +> 1¡¢²éѯ·þÎñʵÀýÁÐ±í£»ÏÈ´Ó»º´æserviceMapÖÐÕÒµ½service¶ÔÓ¦µÄCluster£¬ÔÙ´ÓClusterµÄÁ½¸öSet£º`persistentInstances`¡¢`ephemeralInstances`»ñȡȫÁ¿µÄʵÀýÐÅÏ¢£» +> 2¡¢½«¿Í»§¶Ë´«À´µÄip¡¢udp¶Ë¿ÚºÅ¼ÓÌí¼Óµ½`clientMap`£¬½ø¶ø×ö·þÎñÍÆËÍ£»clientMapÊôÓÚ`NamingSubscriberService`µÄʵÏÖÀà`NamingSubscriberServiceV1Impl`£¬ÆäkeyÊÇservice name£¬valueÊǶ©ÔÄÁ˸÷þÎñµÄ¿Í»§¶ËÁбí(ip+¶Ë¿ÚºÅ)¡£ + +¼ûnamingÏîÄ¿Ï嵀 InstanceControllerÀàµÄlist()·½·¨£º + +#### 1£©»ñÈ¡·þÎñʵÀýÁбí + +``` +@GetMapping("/list") +@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) +public Object list(HttpServletRequest request) throws Exception { + + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); + String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); + NamingUtils.checkServiceNameFormat(serviceName); + + String agent = WebUtils.getUserAgent(request); + String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY); + String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY); + int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0")); + boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false")); + + boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false")); + + String app = WebUtils.optional(request, "app", StringUtils.EMPTY); + String env = WebUtils.optional(request, "env", StringUtils.EMPTY); + String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY); + + Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, + udpPort, clusters); + // ½øÈ¥InstanceOperatorServiceImpl#listInstance()·½·¨»ñÈ¡·þÎñʵÀýÁбí + return getInstanceOperator().listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); +} + +//InstanceOperatorServiceImpl#listInstance() +public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, + boolean healthOnly) throws Exception { + ClientInfo clientInfo = new ClientInfo(subscriber.getAgent()); + String clientIP = subscriber.getIp(); + ServiceInfo result = new ServiceInfo(serviceName, cluster); + Service service = serviceManager.getService(namespaceId, serviceName); + long cacheMillis = switchDomain.getDefaultCacheMillis(); + + // now try to enable the push + try { + // ³¢ÊÔÆôÓÃÍÆËÍ·þÎñUdpPushService£¬¼´·þÎñʵÀýÐÅÏ¢·¢Éú±ä¸üʱͨ¹ýUDPµÄ·½Ê½Í¨ÖªNacos Client + if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) { + subscriberServiceV1.addClient(namespaceId, serviceName, cluster, subscriber.getAgent(), + new InetSocketAddress(clientIP, subscriber.getPort()), pushDataSource, StringUtils.EMPTY, + StringUtils.EMPTY); + cacheMillis = switchDomain.getPushCacheMillis(serviceName); + } + } catch (Exception e) { + Loggers.SRV_LOG.error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, + subscriber.getPort(), e); + cacheMillis = switchDomain.getDefaultCacheMillis(); + } + + if (service == null) { + if (Loggers.SRV_LOG.isDebugEnabled()) { + Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); + } + result.setCacheMillis(cacheMillis); + return result; + } + + // ¼ì²é·þÎñÊÇ·ñ½ûÓà + checkIfDisabled(service); + + // ÕâÀïÊÇ»ñÈ¡·þÎñ×¢²áÐÅÏ¢µÄ¹Ø¼ü´úÂ룬»ñÈ¡ËùÓÐÓÀ¾ÃºÍÁÙʱ·þÎñʵÀý + List srvedIps = service + .srvIPs(Arrays.asList(StringUtils.split(cluster, StringUtils.COMMA))); + + // filter ips using selector£¬Ñ¡ÔñÆ÷¹ýÂË·þÎñ + if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) { + srvedIps = selectorManager.select(service.getSelector(), clientIP, srvedIps); + } + + // Èç¹ûÕÒ²»µ½·þÎñÔò·µ»Øµ±Ç°·þÎñ + if (CollectionUtils.isEmpty(srvedIps)) { + ....... + return result; + } + +// Service#srvIPs() +public List srvIPs(List clusters) { + if (CollectionUtils.isEmpty(clusters)) { + clusters = new ArrayList<>(); + clusters.addAll(clusterMap.keySet()); + } + return allIPs(clusters); +} + +// Service#allIPs() +public List allIPs(List clusters) { + List result = new ArrayList<>(); + for (String cluster : clusters) { + // ·þÎñ×¢²áµÄʱºò£¬»á½«ÊµÀýÐÅϢдµ½clusterMapÖУ¬ÏÖÔÚ´ÓÆäÖÐÈ¡ + Cluster clusterObj = clusterMap.get(cluster); + if (clusterObj == null) { + continue; + } + + result.addAll(clusterObj.allIPs()); + } + return result; +} + +// Cluster#allIPs() +public List allIPs() { + List allInstances = new ArrayList<>(); + // »ñÈ¡·þÎñÏÂËùÓеij־û¯ÊµÀý + allInstances.addAll(persistentInstances); + // »ñÈ¡·þÎñÏÂËùÓеÄÁÙʱʵÀý + allInstances.addAll(ephemeralInstances); + return allInstances; +} +``` + +#### 2£©²ÉÓÃUDP·½Ê½×ö·þÎñʵÀýÍÆËÍ + +NamingSubscriberServiceV1Impl#addClient()£º + +``` +public void addClient(String namespaceId, String serviceName, String clusters, String agent, + InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) { + + // ³õʼ»¯ÍÆËͿͻ§¶ËʵÀýPushClient + PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant, + app); + // Ìí¼ÓÍÆËÍÄ¿±ê¿Í»§¶Ë + addClient(client); +} + +// ÖØÔØ·½·¨addClient() +public void addClient(PushClient client) { + // client is stored by key 'serviceName' because notify event is driven by serviceName change + // ¿Í»§¶ËÓɼü¡° serviceName¡±´æ´¢£¬ÒòΪ֪ͨʼþÓÉserviceName¸ü¸ÄÇý¶¯ + String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName()); + ConcurrentMap clients = clientMap.get(serviceKey); + // Èç¹û»ñÈ¡²»µ½¿Í»§¶ËÏëµ÷ÓõÄServiceName¶ÔÓ¦µÄÍÆËͿͻ§¶Ë£¬Ôòн¨ÍÆËͿͻ§¶Ë£¬²¢»º´æ + if (clients == null) { + clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024)); + clients = clientMap.get(serviceKey); + } + + PushClient oldClient = clients.get(client.toString()); + // ´æÔÚÀϵÄPushClient£¬ÔòˢР+ if (oldClient != null) { + oldClient.refresh(); + } else { + // ·ñÔò»º´æPushClient + PushClient res = clients.putIfAbsent(client.toString(), client); + if (res != null) { + Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res); + } + Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName()); + } +} +``` + +# Îå¡¢×ܽá + +¿Í»§¶Ë£º + +> 1¡¢ÓÅÏÈ´Ó±¾µØ»º´æÖлñÈ¡·þÎñʵÀýÐÅÏ¢£» +> 2¡¢Î¬»¤¶¨Ê±ÈÎÎñ¶¨Ê±´ÓNacos·þÎñ¶Ë»ñÈ¡·þÎñʵÀýÐÅÏ¢£» + +·þÎñ¶Ë£º + +> 1¡¢·µ»ØÖ¸¶¨ÃüÃû¿Õ¼äÏÂÄÚ´æ×¢²á±íÖÐËùÓеÄÓÀ¾ÃʵÀýºÍÁÙʱʵÀý¸ø¿Í»§¶Ë£» +> 2¡¢¿ªÆôÒ»¸öUDP·þÎñʵÀýÐÅÏ¢±ä¸üÍÆËÍ·þÎñ£» + + +# ²Î¿¼ÎÄÕ +https://developer.aliyun.com/article/1058262 +https://ost.51cto.com/posts/14835 +https://developer.aliyun.com/article/1048465 +https://zhuanlan.zhihu.com/p/70478036 +https://juejin.cn/post/6999814668390760484#heading-8 \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" new file mode 100644 index 0000000..1a59865 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -0,0 +1,357 @@ +# Ò»¡¢ Nacos·þÎñ×¢²áÔ´Âë½âÎö + + + +* * * + + + +## 1.1 Ô´Â뷽ʽ´ò°ü + + + +* * * + + + +¿Í»§¶ËÔ´ÂëÖÐÔö¼Ó´ò°ü·½Ê½£¬½«Ô´Âë´òÈë°üÖÐ + + + + + + + +``` org.apache.maven.plugins maven-source-plugin 3.2.1 true compile jar ``` + + + + + + + +È»ºó´ò°ü£º + + + + + + + +```mvn install -DskipTests ``` + + + + + + + +## 1.2 Èë¿Ú + + + +* * * + + + +[github.com/alibaba/nac¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2Fnacos%2Ftree%2F1.4.1) + +Ê×ÏÈÎÒÃÇ»á°ÑÔ´ÂëÏÂÔØÏÂÀ´£¬ÎÒÃÇ»áͨ¹ýÔ´ÂëµÄ·½Ê½½øÐÐÆô¶¯£¬ Äã¿ÉÒÔͨ¹ýdebugµÄ·½Ê½½øÐÐÔËÐÐÀ´ÅжÏËûµÄÔËÐйý³Ì¡£ + +ÎÒÃÇ´ÓÔ´ÂëµÄ½Ç¶ÈÀ´·ÖÎöһϣº·þÎñÆô¶¯ºóËû»áÉϱ¨µ½×¢²áÖÐÐÄ£º + +NacosNamingService ¾ÍÊÇ·þÎñ×¢²áºÍ·¢ÏÖÏà¹ØµÄÀ࣬Ëû¾ÍÊÇÔÚÕâÀォµ±Ç°Æô¶¯µÄ·þÎñµ÷ÓÃ×¢²áʵÀýµÄ·½·¨£¬ÎÒÃÇ¿´Ò»ÏÂÕâ¸ö·½·¨¸ÉʲôÁË£¿ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fc004433c7304147905c088dd3227005.png "image.png") + +![image-20211221124947544](E:\BaiduNetdiskWorkspace\springcloud alibaba\img\image-20211221124947544.png) + +Ëû¾ÍÊÇÆ´½ÓÁËһЩ²ÎÊý·¢ËÍhttpÇëÇ󣬵½´ï·þÎñ×¢²áÖÐÐĽøÐз¢ÏÖ£¬ÇëÇóµÄ¾ÍÊÇÕâ¸ö·¾¶£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51f5b45b70ef4a7dbe1258e05314fa61.png "image.png") + +ºÃÕâ¾ÍÊǶÔÓ¦µÄ·¾¶£¬ÎÒÃǻص½¹Ù·½ÎĵµµÄÖ¸Äϵ±ÖÐ + +[nacos.io/zh-cn/docs/¡­](https://link.juejin.cn?target=https%3A%2F%2Fnacos.io%2Fzh-cn%2Fdocs%2Fopen-api.html) + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d3b30ba715ab4363b3ee8b4e21a2f3a2.png "image.png") + +ºÃ£¬°´ÕÕÎÒÃǽ²µ½ÕâÀï¾Í²»ÓÃÔÙÍùÀïÃæ¿´ÁË£¬ÎÒÃÇ¿ÉÒÔµã½øÈ¥¿´Ò»Ï£¬ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/9f4bbf69cc1746e79545831252796e55.png "image.png") + +ËûÕæÕýµÄµ÷ÓøøÄãÊÇÔÚÕâÀï¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b06c94937d374ddb9fc2c224e5932205.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d0d55d3616ad4109837d9aee36e5a946.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/450565c4c14f4f3bb2097ab1ea69aae7.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/527d2af9bb0c4206b090d3410238a576.png "image.png") + +ÔÚÕâÀï½øÐе÷Óà + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3f3f39de5aaa4ad2b3ac7dccc33aa574.png "image.png") + +ºÃÄÄÓÐͬѧÎÊÄãÔõô֪µÀ¶Ïµã¾Í´ïµ½ÕâÀÄÇÎÒÃÇ¿´Ò»ÏÂÔõÑù²é¿´Ô´ÂëµÄÆô¶¯µÄ·¾¶£¬ÎÒÃÇ¿´Ò»ÏÂÎÒÃǶ©µ¥Î¢·þÎñµÄ·¾¶£¬ÎÒÃÇÒª¼¯³ÉnacosµÄ·þÎñ·¢ÏÖ¹¦ÄÜ£¬ÎÒÃÇÒªÒýÈëÎÒÃǵÄdiscoveryµÄ°ü£¬ËûÊÇÒ»¸östarter£¬Ç°ÃæÎÒÃÇѧ¹ýspringbootÎÒÃÇÖªµÀÈκÎstarterÀïÃæÒ»¶¨Óиöspring.factories£¬×÷Ϊһ¸öÈë¿Ú + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/be47c62f16f94507a1af84d0169ebf53.png "image.png") + +ÕâÀïÃæ¶¯Ì¬¼ÓÔØµÄÀàºÜ¶à£¬NacosServiceRegistryAutoConfiguration ´ÓÕâÃû×ÖÎÒÃÇÄÜ·¢ÏÖËûÊÇÒ»¸önacos·þÎñ×¢²áµÄ×Ô¶¯ÅäÖÃÀ࣬ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/84017587439846a4b21a696f12775b6c.png "image.png") + +ÕâÀïÃæÊµÏÖÁËÈý¸öÀ࣬ÎÒÃÇ¿´Ò»ÏÂÕâ¸öÀàNacosAUtoServiceRegistration + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fe7ee87e88944b689835432c833972c2.png "image.png") + +×Ô¶¯×¢²áÀ࣬ÎÒÃÇ¿ÉÒÔ¿´Ò»ÏÂËûµÄ¼¯³É¹ØÏµ£¬ÊÇÒ»¸öApplicationListener springÆô¶¯Íê³Éºó¶¼»á·¢ËÍÒ»¸öÏûÏ¢£¬applicaitonListener¾ÍÊÇͨ¹ý¼àÌýÕâ¸öÏûϢȻºó½øÐÐÖ´Ðеġ£ËùÒÔÎÒÃÇÖªµÀÏÂÒ»²½ÎÒÃÇÓ¦¸ÃÔõô¿´£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/2a12ba62dae24aa092f97b0cff5dfaaa.png "image.png") + +ËùÒԲ鿴ËûµÄ³éÏóÀà¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1f9253dbbbd349ffb6d0e60d0761b3ec.png "image.png") + +²é¿´onApplicationEvent·½·¨£ºÕâÑùÔÚ·þÎñ·¢ËÍÍê¾Í»á·¢ËÍÕâÑùÒ»¸öÏûÏ¢£¬ÊÕµ½Õâ¸öÏûÏ¢¾Í»áµ÷ÓÃÕâ¸öbind·½·¨ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/41a7323a2e2c49d19aaf5768e66af7d8.png "image.png") + +ÕâÀïÓиöif return ÎÒÃǾÍÖ±½ÓÌø¹ý£¬ÕâÒ»¶¨ÊÇ·ÖÖ§´úÂ룬ÏñÕâÑùµÄ·ÖÖ§´úÂëÎÒÃǾͲ»Òª¿´£¬µÚÒ» ´ÎÒª¿´Ö÷Ïߣ¬ËùÒÔÎÒÃÇÖ±½Ó¿´ÕâÀïµÄstart·½·¨£¬Èç¹ûºóÃæÕâÀïûÓжÔÓ¦µÄ´úÂëÂß¼­ÎÒÃÇ¿ÉÒÔ½øÈëÕâ¸ö·ÖÖ§À´¿´¡£ ºÃ£¬ÏñÕâÑùstart, begin£¬init£¬register·½·¨¶¼ÊǺÜÖØÒªµÄ·½·¨£¬ÎÒÃÇÒ»¶¨Òª½øÈ¥¿´ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3923ffd589e9488eb1143811a50d28c0.png "image.png") + +µÚÒ»¸öif¾Í²»Óÿ´£¬µÚ¶þ¸öifÐèÒª¿´£¬ÒòΪºóÃæ¾ÍûÓÐÂß¼­ÁËÄã¿´Õâ¸öregister()£¬Ó¦¸Ã¾ÍÊÇÕâ¸ö·½·¨£¬ÒòΪÄã¾ÍÊDz鿴ע²áµÄÁ÷³Ì¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d5b527e0900a4f1b9abcd76db4bfc138.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d3501fdf6479417180e881c38638bdac.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7c0ea798fcd7404d8f350047e947add1.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0ca3ab1a98f248a0aa3854730eba85fb.png "image.png") + +ÕâÀïÐèÒªÄãÖªµÀSpringBoot×Ô¶¯×°ÅäµÄ»ù±¾ÖªÊ¶£¬Æä´ÎÒªÖªµÀSpringÆô¶¯·¢ÏÖµÄ »ù±¾ÖªÊ¶¡£ + +## 1.3 ·þÎñ×¢²á + + + +* * * + + + +Nacos¸ß²¢·¢Ö§³ÅÒì²½ÈÎÎñÓëÄÚ´æ¶ÓÁÐÆÊÎö + +¸Õ²ÅÊÇÔÚ·þÎñÌṩÕßÉÏÃæ½²µÄÄÚÈÝ£¬ÏÖÔÚÎÒÃÇ·þÎñ×¢²áÖÐÐÄÀ´¿´Ò»Ï + +ÇëÇóµÄÊÇinstanceʵÀý£ºÕâ¾ÍÊÇÒ»¸öspringmvcµÄcontroller£¬ËùÓÐÎÒÃÇ¿ÉÒÔÈ«ÎÄËÑË÷ Controller£¬ÎÒÃÇÊÇinstanceʵÀý°É£¬ËùÒÔÎÒÃÇÕâÀïInstanceController + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4e02f16787a84704add5aa41f9d7662a.png "image.png") + +ÄÇ֮ǰÎÒÃÇÓõÄÊÇpostÇëÇóËùÒÔÎÒÃDz鿴post·½·¨£¬ÕâÀïÓÐdelete,update...,ËûÕâÀïÊÇʲô·ç¸ñ£¿ restFul + +ÎÒÃÇ·¢ÏÖÀïÃæÃ»ÓжÔÓ¦µÄDefaultGroup£¬ÔÚ·þÎñ×¢²áºÍ·¢ÏÖµÄÇé¿öÏÂÕâ¸ögroupÊDz»¾­³£Óõġ£ÄãÓõϰֻÊÇ×Ô¼ºµÄ¹æ·¶ºÍ·½±ã¹ÜÀíµÄ¡£ÔÚ·þÎñ×¢²áºÍ·¢ÏÖÖÐÔ´ÂëÖж¼Ã»ÓÐÓᣣ¬ÃüÃû¿Õ¼ä£¬·þÎñÃû£¬È»ºó½«ÎÒÃDzÎÊýת»¯ÎªÊµÀý¡£Õâ¾ÍÎÒÃÇ·þÎñÄ£ÐÍÖеÄÈý²ãÄ£ÐÍ¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/09f059ba31a7444a88a24b03399d502d.png "image.png") + +ÄÇÎÒÃÇ¿´Ò»ÏÂËûµÄ×¢²áʵÀýÀïÃæ×öÁËʲô£¿ ÕâÀïÃæÎÒÃÇ×¢ÒâÎÒÃÇÊÇ×¢²áinstance£¬ÎÒÃǾÍÎ§ÈÆ×ÅËû£¬½øÐзÖÎö£¬ÊDz»ÊǾÍÀ´µ½addInstanceÁË + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4b69913abe6f40288a08dee89ecb0772.png "image.png") + +createEmptyService + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b8a3063b3c924ee195af64d5d8efa647.png "image.png") + +1¡¢»ñÈ¡service ³õ´Î»ñȡһ¶¨Îª¿Õ£¬ÎÒÃÇ¿ÉÒÔ½øÈ¥·ÖÎöһϠ+ +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fdf453ebadee486c9def86560bc3b8de.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7025fb4edcc247b3aca0f1f851d26203.png "image.png") + +ÕâÀï¾ÍÊÇ×¢²á±í£¬ÎÒÃÇÇ°ÃæËµ¹ýnacos·þÎñÁìÓòÄ£ÐÍ¡¾¿ÉÒԲο¼Í¼¡¿£¬Õâ¸ömap¾ÍÊǶÔÓ¦µÄ×¢²á±í + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4f1db03d3fcc4f93aaa17cc485fb59c2.png "image.png") + +ÕâÀïÃæÉèÖ÷þÎñºÍ³õʼ»¯ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1af3a9015b2c4e1ab06da283e4dc5332.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/cb795dd33b114f84bc51cdbdca5af202.png "image.png") + +·þÎñ³õʼ»¯£ºÐÄÌø + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0d0583abf0184ed49bf5fd93c9effafe.png "image.png") + +¿´ÕâÀïÊǸöscheduleNamingHealthÊÇÒ»¸ö¶¨Ê±ÈÎÎñ£¬ÎÒÃÇÖ»ÐèÒª¿´Ò»ÏÂtaskÈÎÎñ¾Í¿ÉÒÔ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51ba8120a9954389b9e2f44d49532a14.png "image.png") + +taskÈÎÎñÎÒÃÇÐèÒª¿´Ò»ÏÂrun·½·¨£º + +ÔÚÕâÀïÎÒÃÇ¿´ÊÇ»ñÈ¡ËùÓеÄʵÀý¡¾¿ÉÒÔµã½øÈ¥¿´Ò»Ï¡¿ + +µ±Ç°Ê±¼ä - ÉÏ´ÎÐÄÌøÊ±¼ä ¼ä¸ô³¬¹ý15Ãë Ôò½«ÊµÀýÉèÖÃΪ·Ç½¡¿µ£¬ µ±³¬¹ý30ÃëûÓÐÊÕµ½ÐÄÌø¾ÍÖ±½ÓÌÞ³ý + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f9131eea860e410685d8c4cd5beea69e.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/586fd2fcb489414590295ce2dfafd91d.png "image.png") + +ºÃ£¬ÎÒÃÇ»ØÀ´£¬ÕâÀïÃû×ÖÆðµÄÌØ±ðºÃ£¬createEmtyService£¬ÊÇ´´½¨Ò»¸ö¿Õ·þÎñ£¬ºóÃæÎÒÃǵÄʵÀý£¬ÊDz»ÊÇ»¹»á×¢²áµ½ÀïÃæ£¬ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/9518406856b642298d3ee2f16e36bed0.png "image.png") + +> ÎÒÃÇ¿ÉÒÔ¿´Ò»Ï·þÎñÄ£ÐÍ£¬ºÍÎÒÃÇÒÔǰ˵¹ýµÄÒ»Ñù +> +> ÃüÃû¿Õ¼ä £¬cluster ¼¯Èº¸ÅÄî +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0323827c8249417b8ab3fd6c4bd3e61e.png "image.png") +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/044d8f58dc5b4c7e83dab05c62397566.png "image.png") +> +> ¼¯ÈºÖжÔÓ¦µÄʵÀý¡£ +> +> ![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/da6adcbf25f44992b08f919988f81b62.png "image.png") + +ÎÒÃÇ¿´Ò»ÏÂaddinstance + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/cb9d4cb8fd4d4f0aa75e5d95c4923aae.png "image.png") + +¹¹½¨¶ÔÓ¦µÄkey: + + + + + + + +```String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral); ``` + + + + + + + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/6ab491f925e3403fa1377c2649d4721d.png "image.png") + + + + + + + +```//»ñȡע²áʵÀýµÄIP¶Ë¿ÚÁбí List instanceList = addIpAddresses(service, ephemeral, ips); ``` + + + + + + + +ÎÒÃǽøÈë¼òµ¥µÄ¿´Ò»Ï£¬ÎÒÃÇ·¢ÏÖÕâ¸öadd,remove£¬ÕâÀï¾ÍÊÇÐÂÔöºÍÒÆ³ýʵÀý + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/bebd689b54d04e0dab0e64ac552e965e.png "image.png") + +ÎÒÃÇÖ÷Òª³öÀïÕâдע²á½øÀ´µÄips£¬ÄÇÎÒÃǾ͵ã»÷ips£¬¸ßÁÁÀ´ÏÔʾ¿´¿´£¬È»ºóËû»á½øÐÐÑ­»·instance£¬ÕâÀïÎÒÃÇ¿ÉÒÔ¿´µ½Èç¹ûËûÊÇÒÆ³ý¾Í´ÓmapÀïÃæÒÆ³ý³öÈ¥£¬Èç¹ûÊÇÐÂÔö¾ÍÔÚinstanceMapÖÐÐÂÔöһϣ¬×îºó½«ÆÚ·µ»Ø¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4d797c90012b4c22ac6e999bdfad8d63.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/158d309e86084a76be392ac8b67ccb94.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/04b51596154e4d4b933a39f0eb249587.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/86453757688743df89df30752bbbe0e5.png "image.png") + +ÏÖÀ࣬ÎÒÃÇ¿ÉÒԲ²âһϣ¬»òÕßdebug½øÈ¥£¬µ±È»Õâ¸öÀ࣬ÎÒÃǵã»÷һϣ¬ÔÚÉùÃ÷µÄµØ·½ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f7a189074e00495da10119f2a5de7bc5.png "image.png") + +ËüÖ¸¶¨ÁËÃû³Æ£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c54ca72e12e04d80b189d587a535d834.png "image.png") + +ºÃ£¬ÎÒÃÇÈ«ÎÄËÑË÷¿´Ò»Ï£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/2b24cf84302c42669bed76d19a11e301.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/fcf6ffab779548a694a68c443a90a214.png "image.png") + +ÎÒÃÇÖªµÀÇ°ÃæËµ¹ýephemeralÊÇtrueËùÒÔÑ¡ÔñµÚÒ»¸ö£º ÓÐÒÉÎÊ£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4adb989d26cf46f98c943a114e047679.png "image.png") + +ÎÒÃÇÓ¦¸Ãµ÷ÓÃEphemeralConsistencyService¶ÔÓ¦µÄput·½·¨¡£µ«ÊÇEphemeralConsistencyServiceÖ»ÊÇÒ»¸ö½Ó¿Ú£¬ÎÒÃÇÓ¦¸Ãµ÷ÓöÔÓ¦µÄʵÏÖʵÀý¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/87869280d709441fbb8ceec818d67a97.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3764ecf8f5904315af85482f3c3ee71d.png "image.png") + +ÎÒÃÇ¿´Ò»ÏÂËûµÄonput·½·¨¡£ + +ÏÖÔڷŵ½¶ÓÁÐÖУº + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/02a34386b7444f0e8a6e35e57ceedf84.png "image.png") + +ÕâÀïÃæ¾ÍÊǰѺËÐĵÄÇëÇó·Åµ½blockqueneÀïÃæ£¬Ò²¾ÍÊÇÒ»¸ö×èÈû¶ÓÁÐÖÐ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/81b1c141753c492493eb095ead9e34da.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/252863f614a24c2da64713af52669ce5.png "image.png") + +Õû¸ö×¢²áµÄ¹ý³Ì¾ÍÕâô¼òµ¥£¬Ò»¸öÇëÇó¹ýÀ´£¬×îÖÕ½«·þÎñ×¢²áµÄÇëÇ󣬷ŵ½ÎÒÃǵÄ×èÈû¶ÓÁе±ÖУ¬·Åµ½ÔòÉ«¶ÓÁÐÖ®ºó£¬Õû¸ö×èÈû¶ÓÁоͷµ»ØÁË¡£ÄǷŵ½×èÈû¶ÓÁÐÖ®ºó£¬ÄÄÀïÓзÃÎÊÕâ¸ö×èÈû¶ÓÁС£ + +´ó¼Ò×¢ÒâÕâ¸öNotifierÊÇÒ»¸öỊ̈߳¬ÀÏʦ½»´ó¼ÒÒ»¸ö¼¼ÇÉ£ºÈç¹ûÓöµ½Ò»¸öÏ߳̾ÍÐèÒª¿´ËûµÄrun·½·¨ÒòΪrun·½·¨²ÅÊÇËû ÕæÕýÖ´ÐдúÂëµÄµØ·½¡£ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/659d96fac1d445859a6ea6086ab8277c.png "image.png") + +ÔÚÕâÀï½øÐÐËÀÑ­»·½øÐÐÊý¾Ý´¦Àí£¬²»¶ÏµÄ´¦Àí¿Í»§¶Ë×¢²áµÄÐÅÏ¢£¬¶ª½øÀ´¾ÍʵÏÖºóÃæµÄÒì²½×¢²áÐÅÏ¢£¬ + +Õâ¸öÏ̻߳áÒ»Ö±ÔÚת£¬Ò»Ö±ÔËÐУ¬Èç¹ûËû¹ÒÁËÄÇ˵Ã÷Õû¸ö·þÎñ¾Í¹ÒµôÁË£¬ºÃ£¬Äã¿´×ÅÀïÃæµÄÒì³£Ò²³ÔµôÁË£¬ËùÒÔÒ»Ö±»áÔËÐУ¬Èç¹ûûÓÐÊý¾ÝËû»á×èÈû£¬×èÈû»áÈóöcpu + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/487a2e6d15f642549fe0d145447cc5cf.png "image.png") + +×¢²á±í½á¹¹µÄ·ÖÎö£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3eab037db7e2480ba4a9b439b18567da.png "image.png") + +Ê×Ïȵ±ËûÊǸöchangeµÄʱºò£¬ÎÒÃǾͽøÈëonChange£¬ËûÊǸö·þÎñËùÒÔÎÒÃǽøÈëËûµÄserviceÀïÃæÈ¥¿´£º + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4adf69d76188430d860f2003b6c508d6.png "image.png") + +ÕâÀïÃæ¾Í²»Óÿ´ÁË£¬ÏÈ¿´È¨ÖØ£¬È¨ÖØ´óÓÚ¶àÉÙµÄʱºòÉèÖÃ×î´óÖµ£¬È¨ÖØÐ¡ÓÚ¶àÉÙµÄʱºòÉèÖÃÒ»¸ö×îСֵ¡£È»ºó¾ÍÊǺËÐĵķ½·¨updateIP + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/887332bfdcb64c2f9ad871ee55961b59.png "image.png") + +ÄÇÕâ¸öupdateIPsÊÇ×öÊ²Ã´ÄØ£¿ ×öµÄ¾ÍÊÇÎÒ±éÀúÎÒËùÓÐҪע²áµÄʵÀý£¬È»ºó¾Í·Åµ½ÎÒÃǵÄclusterMapÀïÃæ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/87df4508357847f1ab49a785ab464e5b.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b31cf0150533469ebd4fbc462c6a0952.png "image.png") + +Äǵ½ÕâÀï´ó¼Ò¿ÉÄܾÍÓÐÒÉÎÊÁË£¬ÄÇʲôʱºòÆô¶¯Õâ¸öỊ̈߳¬À´ÊµÊ±¼àÌýÎÒÃÇÏûÏ¢µÄ×èÈû¶ÓÁÐÄØ£¿ ½Ì´ó¼ÒÈçºÎÕÒÒ»ÏÂÕâ¸ö·½·¨£¬ÎÒÃÇÏÖÔÚ¿´×ŸöÀà½ÐNotifier£¬ÒòΪËû±¾Éí¾ÍÊÇÒ»¸öỊ̈߳¬Ëû»á¶ªµ½Ò»¸öÏ̳߳ØÖнøÐÐÔËÐУ¬ÎÒÃÇ¿´Ò»ÏÂËû¾¿¾¹ÊÇÔÚÄÄÀïʵÀý»¯µÄ£¬ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/16223a5f9aab4fdaa004d0698e6cb0a6.png "image.png") + +ÎÒÃÇ¿´µ½Õâ¸ö·½·¨£ºÕâ¸ö×¢½â¾ÍÊǵ±ÄãµÄspringµÄÒ»¸öÀà½øÐгõʼ»¯Ö®ºó½øÐе÷Óõģ¬ÄÇÎÒÃÇ¿´Ò»ÏÂÕâ¸öinit·½·¨µ½µ×ÊÇ×öÁËʲô + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b9d2a25274834d73ab8f939006e9b075.png "image.png") + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/64f25704efac43a7ae6ee5afda46cdf7.png "image.png") + +ÊÇÒ»¸öScheduledÏ̳߳أº + +![image-20211222222903941](E:\BaiduNetdiskWorkspace\springcloud alibaba\img\image-20211222222903941.png) + +Ò²¾ÍÊÇÔÚ¶ÔÏó³õʼ»¯µÄʱºò¾Í½øÐÐÆô¶¯Ò»¸öÏ̳߳أ¬È¥ÔËÐÐnotifier¶ÔÓ¦µÄ·½·¨¡£Õâ¸örun·½·¨¾ÍÊÇÕâÑùrunµÄ¡£Æô¶¯ºó¾Í»áʵʱ¼àÌýÒì²½¶ÓÁС£ÕâÑùдµÄºÃ´¦£¬¾ÍÊǽ«Ð´ºÍ´¦ÀíÍêÈ«¸ôÀëÁË¡£Í¨¹ý¼àÌý¸ßÐÔÄܵÄÄÚ´æ¶ÓÁУ¬À´´¦ÀíÕâ¸öÊÂÇ飬ËûÕâÑùµÄºÃ´¦£¬1¡¢Ìá¸ßÐÔÄÜ + +![image.png](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e27c8e72b30845c3ab9346b89932fb42.png "image.png") + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" new file mode 100644 index 0000000..dc517a0 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\246\202\350\247\210.md" @@ -0,0 +1,501 @@ +ÔÚ¿´ÕâÆªÎÄÕÂ֮ǰ£¬×îºÃ¶ÔNACOSÏà¹Ø¹¦ÄÜÓÐËùÁË½â£¬ÍÆ¼ö¿´Íê[Spring Cloud Alibaba Nacos£¨¹¦ÄÜÆª£©](https://zhuanlan.zhihu.com/p/68700978)¡£ + +Õë¶Ô¹¦ÄÜ£¬ÓÐÄ¿µÄµÄÈ¥ÕÒÏà¶ÔÓ¦µÄÔ´´úÂ룬½øÒ»²½Á˽⹦ÄÜÊÇÈçºÎ±»ÊµÏÖ³öÀ´µÄ¡£ + +±¾ÎÄÕë¶ÔÓÐÒ»¶¨Ô´´úÂëÔĶÁ¾­ÑéµÄÈËȺ£¬²»»áÉîÈëÌ«¶àµÄϸ½Ú£¬»¹ÐèÒª¶ÁÕß´ò¿ªÔ´Âë¸ú×Ù£¬×ÔÐÐÁì»á¡£ + +## Ò»¡¢Òý×Ó + +½øÈëGitHub¶ÔÓ¦µÄ[Ò³Ãæ](https://link.zhihu.com/?target=https%3A//github.com/alibaba/nacos)£¬½«NACOS¹¤³ÌcloneÏÂÀ´¡£Ä¿Â¼ºÍÎļþ¿´ÆðÀ´ºÜÈß³¤£¬µ«ÊǶÔÓÚ¿´Ô´´úÂëÕæÕýÓаïÖúµÄ²¿·Ö²¢²»¶à¡£ + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-e9966f158af7cfac39baf5bba456fd17_720w.webp) + +
nacos¹¤³ÌĿ¼½á¹¹
+ +
+ +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9195cfddde4e94b16f239bc101825a5a_720w.webp) + +
Ä£¿éÒÀÀµÍ¼
+ +
+ +
+ + +![](https://pic4.zhimg.com/80/v2-6b30d0fc994745002ee7dcc7b04154d3_720w.webp) + +
nacosÇëÇóÄ£ÐÍ
+ +
+ +ÓÐÁËÕâÈýÕÅͼ£¬¾ÍÄÜ˳ÀûÕÒµ½Í»ÆÆ¿ÚÁË£¬ºËÐÄÄÚÈݾͼ¯ÖÐÔÚnacos-console£¬nacos-naming£¬nacos-config£¬Ë³ÌÙÃþ¹Ï£¬¾ÍÄÜ¿´µ½²»ÉÙÄÚÈÝÁË¡£ + +Èç¹û»¹ÊǸоõÎÞ´ÓÏÂÊֵϰ£¬ÄǾÍÒÆ²½nacos-example£¬ÀïÃæÓÐÖ÷ÒªÒµÎñµÄµ÷ÓÃÈë¿Ú£¬Ò»¿´±ãÖª¡£ + +## ¶þ¡¢ÅäÖ÷þÎñ + +Ê×ÏÈ´ÓÒ»¸ö¹¤³§Àà˵Æð£ºcom.alibaba.nacos.api.NacosFactory¡£ + +ÀïÃæµÄ¾²Ì¬·½·¨ÓÃÓÚ´´½¨ConfigServiceºÍNamingService£¬´úÂëÀàËÆ£¬ÒÔ´´½¨ConfigServiceΪÀý£º + + + +``` +public static ConfigService createConfigService(Properties properties) throws NacosException { + try { + Class driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); + Constructor constructor = driverImplClass.getConstructor(Properties.class); + ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); + return vendorImpl; + } catch (Throwable e) { + throw new NacosException(-400, e.getMessage()); + } +} +``` + + + +ûÓÐʲô¸´ÔÓµÄÂß¼­£¬Ê¹ÓõÄÊÇ»ù±¾µÄ·´ÉäÔ­Àí¡£¹¹Ôì²ÎÊý´«ÈëÁËproperties£¬ÕâЩÊôÐÔ¿ÉÒÔͨ¹ýbootstrap.ymlÖÐÖ¸¶¨£¬¶ÔÓ¦µÄÊÇNacosConfigProperties¡£ + +ÐèҪϸ¿´µÄÊǹ¹Ô캯ÊýÖжÔÓÚnamespace³õʼ»¯µÄÄDz¿·ÖÄÚÈÝ¡£ + + + +``` +private void initNamespace(Properties properties) { + String namespaceTmp = null; + + String isUseCloudNamespaceParsing = + properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, + System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, + String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING))); + + if (Boolean.valueOf(isUseCloudNamespaceParsing)) { + namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable() { + @Override + public String call() { + return TenantUtil.getUserTenantForAcm(); + } + }); + + namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable() { + @Override + public String call() { + String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE); + return StringUtils.isNotBlank(namespace) ? namespace : EMPTY; + } + }); + } + + if (StringUtils.isBlank(namespaceTmp)) { + namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); + } + namespace = StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : EMPTY; + properties.put(PropertyKeyConst.NAMESPACE, namespace); +} +``` + + + +´«ÈëµÄproperties»áÖ¸¶¨ÊÇ·ñ½âÎöÔÆ»·¾³ÖеÄnamespace²ÎÊý£¬Èç¹ûÊǵ쬾ÍÊÇÈ¥¶ÁÈ¡°¢ÀïÔÆ»·¾³µÄϵͳ±äÁ¿£»Èç¹û²»ÊÇ£¬ÄÇô¾Í¶ÁÈ¡propertiesÖÐÖ¸¶¨µÄnamespace£¬Ã»ÓÐÖ¸¶¨µÄ»°£¬×îÖÕ½âÎö³öÀ´µÄÊÇ¿Õ×Ö·û´®¡£´Ó´úÂëÉÏ¿´³öÀ´£¬»ñÈ¡ÔÆ»·¾³µÄnamespace×ö³ÉÁËÒì²½»¯µÄÐÎʽ£¬µ«ÊÇĿǰ°æ±¾»¹ÊÇʹÓõÄͬ²½µ÷Óᣠ+ +¼ÌÐø¸ú×ÙConfigService£¬ÀïÃæ¶¨ÒåÁËһϵÁнӿڷ½·¨£¬ÕýÊÇÎÒÃÇËùÒª¿´µÄ¡£ + +ÿ¸öÒµÎñʵÏÖ×îÖÕ¶¼¹é½áΪHttpÇëÇ󣬾ÍÊÇÅäÖõÄserverAddr£¬¶à¸öµØÖ·»áÒÀ´ÎÂÖתʹÓ㬵±È»ÊÇÔÚÒ»¶¨³¬Ê±Ê±¼äÄÚÒÀ´ÎÇëÇ󣬶¼ÇëÇ󲻳ɹ¦ÁË£¬ÄǾͻáÅ׳öÒì³£¡£ + +ÇëÇó·½ÊÇnacos-client£¬½ÓÊÕ·½×îÖÕ¶¼ÊÇÂäµ½nacos-config·þÎñÉÏ£¬×îºóʹÓÃJdbcTemplate½øÐÐÊý¾Ý³Ö¾Ã»¯¡£ + +ÕâÒ»²¿·ÖµÄ´úÂëÒ»¿´¾ÍÃ÷°×£¬·¢²¼ÅäÖ㬻ñÈ¡ÅäÖúÍɾ³ýÅäÖö¼ÓÐËùÌåÏÖ£¬¾Í²»Õ¹¿ª²ûÊöÁË¡£ + +ÖØµã½âÎöÒ»ÏÂÅäÖüàÌý²¿·ÖµÄÔ´´úÂë¡£ + +ÏȽ«×¢ÒâÁ¦·ÅÔÚcom.alibaba.nacos.client.config.impl.CacheDataÕâ¸öÊý¾Ý½á¹¹ÉÏ£¬ÊǸöµäÐ͵ijäѪģÐÍ£¬Ö÷ÒªÊdz䵱listener¹ÜÀíÕߵĽÇÉ«£¬ÕâÑù¿´À´£¬ÀàÃûÈ¡µÃ²¢²»ÊÇÄÇôÓѺÃÁË¡£ + +ʵ¼ÊÉÏ£¬¿ÉÒÔ¿´³öCacheData½«ÅäÖÃÐÅÏ¢£¨namespace, content£©ºÍlistener¾ÛºÏÔÚÒ»ÆðÁË£¬¿ÉÒÔÈÏΪһÏîÅäÖÿÉÒÔ¸½¼Ó¶àÖÖlistenerʵʩ¼àÌý£¨ÒòΪlistener½Ó¿Ú¿ÉÄÜÓжàÖÖʵÏÖ£©£¬Ã¿ÖÖlistenerÖ»»áÓÐÒ»¸öʵÀý¸½¼ÓÔÚÅäÖÃÉÏ¡£ + + + +``` +public void addListener(Listener listener) { + if (null == listener) { + throw new IllegalArgumentException("listener is null"); + } + ManagerListenerWrap wrap = new ManagerListenerWrap(listener); + if (listeners.addIfAbsent(wrap)) { + LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group, + listeners.size()); + } +} +``` + + + +ʹÓÃÁËCopyOnWriteArrayList.addIfAbsent·½·¨£¬Õâ¸ö·½·¨×îÖØÒª¾ÍÊÇequals·½·¨£¬ManagerListenerWrapÊǶÔlistenerµÄÁíÍâÒ»ÖÖÐÎʽµÄ°ü¹ü£¬ÆäʵÏÖÁËequals·½·¨£º + + + +``` +@Override +public boolean equals(Object obj) { + if (null == obj || obj.getClass() != getClass()) { + return false; + } + if (obj == this) { + return true; + } + ManagerListenerWrap other = (ManagerListenerWrap) obj; + return listener.equals(other.listener); +} +``` + + + +ÔÙÍùÉϲ㷭£¬¿ÉÒÔÕÒµ½¶ÔÓÚlistener¸ü¸ß²ãµÄ¹ÜÀíAPI£ºcom.alibaba.nacos.client.config.impl.ClientWorker¡£ + +ͬÑùÊǶÔlistenerµÄ¹ÜÀí£¬µ«ÊÇÔö¼ÓÁËÖØ¸´Ð£Ñ飬ÆäÖÐcacheMapÊǹؼü£¬Èç϶¨Ò壺 + + + +``` +private final AtomicReference> cacheMap = new AtomicReference>() +``` + + + +ʹÓÃÁ˾ßÓÐÔ­×ÓÐÔ²Ù×÷ÌØÐÔµÄAtomicReference£¬¿ÉÒÔ±ÜÃâ²¢·¢´øÀ´µÄÊý¾Ý²»Ò»ÖµÄÎÊÌ⣬ÀïÃæ°ü¹üµÄÊÇÒ»¸öHashMap£¬valueÊÇCacheData¶ÔÏ󣬶økeyÊÇÓÐÒ»¶¨Éú³É¹æÔòµÄ£¬ÔÚGroupKeyÕâ¸öÀàÖпÉÒÔÕÒµ½£º + + + +``` +static public String getKeyTenant(String dataId, String group, String tenant) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(tenant)) { + sb.append('+'); + urlEncode(tenant, sb); + } + return sb.toString(); +} +``` + + + +ʵ¼ÊÉÏÊǽ«ÅäÖÃÐÅÏ¢Óá°+¡±ºÅ½øÐÐÆ´½Ó£¬Èç¹ûÅäÖÃÐÅÏ¢Öб¾Éí´æÔÚÁË¡°+¡±ºÍ¡°%¡±£¬»áʹÓÃurlEncode·½·¨½øÐбàÂëתÒå¡£µ±È»£¬Ò²ÓÐÅäÌ׵ĽâÎö·½·¨£¬ÕâÀï¾Í²»ÔÙÕ¹¿ª½²½âÁË¡£ + +½ÓÏÂÀ´µÄÎ޷ǾÍÊǾÍcacheMapµÄһϵÁÐgetºÍset²Ù×÷£¬ÓÃÒÔά»¤listener¡£Ìرð×¢ÒâµÄÊÇ£¬Ã¿´Î¸üвÙ×÷¶¼ÊÇÏÈÉú³ÉÒ»¸öcopy¶ÔÏ󣬲Ù×÷´Ë¶ÔÏóÖ®ºó£¬ÔÙÕû¸öset£¨¸²¸Ç£©µ½cacheMapÖС£ + +×îºó˵һÏÂlistenerÊÇÈçºÎÔËÐÐÆðÀ´µÄ¡£ + +ÈÔÈ»ÊÇÔÚClientWorkerµ±ÖпÉÒÔÕÒµ½£¬½«×¢ÒâÁ¦×ªÒƵ½¹¹Ô캯ÊýÖС£ÆäÖУ¬¿ÉÒÔ×¢Òâµ½£¬³õʼ»¯ÁËÁ½¸öÏ̳߳أº + + + +``` + executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); + t.setDaemon(true); + return t; + } + }); + + executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName()); + t.setDaemon(true); + return t; + } + }); + + executor.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + try { + checkConfigInfo(); + } catch (Throwable e) { + LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); + } + } + }, 1L, 10L, TimeUnit.MILLISECONDS); +``` + + + +Á½¸öÓÃÓÚÖ´Ðж¨Ê±ÈÎÎñµÄscheduledThreadPool£¬¶øÕâÁ½¸öÏ̳߳صķֹ¤Ò²ÊÇǶÌ׵ģºexecutorÓÃÓÚ·¢²¼ÅäÖüì²éµÄÈÎÎñ£¬¶øexecutorServiceÔòÊÇÈÎÎñµÄ½ÓÊÕÕߣ¬ÊÇÕæÕýÖ´ÐÐÈÎÎñµÄ½ÇÉ«¡£ + +ËùÒÔ·¢²¼ÈÎÎñµÄÏ̳߳ØÖ»·ÖÅäÁË1¸öºËÐÄÏß³ÌÊý£¬¶øÖ´ÐÐÈÎÎñµÄÏ̳߳صĺËÐÄÏß³ÌÊÇCPUºËÊý¡£ + +ÒòΪÅäÖüì²éÊÇÒ»¸ö³¤ÂÖѯµÄ¹ý³Ì£¬Ò»¸öÈÎÎñÖ´ÐÐÕßÄܼà²âµÄÅäÖÃÊýÁ¿ÐèÒªµÃµ½¿ØÖÆ£¬ËùÒÔNACOSĿǰʹÓÃÁËÒ»¸ö±È½Ï¼òµ¥µÄ·ÖÈÎÎñ¹æÔò£º + + + +``` +public void checkConfigInfo() { + // ·ÖÈÎÎñ + int listenerSize = cacheMap.get().size(); + // ÏòÉÏÈ¡ÕûΪÅúÊý + int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize()); + if (longingTaskCount > currentLongingTaskCount) { + for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) { + // ÒªÅжÏÈÎÎñÊÇ·ñÔÚÖ´ÐÐ Õâ¿éÐèÒªºÃºÃÏëÏë¡£ ÈÎÎñÁбíÏÖÔÚÊÇÎÞÐòµÄ¡£±ä»¯¹ý³Ì¿ÉÄÜÓÐÎÊÌâ + executorService.execute(new LongPollingRunnable(i)); + } + currentLongingTaskCount = longingTaskCount; + } +} +``` + + + +ÔÚParamUtil.getPerTaskConfigSize()Öзµ»ØµÄÊÇÿ¸öÈÎÎñÄܼà²âµÄÅäÖÃÊýÁ¿ÉÏÏÞ£¬Ä¬ÈÏÊÇ3000Ìõ£¬¿ÉÒÔͨ¹ýϵͳ±äÁ¿PER_TASK_CONFIG_SIZE¸ü¸ÄÕâ¸öÉÏÏÞ¡£ + +´Ó´úÂëÉÏ¿ÉÒÔ¿´³ö£¬Èç¹ûµ±Ç°listenerµÄÊýÁ¿Ã»Óг¬¹ý3000¸ö£¬ÅäÖüà²âµÄÏ̳߳ػ¹ÔËת²»ÆðÀ´¡£Èç¹ûϸ¿´Õâ¸ö²¿·ÖµÄ´úÂ룬»¹ÊǻᷢÏÖһЩÎÊÌâµÄ£¬Ö÷ÒªÊÇÎ§ÈÆ×ÅÈÎÎñ¹ÜÀíÑÜÉú³öÀ´µÄһϵÁÐÎÊÌâ¡£ + +³¤ÂÖѯÀïÃæÖ÷ÒªÓÐÁ½²¿·ÖÂß¼­£º + +* ¼ì²é±¾µØÅäÖã¬ÓëCacheData´æ´¢µÄÐÅÏ¢±£³ÖÒ»Ö£» +* ¼ì²éserver¶ËÅäÖ㬸üÐÂCacheData´æ´¢µÄÐÅÏ¢¡£ + +## Èý¡¢·þÎñ×¢²áÓë·¢ÏÖ + +ÓÐÁËÉÏÊöµÄ»ù´¡£¬Õⲿ·Ö´úÂë¿´ÆðÀ´»á±È½ÏÇáËÉÁË£¬½á¹¹ÉÏ»ù±¾ÏàËÆ¡£ + +Ö±½Ó½øÈëcom.alibaba.nacos.api.naming.NamingService£¬ÀïÃæÓжà¸öregisterInstanceÖØ¹¹·½·¨£¬ÓÃÓÚ·þÎñ×¢²á¡£ + +ÏÈ¿´¿´InstanceʵÌåÀà°üº¬µÄÄÚÈÝ£ºid£¬ip£¬port£¬serviceName£¬clusterName£¨ËùÔÚ¼¯Èº£©£¬weight£¨È¨ÖØ£©£¬healthy£¨ÊÇ·ñÕý³££©£¬enabled£¨ÊÇ·ñÆôÓã©£¬ephemeral£¨ÊÇ·ñÊÇÁÙʱµÄ£©£¬Õâ9¸öÊôÐÔÈ«²¿¶¼¿ÉÒÔÔÚConsoleÖÐÓÐËùÌåÏÖ¡£ + +È»ºó£¬Ö±½Ó¿´×¢²á·þÎñµÄ·½·¨£º + + + +``` + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + + if (instance.isEphemeral()) { + BeatInfo beatInfo = new BeatInfo(); + beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName)); + beatInfo.setIp(instance.getIp()); + beatInfo.setPort(instance.getPort()); + beatInfo.setCluster(instance.getClusterName()); + beatInfo.setWeight(instance.getWeight()); + beatInfo.setMetadata(instance.getMetadata()); + beatInfo.setScheduled(false); + beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); + } + serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); + } +``` + + + +Ç°ÃæÒ»´ó¶Î´úÂëÊǶÔÁÙʱ·þÎñʵÀýµÄ´¦Àí£¬¾ÍÊÇÔÚ¹¹ÔìÒ»¸öÐÄÌø°ü·¢Ë͸øNACOS·þÎñ¡£ + +registerService·½·¨¾ÍÊÇ·â×°ÁËHTTPÇëÇó£¬×îÖÕÔÚInstanceControllerÖд¦ÀíÇëÇó¡£ + +Èç¹ûÏîÄ¿¼¯³ÉÁËspring-cloud-starter-alibaba-nacos-discovery£¬·þÎñÆô¶¯ºóĬÈÏÊÇ×Ô¶¯×¢²áµÄ¡£Èç¹ûÏë¿´×Ô¶¯×¢²áµÄ¹ý³Ì£¬¿ÉÒÔ´ÓAbstractAutoServiceRegistration¿ªÊ¼×ÅÊÖ£¬µ±ÖÐÓÐÒ»¶Î´úÂ룺 + + + +``` + @EventListener(WebServerInitializedEvent.class) + public void bind(WebServerInitializedEvent event) { + ApplicationContext context = event.getApplicationContext(); + if (context instanceof ConfigurableWebServerApplicationContext) { + if ("management".equals( + ((ConfigurableWebServerApplicationContext) context).getServerNamespace())) { + return; + } + } + this.port.compareAndSet(0, event.getWebServer().getPort()); + this.start(); + } +``` + + + +¼àÌýÁËWeb·þÎñ³õʼ»¯Íê³ÉµÄʼþ£¬×îÖÕ»áÖ´ÐÐstart·½·¨£º + + + +``` + public void start() { + if (!isEnabled()) { + if (logger.isDebugEnabled()) { + logger.debug("Discovery Lifecycle disabled. Not starting"); + } + return; + } + // only initialize if nonSecurePort is greater than 0 and it isn't already running + // because of containerPortInitializer below + if (!this.running.get()) { + register(); + if (shouldRegisterManagement()) { + registerManagement(); + } + this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration())); + this.running.compareAndSet(false, true); + } + + } +``` + + + +ÆäÖУ¬register·½·¨¾ÍÊÇ×îºËÐĵIJ¿·ÖÁË£¬À´Ô´ÓÚNacosServiceRegistryµÄʵÏÖ£º + + + +``` + @Override + public void register(NacosRegistration registration) { + + if (!registration.isRegisterEnabled()) { + logger.info("Nacos Registration is disabled..."); + return; + } + if (StringUtils.isEmpty(registration.getServiceId())) { + logger.info("No service to register for nacos client..."); + return; + } + NamingService namingService = registration.getNacosNamingService(); + String serviceId = registration.getServiceId(); + + Instance instance = new Instance(); + instance.setIp(registration.getHost()); + instance.setPort(registration.getPort()); + instance.setWeight(registration.getRegisterWeight()); + instance.setClusterName(registration.getCluster()); + instance.setMetadata(registration.getMetadata()); + try { + namingService.registerInstance(serviceId, instance); + logger.info("nacos registry, {} {}:{} register finished", serviceId, instance.getIp(), instance.getPort()); + }catch (Exception e) { + logger.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e); + } + } +``` + + + +Õâ¶Î´úÂë¾Í·Ç³£ÊìϤÁË£¬×îÖվͻص½ÁËÉÏÊöµÄnamingService.registerInstance·½·¨¡£ + + + +``` + /** + * Map> + */ + private Map> serviceMap = new ConcurrentHashMap<>(); +``` + + + +ÒÔÉϳöÏÖÁËÁíÍâÒ»¸öʵÌåÀࣺcom.alibaba.nacos.naming.core.Service£¬ServiceÊǰüº¬ÁËInstance£¬Ò»¸öServiceÏÂÓжà¸öInstance£¬¼´¿É×é³ÉÒ»¸öCluster¡£ + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-21ca2c51d56d6401dee0db0444df8ddf_720w.webp) + +
¶àʵÀý¼¯Èº
+ +
+ +ÔÚµ÷ÓÃregisterInstance×¢²áʵÀýµÄʱºò£¬Èç¹û·¢ÏÖ¶ÔÓ¦µÄServiceûÓб»×¢²á£¬ÄÇô»áregisterService£¬²¢ÇÒ»á³õʼ»¯¶ÔÓ¦µÄCluster£¬Æô¶¯½¡¿µ¼ì²éµÄ¶¨Ê±Æ÷¡£ + +ºÍregisterInstanceÏà·´µÄÊÇderegisterInstance£¬¼´ÎªÈ¡Ïû×¢²á£¬Ò²¿ÉÒÔÈÏΪÊÇ·þÎñʵÀýÏÂÏß¡£ + +×îºóÀ´¿´¿´NACOSÈçºÎʵÏÖ·þÎñ·¢ÏÖ¹¦ÄÜ¡£ + +´ÓÏû·ÑÕߣ¨µ÷Ó÷½£©µÄ½Ç¶ÈÀ´¿´£¬¼¯³ÉµÄstarterÏîÄ¿ÖÐÓиöÀࣺNacosServerList£¬×îÖØÒªµÄÊǼ̳ÐÁËAbstractServerList£¬ÊµÏÖÁËÁ½¸ö¹Ø¼üµÄ½Ó¿Ú·½·¨£¬Ï൱ÓÚÊÇNACOSÓëRibbonµÄ¶Ô½Óµã¡£ + + + +``` +public interface ServerList { + + public List getInitialListOfServers(); + + /** + * Return updated list of servers. This is called say every 30 secs + * (configurable) by the Loadbalancer's Ping cycle + * + */ + public List getUpdatedListOfServers(); + +} +``` + + + +NACOS¶ÔÓÚÕâ¸öÁ½¸ö½Ó¿ÚµÄʵÏÖ£¬¶¼Ê¹ÓÃÁËgetServers·½·¨£¬¶ø½øÈëµ½getServers·½·¨ÌåÀïÃæ£¬Æäʵ¾ÍÊÇÀûÓÃÁËÉÏÊöËù˵µÄNacosNamingService.selectInstances·½·¨£¬Í¨¹ýserviceId»ñÈ¡µ½ServiceInfo¶ÔÏó£¬È»ºó»ñÈ¡µ½ServiceÏÂÃæµÄËùÓÐÓÐЧµÄInstance¡£ + +´ÓÌṩÕߣ¨±»µ÷Ó÷½£©µÄ½Ç¶È¿´£¬NACOSÊÇͨ¹ý¶¨Ê±Æ÷À´ÊµÊ±¸üÐÂServiceInfo£¬Ö÷ÒªÒµÎñÂß¼­ÊÇÔÚHostReactorÖÐʵÏֵġ£ÓëǰÊöµÄserviceMap²»Ò»Ñù£¬HostReactorÖÐά»¤µÄÊÇserviceInfoMap¡£ + + + +``` +private Map serviceInfoMap; +``` + + + +HostReactor½èÖúÁËFailoverReactor¶ÔServiceInfo×öÁË´ÅÅÌ»º´æ£¬ÈÔÈ»ÊÇÆô¶¯Á˶¨Ê±ÈÎÎñ£¬ÔÚÖ¸¶¨µÄĿ¼ÏÂÐòÁл¯ServiceInfo£¬ÒÔ´ËʵÏÖÁËFailover»úÖÆ¡£¶øÆô¶¯failover-modeÒ²ÊÇÓпª¹ØµÄ£¬Æäʵ¾ÍÊÇÒ»¸öÌØ¶¨ÎļþµÄÒ»²¿·ÖÄÚÈÝ£¬ÕâЩÅäÖõļà²âÒ²ÊÇͨ¹ý¶¨Ê±ÈÎÎñÀ´ÊµÏֵġ£ + + + +``` +File switchFile = new File(failoverDir + UtilAndComs.FAILOVER_SWITCH); +``` + + + +Õû¸ö¹ý³ÌÈçÏÂͼËùʾ£º + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d16112ec6ff6dda0b029b019c313177c_720w.webp) + +
·þÎñ·¢ÏÖ¸ú×Ùͼ
+ +
+ +## ËÄ¡¢¹ÜÀí¿ØÖÆÌ¨(Console) + +ÕâÒ»²¿·ÖÊǹÜÀí¿ØÖÆÌ¨µÄʵÏÖ£¬ÆäʵÊÇÒ»¸ö·Ç³£µäÐ͵ÄWEBÏîÄ¿¡£ + +ʹÓÃÁËSpring Security + JWT½øÐа²È«¿ØÖÆ£¬Ç°¶Ë¼¼ÊõÊÇReactJs£¬ÀûÓÃJdbcTemplate½øÐÐÊý¾Ý¿â³Ö¾Ã»¯¡£ + +ÐèҪעÒâµÄÊÇ£¬¿ØÖÆÌ¨ÌṩµÄ¹¦Äܲ¢²»¶¼ÊÇ´Ónacos-consoleÕâ¸ö·þÎñÖлñÈ¡µÄÊý¾Ý£¬¶øÊÇ·ÖÉ¢ÔÚÁ˸÷¸ö·þÎñÖС£ + +nacos-consoleÌṩÁË¿ØÖÆÌ¨µÇ¼£¬namespace¹ÜÀí£¬¿ØÖÆÌ¨·þÎñ״̬ÕâÈý²¿·ÖÄÜÁ¦£¬¶øÅäÖùÜÀíºÍ·þÎñ¹ÜÀí·Ö±ðÇëÇóµÄÊÇnacos-configºÍnacos-namingËùÌṩµÄAPI£¬¶øÕâЩAPI¾ÍÊǹÙÍøËùÌáµ½µÄOpen-API¡£ + +## Îå¡¢×ܽá + +NACOSÏà¹ØÔ´ÂëͨË×Ò×¶®£¬Ã»ÓÐʲô¸ßÉîµÄÀíÄҲûÓнøÐвã²ã·â×°ºÍ°ü¹ü£¬ÓÐÒ»¶¨±à³Ì¾­ÑéµÄ³ÌÐòÔ±ÄÜÔÚ°ëСʱ֮ÄÚ°ÑÎÕÕû¸öÏîÄ¿µÄÂöÂç¡£ + +µ±È»£¬Ò²»á´æÔÚһЩ²»¿ÉºöÊÓµÄȱµã£¬±ÈÈ磬עÊ͹ýÉÙ£¬´úÂ뻹ÓкܴóµÄÖØ¹¹¿Õ¼ä£¬tenantºÍnamespaceÁ½¸ö¸ÅÄî»ìÏýʹÓᣠ+ +¹ØÓÚSpring Cloud Alibaba NacosµÄ½éÉܵ½´Ë¾Í½áÊøÁË£¬Ï£Íû¶ÔÄãÓÐËù°ïÖú¡£ +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" new file mode 100644 index 0000000..7515d8b --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudAlibabaNacos\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\351\205\215\347\275\256\344\270\255\345\277\203.md" @@ -0,0 +1,266 @@ +# NacosÅäÖÃÖÐÐÄ + +## NacosÅäÖÃÖÐÐĵÄʹÓà + +²Î¿¼¹Ù·½£º[github.com/alibaba/spr¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2Fspring-cloud-alibaba%2Fwiki%2FNacos-config "https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config") + +## ConfigÏà¹ØÅäÖà + + Nacos Êý¾ÝÄ£ÐÍ Key ÓÉÈýÔª×éΨһȷ¶¨, NamespaceĬÈÏÊǿմ®£¬¹«¹²ÃüÃû¿Õ¼ä£¨public£©£¬·Ö×éĬÈÏÊÇ DEFAULT_GROUP + +![image-20230429084711954](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084711954.png) + +* **Ö§³ÖÅäÖõĶ¯Ì¬¸üÐÂ** + + µ±¶¯Ì¬ÅäÖÃË¢ÐÂʱ£¬»á¸üе½ EnviromentÖУ¬Òò´ËÕâÀïÿ¸ôÒ»ÃëÖдÓEnviromentÖлñÈ¡ÅäÖà + +``` +@SpringBootApplication +public class NacosConfigApplication { + + public static void main(String[] args) throws InterruptedException { + ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args); + + while(true) { + //µ±¶¯Ì¬ÅäÖÃË¢ÐÂʱ£¬»á¸üе½ EnviromentÖУ¬Òò´ËÕâÀïÿ¸ôÒ»ÃëÖдÓEnviromentÖлñÈ¡ÅäÖà + String userName = applicationContext.getEnvironment().getProperty("common.name"); + String userAge = applicationContext.getEnvironment().getProperty("common.age"); + System.err.println("common name :" + userName + "; age: " + userAge); + TimeUnit.SECONDS.sleep(1); + } + } +} +¸´ÖÆ´úÂë +``` + +* **Ö§³ÖprofileÁ£¶ÈµÄÅäÖÃ** + + spring-cloud-starter-alibaba-nacos-config ÔÚ¼ÓÔØÅäÖõÄʱºò£¬²»½ö½ö¼ÓÔØÁËÒÔ dataid Ϊ spring.application.name.{spring.application.name}.spring.application.name.{file-extension:properties} Ϊǰ׺µÄ»ù´¡ÅäÖ㬻¹¼ÓÔØÁËdataidΪ spring.application.name?{spring.application.name}-spring.application.name?{profile}.file?extension:propertiesµÄ»ù´¡ÅäÖᣠ+ + ÔÚÈÕ³£¿ª·¢ÖÐÈç¹ûÓöµ½¶àÌ×»·¾³ÏµIJ»Í¬ÅäÖ㬿ÉÒÔͨ¹ýSpringÌṩµÄ{file-extension:properties} µÄ»ù´¡ÅäÖá£ÔÚÈÕ³£¿ª·¢ÖÐÈç¹ûÓöµ½¶àÌ×»·¾³ÏµIJ»Í¬ÅäÖ㬿ÉÒÔͨ¹ýSpring ÌṩµÄ file?extension:propertiesµÄ»ù´¡ÅäÖá£ÔÚÈÕ³£¿ª·¢ÖÐÈç¹ûÓöµ½¶àÌ×»·¾³ÏµIJ»Í¬ÅäÖ㬿ÉÒÔͨ¹ýSpringÌṩµÄ{spring.profiles.active} Õâ¸öÅäÖÃÏîÀ´ÅäÖᣠ+ +``` +spring.profiles.active=dev +¸´ÖÆ´úÂë +``` + +* **Ö§³Ö×Ô¶¨Òå namespace µÄÅäÖÃ** + + ÓÃÓÚ½øÐÐ×â»§Á£¶ÈµÄÅäÖøôÀë¡£²»Í¬µÄÃüÃû¿Õ¼äÏ£¬¿ÉÒÔ´æÔÚÏàͬµÄ Group »ò Data ID µÄÅäÖá£Namespace µÄ³£Óó¡¾°Ö®Ò»ÊDz»Í¬»·¾³µÄÅäÖõÄÇø·Ö¸ôÀ룬ÀýÈ翪·¢²âÊÔ»·¾³ºÍÉú²ú»·¾³µÄ×Ê£¨ÈçÅäÖᢷþÎñ£©¸ôÀëµÈ¡£ + + ÔÚûÓÐÃ÷È·Ö¸¶¨ ${spring.cloud.nacos.config.namespace} ÅäÖõÄÇé¿öÏ£¬ ĬÈÏʹÓõÄÊÇ Nacos ÉÏ Public Õâ¸önamespace¡£Èç¹ûÐèҪʹÓÃ×Ô¶¨ÒåµÄÃüÃû¿Õ¼ä£¬¿ÉÒÔͨ¹ýÒÔÏÂÅäÖÃÀ´ÊµÏÖ£º + +``` +spring.cloud.nacos.config.namespace=71bb9785-231f-4eca-b4dc-6be446e12ff8 +¸´ÖÆ´úÂë +``` + +* **Ö§³Ö×Ô¶¨Òå Group µÄÅäÖÃ** + + GroupÊÇ×éÖ¯ÅäÖõÄά¶ÈÖ®Ò»¡£Í¨¹ýÒ»¸öÓÐÒâÒåµÄ×Ö·û´®£¨Èç Buy »ò Trade £©¶ÔÅäÖü¯½øÐзÖ×飬´Ó¶øÇø·Ö Data ID ÏàͬµÄÅäÖü¯¡£µ±ÄúÔÚ Nacos ÉÏ´´½¨Ò»¸öÅäÖÃʱ£¬Èç¹ûδÌîдÅäÖ÷Ö×éµÄÃû³Æ£¬ÔòÅäÖ÷Ö×éµÄÃû³ÆÄ¬ÈϲÉÓà DEFAULT_GROUP ¡£ÅäÖ÷Ö×éµÄ³£¼û³¡¾°£º²»Í¬µÄÓ¦Óûò×é¼þʹÓÃÁËÏàͬµÄÅäÖÃÀàÐÍ£¬Èç database_url ÅäÖÃºÍ MQ_topic ÅäÖᣠ+ +ÔÚûÓÐÃ÷È·Ö¸¶¨ ${spring.cloud.nacos.config.group} ÅäÖõÄÇé¿öÏ£¬Ä¬ÈÏÊÇDEFAULT_GROUP ¡£Èç¹ûÐèÒª×Ô¶¨Òå×Ô¼ºµÄ Group£¬¿ÉÒÔͨ¹ýÒÔÏÂÅäÖÃÀ´ÊµÏÖ£º + +``` +spring.cloud.nacos.config.group=DEVELOP_GROUP +¸´ÖÆ´úÂë +``` + +* **Ö§³Ö×Ô¶¨ÒåÀ©Õ¹µÄ Data Id ÅäÖÃ** + + Data ID ÊÇ×éÖ¯»®·ÖÅäÖõÄά¶ÈÖ®Ò»¡£Data ID ͨ³£ÓÃÓÚ×éÖ¯»®·ÖϵͳµÄÅäÖü¯¡£Ò»¸öϵͳ»òÕßÓ¦ÓÿÉÒÔ°üº¬¶à¸öÅäÖü¯£¬Ã¿¸öÅäÖü¯¶¼¿ÉÒÔ±»Ò»¸öÓÐÒâÒåµÄÃû³Æ±êʶ¡£Data ID ͨ³£²ÉÓÃÀà Java °ü£¨Èç com.taobao.tc.refund.log.level£©µÄÃüÃû¹æÔò±£Ö¤È«¾ÖΨһÐÔ¡£´ËÃüÃû¹æÔò·ÇÇ¿ÖÆ¡£ + +ͨ¹ý×Ô¶¨ÒåÀ©Õ¹µÄ Data Id ÅäÖ㬼ȿÉÒÔ½â¾ö¶à¸öÓ¦ÓüäÅäÖù²ÏíµÄÎÊÌ⣬ÓÖ¿ÉÒÔÖ§³ÖÒ»¸öÓ¦ÓÃÓжà¸öÅäÖÃÎļþ¡£ + +``` +# ×Ô¶¨Òå Data Id µÄÅäÖà +#²»Í¬¹¤³ÌµÄͨÓÃÅäÖà ֧³Ö¹²ÏíµÄ DataId +spring.cloud.nacos.config.sharedConfigs[0].data-id= common.yaml +spring.cloud.nacos.config.sharedConfigs[0].group=REFRESH_GROUP +spring.cloud.nacos.config.sharedConfigs[0].refresh=true + +# config external configuration +# Ö§³ÖÒ»¸öÓ¦Óöà¸ö DataId µÄÅäÖà +spring.cloud.nacos.config.extensionConfigs[0].data-id=ext-config-common01.properties +spring.cloud.nacos.config.extensionConfigs[0].group=REFRESH_GROUP +spring.cloud.nacos.config.extensionConfigs[0].refresh=true + +spring.cloud.nacos.config.extensionConfigs[1].data-id=ext-config-common02.properties +spring.cloud.nacos.config.extensionConfigs[1].group=REFRESH_GROUP +¸´ÖÆ´úÂë +``` + +## ÅäÖõÄÓÅÏȼ¶ + +Spring Cloud Alibaba Nacos Config ĿǰÌṩÁËÈýÖÖÅäÖÃÄÜÁ¦´Ó Nacos À­È¡Ïà¹ØµÄÅäÖᣠ+ +* A: ͨ¹ý spring.cloud.nacos.config.shared-configs Ö§³Ö¶à¸ö¹²Ïí Data Id µÄÅäÖà + +* B: ͨ¹ý spring.cloud.nacos.config.ext-config[n].data-id µÄ·½Ê½Ö§³Ö¶à¸öÀ©Õ¹ Data Id µÄÅäÖà + +* C: ͨ¹ýÄÚ²¿Ïà¹Ø¹æÔò(Ó¦ÓÃÃû¡¢Ó¦ÓÃÃû+ Profile )×Ô¶¯Éú³ÉÏà¹ØµÄ Data Id ÅäÖà + +µ±ÈýÖÖ·½Ê½¹²Í¬Ê¹ÓÃʱ£¬ËûÃǵÄÒ»¸öÓÅÏȼ¶¹ØÏµÊÇ:A < B < C + +ÓÅÏȼ¶´Ó¸ßµ½µÍ£º + +1. nacos-config-product.yaml ¾«×¼ÅäÖà + +2. nacos-config.yaml ͬ¹¤³Ì²»Í¬»·¾³µÄͨÓÃÅäÖà + +3. ext-config: ²»Í¬¹¤³Ì À©Õ¹ÅäÖà + +4. shared-dataids ²»Í¬¹¤³ÌͨÓÃÅäÖà + +## @RefreshScope + +@Value×¢½â¿ÉÒÔ»ñÈ¡µ½ÅäÖÃÖÐÐĵÄÖµ£¬µ«ÊÇÎÞ·¨¶¯Ì¬¸ÐÖªÐ޸ĺóµÄÖµ£¬ÐèÒªÀûÓÃ@RefreshScope×¢½â + +``` +@RestController +@RefreshScope +public class TestController { + + @Value("${common.age}") + private String age; + + @GetMapping("/common") + public String hello() { + return age; + } +} +¸´ÖÆ´úÂë +``` + +## NacosÅäÖÃÖÐÐÄÔ´Âë·ÖÎö + +**ÏêϸԴÂëÁ÷³Ìͼ£º** + +[www.processon.com/view/link/6¡­](https://link.juejin.cn?target=https%3A%2F%2Fwww.processon.com%2Fview%2Flink%2F60f78ddbf346fb761bbac19d "https://www.processon.com/view/link/60f78ddbf346fb761bbac19d") + +### ÅäÖÃÖÐÐļܹ¹ + +![image-20230429084840636](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084840636.png) + +ÅäÖÃÖÐÐÄʹÓÃdemo + +``` +public class ConfigServerDemo { + + public static void main(String[] args) throws NacosException, InterruptedException { + String serverAddr = "localhost"; + String dataId = "nacos-config-demo.yaml"; + String group = "DEFAULT_GROUP"; + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + //»ñÈ¡ÅäÖ÷þÎñ + ConfigService configService = NacosFactory.createConfigService(properties); + //»ñÈ¡ÅäÖà + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); + //×¢²á¼àÌýÆ÷ + configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("===recieve:" + configInfo); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + + //·¢²¼ÅäÖà + //boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + //System.out.println(isPublishOk); + //·¢ËÍproperties¸ñʽ + configService.publishConfig(dataId,group,"common.age=30", ConfigType.PROPERTIES.getType()); + + Thread.sleep(3000); + content = configService.getConfig(dataId, group, 5000); + System.out.println(content); + +// boolean isRemoveOk = configService.removeConfig(dataId, group); +// System.out.println(isRemoveOk); +// Thread.sleep(3000); + +// content = configService.getConfig(dataId, group, 5000); +// System.out.println(content); +// Thread.sleep(300000); + } +} +¸´ÖÆ´úÂë +``` + +## nacos config clientÔ´Âë + +ÅäÖÃÖÐÐĺËÐĽӿÚConfigService + +![image-20230429084850542](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084850542.png) + +### »ñÈ¡ÅäÖà + + »ñÈ¡ÅäÖõÄÖ÷Òª·½·¨ÊÇ NacosConfigService ÀàµÄ getConfig ·½·¨£¬Í¨³£Çé¿öϸ÷½·¨Ö±½Ó´Ó±¾µØÎļþÖÐÈ¡µÃÅäÖõÄÖµ£¬Èç¹û±¾µØÎļþ²»´æÔÚ»òÕßÄÚÈÝΪ¿Õ£¬ÔòÔÙͨ¹ý HTTP GET ·½·¨´ÓÔ¶¶ËÀ­È¡ÅäÖ㬲¢±£´æµ½±¾µØ¿ìÕÕÖС£µ±Í¨¹ý HTTP »ñȡԶ¶ËÅäÖÃʱ£¬Nacos ÌṩÁËÁ½ÖÖÈ۶ϲßÂÔ£¬Ò»Êdz¬Ê±Ê±¼ä£¬¶þÊÇ×î´óÖØÊÔ´ÎÊý£¬Ä¬ÈÏÖØÊÔÈý´Î¡£ + +![image-20230429084858759](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084858759.png) + +### ×¢²á¼àÌýÆ÷ + +ÅäÖÃÖÐÐĿͻ§¶Ë»áͨ¹ý¶ÔÅäÖÃÏî×¢²á¼àÌýÆ÷´ïµ½ÔÚÅäÖÃÏî±ä¸üµÄʱºòÖ´Ðлص÷µÄ¹¦ÄÜ + +* NacosConfigService#getConfigAndSignListener + +* ConfigService#addListener + + Nacos ¿ÉÒÔͨ¹ýÒÔÉÏ·½Ê½×¢²á¼àÌýÆ÷£¬ËüÃÇÄÚ²¿µÄʵÏÖ¾ùÊǵ÷Óà ClientWorker ÀàµÄ addCacheDataIfAbsent¡£ÆäÖÐ CacheData ÊÇÒ»¸öά»¤ÅäÖÃÏîºÍÆäÏÂ×¢²áµÄËùÓмàÌýÆ÷µÄʵÀý£¬ËùÓÐµÄ CacheData ¶¼±£´æÔÚ ClientWorker ÀàÖеÄÔ­×Ó cacheMap ÖУ¬ÆäÄÚ²¿µÄºËÐijÉÔ±ÓУº + +![image-20230429084908207](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084908207.png) + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f14749bd9b614b21a55a261187cd5521~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp) + +### ÅäÖó¤ÂÖѯ + + ClientWorker ͨ¹ýÆäϵÄÁ½¸öÏ̳߳ØÍê³ÉÅäÖó¤ÂÖѯµÄ¹¤×÷£¬Ò»¸öÊǵ¥Ïß³ÌµÄ executor£¬Ã¿¸ô 10ms °´ÕÕÿ 3000 ¸öÅäÖÃÏîΪһÅú´ÎÀÌÈ¡´ýÂÖѯµÄ cacheData ʵÀý£¬½«Æä°ü×°³ÉΪһ¸ö LongPollingTask Ìá½»½øÈëµÚ¶þ¸öÏß³Ì³Ø executorService ´¦Àí¡£ + +![image-20230429084917535](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084917535.png) + +## nacos config serverÔ´Âë·ÖÎö + +### ÅäÖÃdump + +![image-20230429084926987](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084926987.png) + + ·þÎñ¶ËÆô¶¯Ê±¾Í»áÒÀÀµ DumpService µÄ init ·½·¨£¬´ÓÊý¾Ý¿âÖÐ load ÅäÖô洢ÔÚ±¾µØ´ÅÅÌÉÏ£¬²¢½«Ò»Ð©ÖØÒªµÄÔªÐÅÏ¢ÀýÈç MD5 Öµ»º´æÔÚÄÚ´æÖС£·þÎñ¶Ë»á¸ù¾ÝÐÄÌøÎļþÖб£´æµÄ×îºóÒ»´ÎÐÄÌøÊ±¼ä£¬À´Åжϵ½µ×ÊÇ´ÓÊý¾Ý¿â dump È«Á¿ÅäÖÃÊý¾Ý»¹ÊDz¿·ÖÔöÁ¿ÅäÖÃÊý¾Ý£¨Èç¹û»úÆ÷ÉÏ´ÎÐÄÌø¼ä¸ôÊÇ 6h ÒÔÄڵϰ£©¡£ + +È«Á¿ dump µ±È»ÏÈÇå¿Õ´ÅÅÌ»º´æ£¬È»ºó¸ù¾ÝÖ÷¼ü ID ÿ´ÎÀÌȡһǧÌõÅäÖÃË¢½ø´ÅÅ̺ÍÄÚ´æ¡£ÔöÁ¿ dump ¾ÍÊÇÀÌÈ¡×î½üÁùСʱµÄÐÂÔöÅäÖ㨰üÀ¨¸üеĺÍɾ³ýµÄ£©£¬ÏȰ´ÕÕÕâÅúÊý¾ÝË¢ÐÂÒ»±éÄÚ´æºÍÎļþ£¬ÔÙ¸ù¾ÝÄÚ´æÀïËùÓеÄÊý¾ÝÈ«Á¿È¥±È¶ÔÒ»±éÊý¾Ý¿â£¬Èç¹ûÓиıäµÄÔÙͬ²½Ò»´Î£¬Ïà±ÈÓÚÈ«Á¿ dump µÄ»°»á¼õÉÙÒ»¶¨µÄÊý¾Ý¿â IO ºÍ´ÅÅÌ IO ´ÎÊý¡£ + +### ÅäÖ÷¢²¼ + + ·¢²¼ÅäÖõĴúÂëλÓÚ ConfigController#publishConfigÖС£¼¯Èº²¿Êð£¬ÇëÇóÒ»¿ªÊ¼Ò²Ö»»á´òµ½Ò»Ì¨»úÆ÷£¬Õą̂»úÆ÷½«ÅäÖòåÈëMysqlÖнøÐг־û¯¡£·þÎñ¶Ë²¢²»ÊÇÕë¶Ôÿ´ÎÅäÖòéѯ¶¼È¥·ÃÎÊ MySQL £¬¶øÊÇ»áÒÀÀµ dump ¹¦ÄÜÔÚ±¾µØÎļþÖн«ÅäÖûº´æÆðÀ´¡£Òò´Ëµ±µ¥Ì¨»úÆ÷±£´æÍê±ÏÅäÖÃÖ®ºó£¬ÐèҪ֪ͨÆäËû»úÆ÷Ë¢ÐÂÄÚ´æºÍ±¾µØ´ÅÅÌÖеÄÎļþÄÚÈÝ£¬Òò´ËËü»á·¢²¼Ò»¸öÃûΪ ConfigDataChangeEvent µÄʼþ£¬Õâ¸öʼþ»áͨ¹ý HTTP µ÷ÓÃ֪ͨËùÓм¯Èº½Úµã£¨°üÀ¨×ÔÉí£©£¬´¥·¢±¾µØÎļþºÍÄÚ´æµÄˢС£ + +### ´¦Àí³¤ÂÖѯ + + ¿Í»§¶Ë»áÓÐÒ»¸ö³¤ÂÖѯÈÎÎñ£¬À­È¡·þÎñ¶ËµÄÅäÖñä¸ü£¬·þÎñ¶Ë´¦ÀíÂß¼­ÔÚLongPollingServiceÀàÖУ¬ÆäÖÐÓÐÒ»¸ö Runnable ÈÎÎñÃûΪClientLongPolling£¬·þÎñ¶Ë»á½«Êܵ½µÄÂÖѯÇëÇó°ü×°³ÉÒ»¸ö ClientLongPolling ÈÎÎñ£¬¸ÃÈÎÎñ³ÖÓÐÒ»¸ö AsyncContext ÏìÓ¦¶ÔÏó£¬Í¨¹ý¶¨Ê±Ï̳߳ØÑÓºó 29.5s Ö´ÐС£±È¿Í»§¶Ë 30s µÄ³¬Ê±Ê±¼äÌáǰ 500ms ·µ»ØÊÇΪÁË×î´ó³Ì¶ÈÉϱ£Ö¤¿Í»§¶Ë²»»áÒòÎªÍøÂçÑÓʱÔì³É³¬Ê±. + +![image-20230429084934117](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230429084934117.png) + + + + + + + + + +×÷ÕߣºÕþ²ÉÔÆµç×ÓÂô³¡ÍÅ¶Ó +Á´½Ó£ºhttps://juejin.cn/post/6999814668390760484 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..9be0e22 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRocketMQ\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,534 @@ +### Ò»¡¢NameServerÆô¶¯ + +Ô´ÂëÈë¿Ú£ºNamesrvStartup#main + +##### 1.NamesrvController controller = createNamesrvController(args); + +* ¼ì²âÃüÁîÐвÎÊý +* ´´½¨ºËÐÄÅäÖöÔÏó£¬NamesrvConfig¡¢NettyServerConfig +* ½âÎö -c ¡¢-p²ÎÊý +* ¼ì²éRocketMQ_HOME»·¾³±äÁ¿ +* final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);´´½¨controller +* controller.getConfiguration().registerConfig(properties); ×¢²áËùÓÐÅäÖÃÐÅÏ¢ + +##### 2.start(controller); + +* controller.initialize()£» Ö´Ðгõʼ»¯ + ¡ð this.kvConfigManager.load(); ¼ÓÔØKVÅäÖà + ¡ð this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);´´½¨NettyServerÍøÂç´¦Àí¶ÔÏó + ¡ð this.remotingExecutor =Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_")); ´´½¨Netty·þÎñÆ÷¹¤×÷Ïß³Ì³Ø + ¡ð this.registerProcessor(); ×¢²áNameServerµÄProcessor ×¢²áµ½RemotingServerÖÐ + ¡ð NamesrvController.this.routeInfoManager.scanNotActiveBroker() Æô¶¯¶¨Ê±ÈÎÎñ£¬ÒƳý²»»îÔ¾µÄBroker + ¡ð NamesrvController.this.kvConfigManager.printAllPeriodically() ¶¨Ê±´òÓ¡KVÅäÖÃÐÅÏ¢ +* Runtime.getRuntime().addShutdownHook ×¢²á¹Ø±Õ¹³×Ó£¬ÔڹرշþÎñʱÊÍ·Å×ÊÔ´ +* controller.start()£» Æô¶¯controller + +NameServerµÄ×÷ÓÃÖ÷ÒªÓÐÁ½¸ö£º +1.ά»¤brokerµÄ·þÎñµØÖ·ÐÅÏ¢£¬²¢½øÐиüР+2.¸øProducer¡¢consumerÌṩBrokerµÄ·þÎñÁбí + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3245844-46ca880d83fb583b.png) + + + +image.png + + + +### ¶þ¡¢BrokerÆô¶¯ + +Ô´ÂëÈë¿Ú£ºBrokerstartup#main + +##### 1.createBrokerController(args) + +* ¹¹½¨ËĸöºËÐÄÅäÖöÔÏó£ºBrokerConfig¡¢NettyServerConfig¡¢NettyClientConfig¡¢MessageStoreConfig +* BrokerConfigÖ»½âÎö -c²ÎÊý +* RocketMq_HOME»·¾³±äÁ¿¼ì²é +* RemotingUtil.string2SocketAddress(addr) ½«namesrvAddrµØÖ·½øÐвð·Ö +* messageStoreConfig.getBrokerRole() ͨ¹ýBrokerIdÅжÏÖ÷´Ó£ºmasterId=0£¬Deldger¼¯ÈºµÄËùÓÐBroker½ÚµãID¶¼ÊÇ-1 +* ½âÎö -p¡¢-m²ÎÊý£¬²¢½«½âÎöµÄ²ÎÊýÌí¼Óµ½ËĸöºËÐÄÅäÖöÔÏóÖÐ +* BrokerController controller = new BrokerController ´´½¨brokerController£¬½«ËĸöºËÐÄÅäÖÃÀà´«Èë +* controller.getConfiguration().registerConfig(properties); ÖØÐÂ×¢²á£¨¸üУ©ÅäÖà +* controller.initialize(); ³õʼ»¯controller + ¡ð ¼ÓÔØ´ÅÅÌÉϵÄÅäÖÃÎļþ£ºtopicConfigManager¡¢consumerOffsetManager¡¢subscriptionGroupManager¡¢consumerFilterManager + ¡ð this.messageStore =new DefaultMessageStore() ¹¹½¨ÏûÏ¢´æ´¢×é¼þ + ¡ð this.messageStore.load() ¼ÓÔØ´ÅÅÌÎļþ + ¡ð this.remotingServer = new NettyRemotingServer ¹¹½¨NettyÍøÂç×é¼þ + ¡ð this.fastRemotingServer = new NettyRemotingServer Õâ¸öfastRemotingServerÓëRemotingServer¹¦ÄÜ»ù±¾²î²»¶à£¬´¦ÀíVIP¶Ë¿ÚÇëÇó + ¡ð ºóÃæ¾ÍÊdzõʼ»¯Ò»Ð©Ïß³Ì³Ø + ¡ð this.registerProcessor(); broker×¢²áһЩProcessor´¦Àí·½·¨ +* Runtime.getRuntime().addShutdownHook ×¢²á¹Ø±Õ¹³×Ó + +##### 2.start(BrokerController controller) + +* this.messageStore.start(); ÕâÀïÆô¶¯·þÎñÖ÷ÒªÊÇΪÁ˽«CommitLogµÄдÈëʼþ·Ö·¢¸øComsumeQueueºÍIndexFile +* Æô¶¯Á½¸öNetty·þÎñ£ºremotingServer¡¢fastRemotingServer +* this.fileWatchService.start(); Îļþ¼àÌý·þÎñ +* this.brokerOuterAPI.start(); brokerOuterAPI¿ÉÒÔÀí½âΪһ¸öNetty¿Í»§¶Ë£¬ÍùÍâ·¢ÇëÇóµÄ×é¼þ£¬ÀýÈç·¢ËÍÐÄÌø +* this.pullRequestHoldService.start(); ³¤ÂÖѯÇëÇóÔÝÍ£·þÎñ +* this.filterServerManager.start(); ʹÓÃfilter½øÐйýÂË +* BrokerController.this.registerBrokerAll() BrokerºËÐĵÄÐÄÌø×¢²áÈÎÎñ,Ö÷Òª×÷ÓþÍÊǽ«broker×¢²áµ½NamesrvÖÐ + +brokerµÄºËÐÄ×÷Ó㺠+**1.×÷Ϊclientʱ£¬ÏònameServer·¢ËÍÐÄÌøÐÅÏ¢¡¢·¢ÆðÊÂÎñµÄ״̬¼ì²é** +**2.×÷Ϊ·þÎñ¶Ëʱ£¬ÓÃÓÚ´æ´¢ÏûÏ¢¡¢ÏìÓ¦consumer¶ËµÄÇëÇó** + +### Èý¡¢Netty·þÎñ×¢²á¿ò¼Ü + +### ËÄ¡¢BrokerÐÄÌø×¢²á¹ý³Ì + +Ô´ÂëÈë¿Ú£ºBrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()) + + + +``` +public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway, boolean forceRegister) { + TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper(); + + if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission()) + || !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) { + ConcurrentHashMap topicConfigTable = new ConcurrentHashMap(); + for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) { + TopicConfig tmp = + new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(), + this.brokerConfig.getBrokerPermission()); + topicConfigTable.put(topicConfig.getTopicName(), tmp); + } + topicConfigWrapper.setTopicConfigTable(topicConfigTable); + } + //ÕâÀï²ÅÊDZȽϹؼüµÄµØ·½¡£ÏÈÅжÏÊÇ·ñÐèҪע²á£¬È»ºóµ÷ÓÃdoRegisterBrokerAll·½·¨ÕæÕýÈ¥×¢²á¡£ + if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(), + this.getBrokerAddr(), + this.brokerConfig.getBrokerName(), + this.brokerConfig.getBrokerId(), + this.brokerConfig.getRegisterBrokerTimeoutMills())) { + doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper); + } +} + +``` + + + + + +``` +// Broker×¢²á×îºËÐĵIJ¿·Ö +private void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway, + TopicConfigSerializeWrapper topicConfigWrapper) { + // ×¢²ábroker·½·¨ + List registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll( + this.brokerConfig.getBrokerClusterName(), + this.getBrokerAddr(), + this.brokerConfig.getBrokerName(), + this.brokerConfig.getBrokerId(), + this.getHAServerAddr(), + topicConfigWrapper, + this.filterServerManager.buildNewFilterServerList(), + oneway, + this.brokerConfig.getRegisterBrokerTimeoutMills(), + this.brokerConfig.isCompressedRegister()); + + if (registerBrokerResultList.size() > 0) { + RegisterBrokerResult registerBrokerResult = registerBrokerResultList.get(0); + if (registerBrokerResult != null) { + //×¢²áÍê±£´æÖ÷´Ó½ÚµãµÄµØÖ· + if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) { + this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr()); + } + + this.slaveSynchronize.setMasterAddr(registerBrokerResult.getMasterAddr()); + + if (checkOrderConfig) { + this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable()); + } + } + } +} + +``` + + + + + +``` +public List registerBrokerAll( + final String clusterName, + final String brokerAddr, + final String brokerName, + final long brokerId, + final String haServerAddr, + final TopicConfigSerializeWrapper topicConfigWrapper, + final List filterServerList, + final boolean oneway, + final int timeoutMills, + final boolean compressed) { + //ʹÓÃCopyOnWriteArrayListÌáÉý²¢·¢°²È«ÐÔ + final List registerBrokerResultList = new CopyOnWriteArrayList<>(); + // »ñÈ¡ËùÓÐnameServerµÄµØÖ·ÐÅÏ¢ + List nameServerAddressList = this.remotingClient.getNameServerAddressList(); + if (nameServerAddressList != null && nameServerAddressList.size() > 0) { + + final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader(); + requestHeader.setBrokerAddr(brokerAddr); + requestHeader.setBrokerId(brokerId); + requestHeader.setBrokerName(brokerName); + requestHeader.setClusterName(clusterName); + requestHeader.setHaServerAddr(haServerAddr); + requestHeader.setCompressed(compressed); + + RegisterBrokerBody requestBody = new RegisterBrokerBody(); + requestBody.setTopicConfigSerializeWrapper(topicConfigWrapper); + requestBody.setFilterServerList(filterServerList); + final byte[] body = requestBody.encode(compressed); + final int bodyCrc32 = UtilAll.crc32(body); + requestHeader.setBodyCrc32(bodyCrc32); + //ͨ¹ýCountDownLatch£¬±£Ö¤ÔÚËùÓÐNameServerÉÏÍê³É×¢²áºóÔÙÒ»Æð½áÊø¡£ + final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size()); + for (final String namesrvAddr : nameServerAddressList) { + brokerOuterExecutor.execute(new Runnable() { + @Override + public void run() { + try { + RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body); + if (result != null) { + registerBrokerResultList.add(result); + } + + log.info("register broker[{}]to name server {} OK", brokerId, namesrvAddr); + } catch (Exception e) { + log.warn("registerBroker Exception, {}", namesrvAddr, e); + } finally { + countDownLatch.countDown(); + } + } + }); + } + + try { + countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + } + + return registerBrokerResultList; +} + +``` + + + +NameServer´¦ÀíÇëÇó£º + + + +``` +//NameServer´¦ÀíÇëÇóµÄºËÐÄ´úÂë +@Override + public RemotingCommand processRequest(ChannelHandlerContext ctx, + RemotingCommand request) throws RemotingCommandException { + + if (ctx != null) { + log.debug("receive request, {} {} {}", + request.getCode(), + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), + request); + } + + switch (request.getCode()) { + case RequestCode.PUT_KV_CONFIG: + return this.putKVConfig(ctx, request); + case RequestCode.GET_KV_CONFIG: + return this.getKVConfig(ctx, request); + case RequestCode.DELETE_KV_CONFIG: + return this.deleteKVConfig(ctx, request); + case RequestCode.QUERY_DATA_VERSION: + return queryBrokerTopicConfig(ctx, request); + case RequestCode.REGISTER_BROKER: //Broker×¢²áÇëÇó´¦Àí¡£°æ±¾Ä¬ÈÏÊǵ±Ç°¿ò¼Ü°æ±¾ + Version brokerVersion = MQVersion.value2Version(request.getVersion()); + if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) { + return this.registerBrokerWithFilterServer(ctx, request); //µ±Ç°°æ±¾ + } else { + return this.registerBroker(ctx, request); + } + case RequestCode.UNREGISTER_BROKER: + return this.unregisterBroker(ctx, request); + case RequestCode.GET_ROUTEINFO_BY_TOPIC: + return this.getRouteInfoByTopic(ctx, request); + case RequestCode.GET_BROKER_CLUSTER_INFO: + return this.getBrokerClusterInfo(ctx, request); + case RequestCode.WIPE_WRITE_PERM_OF_BROKER: + return this.wipeWritePermOfBroker(ctx, request); + case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER: + return getAllTopicListFromNameserver(ctx, request); + case RequestCode.DELETE_TOPIC_IN_NAMESRV: + return deleteTopicInNamesrv(ctx, request); + case RequestCode.GET_KVLIST_BY_NAMESPACE: + return this.getKVListByNamespace(ctx, request); + case RequestCode.GET_TOPICS_BY_CLUSTER: + return this.getTopicsByCluster(ctx, request); + case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS: + return this.getSystemTopicListFromNs(ctx, request); + case RequestCode.GET_UNIT_TOPIC_LIST: + return this.getUnitTopicList(ctx, request); + case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST: + return this.getHasUnitSubTopicList(ctx, request); + case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST: + return this.getHasUnitSubUnUnitTopicList(ctx, request); + case RequestCode.UPDATE_NAMESRV_CONFIG: + return this.updateConfig(ctx, request); + case RequestCode.GET_NAMESRV_CONFIG: + return this.getConfig(ctx, request); + default: + break; + } + return null; +} + +``` + + + +ʵ¼Ê¾ÍÊǽ«brokerÐÅÏ¢×¢²áµ½routeInfoÖУº + + + +``` +public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request) + throws RemotingCommandException { + final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class); + final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader(); + final RegisterBrokerRequestHeader requestHeader = + (RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class); + + if (!checksum(ctx, request, requestHeader)) { + response.setCode(ResponseCode.SYSTEM_ERROR); + response.setRemark("crc32 not match"); + return response; + } + + RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody(); + + if (request.getBody() != null) { + try { + registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed()); + } catch (Exception e) { + throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e); + } + } else { + registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0)); + registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0); + } + //routeInfoManager¾ÍÊǹÜÀí·ÓÉÐÅÏ¢µÄºËÐÄ×é¼þ¡£ + RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker( + requestHeader.getClusterName(), + requestHeader.getBrokerAddr(), + requestHeader.getBrokerName(), + requestHeader.getBrokerId(), + requestHeader.getHaServerAddr(), + registerBrokerBody.getTopicConfigSerializeWrapper(), + registerBrokerBody.getFilterServerList(), + ctx.channel()); + + responseHeader.setHaServerAddr(result.getHaServerAddr()); + responseHeader.setMasterAddr(result.getMasterAddr()); + + byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG); + response.setBody(jsonValue); + + response.setCode(ResponseCode.SUCCESS); + response.setRemark(null); + return response; +} + +``` + + + +### Îå¡¢Producer·¢ËÍÏûÏ¢ + +Ô´ÂëÈë¿Ú£ºDefaultMQProducer#start +1.this.defaultMQProducerImpl.start(); Éú²ú¶ËÆô¶¯ + + + +``` +public void start(final boolean startFactory) throws MQClientException { + switch (this.serviceState) { + case CREATE_JUST: + // ĬÈϾÍÊÇCREATE_JUST + this.serviceState = ServiceState.START_FAILED; + + this.checkConfig(); + //Ð޸ĵ±Ç°µÄinstanceNameΪµ±Ç°½ø³ÌID + if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) { + this.defaultMQProducer.changeInstanceNameToPID(); + } + //¿Í»§¶ËºËÐĵÄMQ¿Í»§¶Ë¹¤³§ ¶ÔÓÚÊÂÎñÏûÏ¢·¢ËÍÕߣ¬ÔÚÕâÀïÃæ»áÍê³ÉÊÂÎñÏûÏ¢µÄ·¢ËÍÕߵķþÎñ×¢²á + this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook); + //×¢²áMQ¿Í»§¶Ë¹¤³§Ê¾Àý + boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this); + if (!registerOK) { + this.serviceState = ServiceState.CREATE_JUST; + throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup() + + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), + null); + } + + this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo()); + //Æô¶¯Ê¾Àý --ËùÓпͻ§¶Ë×é¼þ¶¼½»ÓÉmQClientFactoryÆô¶¯ + if (startFactory) { + mQClientFactory.start(); + } + + log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(), + this.defaultMQProducer.isSendMessageWithVIPChannel()); + this.serviceState = ServiceState.RUNNING; + break; + case RUNNING: + case START_FAILED: + case SHUTDOWN_ALREADY: + throw new MQClientException("The producer service state not OK, maybe started once, " + + this.serviceState + + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK), + null); + default: + break; + } + // ÏòËùÓеÄbroker·¢ËÍÐÄÌø + this.mQClientFactory.sendHeartbeatToAllBrokerWithLock(); + + this.startScheduledTask(); + +} + +``` + + + +### Áù¡¢ConsumerÏû·ÑÏûÏ¢ + +Ïû·Ñ¶ËÈë¿Ú£ºDefaultMQPushConsumer#start +this.defaultMQPushConsumerImpl.start(); + + + +``` +public synchronized void start() throws MQClientException { + switch (this.serviceState) { + case CREATE_JUST: + log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(), + this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode()); + this.serviceState = ServiceState.START_FAILED; + + this.checkConfig(); + + this.copySubscription(); + + if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) { + this.defaultMQPushConsumer.changeInstanceNameToPID(); + } + //¿Í»§¶ËʾÀý¹¤³§£¬Éú²úÕßÒ²Êǽ»ÓÉÕâ¸ö¹¤³§Æô¶¯µÄ¡£ + this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook); + //¸ºÔؾùºâ²ßÂÔ + this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup()); + this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel()); + this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy()); + this.rebalanceImpl.setmQClientFactory(this.mQClientFactory); + + this.pullAPIWrapper = new PullAPIWrapper( + mQClientFactory, + this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode()); + this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList); + + if (this.defaultMQPushConsumer.getOffsetStore() != null) { + this.offsetStore = this.defaultMQPushConsumer.getOffsetStore(); + } else { + //´ÓÕâÀï¿ÉÒÔ¿´³ö£¬¹ã²¥Ä£Ê½Ó뼯ȺģʽµÄ×î±¾ÖÊÇø±ð¾ÍÊÇoffset´æ´¢µÄµØ·½²»Ò»Ñù¡£ + switch (this.defaultMQPushConsumer.getMessageModel()) { + //¹ã²¥Ä£Ê½ÊÇÔÚÏû·ÑÕß±¾µØ´æ´¢offset + case BROADCASTING: + this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup()); + break; + //¼¯ÈºÄ£Ê½ÊÇÔÚBrokerÔ¶¶Ë´æ´¢offset + case CLUSTERING: + this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup()); + break; + default: + break; + } + this.defaultMQPushConsumer.setOffsetStore(this.offsetStore); + } + this.offsetStore.load(); + //˳ÐòÏû·Ñ¼àÌý´´½¨ConsumeMessageOrderlyService + if (this.getMessageListenerInner() instanceof MessageListenerOrderly) { + this.consumeOrderly = true; + this.consumeMessageService = + new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner()); + //²¢·¢Ïû·Ñ¼àÌý´´½¨ConsumeMessageConcurrentlyService + } else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) { + this.consumeOrderly = false; + this.consumeMessageService = + new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner()); + } + + this.consumeMessageService.start(); + //×¢²áÏû·ÑÕß¡£ÓëÉú²úÕßÀàËÆ£¬¿Í»§¶ËÖ»Òª°´ÒªÇó×¢²á¼´¿É£¬ºóÐø»áËæmQClientFactoryÒ»ÆðÆô¶¯¡£ + boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this); + if (!registerOK) { + this.serviceState = ServiceState.CREATE_JUST; + this.consumeMessageService.shutdown(defaultMQPushConsumer.getAwaitTerminationMillisWhenShutdown()); + throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup() + + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), + null); + } + + mQClientFactory.start(); + log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup()); + this.serviceState = ServiceState.RUNNING; + break; + case RUNNING: + case START_FAILED: + case SHUTDOWN_ALREADY: + throw new MQClientException("The PushConsumer service state not OK, maybe started once, " + + this.serviceState + + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),null); + default: + break; + } + + this.updateTopicSubscribeInfoWhenSubscriptionChanged(); + this.mQClientFactory.checkClientInBroker(); + this.mQClientFactory.sendHeartbeatToAllBrokerWithLock(); + this.mQClientFactory.rebalanceImmediately(); + } + +``` + + + +**1¡¢consumer¶ËµÄÏû·Ñģʽ£º** +¡ñ ¼¯ÈºÄ£Ê½£º¼¯ÈºÄ£Ê½ÏÂÿ¸öconsumer¶¼»á·ÖÅ䲻ͬµÄÏûÏ¢ +¡ñ ¹ã²¥Ä£Ê½£º¹ã²¥Ä£Ê½ÏÂÿ¸öÏûÏ¢¶¼ÍÆË͸øËùÓÐconsumer +**2¡¢¹ØÓÚoffset´æ´¢£º** +¡ñ ¹ã²¥Ä£Ê½£ºthis.offsetStore = new LocalFileOffsetStore(); ´æ´¢ÔÚÿ¸öconsumerÖÐ +¡ñ ¼¯ÈºÄ£Ê½£ºthis.offsetStore = new RemoteBrokerOffsetStore(); ´æ´¢ÔÚbroker¶Ë + + + +×÷Õߣº·ãÒ¶ºì»¨ +Á´½Ó£ºhttps://www.jianshu.com/p/8dd4cfeae39d +À´Ô´£º¼òÊé +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..19ce963 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSeata\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1005 @@ +×÷Õß | Ò»ÆðߣJava +À´Ô´ |½ñÈÕÍ·Ìõ + +**ѧϰĿ±ê** + +* Seata ATģʽԴÂëÁ÷³Ì + **µÚ1Õ ATģʽÁ÷³Ì** + **1.1 ˼άÁ÷³ÌÍÆµ¼** + ÉÏÎÄÖÐÒѾ­½²ÁËATģʽµÄ´óÌåÔ­Àí£¬ÔÚÔ´ÂëÖУ¬Í¨¹ýREADMEÒ²ÄÜ¿´³öÀ´ATģʽµÄʹÓã¬ÄDZ¾ÎĽ«´Óµ×²ãÔ´Âë²ãÃæÈ¥·ÖÎöATģʽµÄÔ­Àí£¬ÔÚ·ÖÎöÔ­Àí֮ǰÔÛÃÇÏÈÀ´¿´Èý·ùͼ£¬Àí½âÒ»ÏÂËûµÄ¹¤×÷˼·ºÍģʽ£º + +ÏÈ¿´¿´Ë¼Î¬ÍƵ¼Í¼![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0388859932ccee17ed32246e2e5f4e0a88dee6.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")1.2 ³õʼ»¯Á÷³ÌÍÆµ¼ +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f24e8bd05e84b683e3f3227490146155bf5b17.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")1.3 Ö´ÐÐÁ÷³ÌÍÆµ¼ +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/93dea0538eba8be5778699ea2af8f71c0e396c.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")**µÚ2Õ ԴÂë·ÖÎö** +**2.1 SeataAutoConfiguration** +¶ÔÓÚseataÔ´ÂëµÄÑо¿Ö÷Òª¿´seataÈçºÎÀ¹½ØÒµÎñSQLÉú³Éundo_logÊý¾Ý£¬ÈçºÎÔÚÒ»½×¶ÎÍê³ÉºóÌύȫ¾ÖÊÂÎñ£¬ÈçºÎÔÚÒ»½×¶ÎÒµÎñʧ°Üºóͨ¹ýundo_log»Ø¹öÊÂÎñ£¬½øÐÐÊÂÎñ²¹³¥¡£ + +seataÒ²ÊÇÓëspringÕûºÏʹÓõ쬽áºÏSpringBoot£¬seataÒ²ÊÇ×öÁËһЩ×Ô¶¯ÅäÖÃ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/719ef4a775ece0688c929227415e97f68f1c58.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")seataµÄ×Ô¶¯ÅäÖÃÀàÃüÃû·Ç³£µÄÖ±½Ó£¬¾Í½Ð×ö£ºSeataAutoConfiguration£¬ÎÒÃÇ´ò¿ªÕâ¸öÀà + + + +``` +@ComponentScan(basePackages = "io.seata.spring.boot.autoconfigure.properties")@ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)@Configuration@EnableConfigurationProperties({SeataProperties.class})public class SeataAutoConfiguration { } +``` + + + + + + + + + +Ê×ÏÈ£¬@ComponentScanɨÃèÁËÒ»ÏÂproperties°ü£¬¼ÓÔØÁËÒ»´ó¶ÑÀàËÆSeataPropertiesµÄBean¶ÔÏó¡£ + +@ConditionalOnProperty½«ÅäÖÃÀàÉúЧÌõ¼þÉèÖÃΪseata.enabled=true£¬Ä¬ÈÏÖµÊÇtrue£¬ËùÒÔ¿ÉÒÔ¿ª¹Ø·Ö²¼Ê½ÊÂÎñ¹¦ÄÜ£¨ÔÚclient¶ËµÄfile.confÀïÃæ¿ÉÒÔÅäÖã©¡£ + +@Configuration±íÃ÷£¬SeataAutoConfiguration±»¶¨ÒåΪÁËspringµÄÅäÖÃÀà¡£ + +@EnableConfigurationProperties½«ÅäÖðüת³ÉÁËÒ»¸öSeataPropertiesµÄBean¶ÔÏóÀ´Ê¹Óᣠ+ +½ÓÏÂÀ´ÔĶÁSeataAutoConfigurationµÄÄÚ²¿´úÂë + + + +``` +@Bean@DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})@ConditionalOnMissingBean(GlobalTransactionScanner.class)public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Automatically configure Seata"); } return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);} +``` + + + + + + + + + +×Ô¶¯ÅäÖõĺËÐĵãÂäÔÚÁËÏÂÃæµÄÒ»¸öBean£¬GlobalTransactionScanner¡£ + +ÎÒÃÇ¿´µ½¹¹ÔìÕâ¸öBean·Ç³£µÄ¼òµ¥£¬¹¹Ôì·½·¨Ö»ÐèÒªÒ»¸öapplicationIdºÍtxServiceGroup¡£ + +applicationId: ¾ÍÊÇspring.application.name=Ä㶨ÒåµÄµ±Ç°Ó¦ÓõÄÃû×Ö£¬ÀýÈ磺userService + +txServiceGroup: ¾ÍÊÇÒÔapplicationId ¼ÓÉÏ -seata-service-groupÃüÃûµÄ£¬ÀýÈ磺 +userService-seata-service-group¡£Èç¹û°æ±¾½ÏµÍµÄ»°£¬ÄÇʱºò¿ÉÄÜ»¹²»½Ðseata¶øÊÇfescar£¬Òò´ËĬÈÏÃüÃû¾ÍÊÇÒÔfescarΪºó׺¡£ + +newÁËÒ»¸öGlobalTransactionScanner¶ÔÏó£¬SeataAutoConfigurationÕâ¸ö×Ô¶¯ÅäÖÃÀàµÄ×÷ÓþͽáÊøÁË¡£SeataAutoConfigurationÖ»ÊÇ×öÁËÒ»¸öÆô¶¯Òýµ¼µÄ×÷Óᣠ+ +**2.2 GlobalTransactionScanner** +¼ÈÈ»ºËÐĵãÂäÔÚGlobalTransactionScannerÕâ¸öÀ࣬ÎÒÃǼÌÐø¹Ø×¢Ëü¡£¿´Õâ¸öÃû×ÖÆäʵ¾Í¿ÉÒԲ²⵽һµãËüµÄ×÷Óã¬É¨Ãè@GlobalTransactionalÕâ¸ö×¢½â£¬²¢¶Ô´úÀí·½·¨½øÐÐÀ¹½ØÔöÇ¿ÊÂÎñµÄ¹¦ÄÜ¡£ + +ÒªÁ˽âÕâ¸öÀ࣬²»µÃ²»ÏÈÔĶÁÒ»ÏÂËüµÄUMLͼ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/83cb3ed76073a33cb19242ffe542b615346d16.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")¿ÉÒÔ¿´µ½£¬GlobalTransactionScannerÖ÷ÒªÓÐ4¸öµãÖµµÃ¹Ø×¢£º + +1£©ApplicationContextAware±íʾ¿ÉÒÔÄõ½springÈÝÆ÷ + +2£©InitializingBean½Ó¿Ú£¬±í´ïÁ˳õʼ»¯µÄʱºò»á½øÐÐһЩ²Ù×÷ + +3£©AbstractAutoProxyCreator±íʾËü»á¶ÔspringÈÝÆ÷ÖеÄBean½øÐÐÇÐÃæÔöÇ¿£¬Ò²¾ÍÊÇÎÒÃÇÉÏÃæµÄÀ¹½ØÊÂÎñÔöÇ¿µÄ²Â²â¡£ + +4£©Disposable½Ó¿Ú£¬±í´ïÁËspringÈÝÆ÷Ïú»ÙµÄʱºò»á½øÐÐһЩ²Ù×÷ + +ÕâÀïÎÒÃÇÉÔ΢¹Ø×¢Ò»ÏÂÕâ4¸öµÄÖ´ÐÐ˳Ðò£º + +ApplicationContextAware -> InitializingBean -> AbstractAutoProxyCreator -> DisposableBean + +**2.3 InitializingBean** + + + +``` +@Overridepublic void afterPropertiesSet() { if (disableGlobalTransaction) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Global transaction is disabled."); } return; } initClient();} +``` + + + + + + + + + +³õʼ»¯SeataµÄClient¶ËµÄ¶«Î÷£¬Client¶ËÖ÷Òª°üÀ¨TransactionManagerºÍResourceManager¡£»òÐíÊÇΪÁ˼ò»¯°É£¬²¢Ã»ÓаÑinitClientÕâ¼þÊ´ÓGlobalTransactionScannerÀïÃæ¶ÀÁ¢³öÀ´Ò»¸öÀà¡£ + +¸ú½øinitClient·½·¨ + + + +``` +private void initClient() { //init TM TMClient.init(applicationId, txServiceGroup); //init RM RMClient.init(applicationId, txServiceGroup); registerSpringShutdownHook();} +``` + + + + + + + + + +initClientÂß¼­²¢²»¸´ÔÓ£¬µ¥´¿µ÷ÓÃTMClient.init³õʼ»¯TransactionManagerµÄRPC¿Í»§¶Ë£¬RMClient.init³õʼ»¯ResourceManagerµÄRPC¿Í»§¶Ë¡£seataµÄRPC²ÉÓÃnettyÀ´ÊµÏÖ£¬seata·â×°¼ò»¯ÁËÒ»ÏÂʹÓᣲ¢×¢²áÁËÒ»¸öSpringµÄShutdownHook¹³×Óº¯Êý + +**2.3.1 TMClient³õʼ»¯** + + + +``` +@Overridepublic void init() { timerExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { clientChannelManager.reconnect(getTransactionServiceGroup()); } }, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);...} +``` + + + + + + + + + +Æô¶¯ÁËÒ»¸ö¶¨Ê±Æ÷²»¶Ï½øÐÐÖØÁ¬²Ù×÷£¬µ÷Óà +clientChannelManager.reconnect·½·¨½øÐÐÖØÁ¬ + + + +``` +void reconnect(String transactionServiceGroup) { List availList = null; try { availList = getAvailServerList(transactionServiceGroup); } catch (Exception e) { ... } ... for (String serverAddress : availList) { try { acquireChannel(serverAddress); } catch (Exception e) { ... } }} +``` + + + + + + + + + +¸ù¾ÝtransactionServiceGroup»ñÈ¡seata-serverµÄipµØÖ·ÁÐ±í£¬È»ºó½øÐÐÖØÁ¬ + + + +``` +private List getAvailServerList(String transactionServiceGroup) throws Exception { List availInetSocketAddressList = RegistryFactory.getInstance() .lookup(transactionServiceGroup); if (CollectionUtils.isEmpty(availInetSocketAddressList)) { return Collections.emptyList(); } return availInetSocketAddressList.stream() .map(NetUtil::toStringAddress) .collect(Collectors.toList());} +``` + + + + + + + + + +RegistryFactory.getInstance().lookup(transactionServiceGroup);ÊǶԲ»Í¬×¢²áÖÐÐÄ×öÁËÊÊÅäµÄ£¬Ä¬ÈÏ¿´ÏÂNacosÐÎʽµÄʵÏÖ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/745b802128d80980a6e082f91406451526cbb3.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")Ïȸù¾ÝÊÂÎñ·Ö×éÕÒµ½·Ö×éËùÊôµÄserver¼¯ÈºÃû³Æ£¬ÕâÀïÊÇdefault£¬È»ºó¸ù¾Ý¼¯ÈºÃû³ÆÕÒµ½server¶ÔÓ¦ip¶Ë¿ÚµØÖ· + + + +``` +@Overridepublic List lookup(String key) throws Exception { //default String clusterName = getServiceGroup(key); if (clusterName == null) { return null; } if (!LISTENER_SERVICE_MAP.containsKey(clusterName)) { synchronized (LOCK_OBJ) { if (!LISTENER_SERVICE_MAP.containsKey(clusterName)) { List clusters = new ArrayList<>(); clusters.add(clusterName); List firstAllInstances = getNamingInstance().getAllInstances(getServiceName(), getServiceGroup(), clusters); if (null != firstAllInstances) { List newAddressList = firstAllInstances.stream() .filter(instance -> instance.isEnabled() && instance.isHealthy()) .map(instance -> new InetSocketAddress(instance.getIp(), instance.getPort())) .collect(Collectors.toList()); CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList); } subscribe(clusterName, event -> { List instances = ((NamingEvent) event).getInstances(); if (null == instances && null != CLUSTER_ADDRESS_MAP.get(clusterName)) { CLUSTER_ADDRESS_MAP.remove(clusterName); } else if (!CollectionUtils.isEmpty(instances)) { List newAddressList = instances.stream() .filter(instance -> instance.isEnabled() && instance.isHealthy()) .map(instance -> new InetSocketAddress(instance.getIp(), instance.getPort())) .collect(Collectors.toList()); CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList); } }); } } } return CLUSTER_ADDRESS_MAP.get(clusterName);} +``` + + + + + + + + + +Seata-serverµÄIPµØÖ·ÒÑ»ñÈ¡µ½,È»ºóµ÷ÓÃacquireChannel + + + +``` +Channel acquireChannel(String serverAddress) { Channel channelToServer = channels.get(serverAddress); if (channelToServer != null) { channelToServer = getExistAliveChannel(channelToServer, serverAddress); if (channelToServer != null) { return channelToServer; } }... channelLocks.putIfAbsent(serverAddress, new Object()); synchronized (channelLocks.get(serverAddress)) { return doConnect(serverAddress); }} +``` + + + + + + + + + +×îºó½«»ñÈ¡µ½µÄseata-serverµÄIPµØÖ··Åµ½NettyÖзâ×°£¬TmClient¾Í³õʼ»¯Íê±Ï + +TmClient³õʼ»¯×ܽ᣺ + +* Æô¶¯¶¨Ê±Æ÷£¬³¢ÊÔ½øÐÐÒ»´ÎÖØÁ¬seata-server +* ÖØÁ¬Ê±£¬ÏÈ´Ónacos£¨»òÔòÆäËûÅäÖã©Öиù¾Ý·Ö×éÃû³Æ(service_group)ÕÒµ½¼¯ÈºÃû³Æ(cluster_name) +* ÔÙ¸ù¾Ý¼¯ÈºÃû³ÆÕÒµ½¼¯Èºip¶Ë¿ÚÁбí +* ´ÓipÁбíÖÐÑ¡ÔñÒ»¸öÓÃnetty½øÐÐÁ¬½Ó + **2.3.2 RMClient³õʼ»¯** + + + +``` +public static void init(String applicationId, String transactionServiceGroup) { // »ñÈ¡µ¥Àý¶ÔÏó RmRpcClient rmRpcClient = RmRpcClient.getInstance(applicationId, transactionServiceGroup); // ÉèÖÃResourceManagerµÄµ¥Àý¶ÔÏó rmRpcClient.setResourceManager(DefaultResourceManager.get()); // Ìí¼Ó¼àÌýÆ÷£¬¼àÌýServer¶ËµÄÏûÏ¢ÍÆËÍ rmRpcClient.setClientMessageListener(new RmMessageListener(DefaultRMHandler.get())); // ³õʼ»¯RPC rmRpcClient.init();} +``` + + + + + + + + + +ºÍTMClientÏë±È£¬RMClient¶à³öÁËÒ»¸ö¼àÌýServer¶ËÏûÏ¢²¢´¦ÀíµÄ»úÖÆ¡£Ò²¾ÍÊÇ˵TMµÄÖ°Ôð¸ü¶àµÄÊÇÖ÷¶¯ÓëServer¶ËͨÐÅ£¬±ÈÈ磺ȫ¾ÖÊÂÎñµÄbegin¡¢commit¡¢rollbackµÈ¡£ + +¶øRM³ýÁËÖ÷¶¯²Ù×÷±¾µØ×ÊÔ´Í⣬»¹»áÒòΪȫ¾ÖÊÂÎñµÄcommit¡¢rollbackµÈµÄÏûÏ¢ÍÆËÍ£¬´Ó¶ø¶Ô±¾µØ×ÊÔ´½øÐÐÏà¹Ø²Ù×÷¡£ + +ÉèÖÃ×ÊÔ´¹ÜÀíÆ÷resourceManager£¬ÉèÖÃÏûÏ¢»Øµ÷¼àÌýÆ÷ÓÃÓÚ½ÓÊÕTCÔÚ¶þ½×¶Î·¢³öµÄÌá½»»òÕ߻عöÇëÇó£¬SeataÖжÔResourceManager£¬AbstractRMHandler×öÁËSPIÊÊÅ䣬ÒÔResouceManagerΪÀý£º + + + +``` +public class DefaultResourceManager implements ResourceManager { protected void initResourceManagers() { //init all resource managers List allResourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); if (CollectionUtils.isNotEmpty(allResourceManagers)) { for (ResourceManager rm : allResourceManagers) { resourceManagers.put(rm.getBranchType(), rm); } } }} +``` + + + + + + + + + +¿ÉÒÔ¿´µ½³õʼ»¯DefaultResouceManagerʱ»áʹÓÃClassLoaderÈ¥¼ÓÔØ¶ÔÓ¦JarϵÄʵÏÖ£¬¶øÄ¬ÈÏATģʽʹÓõÄʵÏÖÊÇÊý¾Ý¿â£¬Ò²¾ÍÊÇrm-datasource°üϵÄʵÏÖ£¬ÕÒʵÏÖÀà·¾¶ÐèÒª¶¨Î»µ½/resources/META-INF/À©Õ¹½Ó¿Úȫ·¾¶È¥ÕÒ£¬¾Í»áÕÒµ½¶ÔÓ¦µÄʵÏÖÀà ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/84399f558194f3dce18275c72cb9a92219db25.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨ÉÏ£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")ResourceManager¶ÔӦʵÏÖÀàȫ·¾¶ +io.seata.rm.datasource.DataSourceManager£¬¸ÃÀàÖÐÖ¸¶¨ÁËÁËÌá½»ºÍ»Ø¹öµÄ·½·¨£¬DefaultRMHandler¶ÔӦʵÏÖÀàȫ·¾¶io.seata.rm.RMHandlerAT£¬ÊǸö½ÓÊÕserverÏûÏ¢²¢×ö¶ÔÓ¦Ìá½»»òÕ߻عö²Ù×÷µÄ»Øµ÷´¦ÀíÀà¡£ + +RMClinetµÄinit()·½·¨ÓëTMClient»ù±¾Ò»Ö + +**2.3.3 ×ܽá** + +* SpringÆô¶¯Ê±£¬³õʼ»¯ÁË2¸ö¿Í»§¶ËTmClient¡¢RmClient +* TmClientÓëseata-serverͨ¹ýNetty½¨Á¢Á¬½Ó²¢·¢ËÍÏûÏ¢ +* RmClientÓëseata-serverͨ¹ýNetty½¨Á¢Á¬½Ó£¬¸ºÔð½ÓÊÕ¶þ½×¶ÎÌá½»¡¢»Ø¹öÏûÏ¢²¢Ôڻص÷Æ÷(RmHandler)ÖÐ×ö´¦Àí + +×÷Õß | Ò»ÆðߣJava +À´Ô´ |½ñÈÕÍ·Ìõ + +**2.4 AbstractAutoProxyCreator** +GlobalTransactionScanner³õʼ»¯ÍêÁËTMºÍRMÒÔºó£¬ÎÒÃÇÔÙ¹Ø×¢Ò»ÏÂAbstractAutoProxyCreator£¬×Ô¶¯´úÀí¡£ + +×Ô¶¯´úÀí£¬Ëü´úÀíɶ¶«Î÷ÄØ£¿»òÕß˵Ëü¸øspringÖеÄBeanÔöÇ¿ÁËʲô¹¦ÄÜ£¿ + +GlobalTransactionScannerÖ÷ÒªÀ©Õ¹ÁËAbstractAutoProxyCreatorµÄwrapIfNecessary + +´úÀíÔöÇ¿µÄǰÖÃÅжϴ¦Àí£¬±íʾÊÇ·ñ¸ÃBeanÐèÒªÔöÇ¿£¬Èç¹ûÔöÇ¿µÄ»°´´½¨´úÀíÀà + +**2.4.1 wrapIfNecessary** + + + +``` +@Overrideprotected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (disableGlobalTransaction) { return bean; } try { synchronized (PROXYED_SET) { // ÏàͬBeanÅÅÖØ if (PROXYED_SET.contains(beanName)) { return bean; } interceptor = null; // ÅжÏÊÇ·ñ¿ªÆôTCCģʽ if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) { // TCCʵÏÖµÄÀ¹½ØÆ÷ interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName)); } else { Class serviceInterface = SpringProxyUtils.findTargetClass(bean); Class[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean); // ÅжÏÊÇ·ñ´æÔÚ@GlobalTransactional»òÕß@GlobalLock×¢½â if (!existsAnnotation(new Class[]{serviceInterface}) && !existsAnnotation(interfacesIfJdk)) { return bean; } if (interceptor == null) { // ·ÇTCCµÄÀ¹½ØÆ÷ if (globalTransactionalInterceptor == null) { globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook); ConfigurationCache.addConfigListener( ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)globalTransactionalInterceptor); } interceptor = globalTransactionalInterceptor; } } // Åжϵ±Ç°BeanÊÇ·ñÒѾ­ÊÇspringµÄ´úÀíÀàÁË if (!AopUtils.isAopProxy(bean)) { // Èç¹û»¹²»ÊÇ£¬ÄÇô×ßÒ»ÂÖspringµÄ´úÀí¹ý³Ì¼´¿É bean = super.wrapIfNecessary(bean, beanName, cacheKey); } else { // Èç¹ûÊÇÒ»¸öspringµÄ´úÀíÀ࣬ÄÇô·´Éä»ñÈ¡´úÀíÀàÖÐÒѾ­´æÔÚµÄÀ¹½ØÆ÷¼¯ºÏ£¬È»ºóÌí¼Óµ½¸Ã¼¯ºÏµ±ÖÐ AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); for (Advisor avr : advisor) { advised.addAdvisor(0, avr); } } PROXYED_SET.add(beanName); return bean; } } catch (Exception exx) {}} +``` + + + + + + + + + +wrapIfNecessary·½·¨½Ï³¤ÎÒÃÇ·Ö²½Öè¿´¿´ + +1£©isTccAutoProxyÅжÏÊÇ·ñ¿ªÆôtccģʽ£¬¿ªÆôµÄ»°Ñ¡ÔñÁËTccActionInterceptorÀ¹½ØÆ÷£¬·ÇtccģʽѡÔñ +GlobalTransactionalInterceptorÀ¹½ØÆ÷£¬Ä¬Èϲ»¿ªÆô + +2£©existAnnotationÅжϵ±Ç°BeanÊÇ·ñÓÐÀà»òÕ߽ӿڵķ½·¨´æÔÚ@GlobalTransactional»òÕß@GlobalLock×¢½â£¬Èç¹ûûÓÐÔòÖ±½Ó·µ»Ø + +3£©isAopProxy·½·¨ÊÇÅжϵ±Ç°µÄBeanÊÇ·ñÒѾ­ÊÇspringµÄ´úÀíÀàÁË£¬ÎÞÂÛÊÇJDK¶¯Ì¬´úÀí»¹ÊÇCglibÀà´úÀí¡£Èç¹ûÊÇÆÕͨµÄBean£¬×ßÔ­ÓеÄÉú³É´úÀíÂß¼­¼´¿É£¬Èç¹ûÒѾ­ÊÇ´úÀíÀ࣬ÄÇôҪͨ¹ý·´Éä»ñÈ¡´úÀí¶ÔÏóÄÚµÄÀ¹½ØÆ÷¼¯ºÏÒ²½Ð×öAdvisor£¬Ö±½ÓÌí¼Óµ½¸Ã¼¯ºÏµ±ÖС£ + +wrapIfNecessaryµÄ·½·¨²¢²»¸´ÔÓ£¬µ«ÊÇÈç¹û¶Ô´úÀí²»ÊǺÜÊìϤ»òÐí¶Ôϸ½Úµã»áÓÐЩÀ§»ó¡£ + +**2.4.1.1 ATÒ»½×¶Î¿ªÆôÈ«¾ÖÊÂÎñ** +ÔÚÐèÒª½øÐÐÈ«¾ÖÊÂÎñ¹ÜÀíµÄ½Ó¿ÚÉÏ£¬»á¼Ó@GlobalTransactional×¢½â£¬Õâ¸ö×¢½â»áÓÖÒ»¸ö¶ÔÓ¦µÄÀ¹½ØÆ÷½øÐÐÀ¹½Ø +GlobalTransactionalInterceptor£¬invoke¾ÍÊÇÀ¹½Ø·½·¨ + + + +``` +@Overridepublic Object invoke(final MethodInvocation methodInvocation) throws Throwable { Class targetClass = methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null; Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass); if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) { final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod); //»ñÈ¡·½·¨ÉϵÄÈ«¾ÖÊÂÎñ×¢½â final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, targetClass, GlobalTransactional.class); //»ñÈ¡·½·¨ÉϵÄÈ«¾ÖËø×¢½â final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class); boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes); if (!localDisable) { //Èç¹û·½·¨ÉÏÓÐÈ«¾ÖÊÂÎñ×¢½â£¬µ÷ÓÃhandleGlobalTransaction¿ªÆôÈ«¾ÖÊÂÎñ if (globalTransactionalAnnotation != null) { return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation); //Èç¹û·½·¨ÉÏÓÐÈ«¾ÖËø×¢½â£¬µ÷ÓÃhandleGlobalLock¿ªÆôÈ«¾ÖËø } else if (globalLockAnnotation != null) { return handleGlobalLock(methodInvocation); } } } //Èç¹ûɶ¶¼Ã»ÓУ¬°´ÆÕͨ·½·¨Ö´ÐУ¬ÌáÉýÐÔÄÜ return methodInvocation.proceed();} +``` + + + + + + + + + +ÔÚhandleGlobalTransaction·½·¨Öе÷ÓÃÁË +transactionalTemplate.execute·½·¨ + + + +``` +// 2\. ¿ªÆôÈ«¾ÖÊÂÎñbeginTransactionbeginTransaction(txInfo, tx); Object rs = null;try { // Ö´ÐÐÒµÎñ·½·¨business.execute() rs = business.execute(); } catch (Throwable ex) { // 3.³öÏÖÒì³£Ö´ÐÐcompleteTransactionAfterThrowing»Ø¹ö completeTransactionAfterThrowing(txInfo, tx, ex); throw ex;} // 4\. ûÓÐÒì³£Ìá½»ÊÂÎñcommitTransactioncommitTransaction(tx); +``` + + + + + + + + + +¿ªÆôÈ«¾ÖÊÂÎñ×îÖÕµ÷Óà +io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.String)·½·¨ + + + +``` +@Overridepublic void begin(int timeout, String name) throws TransactionException { //´Ë´¦µÄ½ÇÉ«ÅжÏÓйؼüµÄ×÷ÓÃ//±íÃ÷µ±Ç°ÊÇÈ«¾ÖÊÂÎñµÄ·¢ÆðÕߣ¨Launcher£©»¹ÊDzÎÓëÕߣ¨Participant£©//Èç¹ûÔÚ·Ö²¼Ê½ÊÂÎñµÄÏÂÓÎϵͳ·½·¨ÖÐÒ²¼ÓÉÏGlobalTransactional×¢½â//ÄÇôËüµÄ½ÇÉ«¾ÍÊÇParticipant£¬¼´»áºöÂÔºóÃæµÄbegin¾ÍÍ˳öÁË //¶øÅжÏÊÇ·¢ÆðÕߣ¨Launcher£©»¹ÊDzÎÓëÕߣ¨Participant£©ÊǸù¾Ýµ±Ç°ÉÏÏÂÎÄÊÇ·ñÒÑ´æÔÚXIDÀ´ÅÐ¶Ï //ûÓÐXIDµÄ¾ÍÊÇLauncher£¬ÒѾ­´æÔÚXIDµÄ¾ÍÊÇParticipant if (role != GlobalTransactionRole.Launcher) { assertXIDNotNull(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid); } return; } assertXIDNull(); if (RootContext.getXID() != null) { throw new IllegalStateException(); } xid = transactionManager.begin(null, null, name, timeout); status = GlobalStatus.Begin; RootContext.bind(xid); if (LOGGER.isInfoEnabled()) { LOGGER.info("Begin new global transaction [{}]", xid); } } +``` + + + + + + + + + +ÇëÇóseata-server»ñȡȫ¾ÖÊÂÎñXID + + + +``` +@Overridepublic String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { GlobalBeginRequest request = new GlobalBeginRequest(); request.setTransactionName(name); request.setTimeout(timeout); //¸ú½ø GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request); if (response.getResultCode() == ResultCode.Failed) { throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg()); } return response.getXid();} +private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException { try { //TMClient·â×°µÄNetty¶ÔÏó return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request); } catch (TimeoutException toe) { throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe); }} +``` + + + + + + + + + +½«XID°ó¶¨ÔÚRootContextÖУ¬ÓÉ´Ë¿ÉÒÔ¿´³öÈ«¾ÖÊÂÎñÊÇÓÉTM·¢ÆðµÄ£¬TM·¢ÆðÈ«¾ÖÊÂÎñÇëÇó¸øseata-server·þÎñ£¬seata-server·þÎñ½ÓÊܵ½ÇëÇóºó´¦Àí£¨ÒÔÏÂÊÇseata·þÎñ´úÂ룩£º + + + +``` +@Overrideprotected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext) throws TransactionException { //½øÈëbegin response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout())); if (LOGGER.isInfoEnabled()) { LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}", rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid()); }} +``` + + + + + + + + + +io.seata.server.coordinator.DefaultCoordinator#doGlobalBegin·½·¨½ÓÊܿͻ§¶Ë¿ªÆôÈ«¾ÖÊÂÎñµÄÇëÇ󣬵÷ÓÃio.seata.server.coordinator.DefaultCore#begin¿ªÆôÈ«¾ÖÊÂÎñ + + + +``` +@Overridepublic String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name, timeout); MDC.put(RootContext.MDC_KEY_XID, session.getXid()); session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());//¿ªÆô»á»° session.begin(); // transaction start event eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC, session.getTransactionName(), applicationId, transactionServiceGroup, session.getBeginTime(), null, session.getStatus())); return session.getXid();} +``` + + + + + + + + + +ͨ¹ýµ±Ç°»á»°¿ªÆô + + + +``` +@Overridepublic void begin() throws TransactionException { this.status = GlobalStatus.Begin; this.beginTime = System.currentTimeMillis(); this.active = true; for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onBegin(this); }} +``` + + + + + + + + + +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e7c784186f75581eba83325a0fc4708602dadf.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø") + +µ÷Óà +io.seata.server.session.AbstractSessionManager#onBegin·½·¨£¬ÓÖµ÷ÓÃio.seata.server.storage.db.session.DataBaseSessionManager#addGlobalSession·½·¨ + + + +``` +@Overridepublic void addGlobalSession(GlobalSession session) throws TransactionException { if (StringUtils.isBlank(taskName)) { //½øÈë boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_ADD, session); if (!ret) { throw new StoreException("addGlobalSession failed."); } } else { boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session); if (!ret) { throw new StoreException("addGlobalSession failed."); } }} +``` + + + + + + + + + +ÕâÀïÍùÊý¾Ý¿âÀïдÈëÊý¾Ý + + + +``` +@Overridepublic boolean writeSession(LogOperation logOperation, SessionStorable session) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else { throw new StoreException("Unknown LogOperation:" + logOperation.name()); }} +``` + + + + + + + + + +ÕâÀïÏòseata¿âglobal_tab²åÈëÊý¾Ý£¬µ½´ËÈ«¾ÖÊÂÎñÒÑ¿ªÆô + +**2.4.1.2 ATÒ»½×¶ÎÖ´ÐÐÒµÎñSQL** +È«¾ÖÊÂÎñÒÑ¿ªÆô£¬ÏÂÃæÐèÒªÖ´ÐÐÒµÎñSQL£¬Éú³Éundo_logÊý¾Ý£¬È«¾ÖÊÂÎñÀ¹½Ø³É¹¦ºó×îÖÕ»¹ÊÇÖ´ÐÐÁËÒµÎñ·½·¨µÄ£¬µ«ÊÇÓÉÓÚSeata¶ÔÊý¾ÝÔ´×öÁË´úÀí£¬ËùÒÔsql½âÎöÓëundo_logÈë¿â²Ù×÷ÊÇÔÚÊý¾ÝÔ´´úÀíÖÐÖ´Ðе쬴úÀí¾ÍÊÇSeata¶ÔDataSource£¬Connection£¬Statement×öµÄ´úÀí·â×°Àà + + + +``` +/*** ¹¹Ôìdatasource´úÀí¶ÔÏó£¬Ìæ»»Ô­À´µÄµÄdatasource*/@Primary@Bean("dataSource")public DataSourceProxy dataSourceProxy(DataSource druidDataSource){ return new DataSourceProxy(druidDataSource);} +``` + + + + + + + + + +ÏîÄ¿ÖÐʹÓõÄÊý¾ÝÔ´¾ùÓÃseataµÄDataSourceProxy´úÌæ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c8a7a189816e282015a950bfd3c13b7d859974.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")×îÖÕ¶ÔSql½øÐнâÎö²Ù×÷£¬·¢ÉúÔÚStatementProxyÀàÖÐ + + + +``` +@Overridepublic boolean execute(String sql) throws SQLException { this.targetSQL = sql; return ExecuteTemplate.execute(this, (statement, args) -> statement.execute((String) args[0]), sql);} +``` + + + + + + + + + + + +``` +public static T execute(List sqlRecognizers, StatementProxy statementProxy, StatementCallback statementCallback, Object... args) throws SQLException { if (!RootContext.requireGlobalLock() && !StringUtils.equals(BranchType.AT.name(), RootContext.getBranchType())) { //²»ÊÇÈ«¾ÖÊÂÎñµÄÖ±½ÓÖ´ÐУ¬ÌáÉýÐÔÄÜ return statementCallback.execute(statementProxy.getTargetStatement(), args); } String dbType = statementProxy.getConnectionProxy().getDbType(); if (CollectionUtils.isEmpty(sqlRecognizers)) { sqlRecognizers = SQLVisitorFactory.get( statementProxy.getTargetSQL(), dbType); } Executor executor; if (CollectionUtils.isEmpty(sqlRecognizers)) { executor = new PlainExecutor<>(statementProxy, statementCallback); } else { if (sqlRecognizers.size() == 1) { SQLRecognizer sqlRecognizer = sqlRecognizers.get(0); //²»Í¬SQLÀàÐÍ£¬²»Í¬´¦Àí switch (sqlRecognizer.getSQLType()) { case INSERT: executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType, new Class[]{StatementProxy.class, StatementCallback.class, SQLRecognizer.class}, new Object[]{statementProxy, statementCallback, sqlRecognizer}); break; case UPDATE: executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; case DELETE: executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; case SELECT_FOR_UPDATE: executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer); break; default: executor = new PlainExecutor<>(statementProxy, statementCallback); break; } } else { executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers); } } T rs; try { //Ö´ÐÐSQL rs = executor.execute(args); } catch (Throwable ex) { if (!(ex instanceof SQLException)) { // Turn other exception into SQLException ex = new SQLException(ex); } throw (SQLException) ex; } return rs; } +``` + + + + + + + + + +* ÏÈÅжÏÊÇ·ñ¿ªÆôÁËÈ«¾ÖÊÂÎñ£¬Èç¹ûûÓУ¬²»×ß´úÀí£¬²»½âÎösql£¬ÌáÉýÐÔÄÜ +* µ÷ÓÃSQLVisitorFactory¶ÔÄ¿±êsql½øÐнâÎö +* Õë¶ÔÌØ¶¨ÀàÐÍsql²Ù×÷(INSERT,UPDATE,DELETE,SELECT_FOR_UPDATE)µÈ½øÐÐÌØÊâ½âÎö +* Ö´ÐÐsql²¢·µ»Ø½á¹û + ²»Í¬ÀàÐ͵ÄSQL´¦Àí·½·¨²»Ò»Ñù£¬ÕâÀïÒÔinsertΪÀý + +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b1eb1b610beb647b230995f19ecbb4bbc98602.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")insertʹÓõÄÊÇInsertExecutor.execute·½·¨£¬µ«Æäʵ×îÖÕ»¹ÊÇʹÓà +io.seata.rm.datasource.exec.BaseTransactionalExecutor#execute·½·¨ + + + +``` +@Overridepublic T execute(Object... args) throws Throwable { if (RootContext.inGlobalTransaction()) { String xid = RootContext.getXID(); statementProxy.getConnectionProxy().bind(xid); } statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock()); return doExecute(args);} +``` + + + + + + + + + +½«ÉÏÏÂÎÄÖеÄxid°ó¶¨µ½ÁËstatementProxyÖУ¬²¢µ÷ÓÃÁËdoExecute·½·¨£¬¿´ÏÂAbstractDMLBaseExecutorÖеÄdoExecute·½·¨ + + + +``` +@Overridepublic T doExecute(Object... args) throws Throwable { AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); if (connectionProxy.getAutoCommit()) { return executeAutoCommitTrue(args); } else { return executeAutoCommitFalse(args); }} +``` + + + + + + + + + +·½·¨Öе÷ÓÃÁË +executeAutoCommitTrue/executeAutoCommitFalse + + + +``` +protected T executeAutoCommitTrue(Object[] args) throws Throwable { ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); try { connectionProxy.setAutoCommit(false); return new LockRetryPolicy(connectionProxy).execute(() -> { T result = executeAutoCommitFalse(args); connectionProxy.commit(); return result; }); } catch (Exception e) { ... } finally { connectionProxy.getContext().reset(); connectionProxy.setAutoCommit(true); }} +``` + + + + + + + + + +µ«×Ðϸ·¢ÏÖ£¬×îÖÕ¶¼Êǵ÷ÓÃexecuteAutoCommitFalse·½·¨ + + + +``` +protected T executeAutoCommitFalse(Object[] args) throws Exception { //¸úÈëgetTableMeta·½·¨ if (!JdbcConstants.MYSQL.equalsIgnoreCase(getDbType()) && getTableMeta().getPrimaryKeyOnlyName().size() > 1) { throw new NotSupportYetException("multi pk only support mysql!"); } //»ñÈ¡beforeImage TableRecords beforeImage = beforeImage(); //Ö´ÐÐÒµÎñsql T result = statementCallback.execute(statementProxy.getTargetStatement(), args); //»ñÈ¡afterImage TableRecords afterImage = afterImage(beforeImage); //±£´æimage prepareUndoLog(beforeImage, afterImage); return result;} +``` + + + + + + + + + +»ñÈ¡beforeImage + + + +``` +//tableMetaÀïÃæ°üº¬±íÃû¡¢ÁС¢Ë÷ÒýµÈÊý¾Ýprotected TableMeta getTableMeta(String tableName) { if (tableMeta != null) { return tableMeta; } ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); tableMeta = TableMetaCacheFactory.getTableMetaCache(connectionProxy.getDbType()) .getTableMeta(connectionProxy.getTargetConnection(), tableName, connectionProxy.getDataSourceProxy().getResourceId()); return tableMeta;} +``` + + + + + + + + + +Ö´ÐÐÒµÎñsql»¹ÊÇʹÓà +com.alibaba.druid.pool.DruidPooledPreparedStatement#execute·½·¨Ö´ÐÐ + +»ñÈ¡afterImage![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/540ff5c254dde029bed208247025e97bd1f497.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÔÚÌá½»ÊÂÎñʱ£¬²åÈëundo_logÈÕÖ¾ + + + +``` +protected T executeAutoCommitTrue(Object[] args) throws Throwable { ConnectionProxy connectionProxy = statementProxy.getConnectionProxy(); try { connectionProxy.setAutoCommit(false); return new LockRetryPolicy(connectionProxy).execute(() -> { T result = executeAutoCommitFalse(args); //¸úÈë connectionProxy.commit(); return result; }); } catch (Exception e) { ... } finally { connectionProxy.getContext().reset(); connectionProxy.setAutoCommit(true); }} +``` + + + + + + + + + + + +``` +public void commit() throws SQLException { try { LOCK_RETRY_POLICY.execute(() -> { //¸úÈë doCommit(); return null; }); } catch (SQLException e) { throw e; } catch (Exception e) { throw new SQLException(e); }} +``` + + + + + + + + + + + +``` +private void doCommit() throws SQLException { if (context.inGlobalTransaction()) { //¸úÈë processGlobalTransactionCommit(); } else if (context.isGlobalLockRequire()) { processLocalCommitWithGlobalLocks(); } else { targetConnection.commit(); }} +``` + + + + + + + + + + + +``` +private void processGlobalTransactionCommit() throws SQLException { try { //Ïòseata-server×¢²á·ÖÖ§ÐÅÏ¢ register(); } catch (TransactionException e) { recognizeLockKeyConflictException(e, context.buildLockKeys()); } try { //Ìá½»ÊÂÎñ֮ǰ£¬²åÈëundo_log,¸úÈëflushUndoLogs UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this); targetConnection.commit(); } catch (Throwable ex) { ... } if (IS_REPORT_SUCCESS_ENABLE) { report(true); } context.reset();} +``` + + + + + + + + + + + +``` +public void flushUndoLogs(ConnectionProxy cp) throws SQLException { ConnectionContext connectionContext = cp.getContext(); if (!connectionContext.hasUndoLog()) { return; } String xid = connectionContext.getXid(); long branchId = connectionContext.getBranchId(); ...//¸Ã·½·¨²åÈëundo_log insertUndoLogWithNormal(xid, branchId, buildContext(parser.getName()), undoLogContent, cp.getTargetConnection());} +``` + + + + + + + + + +Ôڸ÷½·¨ÖÐ×¢²á·ÖÖ§ÊÂÎñ![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/92309a950c73829078a3170c585fc58fb03b0d.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")Ìá½»ÊÂÎñ£¬Ïòseata-server×¢²á·ÖÖ§ÐÅÏ¢£¬seata-server½ÓÊÕµ½ÇëÇó£¨seataÔ´Â룩![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c116df5929a1116e61e683298e14b953450bb7.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø") + +io.seata.server.coordinator.DefaultCoordinator#doBranchRegister·½·¨ + + + +``` +public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException { GlobalSession globalSession = assertGlobalSessionNotNull(xid, false); return SessionHolder.lockAndExecute(globalSession, () -> { ... try { //½øÐÐ×¢²á globalSession.addBranch(branchSession); } catch (RuntimeException ex) { ... } ... return branchSession.getBranchId(); });} +``` + + + + + + + + + + + +``` +@Overridepublic void addBranch(BranchSession branchSession) throws TransactionException { for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { //¸úÈëonAddBranch£¬Ñ¡ÔñAbstractSessionManager lifecycleListener.onAddBranch(this, branchSession); } branchSession.setStatus(BranchStatus.Registered); add(branchSession);} +``` + + + + + + + + + +io.seata.server.storage.db.session.DataBaseSessionManager#addBranchSession·½·¨ + + + +``` +@Overridepublic void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { //¸úÈ룬ѡÔñDataBaseSessionManager addBranchSession(globalSession, branchSession);} +``` + + + + + + + + + + + +``` +@Overridepublic void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException { if (StringUtils.isNotBlank(taskName)) { return; } //¸úÈë boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session); if (!ret) { throw new StoreException("addBranchSession failed."); }} +``` + + + + + + + + + + + +``` +@Overridepublic boolean writeSession(LogOperation logOperation, SessionStorable session) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); } else { throw new StoreException("Unknown LogOperation:" + logOperation.name()); }} +``` + + + + + + + + + + + +``` +@Overridepublic boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) { String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertBranchTransactionSQL(branchTable); Connection conn = null; PreparedStatement ps = null; try { int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); ps.setString(index++, branchTransactionDO.getXid()); ps.setLong(index++, branchTransactionDO.getTransactionId()); ps.setLong(index++, branchTransactionDO.getBranchId()); ps.setString(index++, branchTransactionDO.getResourceGroupId()); ps.setString(index++, branchTransactionDO.getResourceId()); ps.setString(index++, branchTransactionDO.getBranchType()); ps.setInt(index++, branchTransactionDO.getStatus()); ps.setString(index++, branchTransactionDO.getClientId()); ps.setString(index++, branchTransactionDO.getApplicationData()); return ps.executeUpdate() > 0; } catch (SQLException e) { throw new StoreException(e); } finally { IOUtil.close(ps, conn); }} +``` + + + + + + + + + +Seata-serverÌí¼Ó·ÖÖ§ÐÅÏ¢Íê³É£¬µ½ÕâÀһ½×¶Î½áÊø£¬ÒµÎñÊý¾Ý£¬undo_log£¬·ÖÖ§ÐÅÏ¢¶¼ÒѾ­Ð´ÈëÊý¾Ý¿â + +**2.4.1.3 AT¶þ½×¶ÎÌá½»** +»Øµ½handleGlobalTransaction·½·¨ÖУ¬µ÷ÓÃÁË +transactionalTemplate.execute·½·¨ + + + +``` +// 2\. ¿ªÆôÈ«¾ÖÊÂÎñbeginTransactionbeginTransaction(txInfo, tx); Object rs = null;try { // Ö´ÐÐÒµÎñ·½·¨business.execute() rs = business.execute(); } catch (Throwable ex) { //ÉÏÃæÊÇÒ»½×¶Î //ÏÂÃæÊǶþ½×¶Î // 3.³öÏÖÒì³£Ö´ÐÐcompleteTransactionAfterThrowing»Ø¹ö completeTransactionAfterThrowing(txInfo, tx, ex); throw ex;} // 4\. ûÓÐÒì³£Ìá½»ÊÂÎñcommitTransactioncommitTransaction(tx); +``` + + + + + + + + + +¶þ½×¶ÎÌá½» + +commitTransaction(tx);¸ú½ø + + + +``` +private void commitTransaction(GlobalTransaction tx) throws TransactionalExecutor.ExecutionException { try { triggerBeforeCommit(); //¸úÈë tx.commit(); triggerAfterCommit(); } catch (TransactionException txe) { // 4.1 Failed to commit throw new TransactionalExecutor.ExecutionException(tx, txe, TransactionalExecutor.Code.CommitFailure); }} +``` + + + + + + + + + +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/61b35980530bd17bff9025f1112baa9e3e93e5.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø") + + + +``` +@Overridepublic GlobalStatus commit(String xid) throws TransactionException { GlobalCommitRequest globalCommit = new GlobalCommitRequest(); globalCommit.setXid(xid); //¸úÈësyncCall GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit); return response.getGlobalStatus();} +``` + + + + + + + + + + + +``` +private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException { try { return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request); } catch (TimeoutException toe) { throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe); }} +``` + + + + + + + + + +×îÖÕͨ¹ýTMÇëÇóseata-server£¬Seata-server½ÓÊÕµ½È«¾ÖÌá½»ÇëÇó£¨seataÔ´Â룩 + +DefaultCoordinatorÖÐ + + + +``` +@Overrideprotected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext) throws TransactionException { MDC.put(RootContext.MDC_KEY_XID, request.getXid()); //¸úÈëcommit response.setGlobalStatus(core.commit(request.getXid()));} +``` + + + + + + + + + +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/185262d74acd3a723a7770d5771c19fa9fa5cf.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/51249f8944b29b9ffc82897a818cb4c33ed06a.png "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/14a2f2b58ad3f11be518376d5e6f68cc678af9.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø") +Seata-server½ÓÊÕµ½¿Í»§¶ËÈ«¾ÖÌá½»ÇëÇóºó£¬ÏȻص÷¿Í»§¶Ë£¬É¾³ýundo_log£¬seataÔÚɾ³ý·ÖÖ§¼°È«¾ÖÊÂÎñ + +֮ǰ˵¹ýRMClientÔÚ³õʼ»¯Ê±£¬ÉèÖÃ×ÊÔ´¹ÜÀíÆ÷resourceManager£¬ÉèÖÃÏûÏ¢»Øµ÷¼àÌýÆ÷ÓÃÓÚ½ÓÊÕTCÔÚ¶þ½×¶Î·¢³öµÄÌá½»»òÕ߻عöÇëÇó![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/884c07085b1230a1aab1883d691532ccf6b79c.png "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")Seata-serverɾ³ý·ÖÖ§Êý¾Ý¼°È«¾ÖÊÂÎñÊý¾Ý + + + +``` +@Overridepublic void removeBranch(BranchSession branchSession) throws TransactionException { // do not unlock if global status in (Committing, CommitRetrying, AsyncCommitting), // because it's already unlocked in 'DefaultCore.commit()' if (status != Committing && status != CommitRetrying && status != AsyncCommitting) { if (!branchSession.unlock()) { throw new TransactionException("Unlock branch lock failed, xid = " + this.xid + ", branchId = " + branchSession.getBranchId()); } } for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { //¸úÈë lifecycleListener.onRemoveBranch(this, branchSession); } remove(branchSession);} +``` + + + + + + + + + + + +``` +private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException { if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to store global session"); } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to update global session"); } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to remove global session"); } else if (LogOperation.BRANCH_ADD.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to store branch session"); } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to update branch session"); } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Fail to remove branch session"); } else { throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession, "Unknown LogOperation:" + logOperation.name()); } }} +``` + + + + + + + + + + + +``` +public static void endCommitted(GlobalSession globalSession) throws TransactionException { globalSession.changeStatus(GlobalStatus.Committed); //ɾ³ýÈ«¾ÖÊÂÎñ globalSession.end();} +``` + + + + + + + + + +¿Í»§¶Ëɾ³ýundo_logÊý¾Ý + +ÔÚ½ÓÊÕÌá½»ÀïÃæ + + + +``` +protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response) throws TransactionException { String xid = request.getXid(); long branchId = request.getBranchId(); String resourceId = request.getResourceId(); String applicationData = request.getApplicationData(); if (LOGGER.isInfoEnabled()) { LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData); } //¸úÈë BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId, applicationData); response.setXid(xid); response.setBranchId(branchId); response.setBranchStatus(status); if (LOGGER.isInfoEnabled()) { LOGGER.info("Branch commit result: " + status); } } +``` + + + + + + + + + +getResourceManager»ñÈ¡µÄ¾ÍÊÇRMClient³õʼ»¯Ê±ÉèÖõÄ×ÊÔ´¹ÜÀíÆ÷DataSourceManager + + + +``` +public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { return asyncWorker.branchCommit(branchType, xid, branchId, resourceId, applicationData);} +``` + + + + + + + + + + + +``` +@Overridepublic BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { if (!ASYNC_COMMIT_BUFFER.offer(new Phase2Context(branchType, xid, branchId, resourceId, applicationData))) { LOGGER.warn("Async commit buffer is FULL. Rejected branch [{}/{}] will be handled by housekeeping later.", branchId, xid); } return BranchStatus.PhaseTwo_Committed;} +``` + + + + + + + + + +Õâ±ßÖ»ÊÇÍùÒ»¸öASYNC_COMMIT_BUFFER»º³åListÖÐÐÂÔöÁËÒ»¸ö¶þ½×¶ÎÌá½»µÄcontext£¬µ«ÕæÕýÌá½»ÔÚAsyncWorkerµÄinit()·½·¨ + + + +``` +public synchronized void init() { LOGGER.info("Async Commit Buffer Limit: {}", ASYNC_COMMIT_BUFFER_LIMIT); ScheduledExecutorService timerExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AsyncWorker", 1, true)); timerExecutor.scheduleAtFixedRate(() -> { try {//¸úÈë doBranchCommits(); } catch (Throwable e) { LOGGER.info("Failed at async committing ... {}", e.getMessage()); } }, 10, 1000 * 1, TimeUnit.MILLISECONDS);} +``` + + + + + + + + + +![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a730eb7781ec4bd3ad2578fb7855aa02d45fbc.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")ɾ³ýUndo_log + +**¶þ½×¶Î»Ø¹ö** + +¶þ½×¶Î»Ø¹öseata-server¶Ë´úÂëÓë¶þ½×¶ÎÌá½»ÀàËÆ£¬ÕâÀïÊ¡ÂÔ + + + +``` +protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response, RpcContext rpcContext) throws TransactionException { MDC.put(RootContext.MDC_KEY_XID, request.getXid()); //È«¾Ö»Ø¹öseaËû½ÓÊÕÇëÇó response.setGlobalStatus(core.rollback(request.getXid()));} +``` + + + + + + + + + +Ö÷Òª¿´»Ø¹ö¿Í»§¶ËÈçºÎ½øÐÐÊÂÎñ²¹³¥ + + + +``` +@Overridepublic BranchRollbackResponse handle(BranchRollbackRequest request) { BranchRollbackResponse response = new BranchRollbackResponse(); exceptionHandleTemplate(new AbstractCallback() { @Override public void execute(BranchRollbackRequest request, BranchRollbackResponse response) throws TransactionException { //¸úÈë doBranchRollback(request, response); } }, request, response); return response;} +``` + + + + + + + + + + + +``` +public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { DataSourceProxy dataSourceProxy = get(resourceId); if (dataSourceProxy == null) { throw new ShouldNeverHappenException(); } try { UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).undo(dataSourceProxy, xid, branchId); } catch (TransactionException te) { StackTraceLogger.info(LOGGER, te, "branchRollback failed. branchType:[{}], xid:[{}], branchId:[{}], resourceId:[{}], applicationData:[{}]. reason:[{}]", new Object[]{branchType, xid, branchId, resourceId, applicationData, te.getMessage()}); if (te.getCode() == TransactionExceptionCode.BranchRollbackFailed_Unretriable) { return BranchStatus.PhaseTwo_RollbackFailed_Unretryable; } else { return BranchStatus.PhaseTwo_RollbackFailed_Retryable; } } return BranchStatus.PhaseTwo_Rollbacked; } +``` + + + + + + + + + +×îÖջعö·½·¨µ÷ÓõÄÊÇUndoLogManager.undo(dataSourceProxy, xid, branchId);![SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4842a0701546824cf2720855d8310a1274c576.jpg "SpringCloud AlibabaϵÁСª¡ª17Seata ATģʽԴÂë·ÖÎö£¨Ï£©-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÅжÏundologÊÇ·ñ´æÔÚ£¬´æÔÚÔòɾ³ý¶ÔÓ¦undolog£¬²¢Ò»ÆðÌá½»£¬µ½´ËseataµÄATģʽԴÂë½âÎöÍê±Ï¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..9fc4976 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloudAlibaba\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudSentinel\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,659 @@ +×÷Õß | Ò»ÆðߣJava +À´Ô´ |½ñÈÕÍ·Ìõ + +**ѧϰĿ±ê** + +* SentinelµÄ¹¤×÷Ô­Àí + **µÚ1Õ ÏÞÁ÷Ô­Àí** + ÔÚSentinelÖУ¬ËùÓеÄ×ÊÔ´¶¼¶ÔÓ¦Ò»¸ö×ÊÔ´Ãû³ÆÒÔ¼°Ò»¸öEntry¡£Ã¿Ò»¸öentry¿ÉÒÔ±íʾһ¸öÇëÇó¡£¶øSentinelÖУ¬»áÕë¶Ôµ±Ç°ÇëÇó»ùÓÚ¹æÔòµÄÅжÏÀ´ÊµÏÖÁ÷¿ØµÄ¿ØÖÆ£¬Ô­ÀíÈçÏÂͼËùʾ¡£ + +![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1296c955070646bbc74310e726679bbabfe585.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÉÏͼ½ö×÷ΪÉè¼ÆË¼ÏëµÄչʾ£¬Í¼ÖÐ Slot µÄ˳ÐòÒѺÍ×îаæ Sentinel Slot Chain ˳Ðò²»Ò»Ö +µ±Ò»¸öÍⲿÇëÇó¹ýÀ´Ö®ºó£¬»á´´½¨Ò»¸öEntry£¬¶ø´´½¨EntryµÄͬʱ£¬Ò²»á´´½¨Ò»ÏµÁеÄslot ×é³ÉÒ»¸öÔðÈÎÁ´£¬Ã¿¸öslotÓв»Í¬µÄ¹¤×÷Ö°Ôð¡£ + +* NodeSelectorSlot ¸ºÔðÊÕ¼¯×ÊÔ´µÄ·¾¶£¬²¢½«ÕâЩ×ÊÔ´µÄµ÷Ó÷¾¶£¬ÒÔÊ÷×´½á¹¹´æ´¢ÆðÀ´£¬ÓÃÓÚ¸ù¾Ýµ÷Ó÷¾¶À´ÏÞÁ÷½µ¼¶£» +* ClusterBuilderSlot ÔòÓÃÓÚ´æ´¢×ÊÔ´µÄͳ¼ÆÐÅÏ¢ÒÔ¼°µ÷ÓÃÕßÐÅÏ¢£¬ÀýÈç¸Ã×ÊÔ´µÄ RT, QPS,thread count µÈµÈ£¬ÕâЩÐÅÏ¢½«ÓÃ×÷Ϊ¶àά¶ÈÏÞÁ÷£¬½µ¼¶µÄÒÀ¾Ý£» +* StatisticSlot ÔòÓÃÓڼǼ¡¢Í³¼Æ²»Í¬Î³¶ÈµÄ runtime Ö¸±ê¼à¿ØÐÅÏ¢£» +* FlowSlot ÔòÓÃÓÚ¸ù¾ÝÔ¤ÉèµÄÏÞÁ÷¹æÔòÒÔ¼°Ç°Ãæ slot ͳ¼ÆµÄ״̬£¬À´½øÐÐÁ÷Á¿¿ØÖÆ£» +* AuthoritySlot Ôò¸ù¾ÝÅäÖõĺڰ×Ãûµ¥ºÍµ÷ÓÃÀ´Ô´ÐÅÏ¢£¬À´×öºÚ°×Ãûµ¥¿ØÖÆ£» +* DegradeSlot Ôòͨ¹ýͳ¼ÆÐÅÏ¢ÒÔ¼°Ô¤ÉèµÄ¹æÔò£¬À´×öÈ۶Ͻµ¼¶£» +* SystemSlot Ôòͨ¹ýϵͳµÄ״̬£¬ÀýÈç load1 µÈ£¬À´¿ØÖÆ×ܵÄÈë¿ÚÁ÷Á¿£» +* LogSlot ÔÚ³öÏÖÏÞÁ÷¡¢ÈÛ¶Ï¡¢ÏµÍ³±£»¤Ê±¸ºÔð¼Ç¼ÈÕÖ¾ +* ... + Sentinel ½« ProcessorSlot ×÷Ϊ SPI ½Ó¿Ú½øÐÐÀ©Õ¹£¨1.7.2 °æ±¾ÒÔǰ SlotChainBuilder ×÷ΪSPI£©£¬Ê¹µÃ Slot Chain ¾ß±¸ÁËÀ©Õ¹µÄÄÜÁ¦¡£Äú¿ÉÒÔ×ÔÐмÓÈë×Ô¶¨ÒåµÄ slot ²¢±àÅÅ slot ¼äµÄ˳Ðò£¬´Ó¶ø¿ÉÒÔ¸ø Sentinel Ìí¼Ó×Ô¶¨ÒåµÄ¹¦ÄÜ¡£ + +![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d65c2688084bf5be06d3687ce8663cb1b7167b.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")**Spring Cloud ¼¯³ÉSentinelµÄÔ­Àí** + +Spring Cloud Öм¯³ÉSentinelÏÞÁ÷£¬ÊÇ»ùÓÚÀ¹½ØÆ÷À´ÊµÏÖ£¬¾ßÌåµÄʵÏÖ·¾¶ÈçÏ¡£ + +SentinelWebAutoConfiguration¡ª¡ª>addInterceptors¡ª¡ª>SentinelWebInterceptor->AbstractSentinelInterceptor + + + +``` +public boolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler) throws Exception { try { String resourceName = this.getResourceName(request); if (StringUtil.isEmpty(resourceName)) { return true; } else if (this.increaseReferece(request,this.baseWebMvcConfig.getRequestRefName(), 1) != 1) { return true; } else { String origin = this.parseOrigin(request); String contextName = this.getContextName(request); ContextUtil.enter(contextName, origin); Entry entry = SphU.entry(resourceName, 1, EntryType.IN); request.setAttribute(this.baseWebMvcConfig.getRequestAttributeName(), entry); return true; } } catch (BlockException var12) { BlockException e = var12; try { this.handleBlockException(request, response, e); } finally { ContextUtil.exit(); } return false; }} +``` + + + + + + + + + +> ×ÊÔ´µ÷ÓõÄÁ÷Á¿ÀàÐÍ£¬ÊÇÈë¿ÚÁ÷Á¿£¨ EntryType.IN £©»¹Êdzö¿ÚÁ÷Á¿£¨ EntryType.OUT £©£¬×¢Òâϵͳ¹æÔòÖ»¶Ô IN ÉúЧ + +**µÚ2Õ SphU.entry** +²»¹ÜÊǼ¯³ÉdubboÒ²ºÃ£¬»¹ÊǼ¯³Éµ½spring cloudÖÐÒ²ºÃ£¬×îÖÕ¶¼Êǵ÷ÓÃSphU.entryÕâ¸ö·½·¨À´½øÐÐÏÞÁ÷Åжϵ쬽ÓÏÂÀ´ÎÒÃÇ´ÓSphU.entryÕâ¸ö·½·¨ÖÐÈ¥Á˽âËüµÄʵÏÖÔ­Àí¡£ + +´úÂëÖÐÎÒÃÇ¿ÉÄÜΨһÒÉ»óµÄ£¬Ò²ÊÇ×î¹Ø¼üµÄÒ»²½ÊÇ SphU.entry(resource) £¬ ÎÒÃÇ´«½øÈ¥ÁËÒ»¸ö×ÊÔ´£¬Õâ¸ö×ÊÔ´¿ÉÓÃÊÇ·½·¨Ãû£¬¿ÉÒÔÊǽӿڣ¬ÄÇôËû¾ßÌå×öÁËÊ²Ã´ÄØ£¿ÈÃÎÒÃÇÀ´Ò»²½²½½Ò¿ªËûµÄÉñÃØÃæÉ´£º + + + +``` +public static Entry entry(String name) throws BlockException { return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);}public class Env { public static final Sph sph = new CtSph(); ......//Ê¡ÂÔ²¿·Ö´úÂë} +``` + + + + + + + + + +´Ó SphU.entry() ·½·¨ÍùÏÂÖ´ÐÐ»á½øÈëµ½ Sph.entry() £¬SphµÄĬÈÏʵÏÖÀàÊÇ CtSph,¶ø×îÖÕ»á½øÈëCtSph µÄentry ·½·¨£º + + + +``` +@Overridepublic Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {¡¡¡¡ //·â×°ÁËÒ»¸ö×ÊÔ´¶ÔÏó StringResourceWrapper resource = new StringResourceWrapper(name, type); return entry(resource, count, args);} +``` + + + + + + + + + +ÕâÀïµÄÖ÷Òª²½ÖèÊÇͨ¹ýÎÒÃǸø¶¨µÄ×ÊÔ´È¥·â×°ÁËÒ»¸ö StringResourceWrapper £¬È»ºó´«Èë×Ô¼ºµÄÖØÔØ·½·¨£¬¼Ì¶øµ÷Óà entryWithPriority(resourceWrapper, count, false, args)£º + +* ResourceWrapper ±íʾsentinelµÄ×ÊÔ´£¬×öÁË·â×° +* count±íʾ±¾´ÎÇëÇóµÄÕ¼ÓõIJ¢·¢ÊýÁ¿£¬Ä¬ÈÏÊÇ1 +* prioritized£¬ÓÅÏȼ¶ + + + +``` +private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count,boolean prioritized, Object... args) throws BlockException { //»ñÈ¡ÉÏÏÂÎÄ»·¾³£¬´æ´¢ÔÚThreadLocalÖУ¬contextÖÐ»á´æ´¢Õû¸öµ÷ÓÃÁ´ Context context = ContextUtil.getContext(); //Èç¹ûÊÇ NullContext£¬ÄÇô˵Ã÷ context name ³¬¹ýÁË 2000 ¸ö£¬²Î¼û ContextUtil#trueEnter //Õâ¸öʱºò£¬Sentinel ²»ÔÙ½ÓÊÜ´¦ÀíÐ嵀 context ÅäÖã¬Ò²¾ÍÊDz»×öÕâЩеĽӿڵÄͳ¼Æ¡¢ÏÞÁ÷ÈÛ¶ÏµÈ if (context instanceof NullContext) { // The {@link NullContext} indicates that the amount of context has exceeded the threshold, // so here init the entry only. No rule checking will be done. return new CtEntry(resourceWrapper, null, context); } if (context == null) {//ʹÓÃĬÈÏcontext // Éú³ÉContextµÄ²¿·Ö context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME); } // Global switch is close, no rule checking will do. if (!Constants.ON) {//È«¾ÖÏÞÁ÷¿ª¹ØÊÇ·ñÒѾ­¿ªÆô£¬Èç¹û¹Ø±ÕÁË£¬¾Í²»½øÐÐÏÞÁ÷¹æÔò¼ì²é return new CtEntry(resourceWrapper, null, context); } //Éè¼ÆÄ£Ê½ÖеÄÔðÈÎÁ´Ä£Ê½¡£ //¹¹½¨Ò»¸öslotÁ´±í ProcessorSlot chain = lookProcessChain(resourceWrapper); //¸ù¾Ý lookProcessChain ·½·¨£¬ÎÒÃÇÖªµÀ£¬µ± resource ³¬¹ý Constants.MAX_SLOT_CHAIN_SIZE£¬ // Ò²¾ÍÊÇ 6000 µÄʱºò£¬Sentinel ¿ªÊ¼²»´¦ÀíеÄÇëÇó£¬Õâô×öÖ÷ÒªÊÇΪÁË Sentinel µÄÐÔÄÜ¿¼ÂÇ if (chain == null) { return new CtEntry(resourceWrapper, null, context); } //ÏÂÃæÕâÀï²ÅÕæÕý¿ªÊ¼£¬Éú³É¸öentry Entry e = new CtEntry(resourceWrapper, chain, context); try { //¿ªÊ¼¼ì²âÏÞÁ÷¹æÔò chain.entry(context, resourceWrapper, null, count, prioritized, args); } catch (BlockException e1) { e.exit(count, args); //±»ÏÞÁ÷£¬Å׳öÒì³£¡£ throw e1; } catch (Throwable e1) { // This should not happen, unless there are errors existing in Sentinel internal. RecordLog.info("Sentinel unexpected exception", e1); } return e;//·µ»ØÕý³£µÄ½á¹û} +``` + + + + + + + + + +´ÓÉÏÃæµÄ´úÂëÎÒÃÇ¿ÉÒÔÖªµÀ£¬¸Ã·½·¨ÖÐÖ÷ÒªÊÇ»ñÈ¡µ½Á˱¾×ÊÔ´Ëù¶ÔÓ¦µÄ×ÊÔ´´¦ÀíÁ´£¬´ÓÆðÃüÃû lookProcessChain Öз¢ÏÖ£¬¾ÍÊÇÈ¥»ñÈ¡µ½Ò»Ìõ´¦ÀíÁ´£¬È¥Ö´ÐÐ×ÊÔ´µÄÕûºÏ´¦Àí£¬µ±È»£¬ÕâÀï´¦ÓÚÏÞÁ÷µÄ»·¾³Ï£¬ÄÇôÕâ¸ö´¦ÀíÁ´¿Ï¶¨ÊǶÔÓÚµ±Ç°»·¾³ÏÂÇëÇóµÄÁ÷Á¿ÕûºÏÏÞÁ÷Ïà¹ØµÄ´¦Àí¡£¿ÉÒÔ·ÖΪÒÔϼ¸¸ö²¿·Ö£º + +* ¶Ô²ÎÈ«¾ÖÅäÖÃÏî×ö¼ì²â£¬Èç¹û²»·ûºÏÒªÇó¾ÍÖ±½Ó·µ»ØÁËÒ»¸öCtEntry¶ÔÏ󣬲»»áÔÙ½øÐкóÃæµÄÏÞÁ÷¼ì²â£¬·ñÔò½øÈëÏÂÃæµÄ¼ì²âÁ÷³Ì¡£¸ù¾Ý°ü×°¹ýµÄ×ÊÔ´¶ÔÏó»ñÈ¡¶ÔÓ¦µÄSlotChain +* Ö´ÐÐSlotChainµÄentry·½·¨£¬Èç¹ûSlotChainµÄentry·½·¨Å׳öÁËBlockException£¬Ôò½«¸ÃÒì³£¼ÌÐøÏòÉÏÅ׳ö£¬Èç¹ûSlotChainµÄentry·½·¨Õý³£Ö´ÐÐÁË£¬Ôò×îºó»á½«¸Ãentry¶ÔÏó·µ»Ø +* Èç¹ûÉϲ㷽·¨²¶»ñÁËBlockException£¬Ôò˵Ã÷ÇëÇó±»ÏÞÁ÷ÁË£¬·ñÔòÇëÇóÄÜÕý³£Ö´ÐÐ + **2.1 ´´½¨Context** + InternalContextUtil.internalEnter--->trueEnter + + + +``` +protected static Context trueEnter(String name, String origin) { //´ÓThreadLocalÖлñÈ¡£¬µÚÒ»´Î¿Ï¶¨ÊÇnull Context context = contextHolder.get(); if (context == null) { //ÕâÀïÊǸù¾ÝContextµÄÃû×Ö»ñÈ¡Node Map localCacheNameMap = contextNameNodeMap; DefaultNode node = localCacheNameMap.get(name); if (node == null) { if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) { setNullContext(); return NULL_CONTEXT; } else { LOCK.lock(); try { node = contextNameNodeMap.get(name); if (node == null) { if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) { setNullContext(); return NULL_CONTEXT; } else { //´´½¨¸öEntranceNode node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null); //¼ÓÈëÈ«¾ÖµÄ½Úµã // Add entrance node. Constants.ROOT.addChild(node);//¼ÓÈëmapÖÐ Map newMap = new HashMap<>(contextNameNodeMap.size() + 1); newMap.putAll(contextNameNodeMap); newMap.put(name, node); contextNameNodeMap = newMap; } } } finally { LOCK.unlock(); } } } context = new Context(node, name); context.setOrigin(origin); //·ÅÈëThreadLocalÖÐ contextHolder.set(context); } return context;} +``` + + + + + + + + + +ÕâÀïµÄÂß¼­»¹ÊDZȽϼòµ¥µÄ + +* Ê×ÏÈÔÚThreadLocal»ñÈ¡£¬»ñÈ¡²»µ½¾Í´´½¨£¬²»È»¾Í·µ»Ø +* È»ºóÔÙMapÖиù¾ÝContextNameÕÒÒ»¸öNode +* ûÓÐÕÒµ½Node¾Í¼ÓËøµÄ·½Ê½£¬´´½¨Ò»¸öEntranceNode£¬È»ºó·ÅÈëMapÖÐ +* ´´½¨Context£¬ÉèÖÃnode£¬name£¬origin£¬ÔÙ·ÅÈëThreadLocalÖÐ + µ½´ËContext¾Í´´½¨Íê³É + +ĿǰContext¶ÔÏóµÄ״̬ÈçÏÂͼ![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a114ee154527b7fcf24169d5291c7bac87ac93.png "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")**2.2 ¹¹½¨slotÁ´** +¹¹½¨Ò»¸öslotÁ´£¬Á´Â·µÄ×é³ÉΪ + +> DefaultProcessorSlotChain -> NodeSelectorSlot -> ClusterBuilderSlot -> LogSlot ->StatisticSlot -> AuthoritySlot -> SystemSlot -> ParamFlowSlot -> FlowSlot -> DegradeSlot + + + +``` +ProcessorSlot lookProcessChain(ResourceWrapper resourceWrapper) { //¿ÉÒÔ¿´³ö£¬chainÁ´¸ù¾Ý×ÊÔ´À´×÷Ϊkey£¬²»Í¬µÄ×ÊÔ´¿Ï¶¨ÊDz»Í¬chainÁ´ ProcessorSlotChain chain = chainMap.get(resourceWrapper); if (chain == null) {////ÕâÀïÓëspring(»º´æbean) dubbo(Ë«ÖØ¼ì²éËø)ÖÐÈç³öÒ»ÕÞ£¬²ÉÓûº´æ»úÖÆ synchronized (LOCK) { chain = chainMap.get(resourceWrapper); if (chain == null) { //chainMap´óС´óÓÚÒ»¸öÖµ£¬Ò²¾ÍÊÇentryÊýÁ¿´óСÏÞÖÆÁË£¬Ò»¸öchain¶ÔÓ¦Ò»¸öentry if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) { return null; } //¹¹½¨Ò»¸öslot chain chain = SlotChainProvider.newSlotChain(); //ÕâÀïµÄÂß¼­ÊÇ£¬Ð½¨Ò»¸öMap´óСÊÇoldMap+1 Map newMap = new HashMap( chainMap.size() + 1); //È»ºóÏÈÕûÌå·ÅÈëoldMap£¬ÔÙ·Åн¨µÄchain newMap.putAll(chainMap); newMap.put(resourceWrapper, chain); //Ìí¼Óµ½newMap£¬ ÕâÀïÓ¦¸ÃÊÇ¿¼ÂDZÜÃâÆµ·±À©ÈÝ chainMap = newMap; } } } return chain;} +``` + + + + + + + + + +ÕâÀïµÄ´úÂëºÜÇåÎú¿ÉÒÔ·¢ÏÖ£¬Ê×ÏÈ´Ó»º´æÖлñÈ¡¸Ã´¦ÀíÁ´£¬¶øµÚÒ»´Î½øÀ´¿Ï¶¨ÊÇûÓеģ¬ËùÒÔÕâÀï»á×ß SlotChainProvider È¥¹¹Ôì´¦ÀíÁ´£¬¹¹ÔìÍê³Éºó½«Æð·ÅÈ뻺´æÒÔ±¸Ï´ÎʹÓ㺠+ + + +``` +public static ProcessorSlotChain newSlotChain() { if (slotChainBuilder != null) { return slotChainBuilder.build(); } // ÕâÀïͨ¹ýspi»úÖÆÈ¥´´½¨´¦ÀíÁ´£¬Èç¹ûÏë×Ô¼º´´½¨slotµÄ»°Ö»ÐèÒª°´ÕÕSPI»úÖÆÊµÏÖSlotChainBuilder½Ó¿Ú¾ÍºÃ //SentinelĬÈϵÄÁ´ÔÚsentinel-core°üϵÄMETA-INF.servicesÏ slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class); if (slotChainBuilder == null) { // Should not go through here. RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default"); slotChainBuilder = new DefaultSlotChainBuilder(); } else { RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: " + slotChainBuilder.getClass().getCanonicalName()); } return slotChainBuilder.build();} +``` + + + + + + + + + +Õâ¸ö·½·¨½øÐÐÁ˶à´ÎµÄУÑ飬ȷ±£builder ²»Îª¿Õ£¬È»ºóͨ¹ýÆäÈ¥¹¹ÔìÕâ¸ö´¦ÀíÁ´£º + + + +``` +public class DefaultSlotChainBuilder implements SlotChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain; }} +``` + + + + + + + + + +µ½ÁËÕâÀïÎÒÃÇÖÕÓÚ·¢ÏÖÁËÕâ¸ö´¦ÀíÁ´µÄ×é³ÉÇé¿ö£¬¹ÙÍøÒ²ÓÐ¶ÔÆä½øÐÐ˵Ã÷£¬±Ï¾¹ÊÇSentinelµÄÏÞÁ÷ºËÐÄËã·¨µÄʵÏÖ¸¹µØ£¬ÎÒÃÇ¿´Ò»Ï¹ÙÍøµÄ½éÉÜ£º + +ÔÚ Sentinel ÀïÃæ£¬ËùÓеÄ×ÊÔ´¶¼¶ÔÓ¦Ò»¸ö×ÊÔ´Ãû³Æ£¨resourceName£©£¬Ã¿´Î×ÊÔ´µ÷Óö¼»á´´½¨Ò»¸ö Entry ¶ÔÏó¡£Entry ¿ÉÒÔͨ¹ý¶ÔÖ÷Á÷¿ò¼ÜµÄÊÊÅä×Ô¶¯´´½¨£¬Ò²¿ÉÒÔͨ¹ý×¢½âµÄ·½Ê½»òµ÷Óà SphU API ÏÔʽ´´½¨¡£Entry ´´½¨µÄʱºò£¬Í¬Ê±Ò²»á´´½¨Ò»ÏµÁй¦Äܲå²Û£¨slot chain£©£¬ÕâЩ²å²ÛÓв»Í¬µÄÖ°Ôð¡£¾ßÌåÖ°ÔðÔÚÉÏÃæÒѾ­Ìáµ½ÁË¡£ + +**ÕûÌåÁ÷³Ì** + +ÕûÌåµÄÖ´ÐÐÁ÷³ÌÈçÏ¡£ + +* NodeSelectorSlot£ºÖ÷ÒªÓÃÓÚ¹¹½¨µ÷ÓÃÁ´¡£ + +* ClusterBuilderSlot£ºÓÃÓÚ¼¯ÈºÏÞÁ÷¡¢ÈÛ¶Ï¡£ + +* LogSlot£ºÓÃÓڼǼÈÕÖ¾¡£ + +* StatisticSlot£ºÓÃÓÚʵʱÊÕ¼¯ÊµÊ±ÏûÏ¢¡£ + +* AuthoritySlot£ºÓÃÓÚȨÏÞУÑéµÄ¡£ + +* SystemSlot£ºÓÃÓÚÑé֤ϵͳ¼¶±ðµÄ¹æÔò¡£ + +* FlowSlot£ºÊµÏÖÏÞÁ÷»úÖÆ¡£ + +* DegradeSlot£ºÊµÏÖÈÛ¶Ï»úÖÆ¡£ + **2.3 ´´½¨Entry** + + > Entry e = new CtEntry(resourceWrapper, chain, context); + + + +``` +CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot chain, Context context) { super(resourceWrapper); this.chain = chain; this.context = context; setUpEntryFor(context);}private void setUpEntryFor(Context context) { // The entry should not be associated to NullContext. if (context instanceof NullContext) { return; } this.parent = context.getCurEntry(); if (parent != null) { ((CtEntry) parent).child = this; } context.setCurEntry(this);} + +``` + + + + + + + + + +µ±µÚÒ»´ÎEntryÉú³ÉµÄʱºò£¬context.getCurEntry±Ø¶¨ÊÇNULL£¬ÄÇôֱ½ÓÖ´ÐÐContext.setCurEntry·½·¨ + +È»ºóÕâ¸öContextµÄ״̬ÈçÏÂͼ ![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/57273a794d51cfc587c923de97943491e9da0b.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÔÙÖ´ÐÐÒ»´ÎеÄSphu.entryºó»áÔÙ´Îн¨Ò»¸öEntry£¬Õâ¸öʱºòcurEntry²»ÊÇnull£¬ÄÇôִÐÐ((CtEntry)parent).child = this; + +½á¹ûÈçÏÂͼ![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c3c36f101ef031102dd270287bf7635e715229.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")¿ÉÒÔ¿´³ö£¬Ô­À´µÄCtEntry±»ÒƳöContext£¬Ð½¨µÄCtEntryºÍ¾ÉCtEntryͨ¹ýÄÚ²¿µÄparentºÍchildÒýÓÃÏàÁ¬ + +**2.4 NodeSelectorSlot** +Õâ¸öÀàÖ÷ÒªÓÃÓÚ¹¹½¨µ÷ÓÃÁ´£¬Õâ¸öÐèÒª½²½âһϣ¬ÔÚºóÐø¹ý³ÌÖлá±È½Ï¹Ø¼ü£¬´úÂëÈçÏ¡£ + + + +``` +@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable { //ÕâÀïÓиö»º´æ£¬¸ù¾ÝcontextµÄÃû×Ö»º´ænode DefaultNode node = map.get(context.getName()); //Ë«ÖØ¼ì²â£¬Ḭ̈߳²È« if (node == null) { synchronized (this) { node = map.get(context.getName()); if (node == null) { //ÕâÀïÉú³ÉµÄÊÇDefaultNode½Úµã node = new DefaultNode(resourceWrapper, null); //ÏÂÃæÕâЩÂß¼­ÊÇ·ÅÈëmapµÄÂß¼­£¬ÒòΪºóÆÚmap±È½Ï´ó£¬ËùÒÔÕâÑù·ÅÈ룬ÐÔÄÜ»á¸ßһЩ HashMap cacheMap = new HashMap(map.size()); cacheMap.putAll(map); cacheMap.put(context.getName(), node); map = cacheMap; // ¹Ø¼üÔÚÕ⣬ÕâÊÇÐ޸ĵ÷ÓÃÁ´Ê÷µÄµØ·½ ((DefaultNode) context.getLastNode()).addChild(node); } } } //Ìæ»»contextÖеÄcurEntryÖеÄcurNode context.setCurNode(node); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +²éѯ»º´æÖÐÊÇ·ñÓÐÕâ¸önodeÕâÀïµÄÂß¼­Ò²ºÜ¼òµ¥ + +* ¸ù¾ÝContextName²éѯ»º´æÊÇ·ñÓÐÕâ¸öNode +* ûÓоÍÉú³ÉÕâ¸öDefaultNode£¬·ÅÈ뻺´æ£¬È»ºó¹¹Ôìµ÷ÓÃÊ÷Á´ +* ContextµÄcurEntryÖеÄcurnodeÉèÖÃΪÕâ¸önode + ÕâÀïÓм¸¸ö¶ÔÏóÒªµ¥¶À˵Ã÷£º + +1¡¢context£º±íʾÉÏÏÂÎÄ£¬Ò»¸öÏ̶߳ÔÓ¦Ò»¸öcontext£¬ÆäÖаüº¬Ò»Ð©ÊôÐÔÈçÏ + +* name£ºÃû×Ö +* entranceNode£ºµ÷ÓÃÁ´Èë¿Ú +* curEntry£ºµ±Ç°entry +* origin£ºµ÷ÓÃÕßÀ´Ô´ +* async£ºÒì²½ + 2¡¢Node£º ±íʾһ¸ö½Úµã£¬Õâ¸ö½Úµã»á±£´æÄ³¸ö×ÊÔ´µÄ¸÷¸öʵʱͳ¼ÆÊý¾Ý£¬Í¨¹ý·ÃÎÊij¸ö½Úµã£¬¾Í¿ÉÒÔ»ñµÃ¶ÔÓ¦×ÊÔ´µÄʵʱ״̬£¬¸ù¾ÝÕâ¸öÐÅÏ¢À´½øÐÐÏÞÁ÷ºÍ½µ¼¶£¬ËüÓм¸ÖÖ½ÚµãÀàÐÍ +* StatisticNode£ºÊµÏÖÁËNode½Ó¿Ú£¬·â×°ÁË»ù´¡µÄÁ÷Á¿Í³¼ÆºÍ»ñÈ¡·½·¨ +* DefaultNode£ºÄ¬ÈϽڵ㣬NodeSelectorSlotÖд´½¨µÄ¾ÍÊÇÕâ¸ö½Úµã£»´ú±íͬ¸ö×ÊÔ´ÔÚ²»Í¬ÉÏÏÂÎÄÖи÷×ÔµÄÁ÷Á¿Çé¿ö +* ClusterNode£º¼¯Èº½Úµã£¬´ú±íͬ¸ö×ÊÔ´ÔÚ²»Í¬ÉÏÏÂÎÄÖÐ×ÜÌåµÄÁ÷Á¿Çé¿ö +* EntranceNode£º¸Ã½Úµã±íʾһ¿Ãµ÷ÓÃÁ´Ê÷µÄÈë¿Ú½Úµã£¬Í¨¹ýËû¿ÉÒÔ»ñÈ¡µ÷ÓÃÁ´Ê÷ÖÐËùÓеÄ×ӽڵ㣻ÿ¸öÉÏÏÂÎͼ»áÓÐÒ»¸öÈë¿Ú½Úµã£¬ÓÃÀ´Í³¼Æµ±Ç°ÉÏÏÂÎĵÄ×ÜÌåÁ÷Á¿Çé¿ö +* OriginNode£ºÊÇÒ»¸öStatisticNodeÀàÐ͵Ľڵ㣬´ú±íÁËͬ¸ö×ÊÔ´ÇëÇóÀ´Ô´µÄÁ÷Á¿Çé¿ö + **2.5 StatisticSlot** + ÔÚÕû¸öslotÁ´Â·ÖУ¬±È½ÏÖØÒªµÄ£¬¾ÍÊÇÁ÷Á¿Êý¾Ýͳ¼ÆÒÔ¼°Á÷Á¿¹æÔò¼ì²âÕâÁ½¸öslot£¬ÎÒÃÇÏÈÀ´·ÖÎöÒ»ÏÂStatisticSlotÕâ¸ö¶ÔÏó¡£ + +StatisticSlotÊÇ Sentinel µÄºËÐŦÄܲå²ÛÖ®Ò»£¬ÓÃÓÚͳ¼ÆÊµÊ±µÄµ÷ÓÃÊý¾Ý¡£ + +* clusterNode£º×ÊԴΨһ±êʶµÄ ClusterNode µÄ runtime ͳ¼Æ +* origin£º¸ù¾ÝÀ´×Ô²»Í¬µ÷ÓÃÕßµÄͳ¼ÆÐÅÏ¢ +* defaultnode: ¸ù¾ÝÉÏÏÂÎÄÌõÄ¿Ãû³ÆºÍ×ÊÔ´ ID µÄ runtime ͳ¼Æ +* Èë¿ÚµÄͳ¼Æ + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try { // ÏȽ»ÓɺóÐøµÄÏÞÁ÷&½µ¼¶µÈprocessorSlot´¦Àí£¬È»ºó¸ù¾Ý´¦Àí½á¹û½øÐÐͳ¼Æ // SentinelÔðÈÎÁ´µÄ¾«»ª£¨²»Ê¹Óà for Ñ­»·±éÀúµ÷Óà ProcessorSlot µÄÔ­Òò£© fireEntry(context, resourceWrapper, node, count, prioritized, args); //Ö´Ðе½ÕâÀï±íʾͨ¹ý¼ì²é£¬²»±»ÏÞÁ÷ // Request passed, add thread count and pass count. node.increaseThreadNum(); //µ±Ç°½ÚµãµÄÇëÇóÏß³ÌÊý¼Ó1 node.addPassRequest(count); //Õë¶Ô²»Í¬ÀàÐ͵Änode¼Ç¼Ïß³ÌÊýÁ¿ºÍÇëÇóͨ¹ýÊýÁ¿µÄͳ¼Æ¡£ if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); context.getCurEntry().getOriginNode().addPassRequest(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); Constants.ENTRY_NODE.addPassRequest(count); } //¿Éµ÷Óà StatisticSlotCallbackRegistry#addEntryCallback ¾²Ì¬·½·¨×¢²áProcessorSlotEntryCallback for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } //ÓÅÏȼ¶µÈ´ýÒì³££¬Õâ¸öÔÚFlowRuleÖлáÓÐÉæ¼°µ½¡£ } catch (PriorityWaitException ex) {//Ôö¼ÓÏß³Ìͳ¼Æ node.increaseThreadNum(); if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); } // Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } } catch (BlockException e) { // Blocked, set block exception to current entry. context.getCurEntry().setBlockError(e); //ÉèÖÃÏÞÁ÷Òì³£µ½µ±Ç°entryÖÐ // Add block count. node.increaseBlockQps(count); //Ôö¼Ó±»ÏÞÁ÷µÄÊýÁ¿ //¸ù¾Ý²»Í¬NodeÀàÐÍÔö¼Ó×èÈûÏÞÁ÷µÄ´ÎÊý if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().increaseBlockQps(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseBlockQps(count); } // Handle block event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onBlocked(e, context, resourceWrapper, node, count, args); } throw e; } catch (Throwable e) { // Unexpected internal error, set error to current entry. context.getCurEntry().setError(e); throw e; }} +``` + + + + + + + + + +´úÂë·Ö³ÉÁËÁ½²¿·Ö£¬µÚÒ»²¿·ÖÊÇentry·½·¨£¬¸Ã·½·¨Ê×ÏȻᴥ·¢ºóÐøslotµÄentry·½·¨£¬¼´SystemSlot¡¢FlowSlot¡¢DegradeSlotµÈµÄ¹æÔò£¬Èç¹û¹æÔò²»Í¨¹ý£¬¾Í»áÅ׳öBlockException£¬Ôò»áÔÚnodeÖÐͳ¼Æ±»blockµÄÊýÁ¿¡£·´Ö®»áÔÚnodeÖÐͳ¼ÆÍ¨¹ýµÄÇëÇóÊýºÍÏß³ÌÊýµÈÐÅÏ¢¡£µÚ¶þ²¿·ÖÊÇÔÚexit·½·¨ÖУ¬µ±Í˳ö¸ÃEntryÈë¿Úʱ£¬»áͳ¼ÆrtµÄʱ¼ä£¬²¢¼õÉÙÏß³ÌÊý¡£ + +ÎÒÃÇ¿ÉÒÔ¿´µ½ node.addPassRequest() Õâ¶Î´úÂëÊÇÔÚfireEntryÖ´ÐÐÖ®ºóÖ´Ðеģ¬ÕâÒâζ×Å£¬µ±Ç°ÇëÇóͨ¹ýÁËsentinelµÄÁ÷¿ØµÈ¹æÔò£¬´ËʱÐèÒª½«µ±´ÎÇëÇó¼Ç¼ÏÂÀ´£¬Ò²¾ÍÊÇÖ´ÐÐ node.addPassRequest()ÕâÐдúÂ룬ÎÒÃǸú½øÈ¥¿´¿´£º + +**2.5.1 addPassRequest** +@Overridepublic void addPassRequest(int count) { // µ÷Óø¸ÀࣨStatisticNode£©À´½øÐÐͳ¼Æ super.addPassRequest(count); // ¸ù¾ÝclusterNode »ã×Üͳ¼Æ£¨±³ºóÒ²Êǵ÷Óø¸ÀàStatisticNode£© this.clusterNode.addPassRequest(count);} +Ê×ÏÈÎÒÃÇÖªµÀÕâÀïµÄnodeÊÇÒ»¸ö DefaultNode ʵÀý£¬ÔÚµÚÒ»¸öNodeSelectorSlot µÄentry·½·¨ÖжÔ×ÊÔ´½øÐÐÁË·â×°£¬·â×°³ÉÁËÒ»¸öDefaultNode¡£ + +* DefaultNode£º±£´æ×Åij¸öresourceÔÚij¸öcontextÖеÄʵʱָ±ê£¬Ã¿¸öDefaultNode¶¼Ö¸ÏòÒ»¸öClusterNode +* ClusterNode£º±£´æ×Åij¸öresourceÔÚËùÓеÄcontextÖÐʵʱָ±êµÄ×ܺͣ¬Í¬ÑùµÄresource»á¹²Ïíͬһ¸öClusterNode£¬²»¹ÜËûÔÚÄĸöcontextÖÐ + ·Ö±ðµ÷ÓÃÁ½¸öʱ¼ä´°¿ÚÀ´µÝÔöÇëÇóÊýÁ¿¡£ + +ÄÚ²¿Êµ¼Êµ÷ÓõÄÊÇArrayMetricÀ´½øÐÐÇëÇóÊýÁ¿µÄͳ¼Æ + + + +``` +//°´ÕÕÃëÀ´Í³¼Æ£¬·Ö³ÉÁ½¸ö´°¿Ú£¬Ã¿¸ö´°¿Ú500ms£¬ÓÃÀ´Í³¼ÆQPSprivate transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);//°´ÕÕ·ÖÖÓͳ¼Æ£¬Éú³É60¸ö´°¿Ú£¬Ã¿¸ö´°¿Ú1000msprivate transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);public void addPassRequest(int count) { rollingCounterInSecond.addPass(count); rollingCounterInMinute.addPass(count);} +``` + + + + + + + + + +ÕâÀï²ÉÓõÄÊÇ»¬¶¯´°¿ÚµÄ·½Ê½À´¼Ç¼ÇëÇóµÄ´ÎÊý¡£![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/e4624bb214fd8020d1447098c8cde98a554e59.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")Õû¸öÀàµÄ¹ØÏµÍ¼Êµ¼ÊÉÏÊDZȽÏÇåÎúµÄ£¬ArrayMetricʵ¼ÊÉÏÊÇÒ»¸ö°ü×°À࣬ÄÚ²¿Í¨¹ýLeapArrayÀ´ÊµÏÖ¾ßÌåµÄͳ¼ÆÂß¼­£¬¶øLeapArrayÖÐά»¤Á˶à¸öWindowWrap£¨»¬¶¯´°¿Ú£©£¬¶øWindowWrapÖвÉÓÃÁËMetricBucketÀ´½øÐÐÖ¸±êÊý¾ÝµÄͳ¼Æ¡£ + +* Metric: Ö¸±êÊÕ¼¯µÄ½Ó¿Ú£¬¶¨Ò廬¶¯´°¿ÚÖгɹ¦ÊýÁ¿¡¢Òì³£ÊýÁ¿¡¢×èÈûÊýÁ¿¡¢TPS¡¢ÏìӦʱ¼äµÈÊý¾Ý +* ArrayMetric »¬¶¯´°¿ÚºËÐÄʵÏÖÀà +* LeapArray +* WindowWrap ÿһ¸ö»¬¶¯´°¿ÚµÄ°ü×°À࣬ÄÚ²¿µÄÊý¾Ý½á¹¹²ÉÓÃMetricBucket +* MetricBucket£¬ ±íʾָ±êͰ£¬°üº¬×èÈûÊýÁ¿¡¢Òì³£ÊýÁ¿¡¢³É¹¦Êý¡¢ÏìӦʱ¼äµÈ +* MetricEvent Ö¸±êÀàÐÍ£¬Í¨¹ýÊý¡¢×èÈûÊý¡¢Òì³£Êý¡¢³É¹¦ÊýµÈ + **2.5.2 ArrayMetric.addPass** + ¼ÌÐøÑØ×Å´úÂëÍùÏ¿´£¬½øÈëµ½ArrayMetric.addPass·½·¨¡£ +* ´ÓLeapArrayÖиù¾Ýµ±Ç°Ê±¼äµãµÃµ½¶ÔÓ¦µÄ´°¿Ú +* µ÷ÓÃMetricBucketÖеÄaddPass·½·¨£¬Ôö¼Óµ±Ç°´°¿ÚÖеÄͳ¼Æ´ÎÊý + +´Ó´úÂëÖÐÎÒÃÇ¿ÉÒÔ¿´µ½£¬Ôö¼ÓÖ¸±êµ÷Óà addPass ÊÇͨ¹ýÒ»¸ö½Ð ArrayMetric µÄÀ࣬ÏÖÔÚÎÒÃÇÔÚ½øÈë ArrayMetric Öп´Ò»Ï¡£¾ßÌåµÄ´úÂëÈçÏÂËùʾ£º + + + +``` +private final LeapArray data;// SAMPLE_COUNT=2 INTERVAL=1000public ArrayMetric(int sampleCount, int intervalInMs) { //ÕâÁ½¸ö²ÎÊý±íʾ£¬»¬¶¯´°¿ÚµÄ´óСÊÇ2¸ö£¬Ã¿Ò»¸ö»¬¶¯´°¿ÚµÄʱ¼äµ¥Î»ÊÇ500ms this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);}public void addPass(int count) { WindowWrap wrap = data.currentWindow(); wrap.value().addPass(count);} +``` + + + + + + + + + +ÕâÀïÖÕÓÚ³öÏÖÁËÓ뻬¶¯´°¿ÚÓÐÄÇôµã¹ØÁªµÄ windowÁË£¬window²»¾ÍÊÇ´°»§ÂÕâÀïͨ¹ý data À´»ñÈ¡µ±Ç°´°¿Ú¡£¶øÕâÀïµÄ´°¿Ú´óСΪ sampleCount=2.ÎÒÃÇ¿ÉÒÔ¿´µ½£¬ÕâÀïÊÇͨ¹ý MetricBucket À´±£´æ¸÷ÏîÖ¸±ê£¬ÆäÖÐά»¤ÁËÒ»¸öͳ¼ÆÊÇÊý×éLongAdder[] counters À´±£´æ£¬¶ø WindowWrap£¬ÎÒÃÇ¿ÉÒÔ¿´µ½Ã¿Ò»¸ö WindowWrap¶ÔÏóÓÉÈý¸ö²¿·Ö×é³É£º + + + +``` +public class WindowWrap {¡¡¡¡// ʱ¼ä´°¿ÚµÄ³¤¶È private final long windowLengthInMs;¡¡¡¡// ʱ¼ä´°¿ÚµÄ¿ªÊ¼Ê±¼ä£¬µ¥Î»ÊǺÁÃë private long windowStart;¡¡¡¡ //ʱ¼ä´°¿ÚµÄÄÚÈÝ£¬ÔÚ WindowWrap ÖÐÊÇÓ÷ºÐͱíʾÕâ¸öÖµµÄ£¬µ«Êµ¼ÊÉϾÍÊÇ MetricBucket Àà private T value; //......Ê¡ÂÔ²¿·Ö´úÂë} +``` + + + + + + + + + +ÔÙ¿´ LeapArray Õâ¸öÀࣺ + + + +``` +public abstract class LeapArray { // ʱ¼ä´°¿ÚµÄ³¤¶È protected int windowLength; // ²ÉÑù´°¿ÚµÄ¸öÊý protected int sampleCount; // ÒÔºÁÃëΪµ¥Î»µÄʱ¼ä¼ä¸ô protected int intervalInMs; // ²ÉÑùµÄʱ¼ä´°¿ÚÊý×é protected AtomicReferenceArray> array; /** * LeapArray¶ÔÏó * @param windowLength ʱ¼ä´°¿ÚµÄ³¤¶È£¬µ¥Î»£ººÁÃë * @param intervalInSec ͳ¼ÆµÄ¼ä¸ô£¬µ¥Î»£ºÃë */ public LeapArray(int windowLength, int intervalInSec) { this.windowLength = windowLength; // ʱ¼ä´°¿ÚµÄ²ÉÑù¸öÊý£¬Ä¬ÈÏΪ2¸ö²ÉÑù´°¿Ú this.sampleCount = intervalInSec * 1000 / windowLength; this.intervalInMs = intervalInSec * 1000;//ÔÚÒÔÃëΪµ¥Î»µÄʱ¼ä´°¿ÚÖУ¬»á³õʼ»¯Á½¸ö³¤¶ÈµÄÊý×飺`AtomicReferenceArray>array`£¬Õâ¸öÊý×é±íʾ»¬¶¯´°¿ÚµÄ´óС¡£ÆäÖУ¬Ã¿¸ö´°¿Ú»áÕ¼ÓÃ500msµÄʱ¼ä¡£ this.array = new AtomicReferenceArray>(sampleCount); }} +``` + + + + + + + + + +¿ÉÒÔºÜÇåÎúµÄ¿´³öÀ´ÔÚ LeapArray Öд´½¨ÁËÒ»¸ö AtomicReferenceArray Êý×飬ÓÃÀ´¶Ôʱ¼ä´°¿ÚÖеÄͳ¼ÆÖµ½øÐвÉÑù¡£Í¨¹ý²ÉÑùµÄͳ¼ÆÖµÔÙ¼ÆËã³öƽ¾ùÖµ£¬¾ÍÊÇÎÒÃÇÐèÒªµÄ×îÖÕµÄʵʱָ±êµÄÖµÁË¡£¿ÉÒÔ¿´µ½ÎÒÔÚÉÏÃæµÄ´úÂëÖÐͨ¹ý×¢ÊÍ£¬±êÃ÷ÁËĬÈϲÉÑùµÄʱ¼ä´°¿ÚµÄ¸öÊýÊÇ2¸ö£¬Õâ¸öÖµÊÇÔõôµÃµ½µÄÄØ£¿ÎÒÃÇ»ØÒäһϠLeapArray ¶ÔÏó´´½¨£¬ÊÇͨ¹ýÔÚ StatisticNode ÖУ¬newÁËÒ»¸ö ArrayMetric£¬È»ºó½«²ÎÊýһ·ÍùÉÏ´«µÝºó´´½¨µÄ£º + + + +``` +private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,IntervalProperty.INTERVAL); +``` + + + + + + + + + +**2.5.3 currentWindow** +ÎÒÃǸú½ø»ñÈ¡µ±Ç°´°¿ÚµÄ·½·¨ data.currentWindow() ÖУº + + + +``` +@Overridepublic WindowWrap currentWindow(long time) { .....//Ê¡ÂÔ²¿·Ö´úÂë //¼ÆË㵱ǰʱ¼äÔÚ»¬¶¯´°¿ÚÖеÄË÷Òý£¬¼ÆË㷽ʽ±È½Ï¼òµ¥£¬µ±Ç°Ê±¼ä³ýÒÔµ¥¸öʱ¼ä´°¿ÚµÄʱ¼ä³¤¶È£¬ÔÙ´ÓÕû¸öʱ¼ä´°¿Ú³¤¶È½øÐÐȡģ¡¡¡¡ int idx = calculateTimeIdx(timeMillis); //¼ÆË㵱ǰʱ¼äÔÚʱ¼ä´°¿ÚÖеĿªÊ¼Ê±¼ä long windowStart = calculateWindowStart(timeMillis); // timeÿÔö¼ÓÒ»¸öwindowLengthµÄ³¤¶È£¬timeId¾Í»áÔö¼Ó1£¬Ê±¼ä´°¿Ú¾Í»áÍùǰ»¬¶¯Ò»¸ö while (true) { // ´Ó²ÉÑùÊý×éÖиù¾ÝË÷Òý»ñÈ¡»º´æµÄʱ¼ä´°¿Ú WindowWrap old = array.get(idx); // arrayÊý×鳤¶È²»Ò˹ý´ó£¬·ñÔòoldºÜ¶àÇé¿ö϶¼ÃüÖв»ÁË£¬¾Í»á´´½¨ºÜ¶à¸öWindowWrap¶ÔÏó //Èç¹ûΪ¿Õ£¬ËµÃ÷´Ë´¦»¹Î´³õʼ»¯ if (old == null) { // Èç¹ûûÓлñÈ¡µ½£¬Ôò´´½¨Ò»¸öÐ嵀 WindowWrap window = new WindowWrap(windowLength, currentWindowStart, new Window()); // ͨ¹ýCAS½«Ð´°¿ÚÉèÖõ½Êý×éÖÐÈ¥ if (array.compareAndSet(idx, null, window)) { // Èç¹ûÄÜÉèÖóɹ¦£¬Ôò½«¸Ã´°¿Ú·µ»Ø return window; } else { // ·ñÔòµ±Ç°Ïß³ÌÈóöʱ¼äƬ£¬µÈ´ý Thread.yield(); } // Èç¹ûµ±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼äÓëoldµÄ¿ªÊ¼Ê±¼äÏàµÈ£¬ÔòÖ±½Ó·µ»Øold´°¿Ú } else if (currentWindowStart == old.windowStart()) { return old; // Èç¹ûµ±Ç°Ê±¼ä´°¿ÚµÄ¿ªÊ¼Ê±¼äÒѾ­³¬¹ýÁËold´°¿ÚµÄ¿ªÊ¼Ê±¼ä£¬Ôò·ÅÆúold´°¿Ú // ²¢½«timeÉèÖÃΪеÄʱ¼ä´°¿ÚµÄ¿ªÊ¼Ê±¼ä£¬´Ëʱ´°¿ÚÏòǰ»¬¶¯ } else if (currentWindowStart > old.windowStart()) { if (addLock.tryLock()) { try { // if (old is deprecated) then [LOCK] resetTo currentTime. return resetWindowTo(old, currentWindowStart); } finally { addLock.unlock(); } } else { Thread.yield(); } // Õâ¸öÌõ¼þ²»¿ÉÄÜ´æÔÚ } else if (currentWindowStart < old.windowStart()) { // Cannot go through here. return new WindowWrap(windowLength, currentWindowStart, new Window()); } }} +``` + + + + + + + + + +´úÂëºÜ³¤£¬ÎÒÃÇÖð²½½«Æä·Ö½â£¬ÎÒÃÇʵ¼Ê¿ÉÒÔ°ÑËû·Ö³ÉÒÔϼ¸²½£º + +1. ¸ù¾Ýµ±Ç°Ê±¼ä£¬Ëã³ö¸Ãʱ¼äµÄtimeId£¬²¢¸ù¾ÝtimeIdËã³öµ±Ç°´°¿ÚÔÚ²ÉÑù´°¿ÚÊý×éÖеÄË÷Òýidx¡£ +2. ¸ù¾Ýµ±Ç°Ê±¼äËã³öµ±Ç°´°¿ÚµÄÓ¦¸Ã¶ÔÓ¦µÄ¿ªÊ¼Ê±¼ätime£¬ÒÔºÁÃëΪµ¥Î»¡£ +3. ¸ù¾ÝË÷Òýidx£¬ÔÚ²ÉÑù´°¿ÚÊý×éÖÐÈ¡µÃÒ»¸öʱ¼ä´°¿Ú¡£ +4. Ñ­»·ÅжÏÖ±µ½»ñÈ¡µ½Ò»¸öµ±Ç°Ê±¼ä´°¿Ú old ¡£ + +* Èç¹ûoldΪ¿Õ£¬Ôò´´½¨Ò»¸öʱ¼ä´°¿Ú£¬²¢½«Ëü²åÈëµ½arrayµÄµÚidx¸öλÖã¬arrayÉÏÃæÒѾ­·ÖÎö¹ýÁË£¬ÊÇÒ»¸ö AtomicReferenceArray¡£ +* Èç¹ûµ±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼ätimeÓëoldµÄ¿ªÊ¼Ê±¼äÏàµÈ£¬ÄÇô˵Ã÷old¾ÍÊǵ±Ç°Ê±¼ä´°¿Ú£¬Ö±½Ó·µ»Øold¡£ +* Èç¹ûµ±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼ätime´óÓÚoldµÄ¿ªÊ¼Ê±¼ä£¬Ôò˵Ã÷old´°¿ÚÒѾ­¹ýʱÁË£¬½«oldµÄ¿ªÊ¼Ê±¼ä¸üÐÂΪ×îÐÂÖµ£ºtime£¬½øÈëÏÂÒ»´ÎµÃÑ­»·ÔÙÅжϵ±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼ätimeÓëoldµÄ¿ªÊ¼Ê±¼äÏàµÈµÄʱºò·µ»Ø¡£ +* Èç¹ûµ±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼ätimeСÓÚoldµÄ¿ªÊ¼Ê±¼ä£¬Êµ¼ÊÉÏÕâÖÖÇé¿öÊDz»¿ÉÄÜ´æÔڵģ¬ÒòΪtimeÊǵ±Ç°Ê±¼ä£¬oldÊǹýÈ¥µÄÒ»¸öʱ¼ä¡£ + ÁíÍâtimeIdÊÇ»áËæ×Åʱ¼äµÄÔö³¤¶øÔö¼Ó£¬µ±Ç°Ê±¼äÿÔö³¤Ò»¸öwindowLengthµÄ³¤¶È£¬timeId¾Í¼Ó1¡£µ«ÊÇidx²»»áÔö³¤£¬Ö»»áÔÚ0ºÍ1Ö®¼ä±ä»»£¬ÒòΪarrayÊý×éµÄ³¤¶ÈÊÇ2£¬Ö»ÓÐÁ½¸ö²ÉÑùʱ¼ä´°¿Ú¡£ÖÁÓÚΪʲôĬÈÏÖ»ÓÐÁ½¸ö²ÉÑù´°¿Ú£¬¸öÈ˾õµÃÒòΪsentinelÊDZȽÏÇáÁ¿µÄ¿ò¼Ü¡£Ê±¼ä´°¿ÚÖб£´æ×źܶàͳ¼ÆÊý¾Ý£¬Èç¹ûʱ¼ä´°¿Ú¹ý¶àµÄ»°£¬Ò»·½Ãæ»áÕ¼Óùý¶àÄڴ棬ÁíÒ»·½ÃæÊ±¼ä´°¿Ú¹ý¶à¾ÍÒâζ×Åʱ¼ä´°¿ÚµÄ³¤¶È»á±äС£¬Èç¹ûʱ¼ä´°¿Ú³¤¶È±äС£¬¾Í»áµ¼ÖÂʱ¼ä´°¿Ú¹ýÓÚÆµ·±µÄ»¬¶¯¡£ÏÈÀ´¿´Ò»ÏÂÆäÖеĵÚÒ»²½¼°µÚ¶þ²½£º + + + +``` +private int calculateTimeIdx(/*@Valid*/ long timeMillis) { // timeÿÔö¼ÓÒ»¸öwindowLengthµÄ³¤¶È£¬timeId¾Í»áÔö¼Ó1£¬Ê±¼ä´°¿Ú¾Í»áÍùǰ»¬¶¯Ò»¸ö long timeId = timeMillis / windowLengthInMs; // idx±»·Ö³É[0,arrayLength-1]ÖеÄijһ¸öÊý£¬×÷ΪarrayÊý×éÖеÄË÷Òý return (int)(timeId % array.length());}protected long calculateWindowStart(/*@Valid*/ long timeMillis) { return timeMillis - timeMillis % windowLengthInMs;} +``` + + + + + + + + + +¸ù¾Ýµ±Ç°Ê±¼ä³ýÓÚ windowLength µÃµ½Ò»¸ö timeId(Ïà²î500ms¼ÆËã³öÀ´µÄÖµ½«ÊÇÒ»ÖµÄ),ÔÙÓÃtimeId¸úÈ¡Ñù´°¿ÚµÄ³¤¶È½øÐÐÒ»¸öȡ죬ÄÇôËýÒ»¶¨»áÂäÔÚ 0£¬1Á½¸öλÖÃµÄÆäÖÐÒ»¸ö¡£È»ºó¸ù¾Ýµ±Ç°Ê±¼äËã³öµ±Ç°´°¿ÚµÄÓ¦¸Ã¶ÔÓ¦µÄ¿ªÊ¼Ê±¼ätime¡£ÓÉÓÚ¸Õ¸Õ¿ªÊ¼µÄʱºò array Êǿյģ¬ÄÇôËý»ñÈ¡µ½µÄoldÓ¦µ±ÊÇnull£¬ÄÇôËû»á´´½¨Ò»¸öеÄʵÀý£¬ÎÒÃÇÓÃͼ¿´Ò»Ï³õʼ»¯µÄ LeapArray£º + +¶ÔÓ¦ÉÏÃæ currentWindow ·½·¨µÄ 4.1 ²½Öè(¼ÙÉèidx=0)£º![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/95b0b64926eae4d2753986af9c978da9980da0.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")µ±»ñÈ¡µ½µÄÊÇnull,ÄÇô³õʼµÄʱºòarraysÊý×éÖÐÖ»ÓÐÒ»¸ö´°¿Ú(¿ÉÄÜÊǵÚÒ»¸ö(idx=0)£¬Ò²¿ÉÄÜÊǵڶþ¸ö(idx=1))£¬Ã¿¸öʱ¼ä´°¿ÚµÄ³¤¶ÈÊÇ500ms£¬Õâ¾ÍÒâζ×ÅÖ»Òªµ±Ç°Ê±¼äÓëʱ¼ä´°¿ÚµÄ²îÖµÔÚ500msÖ®ÄÚ£¬Ê±¼ä´°¿Ú¾Í²»»áÏòǰ»¬¶¯¡£ÀýÈ磬¼ÙÈ統ǰʱ¼ä×ßµ½300»òÕß500ʱ£¬µ±Ç°Ê±¼ä´°¿ÚÈÔÈ»ÊÇÏàͬµÄÄǸö£º + +¶ÔÓ¦ÉÏÃæ currentWindow ·½·¨µÄ 4.2 ²½Ö裺![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/85c047336a2c2186bcc7737a4c28da852cb603.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ʱ¼ä¼ÌÐøÍùǰ×ߣ¬µ±³¬¹ý500msʱ£¬Ê±¼ä´°¿Ú¾Í»áÏòǰ»¬¶¯µ½ÏÂÒ»¸ö£¬Õâʱ¾Í»á¸üе±Ç°´°¿ÚµÄ¿ªÊ¼Ê±¼ä,ʱ¼ä¼ÌÐøÍùǰ×ߣ¬Ö»Òª²»³¬¹ý1000ms£¬Ôòµ±Ç°´°¿Ú²»»á·¢Éú±ä»¯£¬ÆäÖдúÂëʵÏÖÊÇ resetWindowTo ·½·¨£º + + + +``` +protected WindowWrap resetWindowTo(WindowWrap w, long time) { // Update the start time and reset value. // ÖØÖÃwindowStart w.resetTo(time); MetricBucket borrowBucket = borrowArray.getWindowValue(time); if (borrowBucket != null) { w.value().reset(); w.value().addPass((int)borrowBucket.pass()); } else { w.value().reset(); } return w;} +``` + + + + + + + + + +¶ÔÓ¦ÉÏÃæ currentWindow ·½·¨µÄ 4.3 ²½Öè![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/03d6e3886f95f5768e05969d7eb8307c26f381.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")µ±Ê±¼ä¼ÌÐøÍùǰ×ߣ¬µ±Ç°Ê±¼ä³¬¹ý1000msʱ£¬¾Í»áÔٴνøÈëÏÂÒ»¸öʱ¼ä´°¿Ú£¬´ËʱarraysÊý×éÖеĴ°¿Ú½«»áÓÐÒ»¸öʧЧ£¬»áÓÐÁíÒ»¸öеĴ°¿Ú½øÐÐÌæ»»£º![SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/241926a53c2a5b06938844199046d31e593d62.jpg "SpringCloud AlibabaϵÁСª¡ª15SentinelÔ­Àí·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÒÔ´ËÀàÍÆËæ×Åʱ¼äµÄÁ÷ÊÅ£¬Ê±¼ä´°¿ÚÒ²ÔÚ·¢Éú±ä»¯£¬ÔÚµ±Ç°Ê±¼äµãÖнøÈëµÄÇëÇ󣬻ᱻͳ¼Æµ½µ±Ç°Ê±¼ä¶ÔÓ¦µÄʱ¼ä´°¿ÚÖУ¬»Øµ½addpass ·½·¨ÖУº + + + +``` +public void addPass(int count) { WindowWrap wrap = data.currentWindow(); wrap.value().addPass(count);} +``` + + + + + + + + + +»ñÈ¡µ½´°¿ÚÒÔºó»á½øÈëµ½ wrap.value().addPass(count); QPSµÄÔö¼Ó¡£¶øÕâÀïµÄ wrap.value() µÃµ½µÄÊÇ֮ǰÌáµ½µÄ MetricBucket £¬ÔÚ Sentinel ÖÐQPSÏà¹ØÊý¾ÝµÄͳ¼Æ½á¹ûÊÇά»¤ÔÚÕâ¸öÀàµÄ LongAdder[] ÖУ¬×îÖÕÓÉÕâ¸öÖ¸±êÀ´ÓëÎÒÃÇʵÏÖÉèÖúõĹæÔò½øÐÐÆ¥Å䣬²é¿´ÊÇ·ñÏÞÁ÷£¬Ò²¾ÍÊÇ StatisticSlotµÄentry ·½·¨ÖÐµÄ fireEntry(context, resourceWrapper, node, count, prioritized, args); ¶¼ÒªÏȽøÈëµ½ FlowSlotµÄentry·½·¨½øÐÐÏÞÁ÷¹ýÂË£º + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +¿ÉÒÔ¿´µ½ÕâÀïÓиöºÜÖØÒªµÄ·½·¨ checkFlow £¬½øÈ¥¿´¿´£º + + + +``` +public void checkFlow(Function> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { if (ruleProvider == null || resource == null) { return; } Collection rules = ruleProvider.apply(resource.getName()); if (rules != null) { for (FlowRule rule : rules) { if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException(rule.getLimitApp(), rule); } } }} +``` + + + + + + + + + +µ½ÕâÀïÒ»Çж¼Ó¦¸ÃÇåÎúÁË£¬ÕâÀïÄõ½ÁËÎÒÃÇÉèÖÃµÄ FlowRule Ñ­»·Æ¥Åä×ÊÔ´½øÐÐÏÞÁ÷¹ýÂË¡£Õâ¾ÍÊÇSentinel ÄÜ×öµ½ÏÞÁ÷µÄÔ­Òò¡£ + +**2.6 FlowRuleSlot** +Õâ¸ö slot Ö÷Òª¸ù¾ÝÔ¤ÉèµÄ×ÊÔ´µÄͳ¼ÆÐÅÏ¢£¬°´Õչ̶¨µÄ´ÎÐò£¬ÒÀ´ÎÉúЧ¡£Èç¹ûÒ»¸ö×ÊÔ´¶ÔÓ¦Á½Ìõ»òÕß¶àÌõÁ÷¿Ø¹æÔò£¬Ôò»á¸ù¾ÝÈçÏ´ÎÐòÒÀ´Î¼ìÑ飬ֱµ½È«²¿Í¨¹ý»òÕßÓÐÒ»¸ö¹æÔòÉúЧΪֹ: + +* Ö¸¶¨Ó¦ÓÃÉúЧµÄ¹æÔò£¬¼´Õë¶Ôµ÷Ó÷½ÏÞÁ÷µÄ£» +* µ÷Ó÷½Îª other µÄ¹æÔò£» +* µ÷Ó÷½Îª default µÄ¹æÔò¡£ + + + +``` +@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args);} +``` + + + + + + + + + +**2.6.1 checkFlow** +½øÈëµ½FlowRuleChecker.checkFlow·½·¨ÖС£ + +* ¸ù¾Ý×ÊÔ´Ãû³Æ£¬ÕÒµ½ÏÞÁ÷¹æÔòÁбí +* Èç¹ûÏÞÁ÷¹æÔò²»Îª¿Õ£¬Ôò±éÀú¹æÔò£¬µ÷ÓÃcanPassCheck·½·¨½øÐÐУÑé¡£ + + + +``` +public void checkFlow(Function> ruleProvider,ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { if (ruleProvider == null || resource == null) { return; } Collection rules = ruleProvider.apply(resource.getName()); if (rules != null) { for (FlowRule rule : rules) { if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException(rule.getLimitApp(), rule); } } }} +``` + + + + + + + + + +**2.6.2 canPassCheck** +ÅжÏÊÇ·ñÊǼ¯ÈºÏÞÁ÷ģʽ£¬Èç¹ûÊÇ£¬Ôò×ßpassClusterCheck£¬·ñÔò£¬µ÷ÓÃpassLocalCheck·½·¨¡£ + + + +``` +public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context,DefaultNode node, int acquireCount, boolean prioritized) { String limitApp = rule.getLimitApp(); if (limitApp == null) { return true; } if (rule.isClusterMode()) { return passClusterCheck(rule, context, node, acquireCount, prioritized); } return passLocalCheck(rule, context, node, acquireCount, prioritized);} +``` + + + + + + + + + +**2.6.3 passLocalCheck** + +* selectNodeByRequesterAndStrategy£¬¸ù¾ÝÇëÇóºÍ²ßÂÔÀ´»ñµÃNode +* rule.getRater(), ¸ù¾Ý²»Í¬µÄÏÞÁ÷¿ØÖÆÐÐΪÀ´£¬µ÷ÓÃcanPass½øÐÐУÑé¡£ + + + +``` +private static boolean passLocalCheck(FlowRule rule, Context context,DefaultNode node, int acquireCount, boolean prioritized) { Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node); if (selectedNode == null) { return true; } return rule.getRater().canPass(selectedNode, acquireCount, prioritized);} +``` + + + + + + + + + +**2.6.4 DefaultController.canPass** +ͨ¹ýĬÈϵÄÏÞÁ÷ÐÐΪ£¨Ö±½Ó¾Ü¾ø£©£¬½øÐÐÏÞÁ÷Åжϡ£ + + + +``` +@Overridepublic boolean canPass(Node node, int acquireCount, boolean prioritized) { //Ïȸù¾Ýnode»ñÈ¡×ÊÔ´µ±Ç°µÄʹÓÃÊýÁ¿£¬ÕâÀï»á¸ù¾Ýqps»òÕß²¢·¢Êý²ßÂÔÀ´»ñµÃÏà¹ØµÄÖµ int curCount = avgUsedTokens(node); //µ±Ç°ÒÑʹÓõÄÇëÇóÊý¼ÓÉϱ¾´ÎÇëÇóµÄÊýÁ¿ÊÇ·ñ´óÓÚãÐÖµ if (curCount + acquireCount > count) {//Èç¹ûΪtrue£¬ËµÃ÷Ó¦¸Ã±»ÏÞÁ÷ // Èç¹û´ËÇëÇóÊÇÒ»¸ö¸ßÓÅÏȼ¶ÇëÇ󣬲¢ÇÒÏÞÁ÷ÀàÐÍΪqps£¬Ôò²»»áÁ¢¼´Ê§°Ü£¬¶øÊÇÈ¥Õ¼ÓÃδÀ´µÄʱ¼ä´°¿Ú£¬µÈµ½ÏÂÒ»¸öʱ¼ä´°¿Úͨ¹ýÇëÇó¡£ if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) { // long currentTime; long waitInMs; currentTime = TimeUtil.currentTimeMillis(); waitInMs = node.tryOccupyNext(currentTime, acquireCount, count); if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) { node.addWaitingRequest(currentTime + waitInMs, acquireCount); node.addOccupiedPass(acquireCount); sleep(waitInMs); // PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}. throw new PriorityWaitException(waitInMs); } } return false; } return true;} +``` + + + + + + + + + +Ò»µ©±»¾Ü¾ø£¬ÔòÅ׳ö FlowException Òì³£¡£ + +**2.7 PriorityWait** +ÔÚDefaultController.canPassÖУ¬µ÷ÓÃÈçÏ´úÂëÈ¥½èºóÐøµÄ´°¿Ú + + + +``` +node.addWaitingRequest(currentTime + waitInMs, acquireCount);node.addOccupiedPass(acquireCount); +``` + + + + + + + + + +> addWaitingRequest -> ArrayMetric.addWaiting->OccupiableBucketLeapArray.addWaiting + +borrowArray£¬ËüÊÇÒ»¸öFutureBucketLeapArray¶ÔÏó£¬ÕâÀﶨÒåµÄÊÇδÀ´µÄʱ¼ä´°¿Ú£¬È»ºó»ñµÃδÀ´Ê±¼äµÄ´°¿ÚÈ¥Ôö¼Ó¼ÆÊý + + + +``` +@Overridepublic void addWaiting(long time, int acquireCount) { WindowWrap window = borrowArray.currentWindow(time); window.value().add(MetricEvent.PASS, acquireCount);} +``` + + + + + + + + + +×îÖÕ£¬ÔÚStatisticSlot.entryÖУ¬²¶»ñÒì³£ + +Èç¹û´æÔÚÓÅÏȼ¶±È½Ï¸ßµÄÈÎÎñ£¬²¢ÇÒµ±Ç°µÄÇëÇóÒѾ­´ïµ½ãÐÖµ£¬Å׳öÕâ¸öÒì³££¬Êµ¼ÊÉÏÊÇÈ¥Õ¼ÓÃδÀ´µÄÒ»¸öʱ¼ä´°¿ÚÈ¥½øÐмÆÊý£¬Å׳öÕâ¸öÒì³£Ö®ºó£¬»á½øÈëµ½StatisticSlotÖнøÐв¶»ñ¡£È»ºóÖ±½Óͨ¹ý + + + +``` +public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try{ //... } catch (PriorityWaitException ex) { node.increaseThreadNum(); if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); } // Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); } }} +``` + + + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..4ae8cc8 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudConfig\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1049 @@ +ÒªÏëÁ˽âspring cloud config£¬ÎÒÃǾͱØÐëÁ˽âspringbootÖÐenvironment»·¾³µÄ¼ÓÔØ¹ý³Ì¡£ + + + +``` +SpringApplication.run(AppApiApplication.class, args); + +public static ConfigurableApplicationContext run(Class[] primarySources, + String[] args) { + return new SpringApplication(primarySources).run(args); +} + +``` + + + +ÔÚ`SpringApplicationµÄ¹¹Ôì·½·¨ÖÐ`£¬ÓÐÕâôһ¶Î´úÂë + + + +``` +setInitializers((Collection) getSpringFactoriesInstances( + ApplicationContextInitializer.class)); + setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); + +``` + + + + + +``` +Set names = new LinkedHashSet<>( + SpringFactoriesLoader.loadFactoryNames(type, classLoader)); + +``` + + + +Õâ¶Î´úÂëÖоÍÊÇ´ÓMETA-INFĿ¼ÏÂÃæ»ñÈ¡`ApplicationListenerºÍApplicationContextInitializer`ÀàÐ͵ÄËùÓÐÀà¡£ +ÔÚspring-boot.jarÏÂÃæspring.factoriesÖÐÅäÖÃÁË`ConfigFileApplicationListener`Õâ¸öÀ࣬ÅäÖû·¾³Ò²¾ÍÊÇapplicationÎļþµÄ¼ÓÔØ¶¼»áͨ¹ýÕâ¸öÀà + + + +``` +# Application Listeners +org.springframework.context.ApplicationListener=\ +org.springframework.boot.context.config.ConfigFileApplicationListener,\ + +# Run Listeners +org.springframework.boot.SpringApplicationRunListener=\ +org.springframework.boot.context.event.EventPublishingRunListener + +``` + + + +¼ÌÐø½øÈëspringbootÆô¶¯½×¶Î + + + +``` + public ConfigurableApplicationContext run(String... args) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ConfigurableApplicationContext context = null; + Collection exceptionReporters = new ArrayList<>(); + configureHeadlessProperty(); + SpringApplicationRunListeners listeners = getRunListeners(args); + listeners.starting(); + try { + ApplicationArguments applicationArguments = new DefaultApplicationArguments( + args); + ConfigurableEnvironment environment = prepareEnvironment(listeners, + applicationArguments); + configureIgnoreBeanInfo(environment); + Banner printedBanner = printBanner(environment); + context = createApplicationContext(); + exceptionReporters = getSpringFactoriesInstances( + SpringBootExceptionReporter.class, + new Class[] { ConfigurableApplicationContext.class }, context); + prepareContext(context, environment, listeners, applicationArguments, + printedBanner); + refreshContext(context); + afterRefresh(context, applicationArguments); + stopWatch.stop(); + if (this.logStartupInfo) { + new StartupInfoLogger(this.mainApplicationClass) + .logStarted(getApplicationLog(), stopWatch); + } + listeners.started(context); + callRunners(context, applicationArguments); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, listeners); + throw new IllegalStateException(ex); + } + + try { + listeners.running(context); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, null); + throw new IllegalStateException(ex); + } + return context; + } + +``` + + + + + +``` +ConfigurableEnvironment environment = prepareEnvironment(listeners, + applicationArguments); +prepareContext(context, environment, listeners, applicationArguments, + printedBanner); + +``` + + + +ÉÏÃæÕâÁ½¶Î´úÂë¾ÍÊÇ×°ÔØ»·¾³µÄ´úÂë +ÔÚ`prepareEnvironment`·½·¨Öгõʼ»¯ÁË`environment`¡£ + + + +``` + private ConfigurableEnvironment getOrCreateEnvironment() { + if (this.environment != null) { + return this.environment; + } + switch (this.webApplicationType) { + case SERVLET: + return new StandardServletEnvironment(); + case REACTIVE: + return new StandardReactiveWebEnvironment(); + default: + return new StandardEnvironment(); + } + } + +``` + + + +ÒòΪÎÒÃÇservlet·þÎñ£¬ËùÒÔ`Environment`Ϊ`StandardServletEnvironment`£¬²¢ÇÒÔÚ¸¸ÀàµÄµÄ¹¹ÔìÆäÖÐÖ´ÐÐÁË`customizePropertySources`·½·¨ + + + +``` + public AbstractEnvironment() { + customizePropertySources(this.propertySources); + if (logger.isDebugEnabled()) { + logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources); + } + } + +``` + + + + + +``` + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); + propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { + propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); + } + super.customizePropertySources(propertySources); + } + +``` + + + + + +``` + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); + propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); + } + +``` + + + +µ½ÕâÀï,ÎÒÃǵõ½ÁË`StandardServletEnvironment`²¢ÇÒ¼ÓÔØµÄÅäÖÃ˳ÐòÊÇ + +1. servletConfigInitParams +2. servletContextInitParams +3. jndiProperties +4. systemProperties +5. systemEnvironment + + + +``` + protected void configureEnvironment(ConfigurableEnvironment environment, + String[] args) { + configurePropertySources(environment, args); + configureProfiles(environment, args); + } + + protected void configurePropertySources(ConfigurableEnvironment environment, + String[] args) { + MutablePropertySources sources = environment.getPropertySources(); + if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { + sources.addLast( + new MapPropertySource("defaultProperties", this.defaultProperties)); + } + if (this.addCommandLineProperties && args.length > 0) { + String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; + if (sources.contains(name)) { + PropertySource source = sources.get(name); + CompositePropertySource composite = new CompositePropertySource(name); + composite.addPropertySource(new SimpleCommandLinePropertySource( + "springApplicationCommandLineArgs", args)); + composite.addPropertySource(source); + sources.replace(name, composite); + } + else { + sources.addFirst(new SimpleCommandLinePropertySource(args)); + } + } + } + +``` + + + +¶øÕâÀïÎÒÃÇ¿ÉÒÔ¿´µ½ÓÖ¼ÓÈëÁË`defaultProperties`ÅäÖÃÔÚ×îºóÃæÒÔ¼°`SimpleCommandLinePropertySource`ÃüÁîÐвÎÊýÔÚ×îÇ°Ãæ¡£Òò´Ë£¬ÏÖÔÚµÄ˳ÐòÊÇ + +1. SimpleCommandLinePropertySourcemÃüÁîÐÐÅäÖà +2. servletConfigInitParams +3. servletContextInitParams +4. jndiProperties +5. systemProperties +6. systemEnvironment +7. defaultProperties + + + +``` + public void environmentPrepared(ConfigurableEnvironment environment) { + for (SpringApplicationRunListener listener : this.listeners) { + listener.environmentPrepared(environment); + } + } + + @Override + public void environmentPrepared(ConfigurableEnvironment environment) { + this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( + this.application, this.args, environment)); + } + +``` + + + +ÔÚÕâÀï`SpringApplicationRunListener`Ϊ`EventPublishingRunListener`,À´×ÔÓÚÇ°ÃæµÄ`getRunListeners` + + + +``` + private SpringApplicationRunListeners getRunListeners(String[] args) { + Class[] types = new Class[] { SpringApplication.class, String[].class }; + return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( + SpringApplicationRunListener.class, types, this, args)); + } + +``` + + + +¶øÍ¨¹ý¹¹Ôì·½·¨³õʼ»¯µÄʱºò´«ÈëÁËthis£¬ËùÒÔ×îÖÕ`initialMulticaster`ÖеÄlistenerΪapplicationÖеÄlistener + + + +``` + public EventPublishingRunListener(SpringApplication application, String[] args) { + this.application = application; + this.args = args; + this.initialMulticaster = new SimpleApplicationEventMulticaster(); + for (ApplicationListener listener : application.getListeners()) { + this.initialMulticaster.addApplicationListener(listener); + } + } + +``` + + + +ËùÒÔÕâÀï»á·¢Ë͸öʼþ֪ͨ£¬À´µ½ÁË`ConfigFileApplicationListener` + + + +``` + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + onApplicationEnvironmentPreparedEvent( + (ApplicationEnvironmentPreparedEvent) event); + } + if (event instanceof ApplicationPreparedEvent) { + onApplicationPreparedEvent(event); + } + } + +``` + + + + + +``` + private void onApplicationEnvironmentPreparedEvent( + ApplicationEnvironmentPreparedEvent event) { + List postProcessors = loadPostProcessors(); + postProcessors.add(this); + AnnotationAwareOrderComparator.sort(postProcessors); + for (EnvironmentPostProcessor postProcessor : postProcessors) { + postProcessor.postProcessEnvironment(event.getEnvironment(), + event.getSpringApplication()); + } + } + + List loadPostProcessors() { + return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, + getClass().getClassLoader()); + } + +``` + + + +ÔÚÕâÀïÓÐÓиöÀ©Õ¹µã£¬Í¨¹ý»ñÈ¡`spring.factories`ÎļþÖÐÅäÖõÄ`EnvironmentPostProcessor`Ààµ÷ÓÃ`postProcessEnvironment`·½·¨£¬×Ô¼º±¾ÉíÒ²¼ÓÈëµ½ÁË`postProcessors`¼¯ºÏÖУ¬ËùÒÔ×îÖÕÓÖ»á×ßµ½`postProcessors`·½·¨ + + + +``` + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + addPropertySources(environment, application.getResourceLoader()); + } + +``` + + + + + +``` + protected void addPropertySources(ConfigurableEnvironment environment, + ResourceLoader resourceLoader) { + RandomValuePropertySource.addToEnvironment(environment); + new Loader(environment, resourceLoader).load(); + } + + public static void addToEnvironment(ConfigurableEnvironment environment) { + environment.getPropertySources().addAfter( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME)); + logger.trace("RandomValuePropertySource add to Environment"); + } + + public void load() { + this.profiles = new LinkedList<>(); + this.processedProfiles = new LinkedList<>(); + this.activatedProfiles = false; + this.loaded = new LinkedHashMap<>(); + initializeProfiles(); + while (!this.profiles.isEmpty()) { + Profile profile = this.profiles.poll(); + if (profile != null && !profile.isDefaultProfile()) { + addProfileToEnvironment(profile.getName()); + } + load(profile, this::getPositiveProfileFilter, + addToLoaded(MutablePropertySources::addLast, false)); + this.processedProfiles.add(profile); + } + resetEnvironmentProfiles(this.processedProfiles); + load(null, this::getNegativeProfileFilter, + addToLoaded(MutablePropertySources::addFirst, true)); + addLoadedPropertySources(); + } + +``` + + + +ÔÚÕâÀÓÖ½«randomÅäÖüÓÈëµ½systemEnvironmentÖ®ºó£¬ËùÒÔÎÒÃǵÄÅäÖüÓÔØË³Ðò³ÉÁË +defaultProperties + +1. SimpleCommandLinePropertySourcemÃüÁîÐÐÅäÖà +2. servletConfigInitParams +3. servletContextInitParams +4. jndiProperties +5. systemProperties +6. systemEnvironment +7. RandomValuePropertySource +8. defaultProperties + +·¾¶ + +> classpath:/,classpath:/config/,file:./,file:./config/ +> spring.config.location= +> spring.config.additional-location= + +ÎļþÃûĬÈÏΪapplication + +> Èç¹ûÅäÖÃÁËspring.config.name= +> ÎļþÃûÔòΪspring.config.nameÅäÖõÄÃû³Æ + +²¢ÇÒͨ¹ý`load`·½·¨½«ÉÏÊö·¾¶ºÍÎļþÃûµÄÎļþ¼ÓÈëµ½`environmentÖÐ`£¬Èç¹ûÅäÖÃÁË`spring.profiles.active`£¬²¢ÇÒ¼ÓÔØprefix + "-" + profile + fileExtensionÎļþ£¬Ò²¾ÍÊÇ`application-.properties`ºÍ`y`Îļþ + + + +``` + private void load(Profile profile, DocumentFilterFactory filterFactory, + DocumentConsumer consumer) { + getSearchLocations().forEach((location) -> { + boolean isFolder = location.endsWith("/"); + Set names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; + names.forEach( + (name) -> load(location, name, profile, filterFactory, consumer)); + }); + } + + private void load(String location, String name, Profile profile, + DocumentFilterFactory filterFactory, DocumentConsumer consumer) { + if (!StringUtils.hasText(name)) { + for (PropertySourceLoader loader : this.propertySourceLoaders) { + if (canLoadFileExtension(loader, location)) { + load(loader, location, profile, + filterFactory.getDocumentFilter(profile), consumer); + return; + } + } + } + Set processed = new HashSet<>(); + for (PropertySourceLoader loader : this.propertySourceLoaders) { + for (String fileExtension : loader.getFileExtensions()) { + if (processed.add(fileExtension)) { + loadForFileExtension(loader, location + name, "." + fileExtension, + profile, filterFactory, consumer); + } + } + } + } + +``` + + + +ÔÚÕâÀïͨ¹ý`this.propertySourceLoaders`±éÀúÅжÏÊÇ·ñ·ûºÏ¸ÃSourceLoaderµÄºó׺£¬Èç¹û·ûºÏ£¬¾Í½«×ÊÔ´¼ÓÔØ½øÀ´,¶ø`propertySourceLoaders`À´×ÔÓÚ`Loader`¹¹Ôì·½·¨ + + + +``` + Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { + this.environment = environment; + this.resourceLoader = (resourceLoader != null) ? resourceLoader + : new DefaultResourceLoader(); + this.propertySourceLoaders = SpringFactoriesLoader.loadFactories( + PropertySourceLoader.class, getClass().getClassLoader()); + } + +``` + + + + + +``` +# PropertySource Loaders +org.springframework.boot.env.PropertySourceLoader=\ +org.springframework.boot.env.PropertiesPropertySourceLoader,\ +org.springframework.boot.env.YamlPropertySourceLoader + +``` + + + +Òò´Ë¿ÉÒÔ¼ÓÔØ`yml`ÎļþºÍ`properties`Îļþ + + + +``` + private void addLoadedPropertySources() { + MutablePropertySources destination = this.environment.getPropertySources(); + List loaded = new ArrayList<>(this.loaded.values()); + Collections.reverse(loaded); + String lastAdded = null; + Set added = new HashSet<>(); + for (MutablePropertySources sources : loaded) { + for (PropertySource source : sources) { + if (added.add(source.getName())) { + addLoadedPropertySource(destination, lastAdded, source); + lastAdded = source.getName(); + } + } + } + } + +``` + + + +×îÖÕ·ÅÈëµ½ÁË`environment` + +½ÓÏÂÀ´ÎÒÃǾÍÀ´¿´ÏÂSpring cloud config + +ÔÚ`spring-cloud-context`µÄjarÖУ¬`spring.factories`ÖÐÒýÈëÁ˸ö`BootstrapApplicationListener` +²¢ÇÒ¸ÃÅäÖÃÓÅÏÈÓÚ`ConfigFileApplicationListener`£¬Òò´ËÈç¹ûÎÒÃÇÒýÈëÁËspringcloud×é¼þ£¬ÄÇô +ʼþ¼àÌý»áÓÅÏÈ×ß`BootstrapApplicationListener` + + + +``` +# Application Listeners +org.springframework.context.ApplicationListener=\ +org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ + +``` + + + + + +``` + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + ConfigurableEnvironment environment = event.getEnvironment(); + if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, + true)) { + return; + } + // don't listen to events in a bootstrap context + if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + return; + } + ConfigurableApplicationContext context = null; + String configName = environment + .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); + for (ApplicationContextInitializer initializer : event.getSpringApplication() + .getInitializers()) { + if (initializer instanceof ParentContextApplicationContextInitializer) { + context = findBootstrapContext( + (ParentContextApplicationContextInitializer) initializer, + configName); + } + } + if (context == null) { + context = bootstrapServiceContext(environment, event.getSpringApplication(), + configName); + } + apply(context, event.getSpringApplication(), environment); + } + +``` + + + + + +``` + private ConfigurableApplicationContext bootstrapServiceContext( + ConfigurableEnvironment environment, final SpringApplication application, + String configName) { + StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); + MutablePropertySources bootstrapProperties = bootstrapEnvironment + .getPropertySources(); + for (PropertySource source : bootstrapProperties) { + bootstrapProperties.remove(source.getName()); + } + String configLocation = environment + .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); + Map bootstrapMap = new HashMap<>(); + bootstrapMap.put("spring.config.name", configName); + // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail + // force the environment to use none, because if though it is set below in the builder + // the environment overrides it + bootstrapMap.put("spring.main.web-application-type", "none"); + if (StringUtils.hasText(configLocation)) { + bootstrapMap.put("spring.config.location", configLocation); + } + bootstrapProperties.addFirst( + new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); + for (PropertySource source : environment.getPropertySources()) { + if (source instanceof StubPropertySource) { + continue; + } + bootstrapProperties.addLast(source); + } + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + // Use names and ensure unique to protect against duplicates + List names = new ArrayList<>(SpringFactoriesLoader + .loadFactoryNames(BootstrapConfiguration.class, classLoader)); + for (String name : StringUtils.commaDelimitedListToStringArray( + environment.getProperty("spring.cloud.bootstrap.sources", ""))) { + names.add(name); + } + // TODO: is it possible or sensible to share a ResourceLoader? + SpringApplicationBuilder builder = new SpringApplicationBuilder() + .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) + .environment(bootstrapEnvironment) + // Don't use the default properties in this builder + .registerShutdownHook(false).logStartupInfo(false) + .web(WebApplicationType.NONE); + final SpringApplication builderApplication = builder.application(); + if(builderApplication.getMainApplicationClass() == null){ + // gh_425: + // SpringApplication cannot deduce the MainApplicationClass here + // if it is booted from SpringBootServletInitializer due to the + // absense of the "main" method in stackTraces. + // But luckily this method's second parameter "application" here + // carries the real MainApplicationClass which has been explicitly + // set by SpringBootServletInitializer itself already. + builder.main(application.getMainApplicationClass()); + } + if (environment.getPropertySources().contains("refreshArgs")) { + // If we are doing a context refresh, really we only want to refresh the + // Environment, and there are some toxic listeners (like the + // LoggingApplicationListener) that affect global static state, so we need a + // way to switch those off. + builderApplication + .setListeners(filterListeners(builderApplication.getListeners())); + } + List> sources = new ArrayList<>(); + for (String name : names) { + Class cls = ClassUtils.resolveClassName(name, null); + try { + cls.getDeclaredAnnotations(); + } + catch (Exception e) { + continue; + } + sources.add(cls); + } + AnnotationAwareOrderComparator.sort(sources); + builder.sources(sources.toArray(new Class[sources.size()])); + final ConfigurableApplicationContext context = builder.run(); + // gh-214 using spring.application.name=bootstrap to set the context id via + // `ContextIdApplicationContextInitializer` prevents apps from getting the actual + // spring.application.name + // during the bootstrap phase. + context.setId("bootstrap"); + // Make the bootstrap context a parent of the app context + addAncestorInitializer(application, context); + // It only has properties in it now that we don't want in the parent so remove + // it (and it will be added back later) + bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); + mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); + return context; + } + +``` + + + +ÕâÀïÖ÷Òª×öµÄÊÂÇéÓÐÁ½µã + +1. ´´½¨Environment¶ÔÏó£¬ÉèÖÃspring.config.name=bootstrap +2. ´´½¨Ò»¸öеÄSpringApplicationÆô¶¯Æ÷£¬²¢ÇÒÉèÖÃsourcesΪÀ©Õ¹µãϵÄBootstrapConfigurationÅäÖÃÀà + + + +``` +List names = new ArrayList<>(SpringFactoriesLoader + .loadFactoryNames(BootstrapConfiguration.class, classLoader)); + +``` + + + +¼òµ¥µÄ¾ÍÊÇ¿ÉÒÔÀí½âΪ´´½¨ÁËÒ»¸öÐÂµÄÆô¶¯Æ÷£¬Æô¶¯ÀàΪBootstrapConfigurationÅäÖõÄÀà¡£²¢ÇÒͨ¹ýrun·½·¨µÃµ½Á˳õʼ»¯ºóµÄBeanFactoryÈÝÆ÷²¢ÇÒ½«contenxt·â×°³ÉAncestorInitializer¼ÓÈëµ½ÁËÎÒÃÇ×Ô¼ºµÄSpringApplicationÖÐ + + + +``` +application.addInitializers(new AncestorInitializer(context)); + +``` + + + +¶øÔÚ`apply`·½·¨ÖÐ,»ñÈ¡ÁË`ApplicationContextInitializer`ÀàÐ͵ÄËùÓжÔÏó¼ÓÈëµ½ÁËÎÒÃǵ±Ç°µÄ`SpringApplication`ÖÐ + + + +``` + private void apply(ConfigurableApplicationContext context, + SpringApplication application, ConfigurableEnvironment environment) { + @SuppressWarnings("rawtypes") + List initializers = getOrderedBeansOfType(context, + ApplicationContextInitializer.class); + application.addInitializers(initializers + .toArray(new ApplicationContextInitializer[initializers.size()])); + addBootstrapDecryptInitializer(application); + } + +``` + + + +ÔÚ`spring-cloud-context`°üÖУ¬`spring.factories`ÅäÖÃÏÂÃæÕâ¸öÀà + + + +``` +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\ + +``` + + + +µ½ÕâÀïÎÒÃÇÆô¶¯Àà`SpringApplication`ÖÐÒѾ­ÓÐÁË`AncestorInitializer,PropertySourceBootstrapConfiguration`Á½¸öApplicationContextInitializer + +·µ»Øµ½ÎÒÃÇÆô¶¯Àà + + + +``` + private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments, Banner printedBanner) { + context.setEnvironment(environment); + postProcessApplicationContext(context); + applyInitializers(context); + listeners.contextPrepared(context); + if (this.logStartupInfo) { + logStartupInfo(context.getParent() == null); + logStartupProfileInfo(context); + } + + // Add boot specific singleton beans + context.getBeanFactory().registerSingleton("springApplicationArguments", + applicationArguments); + if (printedBanner != null) { + context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); + } + + // Load the sources + Set sources = getAllSources(); + Assert.notEmpty(sources, "Sources must not be empty"); + load(context, sources.toArray(new Object[0])); + listeners.contextLoaded(context); + } + +``` + + + + + +``` + protected void applyInitializers(ConfigurableApplicationContext context) { + for (ApplicationContextInitializer initializer : getInitializers()) { + Class requiredType = GenericTypeResolver.resolveTypeArgument( + initializer.getClass(), ApplicationContextInitializer.class); + Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); + initializer.initialize(context); + } + } + +``` + + + +ÔÚÕâÀï¾Í»áµ÷Óõ½`ApplicationContextInitializer.initialize`·½·¨ +¾Í»áÏÈÀ´µ½`AncestorInitializer` + + + +``` + @Override + public void initialize(ConfigurableApplicationContext context) { + while (context.getParent() != null && context.getParent() != context) { + context = (ConfigurableApplicationContext) context.getParent(); + } + reorderSources(context.getEnvironment()); + new ParentContextApplicationContextInitializer(this.parent) + .initialize(context); + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + if (applicationContext != this.parent) { + applicationContext.setParent(this.parent); + applicationContext.addApplicationListener(EventPublisher.INSTANCE); + } + } + +``` + + + +¿ÉÒÔ¿´µ½ÕâÀォBootStrap´´½¨µÄÈÝÆ÷×÷ΪÎÒÃǵ±Ç°ÈÝÆ÷µÄ¸¸ÈÝÆ÷£¬²¢ÇÒ¸¸ÈÝÆ÷ÖеĶÔÏó¶¼³õʼ»¯ºÃÁË£¬`PropertySourceBootstrapConfiguration`µÄÒ²³õʼ»¯ºÃÁË,²¢ÇÒ¸Ã`ApplicationContextInitializer`¼ÓÈëµ½ÁËÎÒÃÇ×Ô¼ºµÄÆô¶¯ÀàÀïÃæ£¬Òò´Ë»áµ÷Óóõʼ»¯ºÃÁ˵Ä`PropertySourceBootstrapConfiguration.initialize`,¶ø`PropertySourceLocator`ÊÇ×¢Èë½øÀ´µÄ + + + +``` +# Bootstrap components +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\ + +``` + + + + + +``` + @Bean + @ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class) + @ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true) + public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) { + ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator( + properties); + return locator; + } + +``` + + + + + +``` + @Autowired(required = false) + private List propertySourceLocators = new ArrayList<>(); + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + CompositePropertySource composite = new CompositePropertySource( + BOOTSTRAP_PROPERTY_SOURCE_NAME); + AnnotationAwareOrderComparator.sort(this.propertySourceLocators); + boolean empty = true; + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + for (PropertySourceLocator locator : this.propertySourceLocators) { + PropertySource source = null; + source = locator.locate(environment); + if (source == null) { + continue; + } + logger.info("Located property source: " + source); + composite.addPropertySource(source); + empty = false; + } + if (!empty) { + MutablePropertySources propertySources = environment.getPropertySources(); + String logConfig = environment.resolvePlaceholders("${logging.config:}"); + LogFile logFile = LogFile.get(environment); + if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); + } + insertPropertySources(propertySources, composite); + reinitializeLoggingSystem(environment, logConfig, logFile); + setLogLevels(applicationContext, environment); + handleIncludedProfiles(environment); + } + } + +``` + + + +Òò´Ë×îÖÕ»áÀ´µ½`ConfigServicePropertySourceLocator`ÖÐ + + + +``` + private Environment getRemoteEnvironment(RestTemplate restTemplate, + ConfigClientProperties properties, String label, String state) { + String path = "/{name}/{profile}"; + String name = properties.getName(); + String profile = properties.getProfile(); + String token = properties.getToken(); + int noOfUrls = properties.getUri().length; + if (noOfUrls > 1) { + logger.info("Multiple Config Server Urls found listed."); + } + + Object[] args = new String[] { name, profile }; + if (StringUtils.hasText(label)) { + if (label.contains("/")) { + label = label.replace("/", "(_)"); + } + args = new String[] { name, profile, label }; + path = path + "/{label}"; + } + ResponseEntity response = null; + + for (int i = 0; i < noOfUrls; i++) { + Credentials credentials = properties.getCredentials(i); + String uri = credentials.getUri(); + String username = credentials.getUsername(); + String password = credentials.getPassword(); + + logger.info("Fetching config from server at : " + uri); + + try { + HttpHeaders headers = new HttpHeaders(); + addAuthorizationToken(properties, headers, username, password); + if (StringUtils.hasText(token)) { + headers.add(TOKEN_HEADER, token); + } + if (StringUtils.hasText(state) && properties.isSendState()) { + headers.add(STATE_HEADER, state); + } + + final HttpEntity entity = new HttpEntity<>((Void) null, headers); + response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, + Environment.class, args); + } + catch (HttpClientErrorException e) { + if (e.getStatusCode() != HttpStatus.NOT_FOUND) { + throw e; + } + } + catch (ResourceAccessException e) { + logger.info("Connect Timeout Exception on Url - " + uri + + ". Will be trying the next url if available"); + if (i == noOfUrls - 1) + throw e; + else + continue; + } + + if (response == null || response.getStatusCode() != HttpStatus.OK) { + return null; + } + + Environment result = response.getBody(); + return result; + } + + return null; + } + +``` + + + +¿ÉÒÔ¿´µ½×îºó¾ÍÊǵ÷Ó÷þÎñ¶ËµÄ½Ó¿Ú»ñÈ¡×îеÄÅäÖᣠ+¶øÕâ¸öÍⲿÅäÖÃ×îºó»á·Åµ½systemEnvironment֮ǰ£¬Òò´Ë¾Í»á¸²¸Ç±¾µØÅäÖ㬵«ÊÇ¿ÉÒÔͨ¹ý²ÎÊý¿ØÖÆ + + + +``` +@ConfigurationProperties("spring.cloud.config") +public class PropertySourceBootstrapProperties { + + /** + * Flag to indicate that the external properties should override system properties. + * Default true. + */ + private boolean overrideSystemProperties = true; + + /** + * Flag to indicate that {@link #isOverrideSystemProperties() + * systemPropertiesOverride} can be used. Set to false to prevent users from changing + * the default accidentally. Default true. + */ + private boolean allowOverride = true; + + /** + * Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is + * true, external properties should take lowest priority, and not override any + * existing property sources (including local config files). Default false. + */ + private boolean overrideNone = false; + +``` + + + + + +``` +if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone() + && remoteProperties.isOverrideSystemProperties())) { + propertySources.addFirst(composite); + return; + } + if (remoteProperties.isOverrideNone()) { + propertySources.addLast(composite); + return; + } + if (propertySources + .contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) { + if (!remoteProperties.isOverrideSystemProperties()) { + propertySources.addAfter( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + composite); + } + else { + propertySources.addBefore( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + composite); + } + } + else { + propertySources.addLast(composite); + } + +``` + + + +**×ܽá** + +1. À©Õ¹µã + 1.1 `EnvironmentPostProcessor` + ¶Ô»·¾³×ö´¦Àí + 1.2 `PropertySourceLoader` + ½âÎö²»Í¬µÄ¸ñʽµÄÎļþ + 1.2 `ApplicationListener` + spring-cloud°üÖÐͨ¹ýÐÂÔöÀ©Õ¹BootstrapApplicationListener + 1.3 `BootstrapConfiguration` + ͨ¹ýÀ©Õ¹µã¼ÓÔØÆô¶¯Àà + +2. Îļþ¼ÓÔØ¹ý³Ì + ͨ¹ýApplicationListenerʼþ´´½¨Ò»¸öеÄSpringAplictionÆô¶¯ÀཫBootstrapConfigurationÀ©Õ¹Àà×÷ΪÆô¶¯ÅäÖÃÀ࣬Ȼºó»ñµÃÁËÒ»¸ö³õʼ»¯Á˵ÄBootStrapÈÝÆ÷£¬½«¸ÃÈÝÆ÷·â×°³ÉAncestorInitializerÀà¼ÓÈëµ½ÎÒÃÇ×Ô¼ºµÄÆô¶¯ÀàÖУ¬×÷ÓþÍÊǽ«ÎÒÃÇ×Ô¼ºÈÝÆ÷µÄ¸¸ÈÝÆ÷ÉèÖÃΪBootStrapÈÝÆ÷¡£Í¨¹ýBootStrapÈÝÆ÷»ñµÃ³õʼ»¯ºÃºóµÄApplicationContextInitializerÀàÐͶÔÏ󣬶ø¸ÃÀàÀ´×ÔÓÚPropertySourceBootstrapConfigurationÆô¶¯ÅäÖÃÀ࣬¸ÃÀàÖÐÓÖ×¢ÈëÁËPropertySourceLocatorÀ࣬¶øÔÚConfigServiceBootstrapConfigurationÆô¶¯ÅäÖÃÀàÖÐÅäÖÃbean¶ÔÏó + ConfigServicePropertySourceLocator£¬ËùÒÔ×îÖջὫBootStrapÖгõʼºÃµÄPropertySourceBootstrapConfiguration¼ÓÈëµ½ÎÒÃÇ×Ô¼ºµÄÆô¶¯ÀàÖе÷Óã¬×îÖÕµ÷ÓÃinitialize·½·¨È»ºóµ÷ÓÃConfigServicePropertySourceLocator.locate·½·¨È¥Config server·þÎñ»ñÈ¡ÅäÖᣠ+ + + +×÷ÕߣºÓµ±§¹Â¶À_to +Á´½Ó£ºhttps://www.jianshu.com/p/60c6ab0e79d5 +À´Ô´£º¼òÊé +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..0236a13 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudEureka\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1784 @@ +EurekaÔ´Âë·ÖÎö + +**µÚ1Õ ºËÐÄÁ÷³Ì** +**1.1 Eureka×öÁËʲôÊÂ** + +Ê×ÏÈÎÒÃǶ¼Ã÷°×£¬EurekaÊÇÓÃÔÚ×ö·þÎñ×¢²áµÄ£¬¶ø×¢²áÖÐÐÄҪʵÏÖʲô¹¦ÄÜÄØ£¿Õâ¸ö±ØÐëÃ÷È·ÁË¡£ + +1. ¼ÈÈ»ÊÇ×¢²áÖÐÐÄ£¬ÄÇÊ×ÏÈÒªÄܱ£´æ·þÎñµÄip¡¢portµÈÐÅÏ¢°É£¬ÕâÊÇEureka-server±ØÐëÌṩµÄ»ù´¡¹¦ÄÜ¡£ +2. µ±ÎÒ×¢²áÉÏÀ´Ö®ºó£¬»¹ÒªÌṩһЩ¶¯Ì¬¸ÐÖª·þÎñÉÏÏÂÏߵŦÄܰɣ¬Èç¹ûÒ»¸ö·þÎñÉÏÏÂÏߣ¬Eureka-server¶¼²»ÖªµÀ£¬ÄÇÕâ¸öÍæÒâ¶ù¾ÍÒ»ÎIJ»ÖµÁË¡£ +3. µ±Eureka-server¶Ë¸ÐÖªµ½·þÎñµÄ±ä»¯Ö®ºó×ܵÃ֪ͨÏû·Ñ¶Ë°É£¬ÕâÀï¾ÍÇ£³¶µ½Ê±server¶ËÖ÷¶¯Í¨Öªclient¶ËÄØ»¹ÊÇclient¶Ë×Ô¼ºÈ¥À­È¡ÐÅÏ¢ÄØ£¿Õâ¸öÏÖÔÚ²»ÖªµÀ£¬µÈ»áÈ¥¿´Ô´ÂëÑéÖ¤¡£ +4. OK£¬ÉÏÃæµÄ¹¦Äܶ¼ÊµÏÖÁË£¬Eureka»ù±¾ºÏ¸ñÁË£¬ÄÇ»¹ÓÐÒ»²½£¬¼ÈÈ»·þÎñµÄipºÍ¶Ë¿Ú¶¼ÔÚEureka-serverÉÏÃæ£¬ÄÇÎÒÏû·Ñ¶Ëµ÷Ó÷þÎñ¶ËµÄʱºò£¬Í¨ÐÅÊÇÓõÄOpenFeign£¬ÄÇOpenFeignÊÇÔõô֪µÀµ÷ÓÃÄĸö·þÎñµÄ£¿Ö®Ç°¶¼ÊÇдËÀÔÚapplication.propertiesÀïÃæµÄ.ribbon.listOfServersÖУ¬ÏÖÔÚEurekaÔõô×Ô¶¯Ð´½øÈ¥µÄÄØ£¿ +5. ÉÏÃæ4¸ö¹¦ÄÜ»ù±¾ÉÏÍê³ÉÁË×¢²áÖÐÐĸÃÓеŦÄÜ£¬ÄÇÕâ¸öʱºòÎÒÃÇÔÙÀ´Ë¼¿¼Ò»Ï£¬×¢²áÖÐÐÄÓÃÓÚ΢·þÎñÏîÄ¿ÖУ¬ÄÇ×¢²áÖÐÐÄÒ²×÷Ϊһ¸ö·þÎñ£¬ËüÒ²ÐèÒª×ö¼¯ÈºµÄ£¬Õâ¸öʱºòÎÒÃǾÍÒªÏëһϣ¬×ö¼¯ÈºÔõô±£Ö¤Êý¾ÝÒ»ÖÂÐÔ£¬¸Ã»ùÓÚʲôÀíÂÛ£¿ + ÉÏÃæÊÇEurekaҪʵÏÖµÄ×îºËÐĵŦÄÜ£¬ÄÇÕâЩ¹¦ÄÜÌṩ³öÀ´ÁË£¬ÎÒÏîÄ¿Ôõôȥµ÷ÓÃÄØ£¿²»ÄÜÖ±½ÓÈ¥µ÷API°É£¬Õâ¶àÂé·³£¬»¹µÃȥѧһ±éEurekaµÄapi£¬Íê¶¿×ÓÁË¡£ + +Õâ¸öʱºòÎÒÃǾͻáÁªÏëµ½SpringBootµÄ×Ô¶¯×°ÅäºÍStarter×é¼þ¡£ÕâÁ½¸öÍæÒâ¶ù°ïÎÒÃÇÍê³ÉÁ˺ËÐÄbeanµÄ×Ô¶¯×¢È룬µ×²ã¿ÉÒÔÖ±½ÓÄõ½bean£¬È»ºóÔÚStarter×é¼þÖÐÓ¦¸Ã×Ô¶¯°ïÎÒÃǵ÷ÓÃÁËAPIµÄ£»OK£¬ÄǻعýÍ·À´ÎÒ·¢ÏÖ£¬ÎÒµÄEureka-client¾ÍÊÇÒ»¸östarter×é¼þ£¬ºÙºÙ£¬Óе㶫Î÷ÁË¡£Õâ¸öclient¶ËºËÐÄÂß¼­¿Ï¶¨ÊǰïÎÒÃÇ·â×°Á˸÷ÖÖbean£¬È»ºó°ïÎÒÃǵ÷ÓÃÁ˺ËÐÄapiÁË¡£ + +ÕâÀïÕë¶ÔEurekaµÄºËÐŦÄÜÎÒÃÇÔÚ×öÒ»¸ö¸ü¼ò±ãµÄ×ܽ᣺ + +1. ʵÏÖ×¢²á£¬²¢´æÔÚÄÚ´æÖÐ +2. ¶¯Ì¬¸ÐÖª·þÎñµÄ½¡¿µ×´Ì¬ +3. ·þÎñµÄ·¢ÏÖ£¬¼°¶¯Ì¬¸ÐÖª·þÎñµÄ±ä»¯ + **1.2 ºËÐÄÁ÷³ÌÍÆµ¼** + Ã÷È·Á˺ËÐŦÄÜ£¬ÒÔ¼°ÈçºÎµ÷Óõ쬽ÓÏÂÀ´ÎÒÃÇÀ´´óµ¨µØÍƵ¼Ò»ÏºËÐÄÁ÷³Ìͼ£º + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f15564313c2406546821713bcaf3eb0e9ac73d.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø")´óÌåÁ÷³ÌÍÆµ¼³öÀ´Ö®ºó£¬ÎÒÃÇÔÙÀ´Í¨¹ýÔ´Âë×öÒ»¸öÑéÖ¤¡£ + +**µÚ2Õ ԴÂë·ÖÎö** +**2.1 ·þÎñ×¢²áµÄÈë¿Ú** +·þÎñ×¢²áÊÇÔÚspring bootÓ¦ÓÃÆô¶¯µÄʱºò·¢ÆðµÄ¡£¾ßÌåµÄÖ´Ðз¾¶ÎÒÃÇÔÝÇÒ²»¿´£¬ÏȻعËÒ»ÏÂÇ°ÃæÔÛÃǽ²¹ýµÄ֪ʶ¡£ + +ÎÒÃÇ˵spring cloudÊÇÒ»¸öÉú̬£¬ËüÌṩÁËÒ»Ì×±ê×¼£¬ÕâÌ×±ê×¼¿ÉÒÔͨ¹ý²»Í¬µÄ×é¼þÀ´ÊµÏÖ£¬ÆäÖоͰüº¬·þÎñ×¢²á/·¢ÏÖ¡¢ÈÛ¶Ï¡¢¸ºÔؾùºâµÈ£¬ÔÚspring-cloud-commonÕâ¸ö°üÖУ¬ +org.springframework.cloud.client.serviceregistry ·¾¶Ï£¬¿ÉÒÔ¿´µ½Ò»¸ö·þÎñ×¢²áµÄ½Ó¿Ú¶¨ Òå ServiceRegistry ¡£Ëü¾ÍÊǶ¨ÒåÁËspring cloudÖзþÎñ×¢²áµÄÒ»¸ö½Ó¿Ú¡£ + +ÎÒÃÇ¿´Ò»ÏÂËüµÄÀà¹ØÏµÍ¼£¬Õâ¸ö½Ó¿ÚÓÐÒ»¸öΨһµÄʵÏÖ EurekaServiceRegistry ¡£±íʾ²ÉÓõÄÊÇEureka Server×÷Ϊ·þÎñ×¢²áÖÐÐÄ¡£ + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/49d085328febfffcbae4030dac151db2d0bfcb.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø")**2.1.1 ×¢²áµÄʱ»ú** +·þÎñ×¢²áµÄ·¢Æð£¬ÎÒÃÇ¿ÉÒԲ²âÒ»ÏÂÓ¦¸ÃÊÇʲôʱºòÍê³É£¿´ó¼Ò×ÔÒªÏëÏëÆäʵӦ¸Ã²»ÄѲ²⵽£¬·þÎñµÄ×¢²áÈ¡¾öÓÚ·þÎñÊÇ·ñÒѾ­Æô¶¯ºÃÁË¡£¶øÔÚspring bootÖУ¬»áµÈµ½spring ÈÝÆ÷Æô¶¯²¢ÇÒËùÓеÄÅäÖö¼Íê³ÉÖ®ºóÀ´½øÐÐ×¢²á¡£¶øÕâ¸ö¶¯×÷ÔÚspring bootµÄÆô¶¯·½·¨ÖеÄrefreshContextÖÐÍê³É¡£ + +ÎÒÃǹ۲ìÒ»ÏÂfinishRefreshÕâ¸ö·½·¨£¬´ÓÃû×ÖÉÏ¿ÉÒÔ¿´µ½ËüÊÇÓÃÀ´ÌåÏÖÍê³ÉˢеIJÙ×÷£¬Ò²¾ÍÊÇË¢ÐÂÍê³ÉÖ®ºóÒª×öµÄºóÖõIJÙ×÷¡£ËüÖ÷Òª×ö¼¸¸öÊÂÇé + +* Çå¿Õ»º´æ + +* ³õʼ»¯Ò»¸öLifecycleProcessor£¬ÔÚSpringÆô¶¯µÄʱºòÆô¶¯bean£¬ÔÚspring½áÊøµÄʱºòÏú»Ùbean + +* µ÷ÓÃLifecycleProcessorµÄonRefresh·½·¨£¬Æô¶¯ÊµÏÖÁËLifecycle½Ó¿ÚµÄbean + +* ·¢²¼ContextRefreshedEvent + +* ×¢²áBean£¬Í¨¹ýJMX½øÐÐ¼à¿ØºÍ¹ÜÀí + + + + ``` + protected void finishRefresh() { + // Clear context-level resource caches (such as ASM metadata from scanning). + clearResourceCaches(); + // Initialize lifecycle processor for this context. + initLifecycleProcessor(); + // Propagate refresh to lifecycle processor first. + getLifecycleProcessor().onRefresh(); + // Publish the final event. + publishEvent(new ContextRefreshedEvent(this)); + // Participate in LiveBeansView MBean, if active. + LiveBeansView.registerApplicationContext(this); + } + ``` + + + + + + + + + +ÔÚÕâ¸ö·½·¨ÖУ¬ÎÒÃÇÖØµã¹Ø×¢ getLifecycleProcessor().onRefresh() £¬ËüÊǵ÷ÓÃÉúÃüÖÜÆÚ´¦ÀíÆ÷µÄonrefresh·½·¨£¬ÕÒµ½SmartLifecycle½Ó¿ÚµÄËùÓÐʵÏÖÀಢµ÷ÓÃstart·½·¨¡£ + +**2.1.2 SmartLifeCycle** +ÎÒÍØÕ¹Ò»ÏÂSmartLifeCycleÕâ¿éµÄ֪ʶ£¬ SmartLifeCycleÊÇÒ»¸ö½Ó¿Ú£¬µ±SpringÈÝÆ÷¼ÓÔØÍêËùÓеÄBean²¢ÇÒ³õʼ»¯Ö®ºó£¬»á¼ÌÐø»Øµ÷ʵÏÖÁËSmartLifeCycle½Ó¿ÚµÄÀàÖжÔÓ¦µÄ·½·¨£¬±ÈÈ磨start£©¡£ + +ʵ¼ÊÉÏÎÒÃÇ×Ô¼ºÒ²¿ÉÒÔÍØÕ¹£¬±ÈÈçÔÚspringboot¹¤³ÌµÄmain·½·¨Í¬¼¶Ä¿Â¼Ï£¬Ð´Ò»¸ö²âÊÔÀ࣬ʵÏÖSmartLifeCycle½Ó¿Ú£¬²¢ÇÒͨ¹ý @Service ÉùÃ÷Ϊһ¸öbean£¬ÒòΪҪ±»springÈ¥¼ÓÔØ£¬Ê×ÏȵÃÊÇbean¡£ + + + +``` +@Service +public class TestSmartLifeCycle implements SmartLifecycle { + /** + * ·þÎñÆô¶¯ºóÖ´ÐÐ.ÎÞÐèÏÔʾµ÷ÓÃstart·½·¨. + * µ«ÊÇÒÀÀµisAutoStartup()·µ»ØÖµ,Ö»ÓÐisAutoStartup()·µ»ØtrueµÄʱºò,start()²Å»á±»Ö´ÐÐ + */ + @Override + public void start() { + System.out.println("----------start-----------"); + } + /** + * ·þÎñֹͣǰִÐз½·¨ + * ǰÌáÌõ¼þ: isRunning()·µ»Øtrue²Å»á±»Ö´ÐÐ + */ + @Override + public void stop() { + System.out.println("----------stop-----------"); + } + /** + * ·µ»Ø·þÎñÔËÐÐ״̬,Ó°Ïìµ½·þÎñÊÇ·ñµ÷ÓÃstop·½·¨ + * @return + */ + @Override + public boolean isRunning() { + return false; + } + /** + * ÊÇ·ñµ÷ÓÃstart·½·¨,ÐèҪעÒâ + * µ±Ç°·½·¨·µ»ØfalseÊDz»»áÖ´ÐÐstart() + * @return + */ + @Override + public boolean isAutoStartup() { + return true; + } + @Override + public void stop(Runnable runnable) { + stop(); + runnable.run(); + } + /** + * Ö¸¶¨Ö´ÐÐ˳Ðò + * µ±Ç°ÈÝÆ÷Èç¹ûÓжà¸öÀàʵÏÖÁËSmartLifecycle,Ôò°´´Ë·¨·½·¨·µ»ØÖµÅÅÐòÖ´ÐÐ + * @return + */ + @Override + public int getPhase() { + return 0; + } +} +``` + + + + + + + + + +½Ó×Å£¬ÎÒÃÇÆô¶¯spring bootÓ¦Óú󣬿ÉÒÔ¿´µ½¿ØÖÆÌ¨Êä³öÁË start ×Ö·û´®¡£ + +ÎÒÃÇÔÚDefaultLifecycleProcessor.startBeans·½·¨ÉϼÓÒ»¸ödebug£¬¿ÉÒÔºÜÃ÷ÏԵĿ´µ½ÎÒÃÇ×Ô¼º¶¨ÒåµÄTestSmartLifeCycle±»É¨Ãèµ½ÁË£¬²¢ÇÒ×îºó»áµ÷ÓøÃbeanµÄstart·½·¨¡£![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/75b18e528ba1879148c106d9bd3fb61ab9b5bb.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÔÚstartBeans·½·¨ÖУ¬ÎÒÃÇ¿ÉÒÔ¿´µ½ËüÊ×ÏÈ»á»ñµÃËùÓÐʵÏÖÁËSmartLifeCycleµÄBean£¬È»ºó»áÑ­»·µ÷ÓÃʵÏÖÁËSmartLifeCycleµÄbeanµÄstart·½·¨£¬´úÂëÈçÏ¡£ + + + +``` +private void startBeans(boolean autoStartupOnly) { + Map lifecycleBeans = this.getLifecycleBeans(); + Map phases = new HashMap(); + lifecycleBeans.forEach((beanName, bean) -> { + if (!autoStartupOnly || bean instanceof SmartLifecycle && + ((SmartLifecycle)bean).isAutoStartup()) { + int phase = this.getPhase(bean); + DefaultLifecycleProcessor.LifecycleGroup group = + (DefaultLifecycleProcessor.LifecycleGroup)phases.get(phase); + if (group == null) { + group = new DefaultLifecycleProcessor.LifecycleGroup(phase,this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly); + phases.put(phase, group); + } + group.add(beanName, bean); + } + }); + if (!phases.isEmpty()) { + List keys = new ArrayList(phases.keySet()); + Collections.sort(keys); + Iterator var5 = keys.iterator(); + while(var5.hasNext()) { + Integer key = (Integer)var5.next(); + ((DefaultLifecycleProcessor.LifecycleGroup)phases.get(key)).start(); + } + } +} +``` + + + + + + + + + +**2.1.3 doStart** + + + +``` +private void doStart(Map lifecycleBeans, String beanName, boolean autoStartupOnly) { + Lifecycle bean = (Lifecycle)lifecycleBeans.remove(beanName); + if (bean != null && bean != this) { + String[] dependenciesForBean = this.getBeanFactory().getDependenciesForBean(beanName); + String[] var6 = dependenciesForBean; + int var7 = dependenciesForBean.length; + for(int var8 = 0; var8 < var7; ++var8) { + String dependency = var6[var8]; + this.doStart(lifecycleBeans, dependency, autoStartupOnly); + } + if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle)bean).isAutoStartup())) { + if (this.logger.isTraceEnabled()) { + this.logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]"); + } + try { + bean.start(); //´Ëʱ BeanµÄʵÀýÓ¦¸ÃÊÇEurekaAutoServiceRegistration + } catch (Throwable var10) { + throw new ApplicationContextException("Failed to start bean '" + beanName + "'", var10); + } + if (this.logger.isDebugEnabled()) { + this.logger.debug("Successfully started bean '" + beanName + "'"); + } + } + } +} +``` + + + + + + + + + +´Ëʱ£¬bean.start()£¬µ÷ÓõĿÉÄÜÊÇ +EurekaAutoServiceRegistrationÖеÄstart·½·¨£¬ÒòΪºÜÏÔÈ»£¬ËüʵÏÖÁËSmartLifeCycle½Ó¿Ú¡£ + + + +``` +public class EurekaAutoServiceRegistration implements AutoServiceRegistration,SmartLifecycle, Ordered, SmartApplicationListener { + @Override + public void start() { + // only set the port if the nonSecurePort or securePort is 0 and this.port != 0 + if (this.port.get() != 0) { + if (this.registration.getNonSecurePort() == 0) { + this.registration.setNonSecurePort(this.port.get()); + } + if (this.registration.getSecurePort() == 0 && + this.registration.isSecure()) { + this.registration.setSecurePort(this.port.get()); + } + } + // only initialize if nonSecurePort is greater than 0 and it isn't already running + // because of containerPortInitializer below + if (!this.running.get() && this.registration.getNonSecurePort() > 0) { + this.serviceRegistry.register(this.registration); + this.context.publishEvent(new InstanceRegisteredEvent<>(this, + this.registration.getInstanceConfig())); + this.running.set(true); + } + } +} +``` + + + + + + + + + +ÔÚstart·½·¨ÖУ¬ÎÒÃÇ¿ÉÒÔ¿´µ½ +this.serviceRegistry.register Õâ¸ö·½·¨£¬Ëüʵ¼ÊÉϾÍÊÇ·¢Æð·þÎñ×¢²áµÄ»úÖÆ¡£ + +´Ëʱthis.serviceRegistryµÄʵÀý£¬Ó¦¸ÃÊÇ EurekaServiceRegistry £¬ Ô­ÒòÊÇ +EurekaAutoServiceRegistrationµÄ¹¹Ôì·½·¨ÖУ¬»áÓÐÒ»¸ö¸³Öµ²Ù×÷£¬¶øÕâ¸ö¹¹Ôì·½·¨ÊÇÔÚEurekaClientAutoConfiguration Õâ¸ö×Ô¶¯×°ÅäÀàÖб»×°ÅäºÍ³õʼ»¯µÄ£¬´úÂëÈçÏ¡£ + + + +``` +@Bean +@ConditionalOnBean(AutoServiceRegistrationProperties.class) +@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) +public EurekaAutoServiceRegistration eurekaAutoServiceRegistration( + ApplicationContext context, EurekaServiceRegistry registry, + EurekaRegistration registration) { + return new EurekaAutoServiceRegistration(context, registry, registration); +} +``` + + + + + + + + + +**2.2 ·þÎñµÄ×¢²áÁ÷³Ì** +½ÓÏÂÀ´ÎÒÃÇ·ÖÎö·þÎñ×¢²áµÄÁ÷³Ì + + + +``` +public class EurekaAutoServiceRegistration implements AutoServiceRegistration, +SmartLifecycle, Ordered, SmartApplicationListener { + @Override + public void start() { + //Ê¡ÂÔ... + this.serviceRegistry.register(this.registration); + this.context.publishEvent(new InstanceRegisteredEvent<> this,this.registration.getInstanceConfig())); + } +} +``` + + + + + + + + + +this.serviceRegistry.register(this.registration); ·½·¨×îÖÕ»áµ÷Óà + +EurekaServiceRegistry ÀàÖÐµÄ register ·½·¨À´ÊµÏÖ·þÎñ×¢²á + +**2.2.1 register** + + + +``` +@Override +public void register(EurekaRegistration reg) { + maybeInitializeClient(reg); + if (log.isInfoEnabled()) { + log.info("Registering application " + + reg.getApplicationInfoManager().getInfo().getAppName() + + " with eureka with status " + + reg.getInstanceConfig().getInitialStatus()); + } + //ÉèÖõ±Ç°ÊµÀýµÄ״̬£¬Ò»µ©Õâ¸öʵÀýµÄ״̬·¢Éú±ä»¯£¬Ö»Òª×´Ì¬²»ÊÇDOWN£¬ÄÇô¾Í»á±»¼àÌýÆ÷¼àÌý²¢ÇÒÖ´ÐзþÎñ×¢²á¡£ + reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus()); + //ÉèÖý¡¿µ¼ì²éµÄ´¦Àí + reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg.getEurekaClient().registerHealthCheck(healthCheckHandler)); +} +``` + + + + + + + + + +´ÓÉÏÊö´úÂëÀ´¿´£¬×¢²á·½·¨Öв¢Ã»ÓÐÕæÕýµ÷ÓÃEurekaµÄ·½·¨È¥Ö´ÐÐ×¢²á£¬¶øÊǽö½öÉèÖÃÁËÒ»¸ö״̬ÒÔ¼°ÉèÖý¡¿µ¼ì²é´¦ÀíÆ÷¡£ÎÒÃǼÌÐø¿´Ò»Ï +reg.getApplicationInfoManager().setInstanceStatus·½·¨¡£ + + + +``` +public synchronized void setInstanceStatus(InstanceStatus status) { + InstanceStatus next = instanceStatusMapper.map(status); + if (next == null) { + return; + } + InstanceStatus prev = instanceInfo.setStatus(next); + if (prev != null) { + for (StatusChangeListener listener : listeners.values()) { + try { + listener.notify(new StatusChangeEvent(prev, next)); + } catch (Exception e) { + logger.warn("failed to notify listener: {}", listener.getId(),e); + } + } + } +} +``` + + + + + + + + + +ÔÚÕâ¸ö·½·¨ÖУ¬Ëü»áͨ¹ý¼àÌýÆ÷À´·¢²¼Ò»¸ö״̬±ä¸üʼþ¡£ok£¬´ËʱlistenerµÄʵÀýÊÇStatusChangeListener £¬Ò²¾ÍÊǵ÷Óà StatusChangeListener µÄnotify·½·¨¡£Õâ¸öʼþÊÇ´¥·¢Ò»¸ö·þÎñ״̬±ä¸ü£¬Ó¦¸ÃÊÇÓеط½»á¼àÌýÕâ¸öʼþ£¬È»ºó»ùÓÚÕâ¸öʼþ½øÐÐ×¢²á¡£ + +Õâ¸öʱºòÎÒÃÇÒÔΪÕÒµ½ÁË·½Ïò£¬È»ºóµã»÷½øÈ¥Ò»¿´£¬·¢ÏÖËüÊÇÒ»¸ö½Ó¿Ú¡£¶øÇÒÎÒÃÇ·¢ÏÖËüÊǾ²Ì¬µÄÄÚ²¿½Ó¿Ú£¬»¹ÎÞ·¨Ö±½Ó¿´µ½ËüµÄʵÏÖÀà¡£ + +ÒÀÎÒ¶àÄêÔ´ÂëÔĶÁ¾­Ñ飬ÓÚÊÇÓÖÍù»ØÕÒ£¬ÒòΪÎÒ»ù±¾ÉÏÄܲ²⵽һ¶¨ÊÇÔÚij¸öµØ·½×öÁ˳õʼ»¯µÄ¹¤×÷£¬ÓÚÊÇ£¬ÎÒÏëÕÒµ½ +EurekaServiceRegistry.register·½·¨ÖÐµÄ reg.getApplicationInfoManager Õâ¸öʵÀýÊÇʲô£¬¶øÇÒÎÒÃÇ·¢ÏÖApplicationInfoManagerÊÇÀ´×ÔÓÚEurekaRegistrationÕâ¸öÀàÖеÄÊôÐÔ¡£¶øEurekaRegistrationÓÖÊÇÔÚEurekaAutoServiceRegistrationÕâ¸öÀàÖÐʵÀý»¯µÄ¡£ÄÇÎÒÔÚÏ룬ÊDz»ÊÇÔÚ×Ô¶¯×°ÅäÖÐ×öÁËʲô¶«Î÷¡£ÓÚÊÇÕÒµ½EurekaClientAutoConfigurationÕâ¸öÀ࣬¹ûÈ»¿´µ½ÁËBeanµÄһЩ×Ô¶¯×°Å䣬ÆäÖаüº¬ EurekaClient ¡¢ ApplicationInfoMangager ¡¢ EurekaRegistration µÈ¡£ + +**2.2.2 EurekaClientConfiguration** + + + +``` +@Configuration(proxyBeanMethods = false) +@ConditionalOnMissingRefreshScope +protected static class EurekaClientConfiguration { + @Autowired + private ApplicationContext context; + @Autowired + private AbstractDiscoveryClientOptionalArgs optionalArgs; + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(value = EurekaClient.class,search = SearchStrategy.CURRENT) + public EurekaClient eurekaClient(ApplicationInfoManager manager,EurekaClientConfig config) { + return new CloudEurekaClient(manager, config, this.optionalArgs,this.context); + } + @Bean + @ConditionalOnMissingBean(value = ApplicationInfoManager.class,search = SearchStrategy.CURRENT) + public ApplicationInfoManager eurekaApplicationInfoManager( + EurekaInstanceConfig config) { + InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); + return new ApplicationInfoManager(config, instanceInfo); + } + @Bean + @ConditionalOnBean(AutoServiceRegistrationProperties.class) + @ConditionalOnProperty( + value = "spring.cloud.service-registry.auto-registration.enabled", + matchIfMissing = true) + public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,CloudEurekaInstanceConfig + instanceConfig,ApplicationInfoManager applicationInfoManager, @Autowired(required = false) + ObjectProvider healthCheckHandler) { + return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager).with(eurekaClient).with(healthCheckHandler).build(); + } +} +``` + + + + + + + + + +²»ÄÑ·¢ÏÖ£¬ÎÒÃÇËÆºõ¿´µ½ÁËÒ»¸öºÜÖØÒªµÄBeanÔÚÆô¶¯µÄʱºò×öÁË×Ô¶¯×°Å䣬Ҳ¾ÍÊÇCloudEurekaClient ¡£´ÓÃû×ÖÉÏÀ´¿´£¬ÎÒ¿ÉÒÔºÜÈÝÒ×µÄʶ±ð²¢²Â²â³öËüÊÇEureka¿Í»§¶ËµÄÒ»¸ö¹¤¾ßÀ࣬ÓÃÀ´ÊµÏֺͷþÎñ¶ËµÄͨÐÅÒÔ¼°´¦Àí¡£Õâ¸öÊǺܶàÔ´ÂëÒ»¹áµÄÌ×·£¬ÒªÃ´ÔÚ¹¹Ôì·½·¨ÀïÃæÈ¥×öºÜ¶àµÄ³õʼ»¯ºÍһЩºǫִ́ÐеijÌÐò²Ù×÷£¬ÒªÃ´¾ÍÊÇͨ¹ýÒ첽ʼþµÄ·½Ê½À´´¦Àí¡£½Ó×Å£¬ÎÒÃÇ¿´Ò»ÏÂCloudEurekaClientµÄ³õʼ»¯¹ý³Ì£¬ËüµÄ¹¹Ôì·½·¨Öлáͨ¹ý super µ÷Óø¸ÀàµÄ¹¹Ôì·½·¨¡£Ò²¾ÍÊÇDiscoveryClientµÄ¹¹Ôì¡£ + +**2.2.3 CloudEurekaClient** +super(applicationInfoManager, config, args);µ÷Óø¸ÀàµÄ¹¹Ôì·½·¨£¬¶øCloudEurekaClientµÄ¸¸ÀàÊÇDiscoveryClient. + + + +``` +public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,EurekaClientConfig config,AbstractDiscoveryClientOptionalArgs args,ApplicationEventPublisher publisher) { + super(applicationInfoManager, config, args); + this.applicationInfoManager = applicationInfoManager; + this.publisher = publisher; + this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,"eurekaTransport"); + ReflectionUtils.makeAccessible(this.eurekaTransportField); +} +``` + + + + + + + + + +**2.2.4 DiscoveryClient¹¹Ôì** +ÎÒÃÇ¿ÉÒÔ¿´µ½ÔÚ×îÖÕµÄDiscoveryClient¹¹Ôì·½·¨ÖУ¬Óзdz£³¤µÄ´úÂë¡£ÆäʵºÜ¶à´úÂë¿ÉÒÔ²»ÐèÒª¹ØÐÄ£¬´ó²¿·Ö¶¼ÊÇһЩ³õʼ»¯¹¤×÷£¬±ÈÈç³õʼ»¯Á˼¸¸ö¶¨Ê±ÈÎÎñ + +* scheduler + +* heartbeatExecutor ÐÄÌø¶¨Ê±ÈÎÎñ + +* cacheRefreshExecutor ¶¨Ê±È¥Í¬²½·þÎñ¶ËµÄʵÀýÁбí + + + + ``` + DiscoveryClient(ApplicationInfoManager applicationInfoManager,EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider backupRegistryProvider,EndpointRandomizer endpointRandomizer) { + //Ê¡ÂÔ²¿·Ö´úÂë... + //ÊÇ·ñÒª´Óeureka serverÉÏ»ñÈ¡·þÎñµØÖ·ÐÅÏ¢ + if (config.shouldFetchRegistry()) { + this.registryStalenessMonitor = new ThresholdLevelsMetric(this,METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L,480L}); + } else { + this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; + } + //ÊÇ·ñҪע²áµ½eureka serverÉÏ + if (config.shouldRegisterWithEureka()) { + this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this,METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L,120L, 240L, 480L}); + } else { + this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; + } + //Èç¹û²»ÐèҪע²á²¢ÇÒ²»ÐèÒª¸üзþÎñµØÖ· + if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) { + + return; // no need to setup up an network tasks and we are done + } + try { + // default size of 2 - 1 each for heartbeat and cacheRefresh + scheduler = Executors.newScheduledThreadPool(2,new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-%d") + .setDaemon(true) + .build()); + heartbeatExecutor = new ThreadPoolExecutor(1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, + TimeUnit.SECONDS, + new SynchronousQueue(), + new ThreadFactoryBuilder() + .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d") + .setDaemon(true) + .build() + ); // use direct handoff + cacheRefreshExecutor = new ThreadPoolExecutor( + 1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, + TimeUnit.SECONDS, + new SynchronousQueue(), + new ThreadFactoryBuilder() + .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d") + .setDaemon(true) + .build() + ); // use direct handoff + eurekaTransport = new EurekaTransport(); + scheduleServerEndpointTask(eurekaTransport, args); + AzToRegionMapper azToRegionMapper; + if (clientConfig.shouldUseDnsForFetchingServiceUrls()) { + azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig); + } else { + azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig); + } + if (null != remoteRegionsToFetch.get()) { + + azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(",")); + } + instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, + clientConfig.getRegion()); + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize DiscoveryClient!", e); + } + //Èç¹ûÐèҪע²áµ½Eureka server²¢ÇÒÊÇ¿ªÆôÁ˳õʼ»¯µÄʱºòÇ¿ÖÆ×¢²á£¬Ôòµ÷ÓÃregister()·¢Æð·þÎñ×¢²á + if (clientConfig.shouldRegisterWithEureka() && + clientConfig.shouldEnforceRegistrationAtInit()) { + try { + if (!register() ) { + throw new IllegalStateException("Registration error at startup.Invalid server response."); + } + } catch (Throwable th) { + logger.error("Registration error at startup: {}", th.getMessage()); + throw new IllegalStateException(th); + } + } + // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat,instanceInfo replicator, fetch + initScheduledTasks(); + } + ``` + + + + + + + + + +**2.2.5 initScheduledTasks** +initScheduledTasks È¥Æô¶¯Ò»¸ö¶¨Ê±ÈÎÎñ¡£ + +* Èç¹ûÅäÖÃÁË¿ªÆô´Ó×¢²áÖÐÐÄˢзþÎñÁÐ±í£¬Ôò»á¿ªÆôcacheRefreshExecutorÕâ¸ö¶¨Ê±ÈÎÎñ +* Èç¹û¿ªÆôÁË·þÎñ×¢²áµ½Eureka£¬Ôòͨ¹ýÐèÒª×ö¼¸¸öÊÂÇé. + +1. ½¨Á¢ÐÄÌø¼ì²â»úÖÆ + +ͨ¹ýÄÚ²¿ÀàÀ´ÊµÀý»¯StatusChangeListener ʵÀý״̬¼à¿Ø½Ó¿Ú£¬Õâ¸ö¾ÍÊÇÇ°ÃæÎÒÃÇÔÚ·ÖÎöÆô¶¯¹ý³ÌÖÐËù¿´µ½µÄ£¬µ÷ÓÃnotifyµÄ·½·¨£¬Êµ¼ÊÉÏ»áÔÚÕâÀïÌåÏÖ¡£ + + + +``` +private void initScheduledTasks() { + //Èç¹ûÅäÖÃÁË¿ªÆô´Ó×¢²áÖÐÐÄˢзþÎñÁÐ±í£¬Ôò»á¿ªÆôcacheRefreshExecutorÕâ¸ö¶¨Ê±ÈÎÎñ + if (clientConfig.shouldFetchRegistry()) { + // registry cache refresh timer + int registryFetchIntervalSeconds = + clientConfig.getRegistryFetchIntervalSeconds(); + int expBackOffBound = + clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); + scheduler.schedule( + new TimedSupervisorTask( + "cacheRefresh", + scheduler, + cacheRefreshExecutor, + registryFetchIntervalSeconds, + TimeUnit.SECONDS, + expBackOffBound, + new CacheRefreshThread() + ), + registryFetchIntervalSeconds, TimeUnit.SECONDS); + } + //Èç¹û¿ªÆôÁË·þÎñ×¢²áµ½Eureka£¬Ôòͨ¹ýÐèÒª×ö¼¸¸öÊÂÇé + if (clientConfig.shouldRegisterWithEureka()) { + int renewalIntervalInSecs = + instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); + int expBackOffBound = + clientConfig.getHeartbeatExecutorExponentialBackOffBound(); + logger.info("Starting heartbeat executor: " + "renew interval is: {}", + renewalIntervalInSecs); + // Heartbeat timer + scheduler.schedule( + new TimedSupervisorTask( + "heartbeat", + scheduler, + heartbeatExecutor, + renewalIntervalInSecs, + TimeUnit.SECONDS, + expBackOffBound, + new HeartbeatThread() + ), + renewalIntervalInSecs, TimeUnit.SECONDS); + // InstanceInfo replicator ³õʼ»¯Ò»¸ö:instanceInfoReplicator + instanceInfoReplicator = new InstanceInfoReplicator( + this, + instanceInfo, + clientConfig.getInstanceInfoReplicationIntervalSeconds(), + 2); // burstSize + statusChangeListener = new ApplicationInfoManager.StatusChangeListener() + { + @Override + public String getId() { + return "statusChangeListener"; + } + @Override + public void notify(StatusChangeEvent statusChangeEvent) { + if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || + InstanceStatus.DOWN == + statusChangeEvent.getPreviousStatus()) { + // log at warn level if DOWN was involved + logger.warn("Saw local status change event {}", + statusChangeEvent); + } else { + logger.info("Saw local status change event {}", + statusChangeEvent); + } + instanceInfoReplicator.onDemandUpdate(); + } + }; + //×¢²áʵÀý״̬±ä»¯µÄ¼àÌý + if (clientConfig.shouldOnDemandUpdateStatusChange()) { + applicationInfoManager.registerStatusChangeListener(statusChangeListener); + } + //Æô¶¯Ò»¸öʵÀýÐÅÏ¢¸´ÖÆÆ÷£¬Ö÷Òª¾ÍÊÇΪÁË¿ªÆôÒ»¸ö¶¨Ê±Ị̈߳¬Ã¿40ÃëÅжÏʵÀýÐÅÏ¢ÊÇ·ñ±ä¸ü£¬Èç¹û±ä¸üÁËÔòÖØÐÂ×¢²á + instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationInte + rvalSeconds()); + } else { + logger.info("Not registering with Eureka server per configuration"); + } +} +``` + + + + + + + + + +**2.2.6 onDemandUpdate** +Õâ¸ö·½·¨µÄÖ÷Òª×÷ÓÃÊǸù¾ÝʵÀýÊý¾ÝÊÇ·ñ·¢Éú±ä»¯£¬À´´¥·¢·þÎñ×¢²áÖÐÐĵÄÊý¾Ý¡£ + + + +``` +public boolean onDemandUpdate() { + //ÏÞÁ÷ÅÐ¶Ï + if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) { + if (!scheduler.isShutdown()) { + //Ìá½»Ò»¸öÈÎÎñ + scheduler.submit(new Runnable() { + @Override + public void run() { + logger.debug("Executing on-demand update of local InstanceInfo"); + //È¡³ö֮ǰÒѾ­Ìá½»µÄÈÎÎñ£¬Ò²¾ÍÊÇÔÚstart·½·¨ÖÐÌá½»µÄ¸üÐÂÈÎÎñ£¬Èç¹ûÈÎÎñ»¹Ã»ÓÐÖ´ÐÐÍê³É£¬ÔòÈ¡Ïû֮ǰµÄÈÎÎñ¡£ + Future latestPeriodic = scheduledPeriodicRef.get(); + if (latestPeriodic != null && !latestPeriodic.isDone()) { + logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update"); + latestPeriodic.cancel(false);//Èç¹û´ËÈÎÎñδÍê³É£¬¾ÍÁ¢¼´È¡Ïû + } + //ͨ¹ýµ÷ÓÃrun·½·¨£¬ÁîÈÎÎñÔÚÑÓʱºóÖ´ÐУ¬Ï൱ÓÚÖÜÆÚÐÔÈÎÎñÖеÄÒ»´Î + InstanceInfoReplicator.this.run(); + } + }); + return true; + } else { + logger.warn("Ignoring onDemand update due to stopped scheduler"); + return false; + } + } else { + logger.warn("Ignoring onDemand update due to rate limiter"); + return false; + } +} +``` + + + + + + + + + +**2.2.7 run** +run·½·¨Êµ¼ÊÉϺÍÇ°Ãæ×Ô¶¯×°ÅäËùÖ´ÐеķþÎñ×¢²á·½·¨ÊÇÒ»ÑùµÄ£¬Ò²¾ÍÊǵ÷Óà register ·½·¨½øÐзþÎñ×¢²á£¬²¢ÇÒÔÚfinallyÖУ¬Ã¿30s»á¶¨Ê±Ö´ÐÐһϵ±Ç°µÄrun ·½·¨½øÐмì²é¡£ + + + +``` +public void run() { + try { + discoveryClient.refreshInstanceInfo(); + Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); + if (dirtyTimestamp != null) { + discoveryClient.register(); + instanceInfo.unsetIsDirty(dirtyTimestamp); + } + } catch (Throwable t) { + logger.warn("There was a problem with the instance info replicator", t); + } finally { + Future next = scheduler.schedule(this, replicationIntervalSeconds, + TimeUnit.SECONDS); + scheduledPeriodicRef.set(next); + } +} +``` + + + + + + + + + +**2.2.8 register** +×îÖÕ£¬ÎÒÃÇÖÕÓÚÕÒµ½·þÎñ×¢²áµÄÈë¿ÚÁË£¬ +eurekaTransport.registrationClient.register ×îÖÕµ÷ÓõÄÊÇ AbstractJerseyEurekaHttpClient#register(...)`£¬ µ±È»´ó¼ÒÈç¹û×Ô¼ºÈ¥¿´´úÂ룬¾Í»á·¢ÏÖÈ¥µ÷ÓÃ֮ǰÓкܶàÈÆÀ´ÈÆÈ¥µÄ´úÂ룬±ÈÈ繤³§Ä£Ê½¡¢×°ÊÎÆ÷ģʽµÈ¡£ + + + +``` +boolean register() throws Throwable { + logger.info(PREFIX + "{}: registering service...", appPathIdentifier); + EurekaHttpResponse httpResponse; + try { + httpResponse = eurekaTransport.registrationClient.register(instanceInfo); + } catch (Exception e) { + logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier,e.getMessage(), e); + throw e; + } + if (logger.isInfoEnabled()) { + logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier,httpResponse.getStatusCode()); + } + return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode(); +} +``` + + + + + + + + + +ºÜÏÔÈ»£¬ÕâÀïÊÇ·¢ÆðÁËÒ»´ÎhttpÇëÇ󣬷ÃÎÊEureka-ServerµÄapps/${APP_NAME}½Ó¿Ú£¬½«µ±Ç°·þÎñʵÀýµÄÐÅÏ¢·¢Ë͵½Eureka Server½øÐб£´æ¡£ + +ÖÁ´Ë£¬ÎÒÃÇ»ù±¾ÉÏÒѾ­ÖªµÀSpring Cloud Eureka ÊÇÈçºÎÔÚÆô¶¯µÄʱºò°Ñ·þÎñÐÅÏ¢×¢²áµ½Eureka ServerÉϵÄÁË¡£ + + + +``` +public EurekaHttpResponse register(InstanceInfo info) { + String urlPath = "apps/" + info.getAppName(); + ClientResponse response = null; + try { + Builder resourceBuilder = + jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder(); + addExtraHeaders(resourceBuilder); + response = resourceBuilder + .header("Accept-Encoding", "gzip") + .type(MediaType.APPLICATION_JSON_TYPE) + .accept(MediaType.APPLICATION_JSON) + .post(ClientResponse.class, info); + return + anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); + } finally { + if (logger.isDebugEnabled()) { + logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(), + response == null ? "N/A" : response.getStatus()); + } + if (response != null) { + response.close(); + } + } +} +``` + + + + + + + + + +µ«ÊÇ£¬Ëƺõ×ʼµÄÎÊÌ⻹ûÓнâ¾ö£¬Ò²¾ÍÊÇSpring BootÓ¦ÓÃÔÚÆô¶¯Ê±£¬»áµ÷ÓÃstart·½·¨£¬×îÖÕµ÷Óà +StatusChangeListener.notify È¥¸üзþÎñµÄÒ»¸ö״̬£¬²¢Ã»ÓÐÖ±½Óµ÷ÓÃregister·½·¨×¢²á¡£ËùÒÔÎÒÃǼÌÐøÈ¥¿´Ò»Ï statusChangeListener.notify ·½·¨¡£ + +**2.2.9 ·þÎñ×ܽá** +ÖÁ´Ë£¬ÎÒÃÇÖªµÀEureka Client·¢Æð·þÎñ×¢²áʱ£¬ÓÐÁ½¸öµØ·½»áÖ´ÐзþÎñ×¢²áµÄÈÎÎñ + +1. ÔÚSpring BootÆô¶¯Ê±£¬ÓÉÓÚ×Ô¶¯×°Åä»úÖÆ½«CloudEurekaClient×¢Èëµ½ÁËÈÝÆ÷£¬²¢ÇÒÖ´ÐÐÁ˹¹Ôì·½·¨£¬¶øÔÚ¹¹Ôì·½·¨ÖÐÓÐÒ»¸ö¶¨Ê±ÈÎÎñÿ40s»áÖ´ÐÐÒ»´ÎÅжϣ¬ÅжÏʵÀýÐÅÏ¢ÊÇ·ñ·¢ÉúÁ˱仯£¬Èç¹ûÊÇÔò»á·¢Æð·þÎñ×¢²áµÄÁ÷³Ì +2. ÔÚSpring BootÆô¶¯Ê±£¬Í¨¹ýrefresh·½·¨£¬×îÖÕµ÷ÓÃStatusChangeListener.notify½øÐзþÎñ״̬±ä¸üµÄ¼àÌý£¬¶øÕâ¸ö¼àÌýµÄ·½·¨Êܵ½Ê¼þÖ®ºó»áÈ¥Ö´ÐзþÎñ×¢²á¡£ + **2.3 Server¶ËÂß¼­** + ÔÚû·ÖÎöÔ´ÂëʵÏÖ֮ǰ£¬ÎÒÃÇÒ»¶¨ÖªµÀËü¿Ï¶¨¶ÔÇëÇó¹ýÀ´µÄ·þÎñʵÀýÊý¾Ý½øÐÐÁË´æ´¢¡£ÄÇôÎÒÃÇÈ¥Eureka Server¶Ë¿´Ò»Ï´¦ÀíÁ÷³Ì¡£ + +ÇëÇóÈë¿ÚÔÚ£º +com.netflix.eureka.resources.ApplicationResource.addInstance() ¡£ + +´ó¼Ò¿ÉÒÔ·¢ÏÖ£¬ÕâÀïËùÌṩµÄREST·þÎñ£¬²ÉÓõÄÊÇjerseyÀ´ÊµÏֵġ£JerseyÊÇ»ùÓÚJAX-RS±ê×¼£¬ÌṩRESTµÄʵÏÖµÄÖ§³Ö£¬ÕâÀï¾Í²»Õ¹¿ª·ÖÎöÁË¡£ + +**2.3.1 addInstance()** +µ±EurekaClientµ÷ÓÃregister·½·¨·¢Æð×¢²áʱ£¬»áµ÷Óà +ApplicationResource.addInstance·½·¨¡£ + +·þÎñ×¢²á¾ÍÊÇ·¢ËÍÒ»¸öPOSTÇëÇó´øÉϵ±Ç°ÊµÀýÐÅÏ¢µ½Àà ApplicationResource µÄ addInstance·½·¨½øÐзþÎñ×¢²á¡£ + + + +``` +@POST +@Consumes({"application/json", "application/xml"}) +public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) { + logger.debug("Registering instance {} (replication={})", info.getId(), + isReplication); + DataCenterInfo dataCenterInfo = info.getDataCenterInfo(); + if (dataCenterInfo instanceof UniqueIdentifier) { + String dataCenterInfoId = + ((UniqueIdentifier)dataCenterInfo).getId(); + if (this.isBlank(dataCenterInfoId)) { + boolean experimental = "true".equalsIgnoreCase(this.serverConfig.getExperimental("registration.validation.dataCenterInfoId")); + if (experimental) { + String entity = "DataCenterInfo of type " + + dataCenterInfo.getClass() + " must contain a valid id"; + return Response.status(400).entity(entity).build(); + } + if (dataCenterInfo instanceof AmazonInfo) { + AmazonInfo amazonInfo = (AmazonInfo)dataCenterInfo; + String effectiveId = amazonInfo.get(MetaDataKey.instanceId); + if (effectiveId == null) { + amazonInfo.getMetadata().put(MetaDataKey.instanceId.getName(), info.getId()); + } + } else { + logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass()); + } + } + } + this.registry.register(info, "true".equals(isReplication)); + return Response.status(204).build(); +} + +``` + + + + + + + + + +**2.3.2 register** +ÎÒÃÇÏÈÀ´¿´ +PeerAwareInstanceRegistryImplµÄÀà¹ØÏµÍ¼£¬´ÓÀà¹ØÏµÍ¼¿ÉÒÔ¿´³ö£¬PeerAwareInstanceRegistryµÄ×î¶¥²ã½Ó¿ÚΪLeaseManagerÓëLookupService, + +* ÆäÖÐLookupService¶¨ÒåÁË×î»ù±¾µÄ·¢ÏÖʾÀýµÄÐÐΪ +* LeaseManager¶¨ÒåÁË´¦Àí¿Í»§¶Ë×¢²á£¬ÐøÔ¼£¬×¢ÏúµÈ²Ù×÷ + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/897d97d444664479bda0430417ec683f1f079a.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®Eureka-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÔÚ addInstance ·½·¨ÖУ¬×îÖÕµ÷ÓõÄÊÇ +PeerAwareInstanceRegistryImpl.register ·½·¨¡£ + +* leaseDuration ±íʾ×âÔ¼¹ýÆÚʱ¼ä£¬Ä¬ÈÏÊÇ90s£¬Ò²¾ÍÊǵ±·þÎñ¶Ë³¬¹ý90sûÓÐÊÕµ½¿Í»§¶ËµÄÐÄÌø£¬ÔòÖ÷¶¯ÌÞ³ý¸Ã½Úµã + +* µ÷ÓÃsuper.register·¢Æð½Úµã×¢²á + +* ½«ÐÅÏ¢¸´ÖƵ½Eureka Server¼¯ÈºÖÐµÄÆäËû»úÆ÷ÉÏ£¬Í¬²½µÄʵÏÖÒ²ºÜ¼òµ¥£¬¾ÍÊÇ»ñµÃ¼¯ÈºÖеÄËùÓнڵ㣬ȻºóÖð¸ö·¢Æð×¢²á + + + + ``` + public void register(final InstanceInfo info, final boolean isReplication) { + int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; + if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() >0) { + leaseDuration = info.getLeaseInfo().getDurationInSecs(); //Èç¹û¿Í»§¶ËÓÐ×Ô¼º¶¨ÒåÐÄÌø³¬Ê±Ê±¼ä£¬Ôò²ÉÓÿͻ§¶ËµÄʱ¼ä + } + super.register(info, leaseDuration, isReplication); //½Úµã×¢²á + //¸´ÖƵ½Eureka Server¼¯ÈºÖÐµÄÆäËû½Úµã + replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, + null, isReplication); + } + ``` + + + + + + + + + +**2.3.3 AbstractInstanceRegistry.register** +¼òµ¥À´Ëµ£¬Eureka-ServerµÄ·þÎñ×¢²á£¬Êµ¼ÊÉÏÊǽ«¿Í»§¶Ë´«µÝ¹ýÀ´µÄʵÀýÊý¾Ý±£´æµ½Eureka-ServerÖеÄConcurrentHashMapÖС£ + + + +``` +public void register(InstanceInfo registrant, int leaseDuration, boolean + isReplication) { + try { + read.lock(); + //´ÓregistryÖлñµÃµ±Ç°ÊµÀýÐÅÏ¢£¬¸ù¾ÝappName + Map> gMap = + registry.get(registrant.getAppName()); + REGISTER.increment(isReplication); //Ôö¼Ó×¢²á´ÎÊýµ½¼à¿ØÐÅÏ¢ÖÐ + if (gMap == null) {//Èç¹ûµ±Ç°appNameÊǵÚÒ»´Î×¢²á£¬Ôò³õʼ»¯Ò»¸öConcurrentHashMap + final ConcurrentHashMap> gNewMap = new + ConcurrentHashMap>(); + gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap); + if (gMap == null) { + gMap = gNewMap; + } + } + //´ÓgMapÖвéѯÒѾ­´æÔÚµÄLeaseÐÅÏ¢£¬LeaseÖÐÎÄ·­ÒëΪ×âÔ¼£¬Êµ¼ÊÉÏËü°Ñ·þÎñÌṩÕßµÄʵÀýÐÅÏ¢°ü×°³ÉÁËÒ»¸ölease£¬ÀïÃæÌṩÁ˶ÔÓڸķþÎñʵÀýµÄ×âÔ¼¹ÜÀí + Lease existingLease = gMap.get(registrant.getId()); + // µ±instanceÒѾ­´æÔÚÊÇ£¬ºÍ¿Í»§¶ËµÄinstanceµÄÐÅÏ¢×ö±È½Ï£¬Ê±¼ä×îеÄÄǸö£¬ÎªÓÐЧinstanceÐÅÏ¢ + if (existingLease != null && (existingLease.getHolder() != null)) { + Long existingLastDirtyTimestamp = + existingLease.getHolder().getLastDirtyTimestamp(); + Long registrationLastDirtyTimestamp = + registrant.getLastDirtyTimestamp(); + logger.debug("Existing lease found (existing={}, provided={}", + existingLastDirtyTimestamp, registrationLastDirtyTimestamp); + // this is a > instead of a >= because if the timestamps are equal,we still take the remote transmitted + // InstanceInfo instead of the server local copy. + if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) { + logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" + + " than the one that is being registered {}", + existingLastDirtyTimestamp, registrationLastDirtyTimestamp); + logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant"); + registrant = existingLease.getHolder(); + } + } else { + //µ±lease²»´æÔÚʱ£¬½øÈëµ½Õâ¶Î´úÂ룬 + synchronized (lock) { + if (this.expectedNumberOfClientsSendingRenews > 0) { + // Since the client wants to register it, increase the number of clients sending renews + this.expectedNumberOfClientsSendingRenews = + this.expectedNumberOfClientsSendingRenews + 1; + updateRenewsPerMinThreshold(); + } + } + logger.debug("No previous lease information found; it is new registration"); + } + //¹¹½¨Ò»¸ölease + Lease lease = new Lease(registrant, + leaseDuration); + if (existingLease != null) { + // µ±Ô­À´´æÔÚLeaseµÄÐÅϢʱ£¬ÉèÖÃserviceUpTimestamp, ±£Ö¤·þÎñÆô¶¯µÄʱ¼äÒ»Ö±ÊǵÚÒ»´Î×¢²áµÄÄǸö + lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp()); + } + gMap.put(registrant.getId(), lease); + synchronized (recentRegisteredQueue) {//Ìí¼Óµ½×î½ü×¢²áµÄ¶ÓÁÐÖÐ + recentRegisteredQueue.add(new Pair( + System.currentTimeMillis(), + registrant.getAppName() + "(" + registrant.getId() + ")")); + } + // ¼ì²éʵÀý״̬ÊÇ·ñ·¢Éú±ä»¯£¬Èç¹ûÊDz¢ÇÒ´æÔÚ£¬Ôò¸²¸ÇÔ­À´µÄ״̬ + if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) { + logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the " + + "overrides", registrant.getOverriddenStatus(), + registrant.getId()); + if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) { + logger.info("Not found overridden id {} and hence adding it", + registrant.getId()); + overriddenInstanceStatusMap.put(registrant.getId(), + registrant.getOverriddenStatus()); + } + } + InstanceStatus overriddenStatusFromMap = + overriddenInstanceStatusMap.get(registrant.getId()); + if (overriddenStatusFromMap != null) { + logger.info("Storing overridden status {} from map", + overriddenStatusFromMap); + registrant.setOverriddenStatus(overriddenStatusFromMap); + } + // Set the status based on the overridden status rules + InstanceStatus overriddenInstanceStatus = + getOverriddenInstanceStatus(registrant, existingLease, isReplication); + registrant.setStatusWithoutDirty(overriddenInstanceStatus); + // µÃµ½instanceStatus£¬ÅжÏÊÇ·ñÊÇUP״̬£¬ + if (InstanceStatus.UP.equals(registrant.getStatus())) { + lease.serviceUp(); + } + // ÉèÖÃ×¢²áÀàÐÍΪÌí¼Ó + registrant.setActionType(ActionType.ADDED); + // ×âÔ¼±ä¸ü¼Ç¼¶ÓÁУ¬¼Ç¼ÁËʵÀýµÄÿ´Î±ä»¯£¬ ÓÃÓÚ×¢²áÐÅÏ¢µÄÔöÁ¿»ñÈ¡ + recentlyChangedQueue.add(new RecentlyChangedItem(lease)); + registrant.setLastUpdatedTimestamp(); + //Èûº´æÊ§Ð§ + invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), + registrant.getSecureVipAddress()); + logger.info("Registered instance {}/{} with status {} (replication={})", + registrant.getAppName(), registrant.getId(), + registrant.getStatus(), isReplication); + } finally { + read.unlock(); + } +} +``` + + + + + + + + + +**2.3.4 С½á** +ÖÁ´Ë£¬ÎÒÃǾͰѷþÎñ×¢²áÔÚ¿Í»§¶ËºÍ·þÎñ¶ËµÄ´¦Àí¹ý³Ì×öÁËÒ»¸öÏêϸµÄ·ÖÎö£¬Êµ¼ÊÉÏÔÚEureka Server¶Ë£¬»á°Ñ¿Í»§¶ËµÄµØÖ·ÐÅÏ¢±£´æµ½ConcurrentHashMapÖд洢¡£²¢ÇÒ·þÎñÌṩÕߺÍ×¢²áÖÐÐÄÖ®¼ä£¬»á½¨Á¢Ò»¸öÐÄÌø¼ì²â»úÖÆ¡£ÓÃÓÚ¼à¿Ø·þÎñÌṩÕߵĽ¡¿µ×´Ì¬¡£ + +**2.4 Eureka µÄ¶à¼¶»º´æÉè¼Æ** +Eureka Server´æÔÚÈý¸ö±äÁ¿£º(registry¡¢readWriteCacheMap¡¢readOnlyCacheMap)±£´æ·þÎñ×¢²áÐÅÏ¢£¬Ä¬ÈÏÇé¿ö϶¨Ê±ÈÎÎñÿ30s½«readWriteCacheMapͬ²½ÖÁreadOnlyCacheMap£¬Ã¿60sÇåÀí³¬¹ý90sÎ´ÐøÔ¼µÄ½Úµã£¬Eureka Clientÿ30s´ÓreadOnlyCacheMap¸üзþÎñ×¢²áÐÅÏ¢£¬¶ø¿Í»§¶Ë·þÎñµÄ×¢²áÔò´Óregistry¸üзþÎñ×¢²áÐÅÏ¢¡£ + +**2.4.1 ¶à¼¶»º´æµÄÒâÒå** +ÕâÀïΪʲôҪÉè¼Æ¶à¼¶»º´æÄØ£¿Ô­ÒòºÜ¼òµ¥£¬¾ÍÊǵ±´æÔÚ´ó¹æÄ£µÄ·þÎñ×¢²áºÍ¸üÐÂʱ£¬Èç¹ûÖ»ÊÇÐÞ¸ÄÒ»¸öConcurrentHashMapÊý¾Ý£¬ÄÇÃ´ÊÆ±ØÒòÎªËøµÄ´æÔÚµ¼Ö¾ºÕù£¬Ó°ÏìÐÔÄÜ¡£ + +¶øEurekaÓÖÊÇAPÄ£ÐÍ£¬Ö»ÐèÒªÂú×ã×îÖÕ¿ÉÓþÍÐС£ËùÒÔËüÔÚÕâÀïÓõ½¶à¼¶»º´æÀ´ÊµÏÖ¶Áд·ÖÀë¡£×¢²á·½·¨Ð´µÄʱºòÖ±½ÓдÄÚ´æ×¢²á±í£¬Ð´Íê±íÖ®ºóÖ÷¶¯Ê§Ð§¶Áд»º´æ¡£ + +»ñȡע²áÐÅÏ¢½Ó¿ÚÏÈ´ÓÖ»¶Á»º´æÈ¡£¬Ö»¶Á»º´æÃ»ÓÐÔÙÈ¥¶Áд»º´æÈ¡£¬¶Áд»º´æÃ»ÓÐÔÙÈ¥ÄÚ´æ×¢²á±íÀïÈ¡£¨²»Ö»ÊÇÈ¡£¬´Ë´¦½Ï¸´ÔÓ£©¡£²¢ÇÒ£¬¶Áд»º´æ»á¸üлØÐ´Ö»¶Á»º´æ + +* responseCacheUpdateIntervalMs £º readOnlyCacheMap »º´æ¸üÐµĶ¨Ê±Æ÷ʱ¼ä¼ä¸ô£¬Ä¬ÈÏΪ30Ãë +* responseCacheAutoExpirationInSeconds : readWriteCacheMap »º´æ¹ýÆÚʱ¼ä£¬Ä¬ÈÏΪ 180 Ãë¡£ + **2.4.2 ·þÎñ×¢²áµÄ»º´æÊ§Ð§** + ÔÚAbstractInstanceRegistry.register·½·¨µÄ×îºó£¬»áµ÷ÓÃinvalidateCache(registrant.getAppName(), registrant.getVIPAddress(),registrant.getSecureVipAddress()); ·½·¨£¬Ê¹µÃ¶Áд»º´æÊ§Ð§¡£ + + + +``` +public void invalidate(Key... keys) { + for (Key key : keys) { + logger.debug("Invalidating the response cache key : {} {} {} {}, {}", + key.getEntityType(), key.getName(), key.getVersion(), + key.getType(), key.getEurekaAccept()); + readWriteCacheMap.invalidate(key); + Collection keysWithRegions = regionSpecificKeys.get(key); + if (null != keysWithRegions && !keysWithRegions.isEmpty()) { + for (Key keysWithRegion : keysWithRegions) { + logger.debug("Invalidating the response cache key : {} {} {} {} {}", + key.getEntityType(), key.getName(), + key.getVersion(), key.getType(), key.getEurekaAccept()); + readWriteCacheMap.invalidate(keysWithRegion); + } + } + } +} +``` + + + + + + + + + +**2.4.3 ¶¨Ê±Í¬²½»º´æ** +ResponseCacheImplµÄ¹¹Ôì·½·¨ÖУ¬»áÆô¶¯Ò»¸ö¶¨Ê±ÈÎÎñ£¬Õâ¸öÈÎÎñ»á¶¨Ê±¼ì²éд»º´æÖеÄÊý¾Ý±ä»¯£¬½øÐиüкÍͬ²½¡£ + + + +``` +private TimerTask getCacheUpdateTask() { + return new TimerTask() { + @Override + public void run() { + logger.debug("Updating the client cache from response cache"); + for (Key key : readOnlyCacheMap.keySet()) { + if (logger.isDebugEnabled()) { + logger.debug("Updating the client cache from response cache for key : {} {} {} {}", + key.getEntityType(), key.getName(), + key.getVersion(), key.getType()); + } + try { + CurrentRequestVersion.set(key.getVersion()); + Value cacheValue = readWriteCacheMap.get(key); + Value currentCacheValue = readOnlyCacheMap.get(key); + if (cacheValue != currentCacheValue) { + readOnlyCacheMap.put(key, cacheValue); + } + } catch (Throwable th) { + logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th); + } finally { + CurrentRequestVersion.remove(); + } + } + } + }; +} +``` + + + + + + + + + +**2.5 ·þÎñÐøÔ¼** +ËùνµÄ·þÎñÐøÔ¼£¬Æäʵ¾ÍÊÇÒ»ÖÖÐÄÌø¼ì²é»úÖÆ¡£¿Í»§¶Ë»á¶¨ÆÚ·¢ËÍÐÄÌøÀ´ÐøÔ¼¡£ÄÇô¼òµ¥¸ø´ó¼Ò¿´Ò»Ï´úÂëµÄʵÏÖ + +**2.5.1 initScheduledTasks** +¿Í»§¶Ë»áÔÚ +DiscoveryClient.initScheduledTasks ÖУ¬´´½¨Ò»¸öÐÄÌø¼ì²âµÄ¶¨Ê±ÈÎÎñ + + + +``` +// Heartbeat timer +scheduler.schedule( + new TimedSupervisorTask( + "heartbeat", + scheduler, + heartbeatExecutor, + renewalIntervalInSecs, + TimeUnit.SECONDS, + expBackOffBound, + new HeartbeatThread() + ), + renewalIntervalInSecs, TimeUnit.SECONDS); +``` + + + + + + + + + +**2.5.2 HeartbeatThread** +È»ºóÕâ¸ö¶¨Ê±ÈÎÎñÖУ¬»áÖ´ÐÐÒ»¸ö HearbeatThread µÄỊ̈߳¬Õâ¸öÏ̻߳ᶨʱµ÷ÓÃrenew()À´×öÐøÔ¼¡£ + + + +``` +//ÿ¸ô30s·¢ËÍÒ»¸öÐÄÌøÇëÇóµ½ +private class HeartbeatThread implements Runnable { + public void run() { + if (renew()) { + lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis(); + } + } +} +``` + + + + + + + + + +**2.5.3 ·þÎñ¶ËÊÕµ½ÐÄÌøÇëÇóµÄ´¦Àí** +ÔÚApplicationResource.getInstanceInfoÕâ¸ö½Ó¿ÚÖУ¬»á·µ»ØÒ»¸öInstanceResourceµÄʵÀý£¬ÔÚ¸ÃʵÀýÏ£¬¶¨ÒåÁËÒ»¸östatusUpdateµÄ½Ó¿ÚÀ´¸üÐÂ״̬ + + + +``` +@Path("{id}") +public InstanceResource getInstanceInfo(@PathParam("id") String id) { + return new InstanceResource(this, id, serverConfig, registry); +} +``` + + + + + + + + + +**2.5.4 InstanceResource.statusUpdate()** +Ôڸ÷½·¨ÖУ¬ÎÒÃÇÖØµã¹Ø×¢ registry.statusUpdate Õâ¸ö·½·¨£¬Ëü»áµ÷Óà +AbstractInstanceRegistry.statusUpdateÀ´¸üÐÂÖ¸¶¨·þÎñÌṩÕßÔÚ·þÎñ¶Ë´æ´¢µÄÐÅÏ¢Öеı仯¡£ + + + +``` +@PUT +@Path("status") +public Response statusUpdate( + @QueryParam("value") String newStatus, + @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication, + @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) { + try { + if (registry.getInstanceByAppAndId(app.getName(), id) == null) { + logger.warn("Instance not found: {}/{}", app.getName(), id); + return Response.status(Status.NOT_FOUND).build(); + } + boolean isSuccess = registry.statusUpdate(app.getName(), id, + InstanceStatus.valueOf(newStatus), lastDirtyTimestamp, + "true".equals(isReplication)); + if (isSuccess) { + logger.info("Status updated: {} - {} - {}", app.getName(), id, + newStatus); + return Response.ok().build(); + } else { + logger.warn("Unable to update status: {} - {} - {}", app.getName(), + id, newStatus); + return Response.serverError().build(); + } + } catch (Throwable e) { + logger.error("Error updating instance {} for status {}", id, + newStatus); + return Response.serverError().build(); + } +} +``` + + + + + + + + + +**2.5.5 AbstractInstanceRegistry.statusUpdate** +ÔÚÕâ¸ö·½·¨ÖУ¬»áÄõ½Ó¦ÓöÔÓ¦µÄʵÀýÁÐ±í£¬È»ºóµ÷ÓÃLease.renew()È¥½øÐÐÐÄÌøÐøÔ¼¡£ + + + +``` +public boolean statusUpdate(String appName, String id, + InstanceStatus newStatus, String + lastDirtyTimestamp, + boolean isReplication) { + try { + read.lock(); + // ¸üÐÂ״̬µÄ´ÎÊý ״̬ͳ¼Æ + STATUS_UPDATE.increment(isReplication); + // ´Ó±¾µØÊý¾ÝÀïÃæ»ñȡʵÀýÐÅÏ¢£¬ + Map> gMap = registry.get(appName); + Lease lease = null; + if (gMap != null) { + lease = gMap.get(id); + } + // ʵÀý²»´æÔÚ£¬ÔòÖ±½Ó·µ»Ø£¬±íʾʧ°Ü + if (lease == null) { + return false; + } else { + // Ö´ÐÐÒ»ÏÂleaseµÄrenew·½·¨£¬ÀïÃæÖ÷ÒªÊǸüÐÂÁËÕâ¸öinstanceµÄ×îºó¸üÐÂʱ¼ä¡£ + lease.renew(); + // »ñÈ¡instanceʵÀýÐÅÏ¢ + InstanceInfo info = lease.getHolder(); + // Lease is always created with its instance info object. + // This log statement is provided as a safeguard, in case this invariant is violated. + if (info == null) { + logger.error("Found Lease without a holder for instance id {}", + id); + } + // µ±instanceÐÅÏ¢²»Îª¿Õʱ£¬²¢ÇÒʵÀý״̬·¢ÉúÁ˱仯 + if ((info != null) && !(info.getStatus().equals(newStatus))) { + // Èç¹ûÐÂ״̬ÊÇUPµÄ״̬£¬ÄÇôÆô¶¯Ò»ÏÂserviceUp() , Ö÷ÒªÊǸüзþÎñµÄ×¢²áʱ + ¼ä + if (InstanceStatus.UP.equals(newStatus)) { + lease.serviceUp(); + } + // ½«instance Id ºÍÕâ¸ö״̬µÄÓ³ÉäÐÅÏ¢·ÅÈ븲¸Ç»º´æMAPÀïÃæÈ¥ + overriddenInstanceStatusMap.put(id, newStatus); + // Set it for transfer of overridden status to replica on + // ÉèÖø²¸Ç״̬µ½ÊµÀýÐÅÏ¢ÀïÃæÈ¥ + info.setOverriddenStatus(newStatus); + long replicaDirtyTimestamp = 0; + info.setStatusWithoutDirty(newStatus); + if (lastDirtyTimestamp != null) { + replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp); + } + // If the replication's dirty timestamp is more than the existing one, just update + // it to the replica's. + // Èç¹ûreplicaDirtyTimestamp µÄʱ¼ä´óÓÚinstanceµÄgetLastDirtyTimestamp() ,Ôò¸üР+ + if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) { + info.setLastDirtyTimestamp(replicaDirtyTimestamp); + } + info.setActionType(ActionType.MODIFIED); + recentlyChangedQueue.add(new RecentlyChangedItem(lease)); + info.setLastUpdatedTimestamp(); + //¸üÐÂд»º´æ + invalidateCache(appName, info.getVIPAddress(), + info.getSecureVipAddress()); + } + return true; + } + } finally { + read.unlock(); + } +} +``` + + + + + + + + + +ÖÁ´Ë£¬ÐÄÌøÐøÔ¼¹¦ÄܾͷÖÎöÍê³ÉÁË¡£ + +**2.6 ·þÎñ·¢ÏÖ** +ÎÒÃǼÌÐøÀ´Ñо¿·þÎñµÄ·¢ÏÖ¹ý³Ì£¬¾ÍÊǿͻ§¶ËÐèÒªÄܹ»Âú×ãÁ½¸ö¹¦ÄÜ + +ÔÚÆô¶¯µÄʱºò»ñȡָ¶¨·þÎñÌṩÕߵĵØÖ·Áбí +Eureka server¶ËµØÖ··¢Éú±ä»¯Ê±£¬ÐèÒª¶¯Ì¬¸ÐÖª +**2.6.1 DiscoveryClient¹¹Ôìʱ²éѯ** +¹¹Ôì·½·¨ÖУ¬Èç¹ûµ±Ç°µÄ¿Í»§¶ËĬÈÏ¿ªÆôÁËfetchRegistry£¬Ôò»á´Óeureka-serverÖÐÀ­È¡Êý¾Ý¡£ + + + +``` +DiscoveryClient(ApplicationInfoManager applicationInfoManager, + EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, + Provider backupRegistryProvider, + EndpointRandomizer endpointRandomizer) { + if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) { + fetchRegistryFromBackup(); + } +} +``` + + + + + + + + + +**2.6.2 fetchRegistry** + + + +``` +private boolean fetchRegistry(boolean forceFullRegistryFetch) { + Stopwatch tracer = FETCH_REGISTRY_TIMER.start(); + try { + // If the delta is disabled or if it is the first time, get all + // applications + Applications applications = getApplications(); + if (clientConfig.shouldDisableDelta() + || + (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress())) + || forceFullRegistryFetch + || (applications == null) + || (applications.getRegisteredApplications().size() == 0) + || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta + { + logger.info("Disable delta property : {}", + clientConfig.shouldDisableDelta()); + logger.info("Single vip registry refresh property : {}", + clientConfig.getRegistryRefreshSingleVipAddress()); + logger.info("Force full registry fetch : {}", + forceFullRegistryFetch); + logger.info("Application is null : {}", (applications == null)); + logger.info("Registered Applications size is zero : {}", + (applications.getRegisteredApplications().size() == 0)); + logger.info("Application version is -1: {}", + (applications.getVersion() == -1)); + getAndStoreFullRegistry(); + } else { + getAndUpdateDelta(applications); + } + applications.setAppsHashCode(applications.getReconcileHashCode()); + logTotalInstances(); + } catch (Throwable e) { + logger.error(PREFIX + "{} - was unable to refresh its cache! status = {}", appPathIdentifier, e.getMessage(), e); + return false; + } finally { + if (tracer != null) { + tracer.stop(); + } + } + // Notify about cache refresh before updating the instance remote status + onCacheRefreshed(); + // Update remote status based on refreshed data held in the cache + updateInstanceRemoteStatus(); + // registry was fetched successfully, so return true + return true; +} +``` + + + + + + + + + +**2.6.3 ¶¨Ê±Ë¢Ð±¾µØµØÖ·Áбí** +ÈÎÎñÿ¸ô30s¸üÐÂÒ»´Î +ÔÚDiscoveryClient¹¹ÔìµÄʱºò£¬»á³õʼ»¯Ò»Ð©ÈÎÎñ£¬Õâ¸öÔÚÇ°ÃæÔÛÃÇ·ÖÎö¹ýÁË¡£ÆäÖÐÓÐÒ»¸öÈÎÎñ¶¯Ì¬¸üб¾µØ·þÎñµØÖ·ÁÐ±í£¬½Ð cacheRefreshTask ¡£ + +Õâ¸öÈÎÎñ×îÖÕÖ´ÐеÄÊÇCacheRefreshThreadÕâ¸öÏ̡߳£ËüÊÇÒ»¸öÖÜÆÚÐÔÖ´ÐеÄÈÎÎñ£¬¾ßÌåÎÒÃÇÀ´¿´Ò»Ï¡£ + + + +``` +private void initScheduledTasks() { + if (clientConfig.shouldFetchRegistry()) { + // registry cache refresh timer + int registryFetchIntervalSeconds = + clientConfig.getRegistryFetchIntervalSeconds(); + int expBackOffBound = + clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); + cacheRefreshTask = new TimedSupervisorTask( + "cacheRefresh", + scheduler, + cacheRefreshExecutor, + registryFetchIntervalSeconds, + TimeUnit.SECONDS, + expBackOffBound, + new CacheRefreshThread() + ); + scheduler.schedule( + cacheRefreshTask, + registryFetchIntervalSeconds, TimeUnit.SECONDS); + } +``` + + + + + + + + + +**2.6.4 TimedSupervisorTask** +´ÓÕûÌåÉÏ¿´£¬TimedSupervisorTaskÊǹ̶¨¼ä¸ôµÄÖÜÆÚÐÔÈÎÎñ£¬Ò»µ©Óöµ½³¬Ê±¾Í»á½«ÏÂÒ»¸öÖÜÆÚµÄ¼ä¸ôʱ¼äµ÷´ó£¬Èç¹ûÁ¬Ðø³¬Ê±£¬ÄÇôÿ´Î¼ä¸ôʱ¼ä¶¼»áÔö´óÒ»±¶£¬Ò»Ö±µ½´ïÍⲿ²ÎÊýÉ趨µÄÉÏÏÞΪֹ£¬Ò»µ©ÐÂÈÎÎñ²»ÔÙ³¬Ê±£¬¼ä¸ôʱ¼äÓÖ»á×Ô¶¯»Ö¸´Îª³õʼֵ¡£ÕâÖÖÉè¼Æ»¹ÊÇÖµµÃѧϰµÄ¡£ + + + +``` +public void run() { + Future future = null; + try { + //ʹÓÃFuture£¬¿ÉÒÔÉ趨×ÓÏ̵߳ij¬Ê±Ê±¼ä£¬ÕâÑùµ±Ç°Ï߳̾Ͳ»ÓÃÎÞÏ޵ȴýÁË + future = executor.submit(task); + threadPoolLevelGauge.set((long) executor.getActiveCount()); + //Ö¸¶¨µÈ´ý×ÓÏ̵߳Ä×ʱ¼ä + future.get(timeoutMillis, TimeUnit.MILLISECONDS); // block until done or timeout + //delayÊǸöºÜÓÐÓõıäÁ¿£¬ºóÃæ»áÓõ½£¬ÕâÀï¼ÇµÃÿ´ÎÖ´ÐÐÈÎÎñ³É¹¦¶¼»á½«delayÖØÖà + delay.set(timeoutMillis); + threadPoolLevelGauge.set((long) executor.getActiveCount()); + } catch (TimeoutException e) { + logger.error("task supervisor timed out", e); + timeoutCounter.increment(); + long currentDelay = delay.get(); + //ÈÎÎñÏ̳߳¬Ê±µÄʱºò£¬¾Í°Ñdelay±äÁ¿·­±¶£¬µ«²»»á³¬¹ýÍⲿµ÷ÓÃʱÉ趨µÄ×î´óÑÓʱʱ¼ä + long newDelay = Math.min(maxDelay, currentDelay * 2); + //ÉèÖÃΪ×îеÄÖµ£¬¿¼Âǵ½¶àỊ̈߳¬ËùÒÔÓÃÁËCAS + delay.compareAndSet(currentDelay, newDelay); + } catch (RejectedExecutionException e) { + //Ò»µ©Ï̳߳صÄ×èÈû¶ÓÁÐÖзÅÂúÁË´ý´¦ÀíÈÎÎñ£¬´¥·¢Á˾ܾø²ßÂÔ£¬¾Í»á½«µ÷¶ÈÆ÷Í£µô + if (executor.isShutdown() || scheduler.isShutdown()) { + logger.warn("task supervisor shutting down, reject the task", e); + } else { + logger.error("task supervisor rejected the task", e); + } + rejectedCounter.increment(); + } catch (Throwable e) { + //Ò»µ©³öÏÖδ֪µÄÒì³££¬¾ÍÍ£µôµ÷¶ÈÆ÷ + if (executor.isShutdown() || scheduler.isShutdown()) { + logger.warn("task supervisor shutting down, can't accept the task"); + } else { + logger.error("task supervisor threw an exception", e); + } + throwableCounter.increment(); + } finally { + //ÕâÀïÈÎÎñҪôִÐÐÍê±Ï£¬ÒªÃ´·¢ÉúÒì³££¬¶¼ÓÃcancel·½·¨À´ÇåÀíÈÎÎñ£» + if (future != null) { + future.cancel(true); + } + //Ö»Òªµ÷¶ÈÆ÷ûÓÐÍ£Ö¹£¬¾ÍÔÙÖ¸¶¨µÈ´ýʱ¼äÖ®ºóÔÚÖ´ÐÐÒ»´ÎͬÑùµÄÈÎÎñ + if (!scheduler.isShutdown()) { + //ÕâÀï¾ÍÊÇÖÜÆÚÐÔÈÎÎñµÄÔ­Òò£ºÖ»ÒªÃ»ÓÐÍ£Ö¹µ÷¶ÈÆ÷£¬¾ÍÔÙ´´½¨Ò»´ÎÐÔÈÎÎñ£¬Ö´ÐÐʱ¼äʱdealyµÄÖµ£¬ + //¼ÙÉèÍⲿµ÷ÓÃʱ´«ÈëµÄ³¬Ê±Ê±¼äΪ30Ã루¹¹Ôì·½·¨µÄÈë²Îtimeout£©£¬×î´ó¼ä¸ôʱ¼äΪ50Ãë(¹¹Ôì·½·¨µÄÈë²ÎexpBackOffBound) + //Èç¹û×î½üÒ»´ÎÈÎÎñûÓг¬Ê±£¬ÄÇô¾ÍÔÚ30Ãëºó¿ªÊ¼ÐÂÈÎÎñ£¬ + //Èç¹û×î½üÒ»´ÎÈÎÎñ³¬Ê±ÁË£¬ÄÇô¾ÍÔÚ50Ãëºó¿ªÊ¼ÐÂÈÎÎñ£¨Òì³£´¦ÀíÖÐÓиö³ËÒÔ¶þµÄ²Ù×÷£¬³ËÒÔ¶þºóµÄ60Ã볬¹ýÁË×î´ó¼ä¸ô50Ã룩 + scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS); + } + } +} +``` + + + + + + + + + +**2.6.5 refreshRegistry** +Õâ¶Î´úÂëÖ÷ÒªÁ½¸öÂß¼­ + +* ÅжÏremoteRegionsÊÇ·ñ·¢ÉúÁ˱仯 + +* µ÷ÓÃfetchRegistry»ñÈ¡±¾µØ·þÎñµØÖ·»º´æ + + + + ``` + @VisibleForTesting + void refreshRegistry() { + try { + boolean isFetchingRemoteRegionRegistries = + isFetchingRemoteRegionRegistries(); + boolean remoteRegionsModified = false; + //Èç¹û²¿ÊðÔÚaws»·¾³ÉÏ£¬»áÅжÏ×îºóÒ»´ÎÔ¶³ÌÇøÓò¸üеÄÐÅÏ¢ºÍµ±Ç°Ô¶³ÌÇøÓòÐÅÏ¢½øÐбȽϣ¬Èç¹û²»ÏëµÈ£¬Ôò¸üР+ String latestRemoteRegions = + clientConfig.fetchRegistryForRemoteRegions(); + if (null != latestRemoteRegions) { + String currentRemoteRegions = remoteRegionsToFetch.get(); + if (!latestRemoteRegions.equals(currentRemoteRegions)) { + //ÅжÏ×îºóÒ»´Î + } + boolean success = fetchRegistry(remoteRegionsModified); + if (success) { + registrySize = localRegionApps.get().size(); + lastSuccessfulRegistryFetchTimestamp = + System.currentTimeMillis(); + } + // Ê¡ÂÔ + } catch (Throwable e) { + logger.error("Cannot fetch registry from server", e); + } + } + ``` + + + + + + + + + +**2.6.6 fetchRegistry** + + + + ``` + private boolean fetchRegistry(boolean forceFullRegistryFetch) { + Stopwatch tracer = FETCH_REGISTRY_TIMER.start(); + try { + // If the delta is disabled or if it is the first time, get all + // applications + // È¡³ö±¾µØ»º´æµÄ·þÎñÁбíÐÅÏ¢ + Applications applications = getApplications(); + //Åж϶à¸öÌõ¼þ£¬È·¶¨ÊÇ·ñ´¥·¢È«Á¿¸üУ¬ÈçÏÂÈÎÒ»¸öÂú×ã¶¼»áÈ«Á¿¸üУº + //1\. ÊÇ·ñ½ûÓÃÔöÁ¿¸üУ» + //2\. ÊÇ·ñ¶Ôij¸öregionÌØ±ð¹Ø×¢£» + //3\. Íⲿµ÷ÓÃʱÊÇ·ñͨ¹ýÈë²ÎÖ¸¶¨È«Á¿¸üУ» + //4\. ±¾µØ»¹Î´»º´æÓÐЧµÄ·þÎñÁбíÐÅÏ¢£» + if (clientConfig.shouldDisableDelta() + || + (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress())) + || forceFullRegistryFetch + || (applications == null) + || (applications.getRegisteredApplications().size() == 0) + || (applications.getVersion() == -1)) //Client application does not + have latest library supporting delta + { + //µ÷ÓÃÈ«Á¿¸üР+ getAndStoreFullRegistry(); + } else { + //µ÷ÓÃÔöÁ¿¸üР+ getAndUpdateDelta(applications); + } + //ÖØÐ¼ÆËãºÍÉèÖÃÒ»ÖÂÐÔhashÂë + applications.setAppsHashCode(applications.getReconcileHashCode()); + logTotalInstances(); //ÈÕÖ¾´òÓ¡ËùÓÐÓ¦ÓõÄËùÓÐʵÀýÊýÖ®ºÍ + } catch (Throwable e) { + logger.error(PREFIX + "{} - was unable to refresh its cache! status = {}", appPathIdentifier, e.getMessage(), e); + return false; + } finally { + if (tracer != null) { + tracer.stop(); + } + } + //½«±¾µØ»º´æ¸üеÄʼþ¹ã²¥¸øËùÓÐÒÑ×¢²áµÄ¼àÌýÆ÷£¬×¢Òâ¸Ã·½·¨Òѱ»CloudEurekaClientÀàÖØÐ´ + onCacheRefreshed(); + // Update remote status based on refreshed data held in the cache + //¼ì²é¸Õ¸Õ¸üÐµĻº´æÖУ¬ÓÐÀ´×ÔEureka serverµÄ·þÎñÁÐ±í£¬ÆäÖаüº¬Á˵±Ç°Ó¦ÓõÄ״̬£¬ + //µ±Ç°ÊµÀýµÄ³ÉÔ±±äÁ¿lastRemoteInstanceStatus£¬¼Ç¼µÄÊÇ×îºóÒ»´Î¸üеĵ±Ç°Ó¦ÓÃ״̬£¬ + //ÉÏÊöÁ½ÖÖ״̬ÔÚupdateInstanceRemoteStatus·½·¨ÖÐ×÷±È½Ï £¬Èç¹û²»Ò»Ö£¬¾Í¸üÐÂlastRemoteInstanceStatus£¬²¢Çҹ㲥¶ÔÓ¦µÄʼþ + updateInstanceRemoteStatus(); + // registry was fetched successfully, so return true + return true; + } + ``` + + + + + + + + + +**2.6.7 getAndStoreFullRegistry** +´Óeureka server¶Ë»ñÈ¡·þÎñ×¢²áÖÐÐĵĵØÖ·ÐÅÏ¢£¬È»ºó¸üв¢ÉèÖõ½±¾µØ»º´æ localRegionApps ¡£ + + + +``` +private void getAndStoreFullRegistry() throws Throwable { + long currentUpdateGeneration = fetchRegistryGeneration.get(); + logger.info("Getting all instance registry info from the eureka server"); + Applications apps = null; + EurekaHttpResponse httpResponse = + clientConfig.getRegistryRefreshSingleVipAddress() == null + ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get()) + : + eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddre + ss(), remoteRegionsRef.get()); + if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) { + apps = httpResponse.getEntity(); + } + logger.info("The response status is {}", httpResponse.getStatusCode()); + if (apps == null) { + logger.error("The application is null for some reason. Not storing this information"); + } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, + currentUpdateGeneration + 1)) { + localRegionApps.set(this.filterAndShuffle(apps)); + logger.debug("Got full registry with apps hashcode {}", + apps.getAppsHashCode()); + } else { + logger.warn("Not updating applications as another thread is updating it already"); + } +} +``` + + + + + + + + + +**2.6.8 ·þÎñ¶Ë²éѯ·þÎñµØÖ·Á÷³Ì** +Ç°ÃæÎÒÃÇÖªµÀ£¬¿Í»§¶Ë·¢Æð·þÎñµØÖ·µÄ²éѯÓÐÁ½ÖÖ£¬Ò»ÖÖÊÇÈ«Á¿¡¢ÁíÒ»ÖÖÊÇÔöÁ¿¡£¶ÔÓÚÈ«Á¿²éѯµÄÇëÇ󣬻áµ÷ÓÃEureka-serverµÄApplicationsResourceµÄgetContainers·½·¨¡£ + +¶ø¶ÔÓÚÔöÁ¿ÇëÇ󣬻áµ÷Óà +ApplicationsResource.getContainerDifferential¡£ + +**2.6.9 ApplicationsResource.getContainers** +½ÓÊÕ¿Í»§¶Ë·¢Ë͵ĻñȡȫÁ¿×¢²áÐÅÏ¢ÇëÇó¡£ + + + +``` +@GET +public Response getContainers(@PathParam("version") String version, + @HeaderParam(HEADER_ACCEPT) String acceptHeader, + @HeaderParam(HEADER_ACCEPT_ENCODING) String + acceptEncoding, + @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) + String eurekaAccept, + @Context UriInfo uriInfo, + @Nullable @QueryParam("regions") String + regionsStr) { + boolean isRemoteRegionRequested = null != regionsStr && + !regionsStr.isEmpty(); + String[] regions = null; + if (!isRemoteRegionRequested) { + EurekaMonitors.GET_ALL.increment(); + } else { + regions = regionsStr.toLowerCase().split(","); + Arrays.sort(regions); // So we don't have different caches for same regions queried in different order. + EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment(); + } + // EurekaServerÎÞ·¨Ìṩ·þÎñ£¬·µ»Ø403 + if (!registry.shouldAllowAccess(isRemoteRegionRequested)) { + return Response.status(Status.FORBIDDEN).build(); + } + CurrentRequestVersion.set(Version.toEnum(version)); + KeyType keyType = Key.KeyType.JSON;// ÉèÖ÷µ»ØÊý¾Ý¸ñʽ£¬Ä¬ÈÏJSON + String returnMediaType = MediaType.APPLICATION_JSON; + if (acceptHeader == null || !acceptHeader.contains(HEADER_JSON_VALUE)) { + // Èç¹û½ÓÊÕµ½µÄÇëÇóÍ·²¿Ã»ÓоßÌå¸ñʽÐÅÏ¢£¬Ôò·µ»Ø¸ñʽΪXML + keyType = Key.KeyType.XML; + returnMediaType = MediaType.APPLICATION_XML; + } + // ¹¹½¨»º´æ¼ü + Key cacheKey = new Key(Key.EntityType.Application, + ResponseCacheImpl.ALL_APPS, + keyType, CurrentRequestVersion.get(), + EurekaAccept.fromString(eurekaAccept), regions + ); + // ·µ»Ø²»Í¬µÄ±àÂëÀàÐ͵ÄÊý¾Ý£¬È¥»º´æÖÐÈ¡Êý¾ÝµÄ·½·¨»ù±¾Ò»Ö + Response response; + if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) { + response = Response.ok(responseCache.getGZIP(cacheKey)) + .header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE) + .header(HEADER_CONTENT_TYPE, returnMediaType) + .build(); + } else { + response = Response.ok(responseCache.get(cacheKey)) + .build(); + } + CurrentRequestVersion.remove(); + return response; +} +``` + + + + + + + + + +**2.6.10 responseCache.getGZIP** +´Ó»º´æÖжÁÈ¡Êý¾Ý¡£ + + + +``` +public byte[] getGZIP(Key key) { + Value payload = getValue(key, shouldUseReadOnlyResponseCache); + if (payload == null) { + return null; + } + return payload.getGzipped(); +} +Value getValue(final Key key, boolean useReadOnlyCache) { + Value payload = null; + try { + if (useReadOnlyCache) { + final Value currentPayload = readOnlyCacheMap.get(key); + if (currentPayload != null) { + payload = currentPayload; + } else { + payload = readWriteCacheMap.get(key); + readOnlyCacheMap.put(key, payload); + } + } else { + payload = readWriteCacheMap.get(key); + } + } catch (Throwable t) { + logger.error("Cannot get value for key : {}", key, t); + } + return payload; +} +``` + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..80db850 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudGateway\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,415 @@ +**ѧϰĿ±ê** + +1. GatewayºËÐÄÔ­Àí·ÖÎö + **µÚ1Õ BeanµÄ×¼±¸** + Ç°ÃæÒ²½²ÁËÕâô¶à×é¼þÁË£¬Õâ»á¶ùÎÒÃǼ¯³É + spring-cloud-starter-gateway×é¼þ·¢ÏÖ£¬ÓÖÊÇÒ»¸östarter×é¼þ£¬¶þ»°²»Ëµ£¬ÏÈÈ¥ÕÒspring.factoriesÎļþ£¬·ÖÎöÒ»ÏÂÓÐÄÄÐ©ÖØÒªµÄbean±»×Ô¶¯×°Åä½øIoCÈÝÆ÷ÀïÃæÁË¡£ + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®GatewayÍø¹Ø-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://dl-harmonyos.51cto.com/images/202207/788f3c1494307a2ad7d935811c9e62bab2c435.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®GatewayÍø¹Ø-¿ªÔ´»ù´¡Èí¼þÉçÇø")1.ÏÈÀ´¿´ +GatewayClassPathWarningAutoConfigurationÕâ¸öÅäÖÃÀà + + + +``` +@Configuration(proxyBeanMethods = false) +//µ±Ç°ÅäÖÃÀàÔÚGatewayAutoConfigurationÕâ¸öºËÐÄÅäÖÃÀà֮ǰ¼ÓÔØ +@AutoConfigureBefore(GatewayAutoConfiguration.class) +public class GatewayClassPathWarningAutoConfiguration { + ... + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet") + protected static class SpringMvcFoundOnClasspathConfiguration { + public SpringMvcFoundOnClasspathConfiguration() { + log.warn(BORDER + + "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. " + + "Please remove spring-boot-starter-web dependency." + BORDER); + } + + } + @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler") + protected static class WebfluxMissingFromClasspathConfiguration { + + public WebfluxMissingFromClasspathConfiguration() { + log.warn(BORDER + "Spring Webflux is missing from the classpath, " + + "which is required for Spring Cloud Gateway at this time. " + + "Please add spring-boot-starter-webflux dependency." + BORDER); + } + } +} +``` + + + + + + + + + +´ÓÕâ¸öÅäÖÃÀàÄÜ¿´³öÀ´£¬Ëüʵ¼ÊÉϾÍͨ¹ýConditionOnClassºÍConditionOnMissingClassÁ½¸ö×öÁËÁ½¸öÈÕÖ¾´òÓ¡µÄ¹¦ÄÜ£»Èç¹ûClassPathÏÂÓÐ +org.springframework.web.servlet.DispatcherServletÀàµÄ»°£¬ÔòʵÀýµÚÒ»¸öBean¶ÔÏó£¬È»ºó´òÓ¡ÈÕÖ¾£º²»ÄÜÒÀÀµspring-boot-starter-webÕâ¸ö°ü¡£È»ºóÔÙ¼ì²éClassPathÏÂÊÇ·ñÓÐÕýÈ·µÄÅäÖÃwebflux£¬Èç¹ûûÓУ¬Ôò´òÓ¡ÈÕÖ¾£º¼Óspring-boot-starter-webfluxÒÀÀµ¡£ + +2.ºËÐÄÅäÖÃÀàGatewayAutoConfiguration + +ÒòΪ´úÂëÌ«³¤£¬ÕâÀï¾Í²»Õ¹Ê¾ÁË£¬ÕâÀï¾ÍÁоټ¸¸ö±È½ÏÖØÒªµÄ + +* PropertiesRouteDefinitionLocator£ºÓÃÓÚ´ÓÅäÖÃÎļþ£¨yml/properties£©ÖжÁȡ·ÓÉÅäÖÃÐÅÏ¢£¡ +* RouteDefinitionLocator£º°Ñ RouteDefinition ת»¯Îª Route +* RoutePredicateHandlerMapping£ºÀàËÆÓÚ mvc µÄHandlerMapping£¬²»¹ýÕâÀïÊÇ GatewayʵÏֵġ£ÓÃÓÚÆ¥Åä¶ÔÓ¦µÄÇëÇóroute +* GatewayProperties£ºymlÅäÖÃÐÅÏ¢·â×°ÔÚ GatewayProperties ¶ÔÏóÖÐ +* AfterRoutePredicateFactory£º¸÷ÖÖ·ÓɶÏÑÔ¹¤³§£¬ÕýÊÇÕâЩ¶ÏÑÔ¹¤³§ÔÚÆô¶¯Ê±ÒѾ­Éú³É¶ÔÓ¦µÄbean£¬ÎÒÃDzſÉÒÔÔÚ yml ÖÐÅäÖÃһϣ¬¼´¿ÉÉúЧ +* RetryGatewayFilterFactory£º¸÷ÖÖ Gateway ¹ýÂËÆ÷£¬ÕýÊÇÕâЩ¹ýÂËÆ÷ÔÚÆô¶¯Ê±ÒѾ­Éú³É¶ÔÓ¦µÄbean£¬ÎÒÃDzſÉÒÔÔÚ yml ÖÐÅäÖÃһϣ¬¼´¿ÉÉúЧ +* GlobalFilterʵÏÖÀࣺȫ¾Ö¹ýÂËÆ÷ + +3.HttpHandlerAutoConfigurationºÍWebFluxAutoConfigurationÅäÖÃÀ࣬ÔÚGatewayAutoConfigurationÖ®ºóʵÀý»¯£¬·Ö±ðʵÀý»¯ÁËHttpHandlerºÍWebFluxConfigBean + +**µÚ2Õ ִÐÐÁ÷³Ì** +ÉÏÒ»ÎÄÖн²µ½HystrixµÄÔ­Àí£¬ÔÚHystrixÖкËÐÄÒµÎñÂß¼­¶¼ÊÇͨ¹ýÏìӦʽ±à³ÌÍê³ÉµÄ£¬ÊÂʵÉÏ£¬ÔÚGatewayÖÐÒ²¶¼ÊÇ»ùÓÚͬÑùµÄ±à³Ì·ç¸ñ¡£Í¬ÑùµÄ£¬GatewayµÄÁ÷³ÌͬSpringMVCÁ÷³ÌÒ²·Ç³£ÏàËÆ¡£ + +µ±Ç°¶ËÓÐÇëÇó½øÀ´µÄʱºò£¬´óÌåµÄÁ÷³ÌÈçÏ£º + +1. Ê×Ïȱ»DispatcherHandler¸ø²¶»ñÀ¹½Ø£¬È»ºó¶ÔÇëÇóµÄURI½øÐнâÎö +2. È»ºó¸ù¾ÝURIÈ¥µ÷ÓÃHandlerMapping£¬»ñÈ¡ÕæÕýÒªÖ´ÐеÄWebHandler +3. È»ºóÑ¡ÔñÒ»¸öºÏÊʵÄÊÊÅäÆ÷HandlerAdapterÖ´ÐÐ +4. Ö´ÐÐWebHandler + µ±ÇëÇógateway·þÎñʱ£¬ËùÓеÄÇëÇó¶¼»á½øÈëµ½DispatcherHandlerÖеÄhandle·½·¨£¬ÏÂÃæÎÒÃÇÒ»Æð¿´¿´Õâ¸ö·½·¨ + + + +``` +@Override +public Mono handle(ServerWebExchange exchange) { + if (this.handlerMappings == null) { + return createNotFoundError(); + } + //ÕâÀï¾ÍÊÇwebFluxµÄÏìӦʽ±à³Ì + return Flux + // 1.ÕâÀï¾ÍÊDZéÀúËùÓÐµÄ handlerMapping + .fromIterable(this.handlerMappings) + // 2.»ñÈ¡¶ÔÓ¦µÄhandlerMapping £¬±ÈÈç³£ÓÃµÄ RequestMappingHandlerMapping¡¢RoutePredicateHandlerMapping + .concatMap(mapping -> mapping.getHandler(exchange)) + .next() + .switchIfEmpty(createNotFoundError()) + // 3.»ñÈ¡¶ÔÓ¦µÄÊÊÅäÆ÷£¬µ÷ÓöÔÓ¦µÄ´¦ÀíÆ÷ + .flatMap(handler -> invokeHandler(exchange, handler)) + // 4.·µ»Ø´¦Àí½á¹û + .flatMap(result -> handleResult(exchange, result)); +} +``` + + + + + + + + + +**2.1 getHandler** +ÎÒÃÇÏÈÀ´¿´¿´getHandler·½·¨£¬Ëü¾ÍÊÇGatewayµÄºËÐÄÂß¼­ËùÔÚ£¬ÔÙgetHandlerÖлñÈ¡¶ÔÓ¦µÄHandlerMapping¡£ + +ÏÂÃæÊÇ +AbstractHandlerMapping.getHandlerµÄÔ´Âë + + + +``` +@Override +public Mono getHandler(ServerWebExchange exchange) { + //ÕâÒ»²½»á»ñȡ·ÓɵÄʵÏÖÀ࣬»á½øÈëµ½RoutePredicateHandlerMapping + return getHandlerInternal(exchange).map(handler -> { + if (logger.isDebugEnabled()) { + logger.debug(exchange.getLogPrefix() + "Mapped to " + handler); + } + ServerHttpRequest request = exchange.getRequest(); + if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { + CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null); + CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange); + config = (config != null ? config.combine(handlerConfig) : handlerConfig); + if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) { + return REQUEST_HANDLED_HANDLER; + } + } + return handler; + }); +} +``` + + + + + + + + + + + +``` +@Override +protected Mono getHandlerInternal(ServerWebExchange exchange) { + // don't handle requests on management port if set and different than server port + if (this.managementPortType == DIFFERENT && this.managementPort != null + && exchange.getRequest().getURI().getPort() == this.managementPort) { + return Mono.empty(); + } + exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); + //ѰÕÒ²¢Æ¥Åä·ÓÉ + return lookupRoute(exchange) + // .log("route-predicate-handler-mapping", Level.FINER) //name this + .flatMap((Function>) r -> { + //ÒÆ³ýÉÏÏÂÎÄÖоɵÄÊôÐÔ + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + if (logger.isDebugEnabled()) { + logger.debug( + "Mapping [" + getExchangeDesc(exchange) + "] to " + r); + } + //°Ñ¸Ã·ÓÉÓëÉÏÏÂÎİ󶨣¬ºóÐø¸ºÔؾùºâ»áÓà + exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); + //·µ»Ø webHandler + return Mono.just(webHandler); + }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + if (logger.isTraceEnabled()) { + logger.trace("No RouteDefinition found for [" + + getExchangeDesc(exchange) + "]"); + } + }))); +} +``` + + + + + + + + + +ÆäÖÐlookupRoute·½·¨»áÕÒµ½ymlÖÐÅäÖõÄËùÓеÄ·ÓɶÏÑÔ¹¤³§£¨Before¡¢After¡¢PathµÈµÈ£©£¬²¢Ö´ÐÐapply·½·¨£¬½øÐзÓÉÆ¥Å䣬ÅжÏÊÇ·ñÔÊÐíÇëÇóͨ¹ý£¡Ö´ÐÐ˳ÐòÓÉspringboot×Ô¶¯ÅäÖÃʱ×Ô¼ºÖƶ¨ + + + +``` +protected Mono lookupRoute(ServerWebExchange exchange) { + // getRoutes »ñÈ¡ËùÓеĶÏÑÔ¹¤³§ + return this.routeLocator.getRoutes() + .concatMap(route -> Mono.just(route).filterWhen(r -> { + exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); + // ÏÈ»ñÈ¡RouteÄÚ²¿µÄpredicateÊôÐÔ + //È»ºóµ÷ÓÃapply·½·¨ Ö´ÐжÏÑÔ£¡ÅжÏÇëÇóÊÇ·ñͨ¹ý + return r.getPredicate().apply(exchange); + }).doOnError(e -> logger.error( + "Error applying predicate for route: " + route.getId(), + e)) + .onErrorResume(e -> Mono.empty())) + .next() + .map(route -> { + if (logger.isDebugEnabled()) { + logger.debug("Route matched: " + route.getId()); + } + validateRoute(route, exchange); + return route; + }); +} +``` + + + + + + + + + +ÆäÖÐgetRoutes()·½·¨¾ÍÊÇͨ¹ý +RouteDefinitionRouteLocator´ÓÅäÖÃÎļþÖлñÈ¡ËùÓзÓɵģ¬È»ºó°ÑÕÒµ½µÄ·ÓÉת»»³ÉRoute + + + +``` +@Override +public Flux getRoutes() { + // getRouteDefinitions() ´ÓÅäÖÃÎļþÖлñÈ¡ËùÓзÓÉ + Flux routes = this.routeDefinitionLocator.getRouteDefinitions() + // convertToRoute()£º°ÑÕÒµ½µÄ·ÓÉת»»³ÉRoute + .map(this::convertToRoute); + ... +} +``` + + + + + + + + + + + +``` +public class Route implements Ordered { + private final String id; + private final URI uri; + private final int order; + private final AsyncPredicate predicate; + private final List gatewayFilters; + private final Map metadata; + ... +} +``` + + + + + + + + + +**2.2 invokeHandler** +GatewayÓÉÓÚÔÚÉÏÒ»²½Æ¥Åä·Óɺ󷵻صÄÊÇwebHandlerÀàÐ͵ģ¬ËùÒÔÒ²ÐèÒªÕÒµ½¶ÔÓ¦µÄHandlerAdaptor£¬½øÈë»ñÈ¡¶ÔÓ¦µÄÊÊÅäÆ÷·½·¨ invokeHandler(exchange, handler)ÖÐ + + + +``` +private Mono invokeHandler(ServerWebExchange exchange, Object handler) { + if (this.handlerAdapters != null) { + //ÕÒµ½ËùÓеÄHandlerAdapterȥƥÅäWebFluxÀàÐÍ + for (HandlerAdapter handlerAdapter : this.handlerAdapters) { + if (handlerAdapter.supports(handler)) { + return handlerAdapter.handle(exchange, handler); + } + } + } + return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); +} +``` + + + + + + + + + +SimpleHandlerAdapter ÖеÄhandle·½·¨ÈçÏ + + + +``` +@Override +public Mono handle(ServerWebExchange exchange, Object handler) { + //´¦ÀíWebHandler ÀàÐÍ + WebHandler webHandler = (WebHandler) handler; + Mono mono = webHandler.handle(exchange); + return mono.then(Mono.empty()); +} +``` + + + + + + + + + +ÆäÖÐwebHandler.handle·½·¨¾ÍÊÇ´¦ÀíËùÓйýÂËÆ÷Á´µÄ·½·¨£¬¸Ã¹ýÂËÆ÷Á´°üÀ¨globalFiltersºÍgatewayFilters + + + +``` +@Override +public Mono handle(ServerWebExchange exchange) { + // 1\. ¸ù¾Ý·ÓÉÓëÉÏÏÂÎİ󶨹ØÏµ£¬»ñÈ¡¶ÔÓ¦µÄ·ÓÉRoute + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + List gatewayFilters = route.getFilters(); + // 2\. ÊÕ¼¯ËùÓÐµÄ globalFilters ²¢·ÅÈëList + //×¢ÒâÕâÀïʹÓÃÁËÊÊÅäÆ÷ģʽ + List combined = new ArrayList<>(this.globalFilters); + // 3\. °Ñ gatewayFilters Ò²·ÅÈëList£¬ÐγÉÒ»Ìõ¹ýÂËÆ÷Á¢°¸ + combined.addAll(gatewayFilters); + // 4\. ¸ù¾ÝorderÅÅÐò + AnnotationAwareOrderComparator.sort(combined); + if (logger.isDebugEnabled()) { + logger.debug("Sorted gatewayFilterFactories: " + combined); + } + // 5\. Ö´ÐйýÂËÆ÷Á´ÖеÄÿһ¸ö¹ýÂËÆ÷·½·¨£¡ + return new DefaultGatewayFilterChain(combined).filter(exchange); +} +``` + + + + + + + + + +×¢Ò⣺ÔÚ×é×°¹ýÂËÆ÷Á´µÄʱºò£¬ÊǰÑglobalFiltersºÍgatewayFiltersÁ½ÖÖ¹ýÂËÆ÷¶¼·Å½øÁËListÖУ¬ÕâÊÇÔõô×öµÄÄØ£¿ + +ÕâÆäʵÓõ½ÁËÒ»ÖÖ ÊÊÅäÆ÷ µÄÉè¼ÆÄ£Ê½£¡ + +* Èç¹û·ÅÈëµÄÊÇglobalFilters£¬»áÏȰÑglobalFiltersת»¯³ÉGatewayFilterAdapter¡£ GatewayFilterAdapterÔÚÄÚ²¿¼¯³ÉÁËGlobalFilter£¬Í¬Ê±Ò²ÊµÏÖÁËGatewayFilter£¬Ê¹ globalFiltersºÍgatewayFiltersÔÚ ÊÊÅäÆ÷ ÀàGatewayFilterAdapterÖй²´æ£¡ +* Èç¹û·ÅÈëµÄÊÇgatewayFilters£¬Ö±½Ó·ÅÈë¼´¿É£¡ + **µÚ3Õ ¸ºÔؾùºâÁ÷³Ì** + GatewayµÄ¸ºÔؾùºâÖ»ÐèÒªÔÚymlÖÐÅäÖà uri: lb://user¼´¿ÉʵÏÖ¸ºÔؾùºâ£¬µ×²ãÊÇÓÉÈ«¾Ö¹ýÂËÆ÷LoadBalancerClientFilterµÄfilter·½·¨È¥×öµÄ£¡ + +ÒÔ¶©µ¥·þÎñµÄ +http://localhost:9527/get/3ΪÀý£¡9527ÎªÍø¹ØGatewayµÄ¶Ë¿Ú + + + +``` +public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 1\. ¸ù¾Ý·ÓÉÓëÉÏÏÂÎİ󶨹ØÏµ + // »ñȡԭʼµÄurl£ºhttp://localhost:9527/get/3 + URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); + if (url == null + || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { + return chain.filter(exchange); + } + addOriginalRequestUrl(exchange, url); + if (log.isTraceEnabled()) { + log.trace("LoadBalancerClientFilter url before: " + url); + } + // 2\. ͨ¹ýribbonµÄ¸ºÔؾùºâËã·¨£¬¸ù¾Ý·þÎñÃûÈ¥nacos»òÕßEurekaÑ¡ÔñÒ»¸öʵÀý£¡ + // ¸ÃʵÀý¾ÍÓÐuser·þÎñÕæÕýµÄ url µØÖ·£ºhttp://localhost:8080/get/3 + final ServiceInstance instance = choose(exchange); + if (instance == null) { + throw NotFoundException.create(properties.isUse404(), + "Unable to find instance for " + url.getHost()); + } + // 3\. Äõ½Ô­ÉúµÄ uri £ºhttp://localhost:9527/get/3 + URI uri = exchange.getRequest().getURI(); + String overrideScheme = instance.isSecure() ? "https" : "http"; + if (schemePrefix != null) { + overrideScheme = url.getScheme(); + } + // 4\. Ä÷þÎñʵÀýinstanceµÄuriÌæ»»Ô­ÉúµÄuriµØÖ· µÃµ½ еÄurl + // еÄurl: http://localhost:8080/get/3 + URI requestUrl = loadBalancer.reconstructURI( + new DelegatingServiceInstance(instance, overrideScheme), uri); + if (log.isTraceEnabled()) { + log.trace("LoadBalancerClientFilter url chosen: " + requestUrl); + } + // 5\. ÔٴμǼÉÏÏÂÎĹØÏµ + exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); + // 6\. Ö´ÐйýÂËÆ÷Á´ÖÐµÄÆäËû¹ýÂËÇëÇó + return chain.filter(exchange); +} +``` + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..87b59fb --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudHystrix\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1192 @@ +ѧϰĿ±ê + +1. ÊÖдMini°æµÄHystrix +2. RxJava֪ʶÊáÀí +3. HystrixµÄºËÐÄÁ÷³Ì·ÖÎö +4. Ô´ÂëÑéÖ¤ + µÚ1Õ ÊÖдMini°æ + ÉÏÎÄÖÐÒѾ­¸ø´ó¼Ò½éÉܹýÁËHystrixµÄºËÐŦÄܺÍʹÓÃÁË£¬ËüÎ޷ǾÍÊÇÌṩÁËÈÛ¶Ï¡¢½µ¼¶¡¢¸ôÀëµÈ¹¦ÄÜ£¬ÆäÖÐÈ۶Ϻ͸ôÀëÊÇÄ¿µÄ£¬½µ¼¶Êǽá¹û¡£ÔÚʹÓùý³ÌÖÐÆäʵ×îºËÐĵÄÓÐÈý¸ö×¢½â£º@EnableHystrix¡¢@HystrixCommandºÍ@HystrixCollapser¡£¿ÉÒÔͨ¹ý×¢½â @HystrixCommand¡¢»òÕ߼̳РHystrixCommand À´ÊµÏÖ½µ¼¶£¬ÒÔ¼°Ò»Ð©ÇëÇóºÏ²¢µÈ²Ù×÷¡£ + +ÔÚÕýʽ½²½âÔ­Àí֮ǰ£¬ÎÒÃÇÊ×ÏÈÒªÃ÷È·Ò»¸öµã£¬µ±²ÉÓà @HystrixCommand ×¢½âÀ´ÊµÏÖ·þÎñ½µ¼¶£¬ÔÚHystrix µÄÄÚ²¿ÊDzÉÓÃAOPµÄ·½Ê½½øÐÐÀ¹½Ø´¦ÀíÇëÇóµÄ£¬Õâ¿éÄÚÈÝ£¬ºóÃæÒ²»áÏêϸ·ÖÎö¡£ÎÒÃÇÕâÀï¾ÍÏÈÀ´ÊµÏÖһϼòÒ×°æµÄ Hystrix À´Ìå»áһϣ¬Ö÷Òª·ÖΪÒÔϲ½Öè + +- ¶¨Òå×Ô¼ºµÄ@HystrixCommand ×¢½â¡£ +- ʵÏÖÀ¹½ØÇëÇóµÄ´¦ÀíÂß¼­¡£ +- ²âÊÔµ÷Óᣠ+ 1.×Ô¶¨Òå×¢½â + + + + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + public @interface MyHystrixCommand { + //ĬÈϳ¬Ê±Ê±¼ä + int timeout() default 1000; + //»ØÍË·½·¨ + String fallback() default ""; + } + + + + + + + + + +2.×Ô¶¨ÒåÇÐÃæÀà + + + + @Aspect //¿ªÆôAspectÖ§³Ö²¢ÇÒ±ê¼ÇΪһ¸öÇÐÃæÀà + @Component + public class MyHystrixCommandAspect { + ExecutorService executorService= Executors.newFixedThreadPool(10); + + //¶¨ÒåÇеã + @Pointcut(value = "@annotation(MyHystrixCommand)") + public void pointCut(){ + + } + //ÔÚÇе㷽·¨Íâ»·ÈÆÖ´ÐÐ @AroundÏ൱ÓÚ@BeforeºÍ@AfterReturning¹¦ÄܵÄ×ÜºÍ + @Around(value = "pointCut()&&@annotation(hystrixCommand)") + public Object doPointCut(ProceedingJoinPoint joinPoint, MyHystrixCommand hystrixCommand) throws Exception { + int timeout=hystrixCommand.timeout(); + Future future=executorService.submit(()->{ + try { + //Ö´ÐÐproceed·½·¨µÄ×÷ÓÃÊÇÈÃÄ¿±ê·½·¨Ö´ÐÐ + return joinPoint.proceed(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + return null; + }); + Object rs; + try { + //ͨ¹ýgetµÄÒì²½µÈ´ýÀ´ÊµÏÖ³¬Ê± + rs=future.get(timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + future.cancel(true); + if(StringUtils.isBlank(hystrixCommand.fallback())){ + throw new Exception("fallback is null"); + } + //µ÷ÓÃfallback + rs=invokeFallback(joinPoint,hystrixCommand.fallback()); + } + return rs; + } + private Object invokeFallback(ProceedingJoinPoint joinPoint,String fallback) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + //»ñÈ¡±»´úÀíµÄ·½·¨²ÎÊýºÍMethod + MethodSignature signature=(MethodSignature)joinPoint.getSignature(); //»ñÈ¡´úÀíÀàºÍ±»´úÀíÀàµÄÐÅÏ¢ + Method method=signature.getMethod(); + Class[] parameterTypes=method.getParameterTypes(); + //µÃµ½»Øµ÷·½·¨ + try { + Method fallbackMethod=joinPoint.getTarget().getClass().getMethod(fallback,parameterTypes); + method.setAccessible(true); + //ͨ¹ý·´É仨µ÷ + return fallbackMethod.invoke(joinPoint.getTarget(),joinPoint.getArgs()); + } catch (Exception e) { + throw e; + } + } + } + + + + + + + + + +3.×Ô¶¨Òå²âÊÔ + + + + @RestController + public class MyHystrixController { + @Autowired + OrderServiceClient orderServiceClient; + @MyHystrixCommand(fallback = "fallback",timeout = 2000) + @GetMapping("/myhystrix/get/{num}") + public String get(@PathVariable("num") int num){ + return orderServiceClient.orderLists(num); + } + public String fallback(int num){ + return "×Ô¶¨Òå×¢½â·½·¨±»½µ¼¶"; + } + } + + + + + + + + + +µ±ÇëÇóhttp://localhost:8080/myhystrix/get/1ʱ»á´¥·¢½µ¼¶£¬ÒòΪÔÚ·þÎñ¶Ë£¬µ±num=1ʱ»áÐÝÃß3s¡£ + +OK£¬ÕâÑùÎÒÃǾÍʵÏÖÁËÒ»¸ö¼òÒ×°æµÄHystrixCommand£¬µ«ÊÇÎÒÃÇÖ»ÊÇʵÏÖÁËHystrixµÄµÚÒ»²½£¬¶¨ÒåÁËÒ»¸ö×¢½âºÍÇÐÃæ£¬µ«ÊÇËüµÄµ×²ãÂß¼­Ô¶Ô¶Ã»ÓÐÕâô¼òµ¥£¬ÔÚ½²Ô´Âë֮ǰ£¬ÎÒÃÇÏÈÀ´ÞÛÒ»ÞÛRxJavaÊÇʲô£¬ÒòΪHystrixµ×²ãÂß¼­ÊÇ»ùÓÚÏìӦʽ±à³ÌʵÏֵġ£ + +µÚ2Õ RxJavaÌåÑé +2.1 RxJava¸ÅÊö +RxJava ÊÇÒ»ÖÖÏìӦʽ±à³Ì£¬À´´´½¨»ùÓÚʼþµÄÒì²½²Ù×÷¿â¡£»ùÓÚʼþÁ÷µÄÁ´Ê½µ÷Óá¢Âß¼­ÇåÎú¼ò½à¡£ + +RxJava¹Û²ìÕßģʽµÄ¶Ô±È + +- ´«Í³¹Û²ìÕßÊÇÒ»¸ö±»¹Û²ìÕß¶à¹ý¹Û²ìÕߣ¬µ±±»¹Û²ìÕß·¢Éú¸Ä±äʱºò¼°Ê±Í¨ÖªËùÓй۲ìÕß +- RxJavaÊÇÒ»¸ö¹Û²ìÕß¶à¸ö±»¹Û²ìÕߣ¬±»¹Û²ìÕßÏñÁ´ÌõÒ»Ñù´®ÆðÀ´£¬Êý¾ÝÔÚ±»¹Û²ìÕßÖ®¼ä³¯×ÅÒ»¸ö·½Ïò´«µÝ£¬Ö±µ½´«µÝ¸ø¹Û²ìÕß ¡£ + Æäʵ˵°×ÁË£¬¾ÍÊÇÔÚRxJavaÖдæÔÚ2ÖÖ¸ÅÄһÖÖÊDZ»¹Û²ìÕߣ¬Ò»ÖÖÊǹ۲ìÕߣ¬µ±¶à¸ö±»¹Û²ìÕß¶©ÔÄÁËͬһ¸ö¹Û²ìÕßµÄʱºò£¬ÄÇÃ´Ëæ×ű»¹Û²ìÕßÍê³Éij¸öʼþµÄʱºò¾Í»áÈ¥»Øµ÷¹Û²ìÕß¡£ + +2.2 ¹Û²ìÕß + +Observer + + + + Observer observer = new Observer() { + @Override + public void onCompleted() { + System.out.println("µ±±»¹Û²ìÕßÉú²úCompleteʼþ£¬µ÷Óø÷½·¨"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("¶ÔErrorʼþ×÷³öÏìÓ¦"); + } + @Override + public void onNext(Object o) { + System.out.println("¶ÔNextʼþ×÷³öÏìÓ¦:" + o); + } + }; + + + + + + + + + + + + //SubscriberÀà = RxJava ÄÚÖõÄÒ»¸öʵÏÖÁË Observer µÄ³éÏóÀ࣬¶Ô Observer ½Ó¿Ú½øÐÐÁËÀ©Õ¹ + Subscriber subscriber = new Subscriber() { + @Override + public void onCompleted() { + System.out.println("µ±±»¹Û²ìÕßÉú²úCompleteʼþ£¬µ÷Óø÷½·¨"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("¶ÔErrorʼþ×÷³öÏìÓ¦"); + } + @Override + public void onNext(Object o) { + System.out.println("¶ÔNextʼþ×÷³öÏìÓ¦:" + o); + } + }; + + + + + + + + + +Subscriber ³éÏóÀàÓëObserver ½Ó¿ÚµÄÇø±ð + +¶þÕß»ù±¾Ê¹Ó÷½Ê½Ò»Ö£¨ÔÚRxJavaµÄsubscribe¹ý³ÌÖУ¬Observer»áÏȱ»×ª»»³ÉSubscriberÔÙʹÓã© +Subscriber³éÏóÀà¶Ô Observer ½Ó¿Ú½øÐÐÁËÀ©Õ¹£¬ÐÂÔöÁËÁ½¸ö·½·¨£º + +- onStart()£ºÔÚ»¹Î´ÏìӦʼþǰµ÷Óã¬ÓÃÓÚ×öһЩ³õʼ»¯¹¤×÷£¬ËûÊÇÔÚsubscribe ËùÔÚµÄÏ̵߳÷Ó㬲»ÄÜÇл»Ị̈߳¬ËùÒÔ²»ÄܽøÐнçÃæUI¸üбÈÈ絯¿òÕâЩ¡£ +- unsubscribe()£ºÓÃÓÚÈ¡Ïû¶©ÔÄ¡£Ôڸ÷½·¨±»µ÷Óú󣬹۲ìÕß½«²»ÔÙ½ÓÊÕÏìӦʼþ£¬±ÈÈçÔÚonStop·½·¨ÖпÉÒÔµ÷Óô˷½·¨½áÊø¶©ÔÄ¡£µ÷Óø÷½·¨Ç°£¬ÏÈʹÓà isUnsubscribed() ÅжÏ״̬£¬È·¶¨±»¹Û²ìÕßObservableÊÇ·ñ»¹³ÖÓй۲ìÕßSubscriberµÄÒýÓᣠ+ 2.3 ±»¹Û²ìÕß + RxJava ÌṩÁ˶àÖÖ·½·¨ÓÃÓÚ ´´½¨±»¹Û²ìÕß¶ÔÏóObservable£¬ÕâÀï½éÉÜÁ½ÖÖ + + + + // ·½·¨1£ºjust(T...)£ºÖ±½Ó½«´«ÈëµÄ²ÎÊýÒÀ´Î·¢ËͳöÀ´ + Observable observable = Observable.just("A", "B", "C"); + // ½«»áÒÀ´Îµ÷Ó㺠+ // onNext("A"); + // onNext("B"); + // onNext("C"); + // onCompleted(); + // ·½·¨2£ºfromArray(T[]) / from(Iterable) : ½«´«ÈëµÄÊý×é / Iterable ²ð·Ö³É¾ßÌå¶ÔÏóºó£¬ÒÀ´Î·¢ËͳöÀ´ + String[] words = {"A", "B", "C"}; + Observable observable = Observable.fromArray(words); + // ½«»áÒÀ´Îµ÷Ó㺠+ // onNext("A"); + // onNext("B"); + // onNext("C"); + // onCompleted(); + + + + + + + + + +2.4 ¶©ÔÄ + + + + observable.subscribe(observer); //½¨Á¢¶©ÔĹØÏµ + + + + + + + + + +2.5 °¸Àý + + + + public class RxJavaDemo { + // ReactiveX Java ÏìӦʽ±à³Ì¿ò¼Ü(android£© + // Java stream() java8 + //¹Û²ìÕßģʽ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final String[] datas = new String[]{"ʼþ1"}; + // ÃüÁîÖ´ÐÐÍêµÄ»Øµ÷²Ù×÷ ÖÕÖ¹ÃüÁîÇåÀí + //»áÔÚObservable½áÊøÇ°´¥·¢»Øµ÷¸Ãcall·½·¨£¬ÎÞÂÛÊÇÕý³£»¹ÊÇÒì³£ÖÕÖ¹ + final Action0 onComplated = new Action0() { + @Override + public void call() { + System.out.println("±»¹Û²ìÕßÒª½áÊøÁË"); + } + }; + //±»¹Û²ìÕß + Observable observable = Observable.defer(new Func0>() { + @Override + public Observable call() { + Observable observable1 = Observable.from(datas); + return observable1.doOnCompleted(onComplated); + } + }); + // Observable observable = Observable.just("ʼþ1","ʼþ2","½áÊø"); + //¹Û²ìÕß + Observer observer = new Observer() { + @Override + public void onCompleted() { + System.out.println("¶ÔComlateʼþ×ö³öÏìÓ¦"); + } + @Override + public void onError(Throwable throwable) { + System.out.println("¶ÔErrorʼþ×÷³öÏìÓ¦"); + } + @Override + public void onNext(Object o) { + System.out.println("¶ÔNextʼþ×÷³öÏìÓ¦:" + o); + } + }; + observable.subscribe(observer); //½¨Á¢¶©ÔĹØÏµ + + // String s = observable.toBlocking().toFuture().get();//Òì²½µÈ´ý½á¹û + // System.out.println(s); + } + } + + + + + + + + + +OK£¬´óÌåµÄÖ¸µ¼ÈçºÎʹÓÃRxJava±à³ÌÁË£¬¼ÇÏÂÀ´ÎÒÃÇ¿ªÊ¼ß£Ô´Âë¡£ + +µÚ3Õ ԴÂë½âÎö +ÏÈÉϹÙÍøÌṩµÄÔ´ÂëÁ÷³Ìͼ£¬´ÓͼÉÏ¿ÉÒÔ¿´³öÀ´£¬Æäʵ¾ÍÊÇÏÈȥɨÃè´øÓÐHystrixCommand×¢½âµÄ·½·¨£¬È»ºó½øÐÐÇÐÃæÀ¹½Ø£¬Ö´ÐÐÇÐÃæµÄÂß¼­¡£Õâ¸öÇÐÃæ¶¨ÒåÁËÁ½¸ö·½·¨£ºexecuteºÍqueue£¬¶þѡһ½øÐе÷Óã¬È»ºó½øÈëÕæÕýµÄÀ¹½ØÂß¼­¡£ËùÒÔÈë¿ÚÊÇHystrixCommand×¢½â£¬¶ø¿ªÆôHystrixÊÇ@EnableHystrix×¢½â¡£ + + + + @SpringBootApplication + @EnableFeignClients("com.example.clients") + //@EnableDiscoveryClient //×¢Ïú±íʾUser·þÎñ²»×¢²á + @EnableHystrix //×¢½â·½Ê½¿ªÆôHystrix + public class HystrixEclipseUserApplication { + + public static void main(String[] args) { + SpringApplication.run(HystrixEclipseUserApplication.class, args); + } + + } + + + + + + + + + +½øÈëµ½@EnableHystrix×¢½âÖÐ + + + + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Inherited + @EnableCircuitBreaker + public @interface EnableHystrix { + } + //×îÖÕ@EnableHystrix¼Ì³ÐÁË@EnableCircuitBreaker + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Inherited + @Import(EnableCircuitBreakerImportSelector.class) + public @interface EnableCircuitBreaker { + } + + + + + + + + + +¿´µ½Õâ²½´úÂ룬ÎÒÏàÐźܶàѧ¹ýspringbootµÄͬѧ¶¼ºÜÊìϤÁË£¬ÕâÀïÓõ½ÁËImport×¢½â£¬Äǿ϶¨ÊÇÒý½øÀ´Ò»Ð©ÅäÖÃÀàÁË£¬È»ºóÎÒÃÇÔÙ½ø +EnableCircuitBreakerImportSelectorÀàÖÐ; + + + + @Order(Ordered.LOWEST_PRECEDENCE - 100) + public class EnableCircuitBreakerImportSelector + extends SpringFactoryImportSelector { + @Override + protected boolean isEnabled() { + return getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled", + Boolean.class, Boolean.TRUE); + } + } + + + + + + + + + +EnableCircuitBreakerImportSelector¼Ì³ÐÁËSpringFactoryImportSelector£¬½øÈëSpringFactoryImportSelectorÀàºó·¢ÏÖÊÇÎÒÃÇÊìϤµÄ´úÂ룬ËüʵÏÖÁËDeferredImportSelector½Ó¿Ú£¬ÊµÏÖÁËselectImports·½·¨£¬selectImports·½·¨»á´ÓÅäÖÃÎļþspring.factoriesÀï¼ÓÔØ¶ÔÓ¦µÄÀà org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker£¬ÎÒÃÇÀ´¿´¿´spring.facotriesÎļþ¡£ + + + + org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerAutoConfiguration,\ + org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration + org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ + org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration + + + + + + + + + +¶ÔÓ¦EnableAutoConfigurationµÄÕâЩʵÏÖÀàÔÚspringÆô¶¯µÄʱºòͨ¹ý×Ô¶¯×°Åä»úÖÆ»áȥʵÀý»¯²¢ÇÒ×¢Èëµ½IoCÈÝÆ÷ÖУ¬ÕâÀïÎÒÃǺËÐĹØ×¢ +HystrixCircuitBreakerConfigurationÀà¡£ + + + + @Configuration(proxyBeanMethods = false) + public class HystrixCircuitBreakerConfiguration { + //ÕâÀïÊǺËÐĵÄÇÐÃæbean + @Bean + public HystrixCommandAspect hystrixCommandAspect() { + return new HystrixCommandAspect(); + } + ... + } + + + + + + + + + +½øÈëµ½Õâ¸öÇÐÃæÀàÖлᷢÏÖ£¬Õâ¸öÇÐÃæÖ÷ÒªÕë¶ÔÁËÁ½¸ö×¢½â×÷ΪÇÐÈëµã@HystrixCommandºÍ@HystrixCollapser£¬µ±Ö´ÐÐÕâÁ½¸ö×¢½âÐÞÊεķ½·¨Ê±£¬»á±»À¹½ØÖ´ÐÐ +methodsAnnotatedWithHystrixCommand + +3.1 HystrixCommandAspect + + + + @Aspect + public class HystrixCommandAspect { + private static final Map META_HOLDER_FACTORY_MAP; + static { + //ͨ¹ý¾²Ì¬·½·¨½«Á½¸ö×¢½âµÄÁ½¸ö¹¤³§ÊµÀý»¯ + META_HOLDER_FACTORY_MAP = ImmutableMap.builder() + .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) + .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) + .build(); + } + //¶¨ÒåÇÐÈëµã×¢½âHystrixCommand + @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") + public void hystrixCommandAnnotationPointcut() { + } + //¶¨ÒåÇÐÈëµã×¢½âHystrixCollapser£¨ÇëÇóºÏ²¢£© + @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") + public void hystrixCollapserAnnotationPointcut() { + } + //»·ÈÆÍ¨Öª + @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") + public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { + //»ñȡĿ±ê·½·¨ + Method method = getMethodFromTarget(joinPoint); + Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint); + //Ö»´¦ÀíÕâÁ½ÖÖ×¢½â±ê×¢µÄ·½·¨ + if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) { + throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " + + "annotations at the same time"); + } + //¸ù¾Ý²»Í¬µÄ×¢½â£¬Ñ¡Ôñ¶ÔÓ¦µÄmetaHolderFactory, ´´½¨MetaHolder, MetaHolder ÀïÃæ°üº¬ÁËËùÓÐÐÅÏ¢ + MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); + //»ñȡĿ±ê·½·¨µÄµÄÔªÊý¾Ý£¬·½·¨Ç©Ãû£¬²ÎÊýµÈ + MetaHolder metaHolder = metaHolderFactory.create(joinPoint); + /** + * ´´½¨´¦ÀíÆ÷CommandCollapser »ò GenericCommand £¨Í¬²½£© »òGenericObservableCommand£¨Òì²½£© + * GenericCommandÀïÓкܶàsuper£¬×îÖÕͨ¹ýHystrixCommandBuilderFactory.getInstance().create(metaHolder) ¹¹½¨ÁËÒ»¸öHystrixCommandBuilder×÷ΪGenericCommadµÄ²ÎÊý + * new GenericCommand ͨ¹ýsuperµ½AbstractHystrixCommand£¬ + * AbstractHystrixCommand ͨ¹ýsuperµ½HystrixCommand£¬ + * HystrixCommand×îÖÕµ½ÁËAbstractCommand һ·´«µÝ + * Ò»»áÔÚAbstractCommandÖзÖÎöÏ + */ + HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); + //¸ù¾Ý·µ»ØÖµÍƶÏÖ´ÐÐÀàÐÍ + ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? + metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); + //¸ù¾Ý²»Í¬µÄÃüÁîÀàÐÍ£¬Ö´ÐÐÃüÁ·µ»Ø½á¹û + Object result; + try { + //ÊÇ·ñÊÇÏìӦʽµÄ£¨ÓÉÓÚÎÒÃÇÕâЩ¶¼ÊÇͬ²½µÄ»á×ßÕâ¸öÂß¼­£© + if (!metaHolder.isObservable()) { + //executeÖ´ÐÐ + result = CommandExecutor.execute(invokable, executionType, metaHolder); + } else { + result = executeObservable(invokable, executionType, metaHolder); + } + } catch (HystrixBadRequestException e) { + throw e.getCause(); + } catch (HystrixRuntimeException e) { + throw hystrixRuntimeExceptionToThrowable(metaHolder, e); + } + return result; + } + //HystrixCommandµÄʱºòMetaHolderµÄ´´½¨ + private static class CommandMetaHolderFactory extends MetaHolderFactory { + @Override + public MetaHolder create(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) { + //»ñȡע½âHystrixCommand + HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class); + //¸ù¾Ý·µ»Ø½á¹ûÍÆ¶ÏÈÎÎñÀàÐÍ£¬¿ÉÒÔÖªµÀÒÔÄÄÖÖ·½Ê½Ö´ÐÐ + ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType()); + MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint); + if (isCompileWeaving()) { + builder.ajcMethod(getAjcMethodFromTarget(joinPoint)); + } + //ÕâÀïûÓжàÉÙ²ÎÊý£¬×îÖØÒªµÄÒ»¸öhystrixCommand£¬ÄãÔÚ×¢½âÀï¼ÓÁËʲô + return builder.defaultCommandKey(method.getName()) + .hystrixCommand(hystrixCommand) + .observableExecutionMode(hystrixCommand.observableExecutionMode()) //Ö´ÐÐģʽ + .executionType(executionType) //Ö´Ðз½Ê½ + .observable(ExecutionType.OBSERVABLE == executionType) + .build(); + } + } + } + //ÔÚö¾ÙExecutionTypeÀàÀï + public static ExecutionType getExecutionType(Class type) { + if (Future.class.isAssignableFrom(type)) { + return ExecutionType.ASYNCHRONOUS; + } else if (Observable.class.isAssignableFrom(type)) { + return ExecutionType.OBSERVABLE; + } else { + return ExecutionType.SYNCHRONOUS; + } + } + + + + + + + + + +ÎÒÃÇÖØµã·ÖÎöÏÂͬ²½´¦Àí£¬Í¨¹ý´úÂëÎÒÃÇ¿ÉÒÔ¿´µ½HystrixInvokable ÊÇ GenericCommand£¬ÎÒÃÇͬ²½ÀïµÄ¿´Ï CommandExecutor.execute(invokable, executionType, metaHolder) + + + + public class CommandExecutor { + public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException { + Validate.notNull(invokable); + Validate.notNull(metaHolder); + + switch (executionType) { + case SYNCHRONOUS: { + //ÖØµã¿´Í¬²½´¦ÀíÕâ¸ö£¬ÏȰÑGenericCommand ת³ÉHystrixExecutable ÔÙÖ´ÐÐexecute + return castToExecutable(invokable, executionType).execute(); + } + case ASYNCHRONOUS: { + // ǿת³ÉHystrixExecutable Òì²½Ö´ÐÐ + HystrixExecutable executable = castToExecutable(invokable, executionType); + // Èç¹ûÓÐ fallback·½·¨£¬ÇÒÊÇÒì²½Ö´ÐУ¬ÔòÖ´Ðв¢·µ»Ø°ü×°½á¹û + if (metaHolder.hasFallbackMethodCommand() + && ExecutionType.ASYNCHRONOUS == metaHolder.getFallbackExecutionType()) { + return new FutureDecorator(executable.queue()); + } + return executable.queue(); + } + case OBSERVABLE: { + // ǿת³É HystrixObservable + HystrixObservable observable = castToObservable(invokable); + // ÅжÏÖ´ÐÐģʽÊDz»ÊǼ±ÇÐ/ÀÁ¶è£¬À´Ñ¡ÔñģʽִÐÐ + return ObservableExecutionMode.EAGER == metaHolder.getObservableExecutionMode() ? observable.observe() : observable.toObservable(); + } + default: + throw new RuntimeException("unsupported execution type: " + executionType); + } + } + } + + + + + + + + + +Õâ¸ö·½·¨Ö÷ÒªÓÃÀ´Ö´ÐÐÃüÁ´Ó´úÂëÖпÉÒÔ¿´³öÕâÀïÓÐÈý¸öÖ´ÐÐÀàÐÍ£¬·Ö±ðÊÇͬ²½¡¢Òì²½¡¢ÒÔ¼°ÏìӦʽ¡£ÆäÖУ¬ÏìӦʽÓÖ·ÖΪCold Observable£¨observable.toObservable()£© ºÍ HotObservable£¨observable.observe()£©Ä¬ÈϵÄexecutionType=SYNCHRONOUS £¬Í¬²½ÇëÇó¡£ + +- execute()£ºÍ¬²½Ö´ÐУ¬·µ»ØÒ»¸öµ¥Ò»µÄ¶ÔÏó½á¹û£¬·¢Éú´íÎóʱÅ׳öÒì³£¡£ +- queue()£ºÒì²½Ö´ÐУ¬·µ»ØÒ»¸ö Future ¶ÔÏ󣬰üº¬×ÅÖ´ÐнáÊøºó·µ»ØµÄµ¥Ò»½á¹û¡£ +- observe()£ºÕâ¸ö·½·¨·µ»ØÒ»¸ö Observable ¶ÔÏó£¬Ëü´ú±í²Ù×÷µÄ¶à¸ö½á¹û£¬µ«ÊÇÒѾ­±»¶©ÔÄÕßÏû·ÑµôÁË¡£ +- toObservable()£ºÕâ¸ö·½·¨·µ»ØÒ»¸ö Observable ¶ÔÏó£¬Ëü´ú±í²Ù×÷µÄ¶à¸ö½á¹û£¬ÐèÒªÔÛÃÇ×Ô¼ºÊÖ¶¯¶©ÔIJ¢Ïû·Ñµô¡£ + Ààͼ¹ØÏµÈçÏ£º + +ͨ¹ýGenericCommandÒ»²ã²ãµÄÍùÉÏ·­£¬×îÖÕ¶¨Î»µ½HystrixCommandÓиöexecute() + + + + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + //ͬ²½Ö´ÐÐ + public R execute() { + try { + //ͨ¹ýqueue().get()À´Í¬²½Ö´ÐУ¨·â×°Òì²½´¦ÀíµÄ½á¹û£© + return queue().get(); + } catch (Exception e) { + throw Exceptions.sneakyThrow(decomposeException(e)); + } + } + //Òì²½Ö´ÐУ¬Ê²Ã´Ê±ºòget()£¬Óɵ÷ÓÃÕß¾ö¶¨£¬get()µÄʱºò»á×èÈû + public Future queue() { + //ºËÐÄ´¦Àí£¬×îÖÕ¶¨Î»µ½ÁËAbstractCommandÀïµÄtoObservable()Àï + // toObservableת»»ÎªObservable,toBlockingת»»ÎªBlockingObservable, + // toFutureת»»ÎªFuture,Íê³ÉÁËObservableµÄ´´½¨ºÍ¶©ÔÄ + final Future delegate = toObservable().toBlocking().toFuture(); + final Future f = new Future() { + ..... + @Override + public R get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + @Override + public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return delegate.get(timeout, unit); + } + }; + //ÌØÊâ´¦ÀíÁËÏ£¬ÒѾ­Ö´ÐÐÍêÁË£¬get()Ò²²»»á×èÈûÁË + if (f.isDone()) { + try { + f.get(); + return f; + } catch (Exception e) { + ... + } + } + return f; + } + } + + + + + + + + + +ÔÚÉÏÊö´úÂëÖУ¬ÖصãÀ´ÁË£¬¹¹½¨ÁËÒ»¸ö +java.util.concurrent.Future £¬È»ºóµ÷Óà getµÄʱºòίÅɸø delegate£¬¶ø delegateÀ´×ÔÓÚ toObservable().toBlocking().toFuture(); ÕâÕýÊÇÎÒÃÇÉÏÃæÀý×ÓÀïÃæµÃ´úÂë¡£ËùÒÔÏÖÔÚµÄÖØµãÓ¦¸Ã·ÅÔÚ toObservable() ·½·¨ÖУº + +3.2 toObservable +ͨ¹ýObservable¶¨ÒåÒ»¸ö±»¹Û²ìÕߣ¬Õâ¸ö±»¹Û²ìÕ߻ᱻtoObservable().toBlocking().toFuture() £¬Êµ¼ÊÉÏÕâÐдúÂëµÄºËÐĺ¬Òå¾ÍÊÇÈ¥¸ù¾ÝһЩÈÛ¶ÏÂß¼­ÅжÏÊÇÖ´ÐÐÕæÊµµÄÒµÎñÂß¼­»¹ÊÇÖ´ÐÐfallbackµÄ»Øµ÷·½·¨£¬È»ºó½«½á¹û·µ»Ø¸øFuture¡£ÀïÃæµÄ run() ·½·¨¾ÍÊÇÖ´ÐÐÕý³£µÄÒµÎñÂß¼­¡£Õâ¸ö·½·¨Ö÷Òª×öÁËÒÔϼ¸¼þÊ£º + +- ´´½¨Ò»¶ÑµÄ¶¯×÷£¬ÎÒÒ²²»ÖªµÀÕâЩ¶¯×÷ÊǸÉɶµÄ£¬²»ÖØÒª¡£ +- ÅжÏÊÇ·ñ¿ªÆôÁË»º´æ£¬Èç¹û¿ªÁË£¬¶øÇÒÒ²ÃüÖÐÁË£¬¾ÍÈ¥»º´æÀïÃæÒÔObservableÐÎʽ·µ»ØÒ»¸ö»º´æ½á¹û +- ´´½¨Ò»¸ö±»¹Û²ìÕߣ¬Õâ¸ö±»¹Û²ìÕߺóÃæ»áÈ¥»Øµ÷ÕæÊµÒµÎñÂß¼­»òÕßfallback¡£ + +ºËÐÄÂß¼­ÊÇÕâ¸ö±»¹Û²ìÕß»áÈ¥Ö´ÐÐapplyHystrixSemanticsÀïÃæµÄ¶¯×÷ + + + + public Observable toObservable() { + final AbstractCommand _cmd = this; + // ÃüÁîÖ´ÐÐÍêµÄ»Øµ÷²Ù×÷ ÖÕÖ¹ÃüÁîÇåÀí + //»áÔÚObservable½áÊøÇ°´¥·¢»Øµ÷¸Ãcall·½·¨£¬ÎÞÂÛÊÇÕý³£»¹ÊÇÒì³£ÖÕÖ¹ + final Action0 terminateCommandCleanup = new Action0() { + ... + }; + // ½«ÃüÁî±ê¼ÇΪÒÑÈ¡Ïû²¢´æ´¢ÑÓ³Ù£¨³ýÁ˱ê×¼ÇåÀí£© + //È¡Ïû¶©ÔÄʱµÄ¼àÌý»á½øÐлص÷¸Ã call·½·¨ + final Action0 unsubscribeCommandCleanup = new Action0() { + @Override + public void call() { + ... + } + }; + // Ö´ÐÐÃüÁîʱµÄ»Øµ÷ + final Func0> applyHystrixSemantics = new Func0>() { + @Override + public Observable call() { + if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { + // Á¢¼´ÖÕÖ¹Õû¸öÁ÷³Ì¡£ + return Observable.never(); + } + //·µ»ØÖ´ÐÐÃüÁîµÄObservable + return applyHystrixSemantics(_cmd); + } + }; + final Func1 wrapWithAllOnNextHooks = new Func1() { + @Override + public R call(R r) { + ... + } + }; + final Action0 fireOnCompletedHook = new Action0() { + @Override + public void call() { + ... + } + }; + // ´´½¨Observable,ÉèÖø÷ÖÖ´¦Àí²Ù×÷ + return Observable.defer(new Func0>() { + @Override + public Observable call() { + // ÉèÖÃÒÑÆô¶¯±êÖ¾, CAS±£Ö¤ÃüÁîÖ»Ö´ÐÐÒ»´Î + if (!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)) { + IllegalStateException ex = new IllegalStateException("This instance can only be executed once. Please instantiate a new instance."); + //TODO make a new error type for this + throw new HystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(), getLogMessagePrefix() + " command executed multiple times - this is not permitted.", ex, null); + } + // ÃüÁʼʱ¼ä´Á + commandStartTimestamp = System.currentTimeMillis(); + // ´òÓ¡ÈÕÖ¾ + if (properties.requestLogEnabled().get()) { + // log this command execution regardless of what happened + if (currentRequestLog != null) { + currentRequestLog.addExecutedCommand(_cmd); + } + } + // »º´æ¿ª¹Ø£¬»º´æKEY£¨Õâ¸öÊÇHystrixÖÐÇëÇ󻺴湦ÄÜ£¬hystrixÖ§³Ö½«Ò»¸öÇëÇó½á¹û»º´æÆðÀ´£¬ + // ÏÂÒ»¸ö¾ßÓÐÏàͬkeyµÄÇëÇó½«Ö±½Ó´Ó»º´æÖÐÈ¡³ö½á¹û£¬¼õÉÙÇëÇó¿ªÏú£© + final boolean requestCacheEnabled = isRequestCachingEnabled(); + final String cacheKey = getCacheKey(); + // Èç¹ûÅäÖÃÔÊÐí»º´æ£¬ÏÈÊÔͼ´Ó»º´æ»ñÈ¡£¬Ä¬ÈÏ false + if (requestCacheEnabled) { + HystrixCommandResponseFromCache fromCache = (HystrixCommandResponseFromCache) requestCache.get(cacheKey); + if (fromCache != null) { + isResponseFromCache = true; + return handleRequestCacheHitAndEmitValues(fromCache, _cmd); + } + } + // ÉùÃ÷Ö´ÐÐÃüÁîµÄObservable + // ´´½¨Observable, applyHystrixSemantics() À´Éú³ÉObservable + Observable hystrixObservable = + Observable.defer(applyHystrixSemantics) + .map(wrapWithAllOnNextHooks); + Observable afterCache; + // put in cache ±£´æÇëÇó½á¹ûµ½»º´æÖÐ + if (requestCacheEnabled && cacheKey != null) { + // wrap it for caching + HystrixCachedObservable toCache = HystrixCachedObservable.from(hystrixObservable, _cmd); + HystrixCommandResponseFromCache fromCache = (HystrixCommandResponseFromCache) requestCache.putIfAbsent(cacheKey, toCache); + if (fromCache != null) { + // another thread beat us so we'll use the cached value instead + toCache.unsubscribe(); + isResponseFromCache = true; + return handleRequestCacheHitAndEmitValues(fromCache, _cmd); + } else { + // we just created an ObservableCommand so we cast and return it + afterCache = toCache.toObservable(); + } + } else { + afterCache = hystrixObservable; + } + // ÉúÃüÖÜÆÚ»Øµ÷ÉèÖà + return afterCache + //»áÔÚObservable½áÊøÇ°´¥·¢»Øµ÷£¬ÎÞÂÛÊÇÕý³£»¹ÊÇÒì³£ÖÕÖ¹ + .doOnTerminate(terminateCommandCleanup) + //È¡Ïû¶©ÔÄʱµÄ¼àÌý + .doOnUnsubscribe(unsubscribeCommandCleanup) + //ObservableÕý³£ÖÕֹʱµÄ¼àÌý + .doOnCompleted(fireOnCompletedHook); + } + }); + } + + + + + + + + + +½ÓÏÂÀ´¿´¿´ºËÐÄÂß¼­applyHystrixSemantics + + + + Observable hystrixObservable = + Observable.defer(applyHystrixSemantics) + .map(wrapWithAllOnNextHooks); + + + + + + + + + + + + final Func0> applyHystrixSemantics = new Func0>() { + @Override + public Observable call() { + if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { + return Observable.never(); + } + return applyHystrixSemantics(_cmd); + } + }; + + + + + + + + + +ÕâÀï´«ÈëµÄ_cmdÊÇÒ»¸öGenericCommand£¬×îÖÕ»áÖ´Ðе½Õâ¸öGenericCommandÖеÄrun·½·¨¡£ + +circuitBreaker.allowRequest() Õâ¸öÊÇÅжÏÊÇ·ñ´¦ÓÚÈÛ¶Ï״̬µÄ£¬true±íʾûÓд¦ÓÚÈÛ¶Ï״̬£¬Õý³£Ö´ÐУ¬·ñÔò£¬µ÷Óà handleShortCircuitViaFallback ʵÏÖ·þÎñ½µ¼¶£¬×îÖÕ»á»Øµ÷µ½ÎÒÃÇ×Ô¶¨ÒåµÄfallback·½·¨ÖС£ + +Èç¹ûµ±Ç°hystrix´¦ÓÚδÈÛ¶Ï״̬£¬Ôò + +- getExecutionSemaphore Åжϵ±Ç°²ßÂÔÊÇ·ñΪÐźÅÁ¿»¹ÊÇÏ̳߳أ¬ÏÔȻĬÈÏÊÇÏ̳߳أ¬È»ºóÔÙµ÷ÓÃtryAcquireʱдËÀÁËΪtrue¡£ + +µ÷ÓÃexecuteCommandAndObserve¡£ + + + + private Observable applyHystrixSemantics(final AbstractCommand _cmd) { + + executionHook.onStart(_cmd); + + // ÊÇ·ñÔÊÐíÇëÇ󣬼´¶Ï·Æ÷ÊÇ·ñ¿ªÆô £¬ÕâÀïÒ²Óкü¸ÖÖÇé¿ö + if (circuitBreaker.allowRequest()) { + // ÐźÅÁ¿»ñÈ¡ + final TryableSemaphore executionSemaphore = getExecutionSemaphore(); + final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false); + + // ÐźÅÊͷŻص÷ + final Action0 singleSemaphoreRelease = new Action0() { + @Override + public void call() { + if (semaphoreHasBeenReleased.compareAndSet(false, true)) { + executionSemaphore.release(); + } + } + }; + + // Òì³£»Øµ÷ + final Action1 markExceptionThrown = new Action1() { + @Override + public void call(Throwable t) { + eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey); + } + }; + + // »ñÈ¡Ðźţ¬²¢·µ»Ø¶ÔÓ¦µÄ Observable + // ÊÇ·ñ¿ªÆôÐźÅÁ¿×ÊÔ´¸ôÀ룬δÅäÖÃ×ß com.netflix.hystrix.AbstractCommand.TryableSemaphoreNoOp#tryAcquire ĬÈÏ·µ»ØÍ¨¹ý + if (executionSemaphore.tryAcquire()) { + try { + executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis()); + return executeCommandAndObserve(_cmd) // Ö´ÐÐÃüÁÒÔÏÂÈý¸öÊǻص÷£¬¿ÉÒÔ²»¿´ + .doOnError(markExceptionThrown) + .doOnTerminate(singleSemaphoreRelease) + .doOnUnsubscribe(singleSemaphoreRelease); + } catch (RuntimeException e) { + return Observable.error(e); + } + } else { + // »ñÈ¡ÐźÅʧ°ÜÔò½µ¼¶ + return handleSemaphoreRejectionViaFallback(); + } + } else { + // ¶Ï·Æ÷ÒÑ´ò¿ª£¬Ö±½Ó½µ¼¶ + return handleShortCircuitViaFallback(); + } + } + + + + + + + + + +ÏÈÀ´¿´Ò»ÏÂÖ´ÐÐʧ°Ü½øÈë½µ¼¶µÄÂß¼­£¬ÕâÀïÎÒÃÇÖ±½Ó½øÈëµ½ HystrixCommand#getFallbackObservable + + + + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + @Override + final protected Observable getFallbackObservable() { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + try { + return Observable.just(getFallback()); + } catch (Throwable ex) { + return Observable.error(ex); + } + } + }); + } + } + + + + + + + + + +ÕâÀïµÄgetFallback×îÖÕ»á»Øµ÷ÎÒÃÇ×Ô¶¨µÄfallback·½·¨¡£ + +»Øµ½executeCommandAndObserve£¬Õâ¸ö·½·¨Ö÷Òª×öÁËÒÔÏÂÈý¼þÊÂÇé + +- ¶¨Ò岻ͬµÄ»Øµ÷£¬doOnNext¡¢doOnCompleted¡¢onErrorResumeNext¡¢doOnEach¡£ +- µ÷ÓÃexecuteCommandWithSpecifiedIsolation¡£ + +ÈôÖ´ÐÐÃüÁî³¬Ê±ÌØÐÔ¿ªÆô£¬µ÷Óà Observable.lift·½·¨ÊµÏÖÖ´ÐÐÃüÁʱ¹¦ÄÜ¡£ + + + + private Observable executeCommandAndObserve(final AbstractCommand _cmd) { + final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread(); + // ActionºÍFunc¶¼ÊǶ¨ÒåµÄÒ»¸ö¶¯×÷£¬ActionÊÇÎÞ·µ»ØÖµ£¬FuncÊÇÓзµ»ØÖµ + // doOnNextÖеĻص÷¡£¼´ÃüÁîÖ´ÐÐ֮ǰִÐеIJÙ×÷ + final Action1 markEmits = new Action1() { + @Override + public void call(R r) { + if (shouldOutputOnNextEvents()) { + executionResult = executionResult.addEvent(HystrixEventType.EMIT); + eventNotifier.markEvent(HystrixEventType.EMIT, commandKey); + } + if (commandIsScalar()) { + long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); + eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); + eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); + executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); + circuitBreaker.markSuccess(); + } + } + }; + // doOnCompletedÖеĻص÷¡£ÃüÁîÖ´ÐÐÍê±ÏºóÖ´ÐеIJÙ×÷ + final Action0 markOnCompleted = new Action0() { + @Override + public void call() { + if (!commandIsScalar()) { + long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); + eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); + eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); + executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); + circuitBreaker.markSuccess(); + } + } + }; + // onErrorResumeNextÖеĻص÷¡£ÃüÁîÖ´ÐÐʧ°ÜºóµÄ»ØÍËÂß¼­ + final Func1> handleFallback = new Func1>() { + @Override + public Observable call(Throwable t) { + Exception e = getExceptionFromThrowable(t); + executionResult = executionResult.setExecutionException(e); + if (e instanceof RejectedExecutionException) { + // Ï̵߳÷¶Èʧ°Ü»Øµ÷ + return handleThreadPoolRejectionViaFallback(e); + } else if (t instanceof HystrixTimeoutException) { + // ³¬Ê±»Øµ÷ + return handleTimeoutViaFallback(); + } else if (t instanceof HystrixBadRequestException) { + // HystrixBadRequestException Òì³£»Øµ÷ + return handleBadRequestByEmittingError(e); + } else { + if (e instanceof HystrixBadRequestException) { + eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey); + return Observable.error(e); + } + // ½µ¼¶´¦Àí + return handleFailureViaFallback(e); + } + } + }; + // doOnEachÖеĻص÷¡£`Observable`ÿ·¢ÉäÒ»¸öÊý¾Ý¶¼»áÖ´ÐÐÕâ¸ö»Øµ÷£¬ÉèÖÃÇëÇóÉÏÏÂÎÄ + final Action1> setRequestContext = new Action1>() { + @Override + public void call(Notification rNotification) { + setRequestContextIfNeeded(currentRequestContext); + } + }; + // ´´½¨¶ÔÓ¦µÄ Observable£¬ÊµÏÖ Ï̸߳ôÀë¡¢ÇëÇó·¢ËÍ µÈ²Ù×÷ + Observable execution; + // ÅÐ¶Ï ³¬Ê±¼à¿Ø¹¦ÄÜÊÇ·ñ´ò¿ª + if (properties.executionTimeoutEnabled().get()) { + // HystrixObservableTimeoutOperator ת»»¶ÔÓ¦µÄ Observable + execution = executeCommandWithSpecifiedIsolation(_cmd) + .lift(new HystrixObservableTimeoutOperator(_cmd)); + } else { + execution = executeCommandWithSpecifiedIsolation(_cmd); + } + //ÉèÖûص÷ + return execution.doOnNext(markEmits) + .doOnCompleted(markOnCompleted) + .onErrorResumeNext(handleFallback) + .doOnEach(setRequestContext); + } + + + + + + + + + +3.3 executeCommandWithSpecifiedIsolation +Õâ¸ö·½·¨Ê×ÏÈÊǸù¾Ýµ±Ç°²»Í¬µÄ×ÊÔ´¸ôÀë²ßÂÔÖ´Ðв»Í¬µÄÂß¼­£¬THREAD¡¢SEMAPHORE¡£ + + + + private Observable executeCommandWithSpecifiedIsolation(final AbstractCommand _cmd) { + // Ï̸߳ôÀë, ÊÇ·ñ¿ªÆô THREAD ×ÊÔ´¸ôÀë½µ¼¶ + if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) { + //´´½¨Ò»¸öObservable + return Observable.defer(new Func0>() { + @Override + public Observable call() { + executionResult = executionResult.setExecutionOccurred(); + if (!commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.USER_CODE_EXECUTED)) { + return Observable.error(new IllegalStateException("execution attempted while in state : " + commandState.get().name())); + } + + metrics.markCommandStart(commandKey, threadPoolKey, ExecutionIsolationStrategy.THREAD); + + // ¸ÃÃüÁîÔÚ°ü×°Ïß³ÌÖг¬Ê±£¬½«Á¢¼´·µ»Ø£¬²¢ÇÒ²»»áÔö¼ÓÈκμÆÊýÆ÷»òÆäËû´ËÀàÂß¼­ + if (isCommandTimedOut.get() == TimedOutStatus.TIMED_OUT) { + // the command timed out in the wrapping thread so we will return immediately + // and not increment any of the counters below or other such logic + return Observable.error(new RuntimeException("timed out before executing run()")); + } + + // ÉèÖÃÏß³ÌÆô¶¯ + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.STARTED)) { + //we have not been unsubscribed, so should proceed + HystrixCounters.incrementGlobalConcurrentThreads(); + threadPool.markThreadExecution(); + // store the command that is being run + endCurrentThreadExecutingCommand = Hystrix.startCurrentThreadExecutingCommand(getCommandKey()); + executionResult = executionResult.setExecutedInThread(); + + try { + executionHook.onThreadStart(_cmd); + executionHook.onRunStart(_cmd); + executionHook.onExecutionStart(_cmd); + //·µ»Ø Observable,Õâ¸öº¯Êý×îÖջ᷵»ØÒ»¸ö·â×°ÁËÎÒÃǵÄrun()Âß¼­µÄObservable + return getUserExecutionObservable(_cmd); + } catch (Throwable ex) { + return Observable.error(ex); + } + } else { + //command has already been unsubscribed, so return immediately + return Observable.error(new RuntimeException("unsubscribed before executing run()")); + } + } + }).doOnTerminate(new Action0() { + @Override + public void call() { + if (threadState.compareAndSet(ThreadState.STARTED, ThreadState.TERMINAL)) { + handleThreadEnd(_cmd); + } + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.TERMINAL)) { + } + } + }).doOnUnsubscribe(new Action0() { + @Override + public void call() { + if (threadState.compareAndSet(ThreadState.STARTED, ThreadState.UNSUBSCRIBED)) { + handleThreadEnd(_cmd); + } + if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.UNSUBSCRIBED)) { + } + } + }).subscribeOn(threadPool.getScheduler(new Func0() { + @Override + public Boolean call() { + return properties.executionIsolationThreadInterruptOnTimeout().get() && _cmd.isCommandTimedOut.get() == TimedOutStatus.TIMED_OUT; + } + })); + } else { + // ÐźÅÁ¿¸ôÀë + return Observable.defer(new Func0>() { + @Override + public Observable call() { + executionResult = executionResult.setExecutionOccurred(); + if (!commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.USER_CODE_EXECUTED)) { + return Observable.error(new IllegalStateException("execution attempted while in state : " + commandState.get().name())); + } + metrics.markCommandStart(commandKey, threadPoolKey, ExecutionIsolationStrategy.SEMAPHORE); + endCurrentThreadExecutingCommand = Hystrix.startCurrentThreadExecutingCommand(getCommandKey()); + try { + executionHook.onRunStart(_cmd); + executionHook.onExecutionStart(_cmd); + // ÕæÕýµÄÖ´ÐÐ + return getUserExecutionObservable(_cmd); + } catch (Throwable ex) { + //If the above hooks throw, then use that as the result of the run method + return Observable.error(ex); + } + } + }); + } + } + + + + + + + + + +- ÅжÏÊÇ·ñÔÊÐí·¢ËÍÇëÇó£¬ÕâÊÇ»ùÓÚ¶Ï·Æ÷ʵÏÖ£¬Èç¹û¶Ï·Æ÷´ò¿ª£¬Ôò½øÐжÔÓ¦»Øµ÷´¦Àí£¨Ê§°Ü»ò½µ¼¶£©¡£ +- Èç¹û ¶Ï·Æ÷ ¹Ø±Õ£¬Ôò½øÐÐÇëÇó£¬ÏÈ»ñÈ¡Ðźţ¬»ñȡʧ°ÜÔò´¦Àí¶ÔÓ¦»Øµ÷¡£ +- »ñÈ¡³É¹¦£¬ÔòÓÉ·½·¨ executeCommandAndObserve ´´½¨¶ÔÓ¦µÄ Observable ʵÏÖ Ï̸߳ôÀë¡¢ÇëÇó·¢ËÍ µÈ²Ù×÷£¬Í¬Ê±×¢²áÁ˶ÔÓ¦µÄ ÉúÃüÖÜÆÚ»Øµ÷¡£ + 3.4 getUserExecutionObservable + È»ºó»áÖ´ÐÐ HystrixCommand#getExecutionObservable + + + + abstract class AbstractCommand implements HystrixInvokableInfo, HystrixObservable { + private Observable getUserExecutionObservable(final AbstractCommand _cmd) { + Observable userObservable; + try { + userObservable = getExecutionObservable(); + } catch (Throwable ex) { + userObservable = Observable.error(ex); + } + return userObservable + .lift(new ExecutionHookApplication(_cmd)) + .lift(new DeprecatedOnRunHookApplication(_cmd)); + } + } + public abstract class HystrixCommand extends AbstractCommand implements HystrixExecutable, HystrixInvokableInfo, HystrixObservable { + @Override + final protected Observable getExecutionObservable() { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + try { + return Observable.just(run()); + } catch (Throwable ex) { + return Observable.error(ex); + } + } + }).doOnSubscribe(new Action0() { + @Override + public void call() { + // Save thread on which we get subscribed so that we can interrupt it later if needed + executionThread.set(Thread.currentThread()); + } + }); + } + } + + + + + + + + + +Õâ¸ö run() ·½·¨ÔÚÉÏÃæÒѾ­½²¹ýÁË£¬¾ÍÊÇÕæÕýµÄÒµÎñÖ´Ðз½·¨¡£ + + + + @ThreadSafe + public class GenericCommand extends AbstractHystrixCommand { + @Override + protected Object run() throws Exception { + LOGGER.debug("execute command: {}", getCommandKey().name()); + return process(new Action() { + @Override + Object execute() { + return getCommandAction().execute(getExecutionType()); + } + }); + } + } + + + + + + + + + +×îÖÕµ÷Óõ½ÎÒÃÇ×Ô¼ºµÄÒµÎñÂß¼­¡£ +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..f66f6ba --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudLoadBalancer\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,670 @@ +# Spring Cloud LoadBalancer + +## ¸ÅÊö + +> Spring Cloud LoadBalancerĿǰSpring¹Ù·½ÊÇ·ÅÔÚspring-cloud-commonsÀSpring Cloud×îа汾Ϊ2021.0.2 +> +> [Spring Cloud LoadBalancer ¹ÙÍøÎĵµµØÖ·](https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer) [https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer](https://docs.spring.io/spring-cloud-commons/docs/3.1.2/reference/html/#spring-cloud-loadbalancer) +> +> [Spring Cloud¹ÙÍøÎĵµµØÖ·](https://docs.spring.io/spring-cloud/docs/current/reference/html/) [https://docs.spring.io/spring-cloud/docs/current/reference/html/](https://docs.spring.io/spring-cloud/docs/current/reference/html/) + +Ò»·½ÃæNetflix RibbonÍ£Ö¹¸üУ¬Spring Cloud LoadBalancerÊÇSpring Cloud¹Ù·½×Ô¼ºÌṩµÄ¿Í»§¶Ë¸ºÔؾùºâÆ÷,³éÏóºÍʵÏÖ£¬ÓÃÀ´Ìæ´úRibbon¡£ + +* ³£¼û¸ºÔؾùºâÆ÷·ÖΪ·þÎñ¶Ë¸ºÔؾùºâÆ÷(ÈçÍø¹Ø²ã¾ùºâ¸ºÔØ)ºÍ¿Í»§¶Ë²ã¾ùºâ¸ºÔØ¡£ + * Íø¹Ø²ãÈçÓ²¼þ²ãÃæµÄF5»òÈí¼þ²ãÃæµÄLVS¡¢»òÕßnginxµÈ¡£ + * ¿Í»§¶Ë²ã¾ÍÈçSpring Cloud LoadBalancer£¬×÷Ϊһ¸ö¿Í»§¶ËÈ¥·¢ÏÖ¸üÐÂά»¤·þÎñÁÐ±í£¬×Ô¶¨Òå·þÎñµÄ¾ùºâ¸ºÔزßÂÔ£¨Ëæ»ú¡¢ÂÖѯ¡¢Ð¡Á÷Á¿µÄ½ð˿ȸµÈµÈ£©¡£ + +Spring CloudÌṩÁË×Ô¼ºµÄ¿Í»§¶Ë¸ºÔØÆ½ºâÆ÷³éÏóºÍʵÏÖ¡£¶ÔÓÚ¸ºÔؾùºâ»úÖÆ£¬Ôö¼ÓÁËReactiveLoadBalancer½Ó¿Ú£¬²¢ÌṩÁË»ùÓÚround-robinÂÖѯºÍRandomËæ»úµÄʵÏÖ¡£ÎªÁË´ÓÏìӦʽServiceInstanceListSupplierÖÐÑ¡ÔñʵÀý£¬ÐèҪʹÓÃServiceInstanceListSupplier¡£Ä¿Ç°Ö§³ÖServiceInstanceListSupplierµÄ»ùÓÚ·þÎñ·¢ÏÖµÄʵÏÖ£¬¸ÃʵÏÖʹÓÃÀà·¾¶Öеķ¢ÏÖ¿Í»§¶Ë´ÓService DiscoveryÖмìË÷¿ÉÓõÄʵÀý¡£ + +¿ÉÒÔͨ¹ýÈçÏÂÅäÖÃÀ´½ûÓÃSpring Cloud LoadBalance + +``` +spring: + cloud: + loadbalancer: + enabled: false + +``` + +## ÈëÃÅʾÀý + +Ç°Ãæsimple-ecommerceÏîÄ¿´´½¨ÒÑÔÚ¸¸PomÒýÈëÈý´ó¸¸ÒÀÀµ£¬Ïêϸ¿ÉÒÔ¿´ÏÂÇ°ÃæµÄÎÄÕÂ<>£¬ÆäÖÐSpring CloudµÄ°æ±¾Îª2021.0.1£¬Ç°ÃæÎÄÕÂÒ²ÒÑ˵¹ý£¬Spring Cloud AlibabaÕûºÏÔÚspring-cloud-starter-alibaba-nacos-discovery±¾Éí¾ÍÒÀÀµspring-cloud-loadbalancer¡£ + +> ×¢ÒâÈç¹ûÊÇHoxton֮ǰµÄ°æ±¾£¬Ä¬ÈϸºÔؾùºâÆ÷ΪRibbon£¬ÐèÒªÒÆ³ýRibbonÒýÓúÍÔö¼ÓÅäÖÃspring.cloud.loadbalancer.ribbon.enabled: false¡£ + +Èç¹ûÊÇÔÚSpring BootÏîÄ¿ÖÐÌí¼ÓÏÂÃæµÄÆô¶¯Æ÷ÒÀÀµ£¬¸ÃstarterÒ²°üº¬ÁËSpring Boot Caching and Evictor. + +``` + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + +``` + +ÎÒÃÇʹÓÃSpring¹Ù·½ÌṩÁ˸ºÔؾùºâµÄ¿Í»§¶ËÖ®Ò»RestTemplate£¬RestTemplateÊÇSpringÌṩµÄÓÃÓÚ·ÃÎÊRest·þÎñµÄ¿Í»§¶Ë£¬RestTemplateÌṩÁ˶àÖÖ±ã½Ý·ÃÎÊÔ¶³ÌHttp·þÎñµÄ·½·¨£¬Äܹ»´ó´óÌá¸ß¿Í»§¶ËµÄ±àдЧÂÊ¡£Ä¬ÈÏÇé¿öÏ£¬RestTemplateĬÈÏÒÀÀµjdkµÄHTTPÁ¬½Ó¹¤¾ß¡£´´½¨RestTemplateConfigÅäÖÃÀ࣬±ê×¢ @LoadBalanced×¢½â£¬Ä¬ÈÏʹÓõÄReactiveLoadBalancerʵÏÖÊÇRoundRobinLoadBalancer¡£ + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate() ; + } +} + +``` + +¶©µ¥Î¢·þÎñÖж©µ¥¿ØÖÆÆ÷Ôö¼ÓdeductRest·½·¨ + +``` +package cn.itxs.ecom.order.controller; + +import cn.itxs.ecom.commons.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * @Name £ºOrderController + * @Description £º¶©µ¥¿ØÖÆÆ÷ + * @Author £ºitxs + * @Date £º2022/4/10 20:15 + * @Version £º1.0 + * @History £º + */ +@RestController +public class OrderController { + + @Autowired + OrderService orderService; + + @Autowired + private RestTemplate restTemplate; + + @RequestMapping("/create/{userId}/{commodityCode}/{count}") + public String create(@PathVariable("userId") String userId,@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){ + return orderService.create(userId,commodityCode,count).toString(); + } + + @RequestMapping("/deductRest/{commodityCode}/{count}") + public String deductRest(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){ + String url = "http://ecom-storage-service/deduct/"+commodityCode+"/"+count; + return restTemplate.getForObject(url, String.class); + } +} + +``` + +Ç°Ãæserver.portÎÒÃÇÊÇ·ÅÔÚNacosÅäÖÃÖÐÐÄÀÕâÀïÎÒÃÇ×¢ÊÍNacosÅäÖÃÖÐÐĵÄÅäÖ÷ÅÔÚ±¾µØÅäÖÃÎļþbootstrap.ymlÀ·Ö±ðÅäÖÃΪ4080¡¢4081¡¢4082Æô¶¯3¸ö¿â´æ·þÎñʵÀý£¬²¢Æô¶¯¶©µ¥Î¢·þÎñ + +``` +server: + port: 4080 + +``` + +![image-20220505191143684](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/938570a0b63b56671a862e8bda11577a.png) + +²é¿´nacos·þÎñ¹ÜÀí-·þÎñÁбíÀï·þÎñÏêÇ飬¿ÉÒÔ¿´µ½3¸ö½¡¿µµÄ¿â´æÊµÀýºÍ1¸ö¶©µ¥Î¢·þÎñʵÀý + +![image-20220505182432182](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/33fa03d937354fe00bb0bb2f3dd5c805.png) + +·ÃÎÊ6´Î¶©µ¥dedect½Ó¿Ú£º[http://localhost:4070/deductRest/1001/1](http://localhost:4070/deductRest/1001/1) £¬´Ó²âÊԵĽá¹ûÒ²ÑéÖ¤ÁËLoadBalancerĬÈÏÊÇÂÖѯ¸ºÔؾùºâ²ßÂÔ¡£ + +![image-20220505192217715](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1fc20b1d482d2a4e3924c5707ac2ff19.png) + +## ¸ºÔؾùºâËã·¨Çл» + +´´½¨×Ô¶¨Òå¸ºÔØ¾ùºâÅäÖÃÀàCustomLoadBalancerConfiguration + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +public class CustomLoadBalancerConfiguration { + + @Bean + ReactorLoadBalancer randomLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RandomLoadBalancer(loadBalancerClientFactory + .getLazyProvider(name, ServiceInstanceListSupplier.class), + name); + } +} + +``` + +RestTemplateConfigÅäÖÃÀàLoadBalancerClientÖ¸¶¨Ëæ»úµÄÅäÖÃÀ࣬valueµÄֵΪÌṩÕßÒ²¼´ÊÇ¿â´æÎ¢·þÎñÃû³Æ¡£ + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +@LoadBalancerClient(value = "ecom-storage-service", configuration = CustomLoadBalancerConfiguration.class) +public class RestTemplateConfig { + + @LoadBalanced + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build() ; + } +} + +``` + +¶à´Î·ÃÎʶ©µ¥dedect½Ó¿Ú²âÊÔÈ·ÈÏÒÑÇл»ÎªËæ»ú¸ºÔؾùºâ²ßÂÔ¡£ + +## ¼¯³É·½Ê½ + +¹ÙÍøÌṩ3Öм¯³ÉSpring Cloud LoadBalancerµÄ·½Ê½£¬³ýÁ˵ÚÒ»ÖÖÉÏÃæÒÑʹÓùý£¬»¹Ö§³ÖSpring Web FluxÏìӦʽ±à³Ì£¬WebClientÊÇ´ÓSpring WebFlux 5.0°æ±¾¿ªÊ¼ÌṩµÄÒ»¸ö·Ç×èÈûµÄ»ùÓÚÏìӦʽ±à³ÌµÄ½øÐÐHttpÇëÇóµÄ¿Í»§¶Ë¹¤¾ß¡£ËüµÄÏìӦʽ±à³ÌµÄ»ùÓÚReactorµÄ¡£WebClientÖÐÌṩÁ˱ê×¼HttpÇëÇó·½Ê½¶ÔÓ¦µÄget¡¢post¡¢put¡¢deleteµÈ·½·¨£¬¿ÉÒÔÓÃÀ´·¢ÆðÏàÓ¦µÄÇëÇó¡£ + +![image-20220506233248585](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/ad44f363342f540b1cb1805b20be6ef0.png) + +ÔÚ¶©µ¥Î¢·þÎñÖÐÒýÈëspring-boot-starter-webfluxÒÀÀµ + +``` + + org.springframework.boot + spring-boot-starter-webflux + + +``` + +¶©µ¥Î¢·þÎñÖÐÔö¼ÓWebClientConfigÅäÖÃÀà + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + @LoadBalanced + @Bean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + + @Bean + WebClient webClient() { + return webClientBuilder().build(); + } +} + +``` + +¶©µ¥Î¢·þÎñ¶©µ¥¿ØÖÆÆ÷ÖÐÌí¼ÓWebClient½Ó¿ÚʵÏÖ£¬´úÂëÈçÏ + +``` + @Autowired + private WebClient webClient; + + @RequestMapping(value = "/deductWebClient/{commodityCode}/{count}") + public Mono deductWebClient(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count) { + String url = "http://ecom-storage-service/deduct/"+commodityCode+"/"+count; + // »ùÓÚWebClient + Mono result = webClient.get().uri(url) + .retrieve().bodyToMono(String.class); + return result; + } + +``` + +ÖØÐÂÆô¶¯¶©µ¥Î¢·þÎñ + +![image-20220506234934948](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/ec48e022accfb3298069c96b7e3799e7.png) + +·ÃÎʶ©µ¥¿ØÖÆÆ÷Öеļõ¿â´æWebClient½Ó¿Ú£¬[http://localhost:4070/deductWebClient/1001/1](http://localhost:4070/deductWebClient/1001/1) £¬½á¹û·µ»Ø³É¹¦ + +![image-20220506234627330](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/5f737fc9d8806dc072ce3e1fdf983de6.png) + +ÎÒÃÇ»¹¿ÉÒÔÅäÖûùÓÚ¹ýÂËÆ÷µÄ·½Ê½£¬Í¨¹ýWebClientʹÓÃReactiveLoadBalancer¡£Èç¹ûÏîÄ¿ÖÐÌí¼ÓÁËSpring Cloud LoadBalancer starter£¬²¢ÇÒSpring -webfluxÔÚÀà·¾¶ÖУ¬ReactorLoadBalancerExchangeFilterFunctionÔòÊÇ×Ô¶¯ÅäÖõġ£ + +¶©µ¥Î¢·þÎñ¶©µ¥¿ØÖÆÆ÷ÖÐÌí¼ÓWebClientʹÓÃReactiveLoadBalancer½Ó¿ÚʵÏÖ£¬´úÂëÈçÏ + +``` + @Autowired + private ReactorLoadBalancerExchangeFilterFunction lbFunction; + + @RequestMapping(value = "/deductWebFluxReactor/{commodityCode}/{count}") + public Mono deductWebFluxReactor(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count) { + String url = "/deduct/"+commodityCode+"/"+count; + Mono result = WebClient.builder().baseUrl("http://ecom-storage-service") + .filter(lbFunction) + .build() + .get() + .uri(url) + .retrieve() + .bodyToMono(String.class); + return result; + } + +``` + +ÖØÐÂÆô¶¯¶©µ¥Î¢·þÎñ + +![image-20220507000930179](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/3546b9913f1253a1b23039ad6f7546d1.png) + +·ÃÎʶ©µ¥¿ØÖÆÆ÷Öеļõ¿â´æWebClient½Ó¿Ú£¬[http://localhost:4070/deductWebFluxReactor/1001/1](http://localhost:4070/deductWebFluxReactor/1001/1) £¬½á¹û·µ»Ø³É¹¦ + +![image-20220507000746900](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/0bc2b04d23d11bc85009c7e040900b2b.png) + +¹ØÓÚLoadBalancer¹ÙÍø»¹ÌṩºÜ¶àÆäËû¹¦ÄÜ£¬ÓÐÐËȤ¿É×ÔÐÐÏêϸ²éÔĺͶ¯ÊÖʵÑé + +![image-20220507001132987](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1d8f35933945b317a2dd71627fdfa748.png) + +## Ô­Àí + +### RestTemplate + +Spring Cloud LoadBalancerÔ´Âë·ÖÎöÎÒÃÇÏÈ´ÓRestTemplate¸ºÔؾùºâµÄ¼òµ¥ÊµÏÖÀ´·ÖÎöÈëÊÖ£¬³ý´ËÖ®ÍâÆäÖ§³ÖSpring Web FluxÏìӦʽ±à³ÌµÄʵÏÖÔ­Àí˼ÏëÒ²ÊÇÏàͬ£¬¶¼ÊÇͨ¹ý¿Í»§¶ËÌí¼ÓÀ¹½ØÆ÷£¬ÔÚÀ¹½ØÆ÷ÖÐʵÏÖ¸ºÔؾùºâ¡£´ÓRestTemplateµÄÔ´ÂëÖпÉÒÔÖªµÀÆä¼Ì³Ð×ÔInterceptingHttpAccessor³éÏóÀà + +![image-20220508142236428](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c3ee7af69f54aeed5f1026823779fee8.png) + +¶øInterceptingHttpAccessor³éÏóÀàÔòÌṩÁËÒ»¸ö·½·¨setInterceptors£¬ÓÃÓÚÉèÖÃÀ¹½ØÆ÷£¬À¹½ØÆ÷ÐèҪʵÏÖClientHttpRequestInterceptor½Ó¿Ú¼´¿É£¬ÔÚʵ¼ÊÔ¶³ÌÇëÇó·þÎñ¶Ë½Ó¿Ú֮ǰ»áÏȵ÷ÓÃÀ¹½ØÆ÷µÄintercept·½·¨¡£ÕâÀïµÄÀ¹½ØÆ÷Ï൱ÓÚServlet¼¼ÊõÖеÄFilter¹¦ÄÜ + +``` + // ´úÂëʵÏÖÔÚ³éÏó¸¸ÀàInterceptingHttpAccessorÀï + // RestTemplate.InterceptingHttpAccessor#setInterceptors + public void setInterceptors(List interceptors) { + Assert.noNullElements(interceptors, "'interceptors' must not contain null elements"); + // Take getInterceptors() List as-is when passed in here + if (this.interceptors != interceptors) { + this.interceptors.clear(); + this.interceptors.addAll(interceptors); + AnnotationAwareOrderComparator.sort(this.interceptors); + } + } + +``` + +![image-20220508142443637](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/5062f793bb2bd7f996eb944f77af7137.png) + +### **LoadBalancerAutoConfiguration** + +´Ó¹ÙÍø¿ÉÒÔÖªµÀSpring Cloud LoadBalancer·ÅÔÚspring-cloud-commons£¬Òò´ËÒ²×÷ΪÆäºËÐĵÄ@LoadBalanced×¢½âÒ²¾ÍÊÇÓÉspring-cloud-commonsÀ´ÊµÏÖ£¬ÒÀ¾ÝSpringBoot×Ô¶¯×°ÅäµÄÔ­ÀíÏȲ鿴ÒÀÀµ°üµÄʵÏÖÂß¼­£¬²»ÄÑ·¢ÏÖspring-cloud-commonsÒýÈëÁË×Ô¶¯ÅäÖÃÀàLoadBalancerAutoConfigurationºÍReactorLoadBalancerClientAutoConfiguration¡£ + +![image-20220509001530634](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b5be12e0dd8d515aa07613738efab122.png) + +µ±Âú×ãÉÏÊöµÄÌõ¼þʱ£¨@ConditionalΪÌõ¼þ×¢½â£©£¬½«×Ô¶¯´´½¨LoadBalancerInterceptor²¢×¢Èëµ½RestTemplateÖС£ + +![image-20220508143752218](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/676f6d81529228511c862c780b0c4532.png) + +### **LoadBalancerLnterceptor** + +LoadBalancerInterceptorʵÏÖÁËClientHttpRequestInterceptor½Ó¿Ú£¬Òò´ËҲʵÏÖintercept·½·¨£¬ÓÃÓÚʵÏÖ¸ºÔؾùºâµÄÀ¹½Ø´¦Àí¡£ + +![image-20220508144048248](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/bb587d8122fee4973eba2fc00ed1af3f.png) + +### **LoadBalancerClient** + +LoadBalancerClientÓÃÓÚ½øÐиºÔؾùºâÂß¼­£¬¼Ì³Ð×ÔServiceInstanceChooser½Ó¿Ú£¬´Ó·þÎñÁбíÖÐÑ¡Ôñ³öÒ»¸ö·þÎñµØÖ·½øÐе÷Óá£ÔÚLoadBalancerClientÖÖ´æÔÚÁ½¸öexecute()·½·¨£¬¾ùÊÇÓÃÀ´Ö´ÐÐÇëÇóµÄ£¬reconstructURI()ÊÇÓÃÀ´Öع¹URL¡£ + +![image-20220508144435104](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/a3590c0ab24cb5ee04cf1cf513724fc0.png) + +¶ÔÓÚLoadBalancerClient½Ó¿ÚSpring Cloud LoadBalancerµÄÌṩĬÈÏʵÏÖΪBlockingLoadBalancerClient + +![image-20220508144750601](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/256e4815a982966d7d6bfb82e0e97f67.png) + +``` +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class BlockingLoadBalancerClient implements LoadBalancerClient { + + private final ReactiveLoadBalancer.Factory loadBalancerClientFactory; + + /** + * @deprecated in favour of + * {@link BlockingLoadBalancerClient#BlockingLoadBalancerClient(ReactiveLoadBalancer.Factory)} + */ + @Deprecated + public BlockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory, + LoadBalancerProperties properties) { + this.loadBalancerClientFactory = loadBalancerClientFactory; + } + + public BlockingLoadBalancerClient(ReactiveLoadBalancer.Factory loadBalancerClientFactory) { + this.loadBalancerClientFactory = loadBalancerClientFactory; + } + + @Override + public T execute(String serviceId, LoadBalancerRequest request) throws IOException { + String hint = getHint(serviceId); + LoadBalancerRequestAdapter lbRequest = new LoadBalancerRequestAdapter<>(request, + new DefaultRequestContext(request, hint)); + Set supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest)); + ServiceInstance serviceInstance = choose(serviceId, lbRequest); + // Ñ¡Ôñ·þÎñ + if (serviceInstance == null) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse()))); + throw new IllegalStateException("No instances available for " + serviceId); + } + return execute(serviceId, serviceInstance, lbRequest); + } + + @Override + public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) + throws IOException { + DefaultResponse defaultResponse = new DefaultResponse(serviceInstance); + Set supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); + Request lbRequest = request instanceof Request ? (Request) request : new DefaultRequest<>(); + supportedLifecycleProcessors + .forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance))); + try { + T response = request.apply(serviceInstance); + Object clientResponse = getClientResponse(response); + supportedLifecycleProcessors + .forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS, + lbRequest, defaultResponse, clientResponse))); + return response; + } + catch (IOException iOException) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.FAILED, iOException, lbRequest, defaultResponse))); + throw iOException; + } + catch (Exception exception) { + supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbRequest, defaultResponse))); + ReflectionUtils.rethrowRuntimeException(exception); + } + return null; + } + + private Object getClientResponse(T response) { + ClientHttpResponse clientHttpResponse = null; + if (response instanceof ClientHttpResponse) { + clientHttpResponse = (ClientHttpResponse) response; + } + if (clientHttpResponse != null) { + try { + return new ResponseData(clientHttpResponse, null); + } + catch (IOException ignored) { + } + } + return response; + } + + private Set getSupportedLifecycleProcessors(String serviceId) { + return LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors( + loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class), + DefaultRequestContext.class, Object.class, ServiceInstance.class); + } + + @Override + public URI reconstructURI(ServiceInstance serviceInstance, URI original) { + return LoadBalancerUriTools.reconstructURI(serviceInstance, original); + } + + @Override + public ServiceInstance choose(String serviceId) { + return choose(serviceId, REQUEST); + } + + // ͨ¹ý²»Í¬µÄ¸ºÔؾùºâ¿Í»§¶ËʵÏÖÑ¡Ôñ²»Í¬µÄ·þÎñ + @Override + public ServiceInstance choose(String serviceId, Request request) { + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance(serviceId); + if (loadBalancer == null) { + return null; + } + Response loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block(); + if (loadBalancerResponse == null) { + return null; + } + return loadBalancerResponse.getServer(); + } + + private String getHint(String serviceId) { + LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId); + String defaultHint = properties.getHint().getOrDefault("default", "default"); + String hintPropertyValue = properties.getHint().get(serviceId); + return hintPropertyValue != null ? hintPropertyValue : defaultHint; + } + +} + +``` + +### **LoadBalancerClientFactory** + +BlockingLoadBalancerClientÖгÖÓÐLoadBalancerClientFactoryͨ¹ýµ÷ÓÃÆägetInstance·½·¨»ñÈ¡¾ßÌåµÄ¸ºÔؾùºâ¿Í»§¶Ë¡£Í¨¹ý¹¤³§ÀàLoadBalancerClientFactory»ñÈ¡¾ßÌåµÄ¸ºÔؾùºâÆ÷ʵÀý£¬ºóÃæµÄloadBalancer.choose(request)µ÷ÓÃÆä½Ó¿Úchoose()·½·¨ÊµÏÖ¸ù¾Ý¸ºÔؾùºâË㷨ѡÔñÏÂÒ»¸ö·þÎñÆ÷Íê³É¸ºÔؾùºâ£¬¶øReactiveLoadBalancer getInstance(String serviceId) ÓÐĬÈÏʵÏÖLoadBalancerClientFactory +![image-20220508190132565](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/4014445b0c2ea7189a232b5d257fb938.png) + +LoadBalancerClientFactory¿Í»§¶ËʵÏÖÁ˲»Í¬µÄ¸ºÔؾùºâËã·¨£¬±ÈÈçÂÖѯ¡¢Ëæ»úµÈ¡£LoadBalancerClientFactory¼Ì³Ð×ÔNamedContextFactory£¬NamedContextFactory¼Ì³ÐApplicationContextAware£¬ÊµÏÖSpring ApplicationContextÈÝÆ÷²Ù×÷¡£ + +![image-20220508190412076](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/18f62338fdb636327fcdb2d08d33661b.png) + +### ReactiveLoadBalancer + +ReactiveLoadBalancer¸ºÔؾùºâÆ÷ʵÏÖ·þÎñÑ¡Ôñ£¬Spring Cloud BalancerÖÐʵÏÖÁËÂÖѯRoundRobinLoadBalancer¡¢Ëæ»úRandomLoadBalancer¡¢NacosLoadBalancerËã·¨¡£ + +![image-20220508235128931](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f19b9fd0d246dbae37b05e67b1a79805.png) + +### LoadBalancerClientConfiguration + +Èç¹ûûÓÐÏÔʽָ¶¨¸ºÔؾùºâËã·¨£¬Ä¬ÈÏȱʡֵΪRoundRobinLoadBalancer + +``` + @Bean + @ConditionalOnMissingBean + public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RoundRobinLoadBalancer( + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); + } + +``` + +![image-20220508235645313](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c8ae46d4cedb33f40939edb4f6fde542.png) + +### **LoadBalancerRequestFactory** + +LoadBalancerRequest¹¤³§Ààµ÷ÓÃcreateRequest·½·¨ÓÃÓÚ´´½¨LoadBalancerRequest¡£ÆäÄÚ²¿³ÖÓÐLoadBalancerClient¶ÔÏóÒ²¼´³ÖÓÐBlockingLoadBalancerClient¡£ + +![image-20220509000049541](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c928dcb312df87a44c33ee77a296ee84.png) + +ÔÚÈÕ³£ÏîÄ¿ÖУ¬Ò»°ã¸ºÔؾùºâ¶¼ÊǽáºÏFeignʹÓ㬺óÐøÎÒÃÇÓÐʱ¼äÔÙÀ´·ÖÎöFeignÕûºÏLoadBalancerµÄ×Ô¶¯ÅäÖÃÀàFeignLoadBalancerAutoConfigurationµÄʵÏÖ + +### ReactorLoadBalancerClientAutoConfiguration + +ÎÒÃÇÒ²Å×һϻùÓÚWebClientµÄ@LoadbalancedµÄÁ÷³ÌµÄÒýÈ룬Ê×ÏÈÉùÃ÷¸ºÔؾùºâ¹ýÂËÆ÷ReactorLoadBalancerClientAutoConfigurationÊÇÒ»¸ö×Ô¶¯×°ÅäÆ÷À࣬ÔÚÏîÄ¿ÖÐÒýÈëÁË WebClient ºÍ ReactiveLoadBalancer ÀàÖ®ºó£¬×Ô¶¯×°ÅäÁ÷³Ì¾Í¿ªÊ¼ÔËÐУ¬Ëü»á³õʼ»¯Ò»¸öʵÏÖÁË ExchangeFilterFunction µÄʵÀý£¬ÔÚºóÃæ¸ÃʵÀý½«×÷Ϊ¹ýÂËÆ÷±»×¢Èëµ½WebClient¡£ºóÐøÁ÷³ÌÓÐÐËȤÔÙ×ÔÐÐÑо¿ + +![image-20220509001650781](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/869a4ea9a6e1409b84c927db08cebea1.png) + +## ×Ô¶¨Òå¸ºÔØ¾ùºâÆ÷ + +´ÓÉÏÃæ¿ÉÒÔÖªµÀLoadBalancerClientFactoryÊÇ´´½¨¿Í»§»ú¡¢¸ºÔؾùºâÆ÷ºÍ¿Í»§»úÅäÖÃʵÀýµÄ¹¤³§¡£Ëü¸ù¾Ý¿Í»§¶ËÃû³Æ´´½¨Ò»¸öSpring ApplicationContext£¬²¢´ÓÖÐÌáÈ¡ËùÐèµÄbean¡£Òò´Ë½øÈëµ½LoadBalancerClientFactoryÀàÖУ¬ÐèҪȥʵÏÖËüµÄ×Ó½Ó¿ÚReactorServiceInstanceLoadBalancer£¬ÒòΪȥ»ñÈ¡¸ºÔؾùºâÆ÷ʵÀýµÄʱºò£¬ÊÇͨ¹ýÈ¥ÈÝÆ÷ÖвéÕÒReactorServiceInstanceLoadBalancerÀàÐ͵ÄbeanÀ´ÊµÏֵģ¬¿ÉÒÔ²ÎÕÕRandomLoadBalancerʵÏÖ´úÂë + +``` +package org.springframework.cloud.loadbalancer.core; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; + +public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private static final Log log = LogFactory.getLog(RandomLoadBalancer.class); + + private final String serviceId; + + private ObjectProvider serviceInstanceListSupplierProvider; + + /** + * @param serviceInstanceListSupplierProvider a provider of + * {@link ServiceInstanceListSupplier} that will be used to get available instances + * @param serviceId id of the service for which to choose an instance + */ + public RandomLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider, + String serviceId) { + this.serviceId = serviceId; + this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; + } + + @SuppressWarnings("rawtypes") + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider + .getIfAvailable(NoopServiceInstanceListSupplier::new); + return supplier.get(request).next() + .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); + } + + private Response processInstanceResponse(ServiceInstanceListSupplier supplier, + List serviceInstances) { + Response serviceInstanceResponse = getInstanceResponse(serviceInstances); + if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { + ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); + } + return serviceInstanceResponse; + } + + private Response getInstanceResponse(List instances) { + if (instances.isEmpty()) { + if (log.isWarnEnabled()) { + log.warn("No servers available for service: " + serviceId); + } + return new EmptyResponse(); + } + int index = ThreadLocalRandom.current().nextInt(instances.size()); + + ServiceInstance instance = instances.get(index); + + return new DefaultResponse(instance); + } + +} + +``` + +±£ÁôºËÐÄʵÏÖ½øÐмòµ¥·ÂдÈçÏ + +``` +package cn.itxs.ecom.order.config; + +import java.util.List; +import java.util.Random; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import reactor.core.publisher.Mono; + +public class ItxsRandomLoadBalancerClient implements ReactorServiceInstanceLoadBalancer { + // ·þÎñÁбí + private ObjectProvider serviceInstanceListSupplierProvider; + + public ItxsRandomLoadBalancerClient(ObjectProvider serviceInstanceListSupplierProvider) { + this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; + } + + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(); + return supplier.get().next().map(this::getInstanceResponse); + } + + /** + * ʹÓÃËæ»úÊý»ñÈ¡·þÎñ + * @param instances + * @return + */ + private Response getInstanceResponse( + List instances) { + System.out.println("ItxsRandomLoadBalancerClient start"); + if (instances.isEmpty()) { + return new EmptyResponse(); + } + + System.out.println("ItxsRandomLoadBalancerClient random"); + // Ëæ»úËã·¨ + int size = instances.size(); + Random random = new Random(); + ServiceInstance instance = instances.get(random.nextInt(size)); + + return new DefaultResponse(instance); + } +} + +``` + +½«ÉÏÃæCustomLoadBalancerConfigurationÌæ»»ÎªÈçÏÂÄÚÈÝ + +``` +package cn.itxs.ecom.order.config; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.context.annotation.Bean; + +public class CustomLoadBalancerConfiguration { + + @Bean + public ReactorServiceInstanceLoadBalancer customLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider) { + return new ItxsRandomLoadBalancerClient(serviceInstanceListSupplierProvider); + } +} + +``` + +Æô¶¯¿â´æÎ¢·þÎñºÍ¶©µ¥Î¢·þÎñ£¬·ÃÎÊhttp://localhost:4070/deductRest/1001/1 £¬¿ØÖÆÌ¨ÒÑ´òÓ¡×Ô¶¨ÒåItxsRandomLoadBalancerClientÖеÄÈÕÖ¾ºÍ³É¹¦·ÃÎʽá¹û + +![image-20220509003807968](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/c72e3f02f7e0d5d3343f8ae9c464b69c.png) + +![image-20220509003927550](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/59796bbbd6b3524e32759d42b622f1bc.png) + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..17db744 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudOpenFeign\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,902 @@ +×÷Õß | ÓîľľÙâ +À´Ô´ |½ñÈÕÍ·Ìõ + +**ѧϰĿ±ê** + +1. Ϊʲô¼ÓÒ»¸ö×¢½â¾ÍÄÜʵÏÖÔ¶³Ì¹ý³Ìµ÷ÓÃÄØ£¿ÍƵ¼Ëüµ×²ãµÄʵÏÖÖ÷Á÷³Ì£¿ +2. OpenFeignÔõôʵÏÖRPCµÄ»ù±¾¹¦ÄÜµÄ +3. ͨ¹ýÔ´ÂëÑéÖ¤ + **µÚ1Õ OpenFeignÖ÷Á÷³ÌÍÆµ¼** + ÒªÃ÷È·OpenFeignµÄÖ÷Á÷³ÌÊ×ÏÈÎÒÃÇ»¹ÊÇÒªÃ÷È·ËüµÄºËÐÄÄ¿±êÊÇʲô£¿ + +˵°×ÁË£¬OpenFeign×îºËÐĵÄÄ¿±ê¾ÍÊÇÈÿͻ§¶ËÔÚÔ¶³Ìµ÷Óùý³ÌÖв»ÐèÒª×öʲô¶àÓàµÄ²Ù×÷£¬Ö»ÒªÄõ½Ò»¸ö¶ÔÏó£¬È»ºóµ÷ÓøöÔÏóµÄ·½·¨¾ÍºÃÁË£¬Ê£ÏµIJÙ×÷¶¼½»¸øOpenFeignÈ¥°ïÄãÍê³É£¬ÄÇÊ£ÏÂһЩʲô²Ù×÷ÄØ£¿ + +1. Ê×Ïȿ϶¨ÊDZ£Ö¤ÍøÂçͨÐÅ£¬ÄÇÎÒÃǴ󵨵ز²âһϣ¬OpenFeignÆäʵµ×²ã°ïÎÒÃÇ·â×°ÁËÇëÇóµÄµØÖ·¡¢¶Ë¿Ú¡¢ÇëÇó²ÎÊýÒÔ¼°ÏìÓ¦µÄ²ÎÊý¡£ +2. Æä´Î£¬µ±ÎÒÃÇÒªÓöÔÏóÈ¥ÇëÇó·½·¨£¬ÄÇÕâ¸ö¶ÔÏóÊÇÔ¶³ÌµÄ·þÎñ£¬Õâ¸ö¶ÔÏó¿Ï¶¨²»¼òµ¥£¬ÕâÀïÒ²´óµ¨²Â²âһϣ¬¸Ã¶ÔÏóÒ²ÊÇOpenFeign¸øÎÒÃÇ´´½¨µÄ¡£ +3. È»ºóÔÚµ÷Óùý³ÌÖУ¬Èç¹û·þÎñÊÇÓɶą̀·þÎñÆ÷ÌṩµÄ£¬ÄÇÓÖÉæ¼°µ½¸ºÔؾùºâÁË£¬Õâ¿Ï¶¨Ò²ÊÇOpenFeign°ïÎÒÃÇÍê³ÉÁË¡£ +4. ³ýÁËÉÏÃæµÄÎÊÌ⣬ÔÙÁªÏëһϣ¬¼ÈÈ»´æÔÚ·þÎñ¶ËÊǼ¯ÈºµÄÇé¿ö£¬ÄÇ·þÎñ¶ËµÄµØÖ·ºÍ¶Ë¿Ú»¹ÐèÒªÒ»¸ö×¢²áÖÐÐÄÀ´×¢²á£¬Õâ¿Ï¶¨Ò²²»ÄÜÓɿͻ§¶ËÀ´Íê³É£¬ÒòΪ¿Í»§¶ËÖ»¹Ø×¢ÒµÎñ´úÂë¡£ÄÇÏë¶¼²»ÓÃÏ룬ҲÊÇOpenFeignÀ´Íê³ÉÁË¡£ + +OK£¬ÉÏÃæÍÆµ¼ÁËOpenFeignÓ¦¸ÃÍê³ÉµÄÖ÷ҪĿ±ê£¬½ÓÏÂÀ´ÎÒÃÇÔÙÀ´·ÖÎö·ÖÎöËüÊÇÔõô×öµÄ¡£ +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b45564c706ab4113cc23492cc0796907b851f8.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø")֮ǰµÄÎÄÕÂÓн²¹ýÒ»¸ö¸ÅÄ²»¹ÜÊÇʲô×é¼þ£¬Ö»ÒªÊǼ¯³Éspring»òÕßspringbootµÄ»°£¬ÄÇÒ»¶¨ÊÇÏëͨ¹ýspring»òÕßspringbootÈ¥¹ÜÀíbean¶ÔÏóµÄ´´½¨µÄ£¬µ±Í¨¹ýÈÝÆ÷Äõ½¶ÔÏóÖ®ºóÔÙÈ¥µ÷ÓöÔÏóµÄºËÐÄ·½·¨£¬ÄÇOpenFeignÔÚ¼¯³ÉspringbootµÄʱºòÀíÄîÒ²Ó¦¸ÃÊÇÕâÑù¡£ + +1. ËùÒÔµÚÒ»²½£¬OpenFeign¼¯³Éspringboot£¬Í¨¹ýspringbootÄõ½ºËÐÄbean¶ÔÏó£¬ÀýÈçÉÏͼÖеÄuserService¶ÔÏó¡£ +2. Õâ¸ö¶ÔÏó¿Ï¶¨²»¼òµ¥£¬²»¿ÉÄÜÖ»ÓÐgetUserµÄ¹¦ÄÜ¡£ÄÇÏëÒ»Ï룬springÖÐ×ö¹¦ÄÜÔöÇ¿¿ÉÒÔÓÃʲôÀ´×öÄØ£¿¡ª¡ª´úÀíÂÄÇÕâ¸ö¶ÔÏóµÄÀàÐ;ͺôÖ®Óû³öÁË£¬´úÀí¶ÔÏó¡£ +3. µ÷ÓôúÀí¶ÔÏóµÄ·½·¨Ê±£¬Á÷³ÌÏȽøÈëµ½invokeÖУ¬ÔÚÕâ¸öinvokeÖУ¬×öµÄÔöÇ¿°üÀ¨Á˸ºÔؾùºâLoadBalance£¬ÒòΪÎÒÔÚÕæÕýµ÷ÓÃgetUserµÄʱºòÒªÖªµÀ¾ßÌåÊǵ÷ÓÃÄĄ̈·þÎñÆ÷µÄ·þÎñ¡£ +4. ¸ºÔؾùºâ×öÍê¾ÍµÃÆ´½Ó¾ßÌåµÄhttpÇëÇó²ÎÊý£¬ÇëÇóÍ·£¬ÇëÇóµØÖ·£¬ÇëÇó¶Ë¿ÚÁË¡£ + OK£¬ÒÔÉÏ·ÖÎöÁËOpenFeignµ×²ãҪʵÏֵľßÌ幦ÄÜ£¬Ò²·ÖÎöÁËËüµÄ´¦ÀíÁ÷³Ì£¬ÄÇô½ÓÏÂÀ´ÎÒÃÇͨ¹ýÔ´ÂëÀ´Ñé֤һϣ¬ËüÊDz»ÊÇÕâÃ´ÍæµÄ¡£ + +**µÚ2Õ ԴÂëÑéÖ¤** + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/93070e354fbacedc2b85274846d2dde7dd170a.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø")**2.1 EnableFeignClients** +ÎÒÃÇ´ÓÏÂÃæÕâ¸ö×¢½â½øÐÐÇÐÈ룬Õâ¸ö×¢½â¿ªÆôÁËFeignClientµÄ½âÎö¹ý³Ì¡£ + + + +``` +@EnableFeignClients(basePackages = "com.example.client") +``` + + + + + + + + + +Õâ¸ö×¢½âµÄÉùÃ÷ÈçÏ£¬ËüÓõ½ÁËÒ»¸ö@Import×¢½â£¬ÎÒÃÇÖªµÀImportÊÇÓÃÀ´µ¼ÈëÒ»¸öÅäÖÃÀàµÄ£¬½ÓÏÂÀ´È¥¿´Ò»ÏÂFeignClientsRegistrarµÄ¶¨Òå¡£ + + + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(FeignClientsRegistrar.class) +public @interface EnableFeignClients { +} +``` + + + + + + + + + +FeignClientsRegistrarʵÏÖÁË +ImportBeanDefinitionRegistrar£¬ËüÊÇÒ»¸ö¶¯Ì¬×¢ÈëbeanµÄ½Ó¿Ú£¬Spring BootÆô¶¯µÄʱºò£¬»áÈ¥µ÷ÓÃÕâ¸öÀàÖеÄregisterBeanDefinitionsÀ´ÊµÏÖ¶¯Ì¬BeanµÄ×°ÔØ¡£registerBeanDefinitionsÊÇÔÚspringÈÝÆ÷Æô¶¯Ê±Ö´ÐÐinvokeBeanFactoryPostProcessors·½·¨£¬È»ºó¶ÔÏàÓ¦µÄÀà½øÐнâÎö×¢²á£¬ËüµÄ×÷ÓÃÀàËÆÓÚImportSelector¡£ + + + +``` +class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware,EnvironmentAware { + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + registerDefaultConfiguration(metadata, registry); + registerFeignClients(metadata, registry); + } +} +``` + + + + + + + + + +**2.1.1 ImportBeanDefinitionRegistrar** +¼òµ¥¸ø´ó¼ÒÑÝʾһÏ +ImportBeanDefinitionRegistrarµÄ×÷Óᣠ+ +* ¶¨ÒåÒ»¸öÐèÒª±»×°Ôص½IOCÈÝÆ÷ÖеÄÀàHelloService + + + +``` +public class HelloService { +} +``` + + + + + + + + + +* ¶¨ÒåÒ»¸öRegistrarµÄʵÏÖ£¬¶¨ÒåÒ»¸öbean£¬×°Ôص½IOCÈÝÆ÷ + + + +``` +public class FeignImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + BeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClassName(HelloService.class.getName()); + registry.registerBeanDefinition("helloService",beanDefinition); + } +} +``` + + + + + + + + + +* ¶¨ÒåÒ»¸ö×¢½âÀà + + + + ``` + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + @Documented + @Import({FeignImportBeanDefinitionRegistrar.class}) + public @interface EnableFeignTest { + } + ``` + + + + + + + + + +* Æô¶¯Àà + + + +``` +@EnableFeignClients(basePackages = "com.example.clients") +@EnableFeignTest +@SpringBootApplication +public class OpenfeignUserServiceApplication { + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(OpenfeignUserServiceApplication.class, args); + System.out.println(context.getBean(HelloService.class)); + } + +} +``` + + + + + + + + + +* ͨ¹ý½á¹ûÑÝʾ¿ÉÒÔ·¢ÏÖ£¬HelloServiceÕâ¸öbean ÒѾ­×°Ôص½ÁËIOCÈÝÆ÷¡£ + Õâ¾ÍÊǶ¯Ì¬×°ÔصŦÄÜʵÏÖ£¬ËüÏà±ÈÓÚ@ConfigurationÅäÖÃ×¢È룬»á¶àÁ˺ܶàµÄÁé»îÐÔ¡£ ok£¬Ôٻص½FeignClientµÄ½âÎöÖÐÀ´¡£ + +**2.1.2 FeignClientsRegistrar** + +* registerDefaultConfiguration ·½·¨ÄÚ²¿´Ó SpringBoot Æô¶¯ÀàÉϼì²éÊÇ·ñÓÐ@EnableFeignClients, ÓиÃ×¢½âµÄ»°£¬ ÔòÍê³É Feign ¿ò¼ÜÏà¹ØµÄһЩÅäÖÃÄÚÈÝ×¢²á + +* registerFeignClients ·½·¨ÄÚ²¿´Ó classpath ÖУ¬ ɨÃè»ñµÃ @FeignClient ÐÞÊεÄÀ࣬ ½«ÀàµÄÄÚÈݽâÎöΪ BeanDefinition , ×îÖÕͨ¹ýµ÷Óà Spring ¿ò¼ÜÖеÄBeanDefinitionReaderUtils.resgisterBeanDefinition ½«½âÎö´¦Àí¹ýµÄ FeignClientBeanDeifinition Ìí¼Óµ½ spring ÈÝÆ÷ÖÐ. + + + + ``` + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + //×¢²á@EnableFeignClientsÖж¨ÒådefaultConfigurationÊôÐÔϵÄÀ࣬°ü×°³ÉFeignClientSpecification£¬×¢²áµ½SpringÈÝÆ÷¡£ + //ÔÚ@FeignClientÖÐÓÐÒ»¸öÊôÐÔ£ºconfiguration£¬Õâ¸öÊôÐÔÊDZíʾ¸÷¸öFeignClient×Ô¶¨ÒåµÄÅäÖÃÀ࣬ºóÃæÒ²»áͨ¹ýµ÷ÓÃregisterClientConfiguration·½·¨À´×¢²á³ÉFeignClientSpecificationµ½ÈÝÆ÷¡£ + //ËùÒÔ£¬ÕâÀï¿ÉÒÔÍêÈ«Àí½âÔÚ@EnableFeignClientsÖÐÅäÖõÄÊÇ×öΪ¶µµ×µÄÅäÖã¬ÔÚ¸÷¸ö@FeignClientÅäÖõľÍÊÇ×Ô¶¨ÒåµÄÇé¿ö¡£ + registerDefaultConfiguration(metadata, registry); + registerFeignClients(metadata, registry); + } + ``` + + + + + + + + + +**2.2 registerDefaultConfiguration** + + + + ``` + private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + // »ñÈ¡µ½metadataÖйØÓÚEnableFeignClientsµÄÊôÐÔÖµ¼üÖµ¶Ô¡£ + Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true); + //Èç¹ûÅäÖÃÁËdefaultConfiguration ½øÐÐÅäÖÃ,Èç¹ûûÓÐʹÓÃĬÈϵÄconfiguration + if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { + String name; + if (metadata.hasEnclosingClass()) { + name = "default." + metadata.getEnclosingClassName(); + } else { + name = "default." + metadata.getClassName(); + } + //½øÐÐ×¢²á + this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); + } + + } + ``` + + + + + + + + + + + +``` +private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { + //ʹÓÃBeanDefinitionBuilderÀ´Éú³ÉBeanDefinition,²¢°ÑËü½øÐÐ×¢²á + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); + builder.addConstructorArgValue(name); + builder.addConstructorArgValue(configuration); + registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); +} +``` + + + + + + + + + +·½·¨µÄÈë²ÎBeanDefinitionRegistryÊÇspring¿ò¼ÜÓÃÓÚ¶¯Ì¬×¢²áBeanDefinitionÐÅÏ¢µÄ½Ó¿Ú£¬µ÷ÓÃregisterBeanDefinition·½·¨¿ÉÒÔ½«BeanDefinition×¢²áµ½SpringÈÝÆ÷ÖУ¬ÆäÖÐnameÊôÐÔ¾ÍÊÇ×¢²áµÄBeanDefinitionµÄÃû³Æ£¬ÔÚÕâÀïËü×¢²áÁËÒ»¸öFeignClientSpecificationµÄ¶ÔÏó¡£ + +FeignClientSpecificationʵÏÖÁË +NamedContextFactory.Specification½Ó¿Ú£¬ËüÊÇFeignʵÀý»¯µÄÖØÒªÒ»»·£¬ÔÚÉÏÃæµÄ·½·¨ÖУ¬Ëü³ÖÓÐ×Ô¶¨ÒåÅäÖõÄ×é¼þʵÀý£¬SpringCloudʹÓÃNamedContextFactory´´½¨Ò»Ð©ÁеÄÔËÐÐÉÏÏÂÎÄApplicationContextÀ´ÈöÔÓ¦µÄSpecificationÔÚÕâЩÉÏÏÂÎÄÖд´½¨ÊµÀý¶ÔÏó¡£ + +NamedContextFactoryÓÐ3¸ö¹¦ÄÜ£º + +* ´´½¨AnnotationConfigApplicationContextÉÏÏÂÎÄ¡£ +* ÔÚÉÏÏÂÎÄÖд´½¨²¢»ñÈ¡beanʵÀý¡£ +* µ±ÉÏÏÂÎÄÏú»ÙʱÇå³ýÆäÖеÄfeignʵÀý¡£ + NamedContextFactoryÓиö·Ç³£ÖØÒªµÄ×ÓÀàFeignContext£¬ÓÃÓÚ´æ´¢¸÷ÖÖOpenFeignµÄ×é¼þʵÀý¡£ + + + +``` +public class FeignContext extends NamedContextFactory { + public FeignContext() { + super(FeignClientsConfiguration.class, "feign", "feign.client.name"); + } + } +``` + + + + + + + + + +FeignContextÊÇÄÄÀï¹¹½¨µÄÄØ£¿ + +ÅäÖüû£º +pring-cloud-openfeign-core-2.2.3.RELEASE.jar!\META-INF\spring.factories![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d830f20713aca01438472786f5f84c7058232b.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø")**2.2.1 FeignAutoConfiguration** + +![SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/894c73791fbf7215782217d7132bc57f004085.jpg "SpringCloudϵÁСªSpring Cloud Ô´Âë·ÖÎöÖ®OpenFeign-¿ªÔ´»ù´¡Èí¼þÉçÇø") + +½«Ä¬ÈϵÄFeignClientsConfiguration×÷Ϊ²ÎÊý´«µÝ¸ø¹¹Ô캯Êý + +FeignContext´´½¨µÄʱºò»á½«Ö®Ç°FeignClientSpecificationͨ¹ýsetConfigurationsÉèÖøøcontextÉÏÏÂÎÄ¡£ + +**2.2.2 createContext** +´úÂëÏê¼û£º +org.springframework.cloud.context.named.NamedContextFactory#createContext·½·¨¡£ + +FeignContextµÄ¸¸ÀàµÄcreateContext·½·¨»á½«´´½¨ +AnnotationConfigApplicationContextʵÀý£¬ÕâʵÀý½«×÷Ϊµ±Ç°ÉÏÏÂÎĵÄ×ÓÉÏÏÂÎÄ£¬ÓÃÓÚ¹ØÁªfeign×é¼þµÄ²»Í¬ÊµÀý¡£ÔÚµ÷ÓÃFeignClientFactoryBeanµÄgetObject·½·¨Ê±µ÷Óᣣ¨createContextµ÷ÓÃÔÚÏÂÎĻὲ½â£© + + + +``` +protected AnnotationConfigApplicationContext createContext(String name) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + //»ñÈ¡nameËù¶ÔÓ¦µÄconfiguration,Èç¹ûÓоÍ×¢²áµ½×ÓcontextÖÐ + if (this.configurations.containsKey(name)) { + for (Class configuration : this.configurations.get(name) + .getConfiguration()) { + context.register(configuration); + } + } + //×¢²ádefaultµÄConfiguration,Ò²¾ÍÊÇ FeignClientsRegistrarÀàÖÐregisterDefaultConfiguration·½·¨ÖÐ×¢²áµÄConfiguration + for (Map.Entry entry : this.configurations.entrySet()) { + if (entry.getKey().startsWith("default.")) { + for (Class configuration : entry.getValue().getConfiguration()) { + context.register(configuration); + } + } + } + //×¢²áPropertyPlaceholderAutoConfiguration + context.register(PropertyPlaceholderAutoConfiguration.class, + this.defaultConfigType); + //ÖÃEnvironmentµÄpropertySourcesÊôÐÔÔ´ + context.getEnvironment().getPropertySources().addFirst(new MapPropertySource( + this.propertySourceName, + Collections.singletonMap(this.propertyName, name))); + if (this.parent != null) { + // Uses Environment from parent as well as beans + context.setParent(this.parent); + // jdk11 issue + // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101 + context.setClassLoader(this.parent.getClassLoader()); + } + context.setDisplayName(generateDisplayName(name)); + context.refresh(); + return context; +} +``` + + + + + + + + + +ÓÉÓÚNamedContextFactoryʵÏÖÁËDisposableBean£¬ËùÒÔµ±ÊµÀýÏûÍöµÄʱºò»áµ÷Óà + + + +``` +@Override +public void destroy() { + Collection values = this.contexts.values(); + for (AnnotationConfigApplicationContext context : values) { + // This can fail, but it never throws an exception (you see stack traces + // logged as WARN). + context.close(); + } + this.contexts.clear(); +} +``` + + + + + + + + + +×ܽ᣺NamedContextFactory»á´´½¨³ö +AnnotationConfigApplicationContextʵÀý£¬²¢ÒÔname×÷ΪΨһ±êʶ£¬È»ºóÿ¸öAnnotationConfigApplicationContextʵÀý¶¼»á×¢²á²¿·ÖÅäÖÃÀ࣬´Ó¶ø¿ÉÒÔ¸ø³öһϵÁеĻùÓÚÅäÖÃÀàÉú³ÉµÄ×é¼þʵÀý£¬ÕâÑù¾Í¿ÉÒÔ»ùÓÚnameÀ´¹ÜÀíһϵÁеÄ×é¼þʵÀý£¬Îª²»Í¬µÄFeignClient×¼±¸²»Í¬ÅäÖÃ×é¼þʵÀý¡£ + +**2.3 registerFeignClients** +Õâ¸ö·½·¨Ö÷ÒªÊÇɨÃèÀà·¾¶ÏÂËùÓеÄ@FeignClient×¢½â£¬È»ºó½øÐж¯Ì¬BeanµÄ×¢Èë¡£Ëü×îÖÕ»áµ÷Óà registerFeignClient ·½·¨¡£ + + + +``` +public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + //Ê¡ÂÔ´úÂë... + registerFeignClient(registry, annotationMetadata, attributes); +} +``` + + + + + + + + + +ÔÚÕâ¸ö·½·¨ÖУ¬¾ÍÊÇÈ¥×é×°BeanDefinition£¬Ò²¾ÍÊÇBeanµÄ¶¨Ò壬Ȼºó×¢²áµ½Spring IOCÈÝÆ÷¡£ + + + +``` +private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map attributes) { + String className = annotationMetadata.getClassName(); + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); + //Ê¡ÂÔ´úÂë... + BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition,className,new String[] { alias }); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); +} +``` + + + + + + + + + +ÎÒÃǹØ×¢Ò»Ï£¬BeanDefinitionBuilderÊÇÓÃÀ´¹¹½¨Ò»¸öBeanDefinitionµÄ£¬ËüÊÇͨ¹ýgenericBeanDefinition À´¹¹½¨µÄ£¬²¢ÇÒ´«ÈëÁËÒ»¸öFeignClientFactoryBeanµÄÀà¡£ + +ÎÒÃÇ¿ÉÒÔ·¢ÏÖ£¬FeignClient±»¶¯Ì¬×¢²á³ÉÁËÒ»¸öFactoryBean + +> Spring Cloud FengnClientʵ¼ÊÉÏÊÇÀûÓÃSpringµÄ´úÀí¹¤³§À´Éú³É´úÀíÀ࣬ËùÒÔÔÚÕâÀï²Å»á°ÑËùÓеÄFeignClientµÄBeanDefinitionÉèÖÃΪFeignClientFactoryBeanÀàÐÍ£¬¶øFeignClientFactoryBean¼Ì³Ð×ÔFactoryBean£¬ËüÊÇÒ»¸ö¹¤³§Bean¡£ +> +> ÔÚSpringÖУ¬FactoryBeanÊÇÒ»¸ö¹¤³§Bean£¬ÓÃÀ´´´½¨´úÀíBean¡£ +> +> ¹¤³§ Bean ÊÇÒ»ÖÖÌØÊâµÄ Bean, ¶ÔÓÚ Bean µÄÏû·ÑÕßÀ´Ëµ£¬ ËûÂß¼­ÉÏÊǸÐÖª²»µ½Õâ¸ö Bean ÊÇÆÕͨµÄ Bean »¹Êǹ¤³§ Bean, Ö»Êǰ´ÕÕÕý³£µÄ»ñÈ¡ Bean ·½Ê½È¥µ÷Ó㬠µ«¹¤³§bean ×îºó·µ»ØµÄʵÀý²»Êǹ¤³§Bean ±¾Éí£¬ ¶øÊÇÖ´Ðй¤³§ Bean µÄ getObject Âß¼­·µ»ØµÄʾÀý¡££¨Ò²¾ÍÊÇÔÚʵÀý»¯¹¤³§BeanµÄʱºò»áÈ¥µ÷ÓÃËüµÄgetObject·½·¨£© + + + +``` +public static BeanDefinitionBuilder genericBeanDefinition(Class beanClass) { + BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition()); + builder.beanDefinition.setBeanClass(beanClass); + return builder; +} +``` + + + + + + + + + +¼òµ¥À´Ëµ£¬FeignClient±ê×¢µÄÕâ¸ö½Ó¿Ú£¬»áͨ¹ý +FeignClientFactoryBean.getObject()Õâ¸ö·½·¨»ñµÃÒ»¸ö´úÀí¶ÔÏó¡£ + +**2.3.1 FeignClientFactoryBean.getObject** +getObjectµ÷ÓõÄÊÇgetTarget·½·¨£¬Ëü´ÓapplicationContextÈ¡³öFeignContext£¬FeignContext¼Ì³ÐÁËNamedContextFactory£¬ËüÊÇÓÃÀ´À´Í³Ò»Î¬»¤feignÖи÷¸öfeign¿Í»§¶ËÏ໥¸ôÀëµÄÉÏÏÂÎÄ¡£ + +½Ó×Å£¬¹¹½¨feign.builder£¬ÔÚ¹¹½¨Ê±»áÏòFeignContext»ñÈ¡ÅäÖõÄEncoder£¬DecoderµÈ¸÷ÖÖÐÅÏ¢¡£FeignContextÔÚÉÏÆªÖÐÒѾ­Ìáµ½»áΪÿ¸öFeign¿Í»§¶Ë·ÖÅäÁËÒ»¸öÈÝÆ÷£¬ËüÃǵĸ¸ÈÝÆ÷¾ÍÊÇspringÈÝÆ÷ + +ÅäÖÃÍêFeign.BuilderÖ®ºó£¬ÔÙÅжÏÊÇ·ñÐèÒªLoadBalance£¬Èç¹ûÐèÒª£¬Ôòͨ¹ýLoadBalanceµÄ·½·¨À´ÉèÖá£Êµ¼ÊÉÏËûÃÇ×îÖÕµ÷ÓõÄÊÇTarget.target()·½·¨¡£ + + + +``` +@Override +public Object getObject() throws Exception { + return getTarget(); +} + T getTarget() { + //ʵÀý»¯FeignÉÏÏÂÎĶÔÏóFeignContext + FeignContext context = this.applicationContext.getBean(FeignContext.class); + Feign.Builder builder = feign(context);//¹¹½¨Builder¶ÔÏó + if (!StringUtils.hasText(this.url)) {//Èç¹ûurlΪ¿Õ£¬Ôò×߸ºÔؾùºâ£¬Éú³ÉÓиºÔؾùºâ¹¦ÄܵĴúÀíÀà + if (!this.name.startsWith("http")) { + this.url = "http://" + this.name; + } + else { + this.url = this.name; + } + this.url += cleanPath(); + return (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name,this.url)); + } + //Èç¹ûÖ¸¶¨ÁËurl£¬ÔòÉú³ÉĬÈϵĴúÀíÀà + if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { + this.url = "http://" + this.url; + } + String url = this.url + cleanPath(); + //µ÷ÓÃFeignContextµÄgetInstance·½·¨»ñÈ¡Client¶ÔÏó + Client client = getOptional(context, Client.class); + if (client != null) { + if (client instanceof LoadBalancerFeignClient) { + // not load balancing because we have a url, + // but ribbon is on the classpath, so unwrap + client = ((LoadBalancerFeignClient) client).getDelegate(); + } + if (client instanceof FeignBlockingLoadBalancerClient) { + // not load balancing because we have a url, + // but Spring Cloud LoadBalancer is on the classpath, so unwrap + client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); + } + builder.client(client); + }//Éú³ÉĬÈÏ´úÀíÀà + Targeter targeter = get(context, Targeter.class); + return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name,url)); +} +``` + + + + + + + + + +**2.3.2 loadBalance** +Éú³É¾ß±¸¸ºÔؾùºâÄÜÁ¦µÄfeign¿Í»§¶Ë£¬Îªfeign¿Í»§¶Ë¹¹½¨Æð°ó¶¨¸ºÔؾùºâ¿Í»§¶Ë. + +Client client = (Client)this.getOptional(context, Client.class); ´ÓÉÏÏÂÎÄÖлñȡһ¸öClient£¬Ä¬ÈÏÊÇLoadBalancerFeignClient¡£ + +ËüÊÇÔÚFeignRibbonClientAutoConfigurationÕâ¸ö×Ô¶¯×°ÅäÀàÖУ¬Í¨¹ýImportʵÏÖµÄ + + + +``` +@Import({ HttpClientFeignLoadBalancedConfiguration.class,OkHttpFeignLoadBalancedConfiguration.class,DefaultFeignLoadBalancedConfiguration.class }) +``` + + + + + + + + + + + +``` +protected T loadBalance(Builder builder, FeignContext context, + HardCodedTarget target) { + Client client = (Client)this.getOptional(context, Client.class); + if (client != null) { + builder.client(client); + Targeter targeter = (Targeter)this.get(context, Targeter.class); + return targeter.target(this, builder, context, target); + } else { + throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); + } +} +``` + + + + + + + + + +**2.3.3 DefaultTarget.target** + + + +``` +@Override +public T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget target) { + return feign.target(target); +} +``` + + + + + + + + + +**2.3.4 ReflectiveFeign.newInstance** +Õâ¸ö·½·¨ÊÇÓÃÀ´´´½¨Ò»¸ö¶¯Ì¬´úÀíµÄ·½·¨£¬ÔÚÉú³É¶¯Ì¬´úÀí֮ǰ£¬»á¸ù¾ÝContractЭÒ飨ЭÒé½âÎö¹æÔò£¬½âÎö½Ó¿ÚÀàµÄ×¢½âÐÅÏ¢£¬½âÎö³ÉÄÚ²¿µÄMethodHandlerµÄ´¦Àí·½Ê½¡£ + +´ÓʵÏֵĴúÂëÖпÉÒÔ¿´µ½ÊìϤµÄProxy.newProxyInstance·½·¨²úÉú´úÀíÀà¡£¶øÕâÀïÐèÒª¶Ôÿ¸ö¶¨ÒåµÄ½Ó¿Ú·½·¨½øÐÐÌØ¶¨µÄ´¦ÀíʵÏÖ£¬ËùÒÔÕâÀï»á³öÏÖÒ»¸öMethodHandlerµÄ¸ÅÄ¾ÍÊǶÔÓ¦·½·¨¼¶±ðµÄInvocationHandler¡£ + + + +``` +public T newInstance(Target target) { + //¸ù¾Ý½Ó¿ÚÀàºÍContractЭÒé½âÎö·½Ê½£¬½âÎö½Ó¿ÚÀàÉϵķ½·¨ºÍ×¢½â£¬×ª»»³ÉÄÚ²¿µÄMethodHandler´¦Àí·½Ê½ + Map nameToHandler = this.[targetToHandlersByName.apply(target)]; + Map methodToHandler = new LinkedHashMap(); + List defaultMethodHandlers = new LinkedList(); + Method[] var5 = target.type().getMethods(); + int var6 = var5.length; + for(int var7 = 0; var7 < var6; ++var7) { + Method method = var5[var7]; + if (method.getDeclaringClass() != Object.class) { + if (Util.isDefault(method)) { + DefaultMethodHandler handler = new DefaultMethodHandler(method); + defaultMethodHandlers.add(handler); + methodToHandler.put(method, handler); + } else { + methodToHandler.put(method,nameToHandler.get(Feign.configKey(target.type(), method))); + } + } + } + InvocationHandler handler = this.factory.create(target, methodToHandler); + // »ùÓÚProxy.newProxyInstance Ϊ½Ó¿ÚÀà´´½¨¶¯Ì¬ÊµÏÖ£¬½«ËùÓеÄÇëÇóת»»¸øInvocationHandler ´¦Àí¡£ + T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler); + Iterator var12 = defaultMethodHandlers.iterator(); + while(var12.hasNext()) { + DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next(); + defaultMethodHandler.bindTo(proxy); + } + return proxy; +} +``` + + + + + + + + + +**2.4 ½Ó¿Ú¶¨ÒåµÄ²ÎÊý½âÎö** +¸ù¾ÝFeignClient½Ó¿ÚµÄÃèÊö½âÎö³ö¶ÔÓ¦µÄÇëÇóÊý¾Ý¡£ + +**2.4.1 targetToHandlersByName.apply(target)** +¸ù¾ÝContractЭÒ鹿Ôò£¬½âÎö½Ó¿ÚÀàµÄ×¢½âÐÅÏ¢£¬½âÎö³ÉÄÚ²¿±íÏÖ£º + +targetToHandlersByName.apply(target);»á½âÎö½Ó¿Ú·½·¨ÉϵÄ×¢½â£¬´Ó¶ø½âÎö³ö·½·¨Á£¶ÈµÄÌØ¶¨µÄÅäÖÃÐÅÏ¢£¬È»ºóÉú²úÒ»¸öSynchronousMethodHandler È»ºóÐèҪά»¤Ò»¸öµÄmap£¬·ÅÈëInvocationHandlerµÄʵÏÖFeignInvocationHandlerÖС£ + + + +``` +public Map apply(Target target) { + List metadata = contract.parseAndValidateMetadata(target.type()); + Map result = new LinkedHashMap(); + for (MethodMetadata md : metadata) { + BuildTemplateByResolvingArgs buildTemplate; + if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) + { + buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder,target); + } else if (md.bodyIndex() != null) { + buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder,queryMapEncoder, target); + } else { + buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder,target); + } + if (md.isIgnored()) { + result.put(md.configKey(), args -> { + throw new IllegalStateException(md.configKey() + " is not a method handled by feign"); + }); + } else { + result.put(md.configKey(), + factory.create(target, md, buildTemplate, options, decoder,errorDecoder)); + } + } + return result; +} +``` + + + + + + + + + +**2.4.2 SpringMvcContract** +µ±Ç°Spring Cloud ΢·þÎñ½â¾ö·½°¸ÖУ¬ÎªÁ˽µµÍѧϰ³É±¾£¬²ÉÓÃÁËSpring MVCµÄ²¿·Ö×¢½âÀ´Íê³É ÇëÇóÒé½âÎö£¬Ò²¾ÍÊÇ˵ £¬Ð´¿Í»§¶ËÇëÇó½Ó¿ÚºÍÏñд·þÎñ¶Ë´úÂëÒ»Ñù£º¿Í»§¶ËºÍ·þÎñ¶Ë¿ÉÒÔͨ¹ýSDKµÄ·½Ê½½øÐÐÔ¼¶¨£¬¿Í»§¶ËÖ»ÐèÒªÒýÈë·þÎñ¶Ë·¢²¼µÄSDK API£¬¾Í¿ÉÒÔʹÓÃÃæÏò½Ó¿ÚµÄ±àÂ뷽ʽ¶Ô½Ó·þÎñ¡£ + +¸ÃÀà¼Ì³ÐÁËContract.BaseContract²¢ÊµÏÖÁËResourceLoaderAware½Ó¿Ú£¬ + +Æä×÷ÓþÍÊǶÔRequestMapping¡¢RequestParam¡¢RequestHeaderµÈ×¢½â½øÐнâÎöµÄ¡£ + +**2.5 OpenFeignµ÷Óùý³Ì** +ÔÚÇ°ÃæµÄ·ÖÎöÖУ¬ÎÒÃÇÖªµÀOpenFeign×îÖÕ·µ»ØµÄÊÇÒ»¸ö# +ReflectiveFeign.FeignInvocationHandlerµÄ¶ÔÏó¡£ + +ÄÇôµ±¿Í»§¶Ë·¢ÆðÇëÇóʱ£¬»á½øÈëµ½ +FeignInvocationHandler.invoke·½·¨ÖУ¬Õâ¸ö´ó¼Ò¶¼ÖªµÀ£¬ËüÊÇÒ»¸ö¶¯Ì¬´úÀíµÄʵÏÖ¡£ + + + +``` +public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (!"equals".equals(method.getName())) { + if ("hashCode".equals(method.getName())) { + return this.hashCode(); + } else { + return "toString".equals(method.getName()) ? this.toString() : + ((MethodHandler)this.dispatch.get(method)).invoke(args); + } + } else { + try { + Object otherHandler = args.length > 0 && args[0] != null ? + Proxy.getInvocationHandler(args[0]) : null; + return this.equals(otherHandler); + } catch (IllegalArgumentException var5) { + return false; + } + } +} +``` + + + + + + + + + +¶ø½Ó×Å£¬ÔÚinvoke·½·¨ÖУ¬»áµ÷Óà this.dispatch.get(method)).invoke(args) ¡£this.dispatch.get(method) »á·µ»ØÒ»¸öSynchronousMethodHandler,½øÐÐÀ¹½Ø´¦Àí¡£ + +Õâ¸ö·½·¨»á¸ù¾Ý²ÎÊýÉú³ÉÍê³ÉµÄRequestTemplate¶ÔÏó£¬Õâ¸ö¶ÔÏóÊÇHttpÇëÇóµÄÄ£°æ£¬´úÂëÈçÏ¡£ + + + +``` +public Object invoke(Object[] argv) throws Throwable { + RequestTemplate template = this.buildTemplateFromArgs.create(argv); + Options options = this.findOptions(argv); + Retryer retryer = this.retryer.clone(); + while(true) { + try { + return this.executeAndDecode(template, options); + } catch (RetryableException var9) { + RetryableException e = var9; + try { + retryer.continueOrPropagate(e); + } catch (RetryableException var8) { + Throwable cause = var8.getCause(); + if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP + && cause != null) { + throw cause; + } + throw var8; + } + if (this.logLevel != Level.NONE) { + this.logger.logRetry(this.metadata.configKey(), this.logLevel); + } + } + } +} +``` + + + + + + + + + +**2.5.1 executeAndDecode** +¾­¹ýÉÏÊöµÄ´úÂ룬ÎÒÃÇÒѾ­½«restTemplateÆ´×°Íê³É£¬ÉÏÃæµÄ´úÂëÖÐÓÐÒ»¸ö executeAndDecode() ·½·¨£¬¸Ã·½·¨Í¨¹ýRequestTemplateÉú³ÉRequestÇëÇó¶ÔÏó£¬È»ºóÀûÓÃHttp Client»ñÈ¡response£¬À´»ñÈ¡ÏìÓ¦ÐÅÏ¢¡£ + + + +``` +Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { + //ת»¯ÎªHttpÇëÇó±¨ÎÄ + Request request = this.targetRequest(template); + if (this.logLevel != Level.NONE) { + this.logger.logRequest(this.metadata.configKey(), this.logLevel,request); + } + long start = System.nanoTime(); + Response response; + try { + //·¢ÆðÔ¶³ÌͨÐÅ + response = this.client.execute(request, options); + //»ñÈ¡·µ»Ø½á¹û + response = response.toBuilder().request(request).requestTemplate(template).build(); + } catch (IOException var16) { + if (this.logLevel != Level.NONE) { + this.logger.logIOException(this.metadata.configKey(), this.logLevel,var16, this.elapsedTime(start)); + } + throw FeignException.errorExecuting(request, var16); + } + long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + boolean shouldClose = true; + Response var10; + try { + if (this.logLevel != Level.NONE) { + response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel,response, elapsedTime); + } + if (Response.class != this.metadata.returnType()) { + Object result; + Object var21; + if (response.status() >= 200 && response.status() < 300) { + if (Void.TYPE == this.metadata.returnType()) { + var10 = null; + return var10; + } + result = this.decode(response); + shouldClose = this.closeAfterDecode; + var21 = result; + return var21; + } + if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) { + result = this.decode(response); + shouldClose = this.closeAfterDecode; + var21 = result; + return var21; + } + throw this.errorDecoder.decode(this.metadata.configKey(), response); + } + if (response.body() == null) { + var10 = response; + return var10; + } + if (response.body().length() != null && (long)response.body().length() <= 8192L) { + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + Response var11 = response.toBuilder().body(bodyData).build(); + return var11; + } + shouldClose = false; + var10 = response; + } catch (IOException var17) { + if (this.logLevel != Level.NONE) { + this.logger.logIOException(this.metadata.configKey(), this.logLevel,var17, elapsedTime); + } + throw FeignException.errorReading(request, response, var17); + } finally { + if (shouldClose) { + Util.ensureClosed(response.body()); + } + } + return var10; +} +``` + + + + + + + + + +**2.5.2 Client.execute** +ĬÈϲÉÓÃJDKµÄ HttpURLConnection ·¢ÆðÔ¶³Ìµ÷Óᣠ+ + + +``` +@Override +public Response execute(Request request, Options options) throws IOException { + HttpURLConnection connection = convertAndSend(request, options); + return convertResponse(connection, request); +} +Response convertResponse(HttpURLConnection connection, Request request) throws IOException { + int status = connection.getResponseCode(); + String reason = connection.getResponseMessage(); + if (status < 0) { + throw new IOException(format("Invalid status(%s) executing %s %s",status,connection.getRequestMethod(),connection.getURL())); + } + Map> headers = new LinkedHashMap<>(); + for (Map.Entry> field : + connection.getHeaderFields().entrySet()) { + // response message + if (field.getKey() != null) { + headers.put(field.getKey(), field.getValue()); + } + } + Integer length = connection.getContentLength(); + if (length == -1) { + length = null; + } + InputStream stream; + if (status >= 400) { + stream = connection.getErrorStream(); + } else { + stream = connection.getInputStream(); + } + return Response.builder() + .status(status) + .reason(reason) + .headers(headers) + .request(request) + .body(stream, length) + .build(); +} +``` + + + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..d06f945 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringCloud\346\272\220\347\240\201\345\210\206\346\236\220/SpringCloudRibbon\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,955 @@ +**ѧϰĿ±ê** + +1. ÍÆµ¼RibbonµÄºËÐÄÁ÷³Ì +2. ÊÖдһ¸ö¼òÒ×°æµÄRibbon +3. ͨ¹ýÔ´ÂëÑéÖ¤ÍÆµ¼µÄÁ÷³Ì + **µÚ1Õ ºËÐÄÁ÷³ÌÍÆµ¼** + ÆäʵRibbonµÄºËÐÄÁ÷³ÌºÜ¼òµ¥£¬ÎÒÃÇÔÚʹÓùý³ÌÖÐÎ޷ǾÍÊÇÒýÈëÁËÒ»¸öspring-cloud-starter-netflix-ribbonµÄjar°ü£¬È»ºóÔÚ³ÌÐòÆô¶¯µÄʱºò×¢ÈëÁËÒ»¸öRestTemplate¶ÔÏó£¬ÔڸöÔÏóÉÏÃæÔö¼ÓÁËÒ»¸ö@LoadBalanced×¢½â£¬È»ºóÔÚͨ¹ýRestTemplate¶ÔÏóÈ¥µ÷ÓÃURLµÄʱºò¾ÍÄܸù¾Ý²»Í¬µÄ¸ºÔؾùºâ²ßÂÔÈ¥µ÷²»Í¬µÄ·þÎñ£¬ÄÇÕâ¸ö×¢½â£¬»òÕß˵Õâ¸öjar°üµ½µ××öÁËʲôÊÂÇéÄØ£¿ + +Ê×ÏÈÎÒÃÇÒªÃ÷°×£¬spring-cloud-starter-netflix-ribbonÕâ¸öjar°ü´ÓÃû×ÖÉÏÀ´¿´¾ÍÖªµÀ£¬ËüÊÇ»ùÓÚstarter×é¼þµÄ£¬ÄÇËü¿Ï¶¨ÊÇÒÀ¾ÝspringbootµÄ×Ô¶¯×°ÅäÔ­Àí£¬ÔÚÈÝÆ÷Æô¶¯µÄʱºòÌṩÁËÒ»¸ö×Ô¶¯ÅäÖÃÀ࣬½«ÎÒÃÇËùÐèÒªÓõĶÔÏó×¢Èëµ½IoCÈÝÆ÷ÀïÃæÈ¥ÁË£¬ÕâµãÎãÓ¹ÖÃÒÉ¡£ + +È»ºóµ±ÒªÓÃRestTemplate¶ÔÏóÈ¥ÇëÇóÄ¿±ê·þÎñµÄʱºò£¬Õâ¸öʱºò£¬ÎÒÃǿ϶¨ÊÇÒªÓÃÕæÊµµÄIPºÍ¶Ë¿ÚÀ´Ìæ»»·þÎñÃû¡£ÕâÒ»²½Æäʵ¾ÍÊǺËÐIJ½Ö裬ËüÒªÔõô×öÄØ£¿ÔõôÔÚÕæÕýÇëÇó֮ǰ½«µØÖ·ºÍ¶Ë¿ÚÀê軻̫×Ó»»³ÉÕæÊµµÄÄØ£¿ + +ÔÚÎÒÃÇÈÕ³£¿ª·¢ÖУ¬ÎÒÃÇÓ¦¸ÃÖªµÀ£¬ÓÐÒ»¸ö¹ýÂËÆ÷ºÍÒ»¸öÀ¹½ØÆ÷Æäʵ¿ÉÒÔ×öµ½Õâ¸ö²Ù×÷£¬¶Ô°É£¬ËùÒÔ£¬Êµ¼ÊÉϾÍÊÇÔÚ»ñÈ¡RestTemplate¶ÔÏóµÄʱºò£¬½«¸Ã¶ÔÏóÀïÃæÌí¼ÓÁËÒ»¸öÀ¹½ØÆ÷£¬µ±RestTemplate¶ÔÏóÖ´ÐÐij¸ö·½·¨µÄʱºò£¬¶¼»áÈ¥À¹½ØÆ÷ÀïÃæÖ´ÐÐÒ»±é¡£È»ºó¾ÍÍêÊÂÁË¡£ + +¾ßÌåµÄÁ÷³ÌÍ¼ÍÆµ¼ÈçÏ£º![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1790fb324161913dd79098940f8102d652cd86.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")**µÚ2Õ ¼òÒ×°æRibbonʵÏÖ** +¸ù¾ÝÉÏÃæµÄÍÆµ¼¹ý³Ì£¬ÎÒÃǽÓÏÂÀ´À´ÊµÏÖÒ»¸ö¼òÒ×°æµÄRibbon¡£ + +¾ßÌåµÄ²½ÖèÎÒÃÇÒªÓÐÇåÎúµÄ˼·£º + +1.Ê×ÏÈҪʵÏÖÒ»¸östarter×é¼þ£¬¼¯³ÉÎÒÃÇspringboot£¬ÈÃspringbootÔÚÆô¶¯µÄʱºò¿ÉÒÔÄõ½ÏàÓ¦µÄRestTemplateµÄbean¶ÔÏó¡£´´½¨Ò»¸öMavenµÄquickstartÏîÄ¿ + +2.È»ºóÒý°ü + + + +``` + + + + 4.0.0 + + com.example + myribbon-spring-cloud-starter + 1.0-SNAPSHOT + + myribbon-spring-cloud-starter + + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + 2.3.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + + + + + org.springframework.cloud + spring-cloud-commons + 2.2.6.RELEASE + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + +``` + + + + + + + + + +3.´´½¨ÅäÖÃÀà + + + +``` +@Configuration +public class MyRibbonAutoConfiguration { + //¼òÒ×°æµÄRibbonÊÇ¿¿Õâ¸öÀàÈ¥Íê³É¸ºÔؾùºâËã·¨ÒÔ¼°ÕæÊµµÄipºÍ¶Ë¿ÚÌæ»»µÄ + @Bean + public LoadBalancerClient loadBalancerClient(){ + return new MyLoadBalancerClient(); + } + //ÊÕ¼¯ËùÓдøMyLoadBalanced×¢½âµÄRestTemplate¶ÔÏó + @MyLoadBalanced + @Autowired(required = false) + private List restTemplates = Collections.emptyList(); + @Bean + @ConditionalOnMissingBean + public LoadBalancerRequestFactory loadBalancerRequestFactory( + LoadBalancerClient loadBalancerClient) { + return new LoadBalancerRequestFactory(loadBalancerClient); + } + //Õâ¾ÍÊǺËÐĵÄÀ¹½ØÆ÷ + @Bean + public MyLoadBalancerInterceptor myLoadBalancerInterceptor( + LoadBalancerClient loadBalancerClient, + LoadBalancerRequestFactory requestFactory){ + return new MyLoadBalancerInterceptor(loadBalancerClient,requestFactory); + } + //ÊÕ¼¯µ½µÄRestTemplate¶ÔÏó£¬ÔÚÕâÀï¶¼»áÅäÖÃÒ»¸öÀ¹½ØÆ÷ + @Bean + public SmartInitializingSingleton smartInitializingSingleton( + final MyLoadBalancerInterceptor myLoadBalancerInterceptor){ + return ()->{ + for (RestTemplate restTemplate : MyRibbonAutoConfiguration.this.restTemplates) { + List list = new ArrayList<>( + restTemplate.getInterceptors()); + list.add(myLoadBalancerInterceptor); + restTemplate.setInterceptors(list); + } + }; + } +} +``` + + + + + + + + + +4.´´½¨MyLoadBalancerClient + + + +``` +public class MyLoadBalancerClient implements LoadBalancerClient { + @Autowired + AbstractEnvironment environment; + //1.ÔÚÀ¹½ØÆ÷ÀïÃæ»áµ÷ÓÃÕâ¸ö·½·¨ + @Override + public T execute(String serviceId, LoadBalancerRequest request) throws IOException { + ServiceInstance server = this.choose(serviceId); + return execute(serviceId, server, request); + } + //3.ÕæÕýÖ´ÐÐHttpÇëÇó + @Override + public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { + T returnVal = null; + try { + returnVal = request.apply(serviceInstance); + } catch (Exception e) { + e.printStackTrace(); + } + return returnVal; + } + //4.ÕâÒ»²½¾ÍÊÇÓÃÕæÊµµÄipºÍportÌæ»»·þÎñÃû + @Override + public URI reconstructURI(ServiceInstance instance, URI original) { + String host = instance.getHost(); + int port = instance.getPort(); + if (host.equals(original.getHost()) + && port == original.getPort() + ) { + return original; + } + try { + StringBuilder sb = new StringBuilder(); + sb.append("http").append("://"); + if (!Strings.isNullOrEmpty(original.getRawUserInfo())) { + sb.append(original.getRawUserInfo()).append("@"); + } + sb.append(host); + if (port >= 0) { + sb.append(":").append(port); + } + sb.append(original.getRawPath()); + if (!Strings.isNullOrEmpty(original.getRawQuery())) { + sb.append("?").append(original.getRawQuery()); + } + if (!Strings.isNullOrEmpty(original.getRawFragment())) { + sb.append("#").append(original.getRawFragment()); + } + URI newURI = new URI(sb.toString()); + return newURI; + }catch (URISyntaxException e){ + throw new RuntimeException(e); + } + } + //2.¸ºÔؾùºâËã·¨ÔÚÕâÒ»²½½øÐзþÎñµÄipºÍ¶Ë¿ÚÑ¡Ôñ£¬ÎÒÕâÀïÓõÄÊÇËæ»úËã·¨ + @Override + public ServiceInstance choose(String serviceId) { + Server instance = new Server(serviceId,null,"127.0.0.1",8080); + String sr = environment.getProperty(serviceId+".ribbon.listOfServers"); + if (!StringUtils.isEmpty(sr)){ + String[] arr = sr.split(",",-1); + Random selector = new Random(); + int next = selector.nextInt(arr.length); + String a = arr[next]; + String[] srr = a.split(":",-1); + instance.setHost(srr[0]); + instance.setPort(Integer.parseInt(srr[1])); + } + return instance; + } +} +``` + + + + + + + + + +5.À¹½ØÆ÷µÄÂß¼­ÆäʵºÜ¼òµ¥£¬¾ÍÊÇÔÚÕæÕý·¢ËÍhttpÇëÇó֮ǰÏÈÀ´Ö´ÐÐÎÒµÄÂß¼­ + + + +``` +public class MyLoadBalancerInterceptor implements ClientHttpRequestInterceptor { + private LoadBalancerClient loadBalancerClient; + private LoadBalancerRequestFactory requestFactory; + public MyLoadBalancerInterceptor(LoadBalancerClient loadBalancerClient, + LoadBalancerRequestFactory requestFactory) { + this.loadBalancerClient = loadBalancerClient; + this.requestFactory = requestFactory; + } + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + final URI originalUri = request.getURI(); + String serviceName = originalUri.getHost(); + return this.loadBalancerClient.execute(serviceName, + this.requestFactory.createRequest(request, body, execution)); + } +} +``` + + + + + + + + + +6.¶¨Òå×Ô¼ºµÄ×¢½â + + + +``` +@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Qualifier +public @interface MyLoadBalanced { +} +``` + + + + + + + + + +7.¶¨ÒåÒ»¸ö×Ô¼ºµÄServerʵÌåÀà + + + +``` +public class Server implements ServiceInstance { + private String serviceId; + private String instanceId; + private String host; + private int port; + public Server(String serviceId, String instanceId, String host, int port) { + this.serviceId = serviceId; + this.instanceId = instanceId; + this.host = host; + this.port = port; + } + public Server() { + } + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + public void setHost(String host) { + this.host = host; + } + public void setPort(int port) { + this.port = port; + } + @Override + public String getInstanceId() { + return null; + } + @Override + public String getServiceId() { + return null; + } + @Override + public String getHost() { + return host; + } + @Override + public int getPort() { + return port; + } + @Override + public boolean isSecure() { + return false; + } + @Override + public URI getUri() { + return null; + } + @Override + public Map getMetadata() { + return null; + } + @Override + public String getScheme() { + return null; + } +} +``` + + + + + + + + + +8.дspring.factoriesÎļþ + + + +``` +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.example.config.MyRibbonAutoConfiguration +``` + + + + + + + + + +9.´ò³Éjar°ü£¬²âÊÔ£¬²âÊÔµÄʱºò£¬ÐèÒª¼ÓÅäÖÃÎļþ + + + +``` +.ribbon.listOfServers=127.0.0.1:2223,127.0.0.1:2222 +``` + + + + + + + + + +**µÚ3Õ ԴÂëÑéÖ¤** +**3.1 @LoadBalanced** +´ÓÉϽڿεĴúÂë¿´£¬ÎÒÃÇÖ»ÊÇÔÚRestTemplateÉÏÃæ¼ÓÁËÒ»¸ö@LoadBalance,¾Í¿ÉÒÔʵÏÖ¸ºÔؾùºâÁË£¬¿ÉÊÇÎÒÃǵã»÷½øÈë@LoadBalance¿´ÁËһϣ¬ÔÚÕâ¸ö×¢½âÀïÃæÓÐÒ»¸ö@Qualifier×¢½â¡£¸Ã×¢½âÏÞ¶¨ÄĸöbeanÓ¦¸Ã±»×Ô¶¯×¢Èë¡£µ±SpringÎÞ·¨ÅжϳöÄĸöbeanÓ¦¸Ã±»×¢Èëʱ£¬@Qualifier×¢½âÓÐÖúÓÚÏû³ýÆçÒåbeanµÄ×Ô¶¯×¢È룬Àý×Ó¼û´úÂë¡£ + + + +``` +/** + * Annotation to mark a RestTemplate or WebClient bean to be configured to use a + * LoadBalancerClient. + * @author Spencer Gibb + */ +@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Qualifier +public @interface LoadBalanced { + +} +``` + + + + + + + + + +´Ó×¢ÊÍÖпÉÒÔÖªµÀ£¬Õâ¸ö×¢½âÊÇÓÃÀ´¸øRestTemplate×ö±ê¼Ç£¬ÒÔʹÓøºÔؾùºâ¿Í»§¶Ë£¨LoadBalancerClient£©À´ÅäÖÃËü¡£ËùÒÔ£¬ÎÒÃÇÔÚÉú³ÉµÄRestTemplateµÄbeanÉÏÌí¼ÓÕâôһ¸ö×¢½â£¬Õâ¸öbean¾Í»áÅäÖÃLoadBalancerClient¡£ + +**3.2 LoadBalancerClient** +ÄÇô£¬¾ÍÔÙ¿´ÏÂLoadBalancerClientµÄ´úÂ룺 + + + +``` +public interface LoadBalancerClient extends ServiceInstanceChooser { + T execute(String serviceId, LoadBalancerRequest request) throws IOException; + T execute(String serviceId, ServiceInstance serviceInstance, + LoadBalancerRequest request) throws IOException; + URI reconstructURI(ServiceInstance instance, URI original); +} +public interface ServiceInstanceChooser { + ServiceInstance choose(String serviceId); +} +``` + + + + + + + + + +LoadBalancerClientÊÇÒ»¸ö½Ó¿Ú£¬ÀïÃæÓÐÈý¸ö·½·¨¡£ + +ServiceInstance choose(String serviceId);´Ó·½·¨ÃûÉϾͿÉÒÔ¿´³ö£¬ÊǸù¾Ý´«ÈëµÄserviceId£¨·þÎñÃû£©£¬´Ó¸ºÔؾùºâÆ÷ÖÐÑ¡ÔñÒ»¸ö·þÎñʵÀý£¬·þÎñʵÀýͨ¹ýServiceInstanceÀàÀ´±íʾ¡£ +execute·½·¨£¬Ê¹ÓôӸºÔؾùºâÆ÷ÖÐÑ¡ÔñµÄ·þÎñʵÀýÀ´Ö´ÐÐÇëÇóÄÚÈÝ¡£ +URI reconstructURI(ServiceInstance instance, URI original);·½·¨£¬ÊÇÖØÐ¹¹½¨Ò»¸öURIµÄ£¬»¹¼ÇµÃÎÒÃÇÔÚ´úÂëÖУ¬Í¨¹ýRestTemplateÇëÇó·þÎñʱ£¬Ð´µÄÊÇ·þÎñÃû°É£¬Õâ¸ö·½·¨¾Í»á°ÑÕâ¸öÇëÇóµÄURI½øÐÐת»»£¬·µ»Øhost+port£¬Í¨¹ýhost+portµÄÐÎʽȥÇëÇó·þÎñ¡£ +**3.3 ×Ô¶¯×°Åä** +µ±springbootÆô¶¯Ö®ºó£¬»áͨ¹ý×Ô¶¯×°Åä×Ô¶¯È¥ +spring-cloud-netflix-ribbonÕâ¸öjar°üµÄMETA-INFĿ¼ÕÒspring.factoriesÎļþ£¬²¢ÇÒ½«RibbonAutoConfigurationÅäÖÃÀà½øÐÐ×¢Èë¡£¶øÔÚRibbonAutoConfigurationÅäÖÃÀàÖÐÒòΪ´æÔÚ@AutoConfigureBefore×¢½â£¬ËùÒÔÓÖ»á¼ÓÔØLoadBalancerAutoConfigurationÅäÖÃÀà¡£ÔÚLoadBalancerAutoConfigurationÀàÖУ¬springÈÝÆ÷»á½«ËùÓб»@LoadBalance×¢½âÐÞÊεÄbean×¢Èëµ½IOCÈÝÆ÷ÖÐ + + + +``` +@LoadBalanced +@Autowired(required = false) +private List restTemplates = Collections.emptyList(); +``` + + + + + + + + + +ͬʱ£¬ÔÚLoadBalancerAutoConfigurationÅäÖÃÀàÖл¹»áΪÿ¸öRestTemplateʵÀýÌí¼ÓLoadBalancerInterceptorÀ¹½ØÆ÷¡£ + +ÔÚRibbonAutoConfigurationÀàÖÐ×¢ÈëÁËLoadBalancerClient½Ó¿ÚµÄʵÏÖÀàRibbonLoadBalancerClient + + + +``` +@Bean +@ConditionalOnMissingBean(LoadBalancerClient.class) +public LoadBalancerClient loadBalancerClient() { + return new RibbonLoadBalancerClient(springClientFactory()); +} +``` + + + + + + + + + +**3.4 À¹½ØÆ÷** +ÓÉÓÚÔÚ×Ô¶¯ÅäÖÃÀàÖУ¬¶ÔrestTemplateʵÀýÌí¼ÓÁËLoadBalancerInterceptorÀ¹½ØÆ÷£¬ËùÒÔ£¬µ±ÓÃrestTemplate·¢ËÍhttpÇëÇóʱ£¬¾Í»áÖ´ÐÐÕâ¸öÀ¹½ØÆ÷µÄintercept·½·¨¡£ + +intercept·½·¨ÖУ¬»á¸ù¾Ýrequest.getURI()£¬»ñÈ¡ÇëÇóµÄuri£¬ÔÙ»ñÈ¡host£¬ÎÒÃÇÔÚ·¢ËÍhttpÇëÇóµÄʱºò£¬ÊÇÓõķþÎñÃû×÷Ϊhost£¬ËùÒÔ£¬ÕâÀï¾Í»áÄõ½·þÎñÃû£¬ÔÙµ÷ÓþßÌåLoadBalancerClientʵÀýµÄexecute·½·¨£¬·¢ËÍÇëÇó¡£ + +LoadBalancerClientµÄʵÏÖÀàΪRibbonLoadBalancerClient£¬×îÖյĸºÔؾùºâÇëÇóÓÉËüÀ´Ö´ÐУ¬ËùÒÔ£¬»¹ÐèÒªÔÙÊáÀíÏÂRibbonLoadBalancerClientµÄÂß¼­¡£ + +ÏÈ¿´ÏÂRibbonLoadBalancerClientÖеÄexecute·½·¨£º + + + +``` +public T execute(String serviceId, LoadBalancerRequest request, Object hint) + throws IOException { + ILoadBalancer loadBalancer = getLoadBalancer(serviceId); + Server server = getServer(loadBalancer, hint); + if (server == null) { + throw new IllegalStateException("No instances available for " + serviceId); + } + RibbonServer ribbonServer = new RibbonServer(serviceId, server, + isSecure(server, serviceId), + serverIntrospector(serviceId).getMetadata(server)); + + return execute(serviceId, ribbonServer, request); +} + +@Override +public T execute(String serviceId, ServiceInstance serviceInstance, + LoadBalancerRequest request) throws IOException { + Server server = null; + if (serviceInstance instanceof RibbonServer) { + server = ((RibbonServer) serviceInstance).getServer(); + } + if (server == null) { + throw new IllegalStateException("No instances available for " + serviceId); + } + + RibbonLoadBalancerContext context = this.clientFactory + .getLoadBalancerContext(serviceId); + RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); + try { + T returnVal = request.apply(serviceInstance); + statsRecorder.recordStats(returnVal); + return returnVal; + } + // catch IOException and rethrow so RestTemplate behaves correctly + catch (IOException ex) { + statsRecorder.recordStats(ex); + throw ex; + } + catch (Exception ex) { + statsRecorder.recordStats(ex); + ReflectionUtils.rethrowRuntimeException(ex); + } + return null; +} +``` + + + + + + + + + +·þÎñÃû×÷ΪserviceId×ֶδ«½øÀ´£¬ÏÈͨ¹ýgetLoadBalancer»ñÈ¡loadBalancer£¬ÔÙ¸ù¾ÝloadBalancer»ñÈ¡server£¬ÏÂÃæÊÇgetServerµÄ´úÂ룺 + + + +``` +protected Server getServer(ILoadBalancer loadBalancer, Object hint) { + if (loadBalancer == null) { + return null; + } + // Use 'default' on a null hint, or just pass it on? + return loadBalancer.chooseServer(hint != null ? hint : "default"); +} +``` + + + + + + + + + +Èç¹ûloadBalancerΪ¿Õ£¬¾ÍÖ±½Ó·µ»Ø¿Õ£¬·ñÔò¾Íµ÷ÓÃloadBalancerµÄchooseServer·½·¨£¬»ñÈ¡ÏàÓ¦µÄserver¡£ + +¿´Ò»ÏÂILoadBalancerÊÇÒ»¸ö½Ó¿Ú£¬ÀïÃæÉùÃ÷ÁËһϵÁиºÔؾùºâʵÏֵķ½·¨£º + + + +``` +public interface ILoadBalancer { + public void addServers(List newServers); + public Server chooseServer(Object key); + public void markServerDown(Server server); + @Deprecated + public List getServerList(boolean availableOnly); + public List getReachableServers(); + public List getAllServers(); +} +``` + + + + + + + + + +ÕâЩ·½·¨Ãû±È½ÏÖ±¹Û£¬ºÜÈÝÒ×¾ÍÄܲ³öÊǸÉɶµÄ£¬addServersÊÇÓÃÀ´Ìí¼ÓÒ»¸öserver¼¯ºÏ£¬chooseServerÊÇÑ¡ÔñÒ»¸öserver£¬markServerDownÓÃÀ´±ê¼Çij¸ö·þÎñÏÂÏߣ¬getReachableServers»ñÈ¡¿ÉÓõÄServer¼¯ºÏ£¬getAllServersÊÇ»ñÈ¡ËùÓеÄserver¼¯ºÏ¡£ +ILoadBalancerÓкܶàʵÏÖ£¬ÄǾßÌåÊÇÓõÄÄĸöÀàÄØ£¬ÔÚRibbonAutoConfigurationÀàÖÐ×¢ÈëSpringClientFactory£¬Í¨¹ýRibbonClientConfigurationÀà¿´µ½£¬Õâ¸öÅäÖÃÀàÔÚ³õʼ»¯µÄʱºò£¬·µ»ØÁËZoneAwareLoadBalancer×÷Ϊ¸ºÔؾùºâÆ÷¡£ + + + +``` +@Bean +@ConditionalOnMissingBean +public ILoadBalancer ribbonLoadBalancer(IClientConfig config, + ServerList serverList, ServerListFilter serverListFilter, + IRule rule, IPing ping, ServerListUpdater serverListUpdater) { + if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) { + return this.propertiesFactory.get(ILoadBalancer.class, config, name); + } + return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, + serverListFilter, serverListUpdater); +} +``` + + + + + + + + + +**3.5 ZoneAwareLoadBalancer** +ZoneAwareLoadBalancer´ÓÃû×ÖÖпÉÒÔ¿´³öÀ´£¬Õâ¸ö¸ºÔؾùºâÆ÷ºÍzoneÊÇÓйØÏµµÄ¡£ÏÂÃæ¿´ÏÂZoneAwareLoadBalancerÖеÄchooseServer·½·¨£º + +> eurekaÌṩÁËregionºÍzoneÁ½¸ö¸ÅÄîÀ´½øÐзÖÇø£¬ÕâÁ½¸ö¸ÅÄî¾ùÀ´×ÔÓÚÑÇÂíÑ·µÄAWS£º +> region£º¿ÉÒÔ¼òµ¥Àí½âΪµØÀíÉϵķÖÇø£¬±ÈÈçÑÇÖÞµØÇø£¬»òÕß»ª±±µØÇø£¬ÔÙ»òÕß±±¾©µÈµÈ£¬Ã»ÓоßÌå´óСµÄÏÞÖÆ¡£¸ù¾ÝÏîÄ¿¾ßÌåµÄÇé¿ö£¬¿ÉÒÔ×ÔÐкÏÀí»®·Öregion¡£ +> zone£º¿ÉÒÔ¼òµ¥Àí½âΪregionÄڵľßÌå»ú·¿£¬±ÈÈç˵region»®·ÖΪ±±¾©£¬È»ºó±±¾©ÓÐÁ½¸ö»ú·¿£¬¾Í¿ÉÒÔÔÚ´Ëregion֮ϻ®·Ö³özone1,zone2Á½¸özone¡£ + + + +``` +@Override +public Server chooseServer(Object key) { + //Ö»Óе±¸ºÔؾùºâÆ÷ÖÐά»¤µÄʵÀýËùÊôµÄZoneÇøÓòµÄ¸öÊý´óÓÚ1µÄʱºò²Å»áÖ´ÐÐÑ¡Ôñ²ßÂÔ + //·ñÔò»¹ÊÇʹÓø¸ÀàµÄʵÏÖ + if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) { + logger.debug("Zone aware logic disabled or there is only one zone"); + return super.chooseServer(key); + } + Server server = null; + try { + LoadBalancerStats lbStats = getLoadBalancerStats(); + //Ϊµ±Ç°¸ºÔؾùºâÆ÷ÖеÄËùÓÐZoneÇøÓò·Ö±ð´´½¨¿ìÕÕ£¬±£´æÔÚzoneSnapshotÖУ¬ÕâЩ¿ìÕÕÖеÄÊý¾ÝÓÃÓÚºóÐøµÄËã·¨ + Map zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats); + logger.debug("Zone snapshots: {}", zoneSnapshot); + if (triggeringLoad == null) { + triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty( + "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d); + } + + if (triggeringBlackoutPercentage == null) { + triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty( + "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d); + } + //»ñµÃ¿ÉÓÃZoneÇøÓòµÄ¼¯ºÏ£¬getAvailableZones»áͨ¹ýzoneSnapshotʵÏÖ¿ÉÓÃÇøÓòÌôÑ¡ + Set availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get()); + logger.debug("Available zones: {}", availableZones); + if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) { + //Ëæ»úÑ¡ÔñÒ»¸öZoneÇøÓò + String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones); + logger.debug("Zone chosen: {}", zone); + if (zone != null) { + //»ñµÃ¶ÔÓ¦ÇøÓòµÄ¸ºÔؾùºâÆ÷ + BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone); + //Ñ¡Ôñ¾ßÌåµÄ·þÎñʵÀý + //ÔÚchooseServerÖн«»áʹÓÃIRule½Ó¿ÚµÄchooseº¯ÊýÀ´Ñ¡Ôñ¾ßÌå·þÎñʵÀý¡£ÔÚÕâÀIRule½Ó¿ÚµÄʵÏÖ»áʵÏÖZoneAvoidanceRuleÀ´ÌôÑ¡¾ßÌåµÄ·þÎñʵÀý¡£ + server = zoneLoadBalancer.chooseServer(key); + } + } + } catch (Exception e) { + logger.error("Error choosing server using zone aware logic for load balancer={}", name, e); + } + if (server != null) { + return server; + } else { + logger.debug("Zone avoidance logic is not invoked."); + return super.chooseServer(key); + } +} +``` + + + + + + + + + +Õâ¸ö·½·¨»á¸ù¾ÝserverµÄzoneºÍ¿ÉÓÃÐÔÀ´Ñ¡Ôñ¾ßÌåµÄʵÀý£¬·µ»ØÒ»¸öServer¶ÔÏó¡£ + +Õâ¸öÀàÀïÃæµÄ¼¸¸ö·½·¨£º + +1. setServerListForZones : Õâ¸ö»ùÓÚZone½øÐзþÎñ»®·Ö +2. chooseServerÕâÀïÒ²ÊÇÖ÷Òª¸úzoneÓйصļÆË㣬µ±È»Ä¬ÈϵÄZoneÖ»ÓÐÒ»¸ö£¬ËùÒÔÖ±½ÓÊǵ÷Óõĸ¸ÀàµÄchooseServer£¨key) +3. getLoadBalancer(String zone) »ùÓÚzoneÈ¥»ñÈ¡LoadBalancer +4. setRule(IRule rule) Ϊÿ¸ö¸ºÔؾùºâÆ÷ÉèÖùæÔò¡£ + ÕâÀï¿ÉÒÔ¿´µ½£¬ÆäʵÕâ¸öÖ÷ÒªÊÇÕë¶ÔZone×öÁËһЩ·ÖÀà´¦Àí£¬¾ÍÊǽ«Ô­À´ÊôÓÚͬһ·þÎñµÄ·þÎñʵÀý£¬ÔÙ¸ù¾ÝµØÇø½øÐл®·Ö¡£ÕâÒ²ÊÇΪÁËÄܹ»¿ìËÙÏìÓ¦¶øÉèÖõġ£ + +**3.5.1 DynamicServerListLoadBalancer** + +> ZoneAwareLoadBalancerµÄ¸¸Àà + +Õâ¸öÀà°´ÕÕÃû³ÆÀ´Ëµ¾ÍÊǶ¯Ì¬¼ÓÔØ·þÎñÁбíʹÓõġ£ÆäÖÐÓм¸¸ö±È½ÏÖØÒªµÄ·½·¨ + +1. updateListOfServers:·þÎñÁÐ±í²¢¸üб¾µØ»º´æµÄ·þÎñÁбí +2. enableAndInitLearnNewServersFeature:¿ªÆô·þÎñÁбí¸üж¨Ê±ÈÎÎñ +3. ¿ªÆô¼àÌý:@Monitor + +DynamicServerListLoadBalancerµÄºËÐľÍÊÇ»ñÈ¡·þÎñÁÐ±í£¬ÔÚEurekaÖÐĬÈÏÊÇͨ¹ýDomainExtractingServerListÀ´»ñÈ¡£¬Õâ¸öÀàÊÇÔÚorg.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration#ribbonServerList£¬ÕâÀïûÓм¯³ÉEureka£¬ÔÝʱ²»½² + +**3.5.2 BaseLoadBalancer** + +> DynamicServerListLoadBalancerµÄ¸¸Àà + +ºËÐÄĬÈÏÖµ + +* ĬÈϵĸºÔؾùºâ²ßÂÔRoundRobinRule +* ĬÈϵÄPing²ßÂÔSerialPingStrategy +* ËùÓзþÎñʵÀýÈÝÆ÷£ºallServerList +* ÔÚÏß·þÎñʵÀýÈÝÆ÷£ºupServerList + ![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/b2db0bc66c3045288a762907ff72e01883bf20.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")´Ó×ÓÀ๹ÔìÖн«¶ÔÓ¦µÄ¸ºÔؾùºâ¹æÔò£¬ping²ßÂÔ£¬pingµÈ´«µÝ¹ýÀ´ + +**ping×öÁËЩʲô£¿** + +PingTask×÷Ϊһ¸öÏß³ÌÈÎÎñ£¬¾ÍÊǶ¨ÆÚ¼ì²é·þÎñÊÇ·ñ¶¼´æ»î£¬¸úServerListUpdater·þÎñ¸üлúÖÆ²»³åÍ»¡£ÕâÊÇribbon×Ô¼ºÎ¬»¤µÄÒ»Ì×·þÎñ¼ì²â»úÖÆ£¬Ö÷ÒªÊÇΪÁ˽µµÍ·ÃÎÊʧ°ÜµÄ¸ÅÂÊ¡£Ä¬ÈÏÔÚʹÓÃeurekaʱ£¬pingÊÇʹÓõÄÊÇNIWSDiscoveryPingÀ´Íê³É·þÎñ±£»î¼ì²â¡£ÓÉeureka ºÍ ServerListUpdaterÀ´Ë¢Ð·þÎñÁÐ±í¡£![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/27db4c518aa7c100d427761832a9fa19fa8f04.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÕâÀïÓиö³£ÓõĶ¨Ê±ÈÎÎñ¿ìËÙÍ˳öµÄ·½·¨£¬ÎÒ¾õµÃÔÚÎÒÃÇ×Ô¼ºÐ´µÄʱºòÒ²¿ÉÒÔʹÓᣠ+ +¾ÍÊÇÔÚͬһ¸ö¶¨Ê±ÈÎÎñÈç¹ûÖ´ÐÐʱ¼ä³¬¹ýÁ˶¨Ê±ÖÜÆÚ£¬ÄÇôÏÂÒ»¸ö¶¨Ê±ÈÎÎñ·¢ÏÖÉÏÒ»¸ö¶¨Ê±ÈÎÎñ»¹Ã»ÓÐÖ´ÐÐÍêʱ£¬¾ÍÏÈÈ¡Ïû¡£![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/f2d669b11f17530d2e62603d45949e934a5931.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÕâÀïÒ²ÓÃÁ˺ܶàËø»úÖÆ£¬±ÈÈçËùÓзþÎñʵÀýµ½Ò»¸öеĶÔÏóʱʹÓõÄÊǶÁËø£¬¾ÍÊǸæËßallServersÏÖÔÚÖ»ÄܶÁ²»ÄÜд¡£![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/711c81e67b2c1cd3fb91005f53ea1e1352ad9a.png "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÔÚ·¢ËÍpingºó£¬½«¼ì²âͨ¹ýµÄ·þÎñ·ÅÈënewUpListÖУ¬×îºóͨ¹ýÐ´Ëø£¬½«upServerListËø×¡¡£ + +ÕâÀï¾ÍÊÇÖ»ÄÜÓÐÒ»¸öд£¬ÇÒ²»ÄܶÁ¡£![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/047543857174aa1130b0747d1d149ffd6d5632.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")ÉÏÃæÊÇpingÔÚ¼ì²â¹ý³ÌÖйØÓÚ¶ÁÐ´ËøºÍÔ­×ÓÀàµÄʹÓᣠ+ +Ö÷ÒªÁ÷³Ì¾ÍÊÇ£º + +1. ¶Áȡȫ²¿·þÎñʵÀýÁбí +2. ¼ì²â·þÎñʵÀýÊÇ·ñ´æ»îpingServers +3. ½«·þÎñ״̬·¢Éú¸Ä±äµÄ·ÅÈëchangedServers +4. ½«·þÎñÔÚÏߵķÅÈënewUpList +5. ½«newUpList¸³Öµµ½upServerList ÔÚÏß·þÎñʵÀýÁбíÖÐ + +ÕâÀïÃæpingServers¾ÍÊǼì²éÐÄÌøµÄ + +**BaseLoadBalancerµÄÆäËû¹¦ÄܼòÊö** + +1. ¶ÔallServerListºÍupServerListµÄ¶ÁÐ´Ëø·½·¨ +2. Ìṩ¶ÔallServerListºÍupServerListµÄÔöɾ¸Ä¹¦ÄÜ +3. ÌṩÁËPingTask£¨pingµÄ¶¨Ê±ÈÎÎñ£©£¬Pinger£¨pingµÄÖ´ÐÐÆ÷£© +4. »ùÓÚ¸ºÔؾùºâ²ßÂÔÑ¡Ôñ·þÎñrule.choose(key) +5. ÌṩĬÈϵÄping²ßÂÔSerialPingStrategy + **3.6 LoadBalancerRequest** + ͨ¹ýZoneAwareLoadBalancerÑ¡Ôñ¾ßÌåµÄServerÖ®ºó£¬ÔÙ°ü×°³ÉRibbonServer¶ÔÏó£¬Ö®Ç°·µ»ØµÄserverÊǸöÔÏóÖеÄÒ»¸ö×ֶΣ¬³ý´ËÖ®Í⣬»¹ÓзþÎñÃûserviceId£¬ÊÇ·ñÐèҪʹÓÃhttpsµÈÐÅÏ¢¡£×îºó£¬Í¨¹ýLoadBalancerRequestµÄapply·½·¨£¬Ïò¾ßÌåµÄserver·¢ÇëÇ󣬴ӶøÊµÏÖÁ˸ºÔؾùºâ¡£ + +ÏÂÃæÊÇapply·½·¨µÄ¶¨Ò壺 + + + +``` +public interface LoadBalancerRequest { + T apply(ServiceInstance instance) throws Exception; +} +``` + + + + + + + + + +ÔÚÇëÇóʱ£¬´«ÈëµÄribbonServer¶ÔÏ󣬱»µ±³ÉServiceInstanceÀàÐ͵ĶÔÏó½øÐнÓÊÕ¡£ServiceInstanceÊÇÒ»¸ö½Ó¿Ú£¬¶¨ÒåÁË·þÎñÖÎÀíϵͳÖУ¬Ã¿¸öʵÀýÐèÒªÌṩµÄÐÅÏ¢£¬±ÈÈçserviceId£¬host£¬portµÈ¡£ + +LoadBalancerRequestÊÇÒ»¸ö½Ó¿Ú£¬×îÖÕ»áͨ¹ýʵÏÖÀàµÄapply·½·¨È¥Ö´ÐУ¬ÊµÏÖÀàÊÇÔÚLoadBalancerInterceptorÖе÷ÓÃRibbonLoadBalancerClientµÄexecute·½·¨Ê±£¬´«½øÀ´µÄÒ»¸öÄäÃûÀ࣬¿ÉÒÔͨ¹ý²é¿´LoadBalancerInterceptorµÄ´úÂë¿´µ½¡£ + +´´½¨LoadBalancerRequestÄäÃûÀàµÄʱºò£¬¾ÍÖØÐ´ÁËapply·½·¨£¬apply·½·¨ÖУ¬»¹Ð½¨ÁËÒ»¸öServiceRequestWrapperµÄÄÚ²¿À࣬Õâ¸öÀàÖУ¬¾ÍÖØÐ´ÁËgetURI·½·¨£¬getURI·½·¨»áµ÷ÓÃloadBalancerµÄreconstructURI·½·¨À´¹¹½¨uri¡£ + +¿´µ½ÕâÀÒѾ­¿ÉÒÔ´óÌåÖªµÀRibbonʵÏÖ¸ºÔؾùºâµÄÁ÷³ÌÁË£¬ÎÒÃÇÔÚRestTemplateÉÏÌí¼Ó×¢½â£¬¾Í»áÓÐLoadBalancerClientµÄ¶ÔÏóÀ´ÅäÖÃËü£¬Ò²¾ÍÊÇRibbonLoadBalancerClient¡£Í¬Ê±£¬ +LoadBalancerAutoConfiguration»á½øÐÐÅäÖ㬴´½¨Ò»¸öLoadBalancerInterceptor£¬²¢ÇÒÄõ½ÎÒÃÇÉùÃ÷µÄËùÓÐrestTemplate£¬ÔÚÕâЩrestTemplateÖÐÌí¼ÓLoadBalancerInterceptorÀ¹½ØÆ÷¡£ + +µ±Í¨¹ýrestTemplate·¢ËÍÇëÇóʱ£¬¾Í»á¾­¹ýÕâ¸öÀ¹½ØÆ÷£¬ÔÚÀ¹½ØÆ÷ÖУ¬¾Í»áµ÷ÓÃRibbonLoadBalancerClientÖеķ½·¨£¬»ñÈ¡µ½¸ù¾Ý·þÎñÃû£¬Í¨¹ý¸ºÔؾùºâ·½·¨»ñÈ¡µ½·þÎñʵÀý£¬È»ºóÈ¥ÇëÇóÕâ¸öʵÀý¡£ + +**3.7 »ñÈ¡·þÎñÁбí** +ÉÏÃæËµµÄÕâЩ£¬ÊÇÈçºÎ¶ÔÇëÇó½øÐиºÔؾùºâµÄ£¬µ«ÊÇ»¹ÓиöÎÊÌ⣬ÎÒÃÇÇëÇóµÄʵÀý£¬ÊÇ´ÓEureka ServerÉÏ»ñÈ¡µ½µÄ£¬ÄÇÕâ¸öʵÀýÁбíÊÇÈçºÎ»ñÈ¡µÄÄØ£¿Ôõô±£Ö¤Õâ¸öʵÀýÁбíÖеÄʵÀýÊÇ¿ÉÓõÄÄØ£¿ + +ÔÚRibbonLoadBalancerClientÑ¡ÔñʵÀýµÄʱºò£¬ÊÇͨ¹ýILoadBalancerµÄʵÏÖÀà¸ù¾Ý¸ºÔؾùºâË㷨ѡÔñ·þÎñʵÀýµÄ£¬Ò²¾ÍÊÇZoneAwareLoadBalancerµÄchooseServerÖеÄÂß¼­£¬ÄǾÍÔÚÕâÀïÕÒÏßË÷¡£²é¿´ZoneAwareLoadBalancerµÄ¼Ì³Ð¹ØÏµ£¬¿ÉÒÔ¿´µ½ÈçÏÂͼËùʾ¡£![SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/425d8ff567d9ac6171f6036dab1726a7da04a3.jpg "SpringCloudϵÁСªRibbonÔ´Âë·ÖÎö-¿ªÔ´»ù´¡Èí¼þÉçÇø")¿ÉÒÔ¿´µ½£¬×îÉÏÃæÊÇILoadBalancer½Ó¿Ú£¬AbstractLoadBalancerÀà¼Ì³ÐÁËÕâ¸ö½Ó¿Ú£¬BaseLoadBalancer¼Ì³ÐÁËAbstractLoadBalancerÀ࣬ +DynamicServerListLoadBalancer¼Ì³ÐÁËBaseLoadBalancer£¬ZoneAwareLoadBalancer¼Ì³ÐÁËDynamicServerListLoadBalancer¡£ + +ILoadBalancer½Ó¿ÚµÄ´úÂëÒѾ­¿´¹ýÁË£¬ÏÖÔÚ¿´ÏÂAbstractLoadBalancerµÄ´úÂ룺 + + + +``` +public abstract class AbstractLoadBalancer implements ILoadBalancer { + public enum ServerGroup{ + ALL, + STATUS_UP, + STATUS_NOT_UP + } + /** + * delegate to {@link #chooseServer(Object)} with parameter null. + */ + public Server chooseServer() { + return chooseServer(null); + } + /** + * List of servers that this Loadbalancer knows about + * + * @param serverGroup Servers grouped by status, e.g., {@link ServerGroup#STATUS_UP} + */ + public abstract List getServerList(ServerGroup serverGroup); + /** + * Obtain LoadBalancer related Statistics + */ + public abstract LoadBalancerStats getLoadBalancerStats(); +} +``` + + + + + + + + + +ÕâÊÇÒ»¸ö³éÏóÀ࣬ÀïÃæ¼ÓÁËÒ»¸öö¾Ù£¬Ôö¼ÓÁËÁ½¸ö³éÏó·½·¨¡£¶¨ÒåµÄchooseServer·½·¨¡£ + +ÏÂÃæÔÙ¿´BaseLoadBalancerÀ࣬BaseLoadBalancerÀà¾ÍËãÊǸºÔؾùºâÆ÷µÄÒ»¸ö»ù´¡ÊµÏÖÀ࣬ÔÚÀïÃæ¿ÉÒÔ¿´µ½¶¨ÒåÁËÁ½¸ölist£º + + + +``` +@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL) +protected volatile List allServerList = Collections + .synchronizedList(new ArrayList()); +@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL) +protected volatile List upServerList = Collections + .synchronizedList(new ArrayList()); +``` + + + + + + + + + +´ÓÃû×ÖÉÏ¿´£¬Õâ¾ÍÊÇά»¤ËùÓзþÎñµÄʵÀýÁÐ±í£¬ºÍά»¤×´Ì¬ÎªupµÄʵÀýÁÐ±í¡£ +¶øÇÒ»¹¿ÉÒÔ¿´µ½BaseLoadBalancerÖÐʵÏÖµÄILoadBalancer½Ó¿ÚÖеķ½·¨£¬±ÈÈçÏÂÃæÕâÁ½¸ö£¬»ñÈ¡¿ÉÓõķþÎñÁÐ±í£¬¾Í»á°ÑupServerList·µ»Ø£¬»ñÈ¡ËùÓеķþÎñÁÐ±í£¬¾Í»á°ÑallServerList·µ»Ø¡£ + + + +``` +@Override +public List getReachableServers() { + return Collections.unmodifiableList(upServerList); +} +@Override +public List getAllServers() { + return Collections.unmodifiableList(allServerList); +} +``` + + + + + + + + + +½ÓÏÂÀ´£¬ÔÙ¿´DynamicServerListLoadBalancerÀà¡£´ÓÀàÍ·ÉϵÄ×¢ÊÍ¿ÉÒÔÖªµÀ£¬Õâ¸öÀà¿ÉÒÔ¶¯Ì¬µÄ»ñÈ¡·þÎñÁÐ±í£¬²¢ÇÒÀûÓÃfilter¶Ô·þÎñÁÐ±í½øÐйýÂË¡£ + +ÔÚDynamicServerListLoadBalancerÀàÖУ¬ÄÜ¿´µ½¶¨ÒåÁËÒ»¸öServerListÀàÐ͵ÄserverListImpl×ֶΣ¬ServerListÊÇÒ»¸ö½Ó¿Ú£¬ÀïÃæÓÐÁ½¸ö·½·¨£º + + + +``` +public interface ServerList { + public List getInitialListOfServers(); + /** + * Return updated list of servers. This is called say every 30 secs + * (configurable) by the Loadbalancer's Ping cycle + * + */ + public List getUpdatedListOfServers(); +} +``` + + + + + + + + + +getInitialListOfServersÊÇ»ñÈ¡³õʼ»¯µÄ·þÎñÁÐ±í¡£ +getUpdatedListOfServersÊÇ»ñÈ¡¸üеķþÎñÁÐ±í¡£ +ServerListÓжà¸öʵÏÖÀ࣬¾ßÌåÓõÄÄĸöÄØ£¬¿ÉÒÔÔÚ +EurekaRibbonClientConfigurationÀàÖÐÕÒµ½£¬ÕâÊÇRibbonºÍEureka½áºÏµÄ×Ô¶¯ÅäÖÃÀ࣬µ«ÊÇĿǰÎÒÃÇûÓÐÕûºÏEureka£¬ÊÇͨ¹ýÅäÖÃÎļþÅäÖã¬ËùÒÔ»á×ßConfigurationBasedServerListÀà¡£ + +# ²Î¿¼ÎÄÕ +https://lijunyi.xyz/docs/SpringCloud/SpringCloud.html#_2-2-x-%E5%88%86%E6%94%AF +https://mp.weixin.qq.com/s/2jeovmj77O9Ux96v3A0NtA +https://juejin.cn/post/6931922457741770760 +https://github.com/D2C-Cai/herring +http://c.biancheng.net/springcloud +https://github.com/macrozheng/springcloud-learning \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\233\275\351\231\205\345\214\226\345\212\237\350\203\275.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\233\275\351\231\205\345\214\226\345\212\237\350\203\275.md" new file mode 100644 index 0000000..374388b --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\233\275\351\231\205\345\214\226\345\212\237\350\203\275.md" @@ -0,0 +1,179 @@ +#### ¹ú¼Ê»¯ + +* * * + +ÔÚ¿ª·¢Ó¦ÓóÌÐòµÄʱºò£¬¾­³£»áÓöµ½Ö§³Ö¶àÓïÑÔµÄÐèÇó£¬ÕâÖÖÖ§³Ö¶àÓïÑԵŦÄܳÆÖ®Îª¹ú¼Ê»¯£¬Ó¢ÎÄÊÇinternationalization£¬ËõдΪi18n£¨ÒòΪÊ××ÖĸiºÍÄ©×ÖĸnÖмäÓÐ18¸ö×Öĸ£©¡£ + +»¹ÓÐÕë¶ÔÌØ¶¨µØÇøµÄ±¾µØ»¯¹¦ÄÜ£¬Ó¢ÎÄÊÇlocalization£¬ËõдΪL10n£¬±¾µØ»¯ÊÇÖ¸¸ù¾ÝµØÇøµ÷ÕûÀàËÆÐÕÃû¡¢ÈÕÆÚµÄÏÔʾµÈ¡£ + +Ò²ÓаÑÉÏÃæÁ½ÕߺϳÆÎªÈ«Çò»¯£¬Ó¢ÎÄÊÇglobalization£¬ËõдΪg11n¡£ + +ÔÚJavaÖУ¬Ö§³Ö¶àÓïÑԺͱ¾µØ»¯ÊÇͨ¹ý`MessageFormat`ÅäºÏ`Locale`ʵÏֵģº + + +¶ÔÓÚWebÓ¦ÓóÌÐò£¬ÒªÊµÏÖ¹ú¼Ê»¯¹¦ÄÜ£¬Ö÷ÒªÊÇäÖȾViewµÄʱºò£¬Òª°Ñ¸÷ÖÖÓïÑÔµÄ×ÊÔ´ÎļþÌá³öÀ´£¬ÕâÑù£¬²»Í¬µÄÓû§·ÃÎÊͬһ¸öÒ³ÃæÊ±£¬ÏÔʾµÄÓïÑÔ¾ÍÊDz»Í¬µÄ¡£ + +ÎÒÃÇÀ´¿´¿´ÔÚSpring MVCÓ¦ÓóÌÐòÖÐÈçºÎʵÏÖ¹ú¼Ê»¯¡£ + +### »ñÈ¡Locale + +ʵÏÖ¹ú¼Ê»¯µÄµÚÒ»²½ÊÇ»ñÈ¡µ½Óû§µÄ`Locale`¡£ÔÚWebÓ¦ÓóÌÐòÖУ¬HTTP¹æ·¶¹æ¶¨ÁËä¯ÀÀÆ÷»áÔÚÇëÇóÖÐЯ´ø`Accept-Language`Í·£¬ÓÃÀ´Ö¸Ê¾Óû§ä¯ÀÀÆ÷É趨µÄÓïÑÔ˳Ðò£¬È磺 + +``` +Accept-Language: zh-CN,zh;q=0.8,en;q=0.2 + +``` + +ÉÏÊöHTTPÇëÇóÍ·±íʾÓÅÏÈÑ¡Ôñ¼òÌåÖÐÎÄ£¬Æä´ÎÑ¡ÔñÖÐÎÄ£¬×îºóÑ¡ÔñÓ¢ÎÄ¡£`q`±íÊ¾È¨ÖØ£¬½âÎöºóÎÒÃǿɻñµÃÒ»¸ö¸ù¾ÝÓÅÏȼ¶ÅÅÐòµÄÓïÑÔÁÐ±í£¬°ÑËüת»»ÎªJavaµÄ`Locale`£¬¼´»ñµÃÁËÓû§µÄ`Locale`¡£´ó¶àÊý¿ò¼Üͨ³£Ö»·µ»ØÈ¨ÖØ×î¸ßµÄ`Locale`¡£ + +Spring MVCͨ¹ý`LocaleResolver`À´×Ô¶¯´Ó`HttpServletRequest`ÖлñÈ¡`Locale`¡£ÓжàÖÖ`LocaleResolver`µÄʵÏÖÀ࣬ÆäÖÐ×î³£ÓõÄÊÇ`CookieLocaleResolver`£º + +``` +@Primary +@Bean +LocaleResolver createLocaleResolver() { + var clr = new CookieLocaleResolver(); clr.setDefaultLocale(Locale.ENGLISH); clr.setDefaultTimeZone(TimeZone.getDefault()); return clr;} + +``` + +`CookieLocaleResolver`´Ó`HttpServletRequest`ÖлñÈ¡`Locale`ʱ£¬Ê×Ïȸù¾ÝÒ»¸öÌØ¶¨µÄCookieÅжÏÊÇ·ñÖ¸¶¨ÁË`Locale`£¬Èç¹ûûÓУ¬¾Í´ÓHTTPÍ·»ñÈ¡£¬Èç¹û»¹Ã»ÓУ¬¾Í·µ»ØÄ¬ÈϵÄ`Locale`¡£ + +µ±Óû§µÚÒ»´Î·ÃÎÊÍøÕ¾Ê±£¬`CookieLocaleResolver`Ö»ÄÜ´ÓHTTPÍ·»ñÈ¡`Locale`£¬¼´Ê¹ÓÃä¯ÀÀÆ÷µÄĬÈÏÓïÑÔ¡£Í¨³£ÍøÕ¾Ò²ÔÊÐíÓû§×Ô¼ºÑ¡ÔñÓïÑÔ£¬´Ëʱ£¬`CookieLocaleResolver`¾Í»á°ÑÓû§Ñ¡ÔñµÄÓïÑÔ´æ·Åµ½CookieÖУ¬ÏÂÒ»´Î·ÃÎÊʱ£¬¾Í»á·µ»ØÓû§ÉÏ´ÎÑ¡ÔñµÄÓïÑÔ¶ø²»ÊÇä¯ÀÀÆ÷ĬÈÏÓïÑÔ¡£ + +### ÌáÈ¡×ÊÔ´Îļþ + +µÚ¶þ²½ÊǰÑдËÀÔÚÄ£°åÖеÄ×Ö·û´®ÒÔ×ÊÔ´ÎļþµÄ·½Ê½´æ´¢ÔÚÍⲿ¡£¶ÔÓÚ¶àÓïÑÔ£¬Ö÷ÎļþÃûÈç¹ûÃüÃûΪ`messages`£¬ÄÇô×ÊÔ´Îļþ±ØÐë°´ÈçÏ·½Ê½ÃüÃû²¢·ÅÈëclasspathÖУº + +* ĬÈÏÓïÑÔ£¬ÎļþÃû±ØÐëΪ`messages.properties`£» +* ¼òÌåÖÐÎÄ£¬LocaleÊÇ`zh_CN`£¬ÎļþÃû±ØÐëΪ`messages_zh_CN.properties`£» +* ÈÕÎÄ£¬LocaleÊÇ`ja_JP`£¬ÎļþÃû±ØÐëΪ`messages_ja_JP.properties`£» +* ÆäËü¸ü¶àÓïÑÔ¡­¡­ + +ÿ¸ö×ÊÔ´Îļþ¶¼ÓÐÏàͬµÄkey£¬ÀýÈ磬ĬÈÏÓïÑÔÊÇÓ¢ÎÄ£¬Îļþ`messages.properties`ÄÚÈÝÈçÏ£º + +``` +language.select=Language +home=Home +signin=Sign In +copyright=Copyright?{0,number,#} + +``` + +Îļþ`messages_zh_CN.properties`ÄÚÈÝÈçÏ£º + +``` +language.select=ÓïÑÔ +home=Ê×Ò³ +signin=µÇ¼ +copyright=°æÈ¨ËùÓÐ?{0,number,#} + +``` + +### ´´½¨MessageSource + +µÚÈý²½ÊÇ´´½¨Ò»¸öSpringÌṩµÄ`MessageSource`ʵÀý£¬Ëü×Ô¶¯¶ÁÈ¡ËùÓеÄ`.properties`Îļþ£¬²¢Ìṩһ¸öͳһ½Ó¿ÚÀ´ÊµÏÖ¡°·­Ò롱£º + +``` +// code, arguments, locale: +String text = messageSource.getMessage("signin", null, locale); + +``` + +ÆäÖУ¬`signin`ÊÇÎÒÃÇÔÚ`.properties`ÎļþÖж¨ÒåµÄkey£¬µÚ¶þ¸ö²ÎÊýÊÇ`Object[]`Êý×é×÷Ϊ¸ñʽ»¯Ê±´«ÈëµÄ²ÎÊý£¬×îºóÒ»¸ö²ÎÊý¾ÍÊÇ»ñÈ¡µÄÓû§`Locale`ʵÀý¡£ + +´´½¨`MessageSource`ÈçÏ£º + +``` +@Bean("i18n") +MessageSource createMessageSource() { + var messageSource = new ResourceBundleMessageSource(); // Ö¸¶¨ÎļþÊÇUTF-8±àÂë: + messageSource.setDefaultEncoding("UTF-8"); // Ö¸¶¨Ö÷ÎļþÃû: + messageSource.setBasename("messages"); return messageSource;} + +``` + +×¢Òâµ½`ResourceBundleMessageSource`»á×Ô¶¯¸ù¾ÝÖ÷ÎļþÃû×Ô¶¯°ÑËùÓÐÏà¹ØÓïÑÔµÄ×ÊÔ´Îļþ¶¼¶Á½øÀ´¡£ + +ÔÙ×¢Òâµ½SpringÈÝÆ÷»á´´½¨²»Ö»Ò»¸ö`MessageSource`ʵÀý£¬ÎÒÃÇ×Ô¼º´´½¨µÄÕâ¸ö`MessageSource`ÊÇרßøÒ³Ãæ¹ú¼Ê»¯Ê¹Óõģ¬Òò´ËÃüÃûΪ`i18n`£¬²»»áÓëÆäËü`MessageSource`ʵÀý³åÍ»¡£ + +### ʵÏÖ¶àÓïÑÔ + +ÒªÔÚViewÖÐʹÓÃ`MessageSource`¼ÓÉÏ`Locale`Êä³ö¶àÓïÑÔ£¬ÎÒÃÇͨ¹ý±àдһ¸ö`MvcInterceptor`£¬°ÑÏà¹Ø×ÊÔ´×¢Èëµ½`ModelAndView`ÖУº + +``` +@Component +public class MvcInterceptor implements HandlerInterceptor { + @Autowired LocaleResolver localeResolver; + // ×¢Òâ×¢ÈëµÄMessageSourceÃû³ÆÊÇi18n: + @Autowired @Qualifier("i18n") MessageSource messageSource; + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (modelAndView != null) { // ½âÎöÓû§µÄLocale: + Locale locale = localeResolver.resolveLocale(request); // ·ÅÈëModel: + modelAndView.addObject("__messageSource__", messageSource); modelAndView.addObject("__locale__", locale); } }} + +``` + +²»ÒªÍüÁËÔÚ`WebMvcConfigurer`ÖÐ×¢²á`MvcInterceptor`¡£ÏÖÔÚ£¬¾Í¿ÉÒÔÔÚViewÖе÷ÓÃ`MessageSource.getMessage()`·½·¨À´ÊµÏÖ¶àÓïÑÔ£º + +``` +{{ __messageSource__.getMessage('signin', null, __locale__) }} + +``` + +ÉÏÊöÕâÖÖд·¨ËäÈ»¿ÉÐУ¬µ«¸ñʽ̫¸´ÔÓÁË¡£Ê¹ÓÃViewʱ£¬Òª¸ù¾Ýÿ¸öÌØ¶¨µÄViewÒýÇæ¶¨Öƹú¼Ê»¯º¯Êý¡£ÔÚPebbleÖУ¬ÎÒÃÇ¿ÉÒÔ·â×°Ò»¸ö¹ú¼Ê»¯º¯Êý£¬Ãû³Æ¾ÍÊÇÏ»®Ïß`_`£¬¸ÄÔìһϴ´½¨`ViewResolver`µÄ´úÂ룺 + +``` +@Bean +ViewResolver createViewResolver(@Autowired ServletContext servletContext, @Autowired @Qualifier("i18n") MessageSource messageSource) { + var engine = new PebbleEngine.Builder() .autoEscaping(true) .cacheActive(false) .loader(new Servlet5Loader(servletContext)) // Ìí¼ÓÀ©Õ¹: + .extension(createExtension(messageSource)) .build(); var viewResolver = new PebbleViewResolver(); viewResolver.setPrefix("/WEB-INF/templates/"); viewResolver.setSuffix(""); viewResolver.setPebbleEngine(engine); return viewResolver;} + +private Extension createExtension(MessageSource messageSource) { + return new AbstractExtension() { @Override public Map getFunctions() { return Map.of("_", new Function() { public Object execute(Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) { String key = (String) args.get("0"); List arguments = this.extractArguments(args); Locale locale = (Locale) context.getVariable("__locale__"); return messageSource.getMessage(key, arguments.toArray(), "???" + key + "???", locale); } private List extractArguments(Map args) { int i = 1; List arguments = new ArrayList<>(); while (args.containsKey(String.valueOf(i))) { Object param = args.get(String.valueOf(i)); arguments.add(param); i++; } return arguments; } public List getArgumentNames() { return null; } }); } };} + +``` + +ÕâÑù£¬ÎÒÃÇ¿ÉÒ԰ѶàÓïÑÔÒ³Ãæ¸ÄдΪ£º + +``` +{{ _('signin') }} + +``` + +Èç¹ûÊÇ´ø²ÎÊýµÄ¶àÓïÑÔ£¬ÐèÒª°Ñ²ÎÊý´«½øÈ¥£º + +``` +
{{ _('copyright', 2020) }}
+ +``` + +ʹÓÃÆäËüViewÒýÇæÊ±£¬Ò²Ó¦µ±¸ù¾ÝÒýÇæ½Ó¿ÚʵÏÖ¸ü·½±ãµÄÓï·¨¡£ + +### Çл»Locale + +×îºó£¬ÎÒÃÇÐèÒªÔÊÐíÓû§ÊÖ¶¯Çл»`Locale`£¬±àдһ¸ö`LocaleController`À´ÊµÏָù¦ÄÜ£º + +``` +@Controller +public class LocaleController { + final Logger logger = LoggerFactory.getLogger(getClass()); + @Autowired LocaleResolver localeResolver; + @GetMapping("/locale/{lo}") public String setLocale(@PathVariable("lo") String lo, HttpServletRequest request, HttpServletResponse response) { // ¸ù¾Ý´«ÈëµÄlo´´½¨LocaleʵÀý: + Locale locale = null; int pos = lo.indexOf('_'); if (pos > 0) { String lang = lo.substring(0, pos); String country = lo.substring(pos + 1); locale = new Locale(lang, country); } else { locale = new Locale(lo); } // É趨´ËLocale: + localeResolver.setLocale(request, response, locale); logger.info("locale is set to {}.", locale); // Ë¢ÐÂÒ³Ãæ: + String referer = request.getHeader("Referer"); return "redirect:" + (referer == null ? "/" : referer); }} + +``` + +ÔÚÒ³ÃæÉè¼ÆÖУ¬Í¨³£ÔÚÓÒÉϽǸøÓû§Ìṩһ¸öÓïÑÔÑ¡ÔñÁÐ±í£¬À´¿´¿´Ð§¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416194544.png) + +Çл»µ½ÖÐÎÄ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230416194602.png) + +### С½á + +¶àÓïÑÔÖ§³ÖÐèÒª´ÓHTTPÇëÇóÖнâÎöÓû§µÄLocale£¬È»ºóÕë¶Ô²»Í¬LocaleÏÔʾ²»Í¬µÄÓïÑÔ£» + +Spring MVCÓ¦ÓóÌÐòͨ¹ý`MessageSource`ºÍ`LocaleResolver`£¬ÅäºÏViewʵÏÖ¹ú¼Ê»¯¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\270\270\347\224\250\345\212\237\350\203\275.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\270\270\347\224\250\345\212\237\350\203\275.md" new file mode 100644 index 0000000..6b1c4bb --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\270\270\347\224\250\345\212\237\350\203\275.md" @@ -0,0 +1,594 @@ + + + + +# Spring MVC ʹÓÃ@Controller×¢½â¶¨ÒåÒ»¸ö¿ØÖÆÆ÷ + + + +2018-07-26 14:02 ¸üР+ + + + + + + +
+ +> [Original] The `@Controller` annotation indicates that a particular class serves the role of a controller. Spring does not require you to extend any controller base class or reference the Servlet API. However, you can still reference Servlet-specific features if you need to. + +`@Controller`×¢½â±íÃ÷ÁËÒ»¸öÀàÊÇ×÷Ϊ¿ØÖÆÆ÷µÄ½ÇÉ«¶ø´æÔڵġ£Spring²»ÒªÇóÄãÈ¥¼Ì³ÐÈκοØÖÆÆ÷»ùÀ࣬Ҳ²»ÒªÇóÄãȥʵÏÖServletµÄÄÇÌ×API¡£µ±È»£¬Èç¹ûÄãÐèÒªµÄ»°Ò²¿ÉÒÔȥʹÓÃÈκÎÓëServletÏà¹ØµÄÌØÐÔºÍÉèÊ©¡£ + +> [Original] The `@Controller` annotation acts as a stereotype for the annotated class, indicating its role. The dispatcher scans such annotated classes for mapped methods and detects `@RequestMapping` annotations (see the next section). + +`@Controller`×¢½â¿ÉÒÔÈÏΪÊDZ»±ê×¢ÀàµÄÔ­ÐÍ£¨stereotype£©£¬±íÃ÷ÁËÕâ¸öÀàËù³Ðµ£µÄ½ÇÉ«¡£·ÖÅÉÆ÷£¨`DispatcherServlet`£©»áɨÃèËùÓÐ×¢½âÁË`@Controller`µÄÀ࣬¼ì²âÆäÖÐͨ¹ý`@RequestMapping`×¢½âÅäÖõķ½·¨£¨Ïê¼ûÏÂһС½Ú£©¡£ + +> [Original] You can define annotated controller beans explicitly, using a standard Spring bean definition in the dispatcher¡¯s context. However, the `@Controller` stereotype also allows for autodetection, aligned with Spring general support for detecting component classes in the classpath and auto-registering bean definitions for them. + +µ±È»£¬ÄãÒ²¿ÉÒÔ²»Ê¹ÓÃ`@Controller`×¢½â¶øÏÔʽµØÈ¥¶¨Òå±»×¢½âµÄbean£¬Õâµãͨ¹ý±ê×¼µÄSpring beanµÄ¶¨Ò巽ʽ£¬ÔÚdispatherµÄÉÏÏÂÎÄÊôÐÔÏÂÅäÖü´¿É×öµ½¡£µ«ÊÇ`@Controller`Ô­ÐÍÊÇ¿ÉÒÔ±»¿ò¼Ü×Ô¶¯¼ì²âµÄ£¬SpringÖ§³Öclasspath·¾¶ÏÂ×é¼þÀàµÄ×Ô¶¯¼ì²â£¬ÒÔ¼°¶ÔÒѶ¨ÒåbeanµÄ×Ô¶¯×¢²á¡£ + +> [Original] To enable autodetection of such annotated controllers, you add component scanning to your configuration. Use the spring-context schema as shown in the following XML snippet: + +ÄãÐèÒªÔÚÅäÖÃÖмÓÈë×é¼þɨÃèµÄÅäÖôúÂëÀ´¿ªÆô¿ò¼Ü¶Ô×¢½â¿ØÖÆÆ÷µÄ×Ô¶¯¼ì²â¡£ÇëʹÓÃÏÂÃæXML´úÂëËùʾµÄspring-context schema£º + +``` + + + + + + + + +``` + +
+ + + + + + + +# Spring MVC ʹÓÃ@RequestMapping×¢½âÓ³ÉäÇëÇó·¾¶ + + + +2018-07-26 15:29 ¸üР+ + + + + + + +Äã¿ÉÒÔʹÓÃ`@RequestMapping`×¢½âÀ´½«ÇëÇóURL£¬Èç`/appointments`µÈ£¬Ó³Éäµ½Õû¸öÀàÉÏ»òij¸öÌØ¶¨µÄ´¦ÀíÆ÷·½·¨ÉÏ¡£Ò»°ãÀ´Ëµ£¬À༶±ðµÄ×¢½â¸ºÔð½«Ò»¸öÌØ¶¨£¨»ò·ûºÏijÖÖģʽ£©µÄÇëÇó·¾¶Ó³Éäµ½Ò»¸ö¿ØÖÆÆ÷ÉÏ£¬Í¬Ê±Í¨¹ý·½·¨¼¶±ðµÄ×¢½âÀ´Ï¸»¯Ó³É䣬¼´¸ù¾ÝÌØ¶¨µÄHTTPÇëÇó·½·¨£¨¡°GET¡±¡°POST¡±·½·¨µÈ£©¡¢HTTPÇëÇóÖÐÊÇ·ñЯ´øÌض¨²ÎÊýµÈÌõ¼þ£¬½«ÇëÇóÓ³É䵽ƥÅäµÄ·½·¨ÉÏ¡£ + +
+ +ÏÂÃæÕâ¶Î´úÂëʾÀýÀ´×ÔPetcare£¬ËüչʾÁËÔÚSpring MVCÖÐÈçºÎÔÚ¿ØÖÆÆ÷ÉÏʹÓÃ`@RequestMapping`×¢½â£º + +``` +@Controller +@RequestMapping("/appointments") +public class AppointmentsController { + + private final AppointmentBook appointmentBook; + + @Autowired + public AppointmentsController(AppointmentBook appointmentBook) { + this.appointmentBook = appointmentBook; + } + + @RequestMapping(method = RequestMethod.GET) + public Map get() { + return appointmentBook.getAppointmentsForToday(); + } + + @RequestMapping(path = "/{day}", method = RequestMethod.GET) + public Map getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) { + return appointmentBook.getAppointmentsForDay(day); + } + + @RequestMapping(path = "/new", method = RequestMethod.GET) + public AppointmentForm getNewForm() { + return new AppointmentForm(); + } + + @RequestMapping(method = RequestMethod.POST) + public String add(@Valid AppointmentForm appointment, BindingResult result) { + if (result.hasErrors()) { + return "appointments/new"; + } + appointmentBook.addAppointment(appointment); + return "redirect:/appointments"; + } +} + +``` + +ÔÚÉÏÃæµÄʾÀýÖУ¬Ðí¶àµØ·½¶¼Ê¹Óõ½ÁË`@RequestMapping`×¢½â¡£µÚÒ»´ÎʹÓõãÊÇ×÷ÓÃÓÚÀ༶±ðµÄ£¬ËüָʾÁËËùÓÐ`/appointments`¿ªÍ·µÄ·¾¶¶¼»á±»Ó³Éäµ½¿ØÖÆÆ÷Ï¡£`get()`·½·¨ÉϵÄ`@RequestMapping`×¢½â¶ÔÇëÇó·¾¶½øÐÐÁ˽øÒ»²½Ï¸»¯£ºËü½ö½ÓÊÜGET·½·¨µÄÇëÇó¡£ÕâÑù£¬Ò»¸öÇëÇ󷾶Ϊ`/appointments`¡¢HTTP·½·¨ÎªGETµÄÇëÇ󣬽«»á×îÖÕ½øÈëµ½Õâ¸ö·½·¨±»´¦Àí¡£`add()`·½·¨Ò²×öÁËÀàËÆµÄϸ»¯£¬¶ø`getNewForm()`·½·¨Ôòͬʱע½âÁËÄܹ»½ÓÊܵÄÇëÇóµÄHTTP·½·¨ºÍ·¾¶¡£ÕâÖÖÇé¿öÏ£¬Ò»¸ö·¾¶Îª`appointments/new`¡¢HTTP·½·¨ÎªGETµÄÇëÇ󽫻ᱻÕâ¸ö·½·¨Ëù´¦Àí¡£ + +`getForDay()`·½·¨ÔòչʾÁËʹÓÃ`@RequestMapping`×¢½âµÄÁíÒ»¸ö¼¼ÇÉ£ºURIÄ£°å¡££¨¹ØÓÚURIÄ£°å£¬Çë¼ûÏÂС½Ú£© + +À༶±ðµÄ`@RequestMapping`×¢½â²¢²»ÊDZØÐëµÄ¡£²»ÅäÖõϰÔòËùÓеÄ·¾¶¶¼ÊǾø¶Ô·¾¶£¬¶ø·ÇÏà¶Ô·¾¶¡£ÒÔϵĴúÂëʾÀýÀ´×ÔPetClinic£¬ËüչʾÁËÒ»¸ö¾ßÓжà¸ö´¦ÀíÆ÷·½·¨µÄ¿ØÖÆÆ÷£º + +``` +@Controller +public class ClinicController { + + private final Clinic clinic; + + @Autowired + public ClinicController(Clinic clinic) { + this.clinic = clinic; + } + + @RequestMapping("/") + public void welcomeHandler() { + } + + @RequestMapping("/vets") + public ModelMap vetsHandler() { + return new ModelMap(this.clinic.getVets()); + } +} + +``` + +ÒÔÉÏ´úÂëûÓÐÖ¸¶¨ÇëÇó±ØÐëÊÇGET·½·¨»¹ÊÇPUT/POST»òÆäËû·½·¨£¬`@RequestMapping`×¢½âĬÈÏ»áÓ³ÉäËùÓеÄHTTPÇëÇó·½·¨¡£Èç¹û½öÏë½ÓÊÕijÖÖÇëÇó·½·¨£¬ÇëÔÚ×¢½âÖÐÖ¸¶¨Ö®`@RequestMapping(method=GET)`ÒÔËõС·¶Î§¡£ + +## @ControllerºÍÃæÏòÇÐÃæ£¨AOP£©´úÀí + +ÓÐʱ£¬ÎÒÃÇÏ£ÍûÔÚÔËÐÐʱʹÓÃAOP´úÀíÀ´×°ÊοØÖÆÆ÷£¬±ÈÈçµ±ÄãÖ±½ÓÔÚ¿ØÖÆÆ÷ÉÏʹÓÃ`@Transactional`×¢½âʱ¡£ÕâÖÖÇé¿öÏ£¬ÎÒÃÇÍÆ¼öʹÓÃÀ༶±ð£¨ÔÚ¿ØÖÆÆ÷ÉÏʹÓ㩵ĴúÀí·½Ê½¡£ÕâÒ»°ãÊÇ´úÀí¿ØÖÆÆ÷µÄĬÈÏ×ö·¨¡£Èç¹û¿ØÖÆÆ÷±ØÐëʵÏÖһЩ½Ó¿Ú£¬¶ø¸Ã½Ó¿ÚÓÖ²»Ö§³ÖSpring ContextµÄ»Øµ÷£¨±ÈÈç`InitializingBean`, `*Aware`µÈ½Ó¿Ú£©£¬ÄÇÒªÅäÖÃÀ༶±ðµÄ´úÀí¾Í±ØÐëÊÖ¶¯ÅäÖÃÁË¡£±ÈÈ磬ԭÀ´µÄÅäÖÃÎļþ``ÐèÒªÏÔʽÅäÖÃΪ``¡£ + +## Spring MVC 3.1ÖÐÐÂÔöÖ§³Ö@RequestMappingµÄһЩÀà + +> They are recommended for use and even required to take advantage of new features in Spring MVC 3.1 and going forward. + +Spring 3.1ÖÐÐÂÔöÁËÒ»×éÀàÓÃÒÔÔöÇ¿`@RequestMapping`£¬·Ö±ðÊÇ`RequestMappingHandlerMapping`ºÍ`RequestMappingHandlerAdapter`¡£ÎÒÃÇÍÆ¼öÄãÓÃÒ»Óá£Óв¿·ÖSpring MVC 3.1Ö®ºóÐÂÔöµÄÌØÐÔ£¬ÕâÁ½¸ö×¢½âÉõÖÁÊDZØÐëµÄ¡£ÔÚMVCÃüÃû¿Õ¼äºÍMVC Java±à³ÌÅäÖ÷½Ê½Ï£¬Õâ×éÀ༰ÆäÐÂÌØÐÔĬÈÏÊÇ¿ªÆôµÄ¡£µ«ÈôÄãʹÓÃÆäËûÅäÖ÷½Ê½£¬Ôò¸ÃÌØÐÔ±ØÐëÊÖ¶¯ÅäÖòÅÄÜʹÓᣱ¾Ð¡½Ú½«¼òÒª½éÉÜһϣ¬ÐÂÀàÏà±È֮ǰµÄÒ»Ð©ÖØÒª±ä»¯¡£ + +ÔÚSpring 3.1֮ǰ£¬¿ò¼Ü»áÔÚÁ½¸ö²»Í¬µÄ½×¶Î·Ö±ð¼ì²éÀ༶±ðºÍ·½·¨¼¶±ðµÄÇëÇóÓ³É䡪¡ªÊ×ÏÈ£¬`DefaultAnnotationHanlderMapping`»áÏÈÔÚÀ༶±ðÉÏÑ¡ÖÐÒ»¸ö¿ØÖÆÆ÷£¬È»ºóÔÙͨ¹ý`AnnotationMethodHandlerAdapter`¶¨Î»µ½¾ßÌåÒªµ÷Óõķ½·¨¡£ + +> [Original] With the new support classes in Spring 3.1, the `RequestMappingHandlerMapping` is the only place where a decision is made about which method should process the request. Think of controller methods as a collection of unique endpoints with mappings for each method derived from type and method-level `@RequestMapping` information. + +ÏÖÔÚÓÐÁËSpring 3.1ºóÒýÈëµÄÕâ×éÐÂÀ࣬`RequestMappingHandlerMapping`³ÉΪÁËÕâÁ½¸ö¾ö²ßʵ¼Ê·¢ÉúµÄΨһһ¸öµØ·½¡£Äã¿ÉÒÔ°Ñ¿ØÖÆÆ÷ÖеÄһϵÁд¦Àí·½·¨µ±³ÉÊÇһϵÁжÀÁ¢µÄ·þÎñ½Úµã£¬Ã¿¸ö´ÓÀ༶±ðºÍ·½·¨¼¶±ðµÄ`@RequestMapping`×¢½âÖлñÈ¡µ½×ã¹»ÇëÇó1·¾¶Ó³ÉäÐÅÏ¢¡£ + +> [Original] This enables some new possibilities. For once a `HandlerInterceptor` or a `HandlerExceptionResolver` can now expect the Object-based handler to be a `HandlerMethod`, which allows them to examine the exact method, its parameters and associated annotations. The processing for a URL no longer needs to be split across different controllers. + +ÕâÖÖеĴ¦Àí·½Ê½´øÀ´ÁËеĿÉÄÜÐÔ¡£Ö®Ç°µÄ`HandlerInterceptor`»ò`HandlerExceptionResolver`ÏÖÔÚ¿ÉÒÔÈ·¶¨Äõ½µÄÕâ¸ö´¦ÀíÆ÷¿Ï¶¨ÊÇÒ»¸ö`HandlerMethod`ÀàÐÍ£¬Òò´ËËüÄܹ»¾«È·µØÁ˽âÕâ¸ö·½·¨µÄËùÓÐÐÅÏ¢£¬°üÀ¨ËüµÄ²ÎÊý¡¢Ó¦ÓÃÓÚÆäÉϵÄ×¢½âµÈ¡£ÕâÑù£¬ÄÚ²¿¶ÔÓÚÒ»¸öURLµÄ´¦ÀíÁ÷³ÌÔÙÒ²²»ÐèÒª·Ö¸ôµ½²»Í¬µÄ¿ØÖÆÆ÷ÀïÃæÈ¥Ö´ÐÐÁË¡£ + +> [Original] There are also several things no longer possible: [Original] _Select a controller first with a `SimpleUrlHandlerMapping` or `BeanNameUrlHandlerMapping` and then narrow the method based on `@RequestMapping` annotations. [Original] _Rely on method names as a fall-back mechanism to disambiguate between two `@RequestMapping` methods that don¡¯t have an explicit path mapping URL path but otherwise match equally, e.g. by HTTP method. In the new support classes `@RequestMapping` methods have to be mapped uniquely. [Original] * Have a single default method (without an explicit path mapping) with which requests are processed if no other controller method matches more concretely. In the new support classes if a matching method is not found a 404 error is raised. + +ͬʱ£¬Ò²ÓÐÆäËûµÄһЩ±ä»¯£¬±ÈÈçÓÐЩÊÂÇé¾Íû·¨ÕâÃ´Íæ¶ùÁË£º + +* ÏÈͨ¹ý`SimpleUrlHandlerMapping`»ò`BeanNameUrlHandlerMapping`À´Äõ½¸ºÔð´¦ÀíÇëÇóµÄ¿ØÖÆÆ÷£¬È»ºóͨ¹ý`@RequestMapping`×¢½âÅäÖõÄÐÅÏ¢À´¶¨Î»µ½¾ßÌåµÄ´¦Àí·½·¨£» +* ÒÀ¿¿·½·¨Ãû³ÆÀ´×÷ΪѡÔñ´¦Àí·½·¨µÄ±ê×¼¡£±ÈÈç˵£¬Á½¸ö×¢½âÁË`@RequestMapping`µÄ·½·¨³ýÁË·½·¨Ãû³ÆÓµÓÐÍêÈ«ÏàͬµÄURLÓ³ÉäºÍHTTPÇëÇó·½·¨¡£ÔÚа汾Ï£¬`@RequestMapping`×¢½âµÄ·½·¨±ØÐë¾ßÓÐΨһµÄÇëÇóÓ³É䣻 +* ¶¨ÒåÒ»¸öĬÈÏ·½·¨£¨¼´Ã»ÓÐÉùÃ÷·¾¶Ó³É䣩£¬ÔÚÇëÇó·¾¶ÎÞ·¨±»Ó³Éäµ½¿ØÖÆÆ÷ϸü¾«È·µÄ·½·¨ÉÏȥʱ£¬Îª¸ÃÇëÇóÌṩĬÈÏ´¦Àí¡£ÔÚа汾ÖУ¬Èç¹ûÎÞ·¨ÎªÒ»¸öÇëÇóÕÒµ½ºÏÊʵĴ¦Àí·½·¨£¬ÄÇôһ¸ö404´íÎ󽫱»Å׳ö£» + +> [Original] The above features are still supported with the existing support classes. However to take advantage of new Spring MVC 3.1 features you¡¯ll need to use the new support classes. + +Èç¹ûʹÓÃÔ­À´µÄÀ࣬ÒÔÉϵŦÄÜ»¹ÊÇ¿ÉÒÔ×öµ½¡£µ«ÊÇ£¬Èç¹ûÒªÏíÊÜSpring MVC 3.1°æ±¾´øÀ´µÄ·½±ãÌØÐÔ£¬Äã¾ÍÐèҪȥʹÓÃеÄÀà¡£ + +> [Original] ## URI Template Patterns + +## URIÄ£°å + +> [Original] URI templates can be used for convenient access to selected parts of a URL in a `@RequestMapping` method. + +URIÄ£°å¿ÉÒÔΪ¿ìËÙ·ÃÎÊ`@RequestMapping`ÖÐÖ¸¶¨µÄURLµÄÒ»¸öÌØ¶¨µÄ²¿·ÖÌṩºÜ´óµÄ±ãÀû¡£ + +> [Original] A URI Template is a URI-like string, containing one or more variable names. When you substitute values for these variables, the template becomes a URI. The proposed RFC for URI Templates defines how a URI is parameterized. For example, the URI Template `http://www.example.com/users/{userId}` contains the variable userId. Assigning the value fred to the variable yields `http://www.example.com/users/fred`. + +URIÄ£°åÊÇÒ»¸öÀàËÆÓÚURIµÄ×Ö·û´®£¬Ö»²»¹ýÆäÖаüº¬ÁËÒ»¸ö»ò¶à¸öµÄ±äÁ¿Ãû¡£µ±ÄãʹÓÃʵ¼ÊµÄֵȥÌî³äÕâЩ±äÁ¿ÃûµÄʱºò£¬Ä£°å¾ÍÍË»¯³ÉÁËÒ»¸öURI¡£ÔÚURIÄ£°åµÄRFCÌáÒéÖж¨ÒåÁËÒ»¸öURIÊÇÈçºÎ½øÐвÎÊý»¯µÄ¡£±ÈÈç˵£¬Ò»¸öÕâ¸öURIÄ£°å`http://www.example.com/users/{userId}`¾Í°üº¬ÁËÒ»¸ö±äÁ¿Ãû_userId_¡£½«Öµ_fred_¸³¸øÕâ¸ö±äÁ¿Ãûºó£¬Ëü¾Í±ä³ÉÁËÒ»¸öURI£º`http://www.example.com/users/fred`¡£ + +> [Original] In Spring MVC you can use the `@PathVariable` annotation on a method argument to bind it to the value of a URI template variable: + +ÔÚSpring MVCÖÐÄã¿ÉÒÔÔÚ·½·¨²ÎÊýÉÏʹÓÃ`@PathVariable`×¢½â£¬½«ÆäÓëURIÄ£°åÖеIJÎÊý°ó¶¨ÆðÀ´£º + +``` +@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET) +public String findOwner(@PathVariable String ownerId, Model model) { + Owner owner = ownerService.findOwner(ownerId); + model.addAttribute("owner", owner); + return "displayOwner"; +} + +``` + +> [Original] The URI Template "`/owners/{ownerId}`" specifies the variable name `ownerId`. When the controller handles this request, the value of `ownerId` is set to the value found in the appropriate part of the URI. For example, when a request comes in for `/owners/fred`, the value of `ownerId` is `fred`. + +URIÄ£°å"`/owners/{ownerId}`"Ö¸¶¨ÁËÒ»¸ö±äÁ¿£¬ÃûΪ`ownerId`¡£µ±¿ØÖÆÆ÷´¦ÀíÕâ¸öÇëÇóµÄʱºò£¬`ownerId`µÄÖµ¾Í»á±»URIÄ£°åÖжÔÓ¦²¿·ÖµÄÖµËùÌî³ä¡£±ÈÈç˵£¬Èç¹ûÇëÇóµÄURIÊÇ`/owners/fred`£¬´Ëʱ±äÁ¿`ownerId`µÄÖµ¾ÍÊÇ`fred`. ` + +> ΪÁË´¦Àí`@PathVariables`×¢½â£¬Spring MVC±ØÐëͨ¹ý±äÁ¿ÃûÀ´ÕÒµ½URIÄ£°åÖÐÏà¶ÔÓ¦µÄ±äÁ¿¡£Äã¿ÉÒÔÔÚ×¢½âÖÐÖ±½ÓÉùÃ÷£º +> +> ``` +> @RequestMapping(path="/owners/{ownerId}}", method=RequestMethod.GET) +> public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { +> // ¾ßÌåµÄ·½·¨´úÂë¡­ +> } +> +> ``` +> +> »òÕߣ¬Èç¹ûURIÄ£°åÖеıäÁ¿ÃûÓë·½·¨µÄ²ÎÊýÃûÊÇÏàͬµÄ£¬ÔòÄã¿ÉÒÔ²»±ØÔÙÖ¸¶¨Ò»´Î¡£Ö»ÒªÄãÔÚ±àÒëµÄʱºòÁôÏÂdebugÐÅÏ¢£¬Spring MVC¾Í¿ÉÒÔ×Ô¶¯Æ¥ÅäURLÄ£°åÖÐÓë·½·¨²ÎÊýÃûÏàͬµÄ±äÁ¿Ãû¡£ +> +> ``` +> @RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET) +> public String findOwner(@PathVariable String ownerId, Model model) { +> // ¾ßÌåµÄ·½·¨´úÂë¡­ +> } +> +> ``` +> +> [Original] A method can have any number of `@PathVariable` annotations: + +Ò»¸ö·½·¨¿ÉÒÔÓµÓÐÈÎÒâÊýÁ¿µÄ`@PathVariable`×¢½â£º + +``` +@RequestMapping(path="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) +public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + Owner owner = ownerService.findOwner(ownerId); + Pet pet = owner.getPet(petId); + model.addAttribute("pet", pet); + return "displayPet"; +} + +``` + +> [Original] When a `@PathVariable` annotation is used on a `Map` argument, the map is populated with all URI template variables. + +µ±`@PathVariable`×¢½â±»Ó¦ÓÃÓÚ`Map`ÀàÐ͵IJÎÊýÉÏʱ£¬¿ò¼Ü»áʹÓÃËùÓÐURIÄ£°å±äÁ¿À´Ìî³äÕâ¸ömap¡£ + +> [Original] A URI template can be assembled from type and path level _@RequestMapping_ annotations. As a result the `findPet()` method can be invoked with a URL such as `/owners/42/pets/21`. + +URIÄ£°å¿ÉÒÔ´ÓÀ༶±ðºÍ·½·¨¼¶±ðµÄ _@RequestMapping_ ×¢½â»ñÈ¡Êý¾Ý¡£Òò´Ë£¬ÏñÕâÑùµÄ`findPet()`·½·¨¿ÉÒÔ±»ÀàËÆÓÚ`/owners/42/pets/21`ÕâÑùµÄURL·Óɲ¢µ÷Óõ½£º + +``` +_@Controller_ +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + + @RequestMapping("/pets/{petId}") + public void findPet(_@PathVariable_ String ownerId, _@PathVariable_ String petId, Model model) { + // ·½·¨ÊµÏÖÌåÕâÀïºöÂÔ + } + +} + +``` + +> [Original] A `@PathVariable` argument can be of _any simple type_ such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a `TypeMismatchException` if it fails to do so. You can also register support for parsing additional data types. See [the section called "Method Parameters And Type Conversion"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-ann-typeconversion) and [the section called "Customizing WebDataBinder initialization"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-ann-webdatabinder). + +`@PathVariable`¿ÉÒÔ±»Ó¦ÓÃÓÚËùÓÐ _¼òµ¥ÀàÐÍ_ µÄ²ÎÊýÉÏ£¬±ÈÈçint¡¢long¡¢DateµÈÀàÐÍ¡£Spring»á×Ô¶¯µØ°ïÄã°Ñ²ÎÊýת»¯³ÉºÏÊʵÄÀàÐÍ£¬Èç¹ûת»»Ê§°Ü£¬¾ÍÅ׳öÒ»¸ö`TypeMismatchException`¡£Èç¹ûÄãÐèÒª´¦ÀíÆäËûÊý¾ÝÀàÐ͵Äת»»£¬Ò²¿ÉÒÔ×¢²á×Ô¼ºµÄÀà¡£ÈôÐèÒª¸üÏêϸµÄÐÅÏ¢¿ÉÒԲο¼[¡°·½·¨²ÎÊýÓëÀàÐÍת»»¡±Ò»½Ú](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-typeconversion)ºÍ[¡°¶¨ÖÆWebDataBinder³õʼ»¯¹ý³Ì¡±Ò»½Ú](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-webdatabinder) + +## ´øÕýÔò±í´ïʽµÄURIÄ£°å + +> [Original] Sometimes you need more precision in defining URI template variables. Consider the URL `"/spring-web/spring-web-3.0.5.jar"`. How do you break it down into multiple parts? + +ÓÐʱºòÄã¿ÉÄÜÐèÒª¸ü׼ȷµØÃèÊöÒ»¸öURIÄ£°åµÄ±äÁ¿£¬±ÈÈç˵Õâ¸öURL£º`"/spring-web/spring-web-3.0.5.jar`¡£ÄãÒªÔõô°ÑËü·Ö½â³É¼¸¸öÓÐÒâÒåµÄ²¿·ÖÄØ£¿ + +> [Original] The `@RequestMapping` annotation supports the use of regular expressions in URI template variables. The syntax is `{varName:regex}` where the first part defines the variable name and the second - the regular expression.For example: + +`@RequestMapping`×¢½âÖ§³ÖÄãÔÚURIÄ£°å±äÁ¿ÖÐʹÓÃÕýÔò±í´ïʽ¡£Óï·¨ÊÇ`{varName:regex}`£¬ÆäÖеÚÒ»²¿·Ö¶¨ÒåÁ˱äÁ¿Ãû£¬µÚ¶þ²¿·Ö¾ÍÊÇÄãËùÒªÓ¦ÓõÄÕýÔò±í´ïʽ¡£±ÈÈçÏÂÃæµÄ´úÂëÑùÀý£º + +``` +@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") + public void handle(@PathVariable String version, @PathVariable String extension) { + // ´úÂ벿·ÖÊ¡ÂÔ... + } +} + +``` + +## Path Patterns£¨²»ºÃ·­£¬ÈÝÒ×µôÔÏ棩 + +> [Original] In addition to URI templates, the `@RequestMapping` annotation also supports Ant-style path patterns (for example, `/myPath/*.do`). A combination of URI template variables and Ant-style globs is also supported (e.g. `/owners/*/pets/{petId}`). + +³ýÁËURIÄ£°åÍ⣬`@RequestMapping`×¢½â»¹Ö§³ÖAnt·ç¸ñµÄ·¾¶Ä£Ê½£¨Èç`/myPath/*.do`µÈ£©¡£²»½öÈç´Ë£¬»¹¿ÉÒÔ°ÑURIÄ£°å±äÁ¿ºÍAnt·ç¸ñµÄglob×éºÏÆðÀ´Ê¹Ó㨱ÈÈç`/owners/*/pets/{petId}`ÕâÑùµÄÓ÷¨µÈ£©¡£ + +## ·¾¶ÑùʽµÄÆ¥Åä(Path Pattern Comparison) + +> [Original] When a URL matches multiple patterns, a sort is used to find the most specific match. + +µ±Ò»¸öURLͬʱƥÅä¶à¸öÄ£°å£¨pattern£©Ê±£¬ÎÒÃǽ«ÐèÒªÒ»¸öËã·¨À´¾ö¶¨ÆäÖÐ×îÆ¥ÅäµÄÒ»¸ö¡£ + +> [Original] A pattern with a lower count of URI variables and wild cards is considered more specific. For example `/hotels/{hotel}/*` has 1 URI variable and 1 wild card and is considered more specific than `/hotels/{hotel}/**` which as 1 URI variable and 2 wild cards. + +URIÄ£°å±äÁ¿µÄÊýÄ¿ºÍͨÅä·ûÊýÁ¿µÄ×ܺÍ×îÉÙµÄÄǸö·¾¶Ä£°å¸ü׼ȷ¡£¾Ù¸öÀý×Ó£¬`/hotels/{hotel}/*`Õâ¸ö·¾¶ÓµÓÐÒ»¸öURI±äÁ¿ºÍÒ»¸öͨÅä·û£¬¶ø`/hotels/{hotel}/**`Õâ¸ö·¾¶ÔòÓµÓÐÒ»¸öURI±äÁ¿ºÍÁ½¸öͨÅä·û£¬Òò´Ë£¬ÎÒÃÇÈÏΪǰÕßÊǸü׼ȷµÄ·¾¶Ä£°å¡£ + +> [Original] If two patterns have the same count, the one that is longer is considered more specific. For example `/foo/bar*` is longer and considered more specific than `/foo/*`. + +Èç¹ûÁ½¸öÄ£°åµÄURIÄ£°åÊýÁ¿ºÍͨÅä·ûÊýÁ¿×ܺÍÒ»Ö£¬Ôò·¾¶¸ü³¤µÄÄǸöÄ£°å¸ü׼ȷ¡£¾Ù¸öÀý×Ó£¬`/foo/bar*`¾Í±»ÈÏΪ±È`/foo/*`¸ü׼ȷ£¬ÒòΪǰÕߵķ¾¶¸ü³¤¡£ + +> [Original] When two patterns have the same count and length, the pattern with fewer wild cards is considered more specific. For example `/hotels/{hotel}` is more specific than `/hotels/*`. + +Èç¹ûÁ½¸öÄ£°åµÄÊýÁ¿ºÍ³¤¶È¾ùÒ»Ö£¬ÔòÄǸö¾ßÓиüÉÙͨÅä·ûµÄÄ£°åÊǸü¼Ó׼ȷµÄ¡£±ÈÈ磬`/hotels/{hotel}`¾Í±È`/hotels/*`¸ü¾«È·¡£ + +> [Original] There are also some additional special rules: + +³ý´ËÖ®Í⣬»¹ÓÐһЩÆäËûµÄ¹æÔò£º + +> [Original] _The **default mapping pattern** `/*_`is less specific than any other pattern. For example`/api/{a}/{b}/{c}` is more specific. +> +> [Original] _A **prefix pattern** such as `/public/*_`is less specific than any other pattern that doesn't contain double wildcards. For example`/public/path3/{a}/{b}/{c}` is more specific. + +* **ĬÈϵÄͨÅäģʽ**`/**`±ÈÆäËûËùÓеÄģʽ¶¼¸ü¡°²»×¼È·¡±¡£±È·½Ëµ£¬`/api/{a}/{b}/{c}`¾Í±ÈĬÈϵÄͨÅäģʽ`/**`Òª¸ü׼ȷ +* **ǰ׺ͨÅä**£¨±ÈÈç`/public/**`)±»ÈÏΪ±ÈÆäËûÈκβ»°üÀ¨Ë«Í¨Åä·ûµÄģʽ¸ü²»×¼È·¡£±ÈÈç˵£¬`/public/path3/{a}/{b}/{c}`¾Í±È`/public/**`¸ü׼ȷ + +> [Original] For the full details see `AntPatternComparator` in `AntPathMatcher`. Note that the PathMatcher can be customized (see [Section 21.16.11, "Path Matching"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-config-path-matching) in the section on configuring Spring MVC). + +¸ü¶àµÄϸ½ÚÇë²Î¿¼ÕâÁ½¸öÀࣺ`AntPatternComparator`ºÍ`AntPathMatcher`¡£ÖµµÃÒ»ÌáµÄÊÇ£¬PathMatcherÀàÊÇ¿ÉÒÔÅäÖõ썼û¡°ÅäÖÃSpring MVC¡±Ò»½ÚÖеÄ[·¾¶µÄÆ¥Åä](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-cgvo27t2.html)Ò»½Ú)¡£ + +## ´øÕ¼Î»·ûµÄ·¾¶Ä£Ê½£¨path patterns£© + +> [Original] Patterns in `@RequestMapping` annotations support ${¡­} placeholders against local properties and/or system properties and environment variables. This may be useful in cases where the path a controller is mapped to may need to be customized through configuration. For more information on placeholders, see the javadocs of the `PropertyPlaceholderConfigurer` class. + +`@RequestMapping`×¢½âÖ§³ÖÔÚ·¾¶ÖÐʹÓÃռλ·û£¬ÒÔÈ¡µÃһЩ±¾µØÅäÖá¢ÏµÍ³ÅäÖᢻ·¾³±äÁ¿µÈ¡£Õâ¸öÌØÐÔÓÐʱºÜÓÐÓ㬱ÈÈç˵¿ØÖÆÆ÷µÄÓ³Éä·¾¶ÐèҪͨ¹ýÅäÖÃÀ´¶¨ÖƵij¡¾°¡£Èç¹ûÏëÁ˽â¸ü¶à¹ØÓÚռλ·ûµÄÐÅÏ¢£¬¿ÉÒԲο¼`PropertyPlaceholderConfigurer`Õâ¸öÀàµÄÎĵµ¡£ + +## Suffix Pattern Matching + +## ºó׺ģʽƥÅä + +> [Original] By default Spring MVC performs `".*"` suffix pattern matching so that a controller mapped to `/person` is also implicitly mapped to `/person.*`. This makes it easy to request different representations of a resource through the URL path (e.g. `/person.pdf`, `/person.xml`). + +Spring MVCĬÈϲÉÓÃ`".*"`µÄºó׺ģʽƥÅäÀ´½øÐз¾¶Æ¥Å䣬Òò´Ë£¬Ò»¸öÓ³Éäµ½`/person`·¾¶µÄ¿ØÖÆÆ÷Ò²»áÒþʽµØ±»Ó³Éäµ½`/person.*`¡£ÕâʹµÃͨ¹ýURLÀ´ÇëÇóͬһ×ÊÔ´ÎļþµÄ²»Í¬¸ñʽ±äµÃ¸ü¼òµ¥£¨±ÈÈç`/person.pdf`£¬`/person.xml`£©¡£ + +> [Original] Suffix pattern matching can be turned off or restricted to a set of path extensions explicitly registered for content negotiation purposes. This is generally recommended to minimize ambiguity with common request mappings such as `/person/{id}` where a dot might not represent a file extension, e.g. `/person/joe@email.com` vs `/person/joe@email.com.json)`. Furthermore as explained in the note below suffix pattern matching as well as content negotiation may be used in some circumstances to attempt malicious attacks and there are good reasons to restrict them meaningfully. + +Äã¿ÉÒԹرÕĬÈϵĺó׺ģʽƥÅ䣬»òÕßÏÔʽµØ½«Â·¾¶ºó׺ÏÞ¶¨µ½Ò»Ð©Ìض¨¸ñʽÉÏfor content negotiation purpose¡£ÎÒÃÇÍÆ¼öÕâÑù×ö£¬ÕâÑù¿ÉÒÔ¼õÉÙÓ³ÉäÇëÇóʱ¿ÉÒÔ´øÀ´µÄһЩ¶þÒåÐÔ£¬±ÈÈçÇëÇóÒÔÏ·¾¶`/person/{id}`ʱ£¬Â·¾¶ÖеĵãºÅºóÃæ´øµÄ¿ÉÄܲ»ÊÇÃèÊöÄÚÈݸñʽ£¬±ÈÈç`/person/joe@email.com` vs `/person/joe@email.com.json`¡£¶øÇÒÕýÈçÏÂÃæÂíÉÏÒªÌáµ½µÄ£¬ºó׺ģʽͨÅäÒÔ¼°ÄÚÈÝЭÉÌÓÐʱ¿ÉÄܻᱻºÚ¿ÍÓÃÀ´½øÐй¥»÷£¬Òò´Ë£¬¶Ôºó׺ͨÅä½øÐÐÓÐÒâÒåµÄÏÞ¶¨ÊÇÓкô¦µÄ¡£ + +> [Original] See [Section 21.16.11, "Path Matching"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-config-path-matching) for suffix pattern matching configuration and also [Section 21.16.6, "Content Negotiation"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-config-content-negotiation) for content negotiation configuration. + +¹ØÓÚºó׺ģʽƥÅäµÄÅäÖÃÎÊÌ⣬¿ÉÒԲο¼[Spring MVC·¾¶Æ¥ÅäÅäÖÃ](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-cgvo27t2.html)£»¹ØÓÚÄÚÈÝЭÉ̵ÄÅäÖÃÎÊÌ⣬¿ÉÒԲο¼[Spring MVC ÄÚÈÝЭÉÌ"](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-h8br27sx.html)µÄÄÚÈÝ¡£ + +## ºó׺ģʽƥÅäÓëRFD + +> [Original] Reflected file download (RFD) attack was first described in a [paper by Trustwave](https://www.trustwave.com/Resources/SpiderLabs-Blog/Reflected-File-Download---A-New-Web-Attack-Vector/) in 2014\. The attack is similar to XSS in that it relies on input (e.g. query parameter, URI variable) being reflected in the response. However instead of inserting JavaScript into HTML, an RFD attack relies on the browser switching to perform a download and treating the response as an executable script if double-clicked based on the file extension (e.g. .bat, .cmd). + +RFD(Reflected file download)¹¥»÷×îÏÈÊÇ2014ÄêÔÚ[TrustwaveµÄһƪÂÛÎÄ](https://www.trustwave.com/Resources/SpiderLabs-Blog/Reflected-File-Download---A-New-Web-Attack-Vector/)Öб»Ìá³öµÄ¡£ËüÓëXSS¹¥»÷ÓÐЩÏàËÆ£¬ÒòΪÕâÖÖ¹¥»÷·½Ê½Ò²ÒÀÀµÓÚÄ³Ð©ÌØÕ÷£¬¼´ÐèÒªÄãµÄÊäÈ루±ÈÈç²éѯ²ÎÊý£¬URI±äÁ¿µÈ£©µÈÒ²ÔÚÊä³ö£¨response£©ÖÐÒÔijÖÖÐÎʽ³öÏÖ¡£²»Í¬µÄÊÇ£¬RFD¹¥»÷²¢²»ÊÇͨ¹ýÔÚHTMLÖÐдÈëJavaScript´úÂë½øÐУ¬¶øÊÇÒÀÀµÓÚä¯ÀÀÆ÷À´Ìø×ªµ½ÏÂÔØÒ³Ãæ£¬²¢°ÑÌØ¶¨¸ñʽ£¨±ÈÈç.bat£¬.cmdµÈ£©µÄresponseµ±³ÉÊÇ¿ÉÖ´Ðнű¾£¬Ë«»÷Ëü¾Í»áÖ´ÐС£ + +> [Original] In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because they can render different content types which clients can request including via URL path extensions. Note however that neither disabling suffix pattern matching nor disabling the use of path extensions for content negotiation purposes alone are effective at preventing RFD attacks. + +Spring MVCµÄ`@ResponseBody`ºÍ`ResponseEntity`·½·¨ÊÇÓзçÏյģ¬ÒòΪËüÃÇ»á¸ù¾Ý¿Í»§µÄÇëÇ󡪡ª°üÀ¨URLµÄ·¾¶ºó׺£¬À´äÖȾ²»Í¬µÄÄÚÈÝÀàÐÍ¡£Òò´Ë£¬½ûÓúó׺ģʽƥÅä»òÕß½ûÓýöΪÄÚÈÝЭÉÌ¿ªÆôµÄ·¾¶Îļþºó׺ÃûЯ´ø£¬¶¼ÊÇ·À·¶RFD¹¥»÷µÄÓÐЧ·½Ê½¡£ + +> [Original] For comprehensive protection against RFD, prior to rendering the response body Spring MVC adds a `Content-Disposition:inline;filename=f.txt` header to suggest a fixed and safe download file filename. This is done only if the URL path contains a file extension that is neither whitelisted nor explicitly registered for content negotiation purposes. However it may potentially have side effects when URLs are typed directly into a browser. + +ÈôÒª¿ªÆô¶ÔRFD¸ü¸ß¼¶µÄ±£»¤Ä£Ê½£¬¿ÉÒÔÔÚSpring MVCäÖȾ¿ªÊ¼ÇëÇóÕýÎÄ֮ǰ£¬ÔÚÇëÇóÍ·ÖÐÔö¼ÓÒ»ÐÐÅäÖÃ`Content-Disposition:inline;filename=f.txt`£¬Ö¸¶¨¹Ì¶¨µÄÏÂÔØÎļþµÄÎļþÃû¡£Õâ½öÔÚURL·¾¶Öаüº¬ÁËÒ»¸öÎļþ·ûºÏÒÔÏÂÌØÕ÷µÄÍØÕ¹ÃûʱÊÊÓ㺸ÃÀ©Õ¹Ãû¼È²»ÔÚÐÅÈÎÁÐ±í£¨°×Ãûµ¥£©ÖУ¬Ò²Ã»Óб»ÏÔʽµØ±»×¢²áÓÚÄÚÈÝЭÉÌʱʹÓᣲ¢ÇÒÕâÖÖ×ö·¨»¹¿ÉÒÔÓÐһЩ¸±×÷Ó㬱ÈÈ磬µ±URLÊÇͨ¹ýä¯ÀÀÆ÷ÊÖ¶¯ÊäÈëµÄʱºò¡£ + +> [Original] Many common path extensions are whitelisted by default. Furthermore REST API calls are typically not meant to be used as URLs directly in browsers. Nevertheless applications that use custom `HttpMessageConverter` implementations can explicitly register file extensions for content negotiation and the Content-Disposition header will not be added for such extensions. See [Section 21.16.6, "Content Negotiation"](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-config-content-negotiation). + +ºÜ¶à³£ÓõÄ·¾¶Îļþºó׺ĬÈÏÊDZ»ÐÅÈεġ£ÁíÍ⣬RESTµÄAPIÒ»°ãÊDz»Ó¦¸ÃÖ±½ÓÓÃ×öURLµÄ¡£²»¹ý£¬Äã¿ÉÒÔ×Ô¼º¶¨ÖÆ`HttpMessageConverter`µÄʵÏÖ£¬È»ºóÏÔʽµØ×¢²áÓÃÓÚÄÚÈÝЭÉ̵ÄÎļþÀàÐÍ£¬ÕâÖÖÇéÐÎÏÂContent-DispositionÍ·½«²»»á±»¼ÓÈëµ½ÇëÇóÍ·ÖС£Ïê¼û[Spring MVC ÄÚÈÝЭÉÌ](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-h8br27sx.html)¡£ + +> [Original] This was originally introduced as part of work for [CVE-2015-5211](http://pivotal.io/security/cve-2015-5211). Below are additional recommendations from the report: +> +> * Encode rather than escape JSON responses. This is also an OWASP XSS recommendation. For an example of how to do that with Spring see [spring-jackson-owasp](https://github.com/rwinch/spring-jackson-owasp). +> * Configure suffix pattern matching to be turned off or restricted to explicitly registered suffixes only. +> * Configure content negotiation with the properties "useJaf" and "ignoreUnknownPathExtensions" set to false which would result in a 406 response for URLs with unknown extensions. Note however that this may not be an option if URLs are naturally expected to have a dot towards the end. +> * Add `X-Content-Type-Options: nosniff` header to responses. Spring Security 4 does this by default. + +¸Ð¾õÕâ½ÚµÄ·­ÒëÖÊÁ¿»¹ÓÐÏÞ£¬ÐèÒª¼ÌÐøÁ˽âXSS¹¥»÷ºÍRFD¹¥»÷µÄϸ½ÚÔÙ·­¡£ + +## ¾ØÕó±äÁ¿ + +> [Original] The URI specification [RFC 3986](http://tools.ietf.org/html/rfc3986#section-3.3) defines the possibility of including name-value pairs within path segments. There is no specific term used in the spec. The general "URI path parameters" could be applied although the more unique ["Matrix URIs"](http://www.w3.org/DesignIssues/MatrixURIs.html), originating from an old post by Tim Berners-Lee, is also frequently used and fairly well known. Within Spring MVC these are referred to as matrix variables. + +Ô­À´µÄURI¹æ·¶[RFC 3986](http://tools.ietf.org/html/rfc3986#section-3.3)ÖÐÔÊÐíÔÚ·¾¶¶ÎÂäÖÐЯ´ø¼üÖµ¶Ô£¬µ«¹æ·¶Ã»ÓÐÃ÷È·¸øÕâÑùµÄ¼üÖµ¶Ô¶¨ÒåÊõÓï¡£ÓÐÈ˽С°URI·¾¶²ÎÊý¡±£¬Ò²ÓнÐ[¡°¾ØÕóURI¡±](http://www.w3.org/DesignIssues/MatrixURIs.html)µÄ¡£ºóÕßÊÇTim Berners-LeeÊ×ÏÈÔÚÆä²©¿ÍÖÐÌáµ½µÄÊõÓ±»Ê¹ÓõÃÒª¸ü¼ÓƵ·±Ò»Ð©£¬ÖªÃû¶ÈÒ²¸ü¸ßЩ¡£¶øÔÚSpring MVCÖУ¬ÎÒÃdzÆÕâÑùµÄ¼üÖµ¶ÔΪ¾ØÕó±äÁ¿¡£ + +> [Original] Matrix variables can appear in any path segment, each matrix variable separated with a ";" (semicolon). For example: `"/cars;color=red;year=2012"`. Multiple values may be either "," (comma) separated `"color=red,green,blue"` or the variable name may be repeated `"color=red;color=green;color=blue"`. + +¾ØÕó±äÁ¿¿ÉÒÔÔÚÈκη¾¶¶ÎÂäÖгöÏÖ£¬Ã¿¶Ô¾ØÕó±äÁ¿Ö®¼äʹÓÃÒ»¸ö·ÖºÅ¡°;¡±¸ô¿ª¡£±ÈÈçÕâÑùµÄURI£º`"/cars;color=red;year=2012"`¡£¶à¸öÖµ¿ÉÒÔÓöººÅ¸ô¿ª`"color=red,green,blue"`£¬»òÕßÖØ¸´±äÁ¿Ãû¶à´Î`"color=red;color=green;color=blue"`¡£ + +> [Original] If a URL is expected to contain matrix variables, the request mapping pattern must represent them with a URI template. This ensures the request can be matched correctly regardless of whether matrix variables are present or not and in what order they are provided. + +Èç¹ûÒ»¸öURLÓпÉÄÜÐèÒª°üº¬¾ØÕó±äÁ¿£¬ÄÇôÔÚÇëÇó·¾¶µÄÓ³ÉäÅäÖÃÉϾÍÐèҪʹÓÃURIÄ£°åÀ´ÌåÏÖÕâÒ»µã¡£ÕâÑù²ÅÄÜÈ·±£ÇëÇó¿ÉÒÔ±»ÕýÈ·µØÓ³É䣬¶ø²»¹Ü¾ØÕó±äÁ¿ÔÚURIÖÐÊÇ·ñ³öÏÖ¡¢³öÏֵĴÎÐòÊÇÔõÑùµÈ¡£ + +> [Original] Below is an example of extracting the matrix variable "q": + +ÏÂÃæÊÇÒ»¸öÀý×Ó£¬Õ¹Ê¾ÁËÎÒÃÇÈçºÎ´Ó¾ØÕó±äÁ¿ÖлñÈ¡µ½±äÁ¿¡°q¡±µÄÖµ£º + +``` +// GET /pets/42;q=11;r=22 + +@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) +public void findPet(@PathVariable String petId, @MatrixVariable int q) { + + // petId == 42 + // q == 11 + +} + +``` + +> [Original] Since all path segments may contain matrix variables, in some cases you need to be more specific to identify where the variable is expected to be: + +ÓÉÓÚÈÎÒâ·¾¶¶ÎÂäÖж¼¿ÉÒÔº¬ÓоØÕó±äÁ¿£¬ÔÚijЩ³¡¾°Ï£¬ÄãÐèÒªÓøü¾«È·µÄÐÅÏ¢À´Ö¸¶¨Ò»¸ö¾ØÕó±äÁ¿µÄλÖ㺠+ +``` +// GET /owners/42;q=11/pets/21;q=22 + +@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) +public void findPet( + @MatrixVariable(name="q", pathVar="ownerId") int q1, + @MatrixVariable(name="q", pathVar="petId") int q2) { + + // q1 == 11 + // q2 == 22 + +} + +``` + +> [Original] A matrix variable may be defined as optional and a default value specified: + +ÄãÒ²¿ÉÒÔÉùÃ÷Ò»¸ö¾ØÕó±äÁ¿²»ÊDZØÐë³öÏֵ쬲¢¸øËü¸³Ò»¸öĬÈÏÖµ£º + +``` +// GET /pets/42 + +@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) +public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { + + // q == 1 + +} + +``` + +> [Original] All matrix variables may be obtained in a Map: + +Ò²¿ÉÒÔͨ¹ýÒ»¸öMapÀ´´æ´¢ËùÓеľØÕó±äÁ¿£º + +``` +// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 + +@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) +public void findPet( + @MatrixVariable Map matrixVars, + @MatrixVariable(pathVar="petId") Map petMatrixVars) { + + // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] + // petMatrixVars: ["q" : 11, "s" : 23] + +} + +``` + +> [Original] Note that to enable the use of matrix variables, you must set the `removeSemicolonContent`property of `RequestMappingHandlerMapping` to `false`. By default it is set to `true`. + +Èç¹ûÒªÔÊÐí¾ØÕó±äÁ¿µÄʹÓã¬Äã±ØÐë°Ñ`RequestMappingHandlerMapping`ÀàµÄ`removeSemicolonContent`ÊôÐÔÉèÖÃΪ`false`¡£¸ÃֵĬÈÏÊÇ`true`µÄ¡£ + +> [Original] The MVC Java config and the MVC namespace both provide options for enabling the use of matrix variables. +> +> MVCµÄJava±à³ÌÅäÖúÍÃüÃû¿Õ¼äÅäÖö¼ÌṩÁËÆôÓþØÕó±äÁ¿µÄ·½Ê½¡£ +> +> [Original] If you are using Java config, The [Advanced Customizations with MVC Java Config](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/mvc.html#mvc-config-advanced-java) section describes how the `RequestMappingHandlerMapping` can be customized. +> +> Èç¹ûÄãÊÇʹÓÃJava±à³ÌµÄ·½Ê½£¬[¡°MVC Java¸ß¼¶¶¨ÖÆ»¯ÅäÖá±Ò»½Ú](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-config-advanced-java)ÃèÊöÁËÈçºÎ¶Ô`RequestMappingHandlerMapping`½øÐж¨ÖÆ¡£ +> +> [Original] In the MVC namespace, the `` element has an `enable-matrix-variables` attribute that should be set to `true`. By default it is set to `false`. +> +> ¶øÊ¹ÓÃMVCµÄÃüÃû¿Õ¼äÅäÖÃʱ£¬Äã¿ÉÒÔ°Ñ``ÔªËØÏµÄ`enable-matrix-variables`ÊôÐÔÉèÖÃΪ`true`¡£¸ÃֵĬÈÏÇé¿öÏÂÊÇÅäÖÃΪ`false`µÄ¡£ + +``` + + + + + + + +``` + +## ¿ÉÏû·ÑµÄýÌåÀàÐÍ + +> [Original] You can narrow the primary mapping by specifying a list of consumable media types. The request will be matched only if the _Content-Type_ request header matches the specified media type. For example: + +Äã¿ÉÒÔÖ¸¶¨Ò»×é¿ÉÏû·ÑµÄýÌåÀàÐÍ£¬ËõСӳÉäµÄ·¶Î§¡£ÕâÑùÖ»Óе±ÇëÇóÍ·ÖÐ _Content-Type_ µÄÖµÓëÖ¸¶¨¿ÉÏû·ÑµÄýÌåÀàÐÍÖÐÓÐÏàͬµÄʱºò£¬ÇëÇó²Å»á±»Æ¥Åä¡£±ÈÈçÏÂÃæÕâ¸öÀý×Ó£º + +``` +@Controller +@RequestMapping(path = "/pets", method = RequestMethod.POST, consumes="application/json") +public void addPet(@RequestBody Pet pet, Model model) { + // ·½·¨ÊµÏÖÊ¡ÂÔ +} + +``` + +> [Original] Consumable media type expressions can also be negated as in _!text/plain_ to match to all requests other than those with _Content-Type_ of _text/plain_. Also consider using constants provided in `MediaType` such as `APPLICATION_JSON_VALUE` and `APPLICATION_JSON_UTF8_VALUE`. + +Ö¸¶¨¿ÉÏû·ÑýÌåÀàÐ͵ıí´ïʽÖл¹¿ÉÒÔʹÓ÷ñ¶¨£¬±ÈÈ磬¿ÉÒÔʹÓà _!text/plain_ À´Æ¥ÅäËùÓÐÇëÇóÍ· _Content-Type_ Öв»º¬ _text/plain_ µÄÇëÇó¡£Í¬Ê±£¬ÔÚ`MediaType`ÀàÖл¹¶¨ÒåÁËһЩ³£Á¿£¬±ÈÈç`APPLICATION_JSON_VALUE`¡¢`APPLICATION_JSON_UTF8_VALUE`µÈ£¬ÍƼö¸ü¶àµØÊ¹ÓÃËüÃÇ¡£ + +> [Original] The _consumes_ condition is supported on the type and on the method level. Unlike most other conditions, when used at the type level, method-level consumable types override rather than extend type-level consumable types. +> +> _consumes_ ÊôÐÔÌṩµÄÊÇ·½·¨¼¶µÄÀàÐÍÖ§³Ö¡£ÓëÆäËûÊôÐÔ²»Í¬£¬µ±ÔÚÀàÐͼ¶Ê¹ÓÃʱ£¬·½·¨¼¶µÄÏû·ÑÀàÐͽ«¸²¸ÇÀàÐͼ¶µÄÅäÖ㬶ø·Ç¼Ì³Ð¹ØÏµ¡£ + +## ¿ÉÉú²úµÄýÌåÀàÐÍ + +> [Original] You can narrow the primary mapping by specifying a list of producible media types. The request will be matched only if the _Accept_ request header matches one of these values. Furthermore, use of the _produces_ condition ensures the actual content type used to generate the response respects the media types specified in the _produces_ condition. For example: + +Äã¿ÉÒÔÖ¸¶¨Ò»×é¿ÉÉú²úµÄýÌåÀàÐÍ£¬ËõСӳÉäµÄ·¶Î§¡£ÕâÑùÖ»Óе±ÇëÇóÍ·ÖÐ _Accept_ µÄÖµÓëÖ¸¶¨¿ÉÉú²úµÄýÌåÀàÐÍÖÐÓÐÏàͬµÄʱºò£¬ÇëÇó²Å»á±»Æ¥Åä¡£¶øÇÒ£¬Ê¹Óà _produces_ Ìõ¼þ¿ÉÒÔÈ·±£ÓÃÓÚÉú³ÉÏìÓ¦£¨response£©µÄÄÚÈÝÓëÖ¸¶¨µÄ¿ÉÉú²úµÄýÌåÀàÐÍÊÇÏàͬµÄ¡£¾Ù¸öÀý×Ó£º + +``` +@Controller +@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) +@ResponseBody +public Pet getPet(@PathVariable String petId, Model model) { + // ·½·¨ÊµÏÖÊ¡ÂÔ +} + +``` + +> [Original] Be aware that the media type specified in the _produces_ condition can also optionally specify a character set. For example, in the code snippet above we specify the same media type than the default one configured in `MappingJackson2HttpMessageConverter`, including the `UTF-8`charset. +> +> ҪעÒâµÄÊÇ£¬Í¨¹ý _condition_ Ìõ¼þÖ¸¶¨µÄýÌåÀàÐÍÒ²¿ÉÒÔÖ¸¶¨×Ö·û¼¯¡£±ÈÈçÔÚÉÏÃæµÄС¶Î´úÂëÖУ¬ÎÒÃÇ»¹ÊǸ²Ð´ÁË`MappingJackson2HttpMessageConverter`ÀàÖÐĬÈÏÅäÖõÄýÌåÀàÐÍ£¬Í¬Ê±£¬»¹Ö¸¶¨ÁËʹÓÃ`UTF-8`µÄ×Ö·û¼¯¡£ +> +> [Original] Just like with _consumes_, producible media type expressions can be negated as in _!text/plain_ to match to all requests other than those with an _Accept_ header value of _text/plain_. Also consider using constants provided in `MediaType` such as `APPLICATION_JSON_VALUE` and `APPLICATION_JSON_UTF8_VALUE`. + +Óë _consumes_ Ìõ¼þÀàËÆ£¬¿ÉÉú²úµÄýÌåÀàÐͱí´ïʽҲ¿ÉÒÔʹÓ÷ñ¶¨¡£±ÈÈ磬¿ÉÒÔʹÓà _!text/plain_ À´Æ¥ÅäËùÓÐÇëÇóÍ· _Accept_ Öв»º¬ _text/plain_ µÄÇëÇó¡£Í¬Ê±£¬ÔÚ`MediaType`ÀàÖл¹¶¨ÒåÁËһЩ³£Á¿£¬±ÈÈç`APPLICATION_JSON_VALUE`¡¢`APPLICATION_JSON_UTF8_VALUE`µÈ£¬ÍƼö¸ü¶àµØÊ¹ÓÃËüÃÇ¡£ + +> [Original] The _produces_ condition is supported on the type and on the method level. Unlike most other conditions, when used at the type level, method-level producible types override rather than extend type-level producible types. +> +> _produces_ ÊôÐÔÌṩµÄÊÇ·½·¨¼¶µÄÀàÐÍÖ§³Ö¡£ÓëÆäËûÊôÐÔ²»Í¬£¬µ±ÔÚÀàÐͼ¶Ê¹ÓÃʱ£¬·½·¨¼¶µÄÏû·ÑÀàÐͽ«¸²¸ÇÀàÐͼ¶µÄÅäÖ㬶ø·Ç¼Ì³Ð¹ØÏµ¡£ + +## ÇëÇó²ÎÊýÓëÇëÇóÍ·µÄÖµ + +> [Original] You can narrow request matching through request parameter conditions such as `"myParam"`, `"!myParam"`, or `"myParam=myValue"`. The first two test for request parameter presence/absence and the third for a specific parameter value. Here is an example with a request parameter value condition: + +Äã¿ÉÒÔɸѡÇëÇó²ÎÊýµÄÌõ¼þÀ´ËõСÇëÇóÆ¥Å䷶Χ£¬±ÈÈç`"myParam"`¡¢`"!myParam"`¼°`"myParam=myValue"`µÈ¡£Ç°Á½¸öÌõ¼þÓÃÓÚɸѡ´æÔÚ/²»´æÔÚijЩÇëÇó²ÎÊýµÄÇëÇ󣬵ÚÈý¸öÌõ¼þɸѡ¾ßÓÐÌØ¶¨²ÎÊýÖµµÄÇëÇó¡£ÏÂÃæÓиöÀý×Ó£¬Õ¹Ê¾ÁËÈçºÎʹÓÃÇëÇó²ÎÊýÖµµÄɸѡÌõ¼þ£º + +``` +@Controller +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + + @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue") + public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + // ʵ¼ÊʵÏÖÊ¡ÂÔ + } + +} + +``` + +> [Original] The same can be done to test for request header presence/absence or to match based on a specific request header value: + +ͬÑù£¬Äã¿ÉÒÔÓÃÏàͬµÄÌõ¼þÀ´É¸Ñ¡ÇëÇóÍ·µÄ³öÏÖÓë·ñ£¬»òÕßɸѡ³öÒ»¸ö¾ßÓÐÌØ¶¨ÖµµÄÇëÇóÍ·£º + +``` +@Controller +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + + @RequestMapping(path = "/pets", method = RequestMethod.GET, headers="myHeader=myValue") + public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + // ·½·¨ÌåʵÏÖÊ¡ÂÔ + } + +} + +``` + +> [Original] Although you can match to _Content-Type_ and _Accept_ header values using media type wild cards (for example _"content-type=text/*"_ will match to _"text/plain"_ and _"text/html"_), it is recommended to use the _consumes_ and _produces_ conditions respectively instead. They are intended specifically for that purpose. +> +> ¾¡¹Ü£¬Äã¿ÉÒÔʹÓÃýÌåÀàÐ͵ÄͨÅä·û£¨±ÈÈç _"content-type=text/*"_£©À´Æ¥ÅäÇëÇóÍ· _Content-Type_ºÍ _Accept_µÄÖµ£¬µ«ÎÒÃǸüÍÆ¼ö¶ÀÁ¢Ê¹Óà _consumes_ºÍ _produces_Ìõ¼þÀ´É¸Ñ¡¸÷×ÔµÄÇëÇó¡£ÒòΪËüÃǾÍÊÇרÃÅÎªÇø·ÖÕâÁ½ÖÖ²»Í¬µÄ³¡¾°¶øÉúµÄ¡£ + +
+ + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\274\202\345\270\270\345\244\204\347\220\206\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\274\202\345\270\270\345\244\204\347\220\206\345\231\250.md" new file mode 100644 index 0000000..604e246 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\345\274\202\345\270\270\345\244\204\347\220\206\345\231\250.md" @@ -0,0 +1,235 @@ + + + + +# Spring MVC ´¦ÀíÆ÷Òì³£½âÎöÆ÷ + + + +2018-07-26 14:32 ¸üР+ + + + + + + +SpringµÄ´¦ÀíÆ÷Òì³£½âÎöÆ÷`HandlerExceptionResolver`½Ó¿ÚµÄʵÏÖ¸ºÔð´¦Àí¸÷Àà¿ØÖÆÆ÷Ö´Ðйý³ÌÖгöÏÖµÄÒì³£¡£Ä³Ö̶ֳÈÉϽ²£¬`HandlerExceptionResolver`ÓëÄãÔÚwebÓ¦ÓÃÃèÊö·û`web.xml`ÎļþÖÐÄܶ¨ÒåµÄÒì³£Ó³É䣨exception mapping£©ºÜÏàÏñ£¬²»¹ýËü±ÈºóÕßÌṩÁ˸üÁé»îµÄ·½Ê½¡£±ÈÈçËüÄÜÌṩÒì³£±»Å׳öʱÕýÔÚÖ´ÐеÄÊÇÄĸö´¦ÀíÆ÷ÕâÑùµÄÐÅÏ¢¡£²¢ÇÒ£¬Ò»¸ö¸üÁé»î£¨programmatic£©µÄÒì³£´¦Àí·½Ê½¿ÉÒÔΪÄãÌṩ¸ü¶àÑ¡Ôñ£¬Ê¹ÄãÔÚÇëÇó±»Ö±½ÓתÏòµ½ÁíÒ»¸öURL֮ǰ£¨ÓëÄãʹÓÃServlet¹æ·¶µÄÒì³£Ó³ÉäÊÇÒ»ÑùµÄ£©Óиü¶àµÄ·½Ê½À´´¦ÀíÒì³£¡£ + +
+ +ʵÏÖ`HandlerExceptionResolver`½Ó¿Ú²¢·ÇʵÏÖÒì³£´¦ÀíµÄΨһ·½Ê½£¬ËüÖ»ÊÇÌṩÁË`resolveException(Exception, Hanlder)`·½·¨µÄÒ»¸öʵÏÖ¶øÒÑ£¬·½·¨»á·µ»ØÒ»¸ö`ModelAndView`¡£³ý´ËÖ®Í⣬Ä㻹¿ÉÒÔ¿ò¼ÜÌṩµÄ`SimpleMappingExceptionResolver`»òÔÚÒì³£´¦Àí·½·¨ÉÏ×¢½â`@ExceptionHandler`¡£`SimpleMappingExceptionResolver`ÔÊÐíÄã»ñÈ¡¿ÉÄÜÅ׳öµÄÒì³£ÀàµÄÃû×Ö£¬²¢°ÑËüÓ³Éäµ½Ò»¸öÊÓͼÃûÉÏÈ¥¡£ÕâÓëServlet APIÌṩµÄÒì³£Ó³ÉäÌØÐÔÊǹ¦Äܵȼ۵쬵«ÄãÒ²¿ÉÒÔ»ùÓÚ´ËʵÏÖÁ£¶È¸ü¾«Ï¸µÄÒì³£Ó³Éä¡£¶ø`@ExceptionHandler`×¢½âµÄ·½·¨Ôò»áÔÚÒì³£Å׳öʱ±»µ÷ÓÃÒÔ´¦Àí¸ÃÒì³£¡£ÕâÑùµÄ·½·¨¿ÉÒÔ¶¨ÒåÔÚ`@Controller`×¢½âµÄ¿ØÖÆÆ÷ÀàÀҲ¿ÉÒÔ¶¨ÒåÔÚ`@ControllerAdvice`ÀàÖУ¬ºóÕß¿ÉÒÔʹ¸ÃÒì³£´¦Àí·½·¨±»Ó¦Óõ½¸ü¶àµÄ`@Controller`¿ØÖÆÆ÷ÖС£ÏÂһС½Ú½«Ìṩ¸üΪÏêϸµÄÐÅÏ¢¡£ + +
+ + + + + + + +# Spring MVC ʹÓÃ@ExceptionHandler×¢½â + + + +2018-07-26 14:33 ¸üР+ + + + + + + +
+ +`HandlerExceptionResolver`½Ó¿ÚÒÔ¼°`SimpleMappingExceptionResolver`½âÎöÆ÷ÀàµÄʵÏÖʹµÃÄãÄÜÉùÃ÷ʽµØ½«Òì³£Ó³Éäµ½ÌØ¶¨µÄÊÓͼÉÏ£¬»¹¿ÉÒÔÔÚÒì³£±»×ª·¢£¨forward£©µ½¶ÔÓ¦µÄÊÓͼǰʹÓÃJava´úÂë×öЩÅжϺÍÂß¼­¡£²»¹ýÔÚһЩ³¡¾°£¬ÌرðÊÇÒÀ¿¿`@ResponseBody`·µ»ØÏìÓ¦¶ø·ÇÒÀÀµÊÓͼ½âÎö»úÖÆµÄ³¡¾°Ï£¬Ö±½ÓÉèÖÃÏìÓ¦µÄ״̬Âë²¢½«¿Í»§¶ËÐèÒªµÄ´íÎóÐÅÏ¢Ö±½Óд»ØÏìÓ¦ÌåÖУ¬¿ÉÄÜÊǸü·½±ãµÄ·½·¨¡£ + +ÄãÒ²¿ÉÒÔʹÓÃ`@ExceptionHandler`·½·¨À´×öµ½Õâµã¡£Èç¹û`@ExceptionHandler`·½·¨ÊÇÔÚ¿ØÖÆÆ÷ÄÚ²¿¶¨ÒåµÄ£¬ÄÇôËü»á½ÓÊÕ²¢´¦ÀíÓÉ¿ØÖÆÆ÷£¨»òÆäÈκÎ×ÓÀࣩÖеÄ`@RequestMapping`·½·¨Å׳öµÄÒì³£¡£Èç¹ûÄ㽫`@ExceptionHandler`·½·¨¶¨ÒåÔÚ`@ControllerAdvice`ÀàÖУ¬ÄÇôËü»á´¦ÀíÏà¹Ø¿ØÖÆÆ÷ÖÐÅ׳öµÄÒì³£¡£ÏÂÃæµÄ´úÂë¾ÍչʾÁËÒ»¸ö¶¨ÒåÔÚ¿ØÖÆÆ÷ÄÚ²¿µÄ`@ExceptionHandler`·½·¨£º + +``` +@Controller +public class SimpleController { + + // @RequestMapping methods omitted ... + + @ExceptionHandler(IOException.class) + public ResponseEntity handleIOException(IOException ex) { + // prepare responseEntity + return responseEntity; + } + +} + +``` + +´ËÍ⣬`@ExceptionHandler`×¢½â»¹¿ÉÒÔ½ÓÊÜÒ»¸öÒì³£ÀàÐ͵ÄÊý×é×÷Ϊ²ÎÊýÖµ¡£ÈôÅ׳öÁËÒÑÔÚÁбíÖÐÉùÃ÷µÄÒì³££¬ÄÇôÏàÓ¦µÄ`@ExceptionHandler`·½·¨½«»á±»µ÷Óá£Èç¹ûûÓиø×¢½âÈκβÎÊýÖµ£¬ÄÇôĬÈÏ´¦ÀíµÄÒì³£ÀàÐͽ«ÊÇ·½·¨²ÎÊýËùÉùÃ÷µÄÄÇЩÒì³£¡£ + +Óë±ê×¼µÄ¿ØÖÆÆ÷`@RequestMapping`×¢½â´¦Àí·½·¨Ò»Ñù£¬`@ExceptionHandler`·½·¨µÄ·½·¨²ÎÊýºÍ·µ»ØÖµÒ²¿ÉÒÔºÜÁé»î¡£±ÈÈ磬ÔÚServlet»·¾³Ï·½·¨¿ÉÒÔ½ÓÊÕ`HttpServletRequest`²ÎÊý£¬¶øPortlet»·¾³Ï·½·¨¿ÉÒÔ½ÓÊÕ`PortletRequest`²ÎÊý¡£·µ»ØÖµ¿ÉÒÔÊÇ`String`ÀàÐÍ¡ª¡ªÕâÖÖÇé¿öÏ»ᱻ½âÎöΪÊÓͼÃû¡ª¡ª¿ÉÒÔÊÇ`ModelAndView`ÀàÐ͵ĶÔÏó£¬Ò²¿ÉÒÔÊÇ`ResponseEntity`¡£»òÕßÄ㻹¿ÉÒÔÔÚ·½·¨ÉÏÌí¼Ó`@ResponseBody`×¢½âÒÔʹÓÃÏûϢת»»Æ÷»áת»»ÐÅÏ¢ÎªÌØ¶¨ÀàÐ͵ÄÊý¾Ý£¬È»ºó°ÑËüÃÇд»Øµ½ÏìÓ¦Á÷ÖС£ + +
+ + + + + + + +# Spring MVC ´¦ÀíÒ»°ãµÄÒì³£ + + + +2018-07-26 14:34 ¸üР+ + + + + + + +´¦ÀíÇëÇóµÄ¹ý³ÌÖУ¬Spring MVC¿ÉÄÜ»áÅ׳öһЩµÄÒì³£¡£`SimpleMappingExceptionResolver`¿ÉÒÔ¸ù¾ÝÐèÒªºÜ·½±ãµØ½«ÈκÎÒì³£Ó³Éäµ½Ò»¸öĬÈϵĴíÎóÊÓͼ¡£µ«£¬Èç¹û¿Í»§¶ËÊÇͨ¹ý×Ô¶¯¼ì²âÏìÓ¦µÄ·½Ê½À´·Ö·¢´¦ÀíÒì³£µÄ£¬ÄÇôºó¶Ë¾ÍÐèҪΪÏìÓ¦ÉèÖöÔÓ¦µÄ״̬Âë¡£¸ù¾ÝÅ׳öÒì³£µÄÀàÐͲ»Í¬£¬¿ÉÄÜÐèÒªÉèÖò»Í¬µÄ״̬ÂëÀ´±êʶÊǿͻ§¶Ë´íÎó£¨4xx£©»¹ÊÇ·þÎñÆ÷¶Ë´íÎó£¨5xx£©¡£ + +
+ +ĬÈÏ´¦ÀíÆ÷Òì³£½âÎöÆ÷`DefaultHandlerExceptionResolver`»á½«Spring MVCÅ׳öµÄÒ쳣ת»»³É¶ÔÓ¦µÄ´íÎó״̬Âë¡£¸Ã½âÎöÆ÷ÔÚMVCÃüÃû¿Õ¼äÅäÖûòMVC JavaÅäÖõķ½Ê½ÏÂĬÈÏÒѾ­±»×¢²áÁË£¬ÁíÍ⣬ͨ¹ý`DispatcherServlet`×¢²áÒ²ÊÇ¿ÉÐе썼´²»Ê¹ÓÃMVCÃüÃû¿Õ¼ä»òJava±à³Ì·½Ê½½øÐÐÅäÖõÄʱºò£©¡£Ï±íÁгöÁ˸ýâÎöÆ÷ÄÜ´¦ÀíµÄһЩÒì³££¬¼°ËûÃǶÔÓ¦µÄ״̬Âë¡£ + +| Òì³£ | HTTP״̬Âë | +| --- | --- | +| `BindException` | 400 (ÎÞЧÇëÇó) | +| `ConversionNotSupportedException` | 500 (·þÎñÆ÷ÄÚ²¿´íÎó) | +| `HttpMediaTypeNotAcceptableException` | 406 (²»½ÓÊÜ) | +| `HttpMediaTypeNotSupportedException` | 415 (²»Ö§³ÖµÄýÌåÀàÐÍ) | +| `HttpMessageNotReadableException` | 400 (ÎÞЧÇëÇó) | +| `HttpMessageNotWritableException` | 500 (·þÎñÆ÷ÄÚ²¿´íÎó) | +| `HttpRequestMethodNotSupportedException` | 405 (²»Ö§³ÖµÄ·½·¨) | +| `MethodArgumentNotValidException` | 400 (ÎÞЧÇëÇó) | +| `MissingServletRequestParameterException` | 400 (ÎÞЧÇëÇó) | +| `MissingServletRequestPartException` | 400 (ÎÞЧÇëÇó) | +| `NoHandlerFoundException` | 404 (ÇëÇóδÕÒµ½) | +| `NoSuchRequestHandlingMethodException` | 404 (ÇëÇóδÕÒµ½) | +| `TypeMismatchException` | 400 (ÎÞЧÇëÇó) | +| `MissingPathVariableException` | 500 (·þÎñÆ÷ÄÚ²¿´íÎó) | +| `NoHandlerFoundException` | 404 (ÇëÇóδÕÒµ½) | + +ÒÔÏ´ý·­Òë¡£ + +The `DefaultHandlerExceptionResolver` works transparently by setting the status of the response. However, it stops short of writing any error content to the body of the response while your application may need to add developer- friendly content to every error response for example when providing a REST API. You can prepare a `ModelAndView` and render error content through view resolution?--?i.e. by configuring a `ContentNegotiatingViewResolver`, `MappingJackson2JsonView`, and so on. However, you may prefer to use`@ExceptionHandler` methods instead. + +If you prefer to write error content via `@ExceptionHandler` methods you can extend `ResponseEntityExceptionHandler` instead. This is a convenient base for `@ControllerAdvice` classes providing an `@ExceptionHandler` method to handle standard Spring MVC exceptions and return `ResponseEntity`. That allows you to customize the response and write error content with message converters. See the `ResponseEntityExceptionHandler` javadocs for more details. + +
+ + + + + + + +# Spring MVC ʹÓÃ@ResponseStatus×¢½âÒµÎñÒì³£ + + + +2020-07-31 10:52 ¸üР+ + + + + + + +ÒµÎñÒì³£¿ÉÒÔʹÓÃ`@ResponseStatus`À´×¢½â¡£µ±Òì³£±»Å׳öʱ£¬`ResponseStatusExceptionResolver`»áÉèÖÃÏàÓ¦µÄÏìӦ״̬Âë¡£`DispatcherServlet`»áĬÈÏ×¢²áÒ»¸ö`ResponseStatusExceptionResolver` ÒÔ¹©Ê¹Óᣠ+ +ResponseStatus×¢½âµÄʹÓ÷dz£¼òµ¥£¬ÎÒÃÇ´´½¨Ò»¸öÒì³£À࣬¼ÓÉÏ×¢½â + +``` +package com.zj.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="Óû§²»Æ¥Åä") +public class UserNotMatchException extends RuntimeException{ +} +``` + +> ResponseStatus×¢½âÊÇÐÞÊÎÀàµÄ +> ËüÓÐÁ½¸öÊôÐÔ£¬valueÊôÐÔÊÇhttp״̬Â룬±ÈÈç404£¬500µÈ¡£reasonÊÇ´íÎóÐÅÏ¢ + +дһ¸öÄ¿±ê·½·¨Å׳ö¸ÃÒì³£ + +``` +@RequestMapping("/testResponseStatus") +public String testResponseStatus(int i){ + if(i==0) + throw new UserNotMatchException(); + return "hello"; +} +``` + +> ʹÓÃÁËResponseStatus×¢½âÖ®ºó£¬Óû§¿´µ½µÄÒì³£½çÃæÕýÊÇÎÒÃÇ×Ô¼º¶¨ÒåµÄÒì³££¬¶ø²»ÔÙÊÇÒ»´ó¶ÑÓû§¿´²»¶®µÄ´úÂë¡£ + + + + + + + +# Spring MVC ¶ÔServletĬÈÏÈÝÆ÷´íÎóÒ³ÃæµÄ¶¨ÖÆ»¯ + + + +2018-07-26 14:36 ¸üР+ + + + + + + +µ±ÏìÓ¦µÄ״̬Âë±»ÉèÖÃΪ´íÎó״̬Â룬²¢ÇÒÏìÓ¦ÌåÖÐûÓÐÄÚÈÝʱ£¬ServletÈÝÆ÷ͨ³£»áäÖȾһ¸öHTML´íÎóÒ³¡£ÈôÐèÒª¶¨ÖÆÈÝÆ÷ĬÈÏÌṩµÄ´íÎóÒ³£¬Äã¿ÉÒÔÔÚ`web.xml`Öж¨ÒåÒ»¸ö´íÎóÒ³Ãæ``ÔªËØ¡£ÔÚServlet 3¹æ·¶³öÀ´Ö®Ç°£¬¸Ã´íÎóÒ³ÔªËØ±ØÐë±»ÏÔʽָ¶¨Ó³Éäµ½Ò»¸ö¾ßÌåµÄ´íÎóÂë»òÒ»¸öÒì³£ÀàÐÍ¡£´ÓServlet 3¿ªÊ¼£¬´íÎóÒ³²»ÔÙÐèÒªÓ³Éäµ½ÆäËûÐÅÏ¢ÁË£¬ÕâÒâζ×Å£¬ÄãÖ¸¶¨µÄλÖþÍÊǶÔServletÈÝÆ÷ĬÈÏ´íÎóÒ³µÄ×Ô¶¨ÖÆÁË¡£ + +
+ +``` + + /error + + +``` + +ÕâÀï´íÎóÒ³µÄλÖÃËùÔÚ¿ÉÒÔÊÇÒ»¸öJSPÒ³Ãæ£¬»òÕ߯äËûµÄһЩURL£¬Ö»ÒªËüÖ¸¶¨ÈÝÆ÷ÀïÈÎÒâÒ»¸ö`@Controller`¿ØÖÆÆ÷ϵĴ¦ÀíÆ÷·½·¨£º + +д»Ø`HttpServletResponse`µÄ´íÎóÐÅÏ¢ºÍ´íÎó״̬Âë¿ÉÒÔÔÚ¿ØÖÆÆ÷ÖÐͨ¹ýÇëÇóÊôÐÔÀ´»ñÈ¡£º + +``` +@Controller +public class ErrorController { + + @RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @ResponseBody + public Map handle(HttpServletRequest request) { + + Map map = new HashMap(); + map.put("status", request.getAttribute("javax.servlet.error.status_code")); + map.put("reason", request.getAttribute("javax.servlet.error.message")); + + return map; + } + +} + +``` + +»òÕßÔÚJSPÖÐÕâôʹÓÃ: + +``` +<%@ page contentType="application/json" pageEncoding="UTF-8"%> +{ + status:<%=request.getAttribute("javax.servlet.error.status_code") %>, + reason:<%=request.getAttribute("javax.servlet.error.message") %> +} +``` + +
+ + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\346\213\246\346\210\252\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\346\213\246\346\210\252\345\231\250.md" new file mode 100644 index 0000000..ba66314 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\346\213\246\346\210\252\345\231\250.md" @@ -0,0 +1,88 @@ + + + + +# Spring MVC ʹÓÃHandlerInterceptorÀ¹½ØÇëÇó + + + +2018-07-26 14:07 ¸üР+ + + + + + + +SpringµÄ´¦ÀíÆ÷Ó³Éä»úÖÆ°üº¬ÁË´¦ÀíÆ÷À¹½ØÆ÷¡£À¹½ØÆ÷ÔÚÄãÐèÒªÎªÌØ¶¨ÀàÐ͵ÄÇëÇóÓ¦ÓÃһЩ¹¦ÄÜʱ¿ÉÄܺÜÓÐÓ㬱ÈÈ磬¼ì²éÓû§Éí·ÝµÈ¡£ + +
+ +´¦ÀíÆ÷Ó³Éä´¦Àí¹ý³ÌÅäÖõÄÀ¹½ØÆ÷£¬±ØÐëʵÏÖ `org.springframework.web.servlet`°üÏ嵀 `HandlerInterceptor`½Ó¿Ú¡£Õâ¸ö½Ó¿Ú¶¨ÒåÁËÈý¸ö·½·¨£º `preHandle(..)`£¬ËüÔÚ´¦ÀíÆ÷ʵ¼ÊÖ´ÐÐ _֮ǰ_ »á±»Ö´ÐУ» `postHandle(..)`£¬ËüÔÚ´¦ÀíÆ÷Ö´ÐÐ _Íê±Ï_ ÒÔºó±»Ö´ÐУ» `afterCompletion(..)`£¬ËüÔÚ _Õû¸öÇëÇó´¦ÀíÍê³É_ Ö®ºó±»Ö´ÐС£ÕâÈý¸ö·½·¨Îª¸÷ÖÖÀàÐ͵Äǰ´¦ÀíºÍºó´¦ÀíÐèÇóÌṩÁË×ã¹»µÄÁé»îÐÔ¡£ + +`preHandle(..)`·½·¨·µ»ØÒ»¸öbooleanÖµ¡£Äã¿ÉÒÔͨ¹ýÕâ¸ö·½·¨À´¾ö¶¨ÊÇ·ñ¼ÌÐøÖ´Ðд¦ÀíÁ´ÖеIJ¿¼þ¡£µ±·½·¨·µ»Ø `true`ʱ£¬´¦ÀíÆ÷Á´»á¼ÌÐøÖ´ÐУ»Èô·½·¨·µ»Ø `false`£¬ `DispatcherServlet`¼´ÈÏΪÀ¹½ØÆ÷×ÔÉíÒѾ­Íê³ÉÁ˶ÔÇëÇóµÄ´¦Àí£¨±ÈÈç˵£¬ÒѾ­äÖȾÁËÒ»¸öºÏÊʵÄÊÓͼ£©£¬ÄÇôÆäÓàµÄÀ¹½ØÆ÷ÒÔ¼°Ö´ÐÐÁ´ÖÐµÄÆäËû´¦ÀíÆ÷¾Í²»»áÔÙ±»Ö´ÐÐÁË¡£ + +À¹½ØÆ÷¿ÉÒÔͨ¹ý`interceptors`ÊôÐÔÀ´ÅäÖ㬸ÃÑ¡ÏîÔÚËùÓм̳ÐÁË`AbstractHandlerMapping`µÄ´¦ÀíÆ÷Ó³ÉäÀà`HandlerMapping`¶¼ÌṩÁËÅäÖõĽӿڡ£ÈçÏÂÃæ´úÂëÑùÀýËùʾ£º + +``` + + + + + + + + + + + + + + + +``` + +``` +package samples; + +public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { + + private int openingTime; + private int closingTime; + + public void setOpeningTime(int openingTime) { + this.openingTime = openingTime; + } + + public void setClosingTime(int closingTime) { + this.closingTime = closingTime; + } + + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + Calendar cal = Calendar.getInstance(); + int hour = cal.get(HOUR_OF_DAY); + if (openingTime <= hour && hour < closingTime) { + return true; + } + response.sendRedirect("http://host.com/outsideOfficeHours.html"); + return false; + } +} + +``` + +ÔÚÉÏÃæµÄÀý×ÓÖУ¬ËùÓб»´Ë´¦ÀíÆ÷´¦ÀíµÄÇëÇ󶼻ᱻ`TimeBasedAccessInterceptor`À¹½ØÆ÷À¹½Ø¡£Èç¹ûµ±Ç°Ê±¼äÔÚ¹¤×÷ʱ¼äÒÔÍ⣬ÄÇôÓû§¾Í»á±»Öض¨Ïòµ½Ò»¸öHTMLÎļþÌáʾÓû§£¬±ÈÈçÏÔʾ¡°ÄãÖ»ÓÐÔÚ¹¤×÷ʱ¼ä²Å¿ÉÒÔ·ÃÎʱ¾ÍøÕ¾¡±Ö®ÀàµÄÐÅÏ¢¡£ + +> ʹÓÃ`RequestMappingHandlerMapping`ʱ£¬Êµ¼ÊµÄ´¦ÀíÆ÷ÊÇÒ»¸ö´¦ÀíÆ÷·½·¨`HandlerMethod`µÄʵÀý£¬Ëü±êʶÁËÒ»¸ö½«±»ÓÃÓÚ´¦Àí¸ÃÇëÇóµÄ¿ØÖÆÆ÷·½·¨¡£ + +ÈçÄãËù¼û£¬SpringµÄÀ¹½ØÆ÷ÊÊÅäÆ÷`HandlerInterceptorAdapter`Èü̳Ð`HandlerInterceptor`½Ó¿Ú±äµÃ¸ü¼òµ¥ÁË¡£ + +> ÉÏÃæµÄÀý×ÓÖУ¬ËùÓпØÖÆÆ÷·½·¨´¦ÀíµÄÇëÇ󶼻ᱻÅäÖõÄÀ¹½ØÆ÷ÏÈÀ¹½Øµ½¡£Èç¹ûÄãÏë½øÒ»²½ËõСÀ¹½ØµÄURL·¶Î§£¬Äã¿ÉÒÔͨ¹ýMVCÃüÃû¿Õ¼ä»òMVC Java±à³ÌµÄ·½Ê½À´ÅäÖ㬻òÕߣ¬ÉùÃ÷Ò»¸ö`MappedInterceptor`ÀàÐ͵ÄbeanʵÀýÀ´´¦Àí¡£¾ßÌåÇë¼û [21.16.1 ÆôÓÃMVC Java±à³ÌÅäÖûòMVCÃüÃû¿Õ¼äÅäÖÃ](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-ouxg27ss.html)һС½Ú¡£ + +ÐèҪעÒâµÄÊÇ£¬`HandlerInterceptor`µÄºóÀ¹½Ø`postHandle`·½·¨²»Ò»¶¨×ÜÊÇÊÊÓÃÓÚ×¢½âÁË`@ResponseBody`»ò`ResponseEntity`µÄ·½·¨¡£ÕâЩ³¡¾°ÖУ¬`HttpMessageConverter`»áÔÚÀ¹½ØÆ÷µÄ`postHandle`·½·¨±»µ÷֮ǰ¾Í°ÑÐÅϢд»ØÏìÓ¦ÖС£ÕâÑùÀ¹½ØÆ÷¾ÍÎÞ·¨ÔٸıäÏìÓ¦ÁË£¬±ÈÈçÒªÔö¼ÓÒ»¸öÏìӦͷ֮ÀàµÄ¡£Èç¹ûÓÐÕâÖÖÐèÇó£¬ÇëÈÃÄãµÄÓ¦ÓÃʵÏÖ`ResponseBodyAdvice`½Ó¿Ú£¬²¢½«Æä¶¨ÒåΪһ¸ö`@ControllerAdvice`bean»òÖ±½ÓÔÚ`RequestMappingHandlerMapping`ÖÐÅäÖᣠ+ +
+ + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\231\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\231\250.md" new file mode 100644 index 0000000..6537078 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\231\250.md" @@ -0,0 +1,181 @@ + + + + +# Spring MVC ʹÓÃViewResolver½Ó¿Ú½âÎöÊÓͼ + + + +2018-07-26 15:39 ¸üР+ + + + + + + +ÕýÈçÔÚ[Spring MVC ¿ØÖÆÆ÷µÄʵÏÖ](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-7z6u27rb.html)Ò»½ÚÖÐËùÌÖÂ۵ģ¬Spring MVCÖÐËùÓпØÖÆÆ÷µÄ´¦ÀíÆ÷·½·¨¶¼±ØÐë·µ»ØÒ»¸öÂß¼­ÊÓͼµÄÃû×Ö£¬ÎÞÂÛÊÇÏÔʽ·µ»Ø£¨±ÈÈç·µ»ØÒ»¸ö`String`¡¢`View`»òÕß`ModelAndView`£©»¹ÊÇÒþʽ·µ»Ø£¨±ÈÈç»ùÓÚÔ¼¶¨µÄ·µ»Ø£©¡£SpringÖеÄÊÓͼÓÉÒ»¸öÊÓͼÃû±êʶ£¬²¢ÓÉÊÓͼ½âÎöÆ÷À´äÖȾ¡£SpringÓзdz£¶àÄÚÖõÄÊÓͼ½âÎöÆ÷¡£Ï±íÁгöÁ˴󲿷֣¬±íºóÒ²¸ø³öÁËһЩÀý×Ó¡£ + +
+ +**±í21.3 ÊÓͼ½âÎöÆ÷** + +| ÊÓͼ½âÎöÆ÷ | ÃèÊö | +| --- | --- | +| `AbstractCachingViewResolver` | Ò»¸ö³éÏóµÄÊÓͼ½âÎöÆ÷À࣬ÌṩÁË»º´æÊÓͼµÄ¹¦ÄÜ¡£Í¨³£ÊÓͼÔÚÄܹ»±»Ê¹ÓÃ֮ǰÐèÒª¾­¹ý×¼±¸¡£¼Ì³ÐÕâ¸ö»ùÀàµÄÊÓͼ½âÎöÆ÷¼´¿ÉÒÔ»ñµÃ»º´æÊÓͼµÄÄÜÁ¦¡£ | +| `XmlViewResolver` | ÊÓͼ½âÎöÆ÷½Ó¿Ú`ViewResolver`µÄÒ»¸öʵÏÖ£¬¸ÃÀà½ÓÊÜÒ»¸öXML¸ñʽµÄÅäÖÃÎļþ¡£¸ÃXMLÎļþ±ØÐëÓëSpring XMLµÄbean¹¤³§ÓÐÏàͬµÄDTD¡£Ä¬ÈϵÄÅäÖÃÎļþÃûÊÇ`/WEB-INF/views.xml`¡£ | +| `ResourceBundleViewResolver` | ÊÓͼ½âÎöÆ÷½Ó¿Ú`ViewResolver`µÄÒ»¸öʵÏÖ£¬²ÉÓÃbundle¸ù·¾¶ËùÖ¸¶¨µÄ`ResourceBundle`ÖеÄbean¶¨Òå×÷ΪÅäÖá£Ò»°ãbundle¶¼¶¨ÒåÔÚclasspath·¾¶ÏµÄÒ»¸öÅäÖÃÎļþÖС£Ä¬ÈϵÄÅäÖÃÎļþÃûΪ`views.properties`¡£ | +| `UrlBasedViewResolver` | `ViewResolver`½Ó¿ÚµÄÒ»¸ö¼òµ¥ÊµÏÖ¡£ËüÖ±½ÓʹÓÃURLÀ´½âÎöµ½Âß¼­ÊÓͼÃû£¬³ý´ËÖ®Íâ²»ÐèÒªÆäËûÈκÎÏÔʽµÄÓ³ÉäÉùÃ÷¡£Èç¹ûÄãµÄÂß¼­ÊÓͼÃûÓëÄãÕæÕýµÄÊÓͼ×ÊÔ´ÃûÊÇÖ±½Ó¶ÔÓ¦µÄ£¬ÄÇôÕâÖÖÖ±½Ó½âÎöµÄ·½Ê½¾ÍºÜ·½±ã£¬²»ÐèÒªÄãÔÙÖ¸¶¨¶îÍâµÄÓ³Éä¡£ | +| `InternalResourceViewResolver` | `UrlBasedViewResolver`µÄÒ»¸öºÃÓõÄ×ÓÀà¡£ËüÖ§³ÖÄÚ²¿×ÊÔ´ÊÓͼ£¨¾ßÌåÀ´Ëµ£¬ServletºÍJSP£©¡¢ÒÔ¼°ÖîÈç`JstlView`ºÍ`TilesView`µÈÀàµÄ×ÓÀà¡£You can specify the view class for all views generated by this resolver by using `setViewClass(..)`¡£¸ü¶àµÄϸ½Ú£¬Çë¼û`UrlBasedViewResolver`ÀàµÄjavaÎĵµ¡£ | +| `VelocityViewResolver` / `FreeMarkerViewResolver` | `UrlBasedViewResolver`ϵÄʵÓÃ×ÓÀ֧࣬³ÖVelocityÊÓͼ`VelocityView`£¨VelocityÄ£°å£©ºÍFreeMarkerÊÓͼ`FreeMarkerView`ÒÔ¼°ËüÃǶÔÓ¦×ÓÀà¡£ | +| `ContentNegotiatingViewResolver` | ÊÓͼ½âÎöÆ÷½Ó¿Ú`ViewResolver`µÄÒ»¸öʵÏÖ£¬Ëü»á¸ù¾ÝËùÇëÇóµÄÎļþÃû»òÇëÇóµÄ`Accept`Í·À´½âÎöÒ»¸öÊÓͼ¡£¸ü¶àϸ½ÚÇë¼û[Spring MVC ÄÚÈÝЭÉÌÊÓͼ½âÎöÆ÷](https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-gal927rn.html)һС½Ú¡£ | + +ÎÒÃÇ¿ÉÒÔ¾Ù¸öÀý×Ó£¬¼ÙÉèÕâÀïʹÓõÄÊÇJSPÊÓͼ¼¼Êõ£¬ÄÇôÎÒÃÇ¿ÉÒÔʹÓÃÒ»¸ö»ùÓÚURLµÄÊÓͼ½âÎöÆ÷`UrlBasedViewResolver`¡£Õâ¸öÊÓͼ½âÎöÆ÷»á½«URL½âÎö³ÉÒ»¸öÊÓͼÃû£¬²¢½«ÇëÇóת½»¸øÇëÇó·Ö·¢Æ÷À´½øÐÐÊÓͼäÖȾ¡£ + +``` + + + + + + +``` + +Èô·µ»ØÒ»¸ö`test`Âß¼­ÊÓͼÃû£¬ÄÇô¸ÃÊÓͼ½âÎöÆ÷»á½«ÇëÇóת·¢µ½`RequestDispatcher`£¬ºóÕ߻ὫÇëÇ󽻸ø`/WEB-INF/jsp/test.jsp`ÊÓͼȥäÖȾ¡£ + +Èç¹ûÐèÒªÔÚÓ¦ÓÃÖÐʹÓöàÖÖ²»Í¬µÄÊÓͼ¼¼Êõ£¬Äã¿ÉÒÔʹÓÃ`ResourceBundleViewResolver`£º + +``` + + + + + +``` + +`ResourceBundleViewResolver`»á¼ìË÷ÓÉbundle¸ù·¾¶ÏÂËùÅäÖõÄ`ResourceBundle`£¬¶ÔÓÚÿ¸öÊÓͼ¶øÑÔ£¬ÆäÊÓͼÀàÓÉ`[viewname].(class)`ÊôÐÔµÄÖµÖ¸¶¨£¬ÆäÊÓͼurlÓÉ`[viewname].url`ÊôÐÔµÄÖµÖ¸¶¨¡£ÏÂÒ»½Ú½«Ïêϸ½²½âÊÓͼ¼¼Êõ£¬Äã¿ÉÒÔÔÚÄÇÀïÕÒµ½¸ü¶àÀý×Ó¡£Ä㻹¿ÉÒÔ¿´µ½£¬ÊÓͼ»¹ÔÊÐíÓлùÊÓͼ£¬¼´propertiesÎļþÖÐËùÓÐÊÓͼ¶¼¡°¼Ì³Ð¡±µÄÒ»¸öÎļþ¡£Í¨¹ý¼Ì³Ð¼¼Êõ£¬Äã¿ÉÒÔΪÖÚ¶àÊÓͼָ¶¨Ò»¸öĬÈϵÄÊÓͼ»ùÀà¡£ + +> `AbstractCachingViewResolver`µÄ×ÓÀàÄܹ»»º´æÒѾ­½âÎö¹ýµÄÊÓͼʵÀý¡£¹Ø±Õ»º´æÌØÐÔÒ²ÊÇ¿ÉÒԵģ¬Ö»ÐèÒª½«`cache`ÊôÐÔÉèÖÃΪ`false`¼´¿É¡£ÁíÍ⣬Èç¹ûʵÔÚÐèÒªÔÚÔËÐÐʱˢÐÂij¸öÊÓͼ£¨±ÈÈçÐÞ¸ÄÁËVelocityÄ£°åʱ£©£¬Äã¿ÉÒÔʹÓÃ`removeFromCache(String viewName, Locale loc)`·½·¨¡£` + +
+ + + + + + + +# Spring MVC ÊÓͼÁ´ + + + +2018-07-26 14:11 ¸üР+ + + + + + + +SpringÖ§³ÖͬʱʹÓöà¸öÊÓͼ½âÎöÆ÷¡£Òò´Ë£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö½âÎöÆ÷Á´£¬²¢×ö¸ü¶àµÄʱÈÈ磬ÔÚÌØ¶¨Ìõ¼þϸ²Ð´Ò»¸öÊÓͼµÈ¡£Äã¿ÉÒÔͨ¹ý°Ñ¶à¸öÊÓͼ½âÎöÆ÷ÉèÖõ½Ó¦ÓÃÉÏÏÂÎÄ(application context)Öеķ½Ê½À´´®ÁªËüÃÇ¡£Èç¹ûÐèÒªÖ¸¶¨ËüÃǵĴÎÐò£¬ÄÇôÉèÖÃ`order`ÊôÐÔ¼´¿É¡£Çë¼Çס£¬orderÊôÐÔµÄÖµÔ½´ó£¬¸ÃÊÓͼ½âÎöÆ÷ÔÚÁ´ÖеÄλÖþÍÔ½¿¿ºó¡£ + +
+ +ÔÚÏÂÃæµÄ´úÂëÀý×ÓÖУ¬ÊÓͼ½âÎöÆ÷Á´Öаüº¬ÁËÁ½¸ö½âÎöÆ÷£ºÒ»¸öÊÇ`InternalResourceViewResolver`£¬Ëü×ÜÊÇ×Ô¶¯±»·ÅÖÃÔÚ½âÎöÆ÷Á´µÄ×îºó£»ÁíÒ»¸öÊÇ`XmlViewResolver`£¬ËüÓÃÀ´Ö¸¶¨ExcelÊÓͼ¡£`InternalResourceViewResolver`²»Ö§³ÖExcelÊÓͼ¡£ + +``` + + + + + + + + + + + + + + + + + +``` + +Èç¹ûÒ»¸öÊÓͼ½âÎöÆ÷²»ÄÜ·µ»ØÒ»¸öÊÓͼ£¬ÄÇôSpring»á¼ÌÐø¼ì²éÉÏÏÂÎÄÖÐÆäËûµÄÊÓͼ½âÎöÆ÷¡£´ËʱÈç¹û´æÔÚÆäËûµÄ½âÎöÆ÷£¬Spring»á¼ÌÐøµ÷ÓÃËüÃÇ£¬Ö±µ½²úÉúÒ»¸öÊÓͼ·µ»ØÎªÖ¹¡£Èç¹û×îºóËùÓÐÊÓͼ½âÎöÆ÷¶¼²»ÄÜ·µ»ØÒ»¸öÊÓͼ£¬Spring¾ÍÅ׳öÒ»¸ö`ServletException`¡£ + +ÊÓͼ½âÎöÆ÷µÄ½Ó¿ÚÇå³þÉùÃ÷ÁË£¬Ò»¸öÊÓͼ½âÎöÆ÷ÊÇ_¿ÉÒÔ_·µ»ØnullÖµµÄ£¬Õâ±íʾ²»ÄÜÕÒµ½ÈκκÏÊʵÄÊÓͼ¡£²¢·ÇËùÓеÄÊÓͼ½âÎöÆ÷¶¼Õâô×ö£¬µ«ÊÇÒ²´æÔÚ²»µÃ²»Èç´ËµÄ³¡¾°£¬¼´½âÎöÆ÷ȷʵÎÞ·¨¼ì²â¶ÔÓ¦µÄÊÓͼÊÇ·ñ´æÔÚ¡£±ÈÈ磬`InternalResourceViewResolver`ÔÚÄÚ²¿Ê¹ÓÃÁË`RequestDispatcher`£¬²¢ÇÒ½øÈë·ÖÅɹý³ÌÊǼì²âÒ»¸öJSPÊÓͼÊÇ·ñ´æÔÚµÄΨһ·½·¨£¬µ«Õâ¸ö¹ý³Ì½ö¿ÉÄÜ·¢ÉúΨһһ´Î¡£Í¬ÑùµÄ`VelocityViewResolver`ºÍ²¿·ÖÆäËûµÄÊÓͼ½âÎöÆ÷Ò²´æÔÚÕâÑùµÄÇé¿ö¡£¾ßÌåµÄÇë²éÔÄij¸öÌØ¶¨µÄÊÓͼ½âÎöÆ÷µÄJavaÎĵµ£¬¿´ËüÊÇ·ñ»áreport²»´æÔÚµÄÊÓͼ¡£Òò´Ë£¬Èç¹û²»°Ñ`InternalResourceViewResolver`·ÅÖÃÔÚ½âÎöÆ÷Á´µÄ×îºó£¬½«¿ÉÄܵ¼Ö½âÎöÆ÷Á´ÎÞ·¨ÍêȫִÐУ¬ÒòΪ`InternalResourceViewResolver`_ÓÀÔ¶¶¼»á_ ·µ»ØÒ»¸öÊÓͼ¡£ + +
+ + + + + + + +# Spring MVC ÊÓÍ¼ÖØ¶¨Ïò + + + +2018-07-26 14:12 ¸üР+ + + + + + + +ÈçǰËùÊö£¬¿ØÖÆÆ÷ͨ³£¶¼»á·µ»ØÒ»¸öÂß¼­ÊÓͼÃû£¬È»ºóÊÓͼ½âÎöÆ÷»á°ÑËü½âÎöµ½Ò»¸ö¾ßÌåµÄÊÓͼ¼¼ÊõÉÏÈ¥äÖȾ¡£¶ÔÓÚһЩ¿ÉÒÔÓÉServlet»òJSPÒýÇæÀ´´¦ÀíµÄÊÓͼ¼¼Êõ£¬±ÈÈçJSPµÈ£¬Õâ¸ö½âÎö¹ý³Ìͨ³£ÊÇÓÉ`InternalResourceViewResolver`ºÍ`InternalResourceView`Э×÷À´Íê³ÉµÄ£¬¶øÕâͨ³£»áµ÷ÓÃServletµÄAPI`RequestDispatcher.forward(..)`·½·¨»ò`RequestDispatcher.include(..)`·½·¨£¬²¢·¢ÉúÒ»´ÎÄÚ²¿µÄת·¢£¨forward£©»òÒýÓã¨include£©¡£¶ø¶ÔÓÚÆäËûµÄÊÓͼ¼¼Êõ£¬±ÈÈçVelocity¡¢XSLTµÈ£¬ÊÓͼ±¾ÉíµÄÄÚÈÝÊÇÖ±½Ó±»Ð´»ØÏìÓ¦Á÷Öеġ£ + +
+ +ÓÐʱ£¬ÎÒÃÇÏëÒªÔÚÊÓͼäÖȾ֮ǰ£¬ÏȰÑÒ»¸öHTTPÖØ¶¨ÏòÇëÇó·¢Ëͻؿͻ§¶Ë¡£±ÈÈ磬µ±Ò»¸ö¿ØÖÆÆ÷³É¹¦µØ½ÓÊܵ½ÁË`POST`¹ýÀ´µÄÊý¾Ý£¬¶øÏìÓ¦½ö½öÊÇίÍÐÁíÒ»¸ö¿ØÖÆÆ÷À´´¦Àí£¨±ÈÈçÒ»´Î³É¹¦µÄ±íµ¥Ìá½»£©Ê±£¬ÎÒÃÇÏ£Íû·¢ÉúÒ»´ÎÖØ¶¨Ïò¡£ÔÚÕâÖÖ³¡¾°Ï£¬Èç¹ûÖ»ÊǼòµ¥µØÊ¹ÓÃÄÚ²¿×ª·¢£¬ÄÇôÒâζ×ÅÏÂÒ»¸ö¿ØÖÆÆ÷Ò²ÄÜ¿´µ½Õâ´Î`POST`ÇëÇóЯ´øµÄÊý¾Ý£¬Õâ¿ÉÄܵ¼ÖÂһЩDZÔÚµÄÎÊÌ⣬±ÈÈç¿ÉÄÜ»áÓëÆäËûÆÚÍûµÄÊý¾Ý»ìÏý£¬µÈ¡£´ËÍ⣬ÁíÒ»ÖÖÔÚäÖȾÊÓͼǰ¶ÔÇëÇó½øÐÐÖØ¶¨ÏòµÄÐèÇóÊÇ£¬·ÀÖ¹Óû§¶à´ÎÌá½»±íµ¥µÄÊý¾Ý¡£´ËʱÈôʹÓÃÖØ¶¨Ïò£¬Ôòä¯ÀÀÆ÷»áÏÈ·¢Ë͵ÚÒ»¸ö`POST`ÇëÇó£»ÇëÇó±»´¦Àíºóä¯ÀÀÆ÷»áÊÕµ½Ò»¸öÖØ¶¨ÏòÏìÓ¦£¬È»ºóä¯ÀÀÆ÷Ö±½Ó±»Öض¨Ïòµ½Ò»¸ö²»Í¬µÄURL£¬×îºóä¯ÀÀÆ÷»áʹÓÃÖØ¶¨ÏòÏìÓ¦ÖÐЯ´øµÄURL·¢ÆðÒ»´Î`GET`ÇëÇó¡£Òò´Ë£¬´Óä¯ÀÀÆ÷µÄ½Ç¶È¿´£¬µ±Ç°Ëù¼ûµÄÒ³Ãæ²¢²»ÊÇ`POST`ÇëÇóµÄ½á¹û£¬¶øÊÇÒ»´Î`GET`ÇëÇóµÄ½á¹û¡£Õâ¾Í·ÀÖ¹ÁËÓû§ÒòˢеÈÔ­ÒòÒâÍâµØÌá½»Á˶à´ÎͬÑùµÄÊý¾Ý¡£´ËʱˢлáÖØÐÂ`GET`Ò»´Î½á¹ûÒ³£¬¶ø²»ÊǰÑͬÑùµÄ`POST`Êý¾ÝÔÙ·¢ËÍÒ»±é¡£ + +## ÖØ¶¨ÏòÊÓͼ RedirectView + +Ç¿ÖÆÖØ¶¨ÏòµÄÒ»ÖÖ·½·¨ÊÇ£¬ÔÚ¿ØÖÆÆ÷Öд´½¨²¢·µ»ØÒ»¸öSpringÖØ¶¨ÏòÊÓͼ`RedirectView`µÄʵÀý¡£Ëü»áʹµÃ`DispatcherServlet`·ÅÆúʹÓÃÒ»°ãµÄÊÓͼ½âÎö»úÖÆ£¬ÒòΪÄãÒѾ­·µ»ØÒ»¸ö£¨Öض¨Ïò£©ÊÓͼ¸ø`DispatcherServlet`ÁË£¬ËùÒÔËü»á¹¹ÔìÒ»¸öÊÓͼÀ´Âú×ãäÖȾµÄÐèÇó¡£½ô½Ó×Å`RedirectView`»áµ÷ÓÃ`HttpServletResponse.sendRedirect()`·½·¨£¬·¢ËÍÒ»¸öHTTPÖØ¶¨ÏòÏìÓ¦¸ø¿Í»§¶Ëä¯ÀÀÆ÷¡£ + +Èç¹ûÄã¾ö¶¨·µ»Ø`RedirectView`£¬²¢ÇÒÕâ¸öÊÓͼʵÀýÊÇÓÉ¿ØÖÆÆ÷ÄÚ²¿´´½¨³öÀ´µÄ£¬ÄÇÎÒÃǸüÍÆ¼öÔÚÍⲿÅäÖÃÖØ¶¨ÏòURLÈ»ºó×¢Èëµ½¿ØÖÆÆ÷ÖÐÀ´£¬¶ø²»ÊÇдÔÚ¿ØÖÆÆ÷ÀïÃæ¡£ÕâÑùËü¾Í¿ÉÒÔÓëÊÓͼÃûÒ»ÆðÔÚÅäÖÃÎļþÖÐÅäÖ᣹ØÓÚÈçºÎʵÏÖÕâ¸ö½âñÇë²Î¿¼ [ÖØ¶¨Ïòǰ׺¡ª¡ªredirect:](http://docs.spring.io/spring-framework/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-redirecting-redirect-prefix)һС½Ú¡£ + +## ÏòÖØ¶¨ÏòÄ¿±ê´«µÝÊý¾Ý + +Ä£ÐÍÖеÄËùÓÐÊôÐÔĬÈ϶¼»á¿¼ÂÇ×÷ΪURIÄ£°å±äÁ¿±»Ìí¼Óµ½Öض¨ÏòURLÖС£Ê£ÏÂµÄÆäËûÊôÐÔ£¬Èç¹ûÊÇ»ù±¾ÀàÐÍ»òÕß»ù±¾ÀàÐ͵ļ¯ºÏ»òÊý×飬ÄÇËüÃǽ«±»×Ô¶¯Ìí¼Óµ½URLµÄ²éѯ²ÎÊýÖÐÈ¥¡£Èç¹ûmodelÊÇרÃÅΪ¸ÃÖØ¶¨ÏòËù×¼±¸µÄ£¬ÄÇô°ÑËùÓлù±¾ÀàÐ͵ÄÊôÐÔÌí¼Óµ½²éѯ²ÎÊýÖпÉÄÜÊÇÎÒÃÇÆÚÍûÄǸöµÄ½á¹û¡£µ«ÊÇ£¬ÔÚ°üº¬×¢½âµÄ¿ØÖÆÆ÷ÖУ¬model¿ÉÄܰüº¬ÁËרÃÅ×÷ΪäÖȾÓÃ;µÄÊôÐÔ£¨±ÈÈçÒ»¸öÏÂÀ­ÁбíµÄ×Ö¶ÎÖµµÈ£©¡£ÎªÁ˱ÜÃâ°ÑÕâÑùµÄÊôÐÔÒ²±©Â¶ÔÚURLÖУ¬`@RequestMapping`·½·¨¿ÉÒÔÉùÃ÷Ò»¸ö`RedirectAttributes`ÀàÐ͵ķ½·¨²ÎÊý£¬ÓÃËüÀ´Ö¸¶¨×¨ÃŹ©Öض¨ÏòÊÓͼ`RedirectView`È¡ÓõÄÊôÐÔ¡£Èç¹ûÖØ¶¨Ïò³É¹¦·¢Éú£¬ÄÇô`RedirectAttributes`¶ÔÏóÖеÄÄÚÈݾͻᱻʹÓã»·ñÔòÔòʹÓÃÄ£ÐÍmodelÖеÄÊý¾Ý¡£ + +`RequestMappingHandlerAdapter`ÌṩÁËÒ»¸ö`"ignoreDefaultModelOnRedirect"`±êÖ¾¡£Ëü±»ÓÃÀ´±ê¼ÇĬÈÏ`Model`ÖеÄÊôÐÔÓÀÔ¶²»Ó¦¸Ã±»ÓÃÓÚ¿ØÖÆÆ÷·½·¨µÄÖØ¶¨ÏòÖС£¿ØÖÆÆ÷·½·¨Ó¦¸ÃÉùÃ÷Ò»¸ö`RedirectAttributes`ÀàµÄ²ÎÊý¡£Èç¹û²»ÉùÃ÷£¬ÄǾÍûÓвÎÊý±»´«µÝµ½Öض¨ÏòµÄÊÓͼ`RedirectView`ÖС£ÔÚMVCÃüÃû¿Õ¼ä»òMVC Java±à³ÌÅäÖ÷½Ê½ÖУ¬ÎªÁËά³ÖÏòºóµÄ¼æÈÝÐÔ£¬Õâ¸ö±êÖ¾¶¼ÈÔ±»±£³ÖΪ`false`¡£µ«Èç¹ûÄãµÄÓ¦ÓÃÊÇÒ»¸öеÄÏîÄ¿£¬ÄÇôÎÒÃÇÍÆ¼ö°ÑËüµÄÖµÉèÖóÉ`true`¡£ + +Çë×¢Ò⣬µ±Ç°ÇëÇóURIÖеÄÄ£°å±äÁ¿»áÔÚÌî³äÖØ¶¨ÏòURLµÄʱºò×Ô¶¯¶ÔÓ¦Óÿɼû£¬¶ø²»ÐèÒªÏÔʽµØÔÚ`Model`»ò`RedirectAttributes`ÖÐÔÙÌí¼ÓÊôÐÔ¡£Çë¿´ÏÂÃæµÄÀý×Ó£º + +``` +@RequestMapping(path = "/files/{path}", method = RequestMethod.POST) +public String upload(...) { + // ... + return "redirect:files/{path}"; +} + +``` + +ÁíÍâÒ»ÖÖÏòÖØ¶¨ÏòÄ¿±ê´«µÝÊý¾ÝµÄ·½·¨ÊÇͨ¹ý _ÉÁ´æÊôÐÔ£¨Flash Attributes£©_¡£ÓëÆäËûÖØ¶¨ÏòÊôÐÔ²»Í¬£¬flashÊôÐÔÊÇ´æ´¢ÔÚHTTP sessionÖеģ¨Òò´Ë²»»á³öÏÖÔÚURLÖУ©¡£¸ü¶àÄÚÈÝ£¬Çë²Î¿¼ [21.6 ʹÓÃÉÁ´æÊôÐÔ](http://docs.spring.io/spring-framework/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-flash-attributes)Ò»½Ú¡£ + +## ÖØ¶¨Ïòǰ׺¡ª¡ªredirect: + +¾¡¹ÜʹÓÃ`RedirectView`À´×öÖØ¶¨ÏòÄܹ¤×÷µÃºÜºÃ£¬µ«Èç¹û¿ØÖÆÆ÷×ÔÉí»¹ÊÇÐèÒª´´½¨Ò»¸ö`RedirectView`£¬ÄÇÎÞÒÉ¿ØÖÆÆ÷»¹ÊÇÁ˽âÖØ¶¨ÏòÕâôһ¼þÊÂÇéµÄ·¢Éú¡£Õ⻹ÊÇÓе㲻¾¡ÍêÃÀ£¬²»Í¬·¶³ëµÄñîºÏ»¹ÊÇ̫ǿ¡£¿ØÖÆÆ÷Æäʵ²»Ó¦¸ÃÈ¥¹ØÐÄÏìÓ¦»áÈçºÎ±»äÖȾ¡£In general it should operate only in terms of view names that have been injected into it. + +Ò»¸öÌØ±ðµÄÊÓͼÃûǰ׺ÄÜÍê³ÉÕâ¸ö½âñ`redirect:`¡£Èç¹û·µ»ØµÄÊÓͼÃûÖк¬ÓÐ`redirect:`ǰ׺£¬ÄÇô`UrlBasedViewResolver`£¨¼°ËüµÄËùÓÐ×ÓÀࣩ¾Í»á½ÓÊܵ½Õâ¸öÐźţ¬Òâʶµ½ÕâÀïÐèÒª·¢ÉúÖØ¶¨Ïò¡£È»ºóÊÓͼÃûʣϵIJ¿·Ö»á±»½âÎö³ÉÖØ¶¨ÏòURL¡£ + +ÕâÖÖ·½Ê½Óëͨ¹ý¿ØÖÆÆ÷·µ»ØÒ»¸öÖØ¶¨ÏòÊÓͼ`RedirectView`Ëù´ïµ½µÄЧ¹ûÊÇÒ»ÑùµÄ£¬²»¹ýÕâÑùÒ»À´¿ØÖÆÆ÷¾Í¿ÉÒÔֻרעÓÚ´¦Àí²¢·µ»ØÂß¼­ÊÓͼÃûÁË¡£Èç¹ûÂß¼­ÊÓͼÃûÊÇÕâÑùµÄÐÎʽ£º`redirect:/myapp/some/resource`£¬ËûÃÇÖØ¶¨Ïò·¾¶½«ÒÔServletÉÏÏÂÎÄ×÷ΪÏà¶Ô·¾¶½øÐвéÕÒ£¬¶øÂß¼­ÊÓͼÃûÈç¹ûÊÇÕâÑùµÄÐÎʽ£º`redirect:http://myhost.com/some/arbitrary/path`£¬ÄÇÃ´ÖØ¶¨ÏòURLʹÓõľÍÊǾø¶Ô·¾¶¡£ + +×¢ÒâµÄÊÇ£¬Èç¹û¿ØÖÆÆ÷·½·¨×¢½âÁË`@ResponseStatus`£¬ÄÇôע½âÉèÖõÄ״̬ÂëÖµ»á¸²¸Ç`RedirectView`ÉèÖõÄÏìӦ״̬ÂëÖµ¡£ + +## ÖØ¶¨Ïòǰ׺¡ª¡ªforward: + +¶ÔÓÚ×îÖջᱻ`UrlBasedViewResolver`»òÆä×ÓÀà½âÎöµÄÊÓͼÃû£¬Äã¿ÉÒÔʹÓÃÒ»¸öÌØÊâµÄǰ׺£º`forward:`¡£Õâ»áµ¼ÖÂÒ»¸ö`InternalResourceView`ÊÓͼ¶ÔÏóµÄ´´½¨£¨Ëü×îÖÕ»áµ÷ÓÃ`RequestDispatcher.forward()`·½·¨£©£¬ºóÕß»áÈÏΪÊÓͼÃûʣϵIJ¿·ÖÊÇÒ»¸öURL¡£Òò´Ë£¬Õâ¸öǰ׺ÔÚʹÓÃ`InternalResourceViewResolver`ºÍ`InternalResourceView`ʱ²¢Ã»ÓÐÌØ±ðµÄ×÷Ó㨱ÈÈç¶ÔÓÚJSPÀ´Ëµ£©¡£µ«µ±ÄãÖ÷ҪʹÓõÄÊÇÆäËûµÄÊÓͼ¼¼Êõ£¬¶øÓÖÏëÒªÇ¿ÖÆ°ÑÒ»¸ö×ÊԴת·¢¸øServlet/JSPÒýÇæ½øÐд¦Àíʱ£¬Õâ¸öǰ׺¿ÉÄܾͺÜÓÐÓ㨻òÕߣ¬ÄãÒ²¿ÉÄÜͬʱ´®Áª¶à¸öÊÓͼ½âÎöÆ÷£©¡£ + +Óë`redirect:`ǰ׺һÑù£¬Èç¹û¿ØÖÆÆ÷ÖеÄÊÓͼÃûʹÓÃÁË`forward:`ǰ׺£¬¿ØÖÆÆ÷±¾Éí²¢²»»á·¢¾õÈκÎÒì³££¬Ëü¹Ø×¢µÄÈÔȻֻÊÇÈçºÎ´¦ÀíÏìÓ¦µÄÎÊÌâ¡£ + +
+ + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\277\207\346\273\244\345\231\250Filter.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\277\207\346\273\244\345\231\250Filter.md" new file mode 100644 index 0000000..440f4d4 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\344\270\255\347\232\204\350\277\207\346\273\244\345\231\250Filter.md" @@ -0,0 +1,151 @@ +¼¯³ÉFilter + +ÔÚSpring MVCÖУ¬DispatcherServletÖ»ÐèÒª¹Ì¶¨ÅäÖõ½web.xmlÖУ¬Ê£ÏµĹ¤×÷Ö÷ÒªÊÇרעÓÚ±àдController¡£ + +µ«ÊÇ£¬ÔÚServlet¹æ·¶ÖУ¬ÎÒÃÇ»¹¿ÉÒÔʹÓÃFilter¡£Èç¹ûÒªÔÚSpring MVCÖÐʹÓÃFilter£¬Ó¦¸ÃÔõô×ö£¿ + +ÓеÄͯЬÔÚÉÏÒ»½ÚµÄWebÓ¦ÓÃÖпÉÄÜ·¢ÏÖÁË£¬Èç¹û×¢²áʱÊäÈëÖÐÎĻᵼÖÂÂÒÂ룬ÒòΪServletĬÈϰ´·ÇUTF-8±àÂë¶ÁÈ¡²ÎÊý¡£ÎªÁËÐÞ¸´ÕâÒ»ÎÊÌ⣬ÎÒÃÇ¿ÉÒÔ¼òµ¥µØÊ¹ÓÃÒ»¸öEncodingFilter£¬ÔÚÈ«¾Ö·¶Î§Àà¸øHttpServletRequestºÍHttpServletResponseÇ¿ÖÆÉèÖÃΪUTF-8±àÂë¡£ + +¿ÉÒÔ×Ô¼º±àдһ¸öEncodingFilter£¬Ò²¿ÉÒÔÖ±½ÓʹÓÃSpring MVC×Ô´øµÄÒ»¸öCharacterEncodingFilter¡£ÅäÖÃFilterʱ£¬Ö»ÐèÔÚweb.xmlÖÐÉùÃ÷¼´¿É£º +```` + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + + encodingFilter + /* + + ... + +```` +ÒòΪÕâÖÖFilterºÍÎÒÃÇÒµÎñ¹ØÏµ²»´ó£¬×¢Òâµ½CharacterEncodingFilterÆäʵºÍSpringµÄIoCÈÝÆ÷ûÓÐÈκιØÏµ£¬Á½Õß¾ù»¥²»ÖªÏþ¶Ô·½µÄ´æÔÚ£¬Òò´Ë£¬ÅäÖÃÕâÖÖFilterÊ®·Ö¼òµ¥¡£ + +ÎÒÃÇÔÙ¿¼ÂÇÕâÑùÒ»¸öÎÊÌ⣺Èç¹ûÔÊÐíÓû§Ê¹ÓÃBasicģʽ½øÐÐÓû§ÑéÖ¤£¬¼´ÔÚHTTPÇëÇóÖÐÌí¼ÓÍ·Authorization: Basic email:password£¬Õâ¸öÐèÇóÈçºÎʵÏÖ£¿ + +±àдһ¸öAuthFilterÊÇ×î¼òµ¥µÄʵÏÖ·½Ê½£º +```` +@Component +public class AuthFilter implements Filter { +@Autowired +UserService userService; + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + // »ñÈ¡AuthorizationÍ·: + String authHeader = req.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Basic ")) { + // ´ÓHeaderÖÐÌáÈ¡emailºÍpassword: + String email = prefixFrom(authHeader); + String password = suffixFrom(authHeader); + // µÇ¼: + User user = userService.signin(email, password); + // ·ÅÈëSession: + req.getSession().setAttribute(UserController.KEY_USER, user); + } + // ¼ÌÐø´¦ÀíÇëÇó: + chain.doFilter(request, response); + } +} +```` +ÏÖÔÚÎÊÌâÀ´ÁË£ºÔÚSpringÖд´½¨µÄÕâ¸öAuthFilterÊÇÒ»¸öÆÕͨBean£¬ServletÈÝÆ÷²¢²»ÖªµÀ£¬ËùÒÔËü²»»áÆð×÷Óᣠ+ +Èç¹ûÎÒÃÇÖ±½ÓÔÚweb.xmlÖÐÉùÃ÷Õâ¸öAuthFilter£¬×¢Òâµ½AuthFilterµÄʵÀý½«ÓÉServletÈÝÆ÷¶ø²»ÊÇSpringÈÝÆ÷³õʼ»¯£¬Òò´Ë£¬@Autowire¸ù±¾²»ÉúЧ£¬ÓÃÓڵǼµÄUserService³ÉÔ±±äÁ¿ÓÀÔ¶ÊÇnull¡£ + +ËùÒÔ£¬µÃͨ¹ýÒ»ÖÖ·½Ê½£¬ÈÃServletÈÝÆ÷ʵÀý»¯µÄFilter£¬¼ä½ÓÒýÓÃSpringÈÝÆ÷ʵÀý»¯µÄAuthFilter¡£Spring MVCÌṩÁËÒ»¸öDelegatingFilterProxy£¬×¨ÃÅÀ´¸ÉÕâ¸öÊÂÇ飺 +```` + + + authFilter + org.springframework.web.filter.DelegatingFilterProxy + + + + authFilter + /* + + ... + +```` +ÎÒÃÇÀ´¿´ÊµÏÖÔ­Àí£º + +ServletÈÝÆ÷´Óweb.xmlÖжÁÈ¡ÅäÖã¬ÊµÀý»¯DelegatingFilterProxy£¬×¢ÒâÃüÃûÊÇauthFilter£» +SpringÈÝÆ÷ͨ¹ýɨÃè@ComponentʵÀý»¯AuthFilter¡£ +µ±DelegatingFilterProxyÉúЧºó£¬Ëü»á×Ô¶¯²éÕÒ×¢²áÔÚServletContextÉϵÄSpringÈÝÆ÷£¬ÔÙÊÔͼ´ÓÈÝÆ÷ÖвéÕÒÃûΪauthFilterµÄBean£¬Ò²¾ÍÊÇÎÒÃÇÓÃ@ComponentÉùÃ÷µÄAuthFilter¡£ + +DelegatingFilterProxy½«ÇëÇó´úÀí¸øAuthFilter£¬ºËÐÄ´úÂëÈçÏ£º +```` +public class DelegatingFilterProxy implements Filter { + private Filter delegate; + public void doFilter(...) throws ... { + if (delegate == null) { + delegate = findBeanFromSpringContainer(); + } + delegate.doFilter(req, resp, chain); + } +} +```` +Õâ¾ÍÊÇÒ»¸ö´úÀíģʽµÄ¼òµ¥Ó¦Óá£ÎÒÃÇ»­¸öͼ±íʾËüÃÇÖ®¼äµÄÒýÓùØÏµÈçÏ£º +```` +©° ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©´ ©° ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ +©°©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©´ ©°©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©´ ©¦ +©¦ ©¦DelegatingFilterProxy©¦©¤©¦©¤©¦©¤ ©¤>©¦AuthFilter ©¦ +©¸©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¼ ©¸©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¼ ©¦ +©¦ ©°©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©´ ©¦ ©¦ ©°©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©´ +©¦ DispatcherServlet ©¦©¤ ©¤ ©¤ ©¤>©¦Controllers©¦ ©¦ +©¦ ©¸©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¼ ©¦ ©¦ ©¸©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¤©¼ +©¦ +©¦ Servlet Container ©¦ ©¦ Spring Container +©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¤ ©¼ +```` +Èç¹ûÔÚweb.xmlÖÐÅäÖõÄFilterÃû×ÖºÍSpringÈÝÆ÷µÄBeanµÄÃû×Ö²»Ò»Ö£¬ÄÇôÐèÒªÖ¸¶¨BeanµÄÃû×Ö£º +```` + + basicAuthFilter + org.springframework.web.filter.DelegatingFilterProxy + + + targetBeanName + authFilter + + +```` +ʵ¼ÊÓ¦ÓÃʱ£¬¾¡Á¿±£³ÖÃû×ÖÒ»Ö£¬ÒÔ¼õÉÙ²»±ØÒªµÄÅäÖᣠ+ +ҪʹÓÃBasicģʽµÄÓû§ÈÏÖ¤£¬ÎÒÃÇ¿ÉÒÔʹÓÃcurlÃüÁî²âÊÔ¡£ÀýÈ磬Óû§µÇ¼ÃûÊÇtom@example.com£¬¿ÚÁîÊÇtomcat£¬ÄÇôÏȹ¹ÔìÒ»¸öʹÓÃURL±àÂëµÄÓû§Ãû:¿ÚÁîµÄ×Ö·û´®£º + +tom%40example.com:tomcat +¶ÔÆä½øÐÐBase64±àÂ룬×îÖÕ¹¹Ôì³öµÄHeaderÈçÏ£º + +Authorization: Basic dG9tJTQwZXhhbXBsZS5jb206dG9tY2F0 +ʹÓÃÈçϵÄcurlÃüÁî²¢»ñµÃÏìÓ¦ÈçÏ£º +```` +$ curl -v -H 'Authorization: Basic dG9tJTQwZXhhbXBsZS5jb206dG9tY2F0' http://localhost:8080/profile +> GET /profile HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.64.1 +> Accept: */* +> Authorization: Basic dG9tJTQwZXhhbXBsZS5jb206dG9tY2F0 +> +< HTTP/1.1 200 +< Set-Cookie: JSESSIONID=CE0F4BFC394816F717443397D4FEABBE; Path=/; HttpOnly +< Content-Type: text/html;charset=UTF-8 +< Content-Language: en-CN +< Transfer-Encoding: chunked +< Date: Wed, 29 Apr 2020 00:15:50 GMT +< + +```` +...HTMLÊä³ö... +ÉÏÊöÏìӦ˵Ã÷AuthFilterÒÑÉúЧ¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\237\272\346\234\254\344\273\213\347\273\215\344\270\216\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\237\272\346\234\254\344\273\213\347\273\215\344\270\216\345\277\253\351\200\237\345\205\245\351\227\250.md" new file mode 100644 index 0000000..21a9ea3 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\237\272\346\234\254\344\273\213\347\273\215\344\270\216\345\277\253\351\200\237\345\205\245\351\227\250.md" @@ -0,0 +1,1153 @@ + + + + +## MVC Éè¼Æ¸ÅÊö + +ÔÚÔçÆÚ Java Web µÄ¿ª·¢ÖУ¬Í³Ò»°ÑÏÔʾ²ã¡¢¿ØÖƲ㡢Êý¾Ý²ãµÄ²Ù×÷È«²¿½»¸ø JSP »òÕß JavaBean À´½øÐд¦Àí£¬ÎÒÃdzÆÖ®Îª **Model1£º** + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-7b3f9cd59394b017.png) + + + + + +* **³öÏֵı׶ˣº** +* JSP ºÍ Java Bean Ö®¼äÑÏÖØñîºÏ£¬Java ´úÂëºÍ HTML ´úÂëÒ²ñîºÏÔÚÁËÒ»Æð +* ÒªÇ󿪷¢Õß²»½öÒªÕÆÎÕ Java £¬»¹ÒªÓи߳¬µÄǰ¶Ëˮƽ +* ǰ¶ËºÍºó¶ËÏ໥ÒÀÀµ£¬Ç°¶ËÐèÒªµÈ´ýºó¶ËÍê³É£¬ºó¶ËÒ²ÒÀÀµÇ°¶ËÍê³É£¬²ÅÄܽøÐÐÓÐЧµÄ²âÊÔ +* ´úÂëÄÑÒÔ¸´Óà + +ÕýÒòΪÉÏÃæµÄÖÖÖÖ±×¶Ë£¬ËùÒԺܿìÕâÖÖ·½Ê½¾Í±» Servlet + JSP + Java Bean ËùÌæ´úÁË£¬ÔçÆÚµÄ MVC Ä£ÐÍ**£¨Model2£©**¾ÍÏñÏÂͼÕâÑù£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-403a273b08fec826.png) + + + + + +Ê×ÏÈÓû§µÄÇëÇó»áµ½´ï Servlet£¬È»ºó¸ù¾ÝÇëÇóµ÷ÓÃÏàÓ¦µÄ Java Bean£¬²¢°ÑËùÓеÄÏÔʾ½á¹û½»¸ø JSP È¥Íê³É£¬ÕâÑùµÄģʽÎÒÃǾͳÆÎª MVC ģʽ¡£ + +* **M ´ú±í Ä£ÐÍ£¨Model£©** + Ä£ÐÍÊÇÊ²Ã´ÄØ£¿ Ä£Ð;ÍÊÇÊý¾Ý£¬¾ÍÊÇ dao,bean +* **V ´ú±í ÊÓͼ£¨View£©** + ÊÓͼÊÇÊ²Ã´ÄØ£¿ ¾ÍÊÇÍøÒ³, JSP£¬ÓÃÀ´Õ¹Ê¾Ä£ÐÍÖеÄÊý¾Ý +* **C ´ú±í ¿ØÖÆÆ÷£¨controller)** + ¿ØÖÆÆ÷ÊÇʲô£¿ ¿ØÖÆÆ÷µÄ×÷ÓþÍÊǰѲ»Í¬µÄÊý¾Ý(Model)£¬ÏÔʾÔÚ²»Í¬µÄÊÓͼ(View)ÉÏ£¬Servlet °çÑݵľÍÊÇÕâÑùµÄ½ÇÉ«¡£ + +> À©Õ¹ÔĶÁ£º[Web¿ª·¢Ä£Ê½](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247483775&idx=1&sn=c9d7ead744c6e0c3ab2fe55c09bbe61f&chksm=ebd7407edca0c9688f3870d895b760836101271b912899821fb35c5704fe215da2fc5daff2f9#rd) + +#### Spring MVC µÄ¼Ü¹¹ + +Ϊ½â¾ö³Ö¾Ã²ãÖÐһֱδ´¦ÀíºÃµÄÊý¾Ý¿âÊÂÎñµÄ±à³Ì£¬ÓÖΪÁËÓ­ºÏ NoSQL µÄÇ¿ÊÆáÈÆð£¬Spring MVC ¸ø³öÁË·½°¸£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-a25782fb05f315de.png) + + + + + +**´«Í³µÄÄ£ÐͲ㱻²ð·ÖΪÁËÒµÎñ²ã(Service)ºÍÊý¾Ý·ÃÎʲ㣨DAO,Data Access Object£©¡£** ÔÚ Service Ï¿ÉÒÔͨ¹ý Spring µÄÉùÃ÷ʽÊÂÎñ²Ù×÷Êý¾Ý·ÃÎʲ㣬¶øÔÚÒµÎñ²ãÉÏ»¹ÔÊÐíÎÒÃÇ·ÃÎÊ NoSQL £¬ÕâÑù¾ÍÄܹ»Âú×ãÒì¾üÍ»ÆðµÄ NoSQL µÄʹÓÃÁË£¬Ëü¿ÉÒÔ´ó´óÌá¸ß»¥ÁªÍøÏµÍ³µÄÐÔÄÜ¡£ + +* **ÌØµã£º** + ½á¹¹ËÉÉ¢£¬¼¸ºõ¿ÉÒÔÔÚ Spring MVC ÖÐʹÓø÷ÀàÊÓͼ + ËÉñîºÏ£¬¸÷¸öÄ£¿é·ÖÀë + Óë Spring Î޷켯³É + +* * * + +## Hello Spring MVC + +ÈÃÎÒÃÇÀ´Ð´Ò»ÏÂÎÒÃǵĵÚÒ»¸ö Spring MVC ³ÌÐò£º + +#### µÚÒ»²½£ºÔÚ IDEA ÖÐн¨ Spring MVC ÏîÄ¿ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-74ea4c339e8f35f8.png) + + + + + +²¢ÇÒÈ¡ÃûΪ ¡¾HelloSpringMVC¡¿£¬µã»÷¡¾Finish¡¿£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-cc6cb7d01573ceee.png) + + + + + +IDEA »á×Ô¶¯°ïÎÒÃÇÏÂÔØºÃ±ØÒªµÄ jar °ü£¬²¢ÇÒΪÎÒÃÇ´´½¨ºÃһЩĬÈϵÄĿ¼ºÍÎļþ£¬´´½¨ºÃÒÔºóÏîÄ¿½á¹¹ÈçÏ£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-f6fd3173da6e4939.png) + + + + + +#### µÚ¶þ²½£ºÐÞ¸Ä web.xml + +ÎÒÃÇ´ò¿ª web.xml £¬°´ÕÕÏÂͼÍê³ÉÐ޸ģº + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-2d8a3a7b48dbe59a.png) + + + + + +°Ñ``ÔªËØµÄÖµ¸ÄΪ / £¬±íʾҪÀ¹½ØËùÓеÄÇëÇ󣬲¢½»ÓÉSpring MVCµÄºǫ́¿ØÖÆÆ÷À´´¦Àí£¬¸ÄÍêÖ®ºó£º + + + +``` + + dispatcher + / + + +``` + + + +#### µÚÈý²½£º±à¼­ dispatcher-servlet.xml + +Õâ¸öÎļþÃûµÄ¿ªÍ· dispatcher ÓëÉÏÃæ web.xml ÖÐµÄ `` ÔªËØÅäÖÃµÄ dispatcher ¶ÔÓ¦£¬ÕâÊÇ Spring MVC µÄÓ³ÉäÅäÖÃÎļþ£¨xxx-servlet.xml£©£¬ÎÒÃDZ༭ÈçÏ£º + + + +``` + + + + + + + + helloController + + + + + + +``` + + + +#### µÚËIJ½£º±àд HelloController + +ÔÚ Package¡¾controller¡¿Ï´´½¨ ¡¾HelloController¡¿À࣬²¢ÊµÏÖ org.springframework.web.servlet.mvc.Controller ½Ó¿Ú£º + + + +``` +package controller; + +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.Controller; + +public class HelloController implements Controller{ + @Override + public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + return null; + } +} + +``` + + + +* **³öÏÖÁËÎÊÌ⣺** javax.servlet °üÕÒ²»µ½ +* **½â¾ö£º** ½«±¾µØ Tomcat ·þÎñÆ÷µÄĿ¼Ï¡¾lib¡¿Îļþ¼ÐÏ嵀 servlet-api.jar °ü¿½±´µ½¹¤³Ì¡¾lib¡¿Îļþ¼ÐÏ£¬Ìí¼ÓÒÀÀµ + +Spring MVC ͨ¹ý ModelAndView ¶ÔÏó°ÑÄ£ÐͺÍÊÓͼ½áºÏÔÚÒ»Æð + + + +``` +ModelAndView mav = new ModelAndView("index.jsp"); +mav.addObject("message", "Hello Spring MVC"); + +``` + + + +ÕâÀï±íʾÊÓͼµÄÊÇindex.jsp +Ä£ÐÍÊý¾ÝµÄÊÇ message£¬ÄÚÈÝÊÇ ¡°Hello Spring MVC¡± + + + +``` +package controller; + +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.Controller; + +public class HelloController implements Controller { + + public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + ModelAndView mav = new ModelAndView("index.jsp"); + mav.addObject("message", "Hello Spring MVC"); + return mav; + } +} + +``` + + + +#### µÚÎå²½£º×¼±¸ index.jsp + +½« index.jsp µÄÄÚÈÝÐÞ¸ÄΪ£º + + + +``` +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" isELIgnored="false"%> + +

${message}

+ +``` + + + +ÄÚÈݺܼòµ¥£¬ÓÃEl±í´ïʽÏÔʾ message µÄÄÚÈÝ¡£ + +#### µÚÁù²½£º²¿Êð Tomcat ¼°Ïà¹Ø»·¾³ + +ÔÚ¡¾Run¡¿²Ëµ¥ÏîÏÂÕÒµ½¡¾Edit Configurations¡¿ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-bcca5f5a7c097d6b.png) + + + + + +ÅäÖà Tomcat »·¾³£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-0600768275c85966.png) + + + + + +Ñ¡ÔñºÃ±¾µØµÄ Tomcat ·þÎñÆ÷£¬²¢¸ÄºÃÃû×Ö£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-5f033d1463f08d7b.png) + + + + + +ÔÚ Deployment ±êǩҳÏÂÍê³ÉÈçϲÙ×÷£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-02aa0ac9a6707685.png) + + + + + +µã»÷ OK ¾ÍºÃÁË£¬ÎÒÃǵã»÷ÓÒÉϽǵÄÈý½ÇÐν« Tomcat ·þÎñÆ÷ÔËÐÐÆðÀ´¡£ + +* **³öÏÖµÄÎÊÌ⣺** Tomcat ·þÎñÆ÷ÎÞ·¨Õý³£Æô¶¯ +* **Ô­Òò£º** Tomcat ·þÎñÆ÷ÕÒ²»µ½Ïà¹ØµÄ jar °ü +* **½â¾ö·½·¨£º** ½«¡¾lib¡¿Îļþ¼ÐÕû¸ö¼ôÌùµ½¡¾WEB-INF¡¿Ï£¬²¢ÖØÐ½¨Á¢ÒÀÀµ£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-b8fcf3da677299cf.png) + + + + + +#### µÚÆß²½£ºÖØÆô·þÎñÆ÷ + +ÖØÆô·þÎñÆ÷£¬ÊäÈëµØÖ·£ºlocalhost/hello + + + + + +![](//upload-images.jianshu.io/upload_images/7896890-390fb571e9f6ff03.png) + + + + + +> ²Î¿¼×ÊÁÏ£º[Spring MVC ½Ì³Ì(how2j.cn)](http://how2j.cn/k/springmvc/springmvc-springmvc/615.html#step1891) + +* * * + +## ¸ú×Ù Spring MVC µÄÇëÇó + +ÿµ±Óû§ÔÚ Web ä¯ÀÀÆ÷Öеã»÷Á´½Ó»òÕßÌá½»±íµ¥µÄʱºò£¬ÇëÇó¾Í¿ªÊ¼¹¤×÷ÁË£¬ÏñÊÇÓʵÝÔ±Ò»Ñù£¬´ÓÀ뿪ä¯ÀÀÆ÷¿ªÊ¼µ½»ñÈ¡ÏìÓ¦·µ»Ø£¬Ëü»á¾­ÀúºÜ¶àÕ¾µã£¬ÔÚÿһ¸öÕ¾µã¶¼»áÁôÏÂһЩÐÅϢͬʱҲ»á´øÉÏÆäËûÐÅÏ¢£¬ÏÂͼΪ Spring MVC µÄÇëÇóÁ÷³Ì£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-65ef874ad7da59a2.png) + + + + + +#### µÚÒ»Õ¾£ºDispatcherServlet + +´ÓÇëÇóÀ뿪ä¯ÀÀÆ÷ÒԺ󣬵ÚÒ»Õ¾µ½´ïµÄ¾ÍÊÇ DispatcherServlet£¬¿´Ãû×ÖÕâÊÇÒ»¸ö Servlet£¬Í¨¹ý J2EE µÄѧϰ£¬ÎÒÃÇÖªµÀ Servlet ¿ÉÒÔÀ¹½Ø²¢´¦Àí HTTP ÇëÇó£¬DispatcherServlet »áÀ¹½ØËùÓеÄÇëÇ󣬲¢ÇÒ½«ÕâЩÇëÇó·¢Ë͸ø Spring MVC ¿ØÖÆÆ÷¡£ + + + +``` + + dispatcher + org.springframework.web.servlet.DispatcherServlet + 1 + + + dispatcher + + / + + +``` + + + +* **DispatcherServlet µÄÈÎÎñ¾ÍÊÇÀ¹½ØÇëÇó·¢Ë͸ø Spring MVC ¿ØÖÆÆ÷¡£** + +#### µÚ¶þÕ¾£º´¦ÀíÆ÷Ó³É䣨HandlerMapping£© + +* **ÎÊÌ⣺** µäÐ͵ÄÓ¦ÓóÌÐòÖпÉÄÜ»áÓжà¸ö¿ØÖÆÆ÷£¬ÕâЩÇëÇóµ½µ×Ó¦¸Ã·¢¸øÄÄÒ»¸ö¿ØÖÆÆ÷ÄØ£¿ + +ËùÒÔ DispatcherServlet »á²éѯһ¸ö»ò¶à¸ö´¦ÀíÆ÷Ó³ÉäÀ´È·¶¨ÇëÇóµÄÏÂÒ»Õ¾ÔÚÄÄÀ´¦ÀíÆ÷Ó³Éä»á**¸ù¾ÝÇëÇóËùЯ´øµÄ URL ÐÅÏ¢À´½øÐоö²ß**£¬ÀýÈçÉÏÃæµÄÀý×ÓÖУ¬ÎÒÃÇͨ¹ýÅäÖà simpleUrlHandlerMapping À´½« /hello µØÖ·½»¸ø helloController ´¦Àí£º + + + +``` + + + + + helloController + + + + + +``` + + + +#### µÚÈýÕ¾£º¿ØÖÆÆ÷ + +Ò»µ©Ñ¡ÔñÁ˺ÏÊʵĿØÖÆÆ÷£¬ DispatcherServlet »á½«ÇëÇó·¢Ë͸øÑ¡ÖеĿØÖÆÆ÷£¬µ½ÁË¿ØÖÆÆ÷£¬ÇëÇó»áжÏÂÆä¸ºÔØ£¨Óû§Ìá½»µÄÇëÇ󣩵ȴý¿ØÖÆÆ÷´¦ÀíÍêÕâЩÐÅÏ¢£º + + + +``` +public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + // ´¦ÀíÂß¼­ + .... +} + +``` + + + +#### µÚËÄÕ¾£º·µ»Ø DispatcherServlet + +µ±¿ØÖÆÆ÷ÔÚÍê³ÉÂß¼­´¦Àíºó£¬Í¨³£»á²úÉúһЩÐÅÏ¢£¬ÕâЩÐÅÏ¢¾ÍÊÇÐèÒª·µ»Ø¸øÓû§²¢ÔÚä¯ÀÀÆ÷ÉÏÏÔʾµÄÐÅÏ¢£¬ËüÃDZ»³ÆÎª**Ä£ÐÍ£¨Model£©**¡£½ö½ö·µ»ØÔ­Ê¼µÄÐÅϢʱ²»¹»µÄ¡ª¡ªÕâЩÐÅÏ¢ÐèÒªÒÔÓû§ÓѺõķ½Ê½½øÐиñʽ»¯£¬Ò»°ã»áÊÇ HTML£¬ËùÒÔ£¬ÐÅÏ¢ÐèÒª·¢Ë͸øÒ»¸ö**ÊÓͼ£¨view£©**£¬Í¨³£»áÊÇ JSP¡£ + +¿ØÖÆÆ÷Ëù×öµÄ×îºóÒ»¼þʾÍÊǽ«Ä£ÐÍÊý¾Ý´ò°ü£¬²¢ÇÒ±íʾ³öÓÃÓÚäÖȾÊä³öµÄÊÓͼÃû**£¨Âß¼­ÊÓͼÃû£©¡£Ëü½ÓÏÂÀ´»á½«ÇëÇóÁ¬Í¬Ä£ÐͺÍÊÓͼÃû·¢ËÍ»Ø DispatcherServlet¡£** + + + +``` +public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + // ´¦ÀíÂß¼­ + .... + // ·µ»Ø¸ø DispatcherServlet + return mav; +} + +``` + + + +#### µÚÎåÕ¾£ºÊÓͼ½âÎöÆ÷ + +ÕâÑùÒÔÀ´£¬¿ØÖÆÆ÷¾Í²»»áºÍÌØ¶¨µÄÊÓͼÏàñîºÏ£¬´«µÝ¸ø DispatcherServlet µÄÊÓͼÃû²¢²»Ö±½Ó±íʾij¸öÌØ¶¨µÄ JSP¡££¨Êµ¼ÊÉÏ£¬ËüÉõÖÁ²»ÄÜÈ·¶¨ÊÓͼ¾ÍÊÇ JSP£©Ïà·´£¬**Ëü´«µÝµÄ½ö½öÊÇÒ»¸öÂß¼­Ãû³Æ£¬Õâ¸öÃû³Æ½«»áÓÃÀ´²éÕÒ²úÉú½á¹ûµÄÕæÕýÊÓͼ¡£** + +DispatcherServlet ½«»áʹÓÃÊÓͼ½âÎöÆ÷£¨view resolver£©À´½«Âß¼­ÊÓͼÃûÆ¥ÅäΪһ¸öÌØ¶¨µÄÊÓͼʵÏÖ£¬Ëü¿ÉÄÜÊÇÒ²¿ÉÄܲ»ÊÇ JSP + +> ÉÏÃæµÄÀý×ÓÊÇÖ±½Ó°ó¶¨µ½ÁË index.jsp ÊÓͼ + +#### µÚÁùÕ¾£ºÊÓͼ + +¼ÈÈ» DispatcherServlet ÒѾ­ÖªµÀÓÉÄĸöÊÓͼäÖȾ½á¹ûÁË£¬ÄÇÇëÇóµÄÈÎÎñ»ù±¾ÉÏÒ²¾ÍÍê³ÉÁË¡£ + +ËüµÄ×îºóÒ»Õ¾ÊÇÊÓͼµÄʵÏÖ£¬ÔÚÕâÀïËü½»¸¶Ä£ÐÍÊý¾Ý£¬ÇëÇóµÄÈÎÎñÒ²¾ÍÍê³ÉÁË¡£ÊÓͼʹÓÃÄ£ÐÍÊý¾ÝäÖȾ³ö½á¹û£¬Õâ¸öÊä³ö½á¹û»áͨ¹ýÏìÓ¦¶ÔÏ󴫵ݸø¿Í»§¶Ë¡£ + + + +``` +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" isELIgnored="false"%> + +

${message}

+ +``` + + + +* * * + +## ʹÓÃ×¢½âÅäÖà Spring MVC + +ÉÏÃæÎÒÃÇÒѾ­¶Ô Spring MVC ÓÐÁËÒ»¶¨µÄÁ˽⣬²¢ÇÒͨ¹ý XML ÅäÖõķ½Ê½´´½¨Á˵ÚÒ»¸ö Spring MVC ³ÌÐò£¬ÎÒÃÇÀ´¿´¿´»ùÓÚ×¢½âÓ¦¸ÃÔõôÍê³ÉÉÏÊö³ÌÐòµÄÅäÖ㺠+ +#### µÚÒ»²½£ºÎª HelloController Ìí¼Ó×¢½â + + + +``` +package controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class HelloController{ + + @RequestMapping("/hello") + public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + ModelAndView mav = new ModelAndView("index.jsp"); + mav.addObject("message", "Hello Spring MVC"); + return mav; + } +} + +``` + + + +°ÑʵÏֵĽӿÚÒ²¸øÈ¥µô¡£ + +* **¼òµ¥½âÊÍһϣº** +* `@Controller` ×¢½â£º + ºÜÃ÷ÏÔ£¬Õâ¸ö×¢½âÊÇÓÃÀ´ÉùÃ÷¿ØÖÆÆ÷µÄ£¬µ«Êµ¼ÊÉÏÕâ¸ö×¢½â¶Ô Spring MVC ±¾ÉíµÄÓ°Ïì²¢²»´ó¡££¨Spring ʵս˵Ëü½ö½öÊǸ¨ÖúʵÏÖ×é¼þɨÃ裬¿ÉÒÔÓà `@Component` ×¢½â´úÌæ£¬µ«ÎÒ×Ô¼º³¢ÊÔÁËһϲ¢²»ÐУ¬ÒòΪÉÏÊöÀý×ÓûÓÐÅäÖà JSP ÊÓͼ½âÎöÆ÷ÎÒ»¹×Ô¼ºÅäÁËÒ»¸öÈÔûÓгɹ¦...£© +* `@RequestMapping` ×¢½â£º + ºÜÏÔÈ»£¬Õâ¾Í±íʾ·¾¶ `/hello` »áÓ³Éäµ½¸Ã·½·¨ÉÏ + +#### µÚ¶þ²½£ºÈ¡Ïû֮ǰµÄ XML ×¢ÊÍ + +ÔÚ dispatcher-servlet.xml ÎļþÖУ¬×¢Ê͵ô֮ǰµÄÅäÖã¬È»ºóÔö¼ÓÒ»¾ä×é¼þɨÃ裺 + + + +``` + + + + + --> + + + + + + + + + + + + + +``` + + + +#### µÚÈý²½£ºÖØÆô·þÎñÆ÷ + +µ±ÅäÖÃÍê³É£¬ÖØÐÂÆô¶¯·þÎñÆ÷£¬ÊäÈë `localhost/hello` µØÖ·ÈÔÈ»ÄÜ¿´µ½Ð§¹û£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-390fb571e9f6ff03.png) + + + + + +#### @RequestMapping ×¢½âϸ½Ú + +Èç¹û `@RequestMapping` ×÷ÓÃÔÚÀàÉÏ£¬ÄÇô¾ÍÏ൱ÓÚÊǸø¸ÃÀàËùÓÐÅäÖõÄÓ³ÉäµØÖ·Ç°¼ÓÉÏÁËÒ»¸öµØÖ·£¬ÀýÈ磺 + + + +``` +@Controller +@RequestMapping("/wmyskxz") +public class HelloController { + @RequestMapping("/hello") + public ModelAndView handleRequest(....) throws Exception { + .... + } +} + +``` + + + +* Ôò·ÃÎʵØÖ·£º `localhost/wmyskxz/hello` + +* * * + +## ÅäÖÃÊÓͼ½âÎöÆ÷ + +»¹¼ÇµÃÎÒÃÇ Spring MVC µÄÇëÇóÁ÷³ÌÂð£¬ÊÓͼ½âÎöÆ÷¸ºÔð¶¨Î»ÊÓͼ£¬Ëü½ÓÊÜÒ»¸öÓÉ DispaterServlet ´«µÝ¹ýÀ´µÄÂß¼­ÊÓͼÃûÀ´Æ¥ÅäÒ»¸öÌØ¶¨µÄÊÓͼ¡£ + +* **ÐèÇó£º** ÓÐÒ»Ð©Ò³ÃæÎÒÃDz»Ï£ÍûÓû§Óû§Ö±½Ó·ÃÎʵ½£¬ÀýÈçÓÐÖØÒªÊý¾ÝµÄÒ³Ãæ£¬ÀýÈçÓÐÄ£ÐÍÊý¾ÝÖ§³ÅµÄÒ³Ãæ¡£ +* **Ôì³ÉµÄÎÊÌ⣺** + ÎÒÃÇ¿ÉÒÔÔÚ¡¾web¡¿¸ùĿ¼Ï·ÅÖÃÒ»¸ö¡¾test.jsp¡¿Ä£ÄâÒ»¸öÖØÒªÊý¾ÝµÄÒ³Ãæ£¬ÎÒÃÇʲô¶¼²»ÓÃ×ö£¬ÖØÐÂÆô¶¯·þÎñÆ÷£¬ÍøÒ³ÖÐÊäÈë `localhost/test.jsp` ¾ÍÄܹ»Ö±½Ó·ÃÎʵ½ÁË£¬Õâ»áÔì³É**Êý¾Ýй¶**... + ÁíÍâÎÒÃÇ¿ÉÒÔÖ±½ÓÊäÈë `localhost/index.jsp` ÊÔÊÔ£¬¸ù¾ÝÎÒÃÇÉÏÃæµÄ³ÌÐò£¬Õâ»áÊÇÒ»¸ö¿Õ°×µÄÒ³Ãæ£¬ÒòΪ²¢Ã»ÓлñÈ¡µ½ `${message}` ²ÎÊý¾ÍÖ±½Ó·ÃÎÊÁË£¬Õâ»á**Ó°ÏìÓû§ÌåÑé** + +#### ½â¾ö·½°¸ + +ÎÒÃǽ«ÎÒÃÇµÄ JSP ÎļþÅäÖÃÔÚ¡¾WEB-INF¡¿Îļþ¼ÐÖеġ¾page¡¿Îļþ¼ÐÏ£¬¡¾WEB-INF¡¿ÊÇ Java Web ÖÐĬÈϵݲȫĿ¼£¬ÊDz»ÔÊÐíÓû§Ö±½Ó·ÃÎʵÄ_£¨Ò²¾ÍÊÇÄã˵Äãͨ¹ý `localhost/WEB-INF/` ÕâÑùµÄ·½Ê½ÊÇÓÀÔ¶·ÃÎʲ»µ½µÄ£©_ + +µ«ÊÇÎÒÃÇÐèÒª½«Õâ¸æË߸øÊÓͼ½âÎöÆ÷£¬ÎÒÃÇÔÚ dispatcher-servlet.xml ÎļþÖÐ×öÈçÏÂÅäÖ㺠+ + + +``` + + + + + +``` + + + +ÕâÀïÅäÖÃÁËÒ»¸ö Spring MVC ÄÚÖõÄÒ»¸öÊÓͼ½âÎöÆ÷£¬¸Ã½âÎöÆ÷ÊÇ×ñÑ­×ÅÒ»ÖÖÔ¼¶¨£º»á**ÔÚÊÓͼÃûÉÏÌí¼Óǰ׺ºÍºó׺£¬½ø¶øÈ·¶¨Ò»¸ö Web Ó¦ÓÃÖÐÊÓͼ×ÊÔ´µÄÎïÀí·¾¶µÄ¡£**ÈÃÎÒÃÇʵ¼ÊÀ´¿´¿´Ð§¹û£º + +#### µÚÒ»²½£ºÐÞ¸Ä HelloController + +ÎÒÃǽ«´úÂëÐÞ¸Äһϣº + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-2ce49e171bd6d547.png) + + + + + +#### µÚ¶þ²½£ºÅäÖÃÊÓͼ½âÎöÆ÷£º + +°´ÕÕÉÏÊöµÄÅäÖã¬Íê³É£º + + + +``` + + + + + --> + + + + + + + + + + + + + + + + + +``` + + + +#### µÚÈý²½£º¼ôÌù index.jsp Îļþ + +ÔÚ¡¾WEB-INF¡¿Îļþ¼ÐÏÂн¨Ò»¸ö¡¾page¡¿Îļþ¼Ð£¬²¢½«¡¾index.jsp¡¿Îļþ¼ôÌùµ½ÀïÃæ£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-88995fd05ccd0f80.png) + + + + + +#### µÚËIJ½£º¸üÐÂ×ÊÔ´ÖØÆô·þÎñÆ÷ + +·ÃÎÊ `localhost/hello` ·¾¶£¬¿´µ½ÕýȷЧ¹û£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-390fb571e9f6ff03.png) + + + + + +* **Ô­Àí£º** + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-a716a3ac8f7e541d.png) + + + + + +ÎÒÃÇ´«ÈëµÄÂß¼­ÊÓͼÃûΪ index £¬ÔÙ¼ÓÉÏ ¡°`/WEB-INF/page/`¡± ǰ׺ºÍ ¡°`.jsp`¡± ºó׺£¬¾ÍÄÜÈ·¶¨ÎïÀíÊÓͼµÄ·¾¶ÁË£¬ÕâÑùÎÒÃÇÒÔºó¾Í¿ÉÒÔ½«ËùÓеÄÊÓͼ·ÅÈ롾page¡¿Îļþ¼ÐÏÂÁË£¡ + +* **×¢Ò⣺**´ËʱµÄÅäÖýöÊÇ dispatcher-servlet.xml Ï嵀 + +* * * + +## ¿ØÖÆÆ÷½ÓÊÕÇëÇóÊý¾Ý + +ʹÓÿØÖÆÆ÷½ÓÊÕ²ÎÊýÍùÍùÊÇ Spring MVC ¿ª·¢ÒµÎñÂß¼­µÄµÚÒ»²½£¬ÎªÌ½Ë÷ Spring MVC µÄ´«²Î·½Ê½£¬Îª´ËÎÒÃÇÏÈÀ´´´½¨Ò»¸ö¼òµ¥µÄ±íµ¥ÓÃÓÚÌá½»Êý¾Ý£º + + + +``` + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%> + + + + Spring MVC ´«²Î·½Ê½ + + +
+ Óû§Ãû£º
+ ÃÜÂ룺
+ +
+ + + +``` + + + +³ó¾Í³óµã¶ù°É£¬ÎÒÃǾÍÊÇÀ´²âÊÔһϣº + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-b50a42db8debde97.png) + + + + + +#### ʹÓà Servlet Ô­Éú API ʵÏÖ£º + +ÎÒÃǺÜÈÝÒ×ÖªµÀ£¬±íµ¥»áÌá½»µ½ `/param` Õâ¸öĿ¼£¬ÎÒÃÇÏÈÀ´Ê¹Óà Servlet Ô­ÉúµÄ API À´¿´¿´Äܲ»ÄÜ»ñÈ¡µ½Êý¾Ý£º + + + +``` +@RequestMapping("/param") +public ModelAndView getParam(HttpServletRequest request, + HttpServletResponse response) { + String userName = request.getParameter("userName"); + String password = request.getParameter("password"); + + System.out.println(userName); + System.out.println(password); + return null; +} + +``` + + + +²âÊԳɹ¦£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-df21058b7ef71924.png) + + + + + +#### ʹÓÃͬÃûÆ¥Å乿Ôò + +ÎÒÃÇ¿ÉÒÔ°Ñ·½·¨¶¨ÒåµÄÐβÎÃû×ÖÉèÖóɺÍǰ̨´«Èë²ÎÊýÃûÒ»ÑùµÄ·½·¨£¬À´»ñÈ¡µ½Êý¾Ý£¨Í¬ÃûÆ¥Å乿Ôò£©£º + + + +``` +@RequestMapping("/param") +public ModelAndView getParam(String userName, + String password) { + System.out.println(userName); + System.out.println(password); + return null; +} + +``` + + + +²âÊԳɹ¦£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-55a1c296c778e506.png) + + + + + +* **ÎÊÌ⣺** ÕâÑùÓÖ»áºÍǰ̨²úÉúºÜÇ¿µÄñîºÏ£¬ÕâÊÇÎÒÃDz»Ï£ÍûµÄ +* **½â¾ö£º** ʹÓà `@RequestParam("ǰ̨²ÎÊýÃû")` À´×¢È룺 + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-a649ad50866a01c5.png) + + + + + +* **`@RequestParam` ×¢½âϸ½Ú£º** + ¸Ã×¢½âÓÐÈý¸ö±äÁ¿£º`value`¡¢`required`¡¢`defaultvalue` +* `value` £ºÖ¸¶¨ `name` ÊôÐÔµÄÃû³ÆÊÇʲô£¬`value` ÊôÐÔ¶¼¿ÉÒÔĬÈϲ»Ð´ +* `required` £ºÊÇ·ñ±ØÐëÒªÓиòÎÊý£¬¿ÉÒÔÉèÖÃΪ¡¾true¡¿»òÕß¡¾false¡¿ +* `defaultvalue` £ºÉèÖÃĬÈÏÖµ + +#### ʹÓÃÄ£ÐÍ´«²Î + +* **ÒªÇó£º Ç°Ì¨²ÎÊýÃû×Ö±ØÐëºÍÄ£ÐÍÖеÄ×Ö¶ÎÃûÒ»Ñù** + +ÈÃÎÒÃÇÏÈÀ´ÎªÎÒÃÇµÄ±íµ¥´´½¨Ò»¸ö User Ä£ÐÍ£º + + + +``` +package pojo; + +public class User { + + String userName; + String password; + + /* getter and setter */ +} + +``` + + + +È»ºó²âÊÔÈÔÈ»³É¹¦£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-471d26bcb335aee6.png) + + + + + +#### ÖÐÎÄÂÒÂëÎÊÌâ + +* **×¢Ò⣺** ¸ú Servlet ÖеÄÒ»Ñù£¬¸Ã·½·¨Ö»¶Ô POST ·½·¨ÓÐЧ£¨ÒòΪÊÇÖ±½Ó´¦ÀíµÄ request£© + +ÎÒÃÇ¿ÉÒÔͨ¹ýÅäÖà Spring MVC ×Ö·û±àÂë¹ýÂËÆ÷À´Íê³É£¬ÔÚ web.xml ÖÐÌí¼Ó£º + + + +``` + + CharacterEncodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + + utf-8 + + + + CharacterEncodingFilter + /* + + +``` + + + +* * * + +## ¿ØÖÆÆ÷»ØÏÔÊý¾Ý + +ͨ¹ýÉÏÃæ£¬ÎÒÃÇÖªµÀÁËÔõô½ÓÊÜÇëÇóÊý¾Ý£¬²¢Äܽâ¾ö POST ÂÒÂëµÄÎÊÌ⣬ÄÇôÎÒÃÇÔõô»ØÏÔÊý¾ÝÄØ£¿Îª´ËÎÒÃÇÔÚ¡¾page¡¿Ï´´½¨Ò»¸ö¡¾test2.jsp¡¿£º + + + +``` + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" import="java.util.*" isELIgnored="false" %> + + + Spring MVC Êý¾Ý»ØÏÔ + + +

»ØÏÔÊý¾Ý£º${message}

+ + + +``` + + + +#### ʹÓà Servlet Ô­Éú API À´ÊµÏÖ + +ÎÒÃÇÏÈÀ´²âÊÔһϠServlet Ô­ÉúµÄ API ÊÇ·ñÄÜÍê³ÉÕâ¸öÈÎÎñ£º + + + +``` +@RequestMapping("/value") +public ModelAndView handleRequest(HttpServletRequest request, + HttpServletResponse response) { + request.setAttribute("message","³É¹¦£¡"); + return new ModelAndView("test1"); +} + +``` + + + +ÔÚä¯ÀÀÆ÷µØÖ·À¸ÖÐÊäÈ룺`localhost/value` ²âÊÔ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-66d2f24a876306e6.png) + + + + + +#### ʹÓà Spring MVC ËùÌṩµÄ ModelAndView ¶ÔÏó + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-360ce67947be817d.png) + + + + + +#### ʹÓà Model ¶ÔÏó + +ÔÚ Spring MVC ÖУ¬ÎÒÃÇͨ³£¶¼ÊÇʹÓÃÕâÑùµÄ·½Ê½À´°ó¶¨Êý¾Ý£¬ + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-685dd384904ad28f.png) + + + + + +* **ʹÓà `@ModelAttribute` ×¢½â£º** + + + +``` +@ModelAttribute +public void model(Model model) { + model.addAttribute("message", "×¢½â³É¹¦"); +} + +@RequestMapping("/value") +public String handleRequest() { + return "test1"; +} + +``` + + + +ÕâÑùд¾Í»áÔÚ·ÃÎÊ¿ØÖÆÆ÷·½·¨ handleRequest() ʱ£¬»áÊ×Ïȵ÷Óà model() ·½·¨½« `message` Ìí¼Ó½øÒ³Ãæ²ÎÊýÖÐÈ¥£¬ÔÚÊÓͼÖпÉÒÔÖ±½Óµ÷Ó㬵«ÊÇÕâÑùд»áµ¼Ö¸ÿØÖÆÆ÷ËùÓеķ½·¨¶¼»áÊ×Ïȵ÷Óà model() ·½·¨£¬µ«Í¬ÑùµÄÒ²ºÜ·½±ã£¬ÒòΪ¿ÉÒÔ¼ÓÈë¸÷ÖÖ¸÷ÑùµÄÊý¾Ý¡£ + +* * * + +## ¿Í»§¶ËÌø×ª + +Ç°Ãæ²»¹ÜÊǵØÖ· `/hello` Ìø×ªµ½ index.jsp »¹ÊÇ `/test` Ìø×ªµ½ test.jsp£¬ÕâЩ¶¼ÊÇ·þÎñ¶ËµÄÌø×ª£¬Ò²¾ÍÊÇ `request.getRequestDispatcher("µØÖ·").forward(request, response);` + +ÄÇÎÒÃÇÈçºÎ½øÐпͻ§¶ËÌø×ªÄØ£¿ÎÒÃǼÌÐøÔÚ HelloController Öбàд£º + + + +``` +@RequestMapping("/hello") +public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { + ModelAndView mav = new ModelAndView("index"); + mav.addObject("message", "Hello Spring MVC"); + return mav; +} + +@RequestMapping("/jump") +public ModelAndView jump() { + ModelAndView mav = new ModelAndView("redirect:/hello"); + return mav; +} + +``` + + + +ÎÒÃÇʹÓà `redirect:/hello` ¾Í±íʾÎÒÃÇÒªÌø×ªµ½ `/hello` Õâ¸ö·¾¶£¬ÎÒÃÇÖØÆô·þÎñÆ÷£¬ÔÚµØÖ·À¸ÖÐÊäÈ룺`localhost/jump` £¬»á×Ô¶¯Ìø×ªµ½ `/hello` ·¾¶Ï£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-390fb571e9f6ff03.png) + + + + + +Ò²¿ÉÒÔÕâÑùÓ㺠+ + + +``` +@RequestMapping("/jump") +public String jump() { + return "redirect: ./hello"; +} + +``` + + + +* * * + +## ÎļþÉÏ´« + +ÎÒÃÇÏÈÀ´»Ø¹Ëһϴ«Í³µÄÎļþÉÏ´«ºÍÏÂÔØ£º[ÕâÀï](https://www.jianshu.com/p/e7837435bf4c) + +ÎÒÃÇÔÙÀ´¿´Ò»ÏÂÔÚ Spring MVC ÖÐÈçºÎʵÏÖÎļþµÄÉÏ´«ºÍÏÂÔØ + +* **×¢Ò⣺** ÐèÒªÏȵ¼Èë `commons-io-1.3.2.jar` ºÍ `commons-fileupload-1.2.1.jar` Á½¸ö°ü + +#### µÚÒ»²½£ºÅäÖÃÉÏ´«½âÎöÆ÷ + +ÔÚ dispatcher-servlet.xml ÖÐÐÂÔöÒ»¾ä£º + + + +``` + + +``` + + + +¿ªÆô¶ÔÉÏ´«¹¦ÄܵÄÖ§³Ö + +#### µÚ¶þ²½£º±àд JSP + +ÎļþÃûΪ upload.jsp£¬ÈÔ´´½¨ÔÚ¡¾page¡¿Ï£º + + + +``` +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + ²âÊÔÎļþÉÏ´« + + +
+ + +
+ + + +``` + + + +#### µÚÈý²½£º±àд¿ØÖÆÆ÷ + +ÔÚ Package¡¾controller¡¿ÏÂн¨¡¾UploadController¡¿Àࣺ + + + +``` +package 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.MultipartFile; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class UploadController { + + @RequestMapping("/upload") + public void upload(@RequestParam("picture") MultipartFile picture) throws Exception { + System.out.println(picture.getOriginalFilename()); + } + + @RequestMapping("/test2") + public ModelAndView upload() { + return new ModelAndView("upload"); + } + +} + +``` + + + +#### µÚËIJ½£º²âÊÔ + +ÔÚä¯ÀÀÆ÷µØÖ·À¸ÖÐÊäÈ룺`localhost/test2` £¬Ñ¡ÔñÎļþµã»÷ÉÏ´«£¬²âÊԳɹ¦£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/7896890-531c47b14dbc71e5.png) + + + + + +* * * + +#### ²Î¿¼×ÊÁÏ£º + +* ¡¶Java EE »¥ÁªÍøÇáÁ¿¼¶¿ò¼ÜÕûºÏ¿ª·¢¡· +* ¡¶Spring ʵս¡· +* [How2j Spring MVC ϵÁн̳Ì](http://how2j.cn/k/springmvc/springmvc-springmvc/615.html) +* È«ÄܵİٶȺÍÍòÄܵĴóÄÔ + +* * * + +> »¶Ó­×ªÔØ£¬×ªÔØÇë×¢Ã÷³ö´¦£¡ +> ¼òÊéID£º[@ÎÒûÓÐÈý¿ÅÐÄÔà](https://www.jianshu.com/u/a40d61a49221) +> github£º[wmyskxz](https://github.com/wmyskxz/) +> »¶Ó­¹Ø×¢¹«ÖÚ΢Ðźţºwmyskxz +> ·ÖÏí×Ô¼ºµÄѧϰ & ѧϰ×ÊÁÏ & Éú»î +> ÏëÒª½»Á÷µÄÅóÓÑÒ²¿ÉÒÔ¼ÓqqȺ£º3382693 + + + +×÷ÕߣºÎÒûÓÐÈý¿ÅÐÄÔà +Á´½Ó£ºhttps://www.jianshu.com/p/91a2d0a1e45a +À´Ô´£º¼òÊé +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\246\202\344\275\225\345\256\236\347\216\260\346\226\207\344\273\266\344\270\212\344\274\240.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\246\202\344\275\225\345\256\236\347\216\260\346\226\207\344\273\266\344\270\212\344\274\240.md" new file mode 100644 index 0000000..dd0b576 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\246\202\344\275\225\345\256\236\347\216\260\346\226\207\344\273\266\344\270\212\344\274\240.md" @@ -0,0 +1,237 @@ + + + + +# Spring MVC ÎļþÉÏ´«¸ÅÊö + + + + + + + + + + +SpringÄÚÖöԶà·ÉÏ´«µÄÖ§³Ö£¬×¨ÃÅÓÃÓÚ´¦ÀíwebÓ¦ÓÃÖеÄÎļþÉÏ´«¡£Äã¿ÉÒÔͨ¹ý×¢²áÒ»¸ö¿É²å°ÎµÄ`MultipartResolver`¶ÔÏóÀ´ÆôÓöÔÎļþ¶à·ÉÏ´«µÄÖ§³Ö¡£¸Ã½Ó¿ÚÔÚ¶¨ÒåÓÚ`org.springframework.web.multipart`°üÏ¡£SpringΪ[_Ò»°ãµÄÎļþÉÏ´«_](http://jakarta.apache.org/commons/fileupload)ÌṩÁË`MultipartResolver`½Ó¿ÚµÄÒ»¸öʵÏÖ£¬ÎªServlet 3.0¶à·ÇëÇóµÄת»»ÌṩÁËÁíÒ»¸öʵÏÖ¡£ + +
+ +ĬÈÏÇé¿öÏ£¬SpringµÄ¶à·ÉÏ´«Ö§³ÖÊDz»¿ªÆôµÄ£¬ÒòΪÓÐЩ¿ª·¢ÕßÏ£ÍûÓÉ×Ô¼ºÀ´´¦Àí¶à·ÇëÇó¡£Èç¹ûÏëÆôÓÃSpringµÄ¶à·ÉÏ´«Ö§³Ö£¬ÄãÐèÒªÔÚwebÓ¦ÓõÄÉÏÏÂÎÄÖÐÌí¼ÓÒ»¸ö¶à·´«Êä½âÎöÆ÷¡£Ã¿¸ö½øÀ´µÄÇëÇ󣬽âÎöÆ÷¶¼»á¼ì²éÊDz»ÊÇÒ»¸ö¶à²¿·ÖÇëÇó¡£Èô·¢ÏÖÇëÇóÊÇÍêÕûµÄ£¬ÔòÇëÇó°´Õý³£Á÷³Ì±»´¦Àí£»Èç¹û·¢ÏÖÇëÇóÊÇÒ»¸ö¶à·ÇëÇó£¬ÔòÄãÔÚÉÏÏÂÎÄÖÐ×¢²áµÄ`MultipartResolver`½âÎöÆ÷»á±»ÓÃÀ´´¦Àí¸ÃÇëÇó¡£Ö®ºó£¬ÇëÇóÖеĶà·ÉÏ´«ÊôÐÔ¾ÍÓëÆäËûÊôÐÔÒ»Ñù±»Õý³£¶Ô´ýÁË¡£¡¾×îºóÒ»¾ä·­µÄ²»ºÃ£¬multipart·­Òë³É¶à·»¹ÊǶಿ·Ö»¹ÔÚÕå×ÃÖС£ÍûÔĶÁÕß×¢Òâ´Ë´¦¡£¡¿ + +
+ + + + + + + +# Spring MVC ʹÓÃMultipartResolverÓëCommons FileUpload´«ÊäÎļþ + + + +2018-07-26 14:28 ¸üР+ + + + + + + +ÏÂÃæµÄ´úÂëչʾÁËÈçºÎʹÓÃÒ»¸öͨÓõĶà·ÉÏ´«½âÎöÆ÷`CommonsMultipartResolver`£º + +
+ +``` + + + + + + + +``` + +µ±È»£¬ÒªÈöà·½âÎöÆ÷Õý³£¹¤×÷£¬ÄãÐèÒªÔÚclasspath·¾¶ÏÂ×¼±¸±ØÐëµÄjar°ü¡£Èç¹ûʹÓõÄÊÇͨÓõĶà·ÉÏ´«½âÎöÆ÷`CommonsMultipartResolver`£¬ÄãËùÐèÒªµÄjar°üÊÇ`commons-fileupload.jar`¡£ + +µ±SpringµÄ`DispatcherServlet`¼ì²âµ½Ò»¸ö¶à²¿·ÖÇëÇóʱ£¬Ëü»á¼¤»îÄãÔÚÉÏÏÂÎÄÖÐÉùÃ÷µÄ¶à·½âÎöÆ÷²¢°ÑÇëÇ󽻸øËü¡£½âÎöÆ÷»á°Ñµ±Ç°µÄ`HttpServletRequest`ÇëÇó¶ÔÏó°ü×°³ÉÒ»¸öÖ§³Ö¶à·ÎļþÉÏ´«µÄÇëÇó¶ÔÏó`MultipartHttpServletRequest`¡£ÓÐÁË`MultipartHttpServletRequest`¶ÔÏó£¬Äã²»½ö¿ÉÒÔ»ñÈ¡¸Ã¶à·ÇëÇóÖеÄÐÅÏ¢£¬»¹¿ÉÒÔÔÚÄãµÄ¿ØÖÆÆ÷ÖлñµÃ¸Ã¶à·ÇëÇóµÄÄÚÈݱ¾Éí¡£ + +
+ + + + + + + +# Spring MVC ´¦ÀíServlet 3.0ϵÄMultipartResolver + + + +2018-07-26 14:29 ¸üР+ + + + + + + +ҪʹÓûùÓÚServlet 3.0µÄ¶à·´«Êäת»»¹¦ÄÜ£¬Äã±ØÐëÔÚ`web.xml`ÖÐΪ`DispatcherServlet`Ìí¼ÓÒ»¸ö`multipart-config`ÔªËØ£¬»òÕßͨ¹ýServlet±à³ÌµÄ·½·¨Ê¹ÓÃ`javax.servlet.MultipartConfigElement`½øÐÐ×¢²á£¬»òÄã×Ô¼º¶¨ÖÆÁË×Ô¼ºµÄServletÀ࣬ÄÇÄã±ØÐëʹÓÃ`javax.servlet.annotation.MultipartConfig`¶ÔÆä½øÐÐ×¢½â¡£ÆäËûÖîÈç×î´óÎļþ´óС»ò´æ´¢Î»ÖõÈÅäÖÃÑ¡Ïî¶¼±ØÐëÔÚÕâ¸öServlet¼¶±ð½øÐÐ×¢²á£¬ÒòΪServlet 3.0²»ÔÊÐíÔÚ½âÎöÆ÷MultipartResolverµÄ²ã¼¶ÅäÖÃÕâЩÐÅÏ¢¡£ + +
+ +µ±Äãͨ¹ýÒÔÉÏÈÎÒ»ÖÖ·½Ê½ÆôÓÃÁËServlet 3.0¶à·´«Êäת»»¹¦ÄÜ£¬Äã¾Í¿ÉÒÔ°ÑÒ»¸ö`StandardServletMultipartResolver`½âÎöÆ÷Ìí¼Óµ½ÄãµÄSpringÅäÖÃÖÐÈ¥ÁË£º + +``` + + +``` + +
+ + + + + + +# Spring MVC ´¦Àí±íµ¥ÖеÄÎļþÉÏ´« + + + +2018-07-26 14:30 ¸üР+ + + + + + + +µ±½âÎöÆ÷`MultipartResolver`Íê³É´¦Àíʱ£¬ÇëÇó±ã»áÏñÆäËûÇëÇóÒ»Ñù±»Õý³£Á÷³Ì´¦Àí¡£Ê×ÏÈ£¬´´½¨Ò»¸ö½ÓÊÜÎļþÉÏ´«µÄ±íµ¥½«ÔÊÐíÓÃÓÚÖ±½ÓÉÏ´«Õû¸ö±íµ¥¡£±àÂëÊôÐÔ£¨`enctype="multipart/form-data"`£©ÄÜÈÃä¯ÀÀÆ÷ÖªµÀÈçºÎ¶Ô¶à·ÉÏ´«ÇëÇóµÄ±íµ¥½øÐбàÂ루encode£©¡£ + +
+ +``` + + + Upload a file please + + +

Please upload a file

+
+ + + +
+ + + +``` + +ÏÂÒ»²½ÊÇ´´½¨Ò»¸öÄÜ´¦ÀíÎļþÉÏ´«µÄ¿ØÖÆÆ÷¡£ÕâÀïÐèÒªµÄ¿ØÖÆÆ÷Óë[Ò»°ã×¢½âÁË`@Controller`µÄ¿ØÖÆÆ÷](http://docs.spring.io/spring-framework/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-ann-controller)»ù±¾Ò»Ñù£¬³ýÁËËü½ÓÊܵķ½·¨²ÎÊýÀàÐÍÊÇ`MultipartHttpServletRequest`£¬»ò`MultipartFile`¡£ + +``` +@Controller +public class FileUploadController { + + @RequestMapping(path = "/form", method = RequestMethod.POST) + public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { + + if (!file.isEmpty()) { + byte[] bytes = file.getBytes(); + // store the bytes somewhere + return "redirect:uploadSuccess"; + } + + return "redirect:uploadFailure"; + } + +} + +``` + +ÇëÁôÒâ`@RequestParam`×¢½âÊÇÈçºÎ½«·½·¨²ÎÊý¶ÔÓ¦µ½±íµ¥Öе͍ÒåµÄÊäÈë×ֶεġ£ÔÚÉÏÃæµÄÀý×ÓÖУ¬ÎÒÃÇÄõ½ÁË`byte[]`ÎļþÊý¾Ý£¬Ö»ÊÇû¶ÔËü×öÈκÎÊ¡£ÔÚʵ¼ÊÓ¦ÓÃÖУ¬Äã¿ÉÄܻὫËü±£´æµ½Êý¾Ý¿â¡¢´æ´¢ÔÚÎļþϵͳÉÏ£¬»ò×öÆäËûµÄ´¦Àí¡£ + +µ±Ê¹ÓÃServlet 3.0µÄ¶à·´«Êäת»»Ê±£¬ÄãÒ²¿ÉÒÔʹÓÃ`javax.servlet.http.Part`×÷Ϊ·½·¨²ÎÊý£º + +``` +@Controller +public class FileUploadController { + + @RequestMapping(path = "/form", method = RequestMethod.POST) + public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") Part file) { + + InputStream inputStream = file.getInputStream(); + // store bytes from uploaded file somewhere + + return "redirect:uploadSuccess"; + } + +} +``` + +
+ + + + + + + +# Spring MVC ´¦Àí¿Í»§¶Ë·¢ÆðµÄÎļþÉÏ´«ÇëÇó + + + +2018-07-26 14:30 ¸üР+ + + + + + + +ÔÚʹÓÃÁËRESTful·þÎñµÄ³¡¾°Ï£¬·Çä¯ÀÀÆ÷µÄ¿Í»§¶ËÒ²¿ÉÒÔÖ±½ÓÌá½»¶à·ÎļþÇëÇó¡£ÉÏÒ»½Ú½²ÊöµÄËùÓÐÀý×ÓÓëÅäÖÃÔÚÕâÀïÒ²¶¼Í¬ÑùÊÊÓᣵ«Óëä¯ÀÀÆ÷²»Í¬µÄÊÇ£¬Ìá½»µÄÎļþºÍ¼òµ¥µÄ±íµ¥×ֶΣ¬¿Í»§¶Ë·¢Ë͵ÄÊý¾Ý¿ÉÒÔ¸ü¼Ó¸´ÔÓ£¬Êý¾Ý¿ÉÒÔÖ¸¶¨ÎªÄ³ÖÖÌØ¶¨µÄÄÚÈÝÀàÐÍ£¨content type£©¡ª¡ª±ÈÈ磬һ¸ö¶à·ÉÏ´«ÇëÇó¿ÉÄܵÚÒ»²¿·ÖÊǸöÎļþ£¬¶øµÚ¶þ²¿·ÖÊǸöJSON¸ñʽµÄÊý¾Ý£º + +
+ +``` + POST /someUrl + Content-Type: multipart/mixed + + --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp + Content-Disposition: form-data; name="meta-data" + Content-Type: application/json; charset=UTF-8 + Content-Transfer-Encoding: 8bit + + { + "name": "value" + } + --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp + Content-Disposition: form-data; name="file-data"; filename="file.properties" + Content-Type: text/xml + Content-Transfer-Encoding: 8bit + ... File Data ... + +``` + +¶ÔÓÚÃû³ÆÎª`meta-data`µÄ²¿·Ö£¬Äã¿ÉÒÔͨ¹ý¿ØÖÆÆ÷·½·¨ÉϵÄ`@RequestParam("meta-data") String metadata`²ÎÊýÀ´»ñµÃ¡£µ«¶ÔÓÚÄDz¿·ÖÇëÇóÌåÖÐΪJSON¸ñʽÊý¾ÝµÄÇëÇó£¬Äã¿ÉÄܸüÏëͨ¹ý½ÓÊÜÒ»¸ö¶ÔÓ¦µÄÇ¿ÀàÐͶÔÏ󣬾ÍÏñ`@RequestBody`ͨ¹ý`HttpMessageConverter`½«Ò»°ãÇëÇóµÄÇëÇóÌåת»»³ÉÒ»¸ö¶ÔÏóÒ»Ñù¡£ + +ÕâÊÇ¿ÉÄܵģ¬Äã¿ÉÒÔʹÓÃ`@RequestPart`×¢½âÀ´ÊµÏÖ£¬¶ø·Ç`@RequestParam`¡£¸Ã×¢½â½«Ê¹µÃÌØ¶¨¶à·ÇëÇóµÄÇëÇóÌå±»´«¸ø`HttpMessageConverter`£¬²¢ÇÒÔÚת»»Ê±¿¼ÂǶà·ÇëÇóÖв»Í¬µÄÄÚÈÝÀàÐͲÎÊý`'Content-Type'`£º + +``` +@RequestMapping(path = "/someUrl", method = RequestMethod.POST) +public String onSubmit(@RequestPart("meta-data") MetaData metadata, @RequestPart("file-data") MultipartFile file) { + + // ... + +} + +``` + +Çë×¢Òâ`MultipartFile`·½·¨²ÎÊýÊÇÈçºÎÄܹ»ÔÚ`@RequestParam`»ò`@RequestPart`×¢½âÏ»¥Óõģ¬Á½ÖÖ·½·¨¶¼ÄÜÄõ½Êý¾Ý¡£µ«£¬ÕâÀïµÄ·½·¨²ÎÊý`@RequestPart("meta-data") MetaData`Ôò»áÒòΪÇëÇóÖеÄÄÚÈÝÀàÐÍÇëÇóÍ·`'Content-Type'`±»¶ÁÈë³ÉΪJSONÊý¾Ý£¬È»ºóÔÙͨ¹ý`MappingJackson2HttpMessageConverter`±»×ª»»³ÉÌØ¶¨µÄ¶ÔÏó¡£ + +
+ + + + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\270\270\350\247\201\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\270\270\350\247\201\346\263\250\350\247\243.md" new file mode 100644 index 0000000..6bd5272 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC/SpringMVC\345\270\270\350\247\201\346\263\250\350\247\243.md" @@ -0,0 +1,232 @@ +## 1. ¸ÅÊö + ÔÚ±¾½Ì³ÌÖУ¬ÎÒÃǽ«Ì½Ë÷ org.springframework.web.bind.annotation °üÖÐµÄ Spring Web ×¢ÊÍ¡£ + +## 2. @RequestMapping + +¼òµ¥µØËµ£¬@RequestMapping ±ê¼Ç@Controller ÀàÄÚ²¿µÄÇëÇó´¦Àí³ÌÐò·½·¨£» Ëü¿ÉÒÔʹÓÃÅäÖ㺠+ +·¾¶£¬»òÆä±ðÃû¡¢Ãû³ÆºÍÖµ£º·½·¨Ó³Éäµ½Äĸö URL +·½·¨£º¼æÈÝµÄ HTTP ·½·¨ +params£º¸ù¾Ý HTTP ²ÎÊýµÄ´æÔÚ¡¢²»´æÔÚ»òÖµ¹ýÂËÇëÇó +headers£º¸ù¾Ý HTTP ±êÍ·µÄ´æÔÚ¡¢²»´æÔÚ»òÖµ¹ýÂËÇëÇó +ÏûºÄ£º¸Ã·½·¨¿ÉÒÔÔÚ HTTP ÇëÇóÕýÎÄÖÐÏûºÄÄÄЩýÌåÀàÐÍ +produces£º¸Ã·½·¨¿ÉÒÔÔÚ HTTP ÏìÓ¦ÕýÎÄÖÐÉú³ÉÄÄЩýÌåÀàÐÍ +ÕâÊÇÒ»¸ö¼òµ¥µÄʾÀý£º + +```` +@Controller +class VehicleController { + + @RequestMapping(value = "/vehicles/home", method = RequestMethod.GET) + String home() { + return "home"; + } +} +```` +Èç¹ûÎÒÃÇÔÚÀ༶±ðÓ¦ÓôË×¢ÊÍ£¬ÎÒÃÇ¿ÉÒÔΪ @Controller ÀàÖеÄËùÓд¦Àí³ÌÐò·½·¨ÌṩĬÈÏÉèÖᣠΨһµÄÀýÍâÊÇ Spring ²»»áʹÓ÷½·¨¼¶±ðÉèÖø²¸Çµ«»á¸½¼ÓÁ½¸ö·¾¶²¿·ÖµÄ URL¡£ + +ÀýÈ磬ÏÂÃæµÄÅäÖúÍÉÏÃæµÄЧ¹ûÊÇÒ»ÑùµÄ£º + +```` +@Controller +@RequestMapping(value = "/vehicles", method = RequestMethod.GET) +class VehicleController { + + @RequestMapping("/home") + String home() { + return "home"; + } +} +```` + +´ËÍ⣬@GetMapping¡¢@PostMapping¡¢@PutMapping¡¢@DeleteMapping ºÍ@PatchMapping ÊÇ@RequestMapping µÄ²»Í¬±äÌ壬ÆäHTTP ·½·¨ÒÑ·Ö±ðÉèÖÃΪGET¡¢POST¡¢PUT¡¢DELETE ºÍPATCH¡£ + +ÕâЩ´Ó Spring 4.3 °æ±¾¿ªÊ¼¿ÉÓᣠ+ +## 3 @RequestBody + +ÈÃÎÒÃǼÌÐø@RequestBody¡ª¡ªËü½« HTTP ÇëÇóµÄÖ÷ÌåÓ³Éäµ½Ò»¸ö¶ÔÏó£º + +```` +@PostMapping("/save") +void saveVehicle(@RequestBody Vehicle vehicle) { +// ... +} +```` +·´ÐòÁл¯ÊÇ×Ô¶¯µÄ£¬È¡¾öÓÚÇëÇóµÄÄÚÈÝÀàÐÍ¡£ + +## 4 @PathVariable +½ÓÏÂÀ´ËµËµ@PathVariable¡£ + +´Ë×¢ÊÍָʾ·½·¨²ÎÊý°ó¶¨µ½ URI Ä£°å±äÁ¿¡£ ÎÒÃÇ¿ÉÒÔʹÓà @RequestMapping ×¢ÊÍÖ¸¶¨ URI Ä£°å£¬²¢Ê¹Óà @PathVariable ½«·½·¨²ÎÊý°ó¶¨µ½Ä£°å²¿·ÖÖ®Ò»¡£ + +ÎÒÃÇ¿ÉÒÔʹÓÃÃû³Æ»òÆä±ðÃû£¬¼´Öµ²ÎÊýÀ´ÊµÏÖÕâÒ»µã£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable("id") long id) { +// ... +} +```` +Èç¹ûÄ£°åÖв¿·ÖµÄÃû³ÆÓë·½·¨²ÎÊýµÄÃû³ÆÏàÆ¥Å䣬ÎÒÃǾͲ»±ØÔÚ×¢ÊÍÖÐÖ¸¶¨Ëü£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable long id) { +// ... +} +```` +´ËÍ⣬ÎÒÃÇ¿ÉÒÔͨ¹ý½«ËùÐèµÄ²ÎÊýÉèÖÃΪ false À´½«Â·¾¶±äÁ¿±ê¼ÇΪ¿ÉÑ¡£º + +```` +@RequestMapping("/{id}") +Vehicle getVehicle(@PathVariable(required = false) long id) { +// ... +} +```` +## 5. @RequestParam + We use @RequestParam for accessing HTTP request parameters: +```` +@RequestMapping +Vehicle getVehicleByParam(@RequestParam("id") long id) { +// ... +} +```` +Ëü¾ßÓÐÓë @PathVariable ×¢ÊÍÏàͬµÄÅäÖÃÑ¡Ïî¡£ + +³ýÁËÕâЩÉèÖÃÖ®Í⣬µ± Spring ÔÚÇëÇóÖз¢ÏÖûÓÐÖµ»òΪ¿Õֵʱ£¬ÎÒÃÇ¿ÉÒÔʹÓà @RequestParam Ö¸¶¨×¢ÈëÖµ¡£ Ϊ´Ë£¬ÎÒÃDZØÐëÉèÖà defaultValue ²ÎÊý¡£ + +ÌṩĬÈÏÖµÒþʽÉèÖà required Ϊ false£º +```` +@RequestMapping("/buy") +Car buyCar(@RequestParam(defaultValue = "5") int seatCount) { +// ... +} +```` +³ýÁ˲ÎÊýÖ®Í⣬ÎÒÃÇ»¹¿ÉÒÔ·ÃÎÊÆäËû HTTP ÇëÇ󲿷֣ºcookie ºÍ±êÍ·¡£ + +ÎÒÃÇ¿ÉÒÔ·Ö±ðʹÓÃ×¢½â@CookieValue ºÍ@RequestHeader À´·ÃÎÊËüÃÇ¡£ + + +## 6. Response Handling Annotations +ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«¿´µ½ÔÚ Spring MVC ÖвÙ×÷ HTTP ÏìÓ¦µÄ×î³£¼û×¢ÊÍ¡£ + +### 6.1 @ResponseBody +Èç¹ûÎÒÃÇÓÃ@ResponseBody ±ê¼ÇÇëÇó´¦Àí³ÌÐò·½·¨£¬Spring »á½«·½·¨µÄ½á¹ûÊÓΪÏìÓ¦±¾Éí£º + +```` +@ResponseBody +@RequestMapping("/hello") +String hello() { +return "Hello World!"; +} +```` +Èç¹ûÎÒÃÇÓÃÕâ¸ö×¢½âÀ´×¢½â @Controller À࣬ËùÓÐÇëÇó´¦Àí³ÌÐò·½·¨¶¼½«Ê¹ÓÃËü¡£ + +### 6.2 @ExceptionHandler + +ʹÓôË×¢ÊÍ£¬ÎÒÃÇ¿ÉÒÔÉùÃ÷Ò»¸ö×Ô¶¨Òå´íÎó´¦Àí³ÌÐò·½·¨¡£ µ±ÇëÇó´¦Àí³ÌÐò·½·¨Å׳öÈκÎÖ¸¶¨µÄÒ쳣ʱ£¬Spring µ÷Óô˷½·¨¡£ + +²¶»ñµÄÒì³£¿ÉÒÔ×÷Ϊ²ÎÊý´«µÝ¸ø·½·¨£º +```` +@ExceptionHandler(IllegalArgumentException.class) +void onIllegalArgumentException(IllegalArgumentException exception) { +// ... +} +```` + +### 6.3 @ResponseStatus +Èç¹ûÎÒÃÇʹÓôË×¢ÊͶÔÇëÇó´¦Àí³ÌÐò·½·¨½øÐÐ×¢ÊÍ£¬Ôò¿ÉÒÔÖ¸¶¨ÏìÓ¦µÄËùÐè HTTP ״̬¡£ ÎÒÃÇ¿ÉÒÔʹÓà code ²ÎÊý»òÆä±ðÃû value ²ÎÊýÀ´ÉùÃ÷״̬´úÂë¡£ + +´ËÍ⣬ÎÒÃÇ¿ÉÒÔʹÓà reason ²ÎÊýÌṩԭÒò¡£ + +ÎÒÃÇÒ²¿ÉÒÔ½«ËüÓë@ExceptionHandler Ò»ÆðʹÓ㺠+ +@ExceptionHandler(IllegalArgumentException.class) +@ResponseStatus(HttpStatus.BAD_REQUEST) +void onIllegalArgumentException(IllegalArgumentException exception) { +// ... +} + +ÓÐ¹Ø HTTP ÏìӦ״̬µÄ¸ü¶àÐÅÏ¢£¬Çë·ÃÎʱ¾ÎÄ¡£ + +## 7 ÆäËû Web×¢½â +һЩעÊͲ»Ö±½Ó¹ÜÀí HTTP ÇëÇó»òÏìÓ¦¡£ ÔÚ½ÓÏÂÀ´µÄ²¿·ÖÖУ¬ÎÒÃǽ«½éÉÜ×î³£¼ûµÄ¡£ + +### 7.1 @Controller +ÎÒÃÇ¿ÉÒÔʹÓÃ@Controller ¶¨ÒåÒ»¸öSpring MVC ¿ØÖÆÆ÷¡£ Óйظü¶àÐÅÏ¢£¬Çë·ÃÎÊÎÒÃǹØÓÚ Spring Bean Annotations µÄÎÄÕ¡£ + +### 7.2 @RestController +@RestController ½áºÏÁË@Controller ºÍ@ResponseBody¡£ + +Òò´Ë£¬ÒÔÏÂÉùÃ÷ÊǵÈЧµÄ£º + +```` +@Controller +@ResponseBody +class VehicleRestController { +// ... +} +```` + +```` +@RestController +class VehicleRestController { +// ... +} +```` +### 7.3 @ModelAttribute +ͨ¹ýÕâ¸ö×¢½â£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÌṩģÐͼüÀ´·ÃÎÊÒѾ­´æÔÚÓÚ MVC @Controller Ä£ÐÍÖеÄÔªËØ£º + +```` +@PostMapping("/assemble") +void assembleVehicle(@ModelAttribute("vehicle") Vehicle vehicleInModel) { +// ... +} +```` +Óë@PathVariable ºÍ@RequestParam Ò»Ñù£¬Èç¹û²ÎÊý¾ßÓÐÏàͬµÄÃû³Æ£¬ÎÒÃDz»±ØÖ¸¶¨Ä£Ðͼü£º + +```` +@PostMapping("/assemble") +void assembleVehicle(@ModelAttribute Vehicle vehicle) { +// ... +} +```` +´ËÍ⣬@ModelAttribute»¹ÓÐÒ»¸öÓÃ;£ºÈç¹ûÎÒÃÇÓÃËü×¢½âÒ»¸ö·½·¨£¬Spring»á×Ô¶¯½«·½·¨µÄ·µ»ØÖµÌí¼Óµ½Ä£ÐÍÖУº + +```` +@ModelAttribute("vehicle") +Vehicle getVehicle() { +// ... +} +```` +ºÍÒÔǰһÑù£¬ÎÒÃDz»±ØÖ¸¶¨Ä£Ðͼü£¬Spring ĬÈÏʹÓ÷½·¨µÄÃû³Æ£º +```` +@ModelAttribute +Vehicle vehicle() { +// ... +} +```` +ÔÚ Spring µ÷ÓÃÇëÇó´¦Àí³ÌÐò·½·¨Ö®Ç°£¬Ëü»áµ÷ÓÃÀàÖÐËùÓÐ @ModelAttribute ×¢Ê͵ķ½·¨¡£ + +ÓÐ¹Ø @ModelAttribute µÄ¸ü¶àÐÅÏ¢£¬Çë²ÎÔı¾ÎÄ¡£ + +### 7.4 @CrossOrigin +@CrossOrigin Ϊ´ø×¢Ê͵ÄÇëÇó´¦Àí³ÌÐò·½·¨ÆôÓÿçÓòͨÐÅ£º + +```` +@CrossOrigin +@RequestMapping("/hello") +String hello() { +return "Hello World!"; +} +```` +Èç¹ûÎÒÃÇÓÃËü±ê¼ÇÒ»¸öÀ࣬ËüÊÊÓÃÓÚÆäÖеÄËùÓÐÇëÇó´¦Àí³ÌÐò·½·¨¡£ + +ÎÒÃÇ¿ÉÒÔʹÓôË×¢Ê͵IJÎÊý΢µ÷ CORS ÐÐΪ¡£ + +ÓйØÏêϸÐÅÏ¢£¬Çë·ÃÎʱ¾ÎÄ¡£ + + +# ²Î¿¼ÎÄÕ +https://www.baeldung.com/spring-annotations diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" new file mode 100644 index 0000000..4ffcaa6 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/DispatcherServlet\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" @@ -0,0 +1,477 @@ +ÔÚ[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4696657 "ÉÏһƪÎÄÕÂ")ÖУ¬ÎÒÃÇͨ¹ýÒ»¸ö¼òµ¥µÄ demo ³É¹¦Æô¶¯ÁË springmvc Ó¦Óã¬ÔÚÌṩµÄ demo ÖУ¬ÎÒÃÇÖªµÀ tomcat ÔÚÆô¶¯Ê±»áµ÷Óà `MyWebApplicationInitializer#onStartup` ·½·¨£¬È»ºóÆô¶¯ spring ÈÝÆ÷¡£ÄÇô tomcat ¾¿¾¹ÊÇÈçºÎÆô¶¯ spring µÄÄØ£¿ + +### 1\. servlet ³õʼ»¯£º`DispatcherServlet#init` + +ÎÒÃÇÔÙ»ØÒäÏ `MyWebApplicationInitializer#onStartup` ·½·¨£º + +``` +@Override +public void onStartup(ServletContext servletContext) { + System.out.println("webApplicationInitializer ..."); + // ´´½¨ spring µÄ applicationContext + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.register(MvcConfig.class); + + // ʵÀý»¯ DispatcherServlet + DispatcherServlet servlet = new DispatcherServlet(context); + + // ½«DispatcherServlet×¢²áµ½servletÈÝÆ÷ + ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); + registration.setLoadOnStartup(1); + registration.addMapping("/*"); +} + +``` + +Õâ¶Î´úÂëÏÈ×¼±¸ÁËÒ»¸ö `AnnotationConfigWebApplicationContext`£¬½«Æä×÷Ϊ²ÎÊý´«Èë `DispatcherServlet` ÖУ¬È»ºóÍù servlet ÈÝÆ÷ÖÐÌí¼Ó `DispatcherServlet`¡£ÕâÑù£¬servlet ÈÝÆ÷ÔÚÆô¶¯Ê±£¬¾Í»áÆô¶¯ spring ÈÝÆ÷ÁË¡£ÕâÀï×îÖØÒªµÄ¾ÍÊÇ `DispatcherServlet`£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÕâ¸ö servlet. + +ÎÒÃÇÏÈÀ´¿´Ï `DispatcherServlet` µÄ¼Ì³Ð½á¹¹£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1df54493011b3fc9cb9cafbb944f1a88256.png) + +´ÓÉÏͼ¿ÉÒÔ¿´µ½£¬spring ÌṩµÄ¡¢¸ú servlet Ïà¹ØµÄÀàÓÐÈý¸ö£º`HttpServletBean`¡¢`FrameworkServlet` Óë `DispatcherServlet`¡£×÷Ϊ servlet£¬ÎÒÃÇÖªµÀÆä³õʼ»¯·½·¨Îª `GenericServlet#init()`£¬Ò²¾ÍÊÇ servlet µÄÈë¿Ú·½·¨£¬ÎÒÃǵķÖÎöÒ²½«´ÓÕâÀ↑ʼ¡£ + +ÓÉÓÚ `DispatcherServlet` ʵÏÖÁË `HttpServletBean`¡¢`FrameworkServlet`£¬`DispatcherServlet#init()` ʵ¼ÊÉϼ̳Ð×Ô `HttpServletBean#init`£º + +``` +@Override +public final void init() throws ServletException { + + PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); + if (!pvs.isEmpty()) { + try { + BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); + ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); + bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); + initBeanWrapper(bw); + bw.setPropertyValues(pvs, true); + } + catch (BeansException ex) { + ... + } + } + + // ³õʼ»¯ servlet bean£¬springµÄÏà¹ØÅäÖþÍÊÇÔÚÕâÀï½øÐÐµÄ + initServletBean(); +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨¼ÓÔØÁËһЩÅäÖã¬È»ºó¾Íµ÷ÓÃÁË `initServletBean`£¬²¢Ã»ÓÐ×öһЩ spring ʵÖÊÐÔµÄÄÚÈÝ¡£ÎÒÃǼÌÐø¸ú½ø£¬Ìø¹ýÆäÖв»ÖØÒªµÄ·½·¨£¬µ÷ÓÃÁ´Â·ÈçÏ£º + +``` +-HttpServletBean#init + -FrameworkServlet#initServletBean + -FrameworkServlet#initWebApplicationContext + +``` + +Ò»Ö±¸úµ½ `FrameworkServlet#initWebApplicationContext`£º + +``` +protected WebApplicationContext initWebApplicationContext() { + // »ñÈ¡ÀàÐÍΪWebServerApplicationContextµÄ¸¸ÈÝÆ÷£¬ÕâÀïµÃµ½µÄ½á¹ûΪnull + WebApplicationContext rootContext = + WebApplicationContextUtils.getWebApplicationContext(getServletContext()); + WebApplicationContext wac = null; + + if (this.webApplicationContext != null) { + // Õâ¸öwebApplicationContext£¬¾ÍÊÇÔÚMyWebApplicationInitializer#onStart·½·¨ÖÐ + // ´«ÈëµÄAnnotationConfigWebApplicationContext + wac = this.webApplicationContext; + if (wac instanceof ConfigurableWebApplicationContext) { + ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; + if (!cwac.isActive()) { + if (cwac.getParent() == null) { + cwac.setParent(rootContext); + } + // ÔÚÕâÀïµ÷Óà AbstractApplicationContext#refresh Æô¶¯ + configureAndRefreshWebApplicationContext(cwac); + } + } + } + // wac²»Îªnull,ÕâÀï²»»áÔËÐÐ + if (wac == null) { + wac = findWebApplicationContext(); + } + // wac²»Îªnull,ÕâÀï²»»áÔËÐÐ + if (wac == null) { + // ´´½¨WebApplicationContext£¬ÖØÐÂÔËÐÐAbstractApplicationContext#refresh + wac = createWebApplicationContext(rootContext); + } + + // ʵ¼ÊÉÏ£¬refreshEventReceivedΪtrue£¬if¿éµÄ´úÂë²¢²»Ö´ÐÐ + if (!this.refreshEventReceived) { + synchronized (this.onRefreshMonitor) { + // Ë¢ÐÂÓ¦ÓÃÉÏÏÂÎÄ£¬springmvcÏà¹Ø´úÂëÔÚÕâÀïÔËÐÐ + onRefresh(wac); + } + } + + if (this.publishContext) { + // ½« WebApplicationContextÊÓΪservletContext Ò»¸öÊôÐÔ£¬¼ÓÈëµ½ servletContext ÖÐ + // Ö®ºó¾Í¿ÉÒÔʹÓà + // WebApplicationContextUtils.getWebApplicationContext(ServletContext, String attrName) + // À´»ñÈ¡ + String attrName = getServletContextAttributeName(); + getServletContext().setAttribute(attrName, wac); + } + + return wac; +} + +``` + +Õâ¸ö·½·¨µÄÏà¹Ø²Ù×÷´úÂëÖÐÒÑÓÐ×¢ÊÍ£¬Êµ¼ÊÉÏÕâ¸ö·½·¨×î ÖØÒªµÄ´úÂëΪ + +``` +protected WebApplicationContext initWebApplicationContext() { + ... + // ÔÚÕâÀïµ÷Óà AbstractApplicationContext#refresh Æô¶¯ + configureAndRefreshWebApplicationContext(cwac); + ... + return wac; +} + +``` + +Ïà¹Ø·ÖÎöÈçÏ£º + +> FrameworkServlet#configureAndRefreshWebApplicationContext + +``` +protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { + if (ObjectUtils.identityToString(wac).equals(wac.getId())) { + if (this.contextId != null) { + wac.setId(this.contextId); + } + else { + // Generate default id... + wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + + '/' + getServletName()); + } + } + wac.setServletContext(getServletContext()); + wac.setServletConfig(getServletConfig()); + wac.setNamespace(getNamespace()); + // Ìí¼Óʼþ¼àÌýÆ÷£¬¼àÌýspringÆô¶¯Íê³Éʼþ + // Õâ¸ö¼àÌýÆ÷Ê®·ÖÖØÒª£¬ºóÃæ»á·ÖÎö + wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); + ConfigurableEnvironment env = wac.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); + } + // À©Õ¹µã£¬Ã»ÓÐʲô¹¦ÄÜ£¬´ýÒÔºóÀ©Õ¹ + postProcessWebApplicationContext(wac); + applyInitializers(wac); + // µ÷ÓþÍÊÇ AbstractApplicationContext.refresh + wac.refresh(); +} + +``` + +Õâ¸ö·½·¨Êµ¼ÊÉÏ»¹ÊÇÅäÖà `ConfigurableWebApplicationContext` µÄһЩÊôÐÔ£¬×îºóµ÷Óà `AbstractApplicationContext#refresh` À´Æô¶¯ spring ÈÝÆ÷¡£¹ØÓÚ `AbstractApplicationContext#refresh` µÄ·ÖÎö£¬¿ÉÒԲο¼ [spring Æô¶¯Á÷³ÌÖ®Æô¶¯Ç°µÄ×¼±¸¹¤×÷](https://my.oschina.net/funcy/blog/4633169 "springÆô¶¯Á÷³ÌÖ®Æô¶¯Ç°µÄ×¼±¸¹¤×÷")¡£ + +µ½ÕâÀspring ÈÝÆ÷¾ÍÕæÕýÆô¶¯ÁË¡£ + +### 2. `SourceFilteringListener`£ºÆô¶¯Ê¼þ¼àÌýÆ÷ + +ÕâÀïÓиöÎÊÌ⣺ÔÚ springmvc ÖУ¬ÎÒÃÇÖªµÀ spring »áʶ±ð `@Controller`£¬½« `RequestMapping`/`@PostMapping`/`@GetMapping` µÈ×¢½âÖеÄ·¾¶·âװΪһ¸ö uri£¬µÈ´ýÍⲿ·ÃÎÊ£¬µ«ÊÇÎÒÃÇһ·¿´À´£¬Ëƺõ spring ²¢Ã»ÓÐ×öÕâЩ¹¤×÷£¬ÄÇôÕⲿ·ÖµÄ¹¤×÷ÊÇÔÚÄÄÀï½øÐеÄÄØ£¿ + +ʵ¼ÊÉÏ£¬spring Õⲿ·ÖµÄ¹¤×÷ÊÇÔÚÆô¶¯¼àÌýÆ÷ÖÐÍê³ÉµÄ£¬Ò²¾ÍÊÇ + +``` +wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); + +``` + +Õâ¸ö¼àÌýÆ÷¼àÌýµÄÊÇ spring µÄÆô¶¯ ʼþ£¬½«ÔÚ spring ÈÝÆ÷Æô¶¯Íê³Éºóµ÷Óᣠ+ +¹ØÓÚ spring µÄʼþÏà¹ØÄÚÈÝ£¬¿ÉÒԲο¼ [spring Ì½ÃØÖ® spring ʼþ»úÖÆ](https://my.oschina.net/funcy/blog/4713339 "springÌ½ÃØÖ®spring ʼþ»úÖÆ")£¬ÕâÀïÖ±½Ó˵½áÂÛ£ºspring ÖÐÌṩÁË `ApplicationEventPublisher#publishEvent(Object)`£¨Ê¼þ·¢²¼Æ÷£©¡¢`ApplicationEvent`£¨Ê¼þ£©Óë `ApplicationListener` £¨Ê¼þ¼àÌýÆ÷£©£¬µ± spring ͨ¹ý `ApplicationEventPublisher#publishEvent(Object)` ·¢²¼ `ApplicationEvent`£¨Ê¼þ£©Ê±£¬`ApplicationListener` £¨Ê¼þ¼àÌýÆ÷£©½«»á¼àÌýµ½¡£ + +ÎÒÃÇÀ´¿´¿´ `SourceFilteringListener`£º + +``` +public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener { + + private final Object source; + + @Nullable + private GenericApplicationListener delegate; + + /** + * ¹¹Ôì·½·¨£¬´«Èë event Óë listener + */ + public SourceFilteringListener(Object source, ApplicationListener delegate) { + this.source = source; + this.delegate = (delegate instanceof GenericApplicationListener ? + (GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate)); + } + + /** + * ʼþ¼àÌý·½·¨ + */ + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event.getSource() == this.source) { + // µ÷ÓÃÕæÕýµÄʼþ´¦Àí + onApplicationEventInternal(event); + } + } + + /** + * ´¦Àíʼþ + */ + protected void onApplicationEventInternal(ApplicationEvent event) { + if (this.delegate == null) { + throw new IllegalStateException(...); + } + // ×îÖÕ»¹Êǵ÷Óô«ÈëµÄʼþ¼àÌýÆ÷µÄonApplicationEvent·½·¨ + this.delegate.onApplicationEvent(event); + } + + // Ê¡ÂÔÁËһЩ´úÂë + ... + +``` + +¿ÉÒÔ¿´µ½£¬`SourceFilteringListener` ͨ¹ý¹¹Ôì·½·¨´«ÈëÁË `ContextRefreshListener` µÄʵÀý£¬È»ºóÔÚ `SourceFilteringListener#onApplicationEvent` ·½·¨ÖУ¬×îÖÕµ÷ÓõÄÊÇ `ContextRefreshListener#onApplicationEvent` ·½·¨¡£ + +½ÓÏÂÀ´ÎÒÃÇÔÙÀ´¿´ `ContextRefreshListener`£º + +> FrameworkServlet.ContextRefreshListener + +``` +private class ContextRefreshListener implements ApplicationListener { + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + FrameworkServlet.this.onApplicationEvent(event); + } +} + +``` + +Õâ¸öÀàÊÇ `FrameworkServlet` µÄÄÚ²¿À࣬´úÂëºÜ¼òµ¥£¬×îÖÕµ÷ÓõÄÊÇ `FrameworkServlet#onApplicationEvent`£º + +``` +public void onApplicationEvent(ContextRefreshedEvent event) { + // ÐÞ¸Ä״̬£¬Õâ¸öºÜ¹Ø¼ü£¬ÔËÐÐÁËÕâÐÐºó£¬ + // FrameworkServlet#initWebApplicationContextÀïµÄonRefresh(...)¾Í²»»áÔËÐÐ + this.refreshEventReceived = true; + synchronized (this.onRefreshMonitor) { + // ¾ßÌåµÄÂß¼­´¦Àí + onRefresh(event.getApplicationContext()); + } +} + +``` + +½ÓÏÂÀ´¾ÍÊÇ `DispatcherServlet#onRefresh` ·½·¨ÁË£º + +``` +@Override +protected void onRefresh(ApplicationContext context) { + initStrategies(context); +} + +/** + * springmvcµÄÖÕ¼«°ÂÃØ¾ÍÔÚÕâÀïÁË + * Õâ¸ö·½·¨ÖУ¬³õʼ»¯ÁËspringmvcµÄ¸÷ÖÖ×é¼þ + */ +protected void initStrategies(ApplicationContext context) { + initMultipartResolver(context); + initLocaleResolver(context); + initThemeResolver(context); + initHandlerMappings(context); + initHandlerAdapters(context); + initHandlerExceptionResolvers(context); + initRequestToViewNameTranslator(context); + initViewResolvers(context); + initFlashMapManager(context); +} + +``` + +¿ÉÒÔ¿´µ½£¬×îÖÕÔËÐеķ½·¨ÊÇ `DispatcherServlet#initStrategies`£¬Õâ¸ö·½·¨ËäÖ»ÁÈÁÈÊýÐУ¬ÇÒ³õʼ»¯ÁË springmvc ¸÷ÒÀÀµ×é¼þ£¡ + +### 3. `DispatcherServlet#initStrategies`£º³õʼ»¯ springmvc ×é¼þ + +spring ÔÚÆô¶¯Íê³Éºó£¬»á·¢²¼Æô¶¯Íê³Éʼþ£¬È»ºóÓɼàÌýÆ÷ `SourceFilteringListener` ¼àÌýµ½¸Ãʼþºó£¬Ö´ÐмàÌýÂß¼­£¬×îÖÕµ÷Óõ½ `DispatcherServlet#initStrategies`¡£±¾½ÚÎÒÃǽ«À´·ÖÎö `DispatcherServlet#initStrategies` µÄÖ´Ðйý³Ì¡£ + +ÆäʵÕâ¸ö·½·¨ºÜ¼òµ¥£¬ÀïÃæÓÐ 9 ÐдúÂ룬ÿÐдúÂë¶¼³õʼ»¯ÁË springmvc µÄÒ»¸ö×é¼þ£¬Èç `initMultipartResolver`£º + +``` +public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; + +private void initMultipartResolver(ApplicationContext context) { + try { + // ´ÓspringÈÝÆ÷ÖлñÈ¡multipartResolver¶ÔÏó + this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); + } + catch (NoSuchBeanDefinitionException ex) { + // »ñȡʧ°Ü£¬Ä¬ÈÏΪnull + this.multipartResolver = null; + } + } +} + +``` + +`multipartResolver` ÊÇÓÃÀ´´¦ÀíÎļþÉÏ´«µÄ bean£¬ÔÚ spring ÖУ¬ÎÒÃÇ´¦ÀíÎļþÉÏ´«Ê±£¬Ò»°ã»áÏñÕâÑùÒýÈë `multipartResolver` bean£» + +``` +@Bean(name = "multipartResolver") +public MultipartResolver multipartResolver() { + CommonsMultipartResolver resolver = new CommonsMultipartResolver(); + resolver.setDefaultEncoding("UTF-8"); + resolver.setResolveLazily(true); + resolver.setMaxInMemorySize(40960); + //ÔÊÐíÉÏ´«Îļþ×î´óΪ1G + resolver.setMaxUploadSize(1024 * 1024 * 1024); + return resolver; +} + +``` + +Èç¹ûδÒýÈë `multipartResolver` bean£¬spring ĬÈÏΪ null£¬¾Í²»ÄܽøÐÐÎļþÉÏ´«ÁË¡£ + +ÔÙÀ´¿´¿´ `springmvc` `HandlerMappings` µÄ³õʼ»¯¹ý³Ì£º + +> DispatcherServlet + +``` +public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; + +private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; + +private static final Properties defaultStrategies; + +static { + try { + // ÔÚstatic¿éÖмÓÔØDispatcherServlet.propertiesÎļþ + ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, + DispatcherServlet.class); + defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); + } + catch (IOException ex) { + throw new IllegalStateException(...); + } +} + +/** + * handlerMappings ÊôÐÔ + */ +@Nullable +private List handlerMappings; + +/** + * ³õʼ»¯ HandlerMappings + * 1\. ´Óspring ÈÝÆ÷ÖлñÈ¡ HandlerMapping bean£¬ + * Èç¹û»ñÈ¡³É¹¦£¬Ôò°ÑµÃµ½µÄ½á¹û¸³Öµ¸øhandlerMappings + * 2\. Èç¹ûδ»ñµÃ£¬Ôò»ñȡĬÈ쵀 HandlerMapping bean + */ +private void initHandlerMappings(ApplicationContext context) { + this.handlerMappings = null; + if (this.detectAllHandlerMappings) { + // ¼ÓÔØËùÓÐʵÏÖHandlerMapping½Ó¿ÚµÄbean + Map matchingBeans = BeanFactoryUtils + .beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); + // ÕâÀﲻΪ¿Õ£¬»áÔËÐÐ + if (!matchingBeans.isEmpty()) { + this.handlerMappings = new ArrayList<>(matchingBeans.values()); + // ÅÅÐò£¬Spring´¦ÀíÇëÇó¾ÍÊǸù¾ÝÕâ¸öÅÅÐòµÄ½á¹û½øÐд¦Àí£¬ + // Èç¹ûµ±Ç°handlerMapping²»¿ÉÒÔ´¦ÀíÔòÅ׸øÏÂÒ»¸ö + AnnotationAwareOrderComparator.sort(this.handlerMappings); + } + } + else { + try { + HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); + this.handlerMappings = Collections.singletonList(hm); + } + catch (NoSuchBeanDefinitionException ex) { + // Ignore, we'll add a default HandlerMapping later. + } + } + if (this.handlerMappings == null) { + // Èç¹ûδÌí¼ÓhandlerMappings£¬Ôò»ñȡĬÈ쵀 handlerMappings + this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); + } +} + +/** + * »ñȡĬÈϵIJßÂÔ + */ +protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { + String key = strategyInterface.getName(); + // »ñÈ¡ÅäÖÃÎļþDispatcherServlet.propertiesÖÐĬÈ쵀 class ÅäÖà + String value = defaultStrategies.getProperty(key); + if (value != null) { + String[] classNames = StringUtils.commaDelimitedListToStringArray(value); + List strategies = new ArrayList<>(classNames.length); + for (String className : classNames) { + try { + // ʹÓ÷´Éä´´½¨bean + Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); + Object strategy = createDefaultStrategy(context, clazz); + strategies.add((T) strategy); + } + catch (ClassNotFoundException ex) { + throw new BeanInitializationException(...); + } + catch (LinkageError err) { + throw new BeanInitializationException(...); + } + } + return strategies; + } + else { + return new LinkedList<>(); + } +} + +``` + +³õʼ»¯ `HandlerMappings` ʱ£¬ + +1. ÏÈ´Ó spring ÈÝÆ÷ÖлñÈ¡ `HandlerMapping` bean£¬Èç¹û»ñÈ¡³É¹¦£¨Êµ¼ÊÉÏÕâÀïÒ²ÄÜ»ñµÃ£©£¬Ôò°ÑµÃµ½µÄ½á¹û¸³Öµ¸ø `DispatcherServlet` µÄ `handlerMappings` ÊôÐÔ£» +2. Èç¹ûδʧ°Ü£¬±íÃ÷ spring ÈÝÆ÷ÖÐδ´æÔÚ `HandlerMapping` £¬Ôò»ñȡĬÈ쵀 `HandlerMapping` bean. +3. »ñȡĬÈ쵀 `HandlerMapping` bean ʱ£¬¶ÁÈ¡ `DispatcherServlet.properties` ÅäÖã¬È»ºóʹÓ÷´ÉäʵÀý»¯¡£ + +ÎÒÃÇÀ´¿´¿´ `DispatcherServlet.properties` Îļþ£¬¸ÃÎļþλÓÚ` spring-webmvc/src/main/resources/org/springframework/web/servlet/DispatcherServlet.properties`£¬²¿·ÖÄÚÈÝÈçÏ£º + +``` +org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver + +org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver + +org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ + org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ + org.springframework.web.servlet.function.support.RouterFunctionMapping +... + +``` + +`DispatcherServlet#initStrategies` ÖÐÆäËû `initXxx()` ÄÚÈÝÀàËÆ£¬ÕâÀï¾Í²»Ò»Ò»·ÖÎöÁË¡£ + +### 4\. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË springmvc µÄÆô¶¯¹ý³Ì£¬×ܽáÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-7ab3fae1e39f545c1d7c1811351227fa434.png) + +1. servlet ÈÝÆ÷£¨ÕâÀïÊÇ tomcat£©Æô¶¯Ê±£¬Í¨¹ý spi »úÖÆÖ´ÐÐ `ServletContainerInitializer#onStartup` ·½·¨£¬¶ø springmvc ¶ÔÌṩµÄ `SpringServletContainerInitializer` ¶ÔÆä½øÐÐÁËʵÏÖ£¬ÓÚÊÇ `SpringServletContainerInitializer#onStartup` ·½·¨»á±»µ÷Óã» +2. `SpringServletContainerInitializer#onStartup` ·½·¨ÖУ¬spring »áµ÷Óà `WebApplicationInitializer#onStartup` ·½·¨£¬¶ø `MyWebApplicationInitializer` ¶ÔÆä½øÐÐÁËʵÏÖ£¬ÓÚÊÇ `MyWebApplicationInitializer#onStartup` »á±»µ÷Óã» +3. ÔÚ `MyWebApplicationInitializer#onStartup` ·½·¨ÖÐ £¬ÎÒÃÇ´´½¨ÁËÒ»¸ö `applicationContext` ¶ÔÏ󣬽«ÆäÓë `DispatcherServlet` °ó¶¨£¬È»ºó½« `DispatcherServlet` ×¢²áµ½ servlet ÈÝÆ÷ÖУ¨ÕâÀïÊÇ tomcat£©£» +4. `DispatcherServlet` ×¢²áµ½ servlet ÈÝÆ÷ÖУ¨ÕâÀïÊÇ tomcat£©ºó£¬¸ù¾Ý servlet ÉúÃüÖÜÆÚ£¬`DispatcherServlet#init` ½«»á±»µ÷Óã» +5. `DispatcherServlet#init` ÖлáÖ´ÐÐ spring ÈÝÆ÷µÄÆô¶¯¹ý³Ì£¬spring ÈÝÆ÷Æô¶¯ºó£¬»á·¢²¼Æô¶¯Íê³Éʼþ£» +6. spring Æô¶¯Íê³Éºó£¬`ContextRefreshListener` ½«»á¼àÌý spring Æô¶¯Íê³Éʼþ£¬`FrameworkServlet.ContextRefreshListener#onApplicationEvent` ·½·¨»á±»µ÷Ó㬵÷Óõ÷Óõ½ `DispatcherServlet#initStrategies`£» +7. spring ×îÖÕÔÚ `DispatcherServlet#initStrategies` Öгõʼ»¯ `MultipartResolver`¡¢`LocaleResolver` µÈ×é¼þ£¬ËùνµÄ³õʼ»¯£¬ÆäʵÊÇ»ñÈ¡»ò´´½¨¶ÔÓ¦µÄ bean£¬È»ºó¸³Öµ¸ø `DispatcherServlet` µÄÊôÐÔ¡£ + +µ½´Ë£¬springmvc Õû¸öÆô¶¯Á÷³Ì¾ÍÍê³ÉÁË¡£²»¹ýµ½´ËÎÒÃǶ¼Ã»Óп´µ½ **spring ´¦Àí `@RequestMapping` µÄÏà¹ØÁ÷³Ì**£¬ÄÇô spring ÊÇÈçºÎ´¦ÀíÕâ¸öÁ÷³ÌÄØ £¬ÏÂһƪÎÄÕ½«½ÒÏþ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4710330](https://my.oschina.net/funcy/blog/4710330) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" new file mode 100644 index 0000000..777a297 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/RequestMapping\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.md" @@ -0,0 +1,762 @@ +Ç°ÃæµÄÎÄÕÂÖУ¬ÎÒÃÇ·ÖÎöÁË `DispatcherServlet` ³õʼ»¯Á÷³Ì£¬±¾ÎĽ«À´·ÖÎö `RequestMapping` ³õʼ»¯Á÷³Ì¡£ÕâÀïËù˵µÄ `RequestMapping` ³õʼ»¯Á÷³Ì£¬Ö±¹ÛÀ´Ëµ£¬¾ÍÊÇ spring ´¦Àí `@RequestMaping` ×¢½âµÄ¹ý³Ì¡£ + +### 1\. ÔÙ̸ `@EnableWebMvc` + +ÔÚ [spring mvc Ö® springmvc demo Óë @EnableWebMvc ×¢½â ](https://my.oschina.net/funcy/blog/4696657)Ò»ÎÄÖÐÌáµ½£¬spring ͨ¹ý `@EnableWebMvc` ×¢½âÀ´ÆôÓà mvc ¹¦ÄÜ£¬×îÖÕͨ¹ý `@Import` ×¢½âΪÏîÄ¿ÒýÈëÁË `DelegatingWebMvcConfiguration.class`£¬¸ÃÀàͨ¹ý `@Bean` ×¢½âµÄ·½·¨Ïò spring ÖÐÒýÈë´óÁ¿µÄ mvc ×é¼þ£º + +* `public RequestMappingHandlerMapping requestMappingHandlerMapping(...)` +* `public PathMatcher mvcPathMatcher()` +* `public UrlPathHelper mvcUrlPathHelper()` +* ... + +Õâô×é¼þÖУ¬Óë `@RequestMaping` ×¢½âÏà¹ØµÄÀà¾ÍÊÇ `RequestMappingHandlerMapping`. + +### 2. `RequestMappingHandlerMapping#afterPropertiesSet` ·½·¨ + +`RequestMappingHandlerMapping` ÊÇ´´½¨ÊÇÔÚ `WebMvcConfigurationSupport` ÖУº + +``` +@Bean +public RequestMappingHandlerMapping requestMappingHandlerMapping( + @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, + @Qualifier("mvcConversionService") FormattingConversionService conversionService, + @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { + + // ´´½¨bean + RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); + mapping.setOrder(0); + mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); + mapping.setContentNegotiationManager(contentNegotiationManager); + mapping.setCorsConfigurations(getCorsConfigurations()); + + // ´¦Àí¸÷ÖÖÅäÖã¬ÕâÀï¾ÍÊÇÉÏһƪÎÄÕÂÖÐÌáµ½µÄgetXxx()·½·¨»ñÈ¡ÅäÖà + PathMatchConfigurer configurer = getPathMatchConfigurer(); + Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch(); + if (useSuffixPatternMatch != null) { + mapping.setUseSuffixPatternMatch(useSuffixPatternMatch); + } + Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch(); + if (useRegisteredSuffixPatternMatch != null) { + mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); + } + Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch(); + if (useTrailingSlashMatch != null) { + mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); + } + UrlPathHelper pathHelper = configurer.getUrlPathHelper(); + if (pathHelper != null) { + mapping.setUrlPathHelper(pathHelper); + } + PathMatcher pathMatcher = configurer.getPathMatcher(); + if (pathMatcher != null) { + mapping.setPathMatcher(pathMatcher); + } + Map>> pathPrefixes = configurer.getPathPrefixes(); + if (pathPrefixes != null) { + mapping.setPathPrefixes(pathPrefixes); + } + + return mapping; +} + +// ´´½¨¶ÔÏó +protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { + return new RequestMappingHandlerMapping(); +} + +``` + +Õâ¸ö·½·¨¾ÍÊÇÓÃÀ´´´½¨¶ÔÏó `RequestMappingHandlerMapping` ¶ÔÏóµÄ£¬ÏÈÊÇ´´½¨ÁËÒ»¸ö¶ÔÏó£¬È»ºóÉèÖÃÁ˸÷ÖÖÊôÐÔ¡£¶ÔÏó´´½¨ºó£¬¼ÌÐø½øÐÐ spring bean µÄÉúÃüÖÜÆÚ£¬¼Ì¶øµ÷Óà `RequestMappingHandlerMapping#afterPropertiesSet` ·½·¨£º + +``` +@Override +public void afterPropertiesSet() { + // ÅäÖÃÁËһЩÊôÐÔ + this.config = new RequestMappingInfo.BuilderConfiguration(); + this.config.setUrlPathHelper(getUrlPathHelper()); + this.config.setPathMatcher(getPathMatcher()); + this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); + this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); + this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); + this.config.setContentNegotiationManager(getContentNegotiationManager()); + // µ÷Óø¸ÀàµÄ·½·¨ + super.afterPropertiesSet(); +} + +``` + +Õâ¸ö·½·¨ÏÈÊÇ ÅäÖÃÁËһЩÊôÐÔ£¬È»ºóÔÙµ÷Óø¸ÀàµÄ `afterPropertiesSet()`£¬¼ÌÐø×·ÏÂÈ¥£º + +> AbstractHandlerMethodMapping#afterPropertiesSet + +``` +@Override +public void afterPropertiesSet() { + initHandlerMethods(); +} + +protected void initHandlerMethods() { + // µ÷ÓÃgetCandidateBeanNames()»ñÈ¡ÈÝÆ÷ÖÐËùÓÐbeanµÄbeanName£¬ + // È»ºó°¤¸ö´¦ÀíÈÝÆ÷ÖÐËùÓÐ bean + for (String beanName : getCandidateBeanNames()) { + if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { + // Öð¸ö bean ´¦Àí£¬¼ÌÐøÍùÏ¿´ + processCandidateBean(beanName); + } + } + // Õâ¸ö·½·¨½ö´òÁËÒ»¸öÈÕÖ¾£¬Ã»Ê²Ã´¹¦ÄÜ + handlerMethodsInitialized(getHandlerMethods()); +} + +``` + +spring ÔÚ´¦Àíʱ£¬»ñÈ¡ÁËÈÝÆ÷ÖÐËùÓÐ bean µÄ beanName£¬È»ºó¶Ô beanName ½øÐа¤¸ö´¦Àí£¬¼ÌÐø¿´ `AbstractHandlerMethodMapping#processCandidateBean`£º + +``` +// ´¦ÀíbeanµÄ¾ßÌåÂß¼­ +protected void processCandidateBean(String beanName) { + // »ñÈ¡ beanName ¶ÔÓ¦µÄ beanType + // 1\. Èç¹ûÊÇcglib´úÀí£¬beanType Ϊ Xxx$$EnhancerBySpringCGLIB + // 2\. Èç¹ûÊÇjdk¶¯Ì¬´úÀí£¬beanType Ϊ com.sum.proxy.$Proxy + Class beanType = null; + try { + beanType = obtainApplicationContext().getType(beanName); + } + catch (Throwable ex) { + ... + } + // isHandler: beanTypeÉÏÊÇ·ñÓÐ @Controller »ò @RequestMapping ×¢½â + if (beanType != null && isHandler(beanType)) { + // ´¦Àí handlerMethods + detectHandlerMethods(beanName); + } +} + +``` + +Õâ¸ö·½·¨»¹ÊDZȽϼòµ¥£¬Ö÷ÒªÊÇ»ñÈ¡ `beanName` ¶ÔÓ¦µÄ `beanType`£¬È»ºóÅжÏÊÇ·ñÓÐ `@Controller/@RequestMapping` ×¢½â£¬Ö®ºó¾Íµ÷Óà `AbstractHandlerMethodMapping#detectHandlerMethods` ½øÒ»²½´¦Àí¡£ + +²»¹ý¶ÔÓÚ `isHandler(Class)` ÐèÒª×ÅÖØËµÃ÷Ï£º + +1. ÄÜʶ±ð `@Controller`£¬Í¬ÑùÄÜʶ±ð `@RestController`£¬ÉõÖÁÆäËû±ê×¢ `@Controller` µÄ×¢½â£¬¼´ÄÜʶ±ðÈçÏÂ×¢½â£º + + ``` + // ±ê¼ÇÁË @Controller + @Controller + // Ê¡ÂÔÆäËû×¢½â + public @interface XxxController { + ... + } + + ``` + +2. Èç¹û `beanName` ¶ÔÓ¦ bean ÊÇ cglib ´úÀí bean£¬beanType Ϊ `Xxx$$EnhancerBySpringCGLIB`£¬ÄÜʶ±ðÆä¸¸Àà (Ò²¾ÍÊÇÄ¿±êÀà) É쵀 `@Controller/@ReestMapping`; + +3. Èç¹û `beanName` ¶ÔÓ¦µÄ bean ÊÇ jdk ¶¯Ì¬´úÀí bean£¬beanType Ϊ `com.sum.proxy.$Proxy`£¬ÄÜʶ±ðÆä¸¸½Ó¿ÚÉ쵀 `@Controller/@RequestMapping`; + +4. Èç¹û beanType ÊÇ `com.sum.proxy.$Proxy`(jdk ¶¯Ì¬´úÀíÀà)£¬** ÎÞ·¨Ê¶±ðÆäÄ¿±êÀàÉ쵀 `@Controller/@RequestMapping` ** µÄ£» + +5. ±ê×¢ `@Controller/@RequestMapping` µÄÀàҪʵÏÖ jdk ¶¯Ì¬´úÀí£¬ÐèÒª½« `@Controller/@RequestMapping` ·ÅÔÚ½Ó¿Ú¼°½Ó¿ÚµÄ·½·¨ÉÏ¡£ + +µ½ÁËÕâÀ´«Èë `AbstractHandlerMethodMapping#detectHandlerMethods` µÄ `beanType` ¶¼ÊDZê×¢ÁË `@Controller/@RequestMapping` µÄÀà»ò½Ó¿ÚÁË¡£ + +### 3. `AbstractHandlerMethodMapping#detectHandlerMethods` + +¼ÌÐø¿´¿´ `AbstractHandlerMethodMapping#detectHandlerMethods` µÄÄÚÈÝ£º + +``` +// ¼ì²âhandler·½·¨ +protected void detectHandlerMethods(Object handler) { + Class handlerType = (handler instanceof String ? + obtainApplicationContext().getType((String) handler) : handler.getClass()); + if (handlerType != null) { + // 1\. Õë¶Ôcglib´úÀí¶ÔÏ󣬵õ½Æä¸¸À࣬Ҳ¾ÍÊÇÄ¿±ê¶ÔÏóµÄÀà + Class userType = ClassUtils.getUserClass(handlerType); + // 2\. ÕâÀï»á´¦Àí userType¡¢userTypeµÄ²»°üÀ¨ObjectµÄËùÓи¸À༰ userType µÄËùÓнӿڵķ½·¨ + Map methods = MethodIntrospector.selectMethods(userType, + // 3\. ¶Ôÿ¸ö·½·¨£¬Èç¹ûÓÐ @RequestMapping£¬Ôò´´½¨ RequestMappingInfo + // ½« @RequestMapping ÐÅÏ¢·â×°µ½¸Ã¶ÔÏóÖÐ + (MethodIntrospector.MetadataLookup) method -> { + try { + // ÔÚÕâÀï´¦Àí·½·¨É쵀 @RequestMapping ×¢½â + return getMappingForMethod(method, userType); + } + catch (Throwable ex) { + ... + } + }); + methods.forEach((method, mapping) -> { + Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); + // 4\. ÔÚÕâÀォhandler¡¢mappingÓëmethod±£´æÆðÀ´ + registerHandlerMethod(handler, invocableMethod, mapping); + }); + } +} + +``` + +Õâ¸ö·½·¨×öÁËÈçϼ¸¼þÊ£º + +1. Õë¶Ô cglib ´úÀí¶ÔÏ󣬵õ½Æä¸¸À࣬Ҳ¾ÍÊÇÄ¿±ê¶ÔÏóµÄÀࣨ**ÕâÀïΪºÎÖ»´¦Àí cglib ´úÀí¶ÔÏ󣬶ø²»´¦Àí jdk ¶¯Ì¬´úÀí¶ÔÏóÄØ£¿** ´ÓÉÏÃæ¶Ô `isHandler(Class)` µÄ˵Ã÷¿ÉÖª£¬spring ²¢²»ÄÜʶ±ð jdk ¶¯Ì¬´úÀí¶ÔÏó¶ÔÓ¦ÀàÉ쵀 `@Controller/@RequestMapping` ×¢½â£¬Òò´Ë²»»áÖ´Ðе½ÕâÀ£» +2. ´¦Àí `userType`¡¢`userType` µÄ²»°üÀ¨ Object µÄËùÓи¸À༰ `userType` µÄËùÓнӿڵķ½·¨£» +3. ¶Ôÿ¸ö·½·¨£¬Èç¹ûÓÐ `@RequestMapping`£¬Ôò´´½¨ `RequestMappingInfo`£¬½« `@RequestMapping` ÐÅÏ¢·â×°µ½¸Ã¶ÔÏóÖУ» +4. ×¢²á£¬½« `handler`¡¢`mapping` Óë `method` ±£´æµ½ Map ÖС£ + +#### 3.1 ²éÕÒ·½·¨ + +ÔÚ `detectHandlerMethods` ·½·¨µÄ´¦Àí¹ý³ÌÖУ¬»á²éÕÒ `userType`¡¢`userType` µÄËùÓи¸Àࣨ²»°üÀ¨ Object£©¼° `userType` µÄËùÓнӿڵķ½·¨£¬¹ý³ÌÈçÏ£º + +> MethodIntrospector#selectMethods(Class, MethodIntrospector.MetadataLookup) + +``` +public static Map selectMethods(Class targetType, final + MetadataLookup metadataLookup) { + final Map methodMap = new LinkedHashMap<>(); + Set> handlerTypes = new LinkedHashSet<>(); + Class specificHandlerType = null; + // ·Çjdk¶¯Ì¬´úÀíÀà + if (!Proxy.isProxyClass(targetType)) { + // Èç¹ûÊÇcglib´úÀíÀ࣬»ñÈ¡Æä¾ßÌåµÄ¸¸Àà class + specificHandlerType = ClassUtils.getUserClass(targetType); + handlerTypes.add(specificHandlerType); + } + // »ñÈ¡ÀàµÄËùÓнӿڣ¬°üÀ¨½Ó¿ÚµÄ¸¸½Ó¿Ú. + handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType)); + for (Class currentHandlerType : handlerTypes) { + final Class targetClass = (specificHandlerType != null + ? specificHandlerType : currentHandlerType); + // ´¦ÀícurrentHandlerType¡¢currentHandlerTypeµÄ²»°üÀ¨ObjectµÄËùÓи¸Àà¡¢ + // currentHandlerTypeµÄËùÓнӿڵķ½·¨ + // ´¦Àíaopʱ£¬µ÷ÓõÄÒ²ÊÇÕâ¸ö·½·¨ + ReflectionUtils.doWithMethods(currentHandlerType, method -> { + Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); + T result = metadataLookup.inspect(specificMethod); + if (result != null) { + Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); + if (bridgedMethod == specificMethod || + metadataLookup.inspect(bridgedMethod) == null) { + methodMap.put(specificMethod, result); + } + } + }, ReflectionUtils.USER_DECLARED_METHODS); + } + return methodMap; +} + +``` + +#### 3.2 ´´½¨ `RequestMappingInfo` + +¶Ôÿ¸ö·½·¨£¬Èç¹ûÓÐ `@RequestMapping`£¬Ôò´´½¨ `RequestMappingInfo`£¬½« `@RequestMapping` ÐÅÏ¢·â×°µ½¸Ã¶ÔÏóÖУº + +> RequestMappingHandlerMapping#getMappingForMethod + +``` +protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { + // ´¦Àí·½·¨É쵀 @RequestMapping + RequestMappingInfo info = createRequestMappingInfo(method); + if (info != null) { + // ´¦ÀíÀàÉ쵀 @RequestMapping + RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); + if (typeInfo != null) { + // ºÏ²¢·½·¨ÓëÀàÉ쵀 @RequestMapping ½á¹û + // ÈçÀàµÄ @RequestMapping("/test")£¬·½·¨É쵀 @RequestMapping("/hello") + // ºÏ²¢ºóµÄ½á¹ûΪ /test/hello + info = typeInfo.combine(info); + } + String prefix = getPathPrefix(handlerType); + if (prefix != null) { + info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); + } + } + return info; +} + +@Nullable +private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { + // »ñÈ¡ @RequestMapping ×¢½â + RequestMapping requestMapping = AnnotatedElementUtils + .findMergedAnnotation(element, RequestMapping.class); + // ´¦ÀíÇëÇóÌõ¼þ£¬Êµ¼ÊÉÏÕâ¸ö·½·¨Îª¿Õ + RequestCondition condition = (element instanceof Class ? + getCustomTypeCondition((Class) element) : getCustomMethodCondition((Method) element)); + return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); +} + +// ´´½¨ RequestMappingInfo£¬Õâ¸ö RequestMapping ¾ÍÊÇ @RequestMapping ×¢½â +protected RequestMappingInfo createRequestMappingInfo( + RequestMapping requestMapping, @Nullable RequestCondition customCondition) { + // Æäʵ¾ÍÊǽ« @RequestMapping ×¢½â·âװΪRequestMappingInfo¶ÔÏó + RequestMappingInfo.Builder builder = RequestMappingInfo + .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) + // ÏÂÃæµÄÊôÐÔ½ÔÀ´×Ô @RequestMapping ×¢½â + .methods(requestMapping.method()) + .params(requestMapping.params()) + .headers(requestMapping.headers()) + .consumes(requestMapping.consumes()) + .produces(requestMapping.produces()) + .mappingName(requestMapping.name()); + if (customCondition != null) { + builder.customCondition(customCondition); + } + return builder.options(this.config).build(); +} + +``` + +µ½ÁËÕâÀÎÒÃǾÍÍê³ÉÁËÓÉ `@RequestMapping` µ½ `RequestMappingInfo¶ÔÏó`µÄת»¯¡£ + +ÔÙÀ´¿´¿´ `RequestMappingInfo` ³¤Ê²Ã´Ñù£º + +``` +public final class RequestMappingInfo implements RequestCondition { + // ÌṩÁ˺ܶàÊôÐÔ£¬¶ÔÓ¦×Å @RequestMappingµÄÊôÐÔ + @Nullable + private final String name; + private final PatternsRequestCondition patternsCondition; + private final RequestMethodsRequestCondition methodsCondition; + private final ParamsRequestCondition paramsCondition; + private final HeadersRequestCondition headersCondition; + private final ConsumesRequestCondition consumesCondition; + private final ProducesRequestCondition producesCondition; + private final RequestConditionHolder customConditionHolder; + + // ¹¹Ôì·½·¨£¬¾ÍÊÇÔÚ + public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns, + @Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params, + @Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes, + @Nullable ProducesRequestCondition produces, @Nullable RequestCondition custom) { + + this.name = (StringUtils.hasText(name) ? name : null); + this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition()); + this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition()); + this.paramsCondition = (params != null ? params : new ParamsRequestCondition()); + this.headersCondition = (headers != null ? headers : new HeadersRequestCondition()); + this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition()); + this.producesCondition = (produces != null ? produces : new ProducesRequestCondition()); + this.customConditionHolder = new RequestConditionHolder(custom); + } + + // builder ¹¹½¨Ä£Ê½£¬Ç°ÃæÕýÊÇʹÓÃbuilderÀ´´´½¨RequestMappingInfo¶ÔÏóµÄ + private static class DefaultBuilder implements Builder { + // Ê¡ÂÔÆäËû + ... + + // ʹÓÃbuilder()·½·¨À´´´½¨¶ÔÏó + @Override + public RequestMappingInfo build() { + ContentNegotiationManager manager = this.options.getContentNegotiationManager(); + PatternsRequestCondition patternsCondition = new PatternsRequestCondition( + this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(), + this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(), + this.options.getFileExtensions()); + // µ÷Óà RequestMappingInfo ¹¹Ôì·½·¨ + return new RequestMappingInfo(this.mappingName, patternsCondition, + new RequestMethodsRequestCondition(this.methods), + new ParamsRequestCondition(this.params), + new HeadersRequestCondition(this.headers), + new ConsumesRequestCondition(this.consumes, this.headers), + new ProducesRequestCondition(this.produces, this.headers, manager), + this.customCondition); + } + } + // Ê¡ÂÔÆäËû + ... +} + +``` + +#### 3.3 ×¢²á + +·â×°Íê `@RequestMapping` ÐÅÏ¢ºó£¬½ÓÏÂÀ´¾ÍÊǽ«½Ó¿ÚÐÅÏ¢×¢²áµ½ springmvc ÖÐÁË£º + +> RequestMappingHandlerMapping#registerHandlerMethod + +``` +@Override +protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { + // µ÷Óø¸ÀàµÄ·½·¨£¬×¢²áÏà¹ØÂß¼­ÔÚÕâÀï + super.registerHandlerMethod(handler, method, mapping); + updateConsumesCondition(mapping, method); +} +// ´¦Àí·½·¨²ÎÊýµÄ @RequestBody ×¢½â +private void updateConsumesCondition(RequestMappingInfo info, Method method) { + ConsumesRequestCondition condition = info.getConsumesCondition(); + if (!condition.isEmpty()) { + for (Parameter parameter : method.getParameters()) { + // ´¦Àí ·½·¨²ÎÊýµÄ @RequestBody ×¢½â£¬ÉèÖà BodyRequired µÄÖµ + MergedAnnotation annot = MergedAnnotations.from(parameter) + .get(RequestBody.class); + if (annot.isPresent()) { + condition.setBodyRequired(annot.getBoolean("required")); + break; + } + } + } +} + +``` + +×îÖÕ£¬·¢ÏÖ¾ßÌåµÄ×¢²áÂß¼­ÊÇÔÚ `AbstractHandlerMethodMapping#registerHandlerMethod` ÖÐÍê³ÉµÄ£¬¿´À´Õâ¸ö¾ÍÊÇ×îÖÕ·½·¨ÁË¡£ÔÚ·ÖÎöÕâ¸ö·½·¨Ç°£¬ÎÒÃÇÏÈÀ´¿´¿´ÕâÒ»²½µÃµ½µÄ `Map methods`: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ec6b4b2d2156e0e041982425173feda38fc.png) + +¿ÉÒÔ¿´µ½£¬¶ÔÓ¦µÄ `T` ¾ÍÊÇ `RequestMappingInfo` ÁË¡£ + +### 3. `AbstractHandlerMethodMapping#registerHandlerMethod` ·½·¨ + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `AbstractHandlerMethodMapping#registerHandlerMethod` ´úÂ룺 + +``` +public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMapping + implements InitializingBean { + + protected void registerHandlerMethod(Object handler, Method method, T mapping) { + this.mappingRegistry.register(mapping, handler, method); + } + + // Ê¡ÂÔÁ˺öà´úÂë + ... + + class MappingRegistry { + // ÐÅÏ¢×îÈ«µÄmap£¬°üº¬mapping, handlerMethod, directUrls, nameµÈÐÅÏ¢ + private final Map> registry = new HashMap<>(); + // ËùÓÐµÄ mapping map£¬/test/hello¡¢/test/{name} + private final Map mappingLookup = new LinkedHashMap<>(); + // Ã÷È·µÄurl map£¬Èç /test/hello + private final MultiValueMap urlLookup = new LinkedMultiValueMap<>(); + + // Ê¡ÂÔÁ˺öà´úÂë + ... + + public void register(T mapping, Object handler, Method method) { + ... + // »ñÈ¡¶ÁÐ´ËøµÄÐ´Ëø + this.readWriteLock.writeLock().lock(); + try { + // 1\. »ñÈ¡ÁË handlerMethod£¬Æäʵ¾ÍÊǽ«handler Óë method °ü×°ÁËÒ»²ã + HandlerMethod handlerMethod = createHandlerMethod(handler, method); + validateMethodMapping(handlerMethod, mapping); + // 2\. ·ÅÈë mappingLookup ÖУ¬ÀàÐÍΪ LinkedHashMap + // ÕâÊÇspringmvcÖÐÒ»¸öÖØÒªµÄmap + this.mappingLookup.put(mapping, handlerMethod); + + // 3\. »ñÈ¡url£¬·ÅÈëurlLookup£¬ÀàÐÍΪMultiValueMap£¬Õâ¸ömap ͬһkey¿ÉÒÔÓжà¸övalue + // ÕâÊÇspringmvcÖÐÁíÒ»¸öÖØÒªµÄmap + List directUrls = getDirectUrls(mapping); + for (String url : directUrls) { + this.urlLookup.add(url, mapping); + } + String name = null; + if (getNamingStrategy() != null) { + name = getNamingStrategy().getName(handlerMethod, mapping); + addMappingName(name, handlerMethod); + } + CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); + if (corsConfig != null) { + this.corsLookup.put(handlerMethod, corsConfig); + } + + // 4\. ½«mapping, handlerMethod, directUrls, nameµÈ·â×°·ÅÈëregistry£¬ + // registry ÀàÐÍΪHashMap£¬ÕâÊÇspringmvc ÖнӿÚÐÅÏ¢×îÈ«µÄÒ»¸ömap + this.registry.put(mapping, new MappingRegistration<>(mapping, + handlerMethod, directUrls, name)); + } + finally { + this.readWriteLock.writeLock().unlock(); + } + } + } +} + +``` + +¿ÉÒÔ¿´µ½£¬×¢²áµÄÂß¼­×îÖÕÊÇÔÚ `AbstractHandlerMethodMapping.MappingRegistry#register` ÖÐÍê³ÉµÄ¡£½ÓÏÂÀ´ÎÒÃǾÍÒ»²½²½·ÖÎö×¢²áÂß¼­¡£ + +#### 3.1 »ñÈ¡ `HandlerMethod` + +Ïà¹Ø´úÂëÈçÏ£º + +``` +protected HandlerMethod createHandlerMethod(Object handler, Method method) { + if (handler instanceof String) { + return new HandlerMethod((String) handler, + obtainApplicationContext().getAutowireCapableBeanFactory(), method); + } + return new HandlerMethod(handler, method); +} + +``` + +Õâ¶Î·½·¨¾ÍÊǼòµ¥µØµ÷ÓÃÁË `HandlerMethod` µÄ¹¹Ôì·½·¨£¬¼ÌÐø£º + +``` +public class HandlerMethod { + + // ÌṩÁ˷dz£¶àµÄÊôÐÔ + protected final Log logger = LogFactory.getLog(getClass()); + private final Object bean; + @Nullable + private final BeanFactory beanFactory; + private final Class beanType; + private final Method method; + private final Method bridgedMethod; + private final MethodParameter[] parameters; + @Nullable + private HttpStatus responseStatus; + @Nullable + private String responseStatusReason; + @Nullable + private HandlerMethod resolvedFromHandlerMethod; + @Nullable + private volatile List interfaceParameterAnnotations; + private final String description; + + // ¹¹Ôì·½·¨ + public HandlerMethod(Object bean, Method method) { + Assert.notNull(bean, "Bean is required"); + Assert.notNull(method, "Method is required"); + this.bean = bean; + this.beanFactory = null; + this.beanType = ClassUtils.getUserClass(bean); + this.method = method; + this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); + this.parameters = initMethodParameters(); + evaluateResponseStatus(); + this.description = initDescription(this.beanType, this.method); + } + + // Ê¡ÂÔÆäËû + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬`HandlerMethod` ÖÐÓзdz£¶àµÄÊôÐÔ£¬¹¹Ôì·½·¨Ëù×öµÄÊÂÒ²½ö½öÊǸ³Öµ¶øÒÑ¡£Óɴ˿ɿ´³ö£¬`HandlerMethod` ¾ÍÊÇ¶Ô `handler` Óë `method` µÄÒ»¸ö°ü×°¡£ + +#### 3.2 ÑéÖ¤ mapping ÊÇ·ñÖØ¸´ + +ÔÚ springmvc ʹÓÃÖУ¬Èç¹û²»Ð¡Ð͍ÒåÁËÁ½¸öÏàͬµÄ `requestMapping`£¬»á³öÏÖÈçÏÂÒì³££º + +``` +Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map +'xxxController' method xxxMethod to /xxx/xxx: There is already 'xxxControllter' +bean method xxxMethod mapped. + +``` + +Õâ¸öÒì³£¾ÍÊÇÔÚÑéÖ¤ mapping ʱ£¬·¢ÏÖÁËÖØ¸´ÁË mapping ¶ø´¥·¢µÄ£¬´úÂëÈçÏ£º + +``` +// ËùÓÐµÄ mapping map£¬/test/hello¡¢/test/{name} +private final Map mappingLookup = new LinkedHashMap<>(); + +private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) { + // ÕÒµ½ÒÑ´æÔÚµÄ method + HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping); + // ÒÑ´æÔÚµÄhandlerMethod²»Îª¿Õ£¬ÇÒ²»µÈÓÚµ±Ç° handlerMethod + if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) { + throw new IllegalStateException( + "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" + + handlerMethod + "\nto " + mapping + ": There is already '" + + existingHandlerMethod.getBean() + "' bean method\n" + + existingHandlerMethod + " mapped."); + } +} + +``` + +Ê×ÏÈÊǸù¾Ý `mapping` ´Ó `mappingLookup` ÖвéÕÒ `HandlerMethod`£¬Èç¹ûÕÒµ½ÁËÇÒÕÒµ½µÄ `handlerMethod` ²»Êǵ±Ç° `handlerMethod`£¬Ôò±íÊ¾ÖØ¸´£¬¾Í±¨Òì³£ÁË¡£ + +×îºó·Ö±ðÀ´¿´¿´ÅÐ¶Ï `HandlerMethod` Óë `RequestMappingInfo` ÊÇÈçºÎÅжÏÏàµÈµÄ£º + +> HandlerMethod#equals + +``` +@Override +public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof HandlerMethod)) { + return false; + } + HandlerMethod otherMethod = (HandlerMethod) other; + return (this.bean.equals(otherMethod.bean) && this.method.equals(otherMethod.method)); +} + +``` + +> RequestMappingInfo#equals + +``` +@Override +public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof RequestMappingInfo)) { + return false; + } + RequestMappingInfo otherInfo = (RequestMappingInfo) other; + return (this.patternsCondition.equals(otherInfo.patternsCondition) && + this.methodsCondition.equals(otherInfo.methodsCondition) && + this.paramsCondition.equals(otherInfo.paramsCondition) && + this.headersCondition.equals(otherInfo.headersCondition) && + this.consumesCondition.equals(otherInfo.consumesCondition) && + this.producesCondition.equals(otherInfo.producesCondition) && + this.customConditionHolder.equals(otherInfo.customConditionHolder)); +} + +``` + +`RequestMappingInfo` ÐèÒª¸÷ÊôÐÔÏàͬ²ÅÅжÏÏàµÈ£¬Òò´ËÏñÏÂÃæÕâÑùµÄ `@RequestMapping`£¬µÃµ½µÄ `RequestMappingInfo` ²¢²»ÏàµÈ£º + +``` +// ÒÔÏÂÈý¸ö @RequestMapping£¬ËäÈ»ÇëÇó·¾¶¶¼ÊÇ¡°/hello¡±£¬µ«Ö§³ÖµÄÇëÇó·½·¨¸÷²»Ïàͬ +// Òò´ËµÃµ½µÄ RequestMappingInfo ²¢²»ÏàµÈ + +@RequestMapping(path = "/hello") +public String hello1() { + ... +} + +@RequestMapping(path = "/hello", method = RequestMethod.GET) +public String hello2() { + ... +} + +@RequestMapping(path = "/hello", method = RequestMethod.POST) +public String hello3() { + ... +} + +``` + +#### 3.3 »ñÈ¡ `directUrls` + +springmvc ÖУ¬ÓÐÁ½ÖÖ url ÀàÐÍ: + +1. Ã÷È·µÄ url£¬Èç + + ``` + @RequestMapping("/hello") + public String hello() { + ... + } + + ``` + +2. ²»Ã÷È·µÄ url£¬Èç + + ``` + @RequestMapping("/{name}") + public String hello(@PathVariable("name") String name) { + ... + } + + ``` + +springmvc ÌṩÁËרÃÅµÄ `urlLookup` À´±£´æÃ÷È·µÄ url£¬½á¹¹ÈçÏ£º + +``` +MultiValueMap> + +``` + +ÔÙÀ´¿´¿´ springmvc ÊÇÈçºÎ»ñÈ¡Ã÷È·µÄ url µÄ: + +``` +List directUrls = getDirectUrls(mapping); + +/** + * AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls + * »ñÈ¡Ã÷È·µÄurl + */ +private List getDirectUrls(T mapping) { + List urls = new ArrayList<>(1); + // ´ÓRequestMappingInfo»ñÈ¡ËùÓÐµÄ MappingPathPattern + for (String path : getMappingPathPatterns(mapping)) { + // Åжϵõ½µÄ MappingPathPattern ÊÇ·ñΪÃ÷È·µÄurl + if (!getPathMatcher().isPattern(path)) { + urls.add(path); + } + } + return urls; +} + +/** + * RequestMappingInfoHandlerMapping#getMappingPathPatterns + * »ñÈ¡ patterns, ´Ó RequestMappingInfo »ñÈ¡ + * ʵ¼ÊÉϾÍÊÇ @RequestMapping ÖÐµÄ path() Öµ + */ +@Override +protected Set getMappingPathPatterns(RequestMappingInfo info) { + return info.getPatternsCondition().getPatterns(); +} + +/** + * AntPathMatcher#isPattern + * ÅжÏÊÇ·ñΪÃ÷È·µÄ url + * Ö»Òª°üº¬ÁË *¡¢£¿Ö®Ò»£¬»òͬʱ°üº¬{¡¢}£¬¾Í²»ÊÇÃ÷È·µÄurl + */ +@Override +public boolean isPattern(@Nullable String path) { + if (path == null) { + return false; + } + boolean uriVar = false; + for (int i = 0; i < path.length(); i++) { + char c = path.charAt(i); + if (c == '*' || c == '?') { + return true; + } + if (c == '{') { + uriVar = true; + continue; + } + if (c == '}' && uriVar) { + return true; + } + } + return false; +} + +``` + +Á÷³ÌÈçÏ£º + +1. »ñÈ¡µ±Ç° `mapping` µÄËùÓÐ `path`£¬Ò²¾ÍÊÇ `@RequestMapping` µÄ `path()` Öµ£» +2. ±éÀúµÃµ½µÄ `path`£¬ÖðÒ»ÅжÏÊÇ·ñΪÃ÷È·µÄ url (Ö»Òª°üº¬ÁË `*`¡¢`?` Ö®Ò»£¬»òͬʱ°üº¬ `{`¡¢`}`£¬¾Í²»ÊÇÃ÷È·µÄ url). + +#### 3.4 ×¢²á½Ó¿ÚÐÅÏ¢ + +×¢²á½Ó¿ÚÐÅÏ¢¾Í±È½Ï¼òµ¥ÁË£¬Æäʵ¾ÍÊÇÍù map ÖÐÌí¼ÓÊý¾Ý£¬ÕâÀï½éÉÜÈý¸ö map£º + +* `urlLookup`£ºÃ÷È·µÄ `url map`£¬ÀàÐÍΪ `MultiValueMap>`£¬`key` ΪÃ÷È·µÄ url£¬Èç `/test/hello`£¬`value` Ϊ `LinkedList`£» + +* `mappingLookup`£ºËùÓÐµÄ `mapping map`£¬ËùÓÐµÄ `@RequestMapping` ¶ÔÓ¦µÄ `RequestMappingInfo` ¶¼ÄÜÔÚÕâÀïÕÒµ½£¬°üÀ¨ `/test/hello`¡¢`/test/{name}` ¶ÔÓ¦µÄ `RequestMappingInfo`£¬ÀàÐÍΪ `Map`£» + +* `registry`£ºÐÅÏ¢×îÈ«µÄ map£¬ÀàÐÍΪ `Map>`£¬°üº¬ËùÓÐµÄ `RequestMappingInfo`£¬key Ϊ `RequestMappingInfo`£¬value Ϊ `MappingRegistration`£¬¶ø `MappingRegistration` Ϊ `mapping`, `handlerMethod`, `directUrls`, `name` µÄ°ü×°À࣬Ҳ¾ÍÊÇ˵ `MappingRegistration` °üº¬ÁË `mapping`, `handlerMethod`, `directUrls`, `name` µÈÐÅÏ¢¡£ + +Àí½âÕâЩ map ºó£¬×¢²á¾ÍÏ൱¼òµ¥ÁË£¬¾ÍÊǼòµ¥µØµ÷ÓÃÏ `Map#put` ·½·¨£¬¾Í²»¶à˵ÁË¡£ + +### 4\. ×ܽá + +±¾ÎÄ·ÖÎöÁË spring ´¦Àí´¦Àí `@RequestMapping` ×¢½âµÄÁ÷³Ì£¬Õⲿ·ÖÁ÷³ÌÔÚ `RequestMappingHandlerMapping#afterPropertiesSet` ·½·¨ÖУ¬Á÷³ÌÈçÏ£º + +1. »ñÈ¡ÈÝÆ÷ÖÐËùÓÐ bean µÄ `beanName`£¬Öð¸ö´¦Àí£¬´¦Àí·½·¨¼ûµÚ 2 ²½£» +2. ÕÒµ½Óë `beanName` ¶ÔÓ¦µÄ `beanType`£¬ÅÐ¶ÏÆäÉÏÊÇ·ñÓÐ `@Controller/@RequestMapping` ×¢½â£» +3. ¶Ô°üº¬ `@Controller/@RequestMapping` µÄ `beanType`£¬ÕÒµ½ÓÐ `@RequestMapping` ×¢½âµÄ·½·¨£¬½«Æä `@RequestMapping` ×¢½â·âװΪ `RequestMappingInfo`£¬ÕâÒ»²½µÃµ½µÄ½á¹ûΪһ¸ö map£º`Map`£» +4. ½« `beanName`¡¢`beanType` Óë `Map` ×¢²áµ½ springmvc ÖУ¬ÕâÀïÓÐÈý¸ö±È½ÏÖØÒªµÄ map£º + * `MultiValueMap>`¡¢ + * `Map`(`HandlerMethod` Ϊ `Method` µÄ°ü×°Àà) + * `Map>`(`MappingRegistration` Ϊ `RequestMappingInfo`, `HandlerMethod`, `directUrls`, `beanName` µÄ°ü×°Àà)£» + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fe807564e60040b262b28977a46a74f60e9.png) + +×ܵÄÀ´Ëµ£¬`RequestMapping` µÄ´¦ÀíÁ÷³ÌÒþ²Ø±È½ÏÉ±¾ÈËÒ²Êǵ÷ÊÔÁ˺öà´Î²ÅÕÒµ½¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4715079](https://my.oschina.net/funcy/blog/4715079) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" new file mode 100644 index 0000000..7fdb68a --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\225\264\344\275\223\346\272\220\347\240\201\347\273\223\346\236\204\346\200\273\347\273\223.md" @@ -0,0 +1,82 @@ +### 1\. servlet 3.0 ¹æ·¶ + +±¾ÏµÁÐÒ»¿ªÊ¼£¬ÏȽéÉÜÁË `servlet3.0` ¹æ·¶£¬Í¨¹ý¸Ã¹æ·¶£¬¿ÉÒÔÈÃÎÒÃÇʵÏÖ web ÏîÄ¿µÄ `0xml` ÅäÖÃ. + +* `servlet3.0` ¹æ·¶ÖУ¬servlet ͨ¹ý `SPI` »úÖÆÌṩÁËÒ»¸ö½Ó¿Ú£º`ServletContainerInitializer`£» +* spring ʵÏÖÁ˸ýӿڣ¬ÔÚÆäʵÏÖÀà `SpringServletContainerInitializer` µÄ `onStartup(...)` ·½·¨ÖУ¬»áÖ´ÐÐËùÓÐʵÏÖÁË `WebApplicationInitializer` ½Ó¿ÚµÄÀàµÄ `onStartup(...)` ·½·¨£¬×îÖÕÎÒÃÇÖ»ÐèҪʵÏÖ `WebApplicationInitializer` ½Ó¿Ú¼´¿É£» +* ÔÚÎÒÃÇ×ÔÖ÷ʵÏÖ `WebApplicationInitializer` ½Ó¿ÚµÄÀàÖУ¬ÔÚ `onStartup(...)` Ïò servlet ÈÝÆ÷ÖÐÊÖ¶¯×¢²áÁËÒ» ¸ö servlet£º`DispatcherServlet`£¬ÔÚÕâ¸ö servlet ÖлáÆô¶¯ spring ÈÝÆ÷£» + +ÒÔÉÏÕû¸öÁ÷³Ì¾ÍÕâÑù£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0874fa7ef39ca9c405cdf55d99ca891ebf2.png) + +### 2\. ÆôÓà webMvc µÄ·½Ê½ + +ÎÒÃÇ·ÖÎöÁËÆôÓà `webMvc` µÄÁ½ÖÖ·½Ê½£º + +#### 1. `@EnableWebMvc` + +ÕâÖÖ·½Ê½ºÜ¼òµ¥£¬Ö»ÐèÒªÕâÑù£º + +``` +// ʹÓÃ@EnableWebMvc×¢½âÆôÓÃmvc¹¦ÄÜ +@Component +@EnableWebMvc +public class MvcConfig { + ... +} + +``` + +Èç¹ûÎÒÃÇÒª´¦Àí webMvc µÄһЩÅäÖÃʱ£¬ÐèҪʵÏÖ `WebMvcConfigurer`: + +``` +// ʵÏÖ WebMvcConfigurer£¬Ìí¼Ó×Ô¶¨ÒåÅäÖà +@Component +public class MyWebMvcConfigurer implements WebMvcConfigurer { + + // ÖØÐ´WebMvcConfigurer·½·¨£¬´¦Àí×Ô¶¨ÒåÅäÖà +} + +``` + +#### 2\. ʵÏÖ `WebMvcConfigurationSupport` + +»¹ÓÐÒ»ÖÖ·½Ê½ÆôÓà `webMvc` µÄ ·½Ê½ÊÇʵÏÖ `WebMvcConfigurationSupport`£º + +``` +@Component +public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport { + // ÖØÐ´ÅäÖ÷½·¨£¬´¦Àí×Ô¶¨ÒåÅäÖà + ... + + /** + * ±ÈÈ磬Ìí¼Ó¿çÓòÅäÖã¬Ö±½ÓÖØÐ´ addCorsMappings ·½·¨ + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + // Ìí¼Ó×Ô¼ºµÄÅäÖà + ... + } + +} + +``` + +ÐèҪעÒâµÄÊÇ£¬Ê¹ÓÃÕâÖÖ·½Ê½ºó£¬ÐèÒª´¦Àí×Ô¶¨ÒåÅäÖÃʱ£¬¾Í²»ÄÜÔÙȥʵÏÖ `WebMvcConfigurer` ½Ó¿ÚÁË£¬¶øÓ¦¸ÃÖ±½ÓÖØÐ´ `WebMvcConfigurationSupport` ÖеÄÏàÓ¦·½·¨£¬ÈçÉÏÃæÖØÐ´ `addCorsMappings()`. + +### 3\. Õû¸öÆô¶¯Á÷³Ì + +ÓÃÒ»ÕÅͼÀ´×ܽáÕû¸öÆô¶¯Á÷³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-74d675bbae28247726b8d054e8758c3d8b1.png) ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-aa6bb35d0ab26925c45c62ab4d709d05cdd.png) + +### 4\. ÇëÇóÁ÷³Ì + +Ò²ÓÃÒ»ÕÅͼÀ´×ܽáÕû¸öÇëÇóÁ÷³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-024b75e7f7952dbf1ace7aa5a8cfe3bcb77.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4773418](https://my.oschina.net/funcy/blog/4773418) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" similarity index 100% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" similarity index 96% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" index df95f75..1ae0ed4 100644 --- "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" @@ -1,11 +1,7 @@ # 目录 * [å‰�言](#å‰�言) - * [é…�置元素读å�–](#init-paramé…�置元素读å�–init-param) * [容器上下文的建立](#容器上下文的建立) * [åˆ�始化SpringMVC默认实现类](#åˆ�始化springmvc默认实现类) -* [Default implementation classes for DispatcherServlet's strategy interfaces.](#default-implementation-classes-for-dispatcherservlets-strategy-interfaces) -* [Used as fallback when no matching beans are found in the DispatcherServlet context.](#used-as-fallback-when-no-matching-beans-are-found-in-the-dispatcherservlet-context) -* [Not meant to be customized by application developers.](#not-meant-to-be-customized-by-application-developers) * [总结](#总结) @@ -160,12 +156,12 @@ initFrameworkServlet()方法是一个没有任何实现的空方法,除去一 this.webApplicationContext = initWebApplicationContext(); ``` -这一å�¥ç®€å�•直白的代ç �,é�“破了FrameworkServlet这个类,在SpringMVC类体系中的设计目的,它是 用æ�¥æŠ½ç¦»å‡ºå»ºç«‹Â WebApplicationContext 上下文这个过程的。 +这一å�¥ç®€å�•直白的代ç �,é�“破了FrameworkServlet这个类,在SpringMVC类体系中的设计目的,它是用æ�¥æŠ½ç¦»å‡ºå»ºç«‹WebApplicationContext上下文这个过程的。 initWebApplicationContext()方法,å°�装了建立Spring容器上下文的整个过程,方法内的逻辑如下: 1. 获å�–ç”±ContextLoaderListeneråˆ�始化并注册在ServletContext中的根上下文,记为rootContext -2. 如果webApplicationContextå·²ç»�ä¸�为空,表示这个Servlet类是通过编程å¼�注册到容器中的(Servlet 3.0+中的ServletContext.addServlet() ),上下文也由编程å¼�传入。若这个传入的上下文还没被åˆ�始化,将rootContext上下文设置为它的父上下文,然å�Žå°†å…¶åˆ�始化,å�¦åˆ™ç›´æŽ¥ä½¿ç”¨ã€‚ +2. 如果webApplicationContextå·²ç»�ä¸�为空,表示这个Servlet类是通过编程å¼�注册到容器中的(Servlet 3.0+中的ServletContext.addServlet()),上下文也由编程å¼�传入。若这个传入的上下文还没被åˆ�始化,将rootContext上下文设置为它的父上下文,然å�Žå°†å…¶åˆ�始化,å�¦åˆ™ç›´æŽ¥ä½¿ç”¨ã€‚ 3. 通过wacå�˜é‡�的引用是å�¦ä¸ºnull,判断第2步中是å�¦å·²ç»�完æˆ�上下文的设置(å�³ä¸Šä¸‹æ–‡æ˜¯å�¦å·²ç»�用编程å¼�æ–¹å¼�传入),如果wac==nullæˆ�立,说明该Servletä¸�是由编程å¼�注册到容器中的。此时以contextAttribute属性的值为键,在ServletContext中查找上下文,查找得到,说明上下文已ç»�以别的方å¼�åˆ�始化并注册在contextAttribute下,直接使用。 4. 检查wacå�˜é‡�的引用是å�¦ä¸ºnull,如果wac==nullæˆ�立,说明2ã€�3两步中的上下文åˆ�始化策略都没æˆ�功,此时调用createWebApplicationContext(rootContext),建立一个全新的以rootContext为父上下文的上下文,作为SpringMVCé…�置元素的容器上下文。大多数情况下我们所使用的上下文,就是这个新建的上下文。 5. 以上三ç§�åˆ�始化上下文的策略,都会回调onRefresh(ApplicationContext context)方法(回调的方å¼�æ ¹æ�®ä¸�å�Œç­–略有ä¸�å�Œï¼‰ï¼ŒonRefresh方法在DispatcherServlet类中被覆写,以上é�¢å¾—到的上下文为ä¾�托,完æˆ�SpringMVC中默认实现类的åˆ�始化。 diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" similarity index 87% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" index 2345fe8..fb7f88e 100644 --- "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\346\246\202\350\277\260.md" @@ -1,4 +1,77 @@ -[## 一ã€�SpringMVC简介 +# 目录 + + * [1.1ã€�SpringMVC引言](#11ã€�springmvc引言) + * [1.2ã€�SpringMVC的优势](#12ã€�springmvc的优势) + * [二ã€�SpringMVC入门](#二ã€�springmvc入门) + * [2.1ã€�环境æ�­å»º](#21ã€�环境æ�­å»º) + * [2.1.1ã€�引入ä¾�èµ–](#211ã€�引入ä¾�èµ–) + * [2.1.2ã€�编写é…�置文件](#212ã€�编写é…�置文件) + * [2.1.3ã€�é…�ç½®web.xml](#213ã€�é…�ç½®webxml) + * [2.1.4ã€�编写控制器](#214ã€�编写控制器) + * [2.2ã€�注解详解](#22ã€�注解详解) + * [2.2.1ã€�@Controller](#221ã€�controller) + * [2.2.2ã€�@RequestMapping](#222ã€�requestmapping) + * [2.3ã€�SpringMVC的跳转方å¼�](#23ã€�springmvc的跳转方å¼�) + * [2.3.1ã€�Controller ——>å‰�å�°é¡µé�¢](#231ã€�controller-å‰�å�°é¡µé�¢) + * [2.3.1.1ã€�forward](#2311ã€�forward) + * [2.3.1.2ã€�redirect](#2312ã€�redirect) + * [2.3.1Controller ——>Controller](#231controller-controller) + * [2.3.1.1ã€�forward](#2311ã€�forward-1) + * [2.3.1.2ã€�redirect](#2312ã€�redirect-1) + * [2.4ã€�SpringMVCçš„å�‚数接收](#24ã€�springmvcçš„å�‚数接收) + * [2.4.1ã€�Servlet接收å�‚数的方å¼�](#241ã€�servlet接收å�‚数的方å¼�) + * [2.4.2ã€�SpringMVCçš„å�‚数接收](#242ã€�springmvcçš„å�‚数接收) + * [2.4.2.1ã€�基本数æ�®ç±»åž‹](#2421ã€�基本数æ�®ç±»åž‹) + * [2.4.2.2ã€�对象类型](#2422ã€�对象类型) + * [2.4.2.3ã€�数组类型](#2423ã€�数组类型) + * [2.4.2.4ã€�集å�ˆç±»åž‹](#2424ã€�集å�ˆç±»åž‹) + * [2.5ã€�SpringMVC接收å�‚数中文乱ç �问题](#25ã€�springmvc接收å�‚数中文乱ç �问题) + * [2.5.1ã€�GET请求](#251ã€�get请求) + * [2.5.2ã€�POST请求](#252ã€�post请求) + * [2.5.2.1ã€�自定义过滤器解决POSTä¹±ç �请求](#2521ã€�自定义过滤器解决postä¹±ç �请求) + * [2.5.2.2ã€�使用CharacterEncodingFilter解决POSTä¹±ç �请求](#2522ã€�使用characterencodingfilter解决postä¹±ç �请求) + * [2.6ã€�SpringMVC中数æ�®ä¼ é€’机制](#26ã€�springmvc中数æ�®ä¼ é€’机制) + * [2.6.1ã€�什么事数æ�®ä¼ é€’机制](#261ã€�什么事数æ�®ä¼ é€’机制) + * [2.6.2ã€�Servlet的数æ�®ä¼ é€’机制](#262ã€�servlet的数æ�®ä¼ é€’机制) + * [三ã€�å‰�端控制器](#三ã€�å‰�端控制器) + * [3.1ã€�什么是å‰�端控制器](#31ã€�什么是å‰�端控制器) + * [3.2ã€�代ç �实现](#32ã€�代ç �实现) + * [3.3ã€�注æ„�](#33ã€�注æ„�) + * [3.4ã€�映射路径](#34ã€�映射路径) + * [3.4.1ã€�访问é�™æ€�资æº�å’Œ JSP 被拦截的原因](#341ã€�访问é�™æ€�资æº�å’Œ-jsp-被拦截的原因) + * [3.4.2ã€�如何解决](#342ã€�如何解决) + * [3.4.2.1ã€�æ–¹å¼�一](#3421ã€�æ–¹å¼�一) + * [3.4.2.2ã€�æ–¹å¼�二](#3422ã€�æ–¹å¼�二) + * [3.5ã€�@ModelAttribute 注解](#35ã€�modelattribute-注解) + * [å››ã€�处ç�†å“�应](#å››ã€�处ç�†å“�应) + * [4.1ã€�返回 ModelAndView](#41ã€�返回-modelandview) + * [4.2ã€�返回String](#42ã€�返回string) + * [4.3ã€�改进](#43ã€�改进) + * [五ã€�请求转å�‘å’Œé‡�定å�‘](#五ã€�请求转å�‘å’Œé‡�定å�‘) + * [5.1ã€�请求转å�‘å’Œé‡�定å�‘的区别](#51ã€�请求转å�‘å’Œé‡�定å�‘的区别) + * [5.2ã€�请求转å�‘](#52ã€�请求转å�‘) + * [5.3ã€�é‡�定å�‘](#53ã€�é‡�定å�‘) + * [5.4ã€�请求路径](#54ã€�请求路径) + * [å…­ã€�å�‚数处ç�†](#å…­ã€�å�‚数处ç�†) + * [6.1ã€�处ç�†ç®€å�•类型的请求å�‚æ•°](#61ã€�处ç�†ç®€å�•类型的请求å�‚æ•°) + * [6.1.1ã€�请求å�‚æ•°å��和控制器方法å�‚数列表形å�‚å�Œå��](#611ã€�请求å�‚æ•°å��和控制器方法å�‚数列表形å�‚å�Œå��) + * [6.1.2ã€�请求å�‚æ•°å��和控制器方法å�‚数列表形å�‚ä¸�å�Œå��](#612ã€�请求å�‚æ•°å��和控制器方法å�‚数列表形å�‚ä¸�å�Œå��) + * [6.2ã€�处ç�†å¤�æ�‚类型的请求å�‚æ•°](#62ã€�处ç�†å¤�æ�‚类型的请求å�‚æ•°) + * [6.2.1ã€�数组类型](#621ã€�数组类型) + * [6.2.2ã€�自定义类型](#622ã€�自定义类型) + * [6.3ã€�处ç�†æ—¥æœŸç±»åž‹çš„请求å�‚æ•°](#63ã€�处ç�†æ—¥æœŸç±»åž‹çš„请求å�‚æ•°) + * [6.3.1ã€�日期在请求å�‚数上](#631ã€�日期在请求å�‚数上) + * [6.3.2ã€�在å°�装的对象上](#632ã€�在å°�装的对象上) + * [七ã€�文件上传与下载](#七ã€�文件上传与下载) + * [7.1ã€�文件上传](#71ã€�文件上传) + * [7.1.1ã€�编写表å�•](#711ã€�编写表å�•) + * [7.1.2ã€�修改web.xml](#712ã€�修改webxml) + * [7.1.3ã€�é…�置上传解æž�器](#713ã€�é…�置上传解æž�器) + * [7.1.4ã€�é…�置上传控制器](#714ã€�é…�置上传控制器) + * [7.2ã€�文件下载](#72ã€�文件下载) + * [7.2.1ã€�å¼€å�‘控制器](#721ã€�å¼€å�‘控制器) + + ## 1.1ã€�SpringMVC引言 @@ -942,5 +1015,3 @@ package cn.linstudy.controller; return null; } ``` - -]([]()) \ No newline at end of file diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" similarity index 99% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" index 6a3357f..cb28598 100644 --- "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" @@ -4,8 +4,6 @@ * [é‡�è¦�接å�£å’Œç±»ä»‹ç»�](#é‡�è¦�接å�£å’Œç±»ä»‹ç»�) * [æº�ç �分æž�](#æº�ç �分æž�) * [ç¼–ç �自定义的ViewResolver](#ç¼–ç �自定义的viewresolver) - * [This is jsp page](#this-is-jsp-page) - * [This is freemarker page](#this-is-freemarker-page) * [总结](#总结) diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" similarity index 98% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" index 665ed2f..a33c502 100644 --- "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" @@ -106,7 +106,7 @@ HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; } ``` -是的,第一步处ç�†å°±è¿™ä¹ˆç®€å�•的完æˆ�了。一个web请求ç»�过处ç�†å�Žï¼Œä¼šå¾—到一个HandlerExecutionChain对象,这就是SpringMVC对URl映射给出的回答。需è¦�ç•™æ„�的是,HandlerMapping接å�£çš„getHandler方法å�‚数是HttpServletRequest,这æ„�味ç�€ï¼ŒHandlerMapping的实现类å�¯ä»¥åˆ©ç”¨HttpServletRequest中的 所有信æ�¯æ�¥å�šå‡ºè¿™ä¸ªHandlerExecutionChain对象的生æˆ�â€�决策“。这包括,请求头ã€�url路径ã€�cookieã€�sessionã€�å�‚数等等一切你从一个web请求中å�¯ä»¥å¾—到的任何东西(最常用的是url路径)。 +是的,第一步处ç�†å°±è¿™ä¹ˆç®€å�•的完æˆ�了。一个web请求ç»�过处ç�†å�Žï¼Œä¼šå¾—到一个HandlerExecutionChain对象,这就是SpringMVC对URl映射给出的回答。需è¦�ç•™æ„�的是,HandlerMapping接å�£çš„getHandler方法å�‚数是HttpServletRequest,这æ„�味ç�€ï¼ŒHandlerMapping的实现类å�¯ä»¥åˆ©ç”¨HttpServletRequest中的所有信æ�¯æ�¥å�šå‡ºè¿™ä¸ªHandlerExecutionChain对象的生æˆ�â€�决策“。这包括,请求头ã€�url路径ã€�cookieã€�sessionã€�å�‚数等等一切你从一个web请求中å�¯ä»¥å¾—到的任何东西(最常用的是url路径)。 SpirngMVC的第一个扩展点,就出现在这里。我们å�¯ä»¥ç¼–写任æ„�çš„HandlerMapping实现类,ä¾�æ�®ä»»ä½•ç­–ç•¥æ�¥å†³å®šä¸€ä¸ªweb请求到HandlerExecutionChain对象的生æˆ�。å�¯ä»¥è¯´ï¼Œä»Žç¬¬ä¸€ä¸ªæ ¸å¿ƒæŽ¥å�£çš„声明开始,SpringMVC就把自己的ç�µæ´»æ€§å’Œé‡Žå¿ƒæš´éœ²æ— ç–‘:哥玩的就是â€�Open-Closed“。 diff --git "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" similarity index 97% rename from "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" index f34de3a..d4cb3bc 100644 --- "a/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" @@ -1,5 +1,4 @@ # 目录 -* [目录](#目录) * [å‰�言](#å‰�言) * [现象](#现象) * [æº�ç �分æž�](#æº�ç �分æž�) @@ -29,17 +28,7 @@ 如果对本系列文章有什么建议,或者是有什么疑问的è¯�,也å�¯ä»¥å…³æ³¨å…¬ä¼—å�·ã€�Java技术江湖】è�”系作者,欢迎你å�‚与本系列å�šæ–‡çš„åˆ›ä½œå’Œä¿®è®¢ã€‚ - - -## 目录 - -* [å‰�言](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#preface) -* [现象](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#phenomenon) -* [æº�ç �分æž�](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#analysis) -* [实例讲解](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#demo) -* [关于é…�ç½®](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#config) -* [总结](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#summary) -* [å�‚考资料](http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html#reference) + ## å‰�言 diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" new file mode 100644 index 0000000..2682d3d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/SpringMVC\347\232\204Demo\344\270\216@EnableWebMvc\346\263\250\350\247\243.md" @@ -0,0 +1,583 @@ +### 1\. demo ×¼±¸ + +ΪÁ˸üºÃµØ·ÖÎö springmvc Ïà¹ØÔ´Â룬ÎÒÃÇÐèÒªÏÈ×¼±¸Ò»¸ö springmvc µÄ demo£¬ÕâÀïµÄ demo »¹ÊÇ·ÅÔÚ `spring-learn` Ä£¿é¡£ + +#### 1\. ÒýÈë tomcat °ü + +ÔÚ tomcat 8 Ö®ºó£¬tomcat ÌṩÁ˶ÀÁ¢µÄÔËÐаü£¬ÐèҪʱֱ½ÓÒýÈëÏà¹ØÒÀÀµ¾Í¿ÉÒÔÁË£¬¶ÔÓ¦µÄ gradle ÒÀÀµÈçÏÂ: + +``` +optional("org.apache.tomcat.embed:tomcat-embed-core") + +``` + +ÔÚ spring ÏîÄ¿µÄ `build.gradle` ÖУ¬ÒѾ­ÒýÈëÁË `tomcat-embed-core-9.0.29.jar`£¬Òò´ËÔÚ `spring-learn` Ä£¿éÖÐÒýÈëʱ²»ÓÃÔÙÖ¸¶¨°æ±¾¡£ + +#### 2\. ×¼±¸ÅäÖÃÀà + +``` +package org.springframework.learn.mvc.demo01; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@Component +@ComponentScan("org.springframework.learn.mvc.demo01") +@EnableWebMvc +public class MvcConfig { + +} + +``` + +ÅäÖÃÀàΪ `MvcConfig`£¬¸ÃÀàÖ¸¶¨ÁËÏîÄ¿µÄ°üɨÃè·¾¶£¬ÒÔ¼°Í¨¹ý `@EnableWebMvc` ¿ªÆô mvc ¹¦ÄÜ¡£ + +#### 3\. ʵÏÖ `WebApplicationInitializer` + +``` +package org.springframework.learn.mvc.demo01; + +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; + +public class MyWebApplicationInitializer implements WebApplicationInitializer { + + @Override + public void onStartup(ServletContext servletContext) { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.register(MvcConfig.class); + + DispatcherServlet servlet = new DispatcherServlet(context); + ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); + registration.setLoadOnStartup(1); + registration.addMapping("/*"); + } +} + +``` + +spring ÌṩÁËÒ»¸ö½Ó¿Ú ¡ª¡ª`WebApplicationInitializer`£¬ÊµÏָýӿÚʱ£¬ÎÒÃÇÔÚ `onStartup(...)` ·½·¨Öд´½¨ spring µÄ `applicationContext`£¬È»ºóÍù servelet ÖÐ×¢²á `DispatcherServlet`¡£ + +#### 4\. ×¼±¸ controller + +``` +package org.springframework.learn.mvc.demo01; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +public class TestController { + + @RequestMapping("/hello") + public String hello() { + System.out.println("hello!!!"); + return "hello world!"; + } +} + +``` + +ÕâÀï×¼±¸ÁËÒ»¸ö¼òµ¥µÄ controller£¬·µ»ØÒ»¸ö×Ö·û´® "hello world". + +#### 5\. Ö÷Àà + +½ÓÏÂÀ´¾ÍÊÇÖ÷ÀàÁË£º + +``` +package org.springframework.learn.mvc.demo01; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; + +public class MvcDemo01Main { + + public static void main(String[] args) throws Exception { + Tomcat tomcat = new Tomcat(); + + Connector connector = new Connector(); + connector.setPort(8080); + connector.setURIEncoding("UTF-8"); + tomcat.getService().addConnector(connector); + + Context context = tomcat.addContext("", System.getProperty("java.io.tmpdir")); + LifecycleListener lifecycleListener = (LifecycleListener) + Class.forName(tomcat.getHost().getConfigClass()) + .getDeclaredConstructor().newInstance(); + context.addLifecycleListener(lifecycleListener); + tomcat.start(); + tomcat.getServer().await(); + } +} + +``` + +ÔÚ `main` ·½·¨ÖУ¬Ö÷Òª´¦Àí tomcat Æô¶¯Âß¼­¡£ + +ÔËÐУ¬½á¹ûÈçÏ£º + +¿ØÖÆÌ¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8d13bb650364488f8ce5e593eff00448ee1.png) + +Ò³Ãæ·µ»Ø£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6a8407a1945dccd6e4ef217a383d4309ec5.png) + +¿ÉÒÔ¿´µ½£¬Ò»¸ö¼òµ¥µÄ springmvc ÏîÄ¿¾Í´î½¨Íê³ÉÁË¡£ + +### 2. `servlet 3.0` ¹æ·¶½éÉÜ + +»ØÒäϹÅÀ쵀 springmvc ÏîÄ¿£¬Ò»°ãÓÐÕ⼸¸ö xml ÅäÖÃÎļþ£º + +* `web.xml`£»servlet ÅäÖÃÎļþ£¬ÅäÖà web Æô¶¯Ê±µÄ²Ù×÷£¬ÒÔ¼° `servlet`/`listener`/`filter`; +* `spring.xml`£ºspring ÈÝÆ÷µÄÅäÖÃÎļþ£¬Ö÷ÒªÓÃÀ´ÅäÖà spring bean. +* `spring-mvc.xml`£ºspringmvc ÅäÖÃÎļþ£¬ÓÃÀ´ÅäÖà mvc Ïà¹ØµÄ bean£¬ÈçÎļþÉÏ´«Ïà¹ØµÄ bean£¬ÊÓͼ½âÎö bean£¬controller °ü·¾¶µÈ¡£ + +ÏîÄ¿ÔÚÆô¶¯Ê±£¬»áÏȼÓÔØ `web.xml`£¬ÔÚ `web.xml` ÖмÓÔØ spring Ïà¹ØÅäÖã¬Æô¶¯ spring ÈÝÆ÷¡£ + +ÔÚÉÏÃæµÄ demo ÖУ¬ÎÒÃÇ·¢ÏÖ²¢ ûÓÐÕâЩÅäÖã¬ÉõÖÁÁ¬ `web.xml` Îļþ¶¼Ã»ÓУ¡ÄÇô£¬ÉÏÃæµÄ web ÏîÄ¿ÊÇÔõôÆô¶¯µÄÄØ£¿ + +ÔÚ `servlet` ÔÚ `3.0` Ö®ºó£¬ÌṩÁËÒ»¸ö spi ¹æ·¶£¬spring ¶ÔÆäʵÏÖÈçÏ£º + +1. ÔÚ `spring-web` Ä£¿éµÄ `/src/main/resources/META-INF/services/` Îļþ¼ÐÏ£¬´´½¨Îļþ `javax.servlet.ServletContainerInitializer`£¬ÄÚÈÝÈçÏ + +``` +org.springframework.web.SpringServletContainerInitializer + +``` + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-92853ebc9c4388d325244b81557ecf80ddd.png) + +1. `org.springframework.web.SpringServletContainerInitializer` ʵÏÖÁË servlet ¹æ·¶£º + +``` +// @HandlesTypes ×¢½âÀ´×ÔÓÚservlet¹æ·¶£¬±íʾ webAppInitializerClass Ϊ WebApplicationInitializer.class +@HandlesTypes(WebApplicationInitializer.class) +public class SpringServletContainerInitializer implements ServletContainerInitializer { + + /* + * ÖØÐ´ ServletContainerInitializer µÄ onStartup ·½·¨ + * ÔÚÕâ¸ö·½·¨ÀÖ÷ÒªÊÇʵÀý»¯ spring ÌṩµÄ WebApplicationInitializer.class£¬È»ºóÖ´ÐÐÆä onStartup ·½·¨ + * + * Set> webAppInitializerClasses ÖеÄÀàÐÍΪ WebApplicationInitializer.class£¬ + * Õâ¸öÀàÐÍÓÉ @HandlesTypes ×¢½âÖ¸¶¨ + */ + @Override + public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext) + throws ServletException { + + List initializers = new LinkedList<>(); + + if (webAppInitializerClasses != null) { + for (Class waiClass : webAppInitializerClasses) { + if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && + WebApplicationInitializer.class.isAssignableFrom(waiClass)) { + try { + // ʹÓ÷´ÉäʵÀý»¯ WebApplicationInitializer µÄʵÏÖÀ࣬Ìí¼Óµ½ initializers ÖÐ + initializers.add((WebApplicationInitializer) + ReflectionUtils.accessibleConstructor(waiClass).newInstance()); + } + catch (Throwable ex) { + ... + } + } + } + } + + servletContext.log(initializers.size() + " ..."); + // ÅÅÐò£¬ÊµÏÖÁËOrderd½Ó¿Ú£¬±ê×¢ @Order ×¢½â£¬»òʵÏÖÁË PriorityOrderd ½Ó¿Ú + AnnotationAwareOrderComparator.sort(initializers); + for (WebApplicationInitializer initializer : initializers) { + // µ÷Óà WebApplicationInitializer ʵÏÖÀàµÄonStartup·½·¨ + initializer.onStartup(servletContext); + } + } + +} + +``` + +1. `WebApplicationInitializer` µÄʵÏÖ ÎÒÃÇÀ´¿´¿´ demo ÖÐ¶Ô `WebApplicationInitializer` µÄʵÏÖ£º + +``` +package org.springframework.learn.mvc.demo01; + +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; + +public class MyWebApplicationInitializer implements WebApplicationInitializer { + + /* + * ÔÚÕâÀïÆô¶¯ spring ÏîÄ¿ + */ + @Override + public void onStartup(ServletContext servletContext) { + // ´´½¨ spring µÄ ApplicationContext + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.register(MvcConfig.class); + + // Ìí¼Ó DispatcherServlet µ½ servlet ÖÐ + DispatcherServlet servlet = new DispatcherServlet(context); + ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); + registration.setLoadOnStartup(1); + registration.addMapping("/*"); + } +} + +``` + +×îÖÕÖ´Ðйý³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0874fa7ef39ca9c405cdf55d99ca891ebf2.png) + +ÓÉ´Ë£¬spring ÈÝÆ÷¾ÍÆô¶¯ÁË¡£ + +### 3\. @EnableWebMvc ×÷Óà + +ÔÚ demo ÖУ¬ÎÒÃÇͨ¹ý `@EnableWebMvc` À´Æô¶¯ mvc ¹¦ÄÜ£¬ÄÇôÕâ¸ö×¢½â×öÁËÊ²Ã´ÄØ£¿ÎÒÃǽøÈë `EnableWebMvc` Àࣺ + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(DelegatingWebMvcConfiguration.class) +public @interface EnableWebMvc { +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö×¢½âͨ¹ý `@Import` ×¢½âÒýÈëÁË `DelegatingWebMvcConfiguration.class`£¬ÎÒÃÇÔÙÀ´¿´¿´ `DelegatingWebMvcConfiguration`: + +``` +@Configuration(proxyBeanMethods = false) +public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { + ... + +} + +``` + +Õâ¸öÀà±êÓÐ `@Configuration` ×¢½â£¬±íÃ÷ÕâÊǸöÅäÖÃÀ࣬Õâ¸öÀà¼Ì³ÐµÄÊÇ `WebMvcConfigurationSupport`£¬´ÓÃû×ÖÀ´¿´£¬`WebMvcConfigurationSupport` Ϊ "mvc ÅäÖÃÖ§³Ö"£¬Õâ±íÃ÷Õâ¸öÀàÊÇÓÃÀ´Öô¦Àí mvc Ïà¹ØµÄÅäÖõġ£ + +ΪÁ˸üºÃµØ·ÖÎö£¬ÕâÀïÏȽéÉܼ¸¸öÀࣺ + +1. `DelegatingWebMvcConfiguration`£ºÓÉ `@EnableWebMvc` ÒýÈëµÄÀ࣬ÊÇ `WebMvcConfigurationSupport` µÄ×ÓÀà£¬ÖØÐ´ÁË `WebMvcConfigurationSupport` ÌṩµÄÅäÖ÷½·¨£º + + ``` + /* + * @Configuration£º±íÃ÷ÕâÊǸöÅäÖÃÀà + * extends WebMvcConfigurationSupport£º¼Ì³ÐÁËWebMvcConfigurationSupportÀà + */ + @Configuration(proxyBeanMethods = false) + public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { + + // WebMvcConfigurerComposite ÊÇ WebMvcConfigurer µÄ×éºÏ£¬ÏÂÃæ»áÌáµ½ + private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); + + /** + * ÉèÖÃconfigurers + * Ìí¼ÓÁË@Autowired×¢½â£¬±íʾ½«springÈÝÆ÷ÖеÄËùÓÐWebMvcConfigurer bean ×÷Ϊ + * ²ÎÊýconfigurersµÄÖµ £¬È»ºóµ÷Óø÷½·¨ + */ + @Autowired(required = false) + public void setConfigurers(List configurers) { + if (!CollectionUtils.isEmpty(configurers)) { + this.configurers.addWebMvcConfigurers(configurers); + } + } + + /** + * ÅäÖÃPathMatch + */ + @Override + protected void configurePathMatch(PathMatchConfigurer configurer) { + // µ÷Óà WebMvcConfigurerComposite µÄ·½·¨½øÐÐÅäÖà + this.configurers.configurePathMatch(configurer); + } + + // ÆäËûÅäÖ÷½·¨Ò²Êǵ÷Óà WebMvcConfigurerComposite ¶ÔÓ¦µÄ·½·¨½øÐÐÅäÖÃµÄ + ... + + } + + ``` + +2. `WebMvcConfigurerComposite`£º`WebMvcConfigurer` µÄ×éºÏ£º + + ``` + /** + * ʵÏÖÁË WebMvcConfigurer + */ + class WebMvcConfigurerComposite implements WebMvcConfigurer { + + // delegatesÊôÐÔΪ WebMvcConfigurer µÄ¼¯ºÏ + private final List delegates = new ArrayList<>(); + + /* + * ±»DelegatingWebMvcConfiguration#setConfigurersµ÷Óà + * ×îÖÕÊǰѴ«ÈëµÄconfigurersÌí¼Óµ½delegates(Ò²¾ÍÊÇWebMvcConfigurer¼¯ºÏ)ÖÐ + */ + public void addWebMvcConfigurers(List configurers) { + if (!CollectionUtils.isEmpty(configurers)) { + this.delegates.addAll(configurers); + } + } + + /** + * ÅäÖÃʱ£¬»á±éÀúdelegates(Ò²¾ÍÊÇWebMvcConfigurer¼¯ºÏ)£¬½«´«ÈëµÄÅäÖÃÉèÖõ½ + * ¼¯ºÏÖеÄÿһ¸öWebMvcConfigurer + */ + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + for (WebMvcConfigurer delegate : this.delegates) { + delegate.configurePathMatch(configurer); + } + } + + // ÆäËûÅäÖÃÀàËÆ£¬Ê¡ÂÔ + ... + } + + ``` + +3. `WebMvcConfigurer`£ºspringmvc µÄÅäÖýӿڣ¬ÌṩÁ˷dz£¶àµÄÅäÖà + + ``` + public interface WebMvcConfigurer { + + default void configurePathMatch(PathMatchConfigurer configurer) { + } + + default void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + } + + default void configureAsyncSupport(AsyncSupportConfigurer configurer) { + } + + ... + } + + ``` + +4. `WebMvcConfigurationSupport`£ºspringmvc µÄÅäÖÃÖ§³ÖÀà + + ``` + /** + * ʵÏÖÁËÁ½¸öaware½Ó¿Ú + */ + public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { + + //================= À´×Ô XxxAware ½Ó¿ÚµÄ·½·¨ ================= + @Override + public void setApplicationContext(@Nullable ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void setServletContext(@Nullable ServletContext servletContext) { + this.servletContext = servletContext; + } + + //================= @Bean ·½·¨£¬ÏòspringÖÐÌí¼Ó bean ================= + @Bean + public RequestMappingHandlerMapping requestMappingHandlerMapping(...) { + RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); + mapping.setOrder(0); + // getInterceptors(...) »ñÈ¡ interceptors£¬ÍùÏ¿´ + mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); + mapping.setContentNegotiationManager(contentNegotiationManager); + // getCorsConfigurations(...) »ñÈ¡CorsÅäÖã¬ÍùÏ¿´ + mapping.setCorsConfigurations(getCorsConfigurations()); + // getPathMatchConfigurer(...) »ñÈ¡PathMatchÅäÖã¬ÍùÏ¿´ + PathMatchConfigurer configurer = getPathMatchConfigurer(); + + ... + + return mapping; + } + ... + //================= get xxx ÅäÖ÷½·¨£¬Ìí¼ÓspringÌṩµÄĬÈÏÅäÖã¬Ìí¼Ó×Ô¶¨ÒåÅäÖà ======= + // »ñÈ¡ interceptors + protected final Object[] getInterceptors( + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { + if (this.interceptors == null) { + InterceptorRegistry registry = new InterceptorRegistry(); + // µ÷ÓÃÅäÖ÷½·¨£¬Ìí¼Ó interceptor£¬ÍùÏ¿´ + addInterceptors(registry); + registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService)); + registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider)); + this.interceptors = registry.getInterceptors(); + } + return this.interceptors.toArray(); + } + + // »ñÈ¡CorsÅäÖà + protected final Map getCorsConfigurations() { + if (this.corsConfigurations == null) { + CorsRegistry registry = new CorsRegistry(); + // µ÷ÓÃÅäÖ÷½·¨£¬Ìí¼Ó CorsMapping£¬ÍùÏ¿´ + addCorsMappings(registry); + this.corsConfigurations = registry.getCorsConfigurations(); + } + return this.corsConfigurations; + } + + // »ñÈ¡PathMatchÅäÖà + protected PathMatchConfigurer getPathMatchConfigurer() { + if (this.pathMatchConfigurer == null) { + this.pathMatchConfigurer = new PathMatchConfigurer(); + configurePathMatch(this.pathMatchConfigurer); + } + return this.pathMatchConfigurer; + } + + ... + + //================= ÅäÖ÷½·¨£¬Áô´ý×ÓÀàʵÏÖ ================= + // Ìí¼Ó×Ô¶¨Òå Interceptor£¬´ý×ÓÀàʵÏÖ + protected void addInterceptors(InterceptorRegistry registry) { + } + + // Ìí¼Ó×Ô¶¨Òå CorsMapping£¬´ý×ÓÀàʵÏÖ + protected void addCorsMappings(CorsRegistry registry) { + } + + // ÅäÖÃ×Ô¶¨Òå PathMatch + protected void configurePathMatch(PathMatchConfigurer configurer) { + } + ... + + } + + ``` + + ¿ÉÒÔ¿´µ½£¬Õâ¸öÀàµÄ·½·¨·ÖΪËÄÀࣺ + + * À´×Ô `XxxAware` µÄ·½·¨£º`XxxAware` ½Ó¿ÚÓÉ spring Ìṩ£¬bean ³õʼ»¯Íê³Éʱ´¦Àí»Øµ÷£» + * ÓÐ `@Bean` ×¢½âµÄ·½·¨£ºÍù spring ÖÐÌí¼Ó bean£¬Éú³É bean ʱ»áµ÷Óà `getXxx` ·½·¨£» + * `getXxx` ·½·¨£º»ñÈ¡ÅäÖ÷½·¨£¬Ôڸ÷½·¨ÖУ¬»áÌí¼Ó spring ÌṩµÄĬÈÏÅäÖã¬ÒÔ¼°µ÷Óà `addXxx/configureXxx` ·½·¨Ìí¼Ó×Ô¶¨ÒåÅäÖã» + * `addXxx/configureXxx` ·½·¨£ºÓÉ×ÓÀàʵÏÖ£¬¿ÉÒÔÏò springmvc ÖÐÌí¼Ó×Ô¶¨ÒåÅäÖᣠ+ +ÕâÀï×ܽáÏÂÕâ 4 ¸öÀàµÄ¹ØÏµ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-5e25e6e7303044e7282b3472492c04cf656.png) + +ÀíÇåÁËÕâËĸöÀàµÄ¹ØÏµ£¬`@EnableWebMvc` µÄÖ´ÐÐÁ÷³Ì¾ÍһĿÁËÈ»ÁË£¬ÕâÀï×ܽáÈçÏ£º + +1. `@EnableWebMvc` Ïò spring ÈÝÆ÷ÖÐÒýÈëÁË` DelegatingWebMvcConfiguration`£» + +2. `DelegatingWebMvcConfiguration` ÖÐÓаüº¬ `@Autowired` ×¢½âµÄ·½·¨ `setConfigurers(List)`£¬ÔÚ spring bean µÄÖÜÆÚÖлá¶ÔÆäÖ´ÐУ¬×÷ÓÃΪ»ñÈ¡ÈÝÆ÷ÖÐËùÓÐ `WebMvcConfigurer` µÄ bean ½«ÆäÉèÖõ½` DelegatingWebMvcConfiguration` µÄÊôÐÔÖУ» + +3. `DelegatingWebMvcConfiguration` ¼Ì³ÐÁË `WebMvcConfigurationSupport`£¬ÔÚ spring bean µÄÖÜÆÚÖлᴦÀí `WebMvcConfigurationSupport` ÖÐÓÐ `@Bean` ×¢½âµÄ·½·¨£¬ÕâÖÖ·½·¨±È½Ï¶à£¬Èç `requestMappingHandlerMapping()`¡¢`mvcPathMatcher` µÈ£¬ÕâЩ¶¼ÊÇ smvc µÄ¹¦ÄÜ×é¼þ£» + +4. ÔÚ´¦Àí `WebMvcConfigurationSupport` ÖÐÓÐ `@Bean` ×¢½âµÄ·½·¨Ê±£¬»áµ÷Óà `getXxx()` »ñÈ¡Ïà¹ØÅäÖ㬸ÃÅäÖðüÀ¨ spring ÌṩµÄĬÈÏÅäÖü°×Ô¶¨ÒåÅäÖã¬`getXxx()` ÓÉ `WebMvcConfigurationSupport` Ìṩ£» + +5. ÔÚµ÷Óà `WebMvcConfigurationSupport#getXxx()` »ñÈ¡×Ô¶¨ÒåÅäÖÃʱ£¬»áµ÷Óà `addXxx()/configureXxx()`£¬¸Ã·½·¨ÔÚ `WebMvcConfigurationSupport` ÖÐÊǿշ½·¨£¬¾ßÌåÐÝÂß¼­ÓÉ×ÓÀà (Ò²¾ÍÊÇ` DelegatingWebMvcConfiguration`) Ìṩ£¬×îÖÕµ÷Ó÷½Ê½ÊÇ**±éÀúÖ´ÐÐµÚ 2 ²½»ñÈ¡µÄ `WebMvcConfigurer` µÄ `addXxx()/configureXxx()`**£» + +Õû¸öÁ÷³ÌÈçÏÂͼËùʾ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4057f913076f01ba0b507b4654e5031391c.png) + +ÔÚÆôÓà springmvc ¹¦ÄÜ£¬²¢Ìí¼Ó×Ô¶¨ÒåÅäÖÃʱ£¬ÎÒÃÇ¿ÉÒÔÕâô×ö: + +1. ·½Ê½ 1£ºÊ¹Óà @EnableWebMvc ×¢½âÆôÓà mvc ¹¦ÄÜ£¬ÊµÏÖ WebMvcConfigurer£¬Ìí¼Ó×Ô¶¨ÒåÅäÖà + + ``` + // ʹÓÃ@EnableWebMvc×¢½âÆôÓÃmvc¹¦ÄÜ + @Component + @EnableWebMvc + public class MvcConfig { + ... + } + + // ʵÏÖ WebMvcConfigurer£¬Ìí¼Ó×Ô¶¨ÒåÅäÖà + @Component + public class MyWebMvcConfigurer implements WebMvcConfigurer { + + // ÖØÐ´WebMvcConfigurer·½·¨£¬´¦Àí×Ô¶¨ÒåÅäÖà + } + + ``` + +2. ·½Ê½ 2£ºÊµÏÖ `WebMvcConfigurationSupport` Àà£¬ÖØÐ´ÆäÖеÄÅäÖ÷½·¨ + + ``` + @Component + public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport { + // ÖØÐ´ÅäÖ÷½·¨£¬´¦Àí×Ô¶¨ÒåÅäÖà + + } + + ``` + + ²»¹ý²ÉÓÃÕâÖÖ·½Ê½ºó£¬ÔÙʵÏÖ `WebMvcConfigurer` Ìí¼Ó×Ô¶¨ÒåÅäÖþͲ»ÉúЧÁË£¬×Ô¶¨ÒåÅäÖÃÖ»ÄÜÔÚ `WebMvcConfigurationSupport` ½øÐÐÅäÖᣠ+ +springmvc ÌṩÁËÄÄЩÅäÖÃÏîÄØ£¿ÎÒÃÇÀ´¿´¿´ `WebMvcConfigurer` ÌṩµÄ·½·¨£º + +* `configurePathMatch`£ºÅäÖ÷ÓÉÇëÇó¹æÔò +* `configureContentNegotiation`£ºÄÚÈÝЭÉÌÅäÖà +* `configureAsyncSupport`£º +* `configureDefaultServletHandling`£ºÄ¬ÈϾ²Ì¬×ÊÔ´´¦ÀíÆ÷ +* `addFormatters`£º×¢²á×Ô¶¨Òåת»¯Æ÷ +* `addInterceptors`£ºÀ¹½ØÆ÷ÅäÖà +* `addResourceHandlers`£º×ÊÔ´´¦Àí +* `addCorsMappings`£ºCORS ÅäÖà +* `addViewControllers`£ºÊÓÍ¼Ìø×ª¿ØÖÆÆ÷ +* `configureViewResolvers`£ºÅäÖÃÊÓͼ½âÎö +* `addArgumentResolvers`£ºÌí¼Ó×Ô¶¨Òå·½·¨²ÎÊý´¦ÀíÆ÷ +* `addReturnValueHandlers`£ºÌí¼Ó×Ô¶¨Òå·µ»Ø½á¹û´¦ÀíÆ÷ +* `configureMessageConverters`£ºÅäÖÃÏûϢת»»Æ÷¡£ÖØÔػḲ¸ÇĬÈÏ×¢²áµÄ `HttpMessageConverter` +* `extendMessageConverters`£ºÅäÖÃÏûϢת»»Æ÷¡£½öÌí¼ÓÒ»¸ö×Ô¶¨ÒåµÄ `HttpMessageConverter`. +* `configureHandlerExceptionResolvers`£ºÅäÖÃÒ쳣ת»»Æ÷ +* `extendHandlerExceptionResolvers`£ºÌí¼ÓÒ쳣ת»¯Æ÷ +* `getValidator`: +* `getMessageCodesResolver`£º + +Èç¹ûÐèÒªÅäÖÃÏà¹ØÏֻÐèÒªÖØÐ´Ïà¹Ø·½·¨ ¼´¿É¡£ + +×îºóÎÒÃÇÔÙÀ´¿´¿´ `WebMvcConfigurationSupport` ÖÐÒýÈëÁËÄÄЩ Bean£¬ÓÐ `@Bean` ×¢½âµÄ·½·¨ÈçÏ£º + +* `public RequestMappingHandlerMapping requestMappingHandlerMapping(...)` +* `public PathMatcher mvcPathMatcher()` +* `public UrlPathHelper mvcUrlPathHelper()` +* `public ContentNegotiationManager mvcContentNegotiationManager()` +* `public HandlerMapping viewControllerHandlerMapping(...)` +* `public BeanNameUrlHandlerMapping beanNameHandlerMapping(...)` +* `public RouterFunctionMapping routerFunctionMapping(...)` +* `public HandlerMapping resourceHandlerMapping(...)` +* `ResourceUrlProvider mvcResourceUrlProvider()` +* `public HandlerMapping defaultServletHandlerMapping()` +* `public RequestMappingHandlerAdapter requestMappingHandlerAdapter(...)` +* `public HandlerFunctionAdapter handlerFunctionAdapter()` +* `public FormattingConversionService mvcConversionService()` +* `public Validator mvcValidator()` +* `public CompositeUriComponentsContributor mvcUriComponentsContributor(...)` +* `public HttpRequestHandlerAdapter httpRequestHandlerAdapter()` +* `public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter()` +* `public HandlerExceptionResolver handlerExceptionResolver(...)` +* `public ViewResolver mvcViewResolver(...)` +* `HandlerMappingIntrospector mvcHandlerMappingIntrospector()` + +ÕâЩ¶¼ÊÇ springmvc ÖÐÓõ½µÄһЩ×é¼þ£¬¾ßÌåµÄ¾ßÌåÄÚÈݾͲ»Õ¹¿ªÁË¡£ + +### 4\. ×ܽá + +±¾ÎÄÄÚÈݱȽÏÔÓ£¬ÏÈÌṩÁËÒ»¸ö springmvc µÄ demo£¬È»ºó½éÉÜÁË demo ÖÐ `0 xml` ÅäÖÃÔ­Àí (Ò²¾ÍÊÇ `servlet 3.0` ¹æ·¶£©£¬½Ó׎éÉÜÁË `@EnableWebMvc` µÄ¹¦ÄÜ£¬×ÅÖØ½éÉÜÁË `WebMvcConfigurationSupport` µÄ×÷Óᣠ+ +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4696657](https://my.oschina.net/funcy/blog/4696657) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" new file mode 100644 index 0000000..4623d0f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\256\271\345\231\250\345\220\257\345\212\250Tomcat.md" @@ -0,0 +1,299 @@ +''ÔÚ [spring mvc Ö® springmvc demo Óë @EnableWebMvc ×¢½â](https://my.oschina.net/funcy/blog/4696657)Ò»ÎÄÖУ¬ÎÒÃÇÌṩÁËÒ»¸öʾÀý demo£¬¸Ã demo »áÏÈÆô¶¯ servlet ÈÝÆ÷£¬È»ºóͨ¹ý `servlet3.0` ¹æ·¶½« `DispatcherServlet` ×¢²áµ½ `servlet` ÈÝÆ÷ÖУ¬È»ºóÔÚ `DispatcherServlet#init` ·½·¨ÖÐÆô¶¯ spring ÈÝÆ÷£¬Õû¸öÁ÷³Ì¾ÍÏñÕâÑù£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0874fa7ef39ca9c405cdf55d99ca891ebf2.png) + +ÕâûʲôÎÊÌ⣬ÄÜÕý³£Æô¶¯Ò²ÔËÐÐÁ¼ºÃ£¬Ö»²»¹ýÎÒÃÇÔÚ spring ÈÝÆ÷ÖÐÎÞ·¨»ñÈ¡ `DispatcherServlet`£¬ÏñÕâÑù£º + +``` +@Component +public class Test { + // ÔÚÇ°ÃæÌṩµÄʾÀý£¨tomcatÀïÆô¶¯springÈÝÆ÷£©ÊÇ×¢Èë²»Á赀 + @Autowired + public DispatcherServlet dispatcherServlet; + + ... + +} + +``` + +Æô¶¯Ê±£¬spring ¿Ï¶¨»á±¨´í£¬ÒòΪÕÒ²»µ½ `DispatcherServlet` ¶ÔÓ¦µÄ bean¡£ + +×î½üÔÚ¿´ springboot Ô´Âëʱ£¬·¢ÏÖ spring ÈÝÆ÷²¢²»ÊÇÓÉ tomcat ÈÝÆ÷Æô¶¯µÄ£¬Ïà·´£¬springboot ÊÇÏÈÆô¶¯ spring ÈÝÆ÷£¬È»ºóÓÉ spring ÈÝÆ÷Æô¶¯ tomcat Æô¶¯£¬ÕâÊÇÈçºÎ×öµ½µÄÄØ£¿ÕâÀï±¾ÈËÌṩһ¸ö demo Ä£ÄâÏ¡£ + +### 1\. ×¼±¸ `DispatcherServlet` + +``` +@Component +@EnableWebMvc +public class MvcConfig implements WebMvcConfigurer { + + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); + viewResolver.setPrefix("/WEB-INF/views/"); + viewResolver.setSuffix(".html"); + registry.viewResolver(viewResolver); + } + + /** + * dispatcherServlet + * @param webApplicationContext + * @return + */ + @Bean + public DispatcherServlet dispatcherServlet(WebApplicationContext webApplicationContext) { + return new DispatcherServlet(webApplicationContext); + } + +} + +``` + +¶Ô¸ÃÀà˵Ã÷ÈçÏ£º + +* `MvcConfig` Àà±» `@EnableWebMvc` ×¢½â±ê¼Ç£¬±íʾÐèÒªÆô¶¯Óà `web mvc` ¹¦ÄÜ +* `MvcConfig` ʵÏÖÁË `WebMvcConfigurer`£¬¿ÉÒÔͨ¹ýÖØÐ´ `WebMvcConfigurer` µÄ·½·¨À´ÊµÏÖ×Ô¶¨Òå `web mvc` µÄÅäÖà +* `MvcConfig` ÖлáÉú³É `DispatcherServlet` bean£¬¸Ã bean »á±£´æµ½ spring ÈÝÆ÷ÖÐ + +### 2\. ×¼±¸Ò»¸ö `WebApplicationInitializer` ʵÏÖÀà + +``` +@Component +public class MyWebApplicationInitializer implements WebApplicationInitializer { + + private static BeanFactory beanFactory; + + private static AbstractRefreshableWebApplicationContext applicationContext; + + @Override + public void onStartup(ServletContext servletContext) { + // ´Ó beanFactory ÖлñÈ¡ DispatcherServlet ²¢×¢²áµ½servletÈÝÆ÷ + DispatcherServlet servlet = beanFactory.getBean(DispatcherServlet.class); + ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); + // loadOnStartup ÉèÖÃ³É -1 ʱ£¬Ö»ÓÐÔÚµÚÒ»´ÎÇëÇóʱ£¬²Å»áµ÷Óà init ·½·¨ + registration.setLoadOnStartup(-1); + registration.addMapping("/*"); + + // Ϊ applicationContext ÉèÖà servletContext + applicationContext.setServletContext(servletContext); + } + + /** + * ÉèÖà beanFactory + * ΪʲôҪÉèÖà beanFactoryµÄÖµ£¿ÒòΪ DispatcherServlet Òª´Ó beanFactory ÖлñÈ¡ + * @param beanFactory + * @throws BeansException + */ + public static void setBeanFactory(BeanFactory beanFactory) throws BeansException { + MyWebApplicationInitializer.beanFactory = beanFactory; + } + + /** + * ÉèÖà applicationContext + * ΪʲôҪÉèÖà applicationContext µÄÖµ£¿ÒòΪ servletContext ÒªÉèÖõ½ applicationContext + * @param applicationContext + */ + public static void setApplicationContext( + AbstractRefreshableWebApplicationContext applicationContext) { + MyWebApplicationInitializer.applicationContext = applicationContext; + } +} + +``` + +`WebApplicationInitializer` ÊÇ spring ¶Ô servlet 3.0 ¹æ·¶µÄʵÏÖ£¬ÔÚ [spring mvc Ö® springmvc demo Óë @EnableWebMvc ×¢½â](https://my.oschina.net/funcy/blog/4696657)Ò»ÎÄÒ²Ïêϸ·ÖÎö¹ý£¬tomcat ÔÚÆô¶¯Ê±£¬»áÖ´ÐÐ `WebApplicationInitializer#onStartup` ·½·¨¡£ + +¶Ô `MyWebApplicationInitializer` ˵Ã÷ÈçÏ£º + +* `MyWebApplicationInitializer` ÖÐÓÐÁ½¸ö¾²Ì¬³ÉÔ±±äÁ¿£º`beanFactory` Óë `applicationContext`£¬¶ÔÓ¦µØÌṩÁËÁ½¸ö¾²Ì¬ `set` ·½·¨£¬ÐèҪעÒâµÄÊÇ£¬ÕâÁ½¸ö¾²Ì¬ `set` ·½·¨ÒªÔÚ `onStartup()` ·½·¨Ç°µ÷Óã¬Ò²¾ÍÊÇ tomcat Æô¶¯Ç°µ÷Óõ÷Óã» +* ÔÚ `MyWebApplicationInitializer#onStartup` ·½·¨ÖУ¬ÎÒÃÇÏÈÊÇ´Ó `beanFactory` ÖлñÈ¡ÁË `DispatcherServlet`£¬È»ºó½«Æä×¢²áµ½ `servlet` ÈÝÆ÷ÖУ¬È»ºó½« `onStartup(...)` ·½·¨µÄ²ÎÊý `servletContext` ÉèÖõ½ `applicationContext` ÖÐ + +### 3\. ×¼±¸Ò»¸ö `ServletContextAwareProcessor` µÄ×ÓÀà + +``` +public class MyServletContextAwareProcessor extends ServletContextAwareProcessor { + + AbstractRefreshableWebApplicationContext webApplicationContext; + + /** + * ´«Èë webApplicationContext + * @param webApplicationContext + */ + public MyServletContextAwareProcessor( + AbstractRefreshableWebApplicationContext webApplicationContext) { + this.webApplicationContext = webApplicationContext; + } + + /** + * ·µ»Ø ServletContext + * ÏÈ´Ó webApplicationContext ÖлñÈ¡£¬Èç¹û»ñÈ¡²»µ½£¬ÔÙ´Ó¸¸Àà·½·¨ÖлñÈ¡ + * @return + */ + @Override + protected ServletContext getServletContext() { + ServletContext servletContext = this.webApplicationContext.getServletContext(); + return (servletContext != null) ? servletContext : super.getServletContext(); + } + + @Override + protected ServletConfig getServletConfig() { + ServletConfig servletConfig = this.webApplicationContext.getServletConfig(); + return (servletConfig != null) ? servletConfig : super.getServletConfig(); + } +} + +``` + +ÔÚ `MyWebApplicationInitializer#onStartup` ·½·¨ÖÐ¶Ô `applicationContext` ÉèÖÃµÄ `servletContext` ¾ÍÊÇÔÚÕâÀïʹÓõģ¬`MyServletContextAwareProcessor` µÄ¹¹Ôì·½·¨´«ÈëÁË `webApplicationContext`£¬È»ºóÖØÐ´ÁË `getServletContext()` ·½·¨£¬»ñÈ¡ `servletContext` ʱ£¬ÏÈ´Ó `webApplicationContext` ÖлñÈ¡£¬Èç¹û»ñÈ¡²»µ½£¬ÔÙ´Ó¸¸Àà·½·¨ÖлñÈ¡¡£ + +### 4\. ×¼±¸Ò»¸ö `ApplicationContext` µÄʵÏÖÀà + +`ApplicationContext` ÖÁ¹ØÖØÒª£¬ÕâÀïÎÒÃÇÑ¡ÔñÖ±½ÓÀ©Õ¹ `AnnotationConfigWebApplicationContext`£º + +``` +public class MyWebApplicationContext extends AnnotationConfigWebApplicationContext { + + private Tomcat tomcat; + + /** + * ÖØÐ´ postProcessBeanFactory ·½·¨ + * ÔÚÕâ¸ö·½·¨ÀïÌí¼ÓÎÒÃÇ×Ô¶¨ÒåµÄ MyServletContextAwareProcessor + * @param beanFactory + */ + @Override + protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + beanFactory.addBeanPostProcessor(new MyServletContextAwareProcessor(this)); + beanFactory.ignoreDependencyInterface(ServletContextAware.class); + WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory()); + } + + /** + * ÔÚÕâ¸ö·½·¨ÀïÆô¶¯ tomcat + */ + @Override + protected void onRefresh() { + // Ïȵ÷Óø¸ÀàµÄ·½·¨ + super.onRefresh(); + // ÉèÖà MyWebApplicationInitializer µÄ beanFactory Óë applicationContext + MyWebApplicationInitializer.setBeanFactory(getBeanFactory()); + MyWebApplicationInitializer.setApplicationContext(this); + + // tomcatµÄ´´½¨¼°Æô¶¯ + tomcat = new Tomcat(); + Connector connector = new Connector(); + connector.setPort(8080); + connector.setURIEncoding("UTF-8"); + tomcat.getService().addConnector(connector); + + Context context = tomcat.addContext("", System.getProperty("java.io.tmpdir")); + LifecycleListener lifecycleListener = null; + try { + lifecycleListener = (LifecycleListener) + Class.forName(tomcat.getHost().getConfigClass()) + .getDeclaredConstructor().newInstance(); + context.addLifecycleListener(lifecycleListener); + // Æô¶¯tomcat + tomcat.start(); + } catch (Exception e) { + System.out.println("Æô¶¯Òì³££º"); + e.printStackTrace(); + } + } + +} + +``` + +Õâ¸öÀàÀ©Õ¹ÁË spring µÄÆô¶¯Á÷³Ì£¬ÕâÀïÒ»¹²ÖØÐ´ÁËÁ½¸ö·½·¨£¬ÕâÀïÒ»Ò»½éÉÜÏÂ: + +* `postProcessBeanFactory()`£ºÕâ¸ö·½·¨Ö÷ÒªÊÇΪÁË×¢²á `MyServletContextAwareProcessor`£¬Ç°ÃæÎÒÃÇ×¼±¸µÄ `MyServletContextAwareProcessor` ¾ÍÊÇÔÚÕâÀï×¢²áµÄ£¬Ö®ËùÒÔÖØÐ´£¬»¹ÊÇΪÁËʹÓà `tomcat` ÌṩµÄ `ServletContext`£» +* `onRefresh()`£ºÔÚÕâ¸ö·½·¨ÀÏÈÊÇÉèÖà `MyWebApplicationInitializer` µÄ `beanFactory` Óë `applicationContext` ÊôÐÔÖµ£¬È»ºóÆô¶¯ `tomcat`£» + +### 5\. ×¼±¸Ò»¸ö¼òµ¥µÄ `Controller` + +×¼±¸Ò»¸ö `Controller`£¬Ö÷ÒªÊǰïÖúÎÒÃÇÑéÖ¤ÏîÄ¿ÊÇ·ñÆô¶¯Õý³££º + +``` +@RestController +@RequestMapping("/test") +public class TestController { + + @RequestMapping("/hello") + public String hello() { + System.out.println("hello!!!"); + return "hello world!"; + } + +} + +``` + +### 6\. Ö÷Àà + +×îºó¾ÍÊÇÖ÷ÀàÁË£¬Ö÷ÒªÊÇ´¦Àí spring µÄÆô¶¯²Ù×÷£¬Ò²ÊÇÏ൱¼òµ¥£º + +``` +@ComponentScan +public class MvcDemo03Main { + + public static void main(String[] args) throws Exception { + MyWebApplicationContext webApplicationContext = new MyWebApplicationContext(); + webApplicationContext.register(MvcDemo03Main.class); + webApplicationContext.refresh(); + } +} + +``` + +ÔËÐУ¬·ÃÎÊ `http://localhost:8080/test/hello`£¬½á¹ûÈçÏ£º + +Ò³Ãæ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d87b4a09e7a87e0535eb52a09759fcc6534.png) + +¿ØÖÆÌ¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c608886c751dcf595d74efb8506e5d67306.png) + +### 7\. ÎÊÌ⣺Æô¶¯ºó£¬`DispatcherServlet#init` ·½·¨»áÔÙ´ÎÆô¶¯ spring ÈÝÆ÷Â𣿠+ +Ç°ÃæÎÒÃÇ·ÖÎöµ½£¬Ê¹Óà tomcat Æô¶¯ spring ÈÝÆ÷µÄ·½Ê½Ê±£¬spring ÈÝÆ÷ÊÇÔÚ `DispatcherServlet#init` ·½·¨ÖÐÆô¶¯µÄ£¬ÔÚÎÒÃÇʹÓà **spring ÈÝÆ÷Æô¶¯ tomcat** µÄÆô¶¯·½Ê½Ê±£¬tomcat Ö´ÐÐ `DispatcherServlet#init` ·½·¨Ê±£¬»áÔÙ´ÎÆô¶¯ spring ÈÝÆ÷Â𣿠+ +ÕâÀïÎÒÃÇÖ±½Ó½øÈë `FrameworkServlet#initWebApplicationContext` ·½·¨£¬´òÉ϶ϵ㣺 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-64ee29f90ef5683f7968f782b9175d42e0f.png) + +ÕâÀïµÄ `wac` Óë `this.webApplicationContext` ¾ÍÊÇ `MyWebApplicationContext` µÄʵÀý£¬ÔÚ´´½¨ `DispatcherServlet` ʱ´«ÈëµÄ: + +``` +@Bean +public DispatcherServlet dispatcherServlet(WebApplicationContext webApplicationContext) { + // ÔÚ¹¹Ôì·½·¨µÄ²ÎÊýÖд«ÈëÁË webApplicationContext + return new DispatcherServlet(webApplicationContext); +} + +``` + +µ½ÓÚΪʲô´òµ½Õâ¸ö·½·¨£¬ÄÇÊÇÒòΪ `DispatcherServlet#init` ¾­¹ý²ã²ãµ÷Óúó£¬×îÖÕÊÇÔÚÕâ¸ö·½·¨Àï´¦Àí spring ÈÝÆ÷µÄÆô¶¯µÄ£¬µ±¶ÏµãÔËÐе½ `if (!cwac.isActive()) {...` ʱ£¬`!cwac.isActive()` ·µ»Ø½á¹ûΪ `false`£¬Òò´Ë `if` ¿éÀïµÄÆô¶¯ spring ÈÝÆ÷¾Í²»»áÖ´Ðе½ÁË¡£ + +×ÛÉÏËùÊö£¬Í¨¹ý spring ÈÝÆ÷Æô¶¯ tomcat ºó£¬ÔÚ `DispatcherServlet#init` Àï²»»áÔÙ´ÎÆô¶¯ spring ÈÝÆ÷¡£ÕâÑùÆô¶¯ºó£¬`DispatcherServlet` ¾ÍÊÇÒ»¸ö spring bean£¬ÎÒÃǾͿÉÒÔÔÚ´úÂëÀïʹÓà `@Autowired` ×¢½â½«Æä×¢Èëµ½ÆäËûÀàÖÐÁË£º + +``` +@Component +public class Test { + // ±¾ÎĵÄʾÀý£¨ÔÚ spring ÈÝÆ÷ÖÐÆô¶¯ tomcat£©ÊÇ¿ÉÒÔ×¢Èë³É¹¦µÄ + @Autowired + public DispatcherServlet dispatcherServlet; + + ... + +} + +``` + +¹ØÓÚ `spring` Æô¶¯ `tomcat` µÄ·ÖÎö¾Íµ½ÕâÀÆäÖеÄÄѵãÔÚÓÚ**ÈçºÎ½« `tomcat` ÌṩµÄ `ServletContext` ÉèÖõ½ `ServletContextAwareProcessor` ÖÐ**£¬ÆäÖеĽâ¾ö·½Ê½×¢ÒâÌå»á¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4928222](https://my.oschina.net/funcy/blog/4928222) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" new file mode 100644 index 0000000..2f1e035 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\344\271\213\350\216\267\345\217\226Handler.md" @@ -0,0 +1,772 @@ +ÉÏһƪÎÄÕÂÎÒÃÇ·ÖÎö `RequestMapping` ³õʼ»¯Á÷³Ì£¬±¾ÎĽ«·ÖÎö spring mvc µÄÇëÇóÖ´ÐÐÁ÷³Ì¡£ + +### 1\. ÇëÇóµÄÖ´ÐÐÈë¿Ú + +ÔÚ [spring mvc Ö® DispatcherServlet ³õʼ»¯Á÷³Ì](https://my.oschina.net/funcy/blog/4710330 "spring mvcÖ®DispatcherServlet ³õʼ»¯Á÷³Ì")Ò»ÎÄÖУ¬ÎÒÃÇÉîÈë·ÖÎöÁËÏò `servlet` ÈÝÆ÷Ìí¼ÓÁË `DispatcherServlet` ºó£¬Òý·¢µÄһϵÁгõʼ»¯Á÷³Ì£¬±¾ÎĽ«¼ÌÐøÎ§ÈÆÕâ¸ö `servlet` ·ÖÎö `springmvc` µÄÇëÇóÁ÷³Ì¡£ + +#### 1.1 »Ø¹Ë servlet µÄÖ´ÐÐÈë¿Ú£º + +ÔÚ·ÖÎö `DispatcherServlet` ǰÊ×ÏÈÒª»Ø¹ËÏ servlet µÄÖ´ÐÐÈë¿Ú¡£ + +ÔÚÎÒÃÇʵÏÖ×Ô¶¨ÒåµÄ servlet ʱ£¬Ò»°ãÊÇʵÏÖ `HttpServlet`£¬È»ºóÖØÐ´ `doGet(xxx)`¡¢`doPost()` ·½·¨£¬¶øÊµ¼ÊÉÏ servlet Ϊ `HttpServlet#service(ServletRequest, ServletResponse)`£º + +``` +public abstract class HttpServlet extends GenericServlet { + ... + + // Õâ¸ö·½·¨½ö×öÁ˲ÎÊýÀàÐÍת»» + @Override + public void service(ServletRequest req, ServletResponse res) + throws ServletException, IOException { + + HttpServletRequest request; + HttpServletResponse response; + + // ÔÚÕâÀï´¦Àí²ÎÊýµÄÀàÐÍת»» + try { + request = (HttpServletRequest) req; + response = (HttpServletResponse) res; + } catch (ClassCastException e) { + throw new ServletException(lStrings.getString("http.non_http")); + } + service(request, response); + } + + /** + * ÔÚÕâÀï´¦ÀíÇëÇó + * ´Ó´úÂë¿ÉÒÔ¿´µ½£¬Õâ¸öÀàÆäʵÊÇ×öÒ»¸öת·¢£ºÅжÏÇëÇó·½·¨£¬È»ºóµ÷ÓþßÌåµÄ·½·¨Ö´ÐÐ + */ + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + String method = req.getMethod(); + + // ÅжϵÄÇëÇó·½·¨£¬È»ºóÕÒµ½¶ÔÓ¦µÄ·½·¨È¥Ö´ÐÐ + if (method.equals(METHOD_GET)) { + long lastModified = getLastModified(req); + if (lastModified == -1) { + doGet(req, resp); + } else { + ... + doGet(req, resp); + } + } else if (method.equals(METHOD_HEAD)) { + long lastModified = getLastModified(req); + maybeSetLastModified(resp, lastModified); + doHead(req, resp); + } else if (method.equals(METHOD_POST)) { + doPost(req, resp); + } else if (method.equals(METHOD_PUT)) { + doPut(req, resp); + } else if (method.equals(METHOD_DELETE)) { + doDelete(req, resp); + } else if (method.equals(METHOD_OPTIONS)) { + doOptions(req,resp); + } else if (method.equals(METHOD_TRACE)) { + doTrace(req,resp); + } else { + // ûÓжÔÓ¦µÄ·½·¨£¬±¨´í + ... + } + } + +} + +``` + +ÒÔÉÏÊÇ servlet Ô´Â룬·½·¨±È½Ï¼òµ¥£¬Öص㲿·Ö¶¼×öÁË×¢ÊÍ£¬ÕâÀïÓÐÁ½¸öÐèÒªÔÙ´ÎÇ¿µ÷Ï£º + +1. servlet µÄÖ´ÐÐÈë¿ÚΪ `HttpServlet#service(ServletRequest, ServletResponse)`£» +2. `HttpServlet#service(HttpServletRequest, HttpServletResponse)` ·½·¨»á¸ù¾ÝÇëÇó·½·¨ÕÒµ½¶ÔÓ¦µÄ´¦Àí·½·¨Ö´ÐУ¬Ò»°ãÀ´Ëµ£¬ÎÒÃÇ×Ô¶¨Òå servlet£¬Ö»ÒªÖØÐ´ `doGet(xxx)`¡¢`doPost(xxx)` µÈ·½·¨¼´¿É¡£ + +ÇëÇóÁ÷³Ì´ó¸ÅÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-60d0e2afcbdb75a175c6f28471fc467f0e5.png) + +#### 1.2 `DispatcherServlet` µÄ¸¸Àࣺ`FrameworkServlet` + +Á˽âÍê servlet µÄÇëÇóÈë¿Úºó£¬½ÓÏÂÀ´¾ÍµÃ·ÖÎöÒ»¸ö²»µÃ²»ÌáµÄÀàÁË£º`FrameworkServlet`¡£`FrameworkServlet` ÊÇ `HttpServlet` µÄ×ÓÀ࣬ʵÏÖÁË `HttpServlet` µÄ¸÷ÖÖ `doXxx()`£¬Í¬Ê±Ò²ÊµÏÖÁË `service(HttpServletRequest, HttpServletResponse)`£º + +``` +/** + * FrameworkServlet¼Ì³ÐÁËHttpServletBean£¬¶øHttpServletBean¼Ì³ÐÁËHttpServlet + * Òò´ËFrameworkServletÒ²ÊÇHttpServletµÄ×ÓÀà + */ +public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { + @Override + protected final void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected final void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected final void doPut(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected final void doDelete(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); + if (httpMethod == HttpMethod.PATCH || httpMethod == null) { + processRequest(request, response); + } + else { + // GET/POST/PUT/DELETE µÈÇëÇó·½·¨£¬»¹ÊÇ»áµ÷Óø¸ÀàµÄ·½·¨ + super.service(request, response); + } + } +} + +``` + +¿ÉÒÔ¿´µ½£¬ÒÔÉÏ´úÂëÖУ¬ÓÐÒ»¸ö·½·¨µÄ³ö¾µÂÊÏ൱¸ß£º`FrameworkServlet#processRequest`£¬²»¹ÜÊÇ `doXxx(xxx)`£¬»¹ÊÇ `service(xxx)`£¬¶¼»áµ÷Óà `processRequest(xxx)`£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´¿´¿´Õâ¸ö·½·¨×öÁËʲô£º + +> FrameworkServlet#processRequest + +``` +protected final void processRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + // ¼Ç¼¿ªÊ¼Ê±¼ä + long startTime = System.currentTimeMillis(); + Throwable failureCause = null; + // ¼Ç¼µ±Ç°Ï̵߳ÄÐÅÏ¢ + LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); + LocaleContext localeContext = buildLocaleContext(request); + RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); + ServletRequestAttributes requestAttributes = buildRequestAttributes( + request, response, previousAttributes); + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), + new RequestBindingInterceptor()); + initContextHolders(request, localeContext, requestAttributes); + try { + // ºËÐÄ´¦Àí + doService(request, response); + } + catch (ServletException | IOException ex) { + failureCause = ex; + throw ex; + } + catch (Throwable ex) { + failureCause = ex; + throw new NestedServletException("Request processing failed", ex); + } + finally { + // Çå³ýḬ̈߳ó¶¨ÐÅÏ¢ + resetContextHolders(request, previousLocaleContext, previousAttributes); + if (requestAttributes != null) { + requestAttributes.requestCompleted(); + } + logResult(request, response, failureCause, asyncManager); + // ·¢ËÍʼþ֪ͨ + publishRequestHandledEvent(request, response, startTime, failureCause); + } +} + +``` + +Õâ¸ö·½·¨ËäÈ»Óе㳤£¬µ«´ó²¿·ÖÓëÇëÇó´¦ÀíÁ÷³Ì¹ØÏµ²»´ó£¬ÓëÇëÇó´¦ÀíÁ÷³ÌÏà¹ØµÄÖ»Óм¸ÐУº + +``` + ... + try { + // ºËÐÄ´¦Àí + doService(request, response); + } + catch (ServletException | IOException ex) { + failureCause = ex; + throw ex; + } + ... + +``` + +ÓÉ´Ë¿ÉÒÔ¿´µ½£¬Êµ¼Ê´¦ÀíÇëÇóµÄ·½·¨ÊÇÔÚ `FrameworkServlet#doService` ÖС£²»¹ý£¬`FrameworkServlet#doService` ÊǸö³éÏó·½·¨£º + +``` +protected abstract void doService(HttpServletRequest request, + HttpServletResponse response) throws Exception; + +``` + +ÕæÕýµÄʵÏÖÊÇÔÚ×ÓÀ࣬Ҳ¾ÍÊÇ `DispatcherServlet#doService` ÖС£ + +#### 1.3 `DispatcherServlet#doService` + +À´¿´¿´ `DispatcherServlet#doService` ×öÁËɶÊ£º + +``` +public class DispatcherServlet extends FrameworkServlet { + @Override + protected void doService(HttpServletRequest request, + HttpServletResponse response) throws Exception { + logRequest(request); + // Ê¡ÂÔÁËÒ»´ó¶ÎÊôÐÔÉèÖà + ... + try { + // ¾ßÌåµÄ´¦Àí + doDispatch(request, response); + } + finally { + ... + } + } +} + +``` + +ÕâÈË·½·¨Ò²Ã»¸ÉʲôÊÂʵ£¬Ö»Êǵ÷ÓÃÁËһϠ`doDispatch` ·½·¨£¬È»ºó¾ÍûÁË¡£ÊÂʵÉÏ£¬`DispatcherServlet#doDispatch` ¾ÍÊÇ×îÖÕ´¦ÀíÇëÇóµÄÂß¼­£¬½ÓÏÂÀ´ÎÒÃÇÖØµã·ÖÎöÕâ¸ö·½·¨ ¡£ + +ÕâÒ»½ÚÎÒÃÇÀ´×ܽáÏ `DispatcherServlet` µÄÇëÇóÁ÷³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f854710c25924666cd5ab755c2983a9baf7.png) + +### 2\. springmvc ÇëÇó·Ö·¢£º`DispatcherServlet#doDispatch` + +ÉÏÒ»½ÚµÄ×îºó£¬ÎÒÃÇ·¢ÏÖ springmvc ´¦ÀíÇëÇóµÄ·½·¨ÊÇ `DispatcherServlet#doDispatch`£¬±¾½Ú¾Í´ÓÕâ¸ö·½·¨ÈëÊÖ£¬¿´¿´Õâ¸ö·½·¨µÄÂß¼­£º + +``` +protected void doDispatch(HttpServletRequest request, HttpServletResponse response) + throws Exception { + HttpServletRequest processedRequest = request; + HandlerExecutionChain mappedHandler = null; + boolean multipartRequestParsed = false; + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + try { + ModelAndView mv = null; + Exception dispatchException = null; + try { + //Èç¹ûÊÇÎļþÉÏ´«ÇëÇóÔò½øÐÐÌØÊâ´¦Àí + processedRequest = checkMultipart(request); + multipartRequestParsed = (processedRequest != request); + // 1\. »ñÈ¡¶ÔÓ¦µÄhandler, + // HandlerÖаüº¬ÕæÕýµØ´¦ÀíÆ÷£¨ControllerÖеķ½·¨£©ºÍÒ»×éHandlerInterceptorÀ¹½ØÆ÷ + mappedHandler = getHandler(processedRequest); + if (mappedHandler == null) { + // Èç¹ûûÕÒµ½£¬±¨¸ö404 + noHandlerFound(processedRequest, response); + return; + } + // 2\. »ñÈ¡¶ÔÓ¦µÄhandlerAdapter£¬ÓÃÀ´ÔËÐÐ handler(xxx) + HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); + // ´¦Àílast-modifiedÇé¿ö + String method = request.getMethod(); + boolean isGet = "GET".equals(method); + if (isGet || "HEAD".equals(method)) { + long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); + if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { + return; + } + } + // 3\. ÔËÐÐspringµÄÀ¹½ØÆ÷, ÔËÐÐ HandlerInterceptor#preHandle ·½·¨ + if (!mappedHandler.applyPreHandle(processedRequest, response)) { + return; + } + // 4\. ͨ¹ýÉÏÃæ»ñÈ¡µ½µÄhandlerAdapterÀ´µ÷ÓÃhandle + mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); + if (asyncManager.isConcurrentHandlingStarted()) { + return; + } + // Èç¹ûº¯Êýµ÷ÓÃûÓзµ»ØÊÓͼÔòʹÓÃĬÈ쵀 + applyDefaultViewName(processedRequest, mv); + // 5\. Ö´ÐÐÀ¹½ØÆ÷£¬ÔËÐÐ HandlerInterceptor#postHandle ·½·¨ + mappedHandler.applyPostHandle(processedRequest, response, mv); + } + catch (Exception ex) { + dispatchException = ex; + } + catch (Throwable err) { + dispatchException = new NestedServletException("Handler dispatch failed", err); + } + // 6\. ´¦Àí·µ»Ø½á¹û£¬ÔÚÕâ¸ö·½·¨Àï»áäÖȾÊÓͼ£¬ÒÔ¼°Ö´ÐÐ HandlerInterceptor#afterCompletion + processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); + } + catch (...) { + // ÕâÀï»áÖ´ÐÐ HandlerInterceptor#afterCompletion + triggerAfterCompletion(processedRequest, response, mappedHandler, ex); + } + finally { + if (asyncManager.isConcurrentHandlingStarted()) { + if (mappedHandler != null) { + // »Øµ÷À¹½ØÆ÷£¬Ö´Ðз½·¨ AsyncHandlerInterceptor#afterConcurrentHandlingStarted + mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); + } + } + else { + if (multipartRequestParsed) { + cleanupMultipart(processedRequest); + } + } + } +} + +``` + +Õâ¸ö·½·¨Ôڵ㳤£¬²»¹ýÁ÷³ÌºÜÇåÎú£¬springmvc µÄÕû¸öÇëÇóÁ÷³Ì¶¼ÔÚÕâÀïÁË£¬ÕâÀï°Ñ¹Ø¼ü²½ÖèչʾÈçÏ£º + +1. »ñÈ¡¶ÔÓ¦µÄ `HandlerExecutionChain`, »ñÈ¡µÄ `HandlerExecutionChain` Öаüº¬ÕæÕýµØ´¦ÀíÆ÷£¨`Controller` Öеķ½·¨£©ºÍÒ»×é `HandlerInterceptor` À¹½ØÆ÷£» +2. »ñÈ¡¶ÔÓ¦µÄ `handlerAdapter`£¬¸Ã¶ÔÏóÓÃÀ´ÔËÐÐ `handler(xxx)` ·½·¨£» +3. Ö´ÐÐ spring µÄÀ¹½ØÆ÷£¬ÔËÐÐ `HandlerInterceptor#preHandle` ·½·¨£» +4. ´¦ÀíÇëÇó£¬Ò²¾ÍÊÇͨ¹ýÉÏÃæ»ñÈ¡µ½µÄ `handlerAdapter` À´µ÷Óà `handle(xxx)` ·½·¨£» +5. Ö´ÐÐ spring µÄÀ¹½ØÆ÷£¬ÔËÐÐ `HandlerInterceptor#postHandle` ·½·¨£» +6. ´¦Àí·µ»Ø½á¹û£¬ÕâÀï»áäÖȾÊÓͼ£¬ÒÔ¼°Ö´ÐÐ spring À¹½ØÆ÷µÄ `HandlerInterceptor#afterCompletion`¡£ + +×ܵÄÁ÷³ÌÊáÀíÇå³þÁË£¬½ÓÏÂÀ´¾ÍÊÇÖð¸öÁ÷³Ì·ÖÎöÁË¡£ + +### 3\. »ñÈ¡ `HandlerExecutionChain` + +»ñÈ¡ `HandlerExecutionChain` µÄ·½·¨ÔÚ `DispatcherServlet#getHandler` ÖУº + +``` +protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { + if (this.handlerMappings != null) { + // ±éÀúËùÓеÄhandlerMapping£¬ + // ÕâÀïµÄ handlerMapping ÊÇÔÚWebMvcConfigurationSupportÖÐÒýÈëµÄ + for (HandlerMapping mapping : this.handlerMappings) { + // ÕâÀïµ÷ÓþßÌåµÄhandler£¬ÄĸöhandlerÄܹ»´¦Àí¾ÍÖ±½Ó·µ»Ø + HandlerExecutionChain handler = mapping.getHandler(request); + if (handler != null) { + return handler; + } + } + } + return null; +} + +``` + +ÕâÀïµÄ `handlerMappings` ÊÇÔÚ `WebMvcConfigurationSupport` ÖÐÒýÈëµÄ£¬¹ØÓÚÕâÒ»¿éµÄ·ÖÎö£¬¿ÉÄܲο¼ [springmvc demo Óë @EnableWebMvc ×¢½â](https://my.oschina.net/funcy/blog/4678093 "springmvc demo Óë @EnableWebMvc ×¢½â")Ò»ÎÄ£¬ÕâÀïÀ´¿´¿´Õâ¸ö `handlerMappings` ÓÐЩɶ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4465a638ca6c52f24f8c9343986be0bbee9.png) + +¶ÔÓÚ `RequestMappingHandlerMapping` ÏàÐÅ´ó¼ÒÒѾ­ºÜÊìϤ£¬¶ÔÓÚ `@Controller`/`@RequestMapping` ·½Ê½ÊµÏÖµÄ `controller`£¬¶ÔÓ¦µÄ `HandlerMapping` ¾ÍÊÇ `RequestMappingHandlerMapping`¡£ÖÁÓÚÁíÍâµÄÁ½¸ö `HandlerMapping`£¬Ôò·Ö±ð¶ÔÓ¦²»Í¬·½Ê½ÊµÏÖµÄ `controller`£¬¹ØÓÚÕâÒ»µã£¬¸ÐÐËȤµÄС»ï°é¿ÉÒÔ×ÔÐаٶȣ¬ÕâÀï¾Í²»Õ¹¿ªÁË¡£ + +ÎÒÃǼÌÐø¿´ `AbstractHandlerMapping#getHandler` ·½·¨£º + +``` +public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { + // 1\. µ÷ÓþßÌåµÄʵÏÖÈ¥»ñÈ¡handler + Object handler = getHandlerInternal(request); + // Èç¹ûΪ¿ÕʹÓÃĬÈ쵀 + if (handler == null) { + handler = getDefaultHandler(); + } + // ûÓÐĬÈϵķµ»Ø¿Õ + if (handler == null) { + return null; + } + // ³¢ÊÔͨ¹ýBeanNameÈ¥»ñÈ¡handler + if (handler instanceof String) { + String handlerName = (String) handler; + handler = obtainApplicationContext().getBean(handlerName); + } + // 2\. »ñÈ¡ executionChain£¬Æäʵ¾ÍÊÇÕÒµ½ uri ¶ÔÓ¦µÄ Interceptors, + // È»ºóÓëÉÏÃæÕÒµ½µÄhandlerÒ»Æð·â×°µ½HandlerExecutionChain¶ÔÏóÖÐ + // ÕâÀïµÄInterceptors£¬Ò²ÊÇÔÚWebMvcConfigurationSupportÖÐÅäÖÃµÄ + HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); + // 3\. ´¦Àí·ÓòÏà¹ØµÄÅäÖãºCorsHandlerExecutionChain + // ÕâÀï¿ÉÒÔ¿´µ½£¬ËùνµÄcors¿çÓòÅäÖã¬Ò²ÊÇÓÉÀ¹½ØÆ÷ʵÏÖµÄ + if (hasCorsConfigurationSource(handler)) { + CorsConfiguration config = (this.corsConfigurationSource != null + ? this.corsConfigurationSource.getCorsConfiguration(request) : null); + CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); + config = (config != null ? config.combine(handlerConfig) : handlerConfig); + // ½«¿çÓòÏà¹ØµÄÅäÖÃÌí¼Óµ½ Interceptors£¬¼Óµ½À¹½ØÆ÷ListµÄµÚÒ»¸öÖÐ + executionChain = getCorsHandlerExecutionChain(request, executionChain, config); + } + return executionChain; +} + +``` + +Õâ¸ö·½·¨Ö÷Òª×öÁËÈý¼þÊ£º + +1. µ÷ÓþßÌåµÄʵÏÖÈ¥»ñÈ¡ handler£¬Õâ¸ö·½·¨ÊÇÖØµã£¬ÏÂÃæ»á¼ÌÐø½²£» +2. »ñÈ¡ `executionChain`£¬Õâ¸ö `executionChain` ³ýÁ˰üº¬ÁËÉÏÒ»²½µÄ `handler` Í⣬»¹°üº¬ `uri` ¶ÔÓ¦µÄ `Interceptors`£¬»ñÈ¡·½·¨Îª»ñÈ¡ËùÓÐµÄ `Interceptors` ÅäÖã¨ÔÚ `WebMvcConfigurationSupport` ÖÐÅäÖõģ©£¬ÔÙÖðÒ»ÅÐ¶Ï uri ÊÇ·ñ·ûºÏ `Interceptor` µÄ uri ÅäÖã» +3. »ñÈ¡ cors ¿çÓòÅäÖã¬È»ºóÌí¼Óµ½ `executionChain` ÖÐµÄ `Interceptors` ÁбíµÄµÚһλ¡£àÅ£¬Ã»´í£¬cors ¿çÓòÅäÖÃÒ²ÊÇÔÚ `WebMvcConfigurationSupport` ÖÐÅäÖõġ£ + +#### 3.1 ²éÕÒ `HandlerMethod` + +ÎÒÃǽøÈë `getHandlerInternal(xxx)` ·½·¨£º + +> AbstractHandlerMethodMapping#getHandlerInternal + +``` +@Override +protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { + // »ñÈ¡ÇëÇóµÄurl + String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); + request.setAttribute(LOOKUP_PATH, lookupPath); + this.mappingRegistry.acquireReadLock(); + try { + // ÔÚÕâÀï²éÕÒuri¶ÔÓ¦µÄhandlerMethod + HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); + // Èç¹ûhandlerMethod²»Îª¿Õ£¬ÔòÖØÐ´´½¨Ò»¸öHandlerMethod·µ»Ø + return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); + } + finally { + this.mappingRegistry.releaseReadLock(); + } +} + +``` + +ÕâÀﻹÊǵ÷Óà `lookupHandlerMethod(xxx)` À´²éÕÒ `handlerMethod`£¬¼ÌÐø + +> AbstractHandlerMethodMapping#lookupHandlerMethod + +``` +protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) + throws Exception { + List matches = new ArrayList<>(); + // ÏÈ´ÓurlLookupÖÐÕÒ£¬urlLookupÊÇÒ»¸ömap£¬keyÊÇurl£¬valueÊÇLinkedList + List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); + if (directPathMatches != null) { + // ÓÉÓÚ·µ»ØµÄÊÇÒ»¸ö list£¬ÕâÀï»á°ÑËùÓÐµÄÆ¥ÅäµÄ½á¹û·ÅÈëÒ»¸ömatchesÖÐ + addMatchingMappings(directPathMatches, matches, request); + } + if (matches.isEmpty()) { + // Èç¹ûͨ¹ýurlûÕÒµ½£¬Ôò±éÀúËùÓÐµÄ mappings Æ¥Å䣬ƥÅäÀàËÆÓÚ /test/{name} µÄurl + // mappingsÒ²ÊÇÒ»¸ömap£¬keyÊÇRequestMappingInfo£¬ valueÊÇHandlerMethod + addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); + } + // ÕÒµ½×î¼ÑÆ¥ÅäµÄmapping,·µ»ØÆä¶ÔÓ¦µÄHandlerMethod + // ±È½Ï¹æÔòÀ´×ÔÓÚ RequestMappingInfo#compareTo·½·¨ + if (!matches.isEmpty()) { + Comparator comparator = new MatchComparator(getMappingComparator(request)); + matches.sort(comparator); + Match bestMatch = matches.get(0); + if (matches.size() > 1) { + if (CorsUtils.isPreFlightRequest(request)) { + return PREFLIGHT_AMBIGUOUS_MATCH; + } + Match secondBestMatch = matches.get(1); + // ÕÒµ½ÁËÁ½¸ö×î¼ÑÆ¥Å䣬Å׳öÒì³£ + if (comparator.compare(bestMatch, secondBestMatch) == 0) { + Method m1 = bestMatch.handlerMethod.getMethod(); + Method m2 = secondBestMatch. .,m.bvc .getMethod(); + String uri = request.getRequestURI(); + throw new IllegalStateException(...); + } + }W + request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); + handleMatch(bestMatch.mapping, lookupPath, request); + return bestMatch.handlerMethod; + } + else { + return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); + } +} + +``` + +Õâ¸ö·½·¨¾ÍÊÇ´¦Àí handler µÄ»ñÈ¡ÁË¡£ÕâÀïµÄ»ñÈ¡·ÖΪ¼¸¸ö²½Ö裺 + +1. ÏÈ´Ó `urlLookup` ÖÐÕÒ£¬`urlLookup` ÊÇÒ»¸ö `map`£¬`key` ÊÇ `url`£¬`value` ÊÇ `LinkedList`£¬Õâ¸ö²Ù×÷¾ÍÊÇ `map.get(xxx)` ·½·¨£» +2. Èç¹ûͨ¹ý `url` ûÕÒµ½£¬Ôò±éÀúËùÓÐµÄ `mappings` Æ¥Å䣬ƥÅäÀàËÆÓÚ `/test/{name}` µÄ `url`£¬`mappings` Ò²ÊÇÒ»¸ö `map`£¬`key` ÊÇ `RequestMappingInfo`£¬ `value` ÊÇ `HandlerMethod`£» +3. Èç¹ûÕÒµ½Á˶à¸ö `HandlerMethod`£¬Ôò¸ù¾Ý `RequestMappingInfo#compareTo` ·½·¨ÌṩµÄ·½·¨£¬ÕÒµ½×î¼ÑµÄ `RequestMappingInfo` ¶ÔÓ¦µÄ `HandlerMethod`¡£ + +ÎÒÃÇÀ´¿´¿´ÔÚ `mappings` ÀïÊÇÈçºÎÕÒµ½Æ¥ÅäµÄ `RequestMappingInfo` µÄ£º + +> AbstractHandlerMethodMapping#addMatchingMappings + +``` +private void addMatchingMappings(Collection mappings, List matches, + HttpServletRequest request) { + for (T mapping : mappings) { + // Æ¥ÅäÆäËûÌõ¼þ£¬ÕÒµ½ÆäÖÐËùÓзûºÏÌõ¼þµÄ mappings + T match = getMatchingMapping(mapping, request); + if (match != null) { + matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); + } + } +} + +``` + +×îÖÕ·¢ÏÖÆ¥ÅäµÄ´¦ÀíÊÇÔÚ `RequestMappingInfo#getMatchingCondition` ·½·¨ÖУ¬`RequestMappingInfo` »¹ÓÐÒ»¸ö `compareTo` ·½·¨£¬ÎÒÃÇÒ²Ò»²¢²é¿´Ï£º + +> RequestMappingInfo + +``` +/** + * Æ¥Å乿Ôò + * »á·Ö±ðÆ¥Åä ÇëÇó·½·¨(get,postµÈ)¡¢ÇëÇó²ÎÊý¡¢ÇëÇóÍ·µÈ + */ +public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { + RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); + if (methods == null) { + return null; + } + ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); + if (params == null) { + return null; + } + HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); + if (headers == null) { + return null; + } + ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); + if (consumes == null) { + return null; + } + ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); + if (produces == null) { + return null; + } + PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); + if (patterns == null) { + return null; + } + RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); + if (custom == null) { + return null; + } + return new RequestMappingInfo(this.name, patterns, + methods, params, headers, consumes, produces, custom.getCondition()); +} + +/** + * ±È½Ï¹æÔò£¬ÕÒµ½×î¼ÑÆ¥Åä + * »á·Ö±ð±È½Ï ÇëÇó·½·¨(get,postµÈ)¡¢ÇëÇó²ÎÊý¡¢ÇëÇóÍ·µÈ + */ +public int compareTo(RequestMappingInfo other, HttpServletRequest request) { + int result; + if (HttpMethod.HEAD.matches(request.getMethod())) { + result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); + if (result != 0) { + return result; + } + } + result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); + if (result != 0) { + return result; + } + result = this.paramsCondition.compareTo(other.getParamsCondition(), request); + if (result != 0) { + return result; + } + result = this.headersCondition.compareTo(other.getHeadersCondition(), request); + if (result != 0) { + return result; + } + result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); + if (result != 0) { + return result; + } + result = this.producesCondition.compareTo(other.getProducesCondition(), request); + if (result != 0) { + return result; + } + result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); + if (result != 0) { + return result; + } + result = this.customConditionHolder.compareTo(other.customConditionHolder, request); + if (result != 0) { + return result; + } + return 0; +} + +``` + +ÎÞÂÛÆ¥Å䣬»¹ÊDZȽϣ¬¶¼»á¶ÔÇëÇó·½·¨ (get,post µÈ)¡¢ÇëÇó²ÎÊý¡¢ÇëÇóÍ·µÈÒ»Ò»½øÐд¦Àí¡£ + +µ½ÕâÀÎÒÃǾÍÃ÷°×ÁË springmvc ÊÇÈçºÎÕÒµ½ `HandlerMethod` µÄÁË¡£ + +#### 3.2 ²éÕÒ `Interceptors` + +ÎÒÃǻص½ `AbstractHandlerMapping#getHandler`£¬¿´¿´ÊÇÈçºÎ»ñÈ¡ `Interceptor` µÄ£º + +``` +public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { + ... + // 2\. »ñÈ¡ executionChain£¬Æäʵ¾ÍÊÇÕÒµ½ uri ¶ÔÓ¦µÄ Interceptors, + // È»ºóÓëÉÏÃæÕÒµ½µÄhandlerÒ»Æð·â×°µ½HandlerExecutionChain¶ÔÏóÖÐ + // ÕâÀïµÄInterceptors£¬Ò²ÊÇÔÚWebMvcConfigurationSupportÖÐÅäÖÃµÄ + HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); + ... + return executionChain; +} + +``` + +½øÈë `getHandlerExecutionChain` ·½·¨£º + +> AbstractHandlerMapping#getHandlerExecutionChain + +``` +protected HandlerExecutionChain getHandlerExecutionChain(Object handler, + HttpServletRequest request) { + HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? + (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); + // »ñÈ¡µ±Ç°µÄÇëÇó·¾¶ + String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH); + for (HandlerInterceptor interceptor : this.adaptedInterceptors) { + if (interceptor instanceof MappedInterceptor) { + MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; + // Åжϵ±Ç°ÇëÇó·¾¶ÊÇ·ñÂú×ãinterceptorÀïÅäÖõÄ·¾¶ + if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { + chain.addInterceptor(mappedInterceptor.getInterceptor()); + } + } + else { + chain.addInterceptor(interceptor); + } + } + return chain; +} + +``` + +Õâ¸ö·½·¨±È½Ï¼òµ¥£¬Ïà¹ØÄÚÈÝÒѾ­ÔÚ´úÂëÖÐ×öÁË×¢ÊÍ£¬¾Í²»¶à˵ÁË ¡£ + +#### 3.3 ´¦Àí cors ¿çÓòÅäÖà + +ÎÒÃÇÔÙÀ´¿´¿´¿çÓòÅäÖõĴ¦Àí£º + +``` +public final HandlerExecutionChain getHandler(HttpServletRequest request) + throws Exception { + ... + // 3\. ´¦Àí·ÓòÏà¹ØµÄÅäÖãºCorsHandlerExecutionChain + // ÕâÀï¿ÉÒÔ¿´µ½£¬ËùνµÄcors¿çÓòÅäÖã¬Ò²ÊÇÓÉÀ¹½ØÆ÷ʵÏÖµÄ + if (hasCorsConfigurationSource(handler)) { + // »ñÈ¡¿çÓòÅäÖà + CorsConfiguration config = (this.corsConfigurationSource != null + ? this.corsConfigurationSource.getCorsConfiguration(request) : null); + CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); + config = (config != null ? config.combine(handlerConfig) : handlerConfig); + // ½«¿çÓòÏà¹ØµÄÅäÖÃÌí¼Óµ½ Interceptors£¬¼Óµ½À¹½ØÆ÷ListµÄµÚÒ»¸öÖÐ + executionChain = getCorsHandlerExecutionChain(request, executionChain, config); + } + return executionChain; +} + +``` + +¿çÓòÏà¹ØÅäÖÃÒ²¿ÉÒÔ `WebMvcConfigurationSupport` ÖÐÅäÖ㺠+ +``` +protected void addCorsMappings(CorsRegistry registry) { + ... +} + +``` + +springmvc »ñÈ¡µ½¿çÓòÅäÖú󣬻á°ÑÏà¹ØÅäÖÃÌí¼Óµ½ `HandlerExecutionChain` ÖУº + +``` +# AbstractHandlerMapping#getCorsHandlerExecutionChain +protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, + HandlerExecutionChain chain, @Nullable CorsConfiguration config) { + if (CorsUtils.isPreFlightRequest(request)) { + HandlerInterceptor[] interceptors = chain.getInterceptors(); + chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors); + } + else { + // Ìí¼Óµ½InterceptorsµÄÊ×λ + chain.addInterceptor(0, new CorsInterceptor(config)); + } + return chain; +} + +# HandlerExecutionChain#addInterceptor(int, HandlerInterceptor) +public void addInterceptor(int index, HandlerInterceptor interceptor) { + // Æäʵ¾ÍÊDzÙ×÷Ò»¸ölist + initInterceptorList().add(index, interceptor); +} + +``` + +ÔÚ `HandlerExecutionChain` ÖУ¬ÓÐÒ»¸ö `List` ÓÃÀ´´æÈë `Interceptor`£¬»ñÈ¡µ½µÄ¿çÓòÅäÖ㬻áÌí¼Óµ½Õâ¸ö `List` µÄ `index=0` µÄλÖᣠ+ +µ½ÕâÀ`handler` ¾Í»ñÈ¡Íê³ÉÁË£¬Õâ¸ö `handler` °üº¬Á½²¿·Ö£º + +* `HandlerMethod`: ´¦ÀíÇëÇóµÄ·½·¨£¬ÓÉÓÚ±¾ÎÄÖ»·ÖÎö `@Controller` ·½Ê½µÄ controller£¬¿ÉÒÔ¼òµ¥Àí½âΪÓÐ `@RequestMapping` ×¢½âµÄ·½·¨£» +* `List`: À¹½ØÆ÷Á´£¬Èç¹ûÓпçÓòÅäÖã¬ÄÇô¿çÓòÅäÖûá·ÅÔÚÕâ¸ö List µÄµÚһλ¡£ + +### 4\. »ñÈ¡ `HandlerAdapter` + +Ôٻص½ `DispatcherServlet#doDispatch` ·½·¨£¬ÎÒÃÇÀ´¿´¿´»ñÈ¡ `HandlerAdapter` µÄ·½·¨£º + +``` +protected void doDispatch(HttpServletRequest request, HttpServletResponse response) + throws Exception { + ... + // 2\. »ñÈ¡¶ÔÓ¦µÄhandlerAdapter£¬ÓÃÀ´ÔËÐÐ handler(xxx) + HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); + ... + +``` + +½øÈë `getHandlerAdapter(xxx)` ·½·¨£º + +> DispatcherServlet#getHandlerAdapter + +``` +protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { + // handlerAdapters ÀïµÄbean£¬Ò²ÊÇÓÉWebMvcConfigurationSupportÒýÈëµÄ + if (this.handlerAdapters != null) { + for (HandlerAdapter adapter : this.handlerAdapters) { + // ²»Í¬µÄhandlerAdapterµÄÅжϷ½·¨²»Í¬ + if (adapter.supports(handler)) { + return adapter; + } + } + } + throw new ServletException(...); +} + +``` + +¿ÉÒÔ¿´µ½£¬ÕâÀï»áÕÒµ½µ±Ç°ËùÓÐµÄ `adapter`£¬È»ºó±éÀú£¬Öð¸öÅжÏÊÇ·ñÄÜ´¦Àíµ±Ç°µÄ `handler`£¬ËùÓÐµÄ `adapter` ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-dedb2d58b9e99091aeb559f6c5a4aab4ccc.png) + +ÔÙÀ´¿´¿´ÈçºÎÅжÏÊÇ·ñÄÜ´¦Àíµ±Ç°µÄ `handler` µÄ£¬ÎÒÃÇ¿´ÆäÖÐÒ»¸ö `handler`£¬½øÈë `AbstractHandlerMethodAdapter#supports` ·½·¨£º + +> AbstractHandlerMethodAdapter#supports + +``` +@Override +public final boolean supports(Object handler) { + // ÅжÏhandlerÊÇ·ñΪHandlerMethodµÄʵÀý + return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); +} + +``` + +ÕâÀï½ö×öÁËÒ»¸ö¼òµ¥µÄÅжϣ¬È»ºóÔÙµ÷Óà `supportsInternal` ·½·¨£¬¼ÌÐø£º + +> RequestMappingHandlerAdapter#supportsInternal + +``` +protected boolean supportsInternal(HandlerMethod handlerMethod) { + return true; +} + +``` + +Õâ¸ö·½·¨Ö±½Ó·µ»Ø true, ÓÉÓڿɼû£¬Èç¹û `handler` µÄʵÀýÊÇ `HandlerMethod`£¬ÄÇô¾Í»á·µ»Ø `RequestMappingHandlerAdapter`. + +ÕâÒ»²½ÕÒµ½µÄ `adapter` Ϊ `RequestMappingHandlerAdapter`£¬Õâ¸ö `adapter` ÓÐʲôÓÃÄØ£¿ÏÞÓÚÆª·ù£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬Ê£ÏµÄÁ÷³ÌÏÂÆªÎÄÕ¼ÌÐø·ÖÎö¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4717420](https://my.oschina.net/funcy/blog/4717420) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" new file mode 100644 index 0000000..daed837 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\220/\350\257\267\346\261\202\346\211\247\350\241\214\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\344\271\213\346\211\247\350\241\214Handler\346\226\271\346\263\225.md" @@ -0,0 +1,668 @@ +±¾ÎÄÊÇ **springmvc ÇëÇóÖ´ÐÐÁ÷³Ì**µÄµÚ¶þƪÎÄÕ£¬ÔÚÉÏһƪÎÄÕÂÖУ¬ÎÒÃÇ·ÖÎöÁË `DispatcherServlet#doDispatch` ·½·¨£¬×ܽá³öÇëÇóÖ´ÐзÖΪÈçϲ½Ö裺 + +1. »ñÈ¡¶ÔÓ¦µÄ `HandlerExecutionChain`, »ñÈ¡µÄ `HandlerExecutionChain` Öаüº¬ÕæÕýµØ´¦ÀíÆ÷£¨`Controller` Öеķ½·¨£©ºÍÒ»×é `HandlerInterceptor` À¹½ØÆ÷£» +2. »ñÈ¡¶ÔÓ¦µÄ `handlerAdapter`£¬¸Ã¶ÔÏóÓÃÀ´ÔËÐÐ `handler(xxx)` ·½·¨£» +3. Ö´ÐÐ spring µÄÀ¹½ØÆ÷£¬ÔËÐÐ `HandlerInterceptor#preHandle` ·½·¨£» +4. ´¦ÀíÇëÇó£¬Ò²¾ÍÊÇͨ¹ýÉÏÃæ»ñÈ¡µ½µÄ `handlerAdapter` À´µ÷Óà `handle(xxx)` ·½·¨£» +5. Ö´ÐÐ spring µÄÀ¹½ØÆ÷£¬ÔËÐÐ `HandlerInterceptor#postHandle` ·½·¨£» +6. ´¦Àí·µ»Ø½á¹û£¬ÕâÀï»áäÖȾÊÓͼ£¬ÒÔ¼°Ö´ÐÐ spring À¹½ØÆ÷µÄ `HandlerInterceptor#afterCompletion`¡£ + +½Ó×Å£¬ÎÒÃǼÌÐø·ÖÎö `HandlerExecutionChain` µÄ»ñÈ¡ÒÔ¼° `handlerAdapter` µÄ»ñÈ¡£¬Êé½ÓÉϻأ¬±¾ÎĽ«¼ÌÐø·ÖÎö½ÓÏÂÀ´µÄ²½Öè¡£ + +### 5\. Ö´ÐÐ spring µÄÀ¹½ØÆ÷£º`HandlerInterceptor#preHandle` + +``` +protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { + ... + // 3\. ÔËÐÐspringµÄÀ¹½ØÆ÷, ÔËÐÐ HandlerInterceptor#preHandle ·½·¨ + // Õâ¸ömappedHandler£¬¾ÍÊǵÚ1²½»ñÈ¡µÄHandlerExecutionChain + if (!mappedHandler.applyPreHandle(processedRequest, response)) { + return; + } + ... +} + +``` + +ÉÏÃæµÄ `mappedHandler`£¬¾ÍÊÇµÚ 1 ²½»ñÈ¡µÄ `HandlerExecutionChain`£¬½øÈë `HandlerExecutionChain#applyPreHandle`£º + +> HandlerExecutionChain#applyPreHandle + +``` +/** + * Ö´ÐÐ HandlerInterceptor#preHandle ·½·¨ + */ +boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) + throws Exception { + // »ñÈ¡ËùÓеÄÀ¹½ØÆ÷ + HandlerInterceptor[] interceptors = getInterceptors(); + if (!ObjectUtils.isEmpty(interceptors)) { + // ±éÀúÖ´ÐÐ preHandle ·½·¨ + for (int i = 0; i < interceptors.length; i++) { + HandlerInterceptor interceptor = interceptors[i]; + if (!interceptor.preHandle(request, response, this.handler)) { + // ʧ°ÜÁË£¬»¹»á¼ÌÐøÖ´ÐÐ HandlerInterceptor#afterCompletion ·½·¨ + triggerAfterCompletion(request, response, null); + return false; + } + this.interceptorIndex = i; + } + } + return true; +} + +/** + * Ö´ÐÐ HandlerInterceptor#afterCompletion ·½·¨ + * ΪÁ˱£Ö¤HandlerInterceptor#afterCompletionµÄÖ´ÐУ¬ + * ½ÓÏÂÀ´µÄ·ÖÎö»á¿´µ½£¬Õâ¸ö·½·¨»áÔÚ¶à¸ö·½·¨µ÷Óõ½ + */ +void triggerAfterCompletion(HttpServletRequest request, + HttpServletResponse response, @Nullable Exception ex) throws Exception { + HandlerInterceptor[] interceptors = getInterceptors(); + if (!ObjectUtils.isEmpty(interceptors)) { + // ±éÀúÖ´ÐÐ HandlerInterceptor#afterCompletion ·½·¨ + for (int i = this.interceptorIndex; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + try { + interceptor.afterCompletion(request, response, this.handler, ex); + } + catch (Throwable ex2) { + logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); + } + } + } +} + +``` + +ÎÒÃÇÀ´¿´Ò»ÑÛ `HandlerExecutionChain`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-40ba62630b5d83a16ed9aba35aba48af8be.png) + +ÔÙÀ´¿´¿´´«Èë `HandlerInterceptor` µÄ `handler` ÊÇɶ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6c50a936804c1db4a7b817dbb5ff417034d.png) + +Õâ¸ö `handler` ¾ÍÊÇ `HandlerMethod`£¬ÀïÃæ°üº¬µÄÐÅÏ¢»¹ÊÇͦ·á¸»µÄ£¬Ïñ `bean`/`beanFactory`/`method`£¬ÀûÓÃÕ⼸¸öÊôÐÔ£¬ÎÒÃÇ¿ÉÒÔ¶ÔÆä½øÐи÷ÖÖ²Ù×÷¡£ + +### 6\. ·½·¨µÄÖ´ÐУº`AbstractHandlerMethodAdapter#handle` + +ÎÒÃÇÔٻص½ `DispatcherServlet#doDispatch`£¬Ö´ÐÐÍê `HandlerInterceptor#preHandle` ·½·¨ºó£¬¾ÍÀ´µ½ËùÓÐÁ÷³ÌÖеÄÖØÍ·Ï·£º`handler` µÄÖ´ÐУ¬Ò²¾ÍÊÇ `controller` ÖУ¬`url` ¶ÔÓ¦µÄ·½·¨Ö´ÐУº + +``` +protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { + ... + // 4\. ͨ¹ýÉÏÃæ»ñÈ¡µ½µÄhandlerAdapterÀ´µ÷ÓÃhandle + mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); + ... +} + +``` + +һ·¸ú½øÈ¥£¬×îÖÕÀ´µ½ÁË `RequestMappingHandlerAdapter#invokeHandlerMethod` ·½·¨£º + +``` +protected ModelAndView invokeHandlerMethod(HttpServletRequest request, + HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { + // °ü×°requesÓërequest¶ÔÏó + ServletWebRequest webRequest = new ServletWebRequest(request, response); + try { + // »ñÈ¡ @InitBinder ×¢½âµÄ·½·¨£¬ + // °üº¬µ±Ç°controllerÓë @ControllerAdvice ±ê×¢µÄÀàÀïµÄ @InitBinder ×¢½âµÄ·½·¨ + WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); + // »ñÈ¡ @ModelAttribute ×¢½âµÄ·½·¨£¬ + // °üº¬µ±Ç°controllerÓë @ControllerAdvice ±ê×¢µÄÀàÀïµÄ @ModelAttribute ×¢½âµÄ·½·¨ + ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); + // ´´½¨·½·¨Ö´ÐжÔÏó + ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); + if (this.argumentResolvers != null) { + invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); + } + if (this.returnValueHandlers != null) { + invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); + } + invocableMethod.setDataBinderFactory(binderFactory); + invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); + // ´´½¨ModelAndViewµÄÈÝÆ÷ + ModelAndViewContainer mavContainer = new ModelAndViewContainer(); + mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); + modelFactory.initModel(webRequest, mavContainer, invocableMethod); + mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); + // ´¦ÀíÒì²½ÇëÇó + AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); + asyncWebRequest.setTimeout(this.asyncRequestTimeout); + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + asyncManager.setTaskExecutor(this.taskExecutor); + asyncManager.setAsyncWebRequest(asyncWebRequest); + asyncManager.registerCallableInterceptors(this.callableInterceptors); + asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); + if (asyncManager.hasConcurrentResult()) { + Object result = asyncManager.getConcurrentResult(); + mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; + asyncManager.clearConcurrentResult(); + LogFormatUtils.traceDebug(logger, traceOn -> { + String formatted = LogFormatUtils.formatValue(result, !traceOn); + return "Resume with async result [" + formatted + "]"; + }); + invocableMethod = invocableMethod.wrapConcurrentResult(result); + } + // Ö´ÐÐControllerµÄ·½·¨£¨Öص㣩 + invocableMethod.invokeAndHandle(webRequest, mavContainer); + if (asyncManager.isConcurrentHandlingStarted()) { + return null; + } + // ´¦Àí·µ»Ø½á¹û + return getModelAndView(mavContainer, modelFactory, webRequest); + } + finally { + webRequest.requestCompleted(); + } +} + +``` + +Õâ¸ö·½·¨Óе㳤£¬µ«Öص㷽·¨Ö»ÓÐÒ»ÐУº`invocableMethod.invokeAndHandle(webRequest, mavContainer);`£¬ÔÚÕâ¸ö·½·¨Ç°µÄ²¿·Ö¶¼ÊÇÔÚ×ö·½·¨Ö´ÐÐǰµÄ×¼±¸¹¤×÷£¬Èç»ñÈ¡ `@InitBinder` ×¢½âµÄ·½·¨¡¢»ñÈ¡ `@ModelAttribute` ×¢½âµÄ·½·¨£¬×¼±¸·½·¨²ÎÊý `webRequest`(°ü×° `request` Óë `response` ¶ÔÏó) Óë `mavContainer`(`ModelAndView` °ü×°¶ÔÏó) µÈ¡£ÕâÀïÎÒÃÇÖ±½Ó½øÈë `invocableMethod.invokeAndHandle`£¬¿´¿´·½·¨ÊÇÈçºÎÖ´Ðеģº + +> ServletInvocableHandlerMethod#invokeAndHandle + +``` +public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, + Object... providedArgs) throws Exception { + // Ö´ÐÐhandler·½·¨ + Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); + // ÉèÖà RequestHandled ÊôÐÔÖµ£¬springmvc»á¸ù¾Ý¸ÃÖµÅжÏÒª²»ÒªÌø×ª + if (returnValue == null) { + if (isRequestNotModified(webRequest) || getResponseStatus() != null + || mavContainer.isRequestHandled()) { + disableContentCachingIfNecessary(webRequest); + mavContainer.setRequestHandled(true); + return; + } + } + else if (StringUtils.hasText(getResponseStatusReason())) { + mavContainer.setRequestHandled(true); + return; + } + mavContainer.setRequestHandled(false); + Assert.state(this.returnValueHandlers != null, "No return value handlers"); + try { + // ´¦Àí·µ»Ø½á¹û + this.returnValueHandlers.handleReturnValue( + returnValue, getReturnValueType(returnValue), mavContainer, webRequest); + } + catch (Exception ex) { + throw ex; + } +} + +``` + +ÕâÀï·½·¨ÏÈÊǵ÷ÓÃÁË `invokeForRequest` Ö´Ðз½·¨£¬È»ºóÔÙ¸ù¾Ý·½·¨µÄ·µ»ØÖµÀ´ÉèÖà `mavContainer` µÄ `RequestHandled` Öµ£¬×îºó´¦Àí·µ»Ø½á¹û¡£¼ÌÐø¸ú½ø `invokeForRequest` ·½·¨£º + +> InvocableHandlerMethod#invokeForRequest + +``` +@Nullable +public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainermavContainer, + Object... providedArgs) throws Exception { + // ´¦Àí²ÎÊý°ó¶¨ + Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); + // ·´Éäµ÷Ó÷½·¨ + return doInvoke(args); +} + +``` + +Õâ¸ö·½·¨ÀïÏÈÊÇ´¦ÀíÁ˲ÎÊý½âÎö£¬È»ºóʹÓ÷´Éä½øÐз½·¨µ÷Óá£ÊÂʵÉÏ£¬Õû¸öÖ´ÐÐÁ÷³ÌÖУ¬×îºËÐĵľÍÊDzÎÊý½âÎöÁË£¬ÔÚ controller ÀïµÄ handler ·½·¨ÖУ¬ÎÒÃÇ¿ÉÒÔÕâÑùÖ¸¶¨²ÎÊý£º + +``` +// Ö±½Ó´«²Î +@RequestMapping("xxx") +public Object test(String name) { + ... +} + +// ÔÚ²ÎÊýÉϱêÓÐ@RequestParam¡¢@RequestHeaderµÈ×¢½â +@RequestMapping("xxx") +public Object test(@RequestParam("name") String name, + @RequestHeader("uid") String uid) { + ... +} + +// ½«´«ÈëµÄ²ÎÊý·âװΪ¶ÔÏó +@RequestMapping("xxx") +public Object test(User user) { + ... +} + +// ÉÏÃæµÄ·½·¨¶¼ÊÇʹÓÃform±íµ¥´«²Î(Ò²¾ÍÊÇk1=v1&2=v2&...µÄ·½·¨) +// Õâ¸ö·½·¨Ê¹Óà RequestBody ·½Ê½´«²Î£¬½«²ÎÊýÄÚÈÝ·ÅÈëÏûÏ¢ÌåÖÐ +@RequestMapping("xxx") +public Object test(@RequestBody User user) { + ... +} + +... + +``` + +µ±ÎÒÃǰ´¹æ·¶´«Èë²ÎÊýʱ£¬springmvc ¶¼ÄÜÕý³£´¦Àí£¬ÎÒÃÇÀ´¿´¿´ springmvc ÊÇÈçºÎ×öµ½ÕâÒ»µãµÄ¡£ + +#### ²ÎÊý½âÎö + +¼ÌÐø£¬½øÈë `InvocableHandlerMethod#getMethodArgumentValues` ·½·¨£º + +> InvocableHandlerMethod#getMethodArgumentValues + +``` +protected Object[] getMethodArgumentValues(NativeWebRequest request, + @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { + // »ñÈ¡·½·¨µÄËùÓвÎÊý£¬¿ÉÒÔ¼òµ¥Àí½âΪÀûÓ÷´Éä»ñÈ¡handler·½·¨²ÎÊý£¬È»ºó°üװΪ MethodParameter + MethodParameter[] parameters = getMethodParameters(); + if (ObjectUtils.isEmpty(parameters)) { + return EMPTY_ARGS; + } + Object[] args = new Object[parameters.length]; + // ÒÀ´Î´¦Àíÿ¸ö²ÎÊý + for (int i = 0; i < parameters.length; i++) { + MethodParameter parameter = parameters[i]; + parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); + args[i] = findProvidedArgument(parameter, providedArgs); + if (args[i] != null) { + continue; + } + // ÅжÏÊÇ·ñÓвÎÊý½âÎöÀàÖ§³Öµ±Ç°²ÎÊýµÄ½âÎö + if (!this.resolvers.supportsParameter(parameter)) { + throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); + } + try { + // ´¦Àí²ÎÊý½âÎö + args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, + this.dataBinderFactory); + } + catch (Exception ex) { + throw ex; + } + } + return args; +} + +``` + +Õâ¸ö·½·¨ÏÈ»ñÈ¡ÁË handler ·½·¨µÄ²ÎÊý (¿ÉÒÔ¼òµ¥Àí½âΪÀûÓ÷´Éä»ñÈ¡ handler ·½·¨²ÎÊý£¬È»ºó°üװΪ `MethodParameter`)£¬È»ºó¶ÔÕâЩ²ÎÊýÖð¸ö½âÎö¡£½âÎöʱ£¬»áÓõ½Á½¸öÖØÒªµÄ·½·¨£º`resolvers.supportsParameter(...)` Óë `resolvers.resolveArgument(...)`£¬ÕâÁ½¸ö·½·¨×îÖÕµ÷Óõķ½·¨ÔÚ `HandlerMethodArgumentResolverComposite` ÖУº + +> HandlerMethodArgumentResolverComposite + +``` +@Nullable +public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainermavContainer, + NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { + // »ñȡһ¸ö½âÎöÆ÷ + HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); + if (resolver == null) { + throw new IllegalArgumentException(...); + } + // ½âÎö²ÎÊý£¬µ÷Óà HandlerMethodArgumentResolver#resolveArgument ·½·¨ + return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); +} + +private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { + HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); + if (result == null) { + // ±éÀúËùÓеĽâÎöÆ÷ + for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { + // ÕÒµ½ÆäÖÐÒ»¸ö½âÎöÆ÷£¬·µ»Ø + // µ÷Óà HandlerMethodArgumentResolver#supportsParameter ·½·¨ + if (resolver.supportsParameter(parameter)) { + result = resolver; + this.argumentResolverCache.put(parameter, result); + break; + } + } + } + return result; +} + +``` + +ÕâÁ½¸ö·½·¨Ö÷ÒªÊDZéÀú½âÎöÆ÷£¬È»ºóµ÷Óà `HandlerMethodArgumentResolver#supportsParameter` Óë `HandlerMethodArgumentResolver#resolveArgument` ´¦Àí¾ßÌåµÄ²Ù×÷¡£`HandlerMethodArgumentResolver` ÊǸö½Ó¿Ú£¬ÀïÃæ½öÓÐÁ½¸ö·½·¨£º + +``` +public interface HandlerMethodArgumentResolver { + /** + * µ±Ç°ÊÇ·ñÖ§³Ö´¦Àíµ±Ç°²ÎÊý + */ + boolean supportsParameter(MethodParameter parameter); + + /** + * ¾ßÌåµÄ½âÎö²Ù×÷ + */ + @Nullable + Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; +} + +``` + +Õâ¸ö¾ÍÊÇÕæÕýµÄ²ÎÊý½âÎöÆ÷ÁË¡£ÔÚ springmvc ÖУ¬ÌṩÁ˶àÉÙÖÖ²ÎÊý½âÎöÆ÷ÄØ£¿¾­¹ý±¾È˵ĵ÷ÊÔ£¬·¢ÏÖ¶à´ï 26 ¸ö£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fd30bc847fc2cf7082de3731e68df6ae5f9.png) ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-894b391bbe4567734ee7237d900bc55ee31.png) + +ÕýÊÇÔÚÕâЩ²ÎÊý½âÎöÆ÷µÄ°ïÖúÏ£¬springmvc ÄÜÖ§³Ö¶àÖÖ²ÎÊý½ÓÊÕ·½Ê½£¡¶ÔÓÚ²ÎÊý½âÎöÆ÷£¬ÓÉÓÚÉè¼Æ±È½Ï¸´ÔÓ£¬Éæ¼°¶àÖÖ´«²Î·½Ê½£¬±¾ÎIJ¢²»ÏëÕ¹¿ªÌÖÂÛ£¬¸ÐÐËȤµÄС»ï°é¿É×ÔÐвéÔÄÏà¹ØÎĵµ¡£ + +#### Ö´ÐÐ handler ·½·¨ + +´¦ÀíÍê²ÎÊý½âÎöºó£¬¾Í¿ªÊ¼Ö´ÐÐ handler ·½·¨ÁË¡£ÈÃÎÒÃǻص½ `InvocableHandlerMethod#invokeForRequest` ·½·¨£º + +> InvocableHandlerMethod#invokeForRequest + +``` +@Nullable +public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainermavContainer, + Object... providedArgs) throws Exception { + // ´¦Àí²ÎÊý°ó¶¨ + Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); + // ·´Éäµ÷Ó÷½·¨ + return doInvoke(args); +} + +``` + +½øÈë `doInvoke` ·½·¨£º + +``` +@Nullable +protected Object doInvoke(Object... args) throws Exception { + // ʹÓ÷´ÉäÖ´Ðз½·¨ + ReflectionUtils.makeAccessible(getBridgedMethod()); + try { + // ÕâÀï¾ÍÊÇ·´Éä²Ù×÷ + return getBridgedMethod().invoke(getBean(), args); + } + catch (...) { + ... + } +} + +``` + +Õâ¸ö·½·¨ºÜ¼òµ¥£¬¾ÍÊÇÀûÓ÷´ÉäÀ´½øÐз½·¨Ö´Ðеģ¬¾Í²»¹ý¶à·ÖÎöÁË¡£ + +#### ´¦Àí·µ»Ø²ÎÊý + +ÈÃÎÒÃǻص½ `ServletInvocableHandlerMethod#invokeAndHandle` ·½·¨£º + +> ServletInvocableHandlerMethod#invokeAndHandle + +``` +public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, + Object... providedArgs) throws Exception { + // Ö´ÐÐhandle·½·¨ + Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); + ... + try { + // ´¦Àí·µ»Ø½á¹û + this.returnValueHandlers.handleReturnValue( + returnValue, getReturnValueType(returnValue), mavContainer, webRequest); + } + catch (Exception ex) { + throw ex; + } +} + +``` + +´¦ÀíÍê·½·¨Ö´Ðк󣬽Óמʹ¦Àí·µ»Ø½á¹û£º + +> HandlerMethodReturnValueHandlerComposite#handleReturnValue + +``` +@Override +public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { + // ¸ù¾Ý·µ»ØÀàÐÍÕÒÒ»¸öºÏÊʵÄhandlerÀ´´¦Àí·µ»ØÊý¾Ý + HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); + if (handler == null) { + throw new IllegalArgumentException(...); + } + handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); +} + +@Nullable +private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameterreturnType) { + boolean isAsyncValue = isAsyncReturnValue(value, returnType); + // ±éÀú£¬Öð¸öÅжÏÊÇ·ñÄÜ´¦Àíµ±Ç°·µ»ØÀàÐÍ + for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { + if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { + continue; + } + if (handler.supportsReturnType(returnType)) { + return handler; + } + } + return null; +} + +``` + +»ñÈ¡ `ReturnValueHandler` µÄÌ×·ÓëÇ°Ãæ»ñÈ¡ `ArgumentResolver` µÄÌ×·Ïà¹ØÎÞ¼¸£¬`ReturnValueHandler` ÊÇ `HandlerMethodReturnValueHandler` µÄ×ÓÀ࣬`HandlerMethodReturnValueHandler` ´úÂëÈçÏ£º + +``` +public interface HandlerMethodReturnValueHandler { + + /** + * Åжϵ±Ç°ReturnValueHandlerÄÜ·ñ´¦Àí´«ÈëµÄreturnType + */ + boolean supportsReturnType(MethodParameter returnType); + + /** + * ¾ßÌåµÄ´¦ÀíÂß¼­ + */ + void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; + +} + +``` + +Í¬Ç°Ãæ²ÎÊý½âÎöÆ÷Ò»Ñù£¬Õâ¸ö½Ó¿ÚÀïÒ²ÓÐÁ½¸ö·½·¨£º + +* `boolean supportsReturnType(xxx)`£ºÅжϵ±Ç° `ReturnValueHandler` ÄÜ·ñ´¦Àí´«ÈëµÄ `returnType` +* `void handleReturnValue(xxx)`£º¾ßÌåµÄ´¦ÀíÂß¼­ + +ͬÑùµØ£¬springmvc Ò²ÌṩÁ˷dz£¶àµÄʵÏÖÀ´´¦Àí·µ»Ø²ÎÊý£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8e3c1fd4dca7c13efbceac5800e26145963.png) + +¶ÔÓÚ²ÎÊýµÄ·µ»Ø£¬ÎÒÃÇÀ´¿´Ò»¸ö¼òµ¥µÄʾÀý£º + +Èç¹ûÎÒÃÇÏñÕâÑù·µ»ØÖµÊ±£º + +``` +@Controller +@RequestMapping("/xxx") +public class XxxController { + + @RequestMapping("/index") + public String index() { + return "index"; + } +} + +``` + +·µ»Øµ½Ò³ÃæµÄ½«ÊÇÒ»¸öÊÓͼ£¬¶ÔÓ¦µÄ `HandlerMethodReturnValueHandler` Ϊ `ViewNameMethodReturnValueHandler`£º + +``` +public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler { + + @Nullable + private String[] redirectPatterns; + + // Ê¡ÂÔ redirectPatterns µÄsetterÓëgetter·½·¨ + ... + + @Override + public boolean supportsReturnType(MethodParameter returnType) { + Class paramType = returnType.getParameterType(); + // Ö§³Ö´¦ÀíµÄ·µ»ØÖµÀàÐÍ£º·µ»ØÖµÀàÐÍΪvoid£¬»òÕßΪ×Ö·û´®ÀàÐÍ + return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType)); + } + + /** + * ¾ßÌåµÄ´¦ÀíÂß¼­ + */ + @Override + public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { + + if (returnValue instanceof CharSequence) { + // viewName ¾ÍÊÇ·µ»ØÖµ + String viewName = returnValue.toString(); + mavContainer.setViewName(viewName); + if (isRedirectViewName(viewName)) { + // ÊÇ·ñÐèÒªÌø×ª + mavContainer.setRedirectModelScenario(true); + } + } + else if (returnValue != null) { + throw new UnsupportedOperationException(...); + } + } + + /** + * ÅжÏÊÇ·ñÐèÒªÌø×ª + */ + protected boolean isRedirectViewName(String viewName) { + // this.redirectPatterns ĬÈÏΪnull£¬¿ÉÒÔ×ÔÐе÷Óà setter ·½·¨ÉèÖà + return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) + // Æ¥Åä redirect: ¿ªÍ·£¬Ò²¾ÍÊÇ˵£¬Èç¹ûÎÒÃÇ·µ»Ø "redirect:index"£¬¾Í±íÃ÷¸Ã½á¹ûÐèÒªÌø×ª + || viewName.startsWith("redirect:")); + } + +} + +``` + +Õâ¸öÀà±È½Ï¼òµ¥£¬¹Ø¼ü²¿·ÖÒÑÔÚ´úÂëÖÐ×÷ÁË×¢ÊÍ£¬ÕâÀï¾Í²»¶à˵ÁË¡£ÖµµÃÒ»ÌáµÄÊÇ£¬ÔÚ `handleReturnValue(xxx)` ·½·¨ÖУ¬springmvc ½«·µ»ØµÄ×Ö·û´®ÉèÖÃΪ `viewName` ÐèҪעÒâÏ£¬ºóÃæÔÚ´¦ÀíÊÓͼʱ£¬»áÀûÓÃÕâ¸ö `viewName` Äõ½¶ÔÓ¦µÄ `View`¡£ + +#### »ñÈ¡ ModelAndView + +ÈÃÎÒÃǻص½ `RequestMappingHandlerAdapter#invokeHandlerMethod` ·½·¨£º + +``` +@Nullable +protected ModelAndView invokeHandlerMethod(HttpServletRequest request, + HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { + ServletWebRequest webRequest = new ServletWebRequest(request, response); + try { + ... + // Ö´ÐÐControllerµÄ·½·¨ + invocableMethod.invokeAndHandle(webRequest, mavContainer); + if (asyncManager.isConcurrentHandlingStarted()) { + return null; + } + // ´¦ÀíÖ´Ðнá¹û£¬´ÓÖÐÄõ½ ModelAndView + return getModelAndView(mavContainer, modelFactory, webRequest); + } + finally { + webRequest.requestCompleted(); + } +} + +``` + +Ö´ÐÐÍê `invocableMethod.invokeAndHandle(webRequest, mavContainer)` ·½·¨ºó½ÓמÍÊÇ´ÓÖ´Ðнá¹ûÖÐÄõ½ `ModelAndView` ÁË£¬½øÈë `RequestMappingHandlerAdapter#getModelAndView` ·½·¨£º + +``` +private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, + ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { + // Õâ¸ö ModelFactory£¬¾ÍÊÇÇ°Ãæ»ñÈ¡µÄ °üº¬@ModelAttribute ×¢½âµÄ·½·¨ + modelFactory.updateModel(webRequest, mavContainer); + if (mavContainer.isRequestHandled()) { + return null; + } + ModelMap model = mavContainer.getModel(); + // ´´½¨ÊÓͼ¶ÔÏ󣬰ÑmavContainer.getViewName()´«Èëµ½ModelAndViewµÄ¹¹Ôì·½·¨ÖÐ + ModelAndView mav = new ModelAndView(mavContainer.getViewName(), + model, mavContainer.getStatus()); + if (!mavContainer.isViewReference()) { + mav.setView((View) mavContainer.getView()); + } + // ´¦ÀíÖØ¶¨Ïò²ÎÊý + if (model instanceof RedirectAttributes) { + Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); + if (request != null) { + RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); + } + } + return mav; +} + +``` + +¿ÉÒÔ¿´µ½£¬`ModelAndView` ¾ÍÊÇ´ÓÇ°ÃæÖ´Ðнá¹ûµÄ `viewName` µÃµ½µÄ¡£ + +ÖÁ´Ë£¬`AbstractHandlerMethodAdapter#handle` Ö´ÐÐÍê±Ï¡£ + +### 7\. Ö´ÐÐÀ¹½ØÆ÷£º`HandlerInterceptor#postHandle` + +ÈÃÎÒÃÇÔٻص½ `DispatcherServlet#doDispatch` ·½·¨£º + +> DispatcherServlet#doDispatch + +``` +protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { + ... + + try { + ... + try { + ... + // 4.ͨ¹ýÉÏÃæ»ñÈ¡µ½µÄhandlerAdapterÀ´µ÷ÓÃhandle + mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); + // 5.Èç¹ûº¯Êýµ÷ÓÃûÓзµ»ØÊÓͼÔòʹÓÃĬÈ쵀 + applyDefaultViewName(processedRequest, mv); + // 6\. Ö´ÐÐÀ¹½ØÆ÷£¬ÔËÐÐ HandlerInterceptor#postHandle ·½·¨ + mappedHandler.applyPostHandle(processedRequest, response, mv); + } + catch (...) { + ... + } + // 7\. ´¦Àí·µ»Ø½á¹û£¬ÔÚÕâ¸ö·½·¨Àï»áÖ´ÐÐ HandlerInterceptor.afterCompletion + processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); + } + catch (...) { + ... + } +} + +``` + +Ö´ÐÐÍê `handler` ·½·¨ºó£¬¾Í¿ªÊ¼Ö´ÐÐÀ¹½ØÆ÷µÄ `HandlerInterceptor#postHandle` ·½·¨ÁË£º + +> HandlerExecutionChain#applyPostHandle + +``` +void applyPostHandle(HttpServletRequest request, HttpServletResponse response, + @NullableModelAndView mv) throws Exception { + HandlerInterceptor[] interceptors = getInterceptors(); + if (!ObjectUtils.isEmpty(interceptors)) { + // ±éÀúÖ´ÐÐ postHandle(...) ·½·¨ + for (int i = interceptors.length - 1; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + interceptor.postHandle(request, response, this.handler, mv); + } + } +} + +``` + +Õâ¸öÍ¬Ç°ÃæÖ´ÐÐÀ¹½ØÆ÷·½·¨ÀàËÆ£¬ÕâÀï¾Í²»¹ý¶à·ÖÎöÁË¡£ÐèҪǿµ÷µÄÊÇ£¬`HandlerInterceptor#postHandle` µÄÖ´ÐÐʱ»úÊÇ**ÔÚÖ´ÐÐÍê `handler` ·½·¨ºóÖ®ºó£¬ÔÚÊÓͼ½âÎö֮ǰ**¡£Òò´Ë£¬ÎÒÃÇ¿ÉÒÔÔÚÀ¹½ØÆ÷µÄÕâ¸ö·½·¨Àï´¦ÀíһЩ¶îÍâµÄäÖȾ²ÎÊý¡£ + +ÏÞÓÚÆª·ù£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬request Ö´ÐеĺóÐøÁ÷³Ì£¬ÎÒÃÇÏÂÆªÎÄÕÂÔÙ·ÖÎö¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4741104](https://my.oschina.net/funcy/blog/4741104) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" new file mode 100644 index 0000000..841ee00 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AOP\347\244\272\344\276\213demo\345\217\212@EnableAspectJAutoProxy.md" @@ -0,0 +1,230 @@ +在å‰�é�¢çš„æ–‡ç« ä¸­ï¼Œæˆ‘们æˆ�功的编译了 spring æº�ç �,也构建了第一个 spring 测试 demo,接下æ�¥æˆ‘们就基于[第一个 spring æº�ç �调试 demo](https://my.oschina.net/funcy/blog/4533250 "第一个springæº�ç �调试demo") 中的代ç �,æ�¥å¯¹ spring æº�ç �进行æº�ç �分æž�。 + +### 1\. spring å�¯åЍæµ�程概览 + +在å‰�é�¢ demo çš„ `main()` 方法中,有这么一行: + +``` +ApplicationContext context = + new AnnotationConfigApplicationContext("org.springframework.learn.demo01"); + +``` + +这短短的一行就是 spring 的整个å�¯åЍæµ�程了。上é�¢çš„代ç �中,声明了一个 `ApplicationContext` 类型的对象 `context`,å�³è¾¹ä½¿ç”¨å…¶å­�ç±» `AnnotationConfigApplicationContext` 实例化,并在构造方法中传入了包å�� `org.springframework.learn.demo01`,这个包å��就表明了接下æ�¥è¦�扫æ��哪些包。 + +> 这里我们接触到了 spring 的第一个组件:`ApplicationContext`,关于 `ApplicationContext` 的分æž�,å�¯ä»¥å�‚考我的文章 [spring 组件(一):ApplicationContext](https://my.oschina.net/funcy/blog/4597456 "spring组件(一):ApplicationContext")。 + +进入到 `AnnotationConfigApplicationContext`,代ç �如下: + +> AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + +``` +public AnnotationConfigApplicationContext(String... basePackages) { + // 1\. 调用无å�‚构造函数,会先调用父类GenericApplicationContext的构造函数 + // 2\. 父类的构造函数里é�¢å°±æ˜¯åˆ�始化DefaultListableBeanFactory,并且赋值给beanFactory + // 3\. 本类的构造函数里é�¢ï¼Œåˆ�始化了一个读å�–器:AnnotatedBeanDefinitionReader read, + // 一个扫æ��器ClassPathBeanDefinitionScanner scanner + // 4\. 这个scanner,就是下é�¢ scan(basePackages) 调用的对象 + this(); + + //对传入的包进行扫æ��,扫æ��完æˆ�å�Žï¼Œä¼šå¾—到一个 BeanDefinition 的集å�ˆ + scan(basePackages); + + //å�¯åЍspring,在这里完æˆ�spring容器的åˆ�始化æ“�作, + //包括bean的实例化ã€�属性注入,将beanä¿�存到spring容器中等 + refresh(); +} + +``` + +这个类就三行,相关æ“�作都已在代ç �中注释了,这里ç¨�å¾®å†�总结下,这段代ç �主è¦�å�šäº†ä¸‰ä»¶äº‹ï¼š + +1. 调用无å�‚构造,进行属性åˆ�始化 +2. 进行包扫æ��,得到 BeanDefinition +3. å�¯ç”¨ spring 容器。 + +接ç�€ï¼Œæˆ‘们å†�æ�¥çœ‹çœ‹ spring å�¯åЍæµ�程中,å�šäº†å“ªäº›äº‹ï¼š + +> AbstractApplicationContext#refresh + +``` +public void refresh() throws BeansException, IllegalStateException { + // 使用synchronized是为了é�¿å…�refresh() 还没结æ�Ÿï¼Œå†�次å�‘èµ·å�¯åŠ¨æˆ–è€…é”€æ¯�容器引起的冲çª� + synchronized (this.startupShutdownMonitor) { + // å�šä¸€äº›å‡†å¤‡å·¥ä½œï¼Œè®°å½•容器的å�¯åŠ¨æ—¶é—´ã€�标记“已å�¯åЍâ€�状æ€�ã€�检查环境å�˜é‡�ç­‰ + prepareRefresh(); + + // åˆ�始化BeanFactory容器ã€�注册BeanDefinition, 最终获得了DefaultListableBeanFactory + ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); + + // 还是一些准备工作: + // 1\. 设置了一个类加载器 + // 2\. 设置了bean表达å¼�è§£æž�器 + // 3\. 添加了属性编辑器的支æŒ� + // 4\. 添加了一个å�Žç½®å¤„ç�†å™¨ï¼šApplicationContextAwareProcessor + // 5\. 设置了一些忽略自动装é…�的接å�£ + // 6\. 设置了一些å…�许自动装é…�的接å�£ï¼Œå¹¶ä¸”进行了赋值æ“�作 + // 7\. 在容器中还没有XXçš„bean的时候,帮我们注册beanName为XXçš„singleton bean + prepareBeanFactory(beanFactory); + + try { + // Spring的一个扩展点. 如果有Bean实现了BeanFactoryPostProcessor接å�£ï¼Œ + // 那么在容器åˆ�始化以å�Žï¼ŒSpring 会负责调用里é�¢çš„ postProcessBeanFactory 方法。 + // 具体的å­�ç±»å�¯ä»¥åœ¨è¿™æ­¥çš„æ—¶å€™æ·»åŠ ç‰¹æ®Šçš„ BeanFactoryPostProcessor 的实现类,æ�¥å�šäº›äº‹ + postProcessBeanFactory(beanFactory); + + // 调用BeanFactoryPostProcessorå�„个实现类的postProcessBeanFactory(factory) 方法 + invokeBeanFactoryPostProcessors(beanFactory); + + // 扩展点,注册 BeanPostProcessor 的实现类,注æ„�ä¸�是BeanFactoryPostProcessor + registerBeanPostProcessors(beanFactory); + + // åˆ�始化当å‰� ApplicationContext çš„ MessageSource,用在国际化æ“�作中 + initMessageSource(); + + // 这个方法主è¦�为åˆ�始化当å‰� ApplicationContext 的事件广播器 + initApplicationEventMulticaster(); + + // 这也是spring的一个扩展点 + onRefresh(); + + // Check for listener beans and register them. + // 注册事件监å�¬å™¨ + registerListeners(); + + // åˆ�始化所有的 singleton beans + finishBeanFactoryInitialization(beanFactory); + + // 完æˆ�å�¯åŠ¨ï¼Œ + finishRefresh(); + } + + catch (BeansException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Exception encountered during context initialization - " + + "cancelling refresh attempt: " + ex); + } + + // Destroy already created singletons to avoid dangling resources. + // 销æ¯�å·²ç»�åˆ�始化的的Bean + destroyBeans(); + + // Reset 'active' flag. + // é‡�ç½® 'active' 状æ€� + cancelRefresh(ex); + + // Propagate exception to caller. + throw ex; + } + + finally { + // Reset common introspection caches in Spring's core, since we + // might not ever need metadata for singleton beans anymore... + // 清除缓存 + resetCommonCaches(); + } + } +} + +``` + +这个方法虽然代ç �ä¸�多,但包å�«äº† spring bean 的整个创建过程,æ¯�个方法å�šäº†äº›ä»€ä¹ˆï¼Œåœ¨ä»£ç �中都有注释,这里就ä¸�赘述了。 + +实际上,`refresh()` 涵盖了 spring 整个创建 bean çš„æµ�程,在å�Žé�¢çš„æ–‡ç« ä¸­ï¼Œæˆ‘们也将é‡�点展开这里é�¢çš„æ–¹æ³•æ�¥åˆ†æž�,在现阶段å�ªéœ€è¦�大致了解这些方法å�šäº†ä»€ä¹ˆäº‹å�³å�¯ã€‚ + +整个æµ�程总结如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9307fefa65470e5c36ae6044631b5416aef.png) + +### 2\. spring å�¯åЍ䏭 `beanFactory` çš„å�˜åŒ– + +本文中的æº�ç �解读就到这里了,接下æ�¥æˆ‘们æ�¥çœ‹çœ‹ï¼Œspring å�¯åЍ䏭 `beanFactory` 有些什么å�˜åŒ–。 + +> `beanFactory` 是 spring çš„é‡�è¦�组件之一,直译为 spring bean 工厂,是 spring 生产 bean 与ä¿�å­˜ bean 的地方,关于 `beanFactory` 的详细分æž�,å�¯ä»¥æŸ¥çœ‹ [spring BeanFactory 分æž�](https://my.oschina.net/funcy/blog/4597529 "spring BeanFactory分æž�")。 + +我们将断点打在 `AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)` çš„ `this()` 方法上,然å�Žè¿�行 demo01 çš„ `main()` 方法: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c3c672a675d9b06f03ea29cb31f6ed5d012.png) + +此时的å�˜é‡�中,并没有 `beanFactory`,我们自己添加 `beanFactory` 到调度窗å�£çš„å�˜é‡�列表中: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e9d8ae8fdd3b02b2279376303e3eae4cf2f.png) + +这样就能看到对应的值了: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-44d1ade26f0cb667425b0dd99d82666877f.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œæ­¤æ—¶çš„ `beanFactory` 为 null,表明 `beanFactory` 并未实例化,我们继续è¿�行: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-664780be9dfef73c12a3f163b349e7e54d8.png) + +当è¿�行完 `this()` å�Žï¼Œå�‘现 `beanFactory` å·²ç»�有值了,类型为 `DefaultListableBeanFactory`。但是,在查看 `beanFactory` 对象时,å�‘现 `beanFactory` 的属性太多了,我们应该é‡�点关注啥呢? + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1b617cf7edda29c652a7661d4be3779ec85.png) + +我们这部分主è¦�关注 spring bean 的创建,因此å�ªéœ€è¦�关注 `beanFactory` 的两个属性就å�¯ä»¥äº†ï¼š + +* beanDefinitionMap:存放 beanDefinition çš„ map. +* singletonObjects:存放 spring bean çš„ map,spring bean 创建å�Žéƒ½å­˜æ”¾åœ¨è¿™é‡Œï¼Œä¹Ÿå�³ç›´è§‚上ç�†è§£çš„ `spring 容器`. + +> `BeanDefinition` 是 spring é‡�è¦�组件之一,为‘spring bean çš„æ��述’,简å�•æ�¥è¯´ï¼Œå°±æ˜¯è¯´æ˜Žäº†ä¸€ä¸ª spring bean 应该如何创建。关于 `BeanDefinition` 的详细分æž�,å�¯ä»¥æŸ¥çœ‹ [spring BeanDefinition 分æž�](https://my.oschina.net/funcy/blog/4597536)。 + +我们手动添加å�˜é‡�,如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0c6368478258f8b9f76b47fc1c85b02f13f.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œæ­¤æ—¶çš„ `beanDefinitionMap` 中已ç»�有 4 个对象了,显然是在 `this()` 方法中添加的,关于这å�—我们å�Žé�¢ä¼šåˆ†æž�。 + +接ç�€è¿�行,å�‘现 `beanDefinitionMap` å�ˆå¤šäº†ä¸¤ä¸ªï¼š + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a493061fbd4b4066f9a4d91e91ff61e8c4e.png) + +这里的 `beanObj1` 与 `beanObj2` 就是我们自己的类了,由此å�¯ä»¥åˆ¤æ–­å‡º **spring 就是在 `AnnotationConfigApplicationContext#scan` 方法中对包进行扫æ��çš„**。 + +接下æ�¥ï¼Œä»£ç �执行进入 `AbstractApplicationContext#refresh` 方法,我们一行行è¿�行下去,å�‘现è¿�行到 `prepareBeanFactory(beanFactory);` 时,`singletonObjects` 中第一次出现了对象: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8cebcb82f5a8754fd1bb4bb3eb3c57dda2d.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œè¿™é‡Œå‡ºçŽ°äº† 3 个类,基本都跟系统ã€�环境相关,如 `environment` 是 spring 当å‰�使用的环境 (`profile`),`systemProperties` 当å‰�系统的属性(æ“�作系统ã€�æ“�作系统版本等)。 + +继续往下è¿�行,å�‘现代ç �è¿�行到 `invokeBeanFactoryPostProcessors(beanFactory)` 时,å�ˆå¤šäº† 4 个类: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-baa09e51272baa384418cb2c82b9dfb079b.png) + +关于这几个类的作用,我们å�Žé�¢çš„æ–‡ç« ä¸­ä¼šåˆ†æž�,这里先ä¸�必管。继续往下è¿�行,å�‘现在 `registerBeanPostProcessors(beanFactory);` 中,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +org.springframework.context.annotation.internalAutowiredAnnotationProcessor + +``` + +这里我们ä¾�æ—§ä¸�用管这个对象,接ç�€è¿�行下去,å�¯ä»¥çœ‹åˆ°åœ¨è¿�行 `initMessageSource()` 时,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +messageSource -> {DelegatingMessageSource@1847} "Empty MessageSource" + +``` + +显然,这个对象是用æ�¥å¤„ç�†å›½é™…化问题的,ä¸�过由于 demo01 中并没有用到国际化,所以这里显示 `Empty MessageSource`。继续è¿�行,å�‘现è¿�行到 `initApplicationEventMulticaster();` 时,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +applicationEventMulticaster -> {SimpleApplicationEventMulticaster@1869} + +``` + +显然,这个对象是用æ�¥å¤„ç�† `ApplicationContext` 的广播事件的,我们的 demo 中并没有用到,暂时ä¸�å¿…ç�†ä¼šã€‚继续下去,å�‘现在è¿�行完 `finishBeanFactoryInitialization(beanFactory);`,`singletonObjects` 中终于出现了我们期待的对象: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-68b1ee71e468ef8cf839230c07b64c45563.png) + +由此å�¯è§�,对象就是在该方法中创建的。 + +### 总结 + +1. spring 包的æ��述:`AnnotationConfigApplicationContext#scan` +2. spring bean 的创建:`AbstractApplicationContext#finishBeanFactoryInitialization` + +本文主è¦�是了解 spring å�¯åЍæµ�程,从整体上把æ�¡ spring å�¯åŠ¨è¿‡ç¨‹ä¸­çš„ beanFactory çš„å�˜åŒ–。本文æ„�在了解 spring 的整体å�¯åЍæµ�程,å�Žç»­çš„分æž�中,我们将对这些æµ�程进行展开分æž�。 + +* * * + +_本文原文链接:[https://my.oschina.net/funcy/blog/4597493](https://my.oschina.net/funcy/blog/4597493) ,é™�于作者个人水平,文中难å…�有错误之处,欢迎指正ï¼�原创ä¸�易,商业转载请è�”系作者获得授æ�ƒï¼Œé�žå•†ä¸šè½¬è½½è¯·æ³¨æ˜Žå‡ºå¤„。_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" new file mode 100644 index 0000000..afcdd55 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\212\357\274\211.md" @@ -0,0 +1,669 @@ +在[上一篇文章](https://my.oschina.net/funcy/blog/4678093 "上一篇文章")的分æž�中,我们介ç»�了使用 `@EnableAspectJAutoProxy` å¼€å�¯ç”¨ spring aop 功能,而 `@EnableAspectJAutoProxy` 最终å�‘ spring 中引入了一个类:`AnnotationAwareAspectJAutoProxyCreator`,本文就从该类入手,进 一步分æž� spring aop 功能。 + +首先我们æ�¥çœ‹çœ‹è¿™ä¸ªç±»çš„继承关系: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-53b639dcaacb2480e9098a046407a80a574.png) + +从继承关系上æ�¥çœ‹ï¼Œ`AnnotationAwareAspectJAutoProxyCreator` 是一个 `BeanPostProcessor`,结å�ˆå‰�é�¢çš„分æž�,spring `BeanPostProcessor` 的执行是在 spring bean åˆ�始化å‰�å�Žï¼Œè¿™é‡Œæˆ‘们通过断点调试的方å¼�验è¯�下。 + +### 1\. 调试查看代ç�†å¯¹è±¡çš„产生 + +我们将断点打在 `AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)` 方法上,然å�Žåœ¨ debug 模å¼�下è¿�行: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-449fe5a3456350ae0d9854c1c13f4a59a80.png) + +此时的 `wrappedBean` 的类型还是 `AopBean1`,继续往下,当è¿�行完 `applyBeanPostProcessorsAfterInitialization` å�Žï¼Œ`wrappedBean` 的类型就å�˜äº†æ ·ï¼š + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8b791334bf1873c652a265db9ab1d0e1441.png) + +表现上看还是 `AopBean1`,但å‰�é�¢å‡ºçŽ°äº† `$Poxy19` 字样,且多了一个属性:`JdkDynamicAopProxy`,这表明该动æ€�是 jdk 动æ€�代ç�†ç”Ÿæˆ�的对象。 + +å†�看看 `AopBean2` è¿�行到此处的å�˜åŒ–: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4fc5d03af4298f55d69e5c619cd72df1ff0.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œç±»å��中出现了 `SpringCGLIB` 字样,这表示该对象是由 spring cglib 代ç�†ç”Ÿæˆ�的。 + +从以上调试结果æ�¥çœ‹ï¼Œspring 是在 `AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization` 中完æˆ�对象代ç�†çš„。 + +实际上,`AnnotationAwareAspectJAutoProxyCreator` 中并未实现 `postProcessAfterInitialization` 方法,`AnnotationAwareAspectJAutoProxyCreator` çš„ `postProcessAfterInitialization` 方法继承自 `AbstractAutoProxyCreator#postProcessAfterInitialization`,因此 spring 对象代ç�†çš„实际完æˆ�是在 `AbstractAutoProxyCreator#postProcessAfterInitialization` 中。 + +`AnnotationAwareAspectJAutoProxyCreator` 是 `BeanPostProcessor` çš„å­�类,spring 在 bean çš„åˆ�始化å‰�å�Žä¼šè°ƒç”¨ `BeanPostProcessor#postProcessBeforeInitialization` 与 `BeanPostProcessor#postProcessAfterInitialization` 方法,我们将从æº�ç �上看看 `AnnotationAwareAspectJAutoProxyCreator` 在 bean çš„åˆ�å§‹å‰�å�Žæ˜¯å¦‚何完æˆ� aop æ“�作。 + +### 2. `AbstractAutoProxyCreator#postProcessBeforeInitialization` 方法 + +代ç�†å¯¹è±¡çš„生æˆ�虽然是在 `BeanPostProcessor#postProcessAfterInitialization` 方法,但正所谓 “å�šæˆ�å�šå…¨å¥—â€�,本文我们先分æž� `AbstractAutoProxyCreator#postProcessBeforeInitialization` 方法,看看这个方法å�šäº†äº›ä»€ä¹ˆï¼Œè‡³äºŽ `AbstractAutoProxyCreator#postProcessAfterInitialization` 方法,将留到下一篇文章分æž�。 + +`AnnotationAwareAspectJAutoProxyCreator` çš„ `postProcessBeforeInitialization` 方法继承自 `AbstractAutoProxyCreator`,`AbstractAutoProxyCreator#postProcessBeforeInitialization` 方法如下: + +``` +public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { + Object cacheKey = getCacheKey(beanClass, beanName); + + if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { + if (this.advisedBeans.containsKey(cacheKey)) { + return null; + } + //1\. 加载所有增强 + if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return null; + } + } + + // 2\. 如果有自定义的TargetSource,则è¿�行下é�¢çš„æ–¹æ³•æ�¥åˆ›å»ºä»£ç�† + TargetSource targetSource = getCustomTargetSource(beanClass, beanName); + if (targetSource != null) { + if (StringUtils.hasLength(beanName)) { + this.targetSourcedBeans.add(beanName); + } + Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); + Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); + this.proxyTypes.put(cacheKey, proxy.getClass()); + return proxy; + } + + return null; +} + +``` + +这个方法主è¦�功能就是加载项目中的增强方法,处ç�†ä»£ç �如下: + +``` +if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { + ... +} + +``` + +这段代ç �包å�«ä¸¤ä¸ªæ–¹æ³•:`isInfrastructureClass(beanClass)` 与 `shouldSkip(beanClass, beanName)`,先æ�¥çœ‹ `isInfrastructureClass(beanClass)` + +#### 2.1 `isInfrastructureClass(beanClass)` 方法 + +> AnnotationAwareAspectJAutoProxyCreator#isInfrastructureClass + +``` +@Override +protected boolean isInfrastructureClass(Class beanClass) { + // 判断当å‰�类是å�¦ä¸º Advice/Pointcut/Advisor/AopInfrastructureBean çš„å­�ç±» + return (super.isInfrastructureClass(beanClass) || + // 判断当å‰�beanClass是å�¦ä¸ºåˆ‡é�¢ï¼šåŒ…å�«æœ‰@Aspect注解, 且ä¸�ç”±ajc编译 + (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass))); +} + +``` + +以上代ç �就两行,功能注释得很清楚了,简å�•æ�¥è¯´å°±æ˜¯åˆ¤æ–­æ˜¯å�¦ä¸º aop 自身相关的类,如果是 aop 自身相关的类,就ä¸�进行切é�¢æ“�作了。 + +#### 2.2 `shouldSkip(beanClass, beanName)` 方法 + +接ç�€ï¼Œæˆ‘们æ�¥çœ‹ `shouldSkip(beanClass, beanName)` 方法: + +> AspectJAwareAdvisorAutoProxyCreator#shouldSkip + +``` +@Override +protected boolean shouldSkip(Class beanClass, String beanName) { + //查找所有标识了@Aspect注解的类,这个方法很é‡�è¦� ,下é�¢ä¼šç»§ç»­åˆ†æž� + List candidateAdvisors = findCandidateAdvisors(); + for (Advisor advisor : candidateAdvisors) { + // 如果当å‰� advisor 为 AspectJPointcutAdvisor 的实例且 AspectName 为 beanName,返回true + if (advisor instanceof AspectJPointcutAdvisor && + ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { + return true; + } + } + // 调用父类的方法,判断当å‰� bean 是å�¦ä¸º OriginalInstance + return super.shouldSkip(beanClass, beanName); +} + +``` + +这个方法会先查找所有标识了 `@Aspect` 注解的类,然å�Žå�šä¸€äº›åˆ¤æ–­ï¼Œå…¶ä¸­ `findCandidateAdvisors` 很关键,我们æ�¥çœ‹çœ‹è¿™ä¸ªæ–¹æ³•: + +> AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors + +``` +@Override +protected List findCandidateAdvisors() { + // 调用父类的方法,查找当å‰�beanFactory中的Advisor bean + List advisors = super.findCandidateAdvisors(); + if (this.aspectJAdvisorsBuilder != null) { + // 将包å�« @Aspect 注解的类构建为 Advisor,这å�¥æ˜¯å…³é”® + advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); + } + return advisors; +} + +``` + +首先æ�¥çœ‹ä¸‹ `super.findCandidateAdvisors()` 方法,也就是 `AbstractAdvisorAutoProxyCreator#findCandidateAdvisors`: + +``` +protected List findCandidateAdvisors() { + Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); + return this.advisorRetrievalHelper.findAdvisorBeans(); +} + +``` + +这个调用了 `BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans`,继续下去: + +``` +public List findAdvisorBeans() { + String[] advisorNames = this.cachedAdvisorBeanNames; + if (advisorNames == null) { + // 1\. 查找当å‰�beanFactory中所有 Advisor çš„ bean class + advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + this.beanFactory, Advisor.class, true, false); + this.cachedAdvisorBeanNames = advisorNames; + } + ... + + List advisors = new ArrayList<>(); + for (String name : advisorNames) { + ... + // 2\. 从spring中获å�– bean class 对应的 bean,将其放入advisors中 + advisors.add(this.beanFactory.getBean(name, Advisor.class)); + ... + + } + return advisors; +} + +``` + +以上代ç �å�šäº†ä¸¤ä»¶äº‹ï¼š + +1. 查找当å‰� beanFactory 中所有 Advisor çš„ bean class,Advisor å�¯ä»¥æ˜¯ç”¨æˆ·å®žçް Advisor 相关接å�£ï¼Œä¹Ÿå�¯ä»¥æ˜¯ xml 指定的 +2. 从 spring 中获å�– bean class 对应的 bean,将其放入 advisors 中 + +#### 2.3 `aspectJAdvisorsBuilder.buildAspectJAdvisors()` 方法 + +接ç�€æˆ‘们å†�回过头æ�¥çœ‹çœ‹ `aspectJAdvisorsBuilder.buildAspectJAdvisors())` 方法 : + +> BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors + +``` +public List buildAspectJAdvisors() { + List aspectNames = this.aspectBeanNames; + + // å�ªæœ‰åœ¨ç¬¬ä¸€æ¬¡è¿�行时,æ‰�会æˆ�ç«‹ + if (aspectNames == null) { + synchronized (this) { + aspectNames = this.aspectBeanNames; + if (aspectNames == null) { + List advisors = new ArrayList<>(); + aspectNames = new ArrayList<>(); + //1\. 获å�–所有Beanå��ç§° + String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + this.beanFactory, Object.class, true, false); + for (String beanName : beanNames) { + ... + //2\. 判断Beançš„Class上是å�¦æ ‡è¯†@Aspect注解 + if (this.advisorFactory.isAspect(beanType)) { + aspectNames.add(beanName); + AspectMetadata amd = new AspectMetadata(beanType, beanName); + if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { + // 创建factory对象,这个对象里包å�«äº†beanFactory与@Aspectçš„beanName + MetadataAwareAspectInstanceFactory factory = + new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); + // 3\. è§£æž�所有的增强方法, 这个方法是é‡�点的é‡�点 + List classAdvisors = this.advisorFactory.getAdvisors(factory); + if (this.beanFactory.isSingleton(beanName)) { + //将解æž�çš„Beanå��ç§°å�Šç±»ä¸Šçš„增强缓存起æ�¥,æ¯�个Beanå�ªè§£æž�一次 + this.advisorsCache.put(beanName, classAdvisors); + } + ... + } + else { + ... + } + } + } + // 对属性赋值,之å�Žå°±ä¸�会å†�è¿�行了 + this.aspectBeanNames = aspectNames; + return advisors; + } + } + } + + if (aspectNames.isEmpty()) { + return Collections.emptyList(); + } + List advisors = new ArrayList<>(); + for (String aspectName : aspectNames) { + //从缓存中获å�–当å‰�Bean的切é�¢å®žä¾‹ï¼Œå¦‚æžœä¸�为空,则指明当å‰�Beançš„Class标识了@Aspect,且有切é�¢æ–¹æ³• + List cachedAdvisors = this.advisorsCache.get(aspectName); + if (cachedAdvisors != null) { + advisors.addAll(cachedAdvisors); + } + else { + MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); + advisors.addAll(this.advisorFactory.getAdvisors(factory)); + } + } + return advisors; +} + +``` + +方法虽然有点长,ä¸�过å�ªå�šäº† 3 件事: + +1. 获å�–所有 beanName:`BeanFactoryUtils.beanNamesForTypeIncludingAncestors` +2. 找出所有标记 Aspect 注解的类:`advisorFactory.isAspect(beanType)` +3. 对标记 Aspect 的类æ��å�–增强器:`this.advisorFactory.getAdvisors(factory)` + +这里我们é‡�点分æž�第 3 点,看看 `Advisors` 是如何构建的。 + +> ReflectiveAspectJAdvisorFactory#getAdvisors + +``` +public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { + //获å�–Aspectç±»ã€�ç±»å��ç§°ã€�并校验 + // aspectInstanceFactory 里包å�«beanFactory与 @Aspect çš„ beanName + Class aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); + String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); + //校验类的å�ˆæ³•性相关 + validate(aspectClass); + + MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = + new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); + List advisors = new ArrayList<>(); + // 获å�–这个类除去 @Pointcut 外的所有方法,看下é�¢çš„分æž� + for (Method method : getAdvisorMethods(aspectClass)) { + // 生æˆ�增强实例,这个 方法是é‡�点,看å�Žé�¢çš„分æž� + // 注æ„� advisors.size(),这个指定了advisor的顺åº�,由于获å�–之å�Žä¼šä¸ªæ·»åŠ  + // æ“�作,因此这个值是递增且是ä¸�é‡�å¤�çš„ + Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, + advisors.size(), aspectName); + if (advisor != null) { + // 添加 advisors 列表 + advisors.add(advisor); + } + } + // 如果需è¦�增强且é…�置了延迟增强,则在第一个ä½�置添加å�Œæ­¥å®žä¾‹åŒ–增强方法 + if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory + .getAspectMetadata().isLazilyInstantiated()) { + Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor( + lazySingletonAspectInstanceFactory); + advisors.add(0, instantiationAdvisor); + } + // 获å�–属性中é…�ç½®DeclareParents注解的增强 + for (Field field : aspectClass.getDeclaredFields()) { + Advisor advisor = getDeclareParentsAdvisor(field); + if (advisor != null) { + advisors.add(advisor); + } + } + return advisors; +} + +/** + * 获å�–指定类除@Pointcut外的所有方法 + */ +private List getAdvisorMethods(Class aspectClass) { + final List methods = new ArrayList<>(); + // 递归æ“�作 :排除@Pointcut标识的方法,获å�–当å‰�类的的方法ã€�æ�¥è‡ªæŽ¥å�£çš„默认方法,å†�对父类进行å�Œæ ·çš„æ“�作 + // 最终结果:除@Pointcut标识之外的所有方法,得到的方法集å�ˆåŒ…括: + // 1\. 继承自父类的方法, + // 2\. æ�¥è‡ªè‡ªæŽ¥å�£çš„默认的方法 + // 3\. ä¸�包å�«ç»§æ‰¿è‡ªObject的方法 + ReflectionUtils.doWithMethods(aspectClass, method -> { + if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { + methods.add(method); + } + }, ReflectionUtils.USER_DECLARED_METHODS); + //对得到的所有方法排åº�, + //如果方法标识了切é�¢æ³¨è§£ï¼Œåˆ™æŒ‰@Around, @Before, @After, @AfterReturning, @AfterThrowing的顺åº�排åº� + //如果没有标识这些注解,则按方法å��称的字符串排åº�, + //有注解的方法排在无注解的方法之å‰� + //最å�Žçš„æŽ’åº�应该是这样的Around, Before, After, AfterReturning, AfterThrowing + methods.sort(METHOD_COMPARATOR); + return methods; +} + +``` + +以上方法主è¦�å�šäº†ä¸¤ä»¶äº‹ï¼š + +1. 获å�–当å‰�类除 `@Pointcut` 外的所有增强方法,包括继承自父类的方法,继承自 Object 的方法,以å�Šæ�¥è‡ªæŽ¥å�£çš„默认方法 +2. é��历得到的方法,从符å�ˆæ�¡ä»¶çš„æ–¹æ³•得到 Advisor 实例,ä¿�存到集å�ˆä¸­ + +关于第一步,上é�¢çš„代ç �å·²ç»�分æž�了,主è¦�是根æ�®å��å°„æ�¥è¿›è¡Œçš„,就ä¸�å†�深入了,这里我们æ�¥çœ‹ç¬¬äºŒæ­¥ï¼Œè¯¥æ“�作是在 `ReflectiveAspectJAdvisorFactory#getAdvisor` 方法中: + +``` +@Override +@Nullable +/** + * declarationOrderInAspect:指定了Advisor的顺åº�,æ�¥æº�于 AdvisorMethods 的排åº�,上é�¢å·²åˆ†æž� + */ +public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory + aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { + //å†�次校验类的å�ˆæ³•性 + validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); + //切点表达å¼�的包装类里é�¢åŒ…å�«è¿™äº›ä¸œè¥¿ï¼š@Around("testAop()") 里的 testAop() + AspectJExpressionPointcut expressionPointcut = getPointcut( + candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); + if (expressionPointcut == null) { + return null; + } + //æ ¹æ�®æ–¹æ³•ã€�切点ã€�AOP实例工厂ã€�ç±»å��ã€�åº�å�·ç”Ÿæˆ�切é�¢å®žä¾‹ï¼Œä¸‹é�¢æˆ‘们先æ�¥çœ‹çœ‹æ˜¯å¦‚何实例化的 + return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, + this, aspectInstanceFactory, declarationOrderInAspect, aspectName); +} + +/** + * 处ç�†åˆ‡ç‚¹è¡¨è¾¾å¼� + */ +@Nullable +private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, + Class candidateAspectClass) { + // 查询方法上的切é�¢æ³¨è§£ï¼Œæ ¹æ�®æ³¨è§£ç”Ÿæˆ�相应类型的AspectJAnnotation, + // 在调用AspectJAnnotation的构造函数的å�Œæ—¶ï¼Œæ ¹æ�®æ³¨è§£value或pointcut属性得到切点表达å¼�, + // 有argNames则设置å�‚æ•°å��ç§° + AspectJAnnotation aspectJAnnotation = + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); + // 过滤那些ä¸�å�«@Before, @Around, @After, @AfterReturning, @AfterThrowing注解的方法 + if (aspectJAnnotation == null) { + return null; + } + + //生æˆ�带表达å¼�的切é�¢åˆ‡å…¥ç‚¹ï¼Œè®¾ç½®å…¶åˆ‡å…¥ç‚¹è¡¨è¾¾å¼� + AspectJExpressionPointcut ajexp = + new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); + ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); + if (this.beanFactory != null) { + ajexp.setBeanFactory(this.beanFactory); + } + return ajexp; +} + +``` + +`Advisor` 实例化过程如下: + +``` +public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, + Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, + int declarationOrder, String aspectName) { + // 处ç�†å±žæ€§è®¾ç½® + this.declaredPointcut = declaredPointcut; + this.declaringClass = aspectJAdviceMethod.getDeclaringClass(); + this.methodName = aspectJAdviceMethod.getName(); + this.parameterTypes = aspectJAdviceMethod.getParameterTypes(); + this.aspectJAdviceMethod = aspectJAdviceMethod; + this.aspectJAdvisorFactory = aspectJAdvisorFactory; + this.aspectInstanceFactory = aspectInstanceFactory; + this.declarationOrder = declarationOrder; + this.aspectName = aspectName; + + if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { + ... + } + else { + this.pointcut = this.declaredPointcut; + this.lazy = false; + //åˆ�始化对应的增强器,这里是é‡�点 + this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); + } +} + +/** + * 实例化过程 + */ +private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { + // getAdvice: 处ç�† Advice 实例化æ“�作 + Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, + this.aspectInstanceFactory, this.declarationOrder, this.aspectName); + return (advice != null ? advice : EMPTY_ADVICE); +} + +``` + +这段代ç �清晰明了,主è¦�是实例化 `Advisor`,所谓的实例化,就是往 `Advisor` 对象里设置了一些属性。这里 `instantiatedAdvice` 属性需è¦�特别说明一下,这个属性å°�装了切é�¢æ–¹æ³•,也就是增强的内容。 + +* 切é�¢æ–¹æ³• (切é�¢å…·ä½“的执行代ç �) å°�装为 `Advice`ï¼› +* `Advice` 是 `Advisor` 的一个属性。 + +#### 2.4 实例化 `Advice` + +接下æ�¥çœ‹çœ‹ `Advice` çš„æµ�程: + +> ReflectiveAspectJAdvisorFactory#getAdvice + +``` +@Override +@Nullable +public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, + int declarationOrder, String aspectName) { + + Class candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); + //å�ˆæ˜¯ä¸€æ¬¡æ ¡éªŒ + validate(candidateAspectClass); + + AspectJAnnotation aspectJAnnotation = + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); + if (aspectJAnnotation == null) { + return null; + } + + if (!isAspect(candidateAspectClass)) { + throw new AopConfigException(...); + } + + AbstractAspectJAdvice springAdvice; + // æ ¹æ�®æ³¨è§£ç±»åž‹ç”Ÿæˆ�ä¸�å�Œçš„通知实例, + // 对应的注解就是 @Before, @Around, @After, @AfterReturning, @AfterThrowing + switch (aspectJAnnotation.getAnnotationType()) { + case AtPointcut: + return null; + case AtAround: + springAdvice = new AspectJAroundAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + break; + case AtBefore: + springAdvice = new AspectJMethodBeforeAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + break; + case AtAfter: + springAdvice = new AspectJAfterAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + break; + case AtAfterReturning: + springAdvice = new AspectJAfterReturningAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); + if (StringUtils.hasText(afterReturningAnnotation.returning())) { + springAdvice.setReturningName(afterReturningAnnotation.returning()); + } + break; + case AtAfterThrowing: + springAdvice = new AspectJAfterThrowingAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); + if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { + springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); + } + break; + default: + throw new UnsupportedOperationException(...); + } + + //设置通知方法所属的类 + springAdvice.setAspectName(aspectName); + //设置通知的åº�å�·,å�Œä¸€ä¸ªç±»ä¸­æœ‰å¤šä¸ªåˆ‡é�¢æ³¨è§£æ ‡è¯†çš„æ–¹æ³•æ—¶,按上方说的排åº�规则æ�¥æŽ’åº�, + //å…¶åº�å�·å°±æ˜¯æ­¤æ–¹æ³•在列表中的åº�å�·ï¼Œç¬¬ä¸€ä¸ªå°±æ˜¯0 + springAdvice.setDeclarationOrder(declarationOrder); + //获å�–通知方法的所有å�‚æ•° + String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); + //将通知方法上的å�‚数设置到通知中 + if (argNames != null) { + springAdvice.setArgumentNamesFromStringArray(argNames); + } + //计算å�‚数绑定工作,此方法详解请接ç�€å¾€ä¸‹çœ‹ + springAdvice.calculateArgumentBindings(); + return springAdvice; +} + +``` + +我们知é�“,切é�¢æ³¨è§£æ ‡è¯†çš„æ–¹æ³•第一个å�‚æ•°è¦�求是 `JoinPoint`,如果是 `@Around` 注解,则第一个å�‚æ•°å�¯ä»¥æ˜¯ `ProceedingJoinPoint`,计算å�‚数绑定就是æ�¥éªŒè¯�å�‚数类型的。 + +对于ä¸�å�Œçš„通知,spring 会å°�装æˆ�ä¸�å�Œçš„ `advice`,这里先æ�¥çœ‹çœ‹ `advice` 的继承结构: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-671bd92a16642a059ff722ec42c8ea7ef28.png) + +关于 `advice`,å�Žé�¢åœ¨åˆ†æž�切é�¢çš„æ‰§è¡Œæ—¶ä¼šè¯¦ç»†åˆ†æž�,这里å�ªéœ€çŸ¥é�“ “ä¸�å�Œçš„切é�¢ç±»åž‹ï¼Œæœ€ç»ˆä¼šå°�装为ä¸�å�Œçš„ adviceâ€� å�³å�¯ã€‚ + +å†�æ�¥çœ‹çœ‹ spring 计算å�‚数绑定的æµ�程: + +> AbstractAspectJAdvice + +``` +/** + * 处ç�†å�‚数绑定 + */ +public final synchronized void calculateArgumentBindings() { + if (this.argumentsIntrospected || this.parameterTypes.length == 0) { + return; + } + + int numUnboundArgs = this.parameterTypes.length; + Class[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes(); + //切é�¢æ³¨è§£æ ‡è¯†çš„æ–¹æ³•第一个å�‚æ•°è¦�求是JoinPoint,或StaticPart,若是@Around注解则也å�¯ä»¥æ˜¯ProceedingJoinPoint + if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) || + maybeBindJoinPointStaticPart(parameterTypes[0])) { + numUnboundArgs--; + } + + if (numUnboundArgs > 0) { + //绑定属性其他属性 + bindArgumentsByName(numUnboundArgs); + } + + this.argumentsIntrospected = true; +} + +``` + +以上代ç �验è¯�了切é�¢æ³¨è§£æ ‡è¯†çš„æ–¹æ³•第一个å�‚数的类型,此外,如果在方法中定义了多个方法,spring 还会进一步进行å�‚数绑定: + +> AbstractAspectJAdvice + +``` +/** + * 绑定属性其他属性 + */ +private void bindArgumentsByName(int numArgumentsExpectingToBind) { + if (this.argumentNames == null) { + //获å�–方法å�‚æ•°çš„å��ç§° + this.argumentNames = createParameterNameDiscoverer() + .getParameterNames(this.aspectJAdviceMethod); + } + if (this.argumentNames != null) { + // 继续绑定 + bindExplicitArguments(numArgumentsExpectingToBind); + } + else { + throw new IllegalStateException(...); + } +} + +/** + * 针对 @AfterReturning 与 @AfterThrowing 注解,进一步处ç�†å…¶å�‚æ•° + */ +private void bindExplicitArguments(int numArgumentsLeftToBind) { + Assert.state(this.argumentNames != null, "No argument names available"); + //此属性用æ�¥å­˜å‚¨æ–¹æ³•未绑定的å�‚æ•°å��称,å�Šå�‚æ•°çš„åº�å�· + this.argumentBindings = new HashMap<>(); + + int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterCount(); + if (this.argumentNames.length != numExpectedArgumentNames) { + throw new IllegalStateException(...); + } + + // argumentIndexOffset代表第一个未绑定å�‚数的顺åº� + int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind; + for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) { + //存储未绑定的å�‚æ•°å��ç§°å�Šå…¶é¡ºåº�的映射关系 + this.argumentBindings.put(this.argumentNames[i], i); + } + + // 如果是@AfterReturning注解的returningName 有值,验è¯�,解æž�,å�Œæ—¶å¾—到定义返回值的类型 + if (this.returningName != null) { + if (!this.argumentBindings.containsKey(this.returningName)) { + throw new IllegalStateException(...); + } + else { + Integer index = this.argumentBindings.get(this.returningName); + this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index]; + this.discoveredReturningGenericType = this.aspectJAdviceMethod + .getGenericParameterTypes()[index]; + } + } + // 如果是@AfterThrowing注解的throwingName 有值,验è¯�,解æž�,å�Œæ—¶å¾—到抛出异常的类型 + if (this.throwingName != null) { + if (!this.argumentBindings.containsKey(this.throwingName)) { + throw new IllegalStateException(...); + } + else { + Integer index = this.argumentBindings.get(this.throwingName); + this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index]; + } + } + + configurePointcutParameters(this.argumentNames, argumentIndexOffset); +} + +/** + * 这一步仅是将å‰�é�¢æœªå¤„ç�†è¿‡çš„å�‚æ•°å�šä¸€ä¸ªä¿�存,记录到 pontcut 中,待å�Žç»­å†�å�šå¤„ç�† + */ +private void configurePointcutParameters(String[] argumentNames, int argumentIndexOffset) { + int numParametersToRemove = argumentIndexOffset; + if (this.returningName != null) { + numParametersToRemove++; + } + if (this.throwingName != null) { + numParametersToRemove++; + } + String[] pointcutParameterNames = new String[argumentNames.length - numParametersToRemove]; + Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length]; + Class[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes(); + + int index = 0; + for (int i = 0; i < argumentNames.length; i++) { + if (i < argumentIndexOffset) { + continue; + } + if (argumentNames[i].equals(this.returningName) || + argumentNames[i].equals(this.throwingName)) { + continue; + } + pointcutParameterNames[index] = argumentNames[i]; + pointcutParameterTypes[index] = methodParameterTypes[i]; + index++; + } + //剩余的未绑定的å�‚数会赋值给AspectJExpressionPointcut(表达å¼�å½¢å¼�的切入点)的属性,以备å�Žç»­ä½¿ç”¨ + this.pointcut.setParameterNames(pointcutParameterNames); + this.pointcut.setParameterTypes(pointcutParameterTypes); +} + +``` + +### 3\. 总结 + +本文主è¦�是分æž�了 `AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInitialization` 方法,也就是 `AbstractAutoProxyCreator#postProcessBeforeInitialization` 方法,在该方法的è¿�行过程中,主è¦�是将 `@Aspect` ç±»å°�装æˆ�一个个 `Advisor`,å°�装步骤如下: + +1. 找到项目中标注了 `@Aspect` 的类; +2. é��历上一步得到的类,通过å��射得到该类的方法,包括父接å�£çš„默认方法ã€�父类的方法但ä¸�包括 Object 类的方法; +3. 从上述方法中,找到标注了 `@Around`, `@Before`, `@After`, `@AfterReturning`, `@AfterThrowing` 等的方法,将其å°�装为 `Advisor` 对象。 + +关于 `Advisor`,其中有个é‡�è¦�属性为 `Advice`,这个属性就是切é�¢æ–¹æ³•的具体内容。关于 `Advice`,在å�Žé�¢åˆ†æž�切é�¢çš„æ‰§è¡Œæ—¶ä¼šè¯¦ç»†åˆ†æž�。 + +* * * + +_本文原文链接:[https://my.oschina.net/funcy/blog/4678817](https://my.oschina.net/funcy/blog/4678817) ,é™�于作者个人水平,文中难å…�有错误之处,欢迎指正ï¼�原创ä¸�易,商业转载请è�”系作者获得授æ�ƒï¼Œé�žå•†ä¸šè½¬è½½è¯·æ³¨æ˜Žå‡ºå¤„。_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" new file mode 100644 index 0000000..c21a988 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/AnnotationAwareAspectJAutoProxyCreator\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" @@ -0,0 +1,628 @@ +[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4678817 "ÉÏһƪÎÄÕÂ")Ö÷Òª·ÖÎöÁË `AbstractAutoProxyCreator#postProcessAfterInitialization` ·½·¨£¬±¾ÎÄÎÒÃÇÀ´·ÖÎö `AbstractAutoProxyCreator#postProcessAfterInitialization` ·½·¨¡£ + +ÎÒÃÇÏÈÀ´¿´¿´Õâ¸ö·½·¨µÄµ÷ÓÃÁ´£º + +``` +|-AnnotationConfigApplicationContext + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-ConfigurableListableBeanFactory#preInstantiateSingletons + |-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory) + |-ObjectFactory#getObject + |-AbstractBeanFactory#createBean + |-AbstractAutowireCapableBeanFactory#doCreateBean + |-AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition) + |-AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization + |-AbstractAutoProxyCreator#postProcessAfterInitialization + +``` + +ʵ¼ÊÉÏÕâ¸ö·½·¨µÄµ÷ÓÃÁ´¾ÍÊÇ spring bean µÄ´´½¨¹ý³Ì£¬ÎÒÃǽøÈë `AbstractAutoProxyCreator#postProcessAfterInitialization`£º + +``` +@Override +public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { + if (bean != null) { + Object cacheKey = getCacheKey(bean.getClass(), beanName); + if (this.earlyProxyReferences.remove(cacheKey) != bean) { + // ÉîÈëwrapIfNecessary()·½·¨ + return wrapIfNecessary(bean, beanName, cacheKey); + } + } + return bean; +} + +``` + +¼ÌÐø½øÈë `AbstractAutoProxyCreator#wrapIfNecessary`£º + +``` +protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { + //Èç¹ûÒѾ­´¦Àí¹ý + if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { + return bean; + } + //Èç¹ûµ±Ç°ÀàÊÇÔöÇ¿À࣬·µ»Ø + if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { + return bean; + } + + // ÖØÒª´úÂëÒ»£ºÅжϵ±Ç°ÀàÊÇ·ñΪÇÐÃæÀ࣬¸Ã´úÂëÔÚÉÏһƪÎÄÕÂÒѾ­·ÖÎöÁË£¬¾Í²»¶à˵ÁË + if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return bean; + } + + // ÖØÒª´úÂë¶þ£ºÐ£Ñé´ËÀàÊÇ·ñÓ¦¸Ã±»´úÀí£¬»ñÈ¡Õâ¸öÀàµÄÔöÇ¿ + Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); + //Èç¹û»ñÈ¡µ½ÁËÔöÇ¿ÔòÐèÒªÕë¶ÔÔöÇ¿´´½¨´úÀí + if (specificInterceptors != DO_NOT_PROXY) { + this.advisedBeans.put(cacheKey, Boolean.TRUE); + // ÖØÒª´úÂëÈý£º´´½¨´úÀí + Object proxy = createProxy( + bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); + this.proxyTypes.put(cacheKey, proxy.getClass()); + return proxy; + } + + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return bean; +} + +``` + +Õâ¸ö·½·¨ ¿´×ÅÓе㳤£¬µ«´ó¶à´úÂë¶¼ÊÇÔÚ×öÅжϣ¬Óë aop ¹¦ÄܹØÏµ²»´ó¡£ÕæÕýÓйØÏµµÄ´úÂëÖ»ÓÐÈýÐУº + +``` +// ÖØÒª´úÂëÒ»£º +// 1\. isInfrastructureClass£ºÅжϵ±Ç°ÊÇ·ñΪaopÏà¹ØÀ࣬ +// ÈçAdvice/Pointcut/AdvisorµÈµÄ×ÓÀ࣬ÊÇ·ñ°üº¬ @AspectJµÄ×¢½â +// 2\. shouldSkip£º²éÕÒËùÓÐÇÐÃæÀ࣬ÅжÏÊÇ·ñ±»Åųý +if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { + ... +} + +// ÖØÒª´úÂë¶þ£ºÐ£Ñé´ËÀàÊÇ·ñÓ¦¸Ã±»´úÀí£¬»ñÈ¡Õâ¸öÀàµÄÔöÇ¿ +Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); + +// ÖØÒª´úÂëÈý£º´´½¨´úÀí +Object proxy = createProxy( + bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); + +``` + +¶ÔÓÚ`ÖØÒª´úÂëÒ»`£¬ÉÏһƪÎÄÕÂÒѾ­·ÖÎö¹ý£¬±¾ÎÄÎÒÃÇÖ÷Òª·ÖÎö`ÖØÒª´úÂë¶þ`Óë`ÖØÒª´úÂëÈý`¡£ + +### 1\. »ñÈ¡ÀàµÄÔöÇ¿ + +> AbstractAdvisorAutoProxyCreator + +``` +@Override +@Nullable +protected Object[] getAdvicesAndAdvisorsForBean( + Class beanClass, String beanName, @Nullable TargetSource targetSource) { + // ²éÕÒ·ûºÏÌõ¼þµÄÔöÇ¿£¬¼ÌÐøÍùÏ¿´ + List advisors = findEligibleAdvisors(beanClass, beanName); + if (advisors.isEmpty()) { + return DO_NOT_PROXY; + } + return advisors.toArray(); +} + +/** + * ²éÕÒ·ûºÏÌõ¼þµÄÔöÇ¿ + */ +protected List findEligibleAdvisors(Class beanClass, String beanName) { + //»ñÈ¡ÈÝÆ÷ÖеÄËùÓÐÔöÇ¿£¬ÔÚÉÏһƪÎÄÕÂÖÐÒѾ­·ÖÎö¹ýÁË + List candidateAdvisors = findCandidateAdvisors(); + //ÑéÖ¤beanClassÊÇ·ñ¸Ã±»´úÀí£¬Èç¹ûÓ¦¸Ã£¬Ôò·µ»ØÊÊÓÃÓÚÕâ¸öbeanµÄÔöÇ¿ + List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); + extendAdvisors(eligibleAdvisors); + if (!eligibleAdvisors.isEmpty()) { + eligibleAdvisors = sortAdvisors(eligibleAdvisors); + } + return eligibleAdvisors; +} + +/** + * ÑéÖ¤beanClassÊÇ·ñ¸Ã±»´úÀí + */ +protected List findAdvisorsThatCanApply( + List candidateAdvisors, Class beanClass, String beanName) { + ProxyCreationContext.setCurrentProxiedBeanName(beanName); + try { + // ÑéÖ¤beanClassÊÇ·ñ¸Ã±»´úÀí + return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); + } + finally { + ProxyCreationContext.setCurrentProxiedBeanName(null); + } +} + +``` + +spring µÄ·½·¨µ÷ÓñȽÏÉһ·׷×Ù£¬×îÖÕµ½ÁË `AopUtils.findAdvisorsThatCanApply` ·½·¨£¬¼ÌÐøÍùÏ¿´£º + +> AopUtils + +``` +public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) { + if (candidateAdvisors.isEmpty()) { + return candidateAdvisors; + } + List eligibleAdvisors = new ArrayList<>(); + // ±éÀú candidateAdvisors£¬ÅжÏÊÇ·ñÂú×ã´úÀíÌõ¼þ + for (Advisor candidate : candidateAdvisors) { + //´¦ÀíÔöÇ¿£¬Öص㣬ÔÙÍùÏ¿´ + if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { + eligibleAdvisors.add(candidate); + } + } + boolean hasIntroductions = !eligibleAdvisors.isEmpty(); + for (Advisor candidate : candidateAdvisors) { + if (candidate instanceof IntroductionAdvisor) { + // already processed + continue; + } + //¶ÔÆÕͨbeanµÄ´¦Àí + if (canApply(candidate, clazz, hasIntroductions)) { + eligibleAdvisors.add(candidate); + } + } + return eligibleAdvisors; +} + +/** + * ÅжÏÊÇ·ñÐèÒªÔöÇ¿ + */ +public static boolean canApply(Advisor advisor, Class targetClass) { + // µ÷ÓÃÏÂÒ»¸ö·½·¨ + return canApply(advisor, targetClass, false); +} + +/** + * ÅжÏÊÇ·ñÐèÒªÔöÇ¿ + */ +public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) { + //Èç¹û´æÔÚÅųýµÄÅäÖà + if (advisor instanceof IntroductionAdvisor) { + return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); + } + else if (advisor instanceof PointcutAdvisor) { + PointcutAdvisor pca = (PointcutAdvisor) advisor; + //½øÈë¸Ã·½·¨¼ÌÐø + return canApply(pca.getPointcut(), targetClass, hasIntroductions); + } + else { + // It doesn't have a pointcut so we assume it applies. + return true; + } +} + +/** + * ÅжÏÊÇ·ñÐèÒªÔöÇ¿ + */ +public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) { + Assert.notNull(pc, "Pointcut must not be null"); + //ÇеãÉÏÊÇ·ñ´æÔÚÅųýÀàµÄÅäÖà + if (!pc.getClassFilter().matches(targetClass)) { + return false; + } + //Ñé֤ע½âµÄ×÷ÓÃÓòÊÇ·ñ¿ÉÒÔ×÷ÓÃÓÚ·½·¨ÉÏ + MethodMatcher methodMatcher = pc.getMethodMatcher(); + if (methodMatcher == MethodMatcher.TRUE) { + // No need to iterate the methods if we're matching any method anyway... + return true; + } + + IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; + if (methodMatcher instanceof IntroductionAwareMethodMatcher) { + introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; + } + + // classes°üº¬targetClass¡¢³ýObjectµÄËùÓи¸Àà¡¢ËùÓÐ½Ó¿Ú + Set> classes = new LinkedHashSet<>(); + if (!Proxy.isProxyClass(targetClass)) { + classes.add(ClassUtils.getUserClass(targetClass)); + } + classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); + + // Ñ­»·ÅжϷ½·¨ÊÇ·ñÐèÒª´úÀí£¬ + // ÕâÀï¿ÉÒÔ¿´³ö£¬ + // 1\. Ö»ÒªÒ»¸ö·½·¨Âú×ã´úÀíÒªÇó£¬ÄÇôÕû¸öÀà¾Í»á±»´úÀí + // 2\. Èç¹û¸¸ÀàÓз½·¨ÐèÒª±»´úÀí£¬ÄÇô×ÓÀàÒ²»á±»´úÀí + for (Class clazz : classes) { + // »ñÈ¡ clazz ¶¨ÒåµÄ·½·¨ + // °üÀ¨µ±Ç°ÀàµÄ·½·¨¡¢³ýObjectÍâµÄËùÓи¸Àà·½·¨¡¢½Ó¿ÚµÄĬÈÏ·½·¨ + Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); + for (Method method : methods) { + //»ñÈ¡ÀàËùʵÏÖµÄËùÓнӿںÍËùÓÐÀà²ã¼¶µÄ·½·¨£¬Ñ­»·ÑéÖ¤ + if (introductionAwareMethodMatcher != null ? + introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : + methodMatcher.matches(method, targetClass)) { + return true; + } + } + } + + return false; +} + +``` + +¿´µ½ÕâÀ»ù±¾ÉϾÍÁ˽âÁË spring ÊÇÈçºÎÀ´ÅжÏÒ»¸ö¶ÔÏóÊÇ·ñÐèÒª±»´úÀíµÄ£¬ÕâÀï×ܽáÁ÷³ÌÈçÏ£º + +1. »ñÈ¡µ½ÏîÄ¿ÖÐËùÓеÄÇÐÃæ¶ÔÏ󣬽«ÆäÖеÄÇÐÃæ·½·¨·âװΪһ¸ö `List`£¬¾ßÌå²Ù×÷ÔÚÉÏһƪÎÄÕÂÖÐÓÐÏêϸ·ÖÎö£» +2. ±éÀú `Advisor`£¬¶Ôÿһ¸ö `Advisor`£¬ÀûÓ÷´Éä»ñÈ¡µ±Ç°ÀàµÄ³ý `Object` ÍâµÄËùÓи¸À༰½Ó¿Ú£¬½á¹ûΪ `Set`£» +3. ±éÀú `Set`£¬¶ÔÆäÖÐÿһ¸ö `Class`£¬ÀûÓ÷´Éä»ñÈ¡¸Ã `Class` ·½·¨¡¢³ý Object ÍâµÄËùÓи¸ÀàµÄ·½·¨¡¢½Ó¿ÚµÄĬÈÏ·½·¨£¬½á¹ûΪ `Method[]`; +4. ±éÀú `Method[]`£¬Èç¹ûÓÐÒ»¸ö `method` Âú×ã `Advisor` µÄÇÐÃæÌõ¼þ£¬Ôò±íʾµ±Ç° `Advisor` ¿ÉÒÔÓ¦Óõ½µ±Ç° bean£¬¸Ã bean ¾ÍÐèÒª±»´úÀí£¬ÕâÒ»²½×îÖյõ½µÄ½á¹ûÒ²ÊÇÒ»¸ö `List`£¬±íʾ¸ÃÓжà¸ö `Advisor` ÐèÒªÓ¦Óõ½¸Ã¶ÔÏó¡£ + +α´úÂëÀàËÆÓÚ£º + +``` +// 1\. »ñÈ¡ËùÓеÄAdvisor +List advisorList = getAdvisorList(); +// 2\. ±éÀúAdvisor +for(Advisor advisor : advisorList) { + // »ñÈ¡µ±Ç°ÀàµÄ³ý`Object`ÍâµÄËùÓи¸À༰½Ó¿Ú£¬classSetÒ²°üº¬targetClass + Set classSet = getSuperClassAndInterfaces(targetClass); + for(Class cls : classSet) { + // ±éÀúclsÖж¨ÒåµÄ·½·¨£¬°üÀ¨µ±Ç°ÀàµÄ·½·¨¡¢³ýObjectÍâµÄËùÓи¸Àà·½·¨¡¢½Ó¿ÚµÄĬÈÏ·½·¨ + Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); + // ±éÀúÕâЩ·½·¨ + for (Method method : methods) { + // ÅжÏmethodÊÇ·ñÂú×ãÇÐÃæÌõ¼þ + } + } +} + +``` + +µÃµ½ `List` ºó£¬½ÓÏÂÀ´¾ÍÊǸù¾Ý `List` À´´´½¨´úÀí¶ÔÏóÁË¡£ + +### 2\. ´´½¨´úÀí¶ÔÏó + +ÎÒÃÇÀ´¿´¿´ spring ´´½¨´úÀí¶ÔÏóµÄÁ÷³Ì¡£ + +> `AbstractAutoProxyCreator#wrapIfNecessary`£º + +``` +protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { + ... + if (specificInterceptors != DO_NOT_PROXY) { + this.advisedBeans.put(cacheKey, Boolean.TRUE); + // ´úÀí¶ÔÏó¾ÍÊÇÔÚÕâÀï´´½¨µÄ + // - specificInterceptors£º¿ÉÒÔÓ¦Óõ½¸Ã¶ÔÏóµÄ Advisor£¬ + // specificInterceptorsµÄ»ñÈ¡¹ý³Ì£¬ÉÏÒ»²¿·ÖÒѾ­Ïêϸ·ÖÎö¹ýÁË£¬²»ÔÙ׸ÊöÁË + // - SingletonTargetSource£º¶Ôԭʼ¶ÔÏóµÄÒ»¸ö°ü×° + Object proxy = createProxy( + bean.getClass(), beanName, specificInterceptors, + new SingletonTargetSource(bean)); + this.proxyTypes.put(cacheKey, proxy.getClass()); + return proxy; + } + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return bean; +} + +``` + +ÎÒÃÇÏÈÀ´¿´¿´ `SingletonTargetSource`£º + +``` +public class SingletonTargetSource implements TargetSource, Serializable { + + private static final long serialVersionUID = 9031246629662423738L; + + private final Object target; + + public SingletonTargetSource(Object target) { + Assert.notNull(target, "Target object must not be null"); + this.target = target; + } + + @Override + public Class getTargetClass() { + return this.target.getClass(); + } + + @Override + public Object getTarget() { + return this.target; + } + + ... +} + +``` + +Õâ¸öÀà´úÂëºÜ¼òµ¥£¬¾ÍÊǶÔԭʼ¶ÔÏó×öÁËÒ»²ã°ü×°¡£¼ÌÐøÍùÏ¿´£º + +> AbstractAutoProxyCreator#createProxy + +``` +protected Object createProxy(Class beanClass, @Nullable String beanName, + @Nullable Object[] specificInterceptors, TargetSource targetSource) { + + if (this.beanFactory instanceof ConfigurableListableBeanFactory) { + AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) + this.beanFactory, beanName, beanClass); + } + + ProxyFactory proxyFactory = new ProxyFactory(); + //ʹÓÃproxyFactory¶ÔÏócopyµ±Ç°ÀàÖеÄÏà¹ØÊôÐÔ + proxyFactory.copyFrom(this); + + // ÅжÏÊÇ·ñʹÓÃCglib¶¯Ì¬´úÀí£¬¿ÉÒÔÔÚ×¢½âÖÐÖ¸¶¨£º + // @EnableAspectJAutoProxy(proxyTargetClass = true) + if (!proxyFactory.isProxyTargetClass()) { + // Èç¹û beanFactory ÊÇ ConfigurableListableBeanFactory£¬ + // Ôò¿ÉÔÚ BeanDefinition ÉèÖÃÊôÐÔ£¬µ¥¶À¿ØÖÆÄ³Ò»¸öÀàʹÓà cglib ´úÀí + if (shouldProxyTargetClass(beanClass, beanName)) { + proxyFactory.setProxyTargetClass(true); + } + else { + // Èç¹ûûÓÐÅäÖÿªÆô, ÔòÅжÏbeanÊÇ·ñÓкÏÊʵĽӿÚʹÓÃJDKµÄ¶¯Ì¬´úÀí + // ×¢Ò⣺JDK¶¯Ì¬´úÀí±ØÐëÊÇ´øÓнӿڵÄÀà + // Èç¹ûÀàûÓÐʵÏÖÈκνӿÚÔòÖ»ÄÜʹÓÃCglib¶¯Ì¬´úÀí£© + evaluateProxyInterfaces(beanClass, proxyFactory); + } + } + + // ¹¹½¨Advisor£¬ÕâÀï°üº¬Á½¸ö²Ù×÷£º + // 1\. Ìí¼Ó¹«¹²µÄ Interceptor + // 2\. ¶ÔÓÚ¸ø¶¨µÄadvisor£¬ÅÐ¶ÏÆäÀàÐÍ£¬È»ºóת»»ÎªÆä¾ßÌåÀàÐÍ + Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); + //Ìí¼ÓËùÓÐÔöÇ¿ + proxyFactory.addAdvisors(advisors); + //ÉèÖÃÒª´úÀíµÄÀà + proxyFactory.setTargetSource(targetSource); + //SpringµÄÒ»¸öÀ©Õ¹µã£¬Ä¬ÈÏʵÏÖΪ¿Õ¡£Áô¸øÎÒÃÇÔÚÐèÒª¶Ô´úÀí½øÐÐÌØÊâ²Ù×÷µÄʱºòʵÏÖ + customizeProxyFactory(proxyFactory); + + proxyFactory.setFrozen(this.freezeProxy); + if (advisorsPreFiltered()) { + proxyFactory.setPreFiltered(true); + } + //ʹÓôúÀí¹¤³§»ñÈ¡´úÀí¶ÔÏó + return proxyFactory.getProxy(getProxyClassLoader()); +} + +``` + +ÔÚ `@EnableAspectJAutoProxy` ×¢½âÖУ¬¿ÉÒÔʹÓà `proxyTargetClass = true` À´ÉèÖÃÏîĿʹÓà `cglib` ´úÀí£¬ÕâÔÚ´úÂëÖÐÒ²ÓÐÌåÏÖ£º + +``` +// Ö»ÓÐÔÚproxyFactory.isProxyTargetClass()Ϊfalseʱ£¬²Å»á½øÐÐÏÂÃæµÄÅÐ¶Ï +// »»ÑÔÖ®£¬µ± @EnableAspectJAutoProxy(proxyTargetClass = true) ʱ +// ÏÂÃæµÄ´úÂëÊDz»»áÔËÐеģ¬Ä¬ÈÏʹÓþÍÊÇcglib´úÀí +if (!proxyFactory.isProxyTargetClass()) { + // ÅжÏÓÐûÔÚ BeanDefinition ÖÐÉèÖÃʹÓà cglib´úÀí + if (shouldProxyTargetClass(beanClass, beanName)) { + proxyFactory.setProxyTargetClass(true); + } + else { + // ÊÇ·ñÂú×ã´úÀí½Ó¿ÚµÄÌõ¼þ£¬¼´ÊÇ·ñÂú×ãjdk¶¯Ì¬´úÀíµÄÌõ¼þ + evaluateProxyInterfaces(beanClass, proxyFactory); + } +} + +``` + +spring ÊÇÈçºÎÅжÏÒ»¸öÀàÊÇ·ñÂú×ã jdk ¶¯Ì¬´úÀíµÄÌõ¼þµÄÄØ£¿´ÓÎÒÃÇÀ´ÈÏÖªÉÏÀ´Ëµ£¬ÊµÏÖÁ˽ӿڣ¬¾Í¿ÉÒÔʹÓö¯Ì¬´úÀí£¬·ñÔò¾ÍÖ»ÄÜʹÓà cglib ´úÀí£¬ÎÒÃÇÀ´¿´¿´ spring ÊÇÈçºÎÅжϵģº + +> ProxyProcessorSupport#evaluateProxyInterfaces + +``` +/** + * ÅжÏÊÇ·ñÄÜʹÓÃjdk¶¯Ì¬´úÀí + */ +protected void evaluateProxyInterfaces(Class beanClass, ProxyFactory proxyFactory) { + // »ñÈ¡ÀàµÄËùÓÐ½Ó¿Ú + Class[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader()); + boolean hasReasonableProxyInterface = false; + for (Class ifc : targetInterfaces) { + // 1.isConfigurationCallbackInterface: ÅжÏifcÊÇ·ñΪInitializingBean£¬DisposableBean£¬ + // Closeable£¬AutoCloseable£¬ÒÔ¼°°üº¬ Aware + // 2.isInternalLanguageInterface: ÊÇ·ñΪÄÚ²¿ÓïÑÔ½Ó¿Ú£¬Èçgroovy£¬mockµÈ + // 3.ifc.getMethods().length > 0£º½Ó¿ÚµÄ·½·¨Êý±ØÐë´óÓÚ1 + if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) && + ifc.getMethods().length > 0) { + hasReasonableProxyInterface = true; + break; + } + } + if (hasReasonableProxyInterface) { + // ÐèÒª½«ËùÓеĽӿڶ¼ÉèÖõ½proxyFactory + // ÊÔÏëһϣ¬Èç¹ûÒ»¸öÀàA ʵÏÖÁË½Ó¿Ú I1 Óë I2£¬ + // Èç¹ûÀàAµÄ¶ÔÏóa Íйܵ½ÁËspringÈÝÆ÷£¬ÄÇôÎÞÂÛÊÇʹÓà beanFactory.get(I1.class)£¬ + // »¹ÊÇ beanFactory.get(I1.class)£¬¶¼Ó¦¸ÃÄÜ»ñÈ¡µ½a. + for (Class ifc : targetInterfaces) { + proxyFactory.addInterface(ifc); + } + } + else { + proxyFactory.setProxyTargetClass(true); + } +} + +``` + +´ÓÔ´ÂëÉÏÀ´¿´£¬spring ÅжÏÊÇ·ñÄÜʹÓà jdk ¶¯Ì¬´úÀíµÄ¹ý³ÌÓëÎÒÃÇÈÏÖªÉϵIJ¶à£¬²»¹ý²¢²»ÊÇʵÏÖÁËÈÎÒâ½Ó¿Ú¾ÍÄÜʹÓà jdk ¶¯Ì¬´úÀí£¬spring »áÅųý `InitializingBean`£¬`DisposableBean`£¬`Closeable`£¬`AutoCloseable` µÈ½Ó¿Ú£¬Í¬Ê±Ò²»áÅųýÎÞÈκη½·¨µÄ½Ó¿Ú¡£ + +·ÖÎöÍê spring ÈçºÎÅжÏÊÇ·ñʹÓà jdk ¶¯Ì¬´úÀíºó£¬½Ó¿ÚÎÒÃÇÀ´¿´¿´ spring ÊÇÈçºÎ´´½¨´úÀí¶ÔÏóµÄ¡£ÎªÁË˵Ã÷ÎÊÌ⣬Ê×Ïȼò»¯Ï `AbstractAutoProxyCreator#createProxy`£º + +``` +protected Object createProxy(Class beanClass, @Nullable String beanName, + @Nullable Object[] specificInterceptors, TargetSource targetSource) { + // Ê¡ÂÔһЩ´úÂë + ... + ProxyFactory proxyFactory = new ProxyFactory(); + //ʹÓÃproxyFactory¶ÔÏócopyµ±Ç°ÀàÖеÄÏà¹ØÊôÐÔ + proxyFactory.copyFrom(this); + // ÕâÀïÊ¡ÂÔÁ˺öàµÄÅÐ¶Ï + proxyFactory.setProxyTargetClass(true); + //Ìí¼ÓËùÓÐÔöÇ¿ + proxyFactory.addAdvisors(advisors); + //ÉèÖÃÒª´úÀíµÄÀà + proxyFactory.setTargetSource(targetSource); + ... + //ʹÓôúÀí¹¤³§»ñÈ¡´úÀí¶ÔÏó + return proxyFactory.getProxy(getProxyClassLoader()); +} + +``` + +´ÓÕâÀï¿ÉÒÔ¿´³ö£¬Õâ¸ö·½·¨´´½¨ÁËÒ»¸ö `ProxyFactory` ¶ÔÏó£¬È»ºóÍù¸Ã¶ÔÏóµÄÊôÐÔÀïÉèÖÃÁËһЩֵ¡£¼ÌÐøÍùÏ¿´£º + +> ProxyFactory#getProxy(java.lang.ClassLoader) + +``` +public Object getProxy(@Nullable ClassLoader classLoader) { + return createAopProxy().getProxy(classLoader); +} + +``` + +ÕâÀïÓÐÁ½¸ö·½·¨£º`createAopProxy()` Óë `getProxy(classLoader)`£¬ÎÒÃÇÏÈÀ´¿´ `createAopProxy()`¡£ + +> ProxyCreatorSupport#createAopProxy + +``` +protected final synchronized AopProxy createAopProxy() { + if (!this.active) { + activate(); + } + return getAopProxyFactory().createAopProxy(this); +} + +``` + +¼ÌÐø£¬ + +> DefaultAopProxyFactory#createAopProxy + +``` +/** + * ÅжϴúÀíÀàÐÍ + * Èç¹ûÄÜʹÓÃjdk¶¯Ì¬´úÀí£¬¾Í·µ»Ø JdkDynamicAopProxy + * ·ñÔò¾Í·µ»Ø ObjenesisCglibAopProxy + */ +public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { + if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { + Class targetClass = config.getTargetClass(); + if (targetClass == null) { + throw new AopConfigException(...); + } + if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { + return new JdkDynamicAopProxy(config); + } + return new ObjenesisCglibAopProxy(config); + } + else { + return new JdkDynamicAopProxy(config); + } +} + +``` + +µ½ÕâÀÎÒÃǾÍÄÜÃ÷°×£¬`JdkDynamicAopProxy` ÊÇÓÃÀ´´¦Àí jdk ¶¯Ì¬´úÀíµÄ£¬`ObjenesisCglibAopProxy` ÊÇÓÃÀ´´¦Àí cglib ´úÀíµÄ¡£½ÓÏÂÀ´£¬ÎÒÃÇÀ´¿´¿´ `getProxy(classLoader)` ·½·¨¡£ + +> JdkDynamicAopProxy#getProxy(java.lang.ClassLoader) + +``` +@Override +public Object getProxy(@Nullable ClassLoader classLoader) { + Class[] proxiedInterfaces = AopProxyUtils + .completeProxiedInterfaces(this.advised, true); + // ÊÇ·ñÓÐequals()ÓëhashCode()·½·¨ + findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); + // µ÷Óà jdk ·½·¨ ´´½¨¶ÔÏó + return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); +} + +``` + +×îºóÎÒÃÇÀ´¿´¿´µÃµ½µÄ´úÀí¶ÔÏóÊÇʲôÑùµÄ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ee46c03d86755b936862c9e8cde266fca1e.png) + +¿ÉÒÔ¿´µ½£¬´úÀí¶ÔÏóµÄ `h` ÊôÐÔ±£´æµÄ¾ÍÊÇ `JdkDynamicAopProxy` ¶ÔÏó£¬`JdkDynamicAopProxy` ¶ÔÏóµÄ `advised` ÊôÐÔ±£´æÁË´úÀíµÄ´úÀíµÄÏà¹ØÐÅÏ¢¡£ + +> CglibAopProxy#getProxy(java.lang.ClassLoader) + +``` +public Object getProxy(@Nullable ClassLoader classLoader) { + try { + Class rootClass = this.advised.getTargetClass(); + Assert.state(rootClass != null, "xxx"); + + Class proxySuperClass = rootClass; + if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { + proxySuperClass = rootClass.getSuperclass(); + Class[] additionalInterfaces = rootClass.getInterfaces(); + for (Class additionalInterface : additionalInterfaces) { + this.advised.addInterface(additionalInterface); + } + } + + validateClassIfNecessary(proxySuperClass, classLoader); + + // ´´½¨ Enhancer ¶ÔÏ󣬲¢setһЩÊôÐÔ + Enhancer enhancer = createEnhancer(); + if (classLoader != null) { + enhancer.setClassLoader(classLoader); + if (classLoader instanceof SmartClassLoader && + ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { + enhancer.setUseCache(false); + } + } + // Superclass¾ÍÊÇÒª´úÀíµÄÀà + enhancer.setSuperclass(proxySuperClass); + // ÉèÖýӿڣ¬Èç SpringProxy£¬Advised + enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); + enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); + enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); + + Callback[] callbacks = getCallbacks(rootClass); + Class[] types = new Class[callbacks.length]; + for (int x = 0; x < types.length; x++) { + types[x] = callbacks[x].getClass(); + } + enhancer.setCallbackFilter(new ProxyCallbackFilter( + this.advised.getConfigurationOnlyCopy(), + this.fixedInterceptorMap, this.fixedInterceptorOffset)); + enhancer.setCallbackTypes(types); + + return createProxyClassAndInstance(enhancer, callbacks); + } + catch (CodeGenerationException | IllegalArgumentException ex) { + throw new AopConfigException(...); + } + catch (Throwable ex) { + throw new AopConfigException("Unexpected AOP exception", ex); + } +} + +``` + +spring ʹÓà cglib ´´½¨¶ÔÏó£¬Ö÷ÒªÓõ½ÁË `Enhancer` À࣬¹ØÓÚÕâÒ»¿éºóÃæ»áÔÙ·ÖÎö¡£ + +×îºóÎÒÃÇÒ²À´¿´¿´´´½¨ºóµÃµ½µÄ¶ÔÏó£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-97d647a818f62fa0979dabd58f3aa19e473.png) + +### 3\. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË `AbstractAutoProxyCreator#postProcessAfterInitialization`£¬¸Ã·½·¨Ö÷Òª×öÁËÈý¼þÊ£º + +1. »ñÈ¡ÏîÄ¿ÖÐËùÓÐÇÐÃæÀ࣬½«Çе㷽·¨°üװΪ `List`£ºÊµ¼ÊÉÏ£¬ÕâÒ»²½µÄ²Ù×÷Ò²»áÔÚ `AbstractAutoProxyCreator#postProcessBeforeInitialization` ÖÐÖ´ÐУ¬È»ºó½«½á¹û»º´æÁËÆðÀ´£¬ÕâÒ»²½ÆäʵÊÇÖ±½ÓÔÚ»º´æÖÐÄýá¹û£» +2. »ñÈ¡µ±Ç°¶ÔÏóµÄËùÓÐÔöÇ¿£ºÕâÒ»²½¾ÍÊÇÅжÏÄÄЩÔöÇ¿¿ÉÒÔÓÃÓÚµ±Ç°¶ÔÏó£¬ÅжÏʱÏÈ»ñÈ¡µ±Ç°ÀàµÄËùÓнӿÚÓë²»°üÀ¨ Object µÄ¸¸À࣬ȻºóÖðÒ»ÅжÏÕâЩ½Ó¿ÚÓëÀàÖеķ½·¨ÊÇ·ñÂú×ãÔöÇ¿Ìõ¼þ£¬Ö»ÒªÓÐÒ»¸ö·½·¨Âú×㣬¾Í±íʾµ±Ç°¶ÔÏóÐèÒª±»´úÀí£» +3. ´´½¨´úÀí¶ÔÏ󣺴´½¨´úÀí¶ÔÏóʱ£¬Ä¬ÈÏÇé¿öÏ£¬»á¸ù¾ÝÊÇ·ñʵÏÖÁ˽ӿÚÀ´Ñ¡ÔñʹÓà jdk ¶¯Ì¬´úÀí»¹ÊÇ cglib£¬¿ÉÓ¦ÓÃÓÚµ±Ç° bean µÄ `List` Ò²»á·â×°½ø´úÀí¶ÔÏóÖС£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4687961](https://my.oschina.net/funcy/blog/4687961) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" new file mode 100644 index 0000000..412483a --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\344\272\224\357\274\211\357\274\232cglib\344\273\243\347\220\206.md" @@ -0,0 +1,449 @@ + + +[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4696654 "ÉÏһƪÎÄÕÂ")½éÉÜÁË spring µÄ jdK ¶¯Ì¬´úÀí£¬±¾ÎÄÀ´½éÉÜ spring µÄ cglib ´úÀí¡£ + +### 1\. cglib ´úÀí¼ò½é + +jdk ËäÈ»ÌṩÁ˶¯Ì¬´úÀí£¬µ«ÊǶ¯Ì¬´úÀíÓÐÒ»¸ö²»×㣺**Èç¹ûÀàûÓÐʵÏÖ½Ó¿Ú£¬¾ÍÎÞ·¨ jdk ¶¯Ì¬´úÀí**¡£ÎªÁ˽â¾öÕâ¸ö²»×㣬spring ÓÖÒýÈëÁË cglib ´úÀí¡£ + +cglib µ×²ãÊÇ»ùÓÚ asm µÄ£¬Ò²¾ÍÊÇÖ±½Ó²Ù×÷×Ö½ÚÂ룬Ï൱ÓÚ¶Ô asm ½øÐÐÁËÒ»²ã·â×°¡£Ö±½Ó²Ù×÷×ÖÂ룬ÐèÒª¶Ô java Ö¸Áî¡¢×Ö½ÚÂëÎļþÓÐÉîÈëÀí½â²ÅÄܽøÐУ¬µ«×Ö½ÚÂë»ÞɬÄѶ®£¬Ò»°ã²»½¨ÒéÖ±½Ó²Ù×÷¡£¾­ cglib ·â×°ºó£¬×Ö½ÚÂëµÄ²Ù×÷¾Í±äµÃ¼òµ¥¶àÁË£¬Òò´Ë**¾ø´ó¶àÊýÇé¿ö϶¼½¨ÒéʹÓà cglib ·â×°ºÃµÄ·½·¨À´½øÐÐ×Ö½ÚÂë²Ù×÷**¡£ + +spring cglib ²Ù×÷λÓÚ `spring-core` Ä£¿é£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6b64440344221ca08ed8c822b5b22c1d341.png) + +ÔÙÀ´¿´¿´ asm Óë cglib °ü˵Ã÷£º + +``` +/** + * Spring's repackaging of + * ASM 7.0 + * (with Spring-specific patches; for internal use only). + * + *

This repackaging technique avoids any potential conflicts with + * dependencies on ASM at the application level or from third-party + * libraries and frameworks. + * + *

As this repackaging happens at the class file level, sources + * and javadocs are not available here. + */ + package org.springframework.asm; + +``` + +×¢ÒâµÚÒ»¾ä£º`Spring's repackaging of ASM 7.0`£¬±íÃ÷ÕâÊÇ spring ¶Ô `asm7.0` ÖØÐ´ò°ü. + +``` +/** + * Spring's repackaging of + * CGLIB 3.3 + * (with Spring-specific patches; for internal use only). + * + *

This repackaging technique avoids any potential conflicts with + * dependencies on CGLIB at the application level or from third-party + * libraries and frameworks. + * + *

As this repackaging happens at the class file level, sources + * and javadocs are not available here. + */ +package org.springframework.cglib; + +``` + +×¢ÒâµÚÒ»¾ä£º`Spring's repackaging of CGLIB 3.3`£¬±íÃ÷ÕâÊÇ spring ¶Ô `CGLIB 3.3` ÖØÐ´ò°ü. + +ºÎÎ½ÖØÐ´ò°üÄØ£¿¸öÈËÀí½â£¬¾ÍÊǽ« `asm7.0` Óë `CGLIB 3.3` µÄÔ´Âë¸Ä¸ö°üÃû£¬¸´ÖƵ½ spring ÏîĿϡ£Òò´Ë spring ²¢Ã»ÓÐÔÚ `gradle` ÎļþÖÐÒýÈë `asm` Óë `cglib` µÄÏ൱ jar °ü£¬¶øÊÇÔÚÏîÄ¿ÖÐÖ±½ÓÒýÈëÁËÕâÁ½¸öÏîÄ¿µÄÔ´Â룡 + +### 2\. cglib ´úÀíʾÀý + +ÔÚÕýʽ¿ªÊ¼Ö®Ç°£¬ÎÒÃÇÏÈÀ´¿´ ¿´ cglib ´úÀíÊÇÈçºÎ½øÐеġ£ + +Ê×ÏÈ×¼±¸Ò»¸öÀࣺ + +``` +package org.springframework.learn.demo04; + +public class CglibProxyService { + public void hello01() { + System.out.println("hello01"); + } +} + +``` + +ÔÙ×¼±¸Ò»¸ö `MethodInterceptor`£¨Àà±È jdk ¶¯Ì¬´úÀíÖÐµÄ `InvocationHandler`£©£º + +``` +package org.springframework.learn.demo04; + +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; + +import java.lang.reflect.Method; + +public class MyMethodInterceptor implements MethodInterceptor { + + /** Ä¿±ê¶ÔÏó */ + private Object target; + + public MyMethodInterceptor(Object target){ + this.target = target; + } + + @Override + public Object intercept(Object proxyObj, Method method, Object[] objects, + MethodProxy proxy) throws Throwable { + System.out.println("Ö´Ðз½·¨Îª:" + method.getName()); + return proxy.invoke(target, objects); + } +} + +``` + +×îºóÊÇÖ÷Àࣺ + +``` +package org.springframework.learn.demo04; + +import org.springframework.cglib.proxy.Enhancer; + +/** + * £ûÕâÀïÌí¼ÓÃèÊö£ý + * + * @author fangchengyan + * @date 2020-11-01 9:23 ÏÂÎç + */ +public class Demo04Main { + + public static void main(String[] args) { + CglibProxyService target = new CglibProxyService(); + MyMethodInterceptor interceptor = new MyMethodInterceptor(target); + + Enhancer enhancer = new Enhancer(); + // ÉèÖø¸Àà + enhancer.setSuperclass(CglibProxyService.class); + // ÉèÖÃcallback£¬Õâ¸öcallback¾ÍÊÇÉÏÃæÌṩµÄ MyMethodInterceptor + enhancer.setCallback(interceptor); + // ʹÓà enhancer ´´½¨´úÀí¶ÔÏó + CglibProxyService proxy = (CglibProxyService)enhancer.create(); + proxy.hello01(); + } +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +Ö´Ðз½·¨Îª:hello01 +hello01 + +``` + +¿ÉÒÔ¿´µ½£¬×îÖÕÊÇÔÚ `MyMethodInterceptor#intercept` Ö´ÐÐÄ¿±ê¶ÔÏóµÄ·½·¨¡£ + +ͬ jdk ¶¯Ì¬´úÀí±È½Ïºó£¬·¢ÏÖÁ½Õß´úÂë¸ß¶ÈÏàËÆ£º + +* `InvocationHandler` Óë `InvocationHandler`£ºÁ½Õß´úÂëÐÎʽ¼¸ºõÒ»Ö ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6f3e8213e743554a7a8ffc587b98b1d7d3a.png) + +* ´úÀí¶ÔÏóµÄ´´½¨£ºÒ»¸öÊÇʹÓà `Enhangcer` ½øÐдúÀí¶ÔÏó´´½¨£¬Ò»¸öÊÇʹÓ÷â×°ºÃµÄ·½·¨½øÐжÔÏó´´½¨¡£ ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8ee847f9cbb7ec7fa6c35b2ad5a5537ef3a.png) + +´Ó´úÀí¶ÔÏó´´½¨À´¿´£¬¿ÉÒÔ¿´³ö `cglib` Éú³É¶ÔÏóʱ£¬ÅäÖõIJÎÊý½Ï¶à£¬¹¦ÄܽϷḻ¡£ + +¹ØÓÚ `Enhangcer` ÊÇÈçºÎ´´½¨´úÀí¶ÔÏó£¬ÒÔ¼° `org.springframework.asm` Óë `org.springframework.cglib` ÕâÁ½¸ö°üϵĴúÂ룬ÕâЩÊôÓÚ cglib µÄÄÚÈÝ£¬¾Í²»¹ý¶à·ÖÎöÁË¡£ + +### 3\. spring ÖÐ `cglib` ´´½¨´úÀí¶ÔÏó + +ÎÒÃÇÔÙÀ´¿´¿´ spring ÊÇÈçºÎ´´½¨´úÀí¶ÔÏóµÄ£º + +> CglibAopProxy#getProxy(java.lang.ClassLoader) + +``` +public Object getProxy(@Nullable ClassLoader classLoader) { + try { + Class rootClass = this.advised.getTargetClass(); + Assert.state(rootClass != null, "..."); + + Class proxySuperClass = rootClass; + if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { + proxySuperClass = rootClass.getSuperclass(); + Class[] additionalInterfaces = rootClass.getInterfaces(); + for (Class additionalInterface : additionalInterfaces) { + this.advised.addInterface(additionalInterface); + } + } + + // ¶ÔÄ¿±êÀà½øÐмì²é£¬Ö÷Òª¼ì²éµãÓÐÈý¸ö£º + // 1\. Ä¿±ê·½·¨²»ÄÜʹÓÃfinalÐÞÊΣ» + // 2\. Ä¿±ê·½·¨²»ÄÜÊÇprivateÀàÐ͵ģ» + // 3\. Ä¿±ê·½·¨²»ÄÜÊǰü·ÃÎÊȨÏ޵ģ» + // ÕâÈý¸öµãÂú×ãÈκÎÒ»¸ö£¬µ±Ç°·½·¨¾Í²»Äܱ»´úÀí£¬´Ëʱ¸Ã·½·¨¾Í»á±»ÂÔ¹ý + validateClassIfNecessary(proxySuperClass, classLoader); + + Enhancer enhancer = createEnhancer(); + if (classLoader != null) { + enhancer.setClassLoader(classLoader); + if (classLoader instanceof SmartClassLoader && + ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { + enhancer.setUseCache(false); + } + } + // Superclass¾ÍÊÇÒª´úÀíµÄÀà + enhancer.setSuperclass(proxySuperClass); + // ÕâÀïAopProxyUtils.completeProxiedInterfaces()·½·¨µÄÖ÷ҪĿµÄÊÇΪҪÉú³ÉµÄ´úÀíÀà + // Ôö¼ÓSpringProxy£¬Advised£¬DecoratingProxyÈý¸öÐèҪʵÏֵĽӿڡ£ÕâÀïÈý¸ö½Ó¿ÚµÄ×÷ÓÃÈçÏ£º + // 1\. SpringProxy£ºÊÇÒ»¸ö¿Õ½Ó¿Ú£¬ÓÃÓÚ±ê¼Çµ±Ç°Éú³ÉµÄ´úÀíÀàÊÇSpringÉú³ÉµÄ´úÀíÀࣻ + // 2\. Advised£ºSpringÉú³É´úÀíÀàËùʹÓõÄÊôÐÔ¶¼±£´æÔڸýӿÚÖУ¬ + // °üÀ¨Advisor£¬AdviceºÍÆäËûÏà¹ØÊôÐÔ£» + // 3\. DecoratingProxy£º¸Ã½Ó¿ÚÓÃÓÚ»ñÈ¡µ±Ç°´úÀí¶ÔÏóËù´úÀíµÄÄ¿±ê¶ÔÏóµÄClassÀàÐÍ¡£ + enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); + enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); + enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); + + // Ìí¼Ócallback + Callback[] callbacks = getCallbacks(rootClass); + Class[] types = new Class[callbacks.length]; + for (int x = 0; x < types.length; x++) { + types[x] = callbacks[x].getClass(); + } + // ÉèÖôúÀíÀàÖи÷¸ö·½·¨½«ÒªÊ¹ÓõÄÇÐÃæÂß¼­£¬ÕâÀïProxyCallbackFilter.accept()·½·¨·µ»Ø + // µÄÕûÐÍÖµÕýºÃÒ»Ò»¶ÔÓ¦ÉÏÃæCallbackÊý×éÖи÷¸öÇÐÃæÂß¼­µÄϱ꣬Ҳ¾ÍÊÇ˵ÕâÀïµÄCallbackFilter + // µÄ×÷ÓÃÕýºÃÖ¸¶¨ÁË´úÀíÀàÖи÷¸ö·½·¨½«ÒªÊ¹ÓÃCallbackÊý×éÖеÄÄĸö»òÄöÇÐÃæÂß¼­ + enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), + this.fixedInterceptorMap, this.fixedInterceptorOffset)); + enhancer.setCallbackTypes(types); + + // Éú³É´úÀí¶ÔÏó + return createProxyClassAndInstance(enhancer, callbacks); + } + catch (...) { + ... + } +} + +``` + +ÒÔÉÏ´úÂëºËÐÄÊÇÉèÖà `Enhancer` µÄÊôÐÔ£¬Èç `classLoader`¡¢`superclass`¡¢`callbackFilter` µÈ£¬×îºóµ÷Óà `createProxyClassAndInstance(xxx)` ´´½¨´úÀí¶ÔÏó£º + +> ObjenesisCglibAopProxy#createProxyClassAndInstance + +``` +protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { + // ´´½¨´úÀíÀà + Class proxyClass = enhancer.createClass(); + Object proxyInstance = null; + + // ¸ù¾Ý´úÀíÀ࣬ʹÓ÷´ÉäÉú³É¶ÔÏó + if (objenesis.isWorthTrying()) { + try { + proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache()); + } + catch (Throwable ex) { + ... + } + } + + if (proxyInstance == null) { + try { + Constructor ctor = (this.constructorArgs != null ? + proxyClass.getDeclaredConstructor(this.constructorArgTypes) : + proxyClass.getDeclaredConstructor()); + ReflectionUtils.makeAccessible(ctor); + proxyInstance = (this.constructorArgs != null ? + ctor.newInstance(this.constructorArgs) : ctor.newInstance()); + } + catch (Throwable ex) { + throw new AopConfigException(...); + } + } + // ÉèÖÃcallbackÊôÐÔ + // µ±ÉèÖÃÁ˶à¸ö callback ʱ£¬»áͨ¹ý CallbackFilter À´È·¶¨×îÖÕʹÓÃÄĸö callback + ((Factory) proxyInstance).setCallbacks(callbacks); + return proxyInstance; +} + +``` + +ͨ¹ýÔÚµÚ¶þ²¿·ÖµÄ `demo04` ¿ÉÒÔÖªµÀ£¬cglib ·½·¨µÄÖ´ÐУ¬»á¾­¹ý `MethodInterceptor#intercept` ·½·¨µ÷Óõģ¬Ò²¾ÍÊÇ `Enhancer` µÄ `callback` ÊôÐÔ£¬Òò´Ë½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `callback` µÄ»ñÈ¡£¬Ïà¹Ø´úÂëλÓÚ `CglibAopProxy#getCallbacks`£º + +> CglibAopProxy#getCallbacks + +``` +private Callback[] getCallbacks(Class rootClass) throws Exception { + boolean exposeProxy = this.advised.isExposeProxy(); + boolean isFrozen = this.advised.isFrozen(); + boolean isStatic = this.advised.getTargetSource().isStatic(); + + // Óû§×Ô¶¨ÒåµÄ´úÀíÂß¼­µÄcallback£¬¼´¿ª·¢ÖУ¬¶¨ÒåµÄ @Before¡¢@Around¡¢@AfterµÈÇÐÃæ·½·¨£¬ + // ½«ÔÚDynamicAdvisedInterceptor½øÐе÷Óà + Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); + + Callback targetInterceptor; + // ÅжÏÈç¹ûÒª±©Â¶´úÀí¶ÔÏó£¬Èç¹ûÊÇ£¬ÔòʹÓÃAopContextÉèÖý«´úÀí¶ÔÏóÉèÖõ½ThreadLocalÖÐ + // Óû§Ôò¿ÉÒÔͨ¹ýAopContext»ñȡĿ±ê¶ÔÏó + if (exposeProxy) { + // Åжϱ»´úÀíµÄ¶ÔÏóÊÇ·ñÊǾ²Ì¬µÄ£¬Èç¹ûÊǾ²Ì¬µÄ£¬Ôò½«Ä¿±ê¶ÔÏ󻺴æÆðÀ´£¬Ã¿´Î¶¼Ê¹ÓøöÔÏó¼´¿É£¬ + // Èç¹ûÄ¿±ê¶ÔÏóÊǶ¯Ì¬µÄ£¬ÔòÔÚDynamicUnadvisedExposedInterceptorÖÐÿ´Î¶¼Éú³ÉÒ»¸öÐ嵀 + // Ä¿±ê¶ÔÏó£¬ÒÔÖ¯ÈëºóÃæµÄ´úÀíÂß¼­ + targetInterceptor = (isStatic ? + new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : + new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); + } + else { + // ÕâÀïµÄÁ½¸öÀàÓëÉÏÃæÁ½¸öµÄÎ¨Ò»Çø±ð¾ÍÔÚÓÚÊÇ·ñʹÓÃAopContext±©Â¶Éú³ÉµÄ´úÀí¶ÔÏó + targetInterceptor = (isStatic ? + new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : + new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); + } + + // µ±Ç°CallbackÓÃÓÚ²»Óñ»´úÀíµÄ·½·¨ + Callback targetDispatcher = (isStatic ? + new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); + + // ½«»ñÈ¡µ½µÄcallback×éװΪһ¸öÊý×é + Callback[] mainCallbacks = new Callback[] { + // Óû§×Ô¼º¶¨ÒåµÄÀ¹½ØÆ÷ + aopInterceptor, // for normal advice + // ¸ù¾ÝÌõ¼þÊÇ·ñ±©Â¶´úÀí¶ÔÏóµÄÀ¹½ØÆ÷ + targetInterceptor, // invoke target without considering advice, if optimized + // ²»×öÈκβÙ×÷µÄÀ¹½ØÆ÷ + new SerializableNoOp(), // no override for methods mapped to this + // ÓÃÓÚ´æ´¢Advised¶ÔÏóµÄ·Ö·¢Æ÷ + targetDispatcher, this.advisedDispatcher, + // Õë¶Ôequals·½·¨µ÷ÓõÄÀ¹½ØÆ÷ + new EqualsInterceptor(this.advised), + // Õë¶Ôhashcode·½·¨µ÷ÓõÄÀ¹½ØÆ÷ + new HashCodeInterceptor(this.advised) + }; + + Callback[] callbacks; + + // Èç¹ûÄ¿±ê¶ÔÏóÊǾ²Ì¬µÄ£¬²¢ÇÒÇÐÃæÂß¼­µÄµ÷ÓÃÁ´Êǹ̶¨µÄ£¬Ôò¶ÔÄ¿±ê¶ÔÏóºÍÕû¸öµ÷ÓÃÁ´½øÐлº´æ + if (isStatic && isFrozen) { + Method[] methods = rootClass.getMethods(); + Callback[] fixedCallbacks = new Callback[methods.length]; + this.fixedInterceptorMap = new HashMap<>(methods.length); + + for (int x = 0; x < methods.length; x++) { + Method method = methods[x]; + // »ñȡĿ±ê¶ÔÏóµÄÇÐÃæÂß¼­ + List chain = this.advised + .getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); + fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( + chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); + // ¶Ôµ÷ÓÃÁ´½øÐлº´æ + this.fixedInterceptorMap.put(method, x); + } + + // ½«Éú³ÉµÄ¾²Ì¬µ÷ÓÃÁ´´æÈëCallbackÊý×éÖÐ + callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; + System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); + System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); + // ÕâÀïfixedInterceptorOffset¼Ç¼Á˵±Ç°¾²Ì¬µÄµ÷ÓÃÁ´µÄÇÐÃæÂß¼­µÄÆðʼλÖ㬠+ // ÕâÀï¼Ç¼µÄÓô¦ÔÚÓÚºóÃæÊ¹ÓÃCallbackFilterµÄʱºò£¬Èç¹û·¢ÏÖÊǾ²Ì¬µÄµ÷ÓÃÁ´£¬ + // ÔòÖ±½Óͨ¹ý¸Ã²ÎÊý»ñÈ¡ÏàÓ¦µÄµ÷ÓÃÁ´£¬¶øÖ±½ÓÂÔ¹ýÁËÇ°ÃæµÄ¶¯Ì¬µ÷ÓÃÁ´ + this.fixedInterceptorOffset = mainCallbacks.length; + } + else { + callbacks = mainCallbacks; + } + return callbacks; +} + +``` + +ÒÔÉÏ´úÂë±È½Ï³¤£¬Ö÷Òª×÷ÓþÍÊÇ»ñÈ¡ `callback`£¬ËäÈ» spring ÌṩÁËÖÚ¶à `callback`£¬µ«ÓëÎÒÃÇ×Ô¶¨Òå֪ͨÏà¹ØµÄ callback Ö»ÓÐÒ»¸ö£º `DynamicAdvisedInterceptor`£¬ÔÚÕâ¸ö `callback` µÄ `CglibAopProxy.DynamicAdvisedInterceptor#intercept` ·½·¨ÖУ¬ÎÒÃÇÔÚ´úÂëÖеÄ×Ô¶¨Í¨Öª¾ÍÊÇÔÚÕâÀïÖ´Ðеġ£ + +ÕâÒ»²½ÎÒÃǵõ½ cglib µÄ´úÀí¶ÔÏóºó£¬½ÓÏÂÀ´¾ÍÀ´¿´¿´ÇÐÃæ·½·¨ÊÇÈçºÎÖ´Ðеġ£ + +### 4\. cglib ÇÐÃæ·½·¨µÄÖ´ÐÐ + +cglib ÇÐÃæ·½·¨µÄÖ´ÐÐ `CglibAopProxy.DynamicAdvisedInterceptor#intercept` ·½·¨£º + +> `CglibAopProxy.DynamicAdvisedInterceptor#intercept` + +``` +public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) + throws Throwable { + Object oldProxy = null; + boolean setProxyContext = false; + Object target = null; + // ͨ¹ýTargetSource»ñȡĿ±ê¶ÔÏó + TargetSource targetSource = this.advised.getTargetSource(); + try { + // ÅжÏÈç¹ûÐèÒª±©Â¶´úÀí¶ÔÏó£¬Ôò½«µ±Ç°´úÀí¶ÔÏóÉèÖõ½ThreadLocalÖÐ + if (this.advised.exposeProxy) { + oldProxy = AopContext.setCurrentProxy(proxy); + setProxyContext = true; + } + target = targetSource.getTarget(); + Class targetClass = (target != null ? target.getClass() : null); + // »ñȡĿ±ê¶ÔÏóÇÐÃæÂß¼­µÄµ÷ÓÃÁ´ + List chain = this.advised + .getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); + Object retVal; + if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { + Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); + // ûÓÐÀ¹½ØÆ÷£¬Ö±½Óµ÷ÓÃÄ¿±ê¶ÔÏóµÄ·½·¨ + retVal = methodProxy.invoke(target, argsToUse); + } + else { + // ÕâÀïÓÐÁ½¸ö²½Ö裺 + // 1\. ´´½¨Ö´ÐÐÆ÷£ºnew CglibMethodInvocation() + // 2\. Ö´ÐÐÀ¹½ØÆ÷Á´£ºCglibMethodInvocation#proceed + retVal = new CglibMethodInvocation(proxy, target, method, args, + targetClass, chain, methodProxy).proceed(); + } + // ¶Ô·µ»ØÖµ½øÐд¦Àí£¬Èç¹û·µ»ØÖµ¾ÍÊǵ±Ç°Ä¿±ê¶ÔÏó£¬ÄÇô½«´úÀíÉú³ÉµÄ´úÀí¶ÔÏ󷵻أ» + // Èç¹û·µ»ØÖµÎª¿Õ£¬²¢ÇÒ·µ»ØÖµÀàÐÍÊÇ·ÇvoidµÄ»ù±¾Êý¾ÝÀàÐÍ£¬ÔòÅ׳öÒì³££» + // Èç¹ûÉÏÊöÁ½¸öÌõ¼þ¶¼²»·ûºÏ£¬ÔòÖ±½Ó½«Éú³ÉµÄ·µ»ØÖµ·µ»Ø + retVal = processReturnType(proxy, target, method, retVal); + return retVal; + } + finally { + if (target != null && !targetSource.isStatic()) { + targetSource.releaseTarget(target); + } + if (setProxyContext) { + AopContext.setCurrentProxy(oldProxy); + } + } +} + +``` + +×îÖÕÖ´ÐÐÊÇÔÚ `new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()`£¬¼ÌÐø¿´ÏÂÈ¥£º + +> CglibAopProxy.CglibMethodInvocation#proceed + +``` +public Object proceed() throws Throwable { + try { + return super.proceed(); + } + catch (...) { + .... + } +} + +``` + +Ö±½Óµ÷ÓõÄÊǸ¸ÀàµÄ·½·¨£¬`CglibAopProxy.CglibMethodInvocation` µÄ¸¸ÀàÊÇË­ÄØ£¿Ò»¿´ÏÂÈ¥£¬·¢ÏÖ¾¹È»ÊÇ `ReflectiveMethodInvocation`£¬`super.proceed()` µ÷ÓõÄÊÇ `ReflectiveMethodInvocation#proceed`£¡ + +ÔÚ[ÉÏÒ» ƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4696654 "ÉÏÒ» ƪÎÄÕÂ")ÖУ¬ÎÒÃǾÍÏêϸ·ÖÎöÁË `ReflectiveMethodInvocation#proceed` µÄµ÷Óùý³Ì£¬¶øÏÖÔÚ£¬ÔÚ cglib ´úÀíÀ×îÖÕÖ´ÐеÄÒ²ÊÇͬÑùµÄ´úÂ룬ÕâÒ»¿éµÄÖ´Ðйý³Ì¾Í²»Öظ´·ÖÎöÁË¡£ + +×îºóÓÃÒ»ÕÅͼÀ´ËµÃ÷ÇÐÃæÍ¨ÖªµÄÖ´Ðйý³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1c31c8e6279af4c150df18ebbd345c7f110.png) + +×îÖÕµÄÖ´ÐÐ˳Ðò£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a1f3ccebe3b7335eaef88b8a39ca36b9e05.png) + +### 5\. ×ܽá + +±¾ÎÄ·ÖÎöÁË cglib ´úÀíµÄÖ´Ðйý³Ì£¬ÇÐÃæµÄÖ´ÐÐÁ÷³ÌλÓÚ `CglibAopProxy.DynamicAdvisedInterceptor#intercept`£¬×îÖÕµ÷ÓõÄÊÇ `ReflectiveMethodInvocation#proceed`£¬ÕâÓë jdk ¶¯Ì¬´úÀíµÄÖ´ÐÐÁ÷³ÌÏàͬ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4696655](https://my.oschina.net/funcy/blog/4696655) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" new file mode 100644 index 0000000..041e629 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\205\255\357\274\211\357\274\232aop\346\200\273\347\273\223.md" @@ -0,0 +1,22 @@ +Ç°Ãæ¼¸ÆªÎÄÕÂÎÒÃÇ·ÖÎöÁË spring aop µÄÏà¹Ø´úÂ룬ÕâÀïÀ´×ö¸ö×ܽᡣ + +### 1\. spring ÆôÓà aop ¹¦ÄÜ + +ÔÚ [spring aop£¨Ò»£©£ºÊ¾Àý demo ¼° @EnableAspectJAutoProxy](https://my.oschina.net/funcy/blog/4678093 "spring aop£¨Ò»£©£ºÊ¾Àý demo ¼° @EnableAspectJAutoProxy") Ò»ÎÄÖУ¬ÎÒÃÇ·ÖÎöÁË spring ͨ¹ý `@EnableAspectJAutoProxy` ×¢½âÆôÓà aop ¹¦ÄÜ£¬¶øÕâ¸ö×¢½âʵ¼ÊÉÏÊÇÏò spring Öе¼ÈëÁË `AnnotationAwareAspectJAutoProxyCreator`£¬¶øÕâ¸öÀàÊÇÒ»¸ö `BeanPostProcessor`£¬×îÖÕÔÚ bean ³õʼ»¯Ç°ºóÍê³É´úÀí¶ÔÏóµÄÉú³É¡£ + +### 2\. ´úÀí¶ÔÏóµÄÉú³É + +spring aop »ùÓÚ×¢½â·½Ê½µÄʵÏÖÊÇͨ¹ý `AnnotationAwareAspectJAutoProxyCreator` ÀàÀ´²Ù×÷µÄ£¬Õâ¸öÀàÊÇÒ»¸ö `BeanPostProcessor`£¬ÔÚ bean µÄ³õʼ»¯Ç°ºóÖ´ÐеIJÙ×÷ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c634a0bda86d94cce68aaa46ac74f57d41d.png) + +### 3\. ÇÐÃæ·½·¨µÄÖ´ÐÐ + +µ±µ÷ÓôúÀí¶ÔÏóµÄ·½·¨£¬jdk »á¸ù¾Ý´úÀíµÄÀàÐͶøÑ¡ÔñÖ´ÐÐ `InvocationHandler#invoke`(jdk ¶¯Ì¬´úÀí) »¹ÊÇ `MethodInterceptor#intercept`(cglib ´úÀí)£¬ÕâÒ»²½ÔÚ´´½¨´úÀí¶ÔÏóʱ£¬ÒѾ­Óë´úÀí¶ÔÏó½áºÏÁË£¬¿ª·¢ÕßÎÞ·¨¸ÉÉæµ÷ÓÃÄĸö·½·¨£¬²»¹ýÕâÁ½¸ö·½·¨ÀïµÄÄÚÈÝ¿ª·¢Õß¿ÉÒÔ×ÔÓÉ·¢»Ó¡£ÔÚÕâÁ½¸ö·½·¨ÖУ¬spring »á»ñÈ¡¿ÉÓÃÓõ±Ç°·½·¨µÄËùÓÐ Advisors£¬È»ºóÖ´ÐÐ Advisors ÀïµÄÇÐÃæ·½·¨£¬Õû¸ö¹ý³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-96bb9ba4b77e60a85a1da1c2cec3858edf7.png) ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-013fb0c06e03fbe5044c211497df8ce306a.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4701587](https://my.oschina.net/funcy/blog/4701587) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ + diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" new file mode 100644 index 0000000..c2965a5 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/SpringAOP/SpringAop\357\274\210\345\233\233\357\274\211\357\274\232jdk\345\212\250\346\200\201\344\273\243\347\220\206.md" @@ -0,0 +1,742 @@ + + +ÉÏһƪÎÄÕµÄ×îºó£¬ÎÒÃÇ·ÖÎöµ½ spring ÖÕÓÚ´´½¨ÁË´úÀí¶ÔÏ󣬯äÖдúÀí¶ÔÏóµÄ·½Ê½Îª `jdk¶¯Ì¬´úÀí`Óë `cglib´úÀí`£¬±¾ÎÄÎÒÃǽ«·ÖÎö spring µÄ¶¯Ì¬´úÀí¡£ + +### 1. jdk ¶¯Ì¬´úÀí½éÉÜ + +À´·ÖÎö spring µÄ¶¯Ì¬´úÀíǰ£¬ÎÒÃÇÏÈÀ´Á˽âÏ jdk µÄ¶¯Ì¬´úÀí¡£jdk ¶¯Ì¬´úÀíÐèÒª½Ó¿Ú£¬Îª´ËÎÒÃÇÏÈ×¼±¸Á½¸ö½Ó¿Ú£º + +> IJdkDynamicProxy01 + +```java +package org.springframework.learn.demo03; + +public interface IJdkDynamicProxy01 { + void hello01(); +} +``` + +> IJdkDynamicProxy02 + +```java +package org.springframework.learn.demo03; + +public interface IJdkDynamicProxy02 { + void hello02(); +} +``` + +ÔÙÀ´×¼±¸Á½¸öʵÏÖÀࣺ + +> JdkDynamicProxyImpl01 + +```java +package org.springframework.learn.demo03; + +public class JdkDynamicProxyImpl01 implements IJdkDynamicProxy01, IJdkDynamicProxy02{ + @Override + public void hello01() { + System.out.println("hello01"); + } + + @Override + public void hello02() { + System.out.println("hello02"); + } +} +``` + +> JdkDynamicProxyImpl02 + +```java +package org.springframework.learn.demo03; + +public class JdkDynamicProxyImpl02 implements IJdkDynamicProxy01 { + + @Override + public void hello01() { + System.out.println("hello01"); + } + +} +``` + +ÕâÀïÐèҪעÒâµÄÊÇ£¬`JdkDynamicProxyImpl01` ʵÏÖÁË `IJdkDynamicProxy01` Óë `IJdkDynamicProxy02` Á½¸ö½Ó¿Ú£¬`JdkDynamicProxyImpl02` ֻʵÏÖÁË `IJdkDynamicProxy01` Ò»¸ö ½Ó¿Ú¡£ + +½Ó×Å×¼±¸Ò»¸ö `InvocationHandler`: + +> MyInvocationHandler + +```java +package org.springframework.learn.demo03; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class MyInvocationHandler implements InvocationHandler { + + /** Ä¿±ê¶ÔÏó */ + private Object target; + + public MyInvocationHandler(Object target){ + this.target = target; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("Ö´Ðз½·¨Îª:" + method.getName()); + // ·½·¨µÄÕæÕýÖ´ÐÐÔÚÕâÀï + Object rs = method.invoke(target,args); + return rs; + } + +} +``` + +×îºóÊÇÖ÷Àà: + +```java +package org.springframework.learn.demo03; + +import java.lang.reflect.Proxy; + +public class Demo03Main { + + public static void main(String[] args) { + System.out.println("------------bean01------------"); + JdkDynamicProxyImpl01 bean01 = new JdkDynamicProxyImpl01(); + Object obj1 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + // JdkDynamicProxyImpl01ʵÏÖÁË IJdkDynamicProxy01, IJdkDynamicProxy02 + // ´«ÈëµÄclassΪ IJdkDynamicProxy01, IJdkDynamicProxy02 + new Class[]{ IJdkDynamicProxy01.class, IJdkDynamicProxy02.class }, + new MyInvocationHandler(bean01)); + // ¿ÉÒÔ½øÐÐÀàÐÍÇ¿ÖÆ×ª»» + ((IJdkDynamicProxy01) obj1).hello01(); + ((IJdkDynamicProxy02) obj1).hello02(); + + System.out.println("------------bean01------------"); + Object obj2 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + // JdkDynamicProxyImpl01ʵÏÖÁË IJdkDynamicProxy01, IJdkDynamicProxy02 + // ´«ÈëµÄclassΪ IJdkDynamicProxy01 + new Class[]{ IJdkDynamicProxy01.class }, + new MyInvocationHandler(bean01)); + ((IJdkDynamicProxy01) obj2).hello01(); + // ±¨Òì³££ºjava.lang.ClassCastException: class com.sun.proxy.$Proxy1 cannot be cast to class xxx + //((IJdkDynamicProxy02) obj2).hello02(); + + System.out.println("-----------bean02-------------"); + JdkDynamicProxyImpl02 bean02 = new JdkDynamicProxyImpl02(); + Object obj3 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + // JdkDynamicProxyImpl01ʵÏÖÁË IJdkDynamicProxy01 + // ´«ÈëµÄclassΪ IJdkDynamicProxy01, IJdkDynamicProxy02 + new Class[]{ IJdkDynamicProxy01.class, IJdkDynamicProxy02.class }, + new MyInvocationHandler(bean02)); + ((IJdkDynamicProxy01) obj3).hello01(); + IJdkDynamicProxy02 proxy02 = (IJdkDynamicProxy02) obj3; + // ±¨Òì³££ºjava.lang.IllegalArgumentException: object is not an instance of declaring class + //proxy02.hello02(); + + } +} +``` + +ÔËÐнá¹û£º + +``` +Ö´Ðз½·¨Îª:hello01 +hello01 +Ö´Ðз½·¨Îª:hello02 +hello02 +------------bean01------------ +Ö´Ðз½·¨Îª:hello01 +hello01 +-----------bean02------------- +Ö´Ðз½·¨Îª:hello01 +hello01 +``` + +¶Ô½á¹û·ÖÎöÈçÏ£º + +1. `Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)` µÄµÚ¶þ¸ö²ÎÊý´«ÈëµÄ½Ó¿Ú£¬ÉùÃ÷µÄÊÇ´úÀí¶ÔÏóËùÊôµÄ½Ó¿ÚÀàÐÍ£¬µÚÈý¸ö²ÎÊýÊÇÖ´ÐÐÆ÷£¬´úÀíÀà¶ÔÏóµÄÖ´ÐÐΪÆä `invoke()` ·½·¨£» +2. `JdkDynamicProxyImpl01` ͬʱʵÏÖÁË `IJdkDynamicProxy01` Óë `IJdkDynamicProxy02` ½Ó¿Ú£¬µ«´«Èë½Ó¿ÚÀàÐÍʱ£¬Ö»´«ÈëÁË `IJdkDynamicProxy01`£¬µ±´úÀí¶ÔÏó obj2 ǿתΪ `IJdkDynamicProxy02` ʱ£¬¾Í»á±¨ `ClassCastException`£¬Ç¿×ªÊ§°Ü£¬Õâ±íÃ÷ `Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)` ÉùÃ÷µÄÊÇ´úÀí¶ÔÏóËùÊôµÄ½Ó¿ÚÀàÐÍ£» +3. `JdkDynamicProxyImpl02` ֻʵÏÖÁË `IJdkDynamicProxy01` ½Ó¿Ú£¬µ«´«Èë½Ó¿ÚÀàÐÍʱ£¬´«ÈëÁË `IJdkDynamicProxy01` Óë `IJdkDynamicProxy02`£¬µ±´úÀí¶ÔÏó `obj3` ǿתΪ `IJdkDynamicProxy02` ʱ£¬²¢Î´±¨Òì³££¬µ«ÊÇÔÚÖ´ÐÐ `proxy02.hello02()` ʱ£¬È´±¨ÁË `java.lang.IllegalArgumentException: object is not an instance of declaring class`£¬Í¬Ñù±íÃ÷ÁË `Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)` ÉùÃ÷µÄÊÇ´úÀí¶ÔÏóËùÊôµÄ½Ó¿ÚÀàÐÍ£¬¸úÄ¿±ê¶ÔÏóµÄÀàÐÍÎ޹ء£ + +### 2. ÔٴηÖÎö spring jdk ¶¯Ì¬´úÀí¶ÔÏóµÄ´´½¨ + +ÓÐÁËÉÏÃæµÄ·ÖÎö£¬ÎÒÃÇÔÙÀ´¿´¿´ spring ÊÇÈçºÎ´´½¨´úÀí¶ÔÏóµÄ£º + +```java +@Override +public Object getProxy(@Nullable ClassLoader classLoader) { + // »ñȡĿ±ê¶ÔÏóʵÏÖµÄ½Ó¿Ú + Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); + // ÊÇ·ñÓÐequals()ÓëhashCode()·½·¨ + findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); + // µ÷Óà jdk ·½·¨ ´´½¨¶ÔÏó + return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); +} +``` + +1. ´«ÈëµÄ½Ó¿ÚΪ `proxiedInterfaces`£¬Õâ¸öÖµ°üº¬ÁËÄ¿±êÀàʵÏÖµÄËùÓнӿڣ¬Í¬Ê± spring Ò²»áÌí¼Ó×ÔÉíµÄ½Ó¿Ú£¬Èç `SpringProxy`¡¢`Advised`£¬ÕâЩÔÚÉÏһƪÎÄÕÂÒѾ­Ïêϸ·ÖÎö¹ýÁË£» +2. Ö¸¶¨ `InvocationHandler` Ϊ `this`£¬Ò²¾ÍÊÇ `JdkDynamicAopProxy` µÄ¶ÔÏó£¬Êµ¼ÊÉÏ `JdkDynamicAopProxy` ʵÏÖÁË `InvocationHandler`. + +ÓɵÚÒ»²¿·ÖµÄ·ÖÎö¿ÉÖª£¬jdk ¶¯Ì¬´úÀí¶ÔÏóµÄ·½·¨×îÖÕÊÇÔÚ `java.lang.reflect.InvocationHandler#invoke` ÖÐÖ´Ðеģ¬Ò²¾ÍÊÇ `JdkDynamicAopProxy#invoke`£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎö `JdkDynamicAopProxy#invoke` ·½·¨£¬À´¿´¿´ spring ÊÇÈçºÎÖ´ÐдúÀí·½·¨µÄ¡£ + +### 3. jdk ¶¯Ì¬´úÀí·½·¨µÄÖ´ÐÐ + +spring jdk ¶¯Ì¬´úÀí·½·¨µÄÖ´ÐÐÔÚ `JdkDynamicAopProxy#invoke`£º + +> JdkDynamicAopProxy#invoke + +```java +public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object oldProxy = null; + boolean setProxyContext = false; + + TargetSource targetSource = this.advised.targetSource; + Object target = null; + + try { + // ÈôÖ´ÐÐµÄ equals ·½·¨£¬²»ÐèÒª´úÀíÖ´ÐÐ + if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { + return equals(args[0]); + } + // ÈôÖ´ÐÐµÄ hashCode ·½·¨£¬²»ÐèÒª´úÀíÖ´ÐÐ + else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { + return hashCode(); + } + // Èç¹ûÖ´ÐеÄclass¶ÔÏóÊÇDecoratingProxy£¬Ò²²»ÐèÒª´úÀíÖ´ÐÐ + else if (method.getDeclaringClass() == DecoratingProxy.class) { + return AopProxyUtils.ultimateTargetClass(this.advised); + } + else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && + method.getDeclaringClass().isAssignableFrom(Advised.class)) { + return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); + } + + Object retVal; + + // ÅÐ¶Ï advisedµÄexposeProxy ÖµÊÇ·ñΪ true + // advisedµÄexposeProxyÀ´Ô´ÓÚ @EnableAspectJAutoProxy µÄ exposeProxy + // ¼´ ÏñÕâÑùÖ¸¶¨Ê±£¬@EnableAspectJAutoProxy(exposeProxy = true)£¬ÒÔÏ´úÂëÖ´ÐÐ + if (this.advised.exposeProxy) { + // ½«µ±Ç°µÄ proxy ¶ÔÏó·Åµ½ threadLocal ÖÐ + // ºóÐø¿ÉÒÔ (UserService (AopContext.currentProxy)).getUser() ·½Ê½µ÷Óà + oldProxy = AopContext.setCurrentProxy(proxy); + setProxyContext = true; + } + + // »ñȡĿ±ê¶ÔÏó¼°Ä¿±ê¶ÔÏóµÄclass + target = targetSource.getTarget(); + Class targetClass = (target != null ? target.getClass() : null); + + // ½« aop µÄ advisor ת»¯ÎªÀ¹½ØÆ÷£¬ÔÚÕâÀïÅжϸ÷½·¨¿ÉÒÔʹÓÃÄÄЩÇÐÃæ·½·¨ + List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice( + method, targetClass); + if (chain.isEmpty()) { + // Èç¹û¼ÓÈëµÄÀ¹½ØÆ÷Á´Îª¿Õ£¬±íÃ÷¸Ã·½·¨Ã»Óб»À¹½Ø£¬Í¨¹ý·´ÉäÖ±½ÓÖ´ÐÐ + Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); + retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); + } + else { + // ´´½¨Ò»¸ö·½·¨µ÷ÓöÔÏó + MethodInvocation invocation = + new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); + // µ÷ÓÃÖ´ÐУ¬Öصã + retVal = invocation.proceed(); + } + + Class returnType = method.getReturnType(); + if (retVal != null && retVal == target && + returnType != Object.class && returnType.isInstance(proxy) && + !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { + retVal = proxy; + } + else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { + throw new AopInvocationException(...); + } + return retVal; + } + finally { + if (target != null && !targetSource.isStatic()) { + targetSource.releaseTarget(target); + } + if (setProxyContext) { + AopContext.setCurrentProxy(oldProxy); + } + } +} +``` + +ÒÔÉÏ·½·¨Á÷³ÌÈçÏ£º + +1. ÅжÏÒªÖ´Ðеķ½·¨ÊÇ·ñΪ `equals`¡¢`hashcode` µÈ£¬ÕâЩ·½·¨²»ÐèÒª´úÀí£» +2. »ñÈ¡¿ÉÓÃÓÚÒªÖ´Ðеķ½·¨µÄËùÓÐÇÐÃæ·½·¨£¬µÃµ½Ò»¸öÀ¹½ØÆ÷¼¯ºÏ£» +3. µ÷ÓÃÇÐÃæ·½·¨¼¯ºÏÓë¼°Ä¿±ê·½·¨¡£ + +ÕâÀïÎÒÃÇÖØµã¹Ø×¢ÇÐÃæ·½·¨ÓëÄ¿±ê·½·¨µÄÖ´ÐУ¬¹Ø¼ü´úÂëÈçÏ£º + +```java +// ½« aop µÄ advisor ת»¯ÎªÀ¹½ØÆ÷£¬ÔÚÕâÀïÅжϸ÷½·¨¿ÉÒÔʹÓÃÄÄЩÇÐÃæ·½·¨ +List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice( + method, targetClass); +// ´´½¨Ò»¸ö·½·¨µ÷ÓöÔÏó +MethodInvocation invocation = + new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); +// µ÷ÓÃÖ´ÐУ¬Öصã +retVal = invocation.proceed(); +``` + +#### »ñÈ¡ `MethodInterceptor` + +ÔÚ·ÖÎö·½·¨µÄÖ´ÐÐǰ£¬ÎÒÃÇÏÈÀ´¿´¿´ `getInterceptorsAndDynamicInterceptionAdvice(...)`£¬Õâ¸ö·½·¨ÊÇÓÃÀ´»ñȡִÐеÄÇÐÃæ·½·¨µÄ£¬Ò²¾ÍÊÇ `MethodInterceptor`£º + +> AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice + +```java +public List getInterceptorsAndDynamicInterceptionAdvice(Method method, + @Nullable ClasstargetClass) { + MethodCacheKey cacheKey = new MethodCacheKey(method); + List cached = this.methodCache.get(cacheKey); + if (cached == null) { + // ÔÚÕâ¸ö·½·¨Àï¼ÌÐø»ñÈ¡£¬ÏÂÃæ»áÔÙ·ÖÎö + cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( + this, method, targetClass); + this.methodCache.put(cacheKey, cached); + } + return cached; +} +``` + +¼ÌÐø£¬ + +```java +/** + * »ñÈ¡ Interceptor£¬¹ý³ÌÈçÏ£º + */ +@Override +public List getInterceptorsAndDynamicInterceptionAdvice( + Advised config, Method method, @Nullable Class targetClass) { + AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); + // »ñÈ¡ advisors£¬aopµÄadvisorsÈçÏ£º + Advisor[] advisors = config.getAdvisors(); + List interceptorList = new ArrayList<>(advisors.length); + Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); + Boolean hasIntroductions = null; + for (Advisor advisor : advisors) { + // Èç¹ûadvisorÊÇPointcutAdvisor£¬ÔòʹÓÃPointcutAdvisorÀïµÄPointcut½øÐÐÆ¥Åä + if (advisor instanceof PointcutAdvisor) { + PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; + // ÕâÀïÅжÏÇÐÃæÂß¼­µÄµ÷ÓÃÁ´ÊÇ·ñÌáǰ½øÐйý¹ýÂË£¬Èç¹û½øÐйý£¬Ôò²»ÔÙ½øÐÐÄ¿±ê·½·¨µÄÆ¥Å䣬 + // Èç¹ûûÓУ¬ÔòÔÙ½øÐÐÒ»´ÎÆ¥Åä¡£ + if (config.isPreFiltered() + || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { + MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); + boolean match; + if (mm instanceof IntroductionAwareMethodMatcher) { + if (hasIntroductions == null) { + hasIntroductions = hasMatchingIntroductions(advisors, actualClass); + } + match = ((IntroductionAwareMethodMatcher) mm) + .matches(method, actualClass, hasIntroductions); + } + else { + match = mm.matches(method, actualClass); + } + if (match) { + // ½«Advisor¶ÔÏóת»»ÎªMethodInterceptorÊý×é + MethodInterceptor[] interceptors = registry.getInterceptors(advisor); + if (mm.isRuntime()) { + for (MethodInterceptor interceptor : interceptors) { + // ½« interceptorÓëmethodMatcher°ü×°³ÉInterceptorAndDynamicMethodMatcher + interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); + } + } + else { + interceptorList.addAll(Arrays.asList(interceptors)); + } + } + } + } + else if (advisor instanceof IntroductionAdvisor) { + // ÅжÏÈç¹ûΪIntroductionAdvisorÀàÐ͵ÄAdvisor£¬Ôò½«µ÷ÓÃÁ´·âװΪInterceptorÊý×é + IntroductionAdvisor ia = (IntroductionAdvisor) advisor; + if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { + Interceptor[] interceptors = registry.getInterceptors(advisor); + interceptorList.addAll(Arrays.asList(interceptors)); + } + } + else { + // ÕâÀïÊÇÌṩµÄʹÓÃ×Ô¶¨ÒåµÄת»»Æ÷¶ÔAdvisor½øÐÐת»»µÄÂß¼­£¬ÒòΪgetInterceptors()·½·¨ÖÐ + // »áʹÓÃÏàÓ¦µÄAdapter¶ÔÄ¿±êAdvisor½øÐÐÆ¥Å䣬Èç¹ûÄÜÆ¥ÅäÉÏ£¬Í¨¹ýÆägetInterceptor()·½·¨ + // ½«×Ô¶¨ÒåµÄAdviceת»»ÎªMethodInterceptor¶ÔÏó + Interceptor[] interceptors = registry.getInterceptors(advisor); + interceptorList.addAll(Arrays.asList(interceptors)); + } + } + return interceptorList; +} +``` + +ÎÒÃǸù¾ÝÒÔÉÏ·½·¨À´×ܽáÏ»ñÈ¡ `MethodInterceptor` µÄ¹ý³Ì¹ý³ÌÈçÏ£º + +1. »ñÈ¡ÏîÄ¿ÖÐËùÓÐµÄ `advisors` + +2. ±éÀú£¬¶Ôÿ¸ö + + + + ``` + advisor + ``` + + + +¼ÌÐø°´ÒÔÏÂÁ÷³Ì´¦Àí£º + +1. Èç¹û `advisor` ÊÇ `PointcutAdvisor`£¬ÔòʹÓÃÆäÖÐµÄ `Pointcut` ½øÐÐÆ¥Å䣬ƥÅä³É¹¦ºó£¬»ñÈ¡ `MethodInterceptor` ·µ»Ø£» +2. Èç¹û `advisor` ÊÇ `IntroductionAdvisor`£¬ÔòʹÓÃÆäÖÐµÄ `ClassFilter` ½øÐÐÆ¥Å䣬ƥÅä³É¹¦ºó£¬»ñÈ¡ `MethodInterceptor` ·µ»Ø£» +3. Èç¹ûÒÔÉÏÌõ¼þ²»Âú×㣬ֱ½Ó»ñÈ¡ `MethodInterceptor` ·µ»Ø£» + +ÄÇô `MethodInterceptor` ÊÇÈçºÎ»ñÈ¡µÄÄØ£¿ÎÒÃǼÌÐøÍùÏ¿´£º + +```java +// ´æ·Å AdvisorAdapter µÄµØ·½ +private final List adapters = new ArrayList<>(3); + +// Ìí¼Ó adapter +public DefaultAdvisorAdapterRegistry() { + // @Before + registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); + // @AfterReturning + registerAdvisorAdapter(new AfterReturningAdviceAdapter()); + // @AfterThrowing + registerAdvisorAdapter(new ThrowsAdviceAdapter()); +} + +/** + * »ñÈ¡advisor¶ÔÓ¦µÄMethodInterceptor + */ +@Override +public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { + List interceptors = new ArrayList<>(3); + // »ñÈ¡µ±Ç°advisorÀïµÄMethodInterceptor + Advice advice = advisor.getAdvice(); + // Èç¹û advice ÊÇ MethodInterceptorµÄʵÀý£¬Ìí¼Ó + if (advice instanceof MethodInterceptor) { + interceptors.add((MethodInterceptor) advice); + } + // + // ʹÓà AdvisorAdapter ½« advice ת»»Îª MethodInterceptor + // Èç¹ûadviceÂú×ãadapter£¬µ÷Óà adapter.getInterceptor »ñÈ¡ MethodInterceptor + for (AdvisorAdapter adapter : this.adapters) { + if (adapter.supportsAdvice(advice)) { + interceptors.add(adapter.getInterceptor(advisor)); + } + } + if (interceptors.isEmpty()) { + throw new UnknownAdviceTypeException(advisor.getAdvice()); + } + return interceptors.toArray(new MethodInterceptor[0]); +} +``` + +¶ÔÕâ¸ö·½·¨µÄÁ÷³Ì×ܽáÈçÏ£º + +1. Èç¹û `advice` ÊÇ `MethodInterceptor`£¬Ö±½Ó½«Æäת»»³É `MethodInterceptor`£» +2. Èç¹ûÒÔÉϲ»Âú×㣬ÔòʹÓà `AdvisorAdapter` ½« advice ת»»³É `MethodInterceptor`. + +¹ØÓÚ `adapters`£¬spring ΪÌṩÁËÈý¸ö `Adapter`£º + +- MethodBeforeAdviceAdapter£º´¦Àí `@Before` +- AfterReturningAdviceAdapter£º´¦Àí `@AfterReturning` +- ThrowsAdviceAdapter£º´¦Àí `@AfterThrowing` + +ÕâÈý¸ö `Adapter` ¾ÍÖ»ÓÐÒ»¸ö¹¦ÄÜ£º·µ»Ø `advice` ¶ÔÓ¦µÄ `MethodInterceptor`£¬ÎÒÃÇÀ´¿´Ï `MethodBeforeAdviceAdapter` µÄ´úÂë¾ÍÃ÷°×ÁË£º + +```java +class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { + /** + * ÊÇ·ñÄÜ´¦Àíµ±Ç°advice + */ + @Override + public boolean supportsAdvice(Advice advice) { + return (advice instanceof MethodBeforeAdvice); + } + + /** + * ·µ»Ø¶ÔÓ¦µÄMethodInterceptor + */ + @Override + public MethodInterceptor getInterceptor(Advisor advisor) { + MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); + return new MethodBeforeAdviceInterceptor(advice); + } +} +``` + +ÆäËûÁ½¸ö `Adapter` µÄ¹¦Äܼ«ÆäÏàËÆ£¬¾Í²»·ÖÎöÁË£¬ÕâÀï×ܽáϸ÷×¢½â¶ÔÓ¦µÄ `advice`¡¢`methodInterceptor`£º + +| ×¢½â | advice | methodInterceptor | +| --------------- | --------------------------- | ------------------------------- | +| @Before | AspectJMethodBeforeAdvice | MethodBeforeAdviceInterceptor | +| @After | AspectJAfterAdvice | AspectJAfterAdvice | +| @Around | AspectJAroundAdvice | AspectJAroundAdvice | +| @AfterReturning | AspectJAfterReturningAdvice | AfterReturningAdviceInterceptor | +| @AfterThrowing | AspectJAfterThrowingAdvice | ThrowsAdviceInterceptor | + +#### ReflectiveMethodInvocation#proceed + +»ñÈ¡Íê `MethodInterceptor` ºó£¬¾Í¿ªÊ¼½øÐз½·¨µÄÖ´ÐÐÁË£¬ÎÒÃÇÖ±½Ó½øÈë `ReflectiveMethodInvocation#proceed` ·½·¨£º + +```java +public Object proceed() throws Throwable { + // Ö´ÐÐÍêËùÓеÄÔöÇ¿ºóÖ´ÐÐÄ¿±ê·½·¨ + // ÕâÀïʹÓÃÁËÔðÈÎÁ´Ä£Ê½£¬Õâ¸ö·½·¨ÔÚÔðÈÎÁ´Öе÷Óã¬Âú×ãÌõ¼þ±íʾµ±Ç°ÔðÈÎÁ´ÒѾ­Ö´Ðе½×îºóÁË + if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { + return invokeJoinpoint(); + } + + // »ñÈ¡ÏÂÒ»¸öÒªÖ´ÐеÄÀ¹½ØÆ÷ + Object interceptorOrInterceptionAdvice = + this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); + if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { + InterceptorAndDynamicMethodMatcher dm = + (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; + Class targetClass = (this.targetClass != null + ? this.targetClass : this.method.getDeclaringClass()); + if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { + // Æ¥Å䣬¾Íµ÷ÓÃÀ¹½ØÆ÷µÄ·½·¨£¬Ò²¾ÍÊÇÇÐÃæ·½·¨ + // ÔÚ MethodInterceptor#invokeÀ»áÔÙ´Î ReflectiveMethodInvocation#proceed£¬ÓÖµ÷»ØÁ˵±Ç°·½·¨ + return dm.interceptor.invoke(this); + } + else { + // ²»Æ¥Å䣬ÔòµÝ¹éµ÷Óõ±Ç°·½·¨ + return proceed(); + } + } + else { + // ×¢Ò⣬Õâ¸ö·½·¨´«ÈëµÄ²ÎÊýÊÇ this£¬±íʾµ±Ç°¶ÔÏó + // ÔÚ MethodInterceptor#invokeÀ»áÔÙ´Î ReflectiveMethodInvocation#proceed£¬ÓÖµ÷»ØÁ˵±Ç°·½·¨ + return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); + } +} + +/** + * µ÷ÓÃÄ¿±ê·½·¨ + */ +protected Object invokeJoinpoint() throws Throwable { + // ʹÓ÷´Éäµ÷ÓÃÄ¿±ê¶ÔÏó·½·¨£¬×¢ÒâÕâÀï´«ÈëµÄÓ¦¸ÃÊÇÄ¿±ê¶ÔÏ󣬶ø²»ÊÇ´úÀí¶ÔÏó + return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments); +} +``` + +ÒÔÉÏ´úÂëµÄµ÷ÓÃʹÓÃÁËÔðÈÎÁ´Ä£Ê½£¬Ö´ÐÐÂß¼­ÈçÏ£º + +1. ÅжÏÊÇ·ñÖ´ÐÐÍêËùÓеÄÇÐÃæ·½·¨£¬ÈôÊÇ£¬ÔòÖ´ÐÐÄ¿±ê·½·¨£¬·ñÔòÖ´ÐÐÏÂÒ»²½£» +2. »ñÈ¡ÏÂÒ»¸öÀ¹½ØÆ÷£¬ÅжÏÄÜ·ñÖ´ÐУ¬ÈôÄÜ£¬Ôòµ÷ÓÃÀ¹½ØÆ÷·½·¨£¬·ñÔòÖ´ÐÐµÚ Ò»²½²Ù×÷£» + +ÒÔÉÏÂß¼­¿´×Åͦ¼òµ¥£¬µ«¾ßÌåÊÇÔõôִÐеÄÄØ£¿ÔÚ spring ÖУ¬ÇÐÃæÍ¨ÖªÓÐÎåÖÖÀàÐÍ£º`@Before`¡¢`@After`¡¢`@AfterReturning`¡¢`@AfterThrowing` Óë `@Around`£¬ÕâÀïÎÒÃÇÒ»Ò»À´¿´¿´ÕâÎåÖÖ֪ͨÊÇÈçºÎµ÷Óõġ£ + +#### 1. `@Before` + +> MethodBeforeAdviceInterceptor#invoke + +```java +@Override +public Object invoke(MethodInvocation mi) throws Throwable { + // Ö´ÐÐǰÖÃ֪ͨ£¬¿´ÏÂÃæ + this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); + // ¼ÌÐøÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷ + return mi.proceed(); +} +``` + +½øÈë `advice.before(xxx)` ·½·¨£º + +> AspectJMethodBeforeAdvice#before + +```java +@Override +public void before(Method method, Object[] args, @Nullable Object target) throws Throwable { + invokeAdviceMethod(getJoinPointMatch(), null, null); +} +``` + +¼ÌÐø¸úÏÂÈ¥£º + +> AbstractAspectJAdvice#invokeAdviceMethod(JoinPointMatch, Object, Throwable) + +```java +protected Object invokeAdviceMethod( + @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) + throws Throwable { + + return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); +} + +/** + * µ÷Ó÷´ÉäÖ´ÐÐ + */ +protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { + Object[] actualArgs = args; + if (this.aspectJAdviceMethod.getParameterCount() == 0) { + actualArgs = null; + } + try { + // ÊìϤµÄjdk·´Éä´úÂë + ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); + return this.aspectJAdviceMethod.invoke( + this.aspectInstanceFactory.getAspectInstance(), actualArgs); + } + catch (...) { + ... +} +``` + +¿ÉÒÔ¿´µ½£¬×îÖÕÊǵ÷Óà jdk ·´ÉäÀ´µ÷Óõġ£ + +#### 2. `@After` + +> AspectJAfterAdvice#invoke + +```java +@Override +public Object invoke(MethodInvocation mi) throws Throwable { + try { + // ¼ÌÐøÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷ + return mi.proceed(); + } + finally { + // µ÷ÓÃÇÐÃæ·½·¨£º·ÅÔÚ finally ¿é£¬±íʾһ¶¨»áÖ´ÐУ¬×îÖÕÒ²ÊÇʹÓ÷´Éäµ÷Óà + invokeAdviceMethod(getJoinPointMatch(), null, null); + } +} +``` + +#### 3. `@AfterReturning` + +> AfterReturningAdviceInterceptor#invoke + +```java +@Override +public Object invoke(MethodInvocation mi) throws Throwable { + // ¼ÌÐøÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷ + Object retVal = mi.proceed(); + // µ÷ÓÃÇÐÃæ·½·¨£¬¼ÌÐøÍùÏ¿´ + this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); + return retVal; +} +``` + +> AspectJAfterReturningAdvice#afterReturning + +```java +public void afterReturning(@Nullable Object returnValue, Method method, + Object[] args, @Nullable Object target) throws Throwable { + if (shouldInvokeOnReturnValueOf(method, returnValue)) { + // µ÷ÓÃÇÐÃæ·½·¨£¬ÒÀÈ»Êǵ÷Ó÷´ÉäÖ´ÐÐ + invokeAdviceMethod(getJoinPointMatch(), returnValue, null); + } +} +``` + +#### 4. `@AfterThrowing` + +> AspectJAfterThrowingAdvice#invoke + +```java +@Override +public Object invoke(MethodInvocation mi) throws Throwable { + try { + // µ÷Óà ReflectiveMethodInvocation#proceed + return mi.proceed(); + } + catch (Throwable ex) { + if (shouldInvokeOnThrowing(ex)) { + // µ÷ÓÃÇÐÃæ·½·¨£¬Ö»ÓÐÔÚÅ׳öÒ쳣ʱ²Å»á±»µ÷Óà + invokeAdviceMethod(getJoinPointMatch(), null, ex); + } + throw ex; + } +} +``` + +#### 5. `@Around` + +> AspectJAroundAdvice#invoke + +```java +@Override +public Object invoke(MethodInvocation mi) throws Throwable { + if (!(mi instanceof ProxyMethodInvocation)) { + throw new IllegalStateException(...); + } + ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; + ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); + JoinPointMatch jpm = getJoinPointMatch(pmi); + // µ÷ÓÃÇÐÃæ·½·¨ + return invokeAdviceMethod(pjp, jpm, null, null); +} +``` + +ÎÒÃÇÔÚʵÏÖ»·ÈÆÍ¨ÖªÊ±£¬Ò»°ãÏñÕâÑùʵÏÖ£º + +```java +@Around(xxx) +public Object around(ProceedingJoinPoint p){ + // Ö´ÐÐÄ¿±ê·½·¨Ç°µÄ²Ù×÷ + ... + + // Ö´ÐÐÄ¿±ê·½·¨£¬ÕâÒ»¾äÊǹؼü + // ʵ¼ÊÕâÀï²¢²»½ö½öÊÇÖ´ÐÐÄ¿±ê·½·¨£¬×îÖÕµ÷ÓõÄÊÇ ReflectiveMethodInvocation#proceed + // ¼´Èç¹ûÓÐÀ¹½ØÆ÷£¬Ôò¼ÌÐøÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£¬·ñÔòÖ´ÐÐÄ¿±ê·½·¨ + Object o = p.proceed(); + + // Ö´ÐÐÄ¿±ê·½·¨ºóµÄ²Ù×÷ + ... + return o; +} +``` + +spring ¶ÔÒÔÉÏÎåÖÖ֪ͨµÄÖ´ÐУ¬Ö÷Òª·ÖΪÁ½¸ö²¿·Ö£º + +1. ʹÓ÷´É䷽ʽִÐÐÇÐÃæ·½·¨£¬Ò²¾ÍÊÇËùνµÄ ¡°ÔöÇ¿¡±£» +2. µ÷Óà `ReflectiveMethodInvocation#proceed` ¼ÌÐøÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£¬»òÖ´ÐÐÄ¿±ê·½·¨¡£ + +ÒÔÉÏÁ½¸ö²¿·ÖµÄ²î±ð½öÔÚÓÚÖ´ÐдÎÐò¼°Ö´ÐеÄλÖã¬Èç + +- `@Before` ֪ͨ£¬`1` ÔÚǰ£¬`2` ÔÚºó£» +- `@AfterReturning` ֪ͨ£¬`2` ÔÚǰ£¬`1` ÔÚºó£¬Èç¹ûÖ´ÐÐ `2` ʱ·¢ÉúÁËÒì³££¬`1` ¾Í²»»áÖ´ÐÐÁË£» +- `@AfterThrowing` ֪ͨ£¬`2` ÔÚǰ£¬`1` ÔÚºó£¬ÇÒ `1` ÊÇ·ÅÔÚ `catch` ¿éÖÐÖ´ÐУ¬¼´Ö»Óз¢ÉúÁËÒì³££¬`1` ²Å»áÖ´ÐУ» +- `@After` ֪ͨ£¬`2` ÔÚǰ£¬`1` ÔÚºó£¬`1` ÊÇ·ÅÔÚ `finally` ¿éÖÐÖ´ÐУ¬ÓÉÓÚ `finally` µÄÌØÊâÐÔ£¬¼´Ê¹·¢ÉúÁËÒì³££¬`1` ͬÑù»áÖ´ÐУ» +- `@Around` ֪ͨ£¬ÔÚÇÐÃæ·½·¨ÖÐ×ÔÐÐÖ¸¶¨ÁË `2` µÄÖ´ÐÐʱ»ú¡£ + +×¢ÒâÇø±ð `@AfterReturning`¡¢`@AfterThrowing` Óë `@After` ֪ͨµÄÖ´ÐÐʱ»ú¡£ + +×îºó£¬ÎÒÃÇÔÙÀ´¿´¿´Õ⼸¸ö֪ͨÊÇÈçºÎÖ´Ðеġ£ + +ͨ¹ýµ÷ÊԵķ½Ê½£¬·¢ÏÖ spring Ö´ÐÐ֪ͨµÄ˳ÐòÈçÏ£º + +1. Ê×ÏÈÖ´ÐÐ `@AfterThrowing` ֪ͨ£¬Ïȵ÷Óà `mi.proceed()` Ö´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£¬È»ºóÔÚ `catch` ¿éÖÐÖ´ÐÐÇÐÃæ·½·¨£¬±íÃ÷Ö»ÓгöÏÖÒ쳣ʱ£¬¸ÃÇÐÃæ·½·¨²Å»áÖ´ÐУ» +2. ÉÏÒ»²½²Ù×÷ÖУ¬µ÷Óà `mi.proceed()` ʱ»áÖ´ÐÐ `@AfterReturning` ֪ͨ£¬Ö´ÐÐʱÏȵ÷Óà `mi.proceed()` Ö´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£¬È»ºóÔÙÖ´ÐÐÇÐÃæ·½·¨£» +3. ÉÏÒ»²½²Ù×÷ÖУ¬µ÷Óà `mi.proceed()` ʱ»áÖ´ÐÐ `@After` ֪ͨ£¬Ö´ÐÐʱÏȵ÷Óà `mi.proceed()` Ö´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£¬È»ºóÔÚ `finally` ¿éÖÐÖ´ÐÐÇÐÃæ·½·¨£¬±íÃ÷¼´Ê¹³öÏÖÒì³££¬ÇÐÃæ·½·¨»¹ÊÇ»áÖ´ÐУ» +4. ÉÏÒ»²½²Ù×÷ÖУ¬µ÷Óà `mi.proceed()` ʱ»áÖ´ÐÐ `@Around` ֪ͨ£¬Ö´ÐÐʱֱ½ÓÖ´ÐÐÇÐÃæ·½·¨£¬ÓÉÓÚ `@Around` ֪ͨµÄÇÐÃæ·½·¨Àï»áµ÷Óà `ProceedingJoinPoint#proceed()`£¬×îÖÕ»¹ÊÇ»áÖ´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£» +5. ÉÏÒ»²½²Ù×÷ÖУ¬µ÷Óà `mi.proceed()` ʱ»áÖ´ÐÐ `@Before` ֪ͨ£¬Ö´ÐÐʱ»áÏÈÖ´ÐÐÇÐÃæ·½·¨£¬ÔÙµ÷Óà `mi.proceed()` Ö´ÐÐÏÂÒ»¸öÀ¹½ØÆ÷£» +6. À¹½ØÆ÷Á´Ö´Ðе½×îºó£¬·¢ÏÖûÓпÉÖ´ÐеÄÀ¹½ØÆ÷ÁË£¬´Ëʱ¾Í¿ªÊ¼Ö´ÐÐÄ¿±ê·½·¨¡£ + +»­Í¼Ê¾Òâ֪ͨµÄÖ´Ðйý³ÌÈçÏ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e88e0cec49c47648005e5d3160663425739.png) + +×îÖÕµÄÖ´ÐÐ˳Ðò£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-97026239d0b2dc02abbe87a9b76325c3cc0.png) + +### 4. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË jdk ¶¯Ì¬´úÀíµÄÖ´Ðйý³Ì£¬ÊáÀíÁ˸÷¸öÇÐÃæÍ¨ÖªµÄÖ´ÐÐ˳Ðò¡£±¾ÎľÍÏȵ½ÕâÀïÁË£¬ÏÂһƪÎÄÕ½«½éÉÜ cglib µÄÖ´Ðйý³Ì¡£ + +------ + +*±¾ÎÄÔ­ÎÄÁ´½Ó£ºhttps://my.oschina.net/funcy/blog/4696654 £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£* \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" new file mode 100644 index 0000000..d46a99e --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\200\357\274\211\357\274\232\350\256\244\350\257\206\344\272\213\345\212\241\347\273\204\344\273\266.md" @@ -0,0 +1,825 @@ +Ç°Ãæ·ÖÎöÍêÁË spring aop Ïà¹Ø¹¦Äܺ󣬱¾ÎĽ«À´·ÖÎö spring aop µÄÒ»¸öÓ¦Óà ¡ª¡ª ÊÂÎñ¹ÜÀí¡£ + +### 1\. ´ÓÁ½¸ö demo ½²Æð + +ÔÚÕýʽ·ÖÎöǰ£¬ÎÒÃÇÏÈÀ´Ë¼¿¼Ï£¬Èç¹ûÈÃÎÒÃÇ×Ô¼ºÀ´»ùÓÚ spring aop À´Éè¼ÆÒ»Ì×ÊÂÎñ´¦Àí»úÖÆ£¬¸ÃÈçºÎʵÏÖÄØ£¿Èç¹ûûÓÐ spring£¬ÎÒÃǵÄÊÂÎñ´¦Àí´úÂëÒ»°ã³¤ÕâÑù£º + +``` +public void fun() { + // ¿ªÆôÊÂÎñ + start(); + try { + // ÒµÎñ´¦Àí + xxx(); + // Ìá½»ÊÂÎñ + commit(); + } catch(Exception e) { + // »Ø¹öÊÂÎñ + rollback(); + throw e; + } +} + +``` + +´ÓÉÏÃæµÄ´úÂëÀ´¿´£¬Ïñ¿ªÆôÊÂÎñ¡¢Ìá½»ÊÂÎñ¡¢»Ø¹öÊÂÎñ£¬¶¼¸úÒµÎñ´úÂëÎ޹أ¬ÕâЩ¿ÉÒÔʹÓà spring aop À´ÊµÏÖ£¬Òò´Ë¾ÍÓÐÁËÏÂÃæÁ½¸ö demo. + +#### demo01£º»ùÓÚ `@Around` ×¢½âʵÏÖÊÂÎñ + +ÔÛÃÇ¿ÉÒÔʹÓà `@Around` ×¢½âÀ´²Ù×÷£¬´úÂëÈçÏ£º + +1. ¶¨ÒåÒ»¸ö×¢½â£º`@MyTransactional` + +``` +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface MyTransactional { +} + +``` + +1. ¶¨Òå aop ²Ù×÷ + +``` +@Aspect +@Component +public class MyAopAspectj { + @Pointcut("@annotation(org.springframework.learn.tx.demo02.MyTransactional)") + public void testAop(){ + + } + + @Around("testAop()") + public Object around(ProceedingJoinPoint p) throws Throwable { + System.out.println("Ö´ÐÐǰ£¬¿ªÆôÊÂÎñ...."); + try { + Object o = p.proceed(); + System.out.println("Ö´ÐÐÍê³É£¬Ìá½»ÊÂÎñ...."); + return o; + } catch (Throwable e) { + System.out.println("³öÏÖÁËÒì³££¬¸ù¾ÝÒì³£ÀàÐͻعöÊÂÎñ...."); + throw e; + } finally { + System.out.println("Ö´Ðкó...."); + } + } + +} + +``` + +1. config£¬½øÐÐһЩ±ØÒªµÄÅäÖà + +``` +@Configuration +@ComponentScan("org.springframework.learn.tx.demo02") +@EnableAspectJAutoProxy(proxyTargetClass = true) +public class TxDemo02Config { + +} + +``` + +1. Ìí¼ÓÒ»¸ö service À࣬ÆäÖÐÒ»¸ö·½·¨ÉÏÓÐ `@MyTransactional` ×¢½â + +``` +@Service +public class TxTestService { + + @MyTransactional + public void test01() { + System.out.println("Ö´ÐÐtest01·½·¨"); + } + + public void test02() { + System.out.println("Ö´ÐÐtest02·½·¨"); + } + +} + +``` + +1. Ö÷Àà + +``` +public class TxDemo02Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext applicationContext + = new AnnotationConfigApplicationContext(TxDemo02Config.class); + TxTestService service = applicationContext.getBean(TxTestService.class); + System.out.println("-------------------"); + service.test01(); + System.out.println("-------------------"); + service.test02(); + + } +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +------------------- +Ö´ÐÐǰ£¬¿ªÆôÊÂÎñ.... +Ö´ÐÐtest01·½·¨ +Ö´ÐÐÍê³É£¬Ìá½»ÊÂÎñ.... +Ö´Ðкó.... +------------------- +Ö´ÐÐtest02·½·¨ + +``` + +Õâ¸ö demo ÖУ¬ÎÒÃÇʹÓà `@Around` ×¢½âÀ´À¹½ØÒµÎñ´úÂëÖ´ÐÐǰºóµÄÇé¿ö£¬¿ÉÒÔ¿´µ½£¬`@Around` ×¢½â¿ÉÒÔÔÚ´úÂëÔËÐÐǰºóÉõÖÁÊdzöÏÖÒ쳣ʱ´¦ÀíһЩ¶îÍâµÄ²Ù×÷¡£ + +#### demo02£º×Ô¶¨Òå `advisor` ʵÏÖÊÂÎñ + +ÈÃÎÒÃÇ»ØÒäÏ spring aop ¶Ô `@Around` ×¢½âµÄ´¦Àí£¬Êµ¼ÊÉÏ `@Around` ×îÖÕ»á·âװΪ `InstantiationModelAwarePointcutAdvisorImpl` ¶ÔÏ󣬺óÃæµÄ´¦Àí¾Í¸ú `@Around` ÎÞ¹ØÁË£¬`@Around` µ½ `InstantiationModelAwarePointcutAdvisorImpl` ¶ÔÏóµÄ¹ý³Ì£¬¿É²Î¿¼ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817). + +`InstantiationModelAwarePointcutAdvisorImpl` ÊǸöʲô¶«Î÷ÄØ£¿ÕâÊǸö `advisor`£¬¾ßÌåÀ´Ëµ¾ÍÊÇ¿ÉÓÃÓÚ·½·¨µÄÔöÇ¿¡£¹ØÓÚ spring aop ÊÇÈçºÎÕÒµ½ÄÜÓ¦ÓÃÓÚµ±Ç°·½·¨µÄ `advisor` µÄ£¬¿É²Î¿¼ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)¡£ + +ͨ¹ýÒÔÉÏ·ÖÎö£¬¸øÎÒÃÇÌṩÁËÁíÒ»¸ö˼·£ºÎÒÃÇ¿ÉÒÔʵÏÖ `advisor` ½Ó¿Ú£¬¶¨ÖÆ»¯×Ô¼ºµÄÂß¼­£¬´úÂëÈçÏ£º + +1. ×¼±¸ `advice` + +``` +/** + * Õâ¸öadvice¾ÍÊÇadvisorµÄÒ»¸öÊôÐÔ£¬ÇÐÃæÂß¼­ÔÚÕâÀï´¦Àí + */ +public class MyAdvice implements MethodInterceptor { + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + System.out.println("Ö´ÐÐǰ£¬¿ªÆôÊÂÎñ...."); + try { + Object val = invocation.proceed(); + System.out.println("Ö´ÐÐÍê³É£¬Ìá½»ÊÂÎñ...."); + return val; + } catch (Throwable e) { + System.out.println("³öÏÖÁËÒì³££¬¸ù¾ÝÒì³£ÀàÐͻعöÊÂÎñ...."); + throw e; + } finally { + System.out.println("Ö´Ðкó...."); + } + } +} + +``` + +1. ×¼±¸ `pointcut` + +``` +/** + * Çеã + * ÅжÏÄÄЩ·½·¨ÄÜÓÃÓÚ¸Ãadvisor + */ +public class MyPointcut extends StaticMethodMatcherPointcut { + /** + * Æ¥Åä·½·¨£¬ÓÐ @MyTransactional µÄÀà»ò·½·¨¾Í·µ»Øtrue + */ + @Override + public boolean matches(Method method, Class targetClass) { + return null != AnnotationUtils.getAnnotation(method, MyTransactional.class) + || null != AnnotationUtils.getAnnotation(targetClass, MyTransactional.class); + } +} + +``` + +1. ×¼±¸ `advisor` + +``` +/** + * advisor ¿É¿´×÷ÊÇ advice Óë pointcut µÄ°ü×° + */ +@Component +public class MyAdvisor extends AbstractBeanFactoryPointcutAdvisor { + + private static final long serialVersionUID = 2651364800145442305L; + + private MyPointcut pointcut; + + public MyAdvisor() { + this.pointcut = new MyPointcut(); + this.setAdvice(new MyAdvice()); + } + + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + +} + +``` + +ÒÔÉÏÊDz»Í¬ÓÚ×¢½âµÄʵÏÖ·½Ê½£¬½ÓÏÂÀ´µÄ´úÂë¾ÍÓë×¢½âÒ»ÑùÁË¡£ + +1. ×¼±¸Ò»¸ö×¢½â£º`@MyTransactional` + +``` +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface MyTransactional { +} + +``` + +1. ´¦ÀíÏîÄ¿ÅäÖà + +``` +@Configuration +@ComponentScan("org.springframework.learn.tx.demo01") +@EnableAspectJAutoProxy(proxyTargetClass = true) +public class TxDemo01Config { + +} + +``` + +1. ×¼±¸Ò»¸ö service + +``` +@Service +public class TxTestService { + + @MyTransactional + public void test01() { + System.out.println("Ö´ÐÐtest01·½·¨"); + } + + public void test02() { + System.out.println("Ö´ÐÐtest02·½·¨"); + } + +} + +``` + +1. Ö÷Àà + +``` +public class TxDemo01Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext applicationContext + = new AnnotationConfigApplicationContext(TxDemo01Config.class); + TxTestService service = applicationContext.getBean(TxTestService.class); + System.out.println("-------------------"); + service.test01(); + System.out.println("-------------------"); + service.test02(); + + } +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +------------------- +Ö´ÐÐǰ£¬¿ªÆôÊÂÎñ.... +Ö´ÐÐtest01·½·¨ +Ö´ÐÐÍê³É£¬Ìá½»ÊÂÎñ.... +Ö´Ðкó.... +------------------- +Ö´ÐÐtest02·½·¨ + +``` + +### 2\. ʹÓà spring ÊÂÎñ¹ÜÀí + +ÓÐÁËÇ°ÃæµÄÁ½¸öС demo ×÷Ϊ¿ªÎ¸²Ë£¬¶ÔÓÚ spring ÊÂÎñ´¦ÀíÏë±ØÄãÒÑÓÐÁËÒ»¸öÇåÎúµÄÈÏʶ£¬spring ÔÚ´¦ÀíÊÂÎñʱ£¬Ê¹ÓõľÍÊǵڶþÖÖ·½Ê½£¬¼´Íù×Ô¶¨ÒåÒ»¸ö `advisor` Ìí¼Óµ½ spring ÈÝÆ÷ÖС£¹ØÓÚ spring ʵÏÖÈÎÎñµÄ¾ßÌåϸ½Ú£¬ÎÒÃÇ´ý»á·ÖÎö£¬ÕâÀïÎÒÃÇÔÙÉÏÒ»¸ö demo£¬ÌåÑéÏÂÎÒÃÇÆ½Ê±ÊÇÔõôʹÓÃÊÂÎñµÄ¡£ + +ΪÁ˽øÐÐÊý¾Ý¿âÁ¬½Ó£¬ÎÒÃÇÐèÒªÒýÈëÊý¾Ý¿âÁ¬½Ó³Ø£¬ÕâÀïÎÒÃÇʹÓõÄÊÇ mysql£¬ÐèÒªÔÚ `spring-learn.gradle` ÖÐÌí¼ÓÒÀÀµ£º + +``` +optional("mysql:mysql-connector-java:5.1.48") + +``` + +½ÓמÍÊÇ´úÂëÁË¡£ + +1. ÅäÖÃÀà + +``` +@Configuration +@ComponentScan("org.springframework.learn.tx.demo03") +@EnableTransactionManagement(proxyTargetClass = true) +public class TxDemo01Config { + + /** + * Éú³ÉÊý¾ÝÔ´ + * @return + * @throws Exception + */ + @Bean + public DataSource dataSource() throws Exception { + Driver driver = new com.mysql.jdbc.Driver(); + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "123"; + return new SimpleDriverDataSource(driver, url, username, password); + } + + /** + * Éú³ÉjdbcTemplate£¬ºóÃæ¾ÍÊÇÓÃÕâ¸öÀàÀ´´¦ÀíÊý¾Ý¿âµÄ²Ù×÷ + * @param dataSource + * @return + */ + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + + /** + * ÊÂÎñ¹ÜÀíÆ÷ + * @param dataSource + * @return + */ + @Bean + public DataSourceTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + +} + +``` + +1. Êý¾Ý¿â²Ù×÷Àà + +``` +@Service +public class UserService { + + @Autowired + private JdbcTemplate jdbcTemplate; + + /** + * Êý¾Ý¿â²åÈë²Ù×÷£¬Ê¹Óà @Transactional ¿ªÆôÊÂÎñ + * @return + */ + @Transactional(rollbackFor = Exception.class) + public int insert() { + String sql = "insert into `user`(`login_name`, `nick`, `create_time`, `update_time`)" + + "values (?, ?, ?, ?)"; + int result = jdbcTemplate.update(sql, "test", "test", new Date(), new Date()); + if(true) { + //throw new RuntimeException("Å׳ö¸öÒì³£"); + } + System.out.println(result); + return result; + } + +} + +``` + +1. Ö÷Àà + +``` +public class TxDemo01Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext applicationContext + = new AnnotationConfigApplicationContext(TxDemo01Config.class); + UserService userService = applicationContext.getBean(UserService.class); + userService.insert(); + + } +} + +``` + +demo ÖУ¬`DataSource` ʹÓà spring ×Ô´øµÄ `SimpleDriverDataSource`£¬`orm` ¿ò¼ÜÒ²ÊÇ spring ÌṩµÄ `jdbcTemplate`£¬Ê¹ÓÃµÄ `user` ±í sql ÈçÏ£º + +``` +CREATE TABLE `user` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Ö÷¼üid', + `login_name` varchar(32) NOT NULL DEFAULT '0' COMMENT 'µÇ¼Ãû', + `nick` varchar(32) NOT NULL DEFAULT '0' COMMENT 'êdzÆ', + `create_time` datetime DEFAULT NULL COMMENT '´´½¨Ê±¼ä', + `update_time` datetime DEFAULT NULL COMMENT '¸üÐÂʱ¼ä', + PRIMARY KEY (`id`), + KEY `create_time` (`create_time`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Óû§±í'; + +``` + +Ö´Ðнá¹ûÈçÏ£º + +µÚÒ»´Î²»Å׳öÒì³££¬Êý¾Ý¿â½á¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-30bbe23a8e0491d1f59378469ad04703e03.png) + +µÚ¶þ´ÎÅ׳öÒì³££¬Êý¾Ý¿â½á¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-edf98369ccef9735d83813cef7af7ea1dcd.png) + +µÚÈý´Î²»Å׳öÒì³££¬Êý¾Ý¿â½á¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fab4169dbec7661f27bba203c5e77232f65.png) + +¿ÉÒÔ¿´µ½£¬µÚ¶þ´ÎÅ׳öÒ쳣ʱ£¬Êý¾ÝÕý³£»Ø¹öÁË¡£ + +ÎÒÃÇÔÙÀ´·ÖÎöÏÂÕâ¸ö demo£¬¸úÊÂÎñÓйصĴúÂëÓÐÈý´¦£º + +* `@EnableTransactionManagement(proxyTargetClass = true)`£ºÆôÓÃÊÂÎñ +* `DataSourceTransactionManager`£ºÊÂÎñ¹ÜÀíÆ÷ +* `@Transactional`£ºÖ¸¶¨¿ªÆôÊÂÎñµÄ·½·¨ + +Àà±ÈÓÚ aop µÄ `@EnableAspectJAutoProxy`£¬`@EnableTransactionManagement` ÊÇÆô¶¯ÊÂÎñµÄÈë¿Ú£¬½Ó×ÅÎÒÃǾʹÓÕâ¸ö×¢½âÈëÊÖ£¬·ÖÎö spring ÊÂÎñµÄÆôÓÃÁ÷³Ì¡£ + +### 3. `@EnableTransactionManagement` ×¢½â + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(TransactionManagementConfigurationSelector.class) +public @interface EnableTransactionManagement { + + /** + * ѧÍêaopºó£¬Ïë±Ø¶ÔÕâ¸öÊôÐÔÒѾ­ºÜÊìϤ + * true: ±íÊ¾Ç¿ÖÆÊ¹ÓÃcglib´úÀí + * false£ºÈç¹ûÄ¿±êÀàʵÏÖÁ˽ӿڣ¬ÔòʹÓÃjdk¶¯Ì¬´úÀí£¬·ñÔòʹÓÃcglib´úÀí + * ½öÔÚ mode Ϊ PROXY ÏÂÉúЧ + */ + boolean proxyTargetClass() default false; + + /** + * adviceģʽ£¬Ê¹ÓôúÀí£¬»¹ÊÇʹÓà aspectJ + */ + AdviceMode mode() default AdviceMode.PROXY; + + /** + * Ö´ÐÐ˳Ðò£¬µ±Ò»¸ö´úÀí¶ÔÏóÓжà¸öÔöǿʱ£¬°´Ê²Ã´ÑùµÄ˳ÐòÀ´Ö´ÐÐ + */ + int order() default Ordered.LOWEST_PRECEDENCE; + +} + +``` + +Õâ¸ö×¢½â±¾Éíûʲô£¬¾ÍÈý¸öÊôÐÔ£¬×¢ÊÍÒѾ­ºÜÃ÷È·ÁË£¬ÎÒÃǹؼü»¹ÊÇ¿´Õâ¸ö×¢½âÒýÈëµÄÀࣺ`TransactionManagementConfigurationSelector`£º + +``` +public class TransactionManagementConfigurationSelector extends + AdviceModeImportSelector { + @Override + protected String[] selectImports(AdviceMode adviceMode) { + switch (adviceMode) { + case PROXY: + // »ùÓÚ´úÀíµÄÊÂÎñ¹ÜÀí£¬»áÒýÈëÁ½¸öÀà + return new String[] {AutoProxyRegistrar.class.getName(), + ProxyTransactionManagementConfiguration.class.getName()}; + case ASPECTJ: + // »ùÓÚaspectJµÄÊÂÎñ¹ÜÀí£¬»áÒýÈëÕâ¸öÀ࣬±¾ÎIJ»·ÖÎö + return new String[] {determineTransactionAspectClass()}; + default: + return null; + } + } + // Ê¡ÂÔÆäËû + ... + +} + +``` + +»ùÓÚ´úÀíµÄÊÂÎñ¹ÜÀí£¬»áÒýÈëÁ½¸öÀࣺ`AutoProxyRegistrar`¡¢`ProxyTransactionManagementConfiguration`£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÕâÁ½¸öÀà¡£ + +#### 3.1 `AutoProxyRegistrar` + +´ÓÃû×ÖÉÏÀ´¿´£¬`AutoProxyRegistrar` ÊÇÒ»¸ö×¢²áÆ÷£¬»¹¼ÇµÃÇ°Ãæ aop µÄ×¢²áÆ÷ `AspectJAutoProxyRegistrar` Âð£¬Á½ÕßÒ»ÑùµÄÌ×·£¡ + +ÎÒÃÇÀ´¿´¿´ÀïÃæ¾¿¾¹×öÁËɶ£º + +``` +public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { + + private final Log logger = LogFactory.getLog(getClass()); + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + boolean candidateFound = false; + Set annTypes = importingClassMetadata.getAnnotationTypes(); + for (String annType : annTypes) { + AnnotationAttributes candidate = AnnotationConfigUtils + .attributesFor(importingClassMetadata, annType); + if (candidate == null) { + continue; + } + Object mode = candidate.get("mode"); + Object proxyTargetClass = candidate.get("proxyTargetClass"); + // Âú×ãifÌõ¼þµÄ£¬¾ÍÊÇ @EnableTransactionManagement ×¢½â + if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && + Boolean.class == proxyTargetClass.getClass()) { + candidateFound = true; + if (mode == AdviceMode.PROXY) { + // ×¢²á²Ù×÷£¬×îÖÕ×¢²áÁË InfrastructureAdvisorAutoProxyCreator À࣬½ÓÏÂÀ´»á¼ÌÐø·ÖÎö + AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); + if ((Boolean) proxyTargetClass) { + // ʹÓÃcglib´úÀí + AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); + return; + } + } + } + } + if (!candidateFound && logger.isInfoEnabled()) { + String name = getClass().getSimpleName(); + logger.info(...); + } + } +} + +``` + +ÕâÐдúÂë¹Ø¼üµÄ¾ÍÖ»ÓÐ if ÀïµÄ¼¸ÐУ¬ÏÈ˵Ï if Ìõ¼þ£º`mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&Boolean.class == proxyTargetClass.getClass()`£¬Í¨¹ýÉÏÃæµÄ¶Ô `@EnableTransactionManagement`£¬Õâ˵µÄ¾ÍÊÇËüÁË£»¶ÔÓÚ `mode == AdviceMode.PROXY`£¬¼ÌÐøµ÷Óà `AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)`£¨¹ØÓÚÕâ¸ö·½·¨µÄµ÷Óã¬ÎÒÃǽÓÏÂÀ´»á¼ÌÐø·ÖÎö£©£»È»ºó´¦Àí `proxyTargetClass`£¬Õâ¸öÊôÐÔµÄ×÷ÓÃÓë `@EnableAspectJAutoProxy` ÖÐµÄ `proxyTargetClass` Ò»Ö£¬Ò²ÊÇ¿ÉÒÔÇ¿ÖÆÊ¹Óà cglib ´úÀí¡£ + +½Ó×ÅÎÒÃÇÀ´·ÖÎöÏ `AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)` µÄ¹ý³Ì£¬¸ú½ø´úÂ룺 + +> AopConfigUtils + +``` + @Nullable + public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) { + // ¼ÌÐøÍùÏ¿´ + return registerAutoProxyCreatorIfNecessary(registry, null); + } + + @Nullable + public static BeanDefinition registerAutoProxyCreatorIfNecessary( + BeanDefinitionRegistry registry, @Nullable Object source) { + // ´«Èë InfrastructureAdvisorAutoProxyCreator À࣬¼ÌÐøµ÷Óà + return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, + registry, source); + } + +``` + +¿´µ½ÕâÀÊDz»ÊÇÓÐÖÖÉîÉîµÄÊìϤ¸Ð£¿aop ÖÐµÄ `AspectJAnnotationAutoProxyCreator` Ò² ÊÇÕâôע²áµÄ£¡½øÈë `AopConfigUtils#registerOrEscalateApcAsRequired` ·½·¨£º + +``` +// AopConfigUtils ¿É×¢²áµÄÀà¶¼ÔÚÕâÀïÁË +private static final List> APC_PRIORITY_LIST = new ArrayList<>(3); + +static { + APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); + APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); + APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); +} + +/** + * ×¢²á²Ù×÷ + */ +private static BeanDefinition registerOrEscalateApcAsRequired( + Class cls, BeanDefinitionRegistry registry, @Nullable Object source) { + Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); + //Èç¹ûÒÑ´æÔÚÕâ¸öbean + if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { + BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); + //ÅжÏÓÅÏȼ¶£¬Èç¹ûÓÅÏȼ¶½Ï¸ßÔòÌæ»»Ô­ÏȵÄbean + if (!cls.getName().equals(apcDefinition.getBeanClassName())) { + int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); + int requiredPriority = findPriorityForClass(cls); + // ÒÑ´æÔÚÀà µÄÓÅÏȼ¶ СÓÚÕýÔÚ×¢²áµÄ£¬ÔòʹÓÃÕýÔÚ×¢²áµÄ£¬ÒÑ´æÔÚµÄÈý¸öÀàµÄÓÅÏȼ¶Îª + // 0: InfrastructureAdvisorAutoProxyCreator(´¦ÀíÊÂÎñ) + // 1: AspectJAwareAdvisorAutoProxyCreator(´¦Àí»ùÓÚxmlµÄaop) + // 2: AnnotationAwareAspectJAutoProxyCreator(´¦Àí»ùÓÚ×¢½âµÄaop) + if (currentPriority < requiredPriority) { + apcDefinition.setBeanClassName(cls.getName()); + } + } + return null; + } + //×¢²áXxxAutoProxyCreatorµ½ÈÝÆ÷ÖÐ + RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); + beanDefinition.setSource(source); + beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); + return beanDefinition; +} + +/** + * ²éÕÒ×¢²áÀàµÄÓÅÏȼ¶ + */ +private static int findPriorityForClass(@Nullable String className) { + for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) { + Class clazz = APC_PRIORITY_LIST.get(i); + if (clazz.getName().equals(className)) { + return i; + } + } + throw new IllegalArgumentException( + "Class name [" + className + "] is not a known auto-proxy creator class"); +} + +``` + +`AopConfigUtils` ¿É×¢²áµÄÀàÓÐÓÐÈý¸ö£º + +* `InfrastructureAdvisorAutoProxyCreator`£º´¦ÀíÊÂÎñ +* `AspectJAwareAdvisorAutoProxyCreator`£º´¦Àí»ùÓÚ xml µÄ aop +* `AnnotationAwareAspectJAutoProxyCreator`£º´¦Àí»ùÓÚ×¢½âµÄ aop + +ÕâÈýÕßµÄÓÅÏȼ¶Îª `AnnotationAwareAspectJAutoProxyCreator` > `AspectJAwareAdvisorAutoProxyCreator` > `InfrastructureAdvisorAutoProxyCreator`£¬×¢Èëʱ£¬»áÅжÏ×¢ÈëÀàµÄÓÅÏȼ¶£¬ÓÅÏȼ¶¸ßµÄ×îÖջᱻעÈëµ½ spring ÈÝÆ÷ÖС£ÕâÑù¾Íµ¼ÖÂÁËÒ»¸öÎÊÌ⣺**Èç¹ûÏîÄ¿ÖÐͬʱ¿ªÆôÁË aop (`@EnableAspectJAutoProxy`) ÓëÊÂÎñ (`@EnableTransactionManagement`)£¬ÄÇô×îÖÕ×¢Èëµ½ÈÝÆ÷µÄ½«ÊÇ `AnnotationAwareAspectJAutoProxyCreator`£¬ÕâÒ²¾ÍÊÇ˵£¬`AnnotationAwareAspectJAutoProxyCreator` Ò²ÄÜ´¦ÀíÊÂÎñ£¡** Õâ¾ä»°·Ç³£¹Ø¼ü£¬ËüÒâζ×ÅÊÂÎñµÄ´¦Àí¹ý³Ì£¬Êµ¼ÊÉϾͰüº¬ÔÚÇ°Ãæ·ÖÎöµÄ aop µÄ¹ý³ÌÖÐÁË£¡ + +ÎÒÃÇÒ²À´¿´¿´ `InfrastructureAdvisorAutoProxyCreator`£º + +``` +// ¼Ì³ÐÁË AbstractAdvisorAutoProxyCreator£¬Õâ¸öÀà·Ç³£¹Ø¼ü +public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator { + + @Nullable + private ConfigurableListableBeanFactory beanFactory; + + @Override + protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { + super.initBeanFactory(beanFactory); + this.beanFactory = beanFactory; + } + + @Override + protected boolean isEligibleAdvisorBean(String beanName) { + return (this.beanFactory != null && + this.beanFactory.containsBeanDefinition(beanName) + && this.beanFactory.getBeanDefinition(beanName).getRole() + == BeanDefinition.ROLE_INFRASTRUCTURE); + } + +} + +``` + +`InfrastructureAdvisorAutoProxyCreator` Æäʵ²¢Ã»ÓÐ×öʲôÓë aop Ïà¹ØµÄÊ£¬µ«Ëü¼Ì³ÐÁËÒ»¸ö¹Ø¼üµÄÀࣺ`AbstractAdvisorAutoProxyCreator`£¬Õâ¸öÀà¿ÉÊÇ´óÓÐÀ´Í·£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-2881f63ac07afc5095c449ddb9a0df2bb55.png) + +´Ó¼Ì³Ð¹ØÏµÀ´¿´£¬Õâ¸öÀà¼Ì³ÐÁË `AbstractAutoProxyCreator`£¬¶ø `AbstractAutoProxyCreator` ÕýÊÇÎÒÃÇÔÚ - [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817) Óë [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)ÖÐÖØµã·ÖÎöµÄ¡¢´úÀí¶ÔÏóµÄ²úÉúËùÔÚ£¡ + +ÎÒÃÇÔÙÀ´¿´Ï `AnnotationAwareAspectJAutoProxyCreator`¡¢`AspectJAwareAdvisorAutoProxyCreator` ¡¢`InfrastructureAdvisorAutoProxyCreator` ÕâÈýÕߵĹØÏµ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-acd72335503eeb686abe81d930b45f1f3f0.png) + +¿ÉÒÔ¿´µ½£¬`AspectJAwareAdvisorAutoProxyCreator` ¡¢`InfrastructureAdvisorAutoProxyCreator` ¶¼¼Ì³ÐÁË `AbstractAdvisorAutoProxyCreator`£¬`AnnotationAwareAspectJAutoProxyCreator` Óּ̳ÐÁË `AspectJAwareAdvisorAutoProxyCreator`¡£ + +ͨ¹ýÒÔÉÏ·ÖÎö£¬`AutoProxyRegistrar` ×îÖÕÏò spring ÈÝÆ÷×¢²áÁË `InfrastructureAdvisorAutoProxyCreator`(`aop` δÆôÓõÄÇé¿öÏÂ)£¬Èç¹ûÆôÓÃÁË `aop`£¬Ôò»á×¢²á `AspectJAwareAdvisorAutoProxyCreator`(»ùÓÚ `xml` µÄ `aop`) »ò `AnnotationAwareAspectJAutoProxyCreator`(»ùÓÚ `annotation` µÄ `aop`)¡£ + +#### 3.2 `ProxyTransactionManagementConfiguration` + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `ProxyTransactionManagementConfiguration` Àà¡£Ãû×ÖÉÏÀ´¿´£¬ÕâÊǸöÅäÖÃÀࣺ + +``` +@Configuration(proxyBeanMethods = false) +public class ProxyTransactionManagementConfiguration + extends AbstractTransactionManagementConfiguration { + + /** + * ¶ÁÈ¡SpringµÄ @Transactional ×¢½â£¬²¢½«ÏàÓ¦µÄÊÂÎñÊôÐÔ¹«¿ª¸øSpringµÄÊÂÎñ»ù´¡½á¹¹ + */ + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public TransactionAttributeSource transactionAttributeSource() { + return new AnnotationTransactionAttributeSource(); + } + + /** + * TransactionInterceptor¼Ì³ÐÁËAdvice£¬Õâ¸öÀàÊǸöadvice£¬ÓÃÀ´´¦ÀíÊÂÎñµÄÖ´ÐвÙ×÷ + * @param transactionAttributeSource£ºÀ´×ÔÓÚÉÏÃæµÄ transactionAttributeSource() ·½·¨ + */ + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public TransactionInterceptor transactionInterceptor( + TransactionAttributeSource transactionAttributeSource) { + TransactionInterceptor interceptor = new TransactionInterceptor(); + // ÉèÖÃÊÂÎñÊôÐÔ´¦Àí¶ÔÏ󣬾ÍÊÇÓÃÀ´´¦Àí @Transactional ×¢½â µÄ¶ÁÈ¡ + interceptor.setTransactionAttributeSource(transactionAttributeSource); + if (this.txManager != null) { + interceptor.setTransactionManager(this.txManager); + } + return interceptor; + } + + /** + * ÊÂÎñÔöÇ¿Æ÷. + * @param transactionAttributeSource£ºÀ´×ÔÓÚÉÏÃæµÄ transactionAttributeSource() ·½·¨ + * @param transactionInterceptor£ºÀ´×ÔÓÚÉÏÃæµÄ transactionInterceptor(...) ·½·¨ + */ + @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( + TransactionAttributeSource transactionAttributeSource, + TransactionInterceptor transactionInterceptor) { + BeanFactoryTransactionAttributeSourceAdvisor advisor + = new BeanFactoryTransactionAttributeSourceAdvisor(); + // ÊÂÎñÊôÐÔÀ࣬ÓÃÀ´±£´æ @Transactional µÄÊôÐÔ + advisor.setTransactionAttributeSource(transactionAttributeSource); + // ÅäÖÃadvice£¬ÔÚadviceÀï´¦ÀíÊÂÎñ + advisor.setAdvice(transactionInterceptor); + if (this.enableTx != null) { + advisor.setOrder(this.enableTx.getNumber("order")); + } + return advisor; + } + +} + +``` + +¿ÉÒÔ¿´µ½Õâ¸öÀàÒýÈëÁËÒ» Щ `bean`£º + +* `transactionAttributeSource`£ºÀàÐÍΪ `AnnotationTransactionAttributeSource`£¬ÓÃÀ´½âÎö `@Transactional` ×¢½â£» +* `transactionInterceptor`£ºÀàÐÍΪ `TransactionInterceptor`£¬`Advice` µÄ×ÓÀ࣬´¦ÀíÊÂÎñµÄÂß¼­ÔÚÕâ¸öÀàÀ +* `transactionAdvisor`£ºÀàÐÍΪ `BeanFactoryTransactionAttributeSourceAdvisor`£¬ÕâÊǸö `Advisor`£¬ÓÃÀ´´¦ÀíÇÐÃæÂß¼­£¬ÄÚ²¿¼¯³ÉÁËÉÏÃæÁ½¸ö¶ÔÏó£º`transactionAttributeSource` Óë `transactionInterceptor`£» + +`ProxyTransactionManagementConfiguration` ¼Ì³ÐÁË `AbstractTransactionManagementConfiguration`£¬¶ø `AbstractTransactionManagementConfiguration` ÖÐÒ²ÒýÈëÁËһЩ `bean`£º + +``` +@Configuration +public abstract class AbstractTransactionManagementConfiguration implements ImportAware { + + @Nullable + protected AnnotationAttributes enableTx; + + /** + * ±£´æÊÂÎñ¹ÜÀíÆ÷ + */ + @Nullable + protected TransactionManager txManager; + + /** + * À´×ÔÓÚ ImportAware ½Ó¿ÚµÄ·½·¨ + */ + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + this.enableTx = AnnotationAttributes.fromMap(importMetadata + .getAnnotationAttributes(EnableTransactionManagement.class.getName(), false)); + if (this.enableTx == null) { + throw new IllegalArgumentException( + "@EnableTransactionManagement is not present on importing class " + + importMetadata.getClassName()); + } + } + + /** + * ÅäÖÃÊÂÎñ¹ÜÀíÆ÷. + * ×¢ÈëspringÈÝÆ÷ÖÐËùÓÐµÄ TransactionManagementConfigurer ¶ÔÏó + * TransactionManagementConfigurer¾ÍÖ»ÓÐÒ»¸ö·½·¨£º + * TransactionManager annotationDrivenTransactionManager() + * Õâ¸ö·½·¨ÓÃÀ´·µ»ØÒ»¸öÊÂÎñ¹ÜÀíÆ÷ + */ + @Autowired(required = false) + void setConfigurers(Collection configurers) { + if (CollectionUtils.isEmpty(configurers)) { + return; + } + if (configurers.size() > 1) { + throw new IllegalStateException("Only one TransactionManagementConfigurer may exist"); + } + TransactionManagementConfigurer configurer = configurers.iterator().next(); + this.txManager = configurer.annotationDrivenTransactionManager(); + } + + /** + * ´¦Àíʼþ¼àÌý£¬ÓÃÀ´´¦Àí @TransactionalEventListener ×¢½âµÄ·½·¨. + */ + @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public static TransactionalEventListenerFactory transactionalEventListenerFactory() { + return new TransactionalEventListenerFactory(); + } + +} + +``` + +* `void setConfigurers(Collection configurers)`£º×¢Èë `TransactionManagementConfigurer` ¶ÔÏ󣬾ßÌå×÷ÓÃÒÑÔÚ´úÂë×¢ÊÍÖÐ˵Ã÷£» +* `TransactionalEventListenerFactory`£ºÀàÐÍΪ `TransactionalEventListenerFactory`£¬ÓÃÀ´´¦ÀíÊÂÎñʼþ£¨Ö÷ÒªÊÇ `@TransactionalEventListener` ×¢½âµÄ·½·¨£¬¹ØÓÚÕⲿ·ÖµÄÄÚÈÝ£¬±¾ÎľͲ»Õ¹¿ªÁË£©¡£ + +ºÃÁË£¬ÓÐÁËÕâЩ¶ÔÏóºó£¬spring ¾Í¿ÉÒÔ½øÐÐÊÂÎñ´¦ÀíÁË£¬ÕâЩÎÒÃÇÁôµ½ÏÂÆªÎÄÕÂÔÙ·ÖÎö¡£ + +### 4\. ×ܽá + +±¾ÎÄÏÈÊÇ´ÓÁ½¸ö demo ÈëÊÖ£¬Ê¾·¶ÁËÈç¹ûÊÇÓÉÎÒÃÇ×Ô¼º¿ª·¢Ò»¸ö»ùÓÚ spring aop µÄÊÂÎñ¹ÜÀí¹¦ÄÜÊÇÈçºÎ½øÐе쬽Ó×ÅÓÖÓÃÒ»¸ö demo ʾ·¶ÁËÈçºÎʹÓà spring ÌṩµÄÊÂÎñ¹ÜÀí¹¦ÄÜ£¬È»ºó¾Í¾ßÌå·ÖÎöÁË spring ÊÂÎñÆôÓÃ×¢½â `@EnableTransactionManagement` µÄ¹¦ÄÜ¡£ + +`@EnableTransactionManagement` ÊÇ spring ÖÐÓÃÀ´ÆôÓÃÊÂÎñ¹ÜÀí¹¦Äܵģ¬ÔÚ `AdviceMode` Ϊ `proxy` ģʽÏ£¬¸Ã×¢½âÏò spring ÖÐÒýÈëÁËÁ½¸öÀࣺ`AutoProxyRegistrar`¡¢`ProxyTransactionManagementConfiguration`£¬×÷ÓÃÈçÏ£º + +* `AutoProxyRegistrar`£º`aop` δÆôÓõÄÇé¿öÏ£¬»áÏò spring ÈÝÆ÷ÖÐ×¢²á `InfrastructureAdvisorAutoProxyCreator`£»Èç¹ûÆôÓÃÁË `aop`£¬Ôò»á×¢²á `AspectJAwareAdvisorAutoProxyCreator`(»ùÓÚ `xml` µÄ `aop`) »ò `AnnotationAwareAspectJAutoProxyCreator`(»ùÓÚ `annotation` µÄ `aop`)¡£ÕâÈý¸öÀà¶¼ÊÇ `AbstractAdvisorAutoProxyCreator` µÄ×ÓÀ࣬ÓÃÀ´Éú³É´úÀí¶ÔÏó¡£ + +* `ProxyTransactionManagementConfiguration`£ºÕâÊÇÒ»¸öÅäÖÃÀ࣬ͨ¹ý´øÓÐ `@Bean` ×¢½âµÄ·½·¨ÏòÈÝÆ÷ÖÐÒýÈëÁËһϵÁÐµÄ bean£¬ÓÃÀ´´¦ÀíÊÂÎñÂß¼­£¬¶ÔÕâЩ bean£¬±¾ÎÄÖ»Ðè´ó¸ÅÁ˽⼴¿É¡£ + +±¾ÎľÍÏȵ½ÕâÀïÁË£¬ÏÂÆªÎÄÕ¼ÌÐø·ÖÎöÊÂÎñ´¦Àí»úÖÆ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4773454](https://my.oschina.net/funcy/blog/4773454) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" new file mode 100644 index 0000000..73a5691 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\270\211\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20601.md" @@ -0,0 +1,387 @@ +ÔÚÉÏһƪÎÄÕµÄ×îºó£¬ÎÒÃÇÌáµ½ÊÂÎñµÄÖ´ÐÐÔÚ `TransactionAspectSupport#invokeWithinTransaction` ·½·¨ÖУ¬±¾ÎĽ«´ÓÕâ¸ö·½·¨³ö·¢£¬Ì½¾¿ spring µÄÊÂÎñ»úÖÆ¡£ + +×¢£º±¾ÎÄËù̽ÌÖµÄÊý¾Ý¿âÊÇ `mysql`£¬ÆäËûÊý¾Ý¿â¿ÉÄÜ»áÓÐËù²îÒì¡£ + +### 1. ÊÂÎñÏà¹Ø¸ÅÄî + +ÔÚÕýʽ·ÖÎöÊÂÎñÔ´Âëǰ£¬Ò»Ð©Ç°ÖÃ֪ʶ»¹ÊÇÒªÁ˽âһϵģ¬ÕâÀïÖ÷Òª½éÉÜÓë spring Ïà¹ØµÄÊÂÎñ¸ÅÄî¡£ + +#### 1.1 ÊÂÎñµÄ¸ôÀë¼¶±ð + +ÊÂÎñÓÐËÄ´óÌØÐÔ `ACID`£¬ÁоÙÈçÏ£º + +- Ô­×ÓÐÔ£¨`Atomicity`£© +- Ò»ÖÂÐÔ£¨`Consistency`£© +- ¸ôÀëÐÔ£¨`Isolation`£© +- ³Ö¾ÃÐÔ£¨`Durability`£© + +¶øÊÂÎñµÄ¸ôÀë¼¶±ðÕýÊǶԸôÀëÐÔ£¨`Isolation`£©µÄ½øÒ»²½»®·Ö£¬ÕâЩ¸ôÀë¼¶±ðÈçÏ£º + +- `¶ÁδÌá½»` +- `¶ÁÌá½»` +- `¿ÉÖØ¸´¶Á` +- `´®Ðл¯` + +¹ØÓÚÕâЩ¸ÅÄî±¾ÎIJ¢²»»áÉîÈë½â¶Á£¬ÎÒÃÇÖØµã»¹ÊǹØ×¢ spring Ïà¹ØµÄÄÚÈÝ£¬spring ¶ÔÊÂÎñ¸ôÀë¼¶±ðµÄ¶¨ÒåÔÚ `org.springframework.transaction.annotation.Isolation` ÖУ¬´úÂëÈçÏ£º + +``` +public enum Isolation { + + /** + * ĬÈÏÖµ£¬²»ÉèÖøôÀë¼¶±ð£¬×îÖÕʹÓõÄÊÇÊý¾Ý¿âÉèÖõĸôÀë¼¶±ð + */ + DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), + + /** + * ¶ÁδÌá½» + */ + READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), + + /** + * ¶ÁÌá½» + */ + READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), + + /** + * ¿ÉÖØ¸´¶Á + */ + REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), + + /** + * ´®Ðл¯ + */ + SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); + + ... +} +``` + +ÎÒÃÇ¿ÉÒÔʹÓà `@Transactional` ×¢½âµÄ `isolation` À´ÉèÖøôÀë¼¶±ð + +#### 1.2 ÊÂÎñµÄ³¬Ê±Ê±¼ä + +- ¿ÉÒÔ¸øÊÂÎñÖ¸¶¨Ò»¸öÖ´ÐÐʱ¼ä£¬Èç¹ûÖ´ÐÐÏûºÄµÄʱ¼ä³¬¹ýÁËÖ¸¶¨Ê±¼ä£¬ÊÂÎñ¾Í»áÅ׳öÒì³£´Ó¶ø»Ø¹ö +- ¿ÉÒÔÔÚ `@Transactional` ×¢½âµÄ `timeout` À´ÉèÖó¬Ê±Ê±¼ä + +#### 1.3 Ö»¶ÁÊÂÎñ + +- ¿ÉÒÔ½«ÊÂÎñÉèÖÃΪ`Ö»¶Áģʽ`£¬Õâ¸öÎÒÆ½Ê±Óûù±¾Ã»Óõ½£¬²éµ½Ò»Ð©ÎÄÕÂ˵£¬½«ÊÂÎñÉèÖÃΪ`Ö»¶Áģʽ`ºó£¬ÆäËûÊÂÎñÖеĸüÐÂÖ»¶ÁÊÂÎñ¿´²»µ½£¬ÇÒÖ»¶ÁÊÂÎñÖв»ÄÜÓÐд²Ù×÷£¬ÕæÊµÐÔÓдýÑéÖ¤ +- ÎÒÃÇ¿ÉÒÔʹÓà `@Transactional` ×¢½âµÄ `readOnly` À´ÉèÖÃÖ»¶Áģʽ + +#### 1.4 ÊÂÎñµÄ´«²¥ÀàÐÍ + +ÎÒÃÇÊÔÏëÒ»ÖÖÇé¿ö£º`·½·¨A` Óë`·½Ê½B` ¶¼¿ªÆôÁËÊÂÎñ£¬ÔÚ`·½·¨B` Öе÷ÓÃ`·½·¨A`£¬Èç¹ûÔÚ`·½·¨A` Ö´ÐÐÍê³Éºó£¬`·½·¨B` ±¨´íÁË£¬´úÂëʾÒâÈçÏ£º + +``` +class A { + // ¿ªÆôÁËÊÂÎñ + @Transactional + public void methdA() { + // ´¦ÀíһЩ²Ù×÷ + ... + } +} + +class B { + // ¿ªÆôÁËÊÂÎñ + @Transactional + public void methodB() { + // 1. ´¦ÀíһЩ²Ù×÷ + ... + // 2. µ÷Óà methodA() + a.methodA(); + // 3. ÕâÀﱨ¸ö´í + throw new RuntimeException(); + } +} +``` + +ÓÉÓÚ¿ªÆôÁËÊÂÎñ£¬`·½·¨B` ÊÇÒ»¶¨»á»Ø¹öµÄ£¬ÄÇô`·½·¨A` Òª²»Òª»Ø¹öÄØ£¿ + +- Èç¹ûÎÒÃǰÑ`·½·¨A` Óë`·½·¨B` µÄÊÂÎñ¿´³ÉÊÇͬһ¸öÊÂÎñ£¬`·½·¨A` Ó¦¸ÃÒ²ÊÇÒª»Ø¹öµÄ +- Èç¹ûÎÒÃǰÑ`·½·¨A` Óë`·½·¨B` ¿´³ÉÊÇÔÚÁ½¸ö¶ÀÁ¢µÄÊÂÎñÖÐÖ´ÐУ¬`·½·¨A` µÄÖ´ÐÐÓë`·½·¨B` µÄ±¨´íÎ޹أ¬`·½·¨A` ¾Í²»Ó¦¸Ã»Ø¹ö + +ΪÁË´¦ÀíÕâÖÖ¾À·×£¬spring ÒýÈëÁË`ÊÂÎñµÄ´«²¥ÀàÐÍ`µÄ¸ÅÄÎÒÃÇ¿ÉÒÔʹÓà `@Transactional` ×¢½âµÄ `propagation` À´ÉèÖÃÖ»¶Áģʽ£º + +``` +public @interface Transactional { + ... + + // ĬÈϵļ¶±ðΪ Propagation.REQUIRED + Propagation propagation() default Propagation.REQUIRED; + +} +``` + +spring Ò»¹²¶¨ÒåÁË 7 ÖÖÊÂÎñµÄ´«²¥ÀàÐÍ£¬ÁоÙÈçÏ£º + +| ÊÂÎñ´«²¥ÐÐΪ | ÃèÊö | +| --------------------------- | ------------------------------------------------------------ | +| `PROPAGATION_REQUIRED` | ¡¾Ä¬ÈÏÖµ£º±ØÐè¡¿µ±Ç°·½·¨±ØÐëÔÚÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖÐûÓÐÊÂÎñ£¬Ôò¿ªÆôÒ»¸öеÄÊÂÎñ£»Èç¹ûµ±Ç°Ïß³ÌÖÐÒѾ­´æÔÚÊÂÎñ£¬Ôò·½·¨½«»áÔÚ¸ÃÊÂÎñÖÐÔËÐС£ | +| `PROPAGATION_MANDATORY` | ¡¾Ç¿ÖÆ¡¿µ±Ç°·½·¨±ØÐëÔÚÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖв»´æÔÚÊÂÎñ£¬Ôò**Å׳öÒì³£** | +| `PROPAGATION_SUPPORTS` | ¡¾Ö§³Ö¡¿µ±Ç°·½·¨µ¥¶ÀÔËÐÐʱ²»ÐèÒªÊÂÎñ£¬µ«Èç¹ûµ±Ç°Ïß³ÌÖдæÔÚÊÂÎñʱ£¬·½·¨»áÔÚÊÂÎñÖÐÔËÐÐ | +| `PROPAGATION_REQUIRES_NEW` | ¡¾ÐÂÊÂÎñ¡¿µ±Ç°·½·¨±ØÐëÔÚ¶ÀÁ¢µÄÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖÐÒѾ­´æÔÚÊÂÎñ£¬Ôò½«¸ÃÊÂÎñ¹ÒÆð£¬ÖØÐ¿ªÆôÒ»¸öÊÂÎñ£¬Ö±µ½·½·¨ÔËÐнáÊøÔÙ»Ö¸´Ö®Ç°µÄÊÂÎñ | +| `PROPAGATION_NESTED` | ¡¾Ç¶Ìס¿µ±Ç°·½·¨±ØÐëÔÚÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖдæÔÚÊÂÎñ£¬Ôò½«¸ÃÊÂÎñ±ê×¢**±£´æµã**£¬ÐγÉǶÌ×ÊÂÎñ¡£Ç¶Ì×ÊÂÎñÖеÄ×ÓÊÂÎñ³öÏÖÒì³£²»»áÓ°Ïìµ½¸¸ÊÂÎñ±£´æµã֮ǰµÄ²Ù×÷¡£ | +| `PROPAGATION_NOT_SUPPORTED` | ¡¾²»Ö§³Ö¡¿µ±Ç°·½·¨²»»áÔÚÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖдæÔÚÊÂÎñ£¬Ôò½«ÊÂÎñ¹ÒÆð£¬Ö±µ½·½·¨ÔËÐнáÊø | +| `PROPAGATION_NEVER` | ¡¾²»ÔÊÐí¡¿µ±Ç°·½·¨²»ÔÊÐíÔÚÊÂÎñÖÐÔËÐУ¬Èç¹ûµ±Ç°Ïß³ÌÖдæÔÚÊÂÎñ£¬Ôò**Å׳öÒì³£** | + +×¢ÒâÌå»áÒÔÏÂÀàÐ͵ÄÇø±ð£º + +1. `PROPAGATION_REQUIRED` Óë `PROPAGATION_MANDATORY` + - `PROPAGATION_REQUIRED`£º±ØÐëÒªÔÚÊÂÎñÖÐÔËÐУ¬Ã»ÓÐÊÂÎñ¾Í**¿ªÆôÐÂÊÂÎñ** + - `PROPAGATION_MANDATORY`£º±ØÐëÒªÔÚÊÂÎñÖÐÔËÐУ¬Ã»ÓÐÊÂÎñ¾Í**Å×Òì³£** +2. `PROPAGATION_NOT_SUPPORTED` Óë `PROPAGATION_NEVER` + - `PROPAGATION_NOT_SUPPORTED`£º²»ÄÜÔÚÊÂÎñÖÐÔËÐУ¬ÓÐÊÂÎñ¾Í**¹ÒÆðÊÂÎñ** + - `PROPAGATION_NEVER`£º²»ÄÜÔÚÊÂÎñÖÐÔËÐУ¬ÓÐÊÂÎñ¾Í**Å×Òì³£** +3. `PROPAGATION_REQUIRES_NEW` Óë `PROPAGATION_NESTED` + - `PROPAGATION_REQUIRES_NEW`£ºÐÂÊÂÎñÖ´ÐÐÍê³Éºó£¬¾ÉÊÂÎñ±¨´í£¬Ö»»Ø¹ö¾ÉÊÂÎñ£¬ÐÂÊÂÎñ²»»Ø¹ö£»ÐÂÊÂÎñÖ´Ðб¨´í£¬Ð¾ÉÊÂÎñÒ»Æð»Ø¹ö + - `PROPAGATION_NESTED`£º×ÓÊÂÎñÖ´ÐÐÍê³Éºó£¬¸¸ÊÂÎñ±¨´í£¬»Ø¹öµ½±£´æµã£»×ÓÊÂÎñÖ´Ðб¨´í£¬Ò²Êǻعöµ½±£´æµã + +### 2. demo ×¼±¸ + +Ã÷È·ÁËÒÔÉϸÅÄîºó£¬½ÓÏÂÀ´¾Í¿ªÊ¼·ÖÎöÁË£¬ÎÒÃÇÏÈ×¼±¸¸ö¼òµ¥µÄ demo£º + +ÏÈÊÇ×¼±¸Ò»Ð©ÅäÖ㺠+ +``` +@Configuration +@ComponentScan("org.springframework.learn.tx.demo03") +@EnableTransactionManagement(proxyTargetClass = true) +public class TxDemo03Config { + + /** + * Éú³ÉÊý¾ÝÔ´ + */ + @Bean + public DataSource dataSource() throws Exception { + Driver driver = new com.mysql.jdbc.Driver(); + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "123"; + return new SimpleDriverDataSource(driver, url, username, password); + } + + /** + * Éú³ÉjdbcTemplate£¬ºóÃæ¾ÍÊÇÓÃÕâ¸öÀàÀ´´¦ÀíÊý¾Ý¿âµÄ²Ù×÷ + */ + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + + /** + * ÊÂÎñ¹ÜÀíÆ÷ + */ + @Bean + public DataSourceTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + +} +``` + +¶ÔÒÔÉÏ´úÂë˵Ã÷ÈçÏ£º + +- Êý¾ÝÔ´£ºÊ¹ÓõÄÊÇ spring ÌṩµÄ `SimpleDriverDataSource`£¬Õâ¸öÊý¾ÝÔ´¹¦Äܲ»¶à£¬ÊʺÏÕâ¸ö¼òµ¥µÄ demo +- ´¦Àí jdbc Ïà¹Ø²Ù×÷£ºÒ²ÊÇʹÓà spring ÌṩµÄ `jdbcTemplate`£¬×÷Ϊһ¸ö¼òµ¥µÄ demo£¬²»ÏëÒýÈë `mybatis`£¬`jpa` µÈ +- ÊÂÎñ¹ÜÀíÆ÷£ºÊ¹ÓõÄÒ²ÊÇ spring ÌṩµÄ `DataSourceTransactionManager` £¬¶Ôµ¥Êý¾ÝÔ´À´Ëµ£¬Õâ¸öÍêÈ«¹»ÓÃÁË + +×¼±¸Ò»¸ö mysql µÄ²Ù×÷£¬Òª¿ªÆôÊÂÎñ£º + +``` +@Service +public class UserService { + + @Autowired + private JdbcTemplate jdbcTemplate; + + /** + * Êý¾Ý¿â²åÈë²Ù×÷£¬Ê¹Óà @Transactional ¿ªÆôÊÂÎñ + */ + @Transactional(rollbackFor = Exception.class) + public int insert() { + String sql = "insert into `user`(`login_name`, `nick`, `create_time`, `update_time`)" + + "values (?, ?, ?, ?)"; + int result = jdbcTemplate.update(sql, "test", "test", new Date(), new Date()); + if(true) { + //throw new RuntimeException("Å׳ö¸öÒì³£"); + } + System.out.println(result); + return result; + } + +} +``` + +×îºóÊÇÖ÷Àࣺ + +``` +public class TxDemo03Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext applicationContext + = new AnnotationConfigApplicationContext(TxDemo03Config.class); + UserService userService = applicationContext.getBean(UserService.class); + userService.insert(); + + } +} +``` + +Õâ¸ö demo Ê®·Ö¼òµ¥£¬¾Í²»¶à×ö·ÖÎöÁË£¬ÎÒÃǽÓÏÂÀ´»áͨ¹ýÕâ¸ö demo ½øÐÐһЩµ÷¶È²Ù×÷£¬Ì½¾¿Ï spring ¶ÔÊÂÎñµÄ¸ôÀë¼¶±ð¡¢´«²¥·½Ê½µÄ´¦Àí¡£ + +### 3. `TransactionAspectSupport#invokeWithinTransaction` + +±¾ÎÄÒ»¿ªÆªÎÒÃǾÍ˵¹ý£¬ÊÂÎñµÄ´¦ÀíÔÚ `TransactionAspectSupport#invokeWithinTransaction` ·½·¨£¬½ÓÏÂÀ´ÎÒÃǽ«Öصã·ÖÎöÕâ¸ö·½·¨¡£ + +ÉÏ´úÂ룺 + +``` +protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, + final InvocationCallback invocation) throws Throwable { + TransactionAttributeSource tas = getTransactionAttributeSource(); + + // 1. »ñÈ¡ @Transactional µÄÊôÐÔÅäÖà + final TransactionAttribute txAttr = (tas != null + ? tas.getTransactionAttribute(method, targetClass) : null); + + // 2. »ñÈ¡ÊÂÎñ¹ÜÀíÆ÷£¨IOCÈÝÆ÷ÖлñÈ¡£© + final TransactionManager tm = determineTransactionManager(txAttr); + + // Õⲿ·ÖµÄ´úÂëÊÇÕë¶Ô TransactionManager ÊÇ ReactiveTransactionManager µÄÇé¿ö£¬²»×÷·ÖÎö + ... + + // 3. ½« TransactionManager ת»»Îª PlatformTransactionManager + PlatformTransactionManager ptm = asPlatformTransactionManager(tm); + // 4. »ñÈ¡·½·¨µÄÈ«ÏÞ¶¨Ãû£¬¸ñʽΪ£º"°üÃû.ÀàÐÍ.·½·¨Ãû" + final String joinpointIdentification + = methodIdentification(method, targetClass, txAttr); + if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { + // 5. »ñÈ¡ÊÂÎñÐÅÏ¢£¬»áÔÚÕâÀ↑ÆôÊÂÎñ + TransactionInfo txInfo = createTransactionIfNecessary( + ptm, txAttr, joinpointIdentification); + Object retVal; + try { + // 6. Ö´ÐоßÌåµÄÒµÎñ + retVal = invocation.proceedWithInvocation(); + } + catch (Throwable ex) { + // 7. Òì³£»Ø¹ö + completeTransactionAfterThrowing(txInfo, ex); + throw ex; + } + finally { + // 8. ÖØÖÃÊÂÎñÐÅÏ¢£¬¾ÍÊǽ«ÊÂÎñÐÅÏ¢ÉèÖÃΪ¾ÉµÄ + cleanupTransactionInfo(txInfo); + } + + if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { + TransactionStatus status = txInfo.getTransactionStatus(); + if (status != null && txAttr != null) { + retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); + } + } + // 9. Ìá½»ÊÂÎñ + commitTransactionAfterReturning(txInfo); + return retVal; + } + else { + // ´¦Àí CallbackPreferringPlatformTransactionManager ÀàÐ굀 TransactionManager£¬²»×÷·ÖÎö + ... + } +} +``` + +¶Ô `TransactionAspectSupport#invokeWithinTransaction` ·½·¨µÄÄÚÈÝ£¬×¢ÊÍÖÐÒÑÏêϸ˵Ã÷£¬½ÓÏÂÀ´ÎªÎÒÃÇÀ´·ÖÎöÕâЩִÐйý³Ì¡£ + +#### 3.1 »ñÈ¡ `@Transactional` µÄÊôÐÔÅäÖà + +Õâ¸ö¾ÍÊÇ»ñÈ¡ `UserService#insert` ·½·¨Éϱê¼ÇµÄ `@Transactional` µÄÊôÐÔÅäÖ㬵õ½µÄ½á¹ûÈçÏ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-aefbe56da2db53982f73092a191e587fcc8.png) + +#### 3.2 »ñÈ¡ÊÂÎñ¹ÜÀíÆ÷ + +»ñÈ¡ÊÂÎñ¹ÜÀíÆ÷µÄ·½·¨Îª `TransactionAspectSupport#determineTransactionManager`£¬Ö±½Ó¿´´úÂ룺 + +``` +protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) { + if (txAttr == null || this.beanFactory == null) { + return getTransactionManager(); + } + + // Èç¹ûÔÚ @Transaction ×¢½âÉÏÖ¸¶¨ÁËÊÂÎñ¹ÜÀíÆ÷£¬¾Í´ÓspringÈÝÆ÷ÖлñÈ¡Õâ¸öÊÂÎñ¹ÜÀíÆ÷ + String qualifier = txAttr.getQualifier(); + if (StringUtils.hasText(qualifier)) { + return determineQualifiedTransactionManager(this.beanFactory, qualifier); + } + // Ö¸¶¨ÊÂÎñ¹ÜÀíÆ÷Ãû³Æ£¬×îÖÕÒ²ÊÇ´ÓspringÈÝÆ÷ÖлñÈ¡ + else if (StringUtils.hasText(this.transactionManagerBeanName)) { + return determineQualifiedTransactionManager( + this.beanFactory, this.transactionManagerBeanName); + } + else { + // Ö¸¶¨ÊÂÎñ¹ÜÀíÆ÷¶ÔÏó£¬Ö±½Ó·µ»Ø + TransactionManager defaultTransactionManager = getTransactionManager(); + if (defaultTransactionManager == null) { + // ´Ó»º´æÖлñȡĬÈ쵀 + defaultTransactionManager = this.transactionManagerCache + .get(DEFAULT_TRANSACTION_MANAGER_KEY); + if (defaultTransactionManager == null) { + // ¸ù¾ÝÀàÐÍ´Ó spring ÈÝÆ÷Öлñȡһ¸öÊÂÎñ¹ÜÀíÆ÷ + defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class); + this.transactionManagerCache.putIfAbsent( + DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); + } + } + return defaultTransactionManager; + } +} +``` + +´úÂëËäÈ»Óе㳤£¬µ«Âß¼­·Ç³£ÇåÎú£¬ÕâÀï¶Ô¸Ã·½·¨µÄÁ÷³Ì×ܽáÈçÏ£º + +1. Èç¹ûÔÚ `@Transaction` ×¢½âÉÏÖ¸¶¨ÁËÊÂÎñ¹ÜÀíÆ÷£¬¾Í´Ó spring ÈÝÆ÷ÖлñÈ¡¸ÃÊÂÎñ¹ÜÀíÆ÷ +2. Èç¹ûÖ¸¶¨ÊÂÎñ¹ÜÀíÆ÷Ãû³Æ£¬¾Í´Ó spring ÈÝÆ÷ÖлñÈ¡¸ÃÊÂÎñ¹ÜÀíÆ÷ +3. Èç¹ûÖ¸¶¨ÊÂÎñ¹ÜÀíÆ÷¶ÔÏó£¬Ö±½Ó·µ»Ø +4. ÒÔÉ϶¼²»Âú×㣬ֱ½Ó´Ó spring ÈÝÆ÷ÖлñÈ¡ÀàÐÍΪ `TransactionManager` µÄ bean + +ÔÚ `TxDemo03Config` ÖУ¬ÎÒÃÇÅäÖÃÊÂÎñ¹ÜÀíÆ÷Ϊ `DataSourceTransactionManager`£º + +``` +public DataSourceTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); +} +``` + +ÕâÀïµÃµ½µÄÒ²ÊÇ `DataSourceTransactionManager`£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-34ac74501b184c44f95432b31b21a041619.png) + +#### 3.3 ½« `TransactionManager` ת»»Îª `PlatformTransactionManager` + +Õâ¸öûɶºÃ˵µÄ£¬`DataSourceTransactionManager` ¾ÍÊÇ `PlatformTransactionManager` µÄ×ÓÀ࣬´úÂëÀï×öÁËÒ»¸öÀàÐÍת»»¡£ + +``` +private PlatformTransactionManager asPlatformTransactionManager( + @Nullable Object transactionManager) { + if (transactionManager == null || transactionManager instanceof PlatformTransactionManager) { + return (PlatformTransactionManager) transactionManager; + } else { + // Å׸öÒì³£ + ... + } +} +``` + +#### 3.4 »ñÈ¡·½·¨µÄÈ«ÏÞ¶¨Ãû + +ÕâÒ»²½»áÄõ½·½·¨µÄÈ«ÏÞ¶¨Ãû£¬¸ñʽΪ£º"°üÃû¡£ÀàÐÍ¡£·½·¨Ãû"£¬ÕâҲûɶºÃ˵µÄ£¬ÕâÒ»²½µÃµ½µÄ½á¹ûÈçÏ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-656a73b9eb1cd3120d3586fe4f1302de373.png) + +ÏÞÓÚÆª·ù£¬±¾ÎľÍÏÈ·ÖÎöµ½ÕâÀïÁË£¬ÏÂÆªÎÒÃǼÌÐø¡£ + +------ + +*±¾ÎÄÔ­ÎÄÁ´½Ó£ºhttps://my.oschina.net/funcy/blog/4773459 £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£* \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" new file mode 100644 index 0000000..f086983 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\214\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\346\211\247\350\241\214\346\265\201\347\250\213.md" @@ -0,0 +1,664 @@ +ÔÚ [spring ÊÂÎñÖ®ÈÏʶÊÂÎñ×é¼þ](https://my.oschina.net/funcy/blog/4773454)Ò»ÎÄÖУ¬ÎÒÃÇͨ¹ýÒ»¸ö demo ÑÝʾÁËÈçºÎʹÓà spring ÊÂÎñ¹ÜÀí¹¦ÄÜ£¬È»ºó·ÖÎöÁË `@EnableTransactionManagement` ×¢½âµÄ¹¦ÄÜ£¬±¾ÎĽ«¼ÌÐø·ÖÎö spring ÊÂÎñÏà¹Ø´úÂë¡£ + +### 1\. ´úÀí¶ÔÏó´´½¨Á÷³Ì + +spring ÊÂÎñ¹ÜÀí¹¦ÄÜÊÇ»ùÓÚ aop µÄ£¬Ê¹ÓôúÀí¶ÔÏóÀ´½øÐÐÊÂÎñµÄһϵÁвÙ×÷£¬±¾ÎĽ«Í¨¹ýµ÷ÊԵķ½Ê½À´·ÖÎö´úÀí¶ÔÏóµÄ´´½¨¹ý³Ì¡£ + +ÔÚ [spring ÊÂÎñÖ®ÈÏʶÊÂÎñ×é¼þ](https://my.oschina.net/funcy/blog/4773454)ÖÐÎÒÃÇͨ¹ý·ÖÎö `@EnableTransactionManagement` ×¢½â£¬·¢ÏÖ¸Ã×¢½â»áÏò spring ÈÝÆ÷ÖÐ×¢²á `InfrastructureAdvisorAutoProxyCreator`£¬Õâ¸öÀàÊÇ `AbstractAdvisorAutoProxyCreator` µÄ×ÓÀ࣬ÓÃÀ´Éú³É´úÀí¶ÔÏóµÄ£¬±¾½Ú½«»ùÓÚ `InfrastructureAdvisorAutoProxyCreator` À´·ÖÎö¶ÔÏóµÄ´´½¨¹ý³Ì¡£ + +> ¹ØÓÚ `AbstractAdvisorAutoProxyCreator` µÄ·ÖÎöÒÔ¼°´úÀí¶ÔÏóµÄÉú³É£¬ÔÚ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817) Óë [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)ÒѾ­×÷ÁËÏêϸ·ÖÎö£¬ÕâÀïÎÒÃÇÖ÷Òª·ÖÎöÓë aop ÓвîÒìµÄµØ·½£¬ÐèÒªÏêϸÁ˽â spring aop ´úÀí¶ÔÏóÈçºÎ²úÉúµÄС»ï°é£¬¿ÉÒÔÔĶÁÉÏÊöÁ½ÆªÎÄÕ¡£ + +ÎÒÃÇÒÀ¾ÉÊǽøÈë `AbstractAutoProxyCreator#postProcessBeforeInitialization`£º + +``` +public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { + ... + if (...) { + //1\. shouldSkip: + // - AspectJAwareAdvisorAutoProxyCreator µÄ shouldSkip ·½·¨»á´¦Àí @Aspect ×¢½âµÄÀ࣬ + // ½«ÆäÖеÄ@Before/@After/@AroundµÈ×¢½â°üװΪAdvisor£¬ÔÙµ÷Óø¸Àà(Ò²¾ÍÊÇ + // AbstractAutoProxyCreator)µÄshouldSkip·½·¨ + // - InfrastructureAdvisorAutoProxyCreatorÖ±½ÓÖ´ÐÐAbstractAutoProxyCreatorµÄshouldSkip·½·¨ + if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return null; + } + } + if(...) { + ... + + // 2\. getAdvicesAndAdvisorsForBean£º»ñÈ¡ÊÊÓÃÓÃÓÚµ±Ç°¶ÔÏóµÄadvisor + Object[] specificInterceptors = getAdvicesAndAdvisorsForBean( + beanClass, beanName, targetSource); + Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); + ... + return proxy; + } + return null; +} + +``` + +Õâ¸ö·½·¨ÓÐÁ½´¦²»Í¬µã£¬´úÂëÖÐÒѾ­×¢Ã÷ÁËÆäÖеIJîÒ죬¹ØÓÚ `shouldSkip`£¬Ã»É¶ºÃ˵µÄ£¬ÎÒÃÇÖØµãÕ¹¿ª `getAdvicesAndAdvisorsForBean(...)` ·½·¨¡£ + +#### 1.1 `BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans` + +һ·¸ú½ø `getAdvicesAndAdvisorsForBean(...)` ·½·¨£¬ÆäÖеIJÙ×÷Óë `AspectJAwareAdvisorAutoProxyCreator` µÄ²Ù×÷²¢²»Ì«´óÇø±ð£¬²»¹ýÓиö·½·¨¸öÈËÈÏΪÐèҪǿµ÷Ï£¬·½·¨ÈçÏ£º + +> BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans + +``` +public List findAdvisorBeans() { + String[] advisorNames = this.cachedAdvisorBeanNames; + if (advisorNames == null) { + // ²éÕÒµ±Ç°beanFactoryÖÐËùÓÐ Advisor µÄ bean class + // Advisor¿ÉÒÔÊÇÓû§ÊµÏÖAdvisorÏà¹Ø½Ó¿Ú£¬Ò²¿ÉÒÔÊÇxmlÖ¸¶¨µÄ + advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + this.beanFactory, Advisor.class, true, false); + this.cachedAdvisorBeanNames = advisorNames; + } + ... + List advisors = new ArrayList<>(); + for (String name : advisorNames) { + ... + // ¸ù¾ÝadvisorµÄbean name£¬´ÓspringÈÝÆ÷ÖлñÈ¡ bean + advisors.add(this.beanFactory.getBean(name, Advisor.class)); + ... + } + ... + return advisors; +} + +``` + +Õâ¸ö·½·¨Ö÷ÒªµÄ×÷ÓÃÊÇ»ñÈ¡ spring ÈÝÆ÷ÖеÄËùÓÐ `advisor`£¬ÆäʵÔÚ `AnnotationAwareAspectJAutoProxyCreator` ÖÐÒ²ÊÇÕâô»ñÈ¡µÄ£¬Ö»²»¹ýÔÚ»ñȡǰ£¬`AnnotationAwareAspectJAutoProxyCreator` »áÔÚ `shouldSkip(...)` ·½·¨ÖÐ°Ñ `@Aspect` ÀàÖаüº¬ `@Befor/@After/@Around` µÈ×¢½âµÄ·½·¨°ü×°³É¶ÔÓ¦µÄ `Advisor`£¬¶ø `InfrastructureAdvisorAutoProxyCreator` Ôò²»»á£¬±¾½ÚÒ»¿ªÊ¼Ò²Ìáµ½¹ýÁË¡£ + +ÔÚ [spring ÊÂÎñÖ®ÈÏʶÊÂÎñ×é¼þ](https://my.oschina.net/funcy/blog/4773454)Ò»ÎÄÖУ¬·ÖÎö `@EnableTransactionManagement` ×¢½â¹¦ÄÜʱ£¬ÎÒÃÇ·ÖÎöµ½¸Ã×¢½â»áͨ¹ý `@Bean` ×¢½âÏò spring ÖÐÒýÈë `BeanFactoryTransactionAttributeSourceAdvisor`£¬Õâ¸ö bean ¾Í»áÔÚ `BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans` ±»»ñÈ¡µ½¡£ + +#### 1.2 `AopUtils#canApply(...)` + +¸ú×Å·½·¨Ò»Â·ÍùÏÂ×ߣ¬½ÓמÍÀ´µ½ÁËÅÐ¶Ï `advisor` ÄÜ·ñÊÊÓÃÓÚÄ¿±ê `class` µÄµØ·½ÁË£º + +``` +/** + * ÅжÏadvisorÄÜ·ñÊÊÓÃÓÚÄ¿±êclass + */ +public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) { + ... + // ÅжÏÊÇ·ñΪ PointcutAdvisor£¬´¦ÀíÊÂÎñµÄadvisorΪBeanFactoryTransactionAttributeSourceAdvisor£¬ + // ËüʵÏÖÁËPointcutAdvisor£¬Òò´ËÏÂÃæµÄ´úÂë»áÖ´ÐÐ + else if (advisor instanceof PointcutAdvisor) { + PointcutAdvisor pca = (PointcutAdvisor) advisor; + //ʹÓà PointcutAdvisor ¼ÌÐøÅÐ¶Ï + return canApply(pca.getPointcut(), targetClass, hasIntroductions); + } + ... +} + +/** + * ÅжÏadvisorÄÜ·ñÊÊÓÃÓÚÄ¿±êclass + */ +public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) { + Assert.notNull(pc, "Pointcut must not be null"); + //1\. ÇеãÉÏÊÇ·ñ´æÔÚÅųýÀàµÄÅäÖà + if (!pc.getClassFilter().matches(targetClass)) { + return false; + } + // »ñÈ¡·½·¨Æ¥Åä¶ÔÏó£¬MethodMatcher.TRUE ΪĬÈ쵀 MethodMatcher ¶ÔÏó + MethodMatcher methodMatcher = pc.getMethodMatcher(); + if (methodMatcher == MethodMatcher.TRUE) { + return true; + } + ... + // classes°üº¬targetClass¡¢³ýObjectµÄËùÓи¸Àà¡¢ËùÓÐ½Ó¿Ú + Set> classes = new LinkedHashSet<>(); + // Ê¡ÂÔ»ñÈ¡targetClassµÄ¸¸ÀಽÖè + ... + for (Class clazz : classes) { + // »ñÈ¡ clazz ¶¨ÒåµÄ·½·¨£¬°üÀ¨µ±Ç°ÀàµÄ·½·¨¡¢³ýObjectÍâµÄËùÓи¸Àà·½·¨¡¢½Ó¿ÚµÄĬÈÏ·½·¨ + Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); + for (Method method : methods) { + // 2\. Æ¥ÅäµÄ¹Ø¼üÔÚÕâÀï + if (introductionAwareMethodMatcher != null ? + introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : + methodMatcher.matches(method, targetClass)) { + return true; + } + } + } + return false; +} + +``` + +ÕâÒ»¿éµÄ´úÂëÓë `AnnotationAwareAspectJAutoProxyCreator` һģһÑù£¬ËûÃǶ¼Êǵ÷ÓÃͬÑùµÄ·½·¨À´Åжϣ¬µ«ÓÉÓÚ´«ÈëµÄµÄ `advisor` ²»Í¬£¬×îÖÕµ÷Óõ½µÄ¾ßÌåµÄÆ¥Å乿ÔòÒ²²»Ïàͬ¡£ + +´Ó´úÂëµÄ·ÖÎöÀ´¿´£¬Æ¥ÅäµÄÂß¼­¶¼ÔÚ `Pointcut` ÖУ¬¶ø `Pointcut` ÓÖÀ´×ÔÓÚ `Advisor`£¬¿É¼û `Advisor` Ê®·Ö¹Ø¼ü¡£´¦ÀíÊÂÎñµÄ `Advisor` Ϊ `BeanFactoryTransactionAttributeSourceAdvisor`£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÕâ¸öÀà¡£ + +#### 1.3 `BeanFactoryTransactionAttributeSourceAdvisor` Æ¥Å乿Ôò·ÖÎö + +´ÓÉÏһС ½ÚµÄ·ÖÎöÖУ¬ÎÒÃÇÖªµÀÅÐ¶Ï `targetClass` ÄÜ·ñÓ¦Óõ±Ç° `advisor` µÄ¹æÔòÀ´Ô´ÓÚ `advisor` µÄ `pointcut`£¬`pointcut` ÓÐÁ½¸öµØ·½°üº¬ÁËÅжϹæÔò£º + +* Æ¥ÅäÀࣺ`pc.getClassFilter().matches(targetClass)` +* Æ¥Åä·½·¨£º`pc.getMethodMatcher().matches(method, targetClass)` + +ÕâһС ½ÚÎÒÃÇ´Ó `BeanFactoryTransactionAttributeSourceAdvisor` ÈëÊÖ£¬Ò»²½²½·ÖÎöÆ¥Å乿Ôò¡£ + +``` +public class BeanFactoryTransactionAttributeSourceAdvisor + extends AbstractBeanFactoryPointcutAdvisor { + + @Nullable + private TransactionAttributeSource transactionAttributeSource; + + /** + * Õâ¸ö¾ÍÊÇ pointcut + */ + private final TransactionAttributeSourcePointcut pointcut = + new TransactionAttributeSourcePointcut() { + @Override + @Nullable + protected TransactionAttributeSource getTransactionAttributeSource() { + return transactionAttributeSource; + } + }; + + /** + * ÉèÖà transactionAttributeSource + */ + public void setTransactionAttributeSource(TransactionAttributeSource + transactionAttributeSource) { + this.transactionAttributeSource = transactionAttributeSource; + } + + /** + * ÉèÖà ClassFilter + */ + public void setClassFilter(ClassFilter classFilter) { + this.pointcut.setClassFilter(classFilter); + } + + /** + * »ñÈ¡ pointcut + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } +} + +``` + +ÉÏÃæµÄ´úÂë¹Ø¼ü²¿·ÖÒѾ­×¢ÊÍÁË£¬ÕâÀïÀ´×ܽáÏ£º`BeanFactoryTransactionAttributeSourceAdvisor#getPointcut` µÃµ½µÄ `pointcut` Ϊ `TransactionAttributeSourcePointcut`£¬¾ÍÊÇÔÚ `private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {...}` Öд´½¨µÄ¡£ + +`BeanFactoryTransactionAttributeSourceAdvisor` µÄ `transactionAttributeSource` ÊÇÊ²Ã´ÄØ£¿»ØÒäÏ `ProxyTransactionManagementConfiguration` Öд´½¨ `transactionAdvisor` µÄ´úÂ룺 + +``` +public class ProxyTransactionManagementConfiguration + extends AbstractTransactionManagementConfiguration { + + // Ê¡ÂÔÆäËû + ... + + /** + * ¶ÁÈ¡SpringµÄ @Transactional ×¢½â£¬²¢½«ÏàÓ¦µÄÊÂÎñÊôÐÔ¹«¿ª¸øSpringµÄÊÂÎñ»ù´¡½á¹¹ + */ + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public TransactionAttributeSource transactionAttributeSource() { + return new AnnotationTransactionAttributeSource(); + } + + /** + * ÊÂÎñÔöÇ¿Æ÷. + * transactionAttributeSource£ºtransactionAttributeSource() ·µ»ØµÄ¶ÔÏó + */ + @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( + TransactionAttributeSource transactionAttributeSource, + TransactionInterceptor transactionInterceptor) { + BeanFactoryTransactionAttributeSourceAdvisor advisor = + new BeanFactoryTransactionAttributeSourceAdvisor(); + // ÉèÖÃÊÂÎñÊôÐÔÀ࣬ÓÃÀ´±£´æ @Transactional µÄÊôÐÔ + advisor.setTransactionAttributeSource(transactionAttributeSource); + ... + return advisor; + } + +} + +``` + +ÓÉ´Ë¿ÉÖª£¬`BeanFactoryTransactionAttributeSourceAdvisor` µÄ `transactionAttributeSource` ÊôÐÔΪ `AnnotationTransactionAttributeSource`. + +ÎÒÃÇÔٻص½ `BeanFactoryTransactionAttributeSourceAdvisor`£¬´ÓÉÏÃæµÄ·ÖÎö¿ÉÖª£¬`getPointcut()` µÃµ½µÄÊÇ `TransactionAttributeSourcePointcut` ¶ÔÏó£¬È»ºó½øÈëÕâ¸öÀࣺ + +``` +abstract class TransactionAttributeSourcePointcut + extends StaticMethodMatcherPointcut implements Serializable { + + protected TransactionAttributeSourcePointcut() { + // ÔÚ¹¹Ôì·½·¨ÖÐÉèÖà ClassFilter + setClassFilter(new TransactionAttributeSourceClassFilter()); + } + + /** + * pointcut µÄ matches ·½·¨ + */ + @Override + public boolean matches(Method method, Class targetClass) { + // µÃµ½µÄ½á¹ûΪAnnotationTransactionAttributeSource + TransactionAttributeSource tas = getTransactionAttributeSource(); + return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); + } + + /** + * ÔÚ BeanFactoryTransactionAttributeSourceAdvisor ÖÐÒÑÖ¸¶¨ + */ + @Nullable + protected abstract TransactionAttributeSource getTransactionAttributeSource(); + + /** + * ÄÚ²¿À࣬ʵÏÖÁË ClassFilter + */ + private class TransactionAttributeSourceClassFilter implements ClassFilter { + + /** + * ClassFilter µÄ matches + */ + @Override + public boolean matches(Class clazz) { + // ÊÇ·ñΪTransactionalProxy¡¢PlatformTransactionManager¡¢PersistenceExceptionTranslatorµÄʵÏÖÀà + if (TransactionalProxy.class.isAssignableFrom(clazz) || + PlatformTransactionManager.class.isAssignableFrom(clazz) || + PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) { + return false; + } + //ÅÐ¶Ï TransactionAttributeSource »ñÈ¡ÊÂÎñÊôÐÔÊÇ·ñΪ¿Õ + // µÃµ½µÄ½á¹ûΪAnnotationTransactionAttributeSource + TransactionAttributeSource tas = getTransactionAttributeSource(); + return (tas == null || tas.isCandidateClass(clazz)); + } + } + +} + +``` + +´ÓÉÏÃæµÄ·½·¨£¬ÎÒÃǵõ½ÁËÒ»¸öÖØÒªµÄ¹æÔò£º + +* Æ¥ÅäÀࣺ`pc.getClassFilter().matches(targetClass)`£¬`ClassFilter` Ϊ `TransactionAttributeSourceClassFilter` + +Æ¥ÅäÀàµÄ¹æÔòÊÇÕÒµ½ÁË£¬ÄÇÆ¥Åä·½·¨µÄ¹æÔòÄØ£¿ÎÒÃǽøÈë `TransactionAttributeSourcePointcut#getMethodMatcher()` ·½·¨£¬½øÈëµÄÊÇ `StaticMethodMatcherPointcut`£º + +``` +public abstract class StaticMethodMatcherPointcut + extends StaticMethodMatcher implements Pointcut { + // Ê¡ÂÔÁËһЩ´úÂë + ... + + @Override + public final MethodMatcher getMethodMatcher() { + return this; + } +} + +``` + +·µ»ØµÄ¾¹È»ÊÇ `this`£¡ÕâÊǸöɶ£¿²»Òª»Å£¬ÔÙϸ¿´ `TransactionAttributeSourcePointcut`£¬·¢ÏÖËü¼Ì³ÐÁË `StaticMethodMatcherPointcut`£º + +``` +abstract class TransactionAttributeSourcePointcut + extends StaticMethodMatcherPointcut implements Serializable { + // Ê¡ÂÔÁËһЩ´úÂë + ... +} + +``` + +ËùÒÔ£¬`pc.getMethodMatcher()` µÃµ½µÄ¾ÍÊÇ `TransactionAttributeSourcePointcut`£¬Æä `mathes(...)` ·½·¨¾ÍÊÇ `TransactionAttributeSourcePointcut#matches`. + +ÔÚ±¾Ð¡½ÚµÄ×îºó£¬ÎÒÃÇÀ´×ܽáÏ·ÖÎöµÄ½á¹û£º + +* Æ¥ÅäÀࣺ`pc.getClassFilter().matches(targetClass)`£¬`ClassFilter` Ϊ `TransactionAttributeSourceClassFilter`£» +* Æ¥Åä·½·¨£º`pc.getMethodMatcher().matches(method, targetClass)`£¬`methodMatcher` Ϊ `TransactionAttributeSourcePointcut`£» +* ÔÚÉÏÃæÁ½¸ö¹æÔòÖУ¬¶¼»áµ÷Óà `TransactionAttributeSourcePointcut#getTransactionAttributeSource`£¬Õâ¸ö·½·¨·µ»ØµÄ½á¹ûΪ `AnnotationTransactionAttributeSource`. + +#### 1.4 Æ¥ÅäÁ÷³Ì + +ÔÚ 1.2 ²¿·Ö£¬ÎÒÃÇÖªµÀ£¬µ±Ç° `advisor` ÄÜ·ñÓ¦ÓÃÓÚÄ¿±ê class£¬ÐèҪͬʱÂú×ãÁ½¸öÆ¥Å乿Ôò£º + +* Æ¥ÅäÀࣺ`pc.getClassFilter().matches(targetClass)`£¬`ClassFilter` Ϊ `TransactionAttributeSourceClassFilter`£» +* Æ¥Åä·½·¨£º`pc.getMethodMatcher().matches(method, targetClass)`£¬`methodMatcher` Ϊ `TransactionAttributeSourcePointcut`£» + +ÎÒÃÇÀ´¿´ÏÂÕâÁ½¸ö·½·¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-133740c93d470e16ec8dc9a34106adb8fc8.png) + +* `TransactionAttributeSourceClassFilter#matches`£ºÏÈÅжϵ±Ç°ÀàÊÇ·ñΪÊÇ·ñΪ `TransactionalProxy`¡¢`PlatformTransactionManager`¡¢`PersistenceExceptionTranslator` »òÆäʵÏÖÀ࣬Ȼºóµ÷Óà `AnnotationTransactionAttributeSource#isCandidateClass` ¼ÌÐøÅжϣ» +* `TransactionAttributeSourcePointcut#matches`£ºµ÷Óà `AnnotationTransactionAttributeSource#getTransactionAttribute`£¨ÓÉÓڼ̳йØÏµ£¬Êµ¼Êµ÷ÓõÄÊÇ `AbstractFallbackTransactionAttributeSource#getTransactionAttribute`£©Åжϡ£ + +½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöϾßÌ寥ÅäÁ÷³Ì¡£ + +##### `AnnotationTransactionAttributeSource#isCandidateClass` + +ÈÃÎÒÃÇÖ±±¼Ö÷Ì⣬½øÈë `isCandidateClass` ·½·¨£º + +> AnnotationTransactionAttributeSource#isCandidateClass + +``` +@Override +public boolean isCandidateClass(Class targetClass) { + // ÕÒµ½ËùÓеÄannotationParsers£¬Ñ­»·Æ¥Åä + for (TransactionAnnotationParser parser : this.annotationParsers) { + if (parser.isCandidateClass(targetClass)) { + return true; + } + } + return false; +} + +``` + +¿ÉÒÔ¿´µ½£¬ÕâÊÇÔÚÑ­»·Àïµ÷Óà `TransactionAnnotationParser` µÄ `isCandidateClass` ·½·¨¡£`this.annotationParsers` ÊÇɶĨ£¿Í¨¹ýµ÷ÊÔ£¬ÄÚÈÝÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-5e16d6769e5a74c2f3064afdd090de08d42.png) + +`this.annotationParsers` ÖÐÖ»ÓÐ `SpringTransactionAnnotationParser`£¬ÎÒÃǽøÈëÆä `isCandidateClass` ·½·¨£º + +``` +public class SpringTransactionAnnotationParser + implements TransactionAnnotationParser, Serializable { + + /** + * ÅжÏÀàÉÏÊÇ·ñÓÐ @Transactional ×¢½â + */ + @Override + public boolean isCandidateClass(Class targetClass) { + return AnnotationUtils.isCandidateClass(targetClass, Transactional.class); + } +} + +``` + +Õâ¸ö·½·¨×îÖÕµ÷ÓõÄÊÇ `AnnotationUtils.isCandidateClass`£¬ÓÃÀ´ÅжÏÖ¸¶¨µÄÀàÉÏÊÇ·ñÓÐ `@Transactional` ×¢½â¡£ + +µ½ÕâÀÎÒÃǾÍÃ÷°×ÁË£¬`TransactionAttributeSourceClassFilter#matches` ÏÈÅųýһЩÀà (`TransactionalProxy`/`PlatformTransactionManager`/`PersistenceExceptionTranslator` ¼°Æä×ÓÀà) ºó£¬×îÖÕ»áÆ¥Åä´øÓÐ `@Transactional` ×¢½âµÄÀà¡£ + +##### `AnnotationTransactionAttributeSource#getTransactionAttribute` + +ÉÏÃæµÄ·½·¨Æ¥Åä³É¹¦ºó£¬²¢²»Äܱíʾ³É¹¦Æ¥Å䣬»¹µÃÆ¥Åä `TransactionAttributeSourcePointcut#matches`£¬Á½ÕßͬʱÂú×ã²Å»áÆ¥Åä³É¹¦¡£`TransactionAttributeSourcePointcut#matches` µ÷Óà `AnnotationTransactionAttributeSource#getTransactionAttribute` Íê³ÉÆ¥ÅäµÄ£¬ÎÒÃǸú½øÈ¥£º + +``` +public abstract class AbstractFallbackTransactionAttributeSource + implements TransactionAttributeSource { + + /** + * »ñÈ¡ @Transactional ×¢½âµÄÊôÐÔ + */ + public TransactionAttribute getTransactionAttribute(Method method, + @Nullable Class targetClass) { + if (method.getDeclaringClass() == Object.class) { + return null; + } + + // Ê¡ÂÔ´Ó»º´æÖлñÈ¡ + ... + else { + // »ñÈ¡ Transaction ÊôÐÔ£¬¼´ @Transactional ×¢½âµÄÊôÐÔ + TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); + // Ê¡ÂÔ·ÅÈ뻺´æ²Ù×÷ + ... + return txAttr; + } + } +} + +``` + +`AnnotationTransactionAttributeSource` µÄ `getTransactionAttribute` ÊǼ̳Ð×Ô `AbstractFallbackTransactionAttributeSource` µÄ£¬Òò´ËÎÒÃǽøÈëµÄ·½·¨ÊÇ `AbstractFallbackTransactionAttributeSource#getTransactionAttribute`£¬Õâ¸ö·½·¨ÓÃÀ´»ñÈ¡·½·¨É쵀 `@Transactional` ×¢½âµÄÊôÐÔ£¬ÎÒÃǸú½ø `computeTransactionAttribute(...)`£º + +> AbstractFallbackTransactionAttributeSource + +``` +protected TransactionAttribute computeTransactionAttribute(Method method, + @Nullable Class targetClass) { + // ĬÈϱØÐëÒª public ·½·¨²ÅÖ§³ÖÊÂÎñ + if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { + return null; + } + // 1\. »ñȡȷÇеķ½·¨£¬ÀýÈç´«ÈëµÄclassÊÇIFoo£¬Êµ¼ÊµÄµÄclassÊÇDefaultFoo£¬ + // ÄÇôӦ¸Ã½« IFoo#method ת»»Îª DefaultFoo#method + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + // 2\. ´Ó·½·¨ÉÏ»ñÈ¡ @Transactional µÄÊôÐÔ + TransactionAttribute txAttr = findTransactionAttribute(specificMethod); + if (txAttr != null) { + return txAttr; + } + // 3\. ´ÓÀàÉÏ»ñÈ¡ @Transaction µÄÊôÐÔ + txAttr = findTransactionAttribute(specificMethod.getDeclaringClass()); + if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { + return txAttr; + } + if (specificMethod != method) { + // 4\. È·Çеķ½·¨ÉÏÕÒ²»µ½£¬¾ÍÕÒ´«ÈëµÄ·½·¨É쵀 + txAttr = findTransactionAttribute(method); + if (txAttr != null) { + return txAttr; + } + // 5\. ÒÔÉ϶¼Ã»ÕÒµ½£¬¾ÍÕÒÈ·ÇеÄÀàÉ쵀 + txAttr = findTransactionAttribute(method.getDeclaringClass()); + if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { + return txAttr; + } + } + // 6\. ûÓлñÈ¡µ½£¬×îÖÕ·µ»Ønull + return null; +} + +``` + +´ÓÒÔÉÏ·½·¨µÄÁ÷³Ì£¬¿É×ܽá»ñÈ¡ `@Transactional` ÊôÐÔÁ÷³ÌÈçÏ£º + +1. ½«´«ÈëµÄ·½·¨×ª»»ÎªÈ·Çеķ½·¨£¬ÀýÈç´«ÈëµÄ `class` ÊÇ `IFoo`£¬Êµ¼ÊµÄµÄ `class` ÊÇ `DefaultFoo`£¬ÕâÀï¾Í»á½« `IFoo#method` ת»»Îª `DefaultFoo#method` +2. ´ÓÈ·Çеķ½·¨ÉÏ»ñÈ¡ `@Transactional` µÄÊôÐÔ +3. Èç¹ûûÓлñÈ¡µ½£¬¾Í´ÓÈ·ÇеĴÓÀàÉÏ»ñÈ¡ `@Transaction` µÄÊôÐÔ +4. Èç¹ûûÓлñÈ¡µ½£¬¾Í´«ÈëµÄ·½·¨ÉÏ»ñÈ¡ `@Transaction` µÄÊôÐÔ +5. Èç¹ûûÓлñÈ¡µ½£¬¾Í´«ÈëµÄÀàÉÏ»ñÈ¡ `@Transaction` µÄÊôÐÔ +6. Èç¹ûÒÔÉ϶¼Ã»ÓлñÈ¡µ½£¬¾Í·µ»Ø `null` + +spring ÓÖÊÇÈçºÎ´Ó·½·¨»òÀàÉÏ»ñÈ¡ `@Transactional` µÄÊôÐÔÄØ£¿¼ÌÐø¿´ÏÂÈ¥£º + +> AnnotationTransactionAttributeSource + +``` + // ´Ó·½·¨ÉÏ»ñÈ¡ @Transactional ÊôÐÔ + protected TransactionAttribute findTransactionAttribute(Method method) { + return determineTransactionAttribute(method); + } + + // ´ÓÀàÉÏ»ñÈ¡ @Transactional ÊôÐÔ + protected TransactionAttribute findTransactionAttribute(Class clazz) { + return determineTransactionAttribute(clazz); + } + + // ×îÖÕµ÷Óõķ½·¨ + protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { + for (TransactionAnnotationParser parser : this.annotationParsers) { + // ½âÎö @Transactional ×¢½âµÄÊôÐÔ + TransactionAttribute attr = parser.parseTransactionAnnotation(element); + if (attr != null) { + return attr; + } + } + return null; + } + +``` + +×îÖÕËûÃǶ¼Êǵ÷Óà `AnnotationTransactionAttributeSource#determineTransactionAttribute` À´»ñÈ¡µÄ£¬¶øÔÚ `AnnotationTransactionAttributeSource#determineTransactionAttribute` µ÷ÓÃÁË `TransactionAnnotationParser#parseTransactionAnnotation` ·½·¨¡£¶ÔÓÚ `this.annotationParsers` »¹ÑÛÊìÂð£¬ÔÚÇ°ÃæÒѾ­·ÖÎö¹ýÁË£¬ÀïÃæÖ»ÓÐÒ»¸öÀࣺ`SpringTransactionAnnotationParser`£¬ÎÒÃǸú½øÈ¥£º + +> SpringTransactionAnnotationParser + +``` + /** + * »ñÈ¡ Transactional ×¢½â£¬´æÔÚÔò¼ÌÐø½âÎö£¬²»´æÔÚÔò·µ»Ø null + */ + public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { + // »ñÈ¡ Transactional ×¢½â£¬´æÔÚÔò¼ÌÐø½âÎö£¬²»´æÔÚÔò·µ»Ø null + AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( + element, Transactional.class, false, false); + if (attributes != null) { + return parseTransactionAnnotation(attributes); + } + else { + return null; + } + } + + /** + * ½âÎö Transactional ×¢½âµÄ¾ßÌå²Ù×÷ + */ + protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { + RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); + // ÊÂÎñµÄ´«²¥·½Ê½ + Propagation propagation = attributes.getEnum("propagation"); + rbta.setPropagationBehavior(propagation.value()); + // ÊÂÎñµÄ¸ôÀë¼¶±ð + Isolation isolation = attributes.getEnum("isolation"); + rbta.setIsolationLevel(isolation.value()); + // ÊÂÎñµÄ³¬Ê±Ê±¼ä + rbta.setTimeout(attributes.getNumber("timeout").intValue()); + // ÊÇ·ñΪֻ¶Á + rbta.setReadOnly(attributes.getBoolean("readOnly")); + rbta.setQualifier(attributes.getString("value")); + // ´¦Àí»Ø¹öÒì³£ + List rollbackRules = new ArrayList<>(); + for (Class rbRule : attributes.getClassArray("rollbackFor")) { + rollbackRules.add(new RollbackRuleAttribute(rbRule)); + } + for (String rbRule : attributes.getStringArray("rollbackForClassName")) { + rollbackRules.add(new RollbackRuleAttribute(rbRule)); + } + // ´¦Àí²»»Ø¹öÒì³£ + for (Class rbRule : attributes.getClassArray("noRollbackFor")) { + rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); + } + for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { + rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); + } + rbta.setRollbackRules(rollbackRules); + + return rbta; + } + +``` + +¿ÉÒÔ¿´µ½£¬`Transactional` ×¢½âµÄ¸÷ÊôÐÔ½âÎö³ÉÁË `RuleBasedTransactionAttribute`. + +ÖÁ´Ë£¬ÎÒÃǾÍÃ÷°×ÁË£¬`TransactionAttributeSourcePointcut#matches` ¾ÍÊÇÓÃÀ´ÅжÏÀà»ò·½·¨ÉÏÓÐûÓÐ `Transactional` ×¢½â¡£ + +#### 1.5 ´úÀí¶ÔÏóµÄ´´½¨ + +´úÀí¶ÔÏóµÄ´´½¨ÔÚ `AbstractAutoProxyCreator#postProcessAfterInitialization` ·½·¨ÖÐÍê³ÉµÄ£¬Õâ¸öÁ÷³Ìͬ aop Á÷³ÌÕæµÄÊÇһģһÑù£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË£¬ÏëÁ˽âµÄС»ï°é¿É²é¿´ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)¡£ + +### 2\. ·½·¨µÄÖ´ÐÐ + +·½·¨µÄÖ´Ðз½Ã棬ÊÂÎñÓë aop µÄÖ´ÐÐÁ÷³Ì²¢ÎÞÇø±ð£¬Ò»ÑùÊÇͨ¹ý `Advisor` ÕÒµ½¶ÔÓ¦µÄ `Advice`£¬ÔÙͨ¹ý `Advice` ÕÒµ½¶ÔÓ¦µÄ `methodInterceptor`£¬×îÖÕÖ´ÐеÄÊÇ `MethodInterceptor#invoke` ·½·¨£¬ÔÚÊÂÎñÕâÀï `MethodInterceptor` Ϊ `TransactionInterceptor`£¬Õâ¸öÀàÊÇÔÚ `ProxyTransactionManagementConfiguration` ÖÐͨ¹ý `@Bean` ×¢½âÒýÈëµÄ¡£ + +¹ØÓÚ aop µÄÁ÷³Ì£¬ÎÒÃDz¢²»´òËã·ÖÎö£¬Ïà¹Ø·ÖÎöÔÚ [spring aop Ö® jdk ¶¯Ì¬´úÀí](https://my.oschina.net/funcy/blog/4696654) Óë[ spring aop Ö® cglib ´úÀí](https://my.oschina.net/funcy/blog/4696655) ÒÑÓÐÏêϸ·ÖÎö£¬¸ÐÐËȤµÄС»ï°é¿É×ÔÐвéÔÄ£¬ÕâÀïÎÒÃÇÖ±½ÓÀ´¿´ `TransactionInterceptor#invoke` µÄÖ´ÐÐÁ÷³Ì¡£ + +ÊÂÎñµÄ´¦ÀíµÄÁ÷³ÌÔÚ `TransactionInterceptor#invoke` ·½·¨ÖУº + +> TransactionInterceptor#invoke + +``` +public Object invoke(MethodInvocation invocation) throws Throwable { + Class targetClass = (invocation.getThis() != null + ? AopUtils.getTargetClass(invocation.getThis()) : null); + // ¼ÌÐøÍùÏ¿´ + return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); +} + +``` + +ÕæÕýµÄ´¦ÀíÂß¼­ÔÚ `TransactionAspectSupport#invokeWithinTransaction` ·½·¨ÖУº + +> TransactionAspectSupport#invokeWithinTransaction + +``` +protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, + final InvocationCallback invocation) throws Throwable { + TransactionAttributeSource tas = getTransactionAttributeSource(); + // »ñÈ¡@TransactionalµÄÊôÐÔÅäÖà + final TransactionAttribute txAttr = (tas != null + ? tas.getTransactionAttribute(method, targetClass) : null); + // »ñÈ¡ÊÂÎñ¹ÜÀíÆ÷£¨IOCÈÝÆ÷ÖлñÈ¡£© + final TransactionManager tm = determineTransactionManager(txAttr); + + // Ê¡ÂÔ ReactiveTransactionManager µÄ´¦Àí + ... + + PlatformTransactionManager ptm = asPlatformTransactionManager(tm); + // »ñÈ¡·½·¨µÄÈ«ÏÞ¶¨Ãû£¬¸ñʽΪ£º"°üÃû.ÀàÐÍ.·½·¨Ãû" + final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); + + // ÊÂÎñµÄ´¦ÀíÂß¼­£¬ÕâÒ²ÊÇÎÒÃǽÓÏÂÀ´Ö÷Òª·ÖÎöµÄµØ·½ + if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { + // 1\. ¿ªÆôÊÂÎñ + TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); + Object retVal; + try { + // 2\. Ö´ÐоßÌåµÄÒµÎñ + retVal = invocation.proceedWithInvocation(); + } + catch (Throwable ex) { + // 3\. Òì³£»Ø¹ö + completeTransactionAfterThrowing(txInfo, ex); + throw ex; + } + finally { + // ÖØÖÃÊÂÎñÐÅÏ¢ + cleanupTransactionInfo(txInfo); + } + if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { + TransactionStatus status = txInfo.getTransactionStatus(); + if (status != null && txAttr != null) { + retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); + } + } + // 4\. Ìá½»ÊÂÎñ£¬´úÂëÖлáÅжÏÊÇ·ñÄÜÖ§³Ö + commitTransactionAfterReturning(txInfo); + return retVal; + } + else { + // Ê¡ÂÔÆäËû + ... + } +} + +``` + +ÒÔÉÏ·½·¨¾ÍÊÇÊÂÎñµÄÈ«²¿Á÷³ÌÁË£¬²½ÖèÈçÏ£º + +1. ¿ªÆôÊÂÎñ +2. Ö´ÐÐÒµÎñ´úÂë +3. Òì³£»Ø¹ö +4. Ìá½»ÊÂÎñ + +¹ØÓÚÊÂÎñµÄ¾ßÌå·ÖÎö£¬ÎÒÃǽ«ÔÚÏÂһƪÎÄÕ·ÖÎö£¬±¾ÎÄÖ»ÐèÒª¶ÔÊÂÎñµÄ´¦ÀíÁ÷³ÌÓдóÖÂÁ˽⼴¿É¡£ + +### 3\. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁËÊÂÎñÖдúÀí¶ÔÏóµÄ´´½¨¼°Ö´ÐÐÁ÷³Ì£¬Êµ¼ÊÉÏÕâЩ²½Öèͬ aop »ù±¾Ò»Ö£¬±¾ÎÄ×ÅÖØ·ÖÎöÁËÓë aop ²»Í¬µÄ²¿·Ö£º + +* ÔÚ´úÀí¶ÔÏóµÄ´´½¨·½Ã棬·ÖÎöÁËÈçºÎÅжϵ±Ç°·½·¨ÄÜ·ñʹÓà `BeanFactoryTransactionAttributeSourceAdvisor`£¬Öصã·ÖÎöÁË `TransactionAttributeSourceClassFilter#matches` Óë `TransactionAttributeSourcePointcut#matches` ·½·¨£¬ÕâÊÇÅжϵĺËÐÄËùÔÚ£» + +* ÔÚ·½·¨µÄÖ´ÐÐÉϵ쬴ÖÂÔ·ÖÎöÁË `TransactionInterceptor#invoke` µÄÖ´ÐÐÁ÷³Ì£¬ÕâЩÊÂÎñµÄ¿ªÆô¡¢Ìá½»¡¢Òì³£»Ø¹öµÈÁ÷³Ì¸úÎÒÃÇÆ½³£Ê¹ÓõIJî±ð²»´ó£¬²»¹ýÀïÃæµÄ¾ßÌåϸ½ÚÎÒÃDz¢Ã»ÓзÖÎö¡£ + +±¾ÎÄÖØµãÊÇ´úÀí¶ÔÏóµÄ´´½¨¼°Ö´ÐÐÁ÷³Ì£¬ÊÂÎñÖ´ÐеľßÌåϸ½ÚÎÒÃÇÏÂÆªÔÙ·ÖÎö¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4773457](https://my.oschina.net/funcy/blog/4773457) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" new file mode 100644 index 0000000..8ac63de --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\344\272\224\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20603.md" @@ -0,0 +1,500 @@ +±¾ÎÄÊÇ¡¶ÊÂÎñµÄ¸ôÀë¼¶±ðÓë´«²¥·½Ê½µÄ´¦Àí¡··ÖÎöµÄµÚ 3 ƪ£¬½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø¡£ + +ÉÏÎÄÖÐÎÒÃÇÌáµ½ÁËÊÂÎñµÄ¿ªÆô£¨`doBegin(...)`£©¡¢¹ÒÆð£¨`suspend(...)`£©Óë´´½¨±£´æµã£¨`createAndHoldSavepoint(...)`£©µÄ²Ù×÷£¬±¾ÎĽ«À´·ÖÎöÕâЩ²Ù×÷µÄʵÏÖ¡£ + +### 1. `doBegin(...)`£ºÆô¶¯ÐµÄÊÂÎñ + +Æô¶¯ÐÂÊÂÎñµÄ·½·¨Îª `DataSourceTransactionManager#doBegin`£¬´úÂëÈçÏ£º + +``` +protected void doBegin(Object transaction, TransactionDefinition definition) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; + Connection con = null; + + try { + // 1\. »ñÈ¡Êý¾Ý¿âÁ¬½Ó + if (!txObject.hasConnectionHolder() || + txObject.getConnectionHolder().isSynchronizedWithTransaction()) { + // getConnection(): »ñÈ¡Êý¾Ý¿âÁ¬½Ó£¬obtainDataSource()£º»ñÈ¡Êý¾ÝÔ´ + Connection newCon = obtainDataSource().getConnection(); + txObject.setConnectionHolder(new ConnectionHolder(newCon), true); + } + // ÕâÀォ synchronizedWithTransaction ÉèÖÃΪtrue + txObject.getConnectionHolder().setSynchronizedWithTransaction(true); + con = txObject.getConnectionHolder().getConnection(); + + // 2\. ÉèÖÃÊÂÎñµÄ¸ôÀë¼¶±ð + + Integer previousIsolationLevel + = DataSourceUtils.prepareConnectionForTransaction(con, definition); + txObject.setPreviousIsolationLevel(previousIsolationLevel); + // ÉèÖÃÖ»¶ÁÊôÐÔ + txObject.setReadOnly(definition.isReadOnly()); + + // 3\. ¿ªÆôÊÂÎñ + if (con.getAutoCommit()) { + txObject.setMustRestoreAutoCommit(true); + // ¹Ø±ÕÊÂÎñµÄ×Ô¶¯Ìá½»£¬Ò²¾ÍÊÇ¿ªÆôÊÂÎñ + con.setAutoCommit(false); + } + // 4\. Èç¹ûÊÇÖ»¶ÁÊÂÎñ£¬ÔÚÕâÀïÉèÖà + prepareTransactionalConnection(con, definition); + // ÉèÖÃÊÂÎñµÄ¼¤»î±ê¼Ç + txObject.getConnectionHolder().setTransactionActive(true); + // 5\. ÉèÖÃÊÂÎñµÄ³¬Ê±Ê±¼ä + int timeout = determineTimeout(definition); + if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { + txObject.getConnectionHolder().setTimeoutInSeconds(timeout); + } + + // 6\. °ó¶¨Êý¾ÝÔ´ÓëÁ¬½Óµ½µ±Ç°Ïß³Ì + if (txObject.isNewConnectionHolder()) { + TransactionSynchronizationManager.bindResource( + obtainDataSource(), txObject.getConnectionHolder()); + } + } + } catch (Throwable ex) { + // ´¦ÀíÒì³£²Ù×÷£¬Èç¹ûÊÇÐÂÁ¬½ÓÔò¹Ø±Õ¸ÃÁ¬½Ó + if (txObject.isNewConnectionHolder()) { + DataSourceUtils.releaseConnection(con, obtainDataSource()); + txObject.setConnectionHolder(null, false); + } + throw new CannotCreateTransactionException(...); + } +} + +``` + +ÒÔÉÏ´úÂ뻹ÊÇͦÇåÎúµÄ£¬×¢ÊÍÒ²ºÜÃ÷È·ÁË£¬ÕâÀï¶Ô¹Ø¼ü²½Öè×÷½øÒ»²½·ÖÎö¡£ + +#### 1.1 »ñÈ¡Êý¾Ý¿âÁ¬½Ó + +Êý¾Ý¿âÁ¬½ÓµÄ»ñÈ¡»¹ÊǺܼòµ¥µÄ£¬´úÂëÈçÏ£º + +``` +Connection newCon = obtainDataSource().getConnection(); + +``` + +Æäʵ¾ÍÊǵ÷Óà `javax.sql.DataSource#getConnection()` ·½·¨¡£ + +#### 1.2 ÉèÖÃÊÂÎñµÄ¸ôÀë¼¶±ð + +ÔÚ `@Transactional` ÖУ¬ÎÒÃÇ¿ÉÒÔʹÓà `isolation` Ö¸¶¨ÊÂÎñµÄ¸ôÀë¼¶±ð£º + +``` +public @interface Transactional { + /** + * Ö¸¶¨ÊÂÎñµÄ¸ôÀë¼¶±ð + */ + Isolation isolation() default Isolation.DEFAULT; + ... +} + +``` + +Èç¹û²»Ö¸¶¨£¬¾ÍʹÓÃĬÈϵĸôÀë¼¶±ð£¬Ò²¾ÍÊÇʹÓÃÊý¾Ý¿âÅäÖõġ£ + +spring ÉèÖÃÊÂÎñ¸ôÀë¼¶±ðµÄ·½·¨Îª `DataSourceUtils#prepareConnectionForTransaction`£¬´úÂëÈçÏ£º + +``` +public static Integer prepareConnectionForTransaction(Connection con, + @Nullable TransactionDefinition definition) throws SQLException { + Assert.notNull(con, "No Connection specified"); + if (definition != null && definition.isReadOnly()) { + try { + // ÉèÖÃΪֻ¶Áģʽ + con.setReadOnly(true); + } + catch (SQLException | RuntimeException ex) { + ... + } + } + + Integer previousIsolationLevel = null; + if (definition != null && definition.getIsolationLevel() + != TransactionDefinition.ISOLATION_DEFAULT) { + int currentIsolation = con.getTransactionIsolation(); + if (currentIsolation != definition.getIsolationLevel()) { + // Äõ½Ö®Ç°µÄ¸ôÀë¼¶±ð£¬ÊÂÎñÍê³ÉÊ£¬ÐèÒªÖØÖÃΪԭÀ´µÄ¸ôÀë¼¶±ð + previousIsolationLevel = currentIsolation; + // ÔÚÕâÀïÉèÖÃÊý¾Ý¿âµÄ¸ôÀë¼¶±ð£¬µ÷ÓõÄÊÇ£º + // java.sql.Connection.setTransactionIsolation + con.setTransactionIsolation(definition.getIsolationLevel()); + } + } + return previousIsolationLevel; +} + +``` + +Õâ ¸ö·½·¨´¦ÀíÁËÁ½¸öÉèÖ㺠+ +1. ÉèÖÃΪֻ¶Áģʽ£ºµ÷ÓõÄÊÇ `java.sql.Connection#setReadOnly` ·½·¨ +2. ÉèÖøôÀë¼¶±ð£ºµ÷ÓõÄÊÇ `java.sql.Connection.setTransactionIsolation` ·½·¨ + +¹ØÓÚÖ»¶Áģʽ£¬Ò²ÊÇ¿ÉÒÔÔÚ `@Transactional` ÖÐÉèÖõģº + +``` +public @interface Transactional { + /** + * ÉèÖÃÖ»¶ÁÊÂÎñ + */ + boolean readOnly() default false; + ... +} + +``` + +#### 1.3 ¿ªÆôÊÂÎñ + +¼¤¶¯ÈËÐĵÄʱ¿ÌÖÕÓÚÀ´ÁË£¬Ç°ÃæÆÌµæÁËÄÇô¶à£¬¾ÍÊÇΪÁËÕâÒ»²½µÄ²Ù×÷£º¿ªÆôÊÂÎñ¡£¿ªÆôÊÂÎñµÄ´úÂëÈçÏ£º + +``` +if (con.getAutoCommit()) { + txObject.setMustRestoreAutoCommit(true); + // ¹Ø±ÕÊÂÎñµÄ×Ô¶¯Ìá½»£¬Ò²¾ÍÊÇ¿ªÆôÊÂÎñ + con.setAutoCommit(false); +} + +``` + +¾ßÌåÁ÷³ÌΪ£¬ÏÈÅжÏ×Ô¶¯Ìá½»ÊÇ·ñ¿ªÆô£¬Èç¹û¿ªÉèÖÃÁË£¬¾Í½«ÆäÉèÖÃΪ false£¬µ÷ÓõÄÒ²ÊÇ `java.sql` µÄ·½·¨£º + +* »ñÈ¡×Ô¶¯Ìύ״̬£º`java.sql.Connection#getAutoCommit` +* ÉèÖÃ×Ô¶¯Ìύ״̬£º`java.sql.Connection#setAutoCommit` + +#### 1.4 Èç¹ûÊÇÖ»¶ÁÊÂÎñ£¬ÔÚÕâÀïÉèÖà + +ÔÚÇ°Ãæ·ÖÎö `1.2 ÉèÖÃÊÂÎñµÄ¸ôÀë¼¶±ð`ÖУ¬Í¨¹ýµ÷Óà `java.sql.Connection#setReadOnly` ½«Á¬½ÓÉèÖÃΪֻ¶ÁÁË£¬ÕâÀﻹ»áÔÙÒ»´ÎÉèÖ㬷½·¨Îª `DataSourceTransactionManager#prepareTransactionalConnection`£º + +``` +protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) + throws SQLException { + if (isEnforceReadOnly() && definition.isReadOnly()) { + try (Statement stmt = con.createStatement()) { + // ÉèÖÃÖ»¶ÁÀàÐÍÒªÔËÐÐsql + stmt.executeUpdate("SET TRANSACTION READ ONLY"); + } + } +} + +``` + +ÕâÒ»´ÎÊÇͨ¹ýÖ´ÐÐ sql Óï¾ä `SET TRANSACTION READ ONLY` ½«ÊÂÎñÉèÖÃΪֻ¶Á¡£ + +#### 1.5 ÉèÖÃÊÂÎñµÄ³¬Ê±Ê±¼ä + +ÔÚ `@Transactional` ×¢½âÖУ¬ÎÒÃÇ¿ÉÒÔʹÓà `timeout` À´Ö¸¶¨ÊÂÎñµÄ³¬Ê±Ê±¼ä£º + +``` +public @interface Transactional { + /** + * ÉèÖó¬Ê±Ê±¼ä + */ + int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; + ... +} + +``` + +ÕâÑùÉèÖõij¬Ê±Ê±¼ä»áÔÚÕâÀïÓõ½£¬ÎÒÃǽøÈë `ResourceHolderSupport#setTimeoutInSeconds`£º + +``` +public abstract class ResourceHolderSupport implements ResourceHolder { + /** + * ½ØÖ¹Ê±¼ä + */ + private Date deadline; + + /** + * ÉèÖÃʱ¼ä£¬µ¥Î»£ºÃë + */ + public void setTimeoutInSeconds(int seconds) { + setTimeoutInMillis(seconds * 1000L); + } + + /** + * ÉèÖÃʱ¼ä£¬µ¥Î»£ººÁÃë + * ×îºó»áת»¯Îª ½ØÖ¹Ê±¼ä + */ + public void setTimeoutInMillis(long millis) { + this.deadline = new Date(System.currentTimeMillis() + millis); + } + + /** + * »ñÈ¡½ØÖ¹Ê±¼ä + */ + @Nullable + public Date getDeadline() { + return this.deadline; + } + + /** + * »ñȡʣÓàʱ¼ä£¬µ¥Î»£ºÃë + */ + public int getTimeToLiveInSeconds() { + double diff = ((double) getTimeToLiveInMillis()) / 1000; + int secs = (int) Math.ceil(diff); + checkTransactionTimeout(secs <= 0); + return secs; + } + + /** + * »ñȡʣÓàʱ¼ä£¬µ¥Î»£ººÁÃë + */ + public long getTimeToLiveInMillis() throws TransactionTimedOutException{ + if (this.deadline == null) { + throw new IllegalStateException("No timeout specified for this resource holder"); + } + long timeToLive = this.deadline.getTime() - System.currentTimeMillis(); + checkTransactionTimeout(timeToLive <= 0); + return timeToLive; + } + + ... +} + +``` + +ÔÚ `ResourceHolderSupport` ÖÐά»¤ÁËÒ»¸ö³ÉÔ±±äÁ¿ `deadline`£¨½ØÖ¹Ê±¼ä£©£¬´«ÈëµÄ³¬Ê±Ê±¼ä×îÖÕ¶¼»áת»¯Îª `deadline`¡£ + +»ñȡʣÓàʱ¼äʱ£¬Ò²ÊÇÓÉ `deadline` ¼ÆËãµÃµ½£¬·µ»ØµÄÊ£Óàʱ¼äÓÐÃëÓëºÁÃëÁ½ÖÖ¡£ + +¿´µ½ÕâÀ`txObject.getConnectionHolder().setTimeoutInSeconds(timeout)` Ö»Êǽ«³¬Ê±Ê±¼äÉèÖõ½ `ConnectionHolder` µÄ³ÉÔ±±äÁ¿ÖУ¨`ConnectionHolder` ÊÇ `ResourceHolderSupport` µÄ×ÓÀࣩ£¬Ëƺõ¸úÊý¾Ý¿âûɶ¹ØÏµ£¬Êý¾Ý¿âÊÇÔõô¿ØÖÆÊÂÎñ³¬Ê±µÄÄØ£¿ + +²»µÃ²»Ëµ£¬Õâ¸ö³¬Ê±µÄ¿ØÖÆÊÇÓеãÄÑÕÒ£¬±¾ÎÄÊÇͨ¹ýµ÷ÊÔÕÒµ½µÄ£¬³¬Ê±Ê±¼äµÄÉèÖÃÔÚÔÚ `DataSourceUtils#applyTimeout` ·½·¨ÖУ¬ÕâÆäÖпÉνÊǾ­¹ýÁËǧɽÍòË®£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9fc1a75dc7b0644269b6dba16dfd5d0e676.png) + +¸Ðлµ÷ÊÔ¹¦ÄÜ£¬Ã»ÓÐËü£¬ÌìÖªµÀÒª¶à¾Ã²ÅÄÜÕÒµ½Õâ¸öʱ¼äµÄÉèÖã¡ÎÒÕâÀïʹÓõÄÊÇ `jdbcTemplate`£¬ÔÚÆäËû `orm` ¿ò¼ÜÏ£¬ÉèÖó¬Ê±Ê±¼äÓ¦¸Ã»áÓÐËù²»Í¬ ¡£ + +ÎÒÃÇ¿´¿´ `DataSourceUtils#applyTimeout` ÊÇÔõôÉèÖó¬Ê±Ê±¼äµÄ£º + +``` +public static void applyTimeout(Statement stmt, @Nullable DataSource dataSource, int timeout) + throws SQLException { + Assert.notNull(stmt, "No Statement specified"); + ConnectionHolder holder = null; + if (dataSource != null) { + holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); + } + if (holder != null && holder.hasTimeout()) { + // ÕâÀï¾ÍÊÇ»ñȡʣÓàµÄ³¬Ê±Ê±¼ä£¬ÓÉ ConnectionHolder.dateline ¼ÆËãµÃµ½µÄ + stmt.setQueryTimeout(holder.getTimeToLiveInSeconds()); + } + else if (timeout >= 0) { + // jdbcTemplate ×ÔÉíÒ²¿ÉÒÔÉèÖòéѯ³¬Ê±Ê±¼ä + stmt.setQueryTimeout(timeout); + } +} + +``` + +×îÖÕµ÷ÓõÄÊÇ `java.sql.Statement#setQueryTimeout` À´ÉèÖó¬Ê±Ê±¼äµÄ¡£ + +#### 1.6 °ó¶¨Êý¾ÝÔ´ÓëÁ¬½Óµ½µ±Ç°Ïß³Ì + +ÒÔÉÏÊÂÇé´¦ÀíÍê³Éºó£¬½ÓÏÂÀ´¾ÍÊǰó¶¨Êý¾ÝÔ´ÓëÁ¬½ÓÁË£¬´¦Àí·½·¨Îª `TransactionSynchronizationManager#bindResource`: + +``` +/** + * resources ´æ·Åµ±Ç°Ïß³ÌÖеÄÊý¾ÝÔ´ÓëÁ¬½Ó + * ÆäÖдæ·ÅµÄÄÚÈÝΪһ¸ö Map£¬Map µÄ key ΪÊý¾ÝÔ´£¬value ΪÊý¾ÝÔ´¶ÔÓ¦µÄÁ¬½Ó + */ +private static final ThreadLocal> resources = + new NamedThreadLocal<>("Transactional resources"); + +/** + * °ó¶¨²Ù×÷ + */ +public static void bindResource(Object key, Object value) throws IllegalStateException { + Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); + Assert.notNull(value, "Value must not be null"); + Map map = resources.get(); + if (map == null) { + map = new HashMap<>(); + resources.set(map); + } + // ½«Êý¾ÝÔ´ÓëÁ¬½Ó´æ·Åµ½mapÖÐ + Object oldValue = map.put(actualKey, value); + if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { + oldValue = null; + } + if (oldValue != null) { + throw new IllegalStateException("Already value [" + oldValue + "] for key [" + + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); + } + +``` + +Õâ Ò»²½µÄ²Ù×÷»¹ÊDZȽϼòµ¥µÄ£¬¾ÍÊǽ«Êý¾ÝÔ´ÓëÁ¬½Ó·Å½ø `resources` ÖУ¬´Ó¶øÍê³ÉÓ뵱ǰÏ̵߳İ󶨲Ù×÷¡£ + +### 2. `suspend(...)`£º¹ÒÆðÊÂÎñ + +¹ÒÆðÊÂÎñµÄ²Ù×÷Ϊ `AbstractPlatformTransactionManager#suspend`£¬´úÂëÈçÏ£º + +``` +protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) + throws TransactionException { + // Èç¹ûÓÐͬ²½µÄÊÂÎñ£¬ÔòÓÅÏÈ¹ÒÆðͬ²½µÄÊÂÎñ + if (TransactionSynchronizationManager.isSynchronizationActive()) { + List suspendedSynchronizations = doSuspendSynchronization(); + try { + Object suspendedResources = null; + if (transaction != null) { + // ¹ÒÆð²Ù×÷ + suspendedResources = doSuspend(transaction); + } + // ÖØÖÃÊÂÎñÃû³Æ + String name = TransactionSynchronizationManager.getCurrentTransactionName(); + TransactionSynchronizationManager.setCurrentTransactionName(null); + // ÖØÖÃÖ»¶Á״̬ + boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); + TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); + // ÖØÖøôÀë¼¶±ð + Integer isolationLevel = TransactionSynchronizationManager + .getCurrentTransactionIsolationLevel(); + TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); + // ÖØÖÃÊÂÎñ¼¤»î״̬ + boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); + TransactionSynchronizationManager.setActualTransactionActive(false); + // ·µ»Ø¹ÒÆðµÄÊÂÎñ + return new SuspendedResourcesHolder( + suspendedResources, suspendedSynchronizations, name, readOnly, + isolationLevel, wasActive); + } + catch (RuntimeException | Error ex) { + doResumeSynchronization(suspendedSynchronizations); + throw ex; + } + } + else if (transaction != null) { + Object suspendedResources = doSuspend(transaction); + return new SuspendedResourcesHolder(suspendedResources); + } + else { + return null; + } +} + +``` + +`suspend(...)` ·½·¨ÖÐ×îÖØÒªµÄ¾ÍÊÇ¹ÒÆðÊÂÎñµÄ²Ù×÷ÁË£¬Ò²¾ÍÊÇ `doSuspend(transaction)`£¬¸Ã·½·¨ λÓÚ `` ÖУ¬Ö±½Ó¿´´úÂ룺 + +``` +protected Object doSuspend(Object transaction) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; + txObject.setConnectionHolder(null); + // ½â³ý°ó¶¨ + return TransactionSynchronizationManager.unbindResource(obtainDataSource()); +} + +``` + +¼ÌÐø½øÈë `TransactionSynchronizationManager.unbindResource` ·½·¨£º + +``` +/** + * ½â³ý°ó¶¨²Ù×÷ + */ +public static Object unbindResource(Object key) throws IllegalStateException { + Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); + // ¼ÌÐøµ÷Óà + Object value = doUnbindResource(actualKey); + if (value == null) { + throw new IllegalStateException(...); + } + return value; +} + +/** + * ½â³ý°ó¶¨²Ù×÷ + */ +private static Object doUnbindResource(Object actualKey) { + Map map = resources.get(); + if (map == null) { + return null; + } + // ÒÆ³ý×ÊÔ´ + Object value = map.remove(actualKey); + if (map.isEmpty()) { + resources.remove(); + } + if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { + value = null; + } + return value; +} + +``` + +ÔÚ¿ªÆôÊÂÎñʱ£¬Êǽ«Êý¾ÝÔ´ÓëÁ¬½Ó°ó¶¨µ½µ±Ç°Ị̈߳¬¹ÒÆðʱ £¬Êǽ«Êý¾ÝÔ´ÓëÁ¬½Ó½â³ýÓ뵱ǰÏ̵߳İ󶨹ØÏµ¡£ + +### 3. `createAndHoldSavepoint(...)`£º´´½¨±£´æµã + +±£´æµãµÄ´´½¨ÔÚ `AbstractTransactionStatus#createAndHoldSavepoint` ·½·¨Öд¦Àí£¬´úÂëÈçÏ£º + +``` + // ±£´æµã + private Object savepoint; + + // ´´½¨±£´æµã + public void createAndHoldSavepoint() throws TransactionException { + setSavepoint(getSavepointManager().createSavepoint()); + } + + protected void setSavepoint(@Nullable Object savepoint) { + this.savepoint = savepoint; + } + +``` + +àÅ£¬¿´À´Õâ¸ö·½·¨ÀïÖ»ÊÇ×öÁË`±£´æµã`µÄ±£´æ£¨Ò²¾ÍÊǸ³Öµ¸ø `AbstractTransactionStatus` µÄ³ÉÔ±±äÁ¿£©£¬ÒªÕæÕýÁ˽Ᵽ´æµãµÄ´´½¨£¬»¹µÃ¿´ `getSavepointManager().createSavepoint()`£¬½øÈëµ½ `JdbcTransactionObjectSupport#createSavepoint`£º + +``` +public Object createSavepoint() throws TransactionException { + ConnectionHolder conHolder = getConnectionHolderForSavepoint(); + try { + if (!conHolder.supportsSavepoints()) { + throw new NestedTransactionNotSupportedException(...); + } + if (conHolder.isRollbackOnly()) { + throw new CannotCreateTransactionException(...); + } + // ´´½¨±£´æµã + return conHolder.createSavepoint(); + } + catch (SQLException ex) { + throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex); + } +} + +``` + +·¢ÏÖ×îºóµ÷ÓõÄÊÇ `ConnectionHolder#createSavepoint` ·½·¨£¬Ô­À´±£´æµãÊÇÔÚ `ConnectionHolder` Öд´½¨µÄ°¡£¡¼ÌÐø£º + +``` +// ±£´æµãÃû³ÆÇ°×º +public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_"; + +// ±£´æµãÊýÁ¿ +private int savepointCounter = 0; + +public Savepoint createSavepoint() throws SQLException { + this.savepointCounter++; + // ÕâÀï´´½¨±£´æµã£¬µ÷ÓõÄÊÇ java.sql.Connection#setSavepoint(java.lang.String) ·½·¨ + return getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter); +} + +``` + +±£´æµãÃû³ÆÇ°×ºÎª `SAVEPOINT_`£¬Ã¿´´½¨Ò»¸ö±£´æµã£¬`savepointCounter` µÄ¼ÆÊýÆ÷¾Í¼Ó 1£¬×îÖÕ±£´æµãµÄÃû³ÆÎª `SAVEPOINT_1`¡¢`SAVEPOINT_2`¡¢... + +´´½¨±£´æµã×îÖÕµ÷Óõķ½·¨ÊÇ `java.sql.Connection#setSavepoint(java.lang.String)`£¬ÒÀÈ»ÊÇ jdk ÌṩµÄ·½·¨£¬·ÖÎöµ½ÕâÀÎÒÃǻᷢÏÖÊÂÎñµÄ´ó²¿·Ö²Ù×÷¶¼ÊÇ spring ¶Ô jdk ·½·¨µÄ·â×°¡£ + +ºÃÁË£¬±¾ÎĵķÖÎö¾ÍÏȵ½ÁË£¬¹ØÓÚÊÂÎñµÄÌá½»¡¢»Ø¹ö¡¢»Ø¹öµ½±£´æµã¡¢»Ö¸´¹ÒÆðµÄÊÂÎñµÈ£¬ÏÂÆªÎÄÕ¼ÌÐø·ÖÎö¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4947826](https://my.oschina.net/funcy/blog/4947826) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" new file mode 100644 index 0000000..415c012 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\205\255\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20604.md" @@ -0,0 +1,433 @@ +±¾ÎÄÊÇ¡¶ÊÂÎñµÄ¸ôÀë¼¶±ðÓë´«²¥·½Ê½µÄ´¦Àí¡··ÖÎöµÄµÚ 4 ƪ£¬½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø¡£ + +#### 3.6 Ö´ÐоßÌåµÄÒµÎñ + +Õâ¿é´úÂëÈçÏ£º + +``` +retVal = invocation.proceedWithInvocation(); + +``` + +ÕâÀï×îÖÕ»áµ÷Óõ½ÒµÎñ·½·¨ `UserService#insert`£¬±¾ÎIJ¢²»»á̽¾¿ÕâÆäÖÐÊÇÈçºÎÒ»²½²½µ÷ÓùýÈ¥µÄ£¬ÒªÁ˽âµ÷Óùý³ÌµÄС»ï°é¿ÉÒԲο¼ aop Ïà¹Ø²Ù×÷£º + +* [spring aop Ö® jdk ¶¯Ì¬´úÀí](https://my.oschina.net/funcy/blog/4696654) +* [spring aop Ö® cglib ´úÀí](https://my.oschina.net/funcy/blog/4696655) + +#### 3.7 Òì³£»Ø¹ö + +´¦ÀíÒì³£µÄ·½·¨Îª `TransactionAspectSupport#completeTransactionAfterThrowing`£¬´úÂëÈçÏ£º + +``` +protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { + if (txInfo != null && txInfo.getTransactionStatus() != null) { + // Òì³£·ûºÏ²Å»Ø¹ö + if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { + try { + txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); + } + catch (...) { + ... + } + } + else { + try { + // Òì³£²»·ûºÏ£¬¼´Ê¹Ö´Ðгö´íÒ²»áÌá½» + txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); + } + catch (...) { + ... + } + } + } +} + +``` + +´ÓÕâ¸ö·½·¨À´¿´£¬³öÏÖÁËÒì³£²¢²»»áÖ±½Ó½øÐлعöµÄ£¬¶øÊÇÏÈÒªÅжÏÒì³£ÀàÐÍ£¬¶ÔÓÚÐèÒª»Ø¹öµÄÒì³£²Å»Ø¹ö¡£ + +##### Åжϵ±Ç°Òì³£ÊÇ·ñÒª»Ø¹ö + +ÅжÏÒì³£ÊÇ·ñ·ûºÏµÄ·½·¨Îª `RuleBasedTransactionAttribute#rollbackOn`£º + +``` +public boolean rollbackOn(Throwable ex) { + RollbackRuleAttribute winner = null; + int deepest = Integer.MAX_VALUE; + if (this.rollbackRules != null) { + for (RollbackRuleAttribute rule : this.rollbackRules) { + // »ñÈ¡Òì³£µÄÉî¶È + int depth = rule.getDepth(ex); + // Éî¶ÈÂú×ãÌõ¼þ£¬±íʾµ±Ç°Òì³£ÐèÒª»Ø¹ö + if (depth >= 0 && depth < deepest) { + deepest = depth; + winner = rule; + } + } + } + if (winner == null) { + return super.rollbackOn(ex); + } + return !(winner instanceof NoRollbackRuleAttribute); +} + +``` + +»ñÈ¡Ê÷ÉîµÄ·½·¨Îª `RollbackRuleAttribute#getDepth(Throwable)`£¬´úÂëÈçÏ£º + +``` +public int getDepth(Throwable ex) { + return getDepth(ex.getClass(), 0); +} + +private int getDepth(Class exceptionClass, int depth) { + if (exceptionClass.getName().contains(this.exceptionName)) { + // Found it! + return depth; + } + // If we've gone as far as we can go and haven't found it... + if (exceptionClass == Throwable.class) { + return -1; + } + // µÝ¹é»ñÈ¡ + return getDepth(exceptionClass.getSuperclass(), depth + 1); +} + +``` + +Õâ¸öʵÏֺܼòµ¥£¬¾ÍÊǵݹé»ñÈ¡ `exception` µÄ¸¸À࣬ÕÒµ½Á˾ͷµ»ØµÝ¹éµÄ´ÎÊý£¬Èç¹û×îÖÕÕÒµ½µÄÊÇ `Throwable`£¬¾Í·µ»Ø - 1. + +Ö®ËùÒÔʹÓÃÊ÷ÉîÀ´ÅжÏÊÇ·ñÐèÒª»Ø¹ö£¬Ô­ÒòÊÇÉèÖûعöµÄÒ쳣ʱ£¬¿ÉÒÔÉèÖÃÒì³£Ãû³Æ£º + +``` +public @interface Transactional { + ... + + // ×¢ÒâÕâ¸öÀàÐÍÊÇ×Ö·û´® + String[] rollbackForClassName() default {}; +} + +``` + +Òò´Ë²»ÄÜʹÓà `ex instanceof Exception` µÄ·½Ê½À´ÅжÏÄÜ·ñ»Ø¹ö¡£ + +`RuleBasedTransactionAttribute#rollbackOn` ÖÐµÄ `RollbackRuleAttribute` Óë `NoRollbackRuleAttribute` ÓÖÊÇɶĨ£¿ËüÃÇ `rollbackFor` Óë `noRollbackFor` µÄ°ü×°À࣬ÔÚ `SpringTransactionAnnotationParser#parseTransactionAnnotation(AnnotationAttributes)` ·½·¨ÖÐÉèÖ㬴úÂëÈçÏ£º + +``` +protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { + RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); + ... + // ´¦Àí»Ø¹öÒì³£ + List rollbackRules = new ArrayList<>(); + for (Class rbRule : attributes.getClassArray("rollbackFor")) { + rollbackRules.add(new RollbackRuleAttribute(rbRule)); + } + for (String rbRule : attributes.getStringArray("rollbackForClassName")) { + rollbackRules.add(new RollbackRuleAttribute(rbRule)); + } + // ´¦Àí²»»Ø¹öÒì³£ + for (Class rbRule : attributes.getClassArray("noRollbackFor")) { + rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); + } + for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { + rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); + } + // ´¦Àí¹æÔò + rbta.setRollbackRules(rollbackRules); + return rbta; +} + +``` + +##### »Ø¹ö²Ù×÷ + +»Ø¹ö²Ù×÷ÔÚ `AbstractPlatformTransactionManager#rollback` ·½·¨Öд¦Àí£¬³ýÁËÊý¾Ý¿âµÄ»Ø¹ö²Ù×÷Í⣬Õâ¸ö·½·¨»¹´¦ÀíÁËһЩ»Øµ÷´¦Àí£¬ÕâÀïÎÒÃǾͲ»·ÖÎöÁË£¬Ö±½Ó¿´¹Ø¼üµÄ»Ø¹ö´úÂë¡£ÔÚÇ°ÃæµÄ·ÖÎöÊÂÎñµÄ´«²¥ÀàÐÍʱ£¬´ó¶à´«²¥ÀàÐÍÊǻعöÊÂÎñ£¬²»¹ý `PROPAGATION_NESTED` ÀýÍ⣬ËüÊǻعöµ½±£´æµã£¬ÕâÀïÎÒÃÇ·Ö±ðÀ´¿´¿´¹Ø¼ü´úÂ룺 + +1. ÊÂÎñ»Ø¹ö + + ´¦ÀíÊÂÎñ»Ø¹öµÄ·½·¨Îª `DataSourceTransactionManager#doRollback`£¬×îÖÕµ÷ÓõÄÊÇ `java.sql.Connection` µÄ·½·¨£º + + ``` + protected void doRollback(DefaultTransactionStatus status) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); + // »ñÈ¡Á¬½Ó£¬Connection Ϊ java.sql.Connection + Connection con = txObject.getConnectionHolder().getConnection(); + try { + con.rollback(); + } + catch (SQLException ex) { + throw new TransactionSystemException("Could not roll back JDBC transaction", ex); + } + } + + ``` + +2. »Ø¹öµ½±£´æµã ´¦Àí»Ø¹öµ½±£´æµãµÄ²Ù×÷ÔÚ `AbstractTransactionStatus#rollbackToHeldSavepoint` ·½·¨ÖУº + + ``` + public void rollbackToHeldSavepoint() throws TransactionException { + Object savepoint = getSavepoint(); + if (savepoint == null) { + throw new TransactionUsageException(...); + } + // »Ø¹öµ½±£´æµã + getSavepointManager().rollbackToSavepoint(savepoint); + // Êͷű£´æµã + getSavepointManager().releaseSavepoint(savepoint); + // ½«±£´æµãÖÃΪnull + setSavepoint(null); + } + + ``` + + Õâ¸ö·½·¨Ö÷ÒªÓÐÁ½¸ö²Ù×÷£º»Ø¹öµ½±£´æµãÓëÊͷű£´æµã¡£ + + »Ø¹öµ½±£´æµãµÄ²Ù×÷ÔÚ `JdbcTransactionObjectSupport#rollbackToSavepoint` ·½·¨£º + + ``` + public void rollbackToSavepoint(Object savepoint) throws TransactionException { + ConnectionHolder conHolder = getConnectionHolderForSavepoint(); + try { + conHolder.getConnection().rollback((Savepoint) savepoint); + conHolder.resetRollbackOnly(); + } + catch (Throwable ex) { + throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex); + } + } + + ``` + + Êͷű£´æµãµÄ²Ù×÷ÔÚ `JdbcTransactionObjectSupport#releaseSavepoint` ·½·¨£º + + ``` + public void releaseSavepoint(Object savepoint) throws TransactionException { + ConnectionHolder conHolder = getConnectionHolderForSavepoint(); + try { + conHolder.getConnection().releaseSavepoint((Savepoint) savepoint); + } + catch (Throwable ex) { + logger.debug("Could not explicitly release JDBC savepoint", ex); + } + } + + ``` + + ×îÖÕ¶¼Êǵ÷Óà `java.sql.Connection` ÌṩµÄ·½·¨À´Íê³É²Ù×÷¡£ + +##### Ìá½»²Ù×÷ + +ÔÙÀ´¿´¿´Ìá½»²Ù×÷£¬´¦ÀíÌá½»²Ù×÷µÄ´úÂëΪ + +``` +txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); + +``` + +ÎÒÃǸú½øÕâ¸ö·½·¨£¬Ò»Ö±¸úµ½ `AbstractPlatformTransactionManager#processCommit`£º + +``` +private void processCommit(DefaultTransactionStatus status) throws TransactionException { + try { + ... + + try { + if (status.hasSavepoint()) { + ... + unexpectedRollback = status.isGlobalRollbackOnly(); + // 1\. Êͷű£´æµã + status.releaseHeldSavepoint(); + } + else if (status.isNewTransaction()) { + ... + unexpectedRollback = status.isGlobalRollbackOnly(); + // 2\. ´¦ÀíÌá½»²Ù×÷ + doCommit(status); + } + else if (isFailEarlyOnGlobalRollbackOnly()) { + ... + } + + ... + } + catch (...) { + ... + } + } + finally { + // 3\. ´¦ÀíÍê³É²Ù×÷£¬ÔÚÕâÀï»á»Ö¸´¹ÒÆðµÄÊÂÎñ(»Ö¸´Êý¾Ý¿âÁ¬½Ó) + cleanupAfterCompletion(status); + } +} + +``` + +ÒÔÉÏ·½·¨Ê¡ÂÔÁË´óÁ¿´úÂ룬´ó¶àÊÇÓë `TransactionSynchronization` »Øµ÷Ïà¹ØµÄ£¬ÎÒÃǾۼ¯Ö÷Òª²Ù×÷£º + +1. Êͷű£´æµã£ºÕâ¸ö²Ù×÷ÔÚÉÏÃæÒѾ­·ÖÎö¹ýÁË£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË +2. ´¦ÀíÌá½»²Ù×÷£ºÌá½»ÔÚÕâÀï½øÐУ¬Ò»»á·ÖÎö£¬×îºóÎÒÃǻᷢÏÖËüÊǵ÷ÓÃÁË `java.sql.Connection` ÌṩµÄ·½·¨ +3. ´¦ÀíÍê³É²Ù×÷£ºÕâ¸ö²Ù×÷±È½ÏÖØÒª£¬ÖØÖÃÁ¬½ÓµÄÐÅÏ¢¡¢»Ö¸´¹ÒÆðÊÂÎñÁ¬½Ó¾ÍÊÇÔÚÕâÀï½øÐÐµÄ + +ÎÒÃÇÏÈÀ´¿´Ìá½»²Ù×÷£¬Ö±½Ó½øÈë×îÖÕ´úÂ룺`DataSourceTransactionManager#doCommit` + +``` +protected void doCommit(DefaultTransactionStatus status) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); + // »ñÈ¡Á¬½Ó£¬Connection Ϊ java.sql.Connection + Connection con = txObject.getConnectionHolder().getConnection(); + try { + // Ìá½»ÊÂÎñ + con.commit(); + } + catch (SQLException ex) { + throw new TransactionSystemException("Could not commit JDBC transaction", ex); + } +} + +``` + +×îÖÕÒ²Êǵ÷Óà `java.sql.Connection` ÌṩµÄ·½·¨¡£ + +ÔÙÀ´¿´ÏÂÍê³É²Ù×÷µÄ´¦Àí£¬½øÈë `AbstractPlatformTransactionManager#cleanupAfterCompletion` ·½·¨£º + +``` +private void cleanupAfterCompletion(DefaultTransactionStatus status) { + status.setCompleted(); + if (status.isNewSynchronization()) { + TransactionSynchronizationManager.clear(); + } + if (status.isNewTransaction()) { + // ÕâÀï»áÖØÖÃÁ¬½Ó£¬Èç¹ûÊÇÐÂÁ¬½Ó£¬ÔÚÕâÀï»á¹Ø±ÕÁ¬½Ó + doCleanupAfterCompletion(status.getTransaction()); + } + // Èç¹ûÓÐ¹ÒÆðµÄÊÂÎñ£¬ÔÚÕâÀï½øÐлָ´ + if (status.getSuspendedResources() != null) { + Object transaction = (status.hasTransaction() ? status.getTransaction() : null); + // »Ö¸´¹ÒÆðµÄÊÂÎñ + resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources()); + } +} + +``` + +ÎÒÃÇÏÈÀ´¿´ `DataSourceTransactionManager#doCleanupAfterCompletion` ·½·¨: + +``` +protected void doCleanupAfterCompletion(Object transaction) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; + + // ÒÆ³ýÊý¾ÝÔ´ÓëÁ¬½ÓµÄ°ó¶¨¹ØÏµ + if (txObject.isNewConnectionHolder()) { + TransactionSynchronizationManager.unbindResource(obtainDataSource()); + } + + // ÖØÖÃÁ¬½Ó£¬¾ÍÊǽ«Á¬½ÓÐÅÏ¢»Ö¸´µ½Ö´ÐÐÊÂÎñǰµÄ״̬ + Connection con = txObject.getConnectionHolder().getConnection(); + try { + if (txObject.isMustRestoreAutoCommit()) { + con.setAutoCommit(true); + } + DataSourceUtils.resetConnectionAfterTransaction( + con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly()); + } + catch (Throwable ex) { + logger.debug("Could not reset JDBC Connection after transaction", ex); + } + // Èç¹ûÊÇÐÂÁ¬½Ó£¬ÓÃÍê¾ÍÔÚÕâÀï¹Ø±ÕÁ¬½Ó + if (txObject.isNewConnectionHolder()) { + // ×îÖÕµ÷ÓõÄÊÇ java.sql.Connection#close + DataSourceUtils.releaseConnection(con, this.dataSource); + } + + txObject.getConnectionHolder().clear(); +} + +``` + +ÔÙÀ´¿´¿´ÊÂÎñµÄ»Ö¸´²Ù×÷£¬Ò²¾ÍÊÇ `resume(...)` ·½·¨£¬¸ú½øÕâ¸ö·½·¨ºó£¬·¢ÏÖ×îÖÕµ÷ÓõÄÊÇ `DataSourceTransactionManager#doResume` ·½·¨£¬´úÂëÈçÏ£º + +``` +@Override +protected void doResume(@Nullable Object transaction, Object suspendedResources) { + // ½«Êý¾ÝÔ´¡¢¹ÒÆðµÄÊý¾Ý¿âÁ¬½ÓÓ뵱ǰÏß³Ì°ó¶¨ + TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources); +} + +``` + +´¦ÀíÍêÕâÒ»²½ºó£¬´ËʱµÄÊý¾ÝÔ´¾ÍÊÇ֮ǰ¹ÒÆðµÄÊý¾ÝÔ´ÁË¡£ + +#### 3.8 ÖØÖÃÊÂÎñÐÅÏ¢ + +ÔÚijЩ´«²¥·½Ê½Ï£¬±ÈÈç `PROPAGATION_REQUIRES_NEW`£¬ÎÒÃÇÐèÒª¹ÒÆðµ±Ç°ÊÂÎñ£¬È»ºó´´½¨ÐµÄÊÂÎñ£¬ÔÚÐÂÊÂÎñÖ´ÐÐÍê³Éºó£¬ÐèÒª»Ö¸´Ô­À´µÄÊÂÎñ£¬ÕâÀïµÄÖØÖÃÊÂÎñÐÅÏ¢¾ÍÊǽ«µ±Ç°ÊÂÎñÐÅÏ¢»Ö¸´Îª¹ÒÆðµÄÊÂÎñµÄÐÅÏ¢£¨Ö»Êǻָ´ÁËÊÂÎñÐÅÏ¢£¬¹ÒÆðµÄÊý¾Ý¿âÁ¬½Ó²»ÊÇÔÚÕâÀï»Ö¸´£©£º + +``` +public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { + + // ´æ·Åµ±Ç°Ê¹ÓõÄÊÂÎïÐÅÏ¢ + private static final ThreadLocal transactionInfoHolder = + new NamedThreadLocal<>("Current aspect-driven transaction"); + + // ÖØÖÃΪ¾ÉµÄÊÂÎñÐÅÏ¢ + protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) { + if (txInfo != null) { + txInfo.restoreThreadLocalStatus(); + } + } + + /** + * TransactionInfo: ±£´æÊÂÎñÐÅÏ¢ + */ + protected static final class TransactionInfo { + + // µ±Ç°µÄÊÂÎñ״̬¶ÔÏó + @Nullable + private TransactionStatus transactionStatus; + + // ¾ÉµÄÊÂÎñÐÅÏ¢£¨Ò²¾ÍÊÇ¹ÒÆðµÄÊÂÎñÐÅÏ¢£© + @Nullable + private TransactionInfo oldTransactionInfo; + + ... + + private void restoreThreadLocalStatus() { + // ÉèÖÃΪ¾ÉµÄÊÂÎñÐÅÏ¢ + transactionInfoHolder.set(this.oldTransactionInfo); + } + + ... + } + +} + +``` + +¿ÉÒÔ¿´µ½£¬`TransactionInfo` ¶Ô»»³ÖÓÐÉÏÒ»¸öÊÂÎñµÄÐÅÏ¢£¨`oldTransactionInfo` µÄ³ÉÔ±±äÁ¿£©£¬ÖØÖûØÉÏÒ»¸öÊÂÎñÐÅϢʱ£¬Ö»ÊǼòµ¥µØ½« `oldTransactionInfo` ÉèÖõ½ÃûΪ `transactionInfoHolder` µÄ `ThreadLocal` Àï¡£ + +#### 3.9 Ìá½»ÊÂÎñ + +´¦ÀíÊÂÎñÌá½»µÄ´úÂëΪ `TransactionAspectSupport#commitTransactionAfterReturning`£¬´úÂëÈçÏ£º + +``` +protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { + // ÅжÏÏÂÊÂÎñµÄ״̬ + if (txInfo != null && txInfo.getTransactionStatus() != null) { + // ´¦ÀíÌá½»²Ù×÷£¬ÕâÇ°ÃæÒѾ­·ÖÎö¹ýÁË + txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); + } +} + +``` + +Ôڸ÷½·¨ÖУ¬ÎÒÃÇÓÖ¿´µ½ÁËÕâÐдúÂ룺 + +``` +txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); + +``` + +Õâ¸öÒѾ­ÔÚÓÉ**Òì³£ÒýÆðµÄÊÂÎñÌá½»**ÖзÖÎö¹ýÁË£¬¾Í²»ÔÙ·ÖÎöÁË¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4947800](https://my.oschina.net/funcy/blog/4947800) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" new file mode 100644 index 0000000..eba0726 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\344\272\213\345\212\241/Spring\344\272\213\345\212\241\357\274\210\345\233\233\357\274\211\357\274\232\344\272\213\345\212\241\347\232\204\351\232\224\347\246\273\347\272\247\345\210\253\344\270\216\344\274\240\346\222\255\346\226\271\345\274\217\347\232\204\345\244\204\347\220\20602.md" @@ -0,0 +1,580 @@ +±¾ÎÄÊÇ¡¶ÊÂÎñµÄ¸ôÀë¼¶±ðÓë´«²¥·½Ê½µÄ´¦Àí¡··ÖÎöµÄµÚ 2 ƪ£¬½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø¡£ + +#### 3.5 »ñÈ¡ÊÂÎñÐÅÏ¢ + +ÊÂÎñµÄÐÅÏ¢»áÔÚ `TransactionAspectSupport#createTransactionIfNecessary` ·½·¨ÖлñÈ¡£¬Õâ¸ö·½·¨·Ç³£ÖØÒª£¬Ç°Ãæ½éÉܸôÀë¼¶±ð¡¢´«²¥·½Ê½¶¼»áÔÚÕâ¸ö·½·¨Àï´¦Àí¡£¸Ã·½·¨´úÂëÈçÏ£º + +``` +protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, + @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { + // Èç¹ûδָ¶¨Ãû³Æ£¬Ôò½«·½·¨Ãûµ±×öÊÂÎñÃû³Æ + if (txAttr != null && txAttr.getName() == null) { + txAttr = new DelegatingTransactionAttribute(txAttr) { + @Override + public String getName() { + return joinpointIdentification; + } + }; + } + + TransactionStatus status = null; + if (txAttr != null) { + if (tm != null) { + // »ñÈ¡ÊÂÎñ״̬£¬Èç¹ûµ±Ç°Ã»ÓÐÊÂÎñ£¬¿ÉÄܻᴴ½¨ÊÂÎñ + status = tm.getTransaction(txAttr); + } + } + // ×¼±¸ÊÂÎñÐÅÏ¢£¬¾ÍÊǽ«Ç°ÃæµÃµ½µÄÐÅÏ¢·â×°³É TransactionInfo + return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); +} + +``` + +Õâ¸ö·½·¨Ö÷ÒªÊÇÁ½¸ö²Ù×÷£º + +1. »ñÈ¡ÊÂÎñ״̬ +2. ×¼±¸ÊÂÎñÐÅÏ¢ + +ÏÈÀ´¿´Ï»ñÈ¡ÊÂÎñ״̬µÄÁ÷³Ì£¬·½·¨Îª `AbstractPlatformTransactionManager#getTransaction`£º + +``` +public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) + throws TransactionException { + + TransactionDefinition def = (definition != null ? + definition : TransactionDefinition.withDefaults()); + + // »ñÈ¡ÊÂÎñ¶ÔÏó + Object transaction = doGetTransaction(); + boolean debugEnabled = logger.isDebugEnabled(); + + // ÊÇ·ñ´æÔÚÊÂÎñ£¬´æÔÚÔò·µ»Ø + if (isExistingTransaction(transaction)) { + return handleExistingTransaction(def, transaction, debugEnabled); + } + // ÔËÐе½ÁËÕâÀ±íÃ÷µ±Ç°Ã»ÓÐÊÂÎñ + + // ¼ì²é³¬Ê±Ê±¼äµÄÉèÖÃÊÇ·ñºÏÀí + if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { + throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()); + } + + // PROPAGATION_MANDATORY£º±ØÐëÔÚÊÂÎñÖÐÔËÐУ¬ÕâÀïûÓÐÊÂÎñ£¬Ö±½ÓÅ×Òì³£ + // No existing transaction found -> check propagation behavior to find out how to proceed. + if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { + throw new IllegalTransactionStateException(...); + } + // ¹ÒÆðµ±Ç°ÊÂÎñ£¬´´½¨ÐÂÊÂÎñ + else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { + // suspend(...) ´«Èënull£ºÈç¹ûÓÐͬ²½ÊÂÎñ£¬Ôò¹ÒÆðͬ²½ÊÂÎñ£¬·ñÔòʲôҲ²»×ö + SuspendedResourcesHolder suspendedResources = suspend(null); + try { + boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); + // ´´½¨ÊÂÎñ¶ÔÏó + DefaultTransactionStatus status = newTransactionStatus( + def, transaction, true, newSynchronization, debugEnabled, suspendedResources); + // Æô¶¯ÊÂÎñ + doBegin(transaction, def); + // ÉèÖà TransactionSynchronizationManager µÄÊôÐÔ + prepareSynchronization(status, def); + return status; + } + catch (RuntimeException | Error ex) { + resume(null, suspendedResources); + throw ex; + } + } + else { + boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); + return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); + } +} + +``` + +Õâ¸ö·½·¨Óе㳤£¬ÎÒÃÇÂýÂý·ÖÎö¡£ + +##### 1. `doGetTransaction(...)`£º»ñÈ¡ÊÂÎñ¶ÔÏó + +»ñÈ¡ÊÂÎñ¶ÔÏóµÄ·½·¨Îª `DataSourceTransactionManager#doGetTransaction`£º + +``` +protected Object doGetTransaction() { + DataSourceTransactionObject txObject = new DataSourceTransactionObject(); + txObject.setSavepointAllowed(isNestedTransactionAllowed()); + // »ñÈ¡Á¬½ÓÐÅÏ¢£¬obtainDataSource()£º»ñÈ¡Êý¾ÝÔ´ + ConnectionHolder conHolder = + (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); + txObject.setConnectionHolder(conHolder, false); + return txObject; +} + +``` + +ÕâÀïÓÐÁ½¸ö²Ù×÷£º + +1. »ñÈ¡Êý¾ÝÔ´ +2. »ñÈ¡ `ConnectionHolder` + +ÎÒÃÇÏÈÀ´¿´¿´Êý¾ÝÔ´ÊÇÈçºÎ»ñÈ¡µÄ£º + +``` +public class DataSourceTransactionManager extends AbstractPlatformTransactionManager + implements ResourceTransactionManager, InitializingBean { + + @Nullable + private DataSource dataSource; + + /** + * ¹¹Ôì·½·¨´«ÈëÁËÊý¾ÝÔ´ + */ + public DataSourceTransactionManager(DataSource dataSource) { + this(); + setDataSource(dataSource); + afterPropertiesSet(); + } + + /** + * ÉèÖÃÊý¾ÝÔ´ + */ + public void setDataSource(@Nullable DataSource dataSource) { + if (dataSource instanceof TransactionAwareDataSourceProxy) { + this.dataSource = ((TransactionAwareDataSourceProxy) dataSource) + .getTargetDataSource(); + } + else { + this.dataSource = dataSource; + } + } + + @Nullable + public DataSource getDataSource() { + return this.dataSource; + } + + /** + * »ñÈ¡Êý¾ÝÔ´ + */ + protected DataSource obtainDataSource() { + DataSource dataSource = getDataSource(); + Assert.state(dataSource != null, "No DataSource set"); + return dataSource; + } + + ... +} + +``` + +`obtainDataSource()` ʵ¼ÊÉÏÊǵ÷ÓÃÁË `getDataSource()` ·½·¨£¬·µ»ØµÄÊÇ `dataSource` ³ÉÔ±±äÁ¿£¬¶ø `dataSource` ÓÖÊÇÔÚ `DataSourceTransactionManager` µÄ¹¹Ôì·½·¨Àï´«ÈëµÄ£¬Òò´Ë£¬µÃµ½µÄ½áÂÛÊÇ£¬ÕâÀï»ñÈ¡µÄÊý¾ÝÔ´¾ÍÊÇÎÒÃÇÔÚÉèÖà `DataSourceTransactionManager` ʱ´«ÈëµÄ£º + +``` +@Configuration +public class TxDemo03Config { + + /** + * Éú³ÉÊý¾ÝÔ´ + * @return + * @throws Exception + */ + @Bean + public DataSource dataSource() throws Exception { + Driver driver = new com.mysql.jdbc.Driver(); + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "123"; + return new SimpleDriverDataSource(driver, url, username, password); + } + + /** + * ÊÂÎñ¹ÜÀíÆ÷ + * @param dataSource + * @return + */ + @Bean + public DataSourceTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + + ... + +} + +``` + +¶øÕâ¸öÊý¾ÝÔ´£¬ÕýÊÇ `SimpleDriverDataSource`. + +ÎÒÃÇÔÙÀ´¿´¿´ `ConnectionHolder` µÄ»ñÈ¡£¬¸Ã·½·¨Îª `TransactionSynchronizationManager#getResource` £¬´úÂëÈçÏ£º + +``` +// Óà ThreadLocal À´´æ·Å ConnectionHolder ÐÅÏ¢ +private static final ThreadLocal> resources = + new NamedThreadLocal<>("Transactional resources"); + +/** + * »ñÈ¡ ConnectionHolder + */ +public static Object getResource(Object key) { + // °ü×°Ï´«ÈëµÄ key + Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); + // ÔÚÕâÀï»ñÈ¡Á¬½ÓÐÅÏ¢ + Object value = doGetResource(actualKey); + return value; +} + +/** + * ¾ßÌåµÄ»ñÈ¡²Ù×÷ + */ +private static Object doGetResource(Object actualKey) { + // ´ÓThreadLocalÖлñÈ¡ + Map map = resources.get(); + if (map == null) { + return null; + } + Object value = map.get(actualKey); + if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { + map.remove(actualKey); + if (map.isEmpty()) { + resources.remove(); + } + value = null; + } + return value; +} + +``` + +´Ó´úÂëÀ´¿´£¬`TransactionSynchronizationManager` ³ÖÓÐÒ»¸ö `ThreadLocal` µÄʵÀý£¬ÆäÖдæ·ÅÁËÒ»¸ö `Map`£¬¸Ã `Map` µÄ `key` Ϊ `datasource`£¬`value` Ϊ `ConnectionHolder`. + +ÄÇôÕâ¸ö `ConnectionHolder` ÊÇÊ²Ã´ÄØ£¿¿ÉÒÔ¼òµ¥µØ½«ÆäÀí½âΪ `Connection`(Êý¾Ý¿âÁ¬½Ó) µÄ°ü×°À࣬ÆäÖÐ×îÖØÒªµÄÊôÐÔ¾ÍÊÇ `Connection` ÁË£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d8c4ae3884177f485fbb95d1828fdb39ae2.png) + +ºÃÁË£¬µ½ÕâÀï¾Í°Ñ `doGetTransaction(xxx)` ·½·¨·ÖÎöÍêÁË£¬ÔÙÀ´¿´¿´Õâ¸ö·½·¨·µ»ØµÄ½á¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0944429e31a6c0b67e121674202dd5ec0fd.png) + +##### 2. `isExistingTransaction(...)`£ºÊÇ·ñ´æÔÚÊÂÎñ + +»ñÈ¡µ½ÊÂÎñ¶ÔÏó `DataSourceTransactionObject` ºó£¬½ÓÏÂÀ´¾ÍÊÇÅжÏÊÇ·ñ´æÔÚÊÂÎñÁË£¬ÅжϷ½·¨ÔÚ `DataSourceTransactionManager#isExistingTransaction`£¬´úÂëÈçÏ£º + +``` +protected boolean isExistingTransaction(Object transaction) { + DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; + return (txObject.hasConnectionHolder() + && txObject.getConnectionHolder().isTransactionActive()); +} + +``` + +`ConnectionHolder` ÖÐÓÐÒ»¸ö³ÉÔ±±äÁ¿ `transactionActive`£¬Ëü±íÃ÷µ±Ç° `ConnectionHolder` µÄÊÂÎñÊÇ·ñ´¦ÓÚ¼¤»î״̬£¬`isExistingTransaction(...)` ·½·¨Ö÷ÒªÊǸù¾ÝËüÀ´Åжϵ±Ç°ÊÂÎñ¶ÔÏóÊÇ·ñ´æÔÚÊÂÎñµÄ¡£ + +##### 3\. ´¦ÀíÒÑ´æÔÚµÄÊÂÎñ£º`handleExistingTransaction(...)` + +ÕâÀïÎÒÃÇÀ´¿´¿´Èç¹ûµ±Ç°´æÔÚÊÂÎñ£¬spring ÊÇÔõô´¦ÀíµÄ£¬´¦ÀíÒÑ´æÔÚÊÂÎñµÄ·½·¨Îª `AbstractPlatformTransactionManager#handleExistingTransaction`£¬´úÂëÈçÏ£º + +``` +private TransactionStatus handleExistingTransaction(TransactionDefinition definition, + Object transaction, boolean debugEnabled) throws TransactionException { + // µ±´«²¥·½Ê½Îª¡¾²»Ê¹ÓÃÊÂÎñ¡¿Ê±£¬Å׳öÒì³£ + if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { + throw new IllegalTransactionStateException( + "Existing transaction found for transaction marked with propagation 'never'"); + } + // µ±´«²¥·½Ê½Îª¡¾²»Ö§³ÖÊÂÎñ¡¿Ê±£¬¹ÒÆðµ±Ç°ÊÂÎñ£¬È»ºóÔÚÎÞÊÂÎñµÄ״̬ÖÐÔËÐÐ + if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { + // 1\. suspend()£º¹ÒÆðÊÂÎñ²Ù×÷ + Object suspendedResources = suspend(transaction); + boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); + return prepareTransactionStatus( + definition, null, false, newSynchronization, debugEnabled, suspendedResources); + } + + // µ±´«²¥·½Ê½Îª¡¾ÔÚеÄÊÂÎñÖÐÔËÐС¿Ê±£¬¹ÒÆðµ±Ç°ÊÂÎñ£¬È»ºóÆô¶¯ÐµÄÊÂÎñ + if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { + // ¹ÒÆðÊÂÎñ²Ù×÷ + SuspendedResourcesHolder suspendedResources = suspend(transaction); + try { + boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); + DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, + newSynchronization, debugEnabled, suspendedResources); + // 2\. doBegin()£ºÆô¶¯ÐµÄÊÂÎñ + doBegin(transaction, definition); + prepareSynchronization(status, definition); + return status; + } + catch (RuntimeException | Error beginEx) { + resumeAfterBeginException(transaction, suspendedResources, beginEx); + throw beginEx; + } + } + + // µ±´«²¥·½Ê½Îª¡¾Ç¶Ì×Ö´ÐС¿Ê±£¬ ÉèÖÃÊÂÎñµÄ±£´æµã + // ´æÔÚÊÂÎñ£¬½«¸ÃÊÂÎñ±ê×¢±£´æµã£¬ÐγÉǶÌ×ÊÂÎñ¡£ + // ǶÌ×ÊÂÎñÖеÄ×ÓÊÂÎñ³öÏÖÒì³£²»»áÓ°Ïìµ½¸¸ÊÂÎñ±£´æµã֮ǰµÄ²Ù×÷¡£ + if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { + if (!isNestedTransactionAllowed()) { + throw new NestedTransactionNotSupportedException(...); + } + // 3\. createAndHoldSavepoint(...)£º´´½¨±£´æµã£¬»Ø¹öʱֻ»Ø¹öµ½¸Ã±£´æµã + if (useSavepointForNestedTransaction()) { + DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, + false, false, debugEnabled, null); + status.createAndHoldSavepoint(); + return status; + } + else { + boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); + DefaultTransactionStatus status = newTransactionStatus( + definition, transaction, true, newSynchronization, debugEnabled, null); + // Èç¹û²»Ö§³Ö±£´æµã£¬¾ÍÆô¶¯ÐµÄÊÂÎñ + doBegin(transaction, definition); + prepareSynchronization(status, definition); + return status; + } + } + if (isValidateExistingTransaction()) { + // ´¦ÀíÑéÖ¤²Ù×÷£¬²»×÷·ÖÎö + ... + } + boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); + return prepareTransactionStatus(definition, transaction, false, + newSynchronization, debugEnabled, null); +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨Àï¾Í´¦ÀíÁËÊÂÎñµÄ¸ôÀë¼¶±ðµÄÂß¼­£¬Ïà¹ØµÄ´úÂëÒѾ­×÷ÁË×¢ÊÍ£¬ÕâÀï¾Í²»¶à˵ÁË£¬²»¹ýÕâÀïÓм¸¸ö·½·¨ÐèÒªÌØ±ðÌá³ö£º + +1. `suspend()`£º¹ÒÆðÊÂÎñ²Ù×÷ +2. `doBegin()`£ºÆô¶¯ÐµÄÊÂÎñ +3. `createAndHoldSavepoint(...)`£º´´½¨±£´æµã£¬»Ø¹öʱֻ»Ø¹öµ½¸Ã±£´æµã + +Õ⼸¸ö²Ù×÷°üº¬ÁËÊÂÎñ´¦Àí²Ù×÷£¬ºóÃæÎÒÃÇÔÙͳһ·ÖÎö¡£ + +##### 4\. ¼ÌÐø `AbstractPlatformTransactionManager#getTransaction` + +ÈÃÎÒÃÇÔٻص½ `AbstractPlatformTransactionManager#getTransaction` ·½·¨£¬¼ÌÐøÊ£ÏµÄÁ÷³Ì£º + +``` +public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) + throws TransactionException { + + // Ç°ÃæÒѾ­·ÖÎö¹ýÁË£¬Ê¡ÂÔ + ... + + // ÔËÐе½ÁËÕâÀ±íÃ÷µ±Ç°Ã»ÓÐÊÂÎñ + + // ¼ì²é³¬Ê±Ê±¼äµÄÉèÖÃÊÇ·ñºÏÀí + if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { + throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()); + } + + // PROPAGATION_MANDATORY£º±ØÐëÔÚÊÂÎñÖÐÔËÐУ¬ÕâÀïûÓÐÊÂÎñ£¬Ö±½ÓÅ×Òì³£ + if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { + throw new IllegalTransactionStateException(...); + } + // ¹ÒÆðµ±Ç°ÊÂÎñ£¬´´½¨ÐÂÊÂÎñ + else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { + // suspend(...) ´«Èënull£ºÈç¹ûÓÐͬ²½ÊÂÎñ£¬Ôò¹ÒÆðͬ²½ÊÂÎñ£¬·ñÔòʲôҲ²»×ö + SuspendedResourcesHolder suspendedResources = suspend(null); + try { + boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); + // ´´½¨ÊÂÎñ¶ÔÏó + DefaultTransactionStatus status = newTransactionStatus( + def, transaction, true, newSynchronization, debugEnabled, suspendedResources); + // Æô¶¯ÊÂÎñ + doBegin(transaction, def); + // ÉèÖà TransactionSynchronizationManager µÄÊôÐÔ + prepareSynchronization(status, def); + return status; + } + catch (RuntimeException | Error ex) { + resume(null, suspendedResources); + throw ex; + } + } + else { + boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); + return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); + } +} + +``` + +`handleExistingTransaction(...)` ·½·¨µÄ¹¦ÄÜÊÇ**µ±ÊÂÎñ´æÔÚʱ£¬ÄÇЩ¸ö´«²¥ÀàÐÍÒªÔõô´¦Àí**£¬`getTransaction(...)` ·½·¨Óàϲ¿·ÖµÄ¹¦ÄÜÊÇ£¬**µ±ÊÂÎñ´æÔÚʱ£¬ÄÇЩ¸ö´«²¥ÀàÐÍÓÖÒªÔõô´¦Àí**¡£¿ÉÒÔ¿´µ½£¬ÕâÀïÃæÒÀÈ»»¹ÊÇÓÐ `suspend(...)`¡¢`doBegin(...)` µÈ·½·¨£¬ÕâЩ¸ö·½·¨ÎÒÃÇÒ»»áҲͳһ·ÖÎö¡£ + +##### 5\. ×¼±¸·µ»Ø½á¹û£º`prepareTransactionStatus(...)` + +`handleExistingTransaction(...)` ·½·¨Óë `getTransaction(...)` ·½·¨ÔÚ´¦Àí·µ»Ø½á¹ûʱ£¬¶¼Ê¹ÓÃÁË `prepareTransactionStatus(...)` ·½·¨£º + +``` +// `handleExistingTransaction(...)`·½·¨ +return prepareTransactionStatus(definition, transaction, false, + newSynchronization, debugEnabled, null); + +// `getTransaction(...)`·½·¨ +return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); + +``` + +ÎÒÃÇÀ´·ÖÎöÏÂÕâ¸ö·½·¨ÊÇ×öÁËɶ£¬½øÈë `AbstractPlatformTransactionManager#prepareTransactionStatus`£º + +``` +protected final DefaultTransactionStatus prepareTransactionStatus( + TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction, + boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { + + // ´´½¨ÁËÒ»¸ö DefaultTransactionStatus ¶ÔÏó + DefaultTransactionStatus status = newTransactionStatus( + definition, transaction, newTransaction, newSynchronization, debug, suspendedResources); + // ×¼±¸ Synchronization + prepareSynchronization(status, definition); + return status; +} + +/** + *´´½¨Ò»¸ö TransactionStatus ʵÀý + */ +protected DefaultTransactionStatus newTransactionStatus( + TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction, + boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { + + boolean actualNewSynchronization = newSynchronization && + !TransactionSynchronizationManager.isSynchronizationActive(); + // µ÷Óà DefaultTransactionStatus µÄ¹¹Ôì·½·¨ + return new DefaultTransactionStatus( + transaction, newTransaction, actualNewSynchronization, + definition.isReadOnly(), debug, suspendedResources); +} + +``` + +ËùÒÔÕâ¸ö·½·¨Ö÷Òª¾ÍÊÇΪÁË´´½¨ `DefaultTransactionStatus` ¶ÔÏó£¡ÎÒÃÇÀ´¿´ÏÂÕâÒ»²½µÄÔËÐнá¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a491ee7ca22238b2d9c698c55ae9dd6d005.png) + +##### 6\. ×¼±¸ÊÂÎñÐÅÏ¢£º`TransactionAspectSupport#prepareTransactionInfo` + +»Øµ½ `TransactionAspectSupport#createTransactionIfNecessary` ·½·¨£º + +``` +protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, + @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { + ... + TransactionStatus status = null; + if (txAttr != null) { + if (tm != null) { + // »ñÈ¡ÊÂÎñ״̬£¬Èç¹ûµ±Ç°Ã»ÓÐÊÂÎñ£¬¿ÉÄܻᴴ½¨ÊÂÎñ + status = tm.getTransaction(txAttr); + } + } + // ×¼±¸ÊÂÎñÐÅÏ¢£¬¾ÍÊǽ«Ç°ÃæµÃµ½µÄÐÅÏ¢·â×°³É TransactionInfo + return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); +} + +``` + +Ç°Ãæ·ÖÎöÁËÄÇô¶à£¬Ö»Êǵõ½ÁË `TransactionStatus`£¬ÎÒÃÇÔÙ½ÓÔÙÀ÷£¬¼ÌÐø·ÖÎö×¼±¸ÊÂÎñÐÅÏ¢µÄ·½·¨ `prepareTransactionInfo(...)`£º + +``` +protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, + @Nullable TransactionAttribute txAttr, String joinpointIdentification, + @Nullable TransactionStatus status) { + TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification); + + if (txAttr != null) { + txInfo.newTransactionStatus(status); + } + + // Ê¡ÂÔlogµÄ´òÓ¡ + ... + + // ÓëÏß³Ì°ó¶¨ + txInfo.bindToThread(); + return txInfo; +} + +``` + +àÅ£¬Í¬ `prepareTransactionStatus(...)` ÀàËÆ£¬Õâ¸ö·½·¨Ò²ÊÇ´´½¨ÁËÒ»¸ö `TransactionInfo` ¶ÔÏ󣬲¢ÇÒ½« `TransactionInfo` Ó뵱ǰḬ̈߳󶨣¬°ó¶¨µÄ´úÂëÈçÏ£º + +``` +public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { + + // ´æ·Åµ±Ç°Ê¹ÓõÄÊÂÎïÐÅÏ¢ + private static final ThreadLocal transactionInfoHolder = + new NamedThreadLocal<>("Current aspect-driven transaction"); + + // ÖØÖÃΪ¾ÉµÄÊÂÎñÐÅÏ¢ + protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) { + if (txInfo != null) { + txInfo.restoreThreadLocalStatus(); + } + } + + /** + * TransactionInfo: ±£´æÊÂÎñÐÅÏ¢ + */ + protected static final class TransactionInfo { + + // µ±Ç°µÄÊÂÎñ״̬¶ÔÏó + @Nullable + private TransactionStatus transactionStatus; + + // ¾ÉµÄÊÂÎñÐÅÏ¢£¨Ò²¾ÍÊÇ¹ÒÆðµÄÊÂÎñÐÅÏ¢£© + @Nullable + private TransactionInfo oldTransactionInfo; + + /** + * ½«ÊÂÎñÐÅÏ¢°ó¶¨µ½µ±Ç°Ïß³Ì + */ + private void bindToThread() { + // Äõ½¾ÉµÄÊÂÎñÐÅÏ¢ + this.oldTransactionInfo = transactionInfoHolder.get(); + // ÉèÖóÉ×îеÄÊÂÎñÐÅÏ¢ + transactionInfoHolder.set(this); + } + + /** + * ÊÂÎñÍê³Éºó£¬»á½«¾ÉµÄÊÂÎñÐÅÏ¢°ó¶¨µ½µ±Ç°Ïß³Ì + */ + private void restoreThreadLocalStatus() { + // ÉèÖÃΪ¾ÉµÄÊÂÎñÐÅÏ¢ + transactionInfoHolder.set(this.oldTransactionInfo); + } + + ... + } + +} + +``` + +`TransactionAspectSupport` ÖÐÓÐÒ»¸ö `ThreadLocal`£¬ÓÃÀ´´æ·Åµ±Ç°µÄ `TransactionInfo` ¶ÔÏ󣬽øÐÐḬ̈߳ó¶¨Ê±£¬»áÏÈÄõ½¾ÉµÄÊÂÎñÐÅÏ¢£¬±£´æÔÚ `TransactionInfo` µÄ³ÉÔ±±äÁ¿ `oldTransactionInfo` ÖУ¬È»ºó½«Ð嵀 `TransactionInfo` ·ÅÈë `ThreadLocal` ÖУ»µ±ÊÂÎñÖ´ÐÐÍê³Éºó£¬»á´Ó `TransactionInfo` µÄ³ÉÔ±±äÁ¿ `oldTransactionInfo` ÖÐÄõ½¾ÉµÄÊÂÎñÐÅÏ¢£¬ÔÙ½«¾ÉµÄÊÂÎñÐÅÏ¢·ÅÈë `ThreadLocal` ÖУ¬Õâ¾ÍÍê³ÉÁËÊÂÎñ "¾É - Р- ¾É" µÄÇл». + +ÕâÒ»²½µÃµ½µÄ `TransactionInfo` ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ab882613ce67ba3bf7afcbd2eab01c6cf27.png) + +¶ÔÓÚ `TransactionInfo` µÄ½á¹¹£¬ÕâÀï×÷һЩ˵Ã÷£º + +* ÀàÐÍÊÇ `TransactionAspectSupport.TransactionInfo`£¬ÊÇ `TransactionAspectSupport` µÄÒ»¸öÄÚ²¿À࣬·â×°ÁËÊÂÎñµÄһЩÐÅÏ¢ +* `transactionManager`: ÊÂÎñ¹ÜÀíÆ÷£¬¾ÍÊÇÎÒÃÇÉèÖÃµÄ `DataSourceTransactionManager` +* `transactionAttribute`: ÊÂÎñÊôÐÔÖµ£¬ÓÃÀ´±£´æ `@Transactional` ×¢½âµÄÊôÐÔÖµ +* `joinpointIdentification`: ·½·¨µÄÈ«ÏÞ¶¨Ãû£¨¸ñʽΪ£º"°üÃû¡£ÀàÐÍ¡£·½·¨Ãû"£© +* `transactionStatus`: ´ÓÃû³ÆÉÏ¿´£¬ÊǼǼÊÂÎñµÄ״̬£¬Êµ¼ÊÕâ¸ö¶ÔÏó²»½ö¼Ç¼ÁËÊÂÎñµÄ״̬£¬°üÀ¨µÄÖØ´ó¹¦ÄÜÈçÏ£º + * `complete`: ÊÂÎñµÄÍê³É״̬ + * `connectionHolder`: µ±Ç°³ÖÓеÄÊý¾Ý¿âÁ¬½Ó + * `suspendedResources`: ¹ÒÆðµÄÊý¾Ý¿âÁ¬½Ó£¬µ±ÐèÒª»Ö¸´¹ÒÆðµÄÊÂÎñʱ£¬¿ÉÒÔÄܹý¸Ã¶ÔÏóÄõ½¹ÒÆðµÄÊý¾Ý¿âÁ¬½Ó +* `oldTransactionInfo`: ÉÏÒ»¸öÊÂÎñ£¨Ò²¾ÍÊÇ¹ÒÆðµÄÊÂÎñ£©µÄÐÅÏ¢£¬Ö´ÐÐÍ굱ǰÊÂÎñºó£¬»á»Ö¸´µ½ÉÏÒ»¸öÊÂÎñµÄÖ´ÐÐ + +Ò»²»Ð¡ÐÄÓÖдÁËÕâô³¤ÁË£¬±¾ÎľÍÏÈ·ÖÎöµ½ÕâÀïÁË£¬`suspend(...)`¡¢`doBegin(...)` µÈ·½·¨ÏÂÆªÔÙ·ÖÎö°É¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4947799](https://my.oschina.net/funcy/blog/4947799) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" new file mode 100644 index 0000000..841ee00 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\246\202\350\247\210.md" @@ -0,0 +1,230 @@ +在å‰�é�¢çš„æ–‡ç« ä¸­ï¼Œæˆ‘们æˆ�功的编译了 spring æº�ç �,也构建了第一个 spring 测试 demo,接下æ�¥æˆ‘们就基于[第一个 spring æº�ç �调试 demo](https://my.oschina.net/funcy/blog/4533250 "第一个springæº�ç �调试demo") 中的代ç �,æ�¥å¯¹ spring æº�ç �进行æº�ç �分æž�。 + +### 1\. spring å�¯åЍæµ�程概览 + +在å‰�é�¢ demo çš„ `main()` 方法中,有这么一行: + +``` +ApplicationContext context = + new AnnotationConfigApplicationContext("org.springframework.learn.demo01"); + +``` + +这短短的一行就是 spring 的整个å�¯åЍæµ�程了。上é�¢çš„代ç �中,声明了一个 `ApplicationContext` 类型的对象 `context`,å�³è¾¹ä½¿ç”¨å…¶å­�ç±» `AnnotationConfigApplicationContext` 实例化,并在构造方法中传入了包å�� `org.springframework.learn.demo01`,这个包å��就表明了接下æ�¥è¦�扫æ��哪些包。 + +> 这里我们接触到了 spring 的第一个组件:`ApplicationContext`,关于 `ApplicationContext` 的分æž�,å�¯ä»¥å�‚考我的文章 [spring 组件(一):ApplicationContext](https://my.oschina.net/funcy/blog/4597456 "spring组件(一):ApplicationContext")。 + +进入到 `AnnotationConfigApplicationContext`,代ç �如下: + +> AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + +``` +public AnnotationConfigApplicationContext(String... basePackages) { + // 1\. 调用无å�‚构造函数,会先调用父类GenericApplicationContext的构造函数 + // 2\. 父类的构造函数里é�¢å°±æ˜¯åˆ�始化DefaultListableBeanFactory,并且赋值给beanFactory + // 3\. 本类的构造函数里é�¢ï¼Œåˆ�始化了一个读å�–器:AnnotatedBeanDefinitionReader read, + // 一个扫æ��器ClassPathBeanDefinitionScanner scanner + // 4\. 这个scanner,就是下é�¢ scan(basePackages) 调用的对象 + this(); + + //对传入的包进行扫æ��,扫æ��完æˆ�å�Žï¼Œä¼šå¾—到一个 BeanDefinition 的集å�ˆ + scan(basePackages); + + //å�¯åЍspring,在这里完æˆ�spring容器的åˆ�始化æ“�作, + //包括bean的实例化ã€�属性注入,将beanä¿�存到spring容器中等 + refresh(); +} + +``` + +这个类就三行,相关æ“�作都已在代ç �中注释了,这里ç¨�å¾®å†�总结下,这段代ç �主è¦�å�šäº†ä¸‰ä»¶äº‹ï¼š + +1. 调用无å�‚构造,进行属性åˆ�始化 +2. 进行包扫æ��,得到 BeanDefinition +3. å�¯ç”¨ spring 容器。 + +接ç�€ï¼Œæˆ‘们å†�æ�¥çœ‹çœ‹ spring å�¯åЍæµ�程中,å�šäº†å“ªäº›äº‹ï¼š + +> AbstractApplicationContext#refresh + +``` +public void refresh() throws BeansException, IllegalStateException { + // 使用synchronized是为了é�¿å…�refresh() 还没结æ�Ÿï¼Œå†�次å�‘èµ·å�¯åŠ¨æˆ–è€…é”€æ¯�容器引起的冲çª� + synchronized (this.startupShutdownMonitor) { + // å�šä¸€äº›å‡†å¤‡å·¥ä½œï¼Œè®°å½•容器的å�¯åŠ¨æ—¶é—´ã€�标记“已å�¯åЍâ€�状æ€�ã€�检查环境å�˜é‡�ç­‰ + prepareRefresh(); + + // åˆ�始化BeanFactory容器ã€�注册BeanDefinition, 最终获得了DefaultListableBeanFactory + ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); + + // 还是一些准备工作: + // 1\. 设置了一个类加载器 + // 2\. 设置了bean表达å¼�è§£æž�器 + // 3\. 添加了属性编辑器的支æŒ� + // 4\. 添加了一个å�Žç½®å¤„ç�†å™¨ï¼šApplicationContextAwareProcessor + // 5\. 设置了一些忽略自动装é…�的接å�£ + // 6\. 设置了一些å…�许自动装é…�的接å�£ï¼Œå¹¶ä¸”进行了赋值æ“�作 + // 7\. 在容器中还没有XXçš„bean的时候,帮我们注册beanName为XXçš„singleton bean + prepareBeanFactory(beanFactory); + + try { + // Spring的一个扩展点. 如果有Bean实现了BeanFactoryPostProcessor接å�£ï¼Œ + // 那么在容器åˆ�始化以å�Žï¼ŒSpring 会负责调用里é�¢çš„ postProcessBeanFactory 方法。 + // 具体的å­�ç±»å�¯ä»¥åœ¨è¿™æ­¥çš„æ—¶å€™æ·»åŠ ç‰¹æ®Šçš„ BeanFactoryPostProcessor 的实现类,æ�¥å�šäº›äº‹ + postProcessBeanFactory(beanFactory); + + // 调用BeanFactoryPostProcessorå�„个实现类的postProcessBeanFactory(factory) 方法 + invokeBeanFactoryPostProcessors(beanFactory); + + // 扩展点,注册 BeanPostProcessor 的实现类,注æ„�ä¸�是BeanFactoryPostProcessor + registerBeanPostProcessors(beanFactory); + + // åˆ�始化当å‰� ApplicationContext çš„ MessageSource,用在国际化æ“�作中 + initMessageSource(); + + // 这个方法主è¦�为åˆ�始化当å‰� ApplicationContext 的事件广播器 + initApplicationEventMulticaster(); + + // 这也是spring的一个扩展点 + onRefresh(); + + // Check for listener beans and register them. + // 注册事件监å�¬å™¨ + registerListeners(); + + // åˆ�始化所有的 singleton beans + finishBeanFactoryInitialization(beanFactory); + + // 完æˆ�å�¯åŠ¨ï¼Œ + finishRefresh(); + } + + catch (BeansException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Exception encountered during context initialization - " + + "cancelling refresh attempt: " + ex); + } + + // Destroy already created singletons to avoid dangling resources. + // 销æ¯�å·²ç»�åˆ�始化的的Bean + destroyBeans(); + + // Reset 'active' flag. + // é‡�ç½® 'active' 状æ€� + cancelRefresh(ex); + + // Propagate exception to caller. + throw ex; + } + + finally { + // Reset common introspection caches in Spring's core, since we + // might not ever need metadata for singleton beans anymore... + // 清除缓存 + resetCommonCaches(); + } + } +} + +``` + +这个方法虽然代ç �ä¸�多,但包å�«äº† spring bean 的整个创建过程,æ¯�个方法å�šäº†äº›ä»€ä¹ˆï¼Œåœ¨ä»£ç �中都有注释,这里就ä¸�赘述了。 + +实际上,`refresh()` 涵盖了 spring 整个创建 bean çš„æµ�程,在å�Žé�¢çš„æ–‡ç« ä¸­ï¼Œæˆ‘们也将é‡�点展开这里é�¢çš„æ–¹æ³•æ�¥åˆ†æž�,在现阶段å�ªéœ€è¦�大致了解这些方法å�šäº†ä»€ä¹ˆäº‹å�³å�¯ã€‚ + +整个æµ�程总结如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9307fefa65470e5c36ae6044631b5416aef.png) + +### 2\. spring å�¯åЍ䏭 `beanFactory` çš„å�˜åŒ– + +本文中的æº�ç �解读就到这里了,接下æ�¥æˆ‘们æ�¥çœ‹çœ‹ï¼Œspring å�¯åЍ䏭 `beanFactory` 有些什么å�˜åŒ–。 + +> `beanFactory` 是 spring çš„é‡�è¦�组件之一,直译为 spring bean 工厂,是 spring 生产 bean 与ä¿�å­˜ bean 的地方,关于 `beanFactory` 的详细分æž�,å�¯ä»¥æŸ¥çœ‹ [spring BeanFactory 分æž�](https://my.oschina.net/funcy/blog/4597529 "spring BeanFactory分æž�")。 + +我们将断点打在 `AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)` çš„ `this()` 方法上,然å�Žè¿�行 demo01 çš„ `main()` 方法: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c3c672a675d9b06f03ea29cb31f6ed5d012.png) + +此时的å�˜é‡�中,并没有 `beanFactory`,我们自己添加 `beanFactory` 到调度窗å�£çš„å�˜é‡�列表中: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e9d8ae8fdd3b02b2279376303e3eae4cf2f.png) + +这样就能看到对应的值了: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-44d1ade26f0cb667425b0dd99d82666877f.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œæ­¤æ—¶çš„ `beanFactory` 为 null,表明 `beanFactory` 并未实例化,我们继续è¿�行: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-664780be9dfef73c12a3f163b349e7e54d8.png) + +当è¿�行完 `this()` å�Žï¼Œå�‘现 `beanFactory` å·²ç»�有值了,类型为 `DefaultListableBeanFactory`。但是,在查看 `beanFactory` 对象时,å�‘现 `beanFactory` 的属性太多了,我们应该é‡�点关注啥呢? + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1b617cf7edda29c652a7661d4be3779ec85.png) + +我们这部分主è¦�关注 spring bean 的创建,因此å�ªéœ€è¦�关注 `beanFactory` 的两个属性就å�¯ä»¥äº†ï¼š + +* beanDefinitionMap:存放 beanDefinition çš„ map. +* singletonObjects:存放 spring bean çš„ map,spring bean 创建å�Žéƒ½å­˜æ”¾åœ¨è¿™é‡Œï¼Œä¹Ÿå�³ç›´è§‚上ç�†è§£çš„ `spring 容器`. + +> `BeanDefinition` 是 spring é‡�è¦�组件之一,为‘spring bean çš„æ��述’,简å�•æ�¥è¯´ï¼Œå°±æ˜¯è¯´æ˜Žäº†ä¸€ä¸ª spring bean 应该如何创建。关于 `BeanDefinition` 的详细分æž�,å�¯ä»¥æŸ¥çœ‹ [spring BeanDefinition 分æž�](https://my.oschina.net/funcy/blog/4597536)。 + +我们手动添加å�˜é‡�,如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0c6368478258f8b9f76b47fc1c85b02f13f.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œæ­¤æ—¶çš„ `beanDefinitionMap` 中已ç»�有 4 个对象了,显然是在 `this()` 方法中添加的,关于这å�—我们å�Žé�¢ä¼šåˆ†æž�。 + +接ç�€è¿�行,å�‘现 `beanDefinitionMap` å�ˆå¤šäº†ä¸¤ä¸ªï¼š + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a493061fbd4b4066f9a4d91e91ff61e8c4e.png) + +这里的 `beanObj1` 与 `beanObj2` 就是我们自己的类了,由此å�¯ä»¥åˆ¤æ–­å‡º **spring 就是在 `AnnotationConfigApplicationContext#scan` 方法中对包进行扫æ��çš„**。 + +接下æ�¥ï¼Œä»£ç �执行进入 `AbstractApplicationContext#refresh` 方法,我们一行行è¿�行下去,å�‘现è¿�行到 `prepareBeanFactory(beanFactory);` 时,`singletonObjects` 中第一次出现了对象: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8cebcb82f5a8754fd1bb4bb3eb3c57dda2d.png) + +å�¯ä»¥çœ‹åˆ°ï¼Œè¿™é‡Œå‡ºçŽ°äº† 3 个类,基本都跟系统ã€�环境相关,如 `environment` 是 spring 当å‰�使用的环境 (`profile`),`systemProperties` 当å‰�系统的属性(æ“�作系统ã€�æ“�作系统版本等)。 + +继续往下è¿�行,å�‘现代ç �è¿�行到 `invokeBeanFactoryPostProcessors(beanFactory)` 时,å�ˆå¤šäº† 4 个类: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-baa09e51272baa384418cb2c82b9dfb079b.png) + +关于这几个类的作用,我们å�Žé�¢çš„æ–‡ç« ä¸­ä¼šåˆ†æž�,这里先ä¸�必管。继续往下è¿�行,å�‘现在 `registerBeanPostProcessors(beanFactory);` 中,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +org.springframework.context.annotation.internalAutowiredAnnotationProcessor + +``` + +这里我们ä¾�æ—§ä¸�用管这个对象,接ç�€è¿�行下去,å�¯ä»¥çœ‹åˆ°åœ¨è¿�行 `initMessageSource()` 时,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +messageSource -> {DelegatingMessageSource@1847} "Empty MessageSource" + +``` + +显然,这个对象是用æ�¥å¤„ç�†å›½é™…化问题的,ä¸�过由于 demo01 中并没有用到国际化,所以这里显示 `Empty MessageSource`。继续è¿�行,å�‘现è¿�行到 `initApplicationEventMulticaster();` 时,å�ˆå¤šäº†ä¸€ä¸ªå¯¹è±¡ï¼š + +``` +applicationEventMulticaster -> {SimpleApplicationEventMulticaster@1869} + +``` + +显然,这个对象是用æ�¥å¤„ç�† `ApplicationContext` 的广播事件的,我们的 demo 中并没有用到,暂时ä¸�å¿…ç�†ä¼šã€‚继续下去,å�‘现在è¿�行完 `finishBeanFactoryInitialization(beanFactory);`,`singletonObjects` 中终于出现了我们期待的对象: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-68b1ee71e468ef8cf839230c07b64c45563.png) + +由此å�¯è§�,对象就是在该方法中创建的。 + +### 总结 + +1. spring 包的æ��述:`AnnotationConfigApplicationContext#scan` +2. spring bean 的创建:`AbstractApplicationContext#finishBeanFactoryInitialization` + +本文主è¦�是了解 spring å�¯åЍæµ�程,从整体上把æ�¡ spring å�¯åŠ¨è¿‡ç¨‹ä¸­çš„ beanFactory çš„å�˜åŒ–。本文æ„�在了解 spring 的整体å�¯åЍæµ�程,å�Žç»­çš„分æž�中,我们将对这些æµ�程进行展开分æž�。 + +* * * + +_本文原文链接:[https://my.oschina.net/funcy/blog/4597493](https://my.oschina.net/funcy/blog/4597493) ,é™�于作者个人水平,文中难å…�有错误之处,欢迎指正ï¼�原创ä¸�易,商业转载请è�”系作者获得授æ�ƒï¼Œé�žå•†ä¸šè½¬è½½è¯·æ³¨æ˜Žå‡ºå¤„。_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" new file mode 100644 index 0000000..4f97b54 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\203\357\274\211\357\274\232\345\233\275\351\231\205\345\214\226\344\270\216\344\272\213\344\273\266\345\244\204\347\220\206.md" @@ -0,0 +1,166 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ed6b80d76ba4b2ddb0f8d15e070a0c32df7.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö spring µÄÆô¶¯Á÷³Ì¡£ + +### 7\. ¹ú¼Ê»¯: `initMessageSource()` + +Õâ¸ö·½·¨ÊÇÓÃÀ´³õʼ»¯ `MessageSource` µÄ£¬ÄÚÈÝÈçÏ£º + +``` +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + + ... + + /** + * ³õʼ»¯ MessageSource + */ + protected void initMessageSource() { + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + // Èç¹ûbeanFactoryÖдæÔÚMessageSource£¬ÉèÖÃÆä ParentMessageSource + if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { + this.messageSource = beanFactory.getBean( + MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); + if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { + HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; + if (hms.getParentMessageSource() == null) { + // ÉèÖÃParentMessageSource + hms.setParentMessageSource(getInternalParentMessageSource()); + } + } + } + // Èç¹ûbeanFactoryÖв»´æÔÚMessageSource£¬¾Í ´´½¨-ÉèÖÃ-×¢²á + else { + DelegatingMessageSource dms = new DelegatingMessageSource(); + dms.setParentMessageSource(getInternalParentMessageSource()); + this.messageSource = dms; + // ÉèÖÃParentMessageSource + beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); + } + } + + /** + * ·µ»Ø¸¸ÈÝÆ÷µÄ messageSource + */ + @Nullable + protected MessageSource getInternalParentMessageSource() { + return (getParent() instanceof AbstractApplicationContext ? + ((AbstractApplicationContext) getParent()).messageSource : getParent()); + } + + ... +} + +``` + +¿ÉÒÔ¿´µ½£¬Õû¸ö·½·¨Ö÷ÒªÊDzÙ×÷ `MessageSource`£¬Ö÷ÒªÂß¼­Îª£ºÈç¹ûÒѾ­´æÔÚ `MessageSource` ÁË£¬¾ÍÉèÖÃһЩÊôÐÔ£»·ñÔò¾Í´´½¨ `MessageSource`£¬²¢ÉèÖÃЩ ÊôÐÔ£¬×îºó×¢²áµ½ `beanFactory` ÖС£ + +¹ØÓÚ `MessageSource` µÄ¾ßÌå×÷Ó㬱¾ÎľͲ»Õ¹¿ªÁË¡£ + +### 8\. ³õʼ»¯Ê¼þ¹ã²¥Æ÷£º`initApplicationEventMulticaster()` + +`AbstractApplicationContext#initApplicationEventMulticaster` ´úÂëÈçÏ£º + +``` +protected void initApplicationEventMulticaster() { + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + // Èç¹ûÓû§ÅäÖÃÁË×Ô¶¨Òåʼþ¹ã²¥Æ÷£¬¾ÍʹÓÃÓû§µÄ + if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { + this.applicationEventMulticaster = + beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, + ApplicationEventMulticaster.class); + } + else { + // Óû§Ã»ÓÐÅäÖù㲥Æ÷£¬¾ÍʹÓÃĬÈϵÄʼþ¹ã²¥Æ÷ + this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); + beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, + this.applicationEventMulticaster); + } +} + +``` + +Õâ¿éÂß¼­Ò²ºÜ¼òµ¥£¬Èç¹ûÒÑ´æÔÚʼþ¹ã²¥Æ÷£¬¾ÍʹÓÃÒÑ´æÔڵ쬷ñÔò¾Í´´½¨Ò»¸ö¡£¹ØÓÚ `ApplicationEventMulticaster`£¬Ö÷Òª¾ÍÊÇÓÃÀ´¹ã²¥Ê¼þµÄ£¬¸ü¶à¹ØÓÚʼþµÄÄÚÈÝ£¬¿ÉÒԲο¼ [spring Ì½ÃØÖ® spring ʼþ»úÖÆ](https://my.oschina.net/funcy/blog/4713339). + +### 9\. À©Õ¹µã£ºonRefresh () + +`AbstractApplicationContext#onRefresh` ÊÇ spring ÌṩµÄÒ»¸öÀ©Õ¹µã£¬·½·¨²¢ÎÞÄÚÈÝ£º + +``` +protected void onRefresh() throws BeansException { + +} + +``` + +Èç¹ûÐèÒª´¦ÀíÌØ¶¨µÄ²Ù×÷£¬¿ÉÒÔÔÚ×ÓÀàÖÐʵÏÖ¡£ + +µ±Ç°Ê¹ÓÃµÄ `ApplicationContext` ÊÇ `AnnotationConfigApplicationContext`£¬²¢ÎÞ `onRefresh()` ·½·¨£¬¾Í²»¹ý¶à·ÖÎöÁË¡£ + +### 10\. ×¢²áʼþ¼àÌýÆ÷£ºregisterListeners () + +`AbstractApplicationContext#registerListeners` Ïà¹Ø´úÂëÈçÏ£º + +> AbstractApplicationContext + +``` +/** ÕâÀï¾ÍÊÇÓÃÀ´´æ·Å¼àÌýÆ÷µÄ */ +private final Set> applicationListeners = new LinkedHashSet<>(); + +/** ·µ»Øµ±Ç°ËùÓеļàÌýÆ÷ */ +public Collection> getApplicationListeners() { + return this.applicationListeners; +} + +/** + * Ìí¼Ó¼àÌýÆ÷ + */ +public void addApplicationListener(ApplicationListener listener) { + Assert.notNull(listener, "ApplicationListener must not be null"); + if (this.applicationEventMulticaster != null) { + this.applicationEventMulticaster.addApplicationListener(listener); + } + this.applicationListeners.add(listener); +} + +/** + * ×¢²á¼àÌýÆ÷ + */ +protected void registerListeners() { + // ÏÈÌí¼ÓÊÖ¶¯setµÄһЩ¼àÌýÆ÷ + // getApplicationListeners() »ñÈ¡µÄ¼àÌýÆ÷»ù±¾ÊÇͨ¹ýµ÷Óà addApplicationListener(...) Ìí¼ÓµÄ + for (ApplicationListener listener : getApplicationListeners()) { + getApplicationEventMulticaster().addApplicationListener(listener); + } + // »ñȡȡµ½¼àÌýÆ÷µÄÃû³Æ£¬ÉèÖõ½¹ã²¥Æ÷ + // ´Ëʱ»ñÈ¡µÄ¼àÌýÆ÷ÊÇ´Ó beanFactory ÖлñÈ¡µÄ£¬¼´ÊÇspringͨ¹ý°üɨÃèµÃµ½µÄ + String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); + for (String listenerBeanName : listenerBeanNames) { + getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); + } + // Èç¹û´æÔÚÔçÆÚÓ¦ÓÃʼþ£¬¹ã²¥ + Set earlyEventsToProcess = this.earlyApplicationEvents; + this.earlyApplicationEvents = null; + if (earlyEventsToProcess != null) { + for (ApplicationEvent earlyEvent : earlyEventsToProcess) { + // ¹ã²¥ÔçÆÚʼþ + getApplicationEventMulticaster().multicastEvent(earlyEvent); + } + } +} + +``` + +Õâ¸ö·½·¨µÄÁ÷³Ì´óÖÂÈçÏ£º + +1. Ìí¼Ó `AbstractApplicationContext#applicationListeners` ÖеļàÌýÆ÷µ½ `ApplicationEventMulticaster` ÖÐ +2. ´Ó `beanFactory` »ñÈ¡¼àÌýÆ÷µÄ `beanName`£¬Ìí¼Óµ½ `ApplicationEventMulticaster` ÖÐ +3. Èç¹ûÓÐÔçÆÚʼþ£¬¾Í½øÐй㲥 + +¹ØÓÚ spring µÄʼþ£¬±¾ÎIJ¢²»´òËãÕ¹¿ª£¬Èç¹ûÏëÁ˽â¸ü¶à£¬¿É²Î¿¼ [spring Ì½ÃØÖ® spring ʼþ»úÖÆ](https://my.oschina.net/funcy/blog/4713339). + +±¾ÎĵķÖÎö¾Íµ½ÕâÀïÁË¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4892120](https://my.oschina.net/funcy/blog/4892120) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" new file mode 100644 index 0000000..f7c7a4d --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\270\211\357\274\211\357\274\232\345\214\205\347\232\204\346\211\253\346\217\217\346\265\201\347\250\213.md" @@ -0,0 +1,800 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-3ed1cf4bd6fc2ef3f569376093a6462987d.png) + +ÔÚ [applicationContext µÄ´´½¨](https://my.oschina.net/funcy/blog/4608767)ÖУ¬ÎÒÃÇ·ÖÎöÁË `applicationContext` µÄ´´½¨¹ý³Ì£¬ÔÚ±¾ÎÄÖУ¬ÎÒÃǽ«·ÖÎö spring ÊÇÈçºÎ½øÐаüɨÃèµÄ¡£ + +ÒÀ¾ÉÊÇ `AnnotationConfigApplicationContext` µÄ¹¹Ôì·½·¨£º + +``` +public AnnotationConfigApplicationContext(String... basePackages) { + this(); + //¶Ô´«ÈëµÄ°ü½øÐÐɨÃ裬ɨÃèÍê³Éºó£¬»áµÃµ½Ò»¸ö BeanDefinition µÄ¼¯ºÏ + scan(basePackages); + refresh(); +} + +``` + +Õâ´ÎÎÒÃǽ«Ä¿¹â·ÅÔÚ `scan(basePackages);` ÉÏ£¬½øÈë¸Ã·½·¨£º + +> AnnotationConfigApplicationContext#scan + +``` +public void scan(String... basePackages) { + Assert.notEmpty(basePackages, "At least one base package must be specified"); + // ÕâÀïµÄscanner¶ÔÏó¾ÍÊÇÔÚthis()Öд´½¨µÄ + this.scanner.scan(basePackages); +} + +``` + +Õâ¸ö·½·¨¹Ø¼ü´úÂëÊÇ `this.scanner.scan(basePackages);`£¬Õâ¸ö `scanner` ¾ÍÊÇÔÚ `this()` Öд´½¨µÄ¶ÔÏó£º + +``` +public AnnotationConfigApplicationContext() { + this.reader = new AnnotatedBeanDefinitionReader(this); + // scanner ¾ÍÊÇÔÚÕâÀï´´½¨µÄ + this.scanner = new ClassPathBeanDefinitionScanner(this); +} + +``` + +¼ÌÐø×·×Ù£¬ÕâÀïÎÒÃǶԲ»ÖØÒªµÄ·½·¨½ö¸ø³öµ÷ÓÃÁ´£¬ÖØµã¹Ø×¢É¨Ãè°üµÄ¹ý³Ì£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AnnotationConfigApplicationContext#scan + |-ClassPathBeanDefinitionScanner#scan + |-ClassPathBeanDefinitionScanner#doScan + +``` + +`ClassPathBeanDefinitionScanner#doScan` ´úÂëÈçÏ£º + +``` +protected Set doScan(String... basePackages) { + Assert.notEmpty(basePackages, "At least one base package must be specified"); + Set beanDefinitions = new LinkedHashSet<>(); + //±éÀúÐèҪɨÃèµÄ°ü·¾¶ + for (String basePackage : basePackages) { + //»ñÈ¡ËùÓзûºÏÌõ¼þµÄBeanDefinition + Set candidates = findCandidateComponents(basePackage); + for (BeanDefinition candidate : candidates) { + //°ó¶¨BeanDefinitionÓëScope + ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); + candidate.setScope(scopeMetadata.getScopeName()); + //²é¿´ÊÇ·ñÅäÖÃÀàÊÇ·ñÖ¸¶¨beanµÄÃû³Æ£¬Èçûָ¶¨ÔòʹÓÃÀàÃûÊ××ÖĸСд + String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); + //ÏÂÃæÁ½¸öifÊÇ´¦Àílazy¡¢Autowire¡¢DependencyOn¡¢initMethod¡¢enforceInitMethod¡¢destroyMethod¡¢ + // enforceDestroyMethod¡¢Primary¡¢Role¡¢DescriptionÕâЩÂß¼­µÄ + if (candidate instanceof AbstractBeanDefinition) { + postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); + } + if (candidate instanceof AnnotatedBeanDefinition) { + AnnotationConfigUtils.processCommonDefinitionAnnotations( + (AnnotatedBeanDefinition) candidate); + } + //¼ì²ébeanÊÇ·ñ´æÔÚ + if (checkCandidate(beanName, candidate)) { + //ÓÖ°ü×°ÁËÒ»²ã + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); + //¼ì²éscopeÊÇ·ñ´´½¨£¬Èçδ´´½¨Ôò½øÐд´½¨ + definitionHolder = AnnotationConfigUtils.applyScopedProxyMode( + scopeMetadata, definitionHolder, this.registry); + beanDefinitions.add(definitionHolder); + //×¢²á beanDefinition + registerBeanDefinition(definitionHolder, this.registry); + } + } + } + return beanDefinitions; +} + +``` + +Õâ¶Î´úÂëÍê³ÉµÄ¹¦ÄܺÜÃ÷ÁË£¬´óÌåÉÏ×öÁËÒÔϼ¸¼þÊ£º + +1. ¸ù¾Ý°ü·¾¶£¬µÃµ½·ûºÏÌõ¼þµÄ BeanDefinition +2. ±éÀú BeanDefinition£¬½øÒ»²½·á¸» beanDefinition ÐÅÏ¢ +3. ½« BeanDefinition Ìí¼Óµ½ beanFactory + +> BeanDefinition Ò²ÊÇ spring µÄÖØÒª×é¼þÖ®Ò»£¬¹ØÓÚ BeanDefinition µÄ·ÖÎö£¬¿É²Î¿¼ [spring ×é¼þÖ® BeanDefinition](https://my.oschina.net/funcy/blog/4597536 "spring×é¼þÖ®BeanDefinition")¡£ + +½ÓÏÂÀ´ÎÒÃÇÖ÷Òª·ÖÎöÕâÈý¸öµÄ²Ù×÷¡£ + +### 1\. ¸ù¾Ý°ü·¾¶µÃµ½ BeanDefinition + +ÕâÒ»²½Ö÷Òª·¢ÉúÔÚ `Set candidates = findCandidateComponents(basePackage);`£¬ÎÒÃǸú½øÈ¥¿´¿´´úÂëµÄÖ´ÐУ¬ÕâÀïÒÀ¾É¶Ô²»ÖØÒª´úÂë¸ø³öµ÷ÓÃÁ´£¬¸Ã·½·¨µÄµ÷ÓÃÈçÏ£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AnnotationConfigApplicationContext#scan + |-ClassPathBeanDefinitionScanner#scan + |-ClassPathBeanDefinitionScanner#doScan + |-ClassPathScanningCandidateComponentProvider#findCandidateComponents + |-ClassPathScanningCandidateComponentProvider#scanCandidateComponents + +``` + +×îÖÕµ÷Óõ½ÁË `ClassPathScanningCandidateComponentProvider#scanCandidateComponents`£¬´úÂëÈçÏ (ÓÐɾ¼õ)£º + +``` +private Set scanCandidateComponents(String basePackage) { + Set candidates = new LinkedHashSet<>(); + //×éװɨÃè·¾¶£¨×é×°Íê³ÉºóÊÇÕâÖÖ¸ñʽ£ºclasspath*:org/springframework/learn/demo01/**/*.class£© + String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + resolveBasePackage(basePackage) + '/' + this.resourcePattern; + //¸ù¾Ý·¾¶»ñÈ¡×ÊÔ´¶ÔÏ󣬼´É¨Ãè³ö¸Ã·¾¶ÏµĵÄËùÓÐclassÎļþ£¬µÃµ½ Resource + Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); + for (Resource resource : resources) { + if (resource.isReadable()) { + //¸ù¾Ý×ÊÔ´¶ÔÏó»ñÈ¡×ÊÔ´¶ÔÏóµÄMetadataReader + MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + // ÕâÀï×öÁËÁ½¼þÊ£º + // 1\. ÊÇ·ñÐèÒª³õʼ»¯Îªspring bean£¬¼´ÊÇ·ñÓÐ @Component¡¢@ServiceµÈ×¢½â + // 2\. ²é¿´ÅäÖÃÀàÊÇ·ñÓÐ@ConditionalһϵÁеÄ×¢½â£¬È»ºóÊÇ·ñÂú×ã×¢²áBeanµÄÌõ¼þ + if (isCandidateComponent(metadataReader)) { + ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + sbd.setResource(resource); + sbd.setSource(resource); + if (isCandidateComponent(sbd)) { + candidates.add(sbd); + } + } + } + } + return candidates; +} + +``` + +¿ÉÒÔ¿´µ½£¬ÒÔÉÏ´úÂë×öÁËÈý¼þÊ£º + +1. ¸ù¾Ý´«ÈëµÄ basePackage µÃµ½É¨Ãè·¾¶ +2. ¸ù¾ÝɨÃè·¾¶µÃµ½¸Ã·¾¶ÏµÄËùÓÐ class Îļþ¶ÔÓ¦µÄ Resource +3. ½« Resource ת»¯Îª beanDefinition + +½ÓÏÂÀ´ÎÒÃǾÍÒÔÉÏ´úÂë½øÐзÖÎö¡£ + +#### 1.1 ¸ù¾Ý basePackage µÃµ½°üɨÃè·¾¶ + +ÕâÒ»²½Ã»É¶ºÃ·ÖÎö£¬¾ÍÊÇÒ»¸ö×Ö·û´®µÄÆ´½ÓÓëÌæ»»£¬½«´«ÈëµÄ `org.springframework.learn.demo01` ת»»Îª `classpath*:org/springframework/learn/demo01/**/*.class`£¬Ïà¹Ø´úÂë¾ÍÒ»ÐУº + +``` +String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + resolveBasePackage(basePackage) + '/' + this.resourcePattern; + +``` + +#### 1.2 ɨÃè°ü·¾¶ + +µÃµ½°üɨÃè·¾¶ºó£¬½ÓÏÂÀ´¾ÍÊǽøÐÐɨÃèÁË¡£spring ÔÚɨÃèʱ£¬»á°ÑɨÃè·¾¶ÏµÄËùÓÐ class ÎļþɨÃè³öÀ´£¬È»ºó·â×°³É `Resource`£¬´úÂëÈçÏ + +``` +Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); + +``` + +¸ú½ø´úÂ룬ͬÑùµØ£¬ÎÒÃǶԲ»ÖØÒªµÄ·½·¨£¬ÒÀ¾ÉÖ»¸ø³ö·½·¨µ÷Ó㺠+ +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AnnotationConfigApplicationContext#scan + |-ClassPathBeanDefinitionScanner#scan + |-ClassPathBeanDefinitionScanner#doScan + |-ClassPathScanningCandidateComponentProvider#findCandidateComponents + |-ClassPathScanningCandidateComponentProvider#scanCandidateComponents + |- GenericApplicationContext#getResources + |-AbstractApplicationContext#getResources + |-PathMatchingResourcePatternResolver#getResources + |-PathMatchingResourcePatternResolver#findPathMatchingResources + +``` + +ÎÒÃǽ«´úÂë¾Û¼¯ÓÚ `PathMatchingResourcePatternResolver#findPathMatchingResources`: + +``` +protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { + // ´«ÈëµÄ locationPattern ÊÇ classpath*:org/springframework/learn/demo01/**/*.class + // rootDirPath ÊÇ classpath*:org/springframework/learn/demo01/ + String rootDirPath = determineRootDir(locationPattern); + + // subPattern ÊÇ **/*.class + String subPattern = locationPattern.substring(rootDirPath.length()); + + // ÕâÀï·µ»ØµÄ Resource ÊÇ rootDirPath µÄ¾ø¶Ô·¾¶(ÓÃurl±íʾ) + // URL [file:/xxx/spring-learn/build/classes/java/main/org/springframework/learn/demo01/] + Resource[] rootDirResources = getResources(rootDirPath); + + Set result = new LinkedHashSet<>(16); + for (Resource rootDirResource : rootDirResources) { + rootDirResource = resolveRootDirResource(rootDirResource); + URL rootDirUrl = rootDirResource.getURL(); + if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) { + URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl); + if (resolvedUrl != null) { + rootDirUrl = resolvedUrl; + } + rootDirResource = new UrlResource(rootDirUrl); + } + // ´¦Àí vfs ×ÊÔ´²éÕÒ + if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { + result.addAll(VfsResourceMatchingDelegate + .findMatchingResources(rootDirUrl, subPattern, getPathMatcher())); + } + // ´¦Àíjar°üÎļþ²éÕÒ + else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) { + result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern)); + } + // ´¦ÀíÎļþ·¾¶ÏµÄÎļþ²éÕÒ + else { + result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern)); + } + } + return result.toArray(new Resource[0]); +} + +``` + +ͨ¹ý·ÖÎö£¬·¢ÏÖ¸ÃÀàµÄ´¦Àí¹ý³ÌÈçÏ£º + +1. ͨ¹ý´«ÈëµÄ locationPattern µÃµ½¸Ã pattern Ï嵀 url ¾ø¶Ô·¾¶£¬·âװΪ Resource +2. ±éÀú·µ»ØµÄ·¾¶£¬²éÕÒ class Îļþ£¬·âװΪ Resource + +ÎÒÃÇÀ´¿´¿´ spring ÊÇÈçºÎ½« pattrn ת»»Îª url ·¾¶µÄ£¬ÎÒÃǸú½ø´úÂ룺 + +``` +|-PathMatchingResourcePatternResolver#getResources + |-PathMatchingResourcePatternResolver#findAllClassPathResources + |-PathMatchingResourcePatternResolver#doFindAllClassPathResources + +``` + +×îÖÕ´úÂëµ½ÁË `PathMatchingResourcePatternResolver#doFindAllClassPathResources`: + +``` +protected Set doFindAllClassPathResources(String path) throws IOException { + Set result = new LinkedHashSet<>(16); + ClassLoader cl = getClassLoader(); + // path¶ÔÓ¦µÄurl + Enumeration resourceUrls = (cl != null ? cl.getResources(path) : + ClassLoader.getSystemResources(path)); + while (resourceUrls.hasMoreElements()) { + URL url = resourceUrls.nextElement(); + // ½«urlת»»ÎªResource£¬²¢Ìí¼Óµ½½á¹ûÖÐ + result.add(convertClassLoaderURL(url)); + } + if ("".equals(path)) { + addAllClassLoaderJarRoots(cl, result); + } + return result; +} + +// ½«urlת»»ÎªResource +protected Resource convertClassLoaderURL(URL url) { + return new UrlResource(url); +} + +``` + +´Ëʱ´«ÈëµÄ `path` Ϊ `org/springframework/learn/demo01/`£¬´Ó´úÂë¿ÉÖª£¬×îÖÕµ÷ÓÃÁË java µÄ `ClassLoader` ·½·¨À´»ñÈ¡ path ¶ÔÓ¦µÄ url£¬È»ºó½« url ת»»Îª `Resource` Ìí¼Óµ½½á¹û¼¯Öв¢·µ»Ø¡£ + +Äõ½ÀàµÄ¾ø¶Ô·¾¶Ö®ºó£¬½ÓϾÍÊǶÔ·¾¶½øÐбéÀú£¬Äõ½ class ÎļþÁË¡£ÈÃÎÒÃÇÔٻص½ `PathMatchingResourcePatternResolver#findPathMatchingResources`£¬spring ɨÃèʱ£¬»á¸ù¾Ý´«ÈëµÄ url ÀàÐÍ£¬¹²É¨Ãè 3 ¸öµØ·½£º + +1. vfs +2. jar °ü +3. Îļþ·¾¶ + +`vfs` ×¢ÊÍÉÏ˵ÊÇ "URL protocol for a general JBoss VFS resource"£¬¼´Í¨Óà JBoss VFS ×ÊÔ´µÄ URL ЭÒ飬ÕâÀï²»É¡£Èç¹ûÏîÄ¿ÖÐÒýÈëÁË jar °üÇÒÐèҪɨÃè jar ÖеÄ·¾¶£¬¾Í»áʹÓà jar °üɨÃ跽ʽ½øÐÐ class Îļþ²éÕÒ£¬ÓÉÓÚµ÷ÊÔʱ£¬`demo01` ÊÇʹÓÃÎļþ·½Ê½É¨ÃèµÄ£¬ÕâÀï¾ÍÖØµã·ÖÎöÎļþɨÃ跽ʽ£¬ÖÁÓÚ jar ÊÇÈçºÎɨÃèµÄ£¬ÓÐÐËȤµÄС»ï°é¿É×ÔÐÐÑо¿Ï¡£ + +ÎÒÃǸú½ø `findPathMatchingResources` ·½·¨£º + +``` +|-PathMatchingResourcePatternResolver#findPathMatchingResources + |-PathMatchingResourcePatternResolver#doFindPathMatchingFileResources + |-PathMatchingResourcePatternResolver#doFindMatchingFileSystemResources + +``` + +``` +protected Set doFindMatchingFileSystemResources(File rootDir, + String subPattern) throws IOException { + // ÕâÀï½øÐÐÎļþ²éÕÒ + Set matchingFiles = retrieveMatchingFiles(rootDir, subPattern); + Set result = new LinkedHashSet<>(matchingFiles.size()); + for (File file : matchingFiles) { + result.add(new FileSystemResource(file)); + } + return result; +} + +``` + +ÔÚ `PathMatchingResourcePatternResolver#doFindMatchingFileSystemResources` ÖУ¬spring ½«É¨Ãèµ½µÄ File ת»»Îª `FileSystemResource` ±£´æ£¬ÕâÊÇÎÒÃÇÓöµ½µÄµÚ¶þ¸ö `Resource` ÀàÐÍÁË (Ç°ÃæÎª `UrlResource`£¬ÕâÀïΪ `FileSystemResource`). + +½ÓÏÂÎÒÃÇÖØµã¹Ø×¢ `Set matchingFiles = retrieveMatchingFiles(rootDir, subPattern);`£¬¿´¿´ spring ÊÇÈçºÎÍê³ÉÎļþ²éÕҵģº + +``` +|-PathMatchingResourcePatternResolver#findPathMatchingResources + |-PathMatchingResourcePatternResolver#doFindPathMatchingFileResources + |-PathMatchingResourcePatternResolver#doFindMatchingFileSystemResources + |-PathMatchingResourcePatternResolver#retrieveMatchingFiles + |-PathMatchingResourcePatternResolver#doRetrieveMatchingFiles + +``` + +``` +protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) + throws IOException { + for (File content : listDirectory(dir)) { + String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/"); + if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) { + if (!content.canRead()) { + } + else { + // Èç¹ûÊÇÎļþ¼Ð£¬µÝ¹éµ÷Óà + doRetrieveMatchingFiles(fullPattern, content, result); + } + } + // Èç¹ûÊÇÎļþÇÒÎļþ·¾¶ + if (getPathMatcher().match(fullPattern, currPath)) { + result.add(content); + } + } +} + +``` + +ÒÔÉÏ´úÂë±È½Ï¼òµ¥£¬ÓëÎÒÃÇÆ½³£±éÀúÎļþµÄ·½Ê½ÊÇÒ»ÑùµÄ¡£ + +ÖµµÃÒ»ÌáµÄÊÇ£¬`getPathMatcher().match(fullPattern, currPath)` ×îÖÕµ÷Óõ½µÄÊÇ `AntPathMatcher#doMatch`£¬ÕâÊÇÒ»¸ö ant ·ç¸ñµÄ·¾¶Æ¥ÅäÑéÖ¤£¬¼´Â·¾¶ÖдøÓÐ `*`£¬Èç´«ÈëµÄ pattern ÊÇ `/xxx/spring-framework/spring-learn/build/classes/java/main/org/springframework/learn/demo01/**/*.class`£¬±íʾƥÅä `/xxx/spring-framework/spring-learn/build/classes/java/main/org/springframework/learn/demo01/` ¼°Æä×ÓÎļþ¼ÐÏÂËùÓÐÒÔ`.class` Îļþ½áβµÄÎļþ£¬µ±Ç°´«ÈëµÄ path ÊÇ `/xxx/spring-framework/spring-learn/build/classes/java/main/org/springframework/learn/demo01/BeanObj2.class`£¬ÏÔȻƥÅä¡£¹ØÓÚ `AntPathMatcher#doMatch` ·½·¨ÊÇÈçºÎ½øÐÐÆ¥ÅäµÄ£¬ÕâÀï¾Í²»½øÐÐÕ¹¿ªÁË¡£ + +¾­¹ýÁËÒÔÉϲ½Ö裬ÎÒÃÇÖÕÓڵõ½ÁË class Îļþ¶ÔÓ¦µÄ Resource ÁË. + +#### 1.3 ½« Resource ת»¯Îª BeanDefinition + +½« Resource ת»¯Îª BeanDefinition£¬´úÂëÊÇ + +> ClassPathScanningCandidateComponentProvider#scanCandidateComponents + +``` +// ´Ó resource µÃµ½ MetadataReader +MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + +// ÕâÀï×öÁËÁ½¼þÊ£º +// 1\. ÊÇ·ñÐèÒª³õʼ»¯Îªspring bean£¬¼´ÊÇ·ñÓÐ @Component¡¢@ServiceµÈ×¢½â +// 2\. ²é¿´ÅäÖÃÀàÊÇ·ñÓÐ@ConditionalһϵÁеÄ×¢½â£¬È»ºóÊÇ·ñÂú×ã×¢²áBeanµÄÌõ¼þ +if (isCandidateComponent(metadataReader)) { + // ½« metadataReader ת»»Îª ScannedGenericBeanDefinition£¬ÕâÒ²ÊÇBeanDefinition¼Ò×åÖеÄÒ»Ô± + ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + ... +} + +``` + +##### 1\. ´Ó Resource µÃµ½ MetadataReader + +ÎÒÃÇ×·×ÙÏ `MetadataReader` µÄ»ñÈ¡£º + +``` +|-ClassPathScanningCandidateComponentProvider#scanCandidateComponents + |-CachingMetadataReaderFactory#getMetadataReader + |-SimpleMetadataReaderFactory#getMetadataReader(Resource) + |-SimpleMetadataReader#SimpleMetadataReader + +``` + +´úÂë×îÖÕÔËÐе½ÁË `SimpleMetadataReader` µÄ¹¹Ôì·½·¨: + +``` +SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { + SimpleAnnotationMetadataReadingVisitor visitor + = new SimpleAnnotationMetadataReadingVisitor(classLoader); + // ÕâÀï·¢ÉúÁËclassÎļþµÄ¶ÁÈ¡Óë½âÎö + getClassReader(resource).accept(visitor, PARSING_OPTIONS); + this.resource = resource; + this.annotationMetadata = visitor.getMetadata(); +} + +``` + +ÔÙ½øÒ»²½×·×Ù£¬·¢ÏÖ class ÎļþµÄ¶ÁÈ¡Óë½âÎö·¢ÉúÔÚ `ClassReader` Àࣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-595687d3a766ffaacf69f31216a5b09f9d5.png) + +Õâ¸öÀàʹÓà asm À´¶ÁÈ¡ class Îļþ£¬´úÂë±È½Ï¸´ÔÓ£¬¾Í²»ÉÁË¡£ + +Ò»Ö±ÒÔÀ´£¬ÎÒ¶¼ÒÔΪ spring ÊÇͨ¹ý·´ÉäÀ´»ñÈ¡ÀàÐÅÏ¢µÄ£¬µ½ÕâÀï²ÅÖªµÀ£¬**Ô­À´ spring ÊÇͨ¹ý asm Ö±½Ó¶ÁÈ¡ class ÎļþÀ´»ñÈ¡ÀàµÄÐÅÏ¢µÄ** ¡£ + +×îºóÎÒÃÇÀ´¿´Ïµõ½µÄ `MetadataReader` µÄ½á¹û£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9df07587a3a8191231da87fe21d80052357.png) + +ÕâÀïÖØµã¹Ø×¢ `annotations` ÊôÐÔ£¬ÀïÃæÓÐÒ»¸ö `annotations` ºÍ `mappings`£¬`annotations` ÄÚÈÝΪ `@Service`£¬`mappings` ÊÇÒ»¸öÊý×飬ÄÚÈÝΪ + +``` +0-@Service +1-@Component +2-@Index + +``` + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f5c7e8768b92b7a4303c4e1beb58863fe03.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-38b493e0b4b7b73dda5f06f371b66f16d3e.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-930a4e6844efd3f9f52c6d3f4e13a200337.png) + +`annotations` ±¾È˲²âÊÇ `BeanObj1` ÉϵÄ×¢½â£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-77271c438dde39f2bc905aa9f2cf9fb73d8.png) + +ÖÁÓÚ `mappings` ÊÇɶ£¬ÎÒ²»ºÃ²Â²â£¬²»¹ýÒ²¿ÉÒÔ´Ó×¢½âÖз¢ÏÖһЩ¶ËÄߣº + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f31efb6252beb2108dc0ce93d482666f8d0.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a8ce5d11b4b02907f0ff497c6369a1c1dde.png) + +`@Service` ÉÏÓÐ `@Component` ×¢½â£¬`@Component` ÉÏÓÐ `@Indexed`£¬¶øÕâÈýÕß¶¼³öÏÖÔÚÁË `mappings` ÖУ¬Õâ¿´×ÅÏñÊÇרÃÅÓÃÀ´±£´æÄÃ×¢½âÖ®ÉϵÄ×¢½âµÄ£¿²»¾À½áÕâ¸öÁË£¬ÔÝÇҾ͵±×÷ÊÇÕ⹦Äܰɣ¡**×¢Ò⣺`mappings` ÀïÃæµÄÄÚÈݺÜÖØÒª£¬ºóÃæ»áÓÃÀ´£¡** + +##### 2. `isCandidateComponent(MetadataReader)`£ºÅжÏÊÇ·ñÐèҪʵÀý»¯Îª spring bean + +ÔÚÉÏÒ»²½ÖУ¬ÎÒÃǵõ½ÁË basePackage ÏÂ**ËùÓÐÀà**µÄ `MetadataReader` ÃèÊöÎļþ£¬×¢ÒâÕâÀïÊÇ**ËùÓÐÀà**£¬µ«ÕâЩÀàÊDz»ÊǶ¼Òª×ª³É `spring bean`£¬Íйܵ½ spring ÈÝÆ÷ÄØ£¿Õâ¾ÍÊÇ `isCandidateComponent(MetadataReader)` µÄ¹¦ÄÜÁË¡£·Ï»°ÉÙ˵£¬ÉÏ´úÂ룺 + +``` +protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { + // Ê¡ÂÔ²¿·Ö´úÂë + for (TypeFilter tf : this.includeFilters) { + // ÕâÀïÅжÏÊÇ·ñÐèÒªÍйܵ½springÈÝÆ÷ + if (tf.match(metadataReader, getMetadataReaderFactory())) { + // ÅжÏÊÇ·ñÓÐ@ConditionalһϵÁеÄ×¢½â + return isConditionMatch(metadataReader); + } + } + return false; +} + +``` + +Õâ¶ÎÖ÷ÒªÊÇ×öÁËÁ½¸öÅжϣº + +* ÊÇ·ñÐèҪΪ spring bean +* ÊÇ·ñÓÐ `@Conditional` µÈһϵÁеÄ×¢½â + +ÕâÀïÎÒÃÇÏÈÀ´¿´µÚÒ»¸öÅжϡ£ + +ÔÚ spring ÖУ¬±êÃ÷ spring bean µÄ×¢½âÓкܶ࣬Ïñ `@Component`¡¢`@Repository`¡¢`@Service`¡¢`@Controller`¡¢`@Configuration`£¬ÉõÖÁÊÇÄã×Ô¼ºÐ´µÄ×¢½âÀֻ࣬ҪÉÏÃæ±êÁËÕâЩע½â£¬Ïñ + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +// Ìí¼Ó @Component »ò @Service »ò @Repository µÈÆäÖÐÖ®Ò» +@Component +public @interface MySpringBean { + ... +} + +``` + +¶¼Äܱ» spring ʶ±ð¡£Èç¹ûÊÇ spring ÌṩµÄ×¢½â£¨`@Component`¡¢`@Repository` µÈ£©£¬ÔÚÅжÏÊDz»ÊÇ spring bean ʱ£¬Ö»ÐèÒª×öÀàËÆ + +``` +if(annotation == Component.class || annotation == Repository.class) { + ... +} + +``` + +µÄÅжϾÍÐÐÁË¡£µ«¶ÔÓÚ×Ô¶¨ÒåµÄ×¢½â `@MySpringBean`£¬spring ÊÇÔõô֪µÀÕâÊÇ spring bean ÄØ£¿ÔÚÎÒÃǶ¨Òå `@MySpringBean` ʱ£¬Ò»¶¨ÒªÔÚÀàÉÏÌí¼Ó `@Component` »ò `@Service` »ò `@Repository` µÈÆäÖÐÖ®Ò»²ÅÄܱ» spring ʶ±ð£¬ÕâÆäÖÐÓÐʲôÐþ»úÄØ£¿ÎÒÃǸú½ø´úÂë `AbstractTypeHierarchyTraversingFilter#match(MetadataReader, MetadataReaderFactory)`£¬ÕâÀïÎÒÃǶԲ»ÖØÒªµÄ´úÂëÒÀ¾ÉÖ»¸ø³öµ÷ÓÃÁ´£º + +``` +|-ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader) + |-AbstractTypeHierarchyTraversingFilter#match(MetadataReader, MetadataReaderFactory) + |-AnnotationTypeFilter#matchSelf + +``` + +´úÂë×îÖÕµ½ÁË `AnnotationTypeFilter#matchSelf`: + +``` +@Override +protected boolean matchSelf(MetadataReader metadataReader) { + AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); + // ÕâÀïµÄannotationType¾ÍÊÇ @Component + return metadata.hasAnnotation(this.annotationType.getName()) || + (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); +} + +``` + +¹Ø¼ü¾ÍÔÚÕâÁË£º + +``` +metadata.hasAnnotation(this.annotationType.getName()) +Óë +this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()) + +``` + +ÎÒÃÇÏÈ¿´ `metadata.hasAnnotation(this.annotationType.getName())` µÄ±È½Ï£º + +``` +// AnnotationMetadata#hasAnnotation +default boolean hasAnnotation(String annotationName) { + return getAnnotations().isDirectlyPresent(annotationName); +} + +``` + +ÕâÀïµÄ `getAnnotations()` µÃµ½µÄ½á¹ûÊÇ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-3e46b2ece5ddb2328342a469dc7dd554d9e.png) + +mappings ÀïµÄÄÚÈÝÊÇ + +``` +0-@Service +1-@Component +2-@Index + +``` + +ÕâÆäʵ¾ÍÊÇÎÒÃÇÇ°ÃæµÃµ½µÄ `MetadataReader` ÀïµÄÄÚÈÝ£¡ + +ÔÙ×·×ÙÏÂÈ¥£¬·¢ÏÖ `isDirectlyPresent` ¾ÍÊÇÅÐ¶Ï `annotations` Óë `mappings` ÀïÓÐûÓгöÏÖ `@Component`: + +``` +private boolean isPresent(Object requiredType, boolean directOnly) { + // ÅÐ¶Ï annotations ÀïÓÐûÓгöÏÖ @Component + for (MergedAnnotation annotation : this.annotations) { + Class type = annotation.getType(); + if (type == requiredType || type.getName().equals(requiredType)) { + return true; + } + } + if (!directOnly) { + // ÅÐ¶Ï mappings ÀïÓÐûÓгöÏÖ @Component + for (AnnotationTypeMappings mappings : this.mappings) { + for (int i = 1; i < mappings.size(); i++) { + AnnotationTypeMapping mapping = mappings.get(i); + if (isMappingForType(mapping, requiredType)) { + return true; + } + } + } + } + return false; +} + +``` + +½Ó×ÅÎÒÃÇÔÙÀ´¿´ `this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())`£¬²é¿´µ÷Ó㺠+ +``` +|-AnnotationTypeFilter#matchSelf + |-AnnotationMetadata#hasMetaAnnotation + |-MergedAnnotationsCollection#get(String, Predicate) + |-MergedAnnotationsCollection#get(String, Predicate, MergedAnnotationSelector) + |-MergedAnnotationsCollection#find + +``` + +×îÖյIJéÕÒ·½·¨ÔÚ `MergedAnnotationsCollection#find`: + +``` +private MergedAnnotation find(Object requiredType, + @Nullable Predicate> predicate, + @Nullable MergedAnnotationSelector selector) { + + MergedAnnotation result = null; + for (int i = 0; i < this.annotations.length; i++) { + MergedAnnotation root = this.annotations[i]; + AnnotationTypeMappings mappings = this.mappings[i]; + // mappings ±éÀú mappings + for (int mappingIndex = 0; mappingIndex < mappings.size(); mappingIndex++) { + AnnotationTypeMapping mapping = mappings.get(mappingIndex); + if (!isMappingForType(mapping, requiredType)) { + continue; + } + // µ½ÕâÀ¾ÍÊÇÕÒµ½ÁË @Component ×¢½â + MergedAnnotation candidate = (mappingIndex == 0 + ? (MergedAnnotation) root + : TypeMappedAnnotation.createIfPossible(mapping, root, IntrospectionFailureLogger.INFO)); + if (candidate != null && (predicate == null || predicate.test(candidate))) { + if (selector.isBestCandidate(candidate)) { + return candidate; + } + result = (result != null ? selector.select(result, candidate) : candidate); + } + } + } + return result; +} + +``` + +¿ÉÒÔ¿´µ½£¬²éÕÒ·½Ê½¸úÉÏÃæµÄ `metadata.hasAnnotation(this.annotationType.getName())` ¸ß¶ÈÏàËÆ¡£ + +ÒÔÉϾÍÊÇ spring ÓÃÀ´ÅжÏÊÇ·ñ°üº¬ `@Service`¡¢`@Component` µÈ×¢½âµÄÂß¼­ÁË¡£ + +ÔÚ `java` ÖУ¬×¢½âÊDz»Äܼ̳еģ¬Èç + +``` +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Base { + +} + +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Child extends Base { + +} + +``` + +ÒÔÉÏÓï·¨ÔÚ java Öв»±»ÔÊÐíµÄ£¬spring ¾ÍÊDzÉÓÃÕâ½âÎö`×¢½âµÄ×¢½â`µÄ·½Ê½£¬ÊµÏÖÁËÀàËÆÓڼ̳еŦÄÜ¡£ + +½Ó×ÅÎÒÃÇÔÙÀ´¿´ `ClassPathScanningCandidateComponentProvider#isConditionMatch` ·½·¨¡£Êµ¼ÊÉÏ£¬Õâ¸ö·½·¨ÊÇÓÃÀ´ÅжÏÀàÊÇ·ñº¬ÓÐ `@Conditional` ×¢½âµÄ£¬Âú×ãÌõ¼þÔò»áʶ±ðΪ spring bean£¬´úÂë×îÖÕµ÷Óõ½ÁË `ConditionEvaluator#shouldSkip(AnnotatedTypeMetadata, ConfigurationCondition.ConfigurationPhase)`: + +``` +public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { + // Ê¡ÂÔÁËһЩ´úÂë + + // µÃµ½ condition ¶ÔÏó + List conditions = new ArrayList<>(); + for (String[] conditionClasses : getConditionClasses(metadata)) { + for (String conditionClass : conditionClasses) { + Condition condition = getCondition(conditionClass, this.context.getClassLoader()); + conditions.add(condition); + } + } + } + + AnnotationAwareOrderComparator.sort(conditions); + // ±éÀú£¬ÅÐ¶Ï condition Ìõ¼þÊÇ·ñ³ÉÁ¢ + for (Condition condition : conditions) { + ConfigurationPhase requiredPhase = null; + if (condition instanceof ConfigurationCondition) { + requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); + } + if ((requiredPhase == null || requiredPhase == phase) + // ÅÐ¶Ï condition Ìõ¼þÊÇ·ñ³ÉÁ¢£¬Ò»¸öÌõ¼þÂú×ã¾Í·µ»Øtrue + && !condition.matches(this.context, metadata)) { + return true; + } + } + + return false; +} + +// ͨ¹ý·´Éä»ñÈ¡ Condition ¶ÔÏó +private Condition getCondition(String conditionClassName, @Nullable ClassLoader classloader) { + Class conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader); + return (Condition) BeanUtils.instantiateClass(conditionClass); +} + +``` + +ÕâÀï×öÁËÁ½¼þÊ£º + +1. »ñÈ¡ condition ¶ÔÏó +2. ±éÀú condition ¶ÔÏ󣬵÷Óà `condition.matches()` ·½·¨£¬ÅжÏÌõ¼þÊÇ·ñ³ÉÁ¢ + +##### 3\. ´Ó `MetadataReader` µÃµ½ `ScannedGenericBeanDefinition` + +ÕâÀï½öÊÇ×öÁËÒ»¸ö¼òµ¥µÄ¸³Öµ£¬¿´Ï `ScannedGenericBeanDefinition` µÄ¹¹Ôì·½·¨¾ÍÃ÷°×ÁË£º + +> ScannedGenericBeanDefinition#ScannedGenericBeanDefinition + +``` +public ScannedGenericBeanDefinition(MetadataReader metadataReader) { + Assert.notNull(metadataReader, "MetadataReader must not be null"); + this.metadata = metadataReader.getAnnotationMetadata(); + setBeanClassName(this.metadata.getClassName()); +} + +``` + +´úÂë±È½Ï¼òµ¥£¬¾Í²»¶à×ö·ÖÎöÁË¡£ + +### 2\. ·á¸» beanDefinition ÐÅÏ¢ + +Àú¾­Ç§ÄÑÍòÏÕ£¬ÖÕÓڵõ½ÁË `beanDefinition`£¬µ«´Ëʱ `beanDefinition` ²¢²»·á¸»£¬½ÓÏÂÀ´¾ÍÊǽøÒ»²½À©Õ¹ `beanDefinition` µÄÐÅÏ¢ÁË¡£ÕâЩÐÅÏ¢°üÀ¨ `beanµÄÃû³Æ`¡¢`beanµÄ×÷ÓÃÓò`¡¢`@Lazy` ×¢½â¡¢`@Primary` ×¢½â¡¢`@DependsOn` ×¢½âµÈ£¬´úÂëÈçÏ£º + +``` +public abstract class AnnotationConfigUtils { + + ... + + /** + * ½øÒ»²½·á¸» BeanDefinition + */ + static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, + AnnotatedTypeMetadata metadata) { + // ´¦Àí @Lazy + AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); + if (lazy != null) { + abd.setLazyInit(lazy.getBoolean("value")); + } + else if (abd.getMetadata() != metadata) { + lazy = attributesFor(abd.getMetadata(), Lazy.class); + if (lazy != null) { + abd.setLazyInit(lazy.getBoolean("value")); + } + } + // ´¦Àí @Primary + if (metadata.isAnnotated(Primary.class.getName())) { + abd.setPrimary(true); + } + // ´¦Àí @DependsOn + AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); + if (dependsOn != null) { + abd.setDependsOn(dependsOn.getStringArray("value")); + } + // ´¦Àí @Role + AnnotationAttributes role = attributesFor(metadata, Role.class); + if (role != null) { + abd.setRole(role.getNumber("value").intValue()); + } + // ´¦Àí @Description + AnnotationAttributes description = attributesFor(metadata, Description.class); + if (description != null) { + abd.setDescription(description.getString("value")); + } + } +} + +``` + +### 3.` registerBeanDefinition(definitionHolder, this.registry)`: Ìí¼Ó BeanDefinition µ½ beanFactory + +½« `BeanDefinition` µ½ `beanFactory` µÄ²Ù×÷±È½Ï¼òµ¥£¬¹Ø¼üµÄ´úÂëÈçÏ£º + +``` +|-ClassPathBeanDefinitionScanner#registerBeanDefinition + |-BeanDefinitionReaderUtils#registerBeanDefinition + |-GenericApplicationContext#registerBeanDefinition + |-DefaultListableBeanFactory#registerBeanDefinition + +``` + +> DefaultListableBeanFactory#registerBeanDefinition + +``` +this.beanDefinitionMap.put(beanName, beanDefinition); + +``` + +´Ó `ClassPathBeanDefinitionScanner#registerBeanDefinition` µ½ `DefaultListableBeanFactory#registerBeanDefinition`£¬ÕâÆäÖÐËäÈ»¾­ÀúÁËһЩÍäÍäÈÆÈÆ£¬µ«ÒÀ¾É²»·Á°­ÎÒÃÇÕÒµ½¹Ø¼üµÄ´úÂë¡£ + +µ½´Ë£¬´ÅÅÌÉ쵀 class Îļþ£¬¾­¹ý spring ɨÃ裬ÖÕÓÚ±ä³ÉÁË `BeanDefinition`£¬±£´æÔÚ `BeanFactory` ÖÐÁË¡£ + +### 4\. ×ܽá + +±¾ÎıȽϳ¤£¬Ö÷Òª·ÖÎöÁË spring ɨÃè°ü·¾¶µÃµ½ `beanDefinition` µÄ¹ý³Ì£¬Ö÷ÒªÁ÷³ÌÈçÏ£º + +1. ¸ù¾Ý°üÃûµÃµ½Â·¾¶ `Resource`£» +2. ¸ù¾Ý·¾¶ `Resouce` µÃµ½¸Ã·¾¶ÏÂËùÓÐ class ÎļþµÄ `Resouce`£» +3. ¸ù¾Ý class ÎļþµÄ `Resouce` ͨ¹ý asm ½âÎöµÃµ½ `MetadataReader`£¬×¢Ò⣺ÕâÀïµÄ `MetadataReader` »¹ÊÇËùÓÐ class ÎļþµÄ `MetadataReader`£» +4. ´Ó `MetadataReader` ÖÐÕÒµ½ÐèÒª spring ÍÐ¹ÜµÄ `MetadataReader`£¬½«Æäת»¯Îª `ScannedGenericBeanDefinition`£¬`ScannedGenericBeanDefinition` Ϊ `BeanDefinition` µÄ×ÓÀࣻ +5. ½øÒ»²½·á¸» `ScannedGenericBeanDefinition` µÄÐÅÏ¢£» +6. ½«ÉÏÃæµÃµ½µÄ `BeanDefinition` Ìí¼Óµ½ `BeanFactory` ÖÐ + +ÖÁ´Ë£¬°üÃûת»»Îª `BeanDefinition` Íê³É¡£ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f8ff2e4071ba3941e2a0664f6e478b78961.png) + +±¾ÎÄ»¹ÓÐÁ½¸öÖµµÃ×¢ÒâµÄµØ·½£º + +1. spring ÔÚ»ñÈ¡ÀàÉϵÄ×¢½âʱ£¬²»ÊÇͨ¹ý·´É䣬¶øÊÇʹÓà asm Ö±½Ó½âÎö class Îļþ£¬È»ºóÔÙ»ñÈ¡ÀàÉϵÄ×¢½âµÄ +2. ÔÚ´¦Àí×¢½âʱ£¬spring ͨ¹ý½âÎö ¡°×¢½âµÄ×¢½â¡± ʵÏÖÁËÒ»Ì×ÀàËÆÓÚ×¢½â¼Ì³ÐµÄ·½Ê½£¬ÕâÒ²ÊÇ spring ÄÜʶ±ð `@Component`¡¢`@Service` ÉõÖÁÊÇ¿ª·¢Õß×Ô¶¨Òå×¢½âµÄÔ­Òò¡£ + +µÃµ½ÁË `BeanDefinition` ºó£¬½ÓמÍÊÇ spring ÈÝÆ÷µÄ³õʼ»¯ÁË£¬ÎÒÃÇÏÂÆªÎÄÕÂÔÙ¼û¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4614071](https://my.oschina.net/funcy/blog/4614071) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" new file mode 100644 index 0000000..9690e98 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\271\235\357\274\211\357\274\232\345\215\225\344\276\213bean\347\232\204\345\210\233\345\273\272.md" @@ -0,0 +1,1105 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1f1ac8f3d8241fad9693d9684048ab7f3ae.png) + +½ÓÉÏÎÄ£¬±¾ÎÄÒÀ¾ÉÊÇ·ÖÎö `finishBeanFactoryInitialization(beanFactory)`£¬±¾ÎĽ«Öصã·ÖÎöµ¥Àý bean µÄ´´½¨Á÷³Ì¡£ + +ÔÚÉÏһƪÎÄÕÂÖУ¬ÎÒÃǽéÉÜÁË `AbstractApplicationContext#finishBeanFactoryInitialization` µÄÖ´Ðйý³Ì£¬±¾ÎĽ«ÉîÈëϸ½Ú£¬·ÖÎö spring bean µÄ´´½¨¹ý³Ì¡£ + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-DefaultListableBeanFactory#preInstantiateSingletons + |-AbstractBeanFactory#getBean(java.lang.String) + |-AbstractBeanFactory#doGetBean + |-AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) + +``` + +ÎÒÃÇÖ±½Ó¿´ `AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])`£¬¾«¼òºóµÄ´úÂëÈçÏ£º + +``` +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + RootBeanDefinition mbdToUse = mbd; + + // È·±£ BeanDefinition ÖÐµÄ Class ±»¼ÓÔØ + Class resolvedClass = resolveBeanClass(mbd, beanName); + if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { + mbdToUse = new RootBeanDefinition(mbd); + mbdToUse.setBeanClass(resolvedClass); + } + + // ×¼±¸·½·¨¸²Ð´£¬Èç¹ûbeanÖж¨ÒåÁË ºÍ + try { + mbdToUse.prepareMethodOverrides(); + } + catch (BeanDefinitionValidationException ex) { + ... + } + + try { + // Èç¹ûÓдúÀíµÄ»°Ö±½Ó·µ»Ø + Object bean = resolveBeforeInstantiation(beanName, mbdToUse); + if (bean != null) { + return bean; + } + } + catch (Throwable ex) { + ... + } + + try { + // ´´½¨ bean + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + ... + } + catch (Throwable ex) { + ... + } +} + +``` + +¿ÉÒÔ¿´µ½£¬¸Ã·½·¨×öÁËËļþÊ£º + +1. È·±£ class ±»¼ÓÔØ +2. ¸²Ð´·½·¨ +3. ²éÕÒ´úÀí¶ÔÏó£¬ÈôÓÐÔò·µ»Ø +4. ´´½¨ spring bean + +¶ÔÓÚÇ°ÃæÈý¸ö²Ù×÷£¬±¾ÎIJ»¹Ø×¢£¬ÎÒÃǽö¿´µÚËĸö²Ù×÷¡£½øÈë `AbstractAutowireCapableBeanFactory#doCreateBean`: + +``` +protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, + final @Nullable Object[] args) throws BeanCreationException { + + BeanWrapper instanceWrapper = null; + if (mbd.isSingleton()) { + //Èç¹ûÊÇ factoryBeanÔò´Ó»º´æÉ¾³ý + instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); + } + if (instanceWrapper == null) { + // ʵÀý»¯ Bean£¬Õâ¸ö·½·¨ÀïÃæ²ÅÊÇÖÕµã + instanceWrapper = createBeanInstance(beanName, mbd, args); + } + //beanʵÀý + final Object bean = instanceWrapper.getWrappedInstance(); + //beanÀàÐÍ + Class beanType = instanceWrapper.getWrappedClass(); + if (beanType != NullBean.class) { + mbd.resolvedTargetType = beanType; + } + + synchronized (mbd.postProcessingLock) { + if (!mbd.postProcessed) { + try { + // Ñ­»·µ÷ÓÃMergedBeanDefinitionPostProcessorµÄpostProcessMergedBeanDefinition·½·¨¡£ÀýÈ磬 + // 1\. µ÷Óà AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition ΪÁËÕÒµ½ + // ´øÓÐ@Autowired¡¢@Value ×¢½âµÄÊôÐԺͷ½·¨ + // 2\. µ÷Óà CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition ΪÁËÕÒµ½ + // ´øÓÐ@Resource ×¢½âµÄÊôÐԺͷ½·¨ + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + ... + } + mbd.postProcessed = true; + } + } + + // ½â¾öÑ­»·ÒÀÀµÎÊÌâ, ÊÇ·ñÔÊÐíÑ­»·ÒÀÀµ, allowCircularReferencesĬÈÏΪtrue£¬¿ÉÒÔ¹Ø±Õ + boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && + isSingletonCurrentlyInCreation(beanName)); + if (earlySingletonExposure) { + // Ñ­»·ÒÀÀµµÄ½â¾ö£¬ºóÃæ»áµ¥¶À·ÖÎö + ... + } + + Object exposedObject = bean; + try { + // ¸ºÔðÊôÐÔ×°Åä, Ò²¾ÍÊÇÎÒÃdz£³£ËµµÄ×Ô¶¯×¢È룬ºÜÖØÒª + populateBean(beanName, mbd, instanceWrapper); + // ÕâÀïÊÇ´¦Àíbean³õʼ»¯Íê³ÉºóµÄ¸÷Öֻص÷£¬ + // ÀýÈçinit-method¡¢InitializingBean ½Ó¿Ú¡¢BeanPostProcessor ½Ó¿Ú + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + ... + } + + //ͬÑùµÄ£¬Èç¹û´æÔÚÑ­»·ÒÀÀµ + if (earlySingletonExposure) { + // Ñ­»·ÒÀÀµµÄ½â¾ö£¬ºóÃæ»áµ¥¶À·ÖÎö + ... + } + + // °Ñbean×¢²áµ½ÏàÓ¦µÄScopeÖÐ + try { + registerDisposableBeanIfNecessary(beanName, bean, mbd); + } + catch (BeanDefinitionValidationException ex) { + ... + } + + return exposedObject; +} + +``` + +¿ÉÒÔ¿´µ½£¬spring ÔÚ´´½¨ bean ʱ£¬Ö÷Òª×öÁËÈçÏÂÈý¼þÊ£º + +1. ´´½¨ÊµÀý +2. ²éÕÒÊôÐÔ +3. ×¢ÈëÊôÐÔ +4. ³õʼ»¯ bean + +½ÓÏÂÀ´£¬ÔÛÃǾÍÖ÷Òª·ÖÎöÕâËĸö²½Öè¡£ + +#### 1\. ´´½¨ÊµÀý + +spring ´´½¨ÊµÀýµÄ·½·¨ÊÇ `AbstractAutowireCapableBeanFactory#createBeanInstance`£¬ÄÚÈÝÈçÏ£º + +``` +protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, + @Nullable Object[] args) { + // È·±£ÒѾ­¼ÓÔØÁË´Ë class + Class beanClass = resolveBeanClass(mbd, beanName); + + // УÑéÀàµÄ·ÃÎÊȨÏÞ + if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) + && !mbd.isNonPublicAccessAllowed()) { + throw new BeanCreationException(...); + } + + // ÊÇ·ñÉèÖÃÁËbean´´½¨µÄSupplier£¬SupplierÊÇjava8ÌṩµÄÀ࣬¿ÉÒÔ´«ÈëÒ»¸ölambda±í´ïʽ + // µ÷Óà AbstractBeanDefinition#setInstanceSupplier Ö¸¶¨ + Supplier instanceSupplier = mbd.getInstanceSupplier(); + if (instanceSupplier != null) { + return obtainFromSupplier(instanceSupplier, beanName); + } + + if (mbd.getFactoryMethodName() != null) { + // ²ÉÓù¤³§·½·¨ÊµÀý»¯ + return instantiateUsingFactoryMethod(beanName, mbd, args); + } + + // ÊÇ·ñµÚÒ»´Î + boolean resolved = false; + // ÊÇ·ñ²ÉÓù¹Ô캯Êý×¢Èë + boolean autowireNecessary = false; + if (args == null) { + synchronized (mbd.constructorArgumentLock) { + if (mbd.resolvedConstructorOrFactoryMethod != null) { + resolved = true; + autowireNecessary = mbd.constructorArgumentsResolved; + } + } + } + if (resolved) { + if (autowireNecessary) { + return autowireConstructor(beanName, mbd, null, null); + } + else { + // Î޲ι¹Ô캯Êý + return instantiateBean(beanName, mbd); + } + } + + // Candidate constructors for autowiring? + // ÅжÏÊÇ·ñ²ÉÓÃÓвι¹Ô캯Êý + Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); + if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || + mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { + return autowireConstructor(beanName, mbd, ctors, args); + } + + ctors = mbd.getPreferredConstructors(); + if (ctors != null) { + // ¹¹Ô캯ÊýÒÀÀµ×¢Èë + return autowireConstructor(beanName, mbd, ctors, null); + } + + // µ÷ÓÃÎ޲ι¹Ô캯Êý + return instantiateBean(beanName, mbd); +} + +``` + +´ÓÒÔÉÏ´úÂëÀ´¿´£¬spring ÔÚʵÀý»¯ bean ʱ£¬ÓÐ 4 ÖÖ·½Ê½£º + +1. ʹÓÃʵÀý»¯·½·¨£ºobtainFromSupplier +2. ʹÓù¤³§·½·¨£ºinstantiateUsingFactoryMethod +3. ʹÓÃÓвι¹Ô죺autowireConstructor +4. ʹÓÃÎ޲ι¹Ô죺instantiateBean + +¶ÔÓÚÇ°ÃæÁ½¸ö·½·¨£¬ÕâÊÇ¿ª·¢ÕßÖ¸¶¨µÄ·½·¨£¬Ã»É¶ºÃ˵µÄ£¬ºóÃæÁ½¸ö·½·¨ÊÇ spring ¸ù¾ÝÀàµÄʵ¼ÊÇé¿öÀ´Ñ¡Ôñ¹¹Ôì·½·¨½øÐÐʵÀý»¯µÄ¡£¶ÔÓÚÒ»¸ö bean À´Ëµ£¬Èç¹ûδÌṩÈκι¹Ôì·½·¨»òÌṩÁËÎ޲ι¹Ôì·½·¨£¬Ôòµ÷Óà `AbstractAutowireCapableBeanFactory#instantiateBean` À´½øÐÐʵÀý»¯£¬·ñÔòµ÷Óà `AbstractAutowireCapableBeanFactory#autowireConstructor` ½øÐÐʵÀý»¯¡£Êµ¼ÊÉÏ£¬ÕâÁ½¸ö·½·¨×îÖÕ¶¼»áÖ´Ðе½ `BeanUtils#instantiateClass(Constructor, Object...)`: + +``` +public static T instantiateClass(Constructor ctor, Object... args) + throws BeanInstantiationException { + Assert.notNull(ctor, "Constructor must not be null"); + try { + ReflectionUtils.makeAccessible(ctor); + if (KotlinDetector.isKotlinReflectPresent() + && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) { + return KotlinDelegate.instantiateClass(ctor, args); + } + else { + Class[] parameterTypes = ctor.getParameterTypes(); + Assert.isTrue(args.length <= parameterTypes.length, "..."); + Object[] argsWithDefaultValues = new Object[args.length]; + for (int i = 0 ; i < args.length; i++) { + if (args[i] == null) { + Class parameterType = parameterTypes[i]; + argsWithDefaultValues[i] = (parameterType.isPrimitive() + ? DEFAULT_TYPE_VALUES.get(parameterType) : null); + } + else { + argsWithDefaultValues[i] = args[i]; + } + } + // ÕâÀï½øÐÐʵÀý»¯ + return ctor.newInstance(argsWithDefaultValues); + } + } + catch (...) { + ... + } +} + +``` + +¿ÉÒÔ¿´µ½£¬×îÖÕµ÷ÓõÄÊÇ `java.lang.reflect.Constructor#newInstance`£¬Õâ¾ÍÊÇ jdk µÄ·´ÉäÁË¡£ + +ÁíÍ⣬µ± bean ÌṩÁË¶à ¸ö¹¹Ôì·½·¨Ê±£¬spring »á½øÐÐÒ»Ì׸´ÔÓµÄÍÆ¶Ï»úÖÆ£¬Íƶϳö×î¼ÑµÄ¹¹Ôì·½·¨£¬ÕâÀï¾Í²»Õ¹¿ªÁË¡£ + +#### 2\. ²éÕÒÊôÐÔ + +¶ÔÏó´´½¨ºó£¬½ÓÏÂÀ´¾ÍÊǽøÐÐÊôÐÔ×¢ÈëÁË£¬²»¹ýÔÚ×¢ÈëÊôÐÔǰ£¬ÐèÒªÖªµÀÀàÖÐÓÐÄÄЩÊôÐÔÐèҪעÈë¡£spring ÊôÐÔ²éÕÒÊÇÔÚ `AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors` Öе÷ÓúóÖô¦ÀíÆ÷½øÐвÙ×÷µÄ£º + +``` +protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, + Class beanType, String beanName) { + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof MergedBeanDefinitionPostProcessor) { + // µ÷ÓúóÖô¦ÀíÆ÷ + MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; + bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); + } + } +} + +``` + +ÔÚÕâЩºóÖô¦ÀíÆ÷ÖУ¬ + +1. µ÷Óà `AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition` ²éÕÒ´øÓÐ `@Autowired`¡¢`@Value` ×¢½âµÄÊôÐԺͷ½·¨ +2. µ÷Óà `CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition` ²éÕÒ´øÓÐ `@Resource` ×¢½âµÄÊôÐԺͷ½·¨ + +ÕâÀïΪÁË·½±ã˵Ã÷ `@Autowired` µÄ×¢ÈëÁ÷³Ì£¬ÎÒÃÇÔÚ demo01 µÄ `org.springframework.learn.demo01.BeanObj1` Ìí¼ÓÁ½ÐÐÐдúÂ룺 + +``` +package org.springframework.learn.demo01; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BeanObj1 { + + // ÒÔÏÂÁ½ÐÐΪмÓÈëµÄ´úÂë + @Autowired + private BeanObj2 beanObj2; + + public BeanObj1() { + System.out.println("µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj1{}"; + } +} + +``` + +½øÈë `AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition` ·½·¨£º + +``` +@Override +public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, + Class beanType, String beanName) { + // ²éÕÒÊôÐÔÓë·½·¨ + InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); + // ÑéÖ¤²éÕÒµ½µÄÊôÐÔÓë·½·¨£¬²¢×¢²áµ½beanDefinition + metadata.checkConfigMembers(beanDefinition); +} + +``` + +һ·¸ú½ø `AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata` ·½·¨£¬¸úµ½ÁË `AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata`: + +``` +private InjectionMetadata buildAutowiringMetadata(final Class clazz) { + ... + // ÕâÀïÅÜÁËÒ»¸öÑ­»·£¬ÕÒÍ굱ǰÀàÕÒµ±Ç°ÀàµÄ¸¸Àֱ࣬µ½ObjectÀà + do { + final List currElements = new ArrayList<>(); + + // ²éÕÒÊôÐÔ + ReflectionUtils.doWithLocalFields(targetClass, field -> { + MergedAnnotation ann = findAutowiredAnnotation(field); + if (ann != null) { + if (Modifier.isStatic(field.getModifiers())) { + return; + } + boolean required = determineRequiredStatus(ann); + // ÊôÐÔ·â×°³É AutowiredFieldElement + currElements.add(new AutowiredFieldElement(field, required)); + } + }); + + // ²éÕÒ·½·¨ + ReflectionUtils.doWithLocalMethods(targetClass, method -> { + Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); + if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { + return; + } + MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod); + if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + if (Modifier.isStatic(method.getModifiers())) { + return; + } + if (method.getParameterCount() == 0) { + } + boolean required = determineRequiredStatus(ann); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); + // ·½·¨·â×°³É AutowiredMethodElement + currElements.add(new AutowiredMethodElement(method, required, pd)); + } + }); + + elements.addAll(0, currElements); + targetClass = targetClass.getSuperclass(); + } + while (targetClass != null && targetClass != Object.class); + + return InjectionMetadata.forElements(elements, clazz); +} + +``` + +ÒÔÉÏ´úÂë¾ÍÊDzéÕÒ¹ý³Ì£¬×ܽáÈçÏ£º + +* Ñ­»·²éѯ¸¸À࣬´Óµ±Ç°À࿪ʼ£¬ÕÒÍ굱ǰÀà¾ÍÕÒµ±Ç°ÀàµÄ¸¸Àֱ࣬½Ó Object Ϊֹ£» +* ²éÕÒÊôÐÔÓë·½·¨£¬ÊôÐÔÕÒµ½ºó·âװΪ `AutowiredFieldElement`£¬·½·¨ÕÒµ½ºó·âװΪ `AutowiredMethodElement`£» +* ÎÞÂÛÊDzéÕÒÊôÐÔ»¹ÊÇ·½·¨£¬Ê¹ÓõͼÊÇ `findAutowiredAnnotation` ·½·¨ + +½ÓÏÂÀ´£¬ÎÒÃǾͿ´¿´ `findAutowiredAnnotation` ·½·¨ÊÇÈçºÎ½øÐвéÕҵġ£½øÈë `AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation` ·½·¨£¬ÄÚÈÝÈçÏ£º + +``` +private final Set> autowiredAnnotationTypes + = new LinkedHashSet<>(4); + +// ĬÈϹ¹Ôì·½·¨ÖÐÖ¸¶¨ÁË@Autowired¡¢@Value×¢½â +@SuppressWarnings("unchecked") +public AutowiredAnnotationBeanPostProcessor() { + this.autowiredAnnotationTypes.add(Autowired.class); + this.autowiredAnnotationTypes.add(Value.class); + ... +} + +// ÕâÀï¾ÍÊDzéÕÒ·½·¨ +@Nullable +private MergedAnnotation findAutowiredAnnotation(AccessibleObject ao) { + MergedAnnotations annotations = MergedAnnotations.from(ao); + // autowiredAnnotationTypes °üº¬ÁË @Autowired¡¢@Value×¢½â + for (Class type : this.autowiredAnnotationTypes) { + MergedAnnotation annotation = annotations.get(type); + if (annotation.isPresent()) { + return annotation; + } + } + return null; +} + +``` + +ÒÔÉÏ´úÂë×¢½âµÃºÜÃ÷°×ÁË£¬ÕâÀï¾Í²»¶à˵ÁË¡£ + +ÕÒµ½ÊôÐÔÓë·½·¨ºó£¬½ÓמÍÊÇ×¢²áµ½ `beanDefinition` ÁË£¬ÕâÊÇ `InjectionMetadata#checkConfigMembers` Ëù×öµÄ¹¤×÷£º + +``` +public void checkConfigMembers(RootBeanDefinition beanDefinition) { + Set checkedElements = new LinkedHashSet<>(this.injectedElements.size()); + for (InjectedElement element : this.injectedElements) { + Member member = element.getMember(); + if (!beanDefinition.isExternallyManagedConfigMember(member)) { + // ×¢²áµ½ beanDefinition + beanDefinition.registerExternallyManagedConfigMember(member); + checkedElements.add(element); + } + } + this.checkedElements = checkedElements; +} + +``` + +ËùνµÄ×¢²á£¬ÆäʵҲÏ൱¼òµ¥£¬¾ÍÊÇÍù `beanDefinition` ³ÖÓÐµÄ `externallyManagedConfigMembers` ¼¯ºÏÖÐÌí¼ÓÒ»Ìõ¼Ç¼£º + +> RootBeanDefinition#registerExternallyManagedConfigMember + +``` +public void registerExternallyManagedConfigMember(Member configMember) { + synchronized (this.postProcessingLock) { + if (this.externallyManagedConfigMembers == null) { + this.externallyManagedConfigMembers = new HashSet<>(1); + } + // Íùset¼¯ºÏÖÐÌí¼ÓÒ»Ìõ¼Ç¼ + this.externallyManagedConfigMembers.add(configMember); + } +} + +``` + +#### 3\. ×¢ÈëÊôÐÔ + +spring µÄÊôÐÔ×¢ÈëÊÇÔÚ·½·¨ `AbstractAutowireCapableBeanFactory#populateBean` Öд¦ÀíµÄ£¬ÄÚÈÝÈçÏ£º + +``` +protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { + if (bw == null) { + if (mbd.hasPropertyValues()) { + throw new BeanCreationException(...); + } + else { + return; + } + } + + if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof InstantiationAwareBeanPostProcessor) { + // µ÷ÓþßÌåµÄºóÖô¦ÀíÆ÷Íê³ÉÊôÐÔÌî³ä + // AutowiredAnnotationBeanPostProcessor.postProcessProperties ´¦Àí @Autowired¡¢@Value ×¢½â + // CommonAnnotationBeanPostProcessor.postProcessProperties ´¦Àí @Resource×¢½â + InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; + // Èç¹û·µ»Ø false£¬´ú±í²»ÐèÒª½øÐкóÐøµÄÊôÐÔÉèÖµ£¬Ò²²»ÐèÒªÔÙ¾­¹ýÆäËûµÄ BeanPostProcessor µÄ´¦Àí + if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { + return; + } + } + } + } + + // beanµÄËùÓÐÊôÐÔ + PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); + + int resolvedAutowireMode = mbd.getResolvedAutowireMode(); + // ÊôÐÔ×¢ÈëÀàÐÍΪbyType»¹ÊÇbyName£¬×¢£º@Autowired ¼È²»ÊÇbyTypeÒ²²»ÊÇbyName,Òò´ËÕâÀï·µ»Øfalse + if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { + MutablePropertyValues newPvs = new MutablePropertyValues(pvs); + // ͨ¹ýÃû×ÖÕÒµ½ËùÓÐÊôÐÔÖµ£¬Èç¹ûÊÇ bean ÒÀÀµ£¬Ïȳõʼ»¯ÒÀÀµµÄ bean¡£¼Ç¼ÒÀÀµ¹ØÏµ + if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { + autowireByName(beanName, mbd, bw, newPvs); + } + // ͨ¹ýÀàÐÍ×°Åä¡£¸´ÔÓһЩ + if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { + autowireByType(beanName, mbd, bw, newPvs); + } + pvs = newPvs; + } + + boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); + boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + + PropertyDescriptor[] filteredPds = null; + if (hasInstAwareBpps) { + if (pvs == null) { + pvs = mbd.getPropertyValues(); + } + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof InstantiationAwareBeanPostProcessor) { + // µ÷ÓúóÖô¦ÀíÆ÷Íê³É×¢Ê͵ÄÊôÐÔÌî³ä + // ´¦Àí@Autowired×¢½âµÄÊÇ AutowiredAnnotationBeanPostProcessor + InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; + PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); + if (pvsToUse == null) { + if (filteredPds == null) { + filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); + } + // ÕâÀï¾ÍÊÇÉÏ·½Ôø¾­Ìáµ½¹ýµÃ¶Ô@Autowired´¦ÀíµÄÒ»¸öBeanPostProcessorÁË + // Ëü»á¶ÔËùÓбê¼Ç@Autowired¡¢@Value ×¢½âµÄÊôÐÔ½øÐÐÉèÖµ + pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, + bw.getWrappedInstance(), beanName); + if (pvsToUse == null) { + return; + } + } + pvs = pvsToUse; + } + } + } + if (needsDepCheck) { + if (filteredPds == null) { + filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); + } + checkDependencies(beanName, mbd, filteredPds, pvs); + } + + if (pvs != null) { + // ÉèÖà bean ʵÀýµÄÊôÐÔÖµ + applyPropertyValues(beanName, mbd, bw, pvs); + } +} + +``` + +spring µÄÊôÐÔÌî³äÊÇÔÚºóÖô¦ÀíÆ÷ÖнøÐеģ¬`@Autowired` µÄÊôÐÔÌî³ä·½·¨Îª `AutowiredAnnotationBeanPostProcessor#postProcessProperties`: + +``` +public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { + // µ÷Óà findAutowiringMetadata ·½·¨£¬È·±£±ê×¢ @Autowired @Value ×¢½âµÄÊôÐÔÓë·½·¨»ñÈ¡³É¹¦ + // findAutowiringMetadata ¿ÉÒÔ·¢ÏÖ£¬ÀïÃæ×öÁËÒ»¸ö»º´æ£¬Ö»ÓеÚÒ»´Î²Å»áÕæÕýÈ¥»ñÈ¡£¬Ö®ºó¶¼´Ó»º´æÖлñÈ¡ + InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); + try { + // ÕâÀï½øÐÐÊôÐÔ×¢Èë + metadata.inject(bean, beanName, pvs); + } + catch (...) { + ... + } +} + +``` + +ÎÒÃÇÔÙ½øÈë `InjectionMetadata#inject`£º + +``` +public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) + throws Throwable { + Collection checkedElements = this.checkedElements; + Collection elementsToIterate = + (checkedElements != null ? checkedElements : this.injectedElements); + if (!elementsToIterate.isEmpty()) { + // ÕâÀïµÄ InjectedElement£¬¾ÍÊÇÔÚ AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata + // ÖвéÕÒµ½µÄ£¬field »á·âװΪ AutowiredFieldElement£¬method »á»á·âװΪ AutowiredMethodElement + for (InjectedElement element : elementsToIterate) { + element.inject(target, beanName, pvs); + } + } +} + +``` + +ÎÒÃÇÔÙ¸ú½ø `element.inject(target, beanName, pvs)`£¬ÕâÀïµ÷Óõ½µÄÊÇ `AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`£º + +``` +@Override +protected void inject(Object bean, @Nullable String beanName, + @Nullable PropertyValues pvs) throws Throwable { + Field field = (Field) this.member; + Object value; + if (this.cached) { + value = resolvedCachedArgument(beanName, this.cachedFieldValue); + } + else { + DependencyDescriptor desc = new DependencyDescriptor(field, this.required); + desc.setContainingClass(bean.getClass()); + Set autowiredBeanNames = new LinkedHashSet<>(1); + Assert.state(beanFactory != null, "No BeanFactory available"); + TypeConverter typeConverter = beanFactory.getTypeConverter(); + try { + // ÕâÒ»ÐоÍÊÇ»ñȡעÈëµÄbean + value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); + } + catch (BeansException ex) { + ... + } + ... + } + // ͨ¹ý·´É䏸ÊôÐÔÉèÖÃÖµ + if (value != null) { + ReflectionUtils.makeAccessible(field); + field.set(bean, value); + } + } +} + +``` + +ÒÔÉÏ´úÂëÓеã¶à£¬µ«ÎÒÃÇÖ»ÒªÖ÷Òª¹Ø×¢Á½ÐдúÂë¾ÍÐÐÁË£º + +1. »ñÈ¡ÐèҪעÈëµÄ bean: `value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)` +2. ʹÓ÷´ÉäÉèÖà bean£º`ReflectionUtils.makeAccessible(field);field.set(bean, value);` + +¶ÔÓÚµÚ 2 µã£¬×îÖÕµ÷ÓõÄÊÇ jdk ÌṩµÄ·´Éä·½·¨£¬Ã»Ê²Ã´ºÃ˵µÄ¡£ÕâÀïÎÒÃÇÖØµãÀ´¿´¿´ spring ÊÇÈçºÎ»ñÈ¡ÐèҪעÈëµÄ bean¡£ + +¸ú½ø `beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)`£¬ÕâÀï¶Ô²»ÖØÒªµÄ´úÂë½ö¸ø³öµ÷ÓÃÁ´£¬µ÷ÓÃÈçÏ£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-DefaultListableBeanFactory#preInstantiateSingletons + |-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory) + |-AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) + |-AbstractAutowireCapableBeanFactory#doCreateBean + |-AbstractAutowireCapableBeanFactory#populateBean + |-AutowiredAnnotationBeanPostProcessor#postProcessProperties + |-InjectionMetadata#inject + |-AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject + |-DefaultListableBeanFactory#resolveDependency + |-DefaultListableBeanFactory#doResolveDependency + +``` + +×îÖÕÔËÐе½ `DefaultListableBeanFactory#doResolveDependency`: + +``` +@Nullable +public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, + @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) + throws BeansException { + + InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); + try { + ... + // 1\. ¸ù¾ÝÀàÐͲéÕÒËùÓеÄbeanClass£¬mapÖб£´æµÄÄÚÈÝΪ£ºbeanName -> Class + Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); + ... + String autowiredBeanName; + Object instanceCandidate; + // 2\. Èç¹ûÕÒµ½µÄbeanÓжà¸ö£¬¼´ÊýÁ¿´óÓÚ1 + if (matchingBeans.size() > 1) { + // Ôò»ñȡעÈëµÄÊôÐÔÃû + autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); + if (autowiredBeanName == null) { + if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { + return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); + } + else { + return null; + } + } + // 3\. ¸ù¾Ý×¢ÈëµÄÊôÐÔÃû´Ó ¸ù¾ÝÀàÐÍÕÒ³öÀ´µÄmapµ±ÖÐÕÒ£¬ + // µÃµ½beanµÄClass¶ÔÏó + instanceCandidate = matchingBeans.get(autowiredBeanName); + } + else { + Map.Entry entry = matchingBeans.entrySet().iterator().next(); + autowiredBeanName = entry.getKey(); + instanceCandidate = entry.getValue(); + } + if (autowiredBeanNames != null) { + autowiredBeanNames.add(autowiredBeanName); + } + if (instanceCandidate instanceof Class) { + // ¸ù¾Ýclass »ñÈ¡ bean£¬Èç¹ûbean²»´æÔÚ£¬Ôò»á´´½¨bean + instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); + } + Object result = instanceCandidate; + if (result instanceof NullBean) { + if (isRequired(descriptor)) { + raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); + } + result = null; + } + if (!ClassUtils.isAssignableValue(type, result)) { + throw new BeanNotOfRequiredTypeException(...); + } + return result; + } + finally { + ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); + } +} + +``` + +¶ÔÓÚ bean µÄ»ñÈ¡£¬ÕâÀïÔÙ×ܽáÏ£º + +1. ¸ù¾Ý bean ÀàÐÍ´Ó spring ÖлñÈ¡ bean µÄ Class ¶ÔÏó£¬×¢Ò⣺ÕâÀïµÃµ½µÄ¶ÔÏóΪ Class£¬ÇÒ»ñÈ¡µ½µÄ Class ¶ÔÏó´Ó spring ÖлñÈ¡ beanClass ¿ÉÄÜÓжà¸ö£» +2. Èç¹ûµÃµ½µÄ Class ¶ÔÏóÓжà¸ö£¬Ôò»ñȡעÈëµÄÊôÐÔÃû£¬¸ù¾ÝÊôÐÔÃû²éÕÒ´ÓµÚ 1 ²½µÃµ½µÄ Class ¶ÔÏóÖвéÕÒ·ûºÏÌõ¼þµÄ 1 ¸ö Class ¶ÔÏó£» +3. ¸ù¾Ý Class ¶ÔÏó´Ó spring ÖлñÈ¡ bean. + +ÀýÈ磬ÏÖÔÚ½Ó¿Ú `Inter`¡¢ÊµÏÖÀà `A` Óë `B`£¬ÈýÕß¹ØÏµÈçÏ£º + +``` +interface Inter { + +} + +@Service +class A implements Inter { + +} + +@Service +class B implements Inter { + +} + +``` + +ÔÚÀà `C` ÖУ¬×¢Èë `Inter` ÀàÐÍ£º + +``` +@Service +class C { + @Autowired + private Inter b; +} + +``` + +ÔÚ×¢Èëʱ£¬ + +1. spring »áͨ¹ý `Inter.class` ²éÕÒ£¬µÃµ½Á½¸öÀࣺ`A.class` Óë `B.class`£¬³öÏÖÁ˶à¸ö bean Class ¶ÔÏó£» +2. µÃµ½×¢ÈëµÄÊôÐÔÃû£¬ÕâÀïÊÇ `b`£¬È»ºóʹÓà `b` ÔÚÒÔÉϵõ½µÄ bean Class ¶ÔÏóÕÒµ½·ûºÏµÄ bean Class ¶ÔÏó£¬ÏÔÈ»£¬`B.class` ·ûºÏ£» +3. ´Ó spring ÈÝÆ÷ÖлñÈ¡ `B.class` ¶ÔÓ¦µÄ bean. + +ÕâÀï»áÓиöСÎÊÌ⣺Èç¹ûÔÚ `C` ÖÐÕâÑù×¢Èë: + +``` +@Service +class C { + @Autowired + private Inter inter; +} + +``` + +¼´ `Inter` µÄÊôÐÔÃûΪ `inter`£¬¶ø spring ÖÐÓÖûÓÐÃû³ÆÎª `inter` µÄ bean Class ¶ÔÏó£¬ÕâÑùÄÜ×¢Èë³É¹¦Â𣿠+ +ÊÂʵÉÏ£¬ÕâÑù×¢Èë²»»á³É¹¦£¬ÇÒ spring »á±¨Òì³££¬¸ÐÐËȤµÄС»ï°é¿ÉÒÔ×Ô¼ºÊÔÊÔ¡£ + +×îºó£¬ÎÒÃÇÔÙÀ´¿´¿´ÈçºÎ¸ù¾Ý class ´Ó spring ÖлñÈ¡¶ÔÓ¦µÄ bean£¬¼´ `instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this)`£¬¸ú½øÈ¥£º + +> DependencyDescriptor#resolveCandidate + +``` +public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) + throws BeansException { + // µ÷ÓõÄÉÏ AbstractBeanFactory#getBean(String) + return beanFactory.getBean(beanName); +} + +``` + +Õâ¸ö·½·¨Ï൱¼òµ¥£¬¾ÍÖ»ÓÐÒ»ÐУº`beanFactory.getBean(beanName)`£¬»Ø¹Ëϵ÷ÓÃÁ´£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-DefaultListableBeanFactory#preInstantiateSingletons + // ÕâÀïµÚÒ»´Îµ÷ÓÃÁËAbstractBeanFactory#getBean + // ´Ëʱ´«ÈëµÄ²ÎÊýΪbeanObj1 + |-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory) + |-AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) + |-AbstractAutowireCapableBeanFactory#doCreateBean + |-AbstractAutowireCapableBeanFactory#populateBean + |-AutowiredAnnotationBeanPostProcessor#postProcessProperties + |-InjectionMetadata#inject + |-AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject + |-DefaultListableBeanFactory#resolveDependency + |-DefaultListableBeanFactory#doResolveDependency + |-DependencyDescriptor#resolveCandidate + // ÕâÀïµÚÒ»´Îµ÷ÓÃÁËAbstractBeanFactory#getBean£¬ + // ´Ëʱ´«ÈëµÄ²ÎÊýΪbeanObj2 + |-AbstractBeanFactory#getBean(String) + .. ÏÂÃæ¿ªÊ¼½øÐÐ`beanObj2`µÄʵÀý»¯Óë³õʼ»¯²Ù×÷ + +``` + +½áºÏµ÷ÓÃÁ´¼°Ç°Ãæ·ÖÎö¿ÉÒÔ¿´³ö£¬ + +* ÔÚ»ñÈ¡ `beanObj1` ʱµÚÒ»´Îµ÷Óà `AbstractBeanFactory#getBean(String)`£¬ÓÉÓÚ´Ëʱ spring Öв»´æÔÚ `beanObj1`£¬¾Í¿ªÊ¼ÁË `beanObj1` µÄ´´½¨£» +* `beanObj1` ʵÀý»¯Íê³Éºó£¬½ÓמͽøÐÐÊôÐÔ×¢È룬`beanObj1` ÖÐÓиöÊôÐÔ `BeanObj2 beanObj2`£¬Í¬Ñùµ÷Óà `AbstractBeanFactory#getBean(String)` ´Ó spring ÖлñÈ¡ `beanObj2` ¶ÔÏó£» +* ÓÉÓÚ´Ëʱ spring Öв»´æÔÚ `beanObj2`£¬½ÓמͽøÈëÁË `beanObj2` ¶ÔÏóµÄ´´½¨¹ý³Ì£»´´½¨¶ÔÏóºó£¬Í¬ÑùÒ²»á¶Ô `beanObj2` ÊôÐÔ×¢Èë¡¢ÔËÐгõʼ»¯·½·¨µÈ²Ù×÷£¬ÕâÓë `beanObj1` µÄ²Ù×÷ÀàËÆ£» +* µÃµ½ `beanObj2` ¶ÔÏóºó£¬×¢Èëµ½ `beanObj1` µÄ `BeanObj2 beanObj2` ÊôÐÔÖУ¬È»ºó½øÐÐ `beanObj1` µÄºóÐø²Ù×÷¡£ + +´ÓÕâÀï¿ÉÒÔ¿´³ö£¬ÊôÐÔ×¢Èëʱ£¬¿ÉÄÜ»áʹ bean ³õʼ»¯Ìáǰ£»Í¬Ê±£¬¶ÔÓÚÒÀÀµ²ã´Î¶àµÄÇé¿ö£¬µ÷ÓÃÁ´½«»á·Ç³£³¤£¬Èç A ÐèҪעÈë B£¬B ҪעÈë C£¬ÄÇôÔÚ A µÄÊôÐÔ×¢Èëʱ£¬½«»á³õʼ»¯ B£¬¶ø B ÔÚ³õʼ»¯Ê±£¬ÓÖ»á³õʼ»¯ C¡£ + +¶ÔÓÚ bean µÄʵÀý»¯Óë³õʼ»¯£¬ÕâÀïÐèÒªÃ÷È·Ï£º + +* ʵÀý»¯£º½öÖ¸´´½¨¶ÔÏóµÄ²Ù×÷£¬¿ÉÒÔʹÓà new »ò·´Éä»úÖÆ£¬Î´´¦Àí spring ÊôÐÔ×¢Èë¼°Ö®ºóµÄÔËÐгõʼ»¯·½·¨¡¢±£´æµ½ spring ÈÝÆ÷µÈһϵÁвÙ×÷£» +* ³õʼ»¯£º¶ÔʵÀý»¯ºóµÄ¶ÔÏó½øÐÐÊôÐÔ×¢Èë¡¢ÔËÐгõʼ»¯·½·¨£¬×îÖÕ±£´æµ½ spring ÈÝÆ÷£» + +#### 4\. ³õʼ»¯ bean + +spring ³õʼ»¯ bean µÄ·½·¨ÊÇ `AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)`: + +``` +protected Object initializeBean(final String beanName, final Object bean, + @Nullable RootBeanDefinition mbd) { + // 1\. ÔËÐÐ invokeAwareMethods + if (System.getSecurityManager() != null) { + AccessController.doPrivileged((PrivilegedAction) () -> { + invokeAwareMethods(beanName, bean); + return null; + }, getAccessControlContext()); + } + else { + invokeAwareMethods(beanName, bean); + } + + // 2\. ÔËÐÐ applyBeanPostProcessorsBeforeInitialization + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + // Ö´ÐÐ spring µ±ÖеÄÄÚÖô¦ÀíÆ÷¡ª¡ªxxxPostProcessor-------@PostConstruct + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + try { + // 3\. Ö´ÐÐ InitializingBean ³õʼ»¯ + invokeInitMethods(beanName, wrappedBean, mbd); + } + catch (Throwable ex) { + ... + } + if (mbd == null || !mbd.isSynthetic()) { + // 4\. ÔËÐÐ applyBeanPostProcessorsAfterInitialization + wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); + } + + return wrappedBean; +} + +``` + +ÒÔÉÏ´úÂëÖ÷ÒªÊÇÓÃÀ´ÔËÐÐ 4 ¸ö·½·¨£º + +1. invokeAwareMethods(beanName, bean); +2. applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); +3. invokeInitMethods(beanName, wrappedBean, mbd); +4. applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); + +##### 4.1 µ÷Óà Aware bean µÄ·½·¨ + +`invokeAwareMethods` ¾ÍÊÇÖ´ÐÐ Aware bean µÄÏà¹Ø·½·¨£º + +``` +private void invokeAwareMethods(final String beanName, final Object bean) { + if (bean instanceof Aware) { + if (bean instanceof BeanNameAware) { + ((BeanNameAware) bean).setBeanName(beanName); + } + if (bean instanceof BeanClassLoaderAware) { + ClassLoader bcl = getBeanClassLoader(); + if (bcl != null) { + ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); + } + } + if (bean instanceof BeanFactoryAware) { + ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); + } + } +} + +``` + +ÒÔÉÏ·½·¨±È½Ï¼òµ¥£¬¾Í²»¶à˵ÁË¡£ + +##### 4.2 ÔËÐкóÖô¦ÀíÆ÷µÄ `postProcessBeforeInitialization` ·½·¨ + +½Ó×ÅÀ´¿´¿´ `AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization` µÄÔËÐУº + +``` +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + // µ÷ÓúóÖô¦ÀíÆ÷ + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} + +``` + +ÕâÀïÖ÷ÒªÔËÐÐ `ApplicationContextAwareProcessor#postProcessBeforeInitialization` ·½·¨£¬ÄÚÈÝÈçÏ£º + +``` +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ + return bean; + } + AccessControlContext acc = null; + if (System.getSecurityManager() != null) { + acc = this.applicationContext.getBeanFactory().getAccessControlContext(); + } + if (acc != null) { + AccessController.doPrivileged((PrivilegedAction) () -> { + invokeAwareInterfaces(bean); + return null; + }, acc); + } + else { + // ÔËÐз½·¨ + invokeAwareInterfaces(bean); + } + return bean; +} + +private void invokeAwareInterfaces(Object bean) { + if (bean instanceof EnvironmentAware) { + ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); + } + if (bean instanceof EmbeddedValueResolverAware) { + ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); + } + if (bean instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); + } + if (bean instanceof ApplicationEventPublisherAware) { + ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); + } + if (bean instanceof MessageSourceAware) { + ((MessageSourceAware) bean).setMessageSource(this.applicationContext); + } + // ×°Åä ʵÏÖÁËApplicationContextAwareµÄÀàµÄ applicationContext + if (bean instanceof ApplicationContextAware) { + ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); + } +} + +``` + +ÆäʵÕâ¸ö·½·¨¾ÍÊǸø¸÷ÖÖ `XxxAware` ¸³Öµ£¬±ÈÈç´óÃû¶¦¶¦µÄ `ApplicationContextAware` ¸³ÖµÒ²ÊÇÔÚÕâÀï½øÐС£ + +˳´øÒ»ÌáµÄÊÇ£¬Èç¹ûÔÚ bean ÖУ¬·½·¨ÉÏÓÐ `@PostConstruct` ×¢½â£º + +``` +@PostConstruct +public void test() { + System.out.println("PostConstruct"); +} + +``` + +¸Ã·½·¨½«»áÔÚ `InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization` ÖнøÐе÷Ó㬴úÂë±È½Ï¼òµ¥£¬Ò²ÊÇÀûÓÃÁË jdk µÄ·´Éä»úÖÆ£¬ÕâÀï¾Í²»¶à˵ÁË¡£ + +##### 4.3 µ÷Óà bean µÄ³õʼ»¯·½·¨ + +½Ó×ÅÀ´¿´¿´ `AbstractAutowireCapableBeanFactory#invokeInitMethods` ·½·¨: + +``` +protected void invokeInitMethods(String beanName, final Object bean, + @Nullable RootBeanDefinition mbd) throws Throwable { + // Ö´ÐÐ InitializingBean#afterPropertiesSet ·½·¨ + boolean isInitializingBean = (bean instanceof InitializingBean); + if (isInitializingBean && (mbd == null || + !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { + if (System.getSecurityManager() != null) { + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + ((InitializingBean) bean).afterPropertiesSet(); + return null; + }, getAccessControlContext()); + } + catch (PrivilegedActionException pae) { + throw pae.getException(); + } + } + else { + // Èç¹ûbeanʵÏÖÁË InitializingBean ½Ó¿Ú£¬Ôòµ÷Óà afterPropertiesSet() + ((InitializingBean) bean).afterPropertiesSet(); + } + } + + // Ö´ÐÐ×Ô¶¨ÒåµÄ³õʼ»¯·½·¨£¬¼´ÔÚxmlÖУ¬Í¨¹ý init-method Ö¸¶¨µÄ·½·¨ + if (mbd != null && bean.getClass() != NullBean.class) { + String initMethodName = mbd.getInitMethodName(); + if (StringUtils.hasLength(initMethodName) && + !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && + !mbd.isExternallyManagedInitMethod(initMethodName)) { + invokeCustomInitMethod(beanName, bean, mbd); + } + } +} + +``` + +ÕâÀïÖ÷ÒªÊÇÔËÐÐÁ½¸ö·½·¨£º + +1. Èç¹û bean ʵÏÖÁË InitializingBean ½Ó¿Ú£¬Ôòµ÷Óà afterPropertiesSet () +2. Èç¹ûÖ¸¶¨ÁË init-method£¬ÔòÔÚ `invokeCustomInitMethod` ÖÐÔËÐУ¬Ò²ÊÇͨ¹ý jdk ·´Éä»úÖÆÀ´ÔËÐеģ¬¾Í²»¶à˵ÁË¡£ + +##### 4.4 ÔËÐкóÖô¦ÀíÆ÷µÄ `postProcessAfterInitialization` ·½·¨ + +ÕâÒ»²½Ö÷ÒªÊÇÔËÐÐ `BeanPostProcessor#postProcessAfterInitialization`£¬ËùÔËÐÐµÄ `BeanPostProcessor` ÊÇ `ApplicationListenerDetector`: + +``` +@Override +public Object postProcessAfterInitialization(Object bean, String beanName) { + if (bean instanceof ApplicationListener) { + Boolean flag = this.singletonNames.get(beanName); + if (Boolean.TRUE.equals(flag)) { + this.applicationContext.addApplicationListener((ApplicationListener) bean); + } + else if (Boolean.FALSE.equals(flag)) { + this.singletonNames.remove(beanName); + } + } + return bean; +} + +``` + +Õâ¸ö·½·¨ÊÇÓÃÀ´´¦Àí `ApplicationListener` µÄ£¬Èç¹ûÓÐ bean ʵÏÖÁË `ApplicationListener`£¬¾ÍÊÇÔÚÕâÀïÌí¼Óµ½ spring µÄ¼àÌýÆ÷ÁбíµÄ¡£ + +#### 5\. ×ܽá + +±¾ÎÄÖ÷Òª½éÉÜÁË spring bean ´´½¨¹ý³Ì£¬Ïà¹ØÁ÷³Ì×ܽáÈçÏ£º + +1. ʵÀý»¯ bean£¬Ö¸¶¨Á˳õʼ»¯·½·¨¡¢Ö¸¶¨ÁË `factory method`¡¢×ÔÖ÷ÍÆ¶Ï¹¹Ôì·½·¨µÈ·½Ê½£» + +2. ²éѯ´ý×¢ÈëµÄÊôÐÔ»ò·½·¨£¬¼´ÄÄЩÊôÐÔ»ò·½·¨Éϱê×¢ÁË `@Autowird`/`@Value`/`@Resource` + + * `AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition` ÓÃÀ´²éÕÒ´øÓÐ @Autowired¡¢@Value ×¢½âµÄÊôÐԺͷ½·¨ + * `CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition` ÓÃÀ´²éÕÒ´øÓÐ @Resource ×¢½âµÄÊôÐԺͷ½·¨ +3. ÊôÐÔÌî³ä + + * `AutowiredAnnotationBeanPostProcessor.postProcessProperties` ´¦Àí `@Autowired`¡¢`@Value` ×¢½â£¬`CommonAnnotationBeanPostProcessor.postProcessProperties` ´¦Àí `@Resource` ×¢½â + * ´¦Àí `@Autowired` ʱ£¬Ïȸù¾ÝÀàÐÍÕÒµ½ËùÓÐµÄ `Class`£¬Èç¹ûÓжà¸ö£¬ÔÙ¸ù¾Ý×¢ÈëÊôÐÔµÄÃû³Æ²éÕÒ·ûºÏµÄ `Class`£¬×îºóµ÷Óà `beanFactory.getBean(...)` ´Ó spring »ñȡҪעÈëµÄ¶ÔÏó£¬Í¨¹ý·´Éä»úÖÆÎª¶ÔÓ¦µÄÊôÐÔÉèÖÃÖµ£» +4. ³õʼ»¯ bean + + 1. Ö´ÐÐ Aware bean µÄÏà¹Ø·½·¨ + 2. µ÷Óà `BeanPostProcessor#postProcessBeforeInitialization` ·½·¨ + * ÔÚ `ApplicationContextAwareProcessor#postProcessBeforeInitialization` ÖÐÖ´ÐÐ Aware bean µÄÏà¹Ø·½·¨ + * ÔÚ `InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization` Öд¦Àí `@PostConstruct` ×¢½â + 3. Ö´Ðгõʼ»¯·½·¨ + * Ö´ÐÐ `InitializingBean#afterPropertiesSet` ·½·¨ + * Ö´ÐÐ×Ô¶¨ÒåµÄ `init-method` ·½·¨ + 4. ÔËÐÐ `BeanPostProcessor#postProcessAfterInitialization` ·½·¨ + * ÔÚ `ApplicationListenerDetector#postProcessAfterInitialization` Öд¦Àí spring ¼àÌýÆ÷ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b7a4d4bc1bfbd76537e40cf843b0d18df93.png) + +¹ØÓÚ spring bean µÄ´´½¨Á÷³Ì¾ÍÏÈ·ÖÎöµ½ÕâÀïÁË£¬²»¹ý spring ÔÚ´´½¨ bean µÄ¹ý³ÌÖУ¬ÓÐÒ»¸öÎÞ·¨ºöÊÓµÄÇé¿ö£ºÑ­»·ÒÀÀµ£¬¹ØÓÚÕâ·½ÃæµÄ´¦Àí£¬¿ÉÒԲο¼£º + +* [spring Ì½ÃØÖ®Ñ­»·ÒÀÀµµÄ½â¾ö£¨Ò»£©£ºÀíÂÛ»ùʯ](https://my.oschina.net/funcy/blog/4659555) +* [spring Ì½ÃØÖ®Ñ­»·ÒÀÀµµÄ½â¾ö£¨¶þ£©£ºÔ´Âë·ÖÎö](https://my.oschina.net/funcy/blog/4815992) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4659524](https://my.oschina.net/funcy/blog/4659524) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" new file mode 100644 index 0000000..9b9d16e --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\214\357\274\211\357\274\232ApplicationContext\347\232\204\345\210\233\345\273\272.md" @@ -0,0 +1,161 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-07194ddebd25cb2b71ee5e422bf84e8a397.png) + +ÔÚÇ°ÃæÒ»ÆªÎÄÕÂÖУ¬ÎÒÃÇ·ÖÎöÁË spring µÄÕûÌåÆô¶¯Á÷³Ì£¬´ÓÕâÆª¿ªÊ¼ £¬ÎÒÃǽ«¶ÔÆô¶¯¹ý³ÌÖеÄһЩ¹Ø¼ü´úÂë½øÐзÖÎö¡£ + +·ÖÎöÒÀ¾ÉÊÇ»ùÓÚµÄ `demo01`£¬ÎÒÃÇÖ±½Ó½øÐÐ `ApplicationContext context = new AnnotationConfigApplicationContext("org.springframework.learn.demo01");` µÄÖ´ÐÐÖУº + +> AnnotationConfigApplicationContext + +``` +public AnnotationConfigApplicationContext(String... basePackages) { + this(); + scan(basePackages); + refresh(); +} + +``` + +Õâ¸ö·½·¨Ö»ÓÐÈýÐУ¬Ã¿ÐдúÂëµÄ×÷ÓÃÔÚ [spring Æô¶¯Á÷³Ì¸ÅÀÀ](https://my.oschina.net/funcy/blog/4597493 "springÆô¶¯Á÷³Ì¸ÅÀÀ")ÒÑ×ö¹ý˵Ã÷£¬ÕâÀïÎÒÃǽ«¶ÔÕâЩ·½·¨Õ¹¿ª£¬Ïêϸ·ÖÎöÀïÃæµÄÄÚÈÝ¡£ + +### 1. `beanFacotry` µÄ´´½¨ + +ÎÒÃÇÖ±½Ó½øÐÐ `this()` ·½·¨£¬ÄÚÈÝÈçÏ£º + +> AnnotationConfigApplicationContext + +``` +public AnnotationConfigApplicationContext() { + // AnnotatedBeanDefinitionReader ½âÎö @Configuration + this.reader = new AnnotatedBeanDefinitionReader(this); + this.scanner = new ClassPathBeanDefinitionScanner(this); +} + +``` + +±íÃæÉÏ¿´£¬´úÂëÖ»ÓÐÁ½ÐУ¬´´½¨ÁËÁ½¸ö¶ÔÏó¡£µ«ÊìϤ java »ù´¡Óï·¨µÄ¶¼ÖªµÀ£¬×ÓÀàÔÚµ÷Óù¹Ôì·½·¨Ê±£¬»áÏȵ÷Óø¸ÀàµÄ¹¹Ôì·½·¨ÔÙÖ´ÐÐ×ÓÀ๹Ôì·½·¨µÄ´úÂ룬ÒòЩÎÒÃÇ»¹ÐèÒª¿´¿´¸ÃÀàµÄ¸¸À๹Ôì·½·¨Àï×öÁËʲô£º + +> GenericApplicationContext + +``` +public GenericApplicationContext() { + this.beanFactory = new DefaultListableBeanFactory(); +} + +``` + +¿ÉÒÔ¿´µ½£¬¸¸ÀàµÄ¹¹Ôì·½·¨Àï½ö×öÁËÒ»¼þÊ£º´´½¨ `beanFactory`¡£×Ô´Ë£¬ÎÒÃÇ¿ÉÒÔÖªµÀ£º**`AnnotationConfigApplicationContext` ʹÓÃµÄ `BeanFacotry` Ϊ `DefaultListableBeanFactory`**¡£ + +ÎÒÃÇÔٻص½ `AnnotationConfigApplicationContext` µÄ¹¹Ôì·½·¨£º + +> AnnotationConfigApplicationContext + +``` +public AnnotationConfigApplicationContext() { + // AnnotatedBeanDefinitionReader ½âÎö @Configuration + this.reader = new AnnotatedBeanDefinitionReader(this); + this.scanner = new ClassPathBeanDefinitionScanner(this); +} + +``` + +Õâ¸ö·½·¨ËäȻֻÓÐÁ½ÐУ¬×÷ÓÃÈ´²»Ð¡¡£ÎÒÃÇÉîÈë `new AnnotatedBeanDefinitionReader(this);` ¿´Ï£¬ÓÉÓÚÆäÖеķ½·¨µ÷Óò¢²»ÖØÒª£¬ÕâÀï½öÌṩµ÷ÓÃÁ´£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext() + |-AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry) + |-AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment) + |-AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry) + |-AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object) + +``` + +¿ÉÒÔ¿´µ½£¬×îÖÕÊǵ÷ÓÃÁË `AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)`£¬ÎÒÃDz鿴ϸ÷½·¨£¬ÎªÁËÖ±¹Û²é¿´£¬ÕâÀïÊ¡ÂÔÁ˲»±ØÒªµÄ´úÂ룬ÎÒÃǽö¹Ø×¢Ö÷ÒªµÄÁ÷³Ì¼´¿É£º + +``` +public static Set registerAnnotationConfigProcessors( + BeanDefinitionRegistry registry, @Nullable Object source) { + // »ñµÃbeanFactory + DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); + + // -------- ÍùbeanFactoryÖÐÌí¼Ó´¦ÀíÀà + if (beanFactory != null) { + if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { + beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); + } + if (!(beanFactory.getAutowireCandidateResolver() + instanceof ContextAnnotationAutowireCandidateResolver)) { + beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); + } + } + + Set beanDefs = new LinkedHashSet<>(8); + + // ------------ ÍùbeanFactoryÖÐÌí¼ÓbeanDefinition + if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. + if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. + if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(); + try { + def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, + AnnotationConfigUtils.class.getClassLoader())); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException(...); + } + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); + } + + if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); + } + return beanDefs; +} + +``` + +Õâ¸ö·½·¨´úÂëËäÈ»Óе㳤£¬µ«¹¦ÄÜÈ´Ï൱ֱ°×£¬¾ÍÊÇÏò `beanFactory` Ìí¼Ó `annotation` Ïà¹ØµÄ´¦ÀíÆ÷¡£Êµ¼ÊÉÏ£¬ÔÚ [spring Æô¶¯Á÷³Ì¸ÅÀÀ](https://my.oschina.net/funcy/blog/4597493 "springÆô¶¯Á÷³Ì¸ÅÀÀ")Ìáµ½µÄ beanDefinitionMap ÖÐ 4 ¸öĬÈ쵀 beanDefinition ¾ÍÊÇÔÚÕâÀïÌí¼ÓµÄ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a490052bdc1379ef24a7754c65584214c1c.png) + +½Ó×ÅÎÒÃÇÔÙ¹Ø×¢ `this.scanner = new ClassPathBeanDefinitionScanner(this)`£¬ÕâÊdzõʼ»¯ `scanner` ¶ÔÏó£¬ÀàÐÍΪ `ClassPathBeanDefinitionScanner`£¬¸ÃÀà´ÓÃû×ÖÉÏ¿ÉÒÔ¿´³ö£¬ÕâÊÇÓë `classPath` Ïà¹ØµÄ£¬`beanDefinition` ɨÃèÆ÷£¬Í¨Ë×µØËµ£¬¾ÍÊÇ**ɨÃè classPath ·¾¶£¬½« java class Îļþ×é×°³É `beanDefinition` ¶ÔÏó**¡£ + +### 2\. ×ܽá + +`AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)` ÖÐ `this()` µÄÖ´ÐоͷÖÎöµ½ÕâÀïÁË£¬ÕâÒ» ÐдúÂëÖ÷Òª×öÁËÕ⼸¼þÊ£º + +1. ´´½¨ÁËÀàÐÍΪ `DefaultListableBeanFactory` µÄ `beanFactory` +2. ´´½¨ÁËÀàÐÍΪ `AnnotatedBeanDefinitionReader` µÄ reader£¬ÔÚÆä´´½¨µÄ¹ý³ÌÖУ¬»áÍù `beanFactory` Ìí¼Ó annotation Ïà¹ØµÄ´¦ÀíÆ÷ +3. ´´½¨ÁËÀàÐÍΪ `ClassPathBeanDefinitionScanner` µÄ `scanner` + +±¾ÎÄÁ÷³ÌͼʾÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b7a7a01b4d38769419a0e25e8f60037cbb5.png) + +±¾ÎľÍÏȵ½ÕâÀïÁË£¬½ÓÏÂÀ´µÄÎÄÕÂÎÒÃǼÌÐø·ÖÎöºóÐøµÄ´úÂë¡£ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" new file mode 100644 index 0000000..1aea867 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\344\272\224\357\274\211\357\274\232\346\211\247\350\241\214BeanFactoryPostProcessor.md" @@ -0,0 +1,495 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c8c446fca94a382109bdc7b6babd6db4c0d.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö¡£ + +### 4\. À©Õ¹µã£º`postProcessBeanFactory(beanFactory)` + +Õâ¸öÀàÊÇ spring ÌṩµÄÀ©Õ¹µã£¬±¾Éí²¢ÎÞÈκι¦ÄÜ£¬Áô´ý×ÓÀàʵÏÖ£¬`AbstractApplicationContext` µÄ `postProcessBeanFactory` ·½·¨´úÂëÈçÏ£º + +``` + protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + } + +``` + +µ±Ç°ÎÒÃÇʹÓÃµÄ `ApplicationContext` ÊÇ `AnnotationConfigApplicationContext`£¬²¢Ã»ÓÐʵÏÖÕâ¸ö·½·¨¡£ + +### 5\. Ö´ÐÐ `BeanFactoryPostProcessors`: `invokeBeanFactoryPostProcessors(beanFactory)` + +> `BeanFactoryPostProcessor` ³ÆÎª beanFactory µÄºóÖô¦Àí£¬ÓÃÀ´ÐÞ¸Ä beanFactory µÄһЩÐÐΪ¡£¹ØÓÚ `BeanFactoryPostProcessor` µÄÏêϸ·ÖÎö£¬¿ÉÒԲο¼ [spring ×é¼þÖ® BeanFactoryPostProcessors](https://my.oschina.net/funcy/blog/4597545)¡£ + +¹ØÓÚ `BeanFactoryPostProcessor`£¬ÕâÀïÌἸµã£º + +* `BeanFactoryPostProcessor` ·ÖΪÁ½ÖÖ£º`BeanFactoryPostProcessor` Óë `BeanDefinitionRegistryPostProcessor` +* `BeanDefinitionRegistryPostProcessor` ÊÇ `BeanFactoryPostProcessor` µÄ×ÓÀà +* ÏÈÖ´ÐÐ `BeanDefinitionRegistryPostProcessor` µÄ·½·¨£¬ÔÙÖ´ÐÐ `BeanFactoryPostProcessor` µÄ·½·¨ + +Á˽âÕâЩºó£¬ÎÒÃǸú½ø´úÂ룬ÕâÀï¶Ô²»ÖØÒª´úÂëÒÀ¾ÉÖ»¸ø³öµ÷ÓÃÁ´£¬ÈçÏ£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + +``` + +ÎÒÃÇÖ±½Ó¿´ `invokeBeanFactoryPostProcessors` ·½·¨£º + +``` +public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, + List beanFactoryPostProcessors) { + + // ËùÓдæÔÚµÄBeanDefinitionRegistryPostProcessorµÄÃû×Ö + Set processedBeans = new HashSet<>(); + + //beanFactoryÊÇDefaultListableBeanFactory£¬ÊÇBeanDefinitionRegistryµÄʵÏÖÀ࣬ËùÒԿ϶¨Âú×ãif + if (beanFactory instanceof BeanDefinitionRegistry) { + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + + //regularPostProcessors ÓÃÀ´´æ·ÅBeanFactoryPostProcessor + List regularPostProcessors = new ArrayList<>(); + + //registryProcessors ÓÃÀ´´æ·ÅBeanDefinitionRegistryPostProcessor + //BeanDefinitionRegistryPostProcessor À©Õ¹ÁË BeanFactoryPostProcessor + List registryProcessors = new ArrayList<>(); + + // Ñ­»·´«½øÀ´µÄbeanFactoryPostProcessors£¬Õý³£Çé¿öÏ£¬beanFactoryPostProcessors¿Ï¶¨Ã»ÓÐÊý¾Ý + // ÒòΪbeanFactoryPostProcessorsÊÇ»ñµÃÊÖ¶¯Ìí¼ÓµÄ£¬¶ø²»ÊÇspringɨÃèµÄ + // Ö»ÓÐÊÖ¶¯µ÷ÓÃannotationConfigApplicationContext.addBeanFactoryPostProcessor(XXX)²Å»áÓÐÊý¾Ý + for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { + // ÅжÏpostProcessorÊDz»ÊÇBeanDefinitionRegistryPostProcessor£¬ + // ÒòΪBeanDefinitionRegistryPostProcessor À©Õ¹ÁËBeanFactoryPostProcessor£¬ + // ËùÒÔÕâÀïÏÈÒªÅжÏÊDz»ÊÇBeanDefinitionRegistryPostProcessor, Êǵϰ£¬Ö±½ÓÖ´ÐÐ + // postProcessBeanDefinitionRegistry·½·¨£¬È»ºó°Ñ¶ÔÏó×°µ½registryProcessorsÀïÃæÈ¥ + if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { + BeanDefinitionRegistryPostProcessor registryProcessor = + (BeanDefinitionRegistryPostProcessor) postProcessor; + registryProcessor.postProcessBeanDefinitionRegistry(registry); + registryProcessors.add(registryProcessor); + } + else { + //²»Êǵϰ£¬¾Í×°µ½regularPostProcessors + regularPostProcessors.add(postProcessor); + } + } + + // Ò»¸öÁÙʱ±äÁ¿£¬ÓÃÀ´×°ÔØBeanDefinitionRegistryPostProcessor + // BeanDefinitionRegistry¼Ì³ÐÁËPostProcessorBeanFactoryPostProcessor + List currentRegistryProcessors = new ArrayList<>(); + + // »ñµÃʵÏÖBeanDefinitionRegistryPostProcessor½Ó¿ÚµÄÀàµÄBeanName + // ÕâÀï°üº¬ÁËspringÄÚ²¿ÌṩµÄBeanDefinitionRegistryPostProcessor + // ÒÔ¼°¿ª·¢Õß×Ô¼ºÊµÏÖµÄBeanDefinitionRegistryPostProcessor + String[] postProcessorNames = + beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { + //»ñµÃConfigurationClassPostProcessorÀ࣬²¢Çҷŵ½currentRegistryProcessors + //ConfigurationClassPostProcessorÊǺÜÖØÒªµÄÒ»¸öÀ࣬ËüʵÏÖÁË + //BeanDefinitionRegistryPostProcessor½Ó¿Ú£¬ + //BeanDefinitionRegistryPostProcessor½Ó¿ÚÓÖʵÏÖÁËBeanFactoryPostProcessor½Ó¿Ú + //ConfigurationClassPostProcessorÊǼ«ÆäÖØÒªµÄÀà, ÀïÃæÖ´ÐÐÁË + //ɨÃè @Bean£¬@Import£¬@ImportResource µÈ¸÷ÖÖ²Ù×÷ + currentRegistryProcessors.add(beanFactory.getBean( + ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + } + } + + //´¦ÀíÅÅÐò + sortPostProcessors(currentRegistryProcessors, beanFactory); + + //ºÏ²¢Processors£¬ÎªÊ²Ã´ÒªºÏ²¢£¬ÒòΪregistryProcessorsÊÇ + //×°ÔØBeanDefinitionRegistryPostProcessorµÄ + //Ò»¿ªÊ¼µÄʱºò£¬springÖ»»áÖ´ÐÐBeanDefinitionRegistryPostProcessor¶ÀÓеķ½·¨ + //¶ø²»»áÖ´ÐÐBeanDefinitionRegistryPostProcessor¸¸ÀàµÄ·½·¨£¬¼´BeanFactoryProcessorµÄ·½·¨ + //ËùÒÔÕâÀïÐèÒª°Ñ´¦ÀíÆ÷·ÅÈëÒ»¸ö¼¯ºÏÖУ¬ºóÐøÍ³Ò»Ö´Ðи¸ÀàµÄ·½·¨ + registryProcessors.addAll(currentRegistryProcessors); + + //¿ÉÒÔÀí½âΪִÐÐConfigurationClassPostProcessorµÄpostProcessBeanDefinitionRegistry·½·¨ + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); + //ÒòΪcurrentRegistryProcessorsÊÇÒ»¸öÁÙʱ±äÁ¿£¬ËùÒÔÐèÒªÇå³ý + currentRegistryProcessors.clear(); + + // Ôٴθù¾ÝBeanDefinitionRegistryPostProcessor»ñµÃBeanName£¬ + // ¿´Õâ¸öBeanNameÊÇ·ñÒѾ­±»Ö´ÐйýÁË£¬ÓÐûÓÐʵÏÖOrdered½Ó¿Ú + // Èç¹ûûÓб»Ö´Ðйý£¬Ò²ÊµÏÖÁËOrdered½Ó¿ÚµÄ»°£¬°Ñ¶ÔÏóÍÆË͵½currentRegistryProcessors£¬ + // Ãû³ÆÍÆË͵½processedBeans + // Èç¹ûûÓÐʵÏÖOrdered½Ó¿ÚµÄ»°£¬ÕâÀï²»°ÑÊý¾Ý¼Óµ½currentRegistryProcessors£¬ + // processedBeansÖУ¬ºóÐøÔÙ×ö´¦Àí + // ÕâÀï²Å¿ÉÒÔ»ñµÃÎÒÃǶ¨ÒåµÄʵÏÖÁËBeanDefinitionRegistryPostProcessorµÄBean + postProcessorNames = beanFactory.getBeanNamesForType( + BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { + currentRegistryProcessors.add(beanFactory.getBean( + ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + } + } + //´¦ÀíÅÅÐò + sortPostProcessors(currentRegistryProcessors, beanFactory); + //ºÏ²¢Processors + registryProcessors.addAll(currentRegistryProcessors); + //Ö´ÐÐÎÒÃÇ×Ô¶¨ÒåµÄBeanDefinitionRegistryPostProcessor + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); + //Çå¿ÕÁÙʱ±äÁ¿ + currentRegistryProcessors.clear(); + // ÉÏÃæµÄ´úÂëÊÇÖ´ÐÐÁËʵÏÖÁËOrdered½Ó¿ÚµÄBeanDefinitionRegistryPostProcessor£¬ + // ÏÂÃæµÄ´úÂë¾ÍÊÇÖ´ÐÐûÓÐʵÏÖOrdered½Ó¿ÚµÄBeanDefinitionRegistryPostProcessor + boolean reiterate = true; + while (reiterate) { + reiterate = false; + postProcessorNames = beanFactory.getBeanNamesForType( + BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (!processedBeans.contains(ppName)) { + currentRegistryProcessors.add(beanFactory.getBean( + ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + reiterate = true; + } + } + sortPostProcessors(currentRegistryProcessors, beanFactory); + registryProcessors.addAll(currentRegistryProcessors); + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); + currentRegistryProcessors.clear(); + } + + // registryProcessors¼¯ºÏ×°ÔØBeanDefinitionRegistryPostProcessor + // ÉÏÃæµÄ´úÂëÊÇÖ´ÐÐ×ÓÀà¶ÀÓеķ½·¨£¬ÕâÀïÐèÒªÔٰѸ¸ÀàµÄ·½·¨Ò²Ö´ÐÐÒ»´Î + invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); + invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); + } + + else { + //regularPostProcessors×°ÔØBeanFactoryPostProcessor£¬Ö´ÐÐBeanFactoryPostProcessorµÄ·½·¨ + //µ«ÊÇregularPostProcessorsÒ»°ãÇé¿öÏ£¬ÊDz»»áÓÐÊý¾ÝµÄ£¬ + //Ö»ÓÐÔÚÍâÃæÊÖ¶¯Ìí¼ÓBeanFactoryPostProcessor£¬²Å»áÓÐÊý¾Ý + invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); + } + + String[] postProcessorNames = + beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); + + List priorityOrderedPostProcessors = new ArrayList<>(); + List orderedPostProcessorNames = new ArrayList<>(); + List nonOrderedPostProcessorNames = new ArrayList<>(); + //Ñ­»·BeanNameÊý×é + for (String ppName : postProcessorNames) { + //Èç¹ûÕâ¸öBean±»Ö´ÐйýÁË£¬Ìø¹ý + if (processedBeans.contains(ppName)) { + + } + //Èç¹ûʵÏÖÁËPriorityOrdered½Ó¿Ú£¬¼ÓÈëµ½priorityOrderedPostProcessors + else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { + priorityOrderedPostProcessors.add(beanFactory + .getBean(ppName, BeanFactoryPostProcessor.class)); + } + //Èç¹ûʵÏÖÁËOrdered½Ó¿Ú£¬¼ÓÈëµ½orderedPostProcessorNames + else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { + orderedPostProcessorNames.add(ppName); + } + //Èç¹û¼ÈûÓÐʵÏÖPriorityOrdered£¬Ò²Ã»ÓÐʵÏÖOrdered¡£¼ÓÈëµ½nonOrderedPostProcessorNames + else { + nonOrderedPostProcessorNames.add(ppName); + } + } + + // ÅÅÐò´¦ÀípriorityOrderedPostProcessors£¬¼´ÊµÏÖÁËPriorityOrdered½Ó¿ÚµÄBeanFactoryPostProcessor + sortPostProcessors(priorityOrderedPostProcessors, beanFactory); + + // Ö´ÐÐpriorityOrderedPostProcessors + invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); + + // Ö´ÐÐʵÏÖÁËOrdered½Ó¿ÚµÄBeanFactoryPostProcessor + List orderedPostProcessors + = new ArrayList<>(orderedPostProcessorNames.size()); + for (String postProcessorName : orderedPostProcessorNames) { + orderedPostProcessors.add(beanFactory.getBean( + postProcessorName, BeanFactoryPostProcessor.class)); + } + sortPostProcessors(orderedPostProcessors, beanFactory); + invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); + + // Ö´ÐмÈûÓÐʵÏÖPriorityOrdered½Ó¿Ú£¬Ò²Ã»ÓÐʵÏÖOrdered½Ó¿ÚµÄBeanFactoryPostProcessor + List nonOrderedPostProcessors + = new ArrayList<>(nonOrderedPostProcessorNames.size()); + for (String postProcessorName : nonOrderedPostProcessorNames) { + nonOrderedPostProcessors.add(beanFactory.getBean( + postProcessorName, BeanFactoryPostProcessor.class)); + } + invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); + + beanFactory.clearMetadataCache(); +} + +``` + +Õâ¸ö·½·¨·Ç³£·Ç³£³¤£¬µ«Àí½âÆðÀ´²¢²»ÄÑ£¬Ö´ÐÐÆðÀ´×ñÑ­Õ⼸Ìõ¹æÔò£º + +* ÏÈÖ´ÐÐ `BeanDefinitionRegistryPostProcessor`£¬ÔÙÖ´ÐÐ `BeanFactoryPostProcessor` +* Ö´ÐÐ `BeanDefinitionRegistryPostProcessor` µÄ˳ÐòÈçÏ£º + 1. Ö´ÐвÎÊý´«ÈëµÄ `BeanDefinitionRegistryPostProcessor` + 2. Ö´ÐÐ spring ÄÚ²¿ÌṩµÄ£¬ÕâÀï»áÖ´ÐÐÒ»¸ö·Ç³£ÖØÒªµÄ `BeanDefinitionRegistryPostProcessor`¡ª¡ª`ConfigurationClassPostProcessor`£¬Ëü»á´¦ÀíÏîÄ¿ÖÐµÄ `@ComponentScan`¡¢`@Component`¡¢`@Import`¡¢`@Bean` µÈ×¢½â£¬¼ÓÔØÓû§×Ô¶¨ÒåµÄ `BeanDefinitionRegistryPostProcessor`¡¢`BeanFactoryPostProcessor` + 3. Ö´ÐÐÊ£Ï嵀 `BeanDefinitionRegistryPostProcessor`£¬Ò²¾ÍÊÇÔÚÉÏÒ»²½ÖмÓÔØµ½µÄ `BeanDefinitionRegistryPostProcessor` +* Ö´ÐÐ `BeanFactoryPostProcessor` µÄ˳ÐòÈçÏ£º + 1. Ö´ÐÐʵÏÖÁË `PriorityOrdered` ½Ó¿ÚµÄ `BeanFactoryPostProcessor` + 2. Ö´ÐÐʵÏÖÁË `Ordered` ½Ó¿ÚµÄ `BeanFactoryPostProcessor` + 3. Ö´ÐÐÊ£Ï嵀 `BeanFactoryPostProcessor` +* `BeanDefinitionRegistryPostProcessor` ÊÇ `BeanFactoryPostProcessor` µÄ×ÓÀ࣬ͬÑùÒªÖ´ÐÐ `BeanFactoryPostProcessor` µÄ·½·¨ + +ÒÔÉÏ˵µÄÖ´ÐÐ `BeanDefinitionRegistryPostProcessor`£¬ÊÇÖ¸Ö´ÐÐ `PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`£¬Ö´ÐÐ `BeanFactoryPostProcessor` ÊÇÖ¸Ö´ÐÐ `BeanFactoryPostProcessor#postProcessBeanFactory` + +Àí½âÒÔÉÏÄÚÈݺ󣬽ÓÏÂÀ´¾ÍÊǶÔÒÔÉÏ·½·¨µÄÏêϸ½âÊÍÁË£º + +1. Ö´ÐÐ `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry`: + 1. Ö´Ðпª·¢ÕßÊÖ¶¯µ÷Óà `applicationContext.addBeanFactoryPostProcessor` Ìí¼ÓµÄ `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanDefinitionRegistry` ·½·¨£¨Ò»°ãÇé¿öÏ£¬¿ª·¢Õß²»»áÊÖ¶¯µ÷Óø÷½·¨£©; + 2. Ö´ÐÐ spring ÄÚ²¿ÌṩµÄ¡¢ÊµÏÖÁË `PriorityOrdered` ½Ó¿ÚµÄ `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanDefinitionRegistry` ·½·¨£» + 3. Ö´ÐÐʵÏÖÁË `Ordered` ½Ó¿ÚÇÒδִÐйýµÄ `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanDefinitionRegistry` ·½·¨£» + 4. Ö´ÐÐÒÔÉÏδִÐйýµÄ `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanDefinitionRegistry` ·½·¨£» +2. Ö´ÐÐ `BeanFactoryPostProcessor#postProcessBeanFactory` ·½·¨£º + 1. Ö´Ðпª·¢Õßµ÷Óà `applicationContext.addBeanFactoryPostProcessor` Ìí¼ÓµÄ `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanFactory` ·½·¨£¨Ò»°ãÇé¿öÏ£¬¿ª·¢Õß²»»áÊÖ¶¯µ÷Óø÷½·¨£©; + 2. Ö´ÐÐÒÔÉÏ´ÓδִÐйýµÄ¡¢ÊµÏÖÁË `PriorityOrdered` µÄ `BeanFactoryPostProcessor` µÄ `postProcessBeanFactory` ·½·¨£» + 3. Ö´ÐÐÒÔÉÏ´ÓδִÐйýµÄ¡¢ÊµÏÖÁË `Ordered` µÄ `BeanFactoryPostProcessor` µÄ `postProcessBeanFactory` ·½·¨£» + 4. Ö´ÐÐÒÔÉÏ´ÓδִÐйýµÄ `BeanFactoryPostProcessor` µÄ `postProcessBeanFactory` ·½·¨ + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½·¨Êµ¼Ê¾ÍÊÇΪÁËÖ´ÐÐÁ½¸ö·½·¨£º`BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` Óë `BeanFactoryPostProcessor#postProcessBeanFactory`¡£ÐèҪעÒâµÄÊÇ£¬`BeanDefinitionRegistryPostProcessor` ÊÇ `BeanFactoryPostProcessor` µÄ×ÓÀ࣬ÔÚµ÷Óà `BeanFactoryPostProcessor#postProcessBeanFactory` ʱ £¬Êµ¼ÊÉÏÒ²µ÷ÓÃÁË `BeanDefinitionRegistryPostProcessor` µÄ `postProcessBeanFactory` ·½·¨. + +ÔÚÒÔÉ쵀 `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` Óë `BeanFactoryPostProcessor#postProcessBeanFactory` Ö´ÐÐÖУ¬¾¿¾¹Ö´ÐÐÁËÄÄЩ´úÂëÄØ£¿ÕâÀïÎÒÃÇͨ¹ýµ÷ÊÔ `demo01`£¬·¢ÏÖÖ´ÐеĴúÂëÈçÏ£º + +1. ÔÚ `1.2` ²½Öèʱ£¬Ö´ÐÐÁË `ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry` +2. ÔÚ `1.4` ²½Öèʱ£¬Ö´ÐÐÁË `ConfigurationClassPostProcessor#postProcessBeanFactory` +3. ÔÚ `2.4` ²½Öèʱ£¬Ö´ÐÐÁË `EventListenerMethodProcessor#postProcessBeanFactory` + +½Ó×Å£¬ÎÒÃDZã¶ÔÒÔÉÏÈý¸ö·½·¨½øÐÐÕ¹¿ª£¬¿´¿´¾¿¾¹×öÁËʲô¡£ + +#### 5.1 `ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry` µÄÖ´ÐÐÁ÷³Ì + +ÎÒÃÇÒ»¸úÏÂÈ¥£¬¶Ô²»ÖØÒªµÄ·½·¨Ö»ÏÔʾµ÷ÓÃÕ»£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors + |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry + |-ConfigurationClassPostProcessor#processConfigBeanDefinitions + +``` + +ÎÒÃÇÖ±½Ó½øÈë `ConfigurationClassPostProcessor#processConfigBeanDefinitions`£º + +``` +public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { + List configCandidates = new ArrayList<>(); + String[] candidateNames = registry.getBeanDefinitionNames(); + + //Ñ­»·candidateNamesÊý×é + for (String beanName : candidateNames) { + BeanDefinition beanDef = registry.getBeanDefinition(beanName); + + // ÄÚ²¿ÓÐÁ½¸ö±ê¼ÇλÀ´±ê¼ÇÊÇ·ñÒѾ­´¦Àí¹ýÁË + // ÕâÀï»áÒý·¢Ò»Á¬´®ÖªÊ¶Ã¤µã + // µ±ÎÒÃÇ×¢²áÅäÖÃÀàµÄʱºò£¬¿ÉÒÔ²»¼ÓConfiguration×¢½â£¬ + // Ö±½ÓʹÓÃComponent ComponentScan Import ImportResource×¢½â£¬³ÆÖ®ÎªLiteÅäÖÃÀà + // Èç¹û¼ÓÁËConfiguration×¢½â£¬¾Í³ÆÖ®ÎªFullÅäÖÃÀà + // Èç¹ûÎÒÃÇ×¢²áÁËLiteÅäÖÃÀ࣬ÎÒÃÇgetBeanÕâ¸öÅäÖÃÀ࣬»á·¢ÏÖËü¾ÍÊÇÔ­±¾µÄÄǸöÅäÖÃÀà + // Èç¹ûÎÒÃÇ×¢²áÁËFullÅäÖÃÀ࣬ÎÒÃÇgetBeanÕâ¸öÅäÖÃÀ࣬»á·¢ÏÖËüÒѾ­²»ÊÇÔ­±¾ÄǸöÅäÖÃÀàÁË£¬ + // ¶øÊÇÒѾ­±»cgilb´úÀíµÄÀàÁË + if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { + if (logger.isDebugEnabled()) { + logger.debug(...); + } + } + // ÅжÏÊÇ·ñΪÅäÖÃÀ࣬ÕâÀï·ÖÁ½ÖÖÇé¿ö£º + // 1\. ´øÓÐ @Configuration ×¢½â ÇÒ proxyBeanMethods != false µÄÀ࣬spring ³ÆÆäΪ Full ÅäÖÃÀà + // 2\. ´øÓÐ @Configuration ×¢½â ÇÒ proxyBeanMethods == false, + // »ò ´øÓÐ @Component¡¢@ComponentScan¡¢@Import¡¢@ImportResource¡¢ + // @Bean ÆäÖÐÖ®Ò»×¢½âµÄÀ࣬spring ³ÆÆäΪ Lite ÅäÖÃÀà + // FullÓëLite£¬beanDef»á½øÐбêʶ + else if (ConfigurationClassUtils + .checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { + configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); + } + } + + // Èç¹ûûÓÐÅäÖÃÀֱ࣬½Ó·µ»Ø + if (configCandidates.isEmpty()) { + return; + } + + // ´¦ÀíÅÅÐò + configCandidates.sort((bd1, bd2) -> { + int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); + int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); + return Integer.compare(i1, i2); + }); + + SingletonBeanRegistry sbr = null; + // DefaultListableBeanFactory×îÖÕ»áʵÏÖSingletonBeanRegistry½Ó¿Ú£¬ËùÒÔ¿ÉÒÔ½øÈëµ½Õâ¸öif + if (registry instanceof SingletonBeanRegistry) { + sbr = (SingletonBeanRegistry) registry; + if (!this.localBeanNameGeneratorSet) { + //springÖпÉÒÔÐÞ¸ÄĬÈϵÄbeanÃüÃû·½Ê½£¬ÕâÀï¾ÍÊÇ¿´Óû§ÓÐûÓÐ×Ô¶¨ÒåbeanÃüÃû·½Ê½ + BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( + AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); + if (generator != null) { + this.componentScanBeanNameGenerator = generator; + this.importBeanNameGenerator = generator; + } + } + } + + if (this.environment == null) { + this.environment = new StandardEnvironment(); + } + + ConfigurationClassParser parser = new ConfigurationClassParser( + this.metadataReaderFactory, this.problemReporter, this.environment, + this.resourceLoader, this.componentScanBeanNameGenerator, registry); + + Set candidates = new LinkedHashSet<>(configCandidates); + Set alreadyParsed = new HashSet<>(configCandidates.size()); + do { + //½âÎöÅäÖÃÀ࣬Õâ¸öÀà×öÁ˺ܶàÊ£¬ + //È磺¶Ô@Component£¬@PropertySources£¬@ComponentScans£¬@ImportResourceµÈµÄ´¦Àí + parser.parse(candidates); + parser.validate(); + + Set configClasses + = new LinkedHashSet<>(parser.getConfigurationClasses()); + configClasses.removeAll(alreadyParsed); + + if (this.reader == null) { + this.reader = new ConfigurationClassBeanDefinitionReader( + registry, this.sourceExtractor, this.resourceLoader, this.environment, + this.importBeanNameGenerator, parser.getImportRegistry()); + } + // Ö±µ½ÕâÒ»²½²Å°ÑImportµÄÀ࣬@Bean @ImportRosource ת»»³ÉBeanDefinition + this.reader.loadBeanDefinitions(configClasses); + // °ÑconfigClasses¼ÓÈëµ½alreadyParsed + alreadyParsed.addAll(configClasses); + + candidates.clear(); + // »ñµÃ×¢²áÆ÷ÀïÃæBeanDefinitionµÄÊýÁ¿ ºÍ candidateNames½øÐÐ±È½Ï + // Èç¹û´óÓڵϰ£¬ËµÃ÷ÓÐеÄBeanDefinition×¢²á½øÀ´ÁË + if (registry.getBeanDefinitionCount() > candidateNames.length) { + String[] newCandidateNames = registry.getBeanDefinitionNames(); + Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); + Set alreadyParsedClasses = new HashSet<>(); + // Ñ­»·alreadyParsed¡£°ÑÀàÃû¼ÓÈëµ½alreadyParsedClasses + for (ConfigurationClass configurationClass : alreadyParsed) { + alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); + } + for (String candidateName : newCandidateNames) { + if (!oldCandidateNames.contains(candidateName)) { + BeanDefinition bd = registry.getBeanDefinition(candidateName); + if (ConfigurationClassUtils + .checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && + !alreadyParsedClasses.contains(bd.getBeanClassName())) { + candidates.add(new BeanDefinitionHolder(bd, candidateName)); + } + } + } + candidateNames = newCandidateNames; + } + } + while (!candidates.isEmpty()); + + if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { + sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); + } + + if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { + ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); + } +} + +``` + +ÒÔÉÏ·½·¨»¹ÊÇ¶Ô `BeanDefinition` ÐÅÏ¢µÄ½øÒ»²½ÍêÉÆ£¬°üÀ¨¶Ô `@Configuration`¡¢`@PropertySources`¡¢`@ComponentScans`¡¢`@ImportResource` µÈµÄ´¦Àí¡£ÓÉÓÚ demo01 ûÓÐÕâЩע½â£¬ÕâÀïÎÒÃǾͲ»Õ¹¿ªÁË£¬ºóÃæÎÒÃÇÔÙ·ÖÎö¡£ + +#### 5.2 Ö´ÐÐ `ConfigurationClassPostProcessor#postProcessBeanFactory` µÄÁ÷³Ì + +`ConfigurationClassPostProcessor` µÄ `postProcessBeanFactory` ·½·¨±È½Ï¼òµ¥£¬Ëù×öµÄÊ»¹ÊÇ¶Ô `@Configuration` µÄÔöÇ¿£º + +``` +@Override +public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // Ê¡ÂÔÆäËû´úÂë + + // ¶Ô ConfigurationClasses µÄÔöÇ¿ + enhanceConfigurationClasses(beanFactory); + beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); +} + +public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { + // Ê¡ÂÔÆäËû´úÂë + + // È«ÅäÖÃÀࣺ´¦Àí´úÀí + ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); + for (Map.Entry entry : configBeanDefs.entrySet()) { + AbstractBeanDefinition beanDef = entry.getValue(); + // If a @Configuration class gets proxied, always proxy the target class + beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); + // Set enhanced subclass of the user-specified bean class + // ÔÚÕâÀï½øÐÐÔöÇ¿µÄ + Class configClass = beanDef.getBeanClass(); + Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); + if (configClass != enhancedClass) { + beanDef.setBeanClass(enhancedClass); + } + } +} + +``` + +ÓÉÓÚ demo01 ûÓÐÅäÖà `@Configuration`£¬ÕâÀï¾Í²»Õ¹¿ªÁË£¬ºóÃæÎÒÃÇÔÙ·ÖÎö¡£ + +#### 5.3 Ö´ÐÐ `EventListenerMethodProcessor#postProcessBeanFactory` µÄÁ÷³Ì + +Õâ¸ö·½·¨ÊÇÓÃÀ´´¦Àíʼþ¼àÌýÆ÷µÄ£¬ÎÒÃÇÖ±½ÓÉÏ´úÂ룺 + +``` +@Override +public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + Map beans + = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); + + List factories = new ArrayList<>(beans.values()); + AnnotationAwareOrderComparator.sort(factories); + this.eventListenerFactories = factories; +} + +``` + +¿ÉÒÔ¿´µ½£¬ÕâÀï´Ó spring ÈÝÆ÷ÖУ¬ÄóöÁËËùÓÐµÄ `EventListenerFactory`£¬È»ºó¸³Öµ¸ø `this.eventListenerFactories`£¬Õâ¸ö¾Í²»Õ¹¿ªÁË¡£ + +#### 5.4 ×ܽá + +±¾ÎĽéÉÜÁË `invokeBeanFactoryPostProcessors` µÄÖ´ÐÐÁ÷³Ì£¬Õû¸öÀàÏÂÀ´¾ÍÊÇΪÁËÖ´ÐÐÁ½¸ö·½·¨£º`BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` Óë `BeanFactoryPostProcessor#postProcessBeanFactory`£¬ÕâÁ½¸ö·½·¨µÄÖ´Ðйý³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b6188601112b2b2c031b0a70f64f2cc885f.png) + +ͨ¹ýµ÷ÊÔ·¢ÏÖ£¬`invokeBeanFactoryPostProcessors` Ò»¹²Ö´ÐÐÁË `BeanFactoryPostProcessor`£º + +1. `ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry` +2. `ConfigurationClassPostProcessor#postProcessBeanFactory` +3. `EventListenerMethodProcessor#postProcessBeanFactory` + +ÆäÖУ¬`ConfigurationClassPostProcessor` ÊÇÒ»¸ö·Ç³£·Ç³£ÖØÒªµÄ `BeanFactoryPostProcessor`£¬¹ØÓÚËüµÄ½øÒ»²½·ÖÎö¿ÉÒԲο¼ÒÔÏÂÎÄÕ£º + +* [spring Ì½ÃØÖ® ConfigurationClassPostProcessor Ö®´¦Àí @ComponentScan ×¢½â](https://my.oschina.net/funcy/blog/4836178) +* [spring Ì½ÃØÖ® ConfigurationClassPostProcessor Ö®´¦Àí @Bean ×¢½â](https://my.oschina.net/funcy/blog/4492878) +* [spring Ì½ÃØÖ® ConfigurationClassPostProcessor Ö®´¦Àí @Import ×¢½â](https://my.oschina.net/funcy/blog/4678152) +* [spring Ì½ÃØÖ® ConfigurationClassPostProcessor Ö®´¦Àí @Conditional ×¢½â](https://my.oschina.net/funcy/blog/4873444) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4641114](https://my.oschina.net/funcy/blog/4641114) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" new file mode 100644 index 0000000..a7372b9 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\253\357\274\211\357\274\232\345\256\214\346\210\220BeanFactory\347\232\204\345\210\235\345\247\213\345\214\226.md" @@ -0,0 +1,377 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1f1ac8f3d8241fad9693d9684048ab7f3ae.png) + +½ÓÉÏÎÄ£¬±¾ÎļÌÐø·ÖÎö spring µÄÆô¶¯ Á÷³Ì¡£ + +### 11\. ³õʼ»¯µ¥Àý bean: `finishBeanFactoryInitialization(beanFactory)` + +±¾ÎĽ«·ÖÎöÒ»¸ö**·Ç³£ÖØÒª**µÄ·½·¨ `AbstractApplicationContext#finishBeanFactoryInitialization` ÁË¡£ + +·½·¨µÄµ÷ÓÃÈçÏ£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-DefaultListableBeanFactory#preInstantiateSingletons + +``` + +ÎÒÃÇÖ±½Ó½øÈë `DefaultListableBeanFactory#preInstantiateSingletons`: + +``` +public void preInstantiateSingletons() throws BeansException { + // this.beanDefinitionNames ±£´æÁËËùÓÐµÄ beanNames + List beanNames = new ArrayList<>(this.beanDefinitionNames); + + for (String beanName : beanNames) { + // ºÏ²¢¸¸ Bean ÖеÄÅäÖã¬×¢Òâ ÖÐµÄ parentÊôÐÔ + RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); + // ²»ÊdzéÏóÀà¡¢Êǵ¥ÀýµÄÇÒ²»ÊÇÀÁ¼ÓÔØµÄ + if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { + // ´¦Àí FactoryBean + if (isFactoryBean(beanName)) { + //ÔÚ beanName Ç°Ãæ¼ÓÉÏ¡°&¡± ·ûºÅ + Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); + if (bean instanceof FactoryBean) { + final FactoryBean factory = (FactoryBean) bean; + // Åжϵ±Ç° FactoryBean ÊÇ·ñÊÇ SmartFactoryBean µÄʵÏÖ + boolean isEagerInit; + if (System.getSecurityManager() != null + && factory instanceof SmartFactoryBean) { + isEagerInit = AccessController.doPrivileged((PrivilegedAction) + ((SmartFactoryBean) factory)::isEagerInit, + getAccessControlContext()); + } + else { + isEagerInit = (factory instanceof SmartFactoryBean && + ((SmartFactoryBean) factory).isEagerInit()); + } + if (isEagerInit) { + // ²»ÊÇFactoryBeanµÄÖ±½ÓʹÓô˷½·¨½øÐгõʼ»¯ + getBean(beanName); + } + } + } + else { + getBean(beanName); + } + } + } + + // Trigger post-initialization callback for all applicable beans... + // Èç¹ûbeanʵÏÖÁË SmartInitializingSingleton ½Ó¿ÚµÄ£¬ÄÇôÔÚÕâÀïµÃµ½»Øµ÷ + for (String beanName : beanNames) { + Object singletonInstance = getSingleton(beanName); + if (singletonInstance instanceof SmartInitializingSingleton) { + final SmartInitializingSingleton smartSingleton = + (SmartInitializingSingleton) singletonInstance; + if (System.getSecurityManager() != null) { + AccessController.doPrivileged((PrivilegedAction) () -> { + smartSingleton.afterSingletonsInstantiated(); + return null; + }, getAccessControlContext()); + } + else { + smartSingleton.afterSingletonsInstantiated(); + } + } + } +} + +``` + +ÒÔÉÏ´úÂ룬¿´Ëƺܶ࣬µ«¹Ø¼ü´úÂë¿É¼ò»¯ÈçÏ£º + +``` +List beanNames = new ArrayList<>(this.beanDefinitionNames); +for (String beanName : beanNames) { + RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); + if(... && bd.isSingleton() && ...) { + getBean(beanName); + } +} + +``` + +> ÒÔÉÏ´úÂëµÄ¼ò»¯ÖУ¬Ê¡ÂÔÁËÐí¶àϸ½Ú£¬ÀýÈçÅжÏÊÇ·ñ¿ÉÒÔʵÀý»¯Ê±£¬ÐèÒªÅжÏÊÇ·ñΪ³éÏóÀ࣬ÊÇ·ñΪµ¥Àý£¬ÊÇ·ñΪÀÁ¼ÓÔØµÈ£¬Í¬Ê±¶Ô bean Ò²ÅжÏÁËÊÇ·ñΪÆÕͨ bean »¹ÊÇ `FactoryBean`£¬Èç¹ûʵÏÖÁË `SmartInitializingSingleton` ½Ó¿ÚµÄ bean£¬ÐèÒªÁíÍâ´¦ÀíµÈ¡£¶ÔÓÚÎÒÃdzõ´ÎÔĶÁ´úÂëÀ´Ëµ£¬°Ñ¾«Á¦¾Û¼¯Ö÷ÒªÁ÷³Ì¾ÍÐÐÁË£¬¶ÔÓÚÒ»Ð©ÌØÀý¼°Ï¸½Ú£¬¿ÉÒÔÏȲ»¾À½á£¬ÏëÁ˽â¸ü¶àϸ½Ú£¬¿ÉÒÔÔÚ°ÑÎÕÖ÷ÒªÁ÷³ÌµÄÇé¿öÏ£¬ºóÃæÔÙ¿´¡£¹ýÓÚ¾À½áÌØÀý¼°Ï¸½Ú£¬·´µ¹ÈÃ×Ô¼º×¥²»×¡Öص㣬ÃÔʧÓÚÔ´ÂëÖС£ + +ÕâÑùÒ»¼ò»¯£¬¾Í¿ÉÒÔÇåÎú¿´³ö¸Ã·½·¨µÄ¹¦ÄÜ£º + +1. »ñÈ¡ `beanFactory` ÖÐµÄ `beanNames` ²¢±éÀú£» +2. ͨ¹ý `beanName` »ñÈ¡ `BeanDefinition`£¬½øÐÐÌõ¼þÅжϣ¬ÈçÊÇ·ñΪµ¥Àý£» +3. ±éÀúµ÷Óà `getBean(beanName)` ´´½¨ bean ²¢½«ÆäÌí¼Óµ½ spring ÖС£ + +´Ó¼ò»¯ºóµÄ´úÂë¿ÉÒÔ¿´³ö£¬¿´ËÆÆ½Æ½ÎÞÆæµÄ `getBean(beanName)`£¬¾ÍÊÇ spring ʵÀý»¯ bean µÄ¹Ø¼ü¡£½ÓÏÂÀ´£¬ÎÒÃÇ»¹ÊǺöÂÔÆäËû´úÂ룬ֻ¹Ø×¢Ö÷ÒªÁ÷³Ì£¬¼ÌÐø·ÖÎöÏÂÈ¥£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#finishBeanFactoryInitialization + |-DefaultListableBeanFactory#preInstantiateSingletons + |-AbstractBeanFactory#getBean(java.lang.String) + |-AbstractBeanFactory#doGetBean + +``` + +> AbstractBeanFactory#doGetBean + +``` +protected T doGetBean(final String name, @Nullable final Class requiredType, + @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { + + // Ö÷ÒªÂß¼­¾ÍÊÇÈç¹ûÊÇFactoryBean¾Í°Ñ&È¥µô,Èç¹ûÊDZðÃû¾Í°Ñ¸ù¾Ý±ðÃû»ñÈ¡ÕæÊµÃû³Æ + final String beanName = transformedBeanName(name); + //×îºóµÄ·µ»ØÖµ + Object bean; + + // ¼ì²éÊÇ·ñÒѳõʼ»¯ + Object sharedInstance = getSingleton(beanName); + // Èç¹ûÒѾ­³õʼ»¯¹ýÁË£¬ÇÒûÓд«args²ÎÊý¾Í´ú±íÊÇget£¬Ö±½ÓÈ¡³ö·µ»Ø + if (sharedInstance != null && args == null) { + // ÕâÀïÈç¹ûÊÇÆÕͨBean µÄ»°£¬Ö±½Ó·µ»Ø£¬Èç¹ûÊÇ FactoryBean µÄ»°£¬·µ»ØËü´´½¨µÄÄǸöʵÀý¶ÔÏó + bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); + } + else { + // Èç¹û´æÔÚprototypeÀàÐ͵ÄÕâ¸öbean + if (isPrototypeCurrentlyInCreation(beanName)) { + throw new BeanCurrentlyInCreationException(beanName); + } + + // Èç¹ûµ±Ç°BeanDefinition²»´æÔÚÕâ¸öbeanÇÒ¾ßÓи¸BeanFactory + BeanFactory parentBeanFactory = getParentBeanFactory(); + if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { + String nameToLookup = originalBeanName(name); + if (parentBeanFactory instanceof AbstractBeanFactory) { + return ((AbstractBeanFactory) parentBeanFactory).doGetBean( + nameToLookup, requiredType, args, typeCheckOnly); + } + else if (args != null) {. + // ·µ»Ø¸¸ÈÝÆ÷µÄ²éѯ½á¹û + return (T) parentBeanFactory.getBean(nameToLookup, args); + } + else if (requiredType != null) { + return parentBeanFactory.getBean(nameToLookup, requiredType); + } + else { + return (T) parentBeanFactory.getBean(nameToLookup); + } + } + + if (!typeCheckOnly) { + // typeCheckOnly Ϊ false£¬½«µ±Ç° beanName ·ÅÈëÒ»¸ö alreadyCreated µÄ Set ¼¯ºÏÖС£ + markBeanAsCreated(beanName); + } + + // µ½Õâ¾ÍÒª´´½¨beanÁË + try { + final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + checkMergedBeanDefinition(mbd, beanName, args); + // Ïȳõʼ»¯ÒÀÀµµÄËùÓÐ Bean£¬ depends-on Öж¨ÒåµÄÒÀÀµ + String[] dependsOn = mbd.getDependsOn(); + if (dependsOn != null) { + for (String dep : dependsOn) { + // ¼ì²éÊDz»ÊÇÓÐÑ­»·ÒÀÀµ + if (isDependent(beanName, dep)) { + throw new BeanCreationException(...); + } + // ×¢²áÒ»ÏÂÒÀÀµ¹ØÏµ + registerDependentBean(dep, beanName); + try { + // Ïȳõʼ»¯±»ÒÀÀµÏî + getBean(dep); + } + catch (NoSuchBeanDefinitionException ex) { + throw new BeanCreationException(...); + } + } + } + + // Èç¹ûÊǵ¥ÀýµÄ + if (mbd.isSingleton()) { + sharedInstance = getSingleton(beanName, () -> { + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + destroySingleton(beanName); + throw ex; + } + }); + bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // Èç¹ûÊÇprototype + else if (mbd.isPrototype()) { + Object prototypeInstance = null; + try { + beforePrototypeCreation(beanName); + // Ö´Ðд´½¨ Bean + prototypeInstance = createBean(beanName, mbd, args); + } + finally { + afterPrototypeCreation(beanName); + } + bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); + } + // Èç¹û²»ÊÇ singleton ºÍ prototype, ÄÇô¾ÍÊÇ×Ô¶¨ÒåµÄscope(ÀýÈçWebÏîÄ¿ÖеÄsessionµÈÀàÐÍ)£¬ + // ÕâÀï¾Í½»¸ø×Ô¶¨ÒåscopeµÄÓ¦Ó÷½È¥ÊµÏÖ + else { + String scopeName = mbd.getScope(); + final Scope scope = this.scopes.get(scopeName); + if (scope == null) { + throw new IllegalStateException(...); + } + try { + Object scopedInstance = scope.get(beanName, () -> { + beforePrototypeCreation(beanName); + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + finally { + afterPrototypeCreation(beanName); + } + }); + bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); + } + catch (IllegalStateException ex) { + throw new BeanCreationException(...); + } + } + } + catch (BeansException ex) { + cleanupAfterBeanCreationFailure(beanName); + throw ex; + } + } + + //¼ì²ébeanµÄÀàÐÍ + if (requiredType != null && !requiredType.isInstance(bean)) { + try { + T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); + if (convertedBean == null) { + throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); + } + return convertedBean; + } + catch (TypeMismatchException ex) { + throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); + } + } + return (T) bean; +} + +``` + +ÉÏÃæµÄ´úÂë»ù±¾Éϸø³öÁË×¢ÊÍ£¬ ÔÚÕâ¾Í²»¶à×ö ½âÊÍÁË¡£spring ¹¦ÄܱȽϸ´ÔÓ£¬¿¼ÂǵĶ«Î÷Ò²±È½Ï¶à£¬Òò´ËÉÏÊö´úÂë»á¶à×ö¶àÖÖÅжϣ¬Ó¦¶Ô¶àÖÖÇé¿ö¡£Èç¹ûÎÒÃǽö¿¼ÂÇ demo01 µÄÇé¿ö (`singleton` Çé¿ö)£¬ÒÔÉÏ´úÂë¹Ø¼üÈçÏ£º + +``` +//×îºóµÄ·µ»ØÖµ +Object bean; + +// ÕâÀï½ö¸ø³öµ¥ÀýµÄÇé¿ö +// 1\. »ñÈ¡µ¥Àý¶ÔÏó£¬Í¬Ê±Ò²ÌṩÁËÒ»¸ölambda±í´ïʽ£¬ÓÃÀ´½øÐÐbeanµÄ´´½¨ +sharedInstance = getSingleton(beanName, () -> { + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + destroySingleton(beanName); + throw ex; + } +}); + +// 2\. ½øÒ»²½´¦ÀísharedInstance£¬È»ºó·µ»Øbean¡£ÊÂʵÉÏ£¬Õâ¸ö·½·¨Ö÷Òª´¦ÀíµÄÊÇ£¬ +// Èç¹ûÊÇFactoryBean£¬¾Í·µ»Ø·µ»ØËü´´½¨µÄÄǸöʵÀý¶ÔÏ󣬷ñÔò¾ÍÖ±½Ó·µ»Ø +bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + +return (T) bean; + +``` + +¾«¼òºóµÄ´úÂ룬¾ÍÊÇ spring ´´½¨ bean µÄÁ÷³Ì¡£½Ó×ÅÎÒÃǾͷֱ𿴿´ÕâÁ½¸ö·½·¨µÄÄÚÈÝ£¬ÕâÀïɾ³ýÁËһЩ²»±ØÒªµÄ´úÂ룺 + +> `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)` + +``` +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + synchronized (this.singletonObjects) { + boolean newSingleton = false; + try { + // ¶ÔÏó¾ÍÊÇÔÚÕâÀï½øÐд´½¨µÄ + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (... ex) { + ... + } + finally { + // ´´½¨Íê³Éºó£¬×öһЩÅжϲÙ×÷£¬Óë´´½¨¹ý³Ì¹ØÏµ²»´ó + afterSingletonCreation(beanName); + } + if (newSingleton) { + // Ìí¼Óµ½ beanFactory »º´æ + addSingleton(beanName, singletonObject); + } + return singletonObject; + } +} + +``` + +´ÓÒÔÉÏ´úÂë¿ÉÒÔ¿´³ö£¬bean µÄ´´½¨¾ÍÊÇÔÚ `singletonFactory.getObject()`£¬¹ØÓÚÕâ¸ö·½·¨Ö´ÐÐÁËʲô£¬ÎÒÃÇ»¹Ó¦¸Ã½áºÏ `AbstractBeanFactory#doGetBean`. + +Ê×ÏÈ£¬ÎÒÃǽøÈë `ObjectFactory#getObject`£¬·¢ÏÖ´úÂëÈçÏ£º + +``` +@FunctionalInterface +public interface ObjectFactory { + T getObject() throws BeansException; +} + +``` + +ÕâÊÇÒ»¸öº¯Êýʽ±à³Ì½Ó¿Ú£¬jdk8 ÌṩµÄÐÂÓï·¨¡£ÔÙ¿´¿´ `AbstractBeanFactory#doGetBean` ·½·¨´«ÈëµÄ¶ÔÏó£º + +``` +sharedInstance = getSingleton(beanName, () -> { + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + destroySingleton(beanName); + throw ex; + } +}); + +``` + +ÕâÀï´«ÈëµÄÊÇÒ»¸ö lambda ±í´ïʽ£¬µ±´úÂëÖ´ÐÐ `singletonFactory.getObject()` ʱ£¬Êµ¼ÊÉÏÖ´ÐеÄÊÇ + +``` +try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); +} +catch (BeansException ex) { + destroySingleton(beanName); + throw ex; +} + +``` + +¼´ `AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])`£¬Ç°ÃæÍäÍäÈÆÈÆÁËÄÇô¶à£¬spring ÖÕÓÚÒª½øÈë bean µÄ´´½¨ÁË£¡ + +¹ØÓÚ spring ¶ÔÏóµÄ´´½¨£¬ÎÒÃÇ»áÔÚºóÃæµÄÎÄÕ¼ÌÐø·ÖÎö£¬±¾ÎÄÎÒÃÇÖ»¹Ø×¢ `AbstractBeanFactory#getBean` Óë `DefaultSingletonBeanRegistry#getSingleton`£¬½áºÏÒÔÉÏ·ÖÎö£¬ÎÒÃǶÔÕâÁ½¸ö·½·¨×öÒ»¸ö×ܽ᣺ + +* `AbstractBeanFactory#getBean`£º¶ÔÓÚ scope Ϊ `PropertyType` µÄ bean À´Ëµ£¬¸Ã·½·¨»áÖ±½Ó´´½¨ bean£»¶ÔÓÚ scope Ϊ `singleton` µÄ bean À´Ëµ£¬¸Ã·½·¨»áÏÈÅÐ¶Ï `beanFactory` ÊÇ·ñ´æÔڸà bean£¬Èô´æÔÚÔòÖ±½Ó·µ»Ø£¬·ñÔò¾ÍÏÈ´´½¨ÔÙ·µ»Ø¡£ + +* `DefaultSingletonBeanRegistry#getSingleton`£ºÕâ¸ö·½·¨¾ÍÊÇ´Ó `beanFactory` »ñÈ¡ singleton bean µÄ·½·¨£ºÈô´æÔÚÔòÖ±½Ó·µ»Ø£¬·ñÔò¾ÍÏÈ´´½¨ÔÙ·µ»Ø¡£ + +±¾ÎľÍÏÈ·ÖÎöµ½ÕâÀïÁË£¬ÏÂÆªÎÄÕÂÎÒÃÇÔÙ·ÖÎö spring bean ´´½¨¹ý³Ì¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4658230](https://my.oschina.net/funcy/blog/4658230) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" new file mode 100644 index 0000000..ebdc18b --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\205\255\357\274\211\357\274\232\346\263\250\345\206\214BeanPostProcessor.md" @@ -0,0 +1,146 @@ +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-be7f7797a27a2dc5ab1ad8d11327b140c90.png) + +½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö¡£ + +### 6\. ×¢²á `BeanPostProcessor`: `registerBeanPostProcessors(beanFactory)` + +ÔÚÕýʽ·ÖÎöǰ£¬ÐèÒªÃ÷È·Á½¸ö¸ÅÄ + +* `BeanFactoryPostProcessor`£¬±»³ÆÎª `BeanFactory` µÄºóÖô¦ÀíÆ÷£¬¿ÉÒÔ¶Ô `BeanFactory` ½øÐÐһЩ²Ù×÷£» +* `BeanPostProcessor`£¬±»³ÆÎª `Bean` µÄºóÖô¦ÀíÆ÷£¬¿ÉÒÔ¶Ô `Bean` ½øÐÐһЩ²Ù×÷¡£ + +²»Òª½«±¾ÎÄµÄ `BeanPostProcessor` ÓëÇ°ÃæµÄ `BeanFactoryPostProcessor` Ū»ìÁË¡£ + +±¾ÎÄÖ÷ÒªÊÇ¶Ô `BeanPostProcessor` µÄ `register` ²Ù×÷ (`registerBeanPostProcessors(beanFactory)`)£¬¼´½« `BeanPostProcessor` ×¢²áµ½ `BeanFactory` ÖУ¬ÄÇôµ÷ÓÃÊÇÔÚʲôʱºòÄØ£¿¼ÈÈ»ÊÇ¶Ô `Bean` µÄ²Ù×÷£¬µ±È»ÊÇÓÐÁË bean Ö®ºóÔÙÔËÐÐÁË¡£ + +> `BeanPostProcessor` Ò²ÊÇ spring ÖÐÒ»¸öÖØÒª×é¼þ£¬¹ØÓÚ¸Ã×é¼þµÄÏêϸ·ÖÎö£¬¿ÉÒԲο¼ [spring ×é¼þÖ® BeanPostProcessors](https://my.oschina.net/funcy/blog/4597551) + +·Ï»°²»¶à˵£¬Ö±½ÓÉÏ´úÂ룬ͬÑùµØ£¬¶Ô²»ÖØÒªµÄ·½·¨ÎÒÃÇÖ»¸ø³öµ÷ÓÃÁ´£º + +``` +|-AbstractApplicationContext#refresh + |-AbstractApplicationContext#registerBeanPostProcessors + |-PostProcessorRegistrationDelegate + #registerBeanPostProcessors(ConfigurableListableBeanFactory, AbstractApplicationContext) + +``` + +×îÖÕµ÷Óõ½ÁË `PostProcessorRegistrationDelegate#registerBeanPostProcessors`£¬´úÂëÈçÏ£º + +``` +public static void registerBeanPostProcessors( + ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { + + // »ñÈ¡springÖÐËùÓÐµÄ BeanPostProcessor£¬ÕâÀï½öÓÐÒ»¸öbean: + // org.springframework.context.annotation.internalAutowiredAnnotationProcessor£¬ + // ¼´ AutowiredAnnotationBeanPostProcessor + String[] postProcessorNames = beanFactory + .getBeanNamesForType(BeanPostProcessor.class, true, false); + + int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + + 1 + postProcessorNames.length; + beanFactory.addBeanPostProcessor( + new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); + + List priorityOrderedPostProcessors = new ArrayList<>(); + List internalPostProcessors = new ArrayList<>(); + List orderedPostProcessorNames = new ArrayList<>(); + List nonOrderedPostProcessorNames = new ArrayList<>(); + + // ÏÈ»ñȡʵÏÖÁËPriorityOrderedµÄBeanPostProcessor + // ÔÙ»ñȡʵÏÖÁËOrderedµÄBeanPostProcessor + // ×îºóÔÙ»ñÈ¡²»Âú×ãÒÔÉÏÌõ¼þµÄBeanPostProcessor + for (String ppName : postProcessorNames) { + if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { + BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); + priorityOrderedPostProcessors.add(pp); + if (pp instanceof MergedBeanDefinitionPostProcessor) { + internalPostProcessors.add(pp); + } + } + else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { + orderedPostProcessorNames.add(ppName); + } + else { + nonOrderedPostProcessorNames.add(ppName); + } + } + + // ´¦ÀípriorityOrderedPostProcessor£ºÅÅÐò£¬È»ºóÌí¼Óµ½beanFactoryÖÐ + sortPostProcessors(priorityOrderedPostProcessors, beanFactory); + registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); + + List orderedPostProcessors + = new ArrayList<>(orderedPostProcessorNames.size()); + for (String ppName : orderedPostProcessorNames) { + BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); + orderedPostProcessors.add(pp); + if (pp instanceof MergedBeanDefinitionPostProcessor) { + internalPostProcessors.add(pp); + } + } + // ´¦ÀíorderedPostProcessor£ºÅÅÐò£¬È»ºóÌí¼Óµ½beanFactoryÖÐ + sortPostProcessors(orderedPostProcessors, beanFactory); + registerBeanPostProcessors(beanFactory, orderedPostProcessors); + + List nonOrderedPostProcessors + = new ArrayList<>(nonOrderedPostProcessorNames.size()); + for (String ppName : nonOrderedPostProcessorNames) { + BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); + nonOrderedPostProcessors.add(pp); + if (pp instanceof MergedBeanDefinitionPostProcessor) { + internalPostProcessors.add(pp); + } + } + // ´¦ÀíÓàϵÄBeanPostProcessor£ºÅÅÐò£¬È»ºóÌí¼Óµ½beanFactoryÖÐ + registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); + // ´¦ÀíinternalPostProcessor£ºÅÅÐò£¬È»ºóÌí¼Óµ½beanFactoryÖÐ + // AutowiredAnnotationBeanPostProcessorʵÏÖÁËMergedBeanDefinitionPostProcessor£¬ + // Òò´ËÕâÀï»áÔÙ´Î×¢²á + sortPostProcessors(internalPostProcessors, beanFactory); + registerBeanPostProcessors(beanFactory, internalPostProcessors); + + beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); +} + +``` + +Õâ¿é´úÂëÖ÷ÒªÊÇ¶Ô `BeanFactoryPostProcessor` ½øÐÐ×¢²á²Ù×÷£¬²½ÖèÈçÏ£º + +* ½«ÊµÏÖÁË `PriorityOrdered` µÄ `BeanPostProcessor` ×¢²áµ½ `beanFactory` ÖУ» +* ½«ÊµÏÖÁË `Ordered` µÄ `BeanPostProcessor` ×¢²áµ½ `beanFactory` ÖУ» +* ½«²»Âú×ãÉÏÊöÁ½¸öÌõ¼þµÄ `BeanPostProcessor` ×¢²áµ½ `beanFactory` ÖУ» +* ½«ËùÓÐʵÏÖÁË `MergedBeanDefinitionPostProcessor` µÄ `BeanPostProcessor` ÔÙ´Î×¢²áµ½ `beanFactory` ÖС£ + +ÊÂʵÉÏ£¬¶Ô demo01 ¶øÑÔ£¬ÕâÀï×¢²áµÄ bean Ö»ÓÐÒ»¸ö£º`AutowiredAnnotationBeanPostProcessor`£¬ËüͬʱʵÏÖÁË `MergedBeanDefinitionPostProcessor` Óë `PriorityOrdered`£¬Òò´Ë»á×¢²áÁ½´Î¡£ + +¾¡¹Ü×¢²áÁ˶à´Î `AutowiredAnnotationBeanPostProcessor`£¬µ«×îÖÕÖ»»á´æÔÚÒ»¸ö£¬ÈÃÎÒÃǽøÈë `registerBeanPostProcessors` ¿´¿´ spring ÊÇÈçºÎ×¢²áµÄ£¬Ò»Â·¸úÏÂÈ¥£¬´úÂëµ½ÁË `AbstractBeanFactory#addBeanPostProcessor`: + +> AbstractBeanFactory#addBeanPostProcessor + +``` +private final List beanPostProcessors = new CopyOnWriteArrayList<>(); + +@Override +public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { + Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); + // ÏȽøÐÐÒÆ³ý£¬Òò´Ë¶à´Î×¢²ábeanPostProcessorsÒ²Ö»´æÔÚÒ»¸ö + this.beanPostProcessors.remove(beanPostProcessor); + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + this.hasInstantiationAwareBeanPostProcessors = true; + } + if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { + this.hasDestructionAwareBeanPostProcessors = true; + } + this.beanPostProcessors.add(beanPostProcessor); +} + +``` + +¿ÉÒÔ¿´µ½£¬ËùνµÄ×¢²áµ½ `BeanFactory`£¬Æäʵ¾ÍÊÇ°Ñ `beanPostProcessor` ¼ÓÈëµ½ `BeanFactory` µÄ `beanPostProcessors` ÖС£ + +±¾ÎĽöÉÜÁË `beanPostProcessor` µÄ×¢²á£¬¹ØÓÚ `beanPostProcessor` µÄµ÷Ó㬺óÐøµÄ·ÖÎöÔÙÌáµ½£¬±¾ÎĵķÖÎö¾ÍÏȵ½ÕâÀï°É£¡ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4657181](https://my.oschina.net/funcy/blog/4657181) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" new file mode 100644 index 0000000..1820d8f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\344\270\200\357\274\211\357\274\232\345\220\257\345\212\250\346\265\201\347\250\213\346\200\273\347\273\223.md" @@ -0,0 +1,15 @@ +µ½ÁËÕâÀspring ÈÝÆ÷µÄÆô¶¯Á÷³ÌÖÕÓÚÊÇ·ÖÎöÍê³ÉÁË£¬ÕâÀïʹÓÃÒ»ÕÅͼÀ´×ܽáÕû¸öÆô¶¯Á÷³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-72a4008f2ad3401de6b4f2d5c7f697923a3.png) + +±¾ÏµÁÐ´Ó `AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)` ·½·¨³ö·¢£¬·ÖÎöÁË `AnnotationConfigApplicationContext` µÄÎ޲ι¹Ôì·½·¨¡¢°üɨÃèÁ÷³Ì¡¢ÈÝÆ÷µÄÆô¶¯Á÷³ÌµÈ£¬Õⲿ·ÖÄÚÈÝ±È½Ï¶à£¬ÖØµãÁ÷³ÌÈçÏ£º + +1. °üɨÃèÁ÷³Ì +2. ÔËÐÐ `beanFactoryProcessor` +3. ³õʼ»¯µ¥Àý bean + +ÔÚ·ÖÎö¹ý³ÌÖУ¬ÎÒÃǺöÂÔÁËÐí¶àϸ½Ú£¬½ö¹Ø×¢ÁËÖ÷ÒªÁ÷³Ì£¬¶Ôϸ½Ú¸ÐÐËȤµÄС»ï°é¿ÉÒÔ¸ù¾ÝÌṩµÄÁ´½Ó½øÈëÏàÓ¦ÎÄÕÂÔĶÁ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4659519](https://my.oschina.net/funcy/blog/4659519) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" new file mode 100644 index 0000000..a60a4d6 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\215\201\357\274\211\357\274\232\345\220\257\345\212\250\345\256\214\346\210\220\347\232\204\345\244\204\347\220\206.md" @@ -0,0 +1,158 @@ + + +½ÓÉÏÎÄ£¬¼ÌÐø·ÖÎö spring µÄÆô¶¯Á÷³Ì¡£ + +12. Íê³ÉÆô¶¯²Ù×÷: finishRefresh() + +AbstractApplicationContext#finishRefresh ·½·¨ÈçÏ£º + + protected void finishRefresh() { + // ¿´Ãû×Ö¾ÍÖªµÀÁË£¬ÇåÀí³õʼ»¯¹ý³ÌÖÐһϵÁвÙ×÷ʹÓõ½µÄ×ÊÔ´»º´æ + clearResourceCaches(); + // ³õʼ»¯LifecycleProcessor + initLifecycleProcessor(); + // Õâ¸ö·½·¨µÄÄÚ²¿ÊµÏÖÊÇÆô¶¯ËùÓÐʵÏÖÁËLifecycle½Ó¿ÚµÄbean + getLifecycleProcessor().onRefresh(); + // ·¢²¼ContextRefreshedEventʼþ + publishEvent(new ContextRefreshedEvent(this)); + // ¼ì²éspring.liveBeansView.mbeanDomainÊÇ·ñ´æÔÚ£¬Óоͻᴴ½¨Ò»¸öMBeanServer + LiveBeansView.registerApplicationContext(this); + } + + +Õâ¸ö·½·¨´úÂë²»¶à£¬¾Í¼¸¸ö·½·¨£¬ÎÒÃÇ·Ö±ðÀ´¿´¿´¡£ + +1. ÇåÀí×ÊÔ´»º´æ£ºclearResourceCaches() + +clearResourceCaches() ·½·¨ÄÚÈÝÈçÏ£º + + public class DefaultResourceLoader implements ResourceLoader { + + private final Map, Map> resourceCaches + = new ConcurrentHashMap<>(4); + + public void clearResourceCaches() { + this.resourceCaches.clear(); + } + + // Ê¡ÂÔÁËÕâ¸öÀàµÄºÃ¶à´úÂë + ... + + } + + +Õâ¸ö·½·¨¾ÍÊÇÓÃÀ´ÇåÀí resourceCaches µÄ£¬ÕâÊǸö Map£¬ÀïÃæ´æÈëµÄÄÚÈÝÊÇ Resource¡£ + +ÄÇʲôÊÇ Resource ÄØ£¿ÔÚÇ°Ãæ½éÉÜɨÃè°üµÄ¹ý³ÌÖУ¬ÎÒÃÇ»áÏ顄 class Îļþ¶ÁÈ¡³öÀ´£¬×ª»»³É Resource ºóÔÙ½øÒ»²½´¦Àí£¬³£¼ûµÄ Resource ÀàÐÍÓÐ FileSystemResource¡¢UrlResource µÈ£¬resourceCaches ¾ÍÊÇ´æ·ÅÕâЩ Resource µÄ¡£ + +2. ´¦Àí LifecycleProcessor + +ÎÒÃÇÏÈÀ´¿´¿´Ê²Ã´ÊÇ LifecycleProcessor£º + + /** + * ´¦ÀíÈÝÆ÷µÄÆô¶¯Óë¹Ø±Õ²Ù×÷ + */ + public interface LifecycleProcessor extends Lifecycle { + + /** + * ÈÝÆ÷Æô¶¯Íê³Éʱµ÷Óà + */ + void onRefresh(); + + /** + * ÈÝÆ÷¹Ø±Õʱµ÷Óà + */ + void onClose(); + + } + + +Õâ¸ö½Ó¿ÚÓÃÀ´´¦ÀíÈÝÆ÷´¦ÀíÈÝÆ÷µÄÆô¶¯Óë¹Ø±Õ²Ù×÷£¬±ÈÈçÎÒÃÇ×Ô¼ºÊµÏָýӿڣ¬È»ºóÖØÐ´ onRefresh() Óë onClose()£¬ÒÔ±ãÔÚÈÝÆ÷Æô¶¯Óë¹Ø±Õʱ×öһЩ²Ù×÷£¬ÏñÕâÑù£º + + @Component + public class MyLifecycleProcessor implements LifecycleProcessor { + + @Override + public void onRefresh() { + System.out.println("ÈÝÆ÷Æô¶¯"); + } + + @Override + public void onClose() { + System.out.println("ÈÝÆ÷¹Ø±Õ"); + } + } + + +Óë LifecycleProcessor Ïà¹ØµÄ·½·¨ÓÐÁ½¸ö£ºinitLifecycleProcessor()¡¢getLifecycleProcessor()£¬ÎÒÃÇÒ»ÆðһΪ¿´¿´ÕâÁ½¸ö·½·¨£º + +AbstractApplicationContext + + private LifecycleProcessor lifecycleProcessor; + + /** + * ³õʼ»¯ LifecycleProcessor + */ + protected void initLifecycleProcessor() { + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + // ´æÔÚ£¬Ö±½ÓʹÓà + if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) { + this.lifecycleProcessor = + beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class); + } + // ²»´æÔÚÔò´´½¨£¬Ä¬ÈÏʹÓÃDefaultLifecycleProcessor + else { + DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor(); + defaultProcessor.setBeanFactory(beanFactory); + this.lifecycleProcessor = defaultProcessor; + beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor); + } + } + + /** + * ·µ»Ø lifecycleProcessor + */ + LifecycleProcessor getLifecycleProcessor() throws IllegalStateException { + if (this.lifecycleProcessor == null) { + throw new IllegalStateException(...); + } + return this.lifecycleProcessor; + } + + +initLifecycleProcessor Ëù×öµÄ¾ÍÊÇÉèÖà AbstractApplicationContext#lifecycleProcessor ÊôÐÔ£¬Èç¹û beanFactory ÖдæÔÚ initLifecycleProcessor ÔòÖ±½ÓʹÓ㬷ñÔò¾Í´´½¨Ò»¸ö¡£ + +getLifecycleProcessor() ½ö½öÖ»ÊÇ·µ»ØÁË AbstractApplicationContext#lifecycleProcessor ÊôÐÔ¡£ + +ÔÚ getLifecycleProcessor().onRefresh() ÖУ¬»¹µ÷ÓÃÁË onRefresh() ·½·¨£¬ÎÒÃÇÒ»ÆðÀ´¿´¿´ DefaultLifecycleProcessor#onRefresh ×öÁËʲô£º + + @Override + public void onRefresh() { + startBeans(true); + this.running = true; + } + + +´Ó±äÁ¿À´¿´£¬Õâ¸ö·½·¨½ö½öÖ»ÊǸÄÁËÒ»¸öÔËÐÐ״̬¡£ + +3. ·¢²¼ ContextRefreshedEvent ʼþ + +´úÂë publishEvent(new ContextRefreshedEvent(this)) ·¢²¼ÁË ContextRefreshedEvent£¬ÎÒÃÇ×Ô¼ºÒ²¿ÉÒÔÀ´¼àÌý¸Ãʼþ¡£¹ØÓÚʼþ£¬±¾ÎIJ¢²»´òËãÉîÈ룬¹ØÓÚ spring ʼþµÄÏêϸ·ÖÎö£¬¿ÉÒԲο¼ spring Ì½ÃØÖ® spring ʼþ»úÖÆ¡£ + +13. Çå³ý»º´æ: resetCommonCaches() + +¸Ã·½·¨´úÂëÈçÏ£º + + protected void resetCommonCaches() { + ReflectionUtils.clearCache(); + AnnotationUtils.clearCache(); + ResolvableType.clearCache(); + CachedIntrospectionResults.clearClassLoader(getClassLoader()); + } + + +´Ó·½·¨À´¿´£¬¾ÍÊÇÖ´Ðи÷ÖÖ»º´æ£¬Ö´ÐбȽϼòµ¥£¬¾Í²»ÉÁË¡£ + +--- + +±¾ÎÄÔ­ÎÄÁ´½Ó£ºhttps://my.oschina.net/funcy/blog/4892555 £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" new file mode 100644 index 0000000..10070df --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\345\220\257\345\212\250\346\265\201\347\250\213/Spring\345\220\257\345\212\250\346\265\201\347\250\213\357\274\210\345\233\233\357\274\211\357\274\232\345\220\257\345\212\250\345\211\215\347\232\204\345\207\206\345\244\207\345\267\245\344\275\234.md" @@ -0,0 +1,230 @@ +Íê³É°üµÄɨÃèºó£¬½ÓמͿªÊ¼ÁË spring µÄÆô¶¯ÁË£¬¼´ `AbstractApplicationContext#refresh` ·½·¨£¬¸Ã·½·¨Ò»¹²°üº¬ 13 ¸ö²Ù×÷£¬º­¸ÇÒ² spring Æô¶¯µÄÕû¸öÁ÷³Ì£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-88c3a2486c24ccd0ad390ba9b62b986a6b2.png) + +±¾ÏµÁдӱ¾ÎÄ¿ªÊ¼£¬Öð²½·ÖÎöÕâ 13 ¸ö·½·¨£¬Ì½Ë÷ spring µÄÆô¶¯Á÷³Ì¡£ + +### 1\. Æô¶¯Ç°×¼±¸£º`prepareRefresh()` + +¸ú½ø `prepareRefresh()` ·½·¨£¬µ÷ÓÃÁ´ÈçÏ£º + +``` +|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#prepareRefresh + +``` + +´úÂëÈçÏ£º + +``` +protected void prepareRefresh() { + // Switch to active. + this.startupDate = System.currentTimeMillis(); + this.closed.set(false); + this.active.set(true); + + // ³õʼ»¯¼ÓÔØÅäÖÃÎļþ·½·¨£¬²¢Ã»ÓоßÌåʵÏÖ£¬Ò»¸öÁô¸øÓû§µÄÀ©Õ¹µã + initPropertySources(); + + // ¼ì²é»·¾³±äÁ¿ + getEnvironment().validateRequiredProperties(); + + if (this.earlyApplicationListeners == null) { + this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); + } else { + this.applicationListeners.clear(); + this.applicationListeners.addAll(this.earlyApplicationListeners); + } + + this.earlyApplicationEvents = new LinkedHashSet<>(); +} + +``` + +Õâ¶Î´úÂë±È½Ï¼òµ¥£¬¾ÍÊÇÉèÖÃÁËÏÂÆô¶¯Ê±¼ä¡¢ÈÝÆ÷µÄÆô¶¯×´Ì¬¡¢»·¾³±äÁ¿µÄ¼ì²é¡¢ÊôÐԵijõʼ»¯µÈ¡£ + +### 2\. »ñÈ¡ `beanFactory: obtainFreshBeanFactory()` + +ÎÒÃÇÔÙ¸ú½ø `obtainFreshBeanFactory()` ·½·¨£¬ÄÚÈÝÈçÏ£º + +> AbstractApplicationContext#obtainFreshBeanFactory + +``` +protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { + refreshBeanFactory(); + // ·µ»Ø¸Õ¸Õ´´½¨µÄ BeanFactory + return getBeanFactory(); +} + +``` + +Õâ¸ö·½·¨ÏÈÊÇ `refresh` ÁË `BeanFactory`£¬È»ºóÔÙ·µ»ØÁË `BeanFactory`£¬ÎÒÃǼÌÐø¸ú½ø `refreshBeanFactory()`£º + +> GenericApplicationContext#refreshBeanFactory + +``` +@Override +protected final void refreshBeanFactory() throws IllegalStateException { + // Ê¡ÂÔÁËһЩÅжϴúÂë + this.beanFactory.setSerializationId(getId()); +} + +``` + +Õâ¸ö·½·¨¹Ø¼ü´úÂëÖ»ÓÐÒ»ÐУ¬×÷ÓÃÊÇÓÃÀ´ÉèÖà beanFactory µÄ `SerializationId`. + +ÎÒÃÇÔٻعýÍ·À´¿´¿´ `getBeanFactory()` ·½·¨£º + +> GenericApplicationContext#getBeanFactory + +``` +public final ConfigurableListableBeanFactory getBeanFactory() { + return this.beanFactory; +} + +``` + +Õâ¸ö·½·¨¾Í¸ü¼òµ¥ÁË£¬½ö½ö·µ»ØÁ˵±Ç°ÀàµÄ `beanFactory`£¬Õâ¸ö `beanFactory` ¾ÍÊÇÎÒÃÇÔÚ·ÖÎö `AnnotationConfigApplicationContext` ¹¹Ôì·½·¨Ê±´´½¨µÄ£¬ÀàÐÍΪ `DefaultListableBeanFactory`. + +### 3\. ×¼±¸ `beanFactory: prepareBeanFactory(beanFactory)` + +ÎÒÃǼÌÐø£¬½øÈë `prepareBeanFactory(beanFactory)` ·½·¨£º + +> AbstractApplicationContext#prepareBeanFactory + +``` +protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // ÉèÖÃΪ¼ÓÔØµ±Ç°ApplicationContextÀàµÄÀà¼ÓÔØÆ÷ + beanFactory.setBeanClassLoader(getClassLoader()); + // ÉèÖà BeanExpressionResolver¡ª¡ªbean±í´ïʽ½âÎöÆ÷ + beanFactory.setBeanExpressionResolver( + new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); + // ÊôÐԱ༭Æ÷Ö§³Ö + beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); + + // ÕâÀïÊÇSpringµÄÓÖÒ»¸öÀ©Õ¹µã + // ÔÚËùÓÐʵÏÖÁËAware½Ó¿ÚµÄbeanÔÚ³õʼ»¯µÄʱºò£¬Õâ¸ö processor¸ºÔð»Øµ÷£¬ + // Õâ¸öÎÒÃǺܳ£Óã¬ÈçÎÒÃÇ»áΪÁË»ñÈ¡ ApplicationContext ¶ø implement ApplicationContextAware + // ×¢Ò⣺Ëü²»½ö½ö»Øµ÷ ApplicationContextAware£¬»¹»á¸ºÔð»Øµ÷ EnvironmentAware¡¢ResourceLoaderAware µÈ + beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); + + // ÏÂÃæ¼¸ÐеÄÒâ˼¾ÍÊÇ£¬Èç¹ûij¸ö bean ÒÀÀµÓÚÒÔϼ¸¸ö½Ó¿ÚµÄʵÏÖÀ࣬ + // ÔÚ×Ô¶¯×°ÅäµÄʱºòºöÂÔËüÃÇ£¬Spring »áͨ¹ýÆäËû·½Ê½À´´¦ÀíÕâЩÒÀÀµ¡£ + beanFactory.ignoreDependencyInterface(EnvironmentAware.class); + beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); + beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); + beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); + beanFactory.ignoreDependencyInterface(MessageSourceAware.class); + beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); + + // ÏÂÃæ¼¸ÐоÍÊÇÎªÌØÊâµÄ¼¸¸ö bean ¸³Öµ£¬Èç¹ûÓÐ bean ÒÀÀµÁËÒÔϼ¸¸ö£¬»á×¢ÈëÕâ±ßÏàÓ¦µÄÖµ + beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); + beanFactory.registerResolvableDependency(ResourceLoader.class, this); + beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); + beanFactory.registerResolvableDependency(ApplicationContext.class, this); + + // Ìí¼ÓÒ»¸öºóÖô¦ÀíÆ÷£ºApplicationListenerDetector£¬´ËºóÖô¦ÀíÆ÷ʵÏÖÁËBeanPostProcessor½Ó¿Ú + beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); + + // Èç¹û´æÔÚbeanÃû³ÆÎªloadTimeWeaverµÄbeanÔò×¢²áÒ»¸öBeanPostProcessor + if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { + beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); + // Set a temporary ClassLoader for type matching. + beanFactory.setTempClassLoader( + new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); + } + + // Èç¹ûûÓж¨Òå "environment" Õâ¸ö bean£¬ÄÇô Spring »á "ÊÖ¶¯" ×¢²áÒ»¸ö + if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { + beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); + } + // Èç¹ûûÓж¨Òå "systemProperties" Õâ¸ö bean£¬ÄÇô Spring »á "ÊÖ¶¯" ×¢²áÒ»¸ö + if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { + beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, + getEnvironment().getSystemProperties()); + } + // Èç¹ûûÓж¨Òå "systemEnvironment" Õâ¸ö bean£¬ÄÇô Spring »á "ÊÖ¶¯" ×¢²áÒ»¸ö + if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { + beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, + getEnvironment().getSystemEnvironment()); + } +} + +``` + +Õâ¸ö·½·¨ÊÇ¶Ô beanFactory µÄһЩ׼±¸£¬ÉèÖÃһЩÊôÐÔ£¬Ìí¼ÓһЩ bean ´¦ÀíµÈ£¬´úÂë¶¼ÓÐ×¢½â£¬ÕâÀï¾Í²»Öظ´ËµÁË¡£ + +¹ØÓÚÍù beanFactory ÖÐÌí¼Ó `ApplicationListenerDetector` ÐèÒªÌáÏ£¬Ïà¹Ø´úÂëΪ `beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));`£¬ÎÒÃÇ¿´Ï `ApplicationListenerDetector` Õâ¸öÀࣺ + +> org.springframework.context.support.ApplicationContextAwareProcessor + +``` +class ApplicationContextAwareProcessor implements BeanPostProcessor { + @Override + @Nullable + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + // Ê¡ÂÔÁËһЩ´úÂë + AccessControlContext acc = null; + if (System.getSecurityManager() != null) { + acc = this.applicationContext.getBeanFactory().getAccessControlContext(); + } + if (acc != null) { + AccessController.doPrivileged((PrivilegedAction) () -> { + invokeAwareInterfaces(bean); + return null; + }, acc); + } else { + invokeAwareInterfaces(bean); + } + return bean; + } + + // »Øµ÷ Aware½Ó¿Ú + private void invokeAwareInterfaces(Object bean) { + // µ÷Óà EnvironmentAware#setEnvironment ·½·¨ + if (bean instanceof EnvironmentAware) { + ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); + } + // µ÷Óà EmbeddedValueResolverAware#setEmbeddedValueResolver ·½·¨ + if (bean instanceof EmbeddedValueResolverAware) { + ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); + } + // µ÷Óà ResourceLoaderAware#setResourceLoader ·½·¨ + if (bean instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); + } + // µ÷Óà ApplicationEventPublisherAware#setApplicationEventPublisher ·½·¨ + if (bean instanceof ApplicationEventPublisherAware) { + ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); + } + // µ÷Óà MessageSourceAware#setMessageSource ·½·¨ + if (bean instanceof MessageSourceAware) { + ((MessageSourceAware) bean).setMessageSource(this.applicationContext); + } + // µ÷Óà ApplicationContextAware#setApplicationContext ·½·¨ + if (bean instanceof ApplicationContextAware) { + ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); + } + } + + // Ê¡ÂÔÆäËû´úÂë +} + +``` + +¿ÉÒÔ¿´µ½£¬ + +1. Õâ¸öÀàʵÏÖÁË `BeanPostProcessor` ½Ó¿Ú£» +2. ·½·¨ `postProcessBeforeInitialization` ÓÉ `BeanPostProcessor` Ìṩ£¬×îΪ¹Ø¼üµÄ´úÂëÊÇ` invokeAwareInterfaces(bean);`£» +3. `invokeAwareInterfaces` Ö»ÊÇһϵÁеķ½·¨µ÷Óà + +¹ØÓÚ `BeanPostProcessor` µÄµÄ·ÖÎö£¬¿ÉÒԲο¼ [spring ×é¼þÖ® BeanPostProcessors ](https://my.oschina.net/funcy/blog/4597551)£¬¹ØÓÚ¸ÃÀàµÄ×÷Ó㬺óÐø»á¼ÌÐø½²µ½¡£ + +ºÃÁË£¬±¾ÎĵķÖÎö¾Íµ½ÕâÀïÁË£¬±¾ÎĽö·ÖÎöÁË spring Æô¶¯Ê±¶Ô beanFactory µÄ×¼±¸£¬ÄÚÈݽϼòµ¥£¬×îºóÓÃÒ»·ùͼÀ´×ܽáϱ¾ÎÄÄÚÈÝ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1e10d7aff080b2e0bbfbef5d79c56cc54c9.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4633169](https://my.oschina.net/funcy/blog/4633169) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232AOP\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232AOP\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232AOP\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232AOP\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232JDK\345\222\214cglib\345\212\250\346\200\201\344\273\243\347\220\206\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232JDK\345\222\214cglib\345\212\250\346\200\201\344\273\243\347\220\206\345\216\237\347\220\206\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232JDK\345\222\214cglib\345\212\250\346\200\201\344\273\243\347\220\206\345\216\237\347\220\206\350\257\246\350\247\243.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232JDK\345\222\214cglib\345\212\250\346\200\201\344\273\243\347\220\206\345\216\237\347\220\206\350\257\246\350\247\243.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringAOP\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringAOP\346\246\202\350\277\260.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringAOP\346\246\202\350\277\260.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringAOP\346\246\202\350\277\260.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\246\202\350\277\260.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\246\202\350\277\260.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\246\202\350\277\260.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\246\202\350\277\260.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\272\220\347\240\201\345\211\226\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\272\220\347\240\201\345\211\226\346\236\220.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\272\220\347\240\201\345\211\226\346\236\220.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\344\272\213\345\212\241\346\272\220\347\240\201\345\211\226\346\236\220.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" similarity index 99% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" index 2ffdb35..ba4415e 100644 --- "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" @@ -4,7 +4,6 @@ * [读å�–](#读å�–) * [è§£æž�](#è§£æž�) * [注册](#注册) - * [注入ä¾�èµ–](#注入ä¾�èµ–) 本文转载自互è�”网,侵删 diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\346\207\222\345\212\240\350\275\275\347\232\204\345\215\225\344\276\213Bean\350\216\267\345\217\226\350\277\207\347\250\213\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\346\207\222\345\212\240\350\275\275\347\232\204\345\215\225\344\276\213Bean\350\216\267\345\217\226\350\277\207\347\250\213\345\210\206\346\236\220.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\346\207\222\345\212\240\350\275\275\347\232\204\345\215\225\344\276\213Bean\350\216\267\345\217\226\350\277\207\347\250\213\345\210\206\346\236\220.md" rename to "docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232\346\207\222\345\212\240\350\275\275\347\232\204\345\215\225\344\276\213Bean\350\216\267\345\217\226\350\277\207\347\250\213\345\210\206\346\236\220.md" diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" new file mode 100644 index 0000000..ff74cca --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213ApplicationContext.md" @@ -0,0 +1,802 @@ +### 1. `ApplicationContext` ¼ò½é + +ÎÒÃÇÔÚÆô¶¯ spring ÈÝÆ÷ʱ£¬Ò»°ãÏñÕâÑùÆô¶¯£º + +``` +ApplicationContext context = new AnnotationConfigApplicationContext(Main.class); + +``` + +»òÕâÑù£º + +``` +ApplicationContext context = new AnnotationConfigWebApplicationContext(); +context.register(MvcConfig.class); +context.refresh(); + +``` + +ÕâÀïµÄ `AnnotationConfigApplicationContext` Óë `AnnotationConfigWebApplicationContext` ¶¼ÊÇ `ApplicationContext`£¬×îÖÕ½øÐлáµ÷Óà `AbstractApplicationContext#refresh` ·½·¨Æô¶¯ spring ÈÝÆ÷¡£ + +`ApplicationContext` ·­ÒëΪ **spring Ó¦ÓÃÉÏÏÂÎÄ**£¬´ÓÕâ¸öÀàÀï¿ÉÒÔ»ñÈ¡ spring ÔËÐÐÆÚ¼äµÄ¸÷ÖÖÐÅÏ¢£¬Èç `BeanFactory`¡¢`Environment` µÈ£¬ÊÇ spring ÖÐÖÁ¹ØÖØÒªµÄÒ»¸öÀà¡£ + +`ApplicationContext` ¼Ì³ÐµÄ½Ó¿ÚÈçÏ£º + +``` +public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, + HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, + ResourcePatternResolver { + ... +} + +``` + +´ÓÕâÀï¿ÉÒÔ¿´µ½£¬`ApplicationContext` ±¾ÉíÒ²ÊÇÆäËû½Ó¿ÚµÄ×Ó½Ó¿Ú£¬ÕâЩ½Ó¿ÚµÄ¹¦ÄÜÈçÏ£º + +* `EnvironmentCapable`£ºÌṩÁË»·¾³ÅäÖù¦ÄÜ£¬`applicationContext` ʵÏÖÖлáÓÐÒ»¸ö `Environment` ÀàÐ͵ijÉÔ±±äÁ¿£¬¿ÉÒÔͨ¹ý `EnvironmentCapable#getEnvironment()` ·½·¨È¡µÃ +* `ListableBeanFactory`£º`BeanFactory` µÄ×Ó½Ó¿Ú£¬ÌṩÁËÁо٠`BeanFactory` ÖÐËùÓÐ `bean` µÄ·½·¨ +* `HierarchicalBeanFactory`£º`BeanFactory` µÄ×Ó½Ó¿Ú£¬ÌṩÁË `BeanFactory` ÀàËÆ¼Ì³ÐµÄÄÜÁ¦£¨¿ÉÒÔ»ñÈ¡¸¸ `BeanFactory`£© +* `MessageSource`£ºÖ¸¶¨ÏûÏ¢À´Ô´£¬¿ÉÒÔÓÃÀ´ÊµÏÖ¹ú¼Ê»¯²Ù×÷ +* `ApplicationEventPublisher`£ºÊ¼þ·¢²¼Æ÷£¬ÓÃÆäÌṩµÄ `publishEvent(...)` ·½·¨À´·¢²¼Ê¼þ +* `ResourcePatternResolver`£º×ÊÔ´½âÎöÆ÷£¬ÌṩÁË»ñÈ¡×ÊÔ´£¨`Resource`£©µÄ·½·¨£º`getResources(...)` + +ÎÒÃÇÀ´¿´¿´ `ApplicationContext` ×ÔÉíÌṩµÄ·½·¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-82de66bd51d650aa8a1fda29bdda3efd6b4.png) + +¿ÉÒÔ¿´µ½£¬Ëü×ÔÉíµÄ·½·¨²¢²»¶à¡£ + +### 2. `ApplicationContext` ¼Ì³Ð½á¹¹ + +ÔÚ `ApplicationContext` ¼Ò×åÖУ¬`ApplicationContext` Ö÷Òª·ÖΪÁ½´óÅÉϵ£¬ËûÃǼ°Æä´ú±íÈçÏ£º + +* ·Ç web ÀàÐ굀 `ApplicationContext`£º´¦ÀíÆÕͨ java Ó¦ÓÃµÄ `ApplicationContext`£¬´ú±íÀàΪ `AnnotationConfigApplicationContext` +* web ÀàÐ굀 `ApplicationContext`£º´¦Àí web Ó¦ÓÃµÄ `ApplicationContext`£¬´ú±íÀàΪ `AnnotationConfigWebApplicationContext`£¨ÕâÀïÖ»¿¼ÂÇ `servlet` ÀàÐ굀 web£¬²»¿¼ÂÇ `reactive` µÄ web£© + +ÎÒÃÇÔÙÀ´¿´¿´ `AnnotationConfigApplicationContext` µÄ¼Ì³Ð½á¹¹£º + +![ͼƬÀ´×ÔÍøÂç](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-6e20477e8f5948894a5f241cd41038cfa15.png) + +ÎÒÃÇÔÙÀ´¿´¿´ `AnnotationConfigWebApplicationContext` µÄ¼Ì³Ð½á¹¹£º + +![ͼƬÀ´×ÔÍøÂç](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-23a6c84d82370afe39245a568cbcf918209.png) + +### 3\. ÔÚ bean ÖлñÈ¡ `ApplicationContext` + +ÔÚ spring bean ÖлñÈ¡ `ApplicationContext`£¬¿ÉÒÔͨ¹ý `ApplicationContextAware` ½Ó¿ÚÀ´´¦Àí£º + +``` +@Component +public class TestBean implements ApplicationContextAware { + + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + // ÆäËû²Ù×÷ + +} + +``` + +¼Ì³Ð `ApplicationContextAware` ºó£¬`ApplicationContextAwareProcessor` »áÔÚ³õʼ»¯Íê³Éºóµ÷Óà `setApplicationContext(xxx)` ·½·¨£¬ÕâÑùÎÒÃÇÖ»ÐèÒªÔÚ `TestBean` ÖÐά»¤Ò»¸ö³ÉÔ±±äÁ¿£¬¶Ô `applicationContext` ±£´æ¼´¿É¡£ + +### 4. `ApplicationContextAwareProcessor` + +`ApplicationContextAwareProcessor` ÊÇÒ»¸ö `BeanPostProcessor`£¬ÎÒÃÇÖ÷Òª¹Ø×¢ `ApplicationContextAwareProcessor#postProcessBeforeInitialization` ·½·¨£¬´ú±íÈçÏ£º + +``` +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + /** + * applicationContext ÊÇ Environment¡¢ResourceLoader¡¢ + * ApplicationEventPublisher¡¢MessageSource µÈµÄ×ÓÀ࣬ÕâЩÀàµÄaware½Ó¿ÚµÄµ÷Ó㬶¼¿ÉÒÔ + * ͨ¹ý applicationContext ²ÎÊý½øÐÐ + */ + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ + return bean; + } + AccessControlContext acc = null; + if (System.getSecurityManager() != null) { + acc = this.applicationContext.getBeanFactory().getAccessControlContext(); + } + if (acc != null) { + AccessController.doPrivileged((PrivilegedAction) () -> { + invokeAwareInterfaces(bean); + return null; + }, acc); + } + else { + invokeAwareInterfaces(bean); + } + return bean; +} + +/** + * µ÷Óà Aware ½Ó¿ÚµÄ·½·¨ + * ³ýÁËEmbeddedValueResolverAwareÍ⣬ÆäÓàµÄ´«Èë²ÎÊý¶¼ÊÇ this.applicationContext + */ +private void invokeAwareInterfaces(Object bean) { + if (bean instanceof EnvironmentAware) { + ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); + } + if (bean instanceof EmbeddedValueResolverAware) { + // ×¢ÒâembeddedValueResolverµÄ»ñÈ¡²Ù×÷ÈçÏ£º + // new EmbeddedValueResolver(applicationContext.getBeanFactory()); + ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); + } + if (bean instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); + } + if (bean instanceof ApplicationEventPublisherAware) { + ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher( + this.applicationContext); + } + if (bean instanceof MessageSourceAware) { + ((MessageSourceAware) bean).setMessageSource(this.applicationContext); + } + // ×°Åä ʵÏÖÁËApplicationContextAwareµÄÀàµÄ applicationContext + if (bean instanceof ApplicationContextAware) { + ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); + } +} + +``` + +Õâ¸ö·½·¨»¹ÊDZȽϼòµ¥µÄ£¬¾ÍÊÇÅÐ¶Ï bean ÀàÐÍ£¬È»ºóת»»¡¢µ÷Ó÷½·¨¡£ + +### 5. `ApplicationContext` Óë `BeanFactory` µÄ¹ØÏµ + +×îºóÎÒÃÇÔÙÀ´ÌÖÂÛÏ `ApplicationContext` Óë `BeanFactory` Á½ÕߵĹØÏµ¡£±¾ÎÄÒ»¿ªÊ¼¾Í˵Ã÷ÁË `ApplicationContext` ¼Ì³ÐÁË `BeanFactory` µÄ½Ó¿Ú£¬ÒòΪÕâÁ½ÕßÊǼ̳йØÏµ¡£²»¹ý£¬³ýÁ˼̳йØÏµÍ⣬ËûÃÇ»¹ÊÇ×éºÏ¹ØÏµ£¬`ApplicationContext` ³ÖÓÐ `BeanFactory` µÄ¶ÔÏó£¬Ö±½Ó¿´´úÂ룺 + +¶ÔÓÚ `AnnotationConfigApplicationContext`£¬`beanFactory` ¸³Öµ´úÂëÈçÏ£º + +``` +public class GenericApplicationContext extends AbstractApplicationContext + implements BeanDefinitionRegistry { + + // Õâ¾ÍÊdzÖÓÐµÄ beanFactory ¶ÔÏó + private final DefaultListableBeanFactory beanFactory; + + public GenericApplicationContext() { + this.beanFactory = new DefaultListableBeanFactory(); + } + + @Override + public final ConfigurableListableBeanFactory getBeanFactory() { + return this.beanFactory; + } + + ... + +} + +``` + +¶ÔÓÚ `AnnotationConfigWebApplicationContext`£¬`beanFactory` ¸³Öµ´úÂëÈçÏ£º + +``` +public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { + + // Õâ¾ÍÊdzÖÓÐµÄ beanFactory ¶ÔÏó + @Nullable + private DefaultListableBeanFactory beanFactory; + + @Override + protected final void refreshBeanFactory() throws BeansException { + // Åжϵ±Ç°ApplicationContextÊÇ·ñ´æÔÚBeanFactory£¬Èç¹û´æÔڵϰ¾ÍÏú»ÙËùÓÐ Bean£¬¹Ø±Õ BeanFactory + if (hasBeanFactory()) { + destroyBeans(); + closeBeanFactory(); + } + try { + // ³õʼ»¯DefaultListableBeanFactory£¬¿´ÏÂÃæµÄ´´½¨·½·¨ + DefaultListableBeanFactory beanFactory = createBeanFactory(); + beanFactory.setSerializationId(getId()); + + // ÉèÖà BeanFactory µÄÁ½¸öÅäÖÃÊôÐÔ£ºÊÇ·ñÔÊÐí Bean ¸²¸Ç¡¢ÊÇ·ñÔÊÐíÑ­»·ÒýÓà + customizeBeanFactory(beanFactory); + + // ¼ÓÔØ Bean µ½ BeanFactory ÖÐ + loadBeanDefinitions(beanFactory); + synchronized (this.beanFactoryMonitor) { + this.beanFactory = beanFactory; + } + } + catch (IOException ex) { + ... + } + } + + // ´´½¨ beanFactory + protected DefaultListableBeanFactory createBeanFactory() { + // Ö¸¶¨¸¸beanFactory + return new DefaultListableBeanFactory(getInternalParentBeanFactory()); + } + + // »ñÈ¡ beanFactory + @Override + public final ConfigurableListableBeanFactory getBeanFactory() { + synchronized (this.beanFactoryMonitor) { + if (this.beanFactory == null) { + ... + } + return this.beanFactory; + } + } + + ... + +} + +``` + +`BeanFactory` µÄÏà¹Ø·½·¨ÊµÏÖÈçÏ£º + +``` +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext {### 1\. ʲôÊÇ `BeanDefinition` + +`BeanDefinition` ´ÓÃû³ÆÉÏÀ´¿´£¬¾ÍÊÇ `bean¶¨Òå`£¬ÓÃÀ´¶¨Òå spring bean µÄÐÅÏ¢¡£ + +ÔÚ java ÖУ¬¶¨ÒåÒ»¸öÀàµÄÔªÐÅÏ¢£¨¹¹Ôì·½·¨£¬³ÉÔ±±äÁ¿£¬³ÉÔ±·½·¨µÈ£©£¬Ê¹ÓõÄÊÇ `Class` À࣬һ¸ö`.class` Îļþ¼ÓÔØµ½ jvm ºó£¬¶¼»áÉú³ÉÒ»¸ö `Class` ¶ÔÏó£¬ÔÚ¶ÔÏóʵÀý»¯Ê±£¬¾Í¸ù¾ÝÕâ¸ö `Class` ¶ÔÏóµÄÐÅÏ¢À´Éú³É¡£ + +ÔÚ spring ÖУ¬Ò²ÓÐÕâôһ¸öÀàÀ´¶¨Òå bean µÄÐÅÏ¢£¬Õâ¸öÀà¾ÍÊÇ `BeanDefinition`£¬Ëü¶¨ÒåÁË spring bean ÈçºÎÉú³É£¬ÈçºÎ³õʼ»¯£¬ÈçºÎÏú»ÙµÈ£¬ÎÒÃÇÀ´¿´¿´ËüÖ§³ÖµÄ²¿·Ö·½·¨£º + +``` +public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { + + /** + * ÉèÖø¸ BeanDefinition + * BeanDefinition ÓиöÀàËÆ¼Ì³ÐµÄ¸ÅÄÕâÀïÖ¸¶¨Á˸¸BeanDefinition + * ʵÀý»¯beanʱ£¬»áºÏ²¢¸¸BeanDefinition + */ + void setParentName(@Nullable String parentName); + + /** + * »ñÈ¡¸¸Bean + */ + @Nullable + String getParentName(); + + /** + * ÉèÖÃbeanClassÃû³Æ + * ʵÀý»¯Ê±£¬»áʵÀý»¯³ÉÕâ¸ö Class µÄ¶ÔÏó + */ + void setBeanClassName(@Nullable String beanClassName); + + /** + * »ñÈ¡beanClassÃû³Æ + */ + @Nullable + String getBeanClassName(); + + /** + * ÉèÖÃbeanµÄ×÷Ó÷¶Î§£¬µ¥Àý»òÔ­ÐÍ + */ + void setScope(@Nullable String scope); + + /** + * »ñÈ¡beanµÄ×÷Ó÷¶Î§£¬µ¥Àý»òÔ­ÐÍ + */ + @Nullable + String getScope(); + + /** + * ÉèÖÃÀÁ¼ÓÔØ + */ + void setLazyInit(boolean lazyInit); + + /** + * ÊÇ·ñΪÀÁ¼ÓÔØ + */ + boolean isLazyInit(); + + /** + * ÉèÖøÃBeanÒÀÀµµÄËùÓÐBean + * ¼´ @DependsOn Ö¸¶¨µÄbean + */ + void setDependsOn(@Nullable String... dependsOn); + + /** + * ·µ»Ø¸ÃBeanµÄËùÓÐÒÀÀµ¡£ + */ + @Nullable + String[] getDependsOn(); + + /** + * ÉèÖÃÊÇ·ñ×÷Ϊ×Ô¶¯×¢ÈëµÄºòÑ¡¶ÔÏó¡£ + */ + void setAutowireCandidate(boolean autowireCandidate); + + /** + * ÊÇ·ñ×÷Ϊ×Ô¶¯×¢ÈëµÄºòÑ¡¶ÔÏó + */ + boolean isAutowireCandidate(); + + /** + * ÉèÖÃÊÇ·ñΪÖ÷ÒªµÄ£¬µ±ÈÝÆ÷ÖдæÔÚ¶à¸öͬÀàÐ͵Äbeanʱ£¬¿ÉÒÔÖ»·µ»ØÖ÷ÒªµÄ + * ¾ÍÊÇ @Primary µÄ×÷Óà + */ + void setPrimary(boolean primary); + + /** + * ÊÇ·ñΪÖ÷ÒªµÄbean + */ + boolean isPrimary(); + + /** + * Õë¶ÔfactoryBean + * Ö¸¶¨factoryBeanµÄÃû³Æ + */ + void setFactoryBeanName(@Nullable String factoryBeanName); + + /** + * Õë¶ÔfactoryBean + * »ñÈ¡factoryBeanµÄÃû³Æ + */ + @Nullable + String getFactoryBeanName(); + + /** + * ÉèÖù¤³§·½·¨µÄÃû³Æ + * ÀýÈç @Bean ±ê¼ÇµÄ·½·¨ + */ + void setFactoryMethodName(@Nullable String factoryMethodName); + + /** + * ·µ»Ø¹¤³§·½·¨µÄÃû³Æ + * ÀýÈç @Bean ±ê¼ÇµÄ·½·¨ + */ + @Nullable + String getFactoryMethodName(); + + /** + * »ñÈ¡¹¹Ôì¾ÍÈ¥µÄ²ÎÊýÖµ + */ + ConstructorArgumentValues getConstructorArgumentValues(); + + /** + * ¹¹Ôì·½·¨ÊÇ·ñÓвÎÊý + */ + default boolean hasConstructorArgumentValues() { + return !getConstructorArgumentValues().isEmpty(); + } + + /** + * »ñÈ¡ÊôÐÔÖµ + * ÕâÀï¿ÉÒÔ×ÔÖ÷ÉèÖÃÖµ×÷Ϊ¹¹Ôì·½·¨¡¢¹¤³§·½·¨µÄ²ÎÊý + */ + MutablePropertyValues getPropertyValues(); + + /** + * ÊÇ·ñÓÐÊôÐÔÖµ + */ + default boolean hasPropertyValues() { + return !getPropertyValues().isEmpty(); + } + + /** + * ÉèÖóõʼ»¯·½·¨Ãû³Æ + */ + void setInitMethodName(@Nullable String initMethodName); + + /** + * »ñÈ¡³õʼ»¯·½·¨Ãû³Æ + */ + @Nullable + String getInitMethodName(); + + /** + * ÉèÖÃÏú»Ù·½·¨Ãû³Æ + */ + void setDestroyMethodName(@Nullable String destroyMethodName); + + /** + * »ñÈ¡Ïú»Ù·½·¨Ãû³Æ + */ + @Nullable + String getDestroyMethodName(); + + /** + * ÊÇ·ñΪµ¥Àýbean + */ + boolean isSingleton(); + + /** + * ÊÇ·ñΪԭÐÍbean + */ + boolean isPrototype(); + + /** + * Èç¹ûÕâ¸ö Bean ÊDZ»ÉèÖÃΪ abstract£¬ÄÇô²»ÄÜʵÀý»¯£¬³£ÓÃÓÚ×÷Ϊ ¸¸bean ÓÃÓڼ̳Р+ */ + boolean isAbstract(); + + ... + +} + +``` + +¿ÉÒÔ¿´µ½£¬`BeanDefinition` Ö§³ÖµÄ·½·¨·Ç³£¶à£¬ÓкܶàÊÇÎÒÃÇÆ½Ê±Ê¹ÓÃʱָ¶¨µÄ£º + +* `setScope(...)`£ºÉèÖà bean µÄ×÷Ó÷¶Î§£¬ÓÉ `@Scope` Ö¸¶¨ +* `setLazyInit(...)`£ºÉèÖÃÀÁ¼ÓÔØ£¬ÓÉ `@Lazy` Ö¸¶¨ +* `setDependsOn(...)`£ºÉèÖà bean ÒÀÀµ£¬ÓÉ `@DependsOn` Ö¸¶¨ +* `setPrimary(...)`£ºÉèÖÃΪÖ÷Òª bean£¬ÓÉ `@Primary` Ö¸¶¨ +* `setFactoryMethodName(...)`£ºÉèÖù¤³§·½·¨Ãû³Æ£¬ÀýÈçÓÉ `@Bean` ±ê¼ÇµÄ·½·¨ + +ÒÔÉÏÊÇÔÚ×¢½âʱ´ú¿ÉÒÔÖ¸¶¨µÄ£¬»¹ÓÐЩÊÇÔÚ `xml` ʱ´úÖ¸¶¨µÄ£¬È磺 + +* `setInitMethodName(...)`£ºÉèÖóõʼ»¯·½·¨ £¬ÓÉ `init-method` Ö¸¶¨ +* `setDestroyMethodName(...)`£ºÉèÖÃÏú»Ù·½·¨£¬ÓÉ `destroy-method` Ö¸¶¨ + +### 2\. spring ÌṩÁËÄÄЩ `BeanDefinition` + +`BeanDefinition` ÊÇÒ»¸ö½Ó¿Ú£¬ÎÒÃǵ±È»²»ÄÜÖ±½ÓʹÓ㬽ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ Spring ÌṩÁËÄÄЩ `BeanDefinition`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-49cbc9cb32badc1db52717cd19a9447eca7.png) + +spring ÌṩµÄ `BeanDefinition` »ù±¾¾ÍÊÇÉÏͼËùʾµÄ¼¸ÖÖÁË£¬ÕâÀïÎÒÃÇÖ÷Òª¿´ÕâÏòÖÖ£º + +* `RootBeanDefinition` +* `ChildBeanDefinition` +* `GenericBeanDefinition` +* `ScannedGenericBeanDefinition` +* `AnnotatedGenericBeanDefinition` + +#### 2.1 `RootBeanDefinition` ºÍ `ChildBeanDefinition` + +ÔÚÇ°ÃæÌáµ½ÁË `BeanDefinition` ¼Ì×ӵĸÅÄÕâÀï¾ÍÊÇÓÃÀ´´¦Àí¼Ì³ÐµÄ£¬Ò»°ãÀ´Ëµ£¬ÎÒÃÇ¿ÉÒÔÔÚ `RootBeanDefinition` ¶¨Ò幫¹²²ÎÊý£¬È»ºóÔÚ `ChildBeanDefinition` Öж¨Òå¸÷×ÔµÄÄÚÈÝ£¬Ê¾ÀýÈçÏ£º + +``` +public static void main(String[] args) { +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +// RootBeanDefinition +RootBeanDefinition root = new RootBeanDefinition(); +root.setBeanClass(User.class); +root.getPropertyValues().add("name", "123"); +// ÕâÖÖ×¢²á·½·¨½ö×öչʾ£¬Êµ¼ÊÏîÄ¿Öв»½¨ÒéʹÓà +// ʹÓÃÏîÄ¿½¨ÒéʹÓÃµÄ BeanDefinitionRegistryPostProcessor ÌṩµÄ·½·¨ +context.registerBeanDefinition("root", root); + + // ChildBeanDefinition + ChildBeanDefinition child1 = new ChildBeanDefinition("root"); + child1.getPropertyValues().add("age", "11"); + // ÕâÖÖ×¢²á·½·¨½ö×öչʾ£¬Êµ¼ÊÏîÄ¿Öв»½¨ÒéʹÓà + // ʹÓÃÏîÄ¿½¨ÒéʹÓÃµÄ BeanDefinitionRegistryPostProcessor ÌṩµÄ·½·¨ + context.registerBeanDefinition("child1", child1); + + // ChildBeanDefinition + ChildBeanDefinition child2 = new ChildBeanDefinition("root"); + child2.getPropertyValues().add("age", "12"); + // ÕâÖÖ×¢²á·½·¨½ö×öչʾ£¬Êµ¼ÊÏîÄ¿Öв»½¨ÒéʹÓà + // ʹÓÃÏîÄ¿½¨ÒéʹÓÃµÄ BeanDefinitionRegistryPostProcessor ÌṩµÄ·½·¨ + context.registerBeanDefinition("child2", child2); + // Æô¶¯ÈÝÆ÷ + context.refresh(); + + User rootUser = (User) context.getBean("root"); + User child1User = (User) context.getBean("child1"); + User child2User = (User) context.getBean("child2"); + System.out.println(rootUser); + System.out.println(child1User); + System.out.println(child2User); +} + +``` + +ÔËÐнá¹û£º + +``` +User{name='123', age=null} +User{name='123', age=11} +User{name='123', age=12} + +``` + +¿ÉÒÔ¿´µ½£¬`child1` Óë `child1` ½øÐгɹ¦µØ´Ó `RootBeanDefinition` ¼Ì³Ðµ½ÁËÊôÐÔ¡£ + +#### 2.2 `GenericBeanDefinition` + +ÕâÊǸöͨÓÃµÄ `BeanDefinition`£¬Ö±½Ó¼Ì³ÐÁË `AbstractBeanDefinition`£¬Ëü×ÔÉíÌṩµÄ·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-5ed980070b301dc84926fac2494093d4fc6.png) + +¿ÉÒÔ¿´µ½£¬Ëü×ÔÉíÌṩµÄ·½·¨²¢²»¶à£¬Æä²Ù×÷»ù±¾¼Ì³Ð `AbstractBeanDefinition`£¬Ò»°ãÇé¿öÏ£¬ÎÒÃÇÒªÉú³É×Ô¼ºµÄ `BeanDefinition` ʱ£¬Ö»ÐèҪʹÓÃÕâ¸öÀà¾Í¿ÉÒÔÁË£¬ÕâÀïÒ²Ìṩһ¸öʾÀý£º + +``` +public static void main(String[] args) { +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition(); + userBeanDefinition.setBeanClass(User.class); + userBeanDefinition.getPropertyValues().add("name", "123"); + userBeanDefinition.getPropertyValues().add("age", "11"); + // ÕâÖÖ×¢²á·½·¨½ö×öչʾ£¬Êµ¼ÊÏîÄ¿Öв»½¨ÒéʹÓà + // ʹÓÃÏîÄ¿½¨ÒéʹÓÃµÄ BeanDefinitionRegistryPostProcessor ÌṩµÄ·½·¨ + context.registerBeanDefinition("user", userBeanDefinition); + + // Æô¶¯ÈÝÆ÷ + context.refresh(); + + User user = (User) context.getBean("user"); + System.out.println(user); +} + +``` + +### 2.3 `ScannedGenericBeanDefinition` + +`ScannedGenericBeanDefinition` ¼Ì³ÐÁË `GenericBeanDefinition`£¬Í¬Ê±Ò²ÊµÏÖÁË `AnnotatedBeanDefinition` ½Ó¿Ú£¬±¾ÉíÌṩµÄ·½·¨²¢²»¶à£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9ed3925090a9ae5fc1d4218ed5a59c2ea19.png) + +Æä²Ù×÷»ù±¾À´×Ô `GenericBeanDefinition`£¬ÕâÀï¾Í²»ÌṩʾÀýÁË¡£ + +### 2.4 `AnnotatedGenericBeanDefinition` + +`AnnotatedGenericBeanDefinition` ¼Ì³ÐÁË `GenericBeanDefinition`£¬Í¬Ê±Ò²ÊµÏÖÁË `AnnotatedBeanDefinition` ½Ó¿Ú£¬±¾ÉíÌṩµÄ·½·¨²¢²»¶à£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b2e7f49fcdcae4f7272e2670b4fbb92766a.png) + +Æä²Ù×÷»ù±¾À´×Ô `GenericBeanDefinition`£¬ÕâÀï¾Í²»ÌṩʾÀýÁË¡£ + +### 3\. ²Ù×÷ spring ÈÝÆ÷ÖÐÒÑÓÐµÄ `BeanDefinition` + +ÉÏÃæµÄÀý×Ó¶¼ÊÇÍù spring ÈÝÆ÷ÖÐÌí¼Ó `BeanDefinition`£¬ÎÒÃÇÒªÈçºÎ²Ù×÷ spring ÈÝÆ÷ÖÐÒÑÓÐµÄ `BeanDefinition` ÄØ£¿ + +#### 3.1 demo ×¼±¸ + +ÕâÀïÊ×ÏÈ×¼±¸Ò»¸ö demo£º + +Ê×ÏÈ×¼±¸Á½¸ö `service`£º + +``` +@Service +public class Service01 { + + private String name; + + public void hello() { + System.out.println("hello " + name + ", from service01"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +@Service +public class Service02 { + + private String name; + + public void hello() { + System.out.println("hello " + name + ", from service02"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +``` + +½Ó×ÅÊÇÖ÷ÒªÀࣺ + +``` +@ComponentScan +public class Demo02Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context + = new AnnotationConfigApplicationContext(); + context.register(Demo02Main.class); + context.refresh(); + + Service01 service01 = (Service01) context.getBean("service01"); + Service02 service02 = (Service02) context.getBean("service02"); + service01.hello(); + service02.hello(); + + } +} + +``` + +ÔËÐÐ £¬½á¹ûÈçÏ£º + +``` +hello null, from service01 +hello null, from service02 + +``` + +Õâ˵Ã÷ÎÒÃǵÄÈÝÆ÷ÒѾ­Æô¶¯³É¹¦ÁË£¬`service01` Óë `service02` Ò²³õʼ»¯³É¹¦ÁË¡£ + +#### 3.2 ²»³É¹¦µÄ³¢ÊÔ + +ÕâÀïÎÒÃÇ·´ÍÆÏ£¬`service01` Óë `service02` ÒѾ­³õʼ»¯³É¹¦ÁË£¬¾Í˵Ã÷ÈÝÆ÷ÖбØÈ» `service01` Óë `service02` ¶ÔÓ¦µÄ `beanDefifnition`£¬ÎÒÃÇÏë²Ù×÷Õâ¸ö `beanDefifnition`£¬¾Í±ØÐëÒªÏÈ»ñÈ¡Õâ¸ö `beanDefifnition`¡£ + +ÈçºÎ»ñÈ¡ spring ÖÐÒѾ­´æÔÚµÄ `beanDefifnition` ÄØ£¿²Î¿¼µÚ 2 ½ÚµÄʾÀý£¬Èç¹ûÄãÈÏΪÔÚ `context.refresh()` ǰ»ñÈ¡£¬ÏñÕâÑù£º + +``` +public static void main(String[] args) { +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +context.register(Demo02Main.class); +// ÔÚÕâÀï»ñÈ¡ beanDefinition£¬»á±¨´í +BeanDefinition service01Bd = context.getBeanDefinition("service01"); +service01Bd.getPropertyValues().addPropertyValue("name", "123"); + + context.refresh(); + + Service01 service01 = (Service01) context.getBean("service01"); + Service02 service02 = (Service02) context.getBean("service02"); + service01.hello(); + service02.hello(); +} + +``` + +ÔËÐУ¬·¢Ïֻᱨ´í£º + +``` +Exception in thread "main" org.springframework.beans.factory +.NoSuchBeanDefinitionException: No bean named 'service01' available + +``` + +´ÏÃ÷ÈçÄ㣬һ¶¨»áÏëµ½£¬ÔÚ `context.refresh()` ǰ»ñÈ¡»á±¨´í£¬ÄÇÔÚÖ®ºóÄØ£¿´úÂëÏñÕâÑù£º + +``` +public static void main(String[] args) { +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +context.register(Demo02Main.class); +context.refresh(); + + // »ñÈ¡ beanDefinition£¬Ð޸IJ»Æð×÷Óà + BeanDefinition service01Bd = context.getBeanDefinition("service01"); + service01Bd.getPropertyValues().addPropertyValue("name", "123"); + + Service01 service01 = (Service01) context.getBean("service01"); + Service02 service02 = (Service02) context.getBean("service02"); + service01.hello(); + service02.hello(); + +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-268bb546197c4eb9d1686118a12fabb0009.png) + +ȷʵûÓб¨´í£¬µ«ÊÇÎÒÃǵÄÐÞ¸ÄҲûÆð×÷Óá£ÔÚ´úÂëÀÎÒÃǸø `service01` µÄ `name` ÊôÐÔÖ¸¶¨ÖµÎª `123`£¬ÔËÐнá¹û»¹ÊÇ `null`£¬Ã»Æð×÷ÓõÄÔ­ÒòÊÇ `service01` ÊÇÔÚ `context.refresh()` ½øÐгõʼ»¯ µÄ£¬ºóÃæÔÙÔõô¶ÔËüµÄ `BeanDefinition` Ð޸ģ¬Ò²ÌåÏÖ²»µ½ËüÉíÉÏ¡£ + +ÄÇô¾¿¾¹ÒªÔõô×öÄØ£¿ + +#### 3.2 `BeanDefinitionRegistryPostProcessor`£º¶¨ÖÆ»¯ `beanDefinition` + +½ÓÏÂÀ´ÎÒÃÇÒª·Å³ö´óÕÐÁË£¬Õâ¸ö´óÕоÍÊÇ£º`BeanFactoryPostProcessor`£¬¹ØÓÚÕâ¸öµÄ½éÉÜ£¬¿ÉÒÔËüµÄ½éÉÜ£¬¿ÉÒԲο¼ [spring ×é¼þÖ® BeanFactoryPostProcessor](https://my.oschina.net/funcy/blog/4597545)£¬ÕâÀïÖ±½Ó¸øÏ½áÂÛ£º + +> `BeanFactoryPostProcessor` ÖÐÎÄÃû½Ð `spring beanFactory µÄºóÖô¦ÀíÆ÷`£¬¿ÉÒÔÓÃÀ´¶¨ÖÆ»¯ `beanFactory` µÄһЩÐÐΪ¡£spring ΪÎÒÃÇÌṩÁËÁ½ÖÖ `BeanFactoryPostProcessor`£º +> +> * `BeanFactoryPostProcessor`£º¶¨ÖÆ»¯ `beanFactory` µÄÐÐΪ +> * `BeanDefinitionRegistryPostProcessor`£º¶¨ÖÆ»¯ `beanDefinition` µÄÐÐΪ + +ºÜÃ÷ÏÔ£¬ÎÒÃÇÓ¦¸ÃʹÓà `BeanDefinitionRegistryPostProcessor`£¬Ö±½ÓʵÏÖÕâ¸ö½Ó¿Ú£º + +``` +@Component +public class MyBeanDefinitionRegistryPostProcessor +implements BeanDefinitionRegistryPostProcessor { + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + throws BeansException { + BeanDefinition service01Bd = registry.getBeanDefinition("service01"); + service01Bd.getPropertyValues().addPropertyValue("name", "123"); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { + // BeanDefinitionRegistryPostProcessor ÊÇ BeanFactoryPostProcessor µÄ×Ó½Ó¿Ú + // postProcessBeanFactory(...) À´×Ô BeanFactoryPostProcessor£¬ÎÒÃÇÕâÀï²»´¦Àí + } +} + +``` + +`main` ·½·¨¸ú×î³õ±£³ÖÒ»Ö£º + +``` +public static void main(String[] args) { +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +context.register(Demo02Main.class); +context.refresh(); + + Service01 service01 = (Service01) context.getBean("service01"); + Service02 service02 = (Service02) context.getBean("service02"); + service01.hello(); + service02.hello(); + +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +hello 123, from service01 +hello null, from service02 + +``` + +¿ÉÒÔ¿´µ½ `service01` µÄ `name` ȷʵ±ä³É `123` ÁË¡£ + +ʵ¼ÊÉÏ£¬`BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` Ö÷Òª¾ÍÊÇʹÓà `BeanDefinitionRegistry` À´Íê³É `BeanDefinition` µÄ²Ù×÷£¬ËüÖ§³ÖµÄ·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-5812a2cac994c5940d57c7e6ab55c23a63e.png) + +ÖØÒª²Ù×÷ÈçÏ£º + +* `getBeanDefinition(...)`£º»ñÈ¡ `BeanDefinition`£¬´æÔÚÔò·µ»Ø£¬²»´æÔÚÔò±¨´í£¬µÃµ½ `BeanDefinition` ºó£¬¾Í¿ÉÒÔ¶ÔÆä½øÐи÷ÖÖ²Ù×÷ÁË +* `registerBeanDefinition(...)`£º×¢²á `BeanDefinition`£¬¿ÉÒÔ×Ô¶¨Òå `BeanDefinition` ¶ÔÏó£¬È»ºóµ÷Óø÷½·¨×¢²áµ½ÈÝÆ÷ÖУ¬Ç°ÃæµÄÀý×ÓÖУ¬ÎÒÃÇÊÇÔÚ `context.refresh()` ǰµ÷Óà `context.registerBeanDefinition`£¬ÕâÀïÐèҪ˵µÄÊÇÍüÁËÇ°ÃæµÄ·½·¨°É£¬²¢²»ÊÇËùÓеÄÇé¿öÏ£¬¶¼ÄÜÄõ½ `context.refresh()` µÄ `context`£¨ÀýÈç `springboot` ÖУ©£¬Òò´ËÍÆ¼öʹÓà `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` ·½·¨×¢²á `BeanDefinition` +* `removeBeanDefinition(...)`£ºÒƳý `BeanDefinition`£¬Ò»°ãÇé¿öÏÂÓ¦¸Ã²»»áÓõ½ +* `containsBeanDefinition(...)`£ºÅжÏÊÇ·ñ°üº¬Ä³¸ö `BeanDefinition` + +### 4\. ×ܽá + +±¾ÎÄÖ÷Òª½éÉÜÁË `BeanDefinition` µÄ×÷Ó㺠+ +1. `BeanDefinition` µÄ·½·¨ +2. ¼¸¸öÀàÐ굀 `BeanDefinition` µÄʹÓà +3. ʹÓà `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` ²Ù×÷ `BeanDefinition`£¬Ö÷ÒªÊÇ×¢²á¡¢ÐÞ¸Ä `BeanDefinition` + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4597536](https://my.oschina.net/funcy/blog/4597536) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ + + ... + @Override + public T getBean(Class requiredType) throws BeansException { + assertBeanFactoryActive(); + return getBeanFactory().getBean(requiredType); + } + + @Override + public T getBean(Class requiredType, Object... args) throws BeansException { + assertBeanFactoryActive(); + return getBeanFactory().getBean(requiredType, args); + } + + ... +} + +``` + +ÕâЩ·½·¨ÔÚʵÏÖʱ£¬¶¼Êǵ÷Óà `getBeanFactory()` »ñÈ¡µ½ `BeanFactory` ¶ÔÏó£¬È»ºóÖ±½Óµ÷Óà `BeanFactory` µÄ·½·¨£¬`getBeanFactory()` µ÷ÓõľÍÊÇ `GenericApplicationContext` »ò `AnnotationConfigWebApplicationContext` µÄ `getBeanFactory()` ·½·¨¡£ + +¹ØÓÚ `ApplicationContext` µÄÄÚÈݾͽéÉܵ½ÕâÀïÁË¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4597456](https://my.oschina.net/funcy/blog/4597456) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanDefinition.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanDefinition.md" new file mode 100644 index 0000000..e69de29 diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" new file mode 100644 index 0000000..7700641 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactory.md" @@ -0,0 +1,88 @@ +public interface BeanFactory { + + /** + * factoryBeanʹÓà + */ + String FACTORY_BEAN_PREFIX = "&"; + + /** + * ¸ù¾ÝÃû³Æ»ñÈ¡bean + */ + Object getBean(String name) throws BeansException; + + /** + * ¸ù¾ÝÃû³Æ»ñÈ¡bean + */ + T getBean(String name, Class requiredType) throws BeansException; + + /** + * ¸ù¾ÝÃû³Æ»ñÈ¡bean + */ + Object getBean(String name, Object... args) throws BeansException; + + /** + * ¸ù¾ÝÀàÐÍ»ñÈ¡bean + */ + T getBean(Class requiredType) throws BeansException; + + /** + * ¸ù¾ÝÀàÐÍ»ñÈ¡bean + */ + T getBean(Class requiredType, Object... args) throws BeansException; + + /** + * »ñÈ¡BeanProvider + */ + ObjectProvider getBeanProvider(Class requiredType); + + /** + * »ñÈ¡BeanProvider + */ + ObjectProvider getBeanProvider(ResolvableType requiredType); + + /** + * ÊÇ·ñ°üº¬bean + */ + boolean containsBean(String name); + + /** + * ÊÇ·ñΪµ¥Àýbean + */ + boolean isSingleton(String name) throws NoSuchBeanDefinitionException; + + /** + * ÊÇ·ñΪԭÐÍbean + */ + boolean isPrototype(String name) throws NoSuchBeanDefinitionException; + + /** + * ÅжÏÀàÐÍÊÇ·ñÆ¥Åä + */ + boolean isTypeMatch(String name, ResolvableType typeToMatch) + throws NoSuchBeanDefinitionException; + + /** + * ÅжÏÀàÐÍÊÇ·ñÆ¥Åä + */ + boolean isTypeMatch(String name, Class typeToMatch) + throws NoSuchBeanDefinitionException; + + /** + * ¸ù¾ÝÃû³Æ»ñÈ¡beanµÄÀàÐÍ + */ + @Nullable + Class getType(String name) throws NoSuchBeanDefinitionException; + + /** + * ¸ù¾ÝÃû³Æ»ñÈ¡beanµÄÀàÐÍ + */ + @Nullable + Class getType(String name, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException; + + /** + * ¸ù¾ÝbeanÃû³Æ»ñÈ¡beanµÄ±ðÃû + */ + String[] getAliases(String name); + +} diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" new file mode 100644 index 0000000..121dfeb --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanFactoryPostProcessor.md" @@ -0,0 +1,129 @@ +### 1\. ʲôÊÇ `BeanFactoryPostProcessor` + +`BeanFactoryPostProcessor` ÖÐÎÄÃû½Ð spring beanFactory µÄºóÖô¦ÀíÆ÷£¬¿ÉÒÔÓÃÀ´¶¨ÖÆ»¯ beanFactory µÄһЩÐÐΪ¡£ + +spring ΪÎÒÃÇÌṩÁËÁ½ÖÖ `BeanFactoryPostProcessor`£º + +* `org.springframework.beans.factory.config.BeanFactoryPostProcessor` + + ``` + /** + * beanFactory µÄºóÖô¦ÀíÆ÷£¬¿ÉÒԸıä beanFactory µÄһЩÐÐΪ + */ + public interface BeanFactoryPostProcessor { + + /** + * ´¦Àí beanFactory µÄ·½·¨£¬²ÎÊýΪ beanFactory£¬Êµ¼ÊÀàÐÍÊÇ DefaultListableBeanFactory + */ + void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException; + + } + + ``` + +* `org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor` + + ``` + /** + * BeanDefinition ×¢²áÆ÷£¬´ÓÃû³ÆÀ´¿´£¬¾ÍÊÇÓÃÀ´×¢²á beanDefinition µÄ + * ¼Ì³ÐÁË BeanFactoryPostProcessor ½Ó¿Ú£¬ + * Ò²¿ÉÒÔÖØÐ´ BeanFactoryPostProcessor#postProcessBeanFactory ·½·¨ + * + */ + public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { + + /** + * 1\. ¸Ã·½·¨ÏÈÓÚ BeanDefinitionRegistryPostProcessor#postProcessBeanFactory Ö´ÐÐ + * 2\. ´«Èë²ÎÊýΪ registry£¬Êµ¼ÊÀàÊÇ DefaultListableBeanFactory£¬Ò²¿ÉÒÔʹÓà beanFactory µÄ²Ù×÷ + */ + void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + throws BeansException; + + } + + ``` + +`BeanFactoryPostProcessor` ÔÚ `AbstractApplicationContext#invokeBeanFactoryPostProcessors` ·½·¨Öб»Ö´ÐУ¬Ö´ÐÐʱÏÈÖ´ÐÐ `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry`£¬ÔÙÖ´ÐÐ `BeanFactoryPostProcessor#postProcessBeanFactory`£¬¹ØÓÚÕâ¿éµÄ·ÖÎö£¬¿ÉÒԲο¼ [spring Æô¶¯Á÷³ÌÖ®Ö´ÐÐ BeanFactoryPostProcessor](https://my.oschina.net/funcy/blog/4641114) Ò»ÎÄ¡£ + +### 2. `BeanFactoryPostProcessor` ÌṩµÄ¹¦ÄÜ + +±¾½ÚÎÒÃÇÀ´½éÉÜ `BeanFactoryPostProcessor` ÌṩµÄ¹¦ÄÜ¡£ + +#### 2.1 `BeanFactoryPostProcessor` µÄ×÷Óà + +̽ÌÖǰ£¬ÎÒÃÇÏÈÀ´Á˽âÏ `BeanFactoryPostProcessor` Ìṩ·½·¨£º + +``` +void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException; + +``` + +¸Ã·½·¨ÖÐÖ»ÓÐÒ»¸ö²ÎÊý£º`ConfigurableListableBeanFactory`£¬ÎÒÃÇÏëÁ˽â `BeanFactoryPostProcessor` ÄÜΪÎÒÃÇ×öʲô£¬ÐèÒªÖªµÀÕâ¸ö²ÎÊýÌṩÁËÄÄЩ¹¦ÄÜ£º + +ÎÒÃÇÏÈÀ´¿´¿´ËüµÄ `set` ·½·¨£º ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-63b39c81bcae0b10c60a2f847c6b47af932.png) + +³ý´ËÖ®Í⣬»¹ÓÐ `register` ·½·¨£º ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-50c48da9b50dcf18abcd99db09142644c6c.png) + +ÓÐÁËÕâЩ·½·¨£¬ÎÒÃǾͿÉÒÔ¶¨ÖÆ»¯ `beanFactory` µÄһЩÐÐΪÁË¡£ + +#### 2.2 `BeanDefinitionRegistryPostProcessor` µÄ×÷Óà + +ÎÒÃÇÒ²À´Á˽âÏ `BeanDefinitionRegistryPostProcessor` ÌṩµÄ·½·¨£º + +``` +void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + throws BeansException; + +``` + +Õâ¸ö·½·¨µÄ²ÎÊýÊÇ `BeanDefinitionRegistry`£¬´Ó×ÖÃæÒâÒåÀ´¿´£¬ÕâÊǸö `BeanDefinition ×¢²áÆ÷`£¬ËüÌṩÁËÈçÏ·½·¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-5812a2cac994c5940d57c7e6ab55c23a63e.png) + +¿ÉÒÔ¿´µ½£¬Õâ¸ö²ÎÊýÖ÷Òª¾ÍÊÇÎ§ÈÆ `BeanDefinition` À´²Ù×÷£¬±È½ÏÖØÒªµÄ·½·¨ÁоÙÈçÏ£º + +* `BeanDefinitionRegistry#containsBeanDefinition`£ºÊÇ·ñ°üº¬Ö¸¶¨Ãû³ÆµÄ `BeanDefinition` +* `BeanDefinitionRegistry#getBeanDefinition`£º»ñȡָ¶¨Ãû³ÆµÄ `BeanDefinition` +* `BeanDefinitionRegistry#registerBeanDefinition`£º×¢²áÒ»¸ö `BeanDefinition` +* `BeanDefinitionRegistry#removeBeanDefinition`£ºÒƳý `BeanDefinition` + +### 3\. spring ÌṩµÄ `BeanFactoryPostProcessor` + +spring ÖÐÒ»¹²ÓÐÁ½¸ö `BeanFactoryPostProcessor` µÄʵÏÖÀ࣬Ïà¹ØÐÅÏ¢¼°×÷ÓÃÈçÏ£º + +* `EventListenerMethodProcessor`£º + + * ʵÏÖÁË `BeanFactoryPostProcessor`£¬`SmartInitializingSingleton` ½Ó¿Ú + * ÔÚ `BeanDefinitionRegistryPostProcessor#postProcessBeanFactory` ·½·¨ÖлñÈ¡ÁË `EventListenerFactory` + * ÔÚ `SmartInitializingSingleton#afterSingletonsInstantiated` ·½·¨Öд¦Àí `@EventListener` ×¢½â +* `ConfigurationClassPostProcessor`£º + + * ÔÚ `AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)` ·½·¨ÖÐ×¢²á + * ʵÏÖÁË `BeanDefinitionRegistryPostProcessor` ½Ó¿Ú + * ´¦Àí `@Conditional` ×¢½â + * ´¦Àí `@Component` ×¢½â + * ´¦Àí `@PropertySource/@PropertySources` ×¢½â + * ´¦Àí `@ComponentScan/@ComponentScans` ×¢½â + * ´¦Àí `@Import` ×¢½â + * ´¦Àí `@ImportResource` ×¢½â + * ´¦Àí `@Bean` ×¢½â + * ´¦Àí `@Configuration` ×¢½â + +¹ØÓÚ `EventListenerMethodProcessor` ´¦Àí `@EventListener` µÄ·ÖÎö£¬¿ÉÒԲο¼[¡¾spring Ô´Âë·ÖÎö¡¿spring Ì½ÃØÖ®¼àÌýÆ÷×¢½â @EventListener](https://my.oschina.net/funcy/blog/4926344). + +¹ØÓÚ `ConfigurationClassPostProcessor` ´¦Àí¸÷×¢½âµÄÁ÷³Ì£¬¿ÉÒԲο¼£º + +* [ConfigurationClassPostProcessor£¨Ò»£©£º´¦Àí @ComponentScan ×¢½â](https://my.oschina.net/funcy/blog/4836178) +* [ConfigurationClassPostProcessor£¨¶þ£©£º´¦Àí @Bean ×¢½â](https://my.oschina.net/funcy/blog/4492878) +* [ConfigurationClassPostProcessor£¨Èý£©£º´¦Àí @Import ×¢½â](https://my.oschina.net/funcy/blog/4678152) +* [ConfigurationClassPostProcessor£¨ËÄ£©£º´¦Àí @Conditional ×¢½â](https://my.oschina.net/funcy/blog/4873444) + +### 4\. ×ܽá + +±¾ÎĽéÉÜÁË `BeanFactoryPostProcessor` µÄ¸ÅÄ¾ÙÀý˵Ã÷ÁË `BeanFactoryPostProcessor` µÄʹÓã¬ÒÔ¼°½éÉÜÁË spring ÌṩµÄÁ½¸ö `BeanFactoryPostProcessor` ʵÏÖÀࣺ`EventListenerMethodProcessor` Óë `ConfigurationClassPostProcessor`¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4597545](https://my.oschina.net/funcy/blog/4597545) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" new file mode 100644 index 0000000..8bed1ab --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\347\273\204\344\273\266\345\210\206\346\236\220/Spring\347\273\204\344\273\266\344\271\213BeanPostProcessor.md" @@ -0,0 +1,132 @@ +`BeanPostProcessor` ÖÐÎÄÃûΪ spring bean µÄºóÖô¦ÀíÆ÷£¬Çø±ðÓÚ `BeanFactoryPostProcessor`£¬`BeanPostProcessor` ¿ÉÒÔ¶Ô bean ½øÐвÙ×÷¡£ + +spring `BeanPostProcessor` ÔÚ bean µÄ´´½¨¹ý³ÌÖÐÖ´ÐУ¬Ö´ÐÐʱ»úÈçÏÂ: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b7a4d4bc1bfbd76537e40cf843b0d18df93.png) + +ÔÚ bean µÄ´´½¨¹ý³ÌÖУ¬`BeanPostProcessor` Ò»¹²Ö´Ðйý 8 ´Î£º + +1. ³¢ÊÔÉú³É´úÀí¶ÔÏó +2. ÍÆ¶Ï¹¹Ôì·½·¨ +3. »ñȡעÈëµÄÊôÐÔ +4. Ìí¼ÓÈý¼¶»º´æ +5. ÊÇ·ñÐèҪעÈëÊôÐÔ +6. Ìî³äÊôÐÔ +7. ³õʼ»¯Ç° +8. ³õʼ»¯ºó + +±¾À´½«»áÖðÒ»ÊáÀíÕâЩ¹ý³ÌÖÐÖ´ÐÐµÄ `BeanPostProcessor`¡£ + +### 1\. ʲôÊÇ `BeanPostProcessor` + +ÊáÀí `BeanPostProcessor` ǰ£¬ÎÒÃÇÏÈÀ´¿´¿´Ê²Ã´ÊÇ `BeanPostProcessor`£¬´úÂëÈçÏ£º + +``` +public interface BeanPostProcessor { + + /** + * ³õʼ»¯Ç°Ö´ÐÐ + */ + @Nullable + default Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + + /** + * ³õʼ»¯ºóÖ´ÐÐ + */ + @Nullable + default Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + +} + +``` + +`BeanPostProcessor` ÊÇÒ»¸ö½Ó¿Ú£¬¶¨ÒåÁË bean ³õʼǰºóµÄһЩ²Ù×÷£¬ÎÒÃÇ¿ÉÒÔʵÏÖÕâ¸ö½Ó¿Ú£¬ÖØÐ´ËüµÄÁ½¸ö·½·¨£¬¾Í¿ÉÒÔÔÚ bean ³õʼ»¯Ç°½øÐÐһЩ´¦Àí²Ù×÷ÁË¡£ + +`BeanPostProcessor` ÔÚ `AbstractApplicationContext#registerBeanPostProcessors` ÖÐ×¢²á¡£ + +ʵ¼ÊÉÏ£¬`BeanPostProcessor` »¹ÓÐÖÚ¶àµÄ×Ó½Ó¿Ú£¬ÕâЩÎÒÃǶ¼Í³³ÆÎª `BeanPostProcessor`£¬±¾ÎĵÄÖ÷ҪĿµÄ¾ÍÊÇÊáÀíÕâЩ `BeanPostProcessor`¡£ + +### 2. `BeanPostProcessor` ÊáÀí + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-102ca0d1e4db82a28871661241b05bc3956.png) + +#### 2.1 ³¢ÊÔÉú³É´úÀí¶ÔÏó + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation` +* Ö´Ðз½·¨£º`InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation` +* `AbstractAutoProxyCreator#postProcessBeforeInstantiation`£ºÉú³É´úÀí¶ÔÏó + +#### 2.2 ÍÆ¶Ï¹¹Ôì·½·¨ + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors` +* Ö´Ðз½·¨£º`SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors` +* `AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors`£ºÍƶϹ¹Ôì·½·¨ + +#### 2.3 »ñȡעÈëµÄÊôÐÔ + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors` +* Ö´Ðз½·¨£º`MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition` +* `ApplicationListenerDetector#postProcessMergedBeanDefinition`£ºÊÕ¼¯µ¥ÀýµÄ `ApplicationListener` +* `AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`£º²éÕÒ±» `@Autowired`¡¢`@Value`¡¢`@Inject` ±ê¼ÇµÄÊôÐÔÓë·½·¨ +* `CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`£º²éÕÒ±» `@Resource` ±ê¼ÇµÄÊôÐÔÓë·½·¨ +* `InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`£º²éÕÒ±» `@PostConstruct`¡¢`@PreDestroy` ±ê¼ÇµÄ·½·¨ + +#### 2.4 Ìí¼ÓÈý¼¶»º´æ + +* µ÷ÓÃλÖ㨷½·¨²¢Ã»ÓÐÖ´ÐУ©£º`AbstractAutowireCapableBeanFactory#doCreateBean` +* Ö´Ðз½·¨£º`SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference` +* `AbstractAutoProxyCreator#getEarlyBeanReference`£ºÌáǰÉú³É´úÀí¶ÔÏó + +#### 2.5 ÊÇ·ñÐèҪעÈëÊôÐÔ + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#populateBean` +* Ö´Ðз½·¨£º`InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation` + +#### 2.6 Ìî³äÊôÐÔ + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#populateBean` +* Ö´Ðз½·¨£º`InstantiationAwareBeanPostProcessor#postProcessProperties` +* `AutowiredAnnotationBeanPostProcessor#postProcessProperties`£ºÌî³ä±» `@Autowired`¡¢`@Value`¡¢`@Inject` ±ê¼ÇµÄÊôÐÔÓë·½·¨ +* `CommonAnnotationBeanPostProcessor#postProcessProperties`£ºÌî³ä±» `@Resource` ±ê¼ÇµÄÊôÐÔÓë·½·¨ +* `ImportAwareBeanPostProcessor#postProcessProperties`£ºÎª `EnhancedConfiguration` ʵÀýÉèÖà `beanFactory` + +#### 2.7 ³õʼ»¯Ç° + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization` +* Ö´Ðз½·¨£º`BeanPostProcessor#postProcessBeforeInitialization` +* `ApplicationContextAwareProcessor#postProcessBeforeInitialization`£ºµ÷Óà `XxxAware` ½Ó¿ÚµÄ·½·¨ +* `BeanValidationPostProcessor#postProcessBeforeInitialization`£º´¦Àí `JSR-303` УÑé +* `ImportAwareBeanPostProcessor#postProcessBeforeInitialization`£ºµ÷Óà `ImportAware` ½Ó¿ÚµÄ·½·¨ +* `InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization`£ºµ÷Óñ» `@PostConstruct` ±ê¼ÇµÄ·½·¨ +* `LoadTimeWeaverAwareProcessor#postProcessBeforeInitialization`£ºµ÷Óà `LoadTimeWeaverAware` ½Ó¿ÚµÄ·½·¨ +* `ServletContextAwareProcessor#postProcessBeforeInitialization`£ºµ÷Óà `ServletContextAware` ½Ó¿ÚµÄ·½·¨£¬ÉèÖà `servletContext` Óë `servletConfig` + +#### 2.8 ³õʼ»¯ºó + +* µ÷ÓÃλÖãº`AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization` +* Ö´Ðз½·¨£º`BeanPostProcessor#postProcessAfterInitialization` +* `AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization`£º´¦Àí `AopInfrastructureBean` +* `AbstractAutoProxyCreator#postProcessAfterInitialization`£ºÉú³É´úÀí¶ÔÏó +* `AdvisorAdapterRegistrationManager#postProcessAfterInitialization`£ºÈç¹ûµ±Ç° bean ÊÇ `AdvisorAdapter`£¬Ôò×¢²á +* `ApplicationListenerDetector#postProcessAfterInitialization`£ºÈç¹ûµ±Ç° bean ÊÇ `ApplicationListener`£¬ÔòÌí¼Óµ½Ê¼þ¼àÌýÆ÷ÖÐ +* `BeanPostProcessorChecker#postProcessAfterInitialization`£º¼ì²é²Ù×÷£¬´òÁ˸ö log +* `BeanValidationPostProcessor#postProcessAfterInitialization`£º´¦Àí `JSR-303` УÑé +* `JmsListenerAnnotationBeanPostProcessor#postProcessAfterInitialization`£º´¦Àí `@JmsListener` ×¢½â +* `ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization`£º´¦Àí `@Scheduled` ×¢½â +* `SimpleServletPostProcessor#postProcessAfterInitialization`£º¶Ô `Servlet` ʵÀý£¬µ÷Ó÷½·¨ `Servlet#init(ServletConfig)` + +### 3\. ×ܽá + +×îºóÓÃÒ»¸ö±í¸ñÀ´×ܽáÕâЩ `BeanPostProcessor`: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b1117b66c4881f366669dab69b332164d8f.png) + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4597551](https://my.oschina.net/funcy/blog/4597551) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" new file mode 100644 index 0000000..f8eb270 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\200\357\274\211\357\274\232\345\244\204\347\220\206@ComponentScan\346\263\250\350\247\243.md" @@ -0,0 +1,622 @@ +ÔÚ [spring Æô¶¯Á÷³ÌÖ®Ö´ÐÐ BeanFactoryPostProcessor](https://my.oschina.net/funcy/blog/4641114) Ò»ÎÄÖУ¬ÔÚÖ´ÐÐ `BeanFactoryPostProcessor` ¹ý³ÌÖУ¬ÓÐÒ»¸öÖØÒªµÄÀà»á±»Ö´Ðе½£¬Ëü¾ÍÊÇ `ConfigurationClassPostProcessor`¡£Õâ¸öÀà·Ç³£ÖØÒª£¬Ëü»á´¦Àí spring µÄÅäÖÃÀ࣬Èç `@Component`£¬`@PropertySources`£¬`@ComponentScans`£¬`@ImportResource` µÈ×¢½â£¬±¾ÏµÁÐÎÄÕ½«Í¨¹ý¼¸¸öʵÀý£¬´ÓÔ´ÂëµÄ½Ç¶È·ÖÎö spring ¶ÔÕ⼸¸ö×¢½âµÄ´¦Àí¡£ + +## 1\. »Ø¹Ë `BeanFactoryPostProcessor` µÄÖ´ÐÐ + +»ØÒäÏ `BeanFactoryPostProcessor` µÄÖ´ÐУ¬µ÷ÓÃÁ´ÈçÏÂ: + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.String...) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + +``` + +ÔÚ `PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)` ÖУ¬»áÁ½´Îµ÷Óà `ConfigurationClassPostProcessor` µÄ·½·¨£º + +* `invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)`£ºµ÷ÓõÄÊÇ `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` ·½·¨£» +* `invokeBeanFactoryPostProcessors(registryProcessors, beanFactory)`£ºµ÷ÓõÄÊÇ `BeanFactoryPostProcessor#postProcessBeanFactory` ·½·¨£» + +`ConfigurationClassPostProcessor` ͬʱʵÏÖÁË `BeanDefinitionRegistryPostProcessor` Óë `BeanFactoryPostProcessor`£¬Òò´ËÉÏÊöÁ½¸ö·½·¨¶¼»áÖ´Ðе½£¬ÏÂÃæÎÒÃÇÀ´¿´Ï `ConfigurationClassPostProcessor` µÄÕâÁ½¸ö·½·¨£º + +``` +/** + * ÏÈÖ´ÐÐ postProcessBeanDefinitionRegistry(...) + */ +@Override +public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { + int registryId = System.identityHashCode(registry); + if (this.registriesPostProcessed.contains(registryId)) { + throw new IllegalStateException(...); + } + if (this.factoriesPostProcessed.contains(registryId)) { + throw new IllegalStateException(...); + } + this.registriesPostProcessed.add(registryId); + // ÓÖµ÷ÓÃÁËÒ»¸ö·½·¨ + processConfigBeanDefinitions(registry); +} + +/** + * Ö´ÐÐÍê postProcessBeanDefinitionRegistry(...) ºó£¬ÔÙÖ´ÐÐ postProcessBeanFactory(...) + */ +@Override +public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + int factoryId = System.identityHashCode(beanFactory); + if (this.factoriesPostProcessed.contains(factoryId)) { + throw new IllegalStateException( + "postProcessBeanFactory already called on this post-processor against " + beanFactory); + } + this.factoriesPostProcessed.add(factoryId); + if (!this.registriesPostProcessed.contains(factoryId)) { + // Èç¹û beanFactory ûÓб»´¦Àí£¬»áÔÙÖ´ÐÐÒ»´Î processConfigBeanDefinitions ·½·¨ + // Ò»°ãÇé¿öÏ£¬ÕâÀï²»»á±»Ö´Ðе½ + processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); + } + // ÔöÇ¿ÅäÖÃÀà + enhanceConfigurationClasses(beanFactory); + // Ìí¼Ó´¦Àí ImportAware »Øµ÷µÄ BeanPostProcessor£¬Óë±¾ÎÄÖ÷Ìâ¹ØÏµ²»´ó£¬²»·ÖÎö + beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); +} + +``` + +¶ÔÉÏÊöÁ½¸ö·½·¨£¬ËµÃ÷ÈçÏ£º + +* °´ÕÕ `PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)` µÄÖ´ÐÐÂß¼­£¬»áÏÈÖ´ÐÐ `postProcessBeanDefinitionRegistry(...)`£¬ÔÙÖ´ÐÐ `postProcessBeanDefinitionRegistry(...)` +* `postProcessBeanDefinitionRegistry(...)` Ö÷ÒªÊǵ÷Óà `processConfigBeanDefinitions(...)` ·½·¨£» +* `postProcessBeanFactory(...)` »áÏÈÅжϵ±Ç° beanFactory ÊÇ·ñÖ´Ðйý `processConfigBeanDefinitions(...)` ·½·¨£¬Èç¹ûûÓУ¬Ôò»áÖ´ÐÐ `processConfigBeanDefinitions(...)` ·½·¨£¬Ö®ºó»á `enhanceConfigurationClasses(...)` ·½·¨¶ÔÅäÖÃÀà½øÐÐÔöÇ¿¡£ + +´ÓÒÔÉÏ·ÖÎöÀ´¿´£¬ÒÔÉÏÁ½¸ö·½·¨×îÖÕµ÷ÓõÄÊÇ `processConfigBeanDefinitions(...)` Óë `enhanceConfigurationClasses(...)`£¬Òò´ËÕâÁ½¸ö·½·¨ÊÇÎÒÃǽÓÏÂÀ´·ÖÎöµÄÖØµã¡£ + +## 2\. spring ÊÇÈçºÎ´¦Àí `@ComponentScan` ×¢½âµÄ£¿ + +### 2.1 demo ×¼±¸ + +ÔÚ·ÖÎöÁ÷³Ìǰ£¬ÎÒÃÇÏÈ×¼±¸µ÷ÊÔ demo: + +Ê×ÏÈ×¼±¸Ò»¸öÀ࣬ÉÏÃæ±êÁË `@ComponentScan` ×¢½â£º + +``` +@ComponentScan("org.springframework.learn.explore.demo02") +public class BeanConfigs { + +} + +``` + +ÔÙ×¼±¸Á½¸ö `Bean`£º + +``` +@Component +public class BeanObj1 { + + public BeanObj1() { + System.out.println("µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj1{}"; + } +} + +@Component +public class BeanObj2 { + + public BeanObj2() { + System.out.println("µ÷ÓÃbeanObj2µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj2{}"; + } +} + +``` + +×îºóÊÇÖ÷Àࣺ + +``` +public class Demo05Main { + public static void main(String[] args) { + ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfigs.class); + Object obj1 = context.getBean("beanObj1"); + Object obj2 = context.getBean("beanObj2"); + System.out.println("obj1:" + obj1); + System.out.println("obj2:" + obj2); + System.out.println(context.getBean("beanConfigs")); + } +} + +``` + +ÒÔÉÏÖ»ÊÇ demo µÄÖ÷Òª²¿·Ö£¬ÍêÕûµÄ demo ¼û [gitee/funcy](https://gitee.com/funcy/spring-framework/tree/v5.2.2.RELEASE_learn/spring-learn/src/main/java/org/springframework/learn/explore/demo05). + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨ +µ÷ÓÃbeanObj2µÄ¹¹Ôì·½·¨ +obj1:BeanObj1{} +obj2:BeanObj2{} +org.springframework.learn.explore.demo05.BeanConfigs@13eb8acf + +``` + +½ÓÏÂÀ´£¬¾ÍÒÔÕâ¸ö demo ΪÀý£¬Ò»²½²½½øÐзÖÎö¡£ + +### 2.2 `ApplicationContext` µÄ¹¹Ôì·½·¨£º`AnnotationConfigApplicationContext(Class)` + +ÎÒÃǽøÈë `AnnotationConfigApplicationContext` µÄ¹¹Ôì·½·¨: + +``` +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + // spring ÈÝÆ÷Æô¶¯Àà + refresh(); +} + +/** + * this() ·½·¨µÄµ÷ÓÃÄÚÈÝ + */ +public AnnotationConfigApplicationContext() { + // ¶ÔÁ½¸ö³ÉÔ±½øÐи³Öµ + // Èç¹ûµ÷ÓõÄÊÇ`AnnotationConfigApplicationContext(Class)`·½·¨ £¬ÕâÁ½¸öÊôÐÔ²»»áÓõ½ + // Èç¹ûµ÷ÓõÄÊÇ`AnnotationConfigApplicationContext(String)`·½·¨ £¬ÕâÁ½¸öÊôÐԲŻáÓõ½ + this.reader = new AnnotatedBeanDefinitionReader(this); + this.scanner = new ClassPathBeanDefinitionScanner(this); +} + +/** + * register(...) ·½·¨µÄÄÚÈÝ + */ +@Override +public void register(Class... componentClasses) { + Assert.notEmpty(componentClasses, "At least one component class must be specified"); + this.reader.register(componentClasses); +} + +``` + +`AnnotationConfigApplicationContext` µÄ¹¹Ôì·½·¨ÄÚÈÝÈçÏ£º + +* `this()`£ºµ÷ÓÃÎ޲ι¹Ôì·½·¨£¬Õâ¸ö·½·¨ÀïÖ÷ÒªÊǸø `reader` Óë `scanner` ³ÉÔ±±äÁ¿¸³Öµ£» +* `register(componentClasses)`£º×¢²á `component` Àൽ `beanFactory` ÖУ¬µ÷ÓõÄÊÇ `reader.register(...)` ·½·¨£» +* `refresh()`£ºspring µÄÈÝÆ÷Æô¶¯·½·¨£¬¾Í²»·ÖÎö¡£ + +Ö´ÐÐÍê `register(componentClasses);` ǰºó£¬`beanFactory` ÄÚµÄ `BeanDefinitionMap` ÄÚÈÝÈçÏ£º + +Ö´ÐÐǰ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c317168bfde886f7817e6e9a11301c84b52.png) + +Ö´ÐÐºó£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-cc2f7bcdb4b56f67893ac29774bb8119892.png) + +¿ÉÒÔ¿´µ½£¬`beanConfigs` ÒѾ­×¢²áµ½ `beanDefinitionNames` ÁË¡£ + +µ½ÕâÀspring ½ö½öÖ»ÊÇ°Ñ `beanConfigs` ×¢²áµ½ÁË `beanDefinitionNames`£¨`BeanConfigs` ÓÉ `new AnnotationConfigApplicationContext(BeanConfigs.class)` ´«ÈëµÄ£©£¬²¢Ã»ÓÐɨÃè `@ComponentSacn` ×¢½âÖ¸¶¨µÄ°üÃû£¬Ò²¾ÍÊÇ `org.springframework.learn.explore.demo05`£¬ÄÇô°üµÄɨÃèÊÇÔÚÄÄÀï½øÐеÄÄØ£¿´ðÓ¦¾ÍÊÇ `ConfigurationClassPostProcessor` ÖУ¬ÎÒÃǼÌÐøÍùÏ¿´¡£ + +### 2.3 ´¦ÀíÅäÖÃÀࣺ`ConfigurationClassPostProcessor#processConfigBeanDefinitions` + +¸ù¾Ý¿ªÆªµÄ·ÖÎö£¬ÎÒÃÇÖ±½Ó½øÈë `ConfigurationClassPostProcessor#processConfigBeanDefinitions` ·½·¨£¬µ÷ÓÃÁ´ÈçÏ£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors + |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry + |-ConfigurationClassPostProcessor#processConfigBeanDefinitions + +``` + +·½·¨ÄÚÈÝÈçÏ£º + +``` +public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { + List configCandidates = new ArrayList<>(); + // 1\. »ñÈ¡ËùÓÐBeanDefinitionÃû³Æ + String[] candidateNames = registry.getBeanDefinitionNames(); + // 2\. Ñ­»·candidateNamesÊý×飬±êʶFullÓëLite + for (String beanName : candidateNames) { + BeanDefinition beanDef = registry.getBeanDefinition(beanName); + // Åжϵ±Ç°BeanDefinitionÊÇÒѾ­´¦Àí¹ýÁË£¬´¦Àí¹ýÁ˾Ͳ»ÔÙ´¦ÀíÁË + if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { + // ÕâÀïÖ»ÊÇ´òÁ˸ölog£¬Ê¡ÂÔ + ... + } + // ÅжÏÊÇ·ñΪÅäÖÃÀ࣬ÕâÀï·ÖÁ½ÖÖÇé¿ö£º + // 1\. ´øÓÐ @Configuration ×¢½âÇÒ proxyBeanMethods != false µÄÀ࣬spring ³ÆÆäΪ Full ÅäÖÃÀà + // 2\. ´øÓÐ @Configuration ×¢½âÇÒ proxyBeanMethods == false, »ò´øÓÐ @Component¡¢@ComponentScan¡¢ + // @Import¡¢@ImportResource¡¢@Bean ÆäÖÐÖ®Ò»×¢½âµÄÀ࣬spring ³ÆÆäΪ Lite ÅäÖÃÀà + // ÕâÀï»á¶ÔFullÓëLite£¬½øÐбêʶ + else if (ConfigurationClassUtils + .checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { + configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); + } + } + // Èç¹ûûÓÐÅäÖÃÀֱ࣬½Ó·µ»Ø + if (configCandidates.isEmpty()) { + return; + } + + // Ê¡ÂÔÎ޹صĴúÂë + ... + + // ÅäÖÃÀà½âÎöÆ÷£¬Õâ¸öÀà·Ç³£ÖØÒª£¬ÏÂÃæ¾ÍÊÇÓÃËüÀ´½âÎö ¶Ô@Component¡¢@ImportµÈ×¢½âµÄ + ConfigurationClassParser parser = new ConfigurationClassParser( + this.metadataReaderFactory, this.problemReporter, this.environment, + this.resourceLoader, this.componentScanBeanNameGenerator, registry); + // Á½¸ö½á¹¹£¬candidates£ºÐèÒª½âÎöµÄÅäÖÃÀ࣬alreadyParsed£ºÍê³É½âÎöµÄÅäÖÃÀà + Set candidates = new LinkedHashSet<>(configCandidates); + Set alreadyParsed = new HashSet<>(configCandidates.size()); + do { + // 3\. ½âÎöÅäÖÃÀ࣬Õâ¸ö·½·¨×öÁ˺ܶàÊ£¬ + // È磺½âÎö@Component£¬@PropertySources£¬@ComponentScans£¬@ImportResourceµÈ×¢½â + // ×¢£ºÕâÀïÊÇÒ»´ÎÐÔ½âÎöËùÓеÄcandidates + parser.parse(candidates); + parser.validate(); + Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); + configClasses.removeAll(alreadyParsed); + // ´´½¨ reader£¬Õâ¸öreaderÓëÇ°ÃæApplicationContextÖеÄreader²»ÊÇͬһ¸ö + if (this.reader == null) { + this.reader = new ConfigurationClassBeanDefinitionReader( + registry, this.sourceExtractor, this.resourceLoader, this.environment, + this.importBeanNameGenerator, parser.getImportRegistry()); + } + // 4\. ´¦Àí±¾´Î½âÎöµÄÀà + // ¾ÍÊǰÑ@ImportÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø@BeanµÄ·½·¨¡¢@ImportResourceÒýÈëµÄ×ÊÔ´µÈת»»³ÉBeanDefinition + this.reader.loadBeanDefinitions(configClasses); + // °ÑconfigClasses¼ÓÈëµ½alreadyParsed + alreadyParsed.addAll(configClasses); + + // ½âÎöÍê³Éºó£¬»á°ÑcandidatesµÄÇå¿Õ£¬½ÓÏÂÀ´»á°ÑÐÂÌí¼ÓµÄ¡¢Î´½âÎö¹ýµÄFullÅäÖÃÀàÌí¼Óµ½candidatesÖÐ + candidates.clear(); + // 5\. ´¦Àí·µ»Ø½á¹û£¬Èç¹ûÐÂÌí¼ÓµÄÀàΪ Full ÅäÖÃÀàÇÒδ½âÎö¹ý£¬¾Í°ÑËüÌí¼Óµ½candidatesÖУ¬ÔÚÏ´ÎÑ­»·Ê±ÔÙ½âÎö + // »ñµÃ×¢²áÆ÷ÀïÃæBeanDefinitionµÄÊýÁ¿ ºÍ candidateNames½øÐÐ±È½Ï + // Èç¹û´óÓڵϰ£¬ËµÃ÷ÓÐеÄBeanDefinition×¢²á½øÀ´ÁË + if (registry.getBeanDefinitionCount() > candidateNames.length) { + String[] newCandidateNames = registry.getBeanDefinitionNames(); + Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); + Set alreadyParsedClasses = new HashSet<>(); + // Ñ­»·alreadyParsed¡£°ÑÀàÃû¼ÓÈëµ½alreadyParsedClasses + for (ConfigurationClass configurationClass : alreadyParsed) { + alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); + } + for (String candidateName : newCandidateNames) { + if (!oldCandidateNames.contains(candidateName)) { + BeanDefinition bd = registry.getBeanDefinition(candidateName); + // Èç¹ûмӵÄÀàΪÅäÖÃÀ࣬ÇÒδ½âÎö¹ý£¬¾Í°ÑËüÌí¼Óµ½candidatesÖУ¬µÈ´ýÏ´ÎÑ­»·½âÎö + if (ConfigurationClassUtils + .checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && + !alreadyParsedClasses.contains(bd.getBeanClassName())) { + candidates.add(new BeanDefinitionHolder(bd, candidateName)); + } + } + } + candidateNames = newCandidateNames; + } + } + + // Ê¡ÂÔÓë±¾ÎÄÁ÷³ÌÎ޹صĴúÂë + ... +} + +``` + +Õýʽ·ÖÎöÒÔÉÏ·½·¨Ç°£¬ÏÈÃ÷È· spring ¹ØÓÚÅäÖÃÀàµÄ¼¸¸ö¸ÅÄ£º + +* ÅäÖÃÀࣺ´øÓÐ `@Configuration`¡¢`@Component`¡¢`@ComponentScan`¡¢`@Import`¡¢`@ImportResource` µÈÆäÖÐÖ®Ò»×¢½âµÄÀࣻ +* `Full` ÅäÖÃÀࣺ´øÓÐ `@Configuration` ×¢½âÇÒ `proxyBeanMethods != false` µÄÀ࣬`spring` ³ÆÆäΪ `Full` ÅäÖÃÀࣻ +* `Lite` ÅäÖÃÀࣺ´øÓÐ `@Configuration` ×¢½âÇÒ `proxyBeanMethods == false`, »ò´øÓÐ `@Component`¡¢`@ComponentScan`¡¢`@Import`¡¢`@ImportResource` µÈÆäÖÐÖ®Ò»×¢½âµÄÀ࣬`spring` ³ÆÆäΪ `Lite` ÅäÖÃÀà¡£ + +ÒÔÉÏ·½·¨Óе㳤£¬×ܽáÏÂÀ´´ó¸Å×öÁËÒÔϼ¸¼þÊ£º + +1. »ñÈ¡ËùÓÐ `BeanDefinition` µÄÃû³ÆÕâ²½Ö´ÐÐÍê³Éºó£¬½á¹ûÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-df320c2911c4222a6867a0f0a4fe7d9987d.png) + +2. Ñ­»· `candidateNames` Êý×飬±êʶÅäÖÃÀàµÄÀàÐÍΪ `Full` »¹ÊÇ `Lite`£¬ÕâÒ»²½Ëù×öµÄ¹¤×÷¾ÍÊǶÔÅäÖÃÀà¶ÔÓ¦µÄ `BeanDefinition` ½øÐбêʶ£¨ÖÁÓÚ±êʶºóÓÐʲô×÷Óã¬ÔÚºóÃæ·ÖÎö `@Configuration` ×¢½âʱ»áÔÙ·ÖÎö£©£¬`beanConfigs` ûÓÐ `@Configuration` ×¢½â£¬Òò´ËÊÇ `Lite` ÅäÖÃÀà¡£ÕâÒ»²½µÃµ½µÄ `configCandidates` ÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-81a68ec905807d77153158af4c86d48a1d3.png) + +3. ½âÎöÅäÖÃÀ࣬¼´½âÎö `@Component`£¬`@PropertySources`£¬`@ComponentScans`£¬`@ImportResource` µÈ×¢½â±ê×¢µÄÀ࣬Õâ¸ö·½·¨·Ç³£ÖØÒª£¬ÏÂÃæ»áÖØµã·ÖÎö£» + +4. ´¦Àí±¾´Î½âÎöµÄÀ࣬°Ñ @Import ÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø @Bean µÄ·½·¨¡¢@ImportResource ÒýÈëµÄ×ÊÔ´µÈת»»³É BeanDefinition£¬¼ÓÔØµ½ `BeanDefinitionMap` ÖУ» + +5. ´¦Àí·µ»Ø½á¹û£¬Èç¹ûÐÂÌí¼ÓµÄÀàΪ `Full` ÅäÖÃÀ࣬ÇÒδ½âÎö¹ý£¬¾Í°ÑËüÌí¼Óµ½ `candidates` ÖУ¬ÔÚÏ´ÎÑ­»·Ê±ÔÙ½âÎö¡£ + +ÒÔÉÏ·½·¨µÄÁ÷³Ì¾ÍÕâÑùÁË£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´¿´¿´ÅäÖÃÀàµÄ½âÎö£¬Ò²¾ÍÊÇÉÏÊöµÄµÚ 3 ²½¡£ + +### 2.4 ½âÎö£º`ConfigurationClassParser#parse(Set)` + +``` +public void parse(Set configCandidates) { + // Ñ­»·´«½øÀ´µÄÅäÖÃÀà + for (BeanDefinitionHolder holder : configCandidates) { + BeanDefinition bd = holder.getBeanDefinition(); + try { + // Èç¹û»ñµÃBeanDefinitionÊÇAnnotatedBeanDefinitionµÄʵÀý + // Ç°ÃæµÃµ½µÄ beanConfigs¾ÍÊÇAnnotatedBeanDefinitionµÄʵÀý£¬ifÀïµÄ·½·¨»áÖ´ÐÐ + if (bd instanceof AnnotatedBeanDefinition) { + parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); + } + else if (bd instanceof AbstractBeanDefinition + && ((AbstractBeanDefinition) bd).hasBeanClass()) { + parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); + } + else { + parse(bd.getBeanClassName(), holder.getBeanName()); + } + } + catch (...) { + ... + } + } + this.deferredImportSelectorHandler.process(); +} + +/** + * µ÷ÓÃparse½øÐнâÎö + */ +protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { + // ConfigurationClass£ºmetadataÓëbeanNameµÄ°ü×°Àà + processConfigurationClass(new ConfigurationClass(metadata, beanName)); +} + +``` + +Ç°Ãæ´«ÈëµÄ `BeanConfigs` »á±»°ü×°³É `AnnotatedGenericBeanDefinition`£¬ËüÊÇ `AnnotatedBeanDefinition` µÄʵÀý£¬È»ºó¾Í»áµ÷Óà `ConfigurationClassParser#parse(String, String)`£¬ÕâÀïÆäʵ²¢Ã»×öʲôʵÖÊÐԵŤ×÷£¬¼ÌÐø½øÈë `processConfigurationClass(...)` ·½·¨£º + +``` +/** + * Õâ¸ö·½·¨ÊÇÔÚÅжÏÌõ¼þ£¬±£Ö¤ÅäÖÃÀà²»ÖØ¸´½âÎö + * ʵ¼Ê¸É»îµÄÊÇ doProcessConfigurationClass(...) + */ +protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { + // ÅжÏÊÇ·ñÐèÒªÌø¹ý´¦Àí£¬Õë¶ÔÓÚ @Conditional ×¢½â£¬ÅжÏÊÇ·ñÂú×ãÌõ¼þ + if (this.conditionEvaluator.shouldSkip(configClass.getMetadata() , + ConfigurationPhase.PARSE_CONFIGURATION)) { + return; + } + ConfigurationClass existingClass = this.configurationClasses.get(configClass); + // ÅжÏÊÇ·ñ½âÎö¹ý£¬½âÎö¹ý¾Í²»½âÎöÁË£¬Óë½âÎöÄÚÈݹØÏµ²»´ó£¬Ê¡ÂÔ + if (existingClass != null) { + ... + } + // SourceClass Í¬Ç°ÃæµÄ ConfigurationClass Ò»Ñù£¬Ò²ÊǶÔmetadataÓëbeanNameµÄ°ü×° + SourceClass sourceClass = asSourceClass(configClass); + do { + // doXxx(...) ·½·¨²ÅÊÇÕæÕý¸É»îµÄ + // Èç¹û·µ»ØµÄÄÚÈݲ»Îª¿Õ£¬ÏÂÃæ»áÔÙ´ÎÑ­»· + sourceClass = doProcessConfigurationClass(configClass, sourceClass); + } + while (sourceClass != null); + this.configurationClasses.put(configClass, configClass); +} + +``` + +Õâ¸ö·½·¨ÏÈÅжÏÅäÖÃÀàÊÇ·ñÂú×ãÖ´ÐÐÌõ¼þ£¬È»ºóÔÚ `do-while` Ñ­»·ÖÐÖ´ÐÐ `doProcessConfigurationClass(...)`£¬Ñ­»·Ìõ¼þÊÇ `doProcessConfigurationClass(...)` ·µ»ØµÄÄÚÈݲ»Îª¿Õ£¬ÎÒÃǼÌÐøÍùÏ¿´¡£ + +### 2.5 ½âÎöÅäÖÃÀࣺ`ConfigurationClassParser#doProcessConfigurationClass` + +``` +/** + * Õâ¸ö·½·¨²ÅÊÇÕæÕý´¦Àí½âÎöµÄ·½·¨ + */ +protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, + SourceClass sourceClass) throws IOException { + // 1\. Èç¹ûÊÇ @Component ×¢½â£¬µÝ¹é´¦ÀíÄÚ²¿À࣬±¾ÎIJ»¹Ø×¢ + ... + + // 2\. ´¦Àí@PropertySource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 3\. ´¦Àí @ComponentScan/@ComponentScans ×¢½â + // 3.1 »ñÈ¡ÅäÖÃÀàÉ쵀 @ComponentScan/@ComponentScans ×¢½â + Set componentScans = AnnotationConfigUtils.attributesForRepeatable( + sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); + // Èç¹ûûÓдòÉÏComponentScan£¬»òÕß±»@ConditionÌõ¼þÌø¹ý£¬¾Í²»ÔÙ½øÈëÕâ¸öif + if (!componentScans.isEmpty() && !this.conditionEvaluator + .shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { + // Ñ­»·´¦ÀícomponentScans£¬Ò²¾ÍÊÇÅäÖÃÀàÉϵÄËùÓÐ@ComponentScan×¢½âµÄÄÚÈÝ + for (AnnotationAttributes componentScan : componentScans) { + // 3.2 componentScanParser.parse(...)£º¾ßÌå½âÎöcomponentScanµÄ²Ù×÷ + // componentScan¾ÍÊÇ@ComponentScanÉϵľßÌåÄÚÈÝ£¬ + // sourceClass.getMetadata().getClassName()¾ÍÊÇÅäÖÃÀàµÄÃû³Æ + Set scannedBeanDefinitions = this.componentScanParser + .parse(componentScan, sourceClass.getMetadata().getClassName()); + // 3.3 Ñ­»·µÃµ½µÄ BeanDefinition£¬Èç¹û¶ÔÓ¦µÄÀàÊÇÅäÖÃÀ࣬µÝ¹éµ÷ÓÃparse(...)·½·¨ + // componentScanÒýÈëµÄÀà¿ÉÄÜÓб»@Bean±ê¼ÇµÄ·½·¨£¬»òÕßÓÐ@ComponentScan×¢½â + for (BeanDefinitionHolder holder : scannedBeanDefinitions) { + BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); + if (bdCand == null) { + bdCand = holder.getBeanDefinition(); + } + // ÅжÏBeanDefinition¶ÔÓ¦µÄÀàÊÇ·ñΪÅäÖÃÀà + if (ConfigurationClassUtils + .checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { + // ¶ÔµÃµ½µÄÀ࣬µ÷ÓÃparse(...)·½·¨£¬ÔٴνøÐнâÎö + parse(bdCand.getBeanClassName(), holder.getBeanName()); + } + } + } + } + // 4\. ´¦Àí@Import×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 5\. ´¦Àí@ImportResource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 6\. ´¦Àí@BeanµÄ×¢½â£¬£¬±¾ÎIJ»¹Ø×¢ + ... + + // 7\. ·µ»ØÅäÖÃÀàµÄ¸¸À࣬»áÔÚ processConfigurationClass(...) ·½·¨µÄÏÂÒ»´ÎÑ­»·Ê±½âÎö + // sourceClass.getMetadata()¾ÍÊÇÅäÖÃÀà + if (sourceClass.getMetadata().hasSuperClass()) { + String superclass = sourceClass.getMetadata().getSuperClassName(); + if (superclass != null && !superclass.startsWith("java") && + !this.knownSuperclasses.containsKey(superclass)) { + this.knownSuperclasses.put(superclass, configClass); + return sourceClass.getSuperClass(); + } + } + return null; +} + +/** + * Õâ¸ö·½·¨»áÔٴε÷ÓÃprocessConfigurationClass(...)·½·¨½øÐнâÎö + * Ä¿µÄÊÇ£¬ÐÂÒýÈëµÄÀàÒ²ÓпÉÄÜÓб»@Bean±ê¼ÇµÄ·½·¨£¬»òÕßÓÐComponentScanµÈ×¢½â + */ +protected final void parse(@Nullable String className, String beanName) throws IOException { + Assert.notNull(className, "No bean class name for configuration class bean definition"); + MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); + // ÓÖµ÷»Ø processConfigurationClass(...) ·½·¨ÁË + processConfigurationClass(new ConfigurationClass(reader, beanName)); +} + +``` + +`ConfigurationClassParser#doProcessConfigurationClass` Õâ¸ö·½·¨¾ÍÊÇ¶Ô `@PropertySource`¡¢`@ComponentScan`¡¢`@Import`¡¢`@ImportResource`¡¢`@Bean` µÈ×¢½â£¬±¾ÎÄÎÒÃǽö¹Ø×¢ `@ComponentScan` ×¢½âµÄ´¦Àí£¬Á÷³ÌÈçÏ£º + +1. »ñÈ¡ÅäÖÃÀàÉ쵀 `@ComponentScan/@ComponentScans` ×¢½â£» +2. `componentScanParser.parse(...)`£º¾ßÌå½âÎö `componentScan` µÄ²Ù×÷£¬ºóÃæ»áÖØµã·ÖÎö£» +3. Ñ­»·µÃµ½µÄ `BeanDefinition`£¬Èç¹û¶ÔÓ¦µÄÀàÊÇÅäÖÃÀ࣬µÝ¹éµ÷Óà `parse(...)` ·½·¨£¬Èç¹ûɨÃèµ½µÄÀà°üº¬ `@Import`¡¢`@Bean`¡¢`@ComponentScan` µÈ×¢½â£¬µÝ¹éµ÷Óà `parse(...)` ·½·¨Ê±»á±»½âÎöµ½¡£ + +ÉÏÃæµÄ²Ù×÷Á÷³ÌÔÚ´úÂëÖÐÒѾ­×¢Ê͵úÜÇå³þÁË£¬¾Í²»¶à˵ÁË£¬ÎÒÃÇÖ±½ÓÀ´¿´ `@ComponentScan` µÄ½âÎö²Ù×÷. + +### 2.6 ÕæÕý½âÎöµÄµØ·½£º`ComponentScanAnnotationParser#parse` + +`@ComponentScan` µÄ½âÎöÔÚ `ComponentScanAnnotationParser#parse` ·½·¨ÖУ¬´úÂëÈçÏ£º + +``` +public Set parse(AnnotationAttributes componentScan, final String declaringClass) { + // 1\. ¶¨ÒåÒ»¸öɨÃèÆ÷£¬ÓÃÀ´É¨Ãè°ü + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, + componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); + // 2\. ÅжÏÊÇ·ñÖØÐ´ÁËĬÈϵÄÃüÃû¹æÔò + Class generatorClass = componentScan.getClass("nameGenerator"); + boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); + scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : + BeanUtils.instantiateClass(generatorClass)); + // 3\. ½âÎö @ComponentScan ×¢½âµÄÊôÐÔ + // 3.1 ´¦Àí @ComponentScan µÄ scopedProxy ÊôÐÔ + ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); + if (scopedProxyMode != ScopedProxyMode.DEFAULT) { + scanner.setScopedProxyMode(scopedProxyMode); + } + else { + Class resolverClass = componentScan.getClass("scopeResolver"); + scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); + } + // 3.2 ´¦Àí @ComponentScan µÄ resourcePattern ÊôÐÔ + scanner.setResourcePattern(componentScan.getString("resourcePattern")); + + // 3.3 ´¦Àí @ComponentScan µÄ includeFilters ÊôÐÔ + // addIncludeFilter addExcludeFilter,×îÖÕÊÇÍùListÀïÃæÌî³äÊý¾Ý + for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { + for (TypeFilter typeFilter : typeFiltersFor(filter)) { + scanner.addIncludeFilter(typeFilter); + } + } + // 3.4 ´¦Àí @ComponentScan µÄ excludeFilters ÊôÐÔ + for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { + for (TypeFilter typeFilter : typeFiltersFor(filter)) { + scanner.addExcludeFilter(typeFilter); + } + } + boolean lazyInit = componentScan.getBoolean("lazyInit"); + if (lazyInit) { + scanner.getBeanDefinitionDefaults().setLazyInit(true); + } + Set basePackages = new LinkedHashSet<>(); + // 3.5\. @ComponentScan Ö¸¶¨ÁË basePackages ÊôÐÔ£¬Õâ¸öÊôÐÔµÄÀàÐÍÊÇString + String[] basePackagesArray = componentScan.getStringArray("basePackages"); + for (String pkg : basePackagesArray) { + String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), + ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + Collections.addAll(basePackages, tokenized); + } + // 3.6\. @ComponentScan Ö¸¶¨ÁË basePackageClasses£¬ Õâ¸öÊôÐÔµÄÀàÐÍÊÇClass£¬ + // ¼´Ö»ÒªÊÇÓëÕ⼸¸öÀàͬ¼¶µÄ£¬»òÕßÔÚÕ⼸¸öÀàϼ¶µÄ¶¼¿ÉÒÔ±»É¨Ãèµ½ + for (Class clazz : componentScan.getClassArray("basePackageClasses")) { + basePackages.add(ClassUtils.getPackageName(clazz)); + } + // 3.7 ÒÔÉ϶¼Ã»ÓÐÖ¸¶¨£¬Ä¬ÈÏ»á°ÑÅäÖÃÀàËùÔڵİüÃû×÷ΪɨÃè·¾¶ + if (basePackages.isEmpty()) { + basePackages.add(ClassUtils.getPackageName(declaringClass)); + } + // 3.8 Ìí¼ÓÅųý¹æÔò£¬ÕâÀï¾Í°Ñ×¢²áÀà×ÔÉíµ±×÷Åųý¹æÔò£¬ÕæÕýÖ´ÐÐÆ¥ÅäµÄʱºò£¬»á°Ñ×ÔÉí¸øÅųý + scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { + @Override + protected boolean matchClassName(String className) { + return declaringClass.equals(className); + } + }); + // 4\. µ½ÕâÀ²Å¿ªÊ¼É¨Ãè @ComponentScan Ö¸¶¨µÄ°ü + // ɨÃèÍê³Éºó£¬¶Ô·ûºÏÌõ¼þµÄÀ࣬spring»á½«ÆäÌí¼Óµ½beanFactoryµÄBeanDefinitionMapÖÐ + return scanner.doScan(StringUtils.toStringArray(basePackages)); +} + +``` + +ÒÔÉÏ·½·¨Ö´ÐÐÁ÷³ÌÈçÏ£º + +1. ¶¨ÒåÒ»¸öɨÃèÆ÷£¬ÓÃÀ´É¨Ãè°ü +2. ÅжÏÊÇ·ñÖØÐ´ÁËĬÈϵÄÃüÃû¹æÔò +3. ½âÎö `@ComponentScan` ×¢½âµÄÊôÐÔ£¬ÍêɯɍÃèÆ÷ + 1. ´¦Àí `@ComponentScan` µÄ `scopedProxy` ÊôÐÔ + 2. ´¦Àí `@ComponentScan` µÄ `resourcePattern` ÊôÐÔ + 3. ´¦Àí `@ComponentScan` µÄ `includeFilters` ÊôÐÔ + 4. ´¦Àí `@ComponentScan` µÄ `excludeFilters` ÊôÐÔ + 5. ´¦Àí `@ComponentScan` µÄ `basePackages` ÊôÐÔ£¬Õâ¸öÊôÐÔµÄÀàÐÍÊÇ `String` + 6. ´¦Àí `@ComponentScan` µÄ `basePackageClasses` ÊôÐÔ£¬ Õâ¸öÊôÐÔµÄÀàÐÍÊÇ `Class` + 7. Èç¹ûûÓÐÖ¸¶¨É¨°ü¹æÔò£¬Ä¬ÈÏ»á°ÑÅäÖÃÀàËùÔڵİüÃû×÷ΪɨÃè·¾¶ + 8. Ìí¼ÓÅųý¹æÔò£¬ÕâÀï¾Í°Ñ×¢²áÀà×ÔÉíµ±×÷Åųý¹æÔò£¬ÕæÕýÖ´ÐÐÆ¥ÅäµÄʱºò£¬»á°Ñ×ÔÉí¸øÅųý +4. µ÷Óà `ClassPathBeanDefinitionScanner#doScan` Íê³ÉɨÃè + +×îÖÕ£¬µ÷ÓÃÁË `ClassPathBeanDefinitionScanner#doScan` ·½·¨À´Íê³É°üµÄÃèÊö£¬Õâ¸ö·½·¨ÎÒÃÇÔÚ [spring Æô¶¯Á÷³ÌÖ®°üµÄɨÃèÁ÷³Ì](https://my.oschina.net/funcy/blog/4614071)ÒѾ­Ïêϸ·ÖÎö¹ýÁË£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£ + +ÈÃÎÒÃǻص½ `ConfigurationClassPostProcessor#processConfigBeanDefinitions` ·½·¨£º + +``` +public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { + ... + parser.parse(candidates); + .... +} + +``` + +Ö´ÐÐǰ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-85e400f7d45b4fe5e2353e359f034b1a726.png) + +Ö´ÐÐºó£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a6d02c305f562414326ca7836c281ebf472.png) + +¿ÉÒÔ¿´µ½£¬`BeanObj1`¡¢`BeanObj2` ÒѾ­·ÅÈëÁË `BeanFactory` µÄ `BeanDefinitionMap` ÖÐÁË ¡£ + +### 2.7 ×ܽá + +spring ´¦Àí `@ComponentScan` µÄÁ÷³Ìµ½ÕâÀï¾Í½áÊøÁË£¬½âÎöÁ÷³ÌλÓÚ `ConfigurationClassParser#doProcessConfigurationClass` ·½·¨£¬Õâ¸ö·½·¨³ýÁ˽âÎö `@ComponentScan`£¬»¹»á½âÎö `@Bean`¡¢`@Import` µÈ×¢½â£¬±¾ÎÄÖ»·ÖÎö `@ComponentScan` µÄ´¦ÀíÁ÷³Ì£¬¶ÔÒÔÉÏÁ÷³Ì×ܽáÈçÏ£º + +1. »ñÈ¡ÅäÖÃÀàÉ쵀 `@ComponentScan/@ComponentScans` ×¢½â +2. ½øÐнâÎö `@ComponentScan` µÄ²Ù×÷£¬½âÎöʱ£¬Ïȶ¨ÒåÁËÒ»¸öÃèÊöÆ÷£¬È»ºó¸ù¾Ý `@ComponentScan` µÄÊôÐÔ£¬¶ÔÃèÊöÆ÷Æ÷½øÐÐÊôÐÔÌî³ä£¬´¦ÀíÍêÕâЩ֮ºó£¬¾Í¿ªÊ¼½øÐаüɨÃè²Ù×÷£» +3. ±éÀúɨÃèµÃµ½µÄÀ࣬Èç¹ûÆäΪÅäÖÃÀ࣬»áͨ¹ýµ÷Óà `parse(...)` ·½·¨ÔÙÒ»´Îµ÷Óà `ConfigurationClassParser#doProcessConfigurationClass` ½øÐнâÎö£¬ÕâÒ»µã·Ç³£ÖØÒª£¬Õâ¾Í±£Ö¤ÁËɨÃèµÃµ½µÄÀàÖÐµÄ `@Bean`¡¢`@Import`¡¢`@ComponentScan` µÈ×¢½âµÃµ½½âÎö¡£ + +ºÃÁË£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬½ÓÏÂÀ´¼¸ÆªÎÄÕ»á¼ÌÐø·ÖÎö `ConfigurationClassPostProcessor` ¶ÔÆäËû×¢½âµÄ´¦Àí¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4836178](https://my.oschina.net/funcy/blog/4836178) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" new file mode 100644 index 0000000..ad1a70a --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\270\211\357\274\211\357\274\232\345\244\204\347\220\206@Import\346\263\250\350\247\243.md" @@ -0,0 +1,849 @@ +±¾ÎÄÊÇ `ConfigurationClassPostProcessor` ·ÖÎöµÄµÚÈýƪ£¬Ö÷ÒªÊÇ·ÖÎö spring ¶Ô `@Import` ×¢½âµÄ´¦ÀíÁ÷³Ì¡£ + +## 4. spring ÊÇÈçºÎ´¦Àí @Import ×¢½âµÄ£¿ + +³Ð½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö spring ¶Ô `@Import` ×¢½âµÄ´¦ÀíÁ÷³Ì¡£ + +### 4.1 Á˽â `@Import` ×¢½â + +ÎÒÃÇÀ´¿´Ï `@Import` ×¢½âµÄ¶¨Ò壺 + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Import { + /** + * {@link Configuration @Configuration}, {@link ImportSelector}, + * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import. + */ + Class[] value(); +} +``` + +`@Import` ÀïÓÐÒ»¸ö·½·¨£º`value()`£¬Ö§³ÖµÄÀàÐÍÊÇ `Class`£¬´ÓÉÏÃæµÄÎĵµÀ´¿´£¬¿ÉÒÔÖ§³ÖÀàÐÍÓÐ 4 ÖÖ£º + +- ±» `@Configuration` ×¢½â±ê¼ÇµÄÀà +- ʵÏÖÁË `ImportSelector` µÄÀà +- ʵÏÖÁË `ImportBeanDefinitionRegistrar` µÄÀà +- ÆÕͨÀà + +½ÓÏÂÀ´£¬ÎÒÃÇͨ¹ýÒ»¸ö demo À´Õ¹Ê¾Ê¹Óà `@Import` ÈçºÎ½«ÕâËÄÖÖÀർÈëµ½ spring ÖС£ + +### 4.2 demo ×¼±¸ + +1. Ê×ÏÈ×¼±¸ 4 ¸ö bean£º + +``` +/** + * Element01 + */ +public class Element01 { + public String desc() { + return "this is element 01"; + } +} + +/** + * Element02 + */ +public class Element02 { + public String desc() { + return "this is element 02"; + } +} + +/** + * Element03 + */ +public class Element03 { + public String desc() { + return "this is element 03"; + } +} + +/** + * Element04 + */ +public class Element04 { + public String desc() { + return "this is element 04"; + } +} +``` + +1. ×¼±¸ÊµÏÖ `ImportBeanDefinitionRegistrar` µÄÀ࣬½« `element02` ×¢ÈëÆäÖÐ + +``` +public class Element02ImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + + /** + * ÔÚ registerBeanDefinitions ÖÐ×¢²áelement02¶ÔÓ¦µÄBeanDefinition + * Ò²¾ÍÊÇ°Ñ Element02 ¶ÔÓ¦µÄ beanDefinition ÊÖ¶¯×¢²áµ½beanFactory + * µÄ beanDefinitionMap ÖÐ + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + registry.registerBeanDefinition("element02", new RootBeanDefinition(Element02.class)); + } +} +``` + +1. ×¼±¸ÊµÏÖ `ImportSelector` µÄÀ࣬ÔÚ `selectImports(...)` ·½·¨ÖУ¬·µ»Ø `Element03` µÄ "°üÃû¡£ÀàÃû" + +``` +public class Element03Selector implements ImportSelector { + /** + * ·µ»ØString Ϊ °üÃû.ÀàÃû + * ÓÉÓÚºóÃæÒªÓõ½·´É䣬Òò´Ë±ØÐëÊÇ"°üÃû.ÀàÃû" + * @param importingClassMetadata + * @return + */ + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] {Element03.class.getName()}; + } +} +``` + +1. ×¼±¸Ò»¸ö±» `@Configuration` ×¢½â±ê¼ÇÀ࣬ͨ¹ýÀàÖб» `@Bean` ±ê¼ÇµÄ·½·¨·µ»Ø `Element04` + +``` +@Configuration +public class Element04Configuration { + @Bean + public Element04 element04() { + return new Element04(); + } + +} +``` + +1. ¶¨Òå `@EnableElement` ×¢½â£¬ÆäÖÐµÄ `@Import` ×¢½âÒÀ´ÎÒýÈë `Element01.class`¡¢`Element02ImportBeanDefinitionRegistrar.class`¡¢`Element03Selector.class`¡¢`Element04Configuration.class` + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import({ + // ÆÕͨÀà + Element01.class, + // ʵÏÖÁË ImportBeanDefinitionRegistrar µÄÀà + Element02ImportBeanDefinitionRegistrar.class, + // ʵÏÖÁË ImportSelector µÄÀà + Element03Selector.class, + // ±» @Configuration ±ê¼ÇµÄÀà + Element04Configuration.class +}) +public @interface EnableElement { + +} +``` + +1. Ö÷Àࣺ + +``` +// Ö»ÐèÒª @EnableElement ×¢½â +@EnableElement +public class Demo04Main { + + public static void main(String[] args) { + // ´«ÈëµÄÊÇ Demo04Main.class + ApplicationContext context = new AnnotationConfigApplicationContext(Demo04Main.class); + + Element01 element01 = context.getBean(Element01.class); + System.out.println(element01.desc()); + + Element02 element02 = context.getBean(Element02.class); + System.out.println(element02.desc()); + + Element03 element03 = context.getBean(Element03.class); + System.out.println(element03.desc()); + + Element04 element04 = context.getBean(Element04.class); + System.out.println(element04.desc()); + } +} +``` + +ÍêÕû´úÂë¼û [gitee/funcy](https://gitee.com/funcy/spring-framework/tree/v5.2.2.RELEASE_learn/spring-learn/src/main/java/org/springframework/learn/explore/demo04). + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +this is element 01 +this is element 02 +this is element 03 +this is element 04 +``` + +¿ÉÒÔ¿´µ½£¬4 ¸ö bean ¶¼³É¹¦ÒýÈëÁË spring ÈÝÆ÷¡£½ÓÏÂÀ´£¬ÎÒÃÇͨ¹ý´úÂëÀ´¿´¿´ spring ÊÇÈçºÎ´¦ÀíµÄ¡£ + +×¢£º±¾ÎÄÊÇ `ConfigurationClassPostProcessor` ·ÖÎöµÄµÚÈýƪ£¬¶ÔÓëÇ°ÃæÁ½ÆªÎÄÕ£¨[´¦Àí @ComponentScan ×¢½â](https://my.oschina.net/funcy/blog/4836178)¡¢[´¦Àí @Bean ×¢½â](https://my.oschina.net/funcy/blog/4492878)£©À×ͬµÄ´úÂ룬±¾ÎÄ»áÒ»±Ê´ø¹ý£¬²»»á¾ßÌå·ÖÎöÁË¡£ + +### 4.3 ´¦ÀíÅäÖÃÀࣺ`ConfigurationClassPostProcessor#processConfigBeanDefinitions` + +¹ØÓÚÅäÖÃÀàµÄ½âÁ÷³Ì£¬Ç°Á½ÆªÒѾ­¶à´ÎÌáµ½¹ýÁË£¬ÕâÀïÎÒÃÇÖ±½Ó¿´¹Ø¼ü´úÂ룺 + +``` +public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { + ... + Set candidates = new LinkedHashSet<>(configCandidates); + Set alreadyParsed = new HashSet<>(configCandidates.size()); + do { + // 1. ½âÎöÅäÖÃÀà, ¶Ô@Component£¬@PropertySources£¬@ComponentScans£¬ + // @ImportResourceµÈµÄ½âÎö + parser.parse(candidates); + parser.validate(); + // ½âÎöÍê³Éºó£¬µÃµ½µÄÅäÖÃÀ࣬ÅäÖÃÀà±£´æÔÚ parser µÄ configurationClasses ÊôÐÔÖÐ + Set configClasses + = new LinkedHashSet<>(parser.getConfigurationClasses()); + ... + + // 2. °Ñ @Import ÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø@BeanµÄ·½·¨¡¢ + // @ImportResource ÒýÈëµÄ×ÊÔ´µÈת»»³ÉBeanDefinition + this.reader.loadBeanDefinitions(configClasses); + ... + // »ñµÃ×¢²áÆ÷ÀïÃæBeanDefinitionµÄÊýÁ¿ ºÍ candidateNames½øÐÐ±È½Ï + // Èç¹û´óÓڵϰ£¬ËµÃ÷ÓÐеÄBeanDefinition×¢²á½øÀ´ÁË + if (registry.getBeanDefinitionCount() > candidateNames.length) { + ... + for (String candidateName : newCandidateNames) { + // ¹ýÂ˱¾´ÎÐÂÔöµÄBeanDefinition + if (!oldCandidateNames.contains(candidateName)) { + BeanDefinition bd = registry.getBeanDefinition(candidateName); + // 3. ¶ÔÐÂÌí¼ÓµÄÀ࣬Èç¹ûÊÇÅäÖÃÀ࣬ÇÒδ½âÎö£¬Ìí¼Óµ½candidatesÖУ¬µÈ´ýÏ´ÎÑ­»·´¦Àí + if (ConfigurationClassUtils.checkConfigurationClassCandidate( + bd, this.metadataReaderFactory) + &&!alreadyParsedClasses.contains(bd.getBeanClassName())) { + candidates.add(new BeanDefinitionHolder(bd, candidateName)); + } + } + } + candidateNames = newCandidateNames; + } + } + while (!candidates.isEmpty()); + ... +} +``` + +ÒÔÉÏ´úÂë¾­¹ýÁËһЩ¾«¼ò£¬Ö»±£ÁôÁË´¦Àí `@Import` ¹Ø¼ü²½Ö裬×ܽáÈçÏ£º + +1. ½âÎöÅäÖÃÀ࣬¶Ô `@Component`£¬`@PropertySources`£¬`@ComponentScans`£¬`@ImportResource` µÈµÄ½âÎö£¬Õâ¸ö ·½·¨Ç°Á½ÆªÎÄÕÂÒ²·ÖÎöÁË£¬Õâ´ÎÎÒÃǾۼ¯ÓÚ `@Import` »áÔٴηÖÎö£¬ÔÚÕâÒ»²½£¬Òª½âÎöµÄÅäÖÃÀàÖ»ÓÐÒ»¸ö£¬¾ÍÊÇÎÒÃÇÔÚ `main()` ·½·¨ÖÐ×¢²áµÄ `Demo04.class`£º + + ![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-840c14685be569012b54d94aad67ee48825.png) + +2. °Ñ `@Import` ÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø `@Bean` µÄ·½·¨¡¢`@ImportResource` ÒýÈëµÄ×ÊÔ´µÈת»»³É `BeanDefinition`£¬Õâ¸ö·½·¨ÔÚÇ°Ãæ·ÖÎö `@Bean` µÄʱºòÒ²·ÖÎö¹ýÁË£¬Õâ´ÎÎÒÃÇÒ²»á·ÖÎöµ½£» + +3. ¶ÔÐÂÌí¼ÓµÄÀ࣬Èç¹ûÊÇÅäÖÃÀ࣬ÇÒδ½âÎö£¬Ìí¼Óµ½ `candidates` ÖУ¬µÈ´ýÏ´ÎÑ­»·´¦Àí¡£ + +### 4.4 ½âÎöÅäÖÃÀࣺ`ConfigurationClassParser#doProcessConfigurationClass` + +ÎÒÃÇÀ´¿´¿´ `@Import` ÊÇÈçºÎ½âÎöµÄ£¬½øÈë `ConfigurationClassParser#doProcessConfigurationClass`£º + +``` +/** + * Õâ¸ö·½·¨²ÅÊÇÕæÕý´¦Àí½âÎöµÄ·½·¨ + */ +protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, + SourceClass sourceClass) throws IOException { + // 1. Èç¹ûÊÇ @Component ×¢½â£¬µÝ¹é´¦ÀíÄÚ²¿À࣬±¾ÎIJ»¹Ø×¢ + ... + + // 2. ´¦Àí@PropertySource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 3. ´¦Àí @ComponentScan/@ComponentScans ×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 4. ´¦Àí@Import×¢½â + processImports(configClass, sourceClass, getImports(sourceClass), true); + + // 5. ´¦Àí@ImportResource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 6. ´¦Àí@BeanµÄ×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 7. ·µ»ØÅäÖÃÀàµÄ¸¸À࣬»áÔÚ processConfigurationClass(...) ·½·¨µÄÏÂÒ»´ÎÑ­»·Ê±½âÎö + ... + return null; +} +``` + +´¦Àí `@Import` ×¢½âµ÷ÓõÄÊÇ `processImports(...)` ·½·¨£¬ÎÒÃǼÌÐø£º + +#### 1. »ñÈ¡ `@Import` µ¼ÈëµÄÀà + +ÈÃÎÒÃǰÑÄ¿±ê·Åµ½ `processImports(...)` ·½·¨£º + +``` +processImports(configClass, sourceClass, getImports(sourceClass), true); +``` + +Õâ¸ö·½·¨¹²´«ÈëÁË 4 ¸ö²ÎÊý£º + +- `configClass`£ºÅäÖÃÀ࣬¾ÍÊÇ `demo04Main` ¶ÔÓ¦µÄÅäÖÃÀࣻ +- `sourceClass`£º¶Ô `demo04Main` ¼°ÆäÉϵÄ×¢½âµÄ°ü×°£» +- `getImports(sourceClass)`£º`getImports(...)` ÓÃÀ´»ñÈ¡´«Èë `sourceClass` µÄËùÓÐ `@Import` ×¢½âÒýÈëµÄÀࣻ +- `true`£º²¼¶ûÖµ£¬ÊÇ·ñ¼ì²éÑ­»·ÒýÈë¡£ + +ÆäÖУ¬»ñÈ¡ `@Import` ×¢½âÒýÈëµÄÀàÊÇ `getImports(...)` µÄ¹¦ÄÜ£¬ÎÒÃÇÏÈÀ´¿´¿´Õâ¸ö·½·¨ÊÇÈçºÎ»ñÈ¡µÄ£º + +``` +private Set getImports(SourceClass sourceClass) throws IOException { + Set imports = new LinkedHashSet<>(); + Set visited = new LinkedHashSet<>(); + // ÔÚÕâÀï»ñÈ¡ + collectImports(sourceClass, imports, visited); + return imports; +} + +/** + * ¾ßÌåµÄ»ñÈ¡²Ù×÷ + */ +private void collectImports(SourceClass sourceClass, Set imports, + Set visited) throws IOException { + if (visited.add(sourceClass)) { + for (SourceClass annotation : sourceClass.getAnnotations()) { + String annName = annotation.getMetadata().getClassName(); + if (!annName.equals(Import.class.getName())) { + // Èç¹ûannotationµÄÃû³Æ²»ÊÇimport£¬ÔòµÝ¹éµ÷Óà collectImports(...) ·½·¨ + collectImports(annotation, imports, visited); + } + } + // »ñÈ¡µ±Ç°ÀàµÄ @Import ×¢½â + imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); + } +} +``` + +»ñÈ¡ `@Import` µÄ·½·¨ÊÇ `ConfigurationClassParser#collectImports`£¬Õâ¸ö·½·¨Àï»á»ñÈ¡»ñÈ¡£¬²Ù×÷·½Ê½ÈçÏ£º + +1. »ñÈ¡´«ÈëÀàµÄËùÓÐ×¢½â£» +2. ±éÀúÕâЩע½â£¬Èç¹ûÊÇ `@Import` ×¢½â£¬Ôò»ñÈ¡ `@Import` µÄ `value` Öµ£»·ñÔò£¬»Øµ½µÚÒ»²½£¬¼ÌÐø´¦Àí¡£ + +ÕâÑùÖ®ºó£¬ÅäÖÃÀàÉÏ `demo04Main` É쵀 `@EnableElement` ×¢½â»á±»»ñÈ¡µ½£¬ÓÉÓÚÕâ¸ö×¢½â²»ÊÇ `@Import` ×¢½â£¬¾Í¼ÌÐø»ñÈ¡ `@EnableElement` ÆäÉϵÄ×¢½â£¬´Ëʱ·¢ÏÖ `@EnableElement` ÓÐ `@Import` ×¢½â£¬Õâʱ¾Í»á»ñÈ¡ `@Import` µÄ `value()` Öµ£¬Ò²¾ÍÊÇ `@Import` ×¢½âÒýÈëµÄÀà¡£ + +Õâ¸ö·½·¨ÔËÐÐºó £¬µÃµ½µÄ½á¹ûÈçÏ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-05c7fb442b4bb84d13ff6688e5fbf31cfa5.png) + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4e7153f9cbb922496e7c303cc3c35e7eceb.png) + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b238838641af43b783fc9b14c4226f5c1eb.png) + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d7e2063676e6e19850c35893b62f4ab76a9.png) + +µÃµ½µÄ½á¹ûΪ `LinkedHashSet`£¬Ò»´Î½ØÍ¼²»·½±ã£¬Òò´Ë·ÖΪÁË 4 ÕÅͼ¡£¿ÉÒÔ¿´µ½£¬`@Import` ×¢½âÒýÈëµÄ 4 ¸öÀà¶¼»ñÈ¡µ½ÁË£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d615198a8f6dbef1ac6a800526311f36078.png) + +#### 2. ´¦Àí `@Import` µ¼ÈëµÄÀà + +»ñÈ¡µ½ `@Import` ×¢½âµ¼ÈëµÄÀàºó£¬ÎÒÃÇÔÙÀ´¿´ `processImports(...)` ·½·¨£º + +``` +private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, + Collection importCandidates, boolean checkForCircularImports) { + ... + for (SourceClass candidate : importCandidates) { + // 1. ÒýÈëÀàµÄÀàÐÍÊÇ ImportSelector + if (candidate.isAssignable(ImportSelector.class)) { + Class candidateClass = candidate.loadClass(); + // ʵÀý»¯ÒýÈëµÄ ImportSelector£¬²¢ÇÒ»áÖ´ÐÐ Aware ½Ó¿Ú·½·¨ + // Ö§³ÖµÄAwareÓÐBeanClassLoaderAware£¬BeanFactoryAware£¬EnvironmentAware£¬ResourceLoaderAware + ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, + ImportSelector.class, this.environment, this.resourceLoader, this.registry); + if (selector instanceof DeferredImportSelector) { + this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); + } + else { + // Ö´ÐÐ selectImports ·½·¨£¬»ñÈ¡ÒýÈëµÄÀ࣬Êý×éÖÐΪÒýÈëÀàµÄ"°üÃû.ÀàÃû" + String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); + Collection importSourceClasses = asSourceClasses(importClassNames); + // µÝ¹éµ÷Óà processImports(...) ·½·¨£¬ÔÙÒ»´Î´¦ÀíÒýÈëµÄÀà + processImports(configClass, currentSourceClass, importSourceClasses, false); + } + } + // 2. ÒýÈëÀàµÄÀàÐÍÊÇ ImportBeanDefinitionRegistrar + else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { + Class candidateClass = candidate.loadClass(); + // ʵÀý»¯ ImportBeanDefinitionRegistrar£¬²¢ÇÒÖ´ÐÐ Aware ½Ó¿ÚµÄ·½·¨ + // Ö§³ÖµÄAwareÓÐBeanClassLoaderAware£¬BeanFactoryAware£¬EnvironmentAware£¬ResourceLoaderAware + ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils + .instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, + this.environment, this.resourceLoader, this.registry); + // ½« ImportBeanDefinitionRegistrar ±£´æÆðÀ´ + configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); + } + // 3. ²»ÊÇÒÔÉÏÁ½Õߣ¬µ÷Óà processConfigurationClass(...) Ö±½Ó½âÎö + // Èç¹ûÒýÈëµÄÀàÊÇÅäÖÃÀࣨ°üº¬ @Component¡¢@Configuration¡¢@Import µÈ×¢½â£©£¬»áÔÚÕâÀï½øÐнâÎö + else { + this.importStack.registerImport( + currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); + processConfigurationClass(candidate.asConfigClass(configClass)); + } + } + + ... +} +``` + +ÒÔÉÏÊǾ­¹ý¾«¼òºóµÄ´úÂ룬ÎÒÃÇÖ»¿´´¦Àí `@Import` µÄ¹Ø¼ü²½Ö裺 + +1. Èç¹ûÒýÈëÀàµÄÀàÐÍÊÇ `ImportSelector`£¬´¦ÀíÁ÷³ÌÈçÏ£º + + 1. ʹÓ÷´ÉäʵÀý»¯ `ImportSelector`£¬Ö®ºóÔÙÖ´ÐÐ `Aware` ½Ó¿Ú·½·¨£¬ËùÒÔÎÒÃÇÔÚ´¦Àí `ImportSelector` ʱ£¬»¹¿ÉÒÔʵÏÖ `Aware` ½Ó¿Ú£¬Ö§³ÖµÄ `Aware` ÓÐ `BeanClassLoaderAware`£¬`BeanFactoryAware`£¬`EnvironmentAware`£¬`ResourceLoaderAware`£» + 2. Ö´ÐÐ `ImportSelector` ʵÀýµÄ `selectImports` ·½·¨£¬ÕâÒ»²½ÊÇΪÁË»ñÈ¡ÒýÈëµÄÀ࣬½á¹ûΪ `Class[]`£¬ÒýÈëÀàµÄ "°üÃû¡£ÀàÃû"£¬`Element03Selector` Öи÷½·¨´úÂëÈçÏ£º + + ``` + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] {Element03.class.getName()}; + } + ``` + + ÕâÒ»²½»á»ñÈ¡µ½ `Element03.class`; 3. ½«»ñÈ¡µ½µÄ `Class` Êý×飬ת»»³É `SourceClass` ¼¯ºÏ£¬ÔÙÒ»´Îµ÷Óà `processImports(...)`£¬ÔÚµÚ¶þ´Îµ÷ÓÃʱ£¬²ÅÊÇÕæÕý´¦Àí `Element03`¡£ + +2. Èç¹ûÒýÈëÀàµÄÀàÐÍÊÇ `ImportBeanDefinitionRegistrar`£¬´¦ÀíÁ÷³ÌÈçÏ£º + + 1. ʵÀý»¯ `ImportBeanDefinitionRegistrar`£¬²¢ÇÒÖ´ÐÐ `Aware` ½Ó¿ÚµÄ·½·¨£¬ÕâÒ»²½Í¬ `ImportSelector` µÄʵÀý»¯²Ù×÷Ò»Ñù£¬²»ÔÙ׸Êö£» + 2. ½«ÉÏÒ»²½µÃµ½µÄ `ImportBeanDefinitionRegistrar` ʵÀý±£´æµ½ `configClass` ÖУ¬ºóÃæÎÒÃÇÔÙ·ÖÎö±£´æÆðÀ´µÄʵÀýÊÇÈçºÎ´¦ÀíµÄ£» + +3. Èç¹ûÒýÈëÀàµÄÀàÐͲ»ÊÇÒÔÉÏÁ½Õߣ¬µ÷Óà `processConfigurationClass(...)` Ö±½Ó½âÎö£¬Õâ¸ö·½·¨Ç°Á½ÆªÎÄÕÂÒѾ­¶à´ÎÌá¼°ÁË£¬¾ÍÊÇÓÃÀ´½âÎö `@Component`¡¢`@Import`¡¢`@ComponentScan`¡¢`@Configuration`¡¢`@Bean` µÈ×¢½âµÄ£¬ÕâÒ»²½ÊÇΪÁ˽âÎöÒýÈëÀàÖеÄÕâЩע½â£¬ÒýÈëµÄ `Element01`¡¢`Element02`¡¢`Element03`(ÉÏÃæµÚ 1 ²½ÖÐÌáµ½µÄ£¬´Ó `Element03Selector` ÖлñÈ¡µ½ `Element03.class` ºó£¬½«Æäת»»³É `SourceClass`£¬Ôٴε÷Óà `processImports(...)` µÄ¹ý³Ì) ¶¼ÊÇÔÚÕâÒ»²½½âÎöµÄ¡£ + +µ½ÁËÕâÀÎÒÃÇ·¢ÏÖ£¬³ýÁË `ImportBeanDefinitionRegistrar` µÄ·½Ê½Í⣬ÆäËûÈýÖÖÒýÈ뷽ʽ£¨ÆÕͨÀà¡¢ÅäÖÃÀà¡¢`ImportSelector` µÄʵÏÖÀࣩ¶¼ÊÇÒ»ÑùµÄ´¦Àí·½Ê½£¬¶¼Êǰ´**ÅäÖÃÀàµÄ½âÎö·½Ê½À´´¦Àí**£¬×îÖÕµ÷ÓõÄÊÇ `processConfigurationClass(...)` ·½·¨£¡ÕâÒ» ¿éÎÒÃÇǰÁ½ÆªÎÄÕÂÒѾ­¶à´Î·ÖÎöÁË£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË£¬½ÓÏÂÀ´ÎÒÃÇÀ´¼ÌÐøÀ´¿´ `ImportBeanDefinitionRegistrar` µÄ´¦Àí¡£ + +### 4.5 ¼ÓÔØ `BeanDefinitions`£º`ConfigurationClassBeanDefinitionReader#loadBeanDefinitions` + +ÔÚ·ÖÎö `@Bean` ·½·¨Ê±£¬ÎÒÃÇ·ÖÎö¹ý `ConfigurationClassBeanDefinitionReader#loadBeanDefinitions` ¼ÓÔØ `@Bean` ·½·¨µÄÁ÷³Ì£¬ÕâÀïÎÒÃÇÀ´¿´¼ÓÔØ `@Import` µÄÁ÷³Ì£¬ÎÒÃÇÖ±½Ó½øÈë¹Ø¼ü·½·¨ `ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass`£º + +``` +private void loadBeanDefinitionsForConfigurationClass( + ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { + ... + + // ´¦Àí @Import ÒýÈëµÄÅäÖÃÀà + if (configClass.isImported()) { + registerBeanDefinitionForImportedConfigurationClass(configClass); + } + // ´¦Àí @Bean ·½·¨ + for (BeanMethod beanMethod : configClass.getBeanMethods()) { + loadBeanDefinitionsForBeanMethod(beanMethod); + } + // ´¦Àí @ImportResource ÒýÈëµÄ×ÊÔ´ + loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); + // ´¦Àí @Import ÒýÈëµÄ ImportBeanDefinitionRegistrar + // Ç°Ãæ±£´æÔÚconfigClassÖеÄImportBeanDefinitionRegistrars£¬ÔÚÕâÀïʹÓÃÁË + loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); +} +``` + +´¦Àí `@Import` µÄµØ·½ÓÐÁ½´¦£º + +1. ´¦Àí `@Import` ÒýÈëµÄÅäÖÃÀà +2. ´¦Àí `@Import` ÒýÈëµÄ `ImportBeanDefinitionRegistrar` + +ÎÒÃÇ·Ö±ðÀ´¿´¿´ÕâÁ½´¦ÊÇÈçºÎ´¦ÀíµÄ¡£ + +#### 1. ´¦Àí `@Import` ÒýÈëµÄÅäÖÃÀà + +´¦Àí `@Import` ÒýÈëµÄÅäÖÃÀàµÄÏà¹Ø´úÂëΪ£º + +``` + // ´¦Àí @Import ÒýÈëµÄÅäÖÃÀà + if (configClass.isImported()) { + registerBeanDefinitionForImportedConfigurationClass(configClass); + } +``` + +ÎÒÃǽøÈëÆäÖÐһ̽¾¿¾¹£º + +``` +private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { + AnnotationMetadata metadata = configClass.getMetadata(); + // ´´½¨ BeanDefinition£¬ÀàÐÍΪ AnnotatedGenericBeanDefinition + AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); + // ´¦ÀíһϵÁеÄÊôÐÔ + ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); + configBeanDef.setScope(scopeMetadata.getScopeName()); + String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); + AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); + definitionHolder = AnnotationConfigUtils + .applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); + // ×¢²á + this.registry.registerBeanDefinition(definitionHolder.getBeanName(), + definitionHolder.getBeanDefinition()); + configClass.setBeanName(configBeanName); +} +``` + +Õâ¾ÍÊÇÒ»¸ö½«Àà×¢²áµ½ `BeanDefinition` ÖеĹý³Ì£¬Ê¹ÓÃµÄ `BeanDefinition` ÊÇ `AnnotatedGenericBeanDefinition`¡£ + +#### 2. ´¦Àí `@Import` ÒýÈëµÄ `ImportBeanDefinitionRegistrar` + +´¦Àí¸Ã¹ý³ÌµÄ·½·¨Îª `loadBeanDefinitionsFromRegistrars(...)`£¬´úÂëÈçÏ£º + +``` +private void loadBeanDefinitionsFromRegistrars(Map registrars) { + registrars.forEach((registrar, metadata) -> + registrar.registerBeanDefinitions(metadata, this.registry, + this.importBeanNameGenerator)); +} +``` + +¸Ã·½·¨ÏÈÊDZéÀú´«ÈëµÄ `ImportBeanDefinitionRegistrar` ¼¯ºÏ£¬È»ºóÖðÒ»µ÷ÓÃÆäÖÐµÄ `ImportBeanDefinitionRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry, BeanNameGenerator)` ·½·¨£¬¸Ã·½·¨Î»ÓÚ `ImportBeanDefinitionRegistrar` ½Ó¿Ú£¬´úÂëÈçÏ£º + +``` +public interface ImportBeanDefinitionRegistrar { + + /** + * ĬÈÏ·½·¨£¬Ä¬ÈÏʵÏÖ½ö½ö×öÁËÒ»¸öµ÷Óà + */ + default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) { + registerBeanDefinitions(importingClassMetadata, registry); + } + + /** + * ÔÚ Element02ImportBeanDefinitionRegistrar ÖÐʵÏֵķ½·¨ + */ + default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + } + +} +``` + +`ImportBeanDefinitionRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry, BeanNameGenerator)` ½öÊÇ×öÁËÒ»¸öµ÷Óã¬×îÖÕµ÷ÓõÄÊÇ `ImportBeanDefinitionRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)`£¬¶øÎÒÃÇµÄ `Element02ImportBeanDefinitionRegistrar` ÕýÊÇʵÏÖÁ˸÷½·¨£º + +``` +public class Element02ImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + registry.registerBeanDefinition("element02", new RootBeanDefinition(Element02.class)); + } +} +``` + +ÈÆÁË´ó°ëÌ죬×îÖÕ·¢ÏÖ `ImportBeanDefinitionRegistrar` ×¢²áµ½ `beanDefinitionMap` µÄÂß¼­ÊÇÎÒÃÇ×Ô¼ºÐ´µÄ£¡ + +#### 3. ´ÓÈÝÆ÷ÖлñÈ¡ `ElementXx` + +µ½ÁËÕâÀ`Element01`¡¢`Element02`¡¢`Element03`¡¢`Element04` ¾Íµ½×¢²áµ½ `beanDefinitionMap` ÖÐÁË£¬ÈÃÎÒÃÇ¿´Ò»ÑÛ `beanDefinitionNames` ÖеÄÄÚÈÝ£º + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-68171a4af503b3e80b23d10a91834e231aa.png) + +¿ÉÒÔ·¢ÏÖ£¬`Element01` Óë `Element03` beanName ²»Í¬Ñ°³££¬ÕâÁ½¸ö bean µÄÒýÈ뷽ʽΪ + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0e07154a732c2a884803531e54aa10a0ef9.png) + +ÕâµãÔÚʹÓà `beanFactory.get(¡°beanName¡±)` ʱÐèҪעÒ⣺ + +``` +// »ñÈ¡²»µ½£¬»á±¨´í +beanFactory.get("element01"); +// ÄÜ»ñÈ¡µ½ +beanFactory.get("element02"); +// »ñÈ¡²»µ½£¬»á±¨´í +beanFactory.get("element03"); +// ÄÜ»ñÈ¡µ½ +beanFactory.get("element04"); +``` + +ʹÓà `beanFactory.get(¡°beanName¡±)` »ñÈ¡ `element01` Óë `element03` ÐèÒªÕâÑù»ñÈ¡£º + +``` +// ÄÜ»ñÈ¡µ½ +beanFactory.get("org.springframework.learn.explore.demo04.element.Element01"); +// ÄÜ»ñÈ¡µ½ +beanFactory.get("org.springframework.learn.explore.demo04.element.Element03"); +``` + +µ±È»£¬ÎÒÃÇÒ²¿ÉÒÔʹÓà `beanFactory.get(Class)` µÄ·½Ê½»ñÈ¡£º + +``` +// ÄÜ»ñÈ¡µ½ +beanFactory.get(Element01.class); +// ÄÜ»ñÈ¡µ½ +beanFactory.get(Element02.class); +// ÄÜ»ñÈ¡µ½ +beanFactory.get(Element03.class); +// ÄÜ»ñÈ¡µ½ +beanFactory.get(Element04.class); +``` + +### 4.6 ²¹³ä£º`DeferredImportSelector` µÄ´¦Àí + +ÔÚ·ÖÎö `ConfigurationClassParser#processImports` ·½·¨Ê±£¬´¦Àí `ImportSelector` µÄÀàÐÍʱ£¬ÓÐÕâôһ¶Î´úÂ룺 + +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4fb0dddcbf45d84ad5260c43a5b55e85a0a.png) + +Õâ¶Î´úÂë»áÏÈÅÐ¶Ï `selector` ÊÇ·ñΪ `DeferredImportSelector` µÄʵÀý£¬Èç¹ûÊǾͰ´ `DeferredImportSelector` ÀàÐ͵ÄʵÀý½øÐд¦Àí£¬·ñÔò¾Í°´ÆÕͨµÄ `ImportSelector` ÀàÐÍÀ´´¦Àí¡£±¾½ÚÎÒÃÇÀ´¿´¿´ `DeferredImportSelector` ÓëÆÕͨµÄ `ImportSelector` Óкβ»Í¬¡£ + +`DeferredImportSelector` µÄ´úÂëÈçÏ£º + +``` +/** + * DeferredImportSelector ÊÇ ImportSelectorµÄ×Ó½Ó¿Ú + */ +public interface DeferredImportSelector extends ImportSelector { + + /** + * ·µ»Øµ¼Èë·Ö×é + */ + @Nullable + default Class getImportGroup() { + return null; + } + + + /** + * ·Ö×éµÄ¶¨Òå + */ + interface Group { + + /** + * ¸Ã·Ö×éµÄ´¦Àí·½·¨ + */ + void process(AnnotationMetadata metadata, DeferredImportSelector selector); + + /** + * ·µ»Ø¸Ã·Ö×éµÄÄÚÈÝ + */ + Iterable selectImports(); + + + /** + * ·Ö×éµÄÔªËØ¶¨Òå + */ + class Entry { + + /** + * ×¢½âÊý¾Ý + */ + private final AnnotationMetadata metadata; + + /** + * µ¼ÈëµÄÀàÃû + */ + private final String importClassName; + + public Entry(AnnotationMetadata metadata, String importClassName) { + this.metadata = metadata; + this.importClassName = importClassName; + } + + // Ê¡ÂÔ get/set ·½·¨£¬Ê¡ÂÔ equals/toString/hashCode ·½·¨ + ... + + } + } +} +``` + +´ÓÉÏÃæµÄ´úÂë¿ÉÒÔ¿´³ö£¬ + +- `DeferredImportSelector` ÊÇ `ImportSelector` µÄ×Ó½Ó¿Ú£¬¾ß±¸ `ImportSelector` µÄ¹¦ÄÜ +- `DeferredImportSelector` ÌṩÁËÒ»¸ö·½·¨£º`Class getImportGroup()`£¬¸Ã·½·¨·µ»ØµÄÊǵ±Ç° `DeferredImportSelector` ʵÀýËùÔڵķÖ×é¡£ + +½ÓÏÂÀ´ÎÒÃÇÀ´¹Ø×¢ÕâÐдúÂ룺 + +``` +this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); +``` + +À´¿´¿´ÕâÐдúÂë×ö×öÁËʲô£¬½øÈë `ConfigurationClassParser#handler·½·¨`£º + +``` +class ConfigurationClassParser { + + ... + + private class DeferredImportSelectorHandler { + + @Nullable + private List deferredImportSelectors = new ArrayList<>(); + + public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { + // ½« configClass Óë importSelector °ü×°³É DeferredImportSelectorHolder + DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( + configClass, importSelector); + if (this.deferredImportSelectors == null) { + DeferredImportSelectorGroupingHandler handler + = new DeferredImportSelectorGroupingHandler(); + handler.register(holder); + handler.processGroupImports(); + } + else { + // Ìí¼Óµ½ deferredImportSelectors ÖÐ + this.deferredImportSelectors.add(holder); + } + } + ... + } + ... +} +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö·½Ï´ÏȽ« `configClass` Óë `importSelector` °ü×°³É `DeferredImportSelector`£¬È»ºóÔÙÌí¼Óµ½ `deferredImportSelectors`¡£ + +µ½Ä¿Ç°ÎªÖ¹£¬`DeferredImportSelector` ÒýÈëµÄÀಢûÓнøÐд¦Àí£¬ÄÇô `DeferredImportSelector` ÒýÈëµÄÀàÊÇÔÚÄÄÀï´¦ÀíµÄÄØ£¿ÈÃÎÒÃǻص½ `ConfigurationClassParser#parse(Set)` ·½·¨£º + +``` +public class ConfigurationClassParser { + public void parse(Set configCandidates) { + // Ñ­»·´«½øÀ´µÄÅäÖÃÀà + for (BeanDefinitionHolder holder : configCandidates) { + BeanDefinition bd = holder.getBeanDefinition(); + try { + // Èç¹û»ñµÃBeanDefinitionÊÇAnnotatedBeanDefinitionµÄʵÀý + if (bd instanceof AnnotatedBeanDefinition) { + parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); + } + ... + } + catch (BeanDefinitionStoreException ex) { + throw ex; + } + catch (Throwable ex) { + ... + } + } + // ÔÚÕâÀï´¦Àí DeferredImportSelector + this.deferredImportSelectorHandler.process(); + } + ... +} +``` + +¿ÉÒÔ¿´µ½£¬ÔÚ´¦ÀíÍêÅäÖÃÀàµÄ½âÎöºó£¬×îºóÔÙÀ´´¦Àí `DeferredImportSelector`£¬Ò²¾ÍÊÇÉÏÃæÌí¼Óµ½ `deferredImportSelectors` µÄÄÚÈÝ£¬µ÷ÓõÄÊÇ `deferredImportSelectorHandler.process()`. + +ÎÒÃǼÌÐø£º + +``` +class ConfigurationClassParser { + + private class DeferredImportSelectorHandler { + ... + public void process() { + List deferredImports = this.deferredImportSelectors; + this.deferredImportSelectors = null; + try { + if (deferredImports != null) { + DeferredImportSelectorGroupingHandler handler + = new DeferredImportSelectorGroupingHandler(); + // ÅÅÐò£¬DeferredImportSelector ¿ÉÒÔÖ¸¶¨´¦Àí˳Ðò£¬@Order/Orderd + deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); + // ±éÀúµ÷Óà DeferredImportSelectorGroupingHandler#register ·½·¨ + deferredImports.forEach(handler::register); + // ´¦Àíµ¼Èë + handler.processGroupImports(); + } + } + finally { + this.deferredImportSelectors = new ArrayList<>(); + } + } + ... + } + ... +} +``` + +`process()` ·½·¨²½ÖèÈçÏ£º + +1. ÅÅÐò£¬Õâ¸öÖ÷ÒªÊǸù¾Ý `@Order` ×¢½â£¬»òÕßʵÏÖÁË `Orderd` ½Ó¿ÚÀ´ÅÅÐò£» +2. ±éÀúµ÷Óà `DeferredImportSelectorGroupingHandler#register` ·½·¨£¬Æäʵ¾ÍÊǽ« `deferredImports` ÖеÄÔªËØ×¢²áµ½ `handler` ÖУ» +3. µ÷Óà `handler.processGroupImports()` ·½·¨À´´¦Àíµ¼Èë¡£ + +ÎÒÃÇÔÙÀ´¿´¿´ `handler.processGroupImports()` ·½·¨£º + +``` +class ConfigurationClassParser { + + ... + + private class DeferredImportSelectorGroupingHandler { + ... + /** + * ×îÖյĴ¦ÀíÀà + * ÔÚÕâÀï´¦Àí·Ö×éµ¼Èë + */ + public void processGroupImports() { + for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { + // ±éÀú·Ö×飬 grouping.getImports()Êǹؼü + grouping.getImports().forEach(entry -> { + ConfigurationClass configurationClass = this.configurationClasses.get( + entry.getMetadata()); + try { + // ͬImportSelectorµÄʵÏÖÀàÒ»Ñù£¬×îÖÕÒ²Êǵ÷Óà processImports(...) ´¦Àíµ¼Èë + // ×¢Òâ entry.getImportClassName()£¬ÕâÒ»´Îµ÷Óà processImports(...) µÄ²Î + // ÊýÊÇImportSelectorÒýÈëµÄÀà + processImports(configurationClass, asSourceClass(configurationClass), + asSourceClasses(entry.getImportClassName()), false); + } + catch (...) { + ... + } + }); + } + } + ... + } + ... + +} +``` + +`processGroupImports(...)` Ö÷ÒªÂß¼­ÈçÏ£º + +1. ±éÀú·Ö×é +2. µ÷Óà `grouping.getImports()` »ñÈ¡·Ö×éµÄÀà +3. µ÷Óà `processImports` ´¦Àí·Ö×éÀàµÄµ¼È룬Õâµãͬ´¦Àí `ImportSelector` ½Ó¿ÚÁ÷³ÌÊÇÒ»ÑùµÄ + +ÎÒÃÇÀ´¿´¿´ `grouping.getImports()` ·½·¨£¬¸Ã·½·¨Îª `ConfigurationClassParser.DeferredImportSelectorGrouping#getImports`£¬´úÂëÈçÏ£º + +``` +public Iterable getImports() { + for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { + // Ö´ÐÐ Group#process + this.group.process(deferredImport.getConfigurationClass().getMetadata(), + deferredImport.getImportSelector()); + } + // Ö´ÐÐ Group#selectImports + return this.group.selectImports(); +} +``` + +ÔÚÕâ¸ö·½·¨À»áµ÷Óà `DeferredImportSelector.Group` µÄÁ½¸ö·½·¨£º + +- `DeferredImportSelector.Group#process` +- `DeferredImportSelector.Group#selectImports` + +¹ØÓÚÕâÁ½¸ö·½·¨£¬±¾Ð¡½ÚµÄÒ»¿ªÊ¼¾ÍÌù³öÁËÆä´úÂë¼°Æä×¢ÊÍ£¬¶øÒýÈëÀàÕýÊÇÓÉ `DeferredImportSelector.Group#selectImports` ·½·¨·µ»ØµÄ. + +·ÖÎöÁËÀϰëÌ죬×ܽáÏ `DeferredImportSelector` Óë `ImportSelector` Á½ÕßµÄÇø±ð£º + +- `DeferredImportSelector` ¿ÉÒÔÖ¸¶¨·Ö×飺ÔÚ´¦Àíʱ£¬¿ÉÒÔ¸ù¾Ý·Ö×éͳһ´¦Àí£» +- `DeferredImportSelector` ´¦Àíʱ»ú£º»áÔÚËùÔÚÅäÖÃÀà½âÎöÍêÖ®ºóÔÙ´¦Àí£» +- `DeferredImportSelector` ÒýÈëÀàµÄ·µ»Ø£º²»Í¬ÓÚ `ImportSelector`£¨ÆäÒýÈëÀàÓÉ `ImportSelector#selectImports` ·½·¨·µ»Ø£©£¬ËüµÄÒýÈëÀàÓÉ `DeferredImportSelector.Group#selectImports` ·½·¨·µ»Ø¡£ + +### 4.7 ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË `ConfigurationClassPostProcessor` ¶Ô `@Import` ×¢½âµÄ´¦Àí£¬×ܽáÈçÏÂ: + +1. `@Import` ¿Éµ¼ÈëµÄÀàÓÐ 4 ÖÖ£¬·Ö±ðÊÇÆÕͨÀà¡¢ÅäÖÃÀࡢʵÏÖÁË `ImportSelector` µÄÀàÒÔ¼°ÊµÏÖÁË `ImportBeanDefinitionRegistrar` µÄÀࣻ +2. »ñÈ¡ `@Import` ×¢½â£ºspring ÔÚ»ñÈ¡ÀàÉ쵀 `@Import` ʱ£¬ÏÈ»ñÈ¡ÀàÉϵÄËùÓÐ×¢½â£¬È»ºóÖðÒ»Åжϣ¬Èç¹ûµ±Ç°×¢½âÊÇ `@Import`£¬Ôò»ñÈ¡ `@Import` µÄÒýÈëµÄÀà (·½·¨Îª `Import#value`)£¬·ñÔò»ñÈ¡µ±Ç°×¢½âÉϵÄËùÓÐ×¢½â£¬Öظ´ÒÔÉÏ´¦Àí£» +3. ´¦Àí `@Import` ÒýÈëµÄÀࣺÆÕͨÀà¡¢ÅäÖÃÀàͳһ°´ÅäÖÃÀàÀ´½âÎö£»ÊµÏÖÁË `ImportSelector` µÄÀ࣬»á´Ó `selectImports(...)` ·½·¨·µ»ØµÄ ¡°°üÃû¡£ÀàÃû¡± ÖÐÄõ½ class£¬È»ºóÒ²Êǰ´ÅäÖÃÀà½âÎö£»ÊµÏÖ `ImportBeanDefinitionRegistrar` µÄÀ࣬»á°Ñ¶ÔÓ¦ÀàµÄʵÀý±£´æµ½µ±Ç°ÅäÖÃÀàÖУ¬ºóÃæ×¢²áµ½ `beanDefinitionMap` ʱ£¬¾ÍÊÇ´ÓÕâÀï»ñÈ¡µÄ£» +4. ½«ÒýÈëÀà×¢²áµ½ `beanDefinitionMap`£ºÊµÏÖÁË `ImportSelector` µÄÀà×îÖյõ½µÄÊÇÒ»¸öÆÕͨÀà»òÅäÖÃÀ࣬ͬÒýÈëµÄÆÕͨÀàÓëÅäÖÃÀàÒ»Ñù£¬Ö±½Ó×¢²á£»ÊµÏÖ `ImportBeanDefinitionRegistrar` µÄÀ࣬µ÷ÓÃÆä `ImportBeanDefinitionRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)` ·½·¨½øÐÐ×¢²á£¬×¢²áÂß¼­ÓÉʵÏÖÀà×ÔÐж¨Òå¡£ + +------ + +*±¾ÎÄÔ­ÎÄÁ´½Ó£ºhttps://my.oschina.net/funcy/blog/4678152 £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£* \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" new file mode 100644 index 0000000..84b7b0f --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\344\272\214\357\274\211\357\274\232\345\244\204\347\220\206@Bean\346\263\250\350\247\243.md" @@ -0,0 +1,946 @@ +±¾ÎÄÊÇ `ConfigurationClassPostProcessor` ·ÖÎöµÄµÚ¶þƪ£¬Ö÷ÒªÊÇ·ÖÎö spring ¶Ô `@Bean` ×¢½âµÄ´¦ÀíÁ÷³Ì¡£ + +## 3\. spring ÊÇÈçºÎ´¦Àí `@Bean` ×¢½âµÄ£¿ + +³Ð½ÓÉÏÎÄ£¬ÎÒÃǼÌÐø·ÖÎö spring ¶Ô `@Bean` ×¢½âµÄ´¦ÀíÁ÷³Ì¡£ + +### 3.1 demo ×¼±¸ + +ΪÁË˵Ã÷ÎÊÌ⣬ÎÒÃÇÖ±½ÓÉÏ´úÂ룺 + +Ê×ÏÈ×¼±¸Á½¸ö Bean: + +``` +public class BeanObj1 { + public BeanObj1() { + System.out.println("µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj1{}"; + } +} + +public class BeanObj2 { + public BeanObj2() { + System.out.println("µ÷ÓÃbeanObj2µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj2{}"; + } +} + +``` + +×¢Ò⣺ÒÔÉÏÁ½¸öÀ඼ûÓÐ `Component`¡¢`@Service` µÈ×¢½â¡£ + +ÔÙ×¼±¸Ò»¸öÅäÖÃÀ࣬ͨ¹ý `@Bean` ×¢½âµÄ·½·¨Éú³ÉÁ½¸ö bean£º + +``` +@Component +public class BeanConfigs { + + @Bean + public BeanObj1 beanObj1() { + return new BeanObj1(); + } + + @Bean + public BeanObj2 beanObj2() { + // ÕâÀïµ÷ÓÃÏ beanObj1() ·½·¨ + beanObj1(); + return new BeanObj2(); + } + +} + +``` + +×îºóÊÇÆô¶¯Àࣺ + +``` +@ComponentScan +public class Demo02Main { + + public static void main(String[] args) { + ApplicationContext context + = new AnnotationConfigApplicationContext(Demo02Main.class); + Object obj1 = context.getBean("beanObj1"); + Object obj2 = context.getBean("beanObj2"); + System.out.println("obj1:" + obj1); + System.out.println("obj2:" + obj2); + System.out.println(context.getBean("beanConfigs")); + } +} + +``` + +¶ÔÒÔÉÏ ´úÂ룬×öÒÔϼ¸µãÐèҪ˵Ã÷£º + +* `BeanConfigs` ÀàʹÓõÄ×¢½âµÄÊÇ `@Component`£¬¸ù¾Ý[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4836178)µÄ·ÖÎö£¬`@Component` Ò²ÊôÓÚÅäÖÃÀ࣬½âÎö²½Öèͬ `@Configuration` ×¢½â£» +* ÔÚ `Demo02Main` ÖУ¬´«Èë `AnnotationConfigApplicationContext` ÀàΪ `Demo02Main`£¬ÆäÉÏÓÐÒ»¸ö×¢½â `@ComponentScan`£¬Õâ¸ö×¢½âûָ¶¨°üɨÃè·¾¶£¬¸ù¾Ý[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4836178)µÄ·ÖÎö£¬²»Ö¸¶¨°üɨÃè·¾¶£¬spring »áĬÈÏɨÃèÅäÖÃÀàËùÔÚ°ü£» +* ÎÒÃDz¢Ã»ÓÐÖ±½Ó°Ñ `BeanConfigs` ×¢²áµ½ÈÝÆ÷ÖУ¨Ïñ `new AnnotationConfigApplicationContext(BeanConfigs.class)` ÕâÑù£©£¬´Ó[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4836178)µÄ·ÖÎö¿ÉÖª£¬spring »áÏȽâÎö `Demo02Main` À࣬´¦ÀíÆäÉ쵀 `@Component` ×¢½â£¬´Ó¶øÉ¨Ãèµ½ `BeanConfigs` À࣬Ȼºó»á½âÎö `BeanConfigs`£¬´¦ÀíÄÚ²¿µÄ `@Bean` ·½·¨£¬Õâ¸öÁ÷³ÌÎÒÃǽÓÏÂÀ´Ò²»áͨ¹ýµ÷ÊԵķ½Ê½½øÐÐÑéÖ¤¡£ + +ÔËÐÐÒÔÉÏ´úÂ룬½á¹ûÈçÏ£º + +``` +µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨ +µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨ +µ÷ÓÃbeanObj2µÄ¹¹Ôì·½·¨ +obj1:BeanObj1{} +obj2:BeanObj2{} +org.springframework.learn.explore.demo05.BeanConfigs@2b71e916 + +``` + +½ÓÏÂÀ´£¬¾ÍÒÔÕâ¸ö demo ½øÐзÖÎö¡£ + +**×¢Òâ**£º±¾ÎÄÊÇ `ConfigurationClassPostProcessor` ·ÖÎöµÄµÚ¶þƪ£¬¶ÔÓë[µÚһƪ](https://my.oschina.net/funcy/blog/4836178)À×ͬµÄ´úÂ룬±¾ÎÄÖ»Ò»±Ê´ø¹ý£¬²»»áÔÙ½øÐÐÏêϸ·ÖÎö¡£ + +### 3.2 ´¦ÀíÅäÖÃÀࣺConfigurationClassPostProcessor#processConfigBeanDefinitions + +ÎÒÃÇÖ±½Ó½øÈë `ConfigurationClassPostProcessor#processConfigBeanDefinitions` ·½·¨£¬µ÷ÓÃÁ´ÈçÏ£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors + |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry + |-ConfigurationClassPostProcessor#processConfigBeanDefinitions + +``` + +´ËʱµÄ `candidates` Ö»ÓÐÒ»¸ö ÔªËØ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0d50f32d5ea1d2b79b8e17612d30693f753.png) + +### 3.2 ½âÎö `demo02Main`£ºConfigurationClassParser#doProcessConfigurationClass + +ÕâÒ»¿é¾ÍÊǽâÎö `@ComponentScan` ×¢½âµÄ¹ý³Ì£¬[µÚһƪ](https://my.oschina.net/funcy/blog/4836178)ÒÑÏêϸ·ÖÎö¹ý£¬ÕâÀïÖ»¸ø³öµ÷ÓÃÕ»£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors + |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry + |-ConfigurationClassPostProcessor#processConfigBeanDefinitions + |-ConfigurationClassParser#parse(Set) + |-ConfigurationClassParser#parse(AnnotationMetadata, String) + |-ConfigurationClassParser#processConfigurationClass + |-ConfigurationClassParser#doProcessConfigurationClass + +``` + +´¦Àí `Demo02Main` µÄ `@ComponentScan` Ö®ºó£¬¿ÉÒÔ¿´µ½ `beanConfigs` ÒѾ­É¨Ãèµ½ÁË£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-7fef6007424562f63dda7f2a0b25e9c293b.png) + +ÓÉÓÚ `beanConfigs` ÊÇÅäÖÃÀ࣬Òò´Ë»á¶ÔÆä½øÐнâÎö£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fba3821df9cf2926b16bd064cf164e83471.png) + +×îÖÕ»¹ÊÇ»á»Øµ½ `ConfigurationClassParser#doProcessConfigurationClass`£¬ÆäÖеĵ÷ÓÃÁ´ÈçÏ£º + +``` +AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class) + |-AbstractApplicationContext#refresh + |-AbstractApplicationContext#invokeBeanFactoryPostProcessors + |-PostProcessorRegistrationDelegate + #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List) + |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors + |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry + |-ConfigurationClassPostProcessor#processConfigBeanDefinitions + |-ConfigurationClassParser#parse(Set) + |-ConfigurationClassParser#parse(AnnotationMetadata, String) + |-ConfigurationClassParser#processConfigurationClass + |-ConfigurationClassParser#doProcessConfigurationClass + |-ConfigurationClassParser#parse(String, String) + |-ConfigurationClassParser#processConfigurationClass + |-ConfigurationClassParser#doProcessConfigurationClass + +``` + +´ËʱµÄ `ConfigurationClassParser#doProcessConfigurationClass`£¬Ö÷½Ç¾Í²»ÔÙÊÇ `demo02Main`£¬¶øÊÇ `beanConfigs` ÁË¡£ + +### 3.3 ½âÎö `beanConfigs`£ºConfigurationClassParser#doProcessConfigurationClass + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `ConfigurationClassParser#doProcessConfigurationClass` ¶Ô `@Bean` ×¢½âµÄ´¦Àí£¬´úÂëÈçÏ£º + +``` +/** + * Õâ¸ö·½·¨²ÅÊÇÕæÕý´¦Àí½âÎöµÄ·½·¨ + */ +protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, + SourceClass sourceClass) throws IOException { + // 1\. Èç¹ûÊÇ @Component ×¢½â£¬µÝ¹é´¦ÀíÄÚ²¿À࣬±¾ÎIJ»¹Ø×¢ + ... + + // 2\. ´¦Àí@PropertySource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 3\. ´¦Àí @ComponentScan/@ComponentScans ×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 4\. ´¦Àí@Import×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 5\. ´¦Àí@ImportResource×¢½â£¬±¾ÎIJ»¹Ø×¢ + ... + + // 6\. ´¦Àí@BeanµÄ×¢½â + // ¾ßÌåµÄ½âÎö´úÂë + Set beanMethods = retrieveBeanMethodMetadata(sourceClass); + for (MethodMetadata methodMetadata : beanMethods) { + // Ìí¼Óµ½ configClass ÖУ¬ºóÃæÔÙ´¦Àí + configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); + } + + // 7\. ·µ»ØÅäÖÃÀàµÄ¸¸À࣬»áÔÚ processConfigurationClass(...) ·½·¨µÄÏÂÒ»´ÎÑ­»·Ê±½âÎö + ... + return null; +} + +``` + +»ñÈ¡ `@Bean` µÄ·½·¨µ÷ÓõÄÊÇ `retrieveBeanMethodMetadata(...)`£¬ÎÒÃǸú½øÈ¥£º + +``` +private Set retrieveBeanMethodMetadata(SourceClass sourceClass) { + AnnotationMetadata original = sourceClass.getMetadata(); + // »ñÈ¡°üº¬ @Bean ×¢½âµÄ·½·¨ + Set beanMethods = original.getAnnotatedMethods(Bean.class.getName()); + ... + return beanMethods; +} + +``` + +ÔÙ¸ú½øÈ¥£¬×îÖÕµ÷ÓõÄÊÇ `StandardAnnotationMetadata#getAnnotatedMethods`: + +``` +public Set getAnnotatedMethods(String annotationName) { + Set annotatedMethods = null; + if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) { + try { + // 1\. ͨ¹ý·´ÉäÀàµÄ»ñÈ¡ËùÓеķ½·¨ + Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass()); + for (Method method : methods) { + // 2\. ÅжÏÊÇ·ñÓÐ @Bean ×¢½â + if (isAnnotatedMethod(method, annotationName)) { + if (annotatedMethods == null) { + annotatedMethods = new LinkedHashSet<>(4); + } + annotatedMethods.add(new StandardMethodMetadata( + method, this.nestedAnnotationsAsMap)); + } + } + } + catch (Throwable ex) { + throw new IllegalStateException(¡£¡£¡£); + } + } + return annotatedMethods != null ? annotatedMethods : Collections.emptySet(); +} + +``` + +Õâ¸ö·½·¨ºÜºÃÀí½â£¬¹Ø¼ü¾ÍÁ½²½£º + +1. ͨ¹ý·´Éä»ñÈ¡ÀàÖÐËùÓз½·¨£» +2. ±éÀúµÃµ½µÄ·½·¨£¬ÖðÒ»Åжϸ÷½·¨ÊÇ·ñÓÐ `@Bean` ×¢½â£» + +µ½ÁËÕâÀ`beanConfigs` ÖеÄÁ½¸ö·½·¨ÖÕÓÚ»ñÈ¡µ½ÁË£¨±£´æÔÚ `ConfigurationClass` ¶ÔÏóµÄ `beanMethods` ÊôÐÔ£©£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-121f4d518fa57c8932e6b7fd3e7def8704b.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a4d91c1f52703b8c911bdb9d9afaf697d92.png) + +### 3.4 ½«±» `@Bean` ±ê¼ÇµÄ·½·¨¼ÓÔØµ½ `BeanDefinitionMap` + +ÉÏÃæ»ñÈ¡µ½µÄ `beanMethod` ´Ëʱ»¹Ö»ÊÇÔÚ `ConfigurationClass` ¶ÔÏóµÄ `beanMethods` ÊôÐÔÖУ¬²¢Ã»ÓмÓÔØµ½ `beanFactory` µÄ `BeanDefinitionMap` ÖУ¬±¾Ð¡½ÚÀ´Ì½¾¿ÏÂËüÃÇÊǺÎʱ·ÅÈëµ½ `BeanDefinitionMap` ÖС£ + +»¹¼ÇµÃ `ConfigurationClassPostProcessor#processConfigBeanDefinition` Âð£¬ÆäÖÐÓÐÕâôһÐдúÂ룺 + +``` +public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { + ... + // ´¦Àí±¾´Î½âÎöµÄÀà + // °Ñ @Import ÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø@BeanµÄ·½·¨¡¢@ImportResource ÒýÈëµÄ×ÊÔ´µÈת»»³ÉBeanDefinition + this.reader.loadBeanDefinitions(configClasses); + ... +} + +``` + +Õâ¾ÍÊǼÓÔØÒýÈëµÄ `BeanDefinition` µÄµØ·½£¬¼´°Ñ `@Import` ÒýÈëµÄÀà¡¢ÅäÖÃÀàÖдø `@Bean` µÄ·½·¨¡¢`@ImportResource` ÒýÈëµÄ×ÊÔ´µÈת»»³É `BeanDefinition`£¬±¾ÎÄÖØµã¹Ø×¢ `@Bean` ·½·¨µÄ´¦Àí£¬´úÂëÈçÏ£º + +> ConfigurationClassBeanDefinitionReader + +``` + public void loadBeanDefinitions(Set configurationModel) { + TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); + // ±éÀú´¦Àí´«ÈëµÄ configurationModel + for (ConfigurationClass configClass : configurationModel) { + loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); + } + } + + /** + * Õâ¸ö·½·¨»á¼ÓÔØ¸÷ÖÖ ConfigurationClass ÒýÈëµÄ BeanDefinition + * 1\. @Import ÒýÈëµÄÀà + * 2\. ÅäÖÃÀàÖÐµÄ @Bean ·½·¨ + * 3\. @ImportResource ÒýÈëµÄ×ÊÔ´ + * 4\. @Import ÒýÈëµÄ ImportBeanDefinitionRegistrar + */ + private void loadBeanDefinitionsForConfigurationClass( + ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { + + if (trackedConditionEvaluator.shouldSkip(configClass)) { + ... + } + + // ´¦Àí @Import ÒýÈëµÄÅäÖÃÀà + if (configClass.isImported()) { + registerBeanDefinitionForImportedConfigurationClass(configClass); + } + // ´¦Àí @Bean ·½·¨ + for (BeanMethod beanMethod : configClass.getBeanMethods()) { + loadBeanDefinitionsForBeanMethod(beanMethod); + } + // ´¦Àí @ImportResource ÒýÈëµÄ×ÊÔ´ + loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); + // ´¦Àí @Import ÒýÈëµÄ ImportBeanDefinitionRegistrar + loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); + } + + /** + * ´¦Àí @Bean ·½·¨ + * 1\. ´´½¨BeanDefinition£¬beanMethod ʹÓõÄÊÇ ConfigurationClassBeanDefinition + * 2\. ´¦Àí @Bean µÄ¸÷ÖÖÊôÐÔ£¬ÉèÖõ½ BeanDefinition ÖÐ + * 3\. ½« BeanDefinition ×¢²áµ½ beanFactory ÖÐ + */ + private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { + ConfigurationClass configClass = beanMethod.getConfigurationClass(); + MethodMetadata metadata = beanMethod.getMetadata(); + String methodName = metadata.getMethodName(); + + ... + + // 1\. beanMethodʹÓõÄÊÇConfigurationClassBeanDefinition + ConfigurationClassBeanDefinition beanDef = + new ConfigurationClassBeanDefinition(configClass, metadata); + beanDef.setResource(configClass.getResource()); + beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); + + // 2\. ´¦Àí @Bean µÄ¸÷ÖÖÊôÐÔ + if (metadata.isStatic()) { + // ¾²Ì¬ @Bean ·½·¨ + if (configClass.getMetadata() instanceof StandardAnnotationMetadata) { + beanDef.setBeanClass( + ((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass()); + } + else { + beanDef.setBeanClassName(configClass.getMetadata().getClassName()); + } + beanDef.setUniqueFactoryMethodName(methodName); + } + else { + // ÆÕͨµÄ @Bean ·½·¨ + beanDef.setFactoryBeanName(configClass.getBeanName()); + beanDef.setUniqueFactoryMethodName(methodName); + } + + if (metadata instanceof StandardMethodMetadata) { + beanDef.setResolvedFactoryMethod( + ((StandardMethodMetadata) metadata).getIntrospectedMethod()); + } + + beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); + beanDef.setAttribute( + org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor. + SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); + + AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); + + Autowire autowire = bean.getEnum("autowire"); + if (autowire.isAutowire()) { + beanDef.setAutowireMode(autowire.value()); + } + + boolean autowireCandidate = bean.getBoolean("autowireCandidate"); + if (!autowireCandidate) { + beanDef.setAutowireCandidate(false); + } + + String initMethodName = bean.getString("initMethod"); + if (StringUtils.hasText(initMethodName)) { + beanDef.setInitMethodName(initMethodName); + } + + String destroyMethodName = bean.getString("destroyMethod"); + beanDef.setDestroyMethodName(destroyMethodName); + + ... + + // 3\. ½«BeanDefinition×¢²áµ½beanFactoryÖÐ + this.registry.registerBeanDefinition(beanName, beanDefToRegister); + } + +``` + +`ConfigurationClassBeanDefinitionReader#loadBeanDefinitions` ÓÖµ÷ÓÃÁËÁ½¸ö·½·¨£¬×îÖÕ´¦ÀíÊÇÔÚ `ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod`£¬Õⲿ·ÖÂß¼­ÈçÏ£º + +1. ±éÀú´«ÈëµÄ `configClass` ¼¯ºÏ£¬¶Ôÿ¸ö `configClass` ½øÐÐ `Definitions` ¼ÓÔØ´¦Àí£» +2. ¶Ô `configClass` ½øÐÐ `Definitions` ¼ÓÔØ´¦Àíʱ£¬»áÖðÒ»´¦Àí `@Import`/`@Bean`/`@ImportResource` µÈ×¢½â£¬±¾Ð¡½ÚÎÒÃǽö¹Ø×¢ `@Bean` µÄ´¦Àí£» +3. ´¦Àí `@Bean` ·½·¨Ê±£¬ÏÈ´´½¨ `BeanDefinition`(`@Bean` ·½·¨¶ÔÓ¦µÄ `BeanDefinition` ÊÇ `ConfigurationClassBeanDefinition`)£¬È»ºó½âÎö `@Bean` µÄÊôÐÔ£¬ÉèÖõ½ `BeanDefinition` ÖУ¬×îÖÕÊÇ°Ñ `BeanDefinition` ×¢²áµ½ `beanFactory` ÖÐ. + +ºÃ ÁË£¬Ö´ÐÐÍê `ConfigurationClassBeanDefinitionReader#loadBeanDefinitions` ºó£¬`BeanDefinition` ¾Í¼ÓÔØµ½ `beanFactory` ÁË£¬¶ÔÓ¦µÄ `BeanDefinition` ÀàÐÍÊÇ `ConfigurationClassBeanDefinition`. + +### 3.5 `@Bean` ´´½¨ÊµÀý + +ʵÀýµÄ´´½¨Á÷³ÌͬÆÕͨµÄ `@Component` ÀàÒ»Ö£¬²»Í¬µÄÊÇÆÕͨµÄ `@Component` Ààµ÷ÓõÄÊǹ¹Ôì·½·¨£¬¶ø `@Bean` ʹÓõÄÊÇ `factoryMethod`£¬´úÂëÈçÏ£º + +> AbstractAutowireCapableBeanFactory#createBeanInstance + +``` +/** + * ʵÀýµÄ´´½¨·½Ê½ + * 1\. ʹÓà instanceSupplier£¬SupplierÊÇjava8ÌṩµÄÀ࣬¿ÉÒÔ´«ÈëÒ»¸ölambda±í´ïʽ + * 2\. ʹÓù¤³§·½·¨£¬Èç @Bean ×¢½â¶ÔÓ¦µÄ·½·¨ + * 3\. ʹÓõÄÊǹ¹Ôì·½·¨×¢È룬¼´¹¹Ôì·½·¨ÉÏÓÐ @Autowired ×¢½â + * 4\. ¹¹Ôì·½·¨×¢È룬¿ÉÒÔÊÇÎ޲ι¹Ô죬Ҳ¿ÉÒÔÊÇÓвι¹Ôì + * + */ +protected BeanWrapper createBeanInstance(String beanName, + RootBeanDefinition mbd, @Nullable Object[] args) { + // È·±£ÒѾ­¼ÓÔØÁË´Ë class + Class beanClass = resolveBeanClass(mbd, beanName); + ... + + // ÊÇ·ñÉèÖÃÁËbean´´½¨µÄSupplier£¬SupplierÊÇjava8ÌṩµÄÀ࣬¿ÉÒÔ´«ÈëÒ»¸ölambda±í´ïʽ + // µ÷Óà AbstractBeanDefinition#setInstanceSupplier Ö¸¶¨ + Supplier instanceSupplier = mbd.getInstanceSupplier(); + if (instanceSupplier != null) { + return obtainFromSupplier(instanceSupplier, beanName); + } + if (mbd.getFactoryMethodName() != null) { + // ²ÉÓù¤³§·½·¨ÊµÀý»¯ + return instantiateUsingFactoryMethod(beanName, mbd, args); + } + // ÊÇ·ñµÚÒ»´Î + boolean resolved = false; + // ÊÇ·ñ²ÉÓù¹Ô캯Êý×¢Èë + boolean autowireNecessary = false; + ... + if (resolved) { + if (autowireNecessary) { + return autowireConstructor(beanName, mbd, null, null); + } + else { + // Î޲ι¹Ô캯Êý + return instantiateBean(beanName, mbd); + } + } + // Candidate constructors for autowiring? + // ÅжÏÊÇ·ñ²ÉÓÃÓвι¹Ô캯Êý + Constructor[] ctors = determineConstructorsFromBeanPostProcessors( + beanClass, beanName); + if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || + mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { + return autowireConstructor(beanName, mbd, ctors, args); + } + ctors = mbd.getPreferredConstructors(); + if (ctors != null) { + // ¹¹Ô캯ÊýÒÀÀµ×¢Èë + return autowireConstructor(beanName, mbd, ctors, null); + } + // µ÷ÓÃÎ޲ι¹Ô캯Êý + return instantiateBean(beanName, mbd); +} + +``` + +´Ó´úÂëÉÏÀ´¿´£¬spring ʵÀý¶ÔÏóµÄ·½Ê½ÓÐ 4 ÖÖ£º + +1. ʹÓà `instanceSupplier`£¬`Supplier` ÊÇ `java8` ÌṩµÄÀ࣬¿ÉÒÔ´«ÈëÒ»¸ö `lambda` ±í´ïʽ +2. ʹÓù¤³§·½·¨£¬Èç `@Bean` ×¢½â¶ÔÓ¦µÄ·½·¨ +3. ʹÓõÄÊǹ¹Ôì·½·¨×¢È룬¼´¹¹Ôì·½·¨ÉÏÓÐ `@Autowired` ×¢½â +4. ¹¹Ôì·½·¨×¢È룬¿ÉÒÔÊÇÎ޲ι¹Ô죬Ҳ¿ÉÒÔÊÇÓвι¹Ôì + +ÕâÀïÎÒÃÇÖ÷Òª¹Ø×¢ `@Bean` µÄʵÀý·½Ê½£¬Ò²¾ÍÊǹ¤³§·½·¨ÊµÀý»¯·½Ê½£¬ÎÒÃǽøÈ¥¿´Ï£º + +``` +public BeanWrapper instantiateUsingFactoryMethod(String beanName, + RootBeanDefinition mbd, @Nullable Object[] explicitArgs) { + BeanWrapperImpl bw = new BeanWrapperImpl(); + this.beanFactory.initBeanWrapper(bw); + Object factoryBean; + Class factoryClass; + boolean isStatic; + String factoryBeanName = mbd.getFactoryBeanName(); + if (factoryBeanName != null) { + factoryBean = this.beanFactory.getBean(factoryBeanName); + factoryClass = factoryBean.getClass(); + isStatic = false; + } + ... + + Method factoryMethodToUse = null; + ArgumentsHolder argsHolderToUse = null; + Object[] argsToUse = null; + // ´¦Àí factoryMethod µÄ²ÎÊý + if (explicitArgs != null) { + argsToUse = explicitArgs; + } + else { + ... + } + + if (factoryMethodToUse == null || argsToUse == null) { + factoryClass = ClassUtils.getUserClass(factoryClass); + List candidates = null; + if (mbd.isFactoryMethodUnique) { + if (factoryMethodToUse == null) { + factoryMethodToUse = mbd.getResolvedFactoryMethod(); + } + if (factoryMethodToUse != null) { + candidates = Collections.singletonList(factoryMethodToUse); + } + } + // Ê¡ÂÔÁ˺öà´úÂë + ... + } + bw.setBeanInstance(instantiate(beanName, mbd, + factoryBean, factoryMethodToUse, argsToUse)); + return bw; +} + +``` + +ÒÔÉÏ·½·¨ÊǾ­¾«¼òºóµÄ´úÂ룬ԭ±¾·½·¨´úÂë±È½Ï¶à£¬´ó²¿·Ö´úÂëÊÇÔÚÔÚ´¦Àí `argsToUse` Óë `factoryMethodToUse` ²ÎÊý£¬Ï¸½Ú·Ç³£¶à£¬¾Í²»Õ¹¿ª·ÖÎöÁË£¬ÕâÀïÎÒÃÇÖ÷Òª¹Ø×¢ÒÔϼ¸¸ö±äÁ¿£º + +1. `factoryBean`£º `@Bean` ·½·¨ËùÔÚÀàµÄʵÀý£¬ÕâÀïÊÇ `beanConfig`; +2. `factoryMethodToUse`: ʵÀý»¯ËùÓõķ½·¨£¬Ò²¾ÍÊDZ» `@Bean` ×¢½âµÄ·½·¨£¬ÕâÀïÊÇ `BeanConfigs#beanObj1`; +3. `argsToUse`£º±» `@Bean` ×¢½âµÄ·½·¨ÒªÓõIJÎÊý£¬ÓÉÓÚ `BeanConfigs#beanObj1` ûÓÐÖ¸¶¨²ÎÊý£¬ÕâÀïÊÇ null; + +ÕâÈý¸ö±äÁ¿ÊÇÓÃÀ´½øÐÐʵÀý»¯µÄ±äÁ¿£¬ÊµÀý»¯·½Ê½´óÖÂÒ²ÄÜÏëµ½ÁË£¬ÊµÀý¡¢·½·¨¼°·½·¨²ÎÊý¶¼ÓÐÁË£¬½ÓÏÂÀ´¾ÍÊǵ÷Ó÷´Éä½øÐÐʵÀý»¯ÁË£º + +> ConstructorResolver#instantiate(...) + +``` +private Object instantiate(String beanName, RootBeanDefinition mbd, + @Nullable Object factoryBean, Method factoryMethod, Object[] args) { + try { + + return this.beanFactory.getInstantiationStrategy().instantiate( + mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args); + } + catch (Throwable ex) { + ... + } +} + +``` + +> SimpleInstantiationStrategy#instantiate(...) + +``` +@Override +public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, + @Nullable Object factoryBean, final Method factoryMethod, Object... args) { + try { + ... + try { + currentlyInvokedFactoryMethod.set(factoryMethod); + // ÕâÀï¾ÍÏ൱ÓÚµ÷Óà beanConfigs.beanObj1() + Object result = factoryMethod.invoke(factoryBean, args); + if (result == null) { + result = new NullBean(); + } + return result; + } + finally { + ... + } + } + catch (...) { + ... + } +} + +``` + +×îÖÕÊÇÔÚ `SimpleInstantiationStrategy#instantiate(...)` ·½·¨ÖнøÐз´ÉäʵÀý»¯ÁË£¡ + +ʵÀý»¯Íê³Éºó£¬ºóÃæÒÀÀµ×¢Èë¡¢³õʼ»¯µÈ¶¼ÓëÆÕͨµÄ spring bean Ò»Ö£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£ + +### 3.6 `@Configuration` Óë `@Bean` ×éºÏʹÓà + +ÉÏÃæ½éÉÜÁË `@Component` Óë `@Bean` ʹÓÃʱµÄ´úÂë·ÖÎö£¬¼´ + +``` +@Component +public class BeanConfigs { + @Bean + public Xxx xxx() { + ... + } +} + +``` + +ʵ¼ÊÉÏ£¬´ó¶àÊýÇé¿öÏÂÎÒÃÇʹÓõÄÊÇ `@Configuration` Óë `@Bean` µÄ×éºÏ£º + +``` +@Configuration +public class BeanConfigs { + @Bean + public Xxx xxx() { + ... + } +} + +``` + +ÕâÓëÎÒÃÇÇ°ÃæÊ¹ÓÃµÄ `@Component` Óкβî±ðÄØ£¿±¾½ÚÎÒÃǾÍÀ´·ÖÎöÏ¡£ + +#### 1\. demo ×¼±¸ + +demo ×¼±¸£º + +``` +//@Component +@Configuration +public class BeanConfigs { + + @Bean + public BeanObj1 beanObj1() { + return new BeanObj1(); + } + + @Bean + public BeanObj2 beanObj2() { + // ÕâÀïµ÷ÓÃÏ beanObj1() ·½·¨ + beanObj1(); + return new BeanObj2(); + } + +} + +``` + +Õâ¸ö demo ½ö½öÖ»Êǽ« `@Component` Ìæ»»Îª `@Configuration`£¬Ö´ÐÐÏ£¬½á¹ûÈçÏ£º + +``` +µ÷ÓÃbeanObj1µÄ¹¹Ôì·½·¨ +µ÷ÓÃbeanObj2µÄ¹¹Ôì·½·¨ +obj1:BeanObj1{} +obj2:BeanObj2{} +org.springframework.learn.explore.demo02.BeanConfigs$$EnhancerBySpringCGLIB$$dca1c55b@75c072cb + +``` + +¿´³öÇø±ðÁËÂð£¬ÎÒÃǽ«Ö®Ç°µÄÖ´ÐÐÒ²·ÅÔÚÕâÀ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-88e5047479e4f30e70d09397d2631c0c6a5.png) + +¾­¹ý±È½Ï£¬·¢ÏÖÓÐÁ½´¦²»Í¬£º + +1. `beanObj1` µÄ¹¹Ôì·½·¨½öµ÷ÓÃÁËÒ»´Î£» +2. `beanConfigs` ¶ÔÓ¦µÄÀàÊÇ `BeanConfigs$$EnhancerBySpringCGLIB$$dca1c55b@75c072cb`£¬ËµÃ÷ËüÊÇÒ»¸ö´úÀí¶ÔÏó£¬Ê¹ÓÃÁË cglib ´úÀí¡£ + +ʵ¼ÊÉÏ£¬ÒÔÉÏÁ½¸ö²»Í¬µã¶¼¿ÉÒÔ¹é½áΪһ¸öÔ­Òò£ºspring ¶Ô `beanConfigs` ½øÐÐÁË´úÀí£¬µ÷Óà `BeanConfigs#beanObj1` ʵ¼Êµ÷ÓõÄÊÇ´úÀí·½·¨£¬¼´ **spring »á¶Ô±» `@Configuration` ±ê¼ÇµÄÀà½øÐÐ cglib ´úÀí**£¡ + +ÄÇô´úÀíÊÇÔõô´´½¨¼°ÔËÐеÄÄØ£¿ÎÒÃǼÌÐøÌ½¾¿¡£ + +#### 2\. ´úÀíÀàµÄ´´½¨£º`ConfigurationClassPostProcessor#enhanceConfigurationClasses` + +ÔÚ[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4836178)µÄ¿ªÆª£¬ÎÒÃǾÍÌáµ½¹ý `ConfigurationClassPostProcessor` ÔÚÖ´ÐÐʱ»áµ÷Óõ½Á½¸ö·½·¨£º`processConfigBeanDefinitions(...)` Óë `enhanceConfigurationClasses(...)`£¬`processConfigBeanDefinitions(...)` ÒýÆðÁË spring ¶Ô `@Import`¡¢`@Configuration` µÈ×¢½âµÄ½âÎö£¬Ç°ÃæÒѾ­·ÖÎö¹ýÁË£»¶ø `enhanceConfigurationClasses(...)` ¾ÍÊDZ» `@Configuration` ±ê¼ÇµÄÀà´úÀí²úÉúµÄ¹Ø¼üËùÔÚ£¡ + +`enhanceConfigurationClasses(...)` ·½·¨´úÂëÈçÏ£º + +> ConfigurationClassPostProcessor#enhanceConfigurationClasses + +``` +public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { + Map configBeanDefs = new LinkedHashMap<>(); + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); + Object configClassAttr = beanDef.getAttribute( + ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE); + ... + // 1\. ÅжÏÊÇ·ñΪһ¸öÈ«ÅäÖÃÀà + if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) { + ... + configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); + } + } + // È«ÅäÖÃÀࣺ´¦Àí´úÀí + ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); + for (Map.Entry entry : configBeanDefs.entrySet()) { + AbstractBeanDefinition beanDef = entry.getValue(); + beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); + // ´¦Àí BeanClass + Class configClass = beanDef.getBeanClass(); + // 2\. Éú³É enhancedClass + Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); + if (configClass != enhancedClass) { + // 3\. ÉèÖà BeanClass£¬ÖµÎªenhancedClass + beanDef.setBeanClass(enhancedClass); + } + } +} + +``` + +Õâ¸ö·½·¨±È½Ï¼òµ¥£¬²½ÖèÈçÏ£º + +1. ÅжÏÅäÖÃÀàÊÇ·ñΪȫÅäÖÃÀ࣬ÔÚÔÚ[ÉÏһƪÎÄÕÂ](https://my.oschina.net/funcy/blog/4836178)ÎÄÕÂÖУ¬ÎÒÃÇÌáµ½ spring »á°Ñ´øÓÐ `@Configuration` ×¢½âÇÒ `proxyBeanMethods != false` µÄÀà±ê¼ÇΪ `Full` ÅäÖÃÀ࣬ÕâÀïÕýÊǸù¾ÝÇ°ÃæµÄ±ê¼ÇÀ´ÅжϷñΪȫÅäÖÃÀ࣬ºÜÃ÷ÏÔ£¬´ËʱµÄ `beanConfigs` ¾ÍÊÇÒ»¸öÈ«ÅäÖÃÀࣻ +2. ¶ÔÈ«ÅäÖÃÀ࣬»á¸ù¾ÝÆä `configClass` Éú³É¶ÔÓ¦µÄ `enhancedClass`£» +3. ½«Éú³ÉµÄ `enhancedClass` ÉèÖõ½ `beanDefinition` µÄ `beanClass` ÖС£ + +Ö´ÐÐÍê´Ë·½·¨ºó£¬`beanConfigs` ¶ÔÓ¦µÄ `beanDefinition` µÄ `beanClass` ¾ÍÊÇ´úÀíÀàÁË£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f617fc93f72b13b8f785ccf5a12624dec0b.png) + +ºóÃæ´´½¨µÄ `beanConfigs` ¾ÍÊÇÕâ¸ö´úÀíÀàµÄʵÀýÁË¡£ + +#### 3\. Ö´ÐдúÀí¶ÔÏóµÄ·½·¨ + +Éú³É´úÀí¶ÔÏóºó£¬´úÀí·½·¨ÊÇÈçºÎÖ´ÐеÄÄØ£¿¼´ spring ÊÇÈçºÎÖ´ÐÐ `beanConfigs.beanObj1()` µÄÄØ£¿ËµÆðÕâ¸ö£¬¾ÍÐèҪ̸µ½ cglib ´úÀí¶ÔÏóµÄ·½·¨Ö´ÐÐÁË¡£ÎÒÃÇÖ±½ÓÀ´¿´´úÀíµÄÉú³É£¬½øÈë `enhancer.enhance(configClass, this.beanClassLoader)`£º + +``` +public Class enhance(Class configClass, @Nullable ClassLoader classLoader) { + if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { + return configClass; + } + // newEnhancer(...) ·½·¨²ÅÊǹؼü + Class enhancedClass = createClass(newEnhancer(configClass, classLoader)); + return enhancedClass; +} + +``` + +¼ÌÐø½øÈ룺 + +``` +private static final Callback[] CALLBACKS = new Callback[] { + // Õâ¸öÀàÓÃÀ´±£Ö¤ @Bean ·½·¨µÄµ¥Àý + new BeanMethodInterceptor(), + new BeanFactoryAwareMethodInterceptor(), + NoOp.INSTANCE +}; + +// Éú³É CallbackFilter£¬´«ÈëµÄ¶ÔÏóΪCallback +private static final ConditionalCallbackFilter CALLBACK_FILTER + = new ConditionalCallbackFilter(CALLBACKS); + +// Éú³ÉcglibÔöÇ¿ +private Enhancer newEnhancer(Class configSuperClass, @Nullable ClassLoader classLoader) { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(configSuperClass); + enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class}); + enhancer.setUseFactory(false); + enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); + enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); + // ´úÀí²¿·ÖÔÚcallbackFilter + enhancer.setCallbackFilter(CALLBACK_FILTER); + enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); + return enhancer; +} + +``` + +¹ØÓÚ cglib ´úÀíµÄÄÚÈÝ£¬ÔÚ [spring aop Ö® cglib ´úÀí](https://my.oschina.net/funcy/blog/4696655)Ò»ÎÄÖÐÒÑÏêϸ·ÖÎö¹ýÁË£¬Õâ²»ÔÙ·ÖÎö£¬ÎÒÃÇÖ±½Ó˵½áÂÛ£ºcglib Ö´ÐдúÀí·½·¨Ê±£¬Ö´ÐеÄÊÇ `Enhancer` ÖÐ `callbackFilter` ÊôÐ﵀ `MethodInterceptor#intercept` ·½·¨£¬¼´ `CALLBACKS` Êý×éÖÐµÄ `BeanMethodInterceptor`£¬ÏÂÃæÎÒÃǾÍÀ´¿´ÏÂËüµÄÄÚÈÝ£º + +> ConfigurationClassEnhancer.BeanMethodInterceptor + +``` +private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback { + @Override + @Nullable + public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, + MethodProxy cglibMethodProxy) throws Throwable { + ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); + String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); + ... + // Èç¹ûÊǵ÷Óõ±Ç°µÄ factoryMethod ·½·¨£¬Ö±½Óµ÷Óø¸ÀàµÄ·½·¨ + if (isCurrentlyInvokedFactoryMethod(beanMethod)) { + // µ÷Óø¸ÀàµÄ·½·¨£¬Ò²¾ÍÊÇÄ¿±ê·½·¨ + return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); + } + // ·ñÔòÖ±½Ó»ñÈ¡ beanFactoryÖÐÒÑÓеĶÔÏó + return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); + } + + private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, + ConfigurableBeanFactory beanFactory, String beanName) { + try { + ... + // µ÷ÓõĵÄÊÇ beanFactory.getBean(...) ·½·¨£¬Õâ¸ö·½·¨ÎÒÃÇÒѾ­·Ç³£ÊìϤÁË + Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : + beanFactory.getBean(beanName)); + ... + return beanInstance; + } + finally { + ... + } + } + + /** + * ÅжÏÄÜ·ñÖ´Ðе±Ç° MethodInterceptor + */ + @Override + public boolean isMatch(Method candidateMethod) { + return (candidateMethod.getDeclaringClass() != Object.class && + !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) && + BeanAnnotationHelper.isBeanAnnotated(candidateMethod)); + } +} + +``` + +`BeanMethodInterceptor` ʵÏÖÁË `MethodInterceptor` Óë `ConditionalCallback`£¬`ConditionalCallback#isMatch` ÓÃÀ´Åжϵ±Ç° `MethodInterceptor` ÄÜ·ñÖ´ÐУ¬`MethodInterceptor#intercept` ¾ÍÊÇÖ´Ðеķ½·¨ÄÚÈÝ£¬Ö´ÐÐÂß¼­Îª£º + +1. Èç¹ûÊÇÖ±½Óµ÷Óõ±Ç°µÄ `factoryMethod` ·½·¨£¬Ö±½Óµ÷Óø¸ÀàµÄ·½·¨£¬Ò²¾ÍÊÇ `beanConfigs.beanObj1()`£¬Õâ¸ö¹ý³Ì»áʵÀý»¯ `beanObj1` ʱ±»µ÷Óã» +2. Èç¹û²»ÊÇÖ±½Óµ÷Óõ±Ç°µÄ `factoryMethod` ·½·¨£¨±ÈÈçÔÚ±ðµÄ·½·¨Öе÷Óã©£¬Ôòµ÷Óà `beanFactory.getBean(...)` »ñÈ¡ bean£¬Õâ¸ö¹ý³Ì»áÔÚʵÀý»¯ `beanObj1` ʱ»á ±»µ÷Óᣠ+ +ÒÔÉϾÍÊÇΪʲô `beanObj1` µÄ¹¹Ôì·½·¨Ö»µ÷ÓÃÁËÒ»´Î£¬ÒÔ¼°ÎªÊ²Ã´ `beanConfigs` ÊÇ´úÀíÀàµÄÔ­ÒòËùÔÚÁË¡£ + +×îºóÔÙÌáÒ»¾ä£¬`@Configuration` ÌṩÁË `proxyBeanMethods()` ·½·¨À´ÈÃÎÒÃÇÑ¡ÔñÊÇ·ñ¿ªÆôÅäÖÃÀàµÄ´úÀí£¬Ä¬ÈÏÖµÊÇ true£¬Èç¹ûÏñÕâÑùÉèÖ㺠+ +``` +@Configuration(proxyBeanMethods=false) +public class BeanConfigs { + ... +} + +``` + +`BeanConfigs` ¾Í²»»á½øÐдúÀíÁË£¬ÔËÐнá¹ûͬ `@Component` ×¢½âÒ»Ñù£¬ÕâÀï¾Í²»Õ¹Ê¾ÁË¡£ + +#### 4\. ¼¸¸öСÎÊÌâ + +##### 1\. ÄÚ²¿·½·¨µ÷ÓÃÒ²Äܱ»´úÀíÂ𣿠+ +ÔÚʾÀýÖУ¬ÎÒÃÇÊÇÕâÑùµ÷Óõģº + +``` + @Bean + public BeanObj2 beanObj2() { + // ÕâÀïµ÷ÓÃÏ beanObj1() ·½·¨ + beanObj1(); + return new BeanObj2(); + } + +``` + +¼´ÔÚ `beanObj2()` Öе÷ÓÃÁË `beanObj1()`£¬ÕâÃ÷ÏÔÊÇÄÚ²¿·½·¨µ÷Óã¬`beanObj1()` Ò²Äܱ»´úÀíÂ𣿠+ +**»Ø´ð**£ºcglib ´úÀíµÄµ÷Ó÷½·¨ÓÐÁ½ÖÖ£º + +``` +@Override +public Object intercept(Object proxyObj, Method method, Object[] objects, + MethodProxy proxy) throws Throwable { + // ·½°¸1£º ʹÓÃÄ¿±ê¶ÔÏó£¬Ö±½Óµ÷ÓÃÄ¿±ê¶ÔÏóµÄ·½·¨ + // return proxy.invoke(target, objects); + // ·½°¸2£º ʹÓôúÀí¶ÔÏ󣬵÷ÓÃÆä¸¸ÀàµÄ·½·¨ + return proxy.invokeSuper(proxyObj, objects); +} + +``` + +`beanObj2()` µÄµ÷ÓÃʹÓÃÊÇ`·½°¸2`£¬Ò²¾ÍÊÇʹÓôúÀí¶ÔÏóµ÷Óà `beanObj2()`£¬`beanObj2()` µÄ `this` Ϊ´úÀí¶ÔÏó£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-7ef891450ace815d22a5bf5c8e78e65c2db.png) + +Òò´ËÔÚ `beanObj2()` ÖÐÖ±½Óµ÷Óà `beanObj1()`£¬¾ÍÏ൱ÓÚʹÓôúÀí¶ÔÏóµ÷Óà `beanObj1()`£¬µ±È»Äܱ»´úÀíÁË¡£ + +ÔÚÎÒÃÇÓ¡ÏóÖУ¬·½·¨µÄÄÚ²¿µ÷Óò»Äܱ»´úÀí£¬ÄÇÊÇÒòΪ spring ÔÚ´¦Àí Aop ʱ£¬Ê¹ÓõÄÊÇ·½°¸ 1 µÄµ÷Ó÷½Ê½£¬´ËʱµÄ `this` Ϊԭʼ¶ÔÏ󣬵±È»²»Äܱ»´úÀíÁË¡£ + +##### 2\. ˽ÓÐÊôÐÔÈçºÎ×¢È룿 + +±ÈÈ磬ÎÒÃÇÏÖÔÚÓÐÒ»¸ö `BeanObj3`: + +``` +@Component +public class BeanObj3 { + + public BeanObj3() { + System.out.println("µ÷ÓÃbeanObj3µÄ¹¹Ôì·½·¨"); + } + + @Override + public String toString() { + return "BeanObj3{}"; + } +} + +``` + +È»ºóÔÚ `BeanConfigs` ÖÐ×¢È룺 + +``` +@Configuration +public class BeanConfigs { + + @Autowired + private BeanObj3 beanObj3; + + @Bean + public BeanObj1 beanObj1() { + return new BeanObj1(); + } + + @Bean + public BeanObj2 beanObj2() { + // ÕâÀïµ÷ÓÃÏ beanObj1() ·½·¨ + beanObj1(); + System.out.println("beanObj3£º" + this.beanObj3); + return new BeanObj2(); + } + +} + +``` + +ÔÚ `BeanConfigs` ÖÐ×Ô¶¯×¢ÈëÁË `beanObj3` ÊôÐÔ£¬È»ºóÔÚ `beanObj2()` ÖÐÓÖ´òÓ¡ÁË `beanObj3` ÊôÐÔ¡£ÔËÐУ¬½á¹ûÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e21bf76da41fcf12e674fa155de4b636253.png) + +¿ÉÒÔ¿´µ½£¬×¢ÈëµÄ `beanObj3` Ò²ÄÜ»ñÈ¡µ½ÁË¡£ÕâÀï¾ÍÓиöÎÊÌâÁË£º`beanObj3` ÊÇÊôÓÚÄ¿±ê¶ÔÏóµÄ£¬¶ø `this` ÊÇ´úÀí¶ÔÏó£¬ÄѲ»³É´úÀí¶ÔÏóÄÜÄõ½Ä¿±ê¶ÔÏóµÄ˽ÓÐÊôÐÔ£¿ + +Ê×ÏÈ£¬Ìí¼Óµ½ `beanFactory` µÄ `beanDefinitionMap` ÖеÄÀàÊÇ `BeanConfigs$$EnhancerBySpringCGLIB$$Xxx` Àࣨ´úÀíÀࣩ£¬¶ø²»ÊÇ `BeanConfigs`£¬spring ÔÚ½øÐÐÊôÐÔ×¢Èëʱ£¬»á²éÕÒµ±Ç°À༰Æä¸¸ÀàµÄËùÓеÈ×¢ÈëÊôÐÔ½øÐÐ×¢È룬Òò´Ë£¬ËäÈ»Ìí¼Óµ½ spring ÈÝÆ÷ÖÐµÄ ÊÇ `BeanConfigs$$EnhancerBySpringCGLIB$$Xxx` À࣬µ« `BeanConfigs` ÖÐµÄ `beanObj3` Ò»Ñù»á±»×¢È룬ÖÁÓÚÔ­ÒòÂÓÉÓÚ cglib µÄ´úÀí¹ØÏµ£¬`BeanConfigs` ÊÇ `BeanConfigs$$EnhancerBySpringCGLIB$$Xxx` µÄ¸¸Àà¡£ + +ÄÇ `BeanConfigs$$EnhancerBySpringCGLIB$$Xxx` »á¼Ì³Ð `beanObj3` ÊôÐÔÂð£¿ÕâÀïÖ±½Ó¿´ÔËÐнá¹û°É£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-27ebdebfd8c958f1de362ca1b30f2d0fc7c.png) + +ÕâÊÇ×îÖյõ½µÄ `beanConfigs` ¶ÔÏ󣬿ÉÒÔ¿´µ½£¬ËüÀïÃæ¾ÍÓÐÒ»¸ö `beanObj3` ÊôÐÔ£¬²¢ÇÒ»¹ÓÐÖµ¡£ + +### 3.7 ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË `ConfigurationClassPostProcessor` ´¦Àí `@Bean` ×¢½âµÄ¹ý³Ì£¬×ܽáÈçÏ£º + +1. ½âÎöÅäÖÃÀ࣬ͨ¹ý·´Éä»ñÈ¡ÅäÖÃÀàÀïËùÓб» `@Bean` ±ê¼ÇµÄ·½·¨£» +2. ±éÀúÕâЩ·½·¨£¬½«Æä·â×°³ÉÒ»¸ö¸ö `BeanDefinition` ×¢²áµ½ `beanFactory` ÖУ¬¶ÔÓ¦ `BeanDefinition` ¾ßÌåÀàÐÍΪ `ConfigurationClassBeanDefinition`£» +3. Èç¹ûÅäÖÃÀàÊÇÈ«ÅäÖÃÀ࣬»á¶ÔÅäÖÃÀà½øÐÐ cglib ´úÀí£» +4. ʵÀý»¯Ê±£¬Ê¹Ó÷´Éäµ÷ÓöÔÓ¦µÄ·½·¨Éú³ÉʵÀý£¨µÃµ½ÊµÀýºó£¬spring »áÔÙ¶ÔÆä½øÐÐÒÀÀµ×¢Èë¡¢³õʼ»¯µÈ£©£» +5. ÔÚ±ðµÄ `@Bean` ·½·¨Öе÷Óõ±Ç° `@Bean` ·½·¨Ê±£¬Èç¹ûµ±Ç° `@Bean` ·½·¨ËùÔÚµÄÀàÊÇÈ«ÅäÖÃÀ࣬Ôò»áÈ¥ `beanFactory` ÖвéÕÒ¶ÔÓ¦µÄ `bean`(²éÕҵĹý³ÌÊÇ£¬ÕÒµ½Ôò·µ»Ø£¬ÕÒ²»µ½Ôò´´½¨ÔÙ·µ»Ø£¬**·µ»ØµÄ bean ÓÐÍêÕûµÄ spring bean µÄÉúÃüÖÜÆÚ**)£¬Õâ¸ö²Ù×÷ÊÇÓÉ cglib ´úÀíÍê³É£»Èç¹ûµ±Ç° `@Bean` ·½·¨ËùÔÚµÄÀ಻ÊÇÈ«ÅäÖÃÀ࣬Ôò»á°´ÕÕÆÕͨµÄ·½·¨µ÷Óã¬Éú³É bean µÄʵÀý·µ»Ø£¨**·µ»ØµÄ bean ûÓÐÍêÕûµÄ spring bean µÄÉúÃüÖÜÆÚ**£©¡£ + +±¾ÎĵķÖÎö¾Íµ½ÕâÀïÁË£¬½ÓÏÂÀ´ÎÒÃǼÌÐø·ÖÎö `ConfigurationClassPostProcessor` ´¦ÀíÆäËû×¢½âµÄÁ÷³Ì¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4492878](https://my.oschina.net/funcy/blog/4492878) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" new file mode 100644 index 0000000..5d11cae --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/ConfigurationClassPostProcessor\357\274\210\345\233\233\357\274\211\357\274\232\345\244\204\347\220\206@Conditional\346\263\250\350\247\243.md" @@ -0,0 +1,385 @@ +±¾ÎÄÊÇ `ConfigurationClassPostProcessor` ·ÖÎöµÄµÚËÄÆª£¬Ö÷ÒªÊÇ·ÖÎö spring ¶Ô `@Conditional` ×¢½âµÄ´¦ÀíÁ÷³Ì¡£ + +## 5\. spring ÊÇÈçºÎ´¦Àí @Conditional ×¢½âµÄ£¿ + +### 5.1 `@Conditional` µÄ´¦ÀíÁ÷³Ì + +ÔÚÇ°Ãæ·ÖÎö `ConfigurationClassParser#processConfigurationClass` ·½·¨Ê±£¬ÓÐÕâôһÐУº + +``` +class ConfigurationClassParser { + protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { + // ÅжÏÊÇ·ñÐèÒªÌø¹ý´¦Àí£¬Õë¶ÔÓÚ @Conditional ×¢½â£¬ÅжÏÊÇ·ñÂú×ãÌõ¼þ + if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), + ConfigurationPhase.PARSE_CONFIGURATION)) { + return; + } + ... + } + ... +} + +``` + +`conditionEvaluator.shouldSkip(...)` ·½·¨¾ÍÊÇÓÃÀ´´¦Àí `@Conditional` ×¢½âµÄ£¬¹ØÓÚÕâ¸ö·½·¨µÄ´¦ÀíÁ÷³Ì£¬ÎÒÃÇÍíµãÔÙ·ÖÎö£¬ÎÒÃÇÏÈÀ´¿´¿´Ê²Ã´ÊÇ `@Conditional` ×¢½â£º + +``` +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Conditional { + + /** + * Ìõ¼þÀà + */ + Class[] value(); + +} + +``` + +`@Conditional` ×¢½â·Ç³£¼òµ¥£¬½öÓÐÒ»¸öÊôÐÔ£¬·µ»ØÖµÊÇ `Class[]`£¬ÇÒ±ØÐëÊÇ `Condition` µÄ×ÓÀà¡£ÎÒÃÇÔÙÀ´¿´¿´ `Condition`£º + +``` +public interface Condition { + + /** + * ÔÚÕâÀïÖ¸¶¨Æ¥ÅäÂß¼­ + */ + boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); + +} + +``` + +`Condition` ½Ó¿Ú½öÓÐÒ»¸ö `matches` ·½·¨£¬ÎÒÃÇ¿ÉÒÔÔÚÆäÖÐÖ¸¶¨Æ¥ÅäÂß¼­¡£ + +½Ó×ÅÎÒÃÇÀ´¿´¿´ `conditionEvaluator.shouldSkip(...)` µÄ´¦ÀíÁ÷³Ì£º + +``` +class ConditionEvaluator { + public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, + @Nullable ConfigurationPhase phase) { + // ÊÇ·ñ±ê¼Ç @Conditional + if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { + return false; + } + // Åжϴ«ÈëµÄ phase + if (phase == null) { + if (metadata instanceof AnnotationMetadata && + ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { + return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); + } + return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); + } + + // ʵÀý»¯ condition£¬Æä·ÅÈë conditions ÖÐ + List conditions = new ArrayList<>(); + // 1\. getConditionClasses(metadata)£º»ñÈ¡ @Conditional ÖÐÖ¸¶¨µÄÅжÏÀà + for (String[] conditionClasses : getConditionClasses(metadata)) { + for (String conditionClass : conditionClasses) { + // 2\. ʵÀý»¯²Ù×÷£¨Óõ½µÄ»¹ÊÇ·´É䣩£¬Í³Ò»·Åµ½ conditions ÖÐ + Condition condition = getCondition(conditionClass, this.context.getClassLoader()); + conditions.add(condition); + } + } + // 3\. ÅÅÐòÉÏÃæµÃµ½µÄ condition ʵÀý + AnnotationAwareOrderComparator.sort(conditions); + // ±éÀúÉÏÃæµÃµ½µÄ conditions + for (Condition condition : conditions) { + ConfigurationPhase requiredPhase = null; + if (condition instanceof ConfigurationCondition) { + requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); + } + // 4\. µ÷Óà Condition#matches ·½·¨½øÐÐÅÐ¶Ï + if ((requiredPhase == null || requiredPhase == phase) && + !condition.matches(this.context, metadata)) { + return true; + } + } + + return false; + } + + ... +} + +``` + +¸Ã·½·¨µÄ´¦ÀíÁ÷³ÌÈçÏ£º + +1. »ñÈ¡ `@Conditional` ÖÐÖ¸¶¨µÄÅжÏÀ࣬¾ÍÊÇ `@Conditional` µÄ `value` ÊôÐÔÖµ£» +2. ʹÓà ·´ÉäʵÀý»¯µÚ 1 ²½ÖÐ µÃµ½µÄÅжÏÀ࣬²¢ ±£´æµ½ `conditions`£¨ÕâÊǸö `List`£© ÖУ» +3. ¶ÔµÚ 2 ²½µÃµ½µÄ `conditions` ½øÐÐÅÅÐò£» +4. ±éÀúµÚ 3 ²½µÃµ½µÄ `conditions`£¬µ÷Óà `Condition#matches` ·½·¨½øÐÐÆ¥Åä¡£ + +`@Conditional` µÄ´¦Àí»¹ÊǷdz£¼òµ¥µÄ£¬½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ËüµÄʹÓÃʾÀý¡£ + +### 5.2 `@Conditional` ʹÓÃʾÀý + +#### ʾÀý 1£ºµ±Ö¸¶¨µÄÀà´æÔÚʱ£¬²Å´´½¨ spring bean + +ÕâÀïÎÒÃÇʵÏÖ¸ö¹¦ÄÜ£ºµ±Ö¸¶¨µÄÀà´æÔÚʱ£¬²Å½øÐÐ spring bean µÄ´´½¨¡¢³õʼ»¯£¬´úÂëÈçÏ£º + +1. ×¼±¸Ò»¸ö¼òµ¥µÄ bean: + + ``` + public class BeanObj { + + } + + ``` + +2. ʵÏÖ `Condition` ½Ó¿Ú£¬ÔÚÕâÀï´¦ÀíÅжÏÂß¼­ + + ``` + public class MyCondition implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String className = "java.lang.Object"; + try { + // ÅжÏÀàÊÇ·ñ´æÔÚ + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + } + + ``` + + ÔÚ `matches(...)` ·½·¨ÖÐ £¬ÏÈÊÇÖ¸¶¨ `className` Ϊ `java.lang.Object`£¬È»ºóÅÐ¶ÏÆäÊÇ·ñ´æÔÚ£¬ÅжϷ½Ê½Ò² Ê®·Ö¼òµ¥ £¬¾ÍÊÇʹÓà java µÄ·´Éä»úÖÆ£º`Class.forName(...)`£¬µ±À಻´æÔÚʱ£¬»áÅ׳ö `ClassNotFoundException`£¬ÎÒÃÇ¿ÉÒÔ²¶»ñ¸ÃÒì³££¬´Ó¶ø¾ÍÖªµÀÀà´æÔÚ²»´æÔÚÁË¡£ + +3. ×¼±¸ÅäÖÃÀà + + ``` + @ComponentScan + public class BeanConfigs { + @Bean + @Conditional(MyCondition.class) + public BeanObj beanObj() { + return new BeanObj(); + } + } + + ``` + + ÅäÖÃÀà±È½Ï¼òµ¥£¬ÐèҪעÒâµÄÊÇ `beanObj()` É쵀 `@Conditional` ×¢½â£¬Ö¸¶¨µÄÊÇÌõ¼þÆ¥ÅäÀàÊÇ `MyCondition`£¬Æ¥Åä²Ù×÷¾ÍÊÇÔÚÕâ¸öÀàÖнøÐеġ£ + +4. Ö÷Àà + + ``` + public class Demo06Main { + public static void main(String[] args) { + ApplicationContext context + = new AnnotationConfigApplicationContext(BeanConfigs.class); + try { + Object obj = context.getBean("beanObj"); + System.out.println("beanObj ´æÔÚ£¡"); + } catch (Exception e) { + System.out.println("beanObj ²»´æÔÚ£¡"); + } + } + } + + ``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +beanObj ´æÔÚ£¡ + +``` + +ÔÚ `MyCondition#matches` ÖУ¬ÎÒÃÇÅжϵÄÊǵ±Ç°ÏîÄ¿ÖÐÊÇ·ñ´æÔÚ `java.lang.Object`£¬ÏÔÈ»ÕâÊÇ´æÔڵģ¬Òò´Ë `beanObj` »áÔÚ spring ÈÝÆ÷ÖУ¬½Ó×ÅÎÒÃÇ»»Ï `className`: + +``` +public class MyCondition implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + // ¸ü»»ÀàÃû + String className = "java.lang.Object111"; + ... + } +} + +``` + +ÏÔÈ»£¬`java.lang.Object111` ÊDz»´æÔÚÓÚµ±Ç°ÏîÄ¿Öеģ¬ÔËÐУ¬½á¹ûÈçÏ£º + +``` +beanObj ²»´æÔÚ£¡ + +``` + +#### ʾÀý 2£º¸Ä½øÊ¾Àý 1 ¹¦ÄÜ + +ÔÚʾÀý 1 ÖУ¬ÎÒÃÇͨ¹ýÔÚ `MyCondition#matches` ÐÞ¸Ä `className` À´¸Ä±ä `beanObj` ÔÚÈÝÆ÷ÖеĴæÔÚÇé¿ö£¬ÄÇÈç¹ûÏîÄ¿ÖÐÓзdz£¶àµÄÀàÐèÒª°´ÀàµÄ´æÔÚÓë·ñ½øÐмÓÔØ£¬ÎÒÃÇÊÇʵÏÖ¶à¸ö `MyCondition` Â𣿠+ +±ÈÈ磬Àà `A` ÐèÒª¸ù¾ÝÀà `A1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯£¬Àà `B` ÐèÒª¸ù¾ÝÀà `B1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯£¬Àà `C` ÐèÒª¸ù¾ÝÀà `C1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯... ÎÒÃÇÊÇ·ñÐèÒª·Ö±ðΪ`ÀàA`¡¢`ÀàB`¡¢`ÀàC` ʵÏÖ `Condition`£¬ÔÚ¸÷×﵀ `match(...)` ·½·¨ÖнøÐÐÅжÏÂ𣿠+ +ʵ¼ÊÉÏ£¬ÎÒÃDz¢²»ÐèÒªÕâô×ö£¬ÕâÀïͨ¹ý spring ×éºÏ×¢½âµÄ·½·¨À´Íê³ÉÒÔÉϹ¦ÄÜ¡£ + +1. ×¼±¸Ò»¸ö bean£¬ÕâÓëʾÀý 1 ²¢ÎÞÇø±ð + + ``` + public class BeanObj { + + } + + ``` + +2. ×¼±¸×¢½â `@ConditionalForClass`£¬¸Ã×¢½â×éºÏÁË `@Conditional` µÄ¹¦ÄÜ£¬´¦ÀíÌõ¼þÆ¥ÅäµÄÀàΪ `MyCondition`£¬`className` ÊôÐÔ¾ÍÊDZØÐë´æÔÚµÄÀࣺ + +``` +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +// ×éºÏÁË @Conditional µÄ¹¦ÄÜ£¬´¦ÀíÌõ¼þÆ¥ÅäµÄÀàΪ MyCondition +@Conditional(MyCondition.class) +public @interface ConditionalForClass { + + /** + * ÕâÀïÖ¸¶¨±ØÐë´æÔÚµÄÀà + */ + String className(); + +} + +``` + +1. ×¼±¸ `MyCondition`£¬×¢ÒâÓëʾÀýµÄ²î±ðÔÚÓÚ£¬`className` ²»ÊÇÔڸ÷½·¨Öж¨Ò壬¶øÊÇÓÉ `@ConditionalForClass` ´«È룺 + + ``` + public class MyCondition implements Condition { + /** + * ÕâÀï´¦ÀíÆ¥ÅäÌõ¼þ£¬×¢ÒâÓëʾÀý1ÖеÄÇø±ð + */ + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + // »ñÈ¡ @ConditionalForClass ×¢½âµÄËùÓÐÊôÐÔÖµ + Map annotationAttributes = metadata.getAnnotationAttributes( + ConditionalForClass.class.getName()); + // »ñÈ¡classNameµÄÊôÐÔÖµ£¬¾ÍÊÇ @ConditionalForClass µÄ className ÊôÐÔ + String className = (String)annotationAttributes.get("className"); + if(null == className || className.length() <= 0) { + return true; + } + try { + // ÅжÏÀàÊÇ·ñ´æÔÚ + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + } + + ``` + +2. ×¼±¸ÅäÖÃÀ࣬´ËʱµÄÌõ¼þ×¢½âΪ `@ConditionalForClass`£º + + ``` + @ComponentScan + public class BeanConfigs { + @Bean + /** + * ÔÚ @ConditionalForClass ÖÐÖ¸¶¨ÁËÒÀÀµµÄÀà + */ + @ConditionalForClass(className = "java.lang.Object") + public BeanObj beanObj() { + return new BeanObj(); + } + } + + ``` + +3. ×îºóÊÇÖ÷À࣬ÓëʾÀý 1 ²¢²»Çø±ð£º + + ``` + public class Demo07Main { + + public static void main(String[] args) { + ApplicationContext context + = new AnnotationConfigApplicationContext(BeanConfigs.class); + try { + Object obj = context.getBean("beanObj"); + System.out.println("beanObj ´æÔÚ£¡"); + } catch (Exception e) { + System.out.println("beanObj ²»´æÔÚ£¡"); + } + } + } + + ``` + +ÒÔÉÏÀàͨ¹ý×Ô¶¨Òå×¢½â `@ConditionalForClass` À´Ö¸¶¨£¬µ±Àà `java.lang.Object` ´æÔÚʱ£¬`beanObj` ²Å»á±»Ìí¼Óµ½ spring ÈÝÆ÷ÖУ¬Õâ¸öÌõ¼þÏÔÈ»³ÉÁ¢£¬ÔËÐУ¬½á¹ûÈçÏ£º + +``` +beanObj ´æÔÚ£¡ + +``` + +ÎÒÃÇÔÙÀ´µ÷ÕûÏ `@ConditionalForClass` µÄ `className` Öµ£º + +``` +@ComponentScan +public class BeanConfigs { + @Bean + // ½öÐÞ¸ÄÁË@ConditionalForClassµÄclassNameÖµ£¬ÆäËûÌõ¼þ²»±ä + @ConditionalForClass(className = "java.lang.Object1111") + public BeanObj beanObj() { + return new BeanObj(); + } +} + +``` + +ÕâÀォ `@ConditionalForClass` µÄ `className` Öµµ÷ÕûΪ `java.lang.Object1111`£¬ÏÔÈ»Õâ¸öÀಢ²»ÔÚµ±Ç°ÏîÄ¿ÖУ¬ÔËÐнá¹ûÈçÏ£º + +``` +beanObj ²»´æÔÚ£¡ + +``` + +½á¹ûÒ²ÓëÎÒÃÇµÄÆÚÍûÒ»Ö¡£ + +ÈÃÎÒÃǻص½±¾½Ú¿ªÍ·µÄÎÊÌ⣺±ÈÈ磬Àà `A` ÐèÒª¸ù¾ÝÀà `A1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯£¬Àà `B` ÐèÒª¸ù¾ÝÀà `B1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯£¬Àà `C` ÐèÒª¸ù¾ÝÀà `C1` µÄ´æÔÚÓë·ñÀ´ÅжÏÊÇ·ñ½øÐгõʼ»¯... ÎÒÃÇÊÇ·ñÐèÒª·Ö±ðΪ`ÀàA`¡¢`ÀàB`¡¢`ÀàC` ʵÏÖ `Condition`£¬ÔÚ¸÷×﵀ `match(...)` ·½·¨ÖнøÐÐÅжÏÂ𣿠+ +ÓÐÁË `@ConditionalForClass` ×¢½âºó£¬ÎÒÃDz¢²»ÐèÒªÕâôÂé·³£¬Ö»ÐèÒªÔÚ¸÷×﵀ `@Bean` ·½·¨ÉÏÌí¼Ó `@ConditionalForClass` ¾ÍÐÐÁË£¬ÏñÕâÑù£º + +``` +@Bean +@ConditionalForClass(className = "A1") +public A a() { + return new A(); +} + +@Bean +@ConditionalForClass(className = "B1") +public B b() { + return new B(); +} + +@Bean +@ConditionalForClass(className = "B1") +public C c() { + return new C(); +} + +... + +``` + +×¢ÒâÌå»á `@ConditionalForClass` µÄʵÏÖ£¬springboot ÖÐµÄ `@ConditionalOnClass` ¾ÍÊǰ´ÕâÖÖ˼·ʵÏֵġ£ + +### 5.3 ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË spring ´¦Àí `@Conditional` µÄÁ÷³Ì£¬Âß¼­±È½Ï¼òµ¥£¬×îÖÕµ÷ÓõÄÊÇ `Condition#matches` ·½·¨½øÐÐÆ¥Åä²Ù×÷µÄ£¬¶øÆ¥Åä²Ù×÷ÓÉ `Condition` µÄʵÏÖÀà×ÔÐÐÖ¸¶¨¡£ + +ΪÁ˸üºÃµØËµÃ÷ `@Conditional` µÄʹÓ㬱¾ÎÄ×¼±¸ÁËÁ½¸öʹÓÃʾÀý£¬ÌرðÊÇʾÀý 2£¬ÐèÒªÌØ±ðÌå»á£¬springboot ÖÐµÄ `@ConditionalOnClass` ÕýÊÇ»ùÓÚʾÀý 2 µÄ˼·½øÐÐʵÏֵģ¬ÁíÍ⣬springboot ÖеĶà¸öÌõ¼þ×¢½âÒ²ÊÇ¶Ô `@Conditional` µÄÀ©Õ¹¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4873444](https://my.oschina.net/funcy/blog/4873444) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" new file mode 100644 index 0000000..89b3927 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213AOP\347\232\204\346\211\247\350\241\214\351\241\272\345\272\217.md" @@ -0,0 +1,671 @@ +spring aop Ö´ÐÐʱ£¬Ë³ÐòÊÇÔõÑùµÄ£¬ÈçºÎ¸Ä±äÖ´ÐеÄÓÅÏȼ¶£¿±¾ÎĽ«´ÓÔ´ÂëÉÏÀ´Ì½¾¿ aop Ö´ÐÐ˳ÐòµÄÃØÃÜ¡£ + +ÔÚ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817) Óë [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)ÖУ¬×Ý¹Û spring aop ´´½¨ÓëÖ´Ðйý³Ì£¬ÎÒÃÇÒ»¹²Óöµ½Á½´Î¹ØÓÚ aop µÄÅÅÐò²Ù×÷£º + +* `ReflectiveAspectJAdvisorFactory#getAdvisorMethods`£º¶Ô `aspect` ÖÐµÄ `@Around/@Before/@After` µÈ·½·¨½øÐÐÅÅÐò£» +* `AspectJAwareAdvisorAutoProxyCreator#sortAdvisors`£º¶Ô `advisor` ½øÐÐÅÅÐò¡£ + +½ÓÏÂÀ´ÎÒÃÇÖØµãÀ´·ÖÎöÕâÁ½¸ö·½·¨¡£ + +#### 1 `ReflectiveAspectJAdvisorFactory#getAdvisorMethods` + +µÚÒ»´ÎÅÅÐòÔÚ `ReflectiveAspectJAdvisorFactory#getAdvisorMethods`£¬µ÷ÓýṹÈçÏ£º + +``` +|-AbstractAutoProxyCreator#postProcessBeforeInstantiation + |-AspectJAwareAdvisorAutoProxyCreator#shouldSkip + |-AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors + |-BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors + |-ReflectiveAspectJAdvisorFactory#getAdvisors + |-ReflectiveAspectJAdvisorFactory#getAdvisorMethods + +``` + +´úÂëÈçÏ£º + +> ReflectiveAspectJAdvisorFactory + +``` +// »ñÈ¡ @Aspect ÀàÖеķ½·¨ +private List getAdvisorMethods(Class aspectClass) { + final List methods = new ArrayList<>(); + // Ê¡ÂÔ»ñÈ¡·½·¨µÄ²Ù×÷ + ... + + //¶ÔµÃµ½µÄËùÓз½·¨ÅÅÐò£¬ + methods.sort(METHOD_COMPARATOR); + return methods; +} + +``` + +Õâ¸ö·½·¨µÄ²Ù×÷ÔÚ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817) Ò»ÎÄÒÑ×÷ÁËÏêϸ·ÖÎö£¬ÕâÀïÎÒÃǽö¹Ø×¢ÅÅÐò¹æÔò£º + +> ReflectiveAspectJAdvisorFactory + +``` +/** + * METHOD_COMPARATOR Ïê½â + */ +private static final Comparator METHOD_COMPARATOR; +static { + Comparator adviceKindComparator = new ConvertingComparator<>( + // ±È½ÏÆ÷£¬°´´«Èë˳Ðò½øÐÐ±È½Ï + new InstanceComparator<>( + Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), + // ת»»Æ÷£¬½«·½·¨×ª»¯Îª @Around, @Before, @After, @AfterReturning, @AfterThrowing µÈ×¢½â + (Converter) method -> { + AspectJAnnotation annotation = + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); + return (annotation != null ? annotation.getAnnotation() : null); + }); + // ת»»±È½ÏÆ÷£¬ + // 1\. ת»»£º½«´«ÈëµÄ·½·¨(Method)ת»»Îª·½·¨Ãû(String) + // 2\. ±È½Ï£º°´´«ÈëÀàÐͽøÐбȽϣ¬ÕâÀï´«ÈëµÄµÄÀàÐÍΪString£¬Ô­ÒòÊÇת»»Æ÷½«´«ÈëµÄMethodת»»³ÉStringÁË + Comparator methodNameComparator = new ConvertingComparator<>(Method::getName); + /* + * METHOD_COMPARATOR ±È½Ï¹æÔò£º + * 1\. Èç¹û·½·¨±êʶÁËÇÐÃæ×¢½â, Ôò°´ @Around, @Before, @After, @AfterReturning, + * @AfterThrowing ˳ÐòÅÅÐò (`adviceKindComparator`) + * 2\. Èç¹ûûÓбêʶÕâЩע½â£¬Ôò°´·½·¨Ãû³ÆµÄ×Ö·û´®ÅÅÐò(`methodNameComparator`) + */ + METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); +} + +``` + +ÕâÀï¶ÔÕâ¸ö·½·¨µÄÅÅÐò¹æÔò×ܽáÈçÏ£º + +1. Õâ¸ö·½·¨ÅÅÐòµÄ¶ÔÏóÊÇͬһ¸ö `@Aspect` Öеķ½·¨£» +2. ¶ÔÓÚÇÐÃæ·½·¨£¬ÅÅÐòÈçÏ£º`@Around`, `@Before`, `@After`, `@AfterReturning`, `@AfterThrowing`£» +3. ¶ÔÓÚ·ÇÇÐÃæ·½·¨£¬°´·½·¨Ãû£¨String µÄÅÅÐò¹æÔò£©ÅÅÐò¡£ + +¸Ã·½·¨ÅÅÐòǰºóµÄ±ä»¯ÈçÏ£º + +ÅÅÐòǰ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-23df6d6a46f37badb1017ceee8dcfa6533e.png) + +ÅÅÐòºó£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-85bdb16953c1c6348d39578de6b6144c1cc.png) + +ÅÅÐòºóÓëÎÒÃÇ·ÖÎöµÄ `@Around, @Before, @After, @AfterReturning,@AfterThrowing` µÄ˳ÐòÒ»Ö¡£ + +µÃµ½ `List` ºó£¬½Ó×Å»á±éÀúÕâЩ `method`£¬½«Æä°üװΪһ¸ö¸ö `advisor`£º + +> ReflectiveAspectJAdvisorFactory + +``` +public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { + // Ê¡ÂÔÁËһЩ´úÂë + ... + + List advisors = new ArrayList<>(); + //»ñÈ¡Õâ¸öÀàËùÓеÄÔöÇ¿·½·¨ + for (Method method : getAdvisorMethods(aspectClass)) { + // Éú³ÉÔöǿʵÀý£¬advisors.size() ÒÀ´ÎΪ 0£¬1£¬2£¬... ÕâÊÇ + // declarationOrderInAspect µÄÖµ£¬ºóÃæÅÅÐò»áÓõ½ + Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, + advisors.size(), aspectName); + if (advisor != null) { + advisors.add(advisor); + } + } + + // Ê¡ÂÔÁËһЩ´úÂë + ... +} + +``` + +°ü×°³É `Advisor` ʱ£¬»á´«Èë `declarationOrderInAspect` Öµ£¬¸ÃֵΪ `advisors.size()`£¬ÒÀ´ÎΪ `0£¬1£¬2£¬...`£¬Õâ¸öÖµÔÚºóÃæµÄÅÅÐòÖлáÓõ½¡£ + +### 2\. advisor µÄÅÅÐò£º`AspectJAwareAdvisorAutoProxyCreator#sortAdvisors` + +`@Aspect` ÀàÖеÄÇÐÃæ·½·¨°ü×°³É `advisor` ºó£¬»òÕß»ñÈ¡Íê×Ô¶¨Òå `advisor` ºó£¬½ÓמͽøÐÐÁ˵ڶþ´ÎÅÅÐò£º`AspectJAwareAdvisorAutoProxyCreator#sortAdvisors`£¬·½·¨µÄµ÷ÓÃÁ´ÈçÏ£º + +``` +|-AbstractAutoProxyCreator#postProcessAfterInitialization + |-AbstractAutoProxyCreator#wrapIfNecessary + |-AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean + |-AbstractAdvisorAutoProxyCreator#findEligibleAdvisors + |-AspectJAwareAdvisorAutoProxyCreator#sortAdvisors + +``` + +> AspectJAwareAdvisorAutoProxyCreator + +``` +protected List sortAdvisors(List advisors) { + List partiallyComparableAdvisors + = new ArrayList<>(advisors.size()); + for (Advisor element : advisors) { + partiallyComparableAdvisors.add( + // ±È½Ï¹æÔòΪ DEFAULT_PRECEDENCE_COMPARATOR,ÆäʵÊÇAspectJPrecedenceComparator + new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR)); + } + // ¾ßÌåµÄ±È½Ï²Ù×÷£¬±È½Ï¹æÔòÓÉ AspectJPrecedenceComparator Ìṩ + List sorted + = PartialOrder.sort(partiallyComparableAdvisors); + if (sorted != null) { + List result = new ArrayList<>(advisors.size()); + for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) { + result.add(pcAdvisor.getAdvisor()); + } + return result; + } + else { + return super.sortAdvisors(advisors); + } +} + +``` + +ÕâÀï½øÐÐÁËÁ½¸öÅÅÐò£¬Ò»¸öÊÇ `PartialOrder.sort(...)`£¬Ò»¸öÊÇ `super.sortAdvisors(...)`£¬ÎÒÃÇÏÈÀ´·ÖÎö `PartialOrder.sort(...)`¡£ + +#### 2.1 `PartialOrder.sort(...)` µÄ±È½ÏÆ÷£º`AspectJPrecedenceComparator` + +ʵ¼ÊÉÏ£¬`PartialOrder.sort(...)` Ö»Òª×öÁËÒ»¸öÅÅÐò¶øÒÑ£¬Õâ¸ö·½·¨Ã»É¶·ÖÎöµÄ£¬ÎÒÃÇÕæÕýÒª·ÖÎöµÄÓ¦¸ÃÊÇ´«ÈëµÄÅÅÐò¹æÔò£¬Ò²¾ÍÊÇ `DEFAULT_PRECEDENCE_COMPARATOR`£º + +``` +private static final Comparator DEFAULT_PRECEDENCE_COMPARATOR + = new AspectJPrecedenceComparator(); + +``` + +`DEFAULT_PRECEDENCE_COMPARATOR` µÄÀàÐÍÊÇ `AspectJPrecedenceComparator`£¬ÎÒÃÇÖ±½Ó²é¿´Æä `compare(xxx)` ·½·¨£º + +> AspectJPrecedenceComparator + +``` +@Override +public int compare(Advisor o1, Advisor o2) { + // ±È½Ï¹æÔò£ºAnnotationAwareOrderComparator + int advisorPrecedence = this.advisorComparator.compare(o1, o2); + // ˳ÐòÏàͬ£¬ÇÒÀ´Ô´ÓÚͬһ aspect£¬µ÷Óà comparePrecedenceWithinAspect ÔÙ´Î±È½Ï + if (advisorPrecedence == SAME_PRECEDENCE && declaredInSameAspect(o1, o2)) { + // ±È½ÏÉùÃ÷˳Ðò£¬Èç¹ûÆäÖÐÓÐÒ»¸öÊÇafter֪ͨ£¬ÔòºóÉùÃ÷µÄÓÅÏȼ¶¸ß£»·ñÔòÏÈÉùÃ÷µÄÓÅÏȼ¶¸ß + advisorPrecedence = comparePrecedenceWithinAspect(o1, o2); + } + return advisorPrecedence; +} + +``` + +`AspectJPrecedenceComparator#compare` ±È½Ï¼òµ¥£¬¹ý³ÌÈçÏ£º + +1. µ÷Óà `advisorComparator.compare` ½øÐбȽϣ¬Õâ¸ö±È½Ï¹æÔòÎÒÃǽÓÏÂÀ´»á·ÖÎö£» +2. Èç¹ûÓÉÉÏÊö±È½Ï¹æÔòµÃµ½µÄÓÅÏȼ¶Ïàͬ£¬ÇÒÁ½¸ö `advisor` ÊÇÔÚͬһ aspect Öж¨ÒåµÄ£¬Ôòµ÷Óà `comparePrecedenceWithinAspect` ¼ÌÐø±È½Ï. + +##### `this.advisorComparator.compare` + +ÎÒÃÇÀ´¿´¿´ `this.advisorComparator.compare` µÄ±È½Ï¹æÔò£º + +``` +private final Comparator advisorComparator; + +public AspectJPrecedenceComparator() { + this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE; +} + +public int compare(Advisor o1, Advisor o2) { + // ±È½Ï¹æÔò£ºAnnotationAwareOrderComparator + int advisorPrecedence = this.advisorComparator.compare(o1, o2); + ... +} + +``` + +`this.advisorComparator.compare` µÄ±È½Ï¹æÔòÓÉ `AnnotationAwareOrderComparator` Ìṩ£º + +``` +public int compare(@Nullable Object o1, @Nullable Object o2) { + return doCompare(o1, o2, null); +} + +/** + * ¾ßÌåµÄ±È½Ï²Ù×÷£¬ÏÈ±È½Ï PriorityOrdered£¬ÔÙ±È½Ï Ordered + */ +private int doCompare(@Nullable Object o1, @Nullable Object o2, + @Nullable OrderSourceProvider sourceProvider) { + // Á½Õß֮һΪ PriorityOrdered£¬Ë­ÊÇPriorityOrdered£¬Ë­µÄÓÅÏȼ¶¸ß + boolean p1 = (o1 instanceof PriorityOrdered); + boolean p2 = (o2 instanceof PriorityOrdered); + if (p1 && !p2) { + return -1; + } + else if (p2 && !p1) { + return 1; + } + // ²éÕÒorderµÄÖµ£¬ÏȲéÕÒOrdered½Ó¿Ú£¬Èç¹ûûÕÒµ½£¬ÔÙ²éÕÒ @Order ×¢½â + int i1 = getOrder(o1, sourceProvider); + int i2 = getOrder(o2, sourceProvider); + // °´Integer¹æÔò½øÐÐ±È½Ï + return Integer.compare(i1, i2); +} + +``` + +´ÓÉÏÃæµÄ´úÂë¿ÉÖª£¬ÏÈ±È½Ï `PriorityOrdered`£¬ÔÙ±È½Ï `Ordered`£¬±È½Ï¹æÔòÈçÏ£º + +1. `PriorityOrdered` ±È½Ï£ºÁ½ÕßÖ®ÖУ¬Ö»ÓÐÆäһʵÏÖÁË `PriorityOrdered` ½Ó¿Ú£¬ÄÇôÀàÐÍΪ `PriorityOrdered` ÓÅÏȼ¶¸ß£¬ ÆäËûÇé¿öÔò°´ `Ordered` µÄ¹æÔò±È½Ï£» +2. `Ordered` ±È½Ï¹æÔò£º + 1. Èç¹ûʵÏÖÁË `Ordered` »ò `PriorityOrdered` ½Ó¿Ú£¬Ôò¸ù¾Ý `getOrder()` ·µ»ØÖµ½øÐбȽϣ¬ÖµÔ½Ð¡ÓÅÏȼ¶Ô½¸ß£» + 2. Èç¹û±ê×¢ÁË `@Order/@Priority` ×¢½â£¬Ôò¸ù¾ÝÆä `value()` ·µ»ØÖµ½øÐбȽϣ¬ÖµÔ½Ð¡ÓÅÏȼ¶Ô½¸ß£» + 3. Èç¹ûûÓÐʵÏÖ `Ordered/PriorityOrdered`£¬Ò²Ã»Óбê×¢ `@Order/@Priority` ×¢½â£¬ÔòΪ×îµÍÓÅÏȼ¶ (`Integer.MAX_VALUE`). + +##### `comparePrecedenceWithinAspect` + +¶ÔÓÚ `@Aspect` ±ê×¢µÄÀ࣬Èç¹ûͬһ `aspect` ÀﶨÒåÁËͬÑùµÄ `advice`£¬spring aop Ò²ÌṩÁËÒ»ÌױȽϹæÔò£º + +``` +/** + * Õë¶Ô @Aspect£¬ ͬһaspectÀﶨÒåÁËͬÑùµÄ advice£¬ÔÙ´Î±È½Ï + */ +private int comparePrecedenceWithinAspect(Advisor advisor1, Advisor advisor2) { + boolean oneOrOtherIsAfterAdvice = (AspectJAopUtils.isAfterAdvice(advisor1) + || AspectJAopUtils.isAfterAdvice(advisor2)); + int adviceDeclarationOrderDelta = getAspectDeclarationOrder(advisor1) + - getAspectDeclarationOrder(advisor2); + // ÆäÖÐÓÐÒ»¸öÊÇafter֪ͨ£¬declarationOrderInAspect´óµÄÓÅÏȼ¶¸ß + if (oneOrOtherIsAfterAdvice) { + if (adviceDeclarationOrderDelta < 0) { + return LOWER_PRECEDENCE; + } + else if (adviceDeclarationOrderDelta == 0) { + return SAME_PRECEDENCE; + } + else { + return HIGHER_PRECEDENCE; + } + } + // Á½Õß¶¼²»ÊÇafter֪ͨ£¬declarationOrderInAspectСµÄÓÅÏȼ¶¸ß + else { + if (adviceDeclarationOrderDelta < 0) { + return HIGHER_PRECEDENCE; + } + else if (adviceDeclarationOrderDelta == 0) { + return SAME_PRECEDENCE; + } + else { + return LOWER_PRECEDENCE; + } + } +} + +``` + +±È½Ï¹æÔòÈçÏ£º±È½ÏÁ½ÕßµÄ `declarationOrderInAspect` Öµ£¬Èç¹ûÁ½Õß֮һΪ `after` ֪ͨ£¬`declarationOrderInAspect` ´óµÄÓÅÏȼ¶¸ß£»Èç¹ûÁ½Õß¶¼²»ÊÇ `after` ֪ͨ£¬`declarationOrderInAspect` СµÄÓÅÏȼ¶¸ß¡£ + +ÕâÀïµÄ `declarationOrderInAspect` ÊÇÊ²Ã´ÄØ£¿ÕâÊÇÉÏһС½ÚÌáµ½µÄ `advisor.size()`£¬´úÂëÈçÏ£º + +> ReflectiveAspectJAdvisorFactory + +``` +public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { + // Ê¡ÂÔÁËһЩ´úÂë + ... + + List advisors = new ArrayList<>(); + //»ñÈ¡Õâ¸öÀàËùÓеÄÔöÇ¿·½·¨ + for (Method method : getAdvisorMethods(aspectClass)) { + // Éú³ÉÔöǿʵÀý£¬advisors.size() ÒÀ´ÎΪ 0£¬1£¬2£¬... ÕâÊÇ + // declarationOrderInAspect µÄÖµ£¬ºóÃæÅÅÐò»áÓõ½ + Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, + advisors.size(), aspectName); + if (advisor != null) { + advisors.add(advisor); + } + } + + // Ê¡ÂÔÁËһЩ´úÂë + ... +} + +``` + +ÌØ±ðÇ¿µ÷µÄÊÇ£¬Õâ¸ö¹æÔòÖ»ÊÊÓÃÓÚͬһ `@Aspect` ÀඨÒåµÄ¡¢Í¬ÑùµÄ֪ͨ·½·¨£¬È磺 + +``` +@Aspect +public class AspectTest { + @Before + public void before1() { + ... + } + + @Before + public void before2() { + ... + } + +} + +``` + +ÕâÀïµÄ `before1()` Óë `before2()` ¶ÔÓ¦µÄ `advisor` ÊÊÓÃÓÚ `comparePrecedenceWithinAspect` ÅÅÐò£¬¶øÒÔÏ´úÂë¾Í²»ÊÊÓÃÁË£¬Ô­ÒòÊÇÔÚ²»Í¬µÄ `@Aspect` ÀàÖж¨ÒåµÄ£º + +``` +@Aspect +public class AspectTest1 { + @Before + public void before() { + ... + } + +} + +@Aspect +public class AspectTest2 { + @Before + public void before() { + ... + } + +} + +``` + +#### 2. `super.sortAdvisors` + +ÎÒÃÇÔٻعýÍ·À´¿´ `super.sortAdvisors(advisors)`: + +> AspectJAwareAdvisorAutoProxyCreator + +``` +protected List sortAdvisors(List advisors) { + ... + else { + return super.sortAdvisors(advisors); + } +} + +``` + +ÎÒÃǸú½øÈ¥£º + +> AbstractAdvisorAutoProxyCreator + +``` + protected List sortAdvisors(List advisors) { + AnnotationAwareOrderComparator.sort(advisors); + return advisors; + } + +``` + +·¢ÏÖ×îºóʹÓõÄÊÇ `AnnotationAwareOrderComparator.sort(advisors)`£¬Êµ¼ÊÉÏ£¬Õâ¸ö¾ÍÊÇÉÏÃæ·ÖÎöµÄ `this.advisorComparator.compare` µÄ±È½Ï¹æÔò£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£ + +### 3. `getOrder()` ÖµµÄÓÉÀ´ + +#### `BeanFactoryTransactionAttributeSourceAdvisor#getOrder()` + +`BeanFactoryTransactionAttributeSourceAdvisor` ûÓÐ `@Order/@Priority`£¬µ«ËüʵÏÖÁË `Ordered` ½Ó¿Ú£¬Òò´ËËüµÄÖ´ÐÐ˳ÐòÓÉ `getOrder()` ·½·¨µÄ·µ»ØÖµ¾ö¶¨£¬¶ÔÓ¦µÄ `getOrder()` ·½·¨ÈçÏ£º + +``` + /** + * »ñÈ¡ order£¬·½·¨ÈçÏ£º + * 1\. Èç¹ûÒÑÖ¸¶¨ÁË order£¬Ö±½Ó·µ»Ø£» + * 2\. »ñÈ¡ advisor µÄ advice£¬Èç¹û advice ʵÏÖÁË Ordered ½Ó¿Ú£¬µ÷Óà getOrder()£» + * 3\. Èç¹ûÒÔÉ϶¼²»Âú×㣬Ôò·µ»Ø Ordered.LOWEST_PRECEDENCE (×îµÍÓÅÏȼ¶)¡£ + * @return + */ + @Override + public int getOrder() { + if (this.order != null) { + return this.order; + } + Advice advice = getAdvice(); + if (advice instanceof Ordered) { + return ((Ordered) advice).getOrder(); + } + return Ordered.LOWEST_PRECEDENCE; + } + +``` + +`Ordered.LOWEST_PRECEDENCE` Ϊ `Integer.MAX_VALUE`£¬¼´ `2147483647`£¬ÎÒÃÇÔÙ¿´¿´ `BeanFactoryTransactionAttributeSourceAdvisor` µÄ `getOrder()` ·½·¨·µ»ØµÄÖµ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d05044b7dcd41855ab6e59727c4be74bdb9.png) + +¿É¼û£¬`BeanFactoryTransactionAttributeSourceAdvisor` µÄÖ´ÐÐ˳ÐòÊÇĬÈ쵀 `Integer.MAX_VALUE`¡£Èç¹ûµ÷¶ÈµÄ»°£¬·¢ÏÖÕâ¸öÖµÊÇÔÚ `return this.order` ·µ»ØµÄ£º + +``` +public int getOrder() { + // ͨ¹ýµ÷¶È·¢ÏÖ£¬this.order ²¢²»Îªnull + if (this.order != null) { + return this.order; + } + // Ê¡ÂÔһЩ´úÂë + ... +} + +``` + +ÄÇôÕâÖµÊÇ´ÓÄÄÀïÀ´µÄÄØ£¿¾­¹ýÖØÖØ·ÖÎö£¬·¢ÏÖÊÇÔÚ´´½¨ `BeanFactoryTransactionAttributeSourceAdvisor` ¶ÔÏóʱ£¬µ÷Óà `BeanFactoryTransactionAttributeSourceAdvisor#setOrder` ÉèÖõģº + +> ProxyTransactionManagementConfiguration + +``` +@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) +public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { + BeanFactoryTransactionAttributeSourceAdvisor advisor + = new BeanFactoryTransactionAttributeSourceAdvisor(); + advisor.setTransactionAttributeSource(transactionAttributeSource()); + advisor.setAdvice(transactionInterceptor()); + if (this.enableTx != null) { + // ÕâÀï»ñÈ¡µÄÊÇ @EnableTransactionManagement ×¢½âµÄ order() Öµ + advisor.setOrder(this.enableTx.getNumber("order")); + } + return advisor; +} + +``` + +µ½ÕâÀïÎÒÃǾÍÃ÷°×ÁË£¬ÊÂÎñ advisor µÄÖ´ÐÐ˳Ðò¿ÉÒÔÔÚ `@EnableTransactionManagement` ÖÐÖ¸¶¨£º + +``` +public @interface EnableTransactionManagement { + + boolean proxyTargetClass() default false; + + AdviceMode mode() default AdviceMode.PROXY; + + /** + * ÕâÀïÖ¸¶¨advisorÖ´ÐÐ˳Ðò£¬Ä¬ÈÏÊÇ×îµÍÓÅÏȼ¶ + */ + int order() default Ordered.LOWEST_PRECEDENCE; + +} + +``` + +½áÂÛ£º`@EnableTransactionManagement` ×¢½âµÄ `order()` ·½·¨¿ÉÒÔÖ¸¶¨ `advisor` µÄÖ´ÐÐ˳Ðò¡£ + +#### `InstantiationModelAwarePointcutAdvisorImpl#getOrder()` + +´ÓÇ°ÃæµÄ·ÖÎö¿ÉÖª£¬`@Aspect` ÀàÖеÄÿһ¸ö·½·¨×îÖÕ¶¼»áת»¯Îª `advisor`£¬ÀàÐÍΪ `InstantiationModelAwarePointcutAdvisorImpl`£¬ËüҲʵÏÖÁË `Ordered` ½Ó¿Ú£¬Òò´ËÖ´ÐÐ˳ÐòÒ²ÊÇÓÉ `InstantiationModelAwarePointcutAdvisorImpl#getOrder()` ·½·¨¾ö¶¨£¬ËüµÄ `getOrder()` ·½·¨ÈçÏ£º + +> InstantiationModelAwarePointcutAdvisorImpl + +``` +@Override +public int getOrder() { + return this.aspectInstanceFactory.getOrder(); +} + +``` + +ÔÚ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨ÉÏ£©](https://my.oschina.net/funcy/blog/4678817)ÎÒÃÇÒѾ­Ïêϸ·ÖÎöÁË `method` µ½ `advisor` µÄת±ä¹ý³Ì£¬ÄÜ´Ó´úÂëÉÏÇáËÉÕÒµ½ `aspectInstanceFactory` µÄÀàÐÍ£¬ÕâÀïÎÒÃǾͲ»ÔÙÒ»²½²½·ÖÎöÔ´ÂëÁË£¬Ö±½Óͨ¹ýµ÷ÊԵķ½·¨»ñÈ¡ `aspectInstanceFactory` µÄÀàÐÍ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e2784fc573474f56e8555ef60d57a07bcbf.png) + +´Óµ÷ÊԵĽá¹ûÀ´¿´£¬`aspectInstanceFactory` ÀàÐÍΪ `LazySingletonAspectInstanceFactoryDecorator`£¬ÎÒÃǸú½øÆä `getOrder()` ·½·¨£º + +> LazySingletonAspectInstanceFactoryDecorator + +``` +@Override +public int getOrder() { + return this.maaif.getOrder(); +} + +``` + +ÎÒÃÇÒÀȻʹÓõ÷ÊԵķ½Ê½»ñÈ¡ `maaif` µÄÀàÐÍ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-2a4b87b6c8c824c55c4a3d86d736796def0.png) + +`maaif` ÀàÐÍΪ `BeanFactoryAspectInstanceFactory`£¬ÎÒÃǼÌÐø¸ú½ø£º + +> BeanFactoryAspectInstanceFactory + +``` +public int getOrder() { + // this.name Ö¸µÄÊDZê×¢ÁË @Aspect ×¢½âµÄÀà + Class type = this.beanFactory.getType(this.name); + if (type != null) { + // Èç¹ûʵÏÖÁË Ordered ½Ó¿Ú£¬¾Íµ÷Óà getOrder() ·½·¨»ñÈ¡ + // PriorityOrdered ÊÇ Ordered µÄ×Ó½Ó¿Ú£¬Ò²ÓÐ getOrder() ·½·¨£¬Òò´ËÕâÀïÒ²»á»ñÈ¡µ½ + if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) { + return ((Ordered) this.beanFactory.getBean(this.name)).getOrder(); + } + // 1\. ²éÕÒÀàÊÇÊÇ·ñÓÐ @Order ×¢½â£¬Èç¹ûÓУ¬Ôò·µ»Ø @Order ×¢½âÖ¸¶¨µÄÖµ£» + // 2\. ·ñÔò²éѯÀàÊÇ·ñÓÐ @Priority ×¢½â£¬Èç¹ûÓУ¬Ôò·µ»Ø @Priority ×¢½âÖ¸¶¨µÄÖµ£» + // 3\. Èç¹ûÒÔÉ϶¼²»Âú×㣬·µ»Ø Ordered.LOWEST_PRECEDENCE£¬ÖµÎª Integer.MAX_VALUE + return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE); + } + return Ordered.LOWEST_PRECEDENCE; +} + +``` + +´Ó´úÂëÉÏÀ´¿´£¬`getOrder()` Âß¼­ÈçÏ£º + +1. ͨ¹ýÃû³Æ»ñÈ¡ÇÐÃæÀ࣬Ҳ¾ÍÊDZê×¢ÁË `@Aspect` µÄÀࣻ +2. Èç¹ûÇÐÃæÀàʵÏÖÁË `Ordered` ½Ó¿Ú£¬¾Íµ÷Óà `getOrder()` ·½·¨»ñÈ¡£¬·µ»Ø£¨ÖµµÃÒ»ÌáµÄÊÇ£¬`PriorityOrdered` ÊÇ `Ordered` µÄ×Ó½Ó¿Ú£¬Ò²ÓÐ `getOrder()` ·½·¨£¬ÕâÀïÒ²»á»ñÈ¡µ½£©£» +3. Èç¹ûÉÏÃæÃ»ÓлñÈ¡µ½£¬Ôò²éÕÒÇÐÃæÀàÊÇÊÇ·ñÓÐ `@Order` ×¢½â£¬Èç¹ûÓУ¬Ôò·µ»Ø `@Order` ×¢½âÖ¸¶¨µÄÖµ£»Èç¹ûûÓУ¬²éÕÒÇÐÃæÀàÊÇ·ñÓÐ `@Priority` ×¢½â£¬Èç¹ûÓУ¬Ôò·µ»Ø `@Priority` ×¢½âÖ¸¶¨µÄÖµ£» +4. Èç¹ûÒÔÉÏûÓлñÈ¡µ½Öµ£¬¾Í·µ»ØÄ¬ÈÏÖµ£º`Ordered.LOWEST_PRECEDENCE`¡£ + +µ½ÕâÀï¾ÍÃ÷°×ÁË£¬`@Aspect` Àà¿ÉÒÔͨ¹ýʵÏÖ `Ordered/PriorityOrdered` ½Ó¿ÚÀ´Ö¸¶¨Ö´ÐÐÓÅÏȼ¶£¬Ò²¿ÉÒÔͨ¹ý `@Order/@Priority` ×¢½âÀ´Ö¸¶¨Ö´ÐÐÓÅÏȼ¶¡£ + +**ÐèÒªÌØ±ðÖ¸³öµÄÊÇ**£¬´Ó `getOrder()` ´úÂëÀ´¿´£¬Õⲿ·Ö ´úÂëÖ» ÊÇ°Ñ `PriorityOrdered/@Priority` µ±×÷ `Order` À´´¦Àí£¬ÓÅÏȼ¶²¢²»±È `Ordered/@Order` ¸ß¡£Ò²¾ÍÊÇ˵£¬Èç¹û `AspectA` ±ê×¢Á˵ÄÁË `@Priority`£¬`AspectB` ±ê×¢Á˵ÄÁË `@Order`£¬`AspectA` µÄÓÅÏȼ¶²¢²»Ò»¶¨±È `AspectB` ¸ß£¬ÕæÕý¾ö¶¨ÓÅÏȼ¶µÄÊÇ×¢½â ÀïµÄ `value()` Öµ¡£ + +### 4\. ÈçºÎ×Ô¶¨ÒåÓÅÏȼ¶ + +ÎÒÃÇÔÚ×Ô¼ºÐ´´úÂëʱ£¬ÈçºÎÖ¸¶¨ÓÅÏȼ¶ÄØ£¿ + +1. Èç¹ûÊÇ×ÔÖ÷ʵÏÖ `advisor`£¬¿ÉʵÏÖ `Ordered` ½Ó¿Ú£¬Ò²¿ÉÒÔÔÚ `advisor` ÀàÉϱê×¢ `@Order` ×¢½â£º + + ``` + public class MyAdvisor extends AbstractBeanFactoryPointcutAdvisor implements Ordered { + + @Override + public int getOrder() { + return xxx; + } + + } + + @Order(xxx) + public class MyAdvisor extends AbstractBeanFactoryPointcutAdvisor { + + @Override + public int getOrder() { + return xxx; + } + + } + + ``` + +2. Èç¹ûÊǵ¥¸öÇÐÃæÀà (`@Aspect` ±ê×¢µÄÀà)£¬ÇÒÎÞÖØ¸´µÄ `@Around/@Before/@After` µÈ + + ``` + @Aspect + public class MyAspectj { + + @Around("xxx") + public Object around(ProceedingJoinPoint p){ + ... + } + + @Before("xxx") + public void before(JoinPoint p) { + ... + } + + @After("xxx") + public void after(JoinPoint p) { + ... + } + + @AfterReturning("xxx") + public void afterReturning(JoinPoint p) { + ... + } + + @AfterThrowing("xxx") + public void afterThrowing(JoinPoint p) { + ... + } + } + + ``` + + ¶ÔÓÚͬһÇÐÃæµÄ²»Í¬Í¨Öª£¬spring ÒѾ­°ïÎÒÃÇÉèÖúÃÁËÖ´ÐÐ˳Ðò£¬ÎÒÃÇÎÞ´Ó¸ü¸Ä£¬Ö´ÐÐ˳ÐòÒÀ´ÎΪ `Around, Before, After, AfterReturning, AfterThrowing`. + +3. µ¥¸öÇÐÃæÀà (`@Aspect` ±ê×¢µÄÀà) ÄÚÓÐÖØ¸´µÄ `@Around/@Before/@After` µÈ£¬Çé¿öÈçÏ£º + + ``` + @Aspect + public class MyAspectj { + + @Around("xxx") + public Object around(ProceedingJoinPoint p){ + ... + } + + @Before("xxx") + public void before(JoinPoint p) { + ... + } + + @Around("xxx") + public Object around(ProceedingJoinPoint p){ + ... + } + + @Before("xxx") + public void before(JoinPoint p) { + ... + } + + } + + ``` + + ÕâÖÖÇé¿öÎÒÃÇÔÚ `AspectJPrecedenceComparator#comparePrecedenceWithinAspect` ·½·¨Ê±ÓзÖÎö¹ý£¬µÃµ½µÄ½áÂÛÊÇ£º±È½ÏÁ½ÕßµÄ `declarationOrderInAspect` Öµ£¬Èç¹ûÁ½Õß֮һΪ `after` ֪ͨ£¬`declarationOrderInAspect` ´óµÄÓÅÏȼ¶¸ß£»Èç¹ûÁ½Õß¶¼²»ÊÇ `after` ֪ͨ£¬`declarationOrderInAspect` СµÄÓÅÏȼ¶¸ß¡£Õâ¸ö `declarationOrderInAspect` ÍêÈ«ÒÀÀµÓÚ jdk µÄ·´Éä»úÖÆ£¬ÏÈ»ñÈ¡µ½µÄÊÇÄĸö·½·¨£¬Äĸö·½·¨µÄ `declarationOrderInAspect` ¾ÍС£¬²»Í¬ jdK °æ±¾Ö®¼ä£¬ÄÑÒÔ±£Ö¤»ñµÃµÄ˳ÐòÒ»Ö¡£ + +4. ¶à¸öÇÐÃæÀà (`@Aspect` ±ê×¢µÄÀà) µÄÖ´ÐÐ˳Ðò¿ÉÒÔͨ¹ý `@Order` ×¢½â£¬»òʵÏÖ `Ordered` ½Ó¿ÚÀ´Ö¸¶¨£º + + ``` + @Order(xxx) + public class MyAspectj1 { + ... + } + + @Order(xxx) + public class MyAspectj2 { + ... + } + + ``` + +ÁíÍ⣬`getOrder()` ·µ»ØµÄÖµ»ò `@Order(xxx)` Ö¸¶¨µÄֵԽС£¬±íÃ÷ÓÅÏȼ¶Ô½¸ß¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4784828](https://my.oschina.net/funcy/blog/4784828) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" new file mode 100644 index 0000000..5d4fa2c --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213Spring\344\272\213\344\273\266\346\234\272\345\210\266.md" @@ -0,0 +1,918 @@ +### 1\. demo ×¼±¸ + +ÔÚÕýʽ·ÖÎöʼþ»úÖÆÇ°£¬ÎÒÃÇÏÈÀ´×¼±¸Ò»¸ö demo£º + +1. ×¼±¸Ò»¸öʼþÀà `MyApplicationEvent`£º + +``` +public class MyApplicationEvent extends ApplicationEvent { + + private static final long serialVersionUID = -1L; + + public MyApplicationEvent(Object source) { + super(source); + } +} + +``` + +1. ×¼±¸Ò»¸ö¼àÌýÆ÷ `MyApplicationEventListener`£¬¶Ôʼþ `MyApplicationEvent` ½øÐмàÌý£º + +``` +@Component +public class MyApplicationEventListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(MyApplicationEvent event) { + System.out.println(Thread.currentThread().getName() + " | " + event.getSource()); + } +} + +``` + +×¼±¸Ò»¸öÅäÖÃÀ࣬ÏȲ»Ö¸¶¨ÄÚÈÝ£º + +``` +@Configuration +@ComponentScan +public class Demo08Config { + +} + +``` + +×îºóÊÇÖ÷Àࣺ + +``` +@ComponentScan +public class Demo08Main { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context + = new AnnotationConfigApplicationContext(Demo08Config.class); + // ·¢²¼Ê¼þ + context.publishEvent( + new MyApplicationEvent(Thread.currentThread().getName() + " | ×Ô¶¨Òåʼþ ...")); + } + +} + +``` + +ÒÔÉÏ´úÂ붨ÒåÁËÒ»¸öʼþ `MyApplicationEvent`£¬È»ºó¶¨ÒåÁËÒ»¸ö¼àÌýÆ÷ `MyApplicationEventListener`£¬ÓÃÀ´¼àÌý `MyApplicationEvent` ʼþ£¬È»ºóÔÚ `main()` ·½·¨ÖУ¬µ÷Óà `context.publishEvent(...)` ·½·¨À´·¢²¼¸Ãʼþ¡£ + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +main | main | ×Ô¶¨Òåʼþ ... + +``` + +¿ÉÒÔ¿´µ½£¬Ê¼þ³É¹¦¼àÌýµ½ÁË¡£ + +´ÓÔËÐпÉÖª£¬Ê¼þµÄ·¢²¼Ïß³ÌΪ `main`£¬Ê¼þµÄ¼àÌý´¦ÀíÏß³ÌÒ²ÊÇ `main`¡£ + +### 2\. ʼþ×é¼þ½éÉÜ + +Õýʽ¿ª½²Ç°£¬ÎÒÃÇÏÈÀ´½éÉÜÏÂʼþÏà¹ØµÄ×é¼þ¡£Ê¼þÏà¹ØµÄ×é¼þÓÐ 4 ¸ö£º + +1. ʼþ£º·¢²¼µÄÄÚÈÝ +2. ·¢²¼Æ÷£ºÓÃÀ´·¢²¼Ê¼þµÄ×é¼þ +3. ¹ã²¥Æ÷£º½ÓÊÕ·¢²¼Æ÷·¢²¼µÄʼþ£¬²¢½«½ÓÊÕµ½µÄʼþ¹ã²¥¸ø¼àÌýÆ÷ +4. ¼àÌýÆ÷£º¼àÌýʼþ + +ÏÂÃæÎÒÃÇÒ»Ò»À´·ÖÎö¡£ + +#### 2.1 ʼþ + +spring ÌṩµÄʼþΪ `ApplicationEvent`£¬ÕâÊÇÒ»¸ö³éÏóÀ࣬¼Ì³ÐÁË jdk ÌṩµÄ `EventObject` À࣬·¢²¼×Ô¶¨Òåʼþʱ£¬¿É¼Ì³Ð `ApplicationEvent`£º + +``` +public abstract class ApplicationEvent extends EventObject { + + private static final long serialVersionUID = 7099057708183571937L; + + /** Ôö¼Ó timestamp ÊôÐÔ*/ + private final long timestamp; + + public ApplicationEvent(Object source) { + super(source); + this.timestamp = System.currentTimeMillis(); + } + + public final long getTimestamp() { + return this.timestamp; + } +} + +``` + +`ApplicationEvent` ÊÇ `EventObject`£¬ÎÒÃǼÌÐø£º + +``` +/** + * EventObject ÓÉjdkÌṩ£¬Î»ÓÚ java.util °ü¡£ + */ +public class EventObject implements java.io.Serializable { + + private static final long serialVersionUID = 5516075349620653480L; + + // ʼþÄÚÈÝ + protected transient Object source; + + public EventObject(Object source) { + if (source == null) + throw new IllegalArgumentException("null source"); + + this.source = source; + } + + /** + * »ñȡʼþÄÚÈÝ + */ + public Object getSource() { + return source; + } + + public String toString() { + return getClass().getName() + "[source=" + source + "]"; + } +} + +``` + +½áºÏ `ApplicationEvent` Óë `EventObject` À´¿´£¬`ApplicationEvent` ÌṩÁËÁ½¸öÊôÐÔ£º + +* `source`£ºÀ´×Ô `EventObject` µÄÊôÐÔ£¬¶¨ÒåÁËʼþµÄÄÚÈÝ£» +* `timestamp`: ʱ¼ä´Á£¬ÓÃÀ´¼Ç¼ÊÇʼþµÄÉú³Éʱ¼ä¡£ + +ʵ¼ÊÉÏ£¬spring ³ýÁËÄÜ·¢²¼ `ApplicationEvent` ÀàÐ͵ÄʼþÍ⣬»¹¿ÉÒÔ·¢²¼ `Object` ÀàÐ͵Äʼþ£¬½ÓÏÂÃæ²é¿´·¢²¼Æ÷¾Í¿ÉÒÔ·¢ ÏÖÕâÒ»µã¡£ + +#### 2.2 ·¢²¼Æ÷ + +spring ÌṩµÄ·¢²¼Æ÷Ϊ `ApplicationEventPublisher`£¬´úÂëÈçÏ£º + +``` +public interface ApplicationEventPublisher { + + /** + * ·¢²¼ApplicationEventÀàÐ͵Äʼþ + */ + default void publishEvent(ApplicationEvent event) { + publishEvent((Object) event); + } + + /** + * ·¢²¼ObjectÀàÐ͵Äʼþ + */ + void publishEvent(Object event); + +} + +``` + +ÕâÊǸö½Ó¿Ú£¬ÆäÄÚ¶¨ÒåÁËÁ½¸ö·½·¨£º + +* `void publishEvent(ApplicationEvent event)`: ÓÃÀ´·¢²¼ `ApplicationEvent` ÀàÐ͵Äʼþ +* `void publishEvent(Object event)`: ÓÃÀ´·¢²¼ `Object` ÀàÐ͵Äʼþ + +`AbstractApplicationContext` ÊÇ `ApplicationEventPublisher` µÄ×ÓÀ࣬Òò´ËÎÒÃÇ¿ÉÒÔÖ±½Óµ÷Óà `AbstractApplicationContext#publishEvent` À´·¢²¼Ê¼þ¡£ + +¹ØÓÚ `AbstractApplicationContext` ¶ÔÕâÁ½¸ö·½·¨µÄʵÏÖ£¬ÎÒÃǺóÃæ·ÖÎö·¢²¼Á÷³ÌʱÔÙ¾ßÌå·ÖÎö¡£ + +#### 2.3 ¹ã²¥Æ÷ + +¹ã²¥Æ÷µÄ×÷ÓÃÊǽÓÊÕ·¢²¼µÄʼþ£¬È»ºó½«Ê¼þ¹ã²¥¸ø¼àÌýÆ÷£¬´úÂëÈçÏ£º + +``` +public interface ApplicationEventMulticaster { + + /** + * Ìí¼Ó¼àÌýÆ÷ + */ + void addApplicationListener(ApplicationListener listener); + + /** + * Ìí¼Ó¼àÌýÆ÷µÄ beanName + */ + void addApplicationListenerBean(String listenerBeanName); + + /** + * ÒÆ³ý¼àÌýÆ÷ + */ + void removeApplicationListener(ApplicationListener listener); + + /** + * ÒÆ³ý¼àÌýÆ÷µÄ beanName + */ + void removeApplicationListenerBean(String listenerBeanName); + + /** + * ÒÆ³ýËùÓеļàÌýÆ÷ + */ + void removeAllListeners(); + + /** + * ¹ã²¥Ê¼þ + */ + void multicastEvent(ApplicationEvent event); + + /** + * ¹ã²¥Ê¼þ + */ + void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); + +} + +``` + +´Ó´úÂëÉÏÀ´¿´£¬¹ã²¥Æ÷Ö÷Òª¹¦ÄÜÓÐÁ½¸ö£º + +1. ά»¤¼àÌýÆ÷£¬¿ÉÒÔ¶Ô¼àÌýÆ÷½øÐÐÔöɾ²Ù×÷ +2. ¹ã²¥Ê¼þ + +spring ĬÈϵĹ㲥Æ÷Ϊ `SimpleApplicationEventMulticaster`£¬Õâ¸öÎÒÃǺóÃæ»áÔÙ·ÖÎö¡£ + +#### 2.4 ¼àÌýÆ÷ + +¼àÌýÆ÷ÓÃÀ´¼àÌý·¢²¼µÄʼþ£¬È»ºó×öһЩ´¦Àí²Ù×÷£¬´úÂëÈçÏ£º + +``` +@FunctionalInterface +public interface ApplicationListener extends EventListener { + + /** + * ´¦Àíʼþ + */ + void onApplicationEvent(E event); + +} + +``` + +ÔÚ´¦Àíʼþ¼àÌýʱ£¬ÎÒÃÇÐèҪʵÏÖ `ApplicationListener`£¬È»ºóÔÚ `onApplicationEvent(...)` ·½·¨ÖбàдÎÒÃǵÄʼþ¼àÌýÂß¼­¡£ + +### 3\. »Ø¹Ë£º`¹ã²¥Æ÷µÄ³õʼ»¯`Óë`¼àÌýÆ÷µÄ×¢²á` + +±¾½ÚÎÒÃÇÀ´»Ø¹ËÏÂ`ʼþ¹ã²¥Æ÷µÄ³õʼ»¯`Óë`¼àÌýÆ÷µÄ×¢²á`µÄÁ÷³Ì¡£ + +ÔÚ´¦Àí spring ÈÝÆ÷Æô¶¯µÄ `AbstractApplicationContext#refresh` ·½·¨ÖУ¬`ʼþ¹ã²¥Æ÷µÄ³õʼ»¯`Óë`¼àÌýÆ÷µÄ×¢²á`·Ö±ð·¢ÉúÔÚµÚ 8 ²½ÓëµÚ 10 ²½£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c4d4ac83c23e41a706b7ba545fd8d0f7681.png) + +Ïà¹Ø´úÂëÈçÏ£º + +``` +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + + /** + * ³õʼ»¯¹ã²¥Æ÷ + * Èç¹ûÒÑÅäÖÃÁËʼþ¹ã²¥Æ÷£¬¾ÍʹÓÃÒÑÅäÖõ쬷ñÔò¾ÍʹÓÃĬÈϵÄʼþ¹ã²¥Æ÷ + */ + protected void initApplicationEventMulticaster() { + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + // Èç¹ûÓû§ÅäÖÃÁË×Ô¶¨Òåʼþ¹ã²¥Æ÷£¬¾ÍʹÓÃÓû§µÄ + if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { + this.applicationEventMulticaster = + beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, + ApplicationEventMulticaster.class); + } + else { + // Óû§Ã»ÓÐÅäÖù㲥Æ÷£¬¾ÍʹÓÃĬÈϵÄʼþ¹ã²¥Æ÷ + this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); + beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, + this.applicationEventMulticaster); + } + } + + /** + * ×¢²á¼àÌýÆ÷ + */ + protected void registerListeners() { + // 1\. ÏȽ«ÊÖ¶¯Ìí¼ÓµÄ¼àÌýÆ÷·Åµ½¹ã²¥Æ÷ÖÐ + // µ÷ÓÃAbstractApplicationContext#addApplicationListener½øÐÐÌí¼Ó + for (ApplicationListener listener : getApplicationListeners()) { + getApplicationEventMulticaster().addApplicationListener(listener); + } + + // 2\. ´ÓbeanFactoryÖлñȡȡ¼àÌýÆ÷µÄÃû³Æ£¬Ìí¼Óµ½¹ã²¥Æ÷ÖÐ + String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); + for (String listenerBeanName : listenerBeanNames) { + getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); + } + + // 3\. Èç¹û´æÔÚÔçÆÚÓ¦ÓÃʼþ£¬·¢²¼ + Set earlyEventsToProcess = this.earlyApplicationEvents; + // ½« earlyApplicationEvents ÉèÖÃΪ null£¬ÔÙ·¢²¼ÔçÆÚʼþ + this.earlyApplicationEvents = null; + if (earlyEventsToProcess != null) { + for (ApplicationEvent earlyEvent : earlyEventsToProcess) { + getApplicationEventMulticaster().multicastEvent(earlyEvent); + } + } + } + + ... + +} + +``` + +¹ã²¥Æ÷µÄ³õʼ»¯Âß¼­ºÜ¼òµ¥£¬¼´Èç¹ûÒÑÅäÖÃÁËʼþ¹ã²¥Æ÷£¬¾ÍʹÓÃÒÑÅäÖõ쬷ñÔò¾ÍʹÓÃĬÈϵÄʼþ¹ã²¥Æ÷£¬Ä¬ÈϵÄʼþ¹ã²¥Æ÷ÊÇ `SimpleApplicationEventMulticaster`¡£ + +×¢²á¼àÌýÆ÷µÄÁ÷³ÌÈçÏ£º + +1. ÏȽ«ÊÖ¶¯Ìí¼ÓµÄ¼àÌýÆ÷·Åµ½¹ã²¥Æ÷ÖУ¬ÎÒÃÇ¿ÉÒÔµ÷Óà `AbstractApplicationContext#addApplicationListener` Ìí¼Ó¼àÌýÆ÷£» +2. ´Ó `beanFactory` Öлñȡȡ¼àÌýÆ÷µÄÃû³Æ£¬Ìí¼Óµ½¹ã²¥Æ÷ÖУ¬×¢Ò⣺´Ëʱ `beanFactory` ÖÐµÄ bean ²¢Ã»Óгõʼ»¯£¬Òò´ËÖ»ÄÜÌí¼Ó `beanName`; +3. Èç¹û´æÔÚÔçÆÚʼþ£¬¾Í·¢²¼ÔçÆÚʼþ¡£ + +»Ø¹ËÍêÕâÁ½¸öÁ÷³Ìºó£¬ÎÒÃÇ´óÄÔÖÐÐèÒªÇå³þµØÃ÷°×ÒÔÏÂÁ½µã£º + +1. ¹ã²¥Æ÷ÊÇÔÚÄÄÀï³õʼ»¯µÄ +2. ¼àÌýÆ÷ÊÇÔÚÄÄÀï×¢²áµ½¹ã²¥Æ÷ÖÐµÄ + +### 4\. ʼþ·¢²¼Á÷³Ì + +¾­¹ýÇ°ÃæÁ½½ÚµÄÆÌµæ£¬ÎÒÃǶÔʼþµÄ×é¼þÒѾ­ÓÐÁ˸ö´ó¸ÅµÄ¸ÅÄҲÃ÷°×ÁËʼþ¹ã²¥Æ÷µÄ³õʼ»¯Óë¼àÌýÆ÷µÄ×¢²áÁ÷³Ì£¬½ÓÏÂÀ´ ÎÒÃǾÍÕýʽ·ÖÎöʼþµÄ·¢²¼Á÷³ÌÁË¡£ + +ÔÚ demo ÖУ¬ÎÒÃǵ÷Óà `context.publishEvent(...)` À´·¢²¼Ê¼þ£¬ÎÒÃǸú½øÕâ¸ö·½·¨£º + +``` +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + + @Override + public void publishEvent(ApplicationEvent event) { + publishEvent(event, null); + } + + protected void publishEvent(Object event, @Nullable ResolvableType eventType) { + Assert.notNull(event, "Event must not be null"); + // ´¦ÀíʼþÀàÐÍ + ApplicationEvent applicationEvent; + if (event instanceof ApplicationEvent) { + applicationEvent = (ApplicationEvent) event; + } + else { + applicationEvent = new PayloadApplicationEvent<>(this, event); + if (eventType == null) { + eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType(); + } + } + + // earlyApplicationEvents ²»Îª null£¬½«applicationEventÌí¼Óµ½ earlyApplicationEvents + // ÔÚ×¢²á¼àÌýÆ÷ºó£¬»á°Ñ earlyApplicationEvents ÉèÖÃΪ null + if (this.earlyApplicationEvents != null) { + this.earlyApplicationEvents.add(applicationEvent); + } + else { + // ÕâÀïÊÇ·¢²¼Ê¼þµÄ²Ù×÷ + getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); + } + + // ´æÔÚ¸¸ÈÝÆ÷£¬Ò»Ñù·¢²¼ + if (this.parent != null) { + if (this.parent instanceof AbstractApplicationContext) { + ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); + } + else { + this.parent.publishEvent(event); + } + } + } + +} + +``` + +Õâ¸ö·½·¨ºÜ¼òµ¥£¬¹Ø¼ü´úÂëΪ + +``` +// ÕâÀïÊÇ·¢²¼Ê¼þµÄ²Ù×÷ +getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); + +``` + +ÕâÐдúÂëÏÈ»ñÈ¡ÁËʼþ¹ã²¥Æ÷£¬È»ºó¹ã²¥Ê¼þ¡£ + +Ç°ÃæÌáµ½£¬spring ÌṩµÄĬÈϵÄʼþ¹ã²¥Æ÷ÊÇ `SimpleApplicationEventMulticaster`£¬ÎÒÃǽøÈë `SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)` À´¿´¿´Ê¼þµÄ¹ã²¥Á÷³Ì£º + +``` +/** + * ¹ã²¥Ê¼þ + */ +public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { + ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); + // 1\. »ñÈ¡ TaskExecutor + Executor executor = getTaskExecutor(); + // 2\. getApplicationListeners(...) »ñÈ¡ÄܼàÌý¸ÃʼþµÄ¼àÌýÆ÷ + for (ApplicationListener listener : getApplicationListeners(event, type)) { + // 3\. ±éÀú¼àÌýÆ÷£¬ÖðÒ»µ÷Óà invokeListener(...) ·½·¨ + if (executor != null) { + executor.execute(() -> invokeListener(listener, event)); + } + else { + invokeListener(listener, event); + } + } +} + +``` + +ÒÔÉÏ·½·¨°üº¬ 3 ¸ö²Ù×÷£º + +1. »ñȡִÐÐÆ÷£º`getTaskExecutor()` +2. »ñȡʼþµÄ¼àÌýÆ÷£º`getApplicationListeners(...)` +3. µ÷ÓüàÌýÆ÷µÄ¼àÌý·½·¨£º`invokeListener(...)` + +ÒÔÉϲÙ×÷°üº¬ÁËʼþ¹ã²¥µÄÕû¸öÁ÷³Ì£¬ÎÒÃÇÏÂÃæÀ´ºÃºÃ·ÖÎöÏ¡£ + +#### 1\. »ñȡִÐÐÆ÷£º`getTaskExecutor()` + +`taskExecutor` ÊÇ `SimpleApplicationEventMulticaster` µÄÒ»¸öÊôÐÔ£¬`getTaskExecutor()` ÊÇ `taskExecutor` µÄ `getter` ·½·¨£º + +``` + private Executor taskExecutor; + + public void setTaskExecutor(@Nullable Executor taskExecutor) { + this.taskExecutor = taskExecutor; + } + + @Nullable + protected Executor getTaskExecutor() { + return this.taskExecutor; + } + +``` + +spring ΪÎÒÃÇÌṩÁËÁ½ÖÖÀàÐ굀 `taskExecutor`£º + +1. `SyncTaskExecutor`£ºÍ¬²½µÄ `taskExecutor`£¬Æä `execute(...)` ·½·¨Îª£º + + ``` + @Override + public void execute(Runnable task) { + Assert.notNull(task, "Runnable must not be null"); + task.run(); + } + + ``` + + ¿ÉÒÔ¿´µ½£¬ÕâȷʵÊǸöͬ²½·½·¨£¬Ö±½Óµ÷Óà `Runnable#run` ·½·¨£¬²¢Ã»ÓÐÆô¶¯ÐµÄÏß³Ì + +2. `SimpleAsyncTaskExecutor`£ºÒì²½µÄ `taskExecutor`£¬Æä `execute(...)` ·½·¨Îª£º + + ``` + @Override + public void execute(Runnable task, long startTimeout) { + Assert.notNull(task, "Runnable must not be null"); + Runnable taskToUse = (this.taskDecorator != null + ? this.taskDecorator.decorate(task) : task); + // doExecute(...) ²ÅÊÇÕæÕý¸É»îµÄ·½·¨ + if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) { + this.concurrencyThrottle.beforeAccess(); + doExecute(new ConcurrencyThrottlingRunnable(taskToUse)); + } + else { + doExecute(taskToUse); + } + } + + /** + * ÕæÕý¸É»îµÄ·½·¨ + * ´Ó´úÂëÀ´¿´£¬Õâ¸ö·½·¨»á´´½¨Ð´´½¨À´Ö´ÐÐÈÎÎñ£¬µ«²¢Ã»ÓÐʹÓÃÏß³Ì³Ø + */ + protected void doExecute(Runnable task) { + // ¿ÉÒÔ¿´µ½£¬ÕâÀï´´½¨ÁËỊ̈߳¬²¢ÇÒÆô¶¯ÁËÏß³Ì + Thread thread = (this.threadFactory != null + ? this.threadFactory.newThread(task) : createThread(task)); + thread.start(); + } + + ``` + + ¿ÉÒÔ¿´£¬ÔÚ `SimpleAsyncTaskExecutor` ÖУ¬»á´´½¨ÐµÄÏß³ÌÀ´Ö´ÐÐÈÎÎñ¡£ + +ÕâÀïÄܲ»ÄÜ»ñÈ¡µ½Ö´ÐÐÆ÷ÄØ£¿Í¨¹ýµ÷ÊÔ·¢ÏÖ£¬Ö´ÐÐÆ÷µÄ»ñÈ¡½á¹ûΪ `null`£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-2e6ba6389e603a6373287dac26a89a24109.png) + +Òò´ËÔÚÏÂÃæÖ´ÐÐ `invokeListener(...)` ·½·¨Ê±£¬ÊÇÖ±½Óµ÷Óõģº + +``` +... + else { + invokeListener(listener, event); + } +... + +``` + +#### 2\. »ñȡʼþµÄ¼àÌýÆ÷£º`getApplicationListeners(...)` + +×¼±¸À´Ëµ£¬ÕâÀï»ñÈ¡µÄÊÇÄܼàÌý´«ÈëʼþµÄ¼àÌýÆ÷£¬·½·¨Îª `AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent, ResolvableType)`£º + +``` +/** + * Õâ¸ö·½·¨¾ÍÁ½¸ö²½Ö裺 + * 1\. ´Ó»º´æÖлñÈ¡£¬ÄÜ»ñÈ¡µ½£¬Ö±½Ó·µ»Ø + * 2\. ²»ÄÜ´Ó»º´æÖлñÈ¡£¬µ÷Óà retrieveApplicationListeners(...) ·½·¨»ñÈ¡ + */ +protected Collection> getApplicationListeners( + ApplicationEvent event, ResolvableType eventType) { + Object source = event.getSource(); + Class sourceType = (source != null ? source.getClass() : null); + ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); + // 1\. ´Ó»º´æÖлñÈ¡ + ListenerRetriever retriever = this.retrieverCache.get(cacheKey); + if (retriever != null) { + return retriever.getApplicationListeners(); + } + if (this.beanClassLoader == null || + (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && + (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { + synchronized (this.retrievalMutex) { + retriever = this.retrieverCache.get(cacheKey); + if (retriever != null) { + return retriever.getApplicationListeners(); + } + retriever = new ListenerRetriever(true); + // 2\. »ñÈ¡²Ù×÷£¬ÕâÀï²ÅÊǹؼü + Collection> listeners = + retrieveApplicationListeners(eventType, sourceType, retriever); + this.retrieverCache.put(cacheKey, retriever); + return listeners; + } + } + else { + return retrieveApplicationListeners(eventType, sourceType, null); + } +} + +``` + +Õâ¸ö·½·¨¿´×ų¤£¬µ«¹Ø¼ü²Ù×÷¾ÍÁ½¸ö£º + +1. ´Ó»º´æÖлñÈ¡£¬ÄÜ»ñÈ¡µ½£¬Ö±½Ó·µ»Ø +2. ²»ÄÜ´Ó»º´æÖлñÈ¡£¬µ÷Óà `retrieveApplicationListeners(...)` ·½·¨»ñÈ¡ + +ÎÒÃǼÌÐø½øÈë `retrieveApplicationListeners(...)`£º + +``` +/** + * ÔÚÕâÀïÕæÕý»ñÈ¡¼àÌýÆ÷ + * + */ +private Collection> retrieveApplicationListeners( + ResolvableType eventType, @Nullable Class sourceType, + @Nullable ListenerRetriever retriever) { + List> allListeners = new ArrayList<>(); + Set> listeners; + Set listenerBeans; + synchronized (this.retrievalMutex) { + listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); + listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); + } + // ´Ó listeners ÖлñÈ¡ÄÜ´¦Àíµ±Ç°Ê¼þµÄ lister + for (ApplicationListener listener : listeners) { + // ÔÚÕâÀïÅжϵ±Ç°listenerÊÇ·ñÖ§³Ö´«Èëevent + if (supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { + retriever.applicationListeners.add(listener); + } + allListeners.add(listener); + } + } + // ´Ó listenerBeans ÖлñÈ¡ÄÜ´¦Àíµ±Ç°Ê¼þµÄ lister + if (!listenerBeans.isEmpty()) { + ConfigurableBeanFactory beanFactory = getBeanFactory(); + for (String listenerBeanName : listenerBeans) { + try { + // ÕâÀïÅжϵ±Ç° listenerBeanName ÊÇ·ñÄܼàÌý´«Èëʼþ + if (supportsEvent(beanFactory, listenerBeanName, eventType)) { + // ´ÓÈÝÆ÷ÖлñÈ¡¶ÔÓ¦µÄbean£¬¶ÔÓÚÔçÆÚʼþ£¬»áÒýÆð¼àÌýÆ÷Ìáǰ³õʼ»¯ + ApplicationListener listener = beanFactory.getBean( + listenerBeanName, ApplicationListener.class); + if (!allListeners.contains(listener) + && supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { + // ×¢Òâµ¥ÀýÓë·Çµ¥ÀýµÄÇø±ð + if (beanFactory.isSingleton(listenerBeanName)) { + retriever.applicationListeners.add(listener); + } + else { + retriever.applicationListenerBeans.add(listenerBeanName); + } + } + allListeners.add(listener); + } + } + else { + Object listener = beanFactory.getSingleton(listenerBeanName); + if (retriever != null) { + retriever.applicationListeners.remove(listener); + } + allListeners.remove(listener); + } + } + catch (NoSuchBeanDefinitionException ex) { + } + } + } + // ÅÅÐò + AnnotationAwareOrderComparator.sort(allListeners); + if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { + retriever.applicationListeners.clear(); + retriever.applicationListeners.addAll(allListeners); + } + return allListeners; +} +... + +} + +``` + +ÔÚ `AbstractApplicationContext#registerListeners` ·½·¨ÖУ¬ÎÒÃÇÔÚ×¢²á¼àÌýÆ÷ʱ £¬×¢²áÁËÁ½´óÀà¼àÌýÆ÷£º + +1. ×¢²áÊÖ¶¯Ìí¼ÓµÄ¼àÌýÆ÷£¬ËüÃÇÊÇÒ»¸ö¸öʵÀý£» +2. ´ÓÈÝÆ÷ÖлñÈ¡¼àÌýÆ÷µÄ beanName£¬½øÐÐ×¢²á£» + +`retrieveApplicationListeners()` ÖгöÏÖµÄ `listeners` Óë `listenerBeans` ¾ÍÊÇ´¦ÀíÕâÁ½ÖÖÀàÐ͵ļàÌýÆ÷µÄ¡£ + +ÒÔÉÏ·½·¨ËäÈ»Óе㳤£¬µ«Âß¼­·Ç³£ÇåÎú£º + +1. ±éÀú `listeners`£¬ÖðÒ»µ÷Óà `supportsEvent(listener, eventType, sourceType)` À´Åжϵ±Ç° listener ÄÜ·ñ¼àÌý´«Èëʼþ£» +2. ±éÀú `listenerBeans`£¬ÖðÒ»µ÷Óà `supportsEvent(beanFactory, listenerBeanName, eventType)` À´Åжϵ±Ç° listener ÄÜ·ñ¼àÌý´«Èëʼþ£» + +¹ØÓÚÕâÁ½¸ö·½·¨µÄ´¦Àí±È½Ï¸´ÔÓ£¬ÕâÀï¾Í²»Ò»Ò»½øÐзÖÎöÁË£¬ÕâÀï¸ø³ö´¦ÀíµÄ´óÖÂ˼·£º + +1. ´Ó´«ÈëµÄ `listener` »ò `listenerBeanName` »ñÈ¡ `listener` µÄ Class£¬`listener` Ö»Ðè `listener.getClass()` ¼´¿É£¬`listenerBeanName` ¿Éͨ¹ý `beanFactory.getType(listenerBeanName)` »ñÈ¡£» + +2. »ñÈ¡ `listener` ¼àÌýµÄʼþÀàÐÍ£¬Ò»¸ö¼àÌýÆ÷ÊÇÕâÑù¶¨ÒåµÄ£º + + ``` + public class MyApplicationEventListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(MyApplicationEvent event) { + ... + } + } + + ``` + + ÎÒÃÇ¿ÉÒÔÕâÑù»ñÈ¡µ½ `MyApplicationEvent`£º + + ``` + // ÕâЩÀà¶¼ÊÇjdkÌṩµÄ + ParameterizedType parameterizedType = (ParameterizedType) + MyApplicationEventListener.class.getGenericInterfaces()[0]; + Class type = (Class)parameterizedType.getActualTypeArguments()[0]; + + ``` + + µ±È»£¬spring ÔÚ´¦ÀíÕⲿ·ÖÂß¼­Ê±Ìرð¸´ÔÓ£¬±Ï¾¹Èç¹û `MyApplicationEventListener` ͬʱʵÏÖÁ˶à¸ö½Ó¿Ú£¬»òÕß `MyApplicationEventListener` µÄ¸¸ - ¸¸ - ¸¸ -... ½Ó¿Ú²ÅÊÇ `ApplicationListener`£¬ÕâЩÇé¿ö¶¼Òª¿¼Âǵ½¡£ + +3. »ñÈ¡µ½¼àÌýÆ÷ÄܼàÌýµÄʼþÁË£¬¾ÍÄÜÅжϵ±Ç°ÅжϼàÌýÊÇ·ñÄܼàÌý´«ÈëʼþÁË£¬spring ´¦ÀíÆ¥ÅäµÄ·½·¨Îª `ResolvableType#isAssignableFrom(ResolvableType)`£¬´Ó×¢ÊÍÉÏÀ´¿´£¬ËüʵÏÖÁËÀàËÆ `Class.isAssignableFrom` ·½·¨µÄ¹¦ÄÜ£¬Í¬Ê±»¹¶Ô´«ÈëÀàÉϵķºÐÍҲʵÏÖÁËÀàËÆ `Class.isAssignableFrom` µÄ¹¦ÄÜ¡£ + +#### 3\. µ÷ÓüàÌý·½·¨£º`invokeListener(...)` + +ÖÕÓÚµ½µ÷ÓüàÌý·½·¨ÁË£¬´úÂëÈçÏ£º + +``` +/** + * Ö´ÐмàÌýÆ÷ + */ +protected void invokeListener(ApplicationListener listener, ApplicationEvent event) { + ErrorHandler errorHandler = getErrorHandler(); + // ×îÖÕµ÷ÓõÄÊÇ doInvokeListener(...) + if (errorHandler != null) { + try { + doInvokeListener(listener, event); + } + catch (Throwable err) { + errorHandler.handleError(err); + } + } + else { + doInvokeListener(listener, event); + } +} + +/** + * ¾ßÌåµÄÖ´ÐвÙ×÷£¬×îÖÕµ÷ÓõÄÊÇ ApplicationListener#onApplicationEvent ·½·¨ + */ +private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { + try { + listener.onApplicationEvent(event); + } + catch (ClassCastException ex) { + String msg = ex.getMessage(); + if (msg == null || matchesClassCastMessage(msg, event.getClass())) { + // Ê¡ÂÔÈÕÖ¾´òÓ¡ + } + else { + throw ex; + } + } +} + +``` + +ÕâÒ»¿éºÜ¼òµ¥£¬¾ÍÊDZéÀúÉÏÒ»²½»ñÈ¡µÄ¼àÌýÆ÷£¬ÖðÒ»µ÷ÓÃÆä `onApplicationEvent(...)` ·½·¨¡£ + +ÕâÀïÐèҪעÒâµÄÊÇ£¬ÔÚÇ°Ãæ`»ñȡִÐÐÆ÷`µÄ·ÖÎöÖУ¬ÎÒÃÇÌáµ½ `getTaskExecutor()` µÄ½á¹ûΪ `null`£¬Õâ±íÃ÷ `invokeListener(...)` ÊÇÖ±½ÓÖ´Ðе쬲¢Ã»ÓÐÁíÆðÒ»¸öÏß³ÌÖÐÖ´ÐУ¬ÕâµãÐèÒª¸ñÍâ×¢Òâ¡£ + +### 5\. ʼþµÄÓ¦Óà + +#### 5.1 ¼àÌýÈÝÆ÷Æô¶¯Íê³Éʼþ + +ÔÚ spring Æô¶¯Á÷³ÌÖУ¬ÈÝÆ÷Æô¶¯Íê³É»á·¢²¼ `ContextRefreshed` ʼþ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f60ea2710c59e2ce9e1401f1b54cbd9d700.png) + +ÎÒÃÇÒª¼àÌýÆ÷¸ÃʼþҲʮ·Ö¼òµ¥£¬ÏàÓ¦µÄ `Listener` ÈçÏ£º + +``` +@Component +public class ContextRefreshedListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + System.out.println("ÈÝÆ÷Æô¶¯Íê³É"); + } + +} + +``` + +#### 5.2 ·¢²¼ÔçÆÚʼþ + +Ò»¿ªÊ¼£¬ÎÒÒÔΪÔçÆÚʼþÊÇÕâÑù·¢²¼£º + +``` +AnnotationConfigApplicationContext context + = new AnnotationConfigApplicationContext(); +context.register(Demo08Config.class); +// ·¢²¼Ê¼þ£¬ÔÚÈÝÆ÷Æô¶¯Ç°·¢²¼ +context.publishEvent(new MyApplicationEvent( + Thread.currentThread().getName() + " | ×Ô¶¨Òåʼþ ...")); +context.refresh(); + +``` + +ÔËÐк󣬻ᱨ´í£º ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4fc18c95f69d8c3d65be2a0f603d42e19ce.png) + +´Ó´íÎóÐÅÏ¢À´¿´£¬ÊÇ˵¹ã²¥Æ÷δ³õʼ»¯¡£ + +ÄÇô¹ã²¥Æ÷ÊÇÔÚÄÄÀï³õʼ»¯µÄÄØ£¿»ØÒäÏÂÇ°ÃæµÄ·ÖÎö£¬ºÜÇå³þµØÖªµÀÊÇÔÚ `refresh()` ¹ý³ÌµÄµÚ 8 ²½£¬¶øÔçÆÚʼþµÄ·¢²¼ÓÖÔÚµÚ 10 ²½£¬ÕâÑùÒ»À´£¬ÔçÆÚʼþ¾ÍÖ»ÄÜÔÚµÚ 9 ²½ÁË£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-1fecd516c6d58d628937b20ebd806065891.png) + +´ÓÕâÀï¿ÉÒÔ¿´³ö£¬ÔçÆÚʼþ¾ÍÊÇΪ `onRefresh()` Õâ¸öÀ©Õ¹µã×¼±¸µÄ£¡ + +ÔçÆÚʼþµÄ·¢²¼Ò²¾Í¼òµ¥ÁË£º + +``` +ApplicationContext context = + new AnnotationConfigApplicationContext(Demo08Config.class) { + @Override + public void onRefresh() { + // ÕâÀï·¢²¼¾ÍÊÇÔçÆÚʼþ + publishEvent(new MyApplicationEvent( + Thread.currentThread().getName() + " | ×Ô¶¨Òåʼþ ...")); + } +}; + +``` + +#### 5.3 Òì²½¹ã²¥Ê¼þ + +´ÓÇ°ÃæµÄÔ´Âë·ÖÎöÀ´¿´£¬Ê¼þµÄÖ´ÐÐÊÇÔÚͬһ¸öÏß³ÌÖнøÐеģ¬ÕâµãÔÚ demo µÄÔËÐнá¹ûÖÐÒ²ÄÜ¿´³öÀ´£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-afed97c136df717ba3d9397ff142ae0675f.png) + +Ç°ÃæÒѾ­·ÖÎö¹ý£¬¹ã²¥Ê¼þʱ£¬»áÏÈ»ñÈ¡ `executor`£¬Èç¹û `executor` ´æÔÚ£¬ÔÚ `executor` Ö´ÐУ¬·ñÔò¾ÍÖ±½ÓÖ´ÐУ¬¶ø¹ã²¥Æ÷ÖÐµÄ `executor` Ϊ `null`£¬Òò´ËºÜÈÝÒ×½áÂÛ£ºÒªÏëʵÏÖÒì²½Ö´ÐУ¬ÐèÒªÔڹ㲥Æ÷ÖÐÉèÖà `executor` ÊôÐÔ¡£ + +Ç°ÃæÍ¬ÑùÒ²·ÖÎö¹ý£¬spring ÔÚ³õʼ»¯¹ã²¥Æ÷ʱ£¬»áÏÈÅжÏÈÝÒ×ÖÐÊÇ·ñ´æÔڹ㲥Æ÷£¨`beanName` Ϊ `applicationEventMulticaster`£©£¬²»´æÔÚÔòʹÓà `SimpleApplicationEventMulticaster` ´´½¨Ä¬ÈϵĹ㲥Æ÷¡£ + +Èç´ËÒ»À´£¬ÎÒÃÇÖ»ÐèÒª×Ô¶¨Òå `beanName` Ϊ `applicationEventMulticaster` µÄ bean ²»¾ÍÐУ¬ÏñÕâÑù£º + +``` +@Configuration +@ComponentScan +public class Demo08Config { + + /** + * Éú³É×Ô¶¨Òå¹ã²¥Æ÷ + * ×¢Ò⣺Ãû³Æ±ØÐëΪ applicationEventMulticaster + */ + @Bean + public ApplicationEventMulticaster applicationEventMulticaster() { + SimpleApplicationEventMulticaster applicationEventMulticaster + = new SimpleApplicationEventMulticaster(); + // SimpleAsyncTaskExecutor ÊÇspringÖÐÌṩµÄÒì²½ÈÎÎñÖ´ÐÐÆ÷ + applicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); + return applicationEventMulticaster; + } +} + +``` + +`SimpleApplicationEventMulticaster` ʹÓÃÁË spring ÌṩµÄÒì²½ÈÎÎñÖ´ÐÐÆ÷£º`SimpleAsyncTaskExecutor`£¬ÔËÐУ¬½á¹ûÈçÏ£º + +``` +SimpleAsyncTaskExecutor-2 | main | ×Ô¶¨Òåʼþ ... + +``` + +¿ÉÒÔ¿´µ½£¬·¢²¼Ïß³ÌÓë¼àÌýÏ̲߳»ÔÙÊÇͬһ¸öÁË¡£ + +µ«ÊÂÇéµ½Õ⻹ËãÍê£¬Ç°ÃæÒѾ­·ÖÎö¹ý `SimpleAsyncTaskExecutor` µÄÈÎÎñÖ´Ðйý³Ì£¬ÔÚÖ´ÐÐʱËü»á²»¶Ï´´½¨ÐÂỊ̈߳º + +``` + protected void doExecute(Runnable task) { + // ¿ÉÒÔ¿´µ½£¬ÕâÀï´´½¨ÁËỊ̈߳¬²¢ÇÒÆô¶¯ÁËÏß³Ì + Thread thread = (this.threadFactory != null + ? this.threadFactory.newThread(task) : createThread(task)); + thread.start(); + } + +``` + +Èç¹ûÎÒÃÇÁ¬Ðø·¢²¼ 100 ¸öʼþ£º + +``` +for(int i = 0; i < 100; i++) { + context.publishEvent(new MyApplicationEvent( + Thread.currentThread().getName() + " | ×Ô¶¨Òåʼþ ...")); +} + +``` + +ÔËÐнá¹û»áÕâÑù£º + +``` +SimpleAsyncTaskExecutor-2 | main | ×Ô¶¨Òåʼþ ... +SimpleAsyncTaskExecutor-3 | main | ×Ô¶¨Òåʼþ ... +SimpleAsyncTaskExecutor-4 | main | ×Ô¶¨Òåʼþ ... +... +SimpleAsyncTaskExecutor-99 | main | ×Ô¶¨Òåʼþ ... +SimpleAsyncTaskExecutor-100 | main | ×Ô¶¨Òåʼþ ... +SimpleAsyncTaskExecutor-101 | main | ×Ô¶¨Òåʼþ ... + +``` + +¿ÉÒÔ¿´µ½£¬¶ÔÓÚÿһ¸öʼþ£¬¶¼»á´´½¨Ò»¸öÐÂÏß³ÌÀ´Ö´ÐУ¬Ï̱߳¾¾ÍÊÇÈç´Ë±¦¹óµÄ×ÊÔ´£¬ÕâÑùƵ·±µØ´´½¨¾ÍÊÇÒ»¸ö¼«´óµÄÀË·Ñ£¬ÄÇÈçºÎÖØÓÃÏß³ÌÄØ£¿´ð°¸ÊÇÏ̳߳ء£ + +ÎÒÃÇ¿´Ï `SimpleApplicationEventMulticaster#setTaskExecutor` ·½·¨µÄ²ÎÊý£¬·¢ÏÖËüÊÇ `java.util.concurrent.Executor`£¬½ÓÏÂÀ´ÊÂÇé¾Í±äµÃ¼òµ¥ÁË£¬ÎÒÃÇÖ±½ÓʹÓà jdk ÌṩµÄÏ̳߳أº + +``` +@Bean +public ApplicationEventMulticaster applicationEventMulticaster() { + SimpleApplicationEventMulticaster applicationEventMulticaster + = new SimpleApplicationEventMulticaster(); + // ʹÓÃjdkÌṩµÄÏß³Ì³Ø + applicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(4)); + return applicationEventMulticaster; +} + +``` + +ÕâÀïʹÓõÄÊÇ jdK ÌṩµÄ `newFixedThreadPool`£¬ÉèÖÃÁË 4 ¸öỊ̈߳¬ÔËÐУ¬½á¹ûÈçÏ£º + +``` +pool-1-thread-2 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-3 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-4 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-4 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-1 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-3 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-3 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-2 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-1 | main | ×Ô¶¨Òåʼþ ... +pool-1-thread-3 | main | ×Ô¶¨Òåʼþ ... +... + +``` + +¿ÉÒÔ¿´µ½£¬×ÔʼÖÁÖÕÖ»ÓÐ 4 ¸öÏß³ÌÔڹ㲥ʼþ¡£ + +ÔÚÓÐЩ´úÂë¹æ·¶ÖУ¬ÊǽûÖ¹Ö±½ÓʹÓà jdk ÌṩµÄÏ̵߳ijأ¬ËûÃǸüÌᳫ×Ô¶¨ÒåÏ̳߳أ¬¹ØÓÚÕâµã£¬ÓÉÓÚ±¾ÎIJ¢²»ÊÇÌÖÂÛÏ̳߳ؼ¼ÊõµÄ£¬Òò´Ë¾Í¼òµ¥µØÊ¹Óà jdk ÌṩµÄÏß³ÌʾÀýÏ¡£ + +#### 5.4 ×Ô¶¨Òåʼþ + +ʵ¼ÊÉÏ£¬Ç°ÃæÌṩµÄʾÀý demo ¾ÍÊÇ×Ô¶¨Òåʼþ£¬ÕâÀï¾Í²»Öظ´ÁË¡£ + +### 6\. ×ܽá + +±¾ÎÄ·ÖÎöÁË spring ʼþ»úÖÆ£¬½éÉÜÁËʼþµÄËÄ´ó»ù±¾×é¼þ£ºÊ¼þ£¬·¢²¼Æ÷£¬¹ã²¥Æ÷£¬¼àÌýÆ÷£¬²¢ÇÒ´ÓÔ´Âë·ÖÎöÁË×é¼þµÄ³õʼ»¯¡¢Ê¼þµÄ·¢²¼Á÷³Ì¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4713339](https://my.oschina.net/funcy/blog/4713339) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" new file mode 100644 index 0000000..faddf44 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\270\200\357\274\211\357\274\232\347\220\206\350\256\272\345\237\272\347\237\263.md" @@ -0,0 +1,1091 @@ +### 1\. ʲôÊÇÑ­»·ÒÀÀµ£¿ + +spring ÔÚÒÀÀµ×¢Èëʱ£¬¿ÉÄÜ»á³öÏÖÏ໥עÈëµÄÇé¿ö£º + +``` +@Service +public class Service1 { + @Autowired + private Service2 service2; + +} + +@Service +public class Service2 { + @Autowired + private Service1 service1; + +} + +``` + +ÈçÒÔÉÏ´úÂ룬ÔÚ `Service1` ÖÐͨ¹ý `@Autowird` ×¢ÈëÁË `Service2`£¬ÔÚ `Service2` ÖÐͨ¹ý `@Autowird` ×¢ÈëÁË `Service1`£¬ÕâÖÖÏ໥עÈëµÄÇé¿ö£¬¾Í½Ð×öÑ­»·ÒÀÀµ¡£ + +### 2\. Ñ­»·ÒÀÀµ»áÓÐʲôÎÊÌâ + +ʵ¼ÊÉÏ£¬ÕâÖÖ `A³ÖÓÐB¶ÔÏó£¬BÒ²³ÖÓÐA¶ÔÏó`µÄÇé¿ö£¬java ´úÂëÊÇÍêȫ֧³ÖµÄ£º + +``` +/** + * ×¼±¸service1 + */ +public class Service1 { + private Service2 service2; + + public void setService2(Service2 service2) { + this.service2 = service2; + } + + public Service2 getService2() { + return this.service2; + } +} + +/** + * ×¼±¸service2 + */ +public class Service2 { + private Service1 service1; + + public void setService1(Service1 service1) { + this.service1 = service1; + } + + public Service1 getService1() { + return this.service1; + } +} + +/** + * Ö÷·½·¨Öе÷Óà + */ +public class Main { + public void main(String[] args) { + // ×¼±¸Á½¸ö¶ÔÏó + Service1 service1 = new Service1(); + Service2 service2 = new Service2(); + // Ï໥ÉèÖà + service1.setService2(service2); + service2.setService1(service1); + } +} + +``` + +ÄÇô£¬ÔÚ spring ÖУ¬Á½¸öÀàÏ໥עÈë¶Ô·½ÊµÀýµÄÇé¿ö£¬»áÓÐʲôÎÊÌâÄØ£¿ÎÒÃÇÀ´¿´ `spring bean` µÄ´´½¨¹ý³Ì£¨**×¢Ò⣺ÕâÀïÎÒÃǽö·ÖÎö `bean` µÄ `scope` Ϊ `singleton` µÄÇé¿ö£¬Ò²¾ÍÊÇ `scope` Ϊ`µ¥Àý`µÄÇé¿ö**£©£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ca36e17077b1b191834645b3c8e588ff6c4.png) + +Õâ¸ö¹ý³ÌÖÐÓм¸µãÐèҪ˵Ã÷Ï£º + +1. ´´½¨¶ÔÏó£ºÕâ¸öÆäʵ¾ÍÊÇʹÓà jdk ÌṩµÄ·´Éä»úÖÆ´´½¨ java ¶ÔÏó£¬ÒÔµÚ 1 ½ÚÌáµ½µÄ `Service1` ΪÀý£¬¿É¼òµ¥Àí½âΪ `Service1 service = new Service1()`£» +2. ×¢ÈëÒÀÀµ¶ÔÏ󣺻¹ÊÇÒÔµÚ 1 ½ÚÌáµ½µÄ `Service1` ΪÀý£¬`Service1` ÖÐͨ¹ý `@Autowired` ×Ô¶¯×¢Èë `Service2`£¬ÕâÒ»²½¾ÍÊǸø `Service2` ¸³ÖµµÄ¹ý³Ì£¬¿É¼òµ¥Àí½âΪ `service1.setService2(service2)`£» +3. `singletonObjects`£º¾­¹ýÉÏÃæÁ½²½ºó£¬Ò»¸ö java ¶ÔÏó¾Í±ä³ÉÁËÒ»¸ö spring bean£¬È»ºó±£´æµ½ `singletonObjects` ÁË£¬ÕâÊǸö `map`£¬`key` ÊÇ bean µÄÃû³Æ£¬`value` ÊÇ bean£¬ËüÖ»±£´æ `spring bean`£¬²»»áÖ»ÔÚ java ʵÀý¡£ + +ʵ¼ÊÉÏ£¬`java` ¶ÔÏó±ä³É `spring bean`£¬²»½ö½öÖ»ÊÇÒÀÀµ×¢È룬»¹Óгõʼ»¯¡¢Ö´ÐÐ `beanPorcessor` ·½·¨µÈ£¬**ÓÉÓÚ±¾ÎÄÊÇ·ÖÎö `spring bean` µÄÑ­»·ÒÀÀµµÄ£¬Òò´ËÎÒÃÇÖØµã¹Ø×¢ÓëÑ­»·ÒÀÀµÏà¹ØµÄ²½Öè¡£** + +#### 2.1 Ñ­»·ÒÀÀµ²úÉúµÄÎÊÌâ + +Á˽âÁË spring bean µÄ²úÉú¹ý³ÌÖ®ºó£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÏÂÑ­»·ÒÀÀµ²úÉúµÄÎÊÌ⣬ÔÚÕýʽ·ÖÎöǰ£¬ÎÒÃÇÏÈÀ´Ã÷È·Á½¸ö¸ÅÄ + +* `java¶ÔÏó`£ºÊµ¼ÊÉÏ£¬java ÖÐÒ»ÇжÔÏ󶼿ÉÒÔ³ÆÖ®Îª `java` ¶ÔÏó£¬ÎªÁË˵Ã÷·½±ã£¬ÒÔÏÂÌáµ½µÄ `java¶ÔÏó`½öָʵÀý»¯Íê³É¡¢µ«Î´½øÐÐ spring bean µÄÉúÃüÖÜÆÚ¶ÔÏó£» +* `spring bean`£ºÊÇÒ»¸ö java ¶ÔÏ󣬲¢ÇÒ½øÐÐÁËÍêÕûµÄ spring bean µÄÉúÃüÖÜÆÚ¶ÔÏó£» + +spring bean µÄ´´½¨¹ý³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b55a211447b5fabeaa0c3ef0bfee0920c82.png) + +¶ÔÉÏͼ˵Ã÷ÈçÏ£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºó¾ÍÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬´ËʱÊÇÕÒ²»µ½ `service2`£¬È»ºó¾Í¿ªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +2. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `singletonObjects` ÖвéÕÒ `service1`£¬´ËʱÊÇÕÒ²»µ½ `service1`£¬ÒòΪµÚÒ»²½ÖÐ `service1` ²¢Ã»Óд´½¨³É¹¦ £¬È»ºó¾Í¿ªÊ¼ÁË `service1` µÄ´´½¨¹ý³Ì£» +3. Á÷³ÌÌø»Øµ½ `1`£¬ÔٴοªÊ¼ÁË `service1` µÄ´´½¨¡¢ÊôÐÔ×¢Èë¹ý³Ì¡£ + +µ½ÕâÀÎÒÃǾªÏ²µØ·¢ÏÖ£¬Ñ­»·³öÏÖÁË£¡ + +#### 2.2 ÒýÈë `earlySingletonObjects` ½â¾öÑ­»·ÒÀÀµ + +ÎÒÃÇ·ÖÎöÏ£¬Ñ­»·³öÏÖµÄÔ­ÒòÔÚÓÚ£¬ÔÚ `service2` »ñÈ¡ `service1` ʱ£¬ÓÉÓÚ `singletonObjects` ÖдËʱ²¢²»´æÔÚ `service1`£¬Òò´Ë»áÔÙ×ß `service1` µÄ´´½¨¹ý³Ì£¬ÖØÐ´´½¨ `service1`£¬Òò´Ë£¬ÎÒÃÇÓиö´óµ¨µÄÏë·¨£ºÈç¹ûÔÚ `service1` ʵÀý»¯ºó¾Í°ÑËü±£´æÆðÀ´£¬ºóÃæÔÙÔÙÕÒ `service1` ʱ£¬¾Í·µ»ØÕâ¸öδ½øÐÐÒÀÀµ×¢ÈëµÄ `service1`£¬ÏñÏÂÃæÕâÑù£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c0eaecbe82b144a6fcd9fd048f2cca53497.png) + +ÉÏͼÖУ¬ÒýÈëÁË `earlySingletonObjects`£¬ÕâÒ²ÊǸö map£¬Í¬ `singletonObjects` Ò»Ñù£¬`key` ÊÇ bean µÄÃû³Æ£¬`value` ÊÇÒ»¸öδÍê³ÉÒÀÀµ×¢ÈëµÄ¶ÔÏó¡£ + +¶ÔÉÏͼ˵Ã÷ÈçÏ£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service1` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +2. ¶Ô `service1` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºóÏÈÈ¥ `earlySingletonObjects` ²éÕÒ `service2`£¬Î´ÕÒµ½£»ÔÙÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬»¹ÊÇδÕÒµ½£¬ÓÚÊǾͿªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +3. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service2` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +4. ¶Ô `service2` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `earlySingletonObjects` ²éÕÒ `service1`£¬ÕÒµ½ÁË£¬¾Í½« `service1` ×¢Èëµ½ `service2` ÖУ¬´Ëʱ `service2` ¾ÍÊÇÒ»¸ö `spring bean` ÁË£¬½«Æä±£´æµ½ `singletonObjects` ÖУ» +5. ¾­¹ýµÚ 4 ²½ºó£¬ÎÒÃǵõ½ÁË `service2`£¬È»ºó½«Æä×¢Èëµ½ `service1` ÖУ¬´Ëʱ `service1` Ò²³ÉÁËÒ»¸ö `spring bean`£¬½«Æä±£´æµ½ `singletonObjects` ÖС£ + +¾­¹ýÒÔÉϲ½Ö裬ÎÒÃÇ·¢ÏÖ£¬Ñ­»·ÒÀÀµµÃµ½Á˽â¾ö¡£ + +#### 2.2 aop ϵÄÑ­»·ÒÀÀµ + +¾­¹ýÉÏÃæµÄ·ÖÎö£¬ÎÒÃÇ·¢ÏÖÖ»Òª¶îÍâÒýÈëÒ»¸ö `earlySingletonObjects` ºó£¬Ñ­»·ÒÀÀµ¾ÍÄܵõ½½â¾ö¡£µ«ÊÇ£¬Ñ­»·ÒÀÀµÕæµÄµÃµ½Á˽â¾öÂð£¿spring ³ýÁË ioc Í⣬»¹ÓÐÁíÒ»¸öÖØ´ó¹¦ÄÜ£ºaop£¬ÎÒÃÇÀ´¿´¿´ aop Çé¿öϳöÏÖÑ­»·ÒÀÀµ»áÔõÑù¡£ + +##### 1\. aop ¶ÔÏóµÄ´´½¨¹ý³Ì + +ÔÚÕýʽ½éÉÜ aop ϵÄÑ­»·ÒÀÀµÇ°£¬ÎÒÃÇÏÈÀ´Ã÷È·Á½¸ö¸ö¸ÅÄ + +* `ԭʼ¶ÔÏó`£ºÇø±ðÓÚ´úÀí¶ÔÏó£¬Ö¸Î´½øÐйý aop µÄ¶ÔÏ󣬿ÉÒÔÊÇ java ¶ÔÏó£¬Ò²¿ÉÒÔÊÇδ½øÐÐ aop µÄ spring bean£» +* `´úÀí¶ÔÏó`£º½øÐйý aop µÄ¶ÔÏ󣬿ÉÒÔÊÇ java ¶ÔÏó½ö½øÐйý aop µÃµ½µÄ¶ÔÏó (½ö½øÐйý aop£¬Î´½øÐÐÒÀÀµ×¢È룬Ҳδ½øÐгõʼ»¯)£¬Ò²¿ÉÒÔÊǽøÐйý aop µÄ `spring bean`. + +ÎÒÃÇÏÈÀ´¿´¿´ aop ÊÇÈçºÎ´´½¨¶ÔÏóµÄ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e92991c1c173bbd5579be3001a977555be7.png) + +Ïà±ÈÓÚ `2.1` ÖеÄÁ÷³Ì£¬aop ¶àÁË "Éú³É´úÀí¶ÔÏó" µÄ²Ù×÷£¬²¢ÇÒ×îÖÕ±£´æµ½ `singletonObjects` ÖеĶÔÏóÒ²ÊÇ´úÀí¶ÔÏó¡£ + +ԭʼ¶ÔÏóÓë´úÀí¶ÔÏóÖ®¼äÊÇʲô¹ØÏµÄØ£¿ÓôúÂëʾÒâÏ£¬´óÖÂÈçÏ£º + +``` +public class ProxyObj extends Obj { + + // ԭʼ¶ÔÏó + private Obj obj; + + ... +} + +``` + +ʵ¼ÊÉÏ£¬Á½ÕßÖ®¼äµÄ¹ØÏµ²¢Ã»ÓÐÕâô¼òµ¥£¬µ«ÎªÁË˵Ã÷ÎÊÌ⣬ÕâÀï¶ÔÁ½Õß¹ØÏµ×öÁ˼ò»¯£¬Ð¡»ï°éÃÇÖ»ÐèÒªÃ÷°×£¬**´úÀí¶ÔÏó³ÖÓÐԭʼ¶ÔÏóµÄÒýÓÃ**¼´¿É¡£ + +¹ØÓÚԭʼ¶ÔÏóÈçºÎ±ä³É´úÀí¶ÔÏóµÄ£¬¿ÉÒԲο¼ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)¡£ + +¶ÔÒÔÉÏ´´½¨¹ý³Ì£¬Óà java ´úÂëÄ£ÄâÈçÏ£º + +``` +/** + * ×¼±¸Ò»¸öÀà + */ +public class Obj1 { + +} + +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj1 + */ +public class Obj2 { + + private Obj1 obj1; + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj2µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj2µÄ¶ÔÏó + */ +public class ProxyObj2 extends Obj2 { + + private Obj2 obj2; + + public ProxyObj2(Obj2 obj2) { + this.obj2 = obj2; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +``` + +½Ó×Å£¬¾ÍÊÇÄ£Äâ ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄ Á÷³ÌÁË£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // ¿ªÊ¼ Obj2 µÄ´´½¨Á÷³Ì + // 1\. ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. Íù Obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐobj1£¬Òò´ËÏÈÒª´´½¨obj1£¬ÔÙ½«Æä×¢Èëµ½Obj2ÖÐ + Obj1 obj1 = new Obj1(); + obj2.setObj1(obj1); + + // 3\. Éú³ÉObj2µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2µÄԭʼ¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // 4\. proxyObj2ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + +} + +``` + +ÉÏÊö´úÂëÖУ¬ + +* ÒÔ `new Obj2()` Ä£Äâ¶ÔÏóµÄ´´½¨ +* ÒÔ `obj2.setObj1(xxx)` Ä£ÄâÒÀÀµ×¢Èë +* ÒÔ `new ProxyObj2(xxx)` Ä£Äâ´úÀí¶ÔÏóµÄÉú³É +* ÒÔ `collection.add(xxx)` Ä£Äâ¶ÔÏóÌí¼Óµ½ÈÝÆ÷ÖеĹý³Ì + +Ä£ÄâµÄÁ÷³ÌÈçÏ£º + +1. ´´½¨ `obj2` ¶ÔÏó +2. Íù `Obj2` ÖÐ×¢Èë `obj1`£¬µ«´Ëʱ²¢Ã»ÓÐ `obj1`£¬Òò´ËÏÈÒª´´½¨ `obj1`£¬ÔÙ½«Æä×¢Èëµ½ `Obj2` ÖÐ +3. Éú³É `Obj2` µÄ´úÀí¶ÔÏó `proxyObj2`£¬`proxyObj2` ÖгÖÓÐ `Obj2` µÄԭʼ¶ÔÏó +4. `proxyObj2` ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + +×Ðϸ·ÖÎöÉÏÃæµÄ²½Ö裬¾Í»á·¢ÏÖ£¬ÉÏÃæµÄµÚ 2 ²½ÓëµÚ 3 ²½ÍêÈ«µ÷»»Ë³ÐòҲûÎÊÌ⣬´úÂëÄ£ÄâÈçÏ£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // ¿ªÊ¼ Obj2 µÄ´´½¨Á÷³Ì + // 1\. ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. Éú³ÉObj2µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2µÄԭʼ¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // 3\. Íù obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐobj1£¬Òò´ËÏÈÒª´´½¨obj1£¬ÔÙ½«Æä×¢Èëµ½Obj2ÖÐ + Obj1 obj1 = new Obj1(); + // ÕâÀïÊÇ×¢È뵽ԭʼ¶ÔÏóÖÐ + obj2.setObj1(obj1); + + // 4\. proxyObj2ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + +} + +``` + +ÉÏÊö´úÂëµÄÁ÷³ÌÈçÏ£º + +1. ´´½¨ obj2 ¶ÔÏó +2. Éú³É Obj2 µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2 µÄԭʼ¶ÔÏó +3. Íù Obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐ obj1£¬Òò´ËÏÈÒª´´½¨ obj1£¬ÔÙ½«Æä×¢Èëµ½ Obj2 +4. proxyObj2 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + +´Ó´úÂëÉÏ¿´£¬`proxyObj2(´úÀí¶ÔÏó)` ÖгÖÓÐ `ob2(ԭʼ¶ÔÏó)`£¬Éú³É´úÀí¶ÔÏóºó£¬¼ÌÐø¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢È룬ÒÀÈ»ÄÜÓ°Ïì´úÀí¶ÔÏó£¬×îÖÕ´úÀí¶ÔÏó³ÖÓеÄԭʼ¶ÔÏóÒ²Íê³ÉÁËÒÀÀµ×¢È룬Õû¸ö¹ý³ÌÓÃͼÐÎʾÒâÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8ff5579425f73dd5c2d321a86d0303390ac.png) + +ÕâÀïÎÒÃÇÔÙ´ÎÉêÃ÷£¬´Ó java ¶ÔÏóµ½ spring bean µÄ²½ÖèÓкö࣬ÕâÀïÎÒÃǽö¹Ø×¢ÓëÑ­»·ÒÀÀµÏà¹ØµÄ²½Ö裬Èç¹ûÏëÁ˽â spring bean ÏêϸµÄ³õʼ»¯¹ý³Ì£¬¿É²é¿´ [spring Æô¶¯Á÷³ÌÖ®Æô¶¯Á÷³Ì¸ÅÀÀ](https://my.oschina.net/funcy/blog/4597493)¡£ + +µ½ÕâÀÎÒÃÇ̽Ë÷µ½´úÀí¶ÔÏóµÄÉúÃüÖÜÆÚ¿ÉÒÔÓÐÁ½ÖÖ£º + +* ´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖÐ +* ´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó (Ìáǰ½øÐÐ aop)--> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖÐ + +ÕâÁ½ÖÖ¶¼ÄÜ´ïµ½×îÖÕÄ¿µÄ£¬¼´**±£´æµ½ÈÝÆ÷ÖеÄÊÇ´úÀí¶ÔÏó£¬ÇÒ´úÀí¶ÔÏó¶ÔÓ¦µÄԭʼ¶ÔÏóÍê³ÉÁËÒÀÀµ×¢Èë**¡£ÇëÀμÇÕâÁ½¸ö´´½¨Á÷³Ì£¬ÕâÊǺóÃæ½â¾ö aop ÏÂÑ­»·ÒÀÀµÎÊÌâµÄºËÐÄ£¬Ëµ°×ÁË£¬**aop ϵÄÑ­»·ÒÀÀµÎÊÌâÖ®ËùÒÔÄܽâ¾ö£¬¾ÍÊÇÒòΪ¶ÔÏó¿ÉÒÔÌáǰ½øÐÐ aop ²Ù×÷**¡£ + +##### 2\. ΪʲôÓà `earlySingletonObjects` ÎÞ·¨½â¾öÑ­»·ÒÀÀµ£¿ + +Ç°ÃæÎÒÃÇÖ÷Ҫ˵Ã÷ÁË´úÀí¶ÔÏóµÄ´´½¨¹ý³Ì£¬½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ÔÚ aop Ï£¬Ê¹Óà `earlySingletonObjects` À´½â¾öÑ­»·ÒÀÀµÓÐʲôÎÊÌ⣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d3d00d76ab0c72339faccb7ccd853723d6c.png) + +ÎÒÃÇÀ´·ÖÎöÉÏͼµÄÁ÷³Ì£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service1` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +2. ¶Ô `service1` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºóÏÈÈ¥ `earlySingletonObjects` ²éÕÒ `service2`£¬Î´ÕÒµ½£»ÔÙÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬»¹ÊÇδÕÒµ½£¬ÓÚÊǾͿªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +3. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service2` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +4. ¶Ô `service2` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `earlySingletonObjects` ²éÕÒ `service1`£¬ÕÒµ½ÁË£¬¾Í½« `service1` ×¢Èëµ½ `service2` ÖУ¬È»ºóÔÙ½øÐÐ aop£¬´Ëʱ `service2` ÊÇÒ»¸ö´úÀí¶ÔÏ󣬽«Æä±£´æµ½ `singletonObjects` ÖУ» +5. ¾­¹ýµÚ 4 ²½ºó£¬ÎÒÃǵõ½ÁË `service2` µÄ´úÀí¶ÔÏó£¬È»ºó½«Æä×¢Èëµ½ `service1` ÖУ¬½Ó×ÅÔÙ¶Ô `service1` ½øÐÐ aop£¬´Ëʱ `service1` Ò²³ÉÁËÒ»¸ö `spring bean`£¬½«Æä±£´æµ½ `singletonObjects` ÖС£ + +ÉÏÊö²½ÖèÓÐʲôÎÊÌâÄØ£¿×Ðϸ¿´µÚ 4 ²½£¬¾Í»á·¢ÏÖ£¬**×¢Èëµ½ `service2` µÄ `service1` ²¢²»ÊÇ´úÀí¶ÔÏó**£¡×ݹÛÈ«¾Ö£¬×îÖյõ½µÄ `service1` Óë `service2` ¶¼ÊÇ´úÀí¶ÔÏó£¬×¢Èëµ½ `service2` µÄ `service1` Ó¦¸ÃÒ²ÊÇ´úÀí¶ÔÏó²Å¶Ô¡£Òò´Ë£¬ÔÚ aop Ï£¬Ñ­»·ÒÀÀµµÄÎÊÌâÓÖ³öÏÖÁË£¡ + +#### 2.3 spring µÄ½â¾ö·½°¸ + +Ç°ÃæÎÒÃÇÌáµ½£¬ÔÚ aop Ï£¬ÒýÈë `earlySingletonObjects` ²¢²»Äܽâ¾öÑ­»·ÒÀÀµµÄÎÊÌ⣬ÄÇ spring ÊÇÔõô½â¾öµÄÄØ£¿spring ÔÙ´ÎÒýÈëÁËÒ»¸ö `map` À´½â¾öÕâ¸öÎÊÌ⣬ÕâÒ²ÊÇÈËÃdz£ËµµÄ **spring Èý¼¶»º´æ**£¬¶ÔÕâÈý¸ö `map` ˵Ã÷ÈçÏ£º + +* Ò»¼¶»º´æ `singletonObjects`£ºÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇÍêÕûµÄ `spring bean`£¬¼´Íê³ÉÊôÐÔ×¢Èë¡¢³õʼ»¯µÄ bean£¬Èç¹û bean ÐèÒª aop£¬´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£» +* ¶þ¼¶»º´æ `earlySingletonObjects`£ºÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇʵÀý»¯Íê³É£¬µ«Î´½øÐÐÒÀÀµ×¢ÈëµÄ `bean`£¬Èç¹û `bean` ÐèÒª `aop`£¬ÕâÀï´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£¬Ö»²»¹ý´úÀí¶ÔÏóËù³ÖÓеÄԭʼ¶ÔÏó²¢Î´½øÐÐÒÀÀµ×¢È룻 +* Èý¼¶»º´æ `singletonFactories`£ºÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ´æ´¢µÄÊÇÒ»¸ö `lambda` ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬ `getEarlyBeanReference` ÖÐµÄ `bean` ÊǸմ´½¨Íê³ÉµÄ `java bean`£¬Ã»ÓнøÐÐ spring ÒÀÀµ×¢È룬Ҳû½øÐÐ aop (¹ØÓÚÕâ¸ö `lambda` ±í´ïʽ£¬ºóÃæ»á¼ÌÐø·ÖÎö)¡£ + +ΪÁË˵Ã÷·½±ã£¬ÏÂÃæ¶Ô `singletonObjects`¡¢`earlySingletonObjects` ºÍ `singletonFactories` ·Ö±ð³ÆÎª**Ò»¼¶»º´æ**¡¢**¶þ¼¶»º´æ**ºÍ**Èý¼¶»º´æ**¡£ + +spring ½â¾ö aop ϵÄÑ­»·ÒÀÀµÁ÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-dc325a87b321e4c246a1b2f14169821a75a.png) + +Õâ¸öͼ¿´×űȽϸ´ÔÓ£¬Æäʵ·Ö¿ªÀ´¿´¾Í±È½Ï¼òµ¥ÁË£¬ÉÏÊö²Ù×÷ÖУ¬`1~8` ÊÇ»ñÈ¡ `service1` µÄÁ÷³Ì£¬`5.1~5.8` ÊÇ»ñÈ¡ `service2` µÄÁ÷³Ì£¬`5.5.1` ÊÇÔٴλñÈ¡ `service1` µÄÁ÷³Ì£¬Ö»²»¹ýÔÚ´¦Àí `service1` µÄ³õʼ»¯¹ý³ÌÖУ¬»á´¥·¢ `service2` µÄ³õʼ»¯¹ý³Ì£¬¶ø `service2` µÄ³õʼ»¯Ê±£¬ÓÖ»áÒÀÀµµ½ `service1`£¬Òò´Ë²Å¿´×ÅÏñÊÇÁ¬ÔÚÒ»Æð£¬±È½Ï¸´ÔÓ¡£ + +¶ÔÉÏͼµÄ¹ý³Ì£¬ÕâÀï˵Ã÷ÈçÏ£¨½¨Ò飺Èç¹û¾õµÃÁ÷³Ì±È½Ï¸´ÔÓ£¬¿ÉÒÔÏÈ¿´ `1~8` µÄ²Ù×÷£¬ÔÙ¿´ `5.1~5.8` µÄ²Ù×÷£¬×îºóÁ½ÕßÁªºÏÆðÀ´¿´£¬ÕâÑù»áÇåÎúºÜ¶à£©£º + +* 1. `service1`£º»ñÈ¡ `service1`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬´ËʱÊÇ»ñÈ¡²»µ½µÄ£» +* 1. `service1`£º´´½¨ `service1` µÄʵÀý£» +* 1. `service1`£º»ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¨ÔÚԭʼ¶ÔÏóÉϽøÐлñÈ¡£©£» +* 1. `service1`£ºÈç¹û¿ªÆôÁËÖ§³ÖÑ­»·ÒÀÀµµÄÅäÖ㬾ͽ« `service1` ·Åµ½Èý¼¶»º´æÖУ¨ÊÇ·ñÖ§³ÖÑ­»·ÒÀÀµ£¬ÊÇ¿ÉÒÔÅäÖõģ©£» +* 1. `service1`£º¶Ô `service1` ½øÐÐÒÀÀµ×¢È룬ÐèÒª `service2`£¬È»ºó¾Í¿ªÊ¼ÁË `service2` µÄ»ñÈ¡Á÷³Ì£» +* 5.1 `service2`£º»ñÈ¡ `service2`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬´ËʱÊÇ»ñÈ¡²»µ½µÄ£» +* 5.2 `service2`£º´´½¨ `service2` µÄʵÀý£» +* 5.3 `service2`£º»ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¨ÔÚԭʼ¶ÔÏóÉϽøÐлñÈ¡£©£» +* 5.4 `service2`£ºÈç¹û¿ªÆôÁËÖ§³ÖÑ­»·ÒÀÀµµÄÅäÖ㬾ͽ« `service2` ·Åµ½Èý¼¶»º´æÖУ¨ÊÇ·ñÖ§³ÖÑ­»·ÒÀÀµ£¬ÊÇ¿ÉÒÔÅäÖõģ©£» +* 5.5 `service2`£º¶Ô `service2` ½øÐÐÒÀÀµ×¢È룬ÐèÒª `service1`£¬È»ºó¾Í¿ªÊ¼ÁË `service1` µÄ»ñÈ¡Á÷³Ì£» +* 5.5.1 `service1`: »ñÈ¡ `service1`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬»ñÈ¡²»µ½£»´Ëʱ·¢ÏÖ `service1` ÕýÔÚ´´½¨ÖУ¬ÓÚÊǼÌÐø´Ó¶þ¡¢Èý¼¶»º´æÖлñÈ¡£¬×îÖÕ´ÓÈý¼¶»º´æÖлñÈ¡µ½ÁË£¬½«Æä·ÅÈë¶þ¼¶»º´æ¡£´ÓÈý¼¶»º´æ»ñÈ¡µÄ¹ý³ÌÖУ¬**»áÅÐ¶Ï `service1` ÊÇ·ñÐèÒª½øÐÐ aop£¬È»ºó¿ªÊ¼ aop ²Ù×÷**£¬Òò´Ë·ÅÈë¶þ¼¶»º´æÖеÄÊÇ `service1` ´úÀí´úÀí£¬Ìáǰ½øÐÐ aop Êǽâ¾öÑ­»·ÒÀÀµµÄ¹Ø¼ü£» +* 5.6 `service2`£ºµÃµ½ÁË `service1` ºó£¨ÕâÀïµÄ `service1` ÊÇ´úÀí¶ÔÏ󣩣¬½«Æä×¢Èëµ½ `service2` ÖУ¬½Ó×Å¶Ô `service2` ½øÐÐ aop£¬µÃµ½ `service2` µÄ´úÀí¶ÔÏó£» +* 5.7 `service2`£ºÈç¹ûÖ§³ÖÑ­»·ÒÀÀµ£¬ÏÈ´ÓÒ»¡¢¶þ¼¶»º´æÖÐÔٴλñÈ¡ `service2`£¬¶¼Î´»ñÈ¡µ½£¬¾ÍʹÓõ±Ç° `service2`£¨µ±Ç° `service2` ÊÇ´úÀí¶ÔÏó)£» +* 5.8 `service2`£º½« service2 µÄ´úÀí¶ÔÏó·ÅÈëÒ»¼¶»º´æÖУ¬É¾³ý¶þ¡¢Èý¼¶»º´æ£¬ÖÁ´Ë£¬`service2` ³õʼ»¯Íê³É£¬×¢ÈëµÄ `service1` ÊÇ´úÀí¶ÔÏó£¬Ò»¼¶»º´æÖÐµÄ `service2` Ò²ÊÇ´úÀí¶ÔÏó£» +* 1. `service1`£º»Øµ½ `service1` µÄÉúÃüÖÜÆÚ£¬Äõ½ `service2`£¨ÕâÀïµÄ `service2` ÊÇ´úÀí¶ÔÏ󣩺󣬽«Æä×¢Èëµ½ `service1`£¬`service1` µÄÒÀÀµ×¢ÈëÍê³É£¬½øÐгõʼ»¯£¬ÕâÀï»áÅÐ¶Ï `service1` ÊÇ·ñÐèÒª½øÐÐ aop£¬ËäÈ» `service1` ÊÇÐèÒª½øÐÐ aop µÄ£¬µ«ÓÉÓÚÔÚ `5.5.1` ÒѾ­½øÐйý aop ÁË£¬Òò´Ë£¬ÕâÀïÖ±½Ó·µ»Ø£¨µ½ÕâÒ»²½£¬`service1` »¹ÊÇԭʼ¶ÔÏ󣩣» +* 1. `service1`£ºÈç¹ûÖ§³ÖÑ­»·ÒÀÀµ£¬ÏÈ´ÓÒ»¼¶»º´æÖлñÈ¡ `service1`£¬»ñÈ¡²»µ½£»ÔÙ´Ó¶þ»º´æÖлñÈ¡ `service1`£¬¿ÉÒÔ»ñÈ¡µ½£¨´Ó `5.5.1` ¿ÉÖª£¬¶þ¼¶»º´æÀïÊÇ `service1` ´úÀí¶ÔÏ󣩣¬·µ»Ø£» +* 1. `service1`£º½«¶þ¼¶»º´æÖлñÈ¡µÄ¶ÔÏó×¢²áµ½Ò»¼¶»º´æÖУ¬É¾³ý¶þ¡¢Èý¼¶»º´æ£¬ÖÁ´Ë£¬`service1` ³õʼ»¯Íê³É£¬×¢ÈëµÄ `service2` ÊÇ´úÀí¶ÔÏó£¬Ò»¼¶»º´æÖÐµÄ `service1` Ò²ÊÇ´úÀí¶ÔÏó¡£ + +ÒÔÉÏÁ÷³Ì£¬ËäÈ»²½Öè½Ï¶à£¬µ« `service1` Óë `service2` µÄ»ñÈ¡²½ÖèÊÇÏàͬµÄ£¬Ö»ÒªÅªÇåÁËÆäÖÐÖ®Ò»µÄ»ñÈ¡Á÷³Ì£¬ÁíÒ»¸ö bean µÄ»ñÈ¡Á÷³Ì¾ÍºÜÀ×ͬÁË¡£ + +ÔÚÉÏÊöÁ÷³ÌÖУ¬»¹ÓÐÁ½¸öÊý¾Ý½á¹¹ÐèҪ˵Ã÷Ï£º + +* `singletonsCurrentlyInCreation`£ºÀàÐÍΪ `SetFromMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry`£¬´´½¨·½Ê½Îª `Collections.newSetFromMap(new ConcurrentHashMap<>(16))`£¬±íÃ÷ÕâÊǸöÓÉ `ConcurrentHashMap` ʵÏÖµÄ set£¬´æ´¢µÄÊÇÕýÔÚ´´½¨ÖеĶÔÏó£¬**Åжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ´´½¨ÖоÍÊÇͨ¹ý²éÕÒµ±Ç°¶ÔÏóÊÇ·ñÔÚÕâ¸ö set ÖÐ**×öµ½µÄ£» +* `earlyProxyReferences`£ºÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `AbstractAutoProxyCreator`£¬´æ´¢µÄÊÇÌáǰ½øÐÐ aop µÄ¶ÔÏó£¬**Èç¹ûÒ»¸ö¶ÔÏóÌáǰ½øÐÐÁË aop£¬ÔÚºóÃæÔÙ´Î aop ʱ£¬»áͨ¹ýÅж϶ÔÏóÊÇ·ñÔÚ `earlyProxyReferences` ÖжøÈ·¶¨Òª²»Òª½øÐÐ aop£¬ÒÔ´ËÀ´±£Ö¤Ã¿¸ö¶ÔÏóÖ»½øÐÐÒ»´Î aop**¡£ + +ÖÁ´Ë£¬spring Ò»¹²ÌṩÁË 5 ¸öÊý¾Ý½á¹¹À´¸¨Öú½â¾öÑ­»·ÒÀÀµÎÊÌ⣬×ܽáÈçÏ£º + +| ½á¹¹ | ˵Ã÷ | +| ------------------------------- | ------------------------------------------------------------ | +| `singletonObjects` | **Ò»¼¶»º´æ**£¬ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇÍêÕûµÄ `spring bean`£¬¼´Íê³ÉÊôÐÔ×¢Èë¡¢³õʼ»¯µÄ bean£¬Èç¹û bean ÐèÒª aop£¬´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó | +| `earlySingletonObjects` | **¶þ¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇʵÀý»¯Íê³É£¬µ«Î´½øÐÐÒÀÀµ×¢ÈëµÄ `bean`£¬**Èç¹û `bean` ÐèÒª `aop`£¬ÕâÀï´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£¬Ö»²»¹ý´úÀí¶ÔÏóËù³ÖÓеÄԭʼ¶ÔÏó²¢Î´½øÐÐÒÀÀµ×¢Èë** | +| `singletonFactories` | **Èý¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ´æ´¢µÄÊÇÒ»¸ö `lambda` ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬`getEarlyBeanReference(xxx)` ÖÐµÄ `bean` ÊǸմ´½¨Íê³ÉµÄ `java bean`£¬Ã»ÓнøÐÐ spring ÒÀÀµ×¢È룬Ҳû½øÐÐ aop | +| `singletonsCurrentlyInCreation` | ÀàÐÍΪ `SetFromMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry`£¬´´½¨·½Ê½Îª `Collections.newSetFromMap(new ConcurrentHashMap<>(16))`£¬±íÃ÷ÕâÊǸöÓÉ `ConcurrentHashMap` ʵÏÖµÄ set£¬´æ´¢µÄÊÇÕýÔÚ´´½¨ÖеĶÔÏ󣬿ÉÒÔ**ÓÃÀ´Åжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ´´½¨ÖÐ** | +| `earlyProxyReferences` | ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `AbstractAutoProxyCreator`£¬´æ´¢µÄÊÇÌáǰ½øÐÐ aop µÄ¶ÔÏ󣬿ÉÒÔ**ÓÃÀ´ÅÐ¶Ï bean ÊÇ·ñ½øÐйý aop£¬±£Ö¤Ã¿¸ö¶ÔÏóÖ»½øÐÐÒ»´Î aop** | + +ÒÔÉϾÍÊÇ spring ½â¾öÑ­»·ÒÀÀµµÄÍêÕûÁ÷³ÌÁË¡£ + +### 3\. ´úÂëÄ£Äâ + +ÔÚÕýʽ·ÖÎöÔ´Âëǰ£¬ÎÒÃÇÊ×ÏÈÄ£ÄâÑ­»·ÏÂÒÀÀµ½â¾öµÄ¹ý³Ì£¬´úÂëÈçÏ£º + +``` +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj2 + */ +public class Obj1 { + // ÐèҪעÈë obj2 + private Obj2 obj2; + + // Ê¡ÂÔÆäËû·½·¨ + ... +} + +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj1 + */ +public class Obj2 { + // ÐèҪעÈë ob1 + private Obj1 obj1; + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj2µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj2µÄ¶ÔÏó + */ +public class ProxyObj2 extends Obj2 { + // obj2´úÀíÀàÄÚ²¿³ÖÓÐobj2µÄԭʼ¶ÔÏó + private Obj2 obj2; + + public ProxyObj2(Obj2 obj2) { + this.obj2 = obj2; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj1µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj1µÄ¶ÔÏó + */ +public class ProxyObj1 extends Obj1 { + // obj2´úÀíÀàÄÚ²¿³ÖÓÐobj1µÄԭʼ¶ÔÏó + private Obj1 obj1; + + public ProxyObj1(Obj1 obj1) { + this.obj1 = obj1; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +``` + +* Ê×ÏÈ×¼±¸ÁËÁ½¸öÀࣺ`Obj1` Óë `Obj2`£¬ ÆäÖÐ `Obj1` ÓиöÊôÐÔΪ `Obj2`£¬`Obj2` ÖÐÓиöÊôÐÔΪ `Obj1`£» +* ½Ó×Å×¼±¸ÁË `Obj1` Óë `Obj2` µÄ´úÀíÀà `ProxyObj1`¡¢`ProxyObj2`£¬²¢ÇÒ `ProxyObj1`¡¢`ProxyObj2` ·Ö±ðÓÐÒ»¸öÊôÐÔ£º`Obj1` ¡¢ `Obj2`£» +* ÎÒÃÇÒÀ¾ÉÒÔ `new ObjX()` Ä£Äâ¶ÔÏóµÄ´´½¨£» +* ÎÒÃÇÒÀ¾ÉÒÔ `objX.setObjX(xxx)` Ä£ÄâÒÀÀµ×¢È룻 +* ÎÒÃÇÒÀ¾ÉÒÔ `new ProxyObjX(xxx)` Ä£Äâ´úÀí¶ÔÏóµÄÉú³É£» +* ÎÒÃÇÒÀ¾ÉÒÔ `collection.add(xxx)` Ä£Äâ¶ÔÏóÌí¼Óµ½ÈÝÆ÷ÖеĹý³Ì£» + +ÎÒÃÇÄ£Äâ×îÖյõ½µÄ½á¹ûΪ£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2` + +×¼±¸¹¤×÷ÒѾ­Íê³ÉÁË£¬½ÓÏÂÀ´ÎÒÃǾͿªÊ¼½øÐÐÄ£ÄâÁË¡£ + +#### 3.1 Ä£Äâ 1 + +ÒªÇó£º + +* Obj1 Óë Obj2 ±ØÐëÑϸñ°´ÕÕ ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ +* Á½¸ö¶ÔÏóµÄ´´½¨Á÷³Ì¿ÉÒÔ½»Ìæ½øÐÐ + +Ä¿±ê£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2` + +´úÂëÈçÏ£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // 1\. ´´½¨ Obj1 ¶ÔÏó + Obj1 obj1 = new Obj1(); + + // ½ÓÏÂÀ´ÐèÒª½«obj2µÄ´úÀí¶ÔÏó×¢Èëµ½obj1ÖУ¬µ«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÇл»µ½obj2µÄ´´½¨Á÷³Ì + // Ò». ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // µ½ÕâÀobj2ÐèҪעÈëobj1µÄ´úÀí¶ÔÏ󣬵«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÓÖÒªÇе½obj1µÄ´´½¨Á÷³Ì + +} + +``` + +ÔÚÖ´ÐÐÒÔÉÏÁ÷³ÌÖÐ £¬·¢ÏÖ´´½¨ Obj2 ¶ÔÏóºó£¬Á÷³Ì¾Í½øÐв»ÏÂÈ¥ÁË£º + +* `obj1` ÐèҪעÈë `obj2` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj2` µÄ´´½¨Á÷³Ì£» +* `obj2` ÐèҪעÈë `obj1` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj1` µÄ´´½¨Á÷³Ì£» +* `obj1` ÐèҪעÈë `obj2` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj2` µÄ´´½¨Á÷³Ì£» +* ... + +Èç´ËÑ­»·Íù¸´¡£ + +Ä£Äâ½á¹û£ºÎ´´ïµ½Ô¤ÆÚÄ¿±ê£¬±¾´ÎÄ£ÄâÐû¸æÊ§°Ü¡£ + +#### 3.1 Ä£Äâ 2 + +ÒªÇó£º + +* Obj1 Óë Obj2 ±ØÐëÒÔÏÂÁ½ÖÖÁ÷³ÌÖ®Ò»´´½¨£º + * ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ + * ¡°´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó --> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ +* Á½¸ö¶ÔÏóµÄ´´½¨Á÷³Ì¿ÉÒÔ½»Ìæ½øÐÐ + +Ä¿±ê£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2`### 1\. ʲôÊÇÑ­»·ÒÀÀµ£¿ + +spring ÔÚÒÀÀµ×¢Èëʱ£¬¿ÉÄÜ»á³öÏÖÏ໥עÈëµÄÇé¿ö£º + +``` +@Service +public class Service1 { + @Autowired + private Service2 service2; + +} + +@Service +public class Service2 { + @Autowired + private Service1 service1; + +} + +``` + +ÈçÒÔÉÏ´úÂ룬ÔÚ `Service1` ÖÐͨ¹ý `@Autowird` ×¢ÈëÁË `Service2`£¬ÔÚ `Service2` ÖÐͨ¹ý `@Autowird` ×¢ÈëÁË `Service1`£¬ÕâÖÖÏ໥עÈëµÄÇé¿ö£¬¾Í½Ð×öÑ­»·ÒÀÀµ¡£ + +### 2\. Ñ­»·ÒÀÀµ»áÓÐʲôÎÊÌâ + +ʵ¼ÊÉÏ£¬ÕâÖÖ `A³ÖÓÐB¶ÔÏó£¬BÒ²³ÖÓÐA¶ÔÏó`µÄÇé¿ö£¬java ´úÂëÊÇÍêȫ֧³ÖµÄ£º + +``` +/** + * ×¼±¸service1 + */ +public class Service1 { + private Service2 service2; + + public void setService2(Service2 service2) { + this.service2 = service2; + } + + public Service2 getService2() { + return this.service2; + } +} + +/** + * ×¼±¸service2 + */ +public class Service2 { + private Service1 service1; + + public void setService1(Service1 service1) { + this.service1 = service1; + } + + public Service1 getService1() { + return this.service1; + } +} + +/** + * Ö÷·½·¨Öе÷Óà + */ +public class Main { + public void main(String[] args) { + // ×¼±¸Á½¸ö¶ÔÏó + Service1 service1 = new Service1(); + Service2 service2 = new Service2(); + // Ï໥ÉèÖà + service1.setService2(service2); + service2.setService1(service1); + } +} + +``` + +ÄÇô£¬ÔÚ spring ÖУ¬Á½¸öÀàÏ໥עÈë¶Ô·½ÊµÀýµÄÇé¿ö£¬»áÓÐʲôÎÊÌâÄØ£¿ÎÒÃÇÀ´¿´ `spring bean` µÄ´´½¨¹ý³Ì£¨**×¢Ò⣺ÕâÀïÎÒÃǽö·ÖÎö `bean` µÄ `scope` Ϊ `singleton` µÄÇé¿ö£¬Ò²¾ÍÊÇ `scope` Ϊ`µ¥Àý`µÄÇé¿ö**£©£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-ca36e17077b1b191834645b3c8e588ff6c4.png) + +Õâ¸ö¹ý³ÌÖÐÓм¸µãÐèҪ˵Ã÷Ï£º + +1. ´´½¨¶ÔÏó£ºÕâ¸öÆäʵ¾ÍÊÇʹÓà jdk ÌṩµÄ·´Éä»úÖÆ´´½¨ java ¶ÔÏó£¬ÒÔµÚ 1 ½ÚÌáµ½µÄ `Service1` ΪÀý£¬¿É¼òµ¥Àí½âΪ `Service1 service = new Service1()`£» +2. ×¢ÈëÒÀÀµ¶ÔÏ󣺻¹ÊÇÒÔµÚ 1 ½ÚÌáµ½µÄ `Service1` ΪÀý£¬`Service1` ÖÐͨ¹ý `@Autowired` ×Ô¶¯×¢Èë `Service2`£¬ÕâÒ»²½¾ÍÊǸø `Service2` ¸³ÖµµÄ¹ý³Ì£¬¿É¼òµ¥Àí½âΪ `service1.setService2(service2)`£» +3. `singletonObjects`£º¾­¹ýÉÏÃæÁ½²½ºó£¬Ò»¸ö java ¶ÔÏó¾Í±ä³ÉÁËÒ»¸ö spring bean£¬È»ºó±£´æµ½ `singletonObjects` ÁË£¬ÕâÊǸö `map`£¬`key` ÊÇ bean µÄÃû³Æ£¬`value` ÊÇ bean£¬ËüÖ»±£´æ `spring bean`£¬²»»áÖ»ÔÚ java ʵÀý¡£ + +ʵ¼ÊÉÏ£¬`java` ¶ÔÏó±ä³É `spring bean`£¬²»½ö½öÖ»ÊÇÒÀÀµ×¢È룬»¹Óгõʼ»¯¡¢Ö´ÐÐ `beanPorcessor` ·½·¨µÈ£¬**ÓÉÓÚ±¾ÎÄÊÇ·ÖÎö `spring bean` µÄÑ­»·ÒÀÀµµÄ£¬Òò´ËÎÒÃÇÖØµã¹Ø×¢ÓëÑ­»·ÒÀÀµÏà¹ØµÄ²½Öè¡£** + +#### 2.1 Ñ­»·ÒÀÀµ²úÉúµÄÎÊÌâ + +Á˽âÁË spring bean µÄ²úÉú¹ý³ÌÖ®ºó£¬½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÏÂÑ­»·ÒÀÀµ²úÉúµÄÎÊÌ⣬ÔÚÕýʽ·ÖÎöǰ£¬ÎÒÃÇÏÈÀ´Ã÷È·Á½¸ö¸ÅÄ + +* `java¶ÔÏó`£ºÊµ¼ÊÉÏ£¬java ÖÐÒ»ÇжÔÏ󶼿ÉÒÔ³ÆÖ®Îª `java` ¶ÔÏó£¬ÎªÁË˵Ã÷·½±ã£¬ÒÔÏÂÌáµ½µÄ `java¶ÔÏó`½öָʵÀý»¯Íê³É¡¢µ«Î´½øÐÐ spring bean µÄÉúÃüÖÜÆÚ¶ÔÏó£» +* `spring bean`£ºÊÇÒ»¸ö java ¶ÔÏ󣬲¢ÇÒ½øÐÐÁËÍêÕûµÄ spring bean µÄÉúÃüÖÜÆÚ¶ÔÏó£» + +spring bean µÄ´´½¨¹ý³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b55a211447b5fabeaa0c3ef0bfee0920c82.png) + +¶ÔÉÏͼ˵Ã÷ÈçÏ£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºó¾ÍÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬´ËʱÊÇÕÒ²»µ½ `service2`£¬È»ºó¾Í¿ªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +2. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `singletonObjects` ÖвéÕÒ `service1`£¬´ËʱÊÇÕÒ²»µ½ `service1`£¬ÒòΪµÚÒ»²½ÖÐ `service1` ²¢Ã»Óд´½¨³É¹¦ £¬È»ºó¾Í¿ªÊ¼ÁË `service1` µÄ´´½¨¹ý³Ì£» +3. Á÷³ÌÌø»Øµ½ `1`£¬ÔٴοªÊ¼ÁË `service1` µÄ´´½¨¡¢ÊôÐÔ×¢Èë¹ý³Ì¡£ + +µ½ÕâÀÎÒÃǾªÏ²µØ·¢ÏÖ£¬Ñ­»·³öÏÖÁË£¡ + +#### 2.2 ÒýÈë `earlySingletonObjects` ½â¾öÑ­»·ÒÀÀµ + +ÎÒÃÇ·ÖÎöÏ£¬Ñ­»·³öÏÖµÄÔ­ÒòÔÚÓÚ£¬ÔÚ `service2` »ñÈ¡ `service1` ʱ£¬ÓÉÓÚ `singletonObjects` ÖдËʱ²¢²»´æÔÚ `service1`£¬Òò´Ë»áÔÙ×ß `service1` µÄ´´½¨¹ý³Ì£¬ÖØÐ´´½¨ `service1`£¬Òò´Ë£¬ÎÒÃÇÓиö´óµ¨µÄÏë·¨£ºÈç¹ûÔÚ `service1` ʵÀý»¯ºó¾Í°ÑËü±£´æÆðÀ´£¬ºóÃæÔÙÔÙÕÒ `service1` ʱ£¬¾Í·µ»ØÕâ¸öδ½øÐÐÒÀÀµ×¢ÈëµÄ `service1`£¬ÏñÏÂÃæÕâÑù£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-c0eaecbe82b144a6fcd9fd048f2cca53497.png) + +ÉÏͼÖУ¬ÒýÈëÁË `earlySingletonObjects`£¬ÕâÒ²ÊǸö map£¬Í¬ `singletonObjects` Ò»Ñù£¬`key` ÊÇ bean µÄÃû³Æ£¬`value` ÊÇÒ»¸öδÍê³ÉÒÀÀµ×¢ÈëµÄ¶ÔÏó¡£ + +¶ÔÉÏͼ˵Ã÷ÈçÏ£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service1` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +2. ¶Ô `service1` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºóÏÈÈ¥ `earlySingletonObjects` ²éÕÒ `service2`£¬Î´ÕÒµ½£»ÔÙÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬»¹ÊÇδÕÒµ½£¬ÓÚÊǾͿªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +3. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service2` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +4. ¶Ô `service2` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `earlySingletonObjects` ²éÕÒ `service1`£¬ÕÒµ½ÁË£¬¾Í½« `service1` ×¢Èëµ½ `service2` ÖУ¬´Ëʱ `service2` ¾ÍÊÇÒ»¸ö `spring bean` ÁË£¬½«Æä±£´æµ½ `singletonObjects` ÖУ» +5. ¾­¹ýµÚ 4 ²½ºó£¬ÎÒÃǵõ½ÁË `service2`£¬È»ºó½«Æä×¢Èëµ½ `service1` ÖУ¬´Ëʱ `service1` Ò²³ÉÁËÒ»¸ö `spring bean`£¬½«Æä±£´æµ½ `singletonObjects` ÖС£ + +¾­¹ýÒÔÉϲ½Ö裬ÎÒÃÇ·¢ÏÖ£¬Ñ­»·ÒÀÀµµÃµ½Á˽â¾ö¡£ + +#### 2.2 aop ϵÄÑ­»·ÒÀÀµ + +¾­¹ýÉÏÃæµÄ·ÖÎö£¬ÎÒÃÇ·¢ÏÖÖ»Òª¶îÍâÒýÈëÒ»¸ö `earlySingletonObjects` ºó£¬Ñ­»·ÒÀÀµ¾ÍÄܵõ½½â¾ö¡£µ«ÊÇ£¬Ñ­»·ÒÀÀµÕæµÄµÃµ½Á˽â¾öÂð£¿spring ³ýÁË ioc Í⣬»¹ÓÐÁíÒ»¸öÖØ´ó¹¦ÄÜ£ºaop£¬ÎÒÃÇÀ´¿´¿´ aop Çé¿öϳöÏÖÑ­»·ÒÀÀµ»áÔõÑù¡£ + +##### 1\. aop ¶ÔÏóµÄ´´½¨¹ý³Ì + +ÔÚÕýʽ½éÉÜ aop ϵÄÑ­»·ÒÀÀµÇ°£¬ÎÒÃÇÏÈÀ´Ã÷È·Á½¸ö¸ö¸ÅÄ + +* `ԭʼ¶ÔÏó`£ºÇø±ðÓÚ´úÀí¶ÔÏó£¬Ö¸Î´½øÐйý aop µÄ¶ÔÏ󣬿ÉÒÔÊÇ java ¶ÔÏó£¬Ò²¿ÉÒÔÊÇδ½øÐÐ aop µÄ spring bean£» +* `´úÀí¶ÔÏó`£º½øÐйý aop µÄ¶ÔÏ󣬿ÉÒÔÊÇ java ¶ÔÏó½ö½øÐйý aop µÃµ½µÄ¶ÔÏó (½ö½øÐйý aop£¬Î´½øÐÐÒÀÀµ×¢È룬Ҳδ½øÐгõʼ»¯)£¬Ò²¿ÉÒÔÊǽøÐйý aop µÄ `spring bean`. + +ÎÒÃÇÏÈÀ´¿´¿´ aop ÊÇÈçºÎ´´½¨¶ÔÏóµÄ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-e92991c1c173bbd5579be3001a977555be7.png) + +Ïà±ÈÓÚ `2.1` ÖеÄÁ÷³Ì£¬aop ¶àÁË "Éú³É´úÀí¶ÔÏó" µÄ²Ù×÷£¬²¢ÇÒ×îÖÕ±£´æµ½ `singletonObjects` ÖеĶÔÏóÒ²ÊÇ´úÀí¶ÔÏó¡£ + +ԭʼ¶ÔÏóÓë´úÀí¶ÔÏóÖ®¼äÊÇʲô¹ØÏµÄØ£¿ÓôúÂëʾÒâÏ£¬´óÖÂÈçÏ£º + +``` +public class ProxyObj extends Obj { + + // ԭʼ¶ÔÏó + private Obj obj; + + ... +} + +``` + +ʵ¼ÊÉÏ£¬Á½ÕßÖ®¼äµÄ¹ØÏµ²¢Ã»ÓÐÕâô¼òµ¥£¬µ«ÎªÁË˵Ã÷ÎÊÌ⣬ÕâÀï¶ÔÁ½Õß¹ØÏµ×öÁ˼ò»¯£¬Ð¡»ï°éÃÇÖ»ÐèÒªÃ÷°×£¬**´úÀí¶ÔÏó³ÖÓÐԭʼ¶ÔÏóµÄÒýÓÃ**¼´¿É¡£ + +¹ØÓÚԭʼ¶ÔÏóÈçºÎ±ä³É´úÀí¶ÔÏóµÄ£¬¿ÉÒԲο¼ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)¡£ + +¶ÔÒÔÉÏ´´½¨¹ý³Ì£¬Óà java ´úÂëÄ£ÄâÈçÏ£º + +``` +/** + * ×¼±¸Ò»¸öÀà + */ +public class Obj1 { + +} + +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj1 + */ +public class Obj2 { + + private Obj1 obj1; + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj2µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj2µÄ¶ÔÏó + */ +public class ProxyObj2 extends Obj2 { + + private Obj2 obj2; + + public ProxyObj2(Obj2 obj2) { + this.obj2 = obj2; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +``` + +½Ó×Å£¬¾ÍÊÇÄ£Äâ ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄ Á÷³ÌÁË£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // ¿ªÊ¼ Obj2 µÄ´´½¨Á÷³Ì + // 1\. ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. Íù Obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐobj1£¬Òò´ËÏÈÒª´´½¨obj1£¬ÔÙ½«Æä×¢Èëµ½Obj2ÖÐ + Obj1 obj1 = new Obj1(); + obj2.setObj1(obj1); + + // 3\. Éú³ÉObj2µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2µÄԭʼ¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // 4\. proxyObj2ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + +} + +``` + +ÉÏÊö´úÂëÖУ¬ + +* ÒÔ `new Obj2()` Ä£Äâ¶ÔÏóµÄ´´½¨ +* ÒÔ `obj2.setObj1(xxx)` Ä£ÄâÒÀÀµ×¢Èë +* ÒÔ `new ProxyObj2(xxx)` Ä£Äâ´úÀí¶ÔÏóµÄÉú³É +* ÒÔ `collection.add(xxx)` Ä£Äâ¶ÔÏóÌí¼Óµ½ÈÝÆ÷ÖеĹý³Ì + +Ä£ÄâµÄÁ÷³ÌÈçÏ£º + +1. ´´½¨ `obj2` ¶ÔÏó +2. Íù `Obj2` ÖÐ×¢Èë `obj1`£¬µ«´Ëʱ²¢Ã»ÓÐ `obj1`£¬Òò´ËÏÈÒª´´½¨ `obj1`£¬ÔÙ½«Æä×¢Èëµ½ `Obj2` ÖÐ +3. Éú³É `Obj2` µÄ´úÀí¶ÔÏó `proxyObj2`£¬`proxyObj2` ÖгÖÓÐ `Obj2` µÄԭʼ¶ÔÏó +4. `proxyObj2` ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + +×Ðϸ·ÖÎöÉÏÃæµÄ²½Ö裬¾Í»á·¢ÏÖ£¬ÉÏÃæµÄµÚ 2 ²½ÓëµÚ 3 ²½ÍêÈ«µ÷»»Ë³ÐòҲûÎÊÌ⣬´úÂëÄ£ÄâÈçÏ£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // ¿ªÊ¼ Obj2 µÄ´´½¨Á÷³Ì + // 1\. ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. Éú³ÉObj2µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2µÄԭʼ¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // 3\. Íù obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐobj1£¬Òò´ËÏÈÒª´´½¨obj1£¬ÔÙ½«Æä×¢Èëµ½Obj2ÖÐ + Obj1 obj1 = new Obj1(); + // ÕâÀïÊÇ×¢È뵽ԭʼ¶ÔÏóÖÐ + obj2.setObj1(obj1); + + // 4\. proxyObj2ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + +} + +``` + +ÉÏÊö´úÂëµÄÁ÷³ÌÈçÏ£º + +1. ´´½¨ obj2 ¶ÔÏó +2. Éú³É Obj2 µÄ´úÀí¶ÔÏ󣬴úÀí¶ÔÏóÖгÖÓÐ Obj2 µÄԭʼ¶ÔÏó +3. Íù Obj2 ÖÐ×¢Èë obj1£¬µ«´Ëʱ²¢Ã»ÓÐ obj1£¬Òò´ËÏÈÒª´´½¨ obj1£¬ÔÙ½«Æä×¢Èëµ½ Obj2 +4. proxyObj2 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬Òò´Ë½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + +´Ó´úÂëÉÏ¿´£¬`proxyObj2(´úÀí¶ÔÏó)` ÖгÖÓÐ `ob2(ԭʼ¶ÔÏó)`£¬Éú³É´úÀí¶ÔÏóºó£¬¼ÌÐø¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢È룬ÒÀÈ»ÄÜÓ°Ïì´úÀí¶ÔÏó£¬×îÖÕ´úÀí¶ÔÏó³ÖÓеÄԭʼ¶ÔÏóÒ²Íê³ÉÁËÒÀÀµ×¢È룬Õû¸ö¹ý³ÌÓÃͼÐÎʾÒâÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8ff5579425f73dd5c2d321a86d0303390ac.png) + +ÕâÀïÎÒÃÇÔÙ´ÎÉêÃ÷£¬´Ó java ¶ÔÏóµ½ spring bean µÄ²½ÖèÓкö࣬ÕâÀïÎÒÃǽö¹Ø×¢ÓëÑ­»·ÒÀÀµÏà¹ØµÄ²½Ö裬Èç¹ûÏëÁ˽â spring bean ÏêϸµÄ³õʼ»¯¹ý³Ì£¬¿É²é¿´ [spring Æô¶¯Á÷³ÌÖ®Æô¶¯Á÷³Ì¸ÅÀÀ](https://my.oschina.net/funcy/blog/4597493)¡£ + +µ½ÕâÀÎÒÃÇ̽Ë÷µ½´úÀí¶ÔÏóµÄÉúÃüÖÜÆÚ¿ÉÒÔÓÐÁ½ÖÖ£º + +* ´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖÐ +* ´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó (Ìáǰ½øÐÐ aop)--> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖÐ + +ÕâÁ½ÖÖ¶¼ÄÜ´ïµ½×îÖÕÄ¿µÄ£¬¼´**±£´æµ½ÈÝÆ÷ÖеÄÊÇ´úÀí¶ÔÏó£¬ÇÒ´úÀí¶ÔÏó¶ÔÓ¦µÄԭʼ¶ÔÏóÍê³ÉÁËÒÀÀµ×¢Èë**¡£ÇëÀμÇÕâÁ½¸ö´´½¨Á÷³Ì£¬ÕâÊǺóÃæ½â¾ö aop ÏÂÑ­»·ÒÀÀµÎÊÌâµÄºËÐÄ£¬Ëµ°×ÁË£¬**aop ϵÄÑ­»·ÒÀÀµÎÊÌâÖ®ËùÒÔÄܽâ¾ö£¬¾ÍÊÇÒòΪ¶ÔÏó¿ÉÒÔÌáǰ½øÐÐ aop ²Ù×÷**¡£ + +##### 2\. ΪʲôÓà `earlySingletonObjects` ÎÞ·¨½â¾öÑ­»·ÒÀÀµ£¿ + +Ç°ÃæÎÒÃÇÖ÷Ҫ˵Ã÷ÁË´úÀí¶ÔÏóµÄ´´½¨¹ý³Ì£¬½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ÔÚ aop Ï£¬Ê¹Óà `earlySingletonObjects` À´½â¾öÑ­»·ÒÀÀµÓÐʲôÎÊÌ⣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d3d00d76ab0c72339faccb7ccd853723d6c.png) + +ÎÒÃÇÀ´·ÖÎöÉÏͼµÄÁ÷³Ì£º + +1. ÔÚ `service1` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service1` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +2. ¶Ô `service1` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service1` ÐèҪעÈë `service2`£¬È»ºóÏÈÈ¥ `earlySingletonObjects` ²éÕÒ `service2`£¬Î´ÕÒµ½£»ÔÙÈ¥ `singletonObjects` ÖвéÕÒ `service2`£¬»¹ÊÇδÕÒµ½£¬ÓÚÊǾͿªÊ¼ÁË `service2` µÄ´´½¨¹ý³Ì£» +3. ÔÚ `service2` ¶ÔÏó´´½¨Íê³Éºó£¬ÏȽ« `service2` ·ÅÈë `earlySingletonObjects`£¬È»ºó½øÐÐÒÀÀµ×¢È룻 +4. ¶Ô `service2` ½øÐÐÒÀÀµ×¢Èëʱ£¬`spring` ·¢ÏÖ `service2` ÐèҪעÈë `service1`£¬È»ºó¾ÍÈ¥ `earlySingletonObjects` ²éÕÒ `service1`£¬ÕÒµ½ÁË£¬¾Í½« `service1` ×¢Èëµ½ `service2` ÖУ¬È»ºóÔÙ½øÐÐ aop£¬´Ëʱ `service2` ÊÇÒ»¸ö´úÀí¶ÔÏ󣬽«Æä±£´æµ½ `singletonObjects` ÖУ» +5. ¾­¹ýµÚ 4 ²½ºó£¬ÎÒÃǵõ½ÁË `service2` µÄ´úÀí¶ÔÏó£¬È»ºó½«Æä×¢Èëµ½ `service1` ÖУ¬½Ó×ÅÔÙ¶Ô `service1` ½øÐÐ aop£¬´Ëʱ `service1` Ò²³ÉÁËÒ»¸ö `spring bean`£¬½«Æä±£´æµ½ `singletonObjects` ÖС£ + +ÉÏÊö²½ÖèÓÐʲôÎÊÌâÄØ£¿×Ðϸ¿´µÚ 4 ²½£¬¾Í»á·¢ÏÖ£¬**×¢Èëµ½ `service2` µÄ `service1` ²¢²»ÊÇ´úÀí¶ÔÏó**£¡×ݹÛÈ«¾Ö£¬×îÖյõ½µÄ `service1` Óë `service2` ¶¼ÊÇ´úÀí¶ÔÏó£¬×¢Èëµ½ `service2` µÄ `service1` Ó¦¸ÃÒ²ÊÇ´úÀí¶ÔÏó²Å¶Ô¡£Òò´Ë£¬ÔÚ aop Ï£¬Ñ­»·ÒÀÀµµÄÎÊÌâÓÖ³öÏÖÁË£¡ + +#### 2.3 spring µÄ½â¾ö·½°¸ + +Ç°ÃæÎÒÃÇÌáµ½£¬ÔÚ aop Ï£¬ÒýÈë `earlySingletonObjects` ²¢²»Äܽâ¾öÑ­»·ÒÀÀµµÄÎÊÌ⣬ÄÇ spring ÊÇÔõô½â¾öµÄÄØ£¿spring ÔÙ´ÎÒýÈëÁËÒ»¸ö `map` À´½â¾öÕâ¸öÎÊÌ⣬ÕâÒ²ÊÇÈËÃdz£ËµµÄ **spring Èý¼¶»º´æ**£¬¶ÔÕâÈý¸ö `map` ˵Ã÷ÈçÏ£º + +* Ò»¼¶»º´æ `singletonObjects`£ºÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇÍêÕûµÄ `spring bean`£¬¼´Íê³ÉÊôÐÔ×¢Èë¡¢³õʼ»¯µÄ bean£¬Èç¹û bean ÐèÒª aop£¬´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£» +* ¶þ¼¶»º´æ `earlySingletonObjects`£ºÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇʵÀý»¯Íê³É£¬µ«Î´½øÐÐÒÀÀµ×¢ÈëµÄ `bean`£¬Èç¹û `bean` ÐèÒª `aop`£¬ÕâÀï´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£¬Ö»²»¹ý´úÀí¶ÔÏóËù³ÖÓеÄԭʼ¶ÔÏó²¢Î´½øÐÐÒÀÀµ×¢È룻 +* Èý¼¶»º´æ `singletonFactories`£ºÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ´æ´¢µÄÊÇÒ»¸ö `lambda` ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬ `getEarlyBeanReference` ÖÐµÄ `bean` ÊǸմ´½¨Íê³ÉµÄ `java bean`£¬Ã»ÓнøÐÐ spring ÒÀÀµ×¢È룬Ҳû½øÐÐ aop (¹ØÓÚÕâ¸ö `lambda` ±í´ïʽ£¬ºóÃæ»á¼ÌÐø·ÖÎö)¡£ + +ΪÁË˵Ã÷·½±ã£¬ÏÂÃæ¶Ô `singletonObjects`¡¢`earlySingletonObjects` ºÍ `singletonFactories` ·Ö±ð³ÆÎª**Ò»¼¶»º´æ**¡¢**¶þ¼¶»º´æ**ºÍ**Èý¼¶»º´æ**¡£ + +spring ½â¾ö aop ϵÄÑ­»·ÒÀÀµÁ÷³ÌÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-dc325a87b321e4c246a1b2f14169821a75a.png) + +Õâ¸öͼ¿´×űȽϸ´ÔÓ£¬Æäʵ·Ö¿ªÀ´¿´¾Í±È½Ï¼òµ¥ÁË£¬ÉÏÊö²Ù×÷ÖУ¬`1~8` ÊÇ»ñÈ¡ `service1` µÄÁ÷³Ì£¬`5.1~5.8` ÊÇ»ñÈ¡ `service2` µÄÁ÷³Ì£¬`5.5.1` ÊÇÔٴλñÈ¡ `service1` µÄÁ÷³Ì£¬Ö»²»¹ýÔÚ´¦Àí `service1` µÄ³õʼ»¯¹ý³ÌÖУ¬»á´¥·¢ `service2` µÄ³õʼ»¯¹ý³Ì£¬¶ø `service2` µÄ³õʼ»¯Ê±£¬ÓÖ»áÒÀÀµµ½ `service1`£¬Òò´Ë²Å¿´×ÅÏñÊÇÁ¬ÔÚÒ»Æð£¬±È½Ï¸´ÔÓ¡£ + +¶ÔÉÏͼµÄ¹ý³Ì£¬ÕâÀï˵Ã÷ÈçÏ£¨½¨Ò飺Èç¹û¾õµÃÁ÷³Ì±È½Ï¸´ÔÓ£¬¿ÉÒÔÏÈ¿´ `1~8` µÄ²Ù×÷£¬ÔÙ¿´ `5.1~5.8` µÄ²Ù×÷£¬×îºóÁ½ÕßÁªºÏÆðÀ´¿´£¬ÕâÑù»áÇåÎúºÜ¶à£©£º + +* 1. `service1`£º»ñÈ¡ `service1`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬´ËʱÊÇ»ñÈ¡²»µ½µÄ£» +* 1. `service1`£º´´½¨ `service1` µÄʵÀý£» +* 1. `service1`£º»ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¨ÔÚԭʼ¶ÔÏóÉϽøÐлñÈ¡£©£» +* 1. `service1`£ºÈç¹û¿ªÆôÁËÖ§³ÖÑ­»·ÒÀÀµµÄÅäÖ㬾ͽ« `service1` ·Åµ½Èý¼¶»º´æÖУ¨ÊÇ·ñÖ§³ÖÑ­»·ÒÀÀµ£¬ÊÇ¿ÉÒÔÅäÖõģ©£» +* 1. `service1`£º¶Ô `service1` ½øÐÐÒÀÀµ×¢È룬ÐèÒª `service2`£¬È»ºó¾Í¿ªÊ¼ÁË `service2` µÄ»ñÈ¡Á÷³Ì£» +* 5.1 `service2`£º»ñÈ¡ `service2`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬´ËʱÊÇ»ñÈ¡²»µ½µÄ£» +* 5.2 `service2`£º´´½¨ `service2` µÄʵÀý£» +* 5.3 `service2`£º»ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¨ÔÚԭʼ¶ÔÏóÉϽøÐлñÈ¡£©£» +* 5.4 `service2`£ºÈç¹û¿ªÆôÁËÖ§³ÖÑ­»·ÒÀÀµµÄÅäÖ㬾ͽ« `service2` ·Åµ½Èý¼¶»º´æÖУ¨ÊÇ·ñÖ§³ÖÑ­»·ÒÀÀµ£¬ÊÇ¿ÉÒÔÅäÖõģ©£» +* 5.5 `service2`£º¶Ô `service2` ½øÐÐÒÀÀµ×¢È룬ÐèÒª `service1`£¬È»ºó¾Í¿ªÊ¼ÁË `service1` µÄ»ñÈ¡Á÷³Ì£» +* 5.5.1 `service1`: »ñÈ¡ `service1`£¬´ÓÒ»¼¶»º´æÖлñÈ¡£¬»ñÈ¡²»µ½£»´Ëʱ·¢ÏÖ `service1` ÕýÔÚ´´½¨ÖУ¬ÓÚÊǼÌÐø´Ó¶þ¡¢Èý¼¶»º´æÖлñÈ¡£¬×îÖÕ´ÓÈý¼¶»º´æÖлñÈ¡µ½ÁË£¬½«Æä·ÅÈë¶þ¼¶»º´æ¡£´ÓÈý¼¶»º´æ»ñÈ¡µÄ¹ý³ÌÖУ¬**»áÅÐ¶Ï `service1` ÊÇ·ñÐèÒª½øÐÐ aop£¬È»ºó¿ªÊ¼ aop ²Ù×÷**£¬Òò´Ë·ÅÈë¶þ¼¶»º´æÖеÄÊÇ `service1` ´úÀí´úÀí£¬Ìáǰ½øÐÐ aop Êǽâ¾öÑ­»·ÒÀÀµµÄ¹Ø¼ü£» +* 5.6 `service2`£ºµÃµ½ÁË `service1` ºó£¨ÕâÀïµÄ `service1` ÊÇ´úÀí¶ÔÏ󣩣¬½«Æä×¢Èëµ½ `service2` ÖУ¬½Ó×Å¶Ô `service2` ½øÐÐ aop£¬µÃµ½ `service2` µÄ´úÀí¶ÔÏó£» +* 5.7 `service2`£ºÈç¹ûÖ§³ÖÑ­»·ÒÀÀµ£¬ÏÈ´ÓÒ»¡¢¶þ¼¶»º´æÖÐÔٴλñÈ¡ `service2`£¬¶¼Î´»ñÈ¡µ½£¬¾ÍʹÓõ±Ç° `service2`£¨µ±Ç° `service2` ÊÇ´úÀí¶ÔÏó)£» +* 5.8 `service2`£º½« service2 µÄ´úÀí¶ÔÏó·ÅÈëÒ»¼¶»º´æÖУ¬É¾³ý¶þ¡¢Èý¼¶»º´æ£¬ÖÁ´Ë£¬`service2` ³õʼ»¯Íê³É£¬×¢ÈëµÄ `service1` ÊÇ´úÀí¶ÔÏó£¬Ò»¼¶»º´æÖÐµÄ `service2` Ò²ÊÇ´úÀí¶ÔÏó£» +* 1. `service1`£º»Øµ½ `service1` µÄÉúÃüÖÜÆÚ£¬Äõ½ `service2`£¨ÕâÀïµÄ `service2` ÊÇ´úÀí¶ÔÏ󣩺󣬽«Æä×¢Èëµ½ `service1`£¬`service1` µÄÒÀÀµ×¢ÈëÍê³É£¬½øÐгõʼ»¯£¬ÕâÀï»áÅÐ¶Ï `service1` ÊÇ·ñÐèÒª½øÐÐ aop£¬ËäÈ» `service1` ÊÇÐèÒª½øÐÐ aop µÄ£¬µ«ÓÉÓÚÔÚ `5.5.1` ÒѾ­½øÐйý aop ÁË£¬Òò´Ë£¬ÕâÀïÖ±½Ó·µ»Ø£¨µ½ÕâÒ»²½£¬`service1` »¹ÊÇԭʼ¶ÔÏ󣩣» +* 1. `service1`£ºÈç¹ûÖ§³ÖÑ­»·ÒÀÀµ£¬ÏÈ´ÓÒ»¼¶»º´æÖлñÈ¡ `service1`£¬»ñÈ¡²»µ½£»ÔÙ´Ó¶þ»º´æÖлñÈ¡ `service1`£¬¿ÉÒÔ»ñÈ¡µ½£¨´Ó `5.5.1` ¿ÉÖª£¬¶þ¼¶»º´æÀïÊÇ `service1` ´úÀí¶ÔÏ󣩣¬·µ»Ø£» +* 1. `service1`£º½«¶þ¼¶»º´æÖлñÈ¡µÄ¶ÔÏó×¢²áµ½Ò»¼¶»º´æÖУ¬É¾³ý¶þ¡¢Èý¼¶»º´æ£¬ÖÁ´Ë£¬`service1` ³õʼ»¯Íê³É£¬×¢ÈëµÄ `service2` ÊÇ´úÀí¶ÔÏó£¬Ò»¼¶»º´æÖÐµÄ `service1` Ò²ÊÇ´úÀí¶ÔÏó¡£ + +ÒÔÉÏÁ÷³Ì£¬ËäÈ»²½Öè½Ï¶à£¬µ« `service1` Óë `service2` µÄ»ñÈ¡²½ÖèÊÇÏàͬµÄ£¬Ö»ÒªÅªÇåÁËÆäÖÐÖ®Ò»µÄ»ñÈ¡Á÷³Ì£¬ÁíÒ»¸ö bean µÄ»ñÈ¡Á÷³Ì¾ÍºÜÀ×ͬÁË¡£ + +ÔÚÉÏÊöÁ÷³ÌÖУ¬»¹ÓÐÁ½¸öÊý¾Ý½á¹¹ÐèҪ˵Ã÷Ï£º + +* `singletonsCurrentlyInCreation`£ºÀàÐÍΪ `SetFromMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry`£¬´´½¨·½Ê½Îª `Collections.newSetFromMap(new ConcurrentHashMap<>(16))`£¬±íÃ÷ÕâÊǸöÓÉ `ConcurrentHashMap` ʵÏÖµÄ set£¬´æ´¢µÄÊÇÕýÔÚ´´½¨ÖеĶÔÏó£¬**Åжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ´´½¨ÖоÍÊÇͨ¹ý²éÕÒµ±Ç°¶ÔÏóÊÇ·ñÔÚÕâ¸ö set ÖÐ**×öµ½µÄ£» +* `earlyProxyReferences`£ºÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `AbstractAutoProxyCreator`£¬´æ´¢µÄÊÇÌáǰ½øÐÐ aop µÄ¶ÔÏó£¬**Èç¹ûÒ»¸ö¶ÔÏóÌáǰ½øÐÐÁË aop£¬ÔÚºóÃæÔÙ´Î aop ʱ£¬»áͨ¹ýÅж϶ÔÏóÊÇ·ñÔÚ `earlyProxyReferences` ÖжøÈ·¶¨Òª²»Òª½øÐÐ aop£¬ÒÔ´ËÀ´±£Ö¤Ã¿¸ö¶ÔÏóÖ»½øÐÐÒ»´Î aop**¡£ + +ÖÁ´Ë£¬spring Ò»¹²ÌṩÁË 5 ¸öÊý¾Ý½á¹¹À´¸¨Öú½â¾öÑ­»·ÒÀÀµÎÊÌ⣬×ܽáÈçÏ£º + +| ½á¹¹ | ˵Ã÷ | +| ------------------------------- | ------------------------------------------------------------ | +| `singletonObjects` | **Ò»¼¶»º´æ**£¬ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇÍêÕûµÄ `spring bean`£¬¼´Íê³ÉÊôÐÔ×¢Èë¡¢³õʼ»¯µÄ bean£¬Èç¹û bean ÐèÒª aop£¬´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó | +| `earlySingletonObjects` | **¶þ¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇʵÀý»¯Íê³É£¬µ«Î´½øÐÐÒÀÀµ×¢ÈëµÄ `bean`£¬**Èç¹û `bean` ÐèÒª `aop`£¬ÕâÀï´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£¬Ö»²»¹ý´úÀí¶ÔÏóËù³ÖÓеÄԭʼ¶ÔÏó²¢Î´½øÐÐÒÀÀµ×¢Èë** | +| `singletonFactories` | **Èý¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ´æ´¢µÄÊÇÒ»¸ö `lambda` ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬`getEarlyBeanReference(xxx)` ÖÐµÄ `bean` ÊǸմ´½¨Íê³ÉµÄ `java bean`£¬Ã»ÓнøÐÐ spring ÒÀÀµ×¢È룬Ҳû½øÐÐ aop | +| `singletonsCurrentlyInCreation` | ÀàÐÍΪ `SetFromMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry`£¬´´½¨·½Ê½Îª `Collections.newSetFromMap(new ConcurrentHashMap<>(16))`£¬±íÃ÷ÕâÊǸöÓÉ `ConcurrentHashMap` ʵÏÖµÄ set£¬´æ´¢µÄÊÇÕýÔÚ´´½¨ÖеĶÔÏ󣬿ÉÒÔ**ÓÃÀ´Åжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ´´½¨ÖÐ** | +| `earlyProxyReferences` | ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `AbstractAutoProxyCreator`£¬´æ´¢µÄÊÇÌáǰ½øÐÐ aop µÄ¶ÔÏ󣬿ÉÒÔ**ÓÃÀ´ÅÐ¶Ï bean ÊÇ·ñ½øÐйý aop£¬±£Ö¤Ã¿¸ö¶ÔÏóÖ»½øÐÐÒ»´Î aop** | + +ÒÔÉϾÍÊÇ spring ½â¾öÑ­»·ÒÀÀµµÄÍêÕûÁ÷³ÌÁË¡£ + +### 3\. ´úÂëÄ£Äâ + +ÔÚÕýʽ·ÖÎöÔ´Âëǰ£¬ÎÒÃÇÊ×ÏÈÄ£ÄâÑ­»·ÏÂÒÀÀµ½â¾öµÄ¹ý³Ì£¬´úÂëÈçÏ£º + +``` +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj2 + */ +public class Obj1 { + // ÐèҪעÈë obj2 + private Obj2 obj2; + + // Ê¡ÂÔÆäËû·½·¨ + ... +} + +/** + * ×¼±¸Ò»¸öÀ࣬ÄÚ²¿ÓÐÒ»¸öÊôÐÔ Obj1 + */ +public class Obj2 { + // ÐèҪעÈë ob1 + private Obj1 obj1; + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj2µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj2µÄ¶ÔÏó + */ +public class ProxyObj2 extends Obj2 { + // obj2´úÀíÀàÄÚ²¿³ÖÓÐobj2µÄԭʼ¶ÔÏó + private Obj2 obj2; + + public ProxyObj2(Obj2 obj2) { + this.obj2 = obj2; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +/** + * ×¼±¸Obj1µÄ´úÀíÀ࣬ÄÚ²¿³ÖÓÐobj1µÄ¶ÔÏó + */ +public class ProxyObj1 extends Obj1 { + // obj2´úÀíÀàÄÚ²¿³ÖÓÐobj1µÄԭʼ¶ÔÏó + private Obj1 obj1; + + public ProxyObj1(Obj1 obj1) { + this.obj1 = obj1; + } + + // Ê¡ÂÔÆäËû·½·¨ + ... + +} + +``` + +* Ê×ÏÈ×¼±¸ÁËÁ½¸öÀࣺ`Obj1` Óë `Obj2`£¬ ÆäÖÐ `Obj1` ÓиöÊôÐÔΪ `Obj2`£¬`Obj2` ÖÐÓиöÊôÐÔΪ `Obj1`£» +* ½Ó×Å×¼±¸ÁË `Obj1` Óë `Obj2` µÄ´úÀíÀà `ProxyObj1`¡¢`ProxyObj2`£¬²¢ÇÒ `ProxyObj1`¡¢`ProxyObj2` ·Ö±ðÓÐÒ»¸öÊôÐÔ£º`Obj1` ¡¢ `Obj2`£» +* ÎÒÃÇÒÀ¾ÉÒÔ `new ObjX()` Ä£Äâ¶ÔÏóµÄ´´½¨£» +* ÎÒÃÇÒÀ¾ÉÒÔ `objX.setObjX(xxx)` Ä£ÄâÒÀÀµ×¢È룻 +* ÎÒÃÇÒÀ¾ÉÒÔ `new ProxyObjX(xxx)` Ä£Äâ´úÀí¶ÔÏóµÄÉú³É£» +* ÎÒÃÇÒÀ¾ÉÒÔ `collection.add(xxx)` Ä£Äâ¶ÔÏóÌí¼Óµ½ÈÝÆ÷ÖеĹý³Ì£» + +ÎÒÃÇÄ£Äâ×îÖյõ½µÄ½á¹ûΪ£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2` + +×¼±¸¹¤×÷ÒѾ­Íê³ÉÁË£¬½ÓÏÂÀ´ÎÒÃǾͿªÊ¼½øÐÐÄ£ÄâÁË¡£ + +#### 3.1 Ä£Äâ 1 + +ÒªÇó£º + +* Obj1 Óë Obj2 ±ØÐëÑϸñ°´ÕÕ ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ +* Á½¸ö¶ÔÏóµÄ´´½¨Á÷³Ì¿ÉÒÔ½»Ìæ½øÐÐ + +Ä¿±ê£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2` + +´úÂëÈçÏ£º + +``` +public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // 1\. ´´½¨ Obj1 ¶ÔÏó + Obj1 obj1 = new Obj1(); + + // ½ÓÏÂÀ´ÐèÒª½«obj2µÄ´úÀí¶ÔÏó×¢Èëµ½obj1ÖУ¬µ«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÇл»µ½obj2µÄ´´½¨Á÷³Ì + // Ò». ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // µ½ÕâÀobj2ÐèҪעÈëobj1µÄ´úÀí¶ÔÏ󣬵«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÓÖÒªÇе½obj1µÄ´´½¨Á÷³Ì + +} + +``` + +ÔÚÖ´ÐÐÒÔÉÏÁ÷³ÌÖÐ £¬·¢ÏÖ´´½¨ Obj2 ¶ÔÏóºó£¬Á÷³Ì¾Í½øÐв»ÏÂÈ¥ÁË£º + +* `obj1` ÐèҪעÈë `obj2` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj2` µÄ´´½¨Á÷³Ì£» +* `obj2` ÐèҪעÈë `obj1` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj1` µÄ´´½¨Á÷³Ì£» +* `obj1` ÐèҪעÈë `obj2` µÄ´úÀí¶ÔÏ󣬵«ÕÒ²»µ½£¬ÓÚÊÇÇл»µ½ `obj2` µÄ´´½¨Á÷³Ì£» +* ... + +Èç´ËÑ­»·Íù¸´¡£ + +Ä£Äâ½á¹û£ºÎ´´ïµ½Ô¤ÆÚÄ¿±ê£¬±¾´ÎÄ£ÄâÐû¸æÊ§°Ü¡£ + +#### 3.1 Ä£Äâ 2 + +ÒªÇó£º + +* Obj1 Óë Obj2 ±ØÐëÒÔÏÂÁ½ÖÖÁ÷³ÌÖ®Ò»´´½¨£º + * ¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ + * ¡°´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó --> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖС± µÄÁ÷³Ì´´½¨ +* Á½¸ö¶ÔÏóµÄ´´½¨Á÷³Ì¿ÉÒÔ½»Ìæ½øÐÐ + +Ä¿±ê£º + +* ×îÖÕ·ÅÈëÈÝÆ÷µÄ¶ÔÏó·Ö±ðÊÇ `proxyObj1`£¬`proxyObj2` +* ×¢Èëµ½ `obj1` ÖеÄÊÇ `proxyObj2`£¬×¢Èëµ½ `obj2` ÖеÄÊÇ `proxyObj2` + +ʾÀý´úÂëÈçÏ£º + +``` + public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // 1\. ´´½¨ Obj1 ¶ÔÏó + Obj1 obj1 = new Obj1(); + + // ½ÓÏÂÀ´ÐèÒª½«obj2µÄ´úÀí¶ÔÏó×¢Èëµ½obj1ÖУ¬µ«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÇл»µ½obj2µÄ´´½¨Á÷³Ì + // Ò». ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. ¶Ô Obj1 Ìáǰ´úÀí + ProxyObj1 proxyObj1 = new ProxyObj1(obj1); + + // ¶þ. ½« proxyObj1 ×¢Èëµ½ obj2 ÖÐ + obj2.setObj1(proxyObj1); + + // Èý. Éú³É obj2µÄ´úÀí¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // ËÄ. proxyObj2 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + + // ´ËʱÈÝÆ÷ÖÐÒѾ­ÓÐ obj2 µÄ´úÀí¶ÔÏóÁË£¬¼ÌÐøobj1µÄÉúÃüÖÜÆÚ + // 3\. ½« proxyObj2 ×¢Èëµ½ obj1 ÖÐ + obj1.setObj2(proxyObj2); + + // 4\. proxyObj1 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj1); + } + +``` + +ÉÏÃæµÄ´úÂëÖУ¬obj1 µÄÁ÷³ÌÓà ¡°1£¬2£¬3£¬4¡± ±êʶ£¬obj2 µÄÁ÷³ÌÓà ¡°Ò»£¬¶þ£¬Èý£¬ËÄ¡± ±êʶ£¬Á½ÕßÁ÷³ÌÈçÏ£º + +* obj1£º¡°´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó --> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖС± +* obj2£º¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± + +×îÖÕÁ½Õß¶¼´æÈëÁËÈÝÆ÷ÖУ¬´ïµ½ÁËÔ¤ÆÚµÄÄ¿±ê¡£ + +#### 3.3 ´ÓÄ£ÄâÖеõ½µÄ½áÂÛ + +¶Ô±ÈÉÏÃæÁ½¸öÄ£Äâ´úÂ룬·¢ÏÖÄ£Äâ 2 Ö® ËùÒÔÄÜ´ïµ½Ô¤ÆÚÄ¿±ê£¬Ö÷ÒªÊÇÒòΪÔÚ×¢Èë `obj2` µÄ `obj1` ÊôÐÔʱ£¬ÌáǰÉú³ÉÁË `obj1` µÄ´úÀí¶ÔÏó `proxyObj1`£¬Ê¹µÃ `obj2` ÄÜÍê³ÉÕû¸ö´´½¨Á÷³Ì¡£ÕâÀïÔÙ´ÎÖ¤Ã÷£¬Ìṩ½øÐÐ aop ¶ÔÑ­»·ÒÀÀµµÄ½â¾öÆðµ½ÖÁ¹ØÖØÒªµÄ×÷Óã¡ + +ÏÞÓÚÆª·ù£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬±¾ÎÄÖ÷Òª·ÖÎöÁËÑ­»·ÒÀÀµµÄ²úÉú£¬½éÉÜÁË spring ½â¾öÑ­»·ÒÀÀµµÄ²½Ö裬×îºóͨ¹ýÁ½¶Î´úÂëÄ£ÄâÁËÑ­»·ÒÀÀµµÄ½â¾ö£¬ÏÂһƪÎÄÕÂÎÒÃǽ«´Ó spring Ô´Âë·ÖÎö spring ÊÇÈçºÎ½â¾öÑ­»·ÒÀÀµµÄ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4659555](https://my.oschina.net/funcy/blog/4659555) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ + +ʾÀý´úÂëÈçÏ£º + +``` + public static main(String[] args) { + // ×¼±¸Ò»¸öÈÝÆ÷£¬ÕâÀï±£´æµÄÊÇÍê³ÉÉÏÊöÉúÃüÖÜÆÚµÄ¶ÔÏó + // 1\. Èç¹ûÔªËØÊÇԭʼ¶ÔÏó£¬Ôò¸Ã¶ÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + // 2\. Èç¹ûÔªËØÊÇ´úÀí¶ÔÏó£¬Ôò¸Ã¶ÔÏó³ÖÓеÄÔ­ÓжÔÏóÒѾ­Íê³ÉÁËÊôÐÔ×¢Èë + Collection collection = new ArrayList(); + + // 1\. ´´½¨ Obj1 ¶ÔÏó + Obj1 obj1 = new Obj1(); + + // ½ÓÏÂÀ´ÐèÒª½«obj2µÄ´úÀí¶ÔÏó×¢Èëµ½obj1ÖУ¬µ«´ËʱÈÝÆ÷Öв¢Ã»ÓÐobj2µÄ´úÀí¶ÔÏó£¬ÓÚÊÇÇл»µ½obj2µÄ´´½¨Á÷³Ì + // Ò». ´´½¨ Obj2 ¶ÔÏó + Obj2 obj2 = new Obj2(); + + // 2\. ¶Ô Obj1 Ìáǰ´úÀí + ProxyObj1 proxyObj1 = new ProxyObj1(obj1); + + // ¶þ. ½« proxyObj1 ×¢Èëµ½ obj2 ÖÐ + obj2.setObj1(proxyObj1); + + // Èý. Éú³É obj2µÄ´úÀí¶ÔÏó + ProxyObj2 proxyObj2 = new ProxyObj2(obj2); + + // ËÄ. proxyObj2 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj2); + + // ´ËʱÈÝÆ÷ÖÐÒѾ­ÓÐ obj2 µÄ´úÀí¶ÔÏóÁË£¬¼ÌÐøobj1µÄÉúÃüÖÜÆÚ + // 3\. ½« proxyObj2 ×¢Èëµ½ obj1 ÖÐ + obj1.setObj2(proxyObj2); + + // 4\. proxyObj1 ÒѾ­×ßÍêÁËÍêÕûµÄÉúÃüÖÜÆÚ£¬½«´úÀí¶ÔÏóÌí¼Óµ½ÈÝÆ÷ʱ + collection.add(proxyObj1); + } + +``` + +ÉÏÃæµÄ´úÂëÖУ¬obj1 µÄÁ÷³ÌÓà ¡°1£¬2£¬3£¬4¡± ±êʶ£¬obj2 µÄÁ÷³ÌÓà ¡°Ò»£¬¶þ£¬Èý£¬ËÄ¡± ±êʶ£¬Á½ÕßÁ÷³ÌÈçÏ£º + +* obj1£º¡°´´½¨ (ԭʼ¶ÔÏó)--> Éú³É´úÀí¶ÔÏó --> ¶Ôԭʼ¶ÔÏó½øÐÐÊôÐÔ×¢Èë --> ½«´úÀí¶ÔÏó±£´æµ½ÈÝÆ÷ÖС± +* obj2£º¡°´´½¨ --> ÊôÐÔ×¢Èë --> Éú³É´úÀí¶ÔÏó --> ±£´æµ½ÈÝÆ÷ÖС± + +×îÖÕÁ½Õß¶¼´æÈëÁËÈÝÆ÷ÖУ¬´ïµ½ÁËÔ¤ÆÚµÄÄ¿±ê¡£ + +#### 3.3 ´ÓÄ£ÄâÖеõ½µÄ½áÂÛ + +¶Ô±ÈÉÏÃæÁ½¸öÄ£Äâ´úÂ룬·¢ÏÖÄ£Äâ 2 Ö® ËùÒÔÄÜ´ïµ½Ô¤ÆÚÄ¿±ê£¬Ö÷ÒªÊÇÒòΪÔÚ×¢Èë `obj2` µÄ `obj1` ÊôÐÔʱ£¬ÌáǰÉú³ÉÁË `obj1` µÄ´úÀí¶ÔÏó `proxyObj1`£¬Ê¹µÃ `obj2` ÄÜÍê³ÉÕû¸ö´´½¨Á÷³Ì¡£ÕâÀïÔÙ´ÎÖ¤Ã÷£¬Ìṩ½øÐÐ aop ¶ÔÑ­»·ÒÀÀµµÄ½â¾öÆðµ½ÖÁ¹ØÖØÒªµÄ×÷Óã¡ + +ÏÞÓÚÆª·ù£¬±¾ÎľÍÏȵ½ÕâÀïÁË£¬±¾ÎÄÖ÷Òª·ÖÎöÁËÑ­»·ÒÀÀµµÄ²úÉú£¬½éÉÜÁË spring ½â¾öÑ­»·ÒÀÀµµÄ²½Ö裬×îºóͨ¹ýÁ½¶Î´úÂëÄ£ÄâÁËÑ­»·ÒÀÀµµÄ½â¾ö£¬ÏÂһƪÎÄÕÂÎÒÃǽ«´Ó spring Ô´Âë·ÖÎö spring ÊÇÈçºÎ½â¾öÑ­»·ÒÀÀµµÄ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4659555](https://my.oschina.net/funcy/blog/4659555) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..06fe618 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\345\276\252\347\216\257\344\276\235\350\265\226\347\232\204\350\247\243\345\206\263\357\274\210\344\272\214\357\274\211\357\274\232\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1123 @@ +ÔÚ [spring Ì½ÃØÖ®Ñ­»·ÒÀÀµ£¨Ò»£©£ºÀíÂÛ»ùʯ](https://my.oschina.net/funcy/blog/4659555 "springÌ½ÃØÖ®Ñ­»·ÒÀÀµ£¨Ò»£©£ºÀíÂÛ»ùʯ")Ò»ÎÄÖÐ £¬ÎÒÃÇÌáµ½ spring ½â¾öÑ­»·ÒÀÀµµÄÁ÷³ÌÈçÏÂ: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-dc325a87b321e4c246a1b2f14169821a75a.png) + +ΪÁËÄÜÈÃÒÔÉÏÁ÷³ÌÄÜ˳Àû½øÐУ¬spring ÖÐÓÖÌṩÁË 5 ¸öÊý¾Ý½á¹¹À´±£´æÁ÷³ÌÖвúÉúµÄ¹Ø¼üÐÅÏ¢£¬Õâ 5 ¸öÊý¾Ý½á¹¹ÈçÏ£¨ÏÂÎĽ«Õâ 5 ¸öÊý¾Ý½á¹¹³ÆÎª **5 ´ó½á¹¹**£©£º + +| ½á¹¹ | ˵Ã÷ | +| ------------------------------- | ------------------------------------------------------------ | +| `singletonObjects` | **Ò»¼¶»º´æ**£¬ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇÍêÕûµÄ `spring bean`£¬¼´Íê³ÉÊôÐÔ×¢Èë¡¢³õʼ»¯µÄ bean£¬Èç¹û bean ÐèÒª aop£¬´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó | +| `earlySingletonObjects` | **¶þ¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ÊÇʵÀý»¯Íê³É£¬µ«Î´½øÐÐÒÀÀµ×¢ÈëµÄ `bean`£¬**Èç¹û `bean` ÐèÒª `aop`£¬ÕâÀï´æ´¢µÄ¾ÍÊÇ´úÀí¶ÔÏó£¬Ö»²»¹ý´úÀí¶ÔÏóËù³ÖÓеÄԭʼ¶ÔÏó²¢Î´½øÐÐÒÀÀµ×¢Èë** | +| `singletonFactories` | **Èý¼¶»º´æ**£¬ÀàÐÍΪ `HashMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry` ÀàÖУ¬`key` Ϊ `beanName`£¬`value` ´æ´¢µÄÊÇÒ»¸ö `lambda` ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬`getEarlyBeanReference(xxx)` ÖÐµÄ `bean` ÊǸմ´½¨Íê³ÉµÄ `java bean`£¬Ã»ÓнøÐÐ spring ÒÀÀµ×¢È룬Ҳû½øÐÐ aop | +| `singletonsCurrentlyInCreation` | ÀàÐÍΪ `SetFromMap`£¬Î»ÓÚ `DefaultSingletonBeanRegistry`£¬´´½¨·½Ê½Îª `Collections.newSetFromMap(new ConcurrentHashMap<>(16))`£¬±íÃ÷ÕâÊǸöÓÉ `ConcurrentHashMap` ʵÏÖµÄ set£¬´æ´¢µÄÊÇÕýÔÚ´´½¨ÖеĶÔÏ󣬿ÉÒÔ**ÓÃÀ´Åжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ´´½¨ÖÐ** | +| `earlyProxyReferences` | ÀàÐÍΪ `ConcurrentHashMap`£¬Î»ÓÚ `AbstractAutoProxyCreator`£¬´æ´¢µÄÊÇÌáǰ½øÐÐ aop µÄ¶ÔÏ󣬿ÉÒÔ**ÓÃÀ´ÅÐ¶Ï bean ÊÇ·ñ½øÐйý aop£¬±£Ö¤Ã¿¸ö¶ÔÏóÖ»½øÐÐÒ»´Î aop** | + +Á˽âÁËÕâЩ֮ºó£¬½ÓÏÂÀ´ÎÒÃǾÍÕýʽ¿ªÊ¼½øÐÐÔ´Âë·ÖÎöÁË¡£ + +### 1\. ×¼±¸ demo + +±¾ÎÄÖеÄʾÀý demo λÓÚ [gitee.com/funcy](https://gitee.com/funcy/spring-framework/tree/v5.2.2.RELEASE_learn/spring-learn/src/main/java/org/springframework/learn/explore/demo03 "gitee.com/funcy")£¬ÕâÀï½ö¸ø³ö¹Ø¼ü´úÂë¡£ + +×¼±¸Á½¸ö service£ºservice1£¬service2£¬ÕâÁ½¸ö service Àï¶¼ÓÐÒ»¸ö´úÀí·½·¨£º + +``` +@Service +public class Service1 { + + @Autowired + private Service2 service2; + + public Service1() { + System.out.println("µ÷ÓÃservice1µÄ¹¹Ôì·½·¨"); + } + + /** + * ±ê×¢ @AopAnnotation ÁË£¬±íÃ÷Õâ¸ö·½·¨Òª±»´úÀí + */ + @AopAnnotation + public void printAutowired() { + System.out.println("Service1 Autowired:" + service2.getClass()); + } + + @Override + public String toString() { + return "Service1:" + getClass(); + } +} + +@Component +public class Service2 { + + @Autowired + private Service1 service1; + + public Service2() { + System.out.println("µ÷ÓÃservice2µÄ¹¹Ôì·½·¨"); + } + + /** + * ±ê×¢ @AopAnnotation ÁË£¬±íÃ÷Õâ¸ö·½·¨Òª±»´úÀí + */ + @AopAnnotation + public void printAutowired() { + System.out.println("Service2 Autowired:" + service1.getClass()); + } + + @Override + public String toString() { + return "Service2:" + this.getClass(); + } +} + +``` + +ÕâÀïÊÇÖ÷Àࣺ + +``` +public class Demo03Main { + + public static void main(String[] args) { + ApplicationContext context = + new AnnotationConfigApplicationContext(AopAnnotationConfig.class); + Object obj1 = context.getBean("service1"); + Object obj2 = context.getBean("service2"); + ((Service1)obj1).printAutowired(); + ((Service2)obj2).printAutowired(); + } +} + +``` + +ÔÚ `Service1` ÖУ¬ÐèҪעÈëÊôÐÔ `service2`£¬ÔÚÔÚ `Service2` ÖУ¬ÐèҪעÈëÊôÐÔ `service1`£¬ÇÒ `Service1`¡¢`Service2` ¶¼ÐèÒª½øÐдúÀí£¬×îÖÕ `main()` ·½·¨Ö´Ðнá¹ûÈçÏ£º + +``` +µ÷ÓÃservice1µÄ¹¹Ôì·½·¨ +µ÷ÓÃservice2µÄ¹¹Ôì·½·¨ +Disconnected from the target VM, address: 'localhost:55518', transport: 'socket' +Connected to the target VM, address: '127.0.0.1:55507', transport: 'socket' +@Around: before execute... +Service1 Autowired:class org.springframework.learn.explore.demo03.Service2$$EnhancerBySpringCGLIB$$e7e367ab +@Around: after execute... +@Around: before execute... +Service2 Autowired:class org.springframework.learn.explore.demo03.Service1$$EnhancerBySpringCGLIB$$d447df08 +@Around: after execute... + +``` + +µÃµ½µÄ obj1¡¢obj2 ·Ö±ðΪ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-201844d13a7b489d1a18a8218d6440d6068.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-3781280ce3f4da91504b0665ebfd3aed05b.png) + +·ÖÎöÄÚÈÝ£¬²»ÄѵóöÈçϽáÂÛ£º + +* `service1` Óë `service2` µÄ `printAutowired()` ·½·¨¶¼Êä³öÁËÇÐÃæ·½·¨µÄÄÚÈÝ£¬±íÃ÷Á½Õß¶¼´úÀí³É¹¦ÁË£» +* ´ÓÈÝÆ÷ÖлñÈ¡µÄµÄ `service1` Óë `service2` ¶¼ÊÇ´úÀí¶ÔÏó£» +* `service1` µÄ´úÀí¶ÔÏó³ÖÓÐ `service1` µÄԭʼ¶ÔÏó£¬`service2` µÄ´úÀí¶ÔÏó³ÖÓÐ `service2` µÄԭʼ¶ÔÏó£» +* `service1` µÄԭʼ¶ÔÏóÖÐ×¢ÈëµÄ `service2` Ϊ´úÀí¶ÔÏó£¬`service2` µÄԭʼ¶ÔÏóÖÐ×¢ÈëµÄ `service1` Ϊ´úÀí¶ÔÏó¡£ + +½ÓÏÂÀ´£¬ÎÒÃǾʹÓÔ´Âë·ÖÎö£¬¿´ spring ÊÇÈçºÎÒ»²½²½ÔËÐУ¬×îÖյõ½Õâ¸ö½á¹ûµÄ¡£ + +> `service1` Óë `service2` µÄ´úÀí¶ÔÏóÊÇÓÉ `cglib` ´úÀí²úÉúµÄ£¬Õⲿ·ÖÄÚÈÝÓë±¾ÎÄÎ޹أ¬¾Í²»·ÖÎöÁË¡£ + +### 2\. µÚÒ»´Îµ÷Óà `AbstractBeanFactory#getBean(String)`£º»ñÈ¡ `service1` + +spring bean µÄ´´½¨¡¢ÒÀÀµ×¢ÈëÁ÷³ÌÊÇ´Ó `AbstractBeanFactory#getBean(String)` ·½·¨¿ªÊ¼£¬ÕâÒ»µã¿É²Î¿¼ [spring Æô¶¯Á÷³ÌÖ®Íê³É BeanFactory µÄ³õʼ»¯](https://my.oschina.net/funcy/blog/4658230)£¬ÎÒÃǵÄÔ´Âë·ÖÎöÒ²´ÓÕâ¸ö·½·¨ÈëÊÖ¡£ + +±¾ÎÄÒ»¿ªÊ¼£¬¾ÍÌáµ½ÁË spring Ϊ½â¾öÑ­»·ÒÀÀµµÄ 5 ´ó½á¹¹£¬ÕâÀïչʾÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ---- | +| `singletonObjects` | | +| `earlySingletonObjects` | | +| `singletonFactories` | | +| `singletonsCurrentlyInCreation` | | +| `earlyProxyReferences` | | + +Ò»¿ªÊ¼£¬5 ´ó½á¹¹ÖйØÓÚ `service1` Óë `service2` µÄʲôÊý¾Ý¶¼Ã»ÓÐ £¬½ÓÏÂÀ´£¬ÎÒÃÇÒ»±ß·ÖÎö´úÂ룬һ±ß¹Ø×¢Õ⼸¸ö½á¹¹ÖеÄÄÚÈÝ¡£ + +> ÊÂʵÉÏ£¬5 ´ó½á¹¹ÊÇÓÐÄÚÈݵģ¬ÄÇÊÇ spring ÄÚ²¿ÌṩµÄһЩ bean£¬µ«ÓÉÓÚÎÒÃÇÖ»¹Ø×¢ `service1` Óë `service2` Ïà¹ØµÄÄÚÈÝ£¬Òò´ËÕâÀﲻչʾ¡£ + +### 2.1 `AbstractBeanFactory#doGetBean` + +»ñÈ¡ `service1` µÄ´úÂëÔÚ `AbstractBeanFactory#getBean(String)`£¬µ«ÕæÕýµÄ»ñÈ¡²Ù×÷È´ÊÇÔÚ `AbstractBeanFactory#doGetBean` ÖУ¬µ÷ÓÃÁ´ÈçÏ£º + +``` +|-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + +``` + +´úÂëÈçÏ£º + +``` + protected T doGetBean(final String name, @Nullable final Class requiredType, + @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { + + ... + // 1\. ¼ì²éÊÇ·ñÒѳõʼ»¯£¬ÕâÀï»á°ÑbeanName·ÅÈësingletonsCurrentlyInCreationÖÐ + Object sharedInstance = getSingleton(beanName); + ... + // Èç¹ûÊǵ¥ÀýµÄ + if (mbd.isSingleton()) { + // 2\. getSingleton(): ´´½¨¶ÔÏó£¬É¾³ý¶þ¡¢Èý¼¶»º´æ + sharedInstance = getSingleton(beanName, () -> { + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + destroySingleton(beanName); + throw ex; + } + }); + // ÕâÀïÈç¹ûÊÇÆÕͨBean µÄ»°£¬Ö±½Ó·µ»Ø£¬Èç¹ûÊÇ FactoryBean µÄ»°£¬ + // ·µ»ØËü´´½¨µÄÄǸöʵÀý¶ÔÏó + bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + ... + } + +``` + +ÒÔÉÏ´úÂë¾­¹ýÁ˾«¼ò£¬ÎÒÃÇÖ»±£ÁôÁËÖ÷ÒªÁ÷³Ì£¨ºóÐø·ÖÎöÖУ¬Ò²»á¾«¼ò´úÂ룬ֻ±£ÁôÖ÷ÒªÁ÷³Ì£¬Ê¡ÂÔÎ޹شúÂ룩£¬Õâ¸ö·½·¨ÓÐÁ½¸öµØ·½ÐèÒª·ÖÎö£º + +1. `getSingleton(beanName)`£º»ñÈ¡¶ÔÏó£¬Ö»´ÓÈý¸ö»º´æÖлñÈ¡£» +2. `getSingleton(beanName, () -> { ... })`£º»ñÈ¡¶ÔÏ󣬴´½¨»áÔÚÕâÀï´´½¨¡£ + +#### 2.2 `DefaultSingletonBeanRegistry#getSingleton(String, boolean)` + +ÎÒÃÇÏÈÀ´¿´¿´ `getSingleton(beanName)`£¬µ÷ÓÃÁ´ÈçÏ£º + +``` +|-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String) + |-DefaultSingletonBeanRegistry#getSingleton(String, boolean) + +``` + +Ö±½Ó¿´´úÂ룺 + +``` +/* + * beanName: ´«ÈëµÄֵΪ service1 + * allowEarlyReference£º´«ÈëµÄֵΪ true + */ +protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // 1\. ´ÓÒ»¼¶»º´æÖлñÈ¡£¬ÕâÀïÊÇ»ñÈ¡²»µ½ + Object singletonObject = this.singletonObjects.get(beanName); + // 2\. isSingletonCurrentlyInCreation(...) ÅжÏservice1ÊÇ·ñÔÚ´´½¨ÖУ¬·µ»Ø false + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { + // Ê¡ÂÔÏÂÃæµÄ´úÂë + ... + } + return singletonObject; +} + +``` + +¶ÔÉÏÊö´úÂë·ÖÎöÈçÏ£º + +1. ´ÓÈý¼¶»º´æÖлñÈ¡£¬´Ëʱ `singletonObjects` ÀﲢûÓÐ `service1`£¬»ñÈ¡²»µ½£» + +2. ¼ÌÐø£¬ÅÐ¶Ï `service1` ÊÇ·ñÔÚ´´½¨ÖУ¬ÅжϷ½Ê½ÈçÏ£º + + ``` + public boolean isSingletonCurrentlyInCreation(String beanName) { + return this.singletonsCurrentlyInCreation.contains(beanName); + } + + ``` + + ÏÔÈ»£¬`singletonsCurrentlyInCreation` ÊÇûÓÐ `service1` µÄ£¬ÕâÀïÒ²·µ»Ø false. + +`DefaultSingletonBeanRegistry#getSingleton(String, boolean)` ÔËÐе½ÕâÀï¾Í·µ»ØÁË£¬Õâ¸ö·½·¨ÀïÏÂÃæµÄ·µ»ØÎÒÃǾÍÏȲ»·ÖÎöÁË¡£ + +#### 2.3 `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)` + +ÈÃÎÒÃǻص½ `AbstractBeanFactory#doGetBean`£¬½Ó×Å·ÖÎö `getSingleton(beanName, () -> { ... })`£¬´úÂëÈçÏ£º + +``` + /** + * beanName£º´«ÈëµÄÊÇservice1 + * singletonFactory£º´«ÈëµÄÊÇlambda±í´ïʽ£¬ÖµÎª + * () -> { + * try { + * // ÕâÀï¾ÍÊÇ´´½¨beanµÄÁ÷³Ì + * return createBean(beanName, mbd, args); + * } + * catch (BeansException ex) { + * destroySingleton(beanName); + * throw ex; + * } + * } + */ + public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + Assert.notNull(beanName, "Bean name must not be null"); + synchronized (this.singletonObjects) { + // ÔÙ´ÎÅжÏbeanÊÇ·ñÒÑ´æÔÚ + Object singletonObject = this.singletonObjects.get(beanName); + if (singletonObject == null) { + // 1\. Åжϵ±Ç°ÕýÔÚʵÀý»¯µÄbeanÊÇ·ñ´æÔÚÕýÔÚ´´½¨ÖÐ + // Õâ¸ö·½·¨»ábeanNameÌí¼Óµ½ singletonsCurrentlyInCreation ÖÐ + beforeSingletonCreation(beanName); + try { + // 2\. ÔÚÕâÀï½øÐÐbeanµÄ´´½¨ + singletonObject = singletonFactory.getObject(); + } + catch (...) { + ... + } + ... + } + return singletonObject; + } + } + +``` + +°´ÕÕ´úÂëÖеÄ×¢ÊÍÄÚÈÝ£¬ÎÒÃÇ·ÖÎöÖ÷Òª²½Ö裺 + +1. Åжϵ±Ç°ÕýÔÚʵÀý»¯µÄ bean ÊÇ·ñ´æÔÚÕýÔÚ´´½¨ÖУ¬¾ÍÊÇÅжϵ±Ç°¶ÔÏóÊÇ·ñÔÚ `singletonsCurrentlyInCreation` ÖУ¬Èç¹û´æÔÚÔòÅ׳öÒì³££¬²»´æÔÚÔòÌí¼Óµ½ `singletonsCurrentlyInCreation` ÖУ» + + ÔËÐÐÍêÖ®ºóÕâÒ»ÐдúÂëºó£¬5 ´ó½á¹¹ÖеÄÄÚÈÝÈçÏ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | -------- | + | `singletonObjects` | | + | `earlySingletonObjects` | | + | `singletonFactories` | | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | | + +2. ½øÐÐ bean µÄ´´½¨¹ý³Ì£¬`singletonFactory.getObject()` ÔËÐеÄʵ¼ÊÉÏÊÇ´«ÈëµÄ lambda ±í´ïʽ£º + + ``` + () -> { + try { + // Ö´Ðд´½¨ Bean + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + destroySingleton(beanName); + throw ex; + } + } + + ``` + + Ò²¾ÍÊÇ `AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])`£¬Õâ ¸ö·½·¨ÊǽÓÏÂÀ´·ÖÎöµÄÖØµã£» + +#### 2.4 `AbstractAutowireCapableBeanFactory#doCreateBean` + +´Ó´úÂëÉÏÀ´¿´£¬`AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])` ²¢Ã»ÓÐ×öʲôʵÖÊÐÔÄÚÈÝ£¬ÎÒÃǾÍÖ±½ÓÀ´µ½ `AbstractAutowireCapableBeanFactory#doCreateBean` ÁË£¬ÕâÆäÖо­ÀúµÄǧɽÍòË®ÈçÏ£º + +``` +|-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory) + |-AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) + |-AbstractAutowireCapableBeanFactory#doCreateBean + +``` + +`AbstractAutowireCapableBeanFactory#doCreateBean` ÄÚÈÝÈçÏ£º + +``` +protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, + final @Nullable Object[] args) throws BeanCreationException { + + BeanWrapper instanceWrapper = null; + if (instanceWrapper == null) { + // 1\. ʵÀý»¯ Bean£¬µ÷ÓÃjava·´Éä½øÐÐʵÀý»¯ + instanceWrapper = createBeanInstance(beanName, mbd, args); + } + //beanʵÀý + final Object bean = instanceWrapper.getWrappedInstance(); + //beanÀàÐÍ + Class beanType = instanceWrapper.getWrappedClass(); + if (beanType != NullBean.class) { + mbd.resolvedTargetType = beanType; + } + + synchronized (mbd.postProcessingLock) { + if (!mbd.postProcessed) { + try { + // µ÷Óà AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition ·½·¨ + // 2\. »ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¨±ê×¢ @Autowired ×¢½â£© + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Post-processing of merged bean definition failed", ex); + } + mbd.postProcessed = true; + } + } + // ½â¾öÑ­»·ÒÀÀµÎÊÌâ, ÊÇ·ñÔÊÐíÑ­»·ÒÀÀµ, allowCircularReferencesĬÈÏΪtrue£¬¿ÉÒÔ¹Ø±Õ + boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && + isSingletonCurrentlyInCreation(beanName)); + if (earlySingletonExposure) { + // 3\. ½« bean Ìí¼Óµ½ singletonFactories + addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); + } + + Object exposedObject = bean; + try { + // 4\. ÊôÐÔ×¢È룬·¢ÏÖÐèҪעÈë service2£¬È»ºóÔÙµ÷Óà getBean("service2") + populateBean(beanName, mbd, instanceWrapper); + ... + } + catch (Throwable ex) { + ... + } +} + +``` + +Õâ¸ö·½·¨½øÐеIJ½ÖèÈçÏ£º + +1. ʵÀý»¯ Bean£¬µ÷Óà java ·´Éä½øÐÐʵÀý»¯£¬Õâ¸ö¾Í²»¶à˵ÁË£¬ÊµÀý»¯ºóµÄ `service1` ÈçÏ£¬¿ÉÒÔ¿´µ½ `service2` »¹ÊÇ `null`£¨±íÃ÷δ½øÐÐÊôÐÔ×¢È룩£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-8b2f5fdf0efd6beb7f62d4f35f5c5562b83.png) + +2. »ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¬**±ØÐëÔÚԭʼ¶ÔÏóÖвÅÄÜ»ñÈ¡µ½£¬´úÀí¶ÔÏóÊÇ»ñÈ¡²»µ½µÄ**£¬²éÕÒ `@Autowired` µÄ `beanPostProcessor` ÊÇ `AutowiredAnnotationBeanPostProcessor`£» + +3. ½« bean Ìí¼Óµ½ `singletonFactories` ÖУ¬Ìí¼Ó¹ý³ÌÈçÏ£º + + ``` + protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { + Assert.notNull(singletonFactory, "Singleton factory must not be null"); + synchronized (this.singletonObjects) { + // Èç¹ûµ¥Àý³Øµ±Öв»´æÔڲŻáadd + if (!this.singletonObjects.containsKey(beanName)) { + // °Ñ¹¤³§¶ÔÏóputµ½singletonFactories + this.singletonFactories.put(beanName, singletonFactory); + // ɾ³ý¶þ¼¶»º´æÖеĶÔÏó + this.earlySingletonObjects.remove(beanName); + this.registeredSingletons.add(beanName); + } + } + } + + ``` + + ¼ÓÈëµÄ¶ÔÏóΪ + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-3d9a56a0d655fe1e95d8857095fb46aa71a.png) + + ´ËʱÄÇ 5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ---------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | | + | `singletonFactories` | lambda(service1) | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | | + + `lambda(service1)` ʵ¼ÊÄÚÈÝΪ `() -> getEarlyBeanReference(beanName, mbd, bean)`£¬Õâ¸ö `lambda` ±í´ïʽËù×öµÄ¾ÍÊǽøÐÐ aop ²Ù×÷£¬¾ßÌåÄÚÈݵÈÔËÐеÄʱºòÎÒÃÇÔÙ·ÖÎö¡£ + +4. ÊôÐÔ×¢È룬ÔÚµÚ 2 ²½ÖУ¬spring ÕÒµ½ `service1` ÐèҪעÈë¶ÔÏóÓÐ `service2`£¬ÕâÀï»áÔÙµ÷Óà `beanFactory.getBean("service2")` ½øÈë `service2` µÄÉúÃüÖÜÆÚ£¬ÕâÓֻص½ÁË `AbstractBeanFactory#getBean(String)` ·½·¨¡£ + + ´Ó `populateBean(xxx)` µ½ `getBean(xxx)` µÄ²½ÖèÏ൱¶à£¬µ÷ÓÃÁ´ÈçÏ£º + + ``` + |-AbstractBeanFactory#getBean(String) + |-AbstractBeanFactory#doGetBean + |-DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory) + |-AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) + |-AbstractAutowireCapableBeanFactory#doCreateBean + // ÕâÀï½øÐÐÒÀÀµ×¢Èë + |-AbstractAutowireCapableBeanFactory#populateBean + |-AutowiredAnnotationBeanPostProcessor#postProcessProperties + |-InjectionMetadata#inject + |-AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject + |-DefaultListableBeanFactory#resolveDependency + |-DefaultListableBeanFactory#doResolveDependency + |-DependencyDescriptor#resolveCandidate + |-AbstractBeanFactory#getBean(String) + + ``` + + ¿´Ò»ÑÛ `DependencyDescriptor#resolveCandidate`£¬µ÷ÓõÄÕýÊÇ `beanFactory.getBean(String)` ·½·¨£º + + ``` + public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) + throws BeansException { + return beanFactory.getBean(beanName); + } + + ``` + +µ½ÁËÕâÀ`service1` µÄ»ñÈ¡Á÷³Ì¾ÍÏÈͣһϣ¬ÒòΪ `service1` ҪעÈë `service2`£¬½ÓÏÂÀ´¾Í¿ªÊ¼»ñÈ¡ `service2` µÄÁ÷³Ì¡£ + +±¾Ð¡½Ú½áÊøÊ±£¬5 ´ó½á¹¹ÖеÄÄÚÈÝÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ---------------- | +| `singletonObjects` | | +| `earlySingletonObjects` | | +| `singletonFactories` | lambda(service1) | +| `singletonsCurrentlyInCreation` | service1 | +| `earlyProxyReferences` | | + +### 3\. µÚ¶þ´Îµ÷Óà `AbstractBeanFactory#getBean(String)`£º»ñÈ¡ `service2` + +#### 3.1 `AbstractBeanFactory#doGetBean` + +ÕâÒ»²½¸ú»ñÈ¡ `service1` µÄÁ÷³Ì»ù±¾Ò»Ö£¬²»Í¬µÄÊÇ `beanName` ÊÇ `service2`£¬²»ÔÙ·ÖÎö¡£ + +#### 3.2 `DefaultSingletonBeanRegistry#getSingleton(String, boolean)` + +ÕâÒ»²½¸ú»ñÈ¡ `service1` µÄÁ÷³Ì»ù±¾Ò»Ö£¬²»Í¬µÄÊÇ `beanName` ÊÇ `service2`£¬²»ÔÙ·ÖÎö¡£ + +#### 3.3 `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)` + +ÕâÒ»²½¸ú»ñÈ¡ `service1` µÄÁ÷³Ì»ù±¾Ò»Ö£¬²»Í¬µÄÊÇ `beanName` ÊÇ `service2`£¬¾ßÌåÄÚÈݲ»ÔÙ·ÖÎö¡£ + +`service2` »áÔÚÕâÒ»²½¼ÓÈë µ½ `singletonsCurrentlyInCreation` ½á¹¹ÖУ¬ÕâÒ»²½Ö®ºó£¬5 ´ó½á¹¹ÖеÄÄÚÈÝÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ------------------ | +| `singletonObjects` | | +| `earlySingletonObjects` | | +| `singletonFactories` | lambda(service1) | +| `singletonsCurrentlyInCreation` | service1, service2 | +| `earlyProxyReferences` | | + +#### 3.4 `AbstractAutowireCapableBeanFactory#doCreateBean` + +ÕâÒ»²½¸ú»ñÈ¡ `service1` µÄÁ÷³Ì»ù±¾Ò»Ö£¬ËµÃ÷ÈçÏ£º + +1. ¶ÔÏó´´½¨£¬ÕâÀï»á´´½¨ `service2`£¬´´½¨Íê³ÉºóµÄ¶ÔÏóÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-393811afa91cc1ff696b02ce6d683c97640.png) + + ͬÑùµÄ£¬`service2` ÖеÄÊôÐÔ `service1` ҲΪ `null`£» + +2. »ñÈ¡ÐèҪעÈëµÄÊôÐÔÓë·½·¨£¬`service2` µÄÊôÐÔ `service1` »á±»ÕÒµ½£¬ÒòΪ¸ÃÊôÐÔ±ê×¢ÓÐ `@Autowired` ×¢½â£» + +3. ½« bean Ìí¼Óµ½ `singletonFactories` ÖУ¬ÕâÒ»²½Ö®ºó£¬5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ---------------------------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | | + | `singletonFactories` | lambda(service1), lambda(service2) | + | `singletonsCurrentlyInCreation` | service1, service2 | + | `earlyProxyReferences` | | + + ÕâÒ»²½µÄ¹Ø¼üÔÚÓÚ£¬`service2` Ìí¼Óµ½Èý¼¶»º´æÖÐÁË£» + +4. ÊôÐÔ×¢È룬ÔÚµÚ 2 ²½ÖУ¬spring ÕÒµ½ `service2` ÐèҪעÈë¶ÔÏóÓÐ `service1`£¬ÕâÀï»áÔÙµ÷Óà `getBean("service2")` ·½·¨£¬Óֻص½ÁË `AbstractBeanFactory#getBean(String)` ·½·¨ÁË¡£ + +µ½ÁËÕâÀ`service2` µÄ»ñÈ¡Á÷³ÌÒ²ÒªÏÈÍ£Ò»ÏÂÁË£¬ÒòΪ `service2` ҪעÈë `service1`£¬½ÓÏÂÀ´ÓÖÒª¿ªÊ¼»ñÈ¡ `service1` µÄÁ÷³Ì¡£ + +±¾Ð¡½Ú½áÊøÊ±£¬5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ---------------------------------- | +| `singletonObjects` | | +| `earlySingletonObjects` | | +| `singletonFactories` | lambda(service1), lambda(service2) | +| `singletonsCurrentlyInCreation` | service1, service2 | +| `earlyProxyReferences` | | + +### 4\. µÚÈý´Îµ÷Óà `AbstractBeanFactory#getBean(String)`£ºÔٴλñÈ¡ `service1` + +#### 4.1 `AbstractBeanFactory#doGetBean` + +`AbstractBeanFactory#doGetBean` ´úÂëÈçÏ£º + +``` + protected T doGetBean(final String name, @Nullable final Class requiredType, + @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { + + ... + // 1\. ¼ì²éÊÇ·ñÒѳõʼ»¯£¬ÕâÀï»á°ÑbeanName·ÅÈësingletonsCurrentlyInCreationÖÐ + Object sharedInstance = getSingleton(beanName); + ... +} + +``` + +ÔÚÔËÐе½ `Object sharedInstance = getSingleton(beanName)` ǰ£¬`AbstractBeanFactory#doGetBean` µÄÔËÐÐÁ÷³ÌÓëÇ°ÃæÁ½Ð¡½ÚÎÞÇø±ð£¬ÔÚ `Object sharedInstance = getSingleton(beanName)` Àï¾ÍÓÐÁ˱仯£¬½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ `getSingleton(beanName)` µÄÖ´ÐС£ + +#### 4.2 `DefaultSingletonBeanRegistry#getSingleton(String, boolean)` + +``` +/* + * beanName: ´«ÈëµÄֵΪ service1 + * allowEarlyReference£º´«ÈëµÄֵΪ true + */ +protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // 1\. ´ÓÒ»¼¶»º´æÖлñÈ¡£¬ÕâÀïÊÇ»ñÈ¡²»µ½ + Object singletonObject = this.singletonObjects.get(beanName); + // 2\. isSingletonCurrentlyInCreation(...) ÅжÏservice1ÊÇ·ñÔÚ´´½¨ÖУ¬·µ»Ø true + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { + synchronized (this.singletonObjects) { + // 3\. ´Ó¶þ¼¶»º´æÖлñÈ¡£¬ÕâÀïÒ²»ñÈ¡²»µ½ + singletonObject = this.earlySingletonObjects.get(beanName); + // 4\. ´«ÈëµÄallowEarlyReferenceÊÇtrue£¬¼ÌÐøÖ´ÐÐÀïÃæµÄÄÚÈÝ + if (singletonObject == null && allowEarlyReference) { + // 5\. ´ÓÈý¼¶»º´æÖлñÈ¡£¬ÄÜ»ñÈ¡µ½ + ObjectFactory singletonFactory = this.singletonFactories.get(beanName); + if (singletonFactory != null) { + // 6\. µ÷Óà singletonFactory.getObject() + // ×îÖÕµ÷ÓõÄÊÇ AbstractAutowireCapableBeanFactory.getEarlyBeanReference ·½·¨ + singletonObject = singletonFactory.getObject(); + // 7\. ´¦Àí»º´æ + // ·Åµ½¶þ¼¶»º´æÖÐ + this.earlySingletonObjects.put(beanName, singletonObject); + // ´ÓÈý¼¶»º´æÖÐÇå³ý + this.singletonFactories.remove(beanName); + } + } + } + } + return singletonObject; +} + +``` + +ÔÚ·ÖÎö´úÂëǰ£¬ÏÈÀ´¿´¿´´Ëʱ 5 ´ó½á¹¹ÖеÄÄÚÈÝ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ---------------------------------- | +| `singletonObjects` | | +| `earlySingletonObjects` | | +| `singletonFactories` | lambda(service1), lambda(service2) | +| `singletonsCurrentlyInCreation` | service1, service2 | +| `earlyProxyReferences` | | + +¶ÔÕÕ×ÅÉÏÃæµÄÄÚÈÝ£¬·ÖÎöÈçÏ£º + +1. ´ÓÒ»¼¶»º´æÖлñÈ¡£¬¶ÔÕÕ×Å 5 ¸ö½á¹¹ÖеÄÄÚÈÝ£¬»ñÈ¡²»µ½£¬·µ»Ø `null`; + +2. `isSingletonCurrentlyInCreation(...)` ÅÐ¶Ï `service1` ÊÇ·ñÔÚ´´½¨ÖУ¬¶ÔÕÕ×Å 5 ¸ö½á¹¹ÖеÄÄÚÈÝ£¬`service1` ÔÚ `singletonsCurrentlyInCreation` ÖУ¬·µ»Ø true£¬¼ÌÐøÏÂÃæµÄÁ÷³Ì£¬**ÕâÊÇÓëµÚÒ»´Î»ñÈ¡ `service1` µÄ²»Í¬Ö®´¦**£» + +3. ¼ÌÐø´Ó¶þ¼¶»º´æÖлñÈ¡£¬¶ÔÕÕ×Å 5 ¸ö½á¹¹ÖеÄÄÚÈÝ£¬`service1` ²»ÔÚ `earlySingletonObjects`£¬ÒÀÈ»·µ»Ø `null`£» + +4. `allowEarlyReference` ÊÇ´«ÈëµÄ²ÎÊý£¬Îª `true`£¬¼ÌÐøÖ´ÐÐ ÏÂÃæµÄ´úÂ룻 + +5. ¼ÌÐø´Ó´ÓÈý¼¶»º´æÖлñÈ¡£¬¶ÔÕÕ×Å 5 ¸ö½á¹¹ÖеÄÄÚÈÝ£¬`service1` ÔÚ `singletonFactories` ÖУ¬ÕâÀïÄÜ»ñÈ¡µ½£¬·µ»ØµÄ½á¹ûÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-57677e20855580e5bc2f5163d87ecfda7d2.png) + +6. µ÷Óà `singletonFactory.getObject()` ·½·¨£¬µ±³õÎÒÃÇ´«Èë `singletonFactories` µÄ»¹ÊǸö lambda ±í´ïʽ£º`() -> getEarlyBeanReference(beanName, mbd, bean)`£¬µ÷Óà `singletonFactory.getObject()` ×îÖÕµ÷ÓõľÍÊÇ `AbstractAutowireCapableBeanFactory#getEarlyBeanReference`£º + + ``` + protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { + Object exposedObject = bean; + if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { + // µ÷ÓúóÖô¦ÀíÆ÷£¬ÕâÀïÍê³ÉÁËaop²Ù×÷ + SmartInstantiationAwareBeanPostProcessor ibp = + (SmartInstantiationAwareBeanPostProcessor) bp; + // µ÷Óà `AbstractAutoProxyCreator#getEarlyBeanReference`£¬Ìáǰ½øÐÐ aop + exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); + } + } + } + return exposedObject; + } + + ``` + + ÎÒÃǸú½ø `AbstractAutoProxyCreator#getEarlyBeanReference` ·½·¨£º + + ``` + public Object getEarlyBeanReference(Object bean, String beanName) { + Object cacheKey = getCacheKey(bean.getClass(), beanName); + this.earlyProxyReferences.put(cacheKey, bean); + // ÕâÀïÉú³É´úÀí¶ÔÏó + return wrapIfNecessary(bean, beanName, cacheKey); + } + + ``` + + ¹ØÓÚ aop µÄÁ÷³Ì£¬¿É²Î¿¼ [spring aop Ö® AnnotationAwareAspectJAutoProxyCreator ·ÖÎö£¨Ï£©](https://my.oschina.net/funcy/blog/4687961)£¬ÕâÀï¾Í²»ÔÙ·ÖÎöÁË¡£Ö´ÐÐÍê `AbstractAutoProxyCreator#getEarlyBeanReference` Ö®ºó£¬5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ---------------------------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | | + | `singletonFactories` | lambda(service1), lambda(service2) | + | `singletonsCurrentlyInCreation` | service1, service2 | + | `earlyProxyReferences` | service1 | + + ÈÃÎÒÃÇÔٻص½ `getSingleton` ·½·¨£¬Ö´ÐÐÍê `singletonFactory.getObject()` ºó£¬µÃµ½µÄ `singletonObject` Ϊ + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-cf72a8ff3d91c3d2a4e300741ba7f0268e6.png) + + ÕâÊÇÒ»¸ö´úÀí¶ÔÏ󣬲¢ÇÒ `service2` Ϊ `null`¡£ + +7. ½ÓÏÂÀ´¾ÍÊÇ´¦Àí»º´æÁË£¬Ò»Ð© map µÄ put Óë remove ²Ù×÷£¬¾Í²»¶à˵ÁË¡£ + +`DefaultSingletonBeanRegistry#getSingleton(String, boolean)` ·½·¨Ö´ÐÐÍê³Éºó£¬5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ------------------ | +| `singletonObjects` | | +| `earlySingletonObjects` | service1 | +| `singletonFactories` | lambda(service2) | +| `singletonsCurrentlyInCreation` | service1, service2 | +| `earlyProxyReferences` | service1 | + +#### 4.3 Ôٴλص½ `AbstractBeanFactory#doGetBean` + +Ö´ÐÐÍê `DefaultSingletonBeanRegistry#getSingleton(String, boolean)` ºó£¬ÎÒÃÇÔٻص½ `AbstractBeanFactory#doGetBean`£º + +``` +protected T doGetBean(final String name, @Nullable final Class requiredType, + @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { + ... + + // ¼ì²éÊÇ·ñÒѳõʼ»¯ + Object sharedInstance = getSingleton(beanName); + + // 1\. Õâ´ÎÄÜ»ñÈ¡µ½¶ÔÏó£¬if ÀïÃæµÄ´úÂë»áÖ´ÐÐ + if (sharedInstance != null && args == null) { + // 2\. ÕâÀïÈç¹ûÊÇÆÕͨBean µÄ»°£¬Ö±½Ó·µ»Ø£¬Èç¹ûÊÇ FactoryBean µÄ»°£¬·µ»ØËü´´½¨µÄÄǸöʵÀý¶ÔÏó + // ÕâÀïµÄbeanÊÇservice1µÄ´úÀí¶ÔÏó + bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); + } else { + // ÕâÀïµÄ´úÂë²»»áÖ´Ðе½£¬¾Í²»·ÖÎöÁË + ... + } + + // ´¦ÀíbeanµÄת»»²Ù×÷£¬ÕâÀï·µ»Øfalse£¬²»»áÖ´Ðе½ + if (requiredType != null && !requiredType.isInstance(bean)) { + ... + } + + // 3\. ·µ»Ø´Ó`getSingleton(beanName)`µÃµ½µÄ¶ÔÏó + return bean; + +} + +``` + +1. Õâ´ÎÄÜ»ñÈ¡µ½¶ÔÏó£¬if ÀïÃæµÄ´úÂë»áÖ´ÐУ» + +2. `service1` ²»ÊÇ `FactoryBean`£¬Óë `getSingleton(beanName)` µÃµ½µÄ¶ÔÏóÊÇͬһ¸ö¶ÔÏó£¬ÄÚÈÝÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-0cd9c129bf2387d85a7bee4f4e135143f63.png) + +3. ·µ»Ø´Ó `getSingleton(beanName)` µÃµ½µÄ¶ÔÏó£¬ÔÚ `getSingleton(beanName)` Öзµ»ØµÄ¶ÔÏó²»Îª¿Õºó£¬¾Í²»ÔÙÖ´ÐÐ bean µÄ´´½¨Á÷³ÌÁË£¬×îÖÕ·µ»ØµÄÊÇµÚ 2 ²½Öеõ½µÄ bean ÁË¡£ + +µ½ÁËÕâÀÎÒÃÇÖÕÓÚ»ñÈ¡µ½ÁË `service1` µÄ´úÀí¶ÔÏ󣬾¡¹Ü´úÀí¶ÔÏó¶ÔÓ¦µÄԭʼ¶ÔÏó²¢Ã»ÓÐÍê³ÉÒÀÀµ×¢È룬µ«ÎÒÃÇÒÀÈ»¿ÉÒÔ½øÐкóÐøµÄÁ÷³Ì£¬¼´»ñÈ¡µ½ `service1` µÄ´úÀí¶ÔÏóºó£¬`service2` ¾Í¿ÉÒÔ½øÐÐÒÀÀµ×¢È룬¼ÌÐø½ÓÏÂÀ´µÄÁ÷³ÌÁË¡£ + +×îºó£¬ÎÒÃÇÔÙÀ´¿´¿´±¾½ÚÖ´ÐÐÍêÖ®ºó£¬5 ´ó½á¹¹ÖеÄÄÚÈÝ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ------------------------------- | +| `singletonObjects` | | +| `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | +| `singletonFactories` | lambda(service2) | +| `singletonsCurrentlyInCreation` | service1, service2 | +| `earlyProxyReferences` | service1 | + +»ñÈ¡µ½ `service1` ºó£¬½ÓÏÂÀ´¾Í¼ÌÐø `service2` µÄ»ñÈ¡Á÷³ÌÁË¡£ + +### 5 ¼ÌÐø `service2` µÄ»ñÈ¡Á÷³Ì + +ÔÚµÚ 3 ½ÚÖУ¬ÒòΪ `service2` ÐèҪעÈë `service1`£¬²ÅÓÐÁËµÚ 4 ²¿·ÖµÄÔٴλñÈ¡ `service1` µÄÁ÷³Ì£¬×îÖÕÒ²³É¹¦µØ»ñÈ¡µ½ÁË `service1` µÄ´úÀí¶ÔÏó `service1$$EnhancerBySpringCGLIB`£¬Õâ²ÅÈà `service2` µÃÒÔÍê³ÉÒÀÀµ×¢Èë¡£ + +½ÓÏÂÀ´£¬ÈÃÎÒÃÇÑÓÐø µÚ 3 ½Ú µÄÁ÷³Ì£¬»Øµ½ `AbstractAutowireCapableBeanFactory#doCreateBean`£¬¼ÌÐø `service2` µÄÁ÷³Ì¡£ + +#### 5.1 »Øµ½ `AbstractAutowireCapableBeanFactory#doCreateBean` + +´úÂëÈçÏ£º + +``` +protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, + final @Nullable Object[] args) throws BeanCreationException { + + // ÉÏÃæµÄ´úÂëÔÚ 3.2 ÖÐÒѾ­·ÖÎö¹ýÁË£¬¾Í²»ÔÙ·ÖÎöÁË + ... + + Object exposedObject = bean; + try { + // 1\. ¸ºÔðÊôÐÔ×°Åä, Ò²¾ÍÊÇÎÒÃdz£³£ËµµÄÒÀÀµ×¢Èë + populateBean(beanName, mbd, instanceWrapper); + // 2\. ³õʼ»¯£¬ÔÚÕâÀï»á´¦Àí aop ²Ù×÷ + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + ... + } + + //ͬÑùµÄ£¬Èç¹û´æÔÚÑ­»·ÒÀÀµ + if (earlySingletonExposure) { + // 3\. ´Ó»º´æÖлñÈ¡ beanName ¶ÔÓ¦µÄbean + Object earlySingletonReference = getSingleton(beanName, false); + // 4\. earlySingletonReference Ϊ null£¬if ÀïÃæµÄÄÚÈݲ»»áÖ´ÐÐ + if (earlySingletonReference != null) { + ... + } + } + + // Ê¡ÂÔ²»ÖØÒªµÄ´úÂë + ... + + return exposedObject; + } + +``` + +´úÂë˵Ã÷ÈçÏ£º + +1. ´¦ÀíÒÀÀµ×¢È룬¾ÍÊÇÕâÀï´¥·¢ÁËµÚ 4 ²¿·ÖµÄÁ÷³Ì£¬Ö´ÐÐÍêµÃµ½µÄ `service2` ÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b294a02be5431c0aa66e5f126a6c6abbb92.png) + + ¿ÉÒÔ¿´µ½£¬´ËʱעÈëµ½ `service2` ÖÐµÄ `service1` ¾ÍÊÇ´úÀí¶ÔÏóÁË£» + +2. ³õʼ»¯£¬ÔÚÕâÀï»á´¦Àí aop ²Ù×÷£¬Ö´ÐÐ aop µÄ·½·¨Îª `AbstractAutoProxyCreator#postProcessAfterInitialization`£¬´úÂëÈçÏ£º + + ``` + // ´«ÈëµÄbeanΪservice2£¬beanNameΪ¡°service2¡± + public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { + if (bean != null) { + Object cacheKey = getCacheKey(bean.getClass(), beanName); + // 1\. earlyProxyReferences Öв¢Ã»ÓÐ service2£¬ifÀïµÄ´úÂë»áÖ´Ðе½ + if (this.earlyProxyReferences.remove(cacheKey) != bean) { + // 2\. ÔÚÕâÀï´¦Àíaop²Ù×÷ + return wrapIfNecessary(bean, beanName, cacheKey); + } + } + return bean; + } + + ``` + + ÒÔÉÏ´úÂëÏÈ´Ó `earlyProxyReferences` ÒÆ³ö `service2`£¬È»ºóÔÙÓë´«ÈëµÄ bean ×ö±È½Ï£¬¶ÔÕÕ×Å 5 ´ó½á¹¹ÖеÄÄÚÈÝ£¬ÎÒÃÇ·¢ÏÖ `service2` ²¢²»ÔÚ `earlyProxyReferences` ÖУ» + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | lambda(service2) | + | `singletonsCurrentlyInCreation` | service1, service2 | + | `earlyProxyReferences` | service1 | + + Òò´Ë£¬ÕâÀï·µ»Ø `null`£¬ÏÔÈ»²»µÈÓÚ´«ÈëµÄ bean£¬Òò´Ë if ÖеĴúÂë»áÖ´ÐУ¬`service2` ½øÐÐ aop¡£Ö´ÐÐÍêÕâÒ»²½ºó£¬µÃµ½µÄ `exposedObject` Ϊ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-d62ebdbb71b76ada963a01c9b8c84b88b00.png) + + `service2` Ò²±ä³ÉÁË´úÀí¶ÔÏó£¬ÇÒÒÑÍê³ÉÁËÒÀÀµ×¢Èë¡£ + +3. ´Ó»º´æÖлñÈ¡ beanName ¶ÔÓ¦µÄ bean£¬Ö´ÐеÄÊÇ `DefaultSingletonBeanRegistry#getSingleton(String, boolean)`£¬´úÂëÈçÏ£º + + ``` + /** + * beanName: service2 + * allowEarlyReference£ºfalse + */ + protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // 1\. ´ÓÒ»¼¶»º´æÖлñÈ¡ service2£¬»ñÈ¡²»µ½£¬·µ»Ønull + Object singletonObject = this.singletonObjects.get(beanName); + // 2\. ÅÐ¶Ï service2 ÊÇ·ñÔÚ´´½¨ÖУ¬·µ»Øtrue£¬Ö´ÐÐifÖеĴúÂë + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { + synchronized (this.singletonObjects) { + // 3\. ´Ó¶þ¼¶»º´æÖлñÈ¡ service2£¬»ñÈ¡²»µ½£¬·µ»Ønull + singletonObject = this.earlySingletonObjects.get(beanName); + // 4\. ´Ëʱ´«ÈëµÄ allowEarlyReference Ϊfalse£¬if¿éµÄ´úÂë²»»áÖ´ÐÐ + if (singletonObject == null && allowEarlyReference) { + ... + } + } + } + return singletonObject; + } + + ``` + + ÎÒÃÇ»¹ÊÇÀ´¿´ Ò»ÑÛ 5 ´ó½á¹¹ÖеÄÄÚÈÝ£¬¶ÔÕâ¸ö·½·¨µÄÖ´ÐоÍһĿÁËÈ»ÁË£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | lambda(service2) | + | `singletonsCurrentlyInCreation` | service1, service2 | + | `earlyProxyReferences` | service1 | + + ·½·¨µÄÖ´ÐУ¬ÔÚ´úÂëÖÐÒѾ­×¢Ê͵úÜÃ÷°×ÁË£¬¾Í¶à˵ÁË£» + +4. µÚ 3 ²½·µ»ØµÄ `earlySingletonReference` Ϊ null£¬if ÀïÃæµÄÄÚÈݲ»»áÖ´ÐС£ + +µ½ÕâÀµÃµ½µÄ `service2` ¾Í·µ»ØÁË¡£ + +#### 5.2 »Øµ½ `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)` + +µÃµ½ `service2` µÄ bean ºó£¬ÎÒÃǻص½ `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)`£¬¼ÌÐø `service2` µÄÁ÷³Ì£¬´úÂëÈçÏ£º + +``` +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + Assert.notNull(beanName, "Bean name must not be null"); + synchronized (this.singletonObjects) { + ... + try { + // 1\. ÔÚÕâÀï½øÐÐbeanµÄ´´½¨ + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (...) { + ... + } + finally { + ... + // 2\. ´´½¨Íê³Éºó£¬×öһЩ¼ì²é²Ù×÷ + // ÕâÀï»á°Ñ service2 ´Ó singletonsCurrentlyInCreation ÒÆ³ý + afterSingletonCreation(beanName); + } + if (newSingleton) { + // 3\. ½«¸Ã¶ÔÏóÌí¼Óµ½ beanFactory ÖУ¬Õâ¸ö·½·¨»áɾ³ý¶þÈý¼¶»º´æ + addSingleton(beanName, singletonObject); + } + ... + return singletonObject; + } +} + +``` + +˵Ã÷ÈçÏ£º + +1. Àú¾­Ç§ÄÑÍòÏÕ£¬`singletonFactory.getObject()` ÖÕÓÚÖ´ÐÐÍêÁË£¬ÔÚÖî¶àÇúÕÛÖ®ºó£¬×îÖÕ»¹Êǵõ½ÁË `service2` µÄ´úÀí¶ÔÏó£» + +2. `service2` ´´½¨Íê³Éºó£¬¾Í»á½«Æä´Ó `singletonsCurrentlyInCreation` ÒÆ³ý£¬·½·¨±È½Ï¼òµ¥£¬¾Í²»¶à˵ÁË£¬ÕâÒ»²½µÃµ½µÄ 5 ´ó½á¹¹ÈçÏ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | lambda(service2) | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | service1 | + +3. ½Ó×Å£¬¾ÍÊÇ»º´æµÄ´¦Àí£¬·½·¨Îª `DefaultSingletonBeanRegistry#addSingleton`£º + + ``` + /* + * beanName£ºservice2 + * singletonObject£ºservice2$$EnhancerBySpringCGLIB(service2µÄ´úÀí¶ÔÏó) + */ + protected void addSingleton(String beanName, Object singletonObject) { + synchronized (this.singletonObjects) { + // ¶ÔÈý¸ö»º´æ½øÐÐput¡¢remove²Ù×÷£¬×îÖÕÖ»ÔÚÒ»¼¶»º´æÖÐÓиöÔÏó + this.singletonObjects.put(beanName, singletonObject); + this.singletonFactories.remove(beanName); + this.earlySingletonObjects.remove(beanName); + this.registeredSingletons.add(beanName); + } + } + + ``` + + ·½·¨±È½Ï¼òµ¥£¬¾Í²»¶à˵ÁË£¬×îÖÕ 5 ´ó½á¹¹Îª£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | service2$$EnhancerBySpringCGLIB | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | service1 | + +µ½´Ë£¬`service2` »ñÈ¡Íê³É£¬×îÖÕ±£´æµ½ `singletonObjects` ÖÐµÄ bean Ϊ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-763dad35376ed1d88086c45f01ee9c4cedd.png) + +±¾½ÚµÄ×îºó£¬ÎÒÃÇÒ²À´¿´Ï 5 ¸ö½á¹¹ÖеÄÄÚÈÝ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ------------------------------- | +| `singletonObjects` | service2$$EnhancerBySpringCGLIB | +| `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | +| `singletonFactories` | | +| `singletonsCurrentlyInCreation` | service1 | +| `earlyProxyReferences` | service1 | + +ÖÁ´Ë£¬`service2` »ñÈ¡Íê³É£¬½Ó×ÅÈÃÎÒÃǼÌÐø `service1` µÄ»ñÈ¡Á÷³Ì¡£ + +### 6 ¼ÌÐø `service1` µÄ»ñÈ¡Á÷³Ì + +ÔÚµÚ 2 ½ÚÖУ¬ÒòΪ `servic1` ÐèҪעÈë `service2`£¬²ÅÓÐÁËµÚ 3 ½Ú»ñÈ¡ `service2` µÄÁ÷³Ì£¬È»ºóÓÖ¾­¹ýһϵÁеIJÙ×÷ºó£¬×îÖÕÔÚµÚ 5 ½Ú³É¹¦µØ»ñÈ¡µ½ÁË `service2` µÄ´úÀí¶ÔÏó `service2$$EnhancerBySpringCGLIB`£¬Õâ²ÅÈà `service2` µÃÒÔÍê³ÉÒÀÀµ×¢Èë¡£ + +½ÓÏÂÀ´£¬ÈÃÎÒÃÇÑÓÐøµÚ 2 ½Ú£¬»Øµ½ `AbstractAutowireCapableBeanFactory#doCreateBean` ·½·¨£¬¼ÌÐø `service1` µÄÁ÷³Ì¡£ + +#### 6.1 »Øµ½ `AbstractAutowireCapableBeanFactory#doCreateBean` + +´úÂëÈçÏ£º + +``` +protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, + final @Nullable Object[] args) throws BeanCreationException { + + // ÉÏÃæµÄ´úÂëÔÚ 3.1 ÖÐÒѾ­·ÖÎö¹ýÁË£¬¾Í²»ÔÙ·ÖÎöÁË + ... + + Object exposedObject = bean; + try { + // 1\. ¸ºÔðÊôÐÔ×°Åä, Ò²¾ÍÊÇÎÒÃdz£³£ËµµÄÒÀÀµ×¢Èë + populateBean(beanName, mbd, instanceWrapper); + // 2\. ³õʼ»¯£¬ÔÚÕâÀï»á´¦Àí aop ²Ù×÷ + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + ... + } + + //ͬÑùµÄ£¬Èç¹û´æÔÚÑ­»·ÒÀÀµ + if (earlySingletonExposure) { + // 3\. ´Ó»º´æÖлñÈ¡ beanName ¶ÔÓ¦µÄbean + Object earlySingletonReference = getSingleton(beanName, false); + // 4\. earlySingletonReference ²»Îª null£¬if ÀïÃæµÄÄÚÈÝ»áÖ´ÐÐ + if (earlySingletonReference != null) { + if (exposedObject == bean) { + // 5\. ½«·µ»ØµÄ¶ÔÏó¸³Öµ¸ø exposedObject£¬·µ»Ø¶ÔÏóΪ´úÀí¶ÔÏó + exposedObject = earlySingletonReference; + } + } + } + + // Ê¡ÂÔ²»ÖØÒªµÄ´úÂë + ... + + return exposedObject; + } + +``` + +´úÂë˵Ã÷ÈçÏ£º + +1. ´¦ÀíÒÀÀµ×¢È룬¾ÍÊÇÕâÀï´¥·¢ÁËµÚ 3 ½ÚµÄÁ÷³Ì£¬Ö´ÐÐÍêµÃµ½µÄ `service1` ÈçÏ£º + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-3883285be5904e056bc517d35bfd44c47a0.png) + + ¿ÉÒÔ¿´µ½£¬`service1` ÒѾ­Íê³ÉÁËÒÀÀµ×¢È룬ÇÒ´ËʱעÈëµ½ `service1` ÖÐµÄ `service2` ÒѾ­ÊÇ´úÀí¶ÔÏóÁË£¬²»¹ý `service1` ´Ëʱ»¹ÊÇԭʼ¶ÔÏó£¬ÎÒÃǼÌÐøÍùÏ¿´£» + +2. ³õʼ»¯£¬ÔÚÕâÀï»á´¦Àí aop ²Ù×÷£¬Ö´ÐÐ aop µÄ·½·¨Îª `AbstractAutoProxyCreator#postProcessAfterInitialization`£¬´úÂëÈçÏ£º + + ``` + // ´«ÈëµÄbeanΪservice1£¬beanNameΪ¡°service1¡± + public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { + if (bean != null) { + Object cacheKey = getCacheKey(bean.getClass(), beanName); + // 1\. earlyProxyReferences ÖÐÓÐ service1£¬ifÀïµÄ´úÂë²»»áÖ´Ðе½ + if (this.earlyProxyReferences.remove(cacheKey) != bean) { + // 2\. ÔÚÕâÀï´¦Àíaop²Ù×÷£¬service1 ÒѾ­Ö´ÐйýaopÁË + return wrapIfNecessary(bean, beanName, cacheKey); + } + } + return bean; + } + + ``` + + ÒÔÉÏ´úÂëÏÈ´Ó `earlyProxyReferences` ÒÆ³ö `service1`£¬È»ºóÔÙÓë´«ÈëµÄ bean ×ö±È½Ï£¬¶ÔÕÕ×Å 5 ´ó½á¹¹ÖеÄÄÚÈÝ£¬ÎÒÃÇ·¢ÏÖ `service1` ´Ëʱ¾ÍÔÚ `earlyProxyReferences` ÖУ» + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | service2$$EnhancerBySpringCGLIB | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | service1 | + + Òò´Ë£¬ÕâÀï»á·µ»Ø `service1`£¬if ÖеĴúÂë²»»áÖ´ÐС£Ö´ÐÐÍêÕâÒ»²½ºó£¬5 ¸ö½á¹¹ÖеÄÄÚÈÝΪ£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | service2$$EnhancerBySpringCGLIB | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | | + +3. ´Ó»º´æÖлñÈ¡ beanName ¶ÔÓ¦µÄ bean£¬Ö´ÐеÄÊÇ `DefaultSingletonBeanRegistry#getSingleton(String, boolean)`£¬´úÂëÈçÏ£º + + ``` + /** + * beanName: service1 + * allowEarlyReference£ºfalse + */ + protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // 1\. ´ÓÒ»¼¶»º´æÖлñÈ¡ service1£¬»ñÈ¡²»µ½£¬·µ»Ønull + Object singletonObject = this.singletonObjects.get(beanName); + // 2\. ÅÐ¶Ï service1 ÊÇ·ñÔÚ´´½¨ÖУ¬·µ»Øtrue£¬Ö´ÐÐifÖеĴúÂë + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { + synchronized (this.singletonObjects) { + // 3\. ´Ó¶þ¼¶»º´æÖлñÈ¡ service1£¬ÄÜ»ñÈ¡µ½ + singletonObject = this.earlySingletonObjects.get(beanName); + // 4\. singletonObject ²»Îªnull£¬if¿éµÄ´úÂë²»»áÖ´ÐÐ + if (singletonObject == null && allowEarlyReference) { + ... + } + } + } + return singletonObject; + } + + ``` + + ÎÒÃÇ¿´ Ò»ÑÛ 5 ´ó½á¹¹ÖеÄÄÚÈÝ£¬¶ÔÕâ¸ö·½·¨µÄÖ´ÐоÍһĿÁËÈ»ÁË£º + + | ½á¹¹ | ÄÚÈÝ | + | ------------------------------- | ------------------------------- | + | `singletonObjects` | service2$$EnhancerBySpringCGLIB | + | `earlySingletonObjects` | service1$$EnhancerBySpringCGLIB | + | `singletonFactories` | | + | `singletonsCurrentlyInCreation` | service1 | + | `earlyProxyReferences` | | + + ·½·¨µÄÖ´ÐУ¬ÔÚ´úÂëÖÐÒѾ­×¢Ê͵úÜÃ÷°×ÁË£¬¾Í¶à˵ÁË£¬ÕâÒ»²½»á°Ñ `service1` µÄ´úÀí¶ÔÏ󷵻أ» + +4. ÉÏÒ»²½µÃµ½µÄ `earlySingletonReference` Ϊ + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-425636328820897c75890f01614d94fe9dd.png) + + ´úÀí¶ÔÏóÒѾ­·µ»ØÀ´ÁË£¬¿ÉÒÔ¼ÌÐøÖ´ÐÐ if ¿éµÄ´úÂëÁË£» + +5. ×îºóÒ»²½ÊǸ³Öµ²Ù×÷£¬½«·µ»ØµÄ¶ÔÏó¸³Öµ¸ø `exposedObject`£¬È»ºó·µ»Ø `exposedObject`£¬×îÖÕ·µ»ØµÄ¶ÔÏóΪ `service1` µÄ´úÀí¶ÔÏó¡£ + +µ½ÁËÕâÒ»²½£¬`service1` µÄ´úÀí¶ÔÏóÒ²»ñÈ¡Íê³ÉÁË¡£ + +#### 6.2 »Øµ½ `DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)` + +ÕâÒ»²½µÄ²Ù×÷ÊÇÇåÀíһЩ»º´æ£¬¾Í²»ÔÙ·ÖÎöÁË£¬×îÖÕ 5 ¸ö½á¹¹ÖеÄÄÚÈÝÈçÏ£º + +| ½á¹¹ | ÄÚÈÝ | +| ------------------------------- | ------------------------------------------------------------ | +| `singletonObjects` | service2?????????????????????,???????1EnhancerBySpringCGLIB,service1" role="presentation">EnhancerBySpringCGLIB | +| `earlySingletonObjects` | | +| `singletonFactories` | | +| `singletonsCurrentlyInCreation` | | +| `earlyProxyReferences` | | + +ͨ¹ýµ÷ÊÔ£¬²é¿´µÃµ½ `singletonObjects` ÖжÔÏóÈçÏ£º + +1. service1 + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-b6ba35a7a701056ae55a8f9b2ef9345cac5.png) + +2. service2 + + ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-563d6e323ef8fc5e9fd02f7a938ddbac655.png) + +¿ÉÒÔ¿´µ½£¬`singletonObjects` ÖÐÁ½Õß¶¼ÊÇ´úÀí¶ÔÏ󣬱˴Ë×¢ÈëµÄ£¬Ò²ÊÇ´úÀí¶ÔÏó£¬Ñ­»·ÒÀÀµµÃµ½Á˽â¾ö¡£ + +### 4\. ×ܽá + +spring Ñ­»·ÒÀÀµµÄ·ÖÎöµ½ÕâÀï¾Í½áÊøÁË£¬Ñ­»·ÒÀÀµÓнâµÄºËÐÄÔÚÓÚ**¶ÔÏó¿ÉÒÔÌáǰ½øÐÐ aop**£¬spring ÕýÊÇÀûÓÃÕâÒ»µã£¬ÔÙÅäºÏ 5 ´ó½á¹¹´æ´¢±ØÒªµÄÐÅÏ¢£¬Ò»²½²½½â¾öÑ­»·ÒÀÀµÎÊÌâ¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4815992](https://my.oschina.net/funcy/blog/4815992) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" new file mode 100644 index 0000000..d83f8b5 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\233\221\345\220\254\345\231\250\346\263\250\350\247\243EventListener.md" @@ -0,0 +1,662 @@ +×î½üÔÚÊáÀí `BeanFactoryPostProcessor` ¹¦ÄÜʱ£¬·¢ÏÖÁË´¦Àíʼþ¼àÌýµÄÁíÒ»ÖÖ´¦Àí·½Ê½£ºÊ¹Óà `@EventListener` ×¢½â£¬´¦ÀíÀàµÄÊÇ `EventListenerMethodProcessor`£¬ÎÒÃÇÏÈ´ÓÒ»¸öʾÀý³ö·¢£¬ÔÙÖð²½·ÖÎö `@EventListener` µÄ´¦Àí¹ý³Ì¡£ + +### 1. `@EventListener` ʹÓÃʾÀý + +Ïȶ¨ÒåÒ»¸öʼþ£º + +``` +public class MyApplicationEvent extends ApplicationEvent { + + private static final long serialVersionUID = -1L; + + public MyApplicationEvent(Object source) { + super(source); + } +} + +``` + +ÔÙ×¼±¸Ò»¸öʼþ¼àÌýÆ÷£¬Õâ´ÎʹÓà `@EventListener` Ö¸¶¨¼àÌýÆ÷£º + +``` +@Configuration +public class Demo08Config { + + /** + * ÕâÊǸöʼþ¼àÌýÆ÷ + */ + @EventListener(MyApplicationEvent.class) + public void listener(MyApplicationEvent event) { + System.out.println("@EventListener¼àÌýµ½ÁËʼþ£º" + + Thread.currentThread().getName() + " | " + event.getSource()); + } +} + +``` + +È»ºó·¢²¼Ê¼þ£º + +``` +@ComponentScan +public class Demo08Main { + + public static void main(String[] args) { + ApplicationContext context = new AnnotationConfigApplicationContext(Demo08Config.class); + // ·¢²¼Ê¼þ + context.publishEvent(new MyApplicationEvent( + Thread.currentThread().getName() + " | ×Ô¶¨Òåʼþ ...")); + } +} + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +@EventListener¼àÌýµ½ÁËʼþ£ºmain | main | ×Ô¶¨Òåʼþ ... + +``` + +¿ÉÒÔ¿´µ½£¬±» `@EventListener` ±ê¼ÇµÄ·½·¨È·ÊµÍê³ÉÁËʼþµÄ¼àÌý¡£ + +### 2. `@EventListener` ¸ÅÀÀ + +ʹÓà `@EventListener` ÈÃÎÒÃÇÃâ³ýÁËʵÏÖ `ApplicationListener` ¾ÍÄÜʵÏÖʼþ¼àÌý£¬¼ò»¯ÁË´úÂëµÄ¿ª·¢¡£±¾½ÚÎÒÃÇÀ´¿´¿´ `@EventListener` ÄÜΪÎÒÃÇ×öЩʲô¡£ + +`@EventListener` µÄ´úÂëÈçÏ£º + +``` +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface EventListener { + + /** + * classes µÄ±ðÃû + * ÓÃÀ´Ö¸¶¨¼àÌýµÄʼþ£¬¿ÉÒÔͬʱ¼àÌý¶à¸öʼþ + */ + @AliasFor("classes") + Class[] value() default {}; + + /** + * value µÄ±ðÃû + * ÓÃÀ´Ö¸¶¨¼àÌýµÄʼþ£¬¿ÉÒÔͬʱ¼àÌý¶à¸öʼþ + */ + @AliasFor("value") + Class[] classes() default {}; + + /** + * ¿ÉÒÔÖ¸¶¨Ò»¸öÌõ¼þ£¬Ìõ¼þ³ÉÁ¢Ê±·½·¨²Å»áÖ´ÐÐ + * Ö§³Öspring el ±í´ïʽ + */ + String condition() default ""; + +} + +``` + +´Ó´úÂëÀ´¿´£¬`@EventListener` ÌṩÁËÁ½´ó¹¦ÄÜ£º + +* Ö¸¶¨¼àÌýµÄʼþ£¬¿ÉÒÔÖ¸¶¨¶à¸öʼþ +* Ö¸¶¨Ò»¸öÌõ¼þ£¬Ìõ¼þ³ÉÁ¢Ê±·½·¨²Å»áÖ´ÐУ¬Ìõ¼þÖ§³Ö spring EL ±í´ïʽ + +Á˽â `@EventListener` ÌṩµÄ¹¦Äܺ󣬽ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ spring ÊÇÈçºÎ´¦ÀíÕâ¸ö×¢½âµÄ¡£ + +### 3. `@EventListener` µÄ´¦Àí£º`EventListenerMethodProcessor` + +ÎÄÕ¿ªÆª±ã˵¹ý£¬¹ØÓÚ `@EventListener` µÄ¹¦ÄÜ£¬ÎÒÊÇÔÚÊáÀí `BeanFactoryPostProcessor` ¹¦ÄÜʱ·¢Ïֵ쬵±Ê±·¢ÏÖ `BeanFactoryPostProcessor` µÄʵÏÖÀà `EventListenerMethodProcessor` »á´¦Àí `@EventListener` ×¢½â£¬½ÓÏÂÀ´¾Í´Ó´úÂë½Ç¶ÈÀ´·ÖÎö `EventListenerMethodProcessor` ´¦Àí `@EventListener` µÄÁ÷³Ì¡£ + +Ê×ÏÈÎÒÃÇÀ´ÈÏʶÏ `EventListenerMethodProcessor`£º + +``` +public class EventListenerMethodProcessor + implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor { + ... +} + +``` + +ËüÖ÷ÒªÊÇʵÏÖÁËÁ½¸ö½Ó¿Ú£º + +* `BeanFactoryPostProcessor`£º´óÃû¶¦¶¦µÄ `BeanFactoryPostProcessor` °¡£¬¿ÉÒÔ¶¨ÖÆ»¯ `BeanFactory` µÄһЩÐÐΪ£» +* `SmartInitializingSingleton`£º´¦Àíµ¥Àý bean µÄ³õʼ»¯²Ù×÷£¬Ö´ÐÐʱ»úÊÇÔÚ `bean` ³õʼ»¯Íê³ÉÖ®ºó¡£ + +ÎÒÃÇÏÈÀ´¿´¿´Ëü¶Ô `BeanFactoryPostProcessor#postProcessBeanFactory` µÄʵÏÖ£º + +``` + @Nullable + private List eventListenerFactories; + + /** + * ÕâÊÇ BeanFactoryPostProcessor µÄ postProcessBeanFactory(...) ·½·¨. + * ÔÚÕâ¸ö·½·¨À½öÊÇ»ñÈ¡ÁË EventListenerFactory£¬È»ºó±£´æÔÚ eventListenerFactoriesÖÐ. + * spring ĬÈÏÌṩµÄ EventListenerFactory ÓÐÁ½¸ö£º + * 1\. DefaultEventListenerFactory£ºspring ĬÈ쵀 + * 2\. TransactionalEventListenerFactory£º´¦ÀíÊÂÎñ¼àÌýµÄ + * Õⲿ·Ö²¢Ã»ÓÐ×öʲô¡£ + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + + Map beans = beanFactory + .getBeansOfType(EventListenerFactory.class, false, false); + List factories = new ArrayList<>(beans.values()); + // ÅÅÐò + AnnotationAwareOrderComparator.sort(factories); + this.eventListenerFactories = factories; + } + +``` + +Õâ¸ö·½·¨»¹ÊDZȽϼòµ¥µÄ£¬¾ÍÖ»ÊÇ´ÓÈÝÆ÷ÖлñÈ¡ÁË `EventListenerFactory` ²¢¸³Öµ¸øÁË `eventListenerFactories`¡£ + +`EventListenerFactory` µÄ¹¦ÄÜÊÇÓÃÀ´Éú³É `ApplicationListener`£¬ºóÃæÎÒÃÇ»á·ÖÎö spring ÊÇÈçºÎ°Ñ `@EventListener` ±ê¼ÇµÄ·½·¨×ª»»³É `ApplicationListener` ¶ÔÏóµÄ¡£´Ó´úÂëÀ´¿´£¬spring ÌṩµÄ `EventListenerFactory` ÓÐÁ½¸ö£º + +* `DefaultEventListenerFactory`£ºspring ĬÈ쵀 +* `TransactionalEventListenerFactory`£º´¦ÀíÊÂÎñ¼àÌýµÄ + +½ÓÏÂÀ´ÎÒÃÇÔÙÀ´¿´¿´Ëü¶Ô `SmartInitializingSingleton#afterSingletonsInstantiated()` µÄʵÏÖ£º + +``` + /** + * Õâ¸ö·½·¨ÊÇ SmartInitializingSingleton µÄ afterSingletonsInstantiated() ·½·¨. + * »áÔÚbean³õʼ»¯Íê³Éºóµ÷Óᣠ+ * ÔÚÕâ¸ö·½·¨ÖУ¬Ö÷ÒªÊǽ«±ê¼ÇÁË @EventListener µÄ·½·¨×ª»»³É ApplicationListener ¶ÔÏ󣬲¢×¢²áµ½¼àÌýÆ÷ÖÐ + */ + @Override + public void afterSingletonsInstantiated() { + ConfigurableListableBeanFactory beanFactory = this.beanFactory; + Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set"); + String[] beanNames = beanFactory.getBeanNamesForType(Object.class); + for (String beanName : beanNames) { + if (!ScopedProxyUtils.isScopedTarget(beanName)) { + Class type = null; + try { + // »ñÈ¡ aop ´úÀí¶ÔÓ¦µÄÄ¿±êÀ࣬´Ó beanName ¶ÔÓ¦µÄ BeanDefinition ÖлñÈ¡. + // Èç¹û»ñÈ¡²»µ½£¬±íʾÊDz»ÊÇ´úÀí¶ÔÏó£¬Ê¹Óà beanFactory.getType(beanName) »ñÈ¡ + type = AutoProxyUtils.determineTargetClass(beanFactory, beanName); + } + catch (Throwable ex) { + ... + } + if (type != null) { + if (ScopedObject.class.isAssignableFrom(type)) { + try { + Class targetClass = AutoProxyUtils.determineTargetClass( + beanFactory, ScopedProxyUtils.getTargetBeanName(beanName)); + if (targetClass != null) { + type = targetClass; + } + } + catch (Throwable ex) { + ... + } + } + try { + // ¾ßÌåµÄ´¦Àí²Ù×÷ + processBean(beanName, type); + } + catch (Throwable ex) { + ... + } + } + } + } + } + +``` + +Õâ¸ö·½·¨µÄÖ´ÐÐÁ÷³ÌÈçÏ£º + +1. »ñÈ¡µ±Ç° `beanFactory` ÖеÄËùÓÐ `bean`£¬µ÷ÓõÄÊÇ `beanFactory.getBeanNamesForType(Object.class)` ·½·¨£¬×¢Òâ´«ÈëµÄ `class` ÊÇ `Object`£¬ÕâÑù¾ÍÄóöÁËËùÓÐµÄ `bean`£» +2. ±éÀúÕâЩ `bean`£¬¶Ôÿһ¸ö `bean`£¬Èç¹ûÊÇ´úÀí¶ÔÏó£¬Ôòµ÷Óà `AutoProxyUtils.determineTargetClass(beanFactory, beanName)` ·½·¨»ñÈ¡ÆäÄ¿±êÀ࣬ÎÒÃÇÖªµÀ×¢½âÊDz»Äܼ̳еģ¬Òª»ñÈ¡ `@EventListener` ±ê¼ÇµÄ·½·¨£¬ÐèÒª´ÓÄ¿±êÀàÈ¥»ñÈ¡£»Èç¹û²»ÊÇ´úÀí¶ÔÏó£¬ÔòÄ¿±êÀà¾ÍÊÇ `bean` ¶ÔÓ¦µÄÀࣻ +3. µ÷Óà `processBean(beanName, type)` ·½·¨½øÒ»²½´¦Àí. + +¿´À´¹Ø¼üÊÇÔÚ `EventListenerMethodProcessor#processBean` ·½·¨ÁË£º + +``` +/** + * ´¦Àí bean£¬ Õâ¸ö·½·¨»á½« @EventListener ×¢½â±ê¼ÇµÄ·½·¨×ª»»Îª ApplicationListener ¶ÔÏ󣬲¢×¢²áµ½¼àÌýÆ÷. + * @param beanName + * @param targetType + */ +private void processBean(final String beanName, final Class targetType) { + if (!this.nonAnnotatedClasses.contains(targetType) && + AnnotationUtils.isCandidateClass(targetType, EventListener.class) && + !isSpringContainerClass(targetType)) { + Map annotatedMethods = null; + + try { + // 1\. ÕÒµ½±ê¼Ç @EventListener µÄ·½·¨ + annotatedMethods = MethodIntrospector.selectMethods(targetType, + (MethodIntrospector.MetadataLookup) method -> + AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); + } + catch (Throwable ex) { + ... + } + + if (CollectionUtils.isEmpty(annotatedMethods)) { + this.nonAnnotatedClasses.add(targetType); + } + else { + ConfigurableApplicationContext context = this.applicationContext; + Assert.state(context != null, "No ApplicationContext set"); + List factories = this.eventListenerFactories; + Assert.state(factories != null, "EventListenerFactory List not initialized"); + // 2\. ʹÓà EventListenerFactory À´²úÉú ApplicationListener ¶ÔÏó + for (Method method : annotatedMethods.keySet()) { + for (EventListenerFactory factory : factories) { + // Åжϵ±Ç° EventListenerFactory ÊÇ·ñÖ§³Öµ±Ç°·½·¨ + if (factory.supportsMethod(method)) { + // Èç¹ûÊÇ´úÀí¶ÔÏó£¬ÔòµÃµ½´úÀíÀàµÄ·½·¨ + Method methodToUse = AopUtils.selectInvocableMethod( + method, context.getType(beanName)); + // Éú³É ApplicationListener£¬´«ÈëµÄÊÇ´úÀíÀàµÄ·½·¨ + ApplicationListener applicationListener = factory + .createApplicationListener(beanName, targetType, methodToUse); + // ³õʼ»¯²Ù×÷£¬ÕâÒ»²½Ö÷ÒªÊǶÔÌõ¼þÅÐ¶ÏÆ÷¸³Öµ£ºthis.evaluator + if (applicationListener instanceof ApplicationListenerMethodAdapter) { + ((ApplicationListenerMethodAdapter) applicationListener) + .init(context, this.evaluator); + } + // Ìí¼Óµ½¼àÌýÆ÷ÖУ¬±£´æ Listener ÊÇSet£¬»á×Ô¶¯È¥ÖØ + context.addApplicationListener(applicationListener); + // Âú×ãÆäÒ»£¬ºóÃæµÄ EventListenerFactory ¾Í²»»áÔÙÖ´ÐÐÁË£¬ + // ÕâÖÖʱºò¾Í»á·¢ÏÖ factories µÄ˳ÐòºÜÖØÒª + // ¿ÉÒÔÔÚ EventListenerFactory ÖÐÖ¸¶¨Ë³Ðò + break; + } + } + } + } + } +} + +``` + +ÔÚ `processBean(...)` ·½·¨µÄ¹Ø¼ü´¦Àí¶¼×÷ÁË×¢ÊÍ£¬ÎÒÃÇÔÙ×ܽáÏ´¦ÀíÁ÷³Ì£º + +1. ÕÒµ½±ê¼Ç `@EventListener` µÄ·½·¨£¬ÕâÀïʹÓõÄÊÇ `MethodIntrospector#selectMethods(...)` ·½·¨½øÐвéÕÒ£¬Õâ¸ö·½·¨»á²éµ½µ±Ç°ÀàµÄ·½·¨¡¢Æä¸¸ÀàµÄ·½·¨ÒÔ¼°½Ó¿ÚµÄĬÈÏ·½·¨£¬Ò»Ö±µ½ Object Ϊֹ£¬ÕâЩ·½·¨ÖÐÈç¹û±ê¼ÇÁË `@EventListener`£¬¶¼»á±»ÕÒµ½£¬×îÖÕ»á°ÑÕÒµ½µÄËùÓз½·¨·Åµ½Ò»¸ö Map ÖУ» +2. ±éÀúÉÏÊöµÃµ½µÄ map£¬½«Ã¿¸ö·½·¨×ª»»Îª `ApplicationListener` ¶ÔÏó£¬ÔÙÌí¼Óµ½ `applicationContext` µÄ `ApplicationListener` ÖУ¬¾ßÌåÁ÷³ÌÈçÏ£º + 1. ±éÀúÇ°ÃæµÃµ½µÄ `EventListenerFactory`£» + 2. ±éÀúµ±Ç°µÄ `EventListenerFactory` ÊÇ·ñÖ§³Ö¸Ã·½·¨£¬Ö§³ÖÔò½øÐÐÏÂÒ»²½£¬²»Ö§³ÖÔò²»´¦Àí£» + 3. ¶ÔÓÚ´úÀí¶ÔÏó£¬ÕÒµ½µ±Ç°·½·¨µÄ´úÀí·½·¨£¬×îÖÕÖ´ÐеÄÒ²ÊÇ´úÀí·½·¨£» + 4. ´´½¨ `ApplicationListener` ¶ÔÏ󣬹¹Ôì·½·¨µÄ²ÎÊý»á´«Èë `method`£¬¶ÔÓÚ´úÀí¶ÔÏó£¬Õâ¸ö `method` ÊÇ´úÀí¶ÔÏóµÄ `method`£» + 5. ¶ÔÓÚ `ApplicationListenerMethodAdapter` ʵÀý£¬½øÐгõʼ»¯²Ù×÷£¬Ö÷ÒªÊǸ³Öµ£ºthis.evaluator£» + 6. ½«µÃµ½µÄ `ApplicationListener` Ìí¼Óµ½¼àÌýÆ÷ÖУ¬±£´æ `Listener` ÊÇÒ»¸ö `Set`£¬»á×Ô¶¯È¥ÖØ¡£ + +×ÛÉÏËùÊö£¬±» `@EventListener` ±ê¼ÇµÄ·½·¨Ö®ËùÒÔÄܶÔʼþ½øÐмàÌý£¬ÊÇÒòΪ spring ½«¸Ã·½·¨°ü×°³ÉÒ»¸ö `ApplicationListener` Ìí¼Óµ½¼àÌýÆ÷ÁË£¬Ö®ºóÕâ¸ö¼àÌýÆ÷¾Í¸úʵÏÖÁË `ApplicationListener` ½Ó¿ÚµÄ¼àÌýÆ÷Ò»ÑùÄܶÔʼþ½øÐмàÌýÁË¡£ + +### 5. `ApplicationListener` ¶ÔÏóµÄÉú³É + +Ç°Ãæ·ÖÎöÁË `@EventListener` µÄ´¦ÀíÁ÷³Ì£¬±¾½Ú½«À´·ÖÎö `ApplicationListener` ¶ÔÏóµÄÉú³É£¬¶ÔÓ¦µÄ´úÂëΪ£º + +``` +// EventListenerMethodProcessor#processBean +ApplicationListener applicationListener = factory + .createApplicationListener(beanName, targetType, methodToUse); + +``` + +Ç°ÃæÎÒÃǽéÉܹý spring ÌṩµÄ `EventListenerFactory` ÓÐÁ½¸ö£º + +* `DefaultEventListenerFactory`£ºspring ĬÈ쵀 +* `TransactionalEventListenerFactory`£º´¦ÀíÊÂÎñ¼àÌýµÄ + +½ÓÏÂÀ´ÎÒÃǾÍÀ´·ÖÎöÕâÁ½¸ö `EventListenerFactory`¡£ + +#### 5.1 `DefaultEventListenerFactory` + +``` +public class DefaultEventListenerFactory implements EventListenerFactory, Ordered { + ... + + /** + * ²»À¢ÊÇĬÈϵÄʵÏÖ£¬ÄÜÖ§³ÖËùÓеķ½·¨ + */ + @Override + public boolean supportsMethod(Method method) { + return true; + } + + /** + * ´´½¨²Ù×÷ + */ + @Override + public ApplicationListener createApplicationListener(String beanName, + Class type, Method method) { + return new ApplicationListenerMethodAdapter(beanName, type, method); + } +} + +``` + +¶ÔÒÔÉÏ´úÂë˵Ã÷ÈçÏ£º + +1. `DefaultEventListenerFactory` ÊÇ spring ÌṩµÄĬÈÏʵÏÖ£¬ÄÜÖ§³ÖËùÓбê¼ÇÁË `@EventListener` µÄ·½·¨£» +2. ´´½¨ `ApplicationListener` µÄ·½·¨ÊÇ `createApplicationListener`£¬Ëüµ÷ÓõÄÊÇ `ApplicationListenerMethodAdapter` µÄ¹¹Ôì·½·¨¡£ + +ÎÒÃǼÌÐø¿´ `ApplicationListenerMethodAdapter`£º + +``` +public class ApplicationListenerMethodAdapter implements GenericApplicationListener { + + ... + + public ApplicationListenerMethodAdapter(String beanName, Class targetClass, Method method) { + this.beanName = beanName; + // ´¦Àí·½·¨ + this.method = BridgeMethodResolver.findBridgedMethod(method); + this.targetMethod = (!Proxy.isProxyClass(targetClass) ? + AopUtils.getMostSpecificMethod(method, targetClass) : this.method); + this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass); + + EventListener ann = AnnotatedElementUtils + .findMergedAnnotation(this.targetMethod, EventListener.class); + // ½âÎöÖ§³ÖµÄʼþ + this.declaredEventTypes = resolveDeclaredEventTypes(method, ann); + // ×¢½âÖ¸¶¨µÄÌõ¼þ + this.condition = (ann != null ? ann.condition() : null); + this.order = resolveOrder(this.targetMethod); + } + + ... +} + +``` + +`ApplicationListenerMethodAdapter` µÄ¹¹Ôì·½·¨¾ÍÊÇÒ»¶ÑµÄ¸³Öµ²Ù×÷£¬ÕâÀïÎÒÃÇÖØµãÀ´¹Ø×¢ÏÂʼþµÄ´¦Àí£º + +``` +public class ApplicationListenerMethodAdapter implements GenericApplicationListener { + + ... + + /** + * ½âÎö·½·¨ÄܼàÌýµÄʼþÀàÐÍ + */ + private static List resolveDeclaredEventTypes(Method method, + @Nullable EventListener ann) { + // ·½·¨×î¶àÖ»ÄÜÓÐÒ»¸ö²ÎÊý£¬¼àÌýµÄʼþÀàÐÍÓÉ @EventListener Ö¸¶¨ + int count = method.getParameterCount(); + if (count > 1) { + throw new IllegalStateException( + "Maximum one parameter is allowed for event listener method: " + method); + } + + if (ann != null) { + // ʼþ¿ÉÒÔÖ¸¶¨¶à¸ö£¬»ñÈ¡µÄÊÇ classes ÊôÐÔÖµ + Class[] classes = ann.classes(); + if (classes.length > 0) { + List types = new ArrayList<>(classes.length); + for (Class eventType : classes) { + types.add(ResolvableType.forClass(eventType)); + } + return types; + } + } + + if (count == 0) { + throw new IllegalStateException( + "Event parameter is mandatory for event listener method: " + method); + } + return Collections.singletonList(ResolvableType.forMethodParameter(method, 0)); + } +} + +``` + +´Ó´úÂë¿ÉÒԵóöÈçϽáÂÛ£º + +1. ±» `@EventListener` ±ê¼ÇµÄ·½·¨£¬²ÎÊý×î¶àÖ»ÄÜÓÐÒ»¸ö +2. ·½·¨ÄܼàÌýµÄʼþ¿ÉÒÔÓжà¸ö£¬ÓÉ `@EventListener` Ö¸¶¨ + +#### 5.2 `TransactionalEventListenerFactory` + +½ÓÏÂÀ´ÎÒÃÇÔÙÀ´¿´¿´ `TransactionalEventListenerFactory`£º + +``` +public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered { + + ... + + /** + * Ö»Ö§³Ö±ê¼ÇÁË @TransactionalEventListener ×¢½âµÄ·½·¨ + */ + @Override + public boolean supportsMethod(Method method) { + return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class); + } + + /** + * ´´½¨µÄ¶ÔÏóÊÇ ApplicationListenerMethodTransactionalAdapter + */ + @Override + public ApplicationListener createApplicationListener(String beanName, + Class type, Method method) { + return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method); + } +} + +``` + +´Ó´úÂëÀ´¿´£¬`TransactionalEventListenerFactory` Óë `DefaultEventListenerFactory` ²î±ðÈçÏ£º + +* `ApplicationListenerMethodTransactionalAdapter` Ö»Ö§³Ö±ê¼ÇÁË `@TransactionalEventListener` µÄ·½·¨£» +* ´´½¨µÄ `ApplicationListener` ʵ¼ÊÀàÐÍΪ `ApplicationListenerMethodTransactionalAdapter`¡£ + +ÎÒÃÇÏÈÀ´¿´¿´ `@TransactionalEventListener` ÊǸöɶ£º + +``` +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +/** + * ±ê¼ÇÁË @EventListener ×¢½â + */ +@EventListener +public @interface TransactionalEventListener { + /** + * ÊÂÎñ´¦Àí + */ + TransactionPhase phase() default TransactionPhase.AFTER_COMMIT; + + /** + * ÊÂÎñ´¦Àí + */ + boolean fallbackExecution() default false; + + /** + * Ö¸¶¨¼àÌýʼþ + */ + @AliasFor(annotation = EventListener.class, attribute = "classes") + Class[] value() default {}; + + /** + * Ö¸¶¨¼àÌýʼþ + */ + @AliasFor(annotation = EventListener.class, attribute = "classes") + Class[] classes() default {}; + + /** + * Ö¸¶¨Ìõ¼þ + */ + String condition() default ""; +} + +``` + +¿ÉÒÔ¿´µ½£¬Õâ¸ö×¢½â±ê¼ÇÁË `@EventListener`£¬Òò´Ë¾ßÓÐÓë `@EventListener` ÏàͬµÄ¹¦ÄÜ¡£ÁíÍ⣬Óë `@EventListener` Ïà±È£¬¶àÁËÁ½¸öÊôÐÔ£º`phase()` Óë `fallbackExecution()`£¬¿´À´ÊÇÓÃÀ´¿ØÖÆÊÂÎñµÄ¡£ + +Á˽âÁË `TransactionalEventListener` Ö®ºó£¬ÎÒÃÇÔÙÀ´¿´¿´ `ApplicationListenerMethodTransactionalAdapter`£º + +``` +/** + * ¼Ì³ÐÁË ApplicationListenerMethodAdapter + */ +class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter { + ... + + private final TransactionalEventListener annotation; + + public ApplicationListenerMethodTransactionalAdapter(String beanName, + Class targetClass, Method method) { + // µ÷Óø¸ÀàµÄ·½·¨ + super(beanName, targetClass, method); + // ÕâÀïµÄ×¢½âÊÇ @TransactionalEventListener + TransactionalEventListener ann = AnnotatedElementUtils + .findMergedAnnotation(method, TransactionalEventListener.class); + if (ann == null) { + throw new IllegalStateException(...); + } + this.annotation = ann; + } + + ... +} + +``` + +´Ó´úÂëÉÏÀ´¿´£¬`ApplicationListenerMethodTransactionalAdapter` ¼Ì³ÐÁË `ApplicationListenerMethodAdapter`£¬Æä¹¹Ôì·½·¨Ò²ÊÇÏȵ÷Óà `ApplicationListenerMethodAdapter` µÄ¹¹Ôì·½·¨£¬È»ºóÔÙ¸ø `annotation` ¸³Öµ¡£ + +### 6\. ʼþ¼àÌý + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´Ê¼þµÄ¼àÌý²Ù×÷¡£ + +#### 6.1 `ApplicationListenerMethodAdapter` ¼àÌýʼþ + +ÔÚÇ°ÃæµÄÎÄÕÂÖУ¬ÎÒÃÇÖªµÀ `ApplicationListener` Òª´¦ÀíÒ»¸öʼþ£¬±ØÐë°üº¬Á½¸ö²Ù×÷£º + +1. Åжϵ±Ç° `ApplicationListener` ÊÇ·ñÖ§³Öµ±Ç°Ê¼þ +2. Èç¹ûÖ§³Ö£¬Ôò½øÐÐʼþ´¦Àí + +ÎÒÃÇÀ´¿´¿´ `ApplicationListenerMethodAdapter` µÄÕâÁ½¸ö²Ù×÷£º + +``` +public class ApplicationListenerMethodAdapter implements GenericApplicationListener { + + ... + + /** + * µ±Ç° listener ÊÇ·ñÖ§³Ö´«ÈëµÄʼþ. + * ʹÓõķ½·¨ÊÇ ResolvableType#isAssignableFrom(ResolvableType)£¬¿ÉÒÔ½«¸Ã·½·¨¼òµ¥Àí½â + * ΪÊÇ¶Ô Class#isAssignableFrom ¹¦ÄܵÄÀ©Õ¹ + */ + @Override + public boolean supportsEventType(ResolvableType eventType) { + for (ResolvableType declaredEventType : this.declaredEventTypes) { + if (declaredEventType.isAssignableFrom(eventType)) { + return true; + } + if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) { + ResolvableType payloadType = eventType + .as(PayloadApplicationEvent.class).getGeneric(); + if (declaredEventType.isAssignableFrom(payloadType)) { + return true; + } + } + } + return eventType.hasUnresolvableGenerics(); + } + + /** + * ʼþ´¦Àí + */ + @Override + public void onApplicationEvent(ApplicationEvent event) { + processEvent(event); + } + + public void processEvent(ApplicationEvent event) { + // ½âÎö²ÎÊý + Object[] args = resolveArguments(event); + // shouldHandle£ºÅжÏÌõ¼þ£¬ÓÉ EventListener.condition() Ìṩ + if (shouldHandle(event, args)) { + // ·´Éäµ÷Ó÷½·¨ + Object result = doInvoke(args); + if (result != null) { + // ´¦ÀíÖ´Ðнá¹û£¬½«·µ»ØµÄ½á¹ûµ±×÷ʼþ·¢²¼³öÈ¥ + handleResult(result); + } + else { + logger.trace("No result object given - no result to handle"); + } + } + } + +} + +``` + +¿ÉÒÔ¿´µ½£¬Åжϵ±Ç°µ±Ç° `ApplicationListener` ÊÇ·ñÖ§³Öµ±Ç°Ê¼þʱ£¬Ê¹ÓõÄÊÇ `ResolvableType#isAssignableFrom(ResolvableType)`£¬¹ØÓÚÕâ¸ö·½·¨±¾ÎIJ¢²»´òËãÉîÈë·ÖÎö£¬¿ÉÒÔ¼òµ¥µØ½«¸Ã·½·¨Àí½âΪÊÇ¶Ô `Class#isAssignableFrom` ¹¦ÄܵÄÀ©Õ¹¡£ + +`ApplicationListenerMethodAdapter` ´¦Àíʼþ¼àÌýµÄÁ÷³ÌÈçÏ£º + +1. ½âÎö²ÎÊý£¬½«´«ÈëµÄ `event` ת»»Îª·½·¨²ÎÊýÖоßÌåµÄÖµ +2. ͨ¹ý·´Éäµ÷Óñ» `@EventListener` ±ê¼ÇµÄ·½·¨ +3. ´¦Àí `@EventListener` ·½·¨µÄ·µ»Ø½á¹û£¬ÔÚ `handleResult(...)` ·½·¨Àï¿ÉÒÔ½«·µ»ØµÄ½á¹ûµ±×÷ʼþÔÙ·¢²¼³öÈ¥ + +¹ØÓÚ `ApplicationListenerMethodAdapter` ´¦Àíʼþ¼àÌý¾Í·ÖÎöµ½ÕâÀïÁË¡£ + +#### 6.2 `ApplicationListenerMethodTransactionalAdapter` ¼àÌýʼþ + +`ApplicationListenerMethodTransactionalAdapter` µÄʼþ¼àÌý±È `ApplicationListenerMethodAdapter` Òª¼òµ¥Ò»Ð©£º + +``` +/** + * ¼Ì³ÐÁË ApplicationListenerMethodAdapter + */ +class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter { + + ... + + // supportsEventType(...) Ò²ÊǼ̳Ð×ÔÄãÀàµÄ·½·¨£¬×ÔÉí²¢ÎÞʵÏÖ + + /** + * ʼþ´¦Àí + */ + @Override + public void onApplicationEvent(ApplicationEvent event) { + // ´ó¸ÅÊÇÔÚ´¦ÀíÊÂÎñµÄ²Ù×÷£¬ºóÃæÓлú»áÔÙÑо¿ + if (TransactionSynchronizationManager.isSynchronizationActive() + && TransactionSynchronizationManager.isActualTransactionActive()) { + TransactionSynchronization transactionSynchronization + = createTransactionSynchronization(event); + TransactionSynchronizationManager.registerSynchronization(transactionSynchronization); + } + else if (this.annotation.fallbackExecution()) { + // µ÷ÓõÄÊǸ¸ÀàµÄ·½·¨ + processEvent(event); + } + else { + // Ö»ÊÇlog ´òÓ¡£¬Ê¡ÂÔ + ... + } + } + + ... +} + +``` + +¶ÔÒÔÉÏ´úÂë˵Ã÷ÈçÏ£º + +1. `ApplicationListenerMethodTransactionalAdapter` ÊÇ `ApplicationListenerMethodAdapter` µÄ×ÓÀ࣬»á¼Ì³ÐÀ´×Ô `ApplicationListenerMethodAdapter` µÄ·½·¨ +2. `ApplicationListenerMethodTransactionalAdapter` ²¢Ã»ÓÐÖØÐ´ `supportsEventType(...)` ·½·¨£¬Òò´ËÒ²ÊÇʹÓà `ApplicationListenerMethodAdapter` µÄ `supportsEventType(...)` ·½·¨À´ÅжÏʼþµÄÖ§³ÖÇé¿ö +3. ÔÚ´¦Àíʼþʱ£¬`ApplicationListenerMethodTransactionalAdapter` »á½øÐÐÊÂÎñÏà¹ØµÄ´¦Àí£¬¾ßÌåµÄ´¦ÀíÂß¼­±¾ÎľͲ»ÉîÈëÁË + +### 7\. ×ܽá + +±¾ÎÄÖ÷Òª·ÖÎöÁË `@EventListener` µÄ´¦ÀíÁ÷³Ì£¬×ܽáÈçÏ£º + +1. `@EventListener` ¿ÉÒÔÖ¸¶¨¼àÌýµÄʼþ¡¢Ê¼þ´¦ÀíµÄÌõ¼þ +2. ´¦Àí `@EventListener` µÄÀàÊÇ `EventListenerMethodProcessor`£¬¸ÃÀà»á°Ñ±» `@EventListener` ±ê¼ÇµÄ·½·¨×ª»»³ÉÒ»¸ö `ApplicationListener` ¶ÔÏó£¬È»ºóÌí¼Óµ½ `ApplicationContext` µÄ¼àÌýÆ÷ÖÐ +3. ·½·¨×ª»»³É `ApplicationListener` ¶ÔÏóµÄ²Ù×÷ÓÉ `EventListenerFactory` Ìṩ£¬spring ÌṩÁËÁ½¸ö `EventListenerFactory`£º + * `DefaultEventListenerFactory`£ºspring ĬÈϵģ¬×ª»»³ÉµÄ `ApplicationListener` Ϊ `ApplicationListenerMethodAdapter` + * `TransactionalEventListenerFactory`£º´¦ÀíÊÂÎñ¼àÌýµÄ£¬×ª»»³ÉµÄ `ApplicationListener` Ϊ `ApplicationListenerMethodTransactionalAdapter` + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4926344](https://my.oschina.net/funcy/blog/4926344) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" new file mode 100644 index 0000000..57e42d1 --- /dev/null +++ "b/docs/Spring\345\205\250\345\256\266\346\241\266/Spring\346\272\220\347\240\201\345\210\206\346\236\220/Spring\351\207\215\350\246\201\346\234\272\345\210\266\346\216\242\347\247\230/Spring\346\216\242\347\247\230\344\271\213\347\273\204\345\220\210\346\263\250\350\247\243\347\232\204\345\244\204\347\220\206.md" @@ -0,0 +1,710 @@ +### 1\. ʲôÊÇ×éºÏ×¢½â£¿ + +ÔÚ spring ÖУ¬ÓÐÒ»ÀàÌØ±ðµÄ×¢½â£º×éºÏ×¢½â¡£¾ÙÀýÀ´Ëµ£¬springmvc ÖУ¬`@Controller` ×¢½âÓÃÀ´ÅäÖ÷ÃÎÊ·¾¶µÈ£¬`@ResponseBody` ×¢½âÓÃÀ´±íÃ÷²»×öÊÓͼäÖȾ£¬Ö±½Óչʾ·½·¨µÄÔËÐнá¹û£¨Ò»°ãÊÇת³É json ·µ»Ø£©£¬¶ø `@RestController` ×éºÏÁËÁ½ÕߵŦÄÜ£¬¿ÉÒÔÅäÖ÷ÃÎÊ·¾¶£¬Í¬Ê±Ò²¿ÉÒÔÖ±½Óչʾ·½·¨µÄÔËÐнá¹û£¬´úÂëÈçÏ£º + +``` +@Controller +@ResponseBody +public @interface RestController { + /** + * ×¢½â±ðÃû + */ + @AliasFor(annotation = Controller.class) + String value() default ""; + +} + +``` + +¿ÉÒÔ¿´µ½£¬`@RestController` Éϱê¼ÇÁËÁ½¸ö×¢½â£º`@Controller` Óë `@ResponseBody`£¬ÕâÑùËü¾ÍͬʱӵÓÐÁËÁ½ÕߵŦÄÜ¡£ + +ÔÙÀ´¿´Ò»¸öÀý×Ó£¬spring ÖУ¬ÎÒÃÇÔÚ±êʶһ¸öÀàΪ spring bean µÄʱºò£¬¿ÉÒÔÓõ½ÕâЩע½â£º`@Component`¡¢`@Repository`¡¢`@Service` µÈ£¬ÔÙ½øÒ»²½¿´Æä´úÂ룬·¢ÏÖ `@Repository`¡¢`@Service` Öж¼ÓÐ `@Component`£º + +``` +@Component +public @interface Repository { + @AliasFor(annotation = Component.class) + String value() default ""; +} + +@Component +public @interface Service { + @AliasFor(annotation = Component.class) + String value() default ""; +} + +``` + +Ò²¾ÍÊÇ˵£¬`@Repository`¡¢`@Service` ¶¼×éºÏÁË `@Component` µÄ¹¦ÄÜ£¡ + +ʵ¼ÊÉÏ£¬Èç¹ûÎÒÃÇ×Ô¼ºÐ´Ò»¸ö×¢½â£¬ÏñÕâÑù£º + +``` +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface MyComponent { + + @AliasFor(annotation = Component.class) + String value() default ""; + +} + +``` + +È»ºóÕâÑùʹÓ㺠+ +``` +@MyComponent("beanObj3") +public class BeanObj3 { + ... +} + +``` + +spring ÒÀÈ»»á°Ñ `BeanObj3` ³õʼ»¯Îª spring bean¡£ + +ÄÇô spring ÊÇÈçºÎ×öµ½ÕâÒ»²½µÄÄØ£¿Êµ¼ÊÉÏ£¬spring ÔÚ´¦Àí `@MyComponent` ʱ£¬»áÅжϸÃ×¢½âÖÐÊÇ·ñ°üº¬ `@Component` ×¢½â£¬Èç¹û°üº¬£¬¾Í»ñÈ¡¸Ã×¢½âµÄÅäÖã¬È»ºó°´ `@Component` µÄ´¦ÀíÂß¼­À´½øÐд¦Àí¡£ + +ͬÑùµØ£¬spring ÔÚ´¦Àí `@RestController` ʱ£¬Èç¹ûµ±Ç°ÊÇ´¦Àí `@Controller` µÄÂß¼­£¬¾Í´Ó `@RestController` ÖлñÈ¡ `@Controller` µÄÅäÖÃÈ»ºó½øÐд¦Àí£¬Èç¹ûµ±Ç°ÊÇ´¦Àí `@ResponseBody` Âß¼­£¬¾Í´Ó `@RestController` ÖлñÈ¡ `@ResponseBody` µÄÅäÖÃÈ»ºó½øÐд¦Àí¡£ + +### 2\. µÝ¹é»ñȡָ¶¨ÀàµÄËùÓÐ×¢½â + +ÎÊÌâÓÖÀ´ÁË£º×éºÏ×¢½âÖеÄ×¢½âÒªÔõô»ñÈ¡ÄØ£¿ + +Èç¹û°´ÕÕ jdk ÌṩµÄ·½·¨£¬ÏñÕâÑù£º + +``` +RestController annotation = BeanObj3.class.getAnnotation(MyComponent.class); + +``` + +µÃµ½µÄ `annotation` ±Ø¶¨Îª `null`£¬Ô­ÒòÊÇ `Class#getAnnotation` ·½·¨Ö»ÄÜ»ñÈ¡µ½ÀàÉÏÖ±½Ó³öÏÖµÄ×¢½â£¬`BeanObj3` ÊÇûÓÐÖ±½Ó³öÏÖ `@Component` µÄ£¬Òò´ËµÃµ½µÄ½á¹ûΪ null£¬°ì·¨Ò²ÐíÄãÒ²Ïëµ½ÁË£¬¾ÍÊǼÌÐøÍù϶ÁÈ¡ "×¢½âµÄ×¢½â"£¬ÓôúÂëʾÒâÏ£¬ÀàËÆÕâÑù£º + +``` +public class AnnotationHandler { + + /** + * ´æ·ÅjdkÌṩµÄԪע½â + */ + private static Set> metaAnnotations = new HashSet<>(); + static { + metaAnnotations.add(Target.class); + metaAnnotations.add(Documented.class); + metaAnnotations.add(Retention.class); + } + + public static void main(String[] args) { + List> list = getAnnotations(BeanObj3.class); + System.out.println(list); + } + + /** + * »ñÈ¡²Ù×÷£¬µÝ¹éµ÷Óà + */ + public static List> getAnnotations(Class cls) { + // ÓÃÀ´´æ·Å¸ÃÀàÉϵÄËùÓÐ×¢½â£¬°üÀ¨×¢½âµÄ×¢½â + List> list = new ArrayList<>(); + // µ÷Óà doGetAnnotations(...) »ñÈ¡ + doGetAnnotations(list, cls); + return list; + } + + /** + * »ñȡע½âµÄ¾ßÌå²Ù×÷ + */ + private static void doGetAnnotations(List> list, Class cls) { + // »ñÈ¡ËùÓеÄ×¢½â + Annotation[] annotations = cls.getAnnotations(); + if(annotations != null && annotations.length > 0) { + for(Annotation annotation : annotations) { + // »ñȡע½âµÄÀàÐÍ + Class annotationType = annotation.annotationType(); + // ¹ýÂËjdkÌṩµÄԪע½â + if(metaAnnotations.contains(annotationType)) { + continue; + } + // µÝ¹éµ÷Óà + doGetAnnotations(list, annotationType); + } + } + // Èç¹ûÊÇ×¢½â£¬¾ÍÌí¼Óµ½ list ÖÐ + if(cls.isAnnotation()) { + list.add(cls); + } + } +} + +``` + +ÎÒÃÇÒª»ñÈ¡ `BeanObj3` ÉÏËùÓÐ×¢½â£¬¾Í¿ÉÒÔÕâÑù²Ù×÷ÁË£º + +``` +// µÃµ½ BeanObj3 ÉϵÄËùÓÐ×¢½â£¬°üÀ¨¡°×¢½âµÄ×¢½â¡± +List> list = AnnotationHandler.getAnnotations(BeanObj3.class); +// ÅÐ¶Ï BeanObj3 µÄ×¢½âÖÐÊÇ·ñ°üº¬ @Component +list.contains(Component.class); + +``` + +ÒÔÉÏ demo »¹ÊDZȽϴֲڣ¬Ê×ÏÈÊÇ jdk µÄԪע½â£¬ÕâÀïÖ»ÅųýÁËÈý¸ö£¬ÕâÈý¸ö¶¼ÊÇÔÚ `@Component` ÖгöÏֵ쬴¦Àí `@Component` Ö®ÉϵÄ×¢½â¶ÁÈ¡ÒѾ­×ã¹»ÁË£»Æä´ÎÒ²ÊÇ×îÖØÒªµÄ£¬¾ÍÊÇûÓлñȡע½âµÄÊý¾Ý¡£ÔÚ spring ÖУ¬×¢½â²¢²»Ö»ÊÇÒ»¸ö±ê¼Ç£¬»¹¿ÉÒÔ¶¨ÒåһϵÁÐÊýÁ¿£¬ÏñÕâÑù£º + +``` +// ¶¨Òå spring bean µÄÃû³ÆÎª beanObj3 +@MyComponent("beanObj3") +public class BeanObj3 { + ... +} + +``` + +¶ø `AnnotationHandler` ²¢²»ÄÜ»ñÈ¡µ½×¢½âµÄÊý¾Ý£¡ + +½ÓÏÂÀ´ÎÒÃÇÀ´¿´¿´ spring ÊÇÔõô×öµ½×¢½âÊý¾ÝµÄ¶ÁÈ¡µÄ¡£ + +### 3\. spring ¶Áȡע½âÐÅÏ¢ + +spring 5.2 ÖУ¬¶ÔÓÚ×¢½âÐÅÏ¢µÄ¶ÁÈ¡ÓÐÌṩÁËÈý¸öÀࣺ + +* `AnnotationMetadataReadingVisitor`£º×¢½âÊý¾ÝµÄ¶ÁÈ¡À࣬»ùÓÚ asm ʵÏÖ£¬²»¹ýÔÚ spring5.2 ÖÐÒѾ­·ÏÆú£¨±ê¼ÇÁË `@Deprecated`£©£¬½¨ÒéʹÓà `SimpleAnnotationMetadataReadingVisitor`£¬Òò´Ë±¾ÎIJ»×÷·ÖÎö +* `SimpleAnnotationMetadataReadingVisitor`£º×¢½âÊý¾ÝµÄ¶ÁÈ¡À࣬»ùÓÚ asm ʵÏÖ£¬spring 5.2 ÖÐÐÂÔöµÄÀ࣬ÓÃÓÚÌæ´ú `AnnotationMetadataReadingVisitor`£¬ÐèҪע½âµÄÊÇ£¬`SimpleAnnotationMetadataReadingVisitor` µÄ·ÃÎʼ¶±ðÊÇĬÈϵģ¬ÎÞ·¨ÔÚËùÔÚ°üÖ®Íâ·ÃÎÊ£¬Í¬Ê±ËüÒ²ÊÇ `final` µÄ£¬²»Äܱ»¼Ì³Ð£¬Òò´ËÎÒÃÇÎÞ·¨Ö±½Ó²Ù×÷Ëü£¬²»¹ý spring ÌṩÁËÒ»¸öÀࣺ`SimpleMetadataReaderFactory`£¬Í¨¹ýËü¾Í¿ÉÒÔʹÓà `SimpleAnnotationMetadataReadingVisitor` ÁË +* `StandardAnnotationMetadata`£º×¢½âÊý¾ÝµÄ¶ÁÈ¡À࣬»ùÓÚ·´ÉäʵÏÖ + +#### 3.1 `SimpleAnnotationMetadataReadingVisitor` + +spring ²¢Ã»ÓÐÌṩֱ½Ó²Ù×÷ `SimpleAnnotationMetadataReadingVisitor` µÄ»ú»á£¬¶øÊÇ·â×°µ½ `SimpleMetadataReaderFactory` ÁË£¬ÎÒÃÇÏÈÀ´¿´¿´Õâ¸öÀࣺ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-f2926fbc022517409bfb6f6641c87a193c2.png) + +¿ÉÒÔ¿´µ½£¬`SimpleMetadataReaderFactory` µÄÀàÖ÷Òª·ÖΪÁ½²¿·Ö£º + +1. ¹¹Ôì·½·¨ +2. ×ÊÔ´µÄ»ñÈ¡ + +ÕâÀïÎÒÃÇÖ±½Ó¿´»ñÈ¡µÄ»ñÈ¡£¬Ò²¾ÍÊÇ `getMetadataReader(...)` ·½·¨£º + +`getMetadataReader(Resource resource)`: ¸ù¾Ý `Resource` ¶ÁÈ¡Êý¾Ý `getMetadataReader(String className)`: ¸ù¾ÝÀàÃû¶ÁÈ¡Êý¾Ý£¬´«ÈëµÄÊÇÈ«ÏÞ¶¨ÀàÃû£¨¼´ ¡°°üÃû¡£ÀàÃû¡±£©£¬´Ó´úÂëÀ´¿´£¬Õâ¸öÀàÃû×îÖÕÒ²»áת»¯Îª `Resource`£¬È»ºóµ÷Óà `getMetadataReader(Resource)` ½øÐжÁÈ¡ + +ÕâÁ½¸ö·½·¨µÄ·µ»ØÖµ¶¼ÊÇ `MetadataReader`£¬ÕâÊǸöɶĨ£¿ÎÒÃǼÌÐøÍùÏ¿´. + +##### `MetadataReader` + +`MetadataReader` µÄ²¿·Ö·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-4a1184918664e8a4bd363d6399ed4092692.png) + +¿ÉÒÔ¿´µ½£¬ËüÊǸö½Ó¿Ú£¨ÕâÀï·µ»ØµÄ¾ßÌåÀàÐ;ÍÊÇ `SimpleMetadataReader` ÁË£©£¬ÀïÃæ¾Í 3 ¸ö·½·¨£º + +* `getResource()`: »ñÈ¡×ÊÔ´ +* `getClassMetadata()`: »ñÈ¡ÀàµÄÔªÊý¾Ý +* `getAnnotationMetadata()`: »ñȡע½âµÄÔªÊý¾Ý + +ÓÉÓÚÊÇ»ñȡע½âµÄÐÅÏ¢£¬ÕâÀïÎÒÃÇÖ»¹Ø×¢ `getAnnotationMetadata()` ·½·¨£º + +``` +AnnotationMetadata getAnnotationMetadata(); + +``` + +Õâ¸ö·½·¨·µ»ØµÄÊÇ `AnnotationMetadata`£¬ÕâÓÖÊǸöɶ£¿ + +##### `AnnotationMetadata` + +ÏÈÀ´¿´¿´ËüµÄ·½·¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-fc19d57bbba3d70da04fd32e6eef511ad4f.png) + +ÕâЩ·½·¨·ÖΪÁ½Àࣺ + +* `getXxx(...)`£º¸ù¾Ý×¢½â»ñÈ¡¶ÔÓ¦µÄÐÅÏ¢ +* `hasXxx(...)`£ºÅжÏÊÇ·ñ°üº¬Ä³×¢½â + +Èç¹û½øÒ»²½¿´Õ⼸¸ö·½·¨µÄĬÈÏʵÏÖ£¬·¢ÏÖ¶¼µ÷Óà `getAnnotations()` ·½·¨£º + +``` +public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata { + + default Set getAnnotationTypes() { + // µ÷ÓÃÁË getAnnotations() + return getAnnotations().stream() + .filter(MergedAnnotation::isDirectlyPresent) + .map(annotation -> annotation.getType().getName()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + default Set getMetaAnnotationTypes(String annotationName) { + // µ÷ÓÃÁË getAnnotations() + MergedAnnotation annotation = getAnnotations().get(annotationName, + MergedAnnotation::isDirectlyPresent); + if (!annotation.isPresent()) { + return Collections.emptySet(); + } + return MergedAnnotations.from(annotation.getType(), SearchStrategy.INHERITED_ANNOTATIONS) + .stream() + .map(mergedAnnotation -> mergedAnnotation.getType().getName()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + default boolean hasAnnotation(String annotationName) { + // µ÷ÓÃÁË getAnnotations() + return getAnnotations().isDirectlyPresent(annotationName); + } + + ... +} + +``` + +ÔÙ½øÒ»²½²é¿´ `getAnnotations()` ·½·¨£¬½øÈëÁË `AnnotatedTypeMetadata`£º + +``` +public interface AnnotatedTypeMetadata { + + /** + * »ñȡע½â + */ + MergedAnnotations getAnnotations(); + + ... + +} + +``` + +ÕâÒ»Ñù¿´£¬Ëƺõ×¢½âµÃµ½µÄÖÕ¼«Àà¾ÍÊÇ `MergedAnnotations` ÁË£¿ÎÒÃǼÌÐøÌ½Ë÷¡£ + +##### `MergedAnnotations` + +`MergedAnnotations` µÄ²¿·Ö×¢ÊÍÈçÏ£º + +> Provides access to a collection of merged annotations, usually obtained from a source such as a {@link Class} or {@link Method}. +> +> Ìṩ¶Ô×éºÏ×¢½âµÄ¼¯ºÏµÄ·ÃÎÊ£¬ÕâЩע½âͨ³£ÊÇ´Ó Class »ò Method Ö®ÀàµÄÀ´Ô´»ñµÃµÄ¡£ + +¿´À´£¬`MergedAnnotations` ²ÅÊÇ×îÖÕµÄ×éºÏ×¢½âµÄ¼¯ºÏÁË£¬ÎÒÃÇÀ´¿´¿´ËüµÄ¼¸¸ö·½·¨£º + +``` +// ÅжÏ×¢½âÊÇ·ñ´æÔÚ£¬»á´ÓËùÓеÄ×¢½âÖÐÅÐ¶Ï + boolean isPresent(Class annotationType); + +// ÅжÏ×¢½âÊÇ·ñ´æÔÚ£¬»á´ÓËùÓеÄ×¢½âÖÐÅжϣ¬ÓëÉÏÃæµÄ·½·¨²»Í¬µÄÊÇ£¬ÕâÀï´«ÈëµÄÊÇ×Ö·û´® +boolean isPresent(String annotationType); + +// ÅжÏÖ±½Ó×¢½âÊÇ·ñ´æÔÚ£¬Ò²¾ÍÊÇÖ»Åжϵ±Ç°ÀàÉÏÓÐûÓиÃ×¢½â£¬²»ÅжÏ×¢½âµÄ×¢½â + boolean isDirectlyPresent(Class annotationType); + +// ¹¦ÄÜͬÉÏ£¬ÕâÀï´«ÈëµÄÀàÐÍÊÇ×Ö·û´®£¬¸ñʽΪ"°üÃû.ÀàÃû" +boolean isDirectlyPresent(String annotationType); + +// »ñȡע½â + MergedAnnotation get(Class annotationType); + +// »ñȡע½â£¬ÕâÀï´«ÈëµÄÀàÐÍÊÇ×Ö·û´®£¬¸ñʽΪ"°üÃû.ÀàÃû" + MergedAnnotation get(String annotationType); + +``` + +´Ó·½·¨ÉÏ´óÖ¿ÉÒÔ¿´³ö£¬`MergedAnnotations` ÊÇ×éºÏ×¢½âµÄ¼¯ºÏ£¬ÌṩµÄ×¢½â¿ÉÒÔÅжÏijע½âÊÇ·ñ´æÔÚ£¬Ò²¿ÉÒÔ»ñÈ¡ÆäÖеÄij¸ö×¢½â¡£ + +##### `MergedAnnotation` + +`MergedAnnotations` ÊÇ×¢½âµÄ¼¯ºÏ£¬ÄÇÕâ¸ö¼¯ºÏÖзŵÄÊÇɶĨ£¿´ÓËüµÄ `get(...)` ·½·¨À´¿´£¬Ëü´æ·ÅµÄÊÇ `MergedAnnotation`£¬ÎÒÃÇÔÙÀ´¿´¿´ `MergedAnnotation` Ö§³ÖµÄ·½·¨£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a3a749f4546d63b49bfd25d2fc2c73dcb6f.png) + +´ÓÒÔÉϵķ½·¨¿ÉÒÔ¿´µ½£¬`MergedAnnotation` ¾ÍÊÇ×¢½âµÄÊý¾Ý³éÏó£¬ËüÌṩÁ˷ḻµÄ api ÓÃÀ´»ñȡע½âµÄÊý¾Ý¡£ + +##### ʹÓÃʾÀý + +ÏÂÃæÀ´¿´¸öʾÀý£º + +``` +// µÃµ½ SimpleMetadataReaderFactory ʵÀý£¬×îÖÕµ÷ÓõÄÊÇ SimpleAnnotationMetadataReadingVisitor À´¶ÁÈ¡ +SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); +MetadataReader metadataReader = readerFactory.getMetadataReader(BeanObj3.class.getName()); +AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); + +// AnnotationMetadata ÌṩÁËÐí¶àµÄ²Ù×÷£¬ÖØµã¹Ø×¢×¢½âÏà¹ØµÄ +Set annotationTypes = annotationMetadata.getAnnotationTypes(); +System.out.println("-------------"); +annotationTypes.forEach(type -> System.out.println(type)); +System.out.println("-------------"); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÖ±½Ó±ê¼Ç @MyComponentµÄ£¬·µ»ØµÄÊÇtrue +boolean exist1 = annotationMetadata.hasAnnotation(MyComponent.class.getName()); +System.out.println("hasAnnotation @MyComponent:" + exist1); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÊÇûÓÐÖ±½Ó±ê¼Ç @ComponentµÄ£¬·µ»ØµÄÊÇfalse +boolean exist2 = annotationMetadata.hasAnnotation(Component.class.getName()); +System.out.println("hasAnnotation @Component:" + exist2); + +// »ñÈ¡ MergedAnnotations +MergedAnnotations annotations = annotationMetadata.getAnnotations(); +System.out.println("-------------"); +annotations.forEach(annotationMergedAnnotation -> System.out.println(annotationMergedAnnotation)); +System.out.println("-------------"); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÊÇûÓÐÖ±½Ó±ê¼Ç @ComponentµÄ£¬·µ»ØµÄÊÇfalse +boolean directlyPresent = annotations.isDirectlyPresent(Component.class); +System.out.println("directlyPresent Component:" + directlyPresent); + +// ÅжÏÓÐûÓÐÕâ¸ö×¢½â£¬BeanObj3 ÉϵÄ@MyComponentÖУ¬±ê¼ÇÁË @Component µÄ£¬·µ»ØµÄÊÇtrue +boolean present = annotations.isPresent(Component.class); +System.out.println("present Component:" + present); + +// »ñÈ¡ @Component ×¢½â +MergedAnnotation mergedAnnotation = annotations.get(Component.class); +// ÓÉÓÚ @MyComponent µÄ value() ¼ÓÁË @AliasFor(annotation = Component.class) +// Òò´ËÕâÀïµÃµ½µÄ value ÊÇ beanObj3 £¨BeanObj3ÀïÕâôָ¶¨µÄ£º@MyComponent("beanObj3")£© +String value = mergedAnnotation.getString("value"); +System.out.println("Component value:" + value); + +// ½« @Component µÄ×¢½âµÄÊý¾Ýת»»Îª AnnotationAttributes +AnnotationAttributes annotationAttributes = mergedAnnotation.asAnnotationAttributes(); +System.out.println(annotationAttributes); + +``` + +ÔËÐУ¬½á¹ûÈçÏ£º + +``` +------------- +org.springframework.learn.explore.demo01.MyComponent +------------- +hasAnnotation @MyComponent:true +hasAnnotation @Component:false +------------- +@org.springframework.learn.explore.demo01.MyComponent(value=beanObj3) +@org.springframework.stereotype.Component(value=beanObj3) +@org.springframework.stereotype.Indexed() +------------- +directlyPresent Component:false +present Component:true +Component value:beanObj3 +{value=beanObj3} + +``` + +##### ²¹³ä£º`AnnotationAttributes` + +²¹³ä˵Ã÷Ï `AnnotationAttributes`£º + +``` +public class AnnotationAttributes extends LinkedHashMap { + ... +} + +``` + +ËüʵÏÖÁË `LinkedHashMap`£¬ÌṩµÄ²¿·Ö·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-a8c8dc975790719c37a8e41a4b0067c517c.png) + +´ÓÕâÀï²»ÄÑ¿´³ö£¬`AnnotationAttributes` ¾ÍÊǰüº¬×¢½âËùÓÐÊôÐÔÖµµÄ map£¬key ΪÊôÐÔÃû£¬value ΪÊôÐÔÖµ¡£ + +#### 3.2 `StandardAnnotationMetadata` + +ÎÒÃǽÓ×ÅÀ´¿´¿´ `StandardAnnotationMetadata`: + +``` +public class StandardAnnotationMetadata extends StandardClassMetadata + implements AnnotationMetadata { + + /** + * Create a new {@code StandardAnnotationMetadata} wrapper for the given Class. + * @param introspectedClass the Class to introspect + * @see #StandardAnnotationMetadata(Class, boolean) + * @deprecated since 5.2 in favor of the factory method + * {@link AnnotationMetadata#introspect(Class)} + */ + @Deprecated + public StandardAnnotationMetadata(Class introspectedClass) { + this(introspectedClass, false); + } + + ... +} + +``` + +`StandardAnnotationMetadata` ʵÏÖÁË `AnnotationMetadata` ½Ó¿Ú£¬¶ÔÓÚ×¢½âµÄ²Ù×÷ÓëÉÏÃæ½éÉÜµÄ `AnnotationMetadata` ²¢ÎÞÌ«´óÇø±ð£¬ÕâÀï¾Í²»×¸ÊöÁË¡£ + +´Ó `StandardAnnotationMetadata` µÄ¹¹Ôì·½·¨À´¿´£¬ËüÒѾ­·ÏÆúÁË£¬ÈÃÎÒÃÇʹÓà `AnnotationMetadata#introspect(Class)` À´»ñÈ¡ `StandardAnnotationMetadata` µÄʵÀý£¬ÓÚÊÇ£¬ÎÒÃÇ¿ÉÒÔÏñÕâÑùÀ´²Ù×÷£º + +``` +// »ñÈ¡µ½µÄ annotationMetadata ʵ¼ÊÉÏÊÇ StandardAnnotationMetadata +AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(BeanObj3.class); + +//----------- ÒÔÏÂÄÚÈÝÓëSimpleAnnotationMetadataReadingVisitor һģһÑù + +// AnnotationMetadata ÌṩÁËÐí¶àµÄ²Ù×÷£¬ÖØµã¹Ø×¢×¢½âÏà¹ØµÄ +Set annotationTypes = annotationMetadata.getAnnotationTypes(); +System.out.println("-------------"); +annotationTypes.forEach(type -> System.out.println(type)); +System.out.println("-------------"); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÖ±½Ó±ê¼Ç @MyComponentµÄ£¬·µ»ØµÄÊÇtrue +boolean exist1 = annotationMetadata.hasAnnotation(MyComponent.class.getName()); +System.out.println("hasAnnotation @MyComponent:" + exist1); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÊÇûÓÐÖ±½Ó±ê¼Ç @ComponentµÄ£¬·µ»ØµÄÊÇfalse +boolean exist2 = annotationMetadata.hasAnnotation(Component.class.getName()); +System.out.println("hasAnnotation @Component:" + exist2); + +// »ñÈ¡ MergedAnnotations +MergedAnnotations annotations = annotationMetadata.getAnnotations(); +System.out.println("-------------"); +annotations.forEach(annotationMergedAnnotation -> System.out.println(annotationMergedAnnotation)); +System.out.println("-------------"); + +// ÕâÀïÊÇÖ±½Ó»ñÈ¡£¬BeanObj3 ÉÏÊÇûÓÐÖ±½Ó±ê¼Ç @ComponentµÄ£¬·µ»ØµÄÊÇfalse +boolean directlyPresent = annotations.isDirectlyPresent(Component.class); +System.out.println("directlyPresent Component:" + directlyPresent); + +// ÅжÏÓÐûÓÐÕâ¸ö×¢½â£¬BeanObj3 ÉϵÄ@MyComponentÖУ¬±ê¼ÇÁË @Component µÄ£¬·µ»ØµÄÊÇtrue +boolean present = annotations.isPresent(Component.class); +System.out.println("present Component:" + present); + +// »ñÈ¡ @Component ×¢½â +MergedAnnotation mergedAnnotation = annotations.get(Component.class); +// ÓÉÓÚ @MyComponent µÄ value() ¼ÓÁË @AliasFor(annotation = Component.class) +// Òò´ËÕâÀïµÃµ½µÄ value ÊÇ beanObj3 £¨BeanObj3ÀïÕâôָ¶¨µÄ£º@MyComponent("beanObj3")£© +String value = mergedAnnotation.getString("value"); +System.out.println("Component value:" + value); + +// ½« @Component µÄ×¢½âµÄÊý¾Ýת»»Îª AnnotationAttributes +AnnotationAttributes annotationAttributes = mergedAnnotation.asAnnotationAttributes(); +System.out.println(annotationAttributes); + +``` + +ÔËÐнá¹ûÈçÏ£º + +``` +------------- +org.springframework.learn.explore.demo01.MyComponent +------------- +hasAnnotation @MyComponent:true +hasAnnotation @Component:false +------------- +@org.springframework.learn.explore.demo01.MyComponent(value=beanObj3) +@org.springframework.stereotype.Component(value=beanObj3) +@org.springframework.stereotype.Indexed() +------------- +directlyPresent Component:false +present Component:true +Component value:beanObj3 +{value=beanObj3} + +``` + +´ÓÉÏÃæµÄʾÀýÀ´¿´£¬ÎÒÃÇÀú¾­Ç§ÐÁÍò¿à£¬×îÖյõ½ÁË `MergedAnnotations`£¬È»ºóͨ¹ýËüÀ´ÅжÏ×¢½âÊÇ·ñ´æÔÚ¡¢»ñȡע½âµÄÖµ¡£ + +#### 3.3 Á½ÕßµÄʹÓó¡¾° + +`SimpleAnnotationMetadataReadingVisitor` Óë `StandardAnnotationMetadata` µÄÖ÷񻂿±ðÔÚÓÚ£¬`SimpleAnnotationMetadataReadingVisitor` ÊÇ»ùÓÚ asm µÄʵÏÖ£¬`StandardAnnotationMetadata` ÊÇ»ùÓÚ·´ÉäµÄʵÏÖ£¬ÄÇÎÒÃÇÔÚʹÓÃʱ£¬Ó¦¸ÃÒªÔõÃ´Ñ¡ÄØ£¿ + +ÓÉÓÚ»ùÓÚ·´ÉäÊÇÒªÏȼÓÀà¼ÓÔØµ½ jvm Öеģ¬Òò´ËÎÒµÄÅжÏÊÇ£¬**Èç¹ûµ±Ç°ÀàûÓмÓÔØµ½ jvm ÖУ¬¾ÍʹÓà `SimpleAnnotationMetadataReadingVisitor`£¬Èç¹ûÀàÒѾ­¼ÓÔØµ½ jvm ÖÐÁË£¬Á½Õß½Ô¿ÉʹÓÃ**¡£ + +ÊÂʵÉÏ£¬ÔÚ spring °üɨÃè½×¶Î£¬¶ÁÈ¡ÀàÉϵÄ×¢½âʱ£¬Ê¹ÓõͼÊÇ `SimpleAnnotationMetadataReadingVisitor`£¬ÒòΪ´ËʱÀಢûÓмÓÔØµ½ jvm£¬Èç¹ûʹÓà `StandardAnnotationMetadata` ¶ÁÈ¡£¬¾Í»áµ¼ÖÂÀàÌáǰ¼ÓÔØ¡£ÀàÌáǰ¼ÓÔØÓÐʲôÎÊÌâÄØ£¿java ÀàÊǰ´Ðè¼ÓÔØµÄ£¬ÓеÄÀà¿ÉÄÜÔÚÕû¸ö jvm ÉúÃüÖÜÆÚÄÚ¶¼Ã»Óõ½£¬Èç¹ûÈ«¶¼¼ÓÔØÁË£¬¾Í°×°×ÀË·ÑÄÚ´æÁË¡£ + +### 4\. spring ÌṩµÄ×¢½â¹¤¾ßÀà + +ÔÚÇ°ÃæµÄʾÀýÖУ¬ÎÒÃÇÊÇÕâÑù¶Áȡע½âµÄ£º + +``` +// ¶ÁÈ¡ annotationMetadata£¬Ò²¿ÉÒÔʹÓà SimpleMetadataReaderFactory ¶ÁÈ¡ +AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(BeanObj3.class); +MergedAnnotations annotations = annotationMetadata.getAnnotations(); + +// ÅжÏ×¢½âÊÇ·ñ´æÔÚ +boolean present = annotations.isPresent(Component.class); +// »ñȡע½âµÄÊôÐÔ +MergedAnnotation mergedAnnotation = annotations.get(Component.class); +AnnotationAttributes annotationAttributes = mergedAnnotation.asAnnotationAttributes(); + +``` + +Ïà¶ÔÀ´Ëµ£¬»ñȡע½âµÄÊôÐÔ²½Öè±È½Ï¶à£¬´ÏÃ÷ÈçÄ㣬¾ÍÏëµ½¿ÉÒÔ½«ÕâЩ²½Öè·â×°µ½Ò»¸ö·½·¨ÖнøÐд¦Àí£¬spring Ò²ÊÇÕâô×öµÄ£¬Õâ¾ÍµÃ½éÉÜ spring ÖÐÓë×¢½âÏà¹ØµÄÁ½¸öÀࣺ`AnnotationUtils` Óë `AnnotatedElementUtils`¡£`AnnotationUtils` ÊÇÖ±½Ó»ñȡע½âµÄÖµ£¬²»»á´¦ÀíÊôÐÔ¸²¸Ç£¬¶ø `AnnotatedElementUtils` »á´¦ÀíÊôÐÔ¸²¸Ç¡£ + +ʲôÊÇÊôÐÔ¸²¸ÇÄØ£¿ + +¾ÙÀýÀ´Ëµ£¬`@MyComponent` ³¤ÕâÑù£º + +``` +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +// ×¢ÒâComponentÖ¸¶¨µÄÖµ£º123 +@Component("123") +public @interface MyComponent { + + @AliasFor(annotation = Component.class) + String value() default ""; + +} + +``` + +ÔÚ `@MyComponent` ×¢½âÖУ¬ÎÒÃÇÖ¸¶¨ÁË `@Component` µÄ `value` ֵΪ ¡°123¡±£¬È»ºóÓÖÕâôָ¶¨ `@MyComponent` µÄ `value` Öµ£º + +``` +@MyComponent("beanObj3") +public class BeanObj3 { + + ... +} + +``` + +×îÖÕ spring ³õʼ»¯µÃµ½µÄ `BeanObj3` µÄÃû³ÆÊÇ `123` »¹ÊÇ `beanObj3` ÄØ£¿´ÓÎÒÃÇÉèÖà `@MyComponent` µÄ `value` Ϊ `beanObj3` À´Ëµ£¬µ±È»ÊÇÏ£Íû bean µÄÃû³ÆÎª `beanObj3`£¬¶ø×îÖÕ spring Ò²ÊÇÕâô×öµÄ£¬Õâ¾ÍÊÇÊôÐÔ¸²¸ÇÁË£º`@MyComponent` µÄ `value` ¸²¸ÇÁË `@Component` µÄ `value` Öµ¡£ + +`AnnotationUtils`/`AnnotatedElementUtils` ÓëÉÏÃæ½éÉÜµÄ `SimpleAnnotationMetadataReadingVisitor`/`StandardAnnotationMetadata` ÊǺιØÏµÄØ£¿ + +ÔÚÎÒÃÇʹÓà `SimpleAnnotationMetadataReadingVisitor`/`StandardAnnotationMetadata` ʱ£¬ÎÒÃÇÐèÒªµÃµ½ `MergedAnnotations` ÔÙ½øÐÐһϵÁвÙ×÷£¨ÅжÏ×¢½âÊÇ·ñ´æÔÚ¡¢»ñȡע½âµÄÊôÐÔÖµµÈ£©£¬Èç¹û½øÈë `AnnotationUtils`/`AnnotatedElementUtils` µÄÔ´Â룬¾Í»á·¢ÏÖËüÃǵÄÏà¹Ø·½·¨Ò²ÊDzÙ×÷ `MergedAnnotations` À࣬±ÈÈç»ñȡע½â£º + +`AnnotationUtils#getAnnotation(AnnotatedElement, Class)` ·½·¨£º + +``` +public static A getAnnotation(AnnotatedElement annotatedElement, + Class annotationType) { + if (AnnotationFilter.PLAIN.matches(annotationType) || + AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { + return annotatedElement.getAnnotation(annotationType); + } + // ͨ¹ý²Ù×÷ MergedAnnotations ½øÐлñÈ¡ + return MergedAnnotations.from(annotatedElement, + SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()) + .get(annotationType).withNonMergedAttributes() + .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); +} + +``` + +`AnnotatedElementUtils#getAllMergedAnnotations(AnnotatedElement, Class)` ·½·¨£º + +``` +public static Set getAllMergedAnnotations( + AnnotatedElement element, Class annotationType) { + return getAnnotations(element).stream(annotationType) + .collect(MergedAnnotationCollectors.toAnnotationSet()); +} + +// AnnotatedElementUtils#getAnnotations ·½·¨£¬Ò²ÊDzÙ×÷ MergedAnnotations µÄ·½·¨ +private static MergedAnnotations getAnnotations(AnnotatedElement element) { + return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, + RepeatableContainers.none()); +} + +``` + +Òò´Ë£¬`AnnotationUtils`/`AnnotatedElementUtils` Óë `SimpleAnnotationMetadataReadingVisitor`/`StandardAnnotationMetadata` µ×²ã¶¼ÊDzÙ×÷ `MergedAnnotations` ÀàµÄ¡£ + +#### 4.1 `AnnotationUtils` + +`AnnotationUtils` Ö§³ÖµÄ²¿·Ö·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-51b0f496180a16d947b8f83285370cabd8f.png) + +ÎÒÃÇÀ´Êµ¼ÊʹÓÃÏÂÕâЩ·½·¨£º + +``` +// ÔÚ BeanObj3 »ñÈ¡ @Component +Annotation annotation = AnnotationUtils.getAnnotation(BeanObj3.class, Component.class); +if(null == annotation) { + System.out.println("×¢½â²»´æÔÚ£¡"); + return; +} +System.out.println("annotation: " + annotation); + +// »ñÈ¡ AnnotationAttributes +AnnotationAttributes annotationAttributes + = AnnotationUtils.getAnnotationAttributes(BeanObj3.class, annotation); +System.out.println("AnnotationAttributes: " + annotationAttributes); + +// »ñÈ¡ annotationAttributeMap +Map annotationAttributeMap = AnnotationUtils.getAnnotationAttributes(annotation); +System.out.println("annotationAttributeMap: " + annotationAttributeMap); + +// »ñÈ¡valueµÄÖµ +Object value = AnnotationUtils.getValue(annotation, "value"); +System.out.println("value: " + value); + +``` + +½á¹ûÈçÏ£º + +``` +annotation: @org.springframework.stereotype.Component(value=123) +AnnotationAttributes: {value=123} +annotationAttributeMap: {value=123} +value: 123 + +``` + +´Ó½á¹ûÀ´¿´£¬Ö±½Óͨ¹ý `AnnotationUtils.getAnnotation(...)` Ò²ÊÇÄÜ»ñÈ¡µ½ `@Component` ×¢½âµÄ£¬¾¡¹Ü `BeanObj3` ²¢Ã»ÓÐÖ±½Ó±ê¼Ç `@Component`. ÐèҪעÒâµÄÊÇ£¬ÕâÑù»ñÈ¡µ½µÄ `@Component` µÄ `value` ÖµÊÇ "123"£¬²¢²»ÊÇ `@MyComponent` ÉèÖÃµÄ `beanObj3`£¬ÕâÒ²Ö¤Ã÷ÁË `AnnotationUtils` »ñÈ¡ÊôÐÔֵʱ²¢²»½øÐÐÊôÐÔ¸²¸Ç²Ù×÷¡£ + +#### 4.2 `AnnotatedElementUtils` + +`AnnotatedElementUtils` Ö§³ÖµÄ²¿·Ö·½·¨ÈçÏ£º + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-9815fe697f5ccf14d3aa215952bfcbcc3ab.png) + +¸ø¸öʾÀý°É£º + +``` +// 1\. ÅжÏÊÇ·ñÓÐ Component ×¢½â +boolean result = AnnotatedElementUtils.hasAnnotation(BeanObj3.class, Component.class); +System.out.println("hasAnnotation: " + result); + +// 2\. »ñÈ¡ attributeMap£¬¿ÉÒÔ¿´µ½µÄÊÇ£¬»ñÈ¡ @Component Óë @MyComponent µÃµ½µÄ½á¹û²»Ò»Ñù +// Component attributeMap: {value=[123]} +MultiValueMap attributeMap1 = AnnotatedElementUtils + .getAllAnnotationAttributes(BeanObj3.class, Component.class.getName()); +System.out.println("Component attributeMap: " + attributeMap1); +// MyComponent attributeMap: {value=[beanObj3]} +MultiValueMap attributeMap2 = AnnotatedElementUtils + .getAllAnnotationAttributes(BeanObj3.class, MyComponent.class.getName()); +System.out.println("MyComponent attributeMap: " + attributeMap2); + +// 3\. »ñÈ¡ËùÓÐµÄ @Component ×¢½â£¬value=beanObj3 +Set mergedAnnotations = AnnotatedElementUtils + .getAllMergedAnnotations(BeanObj3.class, Component.class); +System.out.println("mergedAnnotations: " + mergedAnnotations); + +// 4\. »ñÈ¡ÊôÐÔÖµ£¬{value=beanObj3} +AnnotationAttributes attributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(BeanObj3.class, Component.class); +System.out.println("attributes: " + attributes); + +// 5\. »ñÈ¡ MyComponent ÉϵÄ×¢½â +Set types = AnnotatedElementUtils + .getMetaAnnotationTypes(BeanObj3.class, MyComponent.class); +System.out.println("types: " + types); + +``` + +½á¹ûÈçÏ£º + +``` +hasAnnotation: true +Component attributeMap: {value=[123]} +MyComponent attributeMap: {value=[beanObj3]} +mergedAnnotations: [@org.springframework.stereotype.Component(value=beanObj3)] +attributes: {value=beanObj3} +types: [org.springframework.stereotype.Component, org.springframework.stereotype.Indexed] + +``` + +´Ó´úÂëÀ´¿´£¬Ôڵõ½µÄ `Set` Óë `AnnotationAttributes` ÖУ¬ÊôÐÔÖµÒѾ­ºÏ²¢ÁË. + +ÔÚÑ¡ÔñʹÓà `AnnotationUtils` »¹ÊÇ `AnnotatedElementUtils` ʱ£¬¿ÉÒÔ¸ù¾ÝÒª²»ÒªÊôÐÔ¸²¸ÇÀ´Ñ¡Ôñ£¬Èç¹ûÐèÒª´¦ÀíÊôÐÔ¸²¸Ç£¬¾ÍʹÓà `AnnotatedElementUtils`£¬Èç¹û²»ÐèÒª£¬¾ÍʹÓà `AnnotationUtils` °É£¡ + +### 5\. ×ܽá + +±¾ÎĽéÉÜÁË spring ´¦Àí×¢½âµÄ²Ù×÷£¬Ö÷Òª½éÉÜÁË `SimpleAnnotationMetadataReadingVisitor` Óë `StandardAnnotationMetadata` µÄÇø±ðÓëʹÓ÷½·¨¡£ÓÉÓÚÕâÁ½¸öÀàʹÓÃÆðÀ´²½Öè±È½Ï¶à£¬ÎÄÖÐÓÖ½éÉÜÁË spring ÌṩµÄÁ½¸ö¹¤¾ßÀࣺ`AnnotationUtils` Óë `AnnotatedElementUtils`£¬Èç¹ûÐèÒª´¦ÀíÊôÐÔ¸²¸Ç£¬ÐèҪʹÓà `AnnotatedElementUtils`£¬Èç¹û²»ÐèÒª£¬¾ÍʹÓà `AnnotationUtils`¡£ + +* * * + +_±¾ÎÄÔ­ÎÄÁ´½Ó£º[https://my.oschina.net/funcy/blog/4633161](https://my.oschina.net/funcy/blog/4633161) £¬ÏÞÓÚ×÷Õ߸öÈËˮƽ£¬ÎÄÖÐÄÑÃâÓдíÎóÖ®´¦£¬»¶Ó­Ö¸Õý£¡Ô­´´²»Ò×£¬ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£_ \ No newline at end of file diff --git "a/docs/big-backEnd/Hadoop\347\224\237\346\200\201\346\200\273\347\273\223.md" "b/docs/backend/Hadoop\347\224\237\346\200\201\346\200\273\347\273\223.md" similarity index 100% rename from "docs/big-backEnd/Hadoop\347\224\237\346\200\201\346\200\273\347\273\223.md" rename to "docs/backend/Hadoop\347\224\237\346\200\201\346\200\273\347\273\223.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\345\274\200\347\257\207\357\274\232\344\272\221\350\256\241\347\256\227\357\274\214\345\244\247\346\225\260\346\215\256\344\270\216AI\347\232\204\346\225\205\344\272\213.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\345\274\200\347\257\207\357\274\232\344\272\221\350\256\241\347\256\227\357\274\214\345\244\247\346\225\260\346\215\256\344\270\216AI\347\232\204\346\225\205\344\272\213.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\345\274\200\347\257\207\357\274\232\344\272\221\350\256\241\347\256\227\357\274\214\345\244\247\346\225\260\346\215\256\344\270\216AI\347\232\204\346\225\205\344\272\213.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\345\274\200\347\257\207\357\274\232\344\272\221\350\256\241\347\256\227\357\274\214\345\244\247\346\225\260\346\215\256\344\270\216AI\347\232\204\346\225\205\344\272\213.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Docker \346\240\270\345\277\203\346\212\200\346\234\257\344\270\216\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Docker \346\240\270\345\277\203\346\212\200\346\234\257\344\270\216\345\256\236\347\216\260\345\216\237\347\220\206.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Docker \346\240\270\345\277\203\346\212\200\346\234\257\344\270\216\345\256\236\347\216\260\345\216\237\347\220\206.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Docker \346\240\270\345\277\203\346\212\200\346\234\257\344\270\216\345\256\236\347\216\260\345\216\237\347\220\206.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Elasticsearch\344\270\216solr\345\205\245\351\227\250\345\256\236\350\267\265.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Elasticsearch\344\270\216solr\345\205\245\351\227\250\345\256\236\350\267\265.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Elasticsearch\344\270\216solr\345\205\245\351\227\250\345\256\236\350\267\265.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Elasticsearch\344\270\216solr\345\205\245\351\227\250\345\256\236\350\267\265.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Lucene\345\237\272\347\241\200\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Lucene\345\237\272\347\241\200\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Lucene\345\237\272\347\241\200\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232Lucene\345\237\272\347\241\200\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\346\236\266\346\236\204\350\256\276\350\256\241.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\346\236\266\346\236\204\350\256\276\350\256\241.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\346\236\266\346\236\204\350\256\276\350\256\241.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\346\236\266\346\236\204\350\256\276\350\256\241.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\347\232\204\345\237\272\347\237\263KVM.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\347\232\204\345\237\272\347\237\263KVM.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\347\232\204\345\237\272\347\237\263KVM.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232OpenStack\347\232\204\345\237\272\347\237\263KVM.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\344\272\221\350\256\241\347\256\227\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\344\272\221\350\256\241\347\256\227\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\344\272\221\350\256\241\347\256\227\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\344\272\221\350\256\241\347\256\227\347\232\204\345\211\215\344\270\226\344\273\212\347\224\237.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\205\210\346\220\236\346\207\202Docker\346\240\270\345\277\203\346\246\202\345\277\265\345\220\247.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\205\210\346\220\236\346\207\202Docker\346\240\270\345\277\203\346\246\202\345\277\265\345\220\247.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\205\210\346\220\236\346\207\202Docker\346\240\270\345\277\203\346\246\202\345\277\265\345\220\247.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\205\210\346\220\236\346\207\202Docker\346\240\270\345\277\203\346\246\202\345\277\265\345\220\247.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\215\201\345\210\206\351\222\237\347\220\206\350\247\243Kubernetes\346\240\270\345\277\203\346\246\202\345\277\265.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\215\201\345\210\206\351\222\237\347\220\206\350\247\243Kubernetes\346\240\270\345\277\203\346\246\202\345\277\265.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\215\201\345\210\206\351\222\237\347\220\206\350\247\243Kubernetes\346\240\270\345\277\203\346\246\202\345\277\265.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\345\215\201\345\210\206\351\222\237\347\220\206\350\247\243Kubernetes\346\240\270\345\277\203\346\246\202\345\277\265.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\215\213\344\270\200\346\215\213\345\244\247\346\225\260\346\215\256\347\240\224\345\217\221\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\215\213\344\270\200\346\215\213\345\244\247\346\225\260\346\215\256\347\240\224\345\217\221\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\215\213\344\270\200\346\215\213\345\244\247\346\225\260\346\215\256\347\240\224\345\217\221\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\215\213\344\270\200\346\215\213\345\244\247\346\225\260\346\215\256\347\240\224\345\217\221\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\237\272\347\241\200\345\200\222\346\216\222\347\264\242\345\274\225.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\237\272\347\241\200\345\200\222\346\216\222\347\264\242\345\274\225.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\237\272\347\241\200\345\200\222\346\216\222\347\264\242\345\274\225.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\237\272\347\241\200\345\200\222\346\216\222\347\264\242\345\274\225.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\267\245\344\275\234\345\216\237\347\220\206.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\267\245\344\275\234\345\216\237\347\220\206.md" similarity index 100% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\267\245\344\275\234\345\216\237\347\220\206.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\346\220\234\347\264\242\345\274\225\346\223\216\345\267\245\344\275\234\345\216\237\347\220\206.md" diff --git "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" similarity index 98% rename from "docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" rename to "docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" index ec768cb..7896eb0 100644 --- "a/docs/big-backEnd/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" +++ "b/docs/backend/\345\220\216\347\253\257\346\212\200\346\234\257\346\235\202\350\260\210\357\274\232\347\231\275\350\257\235\350\231\232\346\213\237\345\214\226\346\212\200\346\234\257.md" @@ -103,7 +103,7 @@ 对于虚拟机内核æ�¥è®²ï¼Œå�ªè¦�将标志ä½�设为虚拟机状æ€�,则å�¯ä»¥ç›´æŽ¥åœ¨CPU上执行大部分的指令,ä¸�需è¦�虚拟化软件在中间转述,除é�žé�‡åˆ°ç‰¹åˆ«æ•�感的指令,æ‰�需è¦�将标志ä½�设为物ç�†æœºå†…æ ¸æ€�è¿�行,这样大大æ��高了效率。 -所以安装虚拟机的时候,务必è¦�将物ç�†CPU的这个标志ä½�打开,是å�¦æ‰“开对于Intelå�¯ä»¥æŸ¥çœ‹grep "vmx" /proc/cpuinfo,对于AMDå�¯ä»¥æŸ¥çœ‹grep "svm" /proc/cpuinfo +所以安装虚拟机的时候,务必è¦�将物ç�†CPU的这个标志ä½�打开,是å�¦æ‰“开对于Intelå�¯ä»¥æŸ¥çœ‹grep"vmx" /proc/cpuinfo,对于AMDå�¯ä»¥æŸ¥çœ‹grep "svm" /proc/cpuinfo è¿™å�«å�šç¡¬ä»¶è¾…助虚拟化。 diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\345\274\200\347\257\207\357\274\232\344\273\200\344\271\210\346\230\257Redis.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\345\274\200\347\257\207\357\274\232\344\273\200\344\271\210\346\230\257Redis.md" index fe6dad0..51ba773 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\345\274\200\347\257\207\357\274\232\344\273\200\344\271\210\346\230\257Redis.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\345\274\200\347\257\207\357\274\232\344\273\200\344\271\210\346\230\257Redis.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [redis 学习笔记](#redis-学习笔记) * [redis 是什么?](#redis-是什么) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis \347\232\204\345\237\272\347\241\200\346\225\260\346\215\256\347\273\223\346\236\204\346\246\202\350\247\210.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis \347\232\204\345\237\272\347\241\200\346\225\260\346\215\256\347\273\223\346\236\204\346\246\202\350\247\210.md" index 57151b7..9d78bed 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis \347\232\204\345\237\272\347\241\200\346\225\260\346\215\256\347\273\223\346\236\204\346\246\202\350\247\210.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis \347\232\204\345\237\272\347\241\200\346\225\260\346\215\256\347\273\223\346\236\204\346\246\202\350\247\210.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [SDS](#sds) * [链表](#链表) @@ -114,7 +114,7 @@ Redis的字典由三个基础的数æ�®ç»“构组æˆ�。最底层的å�•ä½�是哈希 union { void *val; uint64_t u64; int64_t s64; } v; // 指å�‘下个哈希表节点,形æˆ�链表 struct dictEntry *next; } dictEntry; ```` -实际上哈希表节点就是一个å�•项列表的节点。ä¿�存了一下下一个节点的指针。 key 就是节点的键,v是这个节点的值。这个 v æ—¢å�¯ä»¥æ˜¯ä¸€ä¸ªæŒ‡é’ˆï¼Œä¹Ÿå�¯ä»¥æ˜¯ä¸€ä¸ªÂ `uint64_t`或者 `int64_t` 整数。*next 指å�‘下一个节点。 +实际上哈希表节点就是一个å�•项列表的节点。ä¿�存了一下下一个节点的指针。 key 就是节点的键,v是这个节点的值。这个 v æ—¢å�¯ä»¥æ˜¯ä¸€ä¸ªæŒ‡é’ˆï¼Œä¹Ÿå�¯ä»¥æ˜¯ä¸€ä¸ª`uint64_t`或者`int64_t`整数。*next 指å�‘下一个节点。 通过一个哈希表的数组把å�„个节点链接起æ�¥ï¼š ```` diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\344\272\213\345\212\241\346\265\205\346\236\220\344\270\216ACID\347\211\271\346\200\247\344\273\213\347\273\215.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\344\272\213\345\212\241\346\265\205\346\236\220\344\270\216ACID\347\211\271\346\200\247\344\273\213\347\273\215.md" index 1ec7a8e..f9aef55 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\344\272\213\345\212\241\346\265\205\346\236\220\344\270\216ACID\347\211\271\346\200\247\344\273\213\347\273\215.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\344\272\213\345\212\241\346\265\205\346\236\220\344\270\216ACID\347\211\271\346\200\247\344\273\213\347\273\215.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [事务](#事务) * [用法](#用法) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224dict.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224dict.md" index b5918f8..97fbd0e 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224dict.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224dict.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [dict的数æ�®ç»“构定义](#dict的数æ�®ç»“构定义) * [dict的创建(dictCreate)](#dict的创建(dictcreate)) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224intset.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224intset.md" index b1a6fc3..b8ffc6e 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224intset.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224intset.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [intsetæ•°æ�®ç»“构简介](#intsetæ•°æ�®ç»“构简介) * [intset的查找和添加æ“�作](#intset的查找和添加æ“�作) @@ -84,7 +84,7 @@ typedef struct intset { * `encoding`: æ•°æ�®ç¼–ç �,表示intset中的æ¯�个数æ�®å…ƒç´ ç”¨å‡ ä¸ªå­—节æ�¥å­˜å‚¨ã€‚它有三ç§�å�¯èƒ½çš„å�–值:INTSET_ENC_INT16表示æ¯�个元素用2个字节存储,INTSET_ENC_INT32表示æ¯�个元素用4个字节存储,INTSET_ENC_INT64表示æ¯�个元素用8个字节存储。因此,intset中存储的整数最多å�ªèƒ½å� ç”¨64bit。 * `length`: 表示intset中的元素个数。`encoding`å’Œ`length`两个字段构æˆ�了intset的头部(header)。 -* `contents`: 是一个柔性数组([flexible array member](https://en.wikipedia.org/wiki/Flexible_array_member)),表示intsetçš„headerå�Žé�¢ç´§è·Ÿç�€æ•°æ�®å…ƒç´ ã€‚这个数组的总长度(å�³æ€»å­—节数)等于`encoding * length`。柔性数组在Redis的很多数æ�®ç»“构的定义中都出现过(例如[sds](http://zhangtielei.com/posts/blog-redis-sds.html), [quicklist](http://zhangtielei.com/posts/blog-redis-quicklist.html), [skiplist](http://zhangtielei.com/posts/blog-redis-skiplist.html)),用于表达一个å��ç§»é‡�。`contents`需è¦�å�•独为其分é…�空间,这部分内存ä¸�包å�«åœ¨intset结构当中。 +* `contents`: 是一个柔性数组([flexible array member](https://en.wikipedia.org/wiki/Flexible_array_member)),表示intsetçš„headerå�Žé�¢ç´§è·Ÿç�€æ•°æ�®å…ƒç´ ã€‚这个数组的总长度(å�³æ€»å­—节数)等于`encoding * length`。柔性数组在Redis的很多数æ�®ç»“构的定义中都出现过(例如[sds](http://zhangtielei.com/posts/blog-redis-sds.html),[quicklist](http://zhangtielei.com/posts/blog-redis-quicklist.html),[skiplist](http://zhangtielei.com/posts/blog-redis-skiplist.html)),用于表达一个å��ç§»é‡�。`contents`需è¦�å�•独为其分é…�空间,这部分内存ä¸�包å�«åœ¨intset结构当中。 其中需è¦�注æ„�的是,intsetå�¯èƒ½ä¼šéš�ç�€æ•°æ�®çš„æ·»åŠ è€Œæ”¹å�˜å®ƒçš„æ•°æ�®ç¼–ç �: @@ -97,7 +97,7 @@ typedef struct intset { 在上图中: -* 新创建的intsetå�ªæœ‰ä¸€ä¸ªheader,总共8个字节。其中`encoding` = 2, `length` = 0。 +* 新创建的intsetå�ªæœ‰ä¸€ä¸ªheader,总共8个字节。其中`encoding`= 2,`length`= 0。 * 添加13, 5两个元素之å�Žï¼Œå› ä¸ºå®ƒä»¬æ˜¯æ¯”较å°�的整数,都能使用2个字节表示,所以`encoding`ä¸�å�˜ï¼Œå€¼è¿˜æ˜¯2。 * 当添加32768的时候,它ä¸�å†�能用2个字节æ�¥è¡¨ç¤ºäº†ï¼ˆ2个字节能表达的数æ�®èŒƒå›´æ˜¯-215~215-1,而32768等于215,超出范围了),因此`encoding`å¿…é¡»å�‡çº§åˆ°INTSET_ENC_INT32(值为4),å�³ç”¨4个字节表示一个元素。 * 在添加æ¯�个元素的过程中,intset始终ä¿�æŒ�从å°�到大有åº�。 @@ -247,11 +247,11 @@ intset *intsetAdd(intset *is, int64_t value, uint8_t *success) { * `sadd`用于分别å�‘集å�ˆ`s1`å’Œ`s2`中添加元素。添加的元素既有数字,也有é�žæ•°å­—(â€�aâ€�å’Œâ€�bâ€�)。 * `sismember`用于判断指定的元素是å�¦åœ¨é›†å�ˆå†…存在。 -* `sinter`, `sunion`å’Œ`sdiff`分别用于计算集å�ˆçš„交集ã€�并集和差集。 +* `sinter`,`sunion`å’Œ`sdiff`分别用于计算集å�ˆçš„交集ã€�并集和差集。 我们å‰�é�¢æ��到过,set的底层实现,éš�ç�€å…ƒç´ ç±»åž‹æ˜¯å�¦æ˜¯æ•´åž‹ä»¥å�Šæ·»åŠ çš„å…ƒç´ çš„æ•°ç›®å¤šå°‘ï¼Œè€Œæœ‰æ‰€å�˜åŒ–。例如,具体到上述命令的执行过程中,集å�ˆ`s1`的底层数æ�®ç»“构会å�‘生如下å�˜åŒ–: -* 在开始执行完`sadd s1 13 5`之å�Žï¼Œç”±äºŽæ·»åŠ çš„éƒ½æ˜¯æ¯”è¾ƒå°�的整数,所以`s1`底层是一个intset,其数æ�®ç¼–ç �`encoding` = 2。 +* 在开始执行完`sadd s1 13 5`之å�Žï¼Œç”±äºŽæ·»åŠ çš„éƒ½æ˜¯æ¯”è¾ƒå°�的整数,所以`s1`底层是一个intset,其数æ�®ç¼–ç �`encoding`= 2。 * 在执行完`sadd s1 32768 10 100000`之å�Žï¼Œ`s1`底层ä»�然是一个intset,但其数æ�®ç¼–ç �`encoding`从2å�‡çº§åˆ°äº†4。 * 在执行完`sadd s1 a b`之å�Žï¼Œç”±äºŽæ·»åŠ çš„å…ƒç´ ä¸�å†�是数字,`s1`底层的实现会转æˆ�一个dict。 diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224quicklist.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224quicklist.md" index 9070b83..80bba2e 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224quicklist.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224quicklist.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [quicklist概述](#quicklist概述) * [quicklist的数æ�®ç»“构定义](#quicklist的数æ�®ç»“构定义) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224sds.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224sds.md" index 3c940c8..f9d1726 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224sds.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224sds.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [å‰�言](#å‰�言) * [sds的数æ�®ç»“构定义](#sds的数æ�®ç»“构定义) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224skiplist.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224skiplist.md" index d463e4a..d419a09 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224skiplist.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224skiplist.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [skiplistæ•°æ�®ç»“构简介](#skiplistæ•°æ�®ç»“构简介) * [skiplist的算法性能分æž�](#skiplist的算法性能分æž�) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224ziplist.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224ziplist.md" index d1130f9..85e3de6 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224ziplist.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\206\205\351\203\250\346\225\260\346\215\256\347\273\223\346\236\204\350\257\246\350\247\243\342\200\224\342\200\224ziplist.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [什么是ziplist](#什么是ziplist) * [ziplist的数æ�®ç»“构定义](#ziplist的数æ�®ç»“构定义) @@ -69,7 +69,7 @@ ziplist的数æ�®ç»“构组æˆ�是本文è¦�讨论的é‡�点。实际上,ziplist还 * ``: 表示真正存放数æ�®çš„æ•°æ�®é¡¹ï¼Œé•¿åº¦ä¸�定。一个数æ�®é¡¹ï¼ˆentry)也有它自己的内部结构,这个ç¨�å�Žå†�解释。 * ``: ziplist最å�Ž1个字节,是一个结æ�Ÿæ ‡è®°ï¼Œå€¼å›ºå®šç­‰äºŽ255。 -上é�¢çš„定义中还值得注æ„�的一点是:``, ``, ``既然å� æ�®å¤šä¸ªå­—节,那么在存储的时候就有大端(big endian)和å°�端(little endian)的区别。ziplist采å�–的是å°�端模å¼�æ�¥å­˜å‚¨ï¼Œè¿™åœ¨ä¸‹é�¢æˆ‘们介ç»�具体例å­�的时候还会å†�详细解释。 +上é�¢çš„定义中还值得注æ„�的一点是:``,``,``既然å� æ�®å¤šä¸ªå­—节,那么在存储的时候就有大端(big endian)和å°�端(little endian)的区别。ziplist采å�–的是å°�端模å¼�æ�¥å­˜å‚¨ï¼Œè¿™åœ¨ä¸‹é�¢æˆ‘们介ç»�具体例å­�的时候还会å†�详细解释。 我们å†�æ�¥çœ‹ä¸€ä¸‹æ¯�一个数æ�®é¡¹``的构æˆ�: @@ -207,7 +207,7 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig * 这个函数是在指定的ä½�ç½®pæ�’入一段新的数æ�®ï¼Œå¾…æ�’入数æ�®çš„地å�€æŒ‡é’ˆæ˜¯s,长度为slen。æ�’å…¥å�Žå½¢æˆ�一个新的数æ�®é¡¹ï¼Œå� æ�®åŽŸæ�¥pçš„é…�置,原æ�¥ä½�于pä½�置的数æ�®é¡¹ä»¥å�Šå�Žé�¢çš„æ‰€æœ‰æ•°æ�®é¡¹ï¼Œéœ€è¦�统一å�‘å�Žç§»åŠ¨ï¼Œç»™æ–°æ�’入的数æ�®é¡¹ç•™å‡ºç©ºé—´ã€‚å�‚æ•°p指å�‘的是ziplist中æŸ�一个数æ�®é¡¹çš„èµ·å§‹ä½�置,或者在å�‘尾端æ�’入的时候,它指å�‘ziplist的结æ�Ÿæ ‡è®°``。 * 函数开始先计算出待æ�’å…¥ä½�ç½®å‰�一个数æ�®é¡¹çš„长度`prevlen`。这个长度è¦�存入新æ�’入的数æ�®é¡¹çš„``字段。 -* ç„¶å�Žè®¡ç®—当å‰�æ•°æ�®é¡¹å� ç”¨çš„æ€»å­—节数`reqlen`,它包å�«ä¸‰éƒ¨åˆ†ï¼š``, ``和真正的数æ�®ã€‚其中的数æ�®éƒ¨åˆ†ä¼šé€šè¿‡è°ƒç”¨`zipTryEncoding`å…ˆæ�¥å°�试转æˆ�整数。 +* ç„¶å�Žè®¡ç®—当å‰�æ•°æ�®é¡¹å� ç”¨çš„æ€»å­—节数`reqlen`,它包å�«ä¸‰éƒ¨åˆ†ï¼š``,``和真正的数æ�®ã€‚其中的数æ�®éƒ¨åˆ†ä¼šé€šè¿‡è°ƒç”¨`zipTryEncoding`å…ˆæ�¥å°�试转æˆ�整数。 * 由于æ�’入导致的ziplist对于内存的新增需求,除了待æ�’入数æ�®é¡¹å� ç”¨çš„`reqlen`之外,还è¦�考虑原æ�¥pä½�置的数æ�®é¡¹ï¼ˆçŽ°åœ¨è¦�排在待æ�’入数æ�®é¡¹ä¹‹å�Žï¼‰çš„``字段的å�˜åŒ–。本æ�¥å®ƒä¿�存的是å‰�一项的总长度,现在å�˜æˆ�了ä¿�存当å‰�æ�’入的数æ�®é¡¹çš„æ€»é•¿åº¦ã€‚这样它的``字段本身需è¦�的存储空间也å�¯èƒ½å�‘生å�˜åŒ–,这个å�˜åŒ–å�¯èƒ½æ˜¯å�˜å¤§ä¹Ÿå�¯èƒ½æ˜¯å�˜å°�。这个å�˜åŒ–了多少的值`nextdiff`,是调用`zipPrevLenByteDiff`计算出æ�¥çš„。如果å�˜å¤§äº†ï¼Œ`nextdiff`是正值,å�¦åˆ™æ˜¯è´Ÿå€¼ã€‚ * 现在很容易算出æ�¥æ�’å…¥å�Žæ–°çš„ziplist需è¦�多少字节了,然å�Žè°ƒç”¨`ziplistResize`æ�¥é‡�新调整大å°�。ziplistResize的实现里会调用allocatorçš„`zrealloc`,它有å�¯èƒ½ä¼šé€ æˆ�æ•°æ�®æ‹·è´�。 * 现在é¢�外的空间有了,接下æ�¥å°±æ˜¯å°†åŽŸæ�¥pä½�置的数æ�®é¡¹ä»¥å�Šå�Žé�¢çš„æ‰€æœ‰æ•°æ�®éƒ½å�‘å�ŽæŒªåŠ¨ï¼Œå¹¶ä¸ºå®ƒè®¾ç½®æ–°çš„``字段。此外,还å�¯èƒ½éœ€è¦�调整ziplistçš„``字段。 @@ -223,7 +223,7 @@ hash是Redis中å�¯ä»¥ç”¨æ�¥å­˜å‚¨ä¸€ä¸ªå¯¹è±¡ç»“构的比较ç�†æƒ³çš„æ•°æ�®ç±» 实际上,hashéš�ç�€æ•°æ�®çš„增大,其底层数æ�®ç»“构的实现是会å�‘生å�˜åŒ–的,当然存储效率也就ä¸�å�Œã€‚在field比较少,å�„个value值也比较å°�的时候,hash采用ziplistæ�¥å®žçŽ°ï¼›è€Œéš�ç�€field增多和value值增大,hashå�¯èƒ½ä¼šå�˜æˆ�dictæ�¥å®žçŽ°ã€‚å½“hash底层å�˜æˆ�dictæ�¥å®žçŽ°çš„æ—¶å€™ï¼Œå®ƒçš„å­˜å‚¨æ•ˆçŽ‡å°±æ²¡æ³•è·Ÿé‚£äº›åº�列化方å¼�相比了。 -当我们为æŸ�个key第一次执行 `hset key field value` 命令的时候,Redis会创建一个hash结构,这个新创建的hash底层就是一个ziplist。 +当我们为æŸ�个key第一次执行`hset key field value`命令的时候,Redis会创建一个hash结构,这个新创建的hash底层就是一个ziplist。 diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\210\206\345\270\203\345\274\217\351\224\201\350\277\233\345\214\226\345\217\262.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\210\206\345\270\203\345\274\217\351\224\201\350\277\233\345\214\226\345\217\262.md" index 9f3d353..4a2ea7d 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\210\206\345\270\203\345\274\217\351\224\201\350\277\233\345\214\226\345\217\262.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\345\210\206\345\270\203\345\274\217\351\224\201\350\277\233\345\214\226\345\217\262.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Redis分布å¼�é”�进化å�²](#redis分布å¼�é”�进化å�²) * [å�„个版本的Redis分布å¼�é”�](#å�„个版本的redis分布å¼�é”�) @@ -101,7 +101,7 @@ release(){ tryLock(){ SETNX Key 1 Seconds}release(){ DELETE Key} ``` -Redis 2.6.12版本å�ŽSETNX增加过期时间å�‚数,这样就解决了两æ�¡å‘½ä»¤æ— æ³•ä¿�è¯�原å­�性的问题。但是设想下é�¢ä¸€ä¸ªåœºæ™¯ï¼šÂ  +Redis 2.6.12版本å�ŽSETNX增加过期时间å�‚数,这样就解决了两æ�¡å‘½ä»¤æ— æ³•ä¿�è¯�原å­�性的问题。但是设想下é�¢ä¸€ä¸ªåœºæ™¯ï¼š 1\. C1æˆ�功获å�–到了é”�,之å�ŽC1因为GC进入等待或者未知原因导致任务执行过长,最å�Žåœ¨é”�失效å‰�C1没有主动释放é”� @@ -167,7 +167,7 @@ release(){ ``` -Redis 2.6.12å�Ž[SET](https://redis.io/commands/set)å�Œæ ·æ��供了一个NXå�‚数,等å�ŒäºŽSETNX命令,官方文档上æ��醒å�Žé�¢çš„版本有å�¯èƒ½åŽ»æŽ‰[SETNX](https://redis.io/commands/setnx), [SETEX](https://redis.io/commands/setex), [PSETEX](https://redis.io/commands/psetex),并用SET命令代替,å�¦å¤–一个优化是使用一个自增的唯一UniqId代替时间戳æ�¥è§„é�¿V3.0æ��到的时钟问题。 +Redis 2.6.12å�Ž[SET](https://redis.io/commands/set)å�Œæ ·æ��供了一个NXå�‚数,等å�ŒäºŽSETNX命令,官方文档上æ��醒å�Žé�¢çš„版本有å�¯èƒ½åŽ»æŽ‰[SETNX](https://redis.io/commands/setnx),[SETEX](https://redis.io/commands/setex),[PSETEX](https://redis.io/commands/psetex),并用SET命令代替,å�¦å¤–一个优化是使用一个自增的唯一UniqId代替时间戳æ�¥è§„é�¿V3.0æ��到的时钟问题。 这个方案是目å‰�最优的分布å¼�é”�方案,但是如果在Redis集群环境下ä¾�然存在问题: @@ -175,7 +175,7 @@ Redis 2.6.12å�Ž[SET](https://redis.io/commands/set)å�Œæ ·æ��供了一个NXå�‚æ•° ### 分布å¼�Redisé”�:Redlock -V3.1的版本仅在å�•实例的场景下是安全的,针对如何实现分布å¼�Redisçš„é”�,国外的分布å¼�专家有过激烈的讨论, antirezæ��出了分布å¼�é”�算法Redlock,在[distlock](https://redis.io/topics/distlock)è¯�题下å�¯ä»¥çœ‹åˆ°å¯¹Redlock的详细说明,下é�¢æ˜¯Redlock算法的一个中文说明(引用) +V3.1的版本仅在å�•实例的场景下是安全的,针对如何实现分布å¼�Redisçš„é”�,国外的分布å¼�专家有过激烈的讨论,antirezæ��出了分布å¼�é”�算法Redlock,在[distlock](https://redis.io/topics/distlock)è¯�题下å�¯ä»¥çœ‹åˆ°å¯¹Redlock的详细说明,下é�¢æ˜¯Redlock算法的一个中文说明(引用) å�‡è®¾æœ‰N个独立的Redis节点 diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\351\233\206\347\276\244\346\234\272\345\210\266\345\217\212\344\270\200\344\270\252Redis\346\236\266\346\236\204\346\274\224\350\277\233\345\256\236\344\276\213.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\351\233\206\347\276\244\346\234\272\345\210\266\345\217\212\344\270\200\344\270\252Redis\346\236\266\346\236\204\346\274\224\350\277\233\345\256\236\344\276\213.md" index dc6c078..5d84b69 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\351\233\206\347\276\244\346\234\272\345\210\266\345\217\212\344\270\200\344\270\252Redis\346\236\266\346\236\204\346\274\224\350\277\233\345\256\236\344\276\213.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232Redis\351\233\206\347\276\244\346\234\272\345\210\266\345\217\212\344\270\200\344\270\252Redis\346\236\266\346\236\204\346\274\224\350\277\233\345\256\236\344\276\213.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Replication(主从å¤�制)](#replication(主从å¤�制)) * [é…�置主æœ�务器](#é…�置主æœ�务器) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\344\275\277\347\224\250\345\277\253\347\205\247\345\222\214AOF\345\260\206Redis\346\225\260\346\215\256\346\214\201\344\271\205\345\214\226\345\210\260\347\241\254\347\233\230\344\270\255.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\344\275\277\347\224\250\345\277\253\347\205\247\345\222\214AOF\345\260\206Redis\346\225\260\346\215\256\346\214\201\344\271\205\345\214\226\345\210\260\347\241\254\347\233\230\344\270\255.md" index 119f43b..b239d06 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\344\275\277\347\224\250\345\277\253\347\205\247\345\222\214AOF\345\260\206Redis\346\225\260\346\215\256\346\214\201\344\271\205\345\214\226\345\210\260\347\241\254\347\233\230\344\270\255.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\344\275\277\347\224\250\345\277\253\347\205\247\345\222\214AOF\345\260\206Redis\346\225\260\346\215\256\346\214\201\344\271\205\345\214\226\345\210\260\347\241\254\347\233\230\344\270\255.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [å‰�言](#å‰�言) * [å¿«ç…§æŒ�久化](#å¿«ç…§æŒ�久化) @@ -39,7 +39,7 @@ ``` -Redis给我们æ��供了两ç§�ä¸�å�Œæ–¹å¼�çš„æŒ�久化方法:快照(Snapshotting) 和 å�ªè¿½åŠ æ–‡ä»¶ï¼ˆappend-only-file)。 +Redis给我们æ��供了两ç§�ä¸�å�Œæ–¹å¼�çš„æŒ�久化方法:快照(Snapshotting)和å�ªè¿½åŠ æ–‡ä»¶ï¼ˆappend-only-file)。 (1)å��è¯�简介 @@ -71,17 +71,17 @@ appendonly no #是å�¦ä½¿ç”¨AOFæŒ�久化appendfsync everysec #多久执行一 一ã€�创建快照的方å¼�: -(1)客户端通过å�‘Rediså�‘é€�`BGSAVE` 命令æ�¥åˆ›å»ºå¿«ç…§ã€‚ +(1)客户端通过å�‘Rediså�‘é€�`BGSAVE`命令æ�¥åˆ›å»ºå¿«ç…§ã€‚ 使用BGSAVE的时候,Redis会调用forkæ�¥åˆ›å»ºä¸€ä¸ªå­�进程,然å�Žå­�进程负责将快照写到硬盘中,而父进程则继续处ç�†å‘½ä»¤è¯·æ±‚。 使用场景: -如果用户使用了save设置,例如:`save 60 1000` ,那么从Redis最近一次创建快照之å�Žå¼€å§‹è®¡ç®—,当“60秒之内有1000次写入æ“�作â€�这个æ�¡ä»¶æ»¡è¶³çš„æ—¶å€™ï¼ŒRedis就会自动触å�‘BGSAVE命令。 +如果用户使用了save设置,例如:`save 60 1000`,那么从Redis最近一次创建快照之å�Žå¼€å§‹è®¡ç®—,当“60秒之内有1000次写入æ“�作â€�这个æ�¡ä»¶æ»¡è¶³çš„æ—¶å€™ï¼ŒRedis就会自动触å�‘BGSAVE命令。 如果用户使用了多个save设置,那么当任æ„�一个saveé…�置满足æ�¡ä»¶çš„æ—¶å€™ï¼ŒRedis都会触å�‘一次BGSAVE命令。 -(2)客户端通过å�‘Rediså�‘é€�`SAVE` 命令æ�¥åˆ›å»ºå¿«ç…§ã€‚ +(2)客户端通过å�‘Rediså�‘é€�`SAVE`命令æ�¥åˆ›å»ºå¿«ç…§ã€‚ 接收到SAVE命令的Redisæœ�务器在快照创建完毕之å‰�å°†ä¸�å†�å“�应任何其他命令的请求。SAVE命令并ä¸�常用,我们通常å�ªåœ¨æ²¡æœ‰è¶³å¤Ÿçš„内存去执行BGSAVE命令的时候æ‰�会使用SAVE命令,或者å�³ä½¿ç­‰å¾…æŒ�久化æ“�作执行完毕也无所谓的情况下,æ‰�会使用这个命令; @@ -149,7 +149,7 @@ Redis以æ¯�ç§’å�Œæ­¥ä¸€æ¬¡AOF文件的性能和ä¸�使用任何æŒ�久化特性时 三ã€�é‡�写/压缩AOF文件 -éš�ç�€æ•°æ�®é‡�的增大,AOF的文件å�¯èƒ½ä¼šå¾ˆå¤§ï¼Œè¿™æ ·åœ¨æ¯�次进行数æ�®æ�¢å¤�的时候就会进行很长的时间,为了解决日益增大的AOF文件,用户å�¯ä»¥å�‘Rediså�‘é€�`BGREWRITEAOF` 命令,这个命令会通过移除AOF文件中的冗余命令æ�¥é‡�写AOF文件,是AOF文件的体检å�˜å¾—å°½å�¯èƒ½çš„å°�。 +éš�ç�€æ•°æ�®é‡�的增大,AOF的文件å�¯èƒ½ä¼šå¾ˆå¤§ï¼Œè¿™æ ·åœ¨æ¯�次进行数æ�®æ�¢å¤�的时候就会进行很长的时间,为了解决日益增大的AOF文件,用户å�¯ä»¥å�‘Rediså�‘é€�`BGREWRITEAOF`命令,这个命令会通过移除AOF文件中的冗余命令æ�¥é‡�写AOF文件,是AOF文件的体检å�˜å¾—å°½å�¯èƒ½çš„å°�。 BGREWRITEAOF的工作原ç�†å’ŒBGSAVE的原ç�†å¾ˆåƒ�:Redis会创建一个å­�进程,然å�Žç”±å­�进程负责对AOF文件的é‡�写æ“�作。 @@ -157,7 +157,7 @@ BGREWRITEAOF的工作原ç�†å’ŒBGSAVE的原ç�†å¾ˆåƒ�:Redis会创建一个å­�è¿› å››ã€�触å�‘é‡�写/压缩AOF文件æ�¡ä»¶è®¾å®š -AOF通过设置`auto-aof-rewrite-percentage` 和 `auto-aof-rewrite-min-size` 选项æ�¥è‡ªåŠ¨æ‰§è¡ŒBGREWRITEAOF。 +AOF通过设置`auto-aof-rewrite-percentage`å’Œ`auto-aof-rewrite-min-size`选项æ�¥è‡ªåŠ¨æ‰§è¡ŒBGREWRITEAOF。 其具体å�«ä¹‰ï¼Œé€šè¿‡å®žä¾‹å�¯ä»¥çœ‹å‡ºï¼Œå¦‚下é…�置: @@ -167,7 +167,7 @@ auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb 表示当å‰�AOF的文件体积大于64MB,并且AOF文件的体积比上一次é‡�写之å�Žçš„体积å�˜å¤§äº†è‡³å°‘一å€�(100%)的时候,Redis将执行é‡�写BGREWRITEAOF命令。 -如果AOFé‡�写执行的过于频ç¹�çš„è¯�,å�¯ä»¥å°†`auto-aof-rewrite-percentage` 选项的值设置为100以上,这ç§�最å�¶å�‘å°±å�¯ä»¥è®©Redis在AOF文件的体积å�˜å¾—更大之å�Žæ‰�执行é‡�写æ“�作,ä¸�过,这也使得在进行数æ�®æ�¢å¤�的时候执行的时间å�˜å¾—更加长一些。 +如果AOFé‡�写执行的过于频ç¹�çš„è¯�,å�¯ä»¥å°†`auto-aof-rewrite-percentage`选项的值设置为100以上,这ç§�最å�¶å�‘å°±å�¯ä»¥è®©Redis在AOF文件的体积å�˜å¾—更大之å�Žæ‰�执行é‡�写æ“�作,ä¸�过,这也使得在进行数æ�®æ�¢å¤�的时候执行的时间å�˜å¾—更加长一些。 ## 验è¯�快照文件和AOF文件 @@ -179,7 +179,7 @@ redis-check-aofredis-check-dump 他们å�¯ä»¥å†�系统å�‘生故障的时候,检查快照和AOF文件的状æ€�,并对有需è¦�的情况对文件进行修å¤�。 -如果用户在è¿�行redis-check-aof命令的时候,指定了`--fix` å�‚数,那么程åº�将对AOF文件进行修å¤�。 +如果用户在è¿�行redis-check-aof命令的时候,指定了`--fix`å�‚数,那么程åº�将对AOF文件进行修å¤�。 程åº�ä¿®å¤�AOF文件的方法很简å�•:他会扫æ��给定的AOF文件,寻找ä¸�正确或者ä¸�完整的命令,当å�‘现第一个出现错误命令的时候,程åº�会删除出错命令以å�Šå‡ºé”™å‘½ä»¤ä¹‹å�Žçš„æ‰€æœ‰å‘½ä»¤ï¼Œå�ªä¿�留那些ä½�于出错命令之å‰�的正确命令。大部分情况,被删除的都是AOF文件末尾的ä¸�完整的写命令。 diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\225\260\346\215\256\345\272\223redisDb\344\270\216\351\224\256\350\277\207\346\234\237\345\210\240\351\231\244\347\255\226\347\225\245.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\225\260\346\215\256\345\272\223redisDb\344\270\216\351\224\256\350\277\207\346\234\237\345\210\240\351\231\244\347\255\226\347\225\245.md" index d176fc1..3f14892 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\225\260\346\215\256\345\272\223redisDb\344\270\216\351\224\256\350\277\207\346\234\237\345\210\240\351\231\244\347\255\226\347\225\245.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\225\260\346\215\256\345\272\223redisDb\344\270\216\351\224\256\350\277\207\346\234\237\345\210\240\351\231\244\347\255\226\347\225\245.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [一. æ•°æ�®åº“](#一-æ•°æ�®åº“) * [二ã€�切æ�¢æ•°æ�®åº“原ç�†](#二ã€�切æ�¢æ•°æ�®åº“原ç�†) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\265\205\346\236\220Redis\344\270\273\344\273\216\345\244\215\345\210\266.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\265\205\346\236\220Redis\344\270\273\344\273\216\345\244\215\345\210\266.md" index d23323b..393e8ac 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\265\205\346\236\220Redis\344\270\273\344\273\216\345\244\215\345\210\266.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\346\265\205\346\236\220Redis\344\270\273\344\273\216\345\244\215\345\210\266.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Rediså¤�制概论](#rediså¤�制概论) * [Rediså¤�制方å¼�](#rediså¤�制方å¼�) diff --git "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\350\277\236\346\216\245\345\272\225\345\261\202\344\270\216\350\241\250\351\235\242\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204robj.md" "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\350\277\236\346\216\245\345\272\225\345\261\202\344\270\216\350\241\250\351\235\242\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204robj.md" index ffc8fe4..e8823f6 100644 --- "a/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\350\277\236\346\216\245\345\272\225\345\261\202\344\270\216\350\241\250\351\235\242\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204robj.md" +++ "b/docs/cache/\346\216\242\347\264\242Redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\357\274\232\350\277\236\346\216\245\345\272\225\345\261\202\344\270\216\350\241\250\351\235\242\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204robj.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [robj的数æ�®ç»“构定义](#robj的数æ�®ç»“构定义) * [string robj的编ç �过程](#string-robj的编ç �过程) diff --git "a/docs/cs/algorithms/\345\211\221\346\214\207offer.md" "b/docs/cs/algorithms/\345\211\221\346\214\207offer.md" index 3147337..7952448 100644 --- "a/docs/cs/algorithms/\345\211\221\346\214\207offer.md" +++ "b/docs/cs/algorithms/\345\211\221\346\214\207offer.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [数论和数字规律](#数论和数字规律) * [从1到n整数中1出现的次数](#从1到n整数中1出现的次数) diff --git "a/docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" index 0ee5021..6f4ee49 100644 --- "a/docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" +++ "b/docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [网å�¡å’Œè·¯ç”±å™¨](#网å�¡å’Œè·¯ç”±å™¨) * [交æ�¢æœº](#交æ�¢æœº) diff --git "a/docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" index 4a366fc..4dd7996 100644 --- "a/docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" +++ "b/docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [文件系统和VFS](#文件系统和vfs) * [进程和线程](#进程和线程) @@ -309,7 +309,7 @@ linux使用inodeæ�¥æ ‡è¯†ä»»æ„�一个文件。inode存储除了文件å��以外 -
#include  ssize_t write(int filedes, void *buf, size_t nbytes); // 返回:若�功则返回写入的字节数,若出错则返回-1 // filedes:文件�述符 // buf:待写入数�缓存区 // nbytes:�写入的字节数
+#include ssize_t write(int filedes, void *buf, size_t nbytes); // 返回:若�功则返回写入的字节数,若出错则返回-1 // filedes:文件�述符 // buf:待写入数�缓存区 // nbytes:�写入的字节数目录 @@ -321,7 +321,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外 ![�制代�](http://common.cnblogs.com/images/copycode.gif) -
 1 ssize_t                        /* Write "n" bytes to a descriptor. */
+ 1 ssize_t                        /* Write "n" bytes to a descriptor. */
  2 writen(int fd, const void *vptr, size_t n)
  3 {
  4     size_t nleft;
@@ -335,7 +335,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外
 18         nleft -= nwritten; 19         ptr   += nwritten; 20 } 21     return(n); 22 } 23 /* end writen */
 24 
 25 void
-26 Writen(int fd, void *ptr, size_t nbytes) 27 { 28     if (writen(fd, ptr, nbytes) != nbytes) 29         err_sys("writen error"); 30 }
+26 Writen(int fd, void *ptr, size_t nbytes) 27 { 28 if (writen(fd, ptr, nbytes) != nbytes) 29 err_sys("writen error"); 30 }目录 ![�制代�](http://common.cnblogs.com/images/copycode.gif) @@ -349,7 +349,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外 -
#include  ssize_t read(int filedes, void *buf, size_t nbytes); // 返回:若�功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1 // filedes:文件�述符 // buf:读�数�缓存区 // nbytes:�读�的字节数
+#include ssize_t read(int filedes, void *buf, size_t nbytes); // 返回:若�功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1 // filedes:文件�述符 // buf:读�数�缓存区 // nbytes:�读�的字节数目录 @@ -375,7 +375,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外 ![�制代�](http://common.cnblogs.com/images/copycode.gif) -
 1 ssize_t                        /* Read "n" bytes from a descriptor. */
+ 1 ssize_t                        /* Read "n" bytes from a descriptor. */
  2 readn(int fd, void *vptr, size_t n)
  3 {
  4     size_t nleft;
@@ -390,7 +390,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外
 23 } 24 /* end readn */
 25 
 26 ssize_t 27 Readn(int fd, void *ptr, size_t nbytes) 28 { 29 ssize_t        n; 30 
-31     if ( (n = readn(fd, ptr, nbytes)) < 0) 32         err_sys("readn error"); 33     return(n); 34 }
+31 if ( (n = readn(fd, ptr, nbytes)) < 0) 32 err_sys("readn error"); 33 return(n); 34 }目录 ![�制代�](http://common.cnblogs.com/images/copycode.gif) @@ -406,7 +406,7 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外 -
#include  ssize_t write(int fd, const void *buf, size_t count);
+#include ssize_t write(int fd, const void *buf, size_t count);目录 @@ -416,10 +416,10 @@ linux使用inode�标识任�一个文件。inode存储除了文件�以外 -
# 获�socket的��/接�缓冲区的大�:(��的值是在Linux 2.6.38 x86_64上测试的结果)
+# 获�socket的��/接�缓冲区的大�:(��的值是在Linux 2.6.38 x86_64上测试的结果)目录 -
sysctl net.core.wmem_default       #126976
-sysctl net.core.wmem_max        #131071
+sysctl net.core.wmem_default #126976 +sysctl net.core.wmem_max     #131071目录 diff --git "a/docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" index 97cd45f..4c97490 100644 --- "a/docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" +++ "b/docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [CPU](#cpu) * [程�执行过程](#程�执行过程) diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Innodb\344\270\255\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\222\214\351\224\201\347\232\204\345\205\263\347\263\273.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Innodb\344\270\255\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\222\214\351\224\201\347\232\204\345\205\263\347\263\273.md" index 41157d3..5223578 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Innodb\344\270\255\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\222\214\351\224\201\347\232\204\345\205\263\347\263\273.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Innodb\344\270\255\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\222\214\351\224\201\347\232\204\345\205\263\347\263\273.md" @@ -1,3 +1,17 @@ +# 目录 + + * [Innodb中的事务隔离级别和�的关系](#innodb中的事务隔离级别和�的关系) + * [事务中的加�方�](#事务中的加�方�) + * [MySQL中�的�类](#mysql中�的�类) + * [Read Committed(读��交内容)](#read-committed(读��交内容)) + * [Repeatable Read(��读)](#repeatable-read(��读)) + * [����读和幻读的区别](#����读和幻读的区别) + * [悲观�和�观�](#悲观�和�观�) + * [MVCC在MySQL的InnoDB中的实现](#mvcc在mysql的innodb中的实现) + * [“读�与“读�的区别](#读与读的区别) + * [](#-2) + + 本文转自互�网 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 @@ -265,9 +279,9 @@ RC(���读)模�下的展现 事务的隔离级别实际上都是定义了当�读的级别,MySQL为了�少�处�(包括等待其它�)的时间,��并�能力,引入了快照读的概念,使得select�用加�。而update�insert这些“当�读�,就需��外的模��解决了。 -###写("当�读")事务的隔离级别中虽然�定义了读数�的�求,实际上这也�以说是写数�的�求。上文的“读�,实际是讲的快照读;而这里说的“写�就是当�读了。为了解决当�读中的幻读问题,MySQL事务使用了Next-Key�。 +写("当�读")事务的隔离级别中虽然�定义了读数�的�求,实际上这也�以说是写数�的�求。上文的“读�,实际是讲的快照读;而这里说的“写�就是当�读了。为了解决当�读中的幻读问题,MySQL事务使用了Next-Key�。 -####Next-Key�Next-Key�是行�和GAP(间隙�)的�并,行�上文已�介�了,接下�说下GAP间隙�。 +Next-Key�Next-Key�是行�和GAP(间隙�)的�并,行�上文已�介�了,接下�说下GAP间隙�。 行��以防止��事务版本的数�修改�交时造�数�冲�的情况。但如何��别的事务�入数�就�了问题。我们�以看看RR级别和RC级别的对比 diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232MySQL\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\346\210\230.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232MySQL\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\346\210\230.md" index 026a50a..d048c74 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232MySQL\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\346\210\230.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232MySQL\347\232\204\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\346\210\230.md" @@ -1,3 +1,4 @@ + # 目录 * [事务的基本�素(ACID)](#事务的基本�素(acid)) * [事务的并�问题](#事务的并�问题) diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\344\270\273\344\273\216\345\244\215\345\210\266\357\274\214\350\257\273\345\206\231\345\210\206\347\246\273\357\274\214\345\210\206\350\241\250\345\210\206\345\272\223\347\255\226\347\225\245\344\270\216\345\256\236\350\267\265.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\344\270\273\344\273\216\345\244\215\345\210\266\357\274\214\350\257\273\345\206\231\345\210\206\347\246\273\357\274\214\345\210\206\350\241\250\345\210\206\345\272\223\347\255\226\347\225\245\344\270\216\345\256\236\350\267\265.md" index 53a3fc9..6a2ae30 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\344\270\273\344\273\216\345\244\215\345\210\266\357\274\214\350\257\273\345\206\231\345\210\206\347\246\273\357\274\214\345\210\206\350\241\250\345\210\206\345\272\223\347\255\226\347\225\245\344\270\216\345\256\236\350\267\265.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\344\270\273\344\273\216\345\244\215\345\210\266\357\274\214\350\257\273\345\206\231\345\210\206\347\246\273\357\274\214\345\210\206\350\241\250\345\210\206\345\272\223\347\255\226\347\225\245\344\270\216\345\256\236\350\267\265.md" @@ -11,8 +11,6 @@ * [三�Mycat 垂直切分�水平切分实战](#三�mycat-垂直切分�水平切分实战) * [1�垂直切分](#1�垂直切分) * [2�水平切分](#2�水平切分) -* [range start-end ,data node index](#range-start-end-data-node-index) -* [K=1000,M=10000.](#k1000m10000) * [为什么需�读写分离](#为什么需�读写分离) * [MySQL主从�制](#mysql主从�制) * [Mycat读写分离设置](#mycat读写分离设置) @@ -627,4 +625,4 @@ Mycat的�置有很多,�过因为我们�是使用Mycat的读写分类的 ��已�差�多都解释清楚了,因为我�是用的基本的主从�制,所以我的将`dataHost`的`balance`设置�了3 -�动mycat,然�用数�库连接工具连接到mycat,�以测试是��置�功,最简�的就是通过修改从库的数�,这样方便查看到底是�行到哪个库上�了,�外由于我是基于docker�动的mycat,所以如果是直接在系统中�行的mycat的,�以去看官方文档,看看到底怎么�动mycat \ No newline at end of file +�动mycat,然�用数�库连接工具连接到mycat,�以测试是��置�功,最简�的就是通过修改从库的数�,这样方便查看到底是�行到哪个库上�了,�外由于我是基于docker�动的mycat,所以如果是直接在系统中�行的mycat的,�以去看官方文档,看看到底怎么�动mycat diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\345\255\230\345\202\250\345\274\225\346\223\216\344\270\216\346\225\260\346\215\256\345\255\230\345\202\250\345\216\237\347\220\206.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\345\255\230\345\202\250\345\274\225\346\223\216\344\270\216\346\225\260\346\215\256\345\255\230\345\202\250\345\216\237\347\220\206.md" index 64db961..3d694e3 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\345\255\230\345\202\250\345\274\225\346\223\216\344\270\216\346\225\260\346\215\256\345\255\230\345\202\250\345\216\237\347\220\206.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\345\255\230\345\202\250\345\274\225\346\223\216\344\270\216\346\225\260\346\215\256\345\255\230\345\202\250\345\216\237\347\220\206.md" @@ -1,3 +1,34 @@ +# 目录 + + * [**一� MySQL记录存储(页为��)**](#一�-mysql记录存储(页为��)) + * [**页头**](#页头) + * [**虚记录**](#虚记录) + * [**记录堆**](#记录堆) + * [**自由空间链表**](#自由空间链表) + * [**未分�空间**](#未分�空间) + * [**Slot区**](#slot区) + * [**页内记录维护**](#页内记录维护) + * [**二� MySQL InnoDB存储引擎内存管�**](#二�-mysql-innodb存储引擎内存管�) + * [**预分�内存空间**](#预分�内存空间) + * [**数�以页为��加载 (�少io访问次数)**](#数�以页为��加载-(�少io访问次数)) + * [**数�内外存交�**](#数�内外存交�) + * [**页�管�**](#页�管�) + * [**页�淘汰**](#页�淘汰) + * [**全表扫�对内存的影�?**](#全表扫�对内存的影�?) + * [**页�淘汰**](#页�淘汰-1) + * [**�置移动**](#�置移动) + * [**三�MySQL事务实现原�**](#三�mysql事务实现原�) + * [**1�事务特性**](#1�事务特性) + * [**2�并�问题**](#2�并�问题) + * [**3�隔离级别**](#3�隔离级别) + * [**MySQL事务实现原�(事务管�机制)**](#mysql事务实现原�(事务管�机制)) + * [**1�MVCC 多版本并�控制**](#1�mvcc-多版本并�控制) + * [**2�undo log**](#2�undo-log) + * [**3�redo log**](#3�redo-log) + * [**�义**](#�义) + * [**四�MySQL�实现原�**](#四�mysql�实现原�) + + ## **一� MySQL记录存储(页为��)** ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405205306.png) @@ -255,4 +286,4 @@ RR级别下 死�在库表中有记录,通过kill 那个�删除。 -![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/%E5%86%B3%E5%AE%9A%E7%89%88.jpeg) \ No newline at end of file +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/%E5%86%B3%E5%AE%9A%E7%89%88.jpeg) diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\347\264\242\345\274\225\345\256\236\347\216\260\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\346\225\260\346\215\256\347\273\223\346\236\204\347\256\227\346\263\225.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\347\264\242\345\274\225\345\256\236\347\216\260\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\346\225\260\346\215\256\347\273\223\346\236\204\347\256\227\346\263\225.md" index c404f1e..b522104 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\347\264\242\345\274\225\345\256\236\347\216\260\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\346\225\260\346\215\256\347\273\223\346\236\204\347\256\227\346\263\225.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232Mysql\347\264\242\345\274\225\345\256\236\347\216\260\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\346\225\260\346\215\256\347\273\223\346\236\204\347\256\227\346\263\225.md" @@ -1,3 +1,4 @@ + # 目录 * [MySQL索引](#mysql索引) @@ -904,4 +905,4 @@ title的选择性�足0.0001(精确值为0.00001579),所以实在没有 [5] Codd, E. F. (1970). "A relational model of data for large shared data banks". Communications of the ACM, , Vol. 13, No. 6, pp. 377-387 -[6] MySQL5.1�考手册 - [http://dev.mysql.com/doc/refman/5.1/zh/index.html](http://dev.mysql.com/doc/refman/5.1/zh/index.html "http://dev.mysql.com/doc/refman/5.1/zh/index.html") \ No newline at end of file +[6] MySQL5.1�考手册 -[http://dev.mysql.com/doc/refman/5.1/zh/index.html](http://dev.mysql.com/doc/refman/5.1/zh/index.html "http://dev.mysql.com/doc/refman/5.1/zh/index.html") diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" index d8eaa99..8e3991b 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" @@ -22,7 +22,7 @@ * [����读](#����读) * [幻读](#幻读) * [总结](#总结) - * [[Innodb与Myisam引擎的区别与应用场景](https://www.cnblogs.com/changna1314/p/6878900.html)](#[innodb与myisam引擎的区别与应用场景]httpswwwcnblogscomchangna1314p6878900html) + * [Innodb与Myisam引擎的区别与应用场景](#[innodb与myisam引擎的区别与应用场景]) 本文转自互�网 diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\344\273\216\345\256\236\350\267\265sql\350\257\255\345\217\245\344\274\230\345\214\226\345\274\200\345\247\213.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\344\273\216\345\256\236\350\267\265sql\350\257\255\345\217\245\344\274\230\345\214\226\345\274\200\345\247\213.md" index 7155755..f3bd6d8 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\344\273\216\345\256\236\350\267\265sql\350\257\255\345\217\245\344\274\230\345\214\226\345\274\200\345\247\213.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\344\273\216\345\256\236\350\267\265sql\350\257\255\345\217\245\344\274\230\345\214\226\345\274\200\345\247\213.md" @@ -8,9 +8,9 @@ * [InnoDB](#innodb) * [0�自己写的海�数�sql优化实践](#0�自己写的海�数�sql优化实践) * [mysql百万级分页优化](#mysql百万级分页优化) - * [  普通分页](#  普通分页) - * [   优化分页](#  -优化分页) -* [  总结](#  总结) + * [普通分页](#普通分页) + * [优化分页](#优化分页) +* [总结](#总结) 本文转自互�网 @@ -667,7 +667,7 @@ SELECT s.* from Student s INNER JOIN SC sc on sc.s_id = s.s_id where sc.c_id=0 ## mysql百万级分页优化 -###   普通分页 +### 普通分页 数�分页在网页中�分多�,分页一般都是limit start,offset,然�根�页�page计算start @@ -681,7 +681,7 @@ SELECT * from user limit **100001**,**20**; //time **0**.151s explain SELECT * f ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/d327669b28bc017d62dfe25833ba98245cf.jpg) -###    优化分页 +### 优化分页 使用主键索引�优化数�分页 @@ -693,10 +693,10 @@ SELECT * from user limit **100001**,**20**; //time **0**.151s explain SELECT * f ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/05fffbffc5e3ef9add4719846ad53f25099.jpg) -##   总结 +## 总结 在数��比较大的时候,我们尽�去利用索引�优化语�。上�的优化方法如果id�是主键索引,查询效率比第一�还�低点。我们�以先使用explain�分�语�,查看语�的执行顺�和执行性能。 -转载于:https://my.oschina.net/alicoder/blog/3097141 \ No newline at end of file +转载于:https://my.oschina.net/alicoder/blog/3097141 diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\346\240\271\346\215\256MySQL\347\264\242\345\274\225\345\216\237\347\220\206\350\277\233\350\241\214\345\210\206\346\236\220\344\270\216\344\274\230\345\214\226.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\346\240\271\346\215\256MySQL\347\264\242\345\274\225\345\216\237\347\220\206\350\277\233\350\241\214\345\210\206\346\236\220\344\270\216\344\274\230\345\214\226.md" index e90f560..3dd0dbf 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\346\240\271\346\215\256MySQL\347\264\242\345\274\225\345\216\237\347\220\206\350\277\233\350\241\214\345\210\206\346\236\220\344\270\216\344\274\230\345\214\226.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\346\240\271\346\215\256MySQL\347\264\242\345\274\225\345\216\237\347\220\206\350\277\233\350\241\214\345\210\206\346\236\220\344\270\216\344\274\230\345\214\226.md" @@ -1,5 +1,4 @@ # 目录 - * [一:Mysql原�与慢查询](#一:mysql原�与慢查询) * [一个慢查询引�的�考](#一个慢查询引�的�考) * [二:索引建立](#二:索引建立) @@ -523,4 +522,4 @@ title的选择性�足0.0001(精确值为0.00001579),所以实在没有 此时MySQL�得�为了将新记录�到�适�置而移动数�,甚至目标页��能已�被回写到�盘上而从缓存中清掉,此时��从�盘上读回�,这增加了很多开销,�时频�的移动�分页�作造�了大�的碎片,得到了�够紧凑的索引结构,�续�得�通过OPTIMIZE TABLE��建表并优化填充页�。 -因此,���以,请尽�在InnoDB上采用自增字段�主键。 \ No newline at end of file +因此,���以,请尽�在InnoDB上采用自增字段�主键。 diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\350\257\246\350\247\243MyIsam\344\270\216InnoDB\345\274\225\346\223\216\347\232\204\351\224\201\345\256\236\347\216\260.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\350\257\246\350\247\243MyIsam\344\270\216InnoDB\345\274\225\346\223\216\347\232\204\351\224\201\345\256\236\347\216\260.md" index 473146f..3b3fb32 100644 --- "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\350\257\246\350\247\243MyIsam\344\270\216InnoDB\345\274\225\346\223\216\347\232\204\351\224\201\345\256\236\347\216\260.md" +++ "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240MySQL\346\225\260\346\215\256\345\272\223\357\274\232\350\257\246\350\247\243MyIsam\344\270\216InnoDB\345\274\225\346\223\216\347\232\204\351\224\201\345\256\236\347\216\260.md" @@ -326,4 +326,4 @@ InnoDB在�大部分情况会使用行级�,因为事务和行�往往是 2�在�一个事务中,尽�能�到一次�定所需�的所有资�,�少死�产生概率; -3�对于�常容易产生死�的业务部分,�以�试使用�级�定颗粒度,通过表级�定��少死�产生的概率; \ No newline at end of file +3�对于�常容易产生死�的业务部分,�以�试使用�级�定颗粒度,通过表级�定��少死�产生的概率; diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200 \357\274\232CAP.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200 \357\274\232CAP.md" index 8d61a51..545e034 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200 \357\274\232CAP.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200 \357\274\232CAP.md" @@ -1,3 +1,14 @@ +# 目录 + + * [**引言**](#引言) + * [**CAP定�**](#cap定�) + * [**CAP的工程�示**](#cap的工程�示) + * [**1�关于 P 的�解**](#1�关于-p-的�解) + * [**2�CA�0/1的选择**](#2�ca�01的选择) + * [**3�跳出CAP**](#3�跳出cap) + * [**�结**](#�结) + + 本文转自:https://www.cnblogs.com/bangerlee/p/5328888.html 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 @@ -17,11 +28,11 @@ 如果对本系列文章有什么建议,或者是有什么疑问的�,也�以关注公众��Java技术江湖】�系作者,欢迎你�与本系列�文的创作和修订。 -**引言** +## **引言** CAP是分布�系统�特别是分布�存储领域中被讨论最多的�论,“[什么是CAP定�?](https://www.quora.com/What-Is-CAP-Theorem-1)�在Quora 分布�系统分类下排� FAQ 的 No.1。CAP在程�员中也有较广的普�,它�仅仅是“C�A�P�能�时满足,最多�能3选2�,以下�试综��方观点,从�展历��工程实践等角度讲述CAP�论。希望大家�过本文对CAP�论有更多地了解和认识。 -**CAP定�** +## **CAP定�** CAP由[Eric Brewer](https://en.wikipedia.org/wiki/Eric_Brewer_(scientist))在2000年PODC会议上�出[1][2],是Eric Brewer在Inktomi[3]期间研��索引擎�分布�web缓存时得出的关于数�一致性(consistency)��务�用性(availability)�分区容错性(partition-tolerance)的猜想: @@ -38,13 +49,13 @@ C�A�P三者最多�能满足其中两个,和FLP定�一样,CAP定� ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230407204500.png) -**CAP的工程�示** +## **CAP的工程�示** CAP�论�出7�8年�,NoSql圈将CAP�论当作对抗传统关系型数�库的����明自己放宽对数�一致性(consistency)�求的正确性[6],��引起了大范围关于CAP�论的讨论。 CAP�论看似给我们出了一�3选2的选择题,但在工程实践中存在很多现实�制�件,需�我们�更多地考�与�衡,��进入CAP认识误区[7]。 -**1�关于 P 的�解** +### **1�关于 P 的�解** Partition字���是网络分区,�因网络因素将系统分隔为多个�独的部分,有人�能会说,网络分区的情况�生概率�常�啊,是�是�用考虑P,��CA就好[8]。��解P,我们看回CAP�明[4]中P的定义: @@ -56,7 +67,7 @@ Partition字���是网络分区,�因网络因素将系统分隔为多 > In a network subject to communication failures, it is impossible for any web service to implement an atomic read/write shared memory that guarantees a response to every request. -**2�CA�0/1的选择** +### **2�CA�0/1的选择** P 是必选项,那3选2的选择题�就��数�一致性(consistency)��务�用性(availability) 2选1?工程实践中一致性有��程度,�用性也有��等级,在��分区容错性的��下,放宽约���以兼顾一致性和�用性,两者�是�此�彼[12]。 @@ -71,7 +82,7 @@ CAP定��明中的一致性指强一致性,强一致性�求多节点组 工程实践中,较常�的�法是通过异步拷�副本(asynchronous replication)�quorum/NRW,实现在调用端看�数�强一致�被调端最终一致,在调用端看��务�用�被调端�许部分节点��用(或被网络分隔)的效果[15]。 -**3�跳出CAP** +### **3�跳出CAP** CAP�论对实现分布�系统具有指导�义,但CAP�论并没有涵盖分布�工程实践中的所有��因素。 @@ -79,7 +90,7 @@ CAP�论对实现分布�系统具有指导�义,但CAP�论并没有涵 延时与数�一致性也是一对“冤家�,如果�达到强一致性�多个副本数�一致,必然增加延时。加上延时的考�,我们得到一个CAP�论的修改版本PACELC[17]:如果出现P(网络分区),如何在A(�务�用性)�C(数�一致性)之间选择;�则,如何在L(延时)�C(数�一致性)之间选择。 -**�结** +### **�结** 以上介�了CAP�论的�起和�展,介�了CAP�论给分布�系统工程实践带�的�示。 @@ -125,4 +136,4 @@ CAP�论对分布�系统实现有�常�大的影�,我们�以根� [19] [How to beat the CAP theorem](http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html), nathanmarz , 2011 -[20] [The CAP FAQ](https://github.com/henryr/cap-faq), Henry Robinson \ No newline at end of file +[20] [The CAP FAQ](https://github.com/henryr/cap-faq), Henry Robinson diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\201PC\345\222\214PC.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\201PC\345\222\214PC.md" index 8041d4d..b2ac551 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\201PC\345\222\214PC.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\201PC\345\222\214PC.md" @@ -1,3 +1,12 @@ +# 目录 + + * [**引言**](#引言) + * [**一致性(consensus)**](#一致性consensus) + * [**2PC**](#2pc) + * [**3PC**](#3pc) + * [**�结**](#�结) + + 本文转自 https://www.cnblogs.com/bangerlee/p/5268485.html 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 @@ -31,7 +40,7 @@ 一致性是分布��论中的根本性问题,近�个世纪以�,科学家们围绕�一致性问题�出了很多�论模型,��这些�论模型,业界也出现了很多工程实践投影。下�我们从一致性问题�特定�件下解决一致性问题的两�方法(2PC�3PC)入门,了解最基础的分布�系统�论。 -**一致性(consensus)** +### **一致性(consensus)** 何为一致性问题?简�而言,一致性问题就是相互独立的节点之间如何达�一项决议的问题。分布�系统中,进行数�库事务�交(commit transaction)�Leader选举��列�生�等都会�到一致性问题。这个问题在我们的日常生活中也很常�,比如牌�怎么商定几点在哪打几圈麻将: @@ -60,7 +69,7 @@ _《赌圣》,1990_ -
我: �王,今晚7点�地方,�够48圈���散�  
+我: �王,今晚7点�地方,�够48圈���散�  
 ……  
 (第二天凌晨3点) 隔��王: 没问题� // 消�延迟  
 我: ……  
@@ -71,7 +80,7 @@ _《赌圣》,1990_
 我: ……  
 -----------------------------------------------  
 我: ��头,今晚7点�地方,�够48圈���散�  
-��: 必须的,大��走起� // 拜�庭将军 (这是�打麻将呢?还是�大��?还是一边打麻将一边大��……)
+è€�æ�Ž: 必须的,大ä¿�å�¥èµ°èµ·ï¼� // 拜å� åº­å°†å†› (这是è¦�打麻将呢?还是è¦�大ä¿�å�¥ï¼Ÿè¿˜æ˜¯ä¸€è¾¹æ‰“麻将一边大ä¿�å�¥â€¦â€¦ï¼‰ç›®å½• @@ -94,7 +103,7 @@ FLP定ç�†æ˜¯åˆ†å¸ƒå¼�系统ç�†è®ºä¸­çš„基础ç�†è®ºï¼Œæ­£å¦‚物ç�†å­¦ä¸­çš„能 _《怦然心动 (Flipped)》,2010_ 工程实践上根æ�®å…·ä½“的业务场景,或ä¿�è¯�强一致(safety),或在节点宕机ã€�网络分化的时候ä¿�è¯�å�¯ç”¨(liveness)。2PCã€�3PC是相对简å�•的解决一致性问题的å��议,下é�¢æˆ‘们就æ�¥äº†è§£2PCå’Œ3PC。 -**2PC** +### **2PC** 2PC(tow phase commit)两阶段æ��交[5]顾å��æ€�义它分æˆ�两个阶段,先由一方进行æ��è®®(propose)并收集其他节点的å��馈(vote),å†�æ ¹æ�®å��馈决定æ��交(commit)或中止(abort)事务。我们将æ��议的节点称为å��调者(coordinator),其他å�‚与决议节点称为å�‚与者(participants, 或cohorts): @@ -116,7 +125,7 @@ coordinator如果在å�‘èµ·æ��è®®å�Žå®•机,那么participant将进入阻塞(blo 从coordinator接收到一次事务请求ã€�å�‘èµ·æ��议到事务完æˆ�,ç»�过2PCå��è®®å�Žå¢žåŠ äº†2次RTT(propose+commit),带æ�¥çš„æ—¶å»¶(latency)增加相对较少。 -**3PC** +### **3PC** 3PC(three phase commit)å�³ä¸‰é˜¶æ®µæ��交[6][7],既然2PCå�¯ä»¥åœ¨å¼‚步网络+节点宕机æ�¢å¤�的模型下实现一致性,那还需è¦�3PCå�šä»€ä¹ˆï¼Œ3PC是什么鬼? @@ -141,7 +150,7 @@ participant如果在ä¸�å�Œé˜¶æ®µå®•机,我们æ�¥çœ‹çœ‹3PC如何应对: 因为有了准备æ��交(prepare to commit)阶段,3PC的事务处ç�†å»¶æ—¶ä¹Ÿå¢žåŠ äº†1个RTT,å�˜ä¸º3个RTT(propose+precommit+commit),但是它防止participant宕机å�Žæ•´ä¸ªç³»ç»Ÿè¿›å…¥é˜»å¡žæ€�,增强了系统的å�¯ç”¨æ€§ï¼Œå¯¹ä¸€äº›çŽ°å®žä¸šåŠ¡åœºæ™¯æ˜¯é�žå¸¸å€¼å¾—的。 -**å°�结** +### **å°�结** 以上介ç»�了分布å¼�系统ç�†è®ºä¸­çš„部分基础知识,é˜�述了一致性(consensus)的定义和实现一致性所è¦�é�¢ä¸´çš„问题,最å�Žè®¨è®ºåœ¨å¼‚步网络(asynchronous)ã€�节点宕机æ�¢å¤�(fail-recover)模型下2PCã€�3PC怎么解决一致性问题。 @@ -159,4 +168,4 @@ participant如果在ä¸�å�Œé˜¶æ®µå®•机,我们æ�¥çœ‹çœ‹3PC如何应对: [6][Consensus Protocols: Three-phase Commit](http://the-paper-trail.org/blog/consensus-protocols-three-phase-commit/),Henry Robinson, 2008 -[7][Three-phase commit protocol](https://en.wikipedia.org/wiki/Three-phase_commit_protocol),Wikipedia \ No newline at end of file +[7][Three-phase commit protocol](https://en.wikipedia.org/wiki/Three-phase_commit_protocol),Wikipedia diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" index 47b12a6..d8f2e21 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" @@ -1,3 +1,12 @@ +# 目录 + + * [**物ç�†æ—¶é’Ÿ vs 逻辑时钟**](#物ç�†æ—¶é’Ÿ-vs-逻辑时钟) + * [**Lamport timestamps**](#lamport-timestamps) + * [**Vector clock**](#vector-clock) + * [**Version vector**](#version-vector) + * [**å°�结**](#å°�结) + + 转自:https://www.cnblogs.com/bangerlee/p/5448766.html 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -21,7 +30,7 @@ 现实生活中时间是很é‡�è¦�的概念,时间å�¯ä»¥è®°å½•事情å�‘生的时刻ã€�比较事情å�‘生的先å�Žé¡ºåº�。分布å¼�系统的一些场景也需è¦�记录和比较ä¸�å�ŒèŠ‚ç‚¹é—´äº‹ä»¶å�‘生的顺åº�,但ä¸�å�ŒäºŽæ—¥å¸¸ç”Ÿæ´»ä½¿ç”¨ç‰©ç�†æ—¶é’Ÿè®°å½•时间,分布å¼�系统使用逻辑时钟记录事件顺åº�关系,下é�¢æˆ‘们æ�¥çœ‹åˆ†å¸ƒå¼�系统中几ç§�常è§�的逻辑时钟。 -**物ç�†æ—¶é’Ÿ vs 逻辑时钟** +## **物ç�†æ—¶é’Ÿ vs 逻辑时钟** å�¯èƒ½æœ‰äººä¼šé—®ï¼Œä¸ºä»€ä¹ˆåˆ†å¸ƒå¼�系统ä¸�使用物ç�†æ—¶é’Ÿ(physical clock)记录事件?æ¯�个事件对应打上一个时间戳,当需è¦�比较顺åº�的时候比较相应时间戳就好了。 @@ -29,7 +38,7 @@ ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/116770-20160501132311347-349996615.jpg) -**Lamport timestamps** +## **Lamport timestamps** [Leslie](https://en.wikipedia.org/wiki/Leslie_Cheung)[Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport)在1978å¹´æ��出逻辑时钟的概念,并æ��述了一ç§�逻辑时钟的表示方法,这个方法被称为Lamport时间戳(Lamport timestamps)[3]。 @@ -50,7 +59,7 @@ _图1: Lamport timestamps space time (图片æ�¥æº�: wikipedia)_ 通过以上定义,我们å�¯ä»¥å¯¹æ‰€æœ‰äº‹ä»¶æŽ’åº�ã€�获得事件的[å…¨åº�关系](https://en.wikipedia.org/wiki/Total_order)(total order)。上图例å­�,我们å�¯ä»¥ä»ŽC1到A4进行排åº�。 -**Vector clock** +## **Vector clock** Lamport时间戳帮助我们得到事件顺åº�关系,但还有一ç§�顺åº�关系ä¸�能用Lamport时间戳很好地表示出æ�¥ï¼Œé‚£å°±æ˜¯å�Œæ—¶å�‘生关系(concurrent)[4]。例如图1中事件B4和事件C3没有因果关系,属于å�Œæ—¶å�‘生事件,但Lamport时间戳定义两者有先å�Žé¡ºåº�。 @@ -64,7 +73,7 @@ _图2: Vector clock space time (_图片æ�¥æº�: wikipedia)__ 如果Tb[Q] > Ta[Q] 并且 Tb[P] < Ta[P],则认为aã€�bå�Œæ—¶å�‘生,记作 a <-> b。例如图2中节点B上的第4个事件 (A:2,B:4,C:1) 与节点C上的第2个事件 (B:3,C:2) 没有因果关系ã€�属于å�Œæ—¶å�‘生事件。 -**Version vector** +## **Version vector** 基于Vector clock我们å�¯ä»¥èŽ·å¾—ä»»æ„�两个事件的顺åº�关系,结果或为先å�Žé¡ºåº�或为å�Œæ—¶å�‘生,识别事件顺åº�在工程实践中有很é‡�è¦�的引申应用,最常è§�的应用是å�‘现数æ�®å†²çª�(detect conflict)。 @@ -85,7 +94,7 @@ Vector clockå�ªç”¨äºŽå�‘现数æ�®å†²çª�,ä¸�能解决数æ�®å†²çª�。如何解 解决该问题的方法是使用server idå�–代client id创建vector (因为server的数é‡�相对client稳定),或设定最大的sizeã€�如果超过该size值则淘汰最旧的vectorä¿¡æ�¯[10][13]。 -**å°�结** +## **å°�结** 以上介ç»�了分布å¼�系统里逻辑时钟的表示方法,通过Lamport timestampså�¯ä»¥å»ºç«‹äº‹ä»¶çš„å…¨åº�关系,通过Vector clockå�¯ä»¥æ¯”较任æ„�两个事件的顺åº�关系并且能表示无因果关系的事件,将Vector clock的方法用于å�‘现数æ�®ç‰ˆæœ¬å†²çª�,于是有了Version vector。 @@ -113,4 +122,4 @@ Vector clockå�ªç”¨äºŽå�‘现数æ�®å†²çª�,ä¸�能解决数æ�®å†²çª�。如何解 [12][Why Vector Clocks Are Hard](http://basho.com/posts/technical/why-vector-clocks-are-hard/), Justin Sheehy, 2010 -[13][Causality Is Expensive (and What To Do About It)](http://www.bailis.org/blog/causality-is-expensive-and-what-to-do-about-it/), Peter Bailis ,2014 \ No newline at end of file +[13][Causality Is Expensive (and What To Do About It)](http://www.bailis.org/blog/causality-is-expensive-and-what-to-do-about-it/), Peter Bailis ,2014 diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Paxos.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Paxos.md" index 5171749..603d2d2 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Paxos.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Paxos.md" @@ -1,3 +1,11 @@ +# 目录 + + * [**引言**](#引言) + * [**Basic Paxos**](#basic-paxos) + * [**Multi Paxos**](#multi-paxos) + * [**å°�结**](#å°�结) + + 本文转自:https://www.cnblogs.com/bangerlee/p/5655754.html 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -23,7 +31,7 @@ Paxoså��è®®å�Œæ—¶å�ˆä»¥å…¶â€œè‰°æ·±æ™¦æ¶©â€�著称,下é�¢ç»“å�ˆ[Paxos Made Simple](http://research.microsoft.com/en-us/um/people/lamport/pubs/paxos-simple.pdf)ã€�[The Part-Time Parliament](http://research.microsoft.com/en-us/um/people/lamport/pubs/lamport-paxos.pdf)两篇论文,å°�试通过Paxos推演ã€�学习和了解Paxoså��议。 -**Basic Paxos** +## **Basic Paxos** 何为一致性问题?简å�•而言,一致性问题是在节点宕机ã€�消æ�¯æ— åº�等场景å�¯èƒ½å‡ºçŽ°çš„æƒ…å†µä¸‹ï¼Œç›¸äº’ç‹¬ç«‹çš„èŠ‚ç‚¹ä¹‹é—´å¦‚ä½•è¾¾æˆ�决议的问题,作为解决一致性问题的å��议,Paxos的核心是节点间如何确定并å�ªç¡®å®šä¸€ä¸ªå€¼(value)。 @@ -125,7 +133,7 @@ _(注: 希腊字æ¯�ß表示多轮决议的集å�ˆï¼Œå­—æ¯�B表示一轮决议)_ 最å�Žæˆ‘们å†�引入一个新的角色:learner,learnerä¾�附于acceptor,用于习得已确定的决议。以上决议过程都å�ªè¦�求acceptor多数派å�‚与,而我们希望尽é‡�所有acceptor的状æ€�一致。如果部分acceptor因宕机等原因未知晓已确定决议,宕机æ�¢å¤�å�Žå�¯ç»�本机learner采用pull的方å¼�从其他acceptor习得。 -**Multi Paxos** +## **Multi Paxos** 通过以上步骤分布å¼�系统已ç»�能确定一个值,“å�ªç¡®å®šä¸€ä¸ªå€¼æœ‰ä»€ä¹ˆç”¨ï¼Ÿè¿™å�¯è§£å†³ä¸�了我é�¢ä¸´çš„问题。â€� 你心中å�¯èƒ½æœ‰è¿™æ ·çš„疑问。 @@ -139,7 +147,7 @@ _(注: 希腊字æ¯�ß表示多轮决议的集å�ˆï¼Œå­—æ¯�B表示一轮决议)_ proposer leader在Multi Paxos中还有助于æ��å�‡æ€§èƒ½ï¼Œå¸¸æ€�下统一由leaderå�‘èµ·æ��议,å�¯èŠ‚çœ�prepare步骤(leaderä¸�用问询acceptor曾接å�—过的ID最大的æ��è®®ã€�å�ªæœ‰leaderæ��议也ä¸�需è¦�acceptor进行promise)直至å�‘生leader宕机ã€�é‡�新选主。 -**å°�结** +## **å°�结** 以上介ç»�了Paxos的推演过程ã€�如何在Basic Paxos的基础上通过状æ€�机构建Multi Paxos。Paxoså��议比较“艰深晦涩â€�,但多读几é��论文一般能ç�†è§£å…¶å†…涵,更难的是如何将Paxos真正应用到工程实践。 @@ -155,4 +163,4 @@ proposer leader在Multi Paxos中还有助于æ��å�‡æ€§èƒ½ï¼Œå¸¸æ€�下统一由lea [《分布å¼�系统ç�†è®ºåŸºç¡€ - æ—¶é—´ã€�时钟和事件顺åº�》](http://www.cnblogs.com/bangerlee/p/5448766.html) -[《分布å¼�系统ç�†è®ºåŸºç¡€ - CAP》](http://www.cnblogs.com/bangerlee/p/5328888.html) \ No newline at end of file +[《分布å¼�系统ç�†è®ºåŸºç¡€ - CAP》](http://www.cnblogs.com/bangerlee/p/5328888.html) diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Raft\343\200\201Zab.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Raft\343\200\201Zab.md" index a03b394..4530e9d 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Raft\343\200\201Zab.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232Raft\343\200\201Zab.md" @@ -1,3 +1,12 @@ +# 目录 + + * [**引言**](#引言) + * [**Raft**](#raft) + * [**Zab**](#zab) + * [**Paxosã€�Raftã€�Zabå†�比较**](#paxosã€�raftã€�zabå†�比较) + * [**å°�结**](#å°�结) + + 本文转自:[https://www.cnblogs.com/bangerlee/p/5991417.html](https://www.cnblogs.com/bangerlee/p/5991417.html) 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -22,7 +31,7 @@ [《分布å¼�系统ç�†è®ºè¿›é˜¶ - Paxos》](http://www.cnblogs.com/bangerlee/p/5655754.html)介ç»�了一致性å��è®®Paxos,今天我们æ�¥å­¦ä¹ å�¦å¤–两个常è§�的一致性å��议——Raftå’ŒZab。通过与Paxos对比,了解Raftå’ŒZab的核心æ€�想ã€�加深对一致性å��议的认识。 -**Raft** +## **Raft** Paxoså��å�‘于ç�†è®ºã€�对如何应用到工程实践æ��å�Šè¾ƒå°‘。ç�†è§£çš„难度加上现实的骨感,在生产环境中基于Paxos实现一个正确的分布å¼�系统é�žå¸¸éš¾[1]: @@ -51,7 +60,7 @@ Raft[2](#)在2013å¹´æ��出,æ��出的时间虽然ä¸�长,但已ç»�有很多系 Paxos中Leader的存在是为了æ��å�‡å†³è®®æ•ˆçŽ‡ï¼ŒLeader的有无和数目并ä¸�å½±å“�决议一致性,Raftè¦�求具备唯一Leader,并把一致性问题具体化为ä¿�æŒ�日志副本的一致性,以此实现相较Paxos而言更容易ç�†è§£ã€�更容易实现的目标。 -**Zab** +## **Zab** Zab[5](#)的全称是Zookeeper atomic broadcast protocol,是Zookeeper内部用到的一致性å��议。相比Paxos,Zab最大的特点是ä¿�è¯�强一致性(strong consistency,或å�«çº¿æ€§ä¸€è‡´æ€§linearizable consistency)。 @@ -85,7 +94,7 @@ Zab中æ¯�个事务对应一个zxid,它由两部分组æˆ�:,eå�³Leader 相比Paxos,Zab约æ�Ÿäº†äº‹åŠ¡é¡ºåº�ã€�适用于有强一致性需求的场景。 -**Paxosã€�Raftã€�Zabå†�比较** +## **Paxosã€�Raftã€�Zabå†�比较** 除Paxosã€�Raftå’ŒZab外,Viewstamped Replication(简称VR)[7](#)也是讨论比较多的一致性å��议。这些å��议包å�«å¾ˆå¤šå…±å�Œçš„内容(Leaderã€�quorumã€�state machineç­‰),因而我们ä¸�ç¦�è¦�问:Paxosã€�Raftã€�Zabå’ŒVR等分布å¼�一致性å��议区别到底在哪,还是根本就是一回事?[9] @@ -95,7 +104,7 @@ Paxosã€�Raftã€�Zabå’ŒVR都是解决一致性问题的å��议,Paxoså��议原文 相比Raftã€�Zabã€�VR,Paxos更纯粹ã€�更接近一致性问题本æº�,尽管Paxos倾å�‘ç�†è®ºï¼Œä½†ä¸�代表Paxosä¸�能应用于工程。基于Paxos的工程实践,须考虑具体需求场景(如一致性è¦�达到什么程度),å†�在Paxos原始语æ„�上进行包装。 -**å°�结** +## **å°�结** 以上介ç»�分布å¼�一致性å��è®®Raftã€�Zab的核心æ€�想,分æž�Raftã€�Zab与Paxos的异å�Œã€‚实现分布å¼�系统时,先从具体需求和场景考虑,Raftã€�Zabã€�VRã€�Paxosç­‰å��议没有ç»�对地好与ä¸�好,å�ªæ˜¯é€‚ä¸�适å�ˆã€‚ @@ -117,4 +126,4 @@ Paxosã€�Raftã€�Zabå’ŒVR都是解决一致性问题的å��议,Paxoså��议原文 [9][Can’t we all just agree?](https://blog.acolyer.org/2015/03/01/cant-we-all-just-agree/)The morning paper, 2015 -[10][Vive La Difference: Paxos vs. Viewstamped Replication vs. Zab](https://arxiv.org/pdf/1309.5671.pdf),Robbert van Renesse, Nicolas Schiper and Fred B. Schneider, 2014 \ No newline at end of file +[10][Vive La Difference: Paxos vs. Viewstamped Replication vs. Zab](https://arxiv.org/pdf/1309.5671.pdf),Robbert van Renesse, Nicolas Schiper and Fred B. Schneider, 2014 diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" index 51f65be..4dd3007 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" @@ -1,3 +1,13 @@ +# 目录 + + * [**ZK API**](#zk-api) + * [**ZK应用场景**](#zk应用场景) + * [**Leader选举**](#leader选举) + * [**é…�置管ç�†**](#é…�置管ç�†) + * [**ZK监控**](#zk监控) + * [**å°�结**](#å°�结) + + 本文转自 https://www.cnblogs.com/bangerlee/p/5268485.html 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -17,7 +27,6 @@ -## 分布å¼�æœ�务å��调员zookeeper - 应用场景和监控 zookeeper在分布å¼�系统中作为å��调员的角色,å�¯åº”用于Leader选举ã€�分布å¼�é”�ã€�é…�置管ç�†ç­‰æœ�务的实现。以下我们从zookeeper供的APIã€�应用场景和监控三方é�¢å­¦ä¹ å’Œäº†è§£zookeeper(以下简称ZK)。 @@ -25,7 +34,7 @@ zookeeper在分布å¼�系统中作为å��调员的角色,å�¯åº”用于Leader选 -**ZK API** +## **ZK API** ZK以Unix文件系统树结构的形å¼�管ç�†å­˜å‚¨çš„æ•°æ�®ï¼Œå›¾ç¤ºå¦‚下: @@ -52,11 +61,11 @@ ZKæ��供了以下API,供clientæ“�作znodeå’Œznode中存储的数æ�®ï¼š * setData(path, data, version):将data[]æ•°æ�®å†™å…¥å¯¹åº”path/versionçš„znode * getChildren(path, watch):返回指定znodeçš„å­�节点集å�ˆ -**ZK应用场景** +## **ZK应用场景** 基于以上ZKæ��供的znodeå’Œznodeæ•°æ�®çš„æ“�作,å�¯è½»æ�¾å®žçްLeader选举ã€�分布å¼�é”�ã€�é…�置管ç�†ç­‰æœ�务。 -**Leader选举** +## **Leader选举** 利用打上sequential标志的Ephemeral,我们å�¯ä»¥å®žçްLeader选举。å�‡è®¾éœ€è¦�从三个client中选å�–Leader,实现过程如下: @@ -77,7 +86,7 @@ ZKæ��供了以下API,供clientæ“�作znodeå’Œznode中存储的数æ�®ï¼š 分布å¼�é”�的实现与以上Leader选举的实现相å�Œï¼Œç¨�作修改,我们还å�¯ä»¥åŸºäºŽZK实现lease机制(有期é™�的授æ�ƒæœ�务)。 -**é…�置管ç�†** +## **é…�置管ç�†** znodeå�¯ä»¥å­˜å‚¨æ•°æ�®ï¼ŒåŸºäºŽè¿™ä¸€ç‚¹ï¼Œæˆ‘们å�¯ä»¥ç”¨ZK实现分布å¼�系统的é…�置管ç�†ï¼Œå�‡è®¾æœ‰æœ�务A,A扩容设备时需è¦�将相应新增的ip/portå�Œæ­¥åˆ°å…¨ç½‘æœ�务器的A.confé…�置,实现过程如下: @@ -96,7 +105,7 @@ cZxid = 0x2ffdeda3be …… æœ�务缩容的步骤类似,机器下线时将ZK相应节点删除,全网机器监å�¬åˆ°è¯¥äº‹ä»¶å�Žå°†é…�置中的设备剔除。 -**ZK监控** +## **ZK监控** ZK自身æ��供了一些“四字命令â€�,通过这些四字命令,我们å�¯ä»¥èŽ·å¾—ZK集群中,æŸ�å�°ZK的角色ã€�znodeæ•°ã€�å�¥åº·çжæ€�等信æ�¯ï¼š @@ -127,6 +136,6 @@ zk_znode_count 16216 å†�æ ¹æ�®è¿”回值判断添加ã€�删除znode是å�¦æˆ�功,从而判断该å�°ZK状æ€�是å�¦æ­£å¸¸ã€‚ -**å°�结** +## **å°�结** -zookeeper以目录树的形å¼�管ç�†æ•°æ�®ï¼Œæ��ä¾›znode监å�¬ã€�æ•°æ�®è®¾ç½®ç­‰æŽ¥å�£ï¼ŒåŸºäºŽè¿™äº›æŽ¥å�£ï¼Œæˆ‘们å�¯ä»¥å®žçްLeader选举ã€�é…�置管ç�†ã€�命å��æœ�务等功能。结å�ˆå››å­—命令,加上模拟zookeeper client 创建/删除znode,我们å�¯ä»¥å®žçް坹zookeeper的有效监控。在å�„ç§�分布å¼�系统中,我们ç»�常å�¯ä»¥çœ‹åˆ°zookeeper的身影。 \ No newline at end of file +zookeeper以目录树的形å¼�管ç�†æ•°æ�®ï¼Œæ��ä¾›znode监å�¬ã€�æ•°æ�®è®¾ç½®ç­‰æŽ¥å�£ï¼ŒåŸºäºŽè¿™äº›æŽ¥å�£ï¼Œæˆ‘们å�¯ä»¥å®žçްLeader选举ã€�é…�置管ç�†ã€�命å��æœ�务等功能。结å�ˆå››å­—命令,加上模拟zookeeper client 创建/删除znode,我们å�¯ä»¥å®žçް坹zookeeper的有效监控。在å�„ç§�分布å¼�系统中,我们ç»�常å�¯ä»¥çœ‹åˆ°zookeeper的身影。 diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" index b039216..1455063 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\200\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" @@ -1,3 +1,11 @@ +# 目录 + + * [**选举(electioin)**](#选举electioin) + * [**多数派(quorum)**](#多数派quorum) + * [**租约(lease)**](#租约lease) + * [**å°�结**](#å°�结) + + 本文转自:https://www.cnblogs.com/bangerlee/p/5767845.html 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -25,7 +33,7 @@ 下é�¢æˆ‘们就æ�¥å­¦ä¹ åˆ†å¸ƒå¼�系统ç�†è®ºä¸­çš„选举ã€�多数派和租约。 -**选举(electioin)** +## **选举(electioin)** 一致性问题(consistency)是独立的节点间如何达æˆ�决议的问题,选出大家都认å�¯çš„leader本质上也是一致性问题,因而如何应对宕机æ�¢å¤�ã€�网络分化等在leader选举中也需è¦�考é‡�。 @@ -45,7 +53,7 @@ Bully算法[1]是最常è§�的选举算法,其è¦�求æ¯�个节点对 在一致性算法[Paxos](http://www.cnblogs.com/bangerlee/p/5655754.html)ã€�ZAB[2]ã€�Raft[3]中,为æ��å�‡å†³è®®æ•ˆçއå�‡æœ‰èŠ‚ç‚¹å……å½“leader的角色。ZABã€�Raft中æ��述了具体的leader选举实现,与Bully算法类似ZAB中使用zxid标识节点,具有最大zxid的节点表示其所具备的事务(transaction)最新ã€�被选为leader。 -**多数派(quorum)** +## **多数派(quorum)** 在网络分化的场景下以上Bully算法会é�‡åˆ°ä¸€ä¸ªé—®é¢˜ï¼Œè¢«åˆ†éš”的节点都认为自己具有最大的åº�å�·ã€�将产生多个leader,这时候就需è¦�引入多数派(quorum)[4]。多数派的æ€�路在分布å¼�系统中很常è§�,其确ä¿�网络分化情况下决议唯一。 @@ -55,7 +63,7 @@ Bully算法[1]是最常è§�的选举算法,其è¦�求æ¯�个节点对 多数派的æ€�路还被应用于副本(replica)管ç�†ï¼Œæ ¹æ�®ä¸šåŠ¡å®žé™…è¯»å†™æ¯”ä¾‹è°ƒæ•´å†™å‰¯æœ¬æ•°Vwã€�读副本数Vr,用以在å�¯é� æ€§å’Œæ€§èƒ½æ–¹é�¢å�–得平衡[5]。 -**租约(lease)** +## **租约(lease)** 选举中很é‡�è¦�的一个问题,以上尚未æ��到:怎么判断leaderä¸�å�¯ç”¨ã€�什么时候应该å�‘èµ·é‡�新选举?最先å�¯èƒ½æƒ³åˆ°ä¼šé€šè¿‡å¿ƒè·³(heart beat)判别leader状æ€�是å�¦æ­£å¸¸ï¼Œä½†åœ¨ç½‘络拥塞或瞬断的情况下,这容易导致出现å�Œä¸»ã€‚ @@ -71,7 +79,7 @@ Bully算法[1]是最常è§�的选举算法,其è¦�求æ¯�个节点对 租约机制确ä¿�了一个时刻最多å�ªæœ‰ä¸€ä¸ªleader,é�¿å…�å�ªä½¿ç”¨å¿ƒè·³æœºåˆ¶äº§ç”Ÿå�Œä¸»çš„问题。在实践应用中,zookeeperã€�ectdå�¯ç”¨äºŽç§Ÿçº¦é¢�å�‘。 -**å°�结** +## **å°�结** 在分布å¼�系统ç�†è®ºå’Œå®žè·µä¸­ï¼Œå¸¸è§�leaderã€�quorumå’Œlease的身影。分布å¼�系统内ä¸�一定事事å��商ã€�事事民主,leader的存在有助于æ��å�‡å†³è®®æ•ˆçŽ‡ã€‚ diff --git "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\266\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\266\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" index c2317ac..abc4289 100644 --- "a/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\266\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" +++ "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\266\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" @@ -1,3 +1,11 @@ +# 目录 + * [**引言**](#引言) + * [**Multi Paxos**](#multi-paxos) + * [**Fast Paxos**](#fast-paxos) + * [**EPaxos**](#epaxos) + * [**å°�结**](#å°�结) + + 本文转自:https://www.cnblogs.com/bangerlee/p/6189646.html 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -16,7 +24,7 @@ -**引言** +## **引言** [《分布å¼�系统ç�†è®ºè¿›é˜¶ - Paxos》](http://www.cnblogs.com/bangerlee/p/5655754.html)中我们了解了Basic Paxosã€�Multi Paxos的基本原ç�†ï¼Œä½†å¦‚果想把Paxos应用于工程实践,了解基本原ç�†è¿˜ä¸�够。 @@ -24,7 +32,7 @@ ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/116770-20161217185911917-43631009.jpg) -**Multi Paxos** +## **Multi Paxos** 首先我们æ�¥å›žé¡¾ä¸€ä¸‹Multi Paxos,Multi Paxos在Basic Paxos的基础上确定一系列值,其决议过程如下: @@ -44,7 +52,7 @@ phase2b: acceptor将决议å�Œæ­¥ç»™learner Multi Paxos中leader用于é�¿å…�æ´»é”�,但leader的存在会带æ�¥å…¶ä»–问题,一是如何选举和ä¿�æŒ�唯一leader(虽然无leader或多leaderä¸�å½±å“�一致性,但影å“�决议进程progress),二是充当leader的节点会承担更多压力,如何å�‡è¡¡èŠ‚ç‚¹çš„è´Ÿè½½ã€‚Mencius[1]æ��出节点轮æµ�æ‹…ä»»leader,以达到å�‡è¡¡è´Ÿè½½çš„目的;租约(lease)å�¯ä»¥å¸®åŠ©å®žçŽ°å”¯ä¸€leader,但leader故障情况下å�¯å¯¼è‡´æœ�务短期ä¸�å�¯ç”¨ã€‚ -**Fast Paxos** +## **Fast Paxos** 在Multi Paxos中,proposer -> leader -> acceptor -> learner,从æ��议到完æˆ�决议共ç»�过3次通信,能ä¸�能å‡�少通信步骤? @@ -56,7 +64,7 @@ Multi Paxos里æ��议都由leaderæ��出,因而ä¸�存在一次决议出现多个 Paxos自身éš�å�«çš„一个特性也å�¯ä»¥è¾¾åˆ°å‡�少通信步骤的目标,如果acceptor上一次确定(chosen)çš„æ��è®®æ�¥è‡ªproposerA,则当次决议proposerAå�¯ä»¥ç›´æŽ¥æ��è®®å‡�少一次通信步骤。如果想实现这样的效果,需è¦�在proposerã€�acceptor记录上一次决议确定(chosen)的历å�²ï¼Œç”¨ä»¥åœ¨æ��è®®å‰�知é�“哪个proposerçš„æ��议上一次被确定ã€�当次决议能ä¸�能节çœ�一次通信步骤。 -**EPaxos** +## **EPaxos** 除了从å‡�少通信步骤的角度æ��高Paxos决议效率外,还有其他方é�¢å�¯ä»¥é™�低Paxos决议时延,比如Generalized Paxos[3]æ��出ä¸�冲çª�çš„æ��è®®(例如对ä¸�å�Œkey的写请求)å�¯ä»¥å�Œæ—¶å†³è®®ã€�以é™�低Paxos时延。 @@ -72,7 +80,7 @@ Paxos自身éš�å�«çš„一个特性也å�¯ä»¥è¾¾åˆ°å‡�少通信步骤的目标,如 为判断决议是å�¦ç›¸äº’å½±å“�,实现EPaxos得记录决议之间的ä¾�赖关系。 -**å°�结** +## **å°�结** 以上介ç»�了几个基于Paxosçš„å�˜ç§�,Mencius中节点轮æµ�å�šleaderã€�å�‡è¡¡èŠ‚ç‚¹è´Ÿè½½ï¼ŒFast Paxoså‡�少一次通信步骤,Generalized Paxoså…�许互ä¸�å½±å“�的决议å�Œæ—¶è¿›è¡Œï¼ŒEPaxos无全局leaderã€�å�„节点平等分担负载。 @@ -84,4 +92,4 @@ Paxos自身éš�å�«çš„一个特性也å�¯ä»¥è¾¾åˆ°å‡�少通信步骤的目标,如 [3][Generalized Consensus and Paxos](http://diyhpl.us/~bryan/papers2/distributed/distributed-systems/generalized-consensus-and-paxos.2004.pdf),Leslie Lamport, 2004 -[4][There Is More Consensus in Egalitarian Parliaments](http://sigops.org/sosp/sosp13/papers/p358-moraru.pdf),Iulian Moraru, David G. Andersen, Michael Kaminsky, 2013 \ No newline at end of file +[4][There Is More Consensus in Egalitarian Parliaments](http://sigops.org/sosp/sosp13/papers/p358-moraru.pdf),Iulian Moraru, David G. Andersen, Michael Kaminsky, 2013 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" index 29837ba..6983151 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" @@ -1,3 +1,4 @@ + # 目录 * [è´Ÿè½½å�‡è¡¡çš„原ç�†](#è´Ÿè½½å�‡è¡¡çš„原ç�†) @@ -555,4 +556,4 @@ public class TestIpHash { } } -```` \ No newline at end of file +```` diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" index 5bc10ee..adb2c2c 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" @@ -1,3 +1,5 @@ + + # 目录 * [Redisé…�ç½®](#redisé…�ç½®) diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" index 01a7be8..5212a27 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" @@ -1,3 +1,31 @@ +# 目录 + + * [ZABå��è®®](#zabå��è®®) + * [消æ�¯å¹¿æ’­æ¨¡å¼�](#消æ�¯å¹¿æ’­æ¨¡å¼�) + * [崩溃æ�¢å¤�](#崩溃æ�¢å¤�) + * [æ•°æ�®å�Œæ­¥](#æ•°æ�®å�Œæ­¥) + * [ZABå��议原ç�†](#zabå��议原ç�†) + * [Zookeeper设计目标](#zookeeper设计目标) +* [ZAB与FastLeaderElection选主算法æµ�程详解](#zab与fastleaderelection选主算法æµ�程详解) + * [选择机制中的概念](#选择机制中的概念) + * [æœ�务器ID](#æœ�务器id) + * [æ•°æ�®ID](#æ•°æ�®id) + * [逻辑时钟](#逻辑时钟) + * [选举状æ€�](#选举状æ€�) + * [选举消æ�¯å†…容](#选举消æ�¯å†…容) + * [选举æµ�程图](#选举æµ�程图) + * [判断是å�¦å·²ç»�胜出](#判断是å�¦å·²ç»�胜出) + * [选举æµ�程简述](#选举æµ�程简述) + * [几ç§�领导选举场景](#几ç§�领导选举场景) + * [集群å�¯åЍ领坼选䏾](#集群å�¯åЍ领坼选䏾) + * [Followeré‡�å�¯](#followeré‡�å�¯) + * [Leaderé‡�å�¯](#leaderé‡�å�¯) +* [一致性ä¿�è¯�](#一致性ä¿�è¯�) + * [Commit过的数æ�®ä¸�丢失](#commit过的数æ�®ä¸�丢失) + * [未Commit过的消æ�¯å¯¹å®¢æˆ·ç«¯ä¸�å�¯è§�")未Commit过的消æ�¯å¯¹å®¢æˆ·ç«¯ä¸�å�¯è§�](#未commit过的消æ�¯å¯¹å®¢æˆ·ç«¯ä¸�å�¯è§�未commit过的消æ�¯å¯¹å®¢æˆ·ç«¯ä¸�å�¯è§�) +* [总结](#总结) + + 本文内容å�‚考网络,侵删 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -348,4 +376,4 @@ ZABå��è®®ä¿�è¯�了在Leader选举的过程中,已ç»�被Commit的数æ�®ä¸�会 * 由于使用主从å¤�制模å¼�,所有的写æ“�作都è¦�ç”±Leader主导完æˆ�,而读æ“�作å�¯é€šè¿‡ä»»æ„�节点完æˆ�,因此Zookeeper读性能远好于写性能,更适å�ˆè¯»å¤šå†™å°‘的场景 * 虽然使用主从å¤�制模å¼�,å�Œä¸€æ—¶é—´å�ªæœ‰ä¸€ä¸ªLeader,但是Failover机制ä¿�è¯�了集群ä¸�存在å�•点失败(SPOF)的问题 * ZABå��è®®ä¿�è¯�了Failover过程中的数æ�®ä¸€è‡´æ€§ -* æœ�务器收到数æ�®å�Žå…ˆå†™æœ¬åœ°æ–‡ä»¶å†�进行处ç�†ï¼Œä¿�è¯�了数æ�®çš„æŒ�久性 \ No newline at end of file +* æœ�务器收到数æ�®å�Žå…ˆå†™æœ¬åœ°æ–‡ä»¶å†�进行处ç�†ï¼Œä¿�è¯�了数æ�®çš„æŒ�久性 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" index 7e5a1bf..e007cd6 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" @@ -1,3 +1,4 @@ + # 目录 * [一.ZooKeeper典型应用场景实践](#一zookeeper典型应用场景实践) @@ -479,4 +480,4 @@ Master选举 第二类,和分布å¼�é”�æœ�务中的控制时åº�场景基本原ç�†ä¸€è‡´ï¼Œå…¥åˆ—有编å�·ï¼Œå‡ºåˆ—按编å�·ã€‚ -å�Œæ­¥é˜Ÿåˆ—。一个job由多个task组æˆ�,å�ªæœ‰æ‰€æœ‰ä»»åŠ¡å®Œæˆ�å�Žï¼Œjobæ‰�è¿�行完æˆ�。å�¯ä¸ºjob创建一个/job目录,然å�Žåœ¨è¯¥ç›®å½•下,为æ¯�个完æˆ�çš„task创建一个临时znode,一旦临时节点数目达到task总数,则jobè¿�行完æˆ�。 \ No newline at end of file +å�Œæ­¥é˜Ÿåˆ—。一个job由多个task组æˆ�,å�ªæœ‰æ‰€æœ‰ä»»åŠ¡å®Œæˆ�å�Žï¼Œjobæ‰�è¿�行完æˆ�。å�¯ä¸ºjob创建一个/job目录,然å�Žåœ¨è¯¥ç›®å½•下,为æ¯�个完æˆ�çš„task创建一个临时znode,一旦临时节点数目达到task总数,则jobè¿�行完æˆ�。 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" index 70fce53..a810400 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" @@ -1,3 +1,5 @@ + + # 目录 * [4.1 é…�置文件](#41-é…�置文件) @@ -347,4 +349,4 @@ server.3=127.0.0.1:2890:3890 ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230407212836.png)) -å�¯è§�,关闭zk3以å�Žï¼Œç”±äºŽé›†ç¾¤ä¸­çš„å�¯ç”¨Serverå�ªå‰©ä¸‹ä¸€å�°ï¼ˆè¾¾ä¸�到集群总数的å�Šæ•°ä»¥ä¸Šï¼‰ï¼Œ**集群将处于ä¸�å�¯ç”¨çš„状æ€�。** \ No newline at end of file +å�¯è§�,关闭zk3以å�Žï¼Œç”±äºŽé›†ç¾¤ä¸­çš„å�¯ç”¨Serverå�ªå‰©ä¸‹ä¸€å�°ï¼ˆè¾¾ä¸�到集群总数的å�Šæ•°ä»¥ä¸Šï¼‰ï¼Œ**集群将处于ä¸�å�¯ç”¨çš„状æ€�。** diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" index 15d55a7..decea79 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" @@ -1,3 +1,4 @@ + # 目录 * [åˆ�步认识RocketMQ的核心模å�—](#åˆ�步认识rocketmq的核心模å�—) diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" index 2ea07c4..1563d5a 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" @@ -1,6 +1,12 @@ # 目录 -* [分布å¼�ID生æˆ�器 | 架构师之路](#分布å¼�id生æˆ�器--架构师之路) + * [**一ã€�需求缘起**](#一ã€�需求缘起) + * [**二ã€�常è§�方法ã€�ä¸�足与优化**](#二ã€�常è§�方法ã€�ä¸�足与优化) + * [**方法二:å�•点批é‡�ID生æˆ�æœ�务**](#方法二:å�•点批é‡�id生æˆ�æœ�务) + * [**方法三:uuid/guid**](#方法三:uuidguid) + * [**方法四:å�–当å‰�毫秒数**](#方法四:å�–当å‰�毫秒数) + + 本文内容å�‚考网络,侵删 @@ -19,11 +25,10 @@ 如果对本系列文章有什么建议,或者是有什么疑问的è¯�,也å�¯ä»¥å…³æ³¨å…¬ä¼—å�·ã€�Java技术江湖】è�”系作者,欢迎你å�‚与本系列å�šæ–‡çš„åˆ›ä½œå’Œä¿®è®¢ã€‚ -## 分布å¼�ID生æˆ�器 | 架构师之路 转自:58沈剑架构师之路2017-06-25 -**一ã€�需求缘起** +## **一ã€�需求缘起** 几乎所有的业务系统,都有生æˆ�一个唯一记录标识的需求,例如: @@ -65,7 +70,7 @@ select message-id/ (order by message-id)/limit 100 这也是本文è¦�讨论的核心问题:**如何高效生æˆ�趋势有åº�的全局唯一ID。** -**二ã€�常è§�方法ã€�ä¸�足与优化** +## **二ã€�常è§�方法ã€�ä¸�足与优化** **方法一:使用数æ�®åº“çš„auto_incrementæ�¥ç”Ÿæˆ�全局唯一递增ID** @@ -103,7 +108,7 @@ select message-id/ (order by message-id)/limit 100 为了解决上述两个问题,引出了第二个常è§�的方案。 -**方法二:å�•点批é‡�ID生æˆ�æœ�务** +## **方法二:å�•点批é‡�ID生æˆ�æœ�务** 分布å¼�系统之所以难,很é‡�è¦�的原因之一是“没有一个全局时钟,难以ä¿�è¯�ç»�对的时åº�â€�,è¦�想ä¿�è¯�ç»�对的时åº�,还是å�ªèƒ½ä½¿ç”¨å�•点æœ�务,用本地时钟ä¿�è¯�“ç»�对时åº�â€�。 @@ -143,7 +148,7 @@ ID生æˆ�æœ�务å�‡è®¾æ¯�次批é‡�拉å�–6个ID,æœ�务访问数æ�®åº“,将当å‰� å�¦å¤–,ID-gen-service也å�¯ä»¥å®žæ–½æ°´å¹³æ‰©å±•,以解决上述缺点(3),但会引å�‘一致性问题,具体解决方案详è§�《》。 -**方法三:uuid/guid** +## **方法三:uuid/guid** ä¸�管是通过数æ�®åº“,还是通过æœ�务æ�¥ç”Ÿæˆ�ID,业务方Application都需è¦�进行一次远程调用,比较耗时。 @@ -165,7 +170,7 @@ string ID =GenUUID(); * uuid过长,往往用字符串表示,作为主键建立索引查询效率低,常è§�优化方案为“转化为两个uint64整数存储â€�或者“折å�Šå­˜å‚¨â€�(折å�Šå�Žä¸�能ä¿�è¯�唯一性) -**方法四:å�–当å‰�毫秒数** +## **方法四:å�–当å‰�毫秒数** uuid是一个本地算法,生æˆ�性能高,但无法ä¿�è¯�趋势递增,且作为字符串ID检索效率低,有没有一ç§�能ä¿�è¯�递增的本地算法呢? @@ -237,4 +242,4 @@ snowflake是twitterå¼€æº�的分布å¼�ID生æˆ�算法,其**核心æ€�想为,** **缺点**: -* 由于“没有一个全局时钟â€�,æ¯�å�°æœ�务器分é…�çš„ID是ç»�对递增的,但从全局看,生æˆ�çš„IDå�ªæ˜¯è¶‹åŠ¿é€’å¢žçš„ï¼ˆæœ‰äº›æœ�务器的时间早,有些æœ�务器的时间晚) \ No newline at end of file +* 由于“没有一个全局时钟â€�,æ¯�å�°æœ�务器分é…�çš„ID是ç»�对递增的,但从全局看,生æˆ�çš„IDå�ªæ˜¯è¶‹åŠ¿é€’å¢žçš„ï¼ˆæœ‰äº›æœ�务器的时间早,有些æœ�务器的时间晚) diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" index 894fcaa..50e6f52 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" @@ -1,13 +1,20 @@ # 目录 + * [一ã€�问题的æ��出](#一ã€�问题的æ��出) - * [1\. 什么是Session?](#1-什么是session?) - * [2\. 什么是Session一致性问题?](#2-什么是session一致性问题?) + * [1. 什么是Session?](#1-什么是session?) + * [2. 什么是Session一致性问题?](#2-什么是session一致性问题?) * [二ã€�Session一致性解决方案](#二ã€�session一致性解决方案) - * [1\. Session Stiky](#1-session-stiky) - * [2\. Session Replication](#2-session-replication) - * [3\. Sessionæ•°æ�®é›†ä¸­å­˜å‚¨](#3-sessionæ•°æ�®é›†ä¸­å­˜å‚¨) - * [4\. Cookie Based](#4-cookie-based) + * [1. Session Stiky](#1-session-stiky) + * [2. Session Replication](#2-session-replication) + * [3. Sessionæ•°æ�®é›†ä¸­å­˜å‚¨](#3-sessionæ•°æ�®é›†ä¸­å­˜å‚¨) + * [4. Cookie Based](#4-cookie-based) * [三ã€�总结](#三ã€�总结) + * [一致性Hash概述](#一致性hash概述) + * [一致性hash的特性](#一致性hash的特性) + * [虚拟节点](#虚拟节点) + * [å�‡åŒ€ä¸€è‡´æ€§hash](#å�‡åŒ€ä¸€è‡´æ€§hash) + * [总结](#总结) + 本文内容å�‚考网络,侵删 @@ -29,12 +36,12 @@ # 一ã€�问题的æ��出 -## 1\. 什么是Session? +## 1. 什么是Session? 用户使用网站的æœ�务,需è¦�使用æµ�览器与Webæœ�务器进行多次交互。HTTPå��议本身是无状æ€�的,需è¦�基于HTTPå��议支æŒ�会è¯�状æ€�(Session State)的机制。具体的实现方å¼�是:在会è¯�开始时,分é…�一个 唯一的会è¯�标识(SessionID),并通过Cookie将这个标识告诉æµ�览器,以å�Žæ¯�次请求的时候,æµ�览器都会带上这个会è¯�标识SessionIDæ�¥å‘Šè¯‰Webæœ�务器这个请求是属于哪个会è¯�的。在Webæœ�务器上,å�„个会è¯�都有独立的存储,ä¿�å­˜ä¸�å�Œä¼šè¯�的信æ�¯ã€‚如果é�‡åˆ°ç¦�用Cookie的情况,一般的å�šæ³•å°±æ˜¯æŠŠè¿™ä¸ªä¼šè¯�标识放到URLçš„å�‚数中。 -## 2\. 什么是Session一致性问题? +## 2. 什么是Session一致性问题? 当Webæœ�务器从一å�°å�˜ä¸ºå¤šå�°æ—¶ï¼Œå°±ä¼šå‡ºçްSession一致性问题。 @@ -44,7 +51,7 @@ # 二ã€�Session一致性解决方案 -## 1\. Session Stiky +## 1. Session Stiky 在å�•机的情况下,会è¯�ä¿�存在å�•机上,请求也是由这个机器处ç�†ï¼Œå› æ­¤ä¸�会有问题。当Webæœ�务器å�˜ä¸ºå¤šå�°ä»¥å�Žï¼Œå¦‚æžœä¿�è¯�å�Œä¸€ä¸ªä¼šè¯�的请求都在å�Œä¸€ä¸ªWebæœ�务器上处ç�†ï¼Œåˆ™å¯¹è¯¥ä¼šè¯�æ�¥è¯´ï¼Œä¸Žä¹‹å‰�å�•机的情况是一样的。 @@ -62,7 +69,7 @@ 打个比方æ�¥è¯´ï¼Œå¯¹äºŽSession Stiky,如果说Webæœ�务器是我们æ¯�次å�ƒé¥­çš„饭店,会è¯�æ•°æ�®å°±æ˜¯æˆ‘们å�ƒé¥­ç”¨çš„碗筷。è¦�ä¿�è¯�æ¯�次å�ƒé¥­éƒ½ç”¨è‡ªå·±çš„碗筷,我就把é¤�具存在æŸ�一家,并且æ¯�次都去这家店å�ƒï¼Œè¿™æ˜¯ä¸ªä¸�错的主æ„�。 -## 2\. Session Replication +## 2. Session Replication 如果我们继续以去饭店å�ƒé¥­ç±»æ¯”,那么除了å‰�é�¢çš„æ–¹å¼�之外,如果我在æ¯�个店都存放一套自己的é¤�具,就å�¯ä»¥æ›´åŠ è‡ªç”±åœ°é€‰æ‹©é¥­åº—ã€‚Session Replication就是这样一ç§�æ–¹å¼�,如下图所示。 @@ -78,7 +85,7 @@ 这就是Session Replication方案。这个方案是é� åº”用容器æ�¥å®Œæˆ�Sessionçš„å¤�制从而使得应用解决Session问题的,应用本身并ä¸�关心这个事情。ä¸�过,这个方案并ä¸�适å�ˆé›†ç¾¤æœºå™¨æ•°å¤šçš„场景。如果å�ªæœ‰å‡ å�°æœºå™¨ï¼Œç”¨è¯¥æ–¹æ¡ˆæ˜¯å�¯ä»¥çš„。 -## 3\. Sessionæ•°æ�®é›†ä¸­å­˜å‚¨ +## 3. Sessionæ•°æ�®é›†ä¸­å­˜å‚¨ å�Œæ ·æ˜¯å¸Œæœ›å�Œä¸€ä¸ªä¼šè¯�的请求å�¯ä»¥å�‘到ä¸�å�Œçš„Webæœ�务器上,å‰�é�¢çš„Session Replication是一ç§�方案,还有一ç§�方案就是把Sessionæ•°æ�®é›†ä¸­å­˜å‚¨èµ·æ�¥ï¼Œç„¶å�Žä¸�å�ŒWebæœ�务器从å�Œæ ·çš„地方æ�¥èŽ·å�–Session。其大概的结构如下图所示: @@ -93,7 +100,7 @@ 相对于Session Replication,当Webæœ�务器数é‡�比较大时ã€�Session数比较多的时候,集中存储方案的优势是é�žå¸¸æ˜Žæ˜¾çš„。 -## 4\. Cookie Based +## 4. Cookie Based 对于Cookie Based方案,它对å�Œä¸€ä¸ªä¼šè¯�çš„ä¸�å�Œè¯·æ±‚也是ä¸�é™�制具体处ç�†æœºå™¨çš„。与Session Replicationå’ŒSessionæ•°æ�®é›†ä¸­ç®¡ç�†çš„æ–¹æ¡ˆä¸�å�Œï¼Œè¿™ä¸ªæ–¹æ¡ˆæ˜¯é€šè¿‡Cookieæ�¥ä¼ é€’Sessionæ•°æ�®çš„。具体如下图所示。 @@ -113,13 +120,13 @@ 综å�ˆè€Œè¨€ï¼Œä¸Šè¿°æ‰€æœ‰æ–¹æ¡ˆéƒ½æ˜¯è§£å†³session问题的方案,对于大型网站æ�¥è¯´ï¼ŒSession Stickyå’ŒSession集中管ç�†æ˜¯æ¯”较好的方案。 -一ã€�å‰�言 + 在解决分布å¼�系统中负载å�‡è¡¡çš„问题时候å�¯ä»¥ä½¿ç”¨Hash算法让固定的一部分请求è�½åˆ°å�Œä¸€å�°æœ�务器上,这样æ¯�å�°æœ�务器固定处ç�†ä¸€éƒ¨åˆ†è¯·æ±‚(并维护这些请求的信æ�¯ï¼‰ï¼Œèµ·åˆ°è´Ÿè½½å�‡è¡¡çš„作用。 但是普通的余数hash(hash(比如用户id)%æœ�务器机器数)算法伸缩性很差,当新增或者下线æœ�务器机器时候,用户id与æœ�务器的映射关系会大é‡�失效。一致性hash则利用hash环对其进行了改进。 -二ã€�一致性Hash概述 +## 一致性Hash概述 为了能直观的ç�†è§£ä¸€è‡´æ€§hash原ç�†ï¼Œè¿™é‡Œç»“å�ˆä¸€ä¸ªç®€å�•的例å­�æ�¥è®²è§£ï¼Œå�‡è®¾æœ‰4å�°æœ�务器,地å�€ä¸ºip1,ip2,ip3,ip4。 @@ -156,7 +163,7 @@ hash环上顺时针从整数0开始,一直到最大正整数,我们根æ�®å›› æ ¹æ�®é¡ºæ—¶é’ˆè§„则å�¯çŸ¥ä¹‹å‰�user1的请求应该被ip1æœ�务器处ç�†ï¼ŒçŽ°åœ¨è¢«æ–°å¢žçš„ip5æœ�务器处ç�†ï¼Œå…¶ä»–用户的请求处ç�†æœ�务器ä¸�å�˜ï¼Œä¹Ÿå°±æ˜¯æ–°å¢žçš„æœ�务器顺时针最近的æœ�务器的一部分请求会被新增的æœ�务器所替代。 -三ã€�一致性hash的特性 +## 一致性hash的特性 å�•调性(Monotonicity),å�•调性是指如果已ç»�有一些请求通过哈希分派到了相应的æœ�务器进行处ç�†ï¼Œå�ˆæœ‰æ–°çš„æœ�务器加入到系统中时候,应ä¿�è¯�原有的请求å�¯ä»¥è¢«æ˜ å°„到原有的或者新的æœ�务器中去,而ä¸�会被映射到原æ�¥çš„其它æœ�务器上去。 这个通过上é�¢æ–°å¢žæœ�务器ip5å�¯ä»¥è¯�明,新增ip5å�Žï¼ŒåŽŸæ�¥è¢«ip1处ç�†çš„user6现在还是被ip1处ç�†ï¼ŒåŽŸæ�¥è¢«ip1处ç�†çš„user5现在被新增的ip5处ç�†ã€‚分散性(Spread):分布å¼�环境中,客户端请求时候å�¯èƒ½ä¸�知é�“所有æœ�务器的存在,å�¯èƒ½å�ªçŸ¥é�“其中一部分æœ�务器,在客户端看æ�¥ä»–看到的部分æœ�务器会形æˆ�一个完整的hash环。如果多个客户端都把部分æœ�务器作为一个完整hash环,那么å�¯èƒ½ä¼šå¯¼è‡´ï¼Œå�Œä¸€ä¸ªç”¨æˆ·çš„请求被路由到ä¸�å�Œçš„æœ�务器进行处ç�†ã€‚è¿™ç§�情况显然是应该é�¿å…�的,因为它ä¸�能ä¿�è¯�å�Œä¸€ä¸ªç”¨æˆ·çš„请求è�½åˆ°å�Œä¸€ä¸ªæœ�务器。所谓分散性是指上述情况å�‘生的严é‡�程度。平衡性(Balance):平衡性也就是说负载å�‡è¡¡ï¼Œæ˜¯æŒ‡å®¢æˆ·ç«¯hashå�Žçš„请求应该能够分散到ä¸�å�Œçš„æœ�务器上去。一致性hashå�¯ä»¥å�šåˆ°æ¯�个æœ�务器都进行处ç�†è¯·æ±‚,但是ä¸�能ä¿�è¯�æ¯�个æœ�务器处ç�†çš„请求的数é‡�大致相å�Œï¼Œå¦‚下图 @@ -166,7 +173,7 @@ hash环上顺时针从整数0开始,一直到最大正整数,我们根æ�®å›› æœ�务器ip1,ip2,ip3ç»�过hashå�Žè�½åˆ°äº†ä¸€è‡´æ€§hash环上,从图中hash值分布å�¯çŸ¥ip1会负责处ç�†å¤§æ¦‚80%的请求,而ip2å’Œip3则å�ªä¼šè´Ÿè´£å¤„ç�†å¤§æ¦‚20%的请求,虽然三个机器都在处ç�†è¯·æ±‚,但是明显æ¯�个机器的负载ä¸�å�‡è¡¡ï¼Œè¿™æ ·ç§°ä¸ºä¸€è‡´æ€§hash的倾斜,虚拟节点的出现就是为了解决这个问题。 -五ã€�虚拟节点 +## 虚拟节点 当æœ�务器节点比较少的时候会出现上节所说的一致性hash倾斜的问题,一个解决方法是多加机器,但是加机器是有æˆ�本的,那么就加虚拟节点,比如上é�¢ä¸‰ä¸ªæœºå™¨ï¼Œæ¯�个机器引入1个虚拟节点å�Žçš„一致性hash环的图如下: ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230407214322.png) @@ -177,7 +184,7 @@ hash环上顺时针从整数0开始,一直到最大正整数,我们根æ�®å›› å�¯çŸ¥å½“物ç�†æœºå™¨æ•°ç›®ä¸ºM,虚拟节点为N的时候,实际hash环上节点个数为M*(N+1)。比如当客户端计算的hash值处于ip2å’Œip3或者处于ip2-1å’Œip3-1之间时候使用ip3æœ�务器进行处ç�†ã€‚ -å…­ã€�å�‡åŒ€ä¸€è‡´æ€§hash +## å�‡åŒ€ä¸€è‡´æ€§hash 上节我们使用虚拟节点å�Žçš„图看起æ�¥æ¯”较å�‡è¡¡ï¼Œä½†æ˜¯å¦‚果生æˆ�虚拟节点的算法ä¸�够好很å�¯èƒ½ä¼šå¾—到下é�¢çš„环: ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230407214331.png) @@ -191,6 +198,6 @@ hash环上顺时针从整数0开始,一直到最大正整数,我们根æ�®å›› å�‡åŒ€ä¸€è‡´æ€§hash的目标是如果æœ�务器有Nå�°ï¼Œå®¢æˆ·ç«¯çš„hash值有M个,那么æ¯�个æœ�务器应该处ç�†å¤§æ¦‚M/N个用户的。也就是æ¯�å�°æœ�务器负载尽é‡�å�‡è¡¡ã€‚dubboæ��供的一致性hashè´Ÿè½½å�‡è¡¡ç®—法就是ä¸�å�‡åŒ€çš„,我们自己实现了dubboçš„spi扩展实现了å�‡åŒ€ä¸€è‡´æ€§hash. -七ã€�总结 +## 总结 -在分布å¼�系统中一致性hashèµ·ç�€ä¸�å�¯å¿½ç•¥çš„地ä½�,无论是分布å¼�缓存,还是分布å¼�Rpc框架的负载å�‡è¡¡ç­–略都有所使用。 \ No newline at end of file +在分布å¼�系统中一致性hashèµ·ç�€ä¸�å�¯å¿½ç•¥çš„地ä½�,无论是分布å¼�缓存,还是分布å¼�Rpc框架的负载å�‡è¡¡ç­–略都有所使用。 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" index 18256b0..c4a809d 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" @@ -1,3 +1,5 @@ + + # 目录 * [2PC](#2pc) @@ -481,4 +483,4 @@ Leader 会等待大多数的 Follower 也进行了修改,然å�Žæ‰�将修改æ�� What is CAP theorem in distributed database system? NEAT ALGORITHMS - PAXOS Raft: Understandable Distributed Consensus - Paxos By Example \ No newline at end of file + Paxos By Example diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" index 16296c9..77adff5 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" @@ -1,3 +1,17 @@ +# 目录 + +* [一ã€�概述](#一ã€�概述) +* [二ã€�ç�†è®º](#二ã€�ç�†è®º) + * [2.1ã€�二阶段æ��交](#21ã€�二阶段æ��交) + * [2.2ã€�三阶段æ��交](#22ã€�三阶段æ��交) + * [2.3ã€�TCC柔性事务](#23ã€�tcc柔性事务) + * [2.4ã€�Seata](#24ã€�seata) +* [三ã€�应用场景](#三ã€�应用场景) +* [å››ã€�总结](#å››ã€�总结) +* [五ã€�å�‚考æº�ç �](#五ã€�å�‚考æº�ç �) + + + > æ•°æ�®ä¸�会无缘无故丢失,也ä¸�会莫å��其妙增加 # 一ã€�概述 @@ -154,4 +168,4 @@ CREATE TABLE `tb_notice_message` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 编程文档:https://gitee.com/cicadasmile/butte-java-note应用仓库:https://gitee.com/cicadasmile/butte-flyer-parent ``` -本文转载自 https://mp.weixin.qq.com/s/xa9Giv9xdSM3AahwN3WckQ \ No newline at end of file +本文转载自 https://mp.weixin.qq.com/s/xa9Giv9xdSM3AahwN3WckQ diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" index d364788..e3e51ac 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" @@ -1,3 +1,4 @@ + # 目录 * [1ã€�分布å¼�](#1ã€�分布å¼�) * [2ã€�集群(Cluster)](#2ã€�集群(cluster)) diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" index f25a7d8..45da3d8 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" @@ -1,5 +1,14 @@ # 目录 +* [目录](#目录) + * [å°�æ¢�的邮件](#å°�æ¢�的邮件) + * [å°�王的Master选举](#å°�王的master选举) + * [å°�蔡的分布å¼�é”�](#å°�蔡的分布å¼�é”�) + * [Zookeeper](#zookeeper) + + +# 目录 + 本文转自:微信公众å�·ã€�ç �农翻身】 本文内容å�‚考网络,侵删 @@ -152,4 +161,4 @@ Zookeeper 所使用的树形结构和自己想象的é�žå¸¸ç±»ä¼¼ï¼Œæ›´é‡�è¦�çš„ _å�Žè®°ï¼šæœ¬æ–‡ä»Žä½¿ç”¨è€…的角度æ��述了Zookeeper有什么用处,至于它内部是如何工作,那是å�¦å¤–一个Big topic了,我们以å�Žå†�讲。___ -(完) \ No newline at end of file +(完) diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" index 6fed876..3f1f40d 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" @@ -1,5 +1,21 @@ # 目录 +* [目录](#目录) + * [什么是事务?](#什么是事务?) + * [事务的四大特性 ACID](#事务的四大特性-acid) + * [事务的隔离级别](#事务的隔离级别) + * [事务并å�‘执行会出现的问题](#事务并å�‘执行会出现的问题) + * [æ•°æ�®åº“的四ç§�隔离级别](#æ•°æ�®åº“的四ç§�隔离级别) + * [什么是分布å¼�事务?](#什么是分布å¼�事务?) + * [CAPç�†è®º](#capç�†è®º) + * [BASEç�†è®º](#baseç�†è®º) + * [分布å¼�事务å��è®®](#分布å¼�事务å��è®®) + * [2PC](#2pc) + * [3PC](#3pc) + + +# 目录 + 本文转载自 linkedkeeper.com 本文内容å�‚考网络,侵删 @@ -22,10 +38,10 @@ 众所周知,数æ�®åº“能实现本地事务,也就是在å�Œä¸€ä¸ªæ•°æ�®åº“中,你å�¯ä»¥å…�许一组æ“�作è¦�么全都正确执行,è¦�么全都ä¸�执行。这里特别强调了本地事务,也就是目å‰�的数æ�®åº“å�ªèƒ½æ”¯æŒ�å�Œä¸€ä¸ªæ•°æ�®åº“中的事务。但现在的系统往往采用微æœ�务架构,业务系统拥有独立的数æ�®åº“,因此就出现了跨多个数æ�®åº“的事务需求,这ç§�事务å�³ä¸ºâ€œåˆ†å¸ƒå¼�事务â€�。那么在目å‰�æ•°æ�®åº“ä¸�支æŒ�跨库事务的情况下,我们应该如何实现分布å¼�事务呢?本文首先会为大家梳ç�†åˆ†å¸ƒå¼�事务的基本概念和ç�†è®ºåŸºç¡€ï¼Œç„¶å�Žä»‹ç»�几ç§�ç›®å‰�常用的分布å¼�事务解决方案。废è¯�ä¸�多说,那就开始å�§ï½ž -什么是事务? +## 什么是事务? 事务由一组æ“�作构æˆ�,我们希望这组æ“�作能够全部正确执行,如果这一组æ“�作中的任æ„�一个步骤å�‘生错误,那么就需è¦�回滚之å‰�å·²ç»�完æˆ�çš„æ“�作。也就是å�Œä¸€ä¸ªäº‹åŠ¡ä¸­çš„æ‰€æœ‰æ“�作,è¦�么全都正确执行,è¦�么全都ä¸�è¦�执行。 -事务的四大特性 ACID +## 事务的四大特性 ACID 说到事务,就ä¸�å¾—ä¸�æ��一下事务著å��的四大特性。 原å­�性原å­�性è¦�求,事务是一个ä¸�å�¯åˆ†å‰²çš„æ‰§è¡Œå�•元,事务中的所有æ“�作è¦�么全都执行,è¦�么全都ä¸�执行。 @@ -38,12 +54,12 @@ 注æ„�:事务å�ªèƒ½ä¿�è¯�æ•°æ�®åº“的高å�¯é� æ€§ï¼Œå�³æ•°æ�®åº“本身å�‘生问题å�Žï¼Œäº‹åŠ¡æ��交å�Žçš„æ•°æ�®ä»�然能æ�¢å¤�;而如果ä¸�是数æ�®åº“本身的故障,如硬盘æ�Ÿå��了,那么事务æ��交的数æ�®å�¯èƒ½å°±ä¸¢å¤±äº†ã€‚这属于『高å�¯ç”¨æ€§ã€�的范畴。因此,事务å�ªèƒ½ä¿�è¯�æ•°æ�®åº“的『高å�¯é� æ€§ã€�,而『高å�¯ç”¨æ€§ã€�需è¦�整个系统共å�Œé…�å�ˆå®žçŽ°ã€‚ -事务的隔离级别 +## 事务的隔离级别 这里扩展一下,对事务的隔离性å�šä¸€ä¸ªè¯¦ç»†çš„解释。 在事务的四大特性ACID中,è¦�求的隔离性是一ç§�严格æ„�义上的隔离,也就是多个事务是串行执行的,彼此之间ä¸�会å�—到任何干扰。这确实能够完全ä¿�è¯�æ•°æ�®çš„安全性,但在实际业务系统中,这ç§�æ–¹å¼�性能ä¸�高。因此,数æ�®åº“定义了四ç§�隔离级别,隔离级别和数æ�®åº“的性能是呈å��比的,隔离级别越低,数æ�®åº“性能越高,而隔离级别越高,数æ�®åº“性能越差。 -事务并å�‘执行会出现的问题 +## 事务并å�‘执行会出现的问题 我们先æ�¥çœ‹ä¸€ä¸‹åœ¨ä¸�å�Œçš„隔离级别下,数æ�®åº“å�¯èƒ½ä¼šå‡ºçŽ°çš„é—®é¢˜ï¼š 更新丢失当有两个并å�‘执行的事务,更新å�Œä¸€è¡Œæ•°æ�®ï¼Œé‚£ä¹ˆæœ‰å�¯èƒ½ä¸€ä¸ªäº‹åŠ¡ä¼šæŠŠå�¦ä¸€ä¸ªäº‹åŠ¡çš„æ›´æ–°è¦†ç›–æŽ‰ã€‚å½“æ•°æ�®åº“没有加任何é”�æ“�作的情况下会å�‘生。 @@ -56,7 +72,7 @@ 幻读:事务1在两次查询的过程中,事务2对该表进行了æ�’å…¥ã€�删除æ“�作,从而事务1第二次查询的结果å�‘生了å�˜åŒ–。 ä¸�å�¯é‡�å¤�读 与 è„�读 的区别?è„�读读到的是尚未æ��交的数æ�®ï¼Œè€Œä¸�å�¯é‡�å¤�读读到的是已ç»�æ��交的数æ�®ï¼Œå�ªä¸�过在两次读的过程中数æ�®è¢«å�¦ä¸€ä¸ªäº‹åŠ¡æ”¹è¿‡äº†ã€‚ -æ•°æ�®åº“的四ç§�隔离级别 +## æ•°æ�®åº“的四ç§�隔离级别 æ•°æ�®åº“一共有如下四ç§�隔离级别: Read uncommitted 读未æ��交在该级别下,一个事务对一行数æ�®ä¿®æ”¹çš„过程中,ä¸�å…�许å�¦ä¸€ä¸ªäº‹åŠ¡å¯¹è¯¥è¡Œæ•°æ�®è¿›è¡Œä¿®æ”¹ï¼Œä½†å…�许å�¦ä¸€ä¸ªäº‹åŠ¡å¯¹è¯¥è¡Œæ•°æ�®è¯»ã€‚因此本级别下,ä¸�会出现更新丢失,但会出现è„�读ã€�ä¸�å�¯é‡�å¤�读。 @@ -69,7 +85,7 @@ Serializable åº�列化该级别è¦�求所有事务都必须串行执行,因此 隔离级别越高,越能ä¿�è¯�æ•°æ�®çš„完整性和一致性,但是对并å�‘性能的影å“�也越大。对于多数应用程åº�,å�¯ä»¥ä¼˜å…ˆè€ƒè™‘把数æ�®åº“系统的隔离级别设为Read Committed。它能够é�¿å…�è„�读å�–,而且具有较好的并å�‘性能。尽管它会导致ä¸�å�¯é‡�å¤�读ã€�幻读和第二类丢失更新这些并å�‘问题,在å�¯èƒ½å‡ºçŽ°è¿™ç±»é—®é¢˜çš„ä¸ªåˆ«åœºå�ˆï¼Œå�¯ä»¥ç”±åº”用程åº�采用悲观é”�或ä¹�è§‚é”�æ�¥æŽ§åˆ¶ã€‚ -什么是分布å¼�事务? +## 什么是分布å¼�事务? 到此为止,所介ç»�的事务都是基于å�•æ•°æ�®åº“的本地事务,目å‰�的数æ�®åº“仅支æŒ�å�•库事务,并ä¸�支æŒ�跨库事务。而éš�ç�€å¾®æœ�务架构的普å�Šï¼Œä¸€ä¸ªå¤§åž‹ä¸šåŠ¡ç³»ç»Ÿå¾€å¾€ç”±è‹¥å¹²ä¸ªå­�系统构æˆ�,这些å­�系统å�ˆæ‹¥æœ‰å�„自独立的数æ�®åº“。往往一个业务æµ�程需è¦�由多个å­�系统共å�Œå®Œæˆ�,而且这些æ“�作å�¯èƒ½éœ€è¦�在一个事务中完æˆ�。在微æœ�务系统中,这些业务场景是普é��存在的。此时,我们就需è¦�在数æ�®åº“之上通过æŸ�ç§�手段,实现支æŒ�跨数æ�®åº“的事务支æŒ�,这也就是大家常说的“分布å¼�事务â€�。 这里举一个分布å¼�事务的典型例å­�——用户下å�•过程。当我们的系统采用了微æœ�务架构å�Žï¼Œä¸€ä¸ªç”µå•†ç³»ç»Ÿå¾€å¾€è¢«æ‹†åˆ†æˆ�如下几个å­�系统:商å“�系统ã€�订å�•系统ã€�支付系统ã€�积分系统等。整个下å�•的过程如下: @@ -80,7 +96,7 @@ Serializable åº�列化该级别è¦�求所有事务都必须串行执行,因此 当支付完æˆ�å�Žï¼Œç”±ç§¯åˆ†ç³»ç»Ÿä¸ºè¯¥ç”¨æˆ·å¢žåŠ ç§¯åˆ† 上述步骤2ã€�3ã€�4需è¦�在一个事务中完æˆ�。对于传统å�•体应用而言,实现事务é�žå¸¸ç®€å�•,å�ªéœ€å°†è¿™ä¸‰ä¸ªæ­¥éª¤æ”¾åœ¨ä¸€ä¸ªæ–¹æ³•A中,å†�用Springçš„@Transactional注解标识该方法å�³å�¯ã€‚Spring通过数æ�®åº“的事务支æŒ�,ä¿�è¯�这些步骤è¦�么全都执行完æˆ�,è¦�么全都ä¸�执行。但在这个微æœ�务架构中,这三个步骤涉å�Šä¸‰ä¸ªç³»ç»Ÿï¼Œæ¶‰å�Šä¸‰ä¸ªæ•°æ�®åº“,此时我们必须在数æ�®åº“和应用系统之间,通过æŸ�项黑科技,实现分布å¼�事务的支æŒ�。 -CAPç�†è®º +## CAPç�†è®º CAPç�†è®ºè¯´çš„æ˜¯ï¼šåœ¨ä¸€ä¸ªåˆ†å¸ƒå¼�系统中,最多å�ªèƒ½æ»¡è¶³Cã€�Aã€�P中的两个需求。 CAPçš„å�«ä¹‰ï¼š @@ -100,7 +116,7 @@ CAPç�†è®ºå‘Šè¯‰æˆ‘们,在分布å¼�系统中,Cã€�Aã€�P三个æ�¡ä»¶ä¸­æˆ‘们 此外,å�¯ç”¨æ€§å¯¹ä¸šåŠ¡ç³»ç»Ÿä¹Ÿå°¤ä¸ºé‡�è¦�。在大谈用户体验的今天,如果业务系统时常出现“系统异常â€�ã€�å“�应时间过长等情况,这使得用户对系统的好感度大打折扣,在互è�”网行业竞争激烈的今天,相å�Œé¢†åŸŸçš„竞争者ä¸�甚枚举,系统的间歇性ä¸�å�¯ç”¨ä¼šç«‹é©¬å¯¼è‡´ç”¨æˆ·æµ�å�‘竞争对手。因此,我们å�ªèƒ½é€šè¿‡ç‰ºç‰²ä¸€è‡´æ€§æ�¥æ�¢å�–系统的å�¯ç”¨æ€§å’Œåˆ†åŒºå®¹é”™æ€§ã€‚这也就是下é�¢è¦�介ç»�çš„BASEç�†è®ºã€‚ -BASEç�†è®º +## BASEç�†è®º CAPç�†è®ºå‘Šè¯‰æˆ‘们一个悲惨但ä¸�å¾—ä¸�接å�—的事实——我们å�ªèƒ½åœ¨Cã€�Aã€�P中选择两个æ�¡ä»¶ã€‚而对于业务系统而言,我们往往选择牺牲一致性æ�¥æ�¢å�–系统的å�¯ç”¨æ€§å’Œåˆ†åŒºå®¹é”™æ€§ã€‚ä¸�过这里è¦�指出的是,所谓的“牺牲一致性â€�å¹¶ä¸�是完全放弃数æ�®ä¸€è‡´æ€§ï¼Œè€Œæ˜¯ç‰ºç‰²å¼ºä¸€è‡´æ€§æ�¢å�–弱一致性。下é�¢æ�¥ä»‹ç»�下BASEç�†è®ºã€‚ BA:Basic Available 基本å�¯ç”¨ @@ -112,7 +128,7 @@ E:Eventual Consisstency:最终一致性å�Œä¸€æ•°æ�®çš„ä¸�å�Œå‰¯æœ¬çš„状æ€� 酸碱平衡 ACID能够ä¿�è¯�事务的强一致性,å�³æ•°æ�®æ˜¯å®žæ—¶ä¸€è‡´çš„。这在本地事务中是没有问题的,在分布å¼�事务中,强一致性会æž�大影å“�分布å¼�系统的性能,因此分布å¼�系统中é�µå¾ªBASEç�†è®ºå�³å�¯ã€‚但分布å¼�系统的ä¸�å�Œä¸šåŠ¡åœºæ™¯å¯¹ä¸€è‡´æ€§çš„è¦�求也ä¸�å�Œã€‚如交易场景下,就è¦�求强一致性,此时就需è¦�é�µå¾ªACIDç�†è®ºï¼Œè€Œåœ¨æ³¨å†Œæˆ�功å�Žå�‘é€�短信验è¯�ç �等场景下,并ä¸�需è¦�实时一致,因此é�µå¾ªBASEç�†è®ºå�³å�¯ã€‚å› æ­¤è¦�æ ¹æ�®å…·ä½“业务场景,在ACIDå’ŒBASE之间寻求平衡。 -分布å¼�事务å��è®® +## 分布å¼�事务å��è®® 下é�¢ä»‹ç»�几ç§�实现分布å¼�事务的å��议。 @@ -120,7 +136,7 @@ ACID能够ä¿�è¯�事务的强一致性,å�³æ•°æ�®æ˜¯å®žæ—¶ä¸€è‡´çš„。这在本 为了解决分布å¼�一致性问题,å‰�人在性能和数æ�®ä¸€è‡´æ€§çš„å��å��å¤�å¤�æ�ƒè¡¡è¿‡ç¨‹ä¸­æ€»ç»“了许多典型的å��议和算法。其中比较著å��的有二阶æ��交å��议(2 Phase Commitment Protocol),三阶æ��交å��议(3 Phase Commitment Protocol)。 -2PC +### 2PC 分布å¼�事务最常用的解决方案就是二阶段æ��交。在分布å¼�系统中,æ¯�个节点虽然å�¯ä»¥çŸ¥æ™“自己的æ“�作时æˆ�功或者失败,å�´æ— æ³•知é�“其他节点的æ“�作的æˆ�功或失败。当一个事务跨越多个节点时,为了ä¿�æŒ�事务的ACID特性,需è¦�引入一个作为å��调者的组件æ�¥ç»Ÿä¸€æŽŒæŽ§æ‰€æœ‰å�‚与者节点的æ“�作结果并最终指示这些节点是å�¦è¦�把æ“�作结果进行真正的æ��交。 @@ -176,7 +192,7 @@ ACID能够ä¿�è¯�事务的强一致性,å�³æ•°æ�®æ˜¯å®žæ—¶ä¸€è‡´çš„。这在本 æ•°æ�®ä¸�一致性:两阶段æ��交å��议虽然为分布å¼�æ•°æ�®å¼ºä¸€è‡´æ€§æ‰€è®¾è®¡ï¼Œä½†ä»�然存在数æ�®ä¸�一致性的å�¯èƒ½ï¼Œæ¯”如在第二阶段中,å�‡è®¾å��调者å�‘出了事务commit的通知,但是因为网络问题该通知仅被一部分å�‚与者所收到并执行了commitæ“�作,其余的å�‚与者则因为没有收到通知一直处于阻塞状æ€�,这时候就产生了数æ�®çš„ä¸�一致性。 -3PC +### 3PC 针对两阶段æ��交存在的问题,三阶段æ��交å��议通过引入一个“预询盘â€�阶段,以å�Šè¶…æ—¶ç­–ç•¥æ�¥å‡�少整个集群的阻塞时间,æ��å�‡ç³»ç»Ÿæ€§èƒ½ã€‚三阶段æ��交的三个阶段分别为:can_commit,pre_commit,do_commit。 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" index 81bb8be..28fc2ff 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" @@ -1,5 +1,20 @@ # 目录 +* [目录](#目录) + * [**Kafka的基本介ç»�**](#kafka的基本介ç»�) + * [**Kafka的设计原ç�†åˆ†æž�**](#kafka的设计原ç�†åˆ†æž�) + * [**Kafkaæ•°æ�®ä¼ è¾“的事务特点**](#kafkaæ•°æ�®ä¼ è¾“的事务特点) + * [**Kafka消æ�¯å­˜å‚¨æ ¼å¼�**](#kafka消æ�¯å­˜å‚¨æ ¼å¼�) + * [**副本(replication)策略**](#副本(replication)策略) + * [**Kafka消æ�¯åˆ†ç»„,消æ�¯æ¶ˆè´¹åŽŸç�†**](#kafka消æ�¯åˆ†ç»„,消æ�¯æ¶ˆè´¹åŽŸç�†) + * [**Push vs. Pull**](#push-vs-pull) + * [**Kafak顺åº�写入与数æ�®è¯»å�–**](#kafak顺åº�写入与数æ�®è¯»å�–) + * [**消费者(读å�–æ•°æ�®ï¼‰**](#消费者(读å�–æ•°æ�®ï¼‰) + * [**Reference**](#reference) + + +# 目录 + 本文转载自 linkedkeeper.com 本文内容å�‚考网络,侵删 @@ -33,7 +48,7 @@ 6pushå’Œpull的区别,顺åº�写入和消æ�¯è¯»å�–,零拷è´�机制 -**Kafka的基本介ç»�** +## **Kafka的基本介ç»�** Kafka是最åˆ�ç”±Linkedinå…¬å�¸å¼€å�‘,是一个分布å¼�ã€�分区的ã€�多副本的ã€�多订阅者,基于zookeeperå��调的分布å¼�日志系统(也å�¯ä»¥å½“å�šMQ系统),常è§�å�¯ä»¥ç”¨äºŽweb/nginx日志ã€�访问日志,消æ�¯æœ�务等等,Linkedin于2010年贡献给了Apache基金会并æˆ�为顶级开æº�项目。 @@ -49,7 +64,7 @@ Kafka主è¦�设计目标如下: * å�Œæ—¶æ”¯æŒ�离线数æ�®å¤„ç�†å’Œå®žæ—¶æ•°æ�®å¤„ç�†ã€‚ -**Kafka的设计原ç�†åˆ†æž�** +## **Kafka的设计原ç�†åˆ†æž�** ![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/linkedkeeper0_3573ea93-7e93-49b4-9779-6765b4fb0878.jpg) @@ -72,7 +87,7 @@ Kafka专用术语: * Consumer Group:æ¯�个Consumer属于一个特定的Consumer Group。 -**Kafkaæ•°æ�®ä¼ è¾“的事务特点** +## **Kafkaæ•°æ�®ä¼ è¾“的事务特点** * at most once:最多一次,这个和JMS中"é�žæŒ�久化"消æ�¯ç±»ä¼¼ï¼Œå�‘é€�一次,无论æˆ�败,将ä¸�会é‡�å�‘。消费者fetch消æ�¯ï¼Œç„¶å�Žä¿�å­˜offset,然å�Žå¤„ç�†æ¶ˆæ�¯ï¼›å½“clientä¿�å­˜offset之å�Žï¼Œä½†æ˜¯åœ¨æ¶ˆæ�¯å¤„ç�†è¿‡ç¨‹ä¸­å‡ºçŽ°äº†å¼‚å¸¸ï¼Œå¯¼è‡´éƒ¨åˆ†æ¶ˆæ�¯æœªèƒ½ç»§ç»­å¤„ç�†ã€‚那么此å�Ž"未处ç�†"的消æ�¯å°†ä¸�能被fetch到,这就是"at most once"。 @@ -82,7 +97,7 @@ Kafka专用术语: 通常情况下"at-least-once"是我们首选。 -**Kafka消æ�¯å­˜å‚¨æ ¼å¼�** +## **Kafka消æ�¯å­˜å‚¨æ ¼å¼�** **Topic & Partition** @@ -135,7 +150,7 @@ segment中index与data file对应关系物ç�†ç»“构如下: -**副本(replication)策略** +## **副本(replication)策略** Kafka的高å�¯é� æ€§çš„ä¿�éšœæ�¥æº�于其å�¥å£®çš„副本(replication)策略。 @@ -195,7 +210,7 @@ Leader选举本质上是一个分布å¼�é”�,有两ç§�æ–¹å¼�实现基于ZooKeepe Majority Vote的选举策略和ZooKeeper中的Zab选举是类似的,实际上ZooKeeper内部本身就实现了少数æœ�从多数的选举策略。kafka中对于Partitionçš„leader副本的选举采用了第一ç§�方法:为Partition分é…�副本,指定一个ZNode临时节点,第一个æˆ�功创建节点的副本就是Leader节点,其他副本会在这个ZNode节点上注册Watcher监å�¬å™¨ï¼Œä¸€æ—¦Leader宕机,对应的临时节点就会被自动删除,这时注册在该节点上的所有Follower都会收到监å�¬å™¨äº‹ä»¶ï¼Œå®ƒä»¬éƒ½ä¼šå°�试创建该节点,å�ªæœ‰åˆ›å»ºæˆ�功的那个followeræ‰�会æˆ�为Leader(ZooKeeperä¿�è¯�对于一个节点å�ªæœ‰ä¸€ä¸ªå®¢æˆ·ç«¯èƒ½åˆ›å»ºæˆ�功),其他followerç»§ç»­é‡�新注册监å�¬äº‹ä»¶ã€‚ -**Kafka消æ�¯åˆ†ç»„,消æ�¯æ¶ˆè´¹åŽŸç�†** +## **Kafka消æ�¯åˆ†ç»„,消æ�¯æ¶ˆè´¹åŽŸç�†** å�Œä¸€Topic的一æ�¡æ¶ˆæ�¯å�ªèƒ½è¢«å�Œä¸€ä¸ªConsumer Group内的一个Consumer消费,但多个Consumer Groupå�¯å�Œæ—¶æ¶ˆè´¹è¿™ä¸€æ¶ˆæ�¯ã€‚ @@ -203,7 +218,7 @@ Majority Vote的选举策略和ZooKeeper中的Zab选举是类似的,实际上Z 这是Kafka用æ�¥å®žçŽ°ä¸€ä¸ªTopic消æ�¯çš„广播(å�‘给所有的Consumer)和å�•播(å�‘ç»™æŸ�一个Consumer)的手段。一个Topicå�¯ä»¥å¯¹åº”多个Consumer Group。如果需è¦�实现广播,å�ªè¦�æ¯�个Consumer有一个独立的Groupå°±å�¯ä»¥äº†ã€‚è¦�实现å�•æ’­å�ªè¦�所有的Consumer在å�Œä¸€ä¸ªGroup里。用Consumer Group还å�¯ä»¥å°†Consumer进行自由的分组而ä¸�需è¦�多次å�‘é€�消æ�¯åˆ°ä¸�å�Œçš„Topic。 -**Push vs. Pull** +## **Push vs. Pull** 作为一个消æ�¯ç³»ç»Ÿï¼ŒKafkaé�µå¾ªäº†ä¼ ç»Ÿçš„æ–¹å¼�,选择由Producerå�‘broker push消æ�¯å¹¶ç”±Consumer从broker pull消æ�¯ã€‚ @@ -211,7 +226,7 @@ push模å¼�很难适应消费速率ä¸�å�Œçš„æ¶ˆè´¹è€…,因为消æ�¯å�‘é€�速率 对于Kafka而言,pull模å¼�æ›´å�ˆé€‚。pull模å¼�å�¯ç®€åŒ–broker的设计,Consumerå�¯è‡ªä¸»æŽ§åˆ¶æ¶ˆè´¹æ¶ˆæ�¯çš„速率,å�Œæ—¶Consumerå�¯ä»¥è‡ªå·±æŽ§åˆ¶æ¶ˆè´¹æ–¹å¼�——å�³å�¯æ‰¹é‡�消费也å�¯é€�æ�¡æ¶ˆè´¹ï¼Œå�Œæ—¶è¿˜èƒ½é€‰æ‹©ä¸�å�Œçš„æ��交方å¼�从而实现ä¸�å�Œçš„传输语义。 -**Kafak顺åº�写入与数æ�®è¯»å�–** +## **Kafak顺åº�写入与数æ�®è¯»å�–** 生产者(producer)是负责å�‘Kafkaæ��交数æ�®çš„,Kafka会把收到的消æ�¯éƒ½å†™å…¥åˆ°ç¡¬ç›˜ä¸­ï¼Œå®ƒç»�对ä¸�会丢失数æ�®ã€‚为了优化写入速度Kafak采用了两个技术,顺åº�写入和MMFile。 @@ -235,7 +250,7 @@ push模å¼�很难适应消费速率ä¸�å�Œçš„æ¶ˆè´¹è€…,因为消æ�¯å�‘é€�速率 通过mmap,进程åƒ�读写硬盘一样读写内存(当然是虚拟机内存)。使用这ç§�æ–¹å¼�å�¯ä»¥èŽ·å�–很大的I/Oæ��å�‡ï¼Œçœ�去了用户空间到内核空间å¤�制的开销(调用文件的read会把数æ�®å…ˆæ”¾åˆ°å†…核空间的内存中,然å�Žå†�å¤�制到用户空间的内存中。) -**消费者(读å�–æ•°æ�®ï¼‰** +## **消费者(读å�–æ•°æ�®ï¼‰** 试想一下,一个Web Serverä¼ é€�一个é�™æ€�文件,如何优化?答案是zero copy。传统模å¼�下我们从硬盘读å�–一个文件是这样的。 @@ -263,7 +278,7 @@ Zero Copy中直接从内核空间(DMA的)到内核空间(Socket的), * [浅谈分布å¼�æœ�务å��调技术 Zookeeper](http://www.linkedkeeper.com/detail/blog.action?bid=1014) -**Reference** +## **Reference** http://www.cnblogs.com/liuming1992/p/6423007.html @@ -281,4 +296,4 @@ https://my.oschina.net/silence88/blog/856195 https://toutiao.io/posts/508935/app_preview -转载请并标注: “本文转载自 linkedkeeper.com â€� \ No newline at end of file +转载请并标注: “本文转载自 linkedkeeper.com â€� diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" index cce0b20..b9e4ea5 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" @@ -1,5 +1,17 @@ # 目录 +* [目录](#目录) + * [å‰�言](#å‰�言) + * [分布å¼�一致性问题](#分布å¼�一致性问题) + * [分布å¼�é”�需è¦�具备哪些æ�¡ä»¶](#分布å¼�é”�需è¦�具备哪些æ�¡ä»¶) + * [分布å¼�é”�实现方å¼�](#分布å¼�é”�实现方å¼�) + * [解决上述问题有两ç§�方案](#解决上述问题有两ç§�方案) + * [zookeeper分布å¼�é”�](#zookeeper分布å¼�é”�) + * [å°�结](#å°�结) + + +# 目录 + 本文内容å�‚考网络,侵删 本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 @@ -20,13 +32,13 @@ 本文转载自 linkedkeeper.com -å‰�言 +## å‰�言 éš�ç�€äº’è�”网技术的ä¸�æ–­å�‘展,数æ�®é‡�çš„ä¸�断增加,业务逻辑日趋å¤�æ�‚,在这ç§�背景下,传统的集中å¼�系统已ç»�无法满足我们的业务需求,分布å¼�系统被应用在更多的场景,而在分布å¼�系统中访问共享资æº�就需è¦�一ç§�互斥机制,æ�¥é˜²æ­¢å½¼æ­¤ä¹‹é—´çš„互相干扰,以ä¿�è¯�一致性,在这ç§�情况下,我们就需è¦�用到分布å¼�é”�。 -分布å¼�一致性问题 +## 分布å¼�一致性问题 首先我们先æ�¥çœ‹ä¸€ä¸ªå°�例å­�: @@ -36,7 +48,7 @@ -分布å¼�é”�需è¦�具备哪些æ�¡ä»¶ +## 分布å¼�é”�需è¦�具备哪些æ�¡ä»¶ 1. 获å�–é”�和释放é”�的性能è¦�好 @@ -50,7 +62,7 @@ -分布å¼�é”�实现方å¼� +## 分布å¼�é”�实现方å¼� 一ã€�æ•°æ�®åº“é”� @@ -80,9 +92,9 @@ 基于setnx(set if not exist)的特点,当缓存里keyä¸�存在时,æ‰�会去set,å�¦åˆ™ç›´æŽ¥è¿”回false。如果返回true则获å�–到é”�,å�¦åˆ™èŽ·å�–é”�失败,为了防止死é”�,我们å†�用expire命令对这个key设置一个超时时间æ�¥é�¿å…�。但是这里看似完美,实则有缺陷,当我们setnxæˆ�功å�Žï¼Œçº¿ç¨‹å�‘生异常中断,expire还没æ�¥çš„å�Šè®¾ç½®ï¼Œé‚£ä¹ˆå°±ä¼šäº§ç”Ÿæ­»é”�。 -解决上述问题有两ç§�方案 +## 解决上述问题有两ç§�方案 -第一ç§�是采用redis2.6.12版本以å�Žçš„set,它æ��供了一系列选项 +1. 第一ç§�是采用redis2.6.12版本以å�Žçš„set,它æ��供了一系列选项 EX seconds – 设置键key的过期时间,å�•ä½�æ—¶ç§’ @@ -130,7 +142,7 @@ redlock算法相对于å�•节点redisé”�å�¯é� æ€§è¦�更高,但是实现起æ�¥ -zookeeper分布å¼�é”� +## zookeeper分布å¼�é”� 关于zookeeper的分布å¼�é”�实现在之å‰�讲述zookeeper的时候已ç»�介ç»�了。这里ä¸�å†�赘述ã€� @@ -164,7 +176,7 @@ zookeeper是一个为分布å¼�应用æ��供一致性æœ�务的软件,它内部 -å°�结 +## å°�结 在分布å¼�系统中,共享资æº�互斥访问问题é�žå¸¸æ™®é��,而针对访问共享资æº�的互斥问题,常用的解决方案就是使用分布å¼�é”�,这里å�ªä»‹ç»�了几ç§�常用的分布å¼�é”�,分布å¼�é”�的实现方å¼�还有有很多ç§�,根æ�®ä¸šåŠ¡é€‰æ‹©å�ˆé€‚的分布å¼�é”�,下é�¢å¯¹ä¸Šè¿°å‡ ç§�é”�进行一下比较: diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" index c2dd051..b47f00d 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" @@ -1,5 +1,5 @@ -#目录 +# 目录 * [何时需è¦�消æ�¯é˜Ÿåˆ—](#何时需è¦�消æ�¯é˜Ÿåˆ—) * [解耦](#解耦) * [最终一致性](#最终一致性) @@ -404,4 +404,4 @@ Future future = request(server);//server立刻返回future return future # 总结 -本文从为何使用消æ�¯é˜Ÿåˆ—开始讲起,然å�Žä¸»è¦�介ç»�了如何从零开始设计一个消æ�¯é˜Ÿåˆ—,包括RPCã€�事务ã€�最终一致性ã€�广播ã€�消æ�¯ç¡®è®¤ç­‰å…³é”®é—®é¢˜ã€‚并对消æ�¯é˜Ÿåˆ—çš„pushã€�pull模型å�šäº†ç®€è¦�分æž�,最å�Žä»Žæ‰¹é‡�和异步角度,分æž�了消æ�¯é˜Ÿåˆ—性能优化的æ€�路。下篇会ç�€é‡�介ç»�一些高级è¯�题,如存储系统的设计ã€�æµ�控和错峰的设计ã€�公平调度等。希望通过这些,让大家对消æ�¯é˜Ÿåˆ—有个æ��纲挈领的整体认识,并给自主开å�‘消æ�¯é˜Ÿåˆ—æ��ä¾›æ€�路。å�¦å¤–,本文主è¦�是æº�自自己在开å�‘消æ�¯é˜Ÿåˆ—中的æ€�考和读æº�ç �时的体会,比较ä¸�"官方",也难å…�会存在一些æ¼�洞,欢迎大家多多交æµ�。 \ No newline at end of file +本文从为何使用消æ�¯é˜Ÿåˆ—开始讲起,然å�Žä¸»è¦�介ç»�了如何从零开始设计一个消æ�¯é˜Ÿåˆ—,包括RPCã€�事务ã€�最终一致性ã€�广播ã€�消æ�¯ç¡®è®¤ç­‰å…³é”®é—®é¢˜ã€‚并对消æ�¯é˜Ÿåˆ—çš„pushã€�pull模型å�šäº†ç®€è¦�分æž�,最å�Žä»Žæ‰¹é‡�和异步角度,分æž�了消æ�¯é˜Ÿåˆ—性能优化的æ€�路。下篇会ç�€é‡�介ç»�一些高级è¯�题,如存储系统的设计ã€�æµ�控和错峰的设计ã€�公平调度等。希望通过这些,让大家对消æ�¯é˜Ÿåˆ—有个æ��纲挈领的整体认识,并给自主开å�‘消æ�¯é˜Ÿåˆ—æ��ä¾›æ€�路。å�¦å¤–,本文主è¦�是æº�自自己在开å�‘消æ�¯é˜Ÿåˆ—中的æ€�考和读æº�ç �时的体会,比较ä¸�"官方",也难å…�会存在一些æ¼�洞,欢迎大家多多交æµ�。 diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" index 61ed18f..22c4550 100644 --- "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" +++ "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" @@ -1,3 +1,4 @@ + # 目录 * [缓存更新的套路](#缓存更新的套路) @@ -104,6 +105,6 @@ Write Back套路,一å�¥è¯´å°±æ˜¯ï¼Œåœ¨æ›´æ–°æ•°æ�®çš„æ—¶å€™ï¼Œå�ªæ›´æ–°ç¼“å­˜ 3)在软件开å�‘或设计中,我é�žå¸¸å»ºè®®åœ¨ä¹‹å‰�先去å�‚考一下已有的设计和æ€�路,**看看相应的guideline,best practice或design pattern,å�ƒé€�了已有的这些东西,å†�决定是å�¦è¦�é‡�æ–°å�‘明轮å­�**。å�ƒä¸‡ä¸�è¦�似是而é�žåœ°ï¼Œæƒ³å½“ç„¶çš„å�šè½¯ä»¶è®¾è®¡ã€‚ -4)上é�¢ï¼Œæˆ‘们没有考虑缓存(Cache)和æŒ�久层(Repository)的整体事务的问题。比如,更新Cacheæˆ�功,更新数æ�®åº“失败了怎么å�—?或是å��过æ�¥ã€‚关于这个事,如果你需è¦�强一致性,你需è¦�使用“两阶段æ��交å��è®®â€�——prepare, commit/rollback,比如Java 7 çš„[XAResource](http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html),还有MySQL 5.7的 [XA Transaction](http://dev.mysql.com/doc/refman/5.7/en/xa.html),有些cache也支æŒ�XA,比如[EhCache](http://www.ehcache.org/documentation/3.0/xa.html)。当然,XA这样的强一致性的玩法会导致性能下é™�,关于分布å¼�的事务的相关è¯�题,你å�¯ä»¥çœ‹çœ‹ã€Š[分布å¼�系统的事务处ç�†](https://coolshell.cn/articles/10910.html)》一文。 +4)上é�¢ï¼Œæˆ‘们没有考虑缓存(Cache)和æŒ�久层(Repository)的整体事务的问题。比如,更新Cacheæˆ�功,更新数æ�®åº“失败了怎么å�—?或是å��过æ�¥ã€‚关于这个事,如果你需è¦�强一致性,你需è¦�使用“两阶段æ��交å��è®®â€�——prepare, commit/rollback,比如Java 7 çš„[XAResource](http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html),还有MySQL 5.7çš„[XA Transaction](http://dev.mysql.com/doc/refman/5.7/en/xa.html),有些cache也支æŒ�XA,比如[EhCache](http://www.ehcache.org/documentation/3.0/xa.html)。当然,XA这样的强一致性的玩法会导致性能下é™�,关于分布å¼�的事务的相关è¯�题,你å�¯ä»¥çœ‹çœ‹ã€Š[分布å¼�系统的事务处ç�†](https://coolshell.cn/articles/10910.html)》一文。 -(全文完) \ No newline at end of file +(全文完) diff --git "a/docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" "b/docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" index d73b1e8..8fb1dc1 100644 --- "a/docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" +++ "b/docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" @@ -25,17 +25,6 @@ * [好了,最å�Žæ˜¯æ•°æ�®åº“层](#好了,最å�Žæ˜¯æ•°æ�®åº“层) * [总结](#总结) - - ---- -title: 分布å¼�技术实践总结 -date: 2018-07-08 22:15:36 -tags: -- 分布å¼�系统 - categories: -- å�Žç«¯ -- 技术总结 ---- 本文基于之å‰�的分布å¼�系统ç�†è®ºç³»åˆ—文章总结而æˆ�,本部分主è¦�是实践内容,详细内容å�¯è§�我的专æ �:分布å¼�系统ç�†è®ºä¸Žå®žè·µ https://blog.csdn.net/column/details/24090.html diff --git "a/docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" "b/docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" index ff2647f..9e341f3 100644 --- "a/docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" +++ "b/docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" @@ -1,3 +1,12 @@ +# 目录 + + * [å…­ã€�Raft](#å…­ã€�raft) + * [å�•个 Candidate 的竞选](#å�•个-candidate-的竞选) + * [多个 Candidate 竞选](#多个-candidate-竞选) + * [æ•°æ�®å�Œæ­¥](#æ•°æ�®å�Œæ­¥) + * [å�‚考](#å�‚考) + + ## å…­ã€�Raft Raft 也是分布å¼�一致性å��议,主è¦�是用æ�¥ç«žé€‰ä¸»èŠ‚ç‚¹ã€‚ @@ -63,4 +72,4 @@ Raft 也是分布å¼�一致性å��议,主è¦�是用æ�¥ç«žé€‰ä¸»èŠ‚ç‚¹ã€‚ - [深入ç�†è§£åˆ†å¸ƒå¼�事务](https://juejin.im/entry/577c6f220a2b5800573492be) - [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/) - [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) -- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) \ No newline at end of file +- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) diff --git "a/docs/hxx/java/Java\345\220\216\347\253\257\345\267\245\347\250\213\345\270\210\345\277\205\345\244\207\344\271\246\345\215\225\357\274\210\344\273\216Java\345\237\272\347\241\200\345\210\260\345\210\206\345\270\203\345\274\217\357\274\211.md" "b/docs/hxx/java/Java\345\220\216\347\253\257\345\267\245\347\250\213\345\270\210\345\277\205\345\244\207\344\271\246\345\215\225\357\274\210\344\273\216Java\345\237\272\347\241\200\345\210\260\345\210\206\345\270\203\345\274\217\357\274\211.md" index 7b4490d..65b42bb 100644 --- "a/docs/hxx/java/Java\345\220\216\347\253\257\345\267\245\347\250\213\345\270\210\345\277\205\345\244\207\344\271\246\345\215\225\357\274\210\344\273\216Java\345\237\272\347\241\200\345\210\260\345\210\206\345\270\203\345\274\217\357\274\211.md" +++ "b/docs/hxx/java/Java\345\220\216\347\253\257\345\267\245\347\250\213\345\270\210\345\277\205\345\244\207\344\271\246\345\215\225\357\274\210\344\273\216Java\345\237\272\347\241\200\345\210\260\345\210\206\345\270\203\345\274\217\357\274\211.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [网络](#网络) * [1 TCP/IPå�·ä¸€](#1-tcpipå�·ä¸€) diff --git "a/docs/hxx/java/Java\345\267\245\347\250\213\345\270\210\344\277\256\347\202\274\344\271\213\350\267\257\357\274\210\346\240\241\346\213\233\346\200\273\347\273\223\357\274\211.md" "b/docs/hxx/java/Java\345\267\245\347\250\213\345\270\210\344\277\256\347\202\274\344\271\213\350\267\257\357\274\210\346\240\241\346\213\233\346\200\273\347\273\223\357\274\211.md" index 1432c75..cd1bd87 100644 --- "a/docs/hxx/java/Java\345\267\245\347\250\213\345\270\210\344\277\256\347\202\274\344\271\213\350\267\257\357\274\210\346\240\241\346\213\233\346\200\273\347\273\223\357\274\211.md" +++ "b/docs/hxx/java/Java\345\267\245\347\250\213\345\270\210\344\277\256\347\202\274\344\271\213\350\267\257\357\274\210\346\240\241\346\213\233\346\200\273\347\273\223\357\274\211.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [å‰�言](#å‰�言) * [大学时期的迷茫与å�šå®š](#大学时期的迷茫与å�šå®š) @@ -173,7 +173,7 @@ 具体的é�¢ç»�都比较长,这里大概介ç»�一下é�¢è¯•的情况,然å�Žæˆ‘会放上é�¢ç»�的链接供大家查阅。 -**1 阿里é�¢ç»�**   +**1 阿里é�¢ç»�** [阿里中间件研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483852&idx=1&sn=9ec90620478b35d63a4a971a2222095d&chksm=f9e5b29dce923b8b2b080151e4b6b78373ee3f3e6f75b69252fc00b1a009d85c6f96944ed6e6&scene=21#wechat_redirect) @@ -189,7 +189,7 @@ 阿里的é�¢è¯•体验还是比较好的,至少ä¸�è¦�求手写算法,但是é�žå¸¸æ³¨é‡�Java基础,中间件部门还会特别安排Java基础笔试。 -**2 腾讯é�¢ç»�**   +**2 腾讯é�¢ç»�** [腾讯研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483849&idx=1&sn=f81fd42954589fb2deaf128026ddd856&chksm=f9e5b298ce923b8ef02ae36f7e9029fef0ddb7d5ae456dfa9d64c0073bebaacfb78fac4c8035&scene=21#wechat_redirect) @@ -201,7 +201,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 腾讯的é�¢è¯•一如既往地注é‡�考查网络和æ“�作系统,并且喜欢问Linux底层的一些知识,在这方é�¢æˆ‘还是有很多ä¸�足的。 -**3 百度é�¢ç»�**   +**3 百度é�¢ç»�** [百度研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483857&idx=1&sn=214b0f93db93407a7ac5a5149778cbad&chksm=f9e5b280ce923b96fcd535b2ef639fee2de78f12aa961d525b21760b11a3b95c0879113c2944&scene=21#wechat_redirect) @@ -213,7 +213,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 百度的é�¢è¯•风格é�žå¸¸ç»Ÿä¸€ï¼Œæ¯�次é�¢è¯•基本都è¦�到电脑上写算法,所以那段时间写算法写的头皮å�‘麻。 -**4 网易é�¢ç»�**   +**4 网易é�¢ç»�** [网易研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483875&idx=1&sn=fa9eaedd9cc3da172ad71d360c46a054&chksm=f9e5b2b2ce923ba443b91d56b24486d22b15bea16a4788e5ed3421906e84f8edd9ee10b2b306&scene=21#wechat_redirect) @@ -225,7 +225,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 网易的é�¢è¯•比我想象中的è¦�难,é�¢è¯•官会问的问题都比较深,并且会让你写一些结å�ˆå®žè·µçš„代ç �。 -**5 头æ�¡é�¢ç»�**   +**5 头æ�¡é�¢ç»�** [今日头æ�¡ç ”å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483869&idx=1&sn=eedb7aebcb90cf3d4fe2450ef2d99947&chksm=f9e5b28cce923b9a9b0852a1c998ec014eb1aeb0442916c9028da276be8db0b2934c92292961&scene=21#wechat_redirect) diff --git "a/docs/hxx/java/\344\270\272\344\273\200\344\271\210\346\210\221\344\274\232\351\200\211\346\213\251\350\265\260 Java \350\277\231\346\235\241\350\267\257\357\274\237.md" "b/docs/hxx/java/\344\270\272\344\273\200\344\271\210\346\210\221\344\274\232\351\200\211\346\213\251\350\265\260 Java \350\277\231\346\235\241\350\267\257\357\274\237.md" index c72b4dd..ed2fe79 100644 --- "a/docs/hxx/java/\344\270\272\344\273\200\344\271\210\346\210\221\344\274\232\351\200\211\346\213\251\350\265\260 Java \350\277\231\346\235\241\350\267\257\357\274\237.md" +++ "b/docs/hxx/java/\344\270\272\344\273\200\344\271\210\346\210\221\344\274\232\351\200\211\346\213\251\350\265\260 Java \350\277\231\346\235\241\350\267\257\357\274\237.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [谈谈我的技术方å�‘选择](#谈谈我的技术方å�‘选择) * [谈谈å�„个技术方å�‘çš„å‰�景](#谈谈å�„个技术方å�‘çš„å‰�景) diff --git "a/docs/hxx/java/\344\275\240\344\270\215\345\217\257\351\224\231\350\277\207\347\232\204Java\345\255\246\344\271\240\350\265\204\346\272\220\346\270\205\345\215\225.md" "b/docs/hxx/java/\344\275\240\344\270\215\345\217\257\351\224\231\350\277\207\347\232\204Java\345\255\246\344\271\240\350\265\204\346\272\220\346\270\205\345\215\225.md" index b4a6a53..2885cab 100644 --- "a/docs/hxx/java/\344\275\240\344\270\215\345\217\257\351\224\231\350\277\207\347\232\204Java\345\255\246\344\271\240\350\265\204\346\272\220\346\270\205\345\215\225.md" +++ "b/docs/hxx/java/\344\275\240\344\270\215\345\217\257\351\224\231\350\277\207\347\232\204Java\345\255\246\344\271\240\350\265\204\346\272\220\346\270\205\345\215\225.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Java入门学习资æº�](#java入门学习资æº�) * [Javaå�Žç«¯æŠ€æœ¯ä¸“æ �](#javaå�Žç«¯æŠ€æœ¯ä¸“æ �) @@ -38,25 +38,25 @@ https://blog.csdn.net/a724888 总的æ�¥è¯´ç®—是比较全é�¢äº†ï¼Œå�šå�Žç«¯æ–¹å�‘çš„å�Œå­¦å�¯ä»¥å�‚考一下。 -深入浅出Java核心技术   +深入浅出Java核心技术 https://blog.csdn.net/column/details/21930.html 本专æ �主è¦�介ç»�Java基础,并且会结å�ˆå®žçŽ°åŽŸç�†ä»¥å�Šå…·ä½“实例æ�¥è®²è§£ã€‚å�Œæ—¶è¿˜ä»‹ç»�了Java集å�ˆç±»ï¼Œè®¾è®¡æ¨¡å¼�以å�ŠJava8的相关知识。 -深入ç�†è§£JVM虚拟机   +深入ç�†è§£JVM虚拟机 https://blog.csdn.net/column/details/21960.html 带你走进JVM的世界,整å�ˆé«˜è´¨é‡�文章以é˜�述虚拟机的原ç�†å�Šç›¸å…³æŠ€æœ¯ï¼Œè®©å¼€å�‘者更好地了解Java的底层è¿�行原ç�†ä»¥å�Šç›¸åº”的调优方法。 -Javaå¹¶å�‘指å�—   +Javaå¹¶å�‘指å�— https://blog.csdn.net/column/details/21961.html 本专æ �主è¦�介ç»�Javaå¹¶å�‘编程相关的基本原ç�†ä»¥å�Šè¿›é˜¶çŸ¥è¯†ã€‚主è¦�包括Java多线程基础,Javaå¹¶å�‘编程基本原ç�†ä»¥å�ŠJUCå¹¶å�‘包的使用和æº�ç �è§£æž�。 -Java网络编程与NIO   +Java网络编程与NIO https://blog.csdn.net/column/details/21963.html @@ -64,7 +64,7 @@ Java网络编程一直是很é‡�è¦�的一部分内容,其中涉å�Šäº†socketçš„ 了解这部分知识对于ç�†è§£ç½‘络编程有很多帮助。å�¦å¤–还补充了两个涉å�ŠNIOçš„é‡�è¦�技术:Tomcatå’ŒNetty。 -JavaWeb技术世界   +JavaWeb技术世界 https://blog.csdn.net/column/details/21850.html @@ -72,7 +72,7 @@ https://blog.csdn.net/column/details/21850.html 我们ä¸�ä»…è¦�了解怎么使用它们,更è¦�去了解它们为什么出现,其中一些技术的实现原ç�†æ˜¯ä»€ä¹ˆã€‚ -Spring与SpringMVCæº�ç �è§£æž�   +Spring与SpringMVCæº�ç �è§£æž� https://blog.csdn.net/column/details/21851.html @@ -108,7 +108,7 @@ https://blog.csdn.net/column/details/25481.html ## Java工程师书å�• -我之å‰�专门写了一篇文章介ç»�了Java工程师的书å�•,å�¯ä»¥è¿™é‡Œé‡�点列举一些好书,推è��给大家。   +我之å‰�专门写了一篇文章介ç»�了Java工程师的书å�•,å�¯ä»¥è¿™é‡Œé‡�点列举一些好书,推è��给大家。 完整内容å�¯ä»¥å�‚考这篇文章: @@ -126,15 +126,15 @@ Java工程师必备书å�• 《Java核心技术å�·ä¸€ã€‹ 这本书还是比较适å�ˆå…¥é—¨çš„,当然,这ç§�厚皮书è¦�看完还是很有难度的,ä¸�过比起上é�¢é‚£æœ¬è¦�简å�•一些 -《深入ç�†è§£JVM虚拟机》 这本书是Javaå¼€å�‘者必须看的书,很多jvm的文章都是æ��å�–这本书的内容。JVM是Java虚拟机,赋予了Java程åº�生命,所以好好看看把,我自己就已ç»�看了三é��了。   +《深入ç�†è§£JVM虚拟机》 这本书是Javaå¼€å�‘者必须看的书,很多jvm的文章都是æ��å�–这本书的内容。JVM是Java虚拟机,赋予了Java程åº�生命,所以好好看看把,我自己就已ç»�看了三é��了。 《Javaå¹¶å�‘编程艺术》 这本书是国内作者写的Javaå¹¶å�‘书ç±�,比上é�¢é‚£ä¸€æœ¬æ›´ç®€å�•易懂,适å�ˆä½œä¸ºå¹¶å�‘编程的入门书ç±�,当然,学习并å�‘原ç�†ä¹‹å‰�,还是先把Java的多线程æ�žæ‡‚å�§ã€‚ -《深入JavaWeb技术内幕》 这本书是Java Web的集大æˆ�之作,涵盖了大部分Java Webå¼€å�‘的知识点,ä¸�过一本书显然无法把所有细节都讲完,但是作为Java Web的入门或者进阶书ç±�æ�¥çœ‹çš„è¯�还是很ä¸�错的。   +《深入JavaWeb技术内幕》 这本书是Java Web的集大æˆ�之作,涵盖了大部分Java Webå¼€å�‘的知识点,ä¸�过一本书显然无法把所有细节都讲完,但是作为Java Web的入门或者进阶书ç±�æ�¥çœ‹çš„è¯�还是很ä¸�错的。 《Redis设计与实现》 该书全é�¢è€Œå®Œæ•´åœ°è®²è§£äº† Redis 的内部è¿�行机制,对 Redis 的大多数å�•机功能以å�Šæ‰€æœ‰å¤šæœºåŠŸèƒ½çš„å®žçŽ°åŽŸç�†è¿›è¡Œäº†ä»‹ç»�。这本书把Redis的基本原ç�†è®²çš„一清二楚,包括数æ�®ç»“构,æŒ�久化,集群等内容,有空应该看看。 -《大型网站技术架构》 这本淘å®�系技术指å�—还是é�žå¸¸å€¼å¾—推崇的,å�¯ä»¥è¯´æ˜¯æŠŠå¤§åž‹ç½‘站的现代架构进行了一次简å�•的总结,内容涵盖了å�„æ–¹é�¢ï¼Œä¸»è¦�讲的是概念,很适å�ˆæ²¡æŽ¥è§¦è¿‡æž¶æž„çš„å�Œå­¦å…¥é—¨ã€‚看完以å�Žä½ ä¼šè§‰å¾—å�Žç«¯æŠ€æœ¯åŽŸæ�¥è¿™ä¹ˆå�šå¤§ç²¾æ·±ã€‚   +《大型网站技术架构》 这本淘å®�系技术指å�—还是é�žå¸¸å€¼å¾—推崇的,å�¯ä»¥è¯´æ˜¯æŠŠå¤§åž‹ç½‘站的现代架构进行了一次简å�•的总结,内容涵盖了å�„æ–¹é�¢ï¼Œä¸»è¦�讲的是概念,很适å�ˆæ²¡æŽ¥è§¦è¿‡æž¶æž„çš„å�Œå­¦å…¥é—¨ã€‚看完以å�Žä½ ä¼šè§‰å¾—å�Žç«¯æŠ€æœ¯åŽŸæ�¥è¿™ä¹ˆå�šå¤§ç²¾æ·±ã€‚ 《分布å¼�æœ�务框架原ç�†ä¸Žå®žè·µã€‹ 上é�¢é‚£æœ¬ä¹¦è®²çš„æ˜¯åˆ†å¸ƒå¼�架构的实践,而这本书更专注于分布å¼�æœ�务的原ç�†è®²è§£å’Œå¯¹åº”实践,很好地讲述了分布å¼�æœ�务的基本概念,相关技术,以å�Šè§£å†³æ–¹æ¡ˆç­‰ï¼Œå¯¹äºŽæƒ³è¦�学习分布å¼�æœ�务框架的å�Œå­¦æ�¥è¯´æ˜¯æœ¬å¥½ä¹¦ã€‚ @@ -176,9 +176,9 @@ https://www.zhihu.com/people/h2pl ## 技术大牛推è�� -1 江å�—白衣这ä½�大大ç»�对是我的Javaå�¯è’™å¯¼å¸ˆï¼Œä»–推è��çš„Javaå�Žç«¯ä¹¦æž¶è®©æˆ‘å�—益匪浅。   +1 江å�—白衣这ä½�大大ç»�对是我的Javaå�¯è’™å¯¼å¸ˆï¼Œä»–推è��çš„Javaå�Žç«¯ä¹¦æž¶è®©æˆ‘å�—益匪浅。 -2 ç �农翻身刘欣,一ä½�工作15å¹´çš„IBM架构师,用最浅显易懂的文章讲解技术的那些事,力è��,他的文章帮我解决了很多困惑。   +2 ç �农翻身刘欣,一ä½�工作15å¹´çš„IBM架构师,用最浅显易懂的文章讲解技术的那些事,力è��,他的文章帮我解决了很多困惑。 3 CoolShell陈皓è€�师的å�šå®¢ç›¸ä¿¡å¤§å®¶éƒ½çœ‹è¿‡ï¼Œå¹²è´§å¾ˆå¤šï¼Œé…·å£³åº”该算是国内最有影å“�力的个人å�šå®¢äº†ã€‚ diff --git "a/docs/hxx/java/\346\203\263\344\272\206\350\247\243Java\345\220\216\347\253\257\345\255\246\344\271\240\350\267\257\347\272\277\357\274\237\344\275\240\345\217\252\351\234\200\350\246\201\350\277\231\344\270\200\345\274\240\345\233\276\357\274\201.md" "b/docs/hxx/java/\346\203\263\344\272\206\350\247\243Java\345\220\216\347\253\257\345\255\246\344\271\240\350\267\257\347\272\277\357\274\237\344\275\240\345\217\252\351\234\200\350\246\201\350\277\231\344\270\200\345\274\240\345\233\276\357\274\201.md" index 202769b..3da5c3d 100644 --- "a/docs/hxx/java/\346\203\263\344\272\206\350\247\243Java\345\220\216\347\253\257\345\255\246\344\271\240\350\267\257\347\272\277\357\274\237\344\275\240\345\217\252\351\234\200\350\246\201\350\277\231\344\270\200\345\274\240\345\233\276\357\274\201.md" +++ "b/docs/hxx/java/\346\203\263\344\272\206\350\247\243Java\345\220\216\347\253\257\345\255\246\344\271\240\350\267\257\347\272\277\357\274\237\344\275\240\345\217\252\351\234\200\350\246\201\350\277\231\344\270\200\345\274\240\345\233\276\357\274\201.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [![](https://mmbiz.qpic.cn/mmbiz_jpg/XHh0SksQZPNiac1cDGFicIqDTr2PoAq2jmvD3JnkUOiaBzxgZDjh9pcicS0rCKcGaWvGVnm7ZH2LvlODKAtzANlDZg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1)](#[]httpsmmbizqpiccnmmbiz_jpgxhh0sksqzpniac1cdgficiqdtr2poaq2jmvd3jnkuoiabzxgzdjh9pcics0rckcgawvgvnm7zh2lvlodkatzanldzg640wx_fmtjpegtpwebpwxfrom5wx_lazy1wx_co1) * [å‰�言](#å‰�言) diff --git "a/docs/hxx/java/\346\210\221\347\232\204Java\347\247\213\346\213\233\351\235\242\347\273\217\345\244\247\345\220\210\351\233\206.md" "b/docs/hxx/java/\346\210\221\347\232\204Java\347\247\213\346\213\233\351\235\242\347\273\217\345\244\247\345\220\210\351\233\206.md" index 3b54d0f..ca3dce9 100644 --- "a/docs/hxx/java/\346\210\221\347\232\204Java\347\247\213\346\213\233\351\235\242\347\273\217\345\244\247\345\220\210\351\233\206.md" +++ "b/docs/hxx/java/\346\210\221\347\232\204Java\347\247\213\346\213\233\351\235\242\347\273\217\345\244\247\345\220\210\351\233\206.md" @@ -1,11 +1,11 @@ -# Table of Contents - -* [阿里é�¢ç»�  ](#阿里é�¢ç»�  ) -* [腾讯é�¢ç»�  ](#腾讯é�¢ç»�  ) -* [百度é�¢ç»�  ](#百度é�¢ç»�  ) -* [网易é�¢ç»�  ](#网易é�¢ç»�  ) -* [头æ�¡é�¢ç»�  ](#头æ�¡é�¢ç»�  ) -* [快手&拼多多é�¢ç»�  ](#快手拼多多é�¢ç»�  ) +# 目录 + +* [阿里é�¢ç»�](#阿里é�¢ç»�) +* [腾讯é�¢ç»�](#腾讯é�¢ç»�) +* [百度é�¢ç»�](#百度é�¢ç»�) +* [网易é�¢ç»�](#网易é�¢ç»�) +* [头æ�¡é�¢ç»�](#头æ�¡é�¢ç»�) +* [快手&拼多多é�¢ç»�](#快手拼多多é�¢ç»�) * [京东&美团é�¢ç»�](#京东美团é�¢ç»�) * [æ–—é±¼é�¢ç»�](#æ–—é±¼é�¢ç»�) * [有赞é�¢ç»�](#有赞é�¢ç»�) @@ -20,7 +20,7 @@ ![](https://mmbiz.qpic.cn/mmbiz_jpg/XHh0SksQZPPAdahlOcBvxxLrx0ibYleGQvVONWia2JjwyBDoUibkYrcm3viapMOCBPehbX6eOxWSHNAa8TYTqj2ibYQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) -# 阿里é�¢ç»�   +# 阿里é�¢ç»� [阿里中间件研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483852&idx=1&sn=9ec90620478b35d63a4a971a2222095d&chksm=f9e5b29dce923b8b2b080151e4b6b78373ee3f3e6f75b69252fc00b1a009d85c6f96944ed6e6&scene=21#wechat_redirect) @@ -36,7 +36,7 @@ 阿里的é�¢è¯•体验还是比较好的,至少ä¸�è¦�求手写算法,但是é�žå¸¸æ³¨é‡�Java基础,中间件部门还会特别安排Java基础笔试。 -# 腾讯é�¢ç»�   +# 腾讯é�¢ç»� [腾讯研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483849&idx=1&sn=f81fd42954589fb2deaf128026ddd856&chksm=f9e5b298ce923b8ef02ae36f7e9029fef0ddb7d5ae456dfa9d64c0073bebaacfb78fac4c8035&scene=21#wechat_redirect) @@ -48,7 +48,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 腾讯的é�¢è¯•一如既往地注é‡�考查网络和æ“�作系统,并且喜欢问Linux底层的一些知识,在这方é�¢æˆ‘还是有很多ä¸�足的。 -# 百度é�¢ç»�   +# 百度é�¢ç»� [百度研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483857&idx=1&sn=214b0f93db93407a7ac5a5149778cbad&chksm=f9e5b280ce923b96fcd535b2ef639fee2de78f12aa961d525b21760b11a3b95c0879113c2944&scene=21#wechat_redirect) @@ -60,7 +60,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 百度的é�¢è¯•风格é�žå¸¸ç»Ÿä¸€ï¼Œæ¯�次é�¢è¯•基本都è¦�到电脑上写算法,所以那段时间写算法写的头皮å�‘麻。 -# 网易é�¢ç»�   +# 网易é�¢ç»� [网易研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483875&idx=1&sn=fa9eaedd9cc3da172ad71d360c46a054&chksm=f9e5b2b2ce923ba443b91d56b24486d22b15bea16a4788e5ed3421906e84f8edd9ee10b2b306&scene=21#wechat_redirect) @@ -72,7 +72,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja 网易的é�¢è¯•比我想象中的è¦�难,é�¢è¯•官会问的问题都比较深,并且会让你写一些结å�ˆå®žè·µçš„代ç �。 -# 头æ�¡é�¢ç»�   +# 头æ�¡é�¢ç»� [今日头æ�¡ç ”å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483869&idx=1&sn=eedb7aebcb90cf3d4fe2450ef2d99947&chksm=f9e5b28cce923b9a9b0852a1c998ec014eb1aeb0442916c9028da276be8db0b2934c92292961&scene=21#wechat_redirect) @@ -82,7 +82,7 @@ SNG的部门æ�žäº†æˆ‘的简历,开始了é�¢è¯•,他们的技术栈主è¦�是Ja ç„¶å�Žå°±å¼€å§‹äº†ä¸€ä¸‹å�ˆçš„视频é�¢è¯•,一共三轮技术é�¢è¯•,æ¯�一轮都è¦�写代ç �,问问题的风格有点åƒ�腾讯,也喜欢问一些底层知识,让我有点懵逼。 -# 快手&拼多多é�¢ç»�   +# 快手&拼多多é�¢ç»� [拼多多&快手研å�‘é�¢ç»�](http://mp.weixin.qq.com/s?__biz=MzUyMDc5MTYxNA==&mid=2247483878&idx=1&sn=aaafff4b1171361ccd1d94708d2beaa0&chksm=f9e5b2b7ce923ba10140dff71dd3a0f9a20593434a32cec66929116bba196dc7496abf502884&scene=21#wechat_redirect) diff --git a/docs/hxx/think/copy.md b/docs/hxx/think/copy.md index c62954a..813a411 100644 --- a/docs/hxx/think/copy.md +++ b/docs/hxx/think/copy.md @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [微信公众å�·](#微信公众å�·) * [个人公众å�·ï¼šé»„å°�æ–œ](#个人公众å�·ï¼šé»„å°�æ–œ) diff --git "a/docs/hxx/\347\224\265\345\255\220\344\271\246.md" "b/docs/hxx/\347\224\265\345\255\220\344\271\246.md" index 5e77629..5908230 100644 --- "a/docs/hxx/\347\224\265\345\255\220\344\271\246.md" +++ "b/docs/hxx/\347\224\265\345\255\220\344\271\246.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [《è�œé¸Ÿç¨‹åº�员æˆ�长之路:从技术å°�白到阿里巴巴Java工程师》](#《è�œé¸Ÿç¨‹åº�员æˆ�长之路:从技术å°�白到阿里巴巴java工程师》) * [Java学习](#java学习) @@ -45,7 +45,7 @@ ### 新手上路 - [用大白è¯�告诉你 :Java å�Žç«¯åˆ°åº•是在å�šä»€ä¹ˆï¼Ÿ](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485885&idx=1&sn=0b3e02d09559ad0fe371ce915a77d7e0&chksm=fa59ce7acd2e476c501ca68b311de5abaf8021c5baa96fa24e0ca621acaaf4b1c64e2e16f3cb&scene=21#wechat_redirect) +[用大白è¯�告诉你 :Java å�Žç«¯åˆ°åº•是在å�šä»€ä¹ˆï¼Ÿ](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485885&idx=1&sn=0b3e02d09559ad0fe371ce915a77d7e0&chksm=fa59ce7acd2e476c501ca68b311de5abaf8021c5baa96fa24e0ca621acaaf4b1c64e2e16f3cb&scene=21#wechat_redirect) [Java工程师学习指å�—(入门篇)](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247484573&idx=2&sn=7baf8dfe36357264ab25010429644792&chksm=fa59c35acd2e4a4c24c5b55003136b6b8c5e778273714093b2df7a016d3bb0ed0c774573a7a4&scene=21#wechat_redirect) @@ -198,15 +198,15 @@ [马云退éš�å‰�,在年会上说了最é‡�è¦�的三件事](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247486268&idx=1&sn=dd13517634cd4f72f824278f93edebef&chksm=fa59ccfbcd2e45ed522c377ab3fa4554632f36c836e9b2e1490fb4748f4c0cdee9a4a99c91a6&scene=21#wechat_redirect) - [那些拼命加ç�­çš„程åº�员们,å�Žæ�¥éƒ½æ€Žä¹ˆæ ·äº†ï¼Ÿ](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485861&idx=1&sn=e90ac488593b4b7adb4ac46303e715be&chksm=fa59ce62cd2e477410c26233ed6518e34fdd772e27ea7538bddfac36a549a50cab29b27881f4&scene=21#wechat_redirect) +[那些拼命加ç�­çš„程åº�员们,å�Žæ�¥éƒ½æ€Žä¹ˆæ ·äº†ï¼Ÿ](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485861&idx=1&sn=e90ac488593b4b7adb4ac46303e715be&chksm=fa59ce62cd2e477410c26233ed6518e34fdd772e27ea7538bddfac36a549a50cab29b27881f4&scene=21#wechat_redirect) - [互è�”网浪潮之下,è�Šè�Š 90 å�Žæ‰€é�¢ä¸´çš„困境](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485843&idx=1&sn=492c9d3f7c9e32de9da7d833013cb9b9&chksm=fa59ce54cd2e4742939a5eb42a470bf282dbc79cb6f38ac9ec0112af5282091a9f2cd3615104&scene=21#wechat_redirect) +[互è�”网浪潮之下,è�Šè�Š 90 å�Žæ‰€é�¢ä¸´çš„困境](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485843&idx=1&sn=492c9d3f7c9e32de9da7d833013cb9b9&chksm=fa59ce54cd2e4742939a5eb42a470bf282dbc79cb6f38ac9ec0112af5282091a9f2cd3615104&scene=21#wechat_redirect) - [程åº�员的工资到底花到哪里去了?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485826&idx=1&sn=cc13da981115341666ae919dd2646cb4&chksm=fa59ce45cd2e475344776b1f7f52a6d281d96210e21229c5d2f300cfad585f712b636cf60427&scene=21#wechat_redirect) +[程åº�员的工资到底花到哪里去了?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485826&idx=1&sn=cc13da981115341666ae919dd2646cb4&chksm=fa59ce45cd2e475344776b1f7f52a6d281d96210e21229c5d2f300cfad585f712b636cf60427&scene=21#wechat_redirect) - [互è�”网公å�¸é‡Œéƒ½æœ‰å“ªäº›æ½œè§„则?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485819&idx=1&sn=816e20fa37a2abd99fa714c1c13e0402&chksm=fa59cebccd2e47aa8572071dadbb301008cd425cf0de4d71978af82b44bd3554faacfa8ad462&scene=21#wechat_redirect) +[互è�”网公å�¸é‡Œéƒ½æœ‰å“ªäº›æ½œè§„则?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485819&idx=1&sn=816e20fa37a2abd99fa714c1c13e0402&chksm=fa59cebccd2e47aa8572071dadbb301008cd425cf0de4d71978af82b44bd3554faacfa8ad462&scene=21#wechat_redirect) - [大厂程åº�员的一天是如何度过的?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485803&idx=1&sn=5814daaa71fb164d33d858311708f1c3&chksm=fa59ceaccd2e47baf573c73fcefbdc93e74becd8b9d4c533cf79404a3f90b2b6cdb46eee4e40&scene=21#wechat_redirect) +[大厂程åº�员的一天是如何度过的?](http://mp.weixin.qq.com/s?__biz=MzUyOTk5NDQwOA==&mid=2247485803&idx=1&sn=5814daaa71fb164d33d858311708f1c3&chksm=fa59ceaccd2e47baf573c73fcefbdc93e74becd8b9d4c533cf79404a3f90b2b6cdb46eee4e40&scene=21#wechat_redirect) ### è�Œåœºå¿ƒå¾—: diff --git a/docs/interview/BATJ-Experience/alipay-pinduoduo-toutiao.md b/docs/interview/BATJ-Experience/alipay-pinduoduo-toutiao.md index 1db1539..27df6fa 100644 --- a/docs/interview/BATJ-Experience/alipay-pinduoduo-toutiao.md +++ b/docs/interview/BATJ-Experience/alipay-pinduoduo-toutiao.md @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [2019å¹´èš‚èš�金æœ�ã€�头æ�¡ã€�拼多多的é�¢è¯•总结](#2019å¹´èš‚èš�金æœ�ã€�头æ�¡ã€�拼多多的é�¢è¯•总结) * [准备过程](#准备过程) diff --git "a/docs/interview/BATJ-Experience/\350\232\202\350\232\201\351\207\221\346\234\215\345\256\236\344\271\240\347\224\237\351\235\242\347\273\217\346\200\273\347\273\223(\345\267\262\346\213\277\345\217\243\345\244\264offer).md" "b/docs/interview/BATJ-Experience/\350\232\202\350\232\201\351\207\221\346\234\215\345\256\236\344\271\240\347\224\237\351\235\242\347\273\217\346\200\273\347\273\223(\345\267\262\346\213\277\345\217\243\345\244\264offer).md" index 95f8403..61fb537 100644 --- "a/docs/interview/BATJ-Experience/\350\232\202\350\232\201\351\207\221\346\234\215\345\256\236\344\271\240\347\224\237\351\235\242\347\273\217\346\200\273\347\273\223(\345\267\262\346\213\277\345\217\243\345\244\264offer).md" +++ "b/docs/interview/BATJ-Experience/\350\232\202\350\232\201\351\207\221\346\234\215\345\256\236\344\271\240\347\224\237\351\235\242\347\273\217\346\200\273\347\273\223(\345\267\262\346\213\277\345\217\243\345\244\264offer).md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [一é�¢ (37 分钟左å�³)](#一é�¢-37-分钟左å�³) * [二é�¢ (33 分钟左å�³)](#二é�¢-33-分钟左å�³) diff --git "a/docs/interview/BATJ-Experience/\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md" "b/docs/interview/BATJ-Experience/\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md" index 5a6e7e3..b06c915 100644 --- "a/docs/interview/BATJ-Experience/\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md" +++ "b/docs/interview/BATJ-Experience/\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [å‰�言](#å‰�言) * [一é�¢(技术é�¢)](#一é�¢æŠ€æœ¯é�¢) diff --git "a/docs/interview/InterviewQuestions/Java\346\240\270\345\277\203\346\212\200\346\234\257\346\200\273\347\273\223.md" "b/docs/interview/InterviewQuestions/Java\346\240\270\345\277\203\346\212\200\346\234\257\346\200\273\347\273\223.md" index d624749..bc89b6d 100644 --- "a/docs/interview/InterviewQuestions/Java\346\240\270\345\277\203\346\212\200\346\234\257\346\200\273\347\273\223.md" +++ "b/docs/interview/InterviewQuestions/Java\346\240\270\345\277\203\346\212\200\346\234\257\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Java基础学习总结](#java基础学习总结) * [é�¢å�‘对象三大特性](#é�¢å�‘对象三大特性) diff --git a/docs/interview/PreparingForInterview/JavaInterviewLibrary.md b/docs/interview/PreparingForInterview/JavaInterviewLibrary.md index e3d4e1d..2325618 100644 --- a/docs/interview/PreparingForInterview/JavaInterviewLibrary.md +++ b/docs/interview/PreparingForInterview/JavaInterviewLibrary.md @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [知识点相关](#知识点相关) * [1.JavaGuide](#1javaguide) diff --git a/docs/interview/PreparingForInterview/JavaProgrammerNeedKnow.md b/docs/interview/PreparingForInterview/JavaProgrammerNeedKnow.md index 417d1f9..3997584 100644 --- a/docs/interview/PreparingForInterview/JavaProgrammerNeedKnow.md +++ b/docs/interview/PreparingForInterview/JavaProgrammerNeedKnow.md @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Question1:我是å�Œé�ž/三本/专科学校的,我有机会进入大厂å�—?](#question1我是å�Œé�žä¸‰æœ¬ä¸“科学校的,我有机会进入大厂å�—?) * [Question2:é�žè®¡ç®—机专业的学生能学好Javaå�Žå�°å�—?我能进大厂å�—?](#question2é�žè®¡ç®—机专业的学生能学好javaå�Žå�°å�—?我能进大厂å�—?) diff --git a/docs/interview/PreparingForInterview/interviewPrepare.md b/docs/interview/PreparingForInterview/interviewPrepare.md index 89a3dc6..72201be 100644 --- a/docs/interview/PreparingForInterview/interviewPrepare.md +++ b/docs/interview/PreparingForInterview/interviewPrepare.md @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [1 如何获å�–大厂é�¢è¯•机会?](#1-如何获å�–大厂é�¢è¯•机会?) * [2 é�¢è¯•å‰�的准备](#2--é�¢è¯•å‰�的准备) diff --git "a/docs/interview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md" "b/docs/interview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md" index 0dc99b2..c1f742b 100644 --- "a/docs/interview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md" +++ "b/docs/interview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [这个问题对最终é�¢è¯•结果的影å“�到底大ä¸�大?](#这个问题对最终é�¢è¯•结果的影å“�到底大ä¸�大) * [真诚一点,ä¸�è¦�问太 Low 的问题](#真诚一点ä¸�è¦�问太-low-的问题) diff --git "a/docs/interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" "b/docs/interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" index 096ecaf..f391399 100644 --- "a/docs/interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" +++ "b/docs/interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [程åº�员简历就该这样写](#程åº�员简历就该这样写) * [为什么说简历很é‡�è¦�?](#为什么说简历很é‡�è¦�?) diff --git "a/docs/interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" "b/docs/interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" index 4e62535..8b51c2a 100644 --- "a/docs/interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" +++ "b/docs/interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [一 基础篇](#一-基础篇) * [1. `System.out.println(3|9)`输出什么?](#1-`systemoutprintln39`输出什么) diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\346\246\202\350\277\260.md" "b/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\346\246\202\350\277\260.md" deleted file mode 100644 index e4d080b..0000000 --- "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\220\357\274\232Spring\346\246\202\350\277\260.md" +++ /dev/null @@ -1,157 +0,0 @@ -# 目录 - * [Spring是什么](#spring是什么) - * [Spring能帮我们å�šä»€ä¹ˆ](#spring能帮我们å�šä»€ä¹ˆ) - * [为何需è¦�Spring](#为何需è¦�spring) - * [为什么需è¦�Springå�ŠSpring的优点](#为什么需è¦�springå�Šspring的优点) - * [如何学好Spring](#如何学好spring) - - -原文出处:[张开涛](http://sishuok.com/forum/blogPost/list/0/2508.html) - -本系列文章将整ç�†åˆ°æˆ‘在GitHub上的《Javaé�¢è¯•指å�—》仓库,更多精彩内容请到我的仓库里查看 -> https://github.com/h2pl/Java-Tutorial - -喜欢的è¯�麻烦点下Star哈 - -文章将å�Œæ­¥åˆ°æˆ‘的个人å�šå®¢ï¼š -> www.how2playlife.com - -本文是微信公众å�·ã€�Java技术江湖】的《Springå’ŒSpringMVCæº�ç �分æž�》其中一篇,本文部分内容æ�¥æº�于网络,为了把本文主题讲得清晰é€�彻,也整å�ˆäº†å¾ˆå¤šæˆ‘认为ä¸�错的技术å�šå®¢å†…容,引用其中了一些比较好的å�šå®¢æ–‡ç« ï¼Œå¦‚有侵æ�ƒï¼Œè¯·è�”系作者。 - -该系列å�šæ–‡ä¼šå‘Šè¯‰ä½ å¦‚ä½•ä»Žspring基础入手,一步步地学习spring基础和springmvc的框架知识,并上手进行项目实战,spring框架是æ¯�一个Java工程师必须è¦�学习和ç�†è§£çš„知识点,进一步æ�¥è¯´ï¼Œä½ è¿˜éœ€è¦�掌æ�¡spring甚至是springmvcçš„æº�ç �以å�Šå®žçŽ°åŽŸç�†ï¼Œæ‰�能更完整地了解整个spring技术体系,形æˆ�自己的知识框架。 - -å�Žç»­è¿˜ä¼šæœ‰springbootå’Œspringcloud的技术专题,陆续为大家带æ�¥ï¼Œæ•¬è¯·æœŸå¾…。 - -为了更好地总结和检验你的学习æˆ�果,本系列文章也会æ��供部分知识点对应的é�¢è¯•题以å�Šå�‚考答案。 - -如果对本系列文章有什么建议,或者是有什么疑问的è¯�,也å�¯ä»¥å…³æ³¨å…¬ä¼—å�·ã€�Java技术江湖】è�”系作者,欢迎你å�‚与本系列å�šæ–‡çš„åˆ›ä½œå’Œä¿®è®¢ã€‚ - - - - -在讲æº�ç �之å‰�,先让我们回顾一下一下Spring的基本概念,当然,在看æº�ç �之å‰�你需è¦�使用过spring或者spirngmvc。 - -## Spring是什么 -Spring是一个开æº�的轻é‡�级Java SE(Java 标准版本)/Java EE(Java ä¼�业版本)开å�‘应用框架,其目的是用于简化ä¼�业级应用程åº�å¼€å�‘。应用程åº�是由一组相互å��作的对象组æˆ�。而在传统应用程åº�å¼€å�‘中,一个完整的应用是由一组相互å��作的对象组æˆ�。所以开å�‘一个应用除了è¦�å¼€å�‘业务逻辑之外,最多的是关注如何使这些对象å��作æ�¥å®Œæˆ�所需功能,而且è¦�低耦å�ˆã€�高内è�šã€‚ - -业务逻辑开å�‘是ä¸�å�¯é�¿å…�的,那如果有个框架出æ�¥å¸®æˆ‘们æ�¥åˆ›å»ºå¯¹è±¡å�Šç®¡ç�†è¿™äº›å¯¹è±¡ä¹‹é—´çš„ä¾�赖关系。å�¯èƒ½æœ‰äººè¯´äº†ï¼Œæ¯”如“抽象工厂ã€�工厂方法设计模å¼�â€�ä¸�也å�¯ä»¥å¸®æˆ‘们创建对象,“生æˆ�器模å¼�â€�帮我们处ç�†å¯¹è±¡é—´çš„ä¾�赖关系,ä¸�也能完æˆ�这些功能å�—? - -å�¯æ˜¯è¿™äº›å�ˆéœ€è¦�我们创建å�¦ä¸€äº›å·¥åŽ‚ç±»ã€�生æˆ�器类,我们å�ˆè¦�而外管ç�†è¿™äº›ç±»ï¼Œå¢žåŠ äº†æˆ‘ä»¬çš„è´Ÿæ‹…ï¼Œå¦‚æžœèƒ½æœ‰ç§�通过é…�置方å¼�æ�¥åˆ›å»ºå¯¹è±¡ï¼Œç®¡ç�†å¯¹è±¡ä¹‹é—´ä¾�赖关系,我们ä¸�需è¦�通过工厂和生æˆ�器æ�¥åˆ›å»ºå�Šç®¡ç�†å¯¹è±¡ä¹‹é—´çš„ä¾�赖关系,这样我们是ä¸�是å‡�少了许多工作,加速了开å�‘,能节çœ�出很多时间æ�¥å¹²å…¶ä»–事。Spring框架刚出æ�¥æ—¶ä¸»è¦�就是æ�¥å®Œæˆ�这个功能。 - -Spring框架除了帮我们管ç�†å¯¹è±¡å�Šå…¶ä¾�赖关系,还æ��ä¾›åƒ�通用日志记录ã€�性能统计ã€�安全控制ã€�异常处ç�†ç­‰é�¢å�‘切é�¢çš„能力,还能帮我管ç�†æœ€å¤´ç–¼çš„æ•°æ�®åº“事务,本身æ��供了一套简å�•çš„JDBC访问实现,æ��供与第三方数æ�®è®¿é—®æ¡†æž¶é›†æˆ�(如Hibernateã€�JPA),与å�„ç§�Java EE技术整å�ˆï¼ˆå¦‚Java Mailã€�任务调度等等),æ��供一套自己的web层框架Spring MVCã€�而且还能é�žå¸¸ç®€å�•的与第三方web框架集æˆ�。 - -从这里我们å�¯ä»¥è®¤ä¸ºSpring是一个超级粘å�ˆå¹³å�°ï¼Œé™¤äº†è‡ªå·±æ��供功能外,还æ��供粘å�ˆå…¶ä»–技术和框架的能力,从而使我们å�¯ä»¥æ›´è‡ªç”±çš„选择到底使用什么技术进行开å�‘。而且ä¸�管是JAVA SE(C/S架构)应用程åº�还是JAVA EE(B/S架构)应用程åº�都å�¯ä»¥ä½¿ç”¨è¿™ä¸ªå¹³å�°è¿›è¡Œå¼€å�‘。让我们æ�¥æ·±å…¥çœ‹ä¸€ä¸‹Spring到底能帮我们å�šäº›ä»€ä¹ˆï¼Ÿ - -## Spring能帮我们å�šä»€ä¹ˆ -Spring除了ä¸�能帮我们写业务逻辑,其余的几乎什么都能帮助我们简化开å�‘: - - - -一ã€�传统程åº�å¼€å�‘,创建对象å�Šç»„装对象间ä¾�赖关系由我们在程åº�内部进行控制,这样会加大å�„个对象间的耦å�ˆï¼Œå¦‚果我们è¦�修改对象间的ä¾�赖关系就必须修改æº�代ç �,é‡�新编译ã€�部署;而如果采用Spring,则由Springæ ¹æ�®é…�置文件æ�¥è¿›è¡Œåˆ›å»ºå�Šç»„装对象间ä¾�赖关系,å�ªéœ€è¦�改é…�置文件å�³å�¯ï¼Œæ— éœ€é‡�新编译。所以,Spring能帮我们根æ�®é…�置文件创建å�Šç»„装对象之间的ä¾�赖关系。 - - - -二ã€�当我们è¦�进行一些日志记录ã€�æ�ƒé™�控制ã€�性能统计等时,在传统应用程åº�当中我们å�¯èƒ½åœ¨éœ€è¦�的对象或方法中进行,而且比如æ�ƒé™�控制ã€�性能统计大部分是é‡�å¤�的,这样代ç �中就存在大é‡�é‡�å¤�代ç �,å�³ä½¿æœ‰äººè¯´æˆ‘把通用部分æ��å�–出æ�¥ï¼Œé‚£å¿…然存在调用还是存在é‡�å¤�,åƒ�性能统计我们å�¯èƒ½å�ªæ˜¯åœ¨å¿…è¦�æ—¶æ‰�进行,在诊断完毕å�Žè¦�删除这些代ç �;还有日志记录,比如记录一些方法访问日志ã€�æ•°æ�®è®¿é—®æ—¥å¿—等等,这些都会渗é€�到å�„个è¦�访问方法中; - -还有æ�ƒé™�控制,必须在方法执行开始进行审核,想想这些是多么å�¯æ€•而且是多么无è�Šçš„工作。如果采用Spring,这些日志记录ã€�æ�ƒé™�控制ã€�性能统计从业务逻辑中分离出æ�¥ï¼Œé€šè¿‡Spring支æŒ�çš„é�¢å�‘切é�¢ç¼–程,在需è¦�这些功能的地方动æ€�添加这些功能,无需渗é€�到å�„个需è¦�的方法或对象中; - - -有人å�¯èƒ½è¯´äº†ï¼Œæˆ‘们å�¯ä»¥ä½¿ç”¨â€œä»£ç�†è®¾è®¡æ¨¡å¼�â€�或“包装器设计模å¼�â€�,你å�¯ä»¥ä½¿ç”¨è¿™äº›ï¼Œä½†è¿˜æ˜¯éœ€è¦�通过编程方å¼�æ�¥åˆ›å»ºä»£ç�†å¯¹è±¡ï¼Œè¿˜æ˜¯è¦�耦å�ˆè¿™äº›ä»£ç�†å¯¹è±¡ï¼Œè€Œé‡‡ç”¨Spring é�¢å�‘切é�¢ç¼–程能æ��供一ç§�更好的方å¼�æ�¥å®Œæˆ�上述功能,一般通过é…�置方å¼�,而且ä¸�需è¦�在现有代ç �中添加任何é¢�外代ç �,现有代ç �专注业务逻辑。 - -所以,Springé�¢å�‘切é�¢ç¼–程能帮助我们无耦å�ˆçš„实现日志记录,性能统计,安全控制。 - - - -三ã€�在传统应用程åº�当中,我们如何æ�¥å®Œæˆ�æ•°æ�®åº“事务管ç�†ï¼Ÿéœ€è¦�一系列“获å�–连接,执行SQL,æ��交或回滚事务,关闭连接â€�,而且还è¦�ä¿�è¯�在最å�Žä¸€å®šè¦�关闭连接,多么å�¯æ€•的事情,而且也很无è�Šï¼›å¦‚果采用Spring,我们å�ªéœ€èŽ·å�–连接,执行SQL,其他的都交给Springæ�¥ç®¡ç�†äº†ï¼Œç®€å�•å�§ã€‚所以,Spring能é�žå¸¸ç®€å�•的帮我们管ç�†æ•°æ�®åº“事务。 - - - -å››ã€�Spring还æ��供了与第三方数æ�®è®¿é—®æ¡†æž¶ï¼ˆå¦‚Hibernateã€�JPA)无ç¼�集æˆ�,而且自己也æ��供了一套JDBC访问模æ�¿ï¼Œæ�¥æ–¹ä¾¿æ•°æ�®åº“访问。 - - - -五ã€�Spring还æ��供与第三方Web(如Strutsã€�JSF)框架无ç¼�集æˆ�,而且自己也æ��供了一套Spring MVC框架,æ�¥æ–¹ä¾¿web层æ�­å»ºã€‚ - - - -å…­ã€�Spring能方便的与Java EE(如Java Mailã€�任务调度)整å�ˆï¼Œä¸Žæ›´å¤šæŠ€æœ¯æ•´å�ˆï¼ˆæ¯”如缓存框架)。 - - - -Spring能帮我们å�šè¿™ä¹ˆå¤šäº‹æƒ…,æ��供这么多功能和与那么多主æµ�技术整å�ˆï¼Œè€Œä¸”是帮我们å�šäº†å¼€å�‘中比较头疼和困难的事情,那å�¯èƒ½æœ‰äººä¼šé—®ï¼Œéš¾é�“å�ªæœ‰Spring这一个框架,没有其他选择?当然有,比如EJB需è¦�ä¾�赖应用æœ�务器ã€�å¼€å�‘效率低ã€�在开å�‘中å°�型项目是宰鸡拿牛刀,虽然å�‘展到现在EJB比较好用了,但还是比较笨é‡�还需è¦�ä¾�赖应用æœ�务器等。那为何需è¦�使用Spring,而ä¸�是其他框架呢?让我们接ç�€å¾€ä¸‹çœ‹ã€‚ - - -## 为何需è¦�Spring -一 首先é˜�述几个概念 - -1ã€�应用程åº�:是能完æˆ�我们所需è¦�功能的æˆ�å“�,比如购物网站ã€�OA系统。 - -2ã€�框架:是能完æˆ�一定功能的å�Šæˆ�å“�,比如我们å�¯ä»¥ä½¿ç”¨æ¡†æž¶è¿›è¡Œè´­ç‰©ç½‘站开å�‘;框架å�šä¸€éƒ¨åˆ†åŠŸèƒ½ï¼Œæˆ‘ä»¬è‡ªå·±å�šä¸€éƒ¨åˆ†åŠŸèƒ½ï¼Œè¿™æ ·åº”ç”¨ç¨‹åº�就创建出æ�¥äº†ã€‚而且框架规定了你在开å�‘应用程åº�时的整体架构,æ��供了一些基础功能,还规定了类和对象的如何创建ã€�如何å��作等,从而简化我们开å�‘,让我们专注于业务逻辑开å�‘。 - -3ã€�é�žä¾µå…¥å¼�设计:从框架角度å�¯ä»¥è¿™æ ·ç�†è§£ï¼Œæ— éœ€ç»§æ‰¿æ¡†æž¶æ��供的类,这ç§�设计就å�¯ä»¥çœ‹ä½œæ˜¯é�žä¾µå…¥å¼�设计,如果继承了这些框架类,就是侵入设计,如果以å�Žæƒ³æ›´æ�¢æ¡†æž¶ä¹‹å‰�写过的代ç �几乎无法é‡�用,如果é�žä¾µå…¥å¼�设计则之å‰�写过的代ç �ä»�ç„¶å�¯ä»¥ç»§ç»­ä½¿ç”¨ã€‚ - -4ã€�è½»é‡�级å�Šé‡�é‡�级:轻é‡�级是相对于é‡�é‡�级而言的,轻é‡�级一般就是é�žå…¥ä¾µæ€§çš„ã€�所ä¾�赖的东西é�žå¸¸å°‘ã€�资æº�å� ç”¨é�žå¸¸å°‘ã€�部署简å�•等等,其实就是比较容易使用,而é‡�é‡�级正好相å��。 - -5ã€�POJO:POJO(Plain Old Java Objects)简å�•çš„Java对象,它å�¯ä»¥åŒ…å�«ä¸šåŠ¡é€»è¾‘æˆ–æŒ�久化逻辑,但ä¸�担当任何特殊角色且ä¸�继承或ä¸�实现任何其它Java框架的类或接å�£ã€‚ - -6ã€�容器:在日常生活中容器就是一ç§�盛放东西的器具,从程åº�设计角度看就是装对象的的对象,因为存在放入ã€�拿出等æ“�作,所以容器还è¦�管ç�†å¯¹è±¡çš„生命周期。 - -7ã€�控制å��转:å�³Inversion of Control,缩写为IoC,控制å��转还有一个å��å­—å�«å�šä¾�赖注入(Dependency Injection),就是由容器控制程åº�之间的关系,而é�žä¼ ç»Ÿå®žçŽ°ä¸­ï¼Œç”±ç¨‹åº�代ç �直接æ“�控。 - -8ã€�Bean:一般指容器管ç�†å¯¹è±¡ï¼Œåœ¨Spring中指Spring IoC容器管ç�†å¯¹è±¡ã€‚ - - - -## 为什么需è¦�Springå�ŠSpring的优点 - -â—�é�žå¸¸è½»é‡�级的容器:以集中的ã€�自动化的方å¼�进行应用程åº�对象创建和装é…�,负责对象创建和装é…�,管ç�†å¯¹è±¡ç”Ÿå‘½å‘¨æœŸï¼Œèƒ½ç»„å�ˆæˆ�å¤�æ�‚的应用程åº�。Spring容器是é�žä¾µå…¥å¼�的(ä¸�需è¦�ä¾�赖任何Spring特定类),而且完全采用POJOs进行开å�‘,使应用程åº�更容易测试ã€�更容易管ç�†ã€‚而且核心JAR包é�žå¸¸å°�,Spring3.0.5ä¸�到1M,而且ä¸�需è¦�ä¾�赖任何应用æœ�务器,å�¯ä»¥éƒ¨ç½²åœ¨ä»»ä½•环境(Java SE或Java EE)。 - -â—�AOP:AOP是Aspect Oriented Programming的缩写,æ„�æ€�是é�¢å�‘切é�¢ç¼–程,æ��供从å�¦ä¸€ä¸ªè§’度æ�¥è€ƒè™‘程åº�结构以完善é�¢å�‘对象编程(相对于OOP),å�³å�¯ä»¥é€šè¿‡åœ¨ç¼–译期间ã€�装载期间或è¿�行期间实现在ä¸�修改æº�代ç �的情况下给程åº�动æ€�添加功能的一ç§�技术。通俗点说就是把å�¯é‡�用的功能æ��å�–出æ�¥ï¼Œç„¶å�Žå°†è¿™äº›é€šç”¨åŠŸèƒ½åœ¨å�ˆé€‚的时候织入到应用程åº�中;比如安全,日记记录,这些都是通用的功能,我们å�¯ä»¥æŠŠå®ƒä»¬æ��å�–出æ�¥ï¼Œç„¶å�Žåœ¨ç¨‹åº�执行的å�ˆé€‚地方织入这些代ç �并执行它们,从而完æˆ�需è¦�的功能并å¤�用了这些功能。 - -â—�简å�•的数æ�®åº“事务管ç�†ï¼šåœ¨ä½¿ç”¨æ•°æ�®åº“的应用程åº�当中,自己管ç�†æ•°æ�®åº“事务是一项很让人头疼的事,而且很容易出现错误,Spring支æŒ�å�¯æ�’入的事务管ç�†æ”¯æŒ�,而且无需JEE环境支æŒ�,通过Spring管ç�†äº‹åŠ¡å�¯ä»¥æŠŠæˆ‘们从事务管ç�†ä¸­è§£æ”¾å‡ºæ�¥æ�¥ä¸“注业务逻辑。 - -â—�JDBC抽象å�ŠORM框架支æŒ�:Spring使JDBC更加容易使用;æ��ä¾›DAO(数æ�®è®¿é—®å¯¹è±¡ï¼‰æ”¯æŒ�,é�žå¸¸æ–¹ä¾¿é›†æˆ�第三方ORM框架,比如Hibernate等;并且完全支æŒ�Spring事务和使用Springæ��供的一致的异常体系。 - -â—�ç�µæ´»çš„Web层支æŒ�:Spring本身æ��供一套é�žå¸¸å¼ºå¤§çš„MVC框架,而且å�¯ä»¥é�žå¸¸å®¹æ˜“的与第三方MVC框架集æˆ�,比如Struts等。 - -â—�简化å�„ç§�技术集æˆ�:æ��供对Java Mailã€�任务调度ã€�JMXã€�JMSã€�JNDIã€�EJBã€�动æ€�语言ã€�远程访问ã€�Web Service等的集æˆ�。 - -Spring能帮助我们简化应用程åº�å¼€å�‘,帮助我们创建和组装对象,为我们管ç�†äº‹åŠ¡ï¼Œç®€å�•çš„MVC框架,å�¯ä»¥æŠŠSpring看作是一个超级粘å�ˆå¹³å�°ï¼Œèƒ½æŠŠå¾ˆå¤šæŠ€æœ¯æ•´å�ˆåœ¨ä¸€èµ·ï¼Œå½¢æˆ�一个整体,使系统结构更优良ã€�性能更出众,从而加速我们程åº�å¼€å�‘,有如上优点,我们没有ç�†ç”±ä¸�考虑使用它。 - -## 如何学好Spring -è¦�学好Spring,首先è¦�明确Spring是个什么东西,能帮我们å�šäº›ä»€ä¹ˆäº‹æƒ…,知é�“了这些然å�Žå�šä¸ªç®€å�•的例å­�,这样就基本知é�“怎么使用Spring了。 - -Spring核心是IoC容器,所以一定è¦�é€�å½»ç�†è§£ä»€ä¹ˆæ˜¯IoC容器,以å�Šå¦‚何é…�ç½®å�Šä½¿ç”¨å®¹å™¨ï¼Œå…¶ä»–所有技术都是基于容器实现的; - -ç�†è§£å¥½IoCå�Žï¼ŒæŽ¥ä¸‹æ�¥æ˜¯é�¢å�‘切é�¢ç¼–程,首先还是明确概念,基本é…�置,最å�Žæ˜¯å®žçŽ°åŽŸç�†ï¼ŒæŽ¥ä¸‹æ�¥å°±æ˜¯æ•°æ�®åº“事务管ç�†ï¼Œå…¶å®žSpring管ç�†äº‹åŠ¡æ˜¯é€šè¿‡é�¢å�‘切é�¢ç¼–程实现的,所以基础很é‡�è¦�,IoC容器和é�¢å�‘切é�¢ç¼–程æ�žå®šå�Žï¼Œå…¶ä½™éƒ½æ˜¯åŸºäºŽè¿™ä¿©ä¸œè¥¿çš„实现,学起æ�¥å°±æ›´åŠ è½»æ�¾äº†ã€‚è¦�学好Springä¸�能急,一定è¦�把基础打牢,基础牢固了,这就是磨刀ä¸�误ç �柴工。 - - -## 微信公众å�· - -### 个人公众å�·ï¼šé»„å°�æ–œ - -黄å°�斜是跨考软件工程的 985 硕士,自学 Java 两年,拿到了 BAT 等近å��家大厂 offer,从技术å°�白æˆ�长为阿里工程师。 - -作者专注于 JAVA å�Žç«¯æŠ€æœ¯æ ˆï¼Œçƒ­è¡·äºŽåˆ†äº«ç¨‹åº�员干货ã€�学习ç»�验ã€�求è�Œå¿ƒå¾—和程åº�人生,目å‰�黄å°�斜的CSDNå�šå®¢æœ‰ç™¾ä¸‡+访问é‡�,知乎粉ä¸�2W+,全网已有10W+读者。 - -黄å°�斜是一个斜æ� é�’年,å�šæŒ�学习和写作,相信终身学习的力é‡�,希望和更多的程åº�员交朋å�‹ï¼Œä¸€èµ·è¿›æ­¥å’Œæˆ�é•¿ï¼� - -**原创电å­�书:** -关注公众å�·ã€�黄å°�斜】å�Žå›žå¤�ã€�原创电å­�书】å�³å�¯é¢†å�–我原创的电å­�书《è�œé¸Ÿç¨‹åº�员修炼手册:从技术å°�白到阿里巴巴Java工程师》 - -**程åº�员3T技术学习资æº�:** 一些程åº�员学习技术的资æº�大礼包,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“资料â€�** å�³å�¯å…�费无套路获å�–。 - -**考研å¤�习资料:** -计算机考研大礼包,都是我自己考研å¤�习时用的一些å¤�习资料,包括公共课和专业的å¤�习视频,这里也推è��给大家,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“考研â€�** å�³å�¯å…�费获å�–。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) - - -### 技术公众å�·ï¼šJava技术江湖 - -如果大家想è¦�实时关注我更新的文章以å�Šåˆ†äº«çš„干货的è¯�,å�¯ä»¥å…³æ³¨æˆ‘的公众å�·ã€�Java技术江湖】一ä½�阿里 Java 工程师的技术å°�站,作者黄å°�斜,专注 Java 相关技术:SSMã€�SpringBootã€�MySQLã€�分布å¼�ã€�中间件ã€�集群ã€�Linuxã€�网络ã€�多线程,å�¶å°”讲点Dockerã€�ELK,å�Œæ—¶ä¹Ÿåˆ†äº«æŠ€æœ¯å¹²è´§å’Œå­¦ä¹ ç»�验,致力于Java全栈开å�‘ï¼� - -**Java工程师必备学习资æº�:** 一些Java工程师常用学习资æº�,关注公众å�·å�Žï¼Œå�Žå�°å›žå¤�关键字 **“Javaâ€�** å�³å�¯å…�费无套路获å�–。 - -![我的公众å�·](https://img-blog.csdnimg.cn/20190805090108984.jpg) - diff --git a/docs/monitor/Spring Actuator.md b/docs/monitor/Spring Actuator.md new file mode 100644 index 0000000..a1ee5ef --- /dev/null +++ b/docs/monitor/Spring Actuator.md @@ -0,0 +1,578 @@ +## ǰÑÔ + +ÔÚ΢·þÎñϵͳ¼Ü¹¹ÖУ¬·þÎñµÄ¼à¿ØÊDZز»¿ÉÉٵġ£Ä¿Ç°´ó¶àÊý΢·þÎñÓ¦ÓÃÓÖÊÇ»ùÓÚSpring CloudϵÁУ¬Ò²¿ÉÒÔ˵ÊÇ»ùÓÚSpring BootϵÁеġ£´ËʱʹÓÃSpring Boot ActuatorÀ´½øÐÐ΢·þÎñµÄ¼à¿Ø£¬²»½ö¹¦ÄÜÈ«Ãæ£¬¶øÇҷdz£·½±ã¡£ + +ÔÚÉÏÆªÎÄÕ¡¶[Spring Boot Actuator¼¯³É£¬ÄѵÄÊÇÁé»îÔËÓã¡](https://link.juejin.cn?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2FBaNQWygQb8UXxktrXetOcw "https://mp.weixin.qq.com/s/BaNQWygQb8UXxktrXetOcw")¡·ÖÐÎÒÃÇÒѾ­½éÉÜÁËÈçºÎ½«Actuator¼¯³Éµ½Spring BootÏîÄ¿ÖУ¬²¢ÇÒ½éÉÜÁËÈçºÎ×Ô¶¨ÒåEndpoint£¨¶Ëµã£©¡£ÓÐÅóÓÑÁôÑÔ˵²»¹»ÉîÈ룬ÄÇô£¬±¾ÆªÎÄÕÂÄØ£¬ÎÒÃǽ«½éÉÜActuatorÔ­Éú¶ËµãµÄ¹¦Äܼ°»ù±¾Ê¹Óó¡¾°¡£ + +## Endpoints ½éÉÜ + +ActuatorÖÐËùνµÄ Endpoints £¨·­ÒëΪ¶Ëµã£©ÌṩÁËÍⲿÀ´ÓëÓ¦ÓóÌÐò½øÐзÃÎʺͽ»»¥µÄ¹¦ÄÜ¡£ ±ÈÈç˵/health¶ËµãÌṩÁËÓ¦Óý¡¿µÇé¿öµÄÐÅÏ¢£¬metrics ¶ËµãÌṩÁËÓ¦ÓóÌÐòµÄÖ¸±ê£¨JVM ÄÚ´æÊ¹Óá¢ÏµÍ³CPUʹÓõȣ©ÐÅÏ¢¡£ + +ActuatorÔ­ÉúµÄ¶Ëµã¿É·ÖΪÈý´óÀࣺ + +* Ó¦ÓÃÅäÖÃÀࣺ»ñȡӦÓóÌÐòÖмÓÔØµÄÓ¦ÓÃÅäÖᢻ·¾³±äÁ¿¡¢×Ô¶¯»¯ÅäÖñ¨¸æµÈÓëSpring BootÓ¦ÓÃÃÜÇÐÏà¹ØµÄÅäÖÃÀàÐÅÏ¢¡£ +* ¶ÈÁ¿Ö¸±êÀࣺ»ñȡӦÓóÌÐòÔËÐйý³ÌÖÐÓÃÓÚ¼à¿ØµÄ¶ÈÁ¿Ö¸±ê£¬±ÈÈ磺ÄÚ´æÐÅÏ¢¡¢Ï̳߳ØÐÅÏ¢¡¢HTTPÇëÇóͳ¼ÆµÈ¡£ +* ²Ù×÷¿ØÖÆÀࣺÌṩÁ˶ÔÓ¦ÓõĹرյȲÙ×÷À๦ÄÜ¡£ + +²»Í¬°æ±¾µÄActuatorÌṩµÄÔ­Éú¶ËµãÓÐËù³öÈ룬ÔÚʹÓõĹý³ÌÖÐ×îºÃÒÔËùʹÓð汾µÄ¹Ù·½ÎĵµÎª×¼¡£Í¬Ê±£¬Ã¿¸öÔ­ÉúµÄ¶Ëµã¶¼¿ÉÒÔͨ¹ýÅäÖÃÀ´µ¥¶ÀµÄ½ûÓûòÆôÓᣠ+ +¶øÔÚActuator 2.x ÖÐĬÈ϶˵ãÔö¼ÓÁË/actuatorǰ׺£¬Í¬Ê±Ä¬ÈÏÖ»±©Â¶µÄÁ½¸ö¶ËµãΪ/actuator/healthºÍ /actuator/info¡£¹ØÓڶ˵㱩¶µÄÅäÖ㬿ɲο¼Ç°Ò»ÆªÎÄÕ¡£ÏÂÃæ»ùÓÚSpring Boot 2.2.2.RELEASE°æ±¾À´Öص㽲½âÿ¸ö¶ËµãµÄ¹¦ÄܺÍÓ¦Óó¡¾°¡£ + +## actuator¶Ëµã + +Actuator 2.xÐÂÔöµÄĬÈ϶˵㣬ÓÃÓÚչʾĿǰӦÓÃÖб©Â¶³öÀ´µÄ¶Ëµã»ã×Ü£¬Äã¿ÉÒÔÀí½âΪ¿ÉÓö˵ãµÄĿ¼¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator "http://localhost:8080/actuator") £¬¶ÔӦչʾ½á¹ûÈçÏÂͼ£º + +![image-20230530233537559](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233537559.png) + +ÉÏÊöֻչʾÁËÒ»²¿·ÖµÄ¶Ëµã£¬·µ»Ø½á¹ûΪ£¬ÕâÀï²ÉÓÃÁËä¯ÀÀÆ÷µÄ²å¼þ-Handler½øÐÐÁ˸ñʽÃÀ»¯¡£Í¨¹ýactuator¿ÉÒÔÖ±¹ÛµÄ¿´³öĿǰ¿ª·ÅÁËÄÄЩ¶Ëµã£¬ÒÔ¼°ÕâЩ¶ËµãµÄÃû³ÆºÍÇëÇó·¾¶¡£ + +ÏÂÃæÎÒÃǾͰ´ÕÕÏÔʾactuator¶ËµãչʾµÄÁбíÖðÒ»½éÉÜ¡£ + +## auditevents¶Ëµã + +auditevents¶ËµãÓÃÓÚÏÔʾӦÓñ©Â¶µÄÉó¼ÆÊ¼þ (±ÈÈçÈÏÖ¤½øÈë¡¢¶©µ¥Ê§°Ü)£¬¼´Ê¹ÎÒÃÇ´ò¿ªÁËËùÓж˵㣬ĬÈÏÇé¿öÏÂÒ²ÊÇ¿´²»µ½Õâ¸ö¶ËµãµÄ¡£ÒòΪʹÓÃËüµÄǰÌáÊÇÐèÒªÔÚSpringÈÝÆ÷ÖдæÔÚÒ»¸öÀàÐÍΪAuditEventRepositoryµÄBeanµÄ¡£ + +²é¿´ÁËÍøÂçÉÏ´ó¶àÊý½Ì³Ì£¬»ù±¾É϶¼ÊǽéÉÜÁËauditevents¶Ëµã¹¦ÄÜ£¬¶øÎ´Õ¹Ê¾¾ßÌåʵÀý¡£±ÊÕß¾­¹ý¶à·½³¢ÊÔ£¬ÖÕÓÚ¸ø´ó¼ÒдÁËÒ»¸ö°¸Àý³öÀ´¡£ + +Ê×ÏÈÉæ¼°µ½È¨ÏÞÈÏÖ¤£¬ÐèÒªÏÈÒýÈëspring-boot-starter-securityÒÀÀµ£º + +```` + + org.springframework.boot + spring-boot-starter-security +` +```` + +µ¥´¿Ìí¼ÓÕâ¸öÒÀÀµ»¹ÊDz»¹»µÄ£¬»¹ÐèÒª¼ÓÈësecurityµÄÅäÖ㬲»È»AuthorizationAuditListener,AuthenticationAuditListener ¼àÌýʲôʼþÄØ? Òò´Ë,ÎÒÃǼÓÈëÈçÏ´úÂ룺 + + +```` +@Configuration +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + + auth.inMemoryAuthentication() + .withUser("admin") + .password(bcryptPasswordEncoder().encode("admin")) + .roles("admin"); + } +```` + +```` + @Bean + public PasswordEncoder bcryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} +```` + +ÕâÀï²ÉÓÃÁËsecurityĬÈϵĵǼ½çÃæºÍȨÏÞ¿ØÖÆ£¬Ò²¾ÍÊÇ˵ËùÓеķÃÎʶ¼ÐèÒª½øÐеǼ¡£¶øµÇ¼µÄÓû§ÃûºÍÃÜÂë¾ùΪadmin¡£ + +ÁíÍâ£¬Ç°ÃæÌáµ½ÐèÒªÓõ½AuditEventRepositoryµÄBean£¬ÕâÀï³õʼ»¯Ò»¸ö¶ÔÓ¦µÄBean£º + + + + +```` +@Configuration +public class AuditEventConfig { + + @Bean + public InMemoryAuditEventRepository repository(){ + return new InMemoryAuditEventRepository(); + } +} +```` + +InMemoryAuditEventRepositoryÊÇAuditEventRepository½Ó¿ÚµÄΨһʵÏÖÀà¡£ + +ÖØÆôÏîÄ¿£¬auditevents¶Ëµã±ã¿ÉÓÃÁË¡£·ÃÎÊ[http://localhost:8080/actuator](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator "http://localhost:8080/actuator") ,´Ëʱ»áÌø×ªµ½SecurityÌṩµÄµÇÂ¼Ò³Ãæ£º + +![image-20230530233604253](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233604253.png) + +ÊäÈë´úÂëÖÐÖ¸¶¨µÄÓû§ÃûºÍÃÜÂ룬µÇ¼³É¹¦£¬Ìø×ªµ½/actuatorÒ³Ãæ£º + +![image-20230530233625068](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233625068.png) + +¿ÉÒÔ¿´µ½auditevents¶ËµãÒѾ­³É¹¦ÏÔʾ³öÀ´ÁË¡£Ð¿ªÒ³Ãæ·ÃÎÊ[http://localhost:8080/actuator/auditevents](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fauditevents "http://localhost:8080/actuator/auditevents") £¬Õ¹Ê¾ÄÚÈÝÈçÏ£º + +![image-20230530233716752](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233716752.png) + +¿ÉÒÔ¿´µ½ÒѾ­¼Ç¼ÏÂÁËȨÏÞÏà¹ØµÄʼþ£¬ÆäÖеÚÒ»´ÎʼþÊÇÎÒÃÇÖ±½Ó·ÃÎÊactuator¶Ëµãʱ£¬ÓÉÓÚ֮ǰΪ×öȨÏÞÈÏÕæ£¬ËùÒÔʼþÀàÐÍΪ"AUTHORIZATION_FAILURE"£¬Ò²¾ÍÊÇÈÏ֤ʧ°Ü¡£´ËÊ±Ìø×ªµ½µÇÂ¼Ò³Ãæ£¬È»ºóÔÚµÇÂ¼Ò³ÃæÊäÈëÓû§ÃûºÍÃÜÂ룬µÇ¼³É¹¦£¬¶ÔÓ¦µÄʼþΪ"AUTHENTICATION_SUCCESS"¡£ + +Ò²¾ÍÊÇ˵auditevents¼Ç¼ÁËÓû§ÈÏÖ¤µÇ¼ϵͳÏà¹ØµÄʼþÐÅÏ¢£¬°üÀ¨Ê±¼ä´Á¡¢ÈÏÖ¤Óû§¡¢Ê¼þÀàÐÍ¡¢·ÃÎʵØÖ·¡¢sessionIdµÈ¡£ + +ʾÀýÔ´ÂëµØÖ·£º[github.com/secbr/sprin¡­](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fsecbr%2Fspringboot-all%2Ftree%2Fmaster%2Fspringboot-actuator-auditevents "https://github.com/secbr/springboot-all/tree/master/springboot-actuator-auditevents") ¡£ + +## beans¶Ëµã + +/beans¶Ëµã»á·µ»ØSpringÈÝÆ÷ÖÐËùÓÐbeanµÄ±ðÃû¡¢ÀàÐÍ¡¢ÊÇ·ñµ¥Àý¡¢ÒÀÀµµÈÐÅÏ¢¡£ + +·ÃÎÊ·¾¶Îª[http://localhost:8080/actuator/beans](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fbeans "http://localhost:8080/actuator/beans") £¬·¶Î§½á¹ûÈçÏ£º + +![image-20230530233748286](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233748286.png) + +Õâ¸ö¶Ëµã»áչʾĿǰSpringÈÝÆ÷Öгõʼ»¯µÄËùÓÐBean£¬ÊÔÏëһϣ¬Èç¹ûÄãÅäÖÃÁËÒ»¸öBean£¬µ«²»È·¶¨ÊÇ·ñ³É¹¦ÊµÀý»¯£¬ÊDz»ÊǾͿÉÒÔͨ¹ýÕâ¸ö¶Ë¿Ú²éѯһÏÂÄØ£¿ + +ÎÒÃÇÔÚÏîÄ¿Öж¨ÒåÒ»¸öTestController£¬²¢×¢ÈëÒ»¸öUserService£º + + +```` +@Controller +public class TestController { + + @Resource + private UserService userService; +} +```` + +ÖØÐÂÆô¶¯²¢·ÃÎʸö˵㣬»á¿´µ½ÈçÏÂÐÅÏ¢£º + +![image-20230530233805161](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233805161.png) + +¿ÉÒÔ¿´µ½TestController±»ÊµÀý»¯ÁË£¬¶øÇÒÒÀÀµÓÚUserService¡£ + +## caches¶Ëµã + +caches¶ËµãÖ÷ÒªÓÃÓÚ±©Â¶Ó¦ÓóÌÐòÖеĻº³å¡£ÕâÀïÒÔSpring BootÌṩµÄCache×é¼þÀ´Õ¹Ê¾Ò»ÏÂʵÀý¡£ + +ÔÚÏîÄ¿Öм¯³Éspring-boot-starter-cache£¬ÒýÈëÒÀÀµ£º + + +```` + + org.springframework.boot + spring-boot-starter-cache + +```` + +È»ºóÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableCaching£¬¿ªÆô»º´æ¹¦ÄÜ¡£ + +¶¨ÒåÒ»¸öCacheController£¬¶ÔÆä·½·¨queryAllʹÓûº´æ»úÖÆ£º + + +```` +@RestController +public class CacheController { + + @RequestMapping("/queryAll") + @Cacheable(value = "queryAll") + public Map queryAll() { + Map map = new HashMap<>(); + map.put("1", "Tom"); + map.put("2", "Steven"); + return map; + } +} +```` + +ÕâÀïʹÓÃ@Cacheable×¢½âÀ´ÊµÏÖ»º´æ¹¦ÄÜ£¬»º´æµÄkeyΪqueryAll¡£´Ëʱ£¬·ÃÎÊ[http://localhost:8080/actuator/caches](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fcaches "http://localhost:8080/actuator/caches") £¬»áչʾ»º´æµÄ¸ùÄÚÈÝ£¬µ«ÀïÃæ²¢Ã»Óлº´æ¡£ + +·ÃÎÊÒ»ÏÂ[http://localhost:8080/queryAll](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2FqueryAll "http://localhost:8080/queryAll") £¬Ò²¾ÍÊÇ´¥·¢Ò»Ï»º´æÄÚÈݵÄÉú³É¡£´ËʱÔÙ·ÃÎÊÉÏÃæµÄÁ´½Ó£¬±ã¿ÉÒÔ¿´µ½Ó¦ÓóÌÐòÖеĻº´æÐÅÏ¢ÁË£º + +![image-20230530233852486](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233852486.png) + +¿ÉÒÔ¿´µ½£¬·µ»ØµÄÄÚÈݲ»½öչʾÁËÓ¦ÓóÌÐòµÄ»º´æ¹ÜÀíÆ÷£¬Í¬Ê±Ò²Õ¹Ê¾ÁË»º´æµÄKeyºÍ»º´æÊý¾Ý´æ´¢ÀàÐÍÐÅÏ¢¡£ + +## caches-cache¶Ëµã + +caches-cache¶ËµãÊǶÔÉÏÃæcaches¶ËµãµÄÀ©Õ¹£¬caches¶ËµãչʾµÄËùÓеĻº´æÐÅÏ¢¡£Èç¹ûÏëÖ±½Ó¿´³ö¾ßÌåµÄÒ»¸ö»º´æÐÅÏ¢£¬Ôò¿ÉÒÔʹÓÃcaches-cache¶Ëµã¡£ + +·ÃÎʵÄURLΪ£º[http://localhost:8080/actuator/caches/{cache}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fcaches%2F%257Bcache%257D "http://localhost:8080/actuator/caches/%7Bcache%7D") £¬ÆäÖдóÀ¨ºÅÄÚµÄÖµ¿ÉÒÔÌæ»»Îª»º´æµÄkey¡£ + + + + +`http://localhost:8080/actuator/caches/queryAll` + +½«ÉÏÃæµÄռλ·û»»³ÉqueryAll£¨»º´æµÄkey£©£¬Ö´Ðнá¹ûÈçÏ£º + +![image-20230530233906164](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233906164.png) + +¿ÉÒÔ¿´³ö£¬Ö»²éѯ³ýÁËÖ¸¶¨µÄ»º´æÐÅÏ¢£¬°üÀ¨»º´æ¹ÜÀíÆ÷¡¢»º´æÃû³Æ£¨key£©£¬»º´æµÄ´æ´¢ÀàÐÍ¡£ + +## health¶Ëµã + +health¶ËµãÓÃÀ´¼ì²éÓ¦ÓõÄÔËÐÐ״̬£¬×î¸ßƵʹÓõÄÒ»¸ö¶Ëµã¡£¼ì²éÓ¦ÓÃʵÀýµÄÔËÐÐ״̬ÒÔ¼°Ó¦Óò»¡±½¡¿µ¡°µÄÔ­Òò£¬±ÈÈçÊý¾Ý¿âÁ¬½Ó¡¢´ÅÅ̿ռ䲻¹»µÈ¡£ + +·ÃÎʵØÖ·£º[http://localhost:8080/actuator/health](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fhealth "http://localhost:8080/actuator/health") + +չʾ½á¹û£º + +`{ +"status": "UP" +}` + +ÉÏÃæµÄʵÀý¹ýÓÚ¼òµ¥£¬ÔÚÏîÄ¿ÖаÑÊý¾Ý¿â¸ø¼¯³É½øÈ¥£º + +` + +```` + + org.springframework.boot + spring-boot-starter-jdbc + + + mysql + mysql-connector-java +` +```` + +È»ºóÔÚapplicationÅäÖÃÎļþÖнøÐÐÅäÖ㺠+ + + +``` +spring: + datasource: + url: jdbc:mysql://xxx:3333/xxx?characterEncoding=utf8&serverTimezone=Asia/Shanghai + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +ͬʱ£¬ÎÒÃÇÒªÔÚapplicationÅäÖÃÎļþÖÐÅäÖÃÒ»ÏÂmanagement.endpoint.health.show-detailsµÄÖµ¡£¸ÃÊôÐÔÓÐÈý¸ö¿ÉÑ¡Ï + +* never £º²»Õ¹Ê¾ÏêϸÐÅÏ¢£¬up »òÕß down µÄ״̬£¬Ä¬ÈÏÅäÖã» +* when-authorized£ºÏêϸÐÅÏ¢½«»áչʾ¸øÍ¨¹ýÈÏÖ¤µÄÓû§¡£ÊÚȨµÄ½ÇÉ«¿ÉÒÔͨ¹ýmanagement.endpoint.health.roles ÅäÖã» +* always£º¶ÔËùÓÐÓû§±©Â¶ÏêϸÐÅÏ¢¡£ + +ĬÈÏÖµÊÇnever£¬ËùÒÔÎÒÃÇÖ±½Ó·ÃÎÊ¿´µ½µÄÖ»ÓÐUP»òDOWN¡£ÏÖÔÚ¼¯³ÉÁËÊý¾Ý¿â£¬Í¬Ê±°Ñ¸ÃÏîÖµÅäÖÃΪalways£¬¿´Ò»ÏÂÏêÇ飺 + +![image-20230530233934501](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233934501.png) + +¿ÉÒÔ¿´µ½ÕûÌå״̬ΪUP£¬ÆäÖÐÏÂÃæµÄÈý¸ö×é¼þ¾ùΪUP£¬¶øÊý¾Ý¿âÊÇMYSQL£¬¼ì²éÊý¾Ý¿âµÄÓï¾äΪ¡°SELECT 1¡±¡£Í¬Ê±£¬»¹Õ¹Ê¾ÁË´ÅÅÌÐÅÏ¢ºÍpingµÄ״̬¡£ + +ÏÖÔÚÎÒÃǰÑÊý¾Ý¿âµÄÓû§ÃûºÍÃÜÂë¹ÊÒâ¸Ä´í£¬ÖØÆô·ÃÎʿɵ㺠+ +![image-20230530233951145](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530233951145.png) + +״̬ΪDOWN£¬×é¼þÖÐdb³öÏÖÁËÎÊÌ⣬״̬ΪDOWN¡£ÎÊÌâµÄÏêÇéÔÚerrorÖÐչʾ£¬¿ÉÒÔ¿´³öÊǽ¨Á¢Á¬½Óʱ³ö´íÁË¡£ÔÚʵ¼ùÖУ¬ÎÒÃÇ¿ÉÒÔͨ¹ýhealth¶Ë¿Ú¼à¿ØÊý¾Ý¿â¡¢Redis¡¢MongoDB¡¢´ÅÅ̵Ƚ¡¿µÇé¿ö¡£ActuatorÔ¤¶¨ÒåµÄ´¦ÀíÀàΪ£ºDataSourceHealthIndicator, DiskSpaceHealthIndicator, MongoHealthIndicator, RedisHealthIndicatorµÈ¡£ + +¶øÇÒÿ¸öÖ¸±ê¶¼¿ÉÒÔµ¥¶ÀµÄ½øÐпªÆôºÍ¹Ø±Õ£¬ÒÔÊý¾Ý¿âµÄΪÀý£º + + +```` +management: + health: + db: + enabled: true` +```` + +## info¶Ëµã + +/info ¶ËµãÓÃÀ´²é¿´ÅäÖÃÎļþ applicationÖÐÒÔinfo¿ªÍ·µÄÅäÖÃÐÅÏ¢£¬Ä¬ÈÏÇé¿öÏ applicationÖв¢Ã»ÓÐ info ½ÚµãÅäÖã¬ËùÒÔĬÈÏΪ¿Õ¡£ + +applicationÖÐÌí¼ÓÈçÏÂÅäÖ㺠+ + + +```` +info: + user: + type: ¹«ÖںŠ+ name: ³ÌÐòÐÂÊÓ½ç + wechat: zhuan2quan +```` + +·ÃÎÊ[http://localhost:8080/actuator/info](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Finfo "http://localhost:8080/actuator/info") £¬Õ¹Ê¾½á¹ûÈçÏ£º + +![image-20230530234019487](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234019487.png) + +## conditions¶Ëµã + +Spring BootÌṩÁË×Ô¶¯ÅäÖù¦ÄÜ£¬Ê¹ÓÃÆðÀ´·Ç³£·½±ã¡£µ«ÕâЩ×Ô¶¯ÅäÖÃÀàÊÇʲôÇé¿öÏÂÉúЧµÄ£¬ÊÇ·ñÉúЧÊDZȽÏÄÑÅŲéµÄ¡£´Ëʱ£¬¿ÉÒÔʹÓà conditions ÔÚÓ¦ÓÃÔËÐÐʱ²é¿´Ä³¸öÅäÖÃÀàÔÚʲôÌõ¼þÏÂÉúЧ£¬»òΪʲôûÓÐÉúЧ¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/conditions](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fconditions "http://localhost:8080/actuator/conditions") £¬²¿·Ö·µ»ØÐÅÏ¢ÈçÏ£º + +![image-20230530234053134](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234053134.png) + +¿ÉÒÔ¿´µ½Ä³¸ö×Ô¶¯ÅäÖÃÀà¶ÔÓ¦µÄÉúЧÌõ¼þºÍÌáʾÐÅÏ¢¡£ + +## shutdown¶Ëµã + +shutdown¶ËµãÊôÓÚ²Ù×÷¿ØÖÆÀà¶Ëµã£¬¿ÉÒÔÓÅÑÅ¹Ø±Õ Spring Boot Ó¦Óá£ÐèÒªÔÚÅäÖÃÎļþÖпªÆô£º + + +```` +management: + endpoint: + shutdown: + enabled: true +```` + +¸Ã¶ËµãÖ»Ö§³ÖPOSTÇëÇó£¬Ö´ÐÐÃüÁî¼°·µ»Ø½á¹ûÈçÏ£º + + +``` +curl -X POST "http://localhost:8080/actuator/shutdown" +{ + "message": "Shutting down, bye..." +} +``` + +Ö´ÐÐÖ®ºó£¬»á·¢ÏÖÓ¦ÓóÌÐòÒѾ­±»¹Ø±ÕÁË¡£ÓÉÓڸö˵ã»á¹Ø±ÕÓ¦ÓóÌÐò£¬Òò´ËʹÓÃÊÇÐèҪСÐÄ¡£ + +## configprops¶Ëµã + +ÔÚSpring BootÏîÄ¿ÖУ¬ÎÒÃǾ­³£»áÓõ½@ConfigurationProperties×¢½âÀ´ÅúÁ¿×¢ÈëһЩÊôÐÔ£¬¶øconfigprops¶Ëµã¾ÍÊÇÓÃÀ´ÏÔʾÕâЩ±»¸Ã×¢½â±ê×¢µÄÅäÖÃÀà¡£ + +±ÈÈçÇ°ÃæµÄinfoÅäÖã¬ÎÒÃǾͿÉÒÔ¶¨ÒåÒ»¸öÀàInfoProperties£º + + +```` +@Component +@ConfigurationProperties(prefix = "info") +public class InfoProperties { + + private String type; + + private String name; + + private String wechat; + + // Ê¡ÂÔgetter/setter +} +```` + +·ÃÎÊURL£º[http://localhost:8080/actuator/configprops](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fconfigprops "http://localhost:8080/actuator/configprops") £¬²¿·ÖÐÅÏ¢ÈçÏ£º + +![image-20230530234110515](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234110515.png) + +²»½ö¿ÉÒÔ¿´µ½ÏµÍ³ÖÐĬÈϼ¯³ÉµÄÅäÖÃÀàÐÅÏ¢£¬»¹¿ÉÒÔ¿´µ½ÎÒÃÇ×Ô¶¨ÒåµÄÅäÖÃÀàÐÅÏ¢¡£ÕâÀïÐèҪעÒâµÄÊǶÔÓ¦µÄÀàÐèÒª½øÐÐʵÀý»¯£¨@Component£©ÕâÀï²ÅÄܹ»¿´µ½¡£ + +ÎÒÃÇ×Ô¶¨ÒåµÄÀàÖзµ»ØÁËBeanµÄÃû³Æ¡¢ÅäÖÃǰ׺¡£ÉÏÃæµÄProjectInfoProperties»¹·µ»ØÁËÊôÐÔÐÅÏ¢¡£ + +## env¶Ëµã + +env¶ËµãÓÃÓÚ»ñȡȫ²¿»·¾³ÊôÐÔ£¬°üÀ¨applicationÅäÖÃÎļþÖеÄÄÚÈÝ¡¢ÏµÍ³±äÁ¿µÈ¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/env£¬·µ»Ø²¿·ÖÐÅÏ¢£º](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%25EF%25BC%258C%25E8%25BF%2594%25E5%259B%259E%25E9%2583%25A8%25E5%2588%2586%25E4%25BF%25A1%25E6%2581%25AF%25EF%25BC%259A "http://localhost:8080/actuator/env%EF%BC%8C%E8%BF%94%E5%9B%9E%E9%83%A8%E5%88%86%E4%BF%A1%E6%81%AF%EF%BC%9A") + +![image-20230530234200949](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234200949.png) + +## env-toMatch¶Ëµã + +env-toMatch¶ËµãÓëcachesºÍcaches-cacheÀàËÆ£¬Ò»¸öÊÇ»ñÈ¡ËùÓеģ¬Ò»¸öÊÇ»ñȡָ¶¨µÄ¡£ÕâÀïµÄenv-toMatch¶ËµãÊÇ»ñȡָ¶¨keyµÄ»·¾³±äÁ¿ÊôÐÔ¡£ + +»ù±¾¸ñʽΪ£º[http://localhost:8080/actuator/env/{toMatch}¡£](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%2F%257BtoMatch%257D%25E3%2580%2582 "http://localhost:8080/actuator/env/%7BtoMatch%7D%E3%80%82") ʵÀýURL£º[http://localhost:8080/actuator/env/info.user.name](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fenv%2Finfo.user.name "http://localhost:8080/actuator/env/info.user.name") £¬·µ»Ø½á¹ûÈçÏÂͼ£º + +![image-20230530234238073](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234238073.png) + +·µ»ØÊý¾ÝÐÅÏ¢°üÀ¨¸ÃÊôÐÔµÄÀ´Ô´¡¢valueÖµµÈÐÅÏ¢¡£ + +## loggers¶Ëµã + +/loggers ¶Ëµã±©Â¶Á˳ÌÐòÄÚ²¿ÅäÖõÄËùÓÐ logger µÄÐÅÏ¢£¬°üÀ¨²»Í¬µÄpackage¡¢²»Í¬µÄÀàµÄÈÕÖ¾¼¶±ðÐÅÏ¢¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/loggers](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers "http://localhost:8080/actuator/loggers") £¬²¿·Ö·µ»Ø½á¹û£º + +![image-20230530234301625](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234301625.png) + +## loggers-name¶Ëµã + +loggers-name¶ËµãÒ²ÊÇlogger¶ËµãµÄϸ·Ö£¬¿ÉÒÔͨ¹ýname·ÃÎÊijһ¸ölogger¡£ + +»ù±¾ÇëÇó¸ñʽ£º[http://localhost:8080/actuator/loggers/{name}](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers%2F%257Bname%257D "http://localhost:8080/actuator/loggers/%7Bname%7D") ʾÀýÇëÇóURL£º[http://localhost:8080/actuator/loggers/com.secbro2.SpringbootActuatorApplication](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Floggers%2Fcom.secbro2.SpringbootActuatorApplication "http://localhost:8080/actuator/loggers/com.secbro2.SpringbootActuatorApplication") £¬·µ»Ø½á¹ûÈçÏ£º + + + +`{ +"configuredLevel": null, +"effectiveLevel": "INFO" +}` + +¿ÉÒÔ¿´³ö£¬Æô¶¯ÀàµÄÈÕÖ¾¼¶±ðΪINFO¡£ + +## heapdump¶Ëµã + +heapdump¶Ëµã»á·µ»ØÒ»¸öJVM ¶Ñdump£¬Í¨¹ýJVM×Ô´øµÄ¼à¿Ø¹¤¾ßVisualVM¿É´ò¿ª´ËÎļþ²é¿´ÄÚ´æ¿ìÕÕ¡£ÕâÊÇÄÚ´æÓÅ»¯£¬»ùÓÚ¶ÑÕ»²ãÃæ½øÐÐÅŲéµÄÀûÆ÷¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/heapdump](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fheapdump "http://localhost:8080/actuator/heapdump") ¡£Mac²Ù×÷ϵͳÏÂä¯ÀÀÆ÷·ÃÎÊ»áÏÂÔØÒ»¸öÃû×ÖΪheapdumpµÄÎļþ£¬ÎÞºó׺£¬30M¡£ + +ÃüÁîÐÐÖ´ÐÐjvisualvmÃüÁ´ò¿ªVisualVM£¬ÒÀ´Îµã»÷¡°Îļþ¡±¡¢¡°×°È롱£¬¼ÇµÃÎļþÀàÐÍҪѡÔñ¡°¶ÑDump(_.hprof,_.*)¡±£¬È»ºóÑ¡Ôñheapdump¡£ + +![image-20230530234346098](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234346098.png) + +´Ëʱ±ã¿ÉÒÔͨ¹ý¹¤¾ßÀ´½øÐжÑÕ»ÐÅÏ¢µÄ·ÖÎöÁË¡£¶ÔÓÚÏßÉÏÎÊÌâµÄ·ÖÎöÌṩÁ˼«Îª±ãÀûµÄ·½Ê½¡£ + +## threaddump¶Ëµã + +/threaddump ¶Ëµã»áÉú³Éµ±Ç°Ï̻߳µÄ¿ìÕÕ¡£ÔÚÈÕ³£¶¨Î»ÎÊÌâµÄʱºò²é¿´Ï̵߳ÄÇé¿ö·Ç³£ÓÐÓã¬Ö÷ҪչʾÁËÏß³ÌÃû¡¢Ïß³ÌID¡¢Ï̵߳Ä״̬¡¢ÊÇ·ñµÈ´ýËø×ÊÔ´µÈÐÅÏ¢¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/threaddump](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fthreaddump "http://localhost:8080/actuator/threaddump") £¬²¿·Ö·µ»Ø½á¹û£º + +![image-20230530234405331](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234405331.png) + +ÎÒÃÇ¿Éͨ¹ýÏ߳̿ìÕÕÀ´ÅŲéÉú²ú»·¾³µÄÎÊÌâ¡£ + +## metrics¶Ëµã + +/metrics ¶ËµãÓÃÀ´±©Â¶µ±Ç°Ó¦Óõĸ÷ÀàÖØÒª¶ÈÁ¿Ö¸±ê£¬±ÈÈ磺ÄÚ´æÐÅÏ¢¡¢Ïß³ÌÐÅÏ¢¡¢À¬»ø»ØÊÕÐÅÏ¢¡¢tomcat¡¢Êý¾Ý¿âÁ¬½Ó³ØµÈ¡£2.x°æ±¾ÕâÀïÖ»ÏÔʾÁËÒ»¸öÖ¸±êµÄÁÐ±í¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/metrics](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics "http://localhost:8080/actuator/metrics") ¡£ + +```` +{ + "names": [ + "jvm.memory.max", + "jvm.threads.states", + "jvm.gc.pause", + "http.server.requests", + "process.files.max", + "jvm.gc.memory.promoted", + "system.load.average.1m", + "jvm.memory.used", + "jvm.gc.max.data.size", + "jvm.memory.committed", + "system.cpu.count", + "logback.events", + "jvm.buffer.memory.used", + "tomcat.sessions.created", + "jvm.threads.daemon", + "system.cpu.usage", + "jvm.gc.memory.allocated", + "tomcat.sessions.expired", + "jvm.threads.live", + "jvm.threads.peak", + "process.uptime", + "tomcat.sessions.rejected", + "process.cpu.usage", + "jvm.classes.loaded", + "jvm.classes.unloaded", + "tomcat.sessions.active.current", + "tomcat.sessions.alive.max", + "jvm.gc.live.data.size", + "process.files.open", + "jvm.buffer.count", + "jvm.buffer.total.capacity", + "tomcat.sessions.active.max", + "process.start.time" + ] +} +```` + +/metrics¶Ëµã¿ÉÒÔÌṩӦÓÃÔËÐÐ״̬µÄÍêÕû¶ÈÁ¿Ö¸±ê±¨¸æ£¬ÕâÏÄܷdz£µÄʵÓ㬵«ÊǶÔÓÚ¼à¿ØÏµÍ³Öеĸ÷Ïî¼à¿Ø¹¦ÄÜ£¬ËüÃÇµÄ¼à¿ØÄÚÈÝ¡¢Êý¾ÝÊÕ¼¯ÆµÂʶ¼ÓÐËù²»Í¬£¬Èç¹ûÎÒÃÇÿ´Î¶¼Í¨¹ýÈ«Á¿»ñÈ¡±¨¸æµÄ·½Ê½À´ÊÕ¼¯£¬ÂÔÏÔ´Ö±©¡£¹Ù·½Ò²ÊÇ¿ÉÄÜÊÇ´¦ÓÚ´Ë·½ÃæµÄ¿¼ÂÇ£¬ÔÚSpring Boot 2.xÖ®ºó£¬/metrics¶ËµãÖ»ÏÔʾÁËÖ¸±êµÄÁÐ±í¡£ + +Èç¹ûÐèÒª²é¿´¾ßÌåµÄijÏîÖ¸±ê£¬Ôò¿Éͨ¹ý/metrics-requiredMetricName¶ËµãÀ´ÊµÏÖ¡£ + +## metrics-requiredMetricName¶Ëµã + +metrics-requiredMetricName¶Ëµã£¬ÓÃÓÚ·ÃÎÊÖ¸¶¨Ö¸±êµÄ±¨¸æ£¬Ò»°ã»áÅäºÏ/metrics¶ËµãÏȲé³öÖ¸±êÁÐ±í£¬È»ºóÔÙ²éѯ¾ßÌåµÄij¸öÖ¸±ê¡£ + +»ù±¾¸ñʽ£º[http://localhost:8080/actuator/metrics/{requiredMetricName}¡£](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics%2F%257BrequiredMetricName%257D%25E3%2580%2582 "http://localhost:8080/actuator/metrics/%7BrequiredMetricName%7D%E3%80%82") ʵÀýURL£º[http://localhost:8080/actuator/metrics/jvm.memory.max](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmetrics%2Fjvm.memory.max "http://localhost:8080/actuator/metrics/jvm.memory.max") £¬·µ»Ø½á¹ûÈçÏ£º + + +```` +{ + "name": "jvm.memory.max", + "description": "The maximum amount of memory in bytes that can be used for memory management", + "baseUnit": "bytes", + "measurements": [ + { + "statistic": "VALUE", + "value": 5606211583 + } + ], + "availableTags": [ + { + "tag": "area", + "values": [ + "heap", + "nonheap" + ] + }, + { + "tag": "id", + "values": [ + "Compressed Class Space", + "PS Survivor Space", + "PS Old Gen", + "Metaspace", + "PS Eden Space", + "Code Cache" + ] + } + ] +} +```` + +ÉÏÊö½á¹ûչʾÁË×î´ó¿ÉÓÃÄÚ´æµÄÇé¿ö¡£ÆäËûÏà¹ØÖ¸±êµÄÕ¹Ê¾Ìæ»»¶ÔÓ¦µÄÃû×Ö½øÐв鿴¼´¿É¡£ + +## scheduledtasks¶Ëµã + +/scheduledtasks¶ËµãÓÃÓÚչʾӦÓÃÖеĶ¨Ê±ÈÎÎñÐÅÏ¢¡£ + +ÏÈÔÚÏîÄ¿Öй¹½¨Á½¸ö¶¨Ê±ÈÎÎñ£¬Ê×ÏÈÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableScheduling¿ªÆô¶¨Ê±ÈÎÎñ¹¦ÄÜ¡£È»ºó´´½¨¶¨Ê±ÈÎÎñÀࣺ + +```` +@Component +public class MyTask { + + @Scheduled(cron = "0/10 * * * * *") + public void work() { + System.out.println("I am a cron job."); + } + + @Scheduled(fixedDelay = 10000) + public void work1() { + System.out.println("I am a fixedDelay job."); + } +} +```` + +ÆäÖж¨ÒåÁËÁ½ÖÖÀàÐ͵Ķ¨Ê±ÈÎÎñ£¬workÊÇ»ùÓÚcronʵÏֵĶ¨Ê±ÈÎÎñ£¬work1ÊÇ»ùÓÚfixedDelayʵÏֵĶ¨Ê±ÈÎÎñ¡£ + +·ÃÎÊURL£º[http://localhost:8080/actuator/scheduledtasks](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fscheduledtasks "http://localhost:8080/actuator/scheduledtasks") £¬·µ»Ø½á¹ûÐÅÏ¢ÈçÏ£º + +``` +{ + "cron": [ + { + "runnable": { + "target": "com.secbro2.job.MyTask.work" + }, + "expression": "0/10 * * * * *" + } + ], + "fixedDelay": [ + { + "runnable": { + "target": "com.secbro2.job.MyTask.work1" + }, + "initialDelay": 0, + "interval": 10000 + } + ], + "fixedRate": [], + "custom": [] +} +``` + +¿ÉÒÔ¿´µ½£¬Í¨¹ý¸Ã¶Ëµã¿ÉÒÔÃ÷È·µÄÖªµÀµ±Ç°Ó¦ÓÃÖж¨ÒåµÄ¶¨Ê±ÈÎÎñ£¬ÒÔ¼°Ö´ÐÐģʽºÍƵ´Î¡£ + +## mappings¶Ëµã + +/mappings¶ËµãÓÃÓÚÃèÊöÈ«²¿µÄ URI ·¾¶£¬ÒÔ¼°ºÍ¿ØÖÆÆ÷µÄÓ³Éä¹ØÏµ¡£Õâ¸ö¹¦ÄÜËãÊDZȽϳ£ÓõÄÁË£¬Èç¹ûÏëϵͳµÄ²é¿´URL¶ÔÓ¦µÄController¼°·½·¨£¬¿ÉÒÔʹÓô˶˵㡣 + +·ÃÎÊURL£º[http://localhost:8080/actuator/mappings](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Factuator%2Fmappings "http://localhost:8080/actuator/mappings") £¬²¿·Ö·µ»Ø½á¹ûÈçÏ£º + +![image-20230530234501440](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230530234501440.png) + +ÖÁ´Ë¹ØÓÚSpring Boot ActuatorÌṩµÄËùÓж˵ã½éÉÜÍê±Ï¡£ + +## С½á + +±¾ÎÄͨ¹ý¶ÔSpring Boot ActuatorÌṩËùÓж˵㹹½¨ÊµÀý²¢ÑÝʾ½á¹û£¬±¾ÎÄ´ó¶àÊýÄÚÈݺÍʵÀý¶¼ÊÇÈ«ÍøµÚÒ»ÊÖ×ÊÁÏ¡£¸Ã¿ò¼Ü¶ÔÅŲéÏßÉÏÎÊÌ⣬ÐÔÄÜÓÅ»¯µÈ¶¼Óм«´óµÄ°ïÖú¡£¶øÔÚд±¾ÎĵĹý³ÌÖÐÒ²Ô½À´Ô½¾ªÌ¾ActuatorµÄ¹¦ÄÜ֮ǿ´ó£¬Ç¿ÁÒÍÆ¼öÓÃÆðÀ´¡£ + +## ²Î¿¼Á´½Ó + +×÷Õߣº³ÌÐòÐÂÊÓ½ç +Á´½Ó£ºhttps://juejin.cn/post/6984550846876876814 +À´Ô´£ºÏ¡ÍÁ¾ò½ð +Öø×÷Ȩ¹é×÷ÕßËùÓС£ÉÌÒµ×ªÔØÇëÁªÏµ×÷Õß»ñµÃÊÚȨ£¬·ÇÉÌÒµ×ªÔØÇë×¢Ã÷³ö´¦¡£ + diff --git a/docs/monitor/SpringBoot Admin.md b/docs/monitor/SpringBoot Admin.md new file mode 100644 index 0000000..7b0c8ab --- /dev/null +++ b/docs/monitor/SpringBoot Admin.md @@ -0,0 +1,495 @@ +## ÕªÒª + +Spring Boot Admin ¿ÉÒÔ¶ÔSpringBootÓ¦Óõĸ÷ÏîÖ¸±ê½øÐÐ¼à¿Ø£¬¿ÉÒÔ×÷Ϊ΢·þÎñ¼Ü¹¹ÖÐµÄ¼à¿ØÖÐÐÄÀ´Ê¹Ó㬱¾ÎĽ«¶ÔÆäÓ÷¨½øÐÐÏêϸ½éÉÜ¡£ + +## Spring Boot Admin ¼ò½é + +SpringBootÓ¦ÓÿÉÒÔͨ¹ýActuatorÀ´±©Â¶Ó¦ÓÃÔËÐйý³ÌÖеĸ÷ÏîÖ¸±ê£¬Spring Boot Adminͨ¹ýÕâЩָ±êÀ´¼à¿ØSpringBootÓ¦Óã¬È»ºóͨ¹ýͼÐλ¯½çÃæ³ÊÏÖ³öÀ´¡£Spring Boot Admin²»½ö¿ÉÒÔ¼à¿Øµ¥ÌåÓ¦Ó㬻¹¿ÉÒÔºÍSpring CloudµÄ×¢²áÖÐÐÄÏà½áºÏÀ´¼à¿ØÎ¢·þÎñÓ¦Óᣠ+ +Spring Boot Admin ¿ÉÒÔÌṩӦÓõÄÒÔÏÂ¼à¿ØÐÅÏ¢£º + +* ¼à¿ØÓ¦ÓÃÔËÐйý³ÌÖеĸÅÀÀÐÅÏ¢£» +* ¶ÈÁ¿Ö¸±êÐÅÏ¢£¬±ÈÈçJVM¡¢Tomcat¼°½ø³ÌÐÅÏ¢£» +* »·¾³±äÁ¿ÐÅÏ¢£¬±ÈÈçϵͳÊôÐÔ¡¢ÏµÍ³»·¾³±äÁ¿ÒÔ¼°Ó¦ÓÃÅäÖÃÐÅÏ¢£» +* ²é¿´ËùÓд´½¨µÄBeanÐÅÏ¢£» +* ²é¿´Ó¦ÓÃÖеÄËùÓÐÅäÖÃÐÅÏ¢£» +* ²é¿´Ó¦ÓÃÔËÐÐÈÕÖ¾ÐÅÏ¢£» +* ²é¿´JVMÐÅÏ¢£» +* ²é¿´¿ÉÒÔ·ÃÎʵÄWeb¶Ëµã£» +* ²é¿´HTTP¸ú×ÙÐÅÏ¢¡£ + +## ´´½¨admin-serverÄ£¿é + +> ÕâÀïÎÒÃÇ´´½¨Ò»¸öadmin-serverÄ£¿éÀ´×÷Ϊ¼à¿ØÖÐÐÄÑÝʾÆä¹¦ÄÜ¡£ + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + + + +```` + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-server + + +```` + +* ÔÚapplication.ymlÖнøÐÐÅäÖ㺠+ + + + + +````spring: + application: + name: admin-server +server: + port: 9301 +```` + +* ÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableAdminServerÀ´ÆôÓÃadmin-server¹¦ÄÜ£º + + + + +```` +@EnableAdminServer +@SpringBootApplication +public class AdminServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminServerApplication.class, args); + } + +} +```` + +## ´´½¨admin-clientÄ£¿é + +> ÕâÀïÎÒÃÇ´´½¨Ò»¸öadmin-clientÄ£¿é×÷Ϊ¿Í»§¶Ë×¢²áµ½admin-server¡£ + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + +```` + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-client + +```` + +* ÔÚapplication.ymlÖнøÐÐÅäÖ㺠+ + + + + + + + +``` +spring: + application: + name: admin-client + boot: + admin: + client: + url: http://localhost:9301 #ÅäÖÃadmin-serverµØÖ· +server: + port: 9305 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always +logging: + file: admin-client.log #Ìí¼Ó¿ªÆôadminµÄÈÕÖ¾¼à¿Ø +``` + +* Æô¶¯admin-serverºÍadmin-client·þÎñ¡£ + +## ¼à¿ØÐÅÏ¢ÑÝʾ + +* ·ÃÎÊÈçϵØÖ·´ò¿ªSpring Boot AdminµÄÖ÷Ò³£º[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001002163](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001002163.png) + + + + + +* µã»÷wallboard°´Å¥£¬Ñ¡Ôñadmin-client²é¿´¼à¿ØÐÅÏ¢£» + +* ¼à¿ØÐÅÏ¢¸ÅÀÀ£» + +![image-20230531001023644](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001023644.png) + + + + + +* ¶ÈÁ¿Ö¸±êÐÅÏ¢£¬±ÈÈçJVM¡¢Tomcat¼°½ø³ÌÐÅÏ¢£» + +![image-20230531001053279](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001053279.png) + + + + + +* »·¾³±äÁ¿ÐÅÏ¢£¬±ÈÈçϵͳÊôÐÔ¡¢ÏµÍ³»·¾³±äÁ¿ÒÔ¼°Ó¦ÓÃÅäÖÃÐÅÏ¢£» + +![image-20230531001103093](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001103093.png) + + + + + +* ²é¿´ËùÓд´½¨µÄBeanÐÅÏ¢£» + +![image-20230531001111221](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001111221.png) + + + + + +* ²é¿´Ó¦ÓÃÖеÄËùÓÐÅäÖÃÐÅÏ¢£» + +![image-20230531001124678](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001124678.png) + + + + + +* ²é¿´ÈÕÖ¾ÐÅÏ¢£¬ÐèÒªÌí¼ÓÒÔÏÂÅäÖòÅÄÜ¿ªÆô£» + + + +`logging: +file: admin-client.log #Ìí¼Ó¿ªÆôadminµÄÈÕÖ¾¼à¿Ø` + +![image-20230531001136184](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001136184.png) + + + + +* ²é¿´JVMÐÅÏ¢£» + +![image-20230531001144614](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001144614.png) + + + + + +* ²é¿´¿ÉÒÔ·ÃÎʵÄWeb¶Ëµã£» + +![image-20230531001156191](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001156191.png) + + + + + +* ²é¿´HTTP¸ú×ÙÐÅÏ¢£» + +![image-20230531001206364](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001206364.png) + + + + + +## ½áºÏ×¢²áÖÐÐÄʹÓà + +> Spring Boot Admin½áºÏSpring Cloud ×¢²áÖÐÐÄʹÓã¬Ö»Ð轫admin-serverºÍ×¢²áÖÐÐÄÕûºÏ¼´¿É£¬admin-server »á×Ô¶¯´Ó×¢²áÖÐÐÄ»ñÈ¡·þÎñÁÐ±í£¬È»ºó°¤¸ö»ñÈ¡¼à¿ØÐÅÏ¢¡£ÕâÀïÒÔEureka×¢²áÖÐÐÄΪÀýÀ´½éÉÜϸù¦ÄÜ¡£ + +### ÐÞ¸Äadmin-server + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + + +```` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +```` + +* ÔÚapplication-eureka.ymlÖнøÐÐÅäÖã¬Ö»ÐèÌí¼Ó×¢²áÖÐÐÄÅäÖü´¿É£º + + + + +```` +spring: + application: + name: admin-server +server: + port: 9301 +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +```` + +* ÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableDiscoveryClientÀ´ÆôÓ÷þÎñ×¢²á¹¦ÄÜ£º + + + + + +```` +`@EnableDiscoveryClient +@EnableAdminServer +@SpringBootApplication +public class AdminServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminServerApplication.class, args); + } + +} +```` + +### ÐÞ¸Äadmin-client + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + +```` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +```` + +* ÔÚapplication-eureka.ymlÖнøÐÐÅäÖã¬É¾³ýÔ­À´µÄadmin-serverµØÖ·ÅäÖã¬Ìí¼Ó×¢²áÖÐÐÄÅäÖü´¿É£º + + + + + +``` +spring: + application: + name: admin-client +server: + port: 9305 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always +logging: + file: admin-client.log #Ìí¼Ó¿ªÆôadminµÄÈÕÖ¾¼à¿Ø +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +``` + +* ÔÚÆô¶¯ÀàÉÏÌí¼Ó@EnableDiscoveryClientÀ´ÆôÓ÷þÎñ×¢²á¹¦ÄÜ£º + + + + + +``` +@EnableDiscoveryClient +@SpringBootApplication +public class AdminClientApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminClientApplication.class, args); + } + +} +``` + +### ¹¦ÄÜÑÝʾ + +* Æô¶¯eureka-server£¬Ê¹ÓÃapplication-eureka.ymlÅäÖÃÆô¶¯admin-server£¬admin-client£» + +* ²é¿´×¢²áÖÐÐÄ·¢ÏÖ·þÎñ¾ùÒÑ×¢²á£º[http://localhost:8001/](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8001%2F "http://localhost:8001/") + +![image-20230531001221519](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001221519.png) + + +* ²é¿´Spring Boot Admin Ö÷Ò³·¢ÏÖ¿ÉÒÔ¿´µ½·þÎñÐÅÏ¢£º[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001232048](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001232048.png) + + +## Ìí¼ÓµÇ¼ÈÏÖ¤ + +> ÎÒÃÇ¿ÉÒÔͨ¹ý¸øadmin-serverÌí¼ÓSpring SecurityÖ§³ÖÀ´»ñµÃµÇ¼ÈÏÖ¤¹¦ÄÜ¡£ + +### ´´½¨admin-security-serverÄ£¿é + +* ÔÚpom.xmlÖÐÌí¼ÓÏà¹ØÒÀÀµ£º + + + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + de.codecentric + spring-boot-admin-starter-server + 2.1.5 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + +``` + +* ÔÚapplication.ymlÖнøÐÐÅäÖã¬ÅäÖõǼÓû§ÃûºÍÃÜÂ룬ºöÂÔadmin-security-serverµÄ¼à¿ØÐÅÏ¢£º + + + + + +``` +spring: + application: + name: admin-security-server + security: # ÅäÖõǼÓû§ÃûºÍÃÜÂë + user: + name: macro + password: 123456 + boot: # ²»ÏÔʾadmin-security-serverµÄ¼à¿ØÐÅÏ¢ + admin: + discovery: + ignored-services: ${spring.application.name} +server: + port: 9301 +eureka: + client: + register-with-eureka: true + fetch-registry: true + service-url: + defaultZone: http://localhost:8001/eureka/ +``` + +* ¶ÔSpringSecurity½øÐÐÅäÖã¬ÒÔ±ãadmin-client¿ÉÒÔ×¢²á£º + + + + + +scss + +¸´ÖÆ´úÂë + + + + + +``` +/** + * Created by macro on 2019/9/30. + */ +@Configuration +public class SecuritySecureConfig extends WebSecurityConfigurerAdapter { + private final String adminContextPath; + + public SecuritySecureConfig(AdminServerProperties adminServerProperties) { + this.adminContextPath = adminServerProperties.getContextPath(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); + successHandler.setTargetUrlParameter("redirectTo"); + successHandler.setDefaultTargetUrl(adminContextPath + "/"); + + http.authorizeRequests() + //1.ÅäÖÃËùÓо²Ì¬×ÊÔ´ºÍµÇ¼ҳ¿ÉÒÔ¹«¿ª·ÃÎÊ + .antMatchers(adminContextPath + "/assets/**").permitAll() + .antMatchers(adminContextPath + "/login").permitAll() + .anyRequest().authenticated() + .and() + //2.ÅäÖõǼºÍµÇ³ö·¾¶ + .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and() + .logout().logoutUrl(adminContextPath + "/logout").and() + //3.¿ªÆôhttp basicÖ§³Ö£¬admin-client×¢²áʱÐèҪʹÓà + .httpBasic().and() + .csrf() + //4.¿ªÆô»ùÓÚcookieµÄcsrf±£»¤ + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + //5.ºöÂÔÕâЩ·¾¶µÄcsrf±£»¤ÒÔ±ãadmin-client×¢²á + .ignoringAntMatchers( + adminContextPath + "/instances", + adminContextPath + "/actuator/**" + ); + } +} +``` + +* ÐÞ¸ÄÆô¶¯À࣬¿ªÆôAdminServer¼°×¢²á·¢ÏÖ¹¦ÄÜ£º + + + + + +``` +@EnableDiscoveryClient +@EnableAdminServer +@SpringBootApplication +public class AdminSecurityServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminSecurityServerApplication.class, args); + } +} +``` + +* Æô¶¯eureka-server£¬admin-security-server£¬·ÃÎÊSpring Boot Admin Ö÷Ò³·¢ÏÖÐèÒªµÇ¼²ÅÄÜ·ÃÎÊ£º[http://localhost:9301](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9301 "http://localhost:9301") + +![image-20230531001242361](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230531001242361.png) + + + + + +## ʹÓõ½µÄÄ£¿é + + + + + +``` +springcloud-learning +©À©¤©¤ eureka-server -- eureka×¢²áÖÐÐÄ +©À©¤©¤ admin-server -- admin¼à¿ØÖÐÐÄ·þÎñ +©À©¤©¤ admin-client -- admin¼à¿ØÖÐÐÄ¼à¿ØµÄÓ¦Ó÷þÎñ +©¸©¤©¤ admin-security-server -- ´øµÇ¼ÈÏÖ¤µÄadmin¼à¿ØÖÐÐÄ·þÎñ` +``` \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\344\272\213\345\212\241\346\266\210\346\201\257\357\274\210\346\234\200\347\273\210\344\270\200\350\207\264\346\200\247\357\274\211.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\237\272\346\234\254\346\246\202\345\277\265.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\345\271\277\346\222\255\344\270\216\345\273\266\350\277\237\346\266\210\346\201\257.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\211\271\351\207\217\345\217\221\351\200\201\344\270\216\350\277\207\346\273\244.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\346\266\210\346\201\257\347\232\204\347\224\237\344\272\247\344\270\216\346\266\210\350\264\271.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\347\216\257\345\242\203\346\220\255\345\273\272.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" new file mode 100644 index 0000000..7b22bce --- /dev/null +++ "b/docs/mq/RocketMQ/RocketMQ\347\263\273\345\210\227\357\274\232\351\241\272\345\272\217\346\266\210\350\264\271.md" @@ -0,0 +1,62 @@ +# [RocketMQϵÁУ¨Ò»£©»ù±¾¸ÅÄî](https://www.cnblogs.com/boboooo/p/13038950.html) + + + + + +RocketMQÊǰ¢Àï³öÆ·µÄÒ»¿î¿ªÔ´µÄÏûÏ¢Öмä¼þ£¬ÈÃÆäÉùÃû´óÔëµÄ¾ÍÊÇËüµÄÊÂÎñÏûÏ¢µÄ¹¦ÄÜ¡£ÔÚÆóÒµÖУ¬ÏûÏ¢Öмä¼þÑ¡ÔñʹÓÃRocketMQµÄ»¹ÊÇͦ¶àµÄ£¬ÕâһϵÁеÄÎÄÕ¶¼ÊÇÕë¶ÔRocketMQµÄ£¬ÔÛÃÇÏÈ´ÓRocketMQµÄһЩ»ù±¾¸ÅÄîºÍ»·¾³µÄ´î½¨¿ªÊ¼ÁÄÆð¡£ + +RocketMQÓÉ4²¿·Ö×é³É£¬·Ö±ðÊÇ£ºÃû³Æ·þÎñ£¨Name Server£©¡¢ÏûÏ¢¶ÓÁУ¨Brokers£©¡¢Éú²úÕߣ¨producer£©ºÍÏû·ÑÕߣ¨consumer£©¡£Õâ4²¿·Ö¶¼¿ÉÒÔ½øÐÐˮƽÀ©Õ¹£¬´Ó¶ø±ÜÃâµ¥µã¹ÊÕÏ£¬ÈçÏÂͼ£¬ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1191201-20200603173058174-1551688390.png) + +ÕâÊÇRocketMQ¹ÙÍøÉϵÄÒ»ÕÅͼ£¬·Ç³£ÇåÎúµÄÁгöÁË4¸ö²¿·Ö£¬²¢ÇÒ¶¼ÊǼ¯ÈºÄ£Ê½¡£ÏÂÃæÎÒÃǾͷֱð˵һ˵Õâ4²¿·Ö¡£ + +## Ãû³Æ·þÎñ£¨NameServer£© + +Name Server°çÑݵĽÇÉ«ÊÇÒ»¸ö×¢²áÖÐÐÄ£¬ºÍZookeeperµÄ×÷Óò¶à¡£ËüµÄÖ÷Òª¹¦ÄÜÓÐÁ½¸ö£¬ÈçÏ£º + +* brokerµÄ¹ÜÀí£ºbroker¼¯Èº½«×Ô¼ºµÄÐÅÏ¢×¢²áµ½NameServer£¬NameServerÌṩÐÄÌø»úÖÆ¼ì²âÿһ¸öbrokerÊÇ·ñÕý³£¡£ +* ·ÓɹÜÀí£ºÃ¿Ò»¸öNameServer¶¼ÓÐÕû¸öbroker¼¯ÈººÍ¶ÓÁеÄÐÅÏ¢£¬ÒÔ±ã¿Í»§¶Ë£¨Éú²úÕߺÍÏû·ÑÕߣ©²éѯ¡£ + +NameServerЭµ÷×Å·Ö²¼Ê½ÏµÍ³ÖеÄÿһ¸ö×é¼þ£¬²¢ÇÒ¹ÜÀí×Åÿһ¸öTopicµÄ·ÓÉÐÅÏ¢¡£ + +## Broker + +BrokerÖ÷ÒªÊÇ´æ´¢ÏûÏ¢£¬²¢ÇÒÌṩTopicµÄ»úÖÆ¡£ËüÌá¹©ÍÆºÍÀ­Á½ÖÖģʽ£¬»¹ÓÐһЩÈÝÔֵĴëÊ©£¬±ÈÈç¿ÉÒÔÅäÖÃÏûÏ¢¸±±¾¡£ÏÂÃæÎÒÃÇ¿´Ò»¿´BrokcerµÄÖ÷´Ó»úÖÆ¡£ + +BrokerµÄ½ÇÉ«·ÖΪ¡°Òì²½Ö÷¡±¡¢¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Èý¸ö½ÇÉ«¡£Èç¹ûÄã²»ÄÜÈÝÈÌÏûÏ¢µÄ¶ªÊ§£¬Äã¿ÉÒÔÅäÖÃÒ»¸ö¡°Í¬²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öBroker£¬Èç¹ûÄã¾õµÃÏûÏ¢¶ªÊ§Ò²ÎÞËùν£¬Ö»Òª¶ÓÁпÉÓþÍokµÄ»°£¬Äã¿ÉÒÔÅäÖá°Òì²½Ö÷¡±ºÍ¡°´Ó¡±Á½¸öbroker¡£Èç¹ûÄãÖ»ÊÇÏë¼òµ¥µÄ´î½¨£¬Ö»ÅäÖÃÒ»¸ö¡°Òì²½Ö÷¡±£¬²»ÅäÖá°´Ó¡±Ò²ÊÇ¿ÉÒԵġ£ + +ÉÏÃæÌáµ½µÄÊÇbrokerÖ®¼äµÄ±¸·Ý£¬brokerÀïµÄÐÅÏ¢Ò²ÊÇ¿ÉÒÔ±£´æµ½´ÅÅ̵쬱£´æµ½´ÅÅ̵ķ½Ê½Ò²ÓÐÁ½ÖÖ£¬ÍƼöµÄ·½Ê½ÊÇÒì²½±£´æ´ÅÅÌ£¬Í¬²½±£´æ´ÅÅÌÊǷdz£ËðºÄÐÔÄܵġ£ + +## Éú²úÕß + +Éú²úÕßÖ§³Ö¼¯Èº²¿Êð£¬ËüÃÇÏòbroker¼¯Èº·¢ËÍÏûÏ¢£¬¶øÇÒÖ§³Ö¶àÖÖ¸ºÔؾùºâµÄ·½Ê½¡£ + +µ±Éú²úÕßÏòbroker·¢ËÍÏûϢʱ£¬»áµÃµ½·¢Ëͽá¹û£¬·¢Ëͽá¹ûÖÐÓÐÒ»¸ö·¢ËÍ״̬¡£¼ÙÉèÎÒÃǵÄÅäÖÃÖУ¬ÏûÏ¢µÄÅäÖÃ`isWaitStoreMsgOK = true`£¬Õâ¸öÅäÖÃĬÈÏÒ²ÊÇ`true`£¬Èç¹ûÄãÅäÖÃΪ`false`£¬ÔÚ·¢ËÍÏûÏ¢µÄ¹ý³ÌÖУ¬Ö»Òª²»·¢ÉúÒì³££¬·¢Ëͽá¹û¶¼ÊÇ`SEND_OK`¡£µ±`isWaitStoreMsgOK = true`£¬·¢Ëͽá¹ûÓÐÒÔϼ¸ÖÖ£¬ + +* `FLUSH_DISK_TIMEOUT`£º±£´æ´ÅÅ̳¬Ê±£¬µ±±£´æ´ÅÅ̵ķ½Ê½ÉèÖÃΪSYNC_FLUSH£¨Í¬²½£©£¬²¢ÇÒÔÚsyncFlushTimeoutÅäÖõÄʱ¼äÄÚ£¨Ä¬ÈÏ5s£©£¬Ã»ÓÐÍê³É±£´æ´ÅÅ̵͝×÷£¬½«»áµÃµ½Õâ¸ö״̬¡£ +* `FLUSH_SLAVE_TIMEOUT`£ºÍ¬²½¡°´Ó¡±³¬Ê±£¬µ±brokerµÄ½ÇÉ«ÉèÖÃΪ¡°Í¬²½Ö÷¡±Ê±£¬µ«ÊÇÔÚÉèÖõÄͬ²½Ê±¼äÄÚ£¬Ä¬ÈÏΪ5s£¬Ã»ÓÐÍê³ÉÖ÷´ÓÖ®¼äµÄͬ²½£¬¾Í»áµÃµ½Õâ¸ö״̬¡£ +* `SLAVE_NOT_AVAILABLE`£º¡°´Ó¡±²»¿ÉÓ㬵±ÎÒÃÇÉèÖá°Í¬²½Ö÷¡±£¬µ«ÊÇûÓÐÅäÖá°´Ó¡±brokerʱ£¬»á·µ»ØÕâ¸ö״̬¡£ +* `SEND_OK`£ºÏûÏ¢·¢Ëͳɹ¦¡£ + +ÔÙÀ´¿´¿´ÏûÏ¢ÖØ¸´ÓëÏûÏ¢¶ªÊ§£¬µ±Äã·¢ÏÖÄãµÄÏûÏ¢¶ªÊ§Ê±£¬Í¨³£ÓÐÁ½¸öÑ¡Ôñ£¬Ò»¸öÊǶª¾Í¶ª°É£¬ÕâÑùÏûÏ¢¾ÍÕæµÄ¶ªÁË£»ÁíÒ»¸öÑ¡ÔñÊÇÏûÏ¢ÖØÐ·¢ËÍ£¬ÕâÑùÓпÉÄÜÒýÆðÏûÏ¢ÖØ¸´¡£Í¨³£Çé¿öÏ£¬»¹ÊÇÍÆ¼öÖØÐ·¢Ë͵ģ¬ÎÒÃÇÔÚÏû·ÑÏûÏ¢µÄʱºòҪȥ³ýµôÖØ¸´µÄÏûÏ¢¡£ + +·¢ËÍmessageµÄ´óСһ°ã²»³¬¹ý512k£¬Ä¬Èϵķ¢ËÍÏûÏ¢µÄ·½Ê½ÊÇͬ²½µÄ£¬·¢ËÍ·½·¨»áÒ»Ö±×èÈû£¬Ö±µ½µÈµ½·µ»ØµÄÏìÓ¦¡£Èç¹ûÄã±È½ÏÔÚÒâÐÔÄÜ£¬Ò²¿ÉÒÔÓÃ`send(msg, callback)`Òì²½µÄ·½Ê½·¢ËÍÏûÏ¢¡£ + +## Ïû·ÑÕß + +¶à¸öÏû·ÑÕß¿ÉÒÔ×é³É**Ïû·ÑÕß×飨consumer group£©**£¬²»Í¬µÄ**Ïû·ÑÕß×é**¿ÉÒÔ¶©ÔÄÏàͬµÄTopic£¬Ò²¿ÉÒÔ¶ÀÁ¢µÄÏû·ÑTopic£¬Ã¿Ò»¸öÏû·ÑÕß×é¶¼ÓÐ×Ô¼ºµÄÏû·ÑÆ«ÒÆÁ¿¡£ + +ÏûÏ¢µÄÏû·Ñ·½Ê½Ò»°ãÓÐÁ½ÖÖ£¬Ë³ÐòÏû·ÑºÍ²¢·¢Ïû·Ñ¡£ + +* ˳ÐòÏû·Ñ£ºÏû·ÑÕß½«Ëø×¡ÏûÏ¢¶ÓÁУ¬È·±£ÏûÏ¢°´ÕÕ˳ÐòÒ»¸öÒ»¸öµÄ±»Ïû·Ñµô£¬Ë³ÐòÏû·Ñ»áÒýÆðÒ»²¿·ÖÐÔÄÜËðʧ¡£ÔÚÏû·ÑÏûÏ¢µÄʱºò£¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇÓ¦¸Ã·µ»Ø`SUSPEND_CURRENT_QUEUE_A_MOMENT`Õâ¸ö״̬£¬Ëü½«¸æËßÏû·ÑÕß¹ýÒ»¶Îʱ¼äºó£¬»áÖØÐÂÏû·ÑÕâ¸öÏûÏ¢¡£ +* ²¢·¢Ïû·Ñ£ºÏû·ÑÕß½«²¢·¢µÄÏû·ÑÏûÏ¢£¬ÕâÖÖ·½Ê½µÄÐÔÄܷdz£ºÃ£¬Ò²ÊÇÍÆ¼öµÄÏû·Ñ·½Ê½¡£ÔÚÏû·ÑµÄ¹ý³ÌÖУ¬Èç¹û³öÏÖÒì³££¬²»½¨ÒéÖ±½ÓÅ׳ö£¬¶øÊÇ·µ»Ø`RECONSUME_LATER`״̬£¬Ëü¸æËßÏû·ÑÕßÏÖÔÚ²»ÄÜÕýÈ·µÄÏû·ÑËü£¬¹ýÒ»¶Îʱ¼äºó£¬»áÔÙ´ÎÏû·ÑËü¡£ + +ÔÚÏû·ÑÕßÄÚ²¿£¬ÊÇʹÓÃ`ThreadPoolExecutor`×÷ΪÏ̳߳صģ¬ÎÒÃÇ¿ÉÒÔͨ¹ý`setConsumeThreadMin`ºÍ`setConsumeThreadMax`ÉèÖÃ×îСÏû·ÑÏ̺߳Í×î´óÏû·ÑÏ̡߳£ + +µ±Ò»¸öеÄÏû·ÑÕß×齨Á¢ÒÔºó£¬ËüÒª¾ö¶¨ÊÇ·ñÏû·Ñ֮ǰµÄÀúÊ·ÏûÏ¢£¬`CONSUME_FROM_LAST_OFFSET`½«ºöÂÔÀúÊ·ÏûÏ¢£¬Ïû·ÑеÄÏûÏ¢¡£`CONSUME_FROM_FIRST_OFFSET`½«Ïû·Ñ¶ÓÁÐÖеÄÿһ¸öÏûÏ¢£¬Ö®Ç°µÄÀúÊ·ÏûÏ¢Ò²»áÔÙÏû·ÑÒ»±é¡£`CONSUME_FROM_TIMESTAMP`¿ÉÒÔÖ¸¶¨Ïû·ÑÏûÏ¢µÄʱ¼ä£¬Ö¸¶¨Ê±¼äÒÔºóµÄÏûÏ¢»á±»Ïû·Ñ¡£ + +Èç¹ûÄãµÄÓ¦Óò»ÄÜÈÝÈÌÖØ¸´Ïû·Ñ£¬ÄÇôÔÚÏû·ÑÏûÏ¢µÄ¹ý³ÌÖУ¬Òª×öºÃÏûÏ¢µÄУÑé¡£ + +ºÃÁË£¬½ñÌì¾Íµ½ÕâÀï°É£¬ÏÂһƪÎÒÃǽ«½éÉÜRocketMQµÄ»·¾³´î½¨¡£ \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" new file mode 100644 index 0000000..b4bc0a9 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka \345\277\253\351\200\237\344\270\212\346\211\213\357\274\210Java\347\211\210\357\274\211.md" @@ -0,0 +1,173 @@ +**å‰�言** + +上篇文章讲解了 Kafka 的基础概念和架构,了解了基本概念之å�Žï¼Œå¿…须得实践一波了,所谓“实践æ‰�是检验真ç�†çš„唯一办法â€�,å�Žç»­ç³»åˆ—关于 Kafka 的文章都以 kafka_2.11-0.9.0.0 为例;å�¦å¤–为了让大家快速入门,本文å�ªæ��ä¾›å�•机版的安装实战教程,如果有想å°�试集群方案的,å�Žé�¢åœ¨å‡ºä¸€ç¯‡é›†ç¾¤å®‰è£…的教程,废è¯�ä¸�多说了,直接开干。 + +## **安装** + +### **1\. 下载** + +版本å�·ï¼škafka_2.11-0.9.0.0 + +下载地å�€ï¼š[http://kafka.apache.org/downloads](https://link.zhihu.com/?target=http%3A//kafka.apache.org/downloads) + +### **2\. 安装** + + + +``` +# 安装目录 +$ pwd +/Users/my/software/study + +# å‡�压 +$ sudo tar -zxvf kafka_2.11-0.9.0.0.tgz + +# é‡�命å�� +$ sudo mv kafka_2.11-0.9.0.0.tgz kafka-0.9 + +# 查看目录结构 +$ cd kafka-0.9 && ls +LICENSE NOTICE bin config libs site-docs + +# 目录结构介ç»�: +# bin: 存放kafka 客户端和æœ�务端的执行脚本 +# config: 存放kafka的一些é…�置文件 +# libs: 存放kafkaè¿�行的的jar包 +# site-docs: 存放kafkaçš„é…�置文档说明 + +# é…�置环境å�˜é‡�,方便在任æ„�目录下è¿�行kafka命令 +# å�šä¸»ä½¿ç”¨çš„Mac,所以é…�置在了 ~/.bash_profile文件中, +# Linux中则é…�置在 ~/.bashrc 或者 ~/.zshrc文件中 +$ vim ~/.bash_profile + +export KAFKA_HOME=/Users/haikuan1/software/study/kafka-0.9 +export PATH=$PATH:$JAVA_HOME:$KAFKA_HOME/bin + +# 使得环境å�˜é‡�生效 +$ source ~/.bash_profile + +``` + + + +### **3.è¿�行** + +### **3.1 å�¯åЍ zookeeper** + + + +``` +# å�¯åЍzookeeper,因为kafka的元数æ�®éœ€è¦�ä¿�存到zookeeper中 +$ bin/zookeeper-server-start.sh config/zookeeper.properties + +# 若出现如下信æ�¯ï¼Œåˆ™è¯�明zookeeperå�¯åЍæˆ�功了 +[2020-04-25 16:23:44,493] INFO Server environment:user.dir=/Users/haikuan1/software/study/kafka-0.10 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO tickTime set to 3000 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO minSessionTimeout set to -1 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,505] INFO maxSessionTimeout set to -1 (org.apache.zookeeper.server.ZooKeeperServer) +[2020-04-25 16:23:44,548] INFO binding to port 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.NIOServerCnxnFactory) + +``` + + + +### **3.2 å�¯åЍ Kafka server** + + + +``` +# 以守护进程的方å¼�å�¯åЍkafkaæœ�务端,去掉 -daemon å�‚数则关闭当å‰�窗å�£æœ�务端自动退出 +$ bin/kafka-server-start.sh -daemon config/server.properties + +``` + + + +### **3.3 kafka 基础命令使用** + + + +``` +# 1\. 创建一个topic +# --replication-factor:指定副本个数 +# --partition:指定partition个数 +# --topic:指定topicçš„å��å­— +$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partition 1 --topic mytopic + +# 2\. 查看创建æˆ�功的topic +$ kafka-topics.sh --list --zookeeper localhost:2181 + +# 3\. 创建生产者和消费者 + +# 3.1 å�¯åЍkafka消费端 +# --from-beginning:从头开始消费,该特性也表明kafka消æ�¯å…·æœ‰æŒ�久性 +$ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic mytopic --from-beginning + +# 3.2 å�¯åЍkafka生产端 +# --broker-list:当å‰�çš„Broker列表,å�³æ��ä¾›æœ�务的列表 +$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic mytopic + +``` + + + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a1e0c6db02c2822b2ad88db1c3b0b8a7_720w.webp) + +
+ +### **4.使用 Java 连接 kafka 进行测试** + +### **4.1 创建一个 maven 工程,引入如下 pom �赖** + + + +``` + + org.apache.kafka + kafka-clients + 0.9.0.0 + + + + org.apache.kafka + kafka_2.11 + 0.9.0.0 + + +``` + + + +### **4.2 消费者端代�** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-5e9876ca0dc733fe8c2df51d2e42d1ce_720w.webp) + +
+ +### **4.3 生产者端代�** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d1e6bfdf23c2b42e23f30d4430c587e2_720w.webp) + +
+ +### **4.4 消费者端效果图** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1912f5b2b12ac766d746d88a04b9bd28_720w.webp) + +
+ +### **5.总结** + +本文介�了 kafka �机版安装�简�命令使用,然�使用 Java 实现了生产者和消费者的简�功能,虽然内容�能比较简�,但还是**强烈建议大家手动去实践一下**,从而对 kafka 的架构有一个更深入的�解。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" new file mode 100644 index 0000000..6af0292 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\270\200\346\235\241\346\266\210\346\201\257\345\255\230\345\210\260broker\347\232\204\350\277\207\347\250\213.md" @@ -0,0 +1,112 @@ +**�言** + +�过上篇文章的简�实战之�,**今天���生产者将消�从客户端��到 Broker 上背��生了哪些故事**,看�看由你,但是我���以本篇文章你一定�以学到应用背�的一些实质东西。 + +本文我们从以下 4 个方��探讨下一�消�如何被准确的��到 Broker 的 partition 上。 + +**1\. 客户端组件** + +**2\. 客户端缓存存储模型** + +**3\. 确定消�的 partition �置** + +**4\. ��线程的工作原�** + +* * * + +## **客户端组件** + +* **KafkaProducer:** + +KafkaProducer 是一个生产者客户端的进程,通过该对象�动生产者���消�。 + +* **RecordAccumulator:** + +RecordAccumulator 是一个记录收集器,用于收集客户端��的消�,并将收集到的消�暂存到客户端缓存中。 + +* **Sender:** + +Sender 是一个��线程,负责读�记录收集器中缓存的批�消�,�过一些中间转��作,将���的数�准备好,然�交由 Selector 进行网络传输。 + +* **Selector:** + +Selector 是一个选择器,用于处�网络连接和读写处�,使用网络连接处�客户端上的网络请求。 + +通过使用以上四大组件��完�客户端消�的��工作。消�在网络中传输的方��能通过二级制的方�,所以首先需�将消��列化为二进制形�缓存在客户端,kafka 使用了�端队列的方�将消�缓存起�,然�使用��线程(Sender)读�队列中的消�交给 Selector 进行网络传输��给�务端(Broker) + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-7d57acd1d7dc5942e999e6ffebb28679_720w.webp) + +
+ +以上为��消�的主�程,附上部分��供大家�考,接下�分�下几个�常���程的具体实现原�。 + +* * * + +## **客户端缓存存储模型** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-5da65c5f9f8c0c9082e07c6431e78cd2_720w.webp) + +
+ +从上图�以看出,一�消�首先需�确定�被存储到那个 partition 对应的�端队列上;其次,存储消�的�端队列是以批的维度存储的,� N �消�组�一批,一批消�最多存储 N �,超过�则新建一个组�存储新消�;其次,新�的消�总是从左侧写入,�越�左侧的消�产生的时间越晚;最�,�有当一批消�凑够 N ���会��给 Broker,�则�会��到 Broker 上。 + +了解了客户端存储模型�,�探讨下确定消�的 partition(分区)�置? + +* * * + +## **确定消�的 partition �置** + +消��分为两�,一�是指定了 key 的消�,一�是没有指定 key 的消�。 + +对于指定了 key 的消�,partition �置的计算方�为:**`Utils.murmur2(key) % numPartitions`**,�先对 key 进行哈希计算,然�在于 partition 个数求余,从而得到该�消�应该被存储在哪个 partition 上。 + +对于没有指定 key 的消�,partition �置的计算方�为:**采用 round-robin 方�确定 partition �置**,�采用轮询的方�,平�的将消�分布到��的 partition 上,从而���些 partition 数��过大影� Broker 和消费端性能。 + +### **注�** + +由于 partition 有主副的区分,此处�与计算的 partition 数�是当�有主 partition 的数�,�如果�个 partition 无主的时候,则此 partition 是�能够进行数�写入的。 + +�微解释一下,主副 partition 的机制是为了�高 kafka 系统的容错性的,�当�个 Broker �外宕机时,在此 Broker 上的主 partition 状�为��读写时(�有主 partition �对外�供读写�务,副 partition �有数�备份的功能),kafka 会从主 partition 对应的 N 个副 partition 中挑选一个,并将其状�改为主 partition,从而继续对外�供读写�作。 + +消�被确定分�到�个 partition 对应记录收集器(��端队列)�,接下�,��线程(Sender)从记录收集器中收集满足�件的批数���给 Broker,那么��线程是如何收集满足�件的批数�的?批数�是按照 partition 维度��的还是按照 Broker 维度��数�的? + +* * * + +## **��线程的工作原�** + +Sender 线程的主�工作是收集满足�件的批数�,何为满足�件的批数�?缓存数�是以批维度存储的,当一批数��达到指定的 N �时,就满足��给 Broker 的�件了。 + +partition 维度和 Broker 维度��消�模型对比。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-36b7c2761f17fb2d6481747523999011_720w.webp) + +
+ +从图中�以看出,左侧按照 partition 维度��消�,�个 partition 都需�和 Broker 建连,总共�生了四次网络连接。而�侧将分布在�一个 Broker 的 partition 按组���在与 Broker 建连,�需�两次网络连接��。所以 Kafka 选择�侧的方�。 + +### **Sender 的主�工作** + +第一步:扫�记录收集器中满足�件的批数�,然�将 partition -> 批数�映射转�� BrokerId -> N 批数�的映射。第二步:Sender 线程会为�个 BrokerId 创建一个客户端请求,然�将请求交给 NetWorkClient,由 NetWrokClient 去真正��网络请求到 Broker。 + +### **NetWorkClient 的工作内容** + +Sender 线程准备好���的数��,交由 NetWorkClient �进行网络相关�作。主�包括客户端与�务端的建连���客户端请求�接��务端�应。完�如上一系列的工作主�由如下方法完�。 + +1. reday()方法。从记录收集器获�准备完毕的节点,并连接所有准备好的节点。 +2. send()方法。为�个节点创建一个客户端请求,然�将请求暂时存到节点对应的 Channel(通�)中。 +3. poll()方法。该方法会真正轮询网络请求,��请求给�务端节点和接��务端的�应。 + +* * * + +## **总结** + +以上,�为生产者客户端的一�消�从生产到��到 Broker 上的全过程。现在是�是就很清晰了呢?也许有些朋�会比较疑惑它的**网络请求模型是什么样的**,作者就猜你会你会问,下一篇我们就�扒开它的神秘�纱看看其究竟是怎么实现的,敬请期待。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" new file mode 100644 index 0000000..bb4fd3b --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\344\273\213\347\273\215.md" @@ -0,0 +1,122 @@ +## 一.官网Kafka介� + +## 1.1 什么是事件�? + +事件�是相当于人体中枢神�系统的数字系统。它是“永远在线�世界的技术基础,在这个世界里,�业越�越多地由软件定义和自动化,软件的用户也更多地是软件。 + +从技术上讲,事件�是指以事件�的形�从数�库�传感器�移动设备�云�务和软件应用等事件�实时�获数�的实践;�久地存储这些事件�以备以�检索;实时和回顾性地�作�处�和�应事件�;并根�需�将事件�路由到��的目的地技术。因此,事件�确�了数�的连续�动和解释,从而使正确的信�在正确的地点�正确的时间出现。 + +## 1.2 我�以使用事件��什么? + +事件�应用于众多行业和组织的��用例。它的许多例�包括: 1. 实时处�支付和金�交易,如股票交易所�银行和�险。 2. 实时跟踪和监控汽车��车�车队和�输,如物�和汽车行业。 3. �续�获和分��自物�网设备或其他设备的传感器数�,如工厂和风电场。 4. 收集并立��应客户的互动和订�,如零售�酒店和旅游行业,以�移动应用程�。 5. 监测�院病人,预测病情�化,确�在紧急情况下�时治疗。 6. 连接�存储公���部门产生的数�并使其�用。 7. 作为数�平��事件驱动架构和微�务的基础。 + +## 1.3 Apache Kafka®是一个事件�平�。这是什么��? + +Kafka结�了三个关键的功能,所以你�以用一个�一的战斗测试解决方案�实现端到端事件�的用例: 1. �布(写)和订阅(读)事件�,包括从其他系统连续导入/导出数�。 2. �久性和��地存储事件�,��你想。 3. 在事件�生或回顾时处�事件�。 + +所有这些功能都是以分布��高度�伸缩�弹性�容错和安全的方��供的。Kafka�以部署在裸金属硬件�虚拟机和容器上,也�以部署在云上。您�以选择自管�您的Kafka环境和使用由��供应商�供的完全管�的�务。 + +## 1.4 简而言之,Kafka是如何工作的? + +Kafka是一个分布�系统,由�务器和客户端组�,通过高性能的TCP网络�议进行通信。它�以部署在裸金属硬件�虚拟机和内部环境中的容器上,也�以部署在云环境中。 + +**�务器:**Kafka作为一个集群�行一个或多个�务器,�以跨越多个数�中心或云区域。其中一些�务器构�存储层,称为代�。其他�务器�行Kafka Connect��续导入和导出数�作为事件�,将Kafka与您现有的系统集�,如关系数�库以�其他Kafka集群。为了让你实现关键任务的用例,Kafka集群具有高度的�扩展性和容错性:如果它的任何一个�务器�生故障,其他�务器将接管它们的工作,以确��续的�作而�丢失任何数�。 + +**客户机:**它们�许您编写分布�应用程�和微�务,这些应用程�和微�务�以并行地�大规模地读��写入和处�事件�,甚至在出现网络问题或机器故障的情况下也�以容错。Kafka附带了一些这样的客户端,这些客户端被Kafka社区�供的几�个客户端增强了:客户端�以用于Java和Scala,包括更高级别的Kafka Streams库,用于Go�Python�C/ c++和许多其他编程语言以�REST api。 + +## 1.5 主�概念和术语 + +事件记录了在世界上或你的�业中“�生了�事�的事实。在文档中也称为记录或消�。当你读或写数�到Kafka时,你以事件的形��这件事。从概念上讲,事件具有键�值�时间戳和�选的元数�头。下�是一个例�: 1. 活动�点:“爱丽�� 2. 事件值:“�Bob支付200美元� 3. 事件时间戳:“2020年6月25日下�2:06。� + +生产者是那些�Kafka�布(写)事件的客户端应用程�,而消费者是那些订阅(读和处�)这些事件的应用程�。在Kafka中,生产者和消费者是完全解耦的,彼此是��知的,这是实现Kafka闻�的高�扩展性的一个关键设计元素。例如,生产者从�需�等待消费者。Kafka�供了���样的��,比如精确处�一次事件的能力。 + +事件被组织并�久地存储在主题中。很简�,一个主题类似于文件系统中的一个文件夹,事件就是该文件夹中的文件。一个示例主题�称�以是“payments�。Kafka中的主题总是多生产者和多订阅者:一个主题�以有0个�1个或多个生产者�它写入事件,也�以有0个�1个或多个消费者订阅这些事件。主题中的事件�以根�需��常读�——与传统消�传递系统��,事件在使用��会删除。相�,你�以通过�个主题的�置设置�定义Kafka应该�留你的事件多长时间,之�旧的事件将被丢弃。Kafka的性能相对于数�大��说是��的,所以长时间存储数�是完全�以的。 + +主题是分区的,这�味�一个主题分散在�于��Kafka broker上的多个“桶�上。这�数�的分布��置对于�伸缩性�常��,因为它�许客户机应用程��时从/�多个代�读�和写入数�。当一个新事件被�布到一个主题时,它实际上被附加到主题的一个分区中。具有相�事件键(例如,客户或车辆ID)的事件被写入�一个分区,Kafka��任何给定主题分区的消费者都将始终以写入的完全相�的顺�读�该分区的事件。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-39059344afb24ff7436bf7fb06bddde4_720w.webp) + +
+ +让你的数�容错和�用性,�一个主题�以被�制,甚至跨geo-regions或数�中心,这样总有多个�纪人有一份数�以防出错,你想�代�维护,等等。一个常�的生产设置是�制因�3,也就是说,您的数�总是有三个副本。这个�制是在主题分区级别执行的。 + +## 二.Kafka简介 + +Kafka是一�消�队列,主�用�处�大�数�状�下的消�队列,一般用��日志的处�。既然是消�队列,那么Kafka也就拥有消�队列的相应的特性了。 + +**消�队列的好处** 1. 解耦� 耦�的状�表示当你实现�个功能的时候,是直接接入当�接�,而利用消�队列,�以将相应的消���到消�队列,这样的�,如果接�出了问题,将�会影�到当�的功能。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-e37a18ea7eddc69582d634cc881eb257_720w.webp) + +
+ +1. 异步处� 异步处�替代了之�的�步处�,异步处��需�让�程走完就返回结果,�以将消���到消�队列中,然�返回结果,剩下让其他业务处�接�从消�队列中拉�消费处���。 + +2. ��削峰 高��的时候,使用消�队列作为中间件�以将��的高峰�存在消�队列中,从而防止了系统的高请求,�轻�务器的请求处�压力。 + +## 2.1 Kafka消费模� + +Kafka的消费模�主�有两�:一�是一对一的消费,也�点对点的通信,�一个��一个接收。第二�为一对多的消费,�一个消���到消�队列,消费者根�消�队列的订阅拉�消�消费。 + +**一对一** + +
+ + +![image-20230525200024084](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230525200024084.png) + +
+ +消�生产者�布消�到Queue队列中,通知消费者从队列中拉�消�进行消费。消�被消费之�则删除,Queue支�多个消费者,但对于一�消�而言,�有一个消费者�以消费,�一�消��能被一个消费者消费。 + +**一对多** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d97a2898f3bdc417262bc88be616281c_720w.webp) + +
+ +这�模�也称为�布/订阅模�,�利用Topic存储消�,消�生产者将消��布到Topic中,�时有多个消费者订阅此topic,消费者�以从中消费消�,注��布到Topic中的消�会被多个消费者消费,消费者消费数�之�,数��会被清除,Kafka会默认�留一段时间,然��删除。 + +## 2.2 Kafka的基础架构 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-ef94691300117c049301d88c6337c9c2_720w.webp) + +
+ +Kafka�其他Mq一样,也有自己的基础架构,主�存在生产者Producer�Kafka集群Broker�消费者Consumer�注册中心Zookeeper. + +1. Producer:消�生产者,�Kafka中�布消�的角色。 + +2. Consumer:消�消费者,�从Kafka中拉�消�消费的客户端。 + +3. Consumer Group:消费者组,消费者组则是一组中存在多个消费者,消费者消费Broker中当�Topic的��分区中的消�,消费者组之间互�影�,所有的消费者都属于�个消费者组,�消费者组是逻辑上的一个订阅者。�一个分区中的消��能够一个消费者组中的一个消费者所消费 + +4. Broker:�纪人,一�Kafka�务器就是一个Broker,一个集群由多个Broker组�,一个Broker�以容纳多个Topic。 + +5. Topic:主题,�以�解为一个队列,生产者和消费者都是��一个Topic + +6. Partition:分区,为了实现扩展性,一个�常大的Topic�以分布到多个Broker上,一个Topic�以分为多个Partition,�个Partition是一个有�的队列(分区有�,�能��全局有�) + +7. Replica:副本Replication,为��集群中�个节点�生故障,节点上的Partition数��丢失,Kafka�以正常的工作,Kafka�供了副本机制,一个Topic的�个分区有若干个副本,一个Leader和多个Follower + +8. Leader:�个分区多个副本的主角色,生产者��数�的对象,以�消费者消费数�的对象都是Leader。 + +9. Follower:�个分区多个副本的从角色,实时的从Leader中�步数�,��和Leader数�的�步,Leader�生故障的时候,�个Follower会�为新的Leader。 + +上述一个Topic会产生多个分区Partition,分区中分为Leader和Follower,消�一般��到Leader,Follower通过数�的�步与Leader���步,消费的�也是在Leader中�生消费,如果多个消费者,则分别消费Leader和�个Follower中的消�,当Leader�生故障的时候,�个Follower会�为主节点,此时会对�消�的�移�。 + +## �考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" new file mode 100644 index 0000000..58632b3 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\216\237\347\220\206\345\210\206\346\236\220\346\200\273\347\273\223\347\257\207.md" @@ -0,0 +1,636 @@ +## 一�概述 + +Kakfa起�是由LinkedIn公�开�的一个分布�的消�系统,��为Apache的一部分,它使用Scala编写,以�水平扩展和高��率而被广泛使用。目�越�越多的开�分布�处�系统如Cloudera�Apache Storm�Spark等都支�与Kafka集�。 + +Kafka凭借�自身的优势,越�越�到互�网�业的��,唯�会也采用Kafka作为其内部核心消�引擎之一。Kafka作为一个商业级消�中间件,消���性的��性�想而知。如何确�消�的精确传输?如何确�消�的准确存储?如何确�消�的正确消费?这些都是需�考虑的问题。本文首先从Kafka的架构�手,先了解下Kafka的基本原�,然�通过对kakfa的存储机制��制原���步原����性和�久性��等等一步步对其��性进行分�,最�通过benchmark�增强对Kafka高��性的认知。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 二�Kafka的使用场景 + +(1)日志收集:一个公��以用Kafka�以收集���务的log,通过kafka以统一接��务的方�开放给��consumer,例如Hadoop�Hbase�Solr等; + +(2)消�系统:解耦和生产者和消费者�缓存消�等; + +(3)用户活动跟踪:Kafka�常被用�记录web用户或者app用户的��活动,如�览网页��索�点击等活动,这些活动信�被�个�务器�布到kafka的topic中,然�订阅者通过订阅这些topic��实时的监控分�,或者装载到Hadoop�数�仓库中�离线分�和挖掘; + +(4)��指标:Kafka也�常用�记录��监控数�。包括收集��分布�应用的数�,生产���作的集中�馈,比如报警和报告; + +(5)��处�:比如spark streaming和storm; + +(6)事件�; + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 三�Kafka基本架构 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201215942487-1393117307.png) + +如上图所示,一个典型的Kafka体系架构包括: + +* 若干Producer(�以是�务器日志,业务数�,页��端产生的page view等等), +* 若干broker(Kafka支�水平扩展,一般broker数�越多,集群��率越高), +* 若干Consumer (Group),以�一个Zookeeper集群。 + +Kafka通过Zookeeper管�集群�置,选举leader,以�在consumer group�生�化时进行rebalance。Producer使用push(推)模�将消��布到broker,Consumer使用pull(拉)模�从broker订阅并消费消�。 + + + +### 1�Topic & Partition + +一个topic�以认为一个一类消�,�个topic将被分�多个partition,�个partition在存储层�是append log文件。任何�布到此partition的消�都会被追加到log文件的尾部,��消�在文件中的�置称为offset(�移�),offset为一个long型的数字,它唯一标记一�消�。��消�都被append到partition中,是顺�写�盘,因此效率�常高(�验�,顺�写�盘效率比�机写内存还�高,这是Kafka高��率的一个很��的��)。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201220820060-2075971944.png) + +�一�消�被��到broker中,会根�partition规则选择被存储到哪一个partition。partition机制�以通过指定producer的partition.class这一�数�指定,该class必须实现kafka.producer.Partitioner接�。如果partition规则设置的��,所有消��以�匀分布到��的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会�为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时�以在$KAFKA_HOME/config/server.properties中指定这个partition的数�(如下所示),当然�以在topic创建之�去修改partition的数�。 + + + +
# The default number of log partitions per topic. More partitions allow greater
+# parallelism for consumption, but this will also result in more files across
+# the brokers.
+#默认partitions数�
+num.partitions=1
+ + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 四�高��性存储分�概述 + +Kafka的高��性的�障��于其�壮的副本(replication)策略。通过调节其副本相关�数,�以使得Kafka在性能和��性之间�转的游刃有余。Kafka从0.8.x版本开始�供partition级别的�制,replication的数��以在$KAFKA_HOME/config/server.properties中�置(default.replication.refactor)。 + +这里先从Kafka文件存储机制入手,从最底层了解Kafka的存储细节,进而对其的存储有个微观的认知。之�通过Kafka�制原�和�步方���述�观层�的概念。最�从ISR,HW,leader选举以�数���性和�久性��等等�个维度�丰富对Kafka相关知识点的认知。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 五�Kafka文件存储机制 + +Kafka中消�是以topic进行分类的,生产者通过topic�Kafka broker��消�,消费者通过topic读�数�。然而topic在物�层��能以partition为分组,一个topic�以分�若干个partition,那么topic以�partition�是怎么存储的呢?partition还�以细分为segment,一个partition物�上由多个segment组�,那么这些segment�是什么呢?下�我们�一一�晓。 + +为了便于说明问题,�设这里�有一个Kafka集群,且这个集群�有一个Kafka broker,��有一�物�机。在这个Kafka broker中�置($KAFKA_HOME/config/server.properties中)log.dirs=/tmp/kafka-logs,以此�设置Kafka消�文件存储目录,与此�时创建一个topic:topic_zzh_test,partition的数�为4($KAFKA_HOME/bin/kafka-topics.sh –create –zookeeper localhost:2181 –partitions 4 –topic topic_vms_test –replication-factor 4)。那么我们此时�以在/tmp/kafka-logs目录中�以看到生�了4个目录: + + + + + +
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-0 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-1 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-2 
+drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-3  
+ + + + +在Kafka文件存储中,�一个topic下有多个��的partition,�个partiton为一个目录,partition的�称规则为:topic�称+有���,第一个��从0开始计,最大的��为partition数��1,partition是实际物�上的概念,而topic是逻辑上的概念。 + +上��到partition还�以细分为segment,这个segment�是什么?如果就以partition为最�存储��,我们�以想象当Kafka producer�断��消�,必然会引起partition文件的无�扩张,这样对于消�文件的维护以�已�被消费的消�的清�带�严�的影�,所以这里以segment为���将partition细分。�个partition(目录)相当于一个巨型文件被平�分�到多个大�相等的segment(段)数�文件中(�个segment 文件中消�数��一定相等)这�特性也方便old segment的删除,�方便已被消费的消�的清�,�高�盘的利用率。�个partition�需�支�顺�读写就行,segment的文件生命周期由�务端�置�数(log.segment.bytes,log.roll.{ms,hours}等若干�数)决定。 + + + + + +[![�制代�](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "�制代�") + +
 #在强制刷新数�到�盘�许接收消�的数�
+#log.flush.interval.messages=10000 # 在强制刷新之�,消��以在日志中�留的最长时间
+#log.flush.interval.ms=1000 #一个日志的最�存活时间,�以被删除
+log.retention.hours=168 #  一个基于大�的日志�留策略。段将被从日志中删除��剩下的部分段�低于log.retention.bytes。
+#log.retention.bytes=1073741824 #  �一个日志段大�的最大值。当到达这个大�时,会生�一个新的片段。
+log.segment.bytes=1073741824 # 检查日志段的时间间隔,看是��以根��留策略删除它们
+log.retention.check.interval.ms=300000
+ + +[![�制代�](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "�制代�") + + + +segment文件由两部分组�,分别为“.index�文件和“.log�文件,分别表示为segment索引文件和数�文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,�续�个segment文件�为上一个segment文件最�一�消�的offset值,数值大�为64�,20�数字字符长度,没有数字用0填充,如下: + + + + + + + +[![�制代�](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "�制代�") + +
00000000000000000000.index 00000000000000000000.log 00000000000000170410.index 00000000000000170410.log 00000000000000239430.index 00000000000000239430.log  
+ +[![�制代�](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "�制代�") + + + +以上�的segment文件为例,展示出segment:00000000000000170410的“.index�文件和“.log�文件的对应的关系,如下图: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201224133022-2085407889.png) + + + + + +如上图,“.index�索引文件存储大�的元数�,“.log�数�文件存储大�的消�,索引文件中的元数�指�对应数�文件中message的物��移地�。其中以“.index�索引文件中的元数�[3, 348]为例,在“.log�数�文件表示第3个消�,�在全局partition中表示170410+3=170413个消�,该消�的物��移地�为348。 + +那么如何从partition中通过offset查找message呢? + +以上图为例,读�offset=170418的消�,首先查找segment文件,其中00000000000000000000.index为最开始的文件,第二个文件为00000000000000170410.index(起始�移为170410+1=170411),而第三个文件为00000000000000239430.index(起始�移为239430+1=239431),所以这个offset=170418就�到了第二个文件之中。其他�续文件�以�次类推,以其实�移�命�并排列这些文件,然�根�二分查找法就�以快速定�到具体文件�置。其次根�00000000000000170410.index文件中的[8,1325]定�到00000000000000170410.log文件中的1325的�置进行读�。 + +�是读�offset=170418的消�,从00000000000000170410.log文件中的1325的�置进行读�,那么怎么知�何时读完本�消�,�则就读到下一�消�的内容了? + +这个就需��系到消�的物�结构了,消�都具有固定的物�结构,包括:offset(8 Bytes)�消�体的大�(4 Bytes)�crc32(4 Bytes)�magic(1 Byte)�attributes(1 Byte)�key length(4 Bytes)�key(K Bytes)�payload(N Bytes)等等字段,�以确定一�消�的大�,�读�到哪里截止。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 六��制原�和�步方� + +Kafka中topic的�个partition有一个预写�的日志文件,虽然partition�以继续细分为若干个segment文件,但是对于上层应用�说�以将partition看�最�的存储�元(一个有多个segment文件拼接的“巨型�文件),�个partition都由一些列有�的����的消�组�,这些消�被连续的追加到partition中。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201231403669-958736996.png) + +上图中有两个新��:HW和LEO。这里先介�下LEO,LogEndOffset的缩写,表示�个partition的log最�一�Message的�置。HW是HighWatermark的缩写,是指consumer能够看到的此partition的�置,这个涉�到多副本的概念,这里先��一下,下节�详表。 + +言归正传,为了�高消�的��性,Kafka�个topic的partition有N个副本(replicas),其中N(大于等于1)是topic的�制因�(replica fator)的个数。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个broker失效情况下�然���务�用。在Kafka中�生�制时确�partition的日志能有�地写到其他节点上,N个replicas中,其中一个replica为leader,其他都为follower, leader处�partition的所有读写请求,与此�时,follower会被动定期地去�制leader上的数�。 + +如下图所示,Kafka集群中有4个broker, �topic有3个partition,且�制因��副本个数也为3: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181201231724531-2038730622.png) + +Kafka�供了数��制算法��,如果leader�生故障或挂掉,一个新leader被选举并被接�客户端的消��功写入。Kafka确�从�步副本列表中选举一个副本为leader,或者说follower追赶leader数�。leader负责维护和跟踪ISR(In-Sync Replicas的缩写,表示副本�步队列,具体��考下节)中所有follower滞�的状�。当producer��一�消�到broker�,leader写入消�并�制到所有follower。消��交之��被�功�制到所有的�步副本。消��制延迟�最慢的follower�制,��的是快速检测慢副本,如果follower“���太多或者失效,leader将会把它从ISR中删除。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 七�零拷� + +Kafka中存在大�的网络数��久化到�盘(Producer到Broker)和�盘文件通过网络��(Broker到Consumer)的过程。这一过程的性能直接影�Kafka的整体���。 + + + +### 1�传统模�下的四次拷�与四次上下文切� + +以将�盘文件通过网络��为例。传统模�下,一般使用如下伪代�所示的方法先将文件数�读入内存,然�通过Socket将内存中的数���出去。 + + + +
buffer = File.read
+Socket.send(buffer)
+ + + + +这一过程实际上�生了四次数�拷�。首先通过系统调用将文件数�读入到内核�Buffer(DMA拷�),然�应用程�将内存�Buffer数�读入到用户�Buffer(CPU拷�),接�用户程�通过Socket��数�时将用户�Buffer数�拷�到内核�Buffer(CPU拷�),最�通过DMA拷�将数�拷�到NIC Buffer。�时,还伴��四次上下文切�,如下图所示。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181227142339448-1209004133.png) + +### 2�sendfile和transferTo实现零拷� + +Linux 2.4+内核通过`sendfile`系统调用,�供了零拷�。数�通过DMA拷�到内核�Buffer�,直接通过DMA拷�到NIC Buffer,无需CPU拷�。这也是零拷�这一说法的��。除了�少数�拷�外,因为整个读文件-网络��由一个`sendfile`调用完�,整个过程�有两次上下文切�,因此大大�高了性能。零拷�过程如下图所示。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181227142423000-1025665055.png) + +从具体实现�看,Kafka的数�传输通过TransportLayer�完�,其�类`PlaintextTransportLayer`通过[Java NIO](http://www.jasongj.com/java/nio_reactor/)的FileChannel的`transferTo`和`transferFrom`方法实现零拷�,如下所示。 + + + +
@Override public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException { return fileChannel.transferTo(position, count, socketChannel);
+}
+ + + + +**注:** `transferTo`和`transferFrom`并���一定能使用零拷�。实际上是�能使用零拷�与�作系统相关,如果�作系统�供`sendfile`这样的零拷�系统调用,则这两个方法会通过这样的系统调用充分利用零拷�的优势,�则并�能通过这两个方法本身实现零拷�。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## 八� ISR(副本�步队列) + +上节我们涉�到ISR (In-Sync Replicas),这个是指副本�步队列。副本数对Kafka的��率是有一定的影�,但�大的增强了�用性。默认情况下Kafka的replica数�为1,��个partition都有一个唯一的leader,为了确�消�的��性,通常应用中将其值(由broker的�数offsets.topic.replication.factor指定)大�设置为大于1,比如3。 所有的副本(replicas)统称为Assigned Replicas,�AR。ISR是AR中的一个�集,由leader维护ISR列表,follower从leader�步数�有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟�数replica.lag.max.messages两个维度, 当�最新的版本0.10.x中�支�replica.lag.time.max.ms这个维度),任�一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR + +Kafka 0.10.x版本�移除了replica.lag.max.messages�数,��留了replica.lag.time.max.ms作为ISR中副本管�的�数。为什么这样�呢?replica.lag.max.messages表示当��个副本��leaeder的消�数�超过了这个�数的值,那么leader就会把follower从ISR中删除。�设设置replica.lag.max.messages=4,那么如果producer一次传�至broker的消�数�都�于4�时,因为在leader接�到producer��的消�之�而follower副本开始拉�这些消�之�,follower��leader的消�数�会超过4�消�,故此没有follower移出ISR,所以这时候replica.lag.max.message的设置似乎是��的。但是producer�起瞬时高峰��,producer一次��的消�超过4�时,也就是超过replica.lag.max.messages,此时follower都会被认为是与leader副本��步了,从而被踢出了ISR。但实际上这些follower都是存活状�的且没有性能问题。那么在之�追上leader,并被�新加入了ISR。于是就会出现它们�断地剔出ISR然��新回归ISR,这无疑增加了无谓的性能�耗。而且这个�数是broker全局的。设置太大了,影�真正“���follower的移除;设置的太�了,导致follower的频�进出。无法给定一个�适的replica.lag.max.messages的值,故此,新版本的Kafka移除了这个�数。注:ISR中包括:leader和follower。 + + + +上�一节还涉�到一个概念,�HW。HW俗称高水�,HighWatermark的缩写,�一个partition对应的ISR中最�的LEO作为HW,consumer最多�能消费到HW所在的�置。�外�个replica都有HW,leader和follower�自负责更新自己的HW的状�。对于leader新写入的消�,consumer�能立刻消费,leader会等待该消�被所有ISR中的replicas�步�更新HW,此时消��能被consumer消费。这样就��了如果leader所在的broker失效,该消��然�以从新选举的leader中获�。对于�自内部broKer的读�请求,没有HW的�制。 + +下图详细的说明了当producer生产消�至broker�,ISR以�HW和LEO的�转过程: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202175002622-1830127657.png) + +由此��,Kafka的�制机制既�是完全的�步�制,也�是�纯的异步�制。事实上,�步�制�求所有能工作的follower都�制完,这�消��会被commit,这��制方��大的影�了��率。而异步�制方�下,follower异步的从leader�制数�,数���被leader写入log就被认为已�commit,这�情况下如果follower都还没有�制完,��于leader时,�然leader宕机,则会丢失数�。而Kafka的这�使用ISR的方�则很好的�衡了确�数��丢失以���率。 + +Kafka的ISR的管�最终都会�馈到Zookeeper节点上。具体�置为:/brokers/topics/[topic]/partitions/[partition]/state。 + +目�有两个地方会对这个Zookeeper的节点进行维护: + +Controller�维护:Kafka集群中的其中一个Broker会被选举为Controller,主�负责Partition管�和副本状�管�,也会执行类似于�分�partition之类的管�任务。在符��些特定�件下,Controller下的LeaderSelector会选举新的leader,ISR和新的leader_epoch�controller_epoch写入Zookeeper的相关节点中。�时�起LeaderAndIsrRequest通知所有的replicas。 + +leader�维护:leader有�独的线程定期检测ISR中follower是�脱离ISR, 如果�现ISR�化,则会将新的ISR的信�返回到Zookeeper的相关节点中。 + + + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## ��数���性和�久性�� + +当producer�leader��数�时,�以通过request.required.acks�数�设置数���性的级别: + + + +* 1(默认):这�味�producer在ISR中的leader已�功收到的数�并得到确认���下一�message。如果leader宕机了,则会丢失数�。 + +* 0:这�味�producer无需等待�自broker的确认而继续��下一批消�。这�情况下数�传输效率最高,但是数���性确是最低的。 + +* -1:producer需�等待ISR中的所有follower都确认接收到数���算一次��完�,��性最高。但是这样也�能��数��丢失,比如当ISR中�有leader时(��ISR那一节讲到,ISR中的�员由于�些情况会增加也会�少,最少就�剩一个leader),这样就��了acks=1的情况。 + + [官网�置说明](http://kafka.apache.org/documentation/#configuration) + +如果��高数�的��性,在设置request.required.acks=-1的�时,也�min.insync.replicas这个�数(�以在broker或者topic层�进行设置)的��,这样�能�挥最大的功效。min.insync.replicas这个�数设定ISR中的最�副本数是多少,默认值为1,当且仅当request.required.acks�数设置为-1时,此�数�生效。如果ISR中的副本数少于min.insync.replicas�置的数�时,客户端会返回异常:org.apache.kafka.common.errors.NotEnoughReplicasExceptoin: Messages are rejected since there are fewer in-sync replicas than required。 + + + +接下�对acks=1和-1的两�情况进行详细分�: + + + + + +### 9.1�request.required.acks=1 + +producer��数�到leader,leader写本地日志�功,返回客户端�功;此时ISR中的副本还没有�得�拉�该消�,leader就宕机了,那么此次��的消�就会丢失。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202181329621-1088596676.png) + + + +### 9.2�request.required.acks=-1 + +�步(Kafka默认为�步,�producer.type=sync)的��模�,replication.factor>=2且min.insync.replicas>=2的情况下,�会丢失数�。 + +有两�典型情况。acks=-1的情况下(如无特殊说明,以下acks都表示为�数request.required.acks),数���到leader, ISR的follower全部完�数��步�,leader此时挂掉,那么会选举出新的leader,数��会丢失。 + +![](https://img2018.cnblogs.com/blog/843808/201812/843808-20181202212242480-242555451.png) + +acks=-1的情况下,数���到leader� ,部分ISR的副本�步,leader此时挂掉。比如follower1h和follower2都有�能��新的leader, producer端会得到返回异常,producer端会�新��数�,数��能会�� +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181202212407453-662912091.png) + +当然上图中如果在leader crash的时候,follower2还没有�步到任何数�,而且follower2被选举为新的leader的�,这样消�就�会��。 + +注:Kafka�处�fail/recover问题,�处�Byzantine问题。 + + + + + +### 9.3�关于HW的进一步探讨 + +考虑上图(�acks=-1,部分ISR副本�步)中的�一�情况,如果在Leader挂掉的时候,follower1�步了消�4,5,follower2�步了消�4,与此�时follower2被选举为leader,那么此时follower1中的多出的消�5该�如何处�呢? + +这里就需�HW的����了。如�所述,一个partition中的ISR列表中,leader的HW是所有ISR列表里副本中最�的那个的LEO。类似于木桶原�,水��决于最低那�短�。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203204010599-1107873190.png) + + + +如上图,�个topic的�partition有三个副本,分别为A�B�C。A作为leader肯定是LEO最高,B紧�其�,C机器由于�置比较低,网络比较差,故而�步最慢。这个时候A机器宕机,这时候如果B�为leader,�如没有HW,在A�新��之�会��步(makeFollower)�作,在宕机时log文件之�直接�追加�作,而�如B的LEO已�达到了A的LEO,会产生数��一致的情况,所以使用HW���这�情况。 + +A在��步�作的时候,先将log文件截断到之�自己的HW的�置,�3,之��从B中拉�消�进行�步。 + +如果失败的follower��过�,它首先将自己的log文件截断到上次checkpointed时刻的HW的�置,之��从leader中�步消�。leader挂掉会�新选举,新的leader会��“指令�让其余的follower截断至自身的HW的�置然��拉�新的消�。 + +当ISR中的个副本的LEO�一致时,如果此时leader挂掉,选举新的leader时并�是按照LEO的高低进行选举,而是按照ISR中的顺�选举。 + + + + + +### 9.4�Leader选举 + +一�消��有被ISR中的所有follower都从leader�制过去�会被认为已�交。这样就��了部分数�被写进了leader,还没�得�被任何follower�制就宕机了,而造�数�丢失。而对于producer而言,它�以选择是�等待消�commit,这�以通过request.required.acks�设置。这�机制确�了��ISR中有一个或者以上的follower,一�被commit的消�就�会丢失。 + +有一个很��的问题是当leader宕机了,怎样在follower中选举出新的leader,因为follower�能��很多或者直接crash了,所以必须确�选择“最新�的follower作为新的leader。一个基本的原则就是,如果leader�在了,新的leader必须拥有原�的leader commit的所有消�。这就需��一个折中,如果leader在表�一个消�被commit�等待更多的follower确认,那么在它挂掉之�就有更多的follower�以�为新的leader,但这也会造���率的下�。 + +一��常常用的选举leader的方�是“少数�从多数�,Kafka并�是采用这�方�。这�模�下,如果我们有2f+1个副本,那么在commit之�必须��有f+1个replica�制完消�,�时为了��能正确选举出新的leader,失败的副本数�能超过f个。这�方�有个很大的优势,系统的延迟�决于最快的几�机器,也就是说比如副本数为3,那么延迟就�决于最快的那个follower而�是最慢的那个。“少数�从多数�的方�也有一些劣势,为了��leader选举的正常进行,它所能容�的失败的follower数比较少,如果�容�1个follower挂掉,那么至少�3个以上的副本,如果�容�2个follower挂掉,必须�有5个以上的副本。也就是说,在生产环境下为了��较高的容错率,必须�有大�的副本,而大�的副本�会在大数��下导致性能的急剧下�。这�算法更多用在Zookeeper这�共享集群�置的系统中而很少在需�大�数�的系统中使用的原因。HDFS的HA功能也是基于“少数�从多数�的方�,但是其数�存储并�是采用这样的方�。 + +实际上,leader选举的算法�常多,比如Zookeeper的Zab�Raft以�Viewstamped Replication。而Kafka所使用的leader选举算法更�是微软的PacificA算法。 + +Kafka在Zookeeper中为�一个partition动�的维护了一个ISR,这个ISR里的所有replication都跟上了leader,�有ISR里的�员�能有被选为leader的�能(unclean.leader.election.enable=false)。在这�模�下,对于f+1个副本,一个Kafka topic能在���丢失已�commit消�的��下容�f个副本的失败,在大多数使用场景下,这�模�是�分有利的。事实上,为了容�f个副本的失败,“少数�从多数�的方�和ISR在commit�需�等待的副本的数�是一样的,但是ISR需�的总的副本的个数几乎是“少数�从多数�的方�的一�。 + +上文�到,在ISR中至少有一个follower时,Kafka�以确�已�commit的数��丢失,但如果�一个partition的所有replica都挂了,就无法��数��丢失了。这�情况下有两��行的方案: + +等待ISR中任�一个replica“活�过�,并且选它作为leader + +选择第一个“活�过�的replica(并�一定是在ISR中)作为leader + +这就需�在�用性和一致性当中作出一个简�的抉择。如果一定�等待ISR中的replica“活�过�,那��用的时间就�能会相对较长。而且如果ISR中所有的replica都无法“活�过�了,或者数�丢失了,这个partition将永远��用。选择第一个“活�过�的replica作为leader,而这个replica�是ISR中的replica,那�使它并��障已�包�了所有已commit的消�,它也会�为leader而作为consumer的数��。默认情况下,Kafka采用第二�策略,�   + + + +* unclean.leader.election.enable=true,也�以将此�数设置为false��用第一�策略。 +* unclean.leader.election.enable这个�数对于leader的选举�系统的�用性以�数�的��性都有至关��的影�。 + +下�我们�分�下几�典型的场景。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203213455180-1212737615.png) + +如果上图所示,�设�个partition中的副本数为3,replica-0, replica-1, replica-2分别存放在broker0, broker1和broker2中。AR=(0,1,2),ISR=(0,1)。 + +设置request.required.acks=-1, min.insync.replicas=2,unclean.leader.election.enable=false。这里讲broker0中的副本也称之为broker0起�broker0为leader,broker1为follower。 + +当ISR中的replica-0出现crash的情况时,broker1选举为新的leader[ISR=(1)],因为�min.insync.replicas=2影�,write�能�务,但是read能继续正常�务。此�情况��方案: + +�试��(��)replica-0,如果能起�,系统正常; +如果replica-0�能��,需�将min.insync.replicas设置为1,��write功能。 + + +当ISR中的replica-0出现crash,紧接�replica-1也出现了crash, 此时[ISR=(1),leader=-1],�能对外�供�务,此�情况��方案: + +�试��replica-0和replica-1,如果都能起�,则系统��正常; +如果replica-0起�,而replica-1�能起�,这时候�然�能选出leader,因为当设置unclean.leader.election.enable=false时,leader�能从ISR中选举,当ISR中所有副本都失效之�,需�ISR中最�失效的那个副本能��之��能选举leader, �replica-0先失效,replica-1�失效,需�replica-1����能选举leader。�守的方案建议把unclean.leader.election.enable设置为true,但是这样会有丢失数�的情况�生,这样�以��read�务。�样需�将min.insync.replicas设置为1,��write功能;replica-1��,replica-0�能��,这个情况上��到过,read�务�用,需�将min.insync.replicas设置为1,��write功能; +replica-0和replica-1都�能��,这�情况�以�考情形2. + +当ISR中的replica-0, replica-1�时宕机,此时[ISR=(0,1)],�能对外�供�务,此�情况��方案:�试��replica-0和replica-1,当其中任�一个副本��正常时,对外�以�供read�务。直到2个副本��正常,write功能�能��,或者将将min.insync.replicas设置为1。 + + + + + +### 9.5�Kafka的��模� + +Kafka的��模�由producer端的�置�数producer.type�设置,这个�数指定了在��线程中消�的��方�是�步的还是异步的,默认是�步的方�,�producer.type=sync。如果设置�异步的模�,�producer.type=async,�以是producer以batch的形�push数�,这样会�大的�高broker的性能,但是这样会增加丢失数�的风险。如果需�确�消�的��性,必须�将producer.type设置为sync。 + +对于异步模�,还有4个�套的�数,如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203213857717-291133501.png) + +以batch的方�推�数��以�大的�高处�效率,kafka producer�以将消�在内存中累计到一定数��作为一个batch��请求。batch的数�大��以通过producer的�数(batch.num.messages)控制。通过增加batch的大�,�以�少网络请求和�盘IO的次数,当然具体�数设置需�在效率和时效性方��一个�衡。在比较新的版本中还有batch.size这个�数。 + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## ��高��性使用分� + + + +### 10.1�消�传输�障 + +��已�介�了Kafka如何进行有效的存储,以�了解了producer和consumer如何工作。接下�讨论的是Kafka如何确�消�在producer和consumer之间传输。有以下三��能的传输�障(delivery guarantee): + + + +* At most once: 消��能会丢,但��会��传输 +* At least once:消���会丢,但�能会��传输 +* Exactly once:��消�肯定会被传输一次且仅传输一次 + +Kafka的消�传输�障机制�常直观。当producer�broker��消�时,一旦这�消�被commit,由于副本机制(replication)的存在,它就�会丢失。但是如果producer��数�给broker�,�到的网络问题而造�通信中断,那producer就无法判断该�消�是�已��交(commit)。虽然Kafka无法确定网络故障期间�生了什么,但是producer�以retry多次,确�消�已�正确传输到broker中,所以目�Kafka实现的是at least once。 + +consumer从broker中读�消��,�以选择commit,该�作会在Zookeeper中存下该consumer在该partition下读�的消�的offset。该consumer下一次�读该partition时会从下一�开始读�。如未commit,下一次读�的开始�置会跟上一次commit之�的开始�置相�。当然也�以将consumer设置为autocommit,�consumer一旦读�到数�立�自动commit。如果�讨论这一读�消�的过程,那Kafka是确�了exactly once, 但是如果由于��producer与broker之间的��原因导致消�的��,那么这里就是at least once。 + +考虑这样一�情况,当consumer读完消�之�先commit�处�消�,在这�模�下,如果consumer在commit�还没�得�处�消�就crash了,下次�新开始工作�就无法读到刚刚已�交而未处�的消�,这就对应于at most once了。 + +读完消�先处��commit。这�模�下,如果处�完了消�在commit之�consumer crash了,下次�新开始工作时还会处�刚刚未commit的消�,实际上该消�已�被处�过了,这就对应于at least once。 + +��到exactly once就需�引入消�去�机制。 + + + +### 10.2�消�去� + + + +如上一节所述,Kafka在producer端和consumer端都会出现消�的��,这就需�去�处�。 + +Kafka文档中��GUID(Globally Unique Identifier)的概念,通过客户端生�算法得到�个消�的unique id,�时�映射至broker上存储的地�,�通过GUID便�查询��消�内容,也便于��方的幂等性��,需�在broker上�供此去�处�模�,最新版本已�支�。 + +针对GUID, 如果从客户端的角度去�,那么需�引入集中�缓存,必然会增加�赖��度,�外缓存的大�难以界定。 + +��是Kafka, 类似RabbitMQ以�RocketMQ这类商业级中间件也��障at least once, 且也无法从自身去进行消�去�。所以我们建议业务方根�自身的业务特点进行去�,比如业务消�本身具备幂等性,或者借助Redis等其他产�进行去�处�。 + + + + + +### 10.3�高��性�置 + +Kafka�供了很高的数�冗余弹性,对于需�数�高��性的场景,我们�以增加数�冗余备份数(replication.factor),调高最�写入副本数的个数(min.insync.replicas)等等,但是这样会影�性能。�之,性能�高而��性则�低,用户需�自身业务特性在彼此之间�一些�衡性选择。 + +���数�写入到Kafka是安全的,高��的,需�如下的�置: + + + +1. topic的�置:replication.factor>=3,�副本数至少是3个;2<=min.insync.replicas<=replication.factor +2. broker的�置:leader的选举�件unclean.leader.election.enable=false +3. producer的�置:request.required.acks=-1(all),producer.type=sync + + + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## �一�内部网络框架 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181212101055804-450926848.png) + +Broker的内部处��水线化,分为多个阶段�进行(SEDA),以�高���和性能,尽���Thead盲等待,以下为过程说明。 + +* Accept Thread负责与客户端建立连接链路,然�把Socket轮转交给Process Thread +* Process Thread负责接收请求和�应数�,Process Thread�次基于Selector事件循环,首先从Response Queue读��应数�,�客户端回��应,然�接收到客户端请求�,读�数�放入Request Queue。 +* Work Thread负责业务逻辑�IO�盘处�等,负责从Request Queue读�请求,并把处�结果放入Response Queue中,待Process Thread��出去。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## �二�rebalance机制 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181212101229091-1187958161.png) + +Kafka���一consumer group中�有一个consumer会消费��消�,实际上,Kafka��的是稳定状�下�一个consumer实例�会消费�一个或多个特定的数�,而�个partition的数��会被�一个特定的consumer实例所消费。这样设计的劣势是无法让�一个consumer group里的consumer�匀消费数�,优势是�个consumer�用都跟大�的broker通信,�少通信开销,�时也�低了分�难度,实现也更简�。�外,因为�一个partition里的数�是有�的,这�设计�以���个partition里的数�也是有�被消费。 + +如果�consumer group中consumer数�少于partition数�,则至少有一个consumer会消费多个partition的数�,如果consumer的数�与partition数�相�,则正好一个consumer消费一个partition的数�,而如果consumer的数�多于partition的数�时,会有部分consumer无法消费该topic下任何一�消�。 + +**Consumer Rebalance算法如下 :** + + + +
1. 将目标 topic 下的所有 partirtion 排�,存于PT 2. 对� consumer group 下所有 consumer 排�,存于 CG,第 i 个consumer 记为 Ci 3. N=size(PT)/size(CG),�上�整 4. 解除 Ci 对原�分�的 partition 的消费�(i从0开始) 5. 将第i*N到(i+1)*N-1个 partition 分�给 Ci 
+ + + +目�consumer rebalance的控制策略是由�一个consumer通过Zookeeper完�的。具体的控制方�如下: + + + +
在/consumers/[consumer-group]/下注册id
+设置对/consumers/[consumer-group] 的watcher
+设置对�brokers/ids的watcher
+zk下设置watcher的路径节点更改,触�consumer rebalance
+ + + + +在这�策略下,**�一个consumer或者broker的增加或者�少都会触�consumer rebalance**。因为�个consumer�负责调整自己所消费的partition,为了��整个consumer group的一致性,所以当一个consumer触�了rebalance时,该consumer group内的其它所有consumer也应该�时触�rebalance。 + +* Herd effect + +任何broker或者consumer的增�都会触�所有的consumer的rebalance + +* Split Brain + +�个consumer分别�独通过Zookeeper判断哪些partition down了,那么��consumer从Zookeeper“看�到的view就�能�一样,这就会造�错误的reblance�试。而且有�能所有的consumer都认为rebalance已�完�了,但实际上�能并�如此。 + +[回到顶部](https://www.cnblogs.com/wangzhuxing/p/10051512.html#_labelTop) + +## �三�BenchMark + +Kafka在唯�会有�很深的历�渊�,根�唯�会消�中间件团队(VMS团队)所掌�的资料显示,在VMS团队�转的Kafka集群中所支撑的topic数已接近2000,�天的请求�也已达�亿级。这里就以Kafka的高��性为基准点�探究几���场景下的行为表现,以此�加深对Kafka的认知,为大家在以�高效的使用Kafka时�供一份��。 + + + +### 13.1�测试环境 + +Kafka broker用到了4�机器,分别为broker[0/1/2/3]�置如下: + +CPU: 24core/2.6GHZ +Memory: 62G +Network: 4000Mb +OS/kernel: CentOs release 6.6 (Final) +Disk: 1089G +Kafka版本:0.10.1.0 +broker端JVM�数设置: + + + + + +
-Xmx8G -Xms8G -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true -Xloggc:/apps/service/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=9999 
+ + + +客户端机器�置: + +* CPU: 24core/2.6GHZ +* Memory: 3G +* Network: 1000Mb +* OS/kernel: CentOs release 6.3 (Final) +* Disk: 240G + +### 13.2���场景测试 + +#### 场景1: + +测试��的副本数�min.insync.replicas策略以�request.required.acks策略(以下简称acks策略)对于��速度(TPS)的影�。 + +具体�置:一个producer;��方�为sync;消�体大�为1kB;partition数为12。副本数为:1/2/4;min.insync.replicas分别为1/2/4;acks分别为-1(all)/1/0。 + +具体测试数�如下表(min.insync.replicas�在acks=-1时有效): + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203221444373-736905445.png) + +测试结果分�: + + + +1. 客户端的acks策略对��的TPS有较大的影�,TPS:acks_0 > acks_1 > ack_-1; +2. 副本数越高,TPS越低;副本数一致时,min.insync.replicas�影�TPS; +3. acks=0/1时,TPS与min.insync.replicas�数以�副本数无关,仅�acks策略的影�。 + +下�将partition的个数设置为1,�进一步确认下��的acks策略���的min.insync.replicas策略以���的副本数对于��速度的影�,详细请看情景2和情景3。 + +#### 场景2: + +在partition个数固定为1,测试��的副本数和min.insync.replicas策略对��速度的影�。 + +具体�置:一个producer;��方�为sync;消�体大�为1kB;producer端acks=-1(all)。��副本数:2/3/4; min.insync.replicas设置为:1/2/4。 + +测试结果如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203221832812-2007554062.png) + +测试结果分�: + +副本数越高,TPS越低(这点与场景1的测试结论��),但是当partition数为1时差�甚微。min.insync.replicas�影�TPS。 + +#### 场景3: + +在partition个数固定为1,测试��的acks策略和副本数对��速度的影�。 + +具体�置:一个producer;��方�为sync;消�体大�为1kB;min.insync.replicas=1。topic副本数为:1/2/4;acks: 0/1/-1。 + +测试结果如下: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222003811-323209661.png) + + + + + + + + + + + + + +测试结果分�(与情景1一致): + +* 副本数越多,TPS越低; +* 客户端的acks策略对��的TPS有较大的影�,TPS:acks_0 > acks_1 > ack_-1。 + +#### 场景4: + +测试��partition数对��速率的影� + +具体�置:一个producer;消�体大�为1KB;��方�为sync;topic副本数为2;min.insync.replicas=2;acks=-1。partition数�设置为1/2/4/8/12。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222113272-1167910484.png) + +测试结果分�: + +partition的��会影�TPS,��partition的个数的增长TPS会有所增长,但并�是一直�正比关系,到达一定临界值时,partition数�的增加�而会使TPS略微�低。 + +#### 场景5: + +通过将集群中部分broker设置����务状�,测试对客户端以�消��盘的影�。 + +具体�置:一个producer;消�体大�1KB;��方�为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;retries=0/100000000;partition数为12。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222220687-1891442968.png) + +测试结果分�: + + + +1. kill两�broker�,客户端�以继续��。broker�少�,partition的leader分布在剩余的两�broker上,造�了TPS的��; +2. kill三�broker�,客户端无法继续��。Kafka的自动�试功能开始起作用,当大于等于min.insync.replicas数�的broker���,�以继续��; +3. 当retries�为0时,消�有���盘;客户端�功返回的消�都�功�盘,异常时部分消��以�盘。 + +#### 场景6: + +测试�个producer的��延迟,以�端到端的延迟。 + +具体�置:一个producer;消�体大�1KB;��方�为sync;topic副本数为4;min.insync.replicas设置为2;acks=-1;partition数为12。 + +测试数��结果(��为ms): + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/843808-20181203222407063-2086989349.png) + +**�场景测试总结:** + + + +1. 当acks=-1时,Kafka��端的TPS��于topic的副本数�(ISR中),副本越多TPS越低; +2. acks=0时,TPS最高,其次为1,最差为-1,�TPS:acks_0 > acks_1 > ack_-1 +3. min.insync.replicas�数�影�TPS; +4. partition的��会影�TPS,��partition的个数的增长TPS会有所增长,但并�是一直�正比关系,到达一定临界值时,partition数�的增加�而会使TPS略微�低; +5. Kafka在acks=-1,min.insync.replicas>=1时,具有高��性,所有�功返回的消�都�以�盘。 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" new file mode 100644 index 0000000..a3eb8fd --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\345\270\270\350\247\201\345\221\275\344\273\244\345\217\212\351\205\215\347\275\256\346\200\273\347\273\223.md" @@ -0,0 +1,517 @@ +## **�动zookeeper** + +bin/zookeeper-server-start.sh config/zookeeper.properties & + +## **�动kafka:** + +bin/kafka-server-start.sh config/server.properties + +这样�动�一个�处,就是kafka�动完毕之�,�能关闭终端,为此,我们�以�行这�命令: + +nohup bin/kafka-server-start.sh config/server.properties > ./dev/null 2>&1 & + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161146385-332776455.png) + +多个kafka的�,在�个虚拟机上�行kafka�动命令多次��。 + +当然这个是�机的命令,集群的命令���讲。 + +## **查看是��动** + +jps -lm + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161210221-836644701.png) + +说明没有�动kfka + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161224734-562363764.png) + +说明�动kafka了 + +## 查看kafka版本 + +find ./libs/ -name \*kafka_\* | head -1 | grep -o '\kafka[^\n]*' + +kafka_2.12-2.4.1.jar + +结果: + +就�以看到kafka的具体版本了。 + +其中,2.12为scala版本,2.4.1为kafka版本。 + +## **�止kafka** + +bin/kafka-server-stop.sh + +## **�止zookeeper** + +bin/zookeeper-server-stop.sh + +## **创建topic** + +bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test + +多集群创建,执行这个需��建多机器的kafka集群环境,zkq1/zkq2/zkq3分别代表了3�zookeeper集群的三�机器 + +/bin/kafka-topics.sh —create —zookeeper zkq1:2181,zkq2:2181,zkq3:2181 -replication-factor 6 —partition 6 —topic test + +解释: + +--topic��的test0是topic的�称 + +--zookeeper应该和server.properties文件中的zookeeper.connect一样 + +--config指定当�topic上有效的�数值 + +--partitions指定topic的partition数�,如果�指定该数�,默认是server.properties文件中的num.partitions�置值 + +--replication-factor指定�个partition的副本个数,默认是1个 + +也�以�没有的topic��消�的时候创建topic + +需� + +开�自动创建�置:auto.create.topics.enable=true + +使用程�直接往kafka中相应的topic��数�,如果topic�存在就会按默认�置进行创建。 + +## **展示topic** + +bin/kafka-topics.sh --list --zookeeper localhost:2181 + +## **�述topic** + +bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161250801-1389051022.png) + +解释: + +�查看多个topic用逗�分割 + +**leader**: + +是该partitons所在的所有broker中担任leader的broker id,�个broker都有�能�为leader,负责处�消�的读和写,leader是从所有节点中�机选择的. + +-1表示此broker移除了 + +**Replicas**: + +显示该partiton所有副本所在的broker列表,包括leader,�管该broker是�是存活,�管是�和leader��了�步。列出了所有的副本节点,�管节点是�在�务中. + +**Isr**: + +in-sync replicas的简写,表示存活且副本都已�步的的broker集�,是replicas的�集,是正在�务中的节点. + +举例: + +比如上�结果的第一行:Topic: test0 .Partition:0 ...Leader: 0 ......Replicas: 0,2,1 Isr: 1,0,2 + +Partition: 0[该partition编�是0] + +Replicas: 0,2,1[代表partition0 在broker0,broker1,broker2上�存了副本] + +Isr: 1,0,2 [代表broker0,broker1,broker2都存活而且目�都和leader���步] + +Leader: 0 + +代表�存在broker0,broker1,broker2上的这三个副本中,leader是broker0 + +leader负责读写,broker1�broker2负责从broker0�步信�,平时没他俩什么事 + +## **查看topic的partition�增加partition** + +/kafka-topics.sh –zookeeper 10.2.1.1:2181 –topic mcc-logs –describe. + +## **删除Topic** + +/bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic test + +如果你的server.properties内没有�置相关的�置的�,会出现如下错误: + +Topic test is marked for deletion. + +Note: This will have no impact if delete.topic.enable is not set to true. + +这边是说,你的Topic已�被标记为待删除的Topic,但是呢,你�置文件的开关没有打开,所以�是给它添加了一个标记,实际上,这个Topic并没有被删除。�有,你打开开关之�,会自动删除被标记删除的Topic。 + +解决办法: + +设置server.properties文件内的“delete.topic.enable=true�,并且��Kafka就�以了。 + +如果�想修改�置也�以完全删除 + +1�删除kafka存储目录(server.propertiewenjian log.dirs�置,默认为“/tmp/kafka-logs�)下对应的topic。(��broker下存储的topic�一定相�,所有broker都�看一下) + +2�进入zookeeper客户端删掉对应topic + +zkCli.sh .-server 127.0.0.1:42182 + +找到topic目录: + +ls ../brokers/topics + +删掉对应topic + +rmr ./brokers/topic/topic-name + +找到目录: + +ls .../config/topics + +删掉对应topic + +rmr ./config/topics/topic-name . + +这样就完全删除了 + +## **删除topic中存储的内容** + +在config/server.properties中找到如下的�置 + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220812161312458-550425542.png) + +删除log.dirs指定的文件目录, + +登录zookeeper client。 + +命令: + +/home/ZooKeeper/bin/zkCli.sh + +删除zookeeper中该topic相关的目录 + +命令: + +rm -r /kafka/config/topics/test0 + +rm -r /kafka/brokers/topics/test0 + +rm -r /kafka/admin/delete_topics/test0 (topic被标记为marked for deletion时需�这个命令) + +��zookeeper和broker + +## **生产者��消�:** + +bin/kafka-console-producer.sh --broker-list 130.51.23.95:9092 --topic my-replicated-topic + +这里的ip和端�是broker的ip�端�,根�自己kafka机器的ip和端�写就�以 + +## **消费者消费消�:** + +bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --new-consumer --from-beginning --consumer.config config/consumer.properties + +## **查看topic�分区�移�最大(�)值** + +bin/kafka-run-class.sh kafka.tools.GetOffsetShell --topic hive-mdatabase-hostsltable .--time -1 --broker-list node86:9092 --partitions 0 + +注: time为-1时表示最大值,time为-2时表示最�值 + +�指定--partitions 就是指这个topic整体的情况 + +## 查看指定group的消费情况 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --describe --group mygroup + +�行结果: + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220816164455794-344440282.png) + +* GROUP:消费者组 +* TOPIC:topic�字 +* PARTITION :partition id +* CURRENT-OFFSET: .当�消费到的offset . . . . . . . . +* LOG-END-OFFSETSIZE :最新的offset +* LAG:未消费的�数 +* CONSUMER-ID:消费者组中消费者的id,为—代表没有active的消费者 +* HOST:消费者的机器ip,为—代表没有active的消费者 +* CLIENT-ID:消费者clientID,为—代表没有active的消费者 + +## .查看所有group的消费情况 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --all-groups --all-topics --describe + +![](https://img2022.cnblogs.com/blog/796632/202208/796632-20220816172442100-1560497638.png) + +## 修改group消费的offset + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --group mygroup --reset-offsets --topic mytopic --to-offset 61 --execute + +上�就是把mygroup在mytopic的消费offset修改到了61 + +�设�移有几�选项: + +--to-earliest:. .设置到最早�移处,也就是0 + +--to-latest:. . .设置到最新处,也就是主题分区HW的�置 + +--to-offset NUM: 指定具体的�移�置 + +--shift-by NUM:. 基于当��移��回退多少 + +--by-duration:. .回退到多长时间 + +## 查看指定group中活跃的消费者 + +kafka-consumer-groups.sh --bootstrap-server 172.20.72.93:9092 --describe --group mygroup --members + +## **增加topic分区数** + +(�能增加�能�少) + +为topic t_cdr 增加10个分区 + +bin/kafka-topics.sh --zookeeper node01:2181 .--alter --topic t_cdr --partitions 10 + +## **常用�置�说明** + +kafka 常����置说明,分为四部分 + +* Broker Config:kafka �务端的�置 +* Producer Config:生产端的�置 +* Consumer Config:消费端的�置 +* Kafka Connect Config:kafka 连接相关的�置 + +### **Broker Config** + +1. **zookeeper.connect** + +连接 zookeeper 集群的地�,用于将 kafka 集群相关的元数�信�存储到指定的 zookeeper 集群中 + +**2\. advertised.port** + +注册到 zookeeper 中的地�端�信�,在 IaaS 环境中,默认注册到 zookeeper 中的是内网地�,通过该�置指定外网访问的地��端�,advertised.host.name 和 advertised.port 作用和 advertised.port 差�多,在 0.10.x 之�,直接�置 advertised.port ��,�两个�数被废弃掉了。 + +**3\. auto.create.topics.enable** + +自动创建 topic,默认为 true。其作用是当�一个还没有创建的 topic ��消�时,此时会自动创建一个 topic,并�时设置 -num.partition 1 (partition 个数) 和 default.replication.factor (副本个数,默认为 1) �数。 + +一般该�数需�手动关闭,因为自动创建会影� topic 的管�,我们�以通过 kafka-topic.sh 脚本手动创建 topic,通常也是建议使用这�方�创建 topic。在 0.10.x 之��供了 kafka-admin 包,�以使用其�创建 topic。 + +**4\. auto.leader.rebalance.enable** + +自动 rebalance,默认为 true。其作用是通过��线程定期扫�检查,在一定�件下触��新 leader 选举;在生产环境中,�建议开�,因为替� leader 在性能上没有什么��。 + +**5\. background.threads** + +��线程数,默认为 10。用于���作的线程,�以�用改动。 + +**6\. broker.id** + +Broker 的唯一标识,用于区分��的 Broker。kafka 的检查就是基于此 id 是�在 zookeeper 中/brokers/ids 目录下是�有相应的 id 目录�判定 Broker 是��康。 + +**7\. compression.type** + +压缩类型。此�置�以接�的压缩类型有 gzip�snappy�lz4。�外�以�设置,���和生产端相�的压缩格�。 + +**8\. delete.topic.enable** + +�用删除 topic。如果关闭,则无法使用 admin 工具进行 topic 的删除�作。 + +**9\. leader.imbalance.check.interval.seconds** + +partition 检查�新 rebalance 的周期时间 + +**10\. leader.imbalance.per.broker.percentage** + +标识�个 Broker 失去平衡的比率,如果超过改比率,则执行�新选举 Broker 的 leader + +**11\. log.dir / log.dirs** + +�存 kafka 日志数�的�置。如果 log.dirs 没有设置,则使用 log.dir 指定的目录进行日志数�存储。 + +**12\. log.flush.interval.messages** + +partition 分区的数��达到指定大�时,对数�进行一次刷盘�作。比如设置了 1024k 大�,当 partition 积累的数��达到这个数值时则将数�写入到�盘上。 + +**13\. log.flush.interval.ms** + +数�写入�盘时间间隔,�内存中的数��留多久就�久化一次,如果没有设置,则使用 log.flush.scheduler.interval.ms �数指定的值。 + +**14\. log.retention.bytes** + +表示 topic 的容�达到指定大�时,则对其数�进行清除�作,默认为-1,永远�删除。 + +**15\. log.retention.hours** + +标示 topic 的数�最长�留多久,��是�时 + +**16\. log.retention.minutes** + +表示 topic 的数�最长�留多久,��是分钟,如果没有设置该�数,则使用 log.retention.hours �数 + +**17\. log.retention.ms** + +表示 topic 的数�最长�留多久,��是毫秒,如果没有设置该�数,则使用 log.retention.minutes �数 + +**18\. log.roll.hours** + +新的 segment 创建周期,���时。kafka 数�是以 segment 存储的,当周期时间到达时,就创建一个新的 segment �存储数�。 + +**19\. log.segment.bytes** + +segment 的大�。当 segment 大�达到指定值时,就新创建一个 segment。 + +**20\. message.max.bytes** + +topic 能够接收的最大文件大�。需�注�的是 producer 和 consumer 端设置的大�需�一致。 + +**21\. min.insync.replicas** + +最�副本�步个数。当 producer 设置了 request.required.acks 为-1 时,则 topic 的副本数��步至该�数指定的个数,如果达�到,则 producer 端会产生异常。 + +**22\. num.io.threads** + +指定 io �作的线程数 + +**23\. num.network.threads** + +执行网络�作的线程数 + +**24\. num.recovery.threads.per.data.dir** + +�个数�目录用于��数�的线程数 + +**25\. num.replica.fetchers** + +从 leader 备份数�的线程数 + +**26\. offset.metadata.max.bytes** + +�许消费者端�存 offset 的最大个数 + +**27\. offsets.commit.timeout.ms** + +offset �交的延迟时间 + +**28\. offsets.topic.replication.factor** + +topic 的 offset 的备份数�。该�数建议设置更高��系统更高的�用性 + +**29\. port** + +端��,Broker 对外�供访问的端��。 + +**30\. request.timeout.ms** + +Broker 接收到请求�的最长等待时间,如果超过设定时间,则会给客户端��错误信� + +**31\. zookeeper.connection.timeout.ms** + +客户端和 zookeeper 建立连接的超时时间,如果没有设置该�数,则使用 zookeeper.session.timeout.ms 值 + +**32\. connections.max.idle.ms** + +空连接的超时时间。�空闲连接超过该时间时则自动销�连接。 + +### **Producer Config** + +1. **bootstrap.servers** + +�务端列表。�接收生产消�的�务端列表 + +**2\. key.serializer** + +消�键的�列化方�。指定 key 的�列化类型 + +3..**value.serializer** + +消�内容的�列化方�。指定 value 的�列化类型 + +4..**acks** + +消�写入 Partition 的个数。通常�以设置为 0,1,all;当设置为 0 时,�需�将消���完��就完�消���功能;当设置为 1 时,� Leader Partition 接收到数�并完��盘;当设置为 all 时,�主从 Partition 都接收到数�并�盘。 + +5..**buffer.memory** + +客户端缓存大�。� Producer 生产的数��缓存达到指定值时,将缓存数�一次��的 Broker 上。 + +6..**compression.type** + +压缩类型。指定消����的压缩类型,通常有 none, gzip, snappy, or, lz4 四�。�指定时消�默认�压缩。 + +7..**retries** + +消���失败时�试次数。当该值设置大于 0 时,消�因为网络异常等因素导致消���失败进行�新��的次数。 + +### **Consumer Config** + +1. **bootstrap.servers** + +�务端列表。�消费端从指定的�务端列表中拉�消�进行消费。 + +2..**key.deserializer** + +消�键的��列化方�。指定 key 的��列化类型,与�列化时指定的类型相对应。 + +3..**value.deserializer** + +消�内容的��列化方�。指定 value 的��列化类型,与�列化时指定的类型相对应。 + +4..**fetch.min.bytes** + +抓�消�的最�内容。指定�次��务端拉�的最�消��。 + +5..**group.id** + +消费组中�个消费者的唯一表示。 + +6..**heartbeat.interval.ms** + +心跳检查周期。�在周期性的� group coordinator ��心跳,当�务端�生 rebalance 时,会将消���给�个消费者。该�数值需��于 session.timeout.ms,通常为�者的 1/3。 + +7..**max.partition.fetch.bytes** + +Partition �次返回的最大数��大�。 + +**8\. session.timeout.ms** + +consumer 失效的时间。� consumer 在指定的时间�,还没有�应则认为该 consumer 已��生故障了。 + +**9\. auto.offset.reset** + +当 kafka 中没有�始�移�或�务器上�存在�移�时,指定从哪个�置开始消�消�。earliest:指定从头开始;latest:从最新的数�开始消费。 + +### **Kafka Connect Config** + +1. **group.id** + +消费者在消费组中的唯一标识 + +**2\. internal.key.converter** + +内部 key 的转�类型。 + +**3\. internal.value.converter** + +内部 value 的转�类型。 + +**4\. key.converter** + +�务端接收到 key 时指定的转�类型。 + +5..**value.converter** + +�务端接收到 value 时指定的转�类型。 + +**6\. bootstrap.servers** + +�务端列表。 + +**7\. heartbeat.interval.ms** + +心跳检测,与 consumer 中指定的�置�义相�。 + +**8\. session.timeout.ms** + +session 有效时间,与 consumer 中指定的�置�义相�。 + +## **总结** + +本文总结了平时�常用到的一些 Kafka �置�命令说明,方便�时查看;喜欢的朋��以收�以备�时之需。 + + +## �考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" new file mode 100644 index 0000000..2d35efe --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\346\236\266\346\236\204\344\273\213\347\273\215.md" @@ -0,0 +1,298 @@ +## 一. 工作�程 + +Kafka中消�是以topic进行分类的,Producer生产消�,Consumer消费消�,都是��topic的。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-b9d626794f6625526598db6627b780e7_720w.webp) + +
+ +Topic是逻辑上的改�,Partition是物�上的概念,�个Partition对应�一个log文件,该log文件中存储的就是producer生产的数�,topic=N*partition;partition=log + +Producer生产的数�会被�断的追加到该log文件的末端,且��数�都有自己的offset,consumer组中的�个consumer,都会实时记录自己消费到了哪个offset,以便出错��的时候,�以从上次的�置继续消费。�程:Producer => Topic(Log with offset)=> Consumer. + +## 二. 文件存储 + +Kafka文件存储也是通过本地�盘的方�存储的,主�是通过相应的log与index等文件�存具体的消�文件。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-116ebd7dffd85595d69f080e5b5f6948_720w.webp) + +
+ +生产者�断的�log文件追加消�文件,为了防止log文件过大导致定�效率低下,Kafka的log文件以1G为一个分界点,当.log文件大�超过1G的时候,此时会创建一个新的.log文件,�时为了快速定�大文件中消��置,Kafka采�了分片和索引的机制�加速定�。 + +在kafka的存储log的地方,�文件的地方,会存在消费的�移�以�具体的分区信�,分区信�的�主�包括.index和.log文件组� + +
+ + +![](https://pic3.zhimg.com/80/v2-c6de61f43ecbe58d4f3e7aa29541220e_720w.webp) + +
+ +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8345e4966d8c5274a1e74e29151bd9c6_720w.webp) + +
+ +副本目的是为了备份,所以�一个分区存储在��的broker上,�当third-2存在当�机器kafka01上,实际上�kafka03中也有这个分区的文件(副本),分区中包�副本,�一个分区�以设置多个副本,副本中有一个是leader,其余为follower。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6e8de9e7dcbdac0b7bd424eaaf4f8568_720w.webp) + +
+ +如果.log文件超出大�,则会产生新的.log文件。如下所示: + + + +``` +00000000000000000000.index +00000000000000000000.log +00000000000000170410.index +00000000000000170410.log +00000000000000239430.index +00000000000000239430.log + +``` + + + +**此时如何快速定�数�,步骤:** + + + +``` +.index文件存储的消�的offset+真实的起始�移�。.log中存放的是真实的数�。 + +``` + + + +首先通过二分查找.index文件到查找到当�消�具体的�移,如上图所示,查找为2,�现第二个文件为6,则定�到一个文件中。 然�通过第一个.index文件通过seek定�元素的�置3,定�到之�获�起始�移�+当�文件大�=总的�移�。 获�到总的�移�之�,直接定�到.log文件��快速获得当�消�大�。 + +## 三. 生产者分区策略 + +**分区的原因** 1\. 方便在集群中扩展:�个partition通过调整以适应它所在的机器,而一个Topic��以有多个partition组�,因此整个集群�以适应适�的数�。 2\. �以�高并�:以Partition为��进行读写。类似于多路。 + +**分区的原则** 1\. 指明partition(这里的指明是指第几个分区)的情况下,直接将指明的值作为partition的值 2\. 没有指明partition的情况下,但是存在值key,此时将key的hash值与topic的partition总数进行�余得到partition值 3\. 值与partition�无的情况下,第一次调用时�机生�一个整数,���次调用在这个整数上自增,将这个值与topic�用的partition总数�余得到partition值,�round-robin算法。 + +## 四. 生产者ISR + +为��producer��的数�能够��的��到指定的topic中,topic的�个partition收到producer��的数��,都需��producer��ackacknowledgement,如果producer收到ack就会进行下一轮的��,�则�新��数�。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-409ea1af4f66bd2f44398850cc2ba9e2_720w.webp) + +
+ +**��ack的时机** 确�有follower与leader�步完�,leader���ack,这样�以��在leader挂掉之�,follower中�以选出新的leader(主�是确�follower中数��丢失) + +**follower�步完�多少���ack** 1\. �数以上的follower�步完�,����ack 2\. 全部的follower�步完�,��以��ack + +## 4.1 副本数��步策略 + +### 4.1.1 �数follower�步完����ack + +优点是延迟低 + +缺点是选举新的leader的时候,容�n�节点的故障,需�2n+1个副本(因为需��数��,所以故障的时候,能够选举的��是剩下的副本超过�数),容错率为1/2 + +### 4.1.2 全部follower�步完�完���ack + +优点是容错率�,选举新的leader的时候,容�n�节点的故障�需�n+1个副本��,因为�需�剩下的一个人������ack了 + +缺点是延迟高,因为需�全部副本�步完��� + +### 4.1.3 kafka的选择 + +kafka选择的是第二�,因为在容器率上�更加有优势,�时对于分区的数�而言,�个分区都有大�的数�,第一�方案会造�大�数�的冗余。虽然第二�网络延迟较高,但是网络延迟对于Kafka的影�较�。 + +## 4.2 ISR(�步副本集) + +**猜想** 采用了第二�方案进行�步ack之�,如果leader收到数�,所有的follower开始�步数�,但有一个follower因为��故障,迟迟�能够与leader进行�步,那么leader就�一直等待下去,直到它�步完�,��以��ack,此时需�如何解决这个问题呢? + +**解决** leader中维护了一个动�的ISR(in-sync replica set),�与leader���步的follower集�,当ISR中的follower完�数�的�步之�,给leader��ack,如果follower长时间没有�leader�步数�,则该follower将从ISR中被踢出,该之间阈值由replica.lag.time.max.ms�数设定。当leader�生故障之�,会从ISR中选举出新的leader。 + +## 五. 生产者ack机制 + +对于�些�太��的数�,对数�的��性�求�是很高,能够容�数�的少�丢失,所以没有必�等到ISR中所有的follower全部接��功。 + +Kafka为用户�供了三���性级别,用户根���性和延迟的�求进行�衡选择��的�置。 + +**ack�数�置** 0:producer�等待broker的ack,这一�作�供了最低的延迟,broker接收到还没有写入�盘就已�返回,当broker故障时有�能丢失数� + +1:producer等待broker的ack,partition的leader�盘�功�返回ack,如果在follower�步�功之�leader故障,那么将丢失数�。(�是leader�盘) + +
+ + +![](https://pic1.zhimg.com/80/v2-a219d261edd97432347f4edf5794e170_720w.webp) + +
+ +-1(all):producer等待broker的ack,partition的leader和ISR的follower全部�盘�功�返回ack,但是如果在follower�步完��,broker��ack之�,如果leader�生故障,会造�数���。(这里的数���是因为没有收到,所以继续��导致的数���) + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-c9741a10f418f7ea4eed929f0f266bbb_720w.webp) + +
+ +producer返ack,0无�盘直接返,1�leader�盘然�返,-1全部�盘然�返 + +## 六. 数�一致性问题 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-031d84a2012f64b122dd64ab67a4e52a_720w.webp) + +
+ +LEO(Log End Offset):�个副本最�的一个offset HW(High Watermark):高水�,指代消费者能�到的最大的offset,ISR队列中最�的LEO。 + +**follower故障和leader故障** 1\. follower故障:follower�生故障�会被临时踢出ISR,等待该follower���,follower会读�本地�盘记录的上次的HW,并将log文件高于HW的部分截�掉,从HW开始�leader进行�步,等待该follower的LEO大于等于该partition的HW,�follower追上leader之�,就�以�新加入ISR了。 2\. leader故障:leader�生故障之�,会从ISR中选出一个新的leader,为了��多个副本之间的数�的一致性,其余的follower会先将�自的log文件高于HW的部分截掉,然�从新的leader中�步数�。 + +**这�能��副本之间的数�一致性,并�能��数��丢失或者���** + +## 七. ExactlyOnce + +将�务器的ACK级别设置为-1(all),�以��producer到Server之间�会丢失数�,�At Least Once至少一次语义。将�务器ACK级别设置为0,�以��生产者��消��会被��一次,�At Most Once至多一次。 + +At Least Once�以��数��丢失,但是�能��数����,而At Most Once�以��数����,但是�能��数��丢失,对于��的数�,则�求数����也�丢失,�Exactly Once�精确的一次。 + +在0.11版本的Kafka之�,�能��数��丢失,在下游对数�的��进行去��作,多余多个下游应用的情况,则分别进行全局去�,对性能有很大影�。 + +0.11版本的kafka,引入了一项�大特性:幂等性,幂等性指代Producer�论�Server��了多少次��数�,Server端都�会�久化一�数�。幂等性结�At Least Once语义就构�了Kafka的Exactly Once语义。 + +�用幂等性,�在Producer的�数中设置enable.idempotence=true��,Kafka的幂等性实现实际是将之�的去��作放在了数�上游��,开�幂等性的Producer在�始化的时候会被分�一个PID,�往�一个Partition的消�会附带Sequence Number,而Broker端会对�缓存,当具有相�主键的消�的时候,Broker�会�久化一�。 + +但PID在��之�会�生�化,�时��的Partition也具有��的主键,所以幂等性无法��跨分区跨会�的Exactly Once。 + +�解决跨分区跨会�的Exactly Once,就引入了生产者事务的概念。 + +## 八. Kafka消费者分区分�策略 + +**消费方�:** consumer采用pull拉的方��从broker中读�数�。 + +push推的模�很难适应消费速率��的消费者,因为消���率是由broker决定的,它的目标是尽�能以最快的速度传递消�,但是这样容易造�consumer���处�消�,典型的表现就是拒��务以�网络拥塞。而pull方�则�以让consumer根�自己的消费处�能力以适当的速度消费消�。 + +pull模��足在于如果Kafka中没有数�,消费者�能会陷入循环之中 (因为消费者类似监�状�获�数�消费的),一直返回空数�,针对这一点,Kafka的消费者在消费数�时会传入一个时长�数timeout,如果当�没有数��供消费,consumer会等待一段时间之��返回,时长为timeout。 + +## 8.1\. 分区分�策略 + +一个consumer group中有多个consumer,一个topic有多个partition,所以必然会涉�到partition的分�问题,�确定那个partition由那个consumer消费的问题。 + +**Kafka的两�分�策略:** 1\. round-robin循环 2\. range + +**Round-Robin** 主�采用轮询的方�分�所有的分区,该策略主�实现的步骤: �设存在三个topic:t0/t1/t2,分别拥有1/2/3个分区,共有6个分区,分别为t0-0/t1-0/t1-1/t2-0/t2-1/t2-2,这里�设我们有三个Consumer,C0�C1�C2,订阅情况为C0:t0,C1:t0�t1,C2:t0/t1/t2。 + +此时round-robin采�的分�方�,则是按照分区的字典对分区和消费者进行排�,然�对分区进行循环�历,�到自己订阅的则消费,�则�下轮询下一个消费者。�按照分区轮询消费者,继而消�被消费。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-21eed325191d7d72c9d4c39455c4cae5_720w.webp) + +
+ +分区在循环�历消费者,自己被当�消费者订阅,则消�与消费者共��下(消�被消费),�则消费者�下消�继续�历(消�没有被消费)。轮询的方�会导致�个Consumer所承载的分区数��一致,从而导致�个Consumer压力��。上�的C2因为订阅的比较多,导致承�的压力也相对较大。 + +**Range** Range的�分�策略,首先计算�个Consumer将会承载的分区数�,然�将指定数�的分区分�给该Consumer。�设存在两个Consumer,C0和C1,两个Topic,t0和t1,这两个Topic分别都有三个分区,那么总共的分区有6个,t0-0,t0-1,t0-2,t1-0,t1-1,t1-2。分�方�如下: + +range按照topic一次进行分�,�消费者�历topic,t0,�有三个分区,�时有两个订阅了该topic的消费者,将这些分区和消费者按照字典�排列。 按照平�分�的方�计算�个Consumer会得到多少个分区,如果没有除尽,多出�的分区则按照字典�挨个分�给消费者。按照此方�以此分��一个topic给订阅的消费者,最�完�topic分区的分�。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-d642ed5512a4abdca9a8f35f2d27c277_720w.webp) + +
+ +## 8.2\. 消费者offset的存储 + +由于Consumer在消费过程中�能会出现断电宕机等故障,Consumer��以�,需�从故障�的�置继续消费,所以Consumer需�实时记录自己消费到了那个offset,以便故障���继续消费。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f2a50fd7f054821e36a80b1f6d99ecb0_720w.webp) + +
+ +Kafka0.9版本之�,consumer默认将offset�存在zookeeper中,从0.9版本之�,consumer默认将offset�存在kafka一个内置的topic中,该topic为__consumer_offsets + +## �. 高效读写&Zookeeper作用 + +## 9.1 高效读写 + +**顺�写�盘** Kafka的producer生产数�,需�写入到log文件中,写的过程是追加到文件末端,顺�写的方�,官网有数�表明,�样的�盘,顺�写能够到600M/s,而�机写�有200K/s,这与�盘的机械结构有关,顺�写之所以快,是因为其�去了大��头寻�的时间。 + +**零�制技术** + +NIC:Network Interface Controller网络接�控制器 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-2807d010381d304949bf8cea16ba1744_720w.webp) + +
+ +这是常规的读��作: 1\. �作系统将数�从�盘文件中读�到内核空间的页�缓存 2\. 应用程�将数�从内核空间读入到用户空间缓冲区 3\. 应用程�将读到的数�写回内核空间并放入到socket缓冲区 4\. �作系统将数�从socket缓冲区�制到网�接�,此时数�通过网络��给消费者 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-390cbabdeaba6f9e79a8ba6f4d08d75f_720w.webp) + +
+ +零拷�技术�用将�盘文件的数��制到页�缓存中一次,然�将数�从页�缓存直接��到网络中(��给��的订阅者时,都�以使用�一个页�缓存),从而��了���制的�作。 + +如果有10个消费者,传统方�下,数��制次数为4*10=40次,而使用“零拷�技术��需�1+10=11次,一次为从�盘�制到页�缓存,10次表示10个消费者�自读�一次页�缓存。 + +## 9.2 zookeeper作用 + +Kafka集群中有一个broker会被选举为Controller,负责管�集群broker的上下线�所有topic的分区副本分�和leader的选举等工作。Controller的工作管�是�赖于zookeeper的。 + +**Partition的Leader的选举过程** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-fc64ea72cba32e702b15344767bdace9_720w.webp) + +
+ +## �. 事务 + +kafka从0.11版本开始引入了事务支�,事务�以��Kafka在Exactly Once语义的基础上,生产和消费�以跨分区的会�,�么全部�功,�么全部失败。 + +## 10.1 Producer事务 + +为了按跨分区跨会�的事务,需�引入一个全局唯一的Transaction ID,并将Producer获得的PID(�以�解为Producer ID)和Transaction ID进行绑定,这样当Producer��之�就�以通过正在进行的Transaction ID获得原�的PID。 + +为了管�Transaction,Kafka引入了一个新的组件Transaction Coordinator,Producer就是通过有和Transaction Coordinator交互获得Transaction ID对应的任务状�,Transaction Coordinator还负责将事务信�写入内部的一个Topic中,这样�使整个�务��,由于事务状�得到�存,进行中的事务状��以��,从而继续进行。 + +## 10.2 Consumer事务 + +对于Consumer而言,事务的��相比Producer相对较弱,尤其是无法��Commit的信�被精确消费,这是由于Consumer�以通过offset访问任�信�,而且��的Segment File声明周期��,�一事务的消��能会出现���被删除的情况。 + +## �考文章 + +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" new file mode 100644 index 0000000..cd6122e --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\347\232\204\351\233\206\347\276\244\345\267\245\344\275\234\345\216\237\347\220\206.md" @@ -0,0 +1,127 @@ +**�言** + +上篇文章讲到了消�在 Partition 上的存储形�,本�准备接����生产中的一些使用方�,想了想还有些很��的工作组件原�没有讲清楚,比如一个 Topic 由 N 个 Partition 组�,那么这些 Partition 是如何�匀的分布在��的 Broker 上?�比如当一个 Broker 宕机�,其上负责读写请求的主 Partition 无法正常访问,如何让从 Partition 转��主 Partition �继续�供正常的读写�务?想�解决这些问题,就必须先�了解一下 Kafka 集群内部的管�机制,其中一个�常��的控制器就是 KafkaController。本文我们就�讲讲 KafkaController 是如何�解决上��到的那些问题的。 + +### **文章概览** + +1. KafkaController 是什么�其选举策略。 +2. KafkaController 监控 ZK 的目录分布。 +3. Partition 分布算法。 +4. Partition 的状�转移。 +5. Kafka 集群的负载�衡处��程解�。 + +## **KafkaController 是什么�其选举策略** + +Kafka 集群由多� Broker 构�,�� Broker 都有一个 KafkaController 用于管�当�的 Broker。试想一下,如果一个集群没有一个“领导者�,那么�去和“外界�(比如 ZK)沟通呢?�去�调 Partition 应该如何分布在集群中的�� Broker 上呢?�去处� Broker 宕机�,在其 Broker 上的主 Partition 无法正常�供读写�务�,将对应的从 Partition 转��主 Partition 继续正常对外�供�务呢?那么由哪个 Broker 的 KafkaController �担当“领导者�这个角色呢? + +Kafka 的设计者很�明,Zookeeper 既然是分布�应用�调�务,那么干脆就让它�帮 Kafka 集群选举一个“领导者�出�,这个“领导者�对应的 KafkaController 称为 Leader,其他的 KafkaController 被称为 Follower,在�一时刻,一个 Kafka 集群�能有一个 Leader 和 N 个 Follower。 + +### **Zookeeper 是怎么实现 KafkaController 的选主工作呢?** + +�微熟悉 Zookeeper 的�伙伴应该都比较清楚,Zookeeper 是通过监控目录(zNode)的�化,从而�出一些相应的动作。 + +Zookeeper 的目录分为四�,第一�是永久的,被称作为 `Persistent`; + +第二�是顺�且永久的,被称作为 `Persistent_Sequential`; + +第三�是临时的,被称为 `Ephemeral`; + +第四�是顺�且临时的,被称作为 `Ephemeral_Sequential`。 + +KafkaController 正是利用了临时的这一特性�完�选主的,在 Broker �动时,�个 Broker 的 KafkaController 都会� ZK 的 `/controller` 目录写入 BrokerId,�先写入�就是 Leader,剩余的 KafkaController 是 Follower,当 Leader 在周期内没有� ZK ��报告的�,则认为 Leader 挂了,此时 ZK 删除临时的 `/controller` 目录,Kafka 集群中的其他 KafkaController 开始新的一轮争主�作,�程和上�一样。下�是选 Leader 的�程图。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-af1f22f109f85fe6b169c6e4a271016f_720w.webp) + +
Leader选举�程图
+ +
+ +从上图�以看出,第一次,Broker1 �功抢先在 Zookeeper 的 /controller 目录上写入信�,所以 Broker1 的 KafkaController 为 Leader,其他两个为 Follower。第二次,Broker1 宕机或者下线,此时 Broker2 和 Broker3 检测到,于是开始新一轮的争抢将信�写入 Zookeeper,从图中�以看出,Broker2 争到了 Leader,所以 Broker3 是 Follower 状�。 + +正常情况下,上�这个�程没有问题,但是如果在 Broker1 离线的情况下,Zookeeper 准备删除 /controller 的临时 node 时,系统 hang �没办法删除,改怎么办呢?这里留个�疑问供大家�考。��会用一篇文章专门�解答 Kafka 相关的问题(包括�试题哦,敬请期待)。 + +## **KafkaController 监控的 ZK 目录分布** + +KafkaController 在�始化的时候,会针对��的 zNode 注册���样的监�器,以便处���的用户请求或者系统内部�化请求。监控 ZK 的目录大概�以分为两部分,分别是 `/admin` 目录和 `/brokers` 目录。�目录�其对应的功能如下表所示,需�的朋�自�。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-2a603adc2e06f3663e693259e8bf16d4_720w.webp) + +
+ +## **Partition 分布算法** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-36d40cb264f6432a81ad83c9365d7997_720w.webp) + +
Partition分布算法图
+ +
+ +图解:�设集群有 3 个 Broker,Partition 因�为 2。 + +1. �机选� Broker 集群中的一个 Broker 节点,然�以轮询的方�将主 Partition �匀的分布到��的 Broker 上。 +2. 主 Partition 分布完��,将从 Partition 按照 AR 组内顺�以轮询的方�将 Partition �匀的分布到��的 Broker 上。 + +## **Partition 的状�转移** + +用户针对特定的 Topic 创建了相应的 Partition ,但是这些 Partition �一定时刻都能够正常工作,所有 Partition 在�一时刻会对应 4 个状�中的�一个;其整个生命周期会�历如下状�的转移,分别是 NonExistentPartition�NewPartition�OnlinePartition�OfflinePartition,其对应的状�转移情况如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-58a8609aa2698130679d9fb80541d19b_720w.webp) + +
Partition状�转移图
+ +
+ +从上图�以看出,Partition 的状�会由�置状��能够转移到目标状�的,而�是胡乱转移状�的。 + +`NonExistentPartition:`代表没有创建 Partition 时的状�,也有�能是离线的 Partition 被删除�的状�。 + +`NewPartition:`当 Partition 被创建时,此时�有 AR(Assigned Replica),还没有 ISR(In-Synic Replica),此时还�能接�数�的写入和读�。 + +`OnlinePartition:`由 NewPartition 状�转移为 OnlinePartition 状�,此时 Partition 的 Leader 已�被选举出�了,并且也有对应的 ISR 列表等。此时已��以对外�供读写请求了。 + +`OfflinePartition:`当 Partition 对应的 Broker 宕机或者网络异常等问题,由 OnlinePartition 转移到 OfflinePartition,此时的 Partition 已��能在对外�供读写�务。当 Partition 被彻底删除�状�就转移� NonExistentPartition,当网络��或者 Broker ���,其状���以转移到 OnlinePartition,从而继续对外�供读写�务。 + +## **Kafka 集群的负载�衡处��程解�** + +��的文章讲到过,Partition 有 Leader Replica 和 Preferred Replica 两�角色,Leader Replica 负责对外�供读写�务 Preferred Replica 负责�步 Leader Replica 上的数�。现在集群中�设有 3 个 Broker,3 个 Partition,�个 Partition 有 3 个 Replica,当集群�行一段时候�,集群中�些 Broker 宕机,Leader Replica 进行转移,其过程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-dc0bcd6f072f7e6cef8289882259d59e_720w.webp) + +
Partition的Leader转移图
+ +
+ +从上图�以看出,集群�行一段时间�,Broker1 挂掉了,在其上�行的 Partition0 对应的 Leader Replica 转移到了 Broker2 上。�设一段时间� Broker3 也挂了,则 Broker3 上的 Partition3 对应的 Leader Replica 也转移到了 Broker2 上,集群中�有 Broker2 上的 Partition 在对外�供读写�务,从而造� Broker2 上的�务压力比较大,之� Broker1 和 Broker3 ���,其上�有 Preferred Replica �备份�作。 + +针对以上这���时间的推移,集群�在�刚开始时那样平衡,需�通过��线程将 Leader Replica �新分�到�� Broker 上,从而使得读写�务尽��匀的分布在��的节点上。 + +�平衡�作是由 partition-rebalance-thread ��线程�作的,由于其优先级很低,所以�会在集群空闲的时候�会执行。集群的�平衡的评判标准是由`leader.imbalance.per.broker.percentage`�置决定的,当集群的�平衡度达到 10%(默认)时,会触���线程�动�平衡�作,其具体执行步骤如下: + +1. 对 Partition 的 AR 列表根� Preferred Replica 进行分组�作。 +2. �历 Broker,对其上的 Partition 进行处�。 +3. 统计 Broker 上的 Leader Replica 和 Preferred Replica �相等的 Partition 个数。 +4. 统计 Broker 上的 Partition 个数。 +5. Partition 个数 / �相等的 Partition 个数,如果大于 10%,则触��平衡�作;�之,则��任何处�。 + +## **总结** + +本文主�介�了 Kafka 集群�务内部的一些工作机制,相信�伙伴们掌�了这部分内容�,对 Broker �务端的工作�程有了进一步的�解,从而更好的把控整体集群�务。下篇文章我们�正�介�一下**Kafka 常用的命令行�作**,敬请期待。 + +# �考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" new file mode 100644 index 0000000..77ac95f --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232Kafka\351\207\215\350\246\201\347\237\245\350\257\206\347\202\271+\351\235\242\350\257\225\351\242\230\345\244\247\345\205\250.md" @@ -0,0 +1,518 @@ +## ���试知识点 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-75cd70ad3052ba44bf706a3ab39e59d5_720w.webp) + + +Kafka 消费端确�一个 Partition 在一个消费者组内�能被一个消费者消费。这��改怎么�解呢? + +1. 在�一个消费者组内,一个 Partition �能被一个消费者消费。 +2. 在�一个消费者组内,所有消费者组�起�必定�以消费一个 Topic 下的所有 Partition。 +3. 在�一个消费组内,一个消费者�以消费多个 Partition 的信�。 +4. 在��消费者组内,�一个分区�以被多个消费者消费。 +5. �个消费者组一定会完整消费一个 Topic 下的所有 Partition。 + +### **消费组存在的�义** + +了解了消费者与消费组的关系�,有朋�会比较疑惑消费者组有啥实际存在的�义呢?或者说消费组的作用是什么? + +作者对消费组的作用归结了如下两点。 + +1. 在实际生产中,对于�一个 Topic,�能有 A�B�C 等 N 个消费方想�消费。比如一份用户点击日志,A 消费方想用��一个用户近 N 天点击过哪些商�;B 消费方想用��一个用户近 N 天点击过� TopN 个相似的商�;C 消费方想用��一个根�用户点击过的商�推�相关周边的商�需求。对于多应用场景,就�以使用消费组�隔离��的业务使用场景,从而达到一个 Topic �以被多个消费组��消费的目的。 +2. 消费组与 Partition 的消费进度绑定。当有新的消费者加入或者有消费者从消费组退出时,会触�消费组的 Repartition �作(��会详细介� Repartition);在 Repartition �,Partition1 被消费组的消费者 A 进行消费,Repartition �,Partition1 消费组的消费者 B 进行消费,为了��消�被��消费,需�从消费组记录的 Partition 消费进度读�当�消费到的�置(� OffSet �置),然�在继续消费,从而达到消费者的平滑�移,�时也�高了系统的�用性。 + +## **Repartition 触�时机** + +使用过 Kafka 消费者客户端的�学肯定知�,消费者组内�尔会触� Repartition �作,所谓 Repartition � Partition 在�些情况下�新被分�给�与消费的消费者。基本�以分为如下几�情况。 + +1. 消费组内�消费者宕机,触� Repartition �作,如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a9ef6a29cb9ba3456a05ad75cb91cb03_720w.webp) + +
消费者宕机情况
+ +
+ +2\. 消费组内新增消费者,触� Repartition �作,如下图所示。一般这�情况是为了�高消费端的消费能力,从而加快消费进度。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8803223d712fdde035b8e7b9170dd3fb_720w.webp) + +
新增消费者情况
+ +
+ +3.Topic 下的 Partition 增多,触� Repartition �作,如下图所示。一般这�调整 Partition 个数的情况也是为了�高消费端消费速度的,因为当消费者个数大于等于 Partition 个数时,在增加消费者个数是没有用的(原因是:在一个消费组内,消费者:Partition = 1:N,当 N �于 1 时,相当于消费者过剩了),所以一方�增加 Partition 个数�时增加消费者个数�以�高消费端的消费速度。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-8f1a427c6842d9babf139454ce23cfa3_720w.webp) + +
新增Partition个数情况
+ +
+ +## **消费者与 ZK 的关系** + +众所周知,ZK �仅�存了消费者消费 partition 的进度,�时也�存了消费组的�员列表�partition 的所有者。消费者想�消费 Partition,需�从 ZK 中获�该消费者对应的分区信��当�分区对应的消费进度,� OffSert 信�。那么 Partition 应该由那个消费者进行消费,决定因素有哪些呢?从之�的图中�难得出,两个��因素分别是:消费组中存活的消费者列表和 Topic 对应的 Partition 列表。通过这两个因素结� Partition 分�算法,��得出消费者与 Partition 的对应关系,然�将信�存储到 ZK 中。Kafka 有高级 API 和低级 API,如果�需��作 OffSet �移�的�交,�通过高级 API 直接使用,从而�低使用者的难度。对于一些比较特殊的使用场景,比如想�消费特定 Partition 的信�,Kafka 也�供了低级 API �进行手动�作。 + +## **消费端工作�程** + +在介�消费端工作�程�,先�熟悉一下用到的一些组件。 + +* `KakfaConsumer`:消费端,用于�动消费者进程�消费消�。 +* `ConsumerConfig`:消费端�置管�,用于给消费端�置相关�数,比如指定 Kafka 集群,设置自动�交和自动�交时间间隔等等�数,都由其�管�。 +* `ConsumerConnector`:消费者连接器,通过消费者连接器�以获得 Kafka 消��,然�通过消��就能获得消�从而使得客户端开始消费消�。 + +以上三者之间的关系�以概括为:消费端使用消费者�置管�创建出了消费者连接器,通过消费者连接器创建队列(这个队列的作用也是为了缓存数�),其中队列中的消�由专门的拉�线程从�务端拉�然�写入,最�由消费者客户端轮询队列中的消�进行消费。具体�作�程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-122b4a706de39655d257928005a83ff1_720w.webp) + +
消费端工作�程
+ +
+ +我们在从消费者与 ZK 的角度�看看其工作�程是什么样的? + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-4ed25ebb9236986b2084ce8a042f65b9_720w.webp) + +
消费端与ZK之间的工作�程
+ +
+ +从上图�以看出,首先拉�线程�拉�一次消�,�步更新一次拉�状�,其作用是为了下一次拉�消�时能够拉�到最新产生的消�;拉�线程将拉�到的消�写入到队列中等待消费消费线程去真正读�处�。消费线程以轮询的方��续读�队列中的消�,���现队列中有消�就开始消费,消费完消��更新消费进度,此处需�注�的是,消费线程�是�次都和 ZK �步消费进度,而是将消费进度暂时写入本地。这样�的目的是为了�少消费者与 ZK 的频��步消�,从而�低 ZK 的压力。 + +## **消费者的三�消费情况** + +消费者从�务端的 Partition 上拉�到消�,消费消�有三�情况,分别如下: + +1. 至少一次。�一�消�至少被消费一次,消���能丢失,但是�能会被��消费。 +2. 至多一次。�一�消�最多�以被消费一次,消���能被��消费,但是消�有�能丢失。 +3. 正好一次。�一�消�正好被消费一次,消���能丢失也��能被��消费。 + +### **1.至少一次** + +消费者读�消�,先处�消�,在�存消费进度。消费者拉�到消�,先消费消�,然�在�存�移�,当消费者消费消��还没�得��存�移�,则会造�消�被��消费。如下图所示: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1a047ed616ba44daebdb4b6ce786a61a_720w.webp) + +
先消费��存消费进度
+ +
+ +### **2.至多一次** + +消费者读�消�,先�存消费进度,在处�消�。消费者拉�到消�,先�存了�移�,当�存了�移��还没消费完消�,消费者挂了,则会造�未消费的消�丢失。如下图所示: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-1f9f91ae54396c5e5d93ae89251eb1ed_720w.webp) + +
先�存消费进度�消费消�
+ +
+ +### **3.正好一次** + +正好消费一次的办法�以通过将消费者的消费进度和消�处�结果�存在一起。��能��两个�作是一个原��作,就能达到正好消费一次的目的。通常�以将两个�作�存在一起,比如 HDFS 中。正好消费一次�程如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-a0bbb114e2ad551227f81c1f26d4bd5d_720w.webp) + +
正好消费一次
+ +
+ +## Partition�Replica�Log 和 LogSegment 的关系 + +�设有一个 Kafka 集群,Broker 个数为 3,Topic 个数为 1,Partition 个数为 3,Replica 个数为 2。Partition 的物�分布如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f8f21631b138321f25c8821c677c5579_720w.webp) + +
Partition分布图
+ +
+ +从上图�以看出,该 Topic 由三个 Partition 构�,并且�个 Partition 由主从两个副本构�。�个 Partition 的主从副本分布在��的 Broker 上,通过这点也�以看出,当�个 Broker 宕机时,�以将分布在其他 Broker 上的从副本设置为主副本,因为�有主副本对外�供读写请求,当然在最新的 2.x 版本中从副本也�以对外读请求了。将主从副本分布在��的 Broker 上从而�高系统的�用性。 + +Partition 的实际物�存储是以 Log 文件的形�展示的,而�个 Log 文件�以多个 LogSegment 组�。Kafka 为什么�这么设计呢?其实原因比较简�,��消�的�断写入,Log 文件肯定是越�越大,Kafka 为了方便管�,将一个大文件切割�一个一个的 LogSegment �进行管�;�个 LogSegment 由数�文件和索引文件构�,数�文件是用�存储实际的消�内容,而索引文件是为了加快消�内容的读�。 + +�能�有朋�会问,Kafka 本身消费是以 Partition 维度顺�消费消�的,�盘在顺�读的时候效率很高完全没有必�使用索引啊。其实 Kafka 为了满足一些特殊业务需求,比如��机消费 Partition 中的消�,此时�以先通过索引文件快速定�到消�的实际存储�置,然�进行处�。 + +总结一下 Partition�Replica�Log 和 LogSegment 之间的关系。消�是以 Partition 维度进行管�的,为了�高系统的�用性,�个 Partition 都�以设置相应的 Replica 副本数,一般在创建 Topic 的时候�时指定 Replica 的个数;Partition 和 Replica 的实际物�存储形�是通过 Log 文件展现的,为了防止消��断写入,导致 Log 文件大��续增长,所以将 Log 切割�一个一个的 LogSegment 文件。 + +**注�:** 在�一时刻,�个主 Partition 中有且�有一个 LogSegment 被标识为�写入状�,当一个 LogSegment 文件大�超过一定大��(比如当文件大�超过 1G,这个就类似于 HDFS 存储的数�文件,HDFS 中数�文件达到 128M 的时候就会被分出一个新的文件�存储数�),就会新创建一个 LogSegment �继续接收新写入的消�。 + +## 写入消��程分� + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-eb66e4ecf7cf07fcb6b12029bfdd9b71_720w.webp) + +
消�写入��盘�程
+ +
+ +�程解� + +在第 3 篇文章讲过,生产者客户端对于�个 Partition 一次会��一批消�到�务端,�务端收到一批消��写入相应的 Partition 上。上图�程主�分为如下几步: + +1. **客户端消�收集器收集属于�一个分区的消�,并对��消�设置一个�移�,且�一批消�总是从 0 开始�调递增。比如第一次�� 3 �消�,则对三�消��次编� [0,1,2],第二次�� 4 �消�,则消��次编�为 [0,1,2,3]。注�此处设置的消��移�是相对�移�。** +2. **客户端将消���给�务端,�务端拿到下一�消�的�对�移�,将传到�务端的这批消�的相对�移�修改��对�移�。** +3. **将修改�的消�以追加的方�追加到当�活跃的 LogSegment ��,然�更新�对�移�。** +4. **将消�集写入到文件通�。** +5. **文件通�将消�集 flush 到�盘,完�消�的写入�作。** + +了解以上过程�,我们在�看看消�的具体构�情况。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6e993c95decd5d274b032cd423936504_720w.webp) + +
消�构�细节图
+ +
+ +一�消�由如下三部分构�: + +* **OffSet:�移�,消�在客户端���将相对�移�存储到该�置,当消�存储到 LogSegment �,先将其修改为�对�移�在写入�盘。** +* **Size:本� Message 的内容大�** +* **Message:消�的具体内容,其具体�由 7 部分组�,crc 用于校验消�,Attribute 代表了属性,key-length 和 value-length 分别代表 key 和 value 的长度,key 和 value 分别代表了其对应的内容。** + +### 消��移�的计算过程 + +通过以上�程�以看出,��消�在被实际存储到�盘时都会被分�一个�对�移���能被写入�盘。在�一个分区内,消�的�对�移�都是从 0 开始,且�调递增;在��分区内,消�的�对�移�是没有任何关系的。接下�讨论下消�的�对�移�的计算规则。 + +确定消��移�有两�方�,一�是顺�读��一�消��确定,此�方�代价比较大,实际上我们并�想知�消�的内容,而�是想知�消�的�移�;第二�是读���消�的 Size 属性,然�计算出下一�消�的起始�移�。比如第一�消�内容为 “abc�,写入�盘�的�移�为:8(OffSet)+ 4(Message 大�)+ 3(Message 内容的长度)= 15。第二�写入的消�内容为“defg�,其起始�移�为 15,下一�消�的起始�移�应该是:15+8+4+4=31,以此类推。 + +## 消费消��副本�步�程分� + +和写入消��程��,读�消��程分为两�情况,分别是消费端消费消�和从副本(备份副本)�步主副本的消�。在开始分�读��程之�,需�先明白几个用到的��,�然�程分��能会看的比较糊涂。 + +* **BaseOffSet**:基准�移�,�个 Partition 由 N 个 LogSegment 组�,�个 LogSegment 都有基准�移�,大概由如下构�,数组中�个数代表一个 LogSegment 的基准�移�:[0,200,400,600, ...]。 +* **StartOffSet**:起始�移�,由消费端�起读�消�请求时,指定从哪个�置开始消费消�。 +* **MaxLength**:拉�大�,由消费端�起读�消�请求时,指定本次最大拉�消�内容的数�大�。该�数�以通过[max.partition.fetch.bytes](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/draft/3020%23)�指定,默认大�为 1M。 +* **MaxOffSet**:最大�移�,消费端拉�消�时,最高�拉�消�的�置,�俗称的“高水��。该�数由�务端指定,其作用是为了防止生产端还未写入的消�就被消费端进行消费。此�数对于从副本�步主副本�会用到。 +* **MaxPosition**:LogSegment 的最大�置,确定了起始�移�在�个 LogSegment 上开始,读� MaxLength �,�能超过 MaxPosition。MaxPosition 是一个实际的物��置,而��移�。 + +�设消费端从 000000621 �置开始消费消�,关于几个��的关系如下图所示。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-cd9c62a71cddccd7bc8a5d810d5af216_720w.webp) + +
�置关系图
+ +
+ +消费端和从副本拉��程如下: + +1. **客户端确定拉�的�置,� StartOffSet 的值,找到主副本对应的 LogSegment。** +2. **LogSegment 由索引文件和数�文件构�,由于索引文件是从�到大排列的,首先从索引文件确定一个�于等于 StartOffSet 最近的索引�置。** +3. **根�索引�置找到对应的数�文件�置,由于数�文件也是从�到大排列的,从找到的数�文件�置顺����历,直到找到和 StartOffSet 相等的�置,�为消费或拉�消�的�置。** +4. **从 StartOffSet 开始��拉� MaxLength 大�的数�,返回给消费端或者从副本进行消费或备份�作。** + +�设拉�消�起始�置为 00000313,消�拉��程图如下: + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9417ca60a0c5e9474ec49a77fff18b1b_720w.webp) + +
消�拉��程图
+ +
+ +## kafka 如何��系统的高�用�数�的��性和数�的一致性的? + +### kafka 的高�用性: + +1. **Kafka 本身是一个分布�系统,�时采用了 Zookeeper 存储元数�信�,�高了系统的高�用性。** +2. **Kafka 使用多副本机制,当状�为 Leader 的 Partition 对应的 Broker 宕机或者网络异常时,Kafka 会通过选举机制从对应的 Replica 列表中�新选举出一个 Replica 当� Leader,从而继续对外�供读写�务(当然,需�注�的一点是,在新版本的 Kafka 中,Replica 也�以对外�供读请求了),利用多副本机制在一定程度上�高了系统的容错性,从而��了系统的高�用。** + +### Kafka 的��性: + +1. **从 Producer 端�看,��性是指生产的消�能够正常的被存储到 Partition 上且消��会丢失。Kafka 通过 [request.required.acks](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)和[min.insync.replicas](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23) 两个�数��,在一定程度上��消��会丢失。** +2. **[request.required.acks](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23) �设置为 1�0�-1 三�情况。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-7946f258c85fb8ca3d4aa423269c483a_720w.webp) + +
request.required.acks=1
+ +
+ +设置为 1 时代表当 Leader 状�的 Partition 接收到消�并�久化时就认为消����功,如果 ISR 列表的 Replica 还没�得��步消�,Leader 状�的 Partition 对应的 Broker 宕机,则消�有�能丢失。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-382c9f37f644feb37dd975c67bc1038f_720w.webp) + +
request.required.acks=0
+ +
+ +设置为 0 时代表 Producer ��消��就认为�功,消�有�能丢失。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-592996f264baadc64967d6f4b28f4d23_720w.webp) + +
request.required.acks=-1
+ +
+ +设置为-1 时,代表 ISR 列表中的所有 Replica 将消��步完���认为消����功;但是如果�存在主 Partition 的时候,Broker 异常时�样会导致消�丢失。所以此时就需�[min.insync.replicas](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)�数的��,该�数需�设定值大于等于 2,当 Partition 的个数�于设定的值时,Producer ��消�会直接报错。 + +上�这个过程看似已�很完美了,但是�设如果消�在�步到部分从 Partition 上时,主 Partition 宕机,此时消�会�传,虽然消��会丢失,但是会造��一�消�会存储多次。在新版本中 Kafka �出了幂等性的概念,通过给��消�设置一个唯一 ID,并且该 ID �以唯一映射到 Partition 的一个固定�置,从而��消���存储的问题(作者到目�还没有使用过该特性,感兴趣的朋��以自行在深入研究一下)。 + +### Kafka 的一致性: + +1. **从 Consumer 端�看,�一�消�在多个 Partition 上读�到的消�是一直的,Kafka 通过引入 HW(High Water)�实现这一特性。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9975539d98bf1a4e1a3038f2eceb2bb9_720w.webp) + +
消��步图
+ +
+ +从上图�以看出,�设 Consumer 从主 Partition1 上消费消�,由于 Kafka 规定��许消费 HW 之�的消�,所以最多消费到 Message2。�设当 Partition1 异常�,Partition2 被选举为 Leader,此时�旧�以从 Partition2 上读�到 Message2。其实 HW 的��利用了木桶效应,始终��最短�的那个�置。 + +从上�我们也�以看出,使用 HW 特性�会使得消��有被所有副本�步��能被消费,所以在一定程度上�低了消费端的性能,�以通过设置[replica.lag.time.max.ms](https://link.zhihu.com/?target=https%3A//xie.infoq.cn/edit/49a133ad2b2f2671aa60706b0%23)�数���消��步的最大时间。 + +## kafka 为什么那么快? + +kafka 使用了顺�写入和“零拷��技术,�达到�秒钟 200w(Apache 官方给出的数�) 的�盘数�写入�,�外 Kafka 通过压缩数�,�低 I/O 的负担。 + +1. **顺�写入** + +大家都知�,对于�盘而已,如果是�机写入数�的�,�次数�在写入时�先进行寻��作,该�作是通过移动�头完�的,�其耗费时间,而顺�读写就能够��该�作。 + +1. **“零拷��技术** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-6930901956f341f1ab4a6e5650a0680b_720w.webp) + +
普通数�拷��程图
+ +
+ +普通的数�拷��程如上图所示,数�由�盘 copy 到内核�,然�在拷�到用户�,然��由用户�拷�到 socket,然�由 socket �议引擎,最�由�议引擎将数���到网络中。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-9e44873a63d8addca917e658667f0b61_720w.webp) + +
&quot;零拷�&quot;�程图
+ +
+ +采用了“零拷��技术��以看出,数��在�过用户�传输,而是直接在内核�完��作,�少了两次 copy �作。从而大大�高了数�传输速度。 + +1. **压缩** + +Kafka 官方�供了多�压缩�议,包括 gzip�snappy�lz4 等等,从而�低了数�传输的�本。 + +## Kafka 中的消�是�会丢失和��消费? + +1. **Kafka 是�会丢消�,答案相信仔细看过��两个问题的�学都比较清楚了,这里就�在赘述了。** +2. **在低版本中,比如作者公�在使用的 Kafka0.8 版本中,还没有幂等性的特性的时候,消�有�能会��被存储到 Kafka 上(原因�上一个问题的),在这�情况下消�肯定是会被��消费的。** + +**这里给大家一个解决��消费的�路,作者公�使用了 Redis 记录了被消费的 key,并设置了过期时间,在 key 还没有过期内,对于�一个 key 的消�全部当���消�直接抛弃掉。** 在网上看到过�外一�解决方案,使用 HDFS 存储被消费过的消�,是�具有�行性存疑(需�读者朋�自行探索),读者朋�们�以根�自己的实际情况选择相应的策略,如果朋�们还有其他比较好的方案,欢迎留言交�。 + +## 为什么�使用 kafka,为什么�使用消�队列? + +### 先�说说为什么�使用消�队列? + +这�题比较主观一些(自认为没有网上其他文章写得�,轻喷),但是都相信大家使用消�队列无�就是为了 **解耦**�**异步**�**消峰**。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-f7c1bb87ab46ddd03255c58109ce360f_720w.webp) + +
系统调用图
+ +
+ +��业务的�展,相信有�少朋�公��到过如上图所示的情况,系统 A 处�的结构被 B�C�D 系统所�赖,当新增系统 E 时,也需�系统 A ��进行�调和上线等�作;还有当系统 A �生�更时�样需�告知 B�C�D�E 系统需��步�级改造。 + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-0f0c8f9531a38f6d79b2cbb2973bfbfc_720w.webp) + +
引入消�队列图
+ +
+ +引入消�队列�有两个好处: + +1. **�个系统进行了解耦,从上图也�以看出,当系统 A �然�生热点事件时,�一时间产生大�结果,MQ 充当了消�暂存的效果,防止 B�C�D�E 系统也跟�崩溃。** +2. **当新系统 E 需�接入系统 A 的数�,�需�和 MQ 对接就�以了,从而��了与系统 A 的调试上线等�作。** + +引入消�队列的�处: + +万事皆具备两�性,看似引入消�队列这件事情很美好,但是�时也增加了系统的��度�系统的维护�本�高(如果 MQ 挂了怎么办)�引入了一致性等等问题需�去解决。 + +## 为什么�使用 Kafka? + +作者认为采用 Kafka 的原因有如下几点: + +1. **Kafka 目�在业界被广泛使用,社区活跃度高,版本更新迭代速度也快。** +2. **Kafka 的生产者和消费者都用 Java 语言进行了�写,在一定程度�低了系统的维护�本(作者的主观��,因为当下 Java 的使用群体相当庞大)。** +3. **Kafka 系统的���高,达到了�秒 10w 级别的处�速度。** +4. **Kafka �以和很多当下优秀的大数�组件进行集�,包括 Spark�Flink�Flume�Storm 等等。** + +## 为什么 Kafka �支�读写分离? + +这个问题有个先决�件,我们�讨论 Kafka0.9 版本的情况。对于高版本,从 Partition 也�以承担读请求了,这里�多赘述。 + +Kafka 如果支�读写分离的�,有如下几个问题。 + +1. **系统设计的��度会比较大,当然这个比较牵强,毕竟高版本的 Kafka 已�实现了。** + +
+ + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/v2-98093ad82970feb7a0c52954c6942aa1_720w.webp) + +
+ +**2\. 从上图å�¯ä»¥çœ‹å‡ºï¼Œä»Žä»Ž Partition 上读å�–æ•°æ�®ä¼šæœ‰ä¸¤ä¸ªé—®é¢˜ã€‚一ã€�æ•°æ�®ä»Žä¸» Partition 上å�Œæ­¥åˆ°ä»Ž Partition 有数æ�®å»¶è¿Ÿé—®é¢˜ï¼Œå› ä¸ºæ•°æ�®ä»Žç”Ÿäº§åˆ°æ¶ˆè´¹ä¼šç»�历 3 次网络传输æ‰�能够被消费,对于时效性è¦�求比较高的场景本身就ä¸�适å�ˆäº†ã€‚二ã€�æ•°æ�®ä¸€è‡´æ€§é—®é¢˜ï¼Œå�‡è®¾ä¸» Partition 将数æ�®ç¬¬ä¸€æ¬¡ä¿®æ”¹æˆ�了 A,然å�Žå�ˆå°†è¯¥æ•°æ�®ä¿®æ”¹æˆ�了 B,由于从主 Partition å�Œæ­¥åˆ°ä»Ž Partition 会有延迟问题,所以也就会产生数æ�®ä¸€è‡´æ€§é—®é¢˜ã€‚** + +分æž�得出,通过解决以上问题æ�¥æ�¢å�–从 Partition 承担读请求,æˆ�本å�¯æƒ³è€ŒçŸ¥ï¼Œè€Œä¸”对于写入压力大,读å�–压力å°�的场景,本身也就没有什么æ„�义了。 + +## 总结 + +本文介ç»�了几个常è§�çš„ Kafka çš„é�¢è¯•题 + +### 常è§�é�¢è¯•题一览 + +#### 1.1 Kafka 中的 ISR(InSyncRepli)ã€� OSR(OutSyncRepli)ã€� AR(AllRepli)代表什么? + +ISR:速率和leader相差低于10sçš„follower的集å�ˆ + +OSR:速率和leader相差大于10sçš„follwer + +AR:所有分区的follower + +#### 1.2 Kafka 中的 HWã€� LEO 等分别代表什么? + +HW:High Water高水ä½�,根æ�®å�Œä¸€åˆ†åŒºä¸­æœ€ä½Žçš„LEO决定(Log End Offset) + +LEO:æ¯�个分区最大的Offset + +#### 1.3 Kafka 中是怎么体现消æ�¯é¡ºåº�性的? + +在æ¯�个分区内,æ¯�æ�¡æ¶ˆæ�¯éƒ½æœ‰offset,所以消æ�¯åœ¨å�Œä¸€åˆ†åŒºå†…有åº�,无法å�šåˆ°å…¨å±€æœ‰åº�性 + +#### 1.4 Kafka 中的分区器ã€�åº�列化器ã€�拦截器是å�¦äº†è§£ï¼Ÿå®ƒä»¬ä¹‹é—´çš„处ç�†é¡ºåº�是什么? + +分区器Partitioner用æ�¥å¯¹åˆ†åŒºè¿›è¡Œå¤„ç�†çš„,å�³æ¶ˆæ�¯å�‘é€�到哪一个分区的问题。åº�列化器,这个是对数æ�®è¿›è¡Œåº�列化和å��åº�列化的工具。拦截器,å�³å¯¹äºŽæ¶ˆæ�¯å�‘é€�进行一个æ��å‰�处ç�†å’Œæ”¶å°¾å¤„ç�†çš„ç±»Interceptor,处ç�†é¡ºåˆ©é¦–先通过拦截器=>åº�列化器=>分区器 + +#### 1.5 Kafka 生产者客户端的整体结构是什么样å­�的?使用了几个线程æ�¥å¤„ç�†ï¼Ÿåˆ†åˆ«æ˜¯ä»€ä¹ˆï¼Ÿ + +使用两个线程:mainå’Œsender 线程,main线程会一次ç»�过拦截器ã€�åº�列化器ã€�分区器将数æ�®å�‘é€�到RecoreAccumulator线程共享å�˜é‡�,å†�ç”±sender线程从共享å�˜é‡�中拉å�–æ•°æ�®å�‘é€�到kafka broker + +batch.size达到此规模消æ�¯æ‰�å�‘é€�,linger.ms未达到规模,等待当å‰�æ—¶é•¿å°±å�‘é€�æ•°æ�®ã€‚ + +#### 1.6 消费组中的消费者个数如果超过 topic 的分区,那么就会有消费者消费ä¸�到数æ�®â€�è¿™å�¥ è¯�是å�¦æ­£ç¡®ï¼Ÿ + +è¿™å�¥è¯�是对的,超过分区个数的消费者ä¸�会在接收数æ�®ï¼Œä¸»è¦�原因是一个分区的消æ�¯å�ªèƒ½å¤Ÿè¢«ä¸€ä¸ªæ¶ˆè´¹è€…组中的一个消费者消费。 + +#### 1.7 消费者æ��交消费ä½�移时æ��交的是当å‰�消费到的最新消æ�¯çš„ offset 还是 offset+1? + +生产者å�‘é€�æ•°æ�®çš„offset是从0开始的,消费者消费的数æ�®çš„offset是从1开始,故最新消æ�¯æ˜¯offset+1 + +#### 1.8 有哪些情形会造æˆ�é‡�å¤�消费? + +先消费å�Žæ��交offset,如果消费完宕机了,则会造æˆ�é‡�å¤�消费 + +#### 1.9 那些情景会造æˆ�消æ�¯æ¼�消费? + +å…ˆæ��交offset,还没消费就宕机了,则会造æˆ�æ¼�消费 + +#### 1.10 当你使用 kafka-topics.sh 创建(删除)了一个 topic 之å�Žï¼Œ Kafka 背å�Žä¼šæ‰§è¡Œä»€ä¹ˆé€»è¾‘? + +会在 zookeeper 中的/brokers/topics 节点下创建一个新的 topic 节点,如:/brokers/topics/first 触å�‘ Controller 的监å�¬ç¨‹åº� kafka Controller è´Ÿè´£ topic 的创建工作,并更新 metadata cache + +#### 1.11 topic 的分区数å�¯ä¸�å�¯ä»¥å¢žåŠ ï¼Ÿå¦‚æžœå�¯ä»¥æ€Žä¹ˆå¢žåŠ ï¼Ÿå¦‚æžœä¸�å�¯ä»¥ï¼Œé‚£å�ˆæ˜¯ä¸ºä»€ä¹ˆï¼Ÿ + +å�¯ä»¥å¢žåŠ ï¼Œä¿®æ”¹åˆ†åŒºä¸ªæ•°--alterå�¯ä»¥ä¿®æ”¹åˆ†åŒºä¸ªæ•° + +#### 1.12 topic 的分区数å�¯ä¸�å�¯ä»¥å‡�少?如果å�¯ä»¥æ€Žä¹ˆå‡�少?如果ä¸�å�¯ä»¥ï¼Œé‚£å�ˆæ˜¯ä¸ºä»€ä¹ˆï¼Ÿ + +ä¸�å�¯ä»¥å‡�少,å‡�少了分区之å�Žï¼Œä¹‹å‰�的分区中的数æ�®ä¸�好处ç�† + +#### 1.13 Kafka 有内部的 topic å�—?如果有是什么?有什么所用? + +有,__consumer_offsets主è¦�用æ�¥åœ¨0.9版本以å�Žä¿�存消费者消费的offset + +#### 1.14 Kafka 分区分é…�的概念? + +Kafka分区对于Kafka集群æ�¥è¯´ï¼Œåˆ†åŒºå�¯ä»¥å�šåˆ°è´Ÿè½½å�‡è¡¡ï¼Œå¯¹äºŽæ¶ˆè´¹è€…æ�¥è¯´åˆ†åŒºå�¯ä»¥æ��高并å�‘度,æ��高读å�–效率 + +#### 1.15 简述 Kafka 的日志目录结构? + +æ¯�一个分区对应ç�€ä¸€ä¸ªæ–‡ä»¶å¤¹ï¼Œå‘½å��为topic-0/topic-1…,æ¯�个文件夹内有.indexå’Œ.log文件。 + +#### 1.16 如果我指定了一个 offset, Kafka Controller 怎么查找到对应的消æ�¯ï¼Ÿ + +offset表示当å‰�消æ�¯çš„ç¼–å�·ï¼Œé¦–å…ˆå�¯ä»¥é€šè¿‡äºŒåˆ†æ³•定ä½�当å‰�消æ�¯å±žäºŽå“ªä¸ª.index文件中,éš�å�Žé‡‡ç”¨seek定ä½�的方法查找到当å‰�offset在.index中的ä½�置,此时å�¯ä»¥æ‹¿åˆ°åˆ�å§‹çš„å��ç§»é‡�。通过åˆ�å§‹çš„å��ç§»é‡�å†�通过seek定ä½�到.log中的消æ�¯å�³å�¯æ‰¾åˆ°ã€‚ + +#### 1.17 è�Šä¸€è�Š Kafka Controller 的作用? + +Kafka集群中有一个broker会被选举为Controller,负责管ç�†é›†ç¾¤broker的上下线ã€�所有topic的分区副本分é…�å’Œleader的选举等工作。Controller的工作管ç�†æ˜¯ä¾�赖于zookeeper的。 + +#### 1.18 Kafka 中有那些地方需è¦�选举?这些地方的选举策略å�ˆæœ‰å“ªäº›ï¼Ÿ + +在ISR中需è¦�选举出Leader,选择策略为先到先得。在分区中需è¦�选举,需è¦�选举出Leaderå’Œfollower。 + +#### 1.19 失效副本是指什么?有那些应对措施? + +失效副本为速率比leader相差大于10sçš„follower,ISR会将这些失效的follower踢出,等速率接近leaderçš„10s内,会é‡�新加入ISR + +#### 1.20 Kafka 的哪些设计让它有如此高的性能? + +1. Kafka天生的分布å¼�æž¶æž„ +2. 对log文件进行了分segment,并对segment建立了索引 +3. 对于å�•节点使用了顺åº�读写,顺åº�读写是指的文件的顺åº�追加,å‡�少了ç£�盘寻å�€çš„开销,相比éš�机写速度æ��å�‡å¾ˆå¤š +4. 使用了零拷è´�技术,ä¸�需è¦�切æ�¢åˆ°ç”¨æˆ·æ€�,在内核æ€�å�³å�¯å®Œæˆ�读写æ“�作,且数æ�®çš„æ‹·è´�次数也更少。 + +## å�‚考文章 +https://blog.csdn.net/cao131502 +https://zhuanlan.zhihu.com/p/137811719 \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" new file mode 100644 index 0000000..584cb61 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\345\273\266\350\277\237\351\230\237\345\210\227.md" @@ -0,0 +1,173 @@ +KafkaÊÇÒ»¸ö·Ö²¼Ê½ÏûϢϵͳ£¬±»¹ã·ºÓ¦ÓÃÓÚ»¥ÁªÍøºÍ´óÊý¾ÝÁìÓò¡£ÔÚʵ¼ÊÓ¦ÓÃÖУ¬¾­³£ÐèҪʵÏÖÑÓ³Ù¶ÓÁеŦÄÜ£¬ÒÔ±ãÔÚÒ»¶¨Ê±¼äºóÖ´ÐÐijЩÈÎÎñ»òÕß·¢ËÍijЩÏûÏ¢¡£KafkaÌṩÁ˶àÖÖ·½Ê½À´ÊµÏÖÑÓ³Ù¶ÓÁУ¬±¾ÎĽ«½éÉÜÆäÖÐÒ»ÖÖ³£¼ûµÄʵÏÖ·½·¨¡£ + + + + + +### Ò»¡¢ÑÓ³Ù¶ÓÁиÅÊö + + + + + +ÑÓ³Ù¶ÓÁÐÊÇÒ»ÖÖÓÃÓÚÔÚÒ»¶¨Ê±¼äºóÖ´ÐÐÈÎÎñ»ò·¢ËÍÏûÏ¢µÄ»úÖÆ¡£³£¼ûµÄÓ¦Óó¡¾°°üÀ¨¶¨Ê±ÈÎÎñ¡¢ÏûÏ¢ÍÆËÍ¡¢¶©µ¥³¬Ê±µÈµÈ¡£ÑÓ³Ù¶ÓÁеÄʵÏÖ·½Ê½ÓжàÖÖ£¬ÆäÖÐÒ»ÖֱȽϳ£¼ûµÄʵÏÖ·½·¨ÊÇ»ùÓÚÏûÏ¢¶ÓÁеÄÑÓ³ÙÏûÏ¢»úÖÆ¡£ + + + + + +ÔÚKafkaÖУ¬ÑÓ³ÙÏûÏ¢ÊÇÖ¸ÔÚ·¢ËÍÏûϢʱ£¬Ö¸¶¨Ò»¸öÑÓ³Ùʱ¼ä£¬ÏûÏ¢½«ÔÚÑÓ³Ùʱ¼äµ½´ïºó²Å±»Ïû·ÑÕßÏû·Ñ¡£KafkaÌṩÁËÒ»Ð©ÌØÊâµÄTopicÓÃÓÚ´æ´¢ÑÓ³ÙÏûÏ¢£¬ÀýÈç"delayed-messages"¡£Ïû·ÑÕß½ø³Ì¿ÉÒÔ¶¨ÆÚ´ÓÕâЩTopicÖÐÏû·ÑÏûÏ¢£¬²¢½«ÏûÏ¢ÖØÐ·¢Ë͵½Ä¿±êTopicÖУ¬´Ó¶øÊµÏÖÑÓ³Ù¶ÓÁеŦÄÜ¡£ + + + + + +### ¶þ¡¢KafkaÖеÄÑÓ³ÙÏûϢʵÏÖÔ­Àí + + + + + +KafkaµÄÑÓ³ÙÏûϢʵÏÖÔ­Àí±È½Ï¼òµ¥£¬Ö÷񻃾¼°µ½ÏûÏ¢µÄkeyºÍʱ¼ä´Á¡£ÔÚÏûÏ¢µÄkeyÖУ¬¿ÉÒÔÉèÖÃÒ»¸öʱ¼ä´Á£¬±íʾÏûÏ¢µÄÑÓ³Ùʱ¼ä¡£ÔÚÉú²úÕß·¢ËÍÏûϢʱ£¬½«ÏûÏ¢·¢Ë͵½"delayed-messages" TopicÖУ¬²¢ÉèÖÃÏûÏ¢µÄkeyÖеÄʱ¼ä´Á¡£Ïû·ÑÕß½ø³Ì»á¶¨ÆÚ´Ó"delayed-messages" TopicÖÐÏû·ÑÏûÏ¢£¬¼ì²éÏûÏ¢µÄkeyÖеÄʱ¼ä´ÁÊÇ·ñÒѾ­¹ýÆÚ¡£Èç¹ûʱ¼ä´ÁÒѾ­¹ýÆÚ£¬Ôò½«ÏûÏ¢ÖØÐ·¢Ë͵½Ä¿±êTopicÖУ¬ÀýÈç"target-messages"¡£Èç¹ûʱ¼ä´Á»¹Î´¹ýÆÚ£¬Ôò½«ÏûÏ¢ÖØÐ·¢Ë͵½"delayed-messages" TopicÖУ¬²¢ÉèÖÃÒ»¸öеÄÑÓ³Ùʱ¼ä´Á¡£ÕâÑù£¬¾Í¿ÉÒÔʵÏÖÑÓ³Ù¶ÓÁеŦÄÜ¡£ + + + +![image-20230526211424767](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211424767.png) + + + + + +### Èý¡¢KafkaÖÐÑÓ³Ù¶ÓÁеÄʵÏÖ²½Öè + + + + + +ʵÏÖKafkaÖеÄÑÓ³Ù¶ÓÁУ¬¿ÉÒÔ°´ÕÕÒÔϲ½Öè½øÐУº + + + + + +1.´´½¨Ò»¸öרÃŵÄTopicÓÃÓÚ´æ´¢ÑÓ³ÙÏûÏ¢£¬ÀýÈç"delayed-messages"¡£¿ÉÒÔʹÓÃKafkaÃüÁîÐй¤¾ß»òKafka API½øÐд´½¨¡£ + + + + + +2.ÔÚÏûÏ¢µÄkeyÖÐÉèÖÃÑÓ³Ùʱ¼ä´Á¡£¿ÉÒÔʹÓõ±Ç°Ê±¼ä´Á¼ÓÉÏÑÓ³Ùʱ¼ä×÷Ϊkey£¬ÀýÈ磺"key":"message_body"¡£¿ÉÒÔʹÓÃKafka API·¢ËÍÏûÏ¢µ½"delayed-messages" TopicÖС£ + + + + + +3.Æô¶¯Ò»¸öÏû·ÑÕß½ø³Ì£¬ÓÃÓÚÏû·Ñ"delayed-messages" TopicÖеÄÏûÏ¢¡£¿ÉÒÔʹÓÃKafka APIʵÏÖÏû·ÑÕß½ø³Ì¡£ + + + + + +4.ÔÚÏû·ÑÕß½ø³ÌÖУ¬¼ì²éÏûÏ¢µÄkeyÖеÄʱ¼ä´ÁÊÇ·ñÒѾ­¹ýÆÚ¡£¿ÉÒÔʹÓõ±Ç°Ê±¼ä´ÁÓëÏûÏ¢µÄkeyÖеÄʱ¼ä´Á½øÐбȽϡ£Èç¹ûʱ¼ä´ÁÒѾ­¹ýÆÚ£¬Ôò½«ÏûÏ¢ÖØÐ·¢Ë͵½Ä¿±êTopicÖУ¬ÀýÈç"target-messages"¡£¿ÉÒÔʹÓÃKafka APIʵÏÖÏûÏ¢µÄÖØÐ·¢ËÍ¡£ + + + + + +5.Èç¹ûʱ¼ä´Á»¹Î´¹ýÆÚ£¬Ôò½«ÏûÏ¢ÖØÐ·¢Ë͵½"delayed-messages" TopicÖУ¬²¢ÉèÖÃÒ»¸öеÄÑÓ³Ùʱ¼ä´Á¡£¿ÉÒÔʹÓÃKafka APIʵÏÖÏûÏ¢µÄÖØÐ·¢ËÍ£¬²¢ÔÚÏûÏ¢µÄkeyÖÐÉèÖÃеÄÑÓ³Ùʱ¼ä´Á¡£ + + + + + +6.µÈ´ýÒ»¶¨Ê±¼äºó£¬Öظ´Ö´ÐеÚ4ºÍµÚ5²½£¬Ö±µ½ÏûÏ¢µÄkeyÖеÄʱ¼ä´ÁÒѾ­¹ýÆÚ¡£ + + + + + +ͨ¹ýÒÔÉϲ½Ö裬¾Í¿ÉÒÔʵÏÖKafkaÖеÄÑÓ³Ù¶ÓÁй¦ÄÜ¡£ÐèҪעÒâµÄÊÇ£¬Ïû·ÑÕß½ø³ÌÐèÒª¶¨ÆÚ´Ó"delayed-messages" TopicÖÐÏû·ÑÏûÏ¢£¬²¢¼ì²éÏûÏ¢µÄkeyÖеÄʱ¼ä´ÁÊÇ·ñÒѾ­¹ýÆÚ¡£¿ÉÒÔ¸ù¾Ý¾ßÌåµÄÓ¦Óó¡¾°ÉèÖò»Í¬µÄÑÓ³Ùʱ¼ä¡£ + + + + + +![image-20230526211452328](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211452328.png) + + + + + +### ËÄ¡¢ÑÓ³ÙÏûϢʵÏÖµÄÓÅȱµã + + + + + +![image-20230526211506828](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526211506828.png) + + + + + +KafkaÖÐʵÏÖÑÓ³Ù¶ÓÁеķ½·¨¾ßÓÐÒÔÏÂÓŵ㣺 + + + + + +1.¿ÉÒÔÔÚ·Ö²¼Ê½ÏµÍ³ÖÐʵÏÖÑÓ³Ù¶ÓÁй¦ÄÜ£¬¾ßÓнϸߵĿÉÀ©Õ¹ÐԺͿɿ¿ÐÔ¡£ + + + + + +2.ʵÏÖ¼òµ¥£¬Ö»ÐèҪʹÓÃKafka API¼´¿É£¬ÎÞÐè¶îÍâµÄ×é¼þ»ò¹¤¾ß¡£ + + + + + +3.Ö§³ÖÏûÏ¢µÄÅúÁ¿·¢ËͺÍÏû·Ñ£¬¿ÉÒÔÌá¸ßÐÔÄܺÍÍÌÍÂÁ¿¡£ + + + + + +4.¿ÉÒÔÁé»îµØµ÷ÕûÑÓ³Ùʱ¼äºÍÄ¿±êTopic£¬ÊÊÓÃÓÚ²»Í¬µÄÓ¦Óó¡¾°¡£ + + + + + +µ«ÊÇ£¬KafkaÖÐʵÏÖÑÓ³Ù¶ÓÁÐÒ²´æÔÚһЩȱµã£º + + + + + +1.ÐèÒªÏû·ÑÕß½ø³Ì¶¨ÆÚ´Ó"delayed-messages" TopicÖÐÏû·ÑÏûÏ¢£¬Èç¹ûÏû·ÑÕß½ø³Ìå´»ú»òÕßÍ£Ö¹¹¤×÷£¬Ôò»áÓ°ÏìÑÓ³Ù¶ÓÁеŦÄÜ¡£ + + + + + +2.Ïû·ÑÕß½ø³ÌÐèÒª¶ÔÏûÏ¢½øÐÐÖØÐ·¢Ëͺͼì²é£¬ÐèÒªÏûºÄÒ»¶¨µÄ×ÊÔ´ºÍʱ¼ä¡£ + + + + + +3.ÑÓ³Ùʱ¼ä¾«¶ÈÓÐÏÞ£¬×îСֻÄÜ´ïµ½ºÁÃë¼¶±ð¡£ + + + + + +### ËÄ¡¢×ܽá + + + + + +KafkaÊÇÒ»¸ö¹¦ÄÜÇ¿´óµÄ·Ö²¼Ê½ÏûϢϵͳ£¬¿ÉÒÔʵÏÖÑÓ³Ù¶ÓÁеŦÄÜ¡£Í¨¹ýÔÚÏûÏ¢µÄkeyÖÐÉèÖÃÑÓ³Ùʱ¼ä´Á£¬½áºÏÏû·ÑÕß½ø³ÌµÄ¶¨ÆÚÏû·ÑºÍÖØÐ·¢ËÍ£¬¿ÉÒÔʵÏÖÑÓ³Ù¶ÓÁеŦÄÜ¡£KafkaÖÐʵÏÖÑÓ³Ù¶ÓÁеķ½·¨¾ßÓÐʵÏÖ¼òµ¥¡¢¿ÉÀ©Õ¹ÐԸߡ¢ÐÔÄܺõÈÓŵ㣬µ«Ò²´æÔÚһЩȱµã¡£ÔÚʵ¼ÊÓ¦ÓÃÖУ¬ÐèÒª¸ù¾Ý¾ßÌåµÄÓ¦Óó¡¾°ºÍÐèÇóÑ¡ÔñºÏÊʵÄÑÓ³Ù¶ÓÁÐʵÏÖ·½·¨¡£ \ No newline at end of file diff --git "a/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" new file mode 100644 index 0000000..5617b88 --- /dev/null +++ "b/docs/mq/kafka/\346\266\210\346\201\257\351\230\237\345\210\227kafka\350\257\246\350\247\243\357\274\232\345\246\202\344\275\225\345\256\236\347\216\260\346\255\273\344\277\241\351\230\237\345\210\227.md" @@ -0,0 +1,578 @@ +### Apache Kafka ÖÐÓÃÓÚ´íÎó´¦ÀíµÄËÀÐŶÓÁУºÀ´×Ô Uber ºÍ Crowdstrike µÄÌæ´ú·½°¸¡¢×î¼Ñʵ¼ùºÍ°¸ÀýÑо¿¡£ + + + + + +ʶ±ðºÍ´¦Àí´íÎó¶ÔÓÚÈκοɿ¿µÄÊý¾ÝÁ÷¹ÜµÀ¶¼ÊDZز»¿ÉÉٵġ£ÕâÆª²©ÎÄ̽ÌÖÁË** ÔÚ Apache Kafka »ù´¡¼Ü¹¹ÖÐ****ʹÓÃËÀÐŶÓÁÐʵÏÖ´íÎó´¦ÀíµÄ×î¼Ñʵ¼ù**¡£ÕâЩѡÏî°üÀ¨×Ô¶¨ÒåʵÏÖ¡¢Kafka Streams¡¢Kafka Connect¡¢Spring ¿ò¼ÜºÍ²¢ÐÐÏû·ÑÕß¡£ÕæÊµ°¸ÀýÑо¿Õ¹Ê¾ÁË Uber¡¢CrowdStrike ºÍɣ̹µÂÒøÐÐÈçºÎÒÔ¼«¶Ë¹æÄ£¹¹½¨¿É¿¿µÄʵʱ´íÎó´¦Àí¡£ + + + + + +Apache Kafka ³ÉΪÐí¶àÆóÒµ¼Ü¹¹×îϲ»¶µÄ¼¯³ÉÖмä¼þ¡£¼´Ê¹¶ÔÓÚÔÆÓÅÏÈÕ½ÂÔ£¬ÆóÒµÒ²¿ÉÒÔÀûÓà Kafka µÄÊý¾ÝÁ÷×÷ÎªÔÆÔ­Éú¼¯³Éƽ̨¼´·þÎñ (iPaaS)¡£ + + + + + +### Apache Kafka Êý¾ÝÁ÷ÖеÄÏûÏ¢¶ÓÁÐģʽ + + + + + +ÔÚÎÒ¿ªÊ¼ÕâÆªÎÄÕÂ֮ǰ£¬ÎÒÏëÈÃÄãÖªµÀÕâ¸öÄÚÈÝÊÇ**¹ØÓÚ¡°JMS¡¢ÏûÏ¢¶ÓÁÐºÍ Apache Kafka¡±µÄ²©¿ÍϵÁÐ**µÄÒ»²¿·Ö£º + + + + + +* JMS ÏûÏ¢´úÀíÓë Apache Kafka Êý¾ÝÁ÷µÄ**10 ¸ö±È½Ï±ê×¼** +* _**ÕâÆªÎÄÕÂ**_**¨C ͨ¹ýApache Kafka ÖеÄËÀÐŶÓÁÐ (DQL)**½øÐдíÎó´¦ÀíµÄÌæ´ú·½·¨ +* ʹÓà Apache KafkaʵÏÖ**ÇëÇó-»Ø¸´Ä£Ê½** +* _¼´½«_ÍÆ³ö¡ª¡ª**ÓÃÓÚÑ¡ÔñÕýÈ·ÏûϢϵͳµÄ¾ö²ßÊ÷**£¨JMS Óë Apache Kafka£© +* _¼´½«ÍƳö_¡ª¡ª´Ó JMS ÏûÏ¢´úÀíµ½ Apache Kafka£º**¼¯³É¡¢Ç¨ÒƺÍ/»òÌæ»»** + + + + + +### ʲôÊÇËÀÐŶÓÁм¯³Éģʽ£¨ÔÚ Apache Kafka ÖУ©£¿ + + + + + +**ËÀÐŶÓÁÐ (DLQ)**ÊÇÏûϢϵͳ»òÊý¾ÝÁ÷ƽ̨ÄÚµÄÒ»ÖÖ·þÎñʵÏÖ£¬ÓÃÓÚ**´æ´¢Î´³É¹¦´¦ÀíµÄÏûÏ¢**¡£ÏµÍ³²»ÊDZ»¶¯µØ×ª´¢ÏûÏ¢£¬¶øÊǽ«ÆäÒÆ¶¯µ½ËÀÐŶÓÁС£ + + + + + +ÆóÒµ**¼¯³Éģʽ (EIP)**¸ÄΪµ÷ÓÃÉè¼ÆÄ£Ê½ËÀÐÅͨµÀ¡£ÎÒÃÇ¿ÉÒÔ½«Á½ÕßÓÃ×÷ͬÒå´Ê¡£ + + + + + +![image-20230526224702433](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224702433.png) + + + + + +±¾ÎÄÖØµã½éÉÜÊý¾ÝÁ÷ƽ̨ Apache Kafka¡£**ÔÚ Kafka Öн«ÏûÏ¢·ÅÈë DLQ µÄ**Ö÷ÒªÔ­Òòͨ³£ÊÇÏûÏ¢¸ñʽ´íÎó»òÏûÏ¢ÄÚÈÝÎÞЧ/ȱʧ¡£ÀýÈ磬Èç¹ûÔ¤ÆÚÖµÊÇÕûÊý£¬µ«Éú²úÕß·¢ËÍÁË×Ö·û´®£¬Ôò»á·¢ÉúÓ¦ÓóÌÐò´íÎó¡£ÔÚ¸ü¶¯Ì¬µÄ»·¾³ÖУ¬¡°Ö÷Ìâ²»´æÔÚ¡±Òì³£¿ÉÄÜÊÇÎÞ·¨´«µÝÏûÏ¢µÄÁíÒ»¸ö´íÎó¡£ + + + + + +Òò´Ë£¬Í¨³£²»ÒªÊ¹ÓÃÏÖÓÐÖмä¼þ¾­ÑéÖеÄ֪ʶ¡£Message Queue Öмä¼þ£¨Èç·ûºÏ JMS µÄ IBM MQ¡¢TIBCO EMS »ò RabbitMQ£©Óë·Ö²¼Ê½Ìá½»ÈÕÖ¾£¨Èç Kafka£©µÄ¹¤×÷·½Ê½²»Í¬¡£ÓÉÓÚÐí¶àÆäËûÔ­Òò£¬ÏûÏ¢¶ÓÁÐÖÐµÄ DLQ ÓÃÓÚÏûÏ¢¶ÓÁÐϵͳ£¬ÕâЩԭÒò²»ÄÜÒ»¶ÔÒ»µØÓ³Éäµ½ Kafka¡£ÀýÈ磬MQ ϵͳÖеÄÏûÏ¢ÓÉÓÚÿÌõÏûÏ¢µÄ TTL£¨Éú´æÊ±¼ä£©¶ø¹ýÆÚ¡£ + + + + + +Òò´Ë£¬**ÔÚ Kafka Öн«ÏûÏ¢·ÅÈë DLQ µÄÖ÷ÒªÔ­ÒòÊÇÏûÏ¢¸ñʽ´íÎó»òÏûÏ¢ÄÚÈÝÎÞЧ/ȱʧ**¡£ + + + + + +### Apache Kafka ÖÐËÀÐŶÓÁеÄÌæ´ú·½°¸ + + + + + +Kafka ÖеÄËÀÐŶÓÁÐÊÇÒ»¸ö»ò¶à¸ö Kafka Ö÷Ì⣬ËüÃÇ**½ÓÊպʹ洢ÓÉÓÚ´íÎó¶øÎÞ·¨ÔÚÁíÒ»¸öÁ÷¹ÜµÀÖд¦ÀíµÄÏûÏ¢**¡£´Ë¸ÅÄîÔÊÐíʹÓÃÒÔÏ´«ÈëÏûÏ¢¼ÌÐøÏûÏ¢Á÷£¬¶ø²»»áÓÉÓÚÎÞЧÏûÏ¢µÄ´íÎó¶øÍ£Ö¹¹¤×÷Á÷¡£ + + + + + +### Kafka Broker ºÜ±¿¡ª¡ªÖÇÄܶ˵ãÌṩ´íÎó´¦Àí + + + + + +**Kafka ¼Ü¹¹²»Ö§³Ö broker** r ÖеÄDLQ¡£ÓÐÒâµØ£¬Kafka ½¨Á¢ÔÚÓëÏÖ´ú΢·þÎñÏàͬµÄÔ­ÔòÉÏ£¬Ê¹Óá°ÑƹܵÀºÍÖÇÄܶ˵㡱ԭÔò¡£Õâ¾ÍÊÇΪʲôÓ봫ͳÏûÏ¢´úÀíÏà±È£¬Kafka µÄÀ©Õ¹ÐÔÈç´ËÖ®ºÃ¡£¹ýÂ˺ʹíÎó´¦Àí·¢ÉúÔÚ¿Í»§¶ËÓ¦ÓóÌÐòÖС£ + + + + + +Êý¾ÝÁ÷ƽ̨µÄÕæÕý½âñî¿ÉÒÔʵÏÖ¸ü¸É¾»µÄÁìÓòÇý¶¯Éè¼Æ¡£**ÿ¸ö΢·þÎñ»òÓ¦ÓóÌÐò¶¼Í¨¹ý×Ô¼ºÑ¡ÔñµÄ¼¼Êõ¡¢Í¨ÐÅ·¶Ê½ºÍ´íÎó´¦ÀíÀ´ÊµÏÖÆäÂß¼­**¡£ + + + + + +ÔÚ´«Í³µÄÖмä¼þºÍÏûÏ¢¶ÓÁÐÖУ¬´úÀíÌṩÁËÕâÖÖÂß¼­¡£½á¹ûÊÇÓòÖеĿÉÀ©Õ¹ÐÔºÍÁé»îÐԽϲÒòΪֻÓÐÖмä¼þÍŶӲÅÄÜʵÏÖ¼¯³ÉÂß¼­¡£ + + + + + +### ÓÃÈκαà³ÌÓïÑÔ×Ô¶¨ÒåʵÏÖ Kafka ËÀÐŶÓÁÐ + + + + + +Kafka ÖеÄËÀÐŶÓÁжÀÁ¢ÓÚÄúʹÓõĿò¼Ü¡£Ò»Ð©×é¼þΪ´íÎó´¦ÀíºÍËÀÐŶÓÁÐÌṩÁË¿ªÏä¼´ÓõŦÄÜ¡£µ«ÊÇ£¬Ê¹ÓÃJava¡¢Go¡¢C++¡¢Python µÈ**Èκαà³ÌÓïÑÔΪ Kafka Ó¦ÓóÌÐò±àдËÀÐŶÓÁÐÂß¼­**Ò²ºÜÈÝÒס£ + + + + + +**ËÀÐŶÓÁÐʵÏÖ**µÄÔ´´úÂë°üº¬Ò»¸ö try-catch ¿éÀ´´¦ÀíÔ¤ÆÚ»òÒâÍâÒì³£¡£Èç¹ûûÓз¢Éú´íÎó£¬Ôò´¦Àí¸ÃÏûÏ¢¡£Èç¹û·¢ÉúÈκÎÒì³££¬Ç뽫ÏûÏ¢·¢Ë͵½×¨ÓÃµÄ DLQ Kafka Ö÷Ìâ¡£ + + + + + +**ʧ°ÜÔ­ÒòÓ¦Ìí¼Óµ½ Kafka ÏûÏ¢µÄ±êÍ·**ÖС£²»Ó¦¸ü¸Ä¼üºÍÖµ£¬ÒԱ㽫À´¶ÔÀúʷʼþ½øÐÐÖØÐ´¦ÀíºÍ¹ÊÕÏ·ÖÎö¡£ + + + + + +### ËÀÐŶÓÁеĿªÏä¼´Óà Kafka ʵÏÖ + + + + + +Äã²¢²»×ÜÊÇÐèҪʵÏÖÄãµÄËÀÐŶÓÁС£**Ðí¶à×é¼þºÍ¿ò¼ÜÒѾ­ÌṩÁËËüÃÇµÄ DLQ ʵÏÖ**¡£ + + + + + +ʹÓÃÄú×Ô¼ºµÄÓ¦ÓóÌÐò£¬Äúͨ³£¿ÉÒÔ¿ØÖÆ´íÎó»òÔÚ³öÏÖ´íÎóʱÐÞ¸´´úÂë¡£µ«ÊÇ£¬**Óë 3rd ·½Ó¦ÓóÌÐòµÄ¼¯³É²¢²»Ò»¶¨ÔÊÐíÄú´¦Àí¿ÉÄܿ缯³ÉÕϰ­ÒýÈëµÄ´íÎó**¡£Òò´Ë£¬DLQ ±äµÃ¸ü¼ÓÖØÒª£¬²¢±»°üº¬ÔÚijЩ¿ò¼ÜÖС£ + + + + + +### Kafka Connect ÄÚÖÃËÀÐŶÓÁÐ + + + + + +**Kafka Connect ÊÇ Kafka µÄ¼¯³É¿ò¼Ü**¡£Ëü°üº¬ÔÚ¿ªÔ´ Kafka ÏÂÔØÖС£²»ÐèÒªÆäËûÒÀÀµÏ³ýÁËÄú²¿Êðµ½ Connect ¼¯ÈºÖеÄÁ¬½ÓÆ÷±¾Éí£©¡£ + + + + + +ĬÈÏÇé¿öÏ£¬Èç¹ûÓÉÓÚʹÓÃÎÞЧÏûÏ¢¶ø·¢Éú´íÎó£¨ÀýÈçʹÓôíÎóµÄ JSON ת»»Æ÷¶ø²»ÊÇÕýÈ·µÄ AVRO ת»»Æ÷ʱ£©£¬Kafka Connect ÈÎÎñ½«Í£Ö¹¡£É¾³ýÎÞЧÏûÏ¢ÊÇÁíÒ»ÖÖÑ¡Ôñ¡£ºóÕßÈÝÈÌ´íÎó¡£ + + + + + +Kafka Connect ÖÐ DLQ µÄÅäÖúܼòµ¥¡£Ö»Ð轫Á½¸öÅäÖÃÑ¡Ïî ' errors.tolerance' ºÍ ' errors.deadletterqueue.topic.name' µÄÖµÉèÖÃΪÕýÈ·µÄÖµ£º + + + + + +![image-20230526224856544](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224856544.png) + + + + + +²©¿ÍÎÄÕ¡° Kafka Connect Deep Dive ¨C ´íÎó´¦ÀíºÍËÀÐŶÓÁС±ÏÔʾÁËʹÓà DLQ µÄÏêϸ¶¯ÊÖ´úÂëʾÀý¡£ + + + + + +**Kafka Connect ÉõÖÁ¿ÉÒÔÓÃÓÚ´¦Àí DLQ ÖеĴíÎóÏûÏ¢**¡£Ö»Ð貿ÊðÁíÒ»¸öʹÓà te DLQ Ö÷ÌâµÄÁ¬½ÓÆ÷¡£ÀýÈ磬Èç¹ûÄúµÄÓ¦ÓóÌÐò´¦Àí Avro ÏûÏ¢²¢ÇÒ´«ÈëÏûÏ¢ÊÇ JSON ¸ñʽ¡£È»ºóÁ¬½ÓÆ÷ʹÓà JSON ÏûÏ¢²¢½«Æäת»»Îª AVRO ÏûÏ¢ÒԳɹ¦ÖØÐ´¦Àí£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/61aa7624a01c5016d3ab9aaa8ee6938d5514.jpeg)Çë×¢Ò⣬Kafka Connect **ûÓÐÓÃÓÚÔ´Á¬½ÓÆ÷µÄËÀÐŶÓÁÐ**¡£ + + + + + +### Kafka Streams Ó¦ÓóÌÐòÖеĴíÎó´¦Àí + + + + + +**Kafka Streams ÊÇ Kafka µÄÁ÷´¦Àí¿â**¡£Ëü¿ÉÓëÆäËûÁ÷ʽ´«Êä¿ò¼ÜÏàæÇÃÀ£¬ÀýÈç Apache Flink¡¢Storm¡¢Beam ºÍÀàËÆ¹¤¾ß¡£µ«ÊÇ£¬ËüÊÇ Kafka Ô­ÉúµÄ¡£ÕâÒâζ×ÅÄú¿ÉÒÔÔÚµ¥¸ö¿ÉÀ©Õ¹ÇÒ¿É¿¿µÄ»ù´¡¼Ü¹¹Öй¹½¨ÍêÕûµÄ¶Ëµ½¶ËÊý¾ÝÁ÷¡£ + + + + + +Èç¹ûÄú·Ö±ðʹÓà Java£¨JVM Éú̬ϵͳ£©À´¹¹½¨ Kafka Ó¦ÓóÌÐò£¬**½¨Ò鼸ºõ×ÜÊÇʹÓà Kafka Streams ¶ø²»ÊÇ Kafka µÄ±ê×¼ Java ¿Í»§¶Ë**¡£ÎªÊ²Ã´£¿ + + + + + +* Kafka Streams¡°Ö»ÊÇ¡±Ò»¸öÎ§ÈÆ³£¹æ Java Éú²úÕߺÍÏû·ÑÕß API µÄ°ü×°Æ÷£¬ÒÔ¼°Ðí¶àÄÚÖõĸ½¼Ó¹¦ÄÜ¡£ +* Á½Õß¶¼Ö»ÊÇǶÈëµ½ Java Ó¦ÓóÌÐòÖеĿ⣨JAR Îļþ£©¡£ +* Á½Õß¶¼ÊÇ¿ªÔ´ Kafka ÏÂÔØµÄÒ»²¿·Ö - ûÓжîÍâµÄÒÀÀµÏî»òÐí¿ÉÖ¤¸ü¸Ä¡£ +* Ðí¶àÎÊÌâÒѾ­¿ªÏä¼´Óõؽâ¾ö£¬ÒÔ¹¹½¨³ÉÊìµÄÁ÷´¦Àí·þÎñ£¨Á÷¹¦ÄÜ¡¢ÓÐ״̬µÄǶÈëʽ´æ´¢¡¢»¬¶¯´°¿Ú¡¢½»»¥Ê½²éѯ¡¢´íÎó´¦ÀíµÈµÈ£©¡£ + + + + + +Kafka StreamsµÄ**ÄÚÖù¦ÄÜÖ®Ò»ÊÇĬÈϵķ´ÐòÁл¯Òì³£´¦Àí³ÌÐò**¡£ËüÔÊÐíÄú¹ÜÀíÎÞ·¨·´ÐòÁл¯µÄ**¼Ç¼Òì³£¡£**Ë𻵵ÄÊý¾Ý¡¢²»ÕýÈ·µÄÐòÁл¯Âß¼­»òδ´¦ÀíµÄ¼Ç¼ÀàÐͶ¼¿ÉÄܵ¼Ö´íÎ󡣸ù¦Äܲ»³ÆÎªËÀÐŶÓÁУ¬µ«¿ªÏä¼´Óõؽâ¾öÁËÏàͬµÄÎÊÌâ¡£ + + + + + +### Spring Kafka ºÍ Spring Cloud Stream µÄ´íÎó´¦Àí + + + + + +Spring ¿ò¼Ü¶Ô Apache Kafka ÓкܺõÄÖ§³Ö¡£ËüÌṩÁËÐí¶àÄ£°åÒÔ±ÜÃâ×Ô¼º±àдÑù°å´úÂë¡£**Spring-Kafka ºÍ Spring Cloud Stream Kafka Ö§³Ö¸÷ÖÖÖØÊԺʹíÎó´¦ÀíÑ¡Ïî**£¬°üÀ¨»ùÓÚʱ¼ä/¼ÆÊýµÄÖØÊÔ¡¢ËÀÐŶÓÁеȡ£ + + + + + +¾¡¹Ü Spring ¿ò¼Ü¹¦Äܷdz£·á¸»£¬µ«ËüÓеãÖØ£¬²¢ÇÒÓÐÒ»¸öѧϰÇúÏß¡£Òò´Ë£¬Ëü·Ç³£ÊʺÏн¨ÏîÄ¿£¬»òÕßÈç¹ûÄúÒѾ­½« Spring ÓÃÓÚÆäËû³¡¾°µÄÏîÄ¿¡£ + + + + + +ÓкܶàºÜ°ôµÄ²©¿ÍÎÄÕÂչʾÁ˲»Í¬µÄʾÀýºÍÅäÖÃÑ¡Ïî¡£»¹ÓÐÓÃÓÚËÀÐŶÓÁеĹٷ½ Spring Cloud Stream ʾÀý¡£Spring ÔÊÐíʹÓüòµ¥µÄ×¢Ê͹¹½¨Âß¼­£¬ÀýÈç DLQ¡£ÕâÖÖ±à³Ì·½·¨ÊÇһЩ¿ª·¢ÈËÔ±ÖÓ°®µÄ·¶Àý£¬¶øÁíһЩÔò²»Ï²»¶Ëü¡£Ö»ÐèÁ˽âÑ¡ÏΪ×Ô¼ºÑ¡ÔñºÏÊʵÄÑ¡Ïî¼´¿É¡£ + + + + + +### Apache Kafka ²¢ÐÐÏû·ÑÕߵĿÉÀ©Õ¹´¦ÀíºÍ´íÎó´¦Àí + + + + + +ÔÚÐí¶à¿Í»§¶Ô»°ÖУ¬ÊÂʵ֤Ã÷£¬**ÇëÇóËÀÐŶÓÁеÄÖ÷ÒªÔ­Òòͨ³£ÊÇ´¦ÀíÁ¬½Óµ½Íⲿ Web ·þÎñ»òÊý¾Ý¿âµÄʧ°Ü**¡£³¬Ê±»ò Kafka ÎÞ·¨²¢Ðз¢Ë͸÷ÖÖÇëÇó»áµ¼ÖÂijЩӦÓóÌÐò̱»¾¡£Õâ¸öÎÊÌâÓÐÒ»¸öºÜºÃµÄ½â¾ö·½°¸£º + + + + + +Apache KafkaµÄ**²¢ÐÐÏû·ÑÕß**ÊÇApache 2.0 Ðí¿ÉÏÂ**µÄ¿ªÔ´ÏîÄ¿¡£**ËüÌṩÁËÒ»¸ö´øÓпͻ§¶Ë¶ÓÁеIJ¢ÐÐ Apache Kafka ¿Í»§¶Ë°ü×°Æ÷¡¢Ò»¸ö¾ßÓÐ**¹Ø¼ü²¢·¢ÐԵĸü¼òµ¥µÄÏû·ÑÕß/Éú²úÕß API£¬**ÒÔ¼°**¿ÉÀ©Õ¹µÄ·Ç×èÈû IO**´¦Àí¡£ + + + + + +¸Ã¿âÔÊÐíÄú**ͨ¹ýµ¥¸ö Kafka Consumer ²¢Ðд¦ÀíÏûÏ¢£¬ÕâÒâζ×ÅÄú¿ÉÒÔÔÚ²»Ôö¼Ó**Òª´¦ÀíµÄÖ÷ÌâÖеķÖÇøÊýÁ¿µÄÇé¿öÏÂÔö¼Ó Kafka Consumer ²¢Ðжȡ£**¶ÔÓÚÐí¶àÓÃÀý£¬Õâͨ¹ý¼õÉÙ Kafka ´úÀíµÄ¸ºÔØÀ´**Ìá¸ßÍÌÍÂÁ¿ºÍÑÓ³Ù¡£Ëü»¹¿ª±ÙÁËеÄÓÃÀý£¬ÀýÈ缫¶Ë²¢ÐÐÐÔ¡¢ÍⲿÊý¾Ý·á¸»ºÍÅŶӡ£ + + + + + +Ò»¸ö¹Ø¼üÌØÐÔÊÇ**ÔÚµ¥¸ö Kafka Ïû·ÑÕßÓ¦ÓóÌÐòÖд¦Àí/ÖØ¸´ Web ·þÎñºÍÊý¾Ý¿âµ÷ÓÃ**¡£²¢Ðл¯±ÜÃâÁËÒ»´Î·¢Ë͵¥¸ö Web ÇëÇóµÄÐèÒª£º + + + + + +![image-20230526224910457](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224910457.png) + + + + + +**Parallel Consumer ¿Í»§¶Ë¾ßÓÐÇ¿´óµÄÖØÊÔ**Âß¼­¡£Õâ°üÀ¨¿ÉÅäÖõÄÑӳٺͶ¯Ì¬´íÎó»ò´¦Àí¡£´íÎóÒ²¿ÉÒÔ·¢Ë͵½ËÀÐŶÓÁС£ + + + + + +### ʹÓÃËÀÐŶÓÁÐÖеÄÏûÏ¢ + + + + + +**½«´íÎó·¢Ë͵½ËÀÐŶÓÁкó£¬Äú»¹Ã»ÓÐÍê³É£¡»µÏûÏ¢ÐèÒª±»´¦Àí»òÖÁÉÙ±»¼à¿Ø£¡** + + + + + +ËÀÐŶÓÁÐÊÇ**´Óʼþ´¦ÀíÖдøÍâ´¦ÀíÊý¾Ý´íÎó´¦Àí**µÄ¾ø¼Ñ·½Ê½£¬ÕâÒâζ×Å´íÎó´¦Àí³ÌÐò¿ÉÒÔÓëʼþ´¦Àí´úÂë·Ö¿ª´´½¨»òÑݱ䡣 + + + + + +´æÔÚ´óÁ¿Ê¹ÓÃËÀÐŶÓÁеĴíÎó´¦Àí²ßÂÔ¡£DO ºÍ DONT ̽Ë÷×î¼Ñʵ¼ùºÍ¾­Ñé½Ìѵ¡£ + + + + + +### ´íÎó´¦Àí²ßÂÔ + + + + + +Óм¸¸öÑ¡Ïî¿ÉÓÃÓÚ´¦Àí´æ´¢ÔÚËÀÐŶÓÁÐÖеÄÏûÏ¢£º + + + + + +* **ÖØÐ´¦Àí**£ºDLQÖеÄһЩÏûÏ¢ÐèÒªÖØÐ´¦Àí¡£µ«ÊÇ£¬Ê×ÏÈ£¬ÐèÒª½â¾öÕâ¸öÎÊÌâ¡£½â¾ö·½°¸¿ÉÒÔÊÇ×Ô¶¯½Å±¾¡¢±à¼­ÏûÏ¢µÄÈ˹¤½»»¥£¬»òÏòÉú²úÕß·µ»Ø´íÎó£¬ÒªÇóÖØÐ·¢ËÍ£¨¸üÕýµÄ£©ÏûÏ¢¡£ +* **ɾ³ý´íÎóÏûÏ¢£¨¾­¹ý½øÒ»²½·ÖÎö£©**£º¸ù¾ÝÄúµÄÉèÖ㬿ÉÄÜ»á³öÏÖ´íÎóÏûÏ¢¡£µ«ÊÇ£¬ÔÚɾ³ýËüÃÇ֮ǰ£¬ÒµÎñÁ÷³ÌÓ¦¸Ã¼ì²éËüÃÇ¡£ÀýÈ磬ÒDZí°åÓ¦ÓóÌÐò¿ÉÒÔʹÓôíÎóÏûÏ¢²¢½«ËüÃÇ¿ÉÊÓ»¯¡£ +* **¸ß¼¶·ÖÎö**£ºÁíÒ»ÖÖÑ¡ÔñÊÇ·ÖÎö´«ÈëÊý¾ÝÒÔ»ñȡʵʱ¶´²ì»òÎÊÌ⣬¶ø²»ÊÇ´¦Àí DLQ ÖеÄÿÌõÏûÏ¢¡£ÀýÈ磬һ¸ö¼òµ¥µÄ ksqlDB Ó¦ÓóÌÐò¿ÉÒÔÓ¦ÓÃÁ÷´¦Àí½øÐмÆË㣬ÀýÈçÿСʱ´íÎóÏûÏ¢µÄƽ¾ùÊýÁ¿»òÈÎºÎÆäËûÓÐÖúÓÚÈ·¶¨ Kafka Ó¦ÓóÌÐòÖеĴíÎóµÄ¼û½â¡£ +* **Í£Ö¹¹¤×÷Á÷**£ºÈç¹ûºÜÉÙ»á³öÏÖ»µÏûÏ¢£¬½á¹û¿ÉÄÜÊÇÍ£Ö¹Õû¸öÒµÎñÁ÷³Ì¡£¸Ã¶¯×÷¿ÉÒÔÊÇ×Ô¶¯µÄ£¬Ò²¿ÉÒÔÓÉÈ˾ö¶¨¡£µ±È»£¬Í£Ö¹¹¤×÷Á÷Ò²¿ÉÒÔÔÚÅ׳ö´íÎóµÄ Kafka Ó¦ÓóÌÐòÖÐÍê³É¡£Èç¹ûÐèÒª£¬DLQ ½«ÎÊÌâºÍ¾ö²ßÍⲿ»¯¡£ +* **ºöÂÔ**£ºÕâÌýÆðÀ´¿ÉÄÜÊÇ×îÔã¸âµÄÑ¡Ôñ¡£Ö»ÊÇÈÃËÀÐŶÓÁÐÌîÂú£¬Ê²Ã´¶¼²»×ö¡£È»¶ø£¬¼´Ê¹ÕâÑùÔÚijЩÓÃÀýÖÐÒ²ºÜºÃ£¬±ÈÈç¼à¿Ø Kafka Ó¦ÓóÌÐòµÄÕûÌåÐÐΪ¡£Çë¼Çס£¬Kafka Ö÷Ìâ¾ßÓб£Áôʱ¼ä£¬²¢ÇÒÔÚ¸Ãʱ¼äÖ®ºó´ÓÖ÷ÌâÖÐɾ³ýÏûÏ¢¡£Ö»ÐèΪÄúÉèÖÃÕýÈ·µÄ·½Ê½¼´¿É¡£²¢¼à¿Ø DQL Ö÷ÌâÊÇ·ñ´æÔÚÒâÍâÐÐΪ£¨ÀýÈçÌî³äÌ«¿ì£©¡£ + + + + + +### Apache Kafka ÖÐËÀÐŶÓÁеÄ×î¼Ñʵ¼ù + + + + + +ÒÔÏÂÊÇÔÚ Kafka Ó¦ÓóÌÐòÖÐʹÓÃËÀÐŶÓÁнøÐдíÎó´¦ÀíµÄһЩ**×î¼Ñʵ¼ùºÍ¾­Ñé½Ìѵ£º** + + + + + +* ¶¨Òå**´¦ÀíÎÞЧÏûÏ¢µÄÒµÎñÁ÷³Ì**£¨×Ô¶¯ÓëÈ˹¤£© + * ÏÖʵ£ºÍ¨³££¬¸ù±¾Ã»ÓÐÈË´¦Àí DLQ ÏûÏ¢ + * ±¸Ñ¡·½°¸ 1£ºÊý¾ÝËùÓÐÕßÐèÒª½ÓÊÕ¾¯±¨£¬¶ø²»½ö½öÊÇ»ù´¡¼Ü¹¹ÍÅ¶Ó + * ±¸Ñ¡·½°¸ 2£º¾¯±¨Ó¦Í¨Öª¼Ç¼ÍŶÓϵͳÊý¾Ý´íÎó£¬ËûÃǽ«ÐèÒª´Ó¼ÇÂ¼ÏµÍ³ÖØÐ·¢ËÍ/ÐÞ¸´Êý¾Ý¡£ + * Èç¹ûûÓÐÈ˹ØÐÄ»ò±§Ô¹£¬Ç뿼ÂÇÖÊÒɺÍÉó²é DLQ ´æÔڵıØÒªÐÔ¡£Ïà·´£¬ÕâЩÏûÏ¢Ò²¿ÉÒÔÔÚ³õʼ Kafka Ó¦ÓóÌÐòÖб»ºöÂÔ¡£Õâ½ÚÊ¡ÁË´óÁ¿µÄÍøÂç¸ºÔØ¡¢»ù´¡ÉèÊ©ºÍ×ʽ𡣠+* ¹¹½¨**´øÓÐÊʵ±¾¯±¨µÄÒDZí°å**²¢ÕûºÏÏà¹ØÍŶӣ¨ÀýÈ磬ͨ¹ýµç×ÓÓʼþ»ò Slack ¾¯±¨£© +* ¶¨Òåÿ¸ö Kafka Ö÷ÌâµÄ**´íÎó´¦ÀíÓÅÏȼ¶£¨Í£Ö¹¡¢É¾³ýºÍÖØÐ´¦Àí£©** +* **½ö½«²»¿ÉÖØÊԵĴíÎóÏûÏ¢ÍÆË͵½ DLQ** - Á¬½ÓÎÊÌâÊÇÏû·ÑÕßÓ¦ÓóÌÐòµÄÔðÈΡ£ +* **±£ÁôԭʼÏûÏ¢**²¢½«ËüÃÇ´æ´¢ÔÚ DLQ ÖУ¨´øÓжîÍâµÄ±êÍ·£¬ÀýÈç´íÎóÏûÏ¢¡¢´íÎóʱ¼ä¡¢·¢Éú´íÎóµÄÓ¦ÓóÌÐòÃû³ÆµÈ£©¡ª¡ªÕâʹµÃÖØÐ´¦ÀíºÍ¹ÊÕÏÅųý±äµÃ¸ü¼ÓÈÝÒס£ +* **ÏëÏëÄãÐèÒª¶àÉÙ Dead Letter Queue Kafka Ö÷Ìâ**¡£×ÜÊÇÓÐÈ¡Éá¡£µ«Êǽ«ËùÓдíÎó´æ´¢ÔÚµ¥¸ö DLQ ÖпÉÄܶԽøÒ»²½·ÖÎöºÍÖØÐ´¦ÀíûÓÐÒâÒå¡£ + + + + + +Çë¼Çס£¬**DLQ »áÒÔÓб£Ö¤µÄ˳ÐòÖÕÖ¹´¦Àí£¬²¢Ê¹ÈκÎÀàÐ͵ÄÀëÏß´¦Àí±äµÃ¸ü¼ÓÀ§ÄÑ**¡£Òò´Ë£¬Kafka DQL ²¢²»ÊʺÏÿ¸öÓÃÀý¡£ + + + + + +### ºÎʱ²»ÔÚ Kafka ÖÐʹÓÃËÀÐŶÓÁУ¿ + + + + + +ÈÃÎÒÃÇ̽ÌÖһϲ»Ó¦¸Ã½«ÄÄЩÀàÐ͵ÄÏûÏ¢·ÅÈë Kafka µÄËÀÐŶÓÁÐÖУº + + + + + +* **DLQ ÓÃÓÚ±³Ñ¹´¦Àí£¿**ÓÉÓÚ´óÁ¿ÏûÏ¢µÄ·åÖµ¶øÊ¹Óà DLQ ½øÐнÚÁ÷²¢²»ÊÇÒ»¸öºÃÖ÷Òâ¡£Kafka ÈÕÖ¾±³ºóµÄ´æ´¢»á×Ô¶¯´¦Àí±³Ñ¹¡£Ïû·ÑÕßÒÔËü¿ÉÒÔ°´×Ô¼ºµÄËÙ¶È»ñÈ¡Êý¾ÝµÄ·½Ê½ÌáÈ¡Êý¾Ý£¨»òÕßÅäÖôíÎ󣩡£Èç¹û¿ÉÄܵϰ£¬µ¯ÐÔµØÀ©Õ¹Ïû·ÑÕß¡£¼´Ê¹ÄúµÄ´æ´¢¿Õ¼äÒÑÂú£¬DLQ Ò²ÎÞ¼ÃÓÚÊ¡£ÕâÊÇËüµÄÎÊÌ⣬ÓëÊÇ·ñʹÓà DLQ Î޹ء£ +* **Á¬½Óʧ°ÜµÄDLQ£¿**ÓÉÓÚÁ¬½Óʧ°Ü¶ø½«ÏûÏ¢·ÅÈë DQL ÎÞ¼ÃÓÚÊ£¨¼´Ê¹ÔÚ¶à´ÎÖØÊÔÖ®ºó£©¡£ÒÔÏÂÏûÏ¢Ò²ÎÞ·¨Á¬½Óµ½¸Ãϵͳ¡£ÄúÐèÒª½â¾öÁ¬½ÓÎÊÌâ¡£ÏûÏ¢¿ÉÒÔ¸ù¾ÝÐèÒª´æ´¢ÔÚ³£¹æÖ÷ÌâÖУ¨È¡¾öÓÚ±£Áôʱ¼ä£©¡£ + + + + + +### ÓÃÓÚÊý¾ÝÖÎÀíºÍ´íÎóÔ¤·ÀµÄģʽע²á±í + + + + + +×îºóµ«Í¬ÑùÖØÒªµÄÊÇ£¬ÈÃÎÒÃÇ̽ÌÖÔÚijЩÇé¿öÏÂ**¼õÉÙÉõÖÁÏû³ý¶ÔËÀÐŶÓÁеÄÐèÇóµÄ¿ÉÄÜÐÔ¡£** + + + + + +**¿¨·ò¿¨**µÄSchema Registry**ÊÇÒ»ÖÖÈ·±£Êý¾ÝÇåÀíÒÔ·ÀÖ¹Éú²úÕßÔÚ¸ºÔØÖгö´íµÄ·½·¨**¡£ËüÔÚ Kafka Éú²úÕßÖÐÇ¿ÖÆÖ´ÐÐÕýÈ·µÄÏûÏ¢½á¹¹£º + +![image-20230526224926155](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526224926155.png) + + + + + +ģʽע²á±íÊÇģʽµÄ¿Í»§¶Ë¼ì²é¡£Confluent Server µÈһЩʵÏÖÔÚ´úÀí¶ËÌṩÁ˶îÍâµÄģʽ¼ì²é£¬ÒԾܾøÀ´×ÔδʹÓÃģʽע²á±íµÄÉú²úÕßµÄÎÞЧ»ò¶ñÒâÏûÏ¢¡£ + + + + + +### Kafka ËÀÐŶÓÁеݸÀýÑо¿ + + + + + +ÈÃÎÒÃÇ¿´¿´**Uber¡¢CrowdStrike ºÍ Santander Bank µÄÈý¸ö°¸ÀýÑо¿£¬ËüÃÇÔÚ Kafka »ù´¡ÉèÊ©ÖÐʵ¼Ê²¿ÊðËÀÐŶÓÁÐ**¡£Çë¼Çס£¬ÕâЩ¶¼ÊǷdz£³ÉÊìµÄÀý×Ó¡£²»ÊÇÿ¸öÏîÄ¿¶¼ÐèÒªÄÇô¸´ÔÓ¡£ + + + + + +### Uber - ¹¹½¨¿É¿¿µÄÔÙ´¦ÀíºÍËÀÐŶÓÁÐ + + + + + +ÔÚ·Ö²¼Ê½ÏµÍ³ÖУ¬ÖØÊÔÊDz»¿É±ÜÃâµÄ¡£´ÓÍøÂç´íÎ󵽸´ÖÆÎÊÌ⣬ÉõÖÁÏÂÓÎÒÀÀµ¹ØÏµµÄÖжϣ¬´ó¹æÄ£ÔËÐеķþÎñ±ØÐë×¼±¸ºÃ¾¡¿ÉÄÜÓÅÑŵØÓöµ½¡¢Ê¶±ðºÍ´¦Àí¹ÊÕÏ¡£ + + + + + +¼øÓÚ Uber µÄÔËÓª·¶Î§ºÍËÙ¶È£¬ËüµÄϵͳ±ØÐë¾ßÓÐ**ÈÝ´íÄÜÁ¦£¬²¢ÇÒÔÚÖÇÄÜʧ°ÜʱºÁ²»Í×Э**¡£Uber ½« Apache Kafka ÓÃÓÚ¸÷ÖÖ¼«¶Ë¹æÄ£µÄÓÃÀýÒÔʵÏÖÕâһĿ±ê¡£ + + + + + +ÀûÓÃÕâÐ©ÌØÐÔ£¬Uber ±£ÏÕ¹¤³ÌÍŶÓÀ©Õ¹ÁË Kafka ÔÚÆäÏÖÓÐʼþÇý¶¯¼Ü¹¹ÖеÄ×÷Óã¬Í¨¹ýʹÓà n**¸ö×èÈûÇëÇóÖØÐ´¦ÀíºÍËÀÐŶÓÁÐÀ´ÊµÏÖ½âñî¡¢¿É¹Û²ìµÄ´íÎó´¦Àí£¬¶ø²»»áÖжÏʵʱÁ÷Á¿**¡£¸Ã²ßÂÔÓÐÖúÓÚËûÃÇÑ¡Ôñ¼ÓÈëµÄ¼ÝʻԱÉ˺¦±£»¤¼Æ»®ÔÚ 200 ¶à¸ö³ÇÊпɿ¿ÔËÐУ¬²¢Îª×¢²á¼ÝʻԱ¿Û³ýÿ´ÎÐг̵ÄÿӢÀï±£·Ñ¡£ + + + + + +ÕâÊÇ Uber ´íÎó´¦ÀíµÄʾÀý¡£´íÎó»á½µµÍÖØÊÔÖ÷ÌâµÄ¼¶±ð£¬Ö±µ½µÇ½ DLQ£º + + + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225030750.png) + + + + + +Óйظü¶àÐÅÏ¢£¬ÇëÔĶÁ Uber ·Ç³£ÏêϸµÄ¼¼ÊõÎÄÕ£º¡°Ê¹Óà Apache Kafka ¹¹½¨¿É¿¿µÄÔÙ´¦ÀíºÍËÀÐŶÓÁС±¡£ + + + + + +### CrowdStrike - ´¦ÀíÊýÍòÒÚʼþµÄ´íÎó + + + + + +CrowdStrike ÊÇÒ»¼ÒλÓڵ¿ËÈøË¹ÖݰÂ˹͡µÄ**ÍøÂ簲ȫ¼¼Êõ¹«Ë¾¡£**ËüÌṩ**ÔÆ¹¤×÷¸ºÔغͶ˵㰲ȫ¡¢ÍþвÇ鱨ºÍÍøÂç¹¥»÷ÏìÓ¦·þÎñ**¡£ + + + + + +CrowdStrike µÄ»ù´¡ÉèÊ©** ÿÌìʹÓà Apache Kafka ´¦ÀíÊýÍòÒÚ¸öʼþ**¡£ÔÚÎҵġ° Apache Kaka ÍøÂ簲ȫ²©¿ÍϵÁС±ÖУ¬ÎÒ½éÉÜÁËÒÔÈκιæÄ£ÊµÊ±´´½¨Ì¬ÊƸÐÖªºÍÍþвÇ鱨µÄÏà¹ØÓÃÀý¡£ + + + + + +CrowdStrike ¶¨ÒåÁËÈý¸ö×î¼Ñʵ¼ù À´³É¹¦ÊµÏÖËÀÐŶÓÁкʹíÎó´¦Àí£º + + + + + +* **ÔÚÕýÈ·µÄϵͳÖд洢´íÎóÏûÏ¢**£º¶¨Òå»ù´¡ÉèÊ©ºÍ´úÂëÒÔ²¶»ñºÍ¼ìË÷ËÀÐÅ¡£CrowdStrike ʹÓà S3 ¶ÔÏó´æ´¢À´´æ´¢Ç±ÔڵĴóÁ¿´íÎóÏûÏ¢¡£Çë×¢Ò⣬Kafka µÄ·Ö²ã´æ´¢¿ªÏä¼´Óõؽâ¾öÁËÕâ¸öÎÊÌ⣬ÎÞÐèÆäËû´æ´¢½Ó¿Ú£¨ÀýÈ磬ÀûÓà Confluent Cloud ÖеÄÎÞÏÞ´æ´¢£©¡£ +* **ʹÓÃ×Ô¶¯»¯**£º·ÅÖù¤¾ßÒÔʹÐÞ¸´ÍòÎÞһʧ£¬ÒòΪÊÖ¶¯Íê³É´íÎó´¦Àí¿ÉÄܷdz£ÈÝÒ׳ö´í¡£ +* **¼Ç¼ҵÎñÁ÷³Ì²¢Æ¸ÇëÏà¹ØÍŶÓ**£º±ê×¼»¯ºÍ¼Ç¼Á÷³ÌÒÔÈ·±£Ò×ÓÚʹÓᣲ¢·ÇËùÓй¤³Ìʦ¶¼ÊìϤ×éÖ¯´¦ÀíËÀÐÅÏûÏ¢µÄ²ßÂÔ¡£ + + + + + +ÔÚ**Ïñ CrowdStrike ÕâÑùµÄÍøÂ簲ȫƽ̨ÖУ¬´ó¹æÄ£ÊµÊ±Êý¾Ý´¦ÀíÖÁ¹ØÖØÒª**¡£´ËÒªÇóÒ²ÊÊÓÃÓÚ´íÎó´¦Àí¡£**ÏÂÒ»´ÎÍøÂç¹¥»÷¿ÉÄÜÊǹÊÒâ°üº¬²»Êʵ±»òÎÞЧÄÚÈݵĶñÒâÏûÏ¢**£¨Èç JavaScript ©¶´ÀûÓã©¡£Òò´Ë£¬±ØÐëͨ¹ýËÀÐŶÓÁÐʵʱ´¦Àí´íÎó¡£ + + + + + +### ɣ̹µÂÒøÐÐ - ÓÃÓÚÖØÊÔºÍ DLQ ×éºÏµÄÓÊÏä 2.0 + + + + + +ɣ̹µÂÒøÐÐ**ÔÚÓÊÏäÓ¦ÓóÌÐòÖд¦Àíº£Á¿Êý¾ÝµÄͬ²½Êý¾Ý´¦ÀíÃæÁÙ¾Þ´óÌôÕ½**¡£ËûÃÇÖØÐ¼ܹ¹ÁËËûÃǵĻù´¡¼Ü¹¹²¢¹¹½¨ÁËÒ»¸ö½âñîÇÒ¿ÉÀ©Õ¹µÄ¼Ü¹¹£¬³ÆÎª¡°Santander Mailbox 2.0¡±¡£ + + + + + +Santander µÄ¹¤×÷¸ºÔز¢×ªÒƵ½**ÓÉ Apache Kafka Ìṩ֧³ÖµÄʼþËÝÔ´**£º + + + + + +![image-20230526225045196](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225045196.png) + + + + + +еĻùÓÚÒ첽ʼþµÄ¼Ü¹¹ÖеÄÒ»¸ö¹Ø¼üÌôÕ½ÊÇ´íÎó´¦Àí¡£** Santander ʹÓÃÖØÊÔºÍ DQL Kafka Ö÷Ìâ¹¹½¨µÄ´íÎó´¦Àí**½â¾öÁËÕâЩÎÊÌ⣺ + + + + + +![image-20230526225057151](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/image-20230526225057151.png) + + + + + +²é¿´À´×Ô Santander µÄ¼¯³ÉºÏ×÷»ï°é ConsdataµÄ Kafka ·å»áÑݽ²¡°»ùÓÚÖØÊÔ²ßÂÔºÍËÀÐÅÖ÷ÌâµÄ Apache Kafka ÖеĿɿ¿Ê¼þ´«µÝ¡±ÖеÄÏêϸÐÅÏ¢¡£ + + + + + +### Apache Kafka Öпɿ¿ÇÒ¿ÉÀ©Õ¹µÄ´íÎó´¦Àí + + + + + +**´íÎó´¦Àí¶ÔÓÚ¹¹½¨¿É¿¿µÄÊý¾ÝÁ÷¹ÜµÀºÍƽ̨ÖÁ¹ØÖØÒª**¡£´æÔÚ²»Í¬µÄÌæ´ú·½°¸À´½â¾öÕâ¸öÎÊÌâ¡£¸Ã½â¾ö·½°¸°üÀ¨ËÀÐŶÓÁеÄ×Ô¶¨ÒåʵÏÖ»òÀûÓÃÕýÔÚʹÓõĿò¼Ü£¬ÀýÈç Kafka Streams¡¢Kafka Connect¡¢Spring ¿ò¼Ü»ò Kafka µÄ²¢ÐÐÏû·ÑÕß¡£ + + + + + +ÓŲ½¡¢CrowdStrike ºÍɣ̹µÂÒøÐеݸÀýÑо¿±íÃ÷£¬´íÎó´¦Àí²¢²»×ÜÊǺÜÈÝÒ×ʵÏÖ¡£µ±ÄúÉè¼ÆÐµÄÓ¦ÓóÌÐò»ò¼Ü¹¹Ê±£¬ÐèÒª´ÓÒ»¿ªÊ¼¾Í¿¼Âǵ½ÕâÒ»µã¡£**ʹÓà Apache Kafka ½øÐÐʵʱÊý¾ÝÁ÷´«ÊäºÜÓÐÎüÒýÁ¦£¬µ«Ö»ÓÐÔÚÄúÄܹ»´¦ÀíÒâÍâÐÐΪʱ²ÅÄܳɹ¦**¡£ËÀÐŶÓÁÐÊÇÐí¶à³¡¾°µÄ¾ø¼ÑÑ¡Ôñ¡£ \ No newline at end of file diff --git a/pom.xml b/pom.xml index de1628d..a91c75e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,8 +7,28 @@ groupId JavaTutorial 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 3.0.5 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + com.github.houbb markdown-toc @@ -51,5 +71,13 @@ + + + + org.springframework.boot + spring-boot-maven-plugin + + + \ No newline at end of file diff --git a/src/main/java/Test.java b/src/main/java/Test.java new file mode 100644 index 0000000..269694c --- /dev/null +++ b/src/main/java/Test.java @@ -0,0 +1,22 @@ +import org.springframework.beans.factory.annotation.Lookup; +import org.springframework.stereotype.Service; + +/** + * @author hpl + * @date 2023/4/15 14:40 + */ +@Service +public class Test { + + + + + public static void main(String[] args) { + + } + + + public void test(){ + + } +} diff --git a/src/main/java/md/mdToc.java b/src/main/java/md/mdToc.java index 05650b7..2fb42bd 100644 --- a/src/main/java/md/mdToc.java +++ b/src/main/java/md/mdToc.java @@ -4,7 +4,7 @@ public class mdToc { public static void main(String[] args) { - String path = "D:\\idea_project\\javaTutorial\\docs\\big-backEnd\\temp"; + String path = "D:\\idea_project\\JavaTutorial\\docs\\java-web\\temp"; AtxMarkdownToc.newInstance().genTocDir(path); } }