spring基础知识(IOC、DI、AOP)
一、spring概述Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。减轻对项目模块之间的管理,类和类之间的管理, 帮助开发人员创建对象,管理对象之间的关系。Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用
目录
3.1Spring的第一个程序——spring创建对象的步骤
一、spring概述
Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。减轻对项目模块之间的管理, 类和类之间的管理, 帮助开发人员创建对象,管理对象之间的关系。
Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。
二、spring核心功能 ioc控制反转
1、什么是IOC
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
IOC:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
2、为什么要使用IOC
目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
3、IOC的技术实现——DI依赖注入
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
3.1Spring的第一个程序——spring创建对象的步骤
1.创建maven项目
2.加入maven的依赖
spring的依赖,版本5.2.5版本【spring-context】
junit依赖【junit】
3.创建类(接口和它的实现类)
和没有使用框架一样, 就是普通的类。
4.创建spring需要使用的配置文件
在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml。声明类的信息,这些类由spring创建和管理
实例化方式
(一)默认构造 <bean id="" class=""/>:用于定义一个实例对象。一个实例对应一个 bean 元素。
id:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,Bean 与 Bean 间的依赖关系也是通过 id 属性关联的。
class:指定该 Bean 所属的类,注意这里只能是类,不能是接口。(二)静态工厂<bean id="" class="工厂类" factory-method="静态方法">
静态工厂:用于生成实例对象,所有的方法必须是static
public class MyBeanFactory { public static UserService createService(){ return new UserServiceImpl(); } }
<!-- 将静态工厂创建的实例交予spring class 确定静态工厂全限定类名 factory-method 确定静态方法名 --> <bean id="userServiceId" class="com.itheima.c_inject.b_static_factory.MyBeanFactory" factory-method="createService"></bean>
(三)实例工厂:<bean id="工厂id" class="工厂类"> <bean id="" factory-bean="工厂id" factory-method="方法">
必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
<!-- 创建工厂实例 --> <bean id="myBeanFactoryId" class="com.itheima.c_inject.c_factory.MyBeanFactory"></bean> <!-- 获得userservice * factory-bean 确定工厂实例 * factory-method 确定普通方法 --> <bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean>
5.测试spring创建的类
若 Spring 配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现类进行加载。
测试方法规范:
1)public 方法
2)没有返回值 void
3)方法名称自定义,建议名称是test + 你要测试方法名称
4)方法没有参数
5)方法的上面加入 @Test ,这样的方法是可以单独执行的。 不用使用main方法。
3.2 DI依赖注入
di:依赖注入,表示创建对象,给属性赋值。
di的实现有两种:
1.在spring的配置文件中, 使用标签和属性完成,叫做基于XML的di实现
2.使用spring中的注解,完成属性赋值, 叫做基于注解的id实现
di的语法分类:
1. set注入(设置注入): spring调用类的set方法,在set方法可以实现属性的赋值。
2. 构造注入,spring调用类的有参数构造方法,创建对象。在构造方法中完成赋值。
3.2.1基于XML的DI
(1)set注入
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用
需要有set方法,没有set方法是报错的。并且调用的是默认的无参构造函数,如果重写了构造函数,一定要手动加入无参的构造函数才可
set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值
1)简单类型的set注入
<bean id="xx" class="yyy">
<property name="属性名字" value="此属性的值"/>
一个property只能给一个属性赋值
<property....>
</bean>2) 引用类型的set注入 : spring调用类的set方法
<bean id="xxx" class="yyy">
<property name="属性名称" ref="bean的id(对象的名称)" />
</bean>
(2)构造注入
构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。构造注入使用 <constructor-arg> 标签
<constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。
<constructor-arg> 标签属性:
name:表示构造方法的形参名
index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序
value:构造方法的形参类型是简单类型的,使用value
ref:构造方法的形参类型是引用类型的,使用ref
(3)引用类型的自动注入
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为<bean/>标签,设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属
性)。根据自动注入判断标准的不同,可以分为两种:
- byName:根据名称自动注入
- byType: 根据类型自动注入
引用类型的自动注入: spring框架根据某些规则可以给引用类型赋值。·不用你在给引用类型赋值了
使用的规则常用的是byName, byType.
1.byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,
且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。
语法:
<bean id="xx" class="yyy" autowire="byName">
简单类型属性赋值
</bean>2.byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性
是同源关系的,这样的bean能够赋值给引用类型
同源就是一类的意思:
1.java类中引用类型的数据类型和bean的class的值是一样的。
2.java类中引用类型的数据类型和bean的class的值父子类关系的。
3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的
语法:
<bean id="xx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的
(4)集合类型注入
<bean id="collDataId" class="com.itheima.f_xml.e_coll.CollData" >
<property name="arrayData">
<array>
<value>DS</value>
<value>DZD</value>
<value>屌丝</value>
<value>屌中屌</value>
</array>
</property>
<property name="listData">
<list>
<value>于嵩楠</value>
<value>曾卫</value>
<value>杨煜</value>
<value>曾小贤</value>
</list>
</property>
<property name="setData">
<set>
<value>停封</value>
<value>薄纸</value>
<value>关系</value>
</set>
</property>
<property name="mapData">
<map>
<entry key="jack" value="杰克"></entry>
<entry>
<key><value>rose</value></key>
<value>肉丝</value>
</entry>
</map>
</property>
<property name="propsData">
<props>
<prop key="高富帅">嫐</prop>
<prop key="白富美">嬲</prop>
<prop key="男屌丝">挊</prop>
</props>
</property>
</bean>
集合的注入都是给<property>添加子标签
数组:<array>
List:<list>
Set:<set>
Map:<map> ,map存放k 键值对,使用<entry>描述
Properties:<props> <prop key=""></prop>
普通数据:<value>
引用数据:<ref>
(5)为应用指定多个spring配置文件
在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将
Spring 配置文件分解成多个配置文件。
包含关系的配置文件:多个配置文件中有一个总文件,总配置文件将各其它子文件通过<import/>引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。
包含关系的配置文件:
spring-total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定义对象的。
语法:<import resource="其他配置文件的路径" />
关键字:"classpath:" 表示类路径(class文件所在的目录),
在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告诉spring到哪去加载读取文件。在包含关系的配置文件中,可以通配符(*:表示任意字符)
注意: 主的配置文件名称不能包含在通配符的范围内
3.2.2基于注解的DI
(1) 定义 Bean 的注解@Component
@Component: 创建对象的, 等同于<bean>的功能
属性:value 就是对象的名称,也就是bean的id值,
value的值是唯一的,创建的对象在整个spring容器中就一个
位置:在类的上面
@Component(value = "myStudent")等同于
<bean id="myStudent" class="com.bjpowernode.ba01.Student" />
spring中和@Component功能一致,创建对象的注解还有:
1.@Repository(用在持久层类的上面) : 放在dao的实现类上面,
表示创建dao对象,dao对象是能访问数据库的。
2.@Service(用在业务层类的上面):放在service的实现类上面,
创建service对象,service对象是做业务处理,可以有事务等功能的。
3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,
控制器对象,能够接受用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。
@Repository,@Service,@Controller是给项目的对象分层的。
(2) 简单 类型属性注入@Value
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
@Value: 简单类型的属性赋值
属性: value 是String类型的,表示简单类型的属性值
位置: 1.在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面
(3)byType 自动注入@Autowired
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
引用类型
@Autowired: spring框架提供的注解,实现引用类型的赋值。
spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType
@Autowired:默认使用的是byType自动注入。
位置:1)在属性定义的上面,无需set方法, 推荐使用
2)在set方法的上面
(4)byName 自动注入@Autowired 与@Qualifier
引用类型
如果要使用byName方式,需要做的是:
1.在属性上面加入@Autowired
2.在属性上面加入@Qualifier(value="bean的id") :表示使用指定名称的bean完成赋值。
属性:required ,是一个boolean类型的,默认true
required=true:表示引用类型赋值失败,程序报错,并终止执行。
required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null
(5)JDK 注解@Resource 自动注入
引用类型
@Resource: 来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
使用的也是自动注入原理,支持byName, byType .默认是byName
位置: 1.在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面
默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType
小结:
注入 --> 字段或setter方法
普通值:@Value
引用值:
类型:@Autowired
名称1:@Autowired @Qualifier("名称")
名称2:@Resource("名称")
3.2.3注解与XML对比
四、面向切面编程AOP
4.1 概述
4.1.1动态代理的实现方式
jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的
4.1.2 动态代理的作用
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。
4.1.3 AOP概述
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP
可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程oop: 面向对象编程
怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
4.1.4 AOP的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。
2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
4.1.5 AOP的好处
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
4.2 AspectJ对AOP的实现
4.2.1 AspectJ切入点表达式
4.2.2 AspectJ 基于注解的 AOP 实现
AspectJ 中常用的通知有五种类型:
1)前置通知
2)后置通知
3)环绕通知
4)异常通知
5)最终通知
(1)实现步骤
@Aspect : 是aspectj框架中的注解。
作用:表示当前类是切面类。
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
位置:在类定义的上面
切面类中的方法定义规范
定义方法,方法是实现切面功能的。
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法名称自定义
4.方法可以有参数,也可以没有参数。
如果有参数,参数不是自定义的,有几个参数类型可以使用。
@Before: 前置通知注解
属性:value ,是切入点表达式,表示切面的功能执行的位置。
位置:在方法的上面
特点:
1.在目标方法之前先执行的
2.不会改变目标方法的执行结果
3.不会影响目标方法的执行。指定通知方法中的参数 : JoinPoint
JoinPoint:业务方法,要加入切面功能的业务方法
作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数
后置通知定义方法,方法是实现切面功能的。
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法名称自定义
4.方法有参数的,推荐是Object ,参数名自定义
@AfterReturning:后置通知
属性:1.value 切入点表达式
2.returning 自定义的变量,表示目标方法的返回值的。
自定义变量名必须和通知方法的形参名一样。
位置:在方法定义的上面
特点:
1. 在目标方法之后执行的。
2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
Object res = doOther();
3. 可以修改这个返回值
后置通知的执行
Object res = doOther();
参数传递: 传值, 传引用
myAfterReturing(res);
System.out.println("res="+res)
环绕通知方法的定义格式
1.public
2.必须有一个返回值,推荐使用Object
3.方法名称自定义
4.方法有参数,固定的参数 ProceedingJoinPoint
@Around: 环绕通知
属性:value 切入点表达式
位置:在方法的定义什么
特点:
1.它是功能最强的通知
2.在目标方法的前和后都能增强功能。
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果。 影响最后的调用结果
环绕通知,等同于jdk动态代理的,InvocationHandler接口
参数: ProceedingJoinPoint 就等同于 Method
作用:执行目标方法的
返回值: 就是目标方法的执行结果,可以被修改。
环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务
异常通知方法的定义格式
1.public
2.没有返回值
3.方法名称自定义
4.方法有个一个Exception, 如果还有是JoinPoint
@AfterThrowing:异常通知
属性:1. value 切入点表达式
2. throwinng 自定义的变量,表示目标方法抛出的异常对象。
变量名必须和方法的参数名一样
特点:
1. 在目标方法抛出异常时执行的
2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
如果有异常,可以发送邮件,短信进行通知
执行就是:
try{
SomeServiceImpl.doSecond(..)
}catch(Exception e){
myAfterThrowing(e);
}
最终通知方法的定义格式
1.public
2.没有返回值
3.方法名称自定义
4.方法没有参数, 如果还有是JoinPoint
@After :最终通知
属性: value 切入点表达式
位置: 在方法的上面
特点:
1.总是会执行
2.在目标方法之后执行的
try{
SomeServiceImpl.doThird(..)
}catch(Exception e){
}finally{
myAfter()
}
@Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。
可以使用@Pointcut
属性:value 切入点表达式
位置:在自定义的方法上面
特点:
当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。
其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
更多推荐
所有评论(0)