前言:前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些组件采用XML的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。Spring建议最好使用注解+组件自动扫描机制的方式代替复杂的xml配置文件,来实现beans自动装配进IOC容器来管理。
举个栗子:
小王:想要把一个bean交给Spring去管理,你想怎么做?
小李:简单啊,在Spring的配置文件里面配置<bean id="" class=""></bean>不就行了。
小王:不行啊,我们的项目太恶心了,用了一万个bean,写配置文件快累死了,密密麻麻的看不清楚
小李:额!这个也太奇葩了,怎么办,翻翻书吧,有没有好方法。
这是什么?自动装配?我擦,这么神奇?下面我们开启神奇的Spring自动装配之旅。
Spring自动装配,目的就是解放Spring的配置文件,我们来看看解放以后的配置文件
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd "> <context:component-scan base-package="com"></context:component-scan> </beans>
小李:我以前得配置一万个bean呢,现在只需要一行了,靠谱吗?
一、组件自动扫描机制
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语句错误
说明:
-
#
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #{user_id},如果传入的值是 name , 那么解析成sql时的值为order by “name”, 如果传入的值是id,则解析成的sql为order by “id”. -
$
将传入的数据直接显示生成在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>
参考链接:
Spring注解<context:component-scan base-package />使用
SpringMVC常用注解:@Controller注解的详细使用方法
Spring注解@Resource和@Autowired区别对比