Spring IOC 和 AOP

相关注解可参考:http://t.csdnimg.cn/yfUKt

1.什么是IOC和AOP

        Spring框架中的IOC(Inverse of Control,控制反转)和AOP(Aspect-Oriented Programming,面向切面编程)是其两大核心特性,它们共同为Java应用程序带来了更高的灵活性、可维护性和松耦合度。

IoC(控制反转)

概念: IoC是一种设计模式,它提倡不直接在代码中创建对象,而是将对象的创建和依赖关系的管理交由一个外部容器(在Spring中,这个容器就是IoC容器)来处理。这样做的好处是降低了组件之间的耦合度,使得组件更易于测试和重用。

工作原理

  • Bean定义:通过XML配置文件、注解或Java配置类定义Bean及其依赖关系。
  • Bean初始化:Spring容器读取这些配置,负责创建Bean实例,并管理Bean的整个生命周期,包括实例化、属性注入、初始化、销毁等。
  • 依赖注入(DI):Spring容器根据配置自动将依赖的Bean注入到需要它们的Bean中,无需Bean自己管理依赖。

AOP(面向切面编程)

概念: AOP是一种编程范式,用于解决那些遍布于各个模块中的交叉关注点(如日志记录、事务管理、安全检查等)的编程问题。它通过定义所谓的“切面”来封装这些横切关注点,然后将其插入到应用程序的其他部分中,而无需修改这些部分的代码。

工作原理

  • 切面(Aspect):封装了横切关注点的模块,如事务管理逻辑就是一个切面。
  • 切点(Pointcut):定义了切面应该在何处应用的规则,可以通过表达式指定匹配的类或方法。
  • 通知(Advice):切面中的实际操作,比如在方法执行前做什么(前置通知)、执行后做什么(后置通知)等。
  • 代理(Proxy):Spring AOP通常通过动态代理(JDK动态代理或CGLIB)来创建目标对象的代理,以实现在不修改原有代码的情况下插入切面逻辑。

IoC与AOP的关系

IoC是Spring的基础,它负责创建Bean并管理Bean之间的依赖关系,而AOP则是建立在IoC之上的高级编程模型,利用IoC容器管理的Bean作为切面操作的目标。两者相辅相成,共同构成了Spring框架强大功能的核心部分,使得开发者能够更加专注于业务逻辑的实现,而非琐碎的基础设施代码。

1.1 IOC(控制反转)

1.1.1 基于XML的形式

1.1.2 基于注解的形式

1.1.2.1 配置类

  • 通过类型去获取:

  • 通过id(名字)去获取:

  • 通过包名也可以进行加载:

1.1.2.2 扫包+注解

1.2 DI(依赖注入)

自动创建对象,完成依赖注入

@Autowried 是通过类型进行注入(byType),如果需要按名字取值(byNamew),就使用@Autowried +@Qualifier的形式,完成名称的映射

注:@Qualifier("config")与@Component("config")名字保持一致

@Resource有两个属性是比较重要的,分别是name和type,默认按byName进行注入,如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType;Spring将@Resource注解的name属性解析为bean的名称,type属性解析为bean的类型,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName。

@Resource和@Autowired的区别-CSDN博客

1.3 AOP(面相切面编程)


        面向切面编程,是一种抽象化的面相对象编程,对面向对象编程的一种补充。底层使用动态代理机制来实现。

打印日志:

    • 为了将核心业务与非业务代码进行解耦,所以需要先拆开。
    • 但又不能影响到之前的功能,所以拆开后还需要合并。
    • 合并成一个代理,对代理进行编程。

1.3.1 基于XML配置

(1)新建Maven项目,添加引用,项目的pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>springAop</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.15</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.15</version>
    </dependency>
  </dependencies>
</project>

(2)创建要被代理的Math类

package com.zsh.aop;

/**
 * 被代理的目标类
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:46
 */
public class Math {
    //加
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }

    //减
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }

    //乘
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }

    //除
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}

(3)编辑AOP中需要使用到的通知类Advices。

package com.zsh.aop;

import org.aspectj.lang.JoinPoint;

import java.util.Arrays;

/**
 * 通知类,横切逻辑
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:47
 */
public class Advices {
    public void before(JoinPoint jp){
        System.out.println("----------前置通知----------"+jp.getSignature().getName()+"方法执行了,参数为"+ Arrays.toString(jp.getArgs()));
    }

    public void after(JoinPoint jp,Object result){
        System.out.println("----------最终通知----------"+jp.getSignature().getName()+"方法执行了,结果为:"+result);
    }
}

(4)配置容器初始化时需要的xml文件。

<?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:p="http://www.springframework.org/schema/p"
  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-4.3.xsd">

  <!-- 被代理对象 -->
  <bean id="math" class="com.zsh.aop.Math"></bean>

  <!-- 通知 -->
  <bean id="advices" class="com.zsh.aop.Advices"></bean>

  <!-- aop配置 -->
  <aop:config proxy-target-class="true">
    <!--切面 -->
    <aop:aspect ref="advices">
      <!-- 切点 -->
      <aop:pointcut expression="execution(* com.zsh.aop.Math.*(..))" id="point1"/>
      <!--连接通知方法与切点 -->
      <aop:before method="before" pointcut-ref="point1"/>
      <!--            <aop:after method="after" pointcut-ref="point1"/>-->
      <aop:after-returning method="after" pointcut-ref="point1" returning="result"/>
    </aop:aspect>
  </aop:config>

</beans>

(5)测试Test代码如下。

package com.zsh.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:49
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        Math math = ctx.getBean("math", Math.class);
        int n1 = 66, n2 = 33;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

1.3.2 基于注解配置

注:在基于XML配置的基础上进行修改

(1)修改被代理的Math类,在Math类的基础上使用注解,注入到spring中

package com.zsh.aop;

import org.springframework.stereotype.Component;

/**
 * 被代理的目标类
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:46
 */
@Component("math")
public class Math {
    //加
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }

    //减
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }

    //乘
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }

    //除
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}

(2)修改AOP中需要使用到的通知类Advices,这里修改后,基本与之前xml中的配置一致

package com.zsh.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 通知类,横切逻辑
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:47
 */
@Component
@Aspect
public class Advices {
    @Before("execution(public int com.zsh.aop.Math.*(..))")
    public void before(JoinPoint jp){
        System.out.println("----------前置通知----------"+jp.getSignature().getName()+"方法执行了,参数为"+ Arrays.toString(jp.getArgs()));
    }
    @AfterReturning(value = "execution(public int com.zsh.aop.Math.*(..))",returning = "result")
    public void after(JoinPoint jp,Object result){
        System.out.println("----------最终通知----------"+jp.getSignature().getName()+"方法执行了,结果为:"+result);
    }
}
  • @Component表示该类的实例会被Spring IOC容器管理;
  • @Aspect表示声明一个切面;
  • @Before表示before为前置通知,通过参数execution声明一个切点
  • @AfterReturning后置通知:
    • value = "execution(public int com.zsh.aop.Math.*(..))":这一部分定义了切点表达式,指定了AOP要应用的范围。具体来说:
      • execution是关键字,用于匹配方法执行的连接点。
      • public int指定了匹配的方法必须是具有public访问权限并且返回类型为int的方法。
      • com.zsh.aop.Math.*指定了匹配的类位于com.zsh.aop.Math包下,*表示Math类中的所有方法。
      • (..)表示不关心方法的参数列表,即匹配任意参数。
    • returning = "result":此属性指定了一个形参名称(在此例中为"result"),该形参将在通知方法中用来接收被织入切面的方法的返回值。也就是说,在执行完匹配的方法后,如果该方法有返回值,这个返回值会被传递给通知方法中对应名称的参数。

(4)配置容器初始化时需要的xml文件。

<?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:p="http://www.springframework.org/schema/p"
       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-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启自动扫描包-->
    <context:component-scan base-package="com.zsh.aop"></context:component-scan>
    <!--开启aop自动代理-->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

</beans>
  • 组件扫描 (`context:component-scan):
    • 功能: <context:component-scan> 标签用于指示Spring在指定的包(及其子包)中搜索带有@Component、@Service、@Repository、@Controller等注解的类,并自动注册这些类为Spring管理的Bean。在这个例子中,它会扫描com.example.spring_aop.aop包及其所有子包。
  • AOP自动代理 (`aop:aspectj-autoproxy):
    • 功能: <aop:aspectj-autoproxy> 配置用于开启Spring对AspectJ切面的支持,它会自动为符合条件的Bean创建代理对象,从而实现切面逻辑(如日志记录、权限校验等)的织入。这使得开发者无需手动配置代理就能使用AOP。

(5)测试Test代码如下。

package com.zsh.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author ZhaoShuHao
 * @Date 2024/4/29 14:49
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        //根据类型
        //        Math math = ctx.getBean(Math.class);
        //根据名称
        Math math = ctx.getBean("math",Math.class);
        int n1 = 66, n2 = 33;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值