赞
踩
代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型模式。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式分为静态代理和动态代理。
使用代理模式的主要目的是:保护目标对象;增强目标对象。
代理模式的通用写法
- interface ISubject{
- void resquest();
- }
-
- class RealSubject implements ISubject{
-
- @Override
- public void resquest() {
- System.out.println("real service is called.");
- }
- }
-
- class Proxy implements ISubject{
- private ISubject subject;
-
- public Proxy(ISubject subject){
- this.subject = subject;
- }
-
- @Override
- public void resquest() {
- before();
- subject.resquest();
- after();
- }
-
- public void before(){
- System.out.println("called before request().");
- }
-
- public void after(){
- System.out.println("called after request().");
- }
- }
-
- public class Test {
- public static void main(String[] args) {
- Proxy proxy = new Proxy(new RealSubject());
- proxy.resquest();
- }
- }
在Java生态中,目前最普遍使用的是JDK自带的代理和Cglib提供的类库。
首先,采用JDK的动态代理优化上述代码:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
- interface ISubject{
- void resquest();
- }
-
-
- class RealSubject implements ISubject{
-
- @Override
- public void resquest() {
- System.out.println("real service is called.");
- }
- }
-
- /**
- * JDK动态代理
- */
- class ProxyJDK implements InvocationHandler {
- private ISubject subject;
-
- public ISubject getInstance(ISubject subject){
- this.subject = subject;
- Class<?> clazz = subject.getClass();
- return (ISubject) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
- }
-
-
- public void before(){
- System.out.println("called before request().");
- }
-
- public void after(){
- System.out.println("called after request().");
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- before();
- Object result = method.invoke(this.subject, args);
- after();
- return result;
- }
- }
-
- public class Test {
- public static void main(String[] args) {
- ISubject instance = new ProxyJDK().getInstance(new RealSubject());
- instance.resquest();
- }
- }
手动实现JDK动态代理:
JDK 动态代理采用字节码重组,重新生成对象来替代原始对象,以达到动态代理的目的。JDK 动态代理生成对象的步骤如下:(JDK代理通过反射调用)
- /**
- * 自己实现JDK动态代理
- */
- interface GPInvocationHandler{
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- }
-
- /**
- * 用来生成源码的工具类
- */
- class GPProxy{
- public static final String ln = "\r\n";
-
- public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces, GPInvocationHandler h){
- try{
- //动态生成源代码.java文件
- String src = generateSrc(interfaces);
-
- //2、Java文件输出磁盘
- String filePath = URLDecoder.decode(GPProxy.class.getResource("").getPath(), "UTF-8");
- System.out.println("GpProxy filePath: "+filePath);
- File f = new File(filePath + "$Proxy0.java");
- System.out.println("GpProxy File: "+f);
- FileWriter fw = new FileWriter(f);
- fw.write(src);
- fw.flush();
- fw.close();
-
- //3、把生成的.java文件编译成.class文件
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
- Iterable iterable = manage.getJavaFileObjects(f);
-
- JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
- task.call();
- manage.close();
-
- //4、编译生成的.class文件加载到JVM中来
- Class proxyClass = classLoader.findClass("$Proxy0", interfaces);
- Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
- System.out.println("GpProxy Constructor: "+c );
- f.delete();
-
- //5、返回字节码重组以后的新的代理对象
- return c.newInstance(h);
- }catch (Exception e){
- e.printStackTrace();
- }
- return null;
- }
-
- private static String generateSrc(Class<?>[] interfaces){
- StringBuffer sb = new StringBuffer();
- sb.append("import java.lang.reflect.*;" + ln);
- sb.append("class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
- sb.append("GPInvocationHandler h;" + ln);
- sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
- sb.append("this.h = h;");
- sb.append("}" + ln);
-
- for (Method m : interfaces[0].getMethods()) {
- Class<?>[] params = m.getParameterTypes();
- StringBuffer paramNames = new StringBuffer();
- StringBuffer paramValues = new StringBuffer();
- StringBuffer paramClasses = new StringBuffer();
-
- for (int i = 0; i < params.length; i++) {
- Class clazz = params[i];
- String type = clazz.getName();
- String paramName = toLowerFirstCase(clazz.getSimpleName());
- paramNames.append(type + " " + paramName);
- paramValues.append(paramName);
- paramClasses.append(clazz.getName() + ".class");
- if (i > 0 && i < params.length - 1) {
- paramNames.append(",");
- paramValues.append(",");
- paramClasses.append(",");
- }
-
- }
- System.out.println("paramNames: "+paramNames);
- System.out.println("paramValues: "+paramValues);
- System.out.println("paramClasses: "+paramClasses);
-
- sb.append("public " + m.getReturnType().getName() + " " + m.getName()
- + "(" + paramNames.toString() + ") {" + ln);
- sb.append("try{" + ln);
- sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\""
- + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
- sb.append((hasReturnValue(m.getReturnType()) ? "return " : "")
- + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})", m.getReturnType())
- + ";" + ln);
- sb.append("}catch(Error _ex) { }");
- sb.append("catch(Throwable e){" + ln);
- sb.append("throw new UndeclaredThrowableException(e);" + ln);
- sb.append("}");
- sb.append(getReturnEmptyCode(m.getReturnType()));
- sb.append("}");
- }
- sb.append("}" + ln);
- return sb.toString();
- }
-
- private static Map<Class,Class> mappings = new HashMap<>();
- static{
- mappings.put(int.class,Integer.class);
- }
-
- private static String getReturnEmptyCode(Class<?> returnClass) {
- if (mappings.containsKey(returnClass)) {
- return "return 0;";
- } else if (returnClass == void.class) {
- return "";
- } else {
- return "return null;";
- }
- }
-
- private static String getCaseCode(String code, Class<?> returnClass) {
- if (mappings.containsKey(returnClass)) {
- return "((" + mappings.get(returnClass).getName() + ")" + code + ")."
- + returnClass.getSimpleName() + "Values()";
- }
- System.out.println("code: "+code);
- return code;
- }
-
- private static boolean hasReturnValue(Class<?> clazz) {
- return clazz != void.class;
- }
-
- private static String toLowerFirstCase(String src) {
- char[] chars = src.toCharArray();
- chars[0] += 32;
- return String.valueOf(chars);
- }
-
- }
-
-
- class GPClassLoader extends ClassLoader{
- private File classPathFile;
- public GPClassLoader(){
- String classPath = GPClassLoader.class.getResource("").getPath();
- this.classPathFile = new File(classPath);
- }
-
- protected Class<?> findClass(String name, Class<?>[] interfaces)throws ClassNotFoundException {
- String className;
- if(StringUtils.isNotBlank(GPClassLoader.class.getPackage().getName())){
- className = GPClassLoader.class.getPackage().getName() + "." + name;
- }else{
- className = name;
- }
- if(classPathFile != null){
- File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
- if(classFile.exists()){
- FileInputStream in = null;
- ByteArrayOutputStream out = null;
- try{
- in = new FileInputStream(classFile);
- out = new ByteArrayOutputStream();
- byte[] buff = new byte[1024];
- int len;
- while((len=in.read(buff)) != -1){
- out.write(buff,0,len);
- }
- return defineClass(className,out.toByteArray(),0,out.size());
- }catch (Exception e){
- e.printStackTrace();
- }finally {
- if(null != in){
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- if(null != out){
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
-
- return null;
- }
- }
-
- /**
- * JDK动态代理
- */
- class ProxyJDK2 implements GPInvocationHandler {
- private ISubject subject;
-
- public ISubject getInstance(ISubject subject){
- this.subject = subject;
- Class<?> clazz = subject.getClass();
- return (ISubject) GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
- }
-
-
- public void before(){
- System.out.println("called before request().");
- }
-
- public void after(){
- System.out.println("called after request().");
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- before();
- Object result = method.invoke(this.subject, args);
- after();
- return result;
- }
- }
-
- public class Test {
- public static void main(String[] args) {
- // ISubject instance = new ProxyJDK().getInstance(new RealSubject());
- // System.out.println(instance.getClass());
- // instance.resquest();
-
- ISubject instance2 = new ProxyJDK2().getInstance(new RealSubject());
- System.out.println(instance2.getClass());
- instance2.resquest();
- }
- }
注意:ISubject接口必须单独放一个文件里,不然会报错:class $Proxy0 cannot access its superinterface ISubject ($Proxy0 is in unnamed module of loader GPClassLoader @7d4793a8; ISubject is in unnamed module of loader 'app'),应该是保护域问题ProtectionDomain。反射机制的应用必须要求该类是public访问权限的。有朋友可能会说,可以调用setAccessible(true)方式来改变可见性,但这是一个概念混淆导致的错误。setAccessible(true)函数是反射机制用于改变类内属性访问权限的,而不是改变类本身的可见性。
CGlib代理的目标对象不需要实现任何接口,它通过动态继承目标对象实现动态代理。
- /**
- * CGLib创建动态代理
- * 这是由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了,为了兼容原先的版本,需要在运行项目时添加 --add-opens java.base/java.lang=ALL-UNNAMED 选项来开启这种默认不被允许的行为。
- */
-
- class ProxyCGLib implements MethodInterceptor {
- public Object getInstance(Class<?> clazz){
- Enhancer enhancer = new Enhancer();
- //要把设置为即将生成的新类的父类
- enhancer.setSuperclass(clazz);
- enhancer.setCallback(this);
-
- return enhancer.create();
- }
-
- @Override
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- before();
- Object obj = methodProxy.invokeSuper(o,objects);
- after();
- return obj;
- }
-
- public void before(){
- System.out.println("called before request().");
- }
-
- public void after(){
- System.out.println("called after request().");
- }
- }
- public class Test {
- public static void main(String[] args) {
- ISubject instance = (ISubject) new ProxyCGLib().getInstance(RealSubject.class);
- instance.resquest();
- }
- }
CGLib和JDK动态代理对比
优点:
缺点:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。