十八、SpringAOP切入点详解

官网:Declaring a pointcut
参考:SpringAOP切入点详解
声明一个切入点包含两部分:切入点表达式和切入点签名

@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
@ComponentScan("com.liaoxiang")
@EnableAspectJAutoProxy
public class AppConfig {

}
@Component
@Aspect
public class Advice {
    /**
     * dao包下的所有方法都增强
     */
    @Pointcut("execution(* com.liaoxiang.dao.*.*(..))")
    public void pointCutExecution1 (){}

    /**
     * 对返回值为String的方法增强
     */
    @Pointcut("execution(String com.liaoxiang.dao.*.*(..))")
    public void pointCutExecution2 (){}

    /**
     * 对第一个参数类型为Integer的方法增强
     */
    @Pointcut("execution(* com.liaoxiang.dao.*.*(Integer, ..))")
    public void pointCutExecution3 (){}


    @Before("pointCutExecution1()")
    public void before(){
        System.out.println("纪录了日志。。");
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        UserDao dao = (UserDao)ac.getBean("userDao");
        dao.save();
        dao.findName();
        dao.update(1);
    }
}

分别修改Advice 类中的前置通知方法的切入点方法@Before("pointCutExecution1()")为1,2,3打印输出如下

纪录了日志。。
保存成功
纪录了日志。。
查询名字
纪录了日志。。
根据id更新

保存成功
纪录了日志。。
查询名字
根据id更新

保存成功
查询名字
纪录了日志。。
根据id更新
2、within - limits matching to join points within certain types
模式描述
within(com.xyz.service.*)service包下的任何方法的执行
within(com.xyz.service..*)service包及其子包下的任何方法的执行
within(com.xyz.service..IHelloService+)service包或所有子包下IHelloService类型及子类型的任何方法
within(@com.xyz..Secure *)持有com.xyz…Secure注解的类的任何方法,注解在接口上无效

简单测试第三项:
在这里插入图片描述
接口

public interface IHelloService {
    void sayHello();
}
public interface IExtendHelloService extends IHelloService{}

实现类

@Service
public class HelloService1 implements IHelloService {
    public void sayHello() {
        System.out.println("Hello1");
    }
}
@Service
public class HelloService2 implements IHelloService {
    public void sayHello() {
        System.out.println("Hello2");
    }
}
@Service
public class ExtendHelloService implements IExtendHelloService {
    public void sayHello() {
        System.out.println("Extend Hello");
    }
}

AOP

@Component
@Aspect
public class DaoAdvice {
    /**
     * within
     */
    @Pointcut("within(com.liaoxiang.service..IHelloService+)")
    public void pointCutWithin2 (){}

    @Before("pointCutWithin2()")
    public void before(){
        System.out.println("增强方法执行");
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        IHelloService service1 = (IHelloService)ac.getBean("helloService1");
        IHelloService service2 = (IHelloService)ac.getBean("helloService2");
        IHelloService service3 = (IHelloService)ac.getBean("extendHelloService");
        service1.sayHello();
        service2.sayHello();
        service3.sayHello();
    }
}

纪录了日志。。
Hello1
纪录了日志。。
Hello2
纪录了日志。。
Extend Hello

测试第四项,修改上面的实现类,加上自定义注解

@MyAnnotation
@Service
public class HelloService1 implements IHelloService {
    public void sayHello() {
        System.out.println("Hello1");
    }
}
@Service
public class HelloService2 implements IHelloService {
    public void sayHello() {
        System.out.println("Hello2");
    }
}
@MyAnnotation
@Service
public class ExtendHelloService implements IExtendHelloService {
    public void sayHello() {
        System.out.println("Extend Hello");
    }
}

AOP

@Pointcut("within(@com.liaoxiang..MyAnnotation *)")
public void pointCutWithin3 (){}

@Before("pointCutWithin3()")
public void before(){
    System.out.println("增强方法执行");
}

执行输出结果:

增强方法执行
Hello1
Hello2
增强方法执行
Extend Hello
3、this - limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type
4、target - limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type

测试两者的区别:

/**
 * @auther Mr.Liao
 * @date 2019/10/19 9:10
 */
@Configuration
@ComponentScan("com.liaoxiang")
//默认使用的JDK动态代理
@EnableAspectJAutoProxy
public class AppConfig {

}
@Component
@Aspect
public class Advice {
    
    /**
     * this
     */
    @Pointcut("this(com.liaoxiang.dao.impl.UserDao)")
    public void pointCutThis (){}
    
    /**
     * target
     */
    @Pointcut("target(com.liaoxiang.dao.impl.UserDao)")
    public void pointCutTarget (){}
    
    /**
     * 增强方法
     * 除了参数是String的其他都增强
     */
    @Before("pointCutThis()")
    public void before(){
        System.out.println("增强方法执行");
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        IUserDao dao = (IUserDao)ac.getBean("userDao");
        System.out.println(dao instanceof UserDao);
        System.out.println(dao instanceof Proxy);
        dao.save();
    }
}
打印结果:
false
true
保存成功,无返回值

可以看到在SpringAOP默认使用JDK动态代理的时候,创建的IUserDao接口的实现类对象并不是UserDao的实例,所以并没有对UserDao的方法进行增强
修改切入点表达式为:"target(com.liaoxiang.dao.impl.UserDao)",再次执行结果如下

false
true
增强方法执行
保存成功,无返回值

修改为CGLIB的动态代理,分别执行两种切入点表达式,方法都得到了增强

@Configuration
@ComponentScan("com.liaoxiang")
//默认使用的JDK动态代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {

}
true
false
增强方法执行
保存成功,无返回值

下面是参考别人的代码写的,不知道表达的确切意思是什么、、先放着吧
在这里插入图片描述

public abstract class User {

    public abstract void who();

    public void say() {
        System.out.println("hello");
    }

    public void root() {
        System.out.println("user");
    }
}
@Component
public class Member extends User{

    @Override
    public void who() {
        System.out.println("member.who()执行");
    }

    public void doSomething() {
        System.out.println("member.doSomething()执行");
    }

    public void getCompany() {
        System.out.println("member.getCompany()执行");
    }
}
@Component
public class Leader extends Member{

    @Override
    public void say() {
        System.out.println("hello, members");
    }

    @Override
    public void who() {
        System.out.println("leader.who()执行");
    }

    @Override
    public void doSomething() {
        System.out.println("leader.doSomething()执行");
    }

    public void self() {
        System.out.println("leader.self()执行");
    }
}
@Aspect
@Component
public class Advice {


    @Before("execution(* com.liaoxiang.spring_aop.model..*(..)) && this(com.liaoxiang.spring_aop.model.Member)")
    public void execute0(){
        System.out.println("this增强方法执行");
    }

    @Before("execution(* com.liaoxiang.spring_aop.model..*(..)) && target(com.liaoxiang.spring_aop.model.Member)")
    public void execute1(){
        System.out.println("target增强方法执行");
    }
}
@SpringBootTest
class SpringAopApplicationTests {
    @Autowired
    private Member member;
    @Autowired
    private Leader leader;

    @Test
    public void test1() {
        System.out.println("---------------member---------------");
        member.who();
        System.out.println("---------------leader---------------");
        leader.who();
    }
    ---------------member---------------
	this增强方法执行
	target增强方法执行
	member.who()执行
	---------------leader---------------
	this增强方法执行
	target增强方法执行
	leader.who()执行
	
	结论:两个类中都重写了who方法,this和target效果一样
    @Test
    public void test2() {
        // 继承
        System.out.println("---------------member---------------");
        member.say();
        // 重写
        System.out.println("---------------leader---------------");
        leader.say();
    }
    ---------------member---------------
	target增强方法执行
	hello
	---------------leader---------------
	this增强方法执行
	target增强方法执行
	leader.say()执行
	
	结论:this不能匹配到继承的方法
    

    @Test
    public void test3() {
        System.out.println("---------------member---------------");
        member.root();
        System.out.println("---------------leader---------------");
        leader.root();
    }
    ---------------member---------------
	target增强方法执行
	user
	---------------leader---------------
	target增强方法执行
	user
	结论:this不能匹配到未重写的方法

    @Test
    public void test4() {
        // 独有方法
        System.out.println("---------------member---------------");
        member.doSomething();
        // 子类重写
        System.out.println("---------------leader---------------");
        leader.doSomething();
    }
    ---------------member---------------
	this增强方法执行
	target增强方法执行
	member.doSomething()执行
	---------------leader---------------
	this增强方法执行
	target增强方法执行
	leader.doSomething()执行
	
	结论:子类重写父类的方法两者都能匹配上

    @Test
    public void test5() {
        System.out.println("---------------member---------------");
        member.getCompany();
        System.out.println("---------------leader---------------");
        leader.getCompany();
    }
	
	同上

    // 独有的方法
    @Test
    public void test6() {
        System.out.println("---------------leader---------------");
        leader.self();
    }
    ---------------leader---------------
	this增强方法执行
	target增强方法执行
	leader.self()执行
	
	子类的方法都可以匹配到
}
args(java.io.Serializable)

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable
修改测试类:

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

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

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

    public void updateByName(String name){
        System.out.println("根据name更新");
    }

    public void updateByNameAndId(String name, Integer id){
        System.out.println("根据name和id更新");
    }
}
@Component
@Aspect
public class DaoAdvice {
    /**
     * dao包下的所有方法都增强
     */
    @Pointcut("execution(* com.liaoxiang.dao.*.*(..))")
    public void pointCutExecution1 (){}

    /**
     * 对返回值为String的方法增强
     */
    @Pointcut("execution(String com.liaoxiang.dao.*.*(..))")
    public void pointCutExecution2 (){}

    /**
     * 对第一个参数类型为Integer的方法增强
     */
    @Pointcut("execution(* com.liaoxiang.dao.*.*(Integer, ..))")
    public void pointCutExecution3 (){}

    /**
     * 根据参数增强
     */
    @Pointcut("args(String)")
    public void pointCutArgs1 (){}

    @Pointcut("args(Integer)")
    public void pointCutArgs2 (){}

    @Pointcut("args(String, Integer)")
    public void pointCutArgs3 (){}


    @Before("pointCutArgs1()")
    public void before(){
        System.out.println("纪录了日志。。");
    }
}

分别执行@Before("pointCutArgs1()")1,2,3

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        UserDao dao = (UserDao)ac.getBean("userDao");
        dao.save();
        dao.findName();
        dao.updateById(1);
        dao.updateByName("jack");
        dao.updateByNameAndId("jack",1);
    }
}

根据输出可以看到,分别对updateById,updateByName,updateByNameAndId三个方法进行了增强

多个切入点表达式配合使用
@Pointcut("within(com.liaoxiang.dao.*)")
public void pointCutWithin (){}

/**
 * 根据参数增强
 */
@Pointcut("args(String)")
public void pointCutArgs1 (){}

@Pointcut("args(Integer)")
public void pointCutArgs2 (){}

@Pointcut("args(String, Integer)")
public void pointCutArgs3 (){}

/**
 * 除了参数是String的其他都增强
 */
@Before("pointCutWithin() &&! pointCutArgs1()")
public void before(){
    System.out.println("纪录了日志。。");
}

纪录了日志。。
保存成功,无返回
纪录了日志。。
查询名字,返回张三
纪录了日志。。
根据id更新
根据name更新
纪录了日志。。
根据name和id更新
@annotation,给具有某个特定注解的方法增强
UserDao类:
@MyAnnotation
public String findName(){
    System.out.println("查询名字,返回张三");
    return "张三";
}

DaoAdvice类:
/**
 * @annotation
 */
@Pointcut("@annotation(com.liaoxiang.anno.MyAnnotation)")
public void pointCutAnnotation (){}

/**
 * 除了参数是String的其他都增强
 */
@Before("pointCutAnnotation()")
public void before(){
    System.out.println("纪录了日志。。");
}
结果:
保存成功,无返回
纪录了日志。。
查询名字,返回张三
根据id更新
根据name更新
根据name和id更新
@args(com.xyz.security.Classified)

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation
匹配当前执行的方法传入的参数具有有指定的注解

@MyAnnotation
public class CoustomParameter {
}

/**
 * @args
 */
@Pointcut("@args(com.liaoxiang.anno.MyAnnotation)")
public void pointCut_Args (){}

public void updateById(CoustomParameter parameter){
    System.out.println("根据id更新");
}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        UserDao dao = (UserDao)ac.getBean("userDao");
        dao.save();
        dao.findName();
        dao.updateById(new CoustomParameter());
        dao.updateByName("jack");
        dao.updateByNameAndId("jack",1);
    }
}
保存成功,无返回
查询名字,返回张三
纪录了日志。。
根据id更新
根据name更新
根据name和id更新
@within(org.springframework.transaction.annotation.Transactional)

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值