第三章 AOP 通过Java API创建切面

简介: <p lang="zh-CN" align="JUSTIFY" style="margin-bottom:0cm; widows:0; orphans:0"><span style="color:#00000a"><span style="font-family:DejaVu Sans"><span style="font-size:10pt">    在前面使用增强的时候,我们发现增强会

    在前面使用增强的时候,我们发现增强会被织入到目标类的所有的方法中。我们如果把增强织入到目标类的特定的方法中,需要使用切点进行目标连接点的定位。然后我们可以通过切点及增强生成一个切面了。


3.4.1切点类型

静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut

动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut

注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut

表达式切点:org.springframework.aop.support.ExpressionPointcut

流程切点:org.springframework.aop.support.ControlFlowPointcut


3.4.2静态方法来匹配切面

org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor


a、增强类

在这里使用的是3.3.2的前置增强:

http://blog.csdn.net/p_3er/article/details/9239925

只是UserDaoImpl中有两个方法,save方法和delete方法。然后我们给save方法配置切面。

b、切面类继承StaticMethodMatcherPointcutAdvisor

public class StaticAdvisor extends StaticMethodMatcherPointcutAdvisor {
	/*
	 * 切点方法匹配规则
	 */
	public boolean matches(Method method, Class<?> clazz) {
		return method.getName().equals("save");
	}

	/*
	 * 重写StaticMethodMatcherPointcut的getClassFilter()
	 * 匹配哪个类下的方法
	 */
	public ClassFilter getClassFilter() {
		ClassFilter classFilter = new ClassFilter() {
			public boolean matches(Class<?> clazz) {
				return UserDaoImpl.class.isAssignableFrom(clazz);
			}
		};
		return classFilter;
	}
}


c、配置

<!-- 增强Bean -->
	<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
	
	<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	<!-- 管理切面类。p:advice-ref把增强放入切面 -->
	<bean id="staticAdvisor" class="cn.framelife.spring.advisor.StaticAdvisor" p:advice-ref="userDaoBeforeAdvice"></bean>
	
	<!-- 
		设置父代理类 
		p:interceptorNames 放切面,而不再是增强
	-->
	<bean id="paramProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="staticAdvisor"
		p:proxyTargetClass="true" />	
	<!-- 设置子代理类 -->
	<bean id="userDaoProxy" parent="paramProxy" p:target-ref="userDao"></bean>

d、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserDao userDao = (UserDao) context.getBean("userDaoProxy");
		//UserDaoImpl中有两个方法,save方法中有切面,delete中没切面
		userDao.save();
		System.out.println("----------");
		userDao.delete();

e、结果

我是前置增强:save
保存用户...
----------
删除用户...



3.4.3静态正则表达式方法配置切面

     这种方法不需要自己写切面类。

       org.springframework.aop.support.RegexpMethodPointcutAdvisor是正则表达式方法匹配的切面实现类。


a、增强类

在这里使用的是3.3.2的前置增强:

http://blog.csdn.net/p_3er/article/details/9239925


b、配置

<!-- 增强Bean -->
	<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
	
	<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	<!-- 
		设置切面Bean 
		patterns 用正则表达式定义目标类全限定方法名的匹配模式串
			目标类全限定方法名,指的是带类名的方法名。如: cn.framelife.spring.dao.impl.UserDaoImpl.delete()
		<value>.*delete</value> 匹配模式串
	-->
	<bean id="regexpAdvisor" 
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
		p:advice-ref="userDaoBeforeAdvice">
		<property name="patterns">
			<list>
				<value>.*delete</value>
			</list>
		</property>
	</bean>
	
	<!-- 
		设置代理类 
	-->
	<bean id="regexpProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="regexpAdvisor"
		p:target-ref="userDao"
		p:proxyTargetClass="true" />	

c、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserDao userDao = (UserDao) context.getBean("regexpProxy");
		userDao.save();
		System.out.println("----------");
		userDao.delete();


d、结果

保存用户...
----------
我是前置增强:delete
删除用户...


常用的正则表达式规则

1.*set.*表示所有类中的以set为前缀的方法。如:com.abc.UserDao.setName()

2com\.abc\.service\..*表示com.abc.service.包下所有的类的所有的方法。

Com.abc.service.a.User.setName()

Com.abc.service.UserService.save()

3com\.abc\.service\..*Service\..*表示com.abc.service包下以Service结尾的类的所有的方法。如:com.abc.service.UserService.save()

4com\.abc\.service\..*Service\.save.+匹配所有以save为前缀的方法,而且save后必须拥有一个或多个字符。如:com.abc.service.UserService.saveUser()



3.4.4动态切面

org.springframework.aop.support.DynamicMethodMatcherPointcut类即有静态切点检查的方法,也有动态切点检查的方法。由于动态切点检查会对性能造成很大的影响,我们应当避免在运行时每次都对目标类的各个方法进行检查。

Spring检查动态切点的机制:在创建代理时对目标类的每个连接点进行静态切点检查,如果仅静态切点检查就可以知道连接点是不匹配的,在运行期就不进行动态检查了;如果静态切点检查是匹配的,在运行期才进行动态方法检查。

动态切面通过DefaultPointcutAdvisor切面类与DynamicMethodMatcherPointcut切点结合起来生成。主要是针对连接点方法的参数。

a、增强类

在这里使用的是3.3.2的前置增强:

http://blog.csdn.net/p_3er/article/details/9239925

只是UserDaoImpl中多了一个out方法,带一个参数。


b、切点类继承DynamicMethodMatcherPointcut

public class DynamicPointcut extends DynamicMethodMatcherPointcut {
	//用list集合保存参数名
	private List<String> specialNames = new ArrayList<String>();
	
	public DynamicPointcut(){
		specialNames.add("aa");
		specialNames.add("bb");
	}
	/*
	 * 对类进行静态切点检查
	 */
	public ClassFilter getClassFilter() {
		
		return new ClassFilter() {
			@Override
			public boolean matches(Class<?> targetClass) {
				System.out.println("使用getClassFilter静态检查:"+targetClass.getName());
				return UserDaoImpl.class.isAssignableFrom(targetClass);
			}
		};
	}

	/*
	 *对方法进行静态切点检查
	 */
	public boolean matches(Method method, Class<?> targetClass) {
		System.out.println("使用matches(method,targetClass)方法静态检查:"+targetClass.getName()+"--"+method.getName());
		return method.getName().equals("out");
	}


	/*
	 *对方法进行动态切点检查
	 */
	public boolean matches(Method method, Class<?> targetClass, Object[] args) {
		System.out.println("使用matches(method,targetClass)方法动态检查:"+targetClass.getName()+"--"+method.getName()+"的参数");
		String name = (String)args[0];
		return specialNames.contains(name);
	}
}

c、配置

<!-- 增强Bean -->
	<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>

	<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	
	<!-- 切面 -->
	<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<!-- 设置切点 -->
		<property name="pointcut">
			<bean class="cn.framelife.spring.pointcut.DynamicPointcut"></bean>
		</property>
		<!-- 织入增强 -->
		<property name="advice" ref="userDaoBeforeAdvice"></property>
	</bean>
	
	<!-- 
		设置代理 
	-->
	<bean id="dynamicProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="dynamicAdvisor"
		p:target-ref="userDao"
		p:proxyTargetClass="true" />	

d、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserDao userDao = (UserDao) context.getBean("dynamicProxy");
		//UserDaoImpl中多了一个out方法,带一个参数。
		System.out.println("----------");
		userDao.save();
		System.out.println("----------");
		userDao.out("aa");
		System.out.println("----------");
		userDao.out("1111");
		System.out.println("----------");


e、结果

使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--out
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--save
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--delete
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--clone
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--toString
----------
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--save
保存用户...
----------
使用getClassFilter静态检查:cn.framelife.spring.dao.impl.UserDaoImpl
使用matches(method,targetClass)方法静态检查:cn.framelife.spring.dao.impl.UserDaoImpl--out
使用matches(method,targetClass)方法动态检查:cn.framelife.spring.dao.impl.UserDaoImpl--out的参数
我是前置增强:out
out输出名字为:aa
----------
使用matches(method,targetClass)方法动态检查:cn.framelife.spring.dao.impl.UserDaoImpl--out的参数
out输出名字为:1111
----------


3.4.5流程切面

一个类中的某一方法使用目标类的两个方法,那么我们可以使用流程切面给这两个方法都积入增强。

如:UserServiceImpl(使用类)operate方法中使用UserDaoImpl(目标类)的两个方法。

流程切面使用ControlFlowPointcutDefaultPointcutAdvisor结合形成。

a、增强类

在这里使用的是3.3.2的前置增强:

http://blog.csdn.net/p_3er/article/details/9239925

只是UserDaoImpl中有两个方法,save方法和delete方法。然后我们给save方法配置切面。


bUserServiceImpl

public class UserServiceImpl implements UserService {
	private UserDao userDao;
	
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void addUser() {
		userDao.save();
	}

	@Override
	public void operate() {
		//这里同时使用了userDao的两个方法
		userDao.delete();
		userDao.save();
	}
}

c、配置

<!-- 增强Bean -->
	<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>

	<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	<!-- 切点 -->
	<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
		<!-- 指定流程切点的类 -->
		<constructor-arg type="java.lang.Class" value="cn.framelife.spring.service.impl.UserServiceImpl"></constructor-arg>
		<!-- 指定流程切点的方法 -->
		<constructor-arg type="java.lang.String" value="operate"></constructor-arg>
	</bean>
	
	<!-- 切面 -->
	<bean id="controlFlowAdvisor" 
		class="org.springframework.aop.support.DefaultPointcutAdvisor"
		p:pointcut-ref="controlFlowPointcut"
		p:advice-ref="userDaoBeforeAdvice"/>	
	
	<!-- 
		设置代理 
	-->
	<bean id="controlFlowProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="controlFlowAdvisor"
		p:target-ref="userDao"
		p:proxyTargetClass="true" />
	
	<!-- 把使用目标类的Bean交由Spring管理 -->
	<bean id="userService" class="cn.framelife.spring.service.impl.UserServiceImpl">
		<property name="userDao" ref="controlFlowProxy"></property>
	</bean>


d、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserService userService = (UserService) context.getBean("userService");
		userService.operate();
		System.out.println("----------------");
		userService.addUser();


e、结果

我是前置增强:delete
删除用户...
我是前置增强:save
保存用户...
----------------
保存用户...


3.4.6复合切面

在前面的例子中,我们所定义的切面都只有一个切点而已。有时候我们一个切面需要多个切点,也就是多个条件才能决定连接点。多个切点组成一个切点,这样的切点是复合切点。由复合切点加上增强形成的切面,称为复合切面。

a、增强类

在这里使用的是3.3.2的前置增强:

http://blog.csdn.net/p_3er/article/details/9239925


b、一个普通类中有一个获取Pointcut的方法

public class MyPointcut {
//获取Pointcut的方法名是get开头
	public Pointcut getMyComposablePointcut(){
		//创建一个复合切点
		ComposablePointcut cp = new ComposablePointcut();

		//创建一个流程切点(参数:使用类、类中的方法)这里使用的是3.4.5中的UserServiceImpl类
		Pointcut pt1 = new ControlFlowPointcut(UserServiceImpl.class,"operate");
		
		//创建一个静态方法切点
		Pointcut pt2 = new StaticMethodMatcherPointcut() {
			public boolean matches(Method method, Class<?> clazz) {
				return UserDaoImpl.class.isAssignableFrom(clazz)&&method.getName().equals("delete");
			}
		};

		//两个切点进行交集操作.
		return cp.intersection(pt1).intersection(pt2);
	}
}

c、配置

<!-- 增强Bean -->
	<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>

	<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	<!-- 切点所在类 -->
	<bean id="myPoincut" class="cn.framelife.spring.pointcut.MyPointcut"></bean>
	
	<!-- 
		切面
		p:pointcut #{ myPoincut.myComposablePointcut} 是由MyPointcut的getMyComposablePointcut方法获取的
	-->
	<bean id="composableAdvisor" 
		class="org.springframework.aop.support.DefaultPointcutAdvisor"
		p:pointcut="#{ myPoincut.myComposablePointcut}"
		p:advice-ref="userDaoBeforeAdvice"/>	
	
	<!-- 
		设置代理 
	-->
	<bean id="composableProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="composableAdvisor"
		p:target-ref="userDao"
		p:proxyTargetClass="true" />
	
	<!-- 把使用目标类的Bean交由Spring管理 -->
	<bean id="userService" class="cn.framelife.spring.service.impl.UserServiceImpl">
		<property name="userDao" ref="composableProxy"></property>
	</bean>


d、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserService userService = (UserService) context.getBean("userService");
		userService.operate();

e、结果

我是前置增强:delete
删除用户...
保存用户...

3.4.7引介切面

a、使用3.3.6引介增强里面的东西(接口与增强类)


b、配置

<!-- 目标Bean -->
	<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
	
	<!-- 
		切面
		constructor-arg 设置引介增强
	-->
	<bean id="introductionAdvisor" 
		class="org.springframework.aop.support.DefaultIntroductionAdvisor">
		<constructor-arg>
			<bean class="cn.framelife.spring.advice.IntroductionAdvice"></bean>
		</constructor-arg>
	</bean>	
	
	<!-- 
		设置代理 
	-->
	<bean id="incluctionProxy" 
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="introductionAdvisor"
		p:target-ref="userDao"
		p:proxyTargetClass="true" />


c、测试

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
		UserDao userDao = (UserDao) context.getBean("aProxy");
		userDao.save();
		
		System.out.println("-------------");
		
		AInterface a = (AInterface)userDao;
		a.say();
		
		System.out.println("-------------");
		userDao.save();


d、结果

方法执行前执行
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation
save
保存用户...
方法执行后执行
-------------
方法执行前执行
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation
say
UserDao要说话
方法执行后执行
-------------
方法执行前执行
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation
save
保存用户...
方法执行后执行


目录
相关文章
|
11月前
|
JSON Java API
【干货满满】分享京东API接口到手价,用Java语言实现
本示例使用 Java 调用京东开放平台商品价格及优惠信息 API,通过商品详情和促销接口获取到手价(含优惠券、满减等),包含签名生成、HTTP 请求及响应解析逻辑,适用于比价工具、电商系统集成等场景。
|
缓存 监控 负载均衡
如何提升 API 性能:来自 Java 和测试开发者的优化建议
本文探讨了如何优化API响应时间,提升用户体验。通过缓存(如Redis/Memcached)、减少数据负载(REST过滤字段或GraphQL精确请求)、负载均衡(Nginx/AWS等工具)、数据压缩(Gzip/Brotli)、限流节流、监控性能(Apipost/New Relic等工具)、升级基础设施、减少第三方依赖、优化数据库查询及采用异步处理等方式,可显著提高API速度。快速响应的API不仅让用户满意,还能增强应用整体性能。
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
568 2
|
11月前
|
JSON Java API
【干货满满】分享拼多多API接口到手价,用Java语言实现
本方案基于 Java 实现调用拼多多开放平台商品详情 API,通过联盟接口获取商品到手价(含拼团折扣与优惠券),包含签名生成、HTTP 请求及响应解析逻辑,适用于电商比价、导购系统集成。
|
11月前
|
JSON Java API
【干货满满】分享淘宝API接口到手价,用Java语言实现
本文介绍了如何使用 Java 调用淘宝开放平台 API 获取商品到手价,涵盖依赖配置、签名生成、HTTP 请求与响应解析等核心实现步骤。
|
JSON JavaScript 前端开发
Python+JAVA+PHP语言,苏宁商品详情API
调用苏宁商品详情API,可通过HTTP/HTTPS发送请求并解析响应数据,支持多种编程语言,如JavaScript、Java、PHP、C#、Ruby等。核心步骤包括构造请求URL、发送GET/POST请求及解析JSON/XML响应。不同语言示例展示了如何获取商品名称与价格等信息,实际使用时请参考苏宁开放平台最新文档以确保兼容性。
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
415 11
|
数据采集 存储 Java
Java爬虫获取微店店铺所有商品API接口设计与实现
本文介绍如何使用Java设计并实现一个爬虫程序,以获取微店店铺的所有商品信息。通过HttpClient发送HTTP请求,Jsoup解析HTML页面,提取商品名称、价格、图片链接等数据,并将其存储到本地文件或数据库中。文中详细描述了爬虫的设计思路、代码实现及注意事项,包括反爬虫机制、数据合法性和性能优化。此方法可帮助商家了解竞争对手,为消费者提供更全面的商品比较。
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
5133 5

热门文章

最新文章