一. 应用开发指导和规则
想通过提供一种在编译时强制执行的策略,来控制在应用程序中允许哪些程序构造。可使用Border Controller(边界控制器)面向方面设计模式声明代码内的一组区域。当依据策略模式在方面中为项目声明任何顶级规则时,重用这些区域。可以扩展项目的顶级策略,为应用程序的特定区域特殊化它们。
示例中BorderControllerAspect声明了4个区域:withinTestingRegion()区域纳入了存放测试代码的包,withinMyapp()指定了组成应用程序的包和子包,withinThirdParty()指定了可以使用第三方源代码的任何区域,withinMyAppMainMethod()则方便地声明了应用程序的main(..)方法的位置。
package com.aspectj;
public aspect BorderControllerAspect
{
/** *//**
* Specifies the testing region.
*/
public pointcut withinTestingRegion() : within(com.oreilly.aspectjcookbook.testing.+);
/** *//**
* Specifies My Applications region.
*/
public pointcut withinMyApp() : within(com.oreilly.aspectjcookbook.myapp.+);
/** *//**
* Specifies a third party source code region.
*/
public pointcut withinThirdParty() : within(com.oreilly.aspectjcookbook.thirdpartylibrary.+);
/** *//**
* Specifies the applications main method.
*/
public pointcut withinMyAppMainMethod() : withincode(public void com.oreilly.aspectjcookbook.myapp.MyClass.main(..));
}
示例中只显示了可以在应用程序中定义的其中一些区域,其他良好的候选有:将发生特殊日志据记录的区域;受延迟加载逻辑支配的区域;以及你发现体系结构的真实定界部分是有用的任何区域,他们使得更多的切入点定义可以重用,并在那些边界内安全地工作。其思想是:如果这些边界发生变化,则只需要更改Boarder Controller,使得应用程序其余切入点逻辑将立即获得对其作用域的任何相关的改变。
Border Controller提供了一个有用的可重用切入点定义的库,它被纳入到策略面向方面设计模式中。
package com.aspectj;
public abstract aspect ProjectPolicyAspect
{
protected abstract pointcut allowedSystemOuts();
declare warning :
call(* *.println(..)) &&
!allowedSystemOuts() &&
!BorderControllerAspect.withinTestingRegion()
: "System.out usage detected. Suggest using logging?";
}
为应用程序区域的细节特殊化项目级策略
package com.aspectj;
public aspect MyAppPolicyAspect extends ProjectPolicyAspect
{
/** *//**
* Specifies regions within the application where system out
* messages are allowed.
*/
protected pointcut allowedSystemOuts() :
BorderControllerAspect.withinMyAppMainMethod() ||
BorderControllerAspect.withinThirdParty() ||
BorderControllerAspect.withinTestingRegion();
}
二.应用事务
package com.aspectj;
public abstract aspect TransactionAspect
{
protected abstract pointcut transactionalCall();
protected interface Transaction
{
public void commit();
public void rollback();
}
protected pointcut transactionBoundary() :
transactionalCall() && !cflowbelow(transactionalCall());
before() : transactionBoundary()
{
setupTransaction(thisJoinPoint.getArgs());
}
after() returning: transactionBoundary()
{
transaction.commit();
}
after() throwing: transactionBoundary()
{
transaction.rollback();
}
protected abstract void setupTransaction(Object[] args);
protected Transaction transaction;
}
TransactionAspect首先指定transactionCall()抽象切入点。特殊化的子方面使用这个切入点来指定将把目标应用程序内的方法视作事务性的。
transactionBounday()切入点然后依靠transactionCall()切入点来指定事务开始和结束的位置。cflowbelow()切入点用于忽略可能出现在事务寿命内的任何连接点。
TransactionAspect()一般需要存储事务并与之交互,因此定义了Transaction接口。Transaction接口为子方面提供了一个基础,用以实现他们自己的事务类。然后使用单个事务属性来指定正被方面管理的当前事务。
最后,在事务生命周期内的不同时刻,将有三份通知处理事务属性。before()通知调用setupTransaction(Object[])抽象方法,以便用合适的Transaction实现正确地初始化事务属性。如果transactionCall()切入点所选的连接点未引发异常地返回,将会执行after() returning通知;这是提交事务的良好时间。after() throwing通知用于连接点带异常地返回的情况,因此需要回滚事务。
package com.aspectj;
public aspect TransferTransactionAspect extends TransactionAspect
{
protected pointcut transactionalCall() :
call(public void com.oreilly.aspectjcookbook.Bank.transfer(..));
private class TransferTransaction extends ThreadLocal implements Transaction
{
private Account from;
private Account to;
private float value;
public TransferTransaction(Account from, Account to, float value)
{
this.from = from;
this.to = to;
this.value = value;
}
public void commit()
{
System.out.println("Committing");
// Nothing to actually commit here, all the changes have been accepted ok
}
public void rollback()
{
System.out.println("Rolling back");
try
{
to.debit(value);
}
catch(InsufficientFundsException ife)
{
System.err.println("Could not complete rollback!");
ife.printStackTrace();
}
}
}
protected void setupTransaction(Object[] args)
{
this.transaction =
new TransferTransaction(
(Account) args[0],
(Account) args[1],
((Float)args[2]).floatValue());
}
}
三.应用资源池
package com.aspectj;
import java.util.WeakHashMap;
public abstract aspect ResourcePoolingAspect
{
public interface Resource
{
}
public interface ResourcePool
{
public void add(Resource resource);
public Resource remove();
}
protected class ResourcePoolsCollection
{
WeakHashMap pools = new WeakHashMap();
public void putResourcePool(ResourcePool pool, Class resourceClass)
{
pools.put(resourceClass, pool);
}
public ResourcePool getResourcePool(Class resourceClass)
{
return (ResourcePool) pools.get(resourceClass);
}
}
protected ResourcePoolsCollection resourcePools = new ResourcePoolsCollection();
public ResourcePoolingAspect()
{
initializeSpecificPool();
}
protected abstract void initializeSpecificPool();
private pointcut excludeAspects() : !within(ResourcePoolingAspect+);
public abstract pointcut catchResourceConstruction();
public abstract pointcut catchResourceDestruction(Resource resource);
Object around() : catchResourceConstruction() && excludeAspects()
{
ResourcePool resources =
resourcePools.getResourcePool(thisJoinPoint.getSignature().getDeclaringType());
return resources.remove();
}
Object around(Resource resource) : catchResourceDestruction(resource) && excludeAspects()
{
ResourcePool resources =
resourcePools.getResourcePool(thisJoinPoint.getSignature().getDeclaringType());
Object returnValue = resourceReturnedToPool(resource);
System.out.println("Resource added back into pool: " + resource);
resources.add(resource);
return returnValue;
}
protected abstract Object resourceReturnedToPool(Resource resource);
}
示例中定义了一个可重用的抽象方面,它将在最初不支持资源池的应用程序内提供一个资源池。声明Resource和ResourcePool接口,以便可以对这些接口定义通用的资源池行为,并把它们与这些接口可能的实现方式隔离开。
可以跨所有声明为子方面的资源池共享在抽象的ResourcePoolingAspect内声明的行为。ResourcePoolCollection类为整个应用程序中的所有资源池提供了一个公共库,使得通用代码可以依据特定资源池说包含资源的类,来查寻该资源池。
package com.aspectj;
import java.util.List;
import java.util.ArrayList;
public aspect BusinessResourcePoolingAspect extends ResourcePoolingAspect
{
declare parents : BusinessResource implements Resource;
public pointcut catchResourceConstruction() : call(public BusinessResource.new());
public pointcut catchResourceDestruction(Resource resource) :
call(public void BusinessResource.close()) && target(resource);
private class BusinessResourcePool implements ResourcePool
{
private static final int RESOURCE_POOL_SIZE = 10;
List resources = new ArrayList();
public BusinessResourcePool()
{
for (int x = 0; x < RESOURCE_POOL_SIZE; x++)
{
this.add(new BusinessResource());
}
}
public synchronized void add(Resource resource)
{
resources.add(resource);
}
public synchronized Resource remove()
{
if (resources.size() == 0)
{
resources.add(new BusinessResource());
}
return (Resource) resources.remove(resources.size() - 1);
}
}
protected void initializeSpecificPool()
{
try
{
this.resourcePools.putResourcePool(new BusinessResourcePool(),
Class.forName("com.oreilly.aspectjcookbook.BusinessResource"));
}
catch (ClassNotFoundException cnfe)
{
System.err.println("Couldn't find resource class to pool");
}
}
protected Object resourceReturnedToPool(Resource resource)
{
// Do any resource specific tudying up if necessary
// None to do in this example
return null;
}
}
四.使用RMI透明地远程访问类
创建一个RMI服务器应用程序,它包含类的一个实例。如:
import java.rmi.RemoteException;
import java.rmi.server.*;
public class ThisOrThatServerImpl extends UnicastRemoteObject implements ThisOrThatServer
{
BusinessClass businessClass = new BusinessClass();
public ThisOrThatServerImpl() throws RemoteException
{
}
public void foo() throws RemoteException
{
this.businessClass.foo();
}
}
在客户应用程序内创建一个方面,它用于截获对远程类的特定实例的调用,并把这些调用路由到相应的RMI服务器,如:
import java.rmi.*;
public aspect RemoteBusinessClassAspect
{
public pointcut callBusinessClassFooInMain() : call(public void BusinessClass.foo()) &&
withincode(public void MainApplication.main(String[]));
void around() : callBusinessClassFooInMain()
{
try
{
ThisOrThatServer rmtServer = (ThisOrThatServer) Naming.lookup("rmi://localhost/TTTServer");
rmtServer.foo();
}
catch (Exception e)
{
System.err.println("Problems occured when attempting " +
"to use remote object, default to local");
proceed();
}
}
}
五.应用安全策略
package com.aspectj;
public aspect SecureClassAAspect
{
private boolean authenticated;
public pointcut secureClassAMethods() :
call(* com.oreilly.aspectjcookbook.ClassA.*(..));
Object around() : secureClassAMethods()
{
if (authenticated)
{
return proceed();
}
else
{
LoginScreen loginScreen = new LoginScreen();
loginScreen.setVisible(true);
// Use the authentication procedure of your choice here
// In this simple example we are just going to check that
// it is the one person we know of
if ((loginScreen.getUsername().equals("Richard")) &&
(new String(loginScreen.getPassword()).equals("password")))
{
authenticated = true;
loginScreen.dispose();
return proceed();
}
loginScreen.dispose();
return null;
}
}
}