精华内容
下载资源
问答
  • AOP正确打开方式

    千次阅读 2017-04-21 21:48:52
    AOP正确打开方式

    这里写图片描述

    Aspect Oriented Programming(AOP)译作“面向切面编程”。

    看名字确实非常抽象,不容易理解。我们从 是什么,为什么,如何做 来攻克它!

    下面是AOP的知识网络图:

    这里写图片描述

    原图下载:
    http://download.csdn.net/detail/qq_34149805/9821716

    一、AOP到底是什么?


    AOP 是一种编程思想,是Spring框架中的一个重要内容。

    利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    Aop采用的是横向抽取机制,取代了传统的纵向继承体系重复代码。

    应用场景:事务管理,权限校验。

    如果你看了上面的说法可能还是有点蒙,请看第二点的解释。

    二、为什么要使用AOP?


    用一个老掉牙的例子来解释:

    如果在调用所有方法前都要进行权限校验,在调用所有方法后都要进行日志记录,而我们不能直接在原方法修改。

    public class UserviceImp implements UserService {
        @Override
        public void add() {
            System.out.println("add");
        }
    
        @Override
        public void delete() {
            System.out.println("delete");
        }
    }

    我们第一想到的可能是装饰者模式,即直接在原类的基础上再继承出一个装饰者类,这样既不改变原来的代码又可以实现需求。

    public class userDecorator implements UserService{
        @Override
        public void add() {
            System.out.println("权限校验");
            System.out.println("add");
            System.out.println("日志记录");
        }
    
        @Override
        public void delete() {
            System.out.println("权限校验");
            System.out.println("delete");
            System.out.println("日志记录");
        }
    }

    这样做确实可以解决问题,但却使我们的程序陷入的高度耦合,试想如果有10个service,就要抽象出10个装饰者。

    AOP就可以解决这个问题,所谓切面,就是要横向切入每一个目标方法中,而不是纵向继承,拉长代码。

    所以,AOP就是如何将目标方法”增强”的一个过程,或者说我们如何将我们自己定义的通知和目标方法结合起来的过程。

    JAVA中的动态代理机制,可以创建目标类的代理类,从何实现在代理类中的目标方法前后执行代码。我们会在下面的代码中用JAVA的代理机制自己写一个”AOP”。

    当然,代理机制只适用于实现了接口的目标类,Spring中使用了CGLIB来实现对目标类(不实现接口)的增强。

    三、如何实现AOP


    3.1 AOP 专有名词


    下面的实现中,经常会用到一些专有名词,提前科普一下会帮助我们更好的书写程序。

    1.target 目标类:需要被代理的类。例如UserService

    2.Joinpoint 连接点:可能被拦截到的方法。例如:所有方法

    3.PointCut 切入点:已经被增强的连接点,例如:addUser()

    4.advice 通知/增强,before,after

    5.weaving 织入 把增强advice应用到目标对象target来创建新代理对象Proxy的过程。

    6.Proxy 代理类

    7.Aspect 切面:是切入点pointcut和通知advice的结合。

    3.2 AOP的实现步骤


    仔细看来,每种AOP的实现方式都离不开以下三步。

    1.创建/声明目标类。

    2.创建/声明切面类(声明advice)。

    3.将advice织入到目标类中。

    我们接下来的程序将以以上三步展开。

    3.3 手动实现AOP


    3.3.1 使用JDK动态代理

    1.创建目标类。

    public interface UserService {
        void add();
        void update();
        void delete();
    }

    2.创建切面类(声明advice)。

    public class MyAspect{
        public void before(){
            System.out.println("鸡首");
        }
        public void after(){
            System.out.println("牛后");
        }
    }

    3.将advice织入到目标类中。

    public static UserService cteateService(){
    
            //1.目标类
            UserService userService = new UserviceImp();
            //2.切面类
            MyAspect myAspect = new MyAspect();
            //3.代理类:将目标类(切入点)和切面类(通知)结合-->切面
            //参数2: 还可以使用 new Class[]{UserService.class}
            UserService proxService = (UserService) Proxy.newProxyInstance(
                    userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(),
                    new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    myAspect.before();
                    Object invoke = (UserService) method.invoke(userService, args);
                    myAspect.after();
                    return invoke;
                }
            });
    
            return proxService;
        }

    3.3.2 CGLIB字节码增强

    public static UserviceImp cteateService(){
            final UserviceImp uService = new UserviceImp();
            final MyAspect myAspect = new MyAspect();
            /*
            * 代理类,采用cglib,底层创建目标类的自雷
            * */
            //核心类
            Enhancer enhancer = new Enhancer();
            //确定父类
            enhancer.setSuperclass(uService.getClass());
            //回调函数,等效jdk中InvocationHandler
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    myAspect.before();
                    Object invoke = method.invoke(uService, objects);
                //执行代理类的父类,执行目标类(目标类和代理类是父子关系)
                    methodProxy.invokeSuper(o,objects);
                    myAspect.after();
                    return invoke;
                }
            });
    
            UserviceImp proxService = (UserviceImp) enhancer.create();
    
            return proxService;
        }

    3.4 AspectJ 实现AOP


    AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法。

    3.4.1 AspectJ入门案例

    1.声明目标类

    2.声明切面

    /**
     * 切面类中确定通知,需要实现不同接口,接口就是规范,从而确定方法名称。
     * 采用环绕通知,(必须手动执行目标方法)
     */
    public class MyAspect implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    
            System.out.println("前方法");
            Object proceed = methodInvocation.proceed();
            System.out.println("后方法");
    
            return proceed;
        }
    }

    3.将advice织入到目标类中。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                              http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                              http://www.springframework.org/schema/aop
                              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                              http://www.springframework.org/schema/context
                              http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
        <!-- 创建目标类-->
        <bean id="userService" class="com.aop.lishiqi.c_spring_aop.UserviceImp"></bean>
        <!-- 创建切面类-->
        <bean id="myAspect" class="com.aop.lishiqi.c_spring_aop.MyAspect"></bean>
        <!-- aop,添加约束
             使用<aop:config>进行配置
             <aop:pointcut>切入点,从目标对象获得具体方法
             <aop:advisor> 特殊的切面,只有一个通知和一个切入点
             advice-ref 通知引用   pointcut-ref:通知引用
             切入点表达式:execution(* com.aop.lishiqi.c_spring_aop.*.*(..))
                           选择方法  返回值任意 包   类名任意,方法任意 参数任意
             <aop:pointcut>
    -->
        <aop:config>
            <aop:pointcut id="pointCut" expression="execution(* com.aop.lishiqi.c_spring_aop.*.*(..))"></aop:pointcut>
            <aop:advisor advice-ref="myAspect" pointcut-ref="pointCut"></aop:advisor>
        </aop:config>
    
    </beans>

    在Spring配置文件中将目标类和切面类配置为bean。

    可以发现,在 <aop:config> 中使用 <aop:pointcut首先声明切入点,即我们要将那些方法织入通知。

    然后使用 <aop:advisor 将切入点和切面关联起来,这是其中一种写法,下面的代码我们也用 <aop:aspect来实现。

    3.4.2 expression语法规则

    <aop:pointcut id="pointCut" expression="execution(* com.aop.lishiqi.c_spring_aop.*.*(..))"></aop:pointcut>

    这句话用来声明哪些目标方法是切入点。

    expression的语法规则是:

    execution(修饰符 返回值 包.类.方法(参数)throws异常)

    3.4.3 通知类型

    我们在入门案例中使用的是环绕通知,AspectJ中还有其他几种类型,他们各有不同的特点。

    前置通知:应用:各种数据校验,在方法前执行,如果排除异常阻止方法运行。

    后置通知:方法正常返回后执行,如果方法中抛出异常,阻止方法运行

    环绕通知:方法执行前后执行,可以阻止方法执行,必须手动执行目标方法

    异常通知:抛出异常时执行

    最终通知:无论发生什么最终都执行

    我们可以通过实现接口和<aop:advisor来使用他们,也可以使用自定义方法和<aop:aspect 来实现。

    下面以第二种写法分别展示他们的用法。

    后置通知:

    public void myBefore(JoinPoint joinPoint){
            System.out.println("前置通知"+joinPoint.getSignature().getName());
        }
    <aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.aop.lishiqi.c_spring_aop.*.*(..))"></aop:pointcut>
            <aop:aspect ref="myAspect">
    <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before>
    </aop:aspect>
        </aop:config>

    后置通知:

    <!--后置通知
                 returning:通知方法第二个参数的名称
    
                 -->
                <aop:after-returning method="myAfterreturning" pointcut-ref="myPointCut" returning="ret"></aop:after-returning>
    //第二个参数是切入点的返回值
        public void myAfterreturning(JoinPoint joinPoint,Object ret){
            System.out.println("后置通知"+joinPoint.getSignature().getName()+ret);
        }

    环绕通知:

    <!--环绕通知
                返回值类型必须为Object
                参数 ProceedingJoinPoint  需要抛出异常
                执行目标方法:Object proceed = joinPoint.proceed();
                方法名 任意-->
    
                <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around>
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
            //手动执行目标方法
            System.out.println("前通知");
            Object proceed = joinPoint.proceed();
            System.out.println("后通知");
            return proceed;
        }

    异常通知

    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
            System.out.println("抛出异常通知");
            System.out.println(e);
        }
    <!-- 抛出异常通知
                参数1 连接点的描述对象
                参数2 获得一场因戏 类型Throwable 参数名由throwing="e"配置
                -->
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing>

    最终通知:

    public void myAfter(JoinPoint joinPoint){
            System.out.println("最终通知");
        }
    <!-- 最终通知
                无论发生什么最终都执行
                -->
                <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after

    3.5 AOP注解开发

    注解是代替XML的配置,所以以上所有的XML都可以用注解替代。

    扫描包:

    <context:component-scan base-package="com.aop.lishiqi.d_aspect_注解"></context:component-scan>

    确定AOP注解生效:

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    切面类:

    //声明切面
    @Component("myAspect")
    @Aspect
    public class MyAspect {
    
    //声明切入点
        @Pointcut("execution(* com.aop.lishiqi.c_spring_aop.*.*(..)) ")
        private void myPointCut(){
    
        }
    
        @Before("execution(* com.aop.lishiqi.c_spring_aop.*.*(..))")
        public void myBefore(JoinPoint joinPoint){
            System.out.println("前置通知"+joinPoint.getSignature().getName());
        }
    
        @AfterReturning(value="myPointCut()",returning = "ret")
        public void myAfterreturning(JoinPoint joinPoint,Object ret){
            System.out.println("后置通知"+joinPoint.getSignature().getName()+ret);
        }
    
        @Around(value = "myPointCut()")
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
            //手动执行目标方法
            System.out.println("前通知");
            Object proceed = joinPoint.proceed();
            System.out.println("后通知");
            return proceed;
        }
    
        @AfterThrowing(value = "myPointCut()",throwing = "e")
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
            System.out.println("抛出异常通知");
            System.out.println(e);
        }
    
        @After("myPointCut()")
        public void myAfter(JoinPoint joinPoint){
            System.out.println("最终通知");
        }
    }
    展开全文
  • 关于AOP的理解

    2019-10-17 16:57:18
    AOP概览 什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个 热点,也是...

    AOP概览

    什么是AOP

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
    式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
    热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
    的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
    了开发的效率。

    AOP中的相关概念

    Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
    Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
    Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
    Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
    Target(目标对象):织入 Advice 的目标对象.。
    Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

    展开全文
  • 如何正确使用AOP

    千次阅读 2017-03-24 17:21:44
    这篇主要讲讲,AOP如何正确的使用它。 首先需要知道,目前毕竟流行的AOP框架,上篇文章也介绍了AOP实现的原理,对Spring这个大家庭来说,它的AOP远远没有这么简单。目前使用毕竟多的 几种方式如下: Jboss Aop:...

    AOP也发展了不久了,虽然在工作上也一直在用,不过毕竟没有深入了解过,停留在概念上的理解,和使用的阶段上。这篇主要讲讲,AOP如何正确的使用它。


    首先需要知道,目前毕竟流行的AOP框架,上篇文章也介绍了AOP实现的原理,对Spring这个大家庭来说,它的AOP远远没有这么简单。目前使用毕竟多的

    几种方式如下:

    • Jboss Aop:基本上没有用过,所以没有发言权,Jboos毕竟之前毕竟火AOP也有自己的一套,现在jboss慢慢分离出来很多东西,独立出来,感觉是Jboss Aop用的不多了。
    • Spring Aop:Spring自己原生的Aop,只能用一个词来形容:难用。 你需要实现大量的接口,继承大量的类,所以spring aop一度被千夫所指。这于他的无侵入,低耦合完全冲突。不过Spring对开源的优秀框架,组建向来是采用兼容,并入的态度。所以,后来的Spring 就提供了Aspectj支持,也就是我们后来所说的基于纯POJO的Aop。本人也是推进Aspectj实现AOP的
    •   Aspectj  这是一个AOP编程语言,一种基于Java平台的面向切面编程的语言,官网地址是http://www.eclipse.org/aspectj/,基本语法还是比较简单的,但毕竟整合起来,因此虽然比较好用,但也不常见了

       区别:spring Aop采用的动态织入,而Aspectj是静态织入。静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。有不清楚的同学,可以自己补下基础的代理知识。


    AOP的一些术语

        pointcut: 是一个(组)基于正则表达式的表达式,有点绕,就是说他本身是一个表达式,但是他是基于正则语法的。通常一个pointcut,会选取程序中的某些我们感兴趣的执行点,或者说是程序执行点的集合。"execution(public * cn.com.sinodata..*.*Controller.*(..))"

       joinPoint: 通过pointcut选取出来的集合中的具体的一个执行点,我们就叫JoinPoint.

       Advice: 在选取出来的JoinPoint上要执行的操作、逻辑。

       aspect: 就是我们关注点的模块化。这个关注点可能会横切多个对象和模块,事务管理是横切关注点的很好的例子。它是一个抽象的概念,从软件的角度来说是指在应用程序不同模块中的某一个领域或方面。由pointcut 和  advice组成。

       Target:被aspectj横切的对象。我们所说的joinPoint就是Target的某一行,如方法开始执行的地方、方法类调用某个其他方法的代码。


     aspect一般放在处理AOP最上面,我们把需要处理的AOP业务放在这个类里面,pointcut 和  advice组成。 pointcut 用类似正则表达式选择需要进行AOP的集合,joinPoint是针对pointcut 具体执行的一个点。而Advice则是具体需要做什么,Advice的參數需要和pointcut的方法名一致。有四种类型。

    1:before advice 在方法执行前执行。

    2:after  returning  advice 在方法执行后返回一个结果后执行。

    3:after  throwing advice 在方法执行过程中抛出异常的时候执行。

    4:Around  advice 在方法执行前后和抛出异常时执行,相当于综合了以上三种通知。

    一般Around  就行了。

    展开全文
  • Spring Boot使用AOP正确姿势

    千次阅读 2020-07-22 22:56:57
    面向切面编程(AOP)是面向对象编程的补充,简单来说就是统一处理某一“切面”的问题的编程思想。如果使用AOP的方式进行日志的记录和处理,所有的日志代码都集中于一处,不需要再每个方法里面都去添加,极大减少了...

    一、为什么需要面向切面编程?

    面向对象编程(OOP)的好处是显而易见的,缺点也同样明显。当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录、性能监控等,如果采用面向对象编程的方法,需要在每个对象里面都添加相同的方法,这样就产生了较大的重复工作量和大量的重复代码,不利于维护。面向切面编程(AOP)是面向对象编程的补充,简单来说就是统一处理某一“切面”的问题的编程思想。如果使用AOP的方式进行日志的记录和处理,所有的日志代码都集中于一处,不需要再每个方法里面都去添加,极大减少了重复代码。

    二、Spring AOP术语

    Spring AOP术语

    通知(Advice)包含了需要用于多个应用对象的横切行为,完全听不懂,没关系,通俗一点说就是定义了“什么时候”和“做什么”。

    连接点(Join Point)是程序执行过程中能够应用通知的所有点。

    切点(Poincut)是定义了在“什么地方”进行切入,哪些连接点会得到通知。显然,切点一定是连接点。

    切面(Aspect)是通知和切点的结合。通知和切点共同定义了切面的全部内容——是什么,何时,何地完成功能。

    引入(Introduction)允许我们向现有的类中添加新方法或者属性。

    织入(Weaving)是把切面应用到目标对象并创建新的代理对象的过程,分为编译期织入、类加载期织入和运行期织入。

    三、Spring Boot AOP实战

    3.1 引入依赖

    Spring Boot使用AOP需要添加spring-boot-starter-aop依赖,如下:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    

    不需要再添加aspectjweaver的依赖了,因为spring-boot-starter-aop包含了aspectjweaver,并且版本是较新的版本,如果在添加老版本(如1.5.4)启动会报错。
    Maven依赖

    3.2 编写用于拦截的bean

    直接定义一个controller,代码如下:

    @RestController
    public class AopController {
    
        @RequestMapping("/hello")
        public String sayHello(){
            System.out.println("hello");
            return "hello";
        }
    }
    
    

    3.3 定义切面

    Spring采用@AspectJ注解对POJO进行标注,该注解表明该类不仅仅是一个POJO,还是一个切面。切面是切点和通知的结合,那么定义一个切面就需要编写切点和通知。在代码中,只需要添加@Aspect注解即可。

    3.3.1 定义切点

    切点是通过@Pointcut注解和切点表达式定义的。

    @Pointcut注解可以在一个切面内定义可重用的切点。

    由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且实际中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如图是execution表达式的语法:

    execution表示在方法执行的时候触发。以“”开头,表明方法返回值类型为任意类型。然后是全限定的类名和方法名,“”可以表示任意类和任意方法。对于方法参数列表,可以使用“…”表示参数为任意类型。如果需要多个表达式,可以使用“&&”、“||”和“!”完成与、或、非的操作。

    切点表达式

    3.3.2 定义通知

    通知有五种类型,分别是:

    前置通知(@Before):在目标方法调用之前调用通知

    后置通知(@After):在目标方法完成之后调用通知

    环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法

    返回通知(@AfterReturning):在目标方法成功执行之后调用通知

    异常通知(@AfterThrowing):在目标方法抛出异常之后调用通知

    代码中定义了三种类型的通知,使用@Before注解标识前置通知,打印“beforeAdvice…”,使用@After注解标识后置通知,打印“AfterAdvice…”,使用@Around注解标识环绕通知,在方法执行前和执行之后分别打印“before”和“after”。这样一个切面就定义好了,代码如下:

    @Aspect
    @Component
    public class AopAdvice {
    
        @Pointcut("execution (* com.shangguan.aop.controller.*.*(..))")
        public void test() {
    
        }
    
        @Before("test()")
        public void beforeAdvice() {
            System.out.println("beforeAdvice...");
        }
    
        @After("test()")
        public void afterAdvice() {
            System.out.println("afterAdvice...");
        }
    
        @Around("test()")
        public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
            System.out.println("before");
            try {
                proceedingJoinPoint.proceed();
            } catch (Throwable t) {
                t.printStackTrace();
            }
            System.out.println("after");
        }
    
    }
    
    

    3.4 启动测试

    完成之后的代码结构如图所示:

    代码结构

    运行AopApplication,在浏览器访问http://localhost:8080/hello,不出意外,控制台输出如图所示:

    控制台输出结果

    展开全文
  • aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。下面这篇文章主要给大家介绍了如何在Spring Boot框架中使用AOP的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
  • 关于AOP的概念

    2007-11-04 11:46:12
    引用一篇文章:http://www.jdon.com/AOPdesign/jdon-aop.htm
  • 在.Net中关于AOP的实现

    2011-10-20 11:37:32
    在.Net中关于AOP的实现 2010-04-21 作者:conan 来源:conan的blog 一、AOP实现初步 AOP将软件系统分为两个部分:核心关注点和横切关注点。核心关注点更多的是Domain Logic,关注...
  • 在配置文件中的正确配置 <bean id="myAdvice" class="aop.MyAdvice"> <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="aop....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,131
精华内容 16,852
关键字:

关于aop正确的是