Spring连载(1)

好久没写博文了,最近玩的太浪,打算把spring再复习一遍,以于温故知新。

先敬上一张spring的图

Spring架构图

Spring是一个非常活跃的开源框架,他是一个基于Core来构架多层javaEE系统的框架,它的主要目的就是为了简化企业的开发。

Spring是一种非入侵的方式来管理你的代码,Spring提倡”最少侵入”。Spring 的每一个模块都是独立的。

Spring-IOC

什么是IOC,IOC全称(Inversion of Control)即控制翻转。IOC的作用就是把对象的创建交给Spring来做。对比以前创建对象通过New来做,IOC有什么好处?

程序员不再关心对象的创建,直接拿来使用。

Spring创建对象

1)Spring所需jar包

spring.jar
commons-logging.jar

2)配置文件

applicationContext.xml

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

</beans>

这里对Spring配置文件中的一些属性做一定的解释

beans:把一个放入到Spring容器中,该类就是一个bean。

这里新建一个类:

public class Spring01 {
    public void testSpring() {
        System.out.println("hello word");
    }
}

把这个bean(类)放入Spring容器中,怎么放?

id:唯一标识符

class:类的全路径

<bean id="test" class="com.liuzehui.spring.Spring01"></bean>

测试类:

测试对象,要想测试这个bean是否创建对象,则需要从spring容器中拿,那么问题来了,怎么启动spring容器?怎么拿这个对象?

public class TestSpring {

    /***
     * 1.启动spring容器
     * 2.从spring容器中把对象取出来
     * 3.对象调用方法
     */
    @Test
    public void testSpring() {
        // 启动/加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过id从Spring容器中拿出对象
        Spring01 spring = (Spring01) context.getBean("test");
        // 对象调用方法
        spring.testSpring();
    }

}

输出结果:hello word

至此,Spring如何创建对象、如何启动Spring容器、怎样从Spring容器中获取bean、对象调用方法,就大功告成了。

Spring容器执行流程:

注意:Spring容器是调用默认的构造方法来进行对象的创建。

我们都知道一个类如果不显示的声明构造方法,系统则会自动生成一个无参构造。如果你不幸覆盖了这个无参的构造方法,即在无参构造方法中写入参数,又无显示声明一个无参构造方法。那么Spring则无法给你创建对象。

举个香甜的栗子:

还是这个Spring01类

public class Spring01 {

    public Spring01(){
        System.out.println("instance..");
    }

    public void testSpring() {
        System.out.println("hello word");
    }
}

我们重写了无参构造方法,并在构造方法执行的时候输出一句话。

再看测试类

public class TestSpring {

    /***
     * 1.启动spring容器
     * 2.从spring容器中把对象取出来
     * 3.对象调用方法
     */
    @Test
    public void testSpring() {
        // 启动/加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过id从Spring容器中拿出对象
        Spring01 spring = (Spring01) context.getBean("test");
        // 对象调用方法
        spring.testSpring();
        System.out.println("after...");
    }

}

没什么大变化,只在最后一句加了个输出语句。

Spring配置文件无变化,现在我们运行此测试方法,得到如下输出:

instance..
hello word
after...

可以很好的验证Spring容器是调用无参构造方法来创建对象,现在我们把这个无参的构造方法改写成有参的,如下。

public class Spring01 {

    //有参数的构造方法
    public Spring01(String name){
        System.out.println("instance..");
    }

    public void testSpring() {
        System.out.println("hello word");
    }
}

传入一个参数,现在再运行测试类,则控制台会报错。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘test‘ defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.liuzehui.spring.Spring01]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.liuzehui.spring.Spring01.<init>()
...
No default constructor found;

可知,没有默认的构造方法。则Spring容器无法创建对象

Spring容器创建对象的时机

什么是时机,就是Spring在什么时候创建对象的,在加载Spring容器时创建呢?还是在getBean时创建呢?

由刚才的经验就很可以测试第一个问题,在getBean之前输出一句话,看Spring01 类的构造方法是否输出instance,如果输出,则说明对象在Spring容器启动的时候就被创建了。

package com.liuzehui.test;

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

import com.liuzehui.spring.Spring01;

public class TestSpring {

    /***
     * 1.启动spring容器
     * 2.从spring容器中把对象取出来
     * 3.对象调用方法
     */
    @Test
    public void testSpring() {
        // 启动/加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        System.out.println("not get object from Spring...");
        // 通过id从Spring容器中拿出对象
        Spring01 spring = (Spring01) context.getBean("test");
        // 对象调用方法
        spring.testSpring();
        System.out.println("after...");
    }

}

测试后输出:

instance..
not get object from Spring...
hello word
after...

根据输出的顺序,可以知道,先加载Spring容器,Spring容器通过反射加载class,然后调用此类的无参构造方法创建对象,因此会输出instance…

你肯定会想了,这样我Spring容器中有一万个对象,那么我不管用不用这些对象,只要在我加载Spring容器的时就创建他们? 那岂不是很占用内存空间?我能不能在使用某个对象的时候再创建他呢?答案是肯定的

lazy-init:他有三个值可供选择,default/fasle/true

因为我们知道默认的时候是default也就是在加载Spring容器的时候就创建对象,所以default表示在加载Spring容器的时候创建对象,而且false的效果和default等同,那么如果我设置lazy-init=true会有什么效果呢?

    <bean id="test" class="com.liuzehui.spring.Spring01" lazy-init="true"></bean>

控制台输出以下结果:

not get object from Spring...
instance..
hello word
after...

由上可见,在加载Spring容器的时候并未创建对象,而是在要使用他的时候才创建的对象。

这样有个好处就是,我在用你的时候再创建对象,如果不用则不创建。

那么这两种方法怎么选择呢?答案是选择第一种,因为如果在加载容器的时候创建对象有问题能及早发现(配置bean写错等问题),第二种则不会及早发现错误。

Spring的bean中的scope

Spring的单例和多例,Spring默认用单例来管理对象的,为什么这么说呢,单例就是一个对象,从Spring容器中获取两次对象,比较一下就知道了

package com.liuzehui.test;

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

import com.liuzehui.spring.Spring01;

public class TestSpring {

    /***
     * 1.启动spring容器
     * 2.从spring容器中把对象取出来
     * 3.对象调用方法
     */
    @Test
    public void testSpring() {
        // 启动/加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过id从Spring容器中拿出对象
        Spring01 spring1 = (Spring01) context.getBean("test");
        Spring01 spring2 = (Spring01) context.getBean("test");
        System.out.println(spring1);
System.out.println(spring2);

    }

}

控制台输出:

com.liuzehui.spring.Spring01@900bac2
com.liuzehui.spring.Spring01@900bac2

也可以比较这两个对象的hashCode值是否相等。

如果要想把单例变为多例则需要使用scope属性的prototype属性

scope:默认是singleton,表示单例。

scope=”prototype”:表示多例。可以把一个bean由单例改为多例。

scope=”request”,在WEB应用程序中,每一个实例的作用域都为request范围;

scope=”session”,在WEB应用程序中,每一个实例的作用域都为session范围;

再进行 测试:

    <bean id="test" class="com.liuzehui.spring.Spring01" scope="prototype"></bean>

测试代码不变, 运行后控制台输出:

com.liuzehui.spring.Spring01@421fbfd6
com.liuzehui.spring.Spring01@326b9c84

可知,通过scope把单例改为多例。

注意:当scope=”prototype”时,在getBean时,创建对象。这样就在SSH整合的时候很完美了,因为把Strust2的action放入Spring容器中,总不能在启动Spring容器就创建对象吧,应该在请求action的时候创建对象,所以,在使用的时候才创建对象(请求此action)。

单例多例需要搞明白两个问题:

1. 什么是单例多例;

2. 如何产生单例多例;

3. 为什么要用单例多例

4. 什么时候用单例,什么时候用多例;

1. 什么是单例多例:

所谓单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的,而多例则指每个请求用一个新的对象来处理,比如action;

2. 如何产生单例多例:

在通用的SSH中,单例在spring中是默认的,如果要产生多例,则在配置文件的bean中添加scope=”prototype”;

3. 为什么用单例多例:

之所以用单例,是因为没必要每个请求都新建一个对象,这样子既浪费CPU又浪费内存;

之所以用多例,是为了防止并发问题;即一个请求改变了对象的状态,此时对象又处理另一个请求,而之前请求对对象状态的改变导致了对象对另一个请求做了错误的处理;

用单例和多例的标准只有一个:

当对象含有可改变的状态时(更精确的说就是在实际应用中该状态会改变),则多例,否则单例;

4. 何时用单例?何时用多例?

对于struts2来说,action必须用多例,因为action本身含有请求参数的值,即可改变的状态;

你用杯子喝可乐,喝完了不刷,继续去倒果汁喝,就是单例。始终在用一个对象。

你用杯子喝可乐,直接扔了杯子,换个杯子去倒果汁喝,就是多例,用一个则创建一个对象。

Spring-DI

DI就是依赖注入(Dependency Injection,依赖注入)

什么是依赖注入?依赖注入说白了就是为属性赋值,和java一样,为属性赋值可以同过setter方法或者构造方法赋值,在实际开发中在配置文件中为属性进行依赖注入的情况比较少,这里先按下不表,等以后用注解来讲解Spring的DI。

案例:用SpringIOC和DI模拟MVC

StudentDao:

package com.liuzehui.spring.mvc;

public interface StudentDao {
    // 保存学生
    public void save();
}

StudentDaoImpl:

package com.liuzehui.spring.mvc;

public class StudentDaoImpl implements StudentDao {

    @Override
    public void save() {
        System.out.println("save student...");
    }

}

StudentService:

package com.liuzehui.spring.mvc;

public interface StudentService {
    // 保存学生
    public void save();
}

StudentServiceImpl:

package com.liuzehui.spring.mvc;

public class StudentServiceImpl implements StudentService {

    // 注入studentDao
    private StudentDao studentDao;

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    @Override
    public void save() {
        studentDao.save();
    }

}

StudentAction:

package com.liuzehui.spring.mvc;

public class StudentAction {

    // 注入StudentService
    private StudentService studentService;

    public StudentService getStudentService() {
        return studentService;
    }

    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }

    // 执行保存操作
    public void saveStudent() {
        studentService.save();
    }

}

Spring配置文件:

<!-- studentDao -->
    <bean id="studentDao" class="com.liuzehui.spring.mvc.StudentDaoImpl"></bean>

    <!-- studentService -->
    <bean id="studentService" class="com.liuzehui.spring.mvc.StudentServiceImpl">
        <property name="studentDao">
            <ref bean="studentDao"/>
        </property>
    </bean>

    <!-- studentAction -->
    <bean id="studentAction" class="com.liuzehui.spring.mvc.StudentAction">
        <property name="studentService">
            <ref bean="studentService"/>
        </property>
    </bean>

测试类:

package com.liuzehui.test;

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

import com.liuzehui.spring.mvc.StudentAction;

public class TestSpring {

    /***
     * 1.启动spring容器 2.从spring容器中把对象取出来 3.对象调用方法
     */
    @Test
    public void testSpring() {
        // 启动/加载Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过id从Spring容器中拿出对象
        StudentAction studentAction = (StudentAction) context
                .getBean("studentAction");
        studentAction.saveStudent();
    }

}

控制台输出:

save student...

用注解 模拟MVC

StudentDaoImpl:

@Repository
public class StudentDaoImpl implements StudentDao {

    @Override
    public void save() {
        System.out.println("save student...");
    }

}

StudentServiceImpl:

package com.liuzehui.spring.mvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

    // 注入studentDao
    @Autowired
    private StudentDao studentDao;

    @Override
    public void save() {
        studentDao.save();
    }

}

StudentAction:

package com.liuzehui.spring.mvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("prototype")
public class StudentAction {

    // 注入StudentService
    @Autowired
    private StudentService studentService;

    // 执行保存操作
    public void saveStudent() {
        studentService.save();
    }

}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:component-scan base-package="com"></context:component-scan>

</beans>

运行测试代码输出:

save student...

补充

这是我以前复制下来的,原文地址已经找不到了。作者写得太生动了!

首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

  那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。

  

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

如果还不明白,放弃java吧!

时间: 2024-10-10 01:31:02

Spring连载(1)的相关文章

《02.Spring Boot连载:Spring Boot实战.Spring Boot核心原理剖析》

在上节中我们通过了一个小的入门案例已经看到了Spring Boot的强大和简单之处.本章将详细介绍Spring Boot的核心注解,基本配置和运行机制.笔者一直认为:精通一个技术一定要深入了解这个技术帮助我们做了哪些动作,深入理解它底层的运行原理,只有达到这个目标才可以熟练使用框架,最终达到融会贯通的目的. 一.启动类与@SpringBootApplication Spring Boot的项目一般都会有注解*Application标注的入口类,入口类中会有一个main方法,main方法是一个标准

《01.Spring Boot连载:Spring Boot入门介绍》

1 Spring Boot的概述 Spring Boot是开发者和Spring 本身框架的中间层,帮助开发者统筹管理应用的配置,提供基于实际开发中常见配置的默认处理(即习惯优于配置),简化应用的开发,简化应用的运维:总的来说,其目的Spring Boot就是为了对Java web 的开发进行"简化"和加"快"速度,简化开发过程中引入或启动相关Spring 功能的配置.这样带来的好处就是降低开发人员对于框架的关注点,可以把更多的精力放在自己的业务代码上. 同时随着微服

大话重构连载5:软件修改的四种动机

软件,自从被我们开发出来并交付使用以后,如果它运行得好好的,我们是不会去修改它的.我们要修改软件,万变不离其宗,无非就是四种动机: 1. 增加新功能: 2. 原有功能有BUG: 3. 改善原有程序的结构: 4. 优化原有系统的性能[1]. 第一种和第二种动机,都是源于客户的功能需求,而第四种是源于客户的非功能需求. 软件的外部质量,其衡量的标准就是客户对软件功能需求与非功能需求的满意度.它涉及到一个企业.一个软件的信誉度与生命力,因此为所有软件企业所高度重视.但是,就在所有企业高管把软件外部质量

学习Spring必学的Java基础知识(1)----反射

引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助.): [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http://www.iteye.com/topic/1123293 [3] 属性

学习Spring必学的Java基础知识(2)----动态代理

学习Spring必学的Java基础知识(2)----动态代理 引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助.): [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http://www

事务基础知识--&gt;Spring事务管理

Spring虽然提供了灵活方便的事务管理功能,但这些功能都是基于底层数据库本身的事务处理机制工作的.要深入了解Spring的事务管理和配置,有必要先对数据库事务的基础知识进行学习. 何为数据库事务 “一荣俱荣,一损俱损”这句话很能体现事务的思想,很多复杂的事物要分步进行,但它们组成一个整体,要么整体生效,要么整体失效.这种思想反映到数据库上,就是多个SQL语句,要么所有执行成功,要么所有执行失败. 数据库事务有严格的定义,它必须同时满足 4 个特性:原子性(Atomic).一致性(Consist

XML基础知识--&gt;Spring配置

XML的特殊字符 XML中共有5个特殊的字符,分别是:&<>“’.如果配置文件中的注入值包括这些特殊字符,就需要进行特别处理.有两种解决方法:其一,采用本例中的<![CDATA[ ]]>特殊标签,将包含特殊字符的字符串封装起来:其二,使用XML转义序列表示这些特殊的字符,这5个特殊字符所对应XML转义序列在表4-2中说明: Spring在进行XML配置时,如果属性值包含了一个XML的特殊符号,因此我们特意在属性值外添加了一个<![CDATA[ ]]>的XML特殊

Java动态代理--&gt;Spring AOP

引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助.): [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http://www.iteye.com/topic/1123293 [3] 属性

连载:面向对象葵花宝典:思想、技巧与实践(38) - 设计模式之道

很多人能够熟练背诵出所有的设计模式,能够快速画出各种设计模式的UML类图,也能够熟练的写出<设计模式>一书中各个模式的样例代码.但一到实际的项目设计和开发的时候,往往都会陷入迷茫:要么无从下手,不知道哪个地方要用设计模式:要么生搬硬套,胡乱使用设计模式,将方案和代码搞得一团乱麻. =========================================================================== [知易行难 -- 设计模式应用的问题] 形而下者谓之器,形而上者