java开发AOP面向切面编程入门

作者:guoyp2126 时间:2021-08-22 04:47:56 

引言

在实际应用场景中,我们封装一个学生的类,这个类用于封装学生的日常行为,如:上学、吃饭、上课等。然而,在疫情期间,学生上学时入校、吃饭时进入餐厅,需要测温查验证件等行为,拿到这样的需求我们怎么办?

不好的解决方案

面向过程的解决方案

遇到问题解决问题,在上学、吃饭方法中加上测温、查验证件方法,或者在学生类中提炼一个测温查验证件私有的方法,在需要时调用。

代码如下:


public class Student {
   public void toSchool(){
       check();
       System.out.println("高高兴兴去上学!");
   }
   public void toEat(){
       check();
       System.out.println("一号餐厅去吃饭!");
   }

public void toClass(){
       System.out.println("排排坐,听讲座!");
   }

public void getUp(){
       System.out.println("起床啦!");
   }

private void check(){
       System.out.println("查验证件、测体温");
   }
}

这种方式存在问题只是头痛医头、脚痛医脚,代码硬拷贝,现在教师进校门也要查验证件测温,如何办?将check代码复制到教师类中,再者,如果check()中业务规则发生变化,则需要到处改代码,显然是一种非常蹩脚的解决方案。

使用继承解决方案

创建一个man的基类,将check()方法放到该类中,教师、学生均继承此类,子类调用这个方法即可,

代码如下:


public class Man {
   protected void check(){
       System.out.println("查验证件、测体温");
   }
}

public class Student extends Man {

public void toSchool(){
       check();
       System.out.println("高高兴兴去上学!");
   }

public void toEat(){
       check();
       System.out.println("一号餐厅去吃饭!");
   }
}

public class Teacher extends Man {
   public void toSchool(){
       check();
       System.out.println("高高兴兴去上班!");
   }

public void toEat(){
       check();
       System.out.println("教工餐厅去吃饭!");
   }

public void toClass(){
       System.out.println("排排坐,听讲座!");
   }
}

这种方式,虽然解决了代码复制问题,但违背了面向对象程序设计的单一职责原则,因为查验证件测温等均不是学生或教师应该拥有的职责,类的划分职责不清,增加了代码扩展维护的难度。

使用聚合的解决方案

将查验证件测温等行为封装一个独立的类,学生和教师的依然只封装他们固有的方法,

代码如下:


public class Checktor {
   public void takeTemperature(){
       System.out.println("查验证件、测体温");
   }
}
public class Student {
   private Checktor checktor = new Checktor();

public void toSchool(){
       checktor.takeTemperature();
       System.out.println("高高兴兴去上学!");
   }

public void toEat(){
       checktor.takeTemperature();
       System.out.println("一号餐厅去吃饭!");
   }
}

这种方法很好的解决了面向对象程序设计单一职责原则,体现了类的封装性,但是代码侵入性很强,而且代码僵化,维护性差。如疫情结束了,我们要求取消查验证件和测温,就需要改原有的代码,破坏了开闭原则,增加了程序员的工作量。有些人写配置开关变量,在调用时使用ifelse进行判断是否调用也能遵循开关变量,但是代码中包含了一些可能用不到代码,不优雅,不是很好解决方案。

面向切面的编程基本概念

面向切面的编程是不破坏原有类封装性的前提下,动态在其方法前面(before)、后边(after)及周围(Around)(前面和后边)增强功能。

  • 连接点(Joinpoint):目标对象中每个方法前面、后面均为连接点。如学生类中toSchool、toEat、toClass、getUp方法前面后面均是切入点;

  • 切入点(Point):需要增强功能方法前面或后面,是切入点,如进学校前、吃饭前需要测体温,则toSchool、toEat两个方法前是切入点,其余的不是;

java开发AOP面向切面编程入门

  • 切面类(Aspect):也称通知类,封装了需要增强功能的类,如:Checktor;

  • 通知方法(advice):需要增强的功能,如:测温方法;

  • 通知类型:前置通知(Before)、后置通知(After)、环绕通知(around)、异常通知(throwing)、最终通知(after (finally) advice);

java开发AOP面向切面编程入门

  • 代理(proxy):将通知应用到目标对象实现机制,是通过创建代理对象完成的

  • 织入(Weaving):将切面代码插入到目标对象上,生成代理对象的过程

基于Spring面向切面程序实现

针对学生上学场景,Spring的使用XML和注解两设计种方式完成面向切面的编程。
工程依赖项:在maven pom文件中,需要引入以下依赖项


    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjtools</artifactId>
     <version>1.8.9</version>
   </dependency>

<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjrt</artifactId>
     <version>1.5.4</version>
   </dependency>

<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.9.5</version>
     <scope>runtime</scope>
   </dependency>

XML方式:
学生类:


public class Student {

public void toSchool(){
       System.out.println("高高兴兴去上学!");
   }

public void toEat(){
       System.out.println("一号餐厅去吃饭!");
   }

public void toClass(){
       System.out.println("排排坐,听讲座!");
   }

public void getUp(){
       System.out.println("起床啦!");
   }
}

切面类:


public class Checktor {
   public void takeTemperature(){
       System.out.println("测体温啦!");
   }

public void washHand(){
       System.out.println("洗洗手更健康!");
   }
}

xml文件


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
   <context:component-scan base-package="com.bjwl.*"/>
   <!--目标(业务)对象-->
   <bean id="student" class="com.bjwl.xmltest.Student"/>
   <bean id="teacher" class="com.bjwl.xmltest.Teacher"/>
   <!--通知(切面)对象-->
   <bean id="checktor" class="com.bjwl.xmltest.Checktor"></bean>

<aop:config>
       <!--定义(切面)对象-->
       <aop:aspect ref="checktor">
           <!--定义切点-->
           <aop:pointcut id="point_test_d" expression="execution(* com.bjwl.xmltest.Student.to*(..))"/>
           <!--定义通知-->
           <aop:before method="takeTemperature" pointcut-ref="point_test_d"></aop:before>
           <aop:after method="washHand" pointcut-ref="point_test_d"></aop:after>
       </aop:aspect>
   </aop:config>
</beans>

其中:目标对象是student对象,切点是student对象中方法名前两个字母是to的所有方法,事前执行测温(takeTemperature)、事后执行洗手(washHand)
测试代码如下:


public class StudentTest {
   @Test
   public void toSchool() {
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
               "bean02.xml"
       );
       Student student = (Student)applicationContext.getBean("student");
       student.toSchool();
   }
}

运行结果如下:

java开发AOP面向切面编程入门

如果教师需也需要测温,编写教师类,代码如下:


public class Teacher {
   public void toSchool(){
       System.out.println("高高兴兴去上班!");
   }

public void toEat(){
       System.out.println("教工餐厅去吃饭!");
   }

public void toClass(){
       System.out.println("排排坐,听讲座!");
   }
}

xml文件更改如下:


   <aop:config>
       <!--定义(切面)对象-->
       <aop:aspect ref="checktor">
           <!--定义切点-->
           <aop:pointcut id="point_test_d" expression="execution(* com.bjwl.xmltest.*.to*(..))"/>
           <!--定义通知-->
           <aop:before method="takeTemperature" pointcut-ref="point_test_d"></aop:before>
           <aop:after method="washHand" pointcut-ref="point_test_d"></aop:after>
       </aop:aspect>
   </aop:config>

则表示com.bjwl.xmltest中的所有类的对象均为目标对象,前两个字母带to的方法前后均为切点。

注解方式:
学生类代码:

java开发AOP面向切面编程入门

切面类的代码

java开发AOP面向切面编程入门

xml文件:

java开发AOP面向切面编程入门

运行结果等同,XML文件。

这样做的好处,学生类、教师类、切面类具有职责单一性,提现业务逻辑封装性,动态增强符合开闭原则。

小结

以上简单的介绍了面向切面编程的基本概念和基本用法。用好面向切面的编程技术很不易,因为切点只能设置到需要增强功能前面或后面,对程序设计人员要求很高,要求能够根据编程经验和技巧设计出合理的切点,需要提现类的层次性。如:执行SQL语句前,在控制台输出完整的SQL语句的切点,切点就不能设置到你自己定义的Dao中,而是设置在PrepareStatement的exectuce的方法前。不过也不用担心,使用各种框架时,常用的日志等,已经给我们设置了开关变量,我们直接使用即可,非常简单。

来源:https://blog.csdn.net/guoyp2126/article/details/120247685

标签:AOP,面向切面,入门
0
投稿

猜你喜欢

  • Java调取创蓝253短信验证码的实现代码

    2021-11-05 00:48:10
  • Android使用SQLite数据库的简单实例

    2022-03-04 13:52:50
  • Hibernate双向多对多映射关系配置代码实例

    2022-12-26 22:30:30
  • Android线程间通信Handler源码详解

    2021-12-31 07:04:10
  • C#判断当前程序是否通过管理员运行的方法

    2023-09-27 15:48:24
  • Android下载进度监听和通知的处理详解

    2022-03-27 08:39:02
  • mvc开启gzip压缩示例分享

    2022-05-03 08:34:29
  • C语言简明讲解变量的属性

    2021-06-23 17:45:28
  • Android 自定义来电秀实现总结

    2023-12-17 07:52:32
  • Java线程生命周期的终止与复位

    2022-04-22 01:57:01
  • nacos中的配置使用@Value注解获取不到值的原因及解决方案

    2023-11-29 13:43:00
  • 七个Spring核心模块详解

    2021-07-25 15:32:18
  • Android编程基于自定义View实现绚丽的圆形进度条功能示例

    2023-07-24 17:13:26
  • Android Flutter中Offstage组件的使用教程详解

    2023-08-20 01:01:44
  • c#基于opencv,开发摄像头播放程序

    2023-06-20 11:54:29
  • java对象转型实例分析

    2021-12-19 14:14:15
  • Java异步调用转同步方法实例详解

    2023-11-01 20:38:25
  • SpringBoot Test类注入失败的解决

    2023-05-20 13:59:40
  • Java switch使用原理及实例解析

    2023-10-11 20:44:20
  • Android自定义双向滑动控件

    2022-04-30 04:11:18
  • asp之家 软件编程 m.aspxhome.com