Spring(一)解耦,IOC

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");执行完就创建好了)
 *
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值