Spring框架有三种配置方式:
1.在spring2.5以前,用xml文件进行配置
2.在spring2.5以后,用xml文件和注解(annotation)共同进行配置
3.在spring3.0以后,用注解(annotation)和JavaConfig配置类进行配置
一、xml文件
见下一篇
二、xml文件+注解
为什么要用注解+xml文件,而不直接用xml文件呢?因为过多的类注入和通过过多的set方法和构造方法来进行注入会导致代码冗余,所以我们可以通过注解+xml文件来优化代码,以下以一个具体的实例,通过注解+xml文件的方法来实现类的注入和基本数据的注入:
注入类
注入普通类
关键字:@Component
语法:@component(value="注入容器的id,如果省略,id自动默认是类名且首字母小写")
使用@component进行类的注入代替了之前我们在xml文件中的:
<bean id="student" class="com.com.apesource.包.Student"></bean>
等价于: 注解+xml文件
注解:
@Component
Class Student{}
xml文件:(含义:扫描所有被@Component注解所修饰的类,再将其注入容器)
<context:component-scan base-package="com.apesource"/>
注入数据访问层
关键字:@Repository
代替xml文件中的:
<bean id="daoImp" class="com.apesource.dao.UserDaoImp"/>
注入业务层
关键字:@Service
代替xml文件中的
<bean id="serviceImp" class="com.apesource.service.UserServiceImp"/>
注入控制层
关键字:@Controller
代替xml文件中的
<bean id="controllerImp" class="com.apesource.controller.
UserControllerImp"/>
注入基本数据
@Value
使用方法
含义:注入基本数据
替换:<property><property/>
修饰:成员变量或对应的set方法
语法:(注解+xml文件)
1.注解:①@Value("数据内容") 、②@Value("动态获取数据内容")
2.xml文件:
<context:property-placeholder location="message.properties" />
具体实现
1.学生类:
<1>//写法1:
@Component(value = "stu")
public class Student {
//方法1:
@Value("团团")
private String stuName;
@Value("180")
private int stuHeight;
@Override
public String toString() {
return "Student{" +
"stuName='" + stuName + '\'' +
", stuHeight=" + stuHeight +
'}';
}
}
<2.1>//写法2:
@Component(value = "stu")
public class Student {
//方法2:
@Value("${msg1}")
private String stuName;
@Value("${msg2}")
private int stuHeight;
@Override
public String toString() {
return "Student{" +
"stuName='" + stuName + '\'' +
", stuHeight=" + stuHeight +
'}';
}
}
<2.2> //message.properties文件:存放动态信息
msg1=毛毛
msg2=182
2.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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描注解-->
<context:component-scan base-package="com.apsource"/>
<!--加载资源文件-->
<context:property-placeholder location="message.properties" />
</beans>
运行结果:
@AutoWried
使用方法
语法:@AutoWried(required="true-默认、false"),其中required代表是否必须进行装配
修饰对象:成员变量或对应的构造方法
含义:按照 通过set方法进行类型装配,set方法可以省略
具体实现
我们使用一个三层架构来说明他的具体实现:
1.//controller控制层
<1.1>IUserController接口:
public interface IUserController {
public void save();
}
<1.2> UserControllerImp实现类
@Component(value="controller")
public class UserControllerImp implements IUserController{
//向spring容器查找IUserService类型的对象实例
,并通过构造方法注入给修饰的属性
@Autowired
IUserService service;
//省略了set方法
@Override
public void save() {
System.out.println("===controller的新增方法===");
service.save();
}
}
2.dao数据访问层
<2.1>IUserDao接口
public interface IUserDao {
public void save();
}
<2.2>UserDaoImp实现类
@Repository
public class UserDaoImp implements IUserDao{
@Override
public void save() {
System.out.println("===dao的新增方法===");
}
}
3.Service服务层
<3.1>IUserService接口
public interface IUserService {
public void save();
}
<3.2> UserServiceImp实现类
@Service
public class UserServiceImp implements IUserService {
@Autowired
//@Autowired(required = false):如果找不到匹配的名字(dao),也不报错
IUserDao dao;
@Override
public void save() {
System.out.println("===service的新增方法===");
dao.save();
}
}
4.测试类
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext=new
ClassPathXmlApplicationContext("applicationContext.xml");
IUserController controller = (IUserController) applicationContext
.getBean("controller");
controller.save();
运行结果:
那当我们有两个UserDaoImp时@Autowired会为我们注入哪一个类呢?
<2.3>UserDaoImp2
@Repository
public class UserDaoImp2 implements IUserDao{
@Override
public void save() {
System.out.println("===dao2的新增方法===");
}
}
结果:
为什么会出现这种错误呢?
是因为@Autowired是先按类型进行匹配的,当我们没有UserDaoImp2时,dao层只有一个实现类类型是IUserDao,所以会直接匹配到UserDaoImp,输出:“===dao1的新增方法===”。那当我们新增了一个实现类UserDaoImp2时,dao层有两个实现类类型是IUserDao,那么此时就会按名称(dao)进行匹配,且两个实现类均没有起别名,默认名称为类名且首字母小写,分别为userDaoImp和userDaoImp2,没有叫dao的,所以会报“NoUniqueBeanDefinitionException”异常。
那我们尝试给UserDaoImp2命名为dao,会发现输出为:
可以总结为@Autowired先按类型匹配,再按名字匹配。当没有一个类型匹配时,会产生NoSuchBeanDefinitionException异常。
其他注解
@Primary
含义:首选项,当类型冲突的情况下,此注解修饰的类被列为首选(备胎扶正)
修饰:类
注意:不能单独使用,必须与@Component....联合使用
@Repository
public class UserDaoImp implements IUserDao{
@Override
public void save() {
System.out.println("===dao1的新增方法===");
}
}
@Repository
public class UserDaoImp2 implements IUserDao{
@Override
public void save() {
System.out.println("===dao2的新增方法===");
}
}
此时两个dao层实现类均没有进行命名,按理来说应该出现NoUniqueBeanDefinitionException错误, 但是当我们给UserDaoImp写一个@Primary注解时,会优先匹配UserDaoImp类:
@Repository
@Primary
public class UserDaoImp implements IUserDao{
@Override
public void save() {
System.out.println("===dao1的新增方法===");
}
}
运行结果:
@Qualifier(value="名称")
含义:按照名称装配
修饰:成员变量
注意:不能单独使用,必须与@Autowired联合使用
@Service
public class UserServiceImp implements IUserService {
@Autowired
@Qualifier(value = "userDaoImp")//按名称匹配userDaoImp
IUserDao dao;
@Override
public void save() {
System.out.println("===service的新增方法===");
dao.save();
}
}
结果:
@Resource(name="名称")
含义:按照名称装配
修饰:成员变量
注意:可以单独使用
这个注解和Qualifier作用相同,只不过Resource可以单独使用:
@Service
public class UserServiceImp implements IUserService {
@Resource(name="userDaoImp")
IUserDao dao;
@Override
public void save() {
System.out.println("===service的新增方法===");
dao.save();
}
}
结果:
@Scope
含义:配置类的作用域
修饰:类
注意:不能单独使用,必须与@Component....联合使用 @Scope("prototype") @Scope("singleton") @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@PostConstruct
初始化,修饰方法
@PreDestroy
销毁,修饰方法
三、注解+配置类
见下篇