Spring AOP实现

  • 前言
  • servlet3.0简介
  • ServletContainerInitializer
    • shared libraries(共享库) / runtimes pluggability(运行时插件)
  • 整合SpringMVC
    • 分析
      • 小结
    • 整合
    • 定制springMVC
  • servlet3.0的异步请求处理
    • servlet实现
    • springMVC实现
  • end...

前言

在以前(不知多久以前,我大学期间学的servlet就是通过@WebServlet注解注册了,虽然现在也不会去使用了,但还是值得我们去回顾回顾),我们需要在代码中添加servlet、filter、listener、DispatcherServlet等组件都需要在web.xml中进行配置,servlet3.0后,为我们提供了相应的注解,简化了我们的开发,本文先介绍了servlet3.0,讲解了ServletContainerInitializer,然后介绍了如何整合springMVC,最后介绍了servlet3.0的异步请求处理。文章课程链接:尚硅谷spring注解驱动教程(雷神)

servlet3.0简介

servlet3.0为我们提供了一些注解,简化我们的开发,servlet、filter、listener的注册对应注解@WebServlet、@WebFilter、@WebListener,要使用初始化参数可使用@WebInitParam,具体可以参考相关文档。注意,servlet3.0需要在tomcat7.0版本及以上版本才能使用,下面我们写一个简单的例子,代码如下:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("hello...");}
}

这样我们就能通过 /hello 访问到了,并给我们返回 hello…

ServletContainerInitializer

shared libraries(共享库) / runtimes pluggability(运行时插件)

servlet容器启动会扫描当前应用里面每一个jar包的ServletContainerInitializer实现类(扫描位置为MATE-INF/services/javax.servlet.ServletContainerInitializer),我们可以提供一个ServletContainerInitializer的实现类(该实现类必须绑定在MATE-INF/services/javax.servlet.ServletContainerInitializer文件中,文件的内容为ServletContainerInitializer实现类的全类名),如图

下面,我们就来写一个,代码如下:

// 容器启动时,会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来
@HandlesTypes(value = {HelloServlet.class})
// HelloServlet的子类会通过Set<Class<?>> set传递过来(不含本身),可以拿过来创建对象等
public class MyServletContainerInitializer implements ServletContainerInitializer {/*** 应用启动的时候会运行onStartup方法* set:感兴趣类型的所有子类型** servletContext :代表当前web应用的ServletContext,一个web应用一个ServletContext,* 可以用来注册web组件(servlet、filter、listener),解决我们注册第三方jar包组件的问题* 1、这种是使用编码的方式,在项目启动的时候给ServletContext里面添加组件(必须在项目启动的时候,运行时不行)* 2、也可以在ServletContextListener中实现的方法中拿到ServletContextEvent进行注册* public class UserListener implements ServletContextListener {*     @Override*     public void contextInitialized(ServletContextEvent sce) {*            sce.addxxx*         System.out.println("初始化");*     }**     @Override*     public void contextDestroyed(ServletContextEvent sce) {*         System.out.println("销毁");*     }* }*/@Overridepublic void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {System.out.println("感兴趣的类型" + set);// 注册组件,先自己创建相应的web组件实现类// 注册servletServletRegistration.Dynamic servlet = servletContext.addServlet("helloServlet", new HelloServlet());// 返回的ServletRegistration.Dynamic用来指定映射信息servlet.addMapping("/hello");// 注册listenerservletContext.addListener(UserListener.class);// 注册filterFilterRegistration.Dynamic filter = servletContext.addFilter("userFilter", userFilter.class);filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");}
}

写好后,运行,即可看到相应的打印信息

整合SpringMVC

分析

整合之前,我们先来分析一下,看这张图,是不是很熟悉
点开spring-web包中,包含了我们前面讲到的META-INF>services包,存在javax.servlet.ServletContainerInitializer这个文件,值为org.springframework.web.SpringServletContainerInitializer,下面,我们就贴上这个实现类的源码,对他进行分析

// spring应用一启动就会加载WebApplicationInitializer接口下的所有组件,并且为这些组件创建对象
// (不是接口或抽象类)
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {public SpringServletContainerInitializer() {}public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {List<WebApplicationInitializer> initializers = new LinkedList();Iterator var4;if (webAppInitializerClasses != null) {var4 = webAppInitializerClasses.iterator();while(var4.hasNext()) {Class<?> waiClass = (Class)var4.next();// 挨个遍历,如果不是接口或抽象类,创建对象if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());} catch (Throwable var7) {throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);}}}// ...}}
}

从SpringServletContainerInitializer中,我们看到,他是要为WebApplicationInitializer的非接口或非抽象类的子类创建对象,下面我们就看看他有哪些子类,并做了什么操作,首先看看有哪些子类


他们其中的部分关系如图

从图中看到有6个子类,这里就不依次进行源码分析,只做文字描述

  • AbstractContextLoaderInitializer

创建根容器:createRootApplicationContext(),如果跟容器不为空,创建ContextLoaderListener并将其放到ServletContext中

  • AbstractDispatcherServletInitializer

是AbstractContextLoaderInitializer的子类,
先是创建一个web的IOC容器:this.createServletApplicationContext();
然后创建了一个DispatcherServlet:this.createDispatcherServlet(servletAppContext);
再将DispatcherServlet加入到ServletContext中,再通过返回的Dynamic配置映射信息等(跟前面我们自己实现的一样),添加映射:registration.addMapping(this.getServletMappings()),这个getServletMappings()留给我们自己来写

  • AbstractAnnotationConfigDispatcherServletInitializer

AbstractDispatcherServletInitializer的子类,注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext(),获取配置类,注册到注解的IOC容器AnnotationConfigWebApplicationContext中并返回
创建web的IOC容器:createServletApplicationContext(),获取配置类,注册到注解的IOC容器AnnotationConfigWebApplicationContext中并返回(跟上一步一模一样)

  • 剩下的三个待补充

小结

以注解方式来启动springMVC,继承 AbstractAnnotationConfigDispatcherServletInitializer,实现抽象方法,指定DispatcherServlet的配置信息

整合

上面,我们对springMVC的整合进行了分析,下面我们对其进行实现,继承AbstractAnnotationConfigDispatcherServletInitializer,代码如下

// web容器启动的时候创建对象,调用方法来初始化容器以及前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {// 获取根容器的配置类,(spring的配置文件),父容器@Overrideprotected Class<?>[] getRootConfigClasses() {// RootConfig为父容器,扫描除Controller的其他的组件return new Class[]{RootConfig.class};}// 获取web容器的配置类(springMVC的配置文件),子容器@Overrideprotected Class<?>[] getServletConfigClasses() {// AppConfig为子容器,只扫描Controllerreturn new Class[]{AppConfig.class};}// 获取DispatcherServlet的映射信息@Overrideprotected String[] getServletMappings() {// 拦截所有的请求,包括静态资源,不包括jsp文件// /*会拦截jsp文件,jsp页面是tomcat的jsp引擎解析的return new String[]{"/"};}
}

定制springMVC

在以前,我们整合springMVC时,需要写mvc.xml配置文件,配置我们的高级功能开启(<mvc:annotation-driven />)、拦截器(mvc:interceptors</mvc:interceptors>)、视图映射等,那现在不用配置文件了该怎么配置了,我们可以通过实现WebMvcConfigurer并使用@EnableWebMvc来进行配置,代码如下

@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {// 默认所有的页面都从/WEB-INF/ xxx.jspregistry.jsp();// 也可以自己配置registry.jsp("/WEB-INF/views/",".jsp");}@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {// 静态资源访问configurer.enable();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 配置拦截器registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");}
}

servlet3.0的异步请求处理

在servlet3.0以前,servlet采用Thread-Per-Request的方式处理请求,即每一次http请求都是由某一个线程从头到尾负责处理,如果一个请求需要进行IO操作,比如数据库访问、调用第三方服务接口等,那么其所对应的线程将同步的等待IO操作完成,而IO操作是非常慢的,此时的线程被占用着,不能被回收继续使用,在大并发情况下,就会带来严重的性能问题。为了解决这个问题,servlet3.0引入了异步处理,servlet3.1又引入了非阻塞IO来进一步增强异步处理的性能。下面我们将通过servlet方式和springMVC方式实现异步处理。

servlet实现

@WebServlet("/hello")
public class AsyncServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.支持异步请求处理asyncSupported=true// 2.开启异步模式System.out.println("主线程开始---" + Thread.currentThread() + "时间:" + System.currentTimeMillis());AsyncContext asyncContext = req.startAsync();// 3.业务逻辑进行异步处理asyncContext.start(() -> {try {System.out.println("副线程开始---" + Thread.currentThread() + "时间:" + System.currentTimeMillis());sayHello();asyncContext.complete();// 这里也可以获取到异步上下文,和上面的一样AsyncContext context = req.getAsyncContext();// 4.获取响应ServletResponse response = context.getResponse();response.getWriter().write("hello async...");System.out.println("副线程结束---" + Thread.currentThread() + "时间:" + System.currentTimeMillis());} catch (Exception e) {e.printStackTrace();}});System.out.println("主线程结束---" + Thread.currentThread() + "时间:" + System.currentTimeMillis());}private void sayHello() throws InterruptedException {System.out.println("processing..." + Thread.currentThread() + "时间:" + System.currentTimeMillis());Thread.sleep(3000);}
}

springMVC实现

参照官方文档,springMVC可以通过两种方式实现(返回值),一是Callable,二是DeferredResult,代码如下:
Callable实现

@RestController
public class AsyncController {@RequestMapping("/async01")public Callable<String> async01() {System.out.println("主线程开始---"+Thread.currentThread()+" 执行时间:"+System.currentTimeMillis());Callable<String> callable = new Callable<String>(){@Overridepublic String call() throws Exception {System.out.println("副线程开始---"+Thread.currentThread()+" 执行时间:"+System.currentTimeMillis());Thread.sleep(2000);System.out.println("副线程结束---"+Thread.currentThread()+" 执行时间:"+System.currentTimeMillis());return "async01执行";}};System.out.println("主线程结束---"+Thread.currentThread()+" 执行时间:"+System.currentTimeMillis());return callable;}
}

结果
>控制器返回Callable,Spring异步处理,将Callbale提交到TaskExecutor使用一个隔离线程进行执行,DispatcherServlet和所有的Filter退出web容器的线程,但是response保持打开状态,Callable返回结果,springMVC将请求重新派发给容器,恢复之前的处理,根据Callable返回的结果,springMVC继续进行视图渲染流程等

DeferredResult实现

在我们实际开发过程中,异步处理是这样来做的,如图,应用1开启一个请求,他需要由应用2来完成某些操作,这时,他会将消息放在消息中间件中(MQ、kafka等),应用2监听是否有消息,监听到就处理,处理完再将消息返回消息中间件,应用1也要监听,监听到就返回。

DeferredResult就能帮我们简单的实现上述过程,我们接收到不能及时处理的请求,就创建一个DeferredResult对象,然后直接返回该DeferredResult对象,当处理完时,拿到这个DeferredResult对象,调用setResult()方法,即可实现异步处理,代码如下

 private static Queue<DeferredResult> queue = new ConcurrentLinkedQueue();@RequestMapping("async02")public DeferredResult<Object> async02() {// 设置超时时间,超时返回提示消息(处理失败!)DeferredResult result = new DeferredResult(3000l,"处理失败!");save(result);return result;}private void save(DeferredResult result) {queue.add(result);}private DeferredResult get() {return queue.poll();}@RequestMapping("/isSuccess")public String isSuccess() {DeferredResult result = get();result.setResult("成功!");return "success";}

end…

到这里,spring注解驱动开发的学习就完结了,相信认真看完视频的同学收获还是挺多。学无止境,尤其是我们搞技术的。在这个内卷的时代,只有不断的提升自己,才能走得长远,加油吧,打工人!

spring注解驱动开发-10 Servlet3.0相关推荐

  1. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  2. SPRING注解驱动开发-雷神课程超详细笔记

    SPRING注解驱动开发-雷神课程超详细笔记 时间:2021-03-21 2022-04-06更新:最近翻起一年多前写的笔记复习,还是收获颇多,很多当时无法理解的知识现在慢慢能理解了,可能是工作一年的 ...

  3. Spring注解驱动开发第26讲——总有人让我给他讲讲@EnableAspectJAutoProxy注解

    @EnableAspectJAutoProxy注解 在配置类上添加@EnableAspectJAutoProxy注解,便能够开启注解版的AOP功能.也就是说,如果要使注解版的AOP功能起作用的话,那么 ...

  4. Spring注解驱动开发学习总结8:自动装配注解@Autowire、@Resource、@Inject

    Spring注解驱动开发学习总结8:自动装配注解@Autowire.@Resource.@Inject 1.自动装配@Autowire.@Resource.@Inject 1.1 构建bookDao ...

  5. spring注解驱动开发-6 Spring AOP实现原理

    Spring AOP实现原理 前言 1.@EnableAspectJAutoProxy注解原理 2.AnnotationAwareAspectJAutoProxyCreator 分析 1.分析前工作, ...

  6. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  7. 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解

    写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...

  8. spring注解驱动开发-5 Spring AOP实现

    Spring AOP实现 前言 AOP案例实现 1.编写目标类 2.编写切面类 3.编写配置类 4.编写测试类 end... 前言 AOP为Aspect Oriented Programming的缩写 ...

  9. spring注解驱动开发-8 Spring 扩展原理

    Spring 扩展原理 前言 BeanFactoryPostProcessor 测试实例编写 ExtConfig MyBeanFactoryPostProcessor ExtTest 源码分析 Bea ...

最新文章

  1. 干货 | 携程是如何做AB实验分流的
  2. qt on android 桌面鼠标事件,關於Qt on Android,程序安裝到手機,界面只占到一小部分。...
  3. jQuery必知必熟基础知识
  4. Python | 使用__del __()和__init __()实现析构函数和构造函数的示例
  5. oracle 换字段顺序,修改ORACLE的字段顺序
  6. 64匹马,8个赛道,最少多少次比赛找出最快的 4 匹马,以及对所有马进行排序
  7. 三星要带头了!考虑明年手机不送充电器
  8. sql语句截断_SQL Server中SQL截断和SQL删除语句之间的区别
  9. 第3章 别碰白块(《C和C++游戏趣味编程》配套教学视频)
  10. sql 整改措施 注入_记一次Sql注入 解决方案
  11. 硬,软连接,以及在windows中的用法
  12. 使用安卓手机自建 KMS 服务器激活Windows系统(任意安卓手机 无需 Root)
  13. 国内外有名的计算机视觉团队汇总
  14. ThreeJs顶点法向量光照计算
  15. Android系统如何修改默认打开程序
  16. Windows下swig安装与配置
  17. 正则表达式校验邮箱号、手机号、身份证号码等等
  18. 微信小程序前端设计,以北京动物园为例
  19. Oracle数据库期末考试范围题
  20. encodeURI和decodeURI转码和解码

热门文章

  1. 连续忙碌了好一阵,太想出去透透气了,最近有假想去湖南玩玩,不知道有哪些好玩的景点?有哪些旅游线路推荐?~在线等哦
  2. iOS-系统相关参数(版本、机型设备等)
  3. 解决Windows 照片查看器无法显示此图片,因为计算机上的可用内存可能不足
  4. 集装箱堆场建模调度计划(建模阶段)
  5. springboot使用华为OBS上传下载文件详解
  6. linux 使用p7zip 解压 zip大文件
  7. 使用Python tkinter写一个简单的按键游戏
  8. PMT函数(5个参数)使用java代码实现
  9. excel中的FV函数、PMT函数与PV函数的java实现
  10. cherrytree安装出现see the logfile for detaile错误的处理