Spring【AOP模块】就是这么简单
2019独角兽企业重金招聘Python工程师标准>>>
前言
到目前为止,已经简单学习了Spring的Core模块、....于是我们就开启了Spring的AOP模块了...在讲解AOP模块之前,首先我们来讲解一下cglib代理、以及怎么手动实现AOP编程
cglib代理
在讲解cglib之前,首先我们来回顾一下静态代理和动态代理....我之前就写过了静态代理、动态代理的博文:blog.csdn.net/hon_3y/arti…
由于静态代理需要实现目标对象的相同接口,那么可能会导致代理类会非常非常多....不好维护---->因此出现了动态代理
动态代理也有个约束:目标对象一定是要有接口的,没有接口就不能实现动态代理.....----->因此出现了cglib代理
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!
- CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
编写cglib代理
接下来我们就讲讲怎么写cglib代理:
- 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。
- 引入功能包后,就可以在内存中动态构建子类
- 代理的类不能为final,否则报错【在内存中构建子类来做扩展,当然不能为final,有final就不能继承了】
- 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
//需要实现MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{// 维护目标对象private Object target;public ProxyFactory(Object target){this.target = target;}// 给目标对象创建代理对象public Object getProxyInstance(){//1. 工具类Enhancer en = new Enhancer();//2. 设置父类en.setSuperclass(target.getClass());//3. 设置回调函数en.setCallback(this);//4. 创建子类(代理对象)return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {System.out.println("开始事务.....");// 执行目标对象的方法Object returnValue = method.invoke(target, args);System.out.println("提交事务.....");return returnValue;}}
- 测试:
public class App {public static void main(String[] args) {UserDao userDao = new UserDao();UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();factory.save();}
}
使用cglib就是为了弥补动态代理的不足【动态代理的目标对象一定要实现接口】
手动实现AOP编程
AOP 面向切面的编程:
- AOP可以实现“业务代码”与“关注点代码”分离
下面我们来看一段代码:
// 保存一个用户
public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession(); // 【关注点代码】trans = session.beginTransaction(); // 【关注点代码】session.save(user); // 核心业务代码trans.commit(); //…【关注点代码】} catch (Exception e) { e.printStackTrace(); if(trans != null){ trans.rollback(); //..【关注点代码】} } finally{ HibernateSessionFactoryUtils.closeSession(session); ..【关注点代码】} }
- 关注点代码,就是指重复执行的代码。
- 业务代码与关注点代码分离,好处?
- 关注点代码写一次即可;
- 开发者只需要关注核心业务;
- 运行时期,执行核心业务代码时候动态植入关注点代码; 【代理】
案例分析:
- IUser接口
public interface IUser {void save();
}
我们一步一步来分析,首先我们的UserDao有一个save()方法,每次都要开启事务和关闭事务
//@Component -->任何地方都能用这个
@Repository //-->这个在Dao层中使用public class UserDao {public void save() {System.out.println("开始事务");System.out.println("DB:保存用户");System.out.println("关闭事务");}}
- 在刚学习java基础的时候,我们知道:如果某些功能经常需要用到就封装成方法:
//@Component -->任何地方都能用这个
@Repository //-->这个在Dao层中使用public class UserDao {public void save() {begin();System.out.println("DB:保存用户");close();}public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
- 现在呢,我们可能有多个Dao,都需要有开启事务和关闭事务的功能,现在只有UserDao中有这两个方法,重用性还是不够高。因此我们抽取出一个类出来
public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
- 在UserDao维护这个变量,要用的时候,调用方法就行了。
@Repository //-->这个在Dao层中使用
public class UserDao {AOP aop;public void save() {aop.begin();System.out.println("DB:保存用户");aop.close();}}
- 现在的开启事务、关闭事务还是需要我在userDao中手动调用。还是不够优雅。。我想要的效果:当我在调用userDao的save()方法时,动态地开启事务、关闭事务。因此,我们就用到了代理。当然了,真正执行方法的都是userDao、要干事的是AOP,因此在代理中需要维护他们的引用。
public class ProxyFactory {//维护目标对象private static Object target;//维护关键点代码的类private static AOP aop;public static Object getProxyInstance(Object target_, AOP aop_) {//目标对象和关键点代码的类都是通过外界传递进来target = target_;aop = aop_;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {aop.begin();Object returnValue = method.invoke(target, args);aop.close();return returnValue;}});}
}
工厂静态方法:
- 把AOP加入IOC容器中
//把该对象加入到容器中
@Component
public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
- 把UserDao放入容器中
@Component
public class UserDao {public void save() {System.out.println("DB:保存用户");}}
- 在配置文件中开启注解扫描,使用工厂静态方法创建代理对象
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance"><constructor-arg index="0" ref="userDao"/><constructor-arg index="1" ref="AOP"/></bean><context:component-scan base-package="aa"/></beans>
测试,得到UserDao对象,调用方法
public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");IUser iUser = (IUser) ac.getBean("proxy");iUser.save();}
}
工厂非静态方法
上面使用的是工厂静态方法来创建代理类对象。我们也使用一下非静态的工厂方法创建对象。
package aa;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** Created by ozc on 2017/5/11.*/public class ProxyFactory {public Object getProxyInstance(final Object target_, final AOP aop_) {//目标对象和关键点代码的类都是通过外界传递进来return Proxy.newProxyInstance(target_.getClass().getClassLoader(),target_.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {aop_.begin();Object returnValue = method.invoke(target_, args);aop_.close();return returnValue;}});}
}
配置文件:先创建工厂,再创建代理类对象
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--创建工厂--><bean id="factory" class="aa.ProxyFactory"/><!--通过工厂创建代理--><bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance"><constructor-arg index="0" ref="userDao"/><constructor-arg index="1" ref="AOP"/></bean><context:component-scan base-package="aa"/></beans>
AOP的概述
Aop: aspect object programming 面向切面编程
- 功能: 让关注点代码与业务代码分离!
- 面向切面编程就是指: 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
关注点:
- 重复代码就叫做关注点。
// 保存一个用户
public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession(); // 【关注点代码】trans = session.beginTransaction(); // 【关注点代码】session.save(user); // 核心业务代码trans.commit(); //…【关注点代码】} catch (Exception e) { e.printStackTrace(); if(trans != null){ trans.rollback(); //..【关注点代码】} } finally{ HibernateSessionFactoryUtils.closeSession(session); ..【关注点代码】} }
切面:
- 关注点形成的类,就叫切面(类)!
public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
切入点:
- 执行目标对象方法,动态植入切面代码。
- 可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。
切入点表达式:
- 指定哪些类的哪些方法被拦截
使用Spring AOP开发步骤
1) 先引入aop相关jar文件 (aspectj aop优秀组件)
- spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】
- aopalliance.jar 【spring2.5源码/lib/aopalliance】
- aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
- aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。
- 需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。
2) bean.xml中引入aop名称空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
引入jar包
引入4个jar包:
引入名称空间
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>
注解方式实现AOP编程
我们之前手动的实现AOP编程是需要自己来编写代理工厂的**,现在有了Spring,就不需要我们自己写代理工厂了。Spring内部会帮我们创建代理工厂**。
- 也就是说,不用我们自己写代理对象了。
因此,我们只要关心切面类、切入点、编写切入表达式指定拦截什么方法就可以了!
还是以上一个例子为案例,使用Spring的注解方式来实现AOP编程
在配置文件中开启AOP注解方式
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="aa"/><!-- 开启aop注解方式 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>
代码:
- 切面类
@Component
@Aspect//指定为切面类
public class AOP {//里面的值为切入点表达式@Before("execution(* aa.*.*(..))")public void begin() {System.out.println("开始事务");}@After("execution(* aa.*.*(..))")public void close() {System.out.println("关闭事务");}
}
- UserDao实现了IUser接口
@Component
public class UserDao implements IUser {@Overridepublic void save() {System.out.println("DB:保存用户");}}
- IUser接口
public interface IUser {void save();
}
- 测试代码:
public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");//这里得到的是代理对象....IUser iUser = (IUser) ac.getBean("userDao");System.out.println(iUser.getClass());iUser.save();}
}
目标对象没有接口
上面我们测试的是UserDao有IUser接口,内部使用的是动态代理...那么我们这次测试的是目标对象没有接口
- OrderDao没有实现接口
@Component
public class OrderDao {public void save() {System.out.println("我已经进货了!!!");}
}
- 测试代码:
public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");OrderDao orderDao = (OrderDao) ac.getBean("orderDao");System.out.println(orderDao.getClass());orderDao.save();}
}
优化和AOP注解API
API:
@Aspect 指定一个类为切面类
@Pointcut("execution( cn.itcast.e_aop_anno..(..))") 指定切入点表达式*
@Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行
测试:
// 前置通知 : 在执行目标方法之前执行@Before("pointCut_()")public void begin(){System.out.println("开始事务/异常");}// 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】@After("pointCut_()")public void after(){System.out.println("提交事务/关闭");}// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】@AfterReturning("pointCut_()")public void afterReturning() {System.out.println("afterReturning()");}// 异常通知: 当目标方法执行异常时候执行此关注点代码@AfterThrowing("pointCut_()")public void afterThrowing(){System.out.println("afterThrowing()");}// 环绕通知:环绕目标方式执行@Around("pointCut_()")public void around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("环绕前....");pjp.proceed(); // 执行目标方法System.out.println("环绕后....");}
优化
我们的代码是这样的:每次写Before、After等,都要重写一次切入点表达式,这样就不优雅了。
@Before("execution(* aa.*.*(..))")public void begin() {System.out.println("开始事务");}@After("execution(* aa.*.*(..))")public void close() {System.out.println("关闭事务");}
于是乎,我们要使用@Pointcut这个注解,来指定切入点表达式,在用到的地方中,直接引用就行了!
- 那么我们的代码就可以改造成这样了:
@Component
@Aspect//指定为切面类
public class AOP {// 指定切入点表达式,拦截哪个类的哪些方法@Pointcut("execution(* aa.*.*(..))")public void pt() {}@Before("pt()")public void begin() {System.out.println("开始事务");}@After("pt()")public void close() {System.out.println("关闭事务");}
}
XML方式实现AOP编程
首先,我们把所有的注解都去掉...
- 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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--对象实例--><bean id="userDao" class="aa.UserDao"/><bean id="orderDao" class="aa.OrderDao"/><!--切面类--><bean id="aop" class="aa.AOP"/><!--AOP配置--><aop:config ><!--定义切入表达式,拦截哪些方法--><aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/><!--指定切面类是哪个--><aop:aspect ref="aop"><!--指定来拦截的时候执行切面类的哪些方法--><aop:before method="begin" pointcut-ref="pointCut"/><aop:after method="close" pointcut-ref="pointCut"/></aop:aspect></aop:config></beans>
- 测试:
public class App {@Testpublic void test1() {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");OrderDao orderDao = (OrderDao) ac.getBean("orderDao");System.out.println(orderDao.getClass());orderDao.save();}@Testpublic void test2() {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");IUser userDao = (IUser) ac.getBean("userDao");System.out.println(userDao.getClass());userDao.save();}
}
测试OrderDao
测试UserDao
切入点表达式
切入点表达式主要就是来配置拦截哪些类的哪些方法
查官方文档
..我们去文档中找找它的语法...
在文档中搜索:execution(
语法解析
那么它的语法是这样子的:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
符号讲解:
- ?号代表0或1,可以不写
- “*”号代表任意类型,0或多
- 方法参数为..表示为可变参数
参数讲解:
- modifiers-pattern?【修饰的类型,可以不写】
- ret-type-pattern【方法返回值类型,必写】
- declaring-type-pattern?【方法声明的类型,可以不写】
- name-pattern(param-pattern)【要匹配的名称,括号里面是方法的参数】
- throws-pattern?【方法抛出的异常类型,可以不写】
官方也有给出一些例子给我们理解:
测试代码
<!-- 【拦截所有public方法】 --><!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>--><!-- 【拦截所有save开头的方法 】 --><!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>--><!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 --><!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>--><!-- 【拦截指定类的所有方法】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>--><!-- 【拦截指定包,以及其自包下所有类的所有方法】 --><!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>--><!-- 【多个表达式】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 下面2个且关系的,没有意义 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 【取非值】 --><!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y
转载于:https://my.oschina.net/u/3777556/blog/1634746
Spring【AOP模块】就是这么简单相关推荐
- 武林秘籍之Spring AOP 切面编程的简单应用
年轻人,我观你骨骼精奇,定是万里无一的练武奇才,老夫这里有一本失传已久的武林秘籍,现赠于你,望你勤加苦练,早日修成正果... AOP(面向切面编程):Aspect Oriented Programmi ...
- spring aop实例讲解_Spring核心技术详解(一)
一.Sring简介 Spring是一个分层的Java SE/EE应用一站式的轻量级开源框架.Spring核心是IOC和AOP. Spring主要优点包括: 方便解耦,简化开发,通过Spring提供的I ...
- 追根溯源之最好的Spring AOP解析
作者:极乐君 链接:https://zhuanlan.zhihu.com/p/25522841 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 本篇是年后第一篇博文,由于博主用了 ...
- 关于 Spring AOP (AspectJ) 你该知晓的一切
[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/54629058 出自[zejian ...
- 【Spring】Spring AOP源码分析-导读(一)
文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...
- 《Spring揭秘》读书笔记 2:Spring AOP
7 一起来看AOP 2009年8月,<一起来看流星雨>开播. 2009年9月,<Spring揭秘>出版. 7.1 AOP核心概念 AOP AOP全称为Aspect-Orient ...
- 深入聊一聊 Spring AOP 实现机制
点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 本文转载于公众号:吉姆餐厅ak 概述 AOP(Aspect-Oriented Programmin ...
- Spring AOP注解方式实现
简介 上文已经提到了Spring AOP的概念以及简单的静态代理.动态代理简单示例,链接地址:https://www.cnblogs.com/chenzhaoren/p/9959596.html 本文 ...
- spring aop 注释_使用Spring AOP,自定义注释和反射为您的应用程序审核基础结构
spring aop 注释 下一篇文章将演示如何使用Spring AOP和注释编写简单的审计. 审核机制将是干净,高效且易于维护的(和Kewwl!). 我将在用户管理系统上演示我的示例(假设您具有反射 ...
- 使用Spring AOP,自定义注释和反射为您的应用审核基础架构
下一篇文章将演示如何使用Spring AOP和注释编写简单的审计. 审核机制将是干净,高效且易于维护的(和Kewwl!). 我将在用户管理系统上演示我的示例(假设您对反射和AOP具有一般知识). 我们 ...
最新文章
- python 字符串list转为数字list
- 介绍一个好用的日期倒计时工具
- 数据库水平切分的实现原理解析——分库,分表,主从,集群,负载均衡器(转)...
- php电商网站开发流程图,php网上购物平台设计+ER图+流程图.doc
- 【英语学习】【科学】【Glencoe Science】【A】Life's Structure and Function目录及术语表
- Ettercap-中间人欺骗
- VB 字符串续行符最多25行…………
- Ubuntu 10.10用LibreOffice替换OpenOffice
- JavaWeb——jsp原理
- 云计算服务与基本术语、概念
- java虚拟机学习笔记(五)---运行时的数据区域
- 【代码规范】Google C++开源风格指南
- linux vi只写入1个字节,关于linux命令的说明(这是一个命令集)
- golang中文字符编码转换
- LeetCode(数据库)- 2142. The Number of Passengers in Each Bus I
- 基于 Windows系统的 KingbaseES 数据库软件安装指南(3. 安装前准备工作)
- 面试时被问“你的缺点是什么?”,这么答就对了
- Ubuntu-20.04永久修改DNS服务器
- D3DXCreateTextureFromFileEx创建纹理的悲催
- 2021-02-17