上一節我們講到了SpringAop基於註解版本的織入和創建代理的流程。這一節我們來講兩個切面類。從前面知道 一個切面是有兩部分構成 增強 和切點 。上節我們也列舉了一些常用的切面 下面我們就來
介紹兩個功能比較相近的切面類。
NameMatchMethodPointcutAdvisor的使用
啓動配置類
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplicationBuilder(App.class).application();
ApplicationContext context = application.run(args);
UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
userServiceImpl.findUser();
}
@Configuration
public static class AppConfig{
/**
* 定義切面
* @return
*/
@Bean
public MethodBeforeAdvice methodBeforeAdvice(){
return new com.coledy.aop.advice.MethodBeforeAdvice();
}
/**
* 創建一個NameMatchMethodPointcutAdvisor 切面
* @return
*/
@Bean
public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
NameMatchMethodPointcutAdvisor matchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
matchMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
matchMethodPointcutAdvisor.setMappedNames("find*");
return matchMethodPointcutAdvisor;
}
}
}
被代理對象 和增強
public interface UserService {
void findUser();
}
@Component
public class UserServiceImpl implements UserService {
@Override
public void findUser() {
System.out.println("findUser....");
}
}
public class MethodBeforeAdvice implements org.springframework.aop.MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("我是一個前置增強 ");
}
}
啓動app 輸出
我是一個前置增強
findUser…
可以看到到這裏 我們看到了我們想要看到的結果。對象被代理了。值得注意的是NameMatchMethodPointcutAdvisor這個切面類 的setMappedNames 需要配置的是方法名字。支持通配符。
RegexpMethodPointcutAdvisor的使用
我們只在上面的例子的基礎上加一個後置增強 和一個切面配置
@SpringBootApplication
//@Import(RegisterAspectJAutoProxyRegisterar.class)
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplicationBuilder(App.class).application();
ApplicationContext context = application.run(args);
UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
userServiceImpl.findUser();
}
@Configuration
public static class AppConfig{
@Bean
public MethodBeforeAdvice methodBeforeAdvice(){
return new com.coledy.aop.advice.MethodBeforeAdvice();
}
@Bean
public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
regexpMethodPointcutAdvisor.setPatterns("*.findGoods");
regexpMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
return regexpMethodPointcutAdvisor;
}
}
}
public class CommonMethodAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("common after return");
}
}
同樣也達到了代理的目的。下面我們就看一下爲什麼我們只是簡單的在容器中定義了一個切面 Spring就幫我們織入了被代理的對象裏面。
SpringAop非註解式的源碼分析
看過上一篇文章的應該有印象 getAdvicesAndAdvisorsForBean 這個方法是AbstractAutoProxyCreator類唯一一個留給子類必須實現的方法 (獲取所有的切面由於不同子類的實現 獲取切面的方式不同 所以這裏交給了子類實現 模板方法。如BeanNameAutoProxyCreator就和我們經常使用的AnnotationAwareAspectJAutoProxyCreator 這個織入類的實現方式不一樣)。
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
跟進findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
//這裏爲了節省篇幅 直接跳到findCandidateAdvisors這個方法裏面來 我們上一節說過 AnnotationAwareAspectJAutoProxyCreator 重寫了這個方法
//但是在子類的方法有行代碼很關鍵List<Advisor> advisors = super.findCandidateAdvisors();
//先回去父類獲取Advisor 然後再用自己的邏輯獲取 父類是獲取實現了Advisor接口的切面
//子類是獲取有AspectJ註解的並且方法中有@Pointcut的方法 封裝成切面。
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
下面我們繼續看看 父類獲取Advisor的邏輯。其實和子類的獲取邏輯很類似。父類的這個更簡單因爲本來就是Advisor不需要解析包裝。
public List<Advisor> findAdvisorBeans() {
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
//獲取beanFactory中實現了Advisor接口的實例
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
//判斷當前bean是不是正在創建。 正在創建則不做任何處理。這裏需要注意的是 即使當前正在
//創建的bean是Advisor的子類 也不必擔心 這個類無法加入到advisors中因爲這個方法不止調用一次。每一個bean實例化的時候都會調用。
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
//這裏如果沒有創建就從beanFactory獲取bean 如果沒創建會創建 繼而加入進緩存中
//bean的創建流程請參考https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105183005
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
到這裏查找的過程就完了、、其實還是很簡單的,就是如果有實現Advisor接口的類。至於 是如何織入到代理類中的請參考上一篇https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105217189