一个建议版本的Spring实现

时间:2021-08-31
本文章向大家介绍一个建议版本的Spring实现,主要包括一个建议版本的Spring实现使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

XML 切点切割放大缩小字体功能 放大缩小字体功能

@Component 订单服务,包括:

/**
* @author mghio
* @since 2021-06-06
*/
@Component(value = "orderService")
public class OrderService {

public void placeOrder() {
System.out.println("place order");
}

}

, 切口位置 ()

<aop:pointcut id="placeOrder" expression="execution(* cn.mghio.service.version5.*.placeOrder(..))"/>

我们需要一个类去表达这个概念,pointcut 要实现的功能是给定一个类的方法,判断是否匹配配置文件中给定的表达式。总的来看 pointcut 由方法匹配器和匹配表达式两部分组成,方法匹配器可以有各种不同的实现,所以是一个接口,pointcut 同样也可以基于多种不同技术实现,故也是一个接口,默认是基于 AspectJ 实现的,类图结构如下:

 前卫

/**
* @author mghio
* @since 2021-08-06
*/
public class AspectJExpressionPointcut implements Pointcut, MethodMatcher {

private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();

static {
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
}

private String expression;
private ClassLoader pointcutClassLoader;
private PointcutExpression pointcutExpression;

@Override
public MethodMatcher getMethodMatcher() {
return this;
}

@Override
public String getExpression() {
return expression;
}

@Override
public boolean matches(Method method) {
checkReadyToMatch();

ShadowMatch shadowMatch = getShadowMatch(method);
return shadowMatch.alwaysMatches();
}

private void checkReadyToMatch() {
if (Objects.isNull(getExpression())) {
throw new IllegalArgumentException("Must set property 'expression' before attempting to match");
}
if (Objects.isNull(this.pointcutExpression)) {
this.pointcutClassLoader = ClassUtils.getDefaultClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}

private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
PointcutParser pointcutParser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, classLoader);
return pointcutParser.parsePointcutExpression(replaceBooleanOperators(getExpression()));
}

private String replaceBooleanOperators(String pcExpr) {
String result = StringUtils.replace(pcExpr, " and ", " && ");
result = StringUtils.replace(result, " or ", " || ");
result = StringUtils.replace(result, " not ", " ! ");
return result;
}

private ShadowMatch getShadowMatch(Method method) {
ShadowMatch shadowMatch;
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(method);
} catch (Exception e) {
throw new RuntimeException("not implemented yet");
}
return shadowMatch;
}

// omit other setter、getter ...

}

到这里就完成了给定一个类的方法,判断是否匹配配置文件中给定的表达式的功能。

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.e3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/beans/spring-context.xsd">

<context:scann-package base-package="cn.mghio.service.version5,cn.mghio.dao.version5" />

<bean id="tx" class="cn.mghio.tx.TransactionManager"/>

<aop:config>
<aop:aspect ref="tx">
<aop:pointcut id="placeOrder" expression="execution(* cn.mghio.service.version5.*.placeOrder(..))"/>
<aop:before pointcut-ref="placeOrder" method="start"/>
<aop:after-returning pointcut-ref="placeOrder" method="commit"/>
<aop:after-throwing pointcut-ref="placeOrder" method="rollback"/>
</aop:aspect>
</aop:config>
</beans>

在实现各种 XXXAdvice 之前需要定位到这个 Method,比如以上配置文件中的 start、commit、rollback 等方法,为了达到这个目标我们还需要实现的功能就是根据一个 Bean 名称(比如这里的 tx)定位到指定的 Method,然后通过反射调用这个定位到的方法。实际上也比较简单,这个类命名为 MethodLocatingFactory,根据其功能可以定义出目标 Bean 的名称 targetBeanName、需要定位的方法名称 methodName 以及定位完成后得到的方法 method 这三个属性,整体类图结构如下所示:

 根据名称和类型定位到方法主要是在 setBeanFactory() 方法中完成的,前提是对应的目标 Bean 名称和方法名称要设置完成,方法定位的类 MethodLocatingFactory 类的代码如下所示:

/**
* @author mghio
* @since 2021-06-06
*/
public class MethodLocatingFactory implements FactoryBean<Method>, BeanFactoryAware {

private String targetBeanName;

private String methodName;

private Method method;

public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
}

public void setMethodName(String methodName) {
this.methodName = methodName;
}

@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!StringUtils.hasText(this.targetBeanName)) {
throw new IllegalArgumentException("Property 'targetBeanName' is required");
}
if (!StringUtils.hasText(this.methodName)) {
throw new IllegalArgumentException("Property 'methodName' is required");
}

Class<?> beanClass = beanFactory.getType(this.targetBeanName);
if (Objects.isNull(beanClass)) {
throw new IllegalArgumentException("Can't determine type of bean with name '" + this.targetBeanName);
}

this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
if (Objects.isNull(this.method)) {
throw new IllegalArgumentException("Unable to locate method [" + this.methodName + "] on bean ["
+ this.targetBeanName + "]");
}
}

@Override
public Method getObject() {
return this.method;
}

@Override
public Class<?> getObjectType() {
return Method.class;
}
}

原文地址:https://www.cnblogs.com/xiaohe0012/p/15209051.html