Spring—— AOP 的引子
动态代理模式
package com.baidu.aop.loggingProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import com.baidu.aop.helloworld.ArithmeticCalculator;
public class ArithmeticCalculatorloggingProxy {
//要代理的对象
private ArithmeticCalculator target ;
public ArithmeticCalculatorloggingProxy(ArithmeticCalculator target) {
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 {
// proxy.toString();
String methodName = method.getName();
//日志
System.out.println("The method " + methodName + "begins with " + Arrays.asList(args));
//执行方法
Object result = null;
try {
//前置通知
result = method.invoke(target, args);
//返回通知,可以访问到方法的返回值
} catch (Exception e) {
e.printStackTrace();
//异常通知: 可以访问到方法出现的异常
}
//后置通知:因为方法可能会出异常,所以访问不到方法的返回值
//日志
System.out.println("The method " + methodName + "ends with " +result);
return result;
}
};
proxy = (ArithmeticCalculator)Proxy.newProxyInstance(loader,interfaces,h);
return proxy;
}
}
测试方法:
package TestSpringAOP;
import com.baidu.aop.helloworld.ArithmeticCalculator;
import com.baidu.aop.helloworld.ArithmeticCalculatorImpl;
import com.baidu.aop.loggingProxy.ArithmeticCalculatorloggingProxy;
public class TestSpringLoggingProxy {
public static void main(String[] args) {
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorloggingProxy(target).getLoggingProxy();
int result = proxy.add(1, 2);
System.out.println("--> " + result);
result = proxy.div(4, 2);
System.out.println("--> " + result);
}
}
运行结果:
The method addbegins with [1, 2]
The method addends with 3
--> 3
The method divbegins with [4, 2]
The method divends with 2
--> 2
~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~示例如下~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
接口 ArithmeticCalculator.java
package com.baidu.aop.impl;
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);
}
接口的实现类 ArithmeticCalculatorImpl.javapackage com.baidu.aop.impl;
import org.springframework.stereotype.Component;
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
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;
}
}
切面
LoggingAspect.java
package com.baidu.aop.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* ①. 使用@Aspect 可以把一个类声明为一个切面:
* ②. 需要把该类放入到IOC容器中 @Component
* ③. 使用Order(value) 可以指定切面的优先级,值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 使用 @Pointcut 把一个方法声明为切入点表达式。一般的,该方法中再不需要添入其他的代码,
*
* 此方法的作用就是简各个通知,使用方法:
* ①. 本类中: @Before("方法名") @Before("declareJoinPointExpression()")
* ②. 本包中: @Before("类名.方法名") @Before("LoggingAspect.declareJoinPointExpression()")
* ③. 外包中: @Before("包名.类名.方法名") @Before("com.baidu.aop.impl.LoggingAspect.declareJoinPointExpression()")
*/
@Pointcut("execution(int com.baidu.aop.impl.ArithmeticCalculator.*(..))")
public void declareJoinPointExpression() {}
/**
* 声明该方法是一个前置通知:在目标方法开始之前执行
* @Before("execution(int com.baidu.aop.impl.ArithmeticCalculator.*(..))")
*/
@Before("declareJoinPointExpression()")
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(int com.baidu.aop.impl.ArithmeticCalculator.*(..))")
*/
@After("declareJoinPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends" );
}
/**
* 返回通知:在方法正常结束后执行的代码 。返回通知是可以访问到方法的返回值的
* @AfterReturning(value="execution(int com.baidu.aop.impl.ArithmeticCalculator.*(..))" ,returning="result")
*/
@AfterReturning(value="declareJoinPointExpression()" ,returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result );
}
/**
* 异常通知:在目标方法出现异常时才执行的代码.其 可以访问到异常对象,且可以指定在出现那种异常时,才执行通知代码
* 这里 的Exception 可以写为NullPointerException:表示只有在出现空指针异常时才执行
* @AfterThrowing(value="execution(int com.baidu.aop.impl.ArithmeticCalculator.*(..))" ,throwing="e")
*/
@AfterThrowing(value="declareJoinPointExpression()" ,throwing="e")
public void afterThrowing(JoinPoint joinPoint,NullPointerException e) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion: " + e );
}
// /**
// * 环绕通知: 其是通知中最强的,但不是最常用的
// * ①. 其需要携带ProceedingJoinPoint 类的参数
// * ②. 其必须有返回值,返回值即为目标方法的返回值
// *
// * 其类似于动态代理的全过程: ProceedingJoinPoint 类型的参数,可以决定是否执行目标方法
// *
// * @param pjd
// */
// @Around("execution(int com.baidu.aop.impl.ArithmeticCalculator.*(..))")
// public Object aroundMethod(ProceedingJoinPoint pjd) {
// System.out.println("aroundMethod: ");
//
// 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 );
// throw new RuntimeException(e);
// }
//
// // 后置通知
// System.out.println("The method " + methodName + " ends" );
//
// return result;
// }
}
为了配合演示切面的优先级而写验证切面:ValidationAspect .javapackage com.baidu.aop.impl;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class ValidationAspect {
@Before("LoggingAspect.declareJoinPointExpression()")
public void validationArgs(JoinPoint joinPoint) {
System.out.println("-->validationArgs.." + Arrays.asList(joinPoint.getArgs()));
}
}
bean:applicationcontext.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
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.0.xsd">
<!-- 配置自动扫描的包
-->
<context:component-scan base-package="com.baidu.aop.impl"></context:component-scan>
<!-- 使aspectj 注解起作用: 自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- Build path is incomplete. Cannot find class file for org/aspectj/weaver/tools/PointcutDesignatorHandler -->
</beans>
测试方法:
package com.baidu.aop.impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringAOP {
public static void main(String[] args) {
// 1. 创建Spring 的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationcontext.xml");
//2. 从IOC 容器中获取bean 的实例
ArithmeticCalculator acImpl = (ArithmeticCalculator) ac.getBean("arithmeticCalculator");
//3. 使用bean
int result = acImpl.add(3, 6);
System.out.println( "result: " + result);
result = acImpl.div(15,3);
System.out.println( "result: " + result);
}
}
运行结果:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~ Spring AOP 用基于 XML 的配置声明切面~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
接口 ArithmeticCalculator.java
package com.baidu.aop.impl;
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);
}
接口的实现类 ArithmeticCalculatorImpl.javapackage com.baidu.aop.impl;
import org.springframework.stereotype.Component;
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
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;
}
}
切面
LoggingAspect.java
package com.baidu.aop.xml;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggingAspect {
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 );
}
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends" );
}
public void afterReturning(JoinPoint joinPoint,Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result );
}
public void afterThrowing(JoinPoint joinPoint,NullPointerException e) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion: " + e );
}
public Object aroundMethod(ProceedingJoinPoint pjd) {
System.out.println("aroundMethod: ");
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 );
throw new RuntimeException(e);
}
// 后置通知
System.out.println("The method " + methodName + " ends" );
return result;
}
}
为了配合演示切面的优先级而写验证切面:ValidationAspect .javapackage com.baidu.aop.xml;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
public class ValidationAspect {
public void validationArgs(JoinPoint joinPoint) {
System.out.println("-->validationArgs.." + Arrays.asList(joinPoint.getArgs()));
}
}
bean:applicationcontext-xml.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 配置 bean -->
<bean id="arithmeticCalculator"
class="com.baidu.aop.xml.ArithmeticCalculatorImpl"></bean>
<!-- 配置切面的 bean. -->
<bean id="loggingAspect"
class="com.baidu.aop.xml.LoggingAspect"></bean>
<bean id="validationAspect"
class="com.baidu.aop.xml.ValidationAspect">
</bean>
<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com.baidu.aop.xml.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="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<!--
<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
-->
</aop:aspect>
<aop:aspect ref="validationAspect" order="1">
<aop:before method="validationArgs" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试方法:TestSpringAOP_xml.java
package com.baidu.aop.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringAOP_xml {
public static void main(String[] args) {
// 1. 创建Spring 的IOC容器
ApplicationContext act = new ClassPathXmlApplicationContext("applicationcontext-xml.xml");
//2. 从IOC 容器中获取bean 的实例
ArithmeticCalculator acImpl = (ArithmeticCalculator) act.getBean("arithmeticCalculator");
//3. 使用bean
int result = acImpl.add(3, 6);
System.out.println( "result: " + result);
result = acImpl.div(15,3);
System.out.println( "result: " + result);
}
}
运行结果: