【Spring】全面讲解IOC、AOP、注入方式、bean的生命周期、aop通知应用 spring与web容器整合

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: Spring是一个开源的轻量级Java应用开发框架,它提供了一种简单、高效、灵活的方式来构建企业级应用程序。Spring框架的核心特点是依赖注入(Dependency Injection)和面向切面编程(Aspect-Oriented Programming),它通过一组模块化的组件提供全面的支持,使开发人员能够快速搭建可扩展、可维护的应用。


一、简介

1、什么是spring?

Spring是一个开源的轻量级Java应用开发框架,它提供了一种简单、高效、灵活的方式来构建企业级应用程序。Spring框架的核心特点是依赖注入Dependency Injection)和面向切面编程Aspect-Oriented Programming),它通过一组模块化的组件提供全面的支持,使开发人员能够快速搭建可扩展、可维护的应用。

2、目的

学习Spring的目的可以总结为以下几点:

    1. 简化开发:Spring提供了一系列的工具和抽象,简化了企业级应用开发过程。它通过依赖注入来解决对象间的依赖关系,使得代码更加灵活、可测试和可维护。
    2. 高效开发:Spring框架提供了诸多模块,如Spring MVC、Spring Boot等,可以快速构建Web应用和微服务。它提供了一种约定优于配置的开发方式,提高了开发效率。
    3. 提高可扩展性:Spring框架基于接口和模块化设计,能够方便地集成其他开源框架和库。它的松耦合特性使得应用程序更易于扩展和维护。
    4. 丰富的功能:Spring拥有丰富的功能和特性,包括事务管理、安全性、缓存、消息队列、调度等。它为开发人员提供了一系列解决方案,使得应用程序开发更加便捷。

    3、功能及使用范围

    Spring的功能范围非常广泛,包括但不限于以下方面:

      1. 依赖注入和控制反转:通过依赖注入,Spring容器能够将对象之间的依赖关系管理起来,使得对象的创建和使用解耦,提高代码的灵活性和可测试性。
      2. 面向切面编程:Spring提供了面向切面编程(AOP)的支持,可以将与核心业务逻辑无关的功能(如事务管理、日志记录等)进行横切,提高了代码的重用性和可维护性。
      3. Web开发支持:Spring提供了Spring MVC模块,用于开发Web应用程序。它能够处理HTTP请求和响应,进行URL路由、数据绑定、表单验证、视图解析等操作,简化了Web开发过程。
      4. 数据访问支持:Spring框架提供了对各种数据访问技术的支持,包括JDBC、ORM(如Hibernate、MyBatis)、JPA等。它通过抽象出一套统一的数据访问接口,使得数据库访问更加方便和可替换。
      5. 安全性支持:Spring提供了一套强大的安全性框架,可以进行认证和授权管理。它支持各种身份验证方式,如基于表单的认证、基于角色的访问控制等,保护应用程序的安全性。

      总之,Spring框架是Java开发领域最流行的框架之一,它提供了丰富的功能和特性,帮助开发人员构建可靠、高效的企业级应用程序。

      一句话概括,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

      二、spring IOC

      1、ioc的理解

      IoC(Inversion of Control,控制反转是Spring框架的核心概念之一,也是实现依赖注入的基础。它通过解耦对象之间的依赖关系,使得对象的创建和管理由框架来负责,而不是由开发人员手动管理。

      在传统的程序设计中,对象之间的依赖关系通常由开发人员在代码中进行硬编码,对象直接通过关键字(如new)来创建和管理。这种方式存在一些问题,如对象之间的耦合度高、可测试性差、扩展性差等。

      而IoC则是一种思想上的转变,它将对象的创建和管理权利交给了框架。具体来说,IoC的核心思想是:通过配置或注解,告诉框架需要创建哪些对象,以及对象之间的依赖关系。然后,框架在应用程序运行时根据配置信息动态地创建和组装对象。

      Spring通过IoC容器来实现控制反转。IoC容器是一个用于管理对象的容器,它负责创建、组装、初始化和销毁对象。开发人员只需要配置对象的创建和依赖关系,然后通过容器来获取需要的对象即可。

      2、开发人员可达到的目的

        1. 解耦对象之间的依赖关系:通过IoC容器,开发人员只需要关注对象的功能实现,而不需要关心对象是如何创建和组装的。对象之间的依赖关系由容器负责管理,降低了对象之间的耦合度。
        2. 提高代码的可测试性:由于对象的创建和组装由容器来完成,开发人员可以很容易地对对象进行替换或模拟,从而实现单元测试和集成测试。
        3. 增强代码的可扩展性:当需要添加新的功能或模块时,只需要配置新的对象和依赖关系,而不需要修改现有的代码。通过配置方式,可以方便地在不影响现有代码的情况下进行扩展。
        4. 提高代码的灵活性:IoC容器使得对象的创建完全可配置化,可以在运行时根据需要创建不同的实例。同时,框架提供了生命周期管理和依赖解析等功能,使得对象的管理更加便捷。

        总而言之,IoC是Spring框架的核心特性之一,它通过控制反转的思想,将对象的创建和依赖关系的管理交给了框架,减少了对象之间的耦合度,提高了代码的可测试性和可扩展性。

        3、分析实现

        首先创建好我们的maven项目

        image.gif编辑

        设置我们的pom.xml

        <dependency>
                <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>3.8.1</version>
              <scope>test</scope>
            </dependency>
            <!-- 2、导入spring依赖 -->
            <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>${spring.version}</version>
            </dependency>
            <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aspects</artifactId>
              <version>${spring.version}</version>
            </dependency>
            <!-- 5.1、junit -->
            <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>${junit.version}</version>
              <scope>test</scope>
            </dependency>
            <!-- 5.2、servlet -->
            <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>${javax.servlet.version}</version>
              <scope>provided</scope>
            </dependency>
        image.gif

        新建image.gif编辑

        我们用原来的方式写一下

          1. 编写一个接口
          public interface UserService {
              public void updUser();
          }
          1. image.gif
          2. 再编写一个实现类
          public class UserServiceImpl1 implements UserService {
              public void updUser() {
                  System.out.println("修改SQL用户数据");
          }
          1. image.gif我们原来的方式是这样写的
          2. 现在我们添加一个实现类
          public class UserServiceImpl2 implements UserService {
              public void updUser() {
                  System.out.println("修改SQL用户数据");
                  //修改用户姓名
                  System.out.println("修改用户姓名");
              }
          }
          1. image.gif
          2. 当我们测试调方法的时候只需要改动后面的实现类image.gif编辑

          手动实例化的弊端:

          1、一旦依赖的接口实现需要大批量改动,迭代,维护成本高

          2、当接口的实现类不统一,维护成本更高

          我们怎么解决这个问题呢?

          4、bean配置

          首先我们要在maven项目里面的resources文件里面建立一个基于spring的xml文件image.gif编辑

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          </beans>
          image.gif

          我们在web文件里面建立两个Class类

          public class UserAction {
              private UserService us;
              public UserService getUs() {
                  return us;
              }
              public void setUs(UserService us) {
                  this.us = us;
              }
              public String updUser() {
                  us.updUser();
                  return "list";
              }
          }
          image.gif
          public class GoodsAction {
              private UserService us;
              public UserService getUs() {
                  return us;
              }
              public void setUs(UserService us) {
                  this.us = us;
              }
              public String updUser() {
                  us.updUser();
                  return "list";
              }
          }
          image.gif

          编写一个测试类

          public class demo1 {
              public static void main(String[] args) {
          //        加载spring核心配置文件(建模),获取spring的上下文对象,上下文对象中可以获取任何JavaBean对象
                  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
                  UserAction userAction = (UserAction) context.getBean("userAction");
                  userAction.updUser();
                  GoodsAction goodsAction = (GoodsAction) context.getBean("goodsAction");
                  goodsAction.updUser();
              }
          }
          image.gif

          在我们的新建的这个spring-context.xml文件里面添加<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"
                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
              <!--凡是在spring核心配置文件spring-context.xml中配置,那么该类JavaBean就交给了spring容器管理-->
              <bean class="com.tgq.ioc.web.UserAction" id="userAction">
                  <property name="us" ref="userService1"/>
              </bean>
              <bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction">
                  <property name="us" ref="userService2"/>
              </bean>
              <bean class="com.tgq.ioc.service.impl.UserServiceImpl1" id="userService1"/>
              <bean class="com.tgq.ioc.service.impl.UserServiceImpl2" id="userService2"/>
          </beans>
          image.gif

           

          三、spring IOC的注入方式

          spring的ioc有哪些注入方式呢?

            1. set方法属性注入
            2. 构造注入
            3. 接口注入/自动装配

            1、set方法属性注入

            在我们前面的GoodsAction新添几个属性和get、set方法,一个输出打印的方法toPrint()

            public class GoodsAction {
                private UserService us;
                private String name;
                private int age;
                public String getName() {
                    return name;
                }
                public void setName(String name) {
                    this.name = name;
                }
                public int getAge() {
                    return age;
                }
                public void setAge(int age) {
                    this.age = age;
                }
                public List<String> getPeoples() {
                    return peoples;
                }
                public void setPeoples(List<String> peoples) {
                    this.peoples = peoples;
                }
                private List<String> peoples;
                public UserService getUs() {
                    return us;
                }
                public void setUs(UserService us) {
                    this.us = us;
                }
                public String updUser() {
                    us.updUser();
                    return "list";
                }
                public void toPrint() {
                    System.out.println(this.name);
                    System.out.println(this.age);
                    System.out.println(this.peoples);
                }
            }
            image.gif

            在print-context.xml里面设置值,使用set方法属性注入用property

            <bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction">
                    <property name="us" ref="userService2">
                    </property>
                    <property name="name" value="旺财"/>
                    <property name="age" value="20"/>
                    <property name="peoples">
                        <list>
                            <value>留守儿童</value>
                            <value>情侣</value>
                            <value>留守老人</value>
                        </list>
                    </property>
                </bean>
            image.gif

            在测试类里面调用

            goodsAction.toPrint();
            image.gif

            image.gif编辑

            2、构造注入

            在我们的User Action里面添加属性及有参无参的构造方法,一个输出打印的方法toPrint()

            public class UserAction {
                private UserService us;
                private String name;
                private int age;
                private List<String> hobby;
                public UserAction() {
                }
                public UserAction(String name, int age, List<String> hobby) {
                    this.name = name;
                    this.age = age;
                    this.hobby = hobby;
                }
                public UserService getUs() {
                    return us;
                }
                public void setUs(UserService us) {
                    this.us = us;
                }
                public String updUser() {
                    us.updUser();
                    return "list";
                }
                public void toPrint() {
                    System.out.println(this.name);
                    System.out.println(this.age);
                    System.out.println(this.hobby);
                }
            }
            image.gif

            在print-context.xml里面设置值,使用构造输入需要用constructor-arg标签

            <bean class="com.tgq.ioc.web.UserAction" id="userAction">
                    <property name="us" ref="userService1"/>
                    <constructor-arg name="name" value="l"/>
                    <constructor-arg name="age" value="18"/>
                    <constructor-arg name="hobby">
                        <list>
                            <value>唱</value>
                            <value>跳</value>
                            <value>rap</value>
                        </list>
                    </constructor-arg>
                </bean>
            image.gif

            继续在测试类里面调用toPrint方法

            image.gif编辑

            image.gif编辑

            3、自动装配

            自动装配中byName和byType的区别:

            byName:在配置好的文件中变量名不更改就不会报错。按照属性的名称进行自动装配。

              1. 当一个bean的属性名称与其他bean的id匹配时,Spring容器会自动将该bean注入到对应的属性中。
              2. 如果找不到匹配的bean,则会抛出异常。
              3. 在XML配置中,可以使用autowire="byName"@Autowired注解实现byName自动装配。

              byType:会在整个spring中寻找JavaBean,按照属性的类型进行自动装配。

                1. 当一个bean的属性的类型与其他bean的class匹配时,Spring容器会自动将该bean注入到对应的属性中。
                2. 如果有多个匹配的bean,则会抛出异常,需要进行更具体的限定或使用@Qualifier注解来解决。
                3. 在XML配置中,可以使用autowire="byType"@Autowired注解实现byType自动装配。

                四、spring与tomcat的整合/spring与web容器的整合

                我们的xml文件

                <?xml version="1.0" encoding="UTF-8"?>
                <beans xmlns="http://www.springframework.org/schema/beans"
                       default-autowire="byName"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
                    <!--凡是在spring核心配置文件spring-context.xml中配置,那么该类JavaBean就交给了spring容器管理-->
                    <bean class="com.tgq.ioc.web.UserAction" id="userAction">
                        <property name="us" ref="userService1"/>
                    </bean>
                    <bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction">
                        <property name="us" ref="userService2"/>
                    </bean>
                    <bean class="com.tgq.ioc.service.impl.UserServiceImpl1" id="userService1"/>
                    <bean class="com.tgq.ioc.service.impl.UserServiceImpl2" id="userService2"/>
                </beans>
                image.gif

                image.gif编辑

                编写一个过滤器

                @WebListener
                public class sprintListener implements ServletContextListener {
                    @Override
                    public void contextInitialized(ServletContextEvent sce) {
                        System.out.println("初始化");
                        //spring的上下文
                        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
                //tomcat上下文
                        ServletContext servletContext = sce.getServletContext();
                        servletContext.setAttribute("sprintContext", context);
                    }
                }
                image.gif

                编写servlet类调用它

                @WebServlet("userlist")
                public class Userservlet extends HttpServlet {
                    @Override
                    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        this.doPost(req, resp);
                    }
                    @Override
                    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        //获取到sprint的上下文对象
                        ClassPathXmlApplicationContext Context = (ClassPathXmlApplicationContext) req.getServletContext().getAttribute("sprintContext");
                        UserService bean = (UserService) Context.getBean("userService1");
                        System.out.println(bean);
                        bean.updUser();
                    }
                }
                image.gif

                启动服务器就完成了整合

                image.gif编辑

                五、spring AOP

                Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的一个重要模块,提供了在应用程序中进行横切关注点的模块化支持。

                1、aop的特点

                  1. 非侵入性:Spring AOP采用编译期间的代理方式实现切面功能,不需要修改原始类的代码。使用者可以在不侵入原有代码的情况下,将切面逻辑插入到目标对象的方法中,实现对横切关注点的处理。
                  2. 松耦合:Spring AOP通过使用切面来处理横切关注点,将关注点的实现与主要业务逻辑进行分离。切面可以独立于应用程序进行开发和测试,并且可以通过配置和织入的方式来将切面应用到不同的目标对象上,实现代码的松耦合性。
                  3. 横切关注点的集中化管理:Spring AOP支持将相同的横切关注点集中定义在一个切面中,并在需要的地方进行切入。这使得横切关注点的管理更加方便,可以避免代码的重复。
                  4. 与IoC容器的集成:Spring AOP与Spring IoC容器无缝集成,可以借助IoC容器来管理切面和目标对象,实现AOP配置的灵活性和可扩展性。通过注解或XML配置,可以方便地将切面和目标对象注入到容器中并进行织入操作。
                  5. 支持丰富的切点表达式:Spring AOP支持使用切点表达式对目标对象的方法进行精确选择。切点表达式可以根据方法名称、参数、返回类型等条件来定义切点,灵活地进行切面织入。
                  6. 支持不同类型的通知:Spring AOP提供了多种类型的通知,如前置通知、后置通知、环绕通知、异常通知和最终通知。用户可以选择合适的通知类型,并定义切面的处理逻辑。
                  7. 可扩展性:Spring AOP对切面的实现支持继承和组合的机制,可以通过实现Advice接口或继承现有的Advice类来进行切面的定制和扩展。这样使得开发者能够根据具体需求来实现自定义的切面逻辑。

                  Spring AOP提供了一种灵活、非侵入性的横切关注点处理机制,通过将关注点的实现与主要业务逻辑分离,实现代码的解耦和管理的集中化。同时,与Spring IoC容器的集成以及丰富的切点表达式和通知类型,使得Spring AOP具有很高的扩展性和灵活性。

                  我们一般会用到系统的日志功能

                  2、AOP中关键性概念  

                  AOP中专业术语

                  名称 描述 注释
                  连接点(Joinpoint) 程序执行过程中明确的点,如方法的调用,或者异常的抛出.
                  目标对象(Target) 被通知(被代理)的对象 完成具体的业务逻辑
                  通知(Advice) 在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理) 完成切面编程
                  代理(Proxy) 将通知应用到目标对象后创建的对象(代理=目标+通知),例子:外科医生+护士 只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
                  切入点(Pointcut) 多个连接点的集合,定义了通知应该应用到那些连接点。 也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序
                  适配器(Advisor) 适配器=通知(Advice)+切入点(Pointcut)

                  3、前置通知

                  1.1、准备工作

                  如果需要可以创建如图一样的项目结构

                  image.gif编辑

                    1. 编写接口
                    public interface IBookBiz {
                        // 购物
                        public boolean buy(String userName, String bookName, Double price);
                        // 发表评论
                        public void comment(String userName, String comments);
                    }
                    1. image.gif
                    2. 实现上面的接口
                    public class BookBizImpl implements IBookBiz {
                      public BookBizImpl() {
                        super();
                      }
                      public boolean buy(String userName, String bookName, Double price) {
                        // 通过控制台的输出方式模拟购书
                        if (null == price || price <= 0) {
                          throw new PriceException("book price exception");
                        }
                        System.out.println(userName + " buy " + bookName + ", spend " + price);
                        return true;
                      }
                      public void comment(String userName, String comments) {
                        // 通过控制台的输出方式模拟发表书评
                        System.out.println(userName + " say:" + comments);
                      }
                    }
                    1. image.gif
                    2. 编写一个异常类
                    public class PriceException extends RuntimeException {
                      public PriceException() {
                        super();
                      }
                      public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
                        super(message, cause, enableSuppression, writableStackTrace);
                      }
                      public PriceException(String message, Throwable cause) {
                        super(message, cause);
                      }
                      public PriceException(String message) {
                        super(message);
                      }
                      public PriceException(Throwable cause) {
                        super(cause);
                      }
                    }
                    1. image.gif
                    2. 配置spring-context.xml
                    <?xml version="1.0" encoding="UTF-8"?>
                    <beans xmlns="http://www.springframework.org/schema/beans"
                           default-autowire="byType"
                           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
                        <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                    </beans>
                    1. image.gif
                    2. 编写一个测试类
                    public class demo {
                        public static void main(String[] args) {
                            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
                            IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz");
                            bookBiz.buy("小牛子", "钢铁是怎样炼成的", 99.9d);
                            bookBiz.comment("小牛子", "睡不着,来劲了");
                        }
                    }
                    1. image.gif
                    2. 测试结果image.gif编辑

                    1.2、前置通知

                    添加一个通知类,需要继承MethodBeforeAdvice 重写方法

                    public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
                      @Override
                      public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
                    //    在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
                        String target = arg2.getClass().getName();
                        String methodName = arg0.getName();
                        String args = Arrays.toString(arg1);
                        System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");
                      }
                    }
                    image.gif

                    配置通知类,代理,在代理里面配置目标对象和通知

                    <?xml version="1.0" encoding="UTF-8"?>
                    <beans xmlns="http://www.springframework.org/schema/beans"
                           default-autowire="byType"
                           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
                        <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                        <!--通知-->
                        <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
                        <!--    代理        -->
                        <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
                            <!--        配置目标对象        -->
                            <property name="target" ref="bookBiz"></property>
                            <!--        配置代理接口,目标对象接口        -->
                            <property name="proxyInterfaces">
                                <list>
                                    <value>com.tgq.aop.biz.IBookBiz</value>
                                </list>
                            </property>
                            <!--        配置通知        -->
                            <property name="interceptorNames">
                                <list>
                                    <value>beforeAdvice</value>
                                </list>
                            </property>
                        </bean>
                    </beans>
                    image.gif

                    编写测试类

                    public class demo {
                        public static void main(String[] args) {
                            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
                            IBookBiz bookBiz = (IBookBiz) context.getBean("factoryBean");
                            bookBiz.buy("小牛子", "钢铁是怎样炼成的", 99.9d);
                            bookBiz.comment("小牛子", "睡不着,来劲了");
                        }
                    }
                    image.gif

                    结果image.gif编辑

                    4、后置通知

                    在前面的基础上再写一个后置通知的类,需要实现AfterReturningAdvice ,重写afterReturning方法

                    public class MyAfterReturningAdvice implements AfterReturningAdvice {
                      @Override
                      public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
                        String target = arg3.getClass().getName();
                        String methodName = arg1.getName();
                        String args = Arrays.toString(arg2);
                        System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0);
                      }
                    }
                    image.gif

                    配置xml

                    <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                        <!--    前置通知-->
                        <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
                        <!-- 后置通知 -->
                        <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
                        <!--    代理        -->
                        <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
                            <!--        配置目标对象        -->
                            <property name="target" ref="bookBiz"></property>
                            <!--        配置代理接口,目标对象接口        -->
                            <property name="proxyInterfaces">
                                <list>
                                    <value>com.tgq.aop.biz.IBookBiz</value>
                                </list>
                            </property>
                            <!--        配置通知        -->
                            <property name="interceptorNames">
                                <list>
                                    <value>beforeAdvice</value>
                                    <value>myAfterReturningAdvice</value>
                                </list>
                            </property>
                        </bean>
                    image.gif

                    测试结果image.gif编辑

                    5、环绕通知

                    编写一个环绕通知类,实现MethodInterceptor接口,重写方法

                    public class MyMethodInterceptor implements MethodInterceptor {//Interceptor拦截器的意思
                        @Override
                        public Object invoke(MethodInvocation arg0) throws Throwable {
                            String target = arg0.getThis().getClass().getName();
                            String methodName = arg0.getMethod().getName();
                            String args = Arrays.toString(arg0.getArguments());
                            System.out.println("【环绕通知调用前:】:" + target + "." + methodName + "(" + args + ")被调用了");
                    //    arg0.proceed()就是目标对象的方法
                            Object proceed = arg0.proceed();
                            System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:" + proceed);
                            return proceed;
                        }
                    }
                    image.gif

                    配置xml

                    <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                        <!--    前置通知-->
                        <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
                        <!-- 后置通知 -->
                        <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
                        <!--    环绕通知-->
                        <bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/>
                        <!--    代理        -->
                        <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
                            <!--        配置目标对象        -->
                            <property name="target" ref="bookBiz"></property>
                            <!--        配置代理接口,目标对象接口        -->
                            <property name="proxyInterfaces">
                                <list>
                                    <value>com.tgq.aop.biz.IBookBiz</value>
                                </list>
                            </property>
                            <!--        配置通知        -->
                            <property name="interceptorNames">
                                <list>
                    <!--                <value>beforeAdvice</value>-->
                    <!--                <value>myAfterReturningAdvice</value>-->
                                    <value>myMethodInterceptor</value>
                                </list>
                            </property>
                        </bean>
                    image.gif

                    测试结果image.gif编辑

                    6、异常通知

                    编写一个异常通知类,实现ThrowsAdvice 接口,编写方法,方法名必须是afterThrowing

                    public class MyThrowsAdvice implements ThrowsAdvice {
                        public void afterThrowing(PriceException ex) {
                            System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!");
                        }
                    }
                    image.gif

                    配置xml文件

                    <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                        <!--    前置通知-->
                        <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
                        <!-- 后置通知 -->
                        <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
                        <!--    环绕通知-->
                        <bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/>
                        <!--异常通知-->
                        <bean class="com.tgq.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"/>
                        <!--    代理        -->
                        <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
                            <!--        配置目标对象        -->
                            <property name="target" ref="bookBiz"></property>
                            <!--        配置代理接口,目标对象接口        -->
                            <property name="proxyInterfaces">
                                <list>
                                    <value>com.tgq.aop.biz.IBookBiz</value>
                                </list>
                            </property>
                            <!--        配置通知        -->
                            <property name="interceptorNames">
                                <list>
                                    <!--                <value>beforeAdvice</value>-->
                                    <!--                <value>myAfterReturningAdvice</value>-->
                                    <value>myMethodInterceptor</value>
                                    <value>myThrowsAdvice</value>
                                </list>
                            </property>
                        </bean>
                    image.gif

                    测试,如果我们的价格有问题会出现异常通知

                    public class demo {
                        public static void main(String[] args) {
                            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
                    //        IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz");
                            IBookBiz bookBiz = (IBookBiz) context.getBean("factoryBean");
                            bookBiz.buy("小牛子", "钢铁是怎样炼成的", -99.9d);
                            bookBiz.comment("小牛子", "睡不着,来劲了");
                        }
                    }
                    image.gif

                    测试结果image.gif编辑

                    7、过滤通知

                    我们配置我们的过滤通知,需要在xml里面进行配置

                    <!--aop的javabean-->
                        <!--对象-->
                        <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
                        <!--    前置通知-->
                        <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
                        <!-- 后置通知 -->
                        <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
                        <!--    环绕通知-->
                        <bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/>
                        <!--异常通知-->
                        <bean class="com.tgq.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"/>
                        <!--    过滤通知-->
                        <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor">
                            <!--        给我们的后置通知做过滤-->
                            <property name="advice" ref="myAfterReturningAdvice"/>
                            <!--        给我们的购物做过滤-->
                            <property name="pattern" value=".*buy"/>
                        </bean><!--正则匹配我们的切入点-->
                        <!--    代理        -->
                        <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
                            <!--        配置目标对象        -->
                            <property name="target" ref="bookBiz"></property>
                            <!--        配置代理接口,目标对象接口        -->
                            <property name="proxyInterfaces">
                                <list>
                                    <value>com.tgq.aop.biz.IBookBiz</value>
                                </list>
                            </property>
                            <!--        配置通知        -->
                            <property name="interceptorNames">
                                <list>
                                    <value>beforeAdvice</value>
                                    <!--                <value>myAfterReturningAdvice</value>-->
                                    <!--                使用过滤通知需要把原有的配置注释或者删除-->
                                    <value>regexpMethodPointcutAdvisor</value>
                                    <value>myMethodInterceptor</value>
                                    <value>myThrowsAdvice</value>
                                </list>
                            </property>
                        </bean>
                    </beans>

                    image.gif

                    测试结果,我们的评论后就没有返利了,我们只有购物后评论前才有返利image.gif编辑

                    8、AOP的总结

                    8.1、概念

                    aop是面向切面编程,程序是由上至下执行,但是aop面向切面编程不是,aop的程序执行,首先当程序执行到目标对象的目标方法时,如果连接点上由前置通知,则先执行前置通知,再执行目标方法,如果没有前置通知,则先执行前置通知,再执行目标方法,如果没有前置通知,则继续执行目标方法;再查看目标方法上是否有后置通知,如果有,则再执行后置通知代码。

                    8.2、应用

                    不管是前置通知、后置通知、环绕通知、异常通知还是过滤通知,代码都是非业务核心代码,比如日志和事务的管理。

                    我们还可以去学习一下:jdk代理与cglib代理,aop数据字典等其他

                    六、spring中bean的生命周期

                      1. 初始化 init
                      2. 使用 service
                      3. 销毁 destroy

                      1、spring管理JavaBean初始化过程

                      1.1、思维导图

                      image.gif编辑

                      BeanDefinition:包含了很多属性和方法。例如:id、class(类名)、

                      scope、ref(依赖的bean)等等。其实就是将bean(例如<bean>)的定义信息

                      存储到这个对应BeanDefinition相应的属性中

                      例如:

                      <bean id="" class="" scope=""> -----> BeanDefinition(id/class/scope)
                      image.gif

                      Aware感知接口:在实际开发中,经常需要用到Spring容器本身的功能资源。

                      例如:BeanNameAware、ApplicationContextAware等等

                      BeanDefinition 实现了 BeanNameAware、ApplicationContextAware

                       

                      BeanPostProcessor:后置处理器。在Bean对象实例化和引入注入完毕后,

                      在显示调用初始化方法的前后添加自定义的逻辑。(类似于AOP的绕环通知)

                      前提条件:如果检测到Bean对象实现了BeanPostProcessor后置处理器才会执行

                      Before和After方法

                      BeanPostProcessor

                       

                      BeanDefinitionReader:解析Bean的定义。在Spring容器启动过程中,会将Bean解析成Spring内部的BeanDefinition结构;

                      理解为:将spring.xml中的<bean>标签转换成BeanDefinition结构

                      有点类似于XML解析

                       

                      BeanPostProcessor:后置处理器。在Bean对象实例化和引入注入完毕后,

                      在显示调用初始化方法的前后添加自定义的逻辑。(类似于AOP的绕环通知)

                      前提条件:如果检测到Bean对象实现了BeanPostProcessor后置处理器才会执行

                      Before和After方法

                      BeanPostProcessor

                      1.2、spring容器管理JavaBean的初始化过程(spring中bean的生命周期)。

                        1. xml/annotation/configuation配置JavaBean
                        2. BeanDefinitionReader解析配置的JavaBean得到BeanDefinition,最终得到List<BeanDefinition>集合。
                        3. 触发BeanFactoryPoatProcessor,JavaBean初始化之前执行自己的业务。
                        4. spring中beanFactory,会通过List<BeanDefinition>集合遍历初始化所有的JavaBean对象。
                        5. 如果自己的JavaBean需要调动spring上下文中的资源,那么需要实现*Aware感知接口
                        6. 如果自己的JavaBean已经初始化好了,还需要扩展功能,那么需要借助BeanPostProcessor来实现。

                        2、spring的JavaBean管理中单例模式及原型模式

                        2.1、spring中JavaBean是单例还是多例。

                          1. 默认是单例的,但是可以配置多例的。
                          2. 单例的优点:节约内存;弊端:有变量污染;
                            多例的优点:无变量污染;弊端:消耗内存;
                          public class ParamAction {
                              private int age;
                              private String name;
                              private List<String> hobby;
                              private int num = 1;//初始值
                              // private UserBiz userBiz = new UserBizImpl1();
                              public ParamAction() {
                                  super();
                              }
                              public ParamAction(int age, String name, List<String> hobby) {
                                  super();
                                  this.age = age;
                                  this.name = name;
                                  this.hobby = hobby;
                              }
                              public void execute() {
                                  // userBiz.upload();
                                  // userBiz = new UserBizImpl2();
                                  System.out.println("this.num=" + this.num++);
                                  System.out.println(this.name);
                                  System.out.println(this.age);
                                  System.out.println(this.hobby);
                              }
                          }
                          1. image.gif
                             
                          @org.junit.Test
                              public void test1() {
                                  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
                          //        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
                                  ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
                                  ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
                                  System.out.println("p1 == p2:" + (p1 == p2));
                                  if (p1 != null && p2 != null) {
                                      p1.execute();
                                      p2.execute();
                                  }
                                  applicationContext.close();
                                  //    单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;
                              }
                          1. image.gif xml配置
                          <!--    spring的生命周期-->
                              <!--  默认单例模式  -->
                              <!--多例模式:prototype--><!--单例模式:singleton-->
                              <bean id="paramAction" class="com.tgq.beanLife.ParamAction" scope="singleton">
                                  <constructor-arg name="name" value="三丰"></constructor-arg>
                                  <constructor-arg name="age" value="21"></constructor-arg>
                                  <constructor-arg name="hobby">
                                      <list>
                                          <value>抽烟</value>
                                          <value>烫头</value>
                                          <value>大保健</value>
                                      </list>
                                  </constructor-arg>
                              </bean>
                          1. image.gifimage.gif编辑
                          2. 单例:JavaBean是跟着spring上下文初始化的;(容器生对象生,容器死对象死)
                            多例:JavaBean是使用的时候才会创建,销毁跟着jvm走。
                             
                          public class InstanceFactory {
                            public void init() {
                              System.out.println("初始化方法");
                            }
                            public void destroy() {
                              System.out.println("销毁方法");
                            }
                            public void service() {
                              System.out.println("业务方法");
                            }
                          }
                          1. image.gif xml配置
                          <bean id="instanceFactory" class="com.tgq.beanLife.InstanceFactory"
                                    scope="singleton" init-method="init" destroy-method="destroy"></bean>
                          1. image.gif
                          @org.junit.Test
                              public void test2() {//
                          //        多例不会调用我们的初始化方法
                                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
                          //多例模式的时候,只要调用就会初始化
                                  InstanceFactory instanceFactory = (InstanceFactory) applicationContext.getBean("instanceFactory");
                              }
                          1. image.gifimage.gif编辑
                          // BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式
                              // 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化
                              @org.junit.Test
                              public void test3() {
                                  // ClassPathXmlApplicationContext applicationContext = new
                                  // ClassPathXmlApplicationContext("/spring-context.xml");
                                  Resource resource = new ClassPathResource("/spring-context.xml");
                                  BeanFactory beanFactory = new XmlBeanFactory(resource);
                          //        调用会初始化
                          //    InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
                              }

                          image.gif

                          什么情况下会使用多例:一个类里面的属性值有变化,会使用多例

                          相关实践学习
                          日志服务之使用Nginx模式采集日志
                          本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
                          相关文章
                          |
                          1天前
                          |
                          Java 开发者 Spring
                          Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
                          【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
                          9 5
                          |
                          1天前
                          |
                          XML Java 数据格式
                          Spring AOP
                          【5月更文挑战第1天】Spring AOP
                          13 5
                          |
                          2天前
                          |
                          Java 编译器 开发者
                          Spring的AOP理解
                          Spring的AOP理解
                          |
                          2天前
                          |
                          XML Java 数据格式
                          如何在Spring AOP中定义和应用通知?
                          【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
                          8 0
                          |
                          2天前
                          |
                          安全 Java 开发者
                          在Spring框架中,IoC和AOP是如何实现的?
                          【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
                          10 0
                          |
                          2天前
                          |
                          Java 测试技术 开发者
                          Spring IoC容器通过依赖注入机制实现控制反转
                          【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
                          9 0
                          |
                          2天前
                          |
                          XML Java 程序员
                          什么是Spring的IoC容器?
                          【4月更文挑战第30天】什么是Spring的IoC容器?
                          7 0
                          |
                          2天前
                          |
                          Java 测试技术 开发者
                          【亮剑】如何通过自定义注解来实现 Spring AOP,以便更加灵活地控制方法的拦截和增强?
                          【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
                          |
                          2天前
                          |
                          缓存 监控 Java
                          【Spring系列笔记】AOP
                          面向切面编程就是面向特定方法编程。通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,提供一种更好的代码模块化和可维护性。 横切关注点指的是在应用程序中横跨多个模块或层的功能,例如日志记录、事务管理、安全性、缓存、异常处理等。
                          14 0
                          |
                          3天前
                          |
                          运维 Serverless 应用服务中间件
                          Serverless 应用引擎产品使用之在Serverless应用引擎(SAE)中,在创建ALB时找不到对应的SAE容器如何解决
                          阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
                          10 0