AOP的功能分析及设计
Posted 踩踩踩从踩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP的功能分析及设计相关的知识,希望对你有一定的参考价值。
前言
本篇文章会基于在上篇文章 IOC及DI的功能设计的基础上,继续实现对bean的增强,来源于对 beanMap 会放置到容器中,做一个功能上的增强,其中最大一个增强也就是本篇文章说的AOP功能的增强。
AOP分析
advice 和 pointcuts 构成切面 。
Join points 连接点:可以被选择来进行增强的方法点。
特点
Advice、Pointcut、Weaving各自有什么特点
- 1 用户性:由用户提供增强功能逻辑代码
- 2 变化的:不同的增强需求,会有不同的逻辑
- 3 可选时机:可选择在方法功能前、后、异常时进行功能增强
- 4 多重的:同一个切入点上可以有多重增强
- 1 用户性:由用户来指定
- 2 变化的:用户可灵活指定
- 3 多点性:用户可以选择在多个点上进行增强
- 1 无侵入性,不改原类代码
- 2 在AOP框架中实现
AOP 设计
Advice设计
前置增强
- 方法本身 Method
- 方法所属的对象 Object
- 方法的参数 Object[]
后置增强
- 方法本身 Method
- 方法所属的对象 Object
- 方法的参数 Object[]
- 方法的返回值 Object
环绕增强
- 方法本身 Method
- 方法所属的对象 Object
- 方法的参数 Object[]
Advice设计
/**
* 后置增强接口
*/
public interface AfterReturningAdvice extends Advice
/**
* 方法执行完毕后的增强接口
*
* @param returnValue 方法执行完后的返回值
* @param method 被执行的方法
* @param args 方法执行参数
* @param target 执行方法的对象
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target);
在spring中引用的就是在aspectj中的。
Pointcut
- 1 用户性:由用户来指定
- 2 变化的:用户可灵活指定
- 3 多点性:用户可以选择在多个点上进行增强
com.dn.spring.aop.Girl.dbj(Boy,Time)
com.dn.spring.aop.Girl.dbj(Boy,Girl,Time)
- 某个包下的某个类的某个方法
- 某个包下的所有类中所有方法
- 某个包下的所有类中的do开头的方法
- 某个包下的以service结尾的类的中的do开头的方法
- 某个包下的及其子包下的以service结尾的类的中的do开头的方法
每部分的要求是怎样的
- 包名:有父子特点,要能模糊匹配
- 类名:要能模糊匹配
- 方法:要能模糊匹配
- 参数类型:参数可以多个
AspectJ官网:
实现
public interface Pointcut
/**
* 匹配类
* @param targetClass 将被匹配的目标类
* @return true,表示匹配规则;否则返回false。
*/
boolean matchsClass(Class<?> targetClass);
/**
* 匹配方法
* @param method 将要被匹配的方法
* @param targetClass 将要被匹配的目标类
* @return true,表示匹配规则;否则返回false。
*/
boolean matchsMethod(Method method, Class<?> targetClass);
在实现时,AspectJExpressionPointcut 上需要实现 pointcut
- 引入AspectJ的jar
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
- 掌握Aspectj的api使用,我们只使用它的切点表达式解析匹配部分。
入口:org.aspectj.weaver.tools.PointcutParser 获得切点解析器;
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
解析表达式,得到 org.aspectj.weaver.tools.PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression("execution(* com.dn.spring.samples.*.set*(..))");
pe.couldMatchJoinPointsInType(ABean.class)
Class<?> cl = ABean.class;
Method aMethod = cl.getMethod("doSomthing", null);
ShadowMatch sm = pe.matchesMethodExecution(aMethod);
System.out.println(sm.alwaysMatches());
最后得到的
public class AspectJExpressionPointcut implements Pointcut
// 先获得切点解析器
private static PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
// 切点表达式的字符串形式
private String expression;
// aspectj中的切点表达式实例pe
private PointcutExpression pe;
public AspectJExpressionPointcut(String expression)
super();
this.expression = expression;
pe = pp.parsePointcutExpression(expression);
@Override
public boolean matchsClass(Class<?> targetClass)
return pe.couldMatchJoinPointsInType(targetClass);
@Override
public boolean matchsMethod(Method method, Class<?> targetClass)
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
public String getExpression()
return expression;
都需要配置成一个bean ,给
Aspect
public interface Advisor
String getAdviceBeanName();
String getExpression();
基于切入点的
Weaving
这部分 要做的是怎么将写的增强功能 织入到具体的某个bean上面。
织入分析
aop框架如何代理增强。代理对象,bean是否增强。 返回的应该是代理实例,用户在那里去注册切面,也不能在beanfactory中去注册切面。 判断匹配、织入的逻辑 这里 这些肯定都不能在beanfactory中, 太累赘了。
让beanfactory去处理代理对象就行,为了灵活扩展框架 ,在beanfactory中去扩展,是非常不好的。
肯定大家的想法都是在beanfactory中相关扩展。
这里 也是利用 灵活扩展,设计能让我们一次写好BeanFactory后,不改代码,就可以灵活扩展
加入注册机制。 扩展点,对不同的阶段
流转过程中, spring中 就利用这样进行扩展开。
不同扩展点,进行拆分开,
针对bean初始化前后,做增强处理, advisorautoproxycreator就是进行增强的处理。
/**
* 后置处理器。
* Bean实例化完毕后及依赖注入完成后触发。
*/
public interface BeanPostProcessor
/**
* bean初始化前的处理
* @param bean
* @param beanName
* @return
* @throws Exception
*/
default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception
return bean;
/**
* bean初始化后的处理
* @param bean
* @param beanName
* @return
* @throws Exception
*/
default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception
return bean;
并且 需要 在beanfactory上面进行注入,这个需要 在 DefaultBeanFactory 中实现,
@Override
public void registerBeanPostProcessor(BeanPostProcessor bpp)
beanPostProcessors.add(bpp);
if (bpp instanceof BeanFactoryAware)
((BeanFactoryAware) bpp).setBeanFactory(this);
获取bean的类及所有方法
利用反射去 就可以判断方法是否需要进行增强。
private List<Advisor> getMatchedAdvisors(Object bean, String beanName)
if (CollectionUtils.isEmpty(advisors))
return null;
// 得到类、所有的方法
Class<?> beanClass = bean.getClass();
List<Method> allMethods = this.getAllMethodForClass(beanClass);
// 存放匹配的Advisor的list
List<Advisor> matchAdvisors = new ArrayList<>();
// 遍历Advisor来找匹配的
for (Advisor ad : this.advisors)
if (ad instanceof PointcutAdvisor)
if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods))
matchAdvisors.add(ad);
return matchAdvisors;
判断是否进行功能增强的。 并且如何找到继承的所有方法。 匹配 判断 是否是pointcutadvisor的
private List<Method> getAllMethodForClass(Class<?> beanClass)
List<Method> allMethods = new LinkedList<>();
Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
classes.add(beanClass);
for (Class<?> clazz : classes)
// 通过spring framework提供的工具类找出所有方法,包括从父类继承而来的方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method m : methods)
allMethods.add(m);
return allMethods;
/**
* 判断指定类中的方法是否有符合切点规则的。
* @param pa 方面信息,带有切点对象
* @param beanClass 指定的类
* @param methods 指定类中的所有方法
* @return
*/
private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods)
Pointcut p = pa.getPointcut();
// 首先判断类是否匹配
if (!p.matchsClass(beanClass))
return false;
// 再判断是否有方法匹配
for (Method method : methods)
if (p.matchsMethod(method, beanClass))
return true;
return false;
代理增强。
判断如何代理生成对象,增强的方法。cglib代理类,把相同的地方抽象出来。
将 代理统一封装成一个aop,把架子搭起来。
匹配的切面
/**
* AOP代理接口,用来创建获得代理对象
*/
public interface AopProxy
/**
* Create a new proxy object.
* <p>
* Uses the AopProxy's default class loader (if necessary for proxy
* creation): usually, the thread context class loader.
*
* @return the new proxy object (never @code null)
* @see Thread#getContextClassLoader()
*/
Object getProxy();
/**
* Create a new proxy object.
* <p>
* Uses the given class loader (if necessary for proxy creation).
* @code null will simply be passed down and thus lead to the low-level
* proxy facility's default, which is usually different from the default
* chosen by the AopProxy implementation's @link #getProxy() method.
*
* @param classLoader
* the class loader to create the proxy with (or @code null for
* the low-level proxy facility's default)
* @return the new proxy object (never @code null)
*/
Object getProxy(ClassLoader classLoader);
/**
* AOP代理接口的工厂模式接口
*/
public interface AopProxyFactory
/**
* 根据参数获得AOP代理接口的实现
* @param bean
* @param beanName
* @param matchAdvisors
* @param beanFactory
* @return
* @throws Exception
*/
AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
throws Exception;
/**
* 获得默认的AopProxyFactory实例
*
* @return AopProxyFactory
*/
static AopProxyFactory getDefaultAopProxyFactory()
return new DefaultAopProxyFactory();
/**
* 通过AopProxyFactory工厂去完成选择、和创建代理对象的工作。
* @param bean
* @param beanName
* @param matchAdvisors
* @return
* @throws Exception
*/
private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Exception
// 默认的代理工厂实现中获得代理工厂实现
return AopProxyFactory.getDefaultAopProxyFactory()
// 根据参数信息,选择创建代理工厂具体实现
.createAopProxy(bean, beanName, matchAdvisors, beanFactory)
// 从选择的代理工厂中,获得代理对象
.getProxy();
这都是尽量不动源码而使用的方法。
获取默认代理的方式。
public class DefaultAopProxyFactory implements AopProxyFactory
@Override
public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
throws Exception
// 是该用jdk动态代理还是cglib?
if (shouldUseJDKDynamicProxy(bean, beanName))
return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
else
return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
private boolean shouldUseJDKDynamicProxy(Object bean, String beanName)
// 如何判断?
// 这样可以吗:有实现接口就用JDK,没有就用cglib?
// 请同学们在读spring的源码时看spring中如何来判断的
return false;
在spring中使用到的代理模式,就采用这种方式。
这里将需要功能进行数据增强处理工具。
/**
* AOP代理工具类
*/
public class AopProxyUtils
/**
* 对方法应用advices增强,获得最终返回结果。
*
* @param target bean对象,需要被增强的对象
* @param method 需要被增强的方法
* @param args 增强方法的参数
* @param matchAdvisors 匹配到的切面
* @param proxy bean对象功能增强后的代理对象
* @param beanFactory ioc容器
* @return 方法增强后的返回结果
* @throws Throwable
*/
public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors,
Object proxy, BeanFactory beanFactory) throws Throwable
// 这里要做什么?
// 1、获取要对当前方法进行增强的advice Bean列表
List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors,
beanFactory);
// 2、如有增强的advice,责任链式增强执行
if (CollectionUtils.isEmpty(advices))
return method.invoke(target, args);
else
// 责任链式执行增强
AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
return chain.invoke();
/**
* 获取与方法匹配的切面的advices Bean对象列表
*
* @param beanClass
* @param method
* @param matchAdvisors
* @param beanFactory
* @return
* @throws Exception
*/
public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors,
BeanFactory beanFactory) throws Throwable
if (CollectionUtils.isEmpty(matchAdvisors))
return null;
List<Object> advices = new ArrayList<>();
for (Advisor ad : matchAdvisors)
if (ad instanceof PointcutAdvisor)
if (((PointcutAdvisor) ad).getPointcut().matchsMethod(method, beanClass))
advices.add(beanFactory.getBean(ad.getAdviceBeanName()));
return advices;
这个作为一个钩子,你需要那个 例如 beanfactory 就实现beanfactoryaware就会将beanfactory给你。持有起来。 这是spring中会拿到的。
解决强一致性的问题, 在aop中 spring框架做这个是解决的很好的。
以上是关于AOP的功能分析及设计的主要内容,如果未能解决你的问题,请参考以下文章