目录
1. EJB存在的问题
2. 什么是Spring
Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式
- 轻量级
1. 对于运行环境是没有额外要求的
开源 tomcat resion jetty
收费 weblogic websphere
2. 代码移植性高
不需要实现额外接口
- JavaEE的解决方案
- 整合设计模式
1. 工厂
2. 代理
3. 模板
4. 策略
3. 设计模式
1. 广义概念
面向对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4人帮定义的23种设计模式:工厂、适配器、装饰器、门面、代理、模板...
4. 工厂设计模式
4.1 一个简单工厂模式实现
-
概念:通过工厂类,创建对象
User user = new User();
UserDAO userDAO = new UserDAOImpl(); -
工厂模式创建对象的好处:解耦合
耦合:指定是代码间的强关联关系,一方的改变会影响到另一方
问题:不利于代码维护,改一处代理会影响到许多地方。
简单:把接口的实现类,硬编码在程序中:UserService userService = new UserServiceImpl();
-
通过工厂类能解除这种耦合。
-
代码案例:
扫描二维码关注公众号,回复: 13306655 查看本文章
public class TestSpring {
@Test
public void test1(){
//UserService userService = new UserServiceImpl();
/**
* 1.运用工厂模式中的工厂方法来创建对象,实现解耦。
* 2.此时在这段代码中,就没有了耦合:UserService接口的实现类,在这段代码中是没有任何体现的。
* 3.但是在工厂中不还是引入了具体的实现类吗?耦合不就转移到工厂中了吗?
* 所以要对工厂进行后续的设计。
* 4.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。
*/
UserService userService = BeanFactory.getUserService();
User user = new User("ttt", "123456");
userService.login(user.getName(), user.getPassword());
userService.register(user);
}
}
-
4.1 耦合体现在这行代码:
UserService userService = new UserServiceImpl();
1.如果我后续提供了一个新的实现类UserServiceImpl2,而且我想要替换掉旧的实现类UserServiceImpl。那么我就要重新改这行代码,改成:UserService userService = new UserServiceImpl2();
2.如果我们在开发中真的进行这样的修改的话,它是会经过一个编译部署的过程,经常这样是有很大额外开销的。而且这也不符合面向对象的“开闭原则”:对扩展开放,对修改关闭。
3.所以耦合处位于:UserService userService = new UserServiceImpl();解决这个耦合:运用工厂模式,通过工厂类来创建对象,代替new对象。
-
4.2 一个最简单的工厂
public class BeanFactory {
/**
* 1.此时在工厂中引入了具体的实现类吗?耦合不就转移到工厂中了吗?所以要对工厂进行后续的设计。
* 2.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。
* @return
*/
public static UserService getUserService() {
return new UserServiceImpl();
}
}
-
- 1.此时在工厂中引入了具体的实现类吗?耦合不就转移到工厂中了吗?所以要对工厂进行后续的设计。
-
- 2.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。
4.2.进一步设计:反射工厂
1.先回顾一下对象的创建方式
- 1.直接调用构造方法创建对象:
UserService userService = new UserServiceImpl();
。但是存在耦合。 - 2.通过反射的形式创建对象,可以实现解耦合的目的。
2.1.先拿到Class类对象:Class clazz = Class.forName(“全限定类名”);
2.2.创建对象实例:UserService userService = (UserService )clazz.newInstance();
2.反射工厂代码实现:
public class BeanFactory {
public static UserService getUserService() {
//return new UserServiceImpl();
UserService userService = null;
try {
Class<?> clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
userService = (UserService)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
}
3.还存在耦合:
- 如果想要创建一个其他对象,那么
Class.forName("com.baizhiedu.basic.UserServiceImpl");
中的全限定类名就需要改变,这又会涉及到耦合。 - 字符串的耦合处理起来较为简单,我们可以用一个外部的properties配置文件来处理,将全限定类名写在配置文件中。那么之后我们只需要根据情况修改配置文件,再加载配置文件即可。
- 这样就把耦合抽取到代码之外了,实现了解耦。
4.properties配置文件实现解耦:继续改进
- 创建一个配置文件
- 将类名信息写到properties文件中
userService = com.baizhiedu.basic.UserServiceImpl
- 在将配置文件中的信息读取出来:
1.Properties集合对象来存储Properties文件的内容
2.Properties集合是一种特殊的Map,并且要求key=String,Value=String类型。 注意:I/O
1.I/O在整个Java操作过程中是一种系统级资源,一般要尽量避免重复性的打开I/O。而且最好是在程序启动的时候,一次性读取完我们想要的内容。
2.所以再读取配置文件的时候,我们用静态代码块来实现。
private static Properties env = new Properties();
static{
//第一步:获取IO输入流
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
try {
//第二步:文件内容封装到Properties集合中,key=userService value=com.baizhiedu.basic.UserServiceImpl
env.load(inputStream);
//第三步:关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
-5.反射工厂的代码:从Properties对象中拿到要创建的类名
public class BeanFactory {
private static Properties env = new Properties();
static{
//第一步:获取IO输入流
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
try {
//第二步:文件内容封装到Properties集合中,key=userService value=com.baizhiedu.basic.UserServiceImpl
env.load(inputStream);
//第三步:关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService() {
UserService userService = null;
try {
//从Properties对象中拿到要创建的类名
Class<?> clazz = Class.forName(env.getProperty("userService"));
userService = (UserService)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
}
6.如果后面想要别的实现类,那么在properties中再配置就好了,这样就解决了实例化UserService实现类的耦合
userService = com.baizhiedu.basic.UserServiceImpl
userServiceNew = com.baizhiedu.basic.UserServiceImplNew
- 比如想要解决:
private UserDAO userDao = new UserDAOImpl();
UserServiceImpl类中的这句代码的耦合。 - 配置文件
userDAO =com.baizhiedu.basic.UserDAOImpl
- 工厂类中再写一个方法
public static UserDAO getUserDao() {
UserDAO userDAO = null;
try {
Class<?> clazz = Class.forName(env.getProperty("userDAO"));
userDAO = (UserDAO)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userDAO;
}
- UserServiceImpl代码修改
public class UserServiceImpl implements UserService {
//private UserDAO userDao = new UserDAOImpl();
private UserDAO userDao = BeanFactory.getUserDao();
@Override
public void register(User user) {
userDao.register(user);
}
@Override
public void login(String name, String password) {
userDao.login(name, password);
}
}
4.3.通用工厂设计
1.回顾一下上面的反射工厂,我们通过工厂类中的工厂方法起到了解耦的效果。我们创建了getUserService方法,解决了UserService的耦合;创建了getUserDao方法,解决了UserDao的耦合。
2.但是在设计层面上这里是有些问题的:
- 上面的反射工厂中分别为UserServiceImpl和UserDaoImpl这两个类创建了工厂方法,那么就会出现一个问题。如果我想要为某个类解耦合,那么就要再创建一个该类的工厂方法吗?这样岂不是要创建成千上万个工厂方法。
- 观察发现上面两个工厂方法存在大量的重复冗余代码。
所以为每个类都提供一个独立的工厂方法是没有价值和意义的。同时也会有大量的冗余代码。
4.那么能不能设计一个通用的工厂方法来解决这个问题,在通用的工厂里面,只提供一个通用的工厂方法,这个方法可以帮我们生产一切我们想要的对象。
- 设计思想:这个工厂方法不固定生产哪个对象,或者说可以生产所有工厂对象。
那么到底生产哪个对象呢?由调用者来决定。
- 所以生产什么对象,体现在参数中,有调用者来决定。
5.具体代码:通用工厂
public class BeanFactory {
private static Properties env = new Properties();
static{
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
try {
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通用工厂方法
* @param key
* @return
*/
public static Object getBean(String key){
Object instance = null;
try {
Class<?> clazz = Class.forName(env.getProperty(key));
instance = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
4.4.通用工厂使用
1.创建工厂还挺复杂的:首先需要反射创建对象,其次涉及大量的I/O操作读取配置文件内容,这个的处理过程相对来讲还挺麻烦的。所以日常开发中,一般不需要我们自己创建工厂。一般都会准备好工厂和配置文件,只要我们会使用它即可。那么我么那该怎么使用这个通用的工厂呢?怎么用这个通用的工厂来帮我们创建对象,最终帮我们完成解耦合的操作呢?
2.那么怎么使用通用工厂?
- 工厂的核心目的:创建对象。既然是创建对象,那么我们首先要定义类型。
- 怎么让工厂类,认识到我们刚才定义的类型(我们想要创建的对象)。通过配置文件的配置告诉工厂。key = value
- 工厂创建好这些对象后,我们就要获得这些对象:BeanFactory.getBean(“key”);
5.引言总结
Spring的引言部分就讲完了,主要是学习了工厂设计模式。因为Spring最核心的就是工厂,Spring的本质就是一个工厂。
Spring的工厂叫做ApplicationContext,配置文件是applicationContext.xml。