美文网首页
Spring第二天(AOP)

Spring第二天(AOP)

作者: 原来蜗牛不是牛 | 来源:发表于2017-07-19 08:37 被阅读0次

什么是AOP?

AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。AOP和OOP互为补充,面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面。可以这样理解:面向对象编程是从静态角度考虑程序结构,而面向切面编程是从动态角度考虑运行过程

AOP的基本概念

  1. AOP从程序运行角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。
  2. AOP框架并不与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(Pointcut),而不与某个具体类耦合。AOP具有如下两个特征:
  • 各步骤之间的良好隔离性
  • 源代码无关性
  1. 关于面向切面编程的一些术语
  • 切面(Aspect):切面用于组织多个Advice,Advice放在切面中定义
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用
  • 增强处理/通知(Advice):AOP框架在特定的切入点执行的增强处理,处理有“around”、“before”、“after”等类型
  • 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求是=时,该连接点将被添加增强处理,该连接点也就变成了切入点

如何使用表达式来定义切入点是AOP的核心,Spring默认使用AspectJ切入点语法。

  • 引入:将方法或字段添加到被处理的类中。Spring允许将新的接口引入到任何被处理的对象中。例如,你可以使用一个引入,使任何对象实现IsModified接口,以此来简化缓存
  • 目标对象:被AOP框架进行增强处理的对象,也被称为被增强的对象。如果AOP框架采用的是动态AOP实现,那么该对象就是一个被代理的对象
  • AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是cglib代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理
  • 织入(Weaving):将增强处理添加到目标对象中去,并创建一个被增强的对象(AOP代理对象)的过程就是织入。织入的两种实现方式--编译时增强(如AspectJ)和运行时增强(如Spring AOP)。Spring和其他纯Java AOP框架一样,在运行时完成织入。

关系图如下:

AOP关系图

AOP的基本操作

  1. 为什么使用AOP
    业务场景:
    服务层:提供30个模块的服务。同时这些模块都依赖某个或某几个公共服务。(事务管理,日志管理。)
    传统方式:使用继承的方式
    将子类公有需要执行的方法抽取成一个父类,在使用到公共方法的地方去调用父类的方法
    AOP目的:程序员能够安心做业务成的业务
  2. 使用AOP之最最最原始的步骤
  • 编写服务层接口和实现类(目标类)
    业务层接口:
public interface StudentService {
    void addStudent();
    void delStudent();
    void updateStudent();
    String findStudent();
}

业务层实现类:

public class StudentServiceImpl implements StudentService {

    @Override
    public void addStudent() {
        System.out.println("addStudent");

    }

    @Override
    public void delStudent() {
        System.out.println("delStudent");

    }

    @Override
    public void updateStudent() {
        System.out.println("updateStudent");
    }

    @Override
    public String findStudent() {
        // TODO Auto-generated method stub
        return "haha";
    }
}
  • 编写通知类
    前置通知:在调用目标方法前执行
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class LogAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        System.out.println("前置通知,日志记录");
        
    }
}

环绕通知:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前");
        //回调原来的目标方法
        Object proceed = invocation.proceed();//上面的代码在调用目标方法之前执行,下面的代码在调用目标方法后执行
        System.out.println("环绕后");
        return proceed;
    }
}

后置通知:在调用目标方法后执行

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class TestAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知返回值:"+returnValue);
        
    }
}
  • 使用Spring的代理类进行整合,在spring核心配置文件中整合
    手动配置
<!-- 1.创建目标类对象 -->
    <bean id="studentService" class="org.cx.service.StudentServiceImpl" ></bean>
    <!-- 2.创建通知类对象 -->
    <!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
    <bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
    <bean id="aroundAdvice" class="com.cx.advice.AroundAdvice" ></bean>
    <!-- 3.创建Spring的代理类对象 -->
    <bean id="studentServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean" >
        <!-- aop编程整合 -->
        <!-- 1.整合目标类 -->
        <property name="target" ref="studentService"></property>
        <!-- 2.整合通知类 -->
        <property name="interceptorNames">
            <list>
                <!-- <value>logAdvice</value>
                <value>testAdvice</value> -->
                <value>aroundAdvice</value>
            </list>
        </property>
        <!-- 3.整合接口关联 -->
        <property name="proxyInterfaces" value="org.cx.service.StudentService"></property>
    </bean>

自动配置

<!-- 1.创建目标类对象 -->
    <bean id="studentService" class="com.cx.service.StudentServiceImpl" ></bean>
    <!-- 2.创建通知类对象 -->
    <!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
    <bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
    <bean id="aroundAdvice" class="com.cx.advice.AroundAdvice" ></bean>
    <!-- 3.自动方式配置AOP -->
    <aop:config>
        <!-- 1.配置扫描的服务层 
            切入点表达式:扫描到具体的方法
            * com.cx.service.*.*(..)
            * 返回类型 任意类型
            com.cx.service 服务层的包名
            第一个.*包下面的所有类
            第二个.*(..)类里面的所有方法-->
        <aop:pointcut expression="execution(* com.cx.service.*.*(..))" id="mypoint"/>
        <!-- 2.让服务层通知关联 -->
        <aop:advisor advice-ref="aroundAdvice" pointcut-ref="mypoint"/>
    </aop:config>
  • 测试
    使用手动配置的测试
public static void main( String[] args )
    {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentService studentService = ac.getBean("studentServiceProxy",StudentService.class);
        studentService.addStudent();
        //studentService.findStudent();
    }

使用自动配置的测试:

public static void main( String[] args )
    {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentService studentService = ac.getBean("studentService",StudentService.class);
        studentService.addStudent();
        //studentService.findStudent();
    }

使用AspectJ框架进行AOP编程

AspectJ的通知和原始通知不同:不需要单独去实现接口,所有通知方法写在一个类

  • 编写通知
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
    //aspectj包下的
    //前置通知
    public void myBefore(JoinPoint jp){
        System.out.println("前置通知");
    }
    
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕前");
        //手动执行目标方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后");
        return proceed;
    }
    
    public void myAfter(JoinPoint jp,Object res){
        System.out.println("后置通知:"+res);
    }
}
  • 编写业务层接口和实现类
    接口
public interface UserService {
    void addUser();
    
    void delUser();
    
    void updateUser();
    
    String findUser();
}

实现类

public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("addUser");
    }

    @Override
    public void delUser() {
        System.out.println("delUser");

    }

    @Override
    public void updateUser() {
        System.out.println("updateUser");

    }

    @Override
    public String findUser() {
        return "美好的一天从微笑开始";
    }
}
  • 使用AspectJ配置整合AOP
<!-- 1.创建目标类对象 -->
    <bean id="userService" class="com.cx.service.UserServiceImpl" ></bean>
    <!-- 2.创建通知类对象 -->
    <!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
    <bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
    <bean id="myAdvice" class="com.cx.advice.MyAdvice" ></bean>
    <!-- 3.AspectJ的AOP编程 
        a.有一个AspectJ单独的节点
        b.通知也有单独的节点-->
    <aop:config>
        <aop:aspect ref="myAdvice">
            <!-- 扫描方法:定义切入点表达式 -->
            <aop:pointcut expression="execution(* com.cx.service.*.*(..))" id="mypoint"/>
            <!-- 配置前置通知 -->
            <!-- <aop:before method="myBefore" pointcut-ref="mypoint"/> -->
            <!-- 配置环绕通知 -->
            <!-- <aop:around method="myAround" pointcut-ref="mypoint"/> -->
            <!-- 配置后置通知 -->
            <aop:after-returning method="myAfter" pointcut-ref="mypoint" returning="res"/>
        </aop:aspect>
    </aop:config>

AspectJ使用注解的方式配置

  • 编写业务接口和实现类
    接口
public interface CarService {
    void addCar();
    String findCar();
}

实现类

import org.springframework.stereotype.Service;
@Service("carService")
public class CarServiceImpl implements CarService {
    @Override
    public void addCar() {
        System.out.println("addCar");
        
    }

    @Override
    public String findCar() {
        // TODO Auto-generated method stub
        return "风驰天下,大运摩托";
    }
}
  • 编写自定义通知类
@Component("myCarAspect")
@Aspect
public class MyCarAspect {
    //定义切入点表达式
    @Pointcut("execution(* com.cx.service.*.*(..))")
    public void myPoint(){
        
    }
//  @Before("myPoint()")//前置通知
//  public void myCarBefore(JoinPoint jp){
//      System.out.println("前置通知:美好的一天从绿色开始");
//  }
    
    @Around("myPoint()")//环绕通知
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕前");
        //手动执行目标方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后");
        return proceed;
    }
}
  • 整合配置
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.cx"></context:component-scan>
    <!-- 开启AspectJ的注解 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

相关文章

网友评论

      本文标题:Spring第二天(AOP)

      本文链接:https://www.haomeiwen.com/subject/bvehkxtx.html