JAVA EE 知识总结(三)Spring AOP

目录

3.1  Spring AOP简介

3.1.1什么是AOP?

3.1.2 AOP术语

3.2  动态代理

3.2.1 JDK动态代理

3.2.2 CGLIB代理

3.3  基于代理类的AOP实现

3.3.1 Spring的通知类型

3.3.2 ProxyFactoryBean

3.4 AspectJ开发

3.4.1 基于XML的声明式AspectJ


3.1  Spring AOP简介

3.1.1什么是AOP?

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充

类与切面的关系

 

AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性

目前最流行的AOP框架有两个:Spring AOP和AspectJ。

Spring AOP:使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。

AspectJ:是一个基于Java语言的AOP框架,从spring2.0开始,Spring Aop引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供了横向代码的织入。

3.1.2 AOP术语

AOP的专业术语:

(1)Aspect(切面):封装的用于横向插入系统功能的类(如事务、日志等)。该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定。

(2)Joinpoint(连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作(例如方法的调用或异常的抛出)。在Spring AOP中,连接点就是指方法的调用

(3)Pointcut(切入点):切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名。(如:某个通知要应用到所有以add开头的方法中,那么满足这一规则的方法都是切入点)。

(4)Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法。是切面的具体实现。

(5)Target Object(目标对象):指所有被通知的对象也被称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。

(6)Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。

(7)Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

3.2  动态代理

AOP中的代理就是由AOP框架动态生成的一个对象,该对象可以作为目标对象的使用。

3.2.1 JDK动态代理

JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。JDK动态代理案例:

(1)创建web项目chapter03,导入所需jar包

(2)创建包com.jdk,在包中创建接口UserDao,并编写添加,删除方法。

public interface UserDao {
	public void addUser();
	public void deleteUser();
}

(3)创建UserDao接口的实现类UserDaoImpl

//目标类(需要对addUser()和deleteUser()方法进行增强)
public class UserDaoImpl implements UserDao{
	@Override
	public void addUser() {
		System.out.println("添加用户");
	}
	@Override
	public void deleteUser() {
		System.out.println("删除用户");
	}
}

(4)创建一个包com.aspect,并创建切面类MyAspect

/*
 * 切面类MyAspect:可以存在多个通知Advice
 * (定义一个模拟权限检查的方法 和 一个模拟记录日志的方法,这两个方法就表示切面中的通知)
 */
public class MyAspect {
	public void check_Permissions() {
		System.out.println("模拟检查权限...");
	}
	public void log() {
		System.out.println("模拟记录日志...");
	}
}

(5)在com.jdk中,创建代理类JdkProxy,该类需要实现接口InvocationHandler,并编写代理方法。

/**
 *代理类:需要实现InvocationHandler接口,并编写代理方法
 *		在代理方法中,需要通过Proxy类实现动态代理
 */
public class JdkProxy implements InvocationHandler{
	//声明目标类接口
	private UserDao userDao;
	//创建代理方法
	public Object createProxy(UserDao userDao) {
		//给已经有的userDao进行赋值
		this.userDao = userDao;
		//1.类加载器
		ClassLoader classLoader = JdkProxy.class.getClassLoader();
		//2.被代理对象实现的所有接口
		Class[] clazz = userDao.getClass().getInterfaces();
		/*
		 * 3.使用代理类,进行增强,返回的是代理后的对象
		 * 		classLoader:当前类的类加载器
		 * 		clazz:被代理对象实现的所有接口
		 * 		this:指代代理类JdkProxy本身
		 */
		return Proxy.newProxyInstance(classLoader, clazz, this);//newProxyInstance创建代理对象
	}
	/*
	 *所有动态代理类的方法调用,都会交由invoke()方法去处理
	 *proxy:被代理后的对象
	 *method:将要被执行的方法信息(反射)
	 *args:执行方法时需要的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//声明切面
		MyAspect myAspect = new MyAspect();
		//前增强
		myAspect.check_Permissions();
		//在目标类上调用方法,并传入参数
		Object obj = method.invoke(userDao, args);
		//后增强
		myAspect.log();
		return obj;
	}
}

(6)创建测试类JdkTest

public class JdkTest {
	public static void main(String[] args) {
		//创建代理对象
		JdkProxy jdkProxy = new JdkProxy();
		//创建目标对象
		UserDao userDao = new UserDaoImpl();
		//从代理对象中获取增强后的目标对象
		UserDao userDao2 = (UserDao) jdkProxy.createProxy(userDao);
		//执行方法
		userDao2.addUser();
		userDao2.deleteUser();
	}
}

3.2.2 CGLIB代理

通过前面的学习可知,JDK的动态代理用起来非常简单,但它是有局限性的,使用动态代理的对象必须实现一个或多个接口。

那么,如何代理没有实现接口的类?——CGLIB代理

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。

案例代码:

(1)创建目标类UserDao

//目标类
public class UserDao {
	public void addUser() {
		System.out.println("添加用户");
	}
	public void deleteUser() {
		System.out.println("删除用户");
	}
}

(2)创建代理类CglibProxy

//代理类
public class CglibProxy implements MethodInterceptor{

	//代理方法
	public Object createProxy(Object target) {
		//创建一个动态类对象
		Enhancer enhancer = new Enhancer();
		//确定需要增强的类,设置其父类
		enhancer.setSuperclass(target.getClass());
		//添加回调函数
		enhancer.setCallback(this);
		//返回创建的代理类
		return enhancer.create();
	}
	
	/**
	 * proxy:CGLIB根据指定父类生成的代理对象
	 * method:拦截的方法
	 * args:拦截方法的参数数组
	 * methodProxy:方法的代表的对象,用于执行父类的方法
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		//创建切面类
		MyAspect myAspect = new MyAspect();
		//前增强
		myAspect.check_Permissions();
		//目标方法执行
		Object obj = methodProxy.invokeSuper(proxy, args);
		//后增强
		myAspect.log();
		return obj;
	}
	
}

(3)创建测试类

public class CglibTest {
	public static void main(String[] args) {
		//创建代理对象
		CglibProxy cglibProxy = new CglibProxy();
		//创建目标对象
		UserDao userDao = new UserDao();
		//获取增强前的目标对象
		UserDao userDao2 = (UserDao) cglibProxy.createProxy(userDao);
		//执行方法
		userDao2.addUser();
		userDao2.deleteUser();
	}
}

3.3  基于代理类的AOP实现

3.3.1 Spring的通知类型

Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:org.springframework.aop.MethodBeforeAdvice(前置通知)     在目标方法执行前实施增强,可以应用于权限管理等功能。org.springframework.aop.AfterReturningAdvice(后置通知)     在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除     临时文件等功能。org.aopalliance.intercept.MethodInterceptor(环绕通知)     在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。org.springframework.aop.ThrowsAdvice(异常抛出通知)     在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。org.springframework.aop.IntroductionInterceptor(引介通知)     在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。

3.3.2 ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。    

ProxyFactoryBean类中的常用可配置属性如下:

环绕通知案例:

(1)创建项目,导入jar包:spring-aop-4.3.6.RELEASE.jar 和 aopalliance-1.0.jar

(2)在com.itheima.factorybean包中创建切面类MyAspect

// 切面类
public class MyAspect implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		check_Permissions();
		// 执行目标方法
		Object obj = mi.proceed();
		log();
		return obj;
	}
	public void check_Permissions(){
		System.out.println("模拟检查权限...");
	}
	public void log(){
		System.out.println("模拟记录日志...");
	}
}

 (3)在包com.itheima.factorybean中,创建配置文件applicableContext.xml,并指定代理对象

<!-- 1 目标类 -->
	<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
	<!-- 2 切面类 -->
	<bean id="myAspect" class="com.itheima.factorybean.MyAspect" />
	<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
	<bean id="userDaoProxy" 
            class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 3.1 指定代理实现的接口-->
		<property name="proxyInterfaces" 
                      value="com.itheima.jdk.UserDao" />
		<!-- 3.2 指定目标对象 -->
		<property name="target" ref="userDao" />
		<!-- 3.3 指定切面,织入环绕通知 -->
		<property name="interceptorNames" value="myAspect" />
		<!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
		<property name="proxyTargetClass" value="true" />
	</bean>

(4)在包com.itheima.factorybean中,创建测试类ProxyFactoryBeanTest

// 测试类
public class ProxyFactoryBeanTest {
	public static void main(String args[]) {
	   String xmlPath = "com/itheima/factorybean/applicationContext.xml";
	   ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
	   // 从Spring容器获得内容
	   UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
	   // 执行方法
	   userDao.addUser();
	   userDao.deleteUser();
	}
}

3.4 AspectJ开发

    AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。    使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ

3.4.1 基于XML的声明式AspectJ

    基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。<aop:config>元素及其子元素如下:

小提示:图中灰色部分标注的元素即为常用的配置元素

XML文件中常用元素的配置方式如下:

<!-- 1.配置切面Bean -->
<bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
<aop:config>
	<!-- 2.配置切面 -->
	<aop:aspect  id="aspect"  ref="myAspect">
		<!-- 3.配置切入点 -->
		<aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))" id="myPointCut" />
		<!-- 前置通知 -->
		<aop:before method="myBefore" pointcut-ref="myPointCut" />
		<!-- 后置通知 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" 
returning="returnVal" />
		<!-- 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
		<!-- 异常通知 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e" />
		<!-- 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
    </aop:aspect>
</aop:config

配置内容进行详细讲解:

1.配置切面

在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。

配置<aop:aspect>元素时,通常会指定id和ref两个属性。

2.配置切入点

当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效

在定义<aop:pointcut>元素时,通常会指定id和expression两个属性。

切入点表达式:

execution(* com.itheima.jdk.*.*(..)) 是定义的切入点表达式,该切入点表达式的意思是匹配com.jdk包中任意类的任意方法的执行。切入点表达式的基本格式:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern) throws-pattern?)

3.配置通知

使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:

 

案例代码

接下来,就通过一个案例来演示如何在Spring中使用基于XML的声明式AspectJ,具体实现步骤请参见教材3.4.1小节。

(1)导入AspectJ相关的jar包spring-aspects-4.3.6.RELEASE.jar和aspectjweaver-1.8.10.jar

(2)创建包:com.aspect.xml   创建切面类MyAspecy.java

/**
 *切面类:在此类中编写通知
 */
public class MyAspecy {
	//前置通知
	public void myBefore(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行权限检查....,");
		System.out.print("目标类是:"+joinPoint.getTarget());
		System.out.println(",被植入增强处理的目标方法:"+joinPoint.getSignature().getName());
	}
	//后置通知
	public void myAfterReturning(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行日志记录....,");
		System.out.println(",被植入增强处理的目标方法:"+joinPoint.getSignature().getName());
	}
/**
	 * 环绕通知
	 * ProceedingJoinPoint是JoinPoint的子接口,表示可以执行目标方法
	 * 	1.必须是Object类型的返回值
	 * 	2.必须接收一个参数,类型为ProceedingJoinPoint
	 * 	3.必须throws Throwable
	 */
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		//开始
		System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
		//执行当前目标方法 
		Object obj = proceedingJoinPoint.proceed();
		//结束
		System.out.println("环绕结束,执行目标方法后,模拟关闭事务...");
		return obj;
	}
	//异常通知
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
		System.out.println("异常通知:"+"出错了"+e.getMessage());
	}
	
	//最终通知
	public void myAfter() {
		System.out.println("最终通知:模拟方法结束后的释放资源...");
	}
}

 (3)在包com.aspect.xml中,创建配置文件applicationContext.xml

<!-- 1.目标类 -->
<bean id="userDao" class="com.jdk.UserDaoImpl"/>
<!-- 2.切面 -->
<bean id="myAspect" class="com.aspect.xml.MyAspecy"/>
<!-- 3.aop编程 -->
<aop:config>
	<!-- 配置切面 -->
<aop:aspect ref="myAspect">
		<!-- 3.1配置切入点,通知最后增强哪些方法 -->
			<aop:pointcut expression="execution(* com.jdk.*.*(..))" id="myPointCut"/>
			<!-- 3.2关联通知Advice和切入点pointCut -->
			<!-- 3.2.1前置通知 -->
			<aop:before method="myBefore" pointcut-ref="myPointCut"/>
			<!-- 3.2.2后置通知
			 returning属性:用于设置后置通知的第二个参数的名称,类型是Object-->
			<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" 
returning="returnVal"/>
			<!-- 3.2.3环绕通知 -->
			<aop:around method="myAround" pointcut-ref="myPointCut"/>
			<!-- 3.2.4抛出通知 -->
			<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" 
throwing="e"/>
			<!-- 3.2.5最终通知 -->
			<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>

(4)在包com.aspect.xml中,创建测试类TestXmlAspectj.java

public class TestXmlAspectj {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("com/aspect/xml/applicationContext.xml");
		// 1 从spring容器获得内容
		UserDao userDao = (UserDao) ac.getBean("userDao");
		
		userDao.addUser();
		userDao.deleteUser();
	}
}

目前整个开发社区对AOP(Aspect Oriented Programing)推崇备至,也涌现出大量支持AOP的优秀Framework,--Spring, JAC, Jboss AOP 等等。AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨,OOP还没有学通呢,又来AOP。本文不是要在理论上具体阐述何为AOP, 为何要进行AOP . 要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通过一个简单的例子向初学者展示一下如何来进行AOP.   为了简单起见,例子没有没有使用任何第方的AOP Framework, 而是利用Java语言本身自带的动态代理功能来实现AOP.   让我们先回到AOP本身,AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面。它的主要意图就要将日志记录,性能统计,安全控制等等代码从商业逻辑代码中清楚的划分出来,我们可以把这些行为一个一个单独看作系统所要解决的问题,就是所谓的面向问题的编程(不知将AOP译作面向问题的编程是否欠妥)。通过对这些行为的分离,我们希望可以将它们独立地配置到商业方法中,而要改变这些行为也不需要影响到商业方法代码。   假设系统由一系列的BusinessObject所完成业务逻辑功能,系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。 Java代码 public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } } public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } }   这里处理商业逻辑的代码和日志记录代码混合在一起,这给日后的维护带来一定的困难,并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。   按照AOP的思想,我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题,我们必须知道商业逻辑代码何时被调用,这样我们好插入日志记录代码。一般来说要截获一个方法,我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些,目前多数的AOP Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。   JDK1.2以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。   有了JDK的这种支持,我们所要做的仅仅是提供一个日志处理器。 Java代码 public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } } public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } }   客户端调用商业方法的代码如下: Java代码 BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness(); BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness();   程序输出如下: INFO: method stats... here is business logic INFO: method ends...   至此我们的第一次小尝试算是完成了。可以看到,采用AOP之后,日志记录和业务逻辑代码完全分开了,以后要改变日志记录的话只需要修改日志记录处理器就行了,而业务对象本身(BusinessObject)无需做任何修改。并且这个日志记录不会造成重复代码了,所有的商业处理对象都可以重用这个日志处理器。   当然在实际应用中,这个例子就显得太粗糙了。由于JDK的动态代理并没有直接支持一次注册多个InvocationHandler,那么我们对业务处理方法既要日志记录又要性能统计时,就需要自己做一些变通了。一般我们可以自己定义一个Handler接口,然后维护一个队列存放所有Handler, 当InvocationHandler被触发的时候我们依次调用自己的Handler。所幸的是目前几乎所有的AOP Framework都对这方面提供了很好的支持.这里推荐大家使用Spring
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条小橘猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值