在不改变类的代码的前提下,对类方法进行功能加强。
复制代码
向用户提供咱们的AOP功能让他们来进行对类方法的加强。
复制代码
1.进行功能加强:功能---Advice 通知
2.对类方法加强:可选的加强方法---PointCut 切入点
3.不改原类代码:与原类解耦---Weaving 织入
复制代码
Advice:通知,加强的功能
Join Point:链接点,可选的方法点
Pointcut:切入点,选择切入的方法点
Aspect:切面,选择的(多个)方法点+加强的功能
Introduction:引入,添加新的方法或属性到已存在的类中
Weaving:织入,不改变原类的代码进行加强
复制代码
其中 Advice, Join Point,Pointcut,Aspect 都是用户提供咱们使用
Weaving 须要咱们本身实现
Introduction 在spring中存在可是不多涉及
ps:提供者和使用者的概念在设计模式中常常涉及
复制代码
Advice:
1.由用户提供加强功能逻辑代码
2.不一样的加强需求会存在不一样的逻辑
3.可选时机,在方法先后或异常时进行加强
4.同一个切入点,可能存在多个加强
Pointcut
1.用户指定切入点
2.用户可在多点进行加强
Weaving
1.不改变原类代码
2.在框架中已经实现
复制代码
Aspect 分析:html
(1)Advice是由用户提供咱们使用,且是多变的,那咱们如何能认识用户提供的东西, (2)如何让咱们的代码隔绝用户提供的多变?java
可否由咱们提供一套标准接口,用户经过实现接口来提供他们不一样的逻辑
应对变化---面向接口编程(好比JDBC,日志等)
复制代码
此时咱们做为空壳接口来编写便可,做为加强功能的总标识spring
public interface Advice {
}
复制代码
首先围绕Advice的特色3,可选时机这块,它能够进行前置加强,后置加强,环绕加强,异常处理加强,这时咱们须要定义一个标准接口方法,让用户作到实现它就能够进行加强。此时咱们须要考虑的因素有:express
四种加强所需的参数是否同样?编程
目的是对方法进行加强,应该须要提供的是方法相关的信息,咱们也仅能提供有关方法的信息
其中方法包含的信息有:
1.方法自己:Method
2.方法所属对象:Object
3.方法的参数:Object[]
复制代码
在方法执行前进行加强,不须要任何的返回值
复制代码
1.方法自己:Method
2.方法所属对象:Object
3.方法的参数:Object[]
4.方法的返回值:Object
复制代码
方法执行后进行加强也不须要返回值
复制代码
1.方法自己:Method
2.方法所属对象:Object
3.方法的参数:Object[]
复制代码
方法被它包裹,也就是自己类方法的执行须要这个方法的执行逻辑来带动执行,
因此它须要返回方法的返回值Object
复制代码
异常信息
复制代码
已经异常了···
复制代码
须要的,正常来讲是使用try-catch来根据不一样的异常来进行不一样的处理,
就是在不一样的catch中进行不一样的加强处理,那其实就是能够借助环绕加强的效果来实现
复制代码
通过前面的分析,咱们已经能够总结出咱们所须要的三个方法设计模式
分三个接口,此时还能够经过类型来区分不一样的Advice
复制代码
public interface MethodBeforeAdvice extends Advice{
/**
* 实现方法的前置加强
*
* @param method 被加强的方法
* @param args 方法的参数
* @param target 被加强的目标对象
* @throws Throwable
*/
void before(Method method,Object[] args,Object target) throws Throwable;
}
复制代码
public interface AfterReturnAdvice extends Advice {
/**
* 实现方法的后置加强
*
* @param returnValue 返回值
* @param method 被加强的方法
* @param args 方法的参数
* @param target 方法的目标对象
* @throws Throwable
*/
void afterReturn(Object returnValue, Method method,Object[] args,Object target) throws Throwable;
}
复制代码
public interface MethodSurroudAdvice extends Advice {
/**
* 对方法进行环绕加强还有异常处理的加强
*
* @param method
* @param args
* @param target
* @return
*/
Object invoke(Method method,Object[] args,Object target);
}
复制代码
Pointcut的特色?---用户指定并多点指定api
咱们须要为用户提供一个东西让他们来灵活指定多个方法点,切入点就是这些点。那问题来了框架
1.指定方法,是否以方法做为描述信息
2.如何指定方法?---XX类的XX方法
3.方法重载如何处理?---加上参数类型
复制代码
此时是否有感受,这些东西恰好就组成了一个完整的方法签名呢!ide
ps:一类类的某些方法,好比说,某个包或者某个类的全部方法,全部类中do开头的方法,全部类中带有service的方法等等学习
咱们须要一个表达式来描述这些信息
包名:有父子特色,要能模糊匹配
类名:模糊匹配
方法名与参数类型:模糊匹配,参数能够多个
咱们常见的表达式,好比正则(其实也是可行),Ant Path,
AspectJ里面的pointcut(首选,由于AspectJ自己就是面向切面编程的组件)
复制代码
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
复制代码
经过spring的官网能够进行学习:
docs.spring.io/spring/docs…
切点定义表达式
复制代码
对类,方法进行匹配
切点应提供匹配类,匹配方法的行为
复制代码
支持可变性问题须要由咱们来定义一个标准接口,定义好基本行为,面向接口编程
屏蔽掉具体的实现.(像实现Advice同样)
复制代码
因此咱们设计一个Pointcut的标准接口
public interface Pointcut {
//提供两个方法,匹配类和匹配方法
boolean matchClass(Class<?> targetClass);
boolean matchMethod(Method method,Class<?> targetClass);
}
复制代码
public class AspectJExpressionPointcut implements Pointcut{
private String expression;
public AspectJExpressionPointcut(String expression){
this.expression = expression;
}
public String getExpression(){
return this.expression;
}
@Override
public boolean matchClass(Class<?> targetClass) {
return false;
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
return false;
}
}
复制代码
此时咱们完成了一个空壳实现,还需引入AspectJ的jar包来完成切点表达式的实现,Spring AOP也仅仅使用了AspectJ的表达式api
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
复制代码
大体理解下AspectJ的简单应用,咱们应该执行的步骤是:
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
复制代码
PointcutExpression pe =
pp.parsePointcutExpression(expression)
复制代码
pe.couldMatchJoinPointsInType(targetClass)为匹配类的方法,但有匹配不许确的问题
因此咱们须要匹配方法的api
pe.matchesMethodExecution(method)
而后使用ShadowMatch类中的alwaysMatches()方法便可
复制代码
public class AspectJExpressionPointcut implements Pointcut{
//获得了一个全局的切点解析器
private static PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
private String expression;
private PointcutExpression pe;
public AspectJExpressionPointcut(String expression) {
super();
this.expression = expression;
//解析成对应的表达式对象
pe = pp.parsePointcutExpression(expression);
}
@Override
public boolean matchClass(Class<?> targetClass) {
return pe.couldMatchJoinPointsInType(targetClass);
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
}
public String getExpression() {
return expression;
}
}
复制代码
为了给用户提供操做优化,咱们设计一个Advisor把Advice和Pointcut组合起来,当用户使用aspectJ来指定他的切入点时,就只需指定adviceBeanName,expression便可
public interface Advisor {
String getAdviceBeanName();
String getExpression();
}
复制代码
public class AspectJPointcutAdvisor implements Advisor{
private String adviceBeanName;
private String expression;
private AspectJExpressionPointcut pointcut;
public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
super();
this.adviceBeanName = adviceBeanName;
this.expression = expression;
this.pointcut = new AspectJExpressionPointcut(this.expression);
}
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String getAdviceBeanName() {
return this.adviceBeanName;
}
@Override
public String getExpression() {
return this.expression;
}
}
复制代码
比较纯理论,代码很少且简单,更多地仍是要理清楚一些概念性的问题.不足之处请在评论处留言...望共同进步,望能在点滴积累中慢慢收获成长