当前位置: 移动技术网 > IT编程>开发语言>Java > Spring系列(四):Spring AOP详解

Spring系列(四):Spring AOP详解

2019年08月17日  | 移动技术网IT编程  | 我要评论

一、aop是什么

  aop(面向切面编程),可以说是一种编程思想,其中的spring aop和aspectj都是现实了这种编程思想。相对oop(面向过程编程)来说,提供了另外一种编程方式,对于oop过程中产生的横切性问题,这些横切性与业务无关,可以通过预编译方式和运行期动态代理来实现。比如可以应用在:日志记录、性能监控、事务管理等。

二、aop的基本概念

  aspect(切面):通常来说是一个类,里面定义了切点和通知,spring aop中可以用@aspectj来标注这个类是切面;

  join point(连接点):可以理解成目标对象中的方法,该方法是要被增强的方法,也就是我们要作用的一个切入点;

  pointcut(切点):切点可以理解成连接点的集合;

  target object(目标对象):被代理的对象,也就是目标对象;

  aop proxy(代理对象):把被代理的对象织入了增强后的对象;

  weaving(织入):把增强也就是代理逻辑加入到目标对象上的过程;

  advice(通知):用于指定在特定连接点上的增强的位置;

    ① before advice(前置通知):在目标方法被调用之前调用通知;

    ② after returning advice(返回通知):在目标方法成功执行之后调用通知;

    ③ after throwing advice(异常通知):在目标方法抛出异常后调用通知;

    ④ after (finally) advice(后置通知):在目标方法完成之后调用通知(不论是否出现异常都会执行,finally中调用);

    ⑤ around advice(环绕通知):围绕连接点(目标方法)的通知。可以在方法调用前后执行自定义行为;

三、代理的实现方式

  我们知道aop可以通过预编译的方式和运行期动态代理来实现,那么代理的实现方式有哪些呢?

  我们定义一个接口类:userservice

package com.toby.service;

/**
 * @desc: user 业务接口类
 * @author: toby
 * @date: 2019/8/4 23:28
 */
public interface userservice {
    /**
     * 添加
     */
    void add();
    
    /**
     * say hello
     * @param name
     * @return
     */
    string say(string name);
}

  在定义一个实现类userserviceimpl

package com.toby.service.impl;

import com.toby.service.userservice;
import org.springframework.stereotype.service;

/**
 * @desc: user业务的实现类
 * @author: toby
 * @date: 2019/8/4 23:29
 */
@service
public class userserviceimpl implements userservice {

    @override
    public void add() {
        system.out.println("执行userserviceimpl的add方法");
    }

    @override
    public string say(string name) {
        system.out.println("执行userserviceimpl的say方法 args = " + name);
        return "hello " + name;
    }
}

  第一种:静态代理

  定义一个静态代理类,需要实现userservice接口:

package com.toby.proxy;

import com.toby.service.userservice;

/**
 * @desc: 静态代理
 * @author: toby
 * @date: 2019/8/4 23:30
 */
public class staticproxy implements userservice {

    private userservice userservice;

    public staticproxy(userservice userservice){
        this.userservice = userservice;
    }

    @override
    public void add() {
        system.out.println("添加日志开始");
        userservice.add();
        system.out.println("添加日志结束");
    }

    @override
    public string say(string name) {
        return "";
    }
}

  第二种:jdk动态代理

package com.toby.proxy;

import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
import java.lang.reflect.proxy;

/**
 * @desc: jdk动态代理 实现一个接口:invocationhandler
 * jdk的动态代理机制只能代理实现了接口的类,而没有实现接口的类就不能实现jdk的动态代理
 * @author: toby
 * @date: 2019/8/4 23:34
 */
public class jdkdynamicproxy implements invocationhandler {
    /**
     * 目标对象
     */
    private object targetobject;

    public object createjdkproxy(final object targetobject){
        this.targetobject = targetobject;
        return proxy.newproxyinstance(this.targetobject.getclass().getclassloader(),
                this.targetobject.getclass().getinterfaces(),this);
    }

    @override
    public object invoke(object proxy, method method, object[] args) throws throwable {
        //写对应的增强代码
        system.out.println("jdk日志记录开始");
        //调用真正的业务方法
        object obj = method.invoke(this.targetobject,args);
        system.out.println("jdk日志记录结束");
        return obj;
    }
}

  第三种:cglib动态代理

package com.toby.proxy;

import org.springframework.cglib.proxy.enhancer;
import org.springframework.cglib.proxy.methodinterceptor;
import org.springframework.cglib.proxy.methodproxy;

import java.lang.reflect.method;

/**
 * @desc: cglib动态代理 cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,
 * 并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理
 * @author: toby
 * @date: 2019/8/4 23:43
 */
public class cglibdynamicproxy implements methodinterceptor {

    /**
     * 目标对象
     */
    private object targetobject;

    /**
     * 创建cglib动态代理
     * @param targetobject
     * @return
     */
    public object createcglibdynamicproxy(final object targetobject){
        this.targetobject = targetobject;
        //cglib中的核心对象,该类用于生成代理对象
        enhancer enhancer = new enhancer();
        //指定委托类也就是目标对象为父类
        enhancer.setsuperclass(this.targetobject.getclass());
        //使用代理,需要一个对应的代理对象
        enhancer.setcallback(this);
        return enhancer.create();
    }

    @override
    public object intercept(object o, method method, object[] args, methodproxy methodproxy) throws throwable {
        system.out.println("cglib日志记录开始");
        //委托类变成了父类。调用真正的服务提供者
        object obj = methodproxy.invoke(this.targetobject,args);
        system.out.println("cglib日志记录结束");
        return obj;
    }
}

  jdk代理的实现方式是基于接口实现,代理类继承proxy,实现接口。而cglib继承被代理的类来实现;这就是为什么jdk动态代理需要实现接口的原因?java是单继承

  下面定义一个字节码生成器bytecodegenerator来一看究竟:

package com.toby.proxy.generator;

import com.toby.service.impl.userserviceimpl;
import org.springframework.cglib.proxy.enhancer;
import org.springframework.cglib.proxy.methodinterceptor;
import sun.misc.proxygenerator;

import java.io.file;
import java.io.fileoutputstream;
import java.lang.reflect.modifier;
import java.nio.file.files;

/**
 * @desc: 字节码生成器
 * @author: toby
 * @date: 2019/8/5 0:05
 */
public class bytecodegenerator {
    /**
     * 根据目标对象生成字节码(jdk)
     * @param target
     * @param <t>
     * @return
     */
    public static <t> byte[] generatorbytecodebyjdkproxy(t target){
        int accessflags = modifier.public | modifier.final;
        byte [] codes = proxygenerator.generateproxyclass("proxy$"+target.getclass().getname(), target.getclass().getinterfaces(),accessflags);
        return codes;
    }

    /**
     * 根据目标对象生成字节码(cglib)
     * @param target
     * @param <t>
     * @return
     * @throws exception
     */
    public static <t> byte[] generatorbytecodebycglib(final t target) throws exception {
        enhancer enhancer = new enhancer();
        enhancer.setsuperclass(target.getclass());
        enhancer.setcallback((methodinterceptor) (o, method, objects, methodproxy) -> methodproxy.invoke(target,objects));
        enhancer.create();
        byte [] codes = enhancer.getstrategy().generate(enhancer);
        return codes;
    }

    public static void main(string[] args) {
        /**
         * 测试jdk
         */
        try {
            byte [] codes = bytecodegenerator.generatorbytecodebyjdkproxy(new userserviceimpl());
            file file = new file(system.getproperty("user.dir")+"/spring-aop/target/proxy$userserviceimpl.class");
            files.write(file.topath(),codes);
        } catch (exception e) {
            e.printstacktrace();
        }
        /**
         * 测试cglib
         */
        try {
            fileoutputstream out = new fileoutputstream(system.getproperty("user.dir")+"/spring-aop/target/cglib$userserviceimpl.class");
            out.write(bytecodegenerator.generatorbytecodebycglib(new userserviceimpl()));
            out.flush();
            out.close();
        } catch (exception e) {
            e.printstacktrace();
        }
    }
}

  jdk生成的动态代理字节码:

//
// source code recreated from a .class file by intellij idea
// (powered by fernflower decompiler)
//

package proxy$com.toby.service.impl;

import com.toby.service.userservice;
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
import java.lang.reflect.proxy;
import java.lang.reflect.undeclaredthrowableexception;

public final class userserviceimpl extends proxy implements userservice {
    private static method m1;
    private static method m2;
    private static method m3;
    private static method m0;

    public userserviceimpl(invocationhandler var1) throws  {
        super(var1);
    }

    public final boolean equals(object var1) throws  {
        try {
            return (boolean)super.h.invoke(this, m1, new object[]{var1});
        } catch (runtimeexception | error var3) {
            throw var3;
        } catch (throwable var4) {
            throw new undeclaredthrowableexception(var4);
        }
    }

    public final string tostring() throws  {
        try {
            return (string)super.h.invoke(this, m2, (object[])null);
        } catch (runtimeexception | error var2) {
            throw var2;
        } catch (throwable var3) {
            throw new undeclaredthrowableexception(var3);
        }
    }

    public final void add() throws  {
        try {
            super.h.invoke(this, m3, (object[])null);
        } catch (runtimeexception | error var2) {
            throw var2;
        } catch (throwable var3) {
            throw new undeclaredthrowableexception(var3);
        }
    }

    public final int hashcode() throws  {
        try {
            return (integer)super.h.invoke(this, m0, (object[])null);
        } catch (runtimeexception | error var2) {
            throw var2;
        } catch (throwable var3) {
            throw new undeclaredthrowableexception(var3);
        }
    }

    static {
        try {
            m1 = class.forname("java.lang.object").getmethod("equals", class.forname("java.lang.object"));
            m2 = class.forname("java.lang.object").getmethod("tostring");
            m3 = class.forname("com.toby.service.userservice").getmethod("add");
            m0 = class.forname("java.lang.object").getmethod("hashcode");
        } catch (nosuchmethodexception var2) {
            throw new nosuchmethoderror(var2.getmessage());
        } catch (classnotfoundexception var3) {
            throw new noclassdeffounderror(var3.getmessage());
        }
    }
}

  cglib生成动态代理的字节码:

//
// source code recreated from a .class file by intellij idea
// (powered by fernflower decompiler)
//

package com.toby.service.impl;

import java.lang.reflect.method;
import org.springframework.cglib.core.reflectutils;
import org.springframework.cglib.core.signature;
import org.springframework.cglib.proxy.callback;
import org.springframework.cglib.proxy.factory;
import org.springframework.cglib.proxy.methodinterceptor;
import org.springframework.cglib.proxy.methodproxy;

public class userserviceimpl$$enhancerbycglib$$b26297df extends userserviceimpl implements factory {
    private boolean cglib$bound;
    public static object cglib$factory_data;
    private static final threadlocal cglib$thread_callbacks;
    private static final callback[] cglib$static_callbacks;
    private methodinterceptor cglib$callback_0;
    private static object cglib$callback_filter;
    private static final method cglib$add$0$method;
    private static final methodproxy cglib$add$0$proxy;
    private static final object[] cglib$emptyargs;
    private static final method cglib$equals$1$method;
    private static final methodproxy cglib$equals$1$proxy;
    private static final method cglib$tostring$2$method;
    private static final methodproxy cglib$tostring$2$proxy;
    private static final method cglib$hashcode$3$method;
    private static final methodproxy cglib$hashcode$3$proxy;
    private static final method cglib$clone$4$method;
    private static final methodproxy cglib$clone$4$proxy;

    static void cglib$statichook2() {
        cglib$thread_callbacks = new threadlocal();
        cglib$emptyargs = new object[0];
        class var0 = class.forname("com.toby.service.impl.userserviceimpl$$enhancerbycglib$$b26297df");
        class var1;
        method[] var10000 = reflectutils.findmethods(new string[]{"equals", "(ljava/lang/object;)z", "tostring", "()ljava/lang/string;", "hashcode", "()i", "clone", "()ljava/lang/object;"}, (var1 = class.forname("java.lang.object")).getdeclaredmethods());
        cglib$equals$1$method = var10000[0];
        cglib$equals$1$proxy = methodproxy.create(var1, var0, "(ljava/lang/object;)z", "equals", "cglib$equals$1");
        cglib$tostring$2$method = var10000[1];
        cglib$tostring$2$proxy = methodproxy.create(var1, var0, "()ljava/lang/string;", "tostring", "cglib$tostring$2");
        cglib$hashcode$3$method = var10000[2];
        cglib$hashcode$3$proxy = methodproxy.create(var1, var0, "()i", "hashcode", "cglib$hashcode$3");
        cglib$clone$4$method = var10000[3];
        cglib$clone$4$proxy = methodproxy.create(var1, var0, "()ljava/lang/object;", "clone", "cglib$clone$4");
        cglib$add$0$method = reflectutils.findmethods(new string[]{"add", "()v"}, (var1 = class.forname("com.toby.service.impl.userserviceimpl")).getdeclaredmethods())[0];
        cglib$add$0$proxy = methodproxy.create(var1, var0, "()v", "add", "cglib$add$0");
    }

    final void cglib$add$0() {
        super.add();
    }

    public final void add() {
        methodinterceptor var10000 = this.cglib$callback_0;
        if (var10000 == null) {
            cglib$bind_callbacks(this);
            var10000 = this.cglib$callback_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, cglib$add$0$method, cglib$emptyargs, cglib$add$0$proxy);
        } else {
            super.add();
        }
    }

    final boolean cglib$equals$1(object var1) {
        return super.equals(var1);
    }

    public final boolean equals(object var1) {
        methodinterceptor var10000 = this.cglib$callback_0;
        if (var10000 == null) {
            cglib$bind_callbacks(this);
            var10000 = this.cglib$callback_0;
        }

        if (var10000 != null) {
            object var2 = var10000.intercept(this, cglib$equals$1$method, new object[]{var1}, cglib$equals$1$proxy);
            return var2 == null ? false : (boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final string cglib$tostring$2() {
        return super.tostring();
    }

    public final string tostring() {
        methodinterceptor var10000 = this.cglib$callback_0;
        if (var10000 == null) {
            cglib$bind_callbacks(this);
            var10000 = this.cglib$callback_0;
        }

        return var10000 != null ? (string)var10000.intercept(this, cglib$tostring$2$method, cglib$emptyargs, cglib$tostring$2$proxy) : super.tostring();
    }

    final int cglib$hashcode$3() {
        return super.hashcode();
    }

    public final int hashcode() {
        methodinterceptor var10000 = this.cglib$callback_0;
        if (var10000 == null) {
            cglib$bind_callbacks(this);
            var10000 = this.cglib$callback_0;
        }

        if (var10000 != null) {
            object var1 = var10000.intercept(this, cglib$hashcode$3$method, cglib$emptyargs, cglib$hashcode$3$proxy);
            return var1 == null ? 0 : ((number)var1).intvalue();
        } else {
            return super.hashcode();
        }
    }

    final object cglib$clone$4() throws clonenotsupportedexception {
        return super.clone();
    }

    protected final object clone() throws clonenotsupportedexception {
        methodinterceptor var10000 = this.cglib$callback_0;
        if (var10000 == null) {
            cglib$bind_callbacks(this);
            var10000 = this.cglib$callback_0;
        }

        return var10000 != null ? var10000.intercept(this, cglib$clone$4$method, cglib$emptyargs, cglib$clone$4$proxy) : super.clone();
    }

    public static methodproxy cglib$findmethodproxy(signature var0) {
        string var10000 = var0.tostring();
        switch(var10000.hashcode()) {
        case -1422568652:
            if (var10000.equals("add()v")) {
                return cglib$add$0$proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()ljava/lang/object;")) {
                return cglib$clone$4$proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(ljava/lang/object;)z")) {
                return cglib$equals$1$proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("tostring()ljava/lang/string;")) {
                return cglib$tostring$2$proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashcode()i")) {
                return cglib$hashcode$3$proxy;
            }
        }

        return null;
    }

    public userserviceimpl$$enhancerbycglib$$b26297df() {
        cglib$bind_callbacks(this);
    }

    public static void cglib$set_thread_callbacks(callback[] var0) {
        cglib$thread_callbacks.set(var0);
    }

    public static void cglib$set_static_callbacks(callback[] var0) {
        cglib$static_callbacks = var0;
    }

    private static final void cglib$bind_callbacks(object var0) {
        userserviceimpl$$enhancerbycglib$$b26297df var1 = (userserviceimpl$$enhancerbycglib$$b26297df)var0;
        if (!var1.cglib$bound) {
            var1.cglib$bound = true;
            object var10000 = cglib$thread_callbacks.get();
            if (var10000 == null) {
                var10000 = cglib$static_callbacks;
                if (var10000 == null) {
                    return;
                }
            }

            var1.cglib$callback_0 = (methodinterceptor)((callback[])var10000)[0];
        }

    }

    public object newinstance(callback[] var1) {
        cglib$set_thread_callbacks(var1);
        userserviceimpl$$enhancerbycglib$$b26297df var10000 = new userserviceimpl$$enhancerbycglib$$b26297df();
        cglib$set_thread_callbacks((callback[])null);
        return var10000;
    }

    public object newinstance(callback var1) {
        cglib$set_thread_callbacks(new callback[]{var1});
        userserviceimpl$$enhancerbycglib$$b26297df var10000 = new userserviceimpl$$enhancerbycglib$$b26297df();
        cglib$set_thread_callbacks((callback[])null);
        return var10000;
    }

    public object newinstance(class[] var1, object[] var2, callback[] var3) {
        cglib$set_thread_callbacks(var3);
        userserviceimpl$$enhancerbycglib$$b26297df var10000 = new userserviceimpl$$enhancerbycglib$$b26297df;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            cglib$set_thread_callbacks((callback[])null);
            return var10000;
        default:
            throw new illegalargumentexception("constructor not found");
        }
    }

    public callback getcallback(int var1) {
        cglib$bind_callbacks(this);
        methodinterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.cglib$callback_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setcallback(int var1, callback var2) {
        switch(var1) {
        case 0:
            this.cglib$callback_0 = (methodinterceptor)var2;
        default:
        }
    }

    public callback[] getcallbacks() {
        cglib$bind_callbacks(this);
        return new callback[]{this.cglib$callback_0};
    }

    public void setcallbacks(callback[] var1) {
        this.cglib$callback_0 = (methodinterceptor)var1[0];
    }

    static {
        cglib$statichook2();
    }
}

  第四种:javassist动态代理(在动态字节码插桩详解

  ① 定义一个javassistdynamicproxy实现javassist动态代理:

package com.toby.proxy;

import javassist.util.proxy.proxyfactory;
import javassist.util.proxy.proxyobject;

/**
 * @desc: javassist
 * @author: toby
 * @date: 2019/8/15 20:22
 */
public class javassistdynamicproxy {

    /**
     * 创建javassist动态代理
     * @param targetobject
     * @throws exception
     * @return
     */
    public object createjavassistdynamicproxy(final object targetobject)throws exception {
        proxyfactory factory = new proxyfactory();
        factory.setinterfaces(targetobject.getclass().getinterfaces());
        class<?> proxyclass = factory.createclass();
        object javassistproxy = proxyclass.newinstance();
        ((proxyobject)javassistproxy).sethandler((self,thismethod,proceed,args)-> {
            //写对应的增强代码
            system.out.println("javassist日志记录开始");
            //调用真正的业务方法
            object obj = thismethod.invoke(targetobject,args);
            system.out.println("javassist日志记录结束");
            return obj;
        });
        return javassistproxy;
    }
}

  ② 定义一个javassistbytecodedynamicproxy实现javassist动态代理:

package com.toby.proxy;

import javassist.*;

import java.lang.reflect.field;

/**
 * @desc: javassist 字节码动态代理
 * @author: toby
 * @date: 2019/8/15 20:42
 */
public class javassistbytecodedynamicproxy {

    /**
     * 创建javassist字节码动态代理
     * @param targetobject
     * @return
     * @throws exception
     */
    public static object createjavassistbytecodedynamicproxy(final object targetobject) throws exception {
        classpool pool = classpool.getdefault();
        ctclass proxyclass = pool.makeclass("javassistproxy" +  "&" +targetobject.getclass().getname());
        proxyclass.addinterface(pool.get(targetobject.getclass().getinterfaces()[0].getname()));
        proxyclass.addconstructor(ctnewconstructor.defaultconstructor(proxyclass));
        proxyclass.addfield(ctfield.make("private " + targetobject.getclass().getname() + " targetobject;", proxyclass));
        proxyclass.addmethod(ctnewmethod.make("public void add() { \n" +
                "system.out.println(\"javassist字节码日志记录开始\");\n" +
                "targetobject.add();\n" +
                "system.out.println(\"javassist字节码日志记录结束\");\n"+
                "}", proxyclass));
        class<?> clazz = proxyclass.toclass();
        object bytecodeproxy = clazz.newinstance();
        field field = bytecodeproxy.getclass().getdeclaredfield("targetobject");
        field.setaccessible(true);
        field.set(bytecodeproxy,targetobject);
        return bytecodeproxy;
    }

    /**
     * 创建javassist字节码动态代理2
     * @param targetobject
     * @return
     * @throws exception
     */
    public static object createjavassistbytecodedynamicproxy2(final object targetobject) throws exception {
        classpool pool = classpool.getdefault();
        pool.appendsystempath();
        ctclass ctl = pool.get(targetobject.getclass().getname());
        ctl.setname("javassistproxy" +  "&" + targetobject.getclass().getname());
        ctmethod ctmethod = ctl.getdeclaredmethod("add");
        ctmethod.insertbefore("system.out.println(\"javassist字节码2日志记录开始\");");
        ctmethod.insertafter("system.out.println(\"javassist字节码2日志记录结束\");");
        class<?> clazz = ctl.toclass();
        object bytecodeproxy = clazz.newinstance();
        return bytecodeproxy;
    }
}

 四、spring aop

  spring aop提供两种编程风格,详细用法见spring官网:

  ① @aspectj support(利用aspectj的注解)

  ② schema-based aop support(基于xml aop:config命名空间)

  启用@aspectj支持

  ① 使用java configuration启用@aspectj支持:要使用java @configuration启用@aspectj支持,要添加@enableaspectjautoproxy注释:

@configuration
@enableaspectjautoproxy
public class appconfig {

}

  ② 使用xml配置启用@aspectj支持:要使用基于xml的配置启用@aspectj支持,可以使用aop:aspectj-autoproxy元素

<aop:aspectj-autoproxy/>

  声明一个aspect

@aspect
@component
public class useraspect {
}

  声明一个pointcut

@pointcut("execution(* com.toby.service.userservice.*(..))")//the pointcut expression
public void pointcutexecution(){}//the pointcut signature

  spring aop支持的9种切入点表达式

  ① execution:execution用于匹配方法执行 join points连接点,最小粒度方法 

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.aspect;
name-pattern:方法名类型,如buisinessservice();
param-pattern:方法的参数类型,如java.lang.string;
throws-pattern:方法抛出的异常类型,如java.lang.exception;
example:
@pointcut("execution(* com.toby.dao.*.*(..))")//匹配com.toby.dao包下的任意接口和类的任意方法
@pointcut("execution(public * com.toby.dao.*.*(..))")//匹配com.toby.dao包下的任意接口和类的public方法
@pointcut("execution(public * com.toby.dao.*.*())")//匹配com.toby.dao包下的任意接口和类的public 无方法参数的方法
@pointcut("execution(* com.toby.dao.*.*(java.lang.string, ..))")//匹配com.toby.dao包下的任意接口和类的第一个参数为string类型的方法
@pointcut("execution(* com.toby.dao.*.*(java.lang.string))")//匹配com.toby.dao包下的任意接口和类的只有一个参数,且参数为string类型的方法
@pointcut("execution(* com.toby.dao.*.*(java.lang.string))")//匹配com.toby.dao包下的任意接口和类的只有一个参数,且参数为string类型的方法
@pointcut("execution(public * *(..))")//匹配任意的public方法
@pointcut("execution(* te*(..))")//匹配任意的以te开头的方法
@pointcut("execution(* com.toby.dao.indexdao.*(..))")//匹配com.toby.dao.indexdao接口中任意的方法
@pointcut("execution(* com.toby.dao..*.*(..))")//匹配com.toby.dao包及其子包中任意的方法
关于这个表达式的详细写法,可以参考官网:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

  定义一个aopconfig配置类:

package com.toby.config;

import org.springframework.context.annotation.componentscan;
import org.springframework.context.annotation.configuration;
import org.springframework.context.annotation.enableaspectjautoproxy;

/**
 * @desc: aop配置类
 * @author: toby
 * @date: 2019/8/5 23:48
 */
@configuration
/**
 * 此处需要注意的是,如果配置设置proxytargetclass=false,或默认为false,则是用jdk代理,否则使用的是cglib代理
 * jdk代理的实现方式是基于接口实现,代理类继承proxy,实现接口。而cglib继承被代理的类来实现。
 * 所以使用target会保证目标不变,匹配目标对象不会受到这个设置的影响。
 * 但是使用this时,会根据该选项的设置,cglib this可以代理原因是继承了被代理的对象也就是目标对象,jdk的this就不能被代理了。
 */
@enableaspectjautoproxy(proxytargetclass = true)
@componentscan(basepackages="com.toby")
public class aopconfig {
}

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * for matching method execution join points. this is the primary pointcut designator to use when working with spring aop.
     * execution用于匹配方法执行 join points连接点,最小粒度方法
     * 详细用法参考:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples
     */
    @pointcut("execution(* com.toby.service.userservice.*(..))")
    public void pointcutexecution(){}

    @before("pointcutexecution()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcutexecution()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  定义一个启动测试类aopmain(下同):

package com.toby;

import com.toby.config.aopconfig;
import com.toby.service.userservice;
import org.springframework.context.annotation.annotationconfigapplicationcontext;

/**
 * @desc: aop启动类
 * @author: toby
 * @date: 2019/8/5 23:50
 */
public class aopmain {
    public static void main(string[] args) {
        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(aopconfig.class);
        userservice userservice = context.getbean(userservice.class);
        userservice.say("toby");
    }
}

  运行结果如下:

  ② within:用于匹配指定类型内的方法执行, within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等。

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points within certain types (the execution of a method declared within a matching type when using spring aop).
     * 用于匹配指定类型内的方法执行, within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等
     */
    @pointcut("within(com.toby.service.impl.userserviceimpl)")
    public void pointcutwithin(){}

    @before("pointcutwithin()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcutwithin()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  运行结果如下:

  ③ this:用于匹配当前aop代理对象类型的执行方法;注意是aop代理对象的类型匹配 

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points (the execution of methods when using spring aop) where the bean reference (spring aop proxy) is an instance of the given type.
     * 用于匹配当前aop代理对象类型的执行方法;注意是aop代理对象的类型匹配
     */
    @pointcut("this(com.toby.service.impl.userserviceimpl)")
    public void pointcutthis(){}

    @before("pointcutthis()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcutthis()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  @enableaspectjautoproxy(proxytargetclass = true),则用cglib代理,而cglib继承被代理的类来实现,所以this能匹配到,运行结果:

   @enableaspectjautoproxy,默认proxytargetclass = false,如果基于接口则用jdk代理,所以this匹配不到,运行结果:

  ④ target:用于匹配当前目标对象类型的执行方法

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points (the execution of methods when using spring aop) where the target object (application object being proxied) is an instance of the given type.
     * 用于匹配当前目标对象类型的执行方法
     */
    @pointcut("target(com.toby.service.impl.userserviceimpl)")
    public void pointcuttarget(){}

    @before("pointcuttarget()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcuttarget()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  运行结果如下:

  ⑤ args:用于匹配当前执行的方法传入的参数为指定类型的执行方法

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points (the execution of methods when using spring aop) where the arguments are instances of the given types.
     * 用于匹配当前执行的方法传入的参数为指定类型的执行方法
     */
    @pointcut("args(java.lang.string)")
    public void pointcutargs(){}

    @before("pointcutargs()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcutargs()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  定义一个启动测试类aopmain:

package com.toby;

import com.toby.config.aopconfig;
import com.toby.service.userservice;
import org.springframework.context.annotation.annotationconfigapplicationcontext;

/**
 * @desc: aop启动类
 * @author: toby
 * @date: 2019/8/5 23:50
 */
public class aopmain {
    public static void main(string[] args) {
        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(aopconfig.class);
        userservice userservice = context.getbean(userservice.class);
        //不能被增强
        userservice.add();
        //能被增强 匹配到了参数string类型
        userservice.say("toby");
    }
}

  运行结果如下:

  ⑥ @target:匹配目标对象类型是否有指定的注解

  定义目标对象userserviceimpl如下:

package com.toby.service.impl;

import com.toby.anno.toby;
import com.toby.service.userservice;
import org.springframework.stereotype.service;

/**
 * @desc: user业务的实现类
 * @author: toby
 * @date: 2019/8/4 23:29
 */
@service
@toby
public class userserviceimpl implements userservice {

    @override
    public void add() {
        system.out.println("执行userserviceimpl的add方法");
    }

    @override
    public string say(string name) {
        system.out.println("执行userserviceimpl的say方法 args = " + name);
        return "hello " + name;
    }
}

  定义一个注解log:

package com.toby.anno;

import java.lang.annotation.elementtype;
import java.lang.annotation.retention;
import java.lang.annotation.retentionpolicy;
import java.lang.annotation.target;

/**
 * @desc: 日志注解
 * @author: toby
 * @date: 2019/8/5 23:06
 */
@target({elementtype.type,elementtype.method})
@retention(retentionpolicy.runtime)
public @interface log {
}

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points (the execution of methods when using spring aop) where the class of the executing object has an annotation of the given type.
     * 用于匹配目标对象类型是否有指定的注解
     */
    @pointcut("@target(com.toby.anno.log)")
    public void pointcuttargetanno(){}

    @before("pointcuttargetanno()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcuttargetanno()")
    public void after(){
        system.out.println("--------after--------");
    }
}

  运行结果如下:

  ⑦ @args:匹配方法参数所属的类型上有指定的注解(例子略)

  ⑧ @within:用于匹配所持有指定注解类型内的所以连接点也就是方法(例子略)

  ⑨ @annotation:用于匹配当前执行方法持有指定注解的方法(注解作用在方法上面)

  定义目标对象userserviceimpl如下:

package com.toby.service.impl;

import com.toby.anno.log;
import com.toby.service.userservice;
import org.springframework.stereotype.service;

/**
 * @desc: user业务的实现类
 * @author: toby
 * @date: 2019/8/4 23:29
 */
@service
public class userserviceimpl implements userservice {

    @override
    public void add() {
        system.out.println("执行userserviceimpl的add方法");
    }

    @override
    @log
    public string say(string name) {
        system.out.println("执行userserviceimpl的say方法 args = " + name);
        return "hello " + name;
    }
}

  定义一个useraspect:

package com.toby.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;

/**
 * @desc: 用户切面,该切面一定要交给spring容器管理
 * @author: toby
 * @date: 2019/8/5 22:56
 */
@aspect
@component
public class useraspect {

    /**
     * limits matching to join points where the subject of the join point (the method being executed in spring aop) has the given annotation.
     * 用于匹配当前执行方法持有指定注解的方法(注解作用在方法上面);
     */
    @pointcut("@annotation(com.toby.anno.log)")
    public void pointcutanno(){}

    @before("pointcutanno()")
    public void before(){
        system.out.println("--------before--------");
    }

    @after("pointcutanno()")
    public void after(){
        system.out.println("--------after--------");
    }
}

   运行结果发现:userservice.add();//不能被增强 userservice.say("toby");//能被增强,因为say方法上有@log注解;

   总结:本章讲解了spring aop的核心概念和应用场景,spring aop可以帮我们解决编程过程中的一些横切性问题,比如我们要记录日志,事务管理,性能监控,权限认证等。使的这些问题能和我们业务逻辑分开,达到了解耦的目的,代码的重用性更高。如何声明一个aspect,声明一个pointcut以及spring aop支持的9种切入点表达式。spring aop的代理方式有2种,一个是jdk一个cglib,如果配置设置proxytargetclass=false,或默认为false,则是用jdk代理,否则使用的是cglib代理,注意jdk动态代理必须实现接口,否则还是会走cglib动态代理。(后续的源码解析会分析到原因),spring系列完整代码在码云:

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网