spring系列之————Aop

spring系列之————Aop

我们最熟悉编程思想oop

简介:
面向对象编程,这是我们最为熟悉的一种编程模式,软件作为对现实事务、工作的一种模拟,我们自然的可以想到将现实中的各种角色抽象成一个一个的类。例如:订餐系统有用户,骑手,商家等类。
问题
我们可以思考一下类似于日志打印、权限、事务这些工作,他们有什么特点?如果用oop的编程方式有什么样的问题?
我们用一张图来看一下:
在这里插入图片描述

这些功能在很多地方都会出现,新增用户,新增商品…等等地方,并且在不同的地方只有较小的区别,那么我们就需要在商品类、用户类各种类中写相同的代码,这就出现大量的冗余。
解决方案
我们通过面向对象的知识不难想到我们可以将公共的部分抽离出来,写成共有类,在具体的业务中进行调研就可以了。那么我们思考一下这样又有什么问题呢?首先就是冗余,我们已经解决,其次就是维护性,我们写成了一个公共类,就可以直接维护这个类了,但有一个问题就是耦合的问题,当我们修改维护日志类时,各个业务类对他的调用也有可能需要修改;况且各个业务类写调用代码本身也是挺麻烦的(懒是没有止境的)这个时候就要引入我们的Aop了。

一个全新的结构图

在这里插入图片描述
将日志、权限这种业务单独写成一个叫切面的东西,而具体的业务也不需要调用他们,他们自己会找准时机自己切入,这样一来所有的问题也都迎刃而解了,但是可能现在我们对切面、切入不太了解,下面将具体讲解Aop.

Aop简介:

面向切面编程,这是对面向对象编程的一种补充,一个对象可以完成与自身相关的很多事情,但是有一些事情是很多对象都会完成的,比如日志打印,比如下单后的库存删减这些工作并非一个对象需要完成,那么这个时候我们就可以通过面向切面编程进行动态生成的方式实现代码复用。

基础概念:

切面(Aspect):就是我们的公共事务,大家共有的一个操作,我们将其抽象为一个切面。
连接点(Joint point):在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,这些点都可以成为切点,这是一个抽象的概念。
切点(Pointcut):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
增强(Advice):定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
目标对象(Target):织入 Advice 的目标对象.。
织入:如上所说我们使用Aop的目的就是动态的实现代码的复用,这里的动态实现就相当于把需要复用的那段代码如同织毛衣一样织入到原代码中。

运用场景:

Authentication 权限、Caching 缓存、Context passing 内容传递、Error handling 错误处理、Lazy loading 懒加载、Debugging 调试、logging, tracing, profiling and monitoring 记录跟踪 优化 校准、Performance optimization 性能优化、Persistence 持久化、Resource pooling 资源池、Synchronization 同步、Transactions 事务

运用场景实例:

1)、介绍:
有关日志打印的实例,我们的日志打印就是一个典型的例子其实几乎所有的类都需要日志打印,我们也没有必要在每个类中都去写日志打印,原因如下:
①一些日志打印是完全可以通用的例如:数据的插入无论什么插入我们就是返回表名以及插入内容打印日志(after)所有没必要每个类写一遍。
②方便根据日志要求变更对日志打印的进行修改、管理。
2)设计介绍:
①数据库:
在这里插入图片描述
②结构功能介绍:
就是普通的ssm框架模式,controller、service、Dao,功能包含无参数全表查询、数据插入、修改、删除。(由于较为简单故具体代码省略)。
切面实现代码:


```java
 import org.apache.log4j.Logger;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.*;
 import org.springframework.stereotype.Component;
 import java.util.Arrays;

   /**
   * 日志切面
   */
@Aspect
@Component
public class LogAspect {
    /**
      * 操作日志文件名
      */
      private static final String OPERATION_LOG_NAME = "operationLog";
      private static final String LOG_FORMATTER = "%s.%s - %s";
    Logger log = Logger.getLogger(OPERATION_LOG_NAME);
    /**
      * 对查询方法记录日志的切点
      */
            @Pointcut("execution(* com.manager..*.*Controller.query*(..))")
    public void query(){}

            /**
      * 对新增方法记录日志的切点
      */
            @Pointcut("execution(* com.manager..*.*Controller.add*(..))")
    public void add(){}

            /**
      * 对修改方法记录日志的切点
      */
            @Pointcut("execution(* com.manager..*.*Controller.update*(..))")
    public void update(){}

            /**
     * 对删除方法记录日志的切点
      */
           @Pointcut("execution(* com.manager..*.*Controller.delete*(..))")
    public void delete(){}

            @AfterReturning(value = "query()", returning = "rvt")
    public void queryLog(JoinPoint joinPoint, Object rvt) {
                    String className = joinPoint.getTarget().getClass().getName();
                    String methodName = joinPoint.getSignature().getName();
                    String returnResult = rvt.toString();
                    log.info(String.format(LOG_FORMATTER, className, methodName, returnResult));
                }

            @Before("add()")
    public void addLog(JoinPoint joinPoint) {
                    String className = joinPoint.getTarget().getClass().getName();
                    String methodName = joinPoint.getSignature().getName();
                    Object[] params = joinPoint.getArgs();
                    log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
                }

           @Before("update()")
    public void updateLog(JoinPoint joinPoint) {
                    String className = joinPoint.getTarget().getClass().getName();
                    String methodName = joinPoint.getSignature().getName();
                    Object[] params = joinPoint.getArgs();
                    log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
                }

            @Before("delete()")
    public void deleteLog(JoinPoint joinPoint) {
                    String className = joinPoint.getTarget().getClass().getName();
                    String methodName = joinPoint.getSignature().getName();
                    Object[] params = joinPoint.getArgs();
                    log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
                }
}

@Ascept注解实现切面编程:

1)标签详解:
①@Aspect:
在类上使用 @Aspect 注解 使之成为切面类,和@Component 注解连用,把切面类加入到IOC容器中。
②@Pointcut(“execution/@annotation ”)
1)这是定义切点的一个注解:里面的execution表达式非常关键它的格式如下:

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
修饰符模式和异常模式都是可选的。
中间三个为必写参数:
返回值类型(两个选择* 代表所有参数类型,或者是写具体参数)
方法名参数(由于方法是在包下的类中,同时方法名参数并不要求唯一,而确定唯一一个方法名参数的必要条件有三(包名、类名、方法名),既然不需要唯一那么这三个参数就可以自由选择
可分为三种:
通过方法签名定义切点:
execution(public * (…))匹配所有目标类的public方法
execution(
To(…))匹配目标类所有以To为后缀的方法。
通过类定义切点:
execution(com.baobaotao.Waiter.(…)) 匹配Waiter接口的所有方法
execution(com.baobaotao.Waiter+.(…)) 匹配Waiter接口及其所有实现类的方法
通过类包定义切点:
在类名模式串中,“.
”表示包下的所有类,而“…”表示包、子孙包下的所有类。
execution(
com.baobaotao.(…)) 匹配com.baobaotao包下所有类的所有方法;
execution(
com.baobaotao…(…)) 匹配com.baobaotao包、子孙包下所有类的所有方法
execution(
com…*.Dao.find(…)) 匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。)
参数类型(两个选择…代表任意参数组合,或者是真实的参数情况进行约束)
2)@annotation(com.mu.demo.annotation.PermissionAnnotation):
这个表达式是规定的注解切点,即被这个注解修饰的地方为切点。
③@Before
前置处理,在切点方法执行之前进行处理
④@After
后置处理,分为三类AfterRunning(正常执行结束后)、AfterThrowing(运行异常出现后)、
Afterfinally(无论运行成功与否都会执行)
⑤@Order(1)
当一个切点有多个切面时,具体的优先级就是用这个标签来确定的,括号中的数字越小优先级越高。

JoinPoint 对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs(); 获取传入目标方法的参数对象
Object getTarget(); 获取被代理的对象
Object getThis(); 获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
两个方法.

注:

aop的运用实例有很多,本文只实现了一个关于日志的实例。

最后一句话:

看到此处便是缘,请您再听我一言;
新生博主甚是难,还请原谅把你烦;
博文写了没人看,博主这活没法干;
看了不评就走人,他人怎知行不行;
看了评论又推荐,博主心里笑开颜。
ps:就想皮一下,但还是希望你能指出错误,如果觉得可以夸奖一下就更好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值