1.Spring AOP顶层设计
2.Spring AOP执行流程
下面是代码实现
3.在 application.properties中增加如下自定义配置:
代码语言:javascript复制#托管的类扫描包路径#
scanPackage=com.gupaoedu.vip.demo
templateRoot=layouts
#切面表达式expression#
pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
#切面类
aspectClass=com.gupaoedu.vip.demo.aspect.LogAspect
#前置通知回调方法
aspectBefore=before
#后置通知回调方法
aspectAfter=after
#异常通知回调方法
aspectAfterThrow=afterThrowing
#异常类型捕获
aspectAfterThrowingName=java.lang.Exception
4.GPJdkDynamicAopProxy
代码语言:javascript复制public class GPJdkDynamicAopProxy implements InvocationHandler {
private GPAdvisedSupport config;
public GPJdkDynamicAopProxy(GPAdvisedSupport config) {
this.config = config;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map<String,GPAdvice> advices = config.getAdvices(method,null);
Object returnValue;
try {
invokeAdivce(advices.get("before"));
returnValue = method.invoke(this.config.getTarget(),args);
invokeAdivce(advices.get("after"));
}catch (Exception e){
invokeAdivce(advices.get("afterThrow"));
throw e;
}
return returnValue;
}
private void invokeAdivce(GPAdvice advice) {
try {
advice.getAdviceMethod().invoke(advice.getAspect());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.config.getTargetClass().getInterfaces(),this);
}
}
5.GPAdvisedSupport 配置解析
代码语言:javascript复制/**
* 解析AOP配置的工具类
*/
public class GPAdvisedSupport {
private GPAopConfig config;
private Object target;
private Class targetClass;
private Pattern pointCutClassPattern;
private Map<Method,Map<String,GPAdvice>> methodCache;
public GPAdvisedSupport(GPAopConfig config) {
this.config = config;
}
//解析配置文件的方法
private void parse() {
//把Spring的Excpress变成Java能够识别的正则表达式
String pointCut = config.getPointCut()
.replaceAll("\.", "\\.")
.replaceAll("\\.\*", ".*")
.replaceAll("\(", "\\(")
.replaceAll("\)", "\\)");
//保存专门匹配Class的正则
String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\(") - 4);
pointCutClassPattern = Pattern.compile("class " pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") 1));
//享元的共享池
methodCache = new HashMap<Method, Map<String, GPAdvice>>();
//保存专门匹配方法的正则
Pattern pointCutPattern = Pattern.compile(pointCut);
try{
Class aspectClass = Class.forName(this.config.getAspectClass());
Map<String,Method> aspectMethods = new HashMap<String, Method>();
for (Method method : aspectClass.getMethods()) {
aspectMethods.put(method.getName(),method);
}
for (Method method : this.targetClass.getMethods()) {
String methodString = method.toString();
if(methodString.contains("throws")){
methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
}
Matcher matcher = pointCutPattern.matcher(methodString);
if(matcher.matches()){
Map<String,GPAdvice> advices = new HashMap<String, GPAdvice>();
if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))){
advices.put("before",new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
}
if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
advices.put("after",new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
}
if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
GPAdvice advice = new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow()));
advice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow",advice);
}
//跟目标代理类的业务方法和Advices建立一对多个关联关系,以便在Porxy类中获得
methodCache.put(method,advices);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
//根据一个目标代理类的方法,获得其对应的通知
public Map<String,GPAdvice> getAdvices(Method method, Object o) throws Exception {
//享元设计模式的应用
Map<String,GPAdvice> cache = methodCache.get(method);
if(null == cache){
Method m = targetClass.getMethod(method.getName(),method.getParameterTypes());
cache = methodCache.get(m);
this.methodCache.put(m,cache);
}
return cache;
}
//给ApplicationContext首先IoC中的对象初始化时调用,决定要不要生成代理类的逻辑
public boolean pointCutMath() {
return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
public void setTargetClass(Class<?> targetClass) {
this.targetClass = targetClass;
parse();
}
public void setTarget(Object target) {
this.target = target;
}
public Class getTargetClass() {
return targetClass;
}
public Object getTarget() {
return target;
}
}
6.GPAdvice通知接口定义
代码语言:javascript复制@Data
public class GPAdvice {
private Object aspect;
private Method adviceMethod;
private String throwName;
public GPAdvice(Object aspect, Method adviceMethod) {
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
}
7.接入getBean()方法与IOC容器衔接
找到GPApplicationContext的 getBean()方 法 ,我们知道getBean()中负责Bean初始化的方法其实 就是instantiateBean() ,我们在初始化时就可以确定是否返回原生Bean还是Proxy Bean。 代码实现 如下:
代码语言:javascript复制 //创建真正的实例对象
private Object instantiateBean(String beanName, GPBeanDefinition beanDefinition) {
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
if(this.factoryBeanObjectCache.containsKey(beanName)){
instance = this.factoryBeanObjectCache.get(beanName);
}else {
Class<?> clazz = Class.forName(className);
//2、默认的类名首字母小写
instance = clazz.newInstance();
//==================AOP开始=========================
//如果满足条件,就直接返回Proxy对象
//1、加载AOP的配置文件
GPAdvisedSupport config = instantionAopConfig(beanDefinition);
config.setTargetClass(clazz);
config.setTarget(instance);
//判断规则,要不要生成代理类,如果要就覆盖原生对象
//如果不要就不做任何处理,返回原生对象
if(config.pointCutMath()){
instance = new GPJdkDynamicAopProxy(config).getProxy();
}
//===================AOP结束========================
this.factoryBeanObjectCache.put(beanName, instance);
}
}catch (Exception e){
e.printStackTrace();
}
return instance;
}
private GPAdvisedSupport instantionAopConfig(GPBeanDefinition beanDefinition) {
GPAopConfig config = new GPAopConfig();
config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
return new GPAdvisedSupport(config);
}
8.LogAspect自定义切面配置
代码语言:javascript复制@Slf4j
public class LogAspect {
//在调用一个方法之前,执行before方法
public void before(){
//这个方法中的逻辑,是由我们自己写的
log.info("Invoker Before Method!!!");
}
//在调用一个方法之后,执行after方法
public void after(){
log.info("Invoker After Method!!!");
}
public void afterThrowing(){
log.info("出现异常");
}
}
9.IModifyService业务接口定义
代码语言:javascript复制/**
* 增删改业务
*/
public interface IModifyService {
/**
* 增加
*/
public String add(String name, String addr) throws Exception;
/**
* 修改
*/
public String edit(Integer id, String name);
/**
* 删除
*/
public String remove(Integer id);
}
10.ModifyService切面业务逻辑实现
代码语言:javascript复制/**
* 增删改业务
*/
@GPService
public class ModifyService implements IModifyService {
/**
* 增加
*/
public String add(String name,String addr) throws Exception{
throw new Exception("这是Tom故意抛出来的异常");
// return "modifyService add,name=" name ",addr=" addr;
}
/**
* 修改
*/
public String edit(Integer id,String name) {
return "modifyService edit,id=" id ",name=" name;
}
/**
* 删除
*/
public String remove(Integer id) {
return "modifyService id=" id;
}
}
至此AOP模块就大功告成。
参考资料:
1.《spring 5核心原理与30个类手写实战》谭勇德著