再再再再次理解IOC
控制反转,对一个对象控制权的反转。再使用spring之后,会把对象的创建,初始化和销毁等操作交给spring容器来管理,也就是在项目启动的时候,bean就会增加注册到spring容器当中去,而其他Bean需要使用这个Bean的话,就不需要自己去new,而是直接去Spring容器里拿
“控制反转其实就是控制权的反转,本来对象的控制权可能在另一个对象那边,而spring的出现让对象的控制权留在了容器当中”
并开始简单尝试:
首先创建一个新的Maven项目:
next
finish
先在pom.xml这边导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
</dependencies>
并创建spring-config : applicationContext
在这个applicationContext.xml下面就可以配置所有注册到spring中的Bean文件了
首先在java下面创建包,在包里创建一个User类
package org.wzw.ioc.model;
public class User {
private String username;
private String address;
private Integer userid;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
}
并创建一个Main方法用来调用User
正常是这样的
package org.wzw.ioc.model;
public class Main {
public static void main(String[] args) {
User user = new User();
System.out.println("user"+user);
}
}
对应的输出:
进行修改:
首先在applicationContext.xml文件这边创建一个bean
<bean class="org.wzw.ioc.model.User" id="user"> </bean>
这里有个细节,class属性是作为需要注册的bean的全路径,而id则表示bean的唯一标记,也可以把name作为bean的标记,但一般情况下,id和name是一样的。
而且bean其实是用了java里的反射,通过class这里可以看到
package org.wzw.ioc;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.wzw.ioc.model.User;
public class Main {
public static void main(String[] args) {
/* User user = new User();
System.out.println("user"+user);*/
//针对bean来创建实例,是利用了反射
//当这行代码执行的时候,user就会被初始化,并且放在spring容器当中
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//就和反射一样 被初始化了 就可以进行调用了
User user = (User) context.getBean("user");
//感觉上面这句代码的意思就是 User user=new User();
User user1 = context.getBean("user", User.class);
//另一种方法
User user2 = context.getBean(User.class);
//这个方法是存在缺陷的
System.out.println("user = " + user);
System.out.println("user1 = " + user1);
System.out.println("user2 = " + user2);
}
}
而针对其中这些个方法,还是有不同的getBean的方法的,并且对应直接.class的方法,其实是不太好的
再者就是关于属性的注入
1.构造方法注入:
首先看到是需要在User这边创建一个有参的构造方法
public User(String username, String address, Integer userid) {
this.username = username;
this.address = address;
this.userid = userid;
}
然后会看到,首先是因为存在了有参的构造函数,上一个会报错,提示你需要输入构造函数的参数,其次关于输入构造函数,有index(从0 开始)和name(对应的name)
<bean class="org.wzw.ioc.model.User" id="user">
<constructor-arg name="address" value="11"/>
<constructor-arg name="userid" value="1"/>
<constructor-arg name="username" value="231"/>
</bean>
并且可以Override toString方法用来输出
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
", userid=" + userid +
'}';
}
然后调用就会发现
set方法注入
<bean class="org.wzw.ioc.model.User" id="user2">
<property name="username" value="11111"/>
</bean>
这里的id是user2
当然这样就需要在User里存在一个无参的构造函数
并在Main方法上
User user2 = (User) context.getBean("user2");
System.out.println("user2 = " + user2);
这样子输出,按照那个toString方法,没有设置 address和userid,所以他们都是默认null
并且可以通过一个小方法来验证,这个property就是调用了对应的set方法
可以在setUsername这边随便写一个
public void setUsername(String username) {
System.out.println("应该是用了这个方法吧");
this.username = username;
}
再运行一次
有个小细节:set中的属性名,都不是你在bean中定义的属性名,而是“通过java内省机制分析出来的属性名,也就是从get/set方法中分析出来的属性名"
p名称空间注入
本质上也是调用set/get方法
<bean class="org.wzw.ioc.model.User" id="user3" p:username="www" p:userid="11"/>
同理 创建User3
User user3 = (User) context.getBean("user3");
System.out.println("user3= " + user3);
运行
这个也证实了,调用的是set方法
外部Bean的注入
主要是静态工厂注入和实例工厂注入
复杂属性注入
1.对象注入 (就是之前的方法)
首先随便创建一个对象,并在User中把这个对象导入
package org.wzw.ioc.model;
public class Cat {
private String catName;
private int catId;
@Override
public String toString() {
return "Cat{" +
"catName='" + catName + '\'' +
", catId=" + catId +
'}';
}
public String getCatName() {
return catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
public int getCatId() {
return catId;
}
public void setCatId(int catId) {
this.catId = catId;
}
}
并在User中加入 Cat对象,以及Get/Set方法和重写toString方法
<bean class="org.wzw.ioc.model.User" id="user4">
<property name="username" value="name"/>
<property name="address" value="value"/>
<property name="userid" value="8"/>
<property name="cat" ref="cat"/>
</bean>
<bean class="org.wzw.ioc.model.Cat" id="cat">
<property name="catId" value="101"/>
<property name="catName" value="mayo"/>
</bean>
运行一下
但很有意思的是,我并没有在User的构造函数这边写上Cat,结果效果是一样的
数组的注入
在User这边创建一个int[]类型的数组
并加入Get/Set方法 和修改toString方法
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
", userid=" + userid +
", cat=" + cat +
", goodNums=" + Arrays.toString(goodNums) +
'}';
}
然后是xm文件
<bean class="org.wzw.ioc.model.User" id="user4">
<property name="username" value="name"/>
<property name="address" value="value"/>
<property name="userid" value="8"/>
<property name="cat" ref="cat"/>
<property name="goodNums">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
</bean>
其中,array可以用list节点来替代 ,并且在list和array中,是可以加入bean对象,也就是在里面也还能再加东西(当然,前提是同一种数据类型)
Map注入
private Map<String,Object> details;
并加入Get/Set方法 和修改toString方法
xml文件中加入
<property name="details">
<map>
<entry key="衣服" value="扣扣扣"></entry>
<entry key="身份证" value="12312"/>
</map>
</property>
运行
Properties(配置)注入
private Properties info;
并加入Get/Set方法 和修改toString方法
xml文件中加入
<property name="info">
<props>
<prop key="age">21</prop>
<prop key="性别">男</prop>
</props>
</property>
运行
java配置
有三种配置方法
1.java配置(适合spring-boot)
创建一个SayHello.java
package org.wzw.ioc.javaconfig;
public class SayHello {
public String sayHello(String name){
return "hello"+name;
}
}
使用java配置的话,就用一个配置类去代替之前的.xml
package org.wzw.ioc.javaconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//作用类似于applicationContext.xml
@Configuration
//加这个注解说明他是个配置类
public class JavaConfig {
//就是把原本放在xml文件里的东西放到这里来
@Bean
SayHello sayHello(){
return new SayHello();
}
//定义方法,方法返回对象 这个返回的对象就是new,并且会把这个返回的对象注入到Spring容器当中
}
在main函数里写入
AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext(JavaConfig.class);
SayHello sayHello = (SayHello) acx.getBean("sayHello");
System.out.println(sayHello.sayHello("wwwww"));
运行
小细节就是,这里的bean是可以改名字的,改成@Bean(“asdas”)都行,默认是方法名,并且由于getBean的各种各样,也有好几种写法
2.自动扫描注入
他可以是java配置的,也可以是xml配置的,使用频率很高
这里就涉及到了注解:这个真的老生常谈了
- @Component 在其他组件上添加注解
- @Repository 在Dao层添加注解
- @Service 在Service层添加注解
- @Controller 在Controller层添加注解
他们都是由Componeent 做出来的,并且功能也是一样, 但变成三个的原因就是为了在不同类上添加的时候方便一点
package org.wzw.ioc.Service;
import org.springframework.stereotype.Service;
import org.wzw.ioc.model.User;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
public List<String> getAllUsers(){
List<String> users=new ArrayList<>();
for(int i=0;i<10;i++){
users.add(i, "a");
}
return users;
}
}
2.1java配置
//作用类似于applicationContext.xml
@Configuration
@ComponentScan(basePackages = "org.wzw.ioc.Service")
//加这个注解就可以进行扫描对应包下面的实体类了,默认是扫描配置类,也就是JavaConfig这个类下面的包以及所在包的子包下面的类
//加这个注解说明他是个配置类
public class JavaConfig {
//就是把原本放在xml文件里的东西放到这里来
}
//定义方法,方法返回对象 这个返回的对象就是new,并且会把这个返回的对象注入到Spring容器当中
}
main方法
AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext(JavaConfig.class);
UserService getAllUsers = acx.getBean("getAllUsers", UserService.class);
System.out.println(getAllUsers.getAllUsers());
运行一下
这里的名字,那就是直接根据Serviece
2.1xml配置
在xml文件里
<context:component-scan base-package=“org.wzw.ioc.Service”/>
修改main方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = context.getBean(UserService.class);
System.out.println(bean.getAllUsers());
这里可是用的是ClassPathXmlApplicationContext
对象注入问题
也有三种方式
- @Autowired
- @Resources
- @Injected
总的来说就是 Injected不太使用,而Autowired则是根据类型来查找的,所以有个潜在的条件就是类型只可以有一个对象(一般是最常见的),而Resources则是根据名称查找的,默认情况下,定义的变量名就是查找的名称,但也是可以再注解中手动指定的。所以,如果一个类中存在多个实例的时候,一般是使用Resources,而如果一定要用Autowired的话,是需要@Qualifier这个注解配合使用的,这样才能可以通过变量名查找到变量
package org.wzw.ioc.Dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public String userDao(){
return "helloDao";
}
}
然后注入一下
@Autowired
UserDao userDao;
public String HelloDao(){
return userDao.userDao();
}
对应的,xml就需要改一下,不然扫描不到
<context:component-scan base-package="org.wzw.ioc"/>
条件注解
依次创建
LinuxCondition:
package org.wzw.ioc.cmd;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
//这个需要继承一个方法
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//就是返回这个数据是否是win
return context.getEnvironment().getProperty("os.name").toLowerCase().contains("linux");
}
}
LinuxShowCmd:
package org.wzw.ioc.cmd;
public class LinuxShowCmd implements ShowCmd{
@Override
public String ShowCmd() {
return "ls";
}
}
ShowCmd
package org.wzw.ioc.cmd;
public interface ShowCmd {
//这个接口实现一个方法
public String ShowCmd();
}
WindowsCondition:
package org.wzw.ioc.cmd;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").toLowerCase().contains("win");
}
}
WindowsShowCmd:
package org.wzw.ioc.cmd;
public class WindowsShowCmd implements ShowCmd{
@Override
public String ShowCmd() {
return "dir";
}
}
然后是JavaConfig
public class JavaConfig {
//就是把原本放在xml文件里的东西放到这里来
@Bean("www")
@Conditional(WindowCondition.class)
ShowCmd winCmd(){
return new WindowsShowCmd();
}
@Bean("www")
@Conditional(LinuxCondition.class)
ShowCmd linuxCmd(){
return new LinuxShowCmd();
}
}
并且在Main函数这边测试一下:
a
运行,因为现在是在windows下面的: