项目地址:https://github.com/gongxianshengjiadexiaohuihui/noobspring
我们先来理一下思路,aop是需要织入通知,我们是按照动态代理来实现的,目的就是生成一个代理类对象替代ioc容器中原有的对象。
private void doAspect(){
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始织入通知");
if(ioc.size() == 0){
return;
}
try {
for (Object obj : ioc.values()) {
Class<?> clazz = obj.getClass();
if (clazz.isAnnotationPresent(NBAspect.class)) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(NBPointcut.class)) {
logger.debug("切点方法:{}",method.toString());
String beanName = StringUtil.getNameByMethod(method.getAnnotation(NBPointcut.class).value());
String[] str = beanName.split("\\.");
String simpleName = StringUtil.lowerFirstCase(str[str.length - 1]);
Class <?>[] interfaces = Class.forName(beanName).getInterfaces();
if(interfaces.length == 0) {
/**
* 没有接口,所以用cglib动态代理
*/
CglibProxy cglibProxy = new CglibProxy();
ioc.put(simpleName,cglibProxy.getProxy(Class.forName(beanName),clazz.newInstance()));
}else {
/**
* 有接口,所以用jdk动态代理
*/
if (ioc.containsKey(simpleName)) {
JDKProxy proxy = new JDKProxy();
ioc.put(simpleName, proxy.bind(Class.forName(beanName).newInstance(), clazz.newInstance()));
} else {
/**
* 不包含,说明ioc里的key放的是接口名,因此获取所有接口
*/
for (Class<?> cl : interfaces) {
String tampName = StringUtil.lowerFirstCase(cl.getSimpleName());
if (ioc.containsKey(tampName)) {
JDKProxy proxy = new JDKProxy();
ioc.put(tampName, proxy.bind(Class.forName(beanName).newInstance(), clazz.newInstance()));
}
}
}
}
}
}
}
}
}catch (Exception e){
logger.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>织入通知失败");
e.printStackTrace();
throw new RuntimeException("织入通知失败");
}
logger.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>织入通知成功");
}
第一步,判断ioc容器是否为空,如果为空就直接返回
然后,遍历ioc里面存的values,判断是否被@NBAspect标记
我们看一下例子
package com.ggp.demo.aop;
import com.ggp.framework.annotation.aop.*;
import com.ggp.framework.annotation.mvc.NBComponent;
/**
* @Author:ggp
* @Date:2019/1/26 14 54
* @Description:
*/
@NBAspect
@NBComponent
public class DemoAspect_jdk {
@NBPointcut(value = "com.ggp.demo.service.impl.DemoServiceImpl.genesis")
public void print(){
};
@NBBefore
public void before(String key){
System.out.println("aspect>>>>>>>>before" + key);
}
@NBAfter
public void after(){
System.out.println("aspect>>>>>>>>after");
};
@NBAfterReturn
public void afterReturning(){
System.out.println("aspect>>>>>>>>afterReturning");
}
@NBAfterThrowing
public void afterThrowing(){
System.out.println("aspect>>>>>>>>afterThrowing");
}
}
我们首先获取它的所有方法,找到被@NBPointcut标记的方法,这里面有我们需要的信息,就是这个切点来自那个类,我们进行处理获取相应的信息,比如上面的例子,我们得到demoServiceImpl,在ioc容器中直接用新生成的代理类对象替换原有的对象,
这里的代理类有两种,如果有接口,使用jdk动态代理
package com.ggp.framework.proxy;
import com.ggp.demo.aop.DemoAspect_jdk;
import com.ggp.demo.service.DemoService;
import com.ggp.demo.service.impl.DemoServiceImpl;
import com.ggp.framework.annotation.aop.*;
import com.ggp.framework.common.util.StringUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.HashSet;
/**
* @Author:ggp
* @Date:2019/1/26 15 50
* @Description:
*/
public class JDKProxy implements InvocationHandler {
/**
* 真实对象
*/
private Object target;
/**
* 切面配置
*/
private Object aspect;
/**
* 保存切点
*/
private HashSet points ;
/**
* 插入方法
*/
private Method before;
private Method after;
private Method afterReturn;
private Method afterThrowing;
/**
* 返回代理对象
* @param target
* @param aspect
* @return
*/
public Object bind(Object target, Object aspect){
this.target = target;
this.aspect = aspect;
points = new HashSet();
Method[] methods = aspect.getClass().getMethods();
for(Method method : methods){
if(method.isAnnotationPresent(NBPointcut.class)){
String tampName = StringUtil.getMethodName(method.getAnnotation(NBPointcut.class).value());
points = StringUtil.setPoints(tampName, points);
} else if(method.isAnnotationPresent(NBBefore.class)){
this.before = method;
} else if(method.isAnnotationPresent(NBAfter.class)){
this.after = method;
} else if(method.isAnnotationPresent(NBAfterReturn.class)){
this.afterReturn = method;
} else if(method.isAnnotationPresent(NBAfterThrowing.class)){
this.afterThrowing = method;
} else {continue;}
}
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
*保存拦截方法的形参列表,判断有无参数
*/
Parameter[] parameters;
String methodName = StringUtil.getMethodName(method.toString());
Object obj;
/**
* 如果不是切点,不织入通知
*/
if(!points.contains(methodName)){
obj = method.invoke(target,args);
/**
* 粗心在这里忘记返回了,导致逻辑继续执行
*/
return obj;
}
if(before != null){
parameters = before.getParameters();
before.invoke(aspect, parameters.length == 0?null:args);
}
try{
obj = method.invoke(target,args);
}catch (Exception e){
if(afterThrowing != null){
parameters = afterThrowing.getParameters();
afterThrowing.invoke(aspect, parameters.length == 0?null:args);
}
throw e;
}finally {
if(after != null){
parameters = after.getParameters();
after.invoke(aspect, parameters.length == 0?null:args);
}
}
if(afterReturn != null){
parameters = afterReturn.getParameters();
afterReturn.invoke(aspect, parameters.length == 0?null:args);
}
return obj;
}
public static void main(String[] args) {
JDKProxy proxy = new JDKProxy();
DemoService demoService = (DemoService)proxy.bind(new DemoServiceImpl(),new DemoAspect_jdk());
demoService.genesis("ggp");
}
}
jdk动态代理实现的是InvocationHandler接口,需要实现invoke方法。
这里的bind方法相当于初始化函数,里面传入的是需要被代理的真实对象和切面对象,初始化步骤中有一步是设置切点,因为如果是接口的话,切点可能不止一个,我们回忆一下ioc在实例化@Service标记的类时,如果它有接口,遍历所有接口,存入对应的值,所以我们在这里应该存入所有接口对应的方法,
public static HashSet setPoints(String name, HashSet points){
/**
* 把本身的作为切点方法添加进去
*/
points.add(name);
String className = StringUtil.getNameByMethod(name);
String[] str = name.split("\\.");
String simpleName = str[str.length - 1];
try {
Class[] interfaces = Class.forName(className).getInterfaces();
if(interfaces.length == 0){
return points;
}
/**
* 如果存在接口,就把接口相同的方法也添加到切点方法集合中
*/
for(Class cl : interfaces){
Method[] methods = cl.getMethods();
for(Method method : methods){
if(StringUtil.getSimpleMethodName(method.toString()).equals(simpleName)){
points.add(StringUtil.getMethodName(method.toString()));
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return points;
}
如过被@NBPointcut修饰的方法获取的信息的类没有接口,就要使用cglib动态代理内容和jdk相似,底层实现原理不同
package com.ggp.framework.proxy;
import com.ggp.demo.aop.DemoAspect_cglib;
import com.ggp.demo.service.impl.CglibTest;
import com.ggp.framework.annotation.aop.*;
import com.ggp.framework.common.util.StringUtil;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Set;
/**
* @Author:ggp
* @Date:2019/1/28 09 43
* @Description:
*/
public class CglibProxy implements MethodInterceptor {
/**
* 切面配置
*/
private Object aspect;
/**
* 保存切点
*/
private Set points ;
/**
* 插入方法
*/
private Method before;
private Method after;
private Method afterReturn;
private Method afterThrowing;
/**
* 生成cglib代理对象
* @param cl
* @param aspect
* @return
*/
public Object getProxy(Class cl, Object aspect){
/**
* 初始化通知
*/
this.aspect = aspect;
points = new HashSet();
Method[] methods = aspect.getClass().getMethods();
for(Method method : methods){
if(method.isAnnotationPresent(NBPointcut.class)){
points.add(StringUtil.getMethodName(method.getAnnotation(NBPointcut.class).value()));
} else if(method.isAnnotationPresent(NBBefore.class)){
this.before = method;
} else if(method.isAnnotationPresent(NBAfter.class)){
this.after = method;
} else if(method.isAnnotationPresent(NBAfterReturn.class)){
this.afterReturn = method;
} else if(method.isAnnotationPresent(NBAfterThrowing.class)){
this.afterThrowing = method;
} else {continue;}
}
/**
* 创建增强类对象
*/
Enhancer enhancer = new Enhancer();
/**
* 设置超类(让代理类成为目标类的子类)
*/
enhancer.setSuperclass(cl);
/**
* 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor接口
*/
enhancer.setCallback(this);
/**
* 生成并返回代理对象
*/
return enhancer.create();
}
/**
* 代理逻辑方法
* @param o 代理对象
* @param method 方法
* @param objects 方法参数
* @param methodProxy 方法代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
*保存拦截方法的形参列表,判断有无参数
*/
Parameter[] parameters;
String methodName = StringUtil.getMethodName(method.toString());
Object obj;
/**
* 如果不是切点,不织入通知
*/
if(!points.contains(methodName)){
obj = methodProxy.invokeSuper(o,objects);
/**
* 粗心在这里忘记返回了,导致逻辑继续执行
*/
return obj;
}
if(before != null){
parameters = before.getParameters();
before.invoke(aspect, parameters.length == 0?null:objects);
}
try{
obj = methodProxy.invokeSuper(o,objects);
}catch (Exception e){
if(afterThrowing != null){
parameters = afterThrowing.getParameters();
afterThrowing.invoke(aspect, parameters.length == 0?null:objects);
}
throw e;
}finally {
if(after != null){
parameters = after.getParameters();
after.invoke(aspect, parameters.length == 0?null:objects);
}
}
if(afterReturn != null){
parameters = afterReturn.getParameters();
afterReturn.invoke(aspect, parameters.length == 0?null:objects);
}
return obj;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
CglibTest cglibTest = (CglibTest) cglibProxy.getProxy(CglibTest.class,new DemoAspect_cglib());
cglibTest.hi("ggp");
}
}
cglib动态代理实现的是MethodInterceptor接口