四阶段--springboot工程中AOP的应用实践

一 AOP介绍

在这里插入图片描述

 

1  为什么需要AOP

在不修改源代码的基础上对对象功能进行拓展

例如现有一个公告(通知)业务接口及实现:

public interface NoticeService{
    int deleteById(Integer…ids);
}

public class NoticeServiceImpl implements NoticeService{
   public int deleteById(Integer…ids){
       System.out.println(Arrays.toString(ids));
       return 0 ;
   }
}

需求:基于OCP(开闭原则-对扩展开放,对修改关闭)设计方式对NoticeServiceImpl类的功能进行扩展,例如在deleteById业务方法执行之前和之后输出一下系统时间.

1.1 解决方式1: 基于继承方式实现其功能扩展

public class CglibLogNoticeService extends NoticeServiceImpl{
   public int deleteById(Integer…ids){
       System.out.println("Start:"+System.currentTimeMillis());
       Int rows=super.deleteById(ids);
       System.out.println("After:"+System.currentTimeMillis());
       return rows;
    }
}

测试类:

public class NoticeServiceTests{
     public static void main(String[] args){
         NoticeService ns=new CglibLogNoticeService();//多态对象
         ns.deleteById(10,20,30);//调用父类的方法
     }
}

但是,基于继承方式实现功能扩展,代码简单,容器理解,但是不够灵活,耦合性比较强

1.2 解决方案2: 基于组合方式实现其功能扩展

public class JdkLogNoticeService implements NoticeService{
      private NoticeService noticeService;//has a 
      public  JdkLogNoticeService(NoticeService noticeService){
        this.noticeService=noticeService;
      }
     public int deleteById(Integer…ids){
         System.out.println("Start:"+System.currentTimeMillis());
         int rows=this.noticeService.deleteById(ids);
         System.out.println("After:"+System.currentTimeMillis());
         return rows;
      }
}

测试类编辑:

public class NoticeServiceTests{
     public static void main(String[] args){
         NoticeService ns=
         new JdkLogNoticeService(new NoticeServiceImpl());//装饰模式--功能增强
         ns.deleteById(10,20);
     }
}

但是,基于组合方式实现功能扩展,代码比较灵活,耦合低,稳定性强,但理解相对比较困难.

1.3 总结

无论是继承,还是组合都是基于OCP方式实现了对象功能扩展,都有相应的优缺点,并且我们都要自己去写这些子类或兄弟类,在这些类中调用目标对象(父类或兄弟类对象)的方法以及扩展业务逻辑.对于这样的模板代码我们能否进行简化呢?例如.由框架实现其共性(创建目录类型的子类类型或兄弟类型),特性交给用户自己实现,这种方法叫做代理.

2 AOP定义

AOP(Aspect Orient Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。

实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(在对象运行时动态织入一些扩展功能或控制对象执行.

在这里插入图片描述

AOP 与 OOP 字面意思相近,但其实两者完全是面向不同领域的设计思想。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面的运行期代理方式,理解为一个动态过程,可以在对象运行时动态织入一些扩展功能或控制对象执行。

3  AOP实现的原理

动态代理对象: AOP可以在系统启动时为目标类型创建子类或兄弟类型对象

在这里插入图片描述

 为目标类型(XxxServiceImpl)创建其代理对象方式有两种:

  1. 借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口.
  2. 借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.目前使用此方法,因为controller没有实现某个service接口

4  AOP下的知识点

  1. 切面(aspect): 横切面对象,一般为一个具体类对象。
  2. 切入点(pointcut):定义了切入扩展业务逻辑的位置(哪些方法运行时切入扩展业务),一般会通过表达式进行相关定义,一个切面中可以定义多个切入点。
  3. 通知(Advice): 内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知(在切面的某个特定位置上执行的动作(扩展功能)。
  4. 连接点(joinpoint):程序执行过程中,封装了某个正在执行的目标方法信息的对象,可以通过此对象获取具体的目标方法信息,甚至去调用目标方法

描述:将机场的一个安检口理解为连接点,多个安检口为切入点,安全检查过程看成是 通知

二 Spring AOP 入门案例

1 业务需求

在项目中定义一个日志切面,通过切面中的通知方法为目标业务对象做日志功能增强。

2 添加AOP依赖

  <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
   </dependency>

说明:基于此依赖spring可以整合AspectJ框架快速完成AOP的基本实现。AspectJ 是一个面向切面的框架,他定义了 AOP 的一些语法,有一个专门的字节码生成器来生成遵守 java 规范的 class 文件

3 切面对象的设计

通过设计切面对象,为目标业务方法做功能增强

3.1  创建自定义注解类型,应用于切入点表达式的定义

package com.jt.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**自定义 注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
    String operation() default "";
}

3.2 创建切面对象,用于做日志业务增强

package com.jt.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Aspect注解描述的类称为切面对象
 * 1.切入点(在哪些方法执行时,进行功能扩展-锦上)
 * 2.通知(所有的扩展逻辑都会写到通过方法中--添花)
 * */
@Aspect
@Component
public class LogAspect {
    /**
     * 通过@Pointcut注解定义切入点,但切入点的定义需要有一个表达式,写法有多种,比较常用
     * 有注解方式的表达式(@annotation(我自己定义的注解))
     * 当使用自己写的注解方法进行描述时,这个方法就是切入点,在这个方法上要锦上添花*/
    @Pointcut("@annotation(com.jt.aop.RequiredLog)")
    public void doLog(){}//这个方法没有意义,主要用于承载切入点
    /**
     * Around通知,在此通知内部可以定义我们扩展业务逻辑,还可以调用目标执行链
     * @param joinPoint 连接点(通知方法与目标方法的连接点对象),ProceedingJoinPoint
     *    类型的连接点只能应用在Around通知方法中
     * @return
     * @throws Throwable
     * */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before:"+System.currentTimeMillis());
        Object result = joinPoint.proceed();//执行链(其他切面,目标对象方法)
        System.out.println("After:"+System.currentTimeMillis());
        return result;
    }
}

3.3 时间拦截器,限制访问时间

package com.jt.web.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalTime;

public class TimeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,Object handle){
        System.out.println("==preHandler==");
        LocalTime now =LocalTime.now();//JDK8中的时间对象
        int hour=now.getHour();//获取当前时间对应小时
        System.out.println("hour="+hour);
        if (hour<=6||hour>=23)
            throw new RuntimeException("请在6~23点进行访问");
        return true;//false表示请求到此结束,true表示执行链中的下一个对象
    }
}

3.4 创建控制层,获取请求调用代理对象并响应

package com.jt.web.controller;

import com.jt.aop.RequiredLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo/")
public class DemoController {//-->AOP-->Controller-->AOP-->Service-->Dao
    @RequiredLog//由此注解描述的方法就是切入点方法
    @GetMapping
    public String doSayHello(){
        System.out.println("==doSayHello==");
        String result = "hello spring";
        return result;
    }
}

3.5 url访问页面,测试aop执行步骤

 后台步骤:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值