Spring学习总结(三)——对AOP的理解和配置使用(基于XML和基于注解)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40348465/article/details/84966718

1.AOP的基本概念

      AOP Aspect-Oriented Programming 面向切面编程,它的另一种翻译是面向方面编程(即将一方面一方面的业务逻辑增强到原有业务逻辑的方式)。它是一种编程思想能使软件简单,容易维护,更加容易被复用的一种编程方式;AOP只是OOP编程方式的一种补充,并非什么新的技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

     AOP编程方式解决问题的关键在于对关注点的抽象。对系统中的一些分散在多个不相关业务模块中的共同问题(关注点),将这些关注点从各个业务模块中抽象出来是AOP编程的精髓。如日志记录功能,事务管理功能,方法的性能分析等功能从系统业务模块的分离。

2.OOP和AOP开发流程的简单对比  :

OOP 编程基本流程

  (1)归纳分析系统,抽象领域模型;

  (2)使用class 来封装领域模型,实现类的功能;

  (3)把各个类相互协作,组装形成一个完整的功能。

 AOP 编程基本流程

 (1)归纳分析系统中的关注点;

 (2)分离方面对象,使用OOP 将这些方面封装成一个公用的类;

 (3)织入到业务功能,按配置织入规则将切面对象(织入或集成)到系统功能中,形成一个增强后的功能。

3.对AOP的原理的简单了解

       在《精通Spring4.x 企业应用开发实战》中提到过:Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器,它在运行期通过代理方式向目标类植入增强代码。在Spring中可以无缝地将Spring AOP、IoC和AspectJ整合在一起。

      在《Spring 实战 (第4版)》中也提到过:Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截

      Spring AOP在项目中主要完成什么?主要完成的是:把被我们横切出来的逻辑代码融合到业务逻辑中,使用动态代理技术来完成和之前(没抽取前)一样的功能(但是经过这样一处理便可以降低耦合度,提高程序的可重用性,同时提高了开发的效率)。其实在没学Spring AOP之前我们也可以使用代理来实现,代理可以帮我们增强对象的行为,使用动态代理实质上就是调用时拦截对象的方法,然后再对方法进行改造和增强。其实Spring AOP的底层原理就是动态代理。

     在JAVA中有两种动态代理的方式:

      (1)JDK动态代理;

      (2)CGLib动态代理。

              

     JDK动态代理和CGLib动态代理的简单对比:

       (1)代理的对象不一样

         JDK动态代理只能对实现了接口的类生成代理,而不能针对类;

    而CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

       (2)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。

       (3)CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

       (4)JDK在创建代理对象时的性能要高于CGLib代理,而生成代理对象的运行性能却比CGLib的低,所以如果是单例的代理,最好采用CGLib代理。

   Spring AOP默认是使用JDK动态代理,如果代理类没有接口则会使用CGLib代理。

   

 4.AOP中的一些关键的专业术语   

(1)关注点:就是系统中要解决的问题。比如在电子商务系统中,安全验证、日志记录等都属于关注点(Core Concerns);

(2)切面(Aspect):用来组织多个Advice,负责管理切入点和增强的关系;

(3)增强(Advice):定义何时在切入点执行“织入关注点”;如,在执行方法前执行before,在执行方法后执行after等等。

(4)连接点(Joinpoint):明确的某一个被织入代码的点,如方法调用,异常抛出,方法返回等等时间点;

(5)切入点(cutpoint):可以织入增强方法的连接点。当连接点满足被增强的要求时,会在该连接点添加增强,此时的连接点就成为切入点。

(6)织入(weaving) :织入是指把解决横切问题的切面模板,与系统中的其它核心模块通过一定策略或规则组合到一起的过程;具体的技术方案有:a.动态代理;b.类加载器织入c.编译器织入:AspectJ主要就是是使用这种织入方式

  5.AOP的具体流程示意图

   6.配置使用Spring AOP

在Spring的AOP开发中,程序员需要做以下三件事情:

  (1)定义普通的业务类;

  (2)定义切入点,对多个关注点进行横切;

  (3)定义增强,在普通的业务功能中织入关注点的处理。

Spring依然提供两种对AOP的配置:

   1)XML的方式

   2)使用注解进行标注的方式

 6.1基于XML的配置使用

 项目目录

     需要导入相关的包:

     

    业务类OperateLogger.java,此处用于记录操作 

package com.springstudy.service;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Service;

@Service
public class OperateLogger  {
	 //操作前
     public void beforeOperate() {
    	 System.out.println("日志:操作开始前");
    	 System.out.println("----------------");
     }
     
     //操作后
     public void afterOperate() {
    	 System.out.println("日志:操作结束后");
    	 System.out.println("----------------");
     }
     
     /**
      * 执行返回通知的函数
      * @param jp 连接点,包含目标方法的信息
      * @param ret 返回值对象
      */
     public boolean afterRetrunOperate(JoinPoint jp,Object ret) {
    	System.out.println("目标方法的参数是:"+Arrays.toString(jp.getArgs()));
 		System.out.println("目标方法的返回值是:"+ret+"");
 		System.out.println("----------------");
		return true;	 
     }
}

   EmployeeService.java

@Service
public class EmployeeService {
	public int addEmployee(Employee emp) {
		System.out.println("添加员工成功");
		System.out.println("----------------");
		return 0;
	}
    ....
}

  Spring配置文件beans.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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-4.0.xsd">
		
    <!-- Spring容器自动装配bean,配置相关搜索路径 -->
    <context:component-scan base-package="com.springstudy" />
    
    <aop:config>
        <!-- 定义一个切点 -->
		<aop:pointcut expression="execution(* com.springstudy..*Service.*(..))" id="pointcut1"/>
		<!-- 配置切面 -->
		<aop:aspect ref="operateLogger">
		     <!-- 前置通知 -->
		     <aop:before method="beforeOperate" pointcut-ref="pointcut1"/>
		     
		     <!-- 后置通知  -->
		     <aop:after method="afterOperate" pointcut-ref="pointcut1"/> 
		     
		     <!-- 返回通知  -->
		     <aop:after-returning method="afterRetrunOperate" pointcut-ref="pointcut1" returning="ret"/> 
				
			
			<!-- <aop:after-returning method=""/>
			<aop:after-throwing method=""/>
			<aop:around method=""/>   -->
			
	   <!--  < aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的通知时,通知必须实现Advice接口。 -->
       <!-- < aop:aspect>大多用于日志,缓存 ,而< aop:advisor>大多用于事务管理。 -->
       <!-- <aop:advisor advice-ref=""/> -->
		</aop:aspect>
	</aop:config> 
</beans>

   补充:< aop:aspect>和< aop:advisor>的简单对比:

          (1)< aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的通知时,通知必须实现Advice接口。

         (2)< aop:aspect>大多用于日志,缓存 ,而< aop:advisor>大多用于事务管理。

测试结果:

  

 6.2基于注解的配置使用

     大体步骤:

          (1)需要导入引用 aspectJ 的 jar 包: aspectjweaver.jar aspectjrt.jar;

          (2)使用注解@Aspect来定义一个切面,在切面中定义切入点(@Pointcut),通知类型(前置通知 @Before,    后置通知 @AfterReturning,     最终通知 @After,     异常通知 @AfterThrowing,     环绕通知 @Around).。

        (3)切面配置到Spring的beans.xml中,可以使用自动扫描Bean的方式,并设置其自动执行: <aop:aspectj-autoproxy/>。

     示例:

     

EmployeeService.java

@Service
public class EmployeeService {
	public int addEmployee(Employee emp) {
		System.out.println("添加员工成功");
		System.out.println("----------------");
		return 0;
	}
	public int delEmployee(Employee emp) {
		System.out.println("删除员工成功");
		System.out.println("----------------");
		return 0;
	}
    //...
}

OperateLogger.java

package com.springstudy.service;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;

@Service
@Aspect
public class OperateLogger  {
	
	@Pointcut("execution(* com.springstudy..*Service.add*(..))")
	public void pointAdd() {}
	
	
	@Pointcut("execution(* com.springstudy..*Service.del*(..))")
	public void pointDel() {}
	
	@Before("pointAdd() or pointDel()")
	public void beforeOperate() {
		System.out.println("日志:操作开始前");
		System.out.println("----------------");
	}
	
	@After("pointAdd() or pointDel()")
	public void afterOperate() {
		System.out.println("日志:操作结束后");
		System.out.println("----------------");
	}
	
     /**
      * 执行返回通知的函数
      * @param jp 连接点,包含目标方法的信息
      * @param ret 返回值对象
      */
	 @AfterReturning(pointcut="pointAdd() or pointDel()",returning="ret")
     public boolean afterRetrunOperate(JoinPoint jp,Object ret) {
    	System.out.println("目标方法的参数是:"+Arrays.toString(jp.getArgs()));
 		System.out.println("目标方法的返回值是:"+ret+"");
 		System.out.println("----------------");
		return true;	 
     }
}

   beans.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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-4.0.xsd">
		
    <!-- Spring容器自动装配bean,配置相关搜索路径 -->
    <context:component-scan base-package="com.springstudy" />
    
      <!-- 自动执行AOP  -->
     <aop:aspectj-autoproxy/>
</beans>

  测试结果:(简单测试添加一个员工和删除一个员工)

  

  测试代码:

  

package com.springstudy.test;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.springstudy.pojo.Employee;
import com.springstudy.service.EmployeeService;

public class SpringAOPTest {
	private static ApplicationContext context;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {

		context = new ClassPathXmlApplicationContext("beans.xml");

	}

	@Test
	public void test() {
		EmployeeService empService = context.getBean(EmployeeService.class);
		Employee emp = new Employee();
		emp.setEmpId(1001);
		emp.setEmpName("张三");
		empService.addEmployee(emp);
		empService.delEmployee(emp);
	}

}

猜你喜欢

转载自blog.csdn.net/qq_40348465/article/details/84966718