代理模式(Proxy Pattern)是只为其他对象提供一种代理,以控制对这个对象的访问,属于结构行模式。
代理模式一般包含三种角色:
抽象主题角色:抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以说抽象类。
真实主题角色:该类被称为代理类,该类定义了代理所表示的真实对象,是负责执行系统真正逻辑业务对象。
代理主角色:也被称为代理类;其内部持有(真实主题角色)的引用,因此具备完全的对(真实主题角色)的代理权。客户端调用代理对象方法,同时也调用了代理类方法,但是会在代理类对象前后增加一些处理代码。
优点:
1、代理模式能将代理对象与真实被调用目标对象分离。
2、在一定程度上降低了系统的耦合性,扩展性好。
3、可以起到保护目标对象的作用。
4、可以增强目标对象的功能。
缺点:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。
3、增加了系统的复杂度。
应用场景:系统日志、事物代理、springAopd等。
静态代理:
静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需同步增加,违背开闭原则。
代码示例: (静态代理只负责自己代理的;如下只能代理售票,如新增一个售卖其他的类无法代理)
//售票接口(抽象主题角色)
public interface ITicketSales {
void sellTrainTicket();
}
//张三购票(真实主题角色)
public class ZhangsanBuyTickets implements ITicketSales {
@Override
public void sellTrainTicket() {
System.out.println("张三购买火车票");
}
}
//代理主角色
public class Proxy implements ITicketSales{
private ITicketSales ticketSales;
public Proxy(ITicketSales ticketSales) {
this.ticketSales = ticketSales;
}
private void after(){
System.out.println("黄牛代理售票收取手续费");
}
private void before(){
System.out.println("取票");
}
@Override
public void sellTrainTicket() {
after();
ticketSales.sellTrainTicket();
before();
}
}
动态代理:
动态代理采用在运行时动态生成代码的方式,取消了被代理类的扩展性,遵循开闭原则。
若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码
JDK动态代理:
代理示例:(JDK实现InvocationHandler 接口,JDK动态代理实现了被代理对象的接口,售卖其他的也可代理,动态代理更灵活,违背单一职责)
//JDK代理主角色
public class Cattle implements InvocationHandler {
private Object target;
public Object getInstance(Object target){
this.target=target;
Class<?> aClass = target.getClass();
return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
after();
Object invoke = method.invoke(this.target, args);
before();
return invoke;
}
private void after(){
System.out.println("黄牛代理售票收取手续费");
}
private void before(){
System.out.println("取票");
}
}
CGLIB动态代理:
代理示例:(CGLIB实现MethodInterceptor 接口,CGLIB代理继承了被代理对象,售卖其他的也可代理,动态代理更灵活,违背单一职责)
public class Cattle implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
private void after(){
System.out.println("黄牛代理售票收取手续费");
}
private void before(){
System.out.println("取票");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
after();
Object invoke = methodProxy.invokeSuper(o, objects);
before();
return invoke;
}
}
CGLIB和JDK动态代理区别:
1、JDK动态代理实现了被代理对象的接口,CGLIB代理继承了被代理对象。
2、JDK动态代理和CGLIB动态代理都在运行期间生成字节码,JDK动态代理直接写Class字节码,CGLIB代理使用ASM框架写Class字节码,CGLIB代理实现更复杂,生成代理类比JDK动态代理效率低。
3、JDK动态代理调用代理方法是通过反射机制调用的,CGLIB代理是通过FastClass机制直接调用方法,CGLIB代理的执行效率更高。
模拟JDK动态代理:
/**
* @Title: MySimpleJavaFileObject
* @Description: 为 JavaFileObject 中的大多数方法提供简单实现。应子类化此类并用作
* JavaFileObject 实现的基础。子类可以重写此类任意方法的实现和规范,
* 只要不违背 JavaFileObject 的常规协定。
*/
public class MySimpleJavaFileObject extends SimpleJavaFileObject {
//源码
private String source;
//加载class流
private ByteArrayOutputStream outputStream;
/**
* java源码抽象实现
* @param className
* @param source
*/
public MySimpleJavaFileObject(String className, String source) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.source=source;
}
/**
* 返回源码
* @param ignoreEncodingErrors
* @return
* @throws IOException
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return this.source;
}
/**
* class源码抽象实现
* @param className
* @param kind
*/
public MySimpleJavaFileObject(String className, Kind kind) {
super(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind);
}
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream=new ByteArrayOutputStream();
}
/**
* 加载class需要当前class流
* @return
*/
public byte[] getClassBytes() {
return outputStream.toByteArray();
}
}
/**
* @Title: MyJavaFileManager
* @Description: 只要继承ForwardingJavaFileManager类就可以实现从内存中直接加载Java源文件,
* 继承并重写URLClassLoader.findClass()就可以实现将编译结果直接以byte[]形式返回
*/
public class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private MySimpleJavaFileObject classFileObject;
protected MyJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
classFileObject = new MySimpleJavaFileObject(className, kind);
return classFileObject;
}
@Override
public ClassLoader getClassLoader(Location location) {
return new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = classFileObject.getClassBytes();
return super.defineClass(name, classBytes, 0, classBytes.length);
}
};
}
}
/**
* 模仿JDK InvocationHandler接口
*/
public interface ImitateInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
/**
* @Title: AchieveProxy
* @Description: 模仿JDK代理
*/
public class AchieveProxy {
public static final String ln = "\r\n";
public static Object newProxyInstance(Class<?> [] interfaces, ImitateInvocationHandler h){
String generateSrc = generateSrc(interfaces);
String className=getFullClassName(generateSrc);
//生成源代码的JavaFileObject
SimpleJavaFileObject fileObject = new MySimpleJavaFileObject(className, generateSrc);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//被修改后的JavaFileManager
JavaFileManager fileManager = new MyJavaFileManager(compiler.getStandardFileManager(null, null, null));
//执行编译
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, Arrays.asList(fileObject));
task.call();
//获得ClassLoader,加载class文件
ClassLoader classLoader = fileManager.getClassLoader(null);
try {
Class<?> clazz = classLoader.loadClass(className);
Constructor c = clazz.getConstructor(ImitateInvocationHandler.class);
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?>[] interfaces){
StringBuffer sb = new StringBuffer();
sb.append(AchieveProxy.class.getPackage() + ";" + ln);
sb.append("import " + interfaces[0].getName() + ";" + ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("ImitateInvocationHandler h;" + ln);
sb.append("public $Proxy0(ImitateInvocationHandler 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+1)!=params.length){
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
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<Class, Class>();
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() + "Value()";
}
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);
}
public static String getFullClassName(String sourceCode) {
String className = "";
Pattern pattern = Pattern.compile("package\\s+\\S+\\s*;");
Matcher matcher = pattern.matcher(sourceCode);
if (matcher.find()) {
className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";
}
Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s*");
matcher = CLASS_PATTERN.matcher(sourceCode);
if(matcher.find()){
//获取class类名
className += matcher.group(1);
} else{
throw new IllegalArgumentException("No such class name in " + sourceCode);
}
return className;
}
}
/**
* @Title: Cattle
* @Description: 黄牛代理售票
*/
public class Cattle implements ImitateInvocationHandler{
private Object target;
public Object getInstance(Object target){
this.target=target;
Class<?> aClass = target.getClass();
return AchieveProxy.newProxyInstance(aClass.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
after();
Object invoke = method.invoke(this.target, args);
before();
return invoke;
}
private void after(){
System.out.println("黄牛代理售票收取手续费");
}
private void before(){
System.out.println("取票");
}
}
//测试
public static void main(String [] args){
//模拟JDK动态代理
com.learn.proxy.dynamic.imitatejdkproxy.client.Cattle ct=new com.learn.proxy.dynamic.imitatejdkproxy.client.Cattle();
ITicketSales sales=(ITicketSales)ct.getInstance(new ZhangsanBuyTickets());
sales.sellTrainTicket();
//通过反编译工具可以查看源代码
//try {
// byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",
// new Class[]{ITicketSales.class});
// FileOutputStream os = new FileOutputStream("D://workspace//$Proxy0.class");
// os.write(bytes);
// os.close();
//} catch (IOException e) {
// e.printStackTrace();
//}
}
输出:
黄牛代理售票收取手续费
张三购买火车票
取票
来源:oschina
链接:https://my.oschina.net/u/4483459/blog/3216598