小张接到一个需要,需要对用户的增删改查每次操作都进行行安全检查,防止不安全的操作破坏用户的敏感信息。
小张之前听说过面向切面编程(aop),而这个需求很贴合面向切面编程,首先肯定不能在用户管理类(userManagerImpl)里增加代码,这会破坏和并且增加了用户管理类的复杂度。
可以这样理解切面,我们的每个操作线程都是一个水管,我们之前写代码都是在一个水管里写代码横向的,但是实际会有很多操作和很多线程,如果要做一件事,我们需要往每个水管代码,但是这很不可取,我们可以纵向写代码,对很多的水管的一个面写代码。
我们来看一下代理模式的概念:
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
我们现在有一个UserManager接口有四个方法分别对应增删改查,我们需要对这四个方法的访问进行安全控制。
UserManager接口:
package xuelongjiang.designpartten.proxy;
/**
*
* 只是示例,所以很多方法没有参数,
* 如果是业务开发,请根据实际业务确定方法参数
*
* @Author xuelongjiang
*/
public interface UserManager {
public void addUsr();
public void updateUser();
public void insert();
public void delete();
}
UserManager的实现类UserManagerImpl:
package xuelongjiang.designpartten.proxy;
import xuelongjiang.designpartten.proxy.UserManager;
import java.io.Serializable;
/**
* @Author xuelongjiang
*/
public class UserManagerImpl implements UserManager {
@Override
public void addUsr() {
System.out.println("---add user ---");
}
@Override
public void updateUser() {
System.out.println("---update user -----");
}
@Override
public void insert() {
System.out.println("----insert user -----");
}
@Override
public void delete() {
System.out.println("---- delete user -------");
}
}
如果不想破坏UserManagerImpl的内部实现,又想对四个方法进行权限控制,那么有一种方式,我们可以创建一个类,用来代理UserManagerImple
类图:
UserManagerImpleProxy代理类:
package xuelongjiang.designpartten.proxy.staticProxy;
import xuelongjiang.designpartten.proxy.UserManager;
/**
* @Author xuelongjiang
*/
public class UserManagerImplePorxy implements UserManager {
private UserManager userManager;
public UserManagerImplePorxy(UserManager userManager) {
this.userManager = userManager;
}
public void checkSecurity(){
System.out.println("----checkSecurity ---");
}
@Override
public void addUsr() {
checkSecurity();
userManager.addUsr();
}
@Override
public void updateUser() {
checkSecurity();
userManager.updateUser();
}
@Override
public void insert() {
checkSecurity();
userManager.insert();
}
@Override
public void delete() {
checkSecurity();
userManager.delete();
}
}
测试类:
package xuelongjiang.designpartten.proxy.staticProxy;
import xuelongjiang.designpartten.proxy.UserManager;
import xuelongjiang.designpartten.proxy.UserManagerImpl;
/**
* @Author xuelongjiang
*/
public class ClientTest {
public static void main(String[] args) {
UserManager userManager = new UserManagerImplePorxy(new UserManagerImpl());
userManager.addUsr();
userManager.insert();
userManager.updateUser();
userManager.delete();
}
}
上面这种方式叫做静态代理,原理很简单,我们创建一个类实现 Usermanager接口,重写四个方法,增加安全检查。回想一下上面的切面和水管图,如果使用静态代理那么如果每一个水管是一个类,那么我们需要创建同等数量的代理类,这样的工作量,作为程序员我们是希望尽量避免的。
如果一个切面,只想写一个类呢?该怎么做呢?我们可以使用动态代理。
动态代理有两种方式:Java jdk提供的方式和spring cglib提供的的方式。
两者的区别是:
类图:
动态代理类:
package xuelongjiang.designpartten.proxy.dynamicProxy;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* 动态代理
*
* @Author xuelongjiang
*/
public class SecurityHanlder implements InvocationHandler {
private Object targetObject;
public SecurityHanlder() {
}
public Object newProxy(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
private void checkSecurity(){
System.out.println("----checkSecurity---");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
checkSecurity();
Object ret = null;
try {
//调用被代理目标对象的真实方法
ret = method.invoke(this.targetObject,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return ret;
}
}
测试类:
package xuelongjiang.designpartten.proxy.dynamicProxy;
import sun.misc.ProxyGenerator;
import xuelongjiang.designpartten.proxy.UserManager;
import xuelongjiang.designpartten.proxy.UserManagerImpl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Modifier;
/**
* @Author xuelongjiang
*/
public class ClientTest {
public static void main(String[] args)throws Exception {
SecurityHanlder securityHanlder = new SecurityHanlder();
//jdk代理对象必须是有接口,且代理返回接口,取消注释,运行失败
//使用cglib,对象不必有接口
//UserManagerImpl userManager = (UserManagerImpl) securityHanlder.newProxy(new UserManagerImpl());
UserManager userManager = (UserManager) securityHanlder.newProxy(new UserManagerImpl());
//输出生成的代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"xuelongjiang.designpartten.proxy.dynamicProxy.$proxy0.class", new Class[]{UserManager.class}, Modifier.FINAL);
// 输出文件到本地
FileOutputStream out = new FileOutputStream(new File("/Users/xuelongjiang/Documents/$Proxy0.class"));
out.write(proxyClassFile);
out.flush();
out.close();
out.close();
//outputStream.close();
//objectOutputStream.close();
userManager.delete();
}
}
测试类中有一段代码是输出生成的代理类,我们会在后面展开。
cglib是spring提供的动态代理方法,我在spring中调试代码经常会看见生成的代理类。
类图:
代理类:
实现MethodInterceptor接口,重写intercept 方法
package xuelongjiang.designpartten.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author xuelongjiang
*/
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
public Object newProxy(Object targetObject){
//创建代理对象
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
private void checkSecurity(){
System.out.println("---checkSecurity---");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
checkSecurity();
Object ret = null;
try {
method.invoke(this.targetObject,objects);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return ret;
}
}
测试类:
package xuelongjiang.designpartten.proxy.cglib;
import sun.misc.ProxyGenerator;
import xuelongjiang.designpartten.proxy.UserManager;
import xuelongjiang.designpartten.proxy.UserManagerImpl;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Modifier;
/**
* @Author xuelongjiang
*/
public class ClientTest {
public static void main(String[] args) throws Exception {
//cglib可以代理不实现接口的类
CglibProxy cglibProxy = new CglibProxy();
UserManagerImpl userManager = (UserManagerImpl) cglibProxy.newProxy(new UserManagerImpl());
userManager.addUsr();
}
}
我们的代理类代码主要两部分:
public Object newProxy(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
invoke是我们的真正执行的方法,使用反射出要执行的方法并且执行我们的功能代码。
还记得我们在jdk动态代理的测试代码中有一段输出代理的代码吗?
jdk代理的思路是:生成代理类,并且代理类会为被代理类中的每个方法生成代理方法,之后调用生成的代理类。
我们反编译了生成的字节码:
我们可以看到,每个方法的执行变成了:
h就是实现InvocationHandler接口的类。
super.h.invoke(this, m4, (Object[])null);
并且:
final class class extends Proxy implements UserManager {}
代理的类继承自proxy,所以jdk动态代理只能代理实现某个接口的类
(Java的单继承性)。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package xuelongjiang.designpartten.proxy.dynamicProxy.$proxy0;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import xuelongjiang.designpartten.proxy.UserManager;
final class class extends Proxy implements UserManager {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m6;
private static Method m0;
private static Method m5;
public class(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void insert() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void delete() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void addUsr() throws {
try {
super.h.invoke(this, m6, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void updateUser() throws {
try {
super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("xuelongjiang.designpartten.proxy.UserManager").getMethod("insert");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("xuelongjiang.designpartten.proxy.UserManager").getMethod("delete");
m6 = Class.forName("xuelongjiang.designpartten.proxy.UserManager").getMethod("addUsr");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m5 = Class.forName("xuelongjiang.designpartten.proxy.UserManager").getMethod("updateUser");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
jdk代理测试代码中输出代理,我们是怎么知道类的全限定名的?
进入proxy类,我们可以看到:
private static final String proxyClassNamePrefix = "$Proxy";
private static final AtomicLong nextUniqueNumber = new AtomicLong();
long num = nextUniqueNumber.getAndIncrement();
我们得知生成的代理类名。