Spring Aop

274 阅读3分钟

AOP 是什么?

面向切面编程(Aspect Oriented Programing)

应用场景

适用于具有横切逻辑的场合,如性能检测、访问控制、事务管理及日志记。

AOP

两种拦截方式

  • 基于注解
  • 基于方法

连接点 JoinPoint

Spring 近支持方法的连接点,能在方法调用前、方法调用后、方法抛出异常时及方法调用前后这些执行点织入增强

连接点是客观存在的方法

切点 Pointcut

指定在哪些类的哪些方法上织入横切逻辑
一个切点可以匹配多个连接点
对应连接点表达式

增强 Advice

增强是织入目标类连接点上的横切逻辑代码,包含定位连接点的方位信息

  • 前置增强:方法执行前增强 @Before("logPointCut()")
  • 后置增强:方法执行后增强 @After("logPointCut()")
  • 环绕增强:方法执行前后增强 @Around("logPointCut()")
  • 异常抛出增强:方法抛出异常后增强 @AfterThrowing()
  • 引介增强:在目标类添加新的方法和属性

切面 Aspect

切面由切点和增强组成。对应一个类

织入

织入是将增强添加到目标类的具体连接点上的过程。
Spring采用动态代理织入,AspectJ采用编译器织入和类装载期织入。

实例


@Slf4j
@Aspect
@Component
public class LogAspect {
	
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Pointcut("execution(public * com.pengtech.school.*.controller..*.*(..))")
    public void logPointCut(){
    }
    
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    	// 接收到请求,记录请求内容
    	ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    	HttpServletRequest request = attributes.getRequest();
    	startTime.set(System.currentTimeMillis());
    	log.info("########请求地址:" + request.getRequestURL());
    	log.info("########开始时间:" + LocalDateTime.now());
    	log.info("########请求方法:" + joinPoint.getSignature().getDeclaringTypeName() +
    		"." + joinPoint.getSignature().getName());
    	Object[] args = joinPoint.getArgs();
    	for (Object param : args) {
    		log.info("########方法参数:" + param);
    	}
    }
    
    @After("logPointCut()")
    public void doAfter() throws Throwable {
    	log.info("#######执行间隔:" + (System.currentTimeMillis() - startTime.get())
    			+ "ms");
    	log.info("#######完成时间:" + LocalDateTime.now());
    	
    }
}

AOP 面向切面编程

概述

适合具有横切逻辑的应用场合,如性能监测、访问控制、事务管理和日志记录。

Spring AOP使用动态代理技术在运行期向目标类织入增强的代码。

Spring AOP 两种代理机制

  • 基于JDK的动态代理
  • 基于CGLib的动态代理
JDK 动态代理
在运行期创建接口的代理实例
限制:只能为接口创建代理实例

// InvocationHandler 实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
public class PerformanceHandler implements InvocationHandler {
    // 目标业务类
	private Object target;
	
	public PerformanceHandler(Object target){
		this.target = target;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		// 横切代码
		PerformanceMonitor.begin(target.getClass().getName()+"."+ method.getName());
		// 反射调用目标类的目标方法
		Object object = method.invoke(target, args);
		// 横切代码
		PerformanceMonitor.end();
		return object;
	}
}
Proxy 利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象

// 使用JDK动态代理

    // 被代理的目标业务类
	ForumService target = new ForumServiceImpl();
	// 将目标业务类和横切代码编织到一起
	PerformanceHandler handler = new PerformanceHandler(target);
	// 创建代理实例
	ForumService proxy = (ForumService) Proxy.newProxyInstance(target
			.getClass().getClassLoader(),
			target.getClass().getInterfaces(), handler);
	// 调用代理方法		
	proxy.removeForum(10);
	proxy.removeTopic(1012);

CGLib 动态代理

动态创建子类的方式生成代理对象
不能对final或private方法进行代理

//实现MethodInterceptor接口,并重写intercept方法
public class CglibProxy implements MethodInterceptor {
	private Enhancer enhancer = new Enhancer();

    //创建动态代理对象
	public Object getProxy(Class clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

    // 拦截所有目标类方法的调用
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
	                        MethodProxy proxy) throws Throwable {
		PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
		Object result=proxy.invokeSuper(obj, args);
		PerformanceMonitor.end();
		return result;
	}
	
	//动态生成子类的方式创建代理类
	CglibProxy cglibProxy = new CglibProxy();
	ForumService forumService = (ForumService)cglibProxy.getProxy(ForumServiceImpl.class);
	forumService.removeForum(10);
	forumService.removeTopic(1023);
}