反射
Java 反射,可以获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器、现实接口等,并且可以调用任意方法和实例化任意一个类的对象,通过反射我们可以实现动态装配,降低代码的耦合度、实现动态代理等,不过反射的过度使用会严重消耗系统资源。
- 反射的优缺点
- 可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
 - 缺点:对性能有影响,这类操作总是慢于直接执行java代码。
 
 - 如何解决反射的性能问题
 
- 聊到性能,记得想好怎么回答优化方面的问题,否则就是自己搬起石头砸自己的脚。
 - 
静态代理
想对方法进行一个增强, 需要修改源代码. 假如有100个方法, 我们需要修改100个方法的源代码来实现方法增强.
/*** 增删改查方法增强例子* @author : Shen Hanbo* @date : 2020/11/9 16:01*/public class CrudServiceImpl implements CrudService {public void insert() {System.out.println("增强新增前置处理");System.out.println("新增");System.out.println("增强新增后置处理");}public void delete() {System.out.println("增强删除前置处理");System.out.println("删除");System.out.println("增强删除后置处理");}public void update() {System.out.println("增强更新前置处理");System.out.println("更新");System.out.println("增强更新后置处理");}public void query() {System.out.println("增强查询前置处理");System.out.println("查询");System.out.println("增强查询后置处理");}//...假如下面还有一堆方法的话, 每个方法都需要去手动改源代码}
解决思路
生成一个代理类, 代理类要和被代理类拥有一样方法(实现同一个接口), 然后调用方直接调用代理类的方法, 而非被代理类的方法. 在代理类的实现方法中, 增强被代理类的方法, 然后客户端请求代理类. 代码
用房东/中介/租客
首先房东和中介都需要有”租房”这个方法, 中介来增强房东的”租房”方法,因此需要定义一个租房接口. ```java /**
- 租房接口
 - @author : Shen Hanbo
 @date : 2020/11/9 15:52 */ public interface ZuFang {
/**
- 房东和中介都需要实现租房接口,因为他们都有租房的功能 */ void zuFang();
 
}
2. 房东的"租房"方法, 即被代理的方法```java/*** 房东类* @author : Shen Hanbo* @date : 2020/11/9 15:54*/public class FangDong implements ZuFang {/*** 房东出租房子(被代理方法)*/public void zuFang() {System.out.println("房东出租房子");}}
中介的”租房”方法, 即代理方法, 在代理方法中对原方法进行增强 ```java /**
- 中介(代理类)
 - @author : Shen Hanbo
 @date : 2020/11/9 16:10 */ public class ZhongJie implements ZuFang {
/**
- 需要一个房东 */ private FangDong fangDong;
 
public ZhongJie(FangDong fangDong) { this.fangDong = fangDong; }
/**
- 中介代理房东出租房子(代理方法) */ public void zuFang() { //前置增强 System.out.println(“前置增强”); //原方法 fangDong.zuFang(); //后置增强 System.out.println(“后置增强”); } }
 
4. main```javapublic static void main(String[] args) {//房东FangDong fangDong = new FangDong();//中介代理房东ZhongJie zhongJie = new ZhongJie(fangDong);//调用中介的租房方法zhongJie.zuFang();}/*打印结果:前置增强房东出租房子后置增强*/
动态代理
- 痛点
- 静态代理需要手动编写代理类, 比如上个例子中的ZhongJie类.
 - 静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类, **每个接口都需要自己的代理类**.
 
 - 静态代理需要手动编写代理类, 比如上个例子中的ZhongJie类.
 - 解决思路
 
在上面的示例中**,一个代理只能代理一种类型,而且是在编译期就已经确定被代理的对象。
而动态代理是在运行时**,通过反射机制实现动态代理,并且能够代理各种类型的对象
使用InvocationHandler接口
//Object proxy:被代理的对象//Method method:要调用的方法//Object[] args:方法调用时所需要参数public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
Proxy类 ```java //用于获取代理类 //CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
3. 实现目前动态代理可以提供两种,分为- JDK 原生动态代理- 基于接口实现- 当目标类有接口的时候才会使用JDK动态代理,其实是因为JDK动态代理无法代理一个没有接口的类,因为JDK动态代理是利用反射机制生成一个实现代理接口的匿名类```javapublic interface Target {public String execute();}========================public class TargetImpl implements Target {@Overridepublic String execute() {System.out.println("TargetImpl execute!");return "execute";}}=======================public class DynamicProxyHandler implements InvocationHandler{private Target target;public DynamicProxyHandler(Target target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("========before==========");Object result = method.invoke(target,args);System.out.println("========after===========");return result;}}============================public class DynamicProxyTest {public static void main(String[] args) {Target target = new TargetImpl();DynamicProxyHandler handler = new DynamicProxyHandler(target);Target proxySubject = (Target) Proxy.newProxyInstance(TargetImpl.class.getClassLoader(),TargetImpl.class.getInterfaces(),handler);// 通过原来的方法名调用String result = proxySubject.execute(); // 返回被代理对象的方法返回值System.out.println(result);}}结果:========before==========TargetImpl execute!========after===========execute
- CGLIB动态代理
- 基于继承当前类的子类实现的
 - CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并且覆盖其中的方法。
 
 
Spring中默认使用的是JDK动态代理,除非目标类没有实现接口,才会转为CGLIB代理,如果想要强行使用CGLIB代理免责需要在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true" />,
然而在SpringBoot中,从2.0开始就默认使用CGLIB代理。
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用(当方法为final修饰时 无法实现代理),顺势织入横切逻辑。
- 目标类 是一个类而不是接口了 ```java package com.test.cglib;
 
public class Target {
public String execute() {String message = "-----------test------------";System.out.println(message);return message;}
}
2. 代理类代理对象的生成过程由Enhancer类实现,大概步骤如下:<br /> 1、生成代理类Class的二进制字节码;<br /> 2、通过Class.forName加载二进制字节码,生成Class对象;<br /> 3、通过反射机制获取实例构造,并初始化代理类对象。```javapackage com.test.cglib;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MyMethodInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println(">>>>MethodInterceptor start...");Object result = proxy.invokeSuper(obj,args);System.out.println(">>>>MethodInterceptor ending...");return "result";}}
- 测试类 ```java package com.test.cglib;
 
import net.sf.cglib.proxy.Enhancer;
public class CglibTest {
public static void main(String ... args) {System.out.println("***************");Target target = new Target();CglibTest test = new CglibTest();Target proxyTarget = (Target) test.createProxy(Target.class);String res = proxyTarget.execute();System.out.println(res);}/**代理对象的生成过程由Enhancer类实现,大概步骤如下:1、生成代理类Class的二进制字节码;2、通过Class.forName加载二进制字节码,生成Class对象;3、通过反射机制获取实例构造,并初始化代理类对象。
作者:冬天里的懒喵 链接:https://www.jianshu.com/p/37d0ac9233b9 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 **/
public Object createProxy(Class targetClass) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(targetClass);enhancer.setCallback(new MyMethodInterceptor());return enhancer.create();}
} ```
