开篇
mmp,今天上班又迟到了一个小时。一下雨这鬼路上就堵车,简直了。。好了,不吐槽了,昨天挖的坑,今天是时候填上了。内容可能有点多,肾功能不强大者勿看0.0。
常见的几种注入方式
1. Set 方式注入
set 方式注入是除了注解以外最常用的一种方式,我们昨天所说的就是通过 set 方式注入属性值的,具体实现如下:
在 applicationContext.xml中配置:
<!-- 通过 set 方式注入值-->
<bean id="user" class="com.student.spring.bean.User">
<property name="name" value="二花"/>
<property name="address" value="大森林"/>
</bean>
在实体类中提供 set 方法
public class User {
private String name;
private String address;
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
测试结果:
注意:
- 必须要提供 set 方法,get 方法可以不提供。
- 通过配置 Spring 就可以实现帮我们创建对象的实例,这说明了什么?说明 Spring 底层是通过反射来帮我们创建对象的实例的,如果不提供 set 方法,Spring 将无法通过反射来获取到 setXXX() 方法来帮我们注入值。
- 提供 toString 方法仅仅只是为了打印结果,并没有其他意图。
2. 通过构造器注入值
在 User 对象中添加如下构造函数:
public class User {
private String name;
private String address;
public User(String name, String address) {
this.name = name;
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
在applicationContext.xml 文件中做如下配置:
<!-- 通过 构造方法的 方式注入值-->
<bean id="user" class="com.student.spring.bean.User">
<constructor-arg name="name" value="二花"/>
<constructor-arg name="address" value="大森林"/>
</bean>
测试结果:
结果是正常的,不过通过构造函数注入时,除了可以通过
<constructor-arg name="name" value="小明"/>
这种方式外,还可以通过索引来注入,比如把配置文件更改成如下的方式:
<!-- 通过 构造方法的 方式注入值-->
<bean id="user" class="com.student.spring.bean.User">
<constructor-arg index="0" value="二花"/>
<constructor-arg index="1" value="广东深圳"/>
</bean>
结果同上面是一样的。(提示:索引从 0 开始)
注意
这里全部都是用的 value ,还记得昨天的文章中有一个 ref 么?为什么要用 value 而不用 ref 请自行去上一篇文章中弄清楚。
假设 User 对象中有另外一个 对象 “Car”, 这个时候我们应该如何注入?如何注入我们试试就知道了。
新建一个 Car 对象
public class Car {
private String cname;
private Double cprice;
public void setCname(String cname) {
this.cname = cname;
}
public void setCprice(Double cprice) {
this.cprice = cprice;
}
}
在 User 对象里面添加
private Car car;
public User(String name, String address, Car car) {
this.name = name;
this.address = address;
this.car = car;
}
在配置文件 applicationContext.xml 中做如下配置:
<!-- 通过 set 方式注入值-->
<bean id="car" class="com.student.spring.bean.Car">
<property name="cname" value="保时捷"/>
<property name="cprice" value="2000000"/>
</bean>
<!-- 通过 构造方法的索引 方式注入值-->
<bean id="user" class="com.student.spring.bean.User">
<constructor-arg index="0" value="二花"/>
<constructor-arg index="1" value="广东深圳"/>
<!-- 引用上面 定义的 car-->
<constructor-arg index="2" ref="car"/>
</bean>
结果
我想,到这里,ref 和 value 的用法你应该了解了。如果还不知道什么区别的话,那你这样记吧。
value 是传值的,ref 是传引用的。
这句话可能不太严谨,但是对初学者来说,还是比较好记住的。
3. 其他的注入方式
注入的方式还有
SpEL 表达式注入
通过 P 标签注入
通过注解注入(重要)
其中 SpEL 表达式 和 p 标签在开发中基本不会用到,这里就不做讲解了。而注解注入,这个是比较重要的知识点,留在后面去说。注解注入也是我们以后开发中最主流的注入方式。
p 标签的注入方式(了解)
<bean id="car" class="com.student.spring.bean.Car" p:cname="奔驰" p:cprice="125000"/>
<!-- 通过 p 标签 方式注入值-->
<bean id="user" class="com.student.spring.bean.User" p:name="宇文玥" p:address="青山院" p:car-ref="car"/>
注意:通过 p 标签注入,一定要提供 set 方法,否则无法注入。
SpEL 的代码我就不写了,有需要的可以去看一些文档。自己要学会举一反三。
注入几种特殊类型
1. 注入数组类型
<!-- 注入数组-->
<bean id="sptype" class="com.student.spring.bean.SpType">
<property name="strs">
<list>
<value>hello</value>
<value>world</value>
</list>
</property>
</bean>
SpType.java
public class SpType {
private String[] strs;
public void setStrs(String[] strs) {
this.strs = strs;
}
}
2. 注入 List
这个套路和注入数组一毛一样,这里就只给出配置文件和截图了。
<bean id="sptype" class="com.student.spring.bean.SpType">
<!-- 注入数组-->
<property name="strs">
<list>
<value>hello</value>
<value>world</value>
</list>
</property>
<!-- 注入集合-->
<property name="lists">
<list>
<value>小明</value>
<value>熊大</value>
</list>
</property>
</bean>
3. 注入Map
这个和上面的两者稍有区别。也不废话了,直接上配置文件。
<bean id="sptype" class="com.student.spring.bean.SpType">
<!-- 注入数组-->
<property name="strs">
<list>
<value>hello</value>
<value>world</value>
</list>
</property>
<!-- 注入 list-->
<property name="lists">
<list>
<value>小明</value>
<value>熊大</value>
</list>
</property>
<!-- 注入map:map 是以键值对的形式存在的-->
<property name="maps">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3" value="value3"/>
</map>
</property>
</bean>
运行截图:
4. 注入 Set
套路和 List 已经数组一样,只不过要注意,Set 没有重复的值。
<!-- 注入 set ,这里给 Set 注入了四个值,实际打印只有三个,去掉了重复的 xixi-->
<property name="sets">
<set>
<value>haha</value>
<value>hehe</value>
<value>xixi</value>
<value>xixi</value>
</set>
</property>
截图:
5. 注入属性文件
<property name="prop">
<props>
<prop key="key">隔壁老王</prop>
<prop key="key2">隔壁老宋</prop>
</props>
</property>
具体测试我就不测试了,挺简单的,有兴趣自己动手,丰衣足食。
Spring 重要的开发方式之注解开发
啥是注解我也就不解释了,不知道的话,推荐你们去重新温习一下 Java 的基础知识。
1. 注解入门
使用注解开发,能够极大的方便我们的开发,所以这也是我们实际开发中必须掌握的一门技术。要使用注解进行开发,我们需要引入 Spring 框架的另外一个包
spring-aop-4.0.2.jar
此时修改我们的 UserServiceImpl.java,在类的上面添加如下注解
//这行注解的功能就相当于<bean id="userService" class="com.student.spring.service.UserServiceImpl">
@Component("userService")
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("service 层使用注解");
}
}
如果要使该注解生效,我们需要告诉 Spring 容器我们使用了注解。所以我们还需要在配置文件中加上一句话:(开启注解扫描)
<!--开启注解扫描 base-package 是指扫描哪一个包下面的注解-->
<context:component-scan base-package="com.student.spring.service"/>
运行结果:
运行结果和我们在配置文件中配置并无其他区别。通过注解,我们不需要再配置文件中在配置数不清的 bean,大大节省了我们的开发时间,也提升了我们的开发效率。
2. 注解功能讲解
@Component 注解是什么意思呢?
Component 是组件的意思,作用在类上的。在我们的 MVC 开发模式中,WEB 层,Service 层以及 Control 层都可以使用该注解。但是为了满足单一职责原则,从该注解又衍生出三个注解:
@Controller 作用在类上,WEB 层使用
@Service 作用在类上,Service 层使用
@Repository 作用在类上,Dao 层使用
这三个注解本身就是为了使各个模块的功能更加清晰,虽然你使用 @Component 也没错,但是谁也不能保证 Spring 的团队后面不会对该注解进行改动,所以为了避免以后出现不必要的麻烦,还是使用功能分工明确的那三个注解。
So?我们的上面的例子也可以使用 @Service 注解来代替:
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("service 层使用注解");
}
}
实现的效果完全一样。
3. 使用注解注入属性
上面只是讲了解决了配置文件中 bean 标签的简化配置
<bean id="xxx" class="xxx">
那如果我们要注入引用类型和基本数据类型呢?难道没有吗?错,Spring 当然给我们也提供了注解的方式。看下面的几个注解:
@AutoWired
@Qualifier
@Value
首先从最下面的注解说起
3.1 如果是要注入普通的数据类型,那么非 Value 注解莫属。
修改 UserServiceImpl.java
@Service("userService")
public class UserServiceImpl implements UserService {
// @Value("二花")
@Value(value = "二花")
private String name;
@Override
public void save() {
System.out.println("service 层使用注解:===" + name);
}
}
注意:
Spring 所提供的注解属性值全部都是 value
在注解只有一个属性的前提下,”value = ” 可省略不写
上面的 @Value(value=”二花”) 和 @Value(“二花”) 是等价的
同理 @Service(“userService”) 同样也和 @Service(value=”userService”) 等价
使用注解后,set 方法可以省略。
3.2 注入引用类型
如果是注入引用类型的话,就要看 @AutoWired 和 @Qualifer 这两个注解了。
@AutoWired //按类型装配
@Qualifer //强制使用名称注入
先说 @AutoWired 注解
更改 UserDaoImpl.java,要把 UserDaoImpl 也交给 Spring 去管理,必须要加上注解 @Repository 注解。
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("dao 层操作数据库去保存用户数据");
}
}
再改 UserServiceImpl.java
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void save() {
System.out.println("service 层保存用户");
userDao.save();
}
}
先不谈 AutoWired 的功能,我们先看结果。
测试结果:
嗯,老铁,没毛病。AutoWired 是自动装配的,它默认是按照类型装配的,如果我们把
//在 UserServiceImpl 中
@AutoWired
private UserDao userDao1;
//在 UserDaoImpl 中
@Repository("userDao")
结果和上面一样,这是为什么呢?是因为使用了 AutoWired 注解后,他并不是根据 id 去找,而是根据类型去找的,所以不管你更改成什么名字,ud,xxx 等等,都是可以注入成功的。但是这样是会有一个弊端的,如果一个接口有多个实现类,那么此时使用 AutoWired 进行自动装配肯定会出错。那么如何解决上面的问题呢?
当然有解决办法,我们不乱写名字不就可以了么?那么我们的另外一个注解 @Qualifer,这个注解的意思就是强制使用名称去注入
更改 UserDaoImpl.java 和 UserServiceImpl.java 中的代码
@Repository(value = "xxx")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("dao 层操作数据库去保存用户数据");
}
}
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Qualifier(value = "xxx")
@Autowired
private UserDao userxxx;
@Override
public void save() {
System.out.println("service 层保存用户");
userxxx.save();
}
}
测试结果:
这样就达到了我们的目的。一个是按类型,一个强制按名称,这两个注解同时使用,只能得出正确的结果。
3.3 Java 提供的 Resource 注解
如果一个接口有多个实现类,那么我们就跟上面一样,需要同时写两个注解,那么这好像一点也不程序员,不满足我们程序员的风格,能不能再简单一点呢?当然可以,Java 中有一个注解是得到了 Spring 的支持了的,他就是 Resource 注解,这个注解的功能就相当于上面的 AutoWired 注解和 Qualifer 注解同时使用。
这样更改 UserServiceImpl.java
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Resource(name = "xxx")
private UserDao userxxx;
@Override
public void save() {
System.out.println("service 层保存用户");
userxxx.save();
}
}
图片展示的结果已经说明了一切,一切尽在不言中。
bingo!此处应该有掌声。。。(鸦雀无声),好吧,此时无声胜有声也行。(自抱自泣.jpg)
3.4 其他注解
Bean的作用范围注解
1. 注解为@Scope(value=”prototype”),作用在类上。值如下:
2. singleton – 单例,默认值
3. prototype – 多例Bean的生命周期的配置(了解)
注解如下:
1. @PostConstruct – 相当于init-method
2. @PreDestroy – 相当于destroy-method
OK,完美收工。
结尾
一点注解和注入方式就讲了这么多,好啦,下面的东西留到下篇文章吧,毕竟需要消化一下。能力有限,只能写出这样的文章,如果有错误的地方,还烦请给我指出,以免误导他人,同时欢迎留言交流。