Spring Core 官方文档阅读笔记(九)
- AOP常见概念
- Aspect:切面,即关注点的模块化。一个切面定义了位于横切点的处理逻辑。可以将一整套业务逻辑看成是一条处理链,当需要在某个处理的节点之前或者之后增加自定义处理的时候,可以在节点上或下切入一个平面,所有调用该处理节点的逻辑,都被切入,那么所有调用这个节点的处理都会先执行自定义的逻辑。不需要修改原有的业务代码。可以通过@Aspect来声明一个切面
- Joint Point:连接点,即切面介入的方法节点。比如说某一个处理链上有5个方法节点,这五个节点上都可以切入切面,那么就有5个连接点。
- Advice:通知,即切面需要完成的处理,以及什么时候使用切面的处理。spring AOP有五种通知类型:前置通知(Before)、后置通知(After)、返回通知(After-Returning)、异常通知(After-Throwing)、环绕通知(Around)。
- Pointcut:切入点,在连接点的基础之上,如果切面切入了某个方法节点,则称该节点为切入点。
- Introduction:引入,即为一个类增加一个新的方法。比如A类里只有一个方法,现在想要A类中拥有B类的方法,可以使用Introduction来实现。
- Target Object:即被AOP增强的对象
- AOP Proxy:Spring为实现切面而创建的对象。Spring AOP代理有两类:JDK动态代理和CGLIB代理
- Weaving:将切面与其他应用程序类型或者对象链接,以创建通知对象。
- execution表达式
execution表达式用于匹配需要需要切入的方法,语法如下
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
如:
execution(* com.loongshawn.method.ces..*.*(..))
| 标识符 | 含义 |
|---|---|
| execution | 表达式主体 |
| 第一个*号 | 返回值类型,*号代表任意类型 |
| com.loongshawn.method.ces | AOP所切的包名 |
| 包名后的".." | 表示当前包及子包 |
| 第二个* | 类名,*号表示所有类 |
| .*(..) | 方法名及参数,*号代表任意方法名,括号内是参数,..表示任意参数 |
execution中除了返回类型模式,方法名模式和参数模式外,其他的都是可选的。
execution中的+号代表所标记类的所有子类,如Object+,就代表Object及其所有的子类。
- 切入点
可以使用@Pointcut声明一个切入点,如
@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
支持的切入点指示符有:
- execution:用于匹配方法的切入点。如下代码:
public interface MyInterface {
void sayHello();
}
/**
* @Author: kuromaru
* @Date: Created in 15:51 2019/4/3
* @Description:
* @modified:
*/
public class MyInterfaceImpl implements MyInterface {
@Override
public void sayHello() {
System.out.println("AOP真牛逼!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:43 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("execution(* configure.pointcut.execution.MyInterface+.*(..))")
private void myExecution(){
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("对,就是辣么牛逼!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyInterface myInterface() {
return new MyInterfaceImpl();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:00 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyInterface myInterfaceImpl = acac.getBean(MyInterface.class);
myInterfaceImpl.sayHello();
}
}
输出结果:
四月 03, 2019 5:08:46 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Wed Apr 03 17:08:46 CST 2019]; root of context hierarchy
AOP牛不牛逼?
AOP真牛逼!
在编写上面代码的时候,遇到了如下问题:
@Pointcut的execution一开始写成了configure.pointcut.execution.MyInterface.(..),跑起来报错,后来改成configure.pointcut.execution.MyInterfaceImpl.(..),一样不行,后来查资料,发现需要写成@Pointcut("execution(* configure.pointcut.execution.MyInterface+.*(..))"),+号表示MyInterface下面的所有子类。
- within
用于匹配特定类型内的方法。
/**
* @author kuromaru
*/
public interface MyWithinInterface {
void say();
}
/**
* @Author: kuromaru
* @Date: Created in 9:34 2019/4/4
* @Description:
* @modified:
*/
public class MyWithinInterfaceImpl implements MyWithinInterface {
@Override
public void say() {
System.out.println("AOP真牛逼!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 9:35 2019/4/4
* @Description:
* @modified:
*/
public class MyWithinInterfaceImpl2 implements MyWithinInterface {
@Override
public void say() {
System.out.println("对,AOP真牛逼!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:43 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("within(configure.pointcut.within.MyWithinInterface+))")
private void myExecution(){
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("AOP牛不牛逼?");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean(name = "myWithinInterface")
public MyWithinInterface myWithinInterface() {
return new MyWithinInterfaceImpl();
}
@Bean(name = "myWithinInterface2")
public MyWithinInterface myWithinInterface2() {
return new MyWithinInterfaceImpl2();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:00 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyWithinInterface myInterfaceImpl = (MyWithinInterface) acac.getBean("myWithinInterface");
MyWithinInterface myInterfaceImpl2 = (MyWithinInterface) acac.getBean("myWithinInterface2");
myInterfaceImpl.say();
myInterfaceImpl2.say();
}
}
四月 04, 2019 10:03:49 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 10:03:49 CST 2019]; root of context hierarchy
AOP牛不牛逼?
AOP真牛逼!
AOP牛不牛逼?
对,AOP真牛逼!
- this
this匹配的是生成的代理类的类型。举个栗子:
public interface MyInterface {
void sayHello();
}
/**
* @Author: kuromaru
* @Date: Created in 15:51 2019/4/3
* @Description:
* @modified:
*/
public class MyInterfaceImpl implements MyInterface {
@Autowired
private MyInvoke myInvoke;
@Override
public void sayHello() {
myInvoke.invoke();
System.out.println("AOP真牛逼!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:29 2019/4/4
* @Description:
* @modified:
*/
public class MyInvoke {
public void invoke() {
System.out.println("我去~");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:43 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("this(configure.pointcut.thethis.MyInterface)")
private void myExecution(){
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("AOP牛不牛逼?");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyInterface myInterface() {
return new MyInterfaceImpl();
}
@Bean
public MyInvoke myInvoke() {
return new MyInvoke();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:00 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyInterface myInterfaceImpl = acac.getBean(MyInterface.class);
myInterfaceImpl.sayHello();
}
}
输出结果:
四月 04, 2019 1:16:50 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 13:16:50 CST 2019]; root of context hierarchy
AOP牛不牛逼?
我去~
AOP真牛逼!
我们知道,JDK代理需要有接口,而CGLIB代理可以是任何类。那么this的匹配逻辑似乎是当需要被切入点的类是接口时,默认使用JDK代理,如图:

所以,this所匹配的类就是这个代理类。但是,如果我们把切入点换成MyInterfaceImpl,就会有问题,如下:
/**
* @Author: kuromaru
* @Date: Created in 15:43 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("this(configure.pointcut.thethis.MyInterfaceImpl)")
private void myExecution(){
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("AOP牛不牛逼?");
}
}
输出结果:
四月 04, 2019 1:23:23 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 13:23:23 CST 2019]; root of context hierarchy
我去~
AOP真牛逼!
根据this匹配类的逻辑,由于切点是实现类,所以this所匹配的类也是CGLIB生成的代理类,但是由于MyInterfaceImpl实现了接口MyInterface,spring为其生成了JDK代理类,所以实际上使用的是JDK的代理类。由于所有的增强处理都织入到了CGLIB的代理类上,所以JDK的代理类只有被代理的方法输出,没有执行before里的逻辑。
我们可以通过强制使用CGLIB代理来达到我们想要的效果,代码如下(只列出需要改动的类和文件):
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config proxy-target-class="true" />
</beans>
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
@ImportResource(value = "file:/thethis/aop-proxy.xml")
public class MyConfig {
@Bean
public MyInterface myInterface() {
return new MyInterfaceImpl();
}
@Bean
public MyInvoke myInvoke() {
return new MyInvoke();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
输出结果:
四月 04, 2019 1:43:02 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 13:43:02 CST 2019]; root of context hierarchy
四月 04, 2019 1:43:02 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from URL [file:F:/workspace/svn/trunk/spring-core-study/src/main/java/configure/pointcut/thethis/aop-proxy.xml]
AOP牛不牛逼?
我去~
AOP真牛逼!
this和target的不同之处也在于此,target匹配的是被代理的类,即MyInterface和MyInterfaceImpl,所以,如果使用target的参数里不管是接口还是实现类,都会正常执行before里的逻辑。target的使用会在下面整理。
- target
匹配的即为上面阐述过的被代理的类。把上面的代码换成target,如下:
/**
* @Author: kuromaru
* @Date: Created in 15:43 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("target(configure.pointcut.target.MyInterfaceImpl)")
private void myExecution(){
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("AOP牛不牛逼?");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyInterface myInterface() {
return new MyInterfaceImpl();
}
@Bean
public MyInvoke myInvoke() {
return new MyInvoke();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
输出结果:
四月 04, 2019 1:48:33 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 13:48:33 CST 2019]; root of context hierarchy
AOP牛不牛逼?
我去~
AOP真牛逼!
把target参数中的MyInterfaceIml换成MyInterface也是一样的结果。
- args
用于匹配方法参数是指定的类型的方法。show you the code:
/**
* @Author: kuromaru
* @Date: Created in 13:54 2019/4/4
* @Description:
* @modified:
*/
public class MyMethod {
public void showArgName(ArgsType argsType) {
System.out.println(argsType.getName());
}
public void showArgName(String name) {
ArgsType argsType = new ArgsType(){{
setName(name);
}};
System.out.println(argsType.getName());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:53 2019/4/4
* @Description:
* @modified:
*/
public class ArgsType {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:56 2019/4/4
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("args(ArgsType)")
private void myExecution() {
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("马上打印" + joinPoint.getTarget().toString() + "的参数");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:00 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
ArgsType argsType = new ArgsType(){{
setName("paramA");
setValue("1");
}};
System.out.println("传入ArgsType");
myMethod.showArgName(argsType);
System.out.println("传入String");
myMethod.showArgName("paramB");
}
}
输出结果:
四月 04, 2019 2:03:49 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 14:03:49 CST 2019]; root of context hierarchy
传入ArgsType
马上打印configure.pointcut.args.MyMethod@477b4cdf的参数
paramA
传入String
paramB
- @target
用于匹配对象上有特定注解的类,如下
/**
* @Author: kuromaru
* @Date: Created in 14:16 2019/4/4
* @Description:
* @modified:
*/
@MyAnnotation
public class MyMethod {
public void run() {
System.out.println("向前跑");
}
public void jump() {
System.out.println("向上跳");
}
public static void distory() {
System.out.println("毁了你丫的");
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:19 2019/4/4
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("@target(MyAnnotation)")
private void pointcut() {}
@After("pointcut()")
private void after() {
System.out.println("你咋不上天呢");
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:20 2019/4/4
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
@Target({ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
}
/**
* @Author: kuromaru
* @Date: Created in 14:21 2019/4/4
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
myMethod.jump();
System.out.println("=======================华丽的分割线===========================");
myMethod.run();
System.out.println("=======================华丽的分割线===========================");
MyMethod.distory();
}
}
输出结果:
四月 04, 2019 3:10:23 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 15:10:23 CST 2019]; root of context hierarchy
向上跳
你咋不上天呢
=华丽的分割线=====
向前跑
你咋不上天呢
=华丽的分割线=====
毁了你丫的
可以看到,MyMethod里的所有非static的方法都被拦截了,而static修饰的方法不会被拦截。
-
@within
匹配所有持有指定注解的类。由于Spring AOP只是对AspectJ的一部分功能的模拟,所以@within和@target的效果几乎没有差别。但是在使用中推荐使用@target。这里不再展示代码。 -
@args
匹配持有指定注解的参数类型的类。大栗子:
@Target({ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
}
/**
* @Author: kuromaru
* @Date: Created in 14:16 2019/4/4
* @Description:
* @modified:
*/
@MyAnnotation
public class MyMethod {
@MyAnnotation
public void run() {
System.out.println("向前跑");
}
public void jump(@MyAnnotation String name) {
System.out.println(name + ",向上跳");
}
public static void distory(@MyAnnotation String name) {
System.out.println("毁了你丫的" + name);
}
public void fly(ArgsTypewa argsTypewa) {
System.out.println(argsTypewa.getName() + "会飞");
}
public void sit(ArgsType argsType) {
System.out.println(argsType.getName() + ", 坐下");
}
public void swim(@MyAnnotation ArgsType argsType) {
System.out.println(argsType.getName() + "会游泳");
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:19 2019/4/4
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("@args(MyAnnotation))")
private void pointcut() {}
@After("pointcut()")
private void after() {
System.out.println("你咋不上天呢");
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:20 2019/4/4
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:25 2019/4/4
* @Description:
* @modified:
*/
@MyAnnotation
public class ArgsTypewa {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:25 2019/4/4
* @Description:
* @modified:
*/
public class ArgsType {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:21 2019/4/4
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
myMethod.jump("Jack");
System.out.println("=======================华丽的分割线===========================");
myMethod.run();
System.out.println("=======================华丽的分割线===========================");
MyMethod.distory("Mike");
System.out.println("=======================华丽的分割线===========================");
ArgsTypewa argsTypewa = new ArgsTypewa(){{
setName("狗剩");
}};
myMethod.fly(argsTypewa);
System.out.println("=======================华丽的分割线===========================");
ArgsType argsType = new ArgsType(){{
setName("铁蛋");
}};
myMethod.sit(argsType);
System.out.println("=======================华丽的分割线===========================");
myMethod.swim(argsType);
}
}
输出结果:
四月 04, 2019 3:30:55 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 15:30:55 CST 2019]; root of context hierarchy
Jack,向上跳
=华丽的分割线=====
向前跑
=华丽的分割线=====
毁了你丫的Mike
=华丽的分割线=====
狗剩会飞
你咋不上天呢
=华丽的分割线=====
铁蛋, 坐下
=华丽的分割线=====
铁蛋会游泳
可以看到,在参数前面加注解是不生效的。
- @annotation
匹配所有含有指定注解的方法。代码与@args的代码相似,只要把@args改成@annotation,执行结果如下:
四月 04, 2019 3:55:22 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 15:55:22 CST 2019]; root of context hierarchy
Jack,向上跳
=华丽的分割线=====
向前跑
你咋不上天呢
=华丽的分割线=====
毁了你丫的Mike
=华丽的分割线=====
狗剩会飞
=华丽的分割线=====
铁蛋, 坐下
=华丽的分割线=====
铁蛋会游泳
可以看到,只有含有@MyAnnotation的方法被拦截了,其他的都没有。
- bean
匹配对象名字符合特定名字特征的类。如下
/**
* @Author: kuromaru
* @Date: Created in 13:53 2019/4/4
* @Description:
* @modified:
*/
public class ArgsType {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:54 2019/4/4
* @Description:
* @modified:
*/
public class MyMethod {
public void showArgName(ArgsType argsType) {
System.out.println(argsType.getName());
}
public void showArgName(String name) {
ArgsType argsType = new ArgsType(){{
setName(name);
}};
System.out.println(argsType.getName());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:56 2019/4/4
* @Description:
* @modified:
*/
@Aspect
public class MyExecution {
@Pointcut("bean(ArgsType)")
private void myExecution() {
}
@Before("myExecution()")
private void before(JoinPoint joinPoint) {
System.out.println("马上打印" + joinPoint.getTarget().toString() + "的参数");
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean("ArgsType")
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:00 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
ArgsType argsType = new ArgsType(){{
setName("paramA");
setValue("1");
}};
System.out.println("传入ArgsType");
myMethod.showArgName(argsType);
System.out.println("传入String");
myMethod.showArgName("paramB");
}
}
输出结果:
四月 04, 2019 4:03:12 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 16:03:12 CST 2019]; root of context hierarchy
传入ArgsType
马上打印configure.pointcut.bean.MyMethod@28701274的参数
paramA
传入String
马上打印configure.pointcut.bean.MyMethod@28701274的参数
paramB
而如果把Bean的名字改掉,就会无法拦截
/**
* @Author: kuromaru
* @Date: Created in 15:59 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean("myMethod")
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyExecution myExecution() {
return new MyExecution();
}
}
输出结果:
四月 04, 2019 4:06:39 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Thu Apr 04 16:06:39 CST 2019]; root of context hierarchy
传入ArgsType
paramA
传入String
paramB
- advice
spring提供了五种advice,分别是:
- Before:前置通知,在方法执行之前执行增强处理
- AfterReturning:后置通知,在方法返回之后执行增强处理
- AfterThrowing:后置通知,在方法抛出异常之后执行增强处理
- After:后置通知,在方法退出之后执行增强处理,在finally之后
- Around:环绕通知,在方法执行之前和之后执行增强处理
其中,AfterReturning和AfterThrowing可以获取到返回值和Exception对象,如下:
/**
* @Author: kuromaru
* @Date: Created in 10:25 2019/4/8
* @Description:
* @modified:
*/
public class MyMethod {
public String testAfterReturn() {
return "testAfterReturn将被AfterReturn拦截";
}
public void testAfterThrowing() throws Exception {
throw new Exception("testAfterThrowing将被AfterThrowing拦截");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:31 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect {
@AfterReturning(pointcut = "execution(public String testAfterReturn(..))", returning = "retVal")
private void afterReturning(String retVal) {
System.out.println("打印返回值:");
System.out.println(retVal);
}
@AfterThrowing(pointcut = "execution(public void testAfterThrowing(..))", throwing = "te")
private void afterThrowing(Exception te) {
System.out.println("打印异常信息:");
System.out.println(te.getMessage());
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:40 2019/4/8
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:41 2019/4/8
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
myMethod.testAfterReturn();
myMethod.testAfterThrowing();
}
}
输出结果:
四月 08, 2019 10:43:13 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 10:43:13 CST 2019]; root of context hierarchy
打印返回值:
Exception in thread "main" java.lang.Exception: testAfterThrowing将被AfterThrowing拦截
testAfterReturn将被AfterReturn拦截
打印异常信息:
at configure.advice.MyMethod.testAfterThrowing(MyMethod.java:16)
testAfterThrowing将被AfterThrowing拦截
at configure.advice.MyMethod$$FastClassBySpringCGLIB$$df527efb.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
at configure.advice.MyMethod$$EnhancerBySpringCGLIB$$57307256.testAfterThrowing()
at configure.advice.Client.main(Client.java:18)
- AOP参数
- JoinPoint:
/**
* @Author: kuromaru
* @Date: Created in 11:25 2019/4/8
* @Description:
* @modified:
*/
public class MyParam {
private String name;
private int age;
private String interest;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getInterest() {
return interest;
}
public void setInterest(String interest) {
this.interest = interest;
}
}
/**
* @Author: kuromaru
* @Date: Created in 11:25 2019/4/8
* @Description:
* @modified:
*/
public class MyMethod {
public void printParam(MyParam myParam) {
System.out.println(myParam.getName() + ": 今年" + myParam.getAge() + "岁,喜欢" + myParam.getInterest());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:22 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect {
@Pointcut("execution(public void MyMethod.*(..))")
private void pointcut() {
}
@Before("pointcut()")
private void before(JoinPoint joinPoint) {
System.out.println(joinPoint.getTarget().toString());
System.out.println(joinPoint.getThis().toString());
System.out.println(Arrays.toString(joinPoint.getArgs()));
System.out.println(joinPoint.getKind());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:25 2019/4/8
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:26 2019/4/8
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
MyParam myParam = new MyParam(){{
setAge(20);
setName("令狐冲");
setInterest("吸星大法");
}};
myMethod.printParam(myParam);
}
}
四月 08, 2019 1:48:56 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 13:48:56 CST 2019]; root of context hierarchy
configure.args.joinPoint.MyMethod@5b12b668
configure.args.joinPoint.MyMethod@5b12b668
[configure.args.joinPoint.Client$1@6150c3ec]
method-execution
令狐冲: 今年20岁,喜欢吸星大法
- 参数名绑定
/**
* @Author: kuromaru
* @Date: Created in 13:22 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect {
@Pointcut("target(MyMethod) && args(myParam))")
private void pointcut(MyParam myParam) {
}
@Before("pointcut(myParam)")
private void before(MyParam myParam) {
System.out.println(myParam.getName());
System.out.println(myParam.getAge());
System.out.println(myParam.getInterest());
}
}
输出结果:
四月 08, 2019 2:04:43 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 14:04:43 CST 2019]; root of context hierarchy
令狐冲
20
吸星大法
令狐冲: 今年20岁,喜欢吸星大法
这种通过参数名绑定的方式来注入参数对象的形式,只适用于target、this和args,如果在其他的匹配类型中使用,则会抛出异常:
四月 08, 2019 2:06:46 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 14:06:46 CST 2019]; root of context hierarchy
四月 08, 2019 2:06:46 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 name binding only allowed in target, this, and args pcds
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 name binding only allowed in target, this, and args pcds
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.(AnnotationConfigApplicationContext.java:84)
at configure.args.arg.Client.main(Client.java:15)
Caused by: java.lang.IllegalArgumentException: error at ::0 name binding only allowed in target, this, and args pcds
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:319)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:217)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:190)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:169)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:220)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:279)
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:311)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:119)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:89)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:70)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:346)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:298)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:423)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1638)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
... 10 more
- argNames绑定参数名
@Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
/**
* @Author: kuromaru
* @Date: Created in 11:25 2019/4/8
* @Description:
* @modified:
*/
public class MyMethod {
@MyAnnotation
public void printParam(MyParam myParam) {
System.out.println(myParam.getName() + ": 今年" + myParam.getAge() + "岁,喜欢" + myParam.getInterest());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:22 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect {
@Pointcut(value = "execution(public void MyMethod.*(..)) && args(myParam) && @annotation(theMethod)", argNames = "myParam, theMethod")
private void pointcut(MyParam myParam, MyAnnotation theMethod) {
}
@Before("pointcut(myParam, theMethod)")
private void before(MyParam myParam, MyAnnotation theMethod) {
System.out.println(myParam.getName());
System.out.println(myParam.getAge());
System.out.println(myParam.getInterest());
System.out.println(theMethod.annotationType());
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:25 2019/4/8
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:26 2019/4/8
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
MyMethod myMethod = acac.getBean(MyMethod.class);
MyParam myParam = new MyParam(){{
setAge(20);
setName("令狐冲");
setInterest("吸星大法");
}};
myMethod.printParam(myParam);
}
}
输出结果:
四月 08, 2019 2:24:00 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 14:24:00 CST 2019]; root of context hierarchy
令狐冲
20
吸星大法
interface configure.args.argNames.MyAnnotation
令狐冲: 今年20岁,喜欢吸星大法
- AOP排序
可以通过实现Ordered接口来达到可排序的效果。如下:
/**
* @Author: kuromaru
* @Date: Created in 13:22 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect implements Ordered{
@Pointcut(value = "execution(public void MyMethod.*(..)) && args(myParam) && @annotation(theMethod)", argNames = "myParam, theMethod")
private void pointcut(MyParam myParam, MyAnnotation theMethod) {
}
@Before("pointcut(myParam, theMethod)")
private void before(MyParam myParam, MyAnnotation theMethod) {
System.out.println(myParam.getName());
System.out.println(myParam.getAge());
System.out.println(myParam.getInterest());
System.out.println(theMethod.annotationType());
}
@Override
public int getOrder() {
return -1;
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:22 2019/4/8
* @Description:
* @modified:
*/
@Aspect
public class MyAspect2 implements Ordered{
@Pointcut(value = "execution(public void MyMethod.*(..)) && args(myParam) && @annotation(theMethod)", argNames = "myParam, theMethod")
private void pointcut(MyParam myParam, MyAnnotation theMethod) {
}
@Before("pointcut(myParam, theMethod)")
private void before(MyParam myParam, MyAnnotation theMethod) {
System.out.println("======================================");
}
@Override
public int getOrder() {
return 1;
}
}
/**
* @Author: kuromaru
* @Date: Created in 13:25 2019/4/8
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
@Bean
public MyAspect2 myAspect2() {
return new MyAspect2();
}
@Bean
public MyMethod myMethod() {
return new MyMethod();
}
}
输出结果:
四月 08, 2019 2:29:16 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 14:29:16 CST 2019]; root of context hierarchy
令狐冲
20
吸星大法
interface configure.args.argNames.MyAnnotation
======================================
令狐冲: 今年20岁,喜欢吸星大法
可以看到,getOrder()方法返回的数值较小的会优先执行。
- introduction
Introduction可以为一个类提供额外的处理能力,比如为A类提供B类的方法。如下:
/**
* @author kuromaru
*/
public interface Singer {
void sing();
}
/**
* @Author: kuromaru
* @Date: Created in 11:04 2019/4/3
* @Description:
* @modified:
*/
public interface Performance {
void jump();
}
/**
* @Author: kuromaru
* @Date: Created in 14:31 2019/4/3
* @Description:
* @modified:
*/
public class Dancer implements Performance {
public void jump() {
System.out.println("我能跳你脸上!");
}
}
/**
* @Author: kuromaru
* @Date: Created in 11:06 2019/4/3
* @Description:
* @modified:
*/
public class GreatSinger implements Singer {
public void sing() {
System.out.println("我会唱忐忑,@#¥%%");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:40 2019/4/3
* @Description:
* @modified:
*/
@Aspect
public class SingIntroduction {
@DeclareParents(value = "configure.introduction.Performance+", defaultImpl = GreatSinger.class)
public static Singer singer;
@Before("this(Singer+)")
private void before() {
System.out.println("我是歌者");
}
@Before("this(Performance+)")
private void before2() {
System.out.println("我是表演艺术家");
}
}
/**
* @Author: kuromaru
* @Date: Created in 11:06 2019/4/3
* @Description:
* @modified:
*/
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean(name = "dancer")
public Performance dancer() {
return new Dancer();
}
@Bean
public SingIntroduction singIntroduction() {
return new SingIntroduction();
}
}
/**
* @Author: kuromaru
* @Date: Created in 14:33 2019/4/3
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
Performance dancer = (Performance) acac.getBean("dancer");
Singer singer = (Singer) dancer;
singer.sing();
System.out.println("=====================================");
dancer.jump();
}
}
输出结果:
四月 08, 2019 2:44:27 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 08 14:44:27 CST 2019]; root of context hierarchy
我会唱忐忑,@#¥%%
=====================================
我是歌者
我是表演艺术家
我能跳你脸上!
可以看到,dancer直接强转成了singer,并且可以正常调用相关方法。而dancer在执行自己的方法时,同时触发了两个before操作。根据this的语义,可以知道,dancer的代理类既代理了dancer,也代理了singer。

浙公网安备 33010602011771号