Spring AOP 编程 中级

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维
	面 = 点+相同的性质

image-20200909202628195

菅江晖个人理解

AOP

4 AOP底层实现原理

1.核心问题

1. AOP如何创建动态代理类(动态字节码技术)
2. Spring工厂如何加工创建代理对象 
通过原始对象的id值获取的是代理对象

2 动态代理类的创建

2.1 JDK的动态代理

image-20200909224035307

image-20200910001049300

动态代理也有对应的实现

# 负责额外代码
InvocationHandler 
	作用:用于书写额外功能,额外功能可以运行原始方法 执行前,后 抛出异常 
	Object:原始方法的返回值
	参数: Proxy 忽略掉 代表的是代理对象
		 Method:额外功能所增加给的原始方法
		 Object[]:原始方法的参数

image-20200910002007646

# interfaces 
 原始对象 所实现的接口

image-20200910004051185

image-20200910151003184

# 类加载器的作用 ClassLoader 
1. 通过类加载器把对应类的字节码文件加载JVM
2. 通过类加载器创建类的Class对象,进而黄健这个类的对象
 User --->user
 User 类UserClass对象 ---> new User() ---> user
 如何获取类加载器:每一个类的.class文件 自动分配与之对应的ClassLoader
 
 借用一个类加载器来创建代理类的class对象,进而可以创建代理对象

​ ClassLoader 第一个作用 加载字节码至JVM

image-20200910005138861

​ ClassLoader 第二个作用 创建类的Class对象

image-20200910005426933

如何获得类加载:虚拟机默认提供的
	每一个类的.class文件 自动分配与之对应的ClassLoader

image-20200910085824111

动态代理类没有源文件,他是通过**动态字节码**技术来创建字节码的 

# Proxy.newProxyInstance(classloader,interfaces,invocationhandler)

此时在动态代理创建的过程中,需要Classloader创建的CLass对象,可是因为动态动态代理类没有对应.class文件,JvM也就不会为其分配ClassLoader,但是有需要?
所以newProxyInstance 第一个参数传入的classloader 用此对象来创建动态代理类的class对象

image-20200910153927599

image-20200910153954884

“孙哥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动态代理回顾

image-20200910161855049

# CGlib:创建动态代理类原理:
	父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证二者方法一致,同时也可以在代理类中提供新的实现(额外功能+原始功能)
代理类继承原始类

image-20200910163555588

编码
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完成对原始对象的加工

image-20200910214205247

# 为什么通过原始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 细节

  1. 切入点复用
切入点复用:在切面类中定义一个函数 上面@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;
}
  1. 动态代理的创建方式

    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;
 }
}
	   
	   			  
自己的话:		   
动态代理 代理对象中有目标对象。 目标对象内的代码互相调用并不会影响到代理对象中的代码。 
代理方法只是在原有方法的基础上拓展。原有方法中的调用跟代理对象无关
 
 

8 AOP阶段知识总结

image-20200914151209206

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值