本文已参与「新人创作礼」活动,一起开启掘金创作之路。
注解 @SpringBootApplication
@SpringBootApplication 注解实际上封装了以下三个注解:
- @SpringBootConfiguration 配置类注解
- @EnableAutoConfiguration 启用自动配置注解
- @ComponentScan 组件扫描注解
关于注解 @ServletComponentScan 和 @ComponentScan:
@ComponentScan(“{package name}”) 自动扫描包名下所有使用 @Service、@Component、@Repository 和 @Controller 的类,并注册为 @Bean 。
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
Spring MVC 视图解析器相关配置
-
新增 JSP 和 JSTL 的 Maven 依赖配置 -
pom.xml<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <scope>provided</scope> </dependency> -
定义视图前后缀配置 -
application.propertiesserver.port =8090 spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp -
新建控制器
IndexController.java这里定义了一个映射为 /index 的路径,然后方法返回了“index”,这样它就与之前配置的前缀和后缀结合起来寻找对应的 jsp 文件,为此还需要开发一个对应的
index.jsp文件。package com.springboot.chapter2.main; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { @RequestMapping("/index") public String index() { return "index"; } }
自定义注解 @interface 和 @AliasFor 使用
@interface 参考文章:Java 注释 @interface 的用法
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@AliasFor 注解的几种使用方式
1. 在同一个注解中显示使用,将注解中的多个属性互相设置别名
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
//...
}
为什么要给 value 属性和 path 属相互相设置别名也是有原因的。在 Spring 中给 value 属性设置值是可以省略属性的,比如可以写成:
RequestMapping("/foo")
这样写比较简洁,但是这样可读性不高,并不知道 value 属性代表什么意思。如果给这个属相设置一个 path 别名的话我们就知道这个是在设置路径。
但是要注意一点,@AliasFor 标签有一些使用限制:
- 互为别名的属性属性值类型,默认值,都是相同的;
- 互为别名的注解必须成对出现,比如 value 属性添加了 @AliasFor("path"),那么 path 属性就必须添加 @AliasFor("value");
- 互为别名的属性必须定义默认值。
如果违反了别名的定义,在使用过程中就会报错。
2. 给元注解中的属性设定别名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//...
}
@SpringBootApplication 这个注解是由其他几个注解组合而成的。下面的代码就是在给 @ComponentScan 注解的 basePackages 属性设置别名 scanBasePackages。如果不设置 attribute 属性的话就是在给元注解的同名属性设置别名。
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
这种使用方式的好处是可以将几个注解的功能组合成一个新的注解。
@AliasFor 的实现代码
@AliasFor 的具体实现在 AnnotationUtils.findAnnotation 中。
依赖注入和 @Autowired
依赖注入(Dependency Injection, DI)即为定义 Bean 之间的依赖。
@Autowired 可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired 的使用来消除 set, get 方法。在使用 @Autowired 之前,我们这样对一个 Bean 配置属性:
<property name="属性名" value=" 属性值"/>
通过这种方式来,配置比较繁琐,而且代码比较多。在 Spring 2.5 引入了 @Autowired 注释。下面用案例来具体说明:
UserRepository.java
package com.proc.bean.repository;
public interface UserRepository {
void save();
}
这里定义了一个 UserRepository 接口,其中定义了一个 save() 方法。
UserRepositoryImps.java
package com.proc.bean.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class UserRepositoryImps implements UserRepository{
@Override
public void save() {
System.out.println("UserRepositoryImps save");
}
}
定义一个 UserRepository 接口的实现类,并实现 save() 方法,在这里指定了该 Bean 在 IoC 中标识符名称为 userRepository。
UserService.java
package com.proc.bean.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.proc.bean.repository.UserRepository;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void save() {
userRepository.save();
}
}
这里需要一个 UserRepository 类型的属性,通过 @Autowired 自动装配方式,从 IoC 容器中去查找到,并返回给该属性。
applicationContext.xml
<context:component-scan base-package="com.proc.bean" />
测试代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
输出结果:UserRepositoryImps save
在使用 @Autowired 时,首先在容器中查询对应类型的 Bean
- 如果查询结果刚好为一个,就将该 Bean 装配给 @Autowired 指定的数据
- 如果查询的结果不止一个,那么 @Autowired 会根据名称来查找
- 如果查询的结果为空,那么会抛出异常。解决方法时,使用 required=false
举例说明,在上面例子中,我们再定一个类来实现 UserRepository.java 接口
package com.proc.bean.repository;
import org.springframework.stereotype.Repository;
@Repository
public class UserJdbcImps implements UserRepository {
@Override
public void save() {
System.out.println("UserJdbcImps save");
}
}
这时在启动容器后,在容器中有两个 UserRepository 类型的实例,一个名称为 userRepository,另一个为 userJdbcImps。
输出结果:UserRepositoryImps save。这里由于查询到有两个该类型的实例,那么采用名称匹配方式,在容器中查找名称为 userRepository 的实例,并自动装配给该参数。
如果这里想要装载 userJdbcImps 的实例,除了将字段 userRepository 名称改成 userJdbcImps 外,还可以使用 @Qualifier 标记,来指定需要装配 Bean 的名称,代码这样写:
@Autowired
@Qualifier("userJdbcImps")
private UserRepository userRepository;
输出结果:UserJdbcImps save
也可以在
UserJdbcImps.java实现类中添加 @Primary 注解,表示当发现有多个同样类型的 Bean 时,优先使用此实现类进行注入。