自定义注解实现aop

接口

package cn.itheima.springannotationaop;


public interface UserService {
void save(int a, int b);
void save(int a, String b);
void update(int a, int b);

void find();
void delete();


}

接口的实现类

package cn.itheima.springannotationaop;


public class UserServiceImpl implements UserService {




@Override
@Description("haha")
public void save(int a, int b) {
// TODO Auto-generated method stub
System.out.println("保存");
}


@Override
@Description("hehe")
public void update(int a, int b) {
// TODO Auto-generated method stub
System.out.println("更新");

}


@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("查找");

}


@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除");

}


@Override
@Description("hehe")
public void save(int a, String b) {
// TODO Auto-generated method stub
System.out.println("保存=======重载");
}


}

增强/通知(这是核心)

package cn.itheima.springannotationaop;


import java.lang.reflect.Method;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//这就是一个切面
@Aspect
public class MyAdvice {



//前置通知
@Pointcut("@annotation(cn.itheima.springannotationaop.Description)")//这是切点
/*
* public class JoinPointUtils {
*  public static Object getMethod(JoinPoint joinPoint, Class t)throws Exception{
        String targetName=joinPoint.getTarget().getClass().getName();//取得目标对象,目标对象的字节码对象,取得类的全路径名称
        //getSimpleName()就是取得类的名称
        String methodName = joinPoint.getSignature().getName();//获取连接点的方法签名对象.获取目标对象切点方法名字




        Object[] arguments = joinPoint.getArgs();//获取获取传入目标方法的参数对象
        Class targetClass = Class.forName(targetName);//利用反射得到目标对象的字节码对象
        Method[] methods = targetClass.getMethods();//取得所有目标对象的所有方法对象
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {//方法名和连接点的方法名一样
                Class[] clazzs = method.getParameterTypes();//得到参数类型列表
                if (clazzs.length == arguments.length) {//列表长度和目标方法参数长度一样
                    return method.getAnnotation(t);//得到注解
                    //这儿其实,又判断方法名一样,有判断方法参数是否一样,其实就是怕重载的情况
                    //至于为什么getClass()得到字节码为什么还要用反射得到字节码,要么就是代码冗余
                    //要么就是这个targetName其他地方还有用
                    //这儿要把jointpoint想成pointcut
                }
            }
        }
        return null;
    }
}
*/
//总结:自己这样写的不具有通用性,只能知道注解在那个确定的方法上,并且知道那个方法的参数,才能这样写,如果想要实现注解丢在任何一个方法上

//都可以用的话,还是要像上面写的那样
//因为Method m = c.getMethod(point.getSignature().getName(), int.class, int.class);这一句
//要确定方法的参数,才能把这个方法对象返回过来,你在使用注解的时候可能会用在任何一个方法上,方法参数根本不能确定
//所以只有连接点(可以看成切点)可以得到到底注解在了哪个方法上,取得这个方法就是遍历这个类的所有方法,看名字和参数和从jointpoint对象
//得到的是一样的不,是的话,那就能确定注解的就是这个方法了,这个方法就可以得到注解的值了,根据注解的值,在五个通知方法中自己实现想要的逻辑就可以了
//自定义注解实现aop真是精巧啊!
//而且把切点放在注解上,注解可以随便换位置,这样切点就可以动态移动了,想切那个方法切那个方法!!!
//具体思路:连接点可以得到注解的那个对象和方法和参数列表,我们需要的是什么?是注解的方法对象,要得到这个方法对象,需要
//Method m = c.getMethod(point.getSignature().getName(), int.class, int.class);即是方法名,后面的参数确定不下来,
//(因为注解可能放在任何一个方法上,然而这个目标对象(注解的那个对象)又可以得到它的所有的方法对象列表,经过对比方法名和参数,就
//可以把注解的方法确定下来了.有了这个方法就可以得到注解上的值,根据值,就可以在通知中写相应的业务逻辑了.
//更进一步:其实得到参数列表也没啥用,只有一个参数值,不能确定类型,不能确定个数,虽然可以通过很多if判断个数和类型,不过不具有通用性
//还是上面的方法好,不过,只是凭方法名和参数列表长度能唯一确定一个方法吗?恐怕不能吧,方法名相同,参数列表长度相同的重载方法就不能确定了,这个方法还是有bug
//实验证明:如果重载方法不是每个都加上注解的话,会空指针异常
//还有个结论:就是配置了切点,通知,切面,但是你没有调用被切的方法,那么切面里面的增强/通知代码也不会执行
//只有你用到了配置了通知的方法,逻辑才会执行

public void pc(){}
@Before("pc()")
//前置通知
public void before(JoinPoint point) {
System.out.println(point.getTarget());//cn.itheima.springxmlaop.UserServiceImpl@1fe47411
System.out.println(point.getTarget().getClass());//class cn.itheima.springxmlaop.UserServiceImpl
System.out.println(point.getTarget().getClass().getName());//cn.itheima.springxmlaop.UserServiceImpl
System.out.println(point.getTarget().getClass().getSimpleName());//UserServiceImpl
System.out.println(point.getSignature().getName());//save,or,delete,or,update
//System.out.println(point.getArgs()[0]);//[Ljava.lang.Object;@1165477e数组对象//2
System.out.println(point.getArgs());//[Ljava.lang.Object;@1165477e数组对象//2
Object target = point.getTarget();
Class c = target.getClass();
try {
Method m = c.getMethod(point.getSignature().getName(), int.class, int.class);
Description annotation = m.getAnnotation(Description.class);
System.out.println(annotation.value()+"------");
if (annotation.value().equals("haha")) {
System.out.println("========haha");
} else if (annotation.value().equals("hehe")) {
System.out.println("+++++++hehe");
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}



System.out.println("前置通知");
}
//后置通知
@AfterReturning("pc()")
public void after() {
System.out.println("后置通知");
}
//环绕通知
@Around("pc()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知之前");
String targetName=joinPoint.getTarget().getClass().getName();//取得目标对象,目标对象的字节码对象,取得类的全路径名称
       //getSimpleName()就是取得类的名称
       String methodName = joinPoint.getSignature().getName();//获取连接点的方法签名对象.获取目标对象切点方法名字




       Object[] arguments = joinPoint.getArgs();//获取获取传入目标方法的参数对象
       Class targetClass = Class.forName(targetName);//利用反射得到目标对象的字节码对象
       Method[] methods = targetClass.getMethods();//取得所有目标对象的所有方法对象
       for (Method method : methods) {
           if (method.getName().equals(methodName)) {//方法名和连接点的方法名一样
               Class[] clazzs = method.getParameterTypes();//得到参数类型列表
               if (clazzs.length == arguments.length) {//列表长度和目标方法参数长度一样
                    Description annotation = method.getAnnotation(Description.class);//得到注解
                   //这儿其实,又判断方法名一样,有判断方法参数是否一样,其实就是怕重载的情况
                   //至于为什么getClass()得到字节码为什么还要用反射得到字节码,要么就是代码冗余
                   //要么就是这个targetName其他地方还有用
                   //这儿要把jointpoint想成pointcut
                    System.out.println(annotation.value()+"验证重载bug");
               }
           }
       }
Object proceed = joinPoint.proceed();

System.out.println("环绕通知之后");
return proceed;
}
//异常拦截通知
@AfterThrowing("pc()")
public void exception() {
System.out.println("出现异常了");
}
//后置异常通知
@After("pc()")
public void afterException(JoinPoint point) {
Object target = point.getTarget();
Class c = target.getClass();
try {
Method m = c.getMethod(point.getSignature().getName(), int.class, int.class);
Description annotation = m.getAnnotation(Description.class);
System.out.println(annotation.value()+"------");
if (annotation.value().equals("haha")) {
System.out.println("========haha后置");
} else if (annotation.value().equals("hehe")) {
System.out.println("+++++++hehe后置");
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("出现异常了,后置通知");
}


}

自定义注解

package cn.itheima.springannotationaop;


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value();
}

测试用例demo

package cn.itheima.springannotationaop;


import javax.annotation.Resource;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itheima/springannotationaop/applicationContext.xml")
public class Demo {


@Resource(name="userServiceImpl")
private UserService userServiceImpl;
@Test
public void fun() {
//userServiceImpl.save(3,4);
//userServiceImpl.save(3, 4);
//userServiceImpl.update(3, 4);
userServiceImpl.save(3, 4);
}
}

配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<bean name="userServiceImpl" class="cn.itheima.springannotationaop.UserServiceImpl"></bean>
<bean name="myAdvice" class="cn.itheima.springannotationaop.MyAdvice"></bean>
<!-- 开启注解aop -->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
<!-- 或者下面这个都可以 -->
<aop:aspectj-autoproxy />
</beans>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值