Spring学习记录(五)——初步体验AOP

2018.4.16

仅为个人理解 不足之处欢迎指正~

什么是AOP?

以下部分引例参考KenWang的博客~

AOP(Aspect Oriented Programming)——面向切面编程

AOP与IoC是Spring的两大核心思想之一

同样也是对传统的OOP(Object Oriented Programming,面向对象编程)的一种补充

在OOP中 通过继承 多态 封装等概念可以模仿现实生活中的各种纵向关系

但OOP并不适用于模仿横向关系

什么是横向关系?

想象几个实际生活中的例子:

1.在操作ATM时我们可能进行如下操作:

  •    取款: 开始——输入密码——取款——结束
  •    转账: 开始——输入密码——转账——结束
  •    余额: 开始——输入密码——查询——结束

在这个例子中我们可以看到 如果我们将 取款 转账 查询余额 作为核心操作

那么输入密码这一操作就是被核心操作所调用的“辅助操作

这一形容并不是说辅助操作的重要性低 而是相对于各有所专的核心操作而言

辅助操作显得重复且无关

2.在一个购物系统中 可能需要进行以下操作:

  • 对生成的每一笔订单提供事务管理
  • 当用户点击、搜索某关键字时 我们想要统计这类商品的点击率
  • 当用户登录、退出、修改密码时 我们需要记录进行操作的地点与时间

这个例子中的三项操作 均可以看作是核心业务功能的“辅助功能

这与 “切面” “横向关系” 又怎么理解?



我们可以看到 在ATM例子中 我们将取款 转账 查询余额三个互不相关的业务画作三条直线

而这三个业务都需要 输入密码 这一辅助功能的支持

而这条切入于三条黑线的红线 就是所谓的“切面

红线与黑线的交点 可以称为“切点

同时 也建立起了“横向关系

在面向切面编程AOP的思想中

让核心业务功能和切面功能分别独立开发

然后再将这两者编织在一起 这就叫做AOP



为什么要使用AOP?

与Spring的另一核心思想IoC一样 AOP同样致力于使代码更加简洁明了 让我们将心思更多的花在核心代码的构建中

假如在ATM例子中

取款、转账、查询余额为三个不同的服务

现在这三个服务都需要“验证身份”这一功能的支持

那么该怎么做?

或许你会想到 在这三段代码中都加入一段验证身份的代码

或许你会想到 写一个单独的函数 然后在三个服务开始时调用它

但是这些方法都会产生代码的重复及耦合

总之:

AOP可以使业务模块更简洁 并解决业务逻辑与辅助模块的耦合度问题


开始使用AOP

额外添加的包:


其他Spring需要包与之前内容相同


我们使用这样一个例子:

现在有一个登录服务 我们需要在登录的前后打印系统时间

我们将登录服务作为核心业务 将打印时间作为辅助业务

首先构建项目结构:


在aspect包中编写Timeprint类:

package com.tzy.aspect;

public class Timeprint 
{
	public void printTime()
	{
		System.out.println("当前时间为:"+System.currentTimeMillis());
	}
}

在serive包中编写Login类:

package com.tzy.serive;

import java.util.Scanner;

public class Login 
{
	public void log()
	{
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入用户名:");
		String username=sc.next();
		System.out.println(username+"  登录成功!");
	}
}

编写测试类:

package com.tzy.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.tzy.serive.Login;

public class testAOP 
{
	public static void main(String args[])
	{
		ApplicationContext context=new ClassPathXmlApplicationContext
				(new String[]{"applicationContext.xml"});
		Login user1=(Login)context.getBean("login");
		user1.log();
	}
}

现在配置applicationContext:

<?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:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
   <context:annotation-config/>
      
   <bean name="login" class="com.tzy.serive.Login" >
   </bean>
    <bean id="Timeprint" class="com.tzy.aspect.Timeprint">
   </bean>
   
   <aop:config>
   		<aop:aspect id="time" ref="Timeprint">
   				<aop:pointcut id="test1" 
   				expression="execution(* com.tzy.serive.Login.*(..))"/>
   				<aop:before method="printTime" pointcut-ref="test1"/>
   				<aop:after method="printTime" pointcut-ref="test1"/>
   		</aop:aspect>
   </aop:config>
	
  </beans>

运行:



接下来解释一下配置文件中的部分问题

1.bean类的装配请参考前面的文章


2.<aop:aspect>


当切面代码是自动注入的bean时 使用ref属性可直接使用

在上面我们可以看到对Timerprint类的注入:



3.<aop:pointcut>与execution

pointcut表示建立一个切点

execution则比较复杂

在这里先用execution(* *.*(..))代替

第一个出现的*表示:对任意的返回类型方法进行匹配 可以改为void int double之类以匹配特定返回类型函数

第二个出现的*表示:全类名

第三个出现的*表示:对任意的方法名进行匹配

最后出现的(..)表示:匹配任意类型、数量的参数 


4.<aop before>及其他

何时执行切面代码是需要我们进行配置的 比如在核心业务调用之前或者之后

一共有如下几种类型:


  • after 在方法完成之后调用通知 无论其是否执行成功
  • after-returning 在方法成功执行之后调用
  • after-throwing 在方法抛出异常之后调用
  • around 在之前与之后调用
  • before 在方法调用之前调用通知

5.method


如图 我们在之前的测试中均调用的是printTime方法 这是切面类中的某个方法

现在我们在切面类中加上一个方法:


此时修改method为:


运行测试类:


可以看到调用的通知函数不同


总结:

以上就是AOP的简单用法

以后会补充使用注解进行AOP的操作

另外需要注意的是Spring只支持方法连接点 只有在调用某个方法时 才能构建切入点


谢谢~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章