超全的SpringIOC和DI实践

这里的环境是在史上超详细的SpringMVC框架搭建基础上搭建的,如有不懂之处,望请参考。

一、配置SpringIOC容器

在web项目中配置Spring的Ioc容器其实就是创建web应用的上下文

在web.xml配置springIOC容器

  <!-- springIOC容器的配置  -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/spring-context.xml</param-value>
  </context-param>
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener> 

在src下config包里创建spring-context.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-3.2.xsd">
		
</beans>

二、基于XML的配置方式

DI依赖注入,有构造器注入和setter注入两种方式

创建User类和Address类作为Bean

public class User {
	private String name;
	private Address address;
	private String[] books;
	private List<String> courses;
	private Map<String,String> cards;
	private Set<String> games;
	private Properties properties;
	public User(String name,Address address){
		this.name=name;
		this.address=address;
	}
	//由于篇幅原因,setter方法省略
}
public class Address {
	private String name;

	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Address [name=" +name + "]";
	}
}

(1)构造器和setter属性的注入

	<!-- 先创建一个空的对象,再用对象来注入,类里面的构造函数必须是无参的 -->
	<!-- setter注入要求类里面必须要有注入属性的set方法 -->
	<bean id="address" class="com.my.model.Address">
	<property name="name" value="洛阳市"/>
	</bean>
	
	<!-- 构造器注入 -->
	<!-- 构造器注入不要求有注入属性的set方法 -->
	<bean id="user" class="com.my.model.User">
	<constructor-arg name="name" value="王雷"></constructor-arg>
	<constructor-arg name="address" ref="address"></constructor-arg>
	</bean>

(2)集合类型setter属性的注入

<bean id="myuser" class="com.my.model.User">
		<property name="name" value="王雷"></property>
		<!-- 数组注入 -->
		<property name="books">
			<array>
				<value>小白书</value>
				<value>白皮书</value>
				<value>小红书</value>
			</array>
		</property>
		<!-- List 注入 -->
		<property name="courses">
			<list>
				<value>java从入门到精通</value>
				<value>java从精通到精辟</value>
			</list>
		</property>
		<!-- Map注入 -->
		<property name="cards">
			<map>
				<entry>
					<key><value>ICBC</value></key>
					<value>工商银行</value>
				</entry>
				<entry key="ABC">
					<value>农业银行</value>
				</entry>
			</map>
		</property>
		<!-- Set注入 -->
		<property name="games">
			<set>
				<value>王者荣耀</value>
				<value>LOL</value>
				<value>dota</value>
			</set>
		</property>
		<!-- 直接赋值为null -->
		<!-- <property name="wife"><value>null</value></property> -->
		<!-- null注入 -->
		<property name="wife"><null /></property>
		<!-- properties 注入 -->
		<property name="properties">
			<props>
				<prop key="driver">com.mysql.jdbc.Driver</prop>
				<prop key="url">jdbc:mysql://localhost:3306/mybatis</prop>
				<prop key="username">root</prop>
				<prop key="password">root</prop>
			</props>
		</property>

	</bean>

三、基于注解的配置方式

常用定义Bean的注解有四种

  1. @Component用于Spring中的Bean(普通的类)
  2. @Repository 用于对DAO实现类进行标注
  3. @Service 用于对Service实现类进行标注
  4. @Controller 用于对Controller实现类进行标注

它们的功能都表示定义一个bean,只是用名字来划清界限

@Resource和@Autowired都是做bean的注入时使用
共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
在这里插入图片描述
不同点
(1)Resource由J2EE提供,需要导入包javax.annotation.Resource,Autowired为Spring提供的注解。
(2)@Resource默认按照ByName自动注入 。
@Resource有两个重要的属性:name和type。如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
@Autowired默认是按照类型byType装配依赖对象。
@Autowired默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。

@Service
public class TestServiceImpl {

	public void print(){
		System.out.println("我是TestServiceImpl的print()");
	}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:config/spring-*.xml"})
public class TestAnnotation {

	@Resource
	private TestServiceImpl my;
	
	@Test
	public void test(){
		my.print();
	}
}

运行结果为:
在这里插入图片描述

需要注意的是,不能直接用@Test做单元测试,否则会出现空指针异常,大致原因如下:
junit单元测试是一个独立的单元测试,它跟你的上下文没有关系,原因据说是因为spring为了考虑安全性问题,在多线程情况下,不支持直接使用 @Resouce 注解方式进行直接的bean注入,也就是说,如果在多线程调用该注入实例化的变量时,将会报NullPointerException 。
解决方案是在需要测试的类上面添加Spring提供的单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:config/spring-*.xml"})

除了需要导入单元测试的那两个包之外,还需要导入,如果不会单元测试请参考Java之JUnit4单元测试
在这里插入图片描述
细心的同学会发现,@Resource默认按照ByName的方式进行自动装配,那么为什么上面的测试代码把名字命名为my也可以运行成功,探究@Resource的原理就知道了。
既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行进行查找,如果找到就注入。也就是说,如果按byName找不到,则按byType去找。

四、获取IOC容器的方式

获取IOC容器,其实就是获取Spring的上下文的容器,即ApplicationContext对象,通过该对象可以得到容器中定义的Bean。

方法一: 直接加载配置IOC上下文的XML文件

ApplicationContext context=new ClassPathXmlApplicationContext("config/spring-context.xml");

方法二: 通过Spring提供的工具类获取容器对象

		ServletContext application=request.getServletContext();
		ApplicationContext context1 = WebApplicationContextUtils.getRequiredWebApplicationContext(application);
		ApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(application);

这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。
上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
由于spring是注入的对象放在ServletContext中的,所以可以直接在ServletContext取出 WebApplicationContext 对象:

WebApplicationContext context = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

方法三: 继承自抽象类ApplicationObjectSupport
抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。 Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
方法四: 继承自抽象类WebApplicationObjectSupport
类似上面方法,调用getWebApplicationContext()获取WebApplicationContext
方法五: 实现接口ApplicationContextAware
实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。 Spring初始化时,会通过该方法将ApplicationContext对象注入。

获取ioc容器之后,就可以根据需要得到容器里面的bean对象了,可以通过ApplicationContext对象的getBean方法得到

		Address address=(Address) context.getBean("address");
		User user=(User) context.getBean(User.class);

第一种方式是通过bean的ID得到的,第二种方式是通过bean的类型得到的,如果得到的结果不是唯一,则会发生异常。

参考链接
https://blog.csdn.net/m_q_x/article/details/77881529
https://blog.csdn.net/Changui_/article/details/78208315
https://blog.csdn.net/khandudu/article/details/81206400

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章