TeaFramework——AOP的实现

    AOP是拦截的方法后要做对应的织入,那么先定义几种通知:前置通知、后置通知、异常通知、结束通知,代码如下:

public interface AopAdvice {

	public void before(Proxy proxy);

	public void after(Proxy proxy);

	public void exception(Proxy proxy);

	public void end(Proxy proxy);
}

    接下来看,哪些类里的哪些方法要被拦截呢?我们需要定义一个表达式,这么使用正则表达式,相对来说比较简单,也是一个通用的规范。只有配到正则的方法,才会执行对应织入的增强。

@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定义要拦截的class正则

    beforeRegex定义需要前置增强的方法的正则匹配

    afterRegex定义需要后置增强的方法的正则匹配

    exceptionRegex定义需要进行异常处理增强方法的正则匹配

    endRegex定义在方法执行完成之后需要额外处理方法的正则匹配

    order表示排序,有多个aop规则命中时,就需要排序,这里是升序排列,order越大,该aop越靠后执行

    AOP实现的本质是动态代理,那么我们需要定义一个代理接口

public interface Proxy {

	public void invoke(Proxy proxy) throws Throwable;
}

    这个接口里定义了一个方法invoke(Proxy proxy),参数是它自己,那么什么这样定义呢?这里就要谈到一种设计模式“责任链”,由于被cglib代理过的类不能再次被代理,所以这里才用责任链模式来解决。具体来看责任链在这里怎么运作的。

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实现了invoke方法,上游节点将调用权给下游节点,直到指针指向链的末尾,当指针指向链尾端是,如果设置的invokeSuper为true(invokeSuper表示是否执行被代理的父类方法),则执行父类方法,如果父类方法是接口,则一定要设置为false。在《TeaFramework——ORM框架的实现(一)》留了一个InterfaceExecutor的疑问,InterfaceExecutor是专门为被代理的类是接口的场景准备的,当被代理的类为接口,但是又想执行一些原本想实现的额外操作,就可以用InterfaceExecutor来实现,例如ORM中OrmProxy就是一个典型的例子。

    特别说明:对于链式调用,invoke方法之前的方法是正序调用,invoke方法之后是逆序。这一点和所有责任链调用过程一致,例如Filter、Struts2的拦截等。

    对于AOP代理,TeaFramework中实现了一个抽象代理类,要编写自己的AOP只需继承这个抽象类,实现对应的前置、后置等方法。

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;
	}

}

    有了这些工具,我们就可以实现自己的AOP业务了,看个例子,比方说对应新增和修改而言,我们需要把CCUU值填充进去,这个时候就可以用AOP了,请看下面的代码

@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定义了要拦截所有dao包里的类,beforeRegex定义具体拦截add或者upate开头的方法进行前置增强织入,重写AbstractProxy的before方法即可实现前置增强了。

    接下来问题来了,DomainAspect等自己实现的AOP类,怎么和bean容器的bean关联起来的呢?我们接着看。

    框架启动时,有一个AopBeanInitialization,这个类将所有加了Aspect注解的类扫描到,然后实例化,根据order升序排列,放到一个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();
			}
		});
	}
}

    AopBeanInitialization执行完成之后,BeanContainerInitialization开始执行,对于命中了正则规则的类就需要动态代理了,然后将对应的AOP代理对象add到代理链中,请看下来的代码

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;
				}
			}
		}
	}

    一旦AOP代理对象被放入对应的代理链中,就可以链式调用了,便发挥出了AOP的作用,上面的代码还有关于事物Transcation的代码,本篇博客先不做介绍,将会有单独的一篇博客讲事物控制的设计。 

 项目地址:https://git.oschina.net/lxkm/teaframework
 博客:https://blog.csdn.net/dong_lxkm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP是Spring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值