@Component @Repository @Service @Controller
本质上以上四类没有区别,但是在你定义应用分层的时候 aop的时候 这些不同语义的注解 就有不同意义,未来Spring是否会给予这些语义注解特殊的意义还并不清楚
在配置java类里面注解@ComponentScan(basePackages = "com.domain")的情况下,会自动扫描com.domain包里的所有类,扫描里面带有@Componet,@Repository,@Service..等等注解的类
该句等同于在xml配置文件里面加入了
<context:component-scan base-package="com.javaconfig.domain"/>
同时@ComponentScan可以在加载包的时候,排除掉一些类,或者包含某些类
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
表示:扫描org.example包下所有带有@Component衍生的各种语义注解,同时包含该包下的匹配 .*Stub.*Repository类型的(正则匹配),排除扫描Repository.class
base-package的配置,从表面意思来看这是个“基本包”,表示下面的过滤路径是基于这个包的
更多过滤方式参考spring-reference
@Component
扫描一个类注册一个bean
@Component
public class Football {
@Override
public String toString() {
// TODO Auto-generated method stub
return "football";
}
}
这就注册了一个名为football的bean 命名规则为首字母小写,如果起始的两个字母都是大写,则不小写首字母。
@Component
public class XYclass {
}
注册了一个XYclass的bean
XYclass xyz = ctx.getBean("XYclass",XYclass.class);
获得该bean
@Bean
以类似工厂的方式产生bean,注意@Bean所在类必须要被spring所扫描到
Tool1:
public class Hammer implements Tool {
public void doWork() {
System.out.println("using hammer to smash something");
}
}
Tool2:
public class Scissors implements Tool{
public void doWork() {
System.out.println("using scissors to cut something");
}
}
ToolBox:
public class ToolBox {
private Tool[] tools;
public ToolBox(Tool t1, Tool t2) {
tools = new Tool[] { t1, t2 };
}
public Tool[] getTools() {
return tools;
}
public void setTools(Tool[] tools) {
this.tools = tools;
}
}
BeanFactory:
@Component
public class ToolFactory {
@Bean
@Qualifier("t1")
public Tool hammer() {
return new Hammer();
}
@Bean
@Qualifier("t2")
public Tool scissors() {
return new Scissors();
}
@Bean
public ToolBox toolBox(@Qualifier("t1") Tool t1, @Qualifier("t2") Tool t2,
@Value("this is just tool box") String description) {
System.out.println(description);
return new ToolBox(t1, t2);
}
}
Unit Test:
@Test
public void beanTest() {
Tool t1 = ctx.getBean("hammer",Tool.class);
Tool t2 = ctx.getBean("scissors",Tool.class);
t1.doWork();
t2.doWork();
ToolBox tb = ctx.getBean("toolBox",ToolBox.class);
}
@Bean产生的bean命名方式 就是方法名字 同时@Bean也会按照类型注入参数
@Configuration 和 @Component 的区别
@Configuration也是从@Component 衍生出来的注解,但是@Component 相比@Configuration不同点在于,@Component 并没有通过CGLIB 增强调用所注册bean的内部方法和fields调用的。还有@Configuration内的@bean注解的方法 不能为final,因为这需要被CGLIB所重写。
@Component @Repository @Service @Controller等注解 也提供了 生成bean非默认名称方式命名
@Service("myMovieLister")
public class SimpleMovieLister { // ...
}
比如该bean名称就会是myMovielister
@Scope
提供了在注册bean的时候申明该bean的lifecycle
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Configuration
是类级注册bean的注解,该类自身被注册为bean,并且其内的@Bean注解的方法 都会注册为bean。并且顾名思义,这就是一个配置类。
@Configuration
public class AppConfig {
@Bean
public Foo foo(Bar bar){
return new Foo(bar);
}
@Bean
public Bar bar(){
return new Bar();
}
}
这里申明的都是单例实类
Unit Test:
@Test
public void beanTest2() {
Foo foo = ctx.getBean("foo", Foo.class);
Foo foo2 = ctx.getBean("foo", Foo.class);
Bar bar = ctx.getBean("bar", Bar.class);
Bar bar2 = ctx.getBean("bar", Bar.class);
assertEquals(foo, foo2);
assertEquals(bar, bar2);
assertEquals(foo.getBar(), foo2.getBar());
}
测试通过
@Import
导入配置类
@Configuration @Import(ConfigA.class) public class ConfigB {
@Bean
public B b() { return new B();
} }
@ImportResource
导入配置文件
@ImportResource(value = "TestCase_01.xml")
@Condtional
@Conditional注解主要用在以下位置:
类级别可以放在注标识有@Component(包含@Configuration)的类上
作为一个meta-annotation,组成自定义注解
方法级别可以放在标识由@Bean的方法上
如果一个@Configuration的类标记了@Conditional,所有标识了@Bean的方法和@Import注解导入的相关类将遵从这些条件。
简单的说就是根据某些情况选择合适的类进行注册
首先需要编写条件,条件类必须实现Condtion接口的matches方法
判断是否为Mac os
public class MacOsCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Mac OS X");
}
}
判断是否为Linux
public class LinuxCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
注册bean的时候或者注册整个类的时候用上
@Bean(name = "systemService")
@Conditional(LinuxCondition.class)
public SystemService linuxService() {
return new LinuxService();
}
@Bean(name = "systemService")
@Conditional(MacOsCondition.class)
public SystemService macService() {
return new MacOsService();
}
根据操作系统不同选择不同的服务
Unit Test:
@Test
public void conditionTest() {
SystemService service = ctx.getBean("systemService",SystemService.class);
service.service();
}
如果在开发时进行一些数据库测试,希望链接到一个测试的数据库,以避免对开发数据库的影响。
该注解提供了灵活的在多个副本之间切换的方法
注册了一个用于开发测试的mockDBService和一个release的MysqlDBService
@Bean(name = "dbService")
@Profile("dev")
public DBService mockDBService(){
return new MockDBService();
}
@Bean(name = "dbService")
@Profile("rc")
public DBService MysqlDBService(){
return new MysqlDBService();
}
在进行单元测试的时候
使用@ActiveProfiles(value = "dev")激活所要使用的副本
Unit Test:
@Test
public void profileTest() {
DBService dbService = ctx.getBean(DBService.class);
System.out.println(dbService.count("select * from users"));
}
也可以使用java激活方式
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(AppConfig.class);
ctx.refresh();