自动装配
### 1. 自动装配:Autowire
自动装配是一种自动为Bean的属性注入值的做法!
假设存在`Student`类:
public class Student { private City from; public City getFrom() { return from; } public void setFrom(City from) { this.from = from; } }
类的属性希望被自动的注入值,则应该配置它的`autowire`属性:
<bean id="student" class="cn.tedu.spring.Student" autowire="default"> </bean>
关于`autowire`属性,取值可以是`byName`,表示**根据名称实现自动装配**,即:要求类中的属性名与被装配进来的Bean的id保持一致(其实本质上还是通过SET方法注入的,要求的是SET方法的名称与Bean的id保持一致),所以:
<bean id="from" class="cn.tedu.spring.City" /> <bean id="student" class="cn.tedu.spring.Student" autowire="byName"> </bean>
自动装配其实是**尝试自动装配**,即:能够装配就装配,不能装配也不会报错!
配置时,`autowire`属性的取值还可以是`byType`,即**根据类型实现自动装配**,Spring容器在尝试自动装配时,会在容器中(Spring管理的范围内)查找类型匹配的对象,如果没有,则不装配,如果有1个,则可以成功的自动装配,如果匹配类型的有2个或更多,则抛出异常`UnsatisfiedDependencyException`!
除此以外,该属性还可以有其它取值,但是,通常,并不会在开发中使用该属性,因为它存在是否装配不明确的问题!而且,在实际开发中,其实自定义的Bean根本就不会在XML文件中配置,所以更加无处使用该属性!
关于自动装配,重点理解`byName`和`byType`的特点。
### 2. 注解
#### 2.1. 为什么要使用注解
使用XML应用Spring时,需要使用大量的配置,导致代码的可读性较差(在阅读代码时,经常需要结合多个Java文件和XML配置一起查阅),并且,在XML配置中,如果出现拼写错误也不会实时提示错误信息!
在实际使用时,会大量的使用注解,来取代XML中的配置!
#### 2.2. 基本注解
如果希望某个类被Spring管理,可以在类上添加`@Component`注解,并且,在Spring的配置文件中添加组件扫描的配置:
<!-- 组件扫描 --> <!-- base-package:根包 --> <context:component-scan base-package="cn.tedu.spring" />
以上配置中的`base-package`用于指定组件所在的包,当加载该配置文件时,Spring框架会扫描指定的包,如果类之前添加了注解,则会被Spring管理。
扫描的包之所以是`base-package`,表示各个类在其各层级的子包下,都会被扫描到,例如配置为`cn.tedu.spring`时,如果类是`cn.tedu.spring.dao.UserDao`,也是在扫描范围之内的!
默认情况下,会使用类名且首字母改为小写作为Bean的id,所以,如果被Spring管理的是`Student`类,则默认的id就是`student`,当然,如果觉得默认的id命名不满足当前的应用需求,可以配置注解属性:
@Component("stu") public class Student { }
与`@Component`相同的注解还有:
- `@Controller`:如果某个类在应用中起到的作用是控制器,则应该使用该注解
- `@Service`:如果某个类在应用中起到的作用是业务逻辑类,则应该使用该注解
- `@Repository`:如果某个类在应用中起到的作用是持久层处理(增删改查),则应该使用该注解
其实,以上4个的作用、语法是完全相同的,只是语义不同,建议区分使用。
#### 2.3. 【不常用】 作用域与生命周期的注解
使用`@Scope`注解可以配置Bean的作用域,例如希望实现非单例效果时:
@Component("stu") @Scope("prototype") public class Student extends Object { }
在单例的情况下,如果需要单例模式是懒汉式的,则可以使用`@Lazy`注解:
@Component("stu") @Lazy public class Student extends Object { public Student() { System.out.println("Student()"); } }
其实,`@Lazy`注解也可以配置属性值为`true`或`false`,但是,没有添加该注解时就是饿汉式的,添加该注解表示懒汉式且值为`true`,所以,不必配置属性值!
关于生命周期方法:
@PostConstruct public void init() { System.out.println("Student.init()"); } @PreDestroy public void destroy() { System.out.println("Student.destroy()"); }
注意:以上2个注解是`javax`包中的注解,应该添加Tomcat或其它服务端环境。
### 2.4. 自动装配的注解
假设存在`Student`类中的`public City from;`属性需要被装配值:
@Component public class Student { public City from; }
首先,`City`应该在Spring管理范围之内,即:需要在组件扫描范围之内,且添加了注解:
package cn.tedu.spring; @Component public class City { }
然后,在`Student`类的`from`属性之前,添加`@Autowired`注解即可:
@Autowired public City from;
与使用XML配置自动装配不同,注解方式的自动装配并不要求属性有SET方法,对属性的访问权限也没有要求,即使属性是私有的,也不影响装配过程!
`@Autowired`注解是优先`byType`也可以byName!所以,要求匹配类型的Bean对象有且仅有1个(0个则无法装配,属性值为null,多个则直接抛出异常)!如果项目中确实有多个匹配类型的类,则只保留1个被Spring管理,其它的不由Spring管理即可,例如去掉类之前的`@Component`或相同作用的注解,或把这些类转移到组件扫描的包以外去。
使用`@Resource`注解也可以实现自动装配,它是优先`byName`,如果失败,则会尝试`byType`。并且,使用`@Resource`时,如果一定要根据名称来装配,却存在名称不匹配的问题,可以在注解中配置名称:
@Resource(name="city") public City from;
以上`@Autowired`和`@Resource`都可以实现自动装配,在实际应用中,使用任何一个都可以!因为`@Resource`功能更强,所以很多人喜欢使用它,但是,并不是每个Java项目都是服务端项目,某些项目中可能并没有`javax`包中的内容,也就根本没有`@Resource`注解,就只能使用`@Autowired`,当然,这样的项目毕竟是少数,所以,在使用Java开发的Web应用程序中,使用这2种注解中的任何一个都是正确的做法!
### 2.5. 小结
常用注解:
- `@Controller`
- `@Service`
- `@Component`
- `@Autowired` / `@Resource`
非常用注解:
- `@Repository`
- `@Scope`
- `@Lazy`
- `@PostConstruct`
- `@PreDetroy`
### 【附】 关于单例模式
public class King { private static King king; private King() { } public static King getInstance() { synchronized("lock") { if (king == null) { king = new King(); } } return king; } }