文章目录

  • 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: 该注解有两个用途,

  1. @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();
   }
}
  1. @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>

spring 自动trim注解_spring


@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>

spring 自动trim注解_注解_02

@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";
    }
 }

spring 自动trim注解_spring_03


spring 自动trim注解_spring 自动trim注解_04


当然,为了安全,密码等参数是不会放到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";
    }

spring 自动trim注解_spring_05


@PathVariable:很多时候,打开网站上的个人中心界面,网站的url后面一般都会加一个后缀,用来区别用户,比如我现在查看我的个人博客界面,url上就加了我的用户名用来区别。

spring 自动trim注解_html_06


那问题来了,那后台的控制器怎么识别这个url请求呢,总不能一个用户就对应一个处理器方法响应把。解决的办法就是利用占位符+ @PathVariable注解。

首先需要在@RequestMapping中使用占位符(即大括号{}),占位符内为占位符名称。占位符可以匹配任意值,比如在下面的例子中,@RequestMapping中的{name}就是一个名为name的占位符,它可以匹配到任意值,如index/asdadindex/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>

spring 自动trim注解_spring_07

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对应了控制层,从某种程度上来说,就可以很方便的分清项目的结构。