spring学习-1

spring


一、spring是什么

      spring是分层的javaSE/EE应用的全栈轻量级开源框架,以及ioc(控制反转)和aop(面向切面编程:提供公共的经常用的类。例如日志)为内核,提供了展现层springMVC和持久层SpringJDBC以及业务层事务管理等众多的企业级应用技术。还能整合开源的许多第三方框架和类库。

二、spring优势

1.方便解耦,简化开发

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

2.aop编程的支持

      1.通过AOP(面向切面编程)功能,方便进行面向切面的编程,许多不容易用传统OOP(面向对象编程)实现的功能可以通过AOP轻松应付。(例如:编写日志)。
      2.OOP(面向对象编程)引入了继承、多态、封装,将系统的业务功能按照模块划分,每个模块用一个或多个类来表示。这样做的好处是降低了代码的复杂程度,使类可重用。
      3.AOP和OOP的关系:3.1AOP是对OOP的一种补充。
                                           3.2AOP即为面向切面编程,它把系统需求按照功能分门归类,把它们封装在一个个切面中,然后再指定这些系统功能往业务功能中织入的规则。最后由第三方机构根据你指定的织入规则,将系统功能整合到业务功能中。(注:OOP是对系统功能的封装到切面中)

3.声明式事务的支持

通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

4.方便程序的测试

可以用非容器依赖(结合junit)进行测试工作。

5.方便集成各种优秀框架

spring可以降低各种框架的使用难度,提供了对各种优秀的框架的支持。

6.降低javaEE API的使用难度

spring对javaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。

二、解决耦合问题

Bean:在计算机英语中,有可重用组件的含义。
JavaBean:用Java语言编写的可重用组件。

解决耦合中类依赖(反复通过new来获取类的代码依赖)的办法:

1.Bean工厂解决耦合

      1.定义一个Bean工厂来创建这些类对象。(方法:第一步:创建一个配置文件来配置我们的类对象的内容,唯一表示=全限定类名–>“key=value”;
第二步:通过读取配置文件中的配置的内容,反射创建对象。

第一步:写配置文件(将需要通过反射创建对象的全限定类名写入配置文件中)代码如下:

//文件名:bean.properties
accountService=com.fjut.service.impl.AccountServiceImpl
accountDao=com.fjut.dao.impl.AccountDaoImpl

第二步:(多例对象。)创建工厂类,读取配置文件,通过反射机制(通过全限定类名创建类):代码如下:

package com.fjut.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
    private static Properties properties;
    static{
        try {
            properties = new Properties();
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            properties.load(in);
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            bean = Class.forName(beanPath).newInstance();       //反射创建对象(调用默认构造创建对象)
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return bean;
    }
}

       上面代码是创建多例对象,问题:多例对象效率低、占用内存多,当多个线程同时操作时不能实现对同一对象中的成员变量的改变(它会创建新的对象,初始化新的成员变量)。
结果:
在这里插入图片描述

package com.fjut.service.impl;

import com.fjut.dao.IAccountDao;
import com.fjut.factory.BeanFactory;
import com.fjut.service.IAccountService;

public class AccountServiceImpl implements IAccountService{
    private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
    private int i = 1;
    public void saveAccount() {
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }
}

       解决方法:将反射创建对象代码放入static(静态代码块中)。代码如下:

package com.fjut.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {
    private static Properties properties;
    private static Map<String,Object> beans;
    static{
        try {
            //实例化对象
            properties = new Properties();
            //获取properties文件的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            properties.load(in);
            //实例化容器
            beans = new HashMap<String, Object>();
            //取出配置文件所有的key
            Enumeration keys = properties.keys();
            //遍历枚举取出每一个key
            while(keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = properties.getProperty(key);
                //通过反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                beans.put(key,value);
                //
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
}

创建的是单例对象
结果:
若变量在方法中:可以实现变量值不变。

 public void saveAccount() {
        int i = 1;
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }

在这里插入图片描述
若变量为成员变量,则变量会改变。
结果:

private int i = 1;
    public void saveAccount() {
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }

在这里插入图片描述

第三步:解耦。(在对应需要用到new来获取类对象的地方通过工厂中的getBean()方法来反射创建);代码如下:

//例如:servlet---调用--->service
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
        accountService.saveAccount();
        
//service---调用--->dao
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
    public void saveAccount() {
        accountDao.saveAccount();
    }

注:工厂模式解耦。如果删除了bean.properties对应的全限定类名下的类文件,编译期错误不会出现(就是可以通过编译),但运行时会出现ClassNotFoundException(类找不到异常)。

2.ioc(控制反转)容器解决

       ioc作用:降低耦合(减少类依赖和方法依赖)。若自己写代码来达到ioc的效果就采用工厂模式来解决,如上的Bean工厂。

使用spring中的ioc准备:
       1.准备spring开发包

在pom.xml文件中添加依赖,让它自动下载。若做拷贝有可能出现错误。(此操作要修改maven的下载路径后才会不是很慢。要在连网状态下)
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>

       2.写bean.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把对象的创建交给spring来管理-->
    <!--id:是获取时的唯一标志;class:是反射要创建的全限定类名;-->
    <bean id="accountService" class="com.fjut.service.impl.AccountServiceImpl"></bean>

    <bean id="accountDao" class="com.fjut.dao.impl.AccountDaoImpl"></bean>
</beans>

       3.创建对象

		//获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //根据唯一标识id获取对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.saveAccount();

测试创建的对象是单例还是多例?

//获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //根据唯一标识id获取对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(as);
            as.saveAccount();
        }

结果:创建的对象是单例的。
在这里插入图片描述
利用BeanFactory(org.springframework.beans.factory.BeanFactory)创建对象:代码如下: --创建的是多例对象–

		Resource resource = new ClassPathResource("bean.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        IAccountService ias = (IAccountService) factory.getBean("accountService");
        ias.saveAccount();

三、核心容器的两个接口引发的问题:

       ApplicationContext: 单例对象下适用 (开发中经常使用此方法)
              它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。(只有一读取完配置文件中的配置对象就会创建对象);也就是说执行了ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);就会创建对象。

       BeanFatory: 多例对象下适用
              它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。(什么时候根据id获取对象了,什么时候才创建对象);也就是说执行了IAccountService ias = (IAccountService) factory.getBean(“accountService”);才会创建对象。

四、创建bean(可重用组件)的三种方式:

       第一种方式:使用默认构造函数创建。

<bean id="accountService" class="com.fjut.service.impl.AccountServiceImpl"></bean>

       第二种方式:使用普通工厂中的创建对象的方法(返回值是bean(可重用组件)对象的方法)或jar包中的对象。

	<bean id="instanceFactory" class="工厂的全限定类名"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="工厂中返回值是所需bean对象的方法名"></bean>

       第三种方式:使用工厂中的静态方法创建对象。

<bean id="accountService" class="静态创建对象工厂的全限定类名" factory-method="静态创建对象的方法名"></bean>

五、xml中bean的作用范围

通过bean标签的scope属性:prototype(多例创建)常用
                                              singleton(单例创建,也是默认值)常用
                                              request(作用于web应用的请求范围)
                                              session(作用于web应用的会话范围)
                                              global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session。

bean对象的声明周期:singleton(单例的),读取配置文件创建容器时出生,容器(ClassPathXmlApplicationContext)消亡时消亡。
                                     prototype(多例的),使用对象时创建(IAccountService ias = (IAccountService) factory.getBean(“accountService”);),当长时间不用被系统认为是垃圾时,垃圾回收机制执行的时候被销毁。

六、依赖注入

1.构造函数注入。

一般不用(原因:若只想构造部分变量,则其他变量也必须注入值)-----
            使用标签:constructor-arg
            标签中出现的位置:bean标签的内部。
            标签中的属性:type:用于指定要注入构造函数中的数据类型,要对应上构造函数的的类型。
                                    index:用于指定要注入的数据给狗子函数中指定索引位置的参数赋值(从0开始)。
                                    name(常用):用于给构造函数中指定名称的参数赋值。
                                    value:用于提高基本类型和String类型的数据。
                                    ref:用于指定其他的bean类型数据,它值的技术在spring的ioc的核心容器中出现过的bean对象(也就是在xml文件中定义过的bean标签的关联对象)。

 <bean id="domain对象的唯一标识" class="实体类全限定类名">
        <constructor-arg name="有参构造的参数名1" value="想要注入来初始化的字符串或基本数据类型的数据"></constructor-arg>
        <constructor-arg name="有参构造的参数名2" value="想要注入来初始化的字符串或基本数据类型的数据"></constructor-arg>
        <constructor-arg name="有参构造的参数名3" value="ioc的核心容器内的bean对象的唯一标识"></constructor-arg>
    </bean>
    <!--参数3例子:日期格式转换-->
    <bean id="ioc的核心容器内的bean对象的唯一标识" class="java.util.Date"></bean>

2.用set方法注入

常用。
注意事项:创建对象的类中要有get和set方法(通过快捷键生成的方法)。

get和set方法

	public String getXxx1() {
        return name;
    }

    public void setXxx1(String name) {
        this.name = name;
    }
    public String getXxx2() {
        return name;
    }

    public void setXxx2(String name) {
        this.name = name;
    }
    public String getXxx3() {
        return name;
    }

    public void setXxx3(String name) {
        this.name = name;
    }
	<bean id="domain对象的唯一标识" class="实体类全限定类名">
        <property name="xxx1" value="想要注入来初始化的字符串或基本数据类型的数据"></property>
        <property name="xxx2" value="想要注入来初始化的字符串或基本数据类型的数据"></property>
        <property name="xxx3" value="ioc的核心容器内的bean对象的唯一标识"></property>
    </bean>
    <!--参数3例子:日期格式转换-->
    <bean id="ioc的核心容器内的bean对象的唯一标识" class="java.util.Date"></bean>

3.复杂类型的注入

<bean id="domain对象的唯一标识" class="实体类全限定类名">
        <!--下面这些name都是set方法注入的,要有对应的set方法才能注入-->
        <property name="xxxArray">
            <array>
                <value>想要注入来初始化的字符串或基本数据类型的数据1</value>
                <value>想要注入来初始化的字符串或基本数据类型的数据2</value>
                <value>想要注入来初始化的字符串或基本数据类型的数据3</value>
            </array>
        </property>
        <property name="xxxMap">
            <map>
                <entry key="testA" value="想要注入来初始化的字符串或基本数据类型的数据1"></entry>
                <entry key="testB" value="想要注入来初始化的字符串或基本数据类型的数据2"></entry>
            </map>
        </property>
        <property name="xxxProps">
            <props>
                <prop key="testC">想要注入来初始化的字符串或基本数据类型的数据1</prop>
                <prop key="testD">想要注入来初始化的字符串或基本数据类型的数据2</prop>
            </props>
        </property>
    </bean>

总结

1.了解spring的ioc可以解决依赖注入的问题:即需要在一个类中调用另一个类时需要进行new的方式,出现的类依赖问题。(解决方法:使用ioc方式解决,见本文章中”2.ioc(控制反转)容器解决“部分)。
2.如何搭建出spring的基于xml的ioc的开发环境。(”2.ioc(控制反转)容器解决“中的”使用spring中的ioc准备“部分)
3.如何实现类之间的依赖注入关系。(常用set方法注入)

springIoc——控制反转
控制:通过spring核心容器去控制对象的创建
反转:程序不主动去创建对象,而是被动的去spring核心容器中接收对象
依赖注入:
通过set方法注入核心容器中的对象
依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

@Autowired与@Resource异同:@Autowired先byType注入,再通过byName注入;
@Resource先byName注入,再通过byType注入。
singleton与prototype:异同:singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

XML与注解比较:
XML可以适用任何场景 ,结构清晰,维护方便
注解不是自己提供的类使用不了,开发简单方便

纵向开发和横向编程的对应的模块图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值