自定义Spring工厂类BeanFactory

自定义Spring工厂类BeanFactory

Spring的概述

概述:

Spring是一个轻量级开源框架,整合了大量的第三方框架。

优势:

  • 方便解耦,简化开发
  • Aop编程的支持
  • 声明式事务的支持
  • 方便程序的测试
  • 方便集成各种优秀框架

解耦:

耦合:程序间的依赖关系,类之间的解耦,方法之间的解耦。
解耦:降低程序间的依赖。
类之间的解耦:
    1.使用反射来创建对象,而避免使用new关键字。
    2.通过读取配置文件来获取要创建对象的全限定名。
实际开发中:编译期不依赖,运行时才依赖。

通过jdbcDemo来分析代码间的耦合度

  • 导入依赖:
 <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
  • 创建数据库
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);
  • 测试工程
/**
 * 通过jdbc查询数据库
 * 耦合度太高
 */
public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1注册驱动
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //通过反射来创建对象,避免使用new来创建 减少代码间的耦合度
        Class.forName("com.mysql.jdbc.Driver");//字符串可以通过配置文件的方式来声明更大的减少耦合度
        //2获取链接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/my_spring", "root", "root");
        //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();
    }
}

模拟耦合度高的代码

//dao层接口
public interface AccountDao {
    public void saveAccount();
}
//dao层实现类
public class AccountDaoImpl implements AccountDao {
    public void saveAccount() {
        System.out.println("用户保存成功");
    }
}
//service接口
public interface AccountService {
    /**
     * 模拟保存数据到数据库
     */
    void saveAccount();
}
//service实现类
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
//模拟表现层
public class ControllerTest {
    public static void main(String[] args) {
        AccountService accountService = new AccountServiceImpl();
        accountService.saveAccount();
    }
}

通过自定义工厂类BeanFactory解决上述代码中的耦合

/**
 * 创建一个Bean对象的工厂
 * Bean:在计算机英语中被定义为可重用组件
 * JavaBean: 用java语言编写的可重用组件,它就是创建我们的service和dao对象
 * javaBean > 实体类
 * 如何创建javaBean:
 * 第一步需要一个配置文件来配置我们的service和dao
 *      配置内容:唯一标识=全限定类名(key=value)
 * 第二步通过配置文件中配置的内容,反射创建对象
 * 配置文件可以是xml也可以是properties
 */
public class BeanFactory {
    //定义一个properties对象
    private static Properties properties;
    //使用静态代码块为Properties对象赋值 静态代码块只会随着类的加载仅加载一次
    static{
        //通过配置文件的方式来配置反射的全限定名
        try {
            //实例化properties对象
            properties=new Properties();
            //从properties中获取流对象
            InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            //读取流
            properties.load(inputStream);
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }
    /**
     * 根据bean的名称获取bean对象
     * @param beanName
     * @return
     */
    //方法想要被类名直接调用需要添加static 如果不加static就必须通过这个类的对象进行调用
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            //通过反射获取全限定名下的类的class对象并实例化
            //通过反射的方式进行创建对象
            bean = Class.forName(beanPath).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return bean;
    }
}

修改service和Controller代码

  • controller
/**
 * 模拟一个表现层,用于调用业务层
 */
public class ControllerTest {
    public static void main(String[] args) {
        //AccountService accountService = new AccountServiceImpl();
        //通过工厂类进行调用service层
        AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
        accountService.saveAccount();
    }
}

-service

public class AccountServiceImpl implements AccountService {
    //private AccountDao accountDao = new AccountDaoImpl();
    private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
    //通过工厂调用dao层
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

再次分析自定义BeanFactory是否有耦合

    每次通过BeanFactory创建的对象都是不同的也就是多例的,对象被多次创建,执行效率也就没有那么高,创建出来的对象不存储起来根据java的垃圾回收机制对象会在长时间不用时被回收,当你下次用的时候就没有了。可以只用一次new创建一个对象然后马上存起来,将对象存到一个容器中,下次创建对象就会从容器中拿到这个对象,不用重新创建对象。
  • 创建一个容器Map<String,Object>,key为配置文件的beanName,values为反射创建的对象,在静态代码块中实现,再类加载的时候只加载一次。它是通过将需要创建的对象放入到map集合中,每次需要创建对象的时候都会到map容器中获取对象,而不是新建一个对象,从而到达每次拿到的都是同一个对象。
public class BeanFactory {
    //定义一个properties对象
    private static Properties properties;
    //定义一个容器
    private static Map<String,Object> beans;
    //使用静态代码块为Properties对象赋值 静态代码块只会随着类的加载仅加载一次
    static{
        //通过配置文件的方式来配置反射的全限定名
        try {
            //实例化properties对象
            properties=new Properties();
            //从properties中获取流对象
            InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            //读取流
            properties.load(inputStream);
            //实例化容器
            beans=new HashMap<String, Object>();
            //取出配置文件中的所有key
            Enumeration<Object> keys = properties.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取beanPath
                String beanPath = properties.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //将对象保存到beans容器中
                beans.put(key,value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }
    /**
     * 根据beans容器获取对象 获取到的都是同一个对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
 /*   *//**
     * 根据bean的名称获取bean对象
     * @param beanName
     * @return
     *//*
    //方法想要被类名直接调用需要添加static 如果不加static就必须通过这个类的对象进行调用
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            //通过反射获取全限定名下的类的class对象并实例化
            //通过反射的方式进行创建对象
            bean = Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }*/
}
  • controller测试层
public class ControllerTest {
    public static void main(String[] args) {
        //AccountService accountService = new AccountServiceImpl();
        //通过工厂类进行调用service层
        for (int i = 0; i < 5; i++) {
            AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
            System.out.println(accountService);//获取的都是同一个com.itheima.spring.service.impl.AccountServiceImpl@14ae5a5
            accountService.saveAccount();
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。