SpringAop实战

1、什么是AOP

与OOP对比,面向切面,传统的OOP开发中的代码逻辑是至上而下的,在这些至上而下的过程中会产生一些横切性的问题,这些横切性的问题和我们的主业务逻辑关系不大,会散落在代码的各个地方,造成难以维护,AOP的编程思想就是把业务逻辑和横切的问题进行分离,从而达到解耦的目的,使代码的重用性和开发效率高。AOP是一种思想。

aop的应用场景

  • 日志记录
  • 权限验证
  • 效率检查
  • 事务管理

1.1 spring Aop的应用

AspectJ是一个面向切面的框架,它扩展了Java语言。

  1. 如果目标对象实现了接口,Spring默认使用JDK动态代理(创建一个继承Proxy 实现目标对象接口的代理对象)
  2. 如果目标对象未实现接口,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 configxml配置
@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>
开启Spring自动代理
@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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值