面试问题浓缩总结 面试问题浓缩总结
  • Go
  • Java
  • C/C++
  • JavaScript/HTML
  • MySQL
  • Redis
  • MongoDB
  • 操作系统
  • 计算机网络
  • spring全家桶
  • mybatis
  • 中间件
  • 软件相关
  • 系统相关
  • 算法
  • 数据结构
  • 设计模式
  • CMU硕士经典100题
  • 剑指offer
  • 重点手撕代码
  • 程序员面试金典
  • 3月
  • 4月
  • 智力题
  • 业务问题
  • 一些技术
  • 安全相关
APP下载 (opens new window)
GitHub (opens new window)
  • Go
  • Java
  • C/C++
  • JavaScript/HTML
  • MySQL
  • Redis
  • MongoDB
  • 操作系统
  • 计算机网络
  • spring全家桶
  • mybatis
  • 中间件
  • 软件相关
  • 系统相关
  • 算法
  • 数据结构
  • 设计模式
  • CMU硕士经典100题
  • 剑指offer
  • 重点手撕代码
  • 程序员面试金典
  • 3月
  • 4月
  • 智力题
  • 业务问题
  • 一些技术
  • 安全相关
APP下载 (opens new window)
GitHub (opens new window)
  • spring全家桶

    • IOC
    • AOP
      • AOP实现原理
        • 使用JDK动态代理
        • 使用CGLIB来实现
        • 基本知识点
        • 两种实现方式
        • 使用
    • spring MVC
    • springSecurity
    • 面试问题
    • 循环依赖
    • 事务
    • 启动流程和配置
    • 注解相关
  • mybatis

  • 中间件

  • 框架
  • spring全家桶
小游
2021-03-23

AOP

AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.

在 AOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)

彻底征服 Spring AOP 之 理论篇 - SegmentFault 思否 (opens new window)

# AOP实现原理

是使用动态代理来实现的,当目标类为接口时用JDKProxy,否则用Cglib,下面来介绍一下这两种方法

# 使用JDK动态代理

我们的目标类端口和代理类实现如下

public interface WorkInter {
	void workInDay(double money);
	void workInNight(double money);
}
public class Worker implements WorkInter{
	@Override
	public void workInDay(double money) {
		System.out.println("workInDay");
	}
	@Override
	public void workInNight(double money) {
		System.out.println("workInNight");
	}
}
public class JdkProxy {
	@Test
	public void run() {
		WorkInter proxy = (WorkInter) Proxy.newProxyInstance(Worker.class.getClassLoader(), Worker.class.getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println("中介费"+((double)args[0])/2);
				Object invoke = method.invoke(Worker.class.newInstance(), args);
				System.out.println("下班");
				return invoke;
			}
		});
		proxy.workInDay(500);
		proxy.workInNight(1000);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

我们这里使用invoke来调用代理类,执行结果如下图所示

img

# 使用CGLIB来实现

实现步骤如下:

  1. 获得代理类的核心类Enhancer对象
  2. 设置父类(目标类),setSuperclass()方法,底层是创建目标类的子类
  3. 设置回调函数enhancer.setCallback(new MethodInterceptor())
  4. 创建代理对象 create()方法
public class Worker{
	public void workInDay(double money) {
		System.out.println("workInDay");
	}
	public void workInNight(double money) {
		System.out.println("workInNight");
	}
}
public class WorkerProxy {
	@Test
	public void run() {
			Enhancer enhancer=new Enhancer();
			//2.设置父类(目标类),setSuperclass()方法,底层是创建目标类的子类
			enhancer.setSuperclass(Worker.class);
			//3.设置回调函数enhancer.setCallback(new MethodInterceptor()) 
			
			enhancer.setCallback(new MethodInterceptor() {
				    *//**
				 * Object object:代理对象
				 * Method method:目标类的方法
				 * Object[] args:方法的形参
				 * MethodProxy methodProxy:方法的代理对象
				 * 
				 *//*
				@Override
				public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
					// TODO Auto-generated method stub
					//开启事务
					//前置通知
					
					System.out.println("代理收取费用"+ (double)args[0]/2);
					//执行目标方法,需要目标类实例
					Object invoke = method.invoke(Worker.class.newInstance(), (double)args[0]/2);
					
					//后置通知
					
					System.out.println("go  home~~~~~");
					return invoke;
				}
			});
			
			//4.创建代理对象 create()方法
			Worker workerPeoxy = (Worker) enhancer.create();
			
			workerPeoxy.workInDay(500);
			workerPeoxy.workInNight(1000);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 基本知识点

  • 切面:拦截器类,其中会定义切点以及通知

  • 织入:织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。

  • 切点:具体拦截的某个业务点。

  • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:

    1. 前置通知:@Before 在目标业务方法执行之前执行
    2. 后置通知:@After 在目标业务方法执行之后执行
    3. 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
    4. 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
    5. 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

# 两种实现方式

  1. jdk实现,缺点是只能对目标接口实现动态代理
  2. cglib实现,可以对类进行动态代理,但是类不可以是final。而且JDK动态代理不依赖其他包,Cglib需要导入ASM包,对于简单的有接口的代理使用JDK动态代理可以少导入一个包。

# 使用

先使用Aspect来标识为切面类

package com.jpeony.spring;
 
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
/**
 *  注解@Aspect标识该类为切面类
 */
@Component
@Aspect
public class PersonAspect {
//    /**
//     * 开会之前--找个位置坐下
//     */
//    @Before("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
//    public void takeSeats() {
//        System.out.println("找位置坐");
//    }
//
//    /**
//     * 开会之前--手机调成静音
//     */
//    @Before("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
//    public void silenceCellPhones() {
//        System.out.println("手机调成静音");
//    }
//
//    /**
//     * 开会之后--写会议总结报告
//     */
//    @After("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
//    public void summary() {
//        System.out.println("写会议总结报告");
//    }
    /**
     * =========================================================================
     * 从上面的执行代码可以看出切点execution表达式内容都是一样,
     * 我们可以通过@Pointcut进行优化。
     * =========================================================================
     */
    /**
     * 通过注解@Pointcut定义切点,conference()只是一个标识,无所谓是什么,
     * 方法中内容本身也是空的,使用该切点的地方直接通过标识conference()引用切点表达式。
     */
    @Pointcut("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
    public void conference() {}
 
    /**
     * 开会之前--找个位置坐下
     */
    @Before("conference()")
    public void takeSeats() {
        System.out.println("找位置坐");
    }
 
    /**
     * 开会之前--手机调成静音
     */
    @Before("conference()")
    public void silenceCellPhones() {
        System.out.println("手机调成静音");
    }

    /**
     * 开会之后--写会议总结报告
     */
    @After("conference()")
    public void summary() {
        System.out.println("写会议总结报告");
    }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

然后我们创建一个目标类

package com.jpeony.spring;
 
import org.springframework.stereotype.Component;
 
@Component
public class ConferenceServiceImpl implements ConferenceService {
 
    @Override
    public void conference() {
        System.out.println("开会......");
    }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13

最后我们开启自动代理功能

package com.jpeony.spring;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
/**
 * Jdk代理:基于接口的代理,一定是基于接口,会生成目标对象的接口的子对象。
 * Cglib代理:基于类的代理,不需要基于接口,会生成目标对象的子对象。
 *
 * 1. 注解@EnableAspectJAutoProxy开启代理;
 *
 * 2. 如果属性proxyTargetClass默认为false, 表示使用jdk动态代理织入增强;
 *
 * 3. 如果属性proxyTargetClass设置为true,表示使用Cglib动态代理技术织入增强;
 *
 * 4. 如果属性proxyTargetClass设置为false,但是目标类没有声明接口,
 *    Spring aop还是会使用Cglib动态代理,也就是说非接口的类要生成代理都用Cglib。
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.jpeony")
public class AppConfig {
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

参考:

【Spring基础】AOP使用注解实战_yhl_jxy的博客-CSDN博客_aop实战 (opens new window)

编辑 (opens new window)
上次更新: 2021/04/24, 10:38:20
IOC
spring MVC

← IOC spring MVC→

Theme by Vdoing | Copyright © 2021-2021 小游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式