Spring 代理模式与AOP基础
2025/9/17大约 8 分钟
Spring JdbcTemplate、代理模式与AOP基础
1. Spring JdbcTemplate
1.1 JdbcTemplate简介
JdbcTemplate是Spring提供的JDBC模板类,用于简化JDBC操作。它封装了JDBC的复杂性,提供了更简洁的API来执行SQL语句。
1.2 环境准备
Maven依赖
<dependencies>
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.0</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String realName;
private Integer age;
}
数据源配置
自定义数据源实现
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
// Setter方法
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Connection getConnection() throws SQLException {
try {
Class.forName(driver);
return DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 其他方法省略...
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {}
@Override
public void setLoginTimeout(int seconds) throws SQLException {}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
Spring配置文件
<?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="myDataSource" class="com.example.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- JdbcTemplate配置 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="myDataSource"/>
</bean>
</beans>
1.3 JdbcTemplate基本操作
新增操作
@Test
public void testInsert() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into t_user(id,real_name,age) values(?,?,?)";
int count = jdbcTemplate.update(sql, null, "张三", 30);
System.out.println("插入的记录条数:" + count);
}
修改操作
@Test
public void testUpdate() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "update t_user set real_name = ?, age = ? where id = ?";
int count = jdbcTemplate.update(sql, "张三丰", 55, 1);
System.out.println("更新的记录条数:" + count);
}
删除操作
@Test
public void testDelete() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "delete from t_user where id = ?";
int count = jdbcTemplate.update(sql, 1);
System.out.println("删除了几条记录:" + count);
}
查询单个对象
@Test
public void testSelectOne() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select id, real_name, age from t_user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 2);
System.out.println(user);
}
查询多个对象
@Test
public void testSelectAll() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select id, real_name, age from t_user";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
System.out.println(users);
}
查询单个值
@Test
public void testSelectOneValue() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select count(1) from t_user";
Integer count = jdbcTemplate.queryForObject(sql, int.class);
System.out.println("总记录条数:" + count);
}
1.4 批量操作
批量添加
@Test
public void testAddBatch() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into t_user(id,real_name,age) values(?,?,?)";
Object[] objs1 = {null, "小花", 20};
Object[] objs2 = {null, "小明", 21};
Object[] objs3 = {null, "小刚", 22};
List<Object[]> list = Arrays.asList(objs1, objs2, objs3);
int[] count = jdbcTemplate.batchUpdate(sql, list);
System.out.println(Arrays.toString(count));
}
2. 代理模式
2.1 代理模式介绍
代理模式是GoF23种设计模式之一,属于结构型设计模式。代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的角色:
- 代理类(代理主题):代替目标对象执行操作
- 目标类(真实主题):被代理的对象
- 公共接口(抽象主题):代理类和目标类的共同接口
代理模式的应用场景:
- 对象A和对象B无法直接交互时
- 功能需要增强时
- 目标需要被保护时
- 代码需要复用时
2.2 静态代理
接口定义
public interface OrderService {
void generate(); // 生成订单
void detail(); // 查看订单详情
void modify(); // 修改订单
}
目标类实现
public class OrderServiceImpl implements OrderService {
@Override
public void generate() {
try {
Thread.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已生成");
}
@Override
public void detail() {
try {
Thread.sleep(2541);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单信息如下:******");
}
@Override
public void modify() {
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已修改");
}
}
代理类实现
public class OrderServiceProxy implements OrderService {
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public void generate() {
long begin = System.currentTimeMillis();
orderService.generate();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
orderService.detail();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
orderService.modify();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
}
客户端使用
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建代理对象
OrderService proxy = new OrderServiceProxy(target);
// 调用代理方法
proxy.generate();
proxy.modify();
proxy.detail();
}
}
2.3 动态代理
动态代理可以在程序运行时动态生成代理类,解决静态代理类爆炸的问题。
JDK动态代理
JDK动态代理只能代理接口,基于反射机制实现。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TimerInvocationHandler implements InvocationHandler {
private Object target;
public TimerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
long begin = System.currentTimeMillis();
// 调用目标方法
Object retValue = method.invoke(target, args);
// 后置增强
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
return retValue;
}
}
代理工具类
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static Object newProxyInstance(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target)
);
}
}
客户端使用
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建代理对象
OrderService proxy = (OrderService) ProxyUtil.newProxyInstance(target);
// 调用代理方法
proxy.detail();
proxy.modify();
proxy.generate();
}
}
CGLIB动态代理
CGLIB可以代理类,基于继承机制实现。
Maven依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
目标类(无需实现接口):
public class UserService {
public void login() {
System.out.println("用户正在登录系统...");
}
public void logout() {
System.out.println("用户正在退出系统...");
}
}
方法拦截器:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前置增强
long begin = System.currentTimeMillis();
// 调用目标方法
Object retValue = methodProxy.invokeSuper(target, objects);
// 后置增强
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
return retValue;
}
}
客户端使用:
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建字节码增强器
Enhancer enhancer = new Enhancer();
// 设置要继承的类
enhancer.setSuperclass(UserService.class);
// 设置回调接口
enhancer.setCallback(new TimerMethodInterceptor());
// 生成代理对象
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login();
userServiceProxy.logout();
}
}
3. AOP基础
3.1 AOP介绍
AOP(Aspect Oriented Programming):面向切面编程,是对OOP的补充延伸。AOP底层使用动态代理实现。
AOP的优点:
- 代码复用性增强
- 代码易维护
- 使开发者更关注业务逻辑
交叉业务: 日志、事务管理、安全等系统服务,这些业务几乎在每个模块中都需要,称为交叉业务。
3.2 AOP七大术语
- 连接点(Joinpoint):程序执行流程中可以织入切面的位置
- 切点(Pointcut):真正织入切面的方法
- 通知(Advice):具体要织入的代码,包括前置、后置、环绕、异常、最终通知
- 切面(Aspect):切点 + 通知
- 织入(Weaving):把通知应用到目标对象的过程
- 代理对象(Proxy):目标对象被织入通知后产生的新对象
- 目标对象(Target):被织入通知的对象
3.3 切点表达式
切点表达式用来定义通知往哪些方法上切入。
语法格式:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
示例:
// 匹配com.powernode.mall.service包下所有类的所有delete开头的public方法
execution(public * com.powernode.mall.service.*.delete*(..))
// 匹配com.powernode.mall包及其子包下所有类的所有方法
execution(* com.powernode.mall..*(..))
// 匹配所有方法
execution(* *(..))
3.4 Spring AOP注解开发
Maven依赖
<dependencies>
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0</version>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.0</version>
</dependency>
<!-- Spring Aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
目标类
import org.springframework.stereotype.Component;
@Component
public class OrderService {
public void generate() {
System.out.println("订单已生成!");
}
}
切面类
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.example.service.OrderService.*(..))")
public void advice() {
System.out.println("我是一个前置通知");
}
}
Spring配置
<?xml version="1.0" encoding="UTF-8"?>
<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: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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.example"/>
<!-- 开启自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
测试程序
@Test
public void testAOP() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
OrderService orderService = context.getBean("orderService", OrderService.class);
orderService.generate();
}
3.5 通知类型
Spring AOP支持五种通知类型:
- @Before:前置通知,目标方法执行之前
- @AfterReturning:后置通知,目标方法执行之后
- @Around:环绕通知,目标方法前后都可以添加通知
- @AfterThrowing:异常通知,发生异常时执行
- @After:最终通知,相当于finally块
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.example.service.OrderService.*(..))")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
// 执行目标方法
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
}
@Before("execution(* com.example.service.OrderService.*(..))")
public void beforeAdvice() {
System.out.println("前置通知");
}
@AfterReturning("execution(* com.example.service.OrderService.*(..))")
public void afterReturningAdvice() {
System.out.println("后置通知");
}
@AfterThrowing("execution(* com.example.service.OrderService.*(..))")
public void afterThrowingAdvice() {
System.out.println("异常通知");
}
@After("execution(* com.example.service.OrderService.*(..))")
public void afterAdvice() {
System.out.println("最终通知");
}
}
总结
本章介绍了Spring的三个重要概念:
- JdbcTemplate:Spring提供的JDBC模板,简化了数据库操作
- 代理模式:包括静态代理和动态代理(JDK动态代理、CGLIB动态代理)
- AOP基础:面向切面编程的基本概念和注解开发方式
这些技术为Spring的高级特性(如事务管理、安全控制等)奠定了基础。在实际开发中,AOP广泛应用于日志记录、性能监控、事务管理等横切关注点的处理。