概述
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
为什么要学习AOP
在不修改源代码的情况下,对原方法进行增强
AOP可以校验,日志记录,性能监控,事务控制
底层实现(了解)
代理机制:
Spring的AOP的底层用到两种代理机制
JDK动态代理:针对实现了接口的类产生的代理
Cglib动态代理:针对没有接口的类产生的代理,应用了底层的字节码增强的技术,生成当前的子类对象
JDK动态代理
创建一个代理类
public class ProxyUser implements InvocationHandler {private Object target; // 代理的目标对象public LogJdkProxy(Object target) {this.target = target;}/** 创建代理对象 */public Object createProxyInstance() {// 第1个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器// 第2个参数设置代理类实现的接口// 第3个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法return Proxy.newProxyInstance(//getClass().getClassLoader(), // 第1个参数target.getClass().getInterfaces(), // 第2个参数this); // 第3个参数}/*** @param proxy 目标对象的代理类实例* @param method 对应于在代理实例上调用接口方法的Method实例* @param args 传入到代理实例上方法参数值的对象数组* @return 方法的返回值,没有返回值(void)时是null*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("== Log:开始执行操作,方法名:"+method.getName()+" ==");Object result = method.invoke(target, args); // 执行原方法,把方法调用委派给目标对象System.out.println("== Log:操作执行完毕 ==");return result;}
}
测试
Cglib动态代理
创建对象接口
// 此类不能是final的,否则不能有子类,CGLIB也就不能工作了
public class UserServiceImpl {// 这是final的方法,不能被子类重写,所以CGLIB不会拦截这个方法public final void foo1() {System.out.println(">> final的方法 <<");}// 这是static的方法,CGLIB不会拦截这个方法public static void foo2() {System.out.println(">> static的方法 <<");}// 这是private的方法,CGLIB不会拦截这个方法private void foo3() {System.out.println(">> private的方法 <<");}// CGLIB会拦截这个方法,可以是public, protected, default的修饰符public void deleteUser() {System.out.println(">> 删除一个User <<");}
}
创建Cglib代理类
// CGLIB代理类
public class LogCglibProxy implements MethodInterceptor {private Object target; // 代理的目标对象/*** 创建代理对象* @param <T>* @param target 目标对象*/public <T> T createProxyInstance(T target) {this.target = target;Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象enhancer.setSuperclass(target.getClass()); // 设置父类enhancer.setCallback(this); // 设置回调对象为自己这个对象return (T) enhancer.create(); // 创建代理对象并返回}/*** @param proxy 目标对象代理类的实例* @param method 代理类实例上调用父类方法的Method实例* @param args 传入到方法参数值的对象数组* @param methodProxy 使用它调用父类的方法*/@Overridepublic Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {System.out.println(proxy.getClass());System.out.println("== Log:开始执行操作,方法名:" + method.getName() + " ==");Object result = methodProxy.invoke(target, args); // 执行原方法System.out.println("== Log:操作执行完毕 ==");return result;}
}
测试
public class MainTest {@Testpublic void test() throws Exception {UserService userService = new UserService();userService = (UserService) new LogCglibProxy().createProxyInstance(userService); // 使用包装之的后的代理对象userService.saveUser();System.out.println();userService.deleteUser();System.out.println();
}
}
Spring使用AspectJ进行AOP开发
引入lib包
引入配置文件
<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.xsd">
</beans>
使用xml配置文件形式
通知类型
切入点表达式
编写一个切面类
public class MyAspectXml {
// 前置增强
public void before(){
System.out.println("前置增强===========");}
}
在application Context.xml中配置
<!-- 配置切面类 -->
<bean id="myAspectXml" class="cn.test.spring.demo3.MyAspectXml"></bean>
<!-- 进行 aop 的配置 -->
<aop:config>
<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* cn.test.spring.demo3.OrderDao.save(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:aspect ref="myAspectXml">
<aop:before method="before" pointcut-ref="pointcut1"/> </aop:aspect>
</aop:config>
使用注解来实现切面类
开启aop注解自动代理
AspectJ的AOP注解
@Aspect 定义切面类
通知类型
@Before 前置通知
@AfterReturing 后置通知
@Around 环绕通知
@After 最终通知
@AfterThrowing 异常抛出通知
@Pointcut 定义切入点
编写切面类
@Component
@Aspect
public class MyAspect {@Before("MyAspect.pointcut01()")public void before(){System.out.println("前置-----");}@After("MyAspect.pointcut01()")public void after(){System.out.println("后置-----");}@Pointcut("execution(* com.xuexi.dao.*.*(..))")public void pointcut01(){}
}
测试