解析Spring第三天(面向切面AOP)

Julywhj 2019-10-28

面向切面:AOP

在不修改源代码的基础上,对方法进行增强。AOP的底层原理就是代理技术(第一种:jdk的动态代理(编写程序必须要有接口)。第二种:cglib代理技术(生成类的子类)。如果编写的程序有借口,则spring框架会自动使用jdk的动态代理技术增强,)。

Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Target(目标对象)-- 代理的目标对象

Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程

Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面)-- 是切入点和通知的结合,以后自己来编写和配置的

 创建一个普通的Maven项目工程引入坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
  
        <!-- AOP联盟 -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- Spring Aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- aspectj -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
        </dependency>
  </dependencies>
  • 创建Spring的配置文件,引入具体的AOP的schema约束

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context.xsd
                    http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop.xsd">
        
    </beans>
  • 创建包结构,编写具体的接口和实现类
    package cn.tx.demo2;
    public class UserServiceImpl implements UserService {
    ?
        @Override
        public void save() {
            System.out.println("业务层:保存用户...");
        }
    ?
    }
  • 将目标类配置到Spring中
    <bean id="userService" class="cn.tx.demo2.UserServiceImpl"/>
  • 自定义切面类
    package cn.tx.demo2;
    ?
    /**
     * 自定义切面类 = 切入点(表达式) + 通知(增强的代码)
     */
    public class MyXmlAspect {
    ?
        /**
         * 通知
         */
        public void log(){
            // 发送手机短信
            // 发送邮件/记录日志/事务管理
    ?
            System.out.println("增强的方法执行了...");
        }
    ?
    }
  • 在配置文件中定义切面类

    1 <bean id="myXmlAspect" class="cn.tx.demo2.MyXmlAspect"/>
  • 在配置文件中完成aop的配置

    <!--配置AOP的增强-->
        <aop:config>
            <!--配置切面 = 切入点 + 通知组成-->
            <aop:aspect ref="myXmlAspect">
                <!--前置通知:UserServiceImpl的save方法执行前,会增强-->
                <aop:before method="log" pointcut="execution(public void cn.tx.demo2.UserServiceImpl.save())" />
            </aop:aspect>
        </aop:config>
  • 对增强进行测试
    package cn.tx.test;
    ?
    import cn.tx.demo2.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext_demo2.xml")
    public class Demo2 {
    ?
        @Autowired
        private UserService userService;
    ?
        /**
         * 测试
         */
        @Test
        public void run1(){
            userService.save();
        }
    ?
    }
  • 切入点的表达式格式:
    • execution([修饰符] 返回值类型 包名.类名.方法名(参数))

    • 修饰符可以省略不写,不是必须要出现的。

    • 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。

    • 包名例如:com.tx.demo3.BookDaoImpl

      • 首先com是不能省略不写的,但是可以使用 * 代替
      • 中间的包名可以使用 * 号代替
      • 如果想省略中间的包名可以使用 ..
    • 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl

    • 方法也可以使用 * 号代替

    • 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..

  • AOP的通知方式
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context.xsd
                    http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--bean管理-->
        <bean id="userService" class="cn.tx.demo1.UserServiceImpl" />
    
        <!--=================编写AOP配置文件====================-->
        <!--先配置切面类-->
        <bean id="myXmlAspect" class="cn.tx.demo1.MyXmlAspect" />
    
        <!--配置AOP的增强-->
        <aop:config>
            <!--正在配置切面,引入真正切面对象-->
            <aop:aspect ref="myXmlAspect">
                <!--配置的是前置通知:目标对象方法执行前,先增强。method="切面类中通知的方法" pointcut="切入点的表达式"-->
                <!--
                    切入点的表达式
                        execution() 写法是固定的
                        public  可以省略不写的
                        方法返回值   void int String * 通用的写法
                        包名  * 通用的写法
                        类名  * 推荐的写法 *ServiceImpl 例如:UserServiceImpl DeptServiceImpl
                        方法名称    *  推荐写法:save*
                        方法参数列表  .. == Object... obj Object类型的可变参数
                        <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
                -->
    
                <!--
                    通知类型
                        前置通知:目标对象方法执行前,先增强。
                        <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
                        最终通知:目标对象方法执行成功或者失败,都会增强。finally
                        <aop:after method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
                        后置通知:目标对象方法执行成功,才会增强。
                        <aop:after-returning method="log"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
                        异常通知:目标对象方法执行失败,才会增强。
                        <aop:after-throwing method="log"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
                        <aop:before method="begin" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
                        <aop:after-returning method="commit"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
                        <aop:after-throwing method="rollback"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
                        <aop:after method="close" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
                        环绕通知:自己决定增强的位置。使用了环绕通知,目标对象的方法默认没有执行的,需要自己手动执行目标对象方法。
                -->
    
                <aop:around method="logAroud"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
    
            </aop:aspect>
        </aop:config>
    
    </beans>

    Spring的AOP技术-注解方式

  • 同样创建一个普通的Maven工程,导入坐标,编写接口,同上
  • 编写一个切面类,给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明
    package cn.tx.demo3;
    ?
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    ?
    @Component  // 把该类交给IOC去管理
    @Aspect     // 声明是切面类  == <aop:aspect ref="myXmlAspect">
    public class MyAnnoAspect {
    ?
        /**
         * 通知的方法
         */
        // @Before(value = "切入点的表达式")
        @Before(value = "execution(public * cn.tx.demo3.OrderServiceImpl.save(..))")
        public void log(){
            System.out.println("增强了...");
        }
    ?
    }
    ?
  • 配置文件中开启自动代理

    <aop:aspectj-autoproxy/>
  • 测试方法

    package cn.tx.test;
    ?
    import cn.tx.demo2.UserService;
    import cn.tx.demo3.OrderService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext_demo3.xml")
    public class Demo3 {
    ?
        @Autowired
        private OrderService orderService;
    ?
        /**
         * 测试
         */
        @Test
        public void run1(){
            orderService.save();
        }
    ?
    }
  • 通知类型注解

    @Before    -- 前置通知
    
    ?    @AfterReturing    -- 后置通知
    
    ?    @Around    -- 环绕通知(目标对象方法默认不执行的,需要手动执行)
    
    ?    @After    -- 最终通知
    
    ?    @AfterThrowing    -- 异常抛出通知
  • 纯注解的方式

    package cn.tx.demo3;
    ?
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    ?
    
    @Configuration      // 配置类
    @ComponentScan(value = "cn.tx.demo3")   // 扫描包
    @EnableAspectJAutoProxy     // 开启自动代理 == <aop:aspectj-autoproxy />
    public class SpringConfig {
        
    }
     

相关推荐