深入理解spring中的AOP-----------实现MethodInterceptor接口,自已动手写一个AOP

时间:2019-08-06
本文章向大家介绍深入理解spring中的AOP-----------实现MethodInterceptor接口,自已动手写一个AOP,主要包括深入理解spring中的AOP-----------实现MethodInterceptor接口,自已动手写一个AOP使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1.前言

   AOP是面向切面编程,即“Aspect Oriented Programming”的缩写。面对切面,就是面向我们的关注面,不能让非关注面影响到我们的关注面。而现实中非关切面又必不可少,例如获取资源、释放资源、处理异常、记录日志等,太多非关切面会让关切面的代码变得杂糅,难以维护。此时面向切面编程便应运而生,减少对非关切面的东西,让我们只专注于核心业务代码。而要理解AOP,就必须要懂得代理模式是啥,能干啥,特别要清楚Cglib动态代理是咋回事。

2.代码

    为了直观看到各个类,我将示例所需的类作为静态内部类放在外层类中。

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;

public class SpringAop {

	public static class UserDaoImpl {
		public int addUser(String user) {
			System.out.println("保存了一个用户 " + user);
			return 1;

		}

		public int deleteUser(int id) {

			if (id <= 0 || id > 99999999) {
				throw new IllegalArgumentException("参数id不能大于99999999或小于0");
			}

			System.out.println("删除了id为" + id + "用户");
			return 1;
		}
	}

	/**
	 * 自定义的方法拦截器
	 *
	 */
	public static class UserMethodInterceptor implements MethodInterceptor {

		@Override
		public Object invoke(MethodInvocation mi) throws Throwable {
			String methodName = mi.getMethod().getName();// 方法名
			Object returnVal = null;

			/*
			 * 根据方法名的不同设置不同的拦截策类 似于配置文件中的<aop:pointcut
			 * expression="execution( xxxmethodName)"/>,以此定义切入点。
			 *
			 * spring框架中可以根据方法对应的包、对应的参数、返回值、方法名等多个条件,并结合正则表达式来定义切入点
			 * 为了简单化,此处我只用方法名的前缀来定义切入点。
			 *
			 */
			if (methodName.startsWith("add")) {

				returnVal = beforeEnhance(mi);

			} else if (methodName.startsWith("delete")) {
				returnVal = afterThrowingEnhance(mi);
			} else {
				returnVal = 0;
			}

			return returnVal;
		}

		/*
		 * 前置增强策略
		 */
		private Object beforeEnhance(MethodInvocation mi) throws Throwable {
			/*
			 * spring框架通过配置文件或注解来获得用户自定义的增强方法名及对其JavaBean的类名, 然后通过反射技术动态地调用用户的增强方法。
			 * 为了简单化的测试,我这里将增强方法及对应的类给定死了、不能改动, 增强JavaBean就是ConsoloEnhancer,
			 * 增强方法就是"public void before(MethodInvocation mi)"
			 * 
			 */
			new ConsoloEnhancer().before(mi); // 调用用户定义的前置处理方式
			Object returnVal = mi.proceed(); // 执行目标方法
			return returnVal;
		}

		/*
		 * 异常处理策略
		 */
		private Object afterThrowingEnhance(MethodInvocation mi) throws Throwable {
			try {
				return mi.proceed();// 执行目标方法
			} catch (Throwable e) {
				new ConsoloEnhancer().afterThrowing(mi, e); // 调用用户定义的异常处理方式
				throw e;

			}

		}
	}

	/**
	 * 定义包含增强方法的JavaBean
	 *
	 */
	public static class ConsoloEnhancer {
		/**
		 * 前置增强方法
		 * 
		 * @param mi
		 */
		public void before(MethodInvocation mi) {
			/*
			 * 这里的MethodInvocation类型参数mi和开发中常用的切点工具类JoinPoint的作用类似,它可以获取
			 * 目标对象("Object getThis()"方法)、目标方法("Method getMethod()"方法)、
			 * 方法参数("Object[] getArguments()"方法)等信息
			 */
			System.out.println("调用了" + mi.getThis() + "对象的" + mi.getMethod().getName() + "方法。方法入参是"
					+ Arrays.toString(mi.getArguments()) + "");
			System.out.println("*********************前置增强加星号 *********************");
		}

		/**
		 * 处理异常的增强方法
		 * 
		 * @param mi
		 * @param e
		 */
		public void afterThrowing(MethodInvocation mi, Throwable e) {
			System.out.println("调用了" + mi.getThis() + "对象的" + mi.getMethod().getName() + "方法,当方法入参是"
					+ Arrays.toString(mi.getArguments()) + "时,发生了异常。");
			System.out.println("异常的堆栈信息是" + Arrays.toString(e.getStackTrace()));
			System.out.println("!!!!!!!!!!!!!!!!!!!!!异常加感叹号!!!!!!!!!!!!!!!!!!!!!!");
		}

	}

	public static void main(String[] args) {

		UserDaoImpl userDaoImpl = new UserDaoImpl();
		ProxyFactory proxyFactory = new ProxyFactory(); // 初始化一个代理工厂
		// 设置目标类,以便于Cglib工具包动态生成目标类的子类,即我们所需的代理类
		proxyFactory.setTarget(userDaoImpl);
		/*
		 * 设置拦截器,而拦截器的"public Object invoke(MethodInvocation mi)"定义了
		 * 代理类(实际是UserDaoImpl的子类)的方法生成策略。
		 * 
		 */
		proxyFactory.addAdvice(new UserMethodInterceptor());

		// 向上转型,转型为父类类型
		UserDaoImpl proxyUserDaoImpl = (UserDaoImpl) proxyFactory.getProxy();

		/**
		 * 调用代理方法
		 */
		proxyUserDaoImpl.deleteUser(2);
		System.out.println("");// 换行

		proxyUserDaoImpl.deleteUser(-1);
		/*
		 * proxyUserDaoImpl.addUser("李华");
		 */
	}
}

  

 实现过程

1)先定义一个业务类

	public static class UserDaoImpl {
		public int addUser(String user) {
			System.out.println("保存了一个用户 " + user);
			return 1;

		}

		public int deleteUser(int id) {

			if (id <= 0 || id > 99999999) {
				throw new IllegalArgumentException("参数id不能大于99999999或小于0");
			}

			System.out.println("删除了id为" + id + "用户");
			return 1;
		}
	}

 

2)自定义一个拦截器

	public static class UserMethodInterceptor implements MethodInterceptor {

		@Override
		public Object invoke(MethodInvocation mi) throws Throwable {
			String methodName = mi.getMethod().getName();// 方法名
			Object returnVal = null;

			/*
			 * 根据方法名的不同设置不同的拦截策类 似于配置文件中的<aop:pointcut
			 * expression="execution( xxxmethodName)"/>,以此定义切入点。
			 *
			 * spring框架中可以根据方法对应的包、对应的参数、返回值、方法名等多个条件,并结合正则表达式来定义切入点
			 * 为了简单化,此处我只用方法名的前缀来定义切入点。
			 *
			 */
			if (methodName.startsWith("add")) {

				returnVal = beforeEnhance(mi);

			} else if (methodName.startsWith("delete")) {
				returnVal = afterThrowingEnhance(mi);
			} else {
				returnVal = 0;
			}

			return returnVal;
		}

		/*
		 * 前置增强策略
		 */
		private Object beforeEnhance(MethodInvocation mi) throws Throwable {
			/*
			 * spring框架通过配置文件或注解来获得用户自定义的增强方法名及对其JavaBean的类名, 然后通过反射技术动态地调用用户的增强方法。
			 * 为了简单化的测试,我这里将增强方法及对应的类给定死了、不能改动, 增强JavaBean就是ConsoloEnhancer,
			 * 增强方法就是"public void before(MethodInvocation mi)"
			 * 
			 */
			new ConsoloEnhancer().before(mi); // 调用用户定义的前置处理方式
			Object returnVal = mi.proceed(); // 执行目标方法
			return returnVal;
		}

		/*
		 * 异常处理策略
		 */
		private Object afterThrowingEnhance(MethodInvocation mi) throws Throwable {
			try {
				return mi.proceed();// 执行目标方法
			} catch (Throwable e) {
				new ConsoloEnhancer().afterThrowing(mi, e); // 调用用户定义的异常处理方式
				throw e;

			}

		}
	}

 

3) 用户定义包含增强方法的JavaBean

	public static class ConsoloEnhancer {
		/**
		 * 前置增强方法
		 * 
		 * @param mi
		 */
		public void before(MethodInvocation mi) {
			/*
			 * 这里的MethodInvocation类型参数mi和开发中常用的切点工具类JoinPoint的作用类似,它可以获取
			 * 目标对象("Object getThis()"方法)、目标方法("Method getMethod()"方法)、
			 * 方法参数("Object[] getArguments()"方法)等信息
			 */
			System.out.println("调用了" + mi.getThis() + "对象的" + mi.getMethod().getName() + "方法。方法入参是"
					+ Arrays.toString(mi.getArguments()) + "");
			System.out.println("*********************前置增强加星号 *********************");
		}

		/**
		 * 处理异常的增强方法
		 * 
		 * @param mi
		 * @param e
		 */
		public void afterThrowing(MethodInvocation mi, Throwable e) {
			System.out.println("调用了" + mi.getThis() + "对象的" + mi.getMethod().getName() + "方法,当方法入参是"
					+ Arrays.toString(mi.getArguments()) + "时,发生了异常。");
			System.out.println("异常的堆栈信息是" + Arrays.toString(e.getStackTrace()));
			System.out.println("!!!!!!!!!!!!!!!!!!!!!异常加感叹号!!!!!!!!!!!!!!!!!!!!!!");
		}

	}

 

4)效果测试

	public static void main(String[] args) {

		UserDaoImpl userDaoImpl = new UserDaoImpl();
		ProxyFactory proxyFactory = new ProxyFactory(); // 初始化一个代理工厂
		// 设置目标类,以便于Cglib工具包动态生成目标类的子类,即我们所需的代理类
		proxyFactory.setTarget(userDaoImpl);
		/*
		 * 设置拦截器,而拦截器的"public Object invoke(MethodInvocation mi)"定义了
		 * 代理类(实际是UserDaoImpl的子类)的方法生成策略。
		 * 
		 */
		proxyFactory.addAdvice(new UserMethodInterceptor());

		// 向上转型,转型为父类类型
		UserDaoImpl proxyUserDaoImpl = (UserDaoImpl) proxyFactory.getProxy();

		/**
		 * 调用代理方法
		 */
		proxyUserDaoImpl.deleteUser(2);
		System.out.println("");// 换行

		proxyUserDaoImpl.deleteUser(-1);
		/*
		 * proxyUserDaoImpl.addUser("李华");
		 */
	}

  

 

$flag 上一页 下一页