Spring(4)

Spring AOP(面向切面编程) 是什么?

        面向切面编程(AOP) 和面向对象编程(OOP)类似,也是一种编程模式/编程思想。

        有效减少了系统见的重复代码,达到了模块间的松耦合目的。

        AOP的全程是"Aspect Oriented Programming",即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员再编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

        程序需要---1.业务需求----需要实现的程序的具体核心功能---添加用户

                          2.系统需求---实现的程序的辅助功能---------记录系统运行日志

        AOP采取横向抽取机制,取代了传统的纵向继承体系的重复性代码,其应用主要体现在事务处理,日志管理,权限控制,异常处理等方面

        目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。

        Spring AOP 使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式【CGLlB动态代理】向目标类植入增强的代码。

        AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。

        为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。

Joinpoint(连接点)指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。
Pointcut(切入点)指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
Advice(通知)指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。
Target(目标)指代理的目标对象。
Weaving(植入)指把增强代码应用到目标上,生成代理对象的过程。
Proxy(代理)指生成的代理对象。

Aspect(切面)

切入点和通知的结合。 

Advice(通知)指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。

org.springframework.aop.MethodBeforeAdvice(前置通知)    
在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。

org.springframework.aop.AfterReturningAdvice(后置通知)
在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。

org.aopalliance.intercept.MethodInterceptor(环绕通知)
在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。

org.springframework.aop.ThrowsAdvice(异常通知)
在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。

Spring使用AspectJ开发AOP:基于XML和基于Annotation

基于XML【Spring核心配置文件】

package com.spring.demo1.impl;

import com.spring.demo1.StudentService;

public class StudentServiceImpl implements StudentService {
    //Joinpoint(连接点)
    @Override
    public void insertStudent() {
        System.out.println("实现添加学生信息的业务方法--insert");
    }
    //Joinpoint(连接点)
    @Override
    public void updateStudent() {
        int a=10/0;
        System.out.println("实现修改学生信息的业务方法--update");
    }
    //Joinpoint(连接点)
    @Override
    public void deleteStudent() {
        System.out.println("实现删除学生信息的业务方法--delete");
    }
    //Joinpoint(连接点)
    @Override
    public void selectStudent() {
        System.out.println("实现查询学生信息的业务方法--select");
    }
}
package com.spring.demo1.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//系统需求功能实现类
public class MyAspect {
//系统需求功能实现方法
    public void saveLog(){
        System.out.println("记录用户的操作日志");
    }
    //实现环绕通知的具体方法
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws  Throwable{
        saveLog();//开始
        Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法
        saveLog();//结束
        return obj;
    }
    //异常通知实现方法
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("异常通知"+"出错了"+e.getMessage());
    }
}
<?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="studentservice" class="com.spring.demo1.impl.StudentServiceImpl"></bean>
    <!-- 创建系统需求功能实现类 -->
    <bean id="myaspect" class="com.spring.demo1.aspect.MyAspect"></bean>
    <!-- aop配置 -->
    <!-- proxy-target-class="true" 设置aop使用CGLIB代理模式 -->
    <aop:config proxy-target-class="true">
        <!-- 引入系统需求功能实现类 -->
        <aop:aspect ref="myaspect">
            <!-- 配置切入点 -->
            <!--id:切入点名称-->
            <!-- expression:切入点表达式 -->
            <aop:pointcut id="point1" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.insertStudent(..))"/>
            <!-- 配置前置通知 -->
            <aop:before method="saveLog" pointcut-ref="point1"></aop:before>
        </aop:aspect>

        <aop:aspect ref="myaspect">
            <!-- 配置切入点 -->
            <aop:pointcut id="point2" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.deleteStudent(..))"/>
            <aop:after method="saveLog" pointcut-ref="point2"></aop:after>
        </aop:aspect>
        <aop:aspect ref="myaspect">
            <!-- 配置切入点 -->
            <aop:pointcut id="point3" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.selectStudent())"/>
            <aop:around method="myAround" pointcut-ref="point3"></aop:around>
        </aop:aspect>
        <aop:aspect ref="myaspect">
            <!-- 配置切入点 -->
            <aop:pointcut id="point4" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.updateStudent(..))"/>
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="point4" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

基于Annotation

package com.spring.demo1;

import org.springframework.stereotype.Component;

@Component("student")
public class StudentServiceImpl {
    public void insertStudent(){
        System.out.println("添加学生信息的业务方法---insertStudent");
    }
    public void uodateStudent(){
        System.out.println("修改学生信息的业务方法---updateStudent");
    }
    public void deleteStudent(){
        System.out.println("删除学生信息的业务方法---deleteStudent");
    }
    public void selectStudent(){
        System.out.println("查询学生信息的业务方法---selectStudent");
        int a=10/0;
    }

}
package com.spring.demo1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component("myAspect")
@Aspect
public class MyAspect {
    @Pointcut("execution(* com.spring.demo1.StudentServiceImpl.insertStudent())")
    public void point1(){}

    @Pointcut("execution(* com.spring.demo1.StudentServiceImpl.uodateStudent())")
    public void point2(){}

    @Pointcut("execution(* com.spring.demo1.StudentServiceImpl.deleteStudent())")
    public void point3(){}

    @Pointcut("execution(* com.spring.demo1.StudentServiceImpl.selectStudent())")
    public void point4(){}

    public void saveLog(){
        System.out.println("记录系统运行日志");
    }

    //测试前置通知
    @Before("point1()")
    public void myBefore(){
        saveLog();
    }

    //测试后置通知
    @After("point2()")
    public void myafter(){
    saveLog();
    }

    //测试环绕通知
    @Around("point3()")
    public Object myAround(ProceedingJoinPoint  proceedingJoinPoin) throws Throwable{
        saveLog();
        Object obj=proceedingJoinPoin.proceed();
        saveLog();
        return obj;
    }

    //测试异常通知
    @AfterThrowing(value = "point4()",throwing = "e")
    public void myAfterThrowing(Throwable e){
        System.out.println("异常通知"+"出错了"+e.getMessage());
    }

}
<?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: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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫面包含com.spring.demo1包下的所有注解 -->
    <context:component-scan base-package="com.spring.demo1"></context:component-scan>
    <!-- 使切面开始自动代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
package com.spring.demo1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl studentService=ac.getBean("student",StudentServiceImpl.class);
        //studentService.insertStudent();
        //测试后置通知
        //studentService.uodateStudent();
        //测试环绕通知
        //studentService.deleteStudent();
        //测试异常通知
        studentService.selectStudent();
    }
}

切入点表达式

        execution(modifiers-pattern? 
                            ret-type-pattern 
                            declaring-type-pattern? 
                            name-pattern(param-pattern)     
                            throws-pattern?)

这里的问号标识当前项可以有也可以没有,其中各项的语义如下:

                 modifiers-pattern:方法的可见性,如public ,protected;

                 ret-type-pattern : 方法的返回值类型,如int ,void等;

                 declaring-type-pattern:方法所在类的全路径名,如com.spring.demo1;

                 name-pattern:方法名类型,如StudentService();

                 param-pattern:方法的参数类型,如java.lang.String;

                 throws-pattern:方法抛出的异常类型,如java.lang.Exception;

如下是一个使用execution表达式的例子:

       execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))

        上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.service.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。

        *通配符,该通配符主要用于匹配单个单词,或者是以某个此为前缀[insert*]或后缀[*Student]的单词

        execution(* com.spring.service.BusinessObject.*())

        表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法

        execution(* com.spring.service.Business*.*())

        表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零的方法

        ..(两个点)通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中。

        如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。

       execution(* com.spring.service..*.businessService())

        表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数

        execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))

        表示匹配返回值为任意类型,并且是com.spring.service.BusinessObject类中的,名称为businessService的方法,而且该方法有第一个参数,并且第一个参数是java.lang.String类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值