spring 入门篇 五.aop(面向切面)原理以及专业名词解释

10 篇文章 0 订阅

实际案例

功能模块划分

现在我们做了一个支付的项目,具体功能模块如下
在这里插入图片描述
需求是,在执行支付业务之前必须先监测安全,执行完支付业务以后,必须记录日志并且清空缓存

编辑逻辑类
安全功能模块
public class SafetyImpl {
	public static void monitorSafe() {
		System.out.println("环境监测安全,可以进行操作");
	}
}
支付功能模块
public class CrudImpl {
	
	public static void save_money() {
		System.out.println("存入100万元");
	}
	
	public static void get_money() {
		System.out.println("查询余额");
	}
	
	public static void take_money() {
		System.out.println("取10万元");
	}
}
日志功能模块
public class LogImpl {
	public static void print_log() {
		System.out.println("打印日志");
	}
}
缓存功能模块
public class CacheImpl {
	public static void clearCache(){
		System.out.println("清除缓存");
	}
}

现在我们完成了这个系统的4个功能模块,那我们要开始实现这四个功能模块之间的顺序问题了,
我们有如下的几种方法

硬编码实现需求

既然你要实现这几个功能模块的顺序执行,那很简单嘛,我顺序调用不就行了

支付模块代码修改
public class CrudImpl {
	private static SafetyImpl safe = new SafetyImpl();
	private static LogImpl loge = new LogImpl();
	private static CacheImpl cache = new CacheImpl(); 
	
	
	public static void save_money() {
		
		
		safe.monitorSafe();
		System.out.println("存入100万元");
		loge.print_log();
		cache.clearCache();
	}
	
	public static void get_money() {
		System.out.println("查询余额");
	}
	
	public static void take_money() {
		System.out.println("取10万元");
	}
	
}
测试类
public class HardAopTest {

	@Test
	public void test() {
		CrudImpl demo = new CrudImpl();
		demo.save_money();
	}

}
运行结果

在这里插入图片描述

总结

使用硬编码方式
优点

逻辑简单,直接顺序调用就好了

缺点

1.修改极不方便,这里我只修改了支付模块的一个方法,就要在这个方法里去修改3行代码,要是这个模块有1000个方法呢,你是不是要晕菜了,你就得机械的打开这1000个方法,将这3行代码弄进去,基本这样一弄,一天的时间都浪费了

2.当我们是一个大型团队进行开发的时候,往往是多个模块同时进行,你如果是使用这种硬编码的格式,你编写支付模块的时候就得等到其他三个模块开发完你才可以进行开发,影响开发进度

3.违反了开闭原则,就是当我们需求有变化时,我们更应该做的是去增添几个子类或者配置,而不是去修改现在已经写好了的代码

4.违背了高内聚 低耦合的原则,当我们开发的时候我们应该让本来就有不可分割的联系的那些内容尽可能聚合在一起,让那些没有必要联系的内容尽可能分的更开,这样我们的工程就是一个一个的模块,当我们修改一个模块的时候完全不用担心会影响到另一个模块,这里就违背了这个原则,支付模块与其他三个模块并没有必要联系,但是这里把它们写到了一起,使这4个模块耦合在了一起

使用代理模式实现需求

代理类
public class AopProxy {
	private static SafetyImpl safe = new SafetyImpl();
	private static LogImpl loge = new LogImpl();
	private static CacheImpl cache = new CacheImpl(); 
	
	
	public static void save_money() {	
		safe.monitorSafe();
		System.out.println("存入100万元");
		loge.print_log();
		cache.clearCache();
	}
	
}
测试类
public class AopProxyTest {

	@Test
	public void test() {
		AopProxy ap = new AopProxy();
		ap.save_money();
	}

}
运行结果

在这里插入图片描述

总结

其实代理模式很简单,我们都用过插座吧,当我们发现现在只有一个二孔的插座,但是我们要使用三孔的电器的时候怎么办呢,我们会拿出来一个神奇"插座转换器"
在这里插入图片描述,这时候我们就可以轻松使用我们的电器了,这个插座转换器就相当于我们新写的代理类,它是我们不用去改变原来的东西的同时,也能完成需求
优点

1.我们没有对4个模块进行一点改动,只是生成了一个类,我们直接调用类文件就可以了,这对我们的代码是没有侵入性

2.符合开闭原则,我们关闭改变(改变原有代码),拥抱扩展(新增类或者接口等等)

缺点

因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

这是因为这个缺点,我们的正主要出场了,代理模式的升级版

AOP实现需求

AOP定义

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

专业名词

切面
通知
连接点
织入
代理
是不是看起来一脸懵逼,单纯讲概念那是谁学起来都很痛苦,不如把概念引入实际情景中来学习,下面我们来讲一个实际的案例

使用AOP
编写xml
<?xml version="1.0" encoding="UTF-8"?>
<beans 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns="http://www.springframework.org/schema/beans" 
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-4.2.xsd 
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
 
	<context:component-scan base-package="cn.springLearn.aop"></context:component-scan>
	 
	<!-- 配置安全模块的bean' -->
	<bean id = "safe" class = "cn.springLearn.aop.SafetyImpl" ></bean>
	
	<!-- 配置支付模块的bean' -->
	<bean id = "crud" class = "cn.springLearn.aop.CrudImpl" ></bean>
	
	<!-- 配置日志模块的bean' -->
	<bean id = "log" class = "cn.springLearn.aop.LogImpl" ></bean>
	
	<!-- 配置缓存模块的bean' -->
	<bean id = "cache" class = "cn.springLearn.aop.CacheImpl" ></bean>
	
	
	<!-- 配置aop -->
	<aop:config proxy-target-class="true">
		<!-- 
			配置连接点
			因为连接点可能是很多的类,或者一个类里面很多的方法
			spring肯定不会傻到让我们每一个连接点都写一个配置
			所以spring采用了通配符匹配的方式来确定连接点
		 -->
		<aop:pointcut  id="pt" expression="execution(* cn.springLearn.aop.*Impl.*(..))" />
					  
		<!-- 
			配置切面
			ref:配置的切面的id
		 -->
		<aop:aspect ref="safe">
			<!-- 
				配置通知 
				method:配置的切面的一个方法的方法名
				pointcut-ref:连接点的id
			-->
			<aop:before method="monitorSafe" pointcut-ref="pt" />
		</aop:aspect>
		
		<!-- 配置日志切面 -->
		<aop:aspect ref="log">
			<aop:after method="print_log" pointcut-ref="pt" />
		</aop:aspect>
		
		<!-- 配置缓存切面 -->
		<aop:aspect ref="cache">
			<aop:after method="clearCache" pointcut-ref="pt" />
		</aop:aspect>
	</aop:config>
	 
</beans>
编写测试类
public class AopTest {

	@Test
	public void test() {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext3.xml");
		CrudImpl demo = (CrudImpl)ac.getBean("crud");
		demo.save_money();
	}

}
运行结果

在这里插入图片描述

总结

通过上面的案例我们已使用aop实现了需求,但是我们仅仅只是写了一个配置文件就完成了,对代码几乎没有侵入性,
这里有一个问题,为什么我们有了代理模式还要使用aop呢?
因为我们使用代理模式是静态的代理,实际是我们在程序运行之前就要写好这些代理类,但是动态代理就是我们实际运行的时候,通过反射机制在字节码层次去生成代理类,就比静态代理要灵活,至于动态代理的实现请继续关注笔者的spring系列

aop的坑

1.只有 Spring 生成的对象才有 AOP 功能,因为 Spring 生成的代理对象才有 AOP 功能。切入的类和被切入的类必须是被spring管理的(springIOC),如果是自己new 出来的,切入无效。

2.而且所对应的切入方法不能是static 修饰的

3.例如上面的案例,我们写了两个after的通知,那么这两个通知的执行顺序是怎么样的呢?
它与我们编写的顺序是反的,越写在前面的越后执行,这是我们就可以给切面设置一个属性,order ,这是属性的值越大越先被执行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值