Spring AOP

##AOP概念

aspect oriented programming

面向切面编程

是什么?

将共同的业务处理从传统业务处理中抽离出来,单独封装,

然后以配置的形式进行关联

 

为什么要使用AOP?

可以在不修改原有逻辑代码的情况下,给系统追加功能.

 

##AOP的典型应用

-追加事务控制

-异常日志记录 

 

案例:

-要求:在每个Controller方法执行前输出打桩信息

-封装一个组件

-通过配置将封装的组件追加到Controller方法上

 

#AOP相关概念

-OOP:类,对象,封装,继承,多态

-AOP:切面,切入点,通知,动态代理

##切面(aspect)

指的是封装了共同处理的组件,并且能够切入到其它组件的方法上.

##切入点(pointcut)

用于指定目标组件的方法.

-方法限定表达式

  可以给某个组件中部分方法追加共同功能

  execution(修饰符? 返回类型 方法名(参数) 异常抛出?)

  //匹配到add开头的所有方法

  execution(* add*(..))

  //匹配UserService下的所有方法

  execution(* cn.tedu.UserService.*(..))

  //匹配到service包下所有类的所有方法

  execution(* cn.tedu.service.*.*(..))

  //匹配到service包及子包下的所有方法

  execution(* cn.tedu.service..*(..))

-类型限定表达式

  within(类型)

  //匹配UserService组件下所有的方法

  within(cn.tedu.service.UserService)

  //匹配service包下所有类的所有方法

  within(cn.tedu.service.*)

  //匹配service包及子包下的所有方法

  within(cn.tedu.service..*)

-bean名称限定表达式

  可以给某个组件中所有的方法追加功能

  bean(id名)

  //匹配id为userService的组件的所有方法

  bean(userService)

  //匹配以Service结尾的所有组件的所有方法

  bean(*Service)

##通知

用于指定切入的时机

spring提供了五种通知类型

try{

    前置通知<aop:before>

    //执行的目标方法

    后置通知<aop:after-returning>

}catch{

    异常通知<aop:after-throwing>

}finally{

    最终通知<aop:after>

}

@around=前置+后置通知

可以用在给Service设置性能审计audit

 

 

切面:追加啥功能?单独封装的代码

切入点:切谁?所有的Controller

通知:啥时候切?前置/后置/环绕

##动态代理

AOP原理:使用动态代理技术

可以创建 一个新的类型,重写目标接口或目标类的方法

在重写方法中,追加了要切入的功能代码和方法代码

 

 

spring有两种动态技术:

-基于目标接口的

public class $Proxy25 implements 目标接口{

  public void checkLogin(){

    //追加了事务处理

    //重写了目标接口的方法

  }

}

-基于目标类的

public class $Proxy25 extends 目标类{

  

}

 

##maven引入 aspectjweaver.jar

#使用配置xml文件方式

LoggerBean类

package cn.tedu.cloud_note.aspect;

public class LoggerBean {
    public void logController() {
        System.out.println("AOP功能注入!");
    }
}

spring-aop.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd     
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="loggerBean" class="cn.tedu.cloud_note.aspect.LoggerBean"></bean>
    <aop:config>
        <!-- 通过REF关联组件类 -->
        <aop:aspect ref="loggerBean">
            <!-- 通过method指定处理方法 
            <aop:before method="logController" pointcut="within(cn.tedu.cloud_note.controller..*)"/>
            -->
            <!-- 方法限定类型 
            <aop:before method="logController" pointcut="execution(* cn.tedu.cloud_note.service.*.*(..))"/>
            -->
            <!-- bean名称限定类型 -->
            <aop:before method="logController" pointcut="bean(userLoginController)"/>
        </aop:aspect>
    </aop:config>

</beans>

 

#AOP注解配置

##注解标记


@Component 起到定义<bean>的作用

@Aspect <aop:aspect ref="loggerBean">

@Before <aop:before pointcut= within()>

spring-aop.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd     
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置AOP的注解扫描 -->
    <context:component-scan base-package="cn.tedu.cloud_note.aspect"/>
    <!-- 开启注解标记 -->
    <aop:aspectj-autoproxy/>
</beans>

LoggerBean.java

package cn.tedu.cloud_note.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggerBean {
    @Before("within(cn.tedu.cloud_note.controller..*)")
    public void logController() {
        System.out.println("AOP功能注入!");
    }
}

#性能审计的小案例

AuditBean.java

package cn.tedu.cloud_note.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AuditBean {
    @Around("within(cn.tedu.cloud_note.service..*)")
    public Object audit(ProceedingJoinPoint point) {
        Object obj = null;
        
        try {
            long timeStart = System.currentTimeMillis();
            //模拟调用service
            obj = point.proceed();
            long timeEnd = System.currentTimeMillis();
            //调用的Service名称
            String str = point.getSignature().toString();
            
            System.out.println("耗时:"+(timeEnd-timeStart));
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return point;
    }
}

#切入记录异常的功能

案例:生成异常日志

-要求:当系统发生service异常,将异常信息写入日志文件

-切面:将异常信息写入文件

-切入点:after-throwing("within(service..*)")

ExceptionBean.java

package cn.tedu.cloud_note.aspect;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class ExceptionBean {
    //e是目标组件抛出的异常对象
    @AfterThrowing(throwing="e",pointcut="within(cn.tedu.cloud_note.service..*)")
    public void execute(Exception e) {
        //将异常信息输入文件
        try {
            //true:以追加的方式写出信息
            FileWriter fw  = new FileWriter("F:\\note_error.log",true);
            PrintWriter pw = new PrintWriter(fw);
            //利用pw对象写入异常信息
            Date time = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String timeStr = sdf.format(time);
            pw.println("**************************************");
            pw.println("*异常类型:"+e);
            pw.println("*异常时间:"+timeStr);
            pw.println("***********异常详细信息**************");
            e.printStackTrace(pw);
            pw.close();
            fw.close();
        } catch (Exception ex) {
            System.out.println("记录异常失败");
        }
    }
}

 

posted @ 2020-06-07 22:00  two_q  阅读(212)  评论(0)    收藏  举报