2025年4月

前面两篇文章我们讲解了整个AOP切面的实现,刚开始我以为AOP也就这么多东西了,而当我总结起来时才发现,这仅仅是AOP的开始,相信你看完本篇文章会有:曲径通幽,豁然开朗 的感觉

从AOP到方法论

我们还是来看看AOP为我们搭建了一个怎样的框架(或者说一个黑盒吧):整个链路的流程如下:

准备阶段:查找所有增强器--> 筛选可以应用到当前对象的增强器 --> 构建代理对象

执行阶段:当代理对象的方法被调用时 --> 构建责任链 --> 执行时走责任链逻辑;

其中查找所有的增强器:Spring会获取所有的Advisor,如果开启了@Aspect的功能就会额外加载标有@Aspect的类,然后将标有@Before、@After等注解的方法封装成Advisor【InstantiationModelAwarePointcutAdvisorImpl】;
筛选可以应用到当前对象的增强器:主要分类两类的筛选:一类是IntroductionAdvisor即类级别的切入,这一类是通过Advisor中的ClassFilter来进行判断是否切入当前对象;另一个是PointcutAdvisor即方法级别的切入,会通过PointCut中的ClassFilter和MethodMatcher分别来进行类及方法级别的筛选,判断是都切入当前方法。构建代理对象,以及执行时责任链的封装这些都不需要我们来管的。总的来说,我们如果需要使用Sping的这套AOP框架来实现我们的业务逻辑,我们只需要写好自己的增强器(Advisor)就可以了,而这个过程中就包含了两个主要的部分:1、编写Advice,也就是我们的拦截逻辑;2、拦截的筛选规则,也就是上面提到的ClassFilter或者PointCut;也就是告诉Spring你要拦截什么,怎么拦截。
好了,说到这里我相信很多人应该都明白如何通过Spring提供的AOP框架来实现自己的业务逻辑拦截,如果没懂也没关系,接下来我们看看Spring家自己是如何在这个AOP框架的基础上扩展出更多功能的:

AOP方法论在Spring中的身影

1、Spring的事务实现

要看Spring的事务是如何实现的,我们看看开启Spring事务时都引入了什么?那我们就要从@EnableTransactionManagement注解到入了什么组件到容器中下手,let we see see :
@EnableTransactionManagement导入了TransactionManagementConfigurationSelector类,TransactionManagementConfigurationSelector帮我们导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration这两个类,前者是为了能开启AOP的功能,我们先不说,ProxyTransactionManagementConfiguration这个类才是Spring事务发挥作用的关键先生,一起来一探究竟吧:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    // 创建了用于事务的Advisor
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
            TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        // 设置transactionAttributeSource
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        // 设置Advice
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }

    //封装事务管理配置的参数,@Transactional(..)中的参数
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

}

哦豁,映入眼帘的是熟悉的Advisor和Advice,也是验证我们上面所说的方法论。就从我们熟练使用Spring申明是事务注解@Transactional的角度来说,PointCut中肯定就会匹配@Transactional这个注解,然后在Advice中帮我们建立连接,开启事务,执行sql,提交或回滚事务。详细的事务讲解,我会留在下一个模块中来讲解,毕竟Spring事务中还是有很多有趣的点的,而且他也是面试的常客之一啊,不单独给一个模块描述不应该。

2、Spring的缓存实现

在查看了Spring事务的逻辑,再来看Spring的缓存逻辑其实是大差不差的,我们直接来看开启Spring缓存的@EnableCaching注解为我们向Spring中导入了什么组件,其实在不引入JSR107和JcacheImpl的情况下,也是为我们引入了两个组件:AutoProxyRegistrar和ProxyCachingConfiguration,同样,前者我们先不关心,我们来看看ProxyCachingConfiguration里给我们带来了什么:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

    @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
            CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {

        BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
        advisor.setCacheOperationSource(cacheOperationSource);
        advisor.setAdvice(cacheInterceptor);
        if (this.enableCaching != null) {
            advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
        }
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
        CacheInterceptor interceptor = new CacheInterceptor();
        interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
        interceptor.setCacheOperationSource(cacheOperationSource);
        return interceptor;
    }

}

我相信,你看到这一串代码,你都不敢相信吧,这和事务的逻辑几乎一模一样啊,是不是,CacheAdvisor以及CacheInterceptor,都是同样的套路,都是上面所说的方法论,用起来简直不要太方便。

3、Spring异步的实现

看了上面Spring事务和Spring缓存的实现,对于Spring异步De实现你是不是也能猜到是怎么实现的了,我们直接看@EnableAsync引入的ProxyAsyncConfiguration吧:

    public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        bpp.configure(this.executor, this.exceptionHandler);
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
        return bpp;
    }

}

乍看第一眼,我们并没有看到我们预料中的Advisor和Interceptor(Advice),而是构建了一个Bean的后置处理器AsyncAnnotationBeanPostProcessor,并为这个后置处理器配置了线程池executor和异常处理器exceptionHandler,那么异步的处理就是在AsyncAnnotationBeanPostProcessor中实现的,我们在进一步看看AsyncAnnotationBeanPostProcessor这个类,它里面的实现究竟是怎么样的呢?

    public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {


    public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
            AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;


    protected final Log logger = LogFactory.getLog(getClass());

    // 此处省略若干行代码

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);

        // 此处注入了 AsyncAnnotationAdvisor
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

}

其中的一起配置方法我们不看,我们就看下最后的setBeanFactory方法,哈哈,惊不惊喜,意不意外,一个大大的Advisor映入了眼帘,这不就是我们期望的吗,读者可能会问那Adice在哪呢?难道你忘记了Advisor是Advice和Pointcut的整合,那么Advisor中当然包含了Advice和Poincut的构建了,不妨一看:

        public AsyncAnnotationAdvisor(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
        asyncAnnotationTypes.add(Async.class);
        try {
            asyncAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            // If EJB 3.1 API not present, simply ignore.
        }
        this.advice = buildAdvice(executor, exceptionHandler);
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }

buildAdvice,buildPointcut这两个方法就是分别来构建Advice和Pointcut的。
但是Spring异步的实现和前面两个的实现确实是有点不同的,首先我们发现在注入的时候@EnableAsync仅仅引入了一个ProxyAsyncConfiguration,并没有像前面两个一样引入AutoProxyRegistrar,而AutoProxyRegistrar的作用就是基于自己是BeanPostProcessor通过bean的生命周期方法postProcessBeforeInstantiation来帮助完成扫描缓存所有的Advisor,然后再通过bean的生命周期方法postProcessAfterInstantiation帮忙给可以应用Advisor的bean创建代理对象,而能够在执行的时候走到责任链的拦截模式关键就是创建出代理对象;那么Spring异步是如何来帮助我们创建代理对象的呢?其实你被忘了,Spring异步的实现本来就是靠的一个BeanPostProcesor即AsyncAnnotationBeanPostProcessor,所以Spring家将创建代理对象的逻辑也就放在了AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法中。看到这个地方,你有没有一个疑问,如果我同时在一个方法上标注了@Transactional和@Async注解,那么它不是会走两遍创建代理对象的逻辑吗?确实是这样的,Spring也对这一情况做了兼容,来一起看下AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization这个方法:

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (this.advisor == null || bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }

        // 兼容走aop的逻辑已经生成了的代理对象,那么只需要将异步的这个增强器添加到代理对象的增强器集合中就可以了
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                // Add our local Advisor to the existing proxy's Advisor chain...
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }
        // 帮助创建代理对象
        if (isEligible(bean, beanName)) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            return proxyFactory.getProxy(getProxyClassLoader());
        }

        // No proxy needed.
        return bean;
    }

如果当前这个bean已经经历过了AOP的增强成代理对象,那么它就属于Advised类型的,我们只需要把当前这个异步的Advisor加入到这个代理对象的增强器链中就可以了;而如果这个bean对象没有经历过AOP的代理,那么就会在此处为它创建代理对象,然后的执行流程都是一样的。至于为什么Spring在实现异步封装的时候没有像实现事务和缓存那样直接基于AOP框架来实现,我也不是很清楚,可能是为了提供一种新的方式来实现告诉读者是可以一题多解的吧,也有可能就是不同的人有不同的想法呢,毕竟作者也不是同一个人,而且这种情况在Spring中也不是第一次出现了,前面我们讲的增强器转拦截器一样使用两种不同的实现方法。

写在最后

好了,说到这里这篇文章也就差不多了,虽然我只讲了Spring中事务,缓存,异步这三种实现的应用,但是在Spring中基于AOP框架的实现不仅仅于此,还有@Validated以及mybatis-plus中多数据源@DS的实现,等等等等。我们需要的是掌握这个方法论,懂了方法论,不管是在以后的开发中,还是以后看别的源码时,就能很快的了解实现的原理,是事倍功倍的。关于AOP就讲到这里了;接下来,我会开始讲解Spring的事务实现,这也是面试的一个高频点,希望对有缘人有所帮助。

原文链接:https://www.jianshu.com/p/912a23e5f01b

IDEA 全称 IntelliJ IDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。IDEA是JetBrains公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。它的旗舰版本还支持HTML,CSS,PHP,MySQL,Python等。免费版只支持Java,Kotlin等少数语言。

俗话说:"工欲善其事必先利其器",今天准备和大家一起分享一下实际工作中能提升幸福感和工作效率的IDEA插件。

废话不多说,let's go !!!

1Alibaba Java Coding Guidelines

在你需要检查的代码上面,点击右键,选择编码规约扫描


image.png

将会出现如下所示的检查结果,并会给出编码规范和提示:

image.png

2GsonFormat

【jsonToBean】

在平时的开发中,将json转为Bean

image.png

3A8Translation

【翻译的插件】

选中你要翻译的汉语或英文,点击鼠标右键,选择Translate, (快捷键是Alt+T)就会实现翻译,不用再去切换屏幕使用翻译软件翻译了。

image.png

4Maven Helper

【分析依赖冲突的插件】

此插件可用来方便显示maven的依赖树,和显示冲突,在我们梳理依赖时帮助很大。

image.png

5Free Mybatis plugin

【增强idea对mybatis支持的插件】

生成mapper xml文件 快速从代码跳转到mapper及从mapper返回代码 mybatis自动补全及语法错误提示 集成mybatis generator gui界面 这个插件超级实用,可以从mapper接口跳转到mybatis的xml文件中,还能找到对应地方

image.png

6Grep Console

【日志高亮显示插件】

image.png

可以Ctrl + Alt +S Other Settings进行配色(颜色参考请搜索:电脑最护眼的色彩、显示屏亮度亦如此)

7Lombok

当我们创建一个实体时,通常对每个字段去生成GET/SET方法,但是万一后面需要增加或者减少字段时,又要重新的去生成GET/SET方法,非常麻烦。可以通过该插件,无需再写那么多冗余的get/set代码。

注意:需要在pom引入依赖

<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

8Nyan progress bar

基于Idea使用的漂亮进度条。把你的加载进度条全都变成彩虹和猫咪

image.png

9FindBugs-IDEA

【Bug检查插件】

在文件上或文件里面点击鼠标右键,选择FingBugs

简单的bug能给你快速找回来,什么NPE啥的,小儿科!推荐!!(嗓门大就说一遍)

10Key Promoter X

你还在为记不住快捷键烦恼吗,Key Promoter X可以帮助你快速记住常用的快捷键。当你在idea中用鼠标点击菜单,它可以显示对应的快捷键以及点击次数。使用一段时间后有助于过渡到更快、无鼠标的开发。

image.png

11JavaDoc

在项目中经常要求写代码注释,否则不能通过代码门禁,JavaDoc工具可以一键生成注释。

插件安装成功后在菜单栏 code -> JavaDocs可以找到

image.png

自动生成注释效果如下:

image.png

12ignore

项目开发中通常会使用到git进行版本管理,在提交代码时经常有人将本地的不必要的文件提交到代码仓库中,使用.ignore插件可以很好解决这个问题。插件安装完成后会在项目中生成一个.ignore文件,编辑该文件忽略一些动态生成的文件,如class文件,maven的target目录等。

image.png

13RainbowBrackets

彩虹括号,代码中有多个括号会显示不同的颜色。

image.png

14Activate-power-mode

在敲代码时有抖动酷炫的特效,非常适合给前端小姐姐表演特技。(坏笑)

15CodeGlance

image.png

16GenerateAllSetter

  • 通过alt+enter对变量类生成对类的所有setter方法的调用
  • 当两个对象具有相同的字段时生成一个转换器
  • 当returnType为List Set Map时生成默认值
  • 在所有getter方法上生成对assertThat的调用
image.png

17RestfulToolkit

  • 根据 URL 直接跳转到对应的方法定义 ( 快捷键搜索 Ctrl + Alt + N 或者 Ctrl + \ ); —这个个人感觉非常好用,和Ctrl + F一样重要。
  • 提供了一个 Services tree 的显示窗口;
  • 一个简单的 http 请求工具;
  • 在请求方法上添加了有用功能: 复制生成 URL;,复制方法参数…
  • 其他功能: java 类上添加 Convert to JSON 功能,格式化 json 数据 ( Windows: Ctrl + Enter; Mac: Command + Enter )。
image.png

18JRebel

热门部署插件,让你在修改完代码后,不用再重新启动,很实用!但是,不是免费的,需要大家发挥下聪明才智自行百度破解!

image.png

19Json Parser

厌倦了打开浏览器格式化和验证JSON?为什么不安装JSON解析器并在IDE中使用离线支持呢?JSON解析器是一个用于验证和格式化JSON字符串的轻量级插件。

image.png

20aiXcode & codota

aiXcoder是一个强大的代码完成器和代码搜索引擎,基于最新的深度学习技术。它有可能向您推荐一整套代码,这将帮助您更快地编写代码。aiXcoder还提供了一个代码搜索引擎,以帮助您在GitHub上搜索API用例。

类似功能的插件还有codota,codota基于数百万个开源Java程序和您的上下文来完成代码行,从而帮助您以更少的错误更快地进行编码。新版本的codota提供以下功能:

  • 全线AI自动完成
  • 内联和相关代码示例
  • 根据用户自己的编码实践进行编码建议
  • 不用担心你的代码会被公开。codota不会将你的代码发送到codota服务器,它只会从当前编辑的文件中发送最少的上下文信息,从而使codota能够根据你当前的本地范围进行预测。

比如我想知道list.stream.map(…)方法的参考使用,只需要光标定位在map上,然后右键选择菜单“Get relevant examples”或者使用快捷键“Ctrl + Shift + O”就可以快速搜索出来很多示例,非常方便。

image.png

来源:blog.csdn.net/CSDN_SAVIOR/article/details/122505538

原文链接:https://www.jianshu.com/p/39e2142b1075

续70同学的《Its about time》,尝试阐述一下typecho的简洁哲学,希望能从下面三点让大家了解我们作为一个小团队,是如何设计程序架构、用户界面及取舍各种需求的,不足之处请多多指教。

安静的容器

快节奏的生活里总是有很多酸甜苦辣,五味杂陈,我们用文字、图片、音频在博客上记录下这些精彩或不精彩的片段,分享给所有能看到的人并希望能获得反馈。typecho想提供这样一个产品,它如同安静的玻璃瓶,简简单单,没有什么花巧的装饰。撰写者只需要考虑往容器里放什么,而不是怎么放;容器能忠实呈现给读者的,是撰写者独一无二的生活,而不是容器自己。

所以, typecho的前后端界面设计,都是趋向简约的,不吵闹的,我们追求“刚刚好”,我们想让typecho就是一张白纸,也许有些质感,但重要的是你用什么色彩在书写,这白色不会成为干扰,而会让这些色彩更凸显。

有限的需求满足

typecho不是我们的全部,时间精力的限制,不允许所有的需求都被满足。我们持有这样的观点:只针对有限的需求去开发有限的功能,并让这些功能表现卓越。所以typecho可能缺少一些你偶尔会用到的功能,但是你经常使用的部分,它一定就在那里,而且会给你惊喜。我们在开发的讨论中也提出开发各种新奇的特性,实验各种领先的技术,但大部分都不会运用到typecho。欲望需要收敛,因为现有功能都不够完美。

收敛的需求也带来负面的影响,typecho不是一款能兼容所有环境的程序,它常常在rewrite或其他方面有一些瑕疵,这方面wordpress做得更棒。

需求的有限满足

经常有用户说需要编辑器、需要友情链接管理、需要置顶日志或其他什么功能,这些都可以通过插件来变通实现。实际上很多易用性问题,我们都寄希望于第三方开发来完善,这也是官方文档建设再次被提上日程并承诺重视的原因,开放会带来期待中的多样性,造就一个有趣的小生态系统。typecho只能有限满足一部分用户对博客程序的要求,可预见的未来里,它也不会是一个CMS。

选择权在用户手中,如果你需要一个简洁的博客程序,typecho就在这里。

标签:none

是的,你没有看错,经过漫长的等待,我们终于发布了 Typecho 1.2.0 正式版。这个版本的开发经历了如此长的时间,以至于我无法一次性列出这么长的详细改进列表。

除了修复了很多bug以外,我们重要的改进有如下几点:

  1. 新的编辑器实时预览自动跟随效果,以及主题内预览功能
  2. 后台界面对移动端的适配
  3. 新的安装程序
  4. 进入 PHP 7.2 时代,以及对 PHP 8 的更好支持
  5. 容器化的更好支持

除了新功能,大家也可以发现我们的版本号规则有变化。新的版本号会采用常规的3组数字组成,比如1.2.0。它适合小步快跑的开发模式,具体的解释可以看这篇文章

这个小小的软件记录了我们很多人生命中最美好的时光,大家用它在缤纷多彩的互联网上留下自己的印记。快乐,伤痛,成长,迷惘,时间无情地流过,我们却让它有了意义。这时我们再回头来看 Typecho 的名称组成:轻击键盘,静候回音。在你写下文字的时候,涟漪已然产生。

而于我来说,十多年前键盘敲下的第一行代码,也不知不觉产生了这么大的回响。在这春回大地之际,感怀之余,未免又对未来心生希望起来。让我们大声宣布,Typecho 回来了!