Spring 02 控制反转
简介
IOC
IOC(Inversion of Control),即控制反转。
这不是一项技术,而是一种思想。
其根本就是对象创建的控制权由使用它的对象转变为第三方的容器,即控制权的反转。
DI
DI(Dependency Injection),即依赖注入,IOC 的实现方式。
所谓依赖注入,就是由 IOC 容器在运行期间,动态地将某种依赖关系注入到对象之中。
优点
对象之间的解耦
对象之间不会相互影响,减少程序出错的可能性,提高了代码的灵活性和可维护性。
缺点
生成对象的步骤变得更为复杂
会增加团队成员学习和认识的培训成本。
运行效率有一定的损耗
由于IOC容器生成对象是通过反射方式,相较于传统的创建对象方式效率是要低一些的。
需要大量的配置工作
由于生成对象交给了第三方容器,对第三方容器的配置是较为繁琐的。
体验
创建一个普通的 Maven 项目,引入 spring-context 依赖。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
在 resources 目录下创建一个 spring 的配置文件。
官方推荐命名为 applicationContext.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">
</beans>
配置需要注册到 Spring 容器的 Bean。
<?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 class="org.javaboy.Book" id="book"/>
</beans>
class 表示需要注册的 bean 的全路径。
id 表示 bean 的唯一标记。
也可以用 name 属性作为 bean 的标记,它们之间有一定的区别,后面会介绍。
加载配置文件
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
从容器中去获取对象
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = ctx.getBean("book", Book.class);
ClassPathXmlApplicationContext 是去 classpath 下查找配置文件的加载方式。
也可以使用 FileSystemXmlApplicationContext ,它会从操作系统路径下去寻找配置文件。
FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\Work\\Project\\training\\src\\main\\resources");
Book book = (Book) ctx.getBean("book");
这种方式要写较长的系统路径,且如果项目路径变更就需要修改,不推荐使用。
通过 Class 获取 Bean
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = ctx.getBean(Book.class);
这种方式有一个很大的弊端:如果存在多个实例,这种方式就不可用。
例如,xml 文件中存在两个同 class 的 Bean。
<?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 class="org.javaboy.Book" id="book"/>
<bean class="org.javaboy.Book" id="book2"/>
</beans>
此时,如果通过 Class 去查找 Bean,会报如下错误:
所以,一般建议使用 name 或者 id 去获取 Bean 的实例。
属性注入
构造方法
通过 Bean 的构造方法给 Bean 的属性注入值
给 Bean 添加对应的构造方法:
public class Book implements Serializable {
private static final long serialVersionUID = 5492270562431552420L;
private Integer id;
private String name;
private Double price;
public Book() {
}
public Book(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
}
在 xml 文件中注入 Bean
<?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="book" class="cn.sail.training.spring.ioc.Book">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="红楼梦"/>
<constructor-arg index="2" value="33"/>
</bean>
</beans>
constructor-arg 中的 index 和 Book 中的构造方法参数一一对应。
写的顺序可以颠倒,但是 index 的值和 value 要一一对应。
另一种构造方法中的属性注入,则是通过直接指定参数名来注入
<?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="book1" class="cn.sail.training.spring.ioc.Book">
<constructor-arg name="id" value="2"/>
<constructor-arg name="name" value="西游记"/>
<constructor-arg name="price" value="40"/>
</bean>
</beans>
如果有多个构造方法,则会根据给出参数个数以及参数类型,自动匹配到对应的构造方法上,进而初始化一个对象。
set 方法
<?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">
<!--set方法注入:可以较直观的看到定义的属性,推荐使用-->
<bean id="user" class="cn.sail.training.spring.ioc.User">
<property name="id" value="1"/>
<property name="age" value="27"/>
<property name="name" value="哈哈"/>
</bean>
</beans>
set 方法注入,不是根据属性名对应的值,而是根据 get/set 方法分析出来的属性名。
如果改变set方法的名称,就算属性名不变,也会找不到值而报错。
p 名称空间
<?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">
<bean id="book2" class="cn.sail.training.spring.ioc.Book" p:id="2" p:name="西游记" p:price="33"/>
</beans>
这种方式本质上也是调用了set方法,但层次结构不清晰,且需要额外引进标签值,不推荐使用。
静态工厂
提供一个 OkHttpClient 的静态工厂
import okhttp3.OkHttpClient;
public class OkHttpUtils {
private static OkHttpClient okHttpClient;
public static OkHttpClient getInstance() {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder().build();
}
return okHttpClient;
}
}
在 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">
<!--静态工厂注入-->
<bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>
</beans>
这个配置表示 OkHttpUtils 类中的 getInstance 是我们需要的实例,实例的名字就叫 okHttpClient。
然后,在 Java 代码中,获取到这个实例,就可以直接使用了。
实例工厂
实例工厂就是工厂方法是一个实例方法,这样,工厂类必须实例化之后才可以调用工厂方法。
<?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">
<!--实例工厂注入-->
<bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>
<bean id="okHttpClient1" class="okhttp3.OkHttpClient" factory-bean="okHttpUtils"/>
</beans>
复杂属性注入
对象
可以通过 xml 注入对象,通过 ref 来引用一个对象。
<bean id="user1" class="cn.sail.training.spring.ioc.User">
<property name="cat" ref="cat"/>
</bean>
<bean id="cat" class="cn.sail.training.spring.ioc.Cat">
<property name="name" value="小白"/>
<property name="color" value="白色"/>
</bean>
数组
<bean id="user2" class="cn.sail.training.spring.ioc.User">
<property name="favorites">
<array>
<value>足球</value>
<value>篮球</value>
<value>乒乓球</value>
</array>
</property>
</bean>
集合
即可以通过 ref 使用外部定义好的 Bean,也可以直接在 list 或者 array 节点中定义 bean。
<bean id="user3" class="cn.sail.training.spring.ioc.User">
<property name="cats">
<list>
<ref bean="cat"/>
<bean id="cat2" class="cn.sail.training.spring.ioc.Cat">
<property name="name" value="小黑"/>
<property name="color" value="黑色"/>
</bean>
</list>
</property>
</bean>
Map
<bean id="user4" class="cn.sail.training.spring.ioc.User">
<property name="map">
<map>
<entry key="name" value="sail"/>
<entry key="age" value="27"/>
</map>
</property>
</bean>
Properties
<bean id="user5" class="cn.sail.training.spring.ioc.User">
<property name="info">
<props>
<prop key="name">sail</prop>
<prop key="age">27</prop>
</props>
</property>
</bean>
Java 配置
在 Spring 中,想要将一个 Bean 注册到 Spring 容器中,整体上来说,有三种不同的方式
- XML 注入
- Java 配置(通过 Java 代码将 Bean 注册到 Spring 容器中)
- 自动化扫描
Java 配置的方式注册有以下步骤:
配置类上加 @Configuration 注解
表示这个类是一个配置类,它的作用相当于 applicationContext.xml。
定义方法,方法返回对象,方法上添加 @Bean 注解
表示将这个方法的返回值注入的Spring容器中去。
也就是说,@Bean 所对应的方法,就相当于 applicationContext.xml 中的 bean 节点。
@Configuration
public class Config {
@Bean
User user() {
return new User();
}
}
配置加载
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean(User.class);
Bean 的默认名称是方法名。如果想自定义方法名,直接在 @Bean 中进行配置。
如下配置表示修改 Bean 的名字为 sail。
@Configuration
public class Config {
@Bean("sail")
User user() {
return new User();
}
}
配置加载
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean("sail", User.class);
配置类需要在项目启动时加载才生效。
自动化配置
自动化配置既可以通过 Java 配置来实现,也可以通过 xml 配置来实现。
类自动化扫描
类自动化扫描的注解一共有四个
- @Component
- @Repository
- @Service
- @Controller
这四个中,另外三个都是基于 @Component 做出来的,从目前的源码来看,功能也是一致的,使用另外三个的目的主要是区分业务。
- 在 Service 层上,添加注解时,使用 @Service。
- 在 Dao 层,添加注解时,使用 @Repository。
- 在 Controller 层,添加注解时,使用 @Controller。
- 在其他组件上添加注解时,使用 @Component。
比如:
@Service
public class UserService {
}
添加完成后,自动化扫描有两种方式
通过 Java 代码配置自动化扫描。
通过 xml 文件来配置自动化扫描。
Java代码配置自动扫描
@Configuration
@ComponentScan(basePackages = "cn.sail.training.spring.ioc.service")
public class JavaConfig {
}
然后,在项目启动中加载配置类,在配置类中,通过 @ComponentScan 注解指定要扫描的包,然后就可以获取 UserService 的实例了。
如果不指定,默认情况下扫描的是配置类所在的包下面的 Bean 以及配置类所在的包下的子包下的类。
Bean 的名字叫什么
默认情况下,Bean 的名字是类名首字母小写。例如上面的 UserService,它的实例名,默认就是 userService。如果开发者想要自定义名字,就直接在 @Service 注解中添加即可。
有几种扫描方式
上面的配置,我们是按照包的位置来扫描的。也就是说,Bean 必须放在指定的扫描位置,否则,即使你有 @Service 注解,也扫描不到。
除了按照包的位置来扫描,还有另外一种方式,就是根据注解来扫描。例如如下配置:
@Configuration
@ComponentScan(basePackages = "cn.sail.training.spring.ioc", useDefaultFilters = true, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class JavaConfig {
}
这个配置表示扫描指定包下的所有 Bean,但是除了 Controller。
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"
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/spring-context.xsd
">
<!--配置自动化扫描-->
<context:component-scan base-package="cn.sail.training.spring.ioc.service"/>
</beans>
这行配置表示扫描指定包下的所有 Bean。也可以按照类来扫描。
也可以在 XML 配置中按照注解的类型进行扫描:
<!--按照注解的类型进行扫描-->
<context:component-scan base-package="cn.sail.training.spring.ioc" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
对象注入
自动扫描时的对象注入有三种方式:
@Autowired
@Resource
@Injected
@Autowired 是根据类型去查找,然后赋值,这就有一个要求,这个类型只可以有一个对象,否则就会报错。
@Resources 是根据名称去查找,默认情况下,定义的变量名,就是查找的名称,也可以在注解中手动指定。如果名称找不到,会再根据类型去找,类型也找不到才会报错。
所以,如果一个类存在多个实例,那么就应该使用 @Resources 去注入。
如果非要使用 @Autowired,需要配合另外一个注解 @Qualifier。
在 @Qualifier 中可以指定变量名,两个一起用就可以实现通过变量名查找到变量。
@Service
public class UserService {
@Autowired
private User user;
public String hello() {
return user.getName();
}
}
条件注解
条件注解就是在满足某一个条件的情况下,生效的配置。
获取 Windows 和 Linux 下的不同文件目录查看命令
定义一个显示文件夹目录的接口。
public interface ShowCmd {
String showCmd();
}
分别实现 Windows 下的实例和 Linux 下的实例。
public class WinShowCmd implements ShowCmd{
@Override
public String showCmd() {
return "dir";
}
}
public class LinuxShowCmd implements ShowCmd{
@Override
public String showCmd() {
return "ls";
}
}
定义两个条件,一个是 Windows 下的条件,另一个是 Linux 下的条件。
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().matches("linux");
}
}
在定义 Bean 的时候,就可以去配置条件注解了。
这里一定要给两个 Bean 取相同的名字,这样在调用时,才可以自动匹配。
给每一个 Bean 加上条件注解,当条件中的 matches 方法返回 true 的时候,这个 Bean 的定义就会生效。
@Bean("showCmd")
@Conditional(WindowsCondition.class)
ShowCmd winCmd() {
return new WinShowCmd();
}
@Bean("showCmd")
@Conditional(LinuxCondition.class)
ShowCmd linuxCmd() {
return new LinuxShowCmd();
}
多环境切换
研发中,如何在开发、生产、测试环境之间进行快速切换是很重要的。
Spring 中提供了 Profile 来解决这个问题。
Profile 的底层就是条件注解。
这个从 @Profile 注解的定义就可以看出来。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
其中的 ProfileCondition 就通过实现 Condition 和重写 matches 来进行条件判断:
class ProfileCondition implements Condition {
ProfileCondition() {
}
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
do {
if (!var4.hasNext()) {
return false;
}
value = var4.next();
} while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true;
} else {
return true;
}
}
}
定义一个 DataSource
public class DataSource implements Serializable {
private static final long serialVersionUID = 3802892946419732473L;
private String url;
private String username;
private String password;
@Override
public String toString() {
return "DataSource{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在配置 Bean 时,通过 @Profile 注解指定不同的环境
@Bean("ds")
@Profile("dev")
DataSource devDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl("devUrl");
dataSource.setUsername("devUserName");
dataSource.setPassword("devPassWord");
return dataSource;
}
@Bean("ds")
@Profile("prod")
DataSource prodDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl("prodUrl");
dataSource.setUsername("prodUserName");
dataSource.setPassword("prodPassWord");
return dataSource;
}
加载配置类
需要先设置当前环境,然后再去加载配置类。
AnnotationConfigApplicationContext不能先加载配置类,否则refresh方法会报错。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(JavaConfig.class);
ctx.refresh();
DataSource dataSource = ctx.getBean("ds", DataSource.class);
System.out.println(dataSource);
环境的切换,也可以在 XML 文件中配置。
如下配置在 XML 文件中,必须放在其他节点后面,否则会层级混乱而报错。
<beans profile="dev">
<bean id="dataSource" class="cn.sail.training.spring.ioc.conditionAnnotation.DataSource">
<property name="url" value="devUrl"/>
<property name="username" value="devUserName"/>
<property name="password" value="devPassWord"/>
</bean>
</beans>
<beans profile="prod">
<bean id="dataSource" class="cn.sail.training.spring.ioc.conditionAnnotation.DataSource">
<property name="url" value="prodUrl"/>
<property name="username" value="prodUserName"/>
<property name="password" value="prodPassWord"/>
</bean>
</beans>
启动类中设置当前环境并加载配置
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles("prod");
ctx.setConfigLocation("applicationContext.xml");
ctx.refresh();
DataSource dataSource = ctx.getBean("dataSource", DataSource.class);
System.out.println(dataSource);
Bean 的作用域
从 Spring 容器中多次获取同一个 Bean,默认情况下,获取到的实际上是同一个实例。
XML 配置
通过设置 scope 属性,我们可以调整默认的实例个数。
<bean id="book" class="cn.sail.training.spring.ioc.Book" scope="prototype">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="红楼梦"/>
<constructor-arg index="2" value="33"/>
</bean>
scope 的值为 singleton(默认),表示这个 Bean 在 Spring 容器中,是以单例的形式存在。
如果值为 prototype,表示这个 Bean 在 Spring 容器中不是单例,多次获取将拿到多个不同的实例。
还有两个取值,request 和 session,这两个取值在 web 环境下有效。
Java配置
@Bean("sail")
@Scope("prototype")
User user() {
return new User();
}
在 Java 代码中,我们可以通过 @Scope 注解指定 Bean 的作用域。
在自动扫描配置中,也可以指定 Bean 的作用域。
id 和 name 的区别
name 支持取多个值
多个 name 之间,用 , 隔开。
<bean name="book3,book4,book5" class="cn.sail.training.spring.ioc.Book" scope="prototype">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="红楼梦"/>
<constructor-arg index="2" value="33"/>
</bean>
此时,通过 book3、book4、book5 都可以获取到当前对象。
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book3 = ctx.getBean("book3", Book.class);
Book book4 = ctx.getBean("book4", Book.class);
Book book5 = ctx.getBean("book5", Book.class);
id 不支持有多个值
如果强行用 , 隔开,它还是一个值。
<bean id="user6,user7,user8" class="cn.sail.training.spring.ioc.User">
<property name="id" value="1"/>
<property name="age" value="27"/>
<property name="name" value="廖航"/>
</bean>
这个配置表示 Bean 的名字为 user6,user7,user8,具体调用如下。
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
User user6 = ctx.getBean("user6,user7,user8", User.class);
混合配置
混合配置就是 Java 配置 + XML 配置。
混用的话,可以在 Java 配置中引入 XML 配置。
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig {
}
Spring 02 控制反转的更多相关文章
- 【SSH】——spring的控制反转和依赖注入
spring是一个轻量级的容器框架,主要是为了使企业的开发变得简单.高效.无论是从大小还是开销来讲,他都可以算是轻量级的,也是非侵入性的. 下图是spring的框架示意图,说到spring,就不得不提 ...
- Spring的控制反转(IOC)和依赖注入(DI)具体解释
Spring的控制反转(IOC)和依赖注入(DI)具体解释 首先介绍下(IOC)控制反转: 所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的.这样控制器就有应 ...
- Spring IoC控制反转创建实例
Spring IoC控制反转创建实例写一个配置文件beans.xml,配置文件的约束可以访问:完整链接:https://repo.spring.io/libs-release-local/org/sp ...
- 学习Spring IOC控制反转和DI依赖注入总结
30岁的小曹,20岁的身体,还在坚持在能力允许控制范围内22点睡觉,5点起床锻炼身体,好好学习,除了加班或者像今天这样的深夜,再一次写已经有X百万人写过的 spring Ioc 的总结博客. 一.IO ...
- Spring IOC(控制反转)思想笔记
Spring IOC(控制反转)思想笔记 IOC控制反转基本理念就是将程序控制权从程序员手中交给用户自定义,从而避免了因为用户一个小需求的变化使得程序员需要改动大量代码. 案例 如果按照之前javaw ...
- Spring学习02——控制反转、依赖注入
有两个人,张三和李四 package com.su.service; public class ZhangSan implements Tester{ public void test(){ Syst ...
- Spring、控制反转与依赖注入(概念)
Spring 一个开源的控制反转(Inversion of Control ,Ioc)和面向切面(AOP)的容器框架. 主要目的:简化开发 控制反转(Inversion of Control ,Ioc ...
- Spring总结——控制反转,注入(配置和注解两种方式)
一.Spring的容器: 1.什么是控制反转:传统的方法,当某个java对象A需要调用对象B时,是由调用者(对象A)通过new关键字来创建对象B的(也可以说类A依赖类B),而在Spring中,则是由s ...
- Spring 之 控制反转(IoC), 依赖注入(DI)和面向切面(AOP)
关于依赖注入, 这篇博文写的非常简单易懂. https://github.com/android-cn/blog/tree/master/java/dependency-injection 此外, 博 ...
随机推荐
- conda cheat sheet可直接百度这个名字
- Python3 filter()函数和map()函数
filter(function or None,iterable) 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换. 该接收两个参数,第 ...
- python各版本下载
python2源码压缩包 Python-2.7.9.tgz Python-2.7.10.tgz Python-2.7.11.tgz Python-2.7.12.tgz Python-2.7.13. ...
- BUUCTF-荷兰宽带数据泄露
荷兰宽带数据泄露 下载后发现是个BIN文件,之前也是做过类似的题目 RouterPassview打开BIn文件即可,搜索username或者password. 最后flag是username
- zabbix监控apache80端口
1.修改zabbix_agentd.conf 修改# EnableRemoteCommands=0 -->去掉注释修改为1--> EnableRemoteCommands=1 ###允许客 ...
- IP寻址与规划
一.IP寻址和子网划分 IP地址的主机部分可被分为三种地址:网络地址.主机地址和定向广播地址. 网络地址是网络号中的第一个地址.它用来将网络内的其他所有网段唯一标识为一个网段或广播域.定向广播地址是网 ...
- JS 会有变量提升和函数提升
JavaScript变量函数声明提升(Hoisting)是在 Javascript 中执行上下文工作方式的一种认识(也可以说是一种预编译),从字面意义上看,"变量提升"意味着变量和 ...
- 记一次 .NET 差旅管理后台 CPU 爆高分析
一:背景 1. 讲故事 前段时间有位朋友在微信上找到我,说他的 web 系统 cpu 运行一段时候后就爆高了,让我帮忙看一下是怎么回事,那就看吧,声明一下,我看 dump 是免费的,主要是锤炼自己技术 ...
- Cf #782 (Div. 2)
A. Red Versus Blue 题意 共有 n 个连续字符 ,其中有 a 个 R ,b 个 B (a+b=n),问怎么排列使 R 的最大连续个数最小,输出一种可能排列 思路 b 个B可以把a个 ...
- Collection子接口:Set接口
1.Set 存储的数据特点:无序的.不可重复的元素具体的:以HashSet为例说明: 1. 无序性:不等于随机性.存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的. 2. 不 ...