Spring入门学习初体验(2)----基于注解的AOP

本文通过具体示例介绍如何使用代理模式和AOP注解实现日志记录等功能,对比两种方式并展示其运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先对设计模式中的代理有比较好的认识,可以一起回忆下代理机制,这样我们就能比较好的理解AOP之于注解的本质上的含义。

我们首先建议一个抽象接口类定义些业务逻辑,ArithmeticCalculator.java

package com.tian;

public interface ArithmeticCalculator {
	public int add(int i , int j);
	public int sub(int i, int j);
	public int mul(int i,int j);
	public int div(int i, int j);

}

接着就是实现的比较纯净的业务逻辑:ArithmeticCalculatorImpl.java

package com.tian;

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		// TODO Auto-generated method stub
		int result = i+j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i-j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i*j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i/j;
		return result;
	}

}
接着我们就需要实现代理类了,实现对上面业务逻辑类的全面的代理,以及加入非业务逻辑的实现(验证,日志等……):MyProxy.java

<span style="color:#ff0000;">package com.tian;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyProxy {

	private ArithmeticCalculator target;
	
	
	public MyProxy(ArithmeticCalculator target) {
		super();
		this.target = target;
	}


	public ArithmeticCalculator getLoggingProxy(){
	
		ArithmeticCalculator proxy = null;
		//代理对象有哪一个加载器加载
		ClassLoader loader = target.getClass().getClassLoader();
		//代理对象类即其中有哪些方法
		Class[] interfaces = new Class[]{ArithmeticCalculator.class};	
		//当调用代理对象其中的方法时,该执行的代码
		InvocationHandler h = new InvocationHandler() {
			/**
			 * 
			* proxy:一般不使用
			 * method;调用的方法
			 * args:调用方法传入的参数 
			 * 
			 * 
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {

				String methodName= method.getName();
				
				System.out.println("the method "+methodName+" begin with "+Arrays.asList(args));
				
				
				Object resutlt = method.invoke(target, args);
				
				System.out.println("the method "+methodName+" end with"+resutlt);;
				return resutlt;
			}
		};
		
		proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
		
		return proxy;
		
	}
	
}
</span>

最后大家可以看下测试函数的代码:Main.java

package com.tian;

public class Main {

	public static void main(String[] args) {
		ArithmeticCalculator target = new ArithmeticCalculatorImpl();
		ArithmeticCalculator proxy = new MyProxy(target).getLoggingProxy();

		int result = proxy.add(1, 2);
		System.out.println("-->"+result);
		
		
		result = proxy.sub(3,1);
		System.out.println("-->"+result);
		
		
		result = proxy.mul(1, 2);
		System.out.println("-->"+result);
		
		result = proxy.div(4, 2);
		System.out.println("-->"+result);
	}

}

大家可以看下最后的运行结果:

the method add begin with [1, 2]
the method add end with3
-->3
the method sub begin with [3, 1]
the method sub end with2
-->2
the method mul begin with [1, 2]
the method mul end with2
-->2
the method div begin with [4, 2]
the method div end with2
-->2

这个例子就是比较简单的体现了代理的原理,下面我们就来是实现基于注解的AOP...

首先还是

package com.aop;

public interface ArithmeticCalculator {
	public int add(int i , int j);
	public int sub(int i, int j);
	public int mul(int i,int j);
	public int div(int i, int j);

}

package com.aop;

import org.springframework.stereotype.Service;


@Service("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		// TODO Auto-generated method stub
		int result = i+j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i-j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i*j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i/j;
		return result;
	}

}

提供通知 日志处理测的的类

package com.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
	
	/**
	 * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
	 * 使用 @Pointcut 来声明切入点表达式. 
	 * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
	 */
	@Pointcut("execution(* com.aop.ArithmeticCalculator.*(..))")
	public void declareJointPointExpression(){}
	

	@Before("declareJointPointExpression()")
	public void beforeMethod(JoinPoint jointPoint){
		String methodName = jointPoint.getSignature().getName();
		Object [] args = jointPoint.getArgs();
		System.out.println("the method:"+methodName+" begin with "+Arrays.asList(args));
	}
	
	
	/**
	 * 方法执行后的执行,无论该方法是否出现异常
	 * @param jointPoint
	 */
	@After("declareJointPointExpression()")
	public void afterMethod(JoinPoint jointPoint){
		String methodName = jointPoint.getSignature().getName();
		Object [] args = jointPoint.getArgs();
		System.out.println("the method:"+methodName+" end");
	}
	
	
	/**
	 * 在方法法正常结束受执行的代码
	 * 返回通知是可以访问到方法的返回值的!
	 */
	@AfterReturning(value="declareJointPointExpression()",
			returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends with " + result);
	}
	
	
	/**
	 * 在目标方法出现异常时会执行的代码.
	 * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
	 */
	@AfterThrowing(value="declareJointPointExpression()",
			throwing="e")
	public void afterThrowing(JoinPoint joinPoint, Exception e){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " occurs excetion:" + e);
	}
	
	/**
	 * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
	 * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
	 * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
	 */
	/*
	@Around("<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 13px; background-color: rgb(245, 245, 245);">declareJointPointExpression()</span>")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		
		Object result = null;
		String methodName = pjd.getSignature().getName();
		
		try {
			//前置通知
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
			//执行目标方法
			result = pjd.proceed();
			//返回通知
			System.out.println("The method " + methodName + " ends with " + result);
		} catch (Throwable e) {
			//异常通知
			System.out.println("The method " + methodName + " occurs exception:" + e);
			throw new RuntimeException(e);
		}
		//后置通知
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}
	*/
Spring.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
		
		
	<context:component-scan base-package="com.aop"></context:component-scan>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

测试类 main.java

package com.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {

		ApplicationContext ctx = new ClassPathXmlApplicationContext("springBean.xml");
		ArithmeticCalculator arc = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
	
		int rs = arc.add(1, 2);
		System.out.println("result:"+rs);
		
		
		rs = arc.add(1, 2);
		System.out.println("result:"+rs);
		
		
		
	}

}
运行结果:

the method:add begin with [1, 2]
the method:add end
The method add ends with 3
result:3
the method:add begin with [1, 2]
the method:add end
The method add ends with 3
result:3

------------------------------------------------------------------

---------------------------------------------------------------------

--------------------------------------------------------------------

最后我们来比较总结下:

其实各种注册通知类,本质上就是在代理处理的时候在方法执行前后的打印日志而已:

InvocationHandler h = new InvocationHandler() {
/**

* proxy:一般不使用
* method;调用的方法
* args:调用方法传入的参数  


*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {


String methodName= method.getName();

//before通知
System.out.println("the method "+methodName+" begin with "+Arrays.asList(args));

try{
Object resutlt = method.invoke(target, args);

//after通知
}catche(Exception e){

//异常通知

}

//afterReturning通知

System.out.println("the method "+methodName+" end with"+resutlt);;
return resutlt;

};


最后还有关于aop注解格式的补充

AOP的注解方式的差不多,@Before(execution (xxxxxx))


实例说明
execution(public * * (..))表示所有的方法都植入
execution(* set * (..))都植入setter方法中
execution(* com.spring.user. * (..))植入到com.spring.user包中所有类的任意方法
execution(* com.spring.user.User. * (..))植入到com.spring.user.User类中的所有方法

如果需要带上参数:



  1. //使用&&符号连接,args(参数名称)  
  1. @After("execution(public void com.spring.user.User.sayUser()) && args(message)")  




表达式代理声明(不用每次都写那么长长的字符串)
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
* 使用 @Pointcut 来声明切入点表达式. 
* 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
 @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
 public void declareJointPointExpression(){}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值