Spring 注解开发学习笔记(二)—— Servlet3.0

新的servlet3.0规范,定义了很多web注解,web工程可以不需要web.xml文件了。首先我们来创建一个没有web.xml的maven工程

        <!-- 原生web程序,没有集成spring,所以是war包 -->
	<packaging>war</packaging> 


	<!-- 添加servlet依赖,Servlet3.0在tomcat7+使用 -->
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.1.0</version>
				<configuration>
					<!-- 使用该插件后,eclipse就不会包遗失web.xml文件 -->
					<failOnMissingWebXml>false</failOnMissingWebXml> 
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 使用注解创建Servlet、Filter、Listener

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

	}
	
	public void sayHello() throws Exception{

	}

}
@WebFilter("/*")
public class MyFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
		
	}

}
@WebListener
public class MyListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		
	}

}

通过ServletContainerInitializer注册Servlet、Filter、Listener

//创建ServletContainerInitializer的实现类
//web容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
@HandlesTypes(value={HelloService.class})  //注意:该类必须是一个父类或接口,下面必须有实现类,否则启动报错
public class MyServletContainerInitializer implements ServletContainerInitializer {


	//web程序启动时,会调用该方法
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		for (Class<?> claz : arg0) {
			//claz.isInterface  是否是接口  claz.里面还有其他方法
			System.out.println(claz);  //输出class cn.fg.service.HelloServiceImpl 它是HelloService的实现类
		}
		
		
		//注册Servlet
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet()); //可以new,也可以UserServlet.class
		servlet.addMapping("/user"); //配置servlet的映射信息
		
		//注册Listener
		sc.addListener(UserListener.class);
	
		//注册Filter  FilterRegistration
		FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
		//配置Filter的映射信息,第二个参数为是否排在上一个Filter之后,第1个参数为null,默认DispatcherType.REQUEST
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");  
		
	}

}

//web程序启动时为什么会调用onStartup()
1. 扫描所有jar包classpath目录下的META-INF/services/javax.servlet.ServletContainerInitializer这样一个文件(包括lib下的jar包)
2. 文件中的内容就是ServletContainerInitializer实现类的全类名,例如cn.fg.servlet.MyServletContainerInitializer

Servlet3.0 与 SpringMVC 分析

1、web容器在启动的时候,会扫描spring-web-xxx.jar下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类org.springframework.web.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();

使用非web.xml的方式配置SpringMVC

//web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	//获取根容器的配置类;(Spring的配置文件)   父容器;
	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[]{RootConfig.class};
	}

	//获取web容器的配置类(SpringMVC配置文件)  子容器;
	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[]{WebConfig.class};
	}

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

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

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


}

使用Java类配置Spring MVC

@EnableWebMvc //启用springmvc注解 等同于 <mvc:annotation-driven />
//本来是implements WebMvcConfigurer接口的,但是WebMvcConfigurer接口的方法太多了,用不到这些,所有我们继承了WebMvcConfigurerAdapter
public class WebConfig  extends WebMvcConfigurerAdapter  {  

	//配置视图解析器 等同于 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">......</bean>
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/page/", ".jsp");
	}
	
	//配置静态资源的处理 等同于 <mvc:default-servlet-handler />
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
	
	//配置拦截器 等同于<mvc:interceptors>......</mvc:interceptors>
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
	}
}

Servlet 异步请求

//非异步请求会占用tomcat线程一直到线程结束才会释放
@WebServlet(value="/async",asyncSupported=true)  //开启异步支持
public class HelloAsyncServlet extends HttpServlet {
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	
		AsyncContext startAsync = req.startAsync(); //获取AsyncContext 
	
		startAsync.start(new Runnable() {   //启动一个线程,这里可以使用自定义的线程池处理业务逻辑
			@Override
			public void run() {
				sayHello();  //调用业务逻辑
				startAsync.complete();  //异步处理完成
				ServletResponse response = asyncContext.getResponse();  //拿到Response
				response.getWriter().write("hello async...");  //输出响应内容
		
			}
		});		

	}

	public void sayHello() {
		
	}
}

注意:一旦servlet开启异步,对应的flter也要开启异步,否则会报错

Spring MVC 的异步请求(Callable)

//注意开启servlet异步支持
@Controller
public class AsyncController {
	

	@ResponseBody
	@RequestMapping("/async01")
	public Callable<String> async01(){
		System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
		
		Callable<String> callable = new Callable<String>() {
			@Override
			public String call() throws Exception {
				System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
				Thread.sleep(2000);
				System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
				return "Callable<String> async01()";
			}
		};
		
		System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
		return callable;
	}

    	/**
	 * 执行原理
	 * 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...
		afterCompletion...
		
		异步的拦截器:
			1)、原生API的AsyncListener
			2)、SpringMVC:实现AsyncHandlerInterceptor;
	 */


}
public class MyFirstInterceptor implements HandlerInterceptor {

	//目标方法运行之前执行
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("preHandle..."+request.getRequestURI());
		return true;
	}

	//目标方法执行正确以后执行
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("postHandle...");

	}

	//页面响应以后执行
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("afterCompletion...");
	}

}

Spring MVC 的异步请求(DeferredResult)

	@RequestMapping("test04")
	public DeferredResult<String> test04(){
		DeferredResult<String> deferredResult = new DeferredResult<>(5000l,"error"); //如果超时还没有拿到消息,则跳转error页面
		MyCacheUtil.setCache("001", deferredResult);  //使用缓存模拟发送消息到mq(消息中间件)
		return deferredResult;
	}
	
	//这里我们请求receive,来模拟监听mq(消息中间件)得到消息
	@ResponseBody
	@RequestMapping("receive")
	public DeferredResult<String> receive(){
		//使用缓存模拟取出消息
		DeferredResult<String> deferredResult = (DeferredResult<String>) MyCacheUtil.getCache("001"); 
		//设置Result的值,值的类型是根据泛型定的。该值就是上面test04的返回值,然后会跳转test01页面
		deferredResult.setResult("test01");   
		return deferredResult;
	}

 

参考手册:

https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/web.html#spring-web

https://docs.spring.io/spring/docs/4.3.26.RELEASE/spring-framework-reference/htmlsingle/#mvc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值