AOP 编程
文章目录
1 AOP概念
AOP (Aspect Oriented Programming) 面向切面编程 Spring动态代理开发
# 以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
* 切面= 切入点+额外功能
OOP(Object Oriented Programming) 面向对象编程 Java
# 以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建.
UserService UserDao
POP(Producer Oriented Programming) 面向过程编程 C
# 以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建
AOP的概念:
本质就是SPring的动态代理开发,通过代理类为原始类增加功能.
好处:利于原始类的维护
注意:有了AOP编程不可能取代OOP,OOP编程有意补充
2 AOP编程的开发步骤
1. 原始对象
2. 额外功能(MethodInterceptor)
3. 切入点
4. 组装切面(额外功能+切入点)
3 切面的名词解释
切面 = 切入点 + 额外功能
几何学
点 0维
线 1维
面 2维
面 = 点+相同的性质
菅江晖个人理解
4 AOP底层实现原理
1.核心问题
1. AOP如何创建动态代理类(动态字节码技术)
2. Spring工厂如何加工创建代理对象
通过原始对象的id值获取的是代理对象
2 动态代理类的创建
2.1 JDK的动态代理
动态代理也有对应的实现
# 负责额外代码
InvocationHandler
作用:用于书写额外功能,额外功能可以运行原始方法 执行前,后 抛出异常
Object:原始方法的返回值
参数: Proxy 忽略掉 代表的是代理对象
Method:额外功能所增加给的原始方法
Object[]:原始方法的参数
# interfaces
原始对象 所实现的接口
# 类加载器的作用 ClassLoader
1. 通过类加载器把对应类的字节码文件加载JVM
2. 通过类加载器创建类的Class对象,进而黄健这个类的对象
User --->user
User 类UserClass对象 ---> new User() ---> user
如何获取类加载器:每一个类的.class文件 自动分配与之对应的ClassLoader
借用一个类加载器来创建代理类的class对象,进而可以创建代理对象
ClassLoader 第一个作用 加载字节码至JVM
ClassLoader 第二个作用 创建类的Class对象
如何获得类加载:虚拟机默认提供的
每一个类的.class文件 自动分配与之对应的ClassLoader
动态代理类没有源文件,他是通过**动态字节码**技术来创建字节码的
# Proxy.newProxyInstance(classloader,interfaces,invocationhandler)
此时在动态代理创建的过程中,需要Classloader创建的CLass对象,可是因为动态动态代理类没有对应.class文件,JvM也就不会为其分配ClassLoader,但是有需要?
所以newProxyInstance 第一个参数传入的classloader 用此对象来创建动态代理类的class对象
“孙哥nb”
编码
public class TestJDKProxy {
/**
* 借用类加载器 任意类加载器
*
* JDK8前
* final UserService userService = new UserServiceImpl();
* @param args
*/
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler handler = (proxy, method, args1) -> {
System.out.println("-----log-----");
Object invoke = method.invoke(userService, args1);
System.out.println("-----log-----");
return invoke;
};
Object o = Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), handler);
((UserService)o).register(new User());
}
}
2.2 CGlib的动态代理
* 与JDK 动态代理
共同点: 都创建的是动态代理类
JDK动态代理回顾
# CGlib:创建动态代理类原理:
父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证二者方法一致,同时也可以在代理类中提供新的实现(额外功能+原始功能)
代理类继承原始类
编码
public class RestCglib {
@Test
public void test(){
UserService userService = new UserService();
/*
* 2.通过cglib 方法创建动态代理对象
* Proxy.newProxyInstance(ClassLoader,interface,invocationHandler)
* Enhancer.setClassLoader() 对应 classLoader
* Enhancer.setSuperClass() 对应 interface 只不过jdk仅支持接口,而cglib可以支持类
* Enhancer.setCallback(); ---> MethodInterceptor(cglib)
* Enhancer.create() --->代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(Object.class.getClassLoader());
enhancer.setSuperclass(UserService.class);
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("--------log--------");
return methodProxy.invoke(userService,objects);
}
};
enhancer.setCallback(interceptor);
UserService userService1 =(UserService)enhancer.create();
userService1.registor(new User());
}
}
总结
1.JDK动态代理 Proxy.newProxyInstance() 通过接口代理的实现类
2.Cglib 动态代理 Enhancer 通过继承父类创建的代理类
5 Spring工厂如何加工创建代理对象
5.1 原理
Spring通过BeanPostProcessor完成对原始对象的加工
# 为什么通过原始id获取的是代理后的bean。原因是spring在BeanPostProcessor.postProcessorAfterinitialization()方法对传入进来的bean进行判断,符合代理条件时会通过动态代理将bean进行加工并返回至Spring工厂!
5.2 编码
java代码
//1.实现BeaPostProcessor 进行加工
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
InvocationHandler handler = (proxy,method,args) -> {
System.out.println("----------log----------");
Object invoke = method.invoke(bean, args);
System.out.println("----------log----------");
return invoke;
};
return Proxy.newProxyInstance(this.getClass().getClassLoader(), bean.getClass().getInterfaces(),handler );
}
}
配置文件配置
<!--2.配置文件中配置-->
<bean class = "com.leetao.factory.ProxyBeanPostProcessor"/>
5.3 简易aop
根据切入点表达式获取的 类&方法进行动态代理对象
package com.leetao.jdk;
import com.leetao.proxy.*;
import org.springframework.aop.framework.ProxyFactory;
import sun.reflect.generics.scope.MethodScope;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.*;
/**
* @author 菅江晖
* @date 2020/9/9 - 23:20
*/
public class TestJDKProxy {
/**
* 借用类加载器 任意类加载器
* 简易版AOP Method Filter
* <p>
* JDK8前
* final UserService userService = new UserServiceImpl();
*
* @param args
*/
public static void main(String[] args) {
Factory factory = new Factory();
UserService userService = factory.getInstance(UserServiceImpl.class.getCanonicalName());
userService.register(new User());
userService.login("1","2");
}
static class Factory {
Map<String, Object> singleton = new HashMap<>();
public <T> T getInstance(String name) {
return (T) singleton.get(name);
}
{
//加载至单例仓库
UserService userService = new UserServiceImpl();
OrderService orderService = new OrderServiceImpl();
singleton.put(userService.getClass().getCanonicalName(), userService);
singleton.put(orderService.getClass().getCanonicalName(), orderService);
//检查是否是需要代理
Aspect aspect = new Aspect();
Class[] classPool = aspect.getClassPool();
for (Class aClass : classPool) {
String canonicalName = aClass.getCanonicalName();
Object o = singleton.get(canonicalName);
if (o != null) {
singleton.put(canonicalName, aspect.newInstanceProxyObject(o));
}
}
}
}
static class Aspect {
Class[] classPool;
Method[] methodPool;
ProxyFactory factory;
{
classPool = new Class[]{UserServiceImpl.class};
methodPool = new Method[]{UserServiceImpl.class.getDeclaredMethods()[0]};
factory = new ProxyFactory();
}
public Object newInstanceProxyObject(Object temp) {
return factory.returnProxyObject(temp);
}
public Class[] getClassPool() {
return classPool;
}
public void setClassPool(Class[] classPool) {
this.classPool = classPool;
}
public Method[] getMethodPool() {
return methodPool;
}
public void setMethodPool(Method[] methodPool) {
this.methodPool = methodPool;
}
class ProxyFactory {
Object object;
List<Method> methods = Arrays.asList(methodPool);
public void setObject(Object object) {
this.object = object;
}
//额外代码
InvocationHandler handler = (proxy, method, args1) -> {
if (clickMethod(method)) {
System.out.println("log-begin");
Object invoke = method.invoke(object, args1);
System.out.println("log-end");
return invoke;
} else {
return method.invoke(object, args1);
}
};
private boolean clickMethod(Method m1) {
if (methods.contains(m1)) {
return true;
}
//用户传入方法
StringBuilder stringBuilder1 = new StringBuilder();
stringBuilder1.append(m1.getDeclaringClass().getCanonicalName()+"."+m1.getName()+"(");
Class<?>[] parameterTypes1 = m1.getParameterTypes();
for (Class<?> parameterType : parameterTypes1) {
stringBuilder1.append(parameterType.getCanonicalName()+",");
}
stringBuilder1.delete(stringBuilder1.length()-1,stringBuilder1.length());
stringBuilder1.append(")");
//切入点方法
for (Method method : methods) {
Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
for (Class<?> anInterface : interfaces) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(anInterface.getCanonicalName()+"."+method.getName()+"(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
stringBuilder.append(parameterType.getCanonicalName()+",");
}
stringBuilder.delete(stringBuilder.length()-1,stringBuilder.length());
stringBuilder.append(")");
//命中
if(stringBuilder.toString().equals(stringBuilder1.toString())){
return true;
}
}
}
return false;
}
public synchronized Object returnProxyObject(Object object) {
setObject(object);
return Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), object.getClass().getInterfaces(), handler);
}
}
}
}
6 基于注解的AOP编程
6.1 基于注解的AOP编程的开发步骤
-
原始对象
class UserServiceImpl{ ... }
-
额外功能
-
切入点
-
组装切面
//通过切面类 定义了 额外功能 @Around // 定义了 切入点 @Around("execution(* *(..))") // @Aspect 切面类 /** * 切面类 */ @Aspect public class MyAspect { @Around("execution(* com.leetao.aspect..*.*(..))") public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("----------log-----------"); Object proceed = joinPoint.proceed(); System.out.println("----------log-----------"); return proceed; } }
<bean id = "userService" class="com.leetao.aspect.UserServiceImpl"/> <!-- 切面: 1.额外功能 2.切入点 3.组装切面 --> <bean id = "around" class="com.leetao.aspect.MyAspect" /> <!--告诉Spring基于注解进行AOP编程--> <aop:aspectj-autoproxy/>
6.2 细节
- 切入点复用
切入点复用:在切面类中定义一个函数 上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用
@Pointcut(value = "execution(* com.leetao.aspect..*.*(..))")
public void myPointCut(){}
@Around("myPointCut()")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----------log-----------");
Object proceed = joinPoint.proceed();
System.out.println("----------log-----------");
return proceed;
}
@Around("myPointCut()")
public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----------log-----------");
Object proceed = joinPoint.proceed();
System.out.println("----------log-----------");
return proceed;
}
-
动态代理的创建方式
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:config>
7 AOP开发的坑
坑:在同一个业务类中 进行业务方法间的互相调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。如果想要内层的方法也调用代理对象的方法,就要ApplicationContextAware 获取工厂,进而获取代理对象 public class UserServiceImpl implements UserService , ApplicationContextAware { private ApplicationContext ctx; @Override @Log public void register(User user) { System.out.println("UserServiceImpl.register 业务运算+DAO"); } @Override public Boolean login(String name, String password) { System.out.println("UserServiceImpl.login "); //调用的是原始对象的login方法 -> 核心功能 //设计目的:代理对象的login方法 -> 额外功能 +核心功能 UserService userService = ctx.getBean(UserService.class); userService.register(new User()); return true; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.ctx = applicationContext; } } 自己的话: 动态代理 代理对象中有目标对象。 目标对象内的代码互相调用并不会影响到代理对象中的代码。 代理方法只是在原有方法的基础上拓展。原有方法中的调用跟代理对象无关