Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。
方便解耦,简化开发;
AOP编程的支持;
声明式事务的支持;
方便程序的测试;
方便集成各种优秀框架;
降低JavaEE API的使用难度;
package com.zibo.jdbc;
import java.sql.*;
/**
* 程序的耦合
* 耦合:简单地认为是程序间的依赖关系;
* 包括:
* 类之间的依赖;
* 方法之间的依赖;
* 说明:我们不可能完全消除程序之间的依赖关系,但我们可以降低,这就是解耦;
* 实际开发中:
* 应做到编译期不依赖,运行期依赖;
* 解耦的思路:
* 第一步:使用反射来创建对象,而避免使用new关键字;
* 第二步:通过读取配置文件来获取要创建的对象权限的类名;
*
*/
public class JdbcDemo1 {
public static void main(String[] args) throws Exception {
//1、注册驱动-问题:在没有这个com.mysql.cj.jdbc.Driver包的时候程序是不能正常编译的,
//编译期异常,
//这就是程序的耦合(可以简单地认为就是程序间的依赖关系)
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//以前的解决方法
// Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zibo?serverTimezone=UTC","root","zibo15239417242");
//3、获取操作数据库的预处理对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
//4、执行SQL,得到结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5、遍历结果集
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
//6、释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
当我们将程序包注释的时候,程序编译就会报错,体现了程序之间相互依赖(即程序之间的耦合性),如图:
耦合:(简单认为)程序间的依赖关系;
包括:类之间的依赖,方法之间的依赖;
解耦:降低程序间的依赖关系;
实际开发中:编译器不依赖,运行时才依赖;
解耦的思路:
1、使用反射来创建对象,而避免使用New关键字;
2、通过读取配置文件来获取要创建的对象全限定类名;
Class.forName("com.mysql.cj.jdbc.Driver");
//说明:这种方式虽然也不能运行,但是这种情况出现的错误是运行时异常,而不是编译器异常;
//但是,这种写法把数据库写死了,可以通过读取配置文件的方式读取要创建的对象的全限定类名;
IAccountDao接口:
package com.zibo.dao;
public interface IAccountDao {
void saveAccount();
}
IAccountDaoImpl接口实现类:
package com.zibo.dao.impl;
import com.zibo.dao.IAccountDao;
public class IAccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}
IAccountService接口:
package com.zibo.services;
/**
* 业务层接口
*/
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
}
IAccountServiceImpl接口是实现类:
package com.zibo.services.impl;
import com.zibo.dao.IAccountDao;
import com.zibo.dao.impl.IAccountDaoImpl;
import com.zibo.services.IAccountService;
/**
* 账户的业务层实现类
*/
public class IAccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao = new IAccountDaoImpl();
@Override
public void saveAccount() {
iAccountDao.saveAccount();
}
}
Client类:
package com.zibo.ui;
import com.zibo.services.IAccountService;
import com.zibo.services.impl.IAccountServiceImpl;
/**
* 模拟表现层,调用业务层
*/
public class Client {
public static void main(String[] args) {
IAccountService service = new IAccountServiceImpl();
service.saveAccount();
}
}
各文件位置:
本示例中各程序之间有着严重的依赖关系,一旦某个程序出错会导致整个程序编译失败!那该怎么解决这个问题呢?
编写工厂类和配置文件!
BeanFactory工厂类:
package com.zibo.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 创建Bean对象的工厂:
* 创建我们的service和dao对象;
* 如何创建:
* 1、需要一个配置文件来配置service和dao;
* 配置内容:唯一标识=全限定类名(key=value)
* 2、通过服务配置文件中的内容,反射创建对象;
* 配置文件可以是xml也可以是properties;
*
* Bean:在计算机英语中,有可重用组件的含义;
* JavaBean不等于实体类,是用Java语言编写的可重用组件;
*
*
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties properties;
//使用静态代码块为properties赋值
static {
//实例化对象
properties = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
public static Object getBean(String beanName){
Object bean = null;
String property = properties.getProperty(beanName);
System.out.println(property);
try {
bean = Class.forName(property).getDeclaredConstructor().newInstance();
} catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
bean.properties配置配置文件:
accountService=com.zibo.services.impl.IAccountServiceImpl
accountDao=com.zibo.dao.impl.IAccountDaoImpl
使用工厂类进行解耦:
//改写IAccountServiceImpl代码:
// private IAccountDao iAccountDao = new IAccountDaoImpl();
private IAccountDao iAccountDao = (IAccountDao)BeanFactory.getBean("AccountDao");
//改写Client代码:
// IAccountService service = new IAccountServiceImpl();
IAccountService service = (IAccountService)BeanFactory.getBean("AccountService");
运行结果(成功):
打印一下IAccountService对象:
for (int i = 0; i <5 ; i++) {
IAccountService service = (IAccountService)BeanFactory.getBean("accountService");
System.out.println(service);
}
可见,这个程序是多例模式,程序的运行创建了多个对象,单例模式中对象智慧被创建一次,而类中的成员也只会被初始化一次;
多例模式中,由于对象被创建多次,运行效率没有单例模式高,但是单例模式存在线程问题;
思想:将创建对象的方法写在静态代码块中,类加载的时候只执行一次,创建的对象存起来,以遍重复使用;
改造后的代码:
package com.zibo.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;
/**
* 创建Bean对象的工厂:
* 创建我们的service和dao对象;
* 如何创建:
* 1、需要一个配置文件来配置service和dao;
* 配置内容:唯一标识=全限定类名(key=value)
* 2、通过服务配置文件中的内容,反射创建对象;
* 配置文件可以是xml也可以是properties;
*
* Bean:在计算机英语中,有可重用组件的含义;
* JavaBean不等于实体类,是用Java语言编写的可重用组件;
*
*
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties properties;
//定义一个Map,拥有存放我们要创建的对象,我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为properties赋值
static {//静态代码块只在类加载的时候执行一次
//实例化对象
properties = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(in);
//实例化容器
beans = new HashMap<>();
//去除配置文件中所有的key
Enumeration<Object> keys = properties.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个key
String key = keys.nextElement().toString();
//根据key获取value
String property = properties.getProperty(key);
//反射创建对象
Object value = Class.forName(property).getDeclaredConstructor().newInstance();//newInstance表明每次都会调用默认构造函数创建对象
//把key和value存入容器中
beans.put(key,value);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
public static Object getBean(String beanName){
return beans.get(beanName);
// Object bean = null;
// String property = properties.getProperty(beanName);
System.out.println(property);
// try {
// bean = Class.forName(property).getDeclaredConstructor().newInstance();//newInstance表明每次都会调用默认构造函数创建对象
// } catch (Exception e){
// e.printStackTrace();
// }
// return bean;
}
}
运行结果:
注意:无论是业务层还是持久层很少包含可以修改的类成员(因为对象是一个,在多线程的情况下容易出错,变量可声明在方法中);