Spring03-AOP
一. AOP介绍
1. Aop介绍
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
2. AOP核心概念
连接点(Joinpoint):
特定点是程序执行的某一个特定位置,如类开始初始化前,类初始化后,类某一个方法调用前/调用后,方法抛出异常后,一个类或一段程序代码拥有一些具有边界性质的特定点,这写代码中的特定点就称为"连接点",Spring仅支持方法连接点,即仅能在方法调用前,方法调用后,方法抛出异常时,以及方法调用前后这些程序执行点织入增强.
切点(Pointcut)
每个程序类都拥有对个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物,但在众多连接点中,如何定位某些感兴趣的连接点呢?AOP通过"切点"定位特定的连接点.
增强(Advice)
增强是织入目标类连接点上的一段程序代码,在Spring中,增强不仅可以描述程序代码,还拥有另一个和连接点相关的信息,这便是执行点的方位,结合执行点的方位信息和切点信息,就可以找到特定的连接,正因为增强既包含了用于添加到目标连接点上的一段执行逻辑,又包含用于定位连接点的方位信息,所以Spring所提供的增强接口都是带方位名的.如BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等.BeforeAdvice表示方法调用前的位置.而AfterReturningAdivce表示访问返回后的位置,所以只有结合切点和增强,才能确定特定的连接点并实施增强逻辑.
目标对象(Target)
增强逻辑的织入目标类.如果没有AOP,那么目标业务类需要自己实现所有逻辑,如果使用AOP可以把一些非逻辑性代码通过AOP织入到主程序代码上.
引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法.这样,即使一个业务类原本没有实现某一个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑.让业务类成为这个接口的实现类.
织入(Weaving)
织入是将增强添加到目标类具体链接点上的过程,AOP就像一台织布机,将目标类,增强,或者引介天衣无缝的编织到一起,我们不能不说"织入"这个词太精辟了,根据不同的实现技术,AOP有3种织入方式:
编译期织入,这要求使用特殊的Java编译器.
类装载期织入,这要求使用特殊的类装载器.
动态代理织入,在运行期为目标类添加增强生成子类的方式.
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载器织入.
代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类.它是融合了原类和增强逻辑的代理类,根据不同的代理方式,代理类既可能是和原类具有相同接口的类(JDK动态代理),也可能就是原类的子类(CGLIB动态代理),所以可以采用与调用原类相同的方法调用代理类.
切面(Aspect)
切面由切点和增强(介入)组成,它既包括横切逻辑的定义,也包括链接点的定义,也包括链接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切所定义的横切逻辑织入切面所指定的链接点中.
AOP的工作重心在于如何将增强应用于目标对象的连接点中上,这里包含两项工作:
第一,如何通过切点和增强定位到链接点上;
第二,如何在增强中编写切面代码;
3. Spring AOP 基础知识
Spring的 AOP底层用到两种代理机制:
- JDK 的动态代理:针对实现了接口的类产生代理。
- CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象
3.1 JDK动态代理实现
创建接口和对应实现类
public interface UserService {
void login();
void loginOut();
}//实现类
public class UserServiceImpl implements UserService {
public void login() {
System.out.println("login方法触发");
}
public void loginOut() {
System.out.println("loginOut方法触发");
}
}2.创建动态代理类
public class PerformHandler implements InvocationHandler {
private Object target; //目标对象
public PerformHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//本方法中的其他输出输入增强
System.out.println("方法触发了");
//执行被代理类 原方法
Object invoke = method.invoke(target, args);
System.out.println("执行完毕了");
return invoke;
}
}测试
@Test
public void test1(){
//测试JDK动态代理技术
UserService userService = new UserServiceImpl();
PerformHandler performHandler = new PerformHandler(userService);
userService = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
performHandler
);
userService.login(); }测试结果: 在调用接口方法的前后都会添加代理类的方法!
3.2 CGlib实现代理
使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法 newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚
第二个入参 interfaces就是需要代理实例实现的接口列表.
对于没有通过接口定义业务方法的类,如何动态创建代理实例呢? JDK动态代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这一空缺.
GCLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑.
1.创建创建CGLib代理器
public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
//设置被代理对象
public Object getProxy(Class clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method,
Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("CGLig代理之前之前");
Object invoke = methodProxy.invokeSuper(obj,objects);
System.out.println("CGLig代理之前之后");
return invoke;
}
}
测试
@Test
public void test2(){
//TODO CGlib实现
CglibProxy cglibProxy = new CglibProxy();
UserServiceImpl userService= (UserServiceImpl) cglibProxy.getProxy(UserServiceImpl.class);
userService.login();
}
二. Spring中AOP开发
1. Spring 中 AOP 基于xml开发
1.1 项目准备
导入jar包:
pom文件添加
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>1.5.7.RELEASE</version>
</dependency>
引入日志:log4j.properties
创建配置文件:
applicationContext-aop.xml
创建测试代码:
AopTest.java
1.2 准备操作对象
先创建UserService接口:
public interface UserService {
void save();
void delete();
void update();
void select();
}
实现类
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("保存用户");
}
public void delete() {
System.out.println("删除用户");
}
public void update() {
System.out.println("更新用户");
}
public void select() {
System.out.println("查找用户");
}
}
1.3 增强类
public class MyAdivce {
/**
//前置通知:目标方法运行之前调用
//后置通知(如果出现异常不会调用):在目标方法运行之后调用
//环绕通知:在目标方法之前和之后都调用
//异常拦截通知:如果出现异常,就会调用
//后置通知(无论是否出现 异常都会调用):在目标方法运行之后调用
*/
//前置通知
public void before(){
System.out.println("这是前置通知");
}
//后置通知
public void afterReturning(){
System.out.println("这是后置通知(方法不出现异常)");
}
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("这是环绕通知之前部分!!");
Object object = point.proceed(); //调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return object;
}
public void afterException(){
System.out.println("异常通知!");
}
public void after(){
System.out.println("这也是后置通知,就算方法发生异常也会调用!");
} }
1.4 将增强织入目标对象(xml)
在applicationContext-aop.xml 中配置
注意:添加了 aop命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<bean name="userService" class="spring.service.UserServiceImpl" />
<!--配置通知对象-->
<bean name="myAdvice" class="spring.aop.MyAdivce" />
<!-- 配置将增强织入目标对象-->
<aop:config>
<!--
spring.service.UserServiceImpl
1 2 3 4
1: 修饰符 public/private/* 可忽略
2: 返回值 String/../*
3: 全限定类名 类名 ..代表不限层数 *ServiceImpl
4: (..)
-->
<aop:pointcut id="pc" expression="execution(*
spring.service.*ServiceImpl.*
(..))"/>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="pc" />
<aop:after-returning method="afterReturning" pointcut-ref="pc" />
<aop:around method="around" pointcut-ref="pc" />
<aop:after-throwing method="afterException" pointcut-ref="pc" />
<aop:after method="after" pointcut-ref="pc" />
</aop:aspect>
</aop:config>
</beans>
测试:
@Test
public void test1(){
//TODO 测试切面引入
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-aop.xml");
UserService userServiceImpl = context.getBean("userService", UserService.class);
userServiceImpl.delete();
}
2. Spring中的AOP基于注解开发
在使用@Aspect之前,首先必须保证所使用的Java是 5.0 以上版本,否则无法使用注解技术.
Spring 在处理@Aspect注解表达式时,需要将Spring的asm模块添加到类路径中,asm是轻量级的字节码处理框架,因为Java的反射机制无法获取入参名,Spring利用asm处理@Aspect中所描述的方法入参名.
此外,Spring采用AspectJ提供的@Aspect注解类库及相应的解析类库,需要在pom.xml文件中添加aspectj.weaver和aspectj.tools类包的依赖.
2.1 创建配置文件
class-path路径下创建applicationContext-aop-annotation.xml
配置目标对象,配置通知对象,开启注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<bean name="userService" class="spring.service.UserServiceImpl" />
<!--配置通知对象-->
<bean name="myAdvice" class="spring.aop.MyAdivce" />
<!-- 配置将增强织入目标对象 使用注解的方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
2.2 修改增强类MyAdvice
在类上添加
@Aspect
注解可以将
execution
直接定义在通知方法名上,如下://后置通知
@AfterReturning("execution(* com..service.*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("这是后置通知(如果出现异常不会调用)!!");
}3.也可以定义一个方法, 如下面的
pc()
方法,然后在通知方法上指定@Before("MyAdvice.pc()")
@Pointcut("execution(* com..service.*ServiceImpl.*(..))")
public void pc(){}
//前置通知
//指定该方法是前置通知,并制定切入点
@Before("MyAdvice.pc()")
public void before(){
System.out.println("这是前置通知!!");
}完整定义增强类
@Aspect
public class MyAdivce {
/**
//前置通知:目标方法运行之前调用
//后置通知(如果出现异常不会调用):在目标方法运行之后调用
//环绕通知:在目标方法之前和之后都调用
//异常拦截通知:如果出现异常,就会调用
//后置通知(无论是否出现 异常都会调用):在目标方法运行之后调用
*/
@Pointcut("execution(* spring.service.*ServiceImpl.*(..))")
public void pc(){
}
//前置通知
@Before("MyAdivce.pc()")
public void before(){
System.out.println("这是前置通知");
}
//后置通知
@AfterReturning("execution(* com..*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("这是后置通知(方法不出现异常)");
} public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("这是环绕通知之前部分!!");
Object object = point.proceed(); //调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return object;
}
public void afterException(){
System.out.println("异常通知!");
}
public void after(){
System.out.println("这也是后置通知,就算方法发生异常也会调用!");
} }
Spring03-AOP的更多相关文章
- JAVAEE——spring03:spring整合JDBC和aop事务
一.spring整合JDBC 1.spring提供了很多模板整合Dao技术 2.spring中提供了一个可以操作数据库的对象.对象封装了jdbc技术. JDBCTemplate => JDBC模 ...
- Spring03——有关于 Spring AOP 的总结
本文将为各位带来 Spring 的另一个重点知识点 -- Spring AOP.关注我的公众号「Java面典」,每天 10:24 和你一起了解更多 Java 相关知识点. 什么是 AOP 面向切面编程 ...
- 基于spring注解AOP的异常处理
一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- 学习AOP之透过Spring的Ioc理解Advisor
花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- 学习AOP之认识一下Spring AOP
心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
- .Net中的AOP系列之构建一个汽车租赁应用
返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...
- .NET里简易实现AOP
.NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...
- 在.Net中实现自己的简易AOP
RealProxy基本代理类 RealProxy类提供代理的基本功能.这个类中有一个GetTransparentProxy方法,此方法返回当前代理实例的透明代理.这是我们AOP实现的主要依赖. 新建一 ...
随机推荐
- 牛客---java练习
一. 1. abstract可以修饰方法和类,不能修饰属性.抽象方法没有方法体,即没有大括号{}.抽象类中的成员属性都是public static final类型的:成员方法都是public abst ...
- python中换行,'\r','\n'及'、'\r\n'
'\r'的本意是回到行首,'\n'的本意是换行. 所以回车相当于做的是'\r\n'或者'\n\r'.'\r'就是换行并回行首, '\n'就是换行并回行首,用'\r\n'表示换行并回行首. window ...
- Django 中的Form、ModelForm
一.ModelForm 源码 class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass): pass def modelform_fact ...
- Jenkins redeploy artifacts
jenkins redeploy artifacts 按钮 - 开源中国https://www.oschina.net/question/3045293_2247829 Jenkins 构建失败后通过 ...
- TortoiseGit push免输密码
(ฅ>ω<*ฅ) 噫又好了~ TortoiseGit push免输密码的方法 – 晨旭的博客~https://www.chenxublog.com/2016/03/04/tortoiseg ...
- linux之常见错误
在日常开发中,尤其是在Linux中进行操作的时候,经常会碰到各种各样的错误.记录一下,熟能生巧,慢慢参透linux的奥秘 1) 在安装ssl证书的时候,发生certbot命令无法使用的情况 解决方案: ...
- vue单页面模板说明文档(2)
Linter Configuration This boilerplate uses ESLint as the linter, and uses the Standard preset with s ...
- composer更改源为国际
composer config -g repo.packagist composer https://repo.packagist.org
- js怎么能取得多选下拉框选中的多个值?
方法:获取多选下拉框对象数组→循环判断option选项的selected属性(true为选中,false为未选中)→使用value属性取出选中项的值.实例演示如下: 1.HTML结构 1 2 3 4 ...
- 校园电商项目4——SSM各项配置
步骤一:数据库连接文件 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/school_o2o?useUni ...