Java Spring-AOP动态代理
2020-01-30 本文已影响0人
鄢栋云
代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
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);
}
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;
}
}
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 {
String methodName = method.getName();
//日志
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
//执行方法
Object result = method.invoke(target,args);
//日志
System.out.println("The method " + methodName + " ends with " + result);
return result;
}
};
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader,interfaces,h);
return proxy;
}
}
public class Main {
public static void main(String[] args) {
// ArithmeticCalculator arithmeticCalculator = null;
// arithmeticCalculator = new ArithmeticCalculatorImpl();
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
int result = proxy.add(1,2);
System.out.println("-->"+result);
proxy.div(4,2);
System.out.println("-->"+result);
}
}
//输出
The method add begins with [1, 2]
The method add ends with 3
-->3
The method div begins with [4, 2]
The method div ends with 2
-->3
AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP(Object-Oriented-Programming,面向对象编程)的补充。AOP的主要编程对象是切面(aspect),而切面模块化横切关注点。在应用AOP编程时,任然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里。AOP的好处:
- 每个事物逻辑位于一个位置,代码不分散,便于维护和升级。
- 业务模块更简洁,只包含核心业务代码。
SpringAOP
1.导入aspectjweaver.jar和aspectjrt.jar
2.在配置文件中加入aop的命名空间
3.基于注解的方式
1.在配置文件中加入以下配置
<!-- 使AspjectJ 注解起作用:自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.把横切关注点的代码抽象到切面的类中
i.切面首先是一个IOC中的bean,即加入@Component注解
ii.切面还需要加入@Aspect注解
3.在类中声明各种通知:
i.声明一个方法
ii.在方法前加入@Before注释
4.可以在通知方法中声明一个类型为JointPoint的参数。然后就能访问链接细节。如方法名称和参数值。
//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面
@Aspect
@Component
public class LoggingAspect {
//声明该方法是一个前置通知:在目标方法开始之前执行
@Before("execution(public int com.cloud.spring.aop.impl.ArithmeticCalculator.*(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);
}
}
例如:
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.cloud.spring.aop.impl"></context:component-scan>
<!-- 使AspjectJ 注解起作用:自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
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);
}
@Component
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;
}
}
//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面
@Aspect
@Component
public class LoggingAspect {
//声明该方法是一个前置通知:在目标方法开始之前执行
@Before("execution(public int com.cloud.spring.aop.impl.ArithmeticCalculator.*(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);
}
}
public class Main {
public static void main(String[] args) {
//1.创建Spring的IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从IOC容器中获取bean的实例
ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
//3.使用bean
int result = arithmeticCalculator.add(3,6);
System.out.println("result:" + result);
result = arithmeticCalculator.div(9,3);
System.out.println("result:" + result);
}
}
//输出
The method add begins with[3, 6]
result:9
The method div begins with[9, 3]
result:3
后置通知:在目标方法执行后(无论是否发生异常),执行的通知,在后置通知中还不能访问目标方法执行的结果。
//后置通知:在目标方法执行后(无论是否发生异常),执行的通知
//在后置通知中还不能访问目标方法执行的结果
@After("execution(* com.cloud.spring.aop.impl.*.*(int,int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
//输出
The method add begins with[3, 6]
The method add ends
result:9
The method div begins with[9, 3]
The method div ends
result:3
后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候,下面的后置通知记录了方法的终止,一个切面可以包括一个或者多个通知。
返回通知:在方法正常结束受执行的代码,返回通知是可以访问到方法的返回值的。
/*
* 在方法正常结束之后执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value = "execution(* com.cloud.spring.aop.impl.*.*(..))",returning = "result")
public void afterReturing(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
//输出
The method add begins with[3, 6]
The method add ends
The method add ends with 9
result:9
The method div begins with[9, 3]
The method div ends
The method div ends with 3
result:3
异常通知:在方法抛出异常之后
/*
* 在目标方法出现异常时会执行的代码
* 可以访问到异常对象,且可以指定在出现特定异常时再执行通知代码
*/
@AfterThrowing(value = "execution(* com.cloud.spring.aop.impl.*.*(..))",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs exception with " + ex);
}
//输出
The method add begins with[3, 6]
Exception in thread "main" java.lang.ArithmeticException: / by zero
The method add ends
The method add ends with 9
at com.cloud.spring.aop.impl.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:32)
result:9
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The method div begins with[9, 0]
The method div ends
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
The method div occurs exception with java.lang.ArithmeticException: / by zero
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
环绕通知:围绕着方法执行
public static void main(String[] args) {
//1.创建Spring的IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从IOC容器中获取bean的实例
ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
//3.使用bean
int result = arithmeticCalculator.add(3,6);
System.out.println("result:" + result);
result = arithmeticCalculator.div(9,3);
System.out.println("result:" + result);
}
/*
* 环绕通知需要携带ProceedingJointPoint 类型的参数
* 环绕通知类似于动态代理的全过程:ProceedingJointPoint 类型的参数可以决定是否执行目标方法。
* 且环绕通知必须有返回值,返回值即为目标方法的返回值
*/
@Around("execution(* com.cloud.spring.aop.impl.*.*(..))")
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;
}
//输出
The method add begins with [3, 6]
The method add ends with 9
The method add ends
result:9
The method div begins with [9, 3]
The method div ends with 3
The method div ends
result:3
public static void main(String[] args) {
//1.创建Spring的IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从IOC容器中获取bean的实例
ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
//3.使用bean
int result = arithmeticCalculator.add(3,6);
System.out.println("result:" + result);
result = arithmeticCalculator.div(9,0);
System.out.println("result:" + result);
}
/*
* 环绕通知需要携带ProceedingJointPoint 类型的参数
* 环绕通知类似于动态代理的全过程:ProceedingJointPoint 类型的参数可以决定是否执行目标方法。
* 且环绕通知必须有返回值,返回值即为目标方法的返回值
*/
@Around("execution(* com.cloud.spring.aop.impl.*.*(..))")
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;
}
//输出
The method add begins with [3, 6]
The method add ends with 9
The method add ends
result:9
The method div begins with [9, 0]
The method div occurs exception: java.lang.ArithmeticException: / by zero