AOP+SpringBoot原理

AOP概述:

AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程

场景:记录操作日志、权限控制、事务管理等。案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时

实现:动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程

优势:
  1. 代码无侵入
  2. 减少重复代码
  3. 提高开发效率
  4. 维护方便
步骤:
  1. 导入依赖:在pom.xml 中导入AOP的依赖
        <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    
  2. 编写AOP程序:针对于特定方法根据业务需要进行编程(加 @Aspect 注解声明为AOP类)
    @Slf4j
    @Component
    @Aspect //AOP类
    public class TimeAspect {
        @Around("execution(* com.itheima.service.*.*(..))") //切入点表达式
        //service.*.*(..)表示 service下的所有接口或者类中的所有方法
        public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
            //1. 记录开始时间
            long begin = System.currentTimeMillis();
            //2. 调用原始方法运行
            Object result = joinPoint.proceed();
            //3. 记录结束时间, 计算方法执行耗时
            long end = System.currentTimeMillis();
            log.info(joinPoint.getSignature()+"方法执行耗时: {}ms", end-begin);
            return result;
        }
    }
    

AOP核心概念:

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指那些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点),被 @Aspect 修饰的类一般成为切面类
  • 目标对象:Target,通知所应用的对象

AOP进阶:

通知类型:
  1. @Around :环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  2. @Before :前置通知,此注解标注的通知方法在目标方法前被执行
  3. @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  4. @AfterReturning :返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  5. @AfterThrowing :异常后通知,此注解标注的通知方法发生异常后执行
注意事项:
  1. @Around 环绕通知需要自己调用ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
  2. @Around 环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值
@Pointcut :

该注解的作用是将公共的切点表达式抽取出来,需要用到是引用该切点表达式即可

    @Pointcut("execution(* com.itheima.service.*.*(..))")
    private void pt(){}

    @Before("pt()")
    public void Before(){
        log.info("before...")
    }

注意:如果pt()是私有的,则只能在当前切面类中引用,想在其他切面类中引用,必须改为public

通知顺序:

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行

  1. 不同切面类中,默认按照切面类的类名字母排序

​ @Before :字母排名靠前的先执行

​ @After :字母排名靠前的后执行

  1. 用 @Order(数字) 加在切面类上来控制顺序

​ @Before :数字小的先执行

​ @After :数字小的后执行

    @Slf4j
    @Component
    @Aspect //AOP类
    @Order(2)
    public class TimeAspect {}

切入点表达式:

描述切入点方法的一种表达式

作用:主要用来决定项目中的哪些方法需要加入通知

常见形式

    @Before("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
    public void Before(JoinPoint joinPoint){}

    @Before("@annotation(com.itheima.anno.Log)")
    public void Before(){}  

    1、execution(……):根据方法的签名来匹配
        execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
        其中带?的表示可以省略的部分
            访问修饰符 :可省略(比如:public、protrcted)
            包名.类名 :可省略,但不建议省略
            throws 异常 :可省略(注意是方法上声明抛出的异常,不是时机抛出的异常)
        可以使用通配符描述切入点:
            * :单个单独的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
            execution(*com.*.service.*.update*(*))
            . :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
            execution(*com.itheima..DeptService.*(..))
        注意事项:
            根据业务需要,可以使用且(&&)、或(||)、非(!)来组合比较复杂的切入点表达式
        书写建议:
            1、所有业务方法名在命名时尽量规范,方便切入带你表达式快速匹配。
              如:查询类方法都是find开头,更新类方法都是update开头
            2、描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
            3、在满足业务需要的前提下,尽量缩小切入点的匹配范围。
              如:包名匹配尽量不使用.. ,使用 * 匹配单个包

    2、@annotation(……):根据注解匹配,……为注解全类名
        ---------------------------------------------------- 
        @Retention(RetentionPolicy.RUNTIME) //描述该注解什么时候生效 .RUNTIME运行时生效
        @Target(ElementType.METHOD) //描述该注解可以作用在什么地方 .METHOD作用在方法上
        public @interface MyLog{}
        ----------------------------------------------------
        @Pointcut("@annotation(com.itheima.aop.MyLog)")
        private void pt(){}
        @Before("pt")
        public void Before(){
            log.info("before...")
        }
        ----------------------------------------------------
        @MyLog
        @Override
        public void delete(){……}    
        ----------------------------------------------------

连接点:

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等

  • 对于 @Aound 通知,获取连接点信息只能使用 ProceedingJoinPoint
  • 对于其他四种通知,获取连接点信息只能使用 JoinPoint ,它是 ProceedingJoinPoint 的父类型
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("MyAspect8 around before ...");
        //1. 获取 目标对象的类名 .
        String className = joinPoint.getTarget().getClass().getName();
        log.info("目标对象的类名:{}", className);
        //2. 获取 目标方法的方法名 .
        String methodName = joinPoint.getSignature().getName();
        log.info("目标方法的方法名: {}",methodName);
        //3. 获取 目标方法运行时传入的参数 .
        Object[] args = joinPoint.getArgs();
        log.info("目标方法运行时传入的参数: {}", Arrays.toString(args));
        //4. 放行 目标方法执行 .
        Object result = joinPoint.proceed();
        //5. 获取 目标方法运行的返回值 .
        log.info("目标方法运行的返回值: {}",result);
        log.info("MyAspect8 around after ...");
        return result;
    }
    

SpringBoot原理:

配置优先级:

properties > yml > yaml
虽然springboot支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置(yml是主流)
Springboot除了支持配置文件属性配置,还支持Java系统属性和命令行参的方式进行属性配置
Java系统属性:-Dserver.port=9000     -D[key]=[value]
命令行参数:--server.port=10010       --[key]=[value]
优先级:命令行参数 > Java系统属性 > properties > yml > yaml

如果不是在idea中:
    1、执行maven打包指令package生成~.jar
    2、执行java命令,运行jar包
        java [Java系统属性] -jar ~.jar [命令行参数]
        java -Dserver.port=9000 -jar ~.jar --server.port=10010

Bean管理:

获取bean:
获取bean:
    默认情况下,Spring项目启动时,会把bean都创建好放在IOC容器中,如果想要主动获取这些bean,可以通过如下方式:
    上述说的[Spring项目启动时,会把bean都创建好]还会受到作用域及延迟初始化影响,
    这里主要针对于默认的单例非延迟加载的bean而言
    1、根据name获取bean:
        Object getBean(String name)
    2、根据类型获取bean:
        <T> T getBean(Class<T> requiredType)
    3、根据name获取bean(带类型转换):
        <T> T getBean(String name,Class<T> requiredType)
    public void testGetBean(){
        //1、根据bean的名称获取
        DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
        System.out.println(bean1);
        //2、根据bean的类型获取
        DeptController bean2 = applicationContext.getBean(DeptController.class);
        System.out.println(bean2);
        //3、根据bean的名称 及 类型获取
        DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
        System.out.println(bean3);
    }   

bean作用域:

Spring支持5种作用域,后3种在web环境才生效:

  1. *singleton :容器内同 名称 的 bean 只有一个实例(默认的单例)
  2. *prototype :每次使用该 bean 时会创建新的实例(非单例)
  3. request :每个请求范围内会创建新的实例(web环境中,了解)
  4. session :每个会话范围内创建新的实例(web环境中,了解)
  5. application:每个应用范围内会创建新的实例(web环境中,了解)
  • ​ 可以通过 @Scope 注解来进行配置作用域:
    @Scope("prototype") //非单例
    @RestController
    @RequestMapping("/depts")
    public class DeptController {}
    
注意事项:
  1. 默认singleton的bean,在容器启动时被创建,可以使用 @Lazy 注解来延迟初始化(延迟到第一次使用时)
  2. prototype 的 bean ,每一次使用该bean的时候都会创建一个新的实例
  3. 实际开发当中,绝大部分的bean是单例的,也就是说绝大部分bean不需要配置scope属性

第三方bean:

  • @Bean:如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到@Bean 注解。
方法1:在启动类中(不建议):
    //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
    public SAXReader saxReader(){
        return new SAXReader();
    }
方法2:单独定义一个配置类
    @Configuration //配置类
    public class CommonConfig {
        //声明第三方bean
        @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
              //通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
        public SAXReader reader(DeptService deptService){
            System.out.println(deptService);
            return new SAXReader();
        }
    }
注意事项:
  1. 通过@Bean 注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名
  2. 如果第三方bean需要依赖其他bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配
创作不易!转载请注明作者及文章链接或作者博客链接——
- 作者:pidanxia
- 链接:https://pidanxia.ink
(链接可为:**文章链接**或者**作者博客链接**)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇