Spring_Ioc容器

Posted dhome

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring_Ioc容器相关的知识,希望对你有一定的参考价值。

一、Spring框架概念

  spring 是众多开源 java 项目中的一员,基于分层的 javaEE 应用一站式轻量级开源框架,主要核心是 Ioc(控制反转/依赖注入) 与 Aop(面向切面)两大技术,实现项目在开发过程中的轻松解耦, 提高项目的开发效率。

  在项目中引入spring的好处:

    ·降低组件之间的耦合度,实现软件各层之间的解耦

    ·可以使用容器提供的众多服务,如:事务管理服务、消息服务等

    ·当使用容器管理事务时,开发人员就不需要手工控制事务,也不需要处理复杂的事务传播

    ·容器提供单例模式支持,开发人员不再需要自己编写实现代码

    ·容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能

 

二、Spring源码架构

  1、核心容器:spring-beans 和 spring-core 模块是 Spring 框架的核心模块,包含控制反转(Inversion of Control, IoC)依赖注入(Dependency Injection, DI) ,核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory, 工厂模式的实现。 BeanFactory 使用控制反转(IOC) 思想将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  2、Spring 上下文 Spring Context: Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。 Spring 上下文包括企业服务,例如 JNDI、 EJB、电子邮件、国际化、校验和调度功能。

  3、Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。它的语法类似于传统 EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。

  4、Spring-AOP: spring-aop 是 Spring 的另一个核心模块, 在 Spring中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的Aop 横切实现,比如前置通知、返回通知、异常通知等。 通过其配置管理特性, Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。

  ......

 三、Spring容器工厂的简单实现

技术分享图片
package com.shsxt.factory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Factory_01 implements Factory {
    // Map集合用来存放id和class
    Map<String, Object> map = new HashMap<>();
    List<Bean> beans = new ArrayList<>();

    // 实例化工厂对象时就完成以下操作
    public Factory_01(String fileName) {
        // xml解析
        this.pathXml(fileName);
        // 实例化对象
        this.instanceBean();
        // 属性赋值
        this.setProperty();
    }

    // 属性赋值
    private void setProperty() {
        try {
            if(null!=beans && beans.size()>0){
                for(Bean bean:beans){
                    List<Property> properties=bean.getProperties();
                    if(null!=properties && properties.size()>0){
                        for(Property property:properties){
                            // 得到property的id和ref
                            String id=property.getId();
                            String ref=property.getRef();
                            // set方法中首字母大写
                            id=id.toUpperCase().charAt(0)+id.substring(1);
                            // 获取到当前class
                            Class clz=map.get(bean.getId()).getClass();
                            // 找到set方法
                            Method method=clz.getDeclaredMethod("set"+id,map.get(ref).getClass());
                            method.invoke(map.get(bean.getId()),map.get(ref));
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 实例化
    private void instanceBean() {
        if (null != beans && beans.size() > 0) {
            try {
                for (Bean bean : beans) {
                    // 放到map集合中,通过key找到value,实例化该对象
                    map.put(bean.getId(), Class.forName(bean.getClz()).newInstance());
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * xml解析
     *
     * @param fileName xml文件名
     */
    private void pathXml(String fileName) {
        // 获取xml文件
        URL url = this.getClass().getClassLoader().getResource(fileName);
        try {
            if (null != url) {
                // 获取解析器
                SAXReader saxReader = new SAXReader();
                // 解析xml文件,返回document对象
                Document document = saxReader.read(url);
                // 获取xpath对象
                XPath xPath = document.createXPath("beans/bean");
                // 查询选择的节点,返回的是list集合
                List<Element> elements = xPath.selectNodes(document);
                // 为空判断
                if (null != elements && elements.size() > 0) {
                    for (Element element : elements) {
                        // 获取property
                        xPath = document.createXPath("property");
                        // 查询选择的节点
                        List<Element> subElements = xPath.selectNodes(element);
                        // 为空判断
                        List<Property> properties = null;
                        // 实例化Bean
                        Bean bean = new Bean(element.attributeValue("id"), element.attributeValue("class"));
                        if (null != subElements && subElements.size() > 0) {
                            properties = new ArrayList<>();
                            for (Element subElement : subElements) {
                                // 实例化Property
                                Property property = new Property(subElement.attributeValue("id"), subElement.attributeValue("ref"));
                                // 将对象放到集合中
                                properties.add(property);
                            }
                            bean.setProperties(properties);
                        }
                        beans.add(bean);
                    }
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getObj(String name) {
        // 通过id返回class
        return map.get(name);
    }
}
View Code

 

四、Spring多文件的加载情况

  1、ClassPathXmlApplicationContext类的构造器参数是一个可变长参数

      可同时配置多个xml文件

  2、import标签,将自配置文件导入总配置

    技术分享图片

 

四、Spring Ioc实例化bean的三种方式

  1、构造器的方式实例化bean对象

    通过默认构造器实例化bean对象,默认空构造器必须存在

技术分享图片

 

  2、静态工厂方式实例化bean

    要有工厂类和静态工厂方法

      通过反射调用静态工厂的静态方法,将该静态方法的返回值作为bean的实例,可以统一管理各个bean的创建

技术分享图片

 

   3、实例化工厂方式实例化bean

    工厂类和实例化方法

      工厂方法为非静态,需要配置工厂bean,并在bean中配置factory-bean和factory-method属性

      1) 可用于集成其他框架的bean创建管理方法  2)能够使bean和factory的角色互换

技术分享图片

 

五、Spring 依赖注入

  在面向接口编程中,依赖接口可以动态传入多种实现

  1、set注入

    property标签,属性的set方法(不会出现循环引用问题)

    技术分享图片

    name:属性名称;ref:bean对象的引用;value:给属性直接赋值(List,Set,Map,properties)

    

  2、构造器注入

    constructor-arg标签,带参构造器(会出现循环引用问题,彼此互相依赖对方导致bean无法实例化)

    技术分享图片

    name:属性名称;ref:bean对象id的引用;index:属性的索引

 

  3、静态工厂注入

    技术分享图片

 

  4、实例化工厂注入

    技术分享图片

 

六、注解方式注入bean

  xml配置:加入context命名空间和xsd地址

  添加<context:annotation-config/>配置

 

  @Resource (属于J2EE)

  @Autowired (Spring)

  常用于属性字段或set方法上

  区别:

     @Autowired 默认按bean的类型匹配,和@Qualifier配合使用可以修改按名称匹配

     @Resource 默认按名称进行装配,可以通过name属性指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行匹配注入,如果注解写在set方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配(当name属性指定,只会按照名称装配)

 

注解方式注入的简单模拟:

  

技术分享图片
package com.shsxt02;

import com.shsxt.annotaioms.Component;
import com.shsxt.annotaioms.Component02;
import com.shsxt02.controller.UserController;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Test {

    private static List<String>  clz=new ArrayList<>();
    private static  Map<String,Object> map=new HashMap<>();

    public static void main(String[] args){
        String pkg="com.shsxt02";
        // 获取标记注解的类
        getClZ(pkg);
        // 实例化这些类
        instanceClz();
        // 属性赋值
        property();
        UserController userController= (UserController) map.get("userController");
        userController.test();
    }

    private static void property() {
        try {
            if (null!=map){
                for(Map.Entry entry:map.entrySet()){
                    Field[] fields=entry.getValue().getClass().getDeclaredFields();
                    if (null!=fields){
                        for(Field field:fields){
                            Component02 component02=field.getAnnotation(Component02.class);
                            if (null!=component02){
                                field.setAccessible(true);
                                field.set(entry.getValue(),map.get(component02.value()));
                            }
                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    private static void instanceClz() {
        try {
            if (null!=clz && clz.size()>0){
                for(String cls:clz){
                    //System.out.println(str);
                    cls=cls.replace(".class","");
                    //System.out.println(cls);
                    Component component=Class.forName(cls).getAnnotation(Component.class);
                    if (null!=component){
                        //System.out.println(cls);
                        String id= getId(cls);
                        Object obj=Class.forName(cls).newInstance();
                        map.put(id,obj);
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    private static String getId(String cls) {
        cls=cls.substring(cls.lastIndexOf(".")+1);
        cls= cls.toLowerCase().charAt(0)+cls.substring(1);
        return cls;
    }

    private static void getClZ(String pkg) {
        // 获取url
        URL url=Thread.currentThread().getContextClassLoader().getResource(repStr(pkg));
        String urlPath=url.getFile();
        //System.out.println(urlPath);
        String[] subFileStrs=new File(urlPath).list();
        for(String str:subFileStrs){
            String subFilePath=urlPath+"/"+str;
            //System.out.println(subFilePath);
            File subFile=new File(subFilePath);
            if (subFile.isDirectory()){
                //System.out.println(pkg+"."+subFile.getName());
                getClZ(pkg+"."+subFile.getName());
            }else{
                clz.add(pkg+"."+subFile.getName());
            }
        }
    }

    private static String repStr(String str){
        str=str.replace(".","/");
        return str;
    }
}
View Code

 

七、Spring IOC 容器自动扫描管理bean

  xml配置<context:component-scan base-package=""/>

  建议的注解

    Dao层:@Repository

    Service层:@Service

    控制层:@Controller

    不明确:@Component

 

八、Bean的作用域问题(scope)

  1、singleton作用域(单例,默认)

    lazy-init 懒加载 默认为false

      如果等于true时,spring容器启动的时候不会去实例化这个bean,而是在程序调用时才会去实例化

      在启动情况下实例化所有singleton的bean对象并缓存与容器中单例的好处:

        1、提前发现潜在的配置问题

        2、bean对象存在于缓存中,使用时不用再实例化bean,提高执行性能

    

      无状态对象适合做单例bean对象(无可变的成员变量)

 

  2、prototype作用域(原型)

    每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean。IOC不会维护该对象

 

  3、Web应用中的作用域(request、session、globalsession)

    request作用域:每一次请求

    session:当前会话

    globlasession:同session(Portlet环境)

以上是关于Spring_Ioc容器的主要内容,如果未能解决你的问题,请参考以下文章

Spring_IOC控制反转和DI依赖注入

分享知识-快乐自己:Spring_IOC(控制反转)详解

Spring_IOC

Spring_IoC&DI

zbb20170216_spring_ioc

spring_IOC编程