用什么网站做框架图,河南省住房和城乡建设厅查询网站首页,平面设计如何接单,室内设计学院Day49
代理模式proxy 概念#xff1a; 代理(Proxy)是一种设计模式#xff0c;提供了对目标对象另外的访问方式#xff0c;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 代理模式分为静态代理和动态代理…Day49
代理模式proxy 概念 代理(Proxy)是一种设计模式提供了对目标对象另外的访问方式即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 代理模式分为静态代理和动态代理两种 。 静态代理 思路对于同一个接口代理类和被代理类都实现了这个接口代理类中将被代理类对象持有为自己的属性这样在使用方法的时候就可以在被代理类的方法前后加上增强即自己的方法逻辑。 以Speaker接口、ChineseSpeaker、AmericaSpeaker、ChinsesSpeakerProxy、AmericaSpeakerProxy为例其中ChinsesSpeakerProxy、AmericaSpeakerProxy分别是ChineseSpeaker、AmericaSpeaker的代理类 Speaker: public interface Speaker {void speak();
}ChineseSpeaker: public class ChineseSpeaker implements Speaker{public void speak() {System.out.println(中文演讲);}
}ChinsesSpeakerProxy: public class ChineseSpeakerProxy implements Speaker{private ChineseSpeaker speaker;public ChineseSpeakerProxy(ChineseSpeaker speaker) {this.speaker speaker;}Overridepublic void speak() {System.out.println(增强处理);speaker.speak();System.out.println(增强处理);}
}可以看出这种写法每代理一个真实类就需要写一个代理类对于AmericaSpeaker同样要写一个AmericaSpeakerProxy。如果对于功能增强的内容完全相同就可以使用一个对于Speaker接口通用的代理类CommonSpeakerProxy利用多态完成代理。 public class CommonSpeakerProxy implements Speaker {private Speaker speaker;public CommonSpeakerProxy(Speaker speaker) {this.speaker speaker;}Overridepublic void speak() {System.out.println(前置功能增强);speaker.speak();System.out.println(后置功能增强);}
}除了这种写法外还可以利用反射的思想来写利用多态通过接口实现类拿到方法通过有参传入的类对象用method.invoke()方法完成代理。 假设这时再添加了Seller接口、ChineseSeller、AmericaSeller类及其代理类CommonSellerProxy public class CommonSellerProxy implements Seller{private static Method method;private Object seller;public CommonSellerProxy(Object seller){this.seller seller;}static {try {method Seller.class.getMethod(sell);} catch (NoSuchMethodException e) {e.printStackTrace();}}Overridepublic void sell() {try {System.out.println(前置功能增强);method.invoke(seller);System.out.println(后置功能增强);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);}}
}可以看出静态代理每实现一个真实类的代理就需要写一个代理类如果代理的功能不同就需要针对代理功能编写多个类十分复杂。那么有没有一种方式使得在使用到代理的时候再去编写代理的逻辑功能而不是每次都去多写一个类呢这就是动态代理的思想。 动态代理 动态代理又根据代理对象进行划分 为接口做代理JDK动态代理 为类做代理CGLIB动态代理 JDK动态代理 由于动态代理和静态代理差别较大这里从静态代理开始进阶优化直到达到动态代理的范畴。 **静态代理进阶**思路写一个接口接口中定义了代理重写的方法在代理类中以匿名内部类的方式创建一个类对象作为自己持有的属性并用全参构造要求使用时创建这个匿名内部类即重写代理方法内容而代理类中就只需要调用接口的实现类的方法就行不需要再写明方法逻辑。 这个接口是jdk自带的接口在这里自己写一遍以更好地明白逻辑 MethodInvocationHandler接口 public interface MethodInvocationHandler {Object handle(Object target, Method method,Object[] args) throws Exception;
}注意这个方法的本质是反射利用method.invoke()方法进行调用真实类的方法再加上代理类的方法因此参数为method.invoke()的参数。 ChineseSpeakerProxy代理类 public class ChineseSpeakerProxy implements Speaker{private Speaker speaker;private MethodInvocationHandler handler;private static Method method;static {try {method Speaker.class.getMethod(speak);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}public ChineseSpeakerProxy(Speaker speaker, MethodInvocationHandler handler) {this.speaker speaker;this.handler handler;}//如果有多个需要代理的方法就都要进行重写Overridepublic void speak() {try {handler.handle(speaker,method,null);//由于真实类中的方法是无参的所以这里的参数数组为空} catch (Exception e) {throw new RuntimeException(e);}}
}使用 public class Test01 {public static void main(String[] args) {ChineseSpeaker chineseSpeaker new ChineseSpeaker();ChineseSpeakerProxy chineseSpeakerProxy new ChineseSpeakerProxy(chineseSpeaker, new MethodInvocationHandler() {Overridepublic Object handle(Object target, Method method, Object[] args) throws Exception {System.out.println(功能增强);method.invoke(target,args);System.out.println(功能增强);return null;}});chineseSpeakerProxy.speak();}至此对于同一个代理类的多个功能实现了让用户自己写增强方法的目的。但是对于一个真实类如果要实现其代理那还是要写一个代理类如果真实类很多那就需要写很多的代理类代理繁多的问题依然存在。 如果这些代理类能够使用代码来生成然后再编译再加载至 JVM 中那么再多的代理也就不是问题了。 动态代理 手动写一个能够自动创建代理类源码的类然后手动完成编译、加载的过程。这些功能jdk的接口都实现了这里只手写一个自动创建代理类源码的类以便深刻理解 因此可以手动写一个能够自动创建代理类源码的类然后手动完成编译、加载的过程。这些功能jdk的接口都实现了这里只手写一个自动创建代理类源码的类以便深刻理解 package com.qf.proxy;import com.qf.proxy.dynamic.MethodInvocationHandler;import java.lang.reflect.Method;
import java.lang.reflect.Parameter;public class MyProxy {private static String generateProxyClass(Class? clazz){if(!clazz.isInterface()) throw new IllegalArgumentException(clazz.getName() 不是接口);StringBuilder builder new StringBuilder();builder.append(package ).append(clazz.getPackage().getName()).append(;\n);builder.append(import ).append(Method.class.getName()).append(;\n);builder.append(import ).append(MethodInvocationHandler.class.getName()).append(;\n);builder.append(import ).append(MyProxy.class.getName()).append(;\n);builder.append(public class $proxy0 extends MyProxy implements ).append(clazz.getSimpleName()).append({\n);StringBuilder staticBuilder new StringBuilder();staticBuilder.append(static {\n);staticBuilder.append(try {\n);StringBuilder overrideMethodBuilder new StringBuilder();Method[] methods clazz.getMethods();for(int i0; imethods.length; i){builder.append(private static Method m).append(i).append(;\n);staticBuilder.append(m).append(i).append(Class.forName(\).append(clazz.getName()).append(\).getMethod(\).append(methods[i].getName()).append(\,);overrideMethodBuilder.append(\nOverride\n);overrideMethodBuilder.append(public ).append(methods[i].getReturnType().getSimpleName()).append( ).append(methods[i].getName()).append(();Parameter[] parameters methods[i].getParameters();for(Parameter parameter : parameters){staticBuilder.append(parameter.getType().getSimpleName()).append(.class,);overrideMethodBuilder.append(parameter.getType().getSimpleName()).append( ).append(parameter.getName()).append(,);}staticBuilder.deleteCharAt(staticBuilder.length()-1);staticBuilder.append();\n);if(parameters.length 0)overrideMethodBuilder.deleteCharAt(overrideMethodBuilder.length()-1);overrideMethodBuilder.append(){\n);Class returnType methods[i].getReturnType();if(returnType ! Void.class returnType ! void.class)overrideMethodBuilder.append(return ().append(methods[i].getReturnType().getSimpleName()).append());overrideMethodBuilder.append(handler.handle(m).append(i).append(,new Object[]{);for(Parameter parameter : parameters){overrideMethodBuilder.append(parameter.getName()).append(,);}if(parameters.length 0)overrideMethodBuilder.deleteCharAt(overrideMethodBuilder.length()-1);overrideMethodBuilder.append(});\n});}staticBuilder.append(} catch (NoSuchMethodException e) {\ne.printStackTrace();\n}catch (ClassNotFoundException e) {\ne.printStackTrace();\n}\n);staticBuilder.append(}\n);builder.append(staticBuilder);builder.append(protected $proxy0(MethodInvocationHandler handler) {\nsuper(handler);\n}\n);builder.append(overrideMethodBuilder);builder.append(\n});System.out.println(builder);return builder.toString();}// public static void main(String[] args) {
// generateProxyClass(Seller.class);
// }
} 这个类完成了自动创建类源码的功能其实现的思路就是将一个代理类中的特定类利用反射和object去替换然后将整个类写成字符串放进StringBuilder中。 然后是编译代理类源文件、加载编译好的代理类利用类加载器、编写创建代理实例的方法。这些底层就不手写了。 接下来对比一下两种写法的区别这里都实现jdk自带的InvocationHandler接口接口内容和上面手写的MethodInvocationHandler一致只是方法名字为invoke我写的是handle 手动创建ChineseSpeakerProxy此时仍然为静态代理因为代理类是在编译时明确定义的并且代理类的代码是手动编写的。相对于动态代理静态代理类在运行时不会自动生成而是在编译时就已经存在。 : public class ChineseSpeakerProxy implements Speaker{private Speaker speaker;private InvocationHandler handler;private static Method method;static {try {method Speaker.class.getMethod(speak);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}public ChineseSpeakerProxy(Speaker speaker, InvocationHandler handler) {this.speaker speaker;this.handler handler;}Overridepublic void speak() {try {handler.invoke(speaker,method,null);} catch (Throwable e) {throw new RuntimeException(e);}}
}public class Test01 {public static void main(String[] args) {ChineseSpeaker chineseSpeaker new ChineseSpeaker();ChineseSpeakerProxy chineseSpeakerProxy new ChineseSpeakerProxy(chineseSpeaker, new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {method.invoke(proxy,args);System.out.println(方法增强);return null;}});chineseSpeakerProxy.speak();} 使用jdk自带的Proxy类静态方法创建动态代理 public class Test01 {public static void main(String[] args) {ChineseSpeaker chineseSpeaker new ChineseSpeaker();Speaker ChineseSpeakerProxy (Speaker) Proxy.newProxyInstance(ChineseSpeaker.class.getClassLoader(), ChineseSpeaker.class.getInterfaces(), new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(方法增强);method.invoke(chineseSpeaker,args);return null;}});ChineseSpeakerProxy.speak();} 注意使用Proxy类实现动态代理的时候method.invoke()的第一个参数不是形参给的而是被代理的类对象 因为使用 Proxy.newProxyInstance动态生成的代理类会把代理实例本身传递给 InvocationHandler.invoke方法的 proxy 参数。因此如果在 invoke 方法中再次调用 method.invoke(proxy, args)就会导致递归调用形成死循环。 总结 静态代理 写法一真实类和代理类实现同一个接口代理类持有真实类对象作为属性并在重写接口方法的时候调用其方法再添加方法增强。 写法二如果多个真实类需要进行的方法增强相同则可以写一个通用的代理类实现和写法一相同。 写法三利用反射的思想代理类持有真实类对象属性并利用反射拿到方法重写的时候用invoke,并添加方法增强。 写法四进阶利用接口这里自己写的是MethodInvocationHandler定义重写的方法在代理类中创建接口的实现类匿名内部类并通过全参构造让用户自己传入真实类对象和接口实现类并重写代理方法调用实现类的方法。 动态代理自动创建代理类源码然后完成编译、加载。 代理类在运行时根据目标对象和增强逻辑动态生成。 在使用的时候利用Proxy类的静态方法newProxyInstance创建代理类并 通过 InvocationHandler 接口的 invoke 方法在运行时拦截方法调用执行增强逻辑。 RESTful风格 RESTful风格不是标准但是在企业中经常使用RESTful风格来完成功能的开发。 REST Representational State Transfer(表属性状态转移) 简单来说就是在编写Servlet的时候重写doGet,doPost,doPut,doDelete实现增查改删功能。 Spring IOC Spring简介 Spring 是目前主流的 Java 开发框架是 Java 世界最为成功的框架。其目的是用于简化企业级应用程序开发的难度和周期任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台除了自己提供功能外还提供粘合其他技术和框架的能力。 什么是框架 框架是一个半成品提供了基本的运行功能但具体业务实现需要我们去编写。 Spring体系结构 IOC概念 IOC全称为 Inverse Of Control表示控制反转。指的是程序员使用硬编码创建的对象转为由Spring容器来创建对于对象生命周期的控制交给Spring容器来管理。控制反转解决了具有依赖关系的组件之间的强耦合使得项目形态更加稳健 依赖注入 DI全称为Dependency Injection表示依赖注入。指的是在Spring创建对象的同时为其属性赋值 设值注入 创建一个类 Data
public class Student {private String name;private String sex;private int age;private Date birthday;
}在xml配置文件中利用设值注入创建对象 常见数据类型 !--application.xml--
bean namestu classcom.qf.spring.ioc.model.Studentproperty namename value张三 /property nameage value20 /property namesex value男 /!--这里需要注意日期类型的默认格式yyyy/MM/dd--property namebirthday value2021/10/10 /
/bean使用 Test
public void studentTest(){//应用上下文使用的是类路径下XML文档作为当前应用上下文ApplicationContext context new ClassPathXmlApplicationContext(application.xml);//从上下文中根据bean的名称或者ID获取bean对象Student stu context.getBean(stu, Student.class);System.out.println(stu);
}注意这里的name虽然是user但是并不是指的user这个类而是指向set方法理解为去掉set后的名字设值注入本质就是通过set方法为属性注入值。设值注入必须保证存在无参构造否则将报错。 注入数组类型 spring 提供了 array 标签来进行数组类型的属性值的注入。 Data
public class Clazz {private int id;private String name;private Student[] students;
}bean nameclazz classcom.qf.spring.ioc.model.Clazzproperty nameid value1 /property namename value张三 /property namestudentsarray!--引用数据类型 可以使用bean标签创建bean对象注入值--!--bean class/bean--!--引用数据类型 可以使用ref标签引用bean对象注入值--ref beans /ref beanstu /!--常用数据类型 可以使用value标签直接注入值--!-- value/value--/array/property
/bean注入集合类型 List集合 Data
AllArgsConstructor
NoArgsConstructor
public class Student {private String name;private String sex;private int age;private Date birthday;private ListDouble scores;
}bean namestu classcom.qf.spring.ioc.model.Studentproperty namename value张三 /property nameage value20 /property namesex value男 /!--这里需要注意日期类型的默认格式yyyy/MM/dd--property namebirthday value2021/10/10 /property namescoreslistvalue80.0/valuevalue90.0/valuevalue81.0/valuevalue82.0/value/list/property
/beanbean names classcom.qf.spring.ioc.model.Student!--这里按照顺序为属性注入值--constructor-arg index0 value李四 /constructor-arg index1 value女 /constructor-arg index2 value22 /constructor-arg index3 value2020/05/05 /constructor-arg index4listvalue80.0/valuevalue90.0/valuevalue81.0/valuevalue82.0/value/list/constructor-arg
/beanSet集合 Data
public class Person {private String name;private SetString friendNames;
}bean namep classcom.qf.spring.ioc.model.Personproperty namename value李刚 /property namefriendNamessetvalue李四/valuevalue王五/value/set/property
/bean 注入Map Data
public class Person {private String name;private ListString friendNames;private MapString, Object map;
} bean namep classcom.qf.spring.ioc.model.Personproperty namename value李刚 /property namefriendNamessetvalue李四/valuevalue王五/value/set/propertyproperty namemapmapentry keyhobby value聊天 /entry keyclazz value-refclazz//map/propertyproperty namepropspropsprop keydesc我很帅/propprop keysecret我有两个女朋友/prop/props/property
/bean 注入Properties Data
public class Person {private String name;private ListString friendNames;private Properties props;
} bean namep classcom.qf.spring.ioc.model.Personproperty namename value李刚 /property namefriendNamessetvalue李四/valuevalue王五/value/set/propertyproperty namepropspropsprop keydesc我很帅/propprop keysecret我有两个女朋友/prop/props/property
/bean 构造注入 构造注入指的是通过构造放入为属性注入值。 Data
AllArgsConstructor
NoArgsConstructor
public class Student {private String name;private String sex;private int age;private Date birthday;
}
!--application.xml--
bean names classcom.qf.spring.ioc.model.Student!--这里按照顺序为属性注入值--constructor-arg index0 value李四 /constructor-arg index1 value女 /constructor-arg index2 value22 /constructor-arg index3 value2020/05/05 /
/bean
Test
public void studentConstructorTest(){//应用上下文使用的是类路径下XML文档作为当前应用上下文ApplicationContext context new ClassPathXmlApplicationContext(application.xml);//从上下文中根据bean的名称或者ID获取bean对象Student stu context.getBean(s, Student.class);System.out.println(stu);
}