只是不愿随波逐流 ...|

lidongdongdong~

园龄:2年7个月粉丝:14关注:8

2、AOP

1、静态代理

image

1. 静态类⽂件数量过多, 不利于项⽬管理
UserServiceImpl --> UserServiceProxy
OrderServiceImpl --> OrderServiceProxy
2. 额外功能维护性差
要对每一个代理类修改功能(麻烦)

2、Spring 的动态代理开发

动态代理不需要定义类⽂件,都是在 JVM 运行过程中动态创建的,所以不会造成类文件数量过多,影响项目管理的问题

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>

2.1、UserServiceImpl

public class UserServiceImpl implements UserService {
@Override
public void login(String name, String password) {
System.out.println("UserServiceImpl.login");
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
}
}

2.2、MethodBeforeAdvice

前置通知

public class Before implements MethodBeforeAdvice {
/**
* 作用: 需要在目标方法执行之前运行的额外功能, 书写在 before 方法中
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----method before advice log------");
}
}

2.3、MethodInterceptor

环绕通知

public class Arround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object ret = null;
try {
System.out.println("------前-----");
ret = invocation.proceed();
System.out.println("------后-----");
} catch (RuntimeException e) {
System.out.println("------异常-----");
} finally {
System.out.println("------最终-----");
}
return ret;
}
}

2.4、配置

Spring 的工厂通过原始对象的 id 值获得的是代理对象

<!--目标对象-->
<bean id="userService" class="xxx.UserServiceImpl"/>
<!--增强对象-->
<bean id="before" class="xxx.Before"/>
<aop:config proxy-target-class="true">
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
</aop:config>
<!--目标对象-->
<bean id="userService" class="xxx.UserServiceImpl"/>
<!--增强对象-->
<bean id="arround" class="xxx.Arround"/>
<aop:config proxy-target-class="true">
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="arround" pointcut-ref="pc"/>
</aop:config>

3、切入点详解

3.1、切入点表达式

方法

* 第一个代表修饰符 + 返回值,第二个代表方法名
`* *(..)` 所有方法
`* login(..)` 所有 login 方法
`* login(String,..)` .. 可以和具体的参数类型连用
`* login(String,String)` 所有 login(String, String) 方法
`* register(com.zzw.proxy.User)` 非 java.lang 包中的类型, 必须要写全限定名
* 精准方法
* 第一个代表修饰符 + 返回值,第二个代表包 + 类 + 方法名
`* com.zzw.proxy.UserServiceImpl.login(..)`
`* com.zzw.proxy.UserServiceImpl.login(String,String)`

`* com.zzw.proxy.UserServiceImpl.*(..)` 类中的所有方法
`* *.UserServiceImpl.*(..)` 一级包的 UserServiceImpl 类
`* *..UserServiceImpl.*(..)` 多级包的 UserServiceImpl 类

`* com.zzw.proxy.*.*(..)` 不包括子包
`* com.zzw.proxy..*.*(..)` 包括子包

3.2、切入点函数

execution

最为重要的切入点函数,功能最全

* 最为重要的切入点函数,功能最全
* 执行:方法切入点表达式、类切入点表达式、包切入点表达式
* 弊端:execution 执行切入点表达式,书写麻烦
`execution(* com.zzw.proxy..*.*(..))`
* 注意:其他的切入点函数是 execution 书写复杂度的简化,功能上完全一致

args

* 作用: 主要用于函数(方法) 参数的匹配
* 切入点: 方法参数必须得是 2 个字符串类型的参数
`execution(* *(String,String))`
`args(String,String)`

within

* 作用: 主要用于进行类、包切入点表达式的匹配
* 切⼊点: UserServiceImpl 这个类
`execution(* *..UserServiceImpl.*(..))`
`within(*..UserServiceImpl)`
`execution(* com.baizhiedu.proxy..*.*(..))`
`within(com.baizhiedu.proxy..*)`

@annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
* 作用: 为具有特殊注解的方法加入额外功能
`<aop:pointcut id="" expression="@annotation(com.zzw.Log)"/>`

3.3、切入点逻辑运算

and

* 案例: login, 参数 2个字符串
`execution(* login(String,String))`
`execution(* login(..)) and args(String,String)`
* 注意: 与操作不同用于同种类型的切入点函数

or

* 案例: register 方法 和 login 方法作为切入点
`execution(* login(..)) or execution(* register(..))`

4、AOP 的底层实现原理

JDK 和 CGLib 动态代理、BeanPostProcessor

public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----- new Log-----");
Object ret = method.invoke(bean, args);
return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler);
}
}
<bean id="userService" class="com.zzw.factory.UserServiceImpl"/>
<!--
1. 实现 BeanPostProcessor 进行加工
2. 配置文件中对 BeanPostProcessor 进行配置
-->
<bean id="proxyBeanPostProcessor" class="com.zzw.factory.ProxyBeanPostProcessor"/>

5、基于注解的 AOP 编程

5.1、四大通知

- 前置通知(before) 在切点运行 "之前" 执行
- 后置通知(after-returning) 在切点正常运行结束 "之后" 执行
- 异常通知(after-throwing) 在切点 "发生异常" 的时候执行
- 最终通知(after) 在切点的 "最终" 执行
try {
前置通知(before)
// 切点执行的位置
后置通知(after-returning)
} catch (Exception e) {
异常通知(after-throwing)
} finally {
最终通知(after)
}
try {
begin(); // 开启事务
obj = pjp.proceed(); // 切点执行的位置
commit(); // 提交事务
} catch (Throwable e) {
rollback(); // 回滚事务
} finally {
close(); // 关闭事务
}
return obj;

5.2、注解实现四大通知

@Aspect
@Component
public class Logger {
// 声明切点
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt() {
}
@Before("pt()")
public void beforeMethod() {
System.out.println("在方法执行之前运行");
}
@AfterReturning("pt()")
public void afterReturnMethod() {
System.out.println("在方法正常返回之后运行");
}
@AfterThrowing("pt()")
public void afterThrowingMethod() {
System.out.println("在方法执行出现异常时运行");
}
@After("pt()")
public void afterMethod() {
System.out.println("在方法最终运行");
}
}
<!--注解扫描-->
<context:component-scan base-package="com.zzw"/>
<!--切面自动代理-->
<aop:aspectj-autoproxy/>
<!--告知 Spring 基于注解进行 AOP 编程, 默认 JDK 代理, true 为 CGLib 代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

5.2、注解实现环绕通知

@Aspect
public class MyAspect {
// 声明切点
@Pointcut("execution(* com.zzw.service.impl.*.*(..))")
private void pt() {
}
// 手动指定切点和增强的执行顺序
// @Around("execution(* com.zzw.service.impl.*.*(..))")
@Around("pt()")
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object obj = null;
try {
System.out.println("在方法执行之前运行");
obj = pjp.proceed(); // 切点执行的位置
System.out.println(new Date().toLocaleString()); // 时间
System.out.println(Arrays.toString(pjp.getArgs())); // 参数
System.out.println(obj); // 返回值
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
System.out.println(methodSignature.getMethod().getName()); // 方法名
System.out.println("在方法正常返回之后运行");
} catch (Throwable e) { // 注意: 捕获的是 Throwable, 它是 Exception 的父接口
System.out.println("在方法执行出现异常时运行");
} finally {
System.out.println("在方法最终运行");
}
return obj;
}
}
<!--没有 @Component 就要配置 bean-->
<bean id="arround" class="xxx.MyAspect"/>
<!--切面自动代理-->
<aop:aspectj-autoproxy/>
<!--告知 Spring 基于注解进行 AOP 编程, 默认 JDK 代理, true 为 CGLib 代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

5.3、0 xml 实现

@Configuration
@ComponentScan("com.zzw")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

6、AOP 的坑

* 坑: 在同⼀个业务类中, 进行业务方法间的相互调用, 只有最外层的方法, 才是加⼊了额外功能(内部的方法, 通过普通的方式调用, 都调用的是原始方法)
* 如果想让内层的方法也调用代理对象的方法, 就要实现 AppicationContextAware 获得工厂, 进而获得代理对象
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
// 如果想让内层的方法也调用代理对象的方法, 就要实现 AppicationContextAware 获得工厂, 进而获得代理对象
UserService userService = (UserService) ctx.getBean("userService");
userService.login("suns", "123456");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}

7、AOP 阶段知识总结

- 前置通知(before) 在切点运行 "之前" 执行
- 后置通知(after-returning) 在切点正常运行结束 "之后" 执行
- 异常通知(after-throwing) 在切点 "发生异常" 的时候执行
- 最终通知(after) 在切点的 "最终" 执行
try {
前置通知(before)
// 切点执行的位置
后置通知(after-returning)
} catch (Exception e) {
异常通知(after-throwing)
} finally {
最终通知(after)
}

image

posted @   lidongdongdong~  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开