什么是Aop?
AOP是Aspect-Oriented Programming(面向方面编程或面向切面)的简称,最主要的是从业务代码中找到关键的关注点,并分离关注点,使解决特定领域问题(日志,监控)的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。
Aop相关技术:
- AsepectJ,源代码级别和字节码级别。
- Javassit,Java字节码操作类。
- java instrument,虚拟机级别的Aop,监控用的比较多,例如各种agent。
Aop联盟定义的Aop体系架构:
“基础”(base)可以视为待增强对象或者说目标对象;“切面”(aspect)通常包含对于基础的增强应用;“配置”(configuration)可以看成是一种编织,通过在AOP体系中提供这个配置环境,可以把基础和切面结合起来,从而完成切面对目标对象的编织实现。
Spring Aop内核心概念
Advice 通知
Advice(通知)定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。在Spring AOP的实现中,使用了这个统一接口,并通过这个接口,为AOP切面增强的织入功能做了更多的细化和扩展,比如提供了更具体的通知类型,如BeforeAdvice、AfterAdvice、ThrowsAdvice等。
PointCut 切点
Pointcut(切点)决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。在这种情况下,Pointcut通常意味着标识方法,例如,这些需要增强的地方可以由某个正则表达式进行标识,或根据某个方法名进行匹配等。
Advisor通知器
完成对目标方法的切面增强设计(Advice)和关注点的设计(Pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor(通知器)。通过Advisor,可以定义应该使用哪个通知并在哪个关注点使用它,也就是说通过Advisor,把Advice和Pointcut结合起来,这个结合为使用IoC容器配置AOP应用,或者说即开即用地使用AOP基础设施,提供了便利。
在Spring内org.springframework.aop.support.DefaultPointcutAdvisor可以用来结合Advice和PointCut,此类有两个属性,一个是Advice一个是PointCut,很简单清晰。
代理设计模式
代理设计模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
维基百科官方实例:
我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。ProxyPatternDemo,我们的演示类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。
public interface Image {
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
JDK动态代理
JDK动态代理模式,通过反射和拦截进行实现,主要有以下几个要求:
1.被代理的类必须实现接口。
2.通过Proxy.newProxyInstance来生成代理类。
3.需要实现InvocationHandler接口。
具体的例子:
public interface RiskService {
void execute();
void execute(String str);
}
public class RiskServiceImpl implements RiskService {
@Override
public void execute() {
System.out.println("execute risk service");
}
@Override
public void execute(String str) {
System.out.println("execute risk service" + str);
}
}
public class RiskServiceInvocation implements InvocationHandler {
private Object target;
public RiskServiceInvocation(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前置操作");
System.out.println(args.toString());
Object result = method.invoke(target, args);
System.out.println("执行后置操作");
return result;
}
}
public class ProxyRiskTest {
public static void main(String[] args) {
RiskService riskService = (RiskService) Proxy.newProxyInstance(RiskService.class.getClassLoader(), new Class[]{
RiskService.class }, new RiskServiceInvocation(new RiskServiceImpl()));
riskService.execute(" str");
}
结论:JDK只能生成接口实现的代理类,在某些使用场景下,不足够灵活,下面可以看下使用Cglib代理。
Cglib动态代理
public class RiskServiceImplCglibProxy implements MethodInterceptor {
public Object createInstance(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib开始");
Object invokeSuper = methodProxy.invokeSuper(o, objects);
System.out.println("cglib结束");
return invokeSuper;
}
}
public class ProxyRiskTest {
public static void main(String[] args) {
/* RiskService riskService = (RiskService) Proxy.newProxyInstance(RiskService.class.getClassLoader(), new Class[]{
RiskService.class }, new RiskServiceInvocation(new RiskServiceImpl()));
riskService.execute(" str");*/
RiskServiceImplCglibProxy cglibProxy = new RiskServiceImplCglibProxy();
RiskServiceImpl proxyInstance = (RiskServiceImpl) cglibProxy.createInstance(new RiskServiceImpl());
proxyInstance.execute();
}
}
总结
Aop即面向切面编程,本质上解决的问题是,将一些有别于业务的逻辑的代码,从业务代码中剥离出来,进行统一的封装和维护。再通过代理设计模式的思想,利用jdk动态代理技术或者cglib技术,通过定义出切点(要执行的哪段逻辑代码,一般是方法维度),通过定义通知(前置,后置,环绕等),再通过通知器把两者给捏合起来。来达到既能执行业务逻辑代码又能执行特定代码的目的。从另一个维度去看,Aop本质上也是解耦和内聚,对不关联逻辑解耦,对关联逻辑内聚。