1、引入(Introduction)
我们在编程式实现Spring AOP中,我们所提到的前置增强(Before Advice)、后置增强(After Advice)、环绕增强(Around Advice)都是对方法的增强;那么在我们实际的项目研发过程中,我们是否可以对类进行增强呢,答案是肯定的。在AOP中,对方法的增强,我们称之为织入(Weaving),而对类的增强,我们称之为引入(Introduction)。如何实现引入,我们通过如下的例子来给大家阐述。
我们先定义一个Greeting接口和该接口的实现类GreetingImpl
Greeting
public interface Greeting {
void sayHello(String name);
}
GreetingImpl
public class GreetingImpl implements Greeting {
@Override
public void sayHello(String name) {
System.out.println("Hello:"+name);
}
}
我们再定义一个接口,接口叫做Apology
Apology
public interface Apology {
void saySorry(String name);
}
我们不想在GreetingImpl直接去实现Apology接口,但是我们想在程序运行的时候动态的实现它。因为假如在GreetingImpl实现了这个接口,我们就必须修改GreetingImpl这个类,关键是我们现在不想修改这个类。于是,我们就借助Spring的引入增强。
实现引入增强,首先我们要先定义一个引入增强类,定义如下。
GreetingIntroAdvice
public class GreetingIntroAdvice extends DelegatingIntroductionInterceptor implements Apology {
@Override
public Object invoke(MethodInvocation mi) throws Throwable
{
return super.invoke(mi);
}
@Override
public void saySorry(String name) {
System.out.println("Sorry "+name);
}
}
该增强类拓展了DelegatingIntroductionInterceptor类并实现了Apology 接口。在类中首先覆盖了父类的invoke方法,然后实现了Apology 的方法。我们想用这个类去增强GreetingImpl类的功能,那么GreetingImpl这个类就不用实现Apology 接口,就可以在程序运行的时候调用Apology接口的方法了。
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
GreetingImpl greetingImpl = new GreetingImpl();
proxyFactory.setTarget(greetingImpl);
proxyFactory.addAdvice(new GreetingIntroAdvice());
Apology greeting = (Apology) proxyFactory.getProxy();
greetingImpl.sayHello("Ron.Zheng");
greeting.saySorry("Ron.Zheng");
}
在上面的代码中,我们先new了一个代理工厂,然后new了一个GreetingImpl的实例,将该实例作为代理工厂的目标对象,同时在代理工厂中添加GreetingIntroAdvice 增强,通过代理工厂获取代理之后强制转换成Apology接口,调用其方法即可实现增强。
Hello:Ron.Zheng
Sorry Ron.Zheng
2、切面
在我们前面编程式实现Spring AOP 这篇文章中所提供的例子中。我们对方法的增强所采用的无论是前置增强、后置增强还是环绕增强,我们都可以将他们理解成方法拦截器,但是这个拦截器非常的武断,如果它所拦截的类,它就会拦截这个类中的所有方法。但是在大量的真实项目中,似乎我们只需要拦截一些指定的方法就行了,没必要拦截所有的方法。如何才能够实现只拦截一些指定的方法呢?就是我们接下来将介绍的AOP的一个很重要的工具—Advisor(切面)。
我们可以通过切面,将增强类与拦截匹配条件组合在一起,然后将这个切面配置到ProxyFactory中,从而生成代理。这里所说的“拦截匹配条件”指定就是我们一直所说的Pointcut(切点)。Advisor(切面)封装了Advice(增强)与Pointcut(切点)。
我们来看一个例子。
在Greeting接口中,我们增加两个方法goodMonrning和goodNight,然后在进行方法拦截时,我们只拦截good开头的方法,而不拦截sayHello方法。
Greeting
public interface Greeting {
void sayHello(String name);
void goodMonrning(String name);
void goodNight(String name);
}
GreetingImpl
public class GreetingImpl implements Greeting {
@Override
public void sayHello(String name) {
System.out.println("Hello:"+name);
}
@Override
public void goodMonrning(String name) {
System.out.println("Good Monrning:"+name);
}
@Override
public void goodNight(String name) {
System.out.println("Good Night:"+name);
}
}
要实现我们上面说的只拦截good开头的方法,而不拦截sayHello方法功能,这里我们借助RegexpMethodPointcutAdvisor这个基于正则表达式的切面类。
public static void main(String[] args) {
// 获得并设置切面
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
advisor.setAdvice(new GreetingAroundAdvice());
advisor.setPattern("com.aop1.service.GreetingImpl.good.*");
// 配置代理
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new GreetingImpl());
proxyFactory.addAdvisor(advisor);
// 获取代理
Greeting greeting = (Greeting) proxyFactory.getProxy();
greeting.sayHello("Ron.Zheng");
greeting.goodMonrning("Ron.Zheng");
}
我们new一个RegexpMethodPointcutAdvisor实例,设置切面的通知为我们在编程式实现Spring AOP 所提供的环绕通知GreetingAroundAdvice;然后设置对应的正则表达式,使得当且仅当执行以good开头的方法时才对对应方法进行增强,执行对应的增强逻辑。
Hello:Ron.Zheng
调用方法之前
Good Monrning:Ron.Zheng
调用方法之后