0%

AOP

本章节也是个人梳理

spring入门第六节——AOP

一、什么是AOP

AOP是一种将功能拆分成多个独立模块的框架,去除任意一个模块不影响原程序以及别的模块的运行,可以有效的降低耦合度,举个例子就是由多个小块拼接而成的机器人,去除其中一个小块,不会整体和其它独立零件造成影响。

二、AOP的类型

(1)有接口的情况:通过proxy对接口的实现类进行动态代理

(2)没接口的情况:通过CGlib对类进行继承后,对子类实现动态代理

三、什么是动态代理

创建一个代理对象,代理对象拥有类的所有功能和增强的部分,由代理对象替代原来的对象参与到程序中,用游戏解释的话类似于仿生泪滴或者外置装甲之类的,让仿生泪滴或者装了外置装甲的另一个我去执行任务。

四、AOP的底层原理(有接口的情况)(实操的时候不需要看底层,方法都被封装好了,但是为了能够更好体会技术的惊喜感和方便开发,还是要掌握底层技术)

由AOP中proxy的newproxyInstance()方法实现,newproxyInstance()有三个参数,第一个是类的加载器,第二个是接口(可以有多个接口),第三个是Invokehandler接口(实现接口后第三个改为Invokehandler的实现类),这个接口定义了原方法和增强部分,不过需要编写者自己实现。

4.1 Invokehandler的实现类

(1)创建一个新的类继承Invokehandler()类,通过有参构造器获得获取对象,并用Object泛型储存

(2)实现Invokehandler的原生方法,该方法有三个参数,第一个是代理对象(Proxy),第二个是对象使用的方法(Method),第三个是使用的方法的参数(String[] arg)

(3)在Invokehandler的原生方法内编写增强部分的代码

(4)在Invokehandler的原生方法内利用Method类的自带的invoke(Object,arg)调用原方法(此处Object是有参构造器获取的对象,arg是参数)

(5)return返回值。

4.2 代码:

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
package spring5test;

import spring5test.Dao.UserDao;
import spring5test.Dao.UserDaoImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class demo {
public static void main(String args[]){
Class[] inter = {UserDao.class};
UserDaoImpl test = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(demo.class.getClassLoader(),inter,new handler(test));
System.out.println("查看代理对象是否成功:"+dao.add(1,2)+dao.update("T头人"));

}
}

class handler implements InvocationHandler{
Object obj;

public handler(Object obj) {
this.obj = obj;
}

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

Object res = method.invoke(obj,args);

System.out.println("执行之后查看对象地址:"+obj);

return res;
}
}

截图:

4.3 代码解释:

(1)UserDao dao = (UserDao) Proxy.newProxyInstance(demo.class.getClassLoader(),inter,new handler(test));

创建了UserDao类的代理对象,所以右侧代码要用(UserDao)进行一个类型强转。

Proxy.newProxyInstance(demo.class.getClassLoader(),inter,new handler(test))

三个参数依次为类的加载器,接口,Invokehandler接口的实现类。

(2)public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

实现Invokehandler接口的方法,三个参数依次为代理对象,方法,方法的参数。

(3)Object res = method.invoke(obj,args);

用method类型自带的invoke方法执行原程序,两个参数分别是对象和方法的参数,将结果存储在Object泛用类中。

五、AOP操作术语

(1)连接点:可以被增强的方法。

(2)切入点:实际被增强的方法。

(3)通知(增强):对方法的功能的增加,有五种类型

前置增强Before

后置增强AfterReturning(在return返回值之后执行增强)

环绕增强Around(前置后置都有增强)

异常增强AfterThrowing(抛出异常的时候才会触发的增强)

最终增强After(类似于Java的final,无论结果如何,只要程序执行到最后就会执行的增强)

(4)切面:将增强导入切入点的过程。

六、AOP准备工作:

Spring本身的框架并不优秀,所以需要导入AspectJ的jar包,用AspectJ的框架和Spring共用,通常情况Spring都是要和AspectJ共同使用的。

导入AOP,AspectJ,以及另外三个Spring的AOP的jar包。

七、注解方式使用AOP框架

前面说到,Spring的IOC容器有两个大步骤,创建对象和注入属性,AOP框架也可以分为两大步,创建对象和增强方法。

(1)创建对象:

在xml文件中定义context名称空间,打开Component扫描,通过@Component注解封装Bean类。

1
context:component-scan base-package="spring5test"></context:component-scan>

(2)增强:

在xml文件中定义aop名称空间,打开aop自动代理,检测并使用所有@Aspect注解

1
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

(3)通过切入点表达式找到需要被增强的类

切入点表达式:execution([限定修饰符][返回值类型][类的路径][方法] (参数列表)))

限定修饰符:public,private这些东西,常用*表示

返回值类型

类的路径:从src目录开始查找路径

方法

参数列表:用..表示所有参数

举例:

1
execution(* spring5test.User.User.Amethod(..))

对spring5text.User.User类中的所有方法进行增强

execution(* spring5test.User.User.*(..))

对spring5text.User类中所有类中的的所有方法进行增强

img

(4)代码

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
package spring5test.User;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Userproxy {
@Pointcut(value = "execution(* spring5test.User.User.Amethod(..))")
public void conf(){}

@Before(value = "conf()")
public void before(){
System.out.println("before...");
}

@After(value = "conf()")
public void after(){
System.out.println("after...");
}

@AfterReturning(value = "conf()")
public void afterReturn(){
System.out.println("afterreturn...");
}

@AfterThrowing(value = "conf()")
public void afterthrow(){
System.out.println("afterthrow...");
}

@Around(value = "conf()")
public void around(ProceedingJoinPoint a) throws Throwable{
System.out.println("环绕之前");
a.proceed();
System.out.println("环绕之后");
}
}

(5)提取公共路径

使用@Pointcut注解

@Pointcut(value = “execution(* spring5test.User.User.Amethod(..))”)
public void conf(){}

之后的注解都可以替换成类

八、全配置文件形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="1" class="spring5test.User.User"></bean>
<bean id="2" class="spring5test.User.Userproxy"></bean>
<aop:config>
<aop:pointcut id="p" expression="execution(* spring5test.User.User.Amethod(..))"/>
<aop:aspect>
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>

九、全注解形式

1
2
3
4
5
6
7
8
9
10
11
package spring5test;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"spring5test"})
@EnableAspectJAutoProxy()
public class conf {
}