Spring中AOP设计思路是这样的.
在BeaFactory.getBean()时,调用方法initBean();
在该方法中判断当前Bean是否含有满足SpEL表达式的Method, 没有的话, 返回原始的Bean,
有的话, 会对当前Bean做动态代理,
当代理对象的方法被调用时,会发送通知到Aop拦截器,进而实现Aop的功能.
本篇文章思路接着上篇文章,
(手动模拟实现Spring-MVC)
https://blog.csdn.net/yangsnow_rain_wind/article/details/80223158
本次手动模拟Spring-Aop思路如下;
1.添加类FLAopProxy, 该类实现接口InvocationHandler, 用来实现Bean的动态代理功能
2.添加类FLAopConfig, 记录每个被代理Bean下所有满足条件的Method,并记录下其对应的aspject通知
3.修改类FLBeanWrapper
, 为该包装类添加新属性FLAopProxy
,用来记录Bean的代理配置
整个关系如下;
每个FLBeanWrapper
包含属性_originalBean(存储原始bean对象), _wrapperedBean(存储动态代理后的对象), FLAopProxy对象(用来生成动态代理).
每个FLAopProxy对象包含一个target属性, ()用来保存原始bean对象引用),包含一个FLAopConfig对象(保存Bean符合SpEl条件的method,及该method被调用时的aspect通知.)
下面是源码:
public class FLAopProxy implements InvocationHandler {
private FLAopConfig config = new FLAopConfig();
private Object target;
//对原始对象生成动态代理,
public Object getProxy(Object instance) {
this.target = instance;
Class> clazz = instance.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当前的Method是代理后的method
Method originalMethod=this.target.getClass().getMethod(method.getName(),method.getParameterTypes());
//如果动态代理对象的方法属于config中配置的方法, 则调用前置通知
if (config.containMethod(originalMethod)) {
FLAopConfig.FLAspectRealHandler handler = this.config.getPoints(originalMethod);
handler.getHanders()[0].invoke(handler.getApsHanderInstance());
}
Object result = method.invoke(target, args);
//如果动态代理对象的方法属于config中配置的方法, 则调用后置通知
if (config.containMethod(originalMethod)) {
FLAopConfig.FLAspectRealHandler handler = this.config.getPoints(originalMethod);
handler.getHanders()[1].invoke(handler.getApsHanderInstance());
}
return result;
}
}
public class FLAopConfig {
private Map points = new HashMap<>();
public FLAspectRealHandler getPoints(Method m) {
return this.points.get(m);
}
public void AddMethod(Method method, Object aspect, Method[] points) {
this.points.put(method, new FLAspectRealHandler(aspect, points));
}
public boolean containMethod(Method m) {
return this.points.containsKey(m);
}
//内部对象, 用来存储method对象及需要通知的aspject
public class FLAspectRealHandler {
private Object apsHanderInstance;
private Method[] handers;
public FLAspectRealHandler(Object apsHanderInstance, Method[] handers) {
this.apsHanderInstance = apsHanderInstance;
this.handers = handers;
}
public Object getApsHanderInstance() {
return apsHanderInstance;
}
public Method[] getHanders() {
return handers;
}
}
}
添加属性FLAopProxy
public class FLBeanWrapper {
private FLAopProxy aopProxy = new FLAopProxy();
private Object _originalBean;
private Object _wrapperedBean;
public FLBeanWrapper(Object originalBean) {
_originalBean = originalBean;
this._wrapperedBean = aopProxy.getProxy(originalBean);
}
public void setAopConfig(FLAopConfig config) {
this.aopProxy.setConfig(config);
}
}
getBean()方法中在bean实例化后, 对Bean进行包装, 并生成Bean对应的Aop配置信息
//com.FL.springlean.framework.context.FLWebApplicationContext#getBean
//getBean时, 全部验证下当前的Bean是否满足Expression的条件
public Object getBean(String beanName) {
//...省略次要代码
FLBeanWrapper beanWrapper = new FLBeanWrapper(beanInstance);
//生成aop配置信息
beanWrapper.setAopConfig(instantAopConfigByBean(beanDefinition));
}
private FLAopConfig instantAopConfigByBean(FLBeanDefinition beanDefinition) throws Exception {
FLAopConfig config = new FLAopConfig();
String expression = reader.getProperties().getProperty("pointCut");
String[] before = reader.getProperties().getProperty("aspectBefore").split("\\s");
String[] after = reader.getProperties().getProperty("aspectAfter").split("\\s");
//此处获得Bean的原始类信息
String className = beanDefinition.getBeanClassName();
Class> clazz = Class.forName(className);
Class aspectClass = Class.forName(before[0]);
Pattern pattern = Pattern.compile(expression);
//遍历当前class下所有方法, 判断该方法是否满足条件.
for (Method m : clazz.getMethods()) {
Matcher matcher = pattern.matcher(m.toString());
if (matcher.matches()) {
//满足条件的记录下来
config.AddMethod(m, aspectClass.newInstance(), new Method[]{aspectClass.getMethod(before[1]), aspectClass.getMethod(after[1])});
}
}
return config;
}
在方法com.FL.springlean.framework.webmvc.servlet.FLDispatcherServlet#initHandlerMappings
中,由于我们使用方法com.FL.springlean.framework.context.FLWebApplicationContext#getBean
返回的Bean是动态代理对象,所以在initHandlerMappings需要获取原始的Controller对象, 为此,我们要修改下该方法;
private void initHandlerMappings(FLWebApplicationContext context) {
String[] beanNames = context.getBeanDefinitions();
for (String beanName : beanNames) {
Object proxy = context.getBean(beanName);
Object controller = null;
try {
controller = FLAopProxyUtils.getTargetObject(proxy);
} catch (Exception e) {
e.printStackTrace();
}
}
//忽略掉非主要代码
}
//com.FL.springlean.framework.aop.FLAopProxyUtils
public class FLAopProxyUtils {
public static Object getTargetObject(Object proxy) throws Exception{
//先判断一下,这个传进来的这个对象是不是一个代理过的对象
//如果不是一个代理对象,就直接返回
if(!isAopProxy(proxy)){ return proxy; }
return getProxyTargetObject(proxy);
}
private static boolean isAopProxy(Object object){
return Proxy.isProxyClass(object.getClass());
}
private static Object getProxyTargetObject(Object proxy) throws Exception{
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
FLAopProxy aopProxy = (FLAopProxy) h.get(proxy);
Field target = aopProxy.getClass().getDeclaredField("target");
target.setAccessible(true);
return target.get(aopProxy);
}
}
注释: FLAopProxyUtils
为一个工具类用来判断一个对象是否为动态代理对象,并返回原始的对象.
public class LogAspect {
public void before(){
//这个是前置通知
System.out.println("Invoker Before Method!!!");
}
//这个是后置通知
public void after(){
System.out.println("Invoker After Method!!!");
}
}
pointCut=public .* com\.FL\.springlean\.userapi\.services\..*Service\..*\(.*\)
aspectBefore=com.FL.springlean.userapi.aspect.LogAspect before
aspectAfter=com.FL.springlean.userapi.aspect.LogAspect after
注释: 为了方便, 前置通知类及方法用空格隔开
如:属性aspectBefore, aop的类是”com.FL.springlean.userapi.aspect.LogAspect
“,前置通知方法是before
———-完:———————
项目源码:
https://gitee.com/yangxulong/gitee-projects