代理模式及Spring AOP (二)
一.Spring AOP
1.1 Spring AOP
底层还是用的动态代理。如果目标对象所对应的类有接口,spring就用jdk生成代理对象;
如果目标对象所对应的类没有接口,spring就用CGLIB生成代理对象。
优点:动态代理,如果一个类被它代理,则接口或者类的所有方法都被强迫执行。而spring AOP可以按业务划分,有些方法需要事务则扩展功能,有些方法不需要则不进行扩展。
1.2 相关概念名词
① 切面aspect:切面是一个类(功能类),类里有若干方法,每个方法代表一个功能。把这些功能方法切到指定的位置,切到到业务方法的前后等
对应:spring中<aop:aspect id="myAspect" ref="tm"></aop:aspect>
② 连接点joinpoint:用代理对象调用方法,这句话的位置就是连接点,用连接点启动横切。
③ 通知advice:切面类中的那些方法就是通知。
前置通知<aop:before 在执行目标方法前面
后置通知<aop:after-returning 在执行完目标方法后执行
异常通知<aop:after-throwing 在目标方法抛异常执行,不会执行后置通知
最终通知<aop:after 前置,后置或异常通知,都执行完毕后会执行最终通知
环绕通知<aop:around 特点:可以控制目标方法的执行
④ 切入点pointcut:把切面中的通知,切到哪个类中的哪些方法上,切入点是指切到哪些方法上,spring给提供一些规则表达式,用于指定切在哪些方法上。
对应:spring中<aop:pointcut id="myPointCut" expression="execution(* com.hdu.service..*.*(..))"/>
⑤ 目标对象targetObject:实际的业务类的对象
⑥ aop代理aopproxy:由spring框架帮程序员用jdk或cglib生成代理对象,这个生成的过程被spring封装了。spring的独有配置,用于指定生成代理对象。
对应: spring中<aop:config></aop:config>
⑦ 织入weaving:也叫横切,把额外的功能用配置文件和注解的方式织入到业务中;取消配置文件和注解,就取消了织入,不影响原有业务流程
在spring中加入<aop:config节点,<aop:pointcut expression="",从spring容器取出expression属性指定的对象都是代理对象。
在spring中没有加入<aop:config节点,从spring容器中取出的所有对象是真实对象。
1.3 参考手册
1.3.1 声明一个切面

1.3.2 声明一个切点
其中<aop:pointcut/>标签写在<aop:aspect></aop:aspect>标签内,为局部切点,只起作用在对应切面。

其中<aop:pointcut/>标签写在<aop:aspect></aop:aspect>标签外,为全局切点,所有切面对其有效。

关于切点的expression 表达式:

参考手册中的示例:
① the execution of any public method:所有的public方法
execution(public * *(..))
② the execution of any method with a name beginning with "set": 所有以set开头的方法
execution(* set*(..))
③ the execution of any method defined by the AccountService interface: com.xyz.service.AccountService类或接口中任意方法,任意参数,任意返回值
execution(* com.xyz.service.AccountService.*(..))
④ the execution of any method defined in the service package: com.xyz.service包下任意类,任意方法,任意参数,任意返回值
execution(* com.xyz.service.*.*(..))
⑤ the execution of any method defined in the service package or a sub-package: com.xyz.service包下以及子包下任意类.任意的方法,任意参数,任意返回值
execution(* com.xyz.service..*.*(..))
关于切点的within表达式:
① any join point (method execution only in Spring AOP) within the service package: com.xyz.service的任意类
within(com.xyz.service.*)
② any join point (method execution only in Spring AOP) within the service package or a sub-package: com.xyz.service及其子包下的任意类
within(com.xyz.service..*)
③ any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface: com.xyz.service的AccountService类
this(com.xyz.service.AccountService)
1.3.3 声明通知
通常 前置通知、后置通知、异常通知、最终通知 与 环绕通知分开使用,否则执行顺序不符合预期逻辑。
若环绕通知叠加使用,按配置顺序先A后B,则执行时,先执行A在目标对象调用方法之前的内容,再执行B在目标对象调用方法之前的内容,调用目标对象的原有方法,执行A在目标对象调用方法之后的内容,执行B在目标对象调用方法之后的内容。
1.3.3.1 前置通知

1.3.3.2 后置通知

1.3.3.3 异常通知

1.3.3.4最终通知

1.3.3.5环绕通知

二.Spring AOP使用示例:使用xml配置文件的方式
2.1 新增jar包,支持对AOP的处理

2.2 接口及业务实现类
package com.hdu.dao;
public interface IUserDao {
public int addrUser();
public int updateUser();
}
Interface IUserDao
package com.hdu.dao.impl;
import com.hdu.dao.IUserDao;
public class UserDaoImpl implements IUserDao {
@Override
public int addrUser() {
System.out.println("UserDaoImpl.addUser()");
return 1;
}
@Override
public int updateUser() {
System.out.println("UserDaoImpl.updateUser()");
return 1;
}
}
Class UserDaoImpl
Class UserDaoImpl
package com.hdu.service;
public interface IUserService {
public boolean addUser();
public boolean updateUser();
}
Interface IUserService
Interface IUserService
package com.hdu.service.impl;
import com.hdu.dao.IUserDao;
import com.hdu.dao.impl.UserDaoImpl;
import com.hdu.service.IUserService;
public class UserServiceImpl implements IUserService {
//应该用spring进行解耦,本次重点在于代理,故简化操作
private IUserDao userDao = new UserDaoImpl();
@Override
public boolean addUser() {
System.out.println("UserServiceImpl.addUser()");
boolean flag = false;
int rowAffect = userDao.addrUser();
if(rowAffect==1) {
flag = true;
}
return flag;
}
@Override
public boolean updateUser() {
System.out.println("UserServiceImpl.updateUser()");
boolean flag = false;
int rowAffect = userDao.updateUser();
if(rowAffect==1) {
flag = true;
}
return flag;
}
}
Class UserServiceImpl
Class UserServiceImpl
2.3 切面类
其中JoinPoint 可以获取目标对象,目标方法及目标方法的参数,环绕通知则使用ProceedingJoinPoint 获取。
package com.hdu.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面
* @author Administrator
*
*/
public class TransactionManager {
//前置通知方法
public void begin(JoinPoint jp) {
System.out.println("begin transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
}
//后置通知方法
public void commit(JoinPoint jp,Object flag) {
System.out.println("commit transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
System.out.println("目标方法的返回值-->"+flag);
}
//异常通知方法
public void rollback(JoinPoint jp,Exception ex) {
System.out.println("rollback transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
System.out.println("目标方法抛异常-->"+ex.getMessage());
}
//最终通知
public void finallyClose(JoinPoint jp) {
System.out.println("finallyClose");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
}
//环绕通知 能控制目标方法的执行
public Object around(ProceedingJoinPoint pjp)throws Throwable {
Object retVal=null;
try {
System.out.println("around begin transaction");
System.out.println("目标对象-->"+pjp.getTarget());
System.out.println("目标方法-->"+pjp.getSignature().getName());
System.out.println("目标方法参数-->"+pjp.getArgs()[0]);
//if() {
//就是直接指定目标方法
retVal = pjp.proceed();
//}
System.out.println("around commit transaction");
}catch(Exception e) {
e.printStackTrace();
System.out.println("around rollback transaction");
}finally {
System.out.println("around finallyClose");
}
return retVal;
}
}
2.4 配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 实例化业务模型对象 -->
<bean id="userDao" class="com.hdu.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.hdu.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 实例化切面的对象 -->
<bean id="tm" class="com.hdu.aspect.TransactionManager"></bean>
<!-- spring 独有的配置,创建代理对象和确定如何横切切面 -->
<!-- <aop:config>这个标签就告知spring要做代理对象了
原则:如果实际对象有接口就jdk动态代理生成代理对象;如果实际对象没有接口就用cglib动态代理生成代理对象
可以人为改动proxy-target-class="true",强制用cglib
-->
<aop:config proxy-target-class="false">
<!-- <aop:aspect 这个节点标签,告知spring使用哪个切面
可以写多个<aop:aspect节点,可以使用多个横切
-->
<aop:aspect id="myAspect" ref="tm">
<!-- 告知spring 把切面 切在哪些类的哪些方法上,切点可以写多个-->
<aop:pointcut id="myPointCut"
expression="execution(* com.hdu.service..*.*(..))"/>
<!-- 前置通知 -->
<aop:before
pointcut-ref="myPointCut"
method="begin"/>
<!-- 后置通知
属性returning="flag" flag对应的通知方法中的参数
-->
<aop:after-returning
pointcut-ref="myPointCut"
method="commit"
returning="flag"/>
<!-- 异常通知
属性throwing="ex" ex对应的异常通知的方法的参数
-->
<aop:after-throwing
pointcut-ref="myPointCut"
method="rollback"
throwing="ex"/>
<!-- 最终通知 -->
<aop:after
pointcut-ref="myPointCut"
method="finallyClose"/>
<!-- 环绕通知 -->
<!-- <aop:around -->
<!-- pointcut-ref="myPointCut" -->
<!-- method="around"/> -->
</aop:aspect>
</aop:config>
</beans>
2.5 测试
package com.hdu.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hdu.dao.UserDao;
import com.hdu.entity.User;
import com.hdu.service.UserService;
public class TestSpringAOP {
@Test
public void testMethod1() {
//初始化spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("resources/spring_aop.xml");
UserService userService=context.getBean("userService",UserService.class);
System.out.println(userService.getClass());
userService.addUser(new User());
//userDao不会被代理
UserDao userDao=context.getBean("userDao",UserDao.class);
System.out.print("userDao不会被代理"+userDao.getClass());
}
}
Test
结果:
class com.sun.proxy.$Proxy6 begin transaction 目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9 目标方法-->addUser 目标方法参数-->com.hdu.entity.User@4b5d6a01 UserServiceImpl.addUser() UserDaoImpl.addUser() commit transaction 目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9 目标方法-->addUser 目标方法参数-->com.hdu.entity.User@4b5d6a01 目标方法的返回值-->true finallyClose 目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9 目标方法-->addUser 目标方法参数-->com.hdu.entity.User@4b5d6a01 userDao不会被代理class com.hdu.dao.impl.UserDaoImpl
Conclusion
2.6调用过程
我们获取对象,要么是对象地址,要么是代理:System.out.println(userService.getClass());
从打印结果
class com.sun.proxy.$Proxy6 可知,目前使用的是jdk动态代理。
其中UserService userService = (UserService) context.getBean("userService");执行时spring已经把UserService变成代理对象。
在执行userService.addUser();业务方法时就会被invoke方法拦截。就被切点表达式expression="execution(* com.hdu.service..*.*(..))"拦截。
切点表达式expression="execution(* com.hdu.service..*.*(..))"匹配上了实现类,就会执行切面中通知。
<aop:aspect id="myAspect" ref="tm">
<aop:before pointcut-ref="myPointCut" method="begin"/>
</aop:aspect>
就找到TransactionManager类,找到before方法,并调用。
也就是说只要程序满足切点表达式,就会调用切面的方法。最终我们就看到它不是真实的对象,而是一个代理对象。由代理去执行就会响应执行通知的方法。
三.Spring AOP使用示例:使用注解的方式
3.1 切面类
参考手册:

切点可以是局部的也可以是全局的。
package com.hdu.aspect;
import org.aspectj.lang.JoinPoint;
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;
import org.springframework.stereotype.Component;
/**
* 切面
* @author Administrator
*
*/
@Component("tm")
@Aspect
public class TransactionManager {
//the pointcut expression
@Pointcut("execution(* com.hdu.service..*.*(..))")
private void myPointCut() {}// the pointcut signature
//前置通知方法
//@Before("myPointCut()")
public void begin(JoinPoint jp) {
System.out.println("begin transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
}
//后置通知方法
//@AfterReturning(pointcut="myPointCut()",returning="flag")
public void commit(JoinPoint jp,Object flag) {
System.out.println("commit transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
System.out.println("目标方法的返回值-->"+flag);
}
//异常通知方法
//@AfterThrowing(pointcut="myPointCut()",throwing="ex")
public void rollback(JoinPoint jp,Exception ex) {
System.out.println("rollback transaction");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
System.out.println("目标方法抛异常-->"+ex.getMessage());
}
//最终通知
//@After("myPointCut()")
public void finallyClose(JoinPoint jp) {
System.out.println("finallyClose");
System.out.println("目标对象-->"+jp.getTarget());
System.out.println("目标方法-->"+jp.getSignature().getName());
System.out.println("目标方法参数-->"+jp.getArgs()[0]);
}
//环绕通知 能控制目标方法的执行
@Around("myPointCut()")
public Object around(ProceedingJoinPoint pjp)throws Throwable {
Object retVal=null;
try {
System.out.println("around begin transaction");
System.out.println("目标对象-->"+pjp.getTarget());
System.out.println("目标方法-->"+pjp.getSignature().getName());
System.out.println("目标方法参数-->"+pjp.getArgs()[0]);
//if() {
//就是直接指定目标方法
retVal = pjp.proceed();
//}
System.out.println("around commit transaction");
}catch(Exception e) {
e.printStackTrace();
System.out.println("around rollback transaction");
}finally {
System.out.println("around finallyClose");
}
return retVal;
}
}
3.2 配置清单
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--
@Controller @Service @Repository @Component @Resource @Autowired @Qualifier
-->
<!-- 实例化业务模型对象 -->
<context:component-scan base-package="com.hdu.dao.impl"></context:component-scan>
<context:component-scan base-package="com.hdu.service.impl"></context:component-scan>
<!-- 实例化切面的对象 -->
<context:component-scan base-package="com.hdu.aspect"></context:component-scan>
<!-- spring 独有的配置,创建代理对象和确定如何横切切面
@Aspect @Pointcut @Before @AfterReturning @AfterThrowing @After @Around
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
3.3 测试
package com.hdu.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hdu.entity.User;
import com.hdu.service.UserService;
public class TestSpringAOP {
@Test
public void testMethod1() {
//初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("resources/spring_aop.xml");
UserService userService=context.getBean("userService",UserService.class);
System.out.println(userService.getClass());
userService.addUser(new User());//连接点 启动横切
}
}
Test
结果
class com.sun.proxy.$Proxy13 around begin transaction 目标对象-->com.hdu.service.impl.UserServiceImpl@217ed35e 目标方法-->addUser 目标方法参数-->com.hdu.entity.User@f5958c9 UserServiceImpl.addUser() UserDaoImpl.addUser() around commit transaction around finallyClose
Conclusion
代理模式及Spring AOP (二)的更多相关文章
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- 代理模式及Spring AOP (一)
一.代理模式 在不更改源码的前提下,加入新功能,通常需要用到代理设计模式. 代理设计模式分类: 静态代理 动态代理 jdk动态代理 cglib动态代理 其中spring AOP的底层用的是动态代理.其 ...
- 从代理模式到Spring AOP
什么是代理模式 假如我喜欢上隔壁班的翠花,但是我没胆量向她送花,这时候我需要一个铁杆哥们帮我做这件事, 很明显这哥们是个代理,是去执行任务的,但是花实际上是我"送"的,代理和我一样 ...
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- Spring AOP(二)--注解方式
本文介绍通过注解@AspectJ实现Spring AOP,这里要重点说明一下这种方式实现时所需的包,因为Aspect是第三方提供的,不包含在spring中,所以不能只导入spring-aop的包,为了 ...
- java代理课程测试 spring AOP代理简单测试
jjava加强课程测试代码 反射. 代理 .泛型.beanUtils等 项目源码下载:http://download.csdn.net/detail/liangrui1988/6568169 热身运动 ...
- Java动态代理学习【Spring AOP基础之一】
Spring AOP使用的其中一个底层技术就是Java的动态代理技术.Java的动态代理技术主要围绕两个类进行的 java.lang.reflect.InvocationHandler java.la ...
- 基于代理类实现Spring AOP
目录 ProxyFactoryBean类介绍 基于JDK动态代理的Spring AOP实现 基于CGLIB代理的Spring AOP实现 Spring的通知类型 ProxyFactoryBean类 ...
- Spring AOP (二)
下面介绍@AspectJ语法基础 一.切点表达式函数 AspectJ的切点表达式由关键字和操作参数组成,如execution(* greetTo(..)) 的切点表达式,execution为关键字,而 ...
随机推荐
- Topless eclipse导入myeclipse的web项目没法识别问题解决
1.进入项目目录,找到.project文件,打开. 2.找到<natures>...</natures>代码段. 3.在第2步的代码段中加入如下标签内容并保存: <nat ...
- Lua中Table的学习
--table 是 Lua 的一种数据结构,用来帮助我们创建不同的数据类型,如:数组.字典等 --Lua也是通过table来解决模块(module).包(package)和对象(Object)的. 例 ...
- English trip -- MC(情景课)6 Time
xu言: 学习就和打仗一样,在开始前一定先要有准备(预习).这样在真正开始打的时候你会发现,本以为很难的仗,你却越战越勇,逐渐进入状态. Vocabulary focus gym [dʒɪm] ...
- quartz---properties
quartz---properties quartz.properties: 如果没有properties默认读取quartz-2.3.0.jar下的org.quartz下的quartz.proper ...
- Tomcat类加载器破坏双亲委派
转载:https://blog.csdn.net/qq_38182963/article/details/78660779 http://www.cnblogs.com/aspirant/p/8991 ...
- vue2.0对不同数据类型的显示
- forget word out a~2
1● an 不,非,无 2● amphi 两个,两种 3● ad 做,加强:
- learning uboot source command
reference: http://www.denx.de/wiki/DULG/UBootCmdGroupExec => help source source - run script from ...
- java代码获取客户端的真实ip
java代码获取客户端的真实ip protected String getIpAddr(HttpServletRequest request) { String ip = request.getHea ...
- 数据集 过滤时 RecordCount 属性
如果是在 OnFilterRecord里写代码,过滤后RecordCount 是不变的. 如果是用 Filter属性过滤,过滤后RecordCount 是变的=过滤后的记录数. 难怪 有的说 变的,有 ...