在之前讲解过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();
}
}
运行上面的程序
现在时运行期的异常,通过这样的方式就能达到解耦的目的
通过上面的例子可以看到解耦的两个步骤
- 使用反射来创建对象,而避免使用new关键字。
- 通过读取配置文件来获取要创建的对象全限定类名,配置文件主要是为了提高程序的灵活性
通过上面的两个步骤就能够降低程序的耦合性,让一些错误发生在运行期而不是编译期
通过反射实现解耦
在上面的项目中使用到了我们自定义的两个类的对象,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(控制反转),创建对象的权利从程序员的手里交给了工厂类,我们提供必要的参数,工厂创建对象,当需要对象的时候直接从工厂中取即可