谈及一个问题,无非牵扯到三点,是什么,怎么来的,怎么用的
Spring自动注入是什么
是指容器中的一个组件中需要用到另一个组件(例如聚合关系)时,依靠spring容器创建对象,而不是手动创建;
Spring自动注入怎么来的
在这里就要提及一种设计思路,在以往,我们很多都是从上到下的描写代码,先写顶层,然后在逐步更改底层,但是这样会出现一种情况,如果底层的某个代码需要更改,那么出现的问题就是整个代码都要更改。所以,为了避免这种情况,我们会采用新的设计模式,依赖倒置模式,也就是从底层出发,然后再依次搭建顶层,而为了实现这一设计模式,自动注入就出现了
依赖倒置原则
方法就是用的依赖注入方法
思路控制反转
Spring自动注入怎么用的
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:
(1)基于注解的注入。
(2)构造方法注入。
(3)setter注入,
1、创建一个模拟DAO层类:UserTestDao。用于测试依赖注入。其源码如下:
public class UserTestDao {
public boolean login(){
System.out.println("UserTestDao.login()");
return true;
}
} 2.1 基于注解的注入-自动装配模式 2.1.1 bean标签的autowire属性 在介绍注解注入的方式前,先了解bean的一个属性autowire。
autowire属性有5种模式:
(1)no
(默认)不采用autowire机制.。这种情况,当我们需要使用依赖注入,只能用标签。
(2)byName
通过属性的名称自动装配(注入)。Spring会在容器中查找名称与bean属性名称一致的bean,并自动注入到bean属性中。当然bean的属性需要有setter方法。例如:bean A有个属性master,master的setter方法就是setMaster,A设置了autowire=“byName”,那么Spring就会在容器中查找名为master的bean通过setMaster方法注入到A中。
(3)byType
通过类型自动装配(注入)。Spring会在容器中查找类(Class)与bean属性类一致的bean,并自动注入到bean属性中,如果容器中包含多个这个类型的bean,Spring将抛出异常。如果没有找到这个类型的bean,那么注入动作将不会执行。
(4)constructor
类似于byType,但是是通过构造函数的参数类型来匹配。假设bean A有构造函数A(B b, C c),那么Spring会在容器中查找类型为B和C的bean通过构造函数A(B b, C c)注入到A中。与byType一样,如果存在多个bean类型为B或者C,则会抛出异常。但时与byType不同的是,如果在容器中找不到匹配的类的bean,将抛出异常,因为Spring无法调用构造函数实例化这个bean。
(5)default
采用父级标签(即beans的default-autowire属性)的配置。
2.1.2 使用注解注册bean 有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
(1)@Component:可以用于注册所有bean
(2)@Repository:主要用于注册dao层的bean
(3)@Controller:主要用于注册控制层的bean
(5)@Service:主要用于注册服务层的bean
这些都是注解在平时的开发过程中出镜率极高,@Component、@Repository、@Service、@Controller实质上属于同一类注解,用法相同,功能相同,区别在于标识组件的类型。@Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。
2.1.3 装配bean时常用的注解 2.1.3.1 @Resource 1、 @Resource是默认以byName的方式去匹配与属性名相同的bean的id。
如果没有找到就会以byType的方式查找,如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。
2、 @Resource不属于spring的注解,而是Java的注解,它来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean。
@Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Resource { String name() default “”; Class type() default java.lang.Object.class; … 使用demo
public class AnotationExp {
@Resource(name = "HappyClient")
private HappyClient happyClient;
@Resource(type = HappyPlayAno.class)
private HappyPlayAno happyPlayAno;
} 2.1.3.2 @Autowired 1、使用@Autowired注解来自动装配指定的bean。
2、在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
-
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; -
如果查询的结果不止一个,那么@Autowired会根据名称来查找; -
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
3、@Autowired属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值。
- @Autowired注解的定义:
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
使用demo:
@Controller
public class HappyController {
@Autowired
@Qualifier("goodClubService")
private ClubService clubService;
}
2.1.3.3 @Autowired和@Resource的总结 1、相同点
@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。
2、不同点
(1)提供方
@Autowired是Spring的注解;@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供,需要JDK1.6及以上。
(2)注入方式
@Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入;
(3)属性
@Autowired注解可用于为类的属性、构造器、方法进行注值。
默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。
还有一个比较重要的点就是,@Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualified注解进行限定,指定注入的bean名称。
@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
需要注意的是:
-
@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。 -
@Resource注解的使用性更为灵活,可指定名称,也可以指定类型 ; -
@Autowired注解进行装配容易抛出异常,特别是装配的bean类型有多个的时候,而解决的办法是需要在增加@Qualitied进行限定。
2.2 构造方法注入 创建一个模拟Service类:UserTestServiceImpl。源码如下:
public class UserTestServiceImpl {
private UserTestDao userTestDao;
public UserTestServiceImpl(UserTestDao userTestDao) {
this.userTestDao = userTestDao;
}
public void login() {
System.out.println("UserTestServiceImpl.login()");
userTestDao.login();
}
}
1、在spring的配置文件中注册UserTestServiceImpl,将UserTestDao 通过constructor-arg标签注入到UserTestServiceImpl的某个有参数的构造方法。spring配置如下:
<
!--dao -->
<bean id="userTestDao" class="com.chenlw.java.web.utils.springwiki.UserTestDao">
</bean>
<!-- 注册userService -->
<bean id="userTestServiceImpl" class="com.chenlw.java.web.utils.springwiki.UserTestServiceImpl">
<!-- 将DAO对象注入Service层 -->
<constructor-arg ref="userTestDao"/>
</bean>
2、测试代码:
@Test
public void testSpringInjection() {
UserTestServiceImpl userTestService = (UserTestServiceImpl) applicationContext.getBean("userTestServiceImpl");
userTestService.login();
}
3、运行结果:
UserTestServiceImpl.login()
UserTestDao.login()
2.3 setter注入 1、修改UserTestServiceImpl类。源码如下:
public class UserTestServiceImpl {
private UserTestDao userTestDao;
public UserTestDao getUserTestDao() {
return userTestDao;
}
public void setUserTestDao(UserTestDao userTestDao) {
this.userTestDao = userTestDao;
}
public void login() {
System.out.println("UserTestServiceImpl.login()");
userTestDao.login();
}
}
2、spring配置如下:
<!-- 注册dao -->
<bean id="userTestDao" class="com.chenlw.java.web.utils.springwiki.UserTestDao">
</bean>
<!-- 注册userService -->
<bean id="userTestServiceImpl" class="com.chenlw.java.web.utils.springwiki.UserTestServiceImpl">
<!-- 将DAO对象注入Service层 -->
<property name="userTestDao" ref="userTestDao"/>
</bean>
注意:
-
spring会将name值的每个单词首字母转换成大写,然后再在前面拼接上"set"构成一个方法名,然后去对应的类中查找该方法,通过反射调用,实现注入。 -
name属性值与类中的成员变量名以及set方法的参数名都无关,只与对应的set方法名有关。
3、测试代码:
@Test
public void testSpringInjection() {
UserTestServiceImpl userTestService = (UserTestServiceImpl) applicationContext.getBean("userTestServiceImpl");
userTestService.login();
}
4、运行结果:
UserTestServiceImpl.login()
UserTestDao.login()
|