Spring学习笔记-------->IOC
IOC理论推导
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
public interface UserDao {
void getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("普通获取用户数据");
}
}
public class UserDaoMySqlImpl implements UserDao{
@Override
public void getUser() {
System.out.println("需求1:通过MySql获取");
}
}
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser() {
System.out.println("需求2:通过Oracle获取数据");
}
}
public class UserServiceImpl implements UserService{
private UserDao userDao=new UserDaoImpl();//每次需求的更改都要修改此处实现类
public void getUser(){
userDao.getUser();
}
}
public class MyTest {
public static void main(String[] args) {
UserService userService=new UserServiceImpl();
userService.getUser();
}
}
在之前的业务中,用户的需求可能回影响我们原来的代码,我们需要根据用户需求去修改原代码,如果程序代码量非常大,修改一次成本代价十分昂贵。
将UserDao 用set进行动态实现的注入
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
之前,程序都是主动创建对象,控制权在程序员手上;
使用了set注入后 程序不再具有主动性,而是变成了被动的接受对象
这种思想,从本质上解决了问题,程序员再也不用去管理对象的创建了 ,系统的耦合度大大降低,可以更加专注在业务的实现上,这就是IOC的原型
public class MyTest {
public static void main(String[] args) {
//用户实际接触业务层 Dao层不需要接触
UserService userService=new UserServiceImpl();
//接口不能实现类的特有方法,需要强转
((UserServiceImpl)userService).setUserDao(new UserDaoMySqlImpl());
userService.getUser();
}
}
第一个spring项目 helloworld
设置bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用spring来创建对象 在spring中 这些都称为bean
bean=对象 id等价于变量名 class相当于new的对象
value相当于给对象属性赋值-->
<bean id="hello" class="com.phq.pojo.Hello" >
<property name="str" value="Spring"/>
</bean>
</beans>
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理,要使用直接去里面取就可以
Hello hello=(Hello)context.getBean("hello");
System.out.println(hello.toString());
}
}
IOC创建对象的方式
1:默认使用无参构造创建对象
public User() {
System.out.println("User的无参构造");
}
<bean id="user" class="com.phq.pojo.User">
<property name="name" value="温存"></property>
</bean>
2.1:有参构造下标方式
public User(String name) {
this.name = name;
System.out.println("有参构造");
}
<bean id="user" class="com.phq.pojo.User">
<!-- <property name="name" value="温存"></property>-->
<constructor-arg index="0" value="有参测试"/>
</bean>
2.2通过类型(不建议使用)
<bean id="user" class="com.phq.pojo.User">
<!-- <property name="name" value="温存"></property>-->
<constructor-arg type="java.lang.String" value="类型赋值" />
</bean>
2.3通过参数名设置
<bean id="user" class="com.phq.pojo.User">
<!-- <property name="name" value="温存"></property>-->
<constructor-arg name="name" value="类型赋值" />
</bean>
Spring配置
- alias 别名 可以通过别名获取对象
- id bean的唯一标识符
- class bean对象所对应的全限定名
- name 也是别名 可以同时取多个别名 可以通过空格 逗号 分号等等分隔
- import 一把用于团队开发 可以将多个配置文件合并为一个
依赖注入(Dependency Injection)
构造器注入
上面已经讲过
Set方式注入
- Set
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象的属性有容器来注入
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> cards;
private Set<String> games;
private String wife;
private Properties info;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.phq.pojo.Student">
<!-- 第一种:普通值注入-->
<property name="name" value="温存"/>
<!-- 第二种:Bean注入 ref-->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</array>
</property>
<!-- List注入-->
<property name="hobbys">
<list>
<value>吃饭</value>
<value>睡觉</value>
</list>
</property>
<!-- Map注入-->
<property name="cards">
<map>
<entry key="身份证" value="1008611"/>
<entry key="银行卡" value="1000000"/>
</map>
</property>
<!-- Set注入-->
<property name="games">
<set>
<value>球球大作战</value>
<value>奇迹暖暖</value>
</set>
</property>
<!--null-->
<property name="wife">
<null></null>
</property>
<!-- properties-->
<property name="info">
<props>
<prop key="学号"> 201720180000</prop>
<prop key="性别">男</prop>
<prop key="姓名">法外狂徒张三</prop>
</props>
</property>
</bean>
<bean id="address" class="com.phq.pojo.Address" >
<property name="location" value="火星"/>
</bean>
</beans>
Student{name='温存', address=Address{location='火星'}, books=[红楼梦, 西游记, 水浒传], hobbys=[吃饭, 睡觉], cards={身份证=1008611, 银行卡=1000000}, games=[球球大作战, 奇迹暖暖], wife='null', info={学号=201720180000, 性别=男, 姓名=法外狂徒张三}}
拓展方式注入
p命名空间 :直接注入属性的值
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
</beans>
c命名空间 通过构造器注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- c-namespace declaration -->
<bean id="thingOne" class="x.y.ThingOne" c:thingTwo-ref="thingTwo" c:thingThree-ref="thingThree" c:email="[emailprotected]"/>
</beans>
Bean的作用域
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
单例模式: (Spring默认机制)
原型模式:每次从容器中get都会产生一个新的对象
其余的只能在WEB开发中使用到
bean的自动装配
- spring会在上下文中自动寻找 并自动给bean装配属性
- 在xml中显式的配置
- 在java中显式的配置
- 隐式的自动装配bean(重要)
环境搭建
一人一猫一狗
public class People {
private String name;
private Dog dog;
private Cat cat;
}
<bean id="cat" class="com.phq.dao.Cat"/>
<bean id="dog" class="com.phq.dao.Dog"/>
<bean id="people" class="com.phq.dao.People" autowire="byName">
<property name="name" value="温存"/>
</bean>
- byName 会自动到容器上下文中查找 和自己对象set方法后面的值对应的bean 的id
使用时需要保证所有的bean的id唯一 并且这个bean需要和自动注入的属性的set方法一致
- byType 会自动到容器上下文中查找 和自己对象属性类型相同的bean
使用时需要保证所有的bean的class唯一 并且这个bean需要和自动注入的属性一致
使用注解进行自动装配
jdk1.5支持的注解 spring2.5就支持注解了
大多时候会使用注解进行开发
- 导入约束
- 配置注解的支持
<?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:annotation-config/>
</beans>
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
@Autowired
直接在属性上使用即可, 也可以在set方法上使用,
@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
如果自动装配环境比较复杂,可以加上@Qualifier,则可以根据byName的方式自动装配
@Resource
private Cat cat;
@Resource
private Dog dog;
@Resource 如有指定的name属性,先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
Recource 和Autowired的区别:
1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型(byType)装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
3、@Resource默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名(byName)进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型(byType)进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
使用注解开发
在spring4 以后 要使用注解开发必须要保证aop的包导入了,使用注解需要导入context约束 添加注解的支持
1:bean
@Component注解 说明类被spring管理
2:属性如何注入
//等价于<bean id="user" class="com.phq.pojo.User"
@Component
public class User {
@Value("温存")
//等价于<property name="naem" value="温存"/>
public String name;
//@Value("温存") 也可以放在set方法上
public void setName(String name) {
this.name = name;
}
}
3:衍生的注解
@Component的衍生注解 在web开发中 会按照mvc三层架构分层
- dao层: @Repository
- service层 @service
- controller层 @Controller
这四个注解功能是一样的 都代表某个类将注册到spring中 装配bean
4:自动装配
5:作用域
@Scope9("singleton")
6:小结
xml与注解:
- 注解最万能 适用于任何场合 维护方便
- 注解 不是自己的类用不了 维护相对复杂
最佳处理方法
- xml用来管理bean
- 注解完成属性的注入
- 使用时必须开启注解支持
<context:component-scan base-package="com.phq"/>
<context:annotation-config/>
使用java的方式配置spring
完全不使用spring 的xml配置 全权交给java做
javaConfig是spring的一个子项目, 在spring4以后 成为一个新功能
pojo:
public class User {
public String name;
public String getName() {
return name;
}
@Value("测试")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Configuration
@ComponentScan("com.phq.pojo")
@Import(MyConfig2.class) //引入其他配置文件同时使用
public class MyConfig {
//注册一个bean 相当于xml添加bean标签
//这个方法的名字就相当于bean中的id属性
//返回值就相当于bean标签的class属性
@Bean
public User User(){
return new User();
}
}
public class MyConfig2 {
//注册一个bean 相当于xml添加bean标签
//这个方法的名字就相当于bean中的id属性
//返回值就相当于bean标签的class属性
@Bean
public User User2(){
return new User();
}
}
测试导入MyConfig 使用MyConfig2中的方法名:
@Test
public void test1(){
//如果完全使用配置类去做 就只能通过AnnotationConfig获取容器 通过配置类的class对象加载
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("User2", User.class);
System.out.println(user.getName());
}
}