(Spring+SpringMVC+MyBatis) SSM框架教程

黑马程序员最全SSM框架教程|Spring+SpringMVC+MyBatis全套教程

01. Spring 简介

1.1 Spring 是什么

  • Spring 是分层的 Java SE/EE 应用 full-stack(全栈)轻量级开源框架,以 IoC(Inversion of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
注:轻量级:API 简单,学习成本低full-stack(全栈):在这儿指在各层都有对应的解决方案Web 层:Spring MVCDAO 层:JDBC 模板、Spring Data
  • Spring 提供了 展现层 Spring MVC 和 持久层 Spring JDBC Template 以及 业务层事务管理 等众多的企业级应用技术,还能整合开源世界众多的著名的第三方框架和类库逐渐成为使用最多的 JAVA EE 企业级开源框架。

1.2 Spring 发展历程

1997 年,IBM 提出了 EJB 的思想。
1998 年,SUN 制定开发标准规范 EJB 1.0。
1999 年,EJB 1.1 发布。
2001 年,EJB 2.0 发布。
2003 年,EJB 2.1 发布。
2006 年,EJB 3.0 发布。
  • Rod Johnson(Spring 之父)
著作:Expert One-to-One J2EE Design and Development(2002)阐述了 J2EE 使用 EJB 开发设计的优点及解决方案。Expert One-to-One J2EE Development without EJB(2004)阐述了 J2EE 不使用 EJB 开发的解决方案(Spring 的雏形)
  • 2017 年 9 月份 发布了 Spring 的最新版本——Spring 5.0 通用版(GA)

1.3 Spring 的优势

1.3.1 方便解耦,简化开发

 通过 Spring 提供的 IoC(Inverse of Control)容器,可以将对象间的依赖交由 Spring 进行控制,避免硬编码所造成的过渡耦合。用户也不必再为 单例模式类、属性文件解析 等这些很底层的需求编写代码,可以更专注于上层的应用。

1.3.2 AOP 编程的支持

 通过 Spring 的 AOP 功能,方便进行 面向切面编程,许多不容易用传统 OOP(Object Oriented Programming:面向对象程序设计)实现的功能可以通过 AOP(Aspect Oriented Programming:面向切面编程/函数式编程)轻松实现。(通过 预编译方式 和 运行期间动态代理 实现程序功能的统一维护的一种技术。AOP 是 OOP 的一种延续)

1.3.3 声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务管理,提高开发效率和质量。

1.3.4 方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

1.3.5 方便集成各种优秀框架

Spring 对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的支持。
  • Struts:
 Struts 是 Apache 软件基金会(ASF)赞助的一个 开源项目。他最初是 Jakarta 项目中的一个子项目,并在 2004 年 3 月成为 ASF 的顶级框架。它通过采用 Java Servlet / JSP 技术,实现了基于 Java EE Web 应用的 MVC(Model-View-Controller)设计模式的应用框架,是 MVC 经典设计模式中的一个经典产品。Jakarta:开放源代码开发项目,受到 IBM 等公司在财力和技术上的鼎力支持。著名的 Tomcat 服务器 即出自 Jakarta 旗下。为方便管理,剥离 Tomcat、Lucene 等大型、成熟的子项目,成为相对独立的 Apache 子项目。Jakarta 项目于 2011 年 12 月 21 日 退役,无剩余子项目。其独立的子项目包括:Ant、Avalon、Commons、DB、Excalibur、Gump、HiveMind、HttpComponents、James、Logging、Lucene、Maven、POI、Portals、Struts、Tapestry、Tomcat、Turbine、Velocity、Watchdog、BCEL、BSF、Cactus、ECS、JCS、JMeter、ORO、Regexp、Taglibs。
  • Hibernate:
 Hibernate 是一个 开放源代码 的 对象关系映射 框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 ORM 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。Hibernate 可以应用在任何使用 JDBC 场合,既可以在 Java 的客户端程序使用,也可以在 Servlet / JSP 的 Web 应用中使用,最具革命意义的是,Hibernate 可以在应用 EJB 的 Java EE 架构中取代 CMP,完成 数据持久化 的重任。ORM(Object Relational Mapping:对象关系映射)框架采用 元数据 来描述对象与关系映射的细节,元数据一般采用 XML 格式,并且存放在专门的 对象-映射文件 中。简单理解为一种框架的格式。当前的 ORM 框架主要有五种:Hibernate(Nhibernate)、Ibatis、Mybatis、EclipseLink、JFinal。CMP指令:CMP(Chip Multiprocessors:单芯片多处理器)指令是由美国斯坦福大学提出的,指 多核心 其思想是将大规模 并行处理器中的 SMP(对称多处理器)集成到统一芯片内,各个处理器 并行执行 不同的进程。与 CMP 比较,SMT 处理器结构 的 灵活性 比较突出。
  • Hessian:
 Hessian 是一个轻量级的 remoting onHTTP 工具,使用简单的方法提供了 RMI 的功能。相比 WebService,Hessian更简单、快捷。采用的是二进制 RPC 协议,因为采用的是二进制协议,所以它很适合用于发送二进制数据。
  • Quartz:
 Quartz 是 OpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目,它可以与 J2EE 与 J2SE 应用程序相结合,也可以单独使用。Quartz 可以用来创建简单或为运行 十个、百个甚至是好几万个 Jobs 这样复杂的程序。Jobs 可以做成标准的 Java 组件或 EJBs。Quartz 最新版本为 Quartz 2.3.2。

1.3.6 降低 Java EE API 的使用难度

Spring 对 Java EE API(如 JDBC、Java Mail、远程调用 等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

1.3.7 Java 源码是经典学习范例

Spring 的 源代码 设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无疑是 Java 技术的最佳实践的范例。

1.4 Spring 的体系结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOcxw6Of-1645447562201)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\SpringFrameworkRuntime.png)]

02. Spring 快速入门

2.1 Spring 程序开发步骤

2.1.1 未使用 Spring 解耦之前的开发流程

com.itheima.dao.UserDaoImplcreate 方法;delete 方法;update 方法;select 方法;com.itheima.service.UserServiceImpl//通过 new 一个 UserDaoImpl 对象来使用UserDao   userDao = new UserDaoImpl();

2.1.2 使用 Spring 解耦之后的开发流程

com.itheima.dao.UserDaoImplcreate 方法;delete 方法;update 方法;select 方法;XML 配置文件id 标识="com.itheima.dao.UserDaoImpl"引入 Spring 框架读取 XML 配置文件;根据 id 标识 获取 全包名 / Bean 全限定名;根据 反射 创建 Bean 对象;返回对象。com.itheima.service.UserServiceImpl//此时不需要再 new UserDaoImpl 对象,直接找 Spring 框架 要UserDao userDao = Spring 客户端.getBean( id 标识 )

2.1.3 Spring 程序开发步骤——文字版

1.导入 Spring 开发的基本包坐标
2.编写 Dao 接口和实现类
3.创建 Spring 核心配置文件
4.在 Spring 配置文件中配置 UserDaoImpl
5.使用 Spring 的 API 获得 Bean 实例
注:Dao层:DAO(Data Access Object:数据访问对象)是一个面向对象的数据库接口,它显露了 Microsoft Jet 数据库引擎(由 Microsoft Access 所使用),并允许 Visual Basic 开发者通过 ODBC 像直接连接到其他数据库一样,直接连接到 Access 表。DAO 最适用于单系统应用程序或小范围本地分布使用。ODBC(Open Database Connectivity:开放数据库连接)是为解决 异构数据库 间的数据共享而产生的,现已成为 WOSA(The Windows Open System Architecture:Windows 开放系统体系结构)的主要部分和基于 Windows 环境的一种数据库访问接口标准。ODBC 为异构数据库访问提供统一接口,允许应用程序以 SQL 为数据存取标准,存取不同 DBMS(DataBase Management System:数据库管理系统)管理的数据;使应用程序直接操纵 DB(DataBase)中的数据,免除随 DB 的改变而改变。用 ODBC 可以访问各类计算机上的 DB 文件,甚至访问如 Excel 表和 ASCII 数据文件这类非数据库对象。

2.7 知识要点

2.7.1 Spring 的开发步骤

1.导入坐标
2.创建 Bean
3.创建 applicationContext.xml
4.在配置文件中进行配置
5.创建 ApplicationContext 对象 getBean

03. Spring 配置文件

3.1 Bean 标签基本配置

  • 用于配置对象交由 Spring 来创建
  • 默认情况下它( Spring ) 调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
基本属性:id:Bean 实例在 Spring 容器中的唯一标识class:Bean 的 全限定名称 / 全包名

3.2 Bean 标签范围配置

  • scope:指对象的作用范围,取值如下:
取值范围             说明singleton     默认值,单例的prototype     多例的,原型模式request          Web 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中session         Web 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中global session  Web 项目中,应用在 Portlet 环境,如果没有 Portlet 环境,那么 globalSession 相当于                Session

3.2.1 当 scope 的取值为 singleton 时

Bean 的实例化个数:1 个
Bean 的实例化时机:当 Spring 核心文件被加载时,实例化配置的 Bean 实例
<bean id="userDaoScopeSingleton" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton"></bean>

Bean 的生命周期:

  • 对象创建:当应用加载,创建容器时,对象就被创建了
  • 对象运行:只要容器在,对象就一直活着
  • 对象销毁:当应用卸载,销毁容器时,对象被销毁

3.2.2 当 scope 的取值为 prototype 时

Bean 的实例化个数:多个 / 有几个对象就实例化几次
Bean 的实例化时机:当调用 getBean() 方法时 实例化 Bean
<bean id="userDaoScopePrototype" class="com.itheima.dao.impl.UserDaoImpl" scope="prototype"></bean>

Bean 的生命周期:

  • 对象创建:当使用对象时,创建新的对象实例
  • 对象运行:只要对象在使用中,就一直活着
  • 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收

3.3 Bean 生命周期配置

  • init-method:指定类中的初始化方法名称
  • destory-method:指定类中销毁方法名称
package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public void init(){System.out.println("Dao ------ 初始化方法......");}public void destroy(){System.out.println("Dao ------ 销毁方法......");}
}
<?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"><bean id="userDaoInitAndDestroy" class="com.itheima.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
</beans>
@Test
public void springBeanInitAndDestroyTest(){ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println(app.getBean("userDaoInitAndDestroy"));app.close();
}

3.4 Bean 实例化的三种方式

3.4.1 无参构造方法实例化

package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl(){System.out.println("Dao ------ 无参构造方法......");}
}
<?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"><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>
@Test
public void springProxyTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserDao userDao = (UserDao) app.getBean("userDao");System.out.println(userDao);
}

3.4.2 工厂静态方法实例化

package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public static UserDaoImpl(){System.out.println("Dao ------ 无参构造方法......");}
}
<?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"><bean id="springBeanStaticFactory" class="com.itheima.dao.factory.BeanStaticFactory" factory-method="getUserDao"></bean>
</beans>
@Test
public void springProxyTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserDao userDao = (UserDao) app.getBean("springBeanStaticFactory");System.out.println(userDao);
}

3.4.2 工厂实例方法实例化

package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl(){System.out.println("Dao ------ 无参构造方法......");}
}
<?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"><!--动态Bean工厂需先配置 动态工厂,再根据动态工厂名指定工厂需执行的方法--><bean id="springBeanDynamicFactory" class="com.itheima.dao.factory.BeanDynamicFactory"></bean><bean id="springBeanDynamicFactoryGetUserDao" factory-bean="springBeanDynamicFactory" factory-method="getUserDao"></bean>
</beans>

3.5 Spring 的重点配置

<beans><bean>    标签id 属性:在容器中 Bean 实例的唯一标识,不允许重复class 属性:要实例化的 Bean 的全限定名scope 属性:Bean 的作用范围,常用是 singleton(单例模式)和 prototype(原型模式/多例模式)<property>    标签:属性注入name 属性:属性名称value 属性:注入的普通属性值ref 属性:注入的对象引用值<list>标签<value>value</value></list><map>标签(key 和 value)<entry key="key" value-ref="value-ref"></entry></map><properties>标签(key 和 value 均为 字符串)<props><prop key="key">value</prop></props></properties></property><constructor-arg>有参结构体 set 注入 标签</constructor-arg><constructor-arg name="userDao" ref="set 方法 - set 转 小驼峰"></constructor-arg></bean><import>标签:导入其他的 Spring 的分文件</import><import resource="applicationContext-user.xml"></import>
</beans>

3.6 Bean 的依赖注入分析

3.6.1 UserService 实例 与 UserDao 实例 在容器外部结合

目前 UserService 实例 和 UserDao 实例 都存在于 Spring 容器中,当前的做法是在容器外部获得 UserService 实例 和 UserDao 实例,然后在程序中进行结合。
         程序代码                                             Spring 容器
---------------------------------------------------------------------------
getBean(name:"userService") <----------------------------- UserService 实例获得 UserService 实例,发现 UserService 实例内部需要 UserDao 实例 的 save()方法,
所以在 UserService 内部获得 UserDao 实例getBean(name:"userDao") <------------------------------------- UserDao 实例
---------------------------------------------------------------------------

3.6.2 UserDao 实例 直接注入到 UserService 实例 中

因为 UserService 和 UserDao 都在 Spring 容器中,而最终程序直接使用的是 UserService,所以可以在 Spring 容器中,将 UserDao 设置到 UserService 内部。
        程序代码                                          Spring 容器
---------------------------------------------------------------------------
getBean(name:"userService")                           UserService 实例
获得 UserService 实例,内部已经                                 ^
存在 UserDao 实例 了,直接调用                                  ||
UserDao 的 save() 方法即可                                UserDao 实例
---------------------------------------------------------------------------
3.6.2.1 依赖注入方式——Set()
3.6.2.2 依赖注入方式——构造方法

3.7 Bean 的依赖注入

3.7.1 Bean 的依赖注入概念

  • 依赖注入(Dependency Injection):它是 Spring 框架核心 IoC(Inverse of Control)的具体实现。
 在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IoC(Inverse of Control)解耦只是降低它们的依赖关系,但是不会消除。例如:业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单地说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

3.7.2 Bean 的依赖注入方式

怎么将 UserDao 怎样 注入到 UserService 内部呢?
3.7.2.1 set 方法注入
  • 传统 set 方法注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"></property>
</bean>
  • P 空间命名注入
 P 命名空间注入本质也是 set 方法注入,但比起来上述的 set 方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入 P 命名空间:
xmlns:p="http://www.springframework.org/schema/p"
其次,需要修改注入方式
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<bean id="userService"    class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>
package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl(){System.out.println("Dao ------ 无参构造方法......");}
}
package com.itheima.service;
public interface UserService {public void save();
}
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao){this.userDao = userDao;}@Overridepublic void save() {userDao.save();}
}
<?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"><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<!--    property 中 name 指 set 方法名 除去 set 外的命名且为小驼峰命名(如:setUserDao -> userDao),ref 指向 需要注入的依赖    --><property name="userDao" ref="userDao"></property></bean>
</beans>
@Test
public void userServiceTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) app.getBean("userService");userService.save();
}
3.7.2.2 有参构造方法注入
package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl(){System.out.println("Dao ------ 无参构造方法......");}
}
package com.itheima.service;
public interface UserService {public void save();
}
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceImpl implements UserService {private UserDao userDao;
//  此处 空参 构造方法 是为了 Spring 容器代理时能够创建对象 public UserServiceImpl(){}public UserServiceImpl(UserDao userDao){this.userDao = userDao;}@Overridepublic void save() {userDao.save();}
}
<?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"><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<!--    constructor-arg 中 name 指 构造方法 参数名,ref 指向 需要注入的依赖    --><constructor-arg name="userDao" ref="userDao"></constructor-arg></bean>
</beans>
@Test
public void userServiceTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) app.getBean("userService");userService.save();
}

3.8 Bean 的依赖注入的数据类型

上面的操作,都是注入的引用 Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型:

  • 普通数据类型
package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl() {}private String username;private int age;public void setUsername(String username) {this.username = username;}public void setAge(int age) {this.age = age;}@Overridepublic void save() {System.out.println(username + " 今年 " + age + " 岁了。");}
}
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"><property name="username" value="25">妖刀</property><property name="age" value="23">瓜女子</property>
</bean>
@Testpublic void userServiceTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) app.getBean("userService");userService.save();
}
  • 引用数据类型
package com.itheima.dao;
public interface UserDao {}
public class User {private String username;private int age;    public String getUsername() { return username; }public void setUsername(String username) {  this.username = username; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }@Overridepublic String toString() {return "User{" + "username='" + username + ", age=" + age + '}';}
}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl() {}private Map<String, User> userMap;public void setUserMap(Map<String, User> userMap) { this.userMap = userMap; }@Overridepublic void save() {System.out.println(userMap);}
}
<bean id="user1" class="com.itheima.util.User"><property name="username" value="赵辰"></property><property name="age" value="25"></property>
</bean>
<bean id="user2" class="com.itheima.util.User"><property name="username" value="赵研"></property><property name="age" value="23"></property>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"><property name="userMap"><map><entry key="user1" value-ref="user1"></entry><entry key="user2" value-ref="user2"></entry></map></property>
</bean>
@Testpublic void userServiceTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) app.getBean("userService");userService.save();
}
  • 集合数据类型
package com.itheima.dao;
public interface UserDao {}
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {public UserDaoImpl() {}private List<String> stringList;private Map<String, User> userMap;private Properties properties;public void setStringList(List<String> stringList) { this.stringList = stringList; }public void setUserMap(Map<String, User> userMap) { this.userMap = userMap;}public void setProperties(Properties properties) { this.properties = properties; }@Overridepublic void save() {System.out.println(stringList);System.out.println(properties);}
}
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"><property name="stringList"><list><value>妖</value><value>刀</value><value>真</value><value>棒</value></list></property>
<!-- map 集合的用法 详见 引用数据类型 示例      -->      <property name="userMap"><map><entry key="user1" value-ref="user1"></entry><entry key="user2" value-ref="user2"></entry></map></property>
<!-- 注:property 本身 key 和 value 都是 字符串     -->      <property name="properties"><props><prop key="乌龟">王八</prop><prop key="土豆">洋芋</prop></props></property>
</bean>
@Testpublic void userServiceTest(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) app.getBean("userService");userService.save();
}

3.9 引入其他配置文件(分模块开发)

实际开发中,Spring 的配置内容非常多,这就导致 Spring 配置很繁杂且体积很大。所以,可以将部分配置拆解到其他配置文件中,而在 Spring 主配置文件中 通过 import 标签 进行加载
<import resource="applicationContext-user.xml"></import>

04. Spring 相关 API

4.1 ApplicationContext 的继承体系

  • ==applicationContext:==接口类型,代表应用上下文,可以通过其 实例 获得 Spring 容器中的 Bean 对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1pWkn55-1645447562211)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\ClassPathXmlApplicationContext.jpg)]

4.2 ApplicationContext 的实现类

4.2.1 ClassPathXMLApplicationContext

它是从类的根路径下加载配置文件推荐使用这种
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

4.2.2 FileSystemXmlApplicationContext

它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
ApplicationContext app = new FileSystemXmlApplicationContext("D:\Spring\applicationContext.xml");

4.2.3 AnnotationConfigApplicationContext

当使用注解配置容器对象时,需要使用此类来创建 spring 容器。他用来读取注解。

4.3 getBean()方法使用

4.3.1 Spring 根据 id 查找 Bean

  • 当 Spring 根据 id 查找 Bean 时,Spring 容器中 允许出现多个 同类型的 Bean
//   传入id,Spring 根据 id 查找 相对应的 Bean
public Object getBean(String name) throws BeanException {assertBeanFactoryActive();return getBeanFactory().getBean(name);
}
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");

4.3.2 Spring 根据 字节码对象 / 类型 查找 Bean

  • 当 Spring 根据 字节码对象 查找 Bean 时,Spring 容器中 只允许有一个 同类型的 Bean
//   传入 字节码对象,Spring 会根据 类型 帮你从容器中进行相应的匹配
public <T> T getBean(Class<T> requiredType) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(requiredType);
}
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = app.getBean("UserDao.class");
注:当参数的数据类型是字符串时,表示根据 Bean 的 id 从容器中获得 Bean 实例,返回是 Object,需要强制转换。当参数的数据类型是 Class 类型时,表示根据类型从容器中匹配 Bean 实例,当容器中相同类型的 Bean 有多个时,则此方法会报错。

4.4 知识要点

  • Spring 的重点 API
重点1:  怎么去创建 ApplicationContext 对象ApplicationContext app = new ClassPathXmlApplicationContext("XML文件");ApplicationContext app = new FileSystemXmlApplicationContext("Xml文件 的 绝对路径");ApplicationContext app = new AnnonationXmlApplicationContext("");//使用注解进行创建对象
重点2: getBean() 怎么去用app.getBean("id");app.getBean(*.Class);

05. Spring 配置数据源

5.1 数据源(连接池)

5.1.1 数据源(连接池)的作用

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

  • 数据源(连接池)是提高程序性能出现的
  • 事先 实例化 数据源,初始化部分连接资源
  • 使用连接资源时 从数据源中获取
  • 使用完毕后 将连接资源 归还给数据源

5.1.2 数据源(连接池)的开发步骤

1.   导入数据源的坐标和数据库驱动坐标
2.  创建数据源对象
3.  设置数据源的基本连接数据(驱动、数据库地址、用户名、密码 等)
4.  使用数据源 获取连接资源 和 归还连接资源

5.2 数据源的手动创建

<dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!-- https://mvnrepository.com/artifact/c3p0/c3p0 --><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency>
</dependencies>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/employment
jdbc.username=root
jdbc.password=123456
   @Test
//    测试手动创建 c3p0 数据源(加载 properties 配置文件)public void propertiesReadFileTest() throws Exception {//        加载配置文件ResourceBundle bundle = ResourceBundle.getBundle("jdbc");String driver = bundle.getString("jdbc.driver");String jdbcUrl = bundle.getString("jdbc.url");String username = bundle.getString("jdbc.username");String password = bundle.getString("jdbc.password");
//        创建数据连接对象ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driver);dataSource.setJdbcUrl(jdbcUrl);dataSource.setUser(username);dataSource.setPassword(password);Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}

5.3 Spring 配置数据源

可以将 DataSource 的创建权交由 Spring 容器去完成
<dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!-- https://mvnrepository.com/artifact/c3p0/c3p0 --><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version></dependency>
</dependencies>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass"    value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl"    value="jdbc:mysql://localhost:3306/employment"></property><property name="user"   value="root"></property><property name="password"   value="123456"></property>
</bean>
public void springDataSourceTest() throws SQLException {ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-dataSource.xml");DataSource dataSource = app.getBean(DataSource.class);Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}

5.4 抽取 jdbc 配置文件

ApplicationContext.xml 加载 jdbc.properties 配置文件获得连接信息
首先,需要引入  Context 命名空间和约束路径:
  • 命名空间:xmlns:context=“http://www.springframework.org/schema/context”
  • 约束路径1:http://www.springframework.org/schema/context
  • 约束路径2:http://www.springframework.org/schema/context/spring-context.xsd
<context:property-placeholder localhost="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driver}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property>
</bean>

5.4 知识要点

Spring 容器加载 properties 文件

<context:property-placeholder location="*.properties"/>
<property name="" value="${key}"></property>

06. Spring 注解开发

6.1 Spring 原始注解

Spring 是轻代码而重配置的框架。配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替 xml 配置文件,可以简化配置,提高开发效率。
Spring 原始注解主要是替代 <Bean> 的配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2OLhyFyx-1645447562215)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\Spring原始注解.png)]

6.1.1 @Component

@component(组件)                     使用在类上用于实例化 Bean
@Component("userDao")
public class UserDaoImpl implements UserDao {……}
@Component("userService")
public class UserServiceImpl implements UserService {……}
@Component("userController")
public class UserControllerImpl implements UserController {……}

6.1.2 @Controller

@Controller(语义化·Web层)         使用在web层类上用于实例化Bean
@Controller("userController")
public class UserControllerImpl implements UserController {……}

6.1.3 @Service

@Service(语义化·Service层)            使用在service层类上用于实例化Bean
@Service("userService")
public class UserServiceImpl implements UserService {……}

6.1.4 @Repository

@Repository(语义化·Dao层)         使用在dao层类上用于实例化Bean
@Repository("userDao")
public class UserDaoImpl implements UserDao {……}

6.1.5 @AutoWired

@AutoWired(对象引用注入·替代ref)   使用在字段上用于根据类型依赖注入
……
@Autowired //默认 寻找 DataSource.class
private DataSource dataSource;
@Test
public void getDataSource() throws Exception {Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}
……

6.1.6 @Qualifier

@Qualifier(对象引用注入·替代ref)   结合@AutoWired一起使用用于根据名称进行依赖注入
……
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Test
public void getDataSource() throws Exception {Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}
……

6.1.7 @Resources

@Resources(对象引用注入·替代ref)   相当于@AutoWired+@Qualifier,按照名称进行注入
……
@Resources("dataSource")
private DataSource dataSource;
@Test
public void getDataSource() throws Exception {Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}
……

6.1.8 @Value

@Value(Bean的值)                     注入普通属性
@Value("${jdbc.driver}")
private String driver;

6.1.9 @Scope

@Scope(Bean范围)                     标注 Bean 的作用范围
@Repository("userDao")
@Scope("singleton")或@Scope("prototype")
public class UserDaoImpl implements UserDao {}

6.1.10 @PostConstruct

@PostConstruct(Bean 初始化)      使用在方法上标注该方法是 Bean 的初始化方法
@Repository("userDao")
public class UserDaoImpl implements UserDao {@PostConstrutpublic void init(){System.out.println("Dao 对象的初始化方法")}
}

6.1.12 @PreDestory

@PreDestory(Bean销毁)                使用在方法上标注该方法是 Bean 的销毁方法
@Repository("userDao")
public class UserDaoImpl implements UserDao {@PreDestorypublic void destory(){System.out.println("Dao 对象的销毁方法")}
}

6.2 Spring 新注解

使用 Spring 原始注解 不能完全替代 xml 配置文件,所以还需要使用 新注解 替代配置如下:
  • 非自定义的 Bean 的配置:< bean >
  • 加载 properties 文件的配置:< context:property-placeholder >
  • 组件扫描的配置:< context:component-scan >
  • 引入其他文件:< import >

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFmHvoRf-1645447562217)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\Spring新注解.png)]

6.2.1 @configuration

@configuration              用于指定当前类是另一个 Spring 配置类,当创建容器时会从该类上加载注解
//    示例:
@Configuration //  标志该类是 Spring 的核心配置类
public class SpringConfiguration {}

6.2.2 @ComponentScan

@ComponentScan          用于指定 Spring 在初始化容器时要扫描的包。作用和在 Spring 的 xml 配置文件中                        的<context:component-scan base-package="com.ithiema">一样
//    示例:
@Configuration //  标志该类是 Spring 的核心配置类
//  <context:component-scan base-package="com.itheima"/>
@ComponentScan("com.itheima")    //
public class SpringConfiguration {}

6.2.3 @PropertySource

@PropertySource             用于加载 *.properties 文件中的配置
//    示例:
@Configuration //  标志该类是 Spring 的核心配置类
//  <context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties") //
public class SpringConfiguration {}

6.2.4 @Bean

@Bean                       使用在方法上,标注将该方法的返回值存储到 Spring 容器中
//  示例:
@Bean("springDataSource")// Spring 会将当前方法的返回值以指定名称存储到 Spring 容器中
public DataSource getDataSource() throws Exception {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driver);dataSource.setJdbcUrl(url);dataSource.setUser(username);dataSource.setPassword(password);return dataSource;
}

6.2.5 @Import

@Import                     用于导入其他配置类
//    示例:
@Configuration //  标志该类是 Spring 的核心配置类
//  <import resource=""/>
@Import({DataSourceConfigura.class,XXX.class}) //
public class SpringConfiguration {}

6.3 Spring 全注解示例

public interface UserDao {public void save();}
@Repository("userDao")
public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("save is running from Dao");}
}
public interface UserService {public void save();}
@Service("userService")
public class UserServiceImpl implements UserService {@Autowired
//      @Autowired
//      @Qualifier("userDao")
//    @Resource(name = "userDao")private UserDao userDao;@Overridepublic void save() {userDao.save();}
}

6.3.1 XML 配置测试

  • ClassPathXmlApplicationContext
public class XmlTest {@Testpublic void UserServiceTest(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = applicationContext.getBean(UserService.class);userService.save();}
}

6.3.2 Spring 全注解测试

  • AnnotationConfigApplicationContext
@Configuration
@ComponentScan("com.itheima")
public class SpringConfiguration {}
public class SpringTest {@Testpublic void UserServiceTest(){ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);UserService userService = app.getBean(UserService.class);userService.save();}
}

07. Spring 集成 Junit

7.1. 原始 Junit 测试 Spring 的问题

在测试类中,每个测试方法都有以下两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

7.2 上述问题解决思路

  • 让 Spring Junit 负责创建 Spring 容器,但是需要将配置文件的名称告诉它
  • 将需要进行测试 Bean 直接在测试类中进行注入

7.3 Spring 集成 Junit 步骤

1.   导入 Spring 集成 Junit 的坐标
2.  使用 @Runwith 注解替代原来的运行期
3.  使用 @ContextConfiguration 指定配置文件或配置类
4.  使用 @Autowired 注入需要测试的对象
5.  创建测试方法进行测试
  • 导入 Spring 集成 Junit 的坐标
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.9.RELEASE</version>
</dependency>
  • 使用 @Runwith 注解替代原来的运行期
  • 使用 @ContextConfiguration 指定配置文件或配置类
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(value = "classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {}
  • 使用 @Autowired 注入需要测试的对象
@Autowired
private DataSource dataSource;
@Autowired
private UserService userService;
  • 创建测试方法进行测试
@Test
public void getDataSource() throws Exception {Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}
@Test
public void getUserServiceTest(){userService.save();
}

7.4 Spring 全注解 集成 Junit 测试

public interface UserDao {public void save();}
@Repository("userDao")
public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("save is running from Dao");}
}
public interface UserService {public void save();}
@Service("userService")
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic void save() {userDao.save();}
}
@Configuration
@ComponentScan("com.itheima")
public class SpringConfiguration {}
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.9.RELEASE</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(value = "classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {@Autowiredprivate UserService userService;@Testpublic void getUserServiceTest(){ userService.save(); }
}

08. Spring 集成 Web 环境

8.1 ApplicationContext 应用上下文获取方式

  • 应用上下文对象是通过 new ClasspathXmlApplicationContext(“Spring 配置文件”) 方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(“Spring 配置文件”) ,这样的弊端是配置文件加载多次吗,应用上下文对象创建多次。
  • 在 Web 项目中,可以使用 ServletContextListener 监听 Web 应用的启动,我们可以在 Web 应用启动时,就加载 Spring 的配置文件,创建应用上下文对象 ApplicationContext ,再将其存储到最大的域 servletContext 域中,这样就可以在任意位置从域中获得应用上下文 ApplicationContext 对象了。

8.1.1 优化前

public class ContextLoaderListener implements ServletContextListener {public void contextInitialized(ServletContextEvent servletContextEvent){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");ServletContext servletContext = servletContextEvent.getServletContext();servletContext.setAttribute("applicationContext",applicationContext);System.out.println("ServletContextListener 初始化 完毕");}
}
<listener><listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
</listener>
public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext servletContext = req.getServletContext();ApplicationContext applicationContext = (ApplicationContext) servletContext.getAttribute("applicationContext");UserService userService = applicationContext.getBean(UserService.class);userService.save();}
}

8.1.2 优化后

public class WebApplicationContextUtils {public static ApplicationContext getAttributeApplicationContext(ServletContext servletContext){return (ApplicationContext) servletContext.getAttribute("applicationContext");}public static void setAttributeApplicationContext(ServletContext servletContext,ApplicationContext applicationContext){servletContext.setAttribute("applicationContext",applicationContext);}
}
public class ContextLoaderListener implements ServletContextListener {public void contextInitialized(ServletContextEvent servletContextEvent){ServletContext servletContext = servletContextEvent.getServletContext();String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");ApplicationContext applicationContext = new ClassPathXmlApplicationContext(contextConfigLocation); WebApplicationContextUtils.setAttributeApplicationContext(servletContext,applicationContext);System.out.println("ServletContextListener 初始化 完毕");}
}
<listener><listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
</listener>
public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ApplicationContext applicationContext = WebApplicationContextUtils.getAttributeApplicationContext(req.getServletContext());UserService userService = applicationContext.getBean(UserService.class);userService.save();}
}

8.2 Spring 提供获取应用上下文的工具

  • 上面的分析不用手动实现,Spring 提供了一个监听器 ContextLoaderListener 就是对上述功能的封装,该监听器内部加载 Spring 配置文件,创建应用上下文对象,并存储到 ServletContext 域中,提供了一个客户端工具 WebApplicationContextUtils 供使用者获得应用上下文对象。
  • 所以我们需要做的只有两件事:
    1. 在 web.xml 中配置 ContextLoaderListener 监听器(导入 spring-web 坐标)
    2. 使用 WebApplicationContextUtils 获得应用上下文对象 ApplicationContext

8.3 导入 Spring 集成 Web 的坐标

<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.0.5.RELEASE</version>
</dependency>

8.4 配置 ContextLoaderListener 监听器

<!--  全局参数 -->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--  Spring 的监听器 -->
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

8.5 通过工具获得应用上下文对象

ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(req.getServletContext());
UserService userService = applicationContext.getBean(UserService.class);
userService.save();

8.6 知识要点

  • Spring 集成 Web 环境步骤

    1. 配置 ContextLoaderListener 监听器
    2. 使用 WebApplicationContextUtils 获得应用上下文

09.Spring MVC 简介

Spring MVC 的简介

9.1 Spring MVC 概述

  • Spring MVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
  • Spring MVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring 3.0 的发布,全面超越 Struts 2,成为最优秀的 MVC 框架。他通过一套完备的注解,让一个简单的 Java 类成为处理请求的控制器,而无需实现任何借口。同时它还支持 RESTful 编程风格的请求。

9.2 Spring MVC 雏形

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qGL82uAg-1645447562221)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\Spring MVC 雏形.png)]

9.3 Spring MVC 流程图示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmxZjuAu-1645447562225)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\Spring MVC 流程图示.png)]

9.4 Spring MVC 开发步骤

需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。开发步骤:
1.导入 Spring MVC 相关坐标
2.配置 Spring MVC 核心控制器 DispathcerServlet
3.创建 Controller 类和视图页面
4.使用注解配置 Controller 类中业务方法的映射地址
5.配置 Spring MVC 核心文件 Spring-MVC.xml
6.客户端发起请求测试
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.9.RELEASE</version>
</dependency>
<servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
@Controller
public class UserController {@RequestMapping("/quick")public String save(){System.out.println("save is running from Controller");return "success.jsp";}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Success</title>
</head>
<body>
<h2>Success</h2>
</body>
</html>
<servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
http://localhost:8080/itheima_spring_mvc_war_exploded/quick

10. Spring MVC 的组件解析

Spring MVC 的组件解析

10.1 Spring MVC 的执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HM0yuzDM-1645447562228)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\image-20211205182752999.png)]

01. 用户发送请求至前端控制器 DispacherServlet。
02. DispacherServlet(解释器 Servlet)收到请求调用 HandlerMapping 处理器映射器。
03. 处理器映射器找到具体的处理器(可以根据 XML 配置、注释进行查找),生成处理器对象及处理器拦截器(如果有则一并生成)一并返回给 DispacherServlet。
04. DispacherServlet 调用 HandlerAdapter 处理器适配器。
05. HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
06. Controller 执行完成返回 ModelAndView。
07. HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
08. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
09. ViewReslover 解析后返回具体 View。
10. DispatcherServlet 根据 View 进行渲染视图(即 将模型数据填充至视图中)。DispatherServlet 响应用户。
dispacher    解释器、
Servlet     控制层、方法、基础、加载、理解
Handler     处理者、n.驯兽员、搬运工、操作者、组织者、顾问
mapping     映射
adapter     适配器

10.2 Spring MVC 快速入门

10.2.1 MVC 命名空间引入

命名空间:
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址:
http://www.springframework.org.schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org.schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd

10.2.2 组件扫描

Spring MVC 基于 Spring 容器,所以在进行 Spring MVC 操作时,需要将 Controller 存储到 Spring 容器中,如果使用 @Controller 注解标注的话,就需要用
<context:component-scan base-package="com.itheima.controller"/> 或
<context:component-scan base-package="com.itheima"><!-- 表示只扫描 com.itheima 包下的 Controller 注解 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/><!-- 表示不扫描 com.itheima 包下的 Controller 注解 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
进行组件扫描

10.3 Spring MVC 注解解析

10.3.1 @RequestMapping

作用:用于建立请求 URL 和处理请求方法之间的对应关系

位置;

  • 类上:请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
  • 方法上:请求 URL 的第二级访问目录,与类上的使用 @RequestMapping 标注的一级目录一起组成访问虚拟路径

属性:

  • value:用于指定请求的 URL。它和 path 属性的作用是一样的
  • method:用于指定请求的方式。method 的值是枚举方式的
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和配置的一模一样

例如:

  • method = RequestMethod.GET,表示当前请求方式必须是 GET 请求
  • method = RequestMethod.POST,表示当前请求方式必须是 POST 请求
  • params = {“userName”},表示请求参数必须有 userName。如:http://localhost:8080/user/quick?userName=xxx
  • params = {“money!100”},表示请求参数中 money 不能是 100

10.4 SpringMVC 的 XML 配置解析

10.4.1 视图解析器

  • Spring MVC 有默认组件配置,默认组件都是 DispatcherServlet.properties 配置文件中配置的,该配置文件地址 org.springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver = org.springframework.web.servlet.view.InternalResourceViewResolver

翻看该解析器源码,可以看到解析器的默认设置,如下:

REDIRECT_URL_PREFIX = "redirect:" --重定向前缀
FORWARD_URL_PREFIX  = "forword:" --转发前缀(默认)
prefix = ""; --视图名称前缀
suffix = ""; --视图名称前缀

10.5 Spring MVC 相关组件及注解配置

10.5.1 Spring MVC 的相关组件

  • 前端控制器:DispatcherServlet (负责调用一些其他功能组件)
  • 处理器映射器:HandlerMapping (负责进行地址解析并返回对应的执行链)
  • 处理器适配器:HandlerAdapter (负责 被前控制器调用,并执行对应的处理器,执行对应的功能)
  • 处理器:Handler (对特有功能封装的处理器,返回 相对应的 ModelAndView)
  • 视图解析器:ViewResolver (根据返回的 ModelAndView 解析出 View)
  • 视图:View (根据 返回的 View 进行 视图展示)

10.5.2 Spring MVC 的注解和配置

  • 请求映射注解:@RequestMapping (负责进行 虚拟地址 映射)
  • 视图解析器配置:
REDIRECT_URL_PREFIX = "redirect:"
FORWORD_URL_PREFIX  = "forword:"
prefix = "";
suffix = "";

11.Spring MVC 的数据响应

Spring MVC 的数据响应

11.1 Spring MVC 的数据响应方式

11.1.1 页面跳转

11.1.1.1 直接返回字符串形式
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。
@RequestMapping("/quick")
public String quickMethod(){return "index";
}
<bean id="viewResource" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"></property><property name="suffix" value=".jsp"></property>
</bean>
  • 组装后的转发地:http://localhost:8080/WEB-INF/views/index.jsp
返回带有前缀的字符串:

转发:forword:/WEB-INF/views/index.jsp

重定向:redirect:/index.jsp

11.1.1.2 通过 ModelAndView 对象返回

11.1.2 回写数据

11.1.2.1 直接返回字符串形式
Web 基础阶段,客户端访问服务器,如果想直接回写字符串作为响应体返回的话,只需要使用 response.getWriter().print("hello world") 即可,那么在 Controller 中想直接回写字符串该怎么样呢?
1. 通过 Spring MVC 框架注入的 response 对象,使用 response.getWriter().print("hello world")回写数据,此时不需要视图跳转,业务方法返回值为 void。
@RequestMapping("/quick4")
public void quickMethod4(HttpServletResponse response) throws IOException{response.getWriter().print("hello world");
}
2. 将需要回写的字符串直接返回,但此时需要通过 @ResponseBody 注解告知 SpringMVC 框架,方法返回的字符串不是跳转是直接在 HTTP 响应体中返回。
@ResponseMapping("/quick5")
@ResponseBody  // 告知 SpringMVC 框架不进行试图跳转,直接进行数据响应
public String quickMethod5() throws IOException {return "Hello SpringMVC !!!";
}
11.1.2.2 返回对象或集合
  • 在方法上添加 @ResponseBody 及配置 jackson 就可以返回 json 格式的字符串。
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.9.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.8</version></dependency>
<!--https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations-->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.9.8</version>
</dependency>
<!--    配置处理器映射器    -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">               </bean></list></property>
</bean>
  • 但是这样配置比较麻烦,配置的代码比较多。因此,我们可以使用 MVC 的注解驱动替代上述配置。
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
<!--    MVC 的 注解驱动      -->
<mvc:annotation-driven/>
  • 在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器 称为 SpringMVC 的三大组件。
使用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器),可用在 Spring-mvc.xml 配置文件中使用 <mvc:annotation-driven> 替代注解处理器和适配器的配置。
同时使用 <mvc:annotation-driven> 默认底层就会集成 jackson 进行对象或集合的 json 格式字符串的转换。

11.2 Spring MVC 获得请求数据

11.2.1 获得请求参数

  • 客户端请求参数的格式是:name=value&age=value……
  • 服务器端要获得请求的参数,有时还需要进行数据的封装,Spring MVC 可以接受如下类型的参数:
    • 基本类型参数
    • POJO 类型参数
    • 数组类型参数
    • 集合类型参数

11.2.2 获得基本类型参数

Controller 中的业务方法的参数名称要与请求参数的 name 一致,参数值会自动映射匹配。
http://localhost:8080/itheima_spring_mvc_war_exploded/quick11?username=zhaochen&age=25
http://localhost:8080/itheima_spring_mvc_war_exploded/quick11?age=25&username=zhangsan
@RequestMapping(value = "/quick11")
@ResponseBody
public void save11(String username,int age) {System.out.println(username);System.out.println(age);
}

11.2.3 获得 POJO 类型参数

Controller 中的业务方法的 POJO(Plain Ordinary Java Object,简单的Java对象,可以理解为简单的实体类,作用是方便程序员使用数据库中的数据表) 参数的属性名与请求参数的 name 一致,参数值会自动映射匹配。
http://localhost:8080/itheima_spring_mvc_war_exploded/quick12?username=zhaochen&age=25
public class User{private String username;private int age;getter/setter……
}
@RequestMapping("quick12")
@ResponseBody
public void quickMethod12(User user){System.out.println(user);
}

11.2.4 获得数组类型参数

Controller 中的业务方法数组名称与请求参数的 name 一致,参数值会自动映射匹配。
http://localhost:8080/itheima_spring_mvc_war_exploded/quick13?str=111&str=222&str=333
@RequestMapping("quick13")
@ResponseBody
public void quickMethod13(String[] strs){System.out.println(Arrays.asList(strs));
}

11.2.5 获得集合类型参数

当使用 ajax 提交时,可以指定 contextType 为 json 形式,那么在方法参数位置使用 @RequestBody 可以直接接收集合数据而无需使用 POJO 进行包装。
public class VO {private List<User> userList;public List<User> getUserList() {return userList;}public void setUserList(List<User> userList) {this.userList = userList;}@Overridepublic String toString() {return "VO{ userList = " + userList +'}';}
}
@RequestMapping("quick14")
@RequestBody
public VO quickMethod14(VO vo){System.out.println(vo);return vo;
}
@RequestMapping("quick15")
@RequestBody
public void quickMethod15(@RequestBody List<User> userList){System.out.println(userList);
}

11.2.6 请求数据乱码问题

当 POST 请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤器。
<filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
</filter>
<filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

11.2.7 参数绑定注解 @requestParam

当请求的参数名与 Controller 的业务方法参数名称不一致时,就需要通过 @RequestParam 注解显示的绑定。
<form action="${pageContext.request.contextPath}/quick16" method="post"><input type="text" name="name"><br/><input type="submit" value="提交"><br/>
</form>
@RequestMapping("/quick16")
@ResponseBody
public void quickMethod16(@RequestParam("name") String username){System.out.println(username);
}
注解 @RequestParam 还有如下参数可以使用:
  • value:与请求参数名称
  • required:此在指定的请求参数是否必须包括,默认是 true,提交时如果没有此参数则报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
@RequestMapping("/quick16")
@RequestBody
public void quickMethod16(@RequestParam(value = "name",required = false,defaultValue = "itcast") String username){System.out.println(username);
}

11.2.8 获得 Restful 风格的参数

  • Restful 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
  • Restful 风格的请求是使用 “url + 请求方式” 表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:
    • Get: 用于获取资源
    • POST: 用户新建资源
    • PUT: 用于更新资源
    • DELETE:用于删除资源

例如:

  • /user/1 GET: 得到 id = 1 的 user
  • /user/1 DELETE: 删除 id = 1 的 user
  • /user/1 PUT: 更新 id = 1 的 user
  • /user POST: 新增 user
上述 URL 地址 /user/1 中的 1 就是要获得的请求参数,在 SpringMVC 中可以使用占位符进行参数绑定。地址 /user/1 可以写成 /user/{id},占位符 {id} 对应的就是 1 的值。在业务方法中我们可以使用 @PathVariable 注解进行占位符的匹配获取工作。
http://localhost:8080/itheima_spring_mvc_war_exploded/quick17/zhangsan
@RequestMapping("quick17/{name}")
@ResponseBody
public void quickMethod17(@PathVariable(value="name",required=true) String name){System.out.println(name);
}

11.2.9 自定义类型转换

Spring MVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符转换成 int 型进行参数设置。
但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器。
例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
1. 定义转换器类 实现 Converter 接口
2. 在配置文件中声明转换器
3. 在 <annotation-driven> 中引用转换器
public class DateConverter implements Converter<String,Date> {@Overridepublic Date convert(String s) {//        将日期字符串转换成日期对象 返回SimpleDateFormat formatSql = new SimpleDateFormat("yyyy/MM/dd");SimpleDateFormat formatUtil = new SimpleDateFormat("yyyy-MM-dd");Date date = null;try {date = formatSql.parse(s);} catch (ParseException e) {try {date = formatUtil.parse(s);} catch (ParseException parseException) {parseException.printStackTrace();}}return date;}
}
<!--    MVC 的 注解驱动      -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<!--    声明转换器       -->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><list><bean class="com.itheima.converter.DateConverter"></bean></list></property>
</bean>

12. Spring MVC 的请求

Spring MVC 的请求

13. JDBC Template 基本使用

JDBC Template 基本使用

13.6 知识要点

1.   导入 spring-jdbc 和 spring-tx 坐标
2.  创建 数据库表 和 实体
3.  创建 JDBC Template 对象JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);
4.  执行数据库操作4.1  更新操作jdbcTemplate.update(sql,params)4.2 查询操作jdbcTemplate.query(sql,mapper,params);jdbcTeaplate.queryForObject(sql,mapper,params);

14. Spring 练习-环境搭建

Spring 练习-环境搭建

14.1 Spring 环境搭建步骤

1.   创建工程(Project & Module)
2.  导入静态页面(见资料 jsp 页面)
3.  导入需要坐标(见资料中的 pom.xml)
4.  创建包结构(controller、service、dao、domain、utils)
5.  导入数据库脚本(见资料 test.sql)并配置 web.xml
6.  创建 POJO 类(见资料 User.java 和 Role.java)
7.  创建配置文件(applicationContext.xml、spring-mvc.xml、jdbc.properties、log4j.properties)

15. Spring MVC 拦截器

Spring MVC 拦截器

15.1 拦截器(interceptor)的作用

  • Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理
  • 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(interceptor Chain)。
在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是 AOP 思想的具体实现。

15.2 拦截器和过滤器区别

 区别              过滤器(Filter)               拦截器(Interceptor)适用范围      是 Servlet 规范中的一部分,任何  是 Spring MVC框架自己的,只有使用Java Web 工程都可以使用            了 Spring MVC 框架的工程才能用拦截范围     在 url-pattern 中配置了 /* 之后    在 <mvc:mapping path=""/> 中可以对所有要访问的资源拦截           配置了 /** 之后,也可以对所有资源进行拦截,但是可以通过<mvc:exclude-mapping path=""/>标签排除不需要拦截的资源

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MwGkWFlq-1645447562233)(E:\01.Java\02. Java EE\00.笔记\黑马程序员最全SSM框架教程 Spring+SpringMVC+MyBatis全套教程.assets\拦截器和过滤器的区别.png)]

16. Spring MVC 异常处理

Spring MVC 异常处理

17. AOP 简介

AOP 简介

18. XML 方式实现 AOP

XML 方式 实现 AOP

19. 注解方式 实现AOP

注解方式 实现AOP

20. Spring 的事务控制

Spring 的事务控制

21. Mybatis 使用

Mybatis 使用

22. Mybatis 的 DAO 层实现

Mybatis 的 DAO 层实现

23. Mybatis 映射文件深入

Mybatis 映射文件深入

24. Mybatis 核心配置文件深入

Mybatis 核心配置文件深入

25. Mybatis 的多表操作

Mybatis 的多表操作

26. Mybatis 注解开发

Mybatis 注解开发

27. SSM 整合

SSM 整合

998.疑难解惑

1.EL 不生效

  • 在头文件件中 加入 isELIgnored=“false”
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
  • 在 web.xml 中配置 所有 isELIgnored=“false”
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"><jsp-config><jsp-property-group><url-pattern>*.jsp</url-pattern><el-ignored>false</el-ignored></jsp-property-group></jsp-config>

2.控制台乱码

-Dfile.encoding=utf-8

999.学习进度

CTRL + 左击 —> 继续学习

SSM 框架学习(黑马程序员)相关推荐

  1. JavaEE(SSM框架,黑马程序员) P163~P178

    一.Mybatis的Dao层实现 1.1 传统开发方式 1.2 代理开发方式 1.Mapper.xml文件中的namespace与Mapper接口的全限定名相同 2.Mapper接口方法名和Mappe ...

  2. Flask学习 黑马程序员-6节课入门Flask框架web开发视频(中途撤退,寻找py3教程)

    文章目录 postman工具 get和post 如何给路由传参 解析 @app.route('')这个叫视图函数 Jinja2模板引擎 动态传参 注释.变量代码块以及控制代码块的使用 注释:ctrl+ ...

  3. Java语法快速学习-黑马程序员(个人整理版本)

    Java入门基础视频教程,java零基础自学首选黑马程序员Java入门教程(含Java项目和Java真题)_哔哩哔哩_bilibili为了帮助广大对Java有兴趣和立志进入本行业的零基础学员,本套课程 ...

  4. Git基础学习(黑马程序员笔记)

    Git介绍 Git是目前世界上最先进的分布式版本控制系统 Git安装 官网 Git与Github 两者区别 Git是一个分布式版本库控制系统,简单的说就是一个软件,用于记录一个或若干文件内容变化,以便 ...

  5. Java入门-学习黑马程序员Java基础视频教程(到P92)

    目录 P0:写在前面的小知识 P3:Java环境搭建: JDK安装.常用命令 P4:入门程序HelloWorld P7:补充知识:JDK组成.跨平台原理 P8:补充知识:JDK安装后Path和JAVA ...

  6. JavaSE基础加强-学习黑马程序员Java基础视频教程(P93开始)

    目录 P0:写在前面的小知识 P93:下阶段:JavaSE基础加强简介 P95:static:修饰成员变量.内存机制 P96:static:修饰成员方法.内存机制 P97:static:访问的注意事项 ...

  7. jQuery学习笔记【黑马程序员】

    文章目录 前言 JQury学习 1.引入JQury 2.jqury简介 3.jQuery入口函数 4.dom对象与jQuery对象 4.1.dom对象 4.2.jQuery对象 4.3.jQuery对 ...

  8. 黑马程序员Javaweb学习笔记01

    该博客主要记录在学习黑马程序员Javaweb过程的一些笔记,方便复习以及加强记忆 文章目录 一 . BS架构,HTTP协议 http请求数据格式和相应数据格式 二 . web服务器 2.1 tomca ...

  9. 黑马程序员Javaweb学习笔记02【request和response】

    该博客主要记录在学习黑马程序员Javaweb过程的一些笔记,方便复习以及加强记忆

最新文章

  1. 理解Linux系统负荷
  2. 人工智能创业指南:AI 产品未来的发展模式及策略
  3. shell中的for循环语句
  4. 实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法
  5. 【计算机网络】网络安全 : 公钥密码体质 ( 公钥 - 加密密钥 | 私钥 - 解密密钥 | 与对称密钥体质对比 | 特点 | 数字签名引入 )
  6. 计算机丢失cv210.dll,cv210.dll文件免费版
  7. 《零基础》MySQL DELETE 语句(十五)
  8. 2015二级java真题及答案_2015计算机二级《JAVA》考前模拟操作和应用题及答案
  9. 为什么出现股市二八现象?
  10. jQuery 学习笔记 选择元素
  11. 一周信创舆情观察(11.16~11.22)
  12. ospf路由 华3_华三模拟器ospf的简单配置
  13. 22个月无休,华为36岁工程师在肯尼亚过劳猝死!
  14. 企业微信社群运营必建的4个内容体系
  15. Unity-拓展篇-pr序列帧转透明视频
  16. flutter 微信聊天输入框
  17. 简单带验证码的登陆网站破解
  18. 如何使用统计显着性检验来解释机器学习结果
  19. 《我从起点开始奔跑》
  20. ubuntu vscode 换字体

热门文章

  1. 解析高程数据asc文件
  2. 00023.11 TCP协议编程:群聊(TCP通信原理,多线程、线程阻塞)
  3. android签名常用命令
  4. 从 varchar 数据类型到 datetime 数据类型的转换产生一个超出范围的值报错处理?
  5. NLP接下来黄金十年-----周明等谈值得关注的NLP技术 | 技术专栏...
  6. 图像特征提取-Hough变换
  7. 定时任务Cron表达式学习
  8. 计算机病毒引起的死机现象属于硬件故障吗,区别计算机病毒与故障
  9. Structs2学习(二)
  10. 程序员市场饱和了吗?未来5年前景如何?工资会下降么?