前言:在 Java 开发领域,Spring、SpringMVC 和 MyBatis 这三个框架堪称得力 “助手”,它们相互协作,能助力我们高效构建各类 Java Web 应用。
目录
(三)Spring 的另一个重要特性 —— 面向切面编程(AOP)
一、Spring 框架
Java 开发的基石
(一)Spring 是什么?
Spring 是一个轻量级的 Java 开发框架,旨在简化企业级应用开发。它通过提供一系列的基础设施支持,让开发者可以更加专注于业务逻辑的实现,而无需过多关注底层的技术细节。就像盖房子需要打地基一样,Spring 为我们的 Java 应用构建了一个坚实的基础。
(二)Spring核心 的特性 —— 依赖注入(DI)
-
DI 的概念 :DI 是 Spring 的核心特性之一,它指的是通过配置文件或注解,将对象之间的依赖关系交给 Spring 容器来管理,而不是在代码中直接创建对象实例。这种方式可以大大降低代码的耦合度,提高代码的可维护性和可测试性。
-
DI 的示例 :
假设我们有一个汽车类(Car)和一个引擎类(Engine),汽车类依赖于引擎类。在传统的开发方式中,我们可能需要在汽车类中直接创建引擎对象。但使用 Spring 的 DI 特性后,我们可以通过配置文件或注解的方式,将引擎对象注入到汽车类中。
// 引擎类
public class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
// Getter 和 Setter 方法
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
// 汽车类
public class Car {
private Engine engine;
// 通过Setter方法注入引擎对象
public void setEngine(Engine engine) {
this.engine = engine;
}
public void displayEngineType() {
System.out.println("引擎类型:" + engine.getType());
}
}
在 Spring 的配置文件中,我们可以这样配置:
<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="engine" class="com.demo.Engine">
<constructor-arg value="V6"/>
</bean>
<!-- 定义汽车Bean,并通过Setter方法注入引擎依赖 -->
<bean id="car" class="com.demo.Car">
<property name="engine" ref="engine"/>
</bean>
</beans>
然后在客户端代码中,我们可以通过 Spring 容器获取汽车对象,并调用其方法:
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取汽车Bean
Car car = (Car) context.getBean("car");
// 调用汽车对象的方法
car.displayEngineType();
}
}
在这个例子中,我们没有在汽车类中直接创建引擎对象,而是通过 Spring 容器将引擎对象注入到汽车类中。这样,汽车类和引擎类之间的耦合度就降低了,同时也方便了对代码的维护和测试。
(三)Spring 的另一个重要特性 —— 面向切面编程(AOP)
-
AOP 的概念 :AOP 是一种编程思想,它允许我们将横切关注点(如日志记录、性能监控、事务管理等)与业务逻辑分离。横切关注点通常是指那些与业务逻辑无关,但在多个模块中都需要使用的功能。通过 AOP,我们可以将这些横切关注点封装成可重用的组件,从而提高代码的模块化程度和可维护性。
-
AOP 的应用场景 :例如,在一个电商系统中,我们需要在多个地方记录日志,如用户登录、下单、支付等操作。如果我们在每个业务方法中都手动编写日志记录代码,那么当需要修改日志记录逻辑时,就需要修改多个地方的代码,这显然非常繁琐。而使用 AOP 后,我们可以将日志记录功能抽取成一个切面,在需要记录日志的地方织入这个切面,这样就可以集中管理日志记录逻辑,方便后续的维护和扩展。
-
AOP 的实现方式 :在 Spring 中,我们可以通过注解或配置文件的方式来定义切面、通知和切入点。其中,切面用于定义横切关注点的逻辑,通知用于指定切面在何时执行(如方法执行前、方法执行后、方法抛出异常时等),切入点用于指定切面作用于哪些方法。
例如,我们定义一个日志切面:
// 日志切面
@Aspect
@Component
public class LoggingAspect {
// 定义切入点,指定切面作用于哪些方法
@Pointcut("execution(* com.demo.service.*.*(..))")
public void logPointcut() {}
// 在方法执行前记录日志
@Before("logPointcut()")
public void beforeLogging(JoinPoint joinPoint) {
System.out.println("方法执行前:" + joinPoint.getSignature().getName());
}
// 在方法执行后记录日志
@After("logPointcut()")
public void afterLogging(JoinPoint joinPoint) {
System.out.println("方法执行后:" + joinPoint.getSignature().getName());
}
}
在这个例子中,我们通过 @Aspect 注解定义了一个日志切面,并通过 @Pointcut 注解指定了切入点,表示该切面作用于 com.example.service 包下所有类的所有方法。然后,我们通过 @Before 和 @After 注解分别定义了方法执行前和方法执行后的通知,用于记录日志信息。
二、SpringMVC 框架
构建 Web 应用的利器
(一)SpringMVC 是什么?
SpringMVC 是 Spring 框架的一个模块,专门用于构建 Web 应用。它遵循 MVC(Model-View-Controller)设计模式,将 Web 应用分为模型(Model)、视图(View)和控制器(Controller)三个部分,实现了职责分离,提高了代码的可维护性和可扩展性。
-
Model(模型) :模型层用于封装业务数据和业务逻辑,通常是一些 Java Bean 类,这些类包含了数据的属性和方法。
-
View(视图) :视图层用于展示数据,通常是 JSP、HTML、Thymeleaf 等页面文件,它们负责将模型层中的数据以友好的方式呈现给用户。
-
Controller(控制器) :控制器层用于处理用户的请求,调用模型层的方法来执行业务逻辑,并根据执行结果返回相应的视图。
(二)SpringMVC 的工作流程
-
用户发送请求 :用户通过浏览器向服务器发送一个 HTTP 请求。
-
请求到达前端控制器 :前端控制器(DispatcherServlet)是 SpringMVC 的核心组件,它负责接收所有的请求,并对请求进行分发处理。
-
处理器映射器查找处理器 :前端控制器会根据请求的 URL 等信息,调用处理器映射器(HandlerMapping)来查找对应的处理器(Controller)。
-
处理器适配器执行处理器 :找到处理器后,前端控制器会调用处理器适配器(HandlerAdapter)来执行处理器的方法。
-
处理器处理请求并返回模型和视图 :处理器执行具体的业务逻辑,处理完请求后,返回一个包含模型数据和视图名称的 ModelAndView 对象给处理器适配器。
-
处理器适配器返回模型和视图给前端控制器 :处理器适配器将处理器返回的 ModelAndView 对象转发给前端控制器。
-
前端控制器解析视图 :前端控制器会调用视图解析器(ViewResolver)来解析视图名称,将视图名称解析为具体的视图对象。
-
渲染视图并返回响应 :前端控制器将模型数据填充到视图中,生成最终的 HTML 页面或其他形式的响应内容,然后将其返回给用户。
(三)SpringMVC 的注解驱动开发
-
@Controller 注解 :用于标注一个类作为控制器,告诉 Spring 容器这个类中的方法可以处理用户请求。
-
@RequestMapping 注解 :用于映射 HTTP 请求到控制器的具体方法上。它可以作用于控制器类或方法上,用于指定请求的 URL 路径、HTTP 方法(如 GET、POST)等条件。
-
@RequestParam 注解 :用于将请求参数绑定到控制器方法的参数上,可以指定请求参数的名称、是否必须等属性。
-
@ModelAttribute 注解 :用于将请求参数绑定到模型对象上,可以将多个请求参数封装到一个对象中,方便在视图中使用。
-
@ResponseBody 注解 :用于将控制器方法的返回值直接作为 HTTP 响应体返回给客户端,而不是经过视图解析器解析为视图。
例如,我们创建一个简单的控制器来处理用户的请求:
// 控制器类
@Controller
@RequestMapping("/user")
public class UserController {
// 处理 GET 请求:获取用户信息
@RequestMapping(value = "/getInfo", method = RequestMethod.GET)
public String getUserInfo(Model model) {
// 创建用户对象
User user = new User();
user.setName("张三");
user.setAge(25);
// 将用户对象添加到模型中
model.addAttribute("user", user);
// 返回视图名称
return "userInfo";
}
// 处理 POST 请求:创建用户
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(@ModelAttribute("user") User user) {
// 获取表单提交的用户信息
System.out.println("用户名:" + user.getName());
System.out.println("用户年龄:" + user.getAge());
// 返回视图名称
return "createSuccess";
}
}
在这个例子中,我们定义了一个 UserController 类,并使用 @Controller 注解标注它为控制器。通过 @RequestMapping 注解,我们分别映射了两个方法来处理不同的请求。在 getUserInfo 方法中,我们创建了一个用户对象,并将其添加到模型中,然后返回一个视图名称 “userInfo”。在 createUser 方法中,我们使用 @ModelAttribute 注解将表单提交的用户信息绑定到 User 对象上,然后返回一个视图名称 “createSuccess”。
对应的 userInfo.jsp 视图页面可以这样编写:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户信息</title>
</head>
<body>
<h1>用户信息</h1>
<p>用户名:${user.name}</p>
<p>用户年龄:${user.age}</p>
</body>
</html>
当用户访问 “http://localhost:8080/user/getInfo” 这个 URL 时,将显示用户信息页面,页面中会展示用户对象中的数据。
三、MyBatis 框架
简化数据库操作的持久层框架
(一)MyBatis 是什么?
MyBatis 是一个优秀的基于 Java 的持久层框架,它简化了 JDBC 操作数据库的流程,通过配置映射关系,将 Java 对象和数据库表进行映射,使得数据库操作变得更加简单、灵活和高效。
(二)MyBatis 的工作流程
-
读取配置文件 :通过 MyBatis 提供的 Resources 类读取 MyBatis 配置文件(mybatis-config.xml),获取数据库连接信息、映射文件路径等配置内容。
-
创建 SQLSessionFactory 工厂 :根据配置文件的信息,创建一个 SQLSessionFactory 工厂,该工厂用于生产 SQLSession 对象。
-
获取 SQLSession 对象 :通过 SQLSessionFactory 工厂获取一个 SQLSession 对象,SQLSession 是 MyBatis 的核心接口,用于执行 SQL 语句、管理事务等。
-
执行数据库操作 :通过 SQLSession 对象执行数据库操作,如查询、插入、更新、删除等。执行操作时,可以使用映射文件中定义的 SQL 映射语句,并传入相应的参数。
-
处理结果 :执行查询操作后,MyBatis 会根据映射文件中定义的结果映射,将查询结果封装成 Java 对象返回给调用者。
-
关闭资源 :完成数据库操作后,需要关闭 SQLSession 等资源,以释放数据库连接。
(三)MyBatis 的核心配置文件和映射文件
-
MyBatis 配置文件(mybatis-config.xml) :这是 MyBatis 的全局配置文件,主要配置了数据库连接信息、事务管理器、映射文件路径等基本信息。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境 -->
<environments default="development">
<environment id="development">
<!-- 使用 JDBC事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池配置 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
-
映射文件(UserMapper.xml) :用于定义 SQL 映射语句的文件,每个映射文件对应一个 DAO 接口,文件中包含了该接口中各个方法所对应的 SQL 语句、结果映射等配置。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 查询用户信息 -->
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT id, name, age FROM user WHERE id = #{id}
</select>
<!-- 插入用户信息 -->
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO user(name, age) VALUES(#{name}, #{age})
</insert>
<!-- 更新用户信息 -->
<update id="updateUser" parameterType="com.example.model.User">
UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<!-- 删除用户信息 -->
<delete id="deleteUser" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
(四)MyBatis 的动态 SQL
在实际开发中,SQL 语句往往不是固定的,而是需要根据不同的条件动态生成。MyBatis 提供了动态 SQL 的功能,通过在映射文件中使用 if、choose(when、otherwise)、foreach 等标签,可以根据条件判断或者循环来动态拼接 SQL 语句。
例如,我们定义一个根据条件查询用户的 SQL 映射:
<select id="getUsersByConditions" parameterType="map" resultType="com.example.model.User">
SELECT id, name, age FROM user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
在这个例子中,我们使用了 <if>
四、框架整合
打造完整的应用架构
在实际项目开发中,我们通常需要将 Spring、SpringMVC 和 MyBatis 这三个框架整合在一起,构建一个完整的应用架构,以实现 Web 层、业务逻辑层和持久层的清晰分离和协同工作。
(一)Spring 整合 MyBatis
为了将 Spring 和 MyBatis 整合在一起,我们需要使用一些中间件或连接器来完成整合。整合的关键在于将 MyBatis 的 SQLSessionFactory 工厂交给 Spring 容器来管理,并通过 MapperScannerConfigurer 或 @MapperScan 注解来扫描 Mapper 接口,使得 MyBatis 的数据访问功能能够融入到 Spring 的 IoC 容器中,从而实现依赖注入等 Spring 特性。
-
整合步骤 :
-
添加 Spring 和 MyBatis 整合所需的依赖包,如 spring-jdbc、mybatis-spring 等。
-
在 Spring 的配置文件中,配置数据源(DataSource),可以使用 Spring 提供的 DriverManagerDataSource 或者其他连接池数据源。
-
配置 SQLSessionFactoryBean,将数据源注入到 SQLSessionFactoryBean 中,由它来创建 SQLSessionFactory。
-
使用 MapperScannerConfigurer 或 @MapperScan 注解来扫描 Mapper 接口所在的包,使得 Spring 容器能够识别并管理这些 Mapper 接口。
-
例如,我们在 Spring 的配置文件中这样配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
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">
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 配置 SQLSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 扫描 Mapper 接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.demo.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 扫描 Spring 注解的组件 -->
<context:component-scan base-package="com.demo"/>
</beans>
然后,我们定义一个 Mapper 接口:
// Mapper接口
public interface UserMapper {
User getUserById(int id);
int insertUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
在业务逻辑层,我们可以通过 Spring 的依赖注入获取 UserMapper 接口的实现类,并调用其方法来执行数据库操作:
// 业务逻辑层
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUser(int id) {
return userMapper.getUserById(id);
}
public void createUser(User user) {
userMapper.insertUser(user);
}
public void updateUser(User user) {
userMapper.updateUser(user);
}
public void deleteUser(int id) {
userMapper.deleteUser(id);
}
}
(二)Spring 整合 SpringMVC
Spring 整合 SpringMVC 是为了将 Spring 的 IoC 和 AOP 等特性应用到 Web 层的开发中。整合后,SpringMVC 的控制器、视图解析器等组件都可以由 Spring 容器来管理,实现依赖注入,使得 Web 层的代码更加简洁、模块化。
-
整合步骤 :
-
添加 SpringMVC 相关的依赖包,如 spring-webmvc 等。
-
在 web.xml 文件中,配置 Spring 的监听器(ContextLoaderListener),用于加载 Spring 的配置文件并初始化 Spring 容器。
-
在 web.xml 文件中,配置 SpringMVC 的前端控制器(DispatcherServlet),并指定其配置文件的位置(可选)。
-
在 Spring 的配置文件中,通过 context:component-scan 标签扫描 SpringMVC 控制器所在的包。
-
配置视图解析器,如 InternalResourceViewResolver,用于将视图名称解析为具体的 JSP 页面路径。
-
例如,我们的 web.xml 文件配置如下:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置 Spring 监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置 SpringMVC 前端控制器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
SpringMVC 的配置文件(springmvc-config.xml)可以这样编写:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
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">
<!-- 扫描 SpringMVC 控制器所在的包 -->
<context:component-scan base-package="com.demo.controller"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
在控制器层,我们可以通过依赖注入获取业务逻辑层的组件(如 UserService),并调用其方法来处理业务逻辑:
// 控制器层
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/getInfo", method = RequestMethod.GET)
public String getUserInfo(Model model, int id) {
User user = userService.getUser(id);
model.addAttribute("user", user);
return "userInfo";
}
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(User user) {
userService.createUser(user);
return "createSuccess";
}
}
这样,我们就完成了 Spring 整合 SpringMVC 的开发模式。通过这种整合,我们能够充分利用 Spring 的 IoC 和 AOP 特性,使得 Web 层和业务逻辑层的代码更加清晰、解耦,方便后续的维护和扩展。
总结
通过对 Spring、SpringMVC 和 MyBatis 这三个框架的深入学习,我们掌握了它们的核心概念、特性和使用方法。从 Spring 的依赖注入和面向切面编程,到 SpringMVC 的 Web 开发功能,再到 MyBatis 的持久层操作,最后通过框架整合实现了完整的应用架构构建。在实际开发中,灵活运用这些框架,能够大大提高开发效率,构建出稳定、高效、可维护的 Java Web 应用。