欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

初识Spring与Spring-IOC容器

程序员文章站 2022-07-12 13:39:12
...

一、Spring框架的概念

  1. Spring是一款企业级级开发的开源框架
  2. Spring提供IOC容器和AOP框架:
    • IOC解决了对象的创建以及对象管理,同时解决对象与对象之间的依赖
    • AOP解决的是一些公共业务逻辑(事物管理、日志收集等)
  3. Spring是一个“粘合剂”
    • 采用Spring去集成第三方框架或者组件(MyBatis/Redis/RabbitMQ/Hibernate…),集成原理还是基于IOC容器

二、Spring框架组件

初识Spring与Spring-IOC容器

  • 数据访问相关组件(Data Access):
    • Spring JDBC/Spring ORM/Spring OXM
  • Web应用相关组件(Web):
    • Spring Web、Spring MVC(重点)、Spring Websocket…
  • AOP(面向切面编程)、Aspects(切面)
  • 核心容器组件(Core Container):
    • Beans(Bean相关内容:BeanFactory)
    • Context(Spring配置的上下文 ApplicationContext)
    • Core
    • SpEL(Spring的EL表达式)
  • 测试组件(Spring-test):
    • mock(模拟)测试、Random Port(随机端口测试)、TestContext Framework(测试上下文框架)

三、集成Spring框架

  • 方法:在项目的pom中添加spring-context依赖,通过Maven的依赖传递将Spring其他核心组件的jar包也一起依赖进项目(aop/beans/core/spel),Spring开源的源代码存放在GitHub上

  • 代码:

    <!--依赖Spring相关jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    

四、BeanFactory和ApplicationContext

初识Spring与Spring-IOC容器

​ ApplicationContext是BeanFactory的子类,它除了拥有BeanFactory的功能外,还包含有国际化、资源加载、事件传递等等

五、Bean实例化的模拟

思路

  1. 读取xml,解析xml配置(dom4j解析)
  2. 获取<bean id="" class="" />标签,将bean标签的属性封装到对象中
  3. 将解析后的<bean>标签的对象存入集合中
  4. 遍历解析后的Bean标签(id, class)集合,同时利用反射机制创建对象,并将id和对象存入map中
  5. 编写一个getBean(id)的方法获取bean实例化后的对象

代码实现

  1. 编写一个BeanFactory,暴露一个getBean接口

    /**
     * 模拟Bean工程提供获取Bean的方法
     */
    public interface BeanFactory {
        public Object getBean(String id);
    }
    
  2. 编写BeanFactory的实现类MyClassPathXmlApplicationContext()

    /**
     * 模拟的是一个ApplicationContext上下文容器
     */
    public class MyClassPathXmlApplicationContext implements BeanFactory {
        private Map<String, Object> beans = new HashMap<>();// 实例化后的对象放入map
        private List<MyBean> myBeans;// 存放已读取bean 配置信息
    }
    
  3. 编写一个构造器,传入xml配置文件,调用解析方法进行xml读取、解析,调用实例化对象的方法去实例化对象(应用反射机制)

    // 构造器,传入xml配置文件名
    public MyClassPathXmlApplicationContext(String fileName) {
        this.readBeansXml(fileName);// 读取并解析配置文件
        this.insranceBeans(); // 实例化bean
    }
    
  4. 读取、解析xml配置文件,将bean标签内容存入集合的MyBean对象中(bean metadata)

    /**
     * 读取 并解析xml 配置文件
     * @param fileName
     */
    private void readBeansXml(String fileName) {
        URL url=this.getClass().getClassLoader().getResource(fileName);
        // 判断xml配置文件是否存在
        if (url == null) {
            System.out.println("资源文件未找到!");
            return;
        }
        // 解析xml配置文件
        SAXReader saxReader=new SAXReader();// 创建saxReader 对象
        Document doc= null;
        try {
            doc = saxReader.read(url);
        } catch (DocumentException e) {
            e.printStackTrace(); // 打印异常的堆栈信息
            return;
        }
        Map<String, String> map = new HashMap<>(); // 一定要加泛型
        map.put("my", "http://www.springframework.org/schema/beans");  //将xml命名空间放入map
        XPath xPath= doc.createXPath("//my:bean");//定义查找路径
        xPath.setNamespaceURIs(map);
        List<Element> elements = xPath.selectNodes(doc);
        if (CollectionUtils.isEmpty(elements)) {
            return;
        }
        myBeans = new ArrayList<>();
        for(Element element : elements){
            //System.out.println(element.attributeValue("id")+element.attributeValue("class"));
            MyBean myBean = new MyBean(element.attributeValue("id"), element.attributeValue("class"));
            myBeans.add(mytBean);
        }
    }
    
  5. 遍历bean的元数据信息(MyBean集合)信息,利用反射实例化对象,同时存储到map中

    /**
     * 实例化bean 对象
     */
    private void insranceBeans() {
        // 先判断<bean>标签集合是否有值
        if (CollectionUtils.isEmpty(myBeans)) {
            return;
        }
        // 遍历<bean>标签,同时利用反射机制生成对象,并将对象存入map中,其中key为bean对象的id,value是对象
        for(MyBean myBean: myBeans){
            try {
                beans.put(myBean.getId(), Class.forName(myBean.getClazz()).newInstance());
            } catch (Exception e) {
                continue;
            }
        }
    }
    
  6. 重写getBean方法,从map中返回bean的实例化后的对象

    /**
     * 根据id 名获取bean
     */
    @Override
    public Object getBean(String id) {
        return beans.get(id);
    }
    

六、IOC容器加载多个配置文件

  • 第一种:启动ApplicationContext上下文容器时,允许加载多个配置文件

  • 第二种:用某一个公共的配置文件,把其他配置文件通过<import resource="" />导入进来

    <bean id="helloService" class="com.myspring.service.HelloService" />
    <import resource="controller.xml" />
    <import resource="service.xml" />
    <import resource="dao.xml" />
    
    public class AppTest {
    
        @Test
        public void testMultiXml() {
            // 启动IOC容器(加载配置文件生成ApplicationContext)
            ApplicationContext ac = new ClassPathXmlApplicationContext("controller.xml", "dao.xml", "service.xml");
            // 获取Bean对象
            UserController userController = ac.getBean("userController", UserController.class);
            UserService userService = ac.getBean("userService", UserService.class);
            UserDao userDao = ac.getBean("userDao", UserDao.class);
            // 调用Bean方法
            userController.show();
            userService.add();
            userDao.save();
        }
    
        @Test
        public void testSingleXml() {
            // 启动IOC容器(加载配置文件生成ApplicationContext)
            ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
            // 获取Bean对象
            UserController userController = ac.getBean("userController", UserController.class);
            UserService userService = ac.getBean("userService", UserService.class);
            UserDao userDao = ac.getBean("userDao", UserDao.class);
            // 调用Bean方法
            userController.show();
            userService.add();
            userDao.save();
        }
    }
    

七、Bean的实例化方式

1. 构造器实例化

  • 通过默认构造器创建 空构造方法必须存在 否则创建失败(简单|重要)
<bean id="userServiceImpl" class="com.myspring.service.impl.UserServiceImpl" ></bean>

2. 静态工厂实例化

  • 编写一个工厂类,同时编写一个静态方法,该方法返回需要创建的对象
/**
 * 静态工厂实例化Bean
 */
public class StaticFactory {
   /**
    * 编写一个静态方法,同时返回一个实例化对象
    * @return
    */
    public static UserService createUserService(){
      return new UserService();
   } 
}
<?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">

    <!--静态工厂实例化Bean,配置的class指向工厂类,添加factory-method属性指向创建对象的静态方法-->
    <bean id="userService" class="com.myspring.factory.StaticFactory" factory-method="createUserService" />
    
</beans>

3. 实例化工厂方法

  • 编写一个工厂类,同时编写一个非静态方法,该方法返回需要创建的对象,在配置时,需要先实例化工厂bean
/**
 * 实例化工厂创建Bean对象
 */
public class InstanceFactory {
    /**
     * 此方法不需要静态化
     * @return
     */
    public UserService createUserService() {
        return new UserService();
    }
}
<?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">

    <!--通过实例化工厂类创建Bean对象-->
    <bean id="instanceFactory" class="com.myspring.factory.InstanceFactory"  /> <!--工厂bean-->

    <!--实例化UserService Bean-->
    <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService" />
    
</beans>

八、对象依赖与注入

IOC本质问题 ***

  • IOC:控制反转,就是由原来主动创建对象改成交给Spring创建对象

    // 主动创建
    public class UserController {
    	private UserService userService = new UserService();
    }
    
    // 交由第三方创建再传入
    public class UserController {
    	private UserService userService ;
    	public UserController(UserService userService) {
    		this.userService = userService;
    	}
    }
    

依赖注入(DI) ***

  • 依赖:对象与对象之间的关系,注入由第三方(Spring)在实例化对象时进行注入。
    • 因此DI是IOC的另外一种表达方式,换个说法:IOC是DI的理论基础
  • 注入:可以简单理解为赋值

依赖注入的方式 ***

1. 构造器注入 **

​ 通过构造器传参,将对象注入给对应的属性

  • 注入对象

    • 例子:

      // 编写一个NoteDao,里面有个findNote()方法
      public class NoteDao {
          public void find() {
              System.out.println("查询笔记。。。");
          }
      }
      
      // 编写一个NoteService,包含NoteDao属性,编写一个构造器,把NoteDao注入进来
      public class NoteService {
          private NoteDao noteDao;
          public NoteService() {}
          // 创建一个构造器方法
          public NoteService(NoteDao noteDao) {
              System.out.println("构造器注入。。。");
              this.noteDao = noteDao;
          }
          /**
           * 查找笔记
           */
          public void findNote() {
              noteDao.find();
          }
      }
      
      <!--实例化NoteDao对象-->
      <bean id="noteDao" class="com.myspring.dao.NoteDao"/>
      
      <!--实例化NoteService-->
      <bean id="noteService" class="com.myspring.service.NoteService">
          <!--给NoteService(NoteDao noteDao)构造器注入NoteDao的对象-->
          <constructor-arg name="noteDao" ref="noteDao"/>
      </bean>
      
      /**
       * 测试
       */
      public class NoteServiceTest {
          private ApplicationContext ac;
      
          @Before
          public void initAc() {
              // 启动IOC容器
              ac = new ClassPathXmlApplicationContext("application-note.xml");
          }
      
          @Test
          public void testFindNote() {
              // 获取UserService对象
              NoteService noteService = ac.getBean("noteService", NoteService.class);
              noteService.findNote();
          }
      }
      
      
  • 注入基本类型

    public class Note {
        private Integer id;
        private String content;
        private Integer userId;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public Note() {
        }
    
        public Note(Integer id, String content, Integer userId) {
            this.id = id;
            this.content = content;
            this.userId = userId;
        }
    }
    
    <!--实例化Note对象-->
    <bean id="note" class="com.myspring.model.Note">
        <!--其中name为参数名,value参数值,index为参数下标-->
        <constructor-arg name="id" value="1" index="0" />
        <constructor-arg name="userId" value="123" index="2"/>
        <constructor-arg name="content" value="今天是个好天气" index="1"/>
    </bean>
    
  • 简化配置:通过引入c标签,简化配置构造器注入

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:c="http://www.springframework.org/schema/c"
           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">
    
        <!--实例化NoteDao对象-->
        <bean id="noteDao" class="com.myspring.dao.NoteDao"/>
    
        <!--实例化NoteService-->
        <bean id="noteService" class="com.myspring.service.NoteService" c:noteDao-ref="noteDao" />
            <!--给NoteService(NoteDao noteDao)构造器注入NoteDao的对象
            <constructor-arg name="noteDao" ref="noteDao"/>
        </bean>-->
    
        <!--实例化Note对象-->
        <bean id="note" class="com.myspring.model.Note" c:id="1" c:userId="123"
              c:content="今天是个好天气" />
            <!--其中name为参数名,value参数值,index为参数下标
            <constructor-arg name="id" value="1" index="0" />
            <constructor-arg name="userId" value="123" index="2"/>
            <constructor-arg name="content" value="今天是个好天气" index="1"/>
        </bean>-->
    
    </beans>
    

2. setter注入 ***

​ 就是编写一个属性的set方法,然后进行依赖注入

  • 注入对象

    public class UserDao {
    
        public void save() {
            System.out.println("Save User...");
        }
    
    }
    
    public class UserService {
    
        private UserDao userDao;
    
        /**
         * 编写一个添加用户的方法
         */
        public void addUser() {
            userDao.save();
        }
    
        /**
         * UserDao的set方法
         * @param userDao
         */
        public void setUserDao(UserDao userDao) {
            System.out.println("setter注入UserDao对象");
            this.userDao = userDao;
        }
    }
    
    <?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">
    
        <!--配置bean 其中id唯一标记这个bean,class属性代表是这个bean的类-->
        <bean id="userService" class="com.myspring.service.UserService" >
            <!--依赖注入UserDao对象-->
            <property name="userDao" ref="userDao" />
        </bean>
    
        <!--实例化UserDao对象-->
        <bean id="userDao" class="com.myspring.dao.UserDao" />
    
    </beans>
    
    @Test
    public void testAddUser() {
        // 启动IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        // 获取UserService对象
        UserService userService = ac.getBean("userService", UserService.class);
        // 调用addUser()方法
        userService.addUser();
    }
    
  • 注入基本类型

    package com.myspring.model;
    
    public class User {
        private Integer id;
        private String userName;
        private String password;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
    }
    
    <!--实例化User对象-->
    <bean id="user" class="com.myspring.model.User">
        <!--基本类型注入-->
        <property name="id" value="2" />
        <property name="userName" value="banzhang"/>
        <property name="password" value="1234555"/>
    </bean>
    
    @Test
    public void testAddUser2() {
        // 启动IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        // 获取UserService对象
        UserService userService = ac.getBean("userService", UserService.class);
        // 调用addUser()方法
        User user = ac.getBean("user", User.class);
        userService.addUser(user);
    }
    
  • 简化set注入配置:通过引入p标签简化

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:p="http://www.springframework.org/schema/p"
                   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唯一标记这个bean,class属性代表是这个bean的类-->
        <bean id="userService" class="com.myspring.service.UserService" p:userDao-ref="userDao" />
            <!--依赖注入UserDao对象
            <property name="userDao" ref="userDao" />
        </bean>-->
    
        <!--实例化UserDao对象-->
        <bean id="userDao" class="com.myspring.dao.UserDao" />
    
        <!--实例化User对象-->
        <bean id="user" class="com.myspring.model.User" p:id="2"
              p:password="122345" p:userName="banzhang" />
            <!--基本类型注入
            <property name="id" value="2" />
            <property name="userName" value="banzhang"/>
            <property name="password" value="1234555"/>
        </bean>-->
    
    </beans>
    

3. 静态工厂注入(不常用)

4. 实例化工厂注入(不常用)

九、List、Set、Map、Properties、空字符、null注入

看代码:

public class User {
    private Integer id;
    private String userName;
    private String password;

    private List<String> noddles;
    private Set<String> cookies;
    private Map<String, String> views;
    private Properties games;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<String> getNoddles() {
        return noddles;
    }

    public void setNoddles(List<String> noddles) {
        this.noddles = noddles;
    }

    public Set<String> getCookies() {
        return cookies;
    }

    public void setCookies(Set<String> cookies) {
        this.cookies = cookies;
    }

    public Map<String, String> getViews() {
        return views;
    }

    public void setViews(Map<String, String> views) {
        this.views = views;
    }

    public Properties getGames() {
        return games;
    }

    public void setGames(Properties games) {
        this.games = games;
    }
}
<bean id="user" class="com.myspring.model.User">
    <property name="id" value="0" />
    <property name="userName" value=""/> <!--空字符串-->
    <property name="password"> <!--null值-->
        <null />
    </property>
    <property name="noddles">
        <list>
            <value>河南烩面</value>
            <value>臊子面</value>
            <value>担担面</value>
            <value>兰州拉面</value>
        </list>
    </property>

    <property name="cookies">
        <set>
            <value>曲奇</value>
            <value>奥利奥</value>
            <value>闲趣</value>
        </set>
    </property>

    <property name="views">
        <map>
            <entry>
                <key><value>上海</value></key>
                <value>城隍庙</value>
            </entry>
            <entry>
                <key><value>北京</value></key>
                <value>四合院</value>
            </entry>
            <entry>
                <key><value>西安</value></key>
                <value>大雁塔</value>
            </entry>
        </map>
    </property>

    <property name="games">
        <props>
            <prop key="暴雪">暗黑破坏神</prop>
            <prop key="育碧">刺客信条</prop>
            <prop key="任天堂">马里奥制造</prop>
        </props>
    </property>

</bean>

十、自动装配(装载)

概念

  • 利用注解方式自动装配bean的依赖关系,达到简化依赖注入的操作
  • 使用时必须要开启注解驱动<context:annotation-config />

1. @Autowired

  • 它是Spring框架注解

  • 根据类型查询bean(byType)

  • 默认情况下依赖的bean必须要存在,否则会提示找不到该bean异常

    • 因为required属性控制了依赖的bean是否必须存在,默认是@Autowired(required=true)
  • @Autowired既可以设置在属性上,也可以设置在setter方法上

    public class UserController {
    
        @Autowired // 推荐,其中required属性代表bean是否必须存在,默认为true
        private UserService userService;
    
        public void show(User user) {
            System.out.println("调用控制层方法。。");
            userService.addUser(user);
        }
    
        // @Autowired // 不推荐
        // public void setUserService(UserService userService) {
        // 		this.userService = userService;
        // }
    }
    
    <!--开启自动装载注入-->
    <context:annotation-config />
    
    <bean name="userController" class="com.myspring.controller.UserController" />
    

2. @Resource

  • 它是Jdk的注解
  • 如果没有指定name(@Resource),先根据name(byName)查询,查询不到再根据类型(byType)匹配,比如
@Resource
private UserService userService;
  • 如果指定name属性,那么只会根据name进行查询(byName)
@Resource(name = "userService")
private UserService userService;

3. @Qualifier

  • @Resource(name=“userService”) = @Autowired + @Qualifier(“userService”)

十一、Spring IOC 扫描器——扫描 + 注解实例化Bean

1. 添加扫描配置

  • 在配置文件中添加:<context:component-scan base-package=" "/>进行扫描配置,其中base-package代表扫描的包路径

  • 不需要开启注解驱动<context:annotation-config />

    <?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--扫描注解,其中base-package属性代表扫描的package-->
        <context:component-scan base-package="com.myspring"/>
    </beans>
    

2. 注解标记实例类

  • @Component注解,如果不能区分这个类是属于那一层,那么当做一个普通组件交给Spring管理
  • @Controller注解,标记这个类是控制层
  • @Service注解,标记这个类是业务逻辑层
  • @Repository注解,标记这个类是数据访问逻辑层

十二、Spring配置Bean的方式

第一种:声明式配置

  • 通过在xml中配置bean标签进行实例化
  • 包括构 造器实例化、静态工厂和实例化工厂实例化

第二种:Spring IOC 扫描器

  • 扫描 + 注解,通过component-scan进行扫描包 + 组件(@Component)注解

第三种:通过注解方式配置Bean

  • @Configuration(类注解) + @Bean(方法)

  • 首先要也要有扫描@Configuration注解的类

  • 通过@Configuration标记的类相当于<beans ></beans>

  • @Bean相当于<bean id=“默认是方法名称” class=“返回类型” />

  • 举例:

    • 第一步:编写HelloController和HelloService类

      @Controller // 标记这个controller交给Spring管理
      public class UserController {
      
          @Autowired
          private UserService userService;
      
          public void show(User user) {
              System.out.println("调用控制层方法。。");
              userService.addUser(user);
          }
      }
      
      public class HelloService {
          public void say() {
              System.out.println("Hello Java Config....");
          }
      }
      
    • 第二步:配置一个HelloService的Bean

      @Configuration
      public class HelloServiceConfiguration {
      
          @Bean// 默认使用方法名作为bean的名称
          public HelloService helloService() {
              return new HelloService();
          }
      }
      
    • 第三步:添加扫描

      <?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
      
          <!--扫描注解,其中base-package属性代表扫描的package-->
          <context:component-scan base-package="com.myspring"/>
      </beans>
      

十三、Spring Bean 的作用域

作用域分类 名称 说明
单例 singleton 在SpringIOC容器中,Bean创建好之后会存在
单例Bean的缓存池中,获取Bean时从池中直接
拿取即可,性能高
原型 prototype 不会缓存,每次获取每次创建
Web应用 request、session、application、webSocket 必须要有 web 环境支持,并配置相应的容器
监听器或拦截器从而能应用这些作用域
  • 默认是单例 singleton

    • 即在SpringIOC容器中,Bean创建好之后会存在单例Bean的缓存池中,获取Bean时从池中直接拿取即可,性能高

      初识Spring与Spring-IOC容器

    • 注意:lazy-init 是懒加载,如果等于 true 时作用是指 Spring 容器启动的时候不会去实例化这个 bean,而是在程序调用时才去实例化。默认是 false 即 Spring 容器启动时实例化

    • 容器在启动的情况下就实例化所有 singleton 的 bean 对象 的好处:

      • 提前发现潜在的配置问题
      • Bean 对象存在于缓存中,使用时不用再去实例化 bean,加快程序运行效率
    • 一般来说对于无状态或状态不可改变的 对象适合使用单例模式

  • 原型 prototype,不会缓存,每次获取每次创建

    • 通过 scope=“prototype” 设置 bean 的类型 ,每次向 Spring 容器请求获取Bean 都返回一个全新的 Bean,相对于 “singleton” 来说就是不缓存 Bean,每次都是一个根据 Bean 定义创建的全新 Bean

      初识Spring与Spring-IOC容器

  • Web应用中存在作用域:request、session、application、webSocket

十四、Bean 的生命周期

(1)定义

  • 声明式配置
  • 扫描器 + 注解
  • 编程式(java config)@Configuration + @Bean

(2)初始化

  • 启动容器,生成上下文对象ApplicationContext

    • (FileSystemXmlApplicationContex、ClassPathXmlApplicationContex、AnnotationConfigApplicationContext)
  • 可以再通过 init-method 添加初始化代码

(3)使用

  • 调用BeanFactory中的getBean方法获取Bean,再调用Bean的方法

(4)销毁

  • 可以通过 destroy-method 添加销毁代码
  • 可以通过调用AbstractApplicationContext的close()方法进行销毁

Bean初始化的时序图

初识Spring与Spring-IOC容器

package com.myspring.model;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;

public class User implements InitializingBean, BeanNameAware {

   private String userId;
   private String userName;
   private String email;
   private String hometown;
   private String job;
   private String beanName;
   
   public User() {
      System.out.println("**************第一步: 调用Bean的默认构造方法**************");
      this.userId = "bean1: myspring001";
      System.out.println("bean1的值: " + this);
   }
   
   @Override
   public void afterPropertiesSet() throws Exception {
      System.out.println("bean2的值: " + this);
      System.out.println("**************第三步: 检查Bean是否实现了InitializingBean接口**************");
      this.userName = "bean3: 隔壁老王";
      this.email = "bean3: aaa@qq.com";
      System.out.println("bean3的值: " + this);
   }
   
   public void init() {
      System.out.println("**************第四步: 检查配置文件中是否指定了init-method方法**************");
      this.hometown = "bean3: 上海";
      System.out.println("bean4: " + this);
   }
   
   public void destroy () {
      System.out.println("**************服务停止**************");
   }
   
   public String getUserId() {
      return userId;
   }
   public void setUserId(String userId) {
      this.userId = userId;
   }
   public String getUserName() {
      return userName;
   }
   public void setUserName(String userName) {
      this.userName = userName;
      System.out.println("**************第二步: Bean的配置文件中是否注入了bean的属性值**************");
      System.out.println("bean11的值: " + this);
   }
   public String getEmail() {
      return email;
   }
   public void setEmail(String email) {
      this.email = email;
   }
   public String getHometown() {
      return hometown;
   }
   public void setHometown(String hometown) {
      this.hometown = hometown;
   }
   public String getBeanName() {
      return beanName;
   }

   @Override
   public void setBeanName(String beanName) {
      System.out.println("**********设置bean的名称**********");  
      this.beanName = "userBeanName";
   }
   public String getJob() {
      return job;
   }
   public void setJob(String job) {
      this.job = job;
   }
   @Override
   public String toString() {
      return "User [userId=" + userId + ", userName=" + userName + ", email="
            + email + ", hometown=" + hometown + ", job=" + job
            + ", beanName=" + beanName + "]";
   }
}
<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--声明式配置
    <bean id="userService" class="com.myspring.service.UserService" destroy-method="destroy" init-method="initUserService"  />
-->
    <bean id="user" class="com.myspring.model.User" init-method="init" destroy-method="destroy">
        <property name="userName" value="abcd" />
    </bean>

</beans>