使用java语言,如何对一个类中的静态方法做切面编程?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用java语言,如何对一个类中的静态方法做切面编程?相关的知识,希望对你有一定的参考价值。

package com.classloader.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class AOPCallStaticMehtod 

private CallBack callBack;

public AOPCallStaticMehtod(CallBack callBack) 
this.callBack = callBack;


public static interface CallBack 
void before(Method method);

void after(Method method, Object result);


@SuppressWarnings( "unchecked", "rawtypes" )
public Object callMethod(Class clazz, String methodName, Class[] parameterTypes, Object[] parameters) 
Object result = null;
try 
Method method = null;
if (parameterTypes == null || parameterTypes.length == 0) 
method = clazz.getMethod(methodName);
if (Modifier.isStatic(method.getModifiers())) 
callBack.before(method);
result = method.invoke(null);
callBack.after(method, result);
else
System.out.println("这不是一个静态方法");

 else 
method = clazz.getMethod(methodName, parameterTypes);
if (Modifier.isStatic(method.getModifiers())) 
callBack.before(method);
result = method.invoke(null, parameters);
callBack.after(method, result);
else
System.out.println("这不是一个静态方法");


 catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) 
if (e instanceof NoSuchMethodException) 
System.out.println("没有这个方法");
 else 
System.out.println("call is error!");


return result;


public static void main(String[] args) 
CallBack callBack = new CallBack() 

@Override
public void before(Method method) 
if(method.getName().equals("test1") || method.getName().equals("test2"))
System.out.println(method.getName() + "方法在调用之前被拦截,可以在这里切面编程");



@Override
public void after(Method method, Object result) 
if(method.getName().equals("test1") || method.getName().equals("test2"))
System.out.println(method.getName() + "方法调用以后被拦截,可以在这里切面编程");
System.out.println(method.getName() + "执行结果是:" + result);
System.out.println("-----------------------------------------");



;
AOPCallStaticMehtod AOPCallStaticMehtod = new AOPCallStaticMehtod(callBack);
AOPCallStaticMehtod.callMethod(Test.class, "test1", new Class[]  String.class , new Object[]  "ppppppppppp" );
AOPCallStaticMehtod.callMethod(Test.class, "test2", null, null);



class Test 
public static void test1(String aa) 
System.out.println(aa);

public static String test2() 
System.out.println("fffffffffffffffff");
return "test2 result";

参考技术A 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
参考技术B 切面编程就是面向接口编程,好处是类似于usb这样的插拔操作,不需要这个功能是删除这个接口就行了,你可以写个接口,然后使用代理类,来实现丰富静态方法的编程 参考技术C 用的什么?Guice?Sprint?AspectJ?
一般静态方法不支持aop,aspectj好像可以,为什么要拦截静态方法?设计没问题吗?可以返回单例,然后调用非静态方法就可以拦截了,或者在非静态方法中调用静态方法,拦截非静态方法。
参考技术D 这个真不行(也许ApsectJ可以,但不是动态的):
切面编程基本原理是动态继承你的类,或者动态生成一个你接口的实现,在调用前后搞事情。
静态的没法继承的。

Spring中AOP的实现

Spring中整合了AOP的功能,虽然有不足,没有专门做AOP框架的那么完美,但是用一用感觉还是不错的

一些概念:

AOP 面向切面编程

aspect 切面/切面类(我个人认为一个真正被解耦的程序,切面类中的功能可以切入到 任何一个目标类中 无所谓是service层或者说是dao层中的类)

joinPoint 连接点

在spring的aop中只有 类中的方法 可以做连接点,每一个方法都可以是一个连接点.

pointCut 切入点 

一组连接点的集合

advice 通知/拦截器

用来控制切面类将来到底是织入到切入点的前面、后面或者是抛异常的时候。

adivsor 增强器
用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).

target 目标对象

proxy 代理对象

wave 织入

-----------------------

advice(通知)的类型:

前置通知(Before advice):
在某些连接点(join point)之前执行的通知

返回后通知(After returning advice):
在某些连接点(join point)正常完成后执行的通知(方法正常结束,没有异常)

抛出异常后通知(After throwing advice):
在某些连接点(join point)抛出异常退出时执行的通知

后通知(After (finally) advice):
当某些连接点(join point)退出的时候执行的通知

环绕通知(Around Advice):
包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交,以及方法抛出异常时候回滚

注:在spring中,连接点(join point)指的就是方法

Spring中提供了AOP的实现方式 在XMl中配置只需要三步就可以实现

1.配置目标类
2.配置拦截器(配置切面类【可选】)
3.配置代理对象

配置xml文件:

1.首先是配置目标类

<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>

<!-- 配置一个测试账户 -->
<bean name="account" class="com.briup.aop.pojo.Account">
<property name="id" value="1"></property>    
<property name="name" value="tom"></property>    
<property name="balance" value="1000"></property>    
</bean>

<!-- 配置目标对象 -->
<bean name="target" 
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
<property name="account" ref="account"></property>
</bean>

2.配置拦截器

<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
<!-- 配置advice -->
<bean name="beforeAdvice" class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>

 

 

3.配置代理对象

<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy" 
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>

<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<!-- 注入代理类需要实现的接口 代理类实现接口与目标类实现接口相同 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>

<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
</list>
</property>

</bean>

 

这样配置有三个问题:
1.这个目标类对应接口下的方法都被加入了功能
2.需要为每一个目标类进行配置
3.目标类必须实现接口

首先我们解决第一个问题
我们的Spring中提供了advisor(增强器),增强器包装了一下advice(拦截器),可以筛选向接口中的那个方法添加功能

1.首先是配置目标类

<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>

<!-- 配置一个测试账户 -->
<bean name="account" class="com.briup.aop.pojo.Account">
<property name="id" value="1"></property>    
<property name="name" value="tom"></property>    
<property name="balance" value="1000"></property>    
</bean>
<!-- 配置目标对象 -->
<bean name="target" 
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
<property name="account" ref="account"></property>
</bean>

2.配置拦截器

<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>

<!-- 配置advice -->
<bean name="beforeAdvice" class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>

 

3.添加增强器

<!-- 配置advisor 增强器-->
<!-- 作用:筛选要拦截(要代理)的方法 -->
<bean name="advisor" 
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*bankAction</value> <!--单个字符出现0到多次-->
</list>
</property>
</bean>
<!--RegexpMethodPointcutAdvisor默认的正则表达式选择方法-->
<!--NameMatchMethodPointcutAdvisor按照方法名匹配-->

 

4.配置代理对象

<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy" 
class="org.springframework.aop.framework.ProxyFactoryBean">

<!-- 注入目标对象 -->
<property name="target" ref="target"></property>

<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<!-- 注入代理类需要实现的接口 代理类实现接口与目标类实现接口相同 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>

<!-- 注入advice/advisor 可以有多个 -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>

 

我们处理了第一个问题,被处理的方法可以指定

我们开始解决第二个问题
自动的进行配置:自动代理:DefaultAdvisorAutoProxyCreator类的使用

xml配置文件:

1.配置拦截器

<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>

<!-- 配置advice -->
<bean name="beforeAdvice" 
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>

 

2.添加增强器

<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>

配置目标对象,这里的对象中只要有增强器中的方法,就可以当作代理对象,Cgilb代理默认的将含有增强器中的方法的
所有对象都进行了处理,比如以下这个target对象,从容器中拿出后就是加入了功能的了,但是这些对象得要放在一个容器下面

在Spring中最需要注意的就是对象在不在容器中,对象不在容器中就没办法受到Spring框架的控制了

<bean name="target" 
class="com.briup.aop.service.AccountServiceImpl">
</bean>

 

3.实现自动代理

<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxy -->
<!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
<bean name="proxy" 
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>

 

这样做就可以自动的对需要功能的方法进行代理处理了,也不用实现接口了

使用自动代理的时候需要注意的方面:
1.当前的配置里面"一定要有"一个advisor的配置
2.不需要向自动代理类中注入任何信息
3.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象(CGLib的方式).
4.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。
5.spring如何确定配置文件中哪个bean是作为目标对象:
通过advisor中筛选的方法,如果这个bean中含有advisor中所配置的方法,则这个bean将来称为我们的目标对象进行代理

但是又有问题来了:
我们对所有含有同名方法的类对象进行了处理,如果想为指定类对象中的方法进行处理呢?

Spring同样进行了处理:

通过名字进行自动代理:BeanNameAutoProxyCreator类的使用

xml配置文件:

1.配置拦截器

<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice" 
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>

2.添加增强器

<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>

 

<!-- 配置目标对象 -->

<bean name="target" class="com.briup.aop.service.AccountServiceImpl"> </bean>

<bean name="target2" class="com.briup.aop.service.AccountServiceImpl"> </bean>

<bean name="target3" class="com.briup.aop.service.AccountServiceImpl"></bean>

3.实现自动代理

<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxybyname -->
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的对象名字 -->
<property name="beanNames">
<list>
<value>target</value>
<value>target2</value>
<value>dao</value>
<value>service*</value>
</list>
</property>

<!-- 注入advice或者advisor -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>

 


使用byName自动代理的时候需要注意的方面:
1.当前的配置里面"有没有"advisor的配置"都没关系"
有advisor可以精确选择加强功能的方法,没有就是类中的所有方法了

2.需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
就是说advisor中指定的方法在 被代理目标对象 中存在

3.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象.

4.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。
这一点在DefaultAdvisorAutoProxyCreator的使用中就说过了
可以说 目标对象加入了功能,变成了代理对象

Spring中封装了AspectJ
所以我们也可以使用到这种框架的功能了,Spring中提供了aop:config标签,使用aop的专用标签来完成相关的配置.这个标签我也是正在研究,据说挺好用的?。

 

以上是关于使用java语言,如何对一个类中的静态方法做切面编程?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA如何获取嵌套在静态LIST类中的静态LIST类

java 多线程3:Thread类中的静态方法

如何调用内部类中的静态方法

Java多线程3:Thread中的静态方法

C++ 静态方法(在不同的类中)(如 Java 的)

如何对Java单例模式进行mock