SpringBoot的AOP编程

本文介绍了SpringBoot中的AOP编程,包括AOP的概念及其在对象间依赖关系中的作用。文章以日志记录为例,详细讲解了AOP的一般使用,并进一步探讨了自定义注解的开发过程,包括配置yml文件、定义注解、使用注解以及装配写入点和切面的实现。内容涵盖了JDK动态代理和CGLIB两种动态代理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Spring的两大核心思想:
IOC
IOC(Inversion of Control) 控制反转:对象的创建 由原来代码中new的方法 转移到 spring的配置文件中,由spring工 厂进行创建。

spring不仅提供了对 对象的管理,而且对象和对象 间的依赖关系 也提供了 完美的解决方案–即,DI(Dependency Injection)依赖注入

AOP
面向切面编程
AOP的使用场景:在主要功能上 增加 可有可用的额外功能时 使用。
在这里插入图片描述

AOP的一般使用

依赖坐标
   	`<!--aop启动器-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-aop</artifactId>
       </dependency>
       <!--注解式开发-->
       <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjweaver</artifactId>
       </dependency>
开发切面整合切入点

以一个执行日志记录为示例


package com.baizhi.aspect;

import com.baizhi.entity.LogEntity;
import com.baizhi.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;

@Component //将此类交给工厂管理
@Aspect		//定义切面
@Slf4j		//引入日志
public class TestBeforeAdvice {
    @Autowired
    HttpServletRequest request; //此对象可由工厂自动管理
  
//定义切入点,因为此注解只可用在方法上,所以随机定义一个无意义的方法
@Pointcut(value = "execution(* com.baizhi.service.*.*(..))")
   public void pt() {   }

//配置环绕通知
 @Around(value = "pt()")
 		//ProceedingJoinPoint  参数是 JoinPoint 连接点 的子类
    public Object testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		
        System.out.println("this is around before");

		 try {
            proceed = proceedingJoinPoint.proceed();   //调用目标方法,必须proceed,目标方法才可执行
            return proceed; //此对象包含了原目标方法中的方法名、参数信息、方法对象等信息...
        } catch (Throwable throwable) {
        	/*这里不直接抛出异常,是因为 业务层事务的关系,相当与这里有2层事务,当执行有误后,日志可以正常记录,而保证业务层事务正常,所有捕获到执行失败,本次日志将记录,同时,不会让外层事务得不到异常*/
            throw throwable;
        } finally {
            log.info("执行完毕”);
        }		
	}

自定义注解开发

注意:自定义注解时,需要改变代理类的实现方式为CGLIB.
AOP动态代理有两种实现方式

    1. Proxy JDK的反射包,aop默认采用 动态代理类 实现(目标类实现的)接口。
    1. CGLIB 开源(对JDK的proxy进行了封装) 动态代理类 扩展目标类。
①配置yml文件
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  mvc:
    view:
      suffix: .jsp
      prefix: /
  aop:
    proxy-target-class: true #修改代理方式为CGLIB 默认fasle JDK
  http:
    multipart:
      max-file-size: 50Mb
      max-request-size: 500Mb
②自定义注解

package com.baizhi.aspect;

/*
* 这是个自定义注解
* */

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)//说明此注解用在的位置 TYPE表示运行在类上
public @interface BlogAdvice {
    //给定一个属性,可以携带数据
    String message();
}

定义一个日志实体类

package com.baizhi.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;


@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Blog implements Serializable {
    private String id;
    private String name;
    private String thing;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private Date time;
    private Boolean result;
}

设置日志业务层的事物等级
REQUIRES_NEW需要事物,开启新事物
在这里插入图片描述

package com.baizhi.service;

import com.baizhi.dao.BlogDao;
import com.baizhi.entity.Blog;
import com.baizhi.entity.JsonDtoBlog;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

@Service
public class BlogServiceImp implements BlogService {
    //提供公用的成员变量,用于调用dao
    @Resource
    private BlogDao blogDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
    public JsonDtoBlog queryByPage(int curPage, int pageSize) {
        JsonDtoBlog jdb = new JsonDtoBlog();
        //查总数据
        int count = blogDao.selectCount();
        //计算总页数
        int total = count%pageSize==0?count/pageSize:count/pageSize+1;
        //查出每页的数据
        List<Blog> blogs = blogDao.selectByPage((curPage - 1) * pageSize, pageSize);
        //赋值并返回
        jdb.setPage(curPage);
        jdb.setTotal(total);
        jdb.setRecords(count);
        jdb.setRows(blogs);
        return jdb;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void add(Blog blog) {
        blogDao.insert(blog);
    }
}

③使用自定义注解
package com.baizhi.service;

import com.baizhi.aspect.BlogAdvice;
import com.baizhi.dao.BarticleDao;
import com.baizhi.entity.Barticle;
import com.baizhi.entity.JsonDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
@Transactional
public class BarticleServiceImp implements BarticleService {
    //提供成员变量,用于调用dao
    @Resource
    private BarticleDao barticleDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
    public List<Barticle> queryAll() {
        List<Barticle> list = barticleDao.selectAll();
        return list;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
    public JsonDto queryByPage(int curPage, int pageSize) {
        
        /*
			public class JsonDtoBlog {
  				  private Integer page;
  				  private Integer total;//总页数
  				  private Integer records;//总行数
   				  private List<Blog> rows;//每页的数据
			}
		*/
		//构造一个jsondto对象 数据传输对象 与本次AOP无关
        JsonDto jsonDto = new JsonDto();
        //查询总数据
        int count = barticleDao.selectCount();
        //计算总页数
        int total = count%pageSize==0?count/pageSize:count/pageSize+1;
        //获取当前页内容
        List<Barticle> list = barticleDao.selectByPage((curPage - 1) * pageSize, pageSize);
        //赋值
        jsonDto.setPage(curPage);
        jsonDto.setTotal(total);
        jsonDto.setRecords(count);
        jsonDto.setRows(list);
        return jsonDto;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
    public Barticle queryOne(String id) {
        Barticle barticle = barticleDao.selectOne(id);
        return barticle;
    }

    @Override
    @BlogAdvice(message = "新增了博文")
    public void regist(Barticle barticle) {
        String uuid = UUID.randomUUID().toString();
        barticle.setId(uuid);
        Date date = new Date();
        barticle.setDate(date);
        barticleDao.insert(barticle);
    }

    @Override
    @BlogAdvice(message = "删除了博文")
    public void remove(String id) {
        barticleDao.deleteOne(id);
    }

    @Override
    @BlogAdvice(message = "修改了博文")
    public void modify(Barticle barticle) {
        barticleDao.update(barticle);
    }
}

④装配写入点与切面
package com.baizhi.aspect;

import com.baizhi.entity.Blog;
import com.baizhi.service.BlogService;
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class BlogSystem {
    //提供成员变量
    @Autowired
    private BlogService blogService;
    @Autowired
    private HttpServletRequest httpServletRequest;

	//执行事件的结果,false为失败
    boolean flag = false;
    //定义切入点
    @Pointcut(value = "@annotation(com.baizhi.aspect.BlogAdvice)")//以此注解为切入点
    public void method1(){
    }

    //定义通知方法
    @Around(value = "method1()")
    public Object BlogAdviceeMethdod(ProceedingJoinPoint proceedingJoinPoint){
        //获取方法签名
       MethodSignature methodSignature = (MethodSignature)proceedingJoinPoint.getSignature();
       //获取方法对象
        Method method = methodSignature.getMethod();
        //获取方法上的注解
        BlogAdvice annotation = method.getAnnotation(BlogAdvice.class);
        //获取注解的信息
        String message = annotation.message();
        //开始存信息
        Blog blog = new Blog();
        //设置id
        blog.setId(UUID.randomUUID().toString());
        //设置用户
       String  admin = (String) httpServletRequest.getSession().getAttribute("admin");
        blog.setName(admin);
        //设置信息
        blog.setThing(message);
        //设置时间
        blog.setTime(new Date());

        try{
            Object proceed = proceedingJoinPoint.proceed();
            flag=true;
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();

        } finally {
            //设置事件
            blog.setResult(flag);
            //最终存此事件入库
            blogService.add(blog);
            log.info("已进行日志存库操作,此次的操作者是{},操作事项是{}",admin,message);
        }
        return null;

    }

}

当您使用Spring Boot AOP(面向切面编程)时,可以通过创建切面、定义切点和编通知来实现横切关注点的功能。下面是一个简单的示例,演示如何使用Spring Boot AOP编程: 1. 首先,确保在pom.xml文件中添加了`spring-boot-starter-aop`依赖项: ```xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 创建一个切面类`LoggingAspect`,使用`@Aspect`注解标记为切面: ```javaimport org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect@Componentpublic class LoggingAspect { @Before("execution(* com.example.demo.*.*(..))") public void beforeAdvice() { System.out.println("Before executing the method..."); } } ``` 在上述代码中,`@Before`注解指定了在执行匹配切点的方法之前要执行的通知。`execution(* com.example.demo.*.*(..))`是一个切点表达式,它表示匹配`com.example.demo`包下的所有类的所有方法。 3. 创建一个简单的示例类`DemoController`: ```javaimport org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestControllerpublic class DemoController { @GetMapping("/hello") public String hello() { return "Hello, World!"; } } ``` 4. 运行Spring Boot应用程序,您将看到在访问`/hello`接口时,控制台将输出`Before executing the method...`的信息。 这个示例展示了一个简单的切面,它在执行匹配切点的方法之前打印出一条消息。您可以根据自己的需求编更复杂的切面和通知。 希望这个示例能帮助您理解如何在Spring Boot中使用AOP编程。如果您有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值