zoukankan      html  css  js  c++  java
  • Spring 切面、注解使用及源码分析

    基本概念

    切面类

    Spring切面类,@Aspect,可以在不侵入代码的情况下执行,如日志的打印。通常定义一个切面类使用@Aspect,@Component注解。定义好一个切面类。

    切点

    切点@PointCut,切点的定义通常使用该注解,一般有两种方式

    1.@PointCut("execution(public * com.test.aspect..*.*(..))"),第一个*表示匹配任意的方法返回值,..表示另个或多个;

    2.@PointCut("@annotation(com.test.utils.log)"),这种请求需要去定义一个注解,在方法上加注解即可打印日志;

    增强

    增强处理,Advice,主要有五个注解:

    @Before,在切点方法执行之前执行

    @After,在切点方法执行之后执行

    @AfterReturning,在切点方法返回后执行

    @AfterThrowing,在切点方法抛异常执行

    @Aroud,能控制切点执行前,执行后

    定义切面示例如下:

    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Service;
    
    @Aspect
    @Service
    public class LogPrint {
    
        @Pointcut("execution(public * com.test.demo.TestMainService.printLogPointCut())")
        private void logPrintPointCut(){
    
            System.out.println("logPrintPointCut log");
        }
    
        @Around("execution(public * com.test.demo.TestMainService.printLogAroud())")
        private void logPrintAroud(){
    
            System.out.println("logPrintAroud log");
        }
    
        @Before("execution(public * com.test.demo.TestMainService.printLogBefore())")
        private void logPrintBefore(){
    
            System.out.println("logPrintBefore log");
        }
    }  

    其他参数处理,比如需要获取方法的入参、出参等,使用JoinPoint可以获取;

    ProceedingJoinPoint,继承了JoinPoint用于支持环绕通知,增加了proceed方法,用于启动目标方法执行;示例如下

    @Aspect
    @Service
    public class LogPrint {
    
        @Around("execution(public * com.test.demo.TestMainService.printLogAroud(..))")   //注意黄色的..,这个地方掉坑里很久,加了参数后直接切面不生效了
        private void logPrintAroud(ProceedingJoinPoint pjp) throws Throwable {
    
    //获取参数 Object[] args = pjp.getArgs(); System.out.println("logPrintAroud log "+args[0]); //执行方法 Object result = pjp.proceed(); System.out.println("logPrintAroud log "+result); } }

     执行结果如下

    logPrintAroud log aaaa
    printLogAroud 
    logPrintAroud log result
    

      

    除了定义具体类,也可以定义一个注解,在方法的上添加注解打印;

    @PointCut("@annotation(com.test.utils.log)

    切点注解示例

    定义一个注解

    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LogAnnotation {
    
        String value() default "";
    
    }

     定义切面

    @Around("@annotation(com.test.demo.LogAnnotation))")
        private void logAnnoAroud(ProceedingJoinPoint pjp) throws Throwable {
    
            Object[] args = pjp.getArgs();
            System.out.println("logAnnoAroud log "+args[0]);
    
            //执行方法
            Object result = pjp.proceed();
            System.out.println("logAnnoAroud log "+result);
        }

    在类上使用注解

    @LogAnnotation("anno")
        public String logAnnoAroud(String id){
    
            System.out.println("logAnnoAroud ");
            return "annotation";
        }
    达到一样的效果

    源码分析

    第一步,搜索aspect对应的parse类

    第二步,查看parser类,目的就是为了注册一个bean类

    class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
    
    	@Override
    	@Nullable
    	public BeanDefinition parse(Element element, ParserContext parserContext) {
    //注册bean类		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    		extendBeanDefinition(element, parserContext);
    		return null;
    	}
    
    ...
    @Nullable
    	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
    	}
    
    	@Nullable
    	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    //注册bean到容器中
    		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    	}
    
    ...
    
    @Nullable
    	private static BeanDefinition registerOrEscalateApcAsRequired(
    			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
    		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
    		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
    			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
    			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
    				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
    				int requiredPriority = findPriorityForClass(cls);
    				if (currentPriority < requiredPriority) {
    					apcDefinition.setBeanClassName(cls.getName());
    				}
    			}
    			return null;
    		}
    
    		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    		beanDefinition.setSource(source);
    		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    		return beanDefinition;
    	}
    

     第三步,AnnotationAwareAspectJAutoProxyCreator,继承关系

    第四步,查看AbstractAutoProxyCreator,继承了BeanPostProcessor(在bean初始化前后使用)

    /** bean初始化后进行封装
    	 * Create a proxy with the configured interceptors if the bean is
    	 * identified as one to proxy by the subclass.
    	 * @see #getAdvicesAndAdvisorsForBean
    	 */
    	@Override
    	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    		if (bean != null) {
    			Object cacheKey = getCacheKey(bean.getClass(), beanName);
    			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    				return wrapIfNecessary(bean, beanName, cacheKey);
    			}
    		}
    		return bean;
    	}
    
    ...
    /**
    	 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
    	 * @param bean the raw bean instance
    	 * @param beanName the name of the bean
    	 * @param cacheKey the cache key for metadata access
    	 * @return a proxy wrapping the bean, or the raw bean instance as-is
    	 */
    	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    			return bean;
    		}
    		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    			return bean;
    		}
    		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    			this.advisedBeans.put(cacheKey, Boolean.FALSE);
    			return bean;
    		}
    
    		// Create proxy if we have advice.,如果有切面则创建代理
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
    			Object proxy = createProxy(
    					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    //直接返回代理,而不是返回bean
    			return proxy;
    		}
    
    		this.advisedBeans.put(cacheKey, Boolean.FALSE);
    		return bean;
    	}
    
    ...
    /** 创建代理,返回bean的代理类
    	 * Create an AOP proxy for the given bean.
    	 * @param beanClass the class of the bean
    	 * @param beanName the name of the bean
    	 * @param specificInterceptors the set of interceptors that is
    	 * specific to this bean (may be empty, but not null)
    	 * @param targetSource the TargetSource for the proxy,
    	 * already pre-configured to access the bean
    	 * @return the AOP proxy for the bean
    	 * @see #buildAdvisors
    	 */
    	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
    		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
    			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    		}
    
    		ProxyFactory proxyFactory = new ProxyFactory();
    		proxyFactory.copyFrom(this);
    
    		if (!proxyFactory.isProxyTargetClass()) {
    			if (shouldProxyTargetClass(beanClass, beanName)) {
    				proxyFactory.setProxyTargetClass(true);
    			}
    			else {
    				evaluateProxyInterfaces(beanClass, proxyFactory);
    			}
    		}
    
    		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    		proxyFactory.addAdvisors(advisors);
    		proxyFactory.setTargetSource(targetSource);
    		customizeProxyFactory(proxyFactory);
    
    		proxyFactory.setFrozen(this.freezeProxy);
    		if (advisorsPreFiltered()) {
    			proxyFactory.setPreFiltered(true);
    		}
    		return proxyFactory.getProxy(getProxyClassLoader());
    	}
    

      第五步,getProxy创建proxy,本例具体的使用的代理类为JdkDynamicAopProxy,创建JDK代理

    @Override
    	public Object getProxy(@Nullable ClassLoader classLoader) {
    		if (logger.isTraceEnabled()) {
    			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    		}
    		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
    	}
    

      第六步,查看具体代理类的invoke方法,看是如果代理bean执行的

    /**
    	 * Implementation of {@code InvocationHandler.invoke}.
    	 * <p>Callers will see exactly the exception thrown by the target,
    	 * unless a hook method throws an exception.
    	 */
    	@Override
    	@Nullable
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object oldProxy = null;
    		boolean setProxyContext = false;
    
    		TargetSource targetSource = this.advised.targetSource;
    		Object target = null;
    
    		try {
    			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    				// The target does not implement the equals(Object) method itself.
    				return equals(args[0]);
    			}
    			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
    				// The target does not implement the hashCode() method itself.
    				return hashCode();
    			}
    			else if (method.getDeclaringClass() == DecoratingProxy.class) {
    				// There is only getDecoratedClass() declared -> dispatch to proxy config.
    				return AopProxyUtils.ultimateTargetClass(this.advised);
    			}
    			else 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;
    			}
    
    			// Get as late as possible to minimize the time we "own" the target,
    			// in case it comes from a pool.
    			target = targetSource.getTarget();
    			Class<?> targetClass = (target != null ? target.getClass() : null);
    
    			// 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...
    				MethodInvocation 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 != Object.class && 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);
    			}
    		}
    	}
    

      具体的proceed方法

    @Override
    	@Nullable
    	public Object proceed() throws Throwable {
    		// We start with an index of -1 and increment early.
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			return invokeJoinpoint();
    		}
    
    		Object interceptorOrInterceptionAdvice =
    				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			// Evaluate dynamic method matcher here: static part will already have
    			// been evaluated and found to match.
    			InterceptorAndDynamicMethodMatcher dm =
    					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    				return dm.interceptor.invoke(this);
    			}
    			else {
    				// Dynamic matching failed.
    				// Skip this interceptor and invoke the next in the chain.
    				return proceed();
    			}
    		}
    		else {
    			// It's an interceptor, so we just invoke it: The pointcut will have
    			// been evaluated statically before this object was constructed.
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}
    

      

  • 相关阅读:
    Baum Welch估计HMM参数实例
    SVM 为什么要从原始问题变为对偶问题来求解
    Baum-Welch算法(EM算法)对HMM模型的训练
    LR采用的Sigmoid函数与最大熵(ME) 的关系
    01背包的常数优化的一点解释
    训练中文词向量
    TensorFlow L2正则化
    TensorFlow batch normalize的使用
    听说你的模型损失是NaN
    编译TensorFlow CPU指令集优化版
  • 原文地址:https://www.cnblogs.com/dpains/p/13865187.html
Copyright © 2011-2022 走看看