Spring5学习随笔-AOP底层实现(JDK、CGlib)、实现切面(@Aspect)
学习视频:【孙哥说Spring5:从设计模式到基本应用到应用级底层分析,一次深入浅出的Spring全探索。学不会Spring?只因你未遇见孙哥】
第四章、AOP编程
1.AOP概念
AOP(Aspect Oriented Programing)面向切面编程 = Spring动态代理开发
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
切面 = 切入点 + 额外功能
OOP(Object Oriented Programing)面向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建
POP(Producer Oriented Programing)面向过程(方法、函数)编程 C
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建
AOP的概念:本质就是Srping的动态代理开发,通过代理类为原始类增加额外功能。好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。
2.AOP编程的开发步骤
- 原始对象
- 额外功能(MethodInterceptor)
- 切入点
- 组装切面(额外功能+切入点)
3.切面的名词解释
切面 = 切入点 + 额外功能
从几何学出发
面 = 点 + 相同的性质
第五章、AOP的底层实现原理
1.核心问题
-
AOP如何创建动态代理类(动态字节码技术)
-
Spring工厂如何加工创建代理对象
通过原始对象的id值,获得的是代理对象
2.动态代理类的创建
2.1 JDK的动态代理
- Proxy.newProxyInstance方法参数详解
public class TestJDKProxy {
public static void main(String[] args) {
//1 创建原始对象
UserService userService = new UserServiceImpl();
//2 JDK创建动态代理
/*
参数详解:
ClassLoader:借用类加载器,哪个都行,TestJDKProxy UserServiceImpl....
interfaces:原始对象所实现的接口
handler:实现invoke方法(额外方法加到原始方法中)
注意.JDK8.x前
创建原始对象要加 final
*/
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----proxy log-------");
// 1.原始方法运行 原始对象和参数
Object ret = method.invoke(userService, args);
return ret;
}
};
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
userServiceProxy.login("suns", "123456");
userServiceProxy.register(new User());
}
}
2.2 CGlib的动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)
package com.baizhi.cglib;
import com.baizhi.proxy.User;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TestCglib {
public static void main(String[] args) {
// 1.创建原始对象
UserService userService = new UserService();
/*
2.通过cglib方式创建动态代理对象
JDK:Proxy.newProxyInstance(classloader,interface,invocationhandler)
Enhancer,setClassLoader()
Enhancer.setSuperClass()
Enhancer.setCallback(); --->MethodInterceptor(cglib)
Enhancer.create() ---> 代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
// 等同于 InvocationHandler ---- invoke
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("----cglib log-----");
Object ret = method.invoke(userService, args);
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("suns","12346");
userServiceProxy.register(new User());
}
}
- 总结(如果有接口就可以使用JDK动态代理,没有使用Cglib继承父类)
- JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类
- Cglib动态代理 Enhancer 通过继承父类创建的代理类
3.Spring工厂如何加工代理对象
- 思路分析
- 编码
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
/*
Proxy.newProxyInstance(); 基于接口创建代理
*/
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.baizhi.factory.UserServiceImpl"/>
<!--
1.实现BeanPostProcessor 进行加工
2.配置文件中对BeanPostProcessor进行配置
-->
<bean id="proxyBeanPostProcessor" class="com.baizhi.factory.ProxyBeanPostProcessor"/>
第六章、基于注解的AOP编程
1.基于注解的AOP编程的开发步骤
- 原始对象
- 额外功能
- 切入点
- 组装切面
最大的区别是在2、3、4步,因为是由切面类定义额外功能(@Around)
定义了切入点@Around(”execution( login(..)))”*
@Aspect
/*
构建切面所需的2个点
1.额外功能
public class MyArround implements MethodInterceptor{
public Object invoke(MethodInvocation invocation){
Object ret = invocation.proceed();
return ret;
}
}
2.切入点
<aop:config
<aop:pointcut id="" expression="execution(*login(..))"/>
*/
public class MyAspect {
@Around("execution(* login(..))") // 1. 切入点
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { // 2.额外功能 等同于 上面的 invoke
System.out.println("-----aspect log-------");
Object ret = joinPoint.proceed();
return ret;
}
}
<!-- 1. 原始对象-->
<bean id="userService" class="com.baizhi.aspect.UserServiceImpl"/>
<!--
切面:
1.额外功能
2.切入点
3.组装切面
-->
<!-- 2. 切面-->
<bean id="arround" class="com.baizhi.aspect.MyAspect"/>
<!-- 告知Spring基于注解进行AOP编程-->
<aop:aspectj-autoproxy/>
2.细节
2.1 切入点复用
在切面类中定义一个函数 上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用。
@Aspect
public class MyAspect {
@Pointcut("execution(* login(..))") // 复用切入点,解耦合
public void myPointcut(){}
@Around("myPointcut()") // 1. 切入点
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { // 2.额外功能 等同于 上面的 invoke
System.out.println("-----aspect log-------");
Object ret = joinPoint.proceed();
return ret;
}
@Around("myPointcut()") // 1. 切入点
public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable { // 2.额外功能 等同于 上面的 invoke
System.out.println("-----aspect tx-------");
Object ret = joinPoint.proceed();
return ret;
}
}
2.2动态代理的创建方式
AOP底层实现 2种代理创建方式
- JDK 通过实现接口 做新的实现类方式 创建代理对象
- Cglib通过继承父类 做新的子类 创建代理对象
默认情况 AOP编程 底层应用JDK动态代理创建方式
如果切换Cglib方式
1. 基于注解AOP开发
<aop:aspectj-autoproxy proxy-target-class="true"/>
2.传统的AOP开发
<aop:config proxy-target-class="true">
</aop>
第七章、AOP开发中的一个坑
坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过this的方式调用,获得的是原始对象),如果想让内层的this.方法也调用代理对象的方法,就需要实现ApplicationContextAware接口获取。
@Service
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Log
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 注册成功");
// throw new RuntimeException("测试异常");
/*
调用的是原始对象的login方法 ---》核心功能,没有额外功能
设计目的:代理对象的login方法 ---》 额外功能+核心功能
所以应该调取代理对象的login方法,要获得工厂,而工厂通过ApplicationContextAware获得
*/
UserService userService = (UserService) ctx.getBean("userService");
userService.login("suns", "123456");
}
@Override
public boolean login(String name, String password) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("UserServiceImpl.login登录成功");
return true;
}
}
第八章、AOP阶段知识总结
作者:扬眉剑出鞘
出处: https://www.cnblogs.com/eyewink/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。