Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展示层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。html
经过 Spring 提供的 IoC 容器,能够将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造 成的过分程序耦合。用户也没必要再为单例模式类、属性文件解析等这些很底层的需求编写代码,能够更专一于上层的应用。java
经过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能能够经过 AOP 轻松应付。mysql
能够将咱们从单调烦闷的事务管理代码中解脱出来,经过声明式方式灵活的进行事务的管理,提升开发效率和质量。spring
能够用非容器依赖的编程方式进行几乎全部的测试工做,测试再也不是昂贵的操做,而是随手可作的事情。sql
Spring 能够下降各类框架的使用难度,提供了对各类优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。数据库
Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为下降。编程
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及经过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时代表其独立性越差( 下降耦合性,能够提升其独立 性)。耦合性存在于各个领域,而非软件设计中独有的,可是咱们只讨论软件工程中的耦合。服务器
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。所以对象的设计应使类和构件之间的耦合最小。软件设计中一般用耦合度和内聚度做为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。架构
下面这段代码就演示了程序的耦合:框架
public class JdbcDemo1 { public static void main(String[] args) throws SQLException { //1.注册驱动 DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver()); //2.获取链接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring", "root","12345678"); //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(); } }
建立数据库的SQL语句以下:
create table account( id int primary key auto_increment, name varchar(40), money float )character set utf8 collate utf8_general_ci; insert into account(name,money) values('aaa',1000); insert into account(name,money) values('bbb',1000); insert into account(name,money) values('ccc',1000);
运行结果以下:
在上述代码中,咱们采用DriverManager.registerDriver方法来注册驱动,这会致使一个编译期依赖,若是咱们的项目中没有导入对应的jar包或者更换了数据库品牌(好比 Oracle),项目在编译时就会出错,须要修改源码来从新数据库驱动。这显然不是咱们想要的。所以,咱们一般采用Class.forName("com.mysql.jdbc.Driver")的方式来注册驱动。
所谓解耦合,咱们能够简单理解为下降程序间的依赖。在实际开发中,应该作到尽可能避免编译器依赖,而尽量是运行时依赖。上述案例,当采用Class.forName方式以后,咱们就可使用反射类建立对象,而避免使用new关键字。此时,咱们的类中再也不依赖具体的驱动类,此时就算删除 mysql 的驱动jar包,依然能够编译(运行仍是会报错,没有驱动不可能运行成功的)。可是,这也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改仍是要修改源码。咱们能够经过读取配置文件的方式来获取要建立对象的权限定类名来解决这个问题。
新建一个IDEA工程用于模拟传统的三层架构,在src/mian/java/目录下建立以下内容:
具体代码以下:
//帐户的持久层接口IAccountDao public interface IAccountDao { /** * 模拟和数据库进行交互 */ void saveAccounts(); } //帐户的持久层接口的实现类AccountDaoImpl public class AccountDaoImpl implements IAccountDao { /** * 模拟和数据库进行交互 */ public void saveAccounts() { System.out.println("向数据库写入帐户数据!!!"); } }
//帐户的业务层接口IAccountService public interface IAccountService { /** * 模拟保存帐户操做 */ void saveAccounts(); } //帐户的业务层接口的实现类AccountServiceImpl public class AccountServiceImpl implements IAccountService { //持久层接口对象的引用 private IAccountDao accountDao = new AccountDaoImpl(); /** * 模拟保存帐户操做 */ public void saveAccounts() { System.out.println("执行保存帐户操做"); //调用持久层接口函数 accountDao.saveAccounts(); } }
//模拟表示层Client,用于调用业务层 public class Client { public static void main(String[] args) { IAccountService accountService = new AccountServiceImpl(); accountService.saveAccounts(); } }
运行结果以下:
在上述案例中,业务层调用持久层,而且此时业务层在依赖持久层的接口和实现类。如 果此时没有持久层实现类, 编译将不能经过。这种编译期依赖关系,应该在咱们开发中杜绝。咱们须要优化代码解决。
在实际开发中,咱们能够用工厂模式来解决这个问题。具体来讲就是,将三层架构的全限定类名都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法经过读取配置文件,运用反射把对象建立出来并存起来。在接下来的使用的时候,直接拿过来用就行了。那么,这个读取配置文件,建立和获取三层对象的类就是工厂类。或者,咱们能够简单地理解为工厂是用来建立Bean对象的。配置文件的内容应该包含两个部分:惟一标识符和全限定类名(key-value的结构)。配置文件能够是XML文件或者properties文件。
a.在src/java/main目录下,新建包factory,在factory包下新建类BeanFactory:
//建立Bean对象的工厂类 public class BeanFactory { //Properties对象,用于读取配置文件 private static Properties properties; //使用静态代码为Properties对象赋值 static { try { //实例化对象 properties = new Properties(); //获取properties文件的流对象 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); //加载配置文件 properties.load(in); } catch (IOException e) { throw new ExceptionInInitializerError("初始化properties对象失败"); } } /** * 根据Bean的名称获取Bean对象 * @param beanName Bean对象的名称 * @return */ public static Object getBean(String beanName) { Object bean = null; //获取Bean对象的全限定类名 String beanPath = properties.getProperty(beanName); //从Properties对象中获取Bean对象 try { bean = Class.forName(beanPath).getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return bean; } }
b.在src/resources目录下,新建bean.properties文件,内容以下:
#帐户的业务层接口的实现类 accountService = service.impl.AccountServiceImpl #帐户的数据访问层接口的实现类 accountDao = dao.impl.AccountDaoImpl
c.改造数据访问层和业务逻辑层
更改ui/Client类中main方法的第一行代码为:
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
更改service/impl/AccountServiceImpl类中实例化持久层接口对象的代码为:
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
d.运行结果
当进行上述改造以后,就再也不经过new关键字建立对象,而是经过对象名来找到配置文件中对应的全限定类名,而后经过反射建立对象。这就下降了程序之间的依赖,运行结果与以前彻底一致。
a.多例模式
更改ui/Client类中main方法为:
public static void main(String[] args) { for(int i = 0; i < 5; i++){ //采用工厂模式建立对象 IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService"); System.out.println(accountService); } }
能够看到运行结果为:
很明显这是5个不一样的对象,这是由于咱们在BeanFactory.getBean方法中建立Bean对象的方式为:Class.forName(beanPath).getDeclaredConstructor().newInstance(),这样获取Bean对象时,都会调用默认的构造函数建立一个新的对象。多例模式因为对象被建立屡次,所以执行效率没有单例模式高。
b.单例模式
更改factory/BeanFactory类的代码以下:
public class BeanFactory { //Properties对象,用于读取配置文件 private static Properties properties; //定义一个Map,用于存放要建立Bean对象,也就是一个容器 private static Map<String, Object> beansMap = null; //单例模式 static { try { //实例化对象 properties = new Properties(); //获取properties文件的流对象 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); //加载配置文件 properties.load(in); //实例化容器 beansMap = new HashMap<String, Object>(); //取出配置文件中全部的key Enumeration<Object> beanKeys = properties.keys(); //遍历keys while(beanKeys.hasMoreElements()){ //取出每一个key,也就是对象名 String beanName = beanKeys.nextElement().toString(); //根据key获取value,也就是全限定类名 String beanPath = properties.getProperty(beanName); //反射建立对象 Object beanObject = Class.forName(beanPath).getDeclaredConstructor().newInstance(); //存入容器 beansMap.put(beanName,beanObject); } } catch (Exception e) { e.printStackTrace(); throw new ExceptionInInitializerError("初始化建立Bean对象失败!"); } } //单例模式 public static Object getBean(String beanName) { return beansMap.get(beanName); } }
运行结果为:
能够看到这时5个对象都是同一个对象,当BeanFacyory类加载时,就会建立Bean对象并存入Map结构中。当须要对象时,就会从这个Map结构中去取,所以都是同一个对象。