Java框架篇2-SpringMVC、Spring注解
1.SpringMVC执行流程
我把整个流程分成三个阶段
- 初始化阶段
- 匹配阶段
- 执行阶段
1.1SpringMVC-Step1-初始化阶段
- 在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法
- init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法
- refresh 过程中会创建并初始化 SpringMVC 中的重要组件, 例如
- MultipartResolver(文件上传时,处理multipart格式的表单数据)
- HandlerMapping(请求映射,浏览器的请求映射到spring中控制器的方法)
- HandlerAdapter(调用控制器的方法,处理请求)
- HandlerExceptionResolver(处理调用请求异常)
- ViewResolver(把字符串解析成视图对象)
- 容器初始化后,会将上一步初始化好的重要组件,赋值给DispatcherServlet 的成员变量,留待后用
1.2SpringMVC-Step2-匹配阶段
-
用户发送的请求统一到达前端控制器 DispatcherServlet
-
DispatcherServlet 遍历所有 HandlerMapping ,找到与路径匹配的处理器
① HandlerMapping 有多个,每个 HandlerMapping 会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别 @RequestMapping 的优先级最高
② 对应 @RequestMapping 的处理器是 HandlerMethod,它包含了控制器对象和控制器方法信息
③ 其中路径与处理器的映射关系在 HandlerMapping 初始化时就会建立好
-
将 HandlerMethod 连同匹配到的拦截器,生成调用链对象HandlerExecutionChain 返回
-
遍历HandlerAdapter 处理器适配器,找到能处理 HandlerMethod 的适配器对象,开始调用
1.3SpringMVC-Step3-执行阶段
-
执行拦截器 preHandle
-
由 HandlerAdapter 调用 HandlerMethod
① 调用前处理不同类型的参数
② 调用后处理不同类型的返回值
-
第 2 步没有异常
① 返回 ModelAndView
② 执行拦截器 postHandle 方法
③ 解析视图,得到 View 对象,进行视图渲染
-
第 2 步有异常,进入 HandlerExceptionResolver 异常处理流程
-
最后都会执行拦截器的 afterCompletion 方法
-
如果控制器方法标注了 @ResponseBody 注解,则在第 2 步,就会生成 json 结果,并标记 ModelAndView 已处理,这样就不会执行第 3 步的视图渲染
2.Spring+SpringMVC+SpringBoot注解
- Spring60个注解
- web,SpringMVC32个
- SpringBoot40个
2.1Spring-事务相关注解
- @EnableTransactionManagement,会额外加载 4 个 bean
- BeanFactoryTransactionAttributeSourceAdvisor 事务切面类
- TransactionAttributeSource 用来解析事务属性
- TransactionInterceptor 事务拦截器
- TransactionalEventListenerFactory 事务监听器工厂
- @Transactional
加载public方法/类上
2.2Spring-核心相关注解
- @Order :控制bean执行顺序
2.3Spring-切面相关注解
- @EnableAspectJAutoProxy
- 会加载 AnnotationAwareAspectJAutoProxyCreator,它是一个 bean 后处理器,用来创建代理
- 如果没有配置 @EnableAspectJAutoProxy,又需要用到代理(如事务)则会使用 InfrastructureAdvisorAutoProxyCreator 这个 bean 后处理器
2.4Spring-组件扫描与配置类相关注解
组件扫描
-
@Component
-
@Controller
-
@Service
-
@Repository
-
@ComponentScan
配置类
-
@Conditional
-
@Configuration
- 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法
- @Bean(方法) 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
- @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
- @Configuration 中如果含有 BeanFactory 后处理器, 则实例工厂方法会导致 MyConfig 提前创建, 造成其依赖注入失败,解决方法是改用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 Mapper 扫描可以改用注解方式
-
@Bean
-
@Import
-
四种用法
① 引入单个 bean
@Configuration @Import(Bean1.class) // 1. 引入单个 bean static class Bean1 { }
② 引入一个配置类
@Import(OtherConfig.class) // 2. 引入一个配置类 @Configuration static class OtherConfig { @Bean public Bean2 bean2() { return new Bean2(); } }
③ 通过 Selector 引入多个类
@Import(MySelector.class) // 3. 通过 Selector 引入多个类 static class MySelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{Bean3.class.getName(), Bean4.class.getName()}; } } static class Bean3 { } static class Bean4 { }
④ 通过 beanDefinition 注册器
@Import(MyRegistrar.class) // 4. 通过 beanDefinition 注册器 static class MyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registry.registerBeanDefinition("bean5", BeanDefinitionBuilder.genericBeanDefinition(Bean5.class).getBeanDefinition()); } } static class Bean5 { }
-
解析规则
-
同一配置类中, @Import 先解析 @Bean 后解析
-
同名定义, 默认后面解析的会覆盖前面解析的
-
不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先?(虽然覆盖方式能解决)
-
采用 DeferredImportSelector,因为它最后工作, 可以简单认为先解析 @Bean, 再 Import
public class TestDeferredImport { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); beanFactory.setAllowBeanDefinitionOverriding(false); // 不允许同名定义覆盖 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); context.registerBean(MyConfig.class); context.refresh(); System.out.println(context.getBean(MyBean.class)); } // 1. 同一配置类中, @Import 先解析 @Bean 后解析 // 2. 同名定义, 默认后面解析的会覆盖前面解析的 // 3. 不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先? (虽然覆盖方式能解决) // 4. DeferredImportSelector 最后工作, 可以简单认为先解析 @Bean, 再 Import @Configuration @Import(MySelector.class) static class MyConfig { // 主配置 - 程序员编写的 @Bean public MyBean myBean() { return new Bean1(); } } static class MySelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{OtherConfig.class.getName()}; } } @Configuration static class OtherConfig { // 从属配置 - 自动配置 @Bean @ConditionalOnMissingBean public MyBean myBean() { return new Bean2(); } } interface MyBean { } static class Bean1 implements MyBean { } static class Bean2 implements MyBean { } }
-
-
-
-
@Lazy
- 加在类上,表示此类延迟实例化、初始化
- 加在方法参数上,此参数会以代理方式注入
-
@PropertySource
2.5Spring-依赖注入
- @Autowired
- @Qualifier
- @Value
2.6SpringMVC-mapping
- @RequestMapping,可以派生多个注解如 @GetMapping 等
2.7SpringMVC-rest
- @RequestBody
处理请求体中的json数据,把json数据转换为Java对象 - @ResponseBody
把Java对象转换为json数据,写入到响应体 - @ResponseStatus
- @RestController :组合了@Controller与@ResponseBody
2.8SpringMVC-统一处理
- @ControllerAdvice
- @ExceptionHandler
处理异常 - @RestControllerAdvice:组合了@ResponseBody与@ControllerAdvice
2.9SpringMVC-参数
- @PathVariable
获取请求路径中的参数值
2.10SpringMVC-ajax
- @CrossOrigin
2.11SpringBoot-auto
- @SpringBootApplication
- @EnableAutoConfiguration
- @SpringBootConfiguration
2.12SpringBoot-condition
- @ConditionalOnClass,classpath 下存在某个 class 时,条件才成立
- @ConditionalOnMissingBean,beanFactory 内不存在某个 bean 时,条件才成立
- @ConditionalOnProperty,配置文件中存在某个 property(键、值)时,条件才成立
2.13SpringBoot-boot properties
- @ConfigurationProperties,会将当前 bean 的属性与配置文件(.properties)中的键值进行绑定
- @EnableConfigurationProperties,会添加两个较为重要的 bean
- ConfigurationPropertiesBindingPostProcessor,bean 后处理器,在 bean 初始化前调用下面的 binder
- ConfigurationPropertiesBinder,真正执行绑定操作