一.概述
AOP:(Aspect Oriented Programming)面向切面编程
OOP:(Object Oriented Programming)面向对象编程
面向切面编程:基于OOP基础之上的新的编程思想。指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式。
二.AOP场景和JDK动态代理(AOP前奏)
怎么理解AOP,现在提出一个场景:计算器运行计算方法的时候进行日志记录。
加上日志记录有下面的方式:
1)直接编写在方法内部。不推荐,修改维护麻烦。日志记录是系统的辅助功能,业务逻辑是核心功能,写在方法内部,会造成耦合。
2)日志模块:在核心功能运行期间,自己动态的加上。
第二种方式,可以用动态代理来实现。
AOP使用场景:
- AOP加日志保存到数据库。
- AOP做权限验证。
- AOP做安全检查。
- AOP是事务控制。
2.1JDK动态代理实验
1)先编写一个接口,接口中有四个方法等待实现,分别是计算器的加减乘除方法:
package com.test.inter;
public interface Calculator {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}
2)编写上面接口的实现类,实现了四种方法:
package com.test.impl;
import com.test.inter.Calculator;
public class MyMathCalculator implements Calculator{
@Override
public int add(int i, int j) {
System.out.println("【add】方法开始了");
int result=i+j;
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("【sub】方法开始了");
int result=i-j;
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("【mul】方法开始了");
int result=i*j;
return result;
}
@Override
public int div(int i, int j) {
System.out.println("【div】方法开始了");
int result=i/j;
return result;
}
}
3)编写一个类来实现目标方法,即日志记录:
package com.test.utils;
import java.lang.reflect.Method;
import java.util.Arrays;
public class LogUtils {
//方法开始前
public static void logStart(Method method,Object...arg2){
System.out.println("【"+method.getName()+"】方法开始执行,用的参数列表【"+Arrays.asList(arg2)+"】");
}
//方法正常返回
public static void logReturn(Method method,Object result){
System.out.println("【"+method.getName()+"】方法开始执行,计算结果是"+result);
}
//方法抛出异常
public static void logException(Method method,Exception e){
System.out.println("【"+method.getName()+"】方法执行出现异常了,异常信息是"+e.getCause()+"这个异常已经通知了测试小组");
}
//方法结束
public static void logEnd(Method method){
System.out.println("【"+method.getName()+"】方法最终结束了");
}
}
4)编写一个类,为传入的参数对象创建一个动态代理对象:
package com.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import com.test.inter.Calculator;
import com.test.utils.LogUtils;
/*
* 生成代理的类
* JDK 默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的。
*/
public class caculatorProxy {
/*
* 为传入的参数对象创建一个动态代理对象
* Calculator calculator:被代理的对象
*/
public static Calculator getProxy(final Calculator calculator)
{
//方法执行器,帮目标对象执行目标方法
InvocationHandler h=new InvocationHandler() {
/*
* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
* Object proxy:代理对象,给JDK使用,任何时候都不要动这个对象
* Method method:当前将要执行的目标对象的方法
* Object[] arg2:这个方法调用时外界传入的参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] arg2)
throws Throwable {
//利用反射执行目标方法
//目标方法执行后的返回值
System.out.println("这是动态代理将要帮你执行方法...");
Object result=null;
try {
LogUtils.logStart(method, arg2);
result = method.invoke(calculator, arg2);
LogUtils.logReturn(method, result);
} catch (Exception e) {
LogUtils.logException(method, e);
}
finally{
LogUtils.logEnd(method);
}
//返回值必须返回出去外界才能拿到真正执行后的返回值
return result;
}
};
Class<?>[] interfaces=calculator.getClass().getInterfaces();
ClassLoader loader=calculator.getClass().getClassLoader();
//Proxy为目标对象创建代理对象
Object proxy=Proxy.newProxyInstance(loader, interfaces, h);
return (Calculator) proxy;
}
}
5)编写一个Junit测试类进行测试:
package com.test.test;
import static org.junit.Assert.*;
import org.junit.Test;
import com.test.impl.MyMathCalculator;
import com.test.inter.Calculator;
import com.test.proxy.caculatorProxy;
public class AOPTest {
@Test
public void test() {
Calculator calculatro=new MyMathCalculator();
//代理对象和被代理对象唯一能产生的关联就是实现了同一个接口
Calculator prox=caculatorProxy.getProxy(calculatro);
prox.add(2, 1);
}
}
执行测试类的结果:
动态代理的缺点:
1)写起来难。
2)JDK默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的。
三.Spring AOP实验(注解)
上面的实动态代理写起来比较麻烦,也有缺点,下面就用Spring AOP来实现日志记录这个功能。Spring实现了AOP功能,底层就是动态代理。
优点:可以利用Spring一句代码都不写的创建动态代理。实现简单,而且没有强制要求目标对象必须实现接口;
3.1 AOP实验
AOP使用步骤:
1)导包
基础包:
commons-logging-1.1.3.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE