一、组件自动扫描机制

Spring 2.5后为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进IOC容器,作为Spring的Bean来管理。它的作用和在XML文件中使用bean节点配置组件是一样的。

1、 <context:component-scan base-package="com.hs.controller" />包扫描功能

要使用组件自动扫描机制,我们需要加上以下配置信息,启动包扫描功能:

<?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/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">
 
    <context:component-scan base-package="com.hs.controller" />
 
</beans>

其中<context:component-scan base-package="com.hs.controller" />这个配置隐式注册了多个对注解进行解析处理的处理器,包括<context:annotation-config/>该配置注册的处理器,也就是说写了<context:component-scan base-package="com... />配置,就不用写<context:annotation-config/>配置了;

而base-package为需要扫描的包(含子包)的路径,启动了包扫描功能,将com.hs.controller这个包下以及子包下的所有类扫描一遍,将标记有@Controller、@Service、@repository、@Component等注解的类纳入到IOC容器中,作为Spring的Bean来管理。

在xml配置了这个标签后,Spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean。注解要开启对应的解析器的才能起作用,Spring已经自动帮你开启了注解的解析器。

小贴士:

  • 自动装配的时候Spring默认给你的bean的取了名字,就是类名的第一个字母小写;
  • 如果你想把这个bean换成自己的名字,只需要在@Component等这几个注解后面加个小括号,像这样@Component(“hello”),下次你去Spring里面找"hello"的时候就能找到你这个bean了;
  • 默认Spring管理你的bean的作用域是应用单例模式singleton,就是Spring容器启动的时候就把你的bean初始化一个放到他自己的仓库里(其实是一个map),每次IOC容器返回的Bean都是同一个实例。当你希望每次IOC容器返回的Bean实例是一个新的实例时,可以设置scope为prototype,如这样@Scope("prototype")。

2、另外<context:component-scan>还提供了两个子标签

Use-dafault-filters=”false”的情况下:

<context:exclude-filter>指定的不扫描

<context:include-filter>指定的扫描

     在说明这两个子标签前,先说一下<context:component-scan>有一个use-default-filters属性,该属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean,也就是@Component的子注解@Service,@Reposity等。所以如果仅仅是在配置文件中这么写<context:component-scan base-package="com.hs.web"/>, Use-default-filter此时默认为true那么会对base-package包或者子包下的所有的进行Java类进行扫描,并把匹配的Java类注册成bean。

      可以发现这种扫描的粒度有点太大,如果你只想扫描指定包下面的Controller,该怎么办?此时子标签<context:include-filter>就起到了用武之地。如下所示

<context:component-scan base-package="com.hs.web" use-default-filters="false">

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

</context:component-scan>

与如下的这个等同:

<context:component-scan base-package="com.hs.web.controller"/>

这样就会只扫描base-package指定下的有@Controller下的Java类,并注册成bean。

____________________________________________________________________________________________

另外在我参与的项目中可以发现在base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定<context:exclude-filter>来进行过滤,说明此包不需要被扫描。

二、Spring基本注解总结
Spring为了能够实现bean的自动装配,提供几个基本注解@Component、@Controller、@Repository、@Service,你只要把这几个注解任选一个放到你想让Spring管理的类的上面,再在配置文件中加上<context:component-scan base-package="包名" />启用包扫描功能,Spring就会自动把这个类纳入到自己的管辖范围,作为Spring的Bean来管理。

Spring通过注解给各个bean分层的,比如@Controller就是对应mvc的控制层,@Service对应的就是业务层,@Repository对应的持久层。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 这样方便以后程序分层。

使用注解+包扫描功能的方式代替复杂的xml配置文件,可以实现beans自动装配进IOC容器来管理。但是缺少DI依赖注入,也就是某个JavaBean依赖其他bean对象,没有传统xml配置ref注入对象。我们可以使用@Resource和@Autowired注解进行bean对象的注入。

1、@Resource与@Autowired用法区别:

  共同点

 @Resource和@Autowired都可以作为bean注入属性时使用,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换, 不影响使用。两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

  不同点

    @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注解一起来修饰。

(1)@Autowired

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

public class TestServiceImpl {
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
        this.userDao = userDao;
    }
}

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao; 
}

(2)@Resource

@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

public class TestServiceImpl {
    // 下面两种@Resource只要使用一种即可
    @Resource(name="userDao")
    private UserDao userDao; // 用于字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
        this.userDao = userDao;
    }
}

2、@RequestBody和@RequestParam注解的区别:

(1)@RequestParam

注解@RequestParam接收的参数是来自requestHeader中,即请求头。

RequestParam可以接受简单类型的属性,也可以接受对象类型。

@RequestParam有三个配置参数:

  • required 表示是否必须,默认为 true,必须。
  • defaultValue 可设置请求参数的默认值。
  • value 为接收url的参数名(相当于key值)。

@RequestParam用来处理 Content-Type 为 application/x-www-form-urlencoded 编码的内容,Content-Type默认为该属性。@RequestParam也可用于其它类型的请求,例如:POST、DELETE等请求。

(2)@RequestBody

注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/json、application/xml等类型的数据。就application/json类型的数据而言,使用注解@RequestBody可以将body里面所有的json数据传到后端,后端再进行解析。

GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。

POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用

HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。

三、Spring常用注解总结
常用注解介绍得很全面:Spring 注解学习笔记

比如列举得有:

声明Bean的注解:

  • @Component : 组件,没有明确的角色
  • @Service : 在业务逻辑层(service层)使用
  • @Repository : 在数据访问层(dao层)使用.
  • @Controller : SpringMVC的Controller层使用

注入Bean的注解:

这个三个注解就是Spring可以自动帮你把bean里面引用的对象的setter/getter方法省略,它会自动帮你set/get。当前它会提前帮你实例化对象,我们不需要再使用getBean的方式获取bean来使用,直接用就行。

  • @Autowired : Spring提供的注解.
  • @Inject : JSR-330提供的注解
  • @Resource : JSR-250提供的注解

配置文件的注解:

  • @Configuration : 声明当前类是个配置类,相当于一个Spring配置的xml文件.
  • @ComponentScan (cn.test.demo): 自动扫描包名下所有使用 @Component @Service  @Repository @Controller 的类,并注册为Bean
  • @WiselyConfiguration : 组合注解 可以替代 @Configuration和@ComponentScan
  • @Bean : 注解在方法上,声明当前方法的返回值为一个Bean

SpringMVC 常用注解:

  • @Controller : 注解在类上声明这个类是SpringMVC里的Controller,将其注册为一个Spring的Bean.
  • @RequestMapping :可以注解在类上或方法上映射WEB请求(访问路径和参数),@RequestMapping注解在方法上,可以在一个类中定义多个接口请求,这样使用起来比只支持单一请求的Servlet更加灵活。
  •      @RequestMapping(value= "/test",produces={"application/json; charset=UTF-8"}) 设置用JSON数据转换器
  • @ResponseBody : 支持将返回值放入response体内,而不是返回一个页面,一般用来声明返回Json格式的数据
  • @RequestBody : 允许request的参数在request体中,而不是直接连接在地址后面,此注解放置在参数前
  • @RestController : @Controller + @ResponseBody 组合注解
  • @Path Variable : 用来接收路径参数 如/test/001,001为参数,此注解放置在参数前
  • @WebAppConfiguration("src/main/resources") : 注解在类上,用来声明加载的ApplicationContex 是一个WebApplicationContext ,它的属性指定的是Web资源的位置,默认为 src/main/webapp ,自定义修改为 resource

Spring Boot 注解:

  •  @SpringBootApplication : 是Spring Boot 项目的核心注解 主要目的是开启自动配置.
  •          @SpringBootApplication一个组合注解,包括@Configuration+@EnableAutoConfiguration+@ComponentScan
  • @Configuration用于声明当前类是个配置类,相当于一个Spring配置的xml文件.
  • @ComponentScan (com.test.demo): 自动扫描包名下所有使用 @Component @Service  @Repository @Controller 的类,并注册为Bean。
  • @EnableAutoConfiguration注解:作用在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置.
  • 这个注解告诉Spring Boot根据添加的jar依赖猜测你想如何配置Spring。由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。
  • @Value : 属性注入,读取properties或者 Yml 文件中的属性
  • @ConfigurationProperties : 将properties属性和一个Bean及其属性关联,从而实现类型安全的配置
  •         @ConfigurationProperties(prefix = "author",locations = {"classpath:config/author.properties"})
  •         通过@ConfigurationProperties加载配置,通过prefix属性指定配置前缀,通过location指定配置文件位

Mybatis注解:

  • @Mapper:
  •        使用 @Mapper动态代理注解,会自动的把 @Mapper 注解的Dao接口自动生成代理实现类。在开发的过程中只需要写 Dao层的接口,无需写其实现类,实现类有框架自己补充。
  • @Insert @Update @Delete @Select系列:
  •         直接在@Mapper注解的Dao接口的方法上使用,对应的是mapper.xml文件中<select>...标签的SQL语句,这样就不用再编写Mapper配置文件了。

四、#{}与${}的区别
#{}在引用时,如果发现目标是一个字符串,则会将其值作为一个字符串拼接在sql上,即拼接时自动包裹引号;
${}在引用时,即使发现目标是一个字符串,也不会作为字符串处理,拼接在sql时不会自动包裹引号。

并且#{}能够很大程度上防止sql注入,${}方式无法防止sql注入。所以,能用#{}时尽量用#{};

默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义。
示例:

执行SQL:Select * from user where name = #{name}
参数:name=>xin
解析后执行的SQL:Select * from user where name = ?

执行SQL:Select * from user where name = ${name}
参数:name:ke
解析后执行的SQL:Select * from user where name =ke

insert into user values (null,#{name},55); --> insert into user values (null,'fff',55);
insert into user values (null,${name},55); --> insert into user values (null,fff,55);//sql语句错误

 说明:

  1. # 将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #{user_id},如果传入的值是 name , 那么解析成sql时的值为order by “name”, 如果传入的值是id,则解析成的sql为order by “id”.
  2. $ 将传入的数据直接显示生成在sql中。如:order by ${user_id},如果传入的值是name, 那么解析成sql时的值为order by name, 如果传入的值是id,则解析成的sql为order by id.

综上所述,${}方式会引发SQL注入的问题、同时也会影响SQL语句的预编译,所以从安全性和正确性的角度出发,能使用#{}的情况下就不要使用 ${}。

${} 在什么情况下使用呢?

如果需要引用的是一个列名,使用${}。比如Mybaties排序时使用order by 动态参数时需要注意,使用${}而不用#{}。

有时候可能需要直接插入一个不做任何修改的字符串到SQL语句中。这时候应该使用${}语法。比如,动态SQL中的字段名,如:ORDER BY ${columnName}

<select id="queryMetaList" resultType="Map">
    Select * from name where name = ${name} ORDER BY ${age}
</select>