Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在“内存中临时生成代理类”的方式来生成 AOP 代理。但在性能上相比较于编译是增强的AOP框架(AspectJ )稍差些。反编译spring AOP目标类的字节码,是没有发生改变的!
Spring AOP 大致执行流程:
Spring AOP的基本概念:
1. 目标对象 : 自己写的类,想要增强的类。
2. AOP代理啊 : 经过代理的类,实现增强。由AOP框架生成。
3. AOP代理方法 : Advice + 目标方法 + Advice.
4. Advice : 增强处理。
5. pointcut : 切入点。
配置spring AOP的三个基本要素:
1. 在目标方法的哪里织入(before、after、after turn、after throwing、around)。
2. 通过execution表达式,指定对哪个类的哪个方法进行织入。
3. 指定织入的代码。
示例代码:
目标类:
public class UserService {
private UserDao dao;
public void test() {
System.out.println("test method run . . . ");
}
public void setDao(UserDao dao) {
System.out.println("setDao method run . . . ");
this.dao = dao;
}
public long getUserTotal() {
System.out.println("getUserTotal method run . . . ");
return 100;
}
public String getUserName(Integer id) {
System.out.println("getUserName method run . . . ");
if(id <= 0) {
throw new NullPointerException("id不可以小于0");
}
return "haha";
}
}
before织入方法:
public class BeforeAdvice {
public void check() {
System.out.println("-------权限检查-------");
}
public void parameCheck(Integer arg) {
System.out.println("--------权限检查,获得目标方法的参数:" + arg + "---------");
}
}
before配置:
<bean id="before" class="spring.demo.aop.advice.BeforeAdvice"/>
<aop:config>
<!-- 对【无返回值,无形参】的方法进行织入-->
<aop:pointcut id="widhoutR_withoutP"
expression="execution(void spring.demo.service.UserService.test())"/>
<!-- 对【无返回值,有形参】的方法进行织入 -->
<aop:pointcut id="widhoutR_P"
expression="execution(void spring.demo.service.UserService.setDao(spring.demo.dao.UserDao))" />
<!-- 对【有返回值,无形参】的方法进行织入 -->
<aop:pointcut id="R_widhoutP"
expression="execution(long spring.demo.service.UserService.getUserTotal())"/>
<!-- 对【有返回值,有形参】的方法进行织入指定方法 -->
<aop:pointcut id="R_P"
expression="execution(String spring.demo.service.UserService.getUserName(Integer)) and args(arg)"/>
<aop:aspect ref="before">
<aop:before method="check" pointcut-ref="widhoutR_withoutP"/>
<aop:before method="check" pointcut-ref="widhoutR_P"/>
<aop:before method="check" pointcut-ref="R_widhoutP"/>
<aop:before method="parameCheck" pointcut-ref="R_P"/>
</aop:aspect>
</aop:config>
1. 在<aop:config>中。
2. 在<aop:aspect>中。
3. 在<aop:before pointcut="...">
after织入方法:
public class AfterAdvice {
public void loginfo() {
System.out.println("-------日志记录--------\n");
}
}
after织入配置:
<bean id="after" class="spring.demo.aop.advice.AfterAdvice"/>
<aop:config>
<!-- 对UserService类中的所有方法进行织入 -->
<aop:pointcut id="after_exe"
expression="execution(* spring.demo.service.UserService.*(..))"/>
<aop:aspect ref="after">
<aop:after method="loginfo" pointcut-ref="after_exe"/>
</aop:aspect>
</aop:config>
after throwing织入方法:
public class ExceptionAdvice {
public void printNullPointerMessage(NullPointerException npe) {
System.out.println("-------出空指针异常 : " + npe.getMessage() + "-------");
}
public void printMessage() {
System.out.println("-------出异常-------");
}
}
after throwing配置:
<aop:config>
<aop:pointcut id="exception_exe"
expression="execution(* spring.demo.service.UserService.*(..))"/>
<!-- 对所有异常织入 -->
<aop:aspect ref="exception">
<aop:after-throwing method="printMessage" pointcut-ref="exception_exe"/>
</aop:aspect>
<!-- 对指定的异常throwing织入 -->
<aop:aspect ref="exception">
<aop:after-throwing method="printNullPointerMessage" pointcut-ref="exception_exe" throwing="npe"/>
</aop:aspect>
</aop:config>
after returning织入方法:
public class ReturnAdvice {
public void returning() {
System.out.println("------对没有抛出异常,有返回值的方法织入---------");
}
//此方法的参数类型和目标方法的返回值类型要相同
public void afterReturning(long userTotal) {
System.out.println("------对没有抛出异常,返回值为long的方法织入---------");
}
}
after returning配置:
<bean id="returning" class="spring.demo.aop.advice.ReturnAdvice"/>
<aop:config>
<aop:pointcut id="returning_exe"
expression="execution(* spring.demo.service.UserService.*(..))"/>
<aop:aspect ref="returning">
<!-- 对所有返回值的方法织入 -->
<aop:after-throwing method="returning" pointcut-ref="returning_exe"/>
<!-- 对所有返回long类型的方法织入 -->
<aop:after-returning method="afterReturning" pointcut-ref="returning_exe" returning="userTotal"/>
</aop:aspect>
</aop:config>
around织入方法:
public class AroundAdvice {
@SuppressWarnings({ "null" })
public Object advice(ProceedingJoinPoint pj) throws Throwable {
System.out.println("目标方法的签名 : " + pj.getSignature());
Object[] args = pj.getArgs();
//可以获得目标方法的参数,也可以对其进行处理
System.out.println("目标方法的原参数 : " + Arrays.toString(args));
args[0] = (Integer)args[0] + 100;
Object result;
//可以决定是否执行目标方法
if(args == null && args.length == 0) {
result = pj.proceed();
} else {
result = pj.proceed(args);
}
//也可以对目标方法的返回值进行修改,但是必须要和原方法的返回值类型一样
result = (String)result + " world " + Arrays.toString(args);
System.out.println("目标方法执行结束");
return result;
}
}
around配置:
<bean id="around" class="spring.demo.aop.advice.AroundAdvice"/>
<aop:config>
<aop:pointcut id="around_exe"
expression="execution(* spring.demo.service.UserService.getUserName(..))"/>
<aop:aspect ref="around">
<aop:around method="advice" pointcut-ref="around_exe"/>
</aop:aspect>
</aop:config>