1、什么是AOP?(是什么)
Aspect OrientedProgramming的缩写,意思就是:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。它是一种编程范式,一种编程思想。AOP是Spring提供的关键特性之一。对了,有人提到这个和拦截器有什么关系,注意AOP是一种编程思想,拦截器就是基于这种编程思想实现的。
基本概念:
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。
连接点(Joinpoint):程序执行过程中的某个特定的点
通知(Advice):在切面的某个特定的连接点上执行的动作。
切入点(Pointcut): 匹配连接点的断言,在AOP中通知和一个切入点表达式的关联。
引入(Introduction)在不修改类代码的前提下,为类添加新的方法和属性。
目标对象(target Object)被一个或者多个切面所通知的对象
AOP代理(Aop proxy)AOP框架创建的对象,用来实现切面契约(aspectcontract)包括通知放法执行等功能。
织入(weaving) 把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为编制时织入,类加载时织入,执行时织入。
2、aop编程思想的作用!(为什么)
其实aop就是对OOP编程的有效补充。其实最终目的是实现程序的解耦。但是OOP一般允许开发者定义纵向的关系,但并不适合定义横向关系,使用AOP技术可以将一些系统性相关的编程工作、独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了 将不同的关注点分离出来的效果。
如上图,我们可以将日志以及权限这种分离出来,然后在需要用到的这些核心功能模块中横向的切入进去,这样对于核心模块是无感的,这样的话讲大大降低系统的耦合性和复杂性,试想如果这些模块散落在各个核心业务中,如果出现大的改动,必然会产生极大的维护成本,需要每一处都进行修改。
3、实现方式
** 方式一:**@AspectJ注解驱动的切面,这个是一种静态代理的方式
Java通过Java编译器将.java源文件编译成.class字节码文件,这种.class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码,JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class文件内的信息,生成对应的Class对象,进而使Class对象创建类的具体实例来进行调用实现具体的功能。
所以这种方式就是在java编译成class的时候去实现代理。性能上很高效,但是没有不太灵活。
这种方式我的理解就是在编译器将切片插入到对应的函数栈中(仅仅自己的理解)
** 方式二:**运行期动态代理,(JDK动态代理,CGLib动态代理)
纯jiava的实现,无需特殊的编译过程,不需要控制类加载器层次。
目前只支持放法执行连接点,(通知spring bean的方法的执行)
不是提供一个最完整的AOP实现尽管它已经非常强大,而是侧重用于提供一种AOP实现和SpringIOC容器之间的整合,用于帮助解决企业应用中常见的问题。
Spring AOP 不会和Aspect竞争,从而提供综合全面的AOP解决方案。
spring中AOP代理由Spring的IOC容器负责生成,管理,其依赖关系也是由IOC容器负责管理的,因此,AOP代理可以直接使用容器中的其他bean实例作为目标,这种关系可由IOC容器的依赖注入提供,Spring创建代理的规则为:
1)、默认使用java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。
2)、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLB代理,也可强制使用CGLIB(CGLIB是一个强大的、高性能的代码生成库)
4、应用场景
其实这里有点多余,aop是一种编程思想,你可以在很多场景中去使用,但是还是想给大家一个直观的感受。
5、在sping中使用切面编程。(怎么用?)
基于@AspectJ注解的AOP实现
这里主要是采用这种方式来演示aop的实现过程,来直观的体会aop实现的思想。
step1:引用依赖
aopalliance
aopalliance
1.0
org.springframework
spring-aspects
5.1.3.RELEASE
Step2:创建测试类
IAopTest.java 接口
package com.mjcc.spring.aop;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:40 2019/7/28
* @Modified By:
*/
public interface IAopTest {
public int add(int a, int b);
public int sub(int a, int b);
}
AopTestImpl.java 实现类
package com.mjcc.spring.aop;import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:42 2019/7/28
* @Modified By:
*/
@Component("aopTest")
public class AopTestImpl implements IAopTest {
public int add(int a, int b) {
int res = a + b;
return res;
}
public int sub(int a, int b) {
int res = a - b;
return res;
}
}
LoggerAspect.java // 日志的切面编程
package com.mjcc.spring.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:59 2019/7/28
* @Modified By:
*/
@Aspect
// 告诉spring 这是一个切面类,就是横切关注点的集合,与核心业务代码无关的日志,权限,事务代码 集中起来组成切面
@Componentpublic
class LoggerAspect {
private Logger logger = Logger.getLogger(this.getClass());
// 指定beforeMethod在哪个类中的哪个方法前切入执行
// @Before的参数里,写一个切入点表达式,来告诉spring 要将下面的方法切入到哪个类的哪个方法前面执行
@Before("execution(public int com.mjcc.spring.aop.AopTestImpl.add(int, int))")
@Before("execution(public int com.mjcc.spring.aop.AopTestImpl.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); // 拿到切入点的的方法名
List
测试base类
package com.mjcc.spring.base;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 14:18 2019-04-13
* @Modified By:
*/
public class UnitTestBase {
private ClassPathXmlApplicationContext context;
private String springXmlPath;
public UnitTestBase () {
}
public UnitTestBase(String springXmlPath) {
this.springXmlPath = springXmlPath;
}
@Before // 初始化方法,执行当前测试类的每个测试方法前执行
public void before () {
if (StringUtils.isEmpty(springXmlPath)) {
springXmlPath = "classpath*:spring-*.xml";
}
try {
context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After //释放资源,执行当前测试类的每个测试方法后执行
public void after () {
context.destroy();
}
@SuppressWarnings("unchecked")
protected T getBean (String beanId) {
try {
return (T)context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
protected T getBean (Class clazz) {
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
测试类:
package com.mjcc.spring.aop;
import com.mjcc.spring.base.UnitTestBase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:47 2019/7/28
* @Modified By:
*/
@RunWith(BlockJUnit4ClassRunner.class)
public class AopTestImplTest extends UnitTestBase {
public AopTestImplTest () {
super("applicationContext.xml");
}
@Test
public void AopTest () {
IAopTest aopTest = (IAopTest) super.getBean("aopTest");
System.out.println("add : " + aopTest.add(1, 1));
System.out.println("sub : " + aopTest.sub(8, 5));
}
}
xml配置
切入点:
- @Before和它注解的这个方法在AOP中叫做通知,在上面基础概念中已经提到了, before属于前置通知。
还有下面这些通知,
@After 后置通知,在目标方法执行之后执行
@AfterReturning 返回通知,(在目标方法返回结果之后执行)
@AfterThrowing异步通知,在目标方法抛出异常之后执行
@Around 环绕通知
对了,注意一个问题,当同一个目标方法有多个切面的时候,到底是哪个先执行,这个顺序可以使用@Order注解来注解切入类,值越小,越先执行。@Order(1) > @Order(2)
个人拙见,望指教。
关注公众号回复获取“java入门就业”获取一套个人收藏的java视频教程 ,