• 【复习 spring IOC与AOP思想】


    控制反转(Inversion of Control:IoC)

      IOC 把对象的创建交给框架,它包括依赖注入(DI)和依赖查找,它的作用是降低程序间的耦合(依赖关系)。

    而所谓依赖注入(DI),就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”,我们需要进行控制反转(IoC),及上层控制下层,而不是下层控制着上层,这样可以减少维护代码的困难度。

      简单来说,IOC特性 就是 把new对象的权利交给了Spring容器来处理,当前就是简述其设计实现思想,实际开发大多通过注解注入即可,就拿注解注入来举例:

      主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:

          @Component:可以用于注册所有bean
          @Repository:主要用于注册dao层的bean
          @Controller:主要用于注册控制层的bean
          @Service:主要用于注册服务层的bean

      还有两个注解是用于描述依赖关系:

      @Resource:java的注解,默认以byName的方式去匹配与属性名相同的bean的id,如果没有找到就会以byType的方式查找。

            如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

      @Autowired:spring注解,默认是以byType的方式去匹配类型相同的bean,如果只匹配到一个,那么就直接注入该bean,无论要注入的 bean 的 name 是什么;如果匹配到多个,就会根据byName去找对应bean名称的。

    切面编程(AOP)

      AOP的基本概念如下:

      (1)Aspect(切面):通常是一个类,里面可以定义切入点(Pointcut )和通知(Advice)

      (2)JointPoint(连接点):表示在程序中明确定义的点,一般是方法的调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point

      (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around,Advice 定义了在 切入点 (PointCut) 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

      (4)Pointcut(切入点):就是一组带有通知(advice)的连接点(JointPoint),在程序中主要体现为书写切入点表达式,并定义相应的通知(advice) 将要发生的地方。

      (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

      (6)Weaving(织入):将 切面(Aspect)和其他对象连接起来, 并创建 Adviced object 的过程

      这些概念比较抽象,可以通过一个demo来理解:

      首先,测试方法为jdk代理(Spring中的AOP代理:代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。)

      jdk的Proxy:动态代理,执行的时候处理,要求必须有接口、实现类,代理创建的是实现类的子类。  

      然后xml实现和注解实现两种方式 我们选择注解实现:

      代码如下:

      接口类:

    package com.wang.service;
    
    /**
     * on 2022/2/6 19:43
     */
    public interface HelloWorldService {
        public void sayHello(int id);
    }

      接口实现类:

    package com.wang.service.Impl;
    
    import com.wang.pojo.User;
    import com.wang.service.HelloWorldService;
    import org.springframework.stereotype.Component;
    
    /**
     *  2022/2/6 19:44
     */
    
    @Component("HelloWorldServiceImpl")
    public class HelloWorldServiceImpl implements HelloWorldService {
    
        @Override
        public void sayHello(int id) {
            System.out.println("Spring AOP——这里是bean内方法");
        }
    }

      切面类:

    package com.wang.Aspect;
    
    import com.wang.pojo.User;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     *  on 2022/2/6 19:49
     */
    
    @Component    //声明这是一个组件
    @Aspect       ///声明这是一个切面Bean
    public class HelloWorldAspect {
        //定义切点
        @Pointcut("execution(* com.wang..*.*(..)) ")
        public void sayings(){
        }
    
        /**
         * 前置通知(注解中的sayings()方法,其实就是上面定义pointcut切点注解所修饰的方法名,那只是个代理对象,不需要写具体方法,
         * 相当于xml声明切面的id名,如下,相当于id="embark",用于供其他通知类型引用)
         * <aop:config>
         <aop:aspect ref="mistrel">
         <!-- 定义切点 -->
         <aop:pointcut expression="execution(* *.saying(..))" id="embark"/>
         <!-- 声明前置通知 (在切点方法被执行前调用) -->
         <aop:before method="beforSay" pointcut-ref="embark"/>
         <!-- 声明后置通知 (在切点方法被执行后调用) -->
         <aop:after method="afterSay" pointcut-ref="embark"/>
         </aop:aspect>
         </aop:config>
         */
    /*
        @Before("sayings()")
        public void sayHello(){
            System.out.println("注解类型前置通知");
        }
    
        //后置通知
        @After("sayings()")
        public void sayGoodbey(){
            System.out.println("注解类型后置通知");
        }
    */
        @Before(value="bean(HelloWorldServiceImpl) && args(id,..)", argNames="id")
        public void beforeWithParam(int id) {
            System.out.println("带参数的前置通知,也可以理解为我们定义一个Advice来拦截这个带参数id的这个方法,bean类为HelloWorldServiceImpl");
            System.out.println(this.getClass().getName()+" ID is : " + id);
        }
    
        //环绕通知。注意要有ProceedingJoinPoint参数传入。
        @Around(value = "sayings()")
        public void sayAround(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("注解类型环绕通知..环绕前");
            pjp.proceed();//执行方法,即执行被通知函数
            System.out.println("注解类型环绕通知..环绕后");
        }
    
    }

      启动类:

    package com.wang.AopTest;
    
    import com.wang.service.HelloWorldService;
    import com.wang.service.Impl.HelloWorldServiceImpl;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    /**
     *  on 2022/2/6 19:51
     */
    public class HelloWorldTest {
        public static void main(String[] args) {
    
            //这个是application容器,所以就会去所有的已经加载的xml文件里面去找,包括jar包里面的xml文件
            ApplicationContext context = new FileSystemXmlApplicationContext("D:\\IDEAprojects\\Mybatis-study\\mybatis-04\\src\\main\\resources\\applicationContext.xml");
    
            //通过ApplicationContext.getBean(beanName)动态加载数据(类)【获取Spring容器中已初始化的bean】。
            HelloWorldServiceImpl helloWorld=(HelloWorldServiceImpl) context.getBean("HelloWorldServiceImpl");
    
            int id = 4;
            //执行动态加载到的类的方法
            helloWorld.sayHello(id);
    
        }
    }

    配置文件:applicationContext.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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-4.3.xsd">
    
    
    
        <!-- 开启注解扫描 -->
        <context:component-scan base-package="com.wang"/>
        <!-- 开启aop注解方式,此步骤s不能少,这样java类中的aop注解才会生效 -->
        <aop:aspectj-autoproxy/>
        <!-- 强制使用cglib代理,如果不设置,将默认使用jdk的代理,但是jdk的代理是基于接口的 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    </beans>

      demo2:改动了一些方法

      还可以更详细一点,定义一些具体的方法来感受所谓的增强,改动后代码如下:(全部放在一起了)

    // interface
    package com.wang.service;
    
    public interface HelloWorldService {
        public void sayHello(int id);
    
        void getTool(String tool);
        void doSomething(String action);
        void initWork(String tool, String action);
    }
    
    
    
    // impl
    package com.wang.service.Impl;
    
    import com.wang.pojo.User;
    import com.wang.service.HelloWorldService;
    import org.springframework.stereotype.Component;
    
    
    @Component("HelloWorldServiceImpl")
    public class HelloWorldServiceImpl implements HelloWorldService {
    
        @Override
        public void sayHello(int id) {
            System.out.println("Spring AOP——这里是bean内方法");
        }
    
        @Override
        public void getTool(String tool) {
            System.out.println("=====调用getTool方法=====");
            System.out.println("获得道具:" + tool);
            System.out.println("=====调用结束=====");
        }
    
        @Override
        public void doSomething(String action) {
            System.out.println("=====调用doSomething方法=====");
            System.out.println("执行操作:" + action);
            System.out.println("=====调用结束=====");
        }
    
    
        @Override
        public void initWork(String tool, String action) {
            System.out.println("生成投篮的测试用例");
            getTool(tool);
            doSomething(action);
        }
    }
    
    
    
    // 切面类
    package com.wang.Aspect;
    
    import com.wang.pojo.User;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    
    @Component    //声明这是一个组件
    @Aspect       ///声明这是一个切面Bean
    public class HelloWorldAspect {
        //定义切点
        @Pointcut("execution(* com.wang..*.*(..)) ")
        public void sayings(){
        }
    
      
        @Before("sayings()")
        public void sayHello(){
            System.out.println("注解类型前置通知");
        }
    
        //后置通知
        @After("sayings()")
        public void sayGoodbey(){
            System.out.println("注解类型后置通知");
        }
    */
        //后置通知
    /*    @After("sayings()")
        public void sayGoodbey(){
            System.out.println("注解类型后置通知");
        }*/
    
    /*    @Before(value="bean(HelloWorldServiceImpl) && args(id,..)", argNames="id")
        public void beforeWithParam(int id) {
            System.out.println("带参数的前置通知,也可以理解为我们定义一个Advice来拦截这个带参数id的这个方法,bean类为HelloWorldServiceImpl");
            System.out.println(this.getClass().getName()+" ID is : " + id);
        }*/
    
        //环绕通知。注意要有ProceedingJoinPoint参数传入。
        @Around(value="bean(HelloWorldServiceImpl) && args(tool, action)", argNames= "pjp,tool, action")
        public void sayAround(ProceedingJoinPoint pjp, String tool, String action) throws Throwable{
            System.out.println("注解类型环绕通知..环绕前");
            System.out.println(this.getClass().getName()+"需要先" +  "去获得投篮所需道具:"+tool);
            pjp.proceed();//执行方法,即执行被通知函数
            System.out.println("nice shooting!");
            System.out.println("注解类型环绕通知..环绕后");
        }
    
    }
    
    
    
    
    
    // 启动类
    package com.wang.AopTest;
    
    import com.wang.service.HelloWorldService;
    import com.wang.service.Impl.HelloWorldServiceImpl;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    
    public class HelloWorldTest {
        public static void main(String[] args) {
    
            //这个是application容器,所以就会去所有的已经加载的xml文件里面去找,包括jar包里面的xml文件
            ApplicationContext context = new FileSystemXmlApplicationContext("D:\\IDEAprojects\\Mybatis-study\\mybatis-04\\src\\main\\resources\\applicationContext.xml");
    
            //通过ApplicationContext.getBean(beanName)动态加载数据(类)【获取Spring容器中已初始化的bean】。
            HelloWorldServiceImpl helloWorld=(HelloWorldServiceImpl) context.getBean("HelloWorldServiceImpl");
    
            //执行动态加载到的类的方法
            helloWorld.initWork("basketball", "shoot");
    
        }
    }

     控制台输出:

    注解类型环绕通知..环绕前
    com.wang.Aspect.HelloWorldAspect需要先去获得投篮所需道具:basketball
    生成投篮的测试用例
    =====调用getTool方法=====
    获得道具:basketball
    =====调用结束=====
    =====调用doSomething方法=====
    执行操作:shoot
    =====调用结束=====
    nice shooting!
    注解类型环绕通知..环绕后
    
    Process finished with exit code 0

     PS:切面类要添加注解@Aspect,不然不会触发aop的那几个注解,比如@Before 等。

      PPS:总结来说spring 允许用户实现自定义切面,用AOP来完善OOP(面向对象编程)的使用,我们可以把Spring AOP看作是对Spring的一种增强

    参考连接:https://blog.csdn.net/q982151756/article/details/80513340 (例子很形象)

         https://www.cnblogs.com/liuruowang/p/5711563.html

  • 相关阅读:
    习题8-8 判断回文字符串
    Field笔记
    Object类中的方法
    字符和字节的区别
    Layui搜索设置每次从第一页开始
    Springboot+Jpa+Layui使用Pageable工具进行数据分页
    Map<String, Object>返回类型
    List集合中剔除重复的数据
    Springboot+Mybatis(Mysql 、Oracle) 注解和使用Xml两种方式批量添加数据
    MySql中group_concat函数的使用
  • 原文地址:https://www.cnblogs.com/dabuliu/p/15865793.html
Copyright © 2020-2023  润新知