SpringAop实战
1、什么是AOP
与OOP对比,面向切面,传统的OOP开发中的代码逻辑是至上而下的,在这些至上而下的过程中会产生一些横切性的问题,这些横切性的问题和我们的主业务逻辑关系不大,会散落在代码的各个地方,造成难以维护,AOP的编程思想就是把业务逻辑和横切的问题进行分离,从而达到解耦的目的,使代码的重用性和开发效率高。AOP是一种思想。
aop的应用场景
- 日志记录
- 权限验证
- 效率检查
- 事务管理
- …
1.1 spring Aop的应用
AspectJ是一个面向切面的框架,它扩展了Java语言。
- 如果目标对象实现了接口,Spring默认使用JDK动态代理(创建一个继承Proxy 实现目标对象接口的代理对象)
- 如果目标对象未实现接口,Spring默认使用CGLIB代理(创建一个目标对象的子类,即代理对象)
1.2 开启spring支持
在Spring中使用切面,需要启用Spring对AspectJ的支持,自动代理bean。通过自动代理,如果Spring确定某个bean有一个或多个切面,那么它将自动为该bean生成代理,用以拦截方法调用,确保通知执行。
java config是指基于java配置的spring。传统的Spring一般都是基本xml配置的,后来spring3.0新增了许多java config的注解,特别是spring boot,基本都是清一色的java config
java config | xml配置 |
---|---|
@Configuration(类级别) | xml配置文件 |
@Bean(方法级别) | xml配置文件中的 <bean>标签 |
@EnableAspectJAutoProxy | <aop:aspectj-autoproxy/> |
@ComponentScan | <context:componentscan basepakage=> |
@EnableWebMvc | <mvc:annotation-driven> |
@ImportResource | <import resource=“applicationContext-cache.xml”> |
maven 依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
1.3 声明一个切面
切面(用@Aspect注释的类)可以有方法和字段,与任何其他类一样。它还可以包含切入点、通知的声明
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
@Configuration //或者 @Component
public class NotVeryUsefulAspect {
}
1.4 声明一个切入点
- execution:用于匹配方法(常用)
@Pointcut("execution(* com.fanneng.service..*.*(..))") //service包下的所有方法
private void pointCut_execution() {
}
- @annotation:匹配注解(常用)
@Pointcut("@annotation(com.fanneng.annoaction.PointCutAnnoaction)") //匹配所有被注解标注的方法
private void pointCut_annoaction() {
}
- within:用于一个或一个包下的类中的所有方法(类级别)
@Pointcut("within(com.fanneng.service..*)") //匹配service包下的所有类的所有方法
private void pointCut_within() {
}
- args:表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关
@Pointcut("args(java.lang.String,..)") //第一个参数为String类型的所有方法
private void pointCut_args() {
}
- this JDK代理时,指向接口,cglib代理时 指向接口和子类(未考证)
- target 指向接口和子类(未考证)
上述所有表达式都有@ 比如@Target(里面是一个注解类xx,表示所有加了xx注解的类,和包名无关)
注意:上述所有的表达式可以混合使用,|| && !
1.5 声明一个通知
Before Advice 前置通知
@Pointcut("execution(* com.fanneng.service..*.*(..))")
private void pointCut_execution() {
}
@Before("pointCut_execution()") //pointCut_execution()为切入点
public void advice(JoinPoint joinPoint) {
System.err.println("-------Before-------");
}
After Advice 后置通知
@After("pointCut_execution()")
public void after(JoinPoint joinPoint) {
System.err.println("-------after-------");
}
After Returning Advice 返回后通知
在(After Advice 后置通知)之后
@AfterReturning("pointCut_execution()")
public void afterReturning(JoinPoint joinPoint) {
System.err.println("-------afterReturning-------");
}
After Throwing Advice 抛出后通知(捕获的异常不会通知)
@AfterThrowing("pointCut_execution()")
public void afterThrowing(JoinPoint joinPoint) {
System.err.println("-------afterThrowing-------");
}
Around Advice 环绕通知
@Around("pointCut_execution()")
public Object afterThrowing(ProceedingJoinPoint joinPoint) throws Throwable {
// 方法执行前
System.err.println("-------before-------");
Object retVal = joinPoint.proceed();
// 方法执行后
System.err.println("-------after-------");
return retVal;
}
1.6 JoinPoint Api
- getArgs(): 返回方法参数
- getThis(): 返回代理对象
- getTarget(): 返回目标对象
- getSignature(): 返回方法的描述
2、@EnableAspectJAutoProxy配置注意事项
2.1 AopProxy
Spring对代理的抽象接口为:AopProxy
- 实现一:JdkDynamicAopProxy
- 实现二:CglibAopProxy
2.2 proxyTargetClass 作用
@EnableAspectJAutoProxy(proxyTargetClass = false) //默认
proxyTargetClass = false (常用)
目标类实现了接口,使用jdk动态代理,否则使用CGLIB
proxyTargetClass = true
强制使用CGLIB动态代理
2.3 exposeProxy 作用
@EnableAspectJAutoProxy(exposeProxy = false) //默认
是否暴露代理对象(代码底层 将代理对象放入ThreadLocal中)
问题代码(内部方法调用 事务失效)
@Service
public class MyServiceImpl implements MyService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void get() {
}
@Transactional(propagation = Propagation.REQUIRED)
public String put(String s) {
/**
* this.get(); 将不再事务中运行
* 因为调用者为目标对象,而非代理对象
*/
this.get();
return s;
}
}
正确写法:
exposeProxy 设置为 true
@EnableAspectJAutoProxy(exposeProxy = true)
AopContext.currentProxy()
@Service
public class MyServiceImpl implements MyService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void get() {
System.err.println("----------get------------");
}
@Transactional(propagation = Propagation.REQUIRED)
public String put(String s) {
/**
* 使用代理对象调用内部方法
*/
((MyService) AopContext.currentProxy()).get();
return s;
}
}