解耦,IOC原理和spring中的IOC
- 1.Spring是什么
- 2.耦合以及解耦
- 2.1 例一:JDBC工程代码分析程序耦合
- 2.2 例二:工厂模式实现解耦(手动实现)
- 2.2.1 工厂类
- 2.2.2 Service层
- 2.2.3 Dao层
- 2.2.4 测试
- 2.2.5 思考
- 2.2.6 解决多例问题
- 3 spring基于XML的IOC实现解耦(将对象的创建交给spring)
- 3.1 XML配置文件
- 3.2 Service和Dao层
- 3.3 获取spring的IOC核心容器,并根据id获取对象
- 3.4 spring核心容器的2个接口及3个实现类
1.Spring是什么
轻量级开源框架,以IOC和AOP为内核,提供展现层SpringMVC和持久层Spring JDBC 以及业务层事务管理等技术,还能整合开源世界众多著名的第三方框架和类库
2.耦合以及解耦
耦合:简单来说就是指程序间的依赖关系。一般包括:类之间的依赖,方法间的依赖。
解耦:降低程序间的依赖关系
实际开发中:应该做到编译器不依赖,运行时才依赖解耦思路:
第一步:使用反射来创建对象,而避免使用new关键字
第二步:通过读取配置文件来获取要创建的对象全限定类名
2.1 例一:JDBC工程代码分析程序耦合
DriverManager.registerDriver(new com.mysql.jdbc.Driver());是依赖一个具体的驱动类,没有导mysql-connector-java.jar驱动包时编译时会报错
而Class.forName("com.mysql.jdbc.Driver")中的com.mysql.jdbc.Driver只是字符串,是通过反射来创建驱动类对象,此时即使驱动类不存在,也不会在编译的时候报错,而是运行时报错
public class JdbcDemo1 {
public static void main(String[] args) throws Exception {
// 1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/eesy?useUnicode=true&characterEncoding=utf8&useSSL=true", "root", "root");
// 3.获取操作数据库的预处理对象
PreparedStatement ps = conn.prepareStatement("select * from account");
// 4.执行SQL,得到结果集
ResultSet rs = ps.executeQuery();
// 5.遍历结果集
while (rs.next()) {
System.out.println(rs.getString("name"));
}
// 6.释放资源
rs.close();
ps.close();
conn.close();
}
}
2.2 例二:工厂模式实现解耦(手动实现)
2.2.1 工厂类
第一步: 需要一个配置文件配置我们的service和dao
配置的内容:唯一标识=全限定类名(key=value)
第二步: 通过读取配置文件中的配置的内容,反射创建对象
配置文件可以是xml也可以是properties
/**
* 一个创建Bean对象的工厂
*
* Bean:在计算机英语中,有可重用组件的含义
* JavaBean:用Java语言编写的可重用组件。
* javabean > > 实体类
* 它就是创建我们的service和dao对象
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//使用静态代码块为Properties对象赋值
static{
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
props.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败");
}
}
/**
* 根据Bean的名称获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean= null ;
String beanPath = props.getProperty(beanName);
try {
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
2.2.2 Service层
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao =(IAccountDao) BeanFactory.getBean("accountDao");
@Override
public void saveAccount(){
accountDao.saveAccount();
}
2.2.3 Dao层
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("账户保存成功");
}
}
2.2.4 测试
public class Client {
public static void main(String[] args) {
// IAccountService as = new AccountServiceImpl();
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
as.saveAccount();
}
}
2.2.5 思考
public class Client {
public static void main(String[] args) {
// IAccountService as = new AccountServiceImpl();
for (int i=0;i<5;i++){
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(as);
as.saveAccount();
}
}
}
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao =(IAccountDao) BeanFactory.getBean("accountDao");
private int i = 1;
@Override
public void saveAccount(){
accountDao.saveAccount();
System.out.println(i);
i++;
}
}
// 输出:
com.java.service.impl.AccountServiceImpl@4554617c
账户保存成功
1
com.java.service.impl.AccountServiceImpl@74a14482
账户保存成功
1
com.java.service.impl.AccountServiceImpl@1540e19d
账户保存成功
1
此时的对象是多例的
但是由于service和dao实现类中一般是没有成员变量(private int i = 1;),且在方法中有调整的,所以不存在线程安全问题, 就算有成员变量也是定义在方法中使用。
为提高效率所以应该将多例改为单例
2.2.6 解决多例问题
在工厂类中新建一个Map用于存储对象,在第一次运行的时候,将配置文件中的多个对象一次性创建并且加到Map中,之后在调用getBean方法的时候通过传入的类名从Map中取出一开始就创建好的对象即可。
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象,我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static{
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中的所有key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化properties失败");
}
}
/**
* 根据bean的名称获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
public class Client {
public static void main(String[] args) {
// IAccountService as = new AccountServiceImpl();
for (int i=0;i<3;i++){
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(as);
as.saveAccount();
}
}
}
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao =(IAccountDao) BeanFactory.getBean("accountDao");
// private int i = 1;
@Override
public void saveAccount(){
int i = 1;
accountDao.saveAccount();
System.out.println(i);
i++;
}
}
//输出:
com.java.service.impl.AccountServiceImpl@4554617c
账户保存成功
1
com.java.service.impl.AccountServiceImpl@4554617c
账户保存成功
2
com.java.service.impl.AccountServiceImpl@4554617c
账户保存成功
3
3 spring基于XML的IOC实现解耦(将对象的创建交给spring)
3.1 XML配置文件
配置唯一标识id与相应类的对应关系。使得可以根据id获得对应的对象。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--把对象的创建交给spring来管理-->
<bean id="accountService" class="com.java.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.java.dao.impl.AccountDaoImpl"></bean>
</beans>
3.2 Service和Dao层
注意:从AccountServiceImpl类(private IAccountDao accountDao = new AccountDaoImpl();)可以看出还没实现Service和Dao的解耦(需要到后面的DI才能实现)
//Service
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
@Override
public void saveAccount(){
accountDao.saveAccount();
}
}
//Dao
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("账户保存成功");
}
}
3.3 获取spring的IOC核心容器,并根据id获取对象
注意:现在只是实现了和service层的耦合
/**
* 获取spring的IOC核心容器,并根据id获取对象
*/
public class Client {
public static void main(String[] args) {
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = (IAccountService) ac.getBean("accountService");
as.saveAccount();
}
}
//输出:
com.java.service.impl.AccountServiceImpl@27efef64
账户保存成功
3.4 spring核心容器的2个接口及3个实现类
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\zhy\\Desktop\\bean.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(adao);
as.saveAccount();
//--------BeanFactory----------
// Resource resource = new ClassPathResource("bean.xml");
// BeanFactory factory = new XmlBeanFactory(resource);
// IAccountService as = (IAccountService)factory.getBean("accountService");
// System.out.println(as);
}
* ApplicationContext的三个常用实现类:
* ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
* FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
* AnnotationConfigApplicationContext:它是用于读取注解创建容器的,是后面的内容。
*
* 核心容器的两个接口引发出的问题:
* ApplicationContext: 单例对象适用 采用此接口
* 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。(ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");执行完就创建好了)
*
* BeanFactory: 多例对象使用
* 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。(IAccountService as = (IAccountService)factory.getBean("accountService");执行完就创建好了)
*