即控制反转,是面向对象的一种设计原则,用来降低代码耦合度。
其中最常见的方式叫做依赖注入(DI),还有一种方式叫依赖查找。
Spring 是常见的一种 IOC 容器。创建对象和对象之间的调用过程,都由 Spring 来管理。
本质是 XML 解析+工厂模式+反射。
<!-- XML配置 -->
<bean id="dao" class="pack.UserDao"></bean>
class UserFactory {
public static userDao getDao() {
//通过XML解析获取全类名
String className = "pack.UserDao";
//通过反射创建类
Class clazz = Class.forName(className);
return (UserDao) clazz.newInstance();
}
}
@Test
public void test() {
//加载 Spring 配置文件
BeanFactory factory = new ClassPathXmlApplicationContext("bean.xml");
//获取配置创建的对象
User user = factory.getBean("user", User.class);
user.test();
}
@Test
public void test() {
//加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//获取配置创建的对象
User user = context.getBean("user", User.class);
user.test();
}
BeanFactory 在加载配置文件时不创建对象,在获取对象时才创建,而 ApplicationContext 在加载配置文件时就创建对象。
在实际开发中,一般采用 ApplicationContext,而只有在系统资源较少时,才考虑使用 BeanFactory。
在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。
<!-- 在 Spring 配置文件中配置对象创建 -->
<bean id="user" class="pack.User"></bean>
id 属性是唯一标识。
class 属性是类的全路径。
创建对象时,默认执行的是无参构造方法。
//必须声明 set 方法
class User{
private String userName;
private String userAge;
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserAge(String userAge) {
this.userAge = userAge;
}
}
<!-- 通过 property 标签注入属性 -->
<bean id="user" class="pack.User">
<property name="userName" value="ZhangSan"></property>
<property name="userAge" value="77"></property>
</bean>
//必须声明有参构造方法
class User{
private String userName;
private String userAge;
public User(String userName, String userAge){
this.userName = userName;
this.userAge = userAge;
}
}
<!-- 通过 constructor-arg 标签注入属性 -->
<bean id="user" class="pack.User">
<constructor-arg name="userName" value="ZhangSan"></constructor-arg>
<constructor-arg name="userAge" value="77"></constructor-arg>
</bean>
<!-- 在配置文件中添加 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">
<!-- 使用 p 名称空间注入属性 -->
<bean id="user" class="pack.User" p:userName="ZhangSan" p:userAge="77"></bean>
</beans>
<!--注入 null -->
<property name="userName">
<null/>
</property>
<!-- 注入特殊符号,例如注入 "<ZhangSan>" -->
<property name="userName">
<value>
<!-- 法一:使用<或>转义符号进行转义 -->
<< ZhangSan <>
</value>
</property>
<property name="userName">
<value>
<!-- 法二:使用 CDATA -->
<![CDATA[
<ZhangSan>
]]>
</value>
</property>
class UserService {
private UserDaoImpl impl;
public void setUserDaoImpl(UserDaoImpl impl) {
this.impl = impl;
}
...
}
<!-- 通过 ref 标签注入外部 Bean -->
<bean id="service" class="pack.service.UserService">
<property name="impl" ref="dao"></property>
</bean>
<bean id="dao" class="pack.dao.UserDaoImpl"></bean>
class UserService {
private UserDaoImpl impl;
public void setUserDaoImpl(UserDaoImpl impl) {
this.impl = impl;
}
//get方法
public UserDaoImpl getUserDaoImpl(UserDaoImpl impl) {
return this.impl;
}
...
}
class UserDaoImpl {
private String value;
...
}
<!-- 法一:如注入外部 Bean -->
<bean id="service" class="pack.service.UserService">
<property name="impl" ref="dao"></property>
</bean>
<bean id="dao" class="pack.dao.UserDaoImpl">
<property name="value" value="value in impl"></property>
</bean>
<!-- 法二:需要写出 UserService中该属性的 get 方法-->
<bean id="service" class="pack.service.UserService">
<property name="impl" ref="dao"></property>
<!-- 使用 impl.value -->
<property name="impl.value" value="value in impl">
</bean>
<bean id="dao" class="pack.dao.UserDaoImpl">
</bean>
<!-- 法三:级联赋值 -->
<bean id="service" class="pack.service.UserService">
<property name="impl">
<bean id="dao" class="pack.dao.UserDapImpl">
<property name="value" value="value in impl"></property>
</bean>
</property>
</bean>
public class Student {
private String[] courses;
private List<String> lists;
private Map<String,String> maps;
private Set<String> sets;
//对象类型
private List<Course> stuLists;
public void setCourses(String[] courses) { this.courses = courses; }
public void setLists(List<String> lists) { this.lists = lists; }
public void setMaps(Map<String, String> maps) { this.maps = maps; }
public void setSets(Set<String> sets) { this.sets = sets; }
public void setStuLists(List<Course> stuLists) { this.stuLists = stuLists; }
}
<bean id="student" class="pack.Student">
<!-- Array -->
<property name="courses">
<array>
<value>Java</value>
<value>MySQL</value>
</array>
</property>
<!-- List -->
<property name="lists">
<list>
<value>Python</value>
</list>
</property>
<!-- Map -->
<property name="maps">
<map>
<entry key="01" value="Ad"></entry>
</map>
</property>
<!-- Set -->
<property name="sets">
<set>
<value>Oracle</value>
</set>
</property>
<!-- List & Class -->
<property name="stuLists">
<list>
<ref bean="cour1"></ref>
<ref bean="cour2"></ref>
</list>
</property>
</bean>
<bean id="cour1" class="pack.Course">
<property name="cName" value="Java"></property>
</bean>
<bean id="cour2" class="pack.Course">
<property name="cName" value="Go"></property>
</bean>
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入名称空间 util -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--提取list集合属性注入-->
<util:list id="bookList">
<value>val-00</value>
<value>val-01</value>
<value>val-02</value>
</util:list>
<!--提取list集合属性注入使用-->
<bean id="book" class="pack.Book">
<property name="list" ref="bookList"></property>
</bean>
</beans>
Spring 中有两种 Bean,普通 Bean 和 FactoryBean。
区别:
普通 Bean:配置文件中定义的类型就是返回类型。
FactoryBean:返回类型可以和配置文件中定义的类型不一样。
<!--FactoryBean-->
<bean id="facBean" class="pack.UserA"></bean>
<!--NormalBean-->
<bean id="norBean" class="pack.UserB"></bean>
public class UserA implements FactoryBean {
//重写返回类型
@Override
public UserB getObject() throws Exception {
return new UserB();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
@Test
public void testFacBean() {
ApplicationContext context
= new ClassPathXmlApplicationContext("bean.xml");
//返回的不是UserA类型,而是UserB类型
UserB user = context.getBean("facBean", UserB.class);
System.out.println(user);
}
在 Spring 中设置 Bean 实例是单实例还是多实例。默认是单实例对象。
//单实例是指多次获取相同的 Bean,返回的是同一个实例。
<!-- 通过 scope 标签来设置单实例 singleton 或多实例 prototype -->
<bean id="user" class="pack.User" scope="prototype"></bean>
单实例 singleton 和多实例 prototype 的区别:
设置为 singleton 时,在加载配置文件的时候就会创建单实例对象。
设置为 prototype 时,在调用 getBean() 时创建多实例对象。
从 Bean 实例创建到销毁的过程:
1. 通过构造器创建 Bean 实例
2. 依赖注入
3. 调用 Bean 的初始化方法
4. 创建完成,对象可以使用
5. 当容器关闭时,调用 Bean 的销毁方法
public class Demo {
private String name;
public Demo() { System.out.println("执行无参构造方法"); }
public void setName(String name) {
this.name = name;
System.out.println("调用 set 方法实现依赖注入");
}
public void init() { System.out.println("执行定义的初始方法"); }
public void destory() { System.out.println("执行定义的销毁方法"); }
@Override
public String toString() {
return "调用创建对象的 toString 方法";
}
}
<!-- 需要配置初始方法和销毁方法 -->
<bean id="demo" class="pack.Demo" init-method="init" destroy-method="destory">
<property name="name" value="my name"></property>
</bean>
@Test
public void testBeanLife() {
ApplicationContext context
= new ClassPathXmlApplicationContext("beanlife.xml");
Demo demo = context.getBean("demo", Demo.class);
System.out.println(demo);
//需要手动关闭容器
((ClassPathXmlApplicationContext)context).close();
}
输出结果:
D:\Tools\JDK\bin\java.exe...
执行无参构造方法
调用set方法实现依赖注入
执行定义的初始方法
调用创建对象的toString方法
执行定义的销毁方法
Process finished with exit code 0
1. 通过构造器创建 Bean 实例
2. 依赖注入
3. 把实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
4. 调用 Bean 的初始化方法
5. 把实例传递给 Bean 后置处理器的 postProcessAfterInitialization 方法
6. 创建完成,对象可以使用
7. 当容器关闭时,调用 Bean 的销毁方法
//创建一个后置处理器
public class Post implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用配置处理器的 Before 方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用配置处理器的 After 方法");
return bean;
}
}
<!-- 后置处理器对同一配置文件中的其他 bean 都起作用 -->
<bean id="post" class="pack.Post"></bean>
添加后置处理器后的运行结果:
D:\Tools\JDK\bin\java.exe...
执行无参构造方法
调用set方法实现依赖注入
调用配置处理器的 Before 方法
执行定义的初始方法
调用配置处理器的 After 方法
调用创建对象的 toString 方法
执行定义的销毁方法
Process finished with exit code 0
根据制定的装配规则(属性名称或属性类型),Spring 自动将匹配的属性值进行注入。
public class User {
private PassWord pwd;
public void setPassWord(Password pwd) {
this.pwd = pwd;
}
}
<!--
通过 autowire 标签来实现自动装配
byName 方式根据 id 与属性名来装入,要求装入的 bean 的 id 与属性名相同
byType 方式根据属性的类型来装入,要求指定类型的 bean 只能定义一个
-->
<bean id="user" class="pack.User" autowire="byName"></bean>
<bean id="pwd" class="pack.Password"></bean>
例:配置 druid 数据库的连接池
<!-- 直接配置连接池方式 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value ="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></ property>
<property name="password" value="root"></ property>
</bean>
#外部 properties 文件
prop.driverClass = com.mysql.jdbc.Driver
prop.url = jdbc:mysql://localhost:3306/userDb
prop.userName = root
prop.password = root
<!-- 引入外部属性文件方式 -->
<!-- step01 引入 context 名称空间 -->
...
<!-- step02 在配置文件中使用标签引入外部文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- step03 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></ property>
<property name="username" value="${prop.userName}"></ property>
<property name="password" value="${prop.password}"></ property>
</bean>
Spring 针对 Bean 管理中创建对象提供的注解,功能一样,用在不同层
1) @Component
2) @Service
3) @Controller
4) @Repository
使用注解方式创建对象的步骤:
1. 引入额外的外部依赖 spring-aop-5.6.2.RELEASE.jar
2. 开启组件扫描
2.1 引入名称空间 context
2.2 使用 component-scan 标签开启组件扫描
<!--
开启组件扫描
1. 多个包的路径用 , 隔开
2. 写需要扫描的包的上层目录也行
-->
<context:component-scan base-package="pack.dao, pack.service"></context:component-scan>
<!--
<context:component-scan base-package="pack"></context:component-scan>
-->
/**
* 相当于 <bean id="userService" class="classPath"></>
* value 可以不写,默认值是类名(驼峰命名,第一个首字母小写)
* @Component 可以用 @Service、@Controller、@Repository 替代
*/
@Component(value = "userService")
public class UserService {
//...
}
<!-- 配置一:只扫描 spring 包下的 @Component 注解 -->
<context:component-scan base-package="com.spring5" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
<!-- 配置二:扫描 spring 报下除了 @Component 的其他注解 -->
<context:component-scan base-package="com.spring5">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
注入方式:
1) @Autowired:根据属性类型进行自动注入
2) @Qualifier:根据属性名称进行自动注入,要和 @Autowired 一起使用
3) @Resource:可以根据类型也可根据名称注入,是 javax.annotation.Resource 中的注解,不建议使用
4) @Value:注入基本类型属性
注入步骤:
1. 基于注释实现对象创建
2. 定义属性,并在属性上面实现注解。不需要定义 set 方法
@Service
public class UserService {
//根据类型注入
@Autowired
private UserDao dao;
//基本类型注入
@Value(value="hello")
private String name;
/**
* 当出现Use人Dao具有多个实现类的时候,配合 @Qualifier来使用
* @Autowired
* @Qualifier(value="myUserDaoImp1")
* private UserDao dao;
*/
/**
* //@Resource //根据类型注入
* @Resource(name="myUserDaoImp1") //根据名称和类型注入
* private UserDao dao;
*/
}
@Repository(value="myUserDaoImpl")
public class UserDaoImpl implements UserDao{
//...
}
//实际开始中一般是基于 SpringBoot 实现
//创建测试类代替 XML 配置文件
@Configuration
@ComponentScan(basePackages = {"com.spring5"})
public class SpringConfig {
}
//获取 ApplicationContext 对象
@Test
public void testService() {
ApplicationContext context
= new AnnotationConfigApplicationContext(SpringConfig.class);
//...
}
原文:https://www.cnblogs.com/zhugaoxiang/p/14314798.html