0%

核心高级

AOP介绍,AOP的一些术语,AspectJ概念,SpringAOP,动态代理

核心高级

AOP介绍

  • AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构。
  • AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范

作用:AOP采取横向抽取机制,补充了传统纵向继承体系(OOP)无法解决的重复性代码优化(性能监视、事务管理、安全检查、缓存),将业务逻辑和系统处理的代码(关闭连接、事务管理、操作日志记录)解耦。

AOP的一些术语

  • Target 目标对象
  • Joinpoint 连接点 目标类的方法
  • Pointcut 切入点 具体要增强的Joinpoint 方法
  • Advice 通知 对应增强的内容类,分为前置通知,后置通知,异常通知,最终通知,环绕
    通知(切面要完成的功能)

  • Aspect 切面 切入点+通知,自己来编写和配置的

  • Proxy(代理)

  • Weaving(织入) 把增强应用到目标对象来创建新的代理对象的过程

AspectJ

AOP的一种早期实现,比较完善,实现的功能多。

有自己的编译器。

与JAVA无缝衔接。

静态织入

SpringAOP

动态代理实现,使用了JDK动态代理和CGlib动态代理实现

两种动态实现原理

类加载时动态生成一个类给目标类添加功能。

jdk动态代理

目标类必须实现一个接口

通过反射实现

比CGlib用的资源少

Spring先用jdk动态代理,目标类没有实现接口时使用CGlib代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 使用JDK的方式生成代理对象
*/
public class MyProxyUtils {
public static UserService getProxy(final UserService service) {
// 使用Proxy类生成代理对象
UserService proxy = (UserService)Proxy.newProxyInstance(service.getClass().
getClassLoader(),service.getClass().getInterfaces(),new InvocationHandler() {
// 代理对象方法一执行,invoke方法就会执行一次
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if("save".equals(method.getName())){
System.out.println("记录日志...");
}
// 让service类的save或者update方法正常的执行下去
return method.invoke(service, args);
}
});
// 返回代理对象
return proxy;
}
}
CGlib动态代理

通过继承目标类实现

目标类不能被 final 或static 修饰

单例时使用较好,因为创建一次消耗的资源较多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static UserService getProxy(){
// 创建CGLIB核心的类
Enhancer enhancer = new Enhancer();
// 设置父类 目标类
enhancer.setSuperclass(UserServiceImpl.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
// 记录日志
System.out.println("记录日志了...");
}
return methodProxy.invokeSuper(obj, args);
}
});
// 生成代理对象
UserService proxy = (UserService) enhancer.create();
return proxy;
}

基于AspectJ的aop使用

Spring+AspectJ整合

添加依赖

1
2
3
4
5
6
7
8
9
10
11
<!-- 基于AspectJ的aop依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

目标类和方法

1
2
UserService接口
UserServiceImpl实现类

xml实现

实现步骤

  1. 编写通知类

    1
    2
    3
    4
    5
    public class MyAdvice{
    public void log(){
    System.out.println("记录日志");
    }
    }
  2. 配置通知,交给IoC容器

    1
    <bean name = "myAdvice" class = "com.xiaoruiit.advice.Myadvice"></bean>
  3. 配置切面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <aop:config>
    <aop:aspect ref="myAdvice">
    <!-- 对哪些切入点进行功能增强由pointcut属性指定 -->
    <!-- 增强哪些功能由method属性指定,它指定的方法是advice类中的方法 -->
    <!-- 什么时候织入?通过aop:before等标签来指定 -->
    <aop:before method="before"
    pointcut="execution(* *..*.*ServiceImpl.*(..))" />
    <aop:after method="after"
    pointcut="execution(* *..*.*ServiceImpl.*(..))" />
    </aop:aspect>
    </aop:config>

切入点表达式

1
execution([修饰符] 返回值类型 包名.类名.方法名(参数))

通知类型

通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知。

XML结合注解实现

实现步骤

1.编写切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component("myAspect")
@Aspect
public class MyAspect {

private static final String pcut="execution(* *..*.*ServiceImpl.*(..))";

@Before(value="MyAspect.fn()")
public void before() {
System.out.println("这是注解方式的前置通知");
}
@AfterReturning(pcut)
public void after() {
System.out.println("这是注解方式的后置通知");
}

@Pointcut("execution(* *..*.*ServiceImpl.*(..))")
public void fn() {}
}

开启AOP自动代理

1
2
<!-- 开启aspectj的自动代理,用于AOP的注解方式 -->
<aop:aspectj-autoproxy/>

注解实现

取代开启Aspect自动代理xml文件

1
2
3
4
@Configuration 
@ComponentScan(basePackages="com.xiaoruiit")
@EnableAspectJAutoProxy
public class SpringConfiguration { }