Ioc容器
1.1 Ioc的功能及原理
ioc容器的功能主要有两个,反转控制和依赖注入
ioc的原理:工厂模式+反射+xml
1.2 IOC容器在Spring中的实现
IOC容器读取bean的实例之前,需要先将IOC容器本身实例化
有两种方法创建ioc容器:
1、BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
2、ApplicationContext :beanFactory的子接口,提供了更多高级特性。(常用)
//iocContainer为IOC容器的实例
//1.创建IOC容器对象
ApplicationContext iocContainer = new ClassPathXmlApplicationContext("getting-start.xml");
//2.根据id值获取bean实例对象
Student student = (Student) iocContainer.getbean("student");
ApplicationContext
的主要实现类
ClassPathXmlApplicationContext
:对应类路径下的XML格式的配置文件(常用)FileSystemXmlApplicationContext
:对应文件系统中的XML格式的配置文件
ConfigurableApplicationContext
接口
- 是
ApplicationContext
的子接口,包含一些扩展方法 refresh()
和close()
让ApplicationContext
具有启动、关闭和刷新上下文的能力
WebApplicationContext
接口
WebApplicationContext
,是继承于ApplicationContext
的一个接口,扩展了ApplicationContext
,是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化
1.3 bean对象的获取
在容器的顶级接口 beanFactory接口中,定义了如下几个方法,用于获取 bean 实例
1、Object getbean(String name) throws beansException;:通过 bean name 获取 bean 实例(常用)
2、 T getBean(Class requiredType) throws BeansException;:通过 bean class 获取 bean 实例
3、 T getBean(String name, Class requiredType) throws BeansException;:通过 bean name 和 bean class 获取 bean 实例
1.4 bean属性的赋值(注入)
1、通过 bean 的 setter 方法注入属性
通过 标签指定属性名,Spring 会帮我们找到该属性对应的 setter 方法,注入其属性值
<bean id="student" class="com.oneby.entity.Student">
<property name="stuId" value="007"/>
<property name="stuName" value="Oneby"/>
</bean>
2、通过构造器注入属性值
通过 constructor-arg
标签为对象的属性赋值,通过 name
指定属性名,value
指定属性值(bean中需提供有参构造)
<bean id="student" class="com.oneby.entity.Student">
<constructor-arg name="stuId" value="1" />
<constructor-arg name="stuName" value="Oneby" />
</bean>
3、通过级联属性赋值
Computer 类(get,set,tostring和构造器 都没写进去,影响观看)
public class Computer {
String computerId;
String computerName;
}
Student 类(get,set,tostring和构造器 都没写进去,影响观看)
public class Student {
private Integer stuId;
private String stuName;
private Computer computer;
}
xml
<!-- 演示级联属性赋值 -->
<bean id="student" class="com.oneby.entity.Student">
<property name="computer" ref="computer"/>
<!-- 设置级联属性(了解) -->
<property name="computer.computerId" value="233"/>
<property name="computer.computerName" value="HP"/>
</bean>
<bean id="computer" class="com.oneby.entity.Computer"/>
运行结果
1.5 属性赋值的类型
1、字面量
- 可以使用字符串表示的值,可以通过
value
属性或value
子节点的方式指定 - 基本数据类型及其封装类、
String
等类型都可以采取字面值注入的方式 - 若字面值中包含特殊字符,可以使用
<![CDATA[]]>
把字面值包裹起来
<!-- 演示字面量的使用 -->
<bean id="student" class="com.oneby.entity.Student">
<property name="stuId" value="233"/>
<property name="stuName" value="Oneby"/>
</bean>
2、null 值
通过 <null/>
标签将引用类型字段的值设置为 null
<!-- 演示 null 值的使用-->
<bean id="student" class="com.oneby.entity.Student">
<property name="stuId" value="233"/>
<!-- 将 stuName 字段的值设置为 null -->
<property name="stuName">
<null/> 《-----重点在这里----》》
</property>
<!-- 将 computer 字段的值设置为 null -->
<property name="computer">
<null/>
</property>
</bean>
3、引用外部 bean
通过 <property>
标签中的 ref
属性引用外部 bean
<!-- 引用外部声明的 bean -->
<bean id="student" class="com.oneby.entity.Student">
<property name="stuId" value="233"/>
<property name="stuName" value="Oneby"/>
<!-- 通过 ref 属性引用外部的 bean -->
<property name="computer" ref="computer"/> 《《-----重点在这里----》》
</bean>
<!-- 外部 bean -->
<bean id="computer" class="com.oneby.entity.Computer">
<property name="computerId" value="255"/>
<property name="computerName" value="HP"/>
</bean>
4、引用内部 bean
当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在**<property>
或<constructor-arg>
**元素里,不需要设置任何id
或name
属性,内部bean不能使用在任何其他地方
<!-- 引用内部声明的 bean -->
<bean id="student" class="com.oneby.entity.Student">
<property name="stuId" value="233"/>
<property name="stuName" value="Oneby"/>
<property name="computer">
<!-- 通过 <bean> 标签定义内部 bean -->
<bean class="com.oneby.entity.Computer">
<property name="computerId" value="255"/>
<property name="computerName" value="HP"/>
</bean>
</property>
</bean>
1.6 对集合属性赋值
在Spring中可以通过一组内置的XML标签来配置集合属性,比如:<array>
、<list>
、<set>
、<map>
、<props>
,并且可以用过引入 util
名称空间来提取集合类型的 bean
1、数组
通过 <array>
标签定义数组集合,并且可以通过<value>
指定简单的常量值,通过<ref>
指定对其他bean的引用。通过<bean>
指定内置bean定义。通过<null/>
指定空元素,甚至可以内嵌其他集合。
<!-- 演示数组的赋值 -->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="array">
<array>
<value>Oneby</value>
<value>Heygo</value>
</array>
</property>
</bean>
2、List
通过 <list>
标签定义数组集合,并且可以通过<value>
指定简单的常量值,通过<ref>
指定对其他bean的引用。通过<bean>
指定内置bean定义。通过<null/>
指定空元素,甚至可以内嵌其他集合。
<!-- 演示 List 的赋值 -->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="list">
<list>
<value>Oneby</value>
<value>Heygo</value>
</list>
</property>
</bean>
3、Set
通过 <set>
标签定义数组集合,并且可以通过<value>
指定简单的常量值,通过<ref>
指定对其他bean的引用。通过<bean>
指定内置bean定义。通过<null/>
指定空元素,甚至可以内嵌其他集合。
<!-- 演示 Set 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="set">
<set>
<value>Oneby</value>
<value>Heygo</value>
</set>
</property>
</bean>
4、Map
Java.util.Map通过
<!-- 演示 Map 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="map">
<map>
<entry key="name" value="Oneby"></entry>
<entry key="hobby" value="code"></entry>
</map>
</property>
</bean>
5、Properties
使用<props>
定义java.util.Properties
,该标签使用多个<prop>
作为子标签,每个<prop>
标签中定义key
和value
<!-- 演示 properties 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="properties">
<props>
<prop key="name">Oneby</prop>
<prop key="hobby">code</prop>
</props>
</property>
</bean>
6、把集合注入部分提取出来**
引入名称空间:配置集合类型的bean需要引入util名称空间
将 beans 名称空间对应的这两项 xmlns:util="http://www.springframework.org/schema/util 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd,将 beans 全部替换为 util 就行啦~
使用 <util>
标签完成对集合类型 bean 的抽取,并为其设置 id
属性,方便其他地方进行引用
<!-- 集合类型的 bean-->
<util:list id="list">
<value>Oneby</value>
<value>Heygo</value>
</util:list>
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
<property name="list" ref="list"/>
</bean>
1.7 创建bean
1、Spring 有两种类型 bean**,一种普通** bean**,另外一种工厂** bean**(FactoryBean)**
2、普通** bean**:在配置文件中定义** bean 类型就是返回类型
3、工厂bean:在配置文件定义 bean 类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
1、FactoryBean 接口**(工厂bean)
我们一般通过实现 FactoryBean 接口,并重写其中的方法来获得工厂类
1、Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean
2、工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject()方法所返回的对象
3、工厂bean必须实现org.springframework.beans.factory.FactoryBean接口
FactoryBean 接口
FactoryBean 接口中有如下三个方法,getObject() 方法负责将创建好的 bean 实例返回给 IOC 容器;getObjectType() 方法负责返回工厂生产的 bean 类型;isSingleton() 方法用于指示该 bean 实例是否为单例,默认是单例 bean
演示 FactoryBean
接口的使用
创建 StudentFactory
类,该类实现了 FactoryBean
接口,并重写了其中的 getObject()
和 getObjectType()
方法
public class StudentFactory implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
return new Student(233,"Oneby");
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
}
在 Spring 配置文件中使用 StudentFactory
工厂创建 Student
对象
<bean id="student" class="com.oneby.entity.StudentFactory"/>
2、普通bean
public class Books {
private int bookID;
private String bookName;
private String bookCounts;
private String detail;
}
<bean id="book" class="com.oneby.dao.Books"/>
1.7 bean 的高级配置
1、bean 的作用域
在Spring中,可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例(单例对象),整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域
两种 bean 的作用域
singleton:在Spring IOC容器中仅存在一个bean实例,bean以单实例的方式存在
prototype:每次调用getBean()
时都会返回一个新的实例
<!-- 演示 单例bean 的作用域 -->
<bean id="student" class="com.oneby.entity.Student" scope="singleton">
<property name="stuId" value="233" />
<property name="stuName" value="Oneby" />
</bean>
<!-- 演示 多例bean 的作用域 -->
<bean id="student" class="com.oneby.entity.Student" scope="prototype">
<property name="stuId" value="233" />
<property name="stuName" value="Oneby" />
</bean>
1.8 bean 的生命周期
1、Spring IOC 管理下的 bean 生命周期
Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务,Spring IOC容器对bean的生命周期进行管理的过程:
通过构造器或工厂方法创建bean实例
为bean的属性设置值和对其他bean的引用
调用bean的初始化方法
bean可以使用了
当容器关闭时,调用bean的销毁方法
注:在配置bean时,通过init-method和destroy-method属性为bean指定初始化和销毁方法
代码演示
创建 Order
类,用于演示 bean 的生命周期
public class Order {
private String name;
public Order() {
System.out.println("第一步:执行无参数构造创建 bean 实例");
}
public void setName(String name) {
this.name = name;
System.out.println("第二步:调用 setter 方法为属性赋值");
}
// init-method 初始化方法
public void initMethod(){
System.out.println("第三步:执行 init-method 初始化方法");
}
// destroy-method 销毁方法
public void destroyMethod(){
System.out.println("第五步:执行 destroy-method 初销毁方法");
}
}
在 <bean>
标签中指定 order
对象的 init-method
方法(初始化方法)和 destroy-method
方法(销毁方法)
<!-- 演示 bean 的生命周期 -->
<bean id="order" class="com.oneby.entity.Order"
init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="iPad" />
</bean>
测试代码:记得要关闭 IOC 容器才会执行 destroy-method
方法,并且接口类型需要上升到 ConfigurableApplicationContext
才会提供 close()
方法
public class SpringTest {
@Test
public void test() {
//1.创建IOC容器对象(使用的是ConfigurableApplicationContex)
ConfigurableApplicationContext iocContainer =
new ClassPathXmlApplicationContext("spring-advance-config.xml");
//2.根据id值获取bean实例对象
Order order = (Order) iocContainer.getBean("order");
//3.打印bean
System.out.println("第四步:使用创建好的 order 对象" + order);
//4.关闭IOC容器
iocContainer.close();
}
}
2、添加 BeanPostProcessor 后的 bean 生命周期
1、bean的后置处理器(添加之后所有bean都会执行后置处理器)
1、bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
2、bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
3、bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
4、postProcessBeforeInitialization(Object, String)
5、postProcessAfterInitialization(Object, String)
2、添加bean后置处理器后bean的生命周期
1、通过构造器或工厂方法创建bean实例
2、为bean的属性设置值和对其他bean的引用
3、将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
4、调用bean的初始化方法
5、将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
6、bean可以使用了
7、当容器关闭时调用bean的销毁方法
代码演示
创建 MyBeanPost
类,实现接口 BeanPostProcessor
类,并重写其中的 postProcessBeforeInitialization
和 postProcessAfterInitialization
方法
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第三步:执行 postProcessBeforeInitialization 方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:执行 postProcessAfterInitialization 方法");
return bean;
}
}
配置文件:在配置文件中实例化我们自定义的 MyBeanPost
后置处理器
<!-- 演示 bean 的生命周期 -->
<bean id="order" class="com.oneby.entity.Order"
init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="iPad" />
</bean>
<!-- 加上 BeanPostProcessor 后的生命周期 -->
<bean id="myBeanPost" class="com.oneby.config.MyBeanPost"/>
3、读取 properties 文件
1、引用外部 properties 配置文件单独存放数据库配置信息
引入 context 名称空间
将 xmlns=“http://www.springframework.org/schema/beans” 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 复制,并将出现 beans 的位置全部替换为 context
代码测试
在类路径下创建 jdbc.properties 数据库配置文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
通过 <context:property-placeholder>
标签中的 location
来制定配置文件的路径,classpath:
表示该配置文件位于类路径下,并通过 ${prop.userName}
的方式来取出配置文件中的属性值
<!-- 引用外部属性文件来配置数据库连接池 -->
<!-- 指定 properties 属性文件的位置,classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="url" value="${prop.url}"/>
<property name="driverClassName" value="${prop.driverClass}"/>
</bean>
1。9 bean 的自动装配
1、自动装配的概念
[1]手动装配:以value或ref的方式明确指定属性值都是手动装配。
[2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
2、装配模式
[1]根据类型自动装配(byType):将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
[2]根据名称自动装配(byName):必须将目标bean的名称和属性名设置的完全相同
[3]通过构造器自动装配(constructor):当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
3、代码举例
通过 <bean>
标签的 autowire="byType"
,指定 student
对象中的 bean
按照类型进行装配
<!-- 自动装配 -->
<bean id="student" class="com.oneby.entity.Student" autowire="byType">
<property name="stuId" value="233"/>
<property name="stuName" value="Oneby"/>
</bean>
<bean id="computer" class="com.oneby.entity.Computer">
<property name="computerId" value="666"/>
<property name="computerName" value="HP"/>
</bean>
4、配置信息的继承(bean中使用 标签)
Spring允许继承bean的配置,被继承的bean称为父bean,继承这个父bean的bean称为子bean
子bean从父bean中继承配置,包括bean的属性配置,子bean也可以覆盖从父bean继承过来的配置
不使用继承配置 bean (两位社畜的 company
、hobby
、profession
属性的值均相同,这样配置显得有些冗余)
<!-- 不使用继承配置 bean -->
<bean id="corporateSlave1" class="com.oneby.entity.CorporateSlave">
<property name="id" value="1"/>
<property name="name" value="Oneby"/>
<!-- 以下都是重复的属性 -->
<property name="company" value="OneTech"/>
<property name="hobby" value="Code"/>
<property name="profession" value="Programer"/>
</bean>
<bean id="corporateSlave2" class="com.oneby.entity.CorporateSlave">
<property name="id" value="2"/>
<property name="name" value="Heygo"/>
<!-- 以下都是重复的属性 -->
<property name="company" value="OneTech"/>
<property name="hobby" value="Code"/>
<property name="profession" value="Programer"/>
</bean>
使用配置信息的继承配置 bean
配置信息的继承:Heygo
的配置信息继承于 Oneby
(指定 bean 的 parent
属性),自然就获得了 Oneby
社畜的所有配置信息,只需要重写自己不一样的配置信息即可
<!-- 演示配置信息的继承 -->
<bean id="corporateSlave1" class="com.oneby.entity.CorporateSlave">
<property name="id" value="1"/>
<property name="name" value="Oneby"/>
<!-- 以下都是重复的属性 -->
<property name="company" value="OneTech"/>
<property name="hobby" value="Code"/>
<property name="profession" value="Programer"/>
</bean>
<bean id="corporateSlave2" parent="corporateSlave1">
<!-- 重写不同值的属性即可 -->
<property name="id" value="2"/>
<property name="name" value="Heygo"/>
</bean>
注意事项:配置信息的继承
父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>
的abstract
属性为true
,这样Spring将不会实例化这个bean
1.10 bean 之间的依赖
有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Student对象的时候必须创建Computer。这里需要注意的是依赖关系不等于引用关系,Student即使依赖Computer也可以不引用它
<!-- 演示 bean 之间的依赖 -->
<bean id="student" class="com.oneby.entity.Student" depends-on="computer">
<property name="stuId" value="233"/>
<property name="stuName" value="Oneby"/>
</bean>
<bean id="computer" class="com.oneby.entity.Computer"/>
1、11 注解方式配置 bean
1、用于标识 bean 的四个注解
①普通组件:@Component,用于标识一个受Spring IOC容器管理的组件
②持久化层组件:@Respository,用于标识一个受Spring IOC容器管理的持久化层组件
③业务逻辑层组件:@Service,用于标识一个受Spring IOC容器管理的业务逻辑层组件
④表述层控制器组件:@Controller,用于标识一个受Spring IOC容器管理的表述层控制器组件
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
2、组件命名规则
[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
**[2]使用组件注解的value
属性指定bean的id**
3、扫描组件
1、引入 AOP 依赖
<!-- spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
引入 context 名称空间
将 xmlns=“http://www.springframework.org/schema/beans” 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 复制,并将出现 beans 的位置全部替换为 context
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
开启组件扫描(开启组件扫描,并指明要扫描的包路径)
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.oneby"/>
组件扫描的详细说明
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
[2]当需要扫描多个包时可以使用逗号分隔。
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
<context:component-scan
base-package="com.oneby"
resource-pattern="autowire/*.class"/>
包含与排除
1、context:include-filter>子节点表示要包含的目标类。注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
2、context:exclude-filter>子节点表示要排除在外的目标类
3、component-scan下可以拥有若干个include-filter和exclude-filter子节点
<!--示例1:use-default-filters="false" 表示现在不使用默认filter,自己配置filter
context:include-filter用于设置扫描哪些内容(这里配置只扫描 Controller 注解) -->
<context:component-scan base-package="com.oneby" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例2:下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描(这里排除 Controller 注解) -->
<context:component-scan base-package="com.oneby">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
1.12 依赖注入的实现
(1)@Autowired:根据属性类型进行自动装配
第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
} }
(2)@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
(3)@Resource:可以根据类型注入,可以根据名称注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
(4)@Value:注入普通类型属性
@Value(value = "abc")
private String name;
6、完全注解开发
(1)创建配置类,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}
AOP
1、什么是 AOP(面向切面编程)
不通过修改源代码方式,在主干功能里面添加新功能
2、底层原理(动态代理)
(1)有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理
⚫ 创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用 CGLIB 动态代理
⚫ 创建子类的代理对象,增强类的方法
3、AOP术语
1**、连接点**
类里面哪些方法可以被增强,这些方法称为连接点
2**、切入点**
实际被真正增强的方法,称为切入点
3**、通知**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jcz5SDL-1619174024022)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112256294.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-la082Mxw-1619174024023)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112230771.png)]
4、切面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMZTH6Ub-1619174024025)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112138000.png)]
4、AOP 操作(准备工作)
1、Spring框架一般都是基于AspectJ 实现 AOP 操作
(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使
用,进行 AOP 操作
2、基 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)
1、实现 AOP 操作的步骤
编写切面类(通过 @Aspect 注解标识这是一个切面类),并且不要忘记将切面类交给 Spring IOC 管理(Component 注解),并编写相应的通知方法与切入点表达式
在 Spring 配置文件中开启 aop 功能:通过 <aop:aspectj-autoproxy/> 注解开启 aop 功能。当Spring IOC容器侦测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的bean创建代理
AspectJ支持5种类型的通知注解
[1]@Before:前置通知,在方法执行之前执行
[2]@After:后置通知,在方法执行之后执行
[3]@AfterRunning:返回通知,在方法返回结果之后执行
[4]@AfterThrowing:异常通知,在方法抛出异常之后执行
[5]@Around:环绕通知,围绕着方法执行
1*准备工作:在 Spring 中使用 AspectJ*
引入 maven 依赖:引入 aop 和 aspects 相关的依赖
编写 Spring 配置文件:引入 context
和 aop
名称空间;开启组件扫描,并指明包路径;开启自动代理功能
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="com.oneby"/>
<!-- 开启 Aspect生成代理对象 -->
<aop:aspectj-autoproxy/>
</beans>
2、切入点表达式的相关细节
切入点的作用:通过表达式的方式定位一个或多个具体的连接点(哪些方法需要被增强)
切入点表达式的语法格式:execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
切入点表达式的举例一
表达式:execution(* com.atguigu.spring.ArithmeticCalculator.*(…))
含义:增强 ArithmeticCalculator 接口中声明的所有方法
解释说明:第一个“”代表任意修饰符及任意返回值;第二个“”代表任意方法;“…”匹配任意数量、任意类型的参数
注:若目标类、接口与该切面类在同一个包中可以省略包名
切入点表达式的举例二
表达式:execution(public * ArithmeticCalculator.*(…))
含义: 增强 ArithmeticCalculator 接口的所有公有方法(TMD 接口中的方法不都是 public 吗)
切入点表达式的举例三
表达式:execution(public double ArithmeticCalculator.*(…))
含义:增强 ArithmeticCalculator 接口中返回double类型数值的方法
切入点表达式的举例四
表达式:execution(public double ArithmeticCalculator.*(double, …))
含义:第一个参数为double类型的方法。“…” 匹配任意数量、任意类型的参数
切入点表达式的举例五
表达式: execution(public double ArithmeticCalculator.*(double, double))
含义:参数类型为double,double类型的方法
切入点表达式的举例六:在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
表达式:execution (* .add(int,…)) || execution( *.sub(int,…))
含义:任意类中第一个参数为int类型的add方法或sub方法
5、AOP **操作(**AspectJ 注解)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgXfZCh0-1619174024028)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312113836285.png)]
3、在普通l类上加注解@Component,在增强类上面添加注解@Component(生成bean)和 @Aspect(生成代理对象)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-te0iQqoy-1619174024029)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312114453186.png)]
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
} }
3、相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}
6、完全使用注解开发**
(1)创建配置类,不需要创建 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
7、AOP **操作(**AspectJ 配置文件)
1**、创建两个类,增强类和被增强类,创建方法**
2**、在** spring 配置文件中创建两个类对象
<!--创建对象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
3**、在** spring 配置文件中配置切入点
<!--配置 aop 增强--> <aop:config>
<!--切入点--> <aop:pointcut id="p" expression="execution(*
com.atguigu.spring5.aopxml.Book.buy(..))"/>
<!--配置切面--> <aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上--> <aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
eed();
System.out.println(“环绕之后…”);
} }
### 3、**相同的切入点抽取**
//相同切入点抽取
@Pointcut(value = “execution(* com.atguigu.spring5.aopanno.User.add(…))”)
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = “pointdemo()”)
public void before() {
System.out.println(“before…”);
}
## 6、完全使用注解开发**
**(1)创建配置类,不需要创建 xml 配置文件**
@Configuration
@ComponentScan(basePackages = {“com.atguigu”})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
## 7、**AOP** **操作(****AspectJ** **配置文件)**
### **1****、创建两个类,增强类和被增强类,创建方法**
### **2****、在** **spring** **配置文件中创建两个类对象**
### **3****、在** **spring** **配置文件中配置切入点**
com.atguigu.spring5.aopxml.Book.buy(…))"/>
</aop:aspect>
</aop:config>