Spring=Spring基础详解

spring官网:https://spring.io/

一.Spring概述

Spring是分层的 Java SE/EE应用 full-stack(全栈) 轻量级开源框架

Spring的核心是 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)

Spring一个全栈应用框架, 提供了表现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多应用技术

Spring还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架

Spring官网:https://spring.io/

(1)发展历程:

* EJB
	1997 年,IBM提出了EJB 的思想
    1998 年,SUN制定开发标准规范 EJB1.0
    1999 年,EJB1.1 发布
    2001 年,EJB2.0 发布
    2003 年,EJB2.1 发布
    2006 年,EJB3.0 发布
    
* Spring
	Rod Johnson( Spring 之父)
		改变Java世界的大师级人物
		
    2002年编著《Expert one on one J2EE design and development》	
   		指出了JavaEE和EJB组件框架中的存在的一些主要缺陷;提出普通java类依赖注入更为简单的解决方案。
   		
    2004年编著《Expert one-on-one J2EE Development without EJB》
    	阐述了JavaEE开发时不使用EJB的解决方式(Spring 雏形)
    	同年4月spring1.0诞生
    	
    2006年10月,发布 Spring2.0
	2009年12月,发布 Spring3.0
	2013年12月,发布 Spring4.0
   	2017年9月, 发布最新 Spring5.0 通用版(GA)

(2)Spring开发优势:

1. 方便解耦,简化开发
	通过Spring提供的 IOC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。  用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

2. AOP编程的支持
	通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
	
3. 声明式事务的支持
	可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。

4. 方便程序的测试
	可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
	
5. 方便集成各种优秀框架
	Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。

6. 降低JavaEE API的使用难度
	Spring对JavaEEAPI(如JDBC、JavaMail、RPC等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

7. Java源码经典学习范例
	Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

(3)Spring体系结构:

 

二.初识IOC(控制反转)

(1)介绍:

控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。

控制:在java中指的是对象的控制权限(创建、销毁)

反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制

比如:

* 原来我们需要一个对象,需要自己手动new出来
	UserServlet
		UserService us = new UserServiceImpl();
	

* 学习了spring之后,需要一个对象,从spring的ioc容器获取		
	UserServlet
		UserService us = spring的IOC容器.getBean("userService");
		
* IOC的核心思想:松耦合

需求:实现service层和dao层代码解耦合

(2)案例

新建idea项目:

项目完毕:

统一进行设置:

  1. 创建Maven的java模块:

导入maven 的jar包

<!--依赖管理-->
<dependencies>
    <!--dom4j-->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <!--xpath-->
    <dependency>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1.6</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

(2)实现方案比较:

==版本1==原始版本

编写UserDao接口和实现类:快捷键alt+insert

编写UserService接口和实现类:

编写UserTest

问题总结;

==版本2==工厂解耦

我们需要在项目中 定义BeanFactory + xml解析,实现各层代码之间解耦合

编写beans.xml

编写BeanFactory

public class BeanFactory {

    public static Object getBean(String id) {
        Object object = null;
        try {
            // 1.通过类加载器读取 beans.xml
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            // 2.创建dom4j核心解析器对象
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(in);
            // 3.编写xpath表达式
            // String xpath = "//bean[@id='userDao']";
            String xpath = "//bean[@id='" + id + "']";
            // 4.获取指定id的标签对象
            Element element = (Element) document.selectSingleNode(xpath);
            // 5.获取全限定名
            String className = element.attributeValue("class");
            // 6.通过反射创建对象实例
            object = Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 7.返回对象实例
        return object;
    }
}

修改UserServiceImpl

问题:

==版本3== 工厂优化:

使用单例模式:

修改BeanFactory

public class BeanFactory {

    // 声明存储对象的容器(map集合)
    private static Map<String, Object> ioc = new HashMap<>();

    // 静态代码块,初始化ioc容器
    static {
        String id = null;
        String className = null;
        Object object = null;
        try {
            // 1.通过类加载器读取 beans.xml
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            // 2.创建dom4j核心解析器对象
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(in);
            // 3.编写xpath表达式
            String xpath = "//bean";
            // 4.获取所有的bean标签对象
            List<Element> list = document.selectNodes(xpath);
            // 5.遍历集合,创建对象实例,设置到ioc容器中
            for (Element element : list) {
                id = element.attributeValue("id");
                className = element.attributeValue("class");
                object = Class.forName(className).newInstance();
                // 设置到map集合
                ioc.put(id, object);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 从ioc容器中获取指定id的对象实例
    public static Object getBean(String id) {
        return ioc.get(id);
    }
}

总结

对象的创建由原来的 使用 new关键字在类中主动创建 变成了 从工厂中获取, 而对象的创建过程由工厂内部来实现, 而这个工厂就是 Spring的IOC容器, 也就是以后我们的对象不再自己创建,而是直接向Spring要, 这种思想就是IOC

ioc目的:松耦合...,你可以简单的认为就是一个map集合(ioc容器)

三.Spring入门:

流程图:

代码实现

创建maven的java模块:

导入spring相关坐标:

<!--依赖管理-->
<dependencies>
    <!--spring的核心坐标-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

编写UserDao接口和实现类:

创建spring的核心配置文件,导入约束

官方推荐:applicationContext.xml

编写bean标签:(id,class)

测试:模拟service层

public class UserTest {

    // spring的快速入门
    @Test
    public void test01() throws Exception {
        // 1.通过spring的api读取配置文件
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2.获取指定id的对象实例
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

 

四/Spring相关API

spring的api体系非常的庞大,我们先介绍核心的内容

(1)两个接口
 

* BeanFactory
	介绍:
		这是IOC容器的顶级接口 它定义了IOC的最基础的功能, 但是其功能比较简单,一般面向Spring自身使用
	特点:
		在第一次使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化[用的时候再创建]
		懒汉设计

* ApplicationContext
	介绍:
		这是在BeanFactory基础上衍生出的接口,它扩展了BeanFactory的功能,一般面向程序员使用
	特点:
		在容器启动时,一次性创建并加载了所有的Bean  [初始化的时候全创建好]
		恶汉设计
		
* 小结:上面两种方式创建的对象都是单例, 只是创建对象的时机不同

 

(2)三个实现类:

* ClassPathXmlApplicationContext
	功能:
		读取类路径(classpath)下的xml配置文件
		
* FileSystemXmlApplicationContext
	功能:
		读取本地磁盘下的xml配置文件
		
* AnnotationConfigApplicationContext
	功能:
		读取java配置类加载配置

(3)一个方法:

* public Object getBean(String name) throws BeansException;
	功能:
		通过指定id获取对象的实例,需要手动强转
		
* public <T> T getBean(Class<T> requiredType);
	功能:
		通过指定类型获取对象的实例,不需要强转

* public <T> T getBean(String name, Class<T> requiredType);
	功能:
		通过指定id和类型获取对象的实例
    // getBean方法介绍
    @Test
    public void test02() throws Exception {
        // 1.通过spring的api读取配置文件
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 方式一: 通过指定id获取对象的实例,需要手动强转
        /*
             UserDao userDao1 = (UserDao) app.getBean("userDao");
            userDao1.save();
        */

        // 方式二:通过指定(接口)类型获取对象的实例,不需要强转
        /*
                缺点:如果同一个接口类型下有多个对象实例,会报错的...
                UserDao userDao2 = app.getBean(UserDao.class);
                userDao2.save();
        */

        // 方式三:通过指定id和类型获取对象的实例
        UserDao userDao3 = app.getBean("userDao", UserDao.class);
        userDao3.save();

    }

五.Spring配置文件

(1)Bean标签基本配置:

基本配置:

    <!--
        bean标签的二个基本属性
            id:在ioc容器的唯一标识
            class:创建对象实例的全限定名
    -->
    <bean id="userDao" class="com.wsl.dao.impl.UserDaoImpl"></bean>

作用范围:

    <!--
        bean标签还有一个属性scope属性:声明此对象的作用范围
            singleton:单例对象(默认)
                何时创建?
                    ioc容器初始化时,创建对象
                对象运行?
                    只要ioc容器在,对象就一直活着....
                何时销毁?
                    ioc容器关闭时,销毁对象
            prototype:多例对象
                何时创建?
                    在调用getBean()方法时,创建
                对象运行?
                    一直使用就一直活着
                何时销毁?
                    当对象不再使用后,根据JVM GC机制垃圾回收
    -->
    <bean id="userDao" class="com.wsl.dao.impl.UserDaoImpl" scope="prototype"></bean>

生命周期:

 

(2)Spring创建对象实例的三种方式:

  ==无参构造方法实例化:使用最多:

在企业开发中,所有的类必须提供无参构造方法:

==工程静态方法实例化

依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

/*
    工厂静态方法实例化对象
 */
public class StaticFactoryBean {

    public static UserDao createUserDao() {
        return new UserDaoImpl();
    }

    // 传统方式,自己通过工厂获取对象...
    public static void main(String[] args) {
        UserDao userDao = StaticFactoryBean.createUserDao();
    }
}

工厂普通方法实例化:

依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

/*
    工厂普通方法实例化对象
 */
public class DynamicFactoryBean {

    public UserDao createUserDao() {
        return new UserDaoImpl();
    }


    // 传统方式,自己通过工厂获取对象...
    public static void main(String[] args) {
        // 1.创建工厂对象
        DynamicFactoryBean dynamicFactoryBean = new DynamicFactoryBean();
        // 2.创建UserDao对象
        UserDao userDao = dynamicFactoryBean.createUserDao();
    }
}

 

(3)Bean依赖注入:

依赖注入(Dependency Injection, DI) 它是 Spring 框架核心 IOC 的具体实现

其实就是给对象中的属性赋值的过程,通过spring完成依赖注入

案例环境搭建:

创建maven的java模块:

创建UserDao,UserService等文件

编写Spring的核心配置文件:

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

    <!--将userDao交给ioc容器-->
    <bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl"></bean>
    <!--将userService交给ioc容器-->
    <bean id="userService" class="cn.wsl.service.impl.UserServiceImpl"></bean>
</beans>

测试,报错:

由于service依赖了dao,但是缺少依赖注入环境

public class UserTest {

    @Test
    public void test01() throws Exception {
        // 1.加载spring配置文件
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2.获取service对象实例
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }

 

(4)Bean依赖注入方式:

===构造方法:

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}
    <!--
        构造方法注入: <constructor-arg> 子标签
            版本一:
                name:构造方法参数名称
                value:简单数据类型(String、int、double...)
                ref:引用数据类型(从ioc容器中获取的对象)
            版本二:
                index:构造方法参数索引
                type:该索引对应的java类型(全限定名)
                value:简单数据类型(String、int、double...)
                ref:引用数据类型(从ioc容器中获取的对象)
    -->
    <bean id="userService" class="cn.wsl.service.impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        <!--<constructor-arg index="0" type="cn.wsl.dao.UserDao"  ref="userDao"></constructor-arg>-->
    </bean>

==set方法:项目中使用

public class UserServiceImpl implements UserService {

    private UserDao userDao;


    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

}
    <!--将userDao交给ioc容器-->
    <bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl"></bean>  

    <!--
            set方法注入:<property> 子标签
                name:set方法的属性名  setUserDao()  -> UserDao -> userDao
                value:简单数据类型(String、int、double...)
                ref:引用数据类型(从ioc容器中获取的对象)
    -->
    <bean id="userService" class="cn.wsl.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

==P 命名空间注入

P命名空间注入底层(本质)使用的也是set方法注入,只是在上着的基础上进行简化

1)导入P命名空间约束

2)使用P命名空间完成注入

 

(5)Bean依赖注入的数据类型:

 简单数据类型

引用数据类型:见上

集合数据类型

单列集合:list,set,array

public class UserDaoImpl implements UserDao {

    private List<Object> list;

    private Set<Object> set;

    private Object[] array;

    public void setList(List<Object> list) {
        this.list = list;
    }

    public void setSet(Set<Object> set) {
        this.set = set;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

    @Override
    public void save() {
        System.out.println("UserDao保存了....");
        System.out.println(list);
        System.out.println(set);
        System.out.println(Arrays.toString(array));
    }
}
   <!--
        di 注入单列集合类型
            需要在 <property>标签中
                list集合  使用子标签  <list>
                    <value> 简单数据类型
                    <ref>   引用数据类型(对象在ioc容器中)

              set集合    使用子标签 <set>
                    <value> 简单数据类型
                    <ref>   引用数据类型(对象在ioc容器中)

             array数组   使用子标签<array>
                    <value> 简单数据类型
                    <ref>   引用数据类型(对象在ioc容器中)
    -->
    <bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl">
        <property name="list">
            <list>
                <value>石榴</value>
                <value>秋香</value>
                <ref bean="user"></ref>
            </list>
        </property>

        <property name="set">
            <set>
                <value>秋奎</value>
                <value>马梅</value>
                <ref bean="user"></ref>
            </set>
        </property>

        <property name="array">
            <array>
                <value>国峰</value>
                <value>小羊</value>
                <ref bean="user"></ref>
            </array>
        </property>
    </bean>

双列集合

map,properties

public class UserDaoImpl implements UserDao {

    private Map<String, Object> map;

    private Properties props;

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

    @Override
    public void save() {
        System.out.println("UserDao保存了....");
        System.out.println(map);
        System.out.println(props);
    }

}
<!--
           di 注入双列集合类型
              需要在 <property>标签中
                map集合 使用子标签<map>
                     <entry key="" value="简单数据类型"  |  value-ref="引用数据类型(对象ioc中)"  ></entry>
               properties集合 使用子标签 <props>
                     <prop key="" >value</prop>

    -->
<bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl">
    <property name="map">
        <map>
            <entry key="k1" value="v1"></entry>
            <entry key="k2" value="v2"></entry>
            <entry key="u1" value-ref="user"></entry>
        </map>
    </property>

    <property name="props">
        <props>
            <prop key="k1" >v1</prop>
            <prop key="k2" >v2</prop>
            <prop key="k3" >v3</prop>
        </props>
    </property>
</bean>

(6)配置文件模块化

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。

==并列加载:

==主从配置:

注意:

1.在同一个xml中 bean标签的 id不能重复....

2.在多个模块中的xml中,bean标签id重复虽然不会报错,但是后加载的会覆盖先加载的....

在开发中绝不允许出现重名的id

 

学习资源:源码解读

死磕Spring

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值