文章目录
- 【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>
springtransaction_128">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类
java">//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
java">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
java">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
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
java">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方法、有参构造函数注入属性
- 基于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
java">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);}@Overridepublic 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.注入空值属性
java"><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
(接口)
java">public interface MemberDao {void addMember();
}
com.zhaoli.dao.MemberDaoImpl(实现类)
public class MemberDaoImpl implements MemberDao{@Overridepublic 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
java">public static void main(String[] args) {ClassPathXmlApplicationContext app = newClassPathXmlApplicationContext("member.xml");MemberService memberService = (MemberService) app.getBean("memberService");memberService.addMember();}
3.4注入内部bean(应用较少)
- 数据库表 一对多或者一对一的关系
- 部门–n多个员工 一对多
- 站在员工角度考虑 员工属于那个部门
- 站在部门的角度考虑 部门下n多个员工
员工对象EmpEntity
java">public class EmpEntity {private String name;private String addres;//住址/*** 员工属于那个部门*/
private DeptEntity deptEntity;
}
部门对象 DeptEntity
java">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
java">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
java">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
java">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
java">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
java">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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://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
java">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
java">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
java">public class MyPostProcessor implements BeanPostProcessor {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之后执行操作..]");return bean;}
}
Resources\spring_06.xml
(加上下面内容)
java"><!-- 注册 bean 对象的后置处理器 MyPostProcessor -->
<bean id="myPostProcessor" class="com.zhaoli.bean.MyPostProcessor"></bean>
Test
同上
配置多个 BeanPostProcessor
实现Ordered接口重写 getOrder()方法 返回的值越小越优先加载
com.zhaoli.bean.MyPostProcessor
java">public class MyPostProcessor implements BeanPostProcessor, Ordered {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之后执行操作..]");return bean;}@Overridepublic int getOrder() {return 1;}
}
com.zhaoli.bean.MyPostProcessor2
java">public class MyPostProcessor2 implements BeanPostProcessor, Ordered {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[MyPostProcessor2:][后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[MyPostProcessor2:][后置处理器调用init方法之后执行操作..]");return bean;}@Overridepublic 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
java">public class EmpEntity {private String name;private String addres;//住址/*** 员工属于那个部门*/
private DeptEntity deptEntity;
}
部门对象 DeptEntity
java">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(
接口)
java">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 {@Overridepublic String addOrder(String orderName) {log.info("<orderName:{}>", orderName);return "ok";}
}
5.1.2 静态代理
基于接口实现方式
com.mayikt.proxy1.OrderServiceProxy
java">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;}@Overridepublic String addOrder(String orderName) {// 目标方法前后处理操作log.info("<目标方法之前执行...>");String result = orderService.addOrder(orderName);log.info("<目标方法之后执行...>");return result;}
}
Tses
java">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
java">import com.mayikt.service.impl.OrderServiceImpl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceProxy extends OrderServiceImpl {@Overridepublic String addOrder(String orderName) {// 目标方法前后处理操作log.info("<目标方法之前执行...>");String result = super.addOrder(orderName);log.info("<目标方法之后执行...>");return result;}
}
Tses
java"> 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
注意粘贴时将这一段代码替换掉
测试代码(前置通知\后置通知)
com.zhaoli01.service.MayiktService
java">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
java">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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://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`
java">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
java">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;}
}
返回通知、表达异常通知
java">//@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");}
代码优化
java">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动态代理