Spring(1):AOP面向切面编程

用了那么久Spring,该是做个总结的时候了

一、Spring AOP介绍

AOP思想是基于代理模式, 因为Java中一般采用JDK动态代理模式,由于Spring同事支持CGLIB、AspectJ、JDK动态代理,但是JDK动态代理只能 代理接口不能代理类,所以Spring会这样子进行切换:

(1)如果目标对象的实现类实现了接口,则直接采用JDK动态代理生成AOP代理类;

(2)如果目标对象类没有实现接口,SpringAOP则会采用CGLIB来生成AOP代理类

1.1、什么是AOP

AOP,即面向切面编程。允许通过分离应用的业务逻辑与系统级服务(如审计、日志、权限校验、事物管理)进行内聚性的开发——原理就是使用代理模式。。应用对象只实现它们应该做的事情——完成业务逻辑,仅此而已。

1.2、AOP基本概念

(1)通知

通知有五种类型:

  • Before 在方法被调用之前调用

  • After 在方法完成后调用通知,无论方法是否执行成功

  • After-returning 在方法成功执行之后调用通知

  • After-throwing 在方法抛出异常后调用通知

  • Around 通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为

(2)切点(Pointcut)

切点在AOP中对应系统中的方法,这个方法是定义在切面中的方法,一般和通知一起使用,一起组成切面

(3)连接点(Join point) 

就是方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、for循环某个点等。总的来说就是准备在系统中执行切点和切入通知的地方(一般是一个方法、一个字段)

(4)切面(Aspect)

切面是切点和通知的集合,一般单独作为一个类。通知和切点共同定义关于切面的全部内容,它是什么时候在何时何处完成功能。

(5)引入(Introduction)

允许向现有类添加新的方法或者属性

(6)织入(Weaving)

组装切面来创建一个被通知的对象。这可以在编译时完成,也可以在运行时完成 (Spring是在运行时完成)

1.3、Spring AOP基于AspectJ注解如何实现AOP

(1)什么是AspectJ(静态织入)

是一个AOP框架,它能对Java代码进行AOP编译,让Java代码具有AspectJ的AOP功能,Spring只是使用了AspectJ5一样的注解,但是没使用它的编译器,底层依然是动态代理技术的实现;同理Spring AOP虽然使用了那一套注解,其实实现AOP的底层还是动态代理(JDK或者CGLIB)来动态织入

1.4、AOP通知类型

(1)@Before 前置通知(Before advice):在某连接点之前执行的通知

(2)@After 后通知(After advice):当某连接点退出时执行的通知(无论正常返回还是抛出异常)

(3)@AfterReturning 返回后通知(After return advice):在某连接点正常完成后的通知(不包括抛出异常)

(4)@Around 环绕通知(Around advice):包围一个连接点的通知,可以在方法调用前后完成自定义行为

(5)@AfterThrowing 抛出异常后通知(After throwing advice) :在方法抛出异常时退出执行的通知

二、AOP代码部分

2.1、代码理解AOP基本概念

(1)定义主题接口,接口方法为连接点

public interface Subject {	
	//登陆
	public void login();	
	//下载
	public void download();	
}

(2)定义实现类,这是代理模式中真正的被代理人

public class SubjectImpl implements Subject {
	public void login() {		
		System.err.println("借书中...");		
	}

	public void download() {		
		System.err.println("下载中...");		
	}
}

(3)定义切面(切面中有 切点和通知)

public class PermissionVerification {
 /**
  * 权限校验
  * @param args 登陆参数
  */
	public void canLogin() {		
		//做一些登陆校验
		System.err.println("我正在校验啦!!!!");		
	}
	
	/**
	 * 校验之后做一些处理(无论是否成功都做处理)
	 * @param args 权限校验参数
	 */
	public void saveMessage() {		
		//做一些后置处理
			System.err.println("我正在处理啦!!!!");
	}
	
}

(4)SpringAOP.xml文件(老式方法,基于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:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
        
        <bean id="SubjectImpl1" class="wokao666.club.aop.spring02.SubjectImpl" />
        <bean id="SubjectImpl2" class="wokao666.club.aop.spring02.SubjectImpl" />
        <bean id="PermissionVerification" class="wokao666.club.aop.spring01.PermissionVerification" />
        
        <aop:config>
        <!-- 这是定义一个切面,切面是切点和通知的集合-->
            <aop:aspect id="do" ref="PermissionVerification">
            	<!-- 定义切点 ,后面是expression语言,表示包括该接口中定义的所有方法都会被执行-->
                <aop:pointcut id="point" expression="execution(* wokao666.club.aop.spring01.Subject.*(..))" />
                <!-- 定义通知 -->
                <aop:before method="canLogin" pointcut-ref="point" />
                <aop:after method="saveMessage" pointcut-ref="point" />
            </aop:aspect>
        </aop:config>
</beans>

(5)测试类和结果

public class Test {
	
	public static void main(String[] args) {
		ApplicationContext ctx = 
	      new ClassPathXmlApplicationContext("SpringAOP.xml");	  
				Subject subject1 = (Subject)ctx.getBean("SubjectImpl1");
				Subject subject2 = (Subject)ctx.getBean("SubjectImpl2");				
				subject1.login();
				subject1.download();								
				System.err.println("==================");														
				subject1.login();
				subject1.download();											
				
	}
}

 

2.2、动态AOP的简单使用示例:利用AOP实现自定义Spring注解

自定义Spring注解原理(自研框架里很多自定义注解):通过AOP的面向切面功能,在指定代码位置织入我们想要的代码,通过参数的形式传递给切面处理程序,然后执行解析。解析完后可以做想要的操作了。

(自定义注解可以使用AOP实现,也可以使用拦截器实现,推荐AOP)

示例:通过AOP定义注解记录日志信息

(1)定义注解类

package net.itaem.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * 说明:
 * 定义日志注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FileLog {
	String value() default "日志记录开始";	
}

(2)定义注解的处理程序

package net.itaem.aspect;
 
import net.itaem.annotation.FileLog;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
 
/**
 * 说明:
 * 日志的切面处理程序
 */
@Aspect
@Component
public class LogAspect {
 
	@AfterReturning("within(net.itaem..*) && @annotation(fl)")  
    public void addSuccessLog(JoinPoint jp, FileLog fl){  
        Object[] parames = jp.getArgs();
        //获取目标方法体参数  
        String className = jp.getTarget().getClass().toString();
        //获取目标类名  
        String signature = jp.getSignature().toString();
        //获取目标方法签名  
        String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("(")); 
        //获取注解值
        String desc=fl.value();
        //把调用的信息写到日常记录信息里面去...
    }  
  
    //标注该方法体为异常通知,当目标方法出现异常时,执行该方法体  
    @AfterThrowing(pointcut="within(net.itaem..*) && @annotation(fl)", throwing="e")  
    public void addExceptionLog(JoinPoint jp, FileLog fl, Exception e){  
       //把错误信息写到错误日志文件里面去...
    }  
}

(3)编写测试用例(老方法,XML形式)

配置spring启动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"  
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd  ">
        
 	<aop:aspectj-autoproxy/>  
    <bean name="purchaseService"   class="net.itaem.test.PurchaseService" />
    <bean class="net.itaem.aspect.LogAspect"/>    
</beans>

(4)编写service类

public class PurchaseService {
 
	public void purchaseProduct(String productName,int price,String type) {
		System.out.println("购买商品。。。");
	}
	
}

(5)编写测试代码

package net.itaem.test;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class Test {
 
	@org.junit.Test
	public void test() {
		ApplicationContext context=new ClassPathXmlApplicationContext("net/itaem/source/test.xml");
		PurchaseService service=(PurchaseService) context.getBean("purchaseService");
		service.purchaseProduct("电风扇", 98, "日用品");
	}
	 
}

(6)结果展示

下一篇:Spring IoC控制反转

books参考资料 :https://blog.csdn.net/fighterandknight/article/details/51209822

发布了52 篇原创文章 · 获赞 116 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/RuiKe1400360107/article/details/103582987
今日推荐