版本: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;
}
}
注意点:
- 加载applicationContext.properties中的文件,因为是流操作,费资源,所以放在static中。仅加载一次。
- 一定要加/啊。从当前项目的根根路径开始的
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的比较,我们很容易发现他们的共同点和不同点
总结如下:
- bean的名字不同,可以通过参数传递过去,
- 返回值统一为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.总结
通过以上的说明与实例,我们明白了工厂到底是怎么产生对象的
- 定义对象(实体类)
- 通过applicationContext.properties告诉工厂name=value的对应关系
- 调用工厂的getBean(String name) 获取对象,然后强制类型装换既可。
而spring的工厂也是这样,一模一样,无非就是定义的接口更丰富了。
下一篇:02-Spring的核心API https://blog.csdn.net/fsjwin/article/details/109406451