zoukankan      html  css  js  c++  java
  • Spring框架深入(二)--AOP面向切面

    一、AOP概述

      1、AOP:面向切面编程,实现在不增加代码的基础上,增加一些新的功能(公共功能);

      2、AOP并不是Spring框架持有的,Spring只是支持AOP编程的框架之一,可以整合第三方框架来实现面向切面编程(如:Aspect);

      3、现实的应用场景:使用面向切面编程,AOP框架已经实现了面向切面的很多内容;

      4、程序员使用AOP要做的事情:

        编写公共功能,切面;

        基于AOP框架的配置,直接把核心业务和切面关联起来;

      5、Spring中实现AOP的方式有三种:

        基于AspectJ注解的方式实现

        基于schema的XML配置

        基于ProxyFactoryBean代理实现

    二、AOP的简单实现_基于AspectJ注解

      编写步骤如下:

      1、创建一个简单的工程,引入jar包:

      

      2、编写核心业务的接口和实现类

    public interface ServiceInterface {
        public void sayHello();
    
        public void sayBye();
    }
    /*
     * 核心关注点,核心业务
     * */
    @Component("service")
    public class ServiceImpl implements ServiceInterface {
        @Override
        public void sayHello() {
            // TODO Auto-generated method stub
            System.out.println("say Hello");
        }
        public void sayBye() {
            System.out.println("say Bye");
        }
    }

      3、编写一个切面类:我们就简单的做一个打印输出

    /*
     * 公共功能:切面
     * */
    @Component
    @Aspect
    public class AdviceMethod {
        //前置通知
        //将这个方法织入核心业务:
        //@Before,表示在核心业务之前织入;
        //execution,表示织入的位置在哪儿
        @Before("point()")
        public void befor() {
            System.out.println("日志");
        }
    }

      4、在核心配置文件中声明这个类为切面

    <!-- 启用注解 -->
    <context:annotation-config></context:annotation-config>
    <!-- 扫描 -->
    <context:component-scan base-package="service,advice"></context:component-scan>
    <!-- 启用AOP aspectj 的配置 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

      5、最后新建一个测试类

    public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //要使用接口来声明核心业务类
        //service就是一个代理对象
        ServiceInterface service = (ServiceInterface) ac.getBean("service");
        //sayHello代码不需要改变,会自动增加日志的功能
        service.sayHello();
        service.sayBye();
    }

        注:这儿必须要用接口来声明核心业务类

      这样,一个简单的AOP案例就搭建完成了;

    三、AOP详解

      利用一种横切的技术,剖析封装的对象的内部,将影响了多个累的行为封装到了一个可重用的模块中,成为Aspect,切面;

      将应用程序中的商业逻辑与其提供支持的通用服务进行分离;

      1、AOP中的常用术语

        (1)切面:公共功能、交叉功能的描述

        (2)通知:实现切面功能的类

        (3)目标对象:被通知的对象,核心关注点的对象

        (4)代理对象:代理的是目标对象,通过代理目标对象就增加了切面功能

          目标对象+切面功能

          实现切面功能最早使用的是代理对象

        (5)连接点:静态概念,代表通知执行的地方

        (6)切入点:动态概念,运行时执行通知的地方,实现切面功能是,连接点就变为切入点;

        (7)引入:静态概念,将切面与目标对象关联起来

        (8)织入:将切面应用到代理对象,是一个过程

      2、通知类型

        前置通知:核心业务执行前执行

        返回后通知:核心业务返回后执行

        异常通知:核心业务发生异常时执行

        后置通知:核心业务执行后执行

        环绕通知:核心任务执行的时候执行

      代码编写如下:使用注解的方式;

    @Before("execution(* service.*.*.*())")
    public void befor() {
        System.out.println("前置通知,日志");
    }
    @After("execution(* service.*.*.*())")
    public void after() {
        System.out.println("后置通知,执行完成");
    }
    @AfterReturning("execution(* service.*.*.*())")
    public void afterReturn() {
        System.out.println("返回后通知,得到返回值后");
    }    
    @AfterThrowing("execution(* service.*.*.*())")
    public void throwExp() {
        System.out.println("异常通知,异常执行时");
    }    
    @Around("execution(* service.*.*.*())")
    public Object round(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前。。。");
        Object o=pjp.proceed();//核心业务的正常执行
        System.out.println("环绕后。。。");
        return o;
    }

        执行结果:

        

      3、AspectJ中使用的注解

      (1)、AspectJ是一个面向切面的框架;

      (2)、Spring通过AspectJ实现了一注解的方式定义AOP相关的配置,减少了对于配置文件的依赖

      (3)、AspectJ提供的注解:

        @AspectJ:在类上使用,声明该类为一个切面;

        @Before:在方法上使用,声明该方法的通知类型;

        @After:在方法上使用,声明该方法的通知类型;

        @AfterReturn:在方法上使用,声明该方法的通知类型;

        @AfterThrowing:在方法上使用,声明该方法的通知类型;

        @Around:在方法上使用,声明该方法的通知类型;

        @Pointcut:声明切入点;

    @Pointcut("execution(* service.*.*.*())")
    public void point() {
    }
    @Before("point()")
    public void befor() {
        System.out.println("前置通知,日志");
    }

      4、execution表达式的使用

        AOP配置时,不管是XML配置,注解配置,用于定义pointcut切入点

        execution(* service.*.*.*(..))

        

      5、AOP案例_基于schema

        通过核心配置文件来实现AOP切面类

    <!-- AspectJ所提供的基于AOP的配置项 -->
    <aop:config>
        <aop:pointcut expression="execution(* service.*.*.*(..))" id="pointCut"/>
        <aop:aspect id="myAspect" ref="adviceMethod_xml">
            <aop:before method="before" pointcut-ref="pointCut"/>
            <aop:after method="after" pointcut-ref="pointCut"/>
            <aop:after-returning method="afterReturn" pointcut-ref="pointCut"/>
            <aop:after-throwing method="throwExp" pointcut-ref="pointCut"/>
            <aop:around method="round" pointcut-ref="pointCut"/>
        </aop:aspect>
    </aop:config>

        其实现效果和基于注解一样;

    四、基于代理实现AOP

      1、Spring的AOP包中提供了实现AOP的各种类

      2、Spring上下文就可以有做AOP的实例,Spring直接基于代理实现AOP

      3、Spring的代理有两种方式

        (1)、JDK动态代理:代理的是接口,目标对象是有接口的

        (2)、CGLIB:代理的就是目标对象本身

      附上从参考文档拿过来的图

      

      

        如果没有代理的时候,就会直接调用pojo.foo();

        如果pojo对象生成了一个与之对应的代理对象,执行的时候,就会调用:代理对象.foo();

      

      4、我们这儿来看一下基于接口代理实现AOP

        (1)、先写一个要代理的接口

    /*
     * 要代理的接口
     * */
    public interface Subject {
        public void doSomething(String role) ; 
    }

        (2)、书写目标对象,实现这个接口

    /*
     * 目标对象
     * */
    @Component
    public class RealSubject implements Subject {
        @Override
        public void doSomething(String role) {
            // TODO Auto-generated method stub
            System.out.println("doSomething...");
        }
    }

        (3)、书写一个通知类,这个类需要实现类通知的接口。拦截下目标对象

    /*
     * 通知类:实现类通知的接口,该类在Spring的上下文中能够被识别为通知
     * */
    @Component
    public class Advice implements MethodBeforeAdvice {
        /*
         * 拦截:做权限验证
         * arg0:被调用的方法
         * arg1:给这个方法传递的参数
         * arg2:被代理的对象
         * */
        @Override
        public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
            // TODO Auto-generated method stub
            if("admin".equals(arg1[0])) {
                System.out.println("通过验证");
            }else {
                System.out.println("没有通过");
            }
        }
    }

        (4)、配置代理对象,也是一个bean,在bean中配置要代理的接口,要实现的通知以及代理的目标对象;

    <!-- 
        已有了目标对象realsubject,已有了通知advice
        生成代理对象:代理realSubject,具备advice的功能
        Spring提供了一个类能够帮助我们生成代理对象
     -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 要代理的接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>proxyAOP.Subject</value>
            </list>
        </property>
        <!-- 要实现的通知 -->
        <property name="interceptorNames">
            <list>
                <value>advice</value>
            </list>
        </property>
        <!-- 代理的目标对象 -->
        <property name="target" ref="realSubject"></property>
    </bean>

        (5)、书写一个测试类。注意,测试类里得到的bean是上面代理的bean;

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("proxyAOP/applicationContext.xml");
        //基于接口
        Subject subject=(Subject) ac.getBean("proxyFactoryBean");
        subject.doSomething("admin");
    }

      这样,一个简单的基于接口代理实现AOP就完成了

    PS:因作者能力有限,如有误还请谅解

  • 相关阅读:
    Leetcode练习(Python):链表类:第206题:反转链表:反转一个单链表。
    Leetcode练习(Python):链表类:第203题:移除链表元素:删除链表中等于给定值 val 的所有节点。
    Leetcode练习(Python):链表类:第160题:相交链表:编写一个程序,找到两个单链表相交的起始节点。
    Leetcode练习(Python):链表类:第141题:环形链表:给定一个链表,判断链表中是否有环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
    Leetcode练习(Python):链表类:第83题:删除排序链表中的重复元素:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
    【Java基础总结】数据库编程
    【Java基础总结】多线程
    特迷茫的大三时期
    解决忘记了开机密码,无法进入系统的方法
    一开机未通过输入密码登录,就出现用户名或密码错误??
  • 原文地址:https://www.cnblogs.com/WHL5/p/9062726.html
Copyright © 2011-2022 走看看