Spring注解驱动开发(六)

[源码]-Spring容器创建-BeanFactory预准备

Spring容器的refresh()【创建刷新】;
1prepareRefresh()刷新前的预处理;
	1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;
	2)、getEnvironment().validateRequiredProperties();检验属性的合法等
	3)、earlyApplicationEvents= new LinkedHashSet<ApplicationEvent>();保存容器中的一些早期的事件;
2obtainFreshBeanFactory();获取BeanFactory;
	1)、refreshBeanFactory();刷新【创建】BeanFactory;
			创建了一个this.beanFactory = new DefaultListableBeanFactory();
			设置id;
	2)、getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;
	3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;
3prepareBeanFactory(beanFactory);BeanFactory的预准备工作(BeanFactory进行一些设置);
	1)、设置BeanFactory的类加载器、支持表达式解析器...
	2)、添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
	3)、设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;
	4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:
			BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
	5)、添加BeanPostProcessor【ApplicationListenerDetector】
	6)、添加编译时的AspectJ;
	7)、给BeanFactory中注册一些能用的组件;
		environment【ConfigurableEnvironment】、
		systemProperties【Map<String, Object>】、
		systemEnvironment【Map<String, Object>4postProcessBeanFactory(beanFactory);BeanFactory准备工作完成后进行的后置处理工作;
	1)、子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
======================以上是BeanFactory的创建及预准备工作==================================

[源码]-Spring容器创建-执行BeanFactoryPostProcessor

5invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor的方法;
	BeanFactoryPostProcessor:BeanFactory的后置处理器。在BeanFactory标准初始化之后执行的;
	两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
	1)、执行BeanFactoryPostProcessor的方法;
		先执行BeanDefinitionRegistryPostProcessor
		1)、获取所有的BeanDefinitionRegistryPostProcessor;
		2)、看先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor、
			postProcessor.postProcessBeanDefinitionRegistry(registry)
		3)、在执行实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor;
			postProcessor.postProcessBeanDefinitionRegistry(registry)
		4)、最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors;
			postProcessor.postProcessBeanDefinitionRegistry(registry)
			
		
		再执行BeanFactoryPostProcessor的方法
		1)、获取所有的BeanFactoryPostProcessor
		2)、看先执行实现了PriorityOrdered优先级接口的BeanFactoryPostProcessor、
			postProcessor.postProcessBeanFactory()
		3)、在执行实现了Ordered顺序接口的BeanFactoryPostProcessor;
			postProcessor.postProcessBeanFactory()
		4)、最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostProcessor;
			postProcessor.postProcessBeanFactory()

[源码]-Spring容器创建-注册BeanPostProcessors

6registerBeanPostProcessors(beanFactory);注册BeanPostProcessor(Bean的后置处理器)【 intercept bean creation】
		不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的
		BeanPostProcessor、
		DestructionAwareBeanPostProcessor、
		InstantiationAwareBeanPostProcessor、
		SmartInstantiationAwareBeanPostProcessor、
		MergedBeanDefinitionPostProcessor【internalPostProcessors】、
		
		1)、获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
		2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;
			把每一个BeanPostProcessor;添加到BeanFactory中
			beanFactory.addBeanPostProcessor(postProcessor);
		3)、再注册Ordered接口的
		4)、最后注册没有实现任何优先级接口的
		5)、最终注册MergedBeanDefinitionPostProcessor;
		6)、注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是
			applicationContext.addApplicationListener((ApplicationListener<?>) bean);

[源码]-Spring容器创建-初始化MessageSource

7initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
		1)、获取BeanFactory
		2)、看容器中是否有id为messageSource的,类型是MessageSource的组件
			如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
				MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
		3)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;
			beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);	
			MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);

[源码]-Spring容器创建-初始化事件派发器、监听器等

8initApplicationEventMulticaster();初始化事件派发器;
		1)、获取BeanFactory
		2)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
		3)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
		4)、将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
9onRefresh();留给子容器(子类)
		1、子类重写这个方法,在容器刷新的时候可以自定义逻辑;
10registerListeners();给容器中将所有项目里面的ApplicationListener注册进来;
		1、从容器中拿到所有的ApplicationListener
		2、将每个监听器添加到事件派发器中;
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		3、派发之前步骤产生的事件;

[源码]-Spring容器创建-Bean创建完成

11finishBeanFactoryInitialization(beanFactory);初始化所有剩下的单实例bean;
	1、beanFactory.preInstantiateSingletons();初始化后剩下的单实例bean
		1)、获取容器中的所有Bean,依次进行初始化和创建对象
		2)、获取Bean的定义信息;RootBeanDefinition
		3)、Bean不是抽象的,是单实例的,是懒加载;
			1)、判断是否是FactoryBean;是否是实现FactoryBean接口的Bean;
			2)、不是工厂Bean。利用getBean(beanName);创建对象
				0getBean(beanName); ioc.getBean();
				1doGetBean(name, null, null, false);
				2、先获取缓存中保存的单实例Bean。如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来)
					从private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);获取的
				3、缓存中获取不到,开始Bean的创建对象流程;
				4、标记当前bean已经被创建
				5、获取Bean的定义信息;
				6、【获取当前Bean依赖的其他Bean;如果有按照getBean()把依赖的Bean先创建出来;】
				7、启动单实例Bean的创建流程;
					1)、createBean(beanName, mbd, args);
					2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);让BeanPostProcessor先拦截返回代理对象;
						【InstantiationAwareBeanPostProcessor】:提前执行;
						先触发:postProcessBeforeInstantiation();
						如果有返回值:触发postProcessAfterInitialization()3)、如果前面的InstantiationAwareBeanPostProcessor没有返回代理对象;调用44)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);创建Bean
						 1)、【创建Bean实例】;createBeanInstance(beanName, mbd, args);
						 	利用工厂方法或者对象的构造器创建出Bean实例;
						 2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
						 	调用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName);
						 3)、【Bean属性赋值】populateBean(beanName, mbd, instanceWrapper);
						 	赋值之前:
						 	1)、拿到InstantiationAwareBeanPostProcessor后置处理器;
						 		postProcessAfterInstantiation()2)、拿到InstantiationAwareBeanPostProcessor后置处理器;
						 		postProcessPropertyValues()=====赋值之前:===
						 	3)、应用Bean属性的值;为属性利用setter方法等进行赋值;
						 		applyPropertyValues(beanName, mbd, bw, pvs);
						 4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd);
						 	1)、【执行Aware接口方法】invokeAwareMethods(beanName, bean);执行xxxAware接口的方法
						 		BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
						 	2)、【执行后置处理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
						 		BeanPostProcessor.postProcessBeforeInitialization();
						 	3)、【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
						 		1)、是否是InitializingBean接口的实现;执行接口规定的初始化;
						 		2)、是否自定义初始化方法;
						 	4)、【执行后置处理器初始化之后】applyBeanPostProcessorsAfterInitialization
						 		BeanPostProcessor.postProcessAfterInitialization()5)、注册Bean的销毁方法;
					5)、将创建的Bean添加到缓存中singletonObjects;
				ioc容器就是这些Map;很多的Map里面保存了单实例Bean,环境信息。。。。;
		所有Bean都利用getBean创建完成以后;
			检查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就执行afterSingletonsInstantiated()

[源码]-Spring容器创建-容器创建完成

12finishRefresh();完成BeanFactory的初始化创建工作;IOC容器就创建完成;
		1)、initLifecycleProcessor();初始化和生命周期有关的后置处理器;LifecycleProcessor
			默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();
			加入到容器;
			
			写一个LifecycleProcessor的实现类,可以在BeanFactory
				void onRefresh();
				void onClose();	
		2)、	getLifecycleProcessor().onRefresh();
			拿到前面定义的生命周期处理器(BeanFactory);回调onRefresh()3)、publishEvent(new ContextRefreshedEvent(this));发布容器刷新完成事件;
		4)、liveBeansView.registerApplicationContext(this);

[源码]-Spring源码总结

	======总结===========
	1)、Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;
		1)、xml注册bean;<bean>
		2)、注解注册Bean;@Service@Component@Bean、xxx
	2)、Spring容器会合适的时机创建这些Bean
		1)、用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
		2)、统一创建剩下所有的bean的时候;finishBeanFactoryInitialization()3)、后置处理器;BeanPostProcessor
		1)、每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
			AutowiredAnnotationBeanPostProcessor:处理自动注入
			AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
			xxx....
			增强的功能注解:
			AsyncAnnotationBeanPostProcessor
			....
	4)、事件驱动模型;
		ApplicationListener;事件监听;
		ApplicationEventMulticaster;事件派发:

servlet3.0-简介&测试

现在,我们来说说注解版的web,我们以前来写web的三大组件:Servlet、Filter、Listener,包括SpringMVC的前端控制器DispatcherServlet都需要在web.xml文件中来进行注册;而在Servlet3.0标准以后,就给我们提供了方便的注解的方式来完成我们这些组件的注册以及添加,提供了运行时的可插拔的插件能力;、

说明:Servlet3.0及以上的标准是需要Tomcat7及以上的支持;


  1. 创建一个动态的web工程:
    在这里插入图片描述

  1. 我们来写上一个jsp页面:
<%--
  Created by IntelliJ IDEA.
  User: WH1803054
  Date: 2019/1/17
  Time: 19:07
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="hello">hello</a>
  </body>
</html>
  1. 我们再来写上一个servlet,并且用@WebServlet("/hello")来标注,并且指明要拦截哪些路径:
package com.ldc.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello...");
    }
}
  1. 启动Tomcat服务器:运行成功
    在这里插入图片描述
    点进这个链接也会打印字符串:
    在这里插入图片描述

同样,要注册Filter用@WebFilter注解、注册Listener用@WebListener注解;如果在注册的时候,需要一些初始化参数,我们就可以用@WebInitParam注解;


servlet3.0-ServletContainerInitializer

Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

1、Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;
	必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
	文件的内容就是ServletContainerInitializer实现类的全类名;

总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;


ServletContainerInitializer;
@HandlesTypes

第一步:我们写一个MyServletContainerInitializer 类实现ServletContainerInitializer 接口

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类或者子接口等)传递过来
//传入感兴趣的类型
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 在应用启动的时候,会运行onStartup方法;
     * Set<Class<?>> :感兴趣的类型的所有子类型;
     * ServletContext 代表当前的web应用的ServletContext对象,一个web应用相当于是一个ServletContext
     * @throws ServletException
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        set.forEach(System.out::println);
    }
}

第二步:在这个路径下新建一个文件

在这里插入图片描述
文件的内容就写我们实现ServletContainerInitializer 这个接口的类MyServletContainerInitializer 的全类名:
在这里插入图片描述


在这里插入图片描述


最后,我们运行起来,运行结果为:

感兴趣的类型:
class com.ldc.service.HelloServiceExt
class com.ldc.service.AbstractHelloService
class com.ldc.service.HelloServiceImpl


servlet3.0-ServletContext注册三大组件

首先我们写一个Servlet:

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("tomcat...");
        System.out.println("UserServlet...doGet...");
    }
}

再来一个Filter:

public class UserFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //过滤请求
        System.out.println("UserFilter...doFilter...");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

最后,再写一个Listener:

/**
 * 监听项目的启动和停止
 */
public class UserListener implements ServletContextListener {
    //监听ServletContextEvent的启动初始化
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("UserListener...contextInitialized");
    }
    //监听ServletContextEvent销毁
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("UserListener...contextDestroyed");
    }
}

最后,我们来用ServletContext来进行注册三大组件:

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类或者子接口等)传递过来
//传入感兴趣的类型
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 在应用启动的时候,会运行onStartup方法;
     * Set<Class<?>> :感兴趣的类型的所有子类型;
     * ServletContext 代表当前的web应用的ServletContext对象,一个web应用相当于是一个ServletContext
     * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
     * 2)、使用编码的方式,在项目启动的时候给ServletContext添加组件
     * 必须在项目启动的时候来添加
     *  (1)ServletContainerInitializer得到ServletContext对象来注册;
     *  (2)ServletContextListener的方法的参数里面的ServletContextEvent对象可以获取ServletContext对象
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        set.forEach(System.out::println);

        //注册组件
        Dynamic servlet = servletContext.addServlet("userServlet", new UserServlet());
        //配置servlet的映射信息
        servlet.addMapping("/user");

        //注册Listener
        servletContext.addListener(UserListener.class);

        //注册Filter
        FilterRegistration.Dynamic filter = servletContext.addFilter("userFilter", UserFilter.class);
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
    }
}

我们启动,然后再浏览器上进行访问:

感兴趣的类型:
class com.ldc.service.AbstractHelloService
class com.ldc.service.HelloServiceImpl
class com.ldc.service.HelloServiceExt
UserListener…contextInitialized
UserFilter…doFilter…
UserFilter…doFilter…
UserFilter…doFilter…
UserServlet…doGet…

如果服务器停止了,监听器也是能监听得到:

UserListener…contextDestroyed


servlet3.0-与SpringMVC整合分析

Spring5.2.0 WebServlet官方文档


1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类SpringServletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
	1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext()2)、AbstractDispatcherServletInitializer:
			创建一个web的ioc容器;createServletApplicationContext();
			创建了DispatcherServlet;createDispatcherServlet();
			将创建的DispatcherServlet添加到ServletContext中;
				getServletMappings();
	3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
			创建根容器:createRootApplicationContext()
					getRootConfigClasses();传入一个配置类
			创建web的ioc容器: createServletApplicationContext();
					获取配置类;getServletConfigClasses();
	
总结:
	以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;



springmvc-整合

  1. 导入jar包:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ldc</groupId>
    <artifactId>springmvc-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>3.0-alpha-1</version>
            <!--因为tomcat也有servlet-api,要是项目打成war包的时候,就不要带上这个jar包,否则就会引起冲突-->
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 创建web初始化器,以及父子配置类:
//Web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //获取父容器的配置类:(Spring的配置文件) --->作为父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    //获取web容器的配置类(SpringMVC配置文件) --->作为一个子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{AppConfig.class};
    }

    //获取DispatcherServlet的映射信息
    //  /:拦截所有请求(包括静态资源(xx.js,xx.png),但是不包括*.jsp)
    //  /*:拦截所有请求,连*.jsp页面都拦截;jsp页面是tomcat引擎解析的
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

//Spring的容器不扫描Controller,父容器
@ComponentScan(value = {"com.ldc."},excludeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class RootConfig {

}

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class AppConfig {

}

  1. 我们再来写一个Controller和Service:
@Controller
public class HelloController {

    @Autowired
    private HelloService helloService;

    @ResponseBody
    @RequestMapping("hello")
    public String hello() {
        String hello = helloService.sayHello("tomcat");
        return hello;
    }

}

@Service
public class HelloService {

    public String sayHello(String name) {
        return "Hello," + name;
    }

}


  1. 测试:我们启动tomcat服务器来运行测试:
    在这里插入图片描述

springmvc-定制与接管SpringMVC

SpringMVC的其他相关的注解的配置参考官方文档

定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能;
	<mvc:annotation-driven/>2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
	extends WebMvcConfigurerAdapter

SpringMVC具体配置的参考文档


我们就可以这样来写:

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {

    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
        //将SpringMVC处理不了的请求交给tomcat,专门针对于静态资源的,这个时候,静态资源就是可以访问的
        defaultServletHandlerConfigurer.enable();
    }

    @Override
    public void addFormatters(FormatterRegistry formatterRegistry) {
        //添加自定义的类型转换器
    }

    @Override
    public void addInterceptors(InterceptorRegistry interceptorRegistry) {

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {

    }

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {

    }

    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {

    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {

    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public Validator getValidator() {
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}

但是上面直接实现WebMvcConfigurer接口的方式,有很多的方法用不到,我们可以用这个适配器WebMvcConfigurerAdapter来实现,它实现了WebMvcConfigurer接口:

我们可以来定义一个视图解析器:

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
    //定制

    //视图解析器

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //默认所有页面都是从/WEB-INF/xxx.jsp
        //registry.jsp();
        //我们也可以自己来写规则
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
}

我们在/WEB-INF/views/这个路径下新建一个success.jsp

<%--
  Created by IntelliJ IDEA.
  User: WH1803054
  Date: 2019/1/17
  Time: 22:27
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>success</h1>
</body>
</html>


在这里插入图片描述


现在,我们来运行项目,测试:这个时候,就表示视图解析器配置成功了
在这里插入图片描述


我们在这个路径下放一个图片,然后再写一个jsp来访问它:
在这里插入图片描述


这个时候,图片是显示不出来的:
在这里插入图片描述


现在,我们来配置允许静态资源的访问:

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
    //定制

    //视图解析器

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //默认所有页面都是从/WEB-INF/xxx.jsp
        //registry.jsp();
        //我们也可以自己来写规则
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    //静态资源的访问
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

这个时候,我们的图片就是可以访问了:
在这里插入图片描述


我们写上一个拦截器:

public class MyFirstInterceptor implements HandlerInterceptor {
    //在目标方法执行之前执行
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("preHandle...");
        return true;
    }

    //在目标方法执行之后执行
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    //页面响应以后执行
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion...");
    }
}

我们在配置类里面添加拦截器:

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
    //定制

    //视图解析器

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //默认所有页面都是从/WEB-INF/xxx.jsp
        //registry.jsp();
        //我们也可以自己来写规则
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    //静态资源的访问
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截任意的路径
        registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
    }
}

我们重新启动,并且访问这个路径:
在这里插入图片描述


我们发现控制台已经打印了,说明拦截器已经起作用了:

preHandle…
postHandle…
afterCompletion…
preHandle…
postHandle…
afterCompletion…


servlet3.0-异步请求

servlet3.0异步处理
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求。
即每一次Http请求都由某一个线程从头到尾负责处理。
在这里插入图片描述
如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。


我们可以在之前写的Servlet加上当前的线程:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(Thread.currentThread()+" start...");
        try {
            sayHello();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resp.getWriter().write("hello...");
        System.out.println(Thread.currentThread()+" end...");
    }

    private void sayHello() throws InterruptedException {
        System.out.println(Thread.currentThread()+ " processing...");
        Thread.sleep(3000);
    }

}

启动服务之后,控制台打印结果为:我们可以发现从线程开始、处理请求到执行结束从始至终都是Thread[http-nio-8081-exec-3,5,main]这个线程,主线程得不到释放,当下一个请求进来就得不到处理;

UserFilter…doFilter…
Thread[http-nio-8081-exec-3,5,main] start…
Thread[http-nio-8081-exec-3,5,main] processing…
Thread[http-nio-8081-exec-3,5,main] end…


我们再来加上是哪些线程处理的:

@WebServlet(value = "/async",asyncSupported = true)
public class HelloAsyncServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.支持异步处理:asyncSupported = true

        //2.开启异步模式
        System.out.println("主线程开始..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
        AsyncContext startAsync = req.startAsync();
        //3.业务逻辑进行异步处理,开始异步处理
        startAsync.start(()-> {
            try {
                System.out.println("副线程开始..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
                sayHello();
                startAsync.complete();
                //获取异步上下文
                //AsyncContext asyncContext = req.getAsyncContext();
                //4.获取响应
                ServletResponse response = startAsync.getResponse();
                response.getWriter().write("hello async...");
                System.out.println("副线程结束..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println("主线程结束..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
    }

    private void sayHello() throws InterruptedException {
        System.out.println(Thread.currentThread()+ " processing..."+"==>"+ Instant.now().toEpochMilli());
        Thread.sleep(3000);
    }
}

执行结果如下:

感兴趣的类型:
class com.ldc.service.HelloServiceImpl
class com.ldc.service.HelloServiceExt
class com.ldc.service.AbstractHelloService
UserListener…contextInitialized
[2019-01-18 04:47:06,991] Artifact servlet3.0:war exploded: Artifact is deployed successfully
[2019-01-18 04:47:06,992] Artifact servlet3.0:war exploded: Deploy took 447 milliseconds
UserFilter…doFilter…
UserFilter…doFilter…
UserFilter…doFilter…
主线程开始…Thread[http-nio-8081-exec-7,5,main]>1547801232248
主线程结束…Thread[http-nio-8081-exec-7,5,main]
>1547801232253
副线程开始…Thread[http-nio-8081-exec-8,5,main]>1547801232253
Thread[http-nio-8081-exec-8,5,main] processing…
>1547801232253
副线程结束…Thread[http-nio-8081-exec-8,5,main]==>1547801235253


现在的处理就是可以表示成下图:
在这里插入图片描述


springmvc-异步请求-返回Callable

@Controller
public class AsyncController {
	 /**
     * 1、控制器返回Callable
     * 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
     * 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
     * 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
     * 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
     *
     * preHandle.../springmvc-annotation/async01
     主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
     主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
     =========DispatcherServlet及所有的Filter退出线程============================

     ================等待Callable执行==========
     副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
     副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
     ================Callable执行完成==========

     ================再次收到之前重发过来的请求========
     preHandle.../springmvc-annotation/async01
     postHandle...(Callable的之前的返回值就是目标方法的返回值)
     afterCompletion...

     异步的拦截器:
     1)、原生API的AsyncListener
     2)、SpringMVC:实现AsyncHandlerInterceptor;
     * @return
     */
    @ResponseBody
    @RequestMapping("/async01")
    public Callable<String> async01() {
        System.out.println("主线程开始..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("副线程开始..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
                Thread.sleep(2000);
                System.out.println("副线程结束..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
                return "Callable<String> async01()";
            }
        };
        System.out.println("主线程结束..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
        return callable;
    }

}

此时运行起来的测试结果如下:

preHandle…
主线程开始…Thread[http-nio-8081-exec-7,5,main]>1547802269
主线程结束…Thread[http-nio-8081-exec-7,5,main]
>1547802269
副线程开始…Thread[MvcAsync1,5,main]>1547802269
副线程结束…Thread[MvcAsync1,5,main]
>1547802271
preHandle…
postHandle…
afterCompletion…


springmvc-异步请求-返回DeferredResult

我们来看一个实际的应用场景:
在这里插入图片描述


public class DeferredResultQueue {

    private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedDeque<>();

    public static void save(DeferredResult<Object> deferredResult) {
        queue.add(deferredResult);
    }
    public static DeferredResult<Object> get() {
        return queue.poll();
    }
}

@Controller
public class AsyncController {


    @ResponseBody
    @RequestMapping("/createOrder")
    public DeferredResult<Object> createOrder(){
        DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");

        DeferredResultQueue.save(deferredResult);

        return deferredResult;
    }


    @ResponseBody
    @RequestMapping("/create")
    public String create(){
        //创建订单
        String order = UUID.randomUUID().toString();
        DeferredResult<Object> deferredResult = DeferredResultQueue.get();
        deferredResult.setResult(order);
        return "success===>"+order;
    }
}

我们先访问这个创建订单createOrder接口:
在这里插入图片描述


我们再来访问这个create接口,此时的结果如图所示:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_37778801/article/details/86522477