Spring-day01

轻量级和重量级
两层含义:
1,轻量级是相对于重量级而言的,轻量级一般就是非入侵性的、所依赖的东西非常少、资源占用非常少、部署简单等等,其实就是比较容易使用,而重量级正好相反。
2,相比较传统的JavaEE项目,即真正意义上的JavaEE规范(比如EJB3.0;JPA;JMX;JMS;JNDI;JSF等),我们把能直接在Tomcat等符合Java Servlet规范的Web服务器上能直接运行的Java应用称为轻量级的JavaEE应用;一般就意味着以Spring为代表的一些事实上的标准开发框架;可以说,Spring就是运行在Web服务器中的Application Server;

Spring的优点:
  1.低侵入/低耦合(降低组件之间的耦合度,实现软件各层之间的解耦。)
  2.声明式事务管理
  3.方便集成其他框架
  4.降低JavaEE开发难度
  5.Spring框架中包括JavaEE 三层的每一层的解决方案 (一站式)
版本:
Spring4.x:
  1,支持Java8,支持JavaEE6规范。
  2,泛型限定式依赖注入。
  3,对Hibernate4的集成和事务提供更好的管理方案。

Spring框架主要构成图:

 

IOC
Spring是一个IoC/DI/AOP容器;

什么叫做容器?装东西的东西;装对象的一个集合就叫做容器;
IoC? inverse of control:控制反转?简单的说,就是把对象的实例化工作交给容器来完成;
HelloWorld:
1,传统的应用,如果A类依赖B类,那么A类就应该负责控制B类的实例化;
2,传统的应用,不能做到真正的解耦,如果对象由容器创建,我们应用从容器里面拿接口的真正实现,就可以完全的实现接口编程;

Spring helloworld:
1,Spring的使用:
1,导入相关jar包(beans/core);
2,导入依赖包(common-logging)
3,配置XML文件(作用:告诉spring帮我管理哪些bean);
  1),在classpath下,application.xml/applicationContext.xml;
  2),<beans><bean id="" class="" /></beans>
4,使用容器:
  1),创建一个资源文件对象(ClasspathResource);
  2),创建一个BeanFactory(Spring的容器);创建一个基于XML的BeanFactory:XmlBeanFactory,传入XML配置文件资源对象;
  3),从容器中拿对象:
    1),getBean(Class):按照类型拿bean;
    2),getBean(String):按照名字拿bean;
    3),getBean(String,Class):按照名字和类型拿;(推荐) 【类型需要是接口的类型】

2,helloworld注意:从容器里面拿对象一定要使用接口去拿对象,不能直接使用真实的实现类;
3,分析Spring的加载过程;
  1),找到对应的配置文件(xml);
  2),加载配置文件;
  3),解析所有的bean元素;识别id和class属性;
  4),通过反射,Class.forName().newInstance()去创建一个这个类型的对象实例;
  5),把id作为key,把实例作为value存到spring容器中;
  6),getBean从容器中拿到创建好的对象的实例;
总结:
1,spring的本质其实就是把应该写在java中的代码移到了配置文件中;
2,默认管理的bean需要一个无参的构造方法;
3,重要:在spring的配置文件中配置的不是类型,而是bean的实例;

import
为了方便管理应用配置文件,推荐使用import来规划配置文件;

    <!-- 
        在Spring中,可以把配置文件分散到各个模块中,然后在总的配置文件中,使用import元素引入这些配置文件
        1,默认情况下,使用相对路径寻找配置文件
        2,Spring提供了两种前缀标记来辅助查找配置文件
            1),[classpath:]:代表从classpath开始寻找后面的配置文件;(推荐使用)
            2),[file:]:代表使用文件系统的方式来寻找后面的配置文件(文件的完整路径)
     -->
    <import resource="com/rk1632/_02_name/name.xml"/>

spring配置中的名字;

    <!-- 
        id属性要求在Spring中必须是唯一的
        如果我们的bean需要其他的名称,可以通过name属性来为bean配置更多的名称
        并且在name属性中可以使用逗号、分号和空格来分割多个名称
        在Java代码中可以通过BeanFactory.getAlias来获取一个bean的所有名称(返回一个String数组)
        
        什么时候会用到name属性?
            1,一般只使用id属性即可;
            2,在SpringMVC版本中,可以在name属性配置多个名称来表示映射的URL地址
     -->     
    <bean id="somebean" class="com.rk1632._02_name.SomeBean"/>

传统的测试

public class NameTest {

    private SomeBean bean;
    
    public NameTest(){
        //启动Spring容器
        //从根路径加载资源对象,ClassPathResource表示从classpath下开始加载的资源对象
        Resource resource = new ClassPathResource("applicationContext.xml");
        //根据资源对象实例化BeanFactory对象
        BeanFactory beanFactory = new XmlBeanFactory(resource);
        //根据BeanFactory得到配置了的JavaBean对象
        bean = beanFactory.getBean("somebean",SomeBean.class);
    }
    
    @Test
    public void test() throws Exception {
        bean.print();
    }
}

Spring中的测试:
1,我们使用传统的方式测试spring有一些问题,
  1,我们需要自己手动的去启动 spring容器;
  2,测试完成之后,我们并没有正常的关闭spring容器;
  3,我们每运行一个测试用例,就相当于要重新启动一次spring容器;
  4,我们的spring容器是运行在JUnit测试里面的;

2,spring推荐的基于spring的测试:
  1,搭建测试环境(test/context/expression/aop.jar包)
  2,在测试类上添加@RunWith标签;
  3,在测试类上添加ContextConfiguration标签;
  4,在测试类中添加一个BeanFactory的字段,在这个字段上添加@Autowired标签;
  5,在测试中,可以直接通过这个BeanFactory字段拿我们需要的bean

/**
 *    添加RunWith标签;
 *    1,告诉JUnit,在测试开始的时候,先启动Spring容器,在测试完成之后记得关闭Spring容器 ;
 *    2,自动的把这个测试类也加入到了Spring容器中管理
 */
@RunWith(SpringJUnit4ClassRunner.class)
/**
 *     告诉Spring从什么地方加载配置文件,默认情况下使用相对路径查询;也可以使用classPath前缀
 *    如果在ContextConfiguration标签中不写配置文件地址,Spring可以通过一个约定的方式找到配置文件
 *        1,配置文件和测试类处于同一个包下
 *        2,这个配置文件的名称叫:测试类名称-context.xml
 */
@ContextConfiguration
public class SpringTest {
    private SomeBean somebean;
    
    /**
     * 使用Autowired标签,就自动的把Spring创建好的容器的引用,设置给了这个字段
     */
    @Autowired
    private BeanFactory beanFactory;
    
    @Test
    public void test() throws Exception {
        somebean = beanFactory.getBean("somebean", SomeBean.class);
        somebean.print();
    }
    
}

Spring中提供的容器

1,BeanFactory:BeanFactory是Spring中提供的最简单,最基本的容器;这个容器只提供了IoC/DI的功能;
2,BeanFactory的重要方法:getBean();
3,我们一般使用的是Spring提供的功能更加全面的ApplicationContext;
ApplicationContext:是一个功能更加全面的容器,一般我们直接使用这个容器;
1,ApplicationContext接口继承了BeanFactory接口,所以,ApplicationCOntext实现了getBean方法;
2,提供了额外的功能:
  1),环境感知;
  2),容器的继承体系;
  3),国际化相关;
  4),事件发布/响应机制;
  5),统一的资源管理;
  6),AOP的功能;
3,ApplicationContext使用:
  手动启动ApplicationContext:

//Spring是一个容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ContainerTest {

    /**
     *     其实在使用Spring测试的时候,启动的容器都是ApplicationContext;
     *     因为ApplicationContext继承了BeanFactory,所以我们可以在这里直接注入BeanFactory
     */
    @Autowired
    private ApplicationContext ctx;
    
    @Test
    public void testCtx() throws Exception {
        SomeBean bean = ctx.getBean("somebean", SomeBean.class);
        bean.print();
    }
}

bean的实例化时机(容器在什么时候实例化bean)

1,对于beanfactory来说,在容器启动的时候,不会去实例化bean,必须要等到使用bean的时候,才会去初始化(延迟实例化);
2,对于ApplicationContext来说,在容器启动的时候,就已经把所有的管理的bean实例化了;
3,对于ApplicationContext:
  1),可以在bean元素配置lazy-init=true来让bean延迟实例化;
  2),可以在beans元素配置default-lazy-init=true来让这个beans里面所有的bean延迟实例化;

对比:
1,延迟实例化,在系统启动的时候快,系统启动的时候占用的资源少;并且,如果在使用的时候再去加载对象,第一次使用的时候性能较低;因为不会在加载的时候去实例化对象,所以延迟实例化不能对实例化出来的对象做更多的功能;
2,非延迟实例化,在系统启动的时候,所有对象被实例化,启动速度慢,使用速度快,启动的时候占用较多系统资源;

选择:
1,对于系统资源较少的应用可以使用延迟实例化(移动应用);
2,对于web应用来说,一般选择迫切实例化;
3,在web应用里面,我们一般把比较耗时的事情放在系统启动的时候完成;

bean的实例化方式: 

1,默认的方式:

    <!-- 
        对于普通的JavaBean,只需要有class即可
        要求,这些class必须要有无参构造器
     -->     
        
    <bean id="somebean" class="com.rk1632._04_create.SomeBean"/>
public class SomeBeanFactory {

    public SomeBean create(){
        SomeBean bean = new SomeBean();
        bean.init();
        return bean;
    }
}

2,静态工厂方法:

    <!-- 配置静态工厂方法生成的bean 
        1,bean的类型必须写工厂的类型;
        2,必须配置factory-method:代表调用工厂类的哪个方法来创建bean实例化对象;
     -->
    <bean id="somebean" class="com.rk1632._04_create.SomeBeanFactory" factory-method="create"/>

静态工厂方法执行流程:
1,找到对应的配置文件;
2,加载配置文件;
3,解析所有的bean元素;识别id和class属性;
4,如果bean元素只有factory-method属性,得到factory-method属性值;
5,使用class属性+factory-method使用反射创建对象实例:
Class.forName(class).getMethod(factory-method).invoke(null,null);
5,把id作为key,把实例作为value存到spring容器中;
6,getBean从容器中拿到创建好的对象的实例;

 3,实例工厂方法

    <!-- 配置实例工厂,实例工厂方法:通过一个工厂的实例对象来生成我们需要的对象
        1,先配置一个工厂的实例对象;
        2,再配置bean,factory-bean:哪个实例作为工厂,factory-method:在实例对象上面调用哪个工厂方法;
        3,注意,在spring中声明的对象之间是可以通过id相互引用的;
     -->
    <bean id="somebeanFactory" class="com.rk1632._04_create.SomeBeanFactory"/>
    <bean id="somebean" class="com.rk1632._04_create.SomeBean" factory-bean="somebeanFactory" factory-method="create"/>

 4,FactoryBean

public class SomeBeanFactory implements FactoryBean<SomeBean> {

    //工厂创建bean的方法
    public SomeBean getObject() throws Exception {
        SomeBean bean = new SomeBean();
        bean.init();
        return bean;
    }

    //方法返回工厂生产bean的类型
    public Class<?> getObjectType() {
        return SomeBean.class;
    }

    public boolean isSingleton() {
        return true;
    }

}
    <!-- 如果我们的工厂类是实现了FactoryBean这个接口
        那么,Spring会自动识别到我们这个类是一个工厂类
        虽然我们在Spring配置文件中只配置了这个工厂类的类型,
        但是Spring知道我们需要的bean是工厂生产的bean对象
     -->    
     <bean id="somebean" class="com.rk1632._05_factorybean.SomeBeanFactory"/>

factorybean的执行流程:
  1,找到对应的配置文件;
  2,加载配置文件;
  3,解析所有的bean元素;识别id和class属性;
  4,通过反射得到对象的实例,Class.forName(class).newInstance();
  5,if(bean instanceof FactoryBean){
  FactoryBean factory=(FactoryBean)bean;
  Object realBean=factory.getObject();
  }
  6,把id作为key,把实例(realBean)作为value存到spring容器中;
  7,getBean从容器中拿到创建好的对象的实例;

Spring中对象的Scope:
1,在spring中配置的bean默认情况下就是单例的;
2,在WEB应用中,持久化层和业务层的对象一般都是单例的;
3,在struts2 中每次请求的Action都是全新的,所以不能为单例

    <!-- 
        可以使用scope属性来限定bean的生命范围(在什么范围内拿到的bean是相同的)
        singleton:整个应用中拿到bean都是同一引用(单例模式);singleton也是默认的scope
        prototype:原型模式:每次拿这个对象,拿到的对象都是这个对象原对象的一份拷贝;每拿一次就创建一个新的对象;
                注意,对于scope为prototype的bean,applicationContext在启动的时候不会去初始化这些类
        request:在一次请求中(HttpRequest),创建一个bean;(一般不会使用,必须是在web环境下的Spring中使用,如SpringMVC)
        session:在一次会话中(HttpSession),创建一个bean;(一般不会使用,必须是在web环境下的Spring中使用,如SpringMVC)
     -->
     <bean id="somebean" class="com.rk1632._06_scope.SomeBean" scope="prototype"/>

initMethod和destoryMethod:

需求:
1,bean需要在spring实例化完成之后需要去调用bean上面的一个初始化方法来完成bean的初始化工作;
2,bean需要在spring正常销毁之前,调用一个结束方法(销毁方法)去完成一些清理工作;

1,如果手动启动容器,必须要正常关闭spring容器,才能调用到destory-method;

    //如果手动启动容器,容器会在测试结束后被强制关闭
    @Test
    public void testClose1() throws Exception {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("com/rk1632/_07_initMethod_destoryMethod/initMethod_destoryMethodTest-context.xml");
        SomeBean bean = ctx.getBean("somebean", SomeBean.class);
        bean.print();
        //调用ApplicationContext的close方法来关闭
        ctx.close();
    }
    
    //registerShutdownHook():代表在JVM正常关闭线程中,添加一个Spring关闭的子线程
    @Test
    public void testClose2() throws Exception {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("com/rk1632/_07_initMethod_destoryMethod/initMethod_destoryMethodTest-context.xml");
        SomeBean bean = ctx.getBean("somebean", SomeBean.class);
        bean.print();
        ctx.registerShutdownHook();
    }

2,如果bean的scope是prototype,spring不会调用destory-method;

    <!-- 
        init-method:告诉Spring,SomeBean对象的初始化方法的名称;初始化方法不能有参数
        destroy-method:告诉Spring,SomeBean对象的销毁方法的名称;销毁方法不能有参数
     -->
     <bean id="somebean" class="com.rk1632._07_initMethod_destoryMethod.SomeBean" 
             init-method="init" destroy-method="close"/>

小结
通过Spring的IoC功能,我们在容器中得到了一个一个独立的bean

 

DI

DI:dependence inject(依赖注入) 把对象的依赖关系全部交给spring容器来处理;

注入的类型:
1,简单类型;
2,其他的bean;
3,集合;

注入方式:
1,通过setter方法注入;
2,通过构造器注入;

     <bean id="otherbean" class="com.rk1632._09_conDI.OtherBean"/>

     <!-- spring提供了一些额外的配置来方便spring来找到对应的构造器
            1,index:通过构造方法参数的位置来指定,从0开始
            2,type:在构造方法中的参数类型
            3,name:构造方法的参数名称来指定设置
      -->
     <bean id="somebean" class="com.rk1632._09_conDI.SomeBean">
            <!--             
            <constructor-arg value="书航"/>
            <constructor-arg value="22"/>
            <constructor-arg ref="otherbean"/>
             -->
             <constructor-arg ref="otherbean"/>
             <constructor-arg value="松鼠"/>
     </bean>

选择:

1,使用构造方法注入
  1),能够保证这个对象依赖的对象都正确注入了;
  2),如果依赖的对象过多,构造方法的参数太多了;
2,使用setter注入:
  1),如果依赖对象过多,代码不会不好维护;
  2),如果依赖对象过多,可能造成某些属性注入掉了;

一般情况,还是使用setter注入;
1,使用setter和构造方法都能够正确的注入bean的属性;
2,构造方法可以更强制的规定依赖的对象必须存在,如果不配置,则创建对象失败;但是构造方法不宜有过多的参数,否则代码难以维护;
3,更多的还是使用setter方式的注入,但是setter注入可能会产生忘记配置的问题,可以使用@Required注解来配置(放在必要的属性的setter方法上);

spring的IoC/DI能为我们的应用做什么?

1,使用传统的方式,会产生:
  1),应用使用了接口,但是并没有做到真正的解耦;
  2),应用中的DAO和service并没有做单例(性能较差);

2,使用spring,
  1),应用中真正的就按照接口在编程了;
  2),spring就像一个粘合剂,在XML文件中自动的帮我们初始化了所有的对象,并设置好所有对象之间的依赖关系;
  3),除了配置文件,根本感觉不到spring的存在;

原文地址:https://www.cnblogs.com/Java0120/p/9943551.html