类路径扫描和组件管理
@Component、@Service、@Controller、@Repository会被扫描注册为组件,DAO层使用注解@Repository,服务层使用@Service,控制层使用@Controller,这三个角色均可以使用@Component注解,但是不够细致化,@Service、@Controller、@Repository注解都是在@Component注解的基础上演变来的
@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
}
为了自动检测注册这些类,需要在@Configuration注解的类中使用@ComponentScan注解,其basePackages属性指明需要扫描的包或类:
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
...
}
在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>
<context:component-scan>包含了<context:annotation-config>,此时不用在注明<context:annotation-config>
可以在@ComponentScan注解的includeFilters或是excludeFilters属性表明需要过滤的选项,这两个属性需要都需要表明type(过滤类型)和expression(类名)的值
custom表示使用自定义的匹配规则,这个类必须实现TypeFilter接口,这个接口的match方法返回true表示匹配成功,否则,表示匹配失败
下面的这个例子表明不注册@Repository注释的类,注册所有.*Stub.*Repository类为组建:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
使用xml配置:
<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>
如果不想要自动检测注册@Component、@Repository、@Service、@Controller,可以将@Component注解的useDefalutFilters置为false,或是将<component-scan/>的use-defalut-filters的值置为fase
spring的组件依然可以使用@Bean注解:
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
除了@Bean,也可以使用@Qualifier、@Scope、@Lazy等注解
@Bean注解在@Component注解的类中使用和在@Configuration中注解的类中解析的方式不一样,@Component不会使用CGLIB代理,容器为了统一管理Bean的生命周期,需要拦截获取bean的请求,因此,在@Configuration会使用CGLIB,由于CGLIB不能代理静态方法,因此调用静态工厂方法将不被容器拦截,在@Configuration中的@Bean方法必须是可重写的,因此不能是private或是final修饰,一个类可以有多个@Bean方法用于产生同一个类的实例,这个时候选择具有最多可满足依赖项的方法进行调用
命名组件
当组件被检测到后,将会注册为bean,bean的名字通过BeanNameGenerator组织,@Component、@Service、@Controller、@Repository的name属性可以设置组件的名字,如果未指定,默认为类名,组件的名字必须是独一无二的
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
上述例子注册的bean名为myMovieLister
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
上述例子注册的bean名为MovieFinderImpl
规定组件的生命周期
组件默认的生命周期是singleton,可以通过@Scope注解更改:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
如果想使用自定义的生命周期组件,继承并实现ScopeMetedataResolver接口,确保默认构造函数的存在,在@ComponentScan指明类的全限定名
@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>
由于组件与依赖的生命周期可能不一样,因此在依赖注入时可能会有问题(之前讲过),此时可以在@ComponentScan注解的scopedProxy属性指明代理的方式:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
...
}
xml的配置方式:
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
该属性有三种值:no,interface(使用JDK代理),targetClass(使用CGLIB代理)