AOP和IOC是Spring框架的两个核心技术。
AOP:面向切面编程(Aspect Oriented Programming),通过预编译方式和运行期动态代理来实现程序功能的统一与扩展的技术。
AOP可以对业务逻辑部分进行隔离,从而使业务逻辑耦合降低,提高代码复用和开发效率。
是基于动态代理实现的,如果目标对象实现了接口,就用JDK动态代理,未实现接口就用CGLIB动态代理。
通过AOP技术,在不修改源代码的情况下,为程序添加了新的功能,是对程序的非侵入式扩展。
AOP的简单模拟实现:
为了在目标方法前后执行新的方法,且不对源代码进行修改,所以要用动态代理,让代理去执行目标方法,然后在执行方法前进行前置拦截,在执行方法后进行后置拦截。
1.目标方法所在类:
//接口
public interface IDoSomething {
String doSomething(String str);
void doOther(int num);
}
//实现接口的类:
public class DoSomething implements IDoSomething {
public DoSomething() {
}
@Override
public String doSomething(String str) {
return "(" + str + ")";
}
@Override
public void doOther(int num) {
System.out.println("num:" + num);
}
}
2.JDK动态代理:
public class JDKProxy {
private static IntercepterChain chain = new IntercepterChain();
static IntercepterChain getIntercepterChain() {
return chain;
}
@SuppressWarnings("unchecked") public <T> T getProxy(Object object) {
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 前置拦截
if (JDKProxy.chain.dobefore(method,args)) {
try {
//目标方法
result = method.invoke(object, args);
}catch (Throwable e) {
throw e;
}
// 后置拦截
result = JDKProxy.chain.doAfter(method,result);
return result;
}
return null;
}
});
}
public <T> T getProxy(Class<?> klass) {
try {
Object object = klass.newInstance();
return getProxy(object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
拦截器链:
public class IntercepterChain {
private IIntercepter intercepter;//数据域
private IntercepterChain chain;//链域
private String targetMethod;//拦截目标
//不断追加拦截器
public static void addIntercepter(String targetMethod, IIntercepter intercepter) { JDKProxy.getIntercepterChain().setIntercepter(targetMethod,intercepter);
}
//链表的追加原理:数据域是空则直接设置,不为空则创建一个新的结点,然后再把数据设置进去,并且让前一个链域指向新的结点。
public void setIntercepter(String targetMethod, IIntercepter intercepter) {
if(this.intercepter == null) {
this.intercepter = intercepter;
this.targetMethod = targetMethod.toString().replace("*", ".*");
return;
}
if(this.chain == null) {
IntercepterChain intercepterChain = new IntercepterChain();
this.chain = intercepterChain;
intercepterChain.setIntercepter(targetMethod, intercepter);
return;
}
this.chain.setIntercepter(targetMethod, intercepter);
}
//让所形成的链表,
boolean dobefore(Method method, Object[] args) {
if(this.chain != null && !this.chain.dobefore(method, args)) {
return false;
}
if(this.intercepter != null) {
String methodStr = method.toString();
if(Pattern.matches(this.targetMethod, methodStr)) {
return this.intercepter.before(args);
}
return true;
}
return true;
}
Object doAfter(Method method, Object result) {
if(this.intercepter != null) {
String methodStr = method.toString();
if(Pattern.matches(this.targetMethod, methodStr)) {
result = this.intercepter.after(result);
}
return result;
}
if(this.chain != null) {
result = this.chain.doAfter(method, result);
}
return result;
}
}
获取代理对象,并且在dobefore()内部采用正则表达式来有选择的进行拦截处理。
当dobefore()方法返回值为true时,则可以正常执行目标方法,否则不执行。
拦截器所要实现的接口:
public interface IIntercepter {
boolean before(Object[] args);//前置拦截:如果返回值为false则为不允许执行下面的方法
Object after(Object result);//后置拦截主要针对于方法执行的结果
}
测试:
首先实现一个拦截器:
public class IntercepterOne implements IIntercepter {
public IntercepterOne() {
}
@Override
public boolean before(Object[] args) {
String arg0 = (String)args[0];
arg0 = "##" + arg0 + "##";
args[0] = arg0;
return true;
}
@Override
public Object after(Object result) {
return "--" + result + "--";
}
}
测试类:
public static void main(String[] args) {
String matcher = "*com.mec.aop.test.*.doSomething(*)";//拦截目标
IntercepterChain.addIntercepter(matcher,new IntercepterOne());
DoSomething ds = new DoSomething();
JDKProxy proxy = new JDKProxy();
IDoSomething iProxy = proxy.getProxy(ds);
String str = iProxy.doSomething("坚果");
System.out.println(str);
iProxy.doOther(20);
}