Spring IOC笔记之Autowire 注入方式

Spring IOC 一些常见容易忽略的问题,包括autowire/resource 注入,类型定义,循环依赖等,会后续不断补充。

Autowire注入五种模式

1.no

默认,如果没找到在调用依赖对象时会抛NullPointerException

2.byType

根据bean class类型在容器中找到唯一一个对应的给目标对象注入

当所注入的类型在容器中只有唯一对象时,较为简单不需要关注属性名或setter方法,以下分析多实现情况

场景1 annotation @Autowired example:

DAO:

public interface UserDAO {
     
	void query();
}

两个DAO实现类(UserDAO1, UserDAO2):

@Repository
public class UserDAO1 implements UserDAO {
     
	@Override
	public void query() {
     
		System.out.println("1----User dao query");
	}
}
@Repository
public class UserDAO2 implements UserDAO {
     
	@Override
	public void query() {
     
		System.out.println("2----User dao query");
	}
}

Service调用类(UserService):

@Service
public class UserService {
     

	@Autowired
	private UserDAO userDAO1;

	public void queryUser(){
     
	   userDAO1.query();
	}
}

测试代码:

@Configuration
@ComponentScan("com.example.demo.ioc")
public class AutowireDemo {
     
	public static void main(String[] args) {
     

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowireDemo.class);
		context.getBean(UserService.class).queryUser();
	}

}

输出:

1----User dao query

因为在Service中注入的UserDAO有多个实现的情况下,找不到该类型唯一的对象, 再次根据属性名userDAO1判断注入UserDAO1. 当属性名为UserDAO2在容器中的id时,则会注入UserDAO2.

场景2 xml autowire byType example:

xml中配置default-autowire

beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
 default-autowire="byType">

	<!--<bean id="userDAO1" class="com.example.demo.ioc.dao.UserDAO1"/>-->
	<bean id="userDAO2" class="com.example.demo.ioc.dao.UserDAO2"/>
	<bean id="userService" class="com.example.demo.ioc.service.UserService" />

</beans>

UserService:

public class UserService {
     

	private UserDAO userDAO1;

	public void queryUser(){
     
		userDAO1.query();
	}

	public UserDAO getUserDAO1() {
     
		return userDAO1;
	}

	public void setUserDAO1(UserDAO userDAO) {
     
		this.userDAO1 = userDAO;
	}
}

测试代码:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
		context.getBean(UserService.class).queryUser();

输出:

2----User dao query

以xml方式使用byType方式注入时,要保证所在环境里对应类型的bean唯一性,xml会自动检测编译报出error。此时属性名、setter方法名都不具备任何特殊处理意义,只跟类型有关。

3.byName

根据name找到容器中唯一id的bean注入给对象.

场景1 annotation方式@Resource 默认将属性名作为name example 1:

当resource不指定name时,则将属性名作为name查找bean,否则按指定name查找bean,与属性名无关


@Service
public class UserService {
     

	@Resource
	private UserDAO userDAO2;

	public void queryUser(){
     
		userDAO2.query();
	}
}

输出:

2----User dao query

@Resource 指定type注入依赖 example 2:

​ @Resource使用Type属性时,属性名不能与存在的bean Name重合,可能会导致注入对象不一致问题,see @exampl 3

@Service
public class UserService {
     

	@Resource(type = UserDAO2.class)
	private UserDAO userDAO;

	public void queryUser(){
     
		userDAO.query();
	}
}

输出:

2----User dao query

@Resource 指定type同时属性名对应要注入bean的类型不一致 example 3:

属性名指定要注入的为UserDAO1,但同时type指定类型为UserDAO2

@Service
public class UserService {
     

	@Resource(type = UserDAO2.class)
	private UserDAO userDAO1;

	public void queryUser(){
     
		userDAO1.query();
	}

输出:

Error:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDAO1' is expected to be of type 'com.example.demo.ioc.dao.UserDAO2' but was actually of type 'com.example.demo.ioc.dao.UserDAO1'
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDAO1' is expected to be of type 'com.example.demo.ioc.dao.UserDAO2' but was actually of type 'com.example.demo.ioc.dao.UserDAO1'
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:321)

场景2 xml配置中配置default-autowire example:

局部为bean单独配置:

<bean id="userService" class="com.example.demo.ioc.service.UserService" autowire="">

beans.xml:

全局配置autowire,指定default-autowire时,不用再单独为bean的property进行显示传值,会自动根据配置的mode初始化bean


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
 default-autowire="byName">

	<bean id="userDAO1" class="com.example.demo.ioc.dao.UserDAO1"/>
	<bean id="userDAO2" class="com.example.demo.ioc.dao.UserDAO2"/>
	<bean id="userService" class="com.example.demo.ioc.service.UserService">
	bean>

beans>

UserService:

注入的UserDAO由setter方法决定,setUserDAO2将容器中名为userDAO2的bean赋给属性userDAO1

public class UserService {
     

	private UserDAO userDAO1;

	public void queryUser(){
     
		userDAO1.query();
	}

	public UserDAO getUserDAO1() {
     
		return userDAO1;
	}

	public void setUserDAO2(UserDAO userDAO) {
     
		this.userDAO1 = userDAO;
	}
}

测试代码:

public class AutowireDemo {
     
	public static void main(String[] args) {
     

		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
		context.getBean(UserService.class).queryUser();
	}

}

输出:

2----User dao query

4.constructor

与byType相似,按类型注入。需要提供含参构造方法,按照类型查找对应的bean作为参数注入。否则会在使用所依赖的对象是Null Pointer

5.default

与no相同

Singleton依赖prototype Bean,原型失效引起的问题

beans.xml

<bean id="prototypeBean" class="com.example.demo.ioc.service.PrototypeBean" scope="prototype">bean>

<bean id="singletonBean" class="com.example.demo.ioc.service.SingletonBean" scope="singleton">bean>

SingletonBean:

public class SingletonBean {
     

	private PrototypeBean prototypeBean;
	public void print(){
     
		System.out.println("prototypeBean hashcode:"+prototypeBean.hashCode());
		System.out.println("singletonBean hashcode:"+this.hashCode());
	}

	public void setPrototypeBean(PrototypeBean prototypeBean) {
     
		this.prototypeBean = prototypeBean;
	}
}

PrototypeBean:

public class PrototypeBean {
     
}

测试代码:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
for(int i=0;i<3;i++) {
     
     context.getBean(SingletonBean.class).print();
}

输出:

prototypeBean hashcode:1512981843
singletonBean hashcode:42768293
prototypeBean hashcode:1512981843
singletonBean hashcode:42768293
prototypeBean hashcode:1512981843
singletonBean hashcode:42768293

singleton中依赖的原型bean在输出中结果表现为单例.因为在spring初始化单例bean时已经构造并初始化好了依赖对象。每次调用的prototypeBean都是初始化好完成的那个bean。

解决方案

1.ApplicationContextAware

实现该接口,可获取ApplicationContext,在每次调用prototype的时候,从容器中获取即可实现每次即用即建。

但对应的会极大程度引入spring整个环境,不利于解耦

2.lookup method

beans.xml:

<bean id="prototypeBean" class="com.example.demo.ioc.service.PrototypeBean" scope="prototype">
	bean>

	<bean id="singletonBean" class="com.example.demo.ioc.service.SingletonBean" scope="singleton" >
		<lookup-method name="getPrototypeBean" bean="prototypeBean">lookup-method>
	bean>

SingletonBean:

将获取prototypeBean的方式转为look up method

public  abstract  class SingletonBean {
     

	public void print(){
     
		System.out.println("prototypeBean hashcode:"+getPrototypeBean().hashCode());
		System.out.println("singletonBean hashcode:"+this.hashCode());
	}

	public void setPrototypeBean(PrototypeBean prototypeBean) {
     
		this.prototypeBean = prototypeBean;
	}

	public abstract PrototypeBean getPrototypeBean();
}

lookup method由spring负责解析返回对应的一个bean,不需要程序员手动实现,因此可作为抽象类存在与配置

输出:

prototypeBean hashcode:668849042
singletonBean hashcode:434176574
prototypeBean hashcode:2096057945
singletonBean hashcode:434176574
prototypeBean hashcode:1689843956
singletonBean hashcode:434176574

你可能感兴趣的:(Java,spring,spring,ioc)