1.9。基于注解的容器配置
注解在配置Spring方面比XML更好吗?基于注解的配置的引入提出了一个问题,即这种办法是否比XML“更好”。简短的答案是“取决于状况”。长话短说,每种办法都有其优缺点,通常,由开发人员决定哪种策略更适宜他们。因为定义形式的不同,注解在申明中提供了很多上下文,从而使配置更短,更简洁。然而,XML善于连贯组件而不接触其源代码或从新编译它们。一些开发人员更喜爱将布线搁置在凑近源的地位,而另一些开发人员则认为带注解的类不再是POJO,而且,该配置变得扩散且难以管制。无论抉择如何,Spring都能够包容两种款式,甚至能够将它们混合在一起。值得指出的是,通过其JavaConfig选项,Spring容许以非侵入形式应用注解,而无需接触指标组件的源代码。
注解是XML配置的代替办法,该配置依赖字节码元数据来连贯组件,而不是尖括号申明。
通过应用相干的 类,办法或字段 申明上的注解,开发人员无需应用XML来形容bean的连贯,而是将配置移入组件类自身。
如示例中所述:将RequiredAnnotationBeanPostProcessor,通过BeanPostProcessor的形式与注解联合应用是扩大Spring IoC容器的罕用办法。
例如,Spring 2.0引入了应用@Required注解强制执行必须属性的可能性。 Spring 2.5引入@Autowired注解,提供的性能与主动拆卸合作器中所述的性能雷同,但具备更细粒度的管制和更宽泛的适用性。Spring 2.5还增加了对JSR-250批注(例如 @PostConstruct和@PreDestroy)的反对。 Spring 3.0减少了对javax.inject包中蕴含的JSR-330(Java依赖性注入)注解的反对,例如@Inject 和@Named。
注解注入在XML注入之前执行。因而,XML配置将笼罩通过注解注入的属性
与平常一样,您能够依据类名将它们注册为独自的bean定义,但也能够通过在基于XML的Spring配置中蕴含以下标记来隐式注册它们:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/></beans>
<context:annotation-config/> 隐式注册后置处理器包含 : AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor PersistenceAnnotationBeanPostProcessor RequiredAnnotationBeanPostProcessor并且当应用<context:component-scan/>后,即可将<context:annotation-config/>省去
<context:annotation-config/>只在定义它的雷同应用程序上下文中查找对于bean的注解。
这意味着,如果你把<context:annotation-config/>定义在WebApplicationContext的DispatcherServlet中,它只是查看controllers中的@Autowired注解,而不是你的services。
上边的这段话意思不是很明确,须要解释一下以前用web.xml配置时的Spring启动流程拿出几段配置<!--配置开始 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring/spring-base.xml </param-value> </context-param><!--配置完结 -->上边的配置应该是多年前webi利用的根底配置,理一下tomcat启动后如何调用Spring的大略流程1. tomcat读取web.xml文件(此处不论tomcat如何找到xml),解析内容并分组,分成ServletContainerInitializer,servlet,listener,context-param等多个数组2.一一进行解析,先解析ServletContainerInitializer //这个就相当典型了 这个货色就是之前的文章讲过的ServletContainerInitializer //Tomcat启动会查找ServletContainerInitializer实现类并执行其中的onStartup办法。 //Spring-web模块存在ServletContainerInitializer实现类,所以Tomcat启动会调用Spring-web的代码。 //然而咱们用Spring框架的话不须要实现这个接口,实现一个Spring的接口WebApplicationInitializer。 //就能够由Tomcat调用Spring-web的ServletContainerInitializer实现类 Iterator i$ = this.initializers.entrySet().iterator(); while(i$.hasNext()) { Entry entry = (Entry)i$.next(); try { ((ServletContainerInitializer)entry.getKey()).onStartup((Set)entry.getValue(), this.getServletContext()); } catch (ServletException var22) { log.error(sm.getString("standardContext.sciFail"), var22); ok = false; break; } }然而这里咱们并没有用这种形式而是用了listener的形式持续往下看3. 解析listener,这里this.listenerStart()会解析咱们配置的ContextLoaderListener if (ok && !this.listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } 就在这里tomcat关联上了Spring的ApplicationContext,会实例化XmlWebApplicationContext, 实例化时取出context-param中的name为contextConfigLocation的配置文件,来进行解析注册 4.解析servlet,this.loadOnStartup(this.findChildren())来解析servlet, if (ok && !this.loadOnStartup(this.findChildren())) { log.error(sm.getString("standardContext.servletFail")); ok = false; }这里就会进入DispatcherServlet的init办法,init办法中会依据以后的ServletContext查找在此之前有没有初始化过Spring的ApplicationContext,而后再判断以后DispatcherServlet有没有ApplicationContext,如果没有就初始化一个并把之前初始化ApplicationContext的设置为父节点总结一下,也就是说用了上边的配置的话,tomcat在启动过程中,会初始化两遍并生成两个ApplicationContext对象,第一遍解析context-param中param-name 为contextConfigLocation的配置文件, 并以此配置文件生成一个ApplicationContext ROOT第二遍是解析DispatcherServlet servlet的spring-mvc.xml配置文件, 再以此配置文件生成一个ApplicationContext,并将ROOT设置为父节点因而就产生了一个问题,当你在两个ApplicationContext都能够扫描到同一个Bean的时候,那么这个bean在连个ApplicationContext中都各存在一个实例,并且实例不一样举一个之前遇到的问题: 之前想给某个controller加一个AOP,拦挡某些办法进行非凡解决,然而我把 <aop:aspectj-autoproxy/>这个注解 放到了上面这个档次的配置文件中了 <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring/spring-base.xml </param-value> </context-param> 最初我的AOP并没有失效,起初又把注解挪到了spring-mvc.xml中,才失效。 之前百度搜到说:spring-mvc 的配置扫描优先于spring的配置文件 通过调试才了解这句话: 我的controller在spring的ApplicationContext中有一份被AOP代理的对象 在spring-mvc的ApplicationContext中还有一份没被代理的一般对象 因为spring-mvc配置加载的晚,所以用到的都是没有被代理的对象
1.9.1。@Required
该@Required注解实用于bean属性setter办法,如上面的例子:
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}
这个注解要求,必须在配置时通过bean定义中的显式属性值或主动拆卸来填充bean属性。
如果未填充bean属性,容器将抛出异样。
这样显式的失败,防止了实例在利用的时候呈现NullPointerException的状况。
@Required注解在Spring Framework 5.1时正式被弃用,Spring更同意应用构造函数注入来进行必须参数的设置(或者应用InitializingBean.afterPropertiesSet()的自定义实现来进行bean属性的设置)。
1.9.2。@Autowired
在本节蕴含的示例中,JSR330的@Inject正文能够用来代替Spring的@Autowired正文。
您能够将@Autowired注解利用于构造函数,如以下示例所示:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ...}
从Spring Framework 4.3开始,@Autowired如果指标bean仅定义一个构造函数作为开始,则不再须要在此类构造函数上进行注解。然而,如果有多个构造函数可用,并且没有主/默认构造函数,则必须至多注解一个构造函数,@Autowired以批示容器应用哪个构造函数。
您还能够将@Autowired注解利用于传统的setter办法,如以下示例所示:
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}
您还能够将注解利用于具备任意名称和多个参数的办法,如以下示例所示:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; }}
您也能够将其利用于@Autowired字段,甚至能够将其与构造函数混合应用,如以下示例所示:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ...}
确保指标组件(例如MovieCatalog或CustomerPreferenceDao)与带@Autowired注解的注入点的类型统一地申明。否则,注入可能会因为运行时呈现“no type match found”谬误而失败。对于通过类门路扫描找到的xml定义的bean或组件类,容器通常事后晓得具体的类型。然而,对于@Bean工厂办法,您须要确保申明的返回类型具备足够的表达能力。对于实现多个接口的组件,或者对于可能由其实现类型援用的组件,思考在您的工厂办法上申明最特定的返回类型(至多与援用您的bean的注入点所要求的那样特定)。
您还能够将@Autowired注解增加到须要该类型数组的字段或办法中,批示Spring提供特定类型的所有bean ,如以下示例所示:
public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ...}
如以下示例所示,这同样实用于类型化汇合:
public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ...}
如果心愿数组或列表中的我的项目以特定程序排序,则指标bean能够实现该org.springframework.core.Ordered接口或应用@Order或规范@Priority注解。否则,它们的程序将遵循容器中相应指标bean定义的注册程序。您能够应用@Order在指标类级别和@Bean办法上申明注解。@Order值可能会影响注入点的优先级,但请留神它们不会影响单例启动程序,这是由依赖关系和@DependsOn申明确定的正交关系。(举例:a,b,c三个bean设置的order别离为1,2,3,然而a依赖c,所以a在初始化的时候会初始化c,导致c比b提前初始化)请留神,规范javax.annotation.Priority注解在该@Bean级别不可用 ,因为无奈在办法上申明它。能够通过将@Order值与@Primary每个类型的单个bean联合应用来对其语义进行建模。
即便是类型化的Map实例也能够主动注入,键蕴含相应的bean名称是String类型,值是对应的bean实例,如上面的示例所示:
public class MovieRecommender { private Map<String, MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; }}
默认状况下,当给定注入点没有匹配的候选bean可用时,主动拆卸将失败。对于申明的数组,汇合或映射,至多应有一个匹配元素。
默认是将带注解的办法和字段视为必须要注入的依赖项。
你能够通过标记为非必须注入来扭转这个行为(例如,通过在@Autowired中设置required属性为false):
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required = false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ...}
@Autowired(required = false)用在办法上时
当存在任何一个参数不可注入,则基本不会调用该办法。
在这种状况下,齐全不须要填充非必须字段,而保留其默认值。
当办法有多个参数时,能够应用该注解标识其中的某个参数能够不被注入
public class ServiceController { private ServiceTwo serviceTwo; private CusService serviceOne; public ServiceController(CusService cusService, @Autowired(required = false) ServiceTwo serviceTwo){ this.serviceOne = cusService; this.serviceTwo = serviceTwo; }}
在任何给定bean类中,只有一个构造函数能够申明@Autowired,并将required属性设置为true,以批示当作为Spring bean应用时要主动拆卸的构造函数。因而,如果required属性的默认值为true,那么只有一个构造函数能够应用@Autowired注解。如果有多个构造函数申明注解,那么它们都必须申明required=false,能力被认为是主动拆卸的候选者(相似于XML中的autowire=constructor)。通过在Spring容器中匹配bean能够满足的依赖关系最多的构造函数将被抉择。如果没有一个候选函数能够满足,那么将应用主/默认构造函数(如果存在)。相似地,如果一个类申明了多个构造函数,然而没有一个是用@Autowired注解的,那么一个主/默认构造函数(如果有的话)将会被应用。如果一个类只申明了一个构造函数,那么它将始终被应用,即便没有@Autowired注解。请留神,带注解的构造函数不用是public的。倡议在setter办法上的已弃用的@Required正文上应用@Autowired属性。将required属性设置为false示意该属性对于主动拆卸目标是不须要的,并且如果该属性不能主动拆卸,则疏忽它。另一方面,@Required更强制,因为它强制用容器反对的任何办法设置属性,如果没有定义值,则会引发相应的异样。
另外,您能够通过Java8来表白特定依赖项的非必须性质java.util.Optional,如以下示例所示:
public class SimpleMovieLister { @Autowired public void setMovieFinder(Optional<MovieFinder> movieFinder) { ... }}
从Spring Framework 5.0开始,您还能够应用@Nullable注解(任何包中的Nullable注解,例如,javax.annotation.Nullable来自JSR-305的注解)。
应用此注解标识该参数不肯定会被注入,有可能会是空值
public class SimpleMovieLister { @Autowired public void setMovieFinder(@Nullable MovieFinder movieFinder) { ... }}
您还能够对这些接口(BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher,和MessageSource)应用@Autowired。
这些接口及其扩大接口(例如ConfigurableApplicationContext或ResourcePatternResolver)将主动解析,而无需进行非凡设置。
以下示例主动拆卸ApplicationContext对象:
public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ...}
在@Autowired,@Inject,@Value,和@Resource注解由Spring注册的BeanPostProcessor实现。 这意味着您不能在本人的类型BeanPostProcessor或BeanFactoryPostProcessor类型(如果有)中利用这些注解。必须通过应用XML或Spring@Bean办法显式地“连贯”这些类型。不仅相当上一章的内容: 您应该看到一条参考性日志音讯: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。 这条音讯的意思大略就是说这个bean没有失去所有BeanPostProcessor的解决如果您自定义的BeanPostProcessor或BeanFactoryPostProcessor在主动注入的BeanPostProcessor之前实例化那么就无奈为您注入您想要的参数。
1.9.3。@Primary
因为按类型主动布线可能会导致多个候选对象,因而通常有必要对抉择过程进行更多管制。
一种实现此目标的办法是应用Spring的 @Primary注解。
@Primary批示当多个bean是要主动拆卸到单值依赖项的候选对象时,应给予特定bean优先权。
如果候选中恰好存在一个主bean,则它将成为主动拆卸的值。
思考以下定义firstMovieCatalog为次要配置的配置MovieCatalog:
@Configurationpublic class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() { ... } @Bean public MovieCatalog secondMovieCatalog() { ... } // ...}
应用后面的配置,以下内容MovieRecommender将主动注入到 firstMovieCatalog:
public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; // ...}
1.9.4。@Qualifier
@Primary当能够确定一个次要候选对象时,它是在几种状况下按类型应用主动拆卸的无效办法。
当须要对抉择过程进行更多管制时,能够应用Spring的@Qualifier注解。
您能够将限定符值与特定的参数相关联,从而放大类型匹配的范畴,以便为每个参数抉择特定的bean。
在最简略的状况下,这能够是简略的描述性值,如以下示例所示:
public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ...}
您还能够@Qualifier在各个结构函数参数或办法参数上指定注解,如以下示例所示:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ...}
上面的示例显示了相应的bean定义。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier value="main"/> <!-- 指定qualifier属性 --> </bean></beans>
bean名称被认为是默认的qualifier值。
也能够不应用qualifier而是将该bean的id定义为main,达到雷同的匹配成果。
然而,只管您能够应用这种约定来按名称援用特定的bean,但@Autowired基本上是对于类型驱动的注入,qualifier只是在类型之上的可选选项,这意味着,即便应用了bean名称来进行qualifier的限定,qualifier 值也总是在类型匹配集中抉择雷同名称的bean。
qualifier 也实用于collections,
如前所述—例如 Set<MovieCatalog>,在这种状况下,依据申明的qualifier值,所有匹配的bean都作为一个汇合注入。
这意味着qualifier不用是惟一的。相同,它们形成了过滤规范。例如,您能够定义具备雷同qualifier值“action”的多个MovieCatalog bean,
所有这些bean都被注入到带有@Qualifier(“action”)正文的汇合中。
public class ServiceController { @Autowired @Qualifier("main") private List<MovieCatalog> serviceList;}<bean class="example.SimpleMovieCatalogOne"> <qualifier value="main"/> <!-- 指定雷同的qualifier属性 --></bean><bean class="example.SimpleMovieCatalogTwo"> <qualifier value="main"/> <!-- 指定雷同的qualifier属性 --></bean><bean class="example.SimpleMovieCatalogThree"> <qualifier value="action"/> <!-- 指定雷同的qualifier属性 --></bean>
如果没有其余注解(例如qualifier或primary ),对于非惟一依赖状况,Spring将注入点名称(即字段名称或参数名称)与指标bean名称或者bean id匹配,并抉择同名的候选对象(如果有同名的的话,没有同名的话则仍然抛出异样)。
如果您打算通过名称进行依赖注入,请不要次要应用@Autowired,即便它可能通过bean名称在类型匹配的候选者中进行抉择。
应用JSR-250 @Resource注解:1. 如果同时指定了name和type,依照bean Name 和 bean 类型同时匹配2. 如果指定了name,就依照bean Name 匹配 3. 如果指定了type,就依照类型匹配4. 如果既没有指定name,又没有指定type,就先依照beanName匹配; 如果没有匹配,再依照类型进行匹配;测试 @Resource的时候还发现一个有意思的货色,当被注入的是个List的时候,不论是什么类型的List,只有@Resource加了name条件,都能被注入进去,比方 List<String> 会被注入到List<MovieCatalog> 大家能够试一下@Autowired注解: 在按类型抉择候选bean之后,再在候选者bean中抉择雷同名称的。
@Autowired实用于 字段,构造方法,和多参数办法,容许通过qualifier注解在参数级别上放大范畴。
相比之下,@Resource只反对具备单个参数的字段和bean属性设置器办法。
因而,如果注入指标是构造函数或多参数办法,则应该保持应用qualifier。
您能够创立本人的自定义限定符注解。为此,请定义一个注解并在您的定义中提供该注解,如以下示例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface Genre { String value();}
而后,您能够在主动连贯的字段和参数上提供自定义限定符,如以下示例所示:
public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ...}
接下来,您能够为候选bean定义提供信息。您能够增加<qualifier></qualifier>标记作为<bean></bean>标记的子元素,而后指定类型和值来匹配您的自定义qualifier注解。该类型与正文的全限定类名匹配。
另外,为了不便起见,如果不存在名称抵触的危险,您能够应用简短的类名。
上面的例子演示了这两种办法:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="Genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="example.Genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/></beans>
在某些状况下,应用没有值的注解就足够了。当注解用于更通用的目标,并且能够跨几种不同类型的依赖项利用时,这一点十分有用。例如,您能够提供一个脱机目录,当没有可用的Internet连贯时能够搜寻该目录。首先,定义简略正文,如下例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface Offline {}
而后将注解增加到要主动拆卸的字段或属性,如以下示例所示:
public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ...}
当初,bean定义仅须要一个限定符type,如以下示例所示:
<bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> <!-- inject any dependencies required by this bean --></bean>
您还能够定义自定义qualifier注解,自定义的注解能够定义除了简略value属性之外的属性。
如果随后在要主动拆卸的字段或参数上指定了多个属性值,则bean定义必须与所有此类属性值匹配能力被视为主动拆卸候选。
例如,请思考以下注解定义:
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface MovieQualifier { String genre(); Format format();}
在这种状况下Format是一个枚举,定义如下:
public enum Format { VHS, DVD, BLURAY}
要主动拆卸的字段将用自定义qualifier进行注解,并包含这两个属性的值:genre和format,如以下示例所示:
public class MovieRecommender { @Autowired @MovieQualifier(format=Format.VHS, genre="Action") private MovieCatalog actionVhsCatalog; @Autowired @MovieQualifier(format=Format.VHS, genre="Comedy") private MovieCatalog comedyVhsCatalog; @Autowired @MovieQualifier(format=Format.DVD, genre="Action") private MovieCatalog actionDvdCatalog; @Autowired @MovieQualifier(format=Format.BLURAY, genre="Comedy") private MovieCatalog comedyBluRayCatalog; // ...}
最初,bean定义应该蕴含匹配的限定符值。这个例子还演示了您能够应用bean元属性来代替<qualifier></qualifier>元素。
如果可用,<qualifier></qualifier>元素及其属性优先,然而如果没有这样的限定符,主动拆卸机制就会回到<meta>标签中提供的值上,就像上面例子中的最初两个bean定义一样:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Action"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Comedy"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="DVD"/> <meta key="genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="BLURAY"/> <meta key="genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean></beans>
1.9.5。将泛型用作主动拆卸限定符
除了@Qualifier注解之外,您还能够将Java泛型类型用作资格的隐式模式。例如,假如您具备以下配置:
@Configurationpublic class MyConfiguration { @Bean public StringStore stringStore() { return new StringStore(); } @Bean public IntegerStore integerStore() { return new IntegerStore(); }}
假如后面的bean实现了一个通用接口(即Store<String>和 Store<Integer>)
class StringStore implements Store<String>{ } class IntegerStore implements Store<Integer>{ }
则能够@Autowire将该Store接口和通用用作限定符,如以下示例所示:
@Autowiredprivate Store<String> s1; // <String> qualifier, 注入 stringStore bean@Autowiredprivate Store<Integer> s2; // <Integer> qualifier, 注入 the integerStore bean
在主动拆卸列表,Map实例和数组时,通用限定符也实用。上面的示例主动连贯泛型List:
// 只注入 Store<Integer> 类型// Store<String> 不会被注入@Autowiredprivate List<Store<Integer>> s;
1.9.6。应用CustomAutowireConfigurer
CustomAutowireConfigurer 是一个BeanFactoryPostProcessor
您能够注册本人的自定义限定符注解类型,即便它们未应用Spring的@Qualifier注解进行注解。
像之前咱们定义的注解
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface MovieQualifier { String value();}
这种写法次要就是托@Qualifier的福分
但咱们也能够不依赖它
以下示例显示如何应用CustomAutowireConfigurer:
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property></bean>example.CustomQualifier Spring会依据这个类门路加载这个类,并将这个类作为和@Qualifier同作用来看待
主动注入是如何解决候选对象的?
- bean definition 的 autowire-candidate 值,值为false示意该bean不参于候选
- <beans/>元素default-autowire-candidates上可用的任何模式,值为false示意该组的bean不参加候选
- @Qualifier注解 和 任何在customautowiresfigurer注册的自定义注解的存在会被优先应用
当多个bean合乎主动拆卸候选条件时,
确定“primary”的步骤如下:如果候选中恰好有一个bean定义将primary属性设置为true,则将其选中。
1.9.7。@Resource
Spring还通过在字段或bean属性设置器办法上应用JSR-250@Resource批注(javax.annotation.Resource)反对注入。
1. 如果同时指定了name和type,依照bean Name 和 bean 类型同时匹配2. 如果指定了name,就依照bean Name 匹配 3. 如果指定了type,就依照类型匹配4. 如果既没有指定name,又没有指定type,就先依照beanName匹配; 如果没有匹配,再依照类型进行匹配;
@Resource具备name属性。默认状况下,Spring将该值解释为要注入的Bean名称。
换句话说,它遵循名称语义,如以下示例所示:
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}
如果未明确指定名称,则默认名称是从字段名称或setter办法派生的。
如果是字段,则采纳字段名称。
在应用setter办法的状况下,它采纳bean属性名称。
以下示例将名为 movieFinder的bean注入到setter办法:
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}
因而,在下例中,customerPreferenceDao字段首先查找名为“customerPreferenceDao”的bean,找不到的话而后返回到与类型customerPreferenceDao匹配的bean:
public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; public MovieRecommender() { }}
1.9.8。应用@Value
@Value 通常用于注入内部属性:
@Componentpublic class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; }}
应用以下配置:
@Configuration@PropertySource("classpath:application.properties")public class AppConfig { }
和以下application.properties文件:
catalog.name=MovieCatalog
在这种状况下,catalog参数和字段将等于MovieCatalog值。
Spring提供了一个默认的值解析器。
它将尝试解析属性值,如果无奈解析,
${catalog.name} 则将被当做值注入到属性中。
例如:catalog="${catalog.name}"
如果要严格控制不存在的值,则应申明一个PropertySourcesPlaceholderConfigurerbean,如以下示例所示:
@Configurationpublic class AppConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); }}
当配置PropertySourcesPlaceholderConfigurer应用JavaConfig,该@Bean办法必须是static。
如果${} 无奈解析任何占位符,则应用上述配置可确保Spring初始化失败。
默认状况下,Spring Boot配置一个PropertySourcesPlaceholderConfigurer
从application.properties和application.yml获取bean的属性。
Spring提供的内置转换器反对容许主动解决简略的类型转换(例如转换为Integer 或转换为简略的类型int)。
多个逗号分隔的值能够主动转换为String数组,而无需付出额定的致力。
能够像下边一样提供默认值:
@Componentpublic class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) { this.catalog = catalog; }}
Spring BeanPostProcessor在后盾应用ConversionService来解决将@Value中的字符串值转换为指标类型的过程。如果你想为本人的自定义类型提供转换反对,你能够提供本人的ConversionService bean实例,如上面的例子所示:
@Configurationpublic class AppConfig { @Bean public ConversionService conversionService() { DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(); conversionService.addConverter(new MyCustomConverter()); return conversionService; }}
当@Value蕴含SpEL表达式时,该值将在运行时动静计算,如以下示例所示:
@Componentpublic class MovieRecommender { private final String catalog; public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) { this.catalog = catalog; }}
SpEL还反对应用更简单的数据结构:
@Componentpublic class MovieRecommender { private final Map<String, Integer> countOfMoviesPerCatalog; public MovieRecommender( @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) { this.countOfMoviesPerCatalog = countOfMoviesPerCatalog; }}
1.9.9。应用@PostConstruct和@PreDestroy
CommonAnnotationBeanPostProcessor不仅解决@Resource注解
也解决javax.annotation.PostConstruct和 javax.annotation.PreDestroy。
在Spring 2.5中引入了对这些注解的反对,为初始化回调和销毁回调中形容的生命周期回调机制提供了一种代替办法。
如果在Spring ApplicationContext中注册了CommonAnnotationBeanPostProcessor,带有这两个注解的办法将会被回调执行。
在上面的例子中,缓存在初始化时被预填充,在销毁时被革除:
public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... }}
像@Resource一样,@PostConstruct和@PreDestroy注解是JDK6到8的规范Java库的一部分。 然而,整个javax.annotation 程序包都与JDK 9中的外围Java模块离开,并最终在JDK 11中删除了。 如果须要,须要对javax.annotation-api工件进行解决。当初能够通过Maven Central获取,只需像其余任何库一样将其增加到应用程序的类门路中即可。