⼿写AOP实现过程
⼀.⼿写Aop前基础知识
1.aop是什么?
⾯向切⾯编程(AOP):是⼀种编程范式,提供从另⼀个⾓度来考虑程序结构从⽽完善⾯向对象编程(OOP)。
在进⾏OOP开发时,都是基于对组件(⽐如类)进⾏开发,然后对组件进⾏组合,OOP最⼤问题就是⽆法解耦组件进⾏开发,⽽AOP就是为了克服这个问题⽽出现的,它来进⾏这种耦合的分离。AOP为开发者提供⼀种进⾏横切关注点(⽐如⽇志关注点横切了⽀付关注点)分离并织⼊的机制,把横切关注点分离,然后通过某种技术织⼊到系统中,从⽽⽆耦合的完成了我们的功能。
2.aop能⼲什么?
AOP主要⽤于横切关注点和织⼊,因此需要理解横切关注点和织⼊:
关注点:可以认为是所关注的任何东西,⽐如上边的⽀付组件;
关注点分离:将问题细化从⽽单独部分,即可以理解为不可再分割的组件,如上边的⽇志组件和⽀付组件;
横切关注点:⼀个组件⽆法完成需要的功能,需要其他组件协作完成,如⽇志组件横切于⽀付组件;
织⼊:横切关注点分离后,需要通过某种技术将横切关注点融合到系统中从⽽完成需要的功能,因此需要织⼊,织⼊可能在编译期、加载期、运⾏期等进⾏。
横切关注点可能包含很多,⽐如⾮业务的:⽇志、事务处理、缓存、性能统计、权限控制等等这些⾮业务的基础功能;还可能是业务的:如某个业务组件横切于多个模块。
⾯向切⾯⽅式,先将横切关注点分离,再将横切关注点织⼊到⽀付系统中:
3.aop的优点?
完善OOP;
降低组件和模块之间的耦合性;
使系统容易扩展;
⽽且由于关注点分离从⽽可以获得组件的更好复⽤。
⼆.aop底层实现原理:代理模式
关于静态代理模式,详情请参阅我另⼀⽚博客:
三.⼿写aop主要实现过程
1.定义配置标记
@Aspect:配置标记横切对象(⽅法)的地址
@Order:配置标记横切的顺序
@Aspect/@Order
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
String pointcut();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
/**
* 控制类的执⾏顺序,值越⼩优先级越⾼
*/
int value();
}
2.定义默认切⾯类
package org.simplespring.aop.aspect;
import flect.Method;
/**
* 定义默认切⾯类
*/
public abstract class DefaultAspect {
/**
* 事前拦截
* @param targetClass 被代理的⽬标类
* @param method 被代理的⽬标⽅法
* @param args 被代理的⽬标⽅法对应的参数列表
* @throws Throwable
*/
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable{
}
/**
* 事后拦截
* @param targetClass 被代理的⽬标类
* @param method 被代理的⽬标⽅法
* @param args 被代理的⽬标⽅法对应的参数列表
* @param returnValue 被代理的⽬标⽅法执⾏后的返回值
* @throws Throwable
*/
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable{
return returnValue;
}
/**
*
* @param targetClass 被代理的⽬标类
* @param method 被代理的⽬标⽅法
* @param args 被代理的⽬标⽅法对应的参数列表
* @param e 被代理的⽬标⽅法抛出的异常
* @throws Throwable
*/
public void afterThrowing(Class<?> targetClass, Method method, Object[] args, Throwable e) throws Throwable{
}
}
3.定义切⾯织⼊器
核⼼流程:
1.获取所有的切⾯类
2.遍历容器管理的类
3.筛选出匹配容器管理类的切⾯aspectlist
4.尝试进⾏Aspect的织⼊⽣成动态代理对象并替换beancontainer中原先class对应所对应的实例对象
package org.simplespring.aop;
import org.simplespring.aop.annotation.Aspect;
import org.simplespring.aop.annotation.Order;
import org.simplespring.aop.aspect.AspectInfo;
import org.simplespring.aop.aspect.DefaultAspect;
import BeanContainer;
import org.simplespring.util.ValidationUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @Classname AspectWeaver
* @Description 切⾯织⼊器
* @Date 2020/8/8 19:43
* @Created by zhangtianci
*/
public class AspectWeaver {
/**
* 拥有⼀个bean容器
*/
private BeanContainer beanContainer;
public AspectWeaver(){
beanContainer = Instance();
}
/**
* 1.获取所有的切⾯类
* 2.遍历容器⾥的类
* 3.筛选出匹配类的切⾯aspect
* 4.尝试进⾏Aspect的织⼊⽣成动态代理对象
*/
public void doAop() {
//1.获取所有的切⾯类
Set<Class<?>> aspectSet = ClassesByAnnotation(Aspect.class);
if(ValidationUtil.isEmpty(aspectSet)){return;}
/
/2.拼装AspectInfoList
List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
//3.遍历容器⾥的类
Set<Class<?>> classSet = Classes();
for (Class<?> targetClass: classSet) {
//排除AspectClass⾃⾝
if(targetClass.isAnnotationPresent(Aspect.class)){
continue;
}
//4.粗筛符合条件的Aspect
List<AspectInfo> roughMatchedAspectList = collectRoughMatchedAspectListForSpecificClass(aspectI
nfoList, targetClass); //5.尝试进⾏Aspect的织⼊
wrapIfNecessary(roughMatchedAspectList,targetClass);
}
}
private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass) {
if(ValidationUtil.isEmpty(roughMatchedAspectList)){return;}
//创建动态代理对象
AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList);
Object proxyBean = ateProxy(targetClass, aspectListExecutor);
beanContainer.addBean(targetClass, proxyBean);
}
private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) { List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
for(AspectInfo aspectInfo : aspectInfoList){
//粗筛
PointcutLocator().roughMatches(targetClass)){
roughMatchedAspectList.add(aspectInfo);
}
}
return roughMatchedAspectList;
}
private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
List<AspectInfo> aspectInfoList = new ArrayList<>();
for(Class<?> aspectClass : aspectSet){
if (verifyAspect(aspectClass)){
Order orderTag = Annotation(Order.class);
Aspect aspectTag = Annotation(Aspect.class);
DefaultAspect defaultAspect = (DefaultAspect) Bean(aspectClass);
//初始化表达式定位器
PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator);
aspectInfoList.add(aspectInfo);
} else {
/
/不遵守规范则直接抛出异常
throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect"); }
}
return aspectInfoList;
}
//框架中⼀定要遵守给Aspect类添加@Aspect和@Order标签的规范,同时,必须继承⾃DefaultAspect.class
//此外,@Aspect的属性值不能是它本⾝
private boolean verifyAspect(Class<?> aspectClass) {
return aspectClass.isAnnotationPresent(Aspect.class) &&
aspectClass.isAnnotationPresent(Order.class) &&
DefaultAspect.class.isAssignableFrom(aspectClass);
}
}
切⾯信息包装类(增强的动作/增强对象地址/横切顺序)
package org.simplespring.aop.aspect;
import lombok.AllArgsConstructor;
import lombok.Getter;
sortedlistimport org.simplespring.aop.PointcutLocator;
@AllArgsConstructor
@Getter
public class AspectInfo {
private int orderIndex;
private DefaultAspect aspectObject;
private PointcutLocator pointcutLocator;
}
采⽤cglib动态的实现⽅式:
实现lib.proxy.MethodInterceptor接⼝,定义代理后⽅法的动作(相当于实现jdk动态代理中的InvocationHandler接⼝)
package org.simplespring.aop;
import lombok.Getter;
import lib.proxy.MethodInterceptor;
import lib.proxy.MethodProxy;
import org.simplespring.aop.aspect.AspectInfo;
import org.simplespring.util.ValidationUtil;
import flect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class AspectListExecutor implements MethodInterceptor {
//被代理的类
private Class<?> targetClass;
//排好序的Aspect列表
@Getter
private List<AspectInfo>sortedAspectInfoList;
public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList){
this.targetClass = targetClass;
this.sortedAspectInfoList = sortAspectInfoList(aspectInfoList);
}
/**
* 按照order的值进⾏升序排序,确保order值⼩的aspect先被织⼊
*
* @param aspectInfoList
* @return
*/
private List<AspectInfo> sortAspectInfoList(List<AspectInfo> aspectInfoList) {
Collections.sort(aspectInfoList, new Comparator<AspectInfo>() {
@Override
public int compare(AspectInfo o1, AspectInfo o2) {
//按照值的⼤⼩进⾏升序排序
OrderIndex() - o2.getOrderIndex();
}
});
return aspectInfoList;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
collectAccurateMatchedAspectList(method);
if(ValidationUtil.isEmpty(sortedAspectInfoList)){
returnValue = methodProxy.invokeSuper(proxy, args);
return returnValue;
}
//1.按照order的顺序升序执⾏完所有Aspect的before⽅法
invokeBeforeAdvices(method, args);
try{
//2.执⾏被代理类的⽅法
returnValue = methodProxy.invokeSuper(proxy, args);
//3.如果被代理⽅法正常返回,则按照order的顺序降序执⾏完所有Aspect的afterReturning⽅法
returnValue = invokeAfterReturningAdvices(method, args, returnValue);
} catch (Exception e){
//4.如果被代理⽅法抛出异常,则按照order的顺序降序执⾏完所有Aspect的afterThrowing⽅法
invokeAfterThrowingAdvides(method, args, e);
}
return returnValue;
}
private void collectAccurateMatchedAspectList(Method method) {
if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;}
Iterator<AspectInfo> it = sortedAspectInfoList.iterator();
while (it.hasNext()){
AspectInfo aspectInfo = it.next();
if(!PointcutLocator().accurateMatches(method)){
}
}
}
//4.如果被代理⽅法抛出异常,则按照order的顺序降序执⾏完所有Aspect的afterThrowing⽅法
private void invokeAfterThrowingAdvides(Method method, Object[] args, Exception e) throws Throwable {
for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){
<(i).getAspectObject().afterThrowing(targetClass, method, args, e);
}
}
//3.如果被代理⽅法正常返回,则按照order的顺序降序执⾏完所有Aspect的afterReturning⽅法
private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
Object result = null;
for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){
result = (i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
}
return result;
}
//1.按照order的顺序升序执⾏完所有Aspect的before⽅法
private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
for(AspectInfo aspectInfo : sortedAspectInfoList){
}
}
}
创建代理类:
package org.simplespring.aop;
import lib.proxy.Enhancer;
import lib.proxy.MethodInterceptor;
public class ProxyCreator {
/**
* 创建动态代理对象并返回
* @param targetClass 被代理的Class对象
* @param methodInterceptor ⽅法
* @return
*/
public static Object createProxy(Class<?> targetClass, MethodInterceptor methodInterceptor){
ate(targetClass, methodInterceptor);
}
}
解析Aspect表达式并且定位被织⼊的⽬标⼯具类:
package org.simplespring.aop;
import org.ls.PointcutExpression;
import org.ls.PointcutParser;
import org.ls.ShadowMatch;
import flect.Method;
/**
* 解析Aspect表达式并且定位被织⼊的⽬标
*/
public class PointcutLocator {
/
**
* Pointcut解析器,直接给它赋值上Aspectj的所有表达式,以便⽀持对众多表达式的解析
*/
private PointcutParser pointcutParser= PointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( AllSupportedPointcutPrimitives()
);
/**
* 表达式解析器
*/
private PointcutExpression pointcutExpression;
public PointcutLocator(String expression){
this.pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}
/**
* 判断传⼊的Class对象是否是Aspect的⽬标代理类,即匹配Pointcut表达式(初筛)
*
* @param targetClass ⽬标类
* @return 是否匹配
*/
public boolean roughMatches(Class<?> targetClass){
//couldMatchJoinPointsInType⽐较坑,只能校验within
//不能校验 (execution(精确到某个类除外), call, get, set),⾯对⽆法校验的表达式,直接返回true uldMatchJoinPointsInType(targetClass);
}
/**
* 判断传⼊的Method对象是否是Aspect的⽬标代理⽅法,即匹配Pointcut表达式(精筛)
* @param method
* @return
*/
public boolean accurateMatches(Method method){
ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
if(shadowMatch.alwaysMatches()){
return true;
} else{
return false;
}
}
}
总结实现aop的主要流程:
1.定义配置标记
2.定义默认切⾯类
3.定义切⾯织⼊器
核⼼流程:
1.获取所有的切⾯类
2.遍历容器管理的类
3.筛选出匹配容器管理类的切⾯aspectlist
4.尝试进⾏Aspect的织⼊⽣成动态代理对象并替换beancontainer中原先class对应所对应的实例对象4.其他类:切⾯信息包装类/InvocationHandler实现类/创建代理实例类/解析Aspect表达式⼯具类等。参考博客地址:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论