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);
    		}
    	}
    

      

  • 相关阅读:
    IXmlSerializable With WCFData Transfer in Service Contracts
    Difference Between XmlSerialization and BinarySerialization
    Using XmlSerializer (using Attributes like XmlElement , XmlAttribute etc ) Data Transfer in Service Contracts
    Introducing XML Serialization
    Version Tolerant Serialization
    Which binding is bestWCF Bindings
    Data Transfer in Service Contracts
    DataContract KnownTypeData Transfer in Service Contracts
    Using the Message ClassData Transfer in Service Contracts
    DataContract POCO SupportData Transfer in Service Contracts
  • 原文地址:https://www.cnblogs.com/dpains/p/13865187.html
Copyright © 2011-2022 走看看