使用cglib实现aop切面编程

我最近在写一个简易的java web框架,其中包括ioc,mvc,aop,dao等基本功能,这几天把aop这块弄得差不多了,所以在这里总结一下。

项目详细信息:coin-framework

spring中核心的概念就是ioc和aop。aop面向切面编程听起来很厉害,所以今天我们用cglib动态代理来实现一个简单的aop。

首先我们模仿一下spring的aop中基于注解的切面编程。

@Aspect   标识切面类

package com.me.coin.framework.aop.annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 标识切面类
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface Aspect {
	
	//切入点
	String pointCut();
	
	//过滤
	String filter() default "";
	
}

@Before 前置通知

package com.me.coin.framework.aop.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 前置通知
 * 
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface Before {

}

@After 后置通知

package com.me.coin.framework.aop.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 后置通知
 * 
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface After {

}

@Throwing 异常通知

package com.me.coin.framework.aop.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 异常通知
 * 
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface Throwing {

}

切面方法封装

package com.me.coin.framework.aop;

import java.lang.reflect.Method;


import com.me.coin.framework.aop.annotation.After;
import com.me.coin.framework.aop.annotation.Before;
import com.me.coin.framework.aop.annotation.Throwing;

import net.sf.cglib.proxy.MethodProxy;

/**
 * aop代理
 * 
 * @author dwl
 *
 */
public class AopProxy {
	//切面类
	private Object proxy;
	
	private Method beforeMethod;
	private Method afterMethod;
	private Method throwingMethod;
	

	public AopProxy(Object proxy) {
		this.proxy = proxy;
		setValue(proxy.getClass());
	}

	/**
	 * 执行动作链
	 * @param chain
	 * @param target
	 * @param method
	 * @param methodProxy
	 * @param params
	 * @return
	 */
	public Object proceed(ProxyChain chain, Object target, Method method, MethodProxy methodProxy, Object[] params) {
		Object result = null;
		before(method,params);
		try {
			AopProxy proxy = chain.next();
			if(null != proxy)
				result = proxy.proceed(chain,target,method,methodProxy,params);
			else
				result = methodProxy.invokeSuper(target, params);
			after(method,params);
		} catch (Throwable e) {
			throwing(method,params,e);
		} finally {
			chain.removeLocal();
		}
		
		return result;

	}

	public void before(Method method, Object[] args){
		if(null == beforeMethod)
			return;
		try {
			beforeMethod.setAccessible(true);
			beforeMethod.invoke(proxy, new Object[]{method,args});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void after(Method method, Object[] args){
		if(null == afterMethod)
			return;
		try {
			afterMethod.setAccessible(true);
			afterMethod.invoke(proxy, new Object[]{method,args});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void throwing( Method method, Object[] args,Throwable e) {
		if(null == throwingMethod)
			return;
		try {
			throwingMethod.setAccessible(true);
			throwingMethod.invoke(proxy, new Object[]{method,args});
		} catch (Exception e1) {
			e.printStackTrace();
		}
	}

	
	public Object getProxy() {
		return proxy;
	}

	public void setProxy(Object proxy) {
		this.proxy = proxy;
	}


	public Method getBeforeMethod() {
		return beforeMethod;
	}

	public void setBeforeMethod(Method beforeMethod) {
		this.beforeMethod = beforeMethod;
	}

	public Method getAfterMethod() {
		return afterMethod;
	}

	public void setAfterMethod(Method afterMethod) {
		this.afterMethod = afterMethod;
	}

	public Method getThrowingMethod() {
		return throwingMethod;
	}

	public void setThrowingMethod(Method throwingMethod) {
		this.throwingMethod = throwingMethod;
	}

	private void setValue(Class clazz){
		Method[] methods = clazz.getDeclaredMethods();
		for(Method method:methods){
			if(method.isAnnotationPresent(Before.class)){
				this.beforeMethod = method;
			}else if(method.isAnnotationPresent(After.class)){
				this.afterMethod = method;
			}else if(method.isAnnotationPresent(Throwing.class)){
				this.throwingMethod = method;
			}
		}
	}

}

一个类或方法可能被多个切面横切,所以这里要维护一个代理链。

package com.me.coin.framework.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import net.sf.cglib.proxy.MethodProxy;

/**
 * 代理链
 * 
 * @author dwl
 *
 */
public class ProxyChain {
	
	private List proxies = new ArrayList<>();
	
	//保证线程安全
	private ThreadLocal currentIndex = new ThreadLocal(){
		protected Integer initialValue() {
			return 0;
		};
	};
	 
	/**
	 * 获取下一个节点
	 * @return
	 */
	public AopProxy next(){
		if(currentIndex.get() >= proxies.size())
			return null;
		AopProxy proxy = proxies.get(currentIndex.get());
		currentIndex.set(currentIndex.get()+1);
		return proxy;
		
	}
	
	/**
	 * 执行代理链
	 * @return
	 */
	public Object handler(Object target, Method method, MethodProxy methodProxy, Object[] args){
		return next().proceed(this, target, method, methodProxy, args);
	}


	public List getProxies() {
		return proxies;
	}


	public void setProxies(List proxies) {
		this.proxies = proxies;
	}

	public void removeLocal(){
		this.currentIndex.remove();
	}
	
	
	
}

生成代理对象

package com.me.coin.framework.aop;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * aop
 * 
 * 根据代理链生成代理后的对象
 * 
 * @author dwl
 *
 */
public class ProxyManager {
	
	
	/**
	 * @param clazz 
	 * 
	 *   被横切的类
	 * @param chain
	 * 
	 *   代理链
	 * @return
	 * 
	 *   生成一个代理实例
	 */
	@SuppressWarnings("unchecked")
	public static  T getProxy(Class clazz,ProxyChain chain){
		
		return (T) Enhancer.create(clazz, new MethodInterceptor(){
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				return chain.handler(obj, method, proxy, args);
			}
		});
	}

}

在ioc初始化的时候生成被代理类和代理链的关系,再生成被代理后的对象放入ioc中。

package com.me.coin.framework.aop;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.me.coin.framework.aop.annotation.Aspect;
import com.me.coin.framework.ioc.CoinIocCache;
import com.me.coin.framework.util.ClassHelper;


/**
 * 切面上下文
 * 
 * @author dwl
 *
 */
public class AopCache {
	
	private static Map, ProxyChain> cache = new HashMap<>();
	
	
	/**
	 * 生成代理链
	 * @param clazz
	 * @param ioc
	 */
	public static void add(Class clazz,CoinIocCache ioc){
		Aspect aspect = clazz.getAnnotation(Aspect.class);
		List> list = ClassHelper.findClassBypackageName(aspect.pointCut().split(","));
		AopProxy proxy = new AopProxy(ioc.getCoinBean(clazz).getBean());
		for(Class cls:list){
			if(cache.containsKey(cls)){
				cache.get(cls).getProxies().add(proxy);
			}else{
				ProxyChain chain = new ProxyChain();
				chain.getProxies().add(proxy);
				cache.put(cls, chain);
			}
		}
	}
	
	public static Set> getProxyClass(){
		return cache.keySet();
	}
	
	
	public static ProxyChain getChain(Class clazz){
		return cache.get(clazz);
	}

}

ioc初始化的一些代码

public class CoinIocImpl implements CoinIoc{
	
	private static Logger logger = LoggerFactory.getLogger(CoinIoc.class);
	
	private static CoinIocCache cache = new CoinIocCache();
	
	//初始化ioc容器 controller service 以及一些组件都需要交给ioc
	static {
		logger.info("CoinIoc - 初始化开始...");
		List> classes = ClassHelper.findClassBypackageName(PropertyUtils.getPropertyArray((Constants.IOC_PACKAGE)));
		classes.forEach(clazz->{
			if(clazz.isAnnotationPresent(Act.class)){
				cache.addActionBean(clazz);;
				logger.info("CoinIoc - 加载controller类:{}",clazz.getName());
			}
			if(clazz.isAnnotationPresent(Service.class)){
				cache.addServiceBean(clazz);
				logger.info("CoinIoc - 加载service类:{}",clazz.getName());
			}
			if(clazz.isAnnotationPresent(IocBean.class)){
				cache.addIocBean(clazz);
                                //切面
				if(clazz.isAnnotationPresent(Aspect.class))
					AopCache.add(clazz,cache);
				logger.info("CoinIoc - 加载组件bean类:{}",clazz.getName());
			}
		});
                //aop
		AopCache.getProxyClass().forEach(clz->{
			cache.addAopBean(clz);
		});
	}

	@SuppressWarnings("unchecked")
	public  T getBean(Class clazz) {
		if(!cache.hasCoinBean(clazz))
			throw new BeanNotFoundException("CoinIoc - 无法获取该实例对象-" + clazz.getName());
		CoinBean coinBean = cache.getCoinBean(clazz);
		if(!coinBean.isInject())
			return injectBean(clazz);
		return (T) coinBean.getBean();
			
		
	}
}

ioc  存放所有bean的容器

package com.me.coin.framework.ioc;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.me.coin.framework.aop.AopCache;
import com.me.coin.framework.aop.ProxyChain;
import com.me.coin.framework.aop.ProxyManager;
import com.me.coin.framework.dao.impl.CoinDao;
import com.me.coin.framework.ioc.annotation.IocBean;
import com.me.coin.framework.tx.TransactionProxy;
import com.me.coin.framework.tx.annotation.Service;
import com.me.coin.framework.util.Strings;

/**
 * 存放所有bean的容器
 * @author dwl
 *
 */
public class CoinIocCache {
	
	//存放所有的bean
	private Map, CoinBean> cache = new HashMap, CoinBean>();
	
	public CoinIocCache() {
		CoinBean dao  = new CoinBean("coinDao", new CoinDao());
		dao.setInject(true);
		cache.put(CoinDao.class,dao);
	}
	
	/**
	 * controller类
	 * @param clazz
	 */
	public void addActionBean(Class clazz){
		try {
			String name = Strings.lowerFirst(clazz.getSimpleName());
			cache.put(clazz, new CoinBean(name, clazz.newInstance()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * service类通过动态代理生成
	 * @param clazz
	 */
	public void addServiceBean(Class clazz){
		Object bean = TransactionProxy.getProxy(clazz);
		Service service = clazz.getAnnotation(Service.class);
		String name = service.value();
		if(Strings.isEmpty(name))
			name = Strings.lowerFirst(clazz.getSimpleName());
		cache.put(clazz, new CoinBean(name, bean));
	}
	
	
	/**
	 * 普通bean类
	 * @param clazz
	 */
	public void addIocBean(Class clazz){
		try {
			IocBean iocBean = clazz.getAnnotation(IocBean.class);
			String name = iocBean.value();
			if(Strings.isEmpty(name))
				name = Strings.lowerFirst(clazz.getSimpleName());
			cache.put(clazz, new CoinBean(name, clazz.newInstance()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * aop切面后的bean
	 * @param clazz
	 */
	public void addAopBean(Class clazz){
		String name = "";
		if(clazz.isAnnotationPresent(IocBean.class))
			name = clazz.getAnnotation(IocBean.class).value();
		if(clazz.isAnnotationPresent(Service.class))
			name = clazz.getAnnotation(Service.class).value();
		ProxyChain chain = AopCache.getChain(clazz);
		Object bean = ProxyManager.getProxy(clazz, chain);
		if(Strings.isEmpty(name))
			name = Strings.lowerFirst(clazz.getSimpleName());
		cache.put(clazz, new CoinBean(name, bean));
	}
}

使用方法跟spring有点像

package com.dwl.controller;

import java.lang.reflect.Method;

import com.me.coin.framework.aop.annotation.After;
import com.me.coin.framework.aop.annotation.Aspect;
import com.me.coin.framework.aop.annotation.Before;
import com.me.coin.framework.aop.annotation.Throwing;
import com.me.coin.framework.ioc.annotation.IocBean;

@IocBean//交给ioc
@Aspect(pointCut="com.dwl.controller")//切面 pointCut:切入点
public class AspectT {
	
	@Before
	void start(Method method,Object[] args){
		System.out.println("前置通知");
		System.out.println(method.getName());
	}
	
	@After
	void end(Method method,Object[] args){
		System.out.println("后置通知");
	}
	
	@Throwing
	void error(Method method,Object[] args){
		System.out.println("异常通知");
	}

}

由于aop与ioc有交叉的地方,有一些代码可能没贴上来,感兴趣的同学请戳这里:coin-framework

你可能感兴趣的:(动态代理,aop)