SpringAOP

只愿长相守 提交于 2020-03-12 22:00:16

1、AOP(Aspect Oriented Programming)面向切面编程
概述:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP是OOP的延续,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率.

作用:在程序运行期间,不修改源码对已有方法进行增强(加上统一的逻辑处理)。
优势:减少重复代码、提高开发效率、维护方便

AOP是一门统一拦截处理的技术,类似于过滤器技术。与过滤器不同的是过滤过滤是请求路径,而AOP拦截的是方法。

AOP技术的底层技术就是动态代理模式的实现,所有先了解代理模式和动态代理模式


2、代理模式
概述:为其他对象(代理对象)提供一种代理以控制对这个对象(源对象)的访问。就是说,声明一个代理对象去控制对源的对象的访问

代理模式的缺陷:
(1)代理类必须要继承或者实现一个基类或者接口,每个接口都要实现一个新的代理类,限制了代理的源对象的类型
(2) 每个方法的逻辑处理,还是要重复编写。(代理模式并不可以减少代码)

 

3、动态代理模式
(1)动态代理解决了以上代理模式两个问题:
   代理模式的类型是固定的,而动态代理模式的代理类,不需要指定固定的接口,可以自由的使用任何接口来实现代理
   代理模式的代码还是重复编写的,动态代理可以将所有方法中重复的代码统一到一个方法中编写

(2)创建动态类的实现:
第一步:实现一个动态代理接口 InvocationHandler
第二步:传入代理源通过,Proxy. newProxyInstance()方法返回动态代理对象
第三步:实现动态代理方法invoke(),该代理方法会自动调用

public class ProxyObject implements InvocationHandler {
  private Object source;  //源对象

  @SuppressWarnings("unchecked")
  public <T> T getInstall (Class<T> sourceClass,Object object) {
  this.source=object;                // 设置源对象的值
  ClassLoader loader = sourceClass.getClassLoader();   // 获得类加载器
  //获得接口,JdK内置的动态代理模式的源对象必须要有接口
  Class<?>[] interfaces = sourceClass.getInterfaces();
  Object instance = Proxy.newProxyInstance(loader, interfaces, this); // 指定创建的对象
  return (T) instance;
}

// 在动态代理对象的方法被调用时,该代理方法会自动调用,在这里就是我们拦截方法统一处理的代码
@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  System.out.println("-获得连接-");
  System.out.println("-获得操作对象-");
  Object object = method.invoke(source, args);
  System.out.println("-关闭连接-");
  return object;
  }
}

(3)动态代理对象缺点:不可以定义拦截的规则,使用Spring AOP技术可以满足通过规则来拦截指定的方法

 

4、AOP相关术语
Joinpoint(连接点):根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点
Pointcut(切入点):所谓的切入点,就是拦截方法设置的规则
Advice(通知/增强):指拦截到Joinpoint之后所要做的事情就是通知
       分为:前置通知、后置通知、异常通知、最终通知、环绕通知
拦截场景:就是可以设置在方法之前拦截或方法执行之后拦截或方法出异常后拦截,或方法之前和之后都拦截
Aspect(切面):所谓的切面就是我们的拦截处理类,是切入点和通知(引介)的结合


5、基于xml配置AOP,编写一个切面类(拦截处理类),在执行Service层的login,register,undo方法,分别在方法执行前、方法之后调用编写统一处理的代码,

第一步:编写业务层类和接口
接口:
public interface StudentService {
  public void login();   //学生登录的方法
  public void register();  //学生注册的方法
  public void undo();   //学生注销的方法
}
编写一个实现类,实现StudentService接口:
public class StudentServiceImpl implements StudentService {
  @Override
  public void login() {
    System.out.println("学生登录");
  }
  @Override
  public void register() {
    System.out.println("学生注册");
  }
  @Override
  public void undo() {
    System.out.println("学生注销");
  }
}

第二步:编写一个切面类,
pupublic class StudentAOP {
  //方法执行前
  public void before() {
    System.out.println("方法执行之前");
  }
  //方法执行后
  public void after() {
    System.out.println("方法执行之后");
  }
}

 

第三步:配置AOP配置
<!-- 配置实现类和代理类 -->

<bean name="studentService" class="cn.ong.service.impl.StudentServiceImpl"></bean>
<bean name="aop" class="cn.ong.aop.StudentAOP"></bean>

<aop:config>
  <!-- 配置拦截规则 -->
  <aop:pointcut expression="execution(public void cn.ong.service.impl.StudentServiceImpl.login())" id="public_pointcut" />
  <!-- 配置切面 -->
  <aop:aspect id="aop" ref="aop">
  <!-- 配置两个通知 -->
    <aop:before method="before" pointcut-ref="public_pointcut" />
    <aop:after method="after" pointcut-ref="public_pointcut" />
  </aop:aspect>
</aop:config>

 

第四部步:编写测试代码
public class ApplicationContextTest {
  public static void main(String[] args) {
  //通过配置文件创建容器
  ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  //获得容器里面的对象
  StudentService studentService = context.getBean("studentService", StudentService.class);
  //调用对象的方法
  studentService.login();
  //关闭容器
  context.close();
  }
}

 

常用标签
<aop:config>用于声明开始aop的配置
<aop:aspect>用于配置切面
   属性:id:给切面提供一个唯一标识
      ref:引用配置好的通知类bean的id
<aop:pointcut>用于配置切入点表达式
   属性:expression:用于定义切入点表达式。
      id:用于给切入点表达式提供一个唯一标识。
<aop:before>用于配置前置通知
   属性:method:指定通知中方法的名称。
      pointct:定义切入点表达式
      pointcut-ref:指定切入点表达式的引用
<aop:after-returning>用于配置后置通知,如果出了异常就一定不会调用切面的方法
   属性:method:指定通知中方法的名称。
      pointct:定义切入点表达式
      pointcut-ref:指定切入点表达式的引用
<aop:after-throwing>用于配置异常通知,只有出了异常才会调用切面对应的方法
   属性:method:指定通知中方法的名称。
      pointct:定义切入点表达式
      pointcut-ref:指定切入点表达式的引用
<aop:after>用于配置最终通知,不管出不出异常,调用的切面的方法
   属性:method:指定通知中方法的名称。
      pointct:定义切入点表达式
      pointcut-ref:指定切入点表达式的引用
<aop:around>用于配置环绕通知
   属性: method:指定通知中方法的名称。
      pointct:定义切入点表达式
      pointcut-ref:指定切入点表达式的引用


常用注解
@Aspect:把当前类声明为切面类。
@Before作用:把当前方法看成是前置通知。
   属性: value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterReturning:把当前方法看成是后置通知。报异常,就不执行
   属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterThrowing:把当前方法看成是异常通知。只有报异常才执行
   属性: value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@After:把当前方法看成是最终通知。不管报不报异常都执行
   属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Around:把当前方法看成是环绕通知。
   属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Pointcut:指定切入点表达式
   属性:value:指定表达式的内容

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!