public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
实现类(在实现类中加入具体方法的实现,即正数的操作和日志功能,通过System.out.println输出实现):
public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
System.out.println("The method add begins with [" + i + "," + j + "]");
int result = i + j;
System.out.println("The method add ends with " + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("The method sub begins with [" + i + "," + j + "]");
int result = i - j;
System.out.println("The method sub ends with " + result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("The method mul begins with [" + i + "," + j + "]");
int result = i * j;
System.out.println("The method mul ends with " + result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("The method div begins with [" + i + "," + j + "]");
int result = i / j;
System.out.println("The method div ends with " + result);
return result;
}
}
/**
* 动态代理
* @author Megustas
*
*/
public class ArithmeticCalculatorLoggingProxy {
//要代理的对象
private ArithmeticCalculator target;
public ArithmeticCalculatorLoggingProxy(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:正在返回的那个代理对象, 一般情况下,在invoke方法中不使用该对象
* method: 正在被调用的方法
* args: 调用方法传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
//打印日志,此种方式对日志进行维护和更改就十分简洁
System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
//调用目标方法
Object result = null;
try {
//前置通知
//invoke:通过函数名反射相应的函数
result = method.invoke(target, args);
//返回通知, 可以访问到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
}
//后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
//打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
};
/**
* loader: 代理对象使用的类加载器。
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
* 以下是代理对象,不同于基本对象通过new生成
*/
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
但是写动态代理,难度却不小,不是很容易掌握。
AOP 的好处: 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级 业务模块更简洁, 只包含核心业务代码.
这里的一个个需求,例如验证参数、日志功能等都是横切关注点,我们将横切关注点抽取出来
首先介绍一个AOP框架,AspectJ,是Java社区里最完整最流行的AOP框架,在Spring2.0以上版本中,可以使用基于AspectJ注解来配置AOP。
要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar,即导入包:
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
将 aop Schema 添加到 <beans>
根元素中.
<aop:aspectj-autoproxy>
<aop:aspectj-autoproxy>
元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类. 通知是标注有某种注解的简单的 Java 方法.
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
/**
* 日志切面
* @author Megustas
*
*/
//把这个类声明为一个切面:首先需要把该类放入到IOC容器中,通过注解@Component、再声明为一个切面,通过注解@Aspect,并且在配置文件中加入配置
//通过Order注解来指定切面的优先级,优先级数字越小代表优先级越高,越先执行
@Order(2)
@Aspect
@Component
public class LoggingAspect {
//这个方法在哪些类的哪些方法前执行,通过注解来规定
//声明该方法是一个前置通知,在目标方法开始之前执行,".add"方法说明在add方法之前执行,".*"则表示在包下所有方法之前执行
@Before("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int, int))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + args);
}
//后置通知:在目标方法执行后(无论是否发生异常),执行的通知
//在后置通知中还不能访问目标目标方法执行的结果,执行结果在返回通知中进行访问
@After("execution(* com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.*(int, int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " end ");
}
/*
* 返回通知:在方法正常结束后执行的代码,返回通知是可以访问到方法的返回值的
*/
@AfterReturning(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int, int))",
returning="result")
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码
* 可以访问到异常对象,且可以指定在出现特定异常时再执行通知代码
* 例如Exception ex,NullPointerException ex,可以指定不同种类的异常,当是指定的异常种类时执行
* @param joinPoint
* @param ex
*/
@AfterThrowing(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.div(int, int))",
throwing="ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion " + ex);
}
/**
* 环绕通知需要携带ProceedingJoinPoint类型的参数
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
* 并且环绕通知必须有返回值,返回值即为目标方法的返回值(类似于动态代理)
* 最强的,前置、后置、返回与异常通知都可以,但是并不代表是最常用的
* @param pjd
*/
@Around("execution(* com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int, int))")
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 excetion " + e);
}
//后置通知
System.out.println("The method " + methodName + " ends" );
return result;
}
}
Bean的配置:
<!-- 自动扫描的包 -->
<context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>
<!-- 使 AspectJ 的注解起作用 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
验证方法:
public class Main {
public static void main(String[] args) {
//1. 创建 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从 IOC 容器中获取 bean 实例
ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator)ctx.getBean(ArithmeticCalculator.class);
//3. 调用 bean 的方法
int result1 = arithmeticCalculator.add(3, 6);
System.out.println("result1:" + result1);
//int result2 = arithmeticCalculator.div(1000, 0);//通过异常通知显示
//System.out.println("result2:" + result2);
//4. 关闭容器
ctx.close();
}
}
实例可以参照上诉代码的@Order
@Aspect
@Order(0)
public class CalculaotorValidationAspect{}
@Aspect
@Order(1)
public class CalculaotorLoggingAspect{}
/**
* 实现重用切面表达式
* 定义一个方法,用于声明切入点表达式。一般该方法中不需要再填入其他的代码
* 使用@Pointcut来声明切入点表达式
*/
@Pointcut("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int, int))")
public void declareJointPointExpression(){
}
@Before("declareJointPointExpression()")
public void beforeMethod1(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + args);
}
在外部类中使用:
//在类的外部使用重用切面表达式,再指定其所在类即可,若不同包下,则再引入包名
@Before("LoggingAspect.declareJointPointExpression()")
public void validateArgs1(JoinPoint joinPoint){
System.out.println("-->validate: " + Arrays.asList(joinPoint.getArgs()));
}
在外部包使用同理。
<aop:config>
元素内部. 对于每个切面而言, 都要创建一个<aop:aspect>
元素来为具体的切面实现引用后端 Bean 实例. <aop:aspect>
元素引用<aop:pointcut>
元素声明<aop:aspect>
元素下: 只对当前切面有效 <aop:config>
元素下: 对所有切面都有效<pointcut-ref>
来引用切入点, 或用 <pointcut>
直接嵌入切入点表达式. method 属性指定切面类中通知方法的名称.具体的一个实例如下(注:此处bean代码的实现不再具体列举):
<!-- 配置bean,里面包含业务逻辑 -->
<bean id="arithmeticCalculator" class="com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl"></bean>
<!-- 配置切面的bean -->
<bean id="LoggingAspect" class="com.atguigu.spring.aop.impl.LoggingAspect"></bean>
<bean id="vlidationAspect" class="com.atguigu.spring.aop.impl.VlidationAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="* com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int)" id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="LoggingAspect" order="2">
<!--
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after method="afterMethod" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterReturning" pointcut="pointcut"/>
<aop:after-returning method="afterReturning" pointcut="pointcut"/>
-->
<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
</aop:aspect>
<aop:aspect ref="vlidationAspect" order="1">
<aop:before method="validateArgs" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
原文:http://blog.csdn.net/megustas_jjc/article/details/54617902