1.AOP概念
AOP (Aspect Oriented Programing)
⾯向切⾯编程 = Spring动态代理开发
以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
切⾯ = 切⼊点 + 额外功能
OOP (Object Oritened Programing)
⾯向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建
POP (Producer Oriented Programing)
⾯向过程(⽅法、函数)编程 C
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建
AOP的概念:
本质就是Spring的动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护
注意:AOP编程不可能取代OOP,是对OOP编程的补充。
2.AOP编程的开发步骤
- 原始对象
- 额外功能 (MethodInterceptor)
- 切⼊点
- 组装切⾯ (额外功能+切⼊点)
3.切面的名词解释
切⾯ = 切⼊点 + 额外功能
AOP底层实现原理
1.核心问题
-
AOP如何创建动态代理类(动态字节码技术)?(第二节解释它)
-
Spring⼯⼚如何加⼯创建代理对象?(第三节解释它)
通过原始对象的id值,获得的是代理对象
2.动态代理类的创建(第二节)
2.1 JDK的动态代理
类加载器(ClassLoader)
创建user对象的过程(分析User user = new User();过程):
步骤:
- 创建源文件User.java
- 编译成User.class文件 含有User类对应的字节码
- 创建对象和运行对象必须在JVM当中的,所以通过类加载器将字节码读取到jvm中,在jvm中先创建User类的Class对象(这个过程也是类加载器完成的),Class对象含有User类的所有信息,进而创建对象user。
图解如下:
动态代理类没有.java和.class文件,所以没有加载字节码的过程,通过动态字节码技术直接将字节码创建在jvm中。因为没有.class文件,所以jvm没有分配类加载器,没有类加载器就不能生成Class对象,也就不能创建代理类对象。
如何获得类加载器呢? 借用一个!
Proxy.newProxyInstance(classloader,interfaces,invocationhandler)借用的类加载器从第一个参数classloader传进去。
Proxy.newProxyInstance(classloader,interfaces,invocationhandler)详解如下图:
1. 第一个参数classloader:借用的类加载器
2. 第二个参数interfaces:通过反射获得原始对象的接口。(因为代理对象要和原始对象实现相同的接口。)
3. 第三个参数invocationhandler:通过反射实现额外功能。
//invoke :激活的意思 传递参数
//方法名.invoke(对象,方法的值); setName.invoke(user3,"豆豆");
2.2 JDK动态代理编码
public class TestJDKProxy {
public static void main(String[] args) {
//研究原理 就不用Spring框架创建对象了
// 1. 创建原始对象。
final UserService userService = new UserServiceImpl();
//2.JDK创建动态代理
//借用的类加载器可以是这里的TestJDKProxy的也可以是UserServiceImpl的
//InvocationHandler是个接口,接口不能new,这里通过内部类实现实现接口。实现接口的invoke方法
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("运行之前额外功能");
//原始方法运行
Object ret = method.invoke(userService, args);
return ret;
}
};
//动态代理类对象serServiceProxy
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
userServiceProxy.login();
userServiceProxy.register();
}
}
2.3 CGlib的动态代理
CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,
这样既可以保证两者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)方法;
jdk和CGlib区别如下图:
CGlib编码:
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) {
final UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
//借用类加载器
enhancer.setClassLoader(TestCglib.class.getClassLoader());
//继承父类UserService
enhancer.setSuperclass(userService.getClass());
//实现接口MethodInterceptor 通过匿名内部类 等同于InvocationHandler --- invoke
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
//实现额外功能
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("TestCglib.intercept 原始功能运行之前");
Object ret = method.invoke(userService, args);
return ret;
}
};
//setCallback()对应的是额外功能 通过参数interceptor传进来
enhancer.setCallback(interceptor);
//创建动态代理对象 代理类继承了原始类 所以可以用原始类创建代理对象
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("suns","111");
userServiceProxy.register(new User());
}
}
总结
- JDK动态代理
Proxy.newProxyInstance() 通过接⼝创建代理的实现类 - Cglib动态代理 Enhancer
通过继承⽗类创建的代理类
3.Spring⼯⼚如何加⼯原始对象(第三节)
思路分析:在BeanPostProcessor的postProcessAfterinitialization方法里创建代理类对象,返回给Spring工厂。(不做初始化操作。)
编码分析:
步骤一:java编码:
1.创建接口
public interface UserService {
public void login(String name,String password);
public void register(User user);
}
2.原始类
public class UserServiceImpl implements UserService{
public void login(String name,String password){
System.out.println("UserServiceImpl.login");
}
public void register(User user){
System.out.println("UserService.register");
}
}
3.动态代理类
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;//注意不能返回null 要把原始对象传到初始化再传给postProcessAfterInitialization
}
@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 {
//bean是第7行的第一个参数bean:原始对象
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.baizhiedu.factory.UserServiceImpl"/>
加工后的对象:
<bean id="proxyBeanPostProcessor" class="com.baizhiedu.factory.ProxyBeanPostProcessor"/>
步骤三:测试
虽然拿的是原始对象的id值,但是获得的是代理对象。(通过BeanPostProcessor对原始对象进行了再加工)
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext3.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("suns","111");
userService.register(new User());
}
}
本系列文章从Spring5原理开始深入浅出,从工厂特性=>依赖注入–IOC=>AOP编程=>Spring事务=>纯注解开发。本文来自观看B站孙帅Spring教程后做的笔记。持续更新…