IOC容器配置(所有的需要用到的类的对象配置)
假设有一个User类:
package com.spring.ioc_1;
public class User {
private int id ;
private String name ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
id:表示当前实例对象在spring容器中的唯一标识。
class:这里指定是当前实例对象类的完全限定名。
<bean id="user" class="com.spring.ioc_1.User"></bean>
看一下对象创建的个数
scope:指定作用域
默认不写scope属性,等价于“scope=singleton”单例模式
scope属性的另一个值,“scope=prototype”多例模式
如下:设置单例模式
<bean id="user" class="com.spring.ioc_1.User" scope="singleton"></bean>
然后,从容器中获取bean实例对象
public class App {
public static void main(String[] args) {
//得到IOC容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//从IOC容器中获得bean实例对象
User user = (User) applicationContext.getBean("user");
User user2 = (User) applicationContext.getBean("user");
System.out.println(user);
System.out.println(user2);
}
}
结果是:
com.spring.ioc_1.User@33f88ab
com.spring.ioc_1.User@33f88ab
可以看出来是同一个对象。
模式修改为多例
<!-- scope="prototype" 多例模式 -->
<bean id="user" class="com.spring.ioc_1.User" scope="prototype"></bean>
还是执行刚才哪个main方法,执行结果如下:
com.spring.ioc_1.User@3b088d51
com.spring.ioc_1.User@1786dec2
可以看出来,每次调用从容器获取bean实例的方法时都会创建一个实例对象。
看一下对象创建的时机
在上述例子中的user创建默认构造函数和带参的构造函数
public User() {
System.out.println(">>>>>>>>>>创建了User对象");
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
在main方法中添加如下日志信息:
public class App {
public static void main(String[] args) {
//得到IOC容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(">>>>>>>>>>>>>得到IOC容器");
//从IOC容器中获得bean实例对象
User user = (User) applicationContext.getBean("user");
User user2 = (User) applicationContext.getBean("user");
System.out.println(">>>>>>>>>从IOC容器中获取bean实例对象");
System.out.println(user);
System.out.println(user2);
}
}
当“scope=“singleton”的时候,执行main,查看打印日志信息”
>>>>>>>>>>创建了User对象
>>>>>>>>>>>>>得到IOC容器
>>>>>>>>>从IOC容器中获取bean实例对象
com.spring.ioc_1.User@33f88ab
com.spring.ioc_1.User@33f88ab
当“scope=“prototype”的时候,执行main,查看打印日志信息”
>>>>>>>>>>>>>得到IOC容器
>>>>>>>>>>创建了User对象
>>>>>>>>>>创建了User对象
>>>>>>>>>从IOC容器中获取bean实例对象
com.spring.ioc_1.User@3b088d51
com.spring.ioc_1.User@1786dec2
从上可以总结出:
单例模式:应用只创建一个实例对象,且在容器(tomcat等)启动,Spring容器被初始化前创建的。
多例模式:应用会在对象被调用的时候才创建对象。
那么单例模式中,怎么让对象也在被调用的时候再创建?
设置“lay-init=“default””
lay-init=“default” 为“默认设置”,代表意思“不延迟初始化创建”,就是上述讲的单例模式中不写lay-init的效果。
lay-init=“true” 为“延迟初始化创建”,设置后,对象会在被调用的时候再创建。
其他参数:
init-method=“” 对象初始化的时候默认指定要调用的方法。
destroy-method=“” 对象被销毁的时候默认指定要调用的方法。
Spring IOC容器:spring的核心(作用:创建对象,处理对象的依赖关系)
IOC创建对象的几种方式:
调用无参构造:
<bean id="user1" class="com.spring.ioc_1.User"></bean>
调用带参构造:
<bean id="user2" class="com.spring.ioc_1.User">
<constructor-arg value="24" type="int" index="0"></constructor-arg>
<constructor-arg value="科比" type="java.lang.String" index="1"></constructor-arg>
</bean>
如上,构造函数有两个参数:
value:参数的值。
type:参数对应的数据类型。
index:参数的顺序,不写index的时候根据在标签中的先后顺序。
这里说一下另一个属性设置:ref(引用其他对象)
举例:
假设上述user2变成如下写法:
<bean id="user2" class="com.spring.ioc_1.User">
<constructor-arg value="24" type="int" index="0"></constructor-arg>
<constructor-arg ref="lakers" index="1"></constructor-arg>
</bean>
<bean id="lakers" class="java.lang.String">
<constructor-arg value="科比" type="java.lang.String"></constructor-arg>
</bean>
通过工厂创建对象:
先创建工厂类:
package com.spring.ioc_1;
/**
* @explain:UserFactory
* @author:jimmy
* @date:2018/12/10
* @create by Intellij Idea
*/
public class UserFactory {
/**
* 非静态方法创建对象
* @return
*/
public User getInstance(){
return new User() ;
}
/**
* 静态方法创建对象
* @return
*/
public static User getInstanceByStatic(){
return new User() ;
}
}
(1)工厂类中,通过非静态方法创建
<bean id="userFactory" class="com.spring.ioc_1.UserFactory"></bean>
<bean id="user3" factory-bean="userFactory" factory-method="getInstance"></bean>
先创建工厂类,
factory-bean:指定通过哪个工厂去创建
factory-method:指定通过工厂中的哪个方法去创建对象。
(2)工厂类中,通过静态方法创建
<bean id="user4" class="com.spring.ioc_1.UserFactory" factory-method="getInstanceByStatic"></bean>
这里:
class:为工厂类的全限定名
factory-method:指定通过class指定的工厂中的哪个静态方法去创建对象。
DI:依赖注入
(1)构造器注入
(2)set方法对属性注入
(3)通过p命名空间
……
(4)自动装配
(5)注解
模拟一个登录业务的整个处理过程:
(1)登录请求肯定是发送到action,所以需要创建一个Action,(Controller)
(2)Action中调用Service进行业务逻辑处理。
(3)Service中实际进行对数据库的操作是Dao。
UserAction:注入userService,然后调用Service中的登录业务逻辑处理
package com.spring.ioc_1;
/**
* @explain:Action
* @author:jimmy
* @date:2018/12/10
* @create by Intellij Idea
*/
public class UserAction {
private UserService userService ;
public void setUserService(UserService userService) {
this.userService = userService;
}
public String login(){
userService.toLogin() ;
return "登录成功";
}
}
UserService:注入UserDao,调用Dao层中的操作数据库的方法
package com.spring.ioc_1;
/**
* @explain:
* @author:jimmy
* @date:2018/12/10
* @create by Intellij Idea
*/
public class UserService {
private UserDao userDao ;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public String toLogin(){
userDao.query() ;
return "ok" ;
}
}
UserDao中执行对数据库的实际操作。
要在Action中调用Service中的方法,就需要先通过set方法注入Service。【刚开始最常规的写法】
【内部bean的写法实现】
<!--内部bean写法-->
<bean id="userAction" class="com.spring.ioc_1.UserAction">
<property name="userService">
<bean class="com.spring.ioc_1.UserService">
<property name="userDao">
<bean class="com.spring.ioc_1.UserDao"></bean>
</property>
</bean>
</property>
</bean>
总结:这种写法,内部bean只在bean内部有用
【p命名空间的写法】
<bean id="userDao" class="com.spring.ioc_1.UserDao" ></bean>
<bean id="userService" class="com.spring.ioc_1.UserService" p:userDao-ref="userDao"></bean>
<bean id="userAction2" class="com.spring.ioc_1.UserAction" p:userService-ref="userService"></bean>
总结:p命名空间的写法,其实是对set属性注入写法的优化
p:跟你要注入的bean的id,如果是传值的话直接“=”,如果是要引用对象的话后面要跟“ref”.
【自动装配通过byName】
<!--自动装配的写法-->
<bean id="userDao3" class="com.spring.ioc_1.UserDao" ></bean>
<bean id="userService3" class="com.spring.ioc_1.UserService" autowire="byName"></bean>
<bean id="userAction3" class="com.spring.ioc_1.UserAction" autowire="byName"></bean>
解释:在userService中,如下图,定义了userDao3,那么只要找到了bean唯一标识为userDao3的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" 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" default-autowire="byName">
<!--自动装配的写法-->
<bean id="userDao3" class="com.spring.ioc_1.UserDao" ></bean>
<bean id="userService3" class="com.spring.ioc_1.UserService" ></bean>
<bean id="userAction3" class="com.spring.ioc_1.UserAction" ></bean>
</beans>
需要注入的是:byName方式定义的名称和set方法注入的名称必须要一直,否则会报错。
【通过byType(根据类型)自动装配】上述中例子修改为byType即可。
通过类型自动装配的时候,需要注意IOC容器中只能有一个这种类型的对象的对象,也就是不能如下这么定义:
<bean id="userDao3" class="com.spring.ioc_1.UserDao" ></bean>
<bean id="userDao4" class="com.spring.ioc_1.UserDao" ></bean>
【缺点是】自动装配在后期项目较大的时候不利于维护。
* 注解方法:(简化Spring IOC容器的配置)
使用注解的步骤:
(1)先要引入context的命名空间:
(2)一定要开启注解扫描
@Component注解
@Component()//将一个对象加入IOC容器
@Resource(name="")//根据name属性注入,在字段上,并不是set方法,set方法就不需要了。
package com.spring.ioc_2;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @explain:userAction
* @author:jimmy
* @date:2018/12/10
* @create by Intellij Idea
*/
@Component("userAction")
public class UserAction {
@Resource(name = "userService")
private UserService userService ;
}
package com.spring.ioc_2;
import com.spring.ioc_1.UserDao;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @explain:1
* @author:jimmy
* @date:2018/12/10
* @create by Intellij Idea
*/
@Component("userService")
public class UserService {
@Resource(name = "userDao")
private UserDao userDao ;
}
@Component(“userService”)可以简化为@Component,表示默认取你当前类名,并且第一个字母小写。
@Resource(name = “userDao”)可以简化为@Resource,表示默认按照userDao这个类型去查找对象,如果找到了则直接注入。不关乎名字。
缺点:@Component虽然可以在service,dao,也可以在action中完成注解,但是我们开发过程中就不能很好的区分持久层,业务层,控制层。
@Service:在业务逻辑层注解使用
@Repository:在持久层注解使用
@Controller:在控制层注解使用
注解:
可以简化配置,可以把对象加入IOC容器,处理依赖关系。