逻辑梳理
这部分完成AOP部分。
先梳理AOP的步骤:
getBean()方法作为入口,而后是几个关键的类。Context在前文都有提到,现在解释一下其他的类。
AdviseSupport:通知的工具类
- 完成配置文件的解析
- 将Advise和目标类的方法建立关系——通过map结构
Map<Method,Map<String,Advice>>
例如:
AopConfig:调用方法时,判断方法的名称是否符合切面规则,如果符合就要触发通知
Advice:该类包含的属性:aspect的实例、method的回调
JdkDynamicAopProxy:生成一个全新的,类字节码重组,用代码生成代码,在内存中编译,在内存中完成实例化,返回的新的代理类,和原始类处于同一个继承体系(可赋值给目标类,对于用户来说,是无感知的,但是实际上在内部完成了偷梁换柱)
骨架与填空
先从context入手,在对象实例化之后进行织入:
上面相当于搭好了骨架,接着分别填空。
//从配置文件读取信息保存到config对象
private PIGAdvisedSupport instantionAopConfig(PigBeanDefinition beanDefinition) {
PIGAopConfig config = new PIGAopConfig();
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 PIGAdvisedSupport(config);
}
配置文件可能长这样:
PIGAdvisedSupport比较关键,它负责根据以上解析出来的config,将Advise和目标类的方法建立关系。
也就是config.setTargetClass(clazz);这个方法
public void setTargetClass(Class targetClass) {
this.targetClass = targetClass;
//解析表达式
parse();
}
public Class getTargetClass() {
return targetClass;
}
private void parse() {
//对配置文件中的特殊字符进行转义
String pointCut = config.getPointCut()
.replaceAll("\\.","\\\\.")
.replaceAll("\\\\.\\*",".*")
.replaceAll("\\(","\\\\(")
.replaceAll("\\)","\\\\)");
// pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
String pointCutForClassRegex = pointCut.substring(0,pointCut.lastIndexOf("\\(") - 4);
//提取class的全名
pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
try {
//开始映射目标类方法和通知的关系
methodCache = new HashMap<Method, Map<String, PIGAdvice>>();
//开始匹配目标类的方法
Pattern pointCutPattern = Pattern.compile(pointCut);
//先把要织入的切面的方法缓存起来
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,PIGAdvice> advices = new HashMap<String, PIGAdvice>();
//前置通知
if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))){
advices.put("before",
new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
}
//后置通知
if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
advices.put("after",
new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
}
//异常通知
if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
advices.put("afterThrowing",
new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow())));
}
methodCache.put(method,advices);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
测试
这是用户侧的代码(切面):
@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("出现异常");
}
}
(请求):
@PIGRequestMapping("/query.json")
public PIGModelAndView query(HttpServletRequest request, HttpServletResponse response,
@PIGRequestParam("name") String name){
String result = queryService.query(name);
return out(response,result);
}