Spring

核心概念

DI: dependency injection
AOP: aspect oriented programming
container: 负责对象的生命周期,从new到finalize
有两种container:

  1. BeanFactory
  2. ApplicationContext基于BeanFactory
    ApplicationContext主要有FileSystemXmlApplicationContext和ClassPathXmlApplicationContext这两个是从xml配置文件中加载,还有从Java配置中加载的使用AnnotationConfigApplicationContext

bean的生命周期

  1. 实例化
  2. 填充属性
  3. 调用BeanNameAware的setBeanName()方法
  4. 调用BeanFactoryAware的setBeanFactory()方法
  5. 调用ApplicationContextAware的setApplicationContext()方法
  6. 调用BeanPostProcessor的预初始化方法
  7. 调用InitializingBean的afterPropertiesSet()方法
  8. 调用自定义的初始化方法
  9. 调用BeanPostProcessor的初始化后方法
    bean可以使用了

容器关闭

  1. 调用DisposableBean的destroy()方法
  2. 调用自定义的销毁方法

Spring模块

核心容器

最核心部分,包含Beans, Core, Context, Expression, ContextSupport

AOP模块

包含AOP和Aspects

数据访问与集成模块

Web与远程调用

Instrumentation

提供了为JVM添加代理的功能,为Tomcat提供了一个织入代理,能够为Tomcat传递类文件

测试

装配Bean

三种装配方式

  1. 在xml中进行显示配置
  2. 在Java中进行显示配置
  3. 隐式的bean发现机制和自动装配

自动装配

注解的形式
关键的注解:@Autowired, @ComponentScan
Spring从两个角度来实现自动化装配:

  1. 组件扫描(component scanning):自动发现应用上下文创建的bean
  2. 自动装配(autowiring):Spring自动满足bean之间的依赖
package soudsystem;
public interface CompactDisc {void play();
}
package soundsystem;
import org.springframework.stereotype.Component;@Component
public class SgtPeppers implements CompactDisc {private String title = "abc";private String artist = "the beatles";public void play() {System.out.println(title + artist);}
}
package soundsystem;
import org.springframework.context.annotation.componentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan
public class CDPlayerConfig {}

这是用Java配置类来开启组件扫描,还有一种用XML的方式开启组件扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""
<!-- 引入context的命名空间xmlns,可以在官网上找到-->
>
<context:component-scan base-package="soudsystem"/>
</beans>

接着就是编写测试类

package soundsystem;
/**
*这里有一系列的包导入
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(class=CDPlayerConfig.class)
public class CDPlayerTest {@Autowiredprivate CompactDisc cd;@Testpublic void cdShouldNotBeNull() {assertNotNull(cd);}
}

bean的命名

Spring会自动生成bean的ID,要想自定义ID只需要在@Component(“name”) 括号中填写ID就行了

设置组件扫描的基础包

@ComponentScan(basePackages=“soundsystem”)
@ComponentScan(basePackages={"soundsystem", "video"})
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})

为bean添加注解实现自动装配

@Autowired
@Autowired不仅可以用在属性上,也可以用在方法上,对方法的参数实现自动装配

Java代码装配

创建配置类

package soundsystem;
import org.springframework.context.annotation.Configuration@Configuration
public class CDPlayerConfig {}

声明Bean

在JavaConfig中声明bean

@Bean
public CompactDisc sgtPeppers() {return new SgtPeppers();
}

bean的ID和方法名一样,也可以通过@Bean(name=“”)来指定方法名

实现注入

装配Bean最简单的方式就是引用创建bean的方法

@Bean
public CDPlayer cdPlayer() {return new CDPlayer(sgtPeppers());
}

因为sgtPeppers()方法上添加了@Bean注解,Spring会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
因为默认情况下Spring中的bean都是单例的。
还有另外一种引用bean的方式

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {return new CDPlayer(compactDisc);
}

通过XML装配bean

使用XML配置要在配置文件顶部声明多个XML模式(XSD)文件。
可以借助Spring Tool Suite来生成,IDEA也可以自动生成。

声明一个简单的bean

<bean id="compactDisc" class="soundsystem.SgtPeppers"/>

两个缺点:

  1. bean的创建被动,功能不够强大
  2. 不能进行编译期的类型检查

借助构造器注入初始化bean

使用元素
<bean id="cdPlayer" class="soundsystem.CDPlayer"><constructor-arg ref="compactDisc"/>
</bean>

以字面量的形式注入到构造器

<bean id="cdPlayer" class="soundsystem.CDPlayer"><constructor-arg value="compactDisc"/>
</bean>

注入的是列表,集合,数组

<constructor-arg><set><value></value></set>
</constructor-arg>
<constructor-arg><list><value></value></list>
</constructor-arg>
c-命名空间
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc"/>

c-: 是命名空间的前缀 cd: 构造器参数名 -ref: 注入bean引用
构造器参数名也可以换成参数的索引,因为XML不支持第一个字为数字,要加一个_,比如第一个参数用_0
以字面量的形式注入到构造器

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_title="compactDisc"/>

属性注入

类中 setter方法

<bean id="cdPlayer" class="soundsystem.CDPlayer"><property name="compactDisc" ref="compactDisc"/>
</bean>
p命名空间

首先要在头对其进行声明,然后使用

<bean id="cdPlayer" class="soundsystem.CDPlayer"
p:compactDisc-ref="compactDisc"/>

命名规则类比c命名空间

util命名空间

可以协助我们进行list,map,set的注入

<util:list id="trackList"><value></value><value></value>
</util:list>

Spring util-命名空间中的元素

|元素|描述|
|—|—|
|util:constant|引用某个类型的public static域|
|util:list|java.util.List|
|util:map|java.util.Map|
|util:properties|java.util.Properties|
|util:property-path|引用一个bean的属性|
|util:set|java.util.Set|

导入和混合配置

在JavaConfig中引用XML配置

如果引入另一个Java配置类使用@Import(CDConfig.class)
引入配置在XML之中的Bean用@ImportResource注解
@ImportResource(“classpath:cd-config.xml”)

在XML配置中引用JavaConfig

引用另一个XML
<import resource="cdplayer-config.xml"/>
引用JavaConfig
<bean class="soundsystem.CDConfig"/>

高级装配

开发和上线部署环镜会需要不同的bean。通过profile可以给不同环境配置不同的bean

配置profile bean

Java配置

使用@Profile注解

@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {}

Spring3.2以后可以在方法上使用@Profile

在XML配置

<beans profile=“dev”> </beans>

激活profile

Spring确定激活哪个profile依赖两个属性:

  1. spring.profiles.active
  2. spring.profiles.default
    可以通过Web应用的context来设置这两个属性
<context-param><param-name>spring.profiles.default</param-name><param-value>dev</param-value>
</context-param>

通过Servlet的初始化参数

<servlet><servlet-name>appServlet</servlet-name><servlet-class></servlet-class><init-param><param-name>spring.profiles.default</param-name><param-value>dev</param-value></init-param>
</servlet>

要在测试类设置通过@ActiveProfiles(“dev”)

条件化bean

我们需要在某些条件下生成Bean,在某些条件下不生成
使用@Conditional注解,给定的条件计算结果为true就会创建bean,否则就不会创建

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

@Contional是通过Condition接口进行的条件对比:

public interface Condition {boolean matches(ConditionContext ctxt, AnnotatedTypeMedtadata metadata);
}

我们需要通过实现这个接口来完成具体的条件计算:

public class MagicExistsCondition implements Condition {public boolean matches(ConditionContext context, AnnotatedTypeMedtadata metadata) {Environment env = context.getEnvironment();return env.containsProperty("magic");   }
}

ConditionContext也是一个接口,源码:

public interface ConditonContext {/***检查Bean定义*/BeanDefinitionRegistry getRegistry();/***检查bean是否存在,甚至探查bean的属性*/ConfigurableListableBeanFactory getBeanFactory();/***检查环境变量是否存在以及它的值是什么*/Environment getEnvironment();/***所加载的资源*/ResourceLoader getResourceLoader();/***返回类加载器,并检查类是否存在*/ClassLoader getClassLoader();
}

AnnotatedTypeMetadata用来检查@Bean方法上还有什么其他注解

public interface AnnotatedTypeMetadata {boolean isAnnotated(String annotationType);Map<String, Object> getAnnotationAttributes(String annotationType);Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType, boolean classValuesAsString);
}

从Spring4开始, @Profile基于@Conditional和@Condition实现

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {String[] value();
}

ProfileCondition

class ProfileCondition implements Condition {public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {if (context.getEnvironment() != null) {MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());if (attrs != null) {for (Object value : attrs.get("value")) {if (context.getEnvironment()
.acceptsProfiles(((String[]) value))) {return true;}}return false;}}return true;}
}

处理自动装配的歧义性

歧义性发生情况:一个接口有多个实现类,同时多个实现类都是bean,自动注入接口的bean时便无法判断注入哪个bean

标示首选bean

  1. @Primary和@Component|@Bean组合使用
  2. <bean id=“iceCream” class=“com.desserteater.IceCream” primary=“true”/>

限定自动装配的bean

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {this.dessert = dessert;
}

“iceCream”是bean的ID,我们可以自定义限定符

@Component
@Qualifier("cold")
public class IceCream implements Dessert {}

当我们多个bean都想要用这个限定符,使用时也会发生歧义,所以我们必须要多加个@Qualifier,但是编译器不允许添加多个@Qualifier,可以通过自定义注解来完成。

@Target({ElementType.CONSTRUCTOR, ELementType.FIELD,ElementType.METHOD, ELementType.TYPE})
@Rentention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {}

bean的作用域

默认的bean是单例的,但是对于易变类型单例并不合适
Spring定义了多种作用域

  1. Singleton
  2. Prototype
  3. Session
  4. Request
    对于使用组件扫描来发现和声明bean,可以使用@Scope注解
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad {}

使用Java配置

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public NotePad notepad() {return new Notepad();
}

使用xml

<bean id="notepad" class="com.myapp.Notepad" scope="prototype"/>

使用会话和请求作用域

以电子商务作为例子,在电子商务中购物车往往都不是单例的,而购物网站服务往往是单例的。
购物车代码:

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

proxyMode这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。
网站服务代码:

@Component
public class StoreService {@Autowiredpublic void setShoppingCart(ShoppingCart shoppingCart) {this.shoppingCart = shoppingCart;}
}

StoreService是个单例的bean,单例的bean会在Spring应用上下文加载的时候创建。但是当它创建时ShoppingCart bean并不存在,直到有对话创建才会存在。而且系统中会有多个ShoppingCart实例,无法注入某个固定的ShoppingCart实例到StoreService中。
Spring注入的实际上是一个到ShoppingCart bean的代理,代理暴露了接口的所有方法
[image:25F34FB8-8997-48E3-8AE4-4593E4B9F12A-743-000008F9ED04D19F/78D6077F-5506-41F6-BA48-1BBDF924A240.png]
proxyMode属性被设置成了ScopedProxyMode.INTERFACES,这表明这个代理要实现ShoppingCart接口。如果要生成基于类的代理要使用CGLib,proxyMode属性设置为ScopedProxyMode.TARGET_CLASS

在XML中声明作用域代理

<bean id="cart" class="com.myapp.ShoppingCart" scope="session">
<aop:scoped-proxy/>

默认是类的代理,实现接口的代理将proxy-target-class属性设置为false,注意使用前同样要声明aop命名空间。

运行时值的注入

注入外部的值

声明属性源并通过Spring的Environment来检索属性

@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")
public class ExpressiveConfig {@AutowiredEnvironment env;@Beanpublic BlankDisc disc() {return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));}
}

app.properties:

disc.title=abc
disc.artist=abc
使用属性占位符

${}
xml

<bean id="sgtPeppers" class="soundsystem.BlankDisc" c:_title="${disc.title}" c:_artist="${disc.artist}"/>

组件扫描和自动装配

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

要使用占位符还需要配置PropertyPlaceholderConfigurer bean或PropertySourcePlaceholderConfigurer bean

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

or

<context:property-placeholder/>

使用Spring表达式语言(SpEL)进行装配

#{}

SpEL

表示字面值

#{3.1415}
#{'Hello'}
#{false}

通过ID引用其他的bean

#{sgtPeppers}

引用bean的属性或方法

#{sgtPeppers.artist}
#{sgtPeppers.selectArtist()}
#{sgtPeppers.selectArtist()?.toUpperCase()}

T()运算符,T()运算符的结果会是一个Class对象

T(java.lang.Math)

ternary && Elvis

#{scoredboard.score > 1000 ? "Winner!" : "Losser!"}
#{disc.title ?: "Rattle"}

regular expression

#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}

查询运算符

#{jukebox.songs.?[artist eq 'qbc']}
#{jukebox.songs.^[artist eq 'qbc']}
#{jukebox.songs.$[artist eq 'qbc']}

投影运算符

#{jukebox.songs.![title]}

Spring的Environment

Environment提供了四种获得属性的方式

String getProperty(String key)
String getProperty(String key, String defaultValue)
T getProperty(String key, Class<T> type)
T getProperty(String key, Class<T> type, T defaultValue)

获得的属性必须要定义

String getRequiredProperty(String key)

检查某个属性是否存在

boolean containsProperty(String key)

将属性解析为类

Class<CompactDisc> cdClass = env.getPropertyasClass("disc.class", CompactDisc.class);

检查哪些profile处于激活状态

String[] getActiveProfiles()
String[] getDefaultProfiles()
boolean acceptsProfiles(String... profiles)

面向切面的Spring

散布于应用中多处的功能被称为横切关注点(cross-cutting concern),这些横切关注点是与应用业务逻辑相分离的。横切关注点可以模块化为类,这些类被称为切面

AOP术语

[image:7077B788-D72D-4014-830C-9E0D950712BC-743-000021664EE25806/A322F226-A662-46B7-933A-F270E9778123.png]

  1. Advice: 切面的工作。通知定义了切面是什么以及何时使用。5种通知类型:前置通知,后置通知,返回通知,异常通知,环绕通知
  2. Join point: 连接点是应用执行过程中能够插入切面的一个点
  3. Point cut: 切点的定义会匹配通知所要织入的一个或多个连接点
  4. Aspect: 切面是通知和切点的结合
  5. Introduction: 引入允许我们向现有的类添加新方法和属性
  6. Weaving: 织入是把切面应用到目标对象并创建新的代理对象的过程。织入时期有:编译期,类加载期,运行期

通过切点来选择连接点

编写切点

定义一个Performance接口

package concert;
public interface Performance {public void perform();
}

使用AspectJ切点表达式来选择

execution(* concert.Performance.perform(..))

让切点仅匹配concert包

execution(* concert.Performance.perform(..)) && within(concert.*)

在切点中选择bean

execution(* concert.Performance.perform(..)) and bean('woodstock')

使用注解创建切面

定义切面

package concert
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class Audience {@Before("execution(** concert.Performance.perform(..))")public void silenceCellPhones() {}@AfterReturning("execution(** concert.Performance.perform(..))")public void applause() {}@AfterThrowing("execution(** concert.Performance.perform(..))")public void demandRefund() {}
}

上面重复写了相同的切点表达式,我们可以通过@Pointcut定义可重用切点

package concert
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class Audience {@Pointcut("execution(** conert.Performance.perform(..))")public void performance() {}@Before("performance()")public void silenceCellPhones() {}@AfterReturning("performance()")public void applause() {}@AfterThrowing("performance()")public void demandRefund() {}
}

在JavaConfig启用自动代理

package concert;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class ConcertConfig {@Beanpublic Audience audience() {return new Audience();}
}

在XML中启用自动代理

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="..."><context:component-scan base-package="concert"/><aop:aspectj-autoproxy/><bean class="concert.Audience"/>
</beans>

创建环绕通知

环绕通知=前置通知+后置通知

package concert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;@Aspect
public class Audience {@Pointcut("execution(** concert.Performance.perform(..))")public void performance();@Around("performance()")public void watchPerformance(ProceedingJoinPoint jp) {try {System.out.println("before");//通过它来调用被通知的方法jp.proceed();System.out.println("after");} catch (Throwable e) {System.out.println("exception");}}
}

处理通知中的参数

使用args()的AspectJ切点表达式

@Pointcut("execution(* soundsystem.CompactDisc.playTrack(int)) && args(trackNumber)")
public void trackPlayed(int trackNumber) {}

通过注解引入新功能

[image:40F5713D-BAEB-422E-BB68-DCDFB5424506-743-0000367A8E10EAB8/ECD87204-224B-470F-914E-E9459C8DAFC4.png]
假设某些类要实现下面的接口

package concert;
public interface Encoreable {void performEncore();
}

新建一个切面

package concert;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;@Aspect
public class EncoreableIntroducer {@DeclareParents(value="concert.Performance+", defaultImpl=DefaultEncoreable.class)public static Encoreable encoreable;
}
  • value属性指定了哪种类型的bean要引入该接口
  • defaultImpl属性指定了为引入功能提供的类
  • @DeclareParents注解所标注的静态属性指明了要引入的接口
    我们同样要把EncoreableIntroducer声明为一个bean。
    <bean class="concert.EncoreableIntroducer"/>

在XML中声明切面

好处:能够以非入侵的方式声明切面

声明前置和后置通知

Java类是上面用的Audience
通过XML将无注解的Audience声明为切面

<aop:config><aop:aspect ref="audience"><aop:before pointcut="execution(** concert.Performance.perform(..))" method="silenceCellPhones"/><aop:after-returnning pointcut="execution(** concert.Performance.perform(..))" method="applause"/><aop:after-throwing pointcut="execution(** concert.Performance.perform(..))" method="demandRefund"/></aop:aspect>
</aop:config>

同样有aop:pointcut来声明重复的切点

<aop:config><aop:aspect ref="audience"><aop:pointcut id="performance" expression="execution(** concert.Performance.perform(..))"/><aop:before pointcut-ref="performance" method="silenceCellPhones"/><aop:after-returnning pointcut-ref="performance" method="applause"/><aop:after-throwing pointcut-ref="performance" method="demandRefund"/></aop:aspect>
</aop:config>

声明环绕通知

<aop:around poincut-ref=“” method=“”/>

为通知传递参数

和bean的装配一样

通过切面引入新的功能

<aop:aspect><aop:declare-parents types-matching="concert.Performance+" implement-interface="concert.Encoreable" default-impl="concert.DefaultEncoreable"/>
</aop:aspect>

types-matching指定类型匹配接口 implement-interface增加Encoreable接口。
Encoreable接口的方法指定通过default-impl或者delegate-ref来指定,delegate-ref引用的是一个bean的ID。

注入AspectJ切面

使用AspectJ实现表演的评论员

package concert;public aspect CriticAspect {public CriticAspect() {}pointcut performance() : execution(* perform(..));afterReturning(): performance() {System.out.println(criticismEngine.getCriticism());}private CriticismEngine criticismEngine;public void setCriticismEngine(CriticsmEngine criticismEngine) {this.criticismEngine = criticismEngine;}
}

在XML中声明这个aspect

<bean class="com.springinaction.springidol.CriticAspect" factory-method="aspectOf"><property name="criticismEngine" ref="criticismEngine"/>
</bean>

学习Spring Boot前需要了解的Spring基础知识相关推荐

  1. spring boot 前后端分离项目(商城项目)学习笔记

    spring boot 前后端分离项目(商城项目)学习笔记 目录 spring boot 前后端分离项目(商城项目)学习笔记 后端配置 springboot项目 pom.xml文件 maven 配置文 ...

  2. sm4 前后端 加密_这7个开源的Spring Boot前后端分离项目整理给你

    来源|公众号:江南一点雨 前后端分离已经开始逐渐走进各公司的技术栈,不少公司都已经切换到前后端分离开发技术栈上面了,因此建议技术人学习前后端分离开发以提升自身优势.同时,也整理了 7 个开源的 Spr ...

  3. 《Vue+Spring Boot前后端分离开发实战》专著累计发行上万册

                杰哥的学术专著<Vue+Spring Boot前后端分离开发实战>由清华大学出版社于2021年3月首次出版发行,虽受疫情影响但热度不减,受到业界读者的热捧,截至今日 ...

  4. Vue+Spring boot前后端响应流程总结

    Vue+Spring boot前后端响应流程总结 前端请求页面路径,首先会经过路由: 经过解决跨域问题以后,就会请求到后端接口,后端接口返回的数据会封装到then回调方法的res参数中. 经过回调函数 ...

  5. Spring Boot前后端分离项目Session问题解决

    Spring Boot前后端分离项目Session问题解决 参考文章: (1)Spring Boot前后端分离项目Session问题解决 (2)https://www.cnblogs.com/sooo ...

  6. Spring Boot前后端分离之后端开发

    Spring Boot前后端分离开发之后端开发 前后端分离开发概述 相关术语 前后端分离开发概述 接口规范 RESTful API的理解 RESTful风格的特点 URI规范 路径 请求方式 过滤条件 ...

  7. Spring Boot 2.0(三):Spring Boot 开源软件都有哪些?

    2016年 Spring Boot 还没有被广泛使用,在网上查找相关开源软件的时候没有发现几个,到了现在经过2年的发展,很多互联网公司已经将 Spring Boot 搬上了生产,而使用 Spring ...

  8. 使用 Spring Boot CLI 运行第一个Spring boot程序

    简介 Spring Boot CLI是Spring Boot的命令行界面.它可以用来快速启动Spring.  它可以运行Groovy脚本.  Spring Boot CLI是创建基于Spring的应用 ...

  9. spring boot jar包_「Spring Boot 新特性」 jar 大小自动瘦身

    自动分析瘦身 Spring Boot 项目最终构建处理 JAR 包大小一直是个诟病,需要把所有依赖包内置最终输出可运行的 jar.当然可以使用其他的插件扩展 实现依赖 JAR 和 可运行 jar 分离 ...

最新文章

  1. 《OpenCV3编程入门》学习笔记5 Core组件进阶(四)图像对比度、亮度值调整
  2. [inside]MySQL 5.7 并行复制实现原理与调优
  3. Error was tenMinuteCache Cache: The Disk store is not active.
  4. QT的QTextLayout类的使用
  5. C++ inline
  6. win mysql 2003错误_windows MySql 报1067错误 2003错误
  7. vue-cli2定制ant-design-vue主题
  8. FastDFS 入门简介
  9. ES6 学习笔记 (1)
  10. 三种坐标系经纬度转化小工具
  11. sar图像matlab,用Matlab制作SAR仿真图像
  12. 在js中调用dede标签
  13. 神经网络及其变种串联
  14. uni-app云开发的网盘助手微信小程序源码抓取网盘资源引流好助手
  15. Opencv3.2移植到arm板
  16. 计算机显示器的ppt,计算机硬之显示器.ppt
  17. 语音朗读html的源码,在网页上通过JS实现文本的语音朗读
  18. WebRequest 类和 WebResponse 类
  19. C语言数据结构一元多项式
  20. win 2016 ssh_【Win】Print Conductor 全能批量打印工具兼容所有打印机

热门文章

  1. HibernateUtil类(可用于连接多个数据库)
  2. 欧亚马 java折叠车_如何选择欧亚马折叠车?
  3. springBoot项目启动去掉多余的启动日志
  4. Java系统中如何拆分同步和异步
  5. 创建索引时,键列位置的重要性
  6. skip-grant-tables:非常有用的mysql启动参数
  7. JS判断对象是不是数组“Array”
  8. cocos2dx打飞机项目笔记三:HeroLayer类和坐标系
  9. Linux内核将用Nftables替代iptables
  10. 传智播客--itcastbbs(四)