1、概述
-
AOP 面向切面编程,AOP 可以对代码进行“增强”并且以松耦合的形式进行“增强”。
-
松耦合的增强例子:对于数据的“增删改”,引入事务的方式来确保数据的准确性。传统的编程中,事务机制代码(比如“开启事务”、“提交事务”、“回滚事务”等)是和业务逻辑代码(数据“增删改”)编写在一块。这种情况下,就是一种严重的耦合。而 AOP 可以将两部分代码分离,形成松耦合。提高可读性和可维护性。
-
Spring AOP 的机制基于的是动态代理。Java 中常见的动态代理技术有两种,一种是 JDK 动态代理,一种是 cglib 动态代理。
2、JDK 动态代理
-
基于接口的动态代理技术。所代理的类必须是实现接口的类(至少实现一个接口)。
-
利用拦截器和反射机制生成一个代理接口的匿名类,在调用具体方法前调用
InvokeHandler
来处理,从而实现方法增强。
- 案例:模拟(使用输出语句)“事务处理数据修改(转账)”
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
public class Account {
private String id;
private Integer amount;
}
import com.yscyber.spring.five.pojo.Account;
public interface AccountRepo {
Account getAccountById(String id);
int updateAccount(String id, Account account);
}
import com.yscyber.spring.five.pojo.Account;
import com.yscyber.spring.five.repo.AccountRepo;
import org.springframework.stereotype.Repository;
/**
* 模拟访问数据库
*/
@Repository
public class AccountRepoImpl implements AccountRepo {
@Override
public Account getAccountById(String id) {
return new Account(id, 1000);
}
@Override
public int updateAccount(String id, Account account) {
System.out.println("更新后账户信息:" + account);
return 1;
}
}
public interface AccountService {
int transfer(String outAccountId, String inAccountId, Integer amount);
}
import com.yscyber.spring.five.pojo.Account;
import com.yscyber.spring.five.repo.AccountRepo;
import com.yscyber.spring.five.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 模拟“转账”
* 由于采用动态代理,有关事务的代码可以抽取出来,使业务代码更纯净
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountRepo accountRepo;
@Override
public int transfer(String outAccountId, String inAccountId, Integer amount) {
Account outAccount = accountRepo.getAccountById(outAccountId);
Account inAccount = accountRepo.getAccountById(inAccountId);
System.out.println("转出账户原始信息:" + outAccount);
System.out.println("转入账户原始信息:" + inAccount);
outAccount.setAmount(outAccount.getAmount() - amount);
inAccount.setAmount(inAccount.getAmount() + amount);
accountRepo.updateAccount(outAccountId, outAccount);
accountRepo.updateAccount(inAccountId, inAccount);
return 1;
}
}
import com.yscyber.spring.five.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Component
public class AccountServiceProxy {
@Autowired
private AccountService accountService;
/**
* 生成代理对象
*/
public AccountService createAccountServiceProxy() {
AccountService accountServiceProxy = null;
// Proxy.newProxyInstance() 返回代理对象
// Proxy.newProxyInstance() 参数1:被代理对象的类加载器
// 参数2:被代理对象所实现的接口
// 参数3:InvocationHandler,负责编写增强代码
accountServiceProxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("开启事务...");
// 执行原方法,参数1是被代理对象
result = method.invoke(accountService, args);
System.out.println("提交事务...");
} catch (Exception e) {
System.out.println("回滚事务...");
} finally {
System.out.println("释放资源等操作...");
}
return result;
}
});
return accountServiceProxy;
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.yscyber.spring.five"/>
</beans>
import com.yscyber.spring.five.proxy.AccountServiceProxy;
import com.yscyber.spring.five.service.AccountService;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MySpringTest {
@Autowired
private AccountServiceProxy accountServiceProxy;
@Test
public void test1() {
AccountService accountService = accountServiceProxy.createAccountServiceProxy();
accountService.transfer("1", "2", 100);
}
}
/*
开启事务...
转出账户原始信息:Account(id=1, amount=1000)
转入账户原始信息:Account(id=2, amount=1000)
更新后账户信息:Account(id=1, amount=900)
更新后账户信息:Account(id=2, amount=1100)
提交事务...
释放资源等操作...
*/
3、cglib 动态代理
-
基于父类的动态代理技术。在某个类没有实现接口的情况下,可以采用 cglib 实现动态代理。
-
动态生成一个要被代理的类的子类,动态生成的子类重写要代理的类的所有不是
final
的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行增强。
- 案例:模拟(使用输出语句)“事务处理数据修改(转账)”
package com.yscyber.spring.five.proxy;
import com.yscyber.spring.five.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class AccountServiceProxy {
@Autowired
private AccountService accountService;
/**
* 生成代理对象
*/
public AccountService createAccountServiceProxy() {
AccountService accountServiceProxy = null;
accountServiceProxy = (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
System.out.println("开启事务...");
// 执行原方法,参数1是被代理对象,参数2是方法入参
result = method.invoke(accountService, objects);
System.out.println("提交事务...");
} catch (Exception e) {
System.out.println("回滚事务...");
} finally {
System.out.println("释放资源等操作...");
}
return result;
}
});
return accountServiceProxy;
}
}
网友评论