文章目录
- bean的自动创建和自动装配
- 通过Java代码新建bean
- 消除自动装配的歧义
- SpringMVC
- 配置
- 控制层(Controller)
- Model层
- 关于`@Repository`、`@Service`,`@Controller`
bean的自动创建和自动装配
@Component
: 标注一个类,spring会自动用默认无参的构造方法
自动创建一个bean,该bean的id默认为类名首字母小写,也可以用@Component(“xxx”)将bean的id设置成xxx@ComponentScan
:标注一个类(一般该类都是配置类,即被@Configuration
标注的类),表示启用组件扫描,默认会扫描与配置类相同的包及这个包下的所有子包,查找带有@Component
注解的类,没有被扫描到@Component
不会自动创建bean。 可以用basePackages属性自定义要扫描的基础包@Configuration
:标注一个类,把该类作为spring的配置类,该类可以替代xml配置文件。这个配置类其实就相当于spring 的一个xml配置文件
@Configuration
@ComponentScan(basePackages = "com.spring.*")
public class Hello {
//把Hello类当成配置类,并且启用组件扫描,扫描的路径为com.spring包及其子包
}
相当于xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.spring.*"/>
</beans>
@Autowired
:可以标注属性或者任意方法。如果标注的是属性,spring会在应用上下文中的所有bean(不论是自动创建还是在xml中声明)找到可以匹配的bean进行自动装配;如果标注的是方法,spring会尝试满足方法参数中的所有需要依赖。如果有多个满足的bean,spring会抛出异常
@Autowired
Hello hello;//自动找到类型为Hello或Hello子类的bean进行注入
@Autowired
public void test(Hello hello){ //@Autowired注解标注test方法,方法参数为Hello类型,spring会自动找到类型为Hello或Hello子类的bean进行注入到hello参数
this.hello = hello;
}
Named
:该注解由java注入规范提供,可用来替代spring的@Component注解,用法也一致。两者有细微差异,但绝大多数情况下可以相互代替@Inject
:该注解有Java依赖规范提供,但spring也支持,使用方法和作用@Autowired
差不多,大多数情况下可以相互替代
通过Java代码新建bean
@Bean
: 标注一个方法。@Bean
注解会告诉spring,这个方法将会返回一个对象,该对象将要注册为spring应用上下文的一个bean。该bean的id默认与@Bean
所标注的方法名一致,但是也可以通过name属性指定一个新id。一般来说,现在的springboot项目也大多采用这种方法生成bean完成一些环境的配置 。另外,值得注意的是,@Bean
注解一般用在@Configuration
标注的配置类里,这样生成的bean就可以被spring容器统一管理。其实换个角度很好理解,配置类相当于xml配置文件,@Bean
相当于xml里的<bean>
标签。
@Configuration
public class Hello {
@Bean
//@Bean(name="manzuo") 也可以通过name属性为bean指定一个新的id
public Hello test(){//将会新建一个类型为Hello,id为test的bean
return new Hello();
}
}
特别说明:默认情况下,spring中的bean都是单例的,所以spring会拦截@Bean
注解所标注的方法的调用并确保返回的是spring生成的bean。以上面的test()方法为例,通过@Bean
注解,spring会生成一个类型为Hello,id为test的bean。当其他方法调用test()方法时候,spring会拦截对test()调用,并直接返回之前spring生成的bean。这样就确保了该bean始终是单例的,而不是调用多少次test()方法就new多少个Hello对象。
消除自动装配的歧义
@Primary
: 通常和@Bean
或者 @Component
注解一起使用,将该bean设置成首选的。当使用@Autowired
自动装配的时候,如果遇到两个以上符合类型的bean的时候,spring会优先选择有@Primary
标注的bean;如果两个或者两个以上符合类型的bean都没有@Primary
注解标识,spring不会自动帮你选择其中一个bean进行自动注入,而是抛出异常提醒你。@Qualifier
: 该注解有两个用途,
- 与
@Bean
或者@Component
一起使用可以为该bean创建一个自定义的限定符(默认情况下,bean的限定符和Bean的id一致)
public class Hello {
@Bean(name="manzuo")
@Qualifier("cool")
//spring 会创建一个 id为manzuo,限定符为cool的bean
public Hello test(){
return new Hello();
}
}
- 与
@Autowired
一起使用,可以通过限定符选择要装配的bean
@Autowired
@Qualifier("cool")
//spring 会在可装配(即类型为Hello)的bean中找到限定符 为cool的bean自动装配
public void setHello(Hello hello){
this.hello = hello;
}
SpringMVC
配置
@EnableWebMvc
:启用SpringMVC功能,一般@Configuration
搭配使用,即标注配置类,相当于在XML中配置 <mvc:annotation-driven />
。
下面是一个最简单的SpringMVC配置
@Configuration
@EnableWebMvc //启动springMVC, 相当于在XML中配置 <mvc:annotation-driven />
@ComponentScan("com.spring.*") //需要开始组件扫描,才能扫描到@Controller
public class WebConfig extends WebMvcConfigurationSupport {
@Bean
public ViewResolver viewResolver() { //配置视图解析器
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
return viewResolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//配置静态资源的处理,使得静态资源转发到Servlet 容器中的默认Servlet上
configurer.enable();
}
}
控制层(Controller)
@Controller
:标注一个类,使得该类成为控制类,可以进行前端请求的处理,转发,重定向等等。@Comtroller
是基于@Component
注解的,所以也会被@ComponentScan
自动扫描扫描到。@Controller
标注的类也会被自动声明成Spring上下文的一个Bean。需要注意的是,但是@Controller
需要配合@RequestMapping
一起使用,@RequestMapping
标注的方法才是真正的处理器,处理前端请求,单独的@Controller
并无太大用处,只能声明这是一个控制器类。@RequestMapping
:可以标注一个控制类或者控制类里的一个方法。value属性指定了该方法(或者类)所要处理的请求路径,method属性(默认为get)设置了所处理的HTTP方法(比如get或者post)。标注的方法一般返回值是String类型的,即一个视图名,该视图名将被视图解析器解析,找到相应的名字的jsp/html,并返回给客户端。(当然,方法返回值也可以是json类型的,直接返回一个json给前端)
//使用@Controller注解,该类成为一个控制器类
@Controller
public class HomeController {
//@RequestMapping注解的方法可以处理请求,
//该方法可以处理 请求方法为get,url为"/index” 的请求
//返回的 “index”被视图解析器解析,找到 index.jsp或index.html视图返回
@RequestMapping(value = "/index"", method = RequestMethod.GET)
public String home() {
return "index"";
}
}
对应的index.jsp内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
Hello World!
</body>
</html>
@RequestParam
: 一般和@RequestMapping
一起配合使用,@RequestMapping
注解方法,@RequestParam
用来注解方法的形参,@RequestParam
可以读取url后附带的参数,默认情况下,会根据所标注的形参名
与url参数名
进行匹配绑定 ,如果形参名和url的参数名不一致,可以使用value属性指定参数的名称,defaultValue属性指定形参的默认值。
更改下刚刚的controller类,简单实现下前后端数据交互
@Controller
public class HomeController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
//这个例子用了三个@RequestParam注解
//第一个标注的形参名为name,它会自动读取url参数里的name参数的值
//第二个标注的形参名为psw,但用value属性规定了它读取的是url参数里的password参数的值
//第二个标注的形参名为age,它会自动读取url参数里的age参数的值,但是如果没读取到,它的默认值将会是11
public String home(@RequestParam String name,@RequestParam(value = "password") String psw,@RequestParam(defaultValue ="11") int age,Model model) {
model.addAttribute("name",name);
model.addAttribute("password",psw);
model.addAttribute("age",age);
return "index";
}
}
再更改一下index.jsp,用El表达式展示一下前端传到后端的值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
Hello World! <br>
name:${name} <br>
passwod:${password} <br>
age:${age}
</body>
</html>
@ModelAttribute
:标注一个model对象,可以把前端传的参数自动绑定到model对象的相应属性里。数据绑定的时候是通过model对象的setter方法自动绑定的,所以setter方法必须和前端传的参数名保持一致,比如前端传了username和password两个参数,@ModelAttribute
标注的model对象必须有两个setter方法,一个是setUsername(),另一个是setPassword()。 数据绑定可能会因为没有相应方法,或者类型不匹配出现错误。
现在我们先来新建一个实体对象User,这个User类没有任何特殊的地方,就是三个私有变量name,password,age和相应的getter/setterr方法而已
public class User {
private String name;
private String password;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
现在我们再改一下控制器类:
@Controller
public class HomeController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
//用@ModelAttribute标注了一个User对象,user对象里有三个属性name,password,age
//所以会自动查找前端传的参数里是否有这三个参数,如果有,则根据参数名进行匹配完成数据绑定
//如果没有,相应的属性则为空
public String home(@ModelAttribute User user,Model model) {
model.addAttribute("name",user.getName());
model.addAttribute("password",user.getPassword());
model.addAttribute("age",user.getAge());
return "index";
}
}
当然,为了安全,密码等参数是不会放到url里的,而是放到POST请求体里,而@ModelAttribute
同样可以读取POST请求体里的内容,但是@RequestParam
就只能读取url里的参数。
我们只需要改下控制器类里相关处理器的method参数就可以了,如下,把method = RequestMethod.GET
改为 method = RequestMethod.POST
,这样就能响应POST请求了
@Controller
public class HomeController {
@RequestMapping(value = "/index", method = RequestMethod.POST)
public String home(@ModelAttribute User user,Model model) {
model.addAttribute("name",user.getName());
model.addAttribute("password",user.getPassword());
model.addAttribute("age",user.getAge());
return "index";
}
@PathVariable
:很多时候,打开网站上的个人中心界面,网站的url后面一般都会加一个后缀,用来区别用户,比如我现在查看我的个人博客界面,url上就加了我的用户名用来区别。
那问题来了,那后台的控制器怎么识别这个url请求呢,总不能一个用户就对应一个处理器方法响应把。解决的办法就是利用占位符
+ @PathVariable
注解。
首先需要在@RequestMapping
中使用占位符(即大括号{}
),占位符内为占位符名称。占位符可以匹配任意值,比如在下面的例子中,@RequestMapping
中的{name}
就是一个名为name的占位符,它可以匹配到任意值,如index/asdad
、index/fffff
等等等,但是这个占位符不能为空,也就是说index
或者index/
的形式是匹配不到的,可以存在多个占位符,比如index/{name}/{age}
。而@PathVariable
注解的作用就是按照占位符的名字获取占位符的内容,并赋值给标注的属性(一般都是String类型的)
@RequestMapping(value = "/index/{name}", method = RequestMethod.GET)
public String home(@PathVariable("name")String name, Model model){
model.addAttribute("name",name);
return "index";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
Hello World! <br>
name:${name} <br>
</body>
</html>
Model层
在SSM框架(Spring+Spring MVC+mybatis)里,Model层也称为为Entity层,因为里面的类都是用@Entity
标注的。Entity层里的类一般是和数据库的某张表相映射,也就是说,类里的成员变量和数据库的表的字段一一对应,这样的话,类的对象就可以是表的一条记录,对表的增删查改就可以转变成对java对象的操作,这就极大的方便了数据的保存和修改。@Entity
标注一个类,表明这是一个实体类,与数据库里的某张表映射。@Table(name="xx")
:与@Entity
配合使用,指定实体类要映射的表名为xx
@Id
: 标注类的一个属性(也只能标注一个属性),这个属性将成为要映射的表的主键。注意,@Entity
必须和@Id
配合使用,因为表是必须要有主键的@GeneratedValue
:与@Id
配合使用,主要作用是定义主键的生成策略,自动生成唯一可以标识id。一般是设置成@GeneratedValue(strategy = GenerationType.AUTO)
,关于这个注解更多的相关知识可以看 这篇博客@Column(name = "xxx")
:标注一个类的一个属性,声明这个属性和表的xxx字段相映射。
值得一提的是:以往对与实体类,设置相应属性的之后,因为java的封装概念,这些属性都是私有的,所以还要为相应的属性生成getter和setter方法,即使是可以用快捷键自动生成,但是一大串的getter和setter方法还是有点影响阅读,并且如果改了某个属性名,还要对相应的getter和setter方法进行更改,略微不便。所以就有了可以自动生成getter/setter方法的注解。
pom依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version> 1.18.8</version>
</dependency>
使用:@Data
:直接标注实体类,该注解会根据类里的声明的属性,自动生成getter/setter方法(但是我们是看不到的)
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
@Column(name = "password")
private String password;
}
关于@Repository
、@Service
,@Controller
(如有错误,请读者指正)
关于这三个注解,我查了一些相关资料,目前来说,这三个注解和@Component
是等效的,都需要 @ComponentScan
的扫描,标注的类会被调用默认的无参的构造方法创建成Bean。不排除以后会加入特殊的功能。
但是从名字来看,@Repository
对应了持久层,@Service
对应业务逻辑层,@Controller
对应了控制层,从某种程度上来说,就可以很方便的分清项目的结构。