Spring--AOP

15 篇文章 0 订阅

1.什么是AOP

AOP(Aspect-Oriented Programming, 面向切面编程):
    将分散在各个方法中的相同的功能提取出来,并在运行时动态的将切面注入到各方法中;它是OOP的有力补充,OOP是纵向的抽象,AOP是横向的抽象;

2. 使用AOP的好处

1. 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
2. 业务模块更简洁, 只包含核心业务代码.

3.AOP术语

术语说明
切面(Aspect) 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象(Advice+PointCut=Aspect);
增强(Advice) 切面必须要完成的工作,也叫做通知(加入的功能即代码段);
切点(PointCut) 指一个具体的连接点,通常使用表达式来确定切点位置
目标(Target) 被通知的对象(加入增强的对象);
代理(Proxy) 向目标对象加入增强(应用通知之后)创建的代理对象
连接点(JoinPoint) 程序执行的某个特定位置,Spring只支持方法(method)
织入(Weaving) 将增强加入到目标对象切点的过程(编译期,加载期,运行期)

4.AOP的实现者##

1. AspectJ:Java 社区里最完整最流行的 AOP 框架,能够在编译期提供横切代码织入。在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
 2. SpringAOP:SpringAOP使用纯java代码实现,不需要专门的编译过程,也不需要特殊的类装载器,在运行期通过代理的方式向目标对象织入增强。
 3. JBossAOP:不常用

5.在Spring中启用AspectJ

 1. 在maven工程中,添加依赖信息
    <dependency>
        <groupId>repo.org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.1.3.RELEASE</version>
    </dependency>
 2. 在spring的IOC容器中启用AspectJ的注解支持
         1. 加入aop命名空间
         2. 在bean配置文件中加入<aop:aspectj-autoproxy>,自动为与 AspectJ 切面匹配的    Bean 创建代理.

6.创建切面 使用@Aspect和@Component

         1. 增强注解(Advice):具体的代码
            AspectJ 支持 5 种类型的通知注解: 
            @Before: 前置通知, 在方法执行之前执行
            @After: 后置通知, 在方法执行之后执行 
            @AfterRunning: 返回通知, 在方法返回结果之后执行
            @AfterThrowing: 异常通知, 在方法抛出异常之后
            @Around: 环绕通知, 围绕着方法执行
         2. 切点(PointCut):根据表达式来匹配各种方法(也可以通过||,&&,!将多个表达式结合起来使用)
                execution (访问修饰符  返回值类型  类名(包括包名).方法名.(参数类型))
                例如:
                execution (* com.oracle.test.*.*(..));//当访问修饰符 返回值没有要求是使用一个"*"即可,该表达式为com.oracle.test包下的任何类的任何一个方法,参数类型没有任何要求
                execution (public * com.oracle.test.Landlord.rent(int));//访问修饰符为public ,类为Landlord,方法为rent();参数要求为int类型
         3. 示例:
            bean配置文件
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <!-- 设置要扫描的包 -->
    <context:component-scan base-package="com.oracle.test"></context:component-scan>
    <!-- 设置自动代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

抽象角色:

package com.oracle.test;

/**
 * 抽象角色
 * @author dingshuangen
 *
 */
public interface Rentable {

    public void rent(int money);

}

具体角色:

package com.oracle.test;

import org.springframework.stereotype.Component;

/**
 * 具体角色
 * @author dingshuangen
 *
 */
@Component
public class Landlord implements Rentable {

    public void rent(int money) {
        System.out.println("我是房东。。出租房子我收取租金:"+money);
    }

}

切面:

package com.oracle.test;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 切面
 * @author dingshuangen
 *
 */
@Component
@Aspect
public class LogAspectJ {

    //将该方法放到切点的前面执行
    @Before("execution (* com.oracle.test.*.*(..))")
    public void before(){
        System.out.println("---在方法之前执行");
    }
    @After("execution (public * com.oracle.test.Landlord.rent(int))")
    public void after(){
        System.out.println("---在方法之后执行");
    }
}

测试:

package com.oracle.junitTest;

import org.junit.After;
import org.junit.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.oracle.test.Landlord;
import com.oracle.test.Rentable;

public class Test {

    //创建工厂,加载配置文件
    ApplicationContext factory=new ClassPathXmlApplicationContext("applicationContext.xml");
    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @org.junit.Test
    public void test() {

        //自动创建代理对象
        Rentable r=factory.getBean(Rentable.class);
        //Rentable r=(Rentable) factory.getBean("landlord");
        System.out.println("类名:"+r.getClass());//动态产生的一个类
        System.out.println("父类:"+r.getClass().getSuperclass());
        r.rent(3000);       
    }

}

输出结果:

类名:class com.sun.proxy.$Proxy15
父类:class java.lang.reflect.Proxy
---在方法之前执行
我是房东。。出租房子我收取租金:3000
---在方法之后执行

当不使用接口时Landlord l=factory.getBean(Landlord.class);
测试以下代码:

System.out.println("不使用接口产生的类名:"+l.getClass());
System.out.println("l父类:"+l.getClass().getSuperclass());
l.rent(3000);

输出结果:

不使用接口产生的类名:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
l父类:class com.oracle.test.Landlord
---在方法之前执行
我是房东。。出租房子我收取租金:3000
---在方法之后执行

说明:
当使用接口时,Spring会使用JDK提供的代理模式就行自动代理,当不提供接口时,Spring使用CGLIB进行自动代理,生成一个真实对象的子类,在子类上进行织入增强

7.JoinPoint对象

JoinPoint对象封装了SpringAOP中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用API:

方法名作用
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象

测试方法:
在上文切面对象的方法中加入参数JoinPoint

    @Before("execution (* com.oracle.test.*.*(..))")
    public void before(JoinPoint p){
        System.out.println("---在方法之前执行");
        System.out.println("目标对象:"+p.getTarget().getClass());
        System.out.println("代理对象:"+p.getThis().getClass());
        System.out.println("目标的方法名:"+p.getSignature());
        System.out.println("参数:"+Arrays.deepToString(p.getArgs()));
        System.out.println(p.getKind());
    }

输出结果为:

不使用接口产生的类名:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
l父类:class com.oracle.test.Landlord
---在方法之前执行
目标对象:class com.oracle.test.Landlord
代理对象:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
目标的方法名:void com.oracle.test.Landlord.rent(int)
参数:[3000]
method-execution
我是房东。。出租房子我收取租金:3000
---在方法之后执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值