01-Spring的初体验:spring工厂的化过程

版本:SpringFramework 5.1.4 
项目:D:\Idea2020Projects\spring5_20201031

1.没有Spring之前的世界

在么有spring之前,使用的是EJB开发web应用。他的优点就不说了。关键有一下缺点:

  • 运行环境苛刻
    必须在ejb容器中运行如was, webservice等,必须针对使用的服务器进行开发,需要实现服务器的某些接口才行
  • 代码移植性差
    如果想把服务器从was修改为webservice不可能,需要重新开发,实现webservice接口。
  • 需要花钱呢。在中国行不通。哈哈

2.Spring来了

Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式。

  • 为什么首Spring是一个轻量级的JavaEE解决方案。
    因为他可以解决javaee各层的所以问题,要不然就是自己有解决方案如controller层的SpringMVC,要不然就是整合了第三方的解决方案如Mybatis等。所以他是一个解决方案。
  • 整合众多优秀的设计模式。
    不是从轮子早起,但是却使用原有的轮子造就了劳斯莱斯,Spring的作者,善于使用前任总结的设计模式,所以Spring框架中使用了诸多设计模式:
    • 工厂
    • 代理
    • 模板
    • 策略
      优秀的设计模式的使用使spring成为了优秀的框架,他的优秀之处主要在于工厂模式的使用,这样可以使代码解耦。

3.工厂模式使用前后

没有对比就没有伤害,我们使用工厂之后中之前对比看看战果怎样

3.1我们的初级代码

这里的代码,分厂简单就是一个dao,一个service,特别关注测试类,通过new的方式获得Userservice,这样就很low。

3.1.1Dao层代码接口UserDao.java

package base.dao;

/**
 * @author yuhl
 * @Date 2020/10/31 14:03
 * @Classname UserDao
 * @Description TODO
 */
public interface UserDao {

    public void login(String username,String password);
}

3.1.2Dao层代码实现类UserDaoImpl.java

package base.dao.impl;

import base.dao.UserDao;

/**
 * @author yuhl
 * @Date 2020/10/31 14:04
 * @Classname UserDaoImpl
 * @Description TODO
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void login(String username, String password) {
        System.out.println("select * from xt_user");
    }
}

3.1.3Dao层代码接口UserService.java

package base.service;

/**
 * @author yuhl
 * @Date 2020/10/31 14:06
 * @Classname UserService
 * @Description TODO
 */
public interface UserService {
    public void login(String username,String password);
}

3.1.4Dao层代码接口UserServiceImpl.java

package base.service.impl;

import base.dao.UserDao;
import base.dao.impl.UserDaoImpl;
import base.service.UserService;

/**
 * @author yuhl
 * @Date 2020/10/31 14:07
 * @Classname userServiceImpl
 * @Description TODO
 */
public class UserServiceImpl implements UserService {
    UserDao userDao = new UserDaoImpl();

    @Override
    public void login(String username, String password) {
        userDao.login(username ,password);
    }
}

3.1.5使用测试类来测试这里是重点哦

    //原始版本
    @Test
    public void test1(){
        //问题,如果我们的userServiceImpl修改为UserserviceImplNew怎么办,这个地方就需要修改了。耦合度太高了
        UserService userService = new UserServiceImpl();
        userService.login("yuhl","111111");
    }

问题,如果我们的userServiceImpl修改为UserserviceImplNew怎么办,这个地方就需要修改了。耦合度太高了

3.2使用工厂进行改进1

对于上面的测试类中使用

UserService userService = new UserServiceImpl();

耦合度太高的问题,我们引入一个BeanFactory进行修改,让BeanFactory帮我我们生成userservice,这样不就可以解耦了吗,至少在controller中解耦了(即我们的测试类中)

3.2.1 BeanFactory.java

package base.factory;

import base.service.UserService;
import base.service.impl.UserServiceImpl;

/**
 * @author yuhl
 * @Date 2020/10/31 14:12
 * @Classname BeanFactory
 * @Description TODO
 */
public class BeanFactory {
    public static UserService getUserservice(){
        return new UserServiceImpl();
    }
}

工厂作为一个生产类,可以把方法设置为static既可以

3.2.2 BeanFactory类获取service对象

这个我们在来看看会不会好一点。

@Test
    public void test2(){
        //test1 针对test1的耦合性搞的问题,可以使用工厂来解决
    //UserService userService = new UserServiceImpl();

        UserService userService = BeanFactory.getUserservice();
        userService.login("yuhl","111111");
    }

我们会发现不用:

//UserService userService = new UserServiceImpl();

而是使用:

UserService userService = BeanFactory.getUserservice();

这样如果我们的UserServiceImpl.java后期不用了。需要接入新的业务类UserServiceImplNew.java只需要在工厂中进行修改即可。这样就做到了解耦。

此时可能有小伙伴会说,没有解耦呀,只是转移了解耦的地方,现在的耦合在Factory中了,是的。那么我们纠结着往下看,看能否彻底耦合把他赶出java代码。答案是肯定的。先给出答案,通过反射,哈哈哈。剧透了。

3.3使用工厂进行改进2

上面工厂中使用了new,有耦合希望继续改造。

3.3.1使用反射,即使用字符串来代替原来的new

BeanFactory.java

package base.factory;

import base.service.UserService;
import base.service.impl.UserServiceImpl;

/**
 * @author yuhl
 * @Date 2020/10/31 14:12
 * @Classname BeanFactory
 * @Description TODO
 */
public class BeanFactory {
    public static UserService getUserservice(){
        //return new UserServiceImpl();
        UserService userService = null;
        try {
            Class<?> aClass = Class.forName("base.service.impl.UserServiceImpl");
            userService = (UserService)aClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return userService;
    }
}

3.3.2把字符串从代码中剔除,引入properties文件

上面的字符串:base.service.impl.UserServiceImpl,也有耦合性,为什么呢?
如果的新的业务类名字为base.service.impl.UserServiceNewImpl,这个java类仍然需要改,我们要的效果是java类不需要改的效果。最多修改配置文件,这样就可以解耦。
继续改进,使用properties文件字符串剔除,(只要是字符串就有招,就是用properties)。

applicationContext.properties

userService=base.service.impl.UserServiceImpl

BeanFactory.java

package base.factory;

import base.service.UserService;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author yuhl
 * @Date 2020/10/31 14:12
 * @Classname BeanFactory
 * @Description TODO
 */
public class BeanFactory {
    static Properties env = new Properties(); //把applicationContext.properties中数据以流的形式加载进来
    //加载applicationContext.properties中的文件,因为是流操作,费资源,所以放在static中。仅加载一次。
    static {
        InputStream resourceAsStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
            env.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(null != resourceAsStream)
                resourceAsStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static UserService getUserservice(){
        //return new UserServiceImpl();
        UserService userService = null;
        try {
            Class<?> aClass = Class.forName(env.getProperty("userService"));
            userService = (UserService)aClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return userService;
    }
}

注意点:

  1. 加载applicationContext.properties中的文件,因为是流操作,费资源,所以放在static中。仅加载一次。
  2. 一定要加/啊。从当前项目的根根路径开始的
BeanFactory.class.getResourceAsStream("/applicationContext.properties");

此时已然可以运行。

3.3.3此时再对实现类进行修改则可以彻底解决耦合

此时新增新的业务处理类:UserServiceNewImpl.java,抛弃:UserServiceNew.java

package base.service.impl;

import base.dao.UserDao;
import base.dao.impl.UserDaoImpl;
import base.service.UserService;

/**
 * @author yuhl
 * @Date 2020/10/31 14:07
 * @Classname userServiceImpl
 * @Description TODO
 */
public class UserServiceNewImpl implements UserService {
    UserDao userDao = new UserDaoImpl();

    @Override
    public void login(String username, String password) {
        userDao.login(username ,password);
    }
}

此时仅需要修改properties文件如下即可:

userService=base.service.impl.UserServiceNewImpl

其他的什么也不用修改。项目就走了新的业务类UserServiceNewImpl。
运行项目一切正常。是不是发现很神奇,到这里,整个类与类之间的强耦合被彻底解开了。很清晰了吧。!!!!!!

3.3.4把service对dao的修改也做上面的优化

applicationContext.properties新增对UserDao配置

userService=base.service.impl.UserServiceNewImpl
userDao=base.dao.impl.UserDaoImpl

工厂类中添加获取UserDao的静态方法:


    //获取userDao的工厂方法
    public static UserDao getUserDao(){
        //return new UserServiceImpl();
        UserDao userDao = null;
        try {
            Class<?> aClass = Class.forName(env.getProperty("userDao"));
            userDao = (UserDao)aClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return userDao;
    }

UserServiceImpl.java使用UserDao出调用工厂方法即可不实用new的方式:

package base.service.impl;

import base.dao.UserDao;
import base.dao.impl.UserDaoImpl;
import base.factory.BeanFactory;
import base.service.UserService;

/**
 * @author yuhl
 * @Date 2020/10/31 14:07
 * @Classname userServiceImpl
 * @Description TODO
 */
public class UserServiceImpl implements UserService {
    //UserDao userDao = new UserDaoImpl();
    UserDao userDao = BeanFactory.getUserDao();

    @Override
    public void login(String username, String password) {
        userDao.login(username ,password);
    }
}

运行结果依然正确:

select * from xt_user

总结:第3章是使用简单工厂的方式的解耦,针对不同的类,提供不同的工厂静态方法,那么如果我们有10000000000000000各类呢?这个是个大问题,肯定需要更为通用的做法。往下看… … 保证让你如沐春风!! 哈哈!

4.通用工厂的形成

那么如果我们有10000000000000000各类呢?这个是个大问题,肯定需要更为通用的做法,这个通用的方法就是通用工厂。

在这里插入图片描述
通过userService和userDao的比较,我们很容易发现他们的共同点和不同点
总结如下:

  1. bean的名字不同,可以通过参数传递过去,
  2. 返回值统一为Object,在调用的地方进行类型转换即可。

4.1 工厂类的出现工厂来了

BeanFactory.java

   public static Object getBean(String name){
        //return new UserServiceImpl();
        Object object = null;
        try {
            Class<?> aClass = Class.forName(env.getProperty(name));
            object = aClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return object;
    }

在调用他的地方做相应的修改:
UserServiceImpl.java

//通过抽象工厂拿对象,然后类型转换即可
    UserDao userDao = (UserDao) BeanFactory.getBean("userDao");

测试类(controller)用同样的方法:

//通过抽象工厂拿对象,然后类型转换即可
UserService userService = (UserService) BeanFactory.getBean("userService");

到此,通用工厂演化完成。

5.总结

通过以上的说明与实例,我们明白了工厂到底是怎么产生对象的

  1. 定义对象(实体类)
  2. 通过applicationContext.properties告诉工厂name=value的对应关系
  3. 调用工厂的getBean(String name) 获取对象,然后强制类型装换既可。
    而spring的工厂也是这样,一模一样,无非就是定义的接口更丰富了。

下一篇:02-Spring的核心API https://blog.csdn.net/fsjwin/article/details/109406451

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值