1 切点复合运算
 
支持在切点定义中加入以下运算符进行复合运算:
 
 
  
   
   | 运算符 | 说明 | 
 
  
  
   
   | && | 与运算。 | 
 
   
   | ! | 非运算。 | 
 
   
   | || | 或运算。 | 
 
  
 
2 切点命名
 
一般情况下,切点是直接声明在需要增强方法处,这种切点的声明方式称为匿名切点,匿名切点只能在声明处被使用 。  如果希望在其它地方可以重用这个切点,我们可以通过 @Pointcut 注解及切面类方法来命名它。
 
public class NamePointcut {
    /**
     * 切点被命名为 method1,且该切点只能在本类中使用
     */
    @Pointcut("within(net.deniro.spring4.aspectj.*)")    private void method1() {
    }    /**
     * 切点被命名为 method2,且该切点可以在本类或子孙类中使用
     */
    @Pointcut("within(net.deniro.spring4.aspectj.*)")    protected void method2() {
    }    /**
     * 切点被命名为 method3,且该切点可以在任何类中使用
     * 这里还使用了复合运算
     */
    @Pointcut("method1() && method2()")    public void method3() {
    }
} 
命名切点的结构如下:
 

 
切点可访问性修饰符与类可访问性修饰符的功能是相同的,它可以决定定义的切点可以在哪些类中可使用。
 
因为命名切点仅利用方法名及访问修饰符的信息,所以我们一般定义方法的返回类型为 void ,并且方法体为空 。
 
定义好切点后,就可以在切面类中引用啦:
 
@Aspectpublic class NamePointcutAspect {    @After("NamePointcut.method2()")    public void aspectMethod1() {
    }    /**
     * 这里使用了复合运算
     */
    @After("NamePointcut.method2() && NamePointcut.method3()")    public void aspectMethod2() {
    }
} 
3 织入顺序
 
一个连接点可以同时匹配多个切点,而切点所对应的增强在连接点上织入顺序的规则是这样的:
 
1.如果在同一个切面类中声明的增强,则按照增强在切面类中定义的顺序进行织入;
 
org.springframework.core.Ordered
org.springframework.core.Ordered
 
假设有两个切面类 A 与 B,它们都实现了 Ordered 接口,A 的顺序号为 1,B 的顺序号为 2,切面类 A 与 B 都定义了 3 个增强,那么同时匹配这 6 个增强的织入顺序如下图所示:
 

 
4 获取连接点信息
 
4.1 JoinPoint
 
org.aspectj.lang.JoinPoint 接口表示目标类连接点对象,它定义这些主要方法。
 
 
  
   
   | 方法 | 说明 | 
 
  
  
   
   | Object[] getArgs() | 获取连接点方法运行时的入参列表。 | 
 
   
   | Signature getSignature() | 获取连接点的方法签名对象。 | 
 
   
   | Object getTarget() | 获取连接点所在的目标对象。 | 
 
   
   | Object getThis() | 获取代理对象。 | 
 
  
 
4.2 ProceedingJoinPoint
 
org.aspectj.lang.ProceedingJoinPoint 继承了 JoinPoint 接口,它新增了两个方法(它们用于执行连接点方法)。
 
 
  
   
   | 方法 | 说明 | 
 
  
  
   
   | Object proceed() throws Throwable | 通过反射执行目标对象连接点处的方法。 | 
 
   
   | Object proceed(Object[] var1) throws Throwable | 使用新的入参(替换掉原来的入参),通过反射执行目标对象连接点处的方法。 | 
 
  
 
4.3 示例
 
Cook 接口:
 
public interface Cook {    /**
     * 制作食品
     */
    void make();    /**
     * 制作
     *
     * @param name 食品名称
     */
    void make(String name);
} 
CookA 类:
 
public class CookA implements Cook {    public void make() {
        System.out.println("制作食品");
    }    public void make(String name) {
        System.out.println("制作" + name);
    }
} 
在切面类中访问连接点信息:
 
@Aspectpublic class JoinPointAspect {    @Around("within(net.deniro.spring4.aspectj.CookA)")    public void test(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("---------获取连接点对象【开始】---------");
        System.out.println("参数:" + pjp.getArgs()[0]);
        System.out.println("签名对象:" + pjp.getTarget().getClass());        //执行目标对象方法
        pjp.proceed();
        System.out.println("---------获取连接点对象【结束】---------");
    }
} 
Spring bean 配置:
 
<?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: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.xsd">
    <!--aspectj 驱动器 -->
    <aop:aspectj-autoproxy/>
    <bean id="cookA" class="net.deniro.spring4.aspectj.CookA"/>
    <bean class="net.deniro.spring4.aspectj.JoinPointAspect"/></beans> 
输出结果:
 
---------获取连接点对象【开始】---------
 
参数:寿司
 
签名对象:class net.deniro.spring4.aspectj.CookA
 
制作寿司
 
---------获取连接点对象【结束】---------
 
分享一些知识点给大家希望能帮助到大家,或者从中启发。
 
加Q君羊:821169538 都是java爱好者,大家一起讨论交流学习
 
5 绑定连接点的方法入参
 
args()、this()、target()、@args()、@within()、@target() 和 @annotation()  这些切点函数除可以指定类名外,还可以指定参数名,将目标对象连接点上的方法入参绑定到增强的方法中 。 其中 args()  用于绑定连接点方法的入参, @annotation() 用于绑定连接点方法的注解对象,而 @args() 用于绑定连接点方法入参的注解。
 
CookC 类:
 
public class CookC implements Cook {    public void make() {
        System.out.println("制作食品");
    }    public void make(String name) {
        System.out.println("制作" + name);
    }    public void make(String name, int num) {
        System.out.println("制作" + name + " " + num + " 个");
    }
} 
切面类:
 
@Aspectpublic class ParamsAspect {    @Before("target(net.deniro.spring4.aspectj.CookC) && args(name,num,..)")    public void test(String name,int num) {
        System.out.println("----------绑定连接点入参【开始】----------");
        System.out.println("name:" + name);
        System.out.println("num:" + num);
        System.out.println("----------绑定连接点入参【结束】----------");
    }
} 
 
 -  这里的连接点表达式 args(name,num,..)会先找到 name 与 num 的类型,从而生成真正的表达式args(String,int,..)。
 
-  增强方法可以通过 name 与 num 得到连接点的方法入参。 
切点匹配和参数绑定的过程是这样的:
 
args()
args()
 
上述示例中的匹配过程如下:
 

 
Spring 配置:
 
<!--aspectj 驱动器 --><aop:aspectj-autoproxy proxy-target-class="true"/><bean id="cookC" class="net.deniro.spring4.aspectj.CookC"/><bean class="net.deniro.spring4.aspectj.ParamsAspect"/>
 
注意:这里必须通过 <aop:aspectj-autoproxy proxy-target-class="true" /> 来启用 CGLib 动态代理,这是因为 CookC 的 public void make(String name, int num) 是该类独有的方法(非接口定义的方法),所以必须使用 CGLib 生成子类的代理方法 。
 
单元测试:
 
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookC cookC = (CookC) context.getBean("cookC");
cookC.make("寿司", 100); 
输出结果:
 
----------绑定连接点入参【开始】----------
 
name:寿司
 
num:100
 
----------绑定连接点入参【结束】----------
 
制作寿司 100 个
 
6 绑定代理对象
 
使用 this() 或 target() 可绑定被代理对象的实例。通过类实例名绑定对象时,依然具有原来连接点匹配的功能,只是类名是由增强方法中的同名入参类型间接决定的。
 
@Aspectpublic class ProxyAspect {
    @Before("this(cook)")
    public void bind(Cook cook) {        System.out.println("--------绑定代理对象【开始】--------");        System.out.println(cook.getClass().getName());        System.out.println("--------绑定代理对象【结束】--------");
    }
} 
首先通过 public void bind(Cook cook) 找出 cook 所对应的类型,接着转换切点表达式为 this(net.deniro.spring4.aspectj.Cook) 。这样就表示该切点匹配所有代理对象为 Cook 类中的所有方法。
 
输出结果:
 
--------绑定代理对象【开始】--------
 
net.deniro.spring4.aspectj.CookC$$EnhancerBySpringCGLIB$$217fb793 
 
--------绑定代理对象【结束】--------
 
target() 绑定与 this() 相似。
 
7 绑定类注解对象
 
@within()  和 @target() 函数都可以将目标类的注解对象绑定到增强方法中。
 
定义一个日志注解类:
 
@Retention(RetentionPolicy.RUNTIME)//保留期限@Target({ElementType.METHOD,ElementType.TYPE})//目标类型public @interface Log {    boolean value() default true;//声明成员变量} 
把该注解类应用于 CookD:
 
@Logpublic class CookD implements Cook {    public void make() {
        System.out.println("制作糕点");
    }    public void make(String name) {
    }
} 
绑定类注解对象:
 
@Aspectpublic class ClassAnnotationObjectAspect {
    @Before("@within(log)")
    public void bind(Log log){        System.out.println("----------绑定类注解对象【开始】----------");        System.out.println(log.getClass().getName());        System.out.println("----------绑定类注解对象【结束】----------");
    }
} 
Spring 配置:
 
<!--aspectj 驱动器 --><aop:aspectj-autoproxy proxy-target-class="true"/><bean id="cookD" class="net.deniro.spring4.aspectj.CookD"/><bean class="net.deniro.spring4.aspectj.ClassAnnotationObjectAspect"/>
 
单元测试:
 
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookD cook = (CookD) context.getBean("cookD");
cook.make(); 
输出结果:
 
----------绑定类注解对象【开始】----------
 
com.sun.proxy.$Proxy8
 
----------绑定类注解对象【结束】----------
 
从输出结果 com.sun.proxy.$Proxy8 可以看出 ,CookD 类的注解 Log 对象也被代理咯O(∩_∩)O哈哈~
 
8 绑定返回值
 
在后置增强中,可以通过 returning 来绑定连接点方法的返回值。
 
切面:
 
@Aspectpublic class ReturnValueAspect {    @AfterReturning(value = "target(net.deniro.spring4.aspectj.CookA)", returning = "value")    public void bind(boolean value) {
        System.out.println("绑定返回值【开始】");
        System.out.println("value:" + value);
        System.out.println("绑定返回值【结束】");
    }
} 
注意:returning  的值必须与方法参数名相同。
 
CookA 新增 smell 方法:
 
public boolean smell(String name) {
    System.out.println(name + "香吗?");    return true;
} 
单元测试:
 
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookA cook = (CookA) context.getBean("cookA");
cook.smell("烤鸭"); 
输出结果:
 
烤鸭香吗?
 
绑定返回值【开始】
 
value:true
 
绑定返回值【结束】
 
9 绑定异常
 
可以使用 AfterThrowing 注解的 throwing 成员变量来绑定连接点抛出的异常。
 
切面类:
 
@Aspectpublic class ExceptionAspect {
    @AfterThrowing(value = "target(net.deniro.spring4.aspectj.CookA)",throwing = "e")
    public void bind(CookException e){        System.out.println("绑定异常【开始】");        System.out.println("e:" + e.getMessage());        System.out.println("绑定异常【结束】");
    }
} 
注意:throwing 的值必须与方法参数名相同。
 
单元测试:
 
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookA cook = (CookA) context.getBean("cookA");
cook.make(""); 
输出结果:
 
绑定异常【开始】
 
e:煮啥呢???
 
绑定异常【结束】