AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
首先对AOP当中的几个术语进行一下解释:
切面:切点、连接点、通知所在的那个类称为切面。
切点:pointcut,切点是连接点的集合。
连接点:在SpringAOP中,连接点的最小单位称之为方法,每一个方法我们称之为一个连接点。连接点可以用表达式来表达,多个连接点就组成了切点。
通知:切入连接的时机。
然后简单的做一个SpringAOP的应用吧
@Aspect
@Component
public class NotVeryUsefulAspect {
@Pointcut("execution(* com.sherlock.service..*.*(..))")
public void anyOldTransfer() {}
@Before("anyOldTransfer()")
public void advice() {
System.out.println("前置通知。。。");
}
}
这就是切面
@Configuration
@ComponentScan("com.sherlock")
@EnableAspectJAutoProxy
public class AppConfig {
}
配置类
好了,这样一个简单的SpringAOP的应用就已经完成了,接下来就对SpringAOP的原理做一个解释,我采用的是断点调试的方法。
首先,写一个简单的测试类
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService)context.getBean(UserService.class);
userService.test();
}
进行断点调试

可以发现在调用userService.test();的时候,userService就已经被代理了,使用的是cglib的动态代理。那么问题就来了:是在执行ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)这一行就完成了代理,还是在UserService userService = (UserService)context.getBean(UserService.class);这一行完成的代理呢?别急,我们用源码说话!
我们先从UserService userService =(UserService)context.getBean(UserService.class)这一行的断点进入,前面都是一些简单的调用,直到来到了这里

这里其实有个小技巧啊,Spring中要获取的Bean有很多,那要找到我们要的userService比较麻烦,那么我们就可以采用条件断点。

这样就可以快速的找到我们要的userService了。但这里其实有一点比较坑啊,Spring本身在初始化的时候也会调用这个方法,就会导致sharedInstance的值为null。那我们就需要先让Spring初始化完。就是先把这一行的断点取消,等Spring初始化完了,到达了UserService userService =(UserService)context.getBean(UserService.class)这一行的时候,再在这里打上断点。

好,现在可以发现,在完成Object sharedInstance = this.getSingleton(beanName);这一行后,代理已经完成了。那就说明这一行代码完成了代理,我们就需要进入看这一行代码。这其实也是一种技巧啦,Spring的代码这么多,我们不可能全部看完。要带有目的性的去看,看哪一行代码对你想要的结果产生了影响,那些没有产生影响的代码我们就可以暂时的忽略。
言归正传,我们进到getSingleton方法里面,和刚才一样,为了过滤其他的bean,我们要打上条件断点。

可以发现,此时代理已经完成。那么下面的这一行代码就是关键了
Object singletonObject = this.singletonObjects.get(beanName);
代理对象从singletonObjects中被get出来了,那我们看看singletonObjects是什么。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
可以看到singletonObjects 是一个ConcurrentHashMap,从Map中get获取了代理对象。那这个时候我们就可以自然而然的想到,要能get到的前提是什么?没错!就是要先put, 我们通过全局搜索就可以找到

我们在这里再打一个断点,进行调试

可以发现啊,此时Spring将这个bean放到singletonObjects(可以简单理解为IOC容器,但其实这个说法应该是错误的) 里面的时候,代理就已经完成了。也就是说,最开始的那个问题解决了,在Spring容器初始化完成的时候,代理就已经完成了。
关于SpringAOP源码的东西实在太多了,本文就先确定了一下代理这件事到底发生在什么时候,本人才疏学浅,以上纯属个人理解,如有不对,还望批评指正。
网友评论