写在前面

  Spring将introduction通知看作一种特殊类型的拦截通知。用Spring的行话来讲,对方法的增强叫做Wearing(织入),而对类的增强叫introduction(引入)。Introduction Advice(引入增强)就是对类的功能增强,它也是Spring AOP 提供的一种特殊增强。

一个简单的例子(以CGLIB的代理实现)

  定义一个接口(这个例子演示了一个mixin。我们想要能够 将被通知对象类型转换为Lockable,不管它们的类型,并且调用lock和unlock方法。如果我们调用 lock()方法,我们希望所有setter方法抛出LockedException异常。 这样我们能添加一个方面使的对象不可变,而它们不需要知道这一点:这是一个很好的AOP例 子。)

public interface Lockable {
void lock();
void unlock();
boolean locked();
}

  首先,我们需要一个做大量转化的IntroductionInterceptor。 在这里,我们继承 org.springframework.aop.support.DelegatingIntroductionInterceptor 实用类。我们可以直接实现IntroductionInterceptor接口,但是大多数情况下 DelegatingIntroductionInterceptor是最合适的。

  这样,LockMixin继承DelegatingIntroductionInterceptor 并自己实现Lockable。父类自动选择支持导入的Lockable,所以我们不需要指定它。 用这种方法我们可以导入任意数量的接口。

public class LockMixin extends DelegatingIntroductionInterceptor
implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
throw new LockedException();
return super.invoke(invocation);
}
}

  通常不要需要改写invoke()方法:实现 DelegatingIntroductionInterceptor就足够了,如果是导入的方法, DelegatingIntroductionInterceptor实现会调用委托方法, 否则继续沿着连接点处理。在现在的情况下,我们需要添加一个检查:在上锁 状态下不能调用setter方法。

  在spring中的配置如下

    <bean id="aServiceImpl" class="com.xxx.plus.aop.demo.AServiceImpl" />
<bean id="lockAdvice" class="com.xxx.plus.aop.demo.LockMixin" />
<bean id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.xxx.plus.aop.demo.Lockable" />
<property name="target" ref="aServiceImpl" />
<property name="proxyTargetClass" value="true"></property>
<property name="interceptorNames">
<list>
<value>lockAdvice</value>
</list>
</property>
</bean>

客户的代码代用如下

ApplicationContext context =new FileSystemXmlApplicationContext( "beans-config.xml");
AService aServiceProxy =(AService) context.getBean("proxyFactoryBean");
// 物件没有被锁定,可以呼叫 set 方法 some.setSome("justin"); System.out.println(some.getSome());
try {
// (重点一)
((LockAble) aServiceProxy).lock();
// 无法呼叫 set 方法,丢出例外
aServiceProxy.setSome("momor");
// 由于会丢出例外,所以下面的这行程式无法被执行
System.out.println(aServiceProxy.getSome());
}
catch(Throwable e) {
e.printStackTrace();
}
some.setSome("momor");
     System.out.println(aServiceProxy.getSome());

  重点一:这里的lock()方法可以直接被aServiceProxy调用,这里将aServiceProxy由AService强转为LockAble 没有报错。原理是在生产代理的时候,代理对象动态的实现了LockAble 接口。

    public Object getProxy(ClassLoader classLoader) {
......
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
       //重点1.1(将advised 的接口也给了代理对象,获得完整的代理接口)
       enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class)); Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
      .......
}

    重点1.1(所以上面的强转是不会报错的)

    public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
     //获取所有的代理接口
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
}
if (addAdvised) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
if (addSpringProxy) {
proxiedInterfaces[specifiedInterfaces.length] = SpringProxy.class;
}
if (addAdvised) {
proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;
}
return proxiedInterfaces;
}

  客户的的代码

     aService.barA();
    //h会发现这里的强制转换时成功的
  Lockable ku = ((Lockable) aService);
   ku.lock();

  输出结果:

AServiceImpl.barA()
is lock

引入增强(JDK 代理实现)

接口与实现类,引入增强类

public interface ISome {
public void doSome();
}
public class Some implements ISome {
public void doSome() {
System.out.println("原来物件的职责。。。");
}
}
public class OtherIntroduction extends DelegatingIntroductionInterceptor implements IOther {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return super.invoke(methodInvocation);
}
public void doOther() {
System.out.println("增加的职责。。。");
}
}

  配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="some" class="com.xxx.Some"/>
<bean id="otherIntroduction" class="com.xxx.OtherIntroduction"/>
<bean id="otherAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg index="0">
<ref bean="otherIntroduction"/>
</constructor-arg>
<constructor-arg index="1">
<value>onlyfun.caterpillar.IOther</value>
</constructor-arg>
</bean>
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.xxx.ISome</value>
</property>
<property name="target">
<ref bean="some"/>
</property>
<property name="interceptorNames">
<list>
<value>otherAdvisor</value>
</list>
</property>
</bean>
</beans>

测试代码

    public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:beans-config.xml");
ISome some = (ISome) context.getBean("proxyFactoryBean");
some.doSome();
// 看来好像 some 物件动态增加了职责
((IOther) some).doOther();
}

运行结果

原来物件的职责。。。
增加的职责。。。

分析:JDK 在代理接口的时候动态的代理了增强类实现的接口

    @Override
public Object getProxy(ClassLoader classLoader) {
    //重点1 这里获取的所有完整的代理接口
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

这里其实和CGLIB 代理是一样的。获取所有的需要代理的接口

    public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
}
if (addAdvised) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
if (addSpringProxy) {
proxiedInterfaces[specifiedInterfaces.length] = SpringProxy.class;
}
if (addAdvised) {
proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;
}
return proxiedInterfaces;
}

然后动态的实现接口,这样就可以实现强转了

spring---aop(9)---Spring AOP中引入增强的更多相关文章

  1. Spring MVC:在jsp中引入css

    为了将css引入jsp中,今天可真是踩了好多坑,最后在stackoverflow上找到了解决方法,不多说贴出代码. 在web.xml中添加以下代码: <servlet-mapping> & ...

  2. 【spring 5】AOP:spring中对于AOP的的实现

    在前两篇博客中,介绍了AOP实现的基础:静态代理和动态代理,这篇博客介绍spring中AOP的实现. 一.采用Annotation方式 首先引入jar包:aspectjrt.jar && ...

  3. Spring AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)

    1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下让你不知所措,心想着:管不得很多人都和我说AOP多难多难.当我看进去以后, ...

  4. Spring AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)(转)

    1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下让你不知所措,心想着:管不得很多人都和我说AOP多难多难.当我看进去以后, ...

  5. Spring 梳理 - AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)

    Spring  AOP那些学术概念—通知.增强处理连接点(JoinPoint)切面(Aspect)   1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是 ...

  6. AOP 与 Spring中AOP使用(下)

    AOP通知类型 前置通知 在目标方法执行之前进行操作 UserDao.java public class UserDao { public void add(){ System.out.println ...

  7. AOP 与 Spring中AOP使用(上)

    AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续 ...

  8. JavaWeb_(Spring框架)认识Spring中的aop

    1.aop思想介绍(面向切面编程):将纵向重复代码,横向抽取解决,简称:横切 2.Spring中的aop:无需我们自己写动态代理的代码,spring可以将容器中管理对象生成动态代理对象,前提是我们对他 ...

  9. Spring配置AOP实现定义切入点和织入增强

    XML里的id=””记得全小写 经过AOP的配置后,可以切入日志功能.访问切入.事务管理.性能监测等功能. 首先实现这个织入增强需要的jar包,除了常用的 com.springsource.org.a ...

随机推荐

  1. Django中cookie和session

    cookie Cookie的由来 大家都知道HTTP协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不 ...

  2. 从一个局长使用BS系统的无奈看测试点

    今天我点名买了个B/S系统,听说只要有浏览器就能用.我最讨厌装客户端了,用浏览器就是方便啊. 下面就是我使用这个系统碰到的麻烦事: 我登录失败的时候没有任何提示,这没什么,反正提示也只是说失败…… 进 ...

  3. vue系列之项目优化

    webpack中的Code Splitting Code Splitting是什么以及为什么 在以前,为了减少HTTP请求,通常地,我们会把所有的代码都打包成一个单独的JS文件,但是,如果这个文件体积 ...

  4. 字体格式类型(.eot/.otf/.woff/.svg)

    @font-face语句是css中的一个功能模块,用于实现网页字体多样性的模块(设计者可随意指定字体,不需要考虑浏览者电脑上是否安装). @font-face文件 而由于网页中使用的字体类型,也是各浏 ...

  5. git —— 分支冲突

    解决冲突 冲突需手动解决 $ git status 查看冲突的文件 <<<<<<<,=======,>>>>>>> ...

  6. jQuery选择器详解及实例---《转载》

    选择器是jQuery最基础的东西,本文中列举的选择器基本上囊括了所有的jQuery选择器,也许各位通过这篇文章能够加深对jQuery选择器的理解,它们本身用法就非常简单,我更希望的是它能够提升个人编写 ...

  7. oralce 笔记

    查某一表的行数 select max(rownum) from tablename 插入数据之前判断是否重复 insert into tablename (coloum1,coloum2) selec ...

  8. 对于ElasticSearch与Hadoop是如何互相调用的?

    1.在HDFS中,数据是以文件形式保存的,比如JSON: https://blog.csdn.net/napoay/article/details/68945483 2.python读写HDFS,一般 ...

  9. CF293B Distinct Paths题解

    CF293B Distinct Paths 题意 给定一个\(n\times m\)的矩形色板,有kk种不同的颜料,有些格子已经填上了某种颜色,现在需要将其他格子也填上颜色,使得从左上角到右下角的任意 ...

  10. Java之反转排序

    顾名思义,反转排序就是以相反的顺序把原来的数组内容重新进行排序.反转排序算法在我们的程序开发中也是经常用到的.而反转排序的基本思想也很简单,就是把数组最后一个元素与第一个元素进行交换,倒数第二个与第二 ...