框架-spring-day-62

2021.12.21

、scope

spring中scope是一个非常关键的概念,简单说就是对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。

目前,scope的取值有5种取值:

在Spring 2.0之前,有singleton和prototype两种;

在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:request,session和global session类型,它们只实用于web程序,通常是和XmlWebApplicationContext共同使用

6.1、singleton

此取值时表明容器中创建时只存在一个实例,所有引用此bean都是单一实例。

也就是说创建对象是单例模式,并且如果不进行设置,默认就是单例

6.2、prototype

spring容器在进行输出prototype的bean对象 时,会每次都重新生成一个新的对象给请求方,虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求 方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一 个新的实例之后,就由这个对象“自生自灭”,最典型的体现就是spring与struts2进行整合时,要把action的scope改为 prototype。

简单来说,就是每次获取都创建一个新对象,并且这个对象的生命周期不归Spring管理

6.3、request

request,session和global session类型只适用于web程序,通常是和XmlWebApplicationContext共同使用。

<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />

Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束,如同java web中request的生命周期。当同时有100个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,简单来讲,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。

6.4、session

对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们可以使用如下形式的制定scope为session:

<bean id ="userPreferences" class="...UserPreferences" scope="session" />

Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,比request scope的bean会存活更长的时间,其他的方面没区别,如同java web中session的生命周期。

6.5、global session

<bean id ="userPreferences" class="...UserPreferences" scope="globalsession" />

global session只有应用在基于porlet的web应用程序中才有意义,它映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待

6.6、配置scope方式

<bean name="userDao" class="com.tledu.dao.impl.UserDaoImpl"> <!-- 如果不是指向对象,直接用value设置值就行 --> <property name="daoId" value="82"></property> <property name="daoStatus" value="good"></property> </bean > <!-- scope singleton : 单例 只创建一个,默认就是 prototype : 每一次getBean 都会创建一个新的实例化对象 request,session : 需要特殊环境支持 --> <bean id="userService" class="com.tledu.service.UserService" scope="singleton"> <!-- 构造方法注入 --> <constructor-arg> <ref bean="userDao" /> </constructor-arg> </bean>

7、集合属性

7.1、相关类

UserDaoImpl中提供对应的变量

private List<String> lists; private Set<String> sets; private Map<String, String> maps; public List<String> getLists() { return lists; } public void setLists(List<String> lists) { this.lists = lists; } public Set<String> getSets() { return sets; } public void setSets(Set<String> sets) { this.sets = sets; } public Map<String, String> getMaps() { return maps; } public void setMaps(Map<String, String> maps) { this.maps = maps; }

7.2、配置文件

<bean name="userDao" class="com.tledu.dao.impl.UserDaoImpl"> <property name="lists" > <list> <value>1</value> <value>2</value> </list> </property> <property name="sets" > <!-- set不可重复,重复不添加,所以只有第一个三 --> <set> <value>3</value> <value>3</value> <value>5</value> <value>4</value> </set> </property> <property name="maps"> <!-- mapkey不可重复,重复key不添加,value覆盖 --> <map> <entry key="1" value="2"></entry> <entry key="1" value="3"></entry> <entry key="2" value="2"></entry> </map> </property> </bean> <bean id="userService" class="com.tledu.zrz.service.UserService" scope="singleton"> <!-- 构造方法注入 --> <constructor-arg> <ref bean="userDao" /> </constructor-arg> </bean>

7.3 测试

8、自动装配

上面我们进行对象注入的时候有两种方式

1 set方法注入

2 构造方法注入

   

现在我们来学习自动注入,就是不需要指定以上两种方式

Autowire : 自动装配,两种取值

1 byName

2 byType  

8.1、byName

byName是根据setter方法名字进行匹配,如果找不到,就不赋值

如 setUserDao 方法  就会找userDao,如果 bean的ID为 UserDao 也一样找不到,区分大小写

设置方式

 autowire="byName">

8.2、byType

byType是根据setter方法的参数列表中的数据类型进行匹配,如果beans.xml中出现了多个相同类型的对象,就会报错

如 setUserDao(UserDao userDao) 方法  就会找UserDao,如果是接口,就找对应的实现类对象

设置方式

 autowire="byType">

注意 : 使用自动装配,需要有公共的无参构造,虽然这里就算是私有化构造方法也依然可以创建对象,但是还是提供一个公共的比较好,万一别的框架需要呢

8.2、constructor

可以根据构造函数进行依赖的注入。

9、生命周期和迟加载

之前servlet的生命周期

构造方法 -- init -- service -- destroy  

那么spring创建对象的生命周期呢?

Spring中是没有init、service和destroy的,但是我们可以指定某个方法在创建完对象之后执行,某个方法在最后销毁的时候执行.

9.1、生命周期

9.1.1、相关类

UserService中提供对应的方法

public void init(){ System.out.println("init--------"); } public void destroy(){ System.out.println("destroy----------"); } public UserService() { System.out.println("service构造方法"); }

9.1.2、配置文件

<bean name="userDao" class="com.tledu.dao.impl.UserDaoImpl" > </bean> <!-- init-method : 用于设置初始化的方法 destory-method : 用于设置销毁资源的方法 --> <bean id="userService" class="com.tledu.service.UserService" autowire="byName" init-method="init" destroy-method="destroy" > </bean>

9.1.3、测试

@Test public void testAdd() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "beans.xml"); // userService是bean的id或name UserService userService = (UserService) applicationContext .getBean("userService"); // // userService.add(null); System.out.println(userService.getUserDao()); // 销毁Spring容器 applicationContext.destroy(); }

Destroy之所以会执行,是因为我们默认创建对象的时候是单例模式

这个时候对象的生命周期会和Spring容器的生命周期绑定到一起,所以当我们销毁Spring容器的时候,会把所有的对象销毁,并自动调用destroy方法

但是如果我们把scope设置为prototype的时候,对象的生命周期就不会再和Spring容器绑定,销毁Spring容器的时候也就不会再执行该对象的destroy方法

比如

<bean name="userDao" class="com.tledu.dao.impl.UserDaoImpl" > </bean> <!-- init-method : 用于设置初始化的方法 destory-method : 用于设置销毁资源的方法 --> <bean id="userService" class="com.tledu.zrz.service.UserService" scope="prototype" autowire="byName" init-method="init" destroy-method="destroy" > </bean>

9.2、迟加载

Spring容器默认是在执行

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

进行解析的时候创建对象并调用init,

如果我们不想让某个类在解析的时候就创建对象,而是用到的时候在创建对象的话,就需要设置迟加载比如想要对userService迟加载可以这样设置

9.2.1、相关类

在UserDaoImpl和UserService中的构造方法添加输出语句进行测试

public UserDaoImpl(){ System.out.println("Dao构造方法"); } public UserService() { System.out.println("service构造方法"); }

9.2.2、配置文件

<bean name="userDao" class="com.tledu.dao.impl.UserDaoImpl" > <bean id="userService" class="com.tledu.service.UserService" scope="prototype" autowire="byName" init-method="init" destroy-method="destroy" lazy-init="true" > </bean>

9.2.3、测试

@Test public void testAdd() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "beans.xml"); }

结果

通过结果可以看出,UserService的对象没有创建,那么什么时候创建呢?

  UserService userService = (UserService) applicationContext.getBean("userService");

当调用getBean()的时候 创建

或者是使用到这个对象的时候,比如我们获取userService对象,但是需要将userDao对象注入到userService中,那么这时候就算没有通过getBean(“userDao”) 而是通过getBean(“userService”)  也会创建userDao对象,因为在UserService中用到userDao了

为了使用方便,Spring还提出了default-lazy-init="true"

比如我们通过xml创建了10个对象,这10个对象都需要迟加载,那么每个bean标签中都设置lazr-init是比较麻烦的

于是我们可以在beans标签中添加default-lazy-init="true" 对所有的bean进行迟加载  

default-lazy-init 和 lazy-init 可以同时存在,比如 10个对象中 只有一个不需要迟加载,那么可以使用 default-lazy-init = true 全部设置迟加载,然后再去指定的bean中添加 lazy-init=false 就可以

<?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" default-lazy-init="true" >

 


1、IOC注解

Spring框架中有注解和XML两种配置方式,包括Spring中的IOC和AOP也一样,都有XML和注解两种方式两种方式各有千秋。

1.1、xml和注解的区别

1.1.1 XML配置

优点有:  

  1. XML配置方式进一步降低了耦合,使得应用更加容易扩展,即使对配置文件进一步修改也

不需要工程进行修改和重新编译。  

  1. 在处理大的业务量的时候,用XML配置应该更加好一些。因为XML更加清晰的表明了各个

对象之间的关系,各个业务类之间的调用。同时spring的相关配置也能一目了然。  

缺点有:

配置文件读取和解析需要花费一定的时间,配置文件过多的时候难以管理,无法对配置的正

确性进行校验,增加了测试难度。

1.1.2  annotation配置

优点有:  

  1. 在class文件中,可以降低维护成本,annotation的配置机制很明显简单  
  2. 不需要第三方的解析工具,利用java反射技术就可以完成任务  
  3. 编译期可以验证正确性,查错变得容易
  4. 提高开发效率  

缺点有:  

  1. 如果需要对于annotation进行修改,那么要重新编译整个工程  
  2. 业务类之间的关系不如XML配置那样容易把握。  
  3. 如果在程序中annotation比较多,直接影响代码质量,对于代码的简洁度有一定的影响

Spring 的IOC 的XML方式我们已经掌握的差不多了,下面来学习学习注解的方式

1.2  注解注入使用步骤

1.2.1  Autowired

1.2.1.1  创建项目并导包

和昨天的步骤一样,不过需要引入一个新的jar包

spring-aop-4.2.1.RELEASE.jar

1.2.1.3  开启注解的支持

<?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 /> </beans>

1.2.1.2  业务类

业务类内容不变,只是要多加几个注解

public class UserService { private IUserDao userDao; @Autowired public void setUserDao(IUserDao userDao) { this.userDao = userDao; } }

1.2.1.3  知识点

@Autowired(自动封装)

该注解可以加在set方法上或者直接加在属性上,如果写在setter方法上,就会通过setter方法进行注入,如果写在变量上,就直接通过反射设置变量的值,不经过setter方法。

注入时,会从spring容器中,找到一个和这个属性数据类型匹配的实例化对象注入进来,默认使用byType,根据类型匹配。

如果只能找到一个这个数据类型的对象的时候,就直接注入该对象。

如果找到了多个同一个类型的对象的时候,就会自动更改为byName来进行匹配,根据set方法对应的参数列表的局部变量名来匹配。

如:

private IUserDao userDao; @Autowired public void setUserDao(IUserDao userDao){}; 会先找符合IUserDao类型的对象有多少,一个的话就直接拿过来 多个的话,就按照setUserDao方法的参数列表的局部变量名来找 找不到就报错 @Autowired(required=false) 就说明 这个值可以为null,如果Spring容器中没有对应的对象,不会报错 默认为true,比如beans.xml中没有创建dao对象,就会报错,加上required=false就不会报错

@Qualifier :  

以指定名字进行匹配  

private IUserDao userDao; @Autowired public void setUserDao(@Qualifier(“userDao2”)IUserDao userDao){}; 这时候就不会按照userDao来进行匹配了,而是强制使用userDao2来进行比配,也就不会按照类型匹配了

1.2.1.4  配置文件

注意增加xml解析器内容,即头部beans内容

<?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 name="userDao" class="com.tledu.dao.impl.UserDaoImpl"> <property name="daoId" value="1"></property> </bean> <bean name="userDao2" class="com.tledu.dao.impl.UserDaoImpl"> <property name="daoId" value="2"></property> </bean> <bean id="userService" class="com.tledu.service.UserService"> </bean> </beans>

1.2.1.5  测试

@Test public void testAdd() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "beans.xml"); // userService是bean的id或name UserService userService = (UserService) applicationContext .getBean("userService"); System.out.println(userService.getUserDao()); }

因为创建了两个对象,所以Dao构造方法执行两次

由于代码中定义了根据name匹配,所以最终找到的是daoId=2的对象

1.2.2  Resource

Resource这个注解是javaEE的,在javax包下,所以不需要导入其他jar包

@Resource默认使用byName的方式,按照名字匹配,可以写在setter方法上也可以写在变量上

先匹配set方法的名字,匹配不上再匹配方法参数列表的名字

如果还是匹配不上就会转换为byType,根据类型匹配

当然我们也可以指定名字

@Resource(name=”userDao”)  

就相当于 Autowired和Qualifier 一起使用

相关的还有一个 @Inject 根据type匹配,通过named指定名字,自行学习

1.2.2.1  业务类

@Resource(name="userDao") public void setUserDao(UserDao userDao) { System.out.println("--------------"); this.userDao2 = userDao; }

1.2.2.2  测试

@Test public void testAdd() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "beans.xml"); UserService userService = (UserService) applicationContext .getBean("userService"); System.out.println(userService.getUserDao()); }

1.3  注解实例化使用步骤

上面的两个注解是用来进行注入对象的,实例化对象也有对应的注解,先来个简单示例进行了解

1.3.1  配置文件

创建项目和导包和上面的一样,业务类也一致,可以复制过来进行更改

在配置文件中设置使用注解形式实例化对象 只需要加入

<?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 /> <!-- 使用注解形式实例化对象 --> <context:component-scan base-package="com.tledu" /> </beans>

1.3.2  业务类

所有需要实例化对象的类上面都加上@Component  

默认是以类名首字母小写作为名字进行存储

可以使用@Component(“xxx”) 或者@Component(value=”xxx”)来设置名字

以上这三种写法都可以

@Component(value="userDao") public class UserDaoImpl implements UserDao { @Component public class User { @Component("userService") public class UserService {

1.3.3  测试

@Test public void testAdd() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "beans.xml"); // userService是value设置的值或者是类名首字母小写 UserService userService = (UserService) applicationContext .getBean("userService"); System.out.println(userService.getUserDao()); }

1.3.4  注解分类

像上面我们写的代码中,所有实例化对象都是要的是@Component 这样不是很好,官方给出了几个分类

@Controller :WEB 层 ,就是和页面交互的类

@Service :业务层 ,主要处理逻辑

@Repository :持久层 ,就是Dao操作数据库

这三个注解是为了让标注类本身的用途清晰,Spring 在后续版本会对其增强  

@Component: 最普通的组件,可以被注入到spring容器进行管理

@Value :用于注入普通类型. 可以写在变量上和setter方法上

@Autowired :自动装配,上面描述比较详细,可以参照上面

@Qualifier:强制使用名称注入.  

@Resource 相当于: @Autowired 和@Qualifier 一起使用

@Scope: 设置对象在spring容器中的生命周期

取值 :  

singleton:单例  

prototype:多例

@PostConstruct :相当于 init-method  

@PreDestroy :相当于 destroy-method  

1.3.5  注解区别

引用spring的官方文档中的一段描述:

在Spring2.0之前的版本中,@Repository注解可以标记在任何的类上,用来表明该类是用来执行与数据库相关的操作(即dao对象),并支持自动处理数据库操作产生的异常

在Spring2.5版本中,引入了更多的Spring类注解:@Component,@Service,@Controller。@Component是一个通用的Spring容器管理的单例bean组件。而@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。

因此,当你的一个类被@Component所注解,那么就意味着同样可以用@Repository, @Service, @Controller来替代它,同时这些注解会具备有更多的功能,而且功能各异。

最后,如果你不知道要在项目的业务层采用@Service还是@Component注解。那么,@Service是一个更好的选择。

就如上文所说的,@Repository早已被支持了在你的持久层作为一个标记可以去自动处理数据库操作产生的异常(译 者注:因为原生的java操作数据库所产生的异常只定义了几种,但是产生数据库异常的原因却有很多种,这样对于数据库操作的报错排查造成了一定的影响;而 Spring拓展了原生的持久层异常,针对不同的产生原因有了更多的异常进行描述。所以,在注解了@Repository的类上如果数据库操作中抛出了异常,就能对其进行处理,转而抛出的是翻译后的spring专属数据库异常,方便我们对异常进行排查处理)。

注解 含义

@Component 最普通的组件,可以被注入到spring容器进行管理

@Repository 作用于持久层

@Service 作用于业务逻辑层

@Controller 作用于表现层(spring-mvc的注解)

1.4  新注解

1.4.1、Configuration

作用: 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要

使用AnnotationApplicationContext(有@Configuration 注解的类.class)。

属性: value:用于指定配置类的字节码

package com.tledu.config; import org.springframework.context.annotation.Configuration; @Configuration public class SpringConfiguration {   }

注意:

我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?请看下一个注解。

1.4.2、ComponentScan

作用: 用于指定 spring 在初始化容器时要扫描的包。

作用和在 spring 的 xml 配置文件中的:

是一样的。

属性: basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。

@Configuration @ComponentScan("com.tledu.spring") public class SpringConfiguration {   }

注意: 我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate 对象如何从配置文件中移除呢? 请看下一个注解。

1.4.3、Bean

作用: 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。

属性: name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。

package com.tledu.zrz.config; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.context.annotation.Bean; public class JdbcConfig { /**  * 创建一个数据源,并存入 spring 容器中  * @return  */ @Bean(name = "dataSource") public BasicDataSource createDataSource() { try { BasicDataSource bds = new BasicDataSource(); bds.setDriverClassName("com.mysql.jdbc.Driver"); bds.setUrl("jdbc:mysql://localhost:3306/ssm"); bds.setUsername("root"); bds.setPassword("root"); return bds; } catch (Exception e) { throw new RuntimeException(e); } } }

注意:

由于没有配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?

请看下一个注解。

1.4.4 PropertySource

作用: 用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。

属性: value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath

package com.tledu.config; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /**  * 创建一个数据源,并存入 spring 容器中  *  * @return  */ @Bean(name = "dataSource") public BasicDataSource createDataSource() { try { BasicDataSource bds = new BasicDataSource(); bds.setDriverClassName(driver); bds.setUrl(url); bds.setUsername(username); bds.setPassword(password); return bds; } catch (Exception e) { throw new RuntimeException(e); } } }

jdbc.properties文件 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=root

注意:

此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?

请看下一个注解。

1.4.5 Import

作用: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。

属性: value[]:用于指定其他配置类的字节码。

@Configuration @ComponentScan("com.tledu.spring") @Import({ JdbcConfig.class}) public class SpringConfiguration {     @Configuration @PropertySource("classpath:jdbc.properties") public class JdbcConfig {

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值