凡是过往,皆为序章

0%

Spring_04(AOP&AspectJ)

本节介绍spring 的AOP——面向切片编程。


动态代理

动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(不是真实定义的类)在程序运行时由VM根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。

简单来说,就是给原来的类增加一些功能,即功能增强

JDK的动态代理

动态代理的实现方式常用的有两种:使用JDK的 Proxy,与通过CGLIB生成代理。

Jdk的动态要求目标对象必须实现接口,这是java设计上的要求。

从jdk1.3以来,java语言通过java.lang.reflect包提供三个类支持代理模式 Proxy,Method 和 InovcationHandler 。

动态代理的作用:

1) 在目标类源代码不改变的情况下,增加功能。

2) 减少代码的重复

3) 专注业务逻辑代码

4) 解耦合,让你的业务功能和日志,事务和非业务的功能分离。

案例

现有 SomeService 接口和它的实现类 SomeServiceImpl,要求使用动态代理,不改变SomeServiceImpl的源码,给类中的doSome 和 doOther() 类添加 时间 和 事务完成代码。具体代码如下

SomeService 接口

1
2
3
4
5
6
package com.thorine.service;

public interface SomeService {
void doSome();
void doOther();
}

SomeServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.thorine.service.impl;

import com.thorine.service.SomeService;

public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
//ServiceTools.doLog(); // 动态代理就是实现注释里的代码的
System.out.println("执行doSome()...");
//ServiceTools.doTrans();
}

@Override
public void doOther() {
//ServiceTools.doLog();
System.out.println("执行doOther()...");
//ServiceTools.doTrans();
}
}

MyInvocationHandler(代理类)

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
package com.thorine.handler;

import com.thorine.util.ServiceTools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
// 目标对象
private Object target; // SomeServiceImpl类

public MyInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行了MyInvocationHandler的invoke方法");
System.out.println("Method方法名:" + method.getName());

Object result = null;

ServiceTools.doLog(); // 方法增强

// 执行目标类的方法,通过Method类实现,通过代理对象执行方法时,会调用这个invoke()
result = method.invoke(target, args); // SomeServiceImpl.doOther,doSome()

ServiceTools.doTrans(); // 方法增强

// 目标方法执行的结果
return result;
}
}

测试类

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
package com.thorine.service;

import com.thorine.handler.MyInvocationHandler;
import com.thorine.service.impl.SomeServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MyApp {
public static void main(String[] args) {
// 使用JDK 的proxy创建代理对象
// 创建目标对象
SomeService target = new SomeServiceImpl();

// 创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);

// 使用proxy创建代理
SomeService proxy = (SomeService) Proxy.newProxyInstance
(target.getClass().getClassLoader(), target.getClass().getInterfaces(),handler);

//通过代理执行方法,会调用handler中的invoke()
proxy.doSome();
}
}

AOP简介

AOP (Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。

AOP底层,就是采用动态代理模式实现的。采用了两种代理 : JDK的动态代理,与CGLIB的动态代理。

AOP为Aspect Oriented Programming的缩写,意为 : 面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

因为动态代理很灵活,实现方法复杂多样,为了统一规则,而出现的AOP。

总之,AOP就是动态代理的规范化。

Aspect:切面

给目标类增加的功能,就是切面。像上面用的增加日志、事务功能 等都是切面。

特点:一般都是非业务方法,可以独立使用。

业务方法:功能性的方法代码等。例如:数据库增删改查,逻辑运算(10+20)等。

怎么理解面向切面编程?

1、在分析项目功能时,找出切面。(最重要)

2、合理的安排切面的执行时间(在目标方法前,还是目标方法后)

3、合理的安全切面执行的位置,在哪个类,哪个方法增加增强方法。

AOP编程术语

1)Aspect(切面):表示增强的非业务性功能,例如:日志,统计信息,参数检查,权限验证。

2)JoinPoint(连接点):连接业务方法和切面的位置,就是某类中的业务方法。(只有一个)

3)PointCut(切入点):指多个连接点的集合。(多个方法)

4)目标对象:给哪个类的方法增加功能,该类便是目标对象。

5)Active(通知):表示切面功能执行的时间(在目标方法前,还是目标方法后)

AOP的实现

aop是一个规范,是动态的一个规范化,一个标准。

aop的技术实现框架:

  1. spring: spring在内部实现了aop规范,能做aop的工作。spring主要在事务处理时使用aop。

    我们项目开发中很少使用spring的aop实现。因为spring的aop比较笨重。

  2. aspectJ(重点掌握) :一个开源的专门做aop的框架。

AspectJ对AOP的实现(掌握)

简介

AspectJ是一个优秀面向切面的框架,它扩展了Java语言,提供了强大的切面实现。

AspectJ实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring将AspectJ的对于AOP的实现引入到了自己的框架中。

官网地址:https://www.eclipse.org/aspectj/

AspectJ框架实现aop有两种方式:

  • 使用xml配置文件:配置全局事务
  • 使用注解(常用),有5个注解

学习AspectJ框架的使用

1、切面的执行时间,这个执行时间在规范中叫做Advice(通知)

​ 在AspectJ 框架中使用注解表示的。也可以使用xml配置文件中的标签

​ 1)@Before

​ 2)@AfterReturning

​ 3)@Around

​ 4)@AfterThrowing

​ 5)@After

AspectJ的切入点表达式(掌握)

AspectJ定义了专门的表达式用于指定切入点。表达式的原型是:

1
2
3
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)

解释:

​ modifiers-pattern:访问权限类型(公有、私有)

ret-type-pattern:返回值类型

​ declaring-type-pattern:包名类名

name-pattern(param-pattern):方法名(参数类型和参数个数)

​ throws-pattern:抛出异常类型

?表示可选的部分

以上表达式共4个部分。

1
execution(访问权限 方法返回值 方法声明(参数) 异常类型) // 方法返回值 和 方法声明是必须的

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。

注意,表达式中第一项和第四项是可省略的部分,各部分间用空格分开。在其中可以使用以下符号:

符合 意义
* 0至多个任意字符
.. 用在方法参数中,表示任意多个参数
用在包名后,表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类
用在接口后,表示当前接口及其实现类

举例(理解掌握)

  1. execution(public * (..)) :指定切入点为所有的公有方法,括号里共有三个部分,第二个 * 表示方法返回值,第三个表示表示方法声明,因为后紧跟括号。
  2. execution(* set*(..)) :指定切入点为任意一个以“set”开始的方法。只有两个部分,所以一点是两个必须部分,返回值和方法声明。
  3. execution(* com.xyz.service.*.*(..)):指定切入点为:定义在service包里的任意类的任意方法。
  4. execution(* com.xyz.service..*.*(..)):指定切入点为:定义在 service包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟 “* ”,表示包、子包下的所有类。
  5. execution(* *..service.*.*(..)):指定所有包下的serivce子包下所有类(接口)中所有方法为切入点
~感谢你请我吃糖果~
-------------本文结束,感谢您的阅读,欢迎评论留言!-------------