Spring

本文详细介绍了Spring框架的核心概念,包括控制反转(IOC)、依赖注入、动态代理、AOP等。通过案例分析,展示了Spring如何降低耦合,提高代码复用性,以及在事务管理和数据库操作中的应用。同时,文章探讨了基于注解和XML配置的不同实现方式,帮助读者深入理解Spring的使用。
摘要由CSDN通过智能技术生成

微信搜索我吃你家米了关注公众号

在这里插入图片描述

示例程序中用到的sql

spring常用jar包

耦合


程序的耦合
    什么是耦合
        即程序间的依赖关系
             - 类之间的依赖关系
             - 方法之间的依赖关系
import java.sql.*;

public class JdbcDemo1 {
    public static void main(String[] args) throws Exception {
        //注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //获取链接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy", "root", "1234");
        //获取操作数据库的预处理对象
        PreparedStatement pstm = conn.prepareStatement("select * from account");
        //执行SQL,得到结果集
        ResultSet rs = pstm.executeQuery();
        //遍历结果集
        while(rs.next()) {
            System.out.println(rs.getString("name"));
        }
        //释放资源
        conn.close();
        pstm.close();
        rs.close();
    }
}

一旦我们失去了com.mysql.jdbc包,程序就会出现编译错误

在这里插入图片描述

当我们把驱动注册的代码改成如下形式时,就可以编译成功了

原来的:DriverManager.registerDriver(new com.mysql.jdbc.Driver());
改动后的:Class.forName("com.mysql.jdbc.Driver");

可以就看出来,当我们使用反射的方式来注册驱动时,就可以达到解耦的目的,因为在使用反射创建对象时,我们传递的参数是一个字符串,并没有真正用到com.mysql.jdbc.Driver类,所以虽然运行失败,但可以编译成功

不过此时也是有弊端的,就是com.mysql.jdbc.Driver被硬编码到了程序中,如果我们需要使用其他数据库的驱动程序,就会遇到问题


解耦的思路:
       	- 使用反射来创建对象,避免使用new关键字
		- 通过读取配置文件来获取要创建的对象的全限定类名

我们自己的一个小案例

~

这个案例就体现出了一些亟待解决的耦合问题

我们使用工厂模式对上面的代码进行了一些改进
使用工厂模式改进后的代码

将类的创建工作交给了BeanFactory类来进行处理

properties文件的都区工作放在了BeanFactory静态代码块中,这样配置文件的读取工作就只会执行一次,然后我们定义了getBean方法,结合java的反射来根据传入的类名创建出相应的对象

但是在刚才的工厂模式中还存在需要讨论的问题

我们在ui包的Clientmain方法中编写如下代码:

for (int i = 0; i < 5; i++) {
	IAccountService as = (IAccountService)BeanFactory.getBean("accountService");
	System.out.println(as);
}

输出结果如下:

com.itheima.service.impl.AccountServiceImpl@677327b6
com.itheima.service.impl.AccountServiceImpl@14ae5a5
com.itheima.service.impl.AccountServiceImpl@7f31245a
com.itheima.service.impl.AccountServiceImpl@6d6f6e28
com.itheima.service.impl.AccountServiceImpl@135fbaa4

可以看到,此时是多例模式,每次调用getBean方法都会生成一个全新的AccountServiceImpl对象

在我们的例子中,是不存在多线程访问造成的安全问题的,因为我们的持久层和业务层中都不存在更改自己成员变量值的方法,也就意味着不存在需要保护的共享资源

因此我们需要对代码进行更改,使其使用单例模式,这样会使程序的效率大大提高

我们对BeanFactory对象做了如下改动

package com.itheima.factory;

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

/*
一个创建Bean对象的工厂
    Bean在计算机英语中的意思是***可重用组件***\
JavaBean
    javabean所代表的的范围远大于实体类
    所谓JavaBean就是使用java编写的可重用组件

在我们的例子中,就是创建我们的service和dao对象

降低程序耦合
    创建配置文件
        全限定类名(就是我们要创建的类的标识符)
            在配置文件中就是一个key-->value形式
    读取配置文件反射创建bean对象
    配置文件的形式有两种选择
        xml文件
        properties文件
 */
public class BeanFactory {
    //定义一个Properties对象
     private static Properties props;

     //定义一个Map,用于存放我们要创建的对象,我们称之为***容器***
    private static Map<String, Object> beans;

     //使用静态代码块为Properties对象赋值
    static {
         try {
             //实例化对象
             props = new Properties();
             //获取Properties文件的流对象
             //为了保证程序的可移植性,我们使用getClassLoader来获取路径
             InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
             props.load(in);
             //实例化容器
/***********************************************************************/
             beans = new HashMap<String, Object>();
             //取出配置文件中所有的key
             Enumeration<Object> keys = props.keys();
             //遍历枚举
             while (keys.hasMoreElements()) {
                 //取出每个key
                 String key = keys.nextElement().toString();
                 //根据key获取value
                 String beanPath = props.getProperty(key);
                 //反射创建对象
                 Object value = Class.forName(beanPath).newInstance();
                 beans.put(key, value);
             }
/***********************************************************************/
         } catch (Exception e) {
             throw new ExceptionInInitializerError("初始化propreties文件失败");
         }
     }
     //根据bean的名称获取bean对象
    public static Object getBean(String beanName) {
        return  beans.get(beanName);
    }
}

Map实现的单例工厂

IOC(inversion of control) 控制反转

先看下面两种对象的创建方式:
private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");

对于第一种对象创建方式:

在这里插入图片描述

在第一种方式中,我们的APP直接和资源产生了联系,体现在程序中就是在代码中使用了new关键字来创建对象

对于第二种对象创建方式

在这里插入图片描述

我们的APP不直接和资源打交道,而是通过工厂来和资源产生联系

现在我们就可以明白,所谓控制反转,就是控制权的转移,由APP转到了工厂,其作用就是降低程序间的依赖关系(耦合)

上面的一系列例子就是为了引进Spring,上面那些创建工厂降低依赖的工作都可以交给Spring框架来做

spring开发包下载地址

spring的入门小案例

通过配置xml文件,然后获取核心容器,使用该容器来获取我们在xml文件中配置的对象

/*
获取spring的IOC核心容器,并根据id获取对象
 */
public static void main(String[] args) {
	//获取核心容器对象
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	//根据id获取bean对象
	//下面演示了两种获取的方式,一种需要强转,一种不需要
	IAccountService as = (IAccountService) ac.getBean("accountService");
	IAccountDao adao = ac.getBean("accountDao", IAccountDao.class);

	System.out.println(as);
	System.out.println(adao);
}

关于核心容器获取的两种方式

public class Client {
    /*
    获取spring的IOC核心容器,并根据id获取对象

    ApplicationContext的三个常用实现类
         - ClassPathXmlApplicationContext
            加载类路径下的配置文件,配置文件必须在类路径下,否则加载不了
         - FileSystemXmlApplicationContext
            加载磁盘中任意位置的配置文件(我们的应用程序必须拥有对该文件的访问权限)
            出于可移植性的考虑,我们一般不选用这种读取配置文件的方式
         - AnnotationConfigApplicationContext
            通过读取注解来创建对象

    核心容器的两个接口引出的问题
        ApplicationContext
            该接口在创建核心容器时,一旦读取完配置文件,就会立即创建对象
                采用的策略是***立即加载***
            在下面的例子中,我们在AccountServiceImpl的构造函数中输出一句话,然后在
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            的后面创建断点,debug程序,可以发现,该语句执行完之后AccountServiceImpl对象已经创建出来,也就是说,读取完配置文件之后,对象就被创建了出来
        BeanFactory
            采用的策略是***延时加载***
                在程序真正根据id去获取对象的时候才进行创建

    ***我们要讨论的问题是,我们什么时候应该使用哪个接口***
        前者适合单例模式
        后这适合多例模式

     最后的结论
        我们在实际开发工作中,使用最多的其实是前者
            因为ApplicationContext的功能相较而言是更强大的,我们可以在配置文件中通过配置
            来告知ApplicationContext使用单例模式还是使用多例模式
     */
    public static void main(String[] args) {
        /******ApplicationContext******/

        /*
        //获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean对象
        //下面演示了两种获取的方式,一种需要强转,一种不需要
        IAccountService as = (IAccountService) ac.getBean("accountService");
        IAccountDao adao = ac.getBean("accountDao", IAccountDao.class);

        System.out.println(as);
        System.out.println(adao);
        */

        /******BeanFactory******/

        Resource resource = new ClassPathResource("bean.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        IAccountService as = (IAccountService)factory.getBean("accountService");
        System.out.println(as);
    }
}

spring中bean的三种创建方式

创建bean的三种方式
	- 使用默认构造函数创建bean对象
		在配置文件中使用bean标签,当该标签中只有id和class属性时,那么即采用类的
		默认构造函数创建对象,如果该类中没有默认构造函数,那么对象将无法创建
		<bean id="accountService"  class="com.itheima.service.impl.AccountServiceImpl"></bean>
	- 使用某个类中的方法创建对象,并存入spring容器
		对于第一种创建方式,我们可能会遇到一些问题,比如我们引入了一个jar包,该jar包
		中的类并没有默认构造函数,此时我们就无法使用第一种方式来获取对象了
		先创建工厂对象,然后再使用该工厂的方法创建对象
		<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
		<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
		***这个工厂类可由我们自己编写,然后再在其中定义一个创建jar包中的对象的方法***
	- 使用某个类中的静态方法创建对象,并将其存入spring对象
		<bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
	***后两种方法就是用来解决jar包中类没有默认构造方法的问题的***

bean的作用范围

bean对象的作用范围
	spring的bean对象默认情况下是单例的,也就是说,在不声明其他属性的情况下,
	使用spring的容器获取的对象都是同一个对象
		我们可以使用scope属性指定bean的作用范围
		最常用的就是前两种
			singleton
				默认值
					单例
			prototype
				多例
			request
				作用于web的请求范围
			session
				作用于web应用的会话范围
			global-session
				作用于集成环境的会话范围
					前台负载均衡服务器将请求交给后台的服务器集群
						由于每次请求不一定总是交给同一台后台服务器进行处理
						引入了global-session,使得后面的所有服务器都能够识别同一用户的会话

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值