IOC
IOC(Inversion of control) :控制反转,是一个理论,概念,思想。描述的是把对象的创建,复制,管理工作都交给代码之外的容器实现,也就是对象的创建是由其他的外部资源完成。
-
控制:创建对象,对象属性的赋值,对象之间的关系管理。
-
反转:把原来开发人员管理,创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象。创建对象,给属性赋值
-
正转:由开发人员在代码中,使用new 构造方法创建对象,开发人员主动管理对象。
-
容器: 是一个服务器软件,一个框架(例如Spring)
-
为什么使用IOC:目的就是减少对代码的改动,也能实现不同的功能,实现解耦合。
Java中创建对象的方式?
- 构造方法,new Student()
- 反射
- 序列化
- 克隆
- ioc容器创建对象
- 动态代理
IOC的体现:
servlet:
- 创建类集成HttpServlet
- 在web.xml 注册servlet
- 没有创建Servlet对象,没有MyServlet s=new Myservlet();
- Servlet 是Tomcat服务器创建的,Tomcat也称为容器,Tomcat作为容器:里面存放的有Servlet对象,Listener,Filter对象。
IOC的技术实现: DI是IOC的技术实现。 DI(Dependency Injection):依赖注入,只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中赋值,查找,都由容器内部实现
Spring 是使用DI实现了IOC的功能,Spring底层创建对象,是通过反射机制
引入Spring依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
sources文件夹中创建Spring配置文件beans.xml,名称自定义
beans是配置文件的跟标签,spring把java对象称为bean spring-beans.xsd 是约束文件,和 mybatis指定dtd是一样的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="someService" class="zengqiang.service.impl.SomeServiceImpl"/>
</beans>
bean标签作用 声明bean,就是告诉Spring要创建某个类的对象
- id: 对象的自定义名称,唯一值。spring通过这个名称找到对象
- class:类的全限定名称(不能是接口,因为spring 是反射机制创建对选哪个,必须使用类)
- spring是把创建好的对象放入到map中,spring框架有一个map存放对象。springMap.put(id的值,对象).例如 springMap.put("someService",new SomeServiceImpl()); 一个bean标签声明一个对象
第一个例子:
@Test
fun test02() {
// 使用spring容器创建对象
// 指定spring配置文件的名称
val config = "beans2.xml"
// 创建表示spring容器的对象,ApplicationContext
// ApplicationContext 就是表示Spring容器,通过容器获取对象
// ClassPathXmlApplicationContext :表示从类路径中加载Spring的配置文件
val applicationContext = ClassPathXmlApplicationContext(config)
// 从容器中获取某个对象,要调用对象的方法
// getBean("配置文件中的bean的id值")
val someService = applicationContext.getBean("someService") as SomeService
// 使用Spring创建好的对象
someService.doSome()
}
Spring创建对象的时机 Spring在创建Spring容器时,会创建所有的对象。即执行 val applicationContext = ClassPathXmlApplicationContext(config)的时候
Spring创建对象默认调用无参构造方法
获取Spring容器中对象的信息
@Test
fun test03() {
val config = "beans2.xml"
val applicationContext = ClassPathXmlApplicationContext(config)
//获取容器中的对象数量
println(applicationContext.beanDefinitionCount)
//获取容器中每个对象的名称
applicationContext.beanDefinitionNames.forEach {
println(it)
}
}
创建一个非自定义类的对象
bean.xml
<bean id="date" class="java.util.Date"/>
@Test
fun test04() {
val config = "beans2.xml"
val applicationContext = ClassPathXmlApplicationContext(config)
val date = applicationContext.getBean("date") as Date
print(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date))
}
DI
DI:依赖注入,表示创建对象,给属性赋值
DI的实现有两种:
- 在Spring的配置文件中,使用标签和属性完成,叫做基于XML的DI实现
- 在Spring中的朱姐,完成属性赋值,叫做基于注解的DI实现
DI的语法分类
- set注入(设置注入):Spring调用类的set方法,在set方法可以实现属性的赋值。80左右都是使用set注入
- 构造注入,Spring调用类的有参数构造方法,创建对象。在构造方法中完成赋值。
注入:就是赋值的意思
DI:给属性赋值
- set注入(设置注入):Spring调用类的set方法,可以在set方法中完成属性赋值
1.简单类型的set注入
语法: <bean id="xx" class="yy> <property name="属性名字" value="此属性的值"/> 一个property只能给一个属性赋值 <property......>
实例:
applicationContext.xml:
<!--声明Student对象-->
<bean id="student" class="zengqiang.di.bao01.Student">
<property name="name" value="李四"/>
<property name="age" value="20"/>
</bean>
kt代码:
fun test01() {
val config = "bao01/applicationContext.xml"
val applicationContext = ClassPathXmlApplicationContext(config)
val student = applicationContext.getBean("student") as Student
println(student.toString())
}
spring使用property属性赋值最终调用的还是属性的set方法。所以没有set方法是不行的。
给引用类型属性set注入
<bean id="student" class="zengqiang.di.bao02.Student">
<property name="name" value="lisi"/>
<property name="age" value="22"/>
<property name="school" ref="school"/>
</bean>
<bean id="school" class="zengqiang.di.bao02.School">
<property name="name" value="清华大学"/>
<property name="address" value="北京"/>
</bean>
@Test
fun test02() {
val config = "bao02/applicationContext.xml"
val applicationContext = ClassPathXmlApplicationContext(config)
val student = applicationContext.getBean("student") as Student02
println(student.toString())
}
构造注入
使用name参数(推荐使用)
<bean id="constructorStudent" class="zengqiang.di.bao02.Student">
<constructor-arg name="name" value="10"/>
<constructor-arg name="age" value="22"/>
<constructor-arg name="school" ref="school"/>
</bean>
<!--index-->
<bean id="student2" class="zengqiang.di.bao02.Student">
<constructor-arg name="name" value="学生2"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="school" ref="school"/>
</bean>
<!-- 省略index-->
<bean id="student3" class="zengqiang.di.bao02.Student">
<constructor-arg value="学生3"/>
<constructor-arg value="25"/>
<constructor-arg ref="school"/>
</bean>
引用类型的自动注入(byName,byType)
byName实例
<bean name="school" class="zengqiang.di.bao04.School">
<property name="name" value="家里蹲"/>
<property name="address" value="江苏省花桥镇"/>
</bean>
<!-- 添加autowire="byName" 将自动给Student中的school属性赋值为 同名的bean对应的对象-->
<bean name="student" class="zengqiang.di.bao04.Student" autowire="byName">
<property name="name" value="李增强"/>
<property name="age" value="25"/>
</bean>
byType实例
<bean name="mySchool" class="zengqiang.di.bao04.School">
<property name="name" value="家里蹲"/>
<property name="address" value="江苏省花桥镇"/>
</bean>
<!-- 添加autowire="byType" 将自动给Student中的school属性赋值为 同类型的bean对应的对象-->
<bean name="student" class="zengqiang.di.bao04.Student" autowire="byType">
<property name="name" value="任香芹"/>
<property name="age" value="18"/>
</bean>
byType注意: 如果一个对象中的引用类型有两个同类型的bean则会导致异常
多配置文件 为了解决项目过大,applicationContext.xml 过于庞大问题。 案例:
spring-school.xml
<bean name="school" class="zengqiang.di.bao05.School">
<property name="name" value="清华大学"/>
<property name="address" value="北京"/>
</bean>
spring-student.xml
<bean name="student" class="zengqiang.di.bao05.Student" autowire="byType">
<property name="name" value="张三"/>
<property name="age" value="25"/>
</bean>
total.xml主配置文件
<!--语法:<import resource="路径"
关键字:classpath: 表示类路径(class文件所在的目录)-->
<import resource="classpath:bao05/spring-school.xml"/>
<import resource="classpath:bao05/spring-student.xml"/>
total.xml主配置文件
<!-- 在包含关系的配置文件中,可以使用通配符,注意:主的配置文件名称不能包含在通配符的范围内-->
<import resource="classpath:bao05/spring-*.xml"/>
如果使用通配符方案,需要放在一个目录里面,直接放到resource文件夹中spring无法进行读取
基于注解的DI
通过注解完成java对象的创建,属性的赋值。
-
加入Maven依赖spring-context,在加入spring-context的同时,间接加入spring-aop的依赖。使用注解必须加入spring-aop依赖
-
在类中加入Spring的注解(多个不同功能的注解)
-
在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你项目中的位置
学习的注解
- @Component
- @Respository
- @Service
- @Controller
- @Value
- @Autowired
- @Resource
@Component
package ba01;
import org.springframework.stereotype.Component;
/**
* @Component 创建对象的,等同与<bean></bean>的功能
* 属性 value就是对象的名称,也就是bean的id值
* value的值是唯一的,创建对象在整个spring容器中就一个
* 此注解作用在类上
*
* @Component(value = "myStudent")等同于
* <bean id="myStudent" ></bean>
*
* 声明组件扫描器(component-scan), 组件就是java对象
* base-package :指定注解在你项目中的包名
* component-scan工作方式:spring会扫描遍历base-package指定的包,
* 把包中和子包中所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值
* 在applicationContext.xml中
* <context:component-scan base-package=""/>
*/
@Component(value="student")
public class Student {
private String name;
private String age;
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age='" + age + ''' +
'}';
}
}
Component如果没有指定value,则生成的对象名称为类名小写
Spring中和@Component 功能一直的注解还有:
- @Respository(用在持久层上面):放在dao的实现类上面,表示创建dao对象,dao对象是用来访问数据库的
- @Service(用在业务层上面): 放在service的实现类上面,创建Service对象,Service对象是做业务处理,可以有事务等功能。
- @Controller(用在控制器上面):放在控制器上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法和@component一样的。都能创建对象,但是这三个注解还有额外的功能。主要是给项目分层的。
如何指定多个组件扫描器的包
1. 多次使用<context-component-scan/>
2. 使用<context-component-scan base-package="包1,包2,包3 3...."/>,包之间使用逗号分隔
3. 指定父包(如果是顶级包的话,会扫描所有包,效率较低,根据情况选择使用)
@Value
简单类型的属性赋值
- 属性:value是String类型,表示简单类型的属性值
- 位置:
- 在属性定义的上面,无需set方法,推荐使用
- 在set方法上面
Autowired
引用类型的属性赋值
-
使用的是自动注入原理,支持byName,byType
-
默认使用的是byType创建
-
多添加一个@Qualifier("对象名称"),就会使用byName进行注入
-
required属性默认true,required=true:表示引用类型赋值失败,将会报错,如果required=false:表示如果引用类型赋值失败,正常执行,只是引用类型为null
@Resource (这个是jdk中的注解)
引用类型的属性赋值
- 使用的是自动注入原理,支持byName,byType
- 默认使用的是byName创建,如果byName失败,再用byType创建
- 如果给value赋值,则表示只使用byName去进行对象创建
经常改变使用配置文件,不经常改变使用注解
AOP
- 动态代理
- jdk动态代理,使用jdk中的Proxy,Method,InvocationHandler创建代理对象。
- cglib动态代理:第三方工具库,创建代理对象,原理是继承。通过继承目标类,创建子类,子类就是代理对象。要求目标类不能是final,方法也不能是final的
- AOP 面向切面编程,基于动态代理,可以使用jdk,cglib两种代理方式。 Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了 AOP:面向切面编程(Aspect Oriented Programmering)
术语:
- Aspect:切面,表示增强的功能,就是一堆代码,完成某个功能。非业务功能,常见的切面功能主要有日志,事务,统计信息,参数检查,权限验证。
- JoinPoint:连接点,连接业务中方法和切面的位置。就是某个类中的业务方法 3.目标对象:给哪个类的方法增加功能,这个类就是目标对象
- Advice:通知,通知表示切面功能执行的时间
说一个切面有三个关键的要素:
- 切面的功能代码,切面要干什么
- 切面的执行位置,使用Pointcut表示切面执行的位置
- 切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
AOP的实现
aop是一个规范,是动态的一个规范化,一个标准 AOP的技术实现框架:
- Spring:Spring内部实现了AOP的规范,能做AOP的工作,Spring主要在事务处理时使用AOP。我们项目开发中很少使用Spring的AOP实现,因为Spring的AOP比较笨重。
- aspectJ:一个开源的专门做AOP的框架。Spring框架中集成了aspectJ框架,通过Spring就能使用aspectJ的功能。aspectJ框架实现AOP有两种方式: 1.使用xml的配置文件:配置全局事务 2.使用注解,我们在项目中要做AOP功能,一般都使用注解,aspectJ有5个注解
学习aspectJ框架的使用
1.切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强) 1.@Before 2.@AfterReturning 3.@Around 4.@AfterThrowing 5.@After 2.切入点表达式