Spring AOP学习笔记

一、AOP

1.什么是 AOP

AOP:Aspect-Oriented Programming面向切面编程,是面向对象编程的一种补充。
将程序中的交叉业务(事务,日志)代码提取出来,封装成切面,由AOP容器在合适的时机(位置)将封装的切面动态的织入到具体的业务逻辑中。
注意:AOP不是Spring特有的

1.1 应用场合

适用于具有横切逻辑的场合,如事务管理,日志记录,性能监测,异常通知,访问控制。

1.2 作用

  • 不改变原有代码的基础上,动态的添加新的功能
  • 模块化(方便维护,可扩展性更强)

1.3连接点

  • 连接点JoinPoint
    程序执行的某个特定的位置,如方法执行前,方法调用后,方法抛出异常时或者方法调用前后。
  • 切入点PointCut
    定位查找到需要的连接点,即切点
  • 增强Advice也称为通知
    在切点上执行的一段程序代码,用来实现某些功能
  • 目标对象Target
    将执行增强处理的目标类
  • 织入Weaving
    将增强添加到目标类具体切入点上的过程
  • 代理Proxy
    一个类被织入增强后,会产生代理类
  • 切面Aspect
    切点和增强的组合
  • 引介Introduction也称为引入

2. 实现原理

2.1 代理模式

2.1.1 概念

为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用。通过代理对象来访问目标对象,可以增强额外的操作,扩展目标对象的功能。

2.1.2 分类:

  • 静态代理
    代理类是程序员创建或工具生成的。
    所谓静态代理就是程序运行前,就已经存在代理类的字节码文件。
    缺点:代理对象需要和目标对象实现相同的接口,如果接口增加了方法,目标对象和代理对象都需要进行维护。
  • 动态代理
    代理类是程序在运行期间由JVM通过反射等机制动态生成的,自动生成代理类和代理对象。
    所谓动态就是指在程序运行前,不存在代理类的字节码文件,在运行的时候动态生成的。

2.1.3 代理三要素

  • 与目标类实现相同接口
  • 找到目标类的实例
  • 交叉业务逻辑,要执行的操作

动态代理的两种技术

  • jdk技术
Proxy.newProxyInstance(
  classLoader,  //目标类的类加载器
  interfaces,  //目标类实现的接口
  InvacationHandler  //交叉业务逻辑
);

缺点:目标对象必须实现一个或多个接口,如果目标类没有实现接口则无法使用。

  • cglibj技术(适用于无接口时使用)
    如果没有实现接口,则通过继承来实现
    步骤:
    1.添加jar包
    2.通过Enhancer类调用create方法进行操作
    用法:
<!--cglib依赖-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>${cglib.version}</version>
</dependency>
Enhancer.create(
    class,//目标类的类型
    InvocationHandler,//交叉业务逻辑
);

2.2 AOP原理

Spring AOP就是使用动态代理

  • 对于实现接口的目标类,使用的是JDK的动态代理
  • 对于没有实现接口的目标类,使用的是CGLIB的动态代理

3.Spring AOP的配置方式

3.1 三种配置方式

  • Spring AOP 1.x,使用ProxyFactoryBean手动代理
  • Spring AOP 2.x,基于命名空间的配置
  • Annotation,基于注解的配置(推荐)

3.2 Advice类型

Spring AOP支持五种类型的通知

注意:多种Advice之间不能有耦合,即多个Advice之间不能有业务交叉

二、Spring AOP 1.x

1.基本用法

1.1添加jar包

<!--Spring AOP-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency>

1.2 配置目标类

<!--配置目标类-->
<bean id="userServiceTarget" class="aop04.service.impl.UserServiceImpl"/>

1.3 配置Advice

定义增强类,并实现相同接口

<!--配置增强类-->
<bean id="logAdvice" class="aop04.advice.LogAdvice"/>

1.4 配置Pointcut

定义切入点,配置位置信息,指定哪些类的哪些方法需要被增强
使用NameMatchMethodPointcutAdvisor对Pointcut进行Advice
Advisor是Pointcut+Advice的配置器,Advisor=Pointcut+Advice

<!--配置要增强的切入点,织入的过程-->
<bean id="logAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <!--指定Advice-->
    <property name="advice" ref="logAdvice"/>
    <!--配置切入点,指定要匹配的方法名-->
    <property name="mappedNames">
        <list>
            <value>login</value>
            <value>logout</value>
        </list>
    </property>
</bean>

1.5 配置代理

使用ProxyFactoryBean配置代理

<!--配置代理-->
<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="userServiceTarget"/><!--目标类的实例-->
    <property name="interfaces"><!--目标类的接口列表-->
        <list>
            <value>aop04.service.UserService</value>
        </list>
    </property>
    <property name="interceptorNames"><!--交叉业务逻辑-->
        <list>
            <value>logAdvisor</value>
        </list>
    </property>
</bean>

三、Spring AOP 2.x

1.简介

基于命名空间的配置,原理时使用后处理器,配置更简单

特点:

  • 简化配置
  • 非侵入性,编写通知时不需要实现任何接口
  • 使用AspectJ表达式定义切点

2.基本用法

2.1配置Advice

定义增强类,不需要实现任何接口,但有多种写法


写法

2.2配置Pointcut并进行织入

四、AspectJ表达式

1.简介

切入点表达式,一种表达式,用来定义切入点的位置

2.用法

2.1within

语法:within(包名.类名)
匹配该类中的所有方法

2.1.1 具体实现

<!--Spring AOP 2.x-->

<!--配置目标类-->
<bean id="userService" class="aop06.service.impl.UserServiceImpl"/>

<!--配置Advice-->
<bean id="logAdvice" class="aop06.advice.LogAdvice"/>

<!--配置切入点,并进行织入-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pc" expression="within(aop06.service.impl.UserServiceImpl)"/>
    <!--织入-->
    <aop:aspect ref="logAdvice">
        <!--将logAdvice中的log方法以前置通知的方式织入到对应的切入点中-->
        <!--<aop:before method="log" pointcut-ref="pc"/>-->

        <!--<aop:after-returning method="log2" pointcut-ref="pc" returning="returnValue"/>-->

        <!--<aop:after-throwing method="log3" pointcut-ref="pc" throwing="e"/>-->

        <aop:around method="log4" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

2.2 execution

匹配特定包中的特定类中的特定返回值类型的特定参数的特定方法。
语法:execution(表达式)
表达式:返回值类型 包名 类名 方法名(参数类型)
通配符:*和..

<!--匹配返回值类型为void并且在aop06包下的service下的impl下的ProductServiceImpl实现类下的deleteById方法并且参数为int类型-->
<aop:pointcut id="pc2" expression="execution(void aop06.service.impl.ProductServiceImpl.deleteById(int))"/>
<!--匹配任意返回值类型并且在aop06包下的service包下的impl包中的任意类中的任意方法并且参数列表也是任意的-->
<aop:pointcut id="pc3" expression="execution(* aop06.service.impl.*.*(..))"/>

五、IoC的注解

1.简介

Spring中提供了一系列的注解,来替代配置文件中的配置
实际开发中,建议使用注解+配置文件的形式

2.IoC注解使用

2.1 步骤:

  • 扫描包
<!--扫描包,可以扫描多个-->
<context:component-scan base-package="ioc"/>
<context:component-scan base-package="com.hxx"/>

2.2 常用注解

2.2.1 组件的定义

  • @Component :定义bean组件,添加到IoC容器中,不区分组件类型
  • @Repository:表示Dao层组件
  • @Service:表示Service层组件
  • @Controller:表示Action层组件
  • 数据装配
    注意:注解方式的装配是直接使用属性进行装配,可以不要set方法
    • 简单类型
      • @Value
@Value("666")
private int num;

@Value("true")
private Boolean flag;

@Value("${jdbc.username}")
private String username;

@Value("java.lang.String")
private Class clazz;

@Value("classpath:ioc/applicationContext.xml")
private Resource resource;
<!--读取属性文件-->
<!--<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="ioc/info.properties"/>
</bean>-->
<context:property-placeholder location="classpath:ioc/info.properties"/>

其他bean的引用

/**
 * 方式1:使用@Autowired
 *      自动装配:默认按byType,如果有多个同类型的bean,则按照byName
 *      结合Qualifier按照指定byName注入
 * 方式2:使用@Resource,javaEE提供
 */
//@Autowired
//@Qualifier("ob")
@Resource
private OtherBean otherBean;

集合的装配

//集合的装配使用@Resource注解,默认按照名字进行装配
@javax.annotation.Resource
private Integer[] arrays;

@javax.annotation.Resource
private List<OtherBean> list;

@javax.annotation.Resource
private Set<OtherBean> set;

@javax.annotation.Resource
private Map<OtherBean,Class> map;

@javax.annotation.Resource
private Properties p;
<!--集合类型的装配-->
<util:list id="arrays">
    <value>1</value>
    <value>2</value>
    <value>3</value>
</util:list>

<util:list id="list">
    <ref bean="otherBean"/>
    <ref bean="otherBean"/>
    <ref bean="ob"/>
    <bean class="ioc.OtherBean">
        <property name="msg" value="嘻嘻嘻"/>
    </bean>
</util:list>

<util:set id="set">
    <ref bean="otherBean"/>
    <ref bean="otherBean"/>
    <ref bean="ob"/>
    <bean class="ioc.OtherBean">
        <property name="msg" value="嘻嘻嘻"/>
    </bean>
</util:set>

<util:map id="map">
    <entry key-ref="otherBean" value="java.lang.String"/>
</util:map>

<util:properties id="p">
    <prop key="key1">value1</prop>
    <prop key="key2">value2</prop>
</util:properties>
  • 生命周期
//相当于init-method属性
@PostConstruct
public void init(){
    System.out.println("SpringBean.init");
}

//相当于destroy-method属性
@PreDestroy
public void destroy(){
    System.out.println("SpringBean.destroy");
}
  • bean实例化的时机
    使用@Lazy注解将实例化的时机更改为调用bean时再进行实例化

  • scope作用域
    默认是单例的,可以使用@Scope("prototype")注解将作用域改为非单例的。

六、AOP的注解

1.配置Advice

定义增强类,添加@Component和@Aspect

@Component
@Aspect //表示这是一个切面
public class LogAdvice {

    //定义切点表达式
    @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")
    public void pc(){
    }

    @Pointcut("execution(* aop.service.impl.*.*(..))")
    public void pc2(){
    }

    /*@Before("pc()")
    public void before(JoinPoint joinPoint){
        System.out.println("LogAdvice.before,name:"+joinPoint.getSignature().getName()+
                ",args:"+ Arrays.toString(joinPoint.getArgs())+",target:"+joinPoint.getTarget());
    }

    @AfterReturning(value = "pc()",returning = "returnValue")
    public void afterReturning(JoinPoint joinPoint,Object returnValue){
        System.out.println("LogAdvice.afterReturning,returnValue:"+returnValue);
    }

    @AfterThrowing(value = "pc2()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        System.out.println("LogAdvice.afterThrowing,e"+e);
    }*/

    @Around("pc()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知执行前。。。");
        Object proceed = joinPoint.proceed();
        System.out.println("环绕通知执行后。。。");
        return proceed;
    }

}

2.配置Pointcut并指定通知的类型

@Pointcut(切点表达式)

@Before(切点方法+())
@AfterReturning()
@AfterThrowing()
@Around()

3.织入

    <!--
        自动创建代理,并织入到切面
        proxy-target-class默认值是false,为JDK的动态代理
                                true代表使用CGLIB
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容