相关注解可参考: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类
java">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。
java">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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://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代码如下。
java">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中
java">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中的配置一致
java">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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://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代码如下。
java">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);}
}