Spring

分层 ,轻量级开源框架,以反转控制(Inverse of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)为内核,提供了展现层Spring MVC、持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,整合第三方框架和类库

Spring_第1张图片

1、IoC方便解耦,简化开发

通过Spring提供的IoC容器,将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。如不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,更加专注于上层应用;通过框架或IoC容器在对象生成或初始化时注入数据或所依赖对象的引用并管理;可递归;亦称DI(依赖倒置)

将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理、获取等工作

BeanFactory接口是Spring框架的核心接口,BeanFactory的实现和高级形态ApplicationContext应用上下文;Context模块构建于核心模块之上,扩展了BeanFactory的功能,添加了i18n国际化、Bean生命周期控制、框架事件体系、资源加载透明化等多项功能,还提供了许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问等。ApplicationContext是Context模块的核心接口

ClassPathResource获取配置文件资源(不同来源对应不同实现类),封装为Resource类型后传给读取器的构造器;getBean():通过名字检索容器中管理的Bean;带参数则检索时类型检查

 

2、AOP编程

面向切面编程应对许多不容易用传统面向对象编程(OOP)实现的功能;是进行横切逻辑编程的思想;基于JDK动态代理或cglib(一个代码类库)动态代理,前者的被代理类必须实现一个接口;运行期实现;集成静态代理AspectJ(编译期实现,性能好),声明式事务处理;代理对象,拦截器,通知,权限,缓存,错误处理,调试,记录跟踪,持久化,同步,事务,等

3、声明式事务的支持

从编程式事务中解脱,声明式事务灵活地进行事务管理,提高开发效率和质量

4、方便程序测试

5、spring入口:listener;监听器为ContextLoaderListner;设置配置文件或配置类(即上下文参数)的位置

6、JDBC/ORM数据访问:提供常用的“对象/关系”映射APIs如Hibernate/MyBatis;封装JDBC如JdbcTemplate模板类(提供数据库操作方法),消除样板式代码

7、Web MVC:springmvc;关联远程调用

=============================================================================================

1、持久层:从数据库表中加载数据并实例化领域对象,或将领域对象持久化到数据表中

2、传统的JDBC API:太底层,即使用户执行一条最简单的数据查询操作,都必须执行如下的过程:获取连接→创建Statement→执行数据操作→获取结果→关闭Statement→关闭结果集→关闭连接,除此之外还需要进行异常处理的操作

3、Spring JDBC:薄层的封装,通过一个模板类org.springframework. jdbc.core.JdbcTemplate封装样板式的代码,用户通过模板类就可以轻松地完成大部分数据访问的操作

4、传统EJB:厚重,编写满足EJB容器规范的EJB组件才能获取服务

5、POJO或JavaBean:最小侵入性(应用开发对接口而非类编程);通过依赖注入和面向接口实现松耦合;基于切面和惯例进行声明式编程;通过切面和模板减少样板式代码

================================================================================================

 

    动态代理:接口与实现类不直接关联,在运行时实现动态关联;基于接口的JDK动态代理:实现InvocationHandler接口并重写invoke();基于类的CGLIB动态代理

 

bean装载后的生命周期

实例化bean

将值和bean的引用注入到bean的属性

根据bean实现的接口调用不同方法(依次)

        BeanNameAware:setBeanName(beanId)

        BeanFactoryAware:setBeanFactory(beanFactory)

        ApplicatonContextAware:setApplicationContext(applicationContext)

bean已就绪,驻留在上下文中供程序使用(工作)

(如果实现DisposableBean)调用destory()和自定义销毁方法

@RequestScope,@SessionScope,@ApplicationScope:定义Bean的生命周期(Bean跟随请求、会话和应用程序的生命周期来创建)

作用域
singleton spring默认单例(无论注入多少次都是同一个实例);使用ThreadLocal保证线程安全;类是易变、会保存状态的则不应为单例
prototype原型 每次注入或上下文获取时,都会新生成一个实例(singleton=”false”时)
request 每个 http请求都创建一个实例(web环境)
session 同一个session共享一个实例,不同的session创建不同实例(web环境)

标签属性或bean上使用如@Scope(“prototype”);用类封装的字段常量(如ConfigurableBeanFactory.SCOPE_PROTOTYPE)更安全

request和session作用域的bean有proxyMode属性,即设置代理并延迟加载(当此bean注入到一个单例bean时,单例已经初始化,但此bean还不存在;委托相同功能的代理bean解析和延迟加载,真正调用是才使用此bean);当bean为接口时,值ScopedProxyMode.INTERFACES;当bean为类时,需要使用CGLIB生成代理类,值ScopedProxyMode.TARGET_CLASS;它对应aop命名空间中的标签,默认使用CGLIB创建目标类的代理,属性proxy-targer-class值为false时创建接口的代理

不需要在启动时立即加载bean到容器中,可延迟加载(即等到调用getBean()时初始化):,对应注解@Lazy(仅针对单例模式)

bean其他属性:

init-method,destroy-method的值为方法名设置启动和关闭时初始方法和销毁方法;基于XML配置的,在getBean()时默认调用了init-method,手动context.destroy()/close();基于自动装配的,方法上使用@PostConstruct和@PreDestroy

factory-method:静态工厂方法,方法中业务一般为new新对象

factory-bean:实例/动态工厂方法,即先构建方法所在类/工厂,由工厂生产方法

 

  • 装配bean:

1、XML显式配置,创建XML文件并以为根

前几行引入库/模式用来管理xml,如使用标签元素则需要spring-bean模式

命名空间规定扫描范围

等同于@Bean,默认beanId为全限定名+”#数字”(计数区分);spring通过默认构造器来创建此bean(不灵活)

setter()参数注入依赖的bean或注入自身field :

可换方案:引入p-命名空间模式,然后做bean标签的属性;用参数名不灵活,可用参数索引,第一个为p:_0-ref,只有一个参数时0可省(标识符不以数字开头,加了_);注入field则去掉-ref;无法针对集合参数

 

构造器参数传入依赖的bean或注入自身field:

可换方案:使用C命名空间,c-作为bean标签的属性;使用方法同上

当参数是集合时子标签/;set无序且忽略重复值

组合:

使用类ClassPathXmlApplicationContext(参数为xml路径)获取context;

2、java显式配置:更解耦和无侵入,类型安全,重构友好

创建被@Configuration修饰的配置类(BeanConfig)

在返回bean对象的方法上使用@Bean表明该对象要声明为上下文中的bean;属性name或重命名方法名为bean命名(默认beanId首字母小写);应用:配合条件如随机数产生并使用各种bean;参数为类.class或配置文件的classpath

当此bean需要依赖其他bean时:

方式1:构造器参数为依赖的bean对象(由对应声明在容器中的bean的@Bean方法返回即去寻找产生bean的地方并使用而不是产生bean;因为默认单例,spring会拦截此方法,直接返回bean)

方式2:依赖的bean直接作方法参数传入,方法体中可使用构造器注入和setter()注入

组合:@Import(其他配置类列表.class);@ImportResource(“classpath:配置文件.xml”)

 

3、bean发现机制(隐式)和自动装配(推荐;而想要装配第三方库中的bean时只能依靠显式)

组件扫描:自动发现上下文中的bean

@Component声明类是一个bean/组件(可具体为@Bean,@Service,@Controller等);属性给bean命名(spring默认给所有bean的ID为类名首字母小写);可换为@Named(来自javaDI规范)

@ComponentScan启用bean发现/扫描(默认不开启;默认扫描基础包为它修饰的类的包及子包;参数可以指定扫描包);属性value或basePackages以字符串形式(类型不安全)指定扫描基础包(数组),或basePackageClasses(如={A.class,B.class})以类/接口形式(安全)

一般主类与扫描装配bean需要解耦,所以新建类NewClass并标注@ComponentScan和@Configuration,主类中调用即可:ApplicationContext context=new AnnotationConfigApplicationContext(NewClass.class/或配置文件的classpath);如此,所有满足的bean都可从context中获取:context.getBean(BeanName.class),重载参数类型可以为类id字符串即中设置的id,默认为首字母小写的类名;系统默认设置为从0开始的”类完全限定名#0”

 

自动装配/注入/满足依赖

    @Autowired(按类型,spring提供的注解)/@Resource(按名字匹配,java提供的注解)在构造器或者setter()的参数(其实可用在任何方法上)需要引用其他bean时;required属性为false设置bean暂时处于未装配状态(否则若没有匹配的bean,上下文在创建时抛异常;或有多个满足的bean时抛出异常),而后进行null检查;可换为@Inject(来自javaDI规范)

    自动装配的模式:no默认不自动,手动ref;byName通过参数名;byType通过参数的类型,多个bean符合则抛出异常;constructor通过构造器的参数的类型,必须有bean符合;autodetect如果有构造器,则用constructor,否则byType;

    不能装配原生数据类型;模糊装配即没有自定义装配明确

用于构造器上比用于属性上装配效率高,还可用于setter()上或任意有类关联/级联的方法上;有继承或实现的,@Component标注在实现类上,而@Autowired则通常用于接口或父类。所以可能出现红色下划线(但项目依然可运行),表名装配的bean有歧义,不唯一,NoUniqueBeanDefinitionException;可对bean设置为首选@Primary(有局限,在设计时不确实谁是首选);或者@Autowired用于具体实现类;或者对bean标注@Qualifier,参数为bean别名(等同于为@Component指定id参数值),然后在@Autowired下使用@Qualifier(别名/id);不指定id则使用bean默认id即类名首字母小写;JDK自带的注解@Resource(name="id")等同于@Autowired和@Qualifier的作用;基于java显式配置文件的装配,这些注解应用在对应的@Bean下;@Qualifier对应基于XML文件中的id(唯一)或name(可不唯一)

 复杂类型数组、集合List/Map/Set,都有对应标签

 

运行时值注入:改善以上(直接value=””)硬编码(通常针对field注入)

1、属性占位符:配置属性源文件,内容一般为key=value列表;它被spring加载到Environment

@PropertySource(“classpath:….properties”)引用属性源

方式1:类中@Autowired注入Environment后使用其方法getProperty(key[,defaultValue])获取value(重载参数还可将String型转为其他类型,略),传给参数(不能传到xml配置中);getRequiredProperty()在文件中没有属性定义时IllegalStateException;boolean containsProperty();getPropertyAsClass()

方式2:占位符${key};在每一个参数前@Value(“${key}”),或xml配置中如c:_name=”${name}”;使用占位符需要配置类PropertySourcePlaceholderConfigurer或配置/

2、springEL表达式:“#{java代码表达式}”

一般注解用于public方法(如@Transactional用于其他可见度的方法,不会报错但事务设置无效);spring一般对运行时异常进行事务回滚,而checked异常不回滚

依赖检查:注入时判断属性的数据类型是否匹配即能否注入成功;;否则UnsatisfiedDependencyException

none:默认,无检查;simple:基本数据类型和集合类型;objects:检查复合类型;all:全检查

使用@Required的依赖检查:自由配置针对特定属性;两种方式

包含标签

注册

 

  • AOP面向切面编程

横切关注点:散布于应用中的功能(日志、安全和事务管理)

切面Advisor:将横切关注点抽象和模块化

通知advice:切面的工作内容(是什么及何时用)

基于动态代理,入口java.lang.proxy

@Before:方法(即属性指定的切点)执行前执行;对应;下同;注意xml文件中不能用&&而用and(切点表达式中如果用到);不能阻止方法执行,除非通知中抛出异常

@After:方法之后一定执行;对应

@AfterReturning:方法成功返回后才执行;对应

@AfterThrowing:抛出异常时执行;对应

@Around:较复杂,可视为通知方法(由这些注解标注的方法)中实现了前置通知和后置通知;此通知方法必有参数proceedingJoinPoint,在方法体中任意位置通过它的proceed()方法来调用被通知方法(切点),即控制转移,执行完后返回继续执行后边代码;对应

连接点(仅方法级别):能够应用通知的点;

切点pointcut:判断在何处使用及是否织入后的具体连接点

织入:把切面应用到目标对象(的编译期如AspectJ、类加载期、运行期如AOP)并创建代理对象;基于动态代理(代理类包裹了目标对象);限于方法拦截

1、编写advice:

public class Advice { 
    //前置通知 
    public void beforeAdvice(){ 
        System.out.println("Before"); 
    } 
    //无论是否异常,一定调用此通知 
    public void afterAdvie(){
         System.out.println("After");
     } 
    //出现异常后不再调用此通知 
    public void afterReturnAdvie(){ 
        System.out.println("AfterReturn"); 
    } 
    //环绕通知:固定写法 
    public Object aroundAdvie(ProceedingJoinPoint point){ 
        System.out.println("Around-Before"); 
        Object proceed=null; 
        try { 
            proceed = point.proceed(); 
    } 
        catch (Throwable throwable) { 
            throwable.printStackTrace(); 
    } 
        System.out.println("Around-After"); 
        return proceed; 
    } 
    //异常通知:出现异常才调用 
    public void exceptionAdvie(){ 
        System.out.println("Exeption"); 
    } 
}

 

2、java显式配置:更解耦和无侵入,类型安全,重构友好

创建被@Configuration修饰的配置类(BeanConfig)

在返回bean对象的方法上使用@Bean表明该对象要声明为上下文中的bean;属性name或重命名方法名为bean命名(默认beanId首字母小写);应用:配合条件如随机数产生并使用各种bean;参数为类.class或配置文件的classpath

当此bean需要依赖其他bean时:

方式1:构造器参数为依赖的bean对象(由对应声明在容器中的bean的@Bean方法返回即去寻找产生bean的地方并使用而不是产生bean;因为默认单例,spring会拦截此方法,直接返回bean)

方式2:依赖的bean直接作方法参数传入,方法体中可使用构造器注入和setter()注入

组合:@Import(其他配置类列表.class);@ImportResource(“classpath:配置文件.xml”)

 

3、bean发现机制(隐式)和自动装配(推荐;而想要装配第三方库中的bean时只能依靠显式)

组件扫描:自动发现上下文中的bean

@Component声明类是一个bean/组件(可具体为@Bean,@Service,@Controller等);属性给bean命名(spring默认给所有bean的ID为类名首字母小写);可换为@Named(来自javaDI规范)

@ComponentScan启用bean发现/扫描(默认不开启;默认扫描基础包为它修饰的类的包及子包;参数可以指定扫描包);属性value或basePackages以字符串形式(类型不安全)指定扫描基础包(数组),或basePackageClasses(如={A.class,B.class})以类/接口形式(安全)

一般主类与扫描装配bean需要解耦,所以新建类NewClass并标注@ComponentScan和@Configuration,主类中调用即可:ApplicationContext context=new AnnotationConfigApplicationContext(NewClass.class/或配置文件的classpath);如此,所有满足的bean都可从context中获取:context.getBean(BeanName.class),重载参数类型可以为类id字符串即中设置的id,默认为首字母小写的类名;系统默认设置为从0开始的”类完全限定名#0”

 

自动装配/注入/满足依赖

    @Autowired(按类型,spring提供的注解)/@Resource(按名字匹配,java提供的注解)在构造器或者setter()的参数(其实可用在任何方法上)需要引用其他bean时;required属性为false设置bean暂时处于未装配状态(否则若没有匹配的bean,上下文在创建时抛异常;或有多个满足的bean时抛出异常),而后进行null检查;可换为@Inject(来自javaDI规范)

    自动装配的模式:no默认不自动,手动ref;byName通过参数名;byType通过参数的类型,多个bean符合则抛出异常;constructor通过构造器的参数的类型,必须有bean符合;autodetect如果有构造器,则用constructor,否则byType;

    不能装配原生数据类型;模糊装配即没有自定义装配明确

用于构造器上比用于属性上装配效率高,还可用于setter()上或任意有类关联/级联的方法上;有继承或实现的,@Component标注在实现类上,而@Autowired则通常用于接口或父类。所以可能出现红色下划线(但项目依然可运行),表名装配的bean有歧义,不唯一,NoUniqueBeanDefinitionException;可对bean设置为首选@Primary(有局限,在设计时不确实谁是首选);或者@Autowired用于具体实现类;或者对bean标注@Qualifier,参数为bean别名(等同于为@Component指定id参数值),然后在@Autowired下使用@Qualifier(别名/id);不指定id则使用bean默认id即类名首字母小写;JDK自带的注解@Resource(name="id")等同于@Autowired和@Qualifier的作用;基于java显式配置文件的装配,这些注解应用在对应的@Bean下;@Qualifier对应基于XML文件中的id(唯一)或name(可不唯一)

 复杂类型数组、集合List/Map/Set,都有对应标签

运行时值注入:改善以上(直接value=””)硬编码(通常针对field注入)

1、属性占位符:配置属性源文件,内容一般为key=value列表;它被spring加载到Environment

@PropertySource(“classpath:….properties”)引用属性源

方式1:类中@Autowired注入Environment后使用其方法getProperty(key[,defaultValue])获取value(重载参数还可将String型转为其他类型,略),传给参数(不能传到xml配置中);getRequiredProperty()在文件中没有属性定义时IllegalStateException;boolean containsProperty();getPropertyAsClass()

方式2:占位符${key};在每一个参数前@Value(“${key}”),或xml配置中如c:_name=”${name}”;使用占位符需要配置类PropertySourcePlaceholderConfigurer或配置/

2、springEL表达式:“#{java代码表达式}”

一般注解用于public方法(如@Transactional用于其他可见度的方法,不会报错但事务设置无效);spring一般对运行时异常进行事务回滚,而checked异常不回滚

依赖检查:注入时判断属性的数据类型是否匹配即能否注入成功;;否则UnsatisfiedDependencyException

none:默认,无检查;simple:基本数据类型和集合类型;objects:检查复合类型;all:全检查

使用@Required的依赖检查:自由配置针对特定属性;两种方式

包含标签

注册

 

  • AOP面向切面编程

横切关注点:散布于应用中的功能(日志、安全和事务管理)

切面Advisor:将横切关注点抽象和模块化

通知advice:切面的工作内容(是什么及何时用)

基于动态代理,入口java.lang.proxy

@Before:方法(即属性指定的切点)执行前执行;对应;下同;注意xml文件中不能用&&而用and(切点表达式中如果用到);不能阻止方法执行,除非通知中抛出异常

@After:方法之后一定执行;对应

@AfterReturning:方法成功返回后才执行;对应

@AfterThrowing:抛出异常时执行;对应

@Around:较复杂,可视为通知方法(由这些注解标注的方法)中实现了前置通知和后置通知;此通知方法必有参数proceedingJoinPoint,在方法体中任意位置通过它的proceed()方法来调用被通知方法(切点),即控制转移,执行完后返回继续执行后边代码;对应

连接点(仅方法级别):能够应用通知的点;

切点pointcut:判断在何处使用及是否织入后的具体连接点

织入:把切面应用到目标对象(的编译期如AspectJ、类加载期、运行期如AOP)并创建代理对象;基于动态代理(代理类包裹了目标对象);限于方法拦截

1、编写advice:

2、编写连接点/切点:advice应用在的地方处

声明一个有f方法的接口A

spring借助AspectJ的切点表达式语言(指示器)来定义切点;常用execution()匹配是连接点的方法,如execution(* 包.A.f(..));*表示不关心返回类型,..表示任意参数;!(not),||(or),&&(and)用于连接指示器;bean()指示器参数为beanId或bean名称,限制切点匹配特定的bean;args(参数名)指示器控制切点方法中的参数到切面里通知方法的参数转移

      

3、织入

标注类为切面,多个切面可包裹在中; 对应@AspectJ

配置将advice织入目标对象/方法:

;对应注解@Pointcut():提取连接点/目标方法为切点,形式是一个方法(方法名为切点命名),如@Pointcut(“execution(** 完全限定名.f(..))”) public void fName(){};然后切点应用和重用如@Before(“fName()”),@After(“fName()”)

  

注解方式:@After,@AfterReturning,@AfaterThrowing,@Around,@Before等声明方法为通知,属性为切点(即在切点前/后调用通知);@Pointcut()提取连接点为切点,形式是一个方法(方法名为切点命名),如@Pointcut(“execution(** 完全限定名.f(..))”) public void fName(){};然后切点应用和重用如@Before(“fName()”),@After(“fName()”);对应

启用切面/代理:

@EnableAspectJAutoProxy标注配置类启用自动代理功能;对应;类上@Aspect将切面声明为bean;spring使用此bean创建一个代理

以上是用切面对方法进行功能扩充;还可利用切面对对象扩充方法,@DeclareParents,略

          

//拦截有顺序

-> getBean(代理类)

以上对所有方法都通知了;可指定切点(不推荐使用配置,而使用AspectJ支持即@对应的通知类型注解):名称匹配或者正则表达式匹配

开启注解支持;注解参数excution匹配完整通知路径;注解对应老版本配置

           由于在每个方法前写@Before(...)...等比较繁琐,将他们抽离出来形成切点方法:

 

@Pointcut(…)

public void pointcut(){…}

然后@Before(“类.pointcut()”)

 

特殊切面之事务管理/拦截器:封装了在数据库层面实现的事务

声明式事务:方法级别

编程式事务:重复且复杂,Bean上使用注解@Transactional开启支持

顶层事务管理器:PlatformTransactionManager;定义方法getTransaction()开启事务,rollback()回滚,commit()提交

消除JDBC的自动提交(不能回滚):connection.setAutoCommit(false);所有sql执行完后commit();异常时rollback()

事务名称空间支持:spring-tx.xsd;配置事务管理器DataSourceTransactionManager管理数据源;设置事务通知advice:,包括事务传播行为

注解方式:

springAOP拦截器只能拦截spring管理Bean的访问service

@EnableTransactionManagement:开启事务管理

@Transactional:开启事务;它的属性设置事务的传播性(由TransactionDefinition接口中的常量定义),隔离性,回滚条件;默认与数据库一致

@TransactionalEventListener:配置事务的回调方法

      

       特殊切面之日志记录:

           配置log4j.properties并设置日志级别为info,将日志输入到控制台和指定文件,;编写切面类LogAspect,通过连接点类ProceedingJoinPoint获取目标类名和方法,调用LOG.info()记录进入方法时日志信息,peoceed()被try-catch,且LOG.error记录异常信息;配置aop-config

   

数据缓存

启用缓存支持:@EnableCaching标注配置类;对应;工作方式:它们会创建aspect并触发缓存pointcut,aspect从缓存中添加和提取数据

 

配置类中配置缓存管理器bean(@Bean标注返回这些对象的方法):

ConcurrentMapCacheManager:基于内存(生命周期随应用)

SimpleCacheManage:

NoOpCacheManager:

CompositeCacheManager:

EhCacheCacheManager:

RedisCacheManager:SpringDataRedis提供

CompostiteCacheManager构建List,迭代使用多个管理器

缓存设置:

不同缓存方式对应不同标签和配置;

 

使用缓存:

@Cacheable:标注的方法调用之前,从缓存查找方法返回值;无则方法调用并将值存储到缓存;常用于频繁请求如findOne();

@CachePut:不检查缓存,方法始终被调用,值存放到缓存;

共有属性:cache;value缓存名;condition是否将缓存应用到方法调用上,springel表达式,不应用则不查找也不缓存;key通过key查找,如通过id查找的方法,key为传递到方法的id参数,springel表达式自定义key略;method;unless是否将返回值存放缓存(true为不放),之前会从缓存查找

@CacheEvict:在缓存中清除值;常用于remove();

@Caching:缓存注解的组合;

可用于类上(对所有方法作用)

以上标签被包裹于:定义缓存通知,配合将通知应用到切点

 

spring+JDBC

数据访问对象DAO实现/委托Repository接口;spring提供大量继承自DataAccessException的数据库操作异常体系;spring根据数据的固定和可变,将类分为模板和回调;模板类如JdbcTemplate处理数据访问的固定部分(事务管理,资源管理和异常处理);回调实现数据访问(语句,参数绑定,处理结果集)

  • 配置数据源

JNDI数据源:

此种配置让数据源在应用程序之外进行处理,仅在访问时查找即可;JndiObjectFactoryBean实现查找dataSource;对应jee命名空间检索任何JNDI对象及数据源

JDBC数据源(每次请求都会新建连接,费性能):spring自带的DriverManagerDataSource/SimpleDriverDataSource/SingleConnectionDataSource(单线程)

连接池数据源(推荐):DBCP/c3p0/JDBC的

对应实现java类及setter()

       嵌入式数据库

@Profile()运行时选择数据源;属性值”development”使用开发数据源;”qa”QA数据源;”production”生产环境数据源;对应

 

你可能感兴趣的:(Spring)