• 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的存在;

  • 相关阅读:
    信息爆炸时代,对待信息的三种方式
    Spring事务管理
    归并排序和快速排序的衍生问题
    Linux之Shell命令
    程序员找工作的干货经验
    css3 Transition动画执行时有可能会出现闪烁的bug
    布尔值
    null, undefined理解
    js文字的无缝滚动(上下)
    vue实现文字上下滚动
  • 原文地址:https://www.cnblogs.com/Java0120/p/9943551.html
Copyright © 2020-2023  润新知