【Spring】Spring之初识AOP

一、AOP的概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。

二、AOP的相关术语

JoinPoint(连接点):可以被拦截到的方法;
Pointcut(切入点):在开发中真正被拦截到的方法;
Advice(通知/增强):在切入点执行的时候触发的其他方法,如权限校验、事务管理等;
Introduction(引介):与Advice是方法层面的增强不同的是,Introduction是类层面的增强;
Target(目标):被增强的对象;
Weaving(织入):将通知(Advice)应用到目标(Target)的过程;
Proxy(代理):将通知(Advice)应用到目标(Target)之后产生的代理对象;
Aspect(切面):多个通知(Advice)和切入点(Pointcut)的组合。

三、AOP作用

AOP可以用于权限校验、日志输出、性能监控、异常处理和事务管理等。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,简化代码的同时提高了开发的效率。假如不使用AOP,例如下面的业务逻辑层代码:

package cn.jingpengchong.calculator.service;

import org.springframework.stereotype.Service;

@Service
public class CalculatorService implements ICalculatorService {

	public int div(int a, int b) {
		System.out.println("日志:The div method begins");
		System.out.println("日志:The div method args:["+a+","+b+"]");
		return a/b;
	}
}

该例没有使用Spring的AOP功能,因此其主要方法只有“return a/b;”这一句,然而为了打印日志信息,却需要两行代码,显得特别臃肿。并且如果要是再扩展一个乘法功能,那么仍然需要写这两行代码,这是非常繁琐的。那么如何解决这么繁琐的事情呢?当然就是使用spring提供的AOP功能了。
使用Spring的基于Aspectj的AOP开发需要的jar包有:
1、Spring所需的基本的jar包:
在这里插入图片描述
2、Spring日志输出规范包:
在这里插入图片描述
3、Log4j的相关jar包:
在这里插入图片描述
4、Spring的AOP的jar包:
在这里插入图片描述
5、AOP联盟的jar包:
在这里插入图片描述
6、Aspectj的jar包:
在这里插入图片描述
7、Spring与Aspectj的整合包:
在这里插入图片描述

四、利用注解方式使用AOP

将所需要的jar包导入当前工程之后,首先在xml配置文件中加入“<aop:aspectj-autoproxy/>”标签,其次编写切面Aspect和通知Advice,在类前面添加@Aspect注解就告知了Spring该类是一个切面,在方法前面添加对应通知的注解就表明该方法是一个通知(或叫做增强),该注解的value属性声明的表达式匹配到的方法被称为切入点,当这些方法得执行时会触发其所对应的通知。

1、切入点前的注解有以下几种:
  • @Before(value=""):前置处理,用于在该注解匹配的方法执行前进行一些操作;
  • @After(value=""):后置处理,用于在该注解匹配的方法执行后进行一些操作;
  • @AfterReturning(value="",returning=""):返回处理,用于在该注解匹配的方法返回结果后进行一些操作;
  • @AfterThrowing(value="",throwing=""):异常处理,用于在该注解匹配的方法返回异常后进行一些操作;
  • @Around(value=""):环绕处理,可以根据需求自编代码在相关环节进行处理。

注:

  • value属性用于匹配要处理的方法;
  • returning属性用于接收返回值;
  • throwing属性用于接收异常对象;
  • @After与@AfterReturning的区别:
    • @After注解的方法先于@AfterReturning注解的方法执行;
    • @在遇到异常时,After注解的方法仍执行,而@AfterReturning注解的方法不执行。
2、例:分别使用@Before、@After、@AfterReturning和@Around注解对上面的service层方法执行的各个环节进行处理

a.将service层方法打印日志的代码删了

package cn.jingpengchong.calculator.service;

import org.springframework.stereotype.Service;

@Service
public class CalculatorService implements ICalculatorService {

	public int div(int a, int b) {
		return a/b;
	}
}

b.在spring xml文件中添加该标签:自动代理

<aop:aspectj-autoproxy/>

c.新建独立模块“cn.jingpengchong.aspect”,在其中新建文件“CalculatorAspect.java”如下:

package cn.jingpengchong.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CalculatorAspect {

	//前置处理
	@Before("execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))")
	public void before(JoinPoint jp) {
		Object[] args = jp.getArgs();
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("日志:The " + name + " method begins");
		System.out.println("日志:The " + name + " method args:[" + args[0] + "," + args[1] + "]");
	}
	
	//后置处理
	@After("execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))")
	public void after(JoinPoint jp) {
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("日志:The " + name + " method ends");
	}
	
	//返回处理
	@AfterReturning(value = "execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))", returning = "result")
	public void afterReturning(JoinPoint jp, Object result) {
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("日志:The " + name + " method result:" + result);
	}
	
	//异常处理
	@AfterThrowing(value = "execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))", throwing = "e")
	public void afterThrowing(JoinPoint jp, Exception e) {
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("日志:The " + name + " method exception:" + e);
	}
}

d.编写测试类:

package cn.jingpengchong.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.jingpengchong.calculator.service.ICalculatorService;

public class Test {
	
	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		ICalculatorService calculatorService = applicationContext.getBean(ICalculatorService.class);
		System.out.println(calculatorService.div(6, 2));
		applicationContext.close();
	}
}

运行结果如下:
在这里插入图片描述

附:

1、上面“CalculatorAspect.java”文件中每个方法都要写带有很长参数的注解,我们可以在该文件中将切入点的表达式提取出来放在一个空方法上,其作用类似于一个全局变量:

@Pointcut("execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))")
public void pointCut() {		
}

这样的话上面的注解的参数就可以写成“pointCut()”,进一步简化了代码。
2、如果觉得在“CalculatorAspect.java”文件中定义四个方法太繁琐,那么可以用环绕注解@Around将处理过程封装在一个方法中:

//环绕处理
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) {
	Object result = null;
	Object[] args = pjp.getArgs();
	String name = pjp.getSignature().getName();
	try {
		try {
			//前置处理
			System.out.println("The " + name + " method begins");
			System.out.println("The " + name + " method args:[" + args[0] + "," + args[1] + "]");
			result = pjp.proceed();
		}finally {
			//后置处理
			System.out.println("The " + name + " method ends");
		}
		//返回处理
		System.out.println("The " + name + " method result:" + result);
	} catch (Throwable e) {
		//异常处理
		System.out.println("The " + name + " method exception:" + e);
	}
	return result;
}
五、利用Spring的xml配置文件使用AOP

同样需要将所需jar包导入当前工程

1、仍然以处理该service层方法的日志为例:
package cn.jingpengchong.calculator.service;

import org.springframework.stereotype.Service;

@Service
public class CalculatorService implements ICalculatorService {
	public int div(int a, int b) {
		return a/b;
	}
}
2、新建独立模块“cn.jingpengchong.aspect”,在其中新建文件“LogAspect.java”如下:
package cn.jingpengchong.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;

public class LogAspect {

	public void before(JoinPoint jp) {
		Object[] args = jp.getArgs();
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("日志:The " + name + " method begins");
		System.out.println("日志:The " + name + " method args:[" + args[0] + "," + args[1] + "]");
	}
}
3、在spring的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: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.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="cn.jingpengchong"></context:component-scan>
 
	<bean id = "logAspect" class = "cn.jingpengchong.aspect.LogAspect"/>
	
	<aop:config>
		<!--该标签是为了简化代码,指定一个id来表示该表达式,下面再引用时,直接用该id就行了-->
		<aop:pointcut expression="execution(public int cn.jingpengchong.calculator.service.CalculatorService.*(..))" id="pointCut"/>
		<aop:aspect ref="logAspect">
			<!--以前置处理为例,其他处理类似:
			method表明触发哪个通知;
			pointcut-ref引入表达式;
			pointcut生命表达式-->
			<aop:before method="before" pointcut-ref="pointCut"/>
		</aop:aspect>
	</aop:config>
</beans>
4、编写测试类:
package cn.jingpengchong.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.jingpengchong.calculator.service.ICalculatorService;

public class Test {
	
	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		ICalculatorService calculatorService = applicationContext.getBean(ICalculatorService.class);
		System.out.println(calculatorService.div(6, 2));
		applicationContext.close();
	}
}

运行结果如下:
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值