Spring之AOP

1.Spring AOP 概述

1.1.何为AOP?
AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(
在对象运行时动态织入一些功能。)

1.2.AOP要解决什么问题?
实际项目中通常会将系统两大部分:核心关注点和非核心关注点
思考?
编程过程中首先要完成的是什么?核心关注点(核心业务)
非核心关注点如何切入到系统中?硬编码(违背OCP),AOP(推荐)

AOP就是要在基于OCP在不改变原有系统核心业务代码的基础上动态添加一些扩展功能。

1.3.AOP实际项目应用场景?

AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。

1.4.AOP底层原理实现分析?

AOP底层基于代理机制实现功能扩展:(了解)
1)假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
2)假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。

2.Spring AOP 编程实现

2.1.AOP 基本步骤

step1:创建maven java 项目
step2:添加aop依赖
step3:配置aop 核心(基于xml,基于注解)
step4:定义核心业务(核心关注点):推荐先写接口再写实现类
step5:定义扩展业务(非核心关注点)
step6:基于配置实现非核心业务的切入
step7:编写测试类进行单元测试

2.2.AOP 基于xml实现

通过AOP为核心业务类添加日志处理
2.2.1.创建项目添加依赖
创建maven java 项目然后添加依赖

<dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.9.RELEASE</version>
       </dependency>
        <!--
        Spring AOP的实现依托于Aspect框架
        所以要引用1.8.5有问题
        -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

2.2.2.添加spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 

</beans>

2.2.3.创建核心业务类
创建接口

public interface HelloService {
     void sayHello(String msg);
}

创建接口实现类
public class HelloServiceImpl implements HelloService {
    public void sayHello(String msg) {
        //假设这条语句是我们系统中的核心业务
        System.out.println(msg);
    }
}

2.2.4.创建日志处理类

public class LoggingAspect {
     public void beforeMethod(){
         System.out.println("method start");
     }
     public void afterMethod(){
         System.out.println("method end");
     }
}

2.2.5.配置bean对象

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 

    <!-- 核心业务对象 -->
    <bean id="helloService"
          class="spring.beans.HelloServiceImpl"/>
    <!-- 配置非核心业务对象(日志处理对象):切面 -->
    <bean id="log" 
          class="spring.aop.LoggingAspect"/>
    <!-- AOP配置(切入点,切面) -->  
    <aop:config>
       <!-- 配置切入点 -->
       <aop:pointcut 
            expression="within(spring.beans.HelloServiceImpl)" //核心业务
            id="logPointCut"/>
       <!-- 配置日志处理 -->
       <aop:aspect ref="log" >
           <aop:before method="beforeMethod" 
                       pointcut-ref="logPointCut"/>
           <aop:after  method="afterMethod"
                       pointcut-ref="logPointCut"/>
       </aop:aspect>
    </aop:config>
</beans>

2.2.6.编写测试类

//1.初始化spring容器

ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        //2.获取Bean对象
        HelloService helloService=
        ctx.getBean("helloService",HelloService.class);
        //3.执行业务
        helloService.sayHello("CGB1710");//proxy的方法
        //4.释放资源
        ctx.close();

2.3.Spring AOP 基于注解方式实现

2.3.1.Spring AOP 项目创建
1.创建maven java 项目
2.添加AOP依赖
3.编写核心业务组件(MessageService接口与MessageServiceImpl实现类)
4.编写扩展业务组件(TxManager)
5.配置核心业务组件以及扩展业务组件(组件扫描及AOP注解应用)
6.编写单元测试(junit)
2.3.2.Spring AOP 核心业务组件定义
定义MessageService接口

public interface MessageService {
     void sendMsg(String msg);
}

定义MessageServiceImpl实现类实现SayHelloService接口

@Service
public class MessageServiceImpl implements MessageService{
    public void sendMsg(String msg) {
        System.out.println(msg);
    }
}

2.3.3.Spring AOP 切面业务组件定义

@Component
@Aspect
public class TxManager {
    @Pointcut("within(com.company.spring.service.MessageServiceImpl)")
    public void pointCut() {}

    @Before("pointCut()")
    public void beginTx() {
        System.out.println("begin transaction");
    }
    @After("pointCut()")
    public void endTx() {
        System.out.println("end transaction");
    }
}

其中:
@Component 表示此组件由Spring对象管理
@Aspect 用于定义切面(对切入点和通知的封装)
@Pointcut 用于定义切入点(用于织入扩展功能的点)
@Before 用于定义前置通知(业务方法之前执行)
@After 用于定义最终通知(业务方法执行完成以后执行)

2.3.4.Spring AOP 配置文件启用AOP功能

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!-- 自动扫描该包 -->
    <context:component-scan base-package="cn.tedu.aop" />
    <!-- 使aspectj注解生效,自动为目标对象生成代理对象 -->
    <aop:aspectj-autoproxy/>

</beans>


说明:基于AOP注解的方式配置AOP时,因为注解要应用在类或方法的上面,所以会由一定的侵入性。但这种侵入性属于声明式侵入性,所以范畴同样也是属于弱耦合范围之内(强耦合一般是在业务代码内部的直接耦合)。
2.3.5.Spring AOP 测试
ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext("aop.xml");
        MessageService msgService=
        ctx.getBean("messageServiceImpl",MessageService.class);
        msgService.sendMsg("hello");
        ctx.close();

3.Spring AOP 编程增强
3.1.Spring AOP 切入点表达式增强

Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:

指示符 作用

1. bean 用于匹配指定类型内的方法执行
2. within 用于匹配指定类型内的方法执行
3. execution 用于匹配方法执行的连接点

3.1.1.bean 表达式
bean应用于类级别,实现粗粒度的控制:
bean(UserServiceImpl)) 指定一个类
bean(*Service) 指定所有的后缀为service的类

例如:
@Aspect
@Component
public class LoggingAspect {
    @Before("bean(*Service)")
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }
    @After("bean(*Service)")
    public void afterMethod() {
        System.out.println("afterMethod");
    }
}


3.1.2.within 表达式
within应用于类级别,实现粗粒度的控制:
within(aop.service.UserServiceImpl))    指定类,只能指定一个类
within(aop.service.*))  只包括当前目录下的类
within(aop.service..*)) 指定当前目录包含所有子目录中的类


@Aspect
@Component
public class LoggingAspect {
    @Before("within(aop.Service.*)")
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }
    @After("within(aop.Service.*)")
    public void afterMethod() {
        System.out.println("afterMethod");
    }
}

3.1.3.execution 表达式
execution方法级别,细粒度的控制:
语法:execution(返回值类型 包名.类名.方法名(参数列表))
execution(void aop.service.UserServiceImpl.addUser()) 匹配方法
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为字符串
execution(* aop.service...(..)) 万能配置

例如:

@Aspect
@Component
public class TimingAspect {
    @Before("execution(* cn.tedu..*Service.*(..))")
    public void timeBeforeMethod(JoinPoint point) {
        String method=point.getSignature().getName();
        System.out.println("timeBeforeMethod:"+method);
    }
    /**方法切入点 (execution: 执行)*/
    @After("execution(* cn.tedu..*Service.update(..))")
    public void timeAfterMethod(JoinPoint point) {
        String method=point.getSignature().getName();
        Object arg=point.getArgs()[0];
        System.out.println("timeAfterMethod:"+method+"("+arg+")");
    }
}

3.2.Spring AOP 配置增强

3.2.1.Spring AOP xml 方式配置增强
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

<aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:before method="beforeMethod" pointcut-ref="pc"/>
            <aop:after method="afterMethod" pointcut-ref="pc"/>
            <aop:after-returning method="returnMethod"
 pointcut-ref="pc"/>
            <aop:after-throwing method="throwMethod" 
pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
切入点及环绕通知的配置
    <aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

3.2.2.Spring AOP 注解方式配置增强
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

@Aspect
@Component
public class LoggingAspect {
    @Before("bean(xxxService)")
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }
    @After("bean(xxxService)")
    public void afterMethod() {
        System.out.println("afterMethod");
    }
    @AfterReturning(pointcut="bean(xxxService)",returning="result")
    public void afterReturningMethod(Object result) {
        System.out.println("afterReturnMethod.result="+result);
    }
    @AfterThrowing("bean(xxxService)")
    public void afterThrowingMethod() {
        System.out.println("afterThrowingMethod");
    }
}


切入点及环绕通知的配置

@Component
@Aspect
public class TxManager {
    @Pointcut("execution(com.company.spring.service..*.*(..))")
    public void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) 
            throws Throwable{
            System.out.println("事务开始");
            Object result = joinPoint.proceed();
            System.out.println("事务结束");
            return result;
    }
}

3.3.Spring AOP 切面顺序配置
当业务系统中存在多个切面时,哪个切面优先执行,哪个切面要延后执行,我们可以通过配置切面 执行顺序实现,序号小的会优先执行。

3.3.1.基于xml方式配置

  <!-- 自动扫描该包 -->
    <context:component-scan base-package="com.company.spring" />
    <!-- AOP配置 -->
    <aop:config>
        <aop:pointcut id="pc"
             expression="execution(* 
com.company.spring.service..*.*(..))"/>
        <aop:aspect ref="loggingAspect" order="1">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
        <aop:aspect ref="txManager" order="2">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

3.3.2.基于注解方式配置
事务切面

/**权限检测的切面
 * 通过Order注解定义Order切面的执行顺序
 * */
@Order(1)
@Aspect
@Service
public class PermissionAspect {
    @Before("execution(* com..*.*(..))")
    public void check(){
        System.out.println("权限检测");
    }
}

日志切面
**将其理解为一个切面
* 切面中通常会有两部分内容
* 1)切入点(PointCut)
* 2)增强功能(Advice)
* */

@Order(2)
@Aspect
@Service
public class TimeAspect {
    /**定义切入点*/
    //within表达式为全类名或者"包.*"
    //@Pointcut("within(com.spring.beans.*)")
    //@Pointcut("within(com.spring.beans.OrderServiceImpl)")
    //bean表达式中的内容为具体bean对象的id
    //@Pointcut("bean(orderService)")
    //execution表达式
    //第一个"*"代表任意的返回值类型
    //第二个"*"代表是任意的类
    //第三个"*"代表任意的方法
    //第一个".."代表任意的子包(多个包)
    //第二个".."代表任意参数个数,任意参数类型
    @Pointcut("execution(* com..*.*(..))")
    public void pointcut(){}
    /**增强功能:前置通知(业务方法执行之前执行)*/
    @Before("pointcut()")
    public void begin(){
      System.out.println("start:"+System.nanoTime());
    }
    /**增强功能:最终通知(业务方法执行最后执行,
     *无论业务方法是否执行成功,此功能都要执行)*/
    @After("pointcut()")
    public void end(){
      System.out.println("end:"+System.nanoTime());
    事务切面
Order(3)
@Aspect
@Service
public class TxAspect {
    @Before("bean(orderService)")
    public void startTx(){
        System.out.println("开启事务");
    }
    @AfterReturning("bean(orderService)")
    public void commitTx(){
        System.out.println("提交事务");
    }
    @AfterThrowing("bean(orderService)")
    public void rollbackTx(){
        System.out.println("回滚事务");
    }
    @After("bean(orderService)")
    public void closeResource(){
        System.out.println("释放资源");
    }
}
/**
 * try{
 *   @Before
 *   saveOrder();
 *   @AfterReturning
 *   return;
 * }catch(Exception e){
 *   @AfterThrowing
 *   throw e;
 * }finally{
 *   @after
 * }                        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值