前面聊servlet的时候可xml配置,还可以通注解进行配置,那么spring配置xml也可以通xml进行配置。
不过还是那句话,注释虽然简单,其实spring中xml和注解优缺点和servlet的两者优缺点很相似。
-
XML
优点:把类与类的之间解耦,修改方便,容易扩展。对象之间的关系一目了然,毕竟是统一管理,而且配置信息更加详细或者说是更加全。
缺点:因为配置信息会有很多,其配置文件会很大,当然也不是说所有配置信息放在一个XML中。如果对项目不熟悉的话,因为xml配置是解耦,可能再排除错误的时候也是一个挑战。
-
注解:
优势: 注释和代码放在一起,如果出现错误或者修改很容易进行调整。而且注解相对于配置XML更方便。
缺点:没有xml可以配置的信息全,注解是方便,但是注解太多的话也会影响代码质量,会让代码变得臃肿。而且对于各个业务的复杂关系呈现,不会很直观。
其实这些优缺点几乎是所有通过配置文件和注解优缺点几乎一样。
本篇就开始聊常用的注解,还是那句话聊的注解不是全部,而是常用的。
当然spring中注解也是可以针对类,也可以是在方法,属性上。
前提
导入jar包
无论如何使用注解,首先要导入支持这个注解的jar,前面体验的时候导入了四个核心包,现在使用注解,需要导入一个包:
同时看见这个包的名带有aop,其实这个名字又带有一个神奇的词汇,也是无法逃避的spring中AOP技术。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
其实可以看出有spring中两个思想IOC和AOP中的重要的一个。
不过这个后面聊,还是那个准则:先用,然后再聊原理。而这个地方就是将jar导入到项目中。
开启组件扫描
既然是注解,那自然需要告诉程序扫描哪些地方,不然注解也不会被启动,这个开始扫描很简单,就是在xml中配置即可:
<?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.xsd
">
<context:component-scan base-package="这里配置扫描的文件或者范围"></context:component-scan>
</beans>
在创建xml配置文件中,添加
第一个:
xmlns:context="http://www.springframework.org/schema/context"
第二个
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
第三个:
<context:component-scan base-package="这里配置扫描的文件或者范围"></context:component-scan>
这个对需要扫描的范围,进行配置,可以设置单独某个文件或者文件夹。
现在聊一些具体对于路径配置的方式(base-package 可以看出是配置包的路径):
-
精确配置某个包
-
单独一个包:
base-package="com.test.dao"
-
两个包
base-package="com.test.dao,com.test.service"
中间添加一个逗号。
-
-
上层配置会扫描其下面的所有包
base-package="com.test"
但是还有一个此参数需要注意:
<context:component-scan base-package="com.xzd.test" use-default-filters="false"></context:component-scan>
其实按照上面两种配置,会扫描精确的某个包下的文件或者扫描某个包下面文件以及其下的包中的文件。其实这个通过的默认的过滤器,所以一般默认:
use-default-filters="true"
这个意思是扫描组件会扫描包(以及子包)中文件的所有信息。
其下面还有两个标签:
<!-- 不扫描 某类型的的数据-->
<context:exclude-filter type="" expression=""/>
<!-- 只扫描某个类型的数据-->
<context:include-filter type="" expression=""/>
这个先用配置,然后解释是什么意思:
-
第一种:
<context:component-scan base-package="com.xzd.test" > <!-- 只扫描 org.springframework.stereotype下面的 Service注解--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
-
第二种:
<context:component-scan base-package="com.xzd.test" > <!-- 不扫描 org.springframework.stereotype下面的 Service注解 ,其它的搜扫描--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
其中type表示的是类型,expression的对这个类型的具体目标进行指向。
而type可以位两个值:
属性 | 描述 |
---|---|
type="annotation" | 根据注解的类型进行排除,expression需要设置排除的注解全类名。比如通过@Repository, expression=“org.springframework.stereotype.Repository” |
type="assignable" | 根据类的类型进行排除,expression需要设置排除的类的全类名。 |
type=“regex” | 根据正则表达匹配到所有类名进行过滤 |
type=“custom” | 通过自定义实现了org.springframework.core.type.filter.TypeFilter接口类,自定义过滤规则。 |
type=“aspectj” | 根据aspectj表达式进行过滤 |
所以为了减少资源浪费,可以配置扫描文件中的内容范围的。
bean管理–创建对象注解
这个注解聊四个注解
注:下面面四个注解功能是一样的,都可以用来创建bean实例。但是为什么要分四个呢?因为是为了更好的描述注解所注的类有不同的效果。
但是如果说不分混合或者单独用一个也是可以的,不过为了更加方便所在的作用,还是最好在不同的功能的类用不同的注解。
注解 | 描述 |
---|---|
@Component | 单词的意思是:组成部份,成分,部件。所以这个注解一般用在各种组件的类上,比如注解在不在控制层和服务层的等类上。 |
@Service | 单词的意思是:服务。这个也是放在业务逻辑层中使用(比如service层注入dao)用于服务层,主要用来进行业务的逻辑处理 |
@Repository | 单词的意思是:贮藏室,仓库 (电脑)数据库。所以这个用于标注数据访问层,也可以说作为持久层操作(数据库)的bean来使用,也就是DAO层中注解。 |
@Controller | 单词的意思是:控制器。所以这个是在展示层,而这个用于标记的类就是一个SpringMvc 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">
<context:component-scan base-package="com.xzd.test" >
<!-- 采用默认全部扫描-->
</context:component-scan>
</beans>
测试类:
public class test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml");
SaveDao saveDao=applicationContext.getBean("saveDao", SaveDao.class);
saveDao.save();
}
}
来一个注解演示一下:
package com.xzd.test.dao;
import org.springframework.stereotype.Repository;
//这里的value如果不写的话等于当前类名首字母小写,如果使用value的话可以随便起名,其等价于配置中id
@Repository(value="saveDao") //相等于在xml中 配置bean
public class SaveDao {
public void save(){
System.out.println("dao添加到数据库中了");
}
}
因为这个地方是一个dao命名的所以使用注解@Repository,但是如果使用其他的三个注解中的一个:
package com.xzd.test.dao;
import org.springframework.stereotype.Component;
//这里的value如果不写的话等于当前类名首字母小写,如果使用value的话可以随便起名,其等价于配置中id
@Component(value="saveDao") //相等于在xml中 配置bean
public class SaveDao {
public void save(){
System.out.println("dao添加到数据库中了");
}
}
可以看出其结果,丝毫没有收到影响。所以不在依次演示了。
属性注解方式实现
这个三个常用的属性注解:
注解 | 描述 |
---|---|
@AutoWired | 根据属性类型自行注入 |
@Qualifier | 根据属性名进行注入 |
@Resource | 可以根据属性类型注入,也可以通名称注入。 |
@Value | 注入普通类型的数据 |
在进行演示的时候,配置文件和一个类不动所以在写在前面,减少重复:
<?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.xsd
">
<context:component-scan base-package="com.xzd.test" >
</context:component-scan>
</beans>
package com.xzd.test.dao;
import org.springframework.stereotype.Component;
//这里的value如果不写的话等于当前类名首字母小写,如果使用value的话可以随便起名,其等价于配置中id
@Component(value="saveDao1") //相等于在xml中 配置bean
public class SaveDao {
public void save(){
System.out.println("dao添加到数据库中了");
}
}
@AutoWired
通过一个servece类调用,这个Dao类,为了方便就不再写接口了,直接通过类来描述。
package com.xzd.test.service;
import com.xzd.test.dao.SaveDao;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
@Component(value="saveService")
public class SaveService {
@Autowired
SaveDao saveDao;
public void save() {
System.out.println("service调用dao");
saveDao.save();
}
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml");
SaveService saveService=applicationContext.getBean("saveService", SaveService.class);
saveService.save();
}
}
可以直接通过类型就进行匹配赋值了。但是这个有一个问题,那就是某个接口下有多个实现类,那么通过类型就会有些问题那就是找错类。所以个人建议不用通过属性进行自动注入。
这里也可以看出通过注解不需要有set方法了,方便了很。
当然也可以写出set方法,在set方法上使用注解,也可以通过带有参数的构造方法使用注解,不过一般直接在属性上使用最方便。
// set方法上使用
@Autowired
public void setSaveDao(SaveDao saveDao) {
this.saveDao = saveDao;
}
// 也可以在构造方法上使用
@Autowired
public SaveService(SaveDao saveDao) {
this.saveDao = saveDao;
}
这样使用效果是一样,不过没有在属性上直接使用方便,可以作为了解即可。
@Qualifier
前面说了通过名字进行注入,相对回更加使用场景广泛一些,但是无法单独使用@Qualifier,Qualifier无法自行开启自动注入,所以需要借用@AutoWired,因为 @AutoWired除了代表是安装属性注入以外,还有一个作用就是告诉程序这个是自动注入,所以需要一起使用,不然会报空异常(也可以理解名字匹配权限较高或者说是对属性匹配后的再此精确匹配),现在演示:
@Component(value="saveService")
public class SaveService {
@Autowired // 不带会报错的
@Qualifier(value = "saveDao1")
SaveDao saveDao;
public void save() {
System.out.println("service调用dao");
saveDao.save();
}
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml");
SaveService saveService=applicationContext.getBean("saveService", SaveService.class);
saveService.save();
}
}
@Resource
这个可以实现两种注入现在演示。
-
根据属性注入:
package com.xzd.test.service; import com.xzd.test.dao.SaveDao; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component(value="saveService") public class SaveService { @Resource SaveDao saveDao; public void save() { System.out.println("service调用dao"); saveDao.save(); } @Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml"); SaveService saveService=applicationContext.getBean("saveService", SaveService.class); saveService.save(); } }
-
根据名字注入
package com.xzd.test.service; import com.xzd.test.dao.SaveDao; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component(value="saveService") public class SaveService { @Resource(name="saveDao1") SaveDao saveDao; public void save() { System.out.println("service调用dao"); saveDao.save(); } @Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml"); SaveService saveService=applicationContext.getBean("saveService", SaveService.class); saveService.save(); } }
@Value
这个就是简单是数据注入,现在可以演示一下:
package com.xzd.test.service;
import com.xzd.test.dao.SaveDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
@Component(value="saveService")
public class SaveService {
@Value("张三") // 或者@Value( value="张三")
String name;
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testAnnotate.xml");
SaveService saveService=applicationContext.getBean("saveService", SaveService.class);
System.out.println(saveService.name);
}
}
完全注解
当然也可以删除注解,然后通过配置类来进行替代配置xml。现在演示:
创建一个配置类
package com.xzd.test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages ={"com.xzd.test"})
public class Config {
}
其调用方式也是需要变化的如下:
package com.xzd.test.service;
import com.xzd.test.dao.SaveDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
@Component(value="saveService")
public class SaveService {
@Value("张三") // 或者@Value( value="张三")
String name;
@Test
public void test(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
SaveService saveService=applicationContext.getBean("saveService", SaveService.class);
System.out.println(saveService.name);
}
}
可见也可以实现自动组件扫描,当然这些只是一部分注解,以后还会补充。