• Spring系列(2):Spring框架


    一、Spring定义

    Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。

    Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一栈式) 轻量级开源框架。

    Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

    目的:解决企业应用开发的复杂性
    功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
    范围:任何Java应用
    
    优点:
        1.低侵入式设计,代码污染极低,【Spring设计为非侵入式的,意味着你的逻辑代码不依赖与框架本身。】
        2.独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺
        3.Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦【方便解耦,简化开发】
        4.Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用【AOP编程的支持,声明式事务的支持】
        5.Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问
        6.Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部
        7.方便程序的测试,Spring对Junit4支持,可以通过注解方便的测试Spring程序。
        8.方便集成各种优秀框架。
    • 不使用事务APIs写代码就让一个方法有事务处理功能
    • 不使用remote APIs就可以让本地方法访问远程程序
    • 不使用JMX APIs就可以让一个本地方法管理操作
    • 不使用JMS APIs就可以让本地代码处理消息
    特点:
    • Core technologies: dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL, AOP.
    • Testing: mock objects, TestContext framework, Spring MVC Test, WebTestClient.
    • Data Access: transactions, DAO support, JDBC, ORM, Marshalling XML.
    • Spring MVC and Spring WebFlux web frameworks.
    • Integration: remoting, JMS, JCA, JMX, email, tasks, scheduling, cache.
    • Languages: Kotlin, Groovy, dynamic languages.

     排疑

      JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

      JMS(Java Message Service,即Java消息服务)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API。

      JCA (J2EE 连接器架构,Java Connector Architecture)是对J2EE标准集的重要补充。因为它注重的是将Java程序连接到非Java程序和软件包中间件的开发。连接器特指基于Java连接器架构的源适配器

    二、Spring框架特点

      Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
      Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
      轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
      控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。【工厂模式】
      面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。【动态代理】
      容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
      框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。    
      MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
      所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
     

    三、Spring框架

    4.x之前

    Spring 框架是一个分层架构,由 7 个定义良好的模块组成。
    
    Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
        1、Spring Core:核心容器提供 Spring 框架的基本功能(Spring Core)。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。它的BeanFactory消除了应用对Singleton和Factory的依赖。
        2、Spring Context:Spring 上下文是一个配置文件,向 Spring框架提供上下文信息。Spring 上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
        3、Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
        4、Spring DAO:JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
        5、Spring ORM:Spring 框架插入了若干个ORM框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatisSQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
        6、Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。提供了面向Web的综合特性,如使用Servlet监听器的Context初始化、文件上传等功能。此外它也用于同其他Web框架的集成。
        7、Spring MVC 框架:MVC框架是一个全功能的构建 Web应用程序的 MVC 实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI模型由javabean构成,存放于Map;视图是一个接口,负责显示模型;控制器表示逻辑代码,是Controller的实现。Spring框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE 环境(Web 或EJB)、独立应用程序、测试环境之间重用。

    下面这张图来自spring4.x的文档。目前最新的5.x版本中右面的portlet组件已经被废弃掉,同时增加了用于异步响应式处理的WebFlux组件。

      依赖关系如下:

      

    1 核心容器
      核心容器由spring-core、spring-beans、spring-context、spring-context和spring-expression 5个模块构成。
      spring-core和spring-beans模块为框架提供最基本的功能,包括IoC和依赖注入。BeanFactory是一个工厂的实现类【工厂模式】。它可以免除用户手动创建单例类并且对象之间解耦。
      context(spring-core)模块基于spring-beans和BeanFactory两个模块,允许以框架方式访问对象,这类似于JNDI。Context模块继承了Beans模块,并支持国际化、事件传播、资源加载等。Context模块支持Java EE特性,如EJB、JMX、远程访问。spring-context接口是Contet模块的重点。spring-context-support提供将第三方库集成到Spring的功能,如缓存(EhCache,JCache)和任务调度(CommonJ,Quartz)等。
      spring-expression模块提供强有力的在运行时查询和操作对象的语言。这种语言支持获取和设置属性、方法执行、获取数组或集合中的对象、逻辑计算、命名变量,在Spring的IoC容器中获得对象。
    2 AOP 和 Instrumentation
      spring-aop模块提供切面编程的实现。可以自定义方法拦截和切入点。
      spring-aspects模块提供与AspectJ的集成。
      spring-instrument模块为特定的服务器提供类加载服务。spring-instrument模块是集成了Tomcat。
    3 消息
      Spring4提供了spring-messaging模块,主要类有Message,MessageChannel,MessageHandler。这个模块还包含一些映射消息到方法的注解,类似于Spring MVC基于编程模式的注解。
    4 数据访问/集成
      这一层由JDBC、ORM、OXM、JMS、和事物模块组成。
      spring-jdbc模块,主要为了解决数据库繁多的问题,应用此可不需要关注使用的数据库。
      spring-tx模块提供编程式或声明式事务处理。
      spring-orm模块提供流行的对象关系映射的APIs,包含JPA和Hibernate.
      spring-oxm模块提供对Object/XML映射的支持,例如JAXB,Castor,JiBX和XStream。
      spring-jms模块(Java消息服务)包含生成和消费消息的功能。在Spring4.1以后,它集成了spring-messaging模块。
    5 Web
      Web层包含spring-web、spring-webmvc和spring-websocket 3个模块。
      spring-web模块提供面向Web方法的集成特性,例如多部分文件上传、通过监听初始化IoC容器和面向Web的Context,还包含HTTP客户端和对远程的支持。
      spring-webmvc模块(也被称作Web-Servlet模块)包含Spring MVC框架。
      spring-websocket模块提供对socket的全面支持6 测试
      spring-test模块通过JUnit或者TestNG来对Spring的模块进行单元测试和集成测试。它提供一致的Spring 的ApplicationContexts 和context的缓存。它还提供mock对象让你测试你的代码。

    四、Spring机制与实现

      https://blog.csdn.net/werqerwer/article/details/88354616

    4.1 AOP

      AOP:AOP的实现是通过代理模式,在调用对象的某个方法时,执行插入的切面逻辑。实现的方式有动态代理也叫运行时增强,比如jdk代理、CGLIB静态代理是在编译时进行织入或类加载时进行织入,比如AspectJ。关于AOP还需要了解一下对应的Aspect、pointcut、advice等注解和具体使用方式。 

    Advice(通知、切面): 某个连接点所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。
      1.1 @Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
      1.2 @After: final增强,不管是抛出异常或者正常退出都会执行.
      1.3 @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行.
      1.4 @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
      1.5 @Around: 环绕增强,相当于MethodInterceptor.
    JointPoint(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。
    Pointcut(切入点): JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。
    Advisor(增强): 是PointCut和Advice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发。
    @Aspect(切面): 通常是一个类的注解,里面可以定义切入点和通知
    AOP Proxy:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。

    4.2 placeHolder动态替换

      placeHolder动态替换主要需要了解替换发生的时间,是在bean definition创建完成后,bean初始化之前,是通过实现BeanFactoryPostProcessor接口实现的。主要实现方式有PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer。这两个类实现逻辑不一样,spring boot使用PropertySourcesPlaceholderConfigurer实现。

      它们和应用的逻辑无关,只和当前环境、当前系统用户相关。(不能硬编码)      

    • 数据库服务器IP地址、端口、用户名;
    • 用来保存上传资料的目录。
    • 一些参数,诸如是否打开cache、加密所用的密钥名称等等。

    4.3 事务

    事务需要了解spring 中对事务规定的隔离类型和事务传播类型。要知道事务的隔离级别是由具体的数据库来实现的。事务的传播类型,可以重点了解最常用的REQUIRED和SUPPORTS类型。

    1. 两大事务管理方式

      spring支持编程式事务管理声明式事务管理两种方式。

            编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

            声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

           显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

            声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

     

    Spring配置声明式事务:
        * 配置DataSource
        * 配置事务管理器
        * 事务的传播特性
        * 那些类那些方法使用事务
    
    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
    
        DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问 时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。
    
    根据代理机制的不同,Spring事务的配置又有几种不同的方式:
        第一种方式:每个Bean都有一个代理
        第二种方式:所有Bean共享一个代理基类
        第三种方式:使用拦截器
        第四种方式:使用tx标签配置的拦截器
        第五种方式:全注解

      


    2. 五大隔离级别

    1. ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应【默认】
    2. ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读【隔离级别最低,并发性能高 】
    3. ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。【锁定正在读取的行】
    4. ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。【锁定所读取的所有行】
    5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。【锁表】

    3. 七大事务传播类型

    1. PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。【默认】
    2. PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
    3. PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
    4. PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
    5. PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
    6. PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
    7. PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
    如果子事务回滚,父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
    如果父事务回滚,子事务也会跟着回滚。因为父事务结束之前,子事务是不会提交的,因为子事务是父事务的一部分。
    事务的提交:子事务先提交,父事务再提交。
    子事务是父事务的一部分,由父事务统一提交。

    4. 事务超时

          所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务

      在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

      默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

    5. 事务只读属性

          只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。默认为读写事务。

      “只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可

      “只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。 但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。 

    6. spring事务回滚规则

       Spring、EJB的声明式事务默认情况下都是在抛出unchecked exception后才会触发事务的回滚

       unchecked异常,即运行时异常runntimeException 回滚事务;

       checked异常,即Exception可try{}捕获的不会回滚.当然也可配置spring参数让其回滚. 

    指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。
    
    spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
    默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚
    可以明确的配置在抛出那些异常时回滚事务,包括checked异常。 也可以明确定义那些异常抛出时不回滚事务。 还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

    让Exception异常也进行回滚操作,在调用该方法前加上: @Transactional(rollbackFor = Exception.class)
    让RuntimeException不进行回滚操作,在调用该方法前加上: @Transactional(rollbackFor = RuntimeException.class)
    在整个方法运行前就不会开启事务: @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率

    4.4 4大核心接口/类

    1. ApplicationContext保存了ioc的整个应用上下文,可以通过其中的beanfactory获取到任意到bean;
    2. BeanFactory主要的作用是根据bean definition来创建具体的bean;
    3. BeanWrapper是对Bean的包装,一般情况下是在spring ioc内部使用,提供了访问bean的属性值、属性编辑器注册、类型转换等功能,方便ioc容器用统一的方式来访问bean的属性;
    4. FactoryBean通过getObject方法返回实际的bean对象,例如motan框架中referer对service的动态代理就是通过FactoryBean来实现的。

    4.5 7种Scope

    Scopebean的scope是指bean的作用域,request、session、global-session是在web服务中使用的scope

    1. singleton默认情况下是单例模式,这也是使用最多的一种方式;
    2. prototype多例模式,即每次从beanFactory中获取bean都会创建一个新的bean。
    3. request每次请求都创建一个实例,
    4. session是在一个会话周期内保证只有一个实例。
    5. global-session在5.x版本中已经不在使用,
    6. Application保证在一个ServletContext中只创建一个实例。
    7. Websocket保证在一个WebSocket中只创建一个实例。

    4.6 5大事件机制

    1、基本概念

        1)事件驱动模型

        当事件被触发的时候,将事件加入一个事件队列,然后通过主程序不断轮训事件队列,处理目标函数。常见的事件驱动如鼠标点击事件、IO事件等,观察者设计模式就是事件驱动的一个很好实现。

        2)消息驱动模型/发布订阅模型

        本质上讲,事件驱动和消息驱动相当,只是各自应用在不同的场景下。事件模式耦合高,同模块内好用;消息模式耦合低,跨模块好用。

    2、五大标准事件

      https://blog.csdn.net/u014746965/article/details/78480159

     在spring中提供的标准事件:

        1)ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。

        2)ContextStartedEvent,当ApplicationContext启动的时候发布事件,即调用ConfigurableApplicationContext接口的start方法的时候

        3)ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候

        4)ContextClosedEvent,当ApplicationContext关闭的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候,关闭指的是所有的单例Bean都被销毁。

        5)equestHandledEvent,只能用于DispatcherServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。

    3、Spring事件机制实现

        事件,ApplicationEvent,继承自EventObject。

        事件监听器,ApplicationListener,是一个接口,继承自EventListener,实际中需要实现其onApplicationEvent方法。

        事件发布,ApplicationEventPublisher,是一个接口,包含publishEvent()方法,ApplicationContext继承了该接口,在AbstractApplicationContext中实现里事件的发布接口。

     Spring是如何通过事件找到对应的监听器的呢?Spring利用反射机制通过getBeansOfType获取所有继承了ApplicationListener接口的监听器,然后把监听器放到注册表中

    五.Spring应用

    5.1 常用注释

    a.类型类注释:

    @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。

    @Service 标注业务层组件,但是目前该功能与 @Component 相同。自动根据bean的类名实例化一个首写字母为小写的bean,例如Chinese实例化为chinese,如果需要自己改名字则:@Service("你自己改的bean名")。  

    @Constroller 标注控制层组件,但是目前该功能与 @Component 相同。

    @Repository 标注持久层组件,用于标注数据访问组件,即DAO组件,标识为 Spring Bean。

    @Configuration 用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)

    @Bean 标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的<bean>,作用为:注册bean对象

    1. 在目前的 Spring 版本中,@Constroller,@Service ,@Repository这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。
    2. 为了让 Spring 能够扫描类路径中的类并识别出注解,需要在 XML 配置文件中启用Bean 的自动扫描功能,这可以通过<context:component-scan/>实现。
    3. @Configuration注解的配置类有如下要求:
        @Configuration不可以是final类型;
        @Configuration不可以是匿名类;
        嵌套的configuration必须是静态类。
    4. 其中component和bean注解的区别如下:
        @Component注解在类上使用表明这个类是个组件类,需要Spring为这个类创建bean。
        @Bean注解使用在方法上,告诉Spring这个方法将会返回一个Bean对象,需要把返回的对象注册到Spring的应用上下文中 @Configuration。

    b.设置类注解

    @Require 注解作用于Bean的 setter 方法上,用于检查一个Bean的属性的值在配置期间是否被赋予或设置(populated),否则,容器会抛出一个BeanInitializationException异常。

    @Autowire和@Resource都是Spring支持的注解方式动态装配bean。作用范围在字段上,均无需在写setter方法

    @Autowire默认按照类型(by-type)装配,默认情况下要求依赖对象必须存在。如果使用按照名称(by-name)装配,需结合@Qualifier注解使用,即

    @Scope注解是springIoc容器中的一个作用域

    1. @Autowire默认按照类型(by-type)装配,默认情况下要求依赖对象必须存在。
        如果允许依赖对象为null,需设置required属性为false,即
        @Autowire(required=false)
        private InjectionBean beanName;
    
        如果使用按照名称(by-name)装配,需结合@Qualifier注解使用,即
        @Autowire
        @Qualifier("beanName")
        private InjectionBean beanName;
    
        说明
        @Autowire按照名称(by-name)装配,则
        @Autowire + @qualifier("") = @Resource(name="")
    
    2. @Resource
      @Resource默认按照名称(by-name)装配,名称可以通过name属性指定。如果没有指定name:1.当注解在字段上时,默认取name=字段名称装配。2.当注解在setter方法上时,默认取name=属性名称装配。当按照名称(by-name)装配未匹配时,按照类型(by-type)装配。当显示指定name属性后,只能按照名称(by-name)装配。
      @Resoure装配顺序
      1. 如果同时指定name和type属性,则找到唯一匹配的bean装配,未找到则抛异常;
      2. 如果指定name属性,则按照名称(by-name)装配,未找到则抛异常;
      3. 如果指定type属性,则按照类型(by-type)装配,未找到或者找到多个则抛异常;
      4. 既未指定name属性,又未指定type属性,则按照名称(by-name)装配;如果未找到,则按照类型(by-type)装配。
      

      https://blog.csdn.net/xxliuboy/article/details/86441832

    3. @Scope a.singleton单例模式 -- 全局有且仅有一个实例【默认】 b.prototype原型模式 -- 每次获取Bean的时候会有一个新的实例 c.request -- request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效 d.session -- session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效 e.globalsession -- global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义
      
       @Scope(value = "prototype")

    c.web类注解

    路径匹配注解

    @GetMapping:用于将Http Get 请求映射到特定处理程序方法的注释。具体来说就是:@GetMapping是一个作为快捷方式的组合注释 @RequestMapping(method = RequestMethod.GET)。

    @PostMapping:用于将Http Post 请求映射到特定处理程序方法的注释。具体来说就是:@PostMapping是一个作为快捷方式的组合注释@RequestMapping(method = RequestMethod.POST)。

    @RequestMapping:一般情况下都是用@RequestMapping(method = RequestMethod),因为@RequestMapping可以直接替代以上两个注解,但是以上两个注解并不能替代@RequestMapping。

    类似组合注解还有:@PutMapping、@DeleteMapping、@PatchMapping

    总结下来就是@PostMapping和@GetMapping都可以用@RequestMapping代替,一般可以统一写成@RequestMapping,但是不利于其他人对代码的阅读和理解!还是建议分开写。

    参数获取注解

    @PathVaribale 获取url中的数据

    @RequestParam 获取请求参数的值

    @ResponseBody: responseBody表示服务器返回的时候以一种什么样的方式进行返回, 将内容或对象作为 HTTP 响应正文返回,值有很多,一般设定为json

    @RequestBody:一般是post请求的时候才会使用这个请求,把参数丢在requestbody里面

    @RequestMapping("/hello/{id}")
        public String getDetails(@PathVariable(value="id") String id,
        @RequestParam(value="param1", required=true) String param1,
        @RequestParam(value="param2", required=false) String param2){
    .......
    }
     
    @PathParam:这个注解是和spring的pathVariable是一样的,也是基于模板的,但是这个是jboss包下面的一个实现,上面的是spring的一个实现,都要导包
    @QueryParam:@QueryParam 是 JAX-RS 本来就提供的,和Spring的RequestParam作用一致

    d.功能类注解

    引用配置

    @Import注解是引入带有@Configuration的java类。

    @ImportResource是引入spring配置文件.xml

    自动扫描注解

    @ComponentScan: 扫描指定包下的Component(@Componment,@Configuration,@Service 等等)

    缓存

    @EnableCaching: 管理Cache对象注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。如果你使用了这个注解,那么你就不需要在XML文件中配置cache manager了,等价于 <cache:annotation-driven/>

    @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。@Cacheable可以标记在一个方法上(该方法是支持缓存的),也可以标记在一个类上(该类所有的方法都是支持缓存的)。

    事务
    @Transactional可以在service类或方法前加上@Transactional,在service类上声明@Transactional,service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。

    AOP

    @Aspect(切面): 通常是一个类的注解,里面可以定义切入点和通知

    @Pointcut(切入点): JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。

    定时任务
    @Scheduled:定时任务

    @ComponentScan
      扫描com.jdon.springboot2的整个父树。@ComponentScan(“com.jdon.springboot2”)   
      使用数组定义两个特定的组件扫描。@ComponentScan({“com.jdon.springboot2.abc”,”com.jdon.springboot2.efg”})

    @Transactional

     

    @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,也可以在方法级别使用该标注来覆盖类级别的定义。

     虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。

    @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

     默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

    @Aspect

    Advice(通知、切面): 某个连接点所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。
      1.1 @Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
      1.2 @After: final增强,不管是抛出异常或者正常退出都会执行.
      1.3 @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行.
      1.4 @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
      1.5 @Around: 环绕增强,相当于MethodInterceptor.
    JointPoint(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。
    Pointcut(切入点): JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。
    Advisor(增强): 是PointCut和Advice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发。
    @Aspect(切面): 通常是一个类的注解,里面可以定义切入点和通知
    AOP Proxy:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。
    @Scheduled
      在启动类上面加上 @EnableScheduling 注解即可开启定时任务
      @EnableScheduling
      @SpringBootApplication
        public class SchedulingApplication {
    
            public static void main(String[] args) {
                SpringApplication.run(SchedulingApplication.class, args);
            }
        }
         
    
        @Scheduled(fixedDelay = 1000 * 10,initialDelay=1000*15)
        public void Task() { 
         
        }
    
        @Scheduled(fixedRate=2000):上一次开始执行时间点后2秒再次执行;
        @Scheduled(fixedDelay=2000):上一次执行完毕时间点后2秒再次执行;
        @Scheduled(initialDelay=1000, fixedDelay=2000):第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行;
        @Scheduled(cron="* * * * * ?"):按cron规则执行。
    
    
        "0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点 
        "0 0/30 9-17 * * ?"   朝九晚五工作时间内每半小时
        "0 0 12 ? * WED" 表示每个星期三中午12点
        "0 0 12 * * ?" 每天中午12点触发
        "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
        "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
        "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
        "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
        "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
        "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
        "0 15 10 15 * ?" 每月15日上午10:15触发
        "0 15 10 L * ?" 每月最后一日的上午10:15触发
        "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
        "0 15 10 ? * 6L 20014-20018" 2014年至2018年的每月的最后一个星期五上午10:15触发
        "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
        "0 15 10 ? * *" 每天上午10:15触发
        "0 15 10 * * ?" 每天上午10:15触发
        "0 15 10 * * ? *" 每天上午10:15触发
        "0 15 10 * * ? 2017" 2017年的每天上午10:15触发
        
        原文链接:https://blog.csdn.net/LDY1016/article/details/84326199

    5.2 配置方式

    spring的三种方式,

      1. xml文件配置、

      2. 注解配置:隐式的bean发现机制和自动装配(@Component @Resource @Autowire @ComponentScan)和使用

      3. Java代码装配bean(@Configuration, @Bean)进行配置。

    1、Explicit configuration in XML:显示的XML配置。
        优点:
             1)XML配置方式进一步降低了耦合,使得应用更加容易扩展,即使对配置文件进一步修改也不需要工程进行修改和重新编译。
             2)在处理大的业务量的时候,用XML配置应该更加好一些。
        缺点:
             1)配置文件读取和解析需要花费一定的时间,配置文件过多的时候难以管理。
             2)无法对配置的正确性进行校验,增加了测试难度。
        举个例子:
            1、配置application.xml文件(名字随意)
            2、声明命名空间   xmlns="http://www.springframework.org/schema/beans 
            3、配置bean
            <?xml version="1.0" encoding="UTF-8"?>
            <beans xmlns="http://www.springframework.org/schema/beans"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
             
                <bean id="user" class="ioc.User"/>
                
            </beans>
     
    
    2、Explicit configuration in Java:显示的JavaConfig,基于java类配置。
        JavaConfig is the preferred option for explicit configuration
    because it’s more powerful, type-safe, and refactor-friendly.
    
        优点:
            1)在class文件中,降低维护成本。
            2)不需要第三方解析工具。
            3)编辑期就可以检验正确性,提高开发效率。
    
        缺点:
            1)配置代码过多时,直接影响代码质量,对于代码的简洁度有影响。
            2)业务类之间的关系不如XML配置那样容易把握。
            3)如果需要修改配置,则要重新编译整个工程。
        
        举个例子:
             1)在标注了@Configuration的java类中,通过在类方法标注@Bean定义一个Bean。
            2)通过在成员变量或者方法入参处标注@Autowired按类型匹配注入,也可以使用@Qualifier按名称配置注入。
             
             @Configuration 
              public class Conf {   
                  @Scope(“prototype”)   
                  @Bean(“loginUserDao”)   
                  public LoginUserDao loginUserDao() {   
                    return new LoginUserDao();   
                 }   
            }   
    
    3、Implicit bean discovery and automatic wiring:隐式的bean扫描,基于java注解配置,自动注入。
       
        优点:
            1)在class文件中,降低维护成本。
            2)不需要第三方解析工具,利用java反射机制。
            3)编辑期就可以检验正确性,提高开发效率。
        
        缺点:
            1)如果需要对annotation进行修改,那么要重新编译整个工程。
            2)业务类之间的关系不如XML配置那样容易把握。
            3)如果在程序中annotation比较多,直接影响代码质量,对于代码的简洁度有影响。
            4)符合条件的多个bean注入时,spring不知道如何选择,会有异常NoUniqueBeanDefinitionException。
    
        举个例子:
            1)@Component:标注一个普通的Spring Bean类
            2)在方法处通过@Autowired使方法入参绑定Bean,然后在方法中通过代码进行注入。
    
            @Scope(“prototype”)   
            @Lazy(true)  
            @Component(“loginUserDao”)  
            public class LoginUserDao {  }   

    ps:
    这三种方式都可以将bean交给Spring管理,那么应该如何选择呢? 

         根据不同方式的优缺点,现在的企业开发一般采用如下方式:

         1、自定义声明的类(非第三方),采用注解的方式。

         2、对于第三方类,采用XML或者JavaConfig的方式。

        由于SpringBoot的出现,在无配置化的趋势下,XML配置逐渐被JavaConfig所取代,因为我们在创建类的同时可能还要做很多事情,这是XML不发满足的。SpringBoot推荐使用JavaConfig配置。

    5.3 自动装配

    spring从两个角度来实现自动化装配:

      1.组件扫描(component scanning):spring会自动发现应用上下文所创建的bean。

      2.自动装配(autowiring):spring自动满足spring之间的依赖。

    Spring提供了五种自动装配的类型

      ①no:显示指明不使用Spring的自动装配功能

      ②byName:根据属性和组件的名称匹配关系来实现bean的自动装配

      ③byType:根据属性和组件的类型匹配关系来实现bean的自动装配,有多个适合类型的对象时装配失败

      ④constructor:与byType类似是根据类型自动装配,但是要求待装配的bean有相应的构造函数

      ⑤autodetect: 自动检测,利用Spring的自省机制判断使用byType或是constructor装配

    3种方式:

      1. 使用XML方式进行装配

      2. 使用注解的方式进行装配:隐式的bean发现机制和自动装配(@Component @Resource @Autowire @ComponentScan)和使用

        @Resource 根据byName进行装配

        @Autowire通过byType进行装配

      3. Java代码装配bean(@Configuration, @Bean)进行配置。

    5.4 四种依赖注入方式

         1、set注入(通常也叫属性注入):(userDao注入到UserServiceImpl中)

         2、构造函数注入

         3、接口注入(这个现在基本不用)

         4、注解注入(@Autowire) (@Component,@Service,@Controller等直接@Autowire

    5.5 集合属性配置方式

    集合的注入都是给<property>添加子标签
    
    数组:<array>
    List:<list>
    Set:<set>
    Map:<map> ,map存放k/v 键值对,使用<entry>描述
    Properties:<props>  <prop key=""></prop>  
    
    普通数据:<value>
    引用数据:<ref>
    https://www.cnblogs.com/lihuibin/p/7928893.html

    5.6 内部Bean

    1. 当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,只能通过该类实例化。
    2. 为了定义inner bean,在Spring 的 基于XML的 配置元数据中,可以在 <property/><constructor-arg/> 元素内使用<bean/> 元素,
    3. 内部bean通常是匿名的,它们的Scope一般是prototype。
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person2" class="com.itdjx.spring.dependency.injection.Person">
            <property name="name" value="李玉"/>
            <property name="age" value="23"/>
            <property name="sex" value="女"/>
            <property name="car" >
                <bean class="com.itdjx.spring.dependency.injection.Car">
                    <constructor-arg value="Ferrari" index="0"/>
                    <constructor-arg value="Italy" index="1"/>
                    <constructor-arg value="22500000" type="double"/>
                </bean>
            </property>
        </bean>
    </beans>

    参考:
    https://www.jianshu.com/p/3ce23a7b096a
    Spring中的内部Bean

    六. Spring Context初始化流程

     

    https://zhuanlan.zhihu.com/p/59327709

      图中左上角是三种类型的context,xml配置方式的context、springboot的context和web服务的context。

      不论哪种context,创建后都会调用到AbstractApplicationContext类的refresh方法,这个方法是我们要重点分析的。

      refresh方法中,操作共分13步:

    1. prepareRefresh(); 对刷新进行准备,包括设置开始时间,设置激活状态,初始化Context中的占位符,子类根据其需求执行具体准备工作,而后再由父类验证必要参数
    2. obtianFreshBeanFactory(); 刷新并获取内部的BeanFactory对象
    3. prepareBeanFactory(beanFactory); 对BeanFactory进行准备工作,包括设置类加载器和后置处理器,配置不能自动装配的类型,注册默认的环境Bean
    4. postProcessBeanFactory(beanFactory); 为Context的子类提供后置处理BeanFactory的扩展能力,如想在bean定义加载完成后,开始初始化上下文之前进行逻辑操作,可重写这个方法
    5. invokeBeanFactoryPostProcessors(beanFactory); 执行Context中注册的BeanFactory后置处理器,有两张处理器,一种是可以注册Bean的后置处理器,一种的针对BeanFactory的后置处理器,执行顺序是先按优先级执行注册Bean的后置处理器,而后再按优先级执行针对BeanFactory的后置处理器SpringBoot中会进行注解Bean的解析,由ConfigurationClassPostProcessor触发,由ClassPathDefinitionScanner解析,并注册到BeanFactory
    6. registerBeanFactoryProcessor(beanFactory(); 按优先级顺序在BeanFactory中注册Bean的后置处理器,Bean处理器可在Bean的初始化前后处理
    7. initMessageSource();初始化消息源,消息源用于支持消息的国际化
    8. initApplicationEventMuticaster();初始化应用事件广播器,用于向ApplicationListener通知各种应用产生的事件,标准的观察者模型
    9. onRefresh(); 用于子类的扩展步骤,用于特定的Context子类初始化其他的Bean
    10. registerListeners(); 把实现了ApplicationListener的类注册到广播器,并对广播其中早期没有广播的事件进行通知
    11. finishBeanFactoryInitialization(beanFactory); 冻结所有Bean描述信息的修改,实例化非延迟加载的单例Bean
    12. finishRefresh(); 完成上下文的刷新工作,调用LifecycleProcessor.onRefresh(),以及发布ContextRefreshedEvent事件
    13. resetCommonCaches(); 在finally中执行该步骤,重置公共的缓存,如ReflectionUtils中的缓存,AnnotationUtils等

       更详细可见:SpringBoot源码分析之Spring容器的refresh过程

    七. Spring中bean的生命周期

    Spring中bean的生命周期

    第1步:调用bean的构造方法创建bean;

    第2步:通过反射调用setter方法进行属性的依赖注入;

    第3步:如果实现BeanNameAware接口的话,会设置bean的name;

    第4步:如果实现了BeanFactoryAware,会把bean factory设置给bean;

    第5步:如果实现了ApplicationContextAware,会给bean设置ApplictionContext;

    第6步:如果实现了BeanPostProcessor接口,则执行前置处理方法;

    第7步:实现了InitializingBean接口的话,执行afterPropertiesSet方法;

    第8步:执行自定义的init方法;

    第9步:执行BeanPostProcessor接口的后置处理方法。

    这时,就完成了bean的创建过程。

    在使用完bean需要销毁时,会先执行DisposableBean接口的destroy方法,然后在执行自定义的destroy方法。

    这部分也建议阅读源码加深理解。

    八. Spring扩展接口

    对spring进行定制化功能扩展时,可以选择如下一些扩展点:
    
    ▌1.BeanFactoryPostProcessor
    
    是beanFactory后置处理器,支持在bean factory标准初始化完成后,对bean factory进行一些额外处理。在讲context初始化流程时介绍过,这时所有的bean的描述信息已经加载完毕,但是还没有进行bean初始化。例如前面提到的PropertyPlaceholderConfigurer,就是在这个扩展点上对bean属性中的占位符进行替换。
    
    ▌2.BeanDefinitionRegistryPostProcessor
    
    它扩展自BeanFactoryPostProcessor,在执行BeanFactoryPostProcessor的功能前,提供了可以添加bean definition的能力,允许在初始化一般bean前,注册额外的bean。例如可以在这里根据bean的scope创建一个新的代理bean。
    
    ▌3.BeanPostProcessor
    
    提供了在bean初始化之前和之后插入自定义逻辑的能力。与BeanFactoryPostProcessor的区别是处理的对象不同,BeanFactoryPostProcessor是对beanfactory进行处理,BeanPostProcessor是对bean进行处理。
    
    注:上面这三个扩展点,可以通过实现Ordered和PriorityOrdered接口来指定执行顺序。实现PriorityOrdered接口的processor会先于实现Ordered接口的执行。
    
    ▌4.ApplicationContextAware
    
    可以获得ApplicationContext及其中的bean,当需要在代码中动态获取bean时,可以通过实现这个接口来实现。
    
    ▌5.InitializingBean
    
    可以在bean初始化完成,所有属性设置完成后执行特定逻辑,例如对自动装配对属性进行验证等等。
    
    ▌6.DisposableBean
    
    用于在bean被销毁前执行特定的逻辑,例如做一些回收工作等。
    
    ▌7.ApplicationListener
    
    用来监听spring的标准应用事件或者自定义事件。

    参考网址

    1. 官网:https://spring.io/projects/spring-framework
    2. 百度百科:https://baike.baidu.com/item/spring/85061?fr=aladdin#7
    3. Spring中文文档-第一部分
    4. 几种placeholder替换项目参数的方法比较
    5. SpringBoot源码分析之Spring容器的refresh过程
  • 相关阅读:
    c语言变量的交换
    牛客网 多多的电子字典
    算法笔记----背包九讲 ③多重背包问题
    统计学习方法 课后习题第五章
    2020.8.2 19:00-21:00 拼多多算法岗笔试
    python构建模块
    leetcode 剑指 Offer 51. 数组中的逆序对
    pytorch的内部计算
    matplotlib
    矩阵微积分
  • 原文地址:https://www.cnblogs.com/haimishasha/p/5808114.html
Copyright © 2020-2023  润新知