目录

一、spring aop简介与应用

二、动态代理介绍

三、简单源码分析


一、spring aop简介与应用

1. Spring AOP的几个概念

  • 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  • 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  • 增强(Advice):增强就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
  • 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
  • 织入(Weaving):将Aspect加入到程序代码的过程,对于SpringAOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
  • 目标对象(Target):就是被AOP动态代理的目标对象;

2. 增强类型

  • 前置增强(@Before):在目标方法执行前实施增强。
  • 后置增强(@AfterReturning,@After):AfterReturning在目标方法正常执行后实施增强,如果方法抛出异常不会执行;@After增强的方法则无论是否有异常都会执行,类似finally方法。类似于finally代码块,只要应用了无论什么情况下都会执行
  • 环绕增强(@Around):在目标方法执行前后实施增强。直接使用了AOP联盟定义的接口。
  • 异常抛出增强(@AfterThrowing):在目标方法抛出异常后实施增强。
  • 引介增强():在目标类中添加一些新的方法和属性,连接点是类级别的而非方法级别的。

3. 切点表达式

内容项

示例

任意公共方法的执行

execution(public * *(..))

任何一个以“set”开始的方法的执行

execution(* set*(..))

DemoService接口的任意方法的执行

execution(* com.demo.service.DemoService.*(..))

定义在service包里的任意方法的执行

execution(* com.demo.service..(..))

定义在service包和所有子包里的任意类的任意方法的执行

execution(* com.demo.service...(..))

定义在demo包和所有子包里的DemoDao类的任意方法的执行

execution(* com.demo..DemoDao.*(..))

二、动态代理介绍

1. jdk动态代理

JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类

// 接口
public interface InterfaceBean {
    String sayHello(String something);
}

// 实现
public class InterfaceBeanImpl implements InterfaceBean {
    @Override
    public String sayHello(String something) {
        System.out.println("hello " + something);
        return "OK";
    }
}

// Jdk动态代理
public class JdkDynamicProxy implements InvocationHandler {

    private InterfaceBean target;

    public JdkDynamicProxy(InterfaceBean target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("sayHello".equals(method.getName())) {
            Logger logger = LoggerFactory.getLogger(JdkDynamicProxy.class);
            logger.info("jdk dynamic proxy is applyed");
        }
        return method.invoke(target, args);
    }
}

// 测试类
public class ProxyTest {

    @Test
    public void testJdkProxy() {
        InterfaceBean bean = (InterfaceBean) Proxy.newProxyInstance(
            InterfaceBean.class.getClassLoader(),
            new Class<?>[] {InterfaceBean.class},
            new JdkDynamicProxy(new InterfaceBeanImpl())
        );

        bean.sayHello("double six six six");

    }
}

上述代码的关键是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)方法,该方法会根据指定的参数动态创建代理对象。三个参数的意义如下:

  • loader,指定代理对象的类加载器;
  • interfaces,代理对象需要实现的接口,可以同时指定多个接口;
  • handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里(*注意1)。

newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。

2. cglib动态代理

CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。

// 目标类
public class ClassBean {
    
    public String print(String something) {
        return something;
    }

}

// cglib代理
public class CglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Logger logger = LoggerFactory.getLogger(CglibProxy.class);
        logger.info("cglib proxy is applyed");
        return proxy.invokeSuper(obj, args);
    }
}

// 测试类
public class ProxyTest {

    @Test
    public void testCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ClassBean.class);
        enhancer.setCallback(new CglibProxy());

        ClassBean bean = (ClassBean)enhancer.create();
        bean.print("double six six six");
    }
}

上述代码中,我们通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper()方法,我们将调用转发给原始对象。

3. 总结

Jdk动态代理获取的对象,实际是Proxy子类,且实现了代理类接口;

Cglib动态代理获取的代理对象,是代理类的子类,

三、简单源码分析

1. 解析增强

ReflectiveAspectJAdvisorFactory.getAdvice()

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
		MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {

	Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
	validate(candidateAspectClass);

	AspectJAnnotation<?> aspectJAnnotation =
			AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
	if (aspectJAnnotation == null) {
		return null;
	}

	// If we get here, we know we have an AspectJ method.
	// Check that it's an AspectJ-annotated class
	if (!isAspect(candidateAspectClass)) {
		throw new AopConfigException("Advice must be declared inside an aspect type: " +
				"Offending method '" + candidateAdviceMethod + "' in class [" +
				candidateAspectClass.getName() + "]");
	}

	if (logger.isDebugEnabled()) {
		logger.debug("Found AspectJ method: " + candidateAdviceMethod);
	}

	AbstractAspectJAdvice springAdvice;

	switch (aspectJAnnotation.getAnnotationType()) {
		case AtBefore:
			springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
			break;
		case AtAfter:
			springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
			break;
		case AtAfterReturning:
			springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
			AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterReturningAnnotation.returning())) {
				springAdvice.setReturningName(afterReturningAnnotation.returning());
			}
			break;
		case AtAfterThrowing:
			springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
			AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
				springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
			}
			break;
		case AtAround:
			springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
			break;
		case AtPointcut:
			if (logger.isDebugEnabled()) {
				logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
			}
			return null;
		default:
			throw new UnsupportedOperationException(
					"Unsupported advice type on method " + candidateAdviceMethod);
	}
    ......
}

2. 生成代理

DefaultAopProxyFactory.createAopProxy()

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

JdkDynamicAopProxy.getProxy()

public Object getProxy(ClassLoader classLoader) {
	if (logger.isDebugEnabled()) {
		logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

3. 执行增强方法

JdkDynamicAopProxy.invoke()

 
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	MethodInvocation invocation;
	Object oldProxy = null;
	boolean setProxyContext = false;

	TargetSource targetSource = this.advised.targetSource;
	Class<?> targetClass = null;
	Object target = null;

	try {
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			// The target does not implement the equals(Object) method itself.
			return equals(args[0]);
		}
		if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
			// The target does not implement the hashCode() method itself.
			return hashCode();
		}
		if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
				method.getDeclaringClass().isAssignableFrom(Advised.class)) {
			// Service invocations on ProxyConfig with the proxy config...
			return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}

		Object retVal;

		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}

		// May be null. Get as late as possible to minimize the time we "own" the target,
		// in case it comes from a pool.
		target = targetSource.getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}

		// Get the interception chain for this method.
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		// Check whether we have any advice. If we don't, we can fallback on direct
		// reflective invocation of the target, and avoid creating a MethodInvocation.
		if (chain.isEmpty()) {
			// We can skip creating a MethodInvocation: just invoke the target directly
			// Note that the final invoker must be an InvokerInterceptor so we know it does
			// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			// We need to create a method invocation...
			invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			// Proceed to the joinpoint through the interceptor chain.
			retVal = invocation.proceed();
		}

		// Massage return value if necessary.
		Class<?> returnType = method.getReturnType();
		if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
				!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
			// Special case: it returned "this" and the return type of the method
			// is type-compatible. Note that we can't help if the target sets
			// a reference to itself in another returned object.
			retVal = proxy;
		}
		else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
			throw new AopInvocationException(
					"Null return value from advice does not match primitive return type for: " + method);
		}
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			// Must have come from TargetSource.
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}








CglibAopProxy->DynamicAdvisedInterceptor.intercept()

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) 	throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	Class<?> targetClass = null;
	Object target = null;
	try {
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
		// May be null. Get as late as possible to minimize the time we
		// "own" the target, in case it comes from a pool...
		target = getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// Check whether we only have one InvokerInterceptor: that is,
		// no real advice, but just reflective invocation of the target.
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// We can skip creating a MethodInvocation: just invoke the target directly.
			// Note that the final invoker must be an InvokerInterceptor, so we know
			// it does nothing but a reflective operation on the target, and no hot
			// swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null) {
			releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

参考链接

posted on 2023-04-25 14:11  vow007  阅读(4)  评论(0编辑  收藏  举报  来源