0、相关类
User
package com.yusian;
public class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
UserDao
package com.yusian;
public interface UserDao {
void register(User user);
boolean login(String username, String password);
}
UserDaoImpl
package com.yusian;
public class UserDaoImpl implements UserDao {
@Override
public void register(User user) {
System.out.println("user " + user + " register success!!");
}
@Override
public boolean login(String username, String password) {
System.out.println("user " + username + " login success!!");
return false;
}
}
UserService
package com.yusian;
public interface UserService {
void register(User user);
boolean login(String username, String password);
}
UserServiceImpl
package com.yusian;
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
@Override
public void register(User user) {
userDao.register(user);
}
@Override
public boolean login(String username, String password) {
return userDao.login(username, password);
}
}
1、构造函数方式创建对象
@Test
public void userTest() {
UserService userService = new UserServiceImpl();
User user = new User("sian", "123456");
userService.register(user);
userService.login("sian", "12345");
}
此时,接口UserService的实现对象与业务是耦合的,因为如果要更换接口的实现类是需要在调用处去修改new出来的类名,为了解决这个问题,我们可以将创建接口实例的过程进行封装,通过一个工具类创建出来,这样就可以将创建对象的过程从业务代码中释放出来。
2、工厂模式创建对象
package com.yusian;
public class BeanFactory {
public static UserService getUserService() {
return new UserServiceImpl();
}
}
虽然通过构造函数创建实例的过程从业务中抽离出来,但只是转移了位置而已,并没有从根本上解决问题,如果需要更换接口实现类,还是需要修改代码重新编译,能不能不编译改了就生效呢?可以的!
3、通过配置文件及反射方式创建实现
3.1、反射创建实例
public static UserService getUserService() {
try {
Class cls = Class.forName("UserServiceImpl");
return (UserService) cls.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
反射机制可以动态创建对象,给定类名就能实现,这种方式奠定了通过配置就能修改接口实例的基础。目前类名还是在写在代码中的,接下来只需要将这个字符串从代码中解脱出来,从配置文件读取即可。
ApplicationContext.properties
userService = com.yusian.UserServiceImpl
BeanFactory
package com.yusian;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
private static Properties prop = new Properties();
static {
ClassLoader classLoader = BeanFactory.class.getClassLoader();
try (InputStream is = classLoader.getResourceAsStream("ApplicationContext.properties")) {
prop.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService() {
try {
Class cls = Class.forName(prop.getProperty("userService"));
return (UserService) cls.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
搞定,打完收工!现在如果有UserService的其他实现类,只需要将ApplicationContext.properties中的字符串修改成对应的类名即可,将耦合从代码中解脱出来,通过配置文件完成切换。
3.2、封装反射方法为工厂
public static Object getBean(String name) {
try {
Class cls = Class.forName(prop.getProperty(name));
return cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
4、Sping创建对象的基本使用
回顾以上解耦的步骤,大致分为:
- 准备POJO类、配置文件;
- 读取配置文件,获取需要创建对象的全限定类名;
- 通过反射机制创建实例对象
Sping其实也一样:
- 准备POJO类、配置文件(applicationContext.xml);
-
读取配置文件,获取上下文对象;
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 通过反射机制创建实例对象
Object obj = ctx.getBean("object");
最简单的Spring使用及其原理解析就是这么简单,重点在这种思想,理解了就知道为什么会是这样了!