简单实现类似Spring的Aop原理实现

关于的Aop:
用处:可以用在连接数据库的方面,比如每一次在连接数据库的时候都需要手动新建一个连接,然后执行数据库操作,最后再来关闭数据库的连接来避免资源的消耗。这时候有人就在想那么可不可以让系统在我们每一次执行数据库操作的时候都自动的新建一个连接然后当我们执行完数据库的连接之后再自动的关闭连接呢。
这里就需要一个数据库

Aop的原理例子(会使用到cglib动态代理):
例子:
首先新建三个注解

//定义在类上面标明该类是一个切点
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
// 前置通知注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String value();
}

//后置通知注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
    String value();
}

新建一个歌曲类

public class Music {
    public void sing(String str){
        System.out.println(str+"唱歌");
    }
}

当然,有人唱个歌就得有人做开始前的准备,例如唱歌前的准备,于是新建一个类表示唱歌前的准备:

@MyAspect // 表明这是一个切点类
public class Player {
    @Before("reflec.aop.cglibtest.Music.sing()")  // 前置通知,当调用sing方法被调用的时候该方法会被在它之前调用
    public void beforeSing() {
        System.out.println("开始唱歌前的准备");
    }

    @After("reflec.aop.cglibtest.Music.sing()") // 同理,在调用sing方法之后再来调用该方法
    public void afterSing() {
        System.out.println("唱完之后开始评分");
    }
}

那么当调用sing方法的时候我们怎样调用这两个方法呢,即怎样织入这两个方法
这里就得考虑Cglib动态代理了,cglib依赖asm包,在目标类的基础上生成一个子类,然后通过子类来实现在目标方法调用的时候实现前置或者后置通知。。关于Cglib的这种,我感觉应该是属于编译器织入,因为是通过子类生成字节码然后进行调用。

建立代理类:
这个类的作用只要是通过动态代理来实现那两个方法的执行

public class CGLIBProxy implements MethodInterceptor {
    private Object target;
    private ProxyUtil proxyUtil ;
    public CGLIBProxy(Object target) throws ClassNotFoundException {
        this.target = target;
        proxyUtil =new ProxyUtil();
    }

    public <T> T getProxy(){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    public <T> T getProxy(Class<?> clazz){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        ProxyEntity proxyEntity =new ProxyEntity(proxy,this.target.getClass(),obj,method,args);
        return proxyUtil.generateEntity(proxyEntity);
    }

建立反射类:(即主要是通)

public class Reflect {
    Map<String,String> map ;   //存入的是方法名以及其注解
    Map<String,String> clazzMap;
    public Reflect() throws ClassNotFoundException {
        map=new HashMap<>();
        clazzMap =new HashMap<>();
        getAnnotationClass();
    }

    public Map<String, String> getMap() {  // 这里返回的是已经全部存好的map方面ProxyUtil使用
        return map;
    }
    @Test
    public  void getAnnotationClass() throws ClassNotFoundException {
        String clazzName="reflec.aop.cglibtest.Player";
        Class<?>  clazz =Class.forName(clazzName,false,Thread.currentThread().getContextClassLoader());   // 这里为了省事直接动态加载了该类
        if (clazz.isAnnotationPresent(MyAspect.class)) {  //假设是注解类
            Method[] methods =clazz.getDeclaredMethods();   //遍历方法
            for (Method method :methods) {
                if (method.isAnnotationPresent(Before.class)) {  // 获取注解
                   Before before =method.getAnnotation(Before.class); 
                    String beforeValue=before.value();  // 获取注解的值以及当前类的名字方面调用方法
                    map.put(method.getName()+ "-"+clazzName+"-"+"before",beforeValue.substring(0,beforeValue.length()-2));
                    // 存入的是方法名和注解名以及执行的顺序,这里为了省事直接就在后面写了
                if (method.isAnnotationPresent(After.class)) {
                    After after =method.getAnnotation(After.class); /
                    String afterValue=after.value();
                    map.put(method.getName()+ "-"+clazzName+"-"+"after",afterValue.substring(0,afterValue.length()-2));
                }
            }
        }
    }

建立处理这个Cglib里面的MethodInterceptor接口中的intercept方法的具体类

public class ProxyUtil {

    Reflect reflect;

    public ProxyUtil() throws ClassNotFoundException {
        reflect = new Reflect();
    }

    public void getMethod(String name) {
        Map<String, String> map = new HashMap<>();

    }

    //该方法负责代理
    public Object generateEntity(ProxyEntity proxyEntity) throws Throwable {
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        Map<String, String> methodMap = reflect.getMap();
        for (Map.Entry<String, String> map : methodMap.entrySet()) {
            if (map.getValue().equals(proxyMethodValue)) {
                String[] str = mapKeyDivision(map.getKey());
                if (str[2].equals("before")) {
                    Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加载该类
                    Method method = clazz.getDeclaredMethod(str[0]);
                    method.invoke(clazz.newInstance(), null); // 反射调用方法
                }
            }
        }
        return doAfter(proxyEntity,methodMap); // 处理后置通知
    }
    private Object  doAfter(ProxyEntity proxyEntity,Map<String,String> map) throws Throwable {
        Object object = proxyEntity.getMethodProxy().invokeSuper(proxyEntity.getObject(), proxyEntity.getArgs());  // 调用方法
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        for(Map.Entry<String,String> aMap:map.entrySet()){
            if (aMap.getValue().equals(proxyMethodValue)){
                String[] str =mapKeyDivision(aMap.getKey());
                    if(str[2].equals("after")){
                        Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加载该类
                        Method method = clazz.getDeclaredMethod(str[0]);
                        method.invoke(clazz.newInstance(), null); // 这一步需要原始的类
                    }
                }
            }
        return object;
    }
//分解map里面的键,因为里面存入了方法和类名以及执行顺序
    private String[] mapKeyDivision(String value) {
        String[] str = new String[10];
        str[0] = value.substring(0, value.indexOf("-"));  //注解下面的方法
        str[1] = value.substring(value.indexOf("-") + 1, value.lastIndexOf("-")); //注解所在的类
        str[2]=value.substring(value.lastIndexOf("-")+1,value.length()); //是before还是after
        return str;
    }

最后是一个bean

public class ProxyEntity {
    private final MethodProxy methodProxy;
    private final Class<?> clazz;
    private final Object object;
    private final Method method;
    private final Object[] args;

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public Method getMethod() {
        return method;
    }

    public ProxyEntity(MethodProxy methodProxy, Class<?> clazz, Object object, Method method, Object[] args) {
        this.methodProxy = methodProxy;
        this.clazz = clazz;
        this.object = object;
        this.method = method;
        this.args = args;
    }

    public MethodProxy getMethodProxy() {
        return methodProxy;
    }
}

最后进行测试:

public class CglibTest {

public static void main(String args[]) throws ClassNotFoundException {
    Music music = new Music();
    CGLIBProxy cglibProxy = new CGLIBProxy(music);
    ((Music)cglibProxy.getProxy()).sing("测试的人 ");
}
}

测试结果:
开始唱歌前的准备
唱歌测试的人
唱完之后开始评分

在这个测试中并没有调用Player类里面的方法却在运行的时候自动的运行了,这个例子执行简单的模仿了下Spring的AOP,其实还有好多地方都没有顾及到

这个类的思路就是先通过反射获取到切点类,然后将用注解标注的方法名以及注解里面的值存入一个map,最后在建立一个类用来处理map

Github地址:https://github.com/Somersames…

    原文作者:Somersames
    原文地址: https://segmentfault.com/a/1190000009165134
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞