目录
- [1. 代理](#1. 代理)
-
- [1.1 **JDK 动态代理**](#1.1 JDK 动态代理)
- [1.2 **CGLIB 代理**](#1.2 CGLIB 代理)
- [1.3 JDK 动态代理与 CGLIB 的对比](#1.3 JDK 动态代理与 CGLIB 的对比)
- [2. AOP思想](#2. AOP思想)
-
- [2.1 基本概念](#2.1 基本概念)
- [2.2 AspectJ 技术](#2.2 AspectJ 技术)
- [2.3 **AspectJ 使用 XML 配置**](#2.3 AspectJ 使用 XML 配置)
- [2.4 **AspectJ 使用注解配置**](#2.4 AspectJ 使用注解配置)
- [2.5 通知](#2.5 通知)
- [3. JdbcTemplate](#3. JdbcTemplate)
-
- [3.1 **主要特点**](#3.1 主要特点)
- [3.2 **核心方法**](#3.2 核心方法)
- [3.3 **使用步骤**](#3.3 使用步骤)
- [3.4 **优点**](#3.4 优点)
- [4. 配置平台事务管理器和事务管理模板](#4. 配置平台事务管理器和事务管理模板)
-
- [4.1 配置平台事务管理器](#4.1 配置平台事务管理器)
- [4.2 配置事务管理模板](#4.2 配置事务管理模板)
-
- [4.2.1 编程式事务管理](#4.2.1 编程式事务管理)
- [4.2.2 声明式事务管理](#4.2.2 声明式事务管理)
1. 代理
1.1 JDK 动态代理
JDK 动态代理是 Java 标准库自带的一种代理机制,基于 接口 实现。它的核心是 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口。
特点
- 基于接口:JDK 动态代理只能代理实现了接口的目标对象。
- 性能较低:相较于 CGLIB,JDK 动态代理在运行时的性能稍低。
- 灵活性高:可以在运行时动态生成代理类,适用于需要动态增强接口方法的场景。
实现步骤
- 定义接口:目标对象需要实现一个接口。
- 实现
InvocationHandler
:用于处理代理类的方法调用。 - 生成代理对象 :通过
Proxy.newProxyInstance()
方法生成代理对象。
示例代码
- 定义接口
java
public interface BankService {
void transferMoney(Long fromAccountId, Long toAccountId, double amount);
}
- 实现目标类
java
public class BankServiceImpl implements BankService {
@Override
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
}
}
- 实现
InvocationHandler
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TransactionHandler implements InvocationHandler {
private Object target; // 目标对象
public TransactionHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 事务控制逻辑
System.out.println("Transaction started...");
try {
Object result = method.invoke(target, args); // 调用目标方法
System.out.println("Transaction committed.");
return result;
} catch (Exception e) {
System.out.println("Transaction rolled back.");
throw e;
}
}
}
- 生成代理对象
java
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
// 目标对象
BankService target = new BankServiceImpl();
// 创建 InvocationHandler
TransactionHandler handler = new TransactionHandler(target);
// 生成代理对象
BankService proxy = (BankService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
handler // InvocationHandler
);
// 调用代理对象的方法
proxy.transferMoney(1L, 2L, 100.0);
}
}
输出结果
Transaction started...
Transferring money from account 1 to account 2
Transaction committed.
1.2 CGLIB 代理
CGLIB(Code Generation Library)是一种基于 字节码生成 的代理技术,允许代理 没有实现接口的类 。它的核心是 net.sf.cglib.proxy.Enhancer
类和 net.sf.cglib.proxy.MethodInterceptor
接口。
特点
- 基于类:CGLIB 可以代理没有实现接口的类。
- 性能较高:CGLIB 生成的代理类直接基于目标类的字节码,性能通常优于 JDK 动态代理。
- 动态生成子类 :CGLIB 通过生成目标类的子类来实现代理。
实现步骤
- 定义目标类:目标类不需要实现接口。
- 实现
MethodInterceptor
:用于处理代理类的方法调用。 - 生成代理对象 :通过
Enhancer
类生成代理对象。
示例代码
- 定义目标类
java
public class BankService {
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
}
}
- 实现
MethodInterceptor
java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TransactionInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 事务控制逻辑
System.out.println("Transaction started...");
try {
Object result = proxy.invokeSuper(obj, args); // 调用目标方法
System.out.println("Transaction committed.");
return result;
} catch (Exception e) {
System.out.println("Transaction rolled back.");
throw e;
}
}
}
- 生成代理对象
java
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
// 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置目标类的父类
enhancer.setSuperclass(BankService.class);
// 设置 MethodInterceptor
enhancer.setCallback(new TransactionInterceptor());
// 生成代理对象
BankService proxy = (BankService) enhancer.create();
// 调用代理对象的方法
proxy.transferMoney(1L, 2L, 100.0);
}
}
输出结果
Transaction started...
Transferring money from account 1 to account 2
Transaction committed.
1.3 JDK 动态代理与 CGLIB 的对比
特性 | JDK 动态代理 | CGLIB 代理 |
---|---|---|
基于 | 接口 | 类 |
性能 | 较低(基于反射) | 较高(基于字节码生成) |
目标对象 | 必须实现接口 | 可以是任意类 |
代理对象 | 代理接口 | 生成目标类的子类 |
适用场景 | 需要代理接口方法 | 需要代理没有接口的类 |
依赖 | JDK 自带 | 需要额外依赖 CGLIB 库 |
2. AOP思想
2.1 基本概念
AOP(Aspect-Oriented Programming) 是一种编程范式,旨在通过将横切关注点(Cross-Cutting Concerns)从核心业务逻辑中分离出来,提高代码的模块化程度。横切关注点是指那些在多个模块中都会用到的功能,例如日志记录、事务管理、安全性检查等。AOP 的核心思想是将这些横切关注点从核心业务逻辑中解耦,封装成独立的模块(切面),并通过配置或注解的方式,将它们动态地应用到需要增强的类或方法上。
AOP 的关键术语
- 切面(Aspect):模块化的横切关注点,例如日志记录、事务管理等。
- 连接点(Join Point):程序执行过程中的某个点,例如方法调用、异常抛出等。
- 通知(Advice):切面在特定的连接点上执行的动作,例如在方法调用前后执行的日志记录。
- 切点(Pointcut) :定义了通知应该应用到哪些连接点的规则,例如所有以
transferMoney
开头的方法。 - 引入(Introduction):允许向现有的类添加新方法或属性。
- 织入(Weaving):将切面应用到目标对象的过程,可以在编译时、类加载时或运行时进行。
2.2 AspectJ 技术
AspectJ 是一个成熟的 AOP 框架,提供了丰富的语法和工具,支持在 Java 应用中实现 AOP。AspectJ 可以在编译时或运行时将切面织入到目标类中。
AspectJ 的主要特点
- 强大的定义切点的能力:AspectJ 提供了灵活的切点表达式,可以精确地指定通知应该应用到哪些方法或代码块。
- 多种通知类型:支持多种通知类型,包括前置通知(Before)、后置通知(After)、环绕通知(Around)等。
- 编译时织入:可以在编译时将切面织入到目标类中,提高运行时性能。
- 运行时织入:也可以在运行时通过代理或字节码操作将切面织入到目标类中。
- 丰富的注解和 XML 配置:支持通过注解或 XML 配置来定义切面和切点。
2.3 AspectJ 使用 XML 配置
添加 AspectJ 依赖
如果使用 Maven,在 pom.xml
中添加 AspectJ 的依赖。
xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
实例代码
1. 切面类
java
public class LoggingAspect {
// 前置通知
public void logBefore() {
System.out.println("Logging before method execution");
}
}
2. 目标类
java
package com.example.service;
public class BankService {
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
}
}
3. XML 配置文件
在 XML 配置文件中定义切面、切点和通知:
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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 -->
<bean id="bankService" class="com.example.service.BankService" />
<!-- 定义切面 bean -->
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />
<!-- 定义切面 -->
<aop:config>
<!-- 定义切点 -->
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<!-- 定义切面和通知 -->
<aop:aspect ref="loggingAspect">
<aop:before pointcut-ref="serviceMethods" method="logBefore"/>
</aop:aspect>
</aop:config>
</beans>
4. 测试代码
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BankService bankService = context.getBean(BankService.class);
bankService.transferMoney(1L, 2L, 100.0);
}
}
输出结果
Logging before method execution
Transferring money from account 1 to account 2
2.4 AspectJ 使用注解配置
添加 AspectJ 依赖
如果使用 Maven,在 pom.xml
中添加 AspectJ 的依赖。
xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
实例代码
1. 切面类
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect // 标记这是一个切面
public class LoggingAspect {
// 定义切点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 定义前置通知
@Before("serviceMethods()")
public void logBefore() {
System.out.println("Logging before method execution");
}
}
2. 目标类
java
package com.example.service;
public class BankService {
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
}
}
3. Spring 配置文件
如果使用 Spring,需要在 Spring 的 XML 配置文件中启用注解支持:
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!-- 启用 AspectJ 注解支持 -->
<aop:aspectj-autoproxy/>
<!-- 定义切面 bean -->
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />
<!-- 定义目标 bean -->
<bean id="bankService" class="com.example.service.BankService" />
</beans>
4. 测试代码
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BankService bankService = context.getBean(BankService.class);
bankService.transferMoney(1L, 2L, 100.0);
}
}
输出结果
Logging before method execution
Transferring money from account 1 to account 2
2.5 通知
AspectJ 提供了多种通知(Advice)类型,每种通知类型在不同的连接点(Join Point)上执行不同的逻辑。以下是 AspectJ 中常见的通知类型及其作用和语法。
1. Before 通知(前置通知)
- 作用:在目标方法执行之前执行。
- 语法:
java
@Aspect
public class BeforeAspect {
@Pointcut("execution(* com.example.service.*.*(..))") // 定义切点
public void serviceMethods() {}
@Before("serviceMethods()") // 前置通知
public void beforeAdvice() {
System.out.println("Before advice executed");
}
}
2. After 通知(后置通知)
- 作用:在目标方法执行之后执行(无论方法是否抛出异常)。
- 语法:
java
@Aspect
public class AfterAspect {
@Pointcut("execution(* com.example.service.*.*(..))") // 定义切点
public void serviceMethods() {}
@After("serviceMethods()") // 后置通知
public void afterAdvice() {
System.out.println("After advice executed");
}
}
3. AfterReturning 通知(返回通知)
- 作用:在目标方法成功返回且没有抛出异常时执行。
- 语法:
java
@Aspect
public class AfterReturningAspect {
@Pointcut("execution(* com.example.service.*.*(..))") // 定义切点
public void serviceMethods() {}
@AfterReturning(pointcut = "serviceMethods()", returning = "result") // 返回通知
public void afterReturningAdvice(Object result) {
System.out.println("AfterReturning advice executed. Result: " + result);
}
}
- 参数说明 :
returning = "result"
:将方法返回值绑定到通知方法的参数result
。
4. AfterThrowing 通知(异常通知)
- 作用:在目标方法抛出异常时执行。
- 语法:
java
@Aspect
public class AfterThrowingAspect {
@Pointcut("execution(* com.example.service.*.*(..))") // 定义切点
public void serviceMethods() {}
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex") // 异常通知
public void afterThrowingAdvice(Exception ex) {
System.out.println("AfterThrowing advice executed. Exception: " + ex.getMessage());
}
}
- 参数说明 :
throwing = "ex"
:将异常对象绑定到通知方法的参数ex
。
5. Around 通知(环绕通知)
- 作用:在目标方法执行前后执行,并且可以完全控制方法的执行流程。
- 语法:
java
@Aspect
public class AroundAspect {
@Pointcut("execution(* com.example.service.*.*(..))") // 定义切点
public void serviceMethods() {}
@Around("serviceMethods()") // 环绕通知
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around advice: Before method execution");
// 调用目标方法
Object result = joinPoint.proceed();
System.out.println("Around advice: After method execution");
return result;
}
}
- 关键点 :
ProceedingJoinPoint
是关键对象,调用其proceed()
方法以执行目标方法。- 可以修改方法的返回值,或完全阻止方法的执行。
3. JdbcTemplate
JdbcTemplate
是 Spring 框架提供的一个核心工具类,用于简化 JDBC 操作。它封装了 JDBC 的繁琐操作(如连接管理、异常处理、资源释放等),使开发者能够更专注于 SQL 语句和业务逻辑的实现。
引入依赖
xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
3.1 主要特点
-
简化 JDBC 操作:
- 自动管理数据库连接、语句和结果集的创建与释放。
- 减少样板代码,提高开发效率。
-
统一的异常处理:
- 将 JDBC 的
SQLException
转换为 Spring 的DataAccessException
,提供更清晰的异常层次结构。
- 将 JDBC 的
-
支持多种操作:
- 支持查询、更新、批量操作、存储过程调用等。
-
与 Spring 集成:
- 与 Spring 的事务管理、数据源等无缝集成。
3.2 核心方法
1. 更新操作(增删改)
-
update()
:用于执行 INSERT、UPDATE、DELETE 等 SQL 语句。javaint update(String sql, Object... args);
示例:
javaString sql = "UPDATE users SET name = ? WHERE id = ?"; int rows = jdbcTemplate.update(sql, "John", 1);
2. 查询操作
-
queryForObject()
:查询单行数据并映射为对象。java<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
示例:
javaString sql = "SELECT * FROM users WHERE id = ?"; User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);
-
query()
:查询多行数据并映射为对象列表。java<T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args);
示例:
javaString sql = "SELECT * FROM users"; List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
3. 执行任意 SQL
-
execute()
:用于执行任意 SQL 语句(如 DDL 语句)。javavoid execute(String sql);
示例:
javajdbcTemplate.execute("CREATE TABLE users (id INT, name VARCHAR(100))");
3.3 使用步骤
-
配置数据源:
-
在 Spring 配置文件中配置数据源(如
DataSource
)。 -
示例:
xml<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean>
-
-
创建 JdbcTemplate:
-
将数据源注入到
JdbcTemplate
中。 -
示例:
xml<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
-
-
在代码中使用:
-
通过依赖注入获取
JdbcTemplate
实例并调用其方法。 -
示例:
java@Autowired private JdbcTemplate jdbcTemplate; public void addUser(User user) { String sql = "INSERT INTO users (name, age) VALUES (?, ?)"; jdbcTemplate.update(sql, user.getName(), user.getAge()); }
-
3.4 优点
- 简化代码 :
- 减少 JDBC 样板代码,提高开发效率。
- 异常处理 :
- 提供统一的异常处理机制,避免繁琐的
try-catch
块。
- 提供统一的异常处理机制,避免繁琐的
- 与 Spring 集成 :
- 与 Spring 的事务管理、数据源等无缝集成。
- 灵活性 :
- 支持自定义
RowMapper
和ResultSetExtractor
,满足复杂需求。
- 支持自定义
4. 配置平台事务管理器和事务管理模板
在 Spring 框架中,事务管理是一个非常重要的功能,用于确保数据库操作的完整性。Spring 提供了多种事务管理方式,其中最常见的两种是声明式事务管理和编程式事务管理。
4.1 配置平台事务管理器
平台事务管理器是 Spring 事务管理的核心接口,它负责事务的开始、提交和回滚。最常见的实现是 DataSourceTransactionManager
,用于管理 JDBC 数据源的事务。
- 配置数据源
首先,需要配置一个数据源。这里我们使用 DriverManagerDataSource
作为示例数据源:
xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
- 配置平台事务管理器
接下来,配置 DataSourceTransactionManager
,将数据源注入到事务管理器中:
xml
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
4.2 配置事务管理模板
事务管理模板(TransactionTemplate
)是一个辅助类,用于简化事务管理的编程式操作。它可以自动管理事务的开始、提交和回滚。
配置事务管理模板
在 Spring 配置文件中配置 TransactionTemplate
,并将平台事务管理器注入到模板中:
xml
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
4.2.1 编程式事务管理
使用 TransactionTemplate
进行编程式事务管理,可以通过 lambda 表达式或 TransactionCallback
接口来实现。
示例代码
java
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
public void addUser(User user) {
transactionTemplate.execute(status -> {
try {
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getAge());
// 模拟异常
// int i = 1 / 0;
return null;
} catch (Exception e) {
// 回滚事务
status.setRollbackOnly();
throw e;
}
});
}
4.2.2 声明式事务管理
声明式事务管理通过在 Spring 配置文件中使用 AOP 配置事务,实现更简洁的事务管理。
- 配置事务管理器
确保已经配置了 DataSourceTransactionManager
。
- 配置事务管理器的 AOP 支持
使用 tx:annotation-driven
开启基于注解的事务管理:
xml
<tx:annotation-driven transaction-manager="transactionManager"/>
- 使用
@Transactional
注解
在需要事务管理的类或方法上使用 @Transactional
注解:
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void addUser(User user) {
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getAge());
// 模拟异常
// int i = 1 / 0;
}
}