简单例子
待增强的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的形式。