注意:改变Test代码不算侵入。改变dao和Service才算破坏开闭原则
修改配置文件XML就更不算侵入了
IOC创建bean的2种方式
- 如果bean的类里面无参和有参构造都有,那么spring是使用你的『无参构造器』来创建对象,(此时对象的各个属性还没有值),然后再通过『反射』对各个属性赋值。
- 无参构造在配置加载前就已经构造好了,此时相当于使用默认配置
- 如果你的类的构造器『只有有参构造器』,而没有无参的构造器,那么,Spring 会调用你有参的构造器去创建这个对象,并同时完成对其属性的赋值。此后,Spring 不再另外对你的属性赋值。
- 有参构造在配置加载完才能开始进行创建bean(因为需要读取配置的参数给有参构造传参)
- Spring 官方推荐使用有参构造器创建并初始化对象。有参构造可能会出现循环依赖的问题,见:循环依赖
一切带@Component的注解就相当于
<bean></bean>
xml有参构造的配置
通过xml配置有参构造创建bean,有三种方式
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="kuangshen"/>
</bean>
<!-- 第二种根据参数名字设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<!-- name指参数名 -->
<constructor-arg name="name" value="kuangshen"/>
</bean>
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen"/>
</bean>
spring创建bean的三种方式
这三种方式本质都是通过构造函数创建,这是基石。其实这三种基本没什么区别,2,3其实就相当于个简化的工具类而已
- 通过构造方法直接创建
- 通过工厂类的工厂方法
通过工厂对象的工厂方法
@Bean //DataSource是一个接口,DruidDataSource是它的一个实现类 public DataSource dataSource() throws Exception { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/scott?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); // ... return dataSource; }
@Bean public DataSource dataSource() throws Exception { Properties properties = new Properties(); properties.setProperty("driver", "com.mysql.cj.jdbc.Driver"); //... //该DruidDataSourceFactory其实就是根据传入的配置调用DruidDataSource()构造方法罢了 DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); return dataSource; }
注入方式
有三种注入方式
- 基于字段的依赖注入(有很多缺点)
- 基于构造函数的依赖注入
- 对于必须的依赖项,建议使用构造函数注入,使得它们成为不可变的对象且防止为空
基于setter的依赖注入
- 对于可选的依赖项建议采用setter方式注入
现在普遍采用java配置的方式进行注入,你将@Autowired或者@Resource用于setter方法上或者构造,或者字段注入,这就可以实现三种不同的注入。
Bean详解
pojo即具有getter/setter 方法的类
bean:即具有getter/setter 方法,无参构造方法,可序列化的类
bean的作用域
重点掌握单例和原型即可,其他作用域只能用于web开发
中文名叫做单例,原型,会话,请求
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
Bean作用域都是在bean那里增加一个scope属性即可
注解版 @Component 后添加 @Scope(“…”)
单例和原型还有一种xml配置法和一种注解配置法,这两种只适用于单例和原型
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON/SCOPE_PROTOTYPE)
**Singleton(单例)**
- **单例作用域在spring里也是bean默认的作用域**
- 当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
- Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域(即默认)。
测试:
```java
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user==user2);
}
结果为true。说明无论User创建几个对象,这些对象实际上都是一个单例对象
另外上面的例子也说明bean-user在spring容器中是一个bean对象,但是在类里他可以分化为class属性类不同对象
比如上面的一个bean分化为user1和user2两个对象
Prototype(原型)
即一个bean分化为多个对象,这些对象不==。(即每次getBean时都会创建一个新对象)见单例作用域
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
bean的注入/装配
自动装配
自动装配是使用spring满足bean依赖的一种方法
spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
- 在xml中显式配置;
- 在java中显式配置;
- 隐式的bean发现机制和自动装配。
即IOC的三种实现方式
这一小节重点看第三种,第二种以后会说
https://www.cnblogs.com/shinubi/p/4184943.html
spring-study/spring-auto-zhuangpei
byName(按名称自动化装配)
有三个类,猫狗人。猫狗是人的宠物,即人拥有猫狗。猫狗各有一个成员方法shout,分别输出喵~
和汪~
Person类privat``Cat``cat``;
private Dog dog;
private String str;
...一系列set方法和get方法
Test类创建People类对象people
people调用猫和狗的shout方法
输出喵~和汪~
XML
按之前的方法依赖是这样创建的<bean id="dog" class="com.rao.autozhuanpei.Dog"/>
<bean id="cat22" class="com.rao.autozhuanpei.Cat"/>
<bean id="people" class="com.rao.autozhuanpei.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="狂神"/>
</bean>
现在我们把people的bean对象的2个ref所在的property标签删除,增加一个autowire属性,得到<bean id="people" class="com.rao.autozhuanpei.People" ``**autowire="byName"**``>
<property name="name" value="狂神"/>
</bean>
可以发现没有2个ref属性
Test类依然可以调用猫狗的shout方法,输出喵~汪~
因为autowire属性为自动装配,byName属性值会使程序自动寻找set方法名set后面同名的bean对象
bean的第一个弊端:bean的Id必须与set方法的方法名值相同(首字母小写),而bean id又=对应私有成员属性的名字。即private Cat 8cat8,则id和set也得是8cat8
如假设people类里Cat cat的set方法叫做setCatMiao,那么会自动catMiao对象(对象首字母必须小写,后面字母大小写得给set的大小写一样,如catmiao,catMIAO都不可以)(一般也不会乱设置set方法名)
byName还有一个弊端:同一id的bean必须唯一,否则识别不出是哪个bean
比如有2个同名set方法(参数/类型有差异),就识别不出来。
byName不成功报空指针异常
(找不到)
byType(按类型自动装配)
byType弊端则是同一类型的bean必须唯一,否则失败。同时要保证bean类型与自动注入(set)的类型一致
byType不成功报不唯一异常(NoUniqueBeanDefinitionException)(识别不了)
实践测试:
将byName项目的autowire值
改为byType。即可
如果增加一个同类型的bean,则报错
注解实现自动装配
- (比xml配置更方便)实际上绝大多数时候都使用注解开发
java1.5以上,spring2.5以上才支持注解。不过话说现在也没人用这么老的版本继续开发吧,也就些老项目还用
在spring4之后,想要使用注解形式,必须得要引入aop的包,还要导入约束(xml中)
约束:xmlns:context``=``"http://www.springframework.org/schema/context"
放xml的其他xmlns旁边
配置注解支持:<context:annotation-config/>
spring-study/spring-zhujie 基于spring-autozhuangpei
<?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/>
<bean id="dog" class="com.rao.zhujie.Dog"/>
<bean id="cat" class="com.rao.zhujie.Cat"/>
<bean id="people" class="com.rao.zhujie.People"/>
</beans>
- 在成员变量/set方法前加上
@Autowired
,bean对象配置按最简单的形式配置。即可实现注解自动装配 - 自动装配的对象得在IOC容器中存在,一个对象时是byType装配(名字可随意),多个同类型对象时是先byType再byName装配(只选择同类型中符合名字的,一个都不符合报错)
- 用autowired实现自动装配可以省略不写set方法
- 注解实现自动装配时,String类型的变量必须单独设置bean,不能像以往一样用子标签
设置 .比如这里的People的name可以这样设置
<bean id="name" class="java.lang.String">
<constructor-arg name="original" value="kuang"/> </bean>
其他基本类型还不清楚,arg name也不清楚设置什么,先按original
设置
**@Qualifier**
用于搭配着autowired使用。假如**@autowired**
有多个对象时,按byType+Name装配。又想选择不符合的name的对象,就在@autowired后再加一个**@Qualifier(name="?")**
- 比如注解自动装配name,可以设置多个name的bean对象。想用哪个name值修改
**@Qualifier的value就好了(感觉有点鸡肋,既然还是得修改直接修改namebean的value不更方便吗)**
- @Qualifier用于注入方法的参数时可以单独使用,相当于隐藏了
@autowired
,如X(@Qualifier("dynamicDb") DataSource ds){}
- 比如注解自动装配name,可以设置多个name的bean对象。想用哪个name值修改
- @Resource(name,type)自动装配,type为bean的class
- resource注解是j2ee的注解,而@Autowired是spring的注解。使用autowired需要有@Controller、@Service、@Component、@Repository等注解
- resource一个就相当于autowired或者@Autowired+@Qualifier的功能结合
有一个value属性,未设置时相当于@Autowired。设置时相当于@Autowired+@Qualifier
Resource有一个参数时根据参数找name/type,无参时则是先byName再byType。2个参数时则全都要符合
@Value
仅仅读取配置文件注入基本类和string等@AutoWired
@Resource
属性值设置为空
**@Autowired**
有一个默认属性为required
值为布尔值,默认为真
注解设置属性的方法@?(属性=...)
如果设置为false,则允许xml里不创建该bean对象。比如上面的例子private Cat cat的注解属性值默认为true,则xml里cat的bean对象必须创建。否则报错
*(测试时先把各个类中的cat调用成员变量/方法的语句删除了,不然无论false还是true,只要没cat这个对象,就报错,这违背了最基本的原则)
还有一个构造器属性注解 **@Nullable**
使用时写在构造器参数列表最前面即可,用于基本类型的构造器
比如public People(@Nullable String name){...}
设置该注解允许一个基本类型值为空(即允许xml里不设置bean对象)
基本类型值设置可以为空是2种方法都可以,其他类型第一种。反正都用第一种就好了
可选注入
默认情况下,当我们标记了一个@Autowired后,Spring如果没有找到对应类型的Bean,它会抛出NoSuchBeanDefinitionException异常。可以给@Autowired增加一个required = false的参数。
在允许的情况下,java会自动将所有能注入的bean都进行注入,这时可以手动指定bean的注入顺序
使用
@Order(x)
x为>=1的整数 数字越小优先级越大public interface Validator { void validate(String email, String password, String name); }
下面三个实现类我觉得可以写为一个实现类,应该是为了展示一次多个注入的情况吧 ```java @Component public class EmailValidator implements Validator { public void validate(String email, String password, String name) {
if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}$")) { throw new IllegalArgumentException("invalid email: " + email); }
} }
@Component public class PasswordValidator implements Validator { public void validate(String email, String password, String name) { if (!password.matches(“^.{6,20}$”)) { throw new IllegalArgumentException(“invalid password”); } } }
@Component public class NameValidator implements Validator { public void validate(String email, String password, String name) { if (name == null || name.isBlank() || name.length() > 20) { throw new IllegalArgumentException(“invalid name: “ + name); } } }
```java
@Component
public class Validators {
@Autowired
List<Validator> validators;
public void validate(String email, String password, String name) {
for (var validator : this.validators) {
validator.validate(email, password, name);
}
}
}
@Component
@Order(1)
public class EmailValidator implements Validator {
...
}
@Component
@Order(2)
public class PasswordValidator implements Validator {
...
}
@Component
@Order(3)
public class NameValidator implements Validator {
...
}
条件注入
- 条件注入提供了很多注解,都是
@Conditional?
的形式,这里只列举4个个人觉得有用的 以下配置文件都是yaml型,properties是否可以不清楚
@ConditionalOnBean
(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)@ConditionalOnClass
(某个class位于类路径上,才会实例化一个Bean) 如一个类被maven引入了依赖,即被spring创建并存在- 参数为
?.class
多个类时{?.class,?.class}
- 参数为
@ConditionalOnExpression
(当表达式为true的时候,才会实例化一个Bean)@ConditionalOnProperty
prefix
为配置前缀name
为配置值havingValue
为期望值,如果读取到的值与期望值不符则falsematchIfMissing
为当不存在这个配置参数时,结果为true还是false
@ConditionalOnMissingBean
:当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个。如一些默认配置类会有该注解,表示允许用户自定义 不清楚和@scope区别,而且spring默认就是单例,这个不知道和单例区别@ConditionalOnClass({Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class}) 表示当这3个类存在时,才会创建bean 这里用于判断相关依赖是否被引入
@Configuration public class WebConfig { @Bean @ConditionalOnProperty(prefix = "rest", name = "auth-open", havingValue = "true", matchIfMissing = true) //rest.auth-open public AuthFilter jwtAuthenticationTokenFilter() { return new AuthFilter(); } }
先注入后续创建
一般用于配置类
@ConfigurationProperties //这里读取配置文件注入到属性中,但是没有@Component等注解,所以没有bean public class UploadConfig {...} //------------------------------------------ @EnableConfigurationProperties(MultipartProperties.class) //这里是对类进行bean创建 public class UploadConfig { private final MultipartProperties multipartProperties; @Autowired public UploadConfig(MultipartProperties multipartProperties) { //注入bean this.multipartProperties = multipartProperties; } }
创建第三方bean
如果一个Bean不在我们自己的package管理之内,例如jdk中某个类的对象,如何创建它?
- 在配置类中创建一个方法用于创建第三方bean,然后给该方法标记
@Bean
。 **@Bean和方法**
即创建一个容器对象,就等同于bean标签。bean id为方法名,bean class为返回值类型- 自定义类直接返回一个new即可,但是这并不是叫你直接调方法来创建个对象! 而是应该让spring帮我们调
- 在配置类中创建一个方法用于创建第三方bean,然后给该方法标记
- 注意使用了
@bean
就不要再使用@Component
,即便设置了别名也会出现NoUniqueBeanDefinitionException
之类型重复。 后面的情景复现不知道为什么没有出现该错误。。。 @Bean比@Component更加灵活些,如下,还可以初始化bean并通过set进行值设置,也可以通过构造方法设置
@Configuration @ComponentScan public class AppConfig { // 创建一个Bean: @Bean UserInfo createUserInfo() { UserInfo userinfo=new UserInfo("18"); userinfo.setID("001"); userinfo.setName("rao"); return userinfo; } }
初始化与销毁bean
有些时候,一个Bean在注入必要的依赖后,需要进行初始化(监听消息等)。在容器关闭时,有时候还需要清理资源(关闭连接池等)。
- 我们通常会定义一个init()方法进行初始化,定义一个shutdown()方法进行清理,然后,引入JSR-250定义的Annotation
Spring容器会对MailService
的Bean做如下初始化流程:
- 调用构造方法创建MailService实例;
- 根据@Autowired进行注入;
- 调用标记有@PostConstruct的init()方法进行初始化。
而销毁时,容器会首先调用标记有@PreDestroy的shutdown()方法。
Spring只根据Annotation查找无参数方法,对方法名不作要求。
导入依赖
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
@Component
public class MailService {
@Autowired(required = false)
ZoneId zoneId = ZoneId.systemDefault();
@PostConstruct
public void init() {
System.out.println("Init mail service with zoneId = " + this.zoneId);
}
@PreDestroy
public void shutdown() {
System.out.println("Shutdown mail service");
}
}
别名
注解配置别名
- 有的时候我们需要创建不同的对象,如建立多个数据库连接。这时如果直接使用
@Bean
创建多个方法,会报bean重复。可以使用别名来创建不同的bean。- bean同名或者同类型都会报错,所以都要指定别名
- 使用
@Bean("name")
或者@Bean+@Qualifier("name")
来指定别名 在
@bean
后添加@Primary
来指定主要bean。如果出现重复bean就默认使用主要bean,如果要使用非主要bean,就用别名来指定使用从bean@Configuration @ComponentScan public class AppConfig { @Bean @Primary // 指定为主要Bean @Qualifier("z") ZoneId createZoneOfZ() { return ZoneId.of("Z"); } @Bean @Qualifier("utc8") 使用从bean ZoneId createZoneOfUTC8() { return ZoneId.of("UTC+08:00"); } }
xml配置别名
alias 设置别名 , 为bean设置别名 , 可以设置多个别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
alias没什么用,被name别名完爆
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,逗号,分号,空格都可以隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
这里就设置了4个别名
导入其他人的配置文件。这样我们就能使用其他配置的bean对象<import resource="{path}/beans.xml"/>
团队合作常常用到,比如多个人负责不同的类,每个人创建了不同的配置文件和bean对象。最后模块整合为一个项目,就创建一个xml,把所有其他的xml导入。
如果不同xml有重名bean,就需要多设置些别名。导入时就会自动选择不重名的别名使用
FactoryBean
- 使用
bena工厂
创建bean需要实现bean工厂接口。 - 当一个Bean实现了FactoryBean接口后,Spring会先实例化这个工厂,然后调用getObject()创建真正的Bean。getObjectType()可以指定创建的Bean的类型,因为指定类型不一定与实际类型一致,可以是接口或抽象类。
因此,如果定义了一个FactoryBean,要注意Spring创建的Bean实际上是这个FactoryBean的getObject()方法返回的Bean。为了和普通Bean区分,我们通常都以XxxFactoryBean命名
@Component
public class ZoneIdFactoryBean implements FactoryBean<ZoneId> {
String zone = "Z";
@Override
public ZoneId getObject() throws Exception {
return ZoneId.of(zone);
}
@Override
public Class<?> getObjectType() {
return ZoneId.class;
}
}
依赖注入(DI)
依赖 : 指Bean对象的创建依赖于容器 .
注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配
有三种方式注入:
- 构造器注入
- set方法注入。set方法注入就是依赖注入的核心
- 扩展方式注入(即命名空间注入)
Set方法注入
DI即怎么把对象注入到容器内(即怎么设置bean对象)这一节就是教你怎么给各种类型的对象创建bean
如基本类型以外的类型,如集合对象,类对象怎么创建beanspring-study/spring-DI
@Address类 私有String类型变量address以及set,get
@Student类
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String>card;
private Set<String>games;
private String wife;
private Properties info;//配置文件
.......一系列get与set方法
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
还有一个toString方法输出所有的成员属性
一个bean对象可以有很多个
<bean id="address" class="com.rao.pojo.Address"></bean>
<bean id="student" class="com.rao.pojo.Student">
基本类型值注入<property name="name" value="饶泽东"></property>
value可以不跟name写一起,单独做一个标签,这时不需要双引号 <value>饶泽东</value>
Bean对象注入<property name="address" ref="address"></property>
数组注入<property name="books">
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>死国年纪</value>
</array>
</property>
List注入<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>
Map注入<property name="card">
<map>
<entry key="中国邮政" value="456456456465456"/>
<entry key="建设" value="1456682255511"/>
</map>
</property>
Set注入,这个是Set类型的注入。DI里的set注入是实现方式,是通过set方法注入。此Set非彼set
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>
NULL注入 <property name="wife"><null/></property> <null/>相当于一对完整的无值的<null>标签
Properties注入 Properties配置文件的写法类似Map的key-value
<property name="info">
<props>
<prop key="学号">20190604</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
最常用的就基本类型注入和Bean注入
扩展方式注入(命名空间)
好处是可以直接在bean id那里注入属性的值,不需要
但是p和c命名空间注入需要先导入xml约束才能使用
p命名注入(可以看成:无有参构造器注入)
1、P命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<``bean`` ``id``=``"user"`` ``class``=``"com.kuang.pojo.User"`` ``p:name``=``"狂神"`` ``p:age``=``"18"``/>
Test
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
``User`` ``user`` ``=``context``.``getBean``(``"user",User.class``);
``System``.``out``.``println``(``**user**``);
结果:User{“name=狂神”,”age=18”}
这种方法会自动调用get-set-toString方法
c命名注入(可以看成有参构造器注入)
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
``<``bean`` ``id``=``"user"`` ``class``=``"com.kuang.pojo.User"`` ``c:name``=``"狂神"`` ``c:age``=``"18"``/>
需要增加一个有参构造器
Test:和p命名注入一样
输出结果:和p命名注入一样
注解运用补充
注解环境之前说过,约束加依赖。见https://www.yuque.com/rain_/gl7kf7/wowarm#GQhlc
注解设置属性是@?(属性=…)
注解设置参数是@?(…)
注解参数只能有一个,也不能重复注释(>8高版本注解依赖允许重复注解)所以别想着用注解创建多个对象,注入多个值什么的
小提醒:spring里只有创建私有成员属性的bean才要求创建set,get方法。公有成员不需要set,get方法也可以直接创建bean对象
@Component三个衍生注解
SpringMVC中,给对应的架构层类添加以下注解即表示该类属于哪个架构层。即表示将该类交由spring容器托管
用法为:写在类前
@Controlle
r:web层(表示层)@Service
:service层(业务逻辑层)@Repository
:dao层(数据访问层)- 该注解作用类似于
@Mapper
,即自动创建bean 没有则会报找不到mapper接口…- 但是
@Repository
只有创建bean的功能,而@Mapper
是既创建bean并且使bean被spring扫描到。所以@Repository
要配合@MapperScannerConfigurer
使用才有效 mapperscan
和mapper
是mybatis
的注解,mapperscan
作用等同于mapper,在mapper接口很多时,一个mapperscan就可以扫描全部mapper类,不需要每一个类都写mapper。
- 但是
- 该注解作用类似于
很多时候写Component和写他的延伸注解都一样的。但是注释的作用不一样,衍生可以表示是哪个层(还有注释作用)
让指定包下的注解生效,交由IOC统一管理
注解与XML联系
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
所以推荐按照以下的方式搭配使用
- xml管理Bean
- 注解完成属性注入
- 使用过程中, 可以不用扫描,扫描是为了类上的注解
当然还是看公司要求和你自己,我偏要全用注解,写的时候爽歪歪,如果要维护就哭唧唧
小项目也可以全用注解
用java完全替代XML配置
Test类:
测试类把通过xml寻找上下文改为通过java配置类寻找上下文ApplicationContext context=``new ``AnnotationConfigApplicationContext(Appconfig.``class``)``;
//通过xml寻找上下文参数是**?.xml**
通过java配置类寻找上下文参数是**?.class**
不是.javaUser user=context.getBean(``"getuser"``,``User.``class``)``;
getBean参数为bean id,而@Bean的方法名即bean id
结果:输出sout(user.toString) -> rao
补充
- SpringBoot在没配置@ComponentScan的情况下,默认只扫描和主类处于同包下的Class。要使用其他包的类就得配置,哪里要用其他包,就在要用的类上加个@scan
- xml中的
<context:component-scan base-package="com.kuang.pojo"/>
- 在注解中为扫描包时
@ComponentScan("com.kuang.pojo")
扫描类时则为@ComponentScan(basePackages ={"com.xkcoding.task.job"} )
- 多个类用逗号分隔
- xml中的
用法:因为指定注解扫描包是一个配置,所以自然写在java配置类中
写在@config后
xml中的<import/>在注解中等于@import
如果要导入其他配置类,就在@config后写上@import("config2.class")
循环依赖(循环引用)
- 如果spring容器中2个对象相互引用,就可能会遇到循环引用的问题
- 解决方法是对其中一个bean所需的对方对象的参数添加
@Lazy
注解,该注解会给该类生成一个代理对象传入而非真实对象,因此既完成了传参构造又避免了循环依赖
- 解决方法是对其中一个bean所需的对方对象的参数添加
- 循环依赖只会发生在使用有参构造进行创建bean的时候 ```java public class Husband { private Wife wife; … }
public class Wife { private Husband husband; … } //对应的配置bean //给Wife参数前面添加@Lazy,就会给Husband的构造传个Wife的代理对象 @Bean public Husband husband([@Lazy]Wife wife) { … } @Bean public Wife wife(Husband husband) { … }
<a name="T9Usm"></a>
# @Import
- 用于多模块的时候,多模块时bean可能分散在多个子模块中。貌似其他模块的bean不能被自动识别到。这时需要手动注入获取
```java
一个独立的配置类:ConfigA.java
@ComponentScan("com.example.commandpattern.config.a")
public class ConfigA {
@Bean
public String demo() {
return "hello world";
}
}
另一个独立的配置类:ConfigB.java
@ComponentScan("com.example.commandpattern.config.b")
public class ConfigB {
@Bean
public LocalDate localDate() {
return LocalDate.now();
}
}
主配置类:MainConfig.java
// 主配置类引入各个独立配置类
@Import({ConfigA.class, ConfigB.class})
public class MainConfig {
}
引入,使用:
// 只需要将主配之类交给 Spring IoC 容器即可。
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println(context.getBean(String.class));
System.out.println(context.getBean(LocalDate.class));
System.out.println(context.getBean(StudentDao.class));
System.out.println(context.getBean(StudentService.class));
}
单例bean中注入多例bean
- 即要在单例bean中获取非单例的bean,详见链接