简单介绍一下AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP的优缺点

Spring AOP(动态):通过代理程序运行时织入
优点:简单,易用
缺点:性能略低,仅适用于方法调用,必须在Spring容器

AOP中关键性概念

一、 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
注1:完成具体的业务逻辑
二、 通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程
三、 代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
例子:外科医生+护士
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
四、切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
五、适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)

首先我们来写一个 接口 IBookBiz

package com.wangshaoyang.aop.biz;
public interface IBookBiz {
	// 购书
	public boolean buy(String userName, String bookName, Double price);
	// 发表书评
	public void comment(String userName, String comments);
}
然后实现 IBookBiz 接口 BookBizImpl
package com.wangshaoyang.aop.biz.impl;
import com.wangshaoyang.aop.biz.IBookBiz;
import com.wangshaoyang.aop.exception.PriceException;

public class BookBizImpl implements IBookBiz {
	public BookBizImpl() {
		super();
	}
	public boolean buy(String userName, String bookName, Double price) {
		// 通过控制台的输出方式模拟购书
		if (null == price || price <= 0) {
			throw new PriceException("book price exception");
		}
		System.out.println(userName + " buy " + bookName + ", spend " + price);
		return true;
	}

	public void comment(String userName, String comments) {
		// 通过控制台的输出方式模拟发表书评
		System.out.println(userName + " say:" + comments);
	}
}
PriceException类 用来处理 BookBizImpl 出现的异常
package com.wangshaoyang.aop.exception;
public class PriceException extends RuntimeException {
	public PriceException() {
		super();
	}
	public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
	public PriceException(String message, Throwable cause) {
		super(message, cause);
	}
	public PriceException(String message) {
		super(message);
	}
	public PriceException(Throwable cause) {
		super(cause);
	}
}
MyAfterReturningAdvice 后置通知
package com.wangshaoyang.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.AfterReturningAdvice;

public class MyAfterReturningAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		String clzName=arg3.getClass().getName();
		String methodName=arg1.getName();
		String params = Arrays.toString(arg2);
		System.out.println("【后置通知(买书返利)】:"+clzName+"."+methodName+"("+params+")");
	}
}
MyMethodBeforeAdvice 系统日志
package com.wangshaoyang.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 买书、评论前加系统日志
 * @author 86135
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		String clzName=arg2.getClass().getName();
		String methodName=arg0.getName();
		String params = Arrays.toString(arg1);
//		syso
//		dao.addLog(className,methodName,args,time,longTime)
		System.out.println("【系统日志】:"+clzName+"."+methodName+"("+params+")");
	}
}
MyMethodInterceptor 环绕通知
package com.wangshaoyang.aop.advice;

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyMethodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		String clzName=arg0.getThis().getClass().getName();
		String methodName=arg0.getMethod().getName();
		String params = Arrays.toString(arg0.getArguments());
		System.out.println("【环绕通知】:"+clzName+"."+methodName+"("+params+")");
//		returnValue是代理对象调用目标方法的返回值
		Object returnValue = arg0.proceed();
		System.out.println("【环绕通知】:"+clzName+"."+methodName+"("+params+")"+"	方法调用的返回值:"+returnValue);
		
		return returnValue;
	}
}
MyThrowsAdvice
package com.wangshaoyang.aop.advice;

import org.springframework.aop.ThrowsAdvice;

import com.wangshaoyang.aop.exception.PriceException;

public class MyThrowsAdvice implements ThrowsAdvice {
	public void afterThrowing( PriceException ex ) {
		System.out.println("价格输入有误,购买失败,请重新输入!!!");
//		加报BUG前需要处理的操作
	}
}

重点要spring 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"
	xmlns:context="http://www.springframework.org/schema/context"
	default-autowire="byName"
	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.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- aop的知识点 -->
	<!-- 目标 -->
	<bean id="bookBiz" class="com.wangshaoyang.aop.biz.impl.BookBizImpl"></bean>
	<!-- 通知 -->
	<!-- 前置通知 -->
	<bean id="myMethodBeforeAdvice" class="com.wangshaoyang.aop.advice.MyMethodBeforeAdvice"></bean>
	<!-- 后置通知 -->
	<bean id="myAfterReturningAdvice" class="com.wangshaoyang.aop.advice.MyAfterReturningAdvice"></bean>
	<!-- 环绕通知 -->
	<bean id="myMethodInterceptor" class="com.wangshaoyang.aop.advice.MyMethodInterceptor"></bean>
	<!-- 异常通知 -->
	<bean id="myThrowsAdvice" class="com.wangshaoyang.aop.advice.MyThrowsAdvice"></bean>
	<!-- 过滤通知 -->
	<bean id="myAfterReturningAdvicePlus" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice" ref="myAfterReturningAdvice"></property>
		<!-- <property name="pattern" value=".*buy"></property> -->
		<property name="patterns">
			<list>
				<value>.*buy</value>
			</list>
		</property>
		
	</bean>
	
	<!-- 代理对象=目标加通知  -->
	<bean id="bookBizProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="bookBiz"></property>
		<property name="proxyInterfaces">
		<list>
			<value>com.wangshaoyang.aop.biz.IBookBiz</value>
		</list>
		</property>
		<property name="interceptorNames">
		<list>
			<value>myMethodBeforeAdvice</value><!-- 前置通知 -->
			<!-- <value>myAfterReturningAdvice</value> --><!-- 后置通知 -->
			<value>myAfterReturningAdvicePlus</value><!-- 过滤通知 -->
			<value>myMethodInterceptor</value><!-- 环绕通知 -->
			<value>myThrowsAdvice</value><!-- 异常通知 -->
		</list>
		</property>
	</bean>	
</beans>

我们写一个 Demo 来测试下
package com.wangshaoyang.aop.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.wangshaoyang.aop.biz.IBookBiz;

public class Demo {
	public static void main(String[] args) {
		ApplicationContext context=new ClassPathXmlApplicationContext("/spring-context.xml");
//		IBookBiz bean = (IBookBiz) context.getBean("bookBiz");
//		System.out.println(bean.getClass());
		IBookBiz bean = (IBookBiz) context.getBean("bookBizProxy");
//		System.out.println(bean.getClass());
//		报错之后,程序终止
		bean.buy("智障", "笑著醒", 66d);
		bean.comment("玉老頭", "醉了");
	}
}
我们运行Demo看到

Spring之AOP_系统日志

最后总结下AOP的核心概念
前置通知
		实现org.springframework.aop.MethodBeforeAdvice接口
		买书、评论前加系统日志

		后置通知
		实现org.springframework.aop.AfterReturningAdvice接口
		买书返利(存在bug)

		环绕通知
		org.aopalliance.intercept.MethodInterceptor
		类似拦截器,会包括切入点,目标类前后都会执行代码。
		
		异常通知
		org.springframework.aop.ThrowsAdvice
		出现异常执行系统提示,然后进行处理。价格异常为例
		
		过滤通知(适配器)
		org.springframework.aop.support.RegexpMethodPointcutAdvisor
		处理买书返利的bug