上一章已经讲到了想要完成AOP的设计原理以及要实现AOP功能,得需要使用代理模式:
本章就介绍一个实现动态代理的两种方式之一——JDK中的Proxy技术
AOP实现原理(使用JDK中的Proxy技术实现AOP功能,InvocationHandler和Proxy(Class)详解)
AOP技术在企业开发中或多或少都会用到,但用的最多的大概就是做权限系统时,判断用户是否是有权限,有权限就执行该方法,没有权限就不能执行该方法,本章就以此为案例
现有如下需求
用户在执行某个业务方法时我们需要对这个用户进行判断是否具有权限。
例如实现用户的增删改查操作,在不使用AOP的情况下,在执行方法之前要判断用户user是否为null(表示没有权限或者没有登录),而一旦我们的需求发生改变,就需要再次修改代码进行判断,这种开发导致修改代码频繁代码不易维护。AOP就实现了代码的可插拔性(AOP第一章介绍过)
为了完成上面的需求,我们可以使用横行扩展的方式给目标业务层添加代理对象。
代理模式的代理角色最起码要考虑三个阶段:
- 在调用真正对象的方法之前,应该需要做什么?
- 在调用真正对象的方法过程中,如果抛出了异常,需要做什么?
- 在调用真正对象的方法后,返回了结果了,需要做什么?
JDK的Proxy技术
在使用Proxy代理对象的时候,对需要代理的对象有一些要求,目标对象必须是实现了接口,是面向接口编程的。(其实就是去实现目标对象实现的所有接口)
新建一个项目spring_aop(java项目或web项目均可),在com.oak.service中创建UserService接口,然后创建UserServiceImpl实现它
UserService如下:
public interface UserService {
void add(String name);
void delete(int id);
}
UserServiceImpl:
public class UserServiceImpl implements UserService{
//假设有该user是个User对象
private String user=null;
public String getUser(){
return user;
}
@Override
public void add(String name) {
System.out.println("我要增加");
}
@Override
public void delete(int id) {
System.out.println("我要删除");
}
}
如果按照我们之前的处理方式,就是下面这种方式:
public class UserServiceImpl implements UserService{
//假设有该user是个User对象
private String user=null;
public String getUser(){
return user;
}
public UserServiceImpl(){
}
public UserServiceImpl(String user){
this.user=user;
}
@Override
public void add(String name) {
if(user!=null){
System.out.println("我要增加");
}else{
System.out.println("没有权限");
}
}
@Override
public void delete(int id) {
if(user!=null){
System.out.println("我要删除");
}else{
System.out.println("没有权限");
}
}
}
很明显这种方式复杂并且维护性较差
现在我们就使用JDK中的Proxy技术来生成UserServiceBean对象的代理对象,但要记住在Java里面有这样一个限定:要想创建某一个对象的代理对象,那么该对象必须实现一个接口。
在com.oak.aop包下新建一个类JDKProxyFactory实现InvocationHandler接口:
public class JDKProxyFactory implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
然后我们来完成这个代理对象类的编写,需要用来以下两个东西:
Proxy类
我们先来看看Proxy这个类,它的作用就是用来动态创建一个代理对象,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
Proxy类的newProxyInstance静态方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
如果不是很明白,继续我们的案例来讲解
把刚才创建好的JDKProxyFactory进行完善,先创建一个成员变量代表要代理的目标对象(那个真实对象),再创建一个实例工厂方法使用 Proxy类的newProxyInstance方法返回一个一个动态代理对象。
代码如下:
public class JDKProxyFactory implements InvocationHandler{
//代理的目标对象
private Object targetObject;
//实例工厂返回返回一个目标对象的代理对象
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
/**
* 第一个参数设置代码使用的类装载器,一般采用跟目标相同的类装载器
* 第二个参数设置代理类实现的接口,所以要求代理的目标对象必须实现一个类
* 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
*/
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader()
,this.targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
InvocationHandler接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
InvocationHandler这个接口的唯一一个方法 invoke 方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数:
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
如果不是很明白,没关系,我们继续案例讲解,
在JDKProxyFactory中完善invoke方法,可以在其中判断权限,根据权限来判断真实对象的方法执不执行,也可以真实方法调用之前指定一段代码,可以在发生异常时指定代码,也可以在之后指定一段代码:
public class JDKProxyFactory implements InvocationHandler{
//代理的目标对象
private Object targetObject;
//实例工厂返回返回一个目标对象的代理对象
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
/**
* 第一个参数设置代码使用的类装载器,一般采用跟目标相同的类装载器
* 第二个参数设置代理类实现的接口,所以要求代理的目标对象必须实现一个类
* 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
*/
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader()
,this.targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
UserServiceImpl service=(UserServiceImpl) this.targetObject;
Object result=null;
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("before service方法执行...");
if(service.getUser()!=null){//判断权限
// 把方法调用委派给目标对象
result=method.invoke(targetObject, args);
}
//在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after service方法执行...");
return result;
}
}
新建测试类:
测试1,使用无参构造创建UserServiceImpl对象,然后调用UserService的add方法
@Test
public void test01(){
UserService userService=(UserService) new JDKProxyFactory()
.createProxyInstance(new UserServiceImpl());
userService.add("哈哈");
}
user为null,没有权限,实际上add方法没有执行,控制台输出:
before service方法执行...
after service方法执行...
测试2,使用有参构造创建UserServiceImpl对象:
@Test
public void test02(){
UserService userService=(UserService) new JDKProxyFactory()
.createProxyInstance(new UserServiceImpl("admin"));
userService.add("哈哈");
}
user为admin,有权限,add方法被调用执行,控制台输出:
before service方法执行...
我要增加
after service方法执行...
在实际的开发中我们的目标对象可能没有实现接口,那么我们就不能使用JDK的动态代理,我们可以使用第三方的实现方式(CGLIB),下一章: