【Java入地】Spring 的 作用域 & MVC & SSM

Spring Framework 5 框架系列




概念


  • 什么是Spring?

    • Spring 是一个轻量级的(对象)控制翻转(IOC/DI)和面向切面编程(AOP)的对象容器框架;
    • 极大简化开发过程,降低开发难度;
  • 什么是控制翻转(IOC)?

    Spring 用户相当于窃取 Spring 成果的存在

    举例:

    我 想要生一个孩子

    我 需要付出的代价是:生孩子、喂奶、教他读书……

    我 生孩子的目的是:玩儿、洗完、赚钱、养老……

    现在有一个第三方机构,可以帮我完成生孩子所付出的代价,也就是可以替我生产、喂奶、教学

    所以我只要等着玩孩子、让他洗碗、赚钱就可以了

  • 什么是 依赖注入(DI) ?

    • 对象 new 出来之后,由 Spring 来决定 setget
    • IOC是思想,DI是解决方案
  • 下载

    • 官网(文档最底部)下载最新版的dist压缩包



五个核心 jar 包


commons-logging-1.2.jar
spring-beans-5.1.7.RELEASE.jar
spring-context-5.1.7.RELEASE.jar
spring-core-5.1.7.RELEASE.jar
spring-expression-5.1.7.RELEASE.jar

commons-lang.3.3.9.jar 需要自己下载



控制翻转 IOC 依赖注入 DI


  • 引用核心类
public class TestGetBean{
    public static void main(String[] args){
        // 原始方法 创建对象
        Person p = new Person();
        p.setAge(18);
        
        // 控制翻转后 创建对象
        ClsssPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");  
        // ↑ ↑  通过 ClassPath 查找 xml 文件 applicationContext.xml
        // ↑ ↑  通过 ApplicationContext(应用程序上下文) 查找 应用程序上下文
        // 有了上面两个就可以通过 ctx 进行 getBean 了
        Object bean = (Person)ctx.getBean("person");
        bean.setAge(18);
        bean.setName("张三");
        // -------- 分割线 ---------
        Sout(bean.getName());
        Sout(bean.getAge() );
    }
}
  • 配置文件 applicationContext.xml 中
// 添加
<beans …… >
	<bean id="person" class="com.…….Person"></bean>
</beans>

解耦合 && 属性


@Data // Lombok 注释:替代set / get / toString 方法
public class Person{
	private Food foot;
}

@Data
public class Food{
    private int hungry;
}
<beans …… >
    <bean id="person" class="…….Person">
    	<constructor-arg name="name" value="zhangsan"></constructor-arg>
      	<constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="food" ref="food"></constructor-arg>
    
    </bean>
</beans>

<constructor-arg name="name" value="zhangsan"></constructor-arg>

其中:constructor-arg是属性,value是,ref代表引用其他的类

Food food = ctx.getBean("food",Food.class);

String a = ToStringBuilder.reflectionToString(person);
Sout(a);

ApplicationContext.xml 文件的详情


<project xmlns="http://maven.apache.org/POM/4.0.0" <!--这里只是一个标签-->
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

xml文件的定义


  • 加载多个配置文件
// 文件一: ApplicationContext.xml
// 文件二: ApplicationContext-service.xml
// 调用:

// 方法一:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml","ApplicationContext-service.xml");
// 方法二:
//将字符串定义成一个数组

// 方法三:在一个 XML 文件中使用
<import resource="applicationContext-service1.xml">
<import resource="applicationContext-service2.xml">
// 优化
<import resource="application-*.xml">
    

// ………………

总结


  • new 换成 <bean id="xxx" class="xxx.class"></bean>
  • set换成 <constructor-arg name="xxx" value="xxx"></constructor-arg>
  • 配置文件的开头部分的功能
  • 引用 一个 / 多个 配置文件的方式



使用 Spring


  • 引用核心类
public class TestGetBean{
    public static void main(String[] args){
        ClsssPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");  
        // ↑ ↑  通过 ClassPath 查找 xml 文件 applicationContext.xml
        // ↑ ↑  通过 ApplicationContext(应用程序上下文) 查找 应用程序上下文
        // 有了上面两个就可以通过 ctx 进行 getBean 了
        Object bean = (Person)ctx.getBean("person");
        bean.setAge(18);
        bean.setName("张三");
        // -------- 分割线 ---------
        Sout(bean.getName());
        Sout(bean.getAge() );
    }
}
  • 配置文件 applicationContext.xml 中
// 添加
<beans …… >
	<bean id="person" class="com.…….Person"></bean>
</beans>





补充学习



什么是 单例?




什么是 Bean?


bean ,作为一个英文单词的意思是豆子,在 Spring 框架中,Bean 的意思就是一个实体,好比我创建一个 Son.java 如下:

@Data
public class Son{
	private int age;
	private String name;
}

那么这个类就是一个 Bean 对象,你创建的所有实体,每一个实体都是一个 Bean;



MVC 简介( 什么是MVC?)


  • 缘起:
    • 最初没有 MVC 的时候,只是一个简单的基于请求 / 响应的流模式
    • Serviet(request / response) 【请求】 → Service【处理业务逻辑】 → jsp【展示】
    • 【Model 控制层】一个业务逻辑都有一个 具体的类 来处理,将所有的 整合起来,称作 Service
    • 【View 视图层】展示 结果 的一个层,jsp、下载 等
    • 【Controller 接收层】接收前端给的数据层,接收之后给 Model 层来处理



SSM 结构

Spring / SpringMVC / Mybatis


  • Spring 作用:new 对象
    • DI 依赖注入(Controller 需要 Service,每次new一个十分消耗性能,DI 可以优化性能);
      • service(单例) - 处理逻辑
      • DAO(单例) - 处理数据库
      • pojo(多例) - 不归Spring容器管理
  • SpringMVC :
    • 一个MVC框架
  • MyBatis:
    • (使用 xml 文件)操作数据库



为什么用单例?


  • 性能好(节省内存);
  • 迸发情况下,容易产生 线程安全 问题;
  • 容器(如:Spring)接到请求,每一个请求都是一个 线程



单例类的注意事项:


  • 不能有状态数据,如果有要非常小心;
  • 一个线程改了,另一个线程读了这个样子;
  • ThreadLocal 用来做线程隔离的,线程 与 线程之间不共享,但是 某些实体类或者某些方法中写了private的属性(可以使用set来修改),一个刚set完(或者还没set结束),另一个就也来set了;



Spring 的作用域

这三个 Person完全一模一样,存储地址也一样;


ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

Person person1 = ctx.getBean("person",Person.class);

Person person2 = ctx.getBean("person",Person.class);

Person person3 = ctx.getBean("person",Person.class);




6 种(单例)作用域


  • singleton scope 单例作用域

  • prototype scope 原型作用域

  • websocket scope 基于连接 连接断开的时候对象就消失

  • request 基于请求的,请求打过来,对象就出来(只出来一次)

  • session 基于登录的,对象登陆了,就 new 一个对象(只有一个)

  • application 基于应用程序,应用程序启动,就 new 一个对象




ApplicationContext.xml 文件中加入 作用域的方式

做了以上更改之后,new出来的三个对象地址不一样了;


<bean id="person" scope="prototype" class="com.…….Person">
	<property name="name" value="和树不困"></property>
</bean>



Spring 对象生产的两种方式(只有两种)


  • 单例:singleton (每次去的都是同一个),包括
    • websocket
    • request
    • session
    • application
    • 以上四种绑定了生命周期,在一个生命周期内不会有两个不同的对象,getBean 的时候永远都是同一个对象;
  • new 出来: (scope = "prototype")

当 scope = "prototype" 的时候,每次 getBean 都是新的 Bean,否则都是一个 Bean;



工厂模式

创建一个工厂接口,需要什么直接传参生成;


public interface Car{
    public String getName();
    public String getPrice();
}

public class AoDi implements Car{
    public String getName(){
        return "奥迪A7";
    }
    
    public String getPrice(){
        return "100w";
    }
}
public class BaoMa implements Car{
    public String getName(){
        return "宝马R7";
    }
    
    public String getPrice(){
        return "120w";
    }
}

// 工厂方法
public class CarFactory{
    public Car getCar(String name){
        if(name.equals("Aodi")){
            retrun new Aodi();
        }else if{
            retrun new BaoMa();
        }else{
            throw new Exception("无法生产这辆车");
        }
    }
}
public static void main(String[] args){
    Car car = new CarFac
}

  • 在 ApplicationContext.xml 中定义这个工厂
<bean id="carFactory" class="com.…….carFactory"></bean>
<bean id="car" factory-bean="carFactory">
    <!--需要 set 方法-->
	<property name="name" value="Aodi"></property>
</bean>
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = ctx.getBean("car",Car.class);



循环引用(强引用)

A引用B / B引用C / C引用A: JVM计数器,当检测到还在被引用的时候就不会销毁它,所以这三个永远都不会被销毁,容易造成内存溢出


  • 在 Spring 中可以检测这种错误的做法,并作出提醒
  • 单例模式是可以允许这样引用的,但是 new 或者 prototype 是不允许的
  • 同时有 单例模式 & prototype 模式是允许的, 并且单例对象的引用对象也变成了单例的。



Spring 初始化 Bean 的四种方式(初始化顺序)


  • 按照 Bean 标签的顺序
// ApplicationContext.cml
<bean id="a" class="………….A.class"></bean>

<bean>标签的位置越靠前,则初始化越早;


  • 先加载 depends-on=" " 依赖于:
// ApplicationContext.cml
<bean id="a" class="………….A.class" depends-on="B"></bean>

A depends-on B,则在初始化 A 之前一定会初始化 B;


  • 懒加载

<lazy-init="true">,用到哪个加载哪个,不用到不加载。


<bean id="a" class="…….class" lazy-init="true" > 

</bean>

  • 强 / 弱引用

强引用:A完全持有一个类B,如果A不初始化,则这个类完全不能运行;
弱引用:A在一个方法里面用到了B的某一个属性值,不初始化B顶多A 的方法会报废;


<bean id="a" class="…….class" lazy-init="true">
	<property name="name">
    <value></value>
    </property>
</bean>

<bean id="b" class="…….class" lazy-init="true">
    <property name="name">
		<null></null>
    </property>
</bean>

<bean id="b" class="…….class" lazy-init="true">
</bean>
// 运行结果(TtoStringBuilder):

b.getName = null // 空指针
a.getName = 	 // 空字符串

a.getName().equals("");// true
b.getName().equals("");// 报错



自动装配

byName 匹配 ID ,A 里面引用的是 b 所以 B 的 id="b"可以,但是byType引用的是 C 这个类型;


<bean id="A" class="…….A" autowire="byName"></bean>
<bean id="b" class="…….B" autowire="byType"></bean>
<bean id="C" class="…….C" autowire="byName"></bean>
  • 配置默认装配的类型
<beans ……………… default-autowire="byType">
	<bean> …… </bean>
    <bean> …… </bean>
    <bean> …… </bean>
</beans>



学习要求


  • 掌握作用域
  • 掌握单例
  • 理解 MVC 与 SSM
  • 掌握工厂方式





总结:





Spring 的执行流程


在这里插入图片描述

我们从前端访问Controller层,又从Controller层访问数据库,就可以到达一个简单的 Spring 框架,但是这样执行的话 Controller 层既要接收前端数据 又要 处理数据 还要对接数据库,所以将“处理数据”这一部分单独划分出去,作为 Service 层,将 “ 对接数据库 ” 这一部分 分离出去,所谓,Mapper层,也就是常说的 Dao 层。

由于前端给出的访问不可能一次只有一个,所以我们实例化的对象不能是单例的,否则ABCD同时登陆的话,登录的可能是同一个用户。


为了让代码看起来更简洁,所以可以在 Application.xml 中写入 Bean 对象,直接实例化便可以 创建 / 引用 一个对象;


由于我们在编程的时候广泛使用注解,所以我们将逐个注解细分,常用注解如下:


注解名称 用途 备注
@Controller Controller层 用于标注当前是Controller层
@Service Service层 用于标注当前是Service层
@Mapper Mapper层 用于标注当前是Mapper层
@autowire 任意层 用于自动装配,下方举例子
@value 测试时给初值
@Data get / set / toString 方法的汇总 在实体类的最上方写上这个方法则不用再写set / get 方法,当然你可以对方法进行重写
@Override 方法重写的时候标注在上面,可以自动检错;
  • autowire 说明
//在Mapper层创建一个 SonService.java 的接口,用于连接数据库读取 Son 这个实体
public class SonController{ // Son 的 Controller 层
	@autowire Sonservice sonService // 这样就创建了一个 SonService 的接口
	List<son> sonList = sonService.list(); //这样就能获取 son 数据库中所有的数据
}
posted @ 2021-01-13 14:41  不食花生的猫  阅读(166)  评论(0编辑  收藏  举报