「2020最新」Spring最易学习教程 3— 代理设计模式 Spring AOP 动态代理原理
0 复习
- FactoryBean技术(用于创建复杂对象)
复杂对象:底层不能直接通过new构造方法创建,通常需要若干步骤才能创建的对象。比如:Connection、SqlSession
- 编码 implements FactoryBean
- 配置 通过bean标签配置
- 配置文件
- import标签
- xsd使用规则
- 拆分jdbc.properties文件
- 概念总结 IOC和DI
- Spring整合Struts2 导入依赖:spring-web struts2-spring-plugin
web.xml 在tomcat启动应用时,创建Spring工厂
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 注解开发 Component(Controller、Service、Repository)替换bean标签 Autowired替换property标签
Spring AOP
1 代理设计模式
1.1 解决的问题
要不要在业务层中定义额外功能?
业务调用者的角度:需要,业务方法中需要使用这些额外功能 软件设计者的角度:不需要,定义后会造成代码的频繁修改
矛盾的解决方案:代理模式
1.2 代理模式
矛盾:
房东不愿意提供额外功能(带看房,车接车送),因为麻烦 租客必须要使用这些额外功能
矛盾的解决方案:中介
中介代理了房东的出租房屋的方法,同时提供额外的功能。 房东无需亲自和租客打交道,节省时间。租客也可以享受完整的服务。
在程序中,Action(租客) 和 Serivce(房东) 的矛盾,也可以通过添加一个代理类解决。
1.3 静态代理
实战:
接口:
public interface UserService {
public boolean login(String username,String password);
public void removeUser(Integer id);
}
原始实现类:
public class UserServiceImpl implements UserService {
@Override
public boolean login(String username, String password) {
System.out.println("username = [" + username + "], password = [" + password + "]");
return false;
}
@Override
public void removeUser(Integer id) {
System.out.println("id = [" + id + "]");
}
}
代理实现类:
public class UserServiceProxy implements UserService {
private UserService userService = new UserServiceImpl();
@Override
public boolean login(String username, String password) {
System.out.println("开启事务");
boolean result = userService.login(username,password);
System.out.println("结束事务");
return result;
}
@Override
public void removeUser(Integer id) {
System.out.println("开启事务");
userService.removeUser(id);
System.out.println("结束事务");
}
}
使用者:
public class UserAction {
private UserService userService = new UserServiceProxy();
public String login(){
userService.login("xiaohei", "123456");
return "success";
}
public String removeUser(){
userService.removeUser(1);
return "success";
}
}
2 Spring动态代理
静态代理的问题:
- 随着额外功能的增多,代理类数量随之增多,不利于管理
- 代理类冗余,存在多个代理类提供相同的功能
解决方案:动态代理
Spring动态代理:无需程序员手动编写代理类,只需要提供额外功能代码,然后由Spring框架自动的为原始类生成有增强功能的代理类。
好处:提高开发效率。
开发步骤:
额外引入以下2个依赖:spring-aop aspectjweaver
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
创建原始类型对象
<bean id="标识" class="原始(目标)类全类名"></bean>
定义额外功能。实现Spring的特定接口
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
/*
method: 原始类型中方法
args: 参数
target: 原始对象
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before...");
}
}
配置额外功能类
<!-- 配置功能增强类 -->
<bean id="myBeforeAdvice" class="com.bcl.advice.MyMethodBeforeAdvice"/>
- 定义切入点:决定额外功能添加到哪个位置
组装
<aop:config>
<!-- 定义切入点-->
<aop:pointcut id="myPointCut" expression="execution(* com.bcl.service.*.*(..))"/>
<!-- 组装 -->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPointCut"/>
</aop:config>
名词(术语)解释:
- 原始类(目标类):提供核心功能的类
- 原始方法(目标方法):原始类中没有加入额外功能的方法
- 额外功能(增强):用于增强原始方法的代码
3 Spring动态代理的实现流程
4 增强(Advice)
4.1 前置增强
增强会在目标方法前执行,实现接口 MethodBeforeAdvice
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
/*
method: 原始方法
args: 调用方法时的实参列表
target: 原始对象
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("method = [" + method + "], args = [" + Arrays.toString(args) + "], target = [" + target + "]");
System.out.println("target.getClass() = " + target.getClass());
System.out.println("前置增强");
}
}
4.2 后置增强
增强代码会在目标方法正常return后执行,实现接口 AfterReturningAdvice
public class MyAfterReturningAdvice implements AfterReturningAdvice {
/*
returnValue: 原始方法正常结束后的返回值
method: 原始方法
args: 实参列表
target: 原始对象
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置增强");
}
}
4.3 异常增强
增强代码在目标方法发生异常时执行,实现接口 ThrowsAdvice
public class MyThrowsAdvice implements ThrowsAdvice {
/*
method: 原始方法
args: 实参列表
target: 原始对象
throwable: 原始方法运行时的异常
*/
public void afterThrowing(Method method, Object[] args,Object target, Throwable throwable){
System.out.println("异常增强");
}
}
4.4 环绕增强
增强代码在目标方法前后以及异常时都可以执行,实现接口 MethodInterceptor
public class MyRoundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation pj) throws Throwable {
System.out.println("前置增强");
Object result = null;
try {
//放行:调用原始方法
result = pj.proceed();
System.out.println("后置增强");
}catch(Exception e){
System.out.println("异常增强");
throw e;
}finally{
System.out.println("最终增强");
}
return result;
}
}
5 切入点详解
切入点:需要做功能增强处理的位置,可以通过切入点表达式描述。
5.1 execution表达式[重点]
实战中,使用 execution(* com.bcl.service.*.*(..))
添加额外功能。
5.2 args表达式
用来匹配特定参数的方法。
args(参数列表)
5.3 within表达式
用来匹配特定的类,根据类名匹配。
within(类名表达式)
5.4 @annotation表达式
@annotation(注解类型),通过特定的注解来匹配类。
自定义一个注解
public @interface MyAnnotation {
}
使用自定义注解描述方法(实现类中的方法)
public class UserServiceImpl implements UserService {
@Override
@MyAnnotation
public boolean login(String username, String password) {
System.out.println("username = [" + username + "], password = [" + password + "]");
return false;
}
@Override
public void removeUser(Integer id) {
System.out.println("id = [" + id + "]");
//int i = 10/0;
}
}
切入点配置为 @annotation(自定义注解类型)
<aop:pointcut id="myPointCut" expression="@annotation(com.bcl.annotation.MyAnnotation)"/>
5.5 表达式的运算符
表达式之间可用过 and or not运算。
6 Spring AOP
AOP(Aspect Oriented Programming)面向切面编程。
OOP(Object Oriented Programming)面向对象编程。
6.1 OOP(面向对象编程)
面向对象编程以对象为单位进行编程,抽取共性方法,通过继承重用这些方法。
6.2 Spring AOP
AOP为了解决程序中零散的共性代码的复用问题,是OOP有力补充。
- 增强:共性代码,额外功能。比如:事务、性能分析
- 切入点:添加额外功能的方法的位置
- 织入(编织):将增强添加到切入点的过程
- 切面:在切点织入增强代码后形成的一个几何平面的概念
面向切面编程的要素:增强、切点和织入
面向切面编程的作用:灵活的以非侵入的方式(非耦合式)为现有的方法增强功能。
Spring切面编程的步骤:
- 配置原始类型对象
- 定义额外功能(增强)
- 配置增强类
- 定义切入点
- 编织
7 数据库中事务的隔离级别
事务的隔离级别:事务并发执行时,微观上多个执行时间相近的事务相互影响的问题。
标准的隔离级别4种:
隔离级别 |
特点 |
问题 |
---|---|---|
READ_UNCOMMITTED |
可以读取到未提交的事务 |
脏读 |
READ_COMMITTED |
只能读到已经提交的事务 |
不可重复读 |
REPEATABLE_READ |
同1个事务中读取到数据始终一致 |
幻影读 |
SERIALIZABLE |
序列化读,不允许并发操作 |
性能差 |
Oracle数据库,只支持2种:READ——COMMITTED
和 SERIALIZABLE
,MySQL支持4种。
MySQL隔离级别的演示:
- 确认MySQL的存储引擎
show engines;
- 查看隔离级别
select @@session.tx_isolation;
- 设置隔离级别
set @@session.tx_isolation=0 | 1 | 2 | 3
- 开启事务
begin;
- 结束事务
commit;
rollback;
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Android开发实现各种图形绘制功能示例
- Android webview手动校验https证书(by 星空武哥)
- AndroidStudio Gradle第三依赖统一管理的实现方法
- 小程序上传多张图片到springboot后台,返回可供访问的图片链接
- AndroidStudio Gradle基于友盟的多渠道打包方法
- Android开发之全屏与非全屏的切换设置方法小结
- Android使用GridView实现日历的方法
- Android控件AppWidgetProvider使用方法详解
- R语言使用链梯法Chain Ladder和泊松定律模拟和预测未来赔款数据
- Android ViewPager实现左右滑动的实例
- R语言通过伽玛与对数正态分布假设下的广义线性模型对大额索赔进行评估预测
- R语言中回归模型预测的不同类型置信区间应用比较分析
- 第06期:Prometheus 存储
- 新特性解读 | 数组范围遍历功能
- 技术分享 | MySQL 内存管理初探