文档章节

spring 基于java的配置

流光韶逝
 流光韶逝
发布于 2016/09/26 12:42
字数 3510
阅读 20
收藏 0

7.10 Classpath scanning and managed componets

文档地址: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-classpath-scanning 本章中大部分例子是用xml来指定配置元数据,以生成容器里的每个bean定义.上个部分描述了如何通过资源注解来提供大量的配置元数据.但是,这很多例子中,很多bean定义是在xml文件中完成的,而注解只负责依赖注入.本节描述如何通过扫描路径来检测可选组件.候选组件都是一些能匹配一些条件的类或者在容器中注册了相应的bean定义.这移除了使用xml注册bean的必要,因此你可用使用注解,AspectJ类型表达,又或者你的自定义拦截条件 来选择容器中注册的bean定义.

从spring3.0,spring的java配置项目提供了的很多功能已经是spring核心功能的一部分.这允许你使用java而不是xml文件来定义beans.学习@Configuration,@Bean,@Import,@Dependson注解,并学习如何使用.

7.10.1 @Component and further sterertype annotations

@Repository注解是一个任何能完成repository(也就是DAO)角色或模板的类的标志.使用这些标志可以自动转译异常,如Section 20.2.2,"Exception translation";

spring提供了很多固定注解:@Component,@Service,@Controller,@Repository.@Component是一个spring管理组件的基本模板类型.@Repository,@Service,@Controller是对@Componet的特殊化,应用于特定的场合,例如,他们分别在持久化,服务层,表现层中使用.因此,你可以用@Component来标志你的组件,但你最好用@Repository,@Service,@Controller来替换,因为这样可以使这些类更好的而配合工作操作或者和切面协同.例如,这些模板注解都是切入点的理想目标.spring框架在以后的发布版中@Repository,@Service,@Controller可能会携带额外的语法.因此,当你考虑用@Service或@Component来标注你的服务层是,@Service是更好的选择.同上文所示,@Repository已被当为了一个支持持久层自动异常转译的标志.

7.10.2 Meta-annotations (元注解)

很多spring提供的注解可以作为元注解在你的代码里使用.元注解是指可以在其他注解中使用的注解.例如,上文中提到的@Service注解中就有@Component元注解.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {

    / / ....
}

元注解可以被聚合生成组合注解.例如,spring mvc的@RestController注解就是@Controller和@ResponseBody组合而成的.

另外,组合注解可以重新申明元注解的属性来允许用户自定义.当你只打算暴露一部分元注解属性时这将非常有用.例如,spring的@SessionScope以硬编码的方式控制scope的名字为session,但扔允许自定义proxyMode的值.


@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() default ScopedProxyMode.TARGET_CLASS;

}

@SessionScope不申明proxyMode属性仍可以如下使用


@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}

更多细节,请查看spring annotaion programming Model

7.10.3 Automatically detectin classes and registering bean definitions(自动检测类并注册bean定义)

spring 可以自动检测模板类并通过ApplicationContext来注册相应的bean定义.例如,一下两个类将会代理以实现自动检测.


@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}


要自动检测这些类并注册相应的bean,你需要在你的@Configuration类中添加@ComponentScan注解,@ComponentScan中的basePackages属性应该是两个类的共同父包.(另外,你可以制定一个逗号,分号,空白分割的列表来包含每个类的父包);

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

为了简化,上面的可以直接使用注解你的value属性,如 @ComponentScan("org.example")

以下是相应的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: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>

还有,当你使用component-scan元素时,AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor都是隐式包含的.这意味着这两个组件会自动检测被一起注册,不需要在xml里提供任何bean配置元数据.

另,你也可以不注册AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor,这需要你在annotation-config元素中将值设为false;

7.10.4 使用过滤器来自定义扫描

一般而言,用@Component,@Repository,@Service,,@Controller,或其他本身标记了@Component注解的注解才能被检测为可选组件.但是,你可以简单的通过使用自定义过滤器来修改并扩展这个行为.将他们作为includeFilters或excludeFilters的参数添加到@ComponentScan注解里.下面的表将描述拦截选项.

拦截器类型

拦截器类型

annotaion,assignable,aspectj,regex,custom五种. 下面的例子表明所有的@Repository注解都会被排除,并使用以stub结尾的repository替代.

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

等同于

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

@Filter,属性时type,pattern,默认是annotation;你也可以通过设置@ComponentScan的useDefaultFileters=false或设置<component-scan>元素中的use-default-filters="false"来禁用默认过滤器.而受影响的类是标注了@Componnet,@Repository,@Service,@Controller,或者@Configuration的类.

7.10.5 Defining bean medata within components(在components中定义bean的元数据)

spring 的components也可以向容器提供bean定义.你可以像在@Configuration注解类中一样使用@Bean注解来定义bean的元数据.下面是一个简单的例子:

@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }

}

这个类是一个spring组件类,在它的doWork()方法里有应用特定的代码.然,它也提供了一个bean定义,用一个工厂方法来指向方法publicInstance().@Bean注解标记着工厂方法或其他bean定义属性.例如通过@Qualifier提供的一个qualifier值.其他方法级别的注解可以被@Scope,@Lazy或其他自定义注解等来指定.

  • 可以使用@Lazy同@Autowired,@Inject标志懒加载.在本上下文中,它将注入懒加载代理.

自动装配的字段和方法支持上文已经讨论了,另外讨论的是对@Bean方法自动装配的支持:

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters

    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }

}

上面的例子会自动装配字符串类型的方法参数country的值为一个名为privateInstance的bean的Age属性.一个spring表达式语言元素通过#{<expression>}的形式定义了表达式的值.对于@Value注解,一个表达式解析器会会再解析表达文本之前前查看bean的名称. 在spring的Component里的@Bean方法不同于@Configuration类里的@Bean方法的处理.这个不同在于@Component的类没有通过CGLIB来拦截方法或属性的调用.CGLIB代理是通过在@Configueration类中定义了指向其他协作对象的bean的元数据@Bean方法来调用相应的方法或字段;这些方法并没有通过正常的java语义而是通过容器调用的,为的就是当通过调用@Bean方法来引用其他beans时可以提供正常的生命周期管理和spring的代理.相反的,在一个简单的@Component类里调用@Bean方法的字段或方法会有正常的java语法,而不是一个特殊的CGLIB处理或其他限制条件.

如果你吧一个@bean的方法宣布为static,这可以允许你在bean未实例化之后调用它们.当你定义了后处理器类时,这会非常有意义.因为这些类会再容器生命周期中很早启动,这样会避免在此时触发容器配置的其他部分.

调用@bean的静态方法不会被容器拦截,即使当你在@Configuration类里调用.这主要是技术的限制:CGLIB子类只能重写非静态的方法.结果,直接调用其他的@Bean方法拥有java标准语法,所以工厂方法自身会返回一个独立的实例.

java语言中@Bean方法的存在并不会对spring容器里的bean定义造成直接的影响.你可以自由的在你认为合适的非@Configuration类里申明工厂方法,也可以将之设置为静态方法.但是,在@Configuration类里的常规@Bean方法可能需要重写,所以 他们不能命名为private或final.

@Bean方法可以在一个指定的组件类或配置类里发现,和java 8 发现组件类和配置类中实现接口申明的默认方法一样.这就允许在组装复杂的配置组合时有很大的灵活性.即使通过java8的默认方法使混合继承成为可能.

最后,记住一个单个的class可能对于同一个bean持有多个混合的@Bean方法,混合工厂方法的安排取决于运行时那些依赖是可获得.这个算法同其他配置场景里选择最贪婪的工厂方法和构造器一样:可适依赖数量最多的种类在构造期间获取,同容器如何选择混合@Autowired构造器一样.

7.10.6 命名自动检测组件 Naming autodected components

当一个组件作为扫描过程的一部分进行自动检测时,它的bean的名称是通过BeanNameGenerator策略来告知扫描器的.默认的,spring任何固定类型注解(@Componnet,@Repository,@Service,@Controller)包含一个那么值,并将它提供给对于的bean定义.

如果一个注解没有包含value值或其他可检测的组件(可以被自定义拦截器拦截的bean).默认的bean名称生成器返回一个小写字母开头的非限制的类名称.例如,如果以下两个组件被检测,那么它们的名称应该是myMovieListener或movieFinderImpl;

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}

@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

如果你不想依赖默认的bean命名策略,你可以提供一个自定义命名策略.首先,实现BeanNameGenerator接口,并确保它有一个无参构造器.另外,在配置扫描器时提供一个全匹配符的类名称.

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    ...
}

或者

<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>

首先,思考指定注解的值,因为其他组件可能会引用它.另一方面,当容器进行注入时,名称自动生成策略总是差强人意的.

7.10.7 Providing a scope for autodetected components(为自动检测组件提供作用域)

一般而言,spring管理的组件默认和通用的自动检测组件的作用域一般是单例.但是,有时你需要使用@Scope来定义其他的作用域.使用注解提供作用域的名称来实现.

@Scope("prototype")
@Repostitory
public class MovieFinderImpl implements MovieFinder{
}

web特定的作用域细节,可查看7.4.5章,Request,session,global session,application,and WebSocket scopes

如果你想使用自定义作用域策略而不是使用基于注解方法,你需要实现ScopeMetadataResolver接口,并保证该实现类有一个无参构造器.另外,在配置扫描器时提供一个类的全路径名称.

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}

xml配置方法

<beans>
    <context:component-scan base-package="org.example"
            scope-resolver="org.example.MyScopeResolver" />
</beans>

当你使用特定非单例作用域,它可能需要为作用域里的对象提供代理. 这个原因在"scoped beans as dependencies"一节里已经描述.为实现这个目标,component-scan元素提供了一个scoped-proxy元素,它有三个值:no,interfacce,targerClass.例如,下面的配置将会是标准的JDK动态代理:

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}

<beans>
    <context:component-scan base-package="org.example"
        scoped-proxy="interfaces" />
</beans>

7.10.8 通过注解提供匹配符元数据

我们在7.9.4这节里考虑过,'Fine-tuning annotation-based autowiring with qualifier'. 本节的例子说明在你要解决自动注入条件问题时,你可以使用@Qualifier注解或自定义注解来获得更好的控制.因为这些例子都是基于xml的bean定义,这个通配符元数据通过是通过候选的bean定义使用xml里bean元素的qualifier或meta子元素来定义.当你使用类路径扫描或组件自动检测,你需要提供在候选类里通过类级别的注解来提供匹配符元数据.以下三个例子说明了这个技术:


@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}

作为大部分基于xml的可替换项,要记住注解元数据是基于类定义自身的,而使用xml可以为同一类型的不同bean定义提供它们相应的配置元数据.因为注解是类级别的,而xml里的bean则是实例级别的.

© 著作权归作者所有

共有 人打赏支持
流光韶逝
粉丝 20
博文 103
码字总数 127621
作品 0
济南
程序员
私信 提问
为什么Java大神,都在看Spring Boot和Spring Cloud的书?

如果你是一名Java开发人员,并且最近正打算学习Spring Boot和Spring Cloud框架并寻找一些关于它们的最好的书籍,那么,你今天就来对地方了。 本文,我们将讨论一些学习Spring Boot和Spring ...

Java小铺
2018/09/18
0
0
Spring Boot实战之基础回顾

本文作者: 吴伟祥 本文链接: https://wuweixiang.cn/2018/08/21/Spring-Boot实战之基础回顾/ 版权声明: 本博客所有文章除特别声明外均为原创,采用CC BY-NC-SA 4.0 许可协议。转载请在文章开...

吴伟祥
2018/08/21
0
0
【Spring reference】Spring基于java的配置

1、简述 spring reference写到: Java-based configuration: Starting with Spring 3.0, many features provided by the Spring JavaConfig project became part of the core Spring Framewo......

666B
2014/07/01
0
0
Spring MVC 使用 JNDI 配置的DataSource

稍微看了下,Spring 中JNDI 的使用,弄了个小例子。有很多不完备的地方,以后慢慢看,再改吧。 <一> 技术使用 Spring MVC JDBC Template Maven JNDI <二> 一些配置 Maven POM 配置 spring-c...

平江夜弹
2015/06/29
0
0
Spring IoC容器(一)

一、Spring IoC 容器和 Bean 简介 IoC也称为依赖注入(dependency injection, DI)。这是一个过程,在这个过程中,对象仅通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后在...

皮皮猫32
04/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

租房软件隐私保护如同虚设

近日,苏州市民赵先生向江苏新闻广播新闻热线025-84658888反映,他在“安居客”手机应用软件上浏览二手房信息,并且使用该软件自动生成的虚拟号码向当地一家中介公司进行咨询。可电话刚挂不久...

linux-tao
今天
1
0
分布式项目(五)iot-pgsql

书接上回,在Mapping server中,我们已经把数据都整理好了,现在利用postgresql存储历史数据。 iot-pgsql 构建iot-pgsql模块,这里我们写数据库为了性能考虑不在使用mybatis,换成spring jd...

lelinked
今天
4
0
一文分析java基础面试题中易出错考点

前言 这篇文章主要针对的是笔试题中出现的通过查看代码执行结果选择正确答案题材。 正式进入题目内容: 1、(单选题)下面代码的输出结果是什么? public class Base { private Strin...

一看就喷亏的小猿
今天
2
0
cocoapods 用法

cocoapods install pod install 更新本地已经install的仓库 更新所有的仓库 pod update --verbose --no-repo-update 更新制定的仓库 pod update ** --verbose --no-repo-update...

HOrange
今天
3
0
linux下socket编程实现一个服务器连接多个客户端

使用socekt通信一般步骤 1)服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。 2)客户端:socker()建立套接字,连接(connect)服务器,连接上后...

shzwork
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部