zoukankan      html  css  js  c++  java
  • 吴裕雄天生自然SPRINGSpring AOP

    Spring AOP是Spring框架体系结构中非常重要的功能模块之一,该模块提供了面向切面编程实现。面向切面编程在事务处理、日志记录、安全控制等操作中被广泛使用。
        1.AOP的概念
        AOP(Aspect-Oriented Programming),即面向切面编程。它与OOP(Object-Oriented Programming,面向对象编程) 相辅相成,提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中,以类作为程序的基本单元,而AOP中的基本单元是Aspect(切面)。
    
        AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是AOP不是OOP的替代品,而是OOP的补充,它们相辅相成。

    AOP的术语
    
    在Spring AOP框架中,涉及以下常用术语。
    (1)切面
    切面(Aspect)是指封装横切到系统功能(如事务处理)的类。
    (2)连接点
    连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。
    (3)切入点
    切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。切面、连接点和切入点的关系如图所示。

    (4)通知(增强处理)
    由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。
    (5)引入
    引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。
    (6)目标对象
    目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象总是一个代理对象。
    (7)代理
    代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。
    (8)组入
    组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载器织入。
    基于注解开发AspectJ
    
    先了解一下Spring的通知类型。根据Spring中通知在目标类方法的连接点位置,可以分为6种如下类型:
    
    1.环绕通知
    环绕通知是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。
    2.前置通知
    前置通知是在目标方法执行前实施增强,可应用于权限管理等功能。
    3.后置返回通知
    后置返回通知是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。
    
    4.后置(最终)通知
    后置通知是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。
    5.异常通知
    异常通知是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。
    6.引入通知
    引入通知是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

    基于注解开发AspectJ的过程
    1.使用Eclipse创建Web应用并导入JAR包
    
    2.创建接口及实现类
    
    3.创建切面类
    
    4.创建配置类
    
    5.创建测试类
    
    6.运行测试类
    package aspectj.dao;
    
    public interface TestDao {
        public void save();
        public void modify();
        public void delete();
    
    }
    package aspectj.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository("testDao")
    public class TestDaoImpl implements TestDao {
        @Override
        public void save() {
            System.out.println("保存");
        }
    
        @Override
        public void modify() {
            System.out.println("修改");
        }
    
        @Override
        public void delete() {
            System.out.println("删除");
        }
    }
    package aspectj.annotation;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    /**
     * 切面类,在此类中编写各种类型通知
     */
    @Aspect // @Aspect声明一个切面
    @Component // @Component让此切面成为Spring容器管理的Bean
    public class MyAspect {
        /**
         * 定义切入点,通知增强哪些方法。 "execution(* aspectj.dao.*.*(..))" 是定义切入点表达式,
         * 该切入点表达式的意思是匹配aspectj.dao包中任意类的任意方法的执行。
         * 其中execution()是表达式的主体,第一个*表示的是返回类型,使用*代表所有类型;
         * aspectj.dao表示的是需要匹配的包名,后面第二个*表示的是类名,使用*代表匹配包中所有的类; 第三个*表示的是方法名,使用*表示所有方法;
         * 后面(..)表示方法的参数,其中“..”表示任意参数。 另外,注意第一个*与包名之间有一个空格。
         */
    
        @Pointcut("execution(* aspectj.dao.*.*(..))")
        private void myPointCut() {
        }
    
        /**
         * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
         */
        @Before("myPointCut()") // myPointCut()是切入点的定义方法
        public void before(JoinPoint jp) {
            System.out.print("前置通知:模拟权限控制");
            System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
        }
    
        /**
         * 后置返回通知
         */
        @AfterReturning("myPointCut()")
        public void afterReturning(JoinPoint jp) {
            System.out.print("后置返回通知:" + "模拟删除临时文件");
            System.out.println(",被增强处理的方法:" + jp.getSignature().getName());
        }
    
        /**
         * 环绕通知 ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法 返回值类型必须是Object
         * 必须一个参数是ProceedingJoinPoint类型 必须throws Throwable
         */
        @Around("myPointCut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            // 开始
            System.out.println("环绕开始:执行目标方法前,模拟开启事务");
            // 执行当前目标方法
            Object obj = pjp.proceed();
            // 结束
            System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
            return obj;
        }
    
        /**
         * 异常通知
         */
        @AfterThrowing(value = "myPointCut()", throwing = "e")
        public void except(Throwable e) {
            System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
        }
    
        /**
         * 后置(最终)通知
         */
        @After("myPointCut()")
        public void after() {
            System.out.println("最终通知:模拟释放资源");
        }
    
    }
    package aspectj.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration // 声明一个配置类
    @ComponentScan("aspectj") // 自动扫描aspectj包下使用的注解
    @EnableAspectJAutoProxy // 开启Spring对AspectJ的支持
    public class AspectjAOPConfig {
    
    }
    package aspectj.config;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import aspectj.dao.TestDao;
    
    public class AOPTest {
    
        public static void main(String[] args) {
            // 初始化Spring容器ApplicationContext
            AnnotationConfigApplicationContext appCon = new AnnotationConfigApplicationContext(AspectjAOPConfig.class);
            // 从容器中,获取增强后的目标对象
            TestDao testDaoAdvice = appCon.getBean(TestDao.class);
            // 执行方法
            testDaoAdvice.save();
            System.out.println("================");
            testDaoAdvice.modify();
            System.out.println("================");
            testDaoAdvice.delete();
            appCon.close();
        }
    
    }

     

  • 相关阅读:
    关于冥想
    Read Later
    你追求的跟我相反
    UML for Java Programmers之dx实战
    20140525
    面试基础-语言基础篇
    面试基础-linux操作系统篇
    面试基础-数据库篇
    面试基础-计算机网络篇
    Eclipse同时编译多个cpp文件
  • 原文地址:https://www.cnblogs.com/tszr/p/15310243.html
Copyright © 2011-2022 走看看