您的位置:首页 > 编程语言 > Java开发

从零开始写javaweb框架笔记20-使框架具备AOP特性-开发AOP框架

2017-06-27 19:05 507 查看
       借鉴Spring AOP的风格,写一个基于切面注解的AOP框架,需了解前面的动态代理技术

      一:定义切面注解

     在框架中添加一个名为Aspect的注解,代码如下:

package org.smart4j.framework.annotation;

import java.lang.annotation.*;

/**
* Created by jack on 2017/6/27.
* 切面注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
/**
* 注解
* @return
*/
Class<? extends Annotation> value();
}


     通过

@Target(ElementType.TYPE)

来设置该注解只能应用在类上。该注解中包含了一个名为value的属性,它是一个注解类,用了定义Controller这类注解。在使用切面之前,我们需要先搭建一个代理框架。

     二:搭建代理框架

   在框架中添加一个名为Proxy的接口,代码如下:

package org.smart4j.framework.proxy;

/**
* Created by jack on 2017/6/28.
* 代理接口
*/
public interface Proxy {
Object doProxy(ProxyChain proxyChain) throws Throwable;
}


   这个Proxy接口中包括了一个doRroxy方法,传入一个ProxyChain,用于执行“链式代理操作”。所谓链式代理,也就是说,可将多个代理通过一条链子串起来,一个个地去执行,执行顺序取决于添加到链式上的先后顺序。

    下面是ProxyChain的代码:

package org.smart4j.framework.proxy;

import net.sf.cglib.proxy.MethodProxy;

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

/**
* Created by jack on 2017/6/28.
* 代理链
*/
public class ProxyChain {
private final Class<?> targetClass;
private final Object targetObject;
private final Method targetMethod;
private final MethodProxy methodProxy;
private final Object[] methodParams;
private List<Proxy> proxyList = new ArrayList<Proxy>();
private int proxyIndex = 0;

/**
* 构造函数
* @param targetClass
* @param targetObject
* @param targetMethod
* @param methodProxy
* @param methodParams
* @param proxyList
*/
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}

public Object[] getMethodParams() {
return methodParams;
}

public Class<?> getTargetClass() {
return targetClass;
}

public Method getTargetMethod() {
return targetMethod;
}
public Object doProxyChain()throws Throwable{
Object methodResult;
if (proxyIndex < proxyList.size()){
methodResult = proxyList.get(proxyIndex++).doProxy(this);
}else {
methodResult = methodProxy.invokeSuper(targetObject,methodParams);
}
return methodResult;
}
}


        在ProxyChain类中,我们定义了一系列的成员变量,包括targetClass(目标类),targetObject(目标对象),targetMethod(目标方法),methodProxy(方法代理),methodParams(方法参数),此外还包括了proxyList(代理列表),proxyIndex(代理索引),这些成员变量在构造中进行初始化,并提供了几个重要的获值方法。

        需要注意的是MethodProxy这个类,它是CGLib开源项目为我们提供的一个方法代理对象在doProxyChain方法中被使用。

        需要解释的是doProxyChain方法,在该方法中,我们通过proxyIndex来充当代理对象的计数器,若未达到proxyList的上限,则从proxyList中取出相应的Proxy对象,并调用其doProxy方法。在Proxy接口的实现中会提供相应的横切逻辑,并调用doProxyChain方法,随后将再次调用当前ProxyChain对象的doProxyChain方法,直到proxyIndex达到proxyList的上限为止,最后调用methodProxy的invokeSuper方法,执行目标对象的业务逻辑。

        我们必须在pom.xml文件中添加CGLib的Maven依赖:

 

<!--添加cglib类库,实现动态代理-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>


       现在我们需要些一个类,让它提供一个创建代理对象的方法,输入一个目标类和一组Proxy接口实现,输出一个代理对象,将该类命名为ProxyManager,让它来创建所有的代理对象,代码如下:

package org.smart4j.framework.proxy;

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.util.List;

/**
* Created by jack on 2017/6/28.
* 代理管理器
*/
public class ProxyManager {
public static <T> T createProxy(final Class<T> targetClass, final List<Proxy> proxyList){
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain();
}
});
}
}


        使用CGLib提供的Enhance的create方法来创建代理对象,将intercept的参数传入ProxyChain的构造器中即可。

        谁来调用ProxyManager呢,当然是切面类了,因为在切面类中,需要一个目标方法被调用前后增加相应的逻辑。我们有必要写一个抽象类,让它提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法。我们不妨将该抽象类命名为AspectProxy,代码如下:

package org.smart4j.framework.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
* Created by jack on 2017/6/28.
* 切面代理
*/
public class AspectProxy implements Proxy{
private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);
public Object doProxy(ProxyChain proxyChain) throws Throwable {
Object result = null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object [] params = proxyChain.getMethodParams();
begin();
try {
if (intercept(cls,method,params)){
before(cls,method,params);
result = proxyChain.doProxyChain();
after(cls,method,params);
}else {
result = proxyChain.doProxyChain();
}
}catch (Exception e){
LOGGER.error("proxy failure ",e);
error(cls,method,params);
throw e;
}finally {
end();
}
return result;
}

public boolean intercept(Class<?> cls,Method method,Object [] params) throws Throwable{
return true;
}
public void before(Class<?> cls,Method method,Object [] params) throws Throwable{
}
public void after(Class<?> cls,Method method,Object [] params) throws Throwable{
}
public void error(Class<?> cls,Method method,Object [] params) throws Throwable{
}
public void begin(){}
public void end(){}

}


      需要注意的是AspectProxy类中的doProxy方法,我们从proxyChain参数中获取了目标类,目标方法与目标参数,随后通过一个try。。。catch。。。finally代码块来实现调用框架,从框架中抽象出一系列的“钩子方法”,这些抽象方法可在AspectProxy的子类中选择性地进行实现,就像下面这样:

      

package org.smart4j.framework.aspect;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Aspect;
import org.smart4j.framework.annotation.Controller;
import org.smart4j.framework.proxy.AspectProxy;

import java.lang.reflect.Method;

/**
* Created by jack on 2017/7/17.
*/
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy{
private static final Logger LOG = LoggerFactory.getLogger(ControllerAspect.class);
private long begin;

@Override
public void before(Class<?> cls, Method method, Object[] params) throws Throwable {
LOG.debug("------------begin--------------");
LOG.debug(String.format("class: %s",cls.getName()));
LOG.debug(String.format("method: %s",method.getName()));
begin = System.currentTimeMillis();
}

@Override
public void after(Class<?> cls, Method method, Object[] params) throws Throwable {
LOG.debug(String.format("time: %s",System.currentTimeMillis()-begin));
LOG.debug("------------end--------------");
}
}


          我们只需实现before与after方法,就可以在目标方法执行前后添加其他需要执行的代码了。

      那么这样就结束了吗?no,我们还需要在整个框架里使用ProxyManager来创建代理对象,并将该代理对象放入框架底层的Bean Map中,随后才能通过IOC将代理的对象注入到其他对象中。

  

三:加载AOP框架

         按照之前的方式,为了加载AOP框架,我们需要编写一个名为AopHelper的类,然后将其添加到HelperLoader类中。

        在AopHelper中我们需要获取所有的目标类及其被拦截的切面类实例,并通过ProxyManager的createProxy方法来创建代理对象,最后将其放入Bean Map中。

        首先需要为BeanHelper类添加一个setBean方法,用于将Bean实例放入Bean Map中,代码如下:

package org.smart4j.framework.helper;

import org.smart4j.framework.util.ReflectionUtil;

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

/**
* Created by jack on 2017/5/23.
* bean 助手类
*/
public class BeanHelper {
/**
* 定义bean映射,(用于存放bean类与bean实例的映射关系)
*/
private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>();

/**
* 静态代码块
*/
static {
Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
for (Class<?> beanClass : beanClassSet) {
Object object = ReflectionUtil.newInstance(beanClass);
BEAN_MAP.put(beanClass,object);
}
}

/**
* 获取Bean映射
*/
public static Map<Class<?>,Object> getBeanMap(){
return BEAN_MAP;
}

/**
* 获取Bean实例
*/
public static <T> T getBean(Class<?> cls){
if (!BEAN_MAP.containsKey(cls)){
throw new RuntimeException("can not get bean by class:"+cls);
}
return (T) BEAN_MAP.get(cls);
}

/**
* 设置Bean实例
* @param cls
* @param obj
*/
public static void setBean(Class<?> cls,Object obj){
BEAN_MAP.put(cls,obj);
}
}


       然后,由于我们需要扩展AspectProxy抽象类的所有具体类,此外,还需要获取带有Aspect注解的所有类,因此需要在ClassHelper中添加以下两个方法:

     

/**
* 获取应用包名下某父类(或接口)的所有子类(或实现类)
* @param superClass
* @return
*/
public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET){
if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){
classSet.add(cls);
}
}
return classSet;
}

/**
* 获取应用包名带有某注解的所有类
* @param annotationClass
* @return
*/
public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET){
if (cls.isAnnotationPresent(annotationClass)){
classSet.add(cls);
}
}
return classSet;
}


   

   完整代码如下:

package org.smart4j.framework.helper;

import org.smart4j.framework.annotation.Controller;
import org.smart4j.framework.annotation.Service;
import org.smart4j.framework.util.ClassUtil;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

/**
* Created by jack on 2017/5/22.
* 类操作助手类
*/
public class ClassHelper {
/**
* 定义类集合,用于存放所加载的类
*/
private static final Set<Class<?>> CLASS_SET;

static {
String basePackage = ConfigHelper.getAppBasePackage();
CLASS_SET = ClassUtil.getClassSet(basePackage);
}

/**
* 获取应用包下的所有类
*/
public static Set<Class<?>> getClassSet() {
return CLASS_SET;
}

/**
* 获取应用包名下所有Service类
*/
public static Set<Class<?>> getServiceClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Service.class)) {
classSet.add(cls);
}
}
return classSet;
}

/**
* 获取应用包名下所有Controller类
*/
public static Set<Class<?>> getControllerClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Controller.class)) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 获取应用包名下所有Bean类(包括Service,Controller)
*/
public static Set<Class<?>> getBeanClassSet() {
Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
beanClassSet.addAll(getServiceClassSet());
beanClassSet.addAll(getControllerClassSet());
return beanClassSet;
}

/** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */ public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){ classSet.add(cls); } } return classSet; } /** * 获取应用包名带有某注解的所有类 * @param annotationClass * @return */ public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (cls.isAnnotationPresent(annotationClass)){ classSet.add(cls); } } return classSet; }

}


     有了以上两个工具方法以后,我们就可以在AopHelper类中编写一个带有Aspect注解的所有类,把封装成一个方法:

private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{
Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
Class<? extends Annotation> annotation = aspect.value();
if (annotation != null && annotation.equals(Aspect.class)){
targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
}
return targetClassSet;
}


      获取Aspect注解中设置的注解类,若该注解类不是Aspect类,则可调用ClassHelper的getClassSetByAnnotation方法获取相关的类,并把这些类放入目标类集合中,最终返回这个集合。

     紧接着我们需要获取代理类及其目标类集合之间的映射关系,一个代理类可对应一个或多个目标类。需要强调的是,这里所说的代理类指的是切面类。通过以下代码获取这个映射关系:

private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws  Exception{
Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class);
for (Class<?> proxyClass: proxyClassSet) {
if (proxyClass.isAssignableFrom(Aspect.class)){
Aspect aspect = proxyClass.getAnnotation(Aspect.class);
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
proxyMap.put(proxyClass,targetClassSet);
}
}
return proxyMap;
}


       代理类需要扩展AspectProxy抽象类,还需要带有Aspect注解,只有满足这两个条件,才能根据Aspect注解中所定义的注解属性去获取该注解所对应的目标类集合,然后才能建立代理类与目标类集合之间的映射关系,最终返回这个映射关系

      一旦获取了代理类与目标类集合之间的映射关系,就能根据这个关系分析出目标类与代理对象列表之间的映射关系,就像下面这样:

      

private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry: proxyMap.entrySet()) {
Class<?> proxyClass = proxyEntry.getKey();
Set<Class<?>> targetClassSet = proxyEntry.getValue();
for (Class<?> targetClass: targetClassSet) {
Proxy proxy = (Proxy) targetClass.newInstance();
if (targetMap.containsKey(targetClass)){
targetMap.get(targetClass).add(proxy);
}else {
List<Proxy> proxyList = new ArrayList<Proxy>();
proxyList.add(proxy);
targetMap.put(targetClass,proxyList);
}
}
}
return targetMap;
}


     最后,在AopHelper中通过一个静态块来初始化整个AOP框架,代码如下:

static {
try {
Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap();
Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap);
for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) {
Class<?> targetClass = targetEntry.getKey();
List<Proxy> proxyList = targetEntry.getValue();
Object proxy = ProxyManager.createProxy(targetClass,proxyList);
BeanHelper.setBean(targetClass,proxy);
}
}catch (Exception e){

}
}


      获取代理类及其目标类集合的映射关系,进一步获取目标类与代理对象列表的映射关系,进而遍历这个映射关系,从中获取目标类与代理对象列表,调用ProxyManager.createProxy方法获取代理对象,调用BeanHelper.setBean方法,将该代理对象重新放入Bean Map中。

       下面是AopHelper类的所有代码:

package org.smart4j.framework.helper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Aspect;
import org.smart4j.framework.proxy.AspectProxy;
import org.smart4j.framework.proxy.Proxy;
import org.smart4j.framework.proxy.ProxyManager;

import java.lang.annotation.Annotation;
import java.util.*;

/**
* Created by jack on 2017/7/17.
*/
public final class AopHelper {

private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);
static {
try {
Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap();
Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap);
for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) {
Class<?> targetClass = targetEntry.getKey();
List<Proxy> proxyList = targetEntry.getValue();
Object proxy = ProxyManager.createProxy(targetClass,proxyList);
BeanHelper.setBean(targetClass,proxy);
}
}catch (Exception e){
LOGGER.error("load aop failure",e);
}
}

private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{ Set<Class<?>> targetClassSet = new HashSet<Class<?>>(); Class<? extends Annotation> annotation = aspect.value(); if (annotation != null && annotation.equals(Aspect.class)){ targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); } return targetClassSet; }

private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws Exception{
Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class);
for (Class<?> proxyClass: proxyClassSet) {
if (proxyClass.isAssignableFrom(Aspect.class)){
Aspect aspect = proxyClass.getAnnotation(Aspect.class);
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
proxyMap.put(proxyClass,targetClassSet);
}
}
return proxyMap;
}

private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{ Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>(); for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry: proxyMap.entrySet()) { Class<?> proxyClass = proxyEntry.getKey(); Set<Class<?>> targetClassSet = proxyEntry.getValue(); for (Class<?> targetClass: targetClassSet) { Proxy proxy = (Proxy) targetClass.newInstance(); if (targetMap.containsKey(targetClass)){ targetMap.get(targetClass).add(proxy); }else { List<Proxy> proxyList = new ArrayList<Proxy>(); proxyList.add(proxy); targetMap.put(targetClass,proxyList); } } } return targetMap; }

}


    别忘了将AopHelper添加到HelperLoader中进行初始化,代码如下:





package org.smart4j.framework;

import org.smart4j.framework.helper.*;
import org.smart4j.framework.util.ClassUtil;

/**
* Created by jack on 2017/5/24.
* 加载相应类的,帮助类
*/
public final class HelperLoader {
//初始化,加载类
public static void init(){
Class<?> [] classList = {ClassHelper.class, BeanHelper.class, AopHelper.class,IocHelper.class, ControllerHelper.class,};
for (Class<?> cls: classList) {
ClassUtil.loadClass(cls.getName(),true);
}
}
}


   需要注意的是,AopHelper要在IocHelper之前加载,因为首先需要通过AopHelper获取代理对象,然后才能通过IocHelper进行依赖注入,到这里一个简单的AOP就开发完毕了。

github代码中的地址:https://github.com/wj903829182/smartframework
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  框架 java java web AOP
相关文章推荐