当前位置: 首页 > news >正文

吊打面试官系列之--吃透Spring ioc 和 aop (中)

目录

Spring

SpringBean的五个作用域

SpringBean的生命周期

创建过程

 销毁过程

AOP的介绍和使用

AOP的介绍

AOP的三种织入方式

操作讲解

AOP的主要名词概念 

Advice的种类


Spring

在上一篇实在过后,大家最好可以去查看一下SpringIOC refresh的源码以及SpringIOC getBean的

源码加深理解。后续为大家补充...

SpringBean的五个作用域

通过上面的解释很容易得出singleton适合无状态的Bean,而prototype则适合有状态的Bean

此外呢,将Bean设置为prototype作用域需要三思而后行,毕竟频繁创建和销毁Bean是有明显的销

的。

此外,如果是web容器,则支持另外三种作用域

第一种是request,它会为每个HTTP请求创建一个单独的Bean实例。

第二个是session,它代表Bean的作用域是session范围的,即该Bean是专属于某个session使用

的。

第三个是global session,在一个全局的http session中,容器会返回该Bean的同一个实例

什么是全局http session呢?这是polly规范提出的概念。该session被所有构成某个polly web应用的

各种不同的policy所共享。因此,仅在使用poly context的时候呢,是有效的poly,也是基于web主

线poly容器呢,是使用jsr 286 poly规范的运行时环境。

SpringBean的生命周期

创建过程

 

我们了解到容器创建后呢,会解析并创建出来bean。spring Bean的生命周期比较复杂,只能靠去

琢磨相关的源码或者死记硬背下来。

既然Bean是spring的Bean,则Bean的生命周期是由容器来管理的。主要在两个时期,创建和销

毁。

首先创建并会经过一系列的步骤,主要包括第一实例化Bean对象以及设置Bean属性。

第二步,就是如果我们通过各种aware接口声明了依赖关系,则会注入Bean对容器基础设施层面的

依赖。Awear接口是为了能够感知到自身的一些属性,那这是什么意思呢?容器管理的Bean一般

不需要了解容器的状态和直接使用容器。但是在某些情况下,是需要在Bean中直接对IOC容器进行

操作的,这时候就需要在Bean中设定对容器的感知。spring IOC容器也提供了该功能,它是通过特

定的awear接口来完成的,就拿BeanNameAwear来讲,它可以在Bean中得到它在IOC容器中的

Bean的实例的名字。

紧接着,会调用BeanPosprocessor的前置初始化方法PostProcessBeforeInitialization 。主要作用

是在spring完成实例化之后呢,对spring容器实例化的Bean添加一些自定义的处理逻辑

第四步,如果实现了InitializingBean接口,则会调用afterpropertiesSet方法,多一些属性被设置之

后的自定义的事情。

之后,会调用Bean自身定义的init方法去做一些初始化相关的工作,

在第六步会调用BeanPostProcessor的后置初始化方法postProcessAfterInitialization,去做一些

Bean实例初始化之后的自定义工作。 

这个就是跟我们的aop相关的了,那在Bean完成创建了之后,就可以在应用里面使用这个Bean

了。

 销毁过程

当Bean不再被用的时候便会来到清理的阶段。那首先如果并实现了DisposableBean这个接口,那

么就会调用接口里的destroy的方法。其次呢,如果这个bin的spring配置中配置了destroy-method

的属性,则会调用其配置的销毁的方法。


AOP的介绍和使用

AOP的介绍

 spring 另外一个比较重要的部分就是AOP即面向切面编程

软件工程有一个基本原则,叫做关注点分离及concern separation。通俗的理解就是不同的问题

交给不同的部分去解决。每部分专注于解决自己的问题。

面向全面编程Aspect oriented programming 即AOP,其实就是一种关注点分离的技术,我们的代

码主要就是实现某种特定的业务逻辑。但是我们往往不能专注于业务逻辑,比如我们写业务逻辑代

码的同时呢,还要写事务管理,缓存日志等等,通用化的功能,而且每个通用的功能都要和这些业

务功能混在一起。

为了将业务功能的关注点和通用化功能的关注点分离开来,就出现了AOP的技术。

这些通用化功能的代码实现对应的就是我们说的切面即Aspect。

代码分开的同时,我们如何保证功能的完整性呢?

你的业务功能依然需要有事务和日志等特性,即切面最终需要合并到业务功能中。


那这是怎么做到的呢?这里就涉及aop的底层技术了,有三种方式。

AOP的三种织入方式

1.编译时织入即在代码编译时把切面代码融合进来,生成完整功能的JAVA字节码,这就需要特殊

的JAVA编译器了,AspectJ属于这一类。

2.在类加载的时候去织入,在JAVA字节码加载的时候把前面的字节码给融合进来,这同样需要特

殊的类加载器,AspectJ j和AspectWerkz都是能够做到的。

3.运行时织入,通过动态代理的方式,调用切面代码增强业务功能,spring采用的正是这种方式,

动态代理会有性能上的开销,但是好处就是不需要特殊的编译器和类加载器,按照写普通JAVA程

序的方式来进行就可以了。


操作讲解

咱们来举个简单的例子,最常见的情况就是我们会对web应用里面的请求都去做日志方面的记录。

不管什么样的请求,我们都会去记录它的一些通用信息,比如说这个请求的IP来源是什么,请求的

URL是什么,以及记录下当时返回的信息是什么。

package com.imooc.framework.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    @ResponseBody
    public String hello(){
        String sentence = "Hello World";
        System.out.println(sentence);
        return sentence;
    }
}

加入一个RequestMapping,来指定方法的访问路径,路径的value,就是路径的past的值,我

们叫做hello。那我们接收请求的用method是RequestMethod.GET。

 接受请求返回一个字符串   @ResponseBody 

在控制台里面打印一下sentence之后,再将sentence就respond响应回去。

咱们启动URL启动,完成了之后,去访问一下,默认呢它是localhost 8080。

 那输入这个url了之后呢,就会发现我们能够成功的往程序发送get请求,并且调用controller里面

的hello方法。

此时如果我们要给这个请求呢,去做一下日志方面的记录,那么只需要在return前面呢,去加一些

日志的记录即可。但是,随着应用的不断的迭代,controller里面的方法会越来越多,或者说我们

不仅只有一个controller,还会有n个不同模块的controller。此时如果给每一个controller的每一个方

法都强行写上相应的日志,记录的逻辑是不现实的,因为这样子做会使得维护成本变得很高


        因此为了优雅的完成这一请求,我们便引入了aop。aop其实是比较简单易用的,我们只需要

处理好3w即可,即what where and when。what指的是切面。首先,我们将记录请求日志这个功能

的代码分离出来,做成一个切面。

将它命名为RequestLogAspect,这个切面里面我们就要告诉他切入的点在哪里,因此@Pointcut

execution(方法的属性为public然后 * 表示返回值是任意的,package在

come.imook.framework.web底下的所有的方法,我们都去打日志,那么就是..*.*.(..)..这点就表明

的是,带有任意参数,任何名字的方法我们都去拦截,去打日志。

接着就是where,在什么地方进行切入?我们选择在方法执行前,@Before+我们要拦截的方法

weblog()

@Aspect注解,是将一个JAVA类定义为切面类。

@Pointcut,定义了一个切入点,可以是正则表达式的。

这里我们发现报错,因为我们没有引入相关的Java包依赖。

先前,我们只在pom里面引入了web的Java包。就是我们在之前创建项目的时候呢,勾选了web,

没有用aop,因此在这里我们需要加入aop的依赖。

在pom.xml文件中:


处理what和where了之后呢,就需要处理when的事情,也就是根据需要在切入点不同位置中切入

内容,我们使用@Before在切入点开始处去切入内容。而使用@AfterReturning,则在切入点return

内容之后再切入。那我们可以用AfterReturning来对返回值,做一些加工处理。

package com.imooc.framework.ioc.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.http.HttpServletRequest;

import static javafx.scene.input.KeyCode.H;

@Aspect
@Component
public class RequestLogAspect {
    private static final Logger logger =
            LoggerFactory.getLogger(RequestLogAspect.class);
    @Pointcut("execution (public * com.imooc.framework.web..*.*(..))")
    public void webLog(){}


    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint){
        //接受到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //记录下请求信息
        logger.info("URL :" + request.getRequestURI().toString());
        logger.info("IP :" + request.getRemoteAddr());

    }
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturing(Object ret){
        //处理完请求,返回内容
        logger.info("RESPONDS :" + ret);
    }

}

接下来呢,咱们可以再次启动应用,应用启动完成了之后呢,咱们再次去访问这个hello的url。

在console就会发现呢,咱们的日志里面已经记录了相关的URL , IP,还有response的内容。

 


AOP的主要名词概念 

首先是切面Aspect,指的就是通用功能的代码实现。比如我们上面演示的日志切面呢,其实就是一

个普通的JAVA类requestLogAspect。

第二个是Target即我们的目标对象,就是要被织入切面的对象。例子中的HelloController,就是我

们的Target。

第三个是Join Point即可切入的点,指的是可以作为切入点的机会,所有方法的执行处都可以作为

aop的切入点,如前面的hello方法。

第四个是Pointcut切入点,只要定义通知应该切入到什么地方,spring支持的切入点就是方法调

用。Pointcut负责具体定义aspect被应用在哪些Join Point中。切入点的定义可以使用正则表达式。

第五个是advice即通知。切面是一个类,而通知就是类里面的方法以及这个方法如何织入到目标

方法的方式

最后一个是织入Weaving, AOP实现的过程中,即将切面应用到实际对象,从而创建一个新的代

理对象的过程。对于spring来说,就是初始化context中的对象时完成织入操作


我们的例子中只展示了两类通知,根据织入到目标方式的不同呢,一共可以分为五种。

Advice的种类

 具体不再做过多讲解...

相关文章:

  • Matlab制作GUI
  • Spring Data JPA或Spring Data JDBC中Like和Containing区别
  • SpringMVC04之JSON和全局异常处理
  • <C++> list容器本质|常用接口|自定义排序规则
  • 【Matlab】简单控制系统建模(控制系统工具箱)
  • 设计模式——模板模式
  • 倍投技巧 - 凯利公式教你如何用正确的方法投资
  • SpringBoot restful api接口设计
  • 软件测试高薪“骗局”软件测试入门就月薪过万,还包就业。别再上当受骗了、清醒点吧
  • 【DP 动态规划 | 精选推荐】持续更新
  • 专利的要求-需要什么条件?
  • Google Earth Engine (GEE)——GEE制作gif动态图(北京市为例)
  • Spring-Framework-ioc-1
  • Vue 动态换肤
  • 从零到一搭建基础架构-玩转maven依赖版本管理
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • Angular数据绑定机制
  • exif信息对照
  • Git的一些常用操作
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Linux CTF 逆向入门
  • magento2项目上线注意事项
  • Phpstorm怎样批量删除空行?
  • Spark学习笔记之相关记录
  • Web设计流程优化:网页效果图设计新思路
  • 从tcpdump抓包看TCP/IP协议
  • 官方解决所有 npm 全局安装权限问题
  • 前言-如何学习区块链
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • - 转 Ext2.0 form使用实例
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (windows2012共享文件夹和防火墙设置
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (四)JPA - JQPL 实现增删改查
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)负载均衡,回话保持,cookie
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • 、写入Shellcode到注册表上线
  • .jks文件(JAVA KeyStore)
  • .net core 控制台应用程序读取配置文件app.config
  • .net 后台导出excel ,word
  • .net 设置默认首页
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .net反编译的九款神器
  • .net访问oracle数据库性能问题
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • @Resource和@Autowired的区别
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝