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的基本概念如下:
(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)和其他对象连接起来, 并创建 Advice
d 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>
注意:切面类要添加注解@Aspect,不然不会触发aop的那几个注解,比如@Before 等。
参考连接:https://blog.csdn.net/q982151756/article/details/80513340 (例子很形象)
https://www.cnblogs.com/liuruowang/p/5711563.html