Spring第一天——入门与IOC

  大致内容

    spring基本概念

    IOC入门

【17.6.9更新】,如何学习spring?

  掌握用法

  深入理解

  不断实践

  反复总结

  再次深入理解与实践

一、Spring相关概念

   1.概述:    

    Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的容器(container)

      无论从大小和开销还是来说,都足以称得上是轻量级的框架
    由Rod Johnson创建。
    简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架

    可以说,spring带来了复杂JavaEE开发的春天!

  【更新】spring是一个解决方案级别的整合框架,主要是用来作解耦

  2.核心有两部分:

    IOC:控制反转
      对象创建不是通过new,而是通过配置交给Spring管理
    AOP:面向切面编程
      所以AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面

    是一种容器,它会管理由他创建的对象,包括生命周期的管理等。

  3.一站式框架
    在JavaEE三层架构中,每一层都提供了不同的解决方案
    web层:SpringMVC
    service层:IOC
    dao层:jdbcTemplate

    对应JavaWeb阶段的三层架构:

    

  

  4.使用的版本
    Spring4.x的版本

  5.Spring包的下载方式
    http://www.open-open.com/news/view/1eb1613

    将文章大致内容提取出来就是:

       找到 spring framework

     找到All avaible features and modules are described in  the Modules section of the reference documentation .

        Their  maven/gradle coordinates are also described there 

     这个就是各个spring版本的下载地址:  http://repo.spring.io/release/org/springframework/spring 而且很清楚的告诉你下载哪个文件。
     (等待maven依赖管理的更新)

 二、IOC(Inverse of Control)控制反转

  【更新】:面向接口编程 每一层只向外提供接口,各层之间只依赖接口而不依赖具体的实现,用于隐藏具体实现并实现多态的组件

    什么被反转了?获得依赖对象的过程被反转了

  IOC主要的观点就是借助第三方(也就是IOC容器)实现依赖关系的对象之间的解耦

  更多详细的IOC与DI的阐述,请参见http://www.cnblogs.com/xdp-gacl/p/3707631.html

  1.对象创建交给Spring管理

    【更新】:应用程序本身并不负责依赖对象的创建和维护,由IOC容器负责创建和维护

      (IOC容器初始化的时候会创建一系列的bean,并存在spring的上下文中,ApplicationContext)

  

   2.Ioc管理分为两部分:

  【更新,初始化IOC容器的方法(WEB应用中) listen可以通过context-param】 主要是指定 contextConfigLocation

    1)Ioc配置文件
    2)Ioc注解 

  (1)==Ioc的底层原理:
      主要用到的技术
        xml配置文件
        dom4j解析配置文件
        工厂设计模式
        反射

之前代码:
            一个User类:
                public class User{
                    public void add(){
                        ...
                    }
                }  
View Code

  想调用add方法,需要构建类的对象,再调用:
    User user = new User();user.add();
  如果想在servlet中使用,需要将上述代码写在servlet中,但如果原来的类名、方法名发生了修改
  则servlet也要同步修改,耦合度太高,不方便!

    针对这种情况,提出第一阶段的解决方案:
      工厂模式,使用此模式进行解耦操作

public class UserService{
                public void add(){
                    ...
                }
            }
            做法是建立一个工厂类:
                public class UserFactory{
                    //提供静态方法返回
                    public static UserService getService(){
                        return new UserService();
                    }
                }
            在servlet中调用可以使用工厂:
                UserFactory.getService().add();
                这样就相对降低了和类的耦合度
                但缺陷是和工厂类又有耦合度(高内聚,低耦合)
View Code

     IOC的解决的大概过程:
      要在servlet得到UserService类的实例
    步骤:
      第一步:创建xml配置文件,配置需要创建的对象的类;
          <bean id="userService" class="cn.UserService">
      第二步:创建工厂类,使用dom4j解析配置文件+反射
          解析xml得到指定id值得class属性值:类路径
          采用反射创建类的对象
          得到类对象过程还是通过工厂:Factory.getUserService();
          此时类名称发生变化时,只需要变更xml配置文件即可

  (2)IOC入门案例:
    第一步:
      导包:jar包里每个都有三个jar包,分别是jar包、doc、源码
          spring核心组件有四个(如图)


分别是:beans core context spEL(表达式的,导expression的包)
再导入支持日志输出的jar包(spring本身并不提供日志jar包)【log4j的使用在后面作补充】
  log4j logging 两个jar包

  也就是如图六个jar包

  第二步:
    创建类和方法
    例如创建User类和add()方法

package cn.ioc;

public class User {

    public void add(){
        System.out.println("spring_ioc_add");
    }
}
View Code

  第三步:
    创建spring配置文件,配置创建的类
    前面两个框架的名称和位置固定,而spring不是,但一般都建议放在src下,【更新】:一般放在与src同级的 source folder 下。
    建议名字为 applicationContext.xml
    自定义命名请参考:http://huihai.iteye.com/blog/1019281
    但完全可以自定义名字(位置建议src!)

      在web.xml中指定spring路径:

<context-param>  
     <param-name>contextConfigLocation</param-name>  
     <param-value>/WEB-INF/beans.xml</param-value>  
</context-param> 

    比如这里创建一个 bean1.xml
    创建完xml文件,首先是引入约束(这里是schema约束!)
    进入包里的docs->spring framework reference->html->找最后一个文件 (见图!)

    【更新】:现在由 springIDE来做这件事

           

    引入schema约束:

<?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-4.0.xsd">

    <!--ioc入门-->
    <bean id="user" class="cn.ioc.User"></bean>
</beans>
View Code

  注意:id建议与类同名,首字母改为小写(这也是spring的默认命名策略)

  第四步:
    简单测试
      步骤:
        加载核心配置文件
        得到对象
        具体不再赘述,见testIoC

package cn.ioc;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestIoC {

    //只是测试这样写,实际开发并不这样操作
    @Test
    public void test(){
        //加载spring核心配置文件
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("bean1.xml");
        //得到配置创建的对象
        User user =(User)context.getBean("user");
        user.add();
    }
}
View Code

  //配置文件没有提示的问题(无法上网,类比之前dtd引入,但稍有区别)此处不再赘述,全部在另外一篇同分类引入约束的博客介绍

  (3)spring的bean管理基于配置文件,day02注解方式
    ==bean实例化的三种方式:(常用一种方式)
      1)使用类的无参构造器(常用)
        上面的入门案例
        <bean id="user01" class="cn.ioc.User"></bean>
        会自动找无参的构造器
      2)使用静态工厂创建
        在类里使用静态方法返回实例对象
        <bean id="user02" class="cn.factory.BeanFactory" factory-method="getBean"></bean>
        依旧使用context.getBean()方法得到bean
      3)使用实例工厂创建
        创建一个不是静态的方法返回类的对象,使用此方式先要创建工厂的对象
        <bean id="bean3Factory" class="cn.factory.Bean3Factory"></bean>
        <bean id="user03" factory-bean="bean3Factory" factory-method="getBean"></bean>
      2、3仅作了解,实际开发用的一般是方式1   

  ==bean标签常用属性的介绍:
    id : 表示起的名称(一般不要包含特殊符号) ,可以根据id值得到配置的对象
    class : 要创建对象所在类的全路径
    name : (一般已经不用了) 功能和id类似,也可以得到bean对象(id值不能得到特殊符号),
        属于遗留问题,为了整合原来的struts1的
    scope : bean的作用范围:一般掌握两个值:
        singleton:单例 (默认)
        prototype:多例(应用于struts2的多实例的action)
      可以分别拿到两个对象比较对象地址

  (4)属性注入:创建对象的时候,也可以向类的属性设置值
    属性注入的方式:支持级联属性的注入(但是必须初始化(不会像struts2一样自动创建,当然,很多时候我们都用不着))
      原始方式:
      有参构造注入
      set()方法注入【用的最多】
      接口注入(用的少)

  spring只支持前两种方式的注入:
    spring有参构造注入:
    建立类

package cn.propertiry;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class PropertiryDemo01 {

    private String username;

    public PropertiryDemo01(String username) {
        this.username = username;
    }
    public void print(){
        System.out.println("有参构造属性构造:"+username);
    }
    
}

  配置文件修改:

 <!-- 使用有参构造器属性注入 -->
  <bean id="prop01" class="cn.propertiry.PropertiryDemo01">
    <constructor-arg name="username" value="jiangbei"></constructor-arg>
  </bean>

  (不出现name属性时,把约束后面的spring-beans-4.0补上)

<!-- 有参构造注入 -->
    <bean id="id" class="class">
        <!-- name必须和构造器的参数名一致,ref为需要注入的引用 -->
        <constructor-arg name="" ref=""/>
    </bean>

  spring的set方式注入【最常用】:
    依旧是:创建类 配置文件配置 简单测试

package cn.propertiry;

public class Book {

    private String BookName;//属性
    //set()方法
    public void setBookName(String bookName) {
        BookName = bookName;
    }
    
    public void print(){
        System.out.println("set属性注入:"+BookName);
    }
}

  修改配置文件

<!-- set()方法属性注入 -->
<bean id="book" class="cn.propertiry.Book">
  <property name="BookName" value="从入门到放弃"></property>
</bean>

【实用】真正开发中不止是注入字符串,更多的是对象的注入等!
  例如service中依赖dao,需要dao的属性,可以进行属性注入(只是注入的是对象!)
  将new的过程交给spring实现!
  先建立两个类 Service Dao

package cn.propertiry;

public class Service {

    //定义dao成员变量
    private Dao dao;
    public void setDao(Dao dao) {
        this.dao = dao;
    }

    public void add(){
        //service中要调用dao的add()方法需要得到其对象
        dao.add();
    }
}

  

package cn.propertiry;

public class Dao {

    public void add(){
        System.out.println("dao.add");
    }
}

  在Service中依赖dao对象,定义变量,生成set()方法
  进行配置(ref):

 <!-- 属性注入,注入对象 -->
  <bean id="dao" class="cn.propertiry.Dao"></bean>
  <!-- 注入dao对象 -->
  <bean id="service" class="cn.propertiry.Service">
    <!-- name为成员变量名,ref为dao的bean的id -->
    <property name="dao" ref="dao"></property>
  </bean>

  当然是可以通过多行注入多个属性的。

  名称空间注入:(也可以实现属性注入)只引出概念,不作演示赘述(用的太少)

  ===spring注入复杂数据(会用)
    例如注入list 注入数组 注入map 注入propertites
    都在案例:Prop类里
    定义成员
    生成set

package cn.propertiry;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class Prop {

    //注入复杂数据类型
        private String[] strs;
        private List<String> list;
        private Map<String,String> map;
        private Properties props;

        
        public void setStrs(String[] strs) {
            this.strs = strs;
        }
        public void setList(List<String> list) {
            this.list = list;
        }
        public void setMap(Map<String, String> map) {
            this.map = map;
        }
        public void setProps(Properties props) {
            this.props = props;
        }
        
        public void print2(){
            System.out.println("array:"+strs);
            System.out.println("list:"+list);
            System.out.println("map:"+map);
            System.out.println("props:"+props);
        }
}

    配置注入(所有的注入都必须先有对象(相关数据)生成)

      如果是配置引用,可以使用 value-ref等(按alt /即可见名知意)

      也可以配置独立的bean(导入util的命名空间),以供多出引用

<!-- 注入复杂属性 -->
    <bean id="p" class="cn.propertiry.Prop">
        <!-- 进行属性注入 -->
        <!-- 数组 -->
        <property name="strs">
            <list>
                <value>sx</value>
                <value>sy</value>
                <value>sz</value>
            </list>
        </property>
        <!-- list -->
        <property name="list">
            <list>
                <value>lx</value>
                <value>ly</value>
                <value>lz</value>
            </list>
        </property>
        <!-- map -->
        <property name="map">
            <map>
                <entry key="mx" value="mvx"></entry>
                <entry key="my" value="mvy"></entry>
                <entry key="mz" value="mvz"></entry>
            </map>
        </property>
        <!-- props -->
        <property name="props">
            <props>
                <!-- props的value值写在标签里 -->
                <prop key="px">pvx</prop>
                <prop key="py">pvy</prop>
                <prop key="pz">pvz</prop>
            </props>
        </property>
    </bean>

  最后,贴出整个演示的配置文件

<?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-4.0.xsd">

    <!-- bean definitions here ioc入门-->
    <bean id="user" class="cn.ioc.User"></bean>
    
    <!-- 使用有参构造器属性注入 -->
    <bean id="prop01" class="cn.propertiry.PropertiryDemo01">
        <constructor-arg name="username" value="jiangbei"></constructor-arg>
    </bean>
    
    <!-- set()方法属性注入 -->
    <bean id="book" class="cn.propertiry.Book">
        <property name="BookName" value="从入门到放弃"></property>
    </bean>
    <!-- 属性注入,注入对象 -->
    <bean id="dao" class="cn.propertiry.Dao"></bean>
    <!-- 注入dao对象 -->
    <bean id="service" class="cn.propertiry.Service">
        <!-- name为成员变量名,ref为dao的bean的id -->
        <property name="dao" ref="dao"></property>
    </bean>
    
    <!-- 注入复杂属性 -->
    <bean id="p" class="cn.propertiry.Prop">
        <!-- 进行属性注入 -->
        <!-- 数组 -->
        <property name="strs">
            <list>
                <value>sx</value>
                <value>sy</value>
                <value>sz</value>
            </list>
        </property>
        <!-- list -->
        <property name="list">
            <list>
                <value>lx</value>
                <value>ly</value>
                <value>lz</value>
            </list>
        </property>
        <!-- map -->
        <property name="map">
            <map>
                <entry key="mx" value="mvx"></entry>
                <entry key="my" value="mvy"></entry>
                <entry key="mz" value="mvz"></entry>
            </map>
        </property>
        <!-- props -->
        <property name="props">
            <props>
                <!-- props的value值写在标签里 -->
                <prop key="px">pvx</prop>
                <prop key="py">pvy</prop>
                <prop key="pz">pvz</prop>
            </props>
        </property>
    </bean>
</beans>

  【更新】:使用外部属性配置文件(场景就是读取数据源等信息)

     之前可以使用此种形式:缺点很明显,如果需要修改数据库的配置信息,将比较复杂,需要拿出spring的配置文件

<!-- 数据库连接池 (此时使用的是dbcp,效率不高,仅作演示使用)-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="10" />
        <property name="maxIdle" value="5" />
    </bean>

    我们将这些属性抽取到db.properties:(一般而言,我们都是给Key这里起类似 jdbc.username这样的以.分割的形式,因为有时候直接写username非常容易和其他的(如系统的用户名)相冲突)

jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test

jdbc.initPoolSize=5
jdbc.maxPoolSize=10

    我们加入context的命名空间(使用springIDE,更简单,更强大!),在配置文件中进行属性文件位置的配置:

<!-- 导入属性文件 -->
    <context:property-placeholder location="classpath:db.properties"/>

    如何在配置文件中取值我们之前已经提过,使用类似EL的形式:${val}

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="10"/>
        <property name="maxIdle" value="5"/>
    </bean>

  //当然,这些配置都可以通过编码方式来代替,实现真正的全注解无配置开发

  【更新】 spring的SpEL:为配置文件动态赋值提供了可能

    使用#{...},可以动态引用其它对象,动态引用其它对象的属性值(字面值这里不做阐述)

     value="#{car.price}",注解形式:

           @Value("#{testConstant.STR}")  

          private String str;  

      并且还支持一些基本的运算,以及三目判断运算符等其他的待补充

  【更新】 spring容器的生命周期方法:init-method 和 destroy-method

  重要概念:
    IoC和DI的区别:
  IoC:控制反转,将对象的创建交给spring管理
  DI:依赖注入,向类的属性中设置值
  DI需要在IoC的基础上进行操作(需要在对象创建的基础上进行)

  【更新】:DI是IOC的一种更简单的诠释,也就是说:通过引入IOC容器,利用依赖注入的方式,实现对象之间的解耦

   【更新】bean的作用域(与struts2整合时使用到)

      prototype(每次请求都创建新的实例,使用hashCode判断) singleton request session

      bean的生命周期

      定义  

      初始化

        (比较简便的配置是bean中配置init-method destroy-method的配置)

        当然不使用配置的时候可以通过bean实现 initlizaingBean disposiableBean两个接口,覆盖方法

          当然,也可以配置全局的初始化和销毁方法(就近原则 全局的最后执行)

      使用

      销毁

第一天的最后引出整合的一些基本概述(后面day会做补充)

===spring整合web项目
  Hibernate时有一个遗留问题:sessionFactory的创建会比较慢,可以交给服务器来创建
  还有上面的bean1.xml每次都要加载spring核心配置文件,会影响性能
  以上的类似问题,解决的实现思想都是:
    把加载配置文件和创建对象在服务器启动时就创建,把压力给服务器
  spring中封装了相关的处理类,这里简单介绍原理:

  web阶段中有一个与天地同寿的对象 ServletContext
  它的创建可以由监听器进行监听
  在服务器启动的时候,服务器会为每个项目创建一个独一无二的对象:ServletContext
  可以使用监听器对ServletContext进行监听,可以知道对象的创建时间
  于是可以在监听器监听到ServletContext创建后
  加载spring配置文件,把配置文件中配置的对象进行创建
  对象创建后将创建的对象放在ServletContext中(它也是一个域对象)
  域对象的存取数据直接使用get/setAttribute()即可

原文地址:https://www.cnblogs.com/jiangbei/p/6790251.html