Spring框架-AOP详细学习[转载]

参考博客:http://www.javashuo.com/article/p-hqdlakuf-ct.htmlspring

--------------------- 
做者:huang-yang 
来源:CSDN 
原文:https://blog.csdn.net/qq_22583741/article/details/79589910 

express

这个大佬写的太厉害了, 索性直接转了编程

 

一. AOP概念缓存

   AOP(Aspect Oriented Programming , 面向切面编程), 经过预编译和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续,是Spring框架的重要内容, 是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。安全

  • AOP采起横向抽取机制,取代了传统纵向继承体系重复性代码
  • 经典应用:事务管理、性能监视、安全检查、缓存 、日志等
  • Spring AOP使用纯Java实现,不须要专门的编译过程和类加载器,在运行期经过代理方式向目标类织入加强代码
  • AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

 

二. AOP实现原理:app

  • aop底层将采用代理机制进行实现。
  • 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
  • 实现类:spring 采用 cglib字节码加强。

 

三. AOP术语 [重点掌握]框架

1.target(目标类):      须要被代理的类。例如:UserService
2.Joinpoint(链接点):    所谓链接点是指那些可能被拦截到的方法。例如:全部的方法
3.PointCut 切入点:   已经被加强的链接点。例如:addUser()
4.advice 通知/加强:     加强代码。例如:after、before
5. Weaving(织入):       是指把加强advice应用到目标对象target来建立新的代理对象proxy的过程.
6.proxy (代理类) 
7. Aspect(切面):          是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成一个特殊的面。 ide

也就是说,咱们最终会得到的, 是advice 和 pointcut 结合起来的proxy代理类
函数式编程

 

 

四. AOP 实现方式函数

             AOP实现方式包括  手动模式,  半自动模式,  全自动模式

    4.1.手动模式: 

         4.1.1JDK动态代理

 (1)目标类:  接口+实现类

public interface ProductService {
    public void addProduct();
    public void updateProduct();
    public void deleteProduct();
}

public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct() {
        System.out.println("add Product");
    }

    @Override
    public void updateProduct() {
        System.out.println("update Product");
    }

    @Override
    public void deleteProduct() {
        System.out.println("delete Product");
    }
}

(2)切面类:  用于实现通知/加强

public class LoggerAspect {
    public void before(){
        System.out.println("在添加商品前作些什么");
    }
    public void after(){
        System.out.println("在添加商品后作些什么");
    }
}

(3)工厂类:  编写工厂生成代理

public class ProductLogFactory {

    public static ProductService createService(){
        //目标类
        final ProductService productService = new ProductServiceImpl();
        //切面类
        final LoggerAspect loggerAspect = new LoggerAspect();

        //代理类
        ProductService proxyService = (ProductService) Proxy.newProxyInstance(
                LoggerAspect.class.getClassLoader(),  //参数1
                productService.getClass().getInterfaces(),  //参数2
                new InvocationHandler() {                  //参数3
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        //前执行
                        loggerAspect.before();

                        //执行目标类方法
                        Object obj = method.invoke(productService,args);

                        //后执行
                        loggerAspect.after();

                        return obj;
                    }
                }
        );
        return proxyService;      //返回建立的代理类对象
    }
}

参数1:loader ,类加载器,动态代理类 运行时建立,任何类都须要类加载器将其加载到内存。
* 通常状况:当前类.class.getClassLoader();
* 目标类实例.getClass().get...
---------------------

参数2:Class[] interfaces 代理类须要实现的全部接口
* 方式1:目标类实例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口
* 方式2:new Class[]{UserService.class}
* 例如:jdbc 驱动 --> DriverManager 得到接口 Connection
---------------------

参数3:InvocationHandler 处理类,接口,必须进行实现类,通常采用匿名内部
* 提供 invoke 方法,代理类的每个方法执行时,都将调用一次invoke
* 参数31:Object proxy :代理对象
* 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
* 执行方法名:method.getName()
* 执行方法:method.invoke(对象,实际参数)
* 参数33:Object[] args :方法实际参数

 

(4)测试:

使用被织入了切面方法的代理类的方法

    @Test
    public void test3(){
        ProductService ps = ProductLogFactory.createService();
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

 

          4.1.1CGLIB字节码加强

  • 没有接口,只有实现类。
  • 采用字节码加强框架 cglib,在运行时 建立目标类的子类,从而对目标类进行加强。
public class ProductLogFactory1 {

    public static ProductServiceImpl createService(){
        //目标类
        final ProductServiceImpl productService = new ProductServiceImpl();
        //切面类
        final LoggerAspect loggerAspect = new LoggerAspect();
        //代理类, 采用cglib, 底层建立目标类的子类
        //核心类
        Enhancer enhancer = new Enhancer();
        //设置父类为目标类
        enhancer.setSuperclass(productService.getClass());
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                //前执行
                loggerAspect.before();

                //执行目标类的方法
                Object obj = method.invoke(productService,args);
                //执行代理类的父类  目标类是代理类的父类
                methodProxy.invokeSuper(proxy,args);

                //后执行
                loggerAspect.after();

                return obj;
            }
        });
        //建立代理
        ProductServiceImpl proxyService = (ProductServiceImpl)enhancer.create();
        
        return proxyService;
    }
}

  用法和上面相似,得到代理类对象后, 使用其中已经织入切面类方法的方法

 

    4.2.半自动方式:    让Spring建立代理对象, 从Spring容器中手动得到代理对象

(1)目标类:  接口+实现类

public interface ProductService {
    public void addProduct();
    public void updateProduct();
    public void deleteProduct();
}

public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct() {
        System.out.println("add Product");
    }

    @Override
    public void updateProduct() {
        System.out.println("update Product");
    }

    @Override
    public void deleteProduct() {
        System.out.println("delete Product");
    }
}

(2)切面类:  与前面的切面类不一样, 这个切面类实现了MethodInterceptor接口, 环绕通知

public class LoggerAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        //前执行
        System.out.println("在操做商品前作些什么");
        
        //手动执行目标方法
        Object obj = methodInvocation.proceed();
        
        //后执行
        System.out.println("在操做商品后作些什么");
        return obj;
    }
}

(3) Spring配置:  要在applicationContext.xml文件中声明目标类,切面类  并让Spring帮忙生成代理类

    <!--目标类 -->
    <bean id="productServiceId" class="service.ProductServiceImpl"></bean>

    <!-- 切面类-->
    <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

   <!-- 代理类 -->
    <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="service.ProductService"></property>
        <property name="target" ref="productServiceId"></property>
        <property name="interceptorNames" value="loggerAspectId"></property>
    </bean>

 

* 经过使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean
* ProxyFactoryBean 用于建立代理工厂bean,生成特殊代理对象
                          参数1: interfaces : 肯定接口们
                                                        经过<array>能够设置多个值
                                                        只有一个值时,value=""
                          参数2: target : 肯定目标类
                          参数3:  interceptorNames : 通知 切面类的名称,类型String[],若是设置一个值 value=""
                          可选参数4:  optimize :强制使用cglib
                                             <property name="optimize" value="true"></property>
底层机制
若是目标类有接口,采用jdk动态代理
若是没有接口,采用cglib 字节码加强
若是声明 optimize = true ,不管是否有接口,都采用cglib

 

(4)测试

    @Test
    public void test3(){
        String xmlPath = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
        ProductService ps = (ProductService)context.getBean("proxyServiceId");
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

 

     4.3.全自动方式:   从Spring容器得到目标类, 经过配置aop, 让Spring自动生成代理

(1)目标类: 接口+实现类 这里就不放代码了

(2)切面类:  与半自动方式类似

(3)xml配置:

    <aop:config>
        <aop:pointcut id="productPointCut" expression="execution(* service.ProductServiceImpl.*(..))"/>
        <aop:advisor advice-ref="loggerAspectId" pointcut-ref="productPointCut"/>
    </aop:config>

aop编程 :
             3.1 导入命名空间
             3.2 使用 <aop:config>进行配置
                                    proxy-target-class="true" 声明时使用cglib代理
                                    <aop:pointcut> 切入点 ,从目标对象得到具体方法
                                    <aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
                                     advice-ref 通知引用
                                     pointcut-ref 切入点引用
             3.3 切入点表达式
                                      execution(* com.itheima.c_spring_aop.*.*(..))
                                     选择方法 返回值任意 包 类名任意 方法名任意 参数任意

(4)测试:

    @Test
    public void test3(){
        String xmlPath = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
        ProductService ps = (ProductService)context.getBean("productServiceId");
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

注意这里,再也不是获取由Spring容器建立的代理类了, 而是直接获取ProductService的bean, 生成代理的定义几乎彻底不可视了

 

4.4. 另外一种全自动方式: 

(1)目标类: 接口+实现类

(2)切面类:  切面类再也不实现某个接口, 将获取切入点的操做交给了Spring

public class LoggerAspect {

    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {

        //前执行
        System.out.println("在操做商品前作些什么");

        //手动执行目标方法
        Object obj = joinPoint.proceed();

        //后执行
        System.out.println("在操做商品后作些什么");
        return obj;
    }
}

(3)xml配置:  指定切面类的bean, 指定切入点是ProductService的全部方法, 指定切面方法为log

    <!-- Aspect -->
    <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

    <!-- aop -->
    <aop:config>
        <aop:pointcut id="loggerCutpoint"
                      expression="execution(* service.ProductService.*(..))"/>
        <aop:aspect id="logAspect" ref="loggerAspectId">
            <aop:around pointcut-ref="loggerCutpoint" method="log"/>
        </aop:aspect>
    </aop:config>
相关文章
相关标签/搜索