什么是AOP切面编程

AOP(Aspect Oriented Programming)称为面向切面编程,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限,安全控制,等待,性能统计,异常处理等等。Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。

AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

Spring支持两种方法,那么我们在使用spring进行动态代理时究竟使用的哪一种方法呢?
spring优先支持实现接口的方式,如果没有接口则使用cglib方式。

我就使用注解来举例,所需要的maven依赖如下

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
	<dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>     
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        ">
    <!--扫描包下所有的注解-->
    <context:component-scan base-package="com.luo.spring.log,com.luo.spring.test"/>
    <!--让spring来自动代理-->
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

AOP的5个通知注解

@before:前置通知
@after: 后置通知
@AfterReturning: 返回通知
@AfterThrowing:异常通知
@Around:环绕通知

execution(访问权限修饰符 返回值 方法权限定类名(参数))

通配符: * 匹配单个或多个字符,中间是*,单层路径,开头就是*,代表任意层路径
通配符: . .任意方法参数,任意路径

执行的顺序

正常执行:before --> after --> afterReturning
异常执行:before --> after --> afterThrowing

如果有环绕通知,顺序改变

环绕前置 /普通前置 -->目标方法执行 --> 环绕正常返回/出现异常–> 环绕后置-> 普通后置–> 普通返回/出现异常

注册进入IOC容器,并且让spring知道这是一个切面类

@Aspect
@Component
public class LogUtils {
 @Before("execution(* com.luo.spring.log.EatRice.*(..))")
    public void print(JoinPoint joinPoint){
        //JoinPoint 封住了目标方法的详细信息
        //获取目标方法的参数
        Object[] args = joinPoint.getArgs();
        //获取到方法签名
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("Before....");
    }

	//告诉spring哪个参数是用来接受返回值
    @AfterReturning(value = "execution(* com.luo.spring.log.EatRice.*(..))",returning = "result")
    public void print01(JoinPoint joinPoint,Object result){
        System.out.println("AfterReturning.....方法返回值为:"+result);
    }
    
	//告诉spring哪个参数是用来接受异常
    @AfterThrowing(value = "execution(* *.log.*.*(..))",throwing = "e")
    public void print02(JoinPoint joinPoint,Exception e){
       System.out.println("AfterThrowing......异常信息为:"+e );
    }
}

@Around(value = "execution(* com.luo.spring.log.EatRice.*(..))")
    public Object print03(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = null;
        try {
            System.out.println("前置通知");
            //proceed()可以不用参数,传参数可以修改参数
           result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
            System.out.println("返回通知.....");
        }catch (Exception e){
            System.out.println("异常通知");
            System.out.println(e.getCause());
        }finally {
            System.out.println("后置通知");
        }
        return result;
    }

每一个注解都有切入点表达式,如果要修改切入的具体位置,每一个都要修改,很是麻烦,因此,可以把它抽取出来

	//抽取可重用的切入点表达式
    @Pointcut(value = "execution(* com.luo.spring.log.EatRice.*(..))")
    public void print6(){

    }
    //这里直接填方法名
    @Before("print6()")
    public void print(){
        System.out.println("Before....");
    }

下面是基于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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        " >

    <bean id="loggerTest" class="dao.LoggerTest"/>

    <bean id="loggerDemo" class="dao.LoggerDemo"/>

    <aop:config>
        <!--切面类-->
        <aop:aspect ref="loggerDemo">
            <!--切入点-->
            <aop:pointcut  expression="execution(* *.*(..))" id="aa"/>
            <!--通知-->
            <aop:after method="after" pointcut-ref="aa"/>
        </aop:aspect>
    </aop:config>

</beans>
 @Test
    public void a(){
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring3.xml");
       LoggerTest loggerTest = (LoggerTest) applicationContext.getBean("loggerTest");
       loggerTest.test();
   }
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗的1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值