• Spring之旅(2)


    Spring简化Java的下一个理念:基于切面的声明式编程

    3、应用切面

    依赖注入的目的是让相互协作的组件保持松散耦合;而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件

    AOP面向切面编程被定义为促使应用程序分离关注点的一项技术。系统由许多不同的组件组成,每个组件除了负责某一特定的功能,还要承担额外的职责,诸如日志、事务管理和安全等等的服务经常融入到自身的核心业务逻辑中去,这些服务统称为横向关注点,因为它们总是跨越系统的各个组件。

    将这些代码分散到多个组件,会导致双重复杂性:

    1. 如果要修改关注点得逻辑,必须修改各个组件的相关实现。即使你把这些关注点抽象成一个独立的模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中(耦合);
    2. 组件代码会因为那些与自身核心业务无关的代码而变得混乱。

    假设你需要使用吟游诗人这个服务类来记载骑士(BraveKnight)的所有事迹,建立Minstrel(吟游诗人)类。

    package com.test.knights;
     
    public class Minstrel {
        public void singBeforeQuest() {
            System.out.println("Fa la la; The knight is so brave!");
        }
     
        public void singAfterQuest() {
            System.out.println("Tee hee he; The brave knight do a quest");
        }
    }

    让我们做适当的调整来让BraveKnight来使用Minstrel

    package com.test.knights;
     
    public class BraveKnight implements Knight {
        private Quest quest;
        private Minstrel minstrel;
     
        public BraKnight(Quest quest, Minstrel minstrel) {
            this.quest = quest;
            this.minstrel = minstrel;
        }
     
        public void embrakOnQuest() throws QuestException {
            minstrel.singBeforeQuest();
            quest.embrak();
            minstrel.singAfterQuest();
        }
    }

    这样达到了预期的效果:骑士做任务前执行singBeforeQuest()方法、任务后执行singAfterQuest()方法。但是管理吟游诗人真的是骑士的责任吗?骑士在依赖注入时就必须提供一个吟游诗人,这显然是不合逻辑的。吟游诗人应该自己独立出来,他有自己分内的事情。

    改进,把Minstrel抽象为一个切面,你所做是是在Spring配置文件中声明它。

    <?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-3.0.xsd">
        <bean id = "knight" class = "com.test.knight.BraveKnight">
            <constructor-arg ref = "quest" />
        </bean>
          
        <bean id = "quest" class = "com.test.knight.SlayDragonQuest" />
     
        <!-- add -->
        <bean id = "minstrel" class = "com.test.knight.Minstrel" />
        <aop:config>
            <aop:aspect ref="minstrel">
                <aop:pointcut id="embark" expression="execution(* *.embrakOnQuest(..))" />
                <aop:before pointcut-ref="embark"> method="singBeforeQuest" />
                <aop:after pointcut-ref="embark"> method="singAfterQuest" />
            </aop:aspect>
        </aop:config>
    </beans>
    

    这里将minstrel Bean声明为一个切面,pointcut定义了一类切入点embark,expression含义是在任意返回类型、任意对象调用方法、任意入参的embrakOnQuest 方法都有效。

    <aop:before>称为前置通知,<aop:after>称为后置通知。从这个示例中获得两个重要的观点:

    1. Minstrel仍然是一个POJO,没有任何代码表示它将被作为一个切面使用。
    2. Minstrel可以被应用到BraveKnight中,而BraveKnight不需要显示地调用。实际上,BraveKnight完全不知道Minstrel的存在。
  • 相关阅读:
    Linux上将文件夹复制到指令目录
    将PC版网页转为手机端自适应网页
    WCF初探-18:WCF数据协定之KnownType
    WCF初探-17:WCF数据协定之等效性
    WCF初探-16:WCF数据协定之基础知识
    WCF初探-15:WCF操作协定
    2018数学二21题解法分析
    柯西不等式:简单常考形式
    等价、合同、相似、正交变换;二次型,正定,惯性指数
    高数狄利克雷收敛条件(傅里叶)
  • 原文地址:https://www.cnblogs.com/houkai/p/4691478.html
Copyright © 2020-2023  润新知