我最近在写一个简易的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