一,什么是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的更多相关文章

  1. Spring学习笔记之aop动态代理(3)

    Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...

  2. Spring学习笔记4——AOP

    AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...

  3. Spring 学习十五 AOP

    http://www.hongyanliren.com/2014m12/22797.html 1: 通知(advice): 就是你想要的功能,也就是安全.事物.日子等.先定义好,在想用的地方用一下.包 ...

  4. spring学习 十三 注解AOP

    spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解,也就是要开启注解扫描,注解的包是spring-context.jar,所以在配置文件中还要引入context约束,也 ...

  5. [Spring学习笔记 4 ] AOP 概念原理以及java动态代理

    一.Spring IoC容器补充(1) Spring IoC容器,DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入Field(注解) ...

  6. spring 学习之二 AOP编程

    AOP概念 AOP, aspect oriented programing,翻译过来就是面向切面编程的意思,那什么叫面向切面编程呢?相对于之前传统的纵向继承方式来对原有功能进行功能扩展, 面向切面编程 ...

  7. Spring学习笔记2—AOP

    1.AOP概念 AOP(Aspect Oriented Programming):面向切面编程,AOP能够将那些与业务无关,却为业务模块所共同调用的应用(例如事务处理.日志管理.权限控制等)封装起来, ...

  8. Spring学习总结(7)-AOP

    参考资料:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop 1 ...

  9. spring学习06(AOP)

    9.AOP 什么是AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...

  10. Spring学习笔记之AOP配置篇(一)

    [TOC] 1. 创建并声明一个切面 首先,创建一个类,添加@Component注解使其添加到IoC容器 然后,添加@Aspect注解,使其成为一个切面 最后,在配置文件里面,使用<aop:as ...

随机推荐

  1. Oracle Data Guard Protection Modes

    Maximum Availability This protection mode provides the highest level of data protection that is poss ...

  2. loj6259「CodePlus 2017 12 月赛」白金元首与独舞

    分析 我们将没连的点连向周围四个点 其余的按照给定的方向连 我们将所有连出去的位置统一连到0点上 再以0作为树根 于是就将问题转化为了有向图内向树计数 代码 #include<iostream& ...

  3. -bash: ./hello.jar: 无法执行二进制文件

    在linux中直接调用java包产生的 解决:依赖多个包要用冒号分隔,而不是分号 正确:> java -cp ./lib/*:./hello.jar hello 错误:> java -cp ...

  4. SEC7 - MySQL 查询语句--------------进阶3:排序查询

    # 进阶3:排序查询 /* 引入: select * from employees; 语法: select 查询列表 from 表 [where 筛选条件] order by 排序的列表 asc/de ...

  5. C#后台去除字符串最后一个字符

    string str = "a,b,c,"; int length = str.length; //获取字符串长度 str = str.substring(0,length-1); ...

  6. jQuery遍历集合

     jQuery 遍历List集合 $(function(){ var tbody = ""; var obj =[{"name ":"xxxx&quo ...

  7. [Linux] 006 命令格式与目录处理命令

    1. 命令格式 命令 [-选项] [参数] 如,ls -la /etc 说明 个别命令使用不遵循此格式 当有多个选项时,可以写在一起 简化选项与完整选项 如,-a 为简化选项,--all 为完整选项 ...

  8. LR为什么用极大似然估计,损失函数为什么是log损失函数(交叉熵)

    首先,逻辑回归是一个概率模型,不管x取什么值,最后模型的输出也是固定在(0,1)之间,这样就可以代表x取某个值时y是1的概率 这里边的参数就是θ,我们估计参数的时候常用的就是极大似然估计,为什么呢?可 ...

  9. HttpUrlConnection工具类

    package com.ligotop.core.utils; import com.ligotop.core.exceptions.BusinessException; import java.io ...

  10. spark连接hive找不到table

    Caused by: org.apache.spark.sql.catalyst.analysis.NoSuchTableException: Table or view 'xxxx' not fou ...