资讯专栏INFORMATION COLUMN

<spring 3.x企业应用开发实战>读书笔记-aop基础

isaced / 2375人阅读

摘要:是什么是面向切面编程的简称。负责实施切面,它将切面所定义的横切逻辑织入到切面所指定的连接点钟。静态正则表达式匹配切面是正则表达式方法匹配的切面实现类。流程切面的流程切面由和实现。

aop是什么

aop是面向切面编程(aspect oriented programing)的简称。aop的出现并不是要完全替代oop,仅是作为oop的有益补充。
aop的应用场合是有限的,一般只适合于那些具有横切逻辑的应用场合。

性能监测

访问控制

事务管理

日志记录
...

aop中的概念 连接点(joinpoint)

一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就称为连接点。比如,

类开始初始化前,后

类中某个方法调用前,后

方法抛出异常后
...

连接点由两个信息确定:

用方法表示的程序执行点

用相对点表示的方位

如在Test.foo()方法执行前的连接点,执行点为Test.foo(),方位为该方法执行前的位置。
spring使用切点对执行点定位,而方位则在增强类型中定义.

切点(pointcut)

每个程序类都可能有多个连接点,aop通过切点定位特定点。类比于数据库查询:连接点相当于数据库中的记录,切点相当于查询条件。
切点和连接点不是一对一关系,一个切点可以匹配多个连接点。
切点只定位到某个方法上,如果希望定位到具体的连接点上,还需要提供方位信息。

增强(advice)

增强是织入到目标类连接点上的一段代码.它除用于描述一段代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,就可以找到特定的连接点了。
spring提供的增强接口都是带方位名的:BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等。

目标对象(target)

增强逻辑的织入目标类。

引介(introduction)

引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原来没有实现某个接口,通过引介,也可以动态的为业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

织入(weaving)

织入是将增强添加对目标类具体连接点的过程。aop有三种织入方式:

编译期织入,这要求使用特殊的java编译器

类装载期织入,这要求使用特殊的类装载器

动态代理织入,在运行期为目标类添加增强,生成子类

spring使用第3种方式织入,aspectj使用第1,2种方式。

代理(proxy)

一个类被aop织入增强后,就产生一个结果类,它融合了原来类和增强逻辑的代理类。我们可以采用调用原来类相同的方式调用代理类。

切面(aspect)

切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义。spring aop负责实施切面,它将切面所定义的横切逻辑织入到切面所指定的连接点钟。

创建增强类 前置增强

场景:高级餐厅的服务员在回答顾客之前都会说"你好!...".

public class Waiter {
    public void check(String name){
        System.out.println("结账?"+name);
    }
    public void serve(String name){
        System.out.println("要点什么?"+name);
    }
}

前置增强

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class GreetAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] args, Object obj)throws Throwable {
        String clientName=args[0].toString();
        System.out.println("你好!"+clientName);
    }
}

测试

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class TestBeforeAdvice {
    public static void main(String[] args){
        Waiter target=new Waiter();
        BeforeAdvice advice=new GreetAdvice();
        ProxyFactory pf=new ProxyFactory();//spring提供的代理工厂
        pf.setTarget(target);//设置代理目标
        pf.addAdvice(advice);//添加增强
        Waiter proxy=(Waiter)pf.getProxy();//代理实例
        proxy.serve("TheViper");
        proxy.check("TheViper");
    }
}

结果

你好!TheViper
来点什么?TheViper
你好!TheViper
结账?TheViper

ProxyFactory内部使用JDK代理或CGLib代理,将增强应用到目标类。

还可以将接口设置为代理目标。

...
ProxyFactory pf=new ProxyFactory();
pf.setInterfaces(target,getClass().getInterfaces);
pf.setTarget(target);
...
在spring中配置

application-context.xml



    
    
    
  

测试

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBeforeAdvice {
    public static void main(String[] args){        
        ApplicationContext ctx=new ClassPathXmlApplicationContext("application-context.xml");
        Waiter waiter=(Waiter)ctx.getBean("waiter");
        waiter.serve("TheViper");
        waiter.check("TheViper");
    }
}

ProxyFactoryBean常用配置:

target:代理的目标对象

proxyInterfaces:代理所要实现的接口,可以是多个接口。该属性还有一个别名属性interfaces

interceptorNames:需要植入目标对象的Bean列表。这些Bean必须是实现了org.aopalliance.intercept.MethodInterceptororg.springframework.aop.Advisor的Bean,配置中的顺序对应调用的顺序。

singleton:返回的代理是否为单例,默认为单例

optimize:设置为true时,强制使用CGLib代理。对于singleton代理,推荐使用CGLib,对于其他作用域类型的代理,最好使用JDK代理。因为CGLib创建代理速度慢,而创建出的代理对象运行效率较高。JDK代理的表现与之相反

proxyTargetClass:是否对类进行代理(不是对接口进行代理),设置为true时,使用CGLib

从上面spring配置可以看到,ProxyFactoryBean用的是JDK代理,如果将proxyTargetClass设置为true后,无需再设置proxyInterfaces属性,即使设置了也会被忽略。

后置增强

场景:服务员和顾客交流后,礼貌的说"please enjoy yourself".
后置增强

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class GreetAfterAdvice implements AfterReturningAdvice{
    
    @Override
    public void afterReturning(Object returnObj,Method method,Object[] args,Object obj)throws Throwable {
        //returnObj:目标实例方法返回的结果 method:目标类的方法 args:目标实例的方法参数 obj:目标类实例
        System.out.println("please enjoy yourself!");
    }
}

spring配置

...




...

结果

你好!TheViper
要点什么?TheViper
please enjoy yourself!
你好!TheViper
结账?TheViper
please enjoy yourself!
环绕增强

环绕增强允许在目标类方法调用前后织入横切逻辑,它综合实现了前置,后置增强两种的功能。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class GreetingInterceptor implements MethodInterceptor{

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] args=invocation.getArguments();
        String clientName=args[0].toString();
        System.out.println("你好!"+clientName);
        Object obj=invocation.proceed();//通过反射调用目标方法
        System.out.println("please enjoy yourself!");
        return obj;
    }
}
...



...
异常抛出增强

异常抛出增强最适合的场景是事务管理。

import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;

public class TransactionManager implements ThrowsAdvice{
    public void afterThrowing(Method method,Object[] args,Object target,Exception ex)throws Throwable{
        System.out.println("method:"+method.getName());
        System.out.println("抛出异常"+ex.getMessage());
        System.out.println("回滚");
    }
}
引介增强

引介增强不是在目标方法周围织入增强,而是为目标类创建新的方法和属性。所以引介增强的连接点是类级别的,非方法级别.

创建切面

前面织入增强时,都是织入到目标类的所有方法中。这节将会介绍如何让增强提供连接点方位的信息,如织入到方法前面,后面等,而切点进一步描述具体织入哪些类的哪些方法上。
spring通过org.springframework.aop.Pointcut接口描述切点,PointcutClassFilterMethodMatcher构成。
通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上。
此外,spring还提供注解切点和表达式切点,两者都使用AspectJ的切点表达式语言。

切点类型

静态方法切点

动态方法切点

注解切点

表达式切点

流程切点

复合切点

静态普通方法名匹配切面

StaticMethodMatcherPointcutAdvisor代表一个静态方法匹配切面,它通过StaticMethodMatcherPointcut定义切点,通过类过滤和方法名匹配定义切点。
例子,Seller类也有serve方法

public class Seller {
    public void serve(String name){
        System.out.println("seller说:要点什么?"+name);
    }
}

前置增强(advice)

public class GreetingBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] args, Object obj)throws Throwable {
        String clientName=args[0].toString();
        System.out.println("你好!"+clientName);
    }
}

切面(advisor)

import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;

public class GreetAdvisor extends StaticMethodMatcherPointcutAdvisor{
    
    @Override
    public boolean matches(Method method, Class cls) {//切点方法匹配
        return "serve".equals(method.getName());
    }
    //切点类匹配规则:Waiter的类或子类
    public ClassFilter getClassFilter(){
        return new ClassFilter(){
            public boolean matches(Class cls){
                return Waiter.class.isAssignableFrom(cls);
            }
        };
    }
}

spring配置

...








...

表示通过一个父定义公共的配置信息。
测试

ApplicationContext ctx=new ClassPathXmlApplicationContext("application-context.xml");
Waiter waiter=(Waiter)ctx.getBean("waiter");
Seller seller=(Seller)ctx.getBean("seller");
waiter.serve("TheViper");
waiter.check("TheViper");
seller.serve("TheViper");

结果

你好!TheViper
waiter说:要点什么?TheViper
waiter说:结账?TheViper
seller说:要点什么?TheViper

可以看到切面只是织入到Waiter.serve()方法调用前的连接点上,而Waiter.check()和Seller.serve()都没有织入切面。

静态正则表达式匹配切面

RegexpMethodPointcutAdvisor是正则表达式方法匹配的切面实现类。该类已经是功能齐备的的实现类了,一般情况下,无需扩展该类。

...

    
        
            .*che.*
        
    


...

这里定义了一个匹配模式.*che.*,它会匹配check()方法。

...
waiter.serve("TheViper");
waiter.check("TheViper");
...
waiter说:要点什么?TheViper
你好!TheViper
waiter说:结账?TheViper
动态切面
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut{
    private static List specialCients=new ArrayList();
    static{
        specialCients.add("Tom");//添加白名单
        specialCients.add("TheViper");
    }
    //切点类匹配规则:Waiter的类或子类
    public ClassFilter getClassFilter(){//静态匹配
        return new ClassFilter(){
            public boolean matches(Class cls){
                System.out.println("对"+cls.getName()+"类做静态检查");
                return Waiter.class.isAssignableFrom(cls);
            }
        };
    }
    public boolean matches(Method method, Class cls) {//切点方法静态匹配
        System.out.println("对"+cls.getName()+"类的"+method.getName()+"方法做静态检查");
        return "serve".equals(method.getName());
    }
    @Override
    public boolean matches(Method method, Class cls, Object[] args) {//动态匹配
        System.out.println("对"+cls.getName()+"类的"+method.getName()+"方法做动态检查");
        String clientName=args[0].toString();
        return specialCients.contains(clientName);
    }
}

匹配规则:目标类为Waiter或其子类,方法名为serve,动态传入的参数name必须在白名单中存在。

    
        
            
        
        
            
        
    
    
        Waiter waiter=(Waiter)ctx.getBean("waiter1");
        waiter.serve("Peter");
        waiter.check("Peter");
        waiter.serve("TheViper");
        waiter.check("TheViper");
对com.Waiter类做静态检查
对com.Waiter类的serve方法做静态检查
对com.Waiter类做静态检查
对com.Waiter类的check方法做静态检查
对com.Waiter类做静态检查
对com.Waiter类的clone方法做静态检查
对com.Waiter类做静态检查
对com.Waiter类的toString方法做静态检查
//上面是织入前spring对目标类中的所有方法进行的静态切点检查
对com.Waiter类做静态检查
对com.Waiter类的serve方法做静态检查
对com.Waiter类的serve方法做动态检查
waiter说:要点什么?Peter
对com.Waiter类做静态检查
对com.Waiter类的check方法做静态检查
//静态方法检查没通过,不用动态检查了
waiter说:结账?Peter
对com.Waiter类的serve方法做动态检查
//第二次调用不用执行静态检查
你好!TheViper
//动态检查,满足白名单,执行前置增强
waiter说:要点什么?TheViper
waiter说:结账?TheViper

定义动态切点时,切勿忘记同时覆盖getClassFilter()和matches(Method method,Class cls)方法,通过静态切点检查可以排除掉大部分不符合匹配规则的方法。

流程切面

spring的流程切面由DefaultPointcutAdvisorControlFlowPointcut实现。流程切点代表由某个方法直接或间接发起调用的其他方法。
定义Waiter的代理

public class WaiterDelegate {
    private Waiter waiter;
    public void setWaiter(Waiter waiter) {
        this.waiter = waiter;
    }
    public void service(String name){
        waiter.serve(name);
        waiter.check(name);
    }
}

    
    


        Waiter waiter=(Waiter)ctx.getBean("waiter2");
        WaiterDelegate wd=new WaiterDelegate();
        wd.setWaiter(waiter);
        waiter.serve("TheViper");
        waiter.check("TheViper");
        wd.service("TheViper");
waiter说:要点什么?TheViper
waiter说:结账?TheViper
//直接调用,增强不起作用
你好!TheViper
waiter说:要点什么?TheViper
你好!TheViper
waiter说:结账?TheViper
复合切面

spring提供ComposablePointcut把两个切点组合起来,通过切点的复合运算表示。
ComposablePointcut本身也是一个切点,它实现了Pointcut接口。

交集运算的方法

并集运算

import java.lang.reflect.Method;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;

public class GreetingComposablePointcut {
    public Pointcut getIntersectionPointcut(){
        ComposablePointcut cp=new ComposablePointcut();
        Pointcut pt1=new ControlFlowPointcut(WaiterDelegate.class,"service");
        MethodMatcher pt2=new NameMatchMethodPointcut(){
            public boolean matches(Method method, Class cls) {// 切点方法静态匹配
                return "check".equals(method.getName());
            }
        };
        return cp.intersection(pt1).intersection(pt2);
    }
}


#{gcp.intersectionPointcut}表示引用gcp.getIntersectionPointcut()方法返回的复合切点

        Waiter waiter=(Waiter)ctx.getBean("waiter3");
        WaiterDelegate wd=new WaiterDelegate();
        wd.setWaiter(waiter);
        waiter.serve("TheViper");
        waiter.check("TheViper");
        wd.service("TheViper");
waiter说:要点什么?TheViper
waiter说:结账?TheViper
//直接调用,增强不起作用
waiter说:要点什么?TheViper
你好!TheViper//匹配check方法
waiter说:结账?TheViper
引介切面

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64690.html

相关文章

  • &lt;spring 3.x企业应用开发实战&gt;读书笔记-基于注解和声明式的aop

    摘要:配置切面编程方式实现说结账说要点什么注解表示前置增强后面的切点表达式表示在目标类的方法织入增强,方法可以带任意的传入参数和任意的返回值。类相当于上一篇中的增强切点,切面三者联合表达的信息。 @AspectJ配置切面 编程方式实现 public class Waiter { public void check(String name){ System.out.pr...

    马龙驹 评论0 收藏0
  • Spring实战读书笔记——Spring简介

    摘要:如何降低开发的复杂性最小侵入编程通过面向接口和依赖注入实现松耦合基于编程惯例和切面进行声明式编程通过模板减少样板式代码容器在应用中,不再由对象自行创建或管理它们之间的依赖关系容器负责创建对象装配对象配置它们并管理它们的整个生命周期。 欢迎大家关注我的微信公众号,一起探讨Java相关技术 showImg(https://segmentfault.com/img/bVboaBO?w=129...

    CKJOKER 评论0 收藏0
  • 那些年我看过的书 —— 致敬我的大学生活 —— Say Good Bye !

    摘要:开头正式开启我入职的里程,现在已是工作了一个星期了,这个星期算是我入职的过渡期,算是知道了学校生活和工作的差距了,总之,尽快习惯这种生活吧。当时是看的廖雪峰的博客自己也用做爬虫写过几篇博客,不过有些是在前人的基础上写的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 开头 2017.08.21 正式开启我...

    xiaoqibTn 评论0 收藏0
  • Spring AOP就是这么简单啦

    摘要:是一种特殊的增强切面切面由切点和增强通知组成,它既包括了横切逻辑的定义也包括了连接点的定义。实际上,一个的实现被拆分到多个类中在中声明切面我们知道注解很方便,但是,要想使用注解的方式使用就必须要有源码因为我们要 前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知...

    Jacendfeng 评论0 收藏0
  • 《HTML与CSS 第一章 认识HTML》读书笔记

    摘要:一让广播明星黯然失色要建立页面,需要创建用超文本标记语言,编写的文件,把它们放在一个服务器上二服务器能做什么服务器在互联网上有一份全天候的工作。一、Web让广播明星黯然失色    要建立Web页面,需要创建用超文本标记语言(HyperText Markup Language,HTML)编写的文件,把它们放在一个Web服务器上二、Web服务器能做什么?  Web服务器在互联网上有一份全天候的工...

    番茄西红柿 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<