Skip to content

Latest commit

 

History

History
 
 

spring-boot-8-AOP

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

image

  • 关于AOP

    • AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。 OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候, OOP则显得无能为力。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象 层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处 的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    • AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到 一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封 装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象” 是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部 的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

    • 使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分 是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的 作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思 想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

    • 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行; 二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

  • AOP使用场景

    • Authentication 权限
    • Caching 缓存
    • Context passing 内容传递
    • Error handling 错误处理
    • Lazy loading 懒加载
    • Debugging  调试
    • logging, tracing, profiling and monitoring 记录跟踪 优化 校准
    • Performance optimization 性能优化
    • Persistence  持久化
    • Resource pooling 资源池
    • Synchronization 同步
    • Transactions 事务
  • Spring AOP实战使用

package cn.timebusker.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

import cn.timebusker.utils.DateUtil;

/**
 * 面向切面编程 主要功能:日志记录、性能统计、安全控制、事务处理、异常处理等等
 */
@Component
@Aspect
//使用 order对多个切面进行排序,参数越小,越在前
@Order(1)
public class LoggerHandlerAop {

	private final static Logger logger = LoggerFactory.getLogger(LoggerHandlerAop.class);

	/**
	 * AOP切面中的同步问题:用于监控业务处理性能
	 */
	ThreadLocal<Long> startTime = new ThreadLocal<>();

// ==========================================================匹配切点表达式========================================================================================
/**
 * 一、execution(方法表达式)
 * 1、匹配方法时,只能匹配到实现类,匹配到接口类不能成功
 * 2、匹配方法执行
 */
	// 匹配cn.timebusker.service包及子包下的任何方法执行
	@Pointcut(value="execution(* cn.timebusker.service.*.*(..))")
	public void log1() {
	}
	
	// 匹配任何包下的service包及子包下的任何方法执行(该模式只能匹配到一级的子包,多级子包不适用)
	@Pointcut(value="execution(* *..service.*.*(..))")
	public void log2() {
	}
	
	// 匹配任何包下的service包及子包下的任何方法执行(该模式能匹配到任何多级的子包下的方法执行)
	@Pointcut(value = "execution(* *..service..*(..))")
	public void log3() {
	}
	
	// 匹配返回值类型为java.lang.String的任何包下的service包及子包下的方法执行
	@Pointcut(value = "execution(java.lang.String *..service..*(..))")
	public void log4() {
	}
	
	// 匹配返回值类型为int的任何包下的service包及子包下的方法执行
	@Pointcut(value = "execution(int *..service..*(..))")
	public void log5() {
	}
	
	// 匹配任何返回值类型的cn.timebusker包及任何子包下的以add开头的参数为Strign类型的方法执行
	@Pointcut(value = "execution(* cn.timebusker..add*(String))")
	public void log6() {
	}
	
	// 匹配 OR、AND
	@Pointcut(value = "execution(* cn.timebusker.service.*.add*(int))")
	public void log7() {
	}
	
	// 匹配 OR、AND、
	@Pointcut(value = "execution(* cn.timebusker.service.*.add*(int)) OR execution(* cn.timebusker..add*(String))")
	public void log8() {
	}
	
/**
 * 二、within(类型表达式)
 * 1、匹配类型时,只能匹配到实现类,匹配到接口类不能成功
 * 2、匹配指定类型内的方法执行;
 */
	// 匹配指定类型内的方法执行--只能匹配类型
	@Pointcut(value = "within(cn.timebusker.service.order.Impl.OrderInfoServiceImpl)")
	public void logw1() {
	}
	
	// 匹配指定类型内的方法执行(包下所有的类)
	@Pointcut(value = "within(cn.timebusker.service.order.Impl.*)")
	public void logw2() {
	}

/**
 * 三、this(类型全限定名)
 * 1、可以直接匹配接口类型完成  类型全名限定匹配
 * 2、注意是AOP代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;注意this中使用的表达式必须是类型全限定名,不支持通配符
 */
	// 匹配指定类型内的方法执行(包下所有的类)
	@Pointcut(value = "this(cn.timebusker.service.order.OrderInfoService)")
	public void logt1() {
	}
	
/**
* 四、target(类型全限定名)--匹配当前目标对象类型的执行方法
* 1、可以直接匹配接口类型完成  类型全名限定匹配
* 2、注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意target中使用的表达式必须是类型全限定名,不支持通配符
*/
		// 匹配指定类型内的方法执行(包下所有的类)
		@Pointcut(value = "target(cn.timebusker.service.order.OrderInfoService)")
		public void logt2() {
		}
		
/**
* 五、args(参数类型列表)--匹配当前执行的方法传入的参数为指定类型的执行方法
* 1、注意是匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,通配符不支持;
* 2、args属于动态切入点,是在运行时动态匹配的,这种切入点开销非常大,非特殊情况最好不要使用;
* 3、此处不作示例
*/
		
/**
* 六、@within(注解类型)--匹配所以持有指定注解类型内的方法;注解类型也必须是全限定类型名;
* 1、注解类型也必须是全限定类型名;
*/
		// 匹配被org.springframework.stereotype.Service这个注解标注的类----注解标注在接口上不起作用
		@Pointcut(value = "@within(org.springframework.stereotype.Service)")
		public void logaw1() {
		}
		
		// 匹配 自定义注解标注的类----注解标注在接口上不起作用
		@Pointcut(value = "@within(cn.timebusker.annotation.timebuskerBean)")
		public void logaw2() {
		}
		
/**
* 七、@target(注解类型)--匹配当前目标对象类型的执行方法
* 1、目标对象持有指定的注解;
* 2、注解类型也必须是全限定类型名;
* 3、此处不作示例
*/		

/**
* 八、@annotation(注解类型)--匹配当前执行方法持有指定注解的方法
* 1、注解类型也必须是全限定类型名;
*/	
		// 匹配 自定义注解标注的类----注解标注在接口的方法上不起作用
		@Pointcut(value = "@annotation(cn.timebusker.annotation.timebuskerMethod)")
		public void logaa1() {
		}

// ==========================================================匹配切点表达式========================================================================================

	/**
	 * 前置通知:在连接点执行前的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
	 */

// 引用多个切入点
//	@Before("log6() OR log7()")
// 引用单个切入点
	@Before("logaa1()")
	public void beforeAdvice(JoinPoint point) {
		logger.info("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS");
		logger.info("LoggerHandlerAop.beforeAdvice...time:" + DateUtil.now());
		Signature signature = point.getSignature();
		logger.info("所属类名称:" + signature.getDeclaringTypeName() + "\n代理类:" + signature.getClass() + "\n方法名称:" + signature.getName() + "\n所属类:" + signature.getDeclaringType());
		Object[] args = point.getArgs();
		logger.info("参数是:" + JSON.toJSONString(args));
		logger.info("被织入的对象是:" + point.getTarget());
		logger.info("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
	}

	/**
	 * 返回后通知:在连接点正常执行完后执行的通知
	 */
	@AfterReturning("logaa1()")
	public void afterReturningAdvice() {
		logger.info("LoggerHandlerAop.afterReturningAdvice...time:" + DateUtil.now());
	}

	/**
	 * 抛出异常后通知:在连接节点抛出异常退出时执行的通知
	 */
	@AfterThrowing("logaa1()")
	public void afterThrowingAdvice() {
		logger.info("LoggerHandlerAop.afterThrowingAdvice...time:" + DateUtil.now());
	}

	/**
	 * 后置通知:当某连接节点退出的时候执行的通知(不论是正常返回还是异常退出)
	 */
	@After("logaa1()")
	public void afterFinallyAdvice() {
		logger.info("LoggerHandlerAop.afterFinallyAdvice...time:" + DateUtil.now());
	}

	/**
	 * 环绕通知:包围一个连接点(join point)的通知
	 * 
	 * @param pjp
	 * @return
	 */
	@Around("logaa1()")
	public Object aroundAdvice(ProceedingJoinPoint pjp) {
		logger.info("LoggerHandlerAop.aroundAdvice...time:" + DateUtil.now());
		Object obj = null;
		try {
			System.out.println("MoocAspect around 1.");
			obj = pjp.proceed();
			System.out.println("MoocAspect around 2.");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return obj;
	}
}


相关文章