spring 面向接口编程
介绍
在理想的面向对象系统中,我们希望将每个对象设计为执行一项特定任务。 但是,除了执行其主要任务之外,对象还执行被动任务,例如日志记录,事务,安全性,缓存等。这些被动活动是必需的,但不是业务逻辑的一部分,被称为“ 跨切问题 ”。
(横切关注点==系统中常用的功能)
将横切关注点与业务逻辑分开可以是编写精心设计的解耦代码的重要一步。 让我们思考一下解决交叉切割问题的方法
遗产
继承立即在脑海中弹出,我们可以继承通用功能并在对象中使用它。 但是继承通用功能需要我们设计一个基类。 如果我们在多个地方重用该类,那么以后修改类可能会很困难。
继承==以后很难修改(非弹性代码)
代表团
委派是处理交叉切割问题的更好方法。 记住组成要重于继承,(授权和组成要共同关注)。 但是,然后我们将不得不在许多地方进行调用以委托对象,从而使其变得繁琐。
委派==繁琐
面向方面的编程
这是否意味着我们要喝汤了。 相反,这给我们留下了第三种也是最好的方法,即面向方面的编程。 AOP避免了继承的脆弱性和委派的繁琐性。 AOP在相互交叉的关注点领域大放异彩
什么是AOP?
AOP允许我们将横切关注点模块化到称为Aspects的特殊对象中,从而创建更清晰和分离的代码。 有了适当的方面,对象就不必担心执行被动横切问题,因为AOP会处理所有这些问题。
与AOP有关的术语
像任何成功的技术一样,AOP也具有自己的术语和术语集。 让我们先看一下那些,然后再进行更深入的了解AOP的工作。
- 关注点 -这些是基于其功能模块化的系统的一部分。 有两种类型的关注点。 1.核心关注点2.跨领域关注点。 核心关注点与系统的业务逻辑有关,即系统执行的主动任务,例如生成工资单,获取员工记录,进行银行转账等。跨部门关注点是执行主动任务(例如日志记录,缓存)所需的被动任务等等
- 连接点–连接点是执行流程中的一个点,其中发生了一些动作,并且有可能应用Aspect(跨领域关注点)。 连接点可以是被调用的方法,抛出异常或对象状态改变。
- 建议 – AOP中的每个方面都有其目的,即必须完成的工作。 该作业必须在连接点处应用。 方面的工作或目的称为建议。 除了定义方面的工作之外,建议还定义方面执行工作的时间。 应该在核心关注事项完成执行之前或之后应用作业,还是在两者之前和之后应用作业。
- 切入点 –系统中可以有许多连接点,但并非所有方面都由Aspect建议。 Aspect从Pointcut获得帮助,以选择要在其中编织建议的Joinpoint。
- 方面 –建议和切入点定义了方面。 正如我们看到的,建议定义了方面的工作以及何时执行。 虽然Pointcut定义了方面编织建议的位置。 因此,工作的内容,时间和地点定义了方面。
- 目标 –目标是被建议的对象。 (核心关注)。 在AOP的帮助下,该对象可以自由地执行其主要任务,而不必担心交叉问题。
- 代理 –将建议应用于目标对象时,将创建一个代理对象。 AOP容器创建并管理对象的生命周期,程序员无需担心它们。
- 编织 –编织是将Advice或Aspect应用于目标对象以创建代理对象的过程。 编织可以在编译时或类加载时或在运行时完成。 通常,Spring AOP在运行时将方面编织到目标对象中。
那是要消化的一长串术语。 在继续之前,请花点时间了解它们。
咨询类型
在沉迷于示例之前的最后一篇文章是了解建议的类型。 主要有4种建议。
- 建议之前 –在Joinpoint开始执行之前应用建议之前。 通过实现org.springframework.aop.MethodBeforeAdvice接口来创建BeforeAdvice。 要实现的方法是公共无效(方法m,对象args [],对象目标)抛出Throwable
- 返回建议之后–在Joinpoint完成执行之后应用建议之后。 AfterReturningAdvice是通过实现org.springframework.aop.AfterReturningAdvice接口创建的。 ThingableReturning(Method m,Object args [],Object target)抛出之后 ,要实现的方法是公共无效
- 引发建议 –当Joinpoint在执行过程中引发异常时,将应用引发建议。
- 围绕建议 –此建议围绕Joinpoint执行,并在Joinpoint执行之前和之后执行。 这甚至可以用来控制Joinpoint的调用。
例
我们将尝试在SpringAOP的帮助下开发一个简单的缓存。 缓存具有三个主要的核心问题。
核心关注点
- 将对象保存在缓存中。
- 从缓存返回对象。
- 从缓存中删除对象。
现在,除了这些核心问题之外,缓存框架还有其他被动任务。 这些被动任务构成了交叉问题。
横切关注点
- 达到其大小限制时重新调整缓存大小。 (LRU)实施。
- 锁定对象以防止在读取对象时将其删除。
- 锁定高速缓存以防止在调整大小时阻止和读取/写入/删除高速缓存。
编码所有这些横切关注点可能是耗时且乏味的,因此让我们简化示例,当缓存已满时,我们将仅实现调整大小逻辑。 因此,在完成示例之后,我们将拥有一个可以放置,获取和删除对象的缓存。 例如,缓存的最大大小已设置为10。 一旦高速缓存存储了10个对象,则对高速缓存的任何添加都将导致通过删除第一个对象来删除(重新调整大小)高速缓存。 调整大小的操作由使用Spring AOP创建的Aspect控制。 这是示例中要遵循的步骤
可以从SVN此处下载示例代码: https : //www.assembla.com/code/weblog4j/subversion/nodes/31/SpringDemos/trunk
- 依赖关系 – AOP是spring的核心功能,因此要使Spring AOP正常运行,我们需要的是核心spring jar,因此在您的POM中添加以下依赖关系。
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency>
- 核心缓存对象。
package com.aranin.spring.aop;import java.util.Date; import java.util.LinkedHashMap; import java.util.Map;public class MyCache {private LinkedHashMap<String, Object> cacheMap = new LinkedHashMap<String, Object>();private LinkedHashMap<String, Date> timeStampMap = new LinkedHashMap<String, Date>();/*** defines the max size of hashmap*/private long maxsize = 10; //should come from properties file or some configuration/*** how long the object should be stored before it is evicted from cache*/private long objectLifeTime = 10000;private boolean lock = false;public LinkedHashMap<String, Object> getCacheMap() {return cacheMap;}public void setCacheMap(LinkedHashMap<String, Object> cacheMap) {this.cacheMap = cacheMap;}public LinkedHashMap<String, Date> getTimeStampMap() {return timeStampMap;}public void setTimeStampMap(LinkedHashMap<String, Date> timeStampMap) {this.timeStampMap = timeStampMap;}public long getMaxsize() {return maxsize;}public void setMaxsize(long maxsize) {this.maxsize = maxsize;}public long getObjectLifeTime() {return objectLifeTime;}public void setObjectLifeTime(long objectLifeTime) {this.objectLifeTime = objectLifeTime;}public boolean isLock() {return lock;}public void setLock(boolean lock) {this.lock = lock;}/*** This method is used to retrive the object from cache* @param key* @return*/public Object get(String key){return this.getCacheMap().get(key);}/*** this method is used for putting an object in cache* @param key* @param object*/public void put(String key, Object object){//get the curr dateDate date = new Date(System.currentTimeMillis());//set object in cacheMapthis.getCacheMap().put(key,object);//put timestamp in cachethis.getTimeStampMap().put(key, date);}public void delete(String key){this.getCacheMap().remove(key);this.getTimeStampMap().remove(key);}public void clearAll(){this.setCacheMap(new LinkedHashMap<String, Object>());this.setTimeStampMap(new LinkedHashMap<String, Date>());}/*** remove last 2 entries* not worried about object life time* this is just an example*/public void resize(){System.out.println("inside resize");long size = this.getCacheMap().size();System.out.println("size + " + size);if(size == this.getMaxsize()){System.out.println("max size has reached");Map.Entry<String, Date> firstEntry = this.getTimeStampMap().entrySet().iterator().next();System.out.println("removing : " + firstEntry.getKey() + " value : " + firstEntry.getValue());this.timeStampMap.remove(firstEntry.getKey());Map.Entry<String, Object> firstCEntry = this.getCacheMap().entrySet().iterator().next();System.out.println("removing : " + firstCEntry.getKey() + " value : " + firstCEntry.getValue());this.cacheMap.remove(firstCEntry.getKey());}System.out.println("leaving resize with size : " + this.getCacheMap().size());} }
这个课没什么好说的。 有两个LinkedHashMaps,一个用于存储对象,另一个用于存储将对象推入缓存时的时间戳。 最大大小设置为10,并且具有get,put和delete方法。 还有一个调整大小的方法,Aspect将调用此方法,稍后我们将进行检查。
- 调整建议
package com.aranin.spring.aop;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class ResizeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("invoking " + method.getName() + " on " + target.getClass() + " Object");if(method.getName().equals("put")){System.out.println("before invoking " + method.getName());((MyCache)target).resize();}} }
如您所见,这是建议之前的一种方法。 类实现MethodBeforeAdvice接口,该接口包含单个方法before()。 如果您检查该方法,则在我们调用put方法时,将检查rezise方法是否被调用。
- Spring上下文springaopdemo.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.1.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.1.xsd"><bean id="resizeAdvice" class="com.aranin.spring.aop.ResizeAdvice" /><bean id="myCache" class="com.aranin.spring.aop.MyCache" /><bean id="myAOPCache"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="target" ref="myCache" /><property name="interceptorNames"><list><value>resizeAdvice</value></list></property></bean> </beans>
如果您注意到上述xml文件,则MyCache和ResizeAdvice均已注册为spring bean。 文件中的主要bean是myAOPCache。 这是spring aop在核心类上应用建议后创建的代理对象。 代理对象由ProxyFactoryBean类创建。 我们将myCache对象的引用传递给代理对象,并注册所有将应用于代理类的建议。
- 最后,让我们检查Client,这将有助于我们运行此演示。
package com.aranin.spring.aop;import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;public class MyCacheClient {public static void main(String[] args){ApplicationContext springcontext = new FileSystemXmlApplicationContext("D:/samayik/SpringDemos/src/main/resources/springaopdemo.xml");MyCache myCache = (MyCache)springcontext.getBean("myAOPCache");myCache.put("1", "1");myCache.put("2", "2");myCache.put("3", "3");myCache.put("4", "4");myCache.put("5", "5");myCache.put("6", "6");myCache.put("7", "7");myCache.put("8", "8");myCache.put("9", "9");myCache.put("10", "10");System.out.println((String)myCache.get("1"));System.out.println((String)myCache.get("2"));System.out.println((String)myCache.get("10"));myCache.put("11", "11");System.out.println((String)myCache.get("1"));System.out.println((String)myCache.get("2"));System.out.println((String)myCache.get("10"));System.out.println((String)myCache.get("11"));}}
在此类中,我们启动弹簧容器并加载spingaopdemo.xml中存在的bean。 我们在缓存中推送10个对象,当我们尝试推送第11个对象时,第一个对象将被删除并插入第11个对象。 输出很大,所以我没有发布输出。 上课并检查输出是否令您满意。
摘要
在这篇文章中,我们学习了如何使用面向方面的编程更好地处理交叉问题。 AOP是一个强大的概念,可让我们编写更简洁,更解耦的代码。 AOP不提供任何新内容。 它所做的只是将业务逻辑与系统必须执行的其他普通任务分离。 它可以重用实现系统范围内交叉关注点的代码。 我们还学习了与AOP相关的各种术语。 最后但并非最不重要的一点是,我们看到了一个简单的示例,在该示例中,我们使用Spring AOP创建了一个简单的方法前建议,并将其应用于管理缓存系统。
注意
您可以自由使用和分发此代码中开发的缓存系统。 尽管不建议在生产系统中使用它。
一如既往,我一直希望这篇文章作为集体学习的启动平台,随意发表一两个关于您对AOP的看法以及如何计划在代码中使用它的评论。 祝您阅读愉快。
翻译自: https://www.javacodegeeks.com/2013/10/aspect-oriented-programming-with-spring-2.html
spring 面向接口编程