@Required
@required注解适用于bean属性的setter方法。
这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值。(也就是说bean的属性必须在配置的时候就被赋值)
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder=movieFinder;
}
//...
}
@Required注解并不常用。
@Autowired
Autowired可以理解为自动的进行装配。
1.可以将@Autowired注解为“传统”的setter方法
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder=movieFinder;
}
2.可用于构造器或成员变量(比@Required范围更广)
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieFinder(CustomerPreferenceDao customerPreferenceDao){
this.customerPreferenceDao=customerPreferenceDao;
}
默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常(@Autowired是byType来查找bean,如果没有对应class的bean就会出问题。),可以通过下面的方式避免
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Required(required=false)
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder=movieFinder;
}
//...
}
设置required=false表示这并不是一个必须的,如果找不到movieFinder这样的bean的实例也可以。
每个类只能有一个构造器被标记为required-true。要知道一个类可以有很多构造器,每个构造器都可以使用@Autowired注解,但是能声明required=true的只有一个。
@Autowired的必要属性,建议使用@Required注解来代替。
例子
之前的InjectionDAOImpl和InjectionServiceImpl可以再次被用到。
给InjectionDAOImpl加上@Repository,InjectionServiceImpl加上@Service,分别对应存储层和业务层
@Repository
public class InjectionDAOImpl implements InjectionDAO {
public void save(String arg) {
//模拟数据库保存操作
System.out.println("保存数据:" + arg);
}
}
@Service
public class InjectionServiceImpl implements InjectionService {
// @Autowired
private InjectionDAO injectionDAO;
@Autowired
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
// @Autowired
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
//模拟业务操作
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
配置文件不变,依然扫描com.imooc.beanannotation这个包。
在InjectionServiceImpl 这里第一个@Autowired用于成员变量。
单元测试类中测试方法代码如下:
@Test
public void testAutowired() {
InjectionService service = super.getBean("injectionServiceImpl");
service.save("This is autowired.");
}
前边的@Service并没有显式的指定name,所以这个bean的id为injectionServiceImpl,首字母小写。然后的输出和Service中对应的save方法一致。如下:
Service接受参数:This is autowired.
保存数据:This is autowired.:1728997654
第一行是Service中的输出,第二行是Service中的save方法调用了DAO中的输出,所以这种方式注入是可以的。
第二个@Autowired是构造器方式,在构造器里为成员变量进行赋值,输出和前边一致。
@Autowired
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
第三个是为DAO生成一个set方法,然后加上@Autowired,输出接一样。
也就是说我们通过@Autowired完成了set注入和构造注入,这个直接放在成员变量上和set注入是类似的。
依然是@Autowired
可以使用@Autowired注解那些众所周知的解析依赖性接口(我们可以使用它来注解很多Spring直接的解析依赖性接口,这些主要是解析xml文件或者注解等),比如:BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,and MessageSource。
举例
public class MovieRecommender{
@Autowired
private ApplicationContext context;
public MovieRecommender(){}
//...
}
我们在当前类里边声明对ApplicationContext的引用,然后使用@Autowired的注解,这时候我们就可以在当前类中得到IOC的上下文信息,然后使用上下文信息。
新的用法,可以通过添加注解给需要该类型的数组的字段或者方法(也就是说可以注解在数组上,所谓的数组就是用Set<>或者List<>),以提供ApplicationContext中的所有特定类型bean(当Set中声明这种类型的时候,当前的ApplicationContext中所有是Set<>中声明类型的bean或者是它的子类都可以被@Autowired注解,并且把这些bean的实例放到当前的集合中去)。
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs){
this.movieCatalogs=movieCatalogs;
}
当然也可以用Map(key,value的形式,key是所有bean的id,value是所有bean的对象),即可用于装配key为String的Map。
private Map<String,MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String,MovieCatalog> movieCatalogs){
this.movieCatalogs=movieCatalogs;
}
同时,如果我们希望数组(bean的实例)是有序的,可以让bean实现org.springframework.core.Ordered接口或使用@Order注解。
注意
@Autowired是由Spring BeanPostProcessor处理的,所以不能再自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,后边这些自定义的类型必须通过XML或者Spring的@Bean注解加载。
例子,数组及Map的自动注入
首先定义一个接口BeanInterface
里边没有任何东西
public interface BeanInterface {
}
然后定义BeanInterface的实现BeanImplOne和BeanImplTwo,都使用@Component注解以便能够让Spring容器发现。
@Order(2)
@Component
public class BeanImplOne implements BeanInterface {
}
@Order(1)
@Component
public class BeanImplTwo implements BeanInterface {
}
测试类中定义了测试方法:
@Test
public void testMultiBean() {
BeanInvoker invoker = super.getBean("beanInvoker");
invoker.say();
}
然后定义一个调用类BeanIncoker,也用@Component去注解,先给代码然后解释这个调用类:
@Component
public class BeanInvoker {
@Autowired
private List<BeanInterface> list;
@Autowired
private Map<String, BeanInterface> map;
public void say() {
if (null != list && 0 != list.size()) {
System.out.println("list...");
for (BeanInterface bean : list) {
System.out.println(bean.getClass().getName());
}
} else {
System.out.println("List<BeanInterface> list is null !!!!!!!!!!");
}
System.out.println();
if (null != map && 0 != map.size()) {
System.out.println("map...");
for (Map.Entry<String, BeanInterface> entry : map.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue().getClass().getName());
}
} else {
System.out.println("Map<String, BeanInterface> map is null !!!!!!!!!!");
}
}
}
List<>声明数组类型BeanInterface,然后用@Autowired注解当前的变量list。正常情况下,这个list里面会被注解进来Bean的实例,严格上讲就是BeanInterface的两个直接类BeanImplOne和BeanImplTwo。
然后我们定义一个方法say看看输出里边有哪些类。循环list,将list中的每一个bean的名称打出来。
发现两个类One和Two都可以被打印出来,说明@Autowired注解确实可以完成把BeanInterface的两个实现类注解到list当中去。
然后试一试Map,注意Map的key值一定是String,输出的结果差不多。
有一点差别,虽然使用@Autowired会将BeanInterface的所有子类在IOC容器中对应的Bean的实例都放到map和list中去。但是放到list中时只是把对象放进来,放到map中时则是将bean的id和实例一起放进来。
为了实现有序,可以在两个类中写上注解@Order,@Order(value=1)可以写为@Order(1)。分别设置1和2,遍历时候list的顺序根据我们设置的顺序来显示,map则无效。因为对于数组我们经常根据顺序来取,但是map则是依靠key。