1.解决大量共同业务的处理
2.解决jdk动态代理中实现类注解获取不到的困扰
3.使用业务链条模式进行前切和后切,可以做到用户自己随意对业务进行增减少
如果想要与spring整合,可以使用factory-method来进行代理对象的生成。
里面有之前一些博客得练习
csdn代码地址 https://code.csdn.net/w172087242/littlehow/tree/master
代码maven依赖:低版本的也可以,这里主要是使用spring集成的cglib包,所以需要引入spring-core
junit
junit
4.10
org.springframework
spring-core
4.3.3.RELEASE
jdk动态父类:处理实际的代理业务,以及缓存部分信息
package littlehow.proxy;
import littlehow.proxy.anno.AfterChain;
import littlehow.proxy.anno.BeforeChain;
import littlehow.proxy.anno.Cut;
import littlehow.proxy.chain.Chain;
import littlehow.proxy.chain.Context;
import littlehow.proxy.chain.ContextManager;
import littlehow.proxy.chain.EndChain;
import littlehow.proxy.enm.CutType;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* ProxyParant
*
* @author littlehow
* @time 2016-09-26 13:52
*/
public class ProxyParant {
protected T instance;
//jdk动态代理时获取不到类中方法的注解
protected HashMap jdkBeanMethods = new HashMap<>();
//前切业务链
protected ConcurrentHashMap beforeChains = new ConcurrentHashMap<>();
//后切业务链
protected ConcurrentHashMap afterChains = new ConcurrentHashMap<>();
//所有业务的终点
private static final EndChain end = new EndChain();
/**
* 执行实际方法
* @param method
* @param arguments
* @return
* @throws Throwable -- 可能是切入时发生异常,也可能是执行方法发生异常,这里就不再进行处理了
*/
public Object executeMethod(Method method, Object[] arguments) throws Throwable{
String key = method.toString();
//查询bean的方法,可能为null
Method beanMethod = jdkBeanMethods.get(key);
//切入方案
Cut cut = null;
//判断是否进行前切
if(method.isAnnotationPresent(Cut.class)) {//优先从顶层取
cut = method.getAnnotation(Cut.class);
} else if(beanMethod != null && beanMethod.isAnnotationPresent(Cut.class)) {
cut = beanMethod.getAnnotation(Cut.class);
}
if(cut == null) {
return method.invoke(instance, arguments);
}
//设置上下文
this.setContext(method, arguments, beanMethod);
if((cut.value().getValue() & CutType.BEFORE.getValue()) != 0) {//进行前切
this.before(method, arguments, beanMethod);
}
//执行业务
Object result = method.invoke(instance, arguments);
if((cut.value().getValue() & CutType.AFTER.getValue()) != 0) {//进行后切
this.after(method, arguments, beanMethod);
}
//释放一次上下文
end.invoke();
return result;
}
/**
* @param main
* @param jdkClass -- jdk动态代理造成的接口和实现都出现chain的情况
* @throws Throwable
*/
private Chain getChain(Class extends Chain>[] main, Class extends Chain>[] jdkClass) throws Throwable{
Chain current = null;
Chain returnValue = null;
if(main != null && main.length > 0) {
current = main[0].newInstance();
returnValue = current;
for(int i=1,len=main.length; i < len; i++) {
Chain chain = main[i].newInstance();
current.setNext(chain);
current = chain;
}
}
if(jdkClass != null && jdkClass.length > 0) {
int index = 0;
if(current == null) {
current = jdkClass[0].newInstance();
returnValue = current;
index ++;
}
for(int i=index,len=jdkClass.length; i < len; i++) {
Chain chain = jdkClass[i].newInstance();
current.setNext(chain);
current = chain;
}
}
//current.setNext(end);
return returnValue;
}
/**
* 设置当前上下文
* @param method
* @param arguments
* @param beanMethod
*/
private void setContext(Method method, Object[] arguments, Method beanMethod) {
//设置上下文
Context context = new Context(beanMethod == null ? method : beanMethod, arguments);
ContextManager.set(context);
}
/**
* 前切业务
* @param method
* @param arguments
* @param beanMethod -- jdk代理中的beanMethod
*/
private void before(Method method, Object[] arguments, Method beanMethod) throws Throwable{
Chain chainObj = beforeChains.get(method);
if(chainObj == null) {
Class extends Chain>[] main = null;
Class extends Chain>[] jdkClass = null;
if(method.isAnnotationPresent(BeforeChain.class)) {//如果有
main = method.getAnnotation(BeforeChain.class).value();
}
if(beanMethod != null && beanMethod.isAnnotationPresent(BeforeChain.class)) {
jdkClass = beanMethod.getAnnotation(BeforeChain.class).value();
}
chainObj = getChain(main, jdkClass);
if(chainObj != null)
beforeChains.put(method, chainObj);
}
if(chainObj != null) {//拥有业务链条
chainObj.execute();
}
}
/**
* 后继业务
* @param method
* @param arguments
* @param beanMethod -- jdk代理中的beanMethod
*/
private void after(Method method, Object[] arguments, Method beanMethod) throws Throwable{
Chain chainObj = afterChains.get(method);
if(chainObj == null) {
Class extends Chain>[] main = null;
Class extends Chain>[] jdkClass = null;
if(method.isAnnotationPresent(AfterChain.class)) {//如果有
main = method.getAnnotation(AfterChain.class).value();
}
if(beanMethod != null && beanMethod.isAnnotationPresent(AfterChain.class)) {
jdkClass = beanMethod.getAnnotation(AfterChain.class).value();
}
chainObj = getChain(main, jdkClass);
if(chainObj != null)
afterChains.put(method, chainObj);
}
if(chainObj != null) {//拥有业务链条
chainObj.execute();
}
}
}
jdk代理类:处理jdk动态代理,并且缓存需要自己类注解的方法,便于处理注解
package littlehow.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* InvocationProxy jdk动态代理生成类
*
* @author littlehow
* @time 2016-09-26 11:42
*/
public class InvocationProxy extends ProxyParant implements InvocationHandler {
/**
* 生成代理对象
* @param obj
* @param 接口类型
* @return
*/
public static T newInstance(T obj) {
InvocationProxy invocationProxy = new InvocationProxy<>();
Class> clazz = obj.getClass();
invocationProxy.instance = obj;
//将类中拥有注解的方法进行缓存
Class>[] classes = obj.getClass().getInterfaces();
for(Class> clz : classes) {//由接口找出适配的方法,简单的根据tostring进行查询
Method[] methods = clz.getMethods();
for(Method m : methods) {
try {
Method objMethod = clazz.getMethod(m.getName(), m.getParameterTypes());
//判断是否有注解,没有的话不进行保存
if(objMethod.getAnnotations().length > 0) {
invocationProxy.jdkBeanMethods.put(m.toString(), objMethod);
}
} catch (Exception ex) {//主要处理NoSuchMethodException
//写入父类的方法不进行处理,因为这里主要处理的是注解在实现类中的差异,父类承接了和接口注解就一致了
}
}
}
return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), invocationProxy);
}
@Override
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
return this.executeMethod(method, arguments);
}
}
cglib代理类:
package littlehow.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib动态代理
*
* @author littlehow
* @time 2016-09-26 11:08
*/
public class CglibProxy extends ProxyParant implements MethodInterceptor {
public static T newCglibInstance(T obj) {
CglibProxy cp = new CglibProxy<>();
cp.instance = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(cp);
return (T)enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
return this.executeMethod(method, arguments);
}
}
由是否有接口来简单判断是使用jdk代理还是cglib代理:
package littlehow.proxy;
/**
* ProxyUtils
*
* @author littlehow
* @time 2016-09-26 11:58
*/
public class ProxyUtils {
/**
* 获取代理对象
* @param obj
* @param
* @return
*/
public static T getProxyInstance(T obj) {
if(obj == null) return null;
//判断是否有接口,当前jdk版本返回的数组不可能为null,如果未实现接口,则数组长度为0
Class>[] interfaces = obj.getClass().getInterfaces();
if (interfaces == null || interfaces.length == 0) {//使用cglib代理
return CglibProxy.newCglibInstance(obj);
} else {//使用jdk代理
return InvocationProxy.newInstance(obj);
}
}
}
package littlehow.proxy.anno;
import littlehow.proxy.chain.Chain;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* BeforeChain
*
* @author littlehow
* @time 2016-09-26 18:22
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeforeChain {
Class extends Chain>[] value();
}
package littlehow.proxy.anno;
import littlehow.proxy.chain.Chain;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* AfterChain
*
* @author littlehow
* @time 2016-09-26 18:26
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterChain {
Class extends Chain>[] value();
}
代理类所依赖的注解(切入方式,前切链,后继链):
package littlehow.proxy.anno;
import littlehow.proxy.enm.CutType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Cut
*
* @author littlehow
* @time 2016-09-26 12:59
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cut {
CutType value() default CutType.ROUND;
}
切入类型枚举(前切,后继,环绕):
package littlehow.proxy.enm;
/**
* CutType
*
* @author littlehow
* @time 2016-09-26 12:56
*/
public enum CutType {
BEFORE(1),//0x0001
AFTER(2),//0x0002
ROUND(3);//0x0003
private int value;
CutType(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
业务链和上下文管理类以及终链:
package littlehow.proxy.chain;
/**
* PreChain
*
* @author littlehow
* @time 2016-09-26 14:06
*/
public abstract class Chain {
protected Chain next;
public void setNext(Chain next) {
this.next = next;
}
public Chain(){
}
public Context getContext() {
return ContextManager.get();
}
/**
* 自己的业务过程
* @return
*/
public abstract void invoke();
/**
* 执行
*/
public void execute() {
invoke();
next();
}
/**
* 下一链
*/
public void next() {
if(next != null)
next.execute();
}
}
package littlehow.proxy.chain;
import java.lang.reflect.Method;
/**
* Context 基础上下文
*
* @author littlehow
* @time 2016-09-26 14:35
*/
public class Context {
/** 切入方法 */
private Method method;
/** 方法参数 */
private Object[] arguments;
public Context(Method method, Object[] arguments) {
this.method = method;
this.arguments = arguments;
}
public Method getMethod() {
return method;
}
public Object[] getArguments() {
return arguments;
}
}
package littlehow.proxy.chain;
/**
* ContextManager
*
* @author littlehow
* @time 2016-09-26 14:52
*/
public class ContextManager {
private static ThreadLocal contextManager = new ThreadLocal<>();
public static void set(Context context) {
contextManager.set(context);
}
public static Context get() {
return contextManager.get();
}
public static void remove() {
contextManager.remove();
}
}
package littlehow.proxy.chain;
/**
* EndChain
*
* @author littlehow
* @time 2016-09-26 14:58
*/
public class EndChain extends Chain {
@Override
public void invoke() {
ContextManager.remove();//移除上下文
}
}
一下是测试用例:
package littlehow.proxy.test;
import littlehow.proxy.anno.AfterChain;
import littlehow.proxy.anno.BeforeChain;
import littlehow.proxy.anno.Cut;
import littlehow.proxy.enm.CutType;
import javax.annotation.PreDestroy;
/**
* Bean
*
* @author littlehow
* @time 2016-09-26 09:51
*/
public class Bean {
@Cut(CutType.ROUND)
@BeforeChain({TimeChain.class})
@AfterChain({TimeChain.class})
public void run() {
System.out.println("这个使用cglib代理");
}
}
package littlehow.proxy.test;
import littlehow.proxy.anno.AfterChain;
/**
* BeanHasInterface
*
* @author littlehow
* @time 2016-09-26 09:49
*/
public final class BeanHasInterface implements MyInter {
@Override
@AfterChain(TimeChain.class)
public void run() {
System.out.println("这个是用jdk进行代理");
}
}
package littlehow.proxy.test;
import littlehow.proxy.chain.Chain;
import java.util.concurrent.TimeUnit;
/**
* DelayChain
*
* @author littlehow
* @time 2016-09-26 18:11
*/
public class DelayChain extends Chain{
@Override
public void invoke() {
try {
System.out.println("我先睡觉3秒钟");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package littlehow.proxy.test;
import littlehow.proxy.anno.AfterChain;
import littlehow.proxy.anno.BeforeChain;
import littlehow.proxy.anno.Cut;
import littlehow.proxy.enm.CutType;
/**
* MyInter
*
* @author littlehow
* @time 2016-09-26 09:49
*/
public interface MyInter {
@Cut(CutType.ROUND)
@BeforeChain(TimeChain.class)
@AfterChain(DelayChain.class)
void run();
}
package littlehow.proxy.test;
import littlehow.proxy.chain.Chain;
/**
* TimeChain 测试链,记录调用前的时间
*
* @author littlehow
* @time 2016-09-26 14:27
*/
public class TimeChain extends Chain {
@Override
public void invoke() {
System.out.println("方法名字是:"+this.getContext().getMethod().getName() + ",调用时间为:" + System.currentTimeMillis());
}
}
package littlehow.proxy.test;
import littlehow.proxy.ProxyUtils;
/**
* Test
*
* @author littlehow
* @time 2016-09-26 09:51
*/
public class Test {
@org.junit.Test
public void cgligTest() {
Bean bean = new Bean();
//进行代理
bean = ProxyUtils.getProxyInstance(bean);
bean.run();
//确定是否为cglib动态生成的字节码类
System.out.println(bean.getClass());//class littlehow.proxy.test.Bean$$EnhancerByCGLIB$$ef9b8cdb
}
@org.junit.Test
public void jdkTest() {
//这个地方需要用接口的引用来接收代理对象
MyInter bean = new BeanHasInterface();
bean = ProxyUtils.getProxyInstance(bean);
bean.run();
//确认是否为jdk进行的代理
System.out.println(bean.getClass());//class com.sun.proxy.$Proxy2
}
}
没有经过大规模测试,后期运用到项目中,会陆续进行测试,所以如果有人能发现问题,请一定要提出,谢谢