spring框架搭建第一天

1. 从jdbc的标准代码了解程序的耦合性

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class JdbcDemo1 {
    public static void main(String[] args) throws Exception{
        //Class.forName("com.mysql.jdbc.Driver");
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "123456");
        PreparedStatement ps = conn.prepareStatement("insert into account(name, money) values(?,?)");
        ps.setString(1,"bbb");
        ps.setFloat(2,2000);
        ps.executeUpdate();
        ps.close();
        conn.close();
    }
}

程序执行过程,

  • 注册驱动
  • 获取链接
  • 获取操作数据库的预处理对象
  • 执行sql语句,得到结果集(本例是新增操作,故没有结果集)
  • 遍历结果集
  • 依次关闭结果集,预处理对象,连接。
    当我们把相关jar包去掉
    在这里插入图片描述
    可以发现,没有myql的驱动,将无法编译。此时可以理解为程序的耦合。
简单理解耦合:程序之间的依赖关系

耦合包括:

  • 类之间的依赖关系
  • 方法之间的依赖关系

如何解耦
降低程序之间的依赖

  • 编译期间不依赖,运行时才依赖(典型例子:DriverManager.registerDriver()转化为用反射来加载驱动Class.forName)

解耦的思路:

  1. 使用反射来创建对象,避免使用new关键字。
  2. 通过读取配置文件来获取要创建的对象全限定类名。

2. 基本的三层

dao层
package com.ssm.dao.impl;

import com.ssm.dao.IAccountDao;

public class AccountDao implements IAccountDao {
    public void saveAccount() {
        System.out.println("账户已保存!");
    }
}
package com.ssm.dao;
/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    //模拟保存账户
    void saveAccount();
}
service层
package com.ssm.service.impl;

import com.ssm.dao.IAccountDao;
import com.ssm.dao.impl.AccountDao;
import com.ssm.factory.BeanFactory;
import com.ssm.service.IAccountService;

import java.util.PriorityQueue;

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao = new AccountDao();

    public void saveAccount(){
        accountDao.saveAccount();
    }
}

package com.ssm.service;

/**
 * 账户业务层
 */
public interface IAccountService {
    //保存账户
    void saveAccount();
}
模拟的controller层(servlet)
package com.ssm.ul;

import com.ssm.factory.BeanFactory;
import com.ssm.service.IAccountService;
import com.ssm.service.impl.AccountServiceImpl;
/**
 模拟表现层
 */
public class Client {
    public static void main(String[] args){
        IAccountService as = new AccountServiceImpl();
        as.saveAccount();
    }
}
存在的问题
  1. controller层调用service层使用了new关键字,如果AccountServiceImpl.java不存在,则会编译不通过。代码耦合性太强。
  2. 假如有多次调用controller(main()多次执行),每次执行都会new一个AccountServiceImpl,AccountDao。假如执行5次controller,就会有10个service和dao对象在内存中。在高并发下性能不好

3. 使用工厂模式解决代码耦合性问题

什么是简单工厂模式

简单工厂模式又称为静态工厂模式,它属于创建型模式,但非23种设计模式之一。它可以根据传入的参数来返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

角色作用
工厂角色(Creator)是简单工厂模式的核心,它负责实现创建所有具体产品类的实例。工厂类可以被外界直接调用,创建所需的产品对象。
抽象产品角色(Product)是所有具体产品角色的父类,它负责描述所有实例所共有的公共接口。
具体产品角色(Concrete Product)继承自抽象产品角色,一般为多个,是简单工厂模式的创建目标。工厂类返回的都是该角色的某一具体产品。
简单工厂模式如何解耦(后续有待深究)

直接new一个对象是最简单的创建对象的方式。但是它也加强了两个对象A和B之间的耦合性。使用简单工厂模式,在A想使用B对象的时候,可以向工厂角色(Creator)请求创建一个实例对象,这样就避免了直接实例化B对象。降低了耦合性。

使用工厂模式解决代码耦合
package com.ssm.factory;


import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 创建一个bean对象的工厂
 */
public class BeanFactory {
    private static Properties props;

    static {
        try {
            props = new Properties();
            //getResourceAsStream(String path):默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。
            InputStream in  = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            System.out.println(in);
            props.load(in);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

    /**
     * 根据Bean名称获取Bean对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanName);
            System.out.println("beanPath:"+beanPath);
            bean = Class.forName(beanPath).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return bean;
    }
}

工厂读取的配置文件

accountService=com.ssm.service.impl.AccountServiceImpl
accuountDao=com.ssm.dao.impl.AccountDao

修改各处的对象实例化方式
比如controller

        //IAccountService as = new AccountServiceImpl();
        IAccountService as  = (IAccountService) BeanFactory.getBean("accountService");

4. 使用单例模式调对象复用性

前面已经使用简单工厂模式降低了ClientAccountServiceImpl的耦合性,但是代码仍然存在一个问题,那就是每次调用main()都会让BeanFactory.getBean()实例化一个对象,多次调用就会有多个实例对象产生,从而影响代码性能。那么如何让程序在运行中只让一个类只实例化一个对象呢?

让工厂不仅负责实例化对象还管理对象

前面实例化对象的工作是交给BeanFactory的,每次有实例化对象的请求过来,它就是实例化一个请求。那么能不能让BeanFactory先实例化所有对象,把它们管理起来。每次有实例化请求过来,就返回需要实例化对象的引用。这样就能保证内存中IAccountService只有一个实例化对象。

修改BeanFactory

修改静态代码块

    static {
        try {
            props = new Properties();
            //getResourceAsStream(String path):默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。
            InputStream in  = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            beans = new HashMap<String, Object>();
            Enumeration keys = props.keys();
            while (keys.hasMoreElements()){
                String key = keys.nextElement().toString();
                String keyPath = props.getProperty(key);
                Object bean = Class.forName(keyPath).newInstance();
                beans.put(key,bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

此时,BeanFactory中已经有所需要的对象实例了,那么获取bean的方法也要改

    public  static Object getBean(String beanName){
        return beans.get(beanName);
    }
什么是单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。与前面说的简单工厂模式一样,这种类型的设计模式也属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

5. 开始Spring框架

抽象前面的模型
更改前的
在这里插入图片描述
更改后的
在这里插入图片描述

5.1 控制反转IOC

百度词条解释
在这里插入图片描述
作用:降低程序代码的耦合(解除代码间的依赖关系)

5.2 创建一个spring工程

首先,创建一个基本的maven工程。
配置pom文件,导入jar包

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>

创建配置文件bean.xml

  1. 创建配置文件
  2. 导约束
  3. 把对象的创建交给spring来管理
<?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.ssm.service.impl.AccountServiceImpl" ></bean>

    <bean id="accuountDao" class="com.ssm.dao.impl.AccountDao"></bean>
    
</beans>

Client的配置

public class Client {
    public static void main(String[] args){
        ApplicationContext applicationContext = null;
        applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        //两种获取实例的方式
        IAccountService as = (IAccountService) applicationContext.getBean("accountService");
        IAccountDao accountDao = applicationContext.getBean("accuountDao",IAccountDao.class);

        System.out.println(as);
        System.out.println(accountDao);
    }
}

ApplicationContext的三个实现类:

  1. ClassPathXmlApplicationContext:加载类路径下的配置文件,要求配置文件必须在类路径下。否则加载不了。
  2. FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件(必须要有访问权限)
  3. AnnotationConfigApplicationContext:用于读取注解创建容器。
5.3 Spring是如何加载配置文件(Spring是什么时候实例化对象的)

前面说过,Spring的IOC指的是Spring代替Application管理我们的JavaBean。那么他是什么时候实例化这些JavaBean的呢?
一般有两种情况

  • 一次性创建所有的对象,就是说Spring一次性实例化所有的对象
  • 按需要加载对象,就是说需要哪个对象,Spring再来帮我们实例化它

这就对应着Spring核心容器的两个接口,ApplicationContextBeanFactory。其中

  • ApplicationContext:构建核心容器时,创建对象采取的策略是立即加载的方式。读完配置文件就创建配置文件中的对象。
  • BeanFactory:构建核心容器时,创建对象采取的策略是延迟加载的方式。什么时候根据id获取对象,什么时候创建对象。
    实验代码
    public static void main(String[] args){
//        ApplicationContext applicationContext = null;
        applicationContext = new FileSystemXmlApplicationContext("");
        applicationContext = new AnnotationConfigApplicationContext("");
//        applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//
//        //两种获取实例的方式
//        IAccountService as = (IAccountService) applicationContext.getBean("accountService");
//        IAccountDao accountDao = applicationContext.getBean("accuountDao",IAccountDao.class);
//
//        System.out.println(as);
//        System.out.println(accountDao);

        //==============使用BeanFactory创建对象==============
        BeanFactory factory = null;
        Resource rs = new ClassPathResource("bean.xml");
        factory = new XmlBeanFactory(rs);
        IAccountService as = (IAccountService) factory.getBean("accountService");
        System.out.println(as);


    }

对应的AccountServiceImpl代码

public class AccountServiceImpl implements IAccountService {


    private IAccountDao accountDao = new AccountDao();

	//重写构造器,看看该对象是什么时候创建的
    public AccountServiceImpl(){
        System.out.println("AccountServiceImpl被实例化了!");
    }

    public void saveAccount(){
        accountDao.saveAccount();
    }
}

6. Spring对bean的管理

6.1 创建bean的三种方式
6.1.1 使用默认构造函数创建bean

在spring的配置文件中使用<bean></bean>标签,配以idclass属性,并且标签中没有其他属性的时候。采用的就是默认类的构造函数创建bean对象。

<bean id="accountService" class="com.ssm.service.impl.AccountServiceImpl" ></bean>

此时如果AccountServiceImpl没有默认构造函数(构造函数被重写了),则无法创建对象。
报错,没有发现默认的构造器。

 Failed to instantiate [com.ssm.service.impl.AccountServiceImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ssm.service.impl.AccountServiceImpl.<init>()
6.1.2 使用普通工厂中的方法创建对象

假设一个情景
如果我们现在要用第三方jar包中的某个类,但是这个类的构造函数被重写了。那么我们该如何实例化这个类?
比如上面的AccountServiceImpl没有默认的构造函数,如何实例化它呢?

此时需要借助一个工厂类InstanceFactory,通过它来实例化我们的对象。

package com.ssm.factory;

import com.ssm.service.IAccountService;
import com.ssm.service.impl.AccountServiceImpl;

public class InstanceFactory {
    public IAccountService getAccountService(){
        return new AccountServiceImpl("用工厂实例化AccountServiceImpl");
    }
}
    <bean id="instanceFactory" class="com.ssm.factory.InstanceFactory"></bean>

    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

过程是先实例化instanceFactory,再调用instanceFactory中的getAccountService方法。

6.1.3 使用工厂中的静态方法创建对象

与上面的类似,只是不需要实例化工厂类了。直接调用工厂类中的静态方法。具体如下

    <!--使用工厂中的静态方法创建对象-->
    <bean id="accountService" class="com.ssm.factory.StaticFactory" factory-method="getAccountService"></bean>
package com.ssm.factory;

import com.ssm.service.IAccountService;
import com.ssm.service.impl.AccountServiceImpl;

public class StaticFactory {
    public static IAccountService getAccountService(){
        return new AccountServiceImpl("使用静态方法实例化AccountServiceImpl");
    }
}

可以在没有实例化StaticFactory情况下,使用它创建一个AccountServiceImpl对象。

回顾下static的知识
在Java类中,有六种成员:属性、方法、构造器、初始化块、内部类和枚举类,其中static可以修饰属性、方法、初始化块、内部类和枚举类。
以static修饰的的成员就是类成员。类成员属于整个类,而不是单个对象。

所以上面可以无需实例化StaticFactory 来创建AccountServiceImpl。因为类方法在类加载到jvm的时候就可以用了。
6.2 bean的作用范围

bean标签有一个scope属性:用于指定bean的作用范围。

取值作用
singleton单例(默认值)
prototype多例
request作用于web应用的请求范围
session作用于web应用的会话范围
global-session作用于集群环境的会话范围(全局会话范围),当不是集群环境时,等价于session
6.3 bean的生命周期

单例对象:我与容器共生死!!!
多例对象:
出生:使用对象时,由spring框架为我们创建
或者:只要是使用,就一直活着
死亡:长时间不用,且没有别的对象引用,有GC负责回收

实验,单例对象

<bean id="accountService" class="com.ssm.service.impl.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destory"></bean>
public class AccountServiceImpl implements IAccountService {


    //private IAccountDao accountDao = new AccountDao();

    public AccountServiceImpl(){
        System.out.println("AccountServiceImpl被实例化了!");
    }

    public void saveAccount(){
        System.out.println("保存账户成功!");
        //accountDao.saveAccount();
    }

    public void init(){
        System.out.println("AccountServiceImpl对象创建了");
    }

    public void destory(){
        System.out.println("AccountServiceImpl对象销毁了");
    }
}

Client片段
   public static void main(String[] args) throws Exception{
        ClassPathXmlApplicationContext applicationContext = null;
//        applicationContext = new FileSystemXmlApplicationContext("");
//        applicationContext = new AnnotationConfigApplicationContext("");
        applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        //两种获取实例的方式
        IAccountService as = (IAccountService) applicationContext.getBean("accountService");

        System.out.println(as);
        as.saveAccount();
        //手动关闭容器
        applicationContext.close();

        Thread.sleep(3000);

    }

结果:
在这里插入图片描述
spring创建时,先调用构造器,在调用init-method对应的方法,摧毁时调用destroy-method对应的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值