目的

看看dubbo是怎么给加了@Reference注解的属性注入invoker实例,为什么有时候加了@Reference注解的属性会是null。

ReferenceAnnotationBeanPostProcessor

看到这个名字,就很容易知道,是专门针对@Reference注解的后置处理。
ReferenceAnnotationBeanPostProcessor的代码比较多,下面列一下比较重要的内容。

public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapterimplements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,DisposableBean {// 缓存加了@Referece注解的元数据信息,key是bean的名称或者类名,value是加了@Reference的属性和方法private final ConcurrentMap<String, ReferenceInjectionMetadata> injectionMetadataCache =new ConcurrentHashMap<String, ReferenceInjectionMetadata>(256);// 缓存new过的ReferenceBean,相同的key,只会产生一个ReferenceBeanprivate final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =new ConcurrentHashMap<String, ReferenceBean<?>>();// spring设置属性到bean之前调用该方法@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {// 根据bean的类型,获取需要注入的元数据信息InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);try {// 注入对象metadata.inject(bean, beanName, pvs);} catch (BeanCreationException ex) {throw ex;} catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);}return pvs;}private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {// 如果是自定义的消费者,没有beanName,退化成使用类名作为缓存keyString cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// 双重检查,判断是否需要刷新注入信息ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);// 判断是否需要刷新if (InjectionMetadata.needsRefresh(metadata, clazz)) {// 第一次判断为需要刷新,则锁住injectionMetadataCache对象synchronized (this.injectionMetadataCache) {// 再次判断是否需要刷新metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {// 需要刷新,而且原来缓存的信息不为空,清除缓存信息if (metadata != null) {metadata.clear(pvs);}try {// 生成新的元数据信息metadata = buildReferenceMetadata(clazz);// 放入缓存this.injectionMetadataCache.put(cacheKey, metadata);} catch (NoClassDefFoundError err) {throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +"] for reference metadata: could not find class that it depends on", err);}}}}return metadata;}private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanClass) {// 查找加了@Reference注解的属性Collection<ReferenceFieldElement> fieldElements = findFieldReferenceMetadata(beanClass);// 查找加了@Reference注解的属性// !!!!@Reference还能加到方法上!!!还真没试过// 不过这个不关心,只关注属性的Collection<ReferenceMethodElement> methodElements = findMethodReferenceMetadata(beanClass);return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements);}private List<ReferenceFieldElement> findFieldReferenceMetadata(final Class<?> beanClass) {// 保存加了@Reference注解的属性列表final List<ReferenceFieldElement> elements = new LinkedList<ReferenceFieldElement>();ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {@Overridepublic void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {// 获取属性上的@Reference注解Reference reference = getAnnotation(field, Reference.class);// 如果存在@Reference注解if (reference != null) {// 不支持静态属性的注入if (Modifier.isStatic(field.getModifiers())) {if (logger.isWarnEnabled()) {logger.warn("@Reference annotation is not supported on static fields: " + field);}return;}// 添加到队列里elements.add(new ReferenceFieldElement(field, reference));}}});return elements;}

大致的流程如下:

ReferenceInjectionMetadata

dubbo在ReferenceAnnotationBeanPostProcessor里定义了一个私有的子类
ReferenceInjectionMetadata继承spring定义的InjectionMetadata类。
之所以需要自定义ReferenceInjectionMetadata类,是因为dubbo的@Reference注解可以使用在属性和方法上,需要区分开。但是spring定义的InjectionMetadata类,只支持一个injectedElements集合,代码如下:

public class InjectionMetadata {private static final Log logger = LogFactory.getLog(InjectionMetadata.class);private final Class<?> targetClass;private final Collection<InjectedElement> injectedElements;@Nullableprivate volatile Set<InjectedElement> checkedElements;public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {// 构造函数接收两个参数,类型,注入的元素this.targetClass = targetClass;this.injectedElements = elements;}..................public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;// 优先使用checkedElements来注入,如果checkedElements为空,则直接使用injectedElements(没有调用checkConfigMembers方法,checkedElements会空)Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 循环遍历所有待注入的元素for (InjectedElement element : elementsToIterate) {if (logger.isTraceEnabled()) {logger.trace("Processing injected element of bean '" + beanName + "': " + element);}// 调用注入方法element.inject(target, beanName, pvs);}}}

基于这个原因,dubbo定义了ReferenceInjectionMetadata类,代码如下:

 private static class ReferenceInjectionMetadata extends InjectionMetadata {private final Collection<ReferenceFieldElement> fieldElements;private final Collection<ReferenceMethodElement> methodElements;public ReferenceInjectionMetadata(Class<?> targetClass, Collection<ReferenceFieldElement> fieldElements,Collection<ReferenceMethodElement> methodElements) {// 构造函数接收3个参数,类型,待注入的属性元素,待注入的方法元素// 把fieldElements和methodElements的内容合并,作为InjectionMetadata的injectedElementssuper(targetClass, combine(fieldElements, methodElements));this.fieldElements = fieldElements;this.methodElements = methodElements;}private static <T> Collection<T> combine(Collection<? extends T>... elements) {List<T> allElements = new ArrayList<T>();for (Collection<? extends T> e : elements) {allElements.addAll(e);}return allElements;}public Collection<ReferenceFieldElement> getFieldElements() {return fieldElements;}public Collection<ReferenceMethodElement> getMethodElements() {return methodElements;}}

代码很简单,入参变为3个,属性和方法列表区分开,然后把两者合并起来,调用父类的构造函数,用fieldElements保存属性列表,用methodElements保存方法列表。

ReferenceFieldElement

属性注入实际发生在ReferenceFieldElement类,代码如下:

 private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {private final Field field;private final Reference reference;private volatile ReferenceBean<?> referenceBean;// 构造函数会传入要设置的Field对象,Reference注解对象protected ReferenceFieldElement(Field field, Reference reference) {super(field, null);this.field = field;this.reference = reference;}@Overrideprotected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {Class<?> referenceClass = field.getType();// 构建ReferenceBean对象referenceBean = buildReferenceBean(reference, referenceClass);// 将属性设置为可访问的ReflectionUtils.makeAccessible(field);// 给Field对象设置属性field.set(bean, referenceBean.getObject());}}

ReferenceMethodElement

方法注入实际发生在ReferenceMethodElement类,代码如下:

private class ReferenceMethodElement extends InjectionMetadata.InjectedElement {private final Method method;private final Reference reference;private volatile ReferenceBean<?> referenceBean;protected ReferenceMethodElement(Method method, PropertyDescriptor pd, Reference reference) {super(method, pd);this.method = method;this.reference = reference;}@Overrideprotected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {// 获取类型Class<?> referenceClass = pd.getPropertyType();// 构建ReferenceBean对象referenceBean = buildReferenceBean(reference, referenceClass);// 将方法设置为可访问ReflectionUtils.makeAccessible(method);// 把referenceBean生成的对象作为入参,调用bean对象的method方法// 看到这里,就能明白,@Reference也可以加在那些setXXX方法上// 例如有个userService属性,有个setUserService方法,可以在setUserService方法上加@Reference注解method.invoke(bean, referenceBean.getObject());}}

为什么加了@Reference注解的属性是null

从上面的代码分析,可以知道属性的注入,是靠ReferenceAnnotationBeanPostProcessor后置处理来触发,往filed设置值。
如果这一过程中,发生异常,导致没有成功为field设置值,则加了@Referencce的属性就会一直是null。

2020-07-30 17:00:00.013  WARN 13092 --- [           main] com.alibaba.dubbo.config.AbstractConfig  : []  [DUBBO] null, dubbo version: 2.6.2, current host: 10.0.45.150java.lang.reflect.InvocationTargetException: nullat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at com.alibaba.dubbo.config.AbstractConfig.toString(AbstractConfig.java:474)at java.lang.String.valueOf(String.java:2994)at java.lang.StringBuilder.append(StringBuilder.java:131)at com.alibaba.dubbo.config.spring.beans.factory.annotation.AbstractAnnotationConfigBeanBuilder.build(AbstractAnnotationConfigBeanBuilder.java:79)at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.buildReferenceBean(ReferenceAnnotationBeanPostProcessor.java:385)at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.access$100(ReferenceAnnotationBeanPostProcessor.java:65)at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor$ReferenceFieldElement.inject(ReferenceAnnotationBeanPostProcessor.java:363)at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.postProcessPropertyValues(ReferenceAnnotationBeanPostProcessor.java:92)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1400)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1325)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1171)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)at com.xdchen.bp.award.api.server.Application.main(Application.java:20)
Caused by: java.lang.IllegalStateException: Failed to check the status of the service com.xdchen.searchplatform.searcher.protocol.SearcherService. No provider available for the service com.xdchen.searchplatform.searcher.protocol.SearcherService:1.0.0111 from the url zookeeper://zk1.esf.fdd:2181/com.alibaba.dubbo.registry.RegistryService?application=award.ddxf.bp.fdd&default.reference.filter=traceIdConsumer,default,consumerCatFilter&default.timeout=6000&dubbo=2.6.2&interface=com.xdchen.searchplatform.searcher.protocol.SearcherService&methods=search,searchByBytes,multiSearch,scrollIndex,searchByHttp,searchByIds,multiSearchByBytes&organization=fangdd&owner=chenxudong&pid=13092&register.ip=10.0.45.150&revision=3.8.0&side=consumer&timeout=5000&timestamp=1596099599500&version=1.0.0111 to the consumer 10.0.45.150 use dubbo version 2.6.2at com.alibaba.dubbo.config.ReferenceConfig.createProxy(ReferenceConfig.java:422)at com.alibaba.dubbo.config.ReferenceConfig.init(ReferenceConfig.java:333)at com.alibaba.dubbo.config.ReferenceConfig.get(ReferenceConfig.java:163)at com.alibaba.dubbo.config.spring.ReferenceBean.getObject(ReferenceBean.java:66)... 42 common frames omitted2020-07-30 17:00:00.014  INFO 13092 --- [           main] c.a.d.c.s.b.f.a.ReferenceBeanBuilder     : [] <dubbo:reference singleton="true" interface="com.xdchen.searchplatform.searcher.protocol.SearcherService" uniqueServiceName="com.xdchen.searchplatform.searcher.protocol.SearcherService:1.0.0111" generic="false" version="1.0.0111" timeout="5000" id="com.xdchen.searchplatform.searcher.protocol.SearcherService" /> has been built.

看这一段错误日志,当SearcherService没有任何provider启动的时候调用ReferenceBean.getObject方法,就会抛IllegalStateException异常,设置属性失败。
网上很多说,遇到加@Reference注解的属性为null的,应该就是这个情况。

什么情况会抛No provider的IllegalStateException异常

ReferenceAnnotationBeanPostProcessor.buildReferenceBean

   private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass);ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);if (referenceBean == null) {ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder.create(reference, classLoader, applicationContext).interfaceClass(referenceClass);// 这里报错referenceBean = beanBuilder.build();referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);}return referenceBean;}

buildReferenceBean方法调用ReferenceBeanBuilder.build报错

ReferenceBeanBuilder.build

ReferenceBeanBuilder.build方法是它的父类AbstractAnnotationConfigBeanBuilder的

 public final B build() throws Exception {checkDependencies();B bean = doBuild();configureBean(bean);if (logger.isInfoEnabled()) {// 这里报错logger.info(bean + " has been built.");}return bean;}

ReferenceBeanBuilder.doBuild

ReferenceBeanBuilder重写了doBuild方法,返回ReferenceBean对象

    @Overrideprotected ReferenceBean doBuild() {return new ReferenceBean<Object>();}

所以,问题是出在了ReferenceBean.toString方法上

AbstractConfig.toString

ReferenceBean并没有重写toString方法,但他的根父类是AbstractConfig,看错误日志,可以看到这个:

 at com.alibaba.dubbo.config.AbstractConfig.toString(AbstractConfig.java:474)

AbstractConfig.toString代码如下:

 @Overridepublic String toString() {try {StringBuilder buf = new StringBuilder();buf.append("<dubbo:");buf.append(getTagName(getClass()));Method[] methods = getClass().getMethods();// 拿到当前类的所有方法for (Method method : methods) {try {String name = method.getName();// 过滤剩下get和is开头的方法,但不包括getClass、get和isif ((name.startsWith("get") || name.startsWith("is"))&& !"getClass".equals(name) && !"get".equals(name) && !"is".equals(name)&& Modifier.isPublic(method.getModifiers())&& method.getParameterTypes().length == 0&& isPrimitive(method.getReturnType())) {int i = name.startsWith("get") ? 3 : 2;String key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);// 反射获取方法返回值,拼接字符串// 就是这里报空指针Object value = method.invoke(this, new Object[0]);if (value != null) {buf.append(" ");buf.append(key);buf.append("=\"");buf.append(value);buf.append("\"");}}} catch (Exception e) {logger.warn(e.getMessage(), e);}}buf.append(" />");return buf.toString();} catch (Throwable t) {logger.warn(t.getMessage(), t);return super.toString();}}

ReferenceBean.getObjet

ReferenceBean类实现类FactoryBean接口,实现了getObject方法,getObject方法满足get开头的条件,会被AbstractConfig.toString方法调用到

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {@Overridepublic Object getObject() throws Exception {return get();}public synchronized T get() {if (destroyed) {throw new IllegalStateException("Already destroyed!");}if (ref == null) {init();}return ref;}private void init() {if (initialized) {return;}initialized = true;............ref = createProxy(map);ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);}private T createProxy(Map<String, String> map) {............Boolean c = check;if (c == null && consumer != null) {c = consumer.isCheck();}if (c == null) {c = true; // default true}if (c && !invoker.isAvailable()) {throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());}if (logger.isInfoEnabled()) {logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());}// create service proxyreturn (T) proxyFactory.getProxy(invoker);}

省略了大部分代码,只保留了比较重要的,调用getObject方法,会判断是否初始化过,如果初始化过,直接返回ref;如果没有初始化,则会进行初始化,然后调用createProxy方法来创建代理,如果我们没有配置consumer的check或者check=true,则会检查invoker对象的可用性“invoker.isAvailable()”,如果不可用,就会抛IllegalStateException异常。

避免@Reference注解的属性为null

配置消费者的检查为false,即@Reference(check=false)

ReferenceBean.getObject调用时机的猜测

看的ReferenceFieldElement.inject方法,很容易以为IllegalStateException是在 “field.set(bean, referenceBean.getObject());”这一行报错的,但实际上是在 “referenceBean = buildReferenceBean(reference, referenceClass);”

 @Overrideprotected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {Class<?> referenceClass = field.getType();// 构建ReferenceBean对象referenceBean = buildReferenceBean(reference, referenceClass);// 将属性设置为可访问的ReflectionUtils.makeAccessible(field);// 给Field对象设置属性field.set(bean, referenceBean.getObject());}

为什么要在AbstractConfig.toString就调用了getObject方法,触发报错呢?
如果AbstractConfig.toString过滤掉getObject方法,会发生什么事情呢?

InjectionMetadata.inject方法是遍历checkedElements列表,挨个调用element.inject方法。
如果AbstractConfig.toString过滤掉getObject方法,则首次调用ReferenceBean.getObject方法是在“field.set(bean, referenceBean.getObject());”。异常没有被catch住,checkedElements列表的遍历会被打断。
会出现这样的情况,有一个bean需要注入5个代理对象,但是调用第一个ReferenceBean.getObject的时候抛异常,则注入行为被中断,另外4个属性也没有被注入。

dubbo的@Reference注解作用分析相关推荐

  1. Dubbo的Reference注解必须先启动provider的问题

    目录 现象 看源码分析原因 注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig 注解Reference第二步:从配置文件里获取参数,写入Referenc ...

  2. Dubbo学习记录(八) -- Spring整合Dubbo中@Reference注解解析原理

    Spring整合Dubbo中@Reference注解解析原理 @Reference: 可以用在属性或者方法, 意味着需要引用某个Dubbo服务, 那么Dubbo整合Spring后, 我很好奇怎么把这个 ...

  3. Spring-Boot 整合Dubbo 解决@Reference 注解为null情况

    首先检查一下你的spring boot版本是多少? 如果是2.X 不用看了,spring boot 2.x 必定会出现这个问题, 改为 1.5.9 或其他1.x版本,目前生产环境建议使用1.x版本. ...

  4. Dubbo之@Reference 和 ReferenceBean

    consumer调用dubbo服务 两种方法: 1.构建一个ReferenceBean,然后使用Spring的@Autowired引用服务 @Bean public ReferenceBean< ...

  5. Dubbo流程及源码分析(一)

    扑街前言:之前的文章说明了zookeeper的使用及源码,那么本次我们继续了解和zookeeper的黄金搭档dubbo的相关内容,当然dubbo也是典型的rpc框架,所以我们从客户端和服务端逐个分析, ...

  6. Dubbo 服务订阅源码分析

    Dubbo 服务引用的时机有两个: 第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务 第二个是在 ReferenceBean 对 ...

  7. Alibaba Dubbo框架同步调用原理分析-1

    2019独角兽企业重金招聘Python工程师标准>>> 由于Dubbo底层采用Socket进行通信,自己对通信理理论也不是很清楚,所以顺便把通信的知识也学习一下. n  通信理论 计 ...

  8. java常用注解及功能_SpringBoot 中常用注解及各种注解作用

    本篇文章将介绍几种SpringBoot 中常用注解 其中,各注解的作用为: @PathVaribale 获取url中的数据 @RequestParam 获取请求参数的值 @GetMapping 组合注 ...

  9. Mybatis的@Param注解作用

    单个参数 若无@Param注解,Blog getBlog(Long id);,mybatis中传入的变量名id 可以随便写 如#{0},#{id}, #{aaa} - 如果带有注解 Blog getB ...

最新文章

  1. 一个晚上,输掉了整个世界!
  2. 关闭裁剪功能_4个图片裁剪技巧,瞬间看出Word大神与小白的差距!
  3. Matlab画图小结(二)
  4. 【Linux】一步一步学Linux——login命令(210)
  5. 消息摘要算法-MAC算法系列
  6. java面试题-----------静态代码块和代码块和构造方法执行顺序
  7. BeetleX进程服务管理组件应用
  8. python api数据接口_python写数据api接口
  9. 坑:找到LoadRunner中Recording Options和 Run Time Settings配置选项确实的原因
  10. LTE系统信息块SIB:LTE system information blocks-MIB,SIB-1,2,3,4,5,6,7,8,9,10,11
  11. LeetCode刷题(50)--Word Search
  12. 计算机软件企业所属行业性质,最新企业所属行业类别、分类及行业代码查询表.doc...
  13. java加密与解密(一)
  14. 禁止视频在手机移动端页面中全屏播放代码范例
  15. 《笨方法学Python》第一期
  16. PHP 100 个最常用的函数
  17. 使用Origin软件对数据进行指数拟合的方法
  18. 浏览量(PV)、访客数(UV)、访问次数、跳出率是什么意思?
  19. php获取判断图片大小,php 获取图片尺寸的方法
  20. 胡乱捣鼓03——PID定身12cm直线追踪小车做起来~

热门文章

  1. php图片上传腾讯云cos,PHP生成腾讯云COS接口需要的请求签名
  2. 软考高级系统架构设计师系列论文之:百篇软考高级架构设计师论文范文
  3. 医保局应用RPA机器人在医保报销、数据处理实现自动化提高业务办理效率
  4. table自定义滚动条样式
  5. 金蝶云星空对接打通精诚ERP应付单查询1接口与执行操作接口接口
  6. 形状分析在植物种类鉴定方面的应用
  7. 基于TG110-E050N5的RJ45接口电路EMC设计分析
  8. Halcon三维测量(2):三维点云模型三角化+切片处理+边缘宽度测量
  9. Java 网络编程(大全)
  10. Python自动化测试 软件测试最全教程(附笔记),看完可就业