四丶SpringAOP自定义注解的一个简单实现(切入点的详解)

1.参考

官网:Declaring a pointcut
参考:SpringAOP切入点详解
Spring AOP四种实现方式Demo详解与相关知识探究,参考博文,点击这里

1.内容:SpringAOP中的Joinpoint的相关用法:
参考博文2

 2.SpringAOP中的@Before和@After执行的顺序:

2.环绕通知

Spring-Boot+AOP+统计单次请求方法的执行次数和耗时情况

只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint

  • 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。
- ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,类似于代理类中的invoke()
  • 2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用
    当方法符合切点规则不符合环绕通知的规则时候,执行的顺序如下

@Before→@After→@AfterRunning(如果有异常→@AfterThrowing)

当方法符合切点规则并且符合环绕通知的规则时候,执行的顺序如下

@Around→@Before→@After→@Around执行 ProceedingJoinPoint.proceed() 之后的操作→@AfterRunning(如果有异常→@AfterThrowing)

3.声明切入点

声明一个切入点包含两部分:切入点表达式和切入点签名

@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

1、execution - for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP
常用的切入点表达式:@Pointcut("execution(xxx)")


切入点表达式的写法:
        关键字:execution(表达式)
        表达式:
            访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
        标准的表达式写法:
            public void com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
        访问修饰符可以省略(不写表示所有,但是不能使用通配符)
            void com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
        返回值可以使用通配符,表示任意返回值
            * com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
        包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
            * *.*.*.*.AccountServiceImpl.saveAccount())
        包名可以使用..表示当前包及其子包
            * *..AccountServiceImpl.saveAccount()
        类名和方法名都可以使用*来实现通配
            * *..*.*()
        参数列表:
            可以直接写数据类型:
                基本类型直接写名称         int
                引用类型写包名.类名的方式   java.lang.String
            可以使用通配符表示任意类型,但是必须有参数
            可以使用..表示有无参数均可,有参数可以是任意类型
        全通配写法:
            * *..*.*(..)

        实际开发中切入点表达式的通常写法:
            切到业务层实现类下的所有方法
                * com.liaoxiang.service.impl.*.*(..)

| public * *(..) |任何公共方法的执行 |
| execution(* set*(..)) |以set开头的任何方法 |
| execution(* com.xyz.service.AccountService.*(..)) |AccountService接口定义的所有方法的执行 |
| execution(* com.xyz.service.*.*(..)) |service包下定义的所有方法的执行 |
| execution(* com.xyz.service..*.*(..)) |service包及其子包下方法的执行 |
| * com.learn..IHelloService.*() |learn包及所有子包下IHelloService接口中的任何无参方法 |
| * com.learn..IHelloService.*(*) |learn包及所有子包下IHelloService接口的任何只有一个参数方法 |
| * com.learn..IHelloService+.*() |learn包及所有子包下IHelloService接口及子类型的的任何无参方法 |
| * com.learn..IHelloService*.test*(java.util.Date) |learn包及所有子包下IHelloService前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法,注意该匹配是根据方法参数类型进行匹配的,而不是根据执行时传入的参数类型决定的,如定义方法:public void test(Object obj);即使执行时传入java.util.Date,也不会匹配的; |
简单的一个测试方法:

@Repository
public class UserDao {
    public void save(){
        System.out.println("保存成功");
    }

    public String findName(){
        System.out.println("查询名字");
        return "张三";
    }

    public void update(Integer id){
        System.out.println("根据id更新");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.wuhuafeng"})
public class AppConfig {

}
@Component
@Aspect
public class Aspectj {

    @Pointcut("execution(* com.wuhuafeng.dao.*.*(..))")
    public void pointCut(){
        System.out.println("日志-正在操作");

    }
    @Before("execution(* com.wuhuafeng.dao.*.*(..))")
    public void before(){
        System.out.println("日志-before");
    }
    @After("execution(* com.wuhuafeng.dao.*.*(..))")
    public void after(){
        System.out.println("日志-After");
    }
}
public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext AC = new AnnotationConfigApplicationContext(AppConfig.class);
         UserDao dao = (UserDao)AC.getBean("userDao");
         dao.findName();
         dao.update(1);
         dao.save();
        System.out.println("----------");
        Skill skill =(Skill)dao;
         skill.getSkill("我很牛逼");
    }
}
日志-before
查询名字
日志-After
日志-before
根据id更新
日志-After
日志-before
保存成功
日志-After

@DeclareParents 为对象添加方法,为已生成的对象添加新的 方法。
(1.)定义一个名为 Skill 的接口及它的实现类 SkillImpl。我们将要把 SkillImpl 的getSkill()方法添加到UserDao的实列dao上。

@Component
public interface Skill {
    public void getSkill(String skill);
}

@Component
public class SkillImpl implements Skill {

    public void getSkill(String skill) {
        System.out.println(skill);
    }
}

@Component
@Aspect
public class Aspectj {

    @DeclareParents(value = "com.wuhuafeng.dao.*",defaultImpl = SkillImpl.class)
    public Skill skill;  //添加的方法是这个接口里面的
    //这里的Value:表示对哪里的类的实列,添加一个新的方法。
    defaultImpl:添加方法的默认实现在SkillImpl中。

    ....//和上面的一样。
public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext AC = new AnnotationConfigApplicationContext(AppConfig.class);
         UserDao dao = (UserDao)AC.getBean("userDao");
         dao.findName();
         dao.update(1);
         dao.save();
        System.out.println("----------");
        Skill skill =(Skill)dao;// 通过类型转换,dao对象就拥有了SkillImp 类的方法
         skill.getSkill("我很牛逼");
    }
}
日志-before
查询名字
日志-After
日志-before
根据id更新
日志-After
日志-before
保存成功
日志-After
----------
我很牛逼
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值