application.yml 或 application.properies
server.port=8080 #application.properies配置方法
server:
port: 8080 #application.yml配置方法
用来简化spring应用的初始搭建及开发过程,使用特定的方式来进行配置(properties,yml)创建独立的spring引用程序,main方法运行,嵌入tomcat,无需部署war, 自动配置spring,添加对应功能的starter自动化配置
优点:
快速创建独立运行的spring项目与主流框架集成;
使用嵌入式的servlet容器,应用无需打成war包
starter自动依赖与版本控制
大量的自动配置,简化开发,也可以修改默认值
准生产韩静的运行应用监控
与云计算的天然集成
缺点:
组件过于繁重,臃肿
springboot的核心注解是 @SpringBootApplication, 他主要包含了一下几个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描
不需要, springboot中默认依赖了tomcat, 也可以选择使用jetty, springboot只需要打成jar包,通过main方法就可以直接运行
从main函数说起,一切的开始要从SpringbootApplication注解说起。每个main方法上都有SpringBootApplication注解
@SpringBootApplication
public class MyBootApplication {
public static void main(String[] args) {
SpringApplication.run(MyBootApplication.class);
}
}
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
}
其中最重要的就是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 {};
}
通过Import注解导入AutoConfigurationImportSelector。在这个类中加载/META-INF/spring.factories文件的信息,然后筛选出以EnableAutoConfiguration为key的数据,加载到IOC容器中,实现自动配置功能。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
从表面看就是自动配置包,主要使用了Import注解,导入了Registrar类。这里Registrar类的registerBeanDefinitions方法导包,也就是导入当前main函数所在路径的包地址
怎么自动装配其他N个类
Import({AutoConfigurationImportSelector.class})该注解给当前配置类导入另外N个自动配置类。
这里既然导入N个自动配置类,那么都导入哪些类呢?
//AutoConfigurationImportSelector实现DeferredImportSelector接口,而DeferredImportSelector接口又继承了ImportSelector
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
AutoConfigurationImportSelector通过实现接口ImportSelector的selectImports方法返回需要导入的组件,selectImports方法返回一个全类名字符串数组。
//AutoConfigurationImportSelector.java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
return configurations;
}
这里又开始调用SpringFactoriesLoader.loadFactoryNames。 SpringFactoriesLoader.loadFactoryNames方法中关键的三步: (1)从当前项目的类路径中获取所有 META-INF/spring.factories 这个文件下的信息. (2)将上面获取到的信息封装成一个 Map 返回,EnableAutoConfiguration为key。 (3)从返回的Map中通过刚才传入的 EnableAutoConfiguration.class参数,获取该 key 下的所有值。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
自动配置都有哪些内容呢?
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...其他省略
XXXAutoConfiguration和XXProperties
在spring.factories文件中看到的都是自动配置类,那么自动配置用到的属性值在那里呢?我们拿出redis为例
Configuration
@ConditionalOnClass(RedisOperations.class) //判断当前项目有没有这个类RedisOperations.class
@EnableConfigurationProperties(RedisProperties.class) //启用配置属性,这里看到了熟悉的XXXProperties
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) //导入这两个类
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
//这里则保存redis初始化时的属性
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private boolean ssl;
}
6. @RequestMapping 和 GetMapping的区别是什么
见下文。
1. 实例化Bean: Ioc容器通过获取BeanDefinition对象中的信息进行实例化,实例化对象被包装在BeanWrapper对象中
2. 设置对象属性(DI):通过BeanWrapper提供的设置属性的接口完成属性依赖注入;
3. 注入Aware接口(BeanFactoryAware, 可以用这个方式来获取其它 Bean,ApplicationContextAware):Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean
4. BeanPostProcessor:自定义的处理(分前置处理和后置处理)
5. InitializingBean和init-method:执行我们自己定义的初始化方法
6. 使用
7. destroy:bean的销毁
这里首先需要说明的一点是,Spring实例化bean是通过ApplicationContext.getBean()方法来进行的。如果要获取的对象依赖了另一个对象,那么其首先会创建当前对象,然后通过递归的调用ApplicationContext.getBean()方法来获取所依赖的对象,最后将获取到的对象注入到当前对象中。
这里我们以上面的首先初始化A对象实例为例进行讲解。首先Spring尝试通过ApplicationContext.getBean()方法获取A对象的实例,由于Spring容器中还没有A对象实例,因而其会创建一个A对象,然后发现其依赖了B对象,因而会尝试递归的通过ApplicationContext.getBean()方法获取B对象的实例,但是Spring容器中此时也没有B对象的实例,因而其还是会先创建一个B对象的实例。读者需要注意这个时间点,此时A对象和B对象都已经创建了,并且保存在Spring容器中了,只不过A对象的属性b和B对象的属性a都还没有设置进去。
在前面Spring创建B对象之后,Spring发现B对象依赖了属性A,因而此时还是会尝试递归的调用ApplicationContext.getBean()方法获取A对象的实例,因为Spring中已经有一个A对象的实例,虽然只是半成品(其属性b还未初始化),但其也还是目标bean,因而会将该A对象的实例返回。此时,B对象的属性a就设置进去了,然后还是ApplicationContext.getBean()方法递归的返回,也就是将B对象的实例返回,此时就会将该实例设置到A对象的属性b中。这个时候,注意A对象的属性b和B对象的属性a都已经设置了目标对象的实例了。
读者朋友可能会比较疑惑的是,前面在为对象B设置属性a的时候,这个A类型属性还是个半成品。但是需要注意的是,这个A是一个引用,其本质上还是最开始就实例化的A对象。而在上面这个递归过程的最后,Spring将获取到的B对象实例设置到了A对象的属性b中了,这里的A对象其实和前面设置到实例B中的半成品A对象是同一个对象,其引用地址是同一个,这里为A对象的b属性设置了值,其实也就是为那个半成品的a属性设置了值
spring启动器是一套方便的依赖,没有描述符,它可以放在自己的程序中。你可以放在自己的程序中,你可以一站式的获取你所需要的spring和相关技术,而不需要依赖描述符的通过实例代码搜索和复制粘贴的负载。
com.mysql.jdbc.Driver 是 mysql-connector-java 5 中的。 com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6 中的。
springboot actuator 是springboot的监视器,可以帮助我们在访问生产环境中正在运行的应用程序的当前状态,在生产环境中必须检查和监视几个指标。甚至一些外部应用程序也可能使用这些服务来出发对相关人员的报警功能。
面向切面编程(AOP):允许程序员模块化横向业务逻辑,或定义核心部分的功能,例如日志管理和事务管理。
切面(Aspect) :AOP的核心就是切面,它将多个类的通用行为封装为可重用的模块。该模块含有一组API提供 cross-cutting功能。例如,日志模块称为日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。
通知(Advice):通知表示在方法执行前后需要执行的动作。实际上它是Spring AOP框架在程序执行过程中触发的一些代码。Spring切面可以执行一下五种类型的通知:
切入点(Pointcut):切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。
引入:引入允许我们在已有的类上添加新的方法或属性。
目标对象:被一个或者多个切面所通知的对象。它通常是一个代理对象。也被称做被通知(advised)对象。
代理:代理是将通知应用到目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。有以下几种代理:
织入:将切面和其他应用类型或对象连接起来创建一个通知对象的过程。织入可以在编译、加载或运行时完成。
Spring-boot-starter-web
Spring-boot-starter-test
Spring-boot-starter-jdbc
Spring-boot-starter-dat-jpa
Spring-boot-starter-rest
Spring-boot-starter-actuator
pom文件中
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
</parent>
或者
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
application-dev.yml application-test.yml application-prod.yml
通过 : spring.profiles.active= test 来指定
写一个类实现 HandlerInterceptor接口, 重新preHandle方法,里边为 拦截器的逻辑
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Resource
private UserCenterClient userCenterClient;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果资源来源是 app ,直接放行
String requestSourceType = request.getHeader("requestSourceType");
String token = request.getHeader("token");
log.debug("token拦截器开始执行, 访问url{}, token:{}, requestSourceType{}", request.getRequestURI(), token, requestSourceType);
Result<Boolean> result = userCenterClient.verifyToken(token, requestSourceType);
if (result.getCode() != Result.success().getCode()) {
if (result.getData() == null || !result.getData()) {
log.info("token {} ,校验失败,请重新登录");
throw new BizException(result.getCode(), result.getMsg());
}
}
return true;
}
}
然后在一个配置类中添加刚才这个类
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor() {
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor()).addPathPatterns("/**");
}
}
编程式事务
声明式事务
Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。 Spring 为 Resource 接口提供了如下实现类:
实现AOP的技术,主要分为两大类:
Spring AOP 的实现原理其实很简单:AOP 框架负责动态地生成 AOP 代理类,这个代理类的方法则由 Advice和回调目标对象的方法所组成, 并将该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异,AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。
Spring AOP使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于JDK的动态代理(JDK本身只提供接口的代理)和基于CGlib的动态代理。
由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首先用spring中的IoC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。
Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。 为常用数据源支持提供了一系列的TransactionManager。
PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系
1) properties 2) yaml 3) 系统环境变量 4) 命令行参数
实现 WebMvcConfigurer 接口
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
/** 添加拦截器 **/
void addInterceptors(InterceptorRegistry registry);
/** 这里配置视图解析器 **/
void configureViewResolvers(ViewResolverRegistry registry);
/** 配置内容裁决的一些选项 **/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 视图跳转控制器 **/
void addViewControllers(ViewControllerRegistry registry);
/** 静态资源处理 **/
void addResourceHandlers(ResourceHandlerRegistry registry);
/** 默认静态资源处理器 **/
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
SpringBoot 中怎么禁用某些自动配置特性?
exclude()
spring boot 核心的两个配置文件:
bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 boostrap 里面的属性不能被覆盖; application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
可以使用@ControllerAdvice 和 @ExceptionHadnler
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
/**
* 请求参数异常处理
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
......
}
}
@ControllerAdvice: 会对所有的Controller做增强,可以做全局异常处理,全局数据绑定和全局数据预处理
ExceptionHandler用于指定对哪类异常做处理。