目录
@Component、@Responsity、@Service、@Controller
作用在类上,表明这些类是交给spring容器进行管理的。
@Controller
控制器,类中注入服务
@RestController
public class DamsController {
@Autowired
MetaDataService metaDataService;
@PostMapping("createMeta")
public SimpleResp metaDataScan(String id,String cipherKey) {
try {
metaDataService.scan(id, cipherKey);
return new SimpleResp(true);
} catch (Exception e) {
log.error("metaDataScan fail", e);
return new SimpleResp(false, e.getMessage());
}
}
}
@Service
服务,默认名称是类名(头字母小写),可以@Service(“xxxx”)这样来指定,类中注入DAO
@Service
public class MetaDataService {
@Autowired
MetaDataDao metaDataDao;
public void scan(String id, String cipherKey) throws Exception {
List<DataDbms> dataDbmsList = metaDataDao.getDataDbmsList(id);
//...
}
}
上述代码让Spring创建一个名字叫metaDataService
的实例
@Responsity
DAO,实现DAO访问
@Repository
public class MetaDataDao {
@Autowired
JdbcTemplate jdbcTemplate;
public List<DataDbms> getDataDbmsList(String id) {
String sql = "SELECT * FROM test";
if (id != null && !id.isEmpty()) sql += (" where id=" + id);
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(DataDbms.class));
}
上述代码让Spring创建一个名字叫metaDataDao
的实例
@Component
把普通ORM实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>
@RestController与@Controller区别
官方文档:@RestController is a stereotype annotation that combines @ResponseBody and @Controller.
意思是:@RestController注解相当于@ResponseBody + @Controller合在一起的作用。web开发用Controller,REST项目用RestController
- 使用@Controller注解,在对应的方法上,视图解析器可以解析return的jsp,html页面,并且跳转到相应页面。若返回json等内容到页面,则需要加@ResponseBody注解
@CrossOrigin
@Controller
public class FileUploadController {
//跳转到上传文件的页面
@RequestMapping(value="/gouploadimg", method = RequestMethod.GET)
public String goUploadImg() {
//跳转到 templates 目录下的 uploadimg.html
return "uploadimg";
}
//处理文件上传
@RequestMapping(value="/testuploadimg", method = RequestMethod.POST)
public @ResponseBody String uploadImg(@RequestParam("file") MultipartFile file,
HttpServletRequest request) {
System.out.println("调用文件上传方法");
String contentType = file.getContentType();
String fileName = file.getOriginalFilename();
- @RestController注解,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器InternalResourceViewResolver无法解析jsp,html页面,直接返回的内容就是Return里的内容
@CrossOrigin
@RestController
public class HospitalController {
//注入Service服务对象
@Autowired
private HospitalService hospitalService;
/**
* 查询所有医院信息(未分页)
*/
@RequestMapping(value = "findAllHospital",method = RequestMethod.GET)
public List<Hospital> findAllHospital(){
List<Hospital> hospitalList= hospitalService.findAllHospital();
return hospitalList;
}
@Configuration和@Component区别
一句话概括就是@Configuration中所有带@Bean注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
@Configuration定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
从定义来看, @Configuration 注解本质上还是 @Component,因此 <context:component-scan/>
或者 @ComponentScan
都能处理@Configuration 注解的类
@Configuration 标记的类必须符合下面的要求:
- 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
- 配置类不能是 final 类(没法动态代理)。
- 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
- 配置类必须是非本地的(即不能在方法中声明,不能是 private)。
- 任何嵌套配置类都必须声明为static。
- @Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。
加载过程
Spring 容器在启动时,会加载默认的一些PostProcessor
,其中就有 ConfigurationClassPostProcessor
,这个后置处理程序专门处理带有 @Configuration 注解的类,这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。
@Bean
@Configuration
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}
上例中直接调用 country() 方法返回的是同一个实例,实际调用的是spring中cglib的代理(源码略)
@Component 注解并没有通过 cglib 来代理@Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country,如下:
@Component
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}
有些特殊情况下,我们不希望 MyBeanConfig 被代理(代理后会变成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293)时,就得用 @Component,这种情况下,上面的写法就需要改成下面这样:
@Component
public class MyBeanConfig {
@Autowired
private Country country;
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country);
}
}
这种方式可以保证使用的同一个 Country 实例,在 @Configuration 注解定义的 bean 方法中可以直接调用方法,不需要 @Autowired 注入后使用。
@Resource、@Autowired
用来修饰字段、构造函数或者设置方法,spring容器会自动的将我需要的属性、方法或对象提供出来。有时候可以替换使用,有时则不可以。
- 可互换
@Resource和@Autowired都可以作为注入属性的修饰,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换,不影响使用。
- 不能互换
@Resource是Java自己的注解,@Resource有两个属性是比较重要的,分是name和type;Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Autowired是spring的注解,是spring2.5版本引入的,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。
public interface Human {
String runMarathon();
}
@Service
public class Man implements Human {
public String runMarathon() {
return "A man run marathon";
}
}
@Service
public class Woman implements Human {
public String runMarathon() {
return "An woman run marathon";
}
}
@RestController
@RequestMapping("/an")
public class HumanController {
@Resource
private Human human;
@RequestMapping("/run")
public String runMarathon() {
return human.runMarathon();
}
}
HumanController里的Human直接使用@Resource报错expected single matching bean but found 2: man,woman
,@Autowired报错required a single bean, but 2 were found:- man: defined in file [...] - woman: defined in file [...]
,正确写法:
@Resource(name="woman")
private Human human;
//或:
@Resource
@Qualifier("woman")
private Human human;
//或:
@Autowired
@Qualifier("woman")
private Human human;
//或:
//@Primary是修饰实现类的告诉spring,如果有多个实现类时,优先注入被其注解修饰的那个。
@Service
@Primary
public class Man implements Human {
public String runMarathon() {
return "A man run marathon";
}
}
上文中注入了一个字段@Resource private Human human;
这个表示字段名为human,也就是表示需要注入名字是human的bean,但是并没有名字为human的bean,这时就会走byType注入, 所以说这个注入成功是根据byType注入成功的,而不是byName。
@RequestMapping
一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestParam
@RequestParam(value=”参数名”, required=true/false, defaultValue=””)
- value:请求参数名(必须配置)单一参数时,可简化如下:
@GetMapping("edit1")
public String edit1(@RequestParam("userId") Integer userId, Model model) {
-
required:是否必需,默认为true,即请求中必须包含该参数,如果没有包含,将会抛出异常
-
defaultValue:默认值,如果设置了该值,required将自动设为false(无论你是否配置了required,配置了什么值,都是false),如果没有传该参数,就使用默认值
-
接收传递过来的数组:
@PostMapping("delete")
@ResponseBody
public RestResponse delete(@RequestParam(value = "ids[]", required = false) List<Long> ids) {
if (null == ids) {
return RestResponse.failure("ID不能为空" );
}
for (Long id : ids) {
rongheMobileListService.deleteById(id);
}
return RestResponse.success();
}
- 接收传递过来的Map<String, Object>:
@PostMapping("entry")
@ResponseBody
public RestResponse add(
@RequestParam(value = "data[mobile]") String mobile,
@RequestParam(value = "data[pro]") String pro,
@RequestParam(value = "data[realTime]", required = false) String realTime,
@RequestParam(value = "data[sjmc]", required = false) String sjmc,
@RequestParam(value = "data[mobBm]", required = false) String mobBm,
@RequestParam(value = "data[mobMc]", required = false) String mobMc,
@RequestParam(value = "data[orderId]", required = false) String orderId,
@RequestParam(value = "data[sn]") String sn,
@RequestParam(value = "data[bakMobile]", required = false) String bakMobile) {
return RestResponse.success();
}
@Conditional
Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。定义如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
//传入一个Class数组(多种条件类,与的关系),需要继承Condition接口
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
例子:
public class Person {
private String name;
private Integer age;
// Getter Setter
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
/**
* BeanConfig类,用于配置两个Person实例并注入
**/
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
/**
* 行验证这两个Bean是否注入成功
**/
public class ConditionalTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test1(){
String osName = applicationContext.getEnvironment().getProperty("os.name");
System.out.println("当前系统为:" + osName);
Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
System.out.println(map);
}
}
根据当前操作系统来注入Person实例,windows下注入bill,linux下注入linus
- 首选实现Condition接口:
public class WindowsCondition implements Condition {
/**
* @param conditionContext:判断条件能使用的上下文环境
* @param annotatedTypeMetadata:注解所在位置的注释信息
* */
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获得当前系统名
String property = environment.getProperty("os.name");
//包含Windows则说明是windows系统,返回true
if (property.contains("Windows")){
return true;
}
return false;
}
}
//conditionContext提供了多种方法,方便获取各种信息,是SpringBoot中@ConditonalOnXX注解多样扩展的基础
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Linux")){
return true;
}
return false;
}
}
接着就是使用这两个类了,因为此注解可以标注在方法上和类上,所以分开测试:
- 标注在方法上,一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入
@Configuration
public class BeanConfig {
//只有一个类时,大括号可以省略
//如果WindowsCondition的实现方法返回true,则注入这个bean
@Conditional({WindowsCondition.class})
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
//如果LinuxCondition的实现方法返回true,则注入这个bean
@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
- 标注在类上,一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入
@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
@Transient
- 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问;
- transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口;
- 被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化;
简单地说,@transient就是在给某个javabean上需要添加个属性,但是这个属性你又不希望给存到数据库中去,仅仅是做个非静态的临时变量用一下,不修改已经存在数据库的数据的数据结构。