上节利用反射及注解技术我们是现在springmvc的核心功能,本节我们继续完成springaop的代理过程。
1.首先我们定义aop需要用到的注解
Aspect:aop的起始点,注解在类上
PointCut:注解包名,aop扫描该包
Before:controller方法执行前
AfterReturning:controller方法执行结束后
2.扫描包,找到aspect类,并找到pointcut注解,获取包名
private ConcurrentHashMap<String, Object> iocMap;
public AopLoader(ConcurrentHashMap<String, Object> iocMap){
this.iocMap=iocMap;
}
private ClassLoader classLoader = this.getClass().getClassLoader();
/**
* 扫描注解aspect
*
* @param packageName :需要扫描的基础包
*/
public void scanAspect(String packageName) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
URL url = classLoader.getResource("/" + packageName.replaceAll("\\.", "/"));
assert url != null;
File classDir = new File(url.getFile());
List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles()));
for (File dir : dirList) {
if (dir.isDirectory()) {
scanAspect(packageName + "." + dir.getName());
continue;
}
System.out.println("aop==" + packageName + "." + dir.getName());
String realPackageName = (packageName + "." + dir.getName()).replaceAll(".class", "");
Class<?> cls = Class.forName(realPackageName);
if (!cls.isAnnotationPresent(Aspect.class))
continue;
Method[] methods = cls.getMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(PointCut.class)){
PointCut pointCut=method.getAnnotation(PointCut.class);
//扫描到了切入点包,下一步进行动态代理
addProxy(pointCut.value(),cls.newInstance());
}
}
//包含Aspect标签,需要扫描切入包
}
}
3.扫描到包,对切入点的包下的controller进行动态代理
本文用到的iocmap,请参照上文手写springmvc
public void addProxy(String proxyPackage,Object aspectClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
URL url = classLoader.getResource("/" + proxyPackage.replaceAll("\\.", "/"));
assert url != null;
File classDir = new File(url.getFile());
List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles()));
for (File dir : dirList) {
if (dir.isDirectory()) {
scanAspect(proxyPackage + "." + dir.getName());
continue;
}
String realPackageName = (proxyPackage + "." + dir.getName()).replaceAll(".class", "");
Class<?> cls = Class.forName(realPackageName);
if(!iocMap.containsKey(StringUtil.lowerFirstCapse(cls.getSimpleName())))
continue;
Object iocBean= iocMap.get(StringUtil.lowerFirstCapse(cls.getSimpleName()));
AdviceProxy adviceProxy=new AdviceProxy();
iocBean= adviceProxy.createProxyObject(iocBean);
adviceProxy.setAspect(aspectClass);
iocMap.put(StringUtil.lowerFirstCapse(cls.getSimpleName()),iocBean);
}
}
下面贴出动态代理的方法
package com.gfh.mvc.framework.aop.proxy;
import com.gfh.mvc.framework.aop.annotation.AfterReturning;
import com.gfh.mvc.framework.aop.annotation.Before;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AdviceProxy implements MethodInterceptor {
private Object aspect;
/**
* 要被代理的对象
*/
private Object targetObject;
public Object createProxyObject(Object target) {
this.targetObject = target;
//该类用于生成代理对象
Enhancer enhancer = new Enhancer();
//设置目标类为代理对象的父类
enhancer.setSuperclass(this.targetObject.getClass());
//设置回调用对象为本身
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result;
//执行前
before();
//执行拦截的方法
result = methodProxy.invoke(targetObject, args);
after();
return result;
}
/**
* 执行方法前
*/
private void before() {
Method[] methods = aspect.getClass().getMethods();
for (Method method : methods) {
//查询出带有Before注解的方法
if (!method.isAnnotationPresent(Before.class))
continue;
try {
method.invoke(aspect);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
* 执行方法后
*/
private void after() {
Method[] methods = aspect.getClass().getMethods();
for (Method method : methods) {
//查询出带有Before注解的方法
if (!method.isAnnotationPresent(AfterReturning.class))
continue;
try {
method.invoke(aspect);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
public Object getAspect() {
return aspect;
}
public void setAspect(Object aspect) {
this.aspect = aspect;
}
}
4.我们只需要在ioc容器注入完成以后初始化aop即可
AopLoader aopLoader=new AopLoader(iocMap);
try {
aopLoader.scanAspect("com.gfh.mvc.framework");
} catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
5.我们来写一下aop的切入方法
package com.gfh.mvc.framework.aop;
import com.gfh.mvc.framework.aop.annotation.AfterReturning;
import com.gfh.mvc.framework.aop.annotation.Aspect;
import com.gfh.mvc.framework.aop.annotation.Before;
import com.gfh.mvc.framework.aop.annotation.PointCut;
@Aspect
public class AopAdvice {
private Long time;
@PointCut("com.gfh.mvc.framework.controller")
public void cut(){
}
@Before
public void before(){
System.out.println("我来了before");
time=System.currentTimeMillis();
}
@AfterReturning
public void AfterReturning(){
System.out.println("我来了AfterReturning:"+(System.currentTimeMillis()-time));
}
}
6.执行结果