【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)
文章目录
- 【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)
- 1.Spring框架快速入门
- 1.1七大核心模块
- 1.1.1 Spring Core
- 1.1.2 Spring-Beans
- 1.1.3 Spring Context
- 1.1.4 Spring-Expression
- 1.1.5 Spring AOP
- 1.1.6 JDBC和DAO模块(Spring DAO)
- 1.1.7 spring-transaction
- 1.1.8 Spring ORM
- 1.1.9 Spring Web MVC
- 1.2 项目构建
- 2.SpringIOC
- 2.1反射创建对象
- 2.2 IOC核心的接口
- 2.3 ApplicationContext主要实现类
- 3.SpringBean的注入方式
- 3.1 创建对象和set方法、有参构造函数注入属性
- 3.2注入空值和特殊符号
- 3.3注入属性外部bean
- 3.4注入内部bean(应用较少)
- 3.5注入级联赋值
- 3.6注入集合类型属性
- 3.7注入集合类型为对象属性
- 3.8集合注入部分提取公共
- 3.9 IOC操作Bean的管理
- 4.Spring的工厂Bean
- 4.1 SpringBean的作用域
- 4.2 SpringBean的生命周期
- 4.3 SpringBean的自动装配
- 5.SpringBean的AOP
- 5.1代理模式
- 5.1.1 代理模式创建方式
- 5.1.2 静态代理
- 5.1.3 动态代理
- 5.2 AOP详解
- 5.2.1 Aop常用术语
- 5.2.2 Aop环境准备
【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)
JDK最低版本要求1.8
Spring概念
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,它提供了依赖注入(DI)和面向切面编程(AOP)等功能。依赖注入可以简化对象之间的依赖关系管理,面向切面编程可以将一些横切关注点(例如事务管理、日志记录等)从核心业务逻辑中解耦出来。能够让编码变的更加简单,核心组件 IOC容器和Aop面向切面编程。
- IOC 控制反转:把整个对象创建的过程,统一交给我们SpringIOC容器来实现管理,底层使用反射+工厂模式实现。
- Aop面向切面编程:对我们功能(方法)前后实现增强,比如打印日志、事务原理、权限管理,底层是基于动态代理模式实现的。
减少到我们的代码的冗余性问题。
Spring优势
- 方法的解耦,简化开发;
- Aop技术的支持;
- 提供声明事务支持
- Junit单元测试
- 方便整合其他框架(
Mybatis
、SpringMVC
、SpringBoot
、SpringCloud
、Redis
等) - 降低我们的JavaEEapi开发使用的难度(Spring对很多复杂的api接口实现了封装)
Spring与SpringBoot关系
SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。
总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。
比如:
@RestController
@ComponentScan("com.mayikt.aop")
@Configuration
@Component
@Scheduled
@Value
@Bean
1.Spring框架快速入门
Spring的官网
Spring官方下载依赖jar包\
1.1七大核心模块
test
对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.
Core Container
Spring的核心组件, 包含了Spring框架最基本的支撑.
Beans, 对应spring-beans.jar. Spring进行对象管理时依赖的jar包.
Core, 对应spring-core.jar, Spring核心jar包.
Context, 对应spring-context.jar, Spring容器上下文对象.
SpEL, 对应spring-expression.jar, Spring表达式语言.
AOP
面向切面编程, 对应spring-aop.jar.
Data Access
Spring对数据访问层的封装
JDBC, 对应spring-jdbc.jar. Spring对jdbc的封装, 当需要使用spring连接数据库时使用. spring-jdbc.jar需要依赖spring-tx.jar.
Transactions, 对应spring-tx.jar. 事务管理
ORM, 对应spring-orm.jar. spring整合第三方orm框架需要使用的jar包, 例如Hibernate框架.
Web
Spring对javax下的接口或类做的扩展功能.
spring-web.jar, 对Servlet, filter, Listener等做的增强.
spring-webmvc.jar, 实际上就是SpringMVC框架. 需要依赖spring环境和spring-web.jar.
1.1.1 Spring Core
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
Maven依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.2 Spring-Beans
这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.3 Spring Context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
1.1.4 Spring-Expression
模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,它也支持列表选择和投影以及常见的列表聚合。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.5 Spring AOP
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.6 JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.7 spring-transaction
以前是在这里org.springframework.transaction
为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.8 Spring ORM
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.1.9 Spring Web MVC
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.2 项目构建
Maven依赖
<dependencies>
<!--
这个jar 文件包含Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。
外部依赖Commons Logging, (Log4J)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--
这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--
这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。
外部依赖spring-beans, (spring-aop)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
创建spring.xml文件
在 resources 目录下创建
<?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">
<!--
配置SpringBean对象
-->
<bean id="userEntity" class="com.zhaoli.entity.UserEntity"></bean>
</beans>
获取Bean对象
在test\java 目录下创建 Test01类
//new UserEntity()
// 1.读取xml配置文件
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 2.根据bean的id获取bean对象
UserEntity userEntity = classPathXmlApplicationContext.getBean("userEntity", UserEntity.class);
System.out.println(userEntity);
userEntity.addUser();
2.SpringIOC
IOC容器底层实现原理;
- IOC容器中非常核心的接口 BeanFactory
BeanFactory
Bean对象 Factory工厂 - IOC容器基本的概念:控制反转
把对象的创建过程与使用统一都交给我们的Spring来进行原理。
不需要开发者自己去new对象 - IOC容器底层实现技术:反射技术、解析xml、工厂模式
- IOC作用 降低我们代码的耦合度。
创建对象的方式有那些:
- 单独new方式—耦合度太高了
每次单独new对象,没有实现统一管理对象,如果后期userDao的名称信息发生变化的情况下,需要改变的引用地方比较多,耦合度太高。 - 工厂模式—降低我们耦合度
概念:统一的管理和维护我们每个对象创建与使用的过程。
不需要自己new对象。 - 反射的方式
降低代码的-耦合度
com.zhaoli.dao—数据库访问层;
com.zhaoli.service—业务逻辑层;
业务逻辑层调用到数据库访问层
2.1反射创建对象
SpringIOC容器底层实现原理:
反射+工厂模式+解析xml技术实现
- 使用解析xml技术 解析spring.xml配置文件;
- 获取 类的完整路径地址
- 使用到反射技术初始化对象
- 需要使用工厂模式封装初始化对象
UserDaoFactory
public class UserDaoFactory {
public static UserDao getUserDao(){
return new UserDao();
}
}
UserFactort
public class UserFactort {
public static UserEntity getUserEntity() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//使用到反射技术初始化对象
Class<?> aClass = Class.forName("com.zhaoli.entity.UserEntity");
//默认执行的是无参构造函数
UserEntity userEntity = (com.zhaoli.entity.UserEntity) aClass.newInstance();
return userEntity;
}
}
使用解析 xml 技术解析 spring.xml 配置文件 应用 dom4j 技术
UserFactort
public class UserFactort {
public static UserEntity getUserEntity() throws ClassNotFoundException, InstantiationException, IllegalAccessException, DocumentException {
//使用解析 xml 技术解析 spring.xml 配置文件 应用 dom4j 技术
String userClass = new Dom4jClass().getUserClass();
//使用到反射技术初始化对象
Class<?> aClass = Class.forName(userClass);
//默认执行的是无参构造函数
UserEntity userEntity = (com.zhaoli.entity.UserEntity) aClass.newInstance();
return userEntity;
}
}
com/zhaoli/utils/Dom4jClass.java
public class Dom4jClass {
public String getUserClass() throws DocumentException {
//this.getClass().getResource() 动态获取到 resources 的绝对路径
File xmlFile = new File(this.getClass().getResource("/") + "spring.xml");
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(xmlFile);
//获取到根节点
Element rootElement = document.getRootElement();
//获取到 beans 中的 bean 标签
Element bean = rootElement.element("bean");
String aClass = bean.attributeValue("class");
return aClass;
}
}
Test02
UserEntity userEntity = UserFactort.getUserEntity();
System.out.println(userEntity);
userEntity.addUser();
2.2 IOC核心的接口
- IOC的核心思想底层基于反射+工厂模式实现
- Spring提供IOC容器实现两种方式:
- BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。 - ApplicationContext 是 BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。当我们在加载配置文件的过程中,就会将配置文件中的对象创建。
- BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。
2.3 ApplicationContext主要实现类
ClassPathXmlApplicationContext
:对应类路径下的XML格式的配置文件
FileSystemXmlApplicationContext
:对应文件系统中的XML格式的配置文件
ConfigurableApplicationContext
是ApplicationContext
的子接口,包含一些扩展方法
refresh()
和close()
让ApplicationContext
具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext
需要new
此接口的对象调用close()
方法
WebApplicationContext
专门为WEB
应用而准备的,它允许从相对于WEB
根目录的路径中完成初始化工作
3.SpringBean的注入方式
3.1 创建对象和set方法、有参构造函数注入属性
- 什么是Bean管理
使用spring创建对象、使用spring注入属性 - Bean的管理有两种方式
- 基于XML方式配置
<bean id="userEntity" class="com.zhaoli.entity.UserEntity"></bean>
在spring的配置文件中,会配置一个bean标签,注入bean的信息,创建bean对象
Id
:获取bean对象,唯一bean对象的名称; bean的名称不允许重复
Class
属性:类的完整路径地址(类名称+包名称)
默认底层使用反射技术执行无参数构造函数
- 基于XML方式注入属性
DI 依赖注入: 对象的属性注入值; (spring实现)
(1)第一种实现方式:基于对象属性set方法实现
<!-- set方法注入属性值 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity">
<property name="bookName" value="面试宝典" ></property>
<property name="bookPrice" value="88.8" ></property>
</bean>
在Bean 标签下再定义一个属性标签
name:类中的属性名称
value:需要注入的属性值
(2)第二种实现方式:有参构造函数注入属性
实例类 OrderEntity
public class OrderEntity {
private String orderId;
private String orderName;
public OrderEntity() {
}
public OrderEntity(String orderId, String orderName) {
this.orderId = orderId;
this.orderName = orderName;
System.out.println("反射机制执行到有参构造函数:"+orderId+";"+orderName);
}
@Override
public String toString() {
return "OrderEntity{" +
"orderId='" + orderId + '\'' +
", orderName='" + orderName + '\'' +
'}';
}
}
Xml配置文件 order.xml
<!-- 第一种方式 指定参数列表名称注入参数 -->
<bean id="orderEntity" class="com.zhaoli.entity.OrderEntity">
<constructor-arg name="orderId" value="123456"></constructor-arg>
<constructor-arg name="orderName" value="赵立"></constructor-arg>
</bean>
<!-- 第二种方式 指定参数列表索引注入参数 -->
<bean id="orderEntity" class="com.zhaoli.entity.OrderEntity">
<constructor-arg index="0" value="123456"></constructor-arg>
<constructor-arg index="1" value="赵立"></constructor-arg>
</bean>
(3)第三种实现方式:p名称空间注入(应用较少)
xml头部引入P标签
xmlns:p="http://www.springframework.org/schema/p"
使用p标签注入属性:
<!-- p标签方式注入属性值 实际上最终还是走set方法 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity"
p:bookName="面试宝典aaa" p:bookPrice="99.9"></bean>
3.2注入空值和特殊符号
1.注入空值属性
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity">
<property name="bookName">
<null></null>
</property>
<property name="bookPrice" value="88.8" ></property>
</bean>
给bookName赋值为空
2.注入特殊符号
(1)转义注入方式
<<
转移为:<<
>>
转移为:>>
<!-- 注入特殊符号 例如 < > -->
<!-- (1)转义注入方式 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity">
<!-- <property name="bookName" value="<<面试宝典>>" ></property>-->
<property name="bookName" value="<<面试宝典>>" ></property>
<property name="bookPrice" value="88.8" ></property>
</bean>
(2)cdata注入方式
<![CDATA[ 需要注入的内容 ]]>
<!-- (2)cdata注入方式 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity">
<property name="bookName">
<value><![CDATA[<<面试宝典aaa>>]]></value>
</property>
<property name="bookPrice" value="88.8" ></property>
</bean>
3.3注入属性外部bean
com.zhaoli.dao.MemberDao
(接口)
public interface MemberDao {
void addMember();
}
com.zhaoli.dao.MemberDaoImpl(实现类)
public class MemberDaoImpl implements MemberDao{
@Override
public void addMember() {
System.out.println(">>MemberDao.addMember()<<");
}
}
com.zhaoli.service.MemberService
public class MemberService {
/**
* 使用到属性注入的方式
*/
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
public void addMember() {
System.out.println(">MemberService.addMember()<");
memberDao.addMember();
}
}
Resources\member.xml
<!-- 将memberService注入到ioc容器中 -->
<bean id="memberService" class="com.zhaoli.service.MemberService">
<!--
name="memberDao" MemberService类中属性的名称
ref="memberDao" 在ioc容器中注入的 beanid
-->
<property name="memberDao" ref="memberDao"></property>
</bean>
<!-- 将memberDao注入到ioc容器中 -->
<bean id="memberDao" class="com.zhaoli.dao.MemberDaoImpl"></bean>
Test
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new
ClassPathXmlApplicationContext("member.xml");
MemberService memberService = (MemberService) app.getBean("memberService");
memberService.addMember();
}
3.4注入内部bean(应用较少)
- 数据库表 一对多或者一对一的关系
- 部门–n多个员工 一对多
- 站在员工角度考虑 员工属于那个部门
- 站在部门的角度考虑 部门下n多个员工
员工对象EmpEntity
public class EmpEntity {
private String name;
private String addres;//住址
/**
* 员工属于那个部门
*/
private DeptEntity deptEntity;
}
部门对象 DeptEntity
public class DeptEntity {
private String name;//部门名称
}
Resources\spring_02.xml
<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity">
<property name="name" value="赵立"></property>
<property name="addres" value="陕西省西安市"></property>
<!-- 注入内部bean对象 -->
<property name="deptEntity">
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity">
<property name="name" value="开发部门"></property>
</bean>
</property>
</bean>
Test
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_02.xml");
EmpEntity empEntity = (EmpEntity) app.getBean("empEntity");
System.out.println(empEntity);
}
注意:两个实体类都要生成 set()方法。
3.5注入级联赋值
实体类 员工对象和部门对象同3.4
写法一
Resources\spring_03.xml
<!-- 级联赋值的形式 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity">
<property name="name" value="赵立"></property>
<property name="addres" value="陕西省西安市"></property>
<!-- 级联赋值的形式 -->
<property name="deptEntity" ref="deptEntity"></property>
</bean>
<!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity">
<property name="name" value="教育部门"></property>
</bean>
写法二
Resources\spring_03.xml
<!-- 级联赋值的形式 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity">
<property name="name" value="赵立"></property>
<property name="addres" value="陕西省西安市"></property>
<!-- 级联赋值的形式 -->
<property name="deptEntity" ref="deptEntity"></property>
<property name="deptEntity.name" value="IT部门"></property>
</bean>
<!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity"></bean>
注意:需要在员工实体类新增:deptEntityGet()方法。
3.6注入集合类型属性
实体类 StuEntity
public class StuEntity {
private String[] arrays;
private List<String> list;
private Map<String, String> map;
private Set<String> set;
}
Resources\spring_04.xml
<bean id="stuEntity" class="com.zhaoli.entity.StuEntity">
<!-- 对 arrays 属性赋值 -->
<property name="arrays">
<array>
<value>arrays000</value>
<value>arrays111</value>
</array>
</property>
<!-- 对 list 属性赋值 -->
<property name="list">
<list>
<value>list000</value>
<value>list111</value>
</list>
</property>
<!-- 对 map属性赋值 -->
<property name="map">
<map>
<entry key="000" value="赵立"></entry>
<entry key="111" value="樊靖"></entry>
</map>
</property>
<!-- 对 set 属性赋值 -->
<property name="set">
<set>
<value>set000</value>
<value>set111</value>
</set>
</property>
</bean>
Test
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_04.xml");
StuEntity stuEntity = (StuEntity) app.getBean("stuEntity");
System.out.println(stuEntity);
}
3.7注入集合类型为对象属性
实体类 StuEntity
public class StuEntity {
private String[] arrays;
private List<String> list;
private Map<String, String> map;
private Set<String> set;
/**
* 一名学生可以上 n 多门课程
*/
private List<CourseEntity> courses;
}
实体类 CourseEntity
public class CourseEntity {
private String name;
}
Resources\spring_04.xml
<bean id="stuEntity" class="com.zhaoli.entity.StuEntity">
<!-- 对 arrays 属性赋值 -->
<property name="arrays">
<array>
<value>arrays000</value>
<value>arrays111</value>
</array>
</property>
<!-- 对 list 属性赋值 -->
<property name="list">
<list>
<value>list000</value>
<value>list111</value>
</list>
</property>
<!-- 对 map属性赋值 -->
<property name="map">
<map>
<entry key="000" value="赵立"></entry>
<entry key="111" value="樊靖"></entry>
</map>
</property>
<!-- 对 set 属性赋值 -->
<property name="set">
<set>
<value>set000</value>
<value>set111</value>
</set>
</property>
<!-- 对 courses 属性赋值(集合的类型为对象) -->
<property name="courses">
<list>
<ref bean="courseEntity_java"></ref>
<ref bean="courseEntity_dsj"></ref>
</list>
</property>
</bean>
<!-- java课程 -->
<bean id="courseEntity_java" class="com.zhaoli.entity.CourseEntity">
<property name="name" value="java课程"></property>
</bean>
<!-- 大数据 -->
<bean id="courseEntity_dsj" class="com.zhaoli.entity.CourseEntity">
<property name="name" value="大数据"></property>
</bean>
Test
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_04.xml");
StuEntity stuEntity = (StuEntity) app.getBean("stuEntity");
System.out.println(stuEntity);
}
3.8集合注入部分提取公共
xml头部引入util标签
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
使用util标签提取公共部分:
<!-- 提取公共部分 -->
<util:list id="list">
<value>zhaoli001</value>
<value>zhaoli002</value>
</util:list>
<bean id="stuEntity" class="com.zhaoli.entity.StuEntity">
<!-- 对 list 属性赋值 -->
<property name="list" ref="list"></property>
</bean>
3.9 IOC操作Bean的管理
- Spring中两种类型bean,一种是为普通的bean,另外一种是工厂bean FactoryBean
- 普通Bean:在配置文件中定义什么类型与返回的类型需一致;
- 工厂Bean:在配置文件中定义Bean类型与返回类型可以不一致;
创建一个类,这个类是为工厂Bean,实现FactoryBean接口
4.Spring的工厂Bean
4.1 SpringBean的作用域
什么是作用域?设定bean作用域是为单例还是多例
作用域单例与多例有什么区别呢?
- 单例的作用域:每次在调用getbean方法获取对象都是为同一个对象。
- 多例的作用域:每次在调用getbean方法获取对象都是一个新的对象。
注意:在spring默认的情况下,bean的作用域就是为单例,节约服务器内存。
单例:在同一个jvm中,该bean对象只会创建一次。
多例:在同一个jvm中,该bean对象可以被创建多次。
设定对象单例还是多例
在spring的默认的情况下,springbean的作用域为单例。
- 单例就是每次获取bean都是同一个对象;
- 多例就是每次获取bean都是新的一个对象;
单例:在同一个jvm中该bean只能存在一个实例;
多例:在同一个jvm中该bean存在多个实例;
证明:如果是为单例,则两个对象地址都是一样的,多例子对象则两个对象地址不一样。
单例配置:(默认就是为单例子)
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="singleton"></bean>
多例配置:
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="prototype"></bean>
4.2 SpringBean的生命周期
简单分为:实例化→属性赋值→初始化→销毁
生命周期概念:对象的创建与销毁的过程,类似之前学习servlet生命的周期过程。
生命周期的原理:
- 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
- 为bean的属性设置(使用反射调用set方法)
- 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
- 正常使用bean对象
- Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)
com.zhaoli.entity.MemberEntity
public class MemberEntity {
private String name;
public MemberEntity(){
System.out.println("[第一步流程:]执行MemberEntity无参构造函数");
}
public void setName(String name) {
System.out.println("[第二步流程:]执行setName方法");
this.name = name;
}
public void init(){
System.out.println("[第三步流程:]调用init方法");
}
public void destroy(){
System.out.println("[第五步流程:]调用destroy方法");
}
}
Resources\spring_06.xml
<!--
初始化的方法 init-method="init"
销毁回调的方法 destroy-method="destroy"
-->
<bean id="memberEntity" class="com.zhaoli.entity.MemberEntity" init-method="init" destroy-method="destroy">
<property name="name" value="赵立"></property>
</bean>
test
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_06.xml");
MemberEntity memberEntity = app.getBean("memberEntity", MemberEntity.class);
System.out.println("[第四步流程:]获取使用到的memberEntity");
app.close();//手动让 bean 容器销毁
}
Bean的后置处理器 作用提供更多的扩展功能 BeanPostProcessor
- 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
- 为bean的属性设置 (使用反射调用set方法)
- 将bean传递给后置处理器 调用初始化方法之前执行
- 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
- 将bean传递给后置处理器 调用初始化方法之后执行
- 正常使用bean对象
- Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)
com.zhaoli.entity.MemberEntity
同上
com.zhaoli.bean.MyPostProcessor
public class MyPostProcessor implements BeanPostProcessor {
/**
* 调用 init 方法之前处理
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[后置处理器调用init方法之前执行操作..]");
return bean;
}
/**
* 调用 init 方法之后处理
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[后置处理器调用init方法之后执行操作..]");
return bean;
}
}
Resources\spring_06.xml
(加上下面内容)
<!-- 注册 bean 对象的后置处理器 MyPostProcessor -->
<bean id="myPostProcessor" class="com.zhaoli.bean.MyPostProcessor"></bean>
Test
同上
配置多个 BeanPostProcessor
实现Ordered接口重写 getOrder()方法 返回的值越小越优先加载
com.zhaoli.bean.MyPostProcessor
public class MyPostProcessor implements BeanPostProcessor, Ordered {
/**
* 调用 init 方法之前处理
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[后置处理器调用init方法之前执行操作..]");
return bean;
}
/**
* 调用 init 方法之后处理
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[后置处理器调用init方法之后执行操作..]");
return bean;
}
@Override
public int getOrder() {
return 1;
}
}
com.zhaoli.bean.MyPostProcessor2
public class MyPostProcessor2 implements BeanPostProcessor, Ordered {
/**
* 调用 init 方法之前处理
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MyPostProcessor2:][后置处理器调用init方法之前执行操作..]");
return bean;
}
/**
* 调用 init 方法之后处理
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MyPostProcessor2:][后置处理器调用init方法之后执行操作..]");
return bean;
}
@Override
public int getOrder() {
return 0;
}
}
Resources\spring_06.xml
<!--
初始化的方法 init-method="init"
销毁回调的方法 destroy-method="destroy"
-->
<bean id="memberEntity" class="com.zhaoli.entity.MemberEntity" init-method="init" destroy-method="destroy">
<property name="name" value="赵立"></property>
</bean>
<!-- 注册 bean 对象的后置处理器 MyPostProcessor -->
<bean id="myPostProcessor" class="com.zhaoli.bean.MyPostProcessor"></bean>
<!-- 注册 bean 对象的后置处理器 MyPostProcessor2 -->
<bean id="myPostProcessor2" class="com.zhaoli.bean.MyPostProcessor2"></bean>
Test
同上
4.3 SpringBean的自动装配
bean 标签中有一个属性 autowire
- byName 根据属性的名称自动装配 bean的id名称与属性的名称一致
- byType 根据属性的类型自动装配 bean的类型与属性类型一致(不能配置多个部门对象)
员工对象 EmpEntity
public class EmpEntity {
private String name;
private String addres;//住址
/**
* 员工属于那个部门
*/
private DeptEntity deptEntity;
}
部门对象 DeptEntity
public class DeptEntity {
private String name;//部门名称
}
根据属性的名称自动装配 bean的id名称与属性的名称一致
<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity" autowire="byName">
<property name="name" value="赵立"></property>
<property name="addres" value="陕西省西安市"></property>
</bean>
<!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity">
<property name="name" value="开发部门"></property>
</bean>
根据属性的类型自动装配 bean的类型与属性类型一致
<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity" autowire="byType">
<property name="name" value="赵立"></property>
<property name="addres" value="陕西省西安市"></property>
</bean>
<!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity">
<property name="name" value="开发部门"></property>
</bean>
5.SpringBean的AOP
AOP基本的概念
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。
简单理解:
Aop面向切面编程,在方法之前和之后实现处理 应用场景在于:日志打印、事务实现、安全、权限控制、自定义注解等。 因为AOP可以解决我们程序上的代码冗余问题。
AOP 底层基于代理设计模式封装
代理设计模式 静态代理与动态代理
动态代理 jdk动态代理与 cglib动态代理
通俗易懂 aop 在我们的目标方法之前和之后处理的操作
开启事务
目标方法
提交或者回滚事务
5.1代理模式
代理模式应用场景
- 日志的采集
- 权限控制
- 实现aop
- Mybatis mapper
- Spring的事务
- 全局捕获异常
- Rpc远程调用接口
- 代理数据源
代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy)
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
5.1.1 代理模式创建方式
com.mayikt.service.OrderService(
接口)
public interface OrderService {
/**
* 添加订单数据
*/
String addOrder(String orderName);
}
com.mayikt.service.impl.OrderServiceImpl
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceImpl implements OrderService {
@Override
public String addOrder(String orderName) {
log.info("<orderName:{}>", orderName);
return "ok";
}
}
5.1.2 静态代理
基于接口实现方式
com.mayikt.proxy1.OrderServiceProxy
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceProxy implements OrderService {
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public String addOrder(String orderName) {
// 目标方法前后处理操作
log.info("<目标方法之前执行...>");
String result = orderService.addOrder(orderName);
log.info("<目标方法之后执行...>");
return result;
}
}
Tses
public static void main(String[] args) {
OrderServiceProxy orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());
String result = orderServiceProxy.addOrder("mayikt");
System.out.println(result);
}
基于继承实现方式
com.mayikt.proxy2.OrderServiceProxy
import com.mayikt.service.impl.OrderServiceImpl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceProxy extends OrderServiceImpl {
@Override
public String addOrder(String orderName) {
// 目标方法前后处理操作
log.info("<目标方法之前执行...>");
String result = super.addOrder(orderName);
log.info("<目标方法之后执行...>");
return result;
}
}
Tses
public static void main(String[] args) {
OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
String result = orderServiceProxy.addOrder("mayikt");
System.out.println(result);
}
5.1.3 动态代理
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。
JDK动态代理的一般步骤如下:
- 创建被代理的接口和类;
- 实现
InvocationHandler
接口,对目标接口中声明的所有方法进行统一处理; - 调用
Proxy
的静态方法,创建代理类并生成相应的代理对象;
实现原理:利用拦截器机制必须实现InvocationHandler
接口中的invoke
方法实现对我们的目标方法增强。
5.2 AOP详解
5.2.1 Aop常用术语
- 连接点(Join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。类中的哪些方法可以被增强,这些方法就被称作为连接点。
- 切点(PointCut): 可以插入增强处理的连接点,实际被增强的方法就称作为切入点
- 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理, 实际增强的业务逻辑,该过程就可以称作为通知 前置、后置、环绕通知
- 切面(Aspect): 切面是通知和切点的结合。 把通知应用到的过程 就是为切面
- 引入(Introduction):允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的代理对象
- 连接点 该类中哪些方法需要被增强,这些方法就可以称作连接点
- 切点 实际被增强的方法
2 .通知 在方法前后执行代码
前置通知 调用方法之前处理…
后置通知 调用完该方法之后处理
环绕通知 在我们被代理方法前后执行
异常通知
最终通知
4 .切面 把通知应用到的过程 就是为切面
5.2.2 Aop环境准备
- Spring框架一般都是基于AspectJ实现AOP操作
(1)什么是AspectJ
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件,AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作.
- 基于AspectJ实现AOP
(1)基于xml配置文件实现
(2)基于注解方式(偏多的)
- 在项目工程目录引入AOP依赖
maven依赖
<!-- aspectj支持(切面依赖) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8_2</version>
</dependency>
<!-- SpringAOP依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
切入点表达式
具体那个类中的那个方法来实现增强
需要描述 该类中哪些方法是需要被增强-----切入点规则
语法规范:execution( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]));
例如:
public.String.com.zhaoli.service.MayiktService.addUser(..)
--拦截的是MayiktService类中addUser方法名称 所有参数 返回值String
2.* com.zhaoli.service.MayiktService.*(..)
拦截我们的MayiktService类中的所有方法
3.* com.zhaoli.service.*.*(..)
拦截就是我们 com.mayikt.service.包下的所有的类所有的方法。
//举例1:对com.zhaoli.service.MayiktService
类里面的 add()
进行增强 execution(*com.zhaoli.service.MayiktService.add(..));
// * 表示所有, … 表示参数列表
//举例2:对com.zhaoli.service.MayiktService
类里面的 所有方法 进行增强 execution(*com.zhaoli.service.MayiktService.*(..));
//举例3:对com.zhaoli.service.MayiktService
所有类里面的 所有方法 进行增强 execution(*com.zhaoli.service.MayiktService.*.*(..));
开启springaop
这是配置 spring.xml 的模板可以直接粘贴
<?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
">
注意粘贴时将这一段代码替换掉
测试代码(前置通知\后置通知)
com.zhaoli01.service.MayiktService
import org.springframework.stereotype.Component;
@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
public class MayiktService {
public String addUser() {
System.out.println("addUser...");
return "ok";
}
}
com.zhaoli01.proxy.UserProxy
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {
/**
* //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法
* 前置通知
*/
@Before(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
public void before() {
System.out.println("在目标方法之前执行...");
}
/**
* 后置通知
*/
@After(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
public void after() {
System.out.println("在目标方法之后执行...");
}
}
Resources\spring_08.xml
<?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.zhaoli01"></context:component-scan>
<!-- 开启切面 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
test`
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext= new ClassPathXmlApplicationContext("spring_08.xml");
MayiktService mayiktService = classPathXmlApplicationContext.getBean("mayiktService", MayiktService.class);
mayiktService.addUser();
}
测试代码(环绕通知)
com.zhaoli01.service.MayiktService
和Resources\spring_08.xml
同上
com.zhaoli01.proxy.UserProxy
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {
/**
* //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法
* 前置通知
*/
@Before(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
public void before() {
System.out.println("在目标方法之前执行...");
}
/**
* 后置通知
*/
@After(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
public void after() {
System.out.println("在目标方法之后执行...");
}
/**
* 环绕通知 前置+后置组合
*/
@Around(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知调用目标方法之前...");
Object result = proceedingJoinPoint.proceed();//调用我们目标方法
System.out.println("环绕通知调用目标方法之后...");
return result;
}
}
返回通知、表达异常通知
//@AfterReturning表达后置通知/返回通知,表达方法返回结果之后执行
@AfterReturning(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void afterReturning() {
System.out.println("afterReturning");
}
//@AfterThrowing表达异常通知
@AfterThrowing(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void afterThrowing() {
System.out.println("afterThrowing");
}
代码优化
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {
/**
* 定义切入点 用的时候直接调用 减少代码冗余
*/
@Pointcut(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")
private void pointcut() {
}
/**
* //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法
* 前置通知
*/
@Before(value = "pointcut()")
public void before() {
System.out.println("在目标方法之前执行...");
}
/**
* 后置通知
*/
@After(value = "pointcut()")
public void after() {
System.out.println("在目标方法之后执行...");
}
/**
* 环绕通知 前置+后置组合
*/
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知调用目标方法之前...");
Object result = proceedingJoinPoint.proceed();//调用我们目标方法
System.out.println("环绕通知调用目标方法之后...");
return result;
}
}
spring框架中使用 cglib?jdk动态代理?
spring aop 底层基于代理模式封装
如果我们被代理类 有实现接口的情况下 则使用 jdk动态代理
如果我们 被代理类 没有实现接口的情况下 则使用 cglib动态代理