java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance

本文主要是介绍java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

时间
4/25

文章目录

      • 克隆
        • 概述
        • 实现克隆的方式
          • 实现Cloneable接口,并重写object类中的clone方法,可以实现浅克隆,也可以实现深克隆
          • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆
          • 利用BeanUtils,apache和spring都提供了bean工具,它是浅克隆
        • 浅克隆(Shallow Clone) 和 深克隆(Deep Clone)
          • 浅克隆(Shallow Clone)
          • 深克隆(Deep Clone)
        • 细节对比点
          • new操作符创建对象和clone方法复制对象的区别
          • 复制引用 和 复制对象 区别
          • 克隆两种实现方式的区别
      • java参数传递 = 值传递
      • RMI
      • Java中的动态代理
        • 代理模式
        • 静态代理
        • 动态代理
          • 基于接口的代理JDK
          • 基于继承的代理 CGlib

克隆

概述

克隆出现的原因:
在实际开发过程中,一个对象obj已经包含一些有用信息,这是我们需要将obj的信息复制到obj2中,使得obj和obj2对象具有两个完全不同的地址,修改一个对象的值,另一个对象不受影响。

实现克隆的方式
实现Cloneable接口,并重写object类中的clone方法,可以实现浅克隆,也可以实现深克隆
1 被克隆对象所属类必须实现Cloneable接口public class 类名 implements Cloneable{private 属性类 属性名;}
1-1 要想实现深克隆的话,类的成员属性 所在的类也需要实现Cloneable接口public class 属性类 implements Cloneable{}
2 所属类必须重写Object类的clone方法@Overridepublic Object clone() throws CloneNotSupportedException{类名 obj =(类名)super.clone(); // 浅克隆obj.属性名 = (属性类)属性名.clone(); //深克隆return obj;
}
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆
// 要序列化对象所属类 需要实现Serializable接口
public class 类名 implements Serializable{}
// 序列化
1 创建序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象类名 obj = new 类名();
3 使用序列化流的writeObject方法将对象写入指定的ObjectOutputStream流中oos.writeObject(obj);
4 释放资源oos.close();// 反序列化
1 创建反序列化流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化流的readObject方法从ObjectInputStream流中读取一个对象Object obj = ois.readObject();
3 强制转化对象类型类型 obj1 = (类型)obj;
4 释放资源ois.close();
利用BeanUtils,apache和spring都提供了bean工具,它是浅克隆

扩展

  • BeanUtils 是工具类,用于简化JavaBean的封装,简化数据的封装。(xhj理解:和collections是一样的,同样是工具类)
  • apache 是 世界使用排名第一的Web服务器软件,具有跨平台和安全性的优势,是最流行的Web服务器端软件之一。
  • Spring框架是一个开放源代码的J2EE应用程序框架,针对bean的生命周期进行管理的轻量级容器。
    在Spring中,构成应用程序主干并由Spring IOC容器管理的对象称为bean。bean是一个由Spring IOC容器实例化、组装和管理的对象。
    实例的创建不再由调用者管理,而是由Spring容器创建,容器会负责控制程序之间的关系,而不是由程序代码直接控制。
浅克隆(Shallow Clone) 和 深克隆(Deep Clone)

两种克隆方式主要区别在于是否支持引用类型的成员变量的复制。
java中的数据类型分为:基本数据类型(数值型{整型、浮点型、字符型}) 和 引用数据类型(接口、类、数组)

浅克隆(Shallow Clone)

浅克隆中,对象的复制只是复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
实现方法
覆盖Object类中的Clone()方法实现。

1 被复制的类需要实现Cloneable接口public class 类名 implements Cloneable{}
2 覆盖clone()方法,方法调用super.clone()方法得到需要的复制对象,输入clone有选项可以选择,自动生成。 修改为public访问级别@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}

实验分析:

1 被复制的类没有实现Cloneable接口,而直接使用  类名 对象2 = (类名)对象1.clone(); 会报错报clone()方法在java.lang.Object中是protected访问控制。
2 被复制的类实现Cloneable接口,使用 类名 对象2 = (类名)对象1.clone();  也会报错,需要抛出异常CloneNotSupportedException异常

案例:

// 320-test1
// 类
public class Student implements Cloneable{private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}// 测试类
public class StudentDemo {public static void main(String[] args) throws CloneNotSupportedException {Student s1 = new Student("汪苏泷",35);Student s2 = (Student)s1.clone();System.out.println(s1.getName() + ", " + s1.getAge());System.out.println("--------------");System.out.println(s2.getName() + ", " + s2.getAge());System.out.println(s1 == s2);// false 说明对象在堆内存中的地址不同}
}
深克隆(Deep Clone)

深克隆中,无论原型对象的成员变量的类型是值类型还是引用数据类型,都会复制一份给克隆对象。
实现方法:
方法一:通过覆盖Object类中的clone()方法实现

1 被复制对象的类 需要实现Cloneable接口,并重写Object类中的clone()方法。public class Outer implements Cloneable{private Inner inner;@Overridepublic Object clone() throws CloneNotSupportedException{Outer obj = (Outer)super.clone(); //类对象使用clone()方法obj.inner = (Inner)inner.clone();//类对象属性的类 使用clone()方法return obj;}}
2 Inner类也要实现Cloneable接口,重写Object类中的clone()方法。public class Inner implements Cloneable{@Overridepublic Object clone() throws CloneNotSupportedException{Inner obj = (Inner)super.clone();}
* 输入clone有选项可以选择 修改为public访问级别

方法二:使用序列化(Serialization)实现,序列化流ObjectOutputStream、反序列化流ObjectInputStream

// 序列化
1 创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象类名 obj1 = new 类名();
3 使用序列化方法writeObject(Object obj)oos.writeObject(obj1);
4 释放资源oos.close();// 反序列化
1 创建反序列化对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化方法readObject()Object obj = ois.readObject();
3 强制类型转换 Object =》 类名类名 obj2 =(类名)obj;
4 释放资源ois.close();

方法一案例:

// 320-test2
// Student的属性 的类
public class Address implements Cloneable {private String address;public Address() {}public Address(String address) {this.address = address;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}// Student 要克隆的对象所属的类
public class Student implements Cloneable{private String name;private int age;private Address addr;// 创建的是Address类的对象public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic Object clone() throws CloneNotSupportedException {Student stu = (Student)super.clone();//浅克隆stu.addr = (Address)addr.clone();//深克隆return stu;}
}
// 测试类
public class StudentDemo {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("西安市");Student s1 = new Student("汪苏泷",35);s1.setAddr(addr);Student s2 = (Student)s1.clone();System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());
//        System.out.println(s1 == s2);// false 说明对象在堆内存中的地址不同// 修改前输出:   汪苏泷, 35, 西安市//              汪苏泷, 35, 西安市// 两个对象的Addr变量都一样,为了验证原对象和新对象是两个不一样的,这里对s2对象的Addr变量修改值addr.setAddress("重庆市");System.out.println("修改之后");System.out.println("--------------");System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());// 对s2对象的成员变量addr进行修改,s1对象的addr变量也发生了变化。//  =》 说明 没有实现深克隆,只完成了浅克隆,成员是值类型的复制。// 要想实现深克隆,需要给Student类中的addr变量所处在类Address设置为浅克隆实现。// Address实现cloneable、且重写clone方法// 修改前输出:汪苏泷, 35, 重庆市//           汪苏泷, 35, 重庆市// 修改后输出:汪苏泷, 35, 重庆市//           汪苏泷, 35, 西安市}
}
细节对比点
new操作符创建对象和clone方法复制对象的区别
具体操作newclone
分配内存根据new操作符后面的类型分配对应大小的内存空间分配与源对象相同大小的内存空间
填充对象的域调用构造方法,填充对象的各个域(对象的初始化)使用源对象中对应的各个域填充新对象的域
对象创建完毕构造方法返回创建完毕的对象,并将其引用地址发布到外部(栈内存)clone()方法返回对象,并将其引用发布到外部(栈内存)
复制引用 和 复制对象 区别

A 是 源对象,B 是 新对象
复制引用 = A 和 B 地址值 相同,说明是一个对象
复制对象 = A 和 B 地址值 不相同,说明是两个对象

克隆两种实现方式的区别
区别项实现Cloneable接口的方式序列化和反序列化方式
是否可以实现深度克隆可以,
若对象属性嵌套很多引用类型,则需要在类中写多个.clone()方法,使用不是很方便
可以,使用方便
是否支持编译时检测异常不支持,是在运行时抛出异常的支持,通过泛型限定,可以检查出克隆的对象是否支持序列化,在编译阶段完成

java参数传递 = 值传递

当java传递的参数是基本数据类型时,一个方法不能改变基本数据类型参数的值;
当java传递的参数是引用数据类型时,一个方法可以修改引用数据类型参数所指向的对象的值;
值传递的精髓:传递的是存储单元中的内容,而不是存储单元的引用。

基本数据类型参数

// idea_face-test2-TransferTest
public class TransferTest {public static void main(String[] args) {int num = 1;System.out.println("method()方法调用前=" + num);method(num);System.out.println("method()方法调用后=" + num);}public static void method(int number){number = 2;}//method()方法调用前=1//method()方法调用后=1
}

分析:
在这里插入图片描述
引用数据类型参数

public class TransterTest2 {public static void main(String[] args) {Person p = new Person("汪苏泷");System.out.println("调用方法前= name:" + p.getName());method(p);System.out.println("调用方法后= name:" + p.getName());}public static void method(Person p){p.setName("许嵩");}// 调用方法前= name:汪苏泷// 调用方法后= name:许嵩
}

分析:
在这里插入图片描述

RMI

  • RMI(Remote Method Invocation),远程方法调用,允许运行在一个java虚拟机的对象调用运行在另一个java虚拟机上的对象的方法。两个虚拟机可以是运行在相同计算机上的不同进程,也可以是运行在网络中的不同计算机。
  • RPC(Remote Procedure Call),远程过程调用,用于一个进程调用另一个进程。

Java中的动态代理

代理模式
  • 代理模式(Proxy或Surrogate),就是一个人或机构代替另一个人或机构采取行动。
    一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
    代理就是为其他对象提供一个代理以控制对某个对象的访问。
  • 优点:
    可以隐藏真实目标类的实现;可以实现客户与真实目标类之间的解耦,在不修改真实目标类代码的情况下能够做一些操作;
  • 代理模式实现条件:
    代理类和被代理类实现共同的接口(或继承);
    代理类中存在指向被代理类的索引;( xhj 这个代理类的索引 = 创建的真实类的对象 )
    实际执行是通过调用代理类的方法从而实际执行被代理类的方法。
  • 代理分类:
    静态代理:程序员创建或特定工具自动生成源码,在对其进行编译。运行前,代理类的.class文件就已经存在了;
    动态代理:程序运行时,运用反射机制动态创建而成,动态代理类的字节码文件在程序运行时由java反射机制动态生成,无需程序员手工编写它的源代码。
    • 动态代理分类:
      基于接口的代理:代表 = JDK代理
      基于继承的代理:代表 = CGlib代理
静态代理

实现步骤

1 创建一个接口MyInterface,并创建抽象方法public interface MyInterface{// 创建抽象方法public abstract 返回值类型 method(接口MyInterface参数);}
2 创建MyInterface真实的实现类RealClass,重写接口中的抽象方法public class RealClass implements MyInterface{// 重写接口中的抽象方法,此时方法不在带有abstract修饰符@Overridepublic 返回值类型 method(接口MyInterface参数){真实的实现过程;}}
3 创建 代理类public class ProxyClass implements MyInterface{// 创建接口MyInterface的实现类对象,// 这里创建的是接口MyInterface的真实类对象,也就是代理类中指向被代理类的索引private MyInterface myinterface;// 创建带参构造方法(真实类 真实类对象)ProxyClass(MyInterface myinterface){this.myinterface = myinterface;}// 重写接口MyInterface中的抽象类方法@Overridepublic method(接口MyInterface参数){调用代理方法前做的事情;返回值 x = myinterface.method(接口MyInterface参数);调用代理方法后做的事情;}

理解

  • 好处:
    比如每次从磁盘中获取字体库的时候,磁盘的I/O比较耗时,想通过缓存将读到的字体库暂存一份,直接修改代理类ProxyClass,不用修改真实RealClass类。
    同样这样的需求直接修改RealClass类也可以实现,但是当多个RealClass2,RealClass3也需要判断缓存是否有字体的时候,那么修改真实类就需要修改多个,而采用代理类的形式,只需要修改一个类即可。

代码
需求:考虑一个字体提供功能,字体库可能源自本地磁盘、网络或者系统。

// 326-test1
// 接口
// 先考虑从本地磁盘中获取字体,采用代理的方式实现,定义一个提供字体的接口FontProvider:
public interface FontProvider {Font getFont(String name);
}
// 真实类:真正提供获取磁盘字体库的类
public class FontProviderFromDisk implements FontProvider{@Overridepublic Font getFont(String name) {System.out.println("磁盘上的字体库");return null;}// 只是输出文字,返回值类型可以不写为Font类型,而是写为void
}
// 代理类
public class ProxyForFont implements FontProvider{private FontProvider fontProvider;// 定义一个接口对象,实际上是被代理类对象ProxyForFont(FontProvider fontProvider){this.fontProvider = fontProvider;}// 定义带参构造方法,参数为接口对象@Overridepublic Font getFont(String name) {System.out.println("提供代理方法前做的事情");Font font = fontProvider.getFont(name);System.out.println("调用代理后做的事情");return font;}
}
// 测试类
public class MyFontDemo {public static void main(String[] args) {FontProvider fp = new ProxyForFont(new FontProviderFromDisk());// 多态的形式创建FontProvider接口对象// 接口 对象名 = new 代理类名(new 真实类名());fp.getFont("宋体");}
}
动态代理

出现的原因
当需要获取多种资源,比如字体、图片、音乐等资源,这时就需要多个接口,比如FontProvider、ImageProvider、MusicProvider接口等,需要对应的实现类(比如FontClass、ImageClass、MusicClass等),和其对一个的代理类(比如FontProxy、ImageProxy、MusicProxy等)。当需要给获取字体、图片、音乐等功能上加上缓存功能,就需要对三个代理类进行改动,但是修改的部分逻辑上又相同,就会造成代码的重复和代理类爆炸。
分类: 基于接口的代理(JDK) 和 基于继承的代理(CGlib)

基于接口的代理JDK

InvocationHandler接口

  • InvocationHandler接口
    每个代理类的实例都会有一个与之相关联的InvocationHandler实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler { // 接口// 唯一的方法invokepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

参数介绍:

参数名说明
Object proxy代理对象
Method method代理对象调用的方法
Object[] args调用方法中的参数

Proxy类的静态newProxyInstance方法
返回指定接口的代理实例,该代理实例将方法调用分配给指定的调用处理程序。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

参数介绍:

参数名说明获得
ClassLoader loader类加载器测试类.class.getClassLoader()
Class<?>[] interfaces用来代理的接口new Class[]{接口.class}
也就是说这里可以是多个接口.class
InvocationHandler h一个InvocationHandler对象InvocationHandler handler = new jdkProxyClass(new RealSubject());
jdkProxyClass是代理类,RealSubject是真实类

说明

  • 测试类.class.getClassLoader()
    测试类.class,得到该类对应的Class类对象,通过类的class属性
    Class类对象.getClassLoader(),得到Class类对象的类加载器
  • Class<?>[] interfaces = new Class[]{接口.class}
    2022/4/25 理解:需要创建一个Class数组,成员是接口的Class对象。

具体的实现步骤

1 创建接口MyInterfacepublic interface MyInterface{// 定义抽象方法public abstract 返回值类型 method1();public abstract 返回值类型 method2();}2 创建真实的接口实现类RealClasspublic class RealClass{// 重写抽象方法@Overridepublic 返回值类型 method1(){method1具体的实现;}@Overridepublic 返回值类型 method2(){method2具体的实现;}}3 创建接口的代理类,需要实现InvocationHandler接口public class jdkProxyClass implements InvocationHandler{// 创建接口的真实类对象private RealClass realObj;// 创建真实类对象为参数的带参构造方法public jdkProxyClass(RealClass realObj){this.realObj = realObj;}// 重写Object类中的invoke方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// proxy 被代理的对象;method:调用的方法;args:方法所需的参数执行代理调用之前的操作。Object result = null;// 这里的方法是通过反射获取的方法对象result = method.invoke(realObj,args);return result;}4 创建测试类public class jdkDemo{public static void main(String[] args){// 创建要代理的真实对象,采用多态的形式MyInterface realObj = new RealClass();// 创建代理对象InvocationHandler handler = new jdkProxyClass(realObj);// 使用Proxy.newProxyInstrace()这个静态方法 获得接口的对象MyInterface myinterface = (MyInterface)Proxy.newProxyInstance(jdkDemo.class.getClassLoader(),new Class[]{MyInterface.class},handler);// 通过接口对象调用方法myinterface.method1();myinterface.method2();}

案例:

// 326-test2
// 接口定义
public interface Subject {void method1();void method2();
}
// 接口真实的实现类定义
public class RealSubject implements Subject{@Overridepublic void method1() {System.out.println("method1执行");}@Overridepublic void method2() {System.out.println("method2执行");}
}// 接口的代理类定义
public class jdkProxySubject implements InvocationHandler {// 创建代理类的真实对象,也就是真实类的对象private Object subject;// 创建带参构造方法public jdkProxySubject(Object subject){this.subject = subject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// proxy:被代理的对象// method:调用的方法// args:方法所需要的参数System.out.println("before");Object result = null;try{// 利用反射动态的来反射方法,动态代理和静态代理的区别result = method.invoke(subject, args);}catch(Exception e){System.out.println("ex:" + e.getMessage());}finally {System.out.println("after");}return result;}
}
// 测试类
public class Client {public static void main(String[] args){// 创建要代理的真实对象Subject realSubject = new RealSubject();// 创建代理对象,将真实对象作为参数传入InvocationHandler handler =new jdkProxySubject(realSubject);Subject subject = (Subject)Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, handler);subject.method1();subject.method2();}
}
基于继承的代理 CGlib

概述

  • CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,为一个类创建子类,并在子类中采用方法拦截技术 拦截所有对父类方法的调用,并顺势加入横切逻辑。
    ASM是一款java字节码层面的代码分析和修改工具。
    ASM的目标是生成、转换、分析已编译的java class文件,可使用ASM工具读/写/转换JVM指令集。
  • CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理。
    由于spring框架中的MethodInterceptor接口,目前没有学习spring框架 ,后期总结。
    参考博客

这篇关于java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/693468

相关文章

35岁,是终点?还是拐点?

35岁,是终点还是拐点,取决于我们对生活和事业的态度、目标以及行动。这个年龄可以看作是一个重要的转折点,具有多重意义和可能性。 很多人在35岁时,已经在自己的职业生涯中建立了一定的基础,可能达到了管理层或专家级别。如果你还是一个基层员工,那你要反思一下,你的职业生涯规划可能出了问题,工作能力与人情世故为什么都没有突破?是否在某个领域深耕多年? 有些人可能会选择在这个年龄段重新评估自己

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转

如何开启和关闭3GB模式

https://jingyan.baidu.com/article/4d58d5414dfc2f9dd4e9c082.html

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

详细分析Springmvc中的@ModelAttribute基本知识(附Demo)

目录 前言1. 注解用法1.1 方法参数1.2 方法1.3 类 2. 注解场景2.1 表单参数2.2 AJAX请求2.3 文件上传 3. 实战4. 总结 前言 将请求参数绑定到模型对象上,或者在请求处理之前添加模型属性 可以在方法参数、方法或者类上使用 一般适用这几种场景: 表单处理:通过 @ModelAttribute 将表单数据绑定到模型对象上预处理逻辑:在请求处理之前

eclipse运行springboot项目,找不到主类

解决办法尝试了很多种,下载sts压缩包行不通。最后解决办法如图: help--->Eclipse Marketplace--->Popular--->找到Spring Tools 3---->Installed。

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001