初识 Spring(07)---(AOP)

简介: AOP参照《Spring思维导图,让Spring不再难懂(aop篇)》AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

AOP

参照《Spring思维导图,让Spring不再难懂(aop篇)》

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。      

但是人们也发现,在分散代码的同时,也增加了代码的重复性。比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。    

也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。      

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。      AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,

在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则允许你定义从左到右的关系,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、通知(Advice)等。

aop使用场景

aop框架种类

  • AspectJ

  • JBoss AOP

  • Spring AOP

使用aop可以做的事情有很多。

  • 性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警。

  • 缓存代理,缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。

  • 软件破解,使用AOP修改软件的验证类的判断逻辑。

  • 记录日志,在方法执行前后记录系统日志。

  • 工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务。

  • 权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉。

观察一下传统编码方式与使用aop的区别

两种动态代理方式

1:JDK 通过proxy对象和InvocationHandler对象
2:CGlib 通过操纵二进制字节码的方式产生动态代理

Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。

JDK动态代理

  • JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

  • Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

CGLib动态代理

  • CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

构建项目

文件目录

applicationContext.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: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/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

		<context:component-scan base-package="com.neuedu"></context:component-scan>
		<!-- 自动生成代理类 -->
		<aop:aspectj-autoproxy>
			
		</aop:aspectj-autoproxy>
	
</beans>

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.UserService.save())")
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
 
}

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
}

Test.java

package com.neuedu.aspect;

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

import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		us.save();
	} 
}

输出:

修改代码:

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
	
	public void delete(String username){           //添加代码
		System.out.println("删除" + username);      //添加代码
	}

}

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.UserService.*(..))")  //修改代码
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
 
}

Test.java

package com.neuedu.aspect;

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

import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		//us.save();
		us.delete("zhang");  //修改代码
	} 
}

输出:

修改代码:新建 GoodsServiec.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class GoodsService {
	public void save() {
		System.out.println("保存商品信息...");
	}
}

Test.java

package com.neuedu.aspect;

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

import com.neuedu.service.GoodsService;
import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		us.save();
		//us.delete("zhang");
		
		GoodsService gs = ac.getBean(GoodsService.class);  //新增代码
		gs.save();                                        //新增代码
	} 
}

输出:

@Aspect    //整个叫做切面类
public class LoggingAspect {
    @Before("execution(public void com.neuedu.service.UserService.*(..))")  //切入点语法 ,确定切入点位置,即所有类的所有方法(before 通知)
@Before("execution(* com.neuedu.service..*(..))")    //终版,扫描该包下的所有子包和所有类的所有方法
    public void before(){      //这个方法叫做切入点 
        System.out.print("方法执行之前...");
    }

修改代码:

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect  //切面类
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.*.*(..))")
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
    @After("execution(public void com.neuedu.service.*.*(..))")      //新增代码
    public void after() {
    	System.out.println("执行方法之后...");
    }
    @AfterReturning("execution(public void com.neuedu.service.*.*(..))")  //新增代码
    public void afterReturning() {
    	System.out.println("只有方法正确执行之后才会执行...");
    }
    @AfterThrowing("execution(public void com.neuedu.service.*.*(..))")  //新增代码
    public void afterThrow() {
    	System.out.println("当方法抛出异常时调用的方法");
    }
}

UserServiceImpl.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
	
	public void delete(String username){
		System.out.println("删除" + username);
	}

	@Override
	public int div(int a, int b) {   //新增代码
		int c = a / b;
		return c;
	}
	
	
}

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;


public interface UserService {
	public void save() ;
	
	public void delete(String username);
	public int div(int a,int b);  //新增代码
}

Test.java

package com.neuedu.aspect;

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

import com.neuedu.service.GoodsService;
import com.neuedu.service.UserService; 

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		//us.save();
		//us.delete("zhang");
		
		GoodsService gs = ac.getBean(GoodsService.class);
		gs.save();
		us.div(2, 1);    //新增代码
		
	} 
}

输出:

@Before("execution(public void com.neuedu.service.*.*(..))")
    //执行这个注解之后,下面方法就会在一些方法执行之前执行


    @After("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法就会在一些方法执行之后执行

    @AfterReturning("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有方法正确执行之后才会执行

    @AfterThrowing("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有当方法抛出异常时才会执行
 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
43 1
|
1月前
|
Java Spring 容器
Spring的AOP失效场景详解
Spring的AOP失效场景详解
113 0
|
2月前
|
XML Java 编译器
Spring AOP初步理解及使用
Spring AOP初步理解及使用
49 0
|
2月前
|
Java Spring
[Spring]aop的配置与使用
[Spring]aop的配置与使用
40 0
[Spring]aop的配置与使用
|
30天前
|
设计模式 Java Maven
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
35 1
|
2月前
|
XML Java 数据格式
5个点轻松搞定Spring AOP底层实现原理
AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:
47 1
|
23天前
|
XML Java Maven
Spring之Aop的注解使用
Spring之Aop的注解使用
|
29天前
|
Java Spring
Spring 如何实现 AOP
Spring 如何实现 AOP
17 0
|
1月前
|
Java 编译器 程序员
Spring AOP 和 AspectJ 的比较
Spring AOP 和 AspectJ 的比较
37 0
|
1月前
|
Java Spring
【spring(三)】AOP总结
【spring(三)】AOP总结