Spring IoC容器与Bean管理

Spring IoC容器与Bean管理,第1张

Spring IoC容器与Bean管理 Spring 初识 IoC控制反转
  • IoC控制反转,全称Inverse of Control,是一种设计理念
  • 由代理人创建与管理对象,消费者通过代理人来获得对象
  • IoC的目的是降低程序与程序之间直接耦合

解决什么问题?

对象直接饮用导致对象硬性关联,程序难以扩展。比如:顾客想要吃各种各样的苹果,就需要顾客取世界各地购买苹果,非常麻烦。

加入Ioc容器将对象统一管理,让对象关联变为弱耦合。就像上面的场景:如果出现了摊贩,那么顾客就不用跑到各地去买水果了,而是由摊贩购买好之后,顾客根据自己的需求购买响应的苹果。

 DI依赖注入
  • IoC是设计理念,是现代程序设计遵循的标准,是宏伟目标
  • DI(Dependency Injection) 是具体技术实现,是微观实现
  • DI在Java中利用反射技术是心啊对象注入(Injection)
Spring

含义 
  • Spring可以从狭义与广义两个角度看
  • 狭义角度的Spring是指Spring框架(Spring Fremework)
  • 广义角度Spring是指Spring生态体系
狭义的Spring框架
  • Spring框架是企业开发复杂性的恶一站式解决方案
  • SPring框架的核心是IoC容器与AOP面向切面编程
  • Spring IoC负责创建与管理系统对象,并在此基础上扩展功能
广义Spring生态体系

Spring IoC容器

IoC容器是Spring生态的地基,用于统一创建与管理对象依赖

 如上:Spring IoC容器将A的依赖B对象注入进来,使用者只需要从中提取就可以了。

Spring IoC容器职责
  • 对象的控制权交由第三方统一管理(IoC控制反转)
  • 利用Java反射技术实现运行时对象创建与关联(DI依赖注入)
  • 基于配置提高应用程序的可维护性与扩展性
Spring IoC初体验

eg: 比如三个孩子 Lily、Andy、Luna分别喜欢吃甜的、酸的、软的苹果。盘子里有三个苹果:红富士、青苹果、金帅。

那孩子们如何获得喜欢的苹果呢?

Apple.class

package com.imooc.spring.ioc.entity;

public class Apple {
    private String title;
    private String color;
    private String origin;

    public Apple() {
        //System.out.println("Apple对象已经创建, " + this);
    }

    public Apple(String title, String color, String origin) {
        this.title = title;
        this.color = color;
        this.origin = origin;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }
}

Child.class

package com.imooc.spring.ioc.entity;

public class Child {
    private String name;
    private Apple apple;

    public Child() {

    }

    public Child(String name, Apple apple) {
        this.name = name;
        this.apple = apple;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Apple getApple() {
        return apple;
    }

    public void setApple(Apple apple) {
        this.apple = apple;
    }

    public void eat() {
        System.out.println(name + "吃到了" + apple.getOrigin() + "种植的" + apple.getTitle());
    }
}

传统方式:

Application.class

public class Application {
    public static void main(String[] args) {
        Apple apple1 = new Apple("红富士", "红色", "欧洲");
        Apple apple2 = new Apple("青苹果", "绿色", "中亚");
        Apple apple3 = new Apple("金帅", "黄色", "中国");

        Child lily = new Child("莉莉", apple1);
        Child andy = new Child("安迪", apple2);
        Child luna = new Child("露娜", apple3);

        lily.eat();
        andy.eat();
        luna.eat();

    }
}

输出结果:

但是这样会有一个弊端,如果后面需要更改喜欢的苹果的时候,我们就需要在源代码这里修改。在开发过程中,往往源代码涉及到的内容较多,修改复杂且容易影响其他地方,很容易出错。

使用IoC容器方式:

在resources目录下创建applicationontext.xml文件:



    
    
        
        
        
    

    
        
        
        
    

    
        
        
        
    

    
        
        
        
    

    
        
        
    

    
        
        
    

    
        
        
    

SpringApplication.class

public class SpringApplication {
    public static void main(String[] args) {
        //创建Spring IoC容器,并根据配置文件在容器中实例化对象
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Apple sweetApple = context.getBean("sweetApple", Apple.class);
        System.out.println(sweetApple.getTitle());
        //从IoC容器中提取beanId=lily的对象
        Child lily = context.getBean("lily", Child.class);
        lily.eat();
        Child andy = context.getBean("andy", Child.class);
        andy.eat();
        Child luna = context.getBean("luna", Child.class);
        luna.eat();

    }
}

执行结果:

这样做的好处是,我们涉及到修改时,只需要修改applicationContent.xml中的内容即可.

 使用XML方式实现Spring IoC

 上面说的applicationContext.xml就是使用xml文件方式实现Spring IoC的一种。

实现方式:
  1. 基于构造方法对对象实例化
  2. 基于动态工厂实例化
  3. 基于工厂实例方法实例化
Spring框架组成模块

 ApplicationContext实现类
  • ClassPathXmlApplicationContext
  • AnnotationConfigApplicationContext
  • WebApplicationContext
基于构造方法对对象实例化 默认构造方法

在 applicationContext.xml文件:

    

Apple.class 中修改为:

public Apple() {
        System.out.println("Apple对象已创建," + this);
    }

打印输出:

带参构造方法

applicationContext.xml

    
        
        
        
        
    

    
        
        
        
        
    

 Apple.class

public Apple(String title, String origin, String color, Float price) {
        System.out.println("通过带参构造方法创建对象, " + this);
        this.title = title;
        this.color = color;
        this.origin = origin;
        this.price = price;
    }

打印输出:

基于工厂实例化对象 静态工厂
public class AppleStaticFactory {
    public static Apple createSweetApple(){
        //logger.info("")
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}

在applicationContext.xml中

    

测试:

 Apple apple4 = context.getBean("apple4", Apple.class);
        System.out.println(apple4.getTitle());

 输出:

工厂实例
public class AppleFactoryInstance {
    public Apple createSweetApple(){
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}

在applicationContext.xml中

 
    
    

然后测试输出即可.

从IoC容器中提取 Bean

方式一:
Apple apple = context.getBean("apple", Apple.class);

方式二:
Apple apple = (Apple)context.getBean("apple");

推荐使用方式一

id和name属性相同点
  • bean id 与 name 都是设置对象在IoC容器中唯一标识
  • 两者在同一个配置文件中都不允许出现重复
  • 两者允许在多个配置文件中出现重复,新对象覆盖旧对象
id和name属性不同点
  • id要求更为严格,一次只能定义一个对象标识(推荐)
  • name更为宽松,一次允许定义多个对象标识
  • tips:id与name的命名要求有意义,按驼峰命名书写

        
        
        
        
    
    
    
        
        
        
        
    

路径匹配表达式
 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

 加载多个配置文件:

String[] configLocations = new String[]{"classpath:applicationContext.xml","classpath:applicationContext-1.xml"};
        //初始化IoC容器并实例化对象
       ApplicationContext context = new ClassPathXmlApplicationContext(configLocations);
路径表达式

 对象依赖注入

依赖注入是指运行时将容器内对象利用反射赋给其他对象的 *** 作

  •  基于setter方法注入对象
  • 基于构造方法注入对象
基于setter方法注入对象

: 静态属性

: 动态属性


        
        
        
        
        
    

    
        
        
        
    
依赖注入的优势

举例:

applicationContext-dao.xml



    

    

applicationContext-serice.xml



    
        
        
    

BookDao

public interface BookDao {
    public void insert();
}

BookDaoImpl

public class BookDaoImpl implements BookDao {
    public void insert() {
        System.out.println("向MySQL Book表插入一条数据");
    }
}

BookService

public class BookService {
    private BookDao bookDao ;
    public void purchase(){
        System.out.println("正在执行图书采购业务方法");
        bookDao.insert();
    }

    public BookDao getBookDao() {
        return bookDao;
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

BookShopApplication

public class BookShopApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-*.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.purchase();
    }
}

输出:

优点: 比如数据库由MySQL迁移到Oracle.这时候只需要将BookDao这个Bean的class更改为Oracle即可. 注入集合对象  注入List

注入set

 

注入map 

注入Properties 

 Properties中的key和value只能是String类型

查看容器内对象 
//获取容器内所有beanId数组
        String[] beanNames = context.getBeanDefinitionNames();

for (String beanName:beanNames){
            System.out.println(beanName);
            System.out.println("类型:" + context.getBean(beanName).getClass().getName());
            System.out.println("内容:" + context.getBean(beanName));
        }
 Computer computer = context.getBean("com.imooc.spring.ioc.entity.Computer", Computer.class);

当有多个相同类的Bean时:

 
        
        
        
        
    

    
        
        
        
        
    
Computer computer1 = context.getBean("com.imooc.spring.ioc.entity.Computer#0", Computer.class);
Computer computer1 = context.getBean("com.imooc.spring.ioc.entity.Computer#1", Computer.class);

Bean对像的作用域及生命周期 bean scope属性
  • bean scope属性用于决定对象何时被创建与作用范围
  • bean scope配置将影响容器内对象的数量
  • 默认情况下bean会在IoC容器创建后自动实例化,全局唯一
scope用法

bean scope属性清单

 singleton的线程安全问题

 

singleton在容器是单例多线程执行,存在线程安全风险 

在单线程下:

当在多线程中:在A用户 *** 作了a.setNum(1)之后,在另一个线程,B用户 *** 了a.setNum(2) .这个时候,A用户打印a.num 就会出现 2,和A用户设置值不同.

prototype多例 

prototype在容器中多实例,占用更多资源,不存在线程安全问题 

singleton和prototype对比

bean生命周期

 

细节调整
  •  prototype使对象创建与init_method延迟至执行业务
  • prototype使对象不再受IoC容器管理,不再触发destroy-method
  • 延迟加载lazy-init属性可让对象创建与初始化延迟到执行代码阶段
生命周期在实战中的应用 singleton和prototype的初始化

singleton的初始化:

applicationContext.xml中:




    

    

在SpringApplication中添加:

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
     
    }
}

打印输出:

也就是说在IoC容器初始化的时候,为我们创建了bean. 

prototype初始化:

 applicationContext.xml中:




    

    

然后在SpringApplication中测试:

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        System.out.println("======IoC容器已初始化=======");
       UserDao userDao1 = context.getBean("userDao", UserDao.class);
}

输出为:

也就是说,在IoC容器创建的时候,并没有为我们初始化bean对像,而是在我们获取对象的时候,才初始化.singleton模式的初始化顺序跟书写顺序一致。

下面会初始化两个bean对象.



    
        
    

在多数情况下,Dao层,server层,control层都是单例的。

实现极简IoC容器

目录结构:

 Apple类

package com.imooc.spring.ioc.entity;

public class Apple {
    private String title;
    private String color;
    private String origin;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }
}

 然后在applicationContext.xml中添加bean的信息:



    
        
        
        
    

自定义记载配置文件的方法:

接口:ApplicationContext

package com.imooc.spring.ioc.context;

public interface ApplicationContext {
    public Object getBean(String beanId);
}

实现类ClassPathXmlApplicationContext 

public class ClassPathXmlApplicationContext implements ApplicationContext {
    private Map iocContainer = new HashMap();

    
    public ClassPathXmlApplicationContext() {
        try {
            String filePath = this.getClass().getResource("/applicationContext.xml").getPath();
            // 进行URL解码
            filePath = new URLDecoder().decode(filePath, "UTF-8");

            
            SAXReader reader = new SAXReader();
            document document = reader.read(new File(filePath));
            // 读取 "bean" 标签
            List beans = document.getRootElement().selectNodes("bean");

            for (Node node : beans) {
                Element ele = (Element) node;

                String id = ele.attributevalue("id");
                String className = ele.attributevalue("class");

                Class c = Class.forName(className);
                Object obj = c.newInstance();

                List properties = ele.selectNodes("property");
                for (Node p : properties) {
                    Element property = (Element) p;
                    String propName = property.attributevalue("name");
                    String propValue = property.attributevalue("value");

                    // set方法
                    String setMethodName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
                    System.out.println("准备执行" + setMethodName + "方法注入数据");
                    Method setMethod = c.getMethod(setMethodName, String.class);
                    setMethod.invoke(obj, propValue);//通过setter方法注入数据
                }
                iocContainer.put(id, obj);
            }
            System.out.println(iocContainer);
            System.out.println("IOC容器初始化完毕");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String beanId) {
        return iocContainer.get(beanId);
    }
}

然后进行测试:

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext();
        Apple apple = (Apple)context.getBean("sweetApple");
        System.out.println(apple);
    }
}

执行结果:

 

 

使用注解方式实现Spring IoC

基于注解的优势

  • 摆脱繁琐的XML形式的bean与依赖注入配置
  • 基于“声明式”的原则,更适合轻量级的现代企业应用
  • 让代码可读性变得更好,研发人员拥有更好的开发体验
三类注解:
  1. 组件类型注解 - 声明当前类的功能与职能
  2. 自动装配注解 - 根据属性特种自动注入对象
  3. 元数据注解 - 更细化的辅助IoC容器管理对象的注解
四种组件类型注解
  1. @Compponent  : 组件注解,通用注解,被该注解描述的类将被IoC容器管理并实例化
  2. @Controller  : 语义注解,说明当前类是MVC应用中的控制器类
  3. @Service. : 语义注解,说明当前类是Service业务服务类
  4. @Repository. : 语义注解,说明当前类是用于业务持久层,通常描述对应Dao类

  

两类自动装配注解
  1. 按类型装配
    1. @Autowired : 按容器内对象类型动态注入属性,由Spring机构提供
    2. @Inject : 基于JSR-330(Dependency Injection for Java)标准,其他同@Autowired,但不支持required属性
  2. 按名称装配
    1. @Named : 与@Inject配合使用,JSR-330规范,按属性名自动装配属性
    2. @Resource :基于JSR-330规范,优先按名称、再按类型智能匹配
元数据注解

@Value 的读取属性文件

@Value("com.imooc")
private String config;

就是相当于在初始化的时候,config的值为"com.inooc",主要用户加载配置文件中的数据: @Value("${config}")

使用Java Config方式实现Spring IoC

基于Java Config的优势

  • 完全摆脱XML的束缚,使用独立Java类管理对象与依赖
  • 注解配置相对分散,利用Java Config可对配置集中管理
  • 可以在编译时进行依赖检查,不容易出错
Java Config核心注解

Java Config初始化方式

 

 举例:

package com.imooc.spring.ioc;

import com.imooc.spring.ioc.controller.UserController;
import com.imooc.spring.ioc.dao.EmployeeDao;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.*;

@Configuration //当前类是一个配置类,用于替代applicationContext.xml
@ComponentScan(basePackages = "com.imooc")
public class Config {
    @Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        System.out.println("已创建" + userDao);
        return userDao;
    }

    @Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
    @Primary
    public UserDao userDao1(){
        UserDao userDao = new UserDao();
        System.out.println("已创建" + userDao);
        return userDao;
    }

    @Bean
    //先按name尝试注入,name不存在则按类型注入
    public UserService userService(UserDao udao , EmployeeDao employeeDao){
        UserService userService = new UserService();
        System.out.println("已创建" + userService);
        userService.setUserDao(udao);
        System.out.println("调用setUserDao:" + udao);
        userService.setEmployeeDao(employeeDao);
        return userService;
    }

    @Bean //
    @Scope("prototype")
    public UserController userController(UserService userService){
        UserController userController = new UserController();
        System.out.println("已创建" + userController);
        userController.setUserService(userService);
        System.out.println("调用setUserService:" + userService);
        return userController;
    }
}

测试:

public class SpringApplication {
    public static void main(String[] args) {
        //基于Java Config配置IoC容器的初始化
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        System.out.println("=========================");
        String[] ids = context.getBeanDefinitionNames();
        for(String id : ids){
            System.out.println(id + ":" + context.getBean(id));
        }
    }
}

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4695921.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-07
下一篇 2022-11-07

发表评论

登录后才能评论

评论列表(0条)

保存