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>
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐