Spring(Spring Framework)是个开源框架,最早又Rod Johnson创建。
Spring是为了解决企业级开发而创建的
ioc控制反转(Inversion of Control)一种概念.思想.
指对 对象控制权 的转移,从 程序代码 本身反转到了 外部容器。
把对象的创建.初始化.销毁等交给了Spring容器去做.由容器控制对象的生命周期。
所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,
然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(依赖注入)来实现的。
控制反转IoC 是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,如IOC容器(ApplicationContext)或BeanFactory
(Dependency lnjection)
组件之间依赖关系由容器在运行期决定
,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
指程序运行过程中,动态的向某个对象提供它所需要的其他对象
●谁依赖于谁:应用程序依赖于IoC容器;
●为什么需要依赖:应用程序(容器管理对象)需要IoC容器来提供对象需要的外部资源;
●谁注入谁:IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
如对象A要操作数据库,之前得再A中编写Connction对象,有了Spring就只要告诉Spring,A中需要一个Connection,至于怎么构造,何时构造,A不用知到。
在系统运行时Spring会在适当的时候造个Connection,就像打针一样,注射到A中,这就完成了对各个对象之间关系的控制。
A需要依赖Connection才能正常运行,而这个Connection是由Spring注入到A中的,名字就是怎么来的
<?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">
<!-- 注册User对象-->
<bean class="com.sxt.pojo.User" name="user" id="user"/>
</beans>
所谓的声明,就是将自己的信息告诉Spring容器,例如id和class,Spring容器根据class,
通过反射(默认使用的是无参构造方法)就可以创建一个名为User的User对象。
初始化容器
通过调用容器中的getBean方法可以获取Spring容器中的对象 或 也可以通过类型直接获取一个Bean的实例,但如果出现多个相同实例 会报错
//通过id查找
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User bean = (User) ac.getBean("userId");
bean.sayId();
}
//通过Name查找
@Test
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User bean = (User) ac.getBean("userName");
bean.sayName();
}
//通过类型查找
@Test
public void test3(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据类型去Spring容器中查找对象 如果Spring容器中有两个及以上该对象的时候会报错
User bean = ac.getBean(User.class);
bean.sayClass1();
}
/**
* 通过name+类型
* 或者Id+类型
*/
@Test
public void test4(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
<!--UserService bean = ac.getBean("userName", UserService.class); -->
UserService bean = ac.getBean("userId", UserService.class);
bean.sayClass2();
}
ApplicationContext和BeanFactory的区别
ApplicationContext:在容器加载时实例化对象,常用
BeanFactory:在使用时实例化对象,且BeanFactory已被弃用
很少用
<!-- 静态工厂配置 -->
<bean class="com.sxt.service.UserFactory" factory-method="getInstance" id="user"/>
<!-- 动态工厂配置 -->
<bean class="com.sxt.service.UserFactory" id="userFactory"/>
<!-- 获取工厂创建的实例对象 -->
<bean factory-bean="userFactory" factory-method="getInstance2" id="user"/>
//创建 静/动态工厂类
public class UserFactory {
// 静态工厂 生产UserService对象
public static UserService getInstance(){
System.out.println("静态");
return new UserService();
}
// 动态工厂 生产UserService对象
public UserService getInstance2(){
System.out.println("动态");
return new UserService();
}
}
指如何给对象中的属性赋值
必须提供有参构造
三种方式:C域,属性,下标
<!-- C域简写的构造注入 Spring2.x开始提供 页面下面Namespanes里勾选C-->
<bean class="com.sxt.model.User" c:id="66" c:name="老王" c:address="阳关路">
<!-- 属性 -->
<constructor-arg name="id" value="123"/>
<constructor-arg name="name" value="张三"/>
<constructor-arg name="address" value="蓬莱东路"/>
<!-- 下标 -->
<constructor-arg index = "0" value="123"/>
<constructor-arg index = "1" value = "hello"/>
<constructor-arg index = "2" value = "深圳宝安"/>
有参构造方法不是必须的了,无参和GetSet方法是必须的!!!
Java对象中提供对应的Set方法 就是利用对象属性的set方法给属性赋值
相当于首先使用无参构造创建了个对象,然后调用对象中的Set方法给属性赋值
<!-- 设值注入比构造注入灵活,设置注入是在对象new出来后再设置的 页面下面Namespanes里勾选P-->
<bean class = "com.sxt.model.User" p:id="1234234" p:name="赵六" p:address="罗湖">
<!-- 通过 设值注入 设置属性 -->
<property name = "id" value="12345"/>
<property name = "name" value = "李四"/>
<property name = "address" value = "深圳福田"/>
<!--对象注入-->
<bean class="com.sxt.model.Cat" id="cat">
<property name = "nikeName" value = "阿猫"/>
<property name = "color" value = "白色"/>
</bean>
<bean class="com.sxt.model.User2">
<!-- 通过设值注入设值cat属性 ref:引用Spring容器中的另一个对象 -->
<property name = "cat" ref="cat"/>
</bean>
<!--对象注入 另外写法-->
<bean class="com.sxt.model.User2">
<property name = "cat">
<bean class = "com.sxt.model.Cat">
<property name="nikeName" value="阿狗"/>
<property name="color" value = "黄色"/>
</bean>
</property>
</bean>
<bean class="com.sxt.model.User3">
<!-- 数组 -->
<property name="games">
<array>
<value>仰天大笑出门去</value>
<value>我辈岂是蓬蒿人</value>
</array>
</property>
<!-- 集合 -->
<property name="cats">
<list>
<bean class="com.sxt.model.Cat">
<property name="nikeName" value="窦尔敦"/>
<property name="color" value="蓝脸"/>
</bean>
<bean class="com.sxt.model.Cat">
<property name="nikeName" value="关羽"/>
<property name="color" value="红脸"/>
</bean>
<bean class="com.sxt.model.Cat">
<property name="nikeName" value="典韦"/>
<property name="color" value="黄脸"/>
</bean>
</list>
</property>
<!-- Map -->
<property name="map">
<map>
<entry key="name" value="曹操"></entry>
<entry key="age" value="18"></entry>
<entry key="address" value="深圳"></entry>
</map>
</property>
<!--Props-->
<property name="props">
<props>
<prop key="url">127.0.0.1:8080</prop>
<prop key="userName">张飞</prop>
</props>
</property>
</bean>
//常用类型
//对象
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("Commonly_used_type.xml");
User2 user = ac.getBean(User2.class);
System.out.println(user);
}
//数组
@Test
public void test2(){
//ApplicationContext ac = new ClassPathXmlApplicationContext("Commonly_used_type.xml");
BeanFactory ac = new XmlBeanFactory(new ClassPathResource("Commonly_used_type.xml"));//
User3 user = ac.getBean(User3.class);
String[] games = user.getGames();
for(String g:games){
System.out.println(g);
}
//集合
System.out.println(user.getCats());
//Map
Map<String,Object> map = user.getMap();
Set<String> keys = map.keySet();
for(String key:keys){
System.out.println(key+":"+map.get(key));
}
//Properties类型
Properties props = user.getProps();
String url = props.getProperty("url");
String userName = props.getProperty("userName");
System.out.println(url+":"+userName);
}
Java配置本质上,是用Java类来代替XML配置,这种方式在SpringBoot广泛使用
@Configuration
:添加该注解 表示 该类和applicationContext.xml
一样
@Bean注解
:和applicationContext.xml
的 bean标签
作用一样,默认的name是方法名称
/**
* 实现Spring中的java配置
*@Configuration 添加该注解 表示 该类和applicationContext.xml一样
*/
@Configuration
public class AppConfig {
/**
* 加了@Bean注解 的方法和
* applicationContext.xml 的 bean标签 作用一样
* 默认的name是方法名称
*/
@Bean ("user")
public User getUser(){
return new User();
}
}
测试调用
@Test
public void test(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
User user = (User) ac.getBean("user");
user.say();
}
也叫自动注入,前面这几种配置方式,对于要使用的类都需要一个一个的配置。
可以通过自动配置来简化Bean的配置。
自动配置需要使用到如下注解:
@Component
用在身份不明的组件上@Controller
用在Controller层(控制层)@Service
用在Service层(业务层)@Repossitroy
用在Dao层(数据访问层)<!-- 开启扫描 配置扫描路径
context:component-scan: 在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,
如果扫描到有@Component @Controller @Service @Repository等这些注解的类,则把这些类注册为bean
这个标签可以写多个,还可以使用 *等
base-package: 告诉Spring要扫描的路径,扫描的路径如果有多个,可以通过 ","连接 -->
<context:component-scan base-package="com.sxt.controller,com.sxt.dao.impl,com.sxt.service.impl"></context:component-scan>
@Configuration
//添加扫描
//@ComponentScan("com.sxt.*")
@ComponentScans(value={
@ComponentScan("com.sxt.controller"),
@ComponentScan("com.sxt.serivce.impl"),
@ComponentScan("com.sxt.dao.impl")
})
public class JavaConfig {
}
@Resource和@Autowired的区别
@Resource
:默认通过name注入对象,JDK提供
@Autowired
:只能通过类型注入对象,Spring提供,需要根据name注入对象需要和 @Qualifier
一块使用
use-default-filters
表示使用使用spring默认提供的过滤器,
false表示不使用,true则表示使用。
一般来说,
true
结合exclude-filter
标签使用,表示除去某个注解
false
结合include
标签使用,表示包含某个注解
<!-- 开启扫描 配置扫描路径
context:component-scan: 在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,
如果扫描到有@Component@Controller@Service@Repository等这些注解的类,则把这些类注册为bean
这个标签可以写多个,还可以使用 " * " 等
base-package: 告诉Spring要扫描的路径,扫描的路径如果有多个,可以通过 ","连接
-->
<context:component-scan base-package="com.sxt.controller,com.sxt.dao.impl,com.sxt.service.impl"></context:component-scan>
<!-- <context:component-scan base-package="com.sxt.controller"></context:component-scan>
<context:component-scan base-package="com.sxt.dao.impl"></context:component-scan>
<context:component-scan base-package="com.sxt.service.impl"></context:component-scan> -->
<!-- <context:component-scan base-package="com.sxt.*.dao"></context:component-scan> -->
<!-- use-default-filters
表示使用spring默认提供的过滤器,会扫描指定包下的全部的标有注解的类,并注册成bean。
false表示不使用,true则表示使用。一般来说,
true结合<context:include-filter>标签使用,表示除去某个注解
false结合<context:exclude-filter> 标签使用,表示包含某个注解 -->
<!-- 不使用默认的过滤器连 只适用org.springframework.stereotype.Controller -->
<context:component-scan base-package="com.sxt.controller" use-default-filters="false">
<!-- 包含 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:component-scan base-package="com.sxt.service.impl,com.sxt.dao.impl" use-default-filters="true">
<!-- 排除 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在实际开发中,项目即将上线时,可能需要不停的在开发环境、生产环境、测试环境…之间进行切换
//实现Spring中的Java配置方式
public class AppConfig {
//@Profile注解相当于一个标记,标记当前dataSource是什么环境下的dataSource(数据源)
//设置开发环境配置
@Bean
@Profile("dev")
public MyDataSource getDevDataSource(){
return new MyDataSource("127.0.1.1","sql", "root", "root");
}
//设置生产环境配置
@Bean
@Profile("pro")
public MyDataSource getProDataSource(){
return new MyDataSource("192.168.1.1","sql", "admin", "admin");
}
//设置生产环境配置
@Bean
@Profile("test")
public MyDataSource getTestDataSource(){
return new MyDataSource("192.1.1.1","sql", "pwd", "pwd");
}
}
<!-- XML配置实现 -->
<!-- profile 配置必须放在整个配置文件的 底部 -->
<beans profile="dev">
<bean class="com.sxt.service.MyDataSource" p:url="http" p:driverName="dev" p:userName="dev" p:password="dev">
<!-- <property name="url" value="http"/>
<property name="driverName" value="dev"/>
<property name="userName" value="dev"/>
<property name="password" value="dev"/> -->
</bean>
</beans>
<beans profile="pro">
<bean class="com.sxt.service.MyDataSource" p:url="http" p:driverName="pro" p:userName="pro" p:password="pro">
<!-- <property name="url" value="http"/>
<property name="driverName" value="pro"/>
<property name="userName" value="pro"/>
<property name="password" value="pro"/> -->
</bean>
</beans>
//测试
@Test
public void test(){
//1、因为配置类,需重新指定模式(开发或是生产等),所以在新建annotation时,不用加载配置类
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
//2、使用Environment来指定当前运行的模式,指定的值必须和@Profile注解中自定义的值匹配。否则都不执行。
//getEnvironment():获取一个环境变量对象
//setActiveProfiles():设置一个活动的配置文件
ac.getEnvironment().setActiveProfiles("dev");//
//3、指定模式后,再注册Bean
ac.register(AppConfig.class);
ac.refresh();
MyDataSource ds = ac.getBean(MyDataSource.class);
System.out.println(ds);
}
@Test
public void test2(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ac.getEnvironment().setActiveProfiles("dev");
ac.refresh();
MyDataSource ds = ac.getBean(MyDataSource.class);
System.out.println(ds);
}
Profile
实际上就是条件注解的一种特殊形式,即条件注解更加灵活,用户可以根据各种不同的条件使用不同的Bean。
条件注解在SpringBoot中使用非常广泛。SpringBoot中提供了许多自动化的配置,例如数据库配置,SpringBoot使用条件注解提前配置好许多常用的类,使用条件注解,在某一个条件满足时,这些配置就会生效。
//创建实例
public class LinuxShowCmd implements ShowCmd{
@Override
public String show() {
return "Linux";
}
}
public class WindowsShowCmd implements ShowCmd{
@Override
public String show() {
return "window";
}
}
//定义条件
public class LinuxCondition implements Condition{
/*
* 匹配环境
* true表示匹配上了
* false表示没有匹配上
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
String[] prof = context.getEnvironment().getActiveProfiles();
for(String p:prof){
if(p.contains("Linux")){
return true;
}
}
return false;
}
}
public class WindowsShowCondition implements Condition{
/*
* 匹配环境
* true表示匹配上了
* false表示没有匹配上
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
String[] prof = context.getEnvironment().getActiveProfiles();
for(String p:prof){
if(p.contains("Window")){
return true;
}
}
return false;
}
}
//Java配置文件
public class AppConfig2 {
/**
* 条件注解
* @Conditional(value=LinuxCondition.class)
* 中的LinuxCondition 的matchs 方法如果返回true 会调用该方法
* 返回false就不会生效
* @return
*/
@Bean
@Conditional(value=LinuxCondition.class)
public LinuxShowCmd getLinuxShowCmd(){
return new LinuxShowCmd();
}
@Bean
@Conditional(value=WindowsShowCondition.class)
public WindowsShowCmd getWindowsShowCmd(){
return new WindowsShowCmd();
}
}
//测试调用
@Test
public void test(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.getEnvironment().setActiveProfiles("Windows");
ac.register(AppConfig2.class);
ac.refresh();
ShowCmd sc = ac.getBean(ShowCmd.class);
System.out.println(sc.show());
}
property
:每次请求都会获取一个新的bean(Java原型模式)singleton
:单例模式,默认设置request
:一次请求中,bean与request生命周期同步session
:一次会话中,session生命周期同步<!-- XML配置中 -->
<!-- 注册UserService
scope: 对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。
property:每次请求都会获取一个新的bean(Java原型模式)
singleton:单例模式,默认设置
request:一次请求中,bean与request生命周期同步
session:一次会话中,session生命周期同步
-->
<bean class="com.sxt.service.UserService" id="userService" scope="singleton"/>
//Java中配置
@Configuration
@Scope(value="prototype")
public class JavaConfig {
}
项目开发中,既有配置文件配置,又有Java配置存在,这时可以用@ImportResource 来实现
@ImportResource
:可以导入一个配置文件
//Java中配置
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig {
}
其他注解
@Scope(value="prototype")
// 单例和多例 修饰在类上
@Lazy
//代表懒加载 修饰在类上
@PostConstruct
//表示初始化操作 修饰在方法上
@PreDestroy
//表示销毁 修饰在方法上