Java Spring各种依赖注入注解的区别
Spring对于Bean的依赖注入,支持多种注解方式:
@Resource javax.annotation JSR250 (Common Annotations for Java) @Inject javax.inject JSR330 (Dependency Injection for Java) @Autowired org.springframework.bean.factory Spring
直观上看起来,@Autowired是Spring提供的注解,其他几个都是JDK本身内建的注解,Spring对这些注解也进行了支持。但是使用起来这三者到底有什么区别呢?笔者经过方法的测试,发现一些有意思的特性。
区别总结如下:
一、@Autowired有个required属性,可以配置为false,这种情况下如果没有找到对应的bean是不会抛异常的。@Inject和@Resource没有提供对应的配置,所以必须找到否则会抛异常。
二、 @Autowired和@Inject基本是一样的,因为两者都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入。但是@Resource是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。当然,两者都是BeanPostProcessor。
@Autowired和@Inject - 默认 autowired by type - 可以 通过@Qualifier 显式指定 autowired by qualifier name。 - 如果 autowired by type 失败(找不到或者找到多个实现),则退化为autowired by field name @Resource - 默认 autowired by field name - 如果 autowired by field name失败,会退化为 autowired by type - 可以 通过@Qualifier 显式指定 autowired by qualifier name - 如果 autowired by qualifier name失败,会退化为 autowired by field name。但是这时候如果 autowired by field name失败,就不会再退化为autowired by type了。
TIPS Qualified name VS Bean name
在Spring设计中,Qualified name并不等同于Bean name,后者必须是唯一的,但是前者类似于tag或者group的作用,对特定的bean进行分类。可以达到getByTag(group)的效果。对于XML配置的bean,可以通过id属性指定bean name(如果没有指定,默认使用类名首字母小写),通过标签指定qualifier name:
<bean id="lamborghini" class="me.arganzheng.study.spring.autowired.Lamborghini">
<qualifier value="luxury"/>
<!-- inject any dependencies required by this bean -->
</bean>
如果是通过注解方式,那么可以通过@Qualifier注解指定qualifier name,通过@Named或者@Component(@Service,@Repository等)的value值指定bean name:
@Component("lamborghini")
@Qualifier("luxury")
public class Lamborghini implements Car {
}
或者
@Component
@Named("lamborghini")
@Qualifier("luxury")
public class Lamborghini implements Car {
}
同样,如果没有指定bean name,那么Spring会默认是用类名首字母小写(Lamborghini=>lamborghini)。
三、 通过Anotation注入依赖的方式在XML注入方式之前进行。如果对同一个bean的依赖同时使用了两种注入方式,那么XML的优先。但是不同担心通过Anotation注入的依赖没法注入XML中配置的bean,依赖注入是在bean的注册之后进行的。
四、目前的autowired by type方式(笔者用的是3.2.3.RELEASE版本),Spring的AutowiredAnnotationBeanPostProcessor实现都是有”bug”的,也就是说@Autowired和@Inject都是有坑的(称之为坑,不称之为bug是因为貌似是故意的。。)。这是来源于线上的一个bug,也是这边文章的写作原因。现场如下:
application-context.xml中有如下定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="me.arganzheng.study" />
<util:constant id="en"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN" />
<util:constant id="ja"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.JP" />
<util:constant id="ind"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.IND" />
<util:constant id="pt"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.PT" />
<util:constant id="th"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.TH" />
<util:constant id="ar"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.AR" />
<util:constant id="en-rIn"
static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN_RIN" />
<util:map id="languageChangesMap" key-type="java.lang.String"
value-type="java.lang.String">
<entry key="pt" value="pt" />
<entry key="br" value="pt" />
<entry key="jp" value="ja" />
<entry key="ja" value="ja" />
<entry key="ind" value="ind" />
<entry key="id" value="ind" />
<entry key="en-rin" value="en-rIn" />
<entry key="in" value="en-rIn" />
<entry key="en" value="en" />
<entry key="gb" value="en" />
<entry key="th" value="th" />
<entry key="ar" value="ar" />
<entry key="eg" value="ar" />
</util:map>
</beans>
其中static-field应用的常量定义在如下类中:
package me.arganzheng.study.spring.autowired;
public interface Constants {
public interface Language {
public static final String EN = "CommonConstants.LANG_ENGLISH";
public static final String JP = "CommonConstants.LANG_JAPANESE";
public static final String IND = "CommonConstants.LANG_INDONESIAN";
public static final String PT = "CommonConstants.LANG_PORTUGUESE";
public static final String TH = "CommonConstants.LANG_THAI";
public static final String EN_RIN = "CommonConstants.LANG_ENGLISH_INDIA";
public static final String AR = "CommonConstants.LANG_Arabic";
}
}
然后如果我们在代码中如下声明依赖:
public class AutowiredTest extends BaseSpringTestCase {
@Autowired
private Map<String, String> languageChangesMap;
@Test
public void testAutowired() {
notNull(languageChangesMap);
System.out.println(languageChangesMap.getClass().getSimpleName());
System.out.println(languageChangesMap);
}
}
Guess what,诡异的事情发生了!
运行结果如下:
LinkedHashMap
{en=CommonConstants.LANG_ENGLISH, ja=CommonConstants.LANG_JAPANESE, ind=CommonConstants.LANG_INDONESIAN, pt=CommonConstants.LANG_PORTUGUESE, th=CommonConstants.LANG_THAI, ar=CommonConstants.LANG_Arabic, en-rIn=CommonConstants.LANG_ENGLISH_INDIA}
也就是说Map
严重: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@5c51ee0a] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@6e301e0]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'me.arganzheng.study.spring.autowired.AutowiredTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.Map me.arganzheng.study.spring.autowired.AutowiredTest.languageChangesMap; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
... 28 more
debug了一下,发现确实是Spring的一个bug。在DefaultListableBeanFactory的这个方法出问题了:
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
...
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
Class<?> keyType = descriptor.getMapKeyType();
if (keyType == null || !String.class.isAssignableFrom(keyType)) {
if (descriptor.isRequired()) {
throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
"] must be assignable to [java.lang.String]");
}
return null;
}
Class<?> valueType = descriptor.getMapValueType();
if (valueType == null) {
if (descriptor.isRequired()) {
throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
return matchingBeans;
}
...
}
关键在这一句:Map
严重: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@9476189] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@2d546e21]
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=languageChangesMap)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
... 28 more
debug了一下,发现跟没有指定qualifie name是一样的执行路径。不是指定了bean name了吗?为什么还是autowired by type呢?仔细查看了一下才发现。DefaultListableBeanFactory的doResolveDependency方法对首先对类型做了区别:
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
if (type.isArray()) {
Class<?> componentType = type.getComponentType();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return converter.convertIfNecessary(matchingBeans.values(), type);
}
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
Class<?> elementType = descriptor.getCollectionType();
if (elementType == null) {
if (descriptor.isRequired()) {
throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return converter.convertIfNecessary(matchingBeans.values(), type);
}
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
Class<?> keyType = descriptor.getMapKeyType();
if (keyType == null || !String.class.isAssignableFrom(keyType)) {
if (descriptor.isRequired()) {
throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
"] must be assignable to [java.lang.String]");
}
return null;
}
Class<?> valueType = descriptor.getMapValueType();
if (valueType == null) {
if (descriptor.isRequired()) {
throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
}
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
return matchingBeans;
}
else {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(type, "", descriptor);
}
return null;
}
if (matchingBeans.size() > 1) {
String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
if (primaryBeanName == null) {
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(primaryBeanName);
}
return matchingBeans.get(primaryBeanName);
}
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
autowiredBeanNames.add(entry.getKey());
}
return entry.getValue();
}
}
如果是Array,Collection或者Map,则根据集合类中元素的类型来进行autowired by type(Map使用value的类型)。为什么这么特殊处理呢?原来,Spring是为了达到这样的目的:让你可以一次注入所有符合类型的实现,也就是说可以这样子注入:
@Autowired private List<Car> cars;
如果你的car有多个实现,那么都会注入进来,不会再报
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [me.arganzheng.study.spring.autowired.Car] is defined: expected single matching bean but found 2: [audi, toyota].
然而,上面的情况如果你用@Resource则不会有这个问题:
public class AutowiredTest extends BaseSpringTestCase {
@Resource
@Qualifier("languageChangesMap")
private Map<String, String> languageChangesMap;
@Test
public void testAutowired() {
assertNotNull(languageChangesMap);
System.out.println(languageChangesMap.getClass().getSimpleName());
System.out.println(languageChangesMap);
}
}
正常运行:
LinkedHashMap
{pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
当然,你如果不指定@Qualifier(“languageChangesMap”),同时field name不是languageChangesMap,那么还是一样报错的。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(shareable=true, mappedName=, description=, name=, type=class java.lang.Object, authenticationType=CONTAINER, lookup=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:438)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:416)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:550)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:303)
... 26 more
而且,@Resource也可以实现上面的List接收所有实现:
public class AutowiredTest extends BaseSpringTestCase {
@Resource
@Qualifier("languageChangesMap")
private Map<String, String> languageChangesMap;
@Resource
private List<Car> cars;
@Test
public void testAutowired() {
assertNotNull(languageChangesMap);
System.out.println(languageChangesMap.getClass().getSimpleName());
System.out.println(languageChangesMap);
assertNotNull(cars);
System.out.println(cars.getClass().getSimpleName());
System.out.println(cars);
}
}
运行的妥妥的:
LinkedHashMap
{pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
ArrayList
[me.arganzheng.study.spring.autowired.Audi@579584da, me.arganzheng.study.spring.autowired.Toyota@19453122]
这是因为@Resource注解使用的是CommonAnnotationBeanPostProcessor处理器,跟AutowiredAnnotationBeanPostProcessor不是同一个作者[/偷笑]。这里就不分析了,感兴趣的同学可以自己看代码研究一下。
最终结论如下:
1、@Autowired和@Inject
autowired by type 可以 通过@Qualifier 显式指定 autowired by qualifier name(非集合类。注意:不是autowired by bean name!) 如果 autowired by type 失败(找不到或者找到多个实现),则退化为autowired by field name(非集合类)
2、@Resource
默认 autowired by field name 如果 autowired by field name失败,会退化为 autowired by type 可以 通过@Qualifier 显式指定 autowired by qualifier name 如果 autowired by qualifier name失败,会退化为 autowired by field name。但是这时候如果 autowired by field name失败,就不会再退化为autowired by type了
测试工程保存在GitHub上,是标准的maven工程,感兴趣的同学可以clone到本地运行测试一下。
补充
有同事指出Spring官方文档上有这么一句话跟我的结有点冲突:
However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id.
也就是说@Autowired即使加了@Qualifier注解,其实也是autowired by type。@Qualifier只是一个限定词,过滤条件而已。重新跟进了一下代码,发现确实是这样子的。Spring设计的这个 @Qualifier name 并不等同于 bean name。他有点类似于一个tag。不过如果这个tag是唯一的化,那么其实效果上等同于bean name。实现上,Spring是先getByType,得到list candicates,然后再根据qualifier name进行过滤。
再定义一个兰博基尼,这里使用@Qualifier指定:
package me.arganzheng.study.spring.autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("luxury")
public class Lamborghini implements Car {
}
再定义一个劳斯莱斯,这里故意用@Named指定:
package me.arganzheng.study.spring.autowired;
import javax.inject.Named;
import org.springframework.stereotype.Component;
@Component
@Named("luxury")
public class RollsRoyce implements Car {
}
测试一下注入定义的豪华车:
package me.arganzheng.study.spring.autowired;
import static junit.framework.Assert.assertNotNull;
import java.util.List;
import me.arganzheng.study.BaseSpringTestCase;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
/**
*
* @author zhengzhibin
*
*/
public class AutowiredTest extends BaseSpringTestCase {
@Autowired
@Qualifier("luxury")
private List<Car> luxuryCars;
@Test
public void testAutowired() {
assertNotNull(luxuryCars);
System.out.println(luxuryCars.getClass().getSimpleName());
System.out.println(luxuryCars);
}
}
运行结果如下:
ArrayList [me.arganzheng.study.spring.autowired.Lamborghini@66b875e1, me.arganzheng.study.spring.autowired.RollsRoyce@58433b76]
补充:Autowiring modes
Spring支持四种autowire模式,当使用XML配置方式时,你可以通过autowire属性指定。
no. (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. byName. Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property. byType. Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. constructor. Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
如果使用@Autowired、@Inject或者@Resource注解的时候,则稍微复杂一些,会有一个失败退化过程,并且引入了Qualifier。不过基本原理是一样。
Java Spring各种依赖注入注解的区别的更多相关文章
- 详解Java Spring各种依赖注入注解的区别
注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Comp ...
- 在ABAP里模拟实现Java Spring的依赖注入
Dependency Injection- 依赖注入,在Java Spring框架中有着广泛地应用.通过依赖注入,我们不必在应用代码里繁琐地初始化依赖的资源,非常方便. 那么ABAP能否从语言层面上也 ...
- 一步一步深入spring(3)--spring的依赖注入方式
对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程,sprin ...
- spring的依赖注入是什么意思
最近学习spring框架,对依赖注入有些模糊,遂上网翻阅资料,做了下列总结,原博客为CSDN 南夏的 spring的依赖注入是什么意思,侵删! Spring 能有效地组织J2EE应用各层的对象.不管是 ...
- SpringBoot系列: 理解 Spring 的依赖注入(二)
==============================Spring 容器中 Bean 的名称==============================声明 bean 有两个方式, 一个是 @B ...
- SpringBoot系列: 理解 Spring 的依赖注入(一)
==============================Spring 的依赖注入==============================对于 Spring 程序, Spring 框架为我们提供 ...
- Spring中依赖注入的四种方式
在Spring容器中为一个bean配置依赖注入有三种方式: · 使用属性的setter方法注入 这是最常用的方式: · 使用构造器注入: · 使用Filed注入(用于注解方式). 使用属性的sett ...
- spring中依赖注入
理解依赖注入:参考https://blog.csdn.net/taijianyu/article/details/2338311 一.依赖注入让bean与bean之间以配置文件组织在一起,而不是以硬编 ...
- Spring系列.依赖注入配置
依赖注入的配置 Spring的依赖注入分为基于构造函数的依赖注入和基于setter方法的依赖注入. 基于构造函数的依赖注入 <!-- 通过构造器参数索引方式依赖注入 --> <bea ...
随机推荐
- DedeCMS官方手册
DedeCMSV5.3使用手册 DedeCMSV57数据库结构文档 Dedecms 文件目录结构
- js判断上传文件大小
下面提供三款网页特效判断上传文件大小哦,这三种方法是现在限制文件上传大小比较好的方法,可以在客户上传文件时限制上传文件大小判断处理<!doctype html public "-//w ...
- BNU10792:沙漠旅行者
有个旅行者计划横穿沙漠,沙漠中水资源很匮乏.旅行者需要依靠补给站的支持,才能横穿整个沙漠.假设所有的补给站都在一条直线上,而且旅行者一定沿着这条直线走.起点在1号补给站,终点在第N号补给站,起点和终点 ...
- [RxJS] Observables can complete
The Observer object has the functions next() and error(). In this lesson we will see the other (and ...
- error C2143: 语法错误 : 缺少“;”(在“using”的前面)
class JJMenuScene : public cocos2d::CCLayer { public: // Here's a difference. Method 'init' in cocos ...
- JavaScript的68个技巧一
1. 严格模式 在自己的项目中 你可以坚持只使用" 严格模式 " 或只使用" 非严格模式 "的策略.但如果你要编写健壮的代码应对各种各样的代码连接 你有两个可选 ...
- Python-elementTree方法解析xml文件-01
[XML基本概念介绍] XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据.概念一: <foo> # foo元素的起始标签 ...
- 【iOS开发之静态库、动态库】
什么是库? 库 就是程序代码的集合, 是共享程序代码的一种方式,库一般分两类:开源库和闭源库.github中共享一般是开源库:闭源库分为:静态库和动态库,闭源库不开放源代码,是经过编译的二进制文件,一 ...
- NPOI心得
一个Excel文件表示为一个IWookbook,Sheet是ISheet,其它细分为IRow,ICell. 2003和2007版本为IWookbook接口的不同实现:HSSFWookbook和XSSF ...
- 【Oracle】-【权限-ORA-04043】- ORA-04043: object "SYS"."V_$DATABASE" does not exist
用非dba账号(但赋予了DBA角色)登录一个新的10g数据库想看下版本号, SQL> desc v$instance; ERROR: ORA-04043: object "SYS&qu ...