Spring框架学习

在之前讲解过Spring的相关知识,但是都比较浅,而且没有将框架的灵魂——反射 融入其中,这次的Spring的讲解将主要从反射入手

Spring框架学习

在之前讲解了Mybatis框架的一些基本使用方法,今天讲解Spring

在我们学习框架之前要完成一个简单的业务,需要使用到三层架构(表现层、服务层、持久层),在没有学习框架之前我们靠纯Java代码来实现这个一个简单的业务

Java纯代码实现三层架构

在这里插入图片描述新建项目,创建dao、service、ui来作为项目的持久层、服务层和表现层

在这里插入图片描述创建相应层次的接口以及实现类,因为项目非常简单,所以表现层只是一个简单的类用来进行测试

dao层

IUserDao接口

package com.qjz.dao;

public interface IUserDao {
    void saveUser();
}

IUserDao接口的实现类UserDaoImpl

package com.qjz.dao.impl;

import com.qjz.dao.IUserDao;

public class UserDaoImpl implements IUserDao {
    public void saveUser() {
        System.out.println("用户已经保存");
    }
}

dao层将直接和数据库进行交互,这个层中的实现类将被实例化并在其它层中被调用

service层

IUserService接口

package com.qjz.service;

public interface IUserService {
    void saveUser();
}

IUserService接口的实现类UserServiceImpl

package com.qjz.service.impl;

import com.qjz.dao.IUserDao;
import com.qjz.dao.impl.UserDaoImpl;
import com.qjz.service.IUserService;

public class UserServiceImpl implements IUserService {

    IUserDao dao = new UserDaoImpl();

    public void saveUser() {
        dao.saveUser();
    }
}

在类中需要调用创建dao层的对象并调用其中的方法来实现和数据库交互数据

ui层

package com.qjz.ui;

import com.qjz.service.IUserService;
import com.qjz.service.impl.UserServiceImpl;

public class ui {
    public static void main(String[] args) {
        IUserService service = new UserServiceImpl();
        service.saveUser();
    }
}

这个层只是一个简单的测试类,创建一个service层的实例来调用方法

执行主方法
在这里插入图片描述保存用户成功

上述就是使用Java代码、使用三层架构来实现的一个简单的业务

但是在上述代码中存在一个问题

Java纯代码实现三层架构中存在的问题及分析

使用纯Java代码实现这个业务的耦合性非常高

如果我们删除上述项目中dao层或者service层中的一个实现类,代码一定就会报错,这里做一个演示

删除dao层下的实现类
在这里插入图片描述继续运行这个项目
在这里插入图片描述程序在编译期就产生了问题,因为我们删除掉的那个类在其他的类中被调用了,这就是耦合性太高产生的问题。在开发的过程中更希望是在运行期依赖而不是在编译器依赖,我们需要降低程序间的耦合关系,需要怎么办呢?

这里需要用到JDBC相关的知识给大家举一个简单的例子

在ui目录下创建一个JdbcDemo类

通过Java代码来操作数据库

package com.qjz.ui;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JdbcDemo {
    public static void main(String[] args) throws  Exception{
        //1.注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());

        //2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis","root","itcast");
        //3.获取操作数据库的预处理对象
        PreparedStatement pstm = conn.prepareStatement("select * from stu");
        //4.执行SQL,得到结果集
        ResultSet rs = pstm.executeQuery();
        //5.遍历结果集
        while(rs.next()){
            System.out.println(rs.getString("username"));
        }
        //6.释放资源
        rs.close();
        pstm.close();
        conn.close();
    }
}

如果我们在项目中不导入JDBC使用的那个驱动jar包或者说在maven工程中没有导入使用JDBC需要的依赖,那么上面这段代码肯定会无法通过编译期,在没有导入jar包或者依赖的情况下运行上面的程序
在这里插入图片描述
在运行前就已将报错
在这里插入图片描述编译期错误,这是我们非常不想看到的结果,但是在JDBC中还有另外一种方法来注册驱动

Class.forName("com.mysql.jdbc.Driver");

当使用这种方法的话将我们需要的包名转换成为了一个字符串,这样运行前就不会报错,能通过编译期,但是在运行期因为没有找到合适的驱动包,所以依然会报错,但这是我们允许的情况

使用第二种注册驱动的方式

package com.qjz.ui;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JdbcDemo {
    public static void main(String[] args) throws  Exception{
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis","root","itcast");
        //3.获取操作数据库的预处理对象
        PreparedStatement pstm = conn.prepareStatement("select * from stu");
        //4.执行SQL,得到结果集
        ResultSet rs = pstm.executeQuery();
        //5.遍历结果集
        while(rs.next()){
            System.out.println(rs.getString("username"));
        }
        //6.释放资源
        rs.close();
        pstm.close();
        conn.close();
    }
}

运行上面的程序
在这里插入图片描述现在时运行期的异常,通过这样的方式就能达到解耦的目的

通过上面的例子可以看到解耦的两个步骤

  1. 使用反射来创建对象,而避免使用new关键字。
  2. 通过读取配置文件来获取要创建的对象全限定类名,配置文件主要是为了提高程序的灵活性

通过上面的两个步骤就能够降低程序的耦合性,让一些错误发生在运行期而不是编译期

通过反射实现解耦

在上面的项目中使用到了我们自定义的两个类的对象,service层和dao层

在qjz目录下创建factory目录,该目录下创建Factory目录

在resources目录下创建bean.properties文件,在这个文件下写上键值对,键是id,值是全限定类名

文件中的内容为

service=com.qjz.service.impl.UserServiceImpl
dao=com.qjz.dao.impl.UserDaoImpl

Factory中的内容为

package com.qjz.factory;


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

public class Factory {

    private static Properties pro;

    static {
        try {
            InputStream in = Factory.class.getClassLoader().getResourceAsStream("bean.properties");
            pro = new Properties();
            pro.load(in);
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

    public static Object getBean(String name){

        Object o = null;
        try {
            String property = pro.getProperty(name);
            o = Class.forName(property).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }
    
}

通过配置文件来获取类的全限定类名,之后通过反射,用该类的无参构造来创建对象,并且返回,通过这种方式就较好的降低了程序之间的耦合性

将编写的Factory类进行测试,既然通过Factory工厂类能够创建对象,就可以将之前的代码中的new关键字创建对象的方式进行替换

对UserServiceImpl中的代码进行修改

package com.qjz.service.impl;


import com.qjz.dao.IUserDao;
import com.qjz.factory.Factory;
import com.qjz.service.IUserService;

public class UserServiceImpl implements IUserService {

    //IUserDao dao = new UserDaoImpl();
    private IUserDao dao = (IUserDao) Factory.getBean("dao");


    public void saveUser() {
        dao.saveUser();
    }
}

在ui类中进行修改

package com.qjz.ui;

import com.qjz.factory.Factory;
import com.qjz.service.IUserService;


public class ui {
    public static void main(String[] args) {
        IUserService service = (IUserService) Factory.getBean("service");
        service.saveUser();
    }
}

修改完成后运行程序
在这里插入图片描述程序运行完成,现在删除dao层中的一个实现类
在这里插入图片描述程序并没有报红,运行程序
在这里插入图片描述发生的是运行期异常,通过上述的方法就可以适当的降低程序的耦合性,产生我们允许的运行期异常

这就是Spring中的一个重要的概念IOC(控制反转),创建对象的权利从程序员的手里交给了工厂类,我们提供必要的参数,工厂创建对象,当需要对象的时候直接从工厂中取即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值