一、Spring注解方式创建对象
Spring针对Bean对象的创建提供了以下注解:@Component、@Controller、@Service、@Repository,早期Spring对于创建Bean对象支持的注解只有@Conmpnent,但是这样就会带来一个麻烦,因为你的视图层、业务层、以及持久化层都使用@Component来进行Bean对象的创建,这样程序员很容易搞混乱,不能直接通过注解的名称一眼就确定自己现在操作的具体是那一层的代码,为了解决上述问题Spring引入了@Controller、@Service、@Repository来区别不同的层,虽然上面四个注解都能实现对象的创建,但是为了避免麻烦,建议在不同的层上使用相对应的注解.
下面就来测试一下使用注解的方式来创建Bean对象,测试步骤如下:
1、创建一个配置类
// 标记该类是一个Spring配置类
@Configuration
// 开启注解扫描,Spring会自动扫描com.spring01包及其子包下的所有注解
@ComponentScan({"com.spring01"})
public class SpringConfiguration {
}
2、自定义一个接口UserDao
public interface UserDao {
public abstract void call();
public abstract String sendMessage();
}
3、UserDaoImpl实现UserDao接口
// 使用@Repository将UserDaoImpl对象注入到IOC容器中,默认的id是将类名的首字母小写,例如UserDaoImpl对象注入到IOC的id就是 userDaoImpl
// 也可以显示的指定名称,例如@Repository("xiaomaomao"),那么UserDaoImpl注入到IOC容器中的id就是xiaomaomao
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void call() {
System.out.println("call somebody");
}
@Override
public String sendMessage() {
System.out.println("send message to somebody");
return null;
}
}
4、测试
@Test
public void testSpringAnnotation(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserDao userDao = context.getBean("userDaoImpl", UserDao.class);
System.out.println(userDao);
}
5、测试结果(可以看到UserDaoImpl的对象由IOC容器创建出来了)
com.spring01.dao.UserDaoImpl@157632c9
注意:开启组件扫描细节:
@ComponentScan 中在扫描类的时候,通过对属性 includeFilters 和 excludeFilters 的设置,可以有对于类型有一个筛选功能.includeFilters 指定了哪些类才能被扫描到,excludeFilters 则相反,指定了哪些类被排除扫描.
1、excludeFilters (下面这个例子的意思是:开启组件扫描com.spring01包下的所有包及其子包,但是不会去扫描@Controller、@Service注解)
// 标记该类是一个Spring配置类
@Configuration
// @ComponentScan:开启Spring注解扫描功能
// value:Spring会自动扫描com.spring01包及其子包下的所有注解
// useDefaultFilters:默认值是true,当需要选定哪些注解不需要扫描时可以忽略不写,
// 但是如果需要选定哪些包要进行注解扫描时必须将其值设置为false
// classes:指定对哪些类型的注解进行扫描/不进行扫描
@ComponentScan(value="com.spring01",useDefaultFilters = true,
excludeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = {Controller.class,Service.class})})
public class SpringConfiguration {
}
2、includeFilters(如果是此种情况,useDefaultFilters的值必须为false)(下面这个例子的意思是,只扫描com.spring01包下的@Repository注解)
// 如果使用includeFilters,则不使用默认的过滤器,useDefaultFilters必须设置值为false
@ComponentScan(value="com.spring01",useDefaultFilters = false,
includeFilters= {@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = {Repository.class})})
public class SpringConfiguration {
}
二、Spring注解方式注入属性
1、注入普通类型属性@Value(" ")
// 注入普通类型的属性的时候,使用@Value注解
@Value("xiaomaomao")
private String name;
2、注入引用类型属性(对象)
Spring根据注解注入对象属性所用到的注解是@Autowired、@Qualifier
注意:@Resource也可以达到相同的作用,只不过@Resource是JDK的注解,不是Spring的注解,一般不使用
// 注意@Resource注解是JDK的注解,这里可以看到该注解属于javax包下面的
import javax.annotation.Resource;
@Resource
public class TestSpringAnnotation
2.1、测试@AutoWired注解
@Autowired注解是根据属性的类型来进行自动装配注入的,如果IOC容器中存在该类型的对象,则将该类型的对象进行注入,如果没有则不注入,如果IOC容器中存在多个该类对象则会报错.
下面就来测试一下使用@AutoWired注解来进行属性注入,测试步骤如下:
1、创建UserDao接口
public interface UserDao {
public abstract void call();
public abstract String sendMessage();
}
2、创建UserDao接口的实现类UserDaoImpl01
// 默认注入到IOC容器中的Bean id 是 userDaoImpl01
@Repository
public class UserDaoImpl01 implements UserDao{
@Override
public void call() {
System.out.println("UserDaoImpl01 call somebody");
}
@Override
public String sendMessage() {
System.out.println("UserDaoImpl01 send message to somebody");
return null;
}
}
3、创建UserService接口
public interface UserService {
public abstract void call();
public abstract String sendMessage();
}
4、创建UserService接口的实现类UserServiceImpl
// 默认注入到IOC容器中的Bean id 是 userDaoImpl01
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void call() {
userDao.call();
}
@Override
public String sendMessage() {
userDao.sendMessage();
return "OK";
}
}
5、创建Spring的配置类
@Configuration
@ComponentScan(value="com.spring01")
public class SpringConfiguration {
}
6、测试类
public class TestSpringAnnotation {
@Test
public void testSpringAnnotation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.call();
userService.sendMessage();
}
}
7、测试结果
UserDaoImpl01 call somebody
UserDaoImpl01 send message to somebody
上面的测试中,因为UserDao只有一个实现类UserDaoImpl01,真正注入到Spring容器中的只有一个userDaoImpl01,这样我们并不能验证@Autowired是不是按照类型进行注入的,所以我们想要测试的话还需要为UserDao接口再创建出一个实现类UserDaoImpl02,这样当两个UserDao类型注入到IOC容器中,看会发生什么情况.
8、再创建一个UserDao接口的实现类UserDaoImpl02
@Repository
public class UserDaoImpl02 implements UserDao{
@Override
public void call() {
System.out.println("UserDaoImpl02 call somebody");
}
@Override
public String sendMessage() {
System.out.println("UserDaoImpl02 send message to somebody");
return null;
}
}
果然不出意外,出现了如下报错,报错的信息也很明显,也就是发现了两个UserDao类型的属性,Spring不知道应该去注入哪一个属性.(这样也验证了@Autowired注解是根据属性的类型来进行依赖注入的,如果Spring中只有某个类的一个对象时使用@Autowired注解就不会有任何问题).
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring01.dao.UserDao' available: expected
single matching bean but found 2: userDaoImpl01,userDaoImpl02
2.2、测试@Qualifier注解
@Autowired虽然能根据属性的类型进行自动装配注入属性,但是如果IOC容器中存在两个UserDao类型的属性时,这个时候Spring就不知道该注入哪个属性了,为了解决这个问题我们用到了@Qualifier注解,来通过属性的名称来选择注入的属性.注意使用@Qualifier注解的时候需要搭配@Autowired注解一起来使用.
1、修改UserServiceImpl实现类,加上@Qualifier注解
// 默认注入到IOC容器中的Bean id 是 userDaoImpl01
@Service
public class UserServiceImpl implements UserService {
@Autowired
// 使用@Qualifier显示的告诉Spring,将userDaoImpl02属性注入到userDao中
@Qualifier("userDaoImpl02")
private UserDao userDao;
@Override
public void call() {
userDao.call();
}
@Override
public String sendMessage() {
userDao.sendMessage();
return "OK";
}
}
2、测试类
public class TestSpringAnnotation {
@Test
public void testSpringAnnotation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.call();
userService.sendMessage();
}
}
3、测试结果
UserDaoImpl02 call somebody
UserDaoImpl02 send message to somebody