高级装配

1 环境与 profile

1.1 配置 profile

@Profile 注解

指定某个 bean 属于哪一个 profile,运行时做出环境决策。

没有指定 profile 的 bean 始终都会被创建, 与激活哪个 profile 没有关系。

@Profile("dev")
@Profile("prod")

1.2 激活 profiles

Spring 提供了两个配置项定义激活哪个 profile:

  • spring.profiles.active:优先
  • spring.profiles.default:没有 active 则找 default

如都没有设置,则只会创建没有被 profile 修饰的 bean。

2 条件化的 bean

@Conditional

作用于 @Bean 注解修饰的方法上,通过判断指定的条件是否满足来决定是否创建该 bean。

传入注解的类一定要实现 Condition 接口,提供 matchs() 方法——如果 matches() 方法返回 true,则被 @Conditional 注解修饰的 bean 就会创建,否则对应的 bean 不会创建。

@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}

3 处理自动装配的歧义

3.1 @Primary

指定优先 bean

3.2 @Qualifier

指定 bean 的 ID

也可在 bean 和注入的地方注解相同 @Qualifier,或定义自定义 QUALIFIER

4 bean 的作用域

4.1 @Scope

Spring 中的 bean 默认都是单例,这对可变类型是非线程安全的。如果选择其他的作用域, 要使用 @Scope 注解。

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

4.2 scoped 代理

单例 bean 在加载时就会被创建,会话/请求 bean 要创建 request/session 时才会被创建。

把会话 bean 装配到单例 bean 时,应给单例 bean 装配一个会话 bean 的代理。

代理类对外暴露的接口和会话 bean 中的一样,当单例 bean 调用对应的接口时,代理采取懒解析策略,并把调用委派给实际的 session-scoped bean。设置 proxyMode 属性。

@Bean
@Scope(value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() { ... }

5 运行时值注入

Spring 提供以下两种方式实现运行时注入:

  • 属性占位符( Property placeholder) 。
  • Spring 表达式语言( SpEL) 。

5.1 Environment

在 Spring 中解析外部值的最好方法是定义一个配置文件,然后通过 Spring 的 Environment 实例获取配置文件中的配置项的值。属性文件被加载到 Spring 的 Environment 实例中,然后通过 getProperty() 方法解析对应配置项的值。

@Configuration
@PropertySource("classpath:/app.properties")
public class ExpressiveConfig {
@Autowired
Environment env;

@Bean
public CompactDisc disc() {
return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));
}
}

5.2 属性占位符

将属性定义到外部的属性的文件中, 并使用占位符“${ … }”将其插入到 Spring bean 中。

public BlankDisc(
@Value("${disc.title}") String title,
@Value("${disc.artist}") String artist) {
this.title = title;
this.artist = artist;
}

为了使用占位符的值,需要配置 PropertySourcesPlaceholderConfigurer

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

5.3 SpEL 表达式

运行时给 bean 的属性或者构造函数参数注入值

SpEL 表达式被 #{ … } 包围:

  • 可以通过 bean 的 ID 引用 bean;
  • 可以调用某个对象的方法或者访问它的属性;
  • 支持数学、关系和逻辑操作;
  • 正则表达式匹配;
  • 支持集合操作

IOC容器和Bean

1 IOC 容器

概念 : IOC 容器(container)是 Spring 框架的核心,它管理着 Spring 应用中 bean 的创建、 配置和管理,负责对象的整个生命周期。

1.1 容器实现

1)BeanFactory

最简单的容器,提供基本的 DI 支持。使用控制反转将应用的配置和依赖与实际的应用代码分离开来。

第一次访问某一 Bean 时才实例化它。

2)ApplicationContext

基于 BeanFactory 构建, 并提供应用框架级别的服务。

初始化容器时就实例化所有单例的 Bean。

有多种 ApplicationContext 的实现, 每一种都提供了配置 Spring 的不同方式。

3)Bean Factory 和 ApplicationContext 的区别

ApplicationContext 提供了一种解决文档信息的方法,一种加载文件资源的方式(如图片),他们可以向监听他们的beans发送消息。

另外,容器或者容器中 beans 的操作,这些必须以 Bean Factory 的编程方式处理的操作可以在 ApplicationContext 中以声明的方式处理。

这两者的继承关系如下:

1.2 getBean()

通过容器 getBean() 方法从容器获取指定 bean。

@Autowired
ApplicationContext applicationContext;

xxx xxxobj = applicationContext.getBean(xxx.class);

2 Spring Bean

2.1 Spring Bean 定义

什么是Bean?

由于Java语言欠缺属性、事件、多重继承功能,如果在Java程序中实现一些面向对象编程的常见需求,只能手写大量胶水代码。Bean正是编写这套胶水代码的惯用模式或约定。这些约定包括getXxx、setXxx、isXxx、addXxxListener、XxxEvent等。简而言之,Bean就是一个具有规范的Java类

在spring中。这些bean对象由 Spring IOC 容器实例化、组装、管理。

Spring Bean 中定义了所有的配置元数据,这些配置信息告知容器如何创建它,它的生命周期是什么以及它的依赖关系。

2.2 定义 bean 的作用域

在 Spring 中创建一个 bean 的时候,我们可以通过“scope”属性声明它的作用域。

Spring 中的 bean 默认都是单例(singleton)的,这对可变类型是非线程安全的。

Spring 定义了几种 bean 的作用域:

  • Singleton:单例。在 Spring IOC 容器中仅存在一个 Bean 实例,Bean 以单实例的方式存在。
  • Prototype:原型。每次被装配时,都会创建一个新的实例。
  • Request:在 WebApplicationContext 中,在每次 http 请求中创建一个 bean 的实例。
  • Session:在 WebApplicationContext 中,在每次 HTTP Session 过程中只创建一个 bean 的实例;
  • GlobalSession:在 WebApplicationContext 中,在同一个全局 HTTP Session 只创建一个 Bean 的实例

3 Bean 的生命周期

对于普通的 Java 对象,当 new 的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而由 Spring IoC 容器托管的对象,它们的生命周期完全由容器控制。

Spring 只管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

bean的生命周期

BeanFactory 负责 bean 创建的最初四步,然后移交给 ApplicationContext 做后续创建过程

3.1 实例化

Spring 容器(从 XML 文件、注解、Java 配置文件)读取 bean 的定义并实例化 bean。

  • 对于 BeanFactory 容器:当客户向容器请求一个尚未初始化的 bean 时,或初始化 bean 的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean 进行实例化。
  • 对于 ApplicationContext 容器:当容器启动结束后,便实例化所有的 bean。

容器通过获取 BeanDefinition 对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。

实例化对象被包装在 BeanWrapper 对象中,BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制设置属性。

3.2 属性填充(依赖注入)

实例化后的对象被封装在 BeanWrapper 对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。

紧接着,Spring 根据 BeanDefinition 中的信息进行依赖注入。

并且通过 BeanWrapper 提供的设置属性的接口完成依赖注入。

3.3 注入 Aware 接口

紧接着,Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给 bean。

  • 如果该 Bean 实现了 BeanNameAware 接口,Spring 将 bean 的 id 传递给 setBeanName() 方法。
  • 如果该 Bean 实现了 BeanFactoryAware 接口,Spring 将 BeanFactory 传递给 setBeanFactory() 方法。
  • 如果该 Bean 实现了 ApplicationContextAware 接口,Spring 将 ApplicationContext 传递给 setApplicationContext() 方法。

xxxAware 接口可以用于在初始化 bean 时获得 Spring 中的一些对象,如获取 Spring 上下文等。

3.4 BeanPostProcessor

当经过上述几个步骤后,bean 对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过 BeanPostProcessor 接口实现。

该接口提供了两个函数:

  • postProcessBeforeInitialzation( Object bean, String beanName )
    • 当前正在初始化的 bean 对象会被传递进来,我们就可以对这个 bean 作任何处理。
    • 这个函数会先于 InitialzationBean 执行,因此称为“前置处理”。
    • 所有 Aware 接口的注入就是在这一步完成的。
  • postProcessAfterInitialzation( Object bean, String beanName )
    • 当前正在初始化的 bean 对象会被传递进来,我们就可以对这个 bean 作任何处理。
    • 这个函数会在 InitialzationBean 完成后执行,因此称为“后置处理”。

3.5 自定义初始化

当 BeanPostProcessor 的“前置处理”完成后就会进入本阶段。

1)InitializingBean 接口

InitializingBean 接口只有一个函数:afterPropertiesSet()

这一阶段也可以在 bean 正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前 bean 对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。

若要使用它,我们需要让 bean 实现该接口,并把要增加的逻辑写在该函数中。

Spring 会在前置处理完成后检测当前 bean 是否实现了 InitializingBean 接口,并执行 afterPropertiesSet 函数。

2)init-method 属性

Spring 为了降低对客户代码的侵入性,给 bean 的配置提供了 init-method 属性,该属性指定了在这一阶段需要执行的函数名。

Spring 便会在初始化阶段执行我们设置的函数。init-method 本质上仍然使用了 InitializingBean 接口。

3)@PostConstruct 注解

对象构建之后调用

3.6 自定义销毁

当 BeanPostProcessor 的“后置处理”完成后就会进入本阶段。

1)DisposableBean 接口

如果该 bean 实现了 DisposableBean,调用 destroy() 方法。

2)destroy-method 属性

和 init-method 一样,通过给 destroy-method 指定函数,就可以在 bean 销毁前执行指定的逻辑。

3)@PreDestroy 注解

对象移除之前调用

如果想从源码的角度再深入了解IOC这块,推荐看这篇文章

https://javadoop.com/post/spring-ioc


spring基础

Spring 是一个开源的 Java EE 开发框架。Spring 框架的核心功能可以应用在任何 Java 应用程序中,但对 Java EE 平台上的 Web 应用程序有更好的扩展性。Spring 框架的目标是使得 Java EE 应用程序的开发更加简捷,通过使用 POJO 为基础的编程模型促进良好的编程风格。

1 Spring 基本理念

简化 Java 开发

  • 轻量级:基础版本的 Spring 框架大约只有 2 MB。
  • 容器:Spring 包含并管理应用程序对象的配置及生命周期。
  • 控制反转(IOC):松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。
  • 面向切面编程(AOP): 把应用的业务逻辑与系统的服务分离开来。消除样板式代码.

2 Spring 基本原理

  1. 启动项目后,按照配置找到要 scan 的包(自动装配)
  2. 解析包里面的所有类,找到所有含有 @bean,@service 等注解的类,利用反射解析,封装成各种信息类放到容器(map 实现)里。
  3. 需要 bean 时,从容器里找。找到则通过构造器 new 出来(控制反转),没找到则抛出异常
  4. 如果类中有需要注入的,仍从 container 寻找、解析类,new 出对象,用 setter 注入(依赖注入)。
  5. 嵌套 bean 用了递归,container 会放到 servletcontext 里,每次 request 从 servletcontext 找 container。
  6. 如果 bean 的 scope 是 singleton,会重用,将这个 bean 放到一个 map 里,每次用都先从这个 map 里面找。
  7. 如果 scope 是 session,则该 bean 会放到 session 里面。

3 Spring 模块

Spring 4.0 有 20 个模块,这些模块依据其所属的功能可以划分为 6 类不同的功能

3.1 Spring 核心容器

最核心的部分。除了 BeanFactory 和 ApplicationContext,还提供 E-mail、 JNDI 访问、 EJB 集成和调度等服务。所有的 Spring 模块都构建于核心容器之上

3.2 面向切面编程

开发切面的基础。借助于 AOP,可以将遍布系统的关注点(例如事务和安全)从它们所应用的对象中解耦出来。

3.3 数据访问与集成

JDBC 和 data-access object 模块封装了样板式代码和异常。

集成了许多流行的 ORM。支持事务管理。

1)JDBC 和 DAO

JDBC 和 DAO(data-access object)模块封装了样板式代码和异常,保证了与数据库连接代码的整洁与简单,同时避免了由于未能关闭数据库资源引起的问题。

它在多种数据库服务器的错误信息之上提供了一个很重要的异常层。它还利用 Spring 的 AOP 模块为 Spring 应用程序中的对象提供事务管理服务。

2)ORM

Spring 通过提供对象/关系映射集成(ORM)模块在 JDBC 的基础上支持对象关系映射工具。

这样的支持使得 Spring 可以集成主流的 ORM 框架,包括 Hibernate, JDO, 及 iBATIS SQL Maps。Spring 的事务管理可以同时支持以上某种框架和 JDBC。

3.4 Web 与远程调用

Spring 的 web 模块建立在应用上下文(application context)模块之上,提供了一个适合基于 web 应用程序的上下文环境。该模块还支持了几个面向 web 的任务,如透明的处理多文件上传请求及将请求参数同业务对象绑定起来。

1)Spring MVC

Spring 可以很轻松的同其他 MVC 框架结合,但 Spring 的 MVC 是个更好的选择,因为它通过控制反转将控制逻辑和业务对象完全分离开来。通过 DispatcherServlet, ModelAndView 和 View Resolver,开发 web 应用变得很容易。

2)HTTP invoker

自带远程调用框架。提供了暴露和使用 REST API 的良好支持。还能集成多种流行远程调用框架。

3.5 Instrumentation

提供为 JVM 添加代理的功能。为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文件,就像这些文件是被类加载器加载的一样。

3.6 测试

可以与常用的 JUNIT、Mockito、Spock 等测试框架整合使用。

为使用 JNDI、 Servlet 和 Portlet 编写单元测试提供了一系列的 mock 对象实现。加载 Spring 应用上下文中的 bean 集合以及与 Spring 上下文中的 bean 进行交互提供了支持。

4 Spring 社区经典项目

4.1 Spring Security

安全对于许多应用都是一个非常关键的切面。 利用 Spring AOP, 为 Spring 应用提供了声明式的安全机制。

4.2 Spring Data

使得在 Spring 中使用任何数据库都变得非常容易。 不管使用文档数据库, 还是关系型数据库,Spring Data 都为持久化提供了一种简单的编程模型。包括为多种数据库类型提供了一种自动化的 Repository 机制。

4.3 Spring Boot

Spring Boot 主要是为了解决使用 Spring 框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。

Spring Boot 大量依赖于自动配置(auto-configuration)技术,它能够消除大部分 Spring 配置。它还提供了多个 Starter 项目,降低了项目搭建的复杂度。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。


Durid实战

Druid简介

Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

com.alibaba.druid.pool.DruidDataSource 基本配置参数如下:

一、配置单数据源

1.添加依赖

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>

2.yaml配置

spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/erp_ydh?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: xxxxx
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源

二、配置多数据源