Spring学习总结(2)- AOP
一,什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在学习AOP时,先要了解什么是代理模式,可以参考: 代理模式
二,使用Spring实现AOP
横切关注点:跨越应用程序多个模块的方法或功能。(软件系统,可以看做由一组关注点即业务或功能或方法组成。其中,直接的业务关注点是直切关注点,而为直切关注点服务的,就是横切关注点。)即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。
切面(ASPECT) |
横切关注点被模块化的特殊对象。即,它是一个类。 |
通知(Advice) |
切面必须要完成的工作。即,它是类中的一个方法。 |
目标(Target) |
被通知对象。 |
代理(Proxy) |
向目标对象应用通知之后创建的对象。 |
切入点(PointCut) |
切面通知执行的"地点"的定义。 |
连接点(JointPoint) |
与切入点匹配的执行点。 |
下面示意图:
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
定义通知类,如下:
前置通知BeforeAdvice类:
// 前置通知
public class BeforeAdvice implements MethodBeforeAdvice{
/**
*
* @方法名: before
* @描述: 前置通知调用方法
* @param arg0 方法信息
* @param arg1 参数列表
* @param arg2 被代理的目标对象
* @throws Throwable
* @创建人:Zender
*/
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("-----------------前置通知-----------------");
}
}
后置通知AfterAdvice类:
//后置通知
public class AfterAdvice implements AfterReturningAdvice {
/**
*
* @方法名: afterReturning
* @描述:后置通知调用的方法
* @param arg0 返回值
* @param arg1 被调用的方法
* @param arg2 方法参数列表
* @param arg3 被代理对象
* @throws Throwable
* @创建人:Zender
*/
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("-----------------后置通知-----------------");
}
}
环绕通知SurroundAdvice类:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//环绕通知
public class SurroundAdvice implements MethodInterceptor {
/**
*
* @方法名: invoke
* @描述:环绕通知调用的方法
* @param arg0 方法信息对象
* @return
* @throws Throwable
* @创建人:Zender
*/
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
//前置横切逻辑
System.out.println("方法:" + arg0.getMethod() + " 被调用在对象:" + arg0.getThis() + "上,参数:" + arg0.getArguments());
//方法调用
Object ret = arg0.proceed();
//后置横切逻辑
System.out.println("返回值:"+ ret);
return ret;
}
}
抽象主题角色(subject):
/**
*
* @类名称:Subject
* @类描述:抽象主题角色(subject)
* @创建人:zender
*/
public interface Subject {
public String sailBook();
}
具体主题角色(RealSubject):
/**
*
* @类名称:RealSubject
* @类描述:具体主题角色(RealSubject)
* @创建人:zender
*/
public class RealSubject implements Subject {
@Override
public String sailBook() {
System.out.println("买书:Spring!");
return "Spring";
}
}
DynamicProxy类:
import org.springframework.aop.framework.ProxyFactory;
import com.zender.aop.advice.AfterAdvice;
import com.zender.aop.advice.BeforeAdvice;
import com.zender.aop.advice.SurroundAdvice;
//获得代理对象
public class DynamicProxy {
/**
*
* @方法名: getProxy
* @描述: 获得任意的代理对象
* @param object
* @return
* @创建人 Zender
*/
public static Object getProxy(Object object){
//实例化Spring代理工厂
ProxyFactory factory=new ProxyFactory();
//设置被代理的对象
factory.setTarget(object);
//添加通知,横切逻辑
factory.addAdvice(new BeforeAdvice());
factory.addAdvice(new AfterAdvice());
factory.addAdvice(new SurroundAdvice());
return factory.getProxy();
}
}
测试类:
import com.zender.aop.DynamicProxy;
import com.zender.aop.RealSubject;
import com.zender.aop.Subject;
public class SpringAOPTest {
public static void main(String[] args) {
//从代理工厂中获得代理对象
Subject rs = (Subject) DynamicProxy.getProxy(new RealSubject());
rs.sailBook();
}
}
结果:
二,使用IOC配置的方式实现AOP
创建IOC的配置文件beans.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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 被代理的目标对象 -->
<bean id="realSubject" class="com.zender.aop.RealSubject"></bean>
<!-- 后置通知 -->
<bean id="afterAdvice" class="com.zender.aop.advice.AfterAdvice"></bean>
<!-- 前置通知 -->
<bean id="beforeAdvice" class="com.zender.aop.advice.BeforeAdvice"></bean>
<!--
代理对象
interceptorNames 通知数组
target 被代理的目标对象
proxyTargetClass 被代理对象是否为一个类,如果是则使用cglib,否则使用jdk动态代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyTargetClass" value="true" />
<property name="target" ref="realSubject" />
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
<value>beforeAdvice</value>
</list>
</property>
</bean>
</beans>
测试类:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zender.aop.Subject;
public class SpringAOPTest {
public static void main(String[] args) {
//容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Subject s = (Subject) ctx.getBean("proxy");
s.sailBook();
}
}
运行结果:
三,使用XML配置Spring AOP切面
这个时候需要引用一个新的jar包:aspectjweaver.jar,该包是AspectJ的组成部分。pom.xml内容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Zender</groupId>
<artifactId>SpringAOP</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
定义通知通知类:
//通知类
public class SubjectAdvices {
//前置通知
public void before(JoinPoint jp)
{
System.out.println("--------------------前置通知--------------------");
System.out.println("方法名:" + jp.getSignature().getName() + ",参数:" + jp.getArgs().length + ",被代理对象:" + jp.getTarget().getClass().getName());
}
//后置通知
public void after(JoinPoint jp){
System.out.println("--------------------后置通知--------------------");
}
//环绕通知
public Object around(ProceedingJoinPoint pjd) throws Throwable{
System.out.println("--------------------环绕通知开始--------------------");
Object object = pjd.proceed();
System.out.println("--------------------环绕通知结束--------------------");
return object;
} //异常后通知
public void afterThrowing(JoinPoint jp,Exception exp)
{
System.out.println("--------------------异常后通知,发生了异常:" + exp.getMessage() + "--------------------");
} //返回结果后通知
public void afterReturning(JoinPoint joinPoint, Object result)
{
System.out.println("结果是:" + result);
}
}
配置IOC容器依赖的XML文件IOCBeans.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:p="http://www.springframework.org/schema/p"
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/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!--被代理的目标对象 -->
<bean id="realSubject" class="com.zender.aop.RealSubject"></bean>
<!-- 通知类 -->
<bean id="advice" class="com.zender.aop.advice.SubjectAdvices"></bean>
<!-- AOP配置 -->
<!-- proxy-target-class属性表示被代理的类是否为一个没有实现接口的类,Spring会依据实现了接口则使用JDK内置的动态代理,如果未实现接口则使用cblib -->
<aop:config proxy-target-class="true">
<!-- 切面配置 -->
<!--ref表示通知对象的引用 -->
<aop:aspect ref="advice">
<!-- 配置切入点(横切逻辑将注入的精确位置) -->
<aop:pointcut expression="execution(* com.zender.aop.RealSubject.s*(..))" id="pointcut1"/>
<!--声明通知,method指定通知类型(即通知类的对应的方法名),pointcut指定切点,就是该通知应该注入那些方法中 -->
<aop:before method="before" pointcut-ref="pointcut1"/>
<aop:after method="after" pointcut-ref="pointcut1"/>
<aop:around method="around" pointcut="execution(* com.zender.aop.RealSubject.s*(..))"/>
<!-- throwing里面的内容与SubjectAdvices类的afterThrowing的参数名相同 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut1" throwing="exp" />
<!-- returning里面的内容与SubjectAdvices类的afterReturning的参数名相同 -->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut1" returning="result" />
</aop:aspect>
</aop:config>
</beans>
aop:after-throwing需要指定通知中参数的名称throwing="exp",则方法中定义应该是这样:afterThrowing(JoinPoint jp,Exception exp);aop:after-returning同样需要设置returning指定方法参数的名称。
测试代码:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zender.aop.Subject;
public class SpringAOPTest {
public static void main(String[] args) {
//容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("IOCBeans.xml");
Subject s = (Subject) ctx.getBean("realSubject");
s.sailBook();
}
}
运行结果如下:
四,使用注解配置Spring AOP切面
1,基于上一个示例,修改被代理的类RealSubject,为了实现IOC扫描在RealSubject类上注解了@Service并命名bean为realSubject。相当于上一个示例中在xml配置文件中增加了一个bean:
<!--被代理的目标对象 --> <bean id="realSubject" class="com.zender.aop.RealSubject"></bean>
修改后的RealSubject类的代码如下:
/**
*
* @类名称:RealSubject
* @类描述:具体主题角色(RealSubject)
* @创建人:zender
*/
@Service("realSubject")
public class RealSubject implements Subject {
@Override
public String sailBook() {
System.out.println("买书:Spring!");
return "Spring";
}
}
2,修改通知类SubjectAdvices,如下:
//通知类
@Component
@Aspect
public class SubjectAdvices {
//前置通知
@Before("execution(* com.zender.aop.RealSubject.s*(..))")
public void before(JoinPoint jp)
{
System.out.println("--------------------前置通知--------------------");
System.out.println("方法名:" + jp.getSignature().getName() + ",参数:" + jp.getArgs().length + ",被代理对象:" + jp.getTarget().getClass().getName());
}
//后置通知
@After("execution(* com.zender.aop.RealSubject.s*(..))")
public void after(JoinPoint jp){
System.out.println("--------------------后置通知--------------------");
}
//环绕通知
@Around("execution(* com.zender.aop.RealSubject.s*(..))")
public Object around(ProceedingJoinPoint pjd) throws Throwable{
System.out.println("--------------------环绕通知开始--------------------");
Object object = pjd.proceed();
System.out.println("--------------------环绕通知结束--------------------");
return object;
} //异常后通知
@AfterThrowing(pointcut="execution(* com.zender.aop.RealSubject.s*(..))",throwing="exp")
public void afterThrowing(JoinPoint jp,Exception exp)
{
System.out.println("--------------------异常后通知,发生了异常:" + exp.getMessage() + "--------------------");
} //返回结果后通知
@AfterReturning(pointcut="execution(* com.zender.aop.RealSubject.s*(..))",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result)
{
System.out.println("结果是:" + result);
}
}
@Component表示该类的实例会被Spring IOC容器管理;@Aspect表示声明一个切面;@Before表示before为前置通知,通过参数execution声明一个切点
3,新增配置文件IOCBeans2.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="com.zender.aop" />
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
在配置IOC的基础上增加了aop:aspectj-autoproxy节点,Spring框架会自动为与AspectJ切面配置的Bean创建代理,属性proxy-target-class="true"表示被代理的目标对象是一个类,而非实现了接口的类。
4,测试代码如下:
public class SpringAOPTest {
public static void main(String[] args) {
//容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("IOCBeans2.xml");
RealSubject s = ctx.getBean("realSubject" , RealSubject.class);
s.sailBook();
}
}
5,运行结果:
Spring学习总结(2)- AOP的更多相关文章
- Spring学习笔记之aop动态代理(3)
Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...
- Spring学习笔记4——AOP
AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...
- Spring 学习十五 AOP
http://www.hongyanliren.com/2014m12/22797.html 1: 通知(advice): 就是你想要的功能,也就是安全.事物.日子等.先定义好,在想用的地方用一下.包 ...
- spring学习 十三 注解AOP
spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解,也就是要开启注解扫描,注解的包是spring-context.jar,所以在配置文件中还要引入context约束,也 ...
- [Spring学习笔记 4 ] AOP 概念原理以及java动态代理
一.Spring IoC容器补充(1) Spring IoC容器,DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入Field(注解) ...
- spring 学习之二 AOP编程
AOP概念 AOP, aspect oriented programing,翻译过来就是面向切面编程的意思,那什么叫面向切面编程呢?相对于之前传统的纵向继承方式来对原有功能进行功能扩展, 面向切面编程 ...
- Spring学习笔记2—AOP
1.AOP概念 AOP(Aspect Oriented Programming):面向切面编程,AOP能够将那些与业务无关,却为业务模块所共同调用的应用(例如事务处理.日志管理.权限控制等)封装起来, ...
- Spring学习总结(7)-AOP
参考资料:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop 1 ...
- spring学习06(AOP)
9.AOP 什么是AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...
- Spring学习笔记之AOP配置篇(一)
[TOC] 1. 创建并声明一个切面 首先,创建一个类,添加@Component注解使其添加到IoC容器 然后,添加@Aspect注解,使其成为一个切面 最后,在配置文件里面,使用<aop:as ...
随机推荐
- inline-block的间隙问题 box-orient属性 line-clamp属性 margin问题
只要设了 display:inline-block 将元素变成行级块元素的时候,会自带空隙,即使你设了 margin 和 padding 依然没有效果! 解决办法:只要在父元素上加上font-size ...
- QT_study
https://blog.csdn.net/a313827758/article/details/72736552 https://blog.csdn.net/xbcreal/article/deta ...
- thread_process_action
import math import random import re import sys import threading from time import ctime, sleep from l ...
- NuGet-Doc:承载自己的 NuGet 源
ylbtech-NuGet-Doc:承载自己的 NuGet 源 1.返回顶部 1. 可能希望将包仅发布到有限受众(例如,组织或工作组),而不是将其公开发布. 此外,一些公司可能希望限制其开发人员可以使 ...
- Wildfly安装以及集成idea(mac)
文章目录 Linux发布运行 下载 集成idea Linux发布运行 首先说一下在linux环境,只需要将war包上传到wildfly-8.2.0.Final/standalone/deploymen ...
- python3 -tensorflow-dataset 之map函数 lambda隐函数的应用
###隐函数lanbda及map函数的应用实例 ''' lambda 参数1,参数2,参数3,...:表达式 ''' f = lambda x,y :x+y print(f(1,2)) ###输出3 ...
- Excelvba从文件中逐行读取并写入excel中
Sub 読み込む() Dim result As Long Dim dialog As FileDialog Set dialog = Application.FileDialog(msoFileDi ...
- #python# 代理过程中遇到的error
做一下总结 urllib.error.HTTPError: HTTP Error 503: Too many open connections TimeoutError: [WinError 1006 ...
- 开源企业IM-免费企业即时通讯-ENTBOOST V2014.183 Linux版本号正式公布
版权声明:本文为博主原创文章,欢迎转载,转载请尽量保持原文章完整,谢谢! https://blog.csdn.net/yanghz/article/details/37807975 ENTBOOST, ...
- Linux ssh的的用法
Linux ssh的的用法 ssh执行远程命令 1. 执行命令 1.执行单条命令 ubuntu@node1:~$ ssh ubuntu@172.16.10.102 hostname ubuntu@17 ...