AOP
AOP 的定义
AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
AOP 的好处
- 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
- 业务模块更简洁, 只包含核心业务代码.
切面(Aspect) 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice)
切面必须要完成的工作
目标(Target)
被通知的对象
代理(Proxy)
向目标对象应用通知之后创建的对象
连接点(Joinpoint)
程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut)
每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
原理
动态代理
代理设计模式
代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.
示例代码(使用动态代理自己实现的日志代理类)
package com.weixuan.springaop;
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
package com.weixuan.springaop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class CalculatorLoggingProxy {
/**
* 要代理的对象
*/
private Calculator calculator;
public CalculatorLoggingProxy(Calculator calculator) {
this.calculator = calculator;
}
public Calculator getLoggingProxy() {
Calculator proxy = null;
/**
* 代理对象有哪一个类加载器来进行加载
*/
ClassLoader classLoader = calculator.getClass().getClassLoader();
/**
* 代码对象有哪些方法
*/
Class[] interfaces = new Class[] { Calculator.class };
/**
* 当调用代理对象的方法时,执行的代码
*/
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* @param proxy
* 正在返回的对象,一般情况下,不使用
* @param method
* 正在被调用的方法
* @param args
* 调用方法时传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 日志
*/
System.out.println("The mathod " + method.getName() + " begins with " + Arrays.asList(args));
/**
* 执行
*/
Object result = method.invoke(calculator, args);
/**
* 日志
*/
System.out.println("The mathod " + method.getName() + " ends with " + result);
return result;
}
};
return proxy = (Calculator) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
package com.weixuan.springaop;
public class Client {
public static void main(String[] args) {
Calculator calculator = new CalculatorImpl();
//System.out.println("----> " + calculator.div(4, 2));
Calculator proxy = new CalculatorLoggingProxy(calculator).getLoggingProxy();
proxy.add(2, 3);
}
}
使用spring aop的注解
package com.weixuan.springsop.aspect;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 把这个类声明为一个切面
*
* @author Nicholas
* 1. 把该类放入IOC容器 @compont
* 2. 添加注解 @aspect
*/
@Component
@Aspect
public class LoggingAspect {
/**
* 声明该方法是一个前置通知 joinpoint 切入点
*/
@Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void beforeMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " begin with " + args);
}
}
package com.weixuan.springsop.impl;
import org.springframework.stereotype.Component;
import com.weixuan.springsop.inter.Calculator;
@Component
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public int sub(int i, int j) {
return i - j;
}
@Override
public int mul(int i, int j) {
return i * j;
}
@Override
public int div(int i, int j) {
if (j != 0)
return i / j;
return 0;
}
}
package com.weixuan.springsop.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.weixuan.springsop.inter.Calculator;
public class Main {
public static void main(String[] args) {
// 1. 创建ioc容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/weixuan/springsop/config/application-context.xml");
// 2. 获取实例
Calculator calculator = applicationContext.getBean(Calculator.class);
// 3. 使用
System.out.println(calculator.add(2, 4));
}
}
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.weixuan.springsop"></context:component-scan>
<!-- 使aspect注解起作用 ,自动为匹配的类生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
各种通知
package com.weixuan.springsop.aspect;
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.springframework.stereotype.Component;
/**
* 把这个类声明为一个切面
*
* @author Nicholas 1. 把该类放入IOC容器 @compont 2. 添加注解 @aspect
*/
@Component
@Aspect
public class LoggingAspect {
/**
* 声明该方法是一个前置通知 joinpoint 切入点
*/
@Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void beforeMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " begin with " + args);
}
/**
* 无论是否有异常,都执行 还不能访问目标方法的返回结果,返回结果要在返回通知中才能访问
*
* @param joinpoint
*/
@After("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void afterMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " end with " + args);
}
/**
* 返回通知
*
* @param joinpoint
* 在方法正常返回的通知
*/
@AfterReturning(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", returning = "result")
public void returnMessage(JoinPoint joinpoint, Object result) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method " + methodName + " return with " + result);
}
/**
* 返回通知
*
* @param joinpoint
* 在方法正常返回的通知
*/
@AfterThrowing(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", throwing = "exception")
public void throwingMessage(JoinPoint joinpoint, Exception exception) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs exception " + exception);
}
/**
* 环绕通知 类似于 动态代理的全过程 这个类型参数可以决定是否执行目标方法,必须有返回值。
*
* @param ProceedingJoinPoint
* 在方法正常返回的通知s
*/
@Around(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public Object aroundMessage(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("The method ");
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
切面的优先级
多个切面,那个先执行?
使用@order(1..n)指定优先级,值越小,优先级越高
@Component
@Aspect
@Order(1)
public class LoggingAspect {}