Spring框架——关于IOC容器和注解的36个小实验
实验1:通过IOC容器创建对象,并为属性赋值★
<bean id="page" class="com.neuedu.Bean.Page"> <property name="fontSize" value="大写"></property> <property name="fontNum" value="12"></property> </bean> ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml"); Page bean2 = (Page)iocContainer.getBean("page");
实验2:根据bean的类型从IOC容器中获取bean的实例★
<bean id="page" class="com.neuedu.Bean.Page"> <property name="fontSize" value="大写"></property> <property name="fontNum" value="12"></property> </bean> ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml"); Page bean2 = iocContainer.getBean(Page.class);
实验3:通过构造器为bean的属性赋值
<bean id="Book" class="com.neuedu.Bean.Book"> <!-- 以下为给带参构造器传送参数 --> <constructor-arg value="123" ></constructor-arg> <constructor-arg value="456" ></constructor-arg> <constructor-arg value="789" ></constructor-arg> </bean>
实验4:通过index属性指定参数的位置
<bean id="Book" class="com.neuedu.Bean.Book"> <property name="num" value="123"></property> <constructor-arg value="123" index="0"></constructor-arg> <constructor-arg value="123" index="1"></constructor-arg> <constructor-arg value="123" index="2"></constructor-arg> </bean>
实验5:通过类型不同区分重载的构造器
<bean id="page" class="com.neuedu.Bean.Page"> <constructor-arg value="123" type="int"></constructor-arg> <constructor-arg value="456" type="String"></constructor-arg> </bean>
实验6:通过p名称空间为bean赋值
<bean id="Book2" parent="Book" p:num="254" />
实验14:给bean的级联属性赋值
<bean id="page" class="com.neuedu.Bean.Page"> <constructor-arg value="123" type="int"></constructor-arg> <constructor-arg value="456" type="String"></constructor-arg> </bean> <bean id="Book" class="com.neuedu.Bean.Book"> <property name="page" ref="page"></property> </bean>
实验21:测试bean的作用域,分别创建单实例和多实例的bean★
<bean id="page" scope="prototype" class="com.neuedu.Bean.Page"> <constructor-arg value="123" type="int"></constructor-arg> <constructor-arg value="456" type="String"></constructor-arg> </bean>
Scope属性:
Prototype:每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()的操作
Request:每次Http请求都会创建一个新的Bean, 仅适用于WebApplication环境
Session:每次创建一个session,创建对象,同一个Http Session共享一个Bean,不同的HttpSession使用不同的Bean。仅适用于WebApplication环境
Singleton:在Spring容器中仅存在一个Bean实例,Bean以单例的方式存在
实验22:创建带有生命周期方法的bean
<bean id="page" scope="singleton" class="com.neuedu.Bean.Page" init-method="int" destroy-method="destroy"> <constructor-arg value="123" type="int"></constructor-arg> <constructor-arg value="456" type="String"></constructor-arg> </bean>
实验20:bean之间的依赖 depends-on="order"被依赖的对象会先创建
<bean id="page" scope="singleton" class="com.neuedu.Bean.Page" depends-on="font"> <constructor-arg value="123" type="int"></constructor-arg> <constructor-arg value="456" type="String"></constructor-arg> </bean> <bean id="font" class="com.neuedu.Bean.Font"></bean>
实验18:通过继承实现bean配置信息的重用
<bean id="Book" class="com.neuedu.Bean.Book" p:name="四大名著" p:num="258" /> <bean id="Book2" parent="Book" p:num="254" />
实验19:通过abstract属性创建一个模板bean
<bean id="Book" abstract="true" class="com.neuedu.Bean.Book" p:name="四大名著" p:num="258" /> <bean id="Book2" parent="Book" p:num="254" />
实验7:测试使用null值
<bean id="page" scope="singleton" class="com.neuedu.Bean.Page" > <property name="fontSize"> <null/> </property> </bean>
null只有包装类和引用类型可以用
实验8:引用其他bean★
<bean id="Book" class="com.neuedu.Bean.Book"> <property name="page" ref="page"></property> </bean>
实验9:引用内部bean
<bean id="Book" class="com.neuedu.Bean.Book"> <property name="page" > <bean id="page" scope="singleton" class="com.neuedu.Bean.Page" > <property name="fontSize" value="123"></property> </bean> </property> </bean>
实验10:使用List类型的集合属性
<bean id="pets" class="com.xgj.ioc.inject.construct.utilSchema.Pets"> <property name="petList" ref="petList" /> </bean> <util:list id="petList" list-class="java.util.ArrayList" value-type="java.lang.String"> <value>DOG</value> <value>CAT</value> <value>BIRD</value> </util:list>
实验11:使用Map类型的集合属性
<!-- 第一种写法 ,通过ref引用,此时需要在 uitl-map中声明id 推荐这种写法 <bean id="pets" class="com.xgj.ioc.inject.construct.utilSchema.Pets"> <property name="petMap" ref="petMap" /> </bean> <util:map id="petMap" map-class="java.util.HashMap"> <entry key="101" value="dog" /> <entry key="103" value="wolf" /> <entry key="105" value="bird" /> </util:map> --> <!-- 第二种写法,嵌入内部 --> <bean id="pets" class="com.xgj.ioc.inject.construct.utilSchema.Pets"> <property name="petMap"> <util:map map-class="java.util.HashMap"> <!-- 可以通过map-class显示指定Map的实现类 --> <entry key="101" value="dog" /> <entry key="103" value="wolf" /> <entry key="105" value="bird" /> </util:map> </property> </bean>
实验12:使用prop子元素为Properties类型的属性赋值
<bean id="pets" class="com.xgj.ioc.inject.construct.utilSchema.Pets"> <property name="petProperties" ref="petProperties" /> </bean> <util:properties id="petProperties"> <prop key="151">PIG</prop> <prop key="153">PINGUIN</prop> </util:properties>
实验15:配置通过静态工厂方法创建的bean[通过静态方法提供实例对象,工厂类本身不需要实例化!]
<bean id="staticFactory" class="com.neuedu.spring.bean.StaticFactory" factory-method="getBook"> <constructor-arg value="book01"></constructor-arg> </bean>
实验16:配置通过实例工厂方法创建的bean[通过实例方法提供实例对象,工厂类本身需要先创建对象!]
<bean id="instanceFactory" class="com.neuedu.spring.bean.InstanceFactory"></bean> <bean id="bookFromInstanceFactory" factory-bean="instanceFactory" factory-method="getBook"> <constructor-arg value="book02"></constructor-arg> </bean>
实验17:配置FactoryBean★
public class MyFactoryBean implements FactoryBean<Book> { @Override public Book getObject() throws Exception { return new Book(22, "无字天书", "好啊", 22.5); } @Override public Class<?> getObjectType() { return Book.class; } @Override public boolean isSingleton() { return false; } }
实验22[补充]:测试bean的后置处理器
①在bean的初始化方法调用前后执行操作的专门的对象
②自定义后置处理器实现该接口:org.springframework.beans.factory.config.BeanPostProcessor
③在springmvc中配置一下该bean对象.
<bean class="com.neuedu.spring.bean.Book" init-method="init"></bean> <bean id="myBeanPostProcessor" class="com.neuedu.spring.bean.MyBeanPostProcessor"></bean>
数据库连接池:
6) 数据库连接池
> 数据库连接池就是存放数据库连接(Connection)的集合
> 我们获取一个数据库连接是一个相对很麻烦的过程,
如果我们获取一个数据库连接,使用一次以后就给它关闭了
下一次再去使用的时候就要重新创建一个新的数据库连接。
> 所以我们提出了一个数据库连接池的概念,数据库连接池放的都是数据库连接(Connection)
我们在去使用数据库连接时候,不用再去重新创建数据库连接,而是直接从池中获取,
使用完的数据库连接,也不是直接销毁,而是要放回到连接池。
>
数据库连接池的常见的属性:
初始连接数量:数据连接池创建以后,保存数据库连接的数量[50]
最小空闲连接数:数据库连接池最少得未使用的数据库连接的数量[10]
最大空闲连接数:数据库连接池最大闲置连接数,当闲置连接数满了以后,将不会有其他连接进入池
每次增加连接数:当数据库连接都被占用以后,一次性增加的数据库连接的个数[20]
最大连接数:数据库连接池的最大容量,当最大连接数饱和了,则不再创建新的数据库连接[100]
最大等待时间:当数据库连接池饱和以后,等待获取数据库连接的时间
> 常见的数据库连接池
- 所有的数据库连接池都需要实现DataSource,当使用数据库连接池是,我们便不再需要使用DriverManger获取数据库连接
而是使用DataSource。
- Connection getConnection()
- 从数据库连接池中获取数据库连接对象
1.DBCP
- DBCP是Apache出品的一款数据库连接
- DBCP依赖于commons-pool
- 使用DBCP需要导入两个jar包:
commons-dbcp-1.4.jar
commons-pool-1.5.5.jar
- 当我们通过数据库连接池获取数据库连接以后,我们所获取到数据库连接已经不是我们熟悉的那个Connection
数据库连接池对Connection对象进行了包装,它修改Connection的close()方法,
再去调用close()数据库连接将不会真的关闭,而是要放回到数据库连接池中,供其他线程使用。
- 核心类:
BasicDataSourceFactory
2.C3P0(重点)
- C3P0使用的是XML作为配置文件
- 使用c3p0需要导入一个jar包:
c3p0-0.9.1.2.jar
- 导入c3p0的配置文件:
1.配置文件的名字:c3p0-cofig.xml
2.配置文件要求放到类路径下(src)
- 核心类:
ComboPooledDataSource
- 注意:
DataSource就相当于池子,我们的数据库连接都是从DataSource中获取的,
如果程序中有多个DataSource的实例,那么我们说你还不如不用数据库连接池。
所以我们的DataSource在项目中应该只有一个实例。
实验23:引用外部属性文件★
jdbc.properties文件:
jdbc.user=root
jdbc.passowrd=123456
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.driver=com.mysql.jdbc.Driver
<context:property-placeholder location="classpath:jdbc.properties"/>
1.在目标属性上加@Value注解
@Value("${jdbc.user}")
private String username;
2.
<!-- 根据外部属性文件中的信息配置数据源 --> <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.passowrd}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driver}"></property> </bean> ComboPooledDataSource bean = ioc.getBean(ComboPooledDataSource.class); Connection connection = bean.getConnection(); System.out.println(connection); Statement st = connection.createStatement(); ResultSet rs = st.executeQuery("select * from stu"); while(rs.next()){ String string = rs.getString("name"); String string2 = rs.getString("school"); System.out.println(string+"==="+string2); }
实验24:基于XML的属性装配
①手动装配
<!-- 属性的装配:手动装配 --> <bean id="userService" class="com.neuedu.spring.bean.UserService"></bean> <bean id="userAction" class="com.neuedu.spring.bean.UserAction"> <property name="userService" ref="userService"></property> </bean>
②自动装配
<!-- 1.按类型装配:byType --> <!-- 首先检测当前bean中需要装配的属性的类型 --> <!-- 然后在IOC容器中查找匹配这个类型的bean --> <!-- 如果类型匹配的bean是唯一的,那么就将这个匹配的bean注入到userAction中 --> <bean id="userService" class="com.neuedu.spring.bean.UserService"></bean> <bean id="userAction" autowire="byType" class="com.neuedu.spring.bean.UserAction"></bean> <!-- 2.按bean的id值装配:byName --> <!-- 首先检测当前bean中需要装配的属性的属性名,属性名是将setXxx()方法去掉set,首字母小写得到的 --> <!-- 然后根据属性名作为id的值,在IOC容器中查找对应的bean --> <!-- 如果能够找到,则将找到bean注入进去 -->
6。SpEL简介【见WORLD文档---了解】
Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图。
和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
6.1 基本语法
SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。
6.2 使用字面量
●整数:<property name="count" value="#{5}"/>
●小数:<property name="frequency" value="#{89.7}"/>
●科学计数法:<property name="capacity" value="#{1e4}"/>
●String类型的字面量可以使用单引号或者双引号作为字符串的定界符号
<property name=“name” value="#{'Chuck'}"/>
<property name='name' value='#{"Chuck"}'/>
●Boolean:<property name="enabled" value="#{false}"/>
实验25:[SpEL测试I]在SpEL中使用字面量
实验26:[SpEL测试II]在SpEL中引用其他bean
实验27:[SpEL测试III]在SpEL中引用其他bean的某个属性值
实验28:[SpEL测试IV]在SpEL中调用非静态方法
实验29:[SpEL测试V]在SpEL中调用静态方法
实验30:[SpEL测试VI]在SpEL中使用运算符
8.使用注解配置bean
①声明bean的注解
@Component 将当前类声明为IOC容器中的一个普通的组件
@Controller 将当前类声明为IOC容器中的一个控制器组件
@Service 将当前类声明为IOC容器中的业务逻辑层组件
@Repository 将当前类声明为IOC容器中的一个持久化层组件
@ControllerAdvice
Spring根据上述注解其实并不能分辨当前类是否真的是一个控制器或Dao,即使标记的类和注解不对应也没有语法错误。
但在实际工作中,肯定要将专门的注解标记在对应的类上面。
②使用基于注解的bean的配置,需要额外导入一个jar包:spring-aop-4.0.0.RELEASE.jar
③需要设置自动扫描的包
< context:component-scan base-package ="com.neuedu.ioc.bean"/>
④使用注解后,默认按照类名首字母小写作为id的值,也可以使用value属性指定id,value属性名也可以省略注解
注解 | id值 |
@Component public class CommonComponent { } |
commonComponent |
@Controller(value="neueduBookAction" ) public class BookAction {
} |
neueduBookAction |
@Service("happyService" ) public class BookService {
} |
happyService |
实验31:通过注解分别创建Dao、Service、Controller★
实验32:使用context:include-filter指定扫描包时要包含的类
实验33:使用context:exclude-filter指定扫描包时不包含的类
< context:component-scan base-package ="com.neuedu.ioc.bean"/>
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
[2]当需要扫描多个包时可以使用逗号分隔,
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
<context:component-scan base-package="com.neuedu.component" resource-pattern="autowire/*.class"/>
[4]包含与排除
●<context:include-filter>子节点表示要包含的目标类
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。
即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则
指定的组件了。
●<context:exclude-filter>子节点表示要排除在外的目标类
●component-scan下可以拥有若干个include-filtejr和exclude-filter子节
⑤使用注解进行自动装配:@Autowired注解[好处就是:连get、set方法都不用写!]
[1]首先检测标记了@Autowired注解的属性的类型
[2]根据类型进行装配
[3]如果指定类型的bean不止一个,那么根据需要被装配的属性的属性名做id的值,查找bean
[4]如果根据id值还是没有找到bean,可以使用@Qualifier注解手动指定要装配的bean的id.
实验34:使用@Autowired注解实现根据类型实现自动装配★
实验34[补充1]:如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配★
实验34[补充2]:如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id★
实验36:Autowired注解的required属性指定某个属性允许不被设置.
实验37:在类上使用注解@Scope可以指定对象是单实例还是多实例的!