Java

4.利用AOP实现自定义Spring注解

2018-07-03  本文已影响42人  faunjoe

AOP基本概念

AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理;反之,采用CGLIB代理。
织入(Weaving)把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才能做到,例如AspectJ的织入编译器;
2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码;
3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理是使用了JDK的动态代理。

AOP通知类型

@Before前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
@After 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
@AfterReturning 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。
@Around 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。
@AfterThrowing 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。

自定义Spring注解实现

定义注解有好几种实现方式,有使用aop实现的,也有使用拦截器实现的,实现的话有可能各自有的优缺点,不过总的来说,还是推荐使用aop方式实现。

定义注解类

/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package net.itaem.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
 
/**
 * 说明:
 * 定义日志注解
 * 
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FileLog {
    String value() default "日志记录开始";
}

定义注解的处理程序

/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package net.itaem.aspect;
 
import net.itaem.annotation.FileLog;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
 
 
/**
 * 说明:
 * 日志的切面处理程序
 * 
 */
@Aspect
@Component
public class LogAspect {
 
    @AfterReturning("within(net.itaem..*) && @annotation(fl)")  
    public void addSuccessLog(JoinPoint jp, FileLog fl){  
        Object[] parames = jp.getArgs();
        //获取目标方法体参数  
        String className = jp.getTarget().getClass().toString();
        //获取目标类名  
        String signature = jp.getSignature().toString();
        //获取目标方法签名  
        String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("(")); 
        //获取注解值
        String desc=fl.value();
        //把调用的信息写到日常记录信息里面去...
    }  
  
    //标注该方法体为异常通知,当目标方法出现异常时,执行该方法体  
    @AfterThrowing(pointcut="within(net.itaem..*) && @annotation(fl)", throwing="e")  
    public void addExceptionLog(JoinPoint jp, FileLog fl, Exception e){  
       //把错误信息写到错误日志文件里面去...
    }  
}

编写测试用例

配置spring启动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-3.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd  ">
        
    <aop:aspectj-autoproxy/>  
    <bean name="purchaseService"   class="net.itaem.test.PurchaseService" />
    <bean class="net.itaem.aspect.LogAspect"/>    
</beans>

编写service类

public class PurchaseService {
    public void purchaseProduct(String productName,int price,String type) {
        System.out.println("购买商品。。。");
    }
}

编写测试代码

package net.itaem.test;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class Test {
 
    @org.junit.Test
    public void test() {
        ApplicationContext context=new ClassPathXmlApplicationContext("net/itaem/source/test.xml");
        PurchaseService service=(PurchaseService) context.getBean("purchaseService");
        service.purchaseProduct("电风扇", 98, "日用品");
    }
     
}

结果展示

image.png

总结

个人觉得这个实现起来比较简答, 也比较容易理解,当然其他的更深入的扩展,还是要慢慢的去学习,探索,这里只是简单的做了一个实现注解的方式介绍。其实学习的这种状态挺快乐的,无形中有一种自己又升华了的感觉,好好加油吧~

上一篇 下一篇

猜你喜欢

热点阅读