03Spring的AOP开发 AOP概念 XML实现AOP XML配置详解 注解实现AOP 注解详解

概念:Aspect Oriented Programming面向切面编程,是OOP的延续。

作用:在程序运行期间,在不修改源码情况下,对方法功能增强。

底层实现:Spring提供的动态代理技术实现:

  • JKD代理:基于接口的动态代理技术(目标对象必须有接口)
  • cglib代理:基于父类的动态代理技术(目标对象可以没有接口)

两者区别和联系:

  • 两者都是通过一个类来包装另一个类实现代理
  • cglib可以通过配置实现,而不用手动去定义。

AOP的相关术语:

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个完成代理的类,返回代理类
  • Joinpoint(连接点):可以被增强的方法(可以被拦截到的方法)
  • Poincut(切入点):被增强的方法(Joinpoint的一部分)
  • Advice(通知/增强):增强方法
  • Aspect(切面):切点+通知(名词)
  • Weaving(织入):将切点和通知结合的过程(动词,创建代理对象的过程)

AOP重点属于:切点、通知、切面、织入

AOP需要编写的代码:

  • 编写核心业务代码(目标类、目标方法,目标方法也叫切点)
  • 编写切面类,切面类中有通知(增强方法)
  • 在配置文件中,配置织入关系

AOP技术原理:

  • Spring框架监控切入点,一旦监控到切入点在执行,动态创建代理对象,在代理对象的对应的位置,将通知方法的功能织入,运行代理对象。

AOP使用哪种代理方式:

  • 框架根据目标类是否实现了接口来决定采用哪种代理方式。

XML实现AOP

1.导入坐标

<!--    Spring框架-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
<!--     aspectj团队实现的aop配置比较好-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>

2.创建目标类

创建目标类和接口,以及内部的方法(切点)

public class MyTarget {
    public void save() {
        System.out.println("save running...");
    }
}

3.创建通知

创建切面类,以及增强方法(通知)

public class MyAspect {
    public void before() {
        System.out.println("前置增强...");
    }
}

4.织入

织入:就是在applicationContext.xml中配置

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd" >
<!--目标对象-->
    <bean />
<!--切面对象-->
    <bean />
<!--配置织入-->
    <aop:config>
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut="execution(public void com.aop.MyTarget.save())"/>
        </aop:aspect>
    </aop:config>
</beans>

XML配置详解

<bean />
<bean />
<aop:config>
	<aop:aspect ref="myAspect">
		<aop:before method="before" pointcut="execution(public void com.aop.MyTarget.save())"/>
	</aop:aspect>
</aop:config>
  1. 通知的类型:

    • aop:before 前置通知
    • aop:after-returning 后置通知
    • aop:around 环绕通知,前后都执行
    • aop:throwing 异常处理通知,抛出异常时执行
    • aop:after 最终通知,不管抛不抛异常
  2. 切点表达式:execution(public void com.aop.MyTarget.save(参数))

    • 访问限定符可以省略

    • 返回值类型、包名、类名、方法名可以用*号代表任意

    • 包名和类名之间可以一个点.表示当前包下所有类,两个点..表示当前及其子包下的所有类

    • 参数列表可以使用两个点..表示任意个数,任意类型的参数列表

      execution(public void com.aop.MyTarget.method())
      execution(* com.aop.MyTarget.*(..)) // MyTarget下的所有方法
      execution(* com.aop.*.*(..)) // aop包下的所有类的所有方法
      execution(* com.aop..*.*(..)) // aop包下的所有包的所有类的所有方法
      
  3. 切点表达式抽取

    <aop:aspect ref="aspect">
      <aop:pointcut />
      <aop:before method="before" pointcut-ref="myPoint"/>
    </aop:aspect>
    

注解实现AOP

导入坐标

<!--    Spring框架-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
<!--     aspectj团队实现的aop配置比较好-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>
<!--        spring-test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

步骤:

  1. 创建目标接口和类,添加注解@Component("xx"),表示交给spring框架

  2. 创建切面类,添加注解@Component("xxx"),表示交给spring框架

  3. 在切面类中使用注解配置织入关系:类上加@Aspect表示切面,方法上添加注解类型

    @Component("myAspect")
    @Aspect
    public class MyAspect {
        @Before("execution(void com.aop.MyTarget.save())")
        public void before() {
            System.out.println("前置增强...");
        }
    }
    
  4. 在applicationContext配置文件中开启组件扫描和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:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd" >
    
    <!--    组件扫描-->
        <context:component-scan base-package="com.aop"/>
    <!--    aop自动代理-->
        <aop:aspectj-autoproxy/>
    
    </beans>
    
  5. 测试(创建一个目标类并执行)

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-anno.xml")
    public class SpringTest {
        @Autowired
        private MyTarget target;
        @Test
        public void test01() throws SQLException {
            target.save();
        }
    }
    

注解详解

  1. 通知的类型 @通知("切点表达式")

    • Before, AfterReturning Around AfterThrowing After
  2. 切点表达式的抽取

    @Component("myAspect")
    @org.aspectj.lang.annotation.Aspect
    public class Aspect {
        @Before("pointchut()")
        public void before() {
            System.out.println("before");
        }
        @Poincut("execution(void baidu.proxy.cglib.Target.save())")
        public void pointcut(){}
    }