注解介绍
从广义上Spring注解可以分为两类:
一类注解是用于注册Bean
假如IOC容器就是一间空屋子,首先这间空屋子啥都没有,我们要吃大餐,我们就要从外部搬运食材和餐具进来。这里把某一样食材或者某一样餐具搬进空屋子的操作就相当于每个注册Bean的注解作用类似。注册Bean的注解作用就是往IOC容器中放(注册)东西! 用于注册Bean的注解: 比如@Component , @Repository , @ Controller , @Service , @Configration这些注解就是用于注册Bean,放进IOC容器中,一来交给spring管理方便解耦,二来还可以进行二次使用,啥是二次使用呢?这里的二次使用可以理解为:在你开始从外部搬运食材和餐具进空屋子的时候,一次性搬运了猪肉、羊肉、铁勺、筷子四样东西,这个时候你要开始吃大餐,首先你吃东西的时候肯定要用筷子或者铁勺,别说你手抓,只要你需要,你就会去找,这个时候发现你已经把筷子或者铁勺放进了屋子,你就不用再去外部拿筷子进屋子了,意思就是IOC容器中已经存在,就可以只要拿去用,而不必再去注册!而拿屋子里已有的东西的操作就是下面要讲的用于使用Bean的注解!
一类注解是用于使用Bean
用于使用Bean的注解:比如@Autowired , @Resource注解,这些注解就是把屋子里的东西自己拿来用,如果你要拿,前提一定是屋子(IOC)里有的,不然就会报错,比如你要做一道牛肉拼盘需要五头牛做原材料才行,你现在锅里只有四头牛,这个时候你知道,自己往屋子里搬过五头牛,这个时候就直接把屋子里的那头牛直接放进锅里,完成牛肉拼盘的组装。是的这些注解就是需要啥想要啥,只要容器中有就往容器中拿!而这些注解又有各自的区别,比如@Autowired用在筷子上,这筷子你可能只想用木质的,或许只想用铁质的,@Autowired作用在什么属性的筷子就那什么筷子,而@Resource如果用在安格斯牛肉上面,就指定要名字就是安格斯牛肉的牛肉。
@Autowired
首先了解一下IOC操作Bean管理,bean管理是指(1)spring创建对象 (2)spring注入属性。当我们在将一个类上标注@Service或者@Controller或@Component或@Repository注解之后,spring的组件扫描就会自动发现它,并且会将其初始化为spring应用上下文中的bean。 而且初始化是根据无参构造函数。
1.介绍
使用@Bean
(或者@Component
)注解将Bean注册到了Spring容器;我们创建这些Bean的目的,最终还是为了使用,@Autowired
注解可以将bean自动注入到类的属性中。
- 当标注的属性是接口时,其实注入的是这个接口的实现类, 如果这个接口有多个实现类,只使用@Autowired就会报错,因为它默认是根据类型找,然后就会找到多个实现类bean,所有就不知道要注入哪个。然后它就会根据属性名去找。所以如果有多个实现类可以配合@Qualifier(value=“类名”)来使用 (是根据名称来进行注入的)
@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。在使用@Autowired之前,我们对一个bean配置起属性时,是这样用的
<property name="属性名" value=" 属性值"/>
通过这种方式来,配置比较繁琐,而且代码比较多。在Spring 2.5 引入了 @Autowired 注释
2.例子
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" />
测试代码:
1 ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); 2 3 UserService userService=(UserService) ctx.getBean("userService"); 4 userService.save();
输出结果:UserRepositoryImps save
@Bean
1.介绍
Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。 产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。@Bean明确地指示了一种方法,什么方法呢?产生一个bean的方法,并且交给Spring容器管理;从这我们就明白了为啥@Bean是放在方法的注释上了,因为它很明确地告诉被注释的方法,你给我产生一个Bean,然后交给Spring容器,剩下的你就别管了。记住,@Bean就放在方法上,就是让方法去产生一个Bean,然后交给Spring容器。
2.使用
如下就能让accountDao方法产生一个AccountDao 对象,然后这个AccountDao 对象交给Spring管理
class A{ @Bean public AccountDao accountDao(){ return new AccountDao(); } }
实际上,@Bean注解和xml配置中的bean标签的作用是一样的。
不知道大家有没有想过,用于注册Bean的注解的有那么多个,为何还要出现@Bean注解?
原因很简单:类似@Component , @Repository , @ Controller , @Service 这些注册Bean的注解存在局限性,只能局限作用于自己编写的类,如果是一个jar包第三方库要加入IOC容器的话,这些注解就手无缚鸡之力了,是的,@Bean注解就可以做到这一点!当然除了@Bean注解能做到还有@Import也能把第三方库中的类实例交给spring管理,而且@Import更加方便快捷,只是@Import注解并不在本篇范围内,这里就不再概述。
使用@Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。
3.总结
1、Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。 产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
2、@Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给spring管理。
3、@Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。
4、记住,@Bean就放在方法上,就是让方法去产生一个Bean,然后交给Spring容器,剩下的你就别管了。
@Component
1.介绍
@component: 标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)
@Component,@Service,@Controller,@Repository注解的类,并把这些类纳入进spring容器中管理。
下面写这个是引入component的扫描组件
<context:component-scan base-package=”com.mmnc”>
其中base-package为需要扫描的包(含所有子包)
1、@Service用于标注业务层组件
2、@Controller用于标注控制层组件(如struts中的action)
3、@Repository用于标注数据访问组件,即DAO组件.
4、@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
getBean的默认名称是类名(头字母小写),如果想自定义,可以@Service(“***”)这样来指定,这种bean默认是单例的
@ConditionalOnBean
1.介绍
在 Spring4 中推出了注解,方便程序根据当前环境或者容器情况来动态注入 bean。
@ConditionalOnBean 注解用来指定多个 bean,只有当指定的所有 bean 均已经包含在 BeanFactory 中时才匹配 @Conditional 注解。但是同一 bean 不必满足这些要求。
2.例子
当 @ConditionalOnBean 注解放置在 @Bean 方法上时,bean 类默认为工厂方法的返回类型,例如:
@Configuration public class MyAutoConfiguration { @ConditionalOnBean @Bean public MyService myService() { ... } }
在上面的示例中,如果 BeanFactory 中已经包含 MyService 类型的 Bean,则条件将匹配。
该条件只能匹配到目前为止应用程序上下文已处理的 bean 定义。因此,强烈建议仅在自动配置类上使用此条件。如果候选 bean 可能是由另一种自动配置创建的,请确保使用此条件的 bean 在之后运行。
@ConditionalOnProperty
1.介绍
通常,在开发基于 Spring 的应用程序时,我们可能需要根据配置属性的存在和值有条件地创建一些 bean。
例如,我们可能想要注册一个 DataSource bean 以指向生产或测试数据库,这取决于我们将属性值设置为“prod”还是“test”。
幸运的是,实现这一目标并不难。 Spring 框架正是为此目的提供了 @ConditionalOnProperty 注解。
简而言之,@ConditionalOnProperty 仅在环境属性存在且具有特定值时才启用 bean 注册。 默认情况下,目标属性要被定义并且不等于false。
现在我们已经熟悉了 @ConditionalOnProperty 注解的用途,让我们深入了解它是如何工作的。
2.举例
为了举例说明@ConditionalOnProperty 的使用,我们将开发一个基本的通知系统。 现在为了简单起见,假设我们要发送电子邮件通知。
首先,我们需要创建一个简单的服务来发送通知消息。 例如,考虑 NotificationSender 接口:
public interface NotificationSender { String send(String message); }
接下来,让我们提供一个 NotificationSender 接口的实现来发送我们的电子邮件:
public class EmailNotification implements NotificationSender { @Override public String send(String message) { return "Email Notification: " + message; } }
现在,让我们看看如何使用 @ConditionalOnProperty 注解。我们把NotificationSender bean配置成,只有在定义了属性 notification.service 时才会被加载:
@Bean(name = "emailNotification") @ConditionalOnProperty(prefix = "notification", name = "service") public NotificationSender notificationSender() { return new EmailNotification(); }
如我们所见,prefix 和 name 属性用于表示应该检查的配置属性。
最后,我们需要添加拼图的最后一个缺失部分。 让我们在 application.properties 文件中定义我们的自定义属性:
notification.service=email
@Configuration
1.介绍
@Configuration注解标识的类中声明了1个或者多个@Bean方法,Spring容器可以使用这些方法来注入Bean,比如:
@Configuration public class AppConfig { //这个方法就向Spring容器注入了一个类型是MyBean名字是myBean的Bean @Bean public MyBean myBean() { // instantiate, configure and return bean ... } }
指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求
简单看一下@Configuration
的内部实现
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { ... }
@Component
注解, 意味也将会注册为bean, 其内部也可以依赖注入。 (换个方式说,一般Bean能用的东西,它也能用) 例如: @Autowired、@Inject、@Scope等@ConfigurationProperties
1.介绍
@ConfigurationProperties需要和@Configuration配合使用,我们通常在一个POJO里面进行配置:
@Data @Configuration @ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; }
上面的例子将会读取properties文件中所有以mail开头的属性,并和bean中的字段进行匹配:
#Simple properties mail.hostname=host@mail.com mail.port=9000 mail.from=mailer@mail.com
如果你不想使用@Configuration, 那么需要在@EnableConfigurationProperties注解中手动导入配置文件如下:
@SpringBootApplication @EnableConfigurationProperties(ConfigProperties.class) public class ConfigPropApp { public static void main(String[] args) { SpringApplication.run(ConfigPropApp.class,args); } }
我们也可以在@ConfigurationPropertiesScan中指定Config文件的路径:
@SpringBootApplication @ConfigurationPropertiesScan("com.flydean.config") public class ConfigPropApp { public static void main(String[] args) { SpringApplication.run(ConfigPropApp.class,args); } }
这样的话程序只会在com.flydean.config包中查找config文件。
@ExceptionHandler
1.介绍
当我们使用这个@ExceptionHandler注解时,我们需要定义一个异常的处理方法,给这个方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法抛出的异常
2.参数
@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常,比如这样:
@ExceptionHandler(NumberFormatException.class) public String handleExeption(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:NumberFormatException"; return resultStr; }
此时注解的参数是NumberFormatException.class,表示只有方法抛出NumberFormatException时,才会调用该方法。
3.就近原则
当异常发生时,Spring会选择最接近抛出异常的处理方法。
比如之前提到的NumberFormatException,这个异常有父类RuntimeException,RuntimeException还有父类Exception,如果我们分别定义异常处理方法,@ExceptionHandler分别使用这三个异常作为参数,比如这样:
@ExceptionHandler(NumberFormatException.class) public String handleExeption(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:NumberFormatException"; return resultStr; } @ExceptionHandler() public String handleExeption2(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:默认"; return resultStr; } @ExceptionHandler(RuntimeException.class) public String handleExeption3(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:RuntimeException"; return resultStr; }
那么,当代码抛出NumberFormatException时,调用的方法将是注解参数NumberFormatException.class的方法,也就是handleExeption(),而当代码抛出IndexOutOfBoundsException时,调用的方法将是注解参数RuntimeException的方法,也就是handleExeption3()。
4.返回值
标识了@ExceptionHandler注解的方法,返回值类型和标识了@RequestMapping的方法是统一的,可参见@RequestMapping的说明,比如默认返回Spring的ModelAndView对象,也可以返回String,这时的String是ModelAndView的路径,而不是字符串本身。
有些情况下我们会给标识了@RequestMapping的方法添加@ResponseBody,比如使用Ajax的场景,直接返回字符串,异常处理类也可以如此操作,添加@ResponseBody注解后,可以直接返回字符串,比如这样:
@ExceptionHandler(NumberFormatException.class) @ResponseBody public String handleExeption(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:NumberFormatException"; return resultStr; }
5.注意点
使用@ExceptionHandler时尽量不要使用相同的注解参数。
@ExceptionHandler(NumberFormatException.class) @ResponseBody public String handleExeption(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:NumberFormatException"; return resultStr; } @ExceptionHandler(NumberFormatException.class) @ResponseBody public String handleExeption2(Exception ex) { System.out.println("抛异常了:" + ex); ex.printStackTrace(); String resultStr = "异常:默认"; return resultStr; }
两个方法都处理NumberFormatException,这种定义方式编译可以通过,而当NumberFormatException真正被抛出时,Spring会给我们报错
@MapperScan
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在SpringBoot启动类上面添加
@SpringBootApplication @MapperScan("com.example.dao") public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
添加@MapperScan(“com.example.dao”)注解以后,com.example.dao包下面的接口类,在编译之后都会生成相应的实现类。
@Param
1.介绍
这个注解是为SQL语句中参数赋值而服务的。
@Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param("userId") int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。
2.使用
第一种:方法有多个参数,需要 @Param 注解
例如下面这样:
@Mapper public interface UserMapper { Integer insert(@Param("username") String username, @Param("address") String address); }
对应的 XML 文件如下:
<insert id="insert" parameterType="org.javaboy.helloboot.bean.User"> insert into user (username,address) values (#{username},#{address}); </insert>
这是最常见的需要添加 @Param 注解的场景。
当需要给参数取一个别名的时候,我们也需要 @Param 注解,例如方法定义如下:
@Mapper public interface UserMapper { User getUserByUsername(@Param("name") String username); }
对应的 XML 定义如下:
<select id="getUserByUsername" parameterType="org.javaboy.helloboot.bean.User"> select * from user where username=#{name}; </select>
第三种:XML 中的 SQL 使用了 $ ,那么参数中也需要 @Param 注解
@Mapper public interface UserMapper { List<User> getAllUsers(@Param("order_by")String order_by); }
对应的 XML 定义如下:
<select id="getAllUsers" resultType="org.javaboy.helloboot.bean.User"> select * from user <if test="order_by!=null and order_by!=''"> order by ${order_by} desc </if> </select>
第四种,那就是动态 SQL ,如果在动态 SQL 中使用了参数作为变量,那么也需要 @Param 注解,即使你只有一个参数。
如果我们在动态 SQL 中用到了 参数作为判断条件,那么也是一定要加 @Param 注解的,例如如下方法:
@Mapper public interface UserMapper { List<User> getUserById(@Param("id")Integer id); }
定义出来的 SQL 如下:
<select id="getUserById" resultType="org.javaboy.helloboot.bean.User"> select * from user <if test="id!=null"> where id=#{id} </if> </select>
这种情况,即使只有一个参数,也需要添加 @Param 注解
@PathVariable
1.介绍
@PathVariable 映射 URL 绑定的占位符。通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx”) 绑定到操作方法的入参中。
2.使用
一般与@RequestMapping(method = RequestMethod.GET)一起使用
@RequestMapping("/getUserById/{name}") public User getUser(@PathVariable("name") String name){ return userService.selectUser(name); }
(1)若方法参数名称和需要绑定的url中变量名称一致时,可以简写:
@RequestMapping("/getUser/{name}") public User getUser(@PathVariable String name){ return userService.selectUser(name); }
(2)若方法参数名称和需要绑定的url中变量名称不一致时,写成:
@RequestMapping("/getUserById/{name}") public User getUser(@PathVariable("name") String userName){ return userService.selectUser(userName); }
@PostConstruct
1.介绍
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法知性之后执行
另外,spring中Constructor、@Autowired、@PostConstruct的顺序
其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象a和对象p,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowried注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
Constructor >> @Autowired >> @PostConstruct
2.例子
public Class AAA { @Autowired private BBB b; public AAA() { System.out.println("此时b还未被注入: b = " + b); } @PostConstruct private void init() { System.out.println("@PostConstruct将在依赖注入完成后被自动调用: b = " + b); } }
@Resource
1.介绍
@Resource和@Autowired注解都是用来实现依赖注入的。只是@AutoWried按by type自动注入,而@Resource默认按byName自动注入。
@Resource有两个重要属性,分别是name和type
spring将name属性解析为bean的名字,而type属性则被解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,如果使用type属性则使用byType的自动注入策略。如果都没有指定,则通过反射机制使用byName自动注入策略。
(1) 既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。
(2)只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean。
@Resource(name="bucket") private String bucketName; @Resource(name="style") private String styleName;
<bean name="bucket" class="java.lang.String"> <constructor-arg value="${oos.bucketName}"/> </bean> <!-- 图片样式名 --> <bean name="style" class="java.lang.String"> <constructor-arg value="${oos.styleName}"/> </bean>
(3)只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
(4)既指定了@Resource的name属性又指定了type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2.@Resource和@Autowired的使用
现在我们定义宝马类和奔驰类
public class BWM { private String name= "宝马"; private String color= "红色"; public String toString(){ return this.name + ":" + this.color; } } public class Benz { private String name= "奔驰"; private String color= "银色"; public String toString(){ return this.name + ":" + this.color; } }
现在我们定义再定义一个人
public class People { private BWM bwm; private Benz benz; public void setBWM(BWM bwm){ this.bwm= bwm; } public void setBenz(Benz benz){ this.benz= benz; } public Tiger getBWM(){ return bwm; } public Monkey getBenz(){ return benz; } public String toString(){ return bwm + "\n" + benz; } }
此时如果将这三个类纳入Spring进行管理,那么在spring-context.xml文件中定义bean对象,如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="Index of /schema/beans" xmlns:context="Index of /schema/context" xsi:schemaLocation="Index of /schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <bean id="people" class="com.lucas.bean.People" > <property name="bwm" ref="bwm" /> <property name="benz" ref="benz" /> </bean> <bean id="bwm" class="com.lucas.bean.BWN" /> <bean id="benz" class="com.lucas.bean.Benz " /> </beans>
通过以上配置,通过Spring上下文
拿到People
对象,Spring
会将BWM
和Benz
对象自动注入到People
的属性,这是通过Spring
的xml
文件配置。那么如何通过注解将BWM
和Benz
对象注入到People
中呢?这是我们的@Resource
和@Autowired
的作用来了。
先揭晓答案是如何使用的。 见如下代码:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="Index of /schema/beans" xmlns:context="Index of /schema/context" xsi:schemaLocation="Index of /schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <!--扫描指定包,如果发现有指定注解,那么该类将由Spring进行管理(用到注解就要配置需要扫描的包哦,不然注解不会生效)--> <!--com.lucas.*的意思是扫描com.lucas的包以及子包--> <context:component-scan base-package="com.lucas.*" /> <!--将三个对象给Spring管理,没有对People的属性进行注入,只是单纯的把People给Spring管理--> <bean id="people" class="com.lucas.bean.People" > <bean id="bwm" class="com.lucas.bean.BWN" /> <bean id="benz" class="com.lucas.bean.Benz " /> </beans>
public class People { //使用@AutoWired和@Resource注解都可以不用写set方法,因为Spring通过反射暴力对Filed进行赋值 //当然也可以标注在属性的setter方法上,也是可以注入的,如果标在属性上那么就是通过set赋值。 @AutoWired private BWM bwm; @Resource private Benz benz; public Tiger getBWM(){ return bwm; } public Monkey getBenz(){ return benz; } public String toString(){ return bwm + "\n" + benz; } }
看到这里,是不是了解了@Resource
和@Autowired
是做什么用的呢,是不是这两个注解简化了一系列代码呢?
3.@Resource和@Autowired的不同点
(1)基因不同: 首先其最大的不同在于她们的爸爸妈妈不同(@Autowired
是由org.springframework.beans.factory.annotation.Autowired
提供,换句话说就是由Spring提供;@Resource
是由javax.annotation.Resource
提供,即J2EE提供,需要JDK1.6及以上。)
(2)注入方式:@Autowired默认按照byType 注入,也提供byName;@Resource默认按byName自动注入,也提供按照byType 注入;
什么是byType
,什么是byName
注入 ????
顾名思义,byType是通过类型进行装配,byName是通过名称进行装配。
- 类型装配:根据要装配的变量类进行查找,如下
@AutoWired private BWM bwm; @AutoWired(默认的查找名称为bwm) private BWM bwm; @Resource(默认的查找名称为benz) private Benz benz;
Spring会自动根据BWM类去IOC容器中寻找他的实现类或子类进行装配(如果出现两个实现类怎么办??
- 名称装配:根据之前在 Spring-context.xml 配置的id进行查找,如果用的 @Service(“id名”) 注解配置的,如果 name 没写,则默认name会定义成非全限定类名(首字母小写,如:类名为 BWM 则 name 会默认为 bWM ),此时 @Autowired 和 @Resource 的默认名称就是定义的属性名 。
如果我@Service
或者xml的id中是我自己定义的id名,那我该如何配置呢?Spring已经想到了,看下面代码写法:
/* 比如你把BWM类的id定义成了bm,Benz类的id定义成了bc */ @AutoWired
@Qualifier("bm")
private BWM bwm;
@Resource(name="bc")
private Benz benz;
4.@Resource装配顺序
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@RequestBody
1.介绍
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的)。
而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
- 一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
- 当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、数组、集合、对象等等(即:当,@RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收参数的机制不变,只不过RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。
- 即:如果参数时放在请求体中,application/json传入后台的话,那么后台要用@RequestBody才能接收到;如果不是放在请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或者形参前 什么也不写也能接收。
- 如果参数前写了@RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。
- 如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名的话,那么就会自动匹配;没有的话,请求也能正确发送。
2.使用
$.ajax({ url:"/login", type:"POST", data:'{"userName":"admin","pwd","admin123"}', content-type:"application/json charset=utf-8", //要特别注意后台接口是否加了@RequestBody,决定了传递参数的类型 success:function(data){ alert("request success ! "); } });
后台接口
@requestMapping("/login") public void login(@requestBody String userName,@requestBody String pwd){ System.out.println(userName+" :"+pwd); }
这种情况是将JSON字符串中的两个变量的值分别赋予了两个字符串,但是呢假如我有一个User类,拥有如下字段:
- String userName;
- String pwd;
那么上述参数可以改为以下形式:@requestBody User user 这种形式会将JSON字符串中的值赋予user中对应的属性上需要注意的是,JSON字符串中的key必须对应user中的属性名,否则是请求不过去的。
@requestMapping("/login") public void login(@requestBody User user){ System.out.println(userName+" :"+pwd); }
@RequestMapping
1.介绍
在Spring MVC 中使用 @RequestMapping 来映射请求,也就是通过它来指定控制器可以处理哪些URL请求,相当于Servlet中在web.xml中配置
<servlet> <servlet-name>servletName</servlet-name> <servlet-class>ServletClass</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletName</servlet-name> <url-pattern>url</url-pattern> </servlet-mapping>
的映射作用一致。让我们先看一下RequestMapping注解类的源码:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name() default ""; String[] value() default {}; String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
1)在@Target中有两个属性,分别为 ElementType.METHOD 和 ElementType.TYPE ,也就是说 @RequestMapping 可以在方法和类的声明中使用
2)可以看到注解中的属性除了 name() 返回的字符串,其它的方法均返回数组,也就是可以定义多个属性值,例如 value() 和 path() 都可以同时定义多个字符串值来接收多个URL请求
2.基础
@RequestMapping 用于将任意HTTP 请求映射到控制器方法上。
- @RequestMapping表示共享映射,如果没有指定请求方式,将接收GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT所有的HTTP请求方式。
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping 都是HTTP方法特有的快捷方式@RequestMapping的变体,分别对应具体的HTTP请求方式的映射注解。
@RequestMapping 注解可以在控制器类上和控制器类中的方法上使用。
在类的级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上。之后你还可以另外添加方法级别的注解来进一步指定到处理方法的映射关系。
需要注意的是,控制器方法都应该映射到一个特定的HTTP方法,而不是使用@RequestMapping共享映射。
注解RequestMapping中produces属性可以设置返回数据的类型以及编码,可以是json或者xml:
@RequestMapping(value="/xxx",produces = {"application/json;charset=UTF-8"})
或
@RequestMapping(value="/xxx",produces = {"application/xml;charset=UTF-8"})
但是必须要和@ResponseBody
注解一起使用才可以,不加@ResponseBody
注解相当于按照和返回String同名jsp页面解析自然就会报错。如果返过来,不加produces属性,只有@ResponseBody注解的话也是没有问题的,只是在浏览器中直接访问的时候有区别:
@RequestMapping(value="/xxx",produces = {"application/json;charset=UTF-8"})
@ResponseBody
3.使用
@RestController public class UserController { // 映射到方法上 // localhost:8080/user/login // 此处通常用 @GetMapping("/user/login") 表明GET请求方式的映射,因为login登录只需向服务器获取用户数据。 @RequestMapping("/user/login") public String login() { return "user login"; } // 映射到方法上 // localhost:8080/user/register // 此处通常用 @PostMapping("/user/login") 表明POST请求方式的映射,因为register注册需要向服务器提交用户数据。 @RequestMapping("/user/register") public String register() { return "user register"; } }
如上述代码所示,到 /user/login 的请求会由 login() 方法来处理,而到 /user/register的请求会由 register() 来处理。
下面是一个同时在类和方法上应用了 @RequestMapping 注解的示例,上述代码与如下代码等价:
@RestController // 映射到类上 // localhost:8080/user @RequestMapping("/user") public class UserController { // 映射到方法上 // localhost:8080/user/login // 此处通常用 @GetMapping("/user/login") 表明GET请求方式的映射 @RequestMapping("/login") public String login() { return "user login"; } // 映射到方法上 // localhost:8080/user/register // 此处通常用 @PostMapping("/user/login") 表明POST请求方式的映射 @RequestMapping("/register") public String register() { return "user register"; } }
一般情况下,这样代码更规范,因为user的控制器UserController只对user表进行操作。
4.处理 HTTP 请求
Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的请求默认都会是 HTTP GET 类型的。
为了能降一个请求映射到一个特定的 HTTP 方法,你需要在 @RequestMapping 中使用 method 属性来声明 HTTP 请求所使用的方法类型,也可以使用等价的组合注解。
请求 | 组合注解 | 共享注解 |
---|---|---|
GET | @GetMapping | @RequestMapping(method = RequestMethod.GET) |
POST | @PostMapping | @RequestMapping(method = RequestMethod.POST) |
PUT | @PutMapping | @RequestMapping(method = RequestMethod.PUT) |
DELETE | @DeleteMapping | @RequestMapping(method = RequestMethod.DELETE) |
PATCH | @PatchMapping | @RequestMapping(method = RequestMethod.PATCH) |
需要注意的是,控制器方法都应该映射到一个特定的HTTP方法,即使用组合注解,而不是使用@RequestMapping共享映射。因为组合注解减少了在应用程序上要配置的元数据,并且代码功能更清晰。
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping(method = RequestMethod.GET) String get() { return "Hello from get"; } @RequestMapping(method = RequestMethod.DELETE) String delete() { return "Hello from delete"; } @RequestMapping(method = RequestMethod.POST) String post() { return "Hello from post"; } @RequestMapping(method = RequestMethod.PUT) String put() { return "Hello from put"; } @RequestMapping(method = RequestMethod.PATCH) String patch() { return "Hello from patch"; } }
在上述这段代码中, @RequestMapping 注解中的 method 元素声明了 HTTP 请求的 HTTP 方法的类型。
所有的处理处理方法会处理从这同一个 URL( /home)进来的请求, 但要看指定的 HTTP 方法是什么来决定用哪个方法来处理。
例如,一个 POST 类型的请求 /home 会交给 post() 方法来处理,而一个 DELETE 类型的请求 /home 则会由 delete() 方法来处理。
@RequestParam
1.介绍
@RequestParam用于将指定的请求参数赋值给方法中的形参。将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)
2.使用
package com.day01springmvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("hello") public class HelloController2 { /** * 接收普通请求参数 * http://localhost:8080/hello/show16?name=linuxsir * url参数中的name必须要和@RequestParam("name")一致 * @return */ @RequestMapping("show16") public ModelAndView test16(@RequestParam("name")String name){ ModelAndView mv = new ModelAndView(); mv.setViewName("hello2"); mv.addObject("msg", "接收普通的请求参数:" + name); return mv; } /** * 接收普通请求参数 * http://localhost:8080/hello/show17 * url中没有name参数不会报错、有就显示出来 * @return */ @RequestMapping("show17") public ModelAndView test17(@RequestParam(value="name",required=false)String name){ ModelAndView mv = new ModelAndView(); mv.setViewName("hello2"); mv.addObject("msg", "接收普通请求参数:" + name); return mv; } /** * 接收普通请求参数 * http://localhost:8080/hello/show18?name=998 显示为998 * http://localhost:8080/hello/show18?name 显示为hello * @return */ @RequestMapping("show18") public ModelAndView test18(@RequestParam(value="name",required=true,defaultValue="hello")String name){ ModelAndView mv = new ModelAndView(); mv.setViewName("hello2"); mv.addObject("msg", "接收普通请求参数:" + name); return mv; } }
@ResponseBody
1.介绍
@RequestMapping("/login") @ResponseBody public User login(User user){ return user; } User字段:userName pwd 那么在前台接收到的数据为:'{"userName":"xxx","pwd":"xxx"}' 效果等同于如下代码: @RequestMapping("/login") public void login(User user, HttpServletResponse response){ response.getWriter.write(JSONObject.fromObject(user).toString()); }
@RestController
1.介绍
@RestController注解有两个目的。首先他是一个类似于@controller和@Service的构造型注解,能够让类被组件扫描功能发现。但是,与REST最相关在于@RestController会告诉Spring,控制器中所有的处理器方法的返回值都要直接写入响应体中,而不是将值放到模型中并传递给一个视图以便于渲染。
- @RestController为开发提供了方便☺,在提供json接口时需要的配置操作再也不需要自己配置了。
- @RestController注解相当于@ResponseBody和@Controller的结合
- @RestController注解时,返回的是内容实例
2.使用
当get请求id为1返回一个new User,状态码200,否则就返回空,状态码404.
@RestController public class MyTestController { @GetMapping("/test/{id}") public HttpEntity testMethodA(@PathVariable Long id) { if (id == 1) { return new ResponseEntity(new User("Trump","123456"), HttpStatus.OK); } else{ return new ResponseEntity(null,HttpStatus.NOT_FOUND); } } }
postman请求结果如下:
注意状态码是封装在Response包里面的,没有数据返回的body就是空。
如果返回只写一个String,而不是new User,则不会跳转到对应的jsp页面,而是会把String封装到body里面。
@RestControllerAdvice
1.介绍
@RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。
@ExceptionHandler(value = Exception.class)ExceptionHandler的作用是用来捕获指定的异常。
@RestControllerAdvice(annotations = RestController.class) public class UniformReponseHandler<T> { @ResponseStatus(HttpStatus.OK) public CallResultMsg sendSuccessResponse(){ return new CallResultMsg(true, CodeAndMsg.SUCCESS, null); } @ResponseStatus(HttpStatus.OK) public CallResultMsg sendSuccessResponse(T data) { return new CallResultMsg(true, CodeAndMsg.SUCCESS, data); } @ExceptionHandler(UserDefinedException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public CallResultMsg sendErrorResponse_UserDefined(Exception exception){ return new CallResultMsg(false, ((UserDefinedException)exception).getException(), null); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public CallResultMsg sendErrorResponse_System(Exception exception){ if (exception instanceof UserDefinedException) { return this.sendErrorResponse_UserDefined(exception); } return new CallResultMsg(false, CodeAndMsg.UNKNOWEXCEPTION, null); } }
通过上面的一波操作,我们的controller中就不需要再去写大量的try-catch了,RestControllerAdvice会自动帮助catch,并匹配相应的ExceptionHandler,然后重新封装异常信息,返回值,统一格式返回给前端。
@ControllerAdvice 和 @RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。
- RestControllerAdvice = ControllerAdvice + ResponseBody
@Service
1.介绍
如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。
此注注解属于业务逻辑层,service或者manager层
默认按照名称进行装配,如果名称可以通过name属性指定,如果没有name属性,注解写在字段上时,默认去字段名进行查找,如果注解写在setter方法上,默认按照方法属性名称进行装配。当找不到匹配的bean时,才按照类型进行装配,如果name名称一旦指定就会按照名称进行装配
@ SpringBootApplication
1.介绍
@ SpringBootApplication这个注解是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。
@SpringBootApplication public class SpringSecurityJwtGuideApplication { public static void main(java.lang.String[] args) { SpringApplication.run(SpringSecurityJwtGuideApplication.class, args); } }
我们可以把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。
- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
- @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
- @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
springboot自动配置了支持mongodb。在启动springboot时会自动实例化一个mongo实例。所以自己配置的话,需要禁用掉springboot的自动配置。
@SpringBootApplication(exclude = {MongoAutoConfiguration.class,MongoDataAutoConfiguration.class})
这个注解可以禁用springboot自带的配置。
@Transactional
1.介绍
@Transactional 是声明式事务管理 编程中使用的注解
- 默认配置下 Spring 只会回滚运行时、未检查异常(继承自 RuntimeException 的异常)或者 Error。
- @Transactional 注解只能应用到 public 方法才有效。
如果需要将某个方法声明为事务操作,可通过将对应属性设置好的@Transactional注解来标记对应方法。@Transactional注解的主要属性入下表所示。
属性名 | 说明 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT。 |
timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
no-rollback-for | 抛出 no-rollback-for 指定的异常类型,不回滚事务。 |
2.使用
只需在方法加上 @Transactional
注解就可以了。
如下有一个保存用户的方法,加入 @Transactional
注解,使用默认配置,抛出异常之后,事务会自动回滚,数据不会插入到数据库。
@Transactional @Override public void save() { User user = new User("服部半藏"); userMapper.insertSelective(user); if (true) { throw new RuntimeException("save 抛异常了"); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了