[Spring框架]Spring AOP基础入门总结一.
前言:
前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP.
一, 什么是AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码.
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码.
二,AOP开发中的专业术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
- Target(目标对象):代理的目标对象
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入. - Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类Aspect(切面): 是切入点和通知(引介)的结合
三, 动态代理
spring AOP核心技术就是使用了Java 的动态代理技术, 这里简单的总结下JDK和CGLIB两种动态代理机制.
概念:
当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象。按照代理对象的创建时期不同,可以分为两种:
静态代理:程序员事先写好代理对象类,在程序发布前就已经存在了;
动态代理:应用程序发布后,通过动态创建代理对象。
其中动态代理又可分为:JDK/Cglib 动态代理.
3.1 JDK动态代理
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。
JDK动态代理只能针对实现了接口的类生成代理。
代码实例:
UserService.java:
public interface UserService {
public void save(); public void update(); public void delete(); public void find();
}
UserServiceImpl.java:
public class UserServiceImpl implements UserService { @Override
public void save() {
System.out.println("保存用户...");
} @Override
public void update() {
System.out.println("修改用户...");
} @Override
public void delete() {
System.out.println("删除用户...");
} @Override
public void find() {
System.out.println("查询用户...");
} }
MyJdbProxy.java:
/**
* 使用JDK的动态代理实现代理机制
*
*/
public class MyJdbProxy implements InvocationHandler{ private UserService userService; public MyJdbProxy(UserService userService){
this.userService = userService;
} public UserService createProxy(){
// 生成UserSErvice的代理:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), this);
return userServiceProxy;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 判断是否是save方法:
if("save".equals(method.getName())){
// 增强:
System.out.println("权限校验===========");
return method.invoke(userService, args);
}
return method.invoke(userService, args);
} }
SpringDemo.java 测试类:
public class SpringDemo1 { @Test
// 没有代理的时候的调用方式
public void demo1() {
// 创建目标对象
UserService userService = new UserServiceImpl(); userService.save();
userService.update();
userService.delete();
userService.find();
} @Test
// 使用代理
public void demo2() {
// 创建目标对象
UserService userService = new UserServiceImpl();
UserService proxy = new MyJdbProxy(userService).createProxy(); proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
3.2 CGLIB 动态代理
CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
代码实例:
CustomerService.java:
public class CustomerService {
public void save(){
System.out.println("保存客户...");
}
public void update(){
System.out.println("修改客户...");
}
public void delete(){
System.out.println("删除客户...");
}
public void find(){
System.out.println("查询客户...");
}
}
MyCglibProxy.java:
/**
* 使用Cglib产生代理
*
*/
public class MyCglibProxy implements MethodInterceptor{ private CustomerService customerService; public MyCglibProxy(CustomerService customerService){
this.customerService = customerService;
} public CustomerService createProxy(){
// 创建核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(customerService.getClass());
// 设置回调:
enhancer.setCallback(this);
// 创建代理:
CustomerService customerServiceProxy = (CustomerService) enhancer.create();
return customerServiceProxy;
} @Override
public Object intercept(Object proxy, Method method, Object[] arg,
MethodProxy methodProxy) throws Throwable {
if("delete".equals(method.getName())){
Object obj = methodProxy.invokeSuper(proxy, arg);
System.out.println("日志记录==========");
return obj;
}
return methodProxy.invokeSuper(proxy, arg);
}
}
SpringDemo.java 测试类:
public class SpringDemo2 { @Test
public void demo1(){
CustomerService customerService = new CustomerService();
customerService.save();
customerService.update();
customerService.delete();
customerService.find();
} @Test
public void demo2(){
CustomerService customerService = new CustomerService();
// 产生代理:
CustomerService proxy = new MyCglibProxy(customerService).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作。
四, Spring的传统的AOP:基于ProxyFactoryBean的方式的代理
4.1 Spring的通知类型
前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
4.2 Spring的切面类型
Advisor : 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截.(不带有切入点的切面,默认增强类中所有方法)
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法.(带有切入点的切面)
4.3 Spring传统AOP的快速入门
4.3.1 不带有切入点的切面开发
AOP开发需要引入的相关jar包:
ProductService.java:
/**
* 商品业务层接口
*
*
*/
public interface ProductService {
public void save(); public void update(); public void delete(); public void find();
}
ProductServiceImpl.java:
/**
* 商品业务层实现类
*
*/
public class ProductServiceImpl implements ProductService { @Override
public void save() {
System.out.println("保存商品...");
} @Override
public void update() {
System.out.println("修改商品...");
} @Override
public void delete() {
System.out.println("删除商品...");
} @Override
public void find() {
System.out.println("查询商品...");
} }
MyBeforeAdvice.java:
/**
* 自定义的前置增强:
*
*/
public class MyBeforeAdvice implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置增强=============");
} }
SpringDemo.java 测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name="productServiceProxy")
private ProductService productService; @Test
/**
* 传统方式:
*/
public void demo1(){
productService.save();
productService.update();
productService.delete();
productService.find();
}
}
4.3.1 带有切入点的切面开发
OrderService.java:
public class OrderService {
public void save(){
System.out.println("添加订单...");
}
public void update(){
System.out.println("修改订单...");
}
public void delete(){
System.out.println("删除订单...");
}
public void find(){
System.out.println("查询订单...");
}
}
MyAroundAdvice.java:
/**
* 定义的环绕增强:
*
*/
public class MyAroundAdvice implements MethodInterceptor{ @Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前增强===============");
// 执行目标方法:
Object obj = methodInvocation.proceed();
System.out.println("环绕后增强===============");
return obj;
} }
SpringDemo.java 测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo4 { @Resource(name="orderServiceProxy")
private OrderService orderService; @Test
public void demo1(){
orderService.save();
orderService.update();
orderService.delete();
orderService.find();
}
}
applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置目标对象 -->
<bean id="productService" class="cn.augmentum.aop.demo3.ProductServiceImpl"></bean> <!-- 配置前置增强 -->
<bean id="myBeforeAdvice" class="cn.augmentum.aop.demo3.MyBeforeAdvice"/> <!-- 配置生成代理 -->
<bean id="productServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- ProxyFactoryBean常用可配置属性
target : 代理的目标对象
proxyInterfaces : 代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
<value></value>
....
</list>
proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
interceptorNames : 需要织入目标的Advice
singleton : 返回代理是否为单实例,默认为单例
optimize : 当设置为true时,强制使用CGLib
-->
<property name="target" ref="productService"/>
<property name="proxyInterfaces" value="cn.augmentum.aop.demo3.ProductService"/>
<property name="interceptorNames" value="myBeforeAdvice"/> </bean> <bean id="orderService" class="cn.augmentum.aop.demo4.OrderService"/> <bean id="myAroundAdvice" class="cn.augmentum.aop.demo4.MyAroundAdvice"/> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 正则表达式: . :代表任意字符 *:任意次数 -->
<!-- <property name="pattern" value=".*"/> -->
<property name="pattern" value="cn\.augmentum\.aop\.demo4\.OrderService\.save"/>
<property name="advice" ref="myAroundAdvice"/>
</bean> <bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="orderService"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="advisor"/>
</bean> </beans>
基于Spring FactoryBean代理的总结:
Spring会根据类是否实现接口采用不同的代理方式:
* 实现接口:JDK动态代理.
* 没有实现接口:CGLIB生成代理.
基于ProxyFactoryBean的方式生成代理的过程中不是特别理想:
* 配置繁琐,不利为维护.
* 需要为每个需要增强的类都配置一个ProxyFactoryBean.
五, Spring的传统的AOP:基于自动代理
5.1 自动代理和基于ProxyFactoryBean代理方式比较:
自动代理基于BeanPostProcessor(前一篇文章讲过的工厂钩子)完成的代理.
* 在类的生成过程中就已经是代理对象.
基于ProxyFactoryBean方式代理:
* 先有目标对象,根据目标对象产生代理.
5.2自动代理的方式:
BeanNameAutoProxyCreator:基于Bean名称的自动代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置目标对象 -->
<bean id="productService" class="cn.augmentum.aop.demo3.ProductServiceImpl"></bean>
<bean id="orderService" class="cn.augmentum.aop.demo4.OrderService"/> <!-- 配置前置增强 -->
<bean id="myBeforeAdvice" class="cn.augmentum.aop.demo3.MyBeforeAdvice"/>
<bean id="myAroundAdvice" class="cn.augmentum.aop.demo4.MyAroundAdvice"/> <!-- 基于Bean名称的自动代理 -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Service"/>
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
</beans>
DefaultAdvisorAutoProxyCreator:基于切面信息的自动代理
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置目标对象 -->
<bean id="productService" class="cn.augmentum.aop.demo3.ProductServiceImpl"></bean>
<bean id="orderService" class="cn.augmentum.aop.demo4.OrderService"/> <!-- 配置前置增强 -->
<bean id="myBeforeAdvice" class="cn.augmentum.aop.demo3.MyBeforeAdvice"/>
<bean id="myAroundAdvice" class="cn.augmentum.aop.demo4.MyAroundAdvice"/> <!-- 配置切面信息 -->
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="myAroundAdvice"/>
<!-- <property name="pattern" value="cn\.augmentum\.aop\.demo4\.OrderService\.save"/> -->
<property name="patterns" value="cn\.itcast\.aop\.demo4\.OrderService\.save,cn\.augmentum\.aop\.demo3\.ProductService\.update"/>
</bean> <!-- 配置基于切面信息自动代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
Spring AOP最基础的知识大概就是这些了, 由于篇幅的限制及阅读的观感, 所以打算再写个Spring AOP基础入门二 来总结下 Spring基于AspectJ的AOP的开发, 这个地方才是重点, 当然这一篇博文的知识也是有利于大家对Spring AOP有了一个整体的认识了. 文章内容皆是自己学习工作整理所得, 如若有问题 欢迎指正.
[Spring框架]Spring AOP基础入门总结一.的更多相关文章
- [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.
前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...
- Spring笔记:AOP基础
Spring笔记:AOP基础 AOP 引入AOP 面向对象的开发过程中,我们对软件开发进行抽象.分割成各个模块或对象.例如,我们对API抽象成三个模块,Controller.Service.Comma ...
- Spring框架之AOP源码完全解析
Spring框架之AOP源码完全解析 Spring可以说是Java企业开发里最重要的技术.Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Orie ...
- Spring框架的AOP技术(注解方式)
1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包 * 再引入Spring框架的AOP的开发包 * spring的传统AOP的开发的包 * sp ...
- 10.Spring——框架的AOP
1.Spring 框架的 AOP 2.Spring 中基于 AOP 的 XML架构 3.Spring 中基于 AOP 的 @AspectJ 1.Spring 框架的 AOP Spring 框架的一个关 ...
- Spring 框架的 AOP 简介
Spring 框架的 AOP Spring 框架的一个关键组件是面向方面的编程(AOP)(也称为面向切面编程)框架. 面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点. 跨一个应用程序的多 ...
- 使用spring框架进行aop编程实现方法调用前日志输出
aop编程 之使用spring框架实现方法调用前日志输出 使用spring框架实现AOP编程首先需要搭建spring框架环境: 使用Spring框架实现AOP工程编程之后,不需要我们去写代理工厂了,工 ...
- spring boot集成redis基础入门
redis 支持持久化数据,不仅支持key-value类型的数据,还拥有list,set,zset,hash等数据结构的存储. 可以进行master-slave模式的数据备份 更多redis相关文档请 ...
- Spring框架 - Spring和Spring框架组成
Spring框架 - Spring和Spring框架组成 Spring是什么?它是怎么诞生的?有哪些主要的组件和核心功能呢? 本文通过这几个问题帮助你构筑Spring和Spring Framework ...
随机推荐
- mvc之文件下载
首先你要有四张图片,也就是数组中的数 public ActionResult Index()//创建视图{ViewBag.list =new int[] { 5, 6, 7,8 };return Vi ...
- Auto_increment详解
Auto_increment Mysql AUTO_INCREMENT 1.Innodb表的自动增长列可以手工插入,但是插入的值如果是空或者0,则实际插入的将是自动增长后的值 mysql> cr ...
- 高速PCB之EMC设计47则
高速PCB之EMC设计47则 差模电流和共模电流 辐射产生 电流导致辐射,而非电压,静态电荷产生静电场,恒定电流产生磁场,时变电流既产生电场又产生磁场.任何电路中存在共模电流和差模电流,差模信号携带数 ...
- Python 性能优化——对象绑定
不同函数调用方式对应不同的绑定次数: import profile class A: def f(self): pass def foo(): a = A() for i in range(10000 ...
- 可变长度的Fibonacci数列
原题目: Write a recursive program that extends the range of the Fibonacci sequence. The Fibonacci sequ ...
- Freemarker日期格式化处理
基本参数: date: 只显示日期,不显示时间.如${createTime?date} 或${createTime?date('yyyy-MM-dd')} time: 只显示时间,不显示日期如${cr ...
- websocket---Html5
使用websocket主要是处理,通过服务器向页面发送消息,进行页面操作的处理. 以前类似情况,由于程序立即相应,处理事件较短,所遇采用过ajax进行轮询, 但是由于本次,需要人工干预,所以采用web ...
- 00.PHP学习建议
各位师弟师妹,大家好~PHP不是我们专业的本该有的方向.我不知道大家为什么来学习这门语言,也许是自己了解之后喜欢这门语言(我想这种可能在我们专业是挺少的),也许是听守中哥说这门语言简单好学,为了躲避学 ...
- java的值传递和引用传递
昨天博主在对于值传递和引用传递这里栽了一个大坑啊,导致一下午时间都浪费在这里,我们先说下值传递和引用传递java官方解释: 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对 ...
- eclipse左边导航package explorer自动定位
eclipse或myeclipse中右边编辑界面点击 左边导航自动定位 左导航Package Explorer的右上角有一个黄色双向箭头图标,鼠标移动到上面提示"Link with ...