首先对设计模式中的代理有比较好的认识,可以一起回忆下代理机制,这样我们就能比较好的理解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类中的所有方法 |
如果需要带上参数: