官网: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