SpringBoot 进阶学习

文章目录


走向自动装配

Spring模式注解

模式注解举例

  • @Responsity

  • @Service

  • @Component

装配方式 (@ComponentScan方式装配bean)

<context:component-scan basePackages="…"/> 之前使用扫描注解的xml方式 , 现在可以使用@ComponentScan注解扫描,比xml简单多了

1.basePackages引入单个包路径可以不用写数组,多个需要使用数组:{}

2.包路径是指需要指向你标注了注解的实体类

  • @Component “派生性”
  • @Component “层次性”
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repository 
public @interface FirstLevelResponsity {
    String value() default "";
}

//@FirstLevelResponsity 此注解为@Repository派生而来
@FirstLevelResponsity(value = "secondLevelResponsity")
public class SecondLevelResponsity {
    public String getName(String name){
        System.out.println("--------->getName name is:"+name);
        return name;
    }
}
//@componentScan注解扫描装配bean
@ComponentScan(basePackages = {"com.example.demo.annotiation", "com.example.demo.three"})
public class ResponsityBootstrap {

    public static void main(String[] args) {
      	//SpringApplicatinBuilder可以指定是什么项目类型(web,非web,react)
        ConfigurableApplicationContext context = new SpringApplicationBuilder(ResponsityBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        SecondLevelResponsity bean = context.getBean("secondLevelResponsity", SecondLevelResponsity.class);

        System.out.println(bean.getName("gwm"));
	    //习惯关闭上下文资源
        context.close();
    }
}

Spring @Enable模块装配

Spring Framework3.1 开始支持@Enable模块驱动 所谓模块驱动就相同领域功能的组合成一个独立的单元模块

比如: WebMvc模块,Aspetj代理模块, Caching模块, JMX(Java 管理扩展模块), Async(异步处理模块)等

@Enable 注解模块举例

框架实现@Enable 注解模块激活模块
Spring Framework@EnableWebMveWeb MVC模块
@EnableTransactionManagement事务管理模块
@EnableCachingCaching模块
@EnableMBeanExportJMX模块
@EnableAsync异步处理模块
@EnableWebFluxWeb Flux模块
@EnableAspectJAutoProxyAspectJ 代理模块
Spring Boot@EnableAutoConfiguration自动装配模块
@EnableManagementContextActuator管理模块
@EnableConfigurationProperties配置属性绑定模块
@EnableOAuth2SsoOAuth2 单点登录 模块
Spring Cloud@EnableEurekaServerEureka服务器模块
@EnableConfigServer配置服务器模块
@EnableFeignClientsFeign客户端模块
@EnableZuulProxy服务网关Zuul模块
@EnableCircuitBreaker服务熔断模块

@Enable模式注解的实现方式

注解驱动方式

只有一种实现返回bean的方式, 下面代码就只返回DelegatingWebMvcConfiguration配置类中的Bean

@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
  //...
}

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
  @Bean
  public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    //...
}
接口编程方式

需要自己实现某个特定 接口,进行一定的编码返回bean

@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
	//....
}

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
  public String[] selectImports(AdviceMode adviceMode) {...}
  //...
}

自定义@Enable 模块

基于注解驱动实现 @EnableHelloworld 直接配置XXXConfiguration类
//代码2.1
//先定义两个Configuration类
@Configuration
public class HelloDogConfiguration {
    @Bean public String dog() { return "hot dog!!!";}
}

@Configuration
public class HelloWorldConfiguration {
    @Bean public String helloworld() {return "gwm";}
    @Bean public String hi() {return "hihihihi";}
}

//这里的Import引入配置类非常的局限性,每次只能装载一个配置类
//还不能选择需要装配的Bean
@Import(HelloDogConfiguration.class) 
public @interface EnableHelloworld {
}

这里启动类使用 @EnableHelloworld 注解替换掉原始的模式注解@ComponentScan , 发现一样可以装载Bean, 之前使用@Companscan 注解手动指定包路径扫描注解装载Bean方式,麻烦的一批, 但是这个

//代码2.2
//@ComponentScan(basePackages = "com.example.demo.bootstrap")
@EnableHelloworld
public class ResponsityBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(ResponsityBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        log.info("hellow "+context.getBean("helloworld", String.class));
        log.info("hi "+context.getBean("hi", String.class));
        log.info("hellow "+context.getBean("dog", String.class));

        context.close();
    }
}

基于接口编程实现 @EnableHelloworld (实现ImportSelector 接口)

代码2.1共用两个配置类HelloDogConfiguration ,HelloWorldConfiguration 不同地方在于@Import 注解 ,导入 的类并不是写死的,是可以选择的, 相当于实现一个模板

  1. 第一步实现ImportSelector 接口, 实现方法selectImports
  2. ``selectImports方法 可以实现过滤需不需要装载的Bean
@Documented
//@Import(HelloDogConfiguration.class)
@Import(HelloworldConfigurationSelector.class) //这里导入一个Selector类
public @interface EnableHelloworld {
}

HelloworldConfigurationSelector类的实现如下: 实现了接口 ImportSelector

public class HelloworldConfigurationSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //此处可以根据annotationMetadata参数进行一定筛选装载的Bean
        return new String[]{HelloWorldConfiguration.class.getName(), HelloDogConfiguration.class.getName()};
    }
}

//加上上述代码 ResponsityBootstrap 类的实现

总结: 基于接口驱动实现过程:

HelloworldConfigurationSelector-->HelloWorldConfiguration-->hellworld

Spring条件装配

从Spring Framework3.1 开始 允许在Bean装配前的条件判断,使用@Profile,@conditional两种方式

条件注解举例

Spring注解场景说明起始版本
@Profile配置化条件装配3.1
@Conditional编程条件装配4.0

实现方式

注解配置方式 - @Profile
@Profile(“java7”)
@Profile(“java8”)

我这里准备实现 一个 求和的接口, 分别有两种不同的方式实现, java7老版本求和,java8新版本求和, 在@Service 类上面指定Profile标识

public interface CacalService {
    Integer cal(Integer... a);
}
@Service
@Profile("java7")
public class CalJava7ServiceImpl implements CacalService{
    @Override
    public Integer cal(Integer... a) {
        System.out.println(this);
        return Stream.of(a).reduce(0, Integer::sum);
    }
}
@Service
@Profile("java8")
public class CalJava8ServiceImpl implements CacalService{
    @Override
    public Integer cal(Integer... a) {
        System.out.println(this);
        return Stream.of(a).reduce(0, Integer::sum);
    }
}

启动类使用SpringApplication加载(省事), SpringApplicationBuilder 类有个 方法可以指定Profile的标识

@SpringBootApplication(scanBasePackages = {"com.example.demo.bootstrap.service"})
//@ComponentScan(basePackages = {"com.example.demo.bootstrap.service"})
public class CalBootStrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CalBootStrap.class)
                .web(WebApplicationType.NONE)
                .profiles("java8")
                .run(args);

        CacalService bean = context.getBean(CacalService.class);
        System.out.println(bean.cal(1,2,3,4));
    }
}

编程方式 - @Conditional

打开@Profile注解会发现条件判断逻辑是类ProfileCondition中 处理的, 而这个父类是Condition ,类中包含了一个方法matches ,所有的判断逻辑都是在这里处理

@Conditional({ProfileCondition.class})
public @interface Profile {
    String[] value();
}
@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

自己定义一个MyConditional注解

@Conditional(OnMyConditional.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) //这个注解很重要
@Documented
public @interface MyConditional {
    String name() default "gwm";
}

接着写真正逻辑处理的类, 实现接口Condition 并实现方法 matches

public class OnMyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(MyConditional.class.getName());
        String name = (String)annotationAttributes.get("name");

        return name.equals(System.getProperty("user.name"));
    }
}

@MyConditional 可以使用到类上, 也可以使用到方法上, 用来判断是否被装载

@Configuration
@MyConditional(name = "gwm")
public class HelloDogConfiguration {
    @Bean
    public String dog() {
        return "hot dog!!!";
    }
}

@ComponentScan(basePackages = "com.example.demo.bootstrap.configuration")
//@EnableHelloworld
//@SpringBootApplication
public class ResponsityBootstrap {
    @Bean
    @MyConditional(name = "gwm")
    public String abc() {
        return "habc";
    }
}

自定义条件装配

参考上述的接口编程方式

SpringBoot自动装配

我们前面都是需要自己手动配置去装在bean, 这样还是太麻烦,所以springboot出现了一种可插拔式的自动化装配

底层装配技术

  • Spring 模式注解装配
  • Spring @Enable 模块装配
  • Spring 条件装配
  • Spring 工厂机制
    • 实现类:SpringFactoriesLoader spring3.2提供
    • 配置资源:META-INF/spring.factories

实现方式

  • 激活自动装配@EnableAutoConfiguration
  • 实现自动装配 XXXAutoConfiguration类
  • 配置自动装配实现META-INF/spring.factories
代码实现

定义个XXXAutoConfiguration类EnableDogAutoConfiguration, 用到了模式注解,@Enable注解,条件装配

//条件判断装配实现类
public class OnMyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(MyConditional.class.getName());
        String name = (String)annotationAttributes.get("name");
        return name.equals(System.getProperty("user.name"));
    }
}
//自定义条件注解
@Conditional(OnMyConditional.class)
public @interface MyConditional {
    String name() default "gwm";
}

//接口编程实现@Enable模块装配
@Import(HelloworldConfigurationSelector.class)
public @interface EnableHelloworld {
}
//正在实现@Enable判断的逻辑
public class HelloworldConfigurationSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{HelloWorldConfiguration.class.getName(), HelloDogConfiguration.class.getName()};
    }
}

//实现自动装配类
@Configuration //模式装配
@EnableHelloworld //@Enable装配(xxxSelectors选择性装配)
@MyConditional(name = "gwm") //条件装配
public class EnableDogAutoConfiguration {
}


接着配置自动装配类, 在Resources文件下建立 META-INF/spring.factories 配置如下, key是自动化装配注解, value就是实现自动化装配具体类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.bootstrap.configuration.EnableDogAutoConfiguration

启动类只需要激活自动化装配就可以, @SpringBootApplication 内置了自动化装配注解

@EnableAutoConfiguration //开启自动化装配
public class SpringBootAutoBootStrap {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = new SpringApplicationBuilder(SpringBootAutoBootStrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
      
        System.out.println(context.getBean("dog", String.class));
    }
}

理解SpringApplication

配置Springboot bean源

java 配置Class或者xml文件集合, 然后让Springboot的 BeanDefinitionLoader 类读取,将文件资源解析成Spring bean定义

//注解和xml形式两种方式
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    //...
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    this.xmlReader = XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null;
    //....
}

java配置Class

XML上下问配置文件

推断Web应用类型

其实是通过classpath路径下有没有存在对应相关的类来判断代码如下所示,可以看出reactive和Servlet是互斥的

String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
                          "org.springframework.web.context.ConfigurableWebApplicationContext" };
String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

static WebApplicationType deduceFromClasspath() {
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
   }
   for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
         return WebApplicationType.NONE;
      }
   }
   return WebApplicationType.SERVLET;
}

我们也可以手动指定类型web方法, 枚举类WebApplicationType

ConfigurableApplicationContext context = new SpringApplicationBuilder(ResponsityBootstrap.class)
        .web(WebApplicationType.NONE)
        .sources()
        .run(args);

推断main引导类

使用堆栈信息来进行推断主类

this.mainApplicationClass = deduceMainApplicationClass();

private Class<?> deduceMainApplicationClass() {
   try {
      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
      for (StackTraceElement stackTraceElement : stackTrace) {
         //遍历到包含main方法的类直接作为主方法的类, 赋值给mainApplicationClass成员变量
         if ("main".equals(stackTraceElement.getMethodName())) {
            return Class.forName(stackTraceElement.getClassName());
         }
      }
   }
   catch (ClassNotFoundException ex) {
      // Swallow and continue
   }
   return null;
}

加载应用上下文初始器 ApplicationContextInitializer

利用Springframework的工厂机制, 实例化ApplicationContextInitializer类 ,并排序对象集合

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

技术实现三部曲

  • 实现类: org.springframework.core.io.support.SpringFactoriesLoader
  • 配置资源: META-INF/spring.factories
  • 排序: AnnotationAwareOrderComparator#sort

添加自定义的ApplicationContextInitializer实现类并排序

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

此接口ApplicationContextInitializerspringframework.context 提供

package org.springframework.context;

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}
//自定义一个XXXInitializer初始化类
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AfterHelloworldInitializer<C extends ConfigurableApplicationContext> implements ApplicationContextInitializer<C> {

    @Override
    public void initialize(C configurableApplicationContext) {
        System.out.println("==============AfterHelloworldInitializer");
    }
}

MET-INF/spring.factories 下配置

org.springframework.context.ApplicationContextInitializer=\
com.example.demo.bootstrap.init.BeforeHelloworldInitializer,\
com.example.demo.bootstrap.init.AfterHelloworldInitializer

加载应用事件监听器 ApplicationListener

利用Springframework工厂机制, 实现类 ApplicationListener 并排序, 类似于上述三部曲

ApplicationListener 类是springframework.context包下的类

org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

有事件监听肯定有数据源通知监听做一定的逻辑处理

package org.springframework.context;

import java.util.EventObject;

public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp = System.currentTimeMillis();

    //Source事件源
    public ApplicationEvent(Object source) {
        super(source);
    }
	
    //事件发生的时间点
    public final long getTimestamp() {
        return this.timestamp;
    }
}

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloworldListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println("监控到ContextRefreshedEvent刷新事件");
    }
}

MET-INF/spring.factories 下配置

org.springframework.context.ApplicationListener=\
com.example.demo.bootstrap.init.HelloworldListener

SpringBoot运行阶段

加载 SpringApplication运行监听器SpringApplicationRunListeners

就是利用Springframework的工厂机制, 读取SpringApplicationRunListner配置的对象集合, 并且封装到组合类 SpringApplicationRunListeners

public ConfigurableApplicationContext run(String... args) {]
   //...
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //...
}       

运行 SpringApplication运行监听器SpringApplicationRunListeners

SpringApplicationRunListener 监听多个运行状态的方法

监听 SpringBoot事件/Spring事件

SpringBoot通过SpringApplicationRunListner的实现类EventPublishingRunListener(唯一的一个实现类)利用Spring事件API, 广播SpringBoot事件

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
SpringFramework 事件/监听器编程模型
  • Spring应用事件

    • 普通应用事件ApplicationEvent
    • 应用上下文事件ApplicationContextEvent
  • Spring应用监听器

    • 接口编程模型ApplicationListener
    • 注解编程模型@EventListener
  • Spring应用事件广播器

    • 接口ApplicationEventMulticaster

    • 实现类SimpleApplicationEventMulticaster

      • 同步或者异步执行

      • public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
            Executor executor = this.getTaskExecutor();
            Iterator var5 = this.getApplicationListeners(event, type).iterator();
        
            while(var5.hasNext()) {
                ApplicationListener<?> listener = (ApplicationListener)var5.next();
                if (executor != null) {
                    executor.execute(() -> {
                        this.invokeListener(listener, event);
                    });
                } else {
                    this.invokeListener(listener, event);
                }
            }
        
        }
        
EventPublishingRunListerner 监听方法与SpringBoot事件对应关系
监听方法Springboot事件Springboot起始版本
starting()ApplicationStartingEvent1.5
environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext context)ApplicationContextInitializedEvent1.0
contextLoaded(ConfigurableApplicationContext context)ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext context)ApplicationStartedEvent2.0
running(ConfigurableApplicationContext context)ApplicationReadyEvent2.0
failed(ConfigurableApplicationContext context, Throwable exception)ApplicationFailedEvent1.0

 

创建Spring应用上 ConfigurableApplicationContext

起始SpringApplication相关的都是SpringBoot后来引出来的新类

  • SpringApplicationEvent extend ApplicationEvent
  • SpringApplication.run(TestInitialzerDemo.class, args)

原始Spring发布事件

public class AnnotationConfigApplicationContextBootStrap {

    public static void main(String[] args) {
      
        AnnotationConfigApplicationContext annotated = new AnnotationConfigApplicationContext();

        //根据不同触发到的事件源做出动作
        annotated.addApplicationListener(event -> {
            if (event instanceof ContextRefreshedEvent) System.out.println("监听到刷新事件:"+event);
            else System.out.println("监听到其他事件:"+event);

        });

        /**                refresh()方法中干的一些事情
         *                 this.postProcessBeanFactory(beanFactory);
         *                 this.invokeBeanFactoryPostProcessors(beanFactory);
         *                 this.registerBeanPostProcessors(beanFactory);//注册bean
         *                 beanPostProcess.end();//bean后置处理完成
         *                 this.initMessageSource();
         *                 this.initApplicationEventMulticaster();//初始化事件广播器
         *                 this.onRefresh();
         *                 this.registerListeners(); //添加监听器
         *                 this.finishBeanFactoryInitialization(beanFactory);
         *                 this.finishRefresh();
         */
        annotated.refresh();
        //自定义发布事件
        annotated.publishEvent("helloworldEvent");
        annotated.close();

    }
}

SpringBoot事件编程

  • 实现接口SpringApplicationRunListener

  • 注意细节: 需要实现该接口的构造方法

    public class HelloworldRunListener implements SpringApplicationRunListener {
    
        @Override
        public void starting(ConfigurableBootstrapContext bootstrapContext) {
            System.out.println("===========此方法在Banner之前打印");
        }
    }
    
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
    }
    
  • 配置META-INF/spring.factories

org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.bootstrap.anntation.HelloworldRunListener

 

理解SpringWebMVC结构

Servlet职责

  • 处理请求
  • 资源管理
  • 视图渲染

SpringMVC架构

  • 编写Controller
public class HelloWorldController { 

	@RequestMapping("") 
	public String index() { 
	return "index"; 
	} 
}

  • 配置WebMVC组件
    @ComponentScan
    RequestMappingHandlerMapping
    RequestMappingHandlerAdapter
    InternalResourceViewResolver
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.imooc.web"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>

  • DispatcherServlet
    配置WEB-INF/DispatcherServlet

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app-context.xml</param-value> </init-param>
    

 

SpringFramework 重新认识 - WebMVC 核心组件

  • 处理器管理
  • 页面渲染
  • 异常处理
    在这里插入图片描述
    在这里插入图片描述

 

处理器管理
  • 映射 HandlerMapping 接口

  • 适配 HandlerAdapter 接口

  • 执行 HandlerExecutionChain

页面渲染
  • 视图解析 ViewResolver
  • 国际化 LocaleResolver LocaleContextResolver
  • 个性化 ThemeResolver
异常处理
  • 异常解析 HandlerExceptionResolver 接口
     

SpringFramework 重新认识 - WebMVC 核心组件 - WebMVC注解驱动

版本依赖在Spring Framework 3.1+

三个步骤
  • 注解配置: @Configuration (Spring模式注解)
  • 组件激活: @EnableWebMvc (Spring模块装配)
  • 自定义组件: WebMvcConfigurer (Spring Bean)
常用注解

注册模型属性: @ModelAttribute

读取请求头: @RequestHeader

读取 Cookie: @CookieValue

校验参数: @Valid @Validated

注解处理: @ExceptionHandler

切面通知:@ControllerAdvice

校验参数: @Vaild, @Validated

注解处理: @ExcptionHandler

切面通知: @ControllerAdvice

 

SpringFramework 重新认识 - WebMVC 核心组件 -Web NVC自动装配

  • Spring SPI: WebApplicationInitializer
  • Servlet 依赖: Servlet3.0+
  • Servlet SPI: ServletContainerInitalizer
  • Spring适配: SpringServletContianerInitializer
  • 编程驱动: AbstractDispatcherServletInitializer

  • 注解驱动: AbstractAnnotationConfigDispatcherServletInitializer

 

SpringBoot 时代的简化

  • 完全自动化装配

  • 条件装配

  • 外部化配置

完全自动化装配(WebMvcAutoConfiguration 类参考)
  • DispatcherServlet: DispatcherServletAutoConfiguration
  • 替换@EnableWebMvc: WebMvcAutoConfiguration
  • Servlet容器: ServletWebServerFactoryAutoConfiguration
理解自动装配的顺序(WebMvcAutoConfiguration 类参考)
  • 绝对顺序:@AutoConfigureOrder
  • 相对顺序: @AutoConfigureAfter
条件装配(WebMvcAutoConfiguration 类参考)
  • Web类型判断: Servlet
  • API依赖判断: Servlet, Spring Web MVC
  • Bean依赖判断: WebMvcConfigurationSupport
外部化配置(WebMvcAutoConfiguration 类参考)
  • WebMvc配置: WebMvcProperties
  • 资源的配置: ResourceProperties (css文件等)

 


 

WebMVC视图应用

模板引擎

核心要素

注意: 引入jar包: spring-boot-starter-Thymeleaf

  • 资源定位: 模板来源
  • 渲染上下文: 变量来源
  • 模板引擎: 模板渲染
     
资源定位(模板来源 )
  • 通用资源抽象
    • 文件资源: File
    • ClassPath资源: ClassLoader
    • 统一资源: URL
    • Web资源: ServletContext
  • Spring 资源抽象:
  • Spring 资源: Resource
渲染上下文(变量来源 )
  • 不同的实现
    • Context :Thyemeaf 渲染上下文
    • Model :Spring Web MVC 模型
    • Attribute :Servlet 上下文
模板引擎(模板渲染)
  • ITemplateEngine 实现
    • TemplateEngine :Thymeleaf 原生实现
    • SpringTemplateEngine :Spring 实现
    • SpringWebFluxTemplateEngine :Spring WebFlux 实现
示例:使用 Thymeleaf API 渲染内容
// 构建引擎
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
// 创建渲染上下文 
Context context = new Context(); context.setVariable("message", "Hello,World"); 
// 模板的内容 
String content = "<p th:text=\"${message}\">!!!</p>"; 
// 渲染(处理)结果 
String result = templateEngine.process(content, context); 
// 输出渲染(处理)结果 
System.out.println(result);
示例:Thymeleaf 与 Spring 资源整合

在文件夹resources 下面建立文件夹templates ,然后在建立xxx.html

  • Spring 资源

    • ResourceLoader 文件加载器类 与 Resource 资源文件

      ResourceLoader resourceLoader = new DefaultResourceLoader();
      Resource resource = resourceLoader.getResource("classpath:/templates/xxx.html");
      File file = resource.getFile();
      
  • 目的

    • 理解 Spring Resource 抽象

视图处理

SpringWebMVC 视图组件 (ViewResovler,View,DispatcherServlet)

在这里插入图片描述

  • 视图解析器: ViewResolver
  • 视图: View
  • 总控制器: DispatcherServlet

Thymeleaf 整合 SpringWebMVC(ThymeleafViewResovler,ThyeleafView,SpringTemplateEngine)

  • 视图解析器: ThymeleafViewResolver
  • 视图:ThymeleafView
  • 渲染: SpringTemplateEngine

交互流程

在这里插入图片描述在这里插入图片描述

实例: 多视图处理器并存 (顺序Order支持)
  • 视图处理器
    • ThymeleafViewResolver 处理Thymeleaf
    • IntervalResourceViewResolver 处理JSP
  • 目的
    • 理解 ViewResolver , Order
    • 理解 ViewResolver 模板资源查找
    • 自定义 ViewResolver , Order
@Bean
@ConditionalOnMissingBean //这个条件就是给了你可以重写这个方法的机会
public InternalResourceViewResolver defaultViewResolver() {
  	InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  	resolver.setPrefix(this.mvcProperties.getView().getPrefix());
  	resolver.setSuffix(this.mvcProperties.getView().getSuffix());
  return resolver;
}

怎么调整这个试图处理器多个情况下的顺序呢?

  • 继承WebMvcConfigurer
  • 重写:configureViewResolvers方法
  • 调整Ordered的执行顺序即可

 

视图内容协商

在这里插入图片描述

核心组件

  • 视图解析

    ContentNegotiatingViewResolver

    InternalResourceViewResolver

    BeanNameViewResolver

    ThymeleafViewResolver

  • 配置策略 (策略模式参考)

    配置 Bean: WebMvcConfigurer

    配置对象: ContentNegotiationConfigurer

  • 策略管理

    Bean: ContentNegotiationManager

    FactoryBean : ContentNegotiationManagerFactoryBean

  • 策略实现

    • ContentNegotiationStrategy

      固定 MediaType : FixedContentNegotiationStrategy

      “Accept” 请求头: HeaderContentNegotiationStrategy

      请求参数: ParameterContentNegotiationStrategy

      路径扩展名: PathExtensionContentNegotiationStrategy

在这里插入图片描述

序列图

在这里插入图片描述

交互图

在这里插入图片描述

实例: 多视图处理器内容协商

视图组件自动装配

自动装配Bean

视图处理器
内容协商
外部化配置
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页