• JAVA入门[14]-Spring MVC AOP


    一、基本概念

    1.AOP简介

    DI能够让相互协作的软件组件保持松散耦合;而面向切面编程(aspect-oriented programming,AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题

    常见场景:日志、安全、事物、缓存

    Image(2)

    2.AOP用到的一些术语

    项目中每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类似的辅助功能,例如安全和事务管理,这时候需要引入AOP的概念。

    Image(3)

    通知定义了切面是什么以及何时使用, Spring切面可以应用5种类型的通知:

    • 前置通知(Before):在目标方法被调用之前调用通知功能;
    • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
    • 返回通知(After-returning):在目标方法成功执行之后调用通知;
    • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
    • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

    连接点(join potint)是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为

    切点(poincut)的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点

    二、准备service模块

    1.service bean

    public class CategoryService1 {
        public void add(int id) {
            System.out.println("CategoryService1.add()");
        }
    }
    
    public class CategoryService2{
        public void add(int id) {
            System.out.println("CategoryService2.add()");
        }
    }

    2.配置bean

    <?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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
    
    <bean id="categoryServiceImpl" class="service.CategoryService1"></bean>
    <bean id="CategoryServiceImpl2" class="service.CategoryService2"></bean>
    </beans>

    3.单元测试

    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("aop.xml");
    
        CategoryService1 service1=context.getBean(CategoryService1.class);
        service1.add(1);
    
        CategoryService2 service2=context.getBean(CategoryService2.class);
        service2.add(2);
    }

    运行结果:

    CategoryService1.add()

    CategoryService2.add()

     

    三、XML方式声明AOP

    Spring所创建的通知都是用标准的Java类编写的, 定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写,这两种语法对于Java开发者来说都是相当熟悉的。

    注意Spring只支持方法级别的连接点。

    切入点表达式

    execution指示器是我们在编写切点定义时最主要使用的指示器

    Image(4)

    Demo

    我们要实现的一个简单示例是:在service方法调用前和调用后打印日志“write log”。

    public class LogHandler {
        public void log(){
            System.out.println("write log.");
        }
    }

    aop.xml添加配置:

    <bean id="logHandler" class="pointcut.LogHandler"></bean>
        <aop:config>
            <aop:aspect id="log" ref="logHandler">
                <aop:pointcut id="addLog" expression="execution(* service.*.*(..))"></aop:pointcut>
                <aop:before method="log" pointcut-ref="addLog"></aop:before>
                <aop:after method="log" pointcut-ref="addLog"></aop:after>
            </aop:aspect>
        </aop:config> 

    单元测试:

    public class AopTests {
        @Test
        public void test() {
            ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
            CategoryService1 service1 = context.getBean(CategoryService1.class);
            service1.add(1);
            CategoryService2 service2 = context.getBean(CategoryService2.class);
            service2.add(2);
        }
    }

    运行报错:

    org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException

    原来是忘了pom依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency> 

    运行结果:

    write log.

    CategoryService1.add()

    write log.

    write log.

    CategoryService2.add()

    write log.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>DemoStore</groupId>
        <artifactId>DemoAOP</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <spring.version>4.3.5.RELEASE</spring.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>RELEASE</version>
            </dependency>
        </dependencies>
    
    </project>
    完整的pom.xml

     

    四、aop:around

    通过使用环绕通知,可以实现前置通知和后置通知所实现的功能,而且只需要在一个方法中实现。

    public class LogTimeHandler {
        public void log(ProceedingJoinPoint jp) throws Throwable {
            try {
                System.out.println("1.before log "+new Date().getTime());//记录开始时间
                jp.proceed();
                System.out.println("2.after log "+new Date().getTime());//记录结束时间
            }catch (Exception e){
                System.out.println("log fail ");
            }
        }
    }
    

      

    在aop1.xml中配置aop:round通知

    <?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.xsd">
    
        <bean id="categoryService" class="service.CategoryService1"></bean>
        <bean id="logHanlder" class="pointcut.LogTimeHandler"></bean>
        <aop:config>
            <aop:aspect id="log" ref="logHanlder">
                <aop:pointcut id="addlog" expression="execution(* service.*.*(..))"></aop:pointcut>
                <aop:around method="log" pointcut-ref="addlog"></aop:around>
            </aop:aspect>
        </aop:config>
    </beans>
    

      

    单元测试:

    public class AopTest1 {
        @Test
        public void test(){
            ApplicationContext context=new ClassPathXmlApplicationContext("aop1.xml");
            CategoryService1 service1=context.getBean(CategoryService1.class);
            service1.add(1);
        }
    }

    运行结果:

    1.before log 1489990832246
    CategoryService1.add()
    2.after log 1489990832263
    

      

    五、注解方式创建AOP

    定义切面需要给类添加@Aspect注解。然后需要给方法添加注解来声明通知方法,各通知类型对应的注解:

    • @After 通知方法会在目标方法返回或抛出异常后
    • @AfterReturning 通知方法会在目标方法返回后调用
    • @AfterThrowing 通知方法会在目标方法抛出异常后调用
    • @Around 通知方法会将目标方法封装起来
    • @Before 通知方法会在目标方法调用之前执行
    @Component
    @Aspect
    public class LogHelper3 {
    
        @Before("execution(* service.*.*(..))")
        public void logStart(){
            System.out.println("log start "+new Date().getTime());
        }
    }

    然后定义JavaConfig类,注意需要给类添加@EnableAspectJAutoProxy注解启用自动代理功能。

    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan(basePackageClasses = {service.CategoryService3.class,pointcut.LogHelper3.class})
    public class BeanConfig {
    }
    

      单元测试:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = BeanConfig.class)
    public class AopTest3 {
    
        @Autowired
        CategoryService3 service;
    
        @Test
        public void testConfigAop(){
            service.add(100);
        }
    }

    运行结果:

    log start 1489990977264
    add category id=100
    

      

    结尾:

    参考:《spring实战》

    源码下载:https://github.com/cathychen00/learnjava/tree/master/DemoAOP

  • 相关阅读:
    MCPD 70536题目 自定义打印参数
    《ERP从内部集成起步》读书笔记——第5章 MRP系统的时间概念 5.1 时间三要素 5.1.2 时段
    Jquey拖拽控件Draggable用法
    MCPD 70536题目 反射
    MCPD 70536题目 非托管资源 释放
    VS2008创建Silverlight项目时出错解决方法
    程序猿去旅行
    EntityFramework5.0 数据迁移笔记解决模型变化重建数据库的问题
    完美生活
    一直很安静
  • 原文地址:https://www.cnblogs.com/janes/p/6873732.html
Copyright © 2020-2023  润新知