Spring学习笔记
author:木子六日
文章目录
DAY1
spring能帮我们创建对象,对象交由spring管理,不用程序员创建,实现了程序员与对象管理之间的解耦(不用new对象了)。
导包
主要的包有五个:beans、context、core、expression再加上一个commons-logging;
配置文件
在src下创建applicationContext.xml配置文件(位置和文件名都无所谓的),导入xmlns(照抄就行了);
<?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-2.5.xsd">
</beans>
三种创建对象的方法
1.通过构造函数创建;
2.通过实例工厂创建;
3.通过静态工厂创建;
这里举一个具体的例子,比如有如下的People类和PeopleFactory类
package com.pojo;
/**
* People类
*/
public class People {
private String name;
private int age;
private String sex;
public People() {
super();
}
public People(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "People [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
package com.pojo;
/**
* PeopleFactory类
*/
public class PeopleFactory {
public People newInstance() {
return new People("实例工厂创建",0,null);
}
public static People newInstance2() {
return new People("静态工厂创建",0,null);
}
}
如果是以前的话,按以上所写有三种办法创建一个People对象:
1.通过构造方法:
People peo = new People();
2.通过实例工厂:
PeopleFactory factory = new PeopleFactory();
People peo = factory.newInstance();
3.通过静态工厂:
People peo = PeopleFactory.newInstance2();
这三种方法在spring中的使用
工厂模式还有其他的一些模式,spring仅对实例工厂和静态工厂提供支持。
首先我们要配置之前的applicationContext.xml文件,具体配置如下,注释里有解释:
<?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-2.5.xsd">
<!--
通过构造方法创建对象
id表示获取到对象标识
class表示创建哪个类的对象
-->
<bean id="peo" class="com.pojo.People">
<!-- 这里面不写东西的话,默认走类的无参构造 -->
<!-- 如果要走有参构造,如下 -->
<constructor-arg index="0" value="木子六日"></constructor-arg>
<constructor-arg index="1" value="24"></constructor-arg>
<constructor-arg index="2" value="男"></constructor-arg>
<!--
index表示参数位置,
name表示参数名称,
type表示参数类型,
只要这三个能唯一标识出使用那个你想要的构造方法即可,三个属性不必都出现。
value表示值,仅限于基本数据类型和字符串作为参数能使用,
如果参数是一个对象,则需使用ref引用其他bean
-->
</bean>
<!--
实例工厂模式创建对象:
创建一个工厂的bean
创建实例的bean(不需要再写class属性了,由工厂创建),
factory-bean属性写工厂bean的id,factory-method属性写创建这个对象的方法名
-->
<bean id="peoFac" class="com.pojo.PeopleFactory"></bean>
<bean id="peo1" factory-bean="peoFac" factory-method="newInstance"></bean>
<!--
静态工厂模式创建对象:
不需要创建工厂bean,直接创建对象的bean
class写工厂的全限定路径,factory-method写创建这个对象的静态方法
-->
<bean id="peo2" class="com.pojo.PeopleFactory" factory-method="newInstance2"></bean>
</beans>
写一个测试类测一下:
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.People;
public class Test {
public static void main(String[] args) {
//这一步是加载配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//第一个参数指明了xml中bean的id,标识对象
//第二个参数指明类(可以不写第二个参数,这样的话返回值是Object,再强转效果一样)
//通过构造方法创建的对象
People peo = ac.getBean("peo",People.class);
System.out.println(peo);
//通过实例工厂创建的对象
People peo1 = ac.getBean("peo1",People.class);
System.out.println(peo1);
//通过静态工厂创建的对象
People peo2 = ac.getBean("peo2",People.class);
System.out.println(peo2);
}
}
DAY2
spring的对象属性注入
之前说了可以通过构造方法,也可以通过set方法来做,具体如下
<!-- 如果不使用构造方法,也可以使用set方法对对象赋值 -->
<bean id="peo3" class="com.pojo.People">
<property name="name" value="哈哈"></property>
<property name="age" value="12"></property>
<!-- 也可以这样写 -->
<property name="sex">
<value>女</value>
</property>
<!-- 如果某一属性是Set -->
<property name="sets">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</set>
</property>
<!--
如果属性是List,跟Set一样,只是把set标签换成list标签
如果list中只有一个值,可以直接使用value属性进行赋值
如果属性是数组,就把set标签换成array标签即可,一个值的情况
也可以使用类似于list的简写形式
-->
<!-- 如果属性是map类型,写法如下 -->
<property name="map">
<map>
<entry key="a" value="b"></entry>
<entry key="c" value="d"></entry>
<entry key="e" value="f"></entry>
</map>
</property>
<!-- 如果属性是另一个对象,就要用到DI(依赖注入) -->
<property name="bag" ref="bag"></property>
</bean>
<!-- DI需要先在外面写好一个对象 -->
<bean id="bag" class="com.pojo.Bag">
<property name="id" value="2"></property>
<property name="price" value="40.6"></property>
</bean>
spring整合Mybatis(十分重要)
首先肯定要导包,除了spring的包和mybatis的包外,还需要数据库的驱动包和spring-mybatis.jar.
我们可以把mybatis.xml那个文件省掉了,舒服,在applicationContext.xml进行如下配置
<?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-2.5.xsd">
<!-- Spring整合mybatis -->
<!-- 整合dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="lijingjing"></property>
</bean>
<!-- 整合生产SQLSession的工厂 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 获取dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 整合mybatis.xml中的mappers(就是加载mapper那个作用) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mapper"></property>
<property name="sqlSessionFactory" ref="factory"></property>
</bean>
<!-- 创建service层的实例 这一步跟mybatis无关 -->
<bean id="flowerService" class="com.service.impl.FlowerServiceImpl">
<property name="flowerMapper" ref="flowerMapper"></property>
</bean>
</beans>
当然之前的mapper包和pojo包是不可少的,这些跟mybatis的时候写法一样。
测试代码如下:
package com.test;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.Flower;
import com.service.FlowerService;
import com.service.impl.FlowerServiceImpl;
public class Test {
public static void main(String[] args) {
//加载配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// for(String str:ac.getBeanDefinitionNames()) {
// System.out.println(str);
// }
FlowerService fs = ac.getBean("flowerService",FlowerServiceImpl.class);
List<Flower> list = fs.showAll();
for(Flower f:list) {
System.out.println(f);
}
}
}
配置文件加载完成后,spring扫描了com.mapper包,之后就已经生成了flowerMapper的实现类(注意mapper.xml必须绑定接口),所以在service层就可以直接拿到flowerMapper的实现类。
service实例的代码如下:
package com.service.impl;
import java.util.List;
import com.mapper.FlowerMapper;
import com.pojo.Flower;
import com.service.FlowerService;
/**
* 注意此类的实例化也要放到applicationContext中
* @author 木子六日
*
*/
public class FlowerServiceImpl implements FlowerService{
//这个在扫描的时候被创建好了,记得要生成get和set
private FlowerMapper flowerMapper;
public FlowerMapper getFlowerMapper() {
return flowerMapper;
}
public void setFlowerMapper(FlowerMapper flowerMapper) {
this.flowerMapper = flowerMapper;
}
@Override
public List<Flower> showAll() {
return flowerMapper.selAll();
}
}
serviceImpl拿到flowerMapper的过程是在applicationContext中完成的,通过DI获取到,
即xml中的这一句<property name="flowerMapper" ref="flowerMapper"></property>
。
还是把mapper.xml和绑定的接口放一下吧,如下(跟纯mybatis的时候一样的):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.FlowerMapper">
<select id="selAll" resultType="com.pojo.Flower">
select * from flower
</select>
</mapper>
package com.mapper;
import java.util.List;
import com.pojo.Flower;
public interface FlowerMapper {
public List<Flower> selAll();
}
DAY3
aop面向切面编程是spring的一个重要概念,主要目的就是给方法添加一些通知。
使用aop之前要记得导包,除了spring的aop包之外还有aopalliance.jar和aspectjweaver.jar.
schema based实现前置、后置通知
applicationContext.xml中要有写如下配置:
<!-- 注入前置通知对象 -->
<bean id="mybefore" class="com.advice.MyBeforeAdvice"></bean>
<!-- 注入后置通知对象 -->
<bean id="myafter" class="com.advice.MyAfterAdvice"></bean>
<!-- 注入切点对象 -->
<bean id="point" class="com.test.Test"></bean>
<aop:config>
<!-- 配置切点 -->
<!--
*表示可以表示配置任意方法
比如com.test.Test.*就是代表所有方法,
()表示无参,若想匹配任意类型参数,(..)
-->
<aop:pointcut expression="execution(* com.test.Test.printTest(..))" id="mypoint"/>
<!-- 添加通知:schema based方式 -->
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
前置通知对象如下:
package com.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 前置通知类
* @author 木子六日
*
*/
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("我是前置通知");
//arg0切点方法对象
System.out.println("切点方法对象:"+arg0+" 切点方法名:"+arg0.getName());
//arg1切点方法参数
if(arg1!=null&&arg1.length>0) {
System.out.println("切点方法有参数");
}else {
System.out.println("切点方法没有参数");
}
//arg2切点对象
System.out.println("切点对象为:"+arg2);
}
}
后置通知对象如下:
package com.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("我是后置通知");
//后三个参数与前置通知一样,arg0代表返回值
System.out.println("返回值是:"+arg0);
}
}
schema based实现环绕通知
与前置后置通知类似
<!-- 注入环绕通知对象 -->
<bean id="myaround" class="com.advice.MyAround"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.test.Test.printTest(..))" id="mypoint"/>
<aop:advisor advice-ref="myaround" pointcut-ref="mypoint"/>
</aop:config>
环绕通知对象如下:
package com.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 环绕通知
* @author 木子六日
*
*/
public class MyAround implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("我是环绕前置通知");
Object res = arg0.proceed();//放行切点方法
System.out.println("我是环绕后置通知");
return res;
}
}
Aspectj实现异常通知
xml配置如下:
<!-- 注入aspectj通知对象 -->
<bean id="mythrow" class="com.advice.MyThrowAdvice"></bean>
<aop:config>
<!-- 添加通知:aspectj方式(此处以异常处理为例) -->
<!-- 通过ref注入通知对象 -->
<aop:aspect ref="mythrow">
<aop:pointcut expression="execution(* com.test.Test.printTest2(..))" id="mypoint2"/>
<!-- method方法名 pointcut-ref切点id throwing方法形参名 -->
<aop:after-throwing method="myThrow" pointcut-ref="mypoint2" throwing="e"/>
</aop:aspect>
</aop:config>
aspectj的异常通知对象(自定义,无需实现接口)如下:
package com.advice;
/**
* aspectj的通知类不需要继承
* @author 木子六日
*
*/
public class MyThrowAdvice {
public void myThrow(Exception e) {
System.out.println("我是抛出的异常,异常信息为:"+e.getMessage());
}
}
DAY 4
scope属性
可设置spring容器中的对象是否为单例,在什么范围内单例,使用如下:
<!-- scope属性 -->
<!--
取出spring容器中的bean时,无论取几次,都是同一个对象
因为默认是单例的。
如果我们想每取一次都是一个新的对象
可以使用scope属性,设置为prototype,默认是singleton。
scope还能取值为request,表示一次请求内单例
session取值表示一次会话内单例
-->
<bean id="eye" class="com.pojo.Eye" scope="prototype">
<property name="color" value="brown"></property>
</bean>
自动注入
此功能可以免去一些需要依赖注入的对象的注入步骤,具体使用如下:
<!-- 自动注入:autowire -->
<!-- 这是以前的标准写法,使用ref注入一个对象 -->
<bean id="peo" class="com.pojo.People">
<property name="eye" ref="eye"></property>
</bean>
<!--使用自动注入可以这样写,通过id与该对象属性的匹配,直接注入,不再写property标签-->
<!-- 当然也可以使用byType进行注入,是通过匹配类型 -->
<bean id="peo2" class="com.pojo.People" autowire="byName">
</bean>
<!-- 我们还可以直接在beans里面配置全局默认的autowire -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byName"
>
spring读取properties配置文件
方法如下:
<!-- spring读取属性文件 -->
<context:property-placeholder location="classpath:test.properties"/>
<bean id="me" class="com.pojo.Me">
<property name="name" value="${ljj.name}"></property>
<property name="age" value="${ljj.age}"></property>
<property name="sex" value="${ljj.sex}"></property>
</bean>
当然我们需要先创建properties文件,test.properties如下:
ljj.name=ljj
ljj.sex=male
ljj.age=18
DAY5
声明式事务
spring帮我们封装好了提交回滚等操作,不用自己动手写了。声明式事务的写法如下:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 加入扫描使注解生效 -->
<context:component-scan base-package="com.service.impl"></context:component-scan>
<!-- 整合mybatis -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
<!-- 声明式事务:spring提供的事务自动提交回滚等操作,不用手动写了 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 当然这边也可以使用自动注入,就不用再写一遍property了 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--
name:方法名称,支持通配符;
readonly:是否为只读,查询建议只读,提升效率,默认为false;
-->
<tx:method name="showAll" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.service.impl.FlowerServiceImpl.*(..))" id="mypoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>
</beans>
常用注解
下面是service实体类的注解:
@Service
//service的注解,这样就不需要在xml配置bean了
public class FlowerServiceImpl implements FlowerService{
@Resource
//注入mapper,使用Resource注解或autowired注解,都不需要再写set get方法
private FlowerMapper flowerMapper;
@Override
public List<Flower> showAll() {
return flowerMapper.selAll();
}
}
flowerMapper是何时实例化的呢?xml也没有配这个bean啊?
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
就是这个,这里会把mapper包下的对象都创建出来。