Spring--->aop面向切面编程

AOP编程

1. AOP概念

 AOP (Aspect Oriented Programing) 面向切面编程 = Spring动态代理开发
 以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
 切面 = 切入点 + 额外功能
OOP (Object Oriented Programing) 面向对象编程 Java 以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建 POP (Procedure Oriented Programing) 面向过程(方法、函数)编程 C 以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建

    aop的本质就是spring的动态代理开发

    想要实现动态代理需要满足三个条件:

  1. 目标对象
  2. 接口 代理对象和目标对象的相同接口
  3. 增强功能

AOP的底层实现原理

1. 核心问题

  • AOP如何创建动态代理类?(动态字节码技术)

  • Spring工厂如何加工创建代理对象?通过原始对象的id值,获得的是代理对象。

2. 动态代理类的创建

2.1 JDK的动态代理

       TestJDKProxy.java

package proxy.jdk;


import proxy.service.UserService;
import proxy.service.impl.UserServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class TestJDKProxy { public static void main(String[] args) { //1.创建原始对象 //注意:由于后面匿名子类的方法中用到了userService,所以应该用final修饰 // 而JDK1.8以后默认加了final,不需要手动加 UserService userService = new UserServiceImpl(); //2.JDK创建代理对象 InvocationHandler handler = new InvocationHandler() {
/**
*proxy:代理对象
*method:额外功能要增加的原始方法
*args:原始方法的参数
*
**/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----------- JDKProxy log -----------");\ //目标方法运行: Object ret = method.invoke(userService, args); return ret; } };
                /**
*ClassLoader:类加载对象
*inerface:实现的接口(相同的接口)
*handler:额外增加的功能
*
**/
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(),handler); 
userServiceProxy.login("海绵宝宝", "1111"); userServiceProxy.register(new User());
}
}

 

在InvocationHandler中invoke的方法中增加额外功能,程序在执行Proxy.newProxyInstance的过程中就是动态字节码加载的过程(没有代理类,这个过程就是通过动态字节码技术在jvm内存中创建代理类,再通过ClassLoad类加载,继而创建代理类对象),借用的类加载器,借谁的都可以


 

2.2 CGlib动态代理

 原理:

1.目标类

StudentService

package proxy.cglib;

import proxy.service.User;


public class StudentServiceImpl {
    public boolean login(String name,String password){
        System.out.println("StudentService.login");
        return true;
    }

    public void register(User user) {
        System.out.println("StudentService.register");
    }
}

 

2.测试类  Enhancer java中已经提供了 

package com.yuziyan.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 TestCGlibProxy {
    public static void main(String[] args) {
        //1.创建原始对象
        UserServiceImpl userService = new UserServiceImpl();

        //2.通过CGlib创建代理对象
        //  2.1 创建Enhancer
        Enhancer enhancer = new Enhancer();
        //  2.2 设置借用类加载器
        enhancer.setClassLoader(TestCGlibProxy.class.getClassLoader());
        //  2.3 设置父类(目标类)
        enhancer.setSuperclass(userService.getClass());
        //  2.4 设置回调,额外功能写在里面
        enhancer.setCallback(new MethodInterceptor() {
            //相当于 InvocationHandler --> invoke()
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //额外功能:
                System.out.println("========= CGlibProxy log ========");
                //目标方法执行:
                Object ret = method.invoke(userService, objects);
                return ret;
            }
        });
        //  2.5 通过Enhancer对象创建代理
        UserServiceImpl service = (UserServiceImpl) enhancer.create();

        //测试:
        service.register();
        service.login();

    }
}

 

jdk和cglib的本质不同

     1. JDK动态代理   Proxy.newProxyInstance()  通过目标类实现的接口创建代理类 

     2. Cglib动态代理 Enhancer                  通过继承目标类创建代理类 

 


 

3. Spring工厂如何返回代理对象

  • 思路分析:
  •  

编码模拟:

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 invocation = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----------- 模拟Spring返回代理对象的方式 log -----------");

                Object ret = method.invoke(bean, args);

                return ret;
            }
        };

        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invocation);
    }
}

 

配置文件

<!-- 1.配置原始对象 -->
<bean id="userService" class="com.yuziyan.factory.UserServiceImpl"></bean>
<!-- 2.配置自己模拟的ProxyBeanPostProcessor -->
<bean id="proxyBeanPostProcessor" class="com.yuziyan.factory.ProxyBeanPostProcessor"/>

 

为什么从spring容器中拿原始id的值,获取的是代理对象

 因为是通过BeanPostProcessor方式拿到的


 

基于注解的AOP编程

1. 开发步骤:

  1. 原始对象

  2. 额外功能

  3. 切入点

  4. 组装切面

/**
 * 声明切面类     @Aspect
 * 定义额外功能   @Around
 * 定义切入点     @Around("execution(* login(..))")
 *
 */
@Aspect
public class MyAspect {

    @Around("execution(* login(..))")//组装了切入点和额外功能
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //额外功能:
        System.out.println("--------- 基于注解的AOP编程 log --------");
        //原始方法执行:
        Object ret = joinPoint.proceed();

        return ret;
    }
}

 

配置文件

<!-- 原始对象 -->
<bean id="userService" class="com.yuziyan.aspect.UserServiceImpl"></bean>

<!-- 切面 -->
<bean id="myAspect" class="com.yuziyan.aspect.MyAspect"/>

<!-- 开启基于注解的AOP编程 -->
<aop:aspectj-autoproxy/>

 

2. 细节分析:

  • 切入点复用:

@Aspect
public class MyAspect {

    /**
     * 切入点复用:定义一个函数,加上@Pointcut注解,通过该注解的value定义切入点表达式,以后可以复用。
     */
    @Pointcut("execution(* login(..))")
    public void myPointcut(){}

    @Around("myPointcut()")//组装了切入点和额外功能
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //额外功能:
        System.out.println("--------- 基于注解的AOP编程 log --------");
        //原始方法执行:
        Object ret = joinPoint.proceed();

        return ret;
    }


    @Around("myPointcut()")
    public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
        //额外功能:
        System.out.println("--------- 基于注解的AOP编程 tx --------");
        //原始方法执行:
        Object ret = joinPoint.proceed();

        return ret;
    }

}

 

动态代理的创建方式:

AOP底层实现  2种代理创建方式
  1.  JDK   通过实现接口,创建代理对象
  2.  Cglib 通过继承目标类,创建代理对象
  
  默认情况 AOP编程 底层应用JDK动态代理创建方式 
  如果要切换Cglib
       1. 基于注解AOP开发
          <aop:aspectj-autoproxy proxy-target-class="true" />
       2. 传统的AOP开发
          <aop:config proxy-target-class="true">
          </aop>

 

 

AOP开发中的一个坑

坑:在同一个业务类中,业务方法间相互调用时,只有最外层的方法,加入了额外功能(内部的方法,通过普通的方式调用,运行的都是原始方法)。如果想让内层的方法也调用代理对象的方法,就要实现AppicationContextAware获得工厂,进而获得代理对象。

一个调用的是原始对象,一个是代理对象,spring的aop只会对代理对象有效

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 业务运算 + DAO ");
        //throw new RuntimeException("测试异常");

        //调用的是原始对象的login方法 ---> 核心功能
        /*
            设计目的:代理对象的login方法 --->  额外功能+核心功能
            ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
            UserService userService = (UserService) ctx.getBean("userService");
            userService.login();

            Spring工厂重量级资源 一个应用中 应该只创建一个工厂
         */

        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;
    }
}

 

AOP阶段知识总结

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章