【Spring】IoC,DI,两种代理方式,AOP定义和使用


前言


一、Spring框架中的核心思想?

两个思想:控制反转(IoC, Inversion of Control) 、 面向切面编程(AOP, )

IoC
定义:控制反转:控制权力的反转。
解释:将原来手动通过new关键字创建对象的权力交给了Spring,通过在配置文件中配置bean标签的形式创建对象,交给Spring 由Spring创建对象的过程。

可是,像Tomcat容器,也能够做到不需要new关键字,就可以使用Servlet,(其实背后就是控制反转)。那么控制反转并不是Spring所特有的特性。
因此,Spring在基于IOC基础上,提出了“依赖注入”(DI,Dependency Injection)。

我们可以发现Service层经常需要调用DAO层,即需要DAO组件对象, 这种 “需要” 可以称为 “依赖”,即 依赖DAO组件。

因为DAO组件对象已经在Spring中生成了,那么下次Service层在调用时,只需要在Spring容器中取就好了。
在这里插入图片描述
像此时,deptDAO只是有声明,如果需要时在12行直接调用save(),会报空指针异常。
那么 第一步 需要谁。就先声明谁为成员变量,
第二步:为成员变量赋值,为成员变量赋值的过程 ,就称为依赖注入。

1.1依赖注入(DI)

定义:为组件中成员变量完成赋值过程 ,这个过程就称之为 依赖注入。
语法:
1.组件对象中需要哪个组件, 就将谁声明为成员变量 并提供公开的SET方法。(示例如下:)
(提供get方法,会出现代码冗余)
在这里插入图片描述
只有上面操作还不够,还要经过第二步
2.在Spring的配置文件中对应的 组件标签内 完成属性的赋值操作。
下面就是依赖注入的示例:

在这里插入图片描述
以上就是依赖注入的过程。
并且可以总结得知:通过IOC可以创建组件对象, 通过DI 维护了组件与组件之间的调用关系
此时
IOC的全部定义概述为:

即控制反转, 将原来手动通过new关键字创建对象的权力交给Spring,由Spring工厂创建对象的过程。
当然,Spring不仅创建对象还要再创建对象的同时,通过DI的方式维护组件与组件的调用关系。

DI(set方式注入)基本语法:
DAO层:
在这里插入图片描述
Service层:
在这里插入图片描述
配置文件的赋值:

在这里插入图片描述
启动工厂测试:
在这里插入图片描述
其实在Spring中注入方式主要有三种:
(重点)a.SET注入 : 使用成员变量SET方法形式进行赋值
(了解)b.构造注入 : 使用构造方法形式 进行属性的赋值
(了解)c.自动注入 : 通过在配置文件中 完成类中属性自动赋值

Spring中SET方法的注入语法:
上面已经讲过的,注入时,把组件对象声明,然后生成SET方法,
但如果需要注入的不是对象, 而是一个String属性值、Date类型的还和上面的语法一样吗?
可以通过在xml文件中选用value对 八种基本数据类型 + String类型 + Date 类型 统一使用 value属性进行注入

在这里插入图片描述
在这里插入图片描述
Spring中对日期类型的值注入,只能使用格式为value=“yyyy/MM/dd HH:mm:ss”
在这里插入图片描述
总结:

在这里插入图片描述

在这里插入图片描述
对于Map类型,其键值对,根据具体情况具体写,总体原则是,对象类型就要用ref, String等类型用value。例如:key-ref,value-ref等
在这里插入图片描述
对于properties配置文件,里面也是key-value:,里面都是放String,所以没有什么key-ref:

在这里插入图片描述

【总结】IOC DI

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、Spring-面向切面编程(AOP)

面向切面的编程,:(AOP,Aspect Oriented Programing )
底层原理:java 代理设计模式 动态代理
好处:在保证 原始业务功能不变的情况下, 通过代理对象完成 业务中附加操作,将业务中核心操作 放在目标对象中执行,实现了附加操作和核心操作的解耦
在说AOP之前,需要先明白代理设计模式
java: (Proxy)代理设计模式

2.1代理模式举例

小张和小丽网恋,小张(业务逻辑调用者,Main())给小丽的账号名(如花)(接口Interface)发消息,本来小丽要给闺蜜做饭,所以就让小丽闺蜜(代理对象Proxy)帮自己打字,并转述聊天内容。在这个过程中小张是不知道手机对面是谁在和自己聊天(接口屏蔽了底层的具体实现逻辑)。
小丽闺蜜的存在,减轻了小丽的负担,保证了原始业务逻辑不变,同时还能使小丽完成一些附加事情。

在这里插入图片描述

2.2如何开发一个代理对象?

2.2.1 静态代理对象

a) 代理对象和 业务逻辑对象 (真正的业务逻辑对象) 实现相同的接口
b) 代理对象中 依赖 真正业务逻辑对象
先看看原始业务逻辑对象的代码:存在大量冗余(忙)
在这里插入图片描述
以上代码可以看到,每个方法都出现了大量的代码冗余,(开启事务,提交事务…)
有代理的存在和无代理的存在,能完成的原始功能是一致的,但是有代理的话,可以让原始业务逻辑对象更加专注去处理原来的核心业务(处理DAO)。
(静态)代理对象的代码:

在这里插入图片描述

spring.xml中:
在这里插入图片描述

因此有了代理存在,就 不能再调用原始的业务逻辑对象,而是 应该调用代理对象,才能保证原始大的功能不变。

在这里插入图片描述
上面讲的,为每一个业务层 通过手动开发一个代理对象的过程 称之为 “静态代理对象”

存在问题很明显,静态代理开发,比原先更加复杂了,(需要多写一个类),如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,
甚至让工作量多了一倍。

解决方案为业务层在运行过程中动态创建代理类,通过动态代理类去解决现有业务层中业务代码冗余问题。

2.2.2动态代理对象

代理对象:保证原始功能不变的情况下,完成业务逻辑中附加操作
原始业务逻辑对象:也叫Target,目标对象| 被代理对象称之为目标对象, 原始业务逻辑对象
什么是动态代理:
在程序运行过程中,通过代码底层的方式,为指定的类创建动态代理对象,(不是手工开发的,是根据接口创建的)

proxy 用来生成动态对象的类
参数1:classLoader 类加载器
参数2:Class[] 目标对象的接口的类型的数组 (一个类可以实现多个接口,所以是数组)
参数3: InvocationHandler 接口类型 invoke 方法用来书写额外功能(附加操作)

UserService userService = new UserServiceImpl(); //目标类
//类加载器,
ClassLoader classLoader = Thread.currentThread().getContextClassLoader()
//Class[] 目标对象的接口 的类型 的数组
Class[] classes = {UserService.class};
//返回值:创建好的动态代理对象:
//(为目标类创建代理对象,方式是,通过底层的classLoader类加载器,
//读到UserServiceImpl的类信息,把信息加载到内存中,然后才能对应生成动态代理)
Uservice userServiceDynamicProxy = (UserService)Proxy.newProxyInstancce(classLoader, classes,new InvocationHandler(){
//通过动态代理对象调用自己里面代理方法时 会优先指定InvocationHandler类中invoke方法
	@Override
//参数1:当前创建好的代理对象; 参数2:当前代理对象执行的方法对象, 参数3:当前代理对象执行方法的参数
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
	System.outprintln("当前执行的方法:"+method.getName());
	System.outprintln("当前执行的方法的参数:"+args[0]);
	try{
		System.outprintln("开启事务");
	//调用目标类中业务方法,通过反射机制,调用目标类中当前方法
		Object invoke = method.invoke(new UserServiceImpl(), args)
		System.outprintln("提交事务");
		return invoke;
	}catch(Exception e){
		e.printStackTrace();
		System.out.println("回滚事务");
	}
	return null;
	
		}
	});	
String name =proxy.findAll("小明");	//通过动态代理对象调用代理中的方法,执行这里时,会跳到上面执行@Override的方法

第一个参数是类加载器,为什么呢?
读接口的信息,底层操作字节码,读字节码 动态生成对象,classLoader就是用来读class文件,读类信息的
classes,告诉基于哪个目标类的接口,去生成代理对象

2.3面向切面编程

下图方框中标记的代码,我们称为附加操作,附加操作在AOP中被称为通知(Advice)
通知(Advice):除了目标方法以外的操作都称之为通知 附加功能 事务通知 日志通知 性能通知…
切入点(Pointcut): 指定开发好的 通知 应用于项目中 哪些组件中 哪些方法,通常通知多用于业务层
切面(Aspect):通知(Advice) + 切入点 (pointcut)
在这里插入图片描述
环绕通知:围绕目标方法执行的通知

在这里插入图片描述

AOP 面向切面编程:
1.开发通知类(附加功能)
2.配置切面点
3.组装切面(Aspect)

2.3.1Spring aop 编程的编程步骤

1.引入aop编程相关依赖

spring-aop、 spring-expression 、spring-aspect

2.项目开发额外功能通知

环绕通知 MethodIntercept
前置通知 MethodforAdvice
后置通知 AfterReturningAdvice
异常通知 ThrowsAdvice

3.配置切面 (在spring.xml中完成)
a).注册通知类

<bean id ="" class="xxxAdvice">

b).组装切面 aspect = advice + pointcut

<aop:config>
	<aop:pointcut id="" expression="execution()"/>
	<aop:advisor advice-ref="" pointcut-ref=""/>
</aop:config>

2.3.2aop编程举例

在这里插入图片描述
上面程序中第8、14行,只是方法名称不同,存在冗余,因此可通过切面编程优化。
解决: 自己写一个前置通知,这个要实现框架中提供的接口 (implements MethodBeforeAdvice)

在这里插入图片描述
上述代码中,expression="execution(* aop.EmpServiceImpl.*(..))"
第一个*代表 可 任意类型的返回值
第二个 参数 aop.EmpServiceImpl.*(..)这个包名 表示 切入点在 aop.EmpServiceImpl类下的所有方法。如果写成aop.*ServiceImpl.*.(..)则表示,切入点在aop包下的所有以ServiceImpl结尾的类,都会切入切面。

上述在spring.xml中配置的代码,只要发现了有<aop.advisor>,那么就会根据该句中的pointcut-ref=""/>生成动态代理对象,

2.4总结

在这里插入图片描述
在这里插入图片描述
说明:
在implements MethodBeforeAdvice之后,重写的这个before()已经把InvokHandler中的invoke方法做了提取,提取成了 各种各样的通知,
在这里插入图片描述
这才使得,当只要切入成功,这里通过getBean得到的empService对象变成了代理对象, 它会优先执行代理对象的方法。然后再调用dao执行业务逻辑方法
在这里插入图片描述

三、如何用Spring创建复杂对象?

3.1Spring工厂获取对象的原理(回顾)

在这里插入图片描述
为什么经过上面的a)b)c)三步,工厂可以获取到组件对象?
因为是反射机制
通过Class.forName("全限定名"),读取类的信息,然后又调用 newInstance()调用类的构造方法进行对象的创建

3.通过工厂创建简单对象
简单对象:可以直接通过new关键字 创建的对象 ,统一称为 简单对象
工厂创建简单对象时直接通过<bean id="xxx" class="xxx">完成

4.通过工厂创建复杂对象
复杂对象: 不能直接通过new关键字进行创建对象,例如:接口类型 | Collection | 抽象类类型 Calender(日历)/ MessageDigest(加密)…

对象名FactoryBean  implements  FactoryBean<Calender|Connection|复杂对象>{
}
例如:创建Calender复杂对象,1) implements+ 2) @override
public class CalenderFactoryBean implements FactoryBean<Calender> {
	//用来书写复杂对象的创建方式
	@Override
	public Calender getObject() throws Exception{
		return Calender.getInstance();
	}
	//指定创建的复杂对象的类型
	@Override
	public Class<?> getObjectType(){
		return Calender.class;
	}
	//用来指定创建的对象模式,单例或多例
	@Override
	public boolean isSingleton(){
		return false;
	}
}

工厂创建日历(复杂)对象:
在这里插入图片描述
在这里插入图片描述

工厂创建连接对象:
在这里插入图片描述
在工厂中获取复杂对象的方式也是用<bean>

代码如下(示例):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值