IOC
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂,DI(依赖注入)是IOC思想的具体实现
-
Spring提供IOC容器实现的两种方式:
-
BeanFactory:IOC容器的基本实现方式,是Spring内部的使用接口,不提供开发人员使用
加载配置文件时候不会创建对象,在获取对象时采取创建对象
-
ApplicatioContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
加载配置文件的时候就会把配置文件中的对象进行创建
-
-
Bean的作用域
Spring中Bean默认是单例的,表示对IOC容器中的同一个Bean多次getBean得到的是同一个实例:
@Test public void testStu(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); Stu stu = (Stu) context.getBean("stu"); Stu stu2 = (Stu) context.getBean("stu"); System.out.println(stu); //com.dxy.spring5.Stu@17776a8 System.out.println(stu2); //com.dxy.spring5.Stu@17776a8 }
当然我们可以通过bean中 scope=”prototype“ 属性设置为多例
<bean id="stu" class="com.dxy.spring5.Stu" scope="prototype"></bean>
修改后得到的输出为
@Test public void testStu(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); Stu stu = (Stu) context.getBean("stu"); Stu stu2 = (Stu) context.getBean("stu"); System.out.println(stu); //com.dxy.spring5.Stu@17776a8 System.out.println(stu2); //com.dxy.spring5.Stu@69a10787 }
除此之外,singleton与prototype还有以下区别:
scope值设置为singleton时,在加载spring配置文件的时候就会创建对象
scope值设置为prototype时,不会在加载spring配置文件的时候创建对象,而是在调用getBean方法时创建对象(每调一次就会创建一个全新的实例)
-
Bean的生命周期
①通过构造器创建bean实例(无参构造)
②为bean 的属性设置值和对其他bean的引用(调用set方法)
③调用bean 的初始化方法(需要进行配置初始化的方法)
④bean可以使用了(对象获取到了)
⑤当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
如果考虑bean的后置处理器,则bean完整的生命周期应该为七步
①通过构造器创建bean实例(无参构造)
②为bean 的属性设置值和对其他bean的引用(调用set方法)
③把bean实例传递给bean后置处理器的方法postProcessBeforeInitialization
④调用bean 的初始化方法(需要进行配置初始化的方法)
⑤把bean实例传递给bean后置处理器的方法postProcessAfterInitialization
⑥bean可以使用了(对象获取到了)
⑦当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
-
Bean管理
Bean管理指的是两个操作:①Spring创建对象;②Spring注入属性
Bean管理操作有两种方式:①基于xml配置文件方式实现;②基于注解方式实现
基于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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--Spring创建对象、注入属性 每个bean对应一个实例,id的值可以任取,class的值为包类名 set注入时需要对应类有属性的set方法 有参构造注入时对应类需要有相应的有参构造器 --> <!-- set注入属性 --> <bean id="user" class="com.dxy.spring5.User"> <property name="userName" value="zhangsan"></property> </bean> <!--p空间注入 对set注入的简化,先引入p空间,再注入属性--> <bean id="user1" class="com.dxy.spring5.User" p:userName="lisi"></bean> <!-- 有参构造注入属性--> <bean id="order" class="com.dxy.spring5.Orders"> <constructor-arg name="oname" value="box"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean> <!--外部bean 使用ref引入id为order的bean对象--> <bean id="user2" class="com.dxy.spring5.User"> <property name="userName" value="zhangsan"></property> <property name="orders" ref="order"></property> <!--级联赋值 User需要有属性orders的get方法 Orders中需要有set方法 以为这里是有set注入--> <property name="orders.oname" value="bottle"></property> </bean> <!--内部bean order为对象,使用内部bean为其赋值--> <bean id="user3" class="com.dxy.spring5.User"> <!--userName为普通属性 --> <property name="userName" value="zhangsan"></property> <property name="orders"> <bean id="order1" class="com.dxy.spring5.Orders"> <constructor-arg name="oname" value="computer"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean> </property> </bean> </beans>
集合的属性注入
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <bean id="stu" class="com.dxy.spring5.Stu"> <!-- 数组注入 --> <property name="courses"> <array> <value>数学</value> <value>语文</value> <value>历史</value> </array> </property> <!--list注入,且元素为对象 --> <property name="books" > <list> <ref bean="book1"></ref> <ref bean="book2"></ref> </list> </property> <!--可以引入util空间,对公共部分进行抽取--> <!-- <property name="books" ref="bookList"></property>--> <!--map注入--> <property name="numbers"> <map> <entry key="zhangsan" value="001"></entry> <entry key="lisi" value="002"></entry> </map> </property> </bean> <util:list id="bookList"> <ref bean="book1"></ref> <ref bean="book2"></ref> </util:list> <bean id="book1" class="com.dxy.spring5.Book"> <property name="bookName" value="Java并发编程"></property> </bean> <bean id="book2" class="com.dxy.spring5.Book"> <property name="bookName" value="深入理解Java虚拟机"></property> </bean> </beans>
用的实体类:
User类
package com.dxy.spring5; /** * Description: <br/> * date: 2022/11/15 18:42 * * @author dxy * @since JDK 1.8 */ public class User { private String userName; private Orders orders; public void setUserName(String name){ this.userName=name; } public void setOrders(Orders orders) { this.orders = orders; } public Orders getOrders() { return orders; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", orders=" + orders + '}'; } }
Orders类
package com.dxy.spring5; /** * Description: <br/> * date: 2022/11/15 19:24 * * @author dxy * @since JDK 1.8 */ public class Orders { private String oname; private String address; public Orders(String oname, String address) { this.oname = oname; this.address = address; } public void setOname(String oname) { this.oname = oname; } @Override public String toString() { return "Orders{" + "oname='" + oname + '\'' + ", address='" + address + '\'' + '}'; } }
Stu类
package com.dxy.spring5; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Description: <br/> * date: 2022/11/15 20:43 * * @author dxy * @since JDK 1.8 */ public class Stu { private String[] courses; private List<Book> books; private Map<String,Integer> numbers; public void setCourses(String[] courses) { this.courses = courses; } public void setBooks(List<Book> books) { this.books = books; } public void setNumbers(Map<String, Integer> numbers) { this.numbers = numbers; } @Override public String toString() { return "Stu{" + "courses=" + Arrays.toString(courses) + ", books=" + books+ ", numbers=" + numbers + '}'; } }
Book类
package com.dxy.spring5; /** * Description: <br/> * date: 2022/11/15 20:46 * * @author dxy * @since JDK 1.8 */ public class Book { private String bookName; public void setBookName(String bookName) { this.bookName = bookName; } @Override public String toString() { return "Book{" + "bookName='" + bookName + '\'' + '}'; } }
用于测试的类:
TestSpring5
package com.dxy.spring5.testdemo; import com.dxy.spring5.Orders; import com.dxy.spring5.Stu; import com.dxy.spring5.User; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Description: <br/> * date: 2022/11/15 18:31 * * @author dxy * @since JDK 1.8 */ public class TestSpring5 { @Test public void testStu(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); Stu stu = (Stu) context.getBean("stu"); System.out.println(stu); } @Test public void testAdd(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); User user = (User)context.getBean("user2"); Orders order = (Orders) context.getBean("order"); System.out.println(user.toString()); } }
以上我们都是进行手动装配的,即在bean中指定属性以及我们要设置的值,下面我们介绍一下Spring的自动装配
自动装配:根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
在xml中演示自动装配
基于byName的自动装配:(在bean中设置autowire=“byName”,此时会根据属性名称来寻找对应id的bean,并将属性值注入)
<!--这里userName为普通属性,我们用set手动注入,orders为对象,我们使用byName自动装配 注意:此时不许有一个bean的id值与orders属性名称完全一致,才能自动装配成功--> <bean id="user" class="com.dxy.spring5.User" autowire="byName"> <property name="userName" value="zhangsan"></property> </bean> <bean id="orders" class="com.dxy.spring5.Orders"> <constructor-arg name="oname" value="box"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
运行测试类中得到:
User{userName='zhangsan', orders=Orders{oname='box', address='China'}}
基于byType的自动装配:在bean中设置autowire=“byType”,此时会根据属性的类型来寻找对应class的bean,并将属性值注入)
<bean id="user" class="com.dxy.spring5.User" autowire="byType"> <property name="userName" value="zhangsan"></property> </bean> <bean id="orders" class="com.dxy.spring5.Orders"> <constructor-arg name="oname" value="box"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
我们可以得到相同的输出结果。但需要特别注意的是,如果有多个类型相同的bean,我们就不能使用byType来进行自动装配了!
基于注解的实现方式
使用注解的目的:简化xml配置
Spring针对Bean管理中创建对象提供注解:@Component @Service @Controoler @Repository
上面四个注解功能一致,都可以创建bean实例,只不过我们习惯在不同的层上使用不同的注解,是开发更加规范
我们先引入了context空间,然后开启组件扫描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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"> <!-- 基于注解实现--> <!--开启组件扫描 方式1.如果扫描多个包,多个包使用逗号隔开 方式2.扫描包的上层目录--> <context:component-scan base-package="com.dxy"></context:component-scan> </beans>
创建UserService类,使用注解实现Spring创建对象:
package com.dxy.spring5.service; import org.springframework.stereotype.Service; /** * Description: <br/> * date: 2022/11/16 10:30 * * @author dxy * @since JDK 1.8 */ //注解中value属性值可以不写,默认值是类名称首字母小写 @Service(value = "userService") //等价于<bean id="userService" class="com.dxy.spring5.testdemo.UserService"></bean> public class UserService { public void print(){ System.out.println("test..."); } }
@Test public void testUserService(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); UserService userService = (UserService) context.getBean("userService"); userService.print(); }
使用注解方式实现属性注入:
@Autowired:根据属性类型进行自动装配 (使用最多)
@Qualifier:根据属性名称进行注入,使用@Qualifier时需要和@Autowired一起使用
@Resource:既可以根据类型注入(使用@Resource),也可以根据名称注入 (使用@Resource(name=“xxx”))
@Value:可以注入普通属性
package com.dxy.spring5.service; import com.dxy.spring5.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * Description: <br/> * date: 2022/11/16 10:30 * * @author dxy * @since JDK 1.8 */ //注解中value属性值可以不写,默认值是类名称首字母小写 @Service(value = "userService") //等价于<bean id="userService" class="com.dxy.spring5.testdemo.UserService"></bean> public class UserService { //在service中注入dao对象,添加注入属性注解 @Autowired private UserDao userDao; public void print(){ System.out.println("test..."); userDao.print(); } }
UserDao
package com.dxy.spring5.dao; /** * Description: <br/> * date: 2022/11/16 10:47 * * @author dxy * @since JDK 1.8 */ public interface UserDao { public void print(); }
UserDaoImpl
package com.dxy.spring5.dao; import org.springframework.stereotype.Repository; /** * Description: <br/> * date: 2022/11/16 10:47 * * @author dxy * @since JDK 1.8 */ @Repository public class UserDaoImpl implements UserDao{ public void print() { System.out.println("dao test..."); } }
完全注解开发
创建配置类,替代xml配置文件
package com.dxy.spring5.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * Description: <br/> * date: 2022/11/16 11:03 * * @author dxy * @since JDK 1.8 */ @Configuration //作为配置类,替代xml配置文件 @ComponentScan(basePackages = {"com.dxy"}) public class SpringConfig { }
测试时也有点变化:
@Test public void testUserService1(){ //加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = (UserService) context.getBean("userService"); userService.print(); }