TeaFramework - Implementation of AOP

    AOP is a method of interception to do corresponding weaving, so first define several kinds of notifications: pre-notification, post-notification, exception notification, end notification, the code is as follows:

public interface AopAdvice {

	public void before(Proxy proxy);

	public void after(Proxy proxy);

	public void exception(Proxy proxy);

	public void end(Proxy proxy);
}

    Next, which methods in which classes should be intercepted? We need to define an expression, so using regular expressions is relatively simple, and it is also a general specification. Enhancements corresponding to weaving will only be performed if a regular method is assigned.

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {

	public String classRegex();

	public String beforeRegex() default "";

	public String afterRegex() default "";

	public String exceptionRegex() default "";

	public String endRegex() default "";

	public int order() default 0;
}

    classRegex defines the class regular to be intercepted

    beforeRegex defines regular matching for methods that require pre-enhancement

    afterRegex defines regular matching for methods that require post-enhancement

    exceptionRegex defines regular matches that require exception handling enhancement methods

    endRegex defines a regular match that requires additional processing of the method after the method execution is complete

    order means sorting. When multiple aop rules are hit, sorting is required. Here is an ascending order. The larger the order, the later the aop will be executed.

    The essence of AOP implementation is dynamic proxy, then we need to define a proxy interface

public interface Proxy {

	public void invoke(Proxy proxy) throws Throwable;
}

    This interface defines a method invoke(Proxy proxy), the parameter is itself, so what is defined like this? Here we will talk about a design pattern "chain of responsibility". Since the class that has been proxied by cglib cannot be proxied again, the chain of responsibility pattern is used here to solve it. Let's see how the chain of responsibility works here.

public class BeanProxy implements Proxy {

	private Object obj;
	private Object[] args;
	private Method method;
	private MethodProxy methodProxy;
	private boolean invokeSuper = true;
	private int proxyIndex = -1;
	private Object result = null;
	private InterfaceExecutor interfaceExecutor;

	private List<Proxy> proxyList = new ArrayList<Proxy>();

	public BeanProxy() {
	}

	public BeanProxy(Object obj, Object[] args, Method method, MethodProxy methodProxy) {
		this.obj = obj;
		this.args = args;
		this.method = method;
		this.methodProxy = methodProxy;
	}

	@Override
	public void invoke(Proxy proxy) throws Throwable {
		proxyIndex++;
		if (proxyIndex < proxyList.size()) {
			proxyList.get(proxyIndex).invoke(this);
		} else if (invokeSuper) {
			result = methodProxy.invokeSuper(obj, args);
		} else {
			result = interfaceExecutor.invoke(obj, method, args, methodProxy);
		}
	}

	public BeanProxy addProxyChain(Proxy proxy) {
		proxyList.add(proxy);
		return this;
	}

	public BeanProxy setInterfaceExecutor(InterfaceExecutor interfaceExecutor) {
		this.interfaceExecutor = interfaceExecutor;
		return this;
	}

………………省略了一些set方法
}

    BeanProxy implements the invoke method. The upstream node will call the downstream node until the pointer points to the end of the chain. When the pointer points to the end of the chain, if the set invokeSuper is true (invokeSuper indicates whether to execute the proxied parent method), then Execute the parent class method. If the parent class method is an interface, it must be set to false. In "TeaFramework - Implementation of ORM Framework (1)", I left a question about InterfaceExecutor. InterfaceExecutor is specially prepared for the scenario where the class to be proxied is an interface. When the class to be proxied is an interface, but I want to execute some originally intended The additional operations implemented can be implemented with InterfaceExecutor. For example, OrmProxy in ORM is a typical example.

    Special note: For chained calls, the method before the invoke method is called in positive order, and the method after the invoke method is called in reverse order. This is consistent with all chain of responsibility calling processes, such as Filter, Struts2 interception, etc.

    For AOP proxy, TeaFramework implements an abstract proxy class. To write your own AOP, you only need to inherit this abstract class and implement the corresponding pre- and post-processing methods.

public abstract class AbstractProxy implements Proxy, AopAdvice {

	private Pattern beforePattern;
	private Pattern afterPattern;
	private Pattern exceptionPattern;
	private Pattern endPattern;

	@Override
	public void invoke(Proxy proxy) throws Throwable {
		BeanProxy beanProxy = (BeanProxy) proxy;
		try {
			if (beforePattern != null && beforePattern.matcher(beanProxy.getMethod().getName()).find()) {
				before(proxy);
			}
			proxy.invoke(proxy);
			if (afterPattern != null && afterPattern.matcher(beanProxy.getMethod().getName()).find()) {
				after(proxy);
			}
		} catch (Throwable e) {
			if (exceptionPattern != null && exceptionPattern.matcher(beanProxy.getMethod().getName()).find()) {
				exception(proxy);
			}
			throw e;
		} finally {
			if (endPattern != null && endPattern.matcher(beanProxy.getMethod().getName()).find()) {
				end(beanProxy);
			}
		}
	}

	@Override
	public void before(Proxy proxy) {
	}

	@Override
	public void after(Proxy proxy) {
	}

	@Override
	public void exception(Proxy proxy) {
	}

	@Override
	public void end(Proxy proxy) {
	}

	public final void setBeforePattern(Pattern beforePattern) {
		this.beforePattern = beforePattern;
	}

	public final void setAfterPattern(Pattern afterPattern) {
		this.afterPattern = afterPattern;
	}

	public final void setExceptionPattern(Pattern exceptionPattern) {
		this.exceptionPattern = exceptionPattern;
	}

	public void setEndPattern(Pattern endPattern) {
		this.endPattern = endPattern;
	}

}

    With these tools, we can implement our own AOP business. Let's take an example. For example, for new additions and modifications, we need to fill in the CCUU value. At this time, we can use AOP. Please see the following code.

@Aspect(classRegex = "org.teaframework.erp.*.dao.*", beforeRegex = "add.*|update.*")
public class DomainAspect extends AbstractProxy {

	@Override
	public void before(Proxy proxy) {
		BeanProxy beanProxy = (BeanProxy) proxy;
		if (beanProxy.getArgs() != null && beanProxy.getArgs()[0] instanceof BaseDomain) {
			BaseDomain domain = (BaseDomain) beanProxy.getArgs()[0];
			domain.setCreateDate(new Date());
			domain.setCreateUser(ThreadVariable.getUser().getUserName());
			domain.setUpdateDate(new Date());
			domain.setUpdateUser(ThreadVariable.getUser().getUserName());
		}
	}
}

    classRegex defines the classes to be intercepted in all dao packages, beforeRegex defines specific interception methods at the beginning of add or upate for pre-enhancement weaving, and rewrites the before method of AbstractProxy to achieve pre-enhancement.

    The next question is, how are the AOP classes implemented by DomainAspect and other self-implemented classes associated with the beans of the bean container? Let's see.

    When the framework starts, there is an AopBeanInitialization, which scans all Aspect-annotated classes, instantiates them, sorts them in ascending order, and puts them in a Map.

public class AopBeanInitialization implements Initialization {

	public static final List<AspectClassMapping> AOP_BEAN_LIST = new ArrayList<AspectClassMapping>();
	public static final Map<Class<?>, Pattern> PATTERN_MAPPING = new HashMap<Class<?>, Pattern>();

	@Override
	public void init() throws Exception {
		for (String classPath : ScanPackageInitialization.classPaths) {// aop
			Class<?> clazz = ClassLoaderUtil.loadClass(classPath);
			if (clazz.isAnnotationPresent(Aspect.class)) {
				AbstractProxy aspectBean = (AbstractProxy) clazz.newInstance();
				Aspect aspect = clazz.getAnnotation(Aspect.class);
				PATTERN_MAPPING.put(clazz, Pattern.compile(aspect.classRegex()));
				if (!"".equals(aspect.beforeRegex())) {
					aspectBean.setBeforePattern(Pattern.compile(aspect.beforeRegex()));
				}
				if (!"".equals(aspect.afterRegex())) {
					aspectBean.setAfterPattern(Pattern.compile(aspect.afterRegex()));
				}
				if (!"".equals(aspect.exceptionRegex())) {
					aspectBean.setExceptionPattern(Pattern.compile(aspect.exceptionRegex()));
				}
				if (!"".equals(aspect.endRegex())) {
					aspectBean.setEndPattern(Pattern.compile(aspect.endRegex()));
				}
				AOP_BEAN_LIST.add(new AspectClassMapping(clazz, aspectBean, aspect.order()));
			}
		}
		Collections.sort(AOP_BEAN_LIST, new Comparator<AspectClassMapping>() {
			public int compare(AspectClassMapping o1, AspectClassMapping o2) {
				return o2.getOrder() - o1.getOrder();
			}
		});
	}
}

    After the execution of AopBeanInitialization is completed, BeanContainerInitialization starts to execute. For classes that hit the regular rules, dynamic proxy is required, and then the corresponding AOP proxy object is added to the proxy chain. Please see the code below.

private void addProxy(BeanProxy beanProxy, Class<?> clazz) {
		for (AspectClassMapping aspectClassMapping : AopBeanInitialization.AOP_BEAN_LIST) {
			Class<?> proxyClass = aspectClassMapping.getClazz();
			Matcher matcher = AopBeanInitialization.PATTERN_MAPPING.get(proxyClass).matcher(clazz.getName());
			if (matcher.find()) {
				beanProxy.addProxyChain(aspectClassMapping.getAspectBean());
			}
		}
		if (clazz.isAnnotationPresent(Transcation.class)) {
			beanProxy.addProxyChain(transcationProxy);
		} else {
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (method.isAnnotationPresent(Transcation.class)) {
					beanProxy.addProxyChain(transcationProxy);
					break;
				}
			}
		}
	}

    Once the AOP proxy object is put into the corresponding proxy chain, it can be called in a chain, and it will play the role of AOP. The above code also has the code about transaction Transcation. This blog will not introduce it first, there will be A separate blog on the design of thing control. 

 Project address: https://git.oschina.net/lxkm/teaframework
 Blog: https://my.oschina.net/u/1778239/blog 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325438215&siteId=291194637