@TOC# Spring系列
记录在程序走的每一步___auth:huf
OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想 在Spring官网中有这么一句话;
Let us begin by defining some central AOP concepts and terminology.
These terms are not Spring-specific. Unfortunately,
AOP terminology is not particularly intuitive. However,
it would be even more confusing if Spring used its own terminology
其意思是:
AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是, 如果Spring重新定义自己的那可能会导致更加混乱
我们知道;Spring当中 有两种代理; 一种代理是JDK代理; 一种代理是CGLIB代理; 其两种代理的区别在什么地方呢? 在什么时候情况下会使用JDK 代理; 在什么情况下使用后CGLIB 代理? 这是我们必须要搞清楚的一件事情; 我们先不管什么是切面 什么是切点 什么是通知 等等 都不用详细的去看里面的源码; 我们一步一步跟着代码实践下来; 就会明白;
CGLIB
创建一个被代理类; 被代理类 就是我们经常用的Service;
创建代理类:
最后一个执行结果:
得到的都是CglibDemoService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(CglibDemoService)是父类,代 理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的;
JDK代理;
JDK代理是基于接口的代理; 也就是说 被代理的一定是接口; 那么 我们就写一个接口 一个实现;
实现:
JDK 代理:
最后的执行结果:
以上就是 CGLIB 以及 JDK 代理的主要实现方式;
以下代码不以图片展示. 以图片展示 是作者希望读者能自己手动敲一遍 这样的话 可以加深对AOP的理解; 本系列文章全部代码 均是作者一个一个字母敲出来的;全部代码均自测过;
ProxyFactory
在Spring中; 针对以上两种代理进行一个封装;封装出口的类 叫做ProxyFactory 表示是创建代理对象的一个工厂,使用起来会比上面的更加方便;
package com.huf.aop;
import com.huf.aop.cglib.CglibDemoService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
/**
* auth : huf
*/
public class ProxyFactoryDemo {
public static void main(String[] args) {
CglibDemoService target = new CglibDemoService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("执行方法前 : before");
Object result = methodInvocation.proceed();
System.out.println("执行方法前 : after");
return result;
}
});
CglibDemoService cglibDemoService = (CglibDemoService) proxyFactory.getProxy();
cglibDemoService.test();
}
}
通过ProxyFactory,我们可以不再关系到底是用cglib还是jdk动态代理了,ProxyFactory会帮我们去 判断
如果我们 setTarget() 放进去的类 实现了接口. 那么 ProxyFactory 就会选择JDK代理 如果 不是接口 就使用CGLIB 代理; 以上就是使用的CGLIB代理的; 如果替换成为JdkDemoService 那么就是JDK代理 所以 转换出来的 是JdkDemoInterface;
在上面Proxy 类中. 我们看到 .我们添加了一个Advice 其中 Advice 就是我们的 <<通知>>
Advice 的分类
- Before Advice:方法之前执行
- After returning advice:方法return后执行
- After throwing advice:方法抛异常后执行
- After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
- Around advice:这是功能最强大的Advice,可以自定义执行顺序
Advisor
Advisor 我个人理解是对 Advice 的增强; 一个Advisor 里面 包含了
一个 Advice – <<通知>>
一个 Pointcut–<<切点>>
通过Pointcut可以指定要需要被代理的逻辑;
一个小的案例 可以清楚的知道Pointcut 是什么
/**
* auth : huf
*/
public class ProxyFactoryDemo {
public static void main(String[] args) {
JdkDemoService jdkDemoService = new JdkDemoService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(jdkDemoService);
proxyFactory.addAdvisor(new PointcutAdvisor() {
切点 切入到哪个方法中;
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> aClass) {
return method.getName().equals("test1");
}
};
}
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("方法执行前 : before");
Object proceed = methodInvocation.proceed();
System.out.println("方法执行后 : after");
return proceed;
}
};
}
@Override
public boolean isPerInstance() {
return false;
}
});
JdkDemoInterface jdkDemoInterface = (JdkDemoInterface) proxyFactory.getProxy();
在执行 test的方法; 是不会被代理的; 因为有一个
jdkDemoInterface.test();
System.out.println("------------------------------------------------------------");
在执行 test1 的方法 被代理了;
jdkDemoInterface.test1();
}
}
创建代理对象的方式
上面介绍了Spring中所提供了[ProxyFactory、Advisor、Advice、PointCut]等技术来实现代理对象的 创建,但是我们在使用Spring时,我们并不会直接这么去使用ProxyFactory,比如说,我们希望 ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对 象,作为开发者的我们肯定得告诉Spring,那些类需要被代理,代理逻辑是什么.
以下是一个演化过程; 我们将逐步深入AOP;
我们通过ProxyBean 进行 Bean的注册;
ProxyFactoryBean
@Bean
public ProxyFactoryBean proxyFactoryBean() {
CglibDemoService cglibDemoService = new CglibDemoService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(cglibDemoService);
proxyFactoryBean.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("方法执行前 : before");
Object proceed = methodInvocation.proceed();
System.out.println("方法执行后 : after");
return proceed;
}
});
return proxyFactoryBean;
}
执行结果是 :
继续演进; 我们可以 将 advice 单独注册;
@Bean
public MethodInterceptor hufAroundAdvise() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("方法执行前 : before");
Object proceed = invocation.proceed();
System.out.println("方法执行后 : after");
return proceed;
}
};
}
@Bean
public ProxyFactoryBean proxyFactoryBean() {
CglibDemoService cglibDemoService = new CglibDemoService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(cglibDemoService);
proxyFactoryBean.setInterceptorNames("hufAroundAdvise");
return proxyFactoryBean;
}
他们的执行结果是一模一样的 仅仅是将 advice 注册成为了Bean;
继续往下深挖; 我们既然可以注册一个 那么 我们是否可以批量注册??
BeanNameAutoProxyCreator
两个普通的Service
@Component
public class CglibDemoService {
public void test(){
System.out.println("CGLIB 动态代理演示;");
}
}
@Component
public class CglibDemoService2 {
public void test(){
System.out.println("CGLIB2 动态代理演示;");
}
}
@Configuration
public class AopConfig {
@Bean
public MethodInterceptor hufAroundAdvise() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("方法执行前 : before");
Object proceed = invocation.proceed();
System.out.println("方法执行后 : after");
return proceed;
}
};
}
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("cglibDemoServic*");
beanNameAutoProxyCreator.setInterceptorNames("hufAroundAdvise");
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
}
执行:
结果:
这个方法虽然可行 但是只能通过Class Name 进行代理 继续往下深挖
DefaultAdvisorAutoProxyCreator
@Bean
public MethodInterceptor hufAroundAdvise() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("方法执行前 : before ....");
Object proceed = invocation.proceed();
System.out.println("方法执行后 : after....");
return proceed;
}
};
}
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(MethodInterceptor hufAroundAdvise){
NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
nameMatchMethodPointcut.addMethodName("test"); //切入的方法名字
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(nameMatchMethodPointcut);//注入切点
defaultPointcutAdvisor.setAdvice(hufAroundAdvise);//注入切点逻辑
return defaultPointcutAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
return defaultAdvisorAutoProxyCreator;
}
通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的 PointCut和Advice信息,确定要代理的Bean以及代理逻辑.
这样 我们把所有本章节所有Bean注册进容器;包括JDK代理的Bean 也注册进去;
执行:
得到的执行结果:
这样 我们就进行了 更深层次的演化;
总结:
本章节一共讲解了
- CGLIB的实现
- JDK 动态代理的实现;
介绍了AOP 的 - Pointcut —切点
- Advice —通知
- Advisor —前面两个的结合;
介绍了实体类 - ProxyFactory —Spring使用的
- ProxyFactoryBean --注册进入Spring容器中的.生成单个的bean
- BeanNameAutoProxyCreator – 通过类名字 批量生产代理对象
- DefaultAdvisorAutoProxyCreator – 通过方法名字 批量生产代理对象
下个章节 将会对AOP 进行源码讲解; AOP本身不难. 所以它的源码 要比之前的文章的要简单; 并且有了这篇文章的一个铺垫; 可以说直接起飞;