四、Spring AOP(面向切面编程)- 核心重点
4.1 AOP 解决的问题
传统开发中,日志、事务、权限等通用功能会嵌入业务代码,导致:
- 代码冗余:每个业务方法都要写重复的通用逻辑;
- 维护困难:修改通用逻辑需改动所有业务方法。
示例问题代码:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void addUser(){
// 日志逻辑(冗余)
System.out.println("方法开始时间:"+new Date());
userDao.addUser();
System.out.println("方法结束时间:"+new Date());
}
}
4.2 AOP 核心概念
| 切面(Aspect) | 通用功能模块(如日志、事务) |
| 连接点(JoinPoint) | 业务方法(如 addUser、deleteUser) |
| 切入点(Pointcut) | 要增强的连接点(指定哪些方法需要增强) |
| 通知(Advice) | 增强的具体逻辑(如前置日志、后置日志) |
| 目标对象(Target) | 被增强的业务对象(如 UserServiceImpl) |
| 代理对象(Proxy) | Spring 创建的增强后的对象 |
4.3 AOP 通知类型
| 前置通知(Before) | 目标方法执行前 |
| 后置通知(AfterReturning) | 目标方法正常执行后 |
| 异常通知(AfterThrowing) | 目标方法抛出异常后 |
| 最终通知(After) | 目标方法执行完毕(无论是否异常) |
| 环绕通知(Around) | 目标方法执行前后均可增强(最灵活) |
4.4 基于 XML 的 AOP 配置(实战)
步骤 1:引入 AOP 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
步骤 2:编写切面类(日志功能)
public class LogAspect {
// 前置通知
public void beforeLog(){
System.out.println("方法开始时间:" + new Date());
}
// 后置通知
public void afterLog(){
System.out.println("方法结束时间:" + new Date());
}
}
步骤 3:配置 AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!– 目标对象 –>
<bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.hg.service.UserServiceImpl" autowire="byType"></bean>
<!– 切面类 –>
<bean id="logAspect" class="com.hg.aspect.LogAspect"></bean>
<!– AOP配置 –>
<aop:config>
<!– 切入点:指定增强的方法(execution表达式) –>
<aop:pointcut id="pt" expression="execution(* com.hg.service.*.*(..))"/>
<!– 切面:关联切入点和通知 –>
<aop:aspect ref="logAspect">
<aop:before method="beforeLog" pointcut-ref="pt"/>
<aop:after method="afterLog" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
步骤 4:测试
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ac.getBean("userService", UserService.class);
userService.addUser();
}
}
4.5 基于注解的 AOP 配置
步骤 1:开启 AOP 注解支持
<context:component-scan base-package="com.hg"></context:component-scan>
<!– 开启AOP注解 –>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
步骤 2:注解实现切面
@Component
@Aspect // 标记为切面类
public class LogAspect {
// 切入点表达式
@Pointcut("execution(* com.hg.service.*.*(..))")
public void pt(){}
// 前置通知
@Before("pt()")
public void beforeLog(){
System.out.println("方法开始时间:" + new Date());
}
// 后置通知
@After("pt()")
public void afterLog(){
System.out.println("方法结束时间:" + new Date());
}
}
五、总结
六、Spring 整合 MyBatis
在实际开发中,Spring 的 IOC+AOP 常与 MyBatis 结合使用,Spring 负责对象管理和事务控制,MyBatis 负责持久层操作,二者整合可大幅简化开发,消除 MyBatis 的繁琐配置和对象管理。
6.1 整合核心思路
6.2 环境搭建(Maven)
6.2.1 引入所有依赖
需引入 Spring、MyBatis、MyBatis-Spring 整合包、数据库驱动、连接池、日志等依赖,完整pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.hg</groupId>
<artifactId>Spring_MyBatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!– Spring核心 + AOP + 事务 + JDBC –>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!– MySQL驱动 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!– Druid连接池 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<!– MyBatis核心 –>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!– MyBatis与Spring整合包 –>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!– 日志 –>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.26</version>
</dependency>
<!– 测试 –>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!– 配置资源扫描,让Maven加载Java目录下的XML配置文件 –>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
6.2.2 配置数据库连接信息
在src/main/resources下创建db.properties,解耦数据库配置:
# 数据库驱动
jdbc.driverClass=com.mysql.jdbc.Driver
# 数据库连接地址
jdbc.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8
# 数据库账号
jdbc.username=root
# 数据库密码
jdbc.password=123456
6.2.3 配置 Spring 核心配置文件
在src/main/resources下创建applicationContext.xml,核心配置数据源、SqlSessionFactory、Mapper 扫描,替代 MyBatis 的核心配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!– 1. 加载数据库配置文件 –>
<context:property-placeholder location="classpath:db.properties" />
<!– 2. 配置Druid数据源,由Spring管理 –>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!– 3. 配置SqlSessionFactory,整合MyBatis –>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!– 注入数据源 –>
<property name="dataSource" ref="dataSource"/>
<!– 配置实体类别名,指定包名即可 –>
<property name="typeAliasesPackage" value="com.hg.pojo"/>
</bean>
<!– 4. 配置Mapper扫描器,自动生成Mapper代理对象并交给Spring管理 –>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!– 指定Mapper接口所在包 –>
<property name="basePackage" value="com.hg.mapper"/>
<!– 关联SqlSessionFactory –>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!– 5. 开启Spring注解扫描,管理Service/Controller等 –>
<context:component-scan base-package="com.hg"></context:component-scan>
</beans>
6.3 实战开发(用户增删改查)
6.3.1 创建数据库表
CREATE DATABASE IF NOT EXISTS spring;
USE spring;
CREATE TABLE IF NOT EXISTS t_user (
id INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
name VARCHAR(255) DEFAULT NULL COMMENT '用户名',
money FLOAT DEFAULT NULL COMMENT '余额',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
6.3.2 创建实体类
在com.hg.pojo下创建User类,与数据库表映射:
package com.hg.pojo;
public class User {
private Integer id;
private String name;
private Float money;
// 无参构造
public User() {}
// 有参构造(方便测试)
public User(String name, Float money) {
this.name = name;
this.money = money;
}
// getter/setter方法
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Float getMoney() { return money; }
public void setMoney(Float money) { this.money = money; }
}
6.3.3 创建 Mapper 接口与映射文件
MyBatis 的 Mapper 接口无需实现类,由 Spring+MyBatis 自动生成代理对象。
1、在com.hg.mapper下创建UserMapper接口:
package com.hg.mapper;
import com.hg.pojo.User;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
// 添加用户
void addUser(User user);
// 扣钱
void updateUserOfSub(@Param("source") String source, @Param("money") Float money);
// 加钱
void updateUserOfAdd(@Param("target") String target, @Param("money") Float money);
}
2、在同目录下创建UserMapper.xml映射文件,编写 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">
<!– namespace必须与Mapper接口全类名一致 –>
<mapper namespace="com.hg.mapper.UserMapper">
<!– 添加用户 –>
<insert id="addUser" parameterType="User">
insert into t_user(name,money) values(#{name},#{money})
</insert>
<!– 扣钱 –>
<update id="updateUserOfSub">
update t_user set money=money-#{money} where name=#{source}
</update>
<!– 加钱 –>
<update id="updateUserOfAdd">
update t_user set money=money+#{money} where name=#{target}
</update>
</mapper>
6.3.4 创建 Service 层
在com.hg.service下创建UserService接口和实现类,通过@Autowired注入 Mapper 代理对象:
// 接口
package com.hg.service;
import com.hg.pojo.User;
public interface UserService {
void addUser(User user);
void updateUser(String source, String target, Float money);
}
// 实现类
package com.hg.service.impl;
import com.hg.mapper.UserMapper;
import com.hg.pojo.User;
import com.hg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service // 交给Spring管理
public class UserServiceImpl implements UserService {
// 自动注入Mapper代理对象(Spring已通过扫描器创建)
@Autowired
private UserMapper userMapper;
@Override
public void addUser(User user) {
userMapper.addUser(user);
}
@Override
public void updateUser(String source, String target, Float money) {
// 扣钱
userMapper.updateUserOfSub(source, money);
// 加钱
userMapper.updateUserOfAdd(target, money);
}
}
6.4 测试整合效果
使用 Spring 整合 JUnit 进行测试,无需手动创建 Spring 容器,在com.hg.test下创建ServiceTest:
package com.hg.test;
import com.hg.pojo.User;
import com.hg.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
// 整合Spring和JUnit
@RunWith(SpringJUnit4ClassRunner.class)
// 加载Spring配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class ServiceTest {
// 自动注入Service
@Autowired
private UserService userService;
// 测试添加用户
@Test
public void testAdd(){
userService.addUser(new User("张三丰", 4000F));
userService.addUser(new User("宋远桥", 2000F));
}
// 测试转账
@Test
public void testUpdate(){
userService.updateUser("张三丰","宋远桥",1000F);
}
}
执行测试方法,数据库表中会成功插入数据并完成转账,说明 Spring 与 MyBatis 整合成功!
未完待续~~~



