• spring-第二章-AOP


    一,回顾

    1.控制反转(IOC)

      以前创建对象,由我们自己决定,现在我们把管理对象的声明周期权力交给spring;

    2.依赖注入(DI)

      A对象需要B对象的支持,spring就把B注入给A,那么A就拥有了B;

      底层通过反射技术实现(Class字节码)

    3.具体实现

      (1)实体类

      (2)把实体类注册到xml配置文件中

      (3)通过context上下文对象(spring容器)获取进行使用

    二,AOP简介

    AOP(Aspect Oriented Programming) 面向切面编程(横向);

    OOP面向对象编程(纵向编程)

    AOP技术利用"横切",解刨开封装的对象内容,将那些影响了多个类的公共行为封装到一个可重用的模块,并将其命名为"Aspect",即切面,切面就是与主业务无关,却为主业务模块提供额外的扩展,便于减少系统代码的重复性,降低模块之间的耦合性,并利于系统的可操作性和维护性;

    AOP底层使用代理技术实现,代理内部有反射技术的影子

    三AOP基本概念

    1.Aspect(切面)

    通常是一个类,里面可以定义切入点和通知

    2.JoinPoint(连接点)

    被拦截的点,因为spring只支持方法类型的连接点,所以在spring中连接点指定就是被拦截的方法,实际上连接点还可以是字段或者构造器

    3.advice(通知)

    指拦截到的连接点(方法)之后要执行的增强代码,通知分为:前置,后置,异常,最终,环绕通知(before,after,afterThrowing,afterReturing,around)

    4.pointcut(切入点)

    对连接点(方法)进行拦截的定义,在程序主要体现为输入切入点表达式

    5.tarage(目标对象)

    代理的目标对象

    6.proxy(代理)

    AOP框架自动创建的代理对象

    四,spring aop基于xml方式实现

    1,添加依赖

    <!-- spring-context -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.0.RELEASE</version>
            </dependency>
    
            <!-- aop依赖的jar包 -->
            <!-- aspectjweaver -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>
            <!--aspectjrt -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.9.2</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.2</version>
                <scope>provided</scope>
            </dependency>

    2、UserServiceImpl业务类

    public class UserServiceImpl {
        
        public boolean delete(int id){
            System.out.println("UserServiceImpl.delete..."+id);
            return true;
        }

    3、TranAop切面类

    此类为切面类,给业务层类中方法添加增强代码

    public class TranAop {
        //前置通知
        public void before(){
            System.out.println("TranAop.before...");
        }
        //返回值后通知
        public void afterReturning(){
            System.out.println("TranAop.afterReturning...");
        }
        //异常通知
        public void afterThrowing(Exception ex){
            System.out.println("TranAop.afterThrowing..."+ex.getMessage());
        }
        //后置通知(最终)
        public void after(){
            System.out.println("TranAop.after...");
        }
        //环绕通知
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("TranAop.around.start");
            Object[] args = pjp.getArgs();
            Object result = null;
            //try {
                result = pjp.proceed();//执行目标方法
            //} catch (Throwable e) {
            //    System.out.println("发生异常了哦:"+e.getMessage());
            //} 
            System.out.println("TranAop.around.end");
            return result;
        }
    }

    4、beans.xml

    我们需要再beans.xml中配置aop;需要在xml中添加头部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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 声明bean -->
        <bean id="user1" class="com.yujun.maven.service.UserServiceImpl"></bean>
        <bean id="tranAop" class="com.yujun.maven.aop.TranAop"></bean>
    
        <!-- spring aop 配置 -->
        <aop:config>
            <!-- 声明切面类    id:唯一标识      ref:引用bean的id-->
            <aop:aspect id="myAspect" ref="tranAop">
                <!-- 声明切入点(拦截哪些方法)                                                 拦截service包中所有类中所有方法-->
                <aop:pointcut expression="execution(* com.yujun.maven.service.*.*(..))" id="myPointcut"/>
                <!-- 实际开发中,下面5中通知,根据实际情况进行合理的选中,不是说像我们现在这样5种都配置 -->
                <!-- 前置通知      method:切面类中的方法名-->
                <aop:before method="before" pointcut-ref="myPointcut"/>
                <!-- 后置通知 -->
                <aop:after method="after" pointcut-ref="myPointcut"/>
                <!-- 最终通知 -->
                <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
                <!-- 异常通知  throwing:是切面类中方法的形参名-->
                <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="ex"/>
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="myPointcut"/>
            </aop:aspect>
            
        </aop:config>
    
    </beans>

    5、测试

    public class Demo1 {
    
        public static void main(String[] args) {
            //context上下文
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserServiceImpl bean = context.getBean("user1", UserServiceImpl.class);
            bean.delete(10);

    运行后的控制台输出结果:

    TranAop.before...

    TranAop.around.start

    UserServiceImpl.delete...10

    TranAop.around.end

    TranAop.afterReturning...

    TranAop.after...

    可以看到在目标方法UserServiceImpl.delete的前后有额外的输出语句,也就表名之前我们的aop配置生效;

             这里没有看到有关于异常通知的输出,原因是我们目标方法delete没有发生异常,所以就不会执行异常通知;

             若想看到异常通知被执行,只需要在目标方法中添加一个可以触发异常的语句即可,当前前提是我们的环绕通知中没有对目标方法进行异常处理;

    五,springaop基于注解的方式实现

    下面我们使用注解方式来实现aop技术,相对来说注解方式比xml配置更简洁,所以现在实际开发中我们都会优先考虑使用注解方式;

    1、AserviceImpl业务类

    业务类,我们通常会统一放在service包中;

    public class AServiceImpl {
        
        public int m1(String s){
            System.out.println("AServiceImpl.m1..."+s);
            return 100;
        }
        
    }

    2、LogAop切面类

    //组件:会把当前类当做bean注册到spring容器中,等效于xml中<bean />
    @Component
    //切面类:等效于xml中的<aop:aspect />
    @Aspect
    public class LogAop {
        //切入点(拦截哪些类中的哪些方法):等效于xml中<aop:pointcut />
        @Pointcut("execution(* com.yujun.maven.service.*.*(..))")
        private void pointCut(){
            
        }
        //前置通知:等效于xml中<aop:before />,里面的pointCut()字符串是本类中的切入点方法
        @Before("pointCut()")
        public void before(){
            System.out.println("LogAop.before...");
        }
        //返回通知:等效于xml中<aop:after-returning />
        @AfterReturning(pointcut="pointCut()",returning="result")
        public void  afterReturning(Object result){
            System.out.println("LogAop.afterReturning..."+result);
        }
        //异常通知:等效于xml中<aop:after-throwing />
        @AfterThrowing(pointcut="pointCut()",throwing="ex")
        public void afterThrowing(Exception ex){
            System.out.println("LogAop.afterThrowing..."+ex.getMessage());
        }
        //最终通知:等效于xml中<aop:after />
        @After("pointCut()")
        public void after(){
            System.out.println("LogAop.after...");
        }
        //环绕通知:等效于xml中<aop:around />
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("LogAop.around.start...");
            Object result = pjp.proceed();//执行目标方法
            System.out.println("LogAop.around.end..."+result);
            return result;
        }
    }

    此类中我们使用很多注解,每个注解的含义都有详细的说明,这里就不多做额外的解释;

    主要说一下@Component注解,次注解是spring的基础注解,被此注解标注的类会被当做组件注册到spring容器中,也就相当于之前我们在xml中配置<bean />,也就是说只要在类上标注此注解,我们就不用像之前一样在xml中使用<bean />标签注册实例了;

    3、beans-anno.xml

             此xml配置文件我们需要添加context的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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        
        
        <!-- 开启扫描包:把base-package指定的包中的类中的组件注册到spring容器中-->
        <context:component-scan base-package="com.yujun.maven.aop"/>
        
        <!-- 为 @AspectJ标注的类提供代理-->
        <aop:aspectj-autoproxy/>
    </beans>

    <context:component-scan />标签配置的作用就是开启扫包功能,它会扫描base-package属性中指定的包中所有类,然后把类上标注有@Component, @Repository, @Service, @Controller, @RestController, @ControllerAdvice, @Configuration等注解的类注册添加到spring容器中;

    <aop:aspectj-autoproxy/>标签则是为使用@AspectJ注解风格的切面类提供代理,若没有此配置,我们的目标方法时没有aop技术支持的

    4、@Service注解

    //组件:业务逻辑层的组件,等效于<bean />
    @Service
    public class AServiceImpl {
        
        public int m1(String s){
            System.out.println("AServiceImpl.m1..."+s);
            return 100;
        }
        
    }

    对于AserviceImpl业务类,我们之前是通过在xml中配置<bean />标签来注册实例,那么这里我们还可以选择使用@Service注解,此注解标注的类会被当做一个服务性质组件实例被注册到spring容器中,等效于xml中<bean />标签配置实例;

             注意的是我们还需要在bean-anno.xml中新增一个扫包器,因为此业务类在service包中;

    <context:component-scan base-package="com.yujun.maven.service"/>

    5、测试

    public class Demo2 {
    
        public static void main(String[] args) {
            //context上下文
            ApplicationContext context = new ClassPathXmlApplicationContext("beans-anno.xml");
            AServiceImpl a1 = context.getBean(AServiceImpl.class);
            a1.m1("admin");
        }
    
    }

    运行之后控制台输出

    LogAop.around.start...

    LogAop.before...

    AServiceImpl.m1...admin

    LogAop.around.end...100

    LogAop.after...

    LogAop.afterReturning...100

    可以看到我们注解版的aop技术已经成功实现,就是输出的语句顺序和执行xml方法稍微不同,但是最终的功能实现是没有差别的;

    六、小结

             对于上述的两种方式aop实现,需要自己下来多多测试,然后认真理解其执行流程,这样才能对aop面向切面技术有一个更深的理解和认识;

             在aop注解版方式中,我们介绍的@Component和@Service两个注解以及 xml配置中的<context:component-scan />标签扫描器,我们在后面章节也就着重说明;

  • 相关阅读:
    Delphi对象的产生和消亡过程
    WIN32的时空观
    PHP类的用法
    D7的System.pas单元的实现部分
    PHP的最简单用法
    C调用Lua
    js连连看
    动态属性的一个架构
    Entity Framework开源了
    apachesolr4.0.0ALPHA中文分析器IKAnalyzer4.0
  • 原文地址:https://www.cnblogs.com/faded8679/p/10793959.html
Copyright © 2020-2023  润新知