当前位置: 首页 > news >正文

spring源码-aop源码-5.1

  一、aop的源码部分还是有点复杂的,但是为了更好的理解,我这里会省去很多不必要的逻辑实现过程。主要方向还是更好的理解整体代码的实现过程。

  二、说明重点:aop的过程主要过程有两点:第一点,发现正确和适配的过程。第二点就是动态代理

  三、源码部分

  1)可能开始有点奇怪哈,从哪里下手呢?spring所有的东西还是基于配置来实现的,虽然后面修改了很多方式比如注解。但是我们这里还是从注解出发。

<aop:aspectj-autoproxy/>

  说明:这个注解也算是spring的自定义注解吧,通过spring源码-自定义标签-4的解读我们可以知道解析是从NamespaceHandlerSupport的实现类开始的。

  2)通过需要我们可以发现aop的NamespaceHandlerSupport的实现类为AopNamespaceHandler

public class AopNamespaceHandler extends NamespaceHandlerSupport {
        public AopNamespaceHandler() {
        }

        public void init() {
            this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
            //这里就是自动解析过程
            this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
            this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
            this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        }
    }

  说明:<aop:aspectj-autoproxy/>的解析过程就是在AspectJAutoProxyBeanDefinitionParser下面进行解析的。

  3)AspectJAutoProxyBeanDefinitionParser

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
        AspectJAutoProxyBeanDefinitionParser() {
        }

        //解析过程,不多解释。不懂可以开自定义标签的解析方式
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            //这里就是整个代理的过程了
            AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
            //这里不做太多讲解,也没有写出来
            this.extendBeanDefinition(element, parserContext);
            return null;
        }
      ......
    }

  4)registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
        //注册过程:名为org.springframework.aop.config.internalAutoProxyCreator
        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        //解析proxy-target-class(设置代理模式默认false, true为cglib),expose-proxy(处理内部增强,可以自己百度)
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

  5)registerAspectJAnnotationAutoProxyCreatorIfNecessary

  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }


    private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        //如果存在org.springframework.aop.config.internalAutoProxyCreator直接使用
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }

            return null;
        } else {
            //没有就自己注册
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }

  6)注册到容器的过程就结束了,接下来就是看AnnotationAwareAspectJAutoProxyCreator.class有什么不一样了

  

  通过上面我们可以知道,bean的后置操作。肯定是做了什么!

  7)BeanPostProcessor的后置操作(不明白可以参考:spring源码-BeanPostProcessor-3.3)

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            //通过自己封装的key获取(beanClassName_beanName)
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            //缓存中不包含
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                //解析过程
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

  8)wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (this.nonAdvisedBeans.contains(cacheKey)) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
            //获取增强器
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.add(cacheKey);
                //创建代理
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                //返回代理过后的bean,加入容器
                return proxy;
            } else {
                this.nonAdvisedBeans.add(cacheKey);
                return bean;
            }
        } else {
            this.nonAdvisedBeans.add(cacheKey);
            return bean;
        }
    }

  说明:增强器可以理解成对需要代理的类的增强,代理过程就是对需要的代理类进行相关处理,后续我会在动态代理讲解。

  9)getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
        //发现合适的增强器
        List advisors = this.findEligibleAdvisors(beanClass, beanName);
        return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
    }

    protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
        //查找所有增强器
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        //发现合适的增强器
        List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        this.extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
        }

        return eligibleAdvisors;
    }

  9.1)findCandidateAdvisors

  protected List<Advisor> findCandidateAdvisors() {
        //发现父类是否存在
        List<Advisor> advisors = super.findCandidateAdvisors();
        //添加新发现的
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }

    public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = null;
        synchronized(this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new LinkedList();
                List<String> aspectNames = new LinkedList();
                //获取所有beanNames
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                String[] var8 = beanNames;
                int var7 = beanNames.length;

                for(int var18 = 0; var18 < var7; ++var18) {
                    String beanName = var8[var18];
                    if (this.isEligibleBean(beanName)) {
                        Class beanType = this.beanFactory.getType(beanName);
                        //判断是否是Aspect类型(不做讲解了,就是注解的判断过程)
                        if (beanType != null && 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);
                                //获取增强器
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                } else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                //加入list返回
                                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.EMPTY_LIST;
        } else {
            List<Advisor> advisors = new LinkedList();
            Iterator var4 = aspectNames.iterator();

            while(var4.hasNext()) {
                String aspectName = (String)var4.next();
                List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
                if (cachedAdvisors != null) {
                    advisors.addAll(cachedAdvisors);
                } else {
                    MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                }
            }

            return advisors;
        }
    }

  9.2)getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
        Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
        final String aspectName = maaif.getAspectMetadata().getAspectName();
        this.validate(aspectClass);
        final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(maaif);
        final List<Advisor> advisors = new LinkedList();
        ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException {
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    //循环获取增强器
                    Advisor advisor = ReflectiveAspectJAdvisorFactory.this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
                    if (advisor != null) {
                        advisors.add(advisor);
                    }
                }

            }
        });
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        Field[] var9;
        int var8 = (var9 = aspectClass.getDeclaredFields()).length;

        for(int var7 = 0; var7 < var8; ++var7) {
            Field field = var9[var7];
            Advisor advisor = this.getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
        this.validate(aif.getAspectMetadata().getAspectClass());
        //获取切点
        AspectJExpressionPointcut ajexp = this.getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
        //是计划增强器
        return ajexp == null ? null : new InstantiationModelAwarePointcutAdvisorImpl(this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
    }

  9.3)getPointcut、InstantiationModelAwarePointcutAdvisorImpl

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        //发现注解的过程
        AbstractAspectJAdvisorFactory.AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        } else {
            //处理表达式(不讲解了)
            AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
            ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
            return ajexp;
        }
    }

    protected static AbstractAspectJAdvisorFactory.AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
        //默认的集中注解
        Class[] classesToLookFor = new Class[]{Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
        Class[] var5 = classesToLookFor;
        int var4 = classesToLookFor.length;
        
        for(int var3 = 0; var3 < var4; ++var3) {
            Class<? extends Annotation> c = var5[var3];
            //寻找过程(通过方法的注解判断,有兴趣自己看一下)
            AbstractAspectJAdvisorFactory.AspectJAnnotation foundAnnotation = findAnnotation(method, c);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }

        return null;
    }
   public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
        this.declaredPointcut = ajexp;
        this.method = method;
        this.atAspectJAdvisorFactory = af;
        this.aspectInstanceFactory = aif;
        this.declarationOrder = declarationOrderInAspect;
        this.aspectName = aspectName;
        //判断是否是lazy
        if (aif.getAspectMetadata().isLazilyInstantiated()) {
            Pointcut preInstantiationPointcut = Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif, (InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut)null);
            this.lazy = true;
        } else {
            //根据默认类型获取增强器
            this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
        }
    }
    
    private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
        //获取增强器(具体表达式的应用)
        return this.atAspectJAdvisorFactory.getAdvice(this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    }

    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
        Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
        this.validate(candidateAspectClass);
        AbstractAspectJAdvisorFactory.AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        } else if (!this.isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
            }

            Object springAdvice;
            switch($SWITCH_TABLE$org$springframework$aop$aspectj$annotation$AbstractAspectJAdvisorFactory$AspectJAnnotationType()[aspectJAnnotation.getAnnotationType().ordinal()]) {
                case 1:
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                    }

                    return null;
                case 2:
                    //前置
                    springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
                    break;
                case 3:
                    //后置
                    springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
                    break;
                case 4:
                    //return
                    springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
                    AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
                    if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                        ((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
                    }
                    break;
                case 5:
                    //异常
                    springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
                    AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
                    if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                        ((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
                    }
                    break;
                case 6:
                    //环绕
                    springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
                    break;
                default:
                    throw new UnsupportedOperationException("Unsupported advice type on method " + candidateAdviceMethod);
            }

            ((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
            ((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrderInAspect);
            String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
            if (argNames != null) {
                ((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
            }

            ((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
            return (Advice)springAdvice;
        }
    }

  备注:这就是整个发现增强器的过程,过程有点多。但是基本流程就是这样子的了。增强器后续会单独讲解

  说明:匹配过程就不讲解了,主要是通过表达式进行匹配

  10)createProxy

protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        int var8;
        int var9;
        if (!this.shouldProxyTargetClass(beanClass, beanName)) {
            Class[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
            Class[] var10 = targetInterfaces;
            var9 = targetInterfaces.length;

            for(var8 = 0; var8 < var9; ++var8) {
                Class<?> targetInterface = var10[var8];
                proxyFactory.addInterface(targetInterface);
            }
        }

        //家里增强器
        Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
        Advisor[] var11 = advisors;
        var9 = advisors.length;

        for(var8 = 0; var8 < var9; ++var8) {
            Advisor advisor = var11[var8];
            proxyFactory.addAdvisor(advisor);
        }

        proxyFactory.setTargetSource(targetSource);
        this.customizeProxyFactory(proxyFactory);
        proxyFactory.setFrozen(this.freezeProxy);
        if (this.advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
        
        //创建代理
        return proxyFactory.getProxy(this.proxyClassLoader);
    }

    public Object getProxy(ClassLoader classLoader) {
        return this.createAopProxy().getProxy(classLoader);
    }

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            this.activate();
        }

        return this.getAopProxyFactory().createAopProxy(this);
    }

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            //默认JDK动态代理
            return new JdkDynamicAopProxy(config);
        } else {
            Class targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            } else if (!cglibAvailable) {
                throw new AopConfigException("Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.");
            } else {
                //cglib
                return DefaultAopProxyFactory.CglibProxyFactory.createCglibProxy(config);
            }
        }
    }

  说明:代理过程就是讲具体的增强加入到代理中去。这里动态代理后续会讲,不详细介绍。

   11)springaop的源码过程却是有些复杂。但是思路已经有了哦

   发现增强-->匹配增强器-->创建代理

转载于:https://www.cnblogs.com/ll409546297/p/10114273.html

相关文章:

  • 洛谷P2805 植物大战僵尸
  • python之上下文管理器与contextlib
  • 数据类型之函数笔记
  • Flutter redux 进阶
  • 为什么携程要做好持续交付?
  • 变频电源老化测试重要吗?需要做老化测试吗
  • JS笔记1
  • EOS区块链智能合约开发
  • Oracle 11g:bin目录下3个特效权限的文件:root用户所有者 + s权限
  • 如何使用虚拟机来运行linux,并通过ftp来访问linux服务器(多图详细教学)
  • FaaS 的简单实践
  • 身为极客,一道题测出你究竟有多机智!|活动推荐
  • java web service 写入图片到web/img/
  • 通过调研开源基准测试集,解读大数据的应用现状和开源未来
  • 如何保证以太坊DApp本地存储localStorage的安全性?
  • 2017年终总结、随想
  • CAP理论的例子讲解
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • JavaScript创建对象的四种方式
  • laravel with 查询列表限制条数
  • Mysql数据库的条件查询语句
  • mysql外键的使用
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 记一次用 NodeJs 实现模拟登录的思路
  • 精彩代码 vue.js
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 一道闭包题引发的思考
  • 一些css基础学习笔记
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​什么是bug?bug的源头在哪里?
  • !!Dom4j 学习笔记
  • #{} 和 ${}区别
  • #includecmath
  • $.ajax()参数及用法
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • $GOPATH/go.mod exists but should not goland
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (排序详解之 堆排序)
  • (三)mysql_MYSQL(三)
  • ******之网络***——物理***
  • .“空心村”成因分析及解决对策122344
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .NET的微型Web框架 Nancy
  • .NET企业级应用架构设计系列之技术选型
  • /etc/sudoer文件配置简析
  • /usr/bin/env: node: No such file or directory
  • @Controller和@RestController的区别?
  • @SuppressWarnings(unchecked)代码的作用
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [ 手记 ] 关于tomcat开机启动设置问题
  • [100天算法】-x 的平方根(day 61)
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [AHOI2009]中国象棋 DP,递推,组合数