Spring AOP的实现原理
AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。
实现原理:
jdk的动态代理和cglib
有接口 | 没有接口 | |
@EnableAspectJAutoProxy(proxyTargetClass = true)/ <aop:aspectj-autoproxy proxy-target-class="true"/> |
cglib | cglib |
@EnableAspectJAutoProxy(proxyTargetClass = false)/ <aop:aspectj-autoproxy proxy-target-class="false"/> |
jdk的动态代理 | cglib |
1.proxyTargetClass = true 有接口
2.proxyTargetClass = true 没有接口
3.proxyTargetClass =false 有接口
4.proxyTargetClass =false 没有接口
代码
//配置类
@Configuration @ComponentScan("com.jt.springAOP") @EnableAspectJAutoProxy(proxyTargetClass = false) public class Appconfig { }
//aop
@Component @Aspect public class LubanAspect { @Pointcut("execution(* com.jt.springAOP.dao.*.*(..))") public void pointCut(){ } @Before("pointCut()") public void before(){ System.out.println("AOP-----pointCut"); } }
//接口
public interface IndexDao { void query(); }
//接口的实现类
@Component("dao") public class IndexDaoImpl { public void query() { System.out.println("dao ---------------- query"); } }
//测试方法
public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class); IndexDaoImpl bean = (IndexDaoImpl) annotationConfigApplicationContext.getBean("dao"); bean.query(); } }
动态代理模式实现
代理类
public class LoggingProxy { private Calculate calculate; LoggingProxy(Calculate calculate) { this.calculate = calculate; } public Calculate getCalculateLog() { Calculate calculateLog = null; //代理对象由哪一个类加载器负责加载 ClassLoader loader = this.calculate.getClass().getClassLoader(); //代理对象的类型,有哪些方法 Class[] interfaces = new Class[]{Calculate.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("调用方法 " + methodName + " 参数 " + Arrays.asList(args)); //调用目标方法 Object result = null; try { //前置通知 result = method.invoke(calculate, args); //返回通知, 可以访问到方法的返回值 } catch (NullPointerException e) { e.printStackTrace(); //异常通知, 可以访问到方法出现的异常 } //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值 //打印日志 System.out.println("结果: " + result); return result; } }; calculateLog = (Calculate) Proxy.newProxyInstance(loader, interfaces, h); return calculateLog; } } 定义的接口 public interface Calculate { 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 CalculateImpl implements Calculate { @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) { return i / j; } public static void main(String[] args) { Calculate calculate = new CalculateImpl(); Calculate arit = new LoggingProxy(calculate).getCalculateLog(); arit.add(1,3); arit.sub(4,3); arit.mul(4,5); arit.div(6,3); arit.div(6,0); }
运行结果:
AOP应用
/** * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高 */ @Order(2) @Aspect @Component public class LoggingAspect { /** * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. * 使用 @Pointcut 来声明切入点表达式. * 后面的其他通知直接使用方法名来引用当前的切入点表达式. */ @Pointcut("execution(public int *.*(..))") public void declareJointPointExpression(){} /** * 在 接口的每一个实现类的每一个方法开始之前执行一段代码 */ @Before("declareJointPointExpression()") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("方法名 " + methodName + " 参数 " + Arrays.asList(args)); } /** * 在方法执行之后执行的代码. 无论该方法是否出现异常 */ @After("declareJointPointExpression()") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("方法名 " + methodName + " 结束"); } /** * 在方法法正常结束受执行的代码 * 返回通知是可以访问到方法的返回值的! */ @AfterReturning(value="declareJointPointExpression()", returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("方法名 " + methodName + " 返回结果 " + result); } /** * 在目标方法出现异常时会执行的代码. * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码 */ @AfterThrowing(value="declareJointPointExpression()", throwing="e") public void afterThrowing(JoinPoint joinPoint, Exception e){ String methodName = joinPoint.getSignature().getName(); System.out.println("方法名 " + methodName + " 异常:" + e); } /** * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法. * 且环绕通知必须有返回值, 返回值即为目标方法的返回值 */ @Around("declareJointPointExpression()") public Object aroundMethod(ProceedingJoinPoint pjd){ Object result = null; String methodName = pjd.getSignature().getName(); try { //前置通知 System.out.println("方法名 " + methodName + " 参数 " + Arrays.asList(pjd.getArgs())); //执行目标方法 result = pjd.proceed(); //返回通知 System.out.println("方法名 " + methodName + " 返回值 " + result); } catch (Throwable e) { //异常通知 System.out.println("方法名 " + methodName + " 异常:" + e); throw new RuntimeException(e); } //后置通知 System.out.println("方法名 " + methodName + " 结束"); return result; } }
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.atguigu.spring.aop"></context:component-scan> <!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>