文章结构
- 首先提到了SpringAOP是基于
postProcessBeforeInstantiation和postProcessAfterInstantiation两个方法实现的,这篇文章主讲postProcessBeforeInstantiation - 加载Advisor
- 获取所有的Advisor又对Advisor进行了一系列的封装
Advisor类
看代码前首先了解一个类Advisor,如果从代码执行角度来看,Spring AOP的执行过程分为四大步骤:
- 步骤一:Spring框架生成Advisor实例,可以是
@Aspect,@Async等注解生成的实例,也可以是程序员自定义的AbstractAdvisor子类的实例。 - 步骤二:Spring框架在目标实例初始化完成后,也就是使用
BeanPostProcessor的postProcessAfterInitialization方法,根据Advisor实例中切入点Pointcut的定义,选择出适合该目标对象的Advisor实例。 - 步骤三:Spring框架根据Advisor实例生成代理对象。
- 步骤四:调用方法执行过程时,Spring框架执行Advisor实例的通知
Advice逻辑。
Spring中有大量的机制都是通过AOP实现的,比如说@Async的异步调用和@Transational。此外,用户也可以使用@Aspect注解定义切面或者直接继承AbstractPointcutAdvisor来提供切面逻辑。上述这些情况下,AOP都会生成对应的Advisor实例
Spring AOP中这个Advisor类,看看它的继承关系
在上篇文章中我们搭建了一个阅读源码的demo工程,然后简单介绍了一下@EnableAspectJAutoProxy注解,这个注解最重要的功能就是为向Spring中注入了一个bean, AnnotationAwareAspectJAutoProxyCreator ,本篇文章就继续来撸AOP的源码
前文已经简单提到了这个类的功能,不过这里还是要先看一下这个类的继承图
观察类图可知,AnnotationAwareAspectJAutoProxyCreator 这个类间接实现了BeanPostProcessor接口。还记得我们之前在对SpringIOC的源码进行解析时提到过,Spring在实例化Bean的前后会分别调用方法postProcessBeforeInstantiation和postProcessAfterInstantiation
而AOP的整体逻辑就是通过这两个方法来实现的
resolveBeforeInstantiation()
在前面SpringIOC中我们跟踪过IOC容器初始化的过程,其中AbstractAutowireCapableBeanFactor 方法createBean()里, resolveBeforeInstantiation()方法会对 BeanPostProcessors 处理,调用 postProcessBeforeInstantiation 和 postProcessAfterInstantiation 生成Bean的代理对象
@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// Prepare method overrides.try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {//代理的BeanPostProcessors处理,调用postProcessBeforeInstantiation 和 postProcessAfterInstantiation// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isDebugEnabled()) {logger.debug("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException ex) {// A previously detected exception with proper bean creation context already...throw ex;}catch (ImplicitlyAppearedSingletonException ex) {// An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry...throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}@Nullableprotected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}@Nullableprotected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;//这里进行调用Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;}@Overridepublic Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {Object current = beanProcessor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;}
postProcessBeforeInstantiation
首先看一下这个postProcessBeforeInstantiation方法,它是在bean实例化之前调用的,主要是针对切面类。这个方法不在AnnotationAwareAspectJAutoProxyCreator 这个类中,而是在其父类AbstractAutoProxyCreator中
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}//不是功能实现的基础类,shouldSkip()方法判断是否跳过, 不跳过则加载bean上的Advisor列表if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}
加载Advisors列表
上方代码中最重要的一个方法就是shouldSkip方法了,这个方法被AspectJAwareAdvisorAutoProxyCreator所重载
protected boolean shouldSkip(Class<?> beanClass, String beanName) {//查找所有标识了@Aspect注解的类,这里是重点,接着往下看List<Advisor> candidateAdvisors = findCandidateAdvisors();for (Advisor advisor : candidateAdvisors) {if (advisor instanceof AspectJPointcutAdvisor) {if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {return true;}}}return super.shouldSkip(beanClass, beanName);}protected List<Advisor> findCandidateAdvisors() {return this.advisorRetrievalHelper.findAdvisorBeans();}protected List<Advisor> findCandidateAdvisors() {List<Advisor> advisors = super.findCandidateAdvisors();//buildAspectJAdvisors是重点advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());return advisors;}
一个长方法buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() {//所有包含Aspect注解类的名称集合List<String> aspectNames = this.aspectBeanNames;if (aspectNames == null) {synchronized (this) {aspectNames = this.aspectBeanNames;//这个双重检查是不是在学习安全的单例模式的时候见过if (aspectNames == null) {List<Advisor> advisors = new LinkedList<Advisor>();aspectNames = new LinkedList<String>();//获取所有Bean名称String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);for (String beanName : beanNames) {//判断是否符合条件,比如说有时会排除一些类,不让这些类注入进Springif (!isEligibleBean(beanName)) {continue;}Class<?> beanType = this.beanFactory.getType(beanName);if (beanType == null) {continue;}//判断Bean的Class上是否标识@Aspect注解if (this.advisorFactory.isAspect(beanType)) {aspectNames.add(beanName);AspectMetadata amd = new AspectMetadata(beanType, beanName);if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);//下一步说,重点的重点,getAdvisors会解析这个类中的@Pointcut、@Before等注解方法封装为Advisor列表,见下List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);if (this.beanFactory.isSingleton(beanName)) {//将解析的Bean名称及类上的Advisors缓存起来,每个Bean只解析一次this.advisorsCache.put(beanName, classAdvisors);}else {this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);}else {if (this.beanFactory.isSingleton(beanName)) {throw new IllegalArgumentException("Bean with name '" + beanName +"' is a singleton, but aspect instantiation model is not singleton");}MetadataAwareAspectInstanceFactory factory =new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}this.aspectBeanNames = aspectNames;return advisors;}}}if (aspectNames.isEmpty()) {return Collections.emptyList();}List<Advisor> advisors = new LinkedList<Advisor>();for (String aspectName : aspectNames) {//从缓存中获取当前Bean的切面实例,如果不为空,则指明当前Bean的Class标识了@Aspect,且有切面方法List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);if (cachedAdvisors != null) {advisors.addAll(cachedAdvisors);}else {MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}return advisors;}
advisorFactory.getAdvisors
advisorFactory.getAdvisors()方法会从@Aspect标识的类上获取@Before,@Pointcut等注解的信息及其标识的方法的信息,生成Advisor
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();//校验类的合法性相关validate(aspectClass);MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new LinkedList<Advisor>();//获取这个类所有的Advisor方法for (Method method : getAdvisorMethods(aspectClass)) {//生成Advisor实例,见下Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}for (Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors;}//获取类的的方法private List<Method> getAdvisorMethods(Class<?> aspectClass) {final List<Method> methods = new LinkedList<Method>();ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {@Overridepublic void doWith(Method method) throws IllegalArgumentException {//在@Aspect标识的类内部排除@Pointcut标识之外的所有方法,得到的方法集合包括继承自父类的方法,包括继承自Object的方法if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {methods.add(method);}}});//对得到的所有方法排序,//如果方法标识了切面注解,则按@Around, @Before, @After, @AfterReturning, @AfterThrowing的顺序排序//如果没有标识这些注解,则按方法名称的字符串排序,//有注解的方法排在无注解的方法之前//最后的排序应该是这样的Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class。。。Collections.sort(methods, METHOD_COMPARATOR);return methods;}public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {//再次校验类的合法性validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());//切点表达式的包装类里面包含这些东西:execution(public * cn.shiyujun.service.IOCService.hollo(..))//或者@Around中的value值,都会被封装到AspectJExpressionPointcut中AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}//根据方法、切点、AOP实例工厂、类名、序号生成Advisor实例,详细代码往下看return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {//查询方法上的切面注解,根据注解生成相应类型的AspectJAnnotation,在调用AspectJAnnotation的构造函数的同时//根据注解value或pointcut属性得到切点表达式,有argNames则设置参数名称AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);//过滤那些不含@Before, @Around, @After, @AfterReturning, @AfterThrowing注解的方法if (aspectJAnnotation == null) {return null;}//生成带表达式的切面切入点,设置其切入点表达式AspectJExpressionPointcut ajexp =new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);ajexp.setExpression(aspectJAnnotation.getPointcutExpression());ajexp.setBeanFactory(this.beanFactory);return ajexp;}
InstantiationModelAwarePointcutAdvisorImpl 构造方法
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {this.declaredPointcut = declaredPointcut;this.declaringClass = aspectJAdviceMethod.getDeclaringClass();this.methodName = aspectJAdviceMethod.getName();this.parameterTypes = aspectJAdviceMethod.getParameterTypes();this.aspectJAdviceMethod = aspectJAdviceMethod;this.aspectJAdvisorFactory = aspectJAdvisorFactory;this.aspectInstanceFactory = aspectInstanceFactory;this.declarationOrder = declarationOrder;this.aspectName = aspectName;if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);this.lazy = true;}else {this.pointcut = this.declaredPointcut;this.lazy = false;//重点在这里this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);}}private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {//再往下看Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,this.aspectInstanceFactory, this.declarationOrder, this.aspectName);return (advice != null ? advice : EMPTY_ADVICE);}
reflectiveAspectJAdvisorFactory.getAdvice
//调用这里的getAdvicepublic class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//又是一次校验validate(candidateAspectClass);AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}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, expressionPointcut, aspectInstanceFactory);break;case AtAfter:springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning:springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing:springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;case AtAround:springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;default:throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);}//设置通知方法所属的类springAdvice.setAspectName(aspectName);//设置通知的序号,同一个类中有多个切面注解标识的方法时,按上方说的排序规则来排序,//其序号就是此方法在列表中的序号,第一个就是0springAdvice.setDeclarationOrder(declarationOrder);//获取通知方法的所有参数String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);//将通知方法上的参数设置到通知中if (argNames != null) {springAdvice.setArgumentNamesFromStringArray(argNames);}//计算参数绑定工作,此方法详解请接着往下看springAdvice.calculateArgumentBindings();return springAdvice;}}
校验方法参数并绑定(argNames()和arg())
public synchronized final void calculateArgumentBindings() {if (this.argumentsIntrospected || this.parameterTypes.length == 0) {return;}int numUnboundArgs = this.parameterTypes.length;Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();//切面注解标识的方法第一个参数要求是JoinPoint,或StaticPart,若是@Around注解则也可以是ProceedingJoinPointif (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {numUnboundArgs--;}else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {numUnboundArgs--;}if (numUnboundArgs > 0) {//绑定属性bindArgumentsByName(numUnboundArgs);}this.argumentsIntrospected = true;}private void bindArgumentsByName(int numArgumentsExpectingToBind) {if (this.argumentNames == null) { //获取方法参数的名称this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);}if (this.argumentNames != null) {// 往下看bindExplicitArguments(numArgumentsExpectingToBind);}else {throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +"the argument names were not specified and could not be discovered.");}}private void bindExplicitArguments(int numArgumentsLeftToBind) {//此属性用来存储方法未绑定的参数名称,及参数的序号this.argumentBindings = new HashMap<String, Integer>();int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length;if (this.argumentNames.length != numExpectedArgumentNames) {throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames +" arguments to bind by name in advice, but actually found " +this.argumentNames.length + " arguments.");}// So we match in number...,argumentIndexOffset代表第一个未绑定参数的顺序int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind;for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {//存储未绑定的参数名称及其顺序的映射关系this.argumentBindings.put(this.argumentNames[i], i);}// Check that returning and throwing were in the argument names list if// specified, and find the discovered argument types.//如果是@AfterReturning注解的returningName 有值,验证,解析,同时得到定义返回值的类型if (this.returningName != null) {if (!this.argumentBindings.containsKey(this.returningName)) {throw new IllegalStateException("Returning argument name '" + this.returningName +"' was not bound in advice arguments");}else {Integer index = this.argumentBindings.get(this.returningName);this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index];this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index];}}//如果是@AfterThrowing注解的throwingName 有值,验证,解析,同时得到抛出异常的类型if (this.throwingName != null) {if (!this.argumentBindings.containsKey(this.throwingName)) {throw new IllegalStateException("Throwing argument name '" + this.throwingName +"' was not bound in advice arguments");}else {Integer index = this.argumentBindings.get(this.throwingName);this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index];}}// configure the pointcut expression accordingly.configurePointcutParameters(argumentIndexOffset);}private void configurePointcutParameters(int argumentIndexOffset) {int numParametersToRemove = argumentIndexOffset;if (this.returningName != null) {numParametersToRemove++;}if (this.throwingName != null) {numParametersToRemove++;}String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove];Class<?>[] pointcutParameterTypes = new Class<?>[pointcutParameterNames.length];Class<?>[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes();int index = 0;for (int i = 0; i < this.argumentNames.length; i++) {if (i < argumentIndexOffset) {continue;}if (this.argumentNames[i].equals(this.returningName) ||this.argumentNames[i].equals(this.throwingName)) {continue;}pointcutParameterNames[index] = this.argumentNames[i];pointcutParameterTypes[index] = methodParameterTypes[i];index++;}//剩余的未绑定的参数会赋值给AspectJExpressionPointcut(表达式形式的切入点)的属性,以备后续使用this.pointcut.setParameterNames(pointcutParameterNames);this.pointcut.setParameterTypes(pointcutParameterTypes);}
