学习spring的学习笔记,持续更新中~~~
Spring是什么
Spring是一个以IoC(Inversion of Control,控制反转)和AOP(Aspect OrientedProgramming)为内核的框架。IoC是Spring的基础。IoC实现的是一种控制,简单地说,就是以前调用new构造方法来创建对象,现在变成使用Spring来创建对象。
spring 容器
- Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,
并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。
这些对象被称为 Spring Beans - Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置和可执行的系统或应用程序
- 通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示
- 通常new一个类的实例,控制权由码农控制,而"控制反转"是指new实例工作不由码农来做而是交给Spring容器来做。
- Spring中IOC容器的 BeanFactory (简单实现) 容器和有ApplicationContext(包含了BeanFactory的功能) 容器
控制反转
- IoC理论上是借助于“第三方”实现具有依赖关系对象之间的解耦
- 即把各个对象类封装之后,通过IoC容器来关联这些对象类。这样对象之间就通过IoC容器进行联系,而对象之间没有直接联系
依赖注入
- DI是Dependency Inject的缩写,译为“依赖注入”。
- 所谓依赖注入,就是由IoC容器在运行期间动态地将某种依赖关系注入对象之中
// TextEditor 类依赖 SpellChecker 类,并实例化 SpellChecker 类
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SpellChecker();
}
}
// 在spring中,可能是这样,构造函数注入
// TextEditor 不实例化 SpellChecker类,控制权交给Spring
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
}
spring bean
- Spring如同一个工厂,用于生产和管理Spring容器中的Bean。要使用这个工厂,需要开发者对Spring的配置文件进行配置
- bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象
- bean是由用容器提供的配置元数据创建
<bean id="helloWorld"
class="com.huahuadavids.wehcat.pojo.HelloWorld"
lazy-init="true" // lazy-init="true" 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例
init-method="initData" // 在 bean 的所有必需的属性被容器设置之后,调用回调方法
destroy-method="destroy" // 当包含该 bean 的容器被销毁时,使用回调方法
>
<property name="message" value="Hello huahuadavids!"/>
</bean>
// java 源文件
// 第一步利用框架提供的 XmlBeanFactory() API 去生成工厂 bean 以及利用 ClassPathResource() API 去加载在路径 CLASSPATH 下可用的 bean 配置文件。
// XmlBeanFactory() API 负责创建并初始化所有的对象,即在配置文件中提到的 bean
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
// 第二步利用第一步生成的 bean 工厂对象的 getBean() 方法得到所需要的 bean。
// 这个方法通过配置文件中的 bean ID 来返回一个真正的对象,该对象最后可以用于实际的对象
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
// FileSystemXmlApplicationContext 是从文件系统找 bean的配置
// ApplicationContext context = new FileSystemXmlApplicationContext("/Users/davidzhang/Desktop/spring-starter/src/main/resources/beans.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj1 = (HelloWorld) context.getBean("helloWorld");
obj1.getMessage();
spring bean的使用过程
- 读取配置信息
- 实例化bean
- 把bean实例放到spring容器中
- 使用bean,从bean缓存池中取
spring bean的scope
- 对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域
- singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
// 两次输出的信息一样,证明 obj1 和 obj2是一样的,单例模式 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); HelloWorld obj1 = (HelloWorld) context.getBean("helloWorld"); obj1.setMessage("huhu"); obj1.getMessage(); HelloWorld obj2 = (HelloWorld) context.getBean("helloWorld"); obj2.getMessage();
- prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
bean的生命周期
- 就是 Bean的定义——Bean的初始化——Bean的使用——Bean的销毁
bean初始化拦截器
// 1. 实现BeanPostProcessor 接口
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class beanClass = bean.getClass();
if (beanClass == HelloWorld.class) {
System.out.println("HelloWorld bean 对象初始化之前······");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("HelloWorld bean 对象初始化之后······");
return bean;
}
}
// 2. xml中配置 这表示应用于所有的bean
<bean class="com.huahuadavids.wehcat.pojo.MyBeanPostProcessor" />
bean的定义继承
- 就是继承一个bean的一些配置信息,而不是java的继承
// 1 两个类
public class HelloWorld implements InitializingBean {
private String message1;
public void getMessage2() {
System.out.println("Hello World Message2 : " + message2);
}
public void setMessage2(String message2) {
this.message2 = message2;
}
private String message2;
public void setMessage1(String message1) {
this.message1 = message1;
}
public void getMessage() {
System.out.println("Hello World Message : " + message1);
}
}
public class HelloIndia {
private String message1;
private String message2;
private String message3;
public void setMessage1(String message){
this.message1 = message;
}
public void setMessage2(String message){
this.message2 = message;
}
public void setMessage3(String message){
this.message3 = message;
}
public void getMessage1(){
System.out.println("India Message1 : " + message1);
}
public void getMessage2(){
System.out.println("India Message2 : " + message2);
}
public void getMessage3(){
System.out.println("India Message3 : " + message3);
}
}
// 2 xml的bean配置
<bean id="helloWorld" class="com.huahuadavids.wehcat.pojo.HelloWorld">
<property name="message1" value="world-message1"/>
<property name="message2" value="world-message2"/>
</bean>
<bean id="helloIndia" class="com.huahuadavids.wehcat.pojo.HelloIndia" parent="beanTeamplate">
<property name="message1" value="HelloIndia-message1"/>
<property name="message3" value="HelloIndia-message3"/>
</bean>
<bean class="com.huahuadavids.wehcat.pojo.MyBeanPostProcessor" />
- 继承模板
<bean id="beanTeamplate" abstract="true">
<property name="message1" value="Hello bbbb!"/>
<property name="message2" value="Hello aaaa World!"/>
<property name="message3" value="Namaste India!"/>
</bean>
<bean id="helloIndia" class="com.demo.HelloIndia" parent="beanTeamplate">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>
Spring 基于构造函数的依赖注入
// 1 两个互相依赖的类
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
public class SpellChecker {
public SpellChecker(){
System.out.println("SpellChecker constructor" );
}
public void checkSpelling() {
System.out.println("SpellChecker checkSpelling..." );
}
}
// 2. bean的xml配置
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.demo.TextEditor">
<constructor-arg ref="spellChecker"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.demo.SpellChecker">
</bean>
// 3. 使用
AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
// 4 如果构造函数有多个参数,顺序问题,就bean中顺序一样即可 ,最好的方式是指定 index
<bean id="textEditor" class="com.demo.TextEditor">
<constructor-arg ref="spellChecker1" index="0"/>
<constructor-arg ref="spellChecker2" index="1"/>
</bean>
// 5. bean 配置可以指定参数的类型
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="2001"/>
<constructor-arg type="java.lang.String" value="Zara"/>
</bean>
Spring 基于设值函数的依赖注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数
// 1 修改 TextEditor 类
public class TextEditor {
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void setSpellChecker(SpellChecker spellChecker) {
System.out.println("SpellChecker setter");
this.spellChecker = spellChecker;
}
private SpellChecker spellChecker;
public void spellCheck() {
spellChecker.checkSpelling();
}
}
// 2 修改bean xml的配置 关联类作为一个属性传入
<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor">
<property name="spellChecker" ref="spellChecker"/>
</bean>
p-namespace 配置XML
// 加入xml的命名空间
xmlns:p="http://www.springframework.org/schema/p"
// 上述配置可以写成
<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" p:spellChecker-ref="spellChecker" />
内部Bean bean内部配置 Bean
<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor">
<property name="spellChecker">
<bean id="spellChecker" class="com.starbugs.wehcat.pojo.SpellChecker" />
</property>
</bean>
注入集合
集合中还可以注入类
// 1 定义pojo
public class JavaBeanCollection {
private List addressList;
private Set addressSet;
private Map addressMap;
private Properties addressProp;
public void setAddressList(List addressList) {
this.addressList = addressList;
}
public List getAddressList() {
System.out.println("List Elements :" + addressList);
return addressList;
}
public void setAddressSet(Set addressSet) {
this.addressSet = addressSet;
}
public Set getAddressSet() {
System.out.println("Set Elements :" + addressSet);
return addressSet;
}
public void setAddressMap(Map addressMap) {
this.addressMap = addressMap;
}
public Map getAddressMap() {
System.out.println("Map Elements :" + addressMap);
return addressMap;
}
public void setAddressProp(Properties addressProp) {
this.addressProp = addressProp;
}
public Properties getAddressProp() {
System.out.println("Property Elements :" + addressProp);
return addressProp;
}
}
// 2 bean的xml配置
<bean id="javaBeanCollection" class="com.starbugs.wehcat.pojo.JavaBeanCollection">
<property name="addressList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="addressSet">
<set>
<ref bean="address2"/>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<property name="addressMap">
<map>
<entry key="11" value="value-11" />
<entry key="22" value="value-22" />
<entry key ="three" value-ref="address2"/>
</map>
</property>
<property name="addressProp">
<props>
<prop key="one">INDIA</prop>
<prop key="two">Pakistan</prop>
<prop key="three">USA</prop>
<prop key="four">USA</prop>
</props>
</property>
</bean>
spring 自动装配
byName 、 byType
// 1 定义类 使用setter注入
public class TextEditor {
private SpellChecker spellChecker;
private String name;
public void setSpellChecker( SpellChecker spellChecker ){
System.out.println("TextEditor setSpellChecker");
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void setName(String name) {
System.out.println("TextEditor setName");
this.name = name;
}
public String getName() {
return name;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
// 2 xml 的Bean 配置
<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" autowire="byName">
<property name="name" value="huahua" />
</bean>
<bean id="spellChecker" class="com.starbugs.wehcat.pojo.SpellChecker" />
由构造函数自动装配
// 1 类修改为构造方法注入
public TextEditor( SpellChecker spellChecker, String name ) {
this.spellChecker = spellChecker;
this.name = name;
}
// 2 修改xml配置
<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" autowire="constructor">
<constructor-arg ref="spellChecker"/>
<constructor-arg value="huhuhu"/>
</bean>
基于注解的配置
注解来配置依赖注入,会被XML注入所覆盖。
@Required
注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中
public class Student {
private Integer age;
private String name;
@Required
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// 标注了 @required的,必须要出现在xml的property 配置中
<bean id="student" class="com.starbugs.wehcat.pojo.Student">
<property name="name" value="huahua"/>
<property name="age" value="22"/>
</bean>
@Autowired
// @Autowired 修饰 setter ,相当于 配置了 byType 方式自动连接
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
// 2 @Autowired 修饰属性,消除了setter,但是并不是为类自动产生了一个 setter
@Autowired
private SpellChecker spellChecker;
// 3 @Autowired默认必须有Integer 这个bean,否则就报错,加上required = false就不会报错了
@Autowired(required = false)
public void setAge(Integer age) {
this.age = age;
}
@Qualifier
当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配
// 1 xml配置
<bean id="student" class="com.starbugs.wehcat.pojo.Student">
<property name="name" value="huahua"/>
<property name="age" value="22"/>
</bean>
<bean id="student1" class="com.starbugs.wehcat.pojo.Student">
<property name="name" value="name1"/>
<property name="age" value="111"/>
</bean>
// 2 使用类
public class Profile {
@Autowired
@Qualifier("student1")
private Student student;
public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}
@Component
标注一个类,表示声明为一个spring配置的bean
// 1 xml的bean配置 开启组件扫描
<context:component-scan base-package="com.starbugs.wehcat.pojo" />
// 2 pojo类中声明bean的id
@Component("profile")
// 3 具体使用
AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
@PostConstruct 、@PreDestroy
@PostConstruct === init-method
@PreDestroy === destroy-method
@Resources
提供一个bean的name字段注入,不可以用在构造函数上
@Resource(name = "spellChecker")
private SpellChecker spellChecker;
基于java的配置
- 使用这种配置,可以不用xml,使用一个类,作为bean的来源
- 带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。
- @Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean
- @Bean注解也可以配置初始化方法和销毁方法
// 1 创建一个配置类
@Configuration
public class HelloWorldConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
// 2 使用
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}
// 3 还可以加载 refresh 多个配置类
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(HelloWorldConfig.class);
ctx.refresh();
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
@Import 注解:
@import 注解允许从另一个配置类中加载 @Bean 定义
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
// 这个配置可以使用上一个配置的信息
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B a() {
return new A();
}
}
spring事件处理
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知
// 1 实现 ApplicationListener 接口
public class CStartEventHandler implements ApplicationListener<ContextStartedEvent> {
@Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
// 2 xml中配置
<bean id="cStartEventHandler" class="com.starbugs.wehcat.demos.CStartEventHandler" />
<bean id="cStopEventHandler" class="com.starbugs.wehcat.demos.CStopEventHandler" />
自定义事件
// 1 新建 事件类
public class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
@Override
public String toString() {
return "CustomEvent{}";
}
}
// 2 新建 handler类
public class CustomEventHandler implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println(event.toString());
}
}
// 3 新建publisher发布类
public class CustomEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher){
this.publisher = publisher;
}
public void publish() {
CustomEvent ce = new CustomEvent(this);
publisher.publishEvent(ce);
}
}
// 4 配置类到xml里
// 5. 使用
CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher");
cvp.publish();
cvp.publish();
Spring 之 AOP
- 传统的OOP编程,多个类公用同一方法,修改时就要修改所有的文件
- AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时再将这些提取出来的代码应用到需要执行的地方
- AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面
AOP的一些知识点如下:
Aspect(切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类,该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定。
Joinpoint(连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用
Pointcut(切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。
Advice(通知增强处理):AOP框架在特定的切入点执行增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。 Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
Weaving(织入):将切面代码插入目标对象上,从而生成代理对象的过程。
springmvc
DispatcherServlet
Spring web mvc模式是围绕 DispatcherServlet 设计的,工作过程
- 收到一个 HTTP 请求,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器。
- 控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。
- DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。
- 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器
如何使用
// 1. 安装依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
// 2 web.xml 中配置 这里的ervelet的class不是具体的实现类了,而是DispatcherServlet这个类,会去找当前目录下的 servletname + servlet.xml 配置
<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
// 3 HelloWeb-servlet.xml 配置
// 开启组件扫描
<context:component-scan base-package="web" />
// 视图如何渲染
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
// 4 定义controller
@Controller
@RequestMapping("/hello")
public class HelloController{
@RequestMapping(method = RequestMethod.GET)
public String printHello(ModelMap model) {
model.addAttribute("message", "Hello Spring MVC Framework!");
return "hello";
}
}
// 5 jsp 页面
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<head>
<title>Helldddo World</title>
</head>
<body>
<h2>${message}</h2>
</body>
</html>
@ModelAttribute
// 1. 这里的 @ModelAttribute 在请求之前 对model做一些前置处理
@ModelAttribute
public void setData(@RequestParam(defaultValue = "default", required = false) String type, Model model) {
System.out.println(type);
model.addAttribute("types", type);
}
// 2. 这里的 @ModelAttribute 在请求之前 对model加一个名字是value1,值是请求参数val的值
@ModelAttribute("value1")
public String setData1(@RequestParam(defaultValue = "default1", required = false) String val, Model model) {
return val;
}
// 3. 这里的 @ModelAttribute 不指定返回属性名字,就用返回类的名字转小写,作为属性名字
@ModelAttribute
public Student1 setData2() {
Student1 s = new Student1();
s.setName("student-name-huahua");
return s;
}
// 4 这种返回的不是视图名字,而是model的值,视图名字是student1,model.setAttr("name3", "values is names")
@RequestMapping(value = "/student1", method = RequestMethod.GET)
@ModelAttribute("name3")
public String setData3(){
return "values is names";
}
// 5 作为函数的参数,取的是getD方法的返回值,放到model里
@ModelAttribute("user")
public Student1 getD(){
Student1 s = new Student1();
s.setName("s1");
s.setAge(11);
return s;
}
@RequestMapping(value = "/data", method = RequestMethod.GET)
public String data(@ModelAttribute("user") Student1 s) {
s.setName("name set in RequestMapping");
return "data";
}
// 6 作为函数参数 从Form表单或URL参数中获取
@Controller
public class HelloWorldController {
@RequestMapping(value = "/helloWorld")
public String helloWorld(@ModelAttribute Student1 user) {
return "helloWorld";
}
}
// 7 作为方法返回值,就是把user2放到model中
@RequestMapping(value = "/data", method = RequestMethod.GET)
public @ModelAttribute("user2") Student1 data(@ModelAttribute Student1 s1) {
return new Student1().setName("huahua");
}