采用Spring实现AOP功能
1.AOP中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
代理方式:
静态代理
动态代理
2.使用JDK的proxy技术实现AOP功能
JDK 动态代理 (采用的是JDK 反射技术)
public class JDKProxy implements InvocationHandler {private Object targetObject;//代理的目标对象public Object createProxyInstance(Object targetObject){this.targetObject = targetObject; /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法 */return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {return method.invoke(this.targetObject, args);//把方法调用委派给目标对象} }
当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
3.使用CGLIB实现AOP功能
public class CGLIBProxy implements MethodInterceptor {private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject){this.targetObject = targetObject;Enhancer enhancer = new Enhancer();//该类用于生成代理对象enhancer.setSuperclass(this.targetObject.getClass());//设置父类enhancer.setCallback(this);//设置回调用对象为本身return enhancer.create();}public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {return methodProxy.invoke(this.targetObject, args);} }CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
注:使用CGLIB之前要导入相应的jar包,在下载的spring文件包的cglib目录下
测试:
新建一个接口:
package cn.itcast.service;public interface PersonService {public void save(String name);public void update(String name, Integer personid);public String getPersonName(Integer personid); }
新建一个接口的 实现类:
代码的目的是,在下面这个业务逻辑类中的方法执行之前都要被容器拦截,拦截了之后进行权限的判断,如果有权限才可以执行类中的方法
本例中的有权限是指 user!=null
package cn.itcast.service.impl;import cn.itcast.service.PersonService;public class PersonServiceBean implements PersonService{private String user = null;public String getUser() {return user;}public PersonServiceBean(){}public PersonServiceBean(String user){this.user = user;}public String getPersonName(Integer personid) {System.out.println("我是getPersonName()方法");return "xxx";}public void save(String name) {System.out.println("我是save()方法");}public void update(String name, Integer personid) {System.out.println("我是update()方法");}}
使用JDK完成动态代理:
package cn.itcast.aop;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;import cn.itcast.service.impl.PersonServiceBean;public class JDKProxyFactory implements InvocationHandler{private Object targetObject;public Object createProxyIntance(Object targetObject){this.targetObject = targetObject;return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//环绕通知PersonServiceBean bean = (PersonServiceBean) this.targetObject;Object result = null; if(bean.getUser()!=null){//..... advice()-->前置通知try {result = method.invoke(targetObject, args);// afteradvice() -->后置通知} catch (RuntimeException e) {//exceptionadvice()--> 例外通知}finally{//finallyadvice(); -->最终通知}}return result;}}
使用CGLIB实现AOP功能:
package cn.itcast.aop;import java.lang.reflect.Method;import cn.itcast.service.impl.PersonServiceBean;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;public class CGlibProxyFactory implements MethodInterceptor{private Object targetObject;public Object createProxyIntance(Object targetObject){this.targetObject = targetObject;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.targetObject.getClass());//非finalenhancer.setCallback(this);return enhancer.create();}public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {PersonServiceBean bean = (PersonServiceBean) this.targetObject;Object result = null;if(bean.getUser()!=null){result = methodProxy.invoke(targetObject, args);}return result;} }
新建一个单元测试:
package junit.test;import org.junit.BeforeClass; import org.junit.Test;import cn.itcast.aop.CGlibProxyFactory; import cn.itcast.aop.JDKProxyFactory; import cn.itcast.service.PersonService; import cn.itcast.service.impl.PersonServiceBean;public class AOPTest {@BeforeClasspublic static void setUpBeforeClass() throws Exception {}@Test public void proxyTest(){JDKProxyFactory factory = new JDKProxyFactory();PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("xxx"));service.save("888");}@Test public void proxyTest2(){CGlibProxyFactory factory = new CGlibProxyFactory();PersonServiceBean service = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("xxx"));service.save("999");} }
两个测试结果都是:
我是save()方法
4.使用Spring框架实现AOP功能
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间: <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> </beans>
注意上面的 aop 命名空间 和 最后一行的两个schema文件路径
Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:
(1)基于注解方式声明切面。
(2)基于XML配置方式声明切面。
<1> 基于注解方式声明切面。
首先启动对@AspectJ注解的支持(下划线部分):
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><aop:aspectj-autoproxy/><bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/><bean id="log" class="cn.itcast.service.LogPrint"/> </beans>
启用@AspectJ支持后,在application context中定义的任意带有一个@Aspect切面(拥有@Aspect
注解)的bean都将被
Spring自动识别并用于配置Spring AOP。
注解方式的示例:
@Aspect public class LogPrint {@Pointcut("execution(* cn.itcast.service..*.*(..))")private void anyMethod() {}//声明一个切入点 @Before("anyMethod() && args(userName)")//定义前置通知public void doAccessCheck(String userName) {} @AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知public void doReturnCheck(String revalue) {}@AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知public void doExceptionAction(Exception ex) {}@After("anyMethod()")//定义最终通知public void doReleaseAction() {}@Around("anyMethod()")//环绕通知public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {return pjp.proceed();} }
<2>基于XML配置方式声明切面。
各种不同类型的通知
public class LogPrint {public void doAccessCheck() {}定义前置通知public void doReturnCheck() {}定义后置通知public void doExceptionAction() {}定义例外通知public void doReleaseAction() {}定义最终通知public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {return pjp.proceed();环绕通知} }
XML配置:
<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/> <bean id="log" class="cn.itcast.service.LogPrint"/> <aop:config><aop:aspect id="myaop" ref="log"><aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/><aop:before pointcut-ref="mycut" method="doAccessCheck"/><aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/><aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/><aop:after pointcut-ref="mycut" method=“doReleaseAction"/><aop:around pointcut-ref="mycut" method="doBasicProfiling"/></aop:aspect> </aop:config>
测试:
重新建立一个接口:
package com.yinger.service;public interface PersonService2 {public void save();public void exTest() throws Exception;public String test(String name);}
新建一个类,实现上面的接口:
package com.yinger.service.impl;import com.yinger.service.PersonService2;public class PersonServiceBean4 implements PersonService2{//默认的构造器public PersonServiceBean4(){System.out.println("instance me");}//exTest 方法public void exTest() throws Exception {throw new Exception("发生了异常");}//test 测试方法public String test(String name){System.out.println("test");return name;}//save 方法public void save(){System.out.println("save");}}
新建一个拦截器:
package com.yinger.service.intercepter;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class PersonServiceIntercepter {@Pointcut("execution(* com.yinger.service.impl.PersonServiceBean4.*(..))")private void anyMethod(){}@Before("anyMethod()")public void doAccessCheck(){System.out.println("前置通知");}@AfterReturning(pointcut="anyMethod()",returning="reValue")public void doReturnCheck(String reValue){System.out.println("后置通知:"+reValue);}@AfterThrowing(pointcut="anyMethod()",throwing="ex")public void doExceptionAction(Exception ex) {System.out.println("例外通知:"+ex);}@After("anyMethod()")public void doReleaseAction(){System.out.println("最终通知");}@Around("anyMethod()")public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕通知");return pjp.proceed();}}
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: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-2.5.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><aop:aspectj-autoproxy/><!-- 注意记得要将自定义的拦截器交给Spring容器管理 --><bean id="myIntercepter" class="com.yinger.service.intercepter.PersonServiceIntercepter"></bean><bean id="personService4" class="com.yinger.service.impl.PersonServiceBean4"></bean></beans>
添加测试方法:
@Test //用于测试AOP功能的方法public void testAOP() throws Exception {AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");System.out.println("--------");PersonService2 ps2 = (PersonService2)ctx.getBean("personService4");ps2.test("name"); // ps2.exTest(); // ps2.save();ctx.close();}
首先测试 test 方法,结果:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. instance me -------- 前置通知 环绕通知 test 后置通知:name 最终通知
其次测试 save 方法:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. instance me -------- 前置通知 环绕通知 save 后置通知:null 最终通知
最后测试 exTest 方法:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. instance me -------- 前置通知 环绕通知 例外通知:java.lang.Exception: 发生了异常 最终通知
从上面的例子中可以看出,使用通知可以得到方法返回的值,当发生了异常时可以知道异常的类型和信息
并且通过给通知方法限制方法参数的类型,可以限制为指定的方法参数的方法
如果是异常的话,就只有抛出的异常和例外通知中的异常一样时,异常通知方法才会执行
参考文档中的内容:
基于xml配置的例子略过。。。(和上面的示例代码差不多)
5.aspectj的切入点的语法细节分析
expression="execution(* cn.itcast.service..*.*(..))"
第一个*表示方法的返回值可以是任何值
接着 cn.itcast.service..* 表示 cn.itcast.service 包以及它的子包(两个点,如果是一个点就不包括子包)中的任何类 ,第二个*表示任何类
接着 .* 表示类中的任何方法,第三个*表示任何方法
最后的 (..)表示方法的参数可以是任意的,可以没有,也可以有一个或者多个
采用Spring实现AOP功能相关推荐
- Spring AOP功能和目标
1.AOP的作用 在OOP中,正是这种分散在各处且与对象核心功能无关的代码(横切代码)的存在,使得模块复用难度增加.AOP则将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可 ...
- Spring之AOP由浅入深
1.AOP的作用 在OOP中,正是这种分散在各处且与对象核心功能无关的代码(横切代码)的存在,使得模块复用难度增加.AOP则将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可 ...
- 通过代码入门Spring②何为AOP
TestApp.java package cn.podger.spring.demo2;import org.junit.BeforeClass; import org.junit.Test; imp ...
- Spring AOP 功能使用详解
前言 AOP 既熟悉又陌生,了解过 Spring 人的都知道 AOP 的概念,即面向切面编程,可以用来管理一些和主业务无关的周边业务,如日志记录,事务管理等:陌生是因为在工作中基本没有使用过,AOP ...
- Spring源码分析之ProxyFactoryBean方式实现Aop功能的分析
实现Aop功能有两种方式, 1. ProxyFactoryBean方式: 这种方式是通过配置实现 2. ProxyFactory方式:这种方式是通过编程实现 这里只说ProxyFactoryBean方 ...
- 【SSM框架系列】Spring 的 AOP(面向切面编程)
什么是 AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP 是 OOP ...
- Spring的AOP原理
AOP是什么? 软件工程有一个基本原则叫做"关注点分离"(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互 ...
- Spring教程--AOP简介
AOP的简介 1 什么是AOP AOP Aspect Oriented Programing 面向切面编程 AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视.事务管理.安全检查.缓存 ...
- spring的aop的动态代理机制都有哪些_Spring学习(4):Spring AOP
Spring AOP说明 AOP(Aspect Oriented Pragraming)面向切面编程,AOP采用横向抽取机制,取代了传统纵向继承体系的重复性代码(性能监视.事务管理.安全检查.缓存). ...
最新文章
- TP5_模型初始化_踩坑记录
- Android NDK环境搭建
- linux 系统网络服务器组建,配置和管理实训教程 pdf,Linux网络服务器配置管理项目实训教程2...
- php7.2与php5.6共存,同域名下php5.6与7.2同时运行
- Oracle与mongodb的区别
- 使用Kotlin开发Android应用 - 环境搭建 (1)
- 倒计时 分秒 小程序 方法_小程序天/小时/分秒倒计时封装
- [转]VS 2005快捷键
- 2014年CPU排名
- chrome浏览器历史版本
- mysql2008完全卸载教程_完美卸载SQL Server 2008的方法
- 毕业5年决定你的一生
- Android调试出现问题:failed to connect to /10.0.2.2 (port 8080) from /192.168.31.150 (port 37592) after 300
- android相关素材以及网站
- 选择BIMC托管电子商务的理由
- 天堂2java报错_那位高手帮我解决一下天堂2单机的服务器问题
- 远程控制计算机显示为什么不能满屏,win7系统连接远程桌面却不能全屏显示的解决方法...
- 一文读懂支付通道背后的江湖!——下
- ZYNQ学习之旅--PS_AXI_VDMA(利用VDMA实现将PS端的数据显示在PL端的HDMI上)
- 多边形网格到B-Rep实体转换:算法详细信息和C ++代码示例
热门文章
- python 排列组合速度_Python实现的简单排列组合算法示例
- android settings 源码,菜鸟学Android源码-Settings(2)
- mysql5.7安装完成后密码是多少_安装了mysql5.7后,如何进行配置(密码等)初始化...
- .net core精彩实例分享 -- 网络编程
- 错误的日志可能会导致疯狂;好日志可能会成为魔杖
- Visual Studio 2019 v16.4 Preview 2 发布
- 华为宣布方舟编译器将于8月31日
- W3C 宣布:WebAuthn 成为正式 Web 标准
- 关于.NET的单元测试
- 编辑器eslint格式_VScode格式化代码,开启ESlint代码检测方法,eslint代码配置