Spring的自动装配是什么?
spring中提供了向Bean中自动注入依赖的功能,这个过程就是自动装配,当向bean中注入的内容非常多的时候,自动注入依赖的功能将极大的节省注入程序的时间。
Spring自动装配有两类:
基于xml文件的自动装配:byType(类型),byName(名称), constructor(根据构造函数)
基于注解的自动装配:@Autowired,@Resource,@Value
使用XML文件的自动装配
使用xml文件的自动装配有三种类型:
byType byName constructor
byType(类型)
在byType(类型模式中)spring容器会基于反射查看bean定义的类,然后找到依赖类型相同的bean注入到另外的bean中,这个过程需要setter注入来完成,因此必须存在setter方法,否则就会注入失败
Dao实现层
public class UserDaoImpl implements UserDao{
@override
public void done(){
System.out.println("UserDaoImpl.invoke.....");
}
}
Service层
public class UserServiceImpl implements UserService{
private UserDao userDao;
@param userDao // 在这里将属性注入了进去
public void setUserDao(UserDao userDao){
this.UserDao=userDao;
}
@override
public void done(){
userDao.done();
}
}
对应的配置文件为:
<bean id="userDao“ class="com.spring.springioc.dao.impl.UserDaoImpl”/>
<!--byType根据类型自动装配userDao-->
<bean id="userService" autowire="byType" class="com.spring.springioc.service.impl.UserServiceImpl"/>
对应的测试代码为
@Test
public void test3(){
ApplicationContext applicationContext=new ClassPathXMLApplicationContext("/spring/spring-ioc2.xml");
UserSercie userService=(userService)applicationContext.getBean("userService“);
userService.done();
}
总结:通过autowire属性,当属性值为byType的时候,会自动启动实例的注入,装配这个类,在autowire=type模式下,Spring容器将会基于反射查看bean定义的类(查看了之后就知道当前bean有什么依赖了),然后找到与依赖类型相同的bean注入到这个bean中,这个过程就要借助setter注入来完成,因此必须存在set函数
使用autowire=byType自动注入,注入失败的情况
使用autowire=byType注入失败,**因为使用byType是基于类型的注入,因此之关系依赖的类型是什么样,不关心被注入的依赖的名称是否是要求注入的,**因此当xml文件中存在多个相同类型名称,不同的实例Bean的时候,因为存在多种适合的选项,Spring容器不知道应该注入哪种,此时需要我们为spring容器提供帮助指定注入的Bean的实例,在不需要注入的实例Bean中添加标签autowire-candidate=false
<bean id="userDao" class="com.spring.springioc.dao.impl.UserDaoImpl"/>
<!--这个bean是userDao类型的,但不是要注入的类型-->
<bean id="userDao2" autowire-candidate="false" class="com.spring.springioc.dao.impl.userDaoImpl"/>
<bean id="userService" autowire="byType" class="com.spring.springioc.service.impl.userSericeImpl"
byName(名称)
使用autowire=byName进行自动装配,此时spring将会尝试将属性名和bean名称进行匹配,如果找到的话就注入依赖中。
autowire=byName进行自动装配的时候,spring容器将会检查需要注意依赖的bean中的依赖名称,当存在依赖的名称时候就将对应得bean注入进去
<bean id="userDao" class="com.spring.springioc.dao.impl.userDaoImpl"/>
<bean id ="userDao2" class="com.spring.springioc.dao.impl.userDaoImpl"/>
采用autowire=typeName
<bean id="userServie” autowire=byName class="com.spring.springioc.service.impl.userServiceImpl"/>
**需要注意的是:**如果spring容器中没有找到对应名称可以注入的bean,将不会向依赖中注入任何bean,此时依赖的bean为null。
constructor(构造器)
使用autowire=constructor的时候,spring容器同样会尝试找到那些类型与构造函数相匹配的bean,然后注入
public class UserServiceImpl implements UserService{
private UserDao userDao;
public UserServiceImpl(userDao userDao){
this.userDao=userDao;
}
@override
public void done(){
userDao.done();
}
}
对应的配置文件
<bean id="userDao" class="com.spring.springioc.dao.impl.UserDaoImpl"/>
<bean id="userService" autowire="constructor" class="com.spring.springioc.service.impl.UserServiceImpl"/>
autowire=constructor模式下,spring容器会找到类型和构造函数中的类型相匹配的bean,然后注入。
在实际测试开发中当spring容器中出现多个类型和构造函数中的类型相匹配的bean,那么bean的名称和要依赖的名称相同的将会注入进去,会自动将同类不同名的bean过滤掉,如果只有一个bean,类名相同但是名称不同,也会将这个注入到该类中,如果有两个以上bean,类名相同,但是名称不同,这个时候spring容器不知道选择哪一个bean,需要使用autowire-candidate="false"进行过滤。
使用注解的自动装配
基于注解的自动装配有三种
@Autowired @Resource @Value
@Autowired
基于xml文件的自动装配虽然是自动装配但是还是需要在xml文件中进行手动注入属性,在bean实例过多的时候,手动设置自动注入属性不太美好。**使用@Autowired注释,可以对成员变量,方法以及构造函数进行标注,完成自动装配的工作。**通过@Autowired的使用标注到成员变量的时候不需要有set方法,Autowired是按照默认类型匹配的
使用注解之前必须要注册注解驱动。
context:annotation-config/
public class UserServiceImpl implements UserService{
// 使用Autowired标注成员变量
@Autowired
private UserDao userDao;
@Autowired 使用autowired标注构造方法
public UserServiceImpl(UserDao userDao){
this.userDao=userDao;
}
@Autowired 使用autowired标注set方法
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Override
public void done(){
userDao.done();
}
}
如上所示,通过@Autowired注解通过修饰成员变量,成员方法,构造方法三种方式将userDao依赖注入到UserService中,xml配置文件只要将bean实例声明出来就系那个,在实际开发中建议使用成员变量的注入方式
在使用@Autowired的时候还传递了一个required=fale属性,required属性为false表示userDao实例存在就进行注入不存在的话就忽略,如果required=true,表示必须要进行注入,如果userDao实例不存在就抛出异常。默认情况下@Autowired是按照类型匹配的,如果需要按照名称进行匹配的话,可以使用@Qualifier注解和@Autowired相结合的方式
public class UserServiceImpl implements UserService{
@Autowired
@Qualifier("userDao1") //@Autowired 和@ Qualifier("依赖的名称") 这样就变成了ByName的方式
private UserDao userdao;
}
<!-- 根据@Qualifier("userDao1")自动识别 -->
<bean id="userDao1" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />
@Resource
与@Autowired具备相同效果的是@Resource,@Resource默认使用的是byName的方式进行注入,**使用之前要导入Resource包:Package:javax.annotation.Resource,**可以标注在成员变量和set方法上,但是不能放在构造方法上面,@Resource有两个重要的属性:name type ,spring容器会解析@Resource注解的name属性为bean的名字,type属性为bean的类型,使用@Resource注解的name属性表示按照byName进行解析,使用type属性表示按照byType类型进行解析,不指定的时候默认使用name
@Autowired
@Qualifier("userDao“)
private UserDao userDao;
@Resource(name="userDao") //@Resource注解用于成员变量
private UserDao userDao;
@Resource(name="userDao") //@Resource注解用于成员方法上
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Value
@Autowired和@Resource进行自动装配注入的依赖都是对象类型,而不是简单的值类型,之前使用配置文件的setter和构造函数的时候可以通过property属性和constructor-arg属性既可以注入对象类型也可以注入简单值类型,对于这些类型,比如 int boolean long String 等等,spring容器提供了@Value注解的注入方式,@Value注解接收一个String的值,该值指定了要注入到 内置的java类型 的属性值(不用担心类型转换),一般情况下@Value属性和properties文件配合使用,分为两种情况,一种是SpEL(类似于js中的EL表达式),一种是占位符方式。如
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
public class UserServiceImpl implements UserService {
//标注成员变量
@Autowired
@Qualifier("userDao")
private UserDao userDao;
//占位符方式
@Value("${jdbc.url}")
private String url;
//SpEL表达方式,其中代表xml配置文件中的id值configProperties
@Value("#{configProperties['jdbc.username']}")
private String userName;
@Override
public void done(){
System.out.println("url:"+url);
System.out.println("username:"+userName);
userDao.done();
}
}
对应的xml配置文件为
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--基于占位符方式 配置单个properties -->
<!--<context:property-placeholder location="conf/jdbc.properties"/>-->
<!--基于占位符方式 配置多个properties -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="location" value="conf/jdbc.properties"/>
</bean>
<!--基于SpEL表达式 配置多个properties id值为configProperties 提供java代码中使用 -->
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:/conf/jdbc.properties</value>
</list>
</property>
</bean>
<!--基于SpEL表达式 配置单个properties -->
<!--<util:properties id="configProperties" location="classpath:conf/jdbc.properties"/>-->
<!--注解驱动 -->
<context:annotation-config/>
<bean id="userDao" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl"/>
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl"/>
</beans>