SpringAOP

一、为什么使用AOP

Aop是面向对象的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

Aop的作用:在程序运行期间,不修改源码对已有方法进行增强。

Aop优势:减少重复代码、提高开发效率、维护方便

AOP的实现方式:使用动态代理技术

二、创建AOP过程

2.1新建Maven项目

2.2添加pom文件依赖

<dependencies>
    <!--spring坐标-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.9.RELEASE</version>
    </dependency>
    <!--解析切入点表达式-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.9.RELEASE</version>
    </dependency>
</dependencies>

刷新pom文件

2.3创建SpringConfig文件

package com.test.configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
//用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解

@ComponentScan({"com"})//用于指定 spring 在初始化容器时要扫描的包
@EnableAspectJAutoProxy//开启动态代理
public class SpringConfig {
}

2.4创建UserServer

package com.test.service;

import org.springframework.stereotype.Service;

@Service
public class UserServer {
    public void saveUser(){
        System.out.println("保存用户");
    }
    public void deleteUser(){
        System.out.println("删除用户");
    }
    public void updateUser(){
        System.out.println("更新用户");
    }

2.5创建切面类

package com.test.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component//交给ioc容器
@Aspect//定义为切面类
public class LoggerAspect {
    @Pointcut("execution(* com.test.*.*.*(..))")
    public void pointcut() {
    }
    @Before("pointcut()")
    public void doBefore(){
        System.out.println("**********运行前面记录日志**********");
    }
    @ @AfterReturning("pointcut()")
    public void doAfter(){

        System.out.println("**********运行后面记录日志**********");
    }
}

@Aspect注解会将此类当做是切面类

定义为切面类后,在其中创建一个切面方法,其中@Pointcut("execution(* com.test.*.*.*(..))")

是标注我的切面在哪里的,固定格式,excution中的com.test.*.*.*(..)表示我切入点的表达式。无参有参构造方法都囊括其中

@Befor是代表我程序运行之前我需要的一些操作

@AfterRunning代表我程序运行结束后的操作

2.4定义测试类

package com.test.contrall;

import com.test.configuration.SpringConfig;
import com.test.service.UserServer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserServer us = (UserServer) context.getBean("userServer");
        us.saveUser();
        us.updateUser();
        us.deleteUser();
}
}

2.5运行结果展示

三、相关知识点补充

3.1切入点表达式的写法-execution函数

关键字:execution(表达式)

表达式:访问修饰符 返回值 包名.包名.包名...类名.方法名

规则

表达式

示例

匹配所有类的public方法

execution(public **(..))

匹配指定包下所有类的方法(不包含子包)

execution(* 包名.*(..))

execution(* test.aop.t1.*(..))

匹配指定包下所有类的方法(包含子包)

execution(* 包名.**(..))

execution(* test.aop.t1..*(..))

匹配指定类下的所有方法

execution(* 包名.类名.*(..))

execution(* test.aop.t1.UserDao.*(..))

匹配指定类下的指定方法

execution(* 包名.类名.方法名(..))

execution(* test.aop.t1.UserDao.add(..))

匹配实现指定接口的所有类的方法

execution(* 包名.接口名+*(..))

execution(* test.aop.t1.UserDao+*(..))

匹配所有名称以save开头的方法

execution(* save*(..))

3.2 Spring中的通知

Spring中一共有5种通知

前置通知(before)、后置通知(returning)、异常通知(throwing)、最终通知(after)、环绕通知(around)

通知类型

作用

应用场景

前置通知

在目标方法执行前增强代码逻辑

权限控制(权限不足抛出异常)、记录方法调用信息日志

后置通知

在目标方法运行后返回值后再增强代码逻辑,在出现异常时不执行

与业务相关的,如银行在存取款结束后的发送短信消息 */

异常通知

目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)

处理异常(一般不可预知),记录日志

最终通知

不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)

释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )

环绕通知

目标方法执行前后,都进行增强(控制目标方法执行)

日志、缓存、权限、性能监控、事务管理

四、练习

计算一千亿累加数字,并使用环绕通知输出计算时间

comfiguration类

package com.demo02.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"com"})
public class configuration {
}

切面类

package com.test.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component//交给ioc容器
@Aspect//定义为切面类
public class LoggerAspect {
    @Pointcut("execution(* com.test.*.*.*(..))")
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point){
        Object result = null;
        //执行前通知
        long beginTime = new Date().getTime();
        //执行要增强的函数
        //获取目标函数中的参数
        Object[] args = point.getArgs();
        try {
            result = point.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //执行后通知
        long endTime = new Date().getTime();
        System.out.println("**********执行时间:"+(endTime-beginTime)+"毫秒**********");
        return result;
    }
}

创建server层

package com.test.service;

import org.springframework.stereotype.Service;

@Service
public class UserServer {
    public void doSum(){
        long sum=0;
        long xx=1000000000+1;
        sum = xx * (1000000000 / 2);
        System.out.println(sum);
    }
}

创建测试类

package com.test.contrall;

import com.test.configuration.SpringConfig;
import com.test.service.UserServer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserServer us = (UserServer) context.getBean("userServer");
         us.doSum();
    }
}

运行结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值