导图社区 Spring
Spring基础 高级功能详解,如 additional-spring-configuration-metadata.json将会合并到spring-configuration-metadata.json中,并覆盖掉相同的说明。
编辑于2023-09-20 16:07:09高级功能
MappingJackson2HttpMessageConverter
GenericFilterBean
SPI(service provider interface)
RequestResponseBodyMethodProcessor
参数转换
使用技巧
有些数据需要静态方法访问,但是又需要SpringBean,此时可以给写一个Bean,将其注入Spring,同时添加给这个Bean添加静态变量值
拦截器
使用步骤
原生代码实现
注解实现
特点
可以获取IOC容器中的各个bean
是spring提供并管理的
拦截器(代理模式)的实现基于反射
过滤器
过滤器能够在请求到达Servlet方法之前拦截,以及在Servlet执行完,返回到控制器时拦截Response
使用步骤
原生代码实现
实现Filter接口
void init(FilterConfig filterConfig)
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
void destroy()
注入
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean registFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new LogCostFilter()); registration.addUrlPatterns("/*"); registration.setName("LogCostFilter"); registration.setOrder(1); return registration; } }
注解实现
@WebFilter
实现Filter接口
特点
基于URL
属于Servlet规范,只能操作request和response
在请求进入容器后,但请求进入servlet之前进行预处理
结束返回也是,是在servlet处理完后,返回给前端之前
过滤器的实现基于回调函数
实体类自动导入
HttpServletRequest
Environment
WebApplicationInitializer
@Configuration public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.setInitParameter( "spring.profiles.active", "dev"); } }
MessageSource
说明
用于解析消息,并支持消息的参数化和国际化
org.springframework.context.support.AbstractApplicationContext.refresh方法有initMessageSource()方法进行了MessageSource初始化
国际化
https://developer.aliyun.com/article/1093644
https://geek-docs.com/spring/spring-tutorials/messagesource.html
默认实现
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
ConfigurableApplicationContext
工具类
AnnotationUtils
SpringFactoriesLoader
spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能 会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖
loadFactories
根据接口类获取其实现类的实例,这个方法返回的是对象列表
loadFactoryNames
根据接口获取其接口类的名称,这个方法返回的是类名的列表
RequestContextHolder
LocaleContextHolder
Yaml文件解析
new YamlPropertiesFactoryBean
META-INF
additional-spring-configuration-metadata.json
博客: https://segmentfault.com/a/1190000020247777 作用: springboot项目中,在application.yml配置文件中编写配置时,会有提示功能,该提示内容就是由spring-configuration-metadata.json文件提供 分部解释: groups:添加@ConfigurationProperties注解对应的类 properties:叶子节点介绍 hints:对已经存在的节点进一步解释(比如一个节点可以有多个固定值,就可以用hints) 属性介绍: name String 属性的全名。名称采用小写的周期分隔形式(例如server.address)。此属性是强制性的。 type String 属性的数据类型的完整签名(例如java.lang.String),但也是完整的泛型类型(例如java.util.Map<java.util.String,acme.MyEnum>)。您可以使用此属性来指导用户可以输入的值的类型。为了保持一致性,通过使用其包装对应项(例如,boolean变为java.lang.Boolean)来指定基元的类型。请注意,此类可能是一个复杂类型,它从Stringas绑定的值转换而来。如果类型未知,则可以省略。 description String 可以向用户显示的组的简短描述。如果没有可用的描述,则可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾。 sourceType String 贡献此属性的源的类名称。例如,如果属性来自带注释的类@ConfigurationProperties,则此属性将包含该类的完全限定名称。如果源类型未知,则可以省略。 defaultValue Object 默认值,如果未指定属性,则使用该值。如果属性的类型是数组,则它可以是值数组。如果默认值未知,则可以省略。 level String 弃用级别,可以是warning(默认)或error。当属性具有warning弃用级别时,它仍应绑定在环境中。但是,当它具有error弃用级别时,该属性不再受管理且不受约束。 reason String 该属性被弃用的原因的简短描述。如果没有可用的原因,可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾。 replacement String 替换此不推荐使用的属性的属性的全名。如果此属性没有替换,则可以省略。
作用
提示
在配置文件中添加配置时,可以自定义提示
提示内容就是由spring-configuration-metadata.json文件提供
特点
需要手动添加
level
弃用级别,可以是warning(默认)或error。当属性具有warning弃用级别时,它仍应绑定在环境中。但是,当它具有error弃用级别时,该属性不再受管理且不受约束
reason
该属性被弃用的原因的简短描述。如果没有可用的原因,可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾
replacement
替换此不推荐使用的属性的属性的全名。如果此属性没有替换,则可以省略
属性
groups
添加@ConfigurationProperties注解对应的类
name
type
sourceType
description
properties
叶子节点介绍
name
属性的全名。名称采用小写的周期分隔形式(例如server.address)。此属性是强制性的
type
属性的数据类型的完整签名(例如java.lang.String),但也是完整的泛型类型(例如java.util.Map )。您可以使用此属性来指导用户可以输入的值的类型。为了保持一致性,通过使用其包装对应项(例如,boolean变为java.lang.Boolean)来指定基元的类型。请注意,此类可能是一个复杂类型,它从Stringas绑定的值转换而来。如果类型未知,则可以省略
description
可以向用户显示的组的简短描述。如果没有可用的描述,则可以省略。建议描述为简短段落,第一行提供简明摘要。描述中的最后一行应以句点(.)结尾
sourceType
贡献此属性的源的类名称。例如,如果属性来自带注释的类@ConfigurationProperties,则此属性将包含该类的完全限定名称。如果源类型未知,则可以省略
defaultValue
默认值,如果未指定属性,则使用该值。如果属性的类型是数组,则它可以是值数组。如果默认值未知,则可以省略
hints
对已经存在的节点进一步解释(比如一个节点可以有多个固定值,就可以用hints)
name
节点全名
values
节点可赋值列表+说明
value
值
description
描述
providers
name
parameters
实例
{"groups": [ { "name": "server", "type": "org.springframework.boot.autoconfigure.web.ServerProperties", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" } ... ],"properties": [ { "name": "server.port", "type": "java.lang.Integer", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" } ... ],"hints": [ { "name": "spring.jpa.hibernate.ddl-auto", "values": [ { "value": "none", "description": "Disable DDL handling." }, { "value": "validate", "description": "Validate the schema, make no changes to the database." } ] } ]}
spring-configuration-metadata.json
作用: 与additional-spring-configuration-metadata.json作用一样,数据格式也一样 注意: 主要的元数据文件是在编译器通过处理所有被@ConfigurationProperties注解的节点来自动生成的。
特点
自动生成
需要依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
概要
additional-spring-configuration-metadata.json将会合并到spring-configuration-metadata.json中,并覆盖掉相同的说明
前者和后者的书写格式是一致的,起到相同的作用。只不过我们一般让IDE maven build来生成前者,而后者由开发者另外编写,以便补充自动生成的前者。
spring会读取这两个文件,在内部合并配置。
spring-autoconfigure-metadata.properties
作用:Spring实例化Bean的时候,我们可以在Bean上添加一些加载条件,只有满足条件的Bean才会被初始化到Spring容器。通常是在Bean上以注解的方式实现,另外一种方式就是在spring-autoconfigure-metadata.properties文件中添加条件 spring-autoconfigure-metadata.properties格式 自动配置的类全名.条件=值 解释:类名表示被初始化的Bean,条件相当于注解中的条件类(如:@ConditionalOnClass等),值相当于注解中的值 自定义condition可模仿@ConditionalOnClass与OnClassCondition
spring.factories
文件解析工具类:SpringFactoriesLoader 可用于自定义扩展 key都是接口,value都是实现类 在META-INF/spring.factories文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化到spring容器。这种自定义的SPI机制是Spring Boot Starter实现的基础 服务启动时,会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。
常见接口
org.springframework.boot.SpringApplicationRunListener
用于SpringApplication运行方法的监听器
org.springframework.boot.autoconfigure.AutoConfigurationImportListener
加载并过滤完@Configuration后的钩子回调
org.springframework.boot.autoconfigure.EnableAutoConfiguration
作用:使自动配置生效 定义自动装配config类,当系统引入该jar包时, spring上下文将初始化这些config类
org.springframework.boot.env.EnvironmentPostProcessor
SpringBoot支持动态的读取文件,留下的扩展接口 源码: @FunctionalInterface public interface EnvironmentPostProcessor { void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application); }
org.springframework.boot.env.PropertySourceLoader
作用:加载配置文件 SpringBoot 的配置文件内置支持 properties、xml、yml、yaml 几种格式,其中 properties和xml 对应的Loader类为 PropertiesPropertySourceLoader ,yml和yaml 对应的Loader类为 YamlPropertySourceLoader。 观察这2个类可以发现,都实现自接口 PropertySourceLoader 。所以我们要新增支持别的格式的配置文件,就可以通过实现接口 PropertySourceLoader 来实现
org.springframework.boot.diagnostics.FailureAnalyzer
特别注意:该功能只作用于启动过程中 当spring bean的调用方法抛出特定异常时由自定义的特定FailureAnalyzer进行捕获并且进行处理 FailureAnalyzer是在启动时拦截异常并将其转换为人类可读消息的好方法,并包装在内FailureAnalysis 如果由于某种原因你无法处理 exception,return null给另一个 implementation 一个处理 exception 的机会
org.springframework.boot.diagnostics.FailureAnalysisReporter
org.springframework.context.ApplicationContextInitializer
ApplicationContextInitializer接口的作用是可以在ApplicationContext初始化之前,对Spring上下文属性进行修改, 既refresh()前的一个钩子函数
org.springframework.boot.SpringBootExceptionReporter
org.springframework.context.ApplicationListener
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
为 org.springframework.boot.autoconfigure.EnableAutoConfiguration定义的所有配置类增加ImportFilter来决定是否进行配置
org.springframework.data.web.config.SpringDataJacksonModules
SpringFactoriesLoader
作用
解析spring.factories文件
方法
loadFactories
根据接口类获取其实现类的实例,这个方法返回的是对象列表
loadFactoryNames
根据接口获取其接口实现类的名称,这个方法返回的是类名的列表
自动配置
解释
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作
常用注解
@Configuration
@EnableConfigurationProperties
@AutoConfigureBefore
钩子
Aware钩子
这是Spring提供的资源注入接口,实现该类接口,Bean在初始化阶段就会把Spring的相关资源注入到当前bean
作用
把自己知道的东西通过这个钩子告诉我们
ApplicationContextAware
返回ApplicationContext容器
BeanFactoryAware
返回BeanFactory工厂
BeanNameAware
返回当前Bean的名称
EnvironmentAware
返回运行的环境参数
BeanClassLoaderAware
获取加载Bean的ClassLoader
MessageSourceAware
获取 Message Source 相关文本信息
ApplicationEventPublisherAware
发布事件
ResourceLoaderAware
获取资源加载器,这样获取外部资源文件
EmbeddedValueResolverAware(@Value)
配置信息注入
@Component // @PropertySource("classpath:/dbconfig.properties") //指定 properties 文件,不是必须的 public class PropertiesUtil implements EmbeddedValueResolverAware { private StringValueResolver resolver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.resolver = resolver; } /** * 获取属性,直接传入属性名称即可 * @param key * @return */ public String getPropertiesValue(String key) { StringBuilder name = new StringBuilder("${").append(key).append("}"); return resolver.resolveStringValue(name); } } String s = propertiesUtil.getPropertiesValue("db.user");
该注解只能在Bean容器管理下使用
通过接口则可以构造成工具类
ImportAware
@Import接口的作用和Spring的xml配置文件中的标签类似,可以导入另一个注解了@Configuration的配置类,也就是说,如果项目中引用了一些第三方的类库,如我用到的Redisson库,其内部包含很多@Configuration注解的配置类,但是我的项目没有自动扫描他的包,那么就可以用@Import(XXX.class)来导入其配置类使其生效。在Spring4.2以后,@Import还支持导入普通的没有@Configuration注解的类,并将其实例化加入IOC容器。
需要和@Import一起使用
排序
AnnotationAwareOrderComparator
根据@Order排序
ServletContextAware
其他
JVM原生钩子
Runtime.getRuntime().addShutdownHook( )
正常关闭
程序正常退出
使用System.exit()
终端使用Ctrl+C触发的中断
异常关闭
OutofMemory宕机
强制关闭
系统关机
使用Kill pid杀死进程(使用kill -9是不会被调用的)
启动后执行
图片地址https://images2017.cnblogs.com/blog/584866/201801/584866-20180125085800006-1407600300.png
CommandLineRunner
public interface CommandLineRunner { void run(String... args) throws Exception; } CommandLineRunner中执行参数是原始的java启动类main方法的String[] args字符串数组参数
SpringBoot在项目启动后会遍历所有实现CommandLineRunner的实体类并执行run方法,如果需要按照一定的顺序去执行,那么就需要在实体类上使用一个@Order注解,或者实现Order接口
run方法
接收原始String数组入参
特别注意:多个实现类单线程执行
ApplicationRunner
public interface ApplicationRunner { void run(ApplicationArguments args) throws Exception; } ApplicationRunner中的参数经过处理提供一些方法例如: 1 List<String> getOptionValues(String name); 根据名称获取值list,java 启动命令中 --foo=bar --foo=baz,则根据foo参数名返回list["bar", "baz"]
Springboot项目启动后调用该接口的run方法
run方法
接收ApplicationArguments入参
可以接收和解析类似--foo=bar的参数
对比
spring boot提供的2个供用户自己拓展的接口:ApplicationRunner和CommandLineRunner。可以在容器启动完毕后(上下文刷新后)执行,做一些类似数据初始化的操作。 不同之处在于:ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组
使用@Component注解使其成为bean
SpringContextShutdownHook
ServletContextListener
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。 当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理
方法
contextInitialized(ServletContextEvent sce)
当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter 初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化
contextDestroyed(ServletContextEvent sce)
当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器
Spring
其他注意
${}和#{}区别
${key名称},#{表达式} @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量 @Value("${}") 可以获取对应属性文件中定义的属性值 注:在使用的时候也允许 #{'${key}'} 这样使用
启动流程
配置文件
动态赋值
java环境变量、系统环境变量
${key:value}
maven: path: ${M2_HOME:abc}
key:键
value:当key对应的值拿不到时,取value默认值
取maven变量
<profiles> <profile> <id>测试配置</id> <activation> <activeByDefault>true</activeByDefault> <jdk>${java.version}</jdk> </activation> <!-- 自定义属性 --> <properties> <!-- mysql database url --> <driverClassName>com.mysql.jdbc.Driver </driverClassName> <url>jdbc:mysql://localhost:3306/db</url> <username>root</username> <password></password> <defaultZone>http://localhost:8500/eureka/</defaultZone> countURL> </properties> </profile>
spring.profiles.active=@key@
mvn clean package -P test 命令时, @profiles.active@ 会替换成 test
profiles.active
自定义变量
取pom中profiles中定义的profile的自定义属性
在application.properties中用@testName@来搜索pom或者mvn打包时-D指定参数的值来进行替换
配置文件自定义提示
属性注入方式
@Value
@ConfigurationProperties
启动类使用 @EnableConfigurationProperties 开启 @ConfigurationProperties 注解
@ConfigurationProperties
@Data @ConfigurationProperties(SwaggerProperties.PREFIX) public class SwaggerProperties { public static final String PREFIX = "swagger"; /** * 文档扫描包路径 */ private String basePackage = ""; /** * title 如: 用户模块系统接口详情 */ private String title = "平台系统接口详情"; /** * 服务文件介绍 */ private String description = "在线文档"; /** * 服务条款网址 */ private String termsOfServiceUrl = "https://www.test.com/"; /** * 版本 */ private String version = "V1.0"; }
需要配合插件
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
其他
spring.profiles.include
包含其他配置,部分环境
数据类型
数组
pets: - cat - dog - pig
pets: [cat,dog,pig]
map
k: v1: v2:
student: {name: qinjiang,age: 3}
其他
单引号/双引号
双
会转义特殊字符
单
不会转义字符串里面的特殊字符
特殊字符最终会变成和普通字符原样输出
文件来源
工程内部
1. ./config/(项目部署包所在目录的同级config目录下的application-[profile].[properties,yml]文件)
2. ./config/*/(项目部署包所在目录的同级config目录下的任意子目录中的application-[profile].[properties,yml]文件)
3. ./(项目部署包所在目录的application-[profile].[properties,yml]文件)
4. classpath:/config/(项目部署包内类路径下的config目录下的application-[profile].[properties,yml]文件)
5. classpath:/(项目部署包内类路径下的application-[profile].[properties,yml]文件)
profile
优先级
SpringApplication.setAdditionalProfiles
SpringApplication application = new SpringApplication(MyApplication.class); application.setAdditionalProfiles("new_dev");
ConfigurableEnvironment、@ActiveProfile
ConfigurableEnvironment environment = new StandardEnvironment(); environment.setActiveProfiles("dev","join_dev"); application.setEnvironment(environment); application.run(args) @ActiveProfiles("test") public void ApiTest{ ... }
Web.xml的 context-param
WebApplicationInitializer
JVM 启动参数
-Dspring.profiles.active=dev
环境变量
export spring_profiles_active=dev
Maven profile、application.properties
spring.profiles.active=dev
外部引入
spring.config.additional-location=配置文件
该命令用于追加配置文件。原有的application.properties或application.yml文件均有效
spring.config.location=配置文件
该命令指定的配置文件会使项目默认的application.properties或application.yml文件失效,换句话说该命令会用指定的配置文件替换application.properties或application.yml文件。
@PropertySource(value={"classpath:student.properties"})
需要注入的类的前面使用该注解
@ImportResource(locations={"classpath:spring.xml"})
首先添加一个spring的配置文件,在里面添加需要映射的类。在启动的SpringBootApplication前面使用该注解
@Configuration和@Bean方式(SpringBoot推荐方式)
添加一个自定义配置类
注解导入配置
@ConfigurationProperties
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定,一般都是将applicatio.properties或者application.yml里面的K:V配置根据prefix前缀名进行映射
@Validated 注解,在字段上加上@NotEmpty等注解,在注入值的时候会自动校验值的参数,不符合则报错

属性
prefix
ignoreInvalidFields
默认
true
属性配置值错误时,忽略错误
比如属性定义的是String,但是配置了Boolean
ignoreUnknownFields
默认
false
忽略未知属性
当注解标注的类,匹配的前缀中,在配置文件中发现没有用到的配置,提示异常
从全局配置中获取配置信息
优先级
yml>properties>yaml
自动更新
@Refresh
注解
Springboot
原理
基础注解
@SpringBootApplication
@SpringBootConfiguration
@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名
@Configuration
标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名
@EnableAutoConfiguration
作用:从classpath中搜索所有的META-INF/spring.factories配置文件,然后将其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的value加载到spring容器中 该注解又用了@Import注解引入了EnableAutoConfigurationImportSelector类,真正的value加载逻辑在这个类中
@AutoConfigurationPackage
@EnableConfigurationProperties
@AutoConfigureAfter
@AutoConfigureBefore
@EnableConfigurationProperties
作用:使使用 @ConfigurationProperties 注解的类生效 如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入 注意:springboot 2.2.0的时候不需要@Component或者@Configuration配合@ConfigurationProperties,单独的@ConfigurationProperties就可以完成注入,因此该注解基本已经无用
其他常用
@Async
为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync @Async所修饰的函数不要定义为static类型,这样异步调用不会生效 需要使用Future<T>来返回异步调用的结果
该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
@EnableAsync
@ExceptionHandler
@Configuration
把一个类作为一个IoC容器
空提示
@Nullable
被注解对象可以为空
@Nonnull
被注解对象不能为空
仅仅是提示,不影响代码运行
探索
AutoConfigureAfter
@CrossOrigin
自定义启动类注解
@Import
ImportSelector
主要作用是收集需要导入的配置类
用来根据给定的条件,选择导入哪些配置类
AOP-IOC
AOP
术语
通知(Advice)
就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
通知执行顺序
1.在目标方法没有抛出异常的情况下 前置通知 环绕通知的调用目标方法之前的代码 目标方法 环绕通知的调用目标方法之后的代码 后置通知 最终通知 2.在目标方法抛出异常的情况下 前置通知 环绕通知的调用目标方法之前的代码 目标方法 抛出异常 异常通知 最终通知 3.如果存在多个切面 多切面执行时,采用了责任链设计模式。 切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法
前置通知(@Before)
可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。 注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。
目标方法执行之前增强
后置通知(@AfterReturning)
接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。返回值作为第二个参数 方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。
目标方法运行后或返回值后
环绕通知(@Around)
必须显式的调用目标方法,目标方法才会执行。该显式调用时通过ProceedingJoinPoint来实现的,可在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入 注意: 1、这个参数必须处在环绕通知的第一个形参位置。 2、只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。 3、环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。 4、环绕通知虽然有这样的能力,但一定要慎用,不是技术上不可行,而是要小心不要破坏了软件分层的“高内聚 低耦合”的目标。
目标方法执行前后
异常抛出通知(@AfterThrowing)
传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位。Throwable可作为第二个入参
目标代码出现异常时
引介通知(@DeclareParents)
在目标类中添加新的方法和属性
最终通知(@After)
和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。 而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。 另外,后置通知可以通过配置得到返回值,而最终通知无法得到。 最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。
不管目标方法是否发生异常,最终通知都会执行
连接点(JoinPoint)
这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。
切入点(Pointcut)
上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
@Pointcut
定义切入点表达式
切面(Aspect)
切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
@Aspect
定义切面
引入(introduction)
允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗
目标(target)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
代理(proxy)
代理模式,将通知应用到目标对象后,被动态创建的对象。
织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。
实现方式
ProxyFactoryBean
AspectJ 框架
切入点详解
表达式
说明
..代表任意段的任意字符,匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。 注意:该符号有数量的含义,即任意字符且任意数量,与*不同,*只表示任意字符
*代表任意
?代表有或者没有,没有问号就必须要写
+匹配指定类型的子类型;仅能作为后缀放在类型模式后边
execution()
execution(<修饰符> <返回类型> <类路径> <方法名>(<参数列表>) <异常模式> )
说明
除了返回类型模式、方法名模式和参数模式外,其它项都是可选的
参数说明
修饰符
返回类型
类路径
方法名
参数列表
异常模式
模式串说明
bao/类
.*
包下的所有类
..*
包、子孙包下的所有类
“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类
方法入参
*
任意类型参数
限定一个
..
任意类型参数
个数不限
实例
.*(..)
任何方法名,括号表示参数,两个点表示任何参数类型
execution(public * *(..))
找到所有的公有方法
execution(* com.baobaotao.*(..))
匹配com.baobaotao包下所有类的所有方法
不含子包
execution(* com.baobaotao..*(..))
匹配com.baobaotao包、子孙包下所有类的所有方法
execution(* *To(..))
匹配目标类所有以To为后缀的方法
execution(public * com.service...(..))
表示com.service包以及子孙包下
execution(* com.baobaotao.Waiter.*(..))
匹配Waiter接口的所有方法
实现类中接口定义的方法同样生效,但未在接口中定义的不生效
execution(* com.baobaotao.Waiter+.*(..))
匹配Waiter接口及其所有实现类的方法
即使子类中存在接口未定义的方法,同样生效
execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀
execution(* joke(String,int)))
匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int
如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int)
execution(* joke(String,*)))
匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型
execution(* joke(Object+)))
@annotation()
标注了特定注解的目标方法连接点
如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法
入参
方法注解类名
args()
判别目标类方法运行时入参对象的类型定义指定连接点
如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法
入参
类名 允许在类名后使用“+”通配符,但该通配符在此处没有意义,添加和不添加的效果都一样
说明
args 中指定的参数名和aroundAdvice指明的参数名一致
如果args(name,age,…)则表示后面参数不定,但最开始两个参数是name,age,如果是args(*,name,age)则表示name,age是第二,三个参数位置上
@args()
通过判别目标方法的运行时“入参对象的类”是否标注特定注解来指定连接点
如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解
入参
入参必须是注解类的类名,当方法的运行时入参对象标注了指定的注解时,匹配切点
特别注意:是入参的对象类标注对应的注解,不是给参数对象之前
within()
用于匹配指定类型内的方法执行
如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点,也即包中所有类的所有方法,而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,所有以Service结尾的类的所有连接点
入参
类名匹配串
@within()
假如目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有连接点匹配这个切点。
如@within(com.baobaotao.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配
入参
类型注解类名
target()
假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点
如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。
入参
类名
@target()
目标类标注了特定注解,则目标类所有连接点匹配该切点
如@target(com.baobaotao.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点
入参
类型注解类名
this()
代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点
这个函数比较难理解,这里暂不举例,留待后面详解
入参
类名
bean
指定bean进行aop代理,bean("emp")
实例
within( com.service..*) 表示com.service包以及子孙包下的所有类, 写法与execution指示器是不同的,不需要写返回类型
target( com.service.emp.impl.EmployeeServiceImpl) 给EmployeeServiceImpl 进行aop代理,不能使用通配符.表达式中指定父类型或者接口时是可以的.
this(com.service.emp.impl.EmployeeServiceImpl):给EmployeeServiceImpl类 进行aop代理.写法与target类似. 意思是:如果能代理成功,那么生成的代理类是表达式里面设定的类型的实例
bean(emp)表示给emp这个bean进行aop代理
IOC
注入方式
setter 方法注入
这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringDao的set方法(这是ioc的注入入口)
构造器注入
注解注入
@Autowired(默认byType)
自动装配,用于替代基于XML配置的自动装配 基于@Autowired的自动装配,默认是根据类型注入,可以用于构造器、字段、方法注入
构造器注入
通过将@Autowired注解放在构造器上来完成构造器注入,默认构造器参数通过类型自动装配
字段注入
通过将@Autowired注解放在构造器上来完成字段注入
方法参数注入
通过将@Autowired注解放在方法上来完成方法参数注入
@Required
应用于bean属性的setter方法使用该注解的属性必须在配置文件中设置,否则容器会抛出一个BeanlnitializationException异常
@Qualifier
在按照类型注入的基础上,再按照bean的id注入。他在类成员注入数据时,不能独立使用,但是在给方法的形参注入数据时,可以独立使用。属性value用于指定bean的id
@Resource(默认byName)
使用属性name指定bean的id
@Configuration
@Configuration 表示这个类可以使用spring IOC容器作为bean定义的来源
@Bean
@Bean 返回一个对象,该对象被注册为spring应用程序上下文中的bean。当返回的是null时,忽略注入动作
@Import
将指定类注入到IOC,可作用于类,注解等
@Inject
静态工厂方法注入
实例工厂方法注入
Spring MVC
运行流程
web.xml 文件配置 DispatcherServlet
http请求到DispatcherServlet前端控制器
HandlerMapping 处理器映射器
根据 HttpServletRequest 查找 HandlerExecutionChain
根据 HandlerExecutionChain 获取 HandlerAdapter、执行 handler
Handler处理器
业务处理
实现开发中又称为controller即后端控制器
执行完成返回 ModelAndView
ViewReslover视图解析
根据逻辑视图名创建一个View对象
DispatcherServlet
进行视图渲染
View视图(jsp)
将模型数据填充进来(将model数据填充到request域)显示给用户
注解
地址映射
@RequestMapping
value(path)
资源地址
method
指定请求的method类型, GET、POST、PUT、DELETE等;
consumes
指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces
指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
params
指定request中必须包含某些参数值是,才让该方法处理
注意:param=value表示存在该键值对时成立;param表示存在该键时成立;param!=value表示param的值不为value时成立;!param表示不存在该键时成立;等
headers
指定request中必须包含某些指定的header值,才能让该方法处理请求
注意:param=value表示存在该键值对时成立;param表示存在该键时成立;param!=value表示param的值不为value时成立;!param表示不存在该键时成立;等
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
注意
地址支持正则表达式
签名
其他
@Controller
@ResponseBody
表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值通常解析为跳转路径,加上@esponsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。
@RestController
@RestControllerAdvice
@ResponseStatus
@ControllerAdvice
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { /** * 用来指定controller所在的包,满足一个就可以 */ @AliasFor("basePackages") String[] value() default {}; /** * 用来指定controller所在的包,满足一个就可以 */ @AliasFor("value") String[] basePackages() default {}; /** * controller所在的包必须为basePackageClasses中同等级或者子包中,满足一个就可以 */ Class<?>[] basePackageClasses() default {}; /** * 用来指定Controller需要满足的类型,满足assignableTypes中指定的任意一个就可以 */ Class<?>[] assignableTypes() default {}; /** * 用来指定Controller上需要有的注解,满足annotations中指定的任意一个就可以 */ Class<? extends Annotation>[] annotations() default {}; }
参数取值
@RequestParam
@RequestBody
@RequestHeader
@ModelAttribute
@CookieValue
@SessionAttributes
@PathVariable
前端提交类型
表单提交
Content-Type
x-www-form-urlencoded
body
整个内容就是为 以& 分隔的参数列表 key1=value1&key2=value2
JSON提交
Content-Type
application/json
body
HTTP POST请求数据必须是JSON的 {"key1": value1, "key2": value2}
text/html
Content-Type
text/html
body
注意:
Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型
源码
组件
说明
handler通常指我们写的controller
前端控制器/调度控制器(DispatcherServlet)
处理器映射器(HandlerMapping)
作用
充当着url和Controller之间映射关系配置的角色
将请求映射到处理程序,以及预处理和处理后的拦截器列表
根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain 对象中
HandlerMapping 是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler
AbstractHandlerMapping
统一继承于 AbstractHandlerMapping
AbstractHandlerMethodMapping
利用 RequestMappingHandlerMapping 获取的 Handler 是 HandlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HandlerMethod 对象 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
AbstractUrlHandlerMapping
这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求
Springboot默认提供7个实现
SimpleUrlHandlerMapping
该bean在 WebMvcAutoConfiguration 的内部配置类 FaviconConfiguration 中声明。它匹配的URL路径是**/favicon.ico,作用是响应浏览器获取收藏夹中的icon文件
RequestMappingHandlerMapping
该bean在 WebMvcAutoConfiguration 的内部配置类 EnableWebMvcConfiguration 中声明。它和XML的注解驱动一样,匹配在Controller中@RequestMapping注解指定的URL。
WelcomePageHandlerMapping
该bean在 WebMvcAutoConfiguration 的内部配置类 WebMvcAutoConfigurationAdapter 中声明。给URL路径是/或者/index映射到欢迎页面。
BeanNameUrlHandlerMapping
bean的名称(也就是beanID)作为URL,由匹配该URL的Controller处理业务。Controller必须继承AbstractController。
处理器适配器(HandlerAdapter)
拦截器(HandlerInterceptor)
HandlerInterceptorAdapter
语言环境处理器(LocaleResolver)
主题解析器(ThemeResolver)
视图解析器(ViewResolver)
文件上传处理器(MultipartResolver)
异常处理器(HandlerExceptionResolver)
数据转换(DataBinder)
WebDataBinder
https://blog.csdn.net/weixin_34088583/article/details/88037971?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_default&utm_relevant_index=5
web请求的parameters绑定到JavaBean
自定义web绑定
消息转换器(HttpMessageConverter)
https://www.jianshu.com/p/e27671c7a140
将 HttpServletRequest 中的数据, 根据 MediaType 转换成指定格式的数据
参数转换器
HandlerMethodArgumentResolver
https://www.jianshu.com/p/f4653fe8c935 https://blog.csdn.net/songzehao/article/details/99641594
Controller方法自定义注解解析入参
作用
解析request请求参数并绑定数据到Controller的入参上
可修改请求参数
注入参数校验容器
方法
boolean supportsParameter(MethodParameter parameter)
该方法首次调用时判断 true:以后直接调用resolveArgument false:不再调用
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception
从 ModelAndViewContainer(被 @ModelAttribute), NativeWebRequest(其实就是HttpServletRequest) 中获取数据, 解决 方法上的参数
使用
注意
自定义class命名规则:注解名+HandlerMethodArgumentResolver
有大量实现类,建议阅读源码
AbstractCookieValueMethodArgumentResolver
ServletCookieValueMethodArgumentResolver
ExpressionValueMethodArgumentResolver
RequestHeaderMethodArgumentResolver
RequestParamMethodArgumentResolver
MatrixVariableMethodArgumentResolver
PathVariableMethodArgumentResolver
RequestAttributeMethodArgumentResolver
SessionAttributeMethodArgumentResolver
封装类
AbstractNamedValueMethodArgumentResolver(基于Name)
boolean supportsParameter(MethodParameter parameter)
判断当前参数是否需要校验
只做一次判断,后期沿用
NamedValueInfo createNamedValueInfo(MethodParameter parameter)
根据参数创建名称值信息,供后边的方法使用
Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
根据名称值信息+request,解析出值并返回
返回值绑定给Controller方法入参
void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException
如果没有解析到值,需要做什么处理
请求转视图翻译器(RequestToViewNameTranslator)
页面跳转参数管理器(FlashMapManager)
处理程序执行链(HandlerExecutionChain)
handler:请求处理器,通常就是我们自定义的 controller 对象及方法
interceptorList:拦截器,当前请求匹配到的拦截器列表
interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了
WebMvcConfigurationSupport
web服务配置
在springboot项目中,有配置类继承了WebMvcConfigurationSupport,那么webmvc的自动配置类WebMvcAutoConfiguration就会失效
如果想要使用自动配置生效,又要按自己的需要重写某些方法,比如增加拦截器。那么将配置类继承 WebMvcConfigurationSupport改为实现WebMvcConfigurer接口即可
方法
configurePathMatch:配置路由请求规则
configureContentNegotiation:内容协商配置
configureAsyncSupport
configureDefaultServletHandling:默认静态资源处理器
addFormatters:注册自定义转化器
addInterceptors:拦截器配置
addResourceHandlers:资源处理
addCorsMappings:CORS配置
addViewControllers:视图跳转控制器
configureViewResolvers:配置视图解析
addArgumentResolvers:添加自定义方法参数处理器
addReturnValueHandlers:添加自定义返回结果处理器
configureMessageConverters:配置消息转换器。重载会覆盖默认注册的HttpMessageConverter
extendMessageConverters:配置消息转换器。仅添加一个自定义的HttpMessageConverter.
configureHandlerExceptionResolvers:配置异常转换器
extendHandlerExceptionResolvers:添加异常转化器
getValidator
getMessageCodesResolver
HandlerMethodReturnValueHandler
返回值处理器
AbstractHandlerMethodAdapter
自定义参数解析器
参数 即api层的方法入参,不同参数类型会对应不同的解析器
注意对于json请求体,只能从数据流读取一次,也就是说只能被一个解析器读取使用,然后数据流就会关闭,其他参数解析器读取不到
但是请求参数等是存在于request对象中,可以多次读取
继承
AbstractMessageConverterMethodArgumentResolver
public class DynamicFieldsMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver { public DynamicFieldsMethodArgumentResolver(List<HttpMessageConverter<?>> converters) { super(converters); } @Override public boolean supportsParameter(MethodParameter parameter) { boolean has = parameter.hasParameterAnnotation(DynamicFields.class); return DynamicData.class.getName().equals(parameter.getParameterType().getName()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws HttpMediaTypeNotSupportedException, IOException { Object obj = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); return obj; } }
public boolean supportsParameter(MethodParameter parameter)
启动时执行,判断当前解析器适配的参数类型
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
适配到以后,每次请求解析对应的参数
注入
继承WebMvcConfigurationSupport
@Configuration public class DynamicConfig extends WebMvcConfigurationSupport { @Override protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { return new CustomRequestMappingHandlerAdapter(); } @Override protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new DynamicFieldsMethodArgumentResolver(Arrays.asList(new MappingJackson2HttpMessageConverter()))); super.addArgumentResolvers(argumentResolvers); } }
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
添加自定义解析器
其他
RequestBodyAdvice
对@RquestBody 进行增强处理
其他注解不生效
请求被读取(到达Controller)之前,自定义数据流
默认实现类
RequestBodyAdviceAdapter
ResponseBodyAdvice
对@ResponseBody 类型返回值进行增强处理
其他注解不生效
响应从Controller返回之前做数据处理
实现类必须添加@ControllerAdvice
https://blog.csdn.net/likun557/article/details/120620328
HttpServletRequestWrapper
事件/监听
组成
事件实体(ApplicationEvent)
监听器(ApplicationListener)
注解方式:@EventListener
@EventListener public void blogModified(BlogModifiedEvent blogModifiedEvent) { externalNotificationSender.blogModified(blogModifiedEvent); }
接口实现方式
@Component public class HelloEventListener implements ApplicationListener<HelloEvent> { private static final Logger logger = LoggerFactory.getLogger(HelloEventListener.class); @Override public void onApplicationEvent(HelloEvent event) { logger.info("receive {} say hello!",event.getName()); } }
事件发布操作(publishEvent)
容器事件
自定义事件
一般通过扩展ApplicationEvent自定义事件
ApplicationContext发布
常见事件
RequestHandledEvent
ServletRequestHandlerEvent
每次处理一个请求后,都会发一个该事件
ApplicationContextEvent(容器事件)
ContextRefreshedEvent
当spring容器初始化或刷新时,会触发此事件。此事件在开发中常用,用于在spring容器启动时,导入自定义的bean实例到spring容器中。
容器刷新的时候触发(onRefresh,在finisRefresh中调用)
ContextClosedEvent
当spring关闭时,或者说context调用close()方法时,会触发此事件
容器关闭的时候触发(close方法)
ContextStartedEvent
当spring启动时,或者说是context调用start()方法时,会触发此事件
容器启动的时候触发(start方法)
ContextStoppedEvent
当spring停止时,或者说context调用stop()方法时,会触发此事件
容器停止的时候触发(stop方法)
注意
同一个事件可以被多个监听器监听到
其他
ServletContextListener
Servlet API能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期
Bean
未知
核心方法
invokeBeanFactoryPostProcessors
registerBeanPostProcessors
finishBeanFactoryInitialization
特殊接口/类
PropertySource
用于存放Spring配置资源信息,例如spring项目中properties或者yaml文件配置信息均会保存在PropertySource对象中。Spring支持使用@PropertySource注解,將配置信息加载到Environment对象中
EnvironmentPostProcessor
BeanFactoryPostProcessor
子类
BeanDefinitionRegistryPostProcessor
是一个特殊的BeanFactoryPostProcessor,用于在BeanDefinition对象注册完成后,访问、新增或者修改BeanDefinition信息
PropertySourcesPlaceholderConfigurer
一个特殊的BeanFactoryPostProcessor,用于解析Bean配置中的${…}参数占位符
作用
将占位符指向的数据库配置信息放在bean中定义的工具
如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找
作用
在spring容器加载了bean的定义文件(BeanDefinition对象)之后,在bean实例化之前执行的
可以修改BeanDefinition对象几乎任何信息
作用范围
全局
方法
void postProcessBeanFactory(ConfigurableListableBeanFactory)
用法
实现接口
全局拦截
实例
Bean初始化之前修改bean的属性值
beanFactory.getBeanDefinition(beanName).getPropertyValues().addPropertyValue( propertyName,value )
ImportBeanDefinitionRegistrar
博客
https://zhuanlan.zhihu.com/p/91461558
https://www.jianshu.com/p/d5ffdccc4f5d
方法
void registerBeanDefinitions
参数
AnnotationMetadata
与@Import配合后,该注解所在的类注解元信息
BeanDefinitionRegistry
BeanDefinition容器
BeanNameGenerator
Bean名称生成器
作用
主要用来动态注册beanDefinition
很多三方框架集成Spring 的时候,都会通过该接口,实现扫描指定的类,然后注册到spring 容器中
@Component、@Service等注解就是通过该方式实现注入
个人理解
类似于Aware,对外暴露接口,实现该接口,就会通过接口方法把BeanDefinition容器和类注解元数据传进来,供扩展使用
使用步骤
1. 实现ImportBeanDefinitionRegistrar接口
2. 通过registerBeanDefinitions实现具体的类初始化
3. 在@Configuration注解的配置类上使用@Import导入实现类
4. 注意
亲测,只能通过@Import注解加入Bean才能生效
注意
该接口中动态注册的bean是优先于依赖其的bean初始化
ImportBeanDefinitionRegistrar接口不是直接注册Bean到IOC容器,它的执行时机比较早,准确的说更像是注册Bean的定义信息以便后面的Bean的创建
使用场景
自定义Bean注入
与ClassPathBeanDefinitionScanner配合,实现自定义注解Bean加入容器
BeanPostProcessor
作用域
作用域全局,不是某个Bean
使用方式
自定义类BeanPostProcessor
添加@Component注解加入到Spring容器即可
使用示例
1
https://blog.csdn.net/pandalovey/article/details/104028043
ConfigurableListableBeanFactory
生命周期
1. 准备实例化
beanDefinition
解释
收集bean的相关信息
再通过反射实例化
信息分类
constructorArgumentValues
构造函数参数列表
beanClass
类名
primary
是否是单例类
lazyInit
scope
factoryBeanName
工厂名称
factoryMethodName
工厂方法名称
initMethodName
初始化方法
destroyMethodName
销毁方法
abstractFlag
dependsOn
依赖关系
如果依赖其他类则先实例化其他
autowireMode
注入模式
autowireCandidate
注入条件
qualifiers
propertyValues
属性
Synthetic
继承
BeanMetadataElement
bean元数据,返回该bean的来源
AttributeAccessor
属性访问器,对Bean的属性进行操作的API,例如设置属性、获取属性、判断是否存在该属性,返回bean所有的属性名称
子类/接口
AnnotatedBeanDefinition
ChildBeanDefinition
RootBeanDefinition
GenericBeanDefinition
容器
BeanDefinitionRegistry
BeanDefinition容器,所有的Bean的BeanDefinition都注册在BeanDefinitionRegistry实现类的对象中
内置实现类
SimpleBeanDefinitionRegistry
GenericApplicationContext
底层调用的是 DefaultListableBeanFactory 中的实现方法,所以严格意义上来说,只有两个实现类
DefaultListableBeanFactory
方法
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
注册BeanDefinition
其他BeanDefinition的查询、删除、修改等方法
获取BeanDefinition的流程
ClassPath
ClassPathBeanDefinitionScanner(类扫描器)
https://www.jianshu.com/p/d5ffdccc4f5d
作用
对指定路径下的 所有class 文件进行逐一排查,对符合条件的 class ,封装成 BeanDefinition注册到IOC 容器
个人理解
可以当做工具包使用,指定某个包,并定义一些规则去扫描出符合的BeanDefinition
注意接口方法的入参registry,这是BeanDefinition的容器,扫描以后会将结果自动加入到容器当中
IOC 容器初始化阶段(调用beanFactoryPostProcessor阶段) 就会采用ClassPathBeanDefinitionScanner进行扫描包下 所有类,并将符合过滤条件的类注册到IOC 容器内
根据指定包扫描
2. BeanFactoryPostProcessor
3. 实例化
4. 设置对象属性(依赖注入)
5. 处理Aware接口
6. BeanPostProcessor接口
Object postProcessBeforeInitialization(Object bean, String beanName)
@PostConstruct注解
实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务
7. InitializingBean接口
等同@Bean的init-method属性
同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法
等同@PostConstruct
与@PostConstruct同时使用,后调用@PostConstruct
8. BeanPostProcessor接口
Object postProcessAfterInitialization(Object bean, String beanName)
实例化、依赖注入、初始化完毕时执行
9. DisposableBean
bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法
等同@Bean的destroy-method属性
等同@PreDestroy
与@PreDestroy同时使用,后调用@PreDestroy
特别注意
多例bean(@Scope("prototype"))的生命周期不归Spring容器来管理,这里的DisposableBean中的方法是由Spring容器来调用的,所以如果一个多例实现了DisposableBean是没有啥意义的,因为相应的方法根本不会被调用
作用域@Scope
singleton(默认)
在SpringIOC容器中仅存在一个Bean实例,bean以单例的方式存在
prototype(多例)
Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用 对于prototype作用域的bean,Spring只负责创建,当容器创建了bean的实例后,bean的实例就交给了客户端的代码管理,Spring容器将不再跟踪其生命周期,并且不会管理那些被配置成prototype作用域的bean的生命周期
每次从spring容器内获取,都会产生一个新的bean,再注入
当容器关闭时,destroy方法不会被调用
特别注意
多例bean生命周期不归Spring容器来管理
session
同一个HTTP Session共享一个Bean,不同的HTTP Sessionn使用不同的Beana.该作用域仅适用于WebApplicationContext环境
request
每次http请求都会创建一个新的bean,该作用域仅适用于WebpplicationContext环境
global session
在一个全局的HTTP Session中,一个bean定义对应一个实例
概要
仅作用于Web应用
application
websocket
是否加入Spring容器
基于XML的配置
基于注解的配置
annotation自动注册bean
<!-- 使用Annotation自动注册Bean,解决事物失效问题:在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解。 --> <context:component-scan base-package="com.qb.keco"><!-- base-package 如果多个,用“,”分隔 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 使用Annotation自动注册Bean,只扫描@Controller --> <context:component-scan base-package="com.qb.keco" use-default-filters="false"><!-- base-package 如果多个,用“,”分隔 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
添加注解
@Component
可以用于注册所有bean
@Repository
主要用于注册dao层的bean
@Service
主要用于注册服务层的bean
@Controller
主要用于注册控制层的bean
装配类型
no
默认方式,手动装配方式,需要通过ref设定bean的依赖关系
byName
根据bean的名字进行装配,当一个bean的名称和其他bean的属性一致,则自动装配
byType
根据bean的类型进行装配
当一个bean的属性类型与其他bean的属性的数据类型一致,则自动装配
constructor
根据构造器进行装配
如果bean的构造器有与其他bean类型相同的属性,则进行自动装配
autodetect
如果有默认构造器,则以constructor方式进行装配,否则以byType方式进行装配
调用/注入
@Autowired
在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean: 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 如果查询的结果不止一个,那么@Autowired会根据名称来查找; 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
按照类去匹配
@Qualifier
指定按照名称去装配 bean
基于Java类配置
@Configuration
@Bean
autowireCandidate
条件化构建
基于注解
condition的使用核心解决了根据不同的条件动态解析或者创建某些bean操作的方式 Spring 自动扫描的一切类 (@Configuration, @Component, @Service, @Repository, or @Controller) 都可以通过添加相应的 @ConditionalOnXxxx 来判断是否加载 都可以应用在 METHOD 上,所以有 @Bean 标记的方法也可以应用这些注解
@Profile
profile中的名称必须为环境中可以接受的profile列表内容
实例
@Component @Profile("dev") public class DevDatasourceConfig
只会在dev模式下被启用
@Component @Profile("!dev") public class DevDatasourceConfig
非dev环境启用
@ConditionalOnBean
在当前上下文中存在某个对象时,才会实例化一个Bean
@ConditionalOnClass
某个class位于类路径上,才会实例化一个Bean
@ConditionalOnExpression
当表达式为true的时候,才会实例化一个Bean
@ConditionalOnMissingBean
在当前上下文中不存在某个对象时,才会实例化一个Bean
@ConditionalOnMissingClass
某个class类路径上不存在的时候,才会实例化一个Bean
@ConditionalOnNotWebApplication
不是web应用
@ConditionalOnProperty
在application.yml里配置的属性是否为true
属性
value
数组,获取对应property名称的值,与name不可同时使用
prefix
配置属性名称的前缀,比如spring.http.encoding
name
数组,配置属性完整名称或部分名称
可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
havingValue
可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
matchIfMissing
缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效
如果未进行属性配置,则自动配置不生效。如果matchIfMissing为true,则表示如果没有对应的属性配置,则自动配置默认生效
@ConditionalOnResource
当类路径下有指定的资源时触发实例化
@ConditionalOnJava
当JVM版本为指定的版本范围时触发实例化
@ConditionalOnSingleCandidate
当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化
@ConditionalOnWebApplication
当项目是一个Web项目时进行实例化
基于spring-autoconfigure-metadata.properties
实例化
构造器实例化
静态工厂方式实例化
实例工厂方式实例化
注入装配
@Autowired
@Resource
@Resource注解由J2EE提供,需要导入包javax.annotation.Resource 使用@Resource可以减少代码和Spring之间的耦合
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
概要
@Primary
自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@PostConstruct
@PreDestory
@Qualifier
@DependsOn
定义Bean初始化及销毁时的顺序
@Import
将指定bean进行实例化,并加入到IOC容器进行管理
作用点
元注解
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy
@Configuration标注的配置类
实现ImportSelector接口的类
实现ImportBeanDefinitionRegistrar接口的类
普通的@component类
特例
与ImportSelector接口配合使用
与ImportAware接口配合使用
@ComponentScan
spring里有四大注解:@Service,@Repository,@Component,@Controller用来定义一个bean.@ComponentScan注解就是用来自动扫描被这些注解标识的类,最终生成ioc容器里的bean
@Value
实例
注入普通字符
@Value("I love you !")//注入普通字符 private String normal;
@Value("I love you !")
注入操作系统属性
@Value("#{systemProperties['os.name']}") //注入操作系统属性 private String osName;
@Value("#{systemProperties['os.name']}")
注入表达式结果
@Value("#{ T(java.lang.Math).random() * 100.0 }") // 注入表达式结果 private double randomNumber;
@Value("#{ T(java.lang.Math).random() * 100.0 }")
注入其他bean属性
@Value("#{ demoService.another}")//注入其他bean属性 private String fromAnother;
@Value("#{ demoService.another}")
注入文件资源
@Value("classpath:com/lyz/highlight_spring4/ch2/el/test.txt")//注入文件资源 private Resource testFile;
@Value("classpath:com/lyz/highlight_spring4/ch2/el/test.txt")
注入网址资源
@Value("http://www.baidu.com")//注入网址资源 private Resource testUrl;
@Value("http://www.baidu.com")
注入配置文件
@Value("${book.name}")//注入配置文件 private String bookName;
@Value("${book.name}")
注入ID为test的Bean
@Value("#{test}")
静态属性注入
public static String port; @Value("${spring.port:8080}") public void setPort(String port){ Class.port = port; }
注意
静态修饰的属性方法注入异常
@Lazy
Spring容器在启动时会创建所有的Bean对象,使用@Lazy注解可以将Bean对象的创建延迟到第一次使用Bean的时候
Bean名称生成
默认
取类名,首字母小写
原理
BeanNameGenerator
AnnotationBeanNameGenerator
为那些用注解方式的bean生成名字
DefaultBeanNameGenerator
自定义生成策略源
第一步
实现BeanNameGenerator接口
第二步
注入
main
@SpringBootApplication public class QuartzBootStrap { public static void main(String[] args) { SpringApplication application = new SpringApplication(QuartzBootStrap.class); //MySelfBeanNameGenerator是自定义bean name生成策略 application.setBeanNameGenerator(new MySelfBeanNameGenerator()); application.run(args); } }
@ComponentScan
@Configuration @ComponentScan(basePackages = "com.springboot.beanname.bean.bean1",nameGenerator = MyNameGenerator.class) public class AppConfig { }
其他
Spring的Bean容器
本质上是SpringFactory中的一个map
key为bean名称
value是bean实例化对象
默认工厂为DefaultListableBeanFactory
注册bean的方法
DefaultListableBeanFactory
registerBeanDefinition方法
基于注解实现自动导入
ImportSelector
主要作用是收集需要导入的配置类 原理:常与@Import注解配合使用,@Import注解会将指定的类实例化,并加入到IOC容器管理。如果指定的是ImportSelector接口的实现类,那么该接口的方法返回的字符串数组中的每一个元素都会被当做实现类全类名,然后进行实例化,并加入到IOC容器管理 如果该接口的实现类同时实现EnvironmentAware, BeanFactoryAware ,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完在导入时可以实现DeferredImportSelector接口。 public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); }
用来根据给定的条件,选择导入哪些配置类
子类
AdviceModeImportSelector
方法
String[] selectImports(AnnotationMetadata importingClassMetadata)
收集需要导入的配置类
接口被调用位置
ConfigurationClassParser这个类的processImports方法
@Import
步骤
实现ImportSelector接口
自定义Enable注解,通过@Import导入接口实现类
启动类添加Enable注解
Spel
操作符
关系操作符
eq(==), ne(!=), lt()<, le(<=), gt(>), ge(>=)
逻辑操作符
and(&&), or(||), not(!)
数学操作符
加(+), 减(-), 乘(*), 除(/), 取模(%), 幂指数(^)
解析
//测试SpringEL解析器 String template = "#{#user},早上好";//设置文字模板,其中#{}表示表达式的起止,#user是表达式字符串,表示引用一个变量。 ExpressionParser paser = new SpelExpressionParser();//创建表达式解析器 //通过evaluationContext.setVariable可以在上下文中设定变量。 EvaluationContext context = new StandardEvaluationContext(); context.setVariable("user","黎明"); //解析表达式,如果表达式是一个模板表达式,需要为解析传入模板解析器上下文。 Expression expression = paser.parseExpression(template,new TemplateParserContext()); //使用Expression.getValue()获取表达式的值,这里传入了Evalution上下文,第二个参数是类型参数,表示返回值的类型。 System.out.println(expression.getValue(context,String.class));
数据操作
文本表达式
parser.parseExpression("'hello'").getValue(String.class); // hello , 注意单引号 parser.parseExpression("1.024E+3").getValue(Long.class); // 1024 , 指数形式 parser.parseExpression("0xFFFF").getValue(Integer.class); // 65535 , 十六进制 parser.parseExpression("true").getValue(Boolean.class); // true parser.parseExpression("null").getValue();
字符串(需要用单引号声明)
日期
null
数字
小数
负数
指数
布尔
变量
#this变量
有个特殊的变量#this来表示当前的对象. 常用于集合的过滤
属性和方法调用
属性可直接使用属性名,属性名首字母大小写均可(只有首字母可不区分大小写)
数组、列表可直接通过下表形式(list[index])访问
map可以直接把key当成索引来访问(map[key])
方法可以直接访问
类型
T操作符可以获取类型, 可以调用对象的静态方法
注意事项
避免空指针
当访问一个对象的属性或方法时, 若该对象为null, 就会出现空指针异常. 安全导航会判断对象是否为null,如果是的话, 就返回null而不是抛出空指针异常. 使用方式就是在对象后面加个?
集合选择
可以使用选择表达式对集合进行过滤或一些操作,从而生成一个新的符合选择条件的集合 // 集合 parser.parseExpression("{1, 3, 5, 7}.?[#this > 3]").getValue(); // [5, 7] , 选择元素 parser.parseExpression("{1, 3, 5, 7}.^[#this > 3]").getValue(); // 5 , 第一个 parser.parseExpression("{1, 3, 5, 7}.$[#this > 3]").getValue(); // 7 , 最后一个 parser.parseExpression("{1, 3, 5, 7}.![#this + 1]").getValue(); // [2, 4, 6, 8] ,每个元素都加1 // map Map<Integer, String> map = Maps.newHashMap(); map.put(1, "A"); map.put(2, "B"); map.put(3, "C"); map.put(4, "D"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("map", map); parser.parseExpression("#map.?[key > 3]").getValue(context); // {4=D} parser.parseExpression("#map.?[value == 'A']").getValue(context); // {1=A} parser.parseExpression("#map.?[key > 2 and key < 4]").getValue(context); // {3=C}
?[expression]: 选择符合条件的元素
^[expression]: 选择符合条件的第一个元素
$[expression]: 选择符合条件的最后一个元素
![expression]: 可对集合中的元素挨个进行处理
模板表达式
模板表达式允许文字和表达式混合使用, 一般选择使用#{}作为一个定界符: parser.parseExpression("他的名字为#{#person.name}", new TemplateParserContext()).getValue(context); // 他的名字为Tom
本地事务
实现方式
基于XML
编程式事务管理
对基于 POJO 的应用来说是唯一选择
POJO(Plain Ordinary Java Object)意思是普通的java对象 ,没有继承任何类、实现任何接口、也没有包含特殊的注解,。 JavaBeans是一类POJO,具有无参构造函数、私有成员变量通过公共的访问器和设置器进行读写、可以序列化。 使用POJO的好处就是和使用的框架解耦,Spring框架就是采用基于POJO开发,Spring开发中不需要自己的业务类继承或实现任何框架的类或接口,通过依赖注入的方式来实现组件之间装配。 但是我们在实际编码中还是可以生成继承或者实现某些接口的实例(Bean),所以在Spring中Bean应该是具备以下特点: 1.无参构造函数便于注入 2.getter和setter函数便于属性的设置 3.可以是实现某个接口的类,但是这些类不得依赖Spring框架,否则Spring框架代码改变,业务代码也有可能需要跟着改变。
需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法
基于 TransactionProxyFactoryBean的声明式事务管理
此方式,可手动开启、提交、回滚事务
基于Aspectj AOP配置事务
事务基础
概要
传播行为
传播行为分为两种:分为支持事物的传播和不支持事物的传播
PROPAGATION_REQUIRED
支持事物)如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
PROPAGATION_SUPPORTS
支持事物)支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
PROPAGATION_MANDATORY
(支持事物)支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
PROPAGATION_REQUIRES_NEW
(支持事物)创建新事务,无论当前存不存在事务,都创建新事务。
PROPAGATION_NOT_SUPPORTED
(不支持事物)以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER
(不支持事物)以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
(不支持事物)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
注解(@Transactional)
value/transactionManager
指定事务管理器,默认为""
propagation
事务的传播行为,默认值为 Propagation.REQUIRED
Propagation.REQUIRED/NESTED
如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
Propagation.MANDATORY
如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
Propagation.REQUIRES_NEW
重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
Propagation.NOT_SUPPORTED
以非事务的方式运行,如果当前存在事务,暂停当前的事务。
Propagation.NEVER
以非事务的方式运行,如果当前存在事务,则抛出异常。
isolation
事务的隔离级别,默认值为 Isolation.DEFAULT
Isolation.DEFAULT
使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
timeout
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。