【推荐】深入浅出学习Spring框架【中】

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【推荐】深入浅出学习Spring框架【中】

1.AOP是什么?  

  • 面向切面编程(Aspect-Oriented Programming)是一种编程范式,它的主要目的是通过预编译和运行期动态代理实现程序功能的横切(cross-cutting)特性,如日志记录、性能统计、事务监控等。它可以帮助开发者将这些原本分散在各个方法或类中的业务逻辑抽象出来,提高代码复用性,降低耦合度
  • 面向切面编程的核心思想是将程序分为两个部分:切入点和切面切入点(entry point)是程序执行过程中需要被拦截的代码段,而切面(weaving)则是实现横切功能的代码段。在运行时,通过动态代理技术,将切入点的代码交由切面织入,实现横切的效果
  • 面向切面编程是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只是修改这个行为即可
  • AOP通过提供另一种思考程序结构的方式来补充了面向对象编程(OOP)。OOP中模块化的基本单元是类(class),而AOP中模块化的基本单元是切面(aspect)。可以这么理解,OOP是解决了纵向的代码复用问题,AOP是解决了横向的代码复用问题.

2.案列:

场景模拟:这里我模拟的场景为线上书城系统

首先进行一个没有使用Aop的模拟代码展示:

实体类:Book:其中是书籍的属性,行为等
Dao类:BookDao:其中是有关于书籍操作的代码
业务逻辑层:BookBiz:...,BookBizImpl:...
Web层:new 对象
//增加
public int add(Book book) {
b.add()
}
//修改
public int edit(Book book) {
b.edit()
}
//删除
public int del(Book book) {
b.del()
}
//上架
public int up(Book book) {
b.up()
}
//下架
public int down(Book book) {
b.down()
}

但是很多时候会出现这样的情况:

①用户购买了书籍,却说自己没有收到货物...

②店家发出的货物为空货物,而其却为了牟利,矢口否认...

针对这样的情况,很多时候都没有证据来证明到底是真是假,而在我们成熟的系统中,通常都会添加一个叫做‘日志记录’的东西,它可以用来记录和跟踪系统、应用程序或事件的活动和状态的过程,通俗来说就是使用这个系统的用户的每一步操作都会被记录下来,这样就话就成为一个证据:那成熟的系统应该是怎么样?

实体类:Book:其中是书籍的属性,行为等
Dao类:BookDao:其中是有关于书籍操作的代码
业务逻辑层:BookBiz:...,BookBizImpl:...
Web层:new对象
//增加
public int add(Book book) {
b.add()
}
//修改
public int edit(Book book) {
b.edit()
}
//删除
public int del(Book book) {
b.del()
}
//上架
public int up(Book book) {
datetime=..//操作时的时间
username=...//操作的用户名
args = ...//参数
logBiz.add(datetime,username,args);//将其都添加到日志中去
b.up()
}
//下架
public int down(Book book) {
datetime=..//操作时的时间
username=...//操作的用户名
args = ...//参数
logBiz.add(datetime,username,args);//将其都添加到日志中去
b.down()
}

现在是在上架和下架中添加了日志记录,如果要在其他的方法操作中,也添加日志记录的话,那就需要将这一段代码再重复几次,这样就有点麻烦,而且还改变了原有代码的结构,如果需求发生改变,需要对打印的日志内容作出修改,那就必须修改用到了日志记录方法中的所有相关代码,如果是1000个方法呢?每次就需要手动去修改1000个方法中的代码,对项目的维护成本就会很高这也不利于我们的系统维护。然后我们可以用到AOP可以帮助开发者将将原本分散在各个方法或类中的业务逻辑抽象出来,提高代码复用性,降低耦合度,接下怎么提高代码的复用性:

3.spring的aop的专业术语

  项目代码但是从上往下依次执行,而现在加入了面向切面的思想,当我们的代码执行到目标对象是,查看连接点是否有前置通知,先执行前置通知,再执行目标方法,如果没有前置通知,那么就直接执行目标方法,最后看连接点上是否有后置通知,如果有,就再执行后置通知,如果没有就执行完了。

  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
  • 目标(Target):被通知(被代理)的对象,就是完成具体的业务逻辑 ,比如书籍的增删改查
  • 通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)  ,完成切面编程,非业务核心代码
  • 代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),  例子:外科医生+护士只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
  • 切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点 , (也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序),比如给新增方法添加日志功能
  • 适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)

    注:目标对象只负责业务逻辑代码

             通知对象负责AOP代码,这二个对象都没有AOP的功能,只有代理对象才有。

4.代码模拟

4.1 前置通知

      首先,我们先写service接口和实现类进行模拟,在里面写两个方法

package com.sy.aop.biz;
public interface IBookBiz {
  // 购书
  public boolean buy(String userName, String bookName, Double price);
  // 发表书评
  public void comment(String userName, String comments);
}

    然后,写实现类,重新这两个方法,并且做了一个价格的判断

package com.sy.aop.biz.impl;
import com.sy.aop.biz.IBookBiz;
import com.sy.aop.exception.PriceException;
public class BookBizImpl implements IBookBiz {
  public BookBizImpl() {
    super();
  }
  public boolean buy(String userName, String bookName, Double price) {
    // 通过控制台的输出方式模拟购书
    if (null == price || price <= 0) {
      throw new PriceException("book price exception");
    }
    System.out.println(userName + " buy " + bookName + ", spend " + price);
    return true;
  }
  public void comment(String userName, String comments) {
    // 通过控制台的输出方式模拟发表书评
    System.out.println(userName + " say:" + comments);
  }
}

接下来要写上面价格判断的异常

package com.sy.aop.exception;
public class PriceException extends RuntimeException {
  public PriceException() {
    super();
  }
  public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
    super(message, cause, enableSuppression, writableStackTrace);
  }
  public PriceException(String message, Throwable cause) {
    super(message, cause);
  }
  public PriceException(String message) {
    super(message);
  }
  public PriceException(Throwable cause) {
    super(cause);
  }
}

然后,我们先创建一个类,将类名.方法名,携带的参数,作为日志存储到数据库。

package com.sy.aop.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
 * 买书、评论前加系统日志
 * @author shenyan
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
  @Override
  public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//    在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
    String target = arg2.getClass().getName();
    String methodName = arg0.getName();
    String args = Arrays.toString(arg1);
    System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");
  }
}

最后,进行一个配置

!--aop-->
<!-- 目标对象 -->
<bean class="com.sy.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
    <!-- 通知 -->
<bean class="com.sy.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean>
    <!-- 代理=目标+通知 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <property name="target" ref="bookBiz"></property>
        <property name="proxyInterfaces">
            <list>
                <value>com.sy.aop.biz.IBookBiz</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>myMethodBeforeAdvice</value>
            </list>
        </property>
    </bean>

前台的一个验证:

package com.sy.aop.demo;
import com.sy.aop.biz.IBookBiz;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * @author 谌艳
 * @site www.shenyan.com
 * @create 2023-08-17 21:13
 */
public class demo1 {
    public static void main (String [] args){
       ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring-context.xml");
        IBookBiz bookBiz=(IBookBiz) context.getBean("bookBiz");
        bookBiz.buy("花花","天下掉下一个林妹妹",18.88);
        bookBiz.comment("嘿嘿","嘿嘿嘿真好看");
    }
}

结果为展示:

 3.2.后置通知

      有了前面的铺垫,直接再创建一个后置通知的类,比起前置通知,多了一个参数,就是返回参数

package com.sy.aop.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
 * 买书、评论前加系统日志
 * @author shenyan
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
  @Override
  public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//    在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
    String target = arg2.getClass().getName();
    String methodName = arg0.getName();
    String args = Arrays.toString(arg1);
    System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");
  }
}

接着,配置文件即可

然后前台看结果;

package com.sy.aop.demo;
import com.sy.aop.biz.IBookBiz;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * @author 谌艳
 * @site www.shenyan.com
 * @create 2023-08-17 21:13
 */
public class demo1 {
    public static void main (String [] args){
       ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("/spring-context.xml");
//        IBookBiz bookBiz=(IBookBiz) context.getBean("bookBiz");
        IBookBiz bookBiz=(IBookBiz) context.getBean("bookProxy");
        bookBiz.buy("花花","天下掉下一个林妹妹",18.88);
        bookBiz.comment("嘿嘿","嘿嘿嘿真好看");
    }
}

 3.3.环绕通知

       结合了前置通知和后置通知,它两个都有所以一般常用这个,

       它只有一个参数,但是这一个参数相当于上面前置通知和后置通知的3,4个参数

package com.sy.aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.util.Arrays;
/**
 * 环绕通知
 *  包含了前置和后置通知
 * 
 * @author shenyan
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {
  @Override
  public Object invoke(MethodInvocation arg0) throws Throwable {
    String target = arg0.getThis().getClass().getName();
    String methodName = arg0.getMethod().getName();
    String args = Arrays.toString(arg0.getArguments());
    System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了");
//    arg0.proceed()就是目标对象的方法
    Object proceed = arg0.proceed();
    System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed);
    return proceed;
  }
}

 接着就是配置文件

然后前台测试:

结果展示:

 3.4.异常通知

       先建一个类,但是注意,这个异常通知的类,重写的话,方法名字只能是这个,否则报错

然后配置文件:

然后在前台测试:

结果展示:

3.5.过滤通知

       过滤通知就是那个适配器,它不需要再建一个类,直接再配置文件里面配置就可以了,需要正则判断,这里举例过滤的是后置通知

结果展示:

今天小编的分享就结束呐,生活总是需要不断去学习新的知识,多想想然后再去实操,持之以恒,经验和思维都会发生转变,我们要保持谦虚学习和自信的态度,各位加油!


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
24天前
|
XML 安全 Java
|
27天前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
39 0
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
2月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
48 0
|
2天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
9天前
|
IDE Java 测试技术
互联网应用主流框架整合之Spring Boot开发
通过本文的介绍,我们详细探讨了Spring Boot开发的核心概念和实践方法,包括项目结构、数据访问层、服务层、控制层、配置管理、单元测试以及部署与运行。Spring Boot通过简化配置和强大的生态系统,使得互联网应用的开发更加高效和可靠。希望本文能够帮助开发者快速掌握Spring Boot,并在实际项目中灵活应用。
27 5
|
20天前
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
48 8
|
1月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
51 6
|
1月前
|
Java 数据库连接 数据库
不可不知道的Spring 框架七大模块
Spring框架是一个全面的Java企业级应用开发框架,其核心容器模块为其他模块提供基础支持,包括Beans、Core、Context和SpEL四大子模块;数据访问及集成模块支持数据库操作,涵盖JDBC、ORM、OXM、JMS和Transactions;Web模块则专注于Web应用,提供Servlet、WebSocket等功能;此外,还包括AOP、Aspects、Instrumentation、Messaging和Test等辅助模块,共同构建强大的企业级应用解决方案。
70 2
|
2月前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
92 1
Spring 框架:Java 开发者的春天