目录
3.创建配置文件bean.properties,该文件中定义管理的对象
4.创建容器管理类,该类在类加载时读取配置文件,将配置文件中配置的对象全部创建并放入容器中。
3.编写xml配置文件,配置文件中配置需要Spring帮我们创建的对象
1.容器接口(BeanFactory、ApplicationContext)
2.ApplicationContext有以下三个常用实现类:
- public interface StudentDao {
- // 根据id查询学生
- Student findById(int id);
- }
-
- public class StudentDaoImpl implements StudentDao{
- @Override
- public Student findById(int id) {
- // 模拟从数据库查找出学生
- return new Student(1,"百战程序员","北 京");
- }
- }
-
- public class StudentService {
- public Student findStudentById(int id){
- // 此处就是调用者在创建对象
- StudentDao studentDao = new StudentDaoImpl();
- return studentDao.findById(1);
- }
- }
2.上述写法中有两个缺点:
(1)浪费资源: StudentService 调用方法时即会创建一个对象,如果不断调用方法则会创建大量StudentDao 对象。(2)代码耦合度高:假设随着开发,我们创建了 StudentDao 另一个更加完善的实现类StudentDaoImpl2 ,如果在 StudentService中想使用StudentDaoImpl2 ,则必须修改源码。
而 IOC思想是将创建对象的权利交给框架,框架会帮助我们创建对象,分配对象的使用,控制权由程序代码转移到了框架中,控制权发生了反转,这就是Spring的IOC 思想。而 IOC 思想可以完美的解决以上两个问题。
- public class Student {
- private int id;
- private String name;
- private String address;
- // 省略getter/setter/构造方法/tostring
- }
- public interface StudentDao {
- // 根据id查询学生
- Student findById(int id);
- }
- public class StudentDaoImpl implements StudentDao{
- @Override
- public Student findById(int id) {
- // 模拟从数据库查找出学生
- return new Student(1,"百战程序员","北京");
- }
- }
- public class StudentDaoImpl2 implements StudentDao{
- @Override
- public Student findById(int id) {
- // 模拟根据id查询学生
- System.out.println("新方法!!!");
- return new Student(1,"百战程序员","北京");
- }
- }
- //容器类,负责管理对象,在类加载时读取配置文件并创建对象
- public class Container {
- static Map
map = new HashMap(); -
- // 读取配置文件并创建对象
- static {
- InputStream is = Container.class.getClassLoader().getResourceAsStream("bean.properties");
- Properties properties = new Properties();
- try{
- properties.load(is);
- }catch (IOException e) {
- e.printStackTrace();
- }
-
- // 遍历配置文件中的所有对象
- Enumeration
- while (keys.hasMoreElements()){
- String key = keys.nextElement().toString();
- String value = properties.getProperty(key);
- // 创建对象
- try {
- Object o = Class.forName(value).newInstance();
- // 把对象放入集合当中
- map.put(key,o);
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
- // 从容器中获取对象
- public static Object getBean(String key){
- return map.get(key);
- }
- }
- public class StudentService {
- public Student findStudentById(int id){
- // 从容器中获取对象
- StudentDao studentDao = (StudentDao) Container.getBean("studentDao");
- System.out.println(studentDao.hashCode());
- return studentDao.findById(id);
- }
- }
- public class Test {
- public static void main(String[] args) {
- StudentService studentService = (StudentService) Container.getBean("studentService");
- System.out.println(studentService.findStudentById(1));
- System.out.println(studentService.findStudentById(1));
- }
- }
8.测试结论:
(1)StudentService从容器中每次拿到的都是同一个StudentDao对象,节约了资源。
studentDao=com.itbaizhan.dao.StudentDaoImpl2
(2)如果想使用StudentDaoImpl2 对象,只需要修改bean.properties的内容即可,无需修改Java 源码。(3)在IOC思想中,将创建好的对象保存到“容器中”
接下来我们使用Spring实现IOC,Spring内部也有一个容器用来管理对象。
- <dependencies>
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.3.13version>
- dependency>
-
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.12version>
- <scope>testscope>
- dependency>
- dependencies>
- public class Student {
- private int id;
- private String name;
- private String address;
- // 省略getter/setter/构造方法/tostring
- }
- public interface StudentDao {
- // 根据id查询学生
- Student findById(int id);
- }
- public class StudentDaoImpl implements StudentDao{
- @Override
- public Student findById(int id) {
- // 模拟从数据库查找出学生
- return new Student(1,"百战程序员","北京");
- }
- }
- <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">
-
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl">bean>
- beans>
- public class TestContainer {
- @Test
- public void t1(){
- // 创建Spring容器
- /**
- * ApplicationContext:容器接口
- * ClassPathXmlApplicationContext:容器接口实现类,该类可以从项目中读取配置文件,传入相对路径
- * FileSystemXmlApplicationContext:容器接口实现类,该类从磁盘中读取配置文件,传入绝对路径
- */
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
- // ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\txxiaoer\\Desktop\\SSM框架Demo\\springdemo\\spring_ioc1\\src\\main\\resources\\bean.xml");
- // 从容器中获取对象
- StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
- System.out.println(studentDao1.hashCode());
- System.out.println(studentDao2.hashCode());
- System.out.println(studentDao1.findById(1));
- }
- }
6.知识点整理:
(1)创建的studentDao1和studentDao2两个对象是同一个对象,并可以调用方法。
(2)Spring配置文件中,通过配置标签“
”配置容器中的对象 。(3)Spring配置文件中,
标签的 属性为配置“对象名”, 属性可以配置“对象类型”。
1.容器接口(BeanFactory、ApplicationContext)
(1)BeanFactory:BeanFactory是Spring容器中的顶层接口,它可以对Bean对象进行管理。
(2)ApplicationContext:ApplicationContext是BeanFactory的子接口。它除了继承BeanFactory的所有功能外,还添加了对国际化、资源访问、事件传播等方面的良好支持。(一般ApplicationContext用的比较多)
2.ApplicationContext有以下三个常用实现类:
(1) ClassPathXmlApplicationContext:该类可以从项目中读取配置文件,传入想对路径(2) FileSystemXmlApplicationContext:该类可以从磁盘中读取配置文件,传入绝对路径(3) AnnotationConfigApplicationContext:使用该类不读取配置文件,而是会读取注解
- public class TestContainer {
- @Test
- public void t1(){
- // 创建Spring容器
- /**
- * ApplicationContext:容器接口
- * ClassPathXmlApplicationContext:容器接口实现类,该类可以从项目中读取配置文件,传入相对路径
- * FileSystemXmlApplicationContext:容器接口实现类,该类从磁盘中读取配置文件,传入绝对路径
- */
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
- // ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\txxiaoer\\Desktop\\SSM框架Demo\\springdemo\\spring_ioc1\\src\\main\\resources\\bean.xml");
- // 从容器中获取对象
- StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
- System.out.println(studentDao1.hashCode());
- System.out.println(studentDao2.hashCode());
- System.out.println(studentDao1.findById(1));
- }
- }
- // 假如类没有空参构造方法,将无法完成bean的创建
- public class StudentDaoImpl implements StudentDao{
-
- // 设置一个有参的构造方法,默认去除了无参的构造方法
- public StudentDaoImpl(int a){}
-
- @Override
- public Student findById(int id) {
- // 模拟根据id查询学生
- return new Student(1,"百战程序员","北 京");
- }
- }
如上,IDEA会报红提示没有无参构造方法,不能通过编译。
- public class StudentDaoFactory {
- public StudentDao getStudentDao(){
-
- //return new StudentDaoImpl(1):返回StudentDaoImpl对象,利用有参构造方法
- return new StudentDaoImpl(1);
- }
- }
(2)在配置文件中配置创建bean的方式为工厂方式。
-
- <bean id="studentDaoFactory" class="com.itbaizhan.dao.StudentDaoFactory">bean>
-
- <bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao">bean>
- public class StudentDaoFactory2 {
- public static StudentDao getStudentDao(){
- return new StudentDaoImpl(1);
- }
- }
(2)在配置文件中配置创建bean的方式为工厂静态方法。
-
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoFactory2" factory-method="getStudentDao">bean>
4.知识点整理:
(1)Spring不可以使用“有参构造方法”创建bean对象;可以使用“空参构造方法”、“工厂方法”、“静态工厂方法”创建bean对象。
(2)Spring调用工厂类的普通方法创建bean对象需要配置“工厂对象和被创建的对象”
Spring通过配置
单例,默认策略。整个项目只会创建一个对象,通过
lazy-init="false"(默认) :立即创建,在容器启动时会创建配置文件中的所有Bean 对象。lazy-init="true" :延迟创建,第一次使用 Bean 对象时才会创建。
(1)配置单例策略:
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton" lazy-init="false" >bean>
(2)测试单例策略:
- public class StudentDaoImpl2 implements StudentDao{
- public StudentDaoImpl2(){
- System.out.println("创建了StudentDao!");
- }
- }
- @Test
- public void t2(){
- // 创建Spring容器
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
- // StudentDao studentDao = new StudentDaoImpl2();等同于下面的创建方式,下面的创建方式是从Spring容器中拿到对象
- // 从容器中获取对象
- StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao");
- System.out.println(studentDao1.hashCode());
- System.out.println(studentDao2.hashCode());
- System.out.println(studentDao3.hashCode());
- }
(3)测试结果:
(4)结论:
多例,每次从容器中获取时都会创建新的对象。
(1)配置多例策略:
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="prototype" >bean>
(2)测试多例策略:
- @Test
- public void t2(){
- // 创建Spring容器
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
- // StudentDao studentDao = new StudentDaoImpl2();等同于下面的创建方式,下面的创建方式是从Spring容器中拿到对象
- // 从容器中获取对象
- StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
- StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao");
- System.out.println(studentDao1.hashCode());
- System.out.println(studentDao2.hashCode());
- System.out.println(studentDao3.hashCode());
- }
(3)测试结果:
(4)结论:
每次从容器中获取时都会创建新的对象。
3.request :每次请求创建一个对象,只在 web 环境有效。4.session :每次会话创建一个对象,只在 web 环境有效。5.gloabal-session :一次集群环境的会话创建一个对象,只在 web环境有效。
对象的创建策略不同,销毁时机也不同:
1.singleton :对象随着容器的销毁而销毁。(因为整个项目中只有一个对象,并且存在容器当中,如果对象被随意地销毁的话,再想获取对象就获取不到了,所以正常情况下单例的对象不会销毁,只会随着容器的销毁而销毁)2.prototype :使用 JAVA 垃圾回收机制销毁对象。(每次获取对象时都会创建新的对象,不存在容器当中,不然容器中要存很多这种对象。所以多例对象也不由Spring来销毁,因为太多了,所以直接使用Java的垃圾回收机制销毁对象,什么时候对象没用了就将其销毁。)3.request :当处理请求结束, bean 实例将被销毁。4.session :当 HTTP Session 最终被废弃的时候, bean 也会被销毁掉。5.gloabal-session :集群环境下的 session 销毁, bean 实例也将被销毁。
Bean对象的生命周期包含创建——使用——销毁,Spring可以配置Bean对象在创建和销毁时自动执行的方法:
- public class StudentDaoImpl2 implements StudentDao{
-
- //创建时自动执行的方法
- public void init(){
- System.out.println("创建StudentDao!!!");
- }
-
- //销毁时自动执行的方法
- public void destroy(){
- System.out.println("销毁StudentDao");
- }
-
- // 构造方法在创建对象的时候被调用
- public StudentDaoImpl2(){
- System.out.println("创建了StudentDao!");
- }
- }
-
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton"
- init-method="init" destroy-method="destroy" >bean>
- @Test
- public void t3(){
- // 创建Spring容器
- ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
- // 销毁Spring容器,
- // ClassPathXmlApplicationContext才有销毁容器的方法,ApplicationContext引用没有销毁容器的方法
- ac.close();
- }
5.知识点整理:
(1)在Spring中,
中配置“init-method ”属性可以定义创建对象时执行的方法,一般放入一些资源 (2)在Spring中,
中配置“destroy-method ”属性可以定义销毁对象时执行的方法,一般放入一些资源关闭
- <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
-
- <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
(2)获取对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
(1)配置文件
<bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
(2)获取对象
StudentDao studentDao2 = ac.getBean(StudentDao.class);
- <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
- <bean name="studentDao1" class="com.itbaizhan.dao.StudentDaoImpl">bean>
(2)获取对象
- //传入id/name属性值+类型
- StudentDao studentDao2 = ac.getBean("studentDao",StudentDao.class);
(1)配置文件:
- <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
- <bean name="studentDao1" class="com.itbaizhan.dao.StudentDaoImpl">bean>
(2)测试方法中获取对象
- @Test
- public void t4(){
- // 创建Spring容器
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
- // 第一种方式:根据name获取对象(需要进行类型强转)
- StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
- System.out.println(studentDao1);
- // 第二种方式:根据类型获取对象(不需要进行类型强转,因为传入的就是确切的类型)
- // 这种方式有个问题:整个容器中同类型对象有可能有多个,这种情况就很难分辨了。
- // 如:StudentDao接口有多个实现类,这时就无法分辨具体要创建哪个实现类的对象了
- StudentDao studentDao2 = ac.getBean(StudentDao.class);
- System.out.println(studentDao2);
- // 第三种方式:根据name/id+类型获取对象
- StudentDao studentDao3 = ac.getBean("studentDao",StudentDao.class);
- System.out.println(studentDao3);
-
- }