Spring AspectJ和Schema的AOP

简单例子

待增强的bean

package com.smart;

import com.smart.anno.NeedTest;

public class NaiveWaiter implements Waiter {
	public void greetTo(String clientName) {
		System.out.println("NaiveWaiter:greet to "+clientName+"...");
	}	
}

前置增强

package com.smart.aspectj.example;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class PreGreetingAspect{
	@Before("execution(* greetTo(..))")
	public void beforeGreeting(){
		System.out.println("How are you");
	}
}

增强

package com.smart.aspectj.example;

import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

import com.smart.NaiveWaiter;
import com.smart.Waiter;
import org.testng.annotations.Test;

public class AspectJProxyTest {

	@Test
	public void proxy(){
		Waiter target = new NaiveWaiter();
		AspectJProxyFactory factory = new AspectJProxyFactory();
		factory.setTarget(target);
		factory.addAspect(PreGreetingAspect.class);
		Waiter proxy = factory.getProxy();
		proxy.greetTo("John");
		proxy.serveTo("John");
	}
}

如果配置xml

<?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-4.0.xsd">
  <!-- 注意前面要配置xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd-->
  <!-- 自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面 -->
  <aop:aspectj-autoproxy/>
  <!-- 若不配置xmlns:aop,则可以使用这个 -->
  <!--bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/-->
  <bean id="waiter" class="com.smart.NaiveWaiter" />
  <bean class="com.smart.aspectj.example.PreGreetingAspect" />
</beans>

@AspectJ语法

切点表达式函数

在这里插入图片描述

spirng中aspectJ可以使用and、or、not

not位于切点表达式开头,必须加空格,比如"not within(com.smart.)“是不对的,要这样” not within(com.smart.)"

不同增强类型

在这里插入图片描述
在这里插入图片描述

引介增强的使用:

package com.smart.aspectj.basic;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

import com.smart.Seller;
import com.smart.SmartSeller;


@Aspect
public class EnableSellerAspect {
    //给com.smart.NaiveWaiter引入一个Seller接口定义的sell方法,实现类是com.smart.SmartSeller
    @DeclareParents(value = "com.smart.NaiveWaiter", defaultImpl = SmartSeller.class)
    public Seller seller;
}

package com.smart;

import com.smart.anno.NeedTest;

public class NaiveWaiter implements Waiter {
	public void greetTo(String clientName) {
		System.out.println("NaiveWaiter:greet to "+clientName+"...");
	}	
}
package com.smart;

public class SmartSeller implements Seller {

	public int sell(String goods,String clientName) {
		System.out.println("SmartSeller: sell "+goods +" to "+clientName+"...");
		return 100;
	}
	
	public void checkBill(int billId){
		if(billId == 1) throw new IllegalArgumentException("iae Exception");
		else throw new RuntimeException("re Exception");
	}
}

切点函数说明

“*”:匹配上下文中的任意的一个元素(一个单词)

“…”:表示匹配任意字符,在表示方法入参的时候可以直接用,在表示类的时候,需要和“*”连用

“+”:表示匹配所有类及其子类的所有方法,如果是接口,则表示实现这个接口的所有类的所有方法。

“.*”:表示匹配包下所有类的所有方法。

“…*”:表示匹配包、子孙包下所有类的所有方法。

“.* ”和“…”区别:比如*com.smart.*(…)表示包下所有类的所有方法,但是不包括子孙包下的所有方法,比如com.smart.dao包下的类的方法就无法匹配,但是“…”就可以匹配。

execution()和@annotation

execution()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:execition(* com…*.*Dao.find*(…))这个,其中…*.*中的.*不是表示".*"的意思,而是表示一个’’.’‘符号,和一个’’*'符号。

@annotation

定义一个标签

package com.smart.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NeedTest{
	boolean value() default false;
}

给要增强的方法打上标签

@NeedTest
public void serveTo(String clientName){
  System.out.println("NaiveWaiter:serving "+clientName+"...");
}

增强定义为这个标签

@AfterReturning("@annotation(com.smart.anno.NeedTest)")
public void atAnnotaionTest(){
  System.out.println("atAnnotaionTest() executed!");
}

如果打在类上,则该类所有的方法都会增强。

args()和@args()

args(com.smart.Waiter)相当于args(com.smart.Waiter+),匹配入参为com.smart.Waiter的函数及其之类

@args()的用法稍微复杂,简单来说,如果定义的标签是com.smart.Monitorable,并且定义@args(com.smart.Monitorable)时,Waiter类有两个继承类NaiveWaiter和NaughtyWaiter,如果给NaiveWaiter打上com.smart.Monitorable标签,方法为add(Waiter w)时,当w时NaiveWaiter时,匹配切点,如果是NaughtyWaiter和Waiter则不行。如果给Waiter打上标签com.smart.Monitorable,则方法add(NaiveWaiter w)方法不匹配,如果方法是add(Waiter w),则w为三个都匹配。

within()和@within()和@target()

within匹配的最小粒度是类,注意不能是接口,比如@within(com.smart.*)匹配com.smart包中所有类的所有方法,但是不包括子类。

@target()只匹配打了标签的类,不匹配子类,比如定义一个标签com.smart.Monitorable,定义@target(com.smart.Monitorable),Waiter类有继承类NaiveWaiter,那么给Waiter类打上标签@Monitorable,则匹配Waiter类的所有方法,不匹配NaiveWaiter类的所有方法。

@within匹配打了标签的类和其所有子类。

target()和this()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AB3XY8lf-1631458399731)(AspectJ%E5%92%8CSchema%E7%9A%84AOP.assets/image-20210422165915971.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E3pwswQY-1631458399732)(AspectJ%E5%92%8CSchema%E7%9A%84AOP.assets/image-20210422170058314.png)]

AspectJ进阶

切点复合运算

可使用&&,!,||或and,not,or,注意!和not使用的时候前面要加空格

比如within(com.smart.*) && excution(* greetTo(…))

命名切点(取别名)

可以给切点取个别名,使用@Pointcut标签,比如:

public class TestNamePointcut {
  //给within(com.smart.*)取别名inPackage
	@Pointcut("within(com.smart.*)")
	private void inPackage(){}
  //同理
	@Pointcut("execution(* greetTo(..)))")
    protected void greetTo(){}
	//可以组合之前定义的别名
  @Pointcut("inPackage() and greetTo()")
  	public void inPkgGreetTo(){}
}

使用:

//使用完整全类名
@Before("TestNamePointcut.inPkgGreetTo()")
public void pkgGreetTo(){
	System.out.println("--pkgGreetTo() executed!--");
}

获得连接点信息

如果想get到被增强的函数的信息,比如入参啊,函数名啊,增强的对象啊,可以使用org.aspectj.lang.JoinPoint在增强的入参处定义JoinPoint joinpoint,例如:

@Aspect
public class PreGreetingAspect{
	@Before("execution(* greetTo(..))")
	public void beforeGreeting(JoinPoint joinPoint){
    //入参
		System.out.println(Arrays.toString(joinPoint.getArgs()));
    //函数名
		System.out.println(joinPoint.getSignature());
    //getTarget和getThis都是一样的,获得增强的对象
		System.out.println(joinPoint.getTarget());
		System.out.println(joinPoint.getThis());
	}
}

如果是环绕增强,则使用org.aspectj.lang.ProceedingJoinPoint,例如:

	@Around("execution(* greetTo(..)) && target(com.smart.NaiveWaiter)")
	public void joinPointAccess(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("------joinPointAccess-------");
		System.out.println("args[0]:"+pjp.getArgs()[0]);
		System.out.println("signature:"+pjp.getTarget().getClass());
    //执行方法,如果有返回值,则得到返回值,可以强转。
		Object proceed = pjp.proceed();
		System.out.println("-------joinPointAccess-------");
	}

绑定入参

其实没有很大的必要,因为可以直接使用前面所说的org.aspectj.lang.JoinPoint获得连接点信息,然后get到入参,但是不妨碍了解一下。

使用args()不仅可以绑定入参的类型,还可以将入参绑定到增强,例如:

在这里插入图片描述

必须启用CGLib代理

绑定被代理的对象

使用this()或target()绑定被代理对象的实例

在这里插入图片描述

绑定注解对象

这个不清楚意义在哪,可以get到打了标签的类的标签?

在这里插入图片描述

绑定返回值

@AfterReturning(value="target(com.smart.SmartSeller)",returning="retVal")
public void bingReturnValue(int retVal){
  System.out.println("----bingReturnValue()----");
  System.out.println("returnValue:"+retVal);
  System.out.println("----bingReturnValue()----");
}

绑定抛出的异常

@AfterThrowing(value="target(com.smart.SmartSeller)",throwing="iae")
public void bindException(IllegalArgumentException iae){
  System.out.println("----bindException()----");
  System.out.println("exception:"+iae.getMessage());
  System.out.println("----bindException()----");
}

注意,这个只会get到IllegalArgumentException异常,如果想要通用的,使用Exception

schema配置切面

之前都是使用@AspectJ注解切面,如果想用xml配AspectJ的AOP,则使用schema的配置方式,其实就是将刚刚的注解使用了xml进行定义,这种不需要配置任何注解,注意和之前的注解配置AOP的区分。

简单例子

定义一下前置增强

public class AdviceMethods {
	public void preGreeting(String name) {
		System.out.println("--how are you!--");
		System.out.println(name);
	}
}

被增强的类

package com.smart;

public class NaughtyWaiter implements Waiter {
	public void greetTo(String clientName) {
		System.out.println("NaughtyWaiter:greet to "+clientName+"...");
	}	
	public void serveTo(String clientName){
		System.out.println("NaughtyWaiter:serving "+clientName+"...");
	}
	public void joke(String clientName,int times){
        	System.out.println("NaughtyWaiter:play "+times+" jokes to "+clientName+"...");
	}
}

配置一下xml,定义一下增强以及切点,再定义一下要被增强类的bean,就OK了。

<?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-4.0.xsd">
	<aop:config proxy-target-class="true">
		<!-- 增强的类 -->
		<aop:aspect ref="adviceMethods">
      <!-- 增强类中的方法 -->
			<aop:before method="preGreeting"
				pointcut="target(com.smart.NaiveWaiter) and execution(* greetTo(..))"/>
		</aop:aspect>
	</aop:config>
	<bean id="adviceMethods" class="com.smart.schema.AdviceMethods" />
	<bean id="naughtyWaiter" class="com.smart.NaughtyWaiter" />
</beans>

配置切点

上面的例子里,将增强和切点直接配置在了一起,这样写不利于切点的重用,可以将切点单独抽取出来。

还是上面那个例子,改造一下:

<?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-4.0.xsd">
	<aop:config proxy-target-class="true">
    <!-- 配置切点 -->
     <aop:pointcut id="greetToPointCut" expression="target(com.smart.NaiveWaiter) and execution(* greetTo(..))""/>                                                                               
		<!-- 增强的类 -->
		<aop:aspect ref="adviceMethods">
      <!-- 增强类中的方法 -->
			<aop:before method="preGreeting" pointcut-ref="greetToPointCut"/>
		</aop:aspect>
    

	</aop:config>
	<bean id="adviceMethods" class="com.smart.schema.AdviceMethods" />
	<bean id="naughtyWaiter" class="com.smart.NaughtyWaiter" />
</beans>

注意,在<aop:config>后是<aop:pointcut>,再是<aop:advisor>,最后是<aop:aspect>,顺序不能乱!

各种增强

package com.smart.schema;

import org.aspectj.lang.ProceedingJoinPoint;


public class AdviceMethods {
	public void preGreeting(String name) {
		System.out.println("--how are you!--");
		System.out.println(name);
	}

    //后置增强对应方法
	public void afterReturning(int retVal){
	   System.out.println("----afterReturning()----");
	   System.out.println("returnValue:"+retVal);
	   System.out.println("----afterReturning()----");
	}

    //环绕增强对应方法
	public void aroundMethod(ProceedingJoinPoint pjp){
	   System.out.println("----aroundMethod()----");
	   System.out.println("args[0]:"+pjp.getArgs()[0]);
	   System.out.println("----aroundMethod()----");
	}

    //抛出异常增强
	public void afterThrowingMethod(IllegalArgumentException iae){
	   System.out.println("----afterThrowingMethod()----");
	   System.out.println("exception msg:"+iae.getMessage());
	   System.out.println("----afterThrowingMethod()----");
	}

    //final增强
	public void afterMethod(){
	   System.out.println("----afterMethod()----");
	}
	

	
      //------------绑定连接点参数----------//
	public void bindParams(int num,String name){
	   System.out.println("----bindParams()----");
	   System.out.println("name:"+name);
	   System.out.println("num:"+num);
	   System.out.println("----bindParams()----");
	}
}
	<aop:config proxy-target-class="true">
		<aop:aspect ref="adviceMethods">
			<aop:before method="preGreeting"
				pointcut="target(com.smart.NaiveWaiter) and args(name)"
				arg-names="name" />
			<aop:after-returning method="afterReturning"
				pointcut="target(com.smart.SmartSeller)" returning="retVal" />
			<aop:around method="aroundMethod"
				pointcut="execution(* serveTo(..)) and within(com.smart.Waiter)" />
			<aop:after-throwing method="afterThrowingMethod"
				pointcut="target(com.smart.SmartSeller) and execution(* checkBill(..))"
				throwing="iae" />
			<aop:after method="afterMethod"
				pointcut="execution(* com..*.Waiter.greetTo(..))" />
      <!-- 引介增强 -->
			<aop:declare-parents
				implement-interface="com.smart.Seller"
				default-impl="com.smart.SmartSeller"
				types-matching="com.smart.Waiter+" />
       <aop:before method="bindParams" 
                   pointcut="target(com.smart.NaiveWaiter) and args(name,num,..)"/>
		</aop:aspect>
	</aop:config>

Advisor配置

就是切面,将增强和切点绑定在一起,可以直接定义<aop:advisor>,而不需要定义<aop:aspect>和<aop:pointcut>

接口MethodBeforeAdvice实现后,可以不用绑定相关参数,直接重写before方法就可以得到切点的信息,这个在之前增强里面已经说明。

package org.springframework.aop;

import java.lang.reflect.Method;

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method var1, Object[] var2, Object var3) throws Throwable;
}

定义增强

package com.smart.schema;
import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;
public class TestBeforeAdvice implements MethodBeforeAdvice {

	public void before(Method method, Object[] args, Object target)
			throws Throwable {
        System.out.println("------TestBeforeAdvice------");
        System.out.println("clientName:"+args[0]);
        System.out.println("------TestBeforeAdvice------");
	}
}
<!-- 定义增强的bean -->
<bean id="testAdvice" class="com.smart.schema.TestBeforeAdvice"/>
<aop:config proxy-target-class="true">
   <aop:advisor advice-ref="testAdvice"  pointcut="execution(* com..*.Waiter.greetTo(..))"/>
</aop:config>

这种方法就是混合了之前的aop和aspectj的形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值