【spring】自定义AOP切面

在Spring中,实现AOP增强不仅可以使用@Aspect注解来实现,还可以通过自定义切面来实现。

下面来看看怎么自定义切面。

切面

切面需要实现PointcutAdvisor接口,一个切面必须包含切点和通知。

package com.morris.spring.advisor;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;

/**
 * 自定义Advisor
 */
public class CustomAdvisor extends AbstractPointcutAdvisor {

	@Override
	public Advice getAdvice() {
		return new CustomAdvice();
	}

	@Override
	public Pointcut getPointcut() {
		return new CustomPointcut();
	}
}

切点

切点需要实现Pointcut接口,里面要实现对类的匹配和方法的匹配。

这里不对类进行校验,所以使用Spring内部的ClassFilter.TRUE。

对方法的匹配需要实现MethodMatcher,所以CustomPointcut同时实现了MethodMatcher和Pointcut。

package com.morris.spring.advisor;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

import java.lang.reflect.Method;

public class CustomPointcut implements MethodMatcher, Pointcut {
	// implement from MethodMatcher
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		if(method.getName().startsWith("insert")) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isRuntime() {
		return true;
	}

	// 如果isRuntime返回true,则会在运行时调用此方法
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		if(null != args && null != args[0] && "morris".equals(args[0])) {
			System.out.println("matches  args");
			return true;
		}
		return false;
	}

	// implement from Pointcut
	@Override
	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return this;
	}
}

通知

通知主要是对目标方法的增强,这里只是打印。

package com.morris.spring.advisor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class CustomAdvice implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println(invocation.getMethod().getName()+"-------------------------");
		return invocation.proceed();
	}
}

测试类

package com.morris.spring.demo.aop;

import com.morris.spring.advisor.CustomAdvisor;
import com.morris.spring.service.UserService;
import com.morris.spring.service.UserServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 自定义Advisor的使用
 */
@Configuration
@EnableAspectJAutoProxy // 开启AOP
public class CustomAspectDemo {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.register(UserServiceImpl.class);
		applicationContext.register(CustomAdvisor.class); // 自定义切面
		applicationContext.register(CustomAspectDemo.class); // 开启AOP
		applicationContext.refresh();
		UserService userService = applicationContext.getBean(UserService.class);
		userService.insert("morris");
	}
}

运行结果如下:

matches  args
insert-------------------------
UserServiceImpl insert morris

发现通知中的增强代码执行了。

尝试获得目标方法上的注解信息

UserServiceImpl.insert()方法上面加上@Entity注解。

com.morris.spring.service.UserServiceImpl#insert

@Entity
@Override
public void insert(String name) {
	System.out.println("UserServiceImpl insert " + name);
}

com.morris.spring.advisor.CustomPointcut#matches

@Override
public boolean matches(Method method, Class<?> targetClass) {
	if(method.isAnnotationPresent(Entity.class)) {
		return true;
	}
	return false;
}

运行结果会发现通知中的增强代码并没有被执行,这是为什么呢?目标方法insert()上面明明有@Entity注解,为什么获取不到呢?

在matches()方法中打上断点,会发现这个方法会被调用两次,第一次会返回true,第二次返回false,为什么两次结果会不一样呢?

先来看一下这两次调用的调用时机:

  1. 第一次调用时,匹配方法,如果匹配上了就会生成代理对象,Method所在的类为com.morris.spring.service.UserServiceImpl。

  2. 第二次调用时,调用代理对象的方法时会再次匹配,因为有的方法不需要代理,Method所在的类为com.morris.spring.service.UserService。

第二次调用时Method是来自于UserService接口,而接口上面的方法是没有注解的,这点也可以从动态代理生成的类中看出:

static {
	try {
	...
    m3 = Class.forName("com.morris.spring.service.UserService").getMethod("insert", Class.forName("java.lang.String"));
	...
	} catch (NoSuchMethodException var2) {
	    throw new NoSuchMethodError(var2.getMessage());
	} catch (ClassNotFoundException var3) {
	    throw new NoClassDefFoundError(var3.getMessage());
	}
}

而UserService.insert()方法上面并没有@Entity注解,那么要怎么样才能获得注解的信息呢?Spring提供了下面的工具类:

com.morris.spring.advisor.CustomPointcut#matches

public boolean matches(Method method, Class<?> targetClass) {
	Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
	if(specificMethod.isAnnotationPresent(Entity.class)) {
		return true;
	}
	return false;
}

方法参数级别更细粒度的匹配

MethodMatcher还有两个方法:

boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);

这两个方法配合使用能在运行时对参数的值进行匹配。

public boolean isRuntime() {
	return true;
}

public boolean matches(Method method, Class<?> targetClass, Object... args) {
	if(null != args && null != args[0] && "morris".equals(args[0])) {
		System.out.println("matches  args");
		return true;
	}
	return false;
}

需要满足两个条件这个带方法参数的matches()才会执行(这个方法只会执行一次,真正调用时才会知道参数的具体值):

  1. 不带方法参数的matches()返回true。
  2. isRuntime()返回true。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

morris131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值