前言
Github:https://github.com/yihonglei/thinking-in-spring(spring-ioc-annotaion工程)
一 Spring配置可选方案
Spring提供了三种主要的装配机制:
1)在XML中进行显示配置。
2)在Java中进行显示配置。
3)隐式的bean发现机制和自动装配。
这三种方式怎么选择,其实没有严格的要求,他们是可以搭配使用的,大部分取决于个人喜好和项目实际情况。
但是建议使用自动配置机制会让你在维护上面省心,代码简洁。显示配置越少越好,如果代码非得需要显示配置时,
比如要用某些第三方组件时,我们可以使用javaConfig配置,因为比XML功能更强大。最后,如果想要使用XML命名空间时,
并且在javaConfig中没有同样的实现时,才应该使用XML。这里主要看下自动化装配。
二 自动化装配Bean
Spring从两个角度来完成自动化装配:
1)组件扫描(component scanning):spring会自动发现应用上下文中所创建的Bean。
2)自动装配(autowiring):spring自动满足bean之间的依赖关系。
通过组件扫描和自动装配组合在一起能发挥出无限的威力,能够把显示配置降低到最少,谁用谁知道,谁用谁说好!
1 创建可被发现的Bean
创建一个接口
package com.jpeony.spring.hello;
/**
* @author yihonglei
*/
public interface HelloService {
void sayHello(String name);
}
创建一个实现类
package com.jpeony.spring.hello;
import org.springframework.stereotype.Component;
/**
* 注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。
*
* @author yihonglei
*/
@Component
public class HelloServiceImpl implements HelloService{
@Override
public void sayHello(String name) {
System.out.println("Hello:" + name);
}
}
注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。
启用组件扫描
javaConfig配置类ApplicationConfig:
package com.jpeony.spring.hello;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 1. 注解@ComponentScan开启组件扫描,spring默认是关闭的;
* 注解@ComponentScan扫描与Application类同级包以及子包下包含@Component的类,
* 并在spring上下文中创建为一个bean;
*
* 2. 如果不想使用@ComponentScan默认扫描包,可以通过basePackages显示指定,
* 比如: @ComponentScan(basePackages = {"com.jpeony.spring"})
* 同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。
* 注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,
* 比如: @ComponentScan(basePackageClasses = {HelloServiceImpl.class})
* 含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。
*
* @author yihonglei
*/
@Configuration
@ComponentScan
public class ApplicationConfig {
}
Spring组件扫描默认是不启用的,通过注解@ComponentScan启用组件扫描。如果没有其他配置的话,
@ComponentScan默认扫描与配置类相同的包。Spring会扫描与ApplicationConfig类所在包以及这个包下的所有子包,
查找带有@Component的注解类,并且在spring中自动创建为一个bean。
测试一下组件扫描能否发现HelloServiceImpl
package com.jpeony.spring;
import com.jpeony.spring.hello.ApplicationConfig;
import com.jpeony.spring.hello.HelloService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 1. SpringJUnit4ClassRunner测试启动时,自动创建spring应用上下文;
* 2. 注解@ContextConfiguration会告诉它需要在ApplicationConfig加载配置;
* 3. 而在ApplicationConfig类中包含@Configuration,@ComponentScan,
* 注解@ComponentScan开启组件扫描,spring默认是关闭的;
* 注解@ComponentScan默认扫描与ApplicationConfig类同级包以
* 及子包下包含@Component的类,spring上下文创建为一个bean;
* 4. 如果不想使用@ComponentScan默认扫描包,可以通过basePackages显示指定,
* 比如: @ComponentScan(basePackages = {"com.jpeony.spring"})
* 同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。
* 注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,
* 比如: @ComponentScan(basePackageClasses = {HelloServiceImpl.class})
* 含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。
* 5. 默认创建bean的ID为注解@Component对应类首字母小写,比如:
*
* @Component public class HelloServiceImpl implements HelloService{
* ......
* }
* 创建的bean的ID为helloServiceImpl,如果我们想要替换,可以显示指定,比如:
* @Component("helloService") public class HelloServiceImpl implements HelloService{
* ......
* }
* 创建出来的bean的ID就为helloService。
* 6. spring除了@Component指定创建bean外,也可以用@Named替代,大部分情况下两者
* 是可以互换的,个人偏好使用@Component。
*
* @author yihonglei
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class HelloTest {
@Autowired
private HelloService helloService;
@Test
public void testHello() {
helloService.sayHello("Tom");
}
}
2 为组件扫描的bean重命名
Spring应用上下文中每一个bean都会有一个ID,默认创建bean的ID为注解@Component对应类首字母小写,比如:
@Component
public class HelloServiceImpl implements HelloService{
......
}
创建的bean的ID为helloServiceImpl,如果我们想要替换,可以显示指定,比如:
@Component("helloService")
public class HelloServiceImpl implements HelloService{
......
}
创建出来的bean的ID就为helloService。Spring除了@Component指定创建bean外,还有另外一种bean的命名方式,
也可以用@Named替代,大部分情况下两者是可以互换的,个人偏好使用@Component。
3 设置组件扫描的基础包
注解@ComponentScan默认扫描与ApplicationConfig类同级包以及子包下包含@Component的类,
Spring上下文创建为一个bean;但是有些时候,我们想要扫描不同的包,我们可以配置@ComponentScan实现。
可以通过basePackages显示指定,比如:
@ComponentScan(basePackages = {"com.jpeony.spring"})
同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。
注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,比如:
@ComponentScan(basePackageClasses = {HelloServiceImpl.class})
含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。
4 通过为bean添加注解实现自动装配
自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,
会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,
我们可以使用@Autowired注解,完成自动化装配,该注解可以用于构造器,以及setter方法上。
创建一个HappyNewYear类
package com.jpeony.spring.hello;
import org.springframework.stereotype.Component;
@Component
public class HappyNewYear {
public void sayHappyNewYear() {
System.out.println("Happy New Year!");
}
}
修改HelloServiceImpl类,引入HappyNewYear,并调用HappyNewYear方法:
HelloService再加个void testAutowired();方法:
package com.jpeony.spring.hello;
/**
* @author yihonglei
*/
public interface HelloService {
void sayHello(String name);
void testAutowired();
}
package com.jpeony.spring.hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。
*
* @author yihonglei
*/
@Component
public class HelloServiceImpl implements HelloService {
/**
* 测试自动装配
*/
@Autowired
private HappyNewYear happyNewYear;
/*@Autowired
public HelloServiceImpl(HappyNewYear happyNewYear) {
this.happyNewYear = happyNewYear;
}*/
@Override
public void testAutowired() {
happyNewYear.sayHappyNewYear();
}
@Override
public void sayHello(String name) {
System.out.println("Hello:" + name);
}
}
新建一个测试类,测试注解@Autowired是否自动装配bean:
package com.jpeony.spring;
import com.jpeony.spring.hello.ApplicationConfig;
import com.jpeony.spring.hello.HappyNewYear;
import com.jpeony.spring.hello.HelloService;
import com.jpeony.spring.hello.HelloServiceImpl;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author yihonglei
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class AutowiredTest {
@Autowired
private HappyNewYear happyNewYear;
@Autowired
private HelloService helloService;
/**
* 通过断言测试happyNewYear是否被注入到spring容器中。
* 如果不为空,说明自动装配成功。
*
* @author yihonglei
*/
@Test
public void testHappyNewYear() {
Assert.assertNotNull(happyNewYear);
}
/**
* 调用HelloServiceImpl类中的testAutowired,
* 而testAutowired方法中通过HappyNewYear类调用
* 其sayHappyNewYear方法。
*
* @author yihonglei
*/
@Test
public void testAutowired() {
helloService.testAutowired();
}
}