Spring学习笔记--Spring面向切面编程AOP之AspectJ开发

继续前一篇

这次来认识什么是AOP。。

AOP:Aspect-Oriented Programming 即面向切面编程,它是面向对象编程(OOP)的一种补充。

传统的业务处理代码中,通常都会进行事务处理,日志记录等操作。虽然使用OOP可以通过组合或者继承的方法来达到代码的重用,但如果要实现某个功能,同样的代码仍然会分散到各个方法中,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。为了解决这一问题,AOP思想随之产生。


1. AOP术语

术语解释
Aspect(切面)指封装的用于横向插入系统功能的类
Joinpoint(连接点)在程序执行过程中的某个连接点
Pointcut(切入点)指切面与程序流程的交叉点
Advice(通知/增强处理)AOP框架在特定的切入点执行的增强处理
Target Object(目标对象)指所有被通知的对象,也称为增强对象
Proxy(代理)将通知应用到目标对象之后,被动态创建的对象
Weaving(织入)将切面代码插入到目标对象上,从而产生代理对象的过程

2. AspectJ开发

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。使用AspectJ实现AOP有两种方式:

  • 基于XML的声明式AspectJ
  • 基于注解的声明式AspectJ
2.1 基于XML的声明式AspectJ

指通过XML文件来定义切面,切入点及通知,所有的切面,切入点和通知都必须定义在<aop:config>元素内。

<aop:config>的子元素

子元素描述
<aop:pointcut>配置全局切入点
<aop:advisor>配置通知器
<aop:aspect>配置切面

<aop:aspect>的子元素

子元素描述
<aop:pointcut>配置全局切入点
<aop:before>配置前置通知
<aop:after-returning>配置后置通知
<aop:around>配置环绕通知
<aop:after-throwing>配置异常通知
<aop:after>配置最终通知
<aop:declare-parents>配置引介通知

Spring配置文件中的<beans>元素下可以包含对个<aop:config>元素,一个<aop:config>元素中也可以包含属性和子元素,其子元素如上所示<aop:pointcut><aop:advisor><aop:aspect>,配置时,这3个元素必须按照此顺序来定义

接下来通过例子来演示(基于XML的声明式AspectJ):

  1. 创建Web项目,导入基本包和AspectJ框架相关的jar包

在这里插入图片描述

  1. 创建包(我创建的包com.xhh.dao),包中的类为目标类

UserDao.java

package com.xhh.dao;

public interface UserDao {

	public void addUser();
	public void deleteUser();
}

UserDaoImpl

package com.xhh.dao;

import org.springframework.stereotype.Repository;

public class UserDaoImpl implements UserDao {

	@Override
	public void addUser() {
		System.out.println("添加用户");
	}

	@Override
	public void deleteUser() {
		System.out.println("删除用户");
	}

}
  1. 创建包(我创建的包com.xhh.aspectJ.xml),在包中创建切面类MyAspect,在类中定义不同类型的通知。
  • Spring中的通知类型
    • 环绕通知:应用于日志,事务管理等功能
    • 前置通知:应用于权限管理等功能
    • 后置通知:应用于关闭流,上传文件,删除临时文件等功能
    • 异常通知:应用于处理异常记录日志等功能
    • 引介通知:应用于修改老版本程序

MyAspect.java

package com.xhh.aspectJ.xml;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {

	// 前置通知
	public void myBefore() {
		System.out.println("前置通知:模拟执行权限检查...");
	}
	
	// 后置通知
	public void myAfterReturning() {
		System.out.println("后置通知:模拟记录日志...");
	}
	
	// 环绕通知
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// 开始
		System.out.println("环绕开始,模拟开启事务");
		// 执行当前目标方法
		Object obj = proceedingJoinPoint.proceed();
		// 结束
		System.out.println("环绕结束,模拟关闭事务");
		return obj;
	}
	
	// 异常通知
	public void myAfterThrowing(Throwable e) {
		System.out.println("异常通知:" + e.getMessage());
	}
	
	// 最终通知
	public void myAfter() {
		System.out.println("最终通知:模拟方法结束释放资源");
	}
}

需要注意:环绕通知必须接受一个类型为ProceedingJoinPoint的参数,返回值也必须是Object类型,且必须抛出异常。

  1. 创建配置文件applicationContext.xml
    需要添加aop标签规范声明地址
    在这里插入图片描述
<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- 目标类 -->
	<bean id="userDao" class="com.xhh.dao.UserDaoImpl"></bean>
	<!-- 切面类 -->
	<bean id="myAspect" class="com.xhh.aspectJ.xml.MyAspect"></bean>
	<!-- aop编程 -->
	<aop:config>
		<!-- 配置切面 -->
		<aop:aspect ref="myAspect">

			<!-- 配置切入点 -->
			<aop:pointcut
				expression="execution(* com.xhh.dao.*.*(..))"
				id="myPointCut" />

			<!-- 前置通知 -->
			<aop:before method="myBefore" pointcut-ref="myPointCut" />

			<!-- 后置通知 -->
			<aop:after-returning method="myAfterReturning"
				pointcut-ref="myPointCut" returning="returnVal" />

			<!-- 环绕通知 -->
			<aop:around method="myAround" pointcut-ref="myPointCut" />

			<!-- 抛出异常通知 -->
			<aop:after-throwing method="myAfterThrowing"
				pointcut-ref="myPointCut" throwing="e" />

			<!-- 最终通知 -->
			<aop:after method="myAfter" pointcut-ref="myPointCut" />
		</aop:aspect>
	</aop:config>
</beans>


对配置文件讲解:

  • 配置切面:使用 <aop:aspect>元素,该属性会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean,如上代码中id为myAspect的Bean。定义完成后使用ref属性引用该Bean。

<aop:aspect>

属性描述
id用于定义该切面的唯一标识名称
ref用于引用普通的Spring Bean
  • 配置切入点:通过 <aop:pointcut>元素来定义的。当<aop:pointcut>作为<aop:config>的子元素定义时,表示该切入点是全局切入点,当<aop:pointcut>作为 <aop:aspect>的子元素定义时,表示该切入点自对当前切面有效。

<aop:pointcut>

属性描述
id用于指定切入点的唯一标识名称
expression用于指定切入点关联的切入点表达式

上述代码中,execution(* com.xhh.dao.*.*(..))就是定义的切入点表达式,意思是匹配com.xhh.dao包中任意类的任意方法的执行。

  • execution()为表达式的主体,第一个*表示的是返回类型,使用*代表所有类型
    第二个*表示的是类名,使用*代表所有类
    第三个*表示的是方法名,使用*代表所有方法
    后面(…)表示方法的参数,(…)表示任意参数

  • 配置通知:通过<aop:pointcut>的子元素配置了5中常用通知,使用时可以指定一些属性。

<aop:pointcut>的子元素

属性描述
pointcut指定一个切入点表达式
pointcut-ref指定一个已经存在的切入点名称
method指定一个方法名
throwing指定一个形参名,只对after-throwing 有效
returning指定一个形参名,只对after-returning 有效
  1. 创建测试类AspectXmlTest.java

AspectXmlTest.java

package com.xhh.aspectJ.xml;

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

import com.xhh.dao.UserDao;

public class AspectXmlTest {

	public static void main(String[] args) {
		ApplicationContext context = new
				ClassPathXmlApplicationContext("com/xhh/aspectJ/xml/applicationContext.xml");
		
		UserDao userDao = (UserDao) context.getBean("userDao");
		
		userDao.addUser();
	}
}

结果:
在这里插入图片描述

2.2 基于注解的声明式AspectJ

在Spring配置文件中配置了大量的代码信息,过于臃肿,还是注解好用。
AspectJ注解介绍

注解名称描述
@Aspect用于定义一个切面
@Pointcut用于定义切入点表达式
@Before用于定义前置通知
@AfterReturning用于定义后置通知
@Around用于定义环绕通知
@AfterThrowing用于定义异常通知来处理程序中未处理的异常
@After用于定义最终通知
  1. 创建包(我创建的包com.xhh.aspectJ.annotation),复制MyAspect到该包下,并添加相应注解。

MyAspect.java

package com.xhh.aspectJ.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

	// 定义切入点表达式,使用一个返回值为void,方法体为空的方法来命名切入点
	@Pointcut("execution(* com.xhh.dao.*.*(..))")
	private void myPointCut() {}
	
	// 前置通知
	@Before("myPointCut()")
	public void myBefore() {
		System.out.println("前置通知:模拟执行权限检查...");
	}
	
	// 后置通知
	@AfterReturning("myPointCut()")
	public void myAfterReturning() {
		System.out.println("后置通知:模拟记录日志...");
	}
	
	// 环绕通知
	@Around("myPointCut()")
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// 开始
		System.out.println("环绕开始,模拟开启事务");
		// 执行当前目标方法
		Object obj = proceedingJoinPoint.proceed();
		// 结束
		System.out.println("环绕结束,模拟关闭事务");
		return obj;
	}
	
	// 异常通知
	@AfterThrowing(value = "myPointCut()", throwing = "e")
	public void myAfterThrowing(Throwable e) {
		System.out.println("异常通知:" + e.getMessage());
	}
	
	// 最终通知
	@After("myPointCut()")
	public void myAfter() {
		System.out.println("最终通知:模拟方法结束释放资源");
	}
}

注意:@Aspect定义了一个切面类,由于该类在Spring中是作为Bean使用的,所以还需要添加@Component。

  1. 在目标类com.xhh.dao.UserDaoImpl添加注解:@Repository("userDao")
  2. 创建配置文件,记得添加相应标签规范context
    在这里插入图片描述
<?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-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<!-- 指定需要扫描的包,使注解生效 -->
	<context:component-scan base-package="com.xhh"></context:component-scan>
	
	<!-- 启动基于注解的声明式AspectJ支持 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>


  1. 创建测试类

AnnotationTest.java

package com.xhh.aspectJ.annotation;

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

import com.xhh.dao.UserDao;

public class AnnotationTest {

	public static void main(String[] args) {
		ApplicationContext context = new
				ClassPathXmlApplicationContext("com/xhh/aspectJ/annotation/applicationContext.xml");
		
		UserDao userDao = (UserDao) context.getBean("userDao");
		
		userDao.addUser();
	}
}

完成。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值