美文网首页
Spring AOP学习笔记

Spring AOP学习笔记

作者: 青年心路 | 来源:发表于2019-05-09 09:47 被阅读0次

    一、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"/>
    

    相关文章

      网友评论

          本文标题:Spring AOP学习笔记

          本文链接:https://www.haomeiwen.com/subject/njxdoqtx.html