360SDN.COM

Spring Framework 5 权威指南(16)

来源:程序员阮威  2017-09-11 13:22:55    评论:0点击:

这是一个Spring 5专题系列,其诞生的动机是:

1.介绍最新版本的Spring Framework;

2.介绍最系统化的Spring Framework;

3.介绍最佳实践的Spring Framework;

Spring支持JSR-250@Resource注解,应用于域或设置方法上,用于依赖注入(@Autowired注解可以应用于构造方法、域、设置方法和任意配置方法)。默认情况下,Spring将其name属性解释为bean的名字。

public class SimpleMovieLister{

 

        privateMovieFinder movieFinder;

 

        @Resource(name="myMovieFinder")

        publicvoid setMovieFinder(MovieFinder movieFinder) {

                this.movieFinder = movieFinder;

        }

}

如果没有显式地指定name属性,则默认name由域名或者设置方法名推断出来,当推断名称匹配也失败时,就回退到按照类型进行匹配。

@Resource注解和@Autowired注解类似,也可以自动注入一些众所周知的对象,包括BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource等。例如:

public class MovieRecommender {

 

        @Resource

        privateCustomerPreferenceDao customerPreferenceDao;

 

        @Resource

        privateApplicationContext context;

 

        publicMovieRecommender() {

        }

 

        // ...

}

Spring (3.0+)支持JSR-330@Inject@Named注解。需要添加如下依赖:

<dependency>

        <groupId>javax.inject</groupId>

        <artifactId>javax.inject</artifactId>

        <version>1</version>

</dependency>

@Inject@Named的组合,类似于@Autowired@Qualifier的组合。例如:

import javax.inject.Inject;

import javax.inject.Named;

 

public class SimpleMovieLister{

 

        privateMovieFinder movieFinder;

        private Provider<MovieFinder>movieFinder2;

 

        @Inject

        publicvoid setMovieFinder(@Named("main") MovieFinder movieFinder) {

                this.movieFinder = movieFinder;

        }

 

       @Inject

        publicvoid setMovieFinder2(Provider<MovieFinder> movieFinder2) {

                this.movieFinder2 = movieFinder2;

        }

 

        // ...

}

注意,基于注解的注入先于基于XML的注入,因此后者会覆盖前者。

9.2 基于注解的Bean定义

上面的注解主要表达了bean之间的依赖注入关系,但是bean定义依然是显式使用XML文件进行配置。接下来介绍隐式的扫描类路径来注册组件。

Spring提供了一些构造型注解:@Component,@Service, @Repository@Controller@Component注解是Spring管理的任意组件的通用构造型注解,@Service, @Repository@Controller注解则是特定场景下的专门的@Component注解。例如,它们分别适用于持久层,服务层和展现层。这些专门的注解方便工具进行处理,方便作为AOP的目标进行切入,也可能在将来的Spring版本中赋予新的语义,例如,@Repository注解在持久层支持作为自动异常转换的标记。

Spring可以自动检测被标记为构造型注解的类并注册其相应的bean定义。例如:

@Service

public class SimpleMovieLister{

 

        privateMovieFinder movieFinder;

 

        @Autowired

        publicSimpleMovieLister(MovieFinder movieFinder) {

                this.movieFinder = movieFinder;

        }

}

@Repository

public class JpaMovieFinder implementsMovieFinder {

        // implementation elided for clarity

}

要自动检测并注册上面两个类,你需要添加如下配置:

<?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

                http://www.springframework.org/schema/beans/spring-beans.xsd

                http://www.springframework.org/schema/context

                http://www.springframework.org/schema/context/spring-context.xsd">

 

        <context:component-scan base-package="org.example"/>

 

</beans>

basePackage属性必须是上面两个类的公共父包名,或者使用逗号,分号,空格分隔的各个类的父包名的列表。<context:component-scan>隐式的启用了<context:annotation-config>,即注册了相应的BeanPostProcessor来处理相关注解(value属性指定为false可以防止注册上述BeanPostProcessor)

等价的基于Java的配置元数据如下:

@Configuration

@ComponentScan(basePackages = "org.example")

public class AppConfig  {

           ...

}

默认情况下,只有被标记了@Component,@Service, @Repository, @Controller以及被@Component标记的自定义的注解(例如@RestController注解)的类才能被自动检测到(即默认过滤器),你可以使用过滤器来改变和扩展这一行为:

<beans>

        <context:component-scan base-package="org.example">

                <context:include-filter type="regex"

                                expression=".*Stub.*Repository"/>

                <context:exclude-filter type="annotation"

                                expression="org.springframework.stereotype.Repository"/>

        </context:component-scan>

</beans>

上面的例子表示忽略所有被@Repository注解标记的类,并且使用stub仓库。

等价的基于Java的配置元数据如下:

@Configuration

@ComponentScan(basePackages = "org.example",

                  includeFilters = @Filter(type =FilterType.REGEX, pattern = ".*Stub.*Repository"),

                  excludeFilters = @Filter(Repository.class))

   public classAppConfig {

           ...

   }

每个过滤器元素都需要有一个typeexpression属性:
                           

设置<component-scan use-default-filters="false"/>,或者@ComponentScan(useDefaultFilters=false),将停用默认过滤器。

默认情况下,Spring提供的构造型注解(@Component,@Service, @Repository@Controller)提供了一个value属性用于指定bean名称。如果没有指定value属性,或者其他非构造型注解,当组件被扫描器自动检测到时,BeanNameGenerator会为其生成一个bean名称,以小写开头的类名作为bean名称。

例如,下面两个组件的名称分别为myMovieListermovieFinderImpl

@Service("myMovieLister")

public class SimpleMovieLister{

        // ...

}

@Repository

public class MovieFinderImpl implementsMovieFinder {

        // ...

}

你可以指定一个自定义的bean命名策略,实现BeanNameGenerator接口并增加如下配置即可:

<beans>

        <context:component-scan base-package="org.example"

                name-generator="org.example.MyNameGenerator"/>

</beans>

等价的基于Java的配置元数据如下:

@Configuration

@ComponentScan(basePackages = "org.example",nameGenerator = MyNameGenerator.class)

public class AppConfig {

           ...

}

可以使用@Scope注解为组件指定作用域。例如:

@Scope("prototype")

@Repository

public class MovieFinderImpl implementsMovieFinder {

        // ...

}

你可以指定一个自定义的作用域解析策略,实现ScopeMetadataResolver接口并增加如下配置即可:

<beans>

        <context:component-scan base-package="org.example"

                       scope-resolver="org.example.MyScopeResolver"/>

</beans>

等价的基于Java的配置元数据如下:

@Configuration

@ComponentScan(basePackages = "org.example",scopeResolver = MyScopeResolver.class)

public class AppConfig {

           ...

}

如果使用非singleton作用域的bean时需要使用代理(通常是由于不同作用域对象间的依赖),可以添加如下配置:

<beans>

        <context:component-scan base-package="org.example"

                scoped-proxy="interfaces"/>

</beans>

等价的基于Java的配置元数据如下:

@Configuration

@ComponentScan(basePackages = "org.example",scopedProxy = ScopedProxyMode.INTERFACES)

public class AppConfig {

           ...

}

可以使用@Qualifier注解为组件指定限定符。例如:

@Component

@Qualifier("main")

public class SimpleMovieCatalogimplements MovieCatalog {

        // ...

}

@Component

@Qualifier("action")

public class ActionMovieCatalogimplements MovieCatalog {

        // ...

}

@Component

@Offline

public class CachingMovieCatalogimplements MovieCatalog {

        // ...

}

JSR-330提供的@Named@Inject的组合基本等价于Spring提供的@Component@Autowired的组合,也基本等价于JSR-250提供的@ManagedBean@Resource的组合。

import javax.inject.Inject;

import javax.inject.Named;

 

@Named("movieListener")// @ManagedBean("movieListener") couldbe used as well

public class SimpleMovieLister{

 

        privateMovieFinder movieFinder;

 

        @Inject

        publicvoid setMovieFinder(MovieFinder movieFinder) {

                this.movieFinder = movieFinder;

        }

 

       @Inject

        publicvoid setMovieFinder(@Named("main") MovieFinder movieFinder) {

                this.movieFinder = movieFinder;

        }

 

       @Inject

        publicvoid setMovieFinder(Provider<MovieFinder> movieFinder) {

                this.movieFinder = movieFinder;

        }

 

        // ...

}

注意,@Named@ManagedBean不支持组合,当需要自定义组件注解时,请使用Spring提供的构造型注解。JSR-330注解的一些局限如下

为了节省扫描类路径的时间,可以在编译时生成索引,当ApplicationContext检测到索引时,就直接使用索引而不用进行类路径扫描。要生成索引,增加以下依赖即可:

<dependencies>

        <dependency>

                <groupId>org.springframework</groupId>

                <artifactId>spring-context-indexer</artifactId>

                <version>5.0.0.RC2</version>

                <optional>true</optional>

        </dependency>

</dependencies>

使用Gradle则添加如下配置:

dependencies {

       compileOnly("org.springframework:spring-context-indexer:5.0.0.RC2")

}

生成的索引文件位于jar包中的META-INF/spring.components文件。

Spring支持JSR-250提供的@PostConstruct@PreDestroy注解。

@Component

public class ExampleBean {

 

        @PostConstruct

        publicvoid init() {

                // do some initialization work

        }

 

        @PreDestroy

        publicvoid destory() {

                // do some destruction work (like releasing pooledconnections)

        }

 

}

配置元数据如下:

<?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

                http://www.springframework.org/schema/beans/spring-beans.xsd

                http://www.springframework.org/schema/context

                http://www.springframework.org/schema/context/spring-context.xsd">

 

        <context:component-scan base-package="org.example"/>

 

</beans>

@PostConstruct@PreDestroy这两个注解是由CommonAnnotationBeanPostProcessor进行处理的,此处进行了隐式的注册。

元注解就是可以应用于其他注解的注解,例如@Component注解就是@Service注解的元注解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component // Spring will see thisand treat @Service in the same way as @Component

public @interface Service {

 

        // ....

}

多个元注解可以组成组合注解,例如SpringMVC中的@RestController注解就是由@Controller注解和@ResponseBody注解组合而成的。组合注解可以重新声明元注解的属性,例如:

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Scope(WebApplicationContext.SCOPE_SESSION)

public @interface SessionScope {

 

        /**

        * Alias for {@link Scope#proxyMode}.

        * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.

        */

        @AliasFor(annotation = Scope.class)

       ScopedProxyMode proxyMode() defaultScopedProxyMode.TARGET_CLASS;

 

}

其用法如下:

@Service

@SessionScope

public class SessionScopedService{

        // ...

}

或者指定proxyMode来覆盖默认值:

@Service

@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)

public class SessionScopedUserServiceimplements UserService {

        // ...

}

阅读原文

为您推荐

友情链接 |九搜汽车网 |手机ok生活信息网|ok生活信息网|ok微生活
 Powered by www.360SDN.COM   京ICP备11022651号-4 © 2012-2016 版权