Spring第二篇【曾经代码中的问题分析】

mac2025-08-22  2

目录

1.曾经代码中的问题分析2.工厂模式解耦3.单例多例问题3.1 单例与多例的区别3.2 改造BeanFactory类 4.总结

1.曾经代码中的问题分析

首先来看我们曾经的做法:

项目结构图 持久层

IAccountDao接口

package com.zhangzemin.dao; /** * 账户持久层接口 */ public interface IAccountDao { /** * 模拟保存账户 */ void saveAccount(); }

AccountDaoImpl实现类

package com.zhangzemin.dao.impl; import com.zhangzemin.dao.IAccountDao; /** * 账户持久层实现类 */ public class AccountDaoImpl implements IAccountDao { public void saveAccount() { System.out.println("保存了账户"); } }

业务层

IAccountService接口

package com.zhangzemin.service; /** * 账户业务层接口 */ public interface IAccountService { /** * 模拟保存账户 */ void saveAccount(); }

AccountServiceImpl实现类

package com.zhangzemin.service.impl; import com.zhangzemin.dao.IAccountDao; import com.zhangzemin.dao.impl.AccountDaoImpl; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = new AccountDaoImpl(); public void saveAccount() { accountDao.saveAccount(); } }

控制层

Client类

package com.zhangzemin.ui; import com.zhangzemin.service.IAccountService; import com.zhangzemin.service.impl.AccountServiceImpl; /** * 模拟一个表现层,用于调用业务层 */ public class Client { public static void main(String[] args) { IAccountService as = new AccountServiceImpl(); as.saveAccount(); } }

运行结果: 曾经的做法中,控制层调用业务层时采用IAccountService as = new AccountServiceImpl(), 业务层调用持久层采用IAccountDao accountDao = new AccountDaoImpl()都采用了new关键字,这种程序的做法就是我们所说的程序的耦合度较高,它的独立性很差。

2.工厂模式解耦

针对上述的问题,解决方式是使用工厂模式来实现解耦,我们需要一个创建Bean对象的工厂:创建我们的service和dao对象。

如何创建:

需要一个配置文件来配置我们的service和dao 配置的内容:唯一标识=全限定类名(key=value)通过读取配置文件中配置的内容,反射创建对象

编写工厂类和配置文件

BeanFactory

package com.zhangzemin.factory; import java.io.InputStream; import java.util.Properties; public class BeanFactory { //定义一个properties private static Properties props; //使用静态代码块为properties对象赋值 static{ try{ //实例化请求 props = new Properties(); //获取properties的文件流对象 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); props.load(in); }catch(Exception e){ throw new ExceptionInInitializerError("初始化properties失败!"); } } public static Object getBean(String beanName){ Object bean = null; try { String beanPath = props.getProperty(beanName); bean = Class.forName(beanPath).newInstance(); }catch(Exception e){ e.printStackTrace(); } return bean; } }

bean.properties

accountService=com.zhangzemin.service.impl.AccountServiceImpl accountDao=com.zhangzemin.dao.impl.AccountDaoImpl

然后修改调用层Client和业务层AccountServiceImpl

Client

package com.zhangzemin.ui; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; import com.zhangzemin.service.impl.AccountServiceImpl; /** * 模拟一个表现层,用于调用业务层 */ public class Client { public static void main(String[] args) { // IAccountService as = new AccountServiceImpl(); IAccountService as = (IAccountService) BeanFactory.getBean("accountService"); as.saveAccount(); } }

AccountServiceImpl

package com.zhangzemin.service.impl; import com.zhangzemin.dao.IAccountDao; import com.zhangzemin.dao.impl.AccountDaoImpl; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { // private IAccountDao accountDao = new AccountDaoImpl(); private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao"); public void saveAccount() { accountDao.saveAccount(); } }

运行结果: 此时的项目结构图:

3.单例多例问题

把上述client中的代码更改为:

package com.zhangzemin.ui; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; import com.zhangzemin.service.impl.AccountServiceImpl; /** * 模拟一个表现层,用于调用业务层 */ public class Client { public static void main(String[] args) { // IAccountService as = new AccountServiceImpl(); // IAccountService as = (IAccountService) BeanFactory.getBean("accountService"); for(int i = 0; i<5; i++){ IAccountService as = (IAccountService) BeanFactory.getBean("accountService"); System.out.println(as); } // as.saveAccount(); } }

运行结果: 可以看到此时的对象是多例的

把AccountServiceImpl中的代码修改为:

package com.zhangzemin.service.impl; import com.zhangzemin.dao.IAccountDao; import com.zhangzemin.dao.impl.AccountDaoImpl; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { // private IAccountDao accountDao = new AccountDaoImpl(); private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao"); int i = 0; public void saveAccount() { accountDao.saveAccount(); System.out.println(i); i++; } }

运行结果: 可以看到由于每个对象都有一个独立的实例,从而保证了类对象在创建时重新初始化它的属性

3.1 单例与多例的区别

单例:对象只被创建一次,从而类中的成员也就只会初始化一次,线程不安全。 多例:对象被创建多次,执行效率没有单例对象高,但是线程安全。

由此得知,我们可能并不需要多例对象,而是需要单例对象。

3.2 改造BeanFactory类

目的:把多例对象改成单例对象

BeanFactory类中的方法newInstance(),每次都会调用默认构造方法创建对象。所以我们要保证newInstance()只能被执行一次。 而由于Java中的GC垃圾回收机制,对象长时间不用就会被回收,所以我们得把工厂中生产的对象保存起来。

所以改造BeanFactory类中的代码为:

package com.zhangzemin.factory; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * 一个创建Bean对象的工厂:创建我们的service和dao对象 * * Bean在计算机英语中,有可重用组件的意思。 * JavaBean: 用java语言编写的可重用组件 * javabean > 实体类 * * * 如何创建: * 第一:需要一个配置文件来配置我们的service和dao * 配置的内容:唯一标识=全限定类名(key=value) * 第二:通过读取配置文件中配置的内容,反射创建对象 */ public class BeanFactory { //定义一个properties private static Properties props; //定义一个Map,用于存放我们要创建的对象。我们把它称之为容器 private static Map<String,Object> beans; //使用静态代码块为properties对象赋值 static{ try{ //实例化请求 props = new Properties(); //获取properties的文件流对象 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(); //把key和value存入容器中 beans.put(key,value); } }catch(Exception e){ throw new ExceptionInInitializerError("初始化properties失败!"); } } /** * 根据bean的名称获取bean对象 * @param beanName * @return */ public static Object getBean(String beanName){ return beans.get(beanName); } // public static Object getBean(String beanName){ // Object bean = null; // try { // String beanPath = props.getProperty(beanName); // bean = Class.forName(beanPath).newInstance(); // }catch(Exception e){ // e.printStackTrace(); // } // return bean; // } }

AccountServiceImpl类代码:

package com.zhangzemin.service.impl; import com.zhangzemin.dao.IAccountDao; import com.zhangzemin.dao.impl.AccountDaoImpl; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { // private IAccountDao accountDao = new AccountDaoImpl(); private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao"); public void saveAccount() { private int i = 0; accountDao.saveAccount(); System.out.println(i); i++; } }

运行结果: 测试得知,这里的对象是单例的。

我们的Service层和Dao层中,一般是把变量定义在方法中,来避免单例对象实例化修改类成员造成线程安全问题。所以改造AccountServiceImpl的代码为:

package com.zhangzemin.service.impl; import com.zhangzemin.dao.IAccountDao; import com.zhangzemin.dao.impl.AccountDaoImpl; import com.zhangzemin.factory.BeanFactory; import com.zhangzemin.service.IAccountService; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { // private IAccountDao accountDao = new AccountDaoImpl(); private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao"); public void saveAccount() { int i = 0; accountDao.saveAccount(); i++; System.out.println(i); } }

4.总结

首先,我们分析了之前的做法代码的耦合度较高,所以采用了工厂模式来解耦,而此时创建的对象是多例的,又引发了我们对单例和多例的思考,比较了各自的优缺点之后,我们采用了单例模式,因为它的效率更高,并且使用一个Map把对象保存起来,一是避免每次都要创建对象消耗资源,二是为了防止单例对象长时间不用被GC垃圾回收机制回收掉,而在业务层和持久层的尽量不定义类变量,而是把变量定义在方法中,这样就避免了单例对象每次实例化都会操作类成员造成线程安全问题。

最新回复(0)