这里的环境是在史上超详细的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的注解有四种
- @Component用于Spring中的Bean(普通的类)
- @Repository 用于对DAO实现类进行标注
- @Service 用于对Service实现类进行标注
- @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