注解切面解析

  • 注解切面解析器
/**
* 注解切面解析器
*/
public class BeanFactoryAspectJAdvisorsBuilder {
/**
* Bean 工厂
*/
private final ListableBeanFactory beanFactory;
/**
* 生成 Advisor 的工厂
*/
private final AspectJAdvisorFactory advisorFactory;
/**
* 切面 Bean 名称
*/
@Nullable
private volatile List<String> aspectBeanNames;
/**
* 通知者缓存
*/
private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>();
/**
* 切面实例缓存
*/
private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>(); public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory));
} public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
this.beanFactory = beanFactory;
this.advisorFactory = advisorFactory;
} public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = aspectBeanNames;
if (aspectNames == null) {
final List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 读取容器中所有的类
final String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
beanFactory, Object.class, true, false);
for (final String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
} // 读取 Bean 的类型,不能执行初始化操作
final Class<?> beanType = beanFactory.getType(beanName);
if (beanType == null) {
continue;
} // 指定的 bean 是否是一个注解切面
if (advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
// 创建切面元数据
final AspectMetadata amd = new AspectMetadata(beanType, beanName);
// 切面是否是单例
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
final MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(beanFactory, beanName);
// 基于目标切面创建相关的 Advisor
final List<Advisor> classAdvisors = advisorFactory.getAdvisors(factory);
// 切面 bean 是单例 && 将通知者加入缓存
if (beanFactory.isSingleton(beanName)) {
advisorsCache.put(beanName, classAdvisors);
}
else {
aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
final MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(beanFactory, beanName);
aspectFactoryCache.put(beanName, factory);
advisors.addAll(advisorFactory.getAdvisors(factory));
}
}
}
aspectBeanNames = aspectNames;
return advisors;
}
}
} // 无注解切面
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
final List<Advisor> advisors = new ArrayList<>();
for (final String aspectName : aspectNames) {
// 获取指定注解切面的所有 Advisor
final List<Advisor> cachedAdvisors = advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
final MetadataAwareAspectInstanceFactory factory = aspectFactoryCache.get(aspectName);
advisors.addAll(advisorFactory.getAdvisors(factory));
}
}
return advisors;
} /**
* 指定 bean 名称的切面是否合格
*/
protected boolean isEligibleBean(String beanName) {
return true;
}
}
  • 通知者创建工厂
/**
* 基于标注了 AspectJ 注解的类创建 Spring AOP 通知者
*/
public interface AspectJAdvisorFactory { /**
* 指定类型是否是一个切面
*/
boolean isAspect(Class<?> clazz); /**
* 指定类型是否是一个有效的 AspectJ 切面类
*/
void validate(Class<?> aspectClass) throws AopConfigException; /**
* 将所有标注了 AspectJ 注解的方法转换为 Advisor
*/
List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory); /**
* 为给定的AspectJ通知方法构建一个Spring AOP通知
*/
@Nullable
Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrder, String aspectName); /**
* 为给定的AspectJ通知方法构建一个Spring AOP通知
*
* @param candidateAdviceMethod 候选通知方法
* @param expressionPointcut AspectJ 切点表达式
* @param aspectInstanceFactory Aspect 实例工厂
* @param declarationOrder 通知方法在切面中的声明顺序
* @param aspectName 切面的名称
* @return {@code null} 非通知方法和切点都返回 null
*
* @see org.springframework.aop.aspectj.AspectJAroundAdvice 环绕通知
* @see org.springframework.aop.aspectj.AspectJMethodBeforeAdvice 前置通知
* @see org.springframework.aop.aspectj.AspectJAfterAdvice 后置通知
* @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice 正常返回通知
* @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice 异常通知
*/
@Nullable
Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName);
} public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
private static final String AJC_MAGIC = "ajc$";
/**
* AspectJ 注解类型
*/
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; /** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass()); protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer(); /**
* 目标类型是否是一个注解切面【目标类型上存在 @Aspect 注解并且不是由 ajc 编译】
*/
@Override
public boolean isAspect(Class<?> clazz) {
return hasAspectAnnotation(clazz) && !compiledByAjc(clazz);
} /**
* 目标类型上是否有 Aspect 注解【如果没有直接标注,则尝试递归查找父类及其类型上的注解、实现的接口及其类型上的注解】
*/
private boolean hasAspectAnnotation(Class<?> clazz) {
return AnnotationUtils.findAnnotation(clazz, Aspect.class) != null;
} private boolean compiledByAjc(Class<?> clazz) {
for (final Field field : clazz.getDeclaredFields()) {
// 方法名称由 ajc$ 开头
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
} @Override
public void validate(Class<?> aspectClass) throws AopConfigException {
// 如果父类有注解而不是抽象类,那就是一个错误
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
!Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
aspectClass.getSuperclass().getName() + "]");
} final AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
if (!ajType.isAspect()) {
throw new NotAnAtAspectException(aspectClass);
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " +
"This is not supported in Spring AOP.");
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " +
"This is not supported in Spring AOP.");
}
} /**
* 查找并返回给定方法上的第一个 AspectJ注释。
* 查找顺序:Pointcut.class, Around.class, Before.class,
* After.class, AfterReturning.class, AfterThrowing.class
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (final Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
final AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
} @Nullable
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
final A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<>(result);
}
else {
return null;
}
} /**
* AspectJ 注解类型枚举
*/
protected enum AspectJAnnotationType {
AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing
} /**
* 对一个 AspectJ 注解进行建模,公开其类型枚举和切点字符串
*/
protected static class AspectJAnnotation<A extends Annotation> {
private static final String[] EXPRESSION_ATTRIBUTES = new String[] {"pointcut", "value"};
private static Map<Class<?>, AspectJAnnotationType> annotationTypeMap = new HashMap<>(8); static {
annotationTypeMap.put(Pointcut.class, AspectJAnnotationType.AtPointcut);
annotationTypeMap.put(Around.class, AspectJAnnotationType.AtAround);
annotationTypeMap.put(Before.class, AspectJAnnotationType.AtBefore);
annotationTypeMap.put(After.class, AspectJAnnotationType.AtAfter);
annotationTypeMap.put(AfterReturning.class, AspectJAnnotationType.AtAfterReturning);
annotationTypeMap.put(AfterThrowing.class, AspectJAnnotationType.AtAfterThrowing);
} /**
* 目标注解
*/
private final A annotation;
/**
* AspectJ 注解类型枚举
*/
private final AspectJAnnotationType annotationType;
/**
* 切点字符串
*/
private final String pointcutExpression;
/**
* 参数名称
*/
private final String argumentNames; public AspectJAnnotation(A annotation) {
this.annotation = annotation;
this.annotationType = determineAnnotationType(annotation);
try {
this.pointcutExpression = resolveExpression(annotation);
final Object argNames = AnnotationUtils.getValue(annotation, "argNames");
this.argumentNames = argNames instanceof String ? (String) argNames : "";
}
catch (final Exception ex) {
throw new IllegalArgumentException(annotation + " is not a valid AspectJ annotation", ex);
}
} private AspectJAnnotationType determineAnnotationType(A annotation) {
final AspectJAnnotationType type = annotationTypeMap.get(annotation.annotationType());
if (type != null) {
return type;
}
throw new IllegalStateException("Unknown annotation type: " + annotation);
} private String resolveExpression(A annotation) {
// "pointcut", "value"
for (final String attributeName : EXPRESSION_ATTRIBUTES) {
final Object val = AnnotationUtils.getValue(annotation, attributeName);
if (val instanceof String) {
final String str = (String) val;
if (!str.isEmpty()) {
return str;
}
}
}
throw new IllegalStateException("Failed to resolve expression: " + annotation);
} public AspectJAnnotationType getAnnotationType() {
return this.annotationType;
} public A getAnnotation() {
return this.annotation;
} public String getPointcutExpression() {
return this.pointcutExpression;
} public String getArgumentNames() {
return this.argumentNames;
} @Override
public String toString() {
return this.annotation.toString();
}
} /**
* 解析在 AspectJ 注解级别定义的参数
*/
private static class AspectJAnnotationParameterNameDiscoverer implements ParameterNameDiscoverer { @Override
@Nullable
public String[] getParameterNames(Method method) {
if (method.getParameterCount() == 0) {
return new String[0];
}
// 读取目标方法上的 AspectJ 注解
final AspectJAnnotation<?> annotation = findAspectJAnnotationOnMethod(method);
if (annotation == null) {
return null;
}
final StringTokenizer nameTokens = new StringTokenizer(annotation.getArgumentNames(), ",");
if (nameTokens.countTokens() > 0) {
final String[] names = new String[nameTokens.countTokens()];
for (int i = 0; i < names.length; i++) {
names[i] = nameTokens.nextToken();
}
return names;
}
else {
return null;
}
} @Override
@Nullable
public String[] getParameterNames(Constructor<?> ctor) {
throw new UnsupportedOperationException("Spring AOP cannot handle constructor advice");
}
}
} /**
* 通过反射来执行 AspectJ 注解关联的通知方法的 AdvisorFactory
*/
@SuppressWarnings("serial")
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
private static final Comparator<Method> METHOD_COMPARATOR; static {
final Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
// 根据声明的类型顺序进行排序
new InstanceComparator<>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
(Converter<Method, Annotation>) method -> {
final AspectJAnnotation<?> annotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return annotation != null ? annotation.getAnnotation() : null;
});
final Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
} @Nullable
private final BeanFactory beanFactory; public ReflectiveAspectJAdvisorFactory() {
this(null);
} public ReflectiveAspectJAdvisorFactory(@Nullable BeanFactory beanFactory) {
this.beanFactory = beanFactory;
} @Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 读取切面类型
final Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 读取切面名称
final String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
// 验证切面类
validate(aspectClass); // 只实例化切面一次的装饰器
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
final List<Advisor> advisors = new ArrayList<>();
// 读取除切点外的所有方法
for (final Method method : getAdvisorMethods(aspectClass)) {
// 尝试从目标方法上构建 Advisor,创建 InstantiationModelAwarePointcutAdvisorImpl 时即初始化
final Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
} // If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
final Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
} // 是否需要动态地为指定的类型增加方法:读取当前切面中定义的所有字段
for (final Field field : aspectClass.getDeclaredFields()) {
final Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
} return advisors;
} private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
ReflectionUtils.doWithMethods(aspectClass, method -> {
// 排除切点方法
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
});
// 排序
methods.sort(METHOD_COMPARATOR);
return methods;
} /**
* 尝试为指定的类型新增方法
*/
@Nullable
private Advisor getDeclareParentsAdvisor(Field introductionField) {
// 1)目标属性上存在 DeclareParents 注解
final DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
return null;
} // 2)指定了增强实现
if (DeclareParents.class == declareParents.defaultImpl()) {
throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
} return new DeclareParentsAdvisor(
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
} @Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 读取切点表达式
final AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
} return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
} @Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
/**
* 查找并返回给定方法上的第一个 AspectJ 注释。
* 查找顺序:Pointcut.class, Around.class, Before.class,
* After.class, AfterReturning.class, AfterThrowing.class
*/
final AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} // 创建 AspectJ 表达式切点
final AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (beanFactory != null) {
ajexp.setBeanFactory(beanFactory);
}
return ajexp;
} @Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
final Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass); final 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 AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
// 环绕通知
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 前置通知
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 通知将方法返回结果绑定了命名参数
final AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
// 异常通知
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
// 如果 AfterThrowing 通知将方法抛出的异常绑定了命名参数
final AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
} // 配置 Advice
springAdvice.setAspectName(aspectName);
// 声明顺序
springAdvice.setDeclarationOrder(declarationOrder);
// 解析参数名称
final String[] argNames = parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
// 计算参数绑定
springAdvice.calculateArgumentBindings(); return springAdvice;
} /**
* 实例化此切面的合成通知则
*/
@SuppressWarnings("serial")
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
super(aif.getAspectMetadata().getPerClausePointcut(), (MethodBeforeAdvice)
(method, args, target) -> aif.getAspectInstance());
}
}
}
  • 通知分类
/**
* 封装 AspectJ 切面或 AspectJ 注解的通知方法
*/
@SuppressWarnings("serial")
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable {
/**
* Key used in ReflectiveMethodInvocation userAttributes map for the current joinpoint.
*/
protected static final String JOIN_POINT_KEY = JoinPoint.class.getName(); /**
* 延迟初始化当前调用的连接点
*/
public static JoinPoint currentJoinPoint() {
final MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
final ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
if (jp == null) {
jp = new MethodInvocationProceedingJoinPoint(pmi);
pmi.setUserAttribute(JOIN_POINT_KEY, jp);
}
return jp;
} private final Class<?> declaringClass; private final String methodName; private final Class<?>[] parameterTypes; protected transient Method aspectJAdviceMethod; private final AspectJExpressionPointcut pointcut; private final AspectInstanceFactory aspectInstanceFactory; /**
* 切面 bean 的名称
*/
private String aspectName = ""; /**
* 通知在切面内的声明顺序
*/
private int declarationOrder; /**
* 通知方法参数名称数组
*/
@Nullable
private String[] argumentNames; /** 异常通知:目标方法抛出的异常所绑定的参数名称 */
@Nullable
private String throwingName; /** 正常返回通知:目标方法的执行结果所绑定的参数名称 */
@Nullable
private String returningName; private Class<?> discoveredReturningType = Object.class; private Class<?> discoveredThrowingType = Object.class; /**
* 通知方法的第一个参数为 JoinPoint,则为 0
*/
private int joinPointArgumentIndex = -1; /**
* 通知方法的第一个参数为 JoinPoint.StaticPart,则为 0
*/
private int joinPointStaticPartArgumentIndex = -1; @Nullable
private Map<String, Integer> argumentBindings; /**
* 参数是否已经内省
*/
private boolean argumentsIntrospected = false; @Nullable
private Type discoveredReturningGenericType;
// Note: Unlike return type, no such generic information is needed for the throwing type,
// since Java doesn't allow exception types to be parameterized. public AbstractAspectJAdvice(
Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) { Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
declaringClass = aspectJAdviceMethod.getDeclaringClass();
methodName = aspectJAdviceMethod.getName();
parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.pointcut = pointcut;
this.aspectInstanceFactory = aspectInstanceFactory;
} public final Method getAspectJAdviceMethod() {
return aspectJAdviceMethod;
} public final AspectJExpressionPointcut getPointcut() {
calculateArgumentBindings();
return pointcut;
} /**
* 构建一个排除了 AspectJ 通知方法本身的安全切入点
*/
public final Pointcut buildSafePointcut() {
final Pointcut pc = getPointcut();
final MethodMatcher safeMethodMatcher = MethodMatchers.intersection(
new AdviceExcludingMethodMatcher(aspectJAdviceMethod), pc.getMethodMatcher());
return new ComposablePointcut(pc.getClassFilter(), safeMethodMatcher);
} public final AspectInstanceFactory getAspectInstanceFactory() {
return aspectInstanceFactory;
} @Nullable
public final ClassLoader getAspectClassLoader() {
return aspectInstanceFactory.getAspectClassLoader();
} @Override
public int getOrder() {
return aspectInstanceFactory.getOrder();
} public void setAspectName(String name) {
aspectName = name;
} @Override
public String getAspectName() {
return aspectName;
} public void setDeclarationOrder(int order) {
declarationOrder = order;
} @Override
public int getDeclarationOrder() {
return declarationOrder;
} public void setArgumentNames(String argNames) {
final String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames);
setArgumentNamesFromStringArray(tokens);
} public void setArgumentNamesFromStringArray(String... args) {
argumentNames = new String[args.length];
for (int i = 0; i < args.length; i++) {
argumentNames[i] = StringUtils.trimWhitespace(args[i]);
if (!isVariableName(argumentNames[i])) {
throw new IllegalArgumentException(
"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
argumentNames[i] + "' that is not a valid Java identifier");
}
}
if (argumentNames != null) {
if (aspectJAdviceMethod.getParameterCount() == argumentNames.length + 1) {
// May need to add implicit join point arg name...
final Class<?> firstArgType = aspectJAdviceMethod.getParameterTypes()[0];
if (firstArgType == JoinPoint.class ||
firstArgType == ProceedingJoinPoint.class ||
firstArgType == JoinPoint.StaticPart.class) {
final String[] oldNames = argumentNames;
argumentNames = new String[oldNames.length + 1];
argumentNames[0] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length);
}
}
}
} public void setReturningName(String name) {
throw new UnsupportedOperationException("Only afterReturning advice can be used to bind a return value");
} protected void setReturningNameNoCheck(String name) {
// name could be a variable or a type...
if (isVariableName(name)) {
returningName = name;
}
else {
// assume a type
try {
discoveredReturningType = ClassUtils.forName(name, getAspectClassLoader());
}
catch (final Throwable ex) {
throw new IllegalArgumentException("Returning name '" + name +
"' is neither a valid argument name nor the fully-qualified " +
"name of a Java type on the classpath. Root cause: " + ex);
}
}
} protected Class<?> getDiscoveredReturningType() {
return discoveredReturningType;
} @Nullable
protected Type getDiscoveredReturningGenericType() {
return discoveredReturningGenericType;
} public void setThrowingName(String name) {
throw new UnsupportedOperationException("Only afterThrowing advice can be used to bind a thrown exception");
} protected void setThrowingNameNoCheck(String name) {
// name could be a variable or a type...
if (isVariableName(name)) {
throwingName = name;
}
else {
// assume a type
try {
discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());
}
catch (final Throwable ex) {
throw new IllegalArgumentException("Throwing name '" + name +
"' is neither a valid argument name nor the fully-qualified " +
"name of a Java type on the classpath. Root cause: " + ex);
}
}
} protected Class<?> getDiscoveredThrowingType() {
return discoveredThrowingType;
} private boolean isVariableName(String name) {
final char[] chars = name.toCharArray();
if (!Character.isJavaIdentifierStart(chars[0])) {
return false;
}
for (int i = 1; i < chars.length; i++) {
if (!Character.isJavaIdentifierPart(chars[i])) {
return false;
}
}
return true;
} /**
* 1)如果第一个参数是 JoinPoint 或 ProceedingJoinPoint,则我们将传递一个 JoinPoint
* 【如果是环绕通知,则传递 ProceedingJoinPoint】给它。
* 2)如果第一个参数是 JoinPoint.StaticPart,则我们将传递一个 JoinPoint.StaticPart 给它。
* 剩余的参数,切点将基于指定的连接点进行计算后执行绑定
*/
public final synchronized void calculateArgumentBindings() {
// 无需执行参数绑定操作
if (argumentsIntrospected || parameterTypes.length == 0) {
return;
} // 未绑定的参数个数
int numUnboundArgs = parameterTypes.length;
// 通知方法的参数类型数组
final Class<?>[] parameterTypes = aspectJAdviceMethod.getParameterTypes();
/**
* 第一个参数类型是 JoinPoint、ProceedingJoinPoint、JoinPoint.StaticPart,
* 则将 numUnboundArgs 减 1
*/
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) ||
maybeBindJoinPointStaticPart(parameterTypes[0])) {
numUnboundArgs--;
} if (numUnboundArgs > 0) {
// 按照匹配的切入点,返回的名称绑定参数
bindArgumentsByName(numUnboundArgs);
} argumentsIntrospected = true;
} /**
* 是否是 JoinPoint
*/
private boolean maybeBindJoinPoint(Class<?> candidateParameterType) {
if (JoinPoint.class == candidateParameterType) {
joinPointArgumentIndex = 0;
return true;
}
else {
return false;
}
} /**
* 是否是 ProceedingJoinPoint
*/
private boolean maybeBindProceedingJoinPoint(Class<?> candidateParameterType) {
if (ProceedingJoinPoint.class == candidateParameterType) {
if (!supportsProceedingJoinPoint()) {
throw new IllegalArgumentException("ProceedingJoinPoint is only supported for around advice");
}
joinPointArgumentIndex = 0;
return true;
}
else {
return false;
}
} protected boolean supportsProceedingJoinPoint() {
return false;
} /**
* 是否是 JoinPoint.StaticPart
*/
private boolean maybeBindJoinPointStaticPart(Class<?> candidateParameterType) {
if (JoinPoint.StaticPart.class == candidateParameterType) {
joinPointStaticPartArgumentIndex = 0;
return true;
}
else {
return false;
}
} /**
* 根据名称实现参数绑定
*/
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
if (argumentNames == null) {
// 读取参数名称数组
argumentNames = createParameterNameDiscoverer().getParameterNames(aspectJAdviceMethod);
}
if (argumentNames != null) {
// 绑定显示的参数
bindExplicitArguments(numArgumentsExpectingToBind);
}
else {
throw new IllegalStateException("Advice method [" + aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
} /**
* 创建一个用于实现参数绑定的 ParameterNameDiscoverer
*/
protected ParameterNameDiscoverer createParameterNameDiscoverer() {
final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
final AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
new AspectJAdviceParameterNameDiscoverer(pointcut.getExpression());
adviceParameterNameDiscoverer.setReturningName(returningName);
adviceParameterNameDiscoverer.setThrowingName(throwingName);
// Last in chain, so if we're called and we fail, that's bad...
adviceParameterNameDiscoverer.setRaiseExceptions(true);
discoverer.addDiscoverer(adviceParameterNameDiscoverer);
return discoverer;
} private void bindExplicitArguments(int numArgumentsLeftToBind) {
Assert.state(argumentNames != null, "No argument names available");
argumentBindings = new HashMap<>(); final int numExpectedArgumentNames = aspectJAdviceMethod.getParameterCount();
if (argumentNames.length != numExpectedArgumentNames) {
throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames +
" arguments to bind by name in advice, but actually found " +
argumentNames.length + " arguments.");
} // 需要显式绑定的参数起始索引
final int argumentIndexOffset = parameterTypes.length - numArgumentsLeftToBind;
for (int i = argumentIndexOffset; i < argumentNames.length; i++) {
argumentBindings.put(argumentNames[i], i);
} // 如果是 AfterReturning 通知 && 指定了绑定返回值的参数名称
if (returningName != null) {
// 通知方法中不存在此绑定参数,则抛出 IllegalStateException 异常
if (!argumentBindings.containsKey(returningName)) {
throw new IllegalStateException("Returning argument name '" + returningName +
"' was not bound in advice arguments");
}
else {
// 读取参数索引
final Integer index = argumentBindings.get(returningName);
// 写入返回值类型
discoveredReturningType = aspectJAdviceMethod.getParameterTypes()[index];
// 写入返回值泛型
discoveredReturningGenericType = aspectJAdviceMethod.getGenericParameterTypes()[index];
}
}
// 如果是 AfterThrowing 通知 && 指定了绑定异常的参数名称
if (throwingName != null) {
// 通知方法中不存在此绑定参数,则抛出 IllegalStateException 异常
if (!argumentBindings.containsKey(throwingName)) {
throw new IllegalStateException("Throwing argument name '" + throwingName +
"' was not bound in advice arguments");
}
else {
// 读取参数索引
final Integer index = argumentBindings.get(throwingName);
// 写入异常类型
discoveredThrowingType = aspectJAdviceMethod.getParameterTypes()[index];
}
} // 按需配置切点表达式
configurePointcutParameters(argumentNames, argumentIndexOffset);
} private void configurePointcutParameters(String[] argumentNames, int argumentIndexOffset) {
int numParametersToRemove = argumentIndexOffset;
if (returningName != null) {
numParametersToRemove++;
}
if (throwingName != null) {
numParametersToRemove++;
}
// 需要执行绑定的切点参数名称数组
final String[] pointcutParameterNames = new String[argumentNames.length - numParametersToRemove];
// 需要执行绑定的切点参数类型数组
final Class<?>[] pointcutParameterTypes = new Class<?>[pointcutParameterNames.length];
// 通知方法参数类型数组
final Class<?>[] methodParameterTypes = aspectJAdviceMethod.getParameterTypes(); int index = 0;
for (int i = 0; i < argumentNames.length; i++) {
// 尝试跳过第一个参数
if (i < argumentIndexOffset) {
continue;
}
// 尝试跳过 returningName 和 throwingName 参数
if (argumentNames[i].equals(returningName) ||
argumentNames[i].equals(throwingName)) {
continue;
}
pointcutParameterNames[index] = argumentNames[i];
pointcutParameterTypes[index] = methodParameterTypes[i];
index++;
}
// 写入切点参数名称数组
pointcut.setParameterNames(pointcutParameterNames);
// 写入切点参数名称类型数组
pointcut.setParameterTypes(pointcutParameterTypes);
} /**
* 执行参数绑定并返回绑定后的参数列表
*/
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
@Nullable Object returnValue, @Nullable Throwable ex) {
calculateArgumentBindings(); // AMC start
final Object[] adviceInvocationArgs = new Object[parameterTypes.length];
int numBound = 0; if (joinPointArgumentIndex != -1) {
adviceInvocationArgs[joinPointArgumentIndex] = jp;
numBound++;
}
else if (joinPointStaticPartArgumentIndex != -1) {
adviceInvocationArgs[joinPointStaticPartArgumentIndex] = jp.getStaticPart();
numBound++;
} if (!CollectionUtils.isEmpty(argumentBindings)) {
// binding from pointcut match
if (jpMatch != null) {
final PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
for (final PointcutParameter parameter : parameterBindings) {
final String name = parameter.getName();
final Integer index = argumentBindings.get(name);
adviceInvocationArgs[index] = parameter.getBinding();
numBound++;
}
}
// binding from returning clause
if (returningName != null) {
final Integer index = argumentBindings.get(returningName);
adviceInvocationArgs[index] = returnValue;
numBound++;
}
// binding from thrown exception
if (throwingName != null) {
final Integer index = argumentBindings.get(throwingName);
adviceInvocationArgs[index] = ex;
numBound++;
}
} if (numBound != parameterTypes.length) {
throw new IllegalStateException("Required to bind " + parameterTypes.length +
" arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
} return adviceInvocationArgs;
} /**
* 执行通知方法
*/
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
} protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
@Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
} protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return aspectJAdviceMethod.invoke(aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (final IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
aspectJAdviceMethod + "]; pointcut expression [" +
pointcut.getPointcutExpression() + "]", ex);
}
catch (final InvocationTargetException ex) {
throw ex.getTargetException();
}
} protected JoinPoint getJoinPoint() {
return currentJoinPoint();
} @Nullable
protected JoinPointMatch getJoinPointMatch() {
final MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
return getJoinPointMatch((ProxyMethodInvocation) mi);
} @Nullable
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
final String expression = pointcut.getExpression();
return expression != null ? (JoinPointMatch) pmi.getUserAttribute(expression) : null;
} @Override
public String toString() {
return getClass().getName() + ": advice method [" + aspectJAdviceMethod + "]; " +
"aspect name '" + aspectName + "'";
} /**
* 排除指定通知方法的 MethodMatcher
* MethodMatcher that excludes the specified advice method.
* @see AbstractAspectJAdvice#buildSafePointcut()
*/
private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher {
private final Method adviceMethod; public AdviceExcludingMethodMatcher(Method adviceMethod) {
this.adviceMethod = adviceMethod;
} @Override
public boolean matches(Method method, Class<?> targetClass) {
return !adviceMethod.equals(method);
} @Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AdviceExcludingMethodMatcher)) {
return false;
}
final AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
return adviceMethod.equals(otherMm.adviceMethod);
} @Override
public int hashCode() {
return adviceMethod.hashCode();
}
}
} @SuppressWarnings("serial")
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable { public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
} @Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
} @Override
public boolean isBeforeAdvice() {
return true;
} @Override
public boolean isAfterAdvice() {
return false;
}
} @SuppressWarnings("serial")
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable { public AspectJAroundAdvice(
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJAroundAdviceMethod, pointcut, aif);
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return false;
} @Override
protected boolean supportsProceedingJoinPoint() {
return true;
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
final ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
final ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
final JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
} /**
* Return the ProceedingJoinPoint for the current invocation,
*/
protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
return new MethodInvocationProceedingJoinPoint(rmi);
} } @SuppressWarnings("serial")
public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice
implements AfterReturningAdvice, AfterAdvice, Serializable { public AspectJAfterReturningAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return true;
} @Override
public void setReturningName(String name) {
setReturningNameNoCheck(name);
} @Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
// 如果返回值类型和关联的通知参数类型匹配,则执行返回通知
if (shouldInvokeOnReturnValueOf(method, returnValue)) {
invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
}
} private boolean shouldInvokeOnReturnValueOf(Method method, @Nullable Object returnValue) {
final Class<?> type = getDiscoveredReturningType();
final Type genericType = getDiscoveredReturningGenericType();
// If we aren't dealing with a raw type, check if generic parameters are assignable.
return matchesReturnValue(type, method, returnValue) &&
(genericType == null || genericType == type ||
TypeUtils.isAssignable(genericType, method.getGenericReturnType()));
} /**
* 返回值类型是否匹配
*/
private boolean matchesReturnValue(Class<?> type, Method method, @Nullable Object returnValue) {
if (returnValue != null) {
return ClassUtils.isAssignableValue(type, returnValue);
}
else if (Object.class == type && void.class == method.getReturnType()) {
return true;
}
else {
return ClassUtils.isAssignable(type, method.getReturnType());
}
} } @SuppressWarnings("serial")
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 执行目标方法
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return true;
}
} @SuppressWarnings("serial")
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterThrowingAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return true;
} @Override
public void setThrowingName(String name) {
setThrowingNameNoCheck(name);
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (final Throwable ex) {
// 是否需要执行异常通知
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
} /**
* 异常类型是否和通知方法中的参数匹配
*/
private boolean shouldInvokeOnThrowing(Throwable ex) {
return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
}
}

@Aspect 注解切面解析的更多相关文章

  1. Spring AOP中使用@Aspect注解 面向切面实现日志横切功能详解

    引言: AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一 ...

  2. SpringAop@Aspect注解实现切面编程

    SpringAOP在springboot中如何使用 #什么是aop## 概念> aop全称Aspect OrientedProgramming,面向切面,AOP主要实现的目的是针对业务处理过程中 ...

  3. Spring的AOP配置文件和注解实例解析

    1.1           Spring的AOP配置文件和注解实例解析 AOP它利用一种称为"横切"的技术,将那些与核心业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减 ...

  4. 循序渐进之Spring AOP(6) - 使用@Aspect注解

    前面几节的示例看起来让人沮丧,要记忆如此多的接口.类和继承关系,做各种复杂的配置.好在这些只是一种相对过时的实现方式,现在只需要使用@Aspect注解及表达式就可以轻松的使用POJO来定义切面,设计精 ...

  5. Springboot中Aspect实现切面(以记录日志为例)

    前言今天我们来说说spring中的切面Aspect,这是Spring的一大优势.面向切面编程往往让我们的开发更加低耦合,也大大减少了代码量,同时呢让我们更专注于业务模块的开发,把那些与业务无关的东西提 ...

  6. Java注解全面解析(转)

    1.基本语法 注解定义看起来很像接口的定义.事实上,与其他任何接口一样,注解也将会编译成class文件. @Target(ElementType.Method) @Retention(Retentio ...

  7. Spring @Aspect实现切面编程

    参考:http://blog.csdn.net/cdl2008sky/article/details/6268628 参考:http://www.360doc.com/content/12/0602/ ...

  8. Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器

    本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...

  9. 【面试加分项】java自己定义注解之解析注解

    我之前的博客中说明过自己定义注解的声明今天我们来看看怎样对我们自己定义的注解进行使用. 1.我们在程序中使用我们的注解. 上一篇中我们自己定义了一个注解: @Target(ElementType.FI ...

随机推荐

  1. js数组与对象的区别

    数组和对象两者都可以用来表示数据的集合,曾一度搞不清楚”数组”(array)和”对象”(object)的根本区别在哪里. 有一个数组a=[1,2,3,4],还有一个对象a={0:1,1:2,2:3,3 ...

  2. JS全屏事件 模拟键盘事件F11 兼容IE

    方法1: // 全屏 //el为全屏对象 fullScreen(el) { var rfs = el.requestFullScreen || el.webkitRequestFullScreen | ...

  3. 记录-- vue+element树节点的标注

    html(背景:状态标注3钟颜色红黄绿对应0,1,2,) <el-tree @check="slclasscheck" v-if="treeShow" : ...

  4. EF部分字段修改 自动忽略为null字段

    传入一个实体 student(){id = 1,name = "测试" age = null,sex = null} 下面 是修改的方法 public async Task Edi ...

  5. MYSQL安装相关知识

    将mysql安装为winsow服务 1.执行命令: mysqld-nt.exe --install (安装到windows的服务) 或者是mysqld -install 2.执行命令: net sta ...

  6. php 各种扩展 - 都有

    https://windows.php.net/downloads/pecl/releases/

  7. XGboost数据比赛实战之调参篇(完整流程)

    这一篇博客的内容是在上一篇博客Scikit中的特征选择,XGboost进行回归预测,模型优化的实战的基础上进行调参优化的,所以在阅读本篇博客之前,请先移步看一下上一篇文章. 我前面所做的工作基本都是关 ...

  8. scikit-plot

    安装说明 安装Scikit-plot非常简单,直接用命令: pip install scikit-plot 即可完成安装. 仓库地址: https://github.com/reiinakano/sc ...

  9. 一种循环C字符数组的骚操作

    #include <stdio.h> #include <stdlib.h> int main() { char wenwa[] = "程劲小盆友在做什么" ...

  10. 使用vue开发输入型组件更好的一种解决方式(子组件向父组件传值,基于2.2.0)

    (本人想封装一个带有input输入框的组件) 之前使用vue开发组件的时候,在遇到子组件向父组件传递值时我采用的方法是这样的: 比如子组件是一个输入框,父组件调用时需要获取到子组件输入的值,子组件通过 ...